aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/webglrenderingcontext.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/webglrenderingcontext.rs')
-rw-r--r--components/script/dom/webglrenderingcontext.rs716
1 files changed, 514 insertions, 202 deletions
diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs
index 7079bbcc76b..34d085d7f6b 100644
--- a/components/script/dom/webglrenderingcontext.rs
+++ b/components/script/dom/webglrenderingcontext.rs
@@ -4,14 +4,15 @@
use byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt};
use canvas_traits::canvas::{byte_swap, multiply_u8_pixel};
-use canvas_traits::webgl::{DOMToTextureCommand, Parameter, ProgramParameter};
-use canvas_traits::webgl::{ShaderParameter, TexParameter, VertexAttrib, WebGLCommand};
+use canvas_traits::webgl::{ActiveAttribInfo, DOMToTextureCommand, Parameter};
+use canvas_traits::webgl::{ShaderParameter, TexParameter, WebGLCommand};
use canvas_traits::webgl::{WebGLContextShareMode, WebGLError};
use canvas_traits::webgl::{WebGLFramebufferBindingRequest, WebGLMsg, WebGLMsgSender};
use canvas_traits::webgl::{WebGLResult, WebGLSLVersion, WebGLVersion};
use canvas_traits::webgl::{WebVRCommand, webgl_channel};
use canvas_traits::webgl::WebGLError::*;
use dom::bindings::cell::DomRefCell;
+use dom::bindings::codegen::Bindings::ANGLEInstancedArraysBinding::ANGLEInstancedArraysConstants;
use dom::bindings::codegen::Bindings::EXTBlendMinmaxBinding::EXTBlendMinmaxConstants;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{self, WebGLContextAttributes};
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
@@ -52,9 +53,10 @@ use euclid::Size2D;
use fnv::FnvHashMap;
use half::f16;
use js::jsapi::{JSContext, JSObject, Type};
-use js::jsval::{BooleanValue, DoubleValue, Int32Value, UInt32Value, JSVal, NullValue, UndefinedValue};
+use js::jsval::{BooleanValue, DoubleValue, Int32Value, UInt32Value, JSVal};
+use js::jsval::{ObjectValue, NullValue, UndefinedValue};
use js::rust::CustomAutoRooterGuard;
-use js::typedarray::ArrayBufferView;
+use js::typedarray::{ArrayBufferView, CreateWith, Float32Array, Int32Array, Uint32Array};
use net_traits::image::base::PixelFormat;
use net_traits::image_cache::ImageResponse;
use offscreen_gl_context::{GLContextAttributes, GLLimits};
@@ -63,7 +65,7 @@ use script_layout_interface::HTMLCanvasDataSource;
use servo_config::prefs::PREFS;
use std::cell::{Cell, Ref};
use std::cmp;
-use std::ptr::NonNull;
+use std::ptr::{self, NonNull};
use webrender_api;
type ImagePixelResult = Result<(Vec<u8>, Size2D<i32>, bool), ()>;
@@ -193,7 +195,7 @@ pub struct WebGLRenderingContext {
bound_texture_unit: Cell<u32>,
bound_buffer_array: MutNullableDom<WebGLBuffer>,
bound_buffer_element_array: MutNullableDom<WebGLBuffer>,
- bound_attrib_buffers: BoundAttribBuffers,
+ vertex_attribs: VertexAttribs,
current_program: MutNullableDom<WebGLProgram>,
#[ignore_malloc_size_of = "Because it's small"]
current_vertex_attrib_0: Cell<(f32, f32, f32, f32)>,
@@ -234,6 +236,7 @@ impl WebGLRenderingContext {
share_mode: ctx_data.share_mode,
webgl_version,
glsl_version: ctx_data.glsl_version,
+ vertex_attribs: VertexAttribs::new(ctx_data.limits.max_vertex_attribs),
limits: ctx_data.limits,
canvas: Dom::from_ref(canvas),
last_error: Cell::new(None),
@@ -244,13 +247,12 @@ impl WebGLRenderingContext {
bound_texture_unit: Cell::new(constants::TEXTURE0),
bound_buffer_array: MutNullableDom::new(None),
bound_buffer_element_array: MutNullableDom::new(None),
- bound_attrib_buffers: Default::default(),
bound_renderbuffer: MutNullableDom::new(None),
current_program: MutNullableDom::new(None),
current_vertex_attrib_0: Cell::new((0f32, 0f32, 0f32, 1f32)),
current_scissor: Cell::new((0, 0, size.width, size.height)),
current_clear_color: Cell::new((0.0, 0.0, 0.0, 0.0)),
- extension_manager: WebGLExtensions::new(webgl_version)
+ extension_manager: WebGLExtensions::new(webgl_version),
}
})
}
@@ -312,8 +314,8 @@ impl WebGLRenderingContext {
})
}
- pub fn bound_attrib_buffers(&self) -> &BoundAttribBuffers {
- &self.bound_attrib_buffers
+ pub fn vertex_attribs(&self) -> &VertexAttribs {
+ &self.vertex_attribs
}
pub fn bound_buffer_element_array(&self) -> Option<DomRoot<WebGLBuffer>> {
@@ -1155,6 +1157,142 @@ impl WebGLRenderingContext {
}
}
+ // https://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays/
+ pub fn draw_arrays_instanced(
+ &self,
+ mode: u32,
+ first: i32,
+ count: i32,
+ primcount: i32,
+ ) {
+ match mode {
+ constants::POINTS | constants::LINE_STRIP |
+ constants::LINE_LOOP | constants::LINES |
+ constants::TRIANGLE_STRIP | constants::TRIANGLE_FAN |
+ constants::TRIANGLES => {},
+ _ => {
+ return self.webgl_error(InvalidEnum);
+ }
+ }
+ if first < 0 || count < 0 || primcount < 0 {
+ return self.webgl_error(InvalidValue);
+ }
+
+ let current_program = handle_potential_webgl_error!(
+ self,
+ self.current_program.get().ok_or(InvalidOperation),
+ return
+ );
+
+ let required_len = if count > 0 {
+ handle_potential_webgl_error!(
+ self,
+ first.checked_add(count).map(|len| len as u32).ok_or(InvalidOperation),
+ return
+ )
+ } else {
+ 0
+ };
+
+ handle_potential_webgl_error!(
+ self,
+ self.vertex_attribs.validate_for_draw(required_len, primcount as u32, &current_program.active_attribs()),
+ return
+ );
+
+ if !self.validate_framebuffer_complete() {
+ return;
+ }
+
+ self.send_command(
+ WebGLCommand::DrawArraysInstanced { mode, first, count, primcount },
+ );
+ self.mark_as_dirty();
+ }
+
+ // https://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays/
+ pub fn draw_elements_instanced(
+ &self,
+ mode: u32,
+ count: i32,
+ type_: u32,
+ offset: i64,
+ primcount: i32,
+ ) {
+ match mode {
+ constants::POINTS | constants::LINE_STRIP |
+ constants::LINE_LOOP | constants::LINES |
+ constants::TRIANGLE_STRIP | constants::TRIANGLE_FAN |
+ constants::TRIANGLES => {},
+ _ => {
+ return self.webgl_error(InvalidEnum);
+ }
+ }
+ if count < 0 || offset < 0 || primcount < 0 {
+ return self.webgl_error(InvalidValue);
+ }
+ let type_size = match type_ {
+ constants::UNSIGNED_BYTE => 1,
+ constants::UNSIGNED_SHORT => 2,
+ _ => return self.webgl_error(InvalidEnum),
+ };
+ if offset % type_size != 0 {
+ return self.webgl_error(InvalidOperation);
+ }
+
+ let current_program = handle_potential_webgl_error!(
+ self,
+ self.current_program.get().ok_or(InvalidOperation),
+ return
+ );
+
+ if count > 0 && primcount > 0 {
+ if let Some(array_buffer) = self.bound_buffer_element_array.get() {
+ // WebGL Spec: check buffer overflows, must be a valid multiple of the size.
+ let val = offset as u64 + (count as u64 * type_size as u64);
+ if val > array_buffer.capacity() as u64 {
+ return self.webgl_error(InvalidOperation);
+ }
+ } else {
+ // From the WebGL spec
+ //
+ // a non-null WebGLBuffer must be bound to the ELEMENT_ARRAY_BUFFER binding point
+ // or an INVALID_OPERATION error will be generated.
+ //
+ return self.webgl_error(InvalidOperation);
+ }
+ }
+
+ // TODO(nox): Pass the correct number of vertices required.
+ handle_potential_webgl_error!(
+ self,
+ self.vertex_attribs.validate_for_draw(0, primcount as u32, &current_program.active_attribs()),
+ return
+ );
+
+ if !self.validate_framebuffer_complete() {
+ return;
+ }
+
+ self.send_command(WebGLCommand::DrawElementsInstanced {
+ mode,
+ count,
+ type_,
+ offset: offset as u32,
+ primcount,
+ });
+ self.mark_as_dirty();
+ }
+
+ pub fn vertex_attrib_divisor(&self, index: u32, divisor: u32) {
+ if index >= self.limits.max_vertex_attribs {
+ return self.webgl_error(InvalidValue);
+ }
+
+ self.vertex_attribs.set_divisor(index, divisor);
+ self.send_command(WebGLCommand::VertexAttribDivisor { index, divisor });
+ }
+
// Used by HTMLCanvasElement.toDataURL
//
// This emits errors quite liberally, but the spec says that this operation
@@ -1331,6 +1469,16 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
return Int32Value(constants::UNSIGNED_BYTE as i32);
}
}
+ constants::COMPRESSED_TEXTURE_FORMATS => {
+ // FIXME(nox): https://github.com/servo/servo/issues/20594
+ rooted!(in(cx) let mut rval = ptr::null_mut::<JSObject>());
+ let _ = Uint32Array::create(
+ cx,
+ CreateWith::Slice(&[]),
+ rval.handle_mut(),
+ ).unwrap();
+ return ObjectValue(rval.get());
+ }
constants::VERSION => {
rooted!(in(cx) let mut rval = UndefinedValue());
"WebGL 1.0".to_jsval(cx, rval.handle_mut());
@@ -1422,13 +1570,27 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
self.send_command(WebGLCommand::GetParameterInt(param, sender));
Int32Value(receiver.recv().unwrap())
}
+ Parameter::Int2(param) => {
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.send_command(WebGLCommand::GetParameterInt2(param, sender));
+ rooted!(in(cx) let mut rval = ptr::null_mut::<JSObject>());
+ let _ = Int32Array::create(
+ cx,
+ CreateWith::Slice(&receiver.recv().unwrap()),
+ rval.handle_mut(),
+ ).unwrap();
+ ObjectValue(rval.get())
+ }
Parameter::Int4(param) => {
let (sender, receiver) = webgl_channel().unwrap();
self.send_command(WebGLCommand::GetParameterInt4(param, sender));
- // FIXME(nox): https://github.com/servo/servo/issues/20655
- rooted!(in(cx) let mut rval = UndefinedValue());
- receiver.recv().unwrap().to_jsval(cx, rval.handle_mut());
- rval.get()
+ rooted!(in(cx) let mut rval = ptr::null_mut::<JSObject>());
+ let _ = Int32Array::create(
+ cx,
+ CreateWith::Slice(&receiver.recv().unwrap()),
+ rval.handle_mut(),
+ ).unwrap();
+ ObjectValue(rval.get())
}
Parameter::Float(param) => {
let (sender, receiver) = webgl_channel().unwrap();
@@ -1438,18 +1600,24 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
Parameter::Float2(param) => {
let (sender, receiver) = webgl_channel().unwrap();
self.send_command(WebGLCommand::GetParameterFloat2(param, sender));
- // FIXME(nox): https://github.com/servo/servo/issues/20655
- rooted!(in(cx) let mut rval = UndefinedValue());
- receiver.recv().unwrap().to_jsval(cx, rval.handle_mut());
- rval.get()
+ rooted!(in(cx) let mut rval = ptr::null_mut::<JSObject>());
+ let _ = Float32Array::create(
+ cx,
+ CreateWith::Slice(&receiver.recv().unwrap()),
+ rval.handle_mut(),
+ ).unwrap();
+ ObjectValue(rval.get())
}
Parameter::Float4(param) => {
let (sender, receiver) = webgl_channel().unwrap();
self.send_command(WebGLCommand::GetParameterFloat4(param, sender));
- // FIXME(nox): https://github.com/servo/servo/issues/20655
- rooted!(in(cx) let mut rval = UndefinedValue());
- receiver.recv().unwrap().to_jsval(cx, rval.handle_mut());
- rval.get()
+ rooted!(in(cx) let mut rval = ptr::null_mut::<JSObject>());
+ let _ = Float32Array::create(
+ cx,
+ CreateWith::Slice(&receiver.recv().unwrap()),
+ rval.handle_mut(),
+ ).unwrap();
+ ObjectValue(rval.get())
}
}
}
@@ -2021,7 +2189,12 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
fn CompileShader(&self, shader: &WebGLShader) {
handle_potential_webgl_error!(
self,
- shader.compile(self.webgl_version, self.glsl_version, &self.extension_manager)
+ shader.compile(
+ self.webgl_version,
+ self.glsl_version,
+ &self.limits,
+ &self.extension_manager,
+ )
)
}
@@ -2075,7 +2248,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
}
// Remove deleted buffer from bound attrib buffers.
- self.bound_attrib_buffers.remove_buffer(buffer);
+ self.vertex_attribs.delete_buffer(buffer);
// Delete buffer.
handle_object_deletion!(self, self.bound_buffer_array, buffer,
@@ -2193,22 +2366,28 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
if first < 0 || count < 0 {
return self.webgl_error(InvalidValue);
}
- if self.current_program.get().is_none() {
- return self.webgl_error(InvalidOperation);
- }
- if let Some(array_buffer) = self.bound_buffer_array.get() {
- if count > 0 && (first as u64 + count as u64 > array_buffer.capacity() as u64) {
- return self.webgl_error(InvalidOperation);
- }
- }
- {
- // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.2
- let buffers = self.bound_attrib_buffers.borrow();
- if buffers.iter().any(|(_, &(enabled, ref buffer))| enabled && buffer.is_none()) {
- return self.webgl_error(InvalidOperation);
- }
- }
+ let current_program = handle_potential_webgl_error!(
+ self,
+ self.current_program.get().ok_or(InvalidOperation),
+ return
+ );
+
+ let required_len = if count > 0 {
+ handle_potential_webgl_error!(
+ self,
+ first.checked_add(count).map(|len| len as u32).ok_or(InvalidOperation),
+ return
+ )
+ } else {
+ 0
+ };
+
+ handle_potential_webgl_error!(
+ self,
+ self.vertex_attribs.validate_for_draw(required_len, 1, &current_program.active_attribs()),
+ return
+ );
if !self.validate_framebuffer_complete() {
return;
@@ -2250,15 +2429,11 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
return self.webgl_error(InvalidValue);
}
- if self.current_program.get().is_none() {
- // From the WebGL spec
- //
- // If the CURRENT_PROGRAM is null, an INVALID_OPERATION error will be generated.
- // WebGL performs additional error checking beyond that specified
- // in OpenGL ES 2.0 during calls to drawArrays and drawElements.
- //
- return self.webgl_error(InvalidOperation);
- }
+ let current_program = handle_potential_webgl_error!(
+ self,
+ self.current_program.get().ok_or(InvalidOperation),
+ return
+ );
if count > 0 {
if let Some(array_buffer) = self.bound_buffer_element_array.get() {
@@ -2277,13 +2452,12 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
}
}
- {
- // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.2
- let buffers = self.bound_attrib_buffers.borrow();
- if buffers.iter().any(|(_, &(enabled, ref buffer))| enabled && buffer.is_none()) {
- return self.webgl_error(InvalidOperation);
- }
- }
+ // TODO(nox): Pass the correct number of vertices required.
+ handle_potential_webgl_error!(
+ self,
+ self.vertex_attribs.validate_for_draw(0, 1, &current_program.active_attribs()),
+ return
+ );
if !self.validate_framebuffer_complete() {
return;
@@ -2299,7 +2473,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
return self.webgl_error(InvalidValue);
}
- self.bound_attrib_buffers.enabled(attrib_id, true);
+ self.vertex_attribs.enabled_as_array(attrib_id, true);
self.send_command(WebGLCommand::EnableVertexAttribArray(attrib_id));
}
@@ -2309,7 +2483,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
return self.webgl_error(InvalidValue);
}
- self.bound_attrib_buffers.enabled(attrib_id, false);
+ self.vertex_attribs.enabled_as_array(attrib_id, false);
self.send_command(WebGLCommand::DisableVertexAttribArray(attrib_id));
}
@@ -2326,18 +2500,12 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
fn GetActiveAttrib(&self, program: &WebGLProgram, index: u32) -> Option<DomRoot<WebGLActiveInfo>> {
- match program.get_active_attrib(index) {
- Ok(ret) => Some(ret),
- Err(e) => {
- self.webgl_error(e);
- return None;
- }
- }
+ handle_potential_webgl_error!(self, program.get_active_attrib(index).map(Some), None)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
fn GetAttribLocation(&self, program: &WebGLProgram, name: DOMString) -> i32 {
- handle_potential_webgl_error!(self, program.get_attrib_location(name), None).unwrap_or(-1)
+ handle_potential_webgl_error!(self, program.get_attrib_location(name), -1)
}
#[allow(unsafe_code)]
@@ -2509,23 +2677,39 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
#[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
unsafe fn GetProgramParameter(&self, _: *mut JSContext, program: &WebGLProgram, param: u32) -> JSVal {
- match handle_potential_webgl_error!(self, ProgramParameter::from_u32(param), return NullValue()) {
- ProgramParameter::Bool(param) => {
+ // FIXME(nox): INVALID_OPERATION if program comes from a different context.
+ match param {
+ constants::DELETE_STATUS => BooleanValue(program.is_deleted()),
+ constants::LINK_STATUS => BooleanValue(program.is_linked()),
+ constants::VALIDATE_STATUS => {
+ // FIXME(nox): This could be cached on the DOM side when we call validateProgram
+ // but I'm not sure when the value should be reset.
let (sender, receiver) = webgl_channel().unwrap();
- self.send_command(WebGLCommand::GetProgramParameterBool(program.id(), param, sender));
+ self.send_command(WebGLCommand::GetProgramValidateStatus(program.id(), sender));
BooleanValue(receiver.recv().unwrap())
}
- ProgramParameter::Int(param) => {
+ constants::ATTACHED_SHADERS => {
+ // FIXME(nox): This allocates a vector and roots a couple of shaders for nothing.
+ Int32Value(program.attached_shaders().map(|shaders| shaders.len() as i32).unwrap_or(0))
+ }
+ constants::ACTIVE_ATTRIBUTES => Int32Value(program.active_attribs().len() as i32),
+ constants::ACTIVE_UNIFORMS => {
+ // FIXME(nox): We'll need to cache that on the DOM side at some point.
let (sender, receiver) = webgl_channel().unwrap();
- self.send_command(WebGLCommand::GetProgramParameterInt(program.id(), param, sender));
+ self.send_command(WebGLCommand::GetProgramActiveUniforms(program.id(), sender));
Int32Value(receiver.recv().unwrap())
}
+ _ => {
+ self.webgl_error(InvalidEnum);
+ NullValue()
+ }
}
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
fn GetShaderInfoLog(&self, shader: &WebGLShader) -> Option<DOMString> {
- shader.info_log().map(DOMString::from)
+ // TODO(nox): https://github.com/servo/servo/issues/21133
+ Some(shader.info_log())
}
#[allow(unsafe_code)]
@@ -2591,43 +2775,55 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
#[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
unsafe fn GetVertexAttrib(&self, cx: *mut JSContext, index: u32, param: u32) -> JSVal {
- if index == 0 && param == constants::CURRENT_VERTEX_ATTRIB {
- rooted!(in(cx) let mut result = UndefinedValue());
- let (x, y, z, w) = self.current_vertex_attrib_0.get();
- let attrib = vec![x, y, z, w];
- attrib.to_jsval(cx, result.handle_mut());
- return result.get()
+ let data = handle_potential_webgl_error!(
+ self,
+ self.vertex_attribs.get(index).ok_or(InvalidValue),
+ return NullValue()
+ );
+ if param == constants::CURRENT_VERTEX_ATTRIB {
+ let value = if index == 0 {
+ let (x, y, z, w) = self.current_vertex_attrib_0.get();
+ [x, y, z, w]
+ } else {
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.send_command(WebGLCommand::GetCurrentVertexAttrib(index, sender));
+ receiver.recv().unwrap()
+ };
+ rooted!(in(cx) let mut result = ptr::null_mut::<JSObject>());
+ let _ = Float32Array::create(
+ cx,
+ CreateWith::Slice(&value),
+ result.handle_mut(),
+ ).unwrap();
+ return ObjectValue(result.get());
}
- if param == constants::VERTEX_ATTRIB_ARRAY_BUFFER_BINDING {
- rooted!(in(cx) let mut jsval = NullValue());
- if let Some(buffer) = self.bound_attrib_buffers.get(index) {
- buffer.to_jsval(cx, jsval.handle_mut());
- }
- return jsval.get();
+ if !self.extension_manager.is_get_vertex_attrib_name_enabled(param) {
+ self.webgl_error(WebGLError::InvalidEnum);
+ return NullValue();
}
- match handle_potential_webgl_error!(self, VertexAttrib::from_u32(param), return NullValue()) {
- VertexAttrib::Bool(param) => {
- let (sender, receiver) = webgl_channel().unwrap();
- self.send_command(WebGLCommand::GetVertexAttribBool(index, param, sender));
- let value = handle_potential_webgl_error!(self, receiver.recv().unwrap(), return NullValue());
- BooleanValue(value)
+ match param {
+ constants::VERTEX_ATTRIB_ARRAY_ENABLED => BooleanValue(data.enabled_as_array),
+ constants::VERTEX_ATTRIB_ARRAY_SIZE => Int32Value(data.size as i32),
+ constants::VERTEX_ATTRIB_ARRAY_TYPE => Int32Value(data.type_ as i32),
+ constants::VERTEX_ATTRIB_ARRAY_NORMALIZED => BooleanValue(data.normalized),
+ constants::VERTEX_ATTRIB_ARRAY_STRIDE => Int32Value(data.stride as i32),
+ constants::VERTEX_ATTRIB_ARRAY_BUFFER_BINDING => {
+ rooted!(in(cx) let mut jsval = NullValue());
+ if let Some(data) = self.vertex_attribs.get(index) {
+ if let Some(buffer) = data.buffer() {
+ buffer.to_jsval(cx, jsval.handle_mut());
+ }
+ }
+ jsval.get()
}
- VertexAttrib::Int(param) => {
- let (sender, receiver) = webgl_channel().unwrap();
- self.send_command(WebGLCommand::GetVertexAttribInt(index, param, sender));
- let value = handle_potential_webgl_error!(self, receiver.recv().unwrap(), return NullValue());
- Int32Value(value)
+ ANGLEInstancedArraysConstants::VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE => {
+ Int32Value(data.divisor as i32)
}
- VertexAttrib::Float4(param) => {
- let (sender, receiver) = webgl_channel().unwrap();
- self.send_command(WebGLCommand::GetVertexAttribFloat4(index, param, sender));
- let value = handle_potential_webgl_error!(self, receiver.recv().unwrap(), return NullValue());
- // FIXME(nox): https://github.com/servo/servo/issues/20655
- rooted!(in(cx) let mut result = UndefinedValue());
- value.to_jsval(cx, result.handle_mut());
- result.get()
+ _ => {
+ self.webgl_error(InvalidEnum);
+ NullValue()
}
}
}
@@ -2638,10 +2834,12 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
self.webgl_error(InvalidEnum);
return 0;
}
- let (sender, receiver) = webgl_channel().unwrap();
- self.send_command(WebGLCommand::GetVertexAttribOffset(index, pname, sender));
-
- receiver.recv().unwrap() as i64
+ let data = handle_potential_webgl_error!(
+ self,
+ self.vertex_attribs.get(index).ok_or(InvalidValue),
+ return 0
+ );
+ data.offset as i64
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
@@ -2956,12 +3154,9 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
- fn LinkProgram(&self, program: Option<&WebGLProgram>) {
- if let Some(program) = program {
- if let Err(e) = program.link() {
- self.webgl_error(e);
- }
- }
+ fn LinkProgram(&self, program: &WebGLProgram) {
+ // FIXME(nox): INVALID_OPERATION if program comes from a different context.
+ handle_potential_webgl_error!(self, program.link());
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
@@ -2971,7 +3166,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
fn GetShaderSource(&self, shader: &WebGLShader) -> Option<DOMString> {
- shader.source()
+ // TODO(nox): https://github.com/servo/servo/issues/21133
+ Some(shader.source())
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
@@ -3245,11 +3441,12 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
fn UseProgram(&self, program: Option<&WebGLProgram>) {
if let Some(program) = program {
- match program.use_program() {
- Ok(()) => self.current_program.set(Some(program)),
- Err(e) => self.webgl_error(e),
+ if program.is_deleted() || !program.is_linked() {
+ return self.webgl_error(InvalidOperation);
}
}
+ self.send_command(WebGLCommand::UseProgram(program.map(|p| p.id())));
+ self.current_program.set(program);
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
@@ -3328,49 +3525,30 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- fn VertexAttribPointer(&self, attrib_id: u32, size: i32, data_type: u32,
- normalized: bool, stride: i32, offset: i64) {
- if attrib_id >= self.limits.max_vertex_attribs {
- return self.webgl_error(InvalidValue);
- }
-
- // GLES spec: If offset or stride is negative, an INVALID_VALUE error will be generated
- // WebGL spec: the maximum supported stride is 255
- if stride < 0 || stride > 255 || offset < 0 {
- return self.webgl_error(InvalidValue);
- }
- if size < 1 || size > 4 {
- return self.webgl_error(InvalidValue);
- }
-
- let buffer_array = match self.bound_buffer_array.get() {
- Some(buffer) => buffer,
- None => {
- return self.webgl_error(InvalidOperation);
- }
- };
-
- // stride and offset must be multiple of data_type
- match data_type {
- constants::BYTE | constants::UNSIGNED_BYTE => {},
- constants::SHORT | constants::UNSIGNED_SHORT => {
- if offset % 2 > 0 || stride % 2 > 0 {
- return self.webgl_error(InvalidOperation);
- }
- },
- constants::FLOAT => {
- if offset % 4 > 0 || stride % 4 > 0 {
- return self.webgl_error(InvalidOperation);
- }
- },
- _ => return self.webgl_error(InvalidEnum),
-
- }
-
- self.bound_attrib_buffers.bind_buffer(attrib_id, &buffer_array);
+ fn VertexAttribPointer(
+ &self,
+ index: u32,
+ size: i32,
+ type_: u32,
+ normalized: bool,
+ stride: i32,
+ offset: i64,
+ ) {
+ handle_potential_webgl_error!(
+ self,
+ self.vertex_attribs.set_pointer(
+ index,
+ size,
+ type_,
+ normalized,
+ stride,
+ offset,
+ self.bound_buffer_array.get().as_ref().map(|buffer| &**buffer),
+ ),
+ return
+ );
- let msg = WebGLCommand::VertexAttribPointer(attrib_id, size, data_type, normalized, stride, offset as u32);
- self.send_command(msg);
+ self.send_command(WebGLCommand::VertexAttribPointer(index, size, type_, normalized, stride, offset as u32));
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.4
@@ -3685,27 +3863,14 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7
- fn RenderbufferStorage(&self, target: u32, internal_format: u32,
- width: i32, height: i32) {
- // From the GLES 2.0.25 spec:
- //
- // "target must be RENDERBUFFER."
+ fn RenderbufferStorage(&self, target: u32, internal_format: u32, width: i32, height: i32) {
if target != constants::RENDERBUFFER {
return self.webgl_error(InvalidEnum);
}
- // From the GLES 2.0.25 spec:
- //
- // "If either width or height is greater than the value of
- // MAX_RENDERBUFFER_SIZE , the error INVALID_VALUE is
- // generated."
- //
- // and we have to throw out negative-size values as well just
- // like for TexImage.
- //
- // FIXME: Handle max_renderbuffer_size, which doesn't seem to
- // be in limits.
- if width < 0 || height < 0 {
+ let max = self.limits.max_renderbuffer_size;
+
+ if width < 0 || width as u32 > max || height < 0 || height as u32 > max {
return self.webgl_error(InvalidValue);
}
@@ -3805,48 +3970,195 @@ impl UniformSetterType {
}
}
-#[derive(Default, JSTraceable, MallocSizeOf)]
+#[derive(JSTraceable, MallocSizeOf)]
#[must_root]
-pub struct BoundAttribBuffers {
- elements: DomRefCell<FnvHashMap<u32, (bool, Option<Dom<WebGLBuffer>>)>>,
+pub struct VertexAttribs {
+ attribs: DomRefCell<Box<[VertexAttribData]>>,
}
-impl BoundAttribBuffers {
+impl VertexAttribs {
+ pub fn new(max: u32) -> Self {
+ // High-end GPUs have 16 of those, let's just use a boxed slice.
+ Self { attribs: DomRefCell::new(vec![Default::default(); max as usize].into()) }
+ }
+
pub fn clear(&self) {
- self.elements.borrow_mut().clear()
+ for attrib in &mut **self.attribs.borrow_mut() {
+ *attrib = Default::default();
+ }
}
- pub fn set_from(&self, other: &BoundAttribBuffers) {
- *self.elements.borrow_mut() = other.elements.borrow().clone();
+ pub fn clone_from(&self, other: &Self) {
+ self.attribs.borrow_mut().clone_from_slice(&other.attribs.borrow());
}
- pub fn borrow(&self) -> Ref<FnvHashMap<u32, (bool, Option<Dom<WebGLBuffer>>)>> {
- self.elements.borrow()
+ pub fn set_pointer(
+ &self,
+ index: u32,
+ size: i32,
+ type_: u32,
+ normalized: bool,
+ stride: i32,
+ offset: i64,
+ buffer: Option<&WebGLBuffer>,
+ ) -> WebGLResult<()> {
+ let mut attribs = self.attribs.borrow_mut();
+ let data = attribs.get_mut(index as usize).ok_or(InvalidValue)?;
+
+ if size < 1 || size > 4 {
+ return Err(InvalidValue);
+ }
+
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#BUFFER_OFFSET_AND_STRIDE
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#VERTEX_STRIDE
+ if stride < 0 || stride > 255 || offset < 0 {
+ return Err(InvalidValue);
+ }
+ let bytes_per_component: i32 = match type_ {
+ constants::BYTE | constants::UNSIGNED_BYTE => 1,
+ constants::SHORT | constants::UNSIGNED_SHORT => 2,
+ constants::FLOAT => 4,
+ _ => return Err(InvalidEnum),
+ };
+ if offset % bytes_per_component as i64 > 0 || stride % bytes_per_component > 0 {
+ return Err(InvalidOperation);
+ }
+
+ let buffer = buffer.ok_or(InvalidOperation)?;
+
+ *data = VertexAttribData {
+ enabled_as_array: data.enabled_as_array,
+ size: size as u8,
+ type_,
+ bytes_per_vertex: size as u8 * bytes_per_component as u8,
+ normalized,
+ stride: stride as u8,
+ offset: offset as u32,
+ buffer: Some(Dom::from_ref(buffer)),
+ divisor: data.divisor,
+ };
+ Ok(())
}
- fn remove_buffer(&self, buffer: &WebGLBuffer) {
- self.elements.borrow_mut().retain(|_, v| {
- v.1.as_ref().map_or(true, |b| b.id() != buffer.id())
- })
+ pub fn borrow(&self) -> Ref<[VertexAttribData]> {
+ Ref::map(self.attribs.borrow(), |attribs| &**attribs)
}
- fn get(&self, index: u32) -> Option<Ref<WebGLBuffer>> {
- ref_filter_map(self.elements.borrow(), |elements| {
- elements.get(&index).and_then(|&(_, ref buffer)| {
- buffer.as_ref().map(|b| &**b)
- })
- })
+ fn delete_buffer(&self, buffer: &WebGLBuffer) {
+ for attrib in &mut **self.attribs.borrow_mut() {
+ if attrib.buffer().map_or(false, |b| b.id() == buffer.id()) {
+ attrib.buffer = None;
+ }
+ }
+ }
+
+ fn get(&self, index: u32) -> Option<Ref<VertexAttribData>> {
+ ref_filter_map(self.attribs.borrow(), |attribs| attribs.get(index as usize))
+ }
+
+ fn enabled_as_array(&self, index: u32, value: bool) {
+ self.attribs.borrow_mut()[index as usize].enabled_as_array = value;
+ }
+
+ fn set_divisor(&self, index: u32, value: u32) {
+ self.attribs.borrow_mut()[index as usize].divisor = value;
+ }
+
+ fn validate_for_draw(
+ &self,
+ required_len: u32,
+ instance_count: u32,
+ active_attribs: &[ActiveAttribInfo],
+ ) -> WebGLResult<()> {
+ // TODO(nox): Cache limits per VAO.
+ let attribs = self.attribs.borrow();
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.2
+ if attribs.iter().any(|data| data.enabled_as_array && data.buffer.is_none()) {
+ return Err(InvalidOperation);
+ }
+ let mut has_active_attrib = false;
+ let mut has_divisor_0 = false;
+ for active_info in active_attribs {
+ if active_info.location < 0 {
+ continue;
+ }
+ has_active_attrib = true;
+ let attrib = &attribs[active_info.location as usize];
+ if attrib.divisor == 0 {
+ has_divisor_0 = true;
+ }
+ if !attrib.enabled_as_array {
+ continue;
+ }
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.6
+ if required_len > 0 && instance_count > 0 {
+ let max_vertices = attrib.max_vertices();
+ if attrib.divisor == 0 {
+ if max_vertices < required_len {
+ return Err(InvalidOperation);
+ }
+ } else if max_vertices.checked_mul(attrib.divisor).map_or(false, |v| v < instance_count) {
+ return Err(InvalidOperation);
+ }
+ }
+ }
+ if has_active_attrib && !has_divisor_0 {
+ return Err(InvalidOperation);
+ }
+ Ok(())
+ }
+}
+
+#[derive(Clone, JSTraceable, MallocSizeOf)]
+#[must_root]
+pub struct VertexAttribData {
+ enabled_as_array: bool,
+ size: u8,
+ type_: u32,
+ bytes_per_vertex: u8,
+ normalized: bool,
+ stride: u8,
+ offset: u32,
+ buffer: Option<Dom<WebGLBuffer>>,
+ divisor: u32,
+}
+
+impl Default for VertexAttribData {
+ #[allow(unrooted_must_root)]
+ fn default() -> Self {
+ Self {
+ enabled_as_array: false,
+ size: 4,
+ type_: constants::FLOAT,
+ bytes_per_vertex: 16,
+ normalized: false,
+ stride: 0,
+ offset: 0,
+ buffer: None,
+ divisor: 0,
+ }
}
+}
- fn enabled(&self, index: u32, value: bool) {
- let mut elements = self.elements.borrow_mut();
- let pair = elements.entry(index).or_insert((false, None));
- pair.0 = value;
+impl VertexAttribData {
+ pub fn buffer(&self) -> Option<&WebGLBuffer> {
+ self.buffer.as_ref().map(|b| &**b)
}
- fn bind_buffer(&self, index: u32, buffer: &WebGLBuffer) {
- let mut elements = self.elements.borrow_mut();
- let pair = elements.entry(index).or_insert((false, None));
- pair.1 = Some(Dom::from_ref(buffer));
+ fn max_vertices(&self) -> u32 {
+ let capacity = (self.buffer().unwrap().capacity() as u32).saturating_sub(self.offset);
+ if capacity < self.bytes_per_vertex as u32 {
+ 0
+ } else if self.stride == 0 {
+ capacity / self.bytes_per_vertex as u32
+ } else if self.stride < self.bytes_per_vertex {
+ (capacity - (self.bytes_per_vertex - self.stride) as u32) / self.stride as u32
+ } else {
+ let mut max = capacity / self.stride as u32;
+ if capacity % self.stride as u32 >= self.bytes_per_vertex as u32 {
+ max += 1;
+ }
+ max
+ }
}
}