diff options
Diffstat (limited to 'components')
-rw-r--r-- | components/canvas/Cargo.toml | 2 | ||||
-rw-r--r-- | components/canvas/webgl_limits.rs | 40 | ||||
-rw-r--r-- | components/canvas/webgl_thread.rs | 67 | ||||
-rw-r--r-- | components/canvas_traits/webgl.rs | 28 | ||||
-rw-r--r-- | components/script/dom/bindings/trace.rs | 5 | ||||
-rw-r--r-- | components/script/dom/webgl2renderingcontext.rs | 357 | ||||
-rw-r--r-- | components/script/dom/webglbuffer.rs | 8 | ||||
-rw-r--r-- | components/script/dom/webglprogram.rs | 149 | ||||
-rw-r--r-- | components/script/dom/webglrenderingcontext.rs | 68 | ||||
-rw-r--r-- | components/script/dom/webidls/WebGL2RenderingContext.webidl | 16 |
10 files changed, 688 insertions, 52 deletions
diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index daa07fab430..2f9495c961d 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -34,7 +34,7 @@ num-traits = "0.2" raqote = {git = "https://github.com/jrmuizel/raqote", optional = true} pixels = {path = "../pixels"} servo_config = {path = "../config"} -sparkle = "0.1.12" +sparkle = "0.1.13" webrender = {git = "https://github.com/servo/webrender"} webrender_api = {git = "https://github.com/servo/webrender"} webrender_traits = {path = "../webrender_traits"} diff --git a/components/canvas/webgl_limits.rs b/components/canvas/webgl_limits.rs index d910be71d99..1a08dfea945 100644 --- a/components/canvas/webgl_limits.rs +++ b/components/canvas/webgl_limits.rs @@ -65,14 +65,24 @@ impl GLLimitsDetect for GLLimits { }; let ( + max_uniform_block_size, max_uniform_buffer_bindings, min_program_texel_offset, max_program_texel_offset, max_transform_feedback_separate_attribs, max_draw_buffers, max_color_attachments, + max_combined_uniform_blocks, + max_combined_vertex_uniform_components, + max_combined_fragment_uniform_components, + max_vertex_uniform_blocks, + max_vertex_uniform_components, + max_fragment_uniform_blocks, + max_fragment_uniform_components, + uniform_buffer_offset_alignment, ); if webgl_version == WebGLVersion::WebGL2 { + max_uniform_block_size = gl.get_integer(gl::MAX_UNIFORM_BLOCK_SIZE); max_uniform_buffer_bindings = gl.get_integer(gl::MAX_UNIFORM_BUFFER_BINDINGS); min_program_texel_offset = gl.get_integer(gl::MIN_PROGRAM_TEXEL_OFFSET); max_program_texel_offset = gl.get_integer(gl::MAX_PROGRAM_TEXEL_OFFSET); @@ -81,14 +91,33 @@ impl GLLimitsDetect for GLLimits { max_color_attachments = gl.get_integer(gl::MAX_COLOR_ATTACHMENTS); max_draw_buffers = gl .get_integer(gl::MAX_DRAW_BUFFERS) - .min(max_color_attachments) + .min(max_color_attachments); + max_combined_uniform_blocks = gl.get_integer(gl::MAX_COMBINED_UNIFORM_BLOCKS); + max_combined_vertex_uniform_components = + gl.get_integer(gl::MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS); + max_combined_fragment_uniform_components = + gl.get_integer(gl::MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS); + max_vertex_uniform_blocks = gl.get_integer(gl::MAX_VERTEX_UNIFORM_BLOCKS); + max_vertex_uniform_components = gl.get_integer(gl::MAX_VERTEX_UNIFORM_COMPONENTS); + max_fragment_uniform_blocks = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_BLOCKS); + max_fragment_uniform_components = gl.get_integer(gl::MAX_FRAGMENT_UNIFORM_COMPONENTS); + uniform_buffer_offset_alignment = gl.get_integer(gl::UNIFORM_BUFFER_OFFSET_ALIGNMENT); } else { + max_uniform_block_size = 0; max_uniform_buffer_bindings = 0; min_program_texel_offset = 0; max_program_texel_offset = 0; max_transform_feedback_separate_attribs = 0; max_color_attachments = 1; max_draw_buffers = 1; + max_combined_uniform_blocks = 0; + max_combined_vertex_uniform_components = 0; + max_combined_fragment_uniform_components = 0; + max_vertex_uniform_blocks = 0; + max_vertex_uniform_components = 0; + max_fragment_uniform_blocks = 0; + max_fragment_uniform_components = 0; + uniform_buffer_offset_alignment = 0; } GLLimits { @@ -111,6 +140,15 @@ impl GLLimitsDetect for GLLimits { max_program_texel_offset, max_color_attachments, max_draw_buffers, + max_uniform_block_size, + max_combined_uniform_blocks, + max_combined_vertex_uniform_components, + max_combined_fragment_uniform_components, + max_vertex_uniform_blocks, + max_vertex_uniform_components, + max_fragment_uniform_blocks, + max_fragment_uniform_components, + uniform_buffer_offset_alignment, } } } diff --git a/components/canvas/webgl_thread.rs b/components/canvas/webgl_thread.rs index b9a4533f134..46ea56c1c4f 100644 --- a/components/canvas/webgl_thread.rs +++ b/components/canvas/webgl_thread.rs @@ -6,6 +6,7 @@ use crate::webgl_limits::GLLimitsDetect; use byteorder::{ByteOrder, NativeEndian, WriteBytesExt}; use canvas_traits::webgl; use canvas_traits::webgl::ActiveAttribInfo; +use canvas_traits::webgl::ActiveUniformBlockInfo; use canvas_traits::webgl::ActiveUniformInfo; use canvas_traits::webgl::AlphaTreatment; use canvas_traits::webgl::DOMToTextureCommand; @@ -1744,6 +1745,40 @@ impl WebGLImpl { } sender.send(value).unwrap(); }, + WebGLCommand::GetUniformBlockIndex(program_id, ref name, ref sender) => { + let name = to_name_in_compiled_shader(name); + let index = gl.get_uniform_block_index(program_id.get(), &name); + sender.send(index).unwrap(); + }, + WebGLCommand::GetUniformIndices(program_id, ref names, ref sender) => { + let names = names + .iter() + .map(|name| to_name_in_compiled_shader(name)) + .collect::<Vec<_>>(); + let name_strs = names.iter().map(|name| name.as_str()).collect::<Vec<_>>(); + let indices = gl.get_uniform_indices(program_id.get(), &name_strs); + sender.send(indices).unwrap(); + }, + WebGLCommand::GetActiveUniforms(program_id, ref indices, pname, ref sender) => { + let results = gl.get_active_uniforms_iv(program_id.get(), indices, pname); + sender.send(results).unwrap(); + }, + WebGLCommand::GetActiveUniformBlockName(program_id, block_idx, ref sender) => { + let name = gl.get_active_uniform_block_name(program_id.get(), block_idx); + sender.send(name).unwrap(); + }, + WebGLCommand::GetActiveUniformBlockParameter( + program_id, + block_idx, + pname, + ref sender, + ) => { + let results = gl.get_active_uniform_block_iv(program_id.get(), block_idx, pname); + sender.send(results).unwrap(); + }, + WebGLCommand::UniformBlockBinding(program_id, block_idx, block_binding) => { + gl.uniform_block_binding(program_id.get(), block_idx, block_binding) + }, WebGLCommand::InitializeFramebuffer { color, depth, @@ -1790,6 +1825,16 @@ impl WebGLImpl { let value = gl.get_sampler_parameter_fv(sampler_id.get(), pname)[0]; sender.send(value).unwrap(); }, + WebGLCommand::BindBufferBase(target, index, id) => { + gl.bind_buffer_base(target, index, id.map_or(0, WebGLBufferId::get)) + }, + WebGLCommand::BindBufferRange(target, index, id, offset, size) => gl.bind_buffer_range( + target, + index, + id.map_or(0, WebGLBufferId::get), + offset as isize, + size as isize, + ), } // If debug asertions are enabled, then check the error state. @@ -1890,6 +1935,7 @@ impl WebGLImpl { linked: false, active_attribs: vec![].into(), active_uniforms: vec![].into(), + active_uniform_blocks: vec![].into(), transform_feedback_length: Default::default(), transform_feedback_mode: Default::default(), }; @@ -1945,6 +1991,26 @@ impl WebGLImpl { }) .collect::<Vec<_>>() .into(); + + let mut num_active_uniform_blocks = [0]; + unsafe { + gl.get_program_iv( + program.get(), + gl::ACTIVE_UNIFORM_BLOCKS, + &mut num_active_uniform_blocks, + ); + } + let active_uniform_blocks = (0..num_active_uniform_blocks[0] as u32) + .map(|i| { + let name = gl.get_active_uniform_block_name(program.get(), i); + let size = + gl.get_active_uniform_block_iv(program.get(), i, gl::UNIFORM_BLOCK_DATA_SIZE) + [0]; + ActiveUniformBlockInfo { name, size } + }) + .collect::<Vec<_>>() + .into(); + let mut transform_feedback_length = [0]; unsafe { gl.get_program_iv( @@ -1965,6 +2031,7 @@ impl WebGLImpl { linked: true, active_attribs, active_uniforms, + active_uniform_blocks, transform_feedback_length: transform_feedback_length[0], transform_feedback_mode: transform_feedback_mode[0], } diff --git a/components/canvas_traits/webgl.rs b/components/canvas_traits/webgl.rs index 63ee46c108c..c5660c24505 100644 --- a/components/canvas_traits/webgl.rs +++ b/components/canvas_traits/webgl.rs @@ -462,6 +462,12 @@ pub enum WebGLCommand { GetUniformFloat4(WebGLProgramId, i32, WebGLSender<[f32; 4]>), GetUniformFloat9(WebGLProgramId, i32, WebGLSender<[f32; 9]>), GetUniformFloat16(WebGLProgramId, i32, WebGLSender<[f32; 16]>), + GetUniformBlockIndex(WebGLProgramId, String, WebGLSender<u32>), + GetUniformIndices(WebGLProgramId, Vec<String>, WebGLSender<Vec<u32>>), + GetActiveUniforms(WebGLProgramId, Vec<u32>, u32, WebGLSender<Vec<i32>>), + GetActiveUniformBlockName(WebGLProgramId, u32, WebGLSender<String>), + GetActiveUniformBlockParameter(WebGLProgramId, u32, u32, WebGLSender<Vec<i32>>), + UniformBlockBinding(WebGLProgramId, u32, u32), InitializeFramebuffer { color: bool, depth: bool, @@ -479,6 +485,8 @@ pub enum WebGLCommand { SetSamplerParameterInt(WebGLSamplerId, u32, i32), GetSamplerParameterFloat(WebGLSamplerId, u32, WebGLSender<f32>), GetSamplerParameterInt(WebGLSamplerId, u32, WebGLSender<i32>), + BindBufferBase(u32, u32, Option<WebGLBufferId>), + BindBufferRange(u32, u32, Option<WebGLBufferId>, i64, i64), } macro_rules! nonzero_type { @@ -671,6 +679,8 @@ pub struct ProgramLinkInfo { pub active_attribs: Box<[ActiveAttribInfo]>, /// The list of active uniforms. pub active_uniforms: Box<[ActiveUniformInfo]>, + /// The list of active uniform blocks. + pub active_uniform_blocks: Box<[ActiveUniformBlockInfo]>, /// The number of varying variables pub transform_feedback_length: i32, /// The buffer mode used when transform feedback is active @@ -713,6 +723,15 @@ impl ActiveUniformInfo { } } +/// Description of a single uniform block. +#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] +pub struct ActiveUniformBlockInfo { + /// The name of the uniform block. + pub name: String, + /// The size of the uniform block. + pub size: i32, +} + macro_rules! parameters { ($name:ident { $( $variant:ident($kind:ident { $( @@ -999,4 +1018,13 @@ pub struct GLLimits { pub max_uniform_buffer_bindings: u32, pub min_program_texel_offset: u32, pub max_program_texel_offset: u32, + pub max_uniform_block_size: u32, + pub max_combined_uniform_blocks: u32, + pub max_combined_vertex_uniform_components: u32, + pub max_combined_fragment_uniform_components: u32, + pub max_vertex_uniform_blocks: u32, + pub max_vertex_uniform_components: u32, + pub max_fragment_uniform_blocks: u32, + pub max_fragment_uniform_components: u32, + pub uniform_buffer_offset_alignment: u32, } diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 3bad24b1b1d..1d83fff920d 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -49,7 +49,9 @@ use canvas_traits::canvas::{ }; use canvas_traits::canvas::{CompositionOrBlending, LineCapStyle, LineJoinStyle, RepetitionStyle}; use canvas_traits::webgl::WebGLVertexArrayId; -use canvas_traits::webgl::{ActiveAttribInfo, ActiveUniformInfo, GlType, TexDataType, TexFormat}; +use canvas_traits::webgl::{ + ActiveAttribInfo, ActiveUniformBlockInfo, ActiveUniformInfo, GlType, TexDataType, TexFormat, +}; use canvas_traits::webgl::{GLLimits, WebGLQueryId, WebGLSamplerId}; use canvas_traits::webgl::{WebGLBufferId, WebGLChan, WebGLContextId, WebGLError}; use canvas_traits::webgl::{WebGLFramebufferId, WebGLMsgSender, WebGLPipeline, WebGLProgramId}; @@ -437,6 +439,7 @@ unsafe impl<A: JSTraceable, B: JSTraceable, C: JSTraceable> JSTraceable for (A, unsafe_no_jsmanaged_fields!(ActiveAttribInfo); unsafe_no_jsmanaged_fields!(ActiveUniformInfo); +unsafe_no_jsmanaged_fields!(ActiveUniformBlockInfo); unsafe_no_jsmanaged_fields!(bool, f32, f64, String, AtomicBool, AtomicUsize, Uuid, char); unsafe_no_jsmanaged_fields!(usize, u8, u16, u32, u64); unsafe_no_jsmanaged_fields!(isize, i8, i16, i32, i64); diff --git a/components/script/dom/webgl2renderingcontext.rs b/components/script/dom/webgl2renderingcontext.rs index 2c63b775bb1..e8993f472d9 100644 --- a/components/script/dom/webgl2renderingcontext.rs +++ b/components/script/dom/webgl2renderingcontext.rs @@ -45,13 +45,14 @@ use dom_struct::dom_struct; use euclid::default::{Point2D, Rect, Size2D}; use ipc_channel::ipc; use js::jsapi::{JSObject, Type}; -use js::jsval::{BooleanValue, DoubleValue, Int32Value, JSVal, NullValue, UInt32Value}; +use js::jsval::{BooleanValue, DoubleValue, Int32Value, UInt32Value}; +use js::jsval::{JSVal, NullValue, ObjectValue, UndefinedValue}; use js::rust::CustomAutoRooterGuard; -use js::typedarray::ArrayBufferView; +use js::typedarray::{ArrayBufferView, CreateWith, Uint32Array}; use script_layout_interface::HTMLCanvasDataSource; use std::cell::Cell; use std::cmp; -use std::ptr::NonNull; +use std::ptr::{self, NonNull}; #[dom_struct] pub struct WebGL2RenderingContext { @@ -160,6 +161,18 @@ impl WebGL2RenderingContext { } } + pub fn buffer_usage(&self, usage: u32) -> WebGLResult<u32> { + match usage { + constants::STATIC_READ | + constants::DYNAMIC_READ | + constants::STREAM_READ | + constants::STATIC_COPY | + constants::DYNAMIC_COPY | + constants::STREAM_COPY => Ok(usage), + _ => self.base.buffer_usage(usage), + } + } + fn unbind_from(&self, slot: &MutNullableDom<WebGLBuffer>, buffer: &WebGLBuffer) { if slot.get().map_or(false, |b| buffer == &*b) { buffer.decrement_attached_counter(); @@ -411,40 +424,94 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { fn GetParameter(&self, cx: JSContext, parameter: u32) -> JSVal { match parameter { constants::MAX_CLIENT_WAIT_TIMEOUT_WEBGL => { - Int32Value(self.base.limits().max_client_wait_timeout_webgl.as_nanos() as i32) + return Int32Value( + self.base.limits().max_client_wait_timeout_webgl.as_nanos() as i32 + ); }, constants::SAMPLER_BINDING => unsafe { let idx = (self.base.textures().active_unit_enum() - constants::TEXTURE0) as usize; assert!(idx < self.samplers.len()); let sampler = self.samplers[idx].get(); - optional_root_object_to_js_or_null!(*cx, sampler) + return optional_root_object_to_js_or_null!(*cx, sampler); }, constants::COPY_READ_BUFFER_BINDING => unsafe { - optional_root_object_to_js_or_null!(*cx, &self.bound_copy_read_buffer.get()) + return optional_root_object_to_js_or_null!( + *cx, + &self.bound_copy_read_buffer.get() + ); }, constants::COPY_WRITE_BUFFER_BINDING => unsafe { - optional_root_object_to_js_or_null!(*cx, &self.bound_copy_write_buffer.get()) + return optional_root_object_to_js_or_null!( + *cx, + &self.bound_copy_write_buffer.get() + ); }, constants::PIXEL_PACK_BUFFER_BINDING => unsafe { - optional_root_object_to_js_or_null!(*cx, &self.bound_pixel_pack_buffer.get()) + return optional_root_object_to_js_or_null!( + *cx, + &self.bound_pixel_pack_buffer.get() + ); }, constants::PIXEL_UNPACK_BUFFER_BINDING => unsafe { - optional_root_object_to_js_or_null!(*cx, &self.bound_pixel_unpack_buffer.get()) + return optional_root_object_to_js_or_null!( + *cx, + &self.bound_pixel_unpack_buffer.get() + ); }, constants::TRANSFORM_FEEDBACK_BUFFER_BINDING => unsafe { - optional_root_object_to_js_or_null!( + return optional_root_object_to_js_or_null!( *cx, &self.bound_transform_feedback_buffer.get() - ) + ); }, constants::UNIFORM_BUFFER_BINDING => unsafe { - optional_root_object_to_js_or_null!(*cx, &self.bound_uniform_buffer.get()) + return optional_root_object_to_js_or_null!(*cx, &self.bound_uniform_buffer.get()); }, constants::TRANSFORM_FEEDBACK_BINDING => unsafe { - optional_root_object_to_js_or_null!(*cx, self.current_transform_feedback.get()) + return optional_root_object_to_js_or_null!( + *cx, + self.current_transform_feedback.get() + ); }, - _ => self.base.GetParameter(cx, parameter), + _ => {}, } + + let limit = match parameter { + constants::MAX_UNIFORM_BUFFER_BINDINGS => { + Some(self.base.limits().max_uniform_buffer_bindings) + }, + constants::MAX_UNIFORM_BLOCK_SIZE => Some(self.base.limits().max_uniform_block_size), + constants::MAX_COMBINED_UNIFORM_BLOCKS => { + Some(self.base.limits().max_combined_uniform_blocks) + }, + constants::MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS => { + Some(self.base.limits().max_combined_vertex_uniform_components) + }, + constants::MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS => { + Some(self.base.limits().max_combined_fragment_uniform_components) + }, + constants::MAX_VERTEX_UNIFORM_BLOCKS => { + Some(self.base.limits().max_vertex_uniform_blocks) + }, + constants::MAX_VERTEX_UNIFORM_COMPONENTS => { + Some(self.base.limits().max_vertex_uniform_components) + }, + constants::MAX_FRAGMENT_UNIFORM_BLOCKS => { + Some(self.base.limits().max_fragment_uniform_blocks) + }, + constants::MAX_FRAGMENT_UNIFORM_COMPONENTS => { + Some(self.base.limits().max_fragment_uniform_components) + }, + constants::UNIFORM_BUFFER_OFFSET_ALIGNMENT => { + Some(self.base.limits().uniform_buffer_offset_alignment) + }, + _ => None, + }; + if let Some(limit) = limit { + return UInt32Value(limit); + } + + self.base.GetParameter(cx, parameter) } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 @@ -571,6 +638,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 fn BufferData(&self, target: u32, data: Option<ArrayBufferViewOrArrayBuffer>, usage: u32) { + let usage = handle_potential_webgl_error!(self.base, self.buffer_usage(usage), return); let bound_buffer = handle_potential_webgl_error!(self.base, self.bound_buffer(target), return); self.base.buffer_data(target, data, usage, bound_buffer) @@ -578,6 +646,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 fn BufferData_(&self, target: u32, size: i64, usage: u32) { + let usage = handle_potential_webgl_error!(self.base, self.buffer_usage(usage), return); let bound_buffer = handle_potential_webgl_error!(self.base, self.bound_buffer(target), return); self.base.buffer_data_(target, size, usage, bound_buffer) @@ -593,6 +662,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { elem_offset: u32, length: u32, ) { + let usage = handle_potential_webgl_error!(self.base, self.buffer_usage(usage), return); let bound_buffer = handle_potential_webgl_error!(self.base, self.bound_buffer(target), return); let bound_buffer = @@ -628,7 +698,10 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 fn BufferSubData(&self, target: u32, offset: i64, data: ArrayBufferViewOrArrayBuffer) { - self.base.BufferSubData(target, offset, data) + let bound_buffer = + handle_potential_webgl_error!(self.base, self.bound_buffer(target), return); + self.base + .buffer_sub_data(target, offset, data, bound_buffer) } /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.3 @@ -2304,6 +2377,260 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { DOMString::from(name), )) } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16 + fn BindBufferBase(&self, target: u32, index: u32, buffer: Option<&WebGLBuffer>) { + let (bind_limit, slot) = match target { + constants::TRANSFORM_FEEDBACK_BUFFER => ( + self.base.limits().max_transform_feedback_separate_attribs, + &self.bound_transform_feedback_buffer, + ), + constants::UNIFORM_BUFFER => ( + self.base.limits().max_uniform_buffer_bindings, + &self.bound_uniform_buffer, + ), + _ => return self.base.webgl_error(InvalidEnum), + }; + if index >= bind_limit { + return self.base.webgl_error(InvalidValue); + } + + if let Some(buffer) = buffer { + handle_potential_webgl_error!(self.base, self.base.validate_ownership(buffer), return); + + if buffer.is_marked_for_deletion() { + return self.base.webgl_error(InvalidOperation); + } + handle_potential_webgl_error!(self.base, buffer.set_target_maybe(target), return); + buffer.increment_attached_counter(); + } + + self.base.send_command(WebGLCommand::BindBufferBase( + target, + index, + buffer.map(|b| b.id()), + )); + if let Some(old) = slot.get() { + old.decrement_attached_counter(); + } + + slot.set(buffer); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16 + fn BindBufferRange( + &self, + target: u32, + index: u32, + buffer: Option<&WebGLBuffer>, + offset: i64, + size: i64, + ) { + let (bind_limit, slot) = match target { + constants::TRANSFORM_FEEDBACK_BUFFER => ( + self.base.limits().max_transform_feedback_separate_attribs, + &self.bound_transform_feedback_buffer, + ), + constants::UNIFORM_BUFFER => ( + self.base.limits().max_uniform_buffer_bindings, + &self.bound_uniform_buffer, + ), + _ => return self.base.webgl_error(InvalidEnum), + }; + if index >= bind_limit { + return self.base.webgl_error(InvalidValue); + } + + if offset < 0 || size < 0 { + return self.base.webgl_error(InvalidValue); + } + if buffer.is_some() && size == 0 { + return self.base.webgl_error(InvalidValue); + } + + match target { + constants::TRANSFORM_FEEDBACK_BUFFER => { + if size % 4 != 0 && offset % 4 != 0 { + return self.base.webgl_error(InvalidValue); + } + }, + constants::UNIFORM_BUFFER => { + let offset_alignment = self.base.limits().uniform_buffer_offset_alignment; + if offset % offset_alignment as i64 != 0 { + return self.base.webgl_error(InvalidValue); + } + }, + _ => unreachable!(), + } + + if let Some(buffer) = buffer { + handle_potential_webgl_error!(self.base, self.base.validate_ownership(buffer), return); + + if buffer.is_marked_for_deletion() { + return self.base.webgl_error(InvalidOperation); + } + handle_potential_webgl_error!(self.base, buffer.set_target_maybe(target), return); + buffer.increment_attached_counter(); + } + + self.base.send_command(WebGLCommand::BindBufferRange( + target, + index, + buffer.map(|b| b.id()), + offset, + size, + )); + if let Some(old) = slot.get() { + old.decrement_attached_counter(); + } + + slot.set(buffer); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16 + fn GetUniformIndices(&self, program: &WebGLProgram, names: Vec<DOMString>) -> Option<Vec<u32>> { + handle_potential_webgl_error!( + self.base, + self.base.validate_ownership(program), + return None + ); + let indices = handle_potential_webgl_error!( + self.base, + program.get_uniform_indices(names), + return None + ); + Some(indices) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16 + #[allow(unsafe_code)] + fn GetActiveUniforms( + &self, + cx: JSContext, + program: &WebGLProgram, + indices: Vec<u32>, + pname: u32, + ) -> JSVal { + handle_potential_webgl_error!( + self.base, + self.base.validate_ownership(program), + return NullValue() + ); + let values = handle_potential_webgl_error!( + self.base, + program.get_active_uniforms(indices, pname), + return NullValue() + ); + + rooted!(in(*cx) let mut rval = UndefinedValue()); + match pname { + constants::UNIFORM_SIZE | + constants::UNIFORM_TYPE | + constants::UNIFORM_BLOCK_INDEX | + constants::UNIFORM_OFFSET | + constants::UNIFORM_ARRAY_STRIDE | + constants::UNIFORM_MATRIX_STRIDE => unsafe { + values.to_jsval(*cx, rval.handle_mut()); + }, + constants::UNIFORM_IS_ROW_MAJOR => unsafe { + let values = values.iter().map(|&v| v != 0).collect::<Vec<_>>(); + values.to_jsval(*cx, rval.handle_mut()); + }, + _ => unreachable!(), + } + rval.get() + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16 + fn GetUniformBlockIndex(&self, program: &WebGLProgram, block_name: DOMString) -> u32 { + handle_potential_webgl_error!( + self.base, + self.base.validate_ownership(program), + return constants::INVALID_INDEX + ); + let index = handle_potential_webgl_error!( + self.base, + program.get_uniform_block_index(block_name), + return constants::INVALID_INDEX + ); + index + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16 + #[allow(unsafe_code)] + fn GetActiveUniformBlockParameter( + &self, + cx: JSContext, + program: &WebGLProgram, + block_index: u32, + pname: u32, + ) -> JSVal { + handle_potential_webgl_error!( + self.base, + self.base.validate_ownership(program), + return NullValue() + ); + let values = handle_potential_webgl_error!( + self.base, + program.get_active_uniform_block_parameter(block_index, pname), + return NullValue() + ); + match pname { + constants::UNIFORM_BLOCK_BINDING | + constants::UNIFORM_BLOCK_DATA_SIZE | + constants::UNIFORM_BLOCK_ACTIVE_UNIFORMS => { + assert!(values.len() == 1); + UInt32Value(values[0] as u32) + }, + constants::UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES => unsafe { + let values = values.iter().map(|&v| v as u32).collect::<Vec<_>>(); + rooted!(in(*cx) let mut result = ptr::null_mut::<JSObject>()); + let _ = Uint32Array::create(*cx, CreateWith::Slice(&values), result.handle_mut()) + .unwrap(); + ObjectValue(result.get()) + }, + constants::UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER | + constants::UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER => { + assert!(values.len() == 1); + BooleanValue(values[0] != 0) + }, + _ => unreachable!(), + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16 + fn GetActiveUniformBlockName( + &self, + program: &WebGLProgram, + block_index: u32, + ) -> Option<DOMString> { + handle_potential_webgl_error!( + self.base, + self.base.validate_ownership(program), + return None + ); + let name = handle_potential_webgl_error!( + self.base, + program.get_active_uniform_block_name(block_index), + return None + ); + Some(DOMString::from(name)) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16 + fn UniformBlockBinding(&self, program: &WebGLProgram, block_index: u32, block_binding: u32) { + handle_potential_webgl_error!(self.base, self.base.validate_ownership(program), return); + + if block_binding >= self.base.limits().max_uniform_buffer_bindings { + return self.base.webgl_error(InvalidValue); + } + + handle_potential_webgl_error!( + self.base, + program.bind_uniform_block(block_index, block_binding), + return + ) + } } impl LayoutCanvasWebGLRenderingContextHelpers for LayoutDom<WebGL2RenderingContext> { diff --git a/components/script/dom/webglbuffer.rs b/components/script/dom/webglbuffer.rs index 182100ae41f..6e951f3f53e 100644 --- a/components/script/dom/webglbuffer.rs +++ b/components/script/dom/webglbuffer.rs @@ -75,7 +75,13 @@ impl WebGLBuffer { match usage { WebGLRenderingContextConstants::STREAM_DRAW | WebGLRenderingContextConstants::STATIC_DRAW | - WebGLRenderingContextConstants::DYNAMIC_DRAW => (), + WebGLRenderingContextConstants::DYNAMIC_DRAW | + WebGL2RenderingContextConstants::STATIC_READ | + WebGL2RenderingContextConstants::DYNAMIC_READ | + WebGL2RenderingContextConstants::STREAM_READ | + WebGL2RenderingContextConstants::STATIC_COPY | + WebGL2RenderingContextConstants::DYNAMIC_COPY | + WebGL2RenderingContextConstants::STREAM_COPY => (), _ => return Err(WebGLError::InvalidEnum), } diff --git a/components/script/dom/webglprogram.rs b/components/script/dom/webglprogram.rs index 3b040a170b1..556573aae2e 100644 --- a/components/script/dom/webglprogram.rs +++ b/components/script/dom/webglprogram.rs @@ -4,6 +4,7 @@ // https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl use crate::dom::bindings::cell::{DomRefCell, Ref}; +use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants2; use crate::dom::bindings::codegen::Bindings::WebGLProgramBinding; use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants; use crate::dom::bindings::inheritance::Castable; @@ -16,7 +17,9 @@ use crate::dom::webglrenderingcontext::WebGLRenderingContext; use crate::dom::webglshader::WebGLShader; use crate::dom::webgluniformlocation::WebGLUniformLocation; use canvas_traits::webgl::{webgl_channel, WebGLProgramId, WebGLResult}; -use canvas_traits::webgl::{ActiveAttribInfo, ActiveUniformInfo, WebGLCommand, WebGLError}; +use canvas_traits::webgl::{ + ActiveAttribInfo, ActiveUniformBlockInfo, ActiveUniformInfo, WebGLCommand, WebGLError, +}; use dom_struct::dom_struct; use fnv::FnvHashSet; use std::cell::Cell; @@ -34,6 +37,7 @@ pub struct WebGLProgram { vertex_shader: MutNullableDom<WebGLShader>, active_attribs: DomRefCell<Box<[ActiveAttribInfo]>>, active_uniforms: DomRefCell<Box<[ActiveUniformInfo]>>, + active_uniform_blocks: DomRefCell<Box<[ActiveUniformBlockInfo]>>, transform_feedback_varyings_length: Cell<i32>, transform_feedback_mode: Cell<i32>, } @@ -52,6 +56,7 @@ impl WebGLProgram { vertex_shader: Default::default(), active_attribs: DomRefCell::new(vec![].into()), active_uniforms: DomRefCell::new(vec![].into()), + active_uniform_blocks: DomRefCell::new(vec![].into()), transform_feedback_varyings_length: Default::default(), transform_feedback_mode: Default::default(), } @@ -143,6 +148,7 @@ impl WebGLProgram { .set(self.link_generation.get().checked_add(1).unwrap()); *self.active_attribs.borrow_mut() = Box::new([]); *self.active_uniforms.borrow_mut() = Box::new([]); + *self.active_uniform_blocks.borrow_mut() = Box::new([]); match self.fragment_shader.get() { Some(ref shader) if shader.successfully_compiled() => {}, @@ -197,6 +203,7 @@ impl WebGLProgram { .set(link_info.transform_feedback_mode); *self.active_attribs.borrow_mut() = link_info.active_attribs; *self.active_uniforms.borrow_mut() = link_info.active_uniforms; + *self.active_uniform_blocks.borrow_mut() = link_info.active_uniform_blocks; Ok(()) } @@ -208,6 +215,10 @@ impl WebGLProgram { Ref::map(self.active_uniforms.borrow(), |uniforms| &**uniforms) } + pub fn active_uniform_blocks(&self) -> Ref<[ActiveUniformBlockInfo]> { + Ref::map(self.active_uniform_blocks.borrow(), |blocks| &**blocks) + } + /// glValidateProgram pub fn validate(&self) -> WebGLResult<()> { if self.is_deleted() { @@ -413,6 +424,142 @@ impl WebGLProgram { ))) } + pub fn get_uniform_block_index(&self, name: DOMString) -> WebGLResult<u32> { + if !self.link_called.get() || self.is_deleted() { + return Err(WebGLError::InvalidOperation); + } + + if !validate_glsl_name(&name)? { + return Ok(constants2::INVALID_INDEX); + } + + let (sender, receiver) = webgl_channel().unwrap(); + self.upcast::<WebGLObject>() + .context() + .send_command(WebGLCommand::GetUniformBlockIndex( + self.id, + name.into(), + sender, + )); + Ok(receiver.recv().unwrap()) + } + + pub fn get_uniform_indices(&self, names: Vec<DOMString>) -> WebGLResult<Vec<u32>> { + if !self.link_called.get() || self.is_deleted() { + return Err(WebGLError::InvalidOperation); + } + + let validation_errors = names + .iter() + .map(|name| validate_glsl_name(&name)) + .collect::<Vec<_>>(); + let first_validation_error = validation_errors.iter().find(|result| result.is_err()); + if let Some(error) = first_validation_error { + return Err(error.unwrap_err()); + } + + let names = names + .iter() + .map(|name| name.to_string()) + .collect::<Vec<_>>(); + + let (sender, receiver) = webgl_channel().unwrap(); + self.upcast::<WebGLObject>() + .context() + .send_command(WebGLCommand::GetUniformIndices(self.id, names, sender)); + Ok(receiver.recv().unwrap()) + } + + pub fn get_active_uniforms(&self, indices: Vec<u32>, pname: u32) -> WebGLResult<Vec<i32>> { + if !self.is_linked() || self.is_deleted() { + return Err(WebGLError::InvalidOperation); + } + + match pname { + constants2::UNIFORM_TYPE | + constants2::UNIFORM_SIZE | + constants2::UNIFORM_BLOCK_INDEX | + constants2::UNIFORM_OFFSET | + constants2::UNIFORM_ARRAY_STRIDE | + constants2::UNIFORM_MATRIX_STRIDE | + constants2::UNIFORM_IS_ROW_MAJOR => {}, + _ => return Err(WebGLError::InvalidEnum), + } + + if indices.len() > self.active_uniforms.borrow().len() { + return Err(WebGLError::InvalidValue); + } + + let (sender, receiver) = webgl_channel().unwrap(); + self.upcast::<WebGLObject>() + .context() + .send_command(WebGLCommand::GetActiveUniforms( + self.id, indices, pname, sender, + )); + Ok(receiver.recv().unwrap()) + } + + pub fn get_active_uniform_block_parameter( + &self, + block_index: u32, + pname: u32, + ) -> WebGLResult<Vec<i32>> { + if !self.link_called.get() || self.is_deleted() { + return Err(WebGLError::InvalidOperation); + } + + if block_index as usize >= self.active_uniform_blocks.borrow().len() { + return Err(WebGLError::InvalidValue); + } + + match pname { + constants2::UNIFORM_BLOCK_BINDING | + constants2::UNIFORM_BLOCK_DATA_SIZE | + constants2::UNIFORM_BLOCK_ACTIVE_UNIFORMS | + constants2::UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES | + constants2::UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER | + constants2::UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER => {}, + _ => return Err(WebGLError::InvalidEnum), + } + + let (sender, receiver) = webgl_channel().unwrap(); + self.upcast::<WebGLObject>().context().send_command( + WebGLCommand::GetActiveUniformBlockParameter(self.id, block_index, pname, sender), + ); + Ok(receiver.recv().unwrap()) + } + + pub fn get_active_uniform_block_name(&self, block_index: u32) -> WebGLResult<String> { + if !self.link_called.get() || self.is_deleted() { + return Err(WebGLError::InvalidOperation); + } + + if block_index as usize >= self.active_uniforms.borrow().len() { + return Err(WebGLError::InvalidValue); + } + + let (sender, receiver) = webgl_channel().unwrap(); + self.upcast::<WebGLObject>().context().send_command( + WebGLCommand::GetActiveUniformBlockName(self.id, block_index, sender), + ); + Ok(receiver.recv().unwrap()) + } + + pub fn bind_uniform_block(&self, block_index: u32, block_binding: u32) -> WebGLResult<()> { + if block_index as usize >= self.active_uniform_blocks.borrow().len() { + return Err(WebGLError::InvalidValue); + } + + self.upcast::<WebGLObject>() + .context() + .send_command(WebGLCommand::UniformBlockBinding( + self.id, + block_index, + block_binding, + )); + Ok(()) + } + /// glGetProgramInfoLog pub fn get_info_log(&self) -> WebGLResult<String> { if self.is_deleted() { diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index 85930cd3529..68eccfa711d 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -1020,6 +1020,13 @@ impl WebGLRenderingContext { } } + pub fn buffer_usage(&self, usage: u32) -> WebGLResult<u32> { + match usage { + constants::STREAM_DRAW | constants::STATIC_DRAW | constants::DYNAMIC_DRAW => Ok(usage), + _ => Err(WebGLError::InvalidEnum), + } + } + pub fn create_vertex_array(&self) -> Option<DomRoot<WebGLVertexArrayObjectOES>> { let (sender, receiver) = webgl_channel().unwrap(); self.send_command(WebGLCommand::CreateVertexArray(sender)); @@ -1146,6 +1153,40 @@ impl WebGLRenderingContext { handle_potential_webgl_error!(self, bound_buffer.buffer_data(target, &data, usage)); } + #[allow(unsafe_code)] + pub fn buffer_sub_data( + &self, + target: u32, + offset: i64, + data: ArrayBufferViewOrArrayBuffer, + bound_buffer: Option<DomRoot<WebGLBuffer>>, + ) { + let bound_buffer = + handle_potential_webgl_error!(self, bound_buffer.ok_or(InvalidOperation), return); + + if offset < 0 { + return self.webgl_error(InvalidValue); + } + + let data = unsafe { + // Safe because we don't do anything with JS until the end of the method. + match data { + ArrayBufferViewOrArrayBuffer::ArrayBuffer(ref data) => data.as_slice(), + ArrayBufferViewOrArrayBuffer::ArrayBufferView(ref data) => data.as_slice(), + } + }; + if (offset as u64) + data.len() as u64 > bound_buffer.capacity() as u64 { + return self.webgl_error(InvalidValue); + } + let (sender, receiver) = ipc::bytes_channel().unwrap(); + self.send_command(WebGLCommand::BufferSubData( + target, + offset as isize, + receiver, + )); + sender.send(data).unwrap(); + } + pub fn bind_buffer_maybe( &self, slot: &MutNullableDom<WebGLBuffer>, @@ -1764,12 +1805,14 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 fn BufferData(&self, target: u32, data: Option<ArrayBufferViewOrArrayBuffer>, usage: u32) { + let usage = handle_potential_webgl_error!(self, self.buffer_usage(usage), return); let bound_buffer = handle_potential_webgl_error!(self, self.bound_buffer(target), return); self.buffer_data(target, data, usage, bound_buffer) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 fn BufferData_(&self, target: u32, size: i64, usage: u32) { + let usage = handle_potential_webgl_error!(self, self.buffer_usage(usage), return); let bound_buffer = handle_potential_webgl_error!(self, self.bound_buffer(target), return); self.buffer_data_(target, size, usage, bound_buffer) } @@ -1778,30 +1821,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { #[allow(unsafe_code)] fn BufferSubData(&self, target: u32, offset: i64, data: ArrayBufferViewOrArrayBuffer) { let bound_buffer = handle_potential_webgl_error!(self, self.bound_buffer(target), return); - let bound_buffer = - handle_potential_webgl_error!(self, bound_buffer.ok_or(InvalidOperation), return); - - if offset < 0 { - return self.webgl_error(InvalidValue); - } - - let data = unsafe { - // Safe because we don't do anything with JS until the end of the method. - match data { - ArrayBufferViewOrArrayBuffer::ArrayBuffer(ref data) => data.as_slice(), - ArrayBufferViewOrArrayBuffer::ArrayBufferView(ref data) => data.as_slice(), - } - }; - if (offset as u64) + data.len() as u64 > bound_buffer.capacity() as u64 { - return self.webgl_error(InvalidValue); - } - let (sender, receiver) = ipc::bytes_channel().unwrap(); - self.send_command(WebGLCommand::BufferSubData( - target, - offset as isize, - receiver, - )); - sender.send(data).unwrap(); + self.buffer_sub_data(target, offset, data, bound_buffer) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 diff --git a/components/script/dom/webidls/WebGL2RenderingContext.webidl b/components/script/dom/webidls/WebGL2RenderingContext.webidl index 71b5ab10034..1dca400b742 100644 --- a/components/script/dom/webidls/WebGL2RenderingContext.webidl +++ b/components/script/dom/webidls/WebGL2RenderingContext.webidl @@ -553,15 +553,15 @@ interface mixin WebGL2RenderingContextBase void resumeTransformFeedback(); /* Uniform Buffer Objects and Transform Feedback Buffers */ - // void bindBufferBase(GLenum target, GLuint index, WebGLBuffer? buffer); - // void bindBufferRange(GLenum target, GLuint index, WebGLBuffer? buffer, GLintptr offset, GLsizeiptr size); + void bindBufferBase(GLenum target, GLuint index, WebGLBuffer? buffer); + void bindBufferRange(GLenum target, GLuint index, WebGLBuffer? buffer, GLintptr offset, GLsizeiptr size); // any getIndexedParameter(GLenum target, GLuint index); - // sequence<GLuint>? getUniformIndices(WebGLProgram program, sequence<DOMString> uniformNames); - // any getActiveUniforms(WebGLProgram program, sequence<GLuint> uniformIndices, GLenum pname); - // GLuint getUniformBlockIndex(WebGLProgram program, DOMString uniformBlockName); - // any getActiveUniformBlockParameter(WebGLProgram program, GLuint uniformBlockIndex, GLenum pname); - // DOMString? getActiveUniformBlockName(WebGLProgram program, GLuint uniformBlockIndex); - // void uniformBlockBinding(WebGLProgram program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); + sequence<GLuint>? getUniformIndices(WebGLProgram program, sequence<DOMString> uniformNames); + any getActiveUniforms(WebGLProgram program, sequence<GLuint> uniformIndices, GLenum pname); + GLuint getUniformBlockIndex(WebGLProgram program, DOMString uniformBlockName); + any getActiveUniformBlockParameter(WebGLProgram program, GLuint uniformBlockIndex, GLenum pname); + DOMString? getActiveUniformBlockName(WebGLProgram program, GLuint uniformBlockIndex); + void uniformBlockBinding(WebGLProgram program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); /* Vertex Array Objects */ /*WebGLVertexArrayObject? createVertexArray(); |