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.rs5867
1 files changed, 3891 insertions, 1976 deletions
diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs
index 34f5b368a42..de5d01909a9 100644
--- a/components/script/dom/webglrenderingcontext.rs
+++ b/components/script/dom/webglrenderingcontext.rs
@@ -1,73 +1,89 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt};
-use canvas_traits::{CanvasCommonMsg, CanvasMsg, byte_swap, multiply_u8_pixel};
-use core::nonzero::NonZero;
-use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{self, WebGLContextAttributes};
-use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
-use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextMethods;
-use dom::bindings::codegen::UnionTypes::ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement;
-use dom::bindings::conversions::{ConversionResult, FromJSValConvertible, ToJSValConvertible};
-use dom::bindings::error::{Error, Fallible};
-use dom::bindings::inheritance::Castable;
-use dom::bindings::js::{JS, LayoutJS, MutNullableJS, Root};
-use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
-use dom::bindings::str::DOMString;
-use dom::event::{Event, EventBubbles, EventCancelable};
-use dom::globalscope::GlobalScope;
-use dom::htmlcanvaselement::HTMLCanvasElement;
-use dom::htmlcanvaselement::utils as canvas_utils;
-use dom::node::{Node, NodeDamage, window_from_node};
-use dom::webgl_validations::WebGLValidator;
-use dom::webgl_validations::tex_image_2d::{CommonTexImage2DValidator, CommonTexImage2DValidatorResult};
-use dom::webgl_validations::tex_image_2d::{TexImage2DValidator, TexImage2DValidatorResult};
-use dom::webgl_validations::types::{TexDataType, TexFormat, TexImageTarget};
-use dom::webglactiveinfo::WebGLActiveInfo;
-use dom::webglbuffer::WebGLBuffer;
-use dom::webglcontextevent::WebGLContextEvent;
-use dom::webglframebuffer::WebGLFramebuffer;
-use dom::webglprogram::WebGLProgram;
-use dom::webglrenderbuffer::WebGLRenderbuffer;
-use dom::webglshader::WebGLShader;
-use dom::webglshaderprecisionformat::WebGLShaderPrecisionFormat;
-use dom::webgltexture::{TexParameterValue, WebGLTexture};
-use dom::webgluniformlocation::WebGLUniformLocation;
-use dom::window::Window;
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use crate::dom::bindings::cell::{DomRefCell, Ref, RefMut};
+use crate::dom::bindings::codegen::Bindings::ANGLEInstancedArraysBinding::ANGLEInstancedArraysConstants;
+use crate::dom::bindings::codegen::Bindings::EXTBlendMinmaxBinding::EXTBlendMinmaxConstants;
+use crate::dom::bindings::codegen::Bindings::OESVertexArrayObjectBinding::OESVertexArrayObjectConstants;
+use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants;
+use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::TexImageSource;
+use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLContextAttributes;
+use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
+use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextMethods;
+use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer;
+use crate::dom::bindings::codegen::UnionTypes::Float32ArrayOrUnrestrictedFloatSequence;
+use crate::dom::bindings::codegen::UnionTypes::Int32ArrayOrLongSequence;
+use crate::dom::bindings::conversions::{DerivedFrom, ToJSValConvertible};
+use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
+use crate::dom::bindings::inheritance::Castable;
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
+use crate::dom::bindings::root::{Dom, DomOnceCell, DomRoot, LayoutDom, MutNullableDom};
+use crate::dom::bindings::str::DOMString;
+use crate::dom::element::cors_setting_for_element;
+use crate::dom::event::{Event, EventBubbles, EventCancelable};
+use crate::dom::htmlcanvaselement::utils as canvas_utils;
+use crate::dom::htmlcanvaselement::{HTMLCanvasElement, LayoutCanvasRenderingContextHelpers};
+use crate::dom::htmliframeelement::HTMLIFrameElement;
+use crate::dom::node::{document_from_node, window_from_node, Node, NodeDamage};
+use crate::dom::promise::Promise;
+use crate::dom::vertexarrayobject::VertexAttribData;
+use crate::dom::webgl_extensions::WebGLExtensions;
+use crate::dom::webgl_validations::tex_image_2d::{
+ CommonCompressedTexImage2DValidatorResult, CommonTexImage2DValidator,
+ CommonTexImage2DValidatorResult, CompressedTexImage2DValidator,
+ CompressedTexSubImage2DValidator, TexImage2DValidator, TexImage2DValidatorResult,
+};
+use crate::dom::webgl_validations::types::TexImageTarget;
+use crate::dom::webgl_validations::WebGLValidator;
+use crate::dom::webglactiveinfo::WebGLActiveInfo;
+use crate::dom::webglbuffer::WebGLBuffer;
+use crate::dom::webglcontextevent::WebGLContextEvent;
+use crate::dom::webglframebuffer::{
+ CompleteForRendering, WebGLFramebuffer, WebGLFramebufferAttachmentRoot,
+};
+use crate::dom::webglobject::WebGLObject;
+use crate::dom::webglprogram::WebGLProgram;
+use crate::dom::webglrenderbuffer::WebGLRenderbuffer;
+use crate::dom::webglshader::WebGLShader;
+use crate::dom::webglshaderprecisionformat::WebGLShaderPrecisionFormat;
+use crate::dom::webgltexture::{TexParameterValue, WebGLTexture};
+use crate::dom::webgluniformlocation::WebGLUniformLocation;
+use crate::dom::webglvertexarrayobject::WebGLVertexArrayObject;
+use crate::dom::webglvertexarrayobjectoes::WebGLVertexArrayObjectOES;
+use crate::dom::window::Window;
+use crate::script_runtime::JSContext as SafeJSContext;
+#[cfg(feature = "webgl_backtrace")]
+use backtrace::Backtrace;
+use canvas_traits::webgl::WebGLError::*;
+use canvas_traits::webgl::{
+ webgl_channel, AlphaTreatment, DOMToTextureCommand, GLContextAttributes, GLLimits, GlType,
+ Parameter, SizedDataType, TexDataType, TexFormat, TexParameter, WebGLChan, WebGLCommand,
+ WebGLCommandBacktrace, WebGLContextId, WebGLError, WebGLFramebufferBindingRequest, WebGLMsg,
+ WebGLMsgSender, WebGLProgramId, WebGLResult, WebGLSLVersion, WebGLSendResult, WebGLSender,
+ WebGLVersion, YAxisTreatment,
+};
use dom_struct::dom_struct;
-use euclid::size::Size2D;
-use ipc_channel::ipc::{self, IpcSender};
-use js::conversions::ConversionBehavior;
-use js::jsapi::{JSContext, JSObject, Type, Rooted};
-use js::jsval::{BooleanValue, DoubleValue, Int32Value, JSVal, NullValue, UndefinedValue};
-use js::typedarray::{TypedArray, TypedArrayElement, Float32, Int32};
-use net_traits::image::base::PixelFormat;
+use embedder_traits::EventLoopWaker;
+use euclid::default::{Point2D, Rect, Size2D};
+use ipc_channel::ipc::{self, IpcSharedMemory};
+use js::jsapi::{JSContext, JSObject, Type};
+use js::jsval::{BooleanValue, DoubleValue, Int32Value, JSVal, UInt32Value};
+use js::jsval::{NullValue, ObjectValue, UndefinedValue};
+use js::rust::CustomAutoRooterGuard;
+use js::typedarray::{
+ ArrayBufferView, CreateWith, Float32, Float32Array, Int32, Int32Array, Uint32Array,
+};
+use js::typedarray::{TypedArray, TypedArrayElementCreator};
use net_traits::image_cache::ImageResponse;
-use offscreen_gl_context::{GLContextAttributes, GLLimits};
-use script_traits::ScriptMsg as ConstellationMsg;
+use pixels::{self, PixelFormat};
+use script_layout_interface::HTMLCanvasDataSource;
+use serde::{Deserialize, Serialize};
+use servo_config::pref;
use std::cell::Cell;
-use webrender_traits;
-use webrender_traits::{WebGLCommand, WebGLError, WebGLFramebufferBindingRequest, WebGLParameter};
-use webrender_traits::WebGLError::*;
-
-type ImagePixelResult = Result<(Vec<u8>, Size2D<i32>), ()>;
-pub const MAX_UNIFORM_AND_ATTRIBUTE_LEN: usize = 256;
-
-macro_rules! handle_potential_webgl_error {
- ($context:ident, $call:expr, $return_on_error:expr) => {
- match $call {
- Ok(ret) => ret,
- Err(error) => {
- $context.webgl_error(error);
- $return_on_error
- }
- }
- };
- ($context:ident, $call:expr) => {
- handle_potential_webgl_error!($context, $call, ());
- };
-}
+use std::cmp;
+use std::ptr::{self, NonNull};
+use std::rc::Rc;
// From the GLES 2.0.25 spec, page 85:
//
@@ -82,25 +98,10 @@ macro_rules! handle_object_deletion {
if let Some(bound_object) = $binding.get() {
if bound_object.id() == $object.id() {
$binding.set(None);
+ if let Some(command) = $unbind_command {
+ $self_.send_command(command);
+ }
}
-
- if let Some(command) = $unbind_command {
- $self_.ipc_renderer
- .send(CanvasMsg::WebGL(command))
- .unwrap();
- }
- }
- };
-}
-
-macro_rules! object_binding_to_js_or_null {
- ($cx: expr, $binding:expr) => {
- {
- rooted!(in($cx) let mut rval = NullValue());
- if let Some(bound_object) = $binding.get() {
- bound_object.to_jsval($cx, rval.handle_mut());
- }
- rval.get()
}
};
}
@@ -111,143 +112,305 @@ fn has_invalid_blend_constants(arg1: u32, arg2: u32) -> bool {
(constants::ONE_MINUS_CONSTANT_COLOR, constants::ONE_MINUS_CONSTANT_ALPHA) => true,
(constants::ONE_MINUS_CONSTANT_COLOR, constants::CONSTANT_ALPHA) => true,
(constants::CONSTANT_COLOR, constants::ONE_MINUS_CONSTANT_ALPHA) => true,
- (_, _) => false
+ (_, _) => false,
}
}
-/// Set of bitflags for texture unpacking (texImage2d, etc...)
+pub fn uniform_get<T, F>(triple: (&WebGLRenderingContext, WebGLProgramId, i32), f: F) -> T
+where
+ F: FnOnce(WebGLProgramId, i32, WebGLSender<T>) -> WebGLCommand,
+ T: for<'de> Deserialize<'de> + Serialize,
+{
+ let (sender, receiver) = webgl_channel().unwrap();
+ triple.0.send_command(f(triple.1, triple.2, sender));
+ receiver.recv().unwrap()
+}
+
+#[allow(unsafe_code)]
+pub unsafe fn uniform_typed<T>(cx: *mut JSContext, value: &[T::Element]) -> JSVal
+where
+ T: TypedArrayElementCreator,
+{
+ rooted!(in(cx) let mut rval = ptr::null_mut::<JSObject>());
+ <TypedArray<T, *mut JSObject>>::create(cx, CreateWith::Slice(&value), rval.handle_mut())
+ .unwrap();
+ ObjectValue(rval.get())
+}
+
bitflags! {
- #[derive(HeapSizeOf, JSTraceable)]
- flags TextureUnpacking: u8 {
- const FLIP_Y_AXIS = 0x01,
- const PREMULTIPLY_ALPHA = 0x02,
- const CONVERT_COLORSPACE = 0x04,
+ /// Set of bitflags for texture unpacking (texImage2d, etc...)
+ #[derive(JSTraceable, MallocSizeOf)]
+ struct TextureUnpacking: u8 {
+ const FLIP_Y_AXIS = 0x01;
+ const PREMULTIPLY_ALPHA = 0x02;
+ const CONVERT_COLORSPACE = 0x04;
}
}
+#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf)]
+pub enum VertexAttrib {
+ Float(f32, f32, f32, f32),
+ Int(i32, i32, i32, i32),
+ Uint(u32, u32, u32, u32),
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum Operation {
+ Fallible,
+ Infallible,
+}
+
#[dom_struct]
pub struct WebGLRenderingContext {
reflector_: Reflector,
- #[ignore_heap_size_of = "Defined in ipc-channel"]
- ipc_renderer: IpcSender<CanvasMsg>,
- #[ignore_heap_size_of = "Defined in offscreen_gl_context"]
+ #[ignore_malloc_size_of = "Channels are hard"]
+ webgl_sender: WebGLMessageSender,
+ #[ignore_malloc_size_of = "Defined in webrender"]
+ webrender_image: webrender_api::ImageKey,
+ webgl_version: WebGLVersion,
+ glsl_version: WebGLSLVersion,
+ #[ignore_malloc_size_of = "Defined in surfman"]
limits: GLLimits,
- canvas: JS<HTMLCanvasElement>,
- #[ignore_heap_size_of = "Defined in webrender_traits"]
+ canvas: Dom<HTMLCanvasElement>,
+ #[ignore_malloc_size_of = "Defined in canvas_traits"]
last_error: Cell<Option<WebGLError>>,
+ texture_packing_alignment: Cell<u8>,
texture_unpacking_settings: Cell<TextureUnpacking>,
+ // TODO(nox): Should be Cell<u8>.
texture_unpacking_alignment: Cell<u32>,
- bound_framebuffer: MutNullableJS<WebGLFramebuffer>,
- bound_renderbuffer: MutNullableJS<WebGLRenderbuffer>,
- bound_texture_2d: MutNullableJS<WebGLTexture>,
- bound_texture_cube_map: MutNullableJS<WebGLTexture>,
- bound_buffer_array: MutNullableJS<WebGLBuffer>,
- bound_buffer_element_array: MutNullableJS<WebGLBuffer>,
- current_program: MutNullableJS<WebGLProgram>,
- #[ignore_heap_size_of = "Because it's small"]
- current_vertex_attrib_0: Cell<(f32, f32, f32, f32)>,
- #[ignore_heap_size_of = "Because it's small"]
- current_scissor: Cell<(i32, i32, i32, i32)>,
- #[ignore_heap_size_of = "Because it's small"]
+ bound_draw_framebuffer: MutNullableDom<WebGLFramebuffer>,
+ // TODO(mmatyas): This was introduced in WebGL2, but listed here because it's used by
+ // Textures and Renderbuffers, but such WebGLObjects have access only to the GL1 context.
+ bound_read_framebuffer: MutNullableDom<WebGLFramebuffer>,
+ bound_renderbuffer: MutNullableDom<WebGLRenderbuffer>,
+ bound_buffer_array: MutNullableDom<WebGLBuffer>,
+ current_program: MutNullableDom<WebGLProgram>,
+ current_vertex_attribs: DomRefCell<Box<[VertexAttrib]>>,
+ #[ignore_malloc_size_of = "Because it's small"]
+ current_scissor: Cell<(i32, i32, u32, u32)>,
+ #[ignore_malloc_size_of = "Because it's small"]
current_clear_color: Cell<(f32, f32, f32, f32)>,
+ size: Cell<Size2D<u32>>,
+ extension_manager: WebGLExtensions,
+ capabilities: Capabilities,
+ default_vao: DomOnceCell<WebGLVertexArrayObjectOES>,
+ current_vao: MutNullableDom<WebGLVertexArrayObjectOES>,
+ default_vao_webgl2: DomOnceCell<WebGLVertexArrayObject>,
+ current_vao_webgl2: MutNullableDom<WebGLVertexArrayObject>,
+ textures: Textures,
+ api_type: GlType,
}
impl WebGLRenderingContext {
- fn new_inherited(window: &Window,
- canvas: &HTMLCanvasElement,
- size: Size2D<i32>,
- attrs: GLContextAttributes)
- -> Result<WebGLRenderingContext, String> {
- let (sender, receiver) = ipc::channel().unwrap();
- let constellation_chan = window.upcast::<GlobalScope>().constellation_chan();
- constellation_chan.send(ConstellationMsg::CreateWebGLPaintThread(size, attrs, sender))
- .unwrap();
+ pub fn new_inherited(
+ window: &Window,
+ canvas: &HTMLCanvasElement,
+ webgl_version: WebGLVersion,
+ size: Size2D<u32>,
+ attrs: GLContextAttributes,
+ ) -> Result<WebGLRenderingContext, String> {
+ if pref!(webgl.testing.context_creation_error) {
+ return Err("WebGL context creation error forced by pref `webgl.testing.context_creation_error`".into());
+ }
+
+ let webgl_chan = match window.webgl_chan() {
+ Some(chan) => chan,
+ None => return Err("WebGL initialization failed early on".into()),
+ };
+
+ let (sender, receiver) = webgl_channel().unwrap();
+ webgl_chan
+ .send(WebGLMsg::CreateContext(webgl_version, size, attrs, sender))
+ .unwrap();
let result = receiver.recv().unwrap();
- result.map(|(ipc_renderer, context_limits)| {
- WebGLRenderingContext {
+ result.map(|ctx_data| {
+ let max_combined_texture_image_units = ctx_data.limits.max_combined_texture_image_units;
+ let max_vertex_attribs = ctx_data.limits.max_vertex_attribs as usize;
+ Self {
reflector_: Reflector::new(),
- ipc_renderer: ipc_renderer,
- limits: context_limits,
- canvas: JS::from_ref(canvas),
+ webgl_sender: WebGLMessageSender::new(
+ ctx_data.sender,
+ window.get_event_loop_waker(),
+ ),
+ webrender_image: ctx_data.image_key,
+ webgl_version,
+ glsl_version: ctx_data.glsl_version,
+ limits: ctx_data.limits,
+ canvas: Dom::from_ref(canvas),
last_error: Cell::new(None),
- texture_unpacking_settings: Cell::new(CONVERT_COLORSPACE),
+ texture_packing_alignment: Cell::new(4),
+ texture_unpacking_settings: Cell::new(TextureUnpacking::CONVERT_COLORSPACE),
texture_unpacking_alignment: Cell::new(4),
- bound_framebuffer: MutNullableJS::new(None),
- bound_texture_2d: MutNullableJS::new(None),
- bound_texture_cube_map: MutNullableJS::new(None),
- bound_buffer_array: MutNullableJS::new(None),
- bound_buffer_element_array: MutNullableJS::new(None),
- bound_renderbuffer: MutNullableJS::new(None),
- current_program: MutNullableJS::new(None),
- current_vertex_attrib_0: Cell::new((0f32, 0f32, 0f32, 1f32)),
+ bound_draw_framebuffer: MutNullableDom::new(None),
+ bound_read_framebuffer: MutNullableDom::new(None),
+ bound_buffer_array: MutNullableDom::new(None),
+ bound_renderbuffer: MutNullableDom::new(None),
+ current_program: MutNullableDom::new(None),
+ current_vertex_attribs: DomRefCell::new(
+ vec![VertexAttrib::Float(0f32, 0f32, 0f32, 1f32); max_vertex_attribs].into(),
+ ),
current_scissor: Cell::new((0, 0, size.width, size.height)),
- current_clear_color: Cell::new((0.0, 0.0, 0.0, 0.0))
+ // FIXME(#21718) The backend is allowed to choose a size smaller than
+ // what was requested
+ size: Cell::new(size),
+ current_clear_color: Cell::new((0.0, 0.0, 0.0, 0.0)),
+ extension_manager: WebGLExtensions::new(
+ webgl_version,
+ ctx_data.api_type,
+ ctx_data.glsl_version,
+ ),
+ capabilities: Default::default(),
+ default_vao: Default::default(),
+ current_vao: Default::default(),
+ default_vao_webgl2: Default::default(),
+ current_vao_webgl2: Default::default(),
+ textures: Textures::new(max_combined_texture_image_units),
+ api_type: ctx_data.api_type,
}
})
}
#[allow(unrooted_must_root)]
- pub fn new(window: &Window, canvas: &HTMLCanvasElement, size: Size2D<i32>, attrs: GLContextAttributes)
- -> Option<Root<WebGLRenderingContext>> {
- match WebGLRenderingContext::new_inherited(window, canvas, size, attrs) {
- Ok(ctx) => Some(reflect_dom_object(box ctx, window, WebGLRenderingContextBinding::Wrap)),
+ pub fn new(
+ window: &Window,
+ canvas: &HTMLCanvasElement,
+ webgl_version: WebGLVersion,
+ size: Size2D<u32>,
+ attrs: GLContextAttributes,
+ ) -> Option<DomRoot<WebGLRenderingContext>> {
+ match WebGLRenderingContext::new_inherited(window, canvas, webgl_version, size, attrs) {
+ Ok(ctx) => Some(reflect_dom_object(Box::new(ctx), window)),
Err(msg) => {
error!("Couldn't create WebGLRenderingContext: {}", msg);
- let event = WebGLContextEvent::new(window,
- atom!("webglcontextcreationerror"),
- EventBubbles::DoesNotBubble,
- EventCancelable::Cancelable,
- DOMString::from(msg));
+ let event = WebGLContextEvent::new(
+ window,
+ atom!("webglcontextcreationerror"),
+ EventBubbles::DoesNotBubble,
+ EventCancelable::Cancelable,
+ DOMString::from(msg),
+ );
event.upcast::<Event>().fire(canvas.upcast());
None
- }
+ },
}
}
+ pub fn webgl_version(&self) -> WebGLVersion {
+ self.webgl_version
+ }
+
pub fn limits(&self) -> &GLLimits {
&self.limits
}
- pub fn bound_texture_for_target(&self, target: &TexImageTarget) -> Option<Root<WebGLTexture>> {
- match *target {
- TexImageTarget::Texture2D => self.bound_texture_2d.get(),
- TexImageTarget::CubeMapPositiveX |
- TexImageTarget::CubeMapNegativeX |
- TexImageTarget::CubeMapPositiveY |
- TexImageTarget::CubeMapNegativeY |
- TexImageTarget::CubeMapPositiveZ |
- TexImageTarget::CubeMapNegativeZ => self.bound_texture_cube_map.get(),
- }
+ pub fn texture_unpacking_alignment(&self) -> u32 {
+ self.texture_unpacking_alignment.get()
+ }
+
+ pub fn current_vao(&self) -> DomRoot<WebGLVertexArrayObjectOES> {
+ self.current_vao.or_init(|| {
+ DomRoot::from_ref(
+ self.default_vao
+ .init_once(|| WebGLVertexArrayObjectOES::new(self, None)),
+ )
+ })
+ }
+
+ pub fn current_vao_webgl2(&self) -> DomRoot<WebGLVertexArrayObject> {
+ self.current_vao_webgl2.or_init(|| {
+ DomRoot::from_ref(
+ self.default_vao_webgl2
+ .init_once(|| WebGLVertexArrayObject::new(self, None)),
+ )
+ })
+ }
+
+ pub fn current_vertex_attribs(&self) -> RefMut<Box<[VertexAttrib]>> {
+ self.current_vertex_attribs.borrow_mut()
}
- pub fn recreate(&self, size: Size2D<i32>) {
- self.ipc_renderer.send(CanvasMsg::Common(CanvasCommonMsg::Recreate(size))).unwrap();
+ pub fn recreate(&self, size: Size2D<u32>) {
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.webgl_sender.send_resize(size, sender).unwrap();
+ // FIXME(#21718) The backend is allowed to choose a size smaller than
+ // what was requested
+ self.size.set(size);
+
+ if let Err(msg) = receiver.recv().unwrap() {
+ error!("Error resizing WebGLContext: {}", msg);
+ return;
+ };
// ClearColor needs to be restored because after a resize the GLContext is recreated
// and the framebuffer is cleared using the default black transparent color.
let color = self.current_clear_color.get();
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::ClearColor(color.0, color.1, color.2, color.3)))
- .unwrap();
+ self.send_command(WebGLCommand::ClearColor(color.0, color.1, color.2, color.3));
// WebGL Spec: Scissor rect must not change if the canvas is resized.
// See: webgl/conformance-1.0.3/conformance/rendering/gl-scissor-canvas-dimensions.html
// NativeContext handling library changes the scissor after a resize, so we need to reset the
// default scissor when the canvas was created or the last scissor that the user set.
let rect = self.current_scissor.get();
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Scissor(rect.0, rect.1, rect.2, rect.3)))
+ self.send_command(WebGLCommand::Scissor(rect.0, rect.1, rect.2, rect.3));
+
+ // Bound texture must not change when the canvas is resized.
+ // Right now surfman generates a new FBO and the bound texture is changed
+ // in order to create a new render to texture attachment.
+ // Send a command to re-bind the TEXTURE_2D, if any.
+ if let Some(texture) = self
+ .textures
+ .active_texture_slot(constants::TEXTURE_2D, self.webgl_version())
.unwrap()
+ .get()
+ {
+ self.send_command(WebGLCommand::BindTexture(
+ constants::TEXTURE_2D,
+ Some(texture.id()),
+ ));
+ }
+
+ // Bound framebuffer must not change when the canvas is resized.
+ // Right now surfman generates a new FBO on resize.
+ // Send a command to re-bind the framebuffer, if any.
+ if let Some(fbo) = self.bound_draw_framebuffer.get() {
+ let id = WebGLFramebufferBindingRequest::Explicit(fbo.id());
+ self.send_command(WebGLCommand::BindFramebuffer(constants::FRAMEBUFFER, id));
+ }
}
- pub fn ipc_renderer(&self) -> IpcSender<CanvasMsg> {
- self.ipc_renderer.clone()
+ pub(crate) fn webgl_sender(&self) -> WebGLMessageSender {
+ self.webgl_sender.clone()
+ }
+
+ pub fn context_id(&self) -> WebGLContextId {
+ self.webgl_sender.context_id()
+ }
+
+ pub fn onscreen(&self) -> bool {
+ self.canvas.upcast::<Node>().is_connected()
+ }
+
+ #[inline]
+ pub fn send_command(&self, command: WebGLCommand) {
+ self.webgl_sender
+ .send(command, capture_webgl_backtrace(self))
+ .unwrap();
+ }
+
+ pub fn send_command_ignored(&self, command: WebGLCommand) {
+ let _ = self
+ .webgl_sender
+ .send(command, capture_webgl_backtrace(self));
}
pub fn webgl_error(&self, err: WebGLError) {
// TODO(emilio): Add useful debug messages to this
- warn!("WebGL error: {:?}, previous error was {:?}", err, self.last_error.get());
+ warn!(
+ "WebGL error: {:?}, previous error was {:?}",
+ err,
+ self.last_error.get()
+ );
// If an error has been detected no further errors must be
// recorded until `getError` has been called
@@ -274,235 +437,274 @@ impl WebGLRenderingContext {
//
// The WebGL spec mentions a couple more operations that trigger
// this: clear() and getParameter(IMPLEMENTATION_COLOR_READ_*).
- fn validate_framebuffer_complete(&self) -> bool {
- match self.bound_framebuffer.get() {
- Some(fb) => match fb.check_status() {
- constants::FRAMEBUFFER_COMPLETE => return true,
- _ => {
- self.webgl_error(InvalidFramebufferOperation);
- return false;
- }
+ pub fn validate_framebuffer(&self) -> WebGLResult<()> {
+ match self.bound_draw_framebuffer.get() {
+ Some(fb) => match fb.check_status_for_rendering() {
+ CompleteForRendering::Complete => Ok(()),
+ CompleteForRendering::Incomplete => Err(InvalidFramebufferOperation),
+ CompleteForRendering::MissingColorAttachment => Err(InvalidOperation),
},
- // The default framebuffer is always complete.
- None => return true,
+ None => Ok(()),
}
}
- fn tex_parameter(&self, target: u32, name: u32, value: TexParameterValue) {
- let texture = match target {
- constants::TEXTURE_2D => self.bound_texture_2d.get(),
- constants::TEXTURE_CUBE_MAP => self.bound_texture_cube_map.get(),
- _ => return self.webgl_error(InvalidEnum),
+ pub fn validate_ownership<T>(&self, object: &T) -> WebGLResult<()>
+ where
+ T: DerivedFrom<WebGLObject>,
+ {
+ if self != object.upcast().context() {
+ return Err(InvalidOperation);
+ }
+ Ok(())
+ }
+
+ pub fn with_location<F>(&self, location: Option<&WebGLUniformLocation>, f: F)
+ where
+ F: FnOnce(&WebGLUniformLocation) -> WebGLResult<()>,
+ {
+ let location = match location {
+ Some(loc) => loc,
+ None => return,
};
- if let Some(texture) = texture {
- handle_potential_webgl_error!(self, texture.tex_parameter(target, name, value));
- } else {
- self.webgl_error(InvalidOperation)
+ match self.current_program.get() {
+ Some(ref program)
+ if program.id() == location.program_id() &&
+ program.link_generation() == location.link_generation() => {},
+ _ => return self.webgl_error(InvalidOperation),
}
+ handle_potential_webgl_error!(self, f(location));
+ }
+
+ pub fn textures(&self) -> &Textures {
+ &self.textures
}
- fn mark_as_dirty(&self) {
- self.canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
+ fn tex_parameter(&self, target: u32, param: u32, value: TexParameterValue) {
+ let texture_slot = handle_potential_webgl_error!(
+ self,
+ self.textures
+ .active_texture_slot(target, self.webgl_version()),
+ return
+ );
+ let texture =
+ handle_potential_webgl_error!(self, texture_slot.get().ok_or(InvalidOperation), return);
+
+ if !self
+ .extension_manager
+ .is_get_tex_parameter_name_enabled(param)
+ {
+ return self.webgl_error(InvalidEnum);
+ }
+
+ handle_potential_webgl_error!(self, texture.tex_parameter(param, value), return);
+
+ // Validate non filterable TEXTURE_2D data_types
+ if target != constants::TEXTURE_2D {
+ return;
+ }
+
+ let target = TexImageTarget::Texture2D;
+ if let Some(info) = texture.image_info_for_target(&target, 0) {
+ self.validate_filterable_texture(
+ &texture,
+ target,
+ 0,
+ info.internal_format(),
+ Size2D::new(info.width(), info.height()),
+ info.data_type().unwrap_or(TexDataType::UnsignedByte),
+ );
+ }
+ }
+
+ pub fn mark_as_dirty(&self) {
+ // If we have a bound framebuffer, then don't mark the canvas as dirty.
+ if self.bound_draw_framebuffer.get().is_some() {
+ return;
+ }
+
+ // Dirtying the canvas is unnecessary if we're actively displaying immersive
+ // XR content right now.
+ if self.global().as_window().in_immersive_xr_session() {
+ return;
+ }
+
+ self.canvas
+ .upcast::<Node>()
+ .dirty(NodeDamage::OtherNodeDamage);
+
+ let document = document_from_node(&*self.canvas);
+ document.add_dirty_webgl_canvas(self);
}
fn vertex_attrib(&self, indx: u32, x: f32, y: f32, z: f32, w: f32) {
- if indx > self.limits.max_vertex_attribs {
+ if indx >= self.limits.max_vertex_attribs {
return self.webgl_error(InvalidValue);
}
- if indx == 0 {
- self.current_vertex_attrib_0.set((x, y, z, w))
- }
+ match self.webgl_version() {
+ WebGLVersion::WebGL1 => self
+ .current_vao()
+ .set_vertex_attrib_type(indx, constants::FLOAT),
+ WebGLVersion::WebGL2 => self
+ .current_vao_webgl2()
+ .set_vertex_attrib_type(indx, constants::FLOAT),
+ };
+ self.current_vertex_attribs.borrow_mut()[indx as usize] = VertexAttrib::Float(x, y, z, w);
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::VertexAttrib(indx, x, y, z, w)))
- .unwrap();
+ self.send_command(WebGLCommand::VertexAttrib(indx, x, y, z, w));
}
- fn get_current_framebuffer_size(&self) -> Option<(i32, i32)> {
- match self.bound_framebuffer.get() {
+ pub fn get_current_framebuffer_size(&self) -> Option<(i32, i32)> {
+ match self.bound_draw_framebuffer.get() {
Some(fb) => return fb.size(),
// The window system framebuffer is bound
- None => return Some((self.DrawingBufferWidth(),
- self.DrawingBufferHeight())),
+ None => return Some((self.DrawingBufferWidth(), self.DrawingBufferHeight())),
}
}
- fn validate_stencil_actions(&self, action: u32) -> bool {
- match action {
- 0 | constants::KEEP | constants::REPLACE | constants::INCR | constants::DECR |
- constants::INVERT | constants::INCR_WRAP | constants::DECR_WRAP => true,
- _ => false,
- }
+ pub fn get_texture_packing_alignment(&self) -> u8 {
+ self.texture_packing_alignment.get()
}
- // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- // https://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml
- // https://www.khronos.org/registry/gles/specs/2.0/es_full_spec_2.0.25.pdf#nameddest=section-2.10.4
- fn validate_uniform_parameters<T>(&self,
- uniform: Option<&WebGLUniformLocation>,
- uniform_type: UniformSetterType,
- data: &[T]) -> bool {
- let uniform = match uniform {
- Some(uniform) => uniform,
- None => return false,
- };
-
- let program = self.current_program.get();
- match program {
- Some(ref program) if program.id() == uniform.program_id() => {},
- _ => {
- self.webgl_error(InvalidOperation);
- return false;
- },
- };
+ // LINEAR filtering may be forbidden when using WebGL extensions.
+ // https://www.khronos.org/registry/webgl/extensions/OES_texture_float_linear/
+ fn validate_filterable_texture(
+ &self,
+ texture: &WebGLTexture,
+ target: TexImageTarget,
+ level: u32,
+ internal_format: TexFormat,
+ size: Size2D<u32>,
+ data_type: TexDataType,
+ ) -> bool {
+ if self
+ .extension_manager
+ .is_filterable(data_type.as_gl_constant()) ||
+ !texture.is_using_linear_filtering()
+ {
+ return true;
+ }
- // TODO(emilio): Get more complex uniform info from ANGLE, and use it to
- // properly validate that the uniform setter type is compatible with the
- // uniform type, and that the uniform size matches.
- if data.len() % uniform_type.element_count() != 0 {
- self.webgl_error(InvalidOperation);
- return false;
- }
-
- true
- }
-
- /// Translates an image in rgba8 (red in the first byte) format to
- /// the format that was requested of TexImage.
- ///
- /// From the WebGL 1.0 spec, 5.14.8:
- ///
- /// "The source image data is conceptually first converted to
- /// the data type and format specified by the format and type
- /// arguments, and then transferred to the WebGL
- /// implementation. If a packed pixel format is specified
- /// which would imply loss of bits of precision from the image
- /// data, this loss of precision must occur."
- fn rgba8_image_to_tex_image_data(&self,
- format: TexFormat,
- data_type: TexDataType,
- pixels: Vec<u8>) -> Vec<u8> {
- // hint for vector allocation sizing.
- let pixel_count = pixels.len() / 4;
-
- match (format, data_type) {
- (TexFormat::RGBA, TexDataType::UnsignedByte) => pixels,
- (TexFormat::RGB, TexDataType::UnsignedByte) => pixels,
-
- (TexFormat::RGBA, TexDataType::UnsignedShort4444) => {
- let mut rgba4 = Vec::<u8>::with_capacity(pixel_count * 2);
- for rgba8 in pixels.chunks(4) {
- rgba4.write_u16::<NativeEndian>((rgba8[0] as u16 & 0xf0) << 8 |
- (rgba8[1] as u16 & 0xf0) << 4 |
- (rgba8[2] as u16 & 0xf0) |
- (rgba8[3] as u16 & 0xf0) >> 4).unwrap();
- }
- rgba4
- }
+ // Handle validation failed: LINEAR filtering not valid for this texture
+ // WebGL Conformance tests expect to fallback to [0, 0, 0, 255] RGBA UNSIGNED_BYTE
+ let data_type = TexDataType::UnsignedByte;
+ let expected_byte_length = size.area() * 4;
+ let mut pixels = vec![0u8; expected_byte_length as usize];
+ for rgba8 in pixels.chunks_mut(4) {
+ rgba8[3] = 255u8;
+ }
- (TexFormat::RGBA, TexDataType::UnsignedShort5551) => {
- let mut rgba5551 = Vec::<u8>::with_capacity(pixel_count * 2);
- for rgba8 in pixels.chunks(4) {
- rgba5551.write_u16::<NativeEndian>((rgba8[0] as u16 & 0xf8) << 8 |
- (rgba8[1] as u16 & 0xf8) << 3 |
- (rgba8[2] as u16 & 0xf8) >> 2 |
- (rgba8[3] as u16) >> 7).unwrap();
- }
- rgba5551
- }
+ // TODO(nox): AFAICT here we construct a RGBA8 array and then we
+ // convert it to whatever actual format we need, we should probably
+ // construct the desired format from the start.
+ self.tex_image_2d(
+ texture,
+ target,
+ data_type,
+ internal_format,
+ internal_format.to_unsized(),
+ level,
+ 0,
+ 1,
+ size,
+ TexSource::Pixels(TexPixels::new(
+ IpcSharedMemory::from_bytes(&pixels),
+ size,
+ PixelFormat::RGBA8,
+ true,
+ )),
+ );
- (TexFormat::RGB, TexDataType::UnsignedShort565) => {
- let mut rgb565 = Vec::<u8>::with_capacity(pixel_count * 2);
- for rgba8 in pixels.chunks(4) {
- rgb565.write_u16::<NativeEndian>((rgba8[0] as u16 & 0xf8) << 8 |
- (rgba8[1] as u16 & 0xfc) << 3 |
- (rgba8[2] as u16 & 0xf8) >> 3).unwrap();
- }
- rgb565
- }
+ false
+ }
- // Validation should have ensured that we only hit the
- // above cases, but we haven't turned the (format, type)
- // into an enum yet so there's a default case here.
- _ => unreachable!()
+ fn validate_stencil_actions(&self, action: u32) -> bool {
+ match action {
+ 0 |
+ constants::KEEP |
+ constants::REPLACE |
+ constants::INCR |
+ constants::DECR |
+ constants::INVERT |
+ constants::INCR_WRAP |
+ constants::DECR_WRAP => true,
+ _ => false,
}
}
- fn get_image_pixels(&self,
- source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>)
- -> ImagePixelResult {
- let source = match source {
- Some(s) => s,
- None => return Err(()),
- };
+ pub fn get_image_pixels(&self, source: TexImageSource) -> Fallible<Option<TexPixels>> {
+ Ok(Some(match source {
+ TexImageSource::ImageData(image_data) => TexPixels::new(
+ image_data.to_shared_memory(),
+ image_data.get_size(),
+ PixelFormat::RGBA8,
+ false,
+ ),
+ TexImageSource::HTMLImageElement(image) => {
+ let document = document_from_node(&*self.canvas);
+ if !image.same_origin(document.origin()) {
+ return Err(Error::Security);
+ }
- // NOTE: Getting the pixels probably can be short-circuited if some
- // parameter is invalid.
- //
- // Nontheless, since it's the error case, I'm not totally sure the
- // complexity is worth it.
- let (pixels, size) = match source {
- ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::ImageData(image_data) => {
- (image_data.get_data_array(), image_data.get_size())
- },
- ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLImageElement(image) => {
let img_url = match image.get_url() {
Some(url) => url,
- None => return Err(()),
+ None => return Ok(None),
};
let window = window_from_node(&*self.canvas);
+ let cors_setting = cors_setting_for_element(image.upcast());
- let img = match canvas_utils::request_image_from_cache(&window, img_url) {
- ImageResponse::Loaded(img) => img,
- ImageResponse::PlaceholderLoaded(_) | ImageResponse::None |
- ImageResponse::MetadataLoaded(_)
- => return Err(()),
- };
-
- let size = Size2D::new(img.width as i32, img.height as i32);
-
- // For now Servo's images are all stored as RGBA8 internally.
- let mut data = match img.format {
- PixelFormat::RGBA8 => img.bytes.to_vec(),
- _ => unimplemented!(),
- };
+ let img =
+ match canvas_utils::request_image_from_cache(&window, img_url, cors_setting) {
+ ImageResponse::Loaded(img, _) => img,
+ ImageResponse::PlaceholderLoaded(_, _) |
+ ImageResponse::None |
+ ImageResponse::MetadataLoaded(_) => return Ok(None),
+ };
- byte_swap(&mut data);
+ let size = Size2D::new(img.width, img.height);
- (data, size)
+ TexPixels::new(img.bytes.clone(), size, img.format, false)
},
// TODO(emilio): Getting canvas data is implemented in CanvasRenderingContext2D,
// but we need to refactor it moving it to `HTMLCanvasElement` and support
// WebGLContext (probably via GetPixels()).
- ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLCanvasElement(canvas) => {
- if let Some((mut data, size)) = canvas.fetch_all_data() {
- byte_swap(&mut data);
- (data, size)
+ TexImageSource::HTMLCanvasElement(canvas) => {
+ if !canvas.origin_is_clean() {
+ return Err(Error::Security);
+ }
+ if let Some((data, size)) = canvas.fetch_all_data() {
+ let data = data.unwrap_or_else(|| {
+ IpcSharedMemory::from_bytes(&vec![0; size.area() as usize * 4])
+ });
+ TexPixels::new(data, size, PixelFormat::BGRA8, true)
} else {
- return Err(());
+ return Ok(None);
}
},
- ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLVideoElement(_rooted_video)
- => unimplemented!(),
- };
-
- return Ok((pixels, size));
+ TexImageSource::HTMLVideoElement(video) => match video.get_current_frame_data() {
+ Some((data, size)) => {
+ let data = data.unwrap_or_else(|| {
+ IpcSharedMemory::from_bytes(&vec![0; size.area() as usize * 4])
+ });
+ TexPixels::new(data, size, PixelFormat::BGRA8, false)
+ },
+ None => return Ok(None),
+ },
+ }))
}
// TODO(emilio): Move this logic to a validator.
- #[allow(unsafe_code)]
- unsafe fn validate_tex_image_2d_data(&self,
- width: u32,
- height: u32,
- format: TexFormat,
- data_type: TexDataType,
- unpacking_alignment: u32,
- data: *mut JSObject,
- cx: *mut JSContext)
- -> Result<u32, ()> {
+ pub fn validate_tex_image_2d_data(
+ &self,
+ width: u32,
+ height: u32,
+ format: TexFormat,
+ data_type: TexDataType,
+ unpacking_alignment: u32,
+ data: Option<&ArrayBufferView>,
+ ) -> Result<u32, ()> {
let element_size = data_type.element_size();
let components_per_element = data_type.components_per_element();
let components = format.components();
@@ -512,23 +714,15 @@ impl WebGLRenderingContext {
// If it is UNSIGNED_BYTE, a Uint8Array must be supplied;
// if it is UNSIGNED_SHORT_5_6_5, UNSIGNED_SHORT_4_4_4_4,
// or UNSIGNED_SHORT_5_5_5_1, a Uint16Array must be supplied.
+ // or FLOAT, a Float32Array must be supplied.
// If the types do not match, an INVALID_OPERATION error is generated.
- typedarray!(in(cx) let typedarray_u8: Uint8Array = data);
- typedarray!(in(cx) let typedarray_u16: Uint16Array = data);
- let received_size = if data.is_null() {
- element_size
- } else {
- if typedarray_u16.is_ok() {
- 2
- } else if typedarray_u8.is_ok() {
- 1
- } else {
- self.webgl_error(InvalidOperation);
- return Err(());
- }
- };
+ let data_type_matches = data.as_ref().map_or(true, |buffer| {
+ Some(data_type.sized_data_type()) ==
+ array_buffer_type_to_sized_type(buffer.get_array_type()) &&
+ data_type.required_webgl_version() <= self.webgl_version()
+ });
- if received_size != element_size {
+ if !data_type_matches {
self.webgl_error(InvalidOperation);
return Err(());
}
@@ -547,388 +741,1535 @@ impl WebGLRenderingContext {
}
}
- /// Flips the pixels in the Vec on the Y axis if
- /// UNPACK_FLIP_Y_WEBGL is currently enabled.
- fn flip_teximage_y(&self,
- pixels: Vec<u8>,
- internal_format: TexFormat,
- data_type: TexDataType,
- width: usize,
- height: usize,
- unpacking_alignment: usize) -> Vec<u8> {
- if !self.texture_unpacking_settings.get().contains(FLIP_Y_AXIS) {
- return pixels;
+ pub fn tex_image_2d(
+ &self,
+ texture: &WebGLTexture,
+ target: TexImageTarget,
+ data_type: TexDataType,
+ internal_format: TexFormat,
+ format: TexFormat,
+ level: u32,
+ _border: u32,
+ unpacking_alignment: u32,
+ size: Size2D<u32>,
+ source: TexSource,
+ ) {
+ // TexImage2D depth is always equal to 1.
+ handle_potential_webgl_error!(
+ self,
+ texture.initialize(
+ target,
+ size.width,
+ size.height,
+ 1,
+ format,
+ level,
+ Some(data_type)
+ )
+ );
+
+ let settings = self.texture_unpacking_settings.get();
+ let dest_premultiplied = settings.contains(TextureUnpacking::PREMULTIPLY_ALPHA);
+
+ let y_axis_treatment = if settings.contains(TextureUnpacking::FLIP_Y_AXIS) {
+ YAxisTreatment::Flipped
+ } else {
+ YAxisTreatment::AsIs
+ };
+
+ let internal_format = self
+ .extension_manager
+ .get_effective_tex_internal_format(internal_format, data_type.as_gl_constant());
+
+ let effective_data_type = self
+ .extension_manager
+ .effective_type(data_type.as_gl_constant());
+
+ match source {
+ TexSource::Pixels(pixels) => {
+ let alpha_treatment = match (pixels.premultiplied, dest_premultiplied) {
+ (true, false) => Some(AlphaTreatment::Unmultiply),
+ (false, true) => Some(AlphaTreatment::Premultiply),
+ _ => None,
+ };
+
+ // TODO(emilio): convert colorspace if requested.
+ self.send_command(WebGLCommand::TexImage2D {
+ target: target.as_gl_constant(),
+ level,
+ internal_format,
+ size,
+ format,
+ data_type,
+ effective_data_type,
+ unpacking_alignment,
+ alpha_treatment,
+ y_axis_treatment,
+ pixel_format: pixels.pixel_format,
+ data: pixels.data.into(),
+ });
+ },
+ TexSource::BufferOffset(offset) => {
+ self.send_command(WebGLCommand::TexImage2DPBO {
+ target: target.as_gl_constant(),
+ level,
+ internal_format,
+ size,
+ format,
+ effective_data_type,
+ unpacking_alignment,
+ offset,
+ });
+ },
+ }
+
+ if let Some(fb) = self.bound_draw_framebuffer.get() {
+ fb.invalidate_texture(&*texture);
+ }
+ }
+
+ fn tex_sub_image_2d(
+ &self,
+ texture: DomRoot<WebGLTexture>,
+ target: TexImageTarget,
+ level: u32,
+ xoffset: i32,
+ yoffset: i32,
+ format: TexFormat,
+ data_type: TexDataType,
+ unpacking_alignment: u32,
+ pixels: TexPixels,
+ ) {
+ // We have already validated level
+ let image_info = match texture.image_info_for_target(&target, level) {
+ Some(info) => info,
+ None => return self.webgl_error(InvalidOperation),
+ };
+
+ // GL_INVALID_VALUE is generated if:
+ // - xoffset or yoffset is less than 0
+ // - x offset plus the width is greater than the texture width
+ // - y offset plus the height is greater than the texture height
+ if xoffset < 0 ||
+ (xoffset as u32 + pixels.size().width) > image_info.width() ||
+ yoffset < 0 ||
+ (yoffset as u32 + pixels.size().height) > image_info.height()
+ {
+ return self.webgl_error(InvalidValue);
+ }
+
+ // The unsized format must be compatible with the sized internal format
+ debug_assert!(!format.is_sized());
+ if format != image_info.internal_format().to_unsized() {
+ return self.webgl_error(InvalidOperation);
+ }
+
+ // See https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.1.6
+ if self.webgl_version() == WebGLVersion::WebGL1 {
+ if data_type != image_info.data_type().unwrap() {
+ return self.webgl_error(InvalidOperation);
+ }
+ }
+
+ let settings = self.texture_unpacking_settings.get();
+ let dest_premultiplied = settings.contains(TextureUnpacking::PREMULTIPLY_ALPHA);
+
+ let alpha_treatment = match (pixels.premultiplied, dest_premultiplied) {
+ (true, false) => Some(AlphaTreatment::Unmultiply),
+ (false, true) => Some(AlphaTreatment::Premultiply),
+ _ => None,
+ };
+
+ let y_axis_treatment = if settings.contains(TextureUnpacking::FLIP_Y_AXIS) {
+ YAxisTreatment::Flipped
+ } else {
+ YAxisTreatment::AsIs
+ };
+
+ let effective_data_type = self
+ .extension_manager
+ .effective_type(data_type.as_gl_constant());
+
+ // TODO(emilio): convert colorspace if requested.
+ self.send_command(WebGLCommand::TexSubImage2D {
+ target: target.as_gl_constant(),
+ level,
+ xoffset,
+ yoffset,
+ size: pixels.size(),
+ format,
+ data_type,
+ effective_data_type,
+ unpacking_alignment,
+ alpha_treatment,
+ y_axis_treatment,
+ pixel_format: pixels.pixel_format,
+ data: pixels.data.into(),
+ });
+ }
+
+ fn get_gl_extensions(&self) -> String {
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.send_command(WebGLCommand::GetExtensions(sender));
+ receiver.recv().unwrap()
+ }
+
+ pub(crate) fn layout_handle(&self) -> HTMLCanvasDataSource {
+ let image_key = self.webrender_image;
+ HTMLCanvasDataSource::WebGL(image_key)
+ }
+
+ // https://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays/
+ pub fn draw_arrays_instanced(
+ &self,
+ mode: u32,
+ first: i32,
+ count: i32,
+ primcount: i32,
+ ) -> WebGLResult<()> {
+ match mode {
+ constants::POINTS |
+ constants::LINE_STRIP |
+ constants::LINE_LOOP |
+ constants::LINES |
+ constants::TRIANGLE_STRIP |
+ constants::TRIANGLE_FAN |
+ constants::TRIANGLES => {},
+ _ => {
+ return Err(InvalidEnum);
+ },
+ }
+ if first < 0 || count < 0 || primcount < 0 {
+ return Err(InvalidValue);
}
- let cpp = (data_type.element_size() *
- internal_format.components() / data_type.components_per_element()) as usize;
+ let current_program = self.current_program.get().ok_or(InvalidOperation)?;
- let stride = (width * cpp + unpacking_alignment - 1) & !(unpacking_alignment - 1);
+ let required_len = if count > 0 {
+ first
+ .checked_add(count)
+ .map(|len| len as u32)
+ .ok_or(InvalidOperation)?
+ } else {
+ 0
+ };
- let mut flipped = Vec::<u8>::with_capacity(pixels.len());
+ match self.webgl_version() {
+ WebGLVersion::WebGL1 => self.current_vao().validate_for_draw(
+ required_len,
+ primcount as u32,
+ &current_program.active_attribs(),
+ )?,
+ WebGLVersion::WebGL2 => self.current_vao_webgl2().validate_for_draw(
+ required_len,
+ primcount as u32,
+ &current_program.active_attribs(),
+ )?,
+ };
- for y in 0..height {
- let flipped_y = height - 1 - y;
- let start = flipped_y * stride;
+ self.validate_framebuffer()?;
- flipped.extend_from_slice(&pixels[start..(start + width * cpp)]);
- flipped.extend(vec![0u8; stride - width * cpp]);
+ if count == 0 || primcount == 0 {
+ return Ok(());
}
- flipped
+ self.send_command(if primcount == 1 {
+ WebGLCommand::DrawArrays { mode, first, count }
+ } else {
+ WebGLCommand::DrawArraysInstanced {
+ mode,
+ first,
+ count,
+ primcount,
+ }
+ });
+ self.mark_as_dirty();
+ Ok(())
}
- /// Performs premultiplication of the pixels if
- /// UNPACK_PREMULTIPLY_ALPHA_WEBGL is currently enabled.
- fn premultiply_pixels(&self,
- format: TexFormat,
- data_type: TexDataType,
- pixels: Vec<u8>) -> Vec<u8> {
- if !self.texture_unpacking_settings.get().contains(PREMULTIPLY_ALPHA) {
- return pixels;
+ // 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,
+ ) -> WebGLResult<()> {
+ match mode {
+ constants::POINTS |
+ constants::LINE_STRIP |
+ constants::LINE_LOOP |
+ constants::LINES |
+ constants::TRIANGLE_STRIP |
+ constants::TRIANGLE_FAN |
+ constants::TRIANGLES => {},
+ _ => {
+ return Err(InvalidEnum);
+ },
+ }
+ if count < 0 || offset < 0 || primcount < 0 {
+ return Err(InvalidValue);
+ }
+ let type_size = match type_ {
+ constants::UNSIGNED_BYTE => 1,
+ constants::UNSIGNED_SHORT => 2,
+ constants::UNSIGNED_INT => match self.webgl_version() {
+ WebGLVersion::WebGL1 if self.extension_manager.is_element_index_uint_enabled() => 4,
+ WebGLVersion::WebGL2 => 4,
+ _ => return Err(InvalidEnum),
+ },
+ _ => return Err(InvalidEnum),
+ };
+ if offset % type_size != 0 {
+ return Err(InvalidOperation);
+ }
+
+ let current_program = self.current_program.get().ok_or(InvalidOperation)?;
+ let array_buffer = match self.webgl_version() {
+ WebGLVersion::WebGL1 => self.current_vao().element_array_buffer().get(),
+ WebGLVersion::WebGL2 => self.current_vao_webgl2().element_array_buffer().get(),
}
+ .ok_or(InvalidOperation)?;
- match (format, data_type) {
- (TexFormat::RGBA, TexDataType::UnsignedByte) => {
- let mut premul = Vec::<u8>::with_capacity(pixels.len());
- for rgba in pixels.chunks(4) {
- premul.push(multiply_u8_pixel(rgba[0], rgba[3]));
- premul.push(multiply_u8_pixel(rgba[1], rgba[3]));
- premul.push(multiply_u8_pixel(rgba[2], rgba[3]));
- premul.push(rgba[3]);
- }
- premul
+ if count > 0 && primcount > 0 {
+ // This operation cannot overflow in u64 and we know all those values are nonnegative.
+ let val = offset as u64 + (count as u64 * type_size as u64);
+ if val > array_buffer.capacity() as u64 {
+ return Err(InvalidOperation);
}
- (TexFormat::LuminanceAlpha, TexDataType::UnsignedByte) => {
- let mut premul = Vec::<u8>::with_capacity(pixels.len());
- for la in pixels.chunks(2) {
- premul.push(multiply_u8_pixel(la[0], la[1]));
- premul.push(la[1]);
- }
- premul
+ }
+
+ // TODO(nox): Pass the correct number of vertices required.
+ match self.webgl_version() {
+ WebGLVersion::WebGL1 => self.current_vao().validate_for_draw(
+ 0,
+ primcount as u32,
+ &current_program.active_attribs(),
+ )?,
+ WebGLVersion::WebGL2 => self.current_vao_webgl2().validate_for_draw(
+ 0,
+ primcount as u32,
+ &current_program.active_attribs(),
+ )?,
+ };
+
+ self.validate_framebuffer()?;
+
+ if count == 0 || primcount == 0 {
+ return Ok(());
+ }
+
+ let offset = offset as u32;
+ self.send_command(if primcount == 1 {
+ WebGLCommand::DrawElements {
+ mode,
+ count,
+ type_,
+ offset,
}
+ } else {
+ WebGLCommand::DrawElementsInstanced {
+ mode,
+ count,
+ type_,
+ offset,
+ primcount,
+ }
+ });
+ self.mark_as_dirty();
+ Ok(())
+ }
- (TexFormat::RGBA, TexDataType::UnsignedShort5551) => {
- let mut premul = Vec::<u8>::with_capacity(pixels.len());
- for mut rgba in pixels.chunks(2) {
- let pix = rgba.read_u16::<NativeEndian>().unwrap();
- if pix & (1 << 15) != 0 {
- premul.write_u16::<NativeEndian>(pix).unwrap();
- } else {
- premul.write_u16::<NativeEndian>(0).unwrap();
- }
- }
- premul
+ pub fn vertex_attrib_divisor(&self, index: u32, divisor: u32) {
+ if index >= self.limits.max_vertex_attribs {
+ return self.webgl_error(InvalidValue);
+ }
+
+ match self.webgl_version() {
+ WebGLVersion::WebGL1 => self.current_vao().vertex_attrib_divisor(index, divisor),
+ WebGLVersion::WebGL2 => self
+ .current_vao_webgl2()
+ .vertex_attrib_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
+ // can fail and that it is UB what happens in that case.
+ //
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#2.2
+ pub fn get_image_data(&self, mut size: Size2D<u32>) -> Option<Vec<u8>> {
+ handle_potential_webgl_error!(self, self.validate_framebuffer(), return None);
+
+ let (fb_width, fb_height) = handle_potential_webgl_error!(
+ self,
+ self.get_current_framebuffer_size().ok_or(InvalidOperation),
+ return None
+ );
+ size.width = cmp::min(size.width, fb_width as u32);
+ size.height = cmp::min(size.height, fb_height as u32);
+
+ let (sender, receiver) = ipc::bytes_channel().unwrap();
+ self.send_command(WebGLCommand::ReadPixels(
+ Rect::from_size(size),
+ constants::RGBA,
+ constants::UNSIGNED_BYTE,
+ sender,
+ ));
+ Some(receiver.recv().unwrap())
+ }
+
+ pub fn array_buffer(&self) -> Option<DomRoot<WebGLBuffer>> {
+ self.bound_buffer_array.get()
+ }
+
+ pub fn array_buffer_slot(&self) -> &MutNullableDom<WebGLBuffer> {
+ &self.bound_buffer_array
+ }
+
+ pub fn bound_buffer(&self, target: u32) -> WebGLResult<Option<DomRoot<WebGLBuffer>>> {
+ match target {
+ constants::ARRAY_BUFFER => Ok(self.bound_buffer_array.get()),
+ constants::ELEMENT_ARRAY_BUFFER => Ok(self.current_vao().element_array_buffer().get()),
+ _ => Err(WebGLError::InvalidEnum),
+ }
+ }
+
+ 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));
+ receiver
+ .recv()
+ .unwrap()
+ .map(|id| WebGLVertexArrayObjectOES::new(self, Some(id)))
+ }
+
+ pub fn create_vertex_array_webgl2(&self) -> Option<DomRoot<WebGLVertexArrayObject>> {
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.send_command(WebGLCommand::CreateVertexArray(sender));
+ receiver
+ .recv()
+ .unwrap()
+ .map(|id| WebGLVertexArrayObject::new(self, Some(id)))
+ }
+
+ pub fn delete_vertex_array(&self, vao: Option<&WebGLVertexArrayObjectOES>) {
+ if let Some(vao) = vao {
+ handle_potential_webgl_error!(self, self.validate_ownership(vao), return);
+ // The default vertex array has no id and should never be passed around.
+ assert!(vao.id().is_some());
+ if vao.is_deleted() {
+ return;
+ }
+ if vao == &*self.current_vao() {
+ // Setting it to None will make self.current_vao() reset it to the default one
+ // next time it is called.
+ self.current_vao.set(None);
+ self.send_command(WebGLCommand::BindVertexArray(None));
}
+ vao.delete(Operation::Infallible);
+ }
+ }
- (TexFormat::RGBA, TexDataType::UnsignedShort4444) => {
- let mut premul = Vec::<u8>::with_capacity(pixels.len());
- for mut rgba in pixels.chunks(2) {
- let pix = rgba.read_u16::<NativeEndian>().unwrap();
- let extend_to_8_bits = |val| { (val | val << 4) as u8 };
- let r = extend_to_8_bits(pix & 0x000f);
- let g = extend_to_8_bits((pix & 0x00f0) >> 4);
- let b = extend_to_8_bits((pix & 0x0f00) >> 8);
- let a = extend_to_8_bits((pix & 0xf000) >> 12);
-
- premul.write_u16::<NativeEndian>((multiply_u8_pixel(r, a) & 0xf0) as u16 >> 4 |
- (multiply_u8_pixel(g, a) & 0xf0) as u16 |
- ((multiply_u8_pixel(b, a) & 0xf0) as u16) << 4 |
- pix & 0xf000).unwrap();
- }
- premul
+ pub fn delete_vertex_array_webgl2(&self, vao: Option<&WebGLVertexArrayObject>) {
+ if let Some(vao) = vao {
+ handle_potential_webgl_error!(self, self.validate_ownership(vao), return);
+ // The default vertex array has no id and should never be passed around.
+ assert!(vao.id().is_some());
+ if vao.is_deleted() {
+ return;
}
+ if vao == &*self.current_vao_webgl2() {
+ // Setting it to None will make self.current_vao() reset it to the default one
+ // next time it is called.
+ self.current_vao_webgl2.set(None);
+ self.send_command(WebGLCommand::BindVertexArray(None));
+ }
+ vao.delete(Operation::Infallible);
+ }
+ }
- // Other formats don't have alpha, so return their data untouched.
- _ => pixels
- }
- }
-
- fn tex_image_2d(&self,
- texture: Root<WebGLTexture>,
- target: TexImageTarget,
- data_type: TexDataType,
- internal_format: TexFormat,
- level: u32,
- width: u32,
- height: u32,
- _border: u32,
- unpacking_alignment: u32,
- pixels: Vec<u8>) { // NB: pixels should NOT be premultipied
- // FINISHME: Consider doing premultiply and flip in a single mutable Vec.
- let pixels = self.premultiply_pixels(internal_format, data_type, pixels);
-
- let pixels = self.flip_teximage_y(pixels, internal_format, data_type,
- width as usize, height as usize, unpacking_alignment as usize);
-
- // TexImage2D depth is always equal to 1
- handle_potential_webgl_error!(self, texture.initialize(target,
- width,
- height, 1,
- internal_format,
- level,
- Some(data_type)));
-
- // Set the unpack alignment. For textures coming from arrays,
- // this will be the current value of the context's
- // GL_UNPACK_ALIGNMENT, while for textures from images or
- // canvas (produced by rgba8_image_to_tex_image_data()), it
- // will be 1.
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::PixelStorei(constants::UNPACK_ALIGNMENT, unpacking_alignment as i32)))
- .unwrap();
+ pub fn is_vertex_array(&self, vao: Option<&WebGLVertexArrayObjectOES>) -> bool {
+ vao.map_or(false, |vao| {
+ // The default vertex array has no id and should never be passed around.
+ assert!(vao.id().is_some());
+ self.validate_ownership(vao).is_ok() && vao.ever_bound() && !vao.is_deleted()
+ })
+ }
- // TODO(emilio): convert colorspace if requested
- let msg = WebGLCommand::TexImage2D(target.as_gl_constant(), level as i32,
- internal_format.as_gl_constant() as i32,
- width as i32, height as i32,
- internal_format.as_gl_constant(),
- data_type.as_gl_constant(), pixels);
+ pub fn is_vertex_array_webgl2(&self, vao: Option<&WebGLVertexArrayObject>) -> bool {
+ vao.map_or(false, |vao| {
+ // The default vertex array has no id and should never be passed around.
+ assert!(vao.id().is_some());
+ self.validate_ownership(vao).is_ok() && vao.ever_bound() && !vao.is_deleted()
+ })
+ }
- self.ipc_renderer
- .send(CanvasMsg::WebGL(msg))
- .unwrap();
+ pub fn bind_vertex_array(&self, vao: Option<&WebGLVertexArrayObjectOES>) {
+ if let Some(vao) = vao {
+ // The default vertex array has no id and should never be passed around.
+ assert!(vao.id().is_some());
+ handle_potential_webgl_error!(self, self.validate_ownership(vao), return);
+ if vao.is_deleted() {
+ return self.webgl_error(InvalidOperation);
+ }
+ vao.set_ever_bound();
+ }
+ self.send_command(WebGLCommand::BindVertexArray(vao.and_then(|vao| vao.id())));
+ // Setting it to None will make self.current_vao() reset it to the default one
+ // next time it is called.
+ self.current_vao.set(vao);
+ }
- if let Some(fb) = self.bound_framebuffer.get() {
- fb.invalidate_texture(&*texture);
+ pub fn bind_vertex_array_webgl2(&self, vao: Option<&WebGLVertexArrayObject>) {
+ if let Some(vao) = vao {
+ // The default vertex array has no id and should never be passed around.
+ assert!(vao.id().is_some());
+ handle_potential_webgl_error!(self, self.validate_ownership(vao), return);
+ if vao.is_deleted() {
+ return self.webgl_error(InvalidOperation);
+ }
+ vao.set_ever_bound();
}
+ self.send_command(WebGLCommand::BindVertexArray(vao.and_then(|vao| vao.id())));
+ // Setting it to None will make self.current_vao() reset it to the default one
+ // next time it is called.
+ self.current_vao_webgl2.set(vao);
}
- fn tex_sub_image_2d(&self,
- texture: Root<WebGLTexture>,
- target: TexImageTarget,
- level: u32,
- xoffset: i32,
- yoffset: i32,
- width: u32,
- height: u32,
- format: TexFormat,
- data_type: TexDataType,
- unpacking_alignment: u32,
- pixels: Vec<u8>) { // NB: pixels should NOT be premultipied
- // We have already validated level
- let image_info = texture.image_info_for_target(&target, level);
+ fn validate_blend_mode(&self, mode: u32) -> WebGLResult<()> {
+ match mode {
+ constants::FUNC_ADD | constants::FUNC_SUBTRACT | constants::FUNC_REVERSE_SUBTRACT => {
+ Ok(())
+ },
+ EXTBlendMinmaxConstants::MIN_EXT | EXTBlendMinmaxConstants::MAX_EXT
+ if self.extension_manager.is_blend_minmax_enabled() =>
+ {
+ Ok(())
+ },
+ _ => Err(InvalidEnum),
+ }
+ }
- // GL_INVALID_VALUE is generated if:
- // - xoffset or yoffset is less than 0
- // - x offset plus the width is greater than the texture width
- // - y offset plus the height is greater than the texture height
- if xoffset < 0 || (xoffset as u32 + width) > image_info.width() ||
- yoffset < 0 || (yoffset as u32 + height) > image_info.height() {
+ pub fn initialize_framebuffer(&self, clear_bits: u32) {
+ if clear_bits == 0 {
+ return;
+ }
+ self.send_command(WebGLCommand::InitializeFramebuffer {
+ color: clear_bits & constants::COLOR_BUFFER_BIT != 0,
+ depth: clear_bits & constants::DEPTH_BUFFER_BIT != 0,
+ stencil: clear_bits & constants::STENCIL_BUFFER_BIT != 0,
+ });
+ }
+
+ pub fn extension_manager(&self) -> &WebGLExtensions {
+ &self.extension_manager
+ }
+
+ #[allow(unsafe_code)]
+ pub fn buffer_data(
+ &self,
+ target: u32,
+ data: Option<ArrayBufferViewOrArrayBuffer>,
+ usage: u32,
+ bound_buffer: Option<DomRoot<WebGLBuffer>>,
+ ) {
+ let data = handle_potential_webgl_error!(self, data.ok_or(InvalidValue), return);
+ let bound_buffer =
+ handle_potential_webgl_error!(self, bound_buffer.ok_or(InvalidOperation), return);
+
+ 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(),
+ }
+ };
+ handle_potential_webgl_error!(self, bound_buffer.buffer_data(target, data, usage));
+ }
+
+ pub fn buffer_data_(
+ &self,
+ target: u32,
+ size: i64,
+ usage: u32,
+ bound_buffer: Option<DomRoot<WebGLBuffer>>,
+ ) {
+ let bound_buffer =
+ handle_potential_webgl_error!(self, bound_buffer.ok_or(InvalidOperation), return);
+
+ if size < 0 {
+ return self.webgl_error(InvalidValue);
+ }
+
+ // FIXME: Allocating a buffer based on user-requested size is
+ // not great, but we don't have a fallible allocation to try.
+ let data = vec![0u8; size as usize];
+ 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);
}
- // NB: format and internal_format must match.
- if format != image_info.internal_format().unwrap() ||
- data_type != image_info.data_type().unwrap() {
+ 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>,
+ target: u32,
+ buffer: Option<&WebGLBuffer>,
+ ) {
+ if let Some(buffer) = buffer {
+ handle_potential_webgl_error!(self, self.validate_ownership(buffer), return);
+
+ if buffer.is_marked_for_deletion() {
+ return self.webgl_error(InvalidOperation);
+ }
+ handle_potential_webgl_error!(self, buffer.set_target_maybe(target), return);
+ buffer.increment_attached_counter();
+ }
+
+ self.send_command(WebGLCommand::BindBuffer(target, buffer.map(|b| b.id())));
+ if let Some(old) = slot.get() {
+ old.decrement_attached_counter(Operation::Infallible);
+ }
+
+ slot.set(buffer);
+ }
+
+ pub fn current_program(&self) -> Option<DomRoot<WebGLProgram>> {
+ self.current_program.get()
+ }
+
+ pub fn uniform_check_program(
+ &self,
+ program: &WebGLProgram,
+ location: &WebGLUniformLocation,
+ ) -> WebGLResult<()> {
+ self.validate_ownership(program)?;
+
+ if program.is_deleted() ||
+ !program.is_linked() ||
+ self.context_id() != location.context_id() ||
+ program.id() != location.program_id() ||
+ program.link_generation() != location.link_generation()
+ {
+ return Err(InvalidOperation);
+ }
+
+ Ok(())
+ }
+
+ fn uniform_vec_section_int(
+ &self,
+ vec: Int32ArrayOrLongSequence,
+ offset: u32,
+ length: u32,
+ uniform_size: usize,
+ uniform_location: &WebGLUniformLocation,
+ ) -> WebGLResult<Vec<i32>> {
+ let vec = match vec {
+ Int32ArrayOrLongSequence::Int32Array(v) => v.to_vec(),
+ Int32ArrayOrLongSequence::LongSequence(v) => v,
+ };
+ self.uniform_vec_section::<i32>(vec, offset, length, uniform_size, uniform_location)
+ }
+
+ fn uniform_vec_section_float(
+ &self,
+ vec: Float32ArrayOrUnrestrictedFloatSequence,
+ offset: u32,
+ length: u32,
+ uniform_size: usize,
+ uniform_location: &WebGLUniformLocation,
+ ) -> WebGLResult<Vec<f32>> {
+ let vec = match vec {
+ Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
+ Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
+ };
+ self.uniform_vec_section::<f32>(vec, offset, length, uniform_size, uniform_location)
+ }
+
+ pub fn uniform_vec_section<T: Clone>(
+ &self,
+ vec: Vec<T>,
+ offset: u32,
+ length: u32,
+ uniform_size: usize,
+ uniform_location: &WebGLUniformLocation,
+ ) -> WebGLResult<Vec<T>> {
+ let offset = offset as usize;
+ if offset > vec.len() {
+ return Err(InvalidValue);
+ }
+
+ let length = if length > 0 {
+ length as usize
+ } else {
+ vec.len() - offset
+ };
+ if offset + length > vec.len() {
+ return Err(InvalidValue);
+ }
+
+ let vec = if offset == 0 && length == vec.len() {
+ vec
+ } else {
+ vec[offset..offset + length].to_vec()
+ };
+
+ if vec.len() < uniform_size || vec.len() % uniform_size != 0 {
+ return Err(InvalidValue);
+ }
+ if uniform_location.size().is_none() && vec.len() != uniform_size {
+ return Err(InvalidOperation);
+ }
+
+ Ok(vec)
+ }
+
+ pub fn uniform_matrix_section(
+ &self,
+ vec: Float32ArrayOrUnrestrictedFloatSequence,
+ offset: u32,
+ length: u32,
+ transpose: bool,
+ uniform_size: usize,
+ uniform_location: &WebGLUniformLocation,
+ ) -> WebGLResult<Vec<f32>> {
+ let vec = match vec {
+ Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
+ Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
+ };
+ if transpose {
+ return Err(InvalidValue);
+ }
+ self.uniform_vec_section::<f32>(vec, offset, length, uniform_size, uniform_location)
+ }
+
+ pub fn get_draw_framebuffer_slot(&self) -> &MutNullableDom<WebGLFramebuffer> {
+ &self.bound_draw_framebuffer
+ }
+
+ pub fn get_read_framebuffer_slot(&self) -> &MutNullableDom<WebGLFramebuffer> {
+ &self.bound_read_framebuffer
+ }
+
+ pub fn validate_new_framebuffer_binding(
+ &self,
+ framebuffer: Option<&WebGLFramebuffer>,
+ ) -> WebGLResult<()> {
+ if let Some(fb) = framebuffer {
+ self.validate_ownership(fb)?;
+ if fb.is_deleted() {
+ // From the WebGL spec:
+ //
+ // "An attempt to bind a deleted framebuffer will
+ // generate an INVALID_OPERATION error, and the
+ // current binding will remain untouched."
+ return Err(InvalidOperation);
+ }
+ }
+ Ok(())
+ }
+
+ pub fn bind_framebuffer_to(
+ &self,
+ target: u32,
+ framebuffer: Option<&WebGLFramebuffer>,
+ slot: &MutNullableDom<WebGLFramebuffer>,
+ ) {
+ match framebuffer {
+ Some(framebuffer) => framebuffer.bind(target),
+ None => {
+ // Bind the default framebuffer
+ let cmd =
+ WebGLCommand::BindFramebuffer(target, WebGLFramebufferBindingRequest::Default);
+ self.send_command(cmd);
+ },
+ }
+ slot.set(framebuffer);
+ }
+
+ pub fn renderbuffer_storage(
+ &self,
+ target: u32,
+ samples: i32,
+ internal_format: u32,
+ width: i32,
+ height: i32,
+ ) {
+ if target != constants::RENDERBUFFER {
+ return self.webgl_error(InvalidEnum);
+ }
+
+ let max = self.limits.max_renderbuffer_size;
+
+ if samples < 0 || width < 0 || width as u32 > max || height < 0 || height as u32 > max {
+ return self.webgl_error(InvalidValue);
+ }
+
+ let rb = handle_potential_webgl_error!(
+ self,
+ self.bound_renderbuffer.get().ok_or(InvalidOperation),
+ return
+ );
+ handle_potential_webgl_error!(
+ self,
+ rb.storage(self.api_type, samples, internal_format, width, height)
+ );
+ if let Some(fb) = self.bound_draw_framebuffer.get() {
+ fb.invalidate_renderbuffer(&*rb);
+ }
+
+ // FIXME: https://github.com/servo/servo/issues/13710
+ }
+
+ pub fn valid_color_attachment_enum(&self, attachment: u32) -> bool {
+ let last_slot = constants::COLOR_ATTACHMENT0 + self.limits().max_color_attachments - 1;
+ constants::COLOR_ATTACHMENT0 <= attachment && attachment <= last_slot
+ }
+
+ pub fn compressed_tex_image_2d<'a>(
+ &self,
+ target: u32,
+ level: i32,
+ internal_format: u32,
+ width: i32,
+ height: i32,
+ border: i32,
+ data: &'a [u8],
+ ) {
+ let validator = CompressedTexImage2DValidator::new(
+ self,
+ target,
+ level,
+ width,
+ height,
+ border,
+ internal_format,
+ data.len(),
+ );
+ let CommonCompressedTexImage2DValidatorResult {
+ texture,
+ target,
+ level,
+ width,
+ height,
+ compression,
+ } = match validator.validate() {
+ Ok(result) => result,
+ Err(_) => return,
+ };
+
+ if texture.is_immutable() {
return self.webgl_error(InvalidOperation);
}
- // FINISHME: Consider doing premultiply and flip in a single mutable Vec.
- let pixels = self.premultiply_pixels(format, data_type, pixels);
+ let size = Size2D::new(width, height);
+ let buff = IpcSharedMemory::from_bytes(data);
+ let pixels = TexPixels::from_array(buff, size);
+ let data = pixels.data;
+
+ handle_potential_webgl_error!(
+ self,
+ texture.initialize(
+ target,
+ size.width,
+ size.height,
+ 1,
+ compression.format,
+ level,
+ Some(TexDataType::UnsignedByte)
+ )
+ );
+
+ self.send_command(WebGLCommand::CompressedTexImage2D {
+ target: target.as_gl_constant(),
+ level,
+ internal_format,
+ size: Size2D::new(width, height),
+ data: data.into(),
+ });
- let pixels = self.flip_teximage_y(pixels, format, data_type,
- width as usize, height as usize, unpacking_alignment as usize);
+ if let Some(fb) = self.bound_draw_framebuffer.get() {
+ fb.invalidate_texture(&*texture);
+ }
+ }
- // Set the unpack alignment. For textures coming from arrays,
- // this will be the current value of the context's
- // GL_UNPACK_ALIGNMENT, while for textures from images or
- // canvas (produced by rgba8_image_to_tex_image_data()), it
- // will be 1.
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::PixelStorei(constants::UNPACK_ALIGNMENT, unpacking_alignment as i32)))
- .unwrap();
+ pub fn compressed_tex_sub_image_2d<'a>(
+ &self,
+ target: u32,
+ level: i32,
+ xoffset: i32,
+ yoffset: i32,
+ width: i32,
+ height: i32,
+ format: u32,
+ data: &'a [u8],
+ ) {
+ let validator = CompressedTexSubImage2DValidator::new(
+ self,
+ target,
+ level,
+ xoffset,
+ yoffset,
+ width,
+ height,
+ format,
+ data.len(),
+ );
+ let CommonCompressedTexImage2DValidatorResult {
+ texture: _,
+ target,
+ level,
+ width,
+ height,
+ ..
+ } = match validator.validate() {
+ Ok(result) => result,
+ Err(_) => return,
+ };
- // TODO(emilio): convert colorspace if requested
- let msg = WebGLCommand::TexSubImage2D(target.as_gl_constant(),
- level as i32, xoffset, yoffset,
- width as i32, height as i32,
- format.as_gl_constant(),
- data_type.as_gl_constant(), pixels);
+ let buff = IpcSharedMemory::from_bytes(data);
+ let pixels = TexPixels::from_array(buff, Size2D::new(width, height));
+ let data = pixels.data;
- self.ipc_renderer
- .send(CanvasMsg::WebGL(msg))
- .unwrap()
+ self.send_command(WebGLCommand::CompressedTexSubImage2D {
+ target: target.as_gl_constant(),
+ level: level as i32,
+ xoffset,
+ yoffset,
+ size: Size2D::new(width, height),
+ format,
+ data: data.into(),
+ });
+ }
+
+ pub fn uniform1iv(
+ &self,
+ location: Option<&WebGLUniformLocation>,
+ val: Int32ArrayOrLongSequence,
+ src_offset: u32,
+ src_length: u32,
+ ) {
+ self.with_location(location, |location| {
+ match location.type_() {
+ constants::BOOL |
+ constants::INT |
+ constants::SAMPLER_2D |
+ constants::SAMPLER_CUBE => {},
+ _ => return Err(InvalidOperation),
+ }
+
+ let val = self.uniform_vec_section_int(val, src_offset, src_length, 1, location)?;
+
+ match location.type_() {
+ constants::SAMPLER_2D | constants::SAMPLER_CUBE => {
+ for &v in val
+ .iter()
+ .take(cmp::min(location.size().unwrap_or(1) as usize, val.len()))
+ {
+ if v < 0 || v as u32 >= self.limits.max_combined_texture_image_units {
+ return Err(InvalidValue);
+ }
+ }
+ },
+ _ => {},
+ }
+ self.send_command(WebGLCommand::Uniform1iv(location.id(), val));
+ Ok(())
+ });
+ }
+
+ pub fn uniform1fv(
+ &self,
+ location: Option<&WebGLUniformLocation>,
+ val: Float32ArrayOrUnrestrictedFloatSequence,
+ src_offset: u32,
+ src_length: u32,
+ ) {
+ self.with_location(location, |location| {
+ match location.type_() {
+ constants::BOOL | constants::FLOAT => {},
+ _ => return Err(InvalidOperation),
+ }
+ let val = self.uniform_vec_section_float(val, src_offset, src_length, 1, location)?;
+ self.send_command(WebGLCommand::Uniform1fv(location.id(), val));
+ Ok(())
+ });
+ }
+
+ pub fn uniform2fv(
+ &self,
+ location: Option<&WebGLUniformLocation>,
+ val: Float32ArrayOrUnrestrictedFloatSequence,
+ src_offset: u32,
+ src_length: u32,
+ ) {
+ self.with_location(location, |location| {
+ match location.type_() {
+ constants::BOOL_VEC2 | constants::FLOAT_VEC2 => {},
+ _ => return Err(InvalidOperation),
+ }
+ let val = self.uniform_vec_section_float(val, src_offset, src_length, 2, location)?;
+ self.send_command(WebGLCommand::Uniform2fv(location.id(), val));
+ Ok(())
+ });
+ }
+
+ pub fn uniform2iv(
+ &self,
+ location: Option<&WebGLUniformLocation>,
+ val: Int32ArrayOrLongSequence,
+ src_offset: u32,
+ src_length: u32,
+ ) {
+ self.with_location(location, |location| {
+ match location.type_() {
+ constants::BOOL_VEC2 | constants::INT_VEC2 => {},
+ _ => return Err(InvalidOperation),
+ }
+ let val = self.uniform_vec_section_int(val, src_offset, src_length, 2, location)?;
+ self.send_command(WebGLCommand::Uniform2iv(location.id(), val));
+ Ok(())
+ });
+ }
+
+ pub fn uniform3fv(
+ &self,
+ location: Option<&WebGLUniformLocation>,
+ val: Float32ArrayOrUnrestrictedFloatSequence,
+ src_offset: u32,
+ src_length: u32,
+ ) {
+ self.with_location(location, |location| {
+ match location.type_() {
+ constants::BOOL_VEC3 | constants::FLOAT_VEC3 => {},
+ _ => return Err(InvalidOperation),
+ }
+ let val = self.uniform_vec_section_float(val, src_offset, src_length, 3, location)?;
+ self.send_command(WebGLCommand::Uniform3fv(location.id(), val));
+ Ok(())
+ });
+ }
+
+ pub fn uniform3iv(
+ &self,
+ location: Option<&WebGLUniformLocation>,
+ val: Int32ArrayOrLongSequence,
+ src_offset: u32,
+ src_length: u32,
+ ) {
+ self.with_location(location, |location| {
+ match location.type_() {
+ constants::BOOL_VEC3 | constants::INT_VEC3 => {},
+ _ => return Err(InvalidOperation),
+ }
+ let val = self.uniform_vec_section_int(val, src_offset, src_length, 3, location)?;
+ self.send_command(WebGLCommand::Uniform3iv(location.id(), val));
+ Ok(())
+ });
+ }
+
+ pub fn uniform4iv(
+ &self,
+ location: Option<&WebGLUniformLocation>,
+ val: Int32ArrayOrLongSequence,
+ src_offset: u32,
+ src_length: u32,
+ ) {
+ self.with_location(location, |location| {
+ match location.type_() {
+ constants::BOOL_VEC4 | constants::INT_VEC4 => {},
+ _ => return Err(InvalidOperation),
+ }
+ let val = self.uniform_vec_section_int(val, src_offset, src_length, 4, location)?;
+ self.send_command(WebGLCommand::Uniform4iv(location.id(), val));
+ Ok(())
+ });
+ }
+
+ pub fn uniform4fv(
+ &self,
+ location: Option<&WebGLUniformLocation>,
+ val: Float32ArrayOrUnrestrictedFloatSequence,
+ src_offset: u32,
+ src_length: u32,
+ ) {
+ self.with_location(location, |location| {
+ match location.type_() {
+ constants::BOOL_VEC4 | constants::FLOAT_VEC4 => {},
+ _ => return Err(InvalidOperation),
+ }
+ let val = self.uniform_vec_section_float(val, src_offset, src_length, 4, location)?;
+ self.send_command(WebGLCommand::Uniform4fv(location.id(), val));
+ Ok(())
+ });
+ }
+
+ pub fn uniform_matrix_2fv(
+ &self,
+ location: Option<&WebGLUniformLocation>,
+ transpose: bool,
+ val: Float32ArrayOrUnrestrictedFloatSequence,
+ src_offset: u32,
+ src_length: u32,
+ ) {
+ self.with_location(location, |location| {
+ match location.type_() {
+ constants::FLOAT_MAT2 => {},
+ _ => return Err(InvalidOperation),
+ }
+ let val =
+ self.uniform_matrix_section(val, src_offset, src_length, transpose, 4, location)?;
+ self.send_command(WebGLCommand::UniformMatrix2fv(location.id(), val));
+ Ok(())
+ });
+ }
+
+ pub fn uniform_matrix_3fv(
+ &self,
+ location: Option<&WebGLUniformLocation>,
+ transpose: bool,
+ val: Float32ArrayOrUnrestrictedFloatSequence,
+ src_offset: u32,
+ src_length: u32,
+ ) {
+ self.with_location(location, |location| {
+ match location.type_() {
+ constants::FLOAT_MAT3 => {},
+ _ => return Err(InvalidOperation),
+ }
+ let val =
+ self.uniform_matrix_section(val, src_offset, src_length, transpose, 9, location)?;
+ self.send_command(WebGLCommand::UniformMatrix3fv(location.id(), val));
+ Ok(())
+ });
+ }
+
+ pub fn uniform_matrix_4fv(
+ &self,
+ location: Option<&WebGLUniformLocation>,
+ transpose: bool,
+ val: Float32ArrayOrUnrestrictedFloatSequence,
+ src_offset: u32,
+ src_length: u32,
+ ) {
+ self.with_location(location, |location| {
+ match location.type_() {
+ constants::FLOAT_MAT4 => {},
+ _ => return Err(InvalidOperation),
+ }
+ let val =
+ self.uniform_matrix_section(val, src_offset, src_length, transpose, 16, location)?;
+ self.send_command(WebGLCommand::UniformMatrix4fv(location.id(), val));
+ Ok(())
+ });
}
- // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14
- fn validate_feature_enum(&self, cap: u32) -> bool {
- match cap {
- constants::BLEND | constants::CULL_FACE | constants::DEPTH_TEST | constants::DITHER |
- constants::POLYGON_OFFSET_FILL | constants::SAMPLE_ALPHA_TO_COVERAGE | constants::SAMPLE_COVERAGE |
- constants::SAMPLE_COVERAGE_INVERT | constants::SCISSOR_TEST | constants::STENCIL_TEST => true,
+ pub fn get_buffer_param(&self, buffer: Option<DomRoot<WebGLBuffer>>, parameter: u32) -> JSVal {
+ let buffer =
+ handle_potential_webgl_error!(self, buffer.ok_or(InvalidOperation), return NullValue());
+
+ match parameter {
+ constants::BUFFER_SIZE => Int32Value(buffer.capacity() as i32),
+ constants::BUFFER_USAGE => Int32Value(buffer.usage() as i32),
_ => {
self.webgl_error(InvalidEnum);
- false
+ NullValue()
},
}
}
}
-impl Drop for WebGLRenderingContext {
- fn drop(&mut self) {
- self.ipc_renderer.send(CanvasMsg::Common(CanvasCommonMsg::Close)).unwrap();
- }
+#[cfg(not(feature = "webgl_backtrace"))]
+#[inline]
+pub fn capture_webgl_backtrace<T: DomObject>(_: &T) -> WebGLCommandBacktrace {
+ WebGLCommandBacktrace {}
}
-// FIXME: After [1] lands and the relevant Servo and codegen PR too, we should
-// convert all our raw JSObject pointers to proper types.
-//
-// [1]: https://github.com/servo/rust-mozjs/pull/304
-#[allow(unsafe_code)]
-unsafe fn typed_array_or_sequence_to_vec<T>(cx: *mut JSContext,
- sequence_or_abv: *mut JSObject,
- config: <T::Element as FromJSValConvertible>::Config)
- -> Result<Vec<T::Element>, Error>
- where T: TypedArrayElement,
- T::Element: FromJSValConvertible + Clone,
- <T::Element as FromJSValConvertible>::Config: Clone,
-{
- // TODO(servo/rust-mozjs#330): replace this with a macro that supports generic types.
- let mut typed_array_root = Rooted::new_unrooted();
- let typed_array: Option<TypedArray<T>> =
- TypedArray::from(cx, &mut typed_array_root, sequence_or_abv).ok();
- if let Some(mut typed_array) = typed_array {
- return Ok(typed_array.as_slice().to_vec());
- }
- assert!(!sequence_or_abv.is_null());
- rooted!(in(cx) let mut val = UndefinedValue());
- sequence_or_abv.to_jsval(cx, val.handle_mut());
-
- match Vec::<T::Element>::from_jsval(cx, val.handle(), config) {
- Ok(ConversionResult::Success(v)) => Ok(v),
- Ok(ConversionResult::Failure(error)) => Err(Error::Type(error.into_owned())),
- // FIXME: What to do here? Generated code only aborts the execution of
- // the script.
- Err(err) => panic!("unexpected conversion error: {:?}", err),
+#[cfg(feature = "webgl_backtrace")]
+#[cfg_attr(feature = "webgl_backtrace", allow(unsafe_code))]
+pub fn capture_webgl_backtrace<T: DomObject>(obj: &T) -> WebGLCommandBacktrace {
+ let bt = Backtrace::new();
+ unsafe {
+ capture_stack!(in(*obj.global().get_cx()) let stack);
+ WebGLCommandBacktrace {
+ backtrace: format!("{:?}", bt),
+ js_backtrace: stack.and_then(|s| s.as_string(None, js::jsapi::StackFormat::Default)),
+ }
}
}
-#[allow(unsafe_code)]
-unsafe fn fallible_array_buffer_view_to_vec(cx: *mut JSContext, abv: *mut JSObject) -> Result<Vec<u8>, Error>
-{
- assert!(!abv.is_null());
- typedarray!(in(cx) let array_buffer_view: ArrayBufferView = abv);
- match array_buffer_view {
- Ok(mut v) => Ok(v.as_slice().to_vec()),
- Err(_) => Err(Error::Type("Not an ArrayBufferView".to_owned())),
+impl Drop for WebGLRenderingContext {
+ fn drop(&mut self) {
+ let _ = self.webgl_sender.send_remove();
}
}
impl WebGLRenderingContextMethods for WebGLRenderingContext {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1
- fn Canvas(&self) -> Root<HTMLCanvasElement> {
- Root::from_ref(&*self.canvas)
+ fn Canvas(&self) -> DomRoot<HTMLCanvasElement> {
+ DomRoot::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();
+ self.send_command(WebGLCommand::Flush);
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11
fn Finish(&self) {
- let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap();
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Finish(sender)))
- .unwrap();
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.send_command(WebGLCommand::Finish(sender));
receiver.recv().unwrap()
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1
fn DrawingBufferWidth(&self) -> i32 {
- let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap();
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::DrawingBufferWidth(sender)))
- .unwrap();
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.send_command(WebGLCommand::DrawingBufferWidth(sender));
receiver.recv().unwrap()
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1
fn DrawingBufferHeight(&self) -> i32 {
- let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap();
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::DrawingBufferHeight(sender)))
- .unwrap();
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.send_command(WebGLCommand::DrawingBufferHeight(sender));
receiver.recv().unwrap()
}
- #[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5
- unsafe fn GetBufferParameter(&self, _cx: *mut JSContext, target: u32, parameter: u32) -> JSVal {
- let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap();
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::GetBufferParameter(target, parameter, sender)))
- .unwrap();
- match handle_potential_webgl_error!(self, receiver.recv().unwrap(), WebGLParameter::Invalid) {
- WebGLParameter::Int(val) => Int32Value(val),
- WebGLParameter::Bool(_) => panic!("Buffer parameter should not be bool"),
- WebGLParameter::Float(_) => panic!("Buffer parameter should not be float"),
- WebGLParameter::FloatArray(_) => panic!("Buffer parameter should not be float array"),
- WebGLParameter::String(_) => panic!("Buffer parameter should not be string"),
- WebGLParameter::Invalid => NullValue(),
- }
+ fn GetBufferParameter(&self, _cx: SafeJSContext, target: u32, parameter: u32) -> JSVal {
+ let buffer =
+ handle_potential_webgl_error!(self, self.bound_buffer(target), return NullValue());
+ self.get_buffer_param(buffer, parameter)
}
#[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
- unsafe fn GetParameter(&self, cx: *mut JSContext, parameter: u32) -> JSVal {
- // Handle the GL_*_BINDING without going all the way
- // to the GL, since we would just need to map back from GL's
- // returned ID to the WebGL* object we're tracking.
- match parameter {
- constants::ARRAY_BUFFER_BINDING =>
- return object_binding_to_js_or_null!(cx, &self.bound_buffer_array),
- constants::ELEMENT_ARRAY_BUFFER_BINDING =>
- return object_binding_to_js_or_null!(cx, &self.bound_buffer_element_array),
- constants::FRAMEBUFFER_BINDING =>
- return object_binding_to_js_or_null!(cx, &self.bound_framebuffer),
- constants::RENDERBUFFER_BINDING =>
- return object_binding_to_js_or_null!(cx, &self.bound_renderbuffer),
- constants::TEXTURE_BINDING_2D =>
- return object_binding_to_js_or_null!(cx, &self.bound_texture_2d),
- constants::TEXTURE_BINDING_CUBE_MAP =>
- return object_binding_to_js_or_null!(cx, &self.bound_texture_cube_map),
+ fn GetParameter(&self, cx: SafeJSContext, parameter: u32) -> JSVal {
+ if !self
+ .extension_manager
+ .is_get_parameter_name_enabled(parameter)
+ {
+ self.webgl_error(WebGLError::InvalidEnum);
+ return NullValue();
+ }
+ match parameter {
+ constants::ARRAY_BUFFER_BINDING => unsafe {
+ return optional_root_object_to_js_or_null!(*cx, &self.bound_buffer_array.get());
+ },
+ constants::CURRENT_PROGRAM => unsafe {
+ return optional_root_object_to_js_or_null!(*cx, &self.current_program.get());
+ },
+ constants::ELEMENT_ARRAY_BUFFER_BINDING => unsafe {
+ let buffer = self.current_vao().element_array_buffer().get();
+ return optional_root_object_to_js_or_null!(*cx, buffer);
+ },
+ constants::FRAMEBUFFER_BINDING => unsafe {
+ return optional_root_object_to_js_or_null!(
+ *cx,
+ &self.bound_draw_framebuffer.get()
+ );
+ },
+ constants::RENDERBUFFER_BINDING => unsafe {
+ return optional_root_object_to_js_or_null!(*cx, &self.bound_renderbuffer.get());
+ },
+ constants::TEXTURE_BINDING_2D => unsafe {
+ let texture = self
+ .textures
+ .active_texture_slot(constants::TEXTURE_2D, self.webgl_version())
+ .unwrap()
+ .get();
+ return optional_root_object_to_js_or_null!(*cx, texture);
+ },
+ constants::TEXTURE_BINDING_CUBE_MAP => unsafe {
+ let texture = self
+ .textures
+ .active_texture_slot(constants::TEXTURE_CUBE_MAP, self.webgl_version())
+ .unwrap()
+ .get();
+ return optional_root_object_to_js_or_null!(*cx, texture);
+ },
+ OESVertexArrayObjectConstants::VERTEX_ARRAY_BINDING_OES => unsafe {
+ let vao = self.current_vao.get().filter(|vao| vao.id().is_some());
+ return optional_root_object_to_js_or_null!(*cx, vao);
+ },
// In readPixels we currently support RGBA/UBYTE only. If
// we wanted to support other formats, we could ask the
// driver, but we would need to check for
// GL_OES_read_format support (assuming an underlying GLES
// driver. Desktop is happy to format convert for us).
constants::IMPLEMENTATION_COLOR_READ_FORMAT => {
- if !self.validate_framebuffer_complete() {
+ if self.validate_framebuffer().is_err() {
+ self.webgl_error(InvalidOperation);
return NullValue();
- } else {
- return Int32Value(constants::RGBA as i32);
}
- }
+ return Int32Value(constants::RGBA as i32);
+ },
constants::IMPLEMENTATION_COLOR_READ_TYPE => {
- if !self.validate_framebuffer_complete() {
+ if self.validate_framebuffer().is_err() {
+ self.webgl_error(InvalidOperation);
return NullValue();
- } else {
- return Int32Value(constants::UNSIGNED_BYTE as i32);
}
- }
- _ => {}
+ return Int32Value(constants::UNSIGNED_BYTE as i32);
+ },
+ constants::COMPRESSED_TEXTURE_FORMATS => unsafe {
+ let format_ids = self.extension_manager.get_tex_compression_ids();
+
+ rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>());
+ let _ = Uint32Array::create(*cx, CreateWith::Slice(&format_ids), rval.handle_mut())
+ .unwrap();
+ return ObjectValue(rval.get());
+ },
+ constants::VERSION => unsafe {
+ rooted!(in(*cx) let mut rval = UndefinedValue());
+ "WebGL 1.0".to_jsval(*cx, rval.handle_mut());
+ return rval.get();
+ },
+ constants::RENDERER | constants::VENDOR => unsafe {
+ rooted!(in(*cx) let mut rval = UndefinedValue());
+ "Mozilla/Servo".to_jsval(*cx, rval.handle_mut());
+ return rval.get();
+ },
+ constants::SHADING_LANGUAGE_VERSION => unsafe {
+ rooted!(in(*cx) let mut rval = UndefinedValue());
+ "WebGL GLSL ES 1.0".to_jsval(*cx, rval.handle_mut());
+ return rval.get();
+ },
+ constants::UNPACK_FLIP_Y_WEBGL => {
+ let unpack = self.texture_unpacking_settings.get();
+ return BooleanValue(unpack.contains(TextureUnpacking::FLIP_Y_AXIS));
+ },
+ constants::UNPACK_PREMULTIPLY_ALPHA_WEBGL => {
+ let unpack = self.texture_unpacking_settings.get();
+ return BooleanValue(unpack.contains(TextureUnpacking::PREMULTIPLY_ALPHA));
+ },
+ constants::PACK_ALIGNMENT => {
+ return UInt32Value(self.texture_packing_alignment.get() as u32);
+ },
+ constants::UNPACK_ALIGNMENT => {
+ return UInt32Value(self.texture_unpacking_alignment.get());
+ },
+ constants::UNPACK_COLORSPACE_CONVERSION_WEBGL => {
+ let unpack = self.texture_unpacking_settings.get();
+ return UInt32Value(if unpack.contains(TextureUnpacking::CONVERT_COLORSPACE) {
+ constants::BROWSER_DEFAULT_WEBGL
+ } else {
+ constants::NONE
+ });
+ },
+ _ => {},
}
- let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap();
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::GetParameter(parameter, sender)))
- .unwrap();
- match handle_potential_webgl_error!(self, receiver.recv().unwrap(), WebGLParameter::Invalid) {
- WebGLParameter::Int(val) => Int32Value(val),
- WebGLParameter::Bool(val) => BooleanValue(val),
- WebGLParameter::Float(val) => DoubleValue(val as f64),
- WebGLParameter::FloatArray(_) => panic!("Parameter should not be float array"),
- WebGLParameter::String(val) => {
- rooted!(in(cx) let mut rval = UndefinedValue());
- val.to_jsval(cx, rval.handle_mut());
+ // Handle any MAX_ parameters by retrieving the limits that were stored
+ // when this context was created.
+ let limit = match parameter {
+ constants::MAX_VERTEX_ATTRIBS => Some(self.limits.max_vertex_attribs),
+ constants::MAX_TEXTURE_SIZE => Some(self.limits.max_tex_size),
+ constants::MAX_CUBE_MAP_TEXTURE_SIZE => Some(self.limits.max_cube_map_tex_size),
+ constants::MAX_COMBINED_TEXTURE_IMAGE_UNITS => {
+ Some(self.limits.max_combined_texture_image_units)
+ },
+ constants::MAX_FRAGMENT_UNIFORM_VECTORS => {
+ Some(self.limits.max_fragment_uniform_vectors)
+ },
+ constants::MAX_RENDERBUFFER_SIZE => Some(self.limits.max_renderbuffer_size),
+ constants::MAX_TEXTURE_IMAGE_UNITS => Some(self.limits.max_texture_image_units),
+ constants::MAX_VARYING_VECTORS => Some(self.limits.max_varying_vectors),
+ constants::MAX_VERTEX_TEXTURE_IMAGE_UNITS => {
+ Some(self.limits.max_vertex_texture_image_units)
+ },
+ constants::MAX_VERTEX_UNIFORM_VECTORS => Some(self.limits.max_vertex_uniform_vectors),
+ _ => None,
+ };
+ if let Some(limit) = limit {
+ return UInt32Value(limit);
+ }
+
+ if let Ok(value) = self.capabilities.is_enabled(parameter) {
+ return BooleanValue(value);
+ }
+
+ match handle_potential_webgl_error!(
+ self,
+ Parameter::from_u32(parameter),
+ return NullValue()
+ ) {
+ Parameter::Bool(param) => {
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.send_command(WebGLCommand::GetParameterBool(param, sender));
+ BooleanValue(receiver.recv().unwrap())
+ },
+ Parameter::Bool4(param) => unsafe {
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.send_command(WebGLCommand::GetParameterBool4(param, sender));
+ rooted!(in(*cx) let mut rval = UndefinedValue());
+ receiver.recv().unwrap().to_jsval(*cx, rval.handle_mut());
rval.get()
+ },
+ Parameter::Int(param) => {
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.send_command(WebGLCommand::GetParameterInt(param, sender));
+ Int32Value(receiver.recv().unwrap())
+ },
+ Parameter::Int2(param) => unsafe {
+ 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) => unsafe {
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.send_command(WebGLCommand::GetParameterInt4(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::Float(param) => {
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.send_command(WebGLCommand::GetParameterFloat(param, sender));
+ DoubleValue(receiver.recv().unwrap() as f64)
+ },
+ Parameter::Float2(param) => unsafe {
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.send_command(WebGLCommand::GetParameterFloat2(param, sender));
+ 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) => unsafe {
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.send_command(WebGLCommand::GetParameterFloat4(param, sender));
+ 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())
+ },
+ }
+ }
+
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
+ fn GetTexParameter(&self, _cx: SafeJSContext, target: u32, pname: u32) -> JSVal {
+ let texture_slot = handle_potential_webgl_error!(
+ self,
+ self.textures
+ .active_texture_slot(target, self.webgl_version()),
+ return NullValue()
+ );
+ let texture = handle_potential_webgl_error!(
+ self,
+ texture_slot.get().ok_or(InvalidOperation),
+ return NullValue()
+ );
+
+ if !self
+ .extension_manager
+ .is_get_tex_parameter_name_enabled(pname)
+ {
+ self.webgl_error(InvalidEnum);
+ return NullValue();
+ }
+
+ match pname {
+ constants::TEXTURE_MAG_FILTER => return UInt32Value(texture.mag_filter()),
+ constants::TEXTURE_MIN_FILTER => return UInt32Value(texture.min_filter()),
+ _ => {},
+ }
+
+ let texparam =
+ handle_potential_webgl_error!(self, TexParameter::from_u32(pname), return NullValue());
+ if self.webgl_version() < texparam.required_webgl_version() {
+ self.webgl_error(InvalidEnum);
+ return NullValue();
+ }
+
+ if let Some(value) = texture.maybe_get_tex_parameter(texparam) {
+ match value {
+ TexParameterValue::Float(v) => return DoubleValue(v as f64),
+ TexParameterValue::Int(v) => return Int32Value(v),
+ TexParameterValue::Bool(v) => return BooleanValue(v),
}
- WebGLParameter::Invalid => NullValue(),
+ }
+
+ match texparam {
+ TexParameter::Float(param) => {
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.send_command(WebGLCommand::GetTexParameterFloat(target, param, sender));
+ DoubleValue(receiver.recv().unwrap() as f64)
+ },
+ TexParameter::Int(param) => {
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.send_command(WebGLCommand::GetTexParameterInt(target, param, sender));
+ Int32Value(receiver.recv().unwrap())
+ },
+ TexParameter::Bool(param) => {
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.send_command(WebGLCommand::GetTexParameterBool(target, param, sender));
+ BooleanValue(receiver.recv().unwrap())
+ },
}
}
@@ -952,13 +2293,18 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.2
fn GetContextAttributes(&self) -> Option<WebGLContextAttributes> {
- let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap();
+ let (sender, receiver) = webgl_channel().unwrap();
// If the send does not succeed, assume context lost
- if let Err(_) = self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::GetContextAttributes(sender))) {
+ let backtrace = capture_webgl_backtrace(self);
+ if self
+ .webgl_sender
+ .send(WebGLCommand::GetContextAttributes(sender), backtrace)
+ .is_err()
+ {
return None;
}
+
let attrs = receiver.recv().unwrap();
Some(WebGLContextAttributes {
@@ -969,50 +2315,57 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
preferLowPowerToHighPerformance: false,
premultipliedAlpha: attrs.premultiplied_alpha,
preserveDrawingBuffer: attrs.preserve_drawing_buffer,
- stencil: attrs.stencil
+ stencil: attrs.stencil,
})
}
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.13
+ fn IsContextLost(&self) -> bool {
+ false
+ }
+
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.14
fn GetSupportedExtensions(&self) -> Option<Vec<DOMString>> {
- Some(vec![])
+ self.extension_manager
+ .init_once(|| self.get_gl_extensions());
+ let extensions = self.extension_manager.get_supported_extensions();
+ Some(
+ extensions
+ .iter()
+ .map(|name| DOMString::from(*name))
+ .collect(),
+ )
}
- #[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.14
- unsafe fn GetExtension(&self, _cx: *mut JSContext, _name: DOMString)
- -> Option<NonZero<*mut JSObject>> {
- None
+ fn GetExtension(&self, _cx: SafeJSContext, name: DOMString) -> Option<NonNull<JSObject>> {
+ self.extension_manager
+ .init_once(|| self.get_gl_extensions());
+ self.extension_manager.get_or_init_extension(&name, self)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn ActiveTexture(&self, texture: u32) {
- self.ipc_renderer.send(CanvasMsg::WebGL(WebGLCommand::ActiveTexture(texture))).unwrap();
+ handle_potential_webgl_error!(self, self.textures.set_active_unit_enum(texture), return);
+ self.send_command(WebGLCommand::ActiveTexture(texture));
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn BlendColor(&self, r: f32, g: f32, b: f32, a: f32) {
- self.ipc_renderer.send(CanvasMsg::WebGL(WebGLCommand::BlendColor(r, g, b, a))).unwrap();
+ self.send_command(WebGLCommand::BlendColor(r, g, b, a));
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn BlendEquation(&self, mode: u32) {
- if mode != constants::FUNC_ADD {
- return self.webgl_error(InvalidEnum);
- }
-
- self.ipc_renderer.send(CanvasMsg::WebGL(WebGLCommand::BlendEquation(mode))).unwrap();
+ handle_potential_webgl_error!(self, self.validate_blend_mode(mode), return);
+ self.send_command(WebGLCommand::BlendEquation(mode))
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn BlendEquationSeparate(&self, mode_rgb: u32, mode_alpha: u32) {
- if mode_rgb != constants::FUNC_ADD || mode_alpha != constants::FUNC_ADD {
- return self.webgl_error(InvalidEnum);
- }
-
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::BlendEquationSeparate(mode_rgb, mode_alpha)))
- .unwrap();
+ handle_potential_webgl_error!(self, self.validate_blend_mode(mode_rgb), return);
+ handle_potential_webgl_error!(self, self.validate_blend_mode(mode_alpha), return);
+ self.send_command(WebGLCommand::BlendEquationSeparate(mode_rgb, mode_alpha));
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
@@ -1029,9 +2382,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
return self.webgl_error(InvalidOperation);
}
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::BlendFunc(src_factor, dest_factor)))
- .unwrap();
+ self.send_command(WebGLCommand::BlendFunc(src_factor, dest_factor));
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
@@ -1048,87 +2399,66 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
return self.webgl_error(InvalidOperation);
}
- self.ipc_renderer.send(
- CanvasMsg::WebGL(WebGLCommand::BlendFuncSeparate(src_rgb, dest_rgb, src_alpha, dest_alpha))).unwrap();
+ self.send_command(WebGLCommand::BlendFuncSeparate(
+ src_rgb, dest_rgb, src_alpha, dest_alpha,
+ ));
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
- fn AttachShader(&self, program: Option<&WebGLProgram>, shader: Option<&WebGLShader>) {
- if let Some(program) = program {
- if let Some(shader) = shader {
- handle_potential_webgl_error!(self, program.attach_shader(shader));
- }
- }
+ fn AttachShader(&self, program: &WebGLProgram, shader: &WebGLShader) {
+ handle_potential_webgl_error!(self, self.validate_ownership(program), return);
+ handle_potential_webgl_error!(self, self.validate_ownership(shader), return);
+ handle_potential_webgl_error!(self, program.attach_shader(shader));
}
// 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));
- }
- }
+ fn DetachShader(&self, program: &WebGLProgram, shader: &WebGLShader) {
+ handle_potential_webgl_error!(self, self.validate_ownership(program), return);
+ handle_potential_webgl_error!(self, self.validate_ownership(shader), return);
+ 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) {
- if let Some(program) = program {
- handle_potential_webgl_error!(self, program.bind_attrib_location(index, name));
- }
+ fn BindAttribLocation(&self, program: &WebGLProgram, index: u32, name: DOMString) {
+ handle_potential_webgl_error!(self, self.validate_ownership(program), return);
+ handle_potential_webgl_error!(self, program.bind_attrib_location(index, name));
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5
fn BindBuffer(&self, target: u32, buffer: Option<&WebGLBuffer>) {
+ let current_vao;
let slot = match target {
constants::ARRAY_BUFFER => &self.bound_buffer_array,
- constants::ELEMENT_ARRAY_BUFFER => &self.bound_buffer_element_array,
-
+ constants::ELEMENT_ARRAY_BUFFER => {
+ current_vao = self.current_vao();
+ current_vao.element_array_buffer()
+ },
_ => return self.webgl_error(InvalidEnum),
};
-
- if let Some(buffer) = buffer {
- match buffer.bind(target) {
- Ok(_) => slot.set(Some(buffer)),
- Err(e) => return self.webgl_error(e),
- }
- } else {
- slot.set(None);
- // Unbind the current buffer
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::BindBuffer(target, None)))
- .unwrap()
- }
+ self.bind_buffer_maybe(&slot, target, buffer);
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6
fn BindFramebuffer(&self, target: u32, framebuffer: Option<&WebGLFramebuffer>) {
+ handle_potential_webgl_error!(
+ self,
+ self.validate_new_framebuffer_binding(framebuffer),
+ return
+ );
+
if target != constants::FRAMEBUFFER {
- return self.webgl_error(InvalidOperation);
+ return self.webgl_error(InvalidEnum);
}
- if let Some(framebuffer) = framebuffer {
- if framebuffer.is_deleted() {
- // From the WebGL spec:
- //
- // "An attempt to bind a deleted framebuffer will
- // generate an INVALID_OPERATION error, and the
- // current binding will remain untouched."
- return self.webgl_error(InvalidOperation);
- } else {
- framebuffer.bind(target);
- self.bound_framebuffer.set(Some(framebuffer));
- }
- } else {
- // Bind the default framebuffer
- let cmd = WebGLCommand::BindFramebuffer(target, WebGLFramebufferBindingRequest::Default);
- self.ipc_renderer.send(CanvasMsg::WebGL(cmd)).unwrap();
- self.bound_framebuffer.set(framebuffer);
- }
+ self.bind_framebuffer_to(target, framebuffer, &self.bound_draw_framebuffer)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7
fn BindRenderbuffer(&self, target: u32, renderbuffer: Option<&WebGLRenderbuffer>) {
+ if let Some(rb) = renderbuffer {
+ handle_potential_webgl_error!(self, self.validate_ownership(rb), return);
+ }
+
if target != constants::RENDERBUFFER {
return self.webgl_error(InvalidEnum);
}
@@ -1140,194 +2470,132 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
Some(renderbuffer) if !renderbuffer.is_deleted() => {
self.bound_renderbuffer.set(Some(renderbuffer));
renderbuffer.bind(target);
- }
+ },
_ => {
+ if renderbuffer.is_some() {
+ self.webgl_error(InvalidOperation);
+ }
+
self.bound_renderbuffer.set(None);
// Unbind the currently bound renderbuffer
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::BindRenderbuffer(target, None)))
- .unwrap()
- }
+ self.send_command(WebGLCommand::BindRenderbuffer(target, None));
+ },
}
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
fn BindTexture(&self, target: u32, texture: Option<&WebGLTexture>) {
- 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),
- };
+ if let Some(texture) = texture {
+ handle_potential_webgl_error!(self, self.validate_ownership(texture), return);
+ }
+
+ let texture_slot = handle_potential_webgl_error!(
+ self,
+ self.textures
+ .active_texture_slot(target, self.webgl_version()),
+ return
+ );
if let Some(texture) = texture {
- match texture.bind(target) {
- Ok(_) => slot.set(Some(texture)),
- Err(err) => return self.webgl_error(err),
- }
+ handle_potential_webgl_error!(self, texture.bind(target), return);
} else {
- slot.set(None);
- // Unbind the currently bound texture
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::BindTexture(target, None)))
- .unwrap()
+ self.send_command(WebGLCommand::BindTexture(target, None));
}
+ texture_slot.set(texture);
}
// 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)
- }
+ let texture_slot = handle_potential_webgl_error!(
+ self,
+ self.textures
+ .active_texture_slot(target, self.webgl_version()),
+ return
+ );
+ let texture =
+ handle_potential_webgl_error!(self, texture_slot.get().ok_or(InvalidOperation), return);
+ handle_potential_webgl_error!(self, texture.generate_mipmap());
}
- #[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5
- unsafe fn BufferData(&self, cx: *mut JSContext, target: u32, data: *mut JSObject, usage: u32) -> Fallible<()> {
- if data.is_null() {
- return Ok(self.webgl_error(InvalidValue));
- }
-
- typedarray!(in(cx) let array_buffer: ArrayBuffer = data);
- let data_vec = match array_buffer {
- Ok(mut data) => data.as_slice().to_vec(),
- Err(_) => try!(fallible_array_buffer_view_to_vec(cx, data)),
- };
-
- let bound_buffer = match target {
- constants::ARRAY_BUFFER => self.bound_buffer_array.get(),
- constants::ELEMENT_ARRAY_BUFFER => self.bound_buffer_element_array.get(),
- _ => return Ok(self.webgl_error(InvalidEnum)),
- };
-
- let bound_buffer = match bound_buffer {
- Some(bound_buffer) => bound_buffer,
- None => return Ok(self.webgl_error(InvalidValue)),
- };
-
- match usage {
- constants::STREAM_DRAW |
- constants::STATIC_DRAW |
- constants::DYNAMIC_DRAW => (),
- _ => return Ok(self.webgl_error(InvalidEnum)),
- }
-
- handle_potential_webgl_error!(self, bound_buffer.buffer_data(target, &data_vec, usage));
-
- Ok(())
+ 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) -> Fallible<()> {
- let bound_buffer = match target {
- constants::ARRAY_BUFFER => self.bound_buffer_array.get(),
- constants::ELEMENT_ARRAY_BUFFER => self.bound_buffer_element_array.get(),
- _ => return Ok(self.webgl_error(InvalidEnum)),
- };
-
- let bound_buffer = match bound_buffer {
- Some(bound_buffer) => bound_buffer,
- None => return Ok(self.webgl_error(InvalidValue)),
- };
-
- if size < 0 {
- return Ok(self.webgl_error(InvalidValue));
- }
-
- match usage {
- constants::STREAM_DRAW |
- constants::STATIC_DRAW |
- constants::DYNAMIC_DRAW => (),
- _ => return Ok(self.webgl_error(InvalidEnum)),
- }
-
- // FIXME: Allocating a buffer based on user-requested size is
- // not great, but we don't have a fallible allocation to try.
- let data = vec![0u8; size as usize];
- handle_potential_webgl_error!(self, bound_buffer.buffer_data(target, &data, usage));
-
- Ok(())
+ 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)
}
- #[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5
- unsafe fn BufferSubData(&self, cx: *mut JSContext, target: u32, offset: i64, data: *mut JSObject) -> Fallible<()> {
- if data.is_null() {
- return Ok(self.webgl_error(InvalidValue));
- }
-
- typedarray!(in(cx) let array_buffer: ArrayBuffer = data);
- let data_vec = match array_buffer {
- Ok(mut data) => data.as_slice().to_vec(),
- Err(_) => try!(fallible_array_buffer_view_to_vec(cx, data)),
- };
-
- let bound_buffer = match target {
- constants::ARRAY_BUFFER => self.bound_buffer_array.get(),
- constants::ELEMENT_ARRAY_BUFFER => self.bound_buffer_element_array.get(),
- _ => return Ok(self.webgl_error(InvalidEnum)),
- };
-
- let bound_buffer = match bound_buffer {
- Some(bound_buffer) => bound_buffer,
- None => return Ok(self.webgl_error(InvalidOperation)),
- };
-
- if offset < 0 {
- return Ok(self.webgl_error(InvalidValue));
- }
-
- if (offset as usize) + data_vec.len() > bound_buffer.capacity() {
- return Ok(self.webgl_error(InvalidValue));
- }
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::BufferSubData(target, offset as isize, data_vec)))
- .unwrap();
-
- Ok(())
+ #[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);
+ self.buffer_sub_data(target, offset, data, bound_buffer)
}
- #[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
- unsafe fn CompressedTexImage2D(&self, cx: *mut JSContext, _target: u32, _level: i32, _internal_format: u32,
- _width: i32, _height: i32, _border: i32, pixels: *mut JSObject) -> Fallible<()> {
- let _data = try!(fallible_array_buffer_view_to_vec(cx, pixels) );
- // FIXME: No compressed texture format is currently supported, so error out as per
- // https://www.khronos.org/registry/webgl/specs/latest/1.0/#COMPRESSED_TEXTURE_SUPPORT
- self.webgl_error(InvalidEnum);
- Ok(())
+ #[allow(unsafe_code)]
+ fn CompressedTexImage2D(
+ &self,
+ target: u32,
+ level: i32,
+ internal_format: u32,
+ width: i32,
+ height: i32,
+ border: i32,
+ data: CustomAutoRooterGuard<ArrayBufferView>,
+ ) {
+ let data = unsafe { data.as_slice() };
+ self.compressed_tex_image_2d(target, level, internal_format, width, height, border, data)
}
- #[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
- unsafe fn CompressedTexSubImage2D(&self, cx: *mut JSContext, _target: u32, _level: i32,
- _xoffset: i32, _yoffset: i32, _width: i32, _height: i32,
- _format: u32, pixels: *mut JSObject) -> Fallible<()> {
- let _data = try!(fallible_array_buffer_view_to_vec(cx, pixels));
- // FIXME: No compressed texture format is currently supported, so error out as per
- // https://www.khronos.org/registry/webgl/specs/latest/1.0/#COMPRESSED_TEXTURE_SUPPORT
- self.webgl_error(InvalidEnum);
-
- Ok(())
+ #[allow(unsafe_code)]
+ fn CompressedTexSubImage2D(
+ &self,
+ target: u32,
+ level: i32,
+ xoffset: i32,
+ yoffset: i32,
+ width: i32,
+ height: i32,
+ format: u32,
+ data: CustomAutoRooterGuard<ArrayBufferView>,
+ ) {
+ let data = unsafe { data.as_slice() };
+ self.compressed_tex_sub_image_2d(
+ target, level, xoffset, yoffset, width, height, format, data,
+ )
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
- fn CopyTexImage2D(&self, target: u32, level: i32, internal_format: u32,
- x: i32, y: i32, width: i32, height: i32, border: i32) {
- if !self.validate_framebuffer_complete() {
- return;
- }
-
- let validator = CommonTexImage2DValidator::new(self, target, level,
- internal_format, width,
- height, border);
+ fn CopyTexImage2D(
+ &self,
+ target: u32,
+ level: i32,
+ internal_format: u32,
+ x: i32,
+ y: i32,
+ width: i32,
+ height: i32,
+ border: i32,
+ ) {
+ handle_potential_webgl_error!(self, self.validate_framebuffer(), return);
+
+ let validator = CommonTexImage2DValidator::new(
+ self,
+ target,
+ level,
+ internal_format,
+ width,
+ height,
+ border,
+ );
let CommonTexImage2DValidatorResult {
texture,
target,
@@ -1341,52 +2609,104 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
Err(_) => return,
};
- let image_info = texture.image_info_for_target(&target, level);
+ if texture.is_immutable() {
+ return self.webgl_error(InvalidOperation);
+ }
- // The color buffer components can be dropped during the conversion to
- // the internal_format, but new components cannot be added.
- //
- // Note that this only applies if we're copying to an already
- // initialized texture.
- //
- // GL_INVALID_OPERATION is generated if the color buffer cannot be
- // converted to the internal_format.
- if let Some(old_internal_format) = image_info.internal_format() {
- if old_internal_format.components() > internal_format.components() {
- return self.webgl_error(InvalidOperation);
- }
+ let framebuffer_format = match self.bound_draw_framebuffer.get() {
+ Some(fb) => match fb.attachment(constants::COLOR_ATTACHMENT0) {
+ Some(WebGLFramebufferAttachmentRoot::Renderbuffer(rb)) => {
+ TexFormat::from_gl_constant(rb.internal_format())
+ },
+ Some(WebGLFramebufferAttachmentRoot::Texture(texture)) => texture
+ .image_info_for_target(&target, 0)
+ .map(|info| info.internal_format()),
+ None => None,
+ },
+ None => {
+ let attrs = self.GetContextAttributes().unwrap();
+ Some(if attrs.alpha {
+ TexFormat::RGBA
+ } else {
+ TexFormat::RGB
+ })
+ },
+ };
+
+ let framebuffer_format = match framebuffer_format {
+ Some(f) => f,
+ None => {
+ self.webgl_error(InvalidOperation);
+ return;
+ },
+ };
+
+ match (framebuffer_format, internal_format) {
+ (a, b) if a == b => (),
+ (TexFormat::RGBA, TexFormat::RGB) => (),
+ (TexFormat::RGBA, TexFormat::Alpha) => (),
+ (TexFormat::RGBA, TexFormat::Luminance) => (),
+ (TexFormat::RGBA, TexFormat::LuminanceAlpha) => (),
+ (TexFormat::RGB, TexFormat::Luminance) => (),
+ _ => {
+ self.webgl_error(InvalidOperation);
+ return;
+ },
}
// NB: TexImage2D depth is always equal to 1
- handle_potential_webgl_error!(self, texture.initialize(target,
- width as u32,
- height as u32, 1,
- internal_format,
- level as u32,
- None));
+ handle_potential_webgl_error!(
+ self,
+ texture.initialize(
+ target,
+ width as u32,
+ height as u32,
+ 1,
+ internal_format,
+ level as u32,
+ None
+ )
+ );
- let msg = WebGLCommand::CopyTexImage2D(target.as_gl_constant(),
- level as i32,
- internal_format.as_gl_constant(),
- x, y,
- width as i32, height as i32,
- border as i32);
+ let msg = WebGLCommand::CopyTexImage2D(
+ target.as_gl_constant(),
+ level as i32,
+ internal_format.as_gl_constant(),
+ x,
+ y,
+ width as i32,
+ height as i32,
+ border as i32,
+ );
- self.ipc_renderer.send(CanvasMsg::WebGL(msg)).unwrap()
+ self.send_command(msg);
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
- fn CopyTexSubImage2D(&self, target: u32, level: i32, xoffset: i32, yoffset: i32,
- x: i32, y: i32, width: i32, height: i32) {
- if !self.validate_framebuffer_complete() {
- return;
- }
+ fn CopyTexSubImage2D(
+ &self,
+ target: u32,
+ level: i32,
+ xoffset: i32,
+ yoffset: i32,
+ x: i32,
+ y: i32,
+ width: i32,
+ height: i32,
+ ) {
+ handle_potential_webgl_error!(self, self.validate_framebuffer(), return);
// NB: We use a dummy (valid) format and border in order to reuse the
// common validations, but this should have its own validator.
- let validator = CommonTexImage2DValidator::new(self, target, level,
- TexFormat::RGBA.as_gl_constant(),
- width, height, 0);
+ let validator = CommonTexImage2DValidator::new(
+ self,
+ target,
+ level,
+ TexFormat::RGBA.as_gl_constant(),
+ width,
+ height,
+ 0,
+ );
let CommonTexImage2DValidatorResult {
texture,
target,
@@ -1399,72 +2719,81 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
Err(_) => return,
};
- let image_info = texture.image_info_for_target(&target, level);
+ let image_info = match texture.image_info_for_target(&target, level) {
+ Some(info) => info,
+ None => return self.webgl_error(InvalidOperation),
+ };
// GL_INVALID_VALUE is generated if:
// - xoffset or yoffset is less than 0
// - x offset plus the width is greater than the texture width
// - y offset plus the height is greater than the texture height
- if xoffset < 0 || (xoffset as u32 + width) > image_info.width() ||
- yoffset < 0 || (yoffset as u32 + height) > image_info.height() {
- self.webgl_error(InvalidValue);
- return;
+ if xoffset < 0 ||
+ (xoffset as u32 + width) > image_info.width() ||
+ yoffset < 0 ||
+ (yoffset as u32 + height) > image_info.height()
+ {
+ self.webgl_error(InvalidValue);
+ return;
}
- let msg = WebGLCommand::CopyTexSubImage2D(target.as_gl_constant(),
- level as i32, xoffset, yoffset,
- x, y,
- width as i32, height as i32);
+ let msg = WebGLCommand::CopyTexSubImage2D(
+ target.as_gl_constant(),
+ level as i32,
+ xoffset,
+ yoffset,
+ x,
+ y,
+ width as i32,
+ height as i32,
+ );
- self.ipc_renderer.send(CanvasMsg::WebGL(msg)).unwrap();
+ self.send_command(msg);
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11
fn Clear(&self, mask: u32) {
- if !self.validate_framebuffer_complete() {
- return;
+ handle_potential_webgl_error!(self, self.validate_framebuffer(), return);
+ if mask &
+ !(constants::DEPTH_BUFFER_BIT |
+ constants::STENCIL_BUFFER_BIT |
+ constants::COLOR_BUFFER_BIT) !=
+ 0
+ {
+ return self.webgl_error(InvalidValue);
}
- self.ipc_renderer.send(CanvasMsg::WebGL(WebGLCommand::Clear(mask))).unwrap();
+ self.send_command(WebGLCommand::Clear(mask));
self.mark_as_dirty();
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn ClearColor(&self, red: f32, green: f32, blue: f32, alpha: f32) {
self.current_clear_color.set((red, green, blue, alpha));
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::ClearColor(red, green, blue, alpha)))
- .unwrap()
+ self.send_command(WebGLCommand::ClearColor(red, green, blue, alpha));
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn ClearDepth(&self, depth: f32) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::ClearDepth(depth as f64)))
- .unwrap()
+ self.send_command(WebGLCommand::ClearDepth(depth))
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn ClearStencil(&self, stencil: i32) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::ClearStencil(stencil)))
- .unwrap()
+ self.send_command(WebGLCommand::ClearStencil(stencil))
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn ColorMask(&self, r: bool, g: bool, b: bool, a: bool) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::ColorMask(r, g, b, a)))
- .unwrap()
+ self.send_command(WebGLCommand::ColorMask(r, g, b, a))
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn CullFace(&self, mode: u32) {
match mode {
- constants::FRONT | constants::BACK | constants::FRONT_AND_BACK =>
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::CullFace(mode)))
- .unwrap(),
+ constants::FRONT | constants::BACK | constants::FRONT_AND_BACK => {
+ self.send_command(WebGLCommand::CullFace(mode))
+ },
_ => self.webgl_error(InvalidEnum),
}
}
@@ -1472,557 +2801,770 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn FrontFace(&self, mode: u32) {
match mode {
- constants::CW | constants::CCW =>
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::FrontFace(mode)))
- .unwrap(),
+ constants::CW | constants::CCW => self.send_command(WebGLCommand::FrontFace(mode)),
_ => self.webgl_error(InvalidEnum),
}
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn DepthFunc(&self, func: u32) {
match func {
- constants::NEVER | constants::LESS |
- constants::EQUAL | constants::LEQUAL |
- constants::GREATER | constants::NOTEQUAL |
- constants::GEQUAL | constants::ALWAYS =>
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::DepthFunc(func)))
- .unwrap(),
+ constants::NEVER |
+ constants::LESS |
+ constants::EQUAL |
+ constants::LEQUAL |
+ constants::GREATER |
+ constants::NOTEQUAL |
+ constants::GEQUAL |
+ constants::ALWAYS => self.send_command(WebGLCommand::DepthFunc(func)),
_ => self.webgl_error(InvalidEnum),
}
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn DepthMask(&self, flag: bool) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::DepthMask(flag)))
- .unwrap()
+ self.send_command(WebGLCommand::DepthMask(flag))
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn DepthRange(&self, near: f32, far: f32) {
- // From the WebGL 1.0 spec, 6.12: Viewport Depth Range:
- //
- // "A call to depthRange will generate an
- // INVALID_OPERATION error if zNear is greater than
- // zFar."
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#VIEWPORT_DEPTH_RANGE
if near > far {
return self.webgl_error(InvalidOperation);
}
-
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::DepthRange(near as f64, far as f64)))
- .unwrap()
+ self.send_command(WebGLCommand::DepthRange(near, far))
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn Enable(&self, cap: u32) {
- if self.validate_feature_enum(cap) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Enable(cap)))
- .unwrap();
+ if handle_potential_webgl_error!(self, self.capabilities.set(cap, true), return) {
+ self.send_command(WebGLCommand::Enable(cap));
}
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn Disable(&self, cap: u32) {
- if self.validate_feature_enum(cap) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Disable(cap)))
- .unwrap()
+ if handle_potential_webgl_error!(self, self.capabilities.set(cap, false), return) {
+ self.send_command(WebGLCommand::Disable(cap));
}
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
- fn CompileShader(&self, shader: Option<&WebGLShader>) {
- if let Some(shader) = shader {
- shader.compile()
- }
+ fn CompileShader(&self, shader: &WebGLShader) {
+ handle_potential_webgl_error!(self, self.validate_ownership(shader), return);
+ handle_potential_webgl_error!(
+ self,
+ shader.compile(
+ self.api_type,
+ self.webgl_version,
+ self.glsl_version,
+ &self.limits,
+ &self.extension_manager,
+ )
+ )
}
- // TODO(emilio): Probably in the future we should keep track of the
- // generated objects, either here or in the webgl thread
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5
- fn CreateBuffer(&self) -> Option<Root<WebGLBuffer>> {
- WebGLBuffer::maybe_new(self.global().as_window(), self.ipc_renderer.clone())
+ fn CreateBuffer(&self) -> Option<DomRoot<WebGLBuffer>> {
+ WebGLBuffer::maybe_new(self)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6
- fn CreateFramebuffer(&self) -> Option<Root<WebGLFramebuffer>> {
- WebGLFramebuffer::maybe_new(self.global().as_window(), self.ipc_renderer.clone())
+ fn CreateFramebuffer(&self) -> Option<DomRoot<WebGLFramebuffer>> {
+ WebGLFramebuffer::maybe_new(self)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7
- fn CreateRenderbuffer(&self) -> Option<Root<WebGLRenderbuffer>> {
- WebGLRenderbuffer::maybe_new(self.global().as_window(), self.ipc_renderer.clone())
+ fn CreateRenderbuffer(&self) -> Option<DomRoot<WebGLRenderbuffer>> {
+ WebGLRenderbuffer::maybe_new(self)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
- fn CreateTexture(&self) -> Option<Root<WebGLTexture>> {
- WebGLTexture::maybe_new(self.global().as_window(), self.ipc_renderer.clone())
+ fn CreateTexture(&self) -> Option<DomRoot<WebGLTexture>> {
+ WebGLTexture::maybe_new(self)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
- fn CreateProgram(&self) -> Option<Root<WebGLProgram>> {
- WebGLProgram::maybe_new(self.global().as_window(), self.ipc_renderer.clone())
+ fn CreateProgram(&self) -> Option<DomRoot<WebGLProgram>> {
+ WebGLProgram::maybe_new(self)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
- fn CreateShader(&self, shader_type: u32) -> Option<Root<WebGLShader>> {
+ fn CreateShader(&self, shader_type: u32) -> Option<DomRoot<WebGLShader>> {
match shader_type {
constants::VERTEX_SHADER | constants::FRAGMENT_SHADER => {},
_ => {
self.webgl_error(InvalidEnum);
return None;
- }
+ },
}
- WebGLShader::maybe_new(self.global().as_window(), self.ipc_renderer.clone(), shader_type)
+ WebGLShader::maybe_new(self, shader_type)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5
fn DeleteBuffer(&self, buffer: Option<&WebGLBuffer>) {
- if let Some(buffer) = buffer {
- handle_object_deletion!(self, self.bound_buffer_array, buffer,
- Some(WebGLCommand::BindBuffer(constants::ARRAY_BUFFER, None)));
- handle_object_deletion!(self, self.bound_buffer_element_array, buffer,
- Some(WebGLCommand::BindBuffer(constants::ELEMENT_ARRAY_BUFFER, None)));
- buffer.delete()
+ let buffer = match buffer {
+ Some(buffer) => buffer,
+ None => return,
+ };
+ handle_potential_webgl_error!(self, self.validate_ownership(buffer), return);
+ if buffer.is_marked_for_deletion() {
+ return;
}
+ self.current_vao().unbind_buffer(buffer);
+ if self
+ .bound_buffer_array
+ .get()
+ .map_or(false, |b| buffer == &*b)
+ {
+ self.bound_buffer_array.set(None);
+ buffer.decrement_attached_counter(Operation::Infallible);
+ }
+ buffer.mark_for_deletion(Operation::Infallible);
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6
fn DeleteFramebuffer(&self, framebuffer: Option<&WebGLFramebuffer>) {
if let Some(framebuffer) = framebuffer {
- handle_object_deletion!(self, self.bound_framebuffer, framebuffer,
- Some(WebGLCommand::BindFramebuffer(constants::FRAMEBUFFER,
- WebGLFramebufferBindingRequest::Default)));
- framebuffer.delete()
+ // https://immersive-web.github.io/webxr/#opaque-framebuffer
+ // Can opaque framebuffers be deleted?
+ // https://github.com/immersive-web/webxr/issues/855
+ handle_potential_webgl_error!(self, framebuffer.validate_transparent(), return);
+ handle_potential_webgl_error!(self, self.validate_ownership(framebuffer), return);
+ handle_object_deletion!(
+ self,
+ self.bound_draw_framebuffer,
+ framebuffer,
+ Some(WebGLCommand::BindFramebuffer(
+ framebuffer.target().unwrap(),
+ WebGLFramebufferBindingRequest::Default
+ ))
+ );
+ framebuffer.delete(Operation::Infallible)
}
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7
fn DeleteRenderbuffer(&self, renderbuffer: Option<&WebGLRenderbuffer>) {
if let Some(renderbuffer) = renderbuffer {
- handle_object_deletion!(self, self.bound_renderbuffer, renderbuffer,
- Some(WebGLCommand::BindRenderbuffer(constants::RENDERBUFFER, None)));
- // From the GLES 2.0.25 spec, page 113:
- //
- // "If a renderbuffer object is deleted while its
- // image is attached to the currently bound
- // framebuffer, then it is as if
- // FramebufferRenderbuffer had been called, with a
- // renderbuffer of 0, for each attachment point to
- // which this image was attached in the currently
- // bound framebuffer."
- //
- if let Some(fb) = self.bound_framebuffer.get() {
- fb.detach_renderbuffer(renderbuffer);
- }
-
- renderbuffer.delete()
+ handle_potential_webgl_error!(self, self.validate_ownership(renderbuffer), return);
+ handle_object_deletion!(
+ self,
+ self.bound_renderbuffer,
+ renderbuffer,
+ Some(WebGLCommand::BindRenderbuffer(
+ constants::RENDERBUFFER,
+ None
+ ))
+ );
+ renderbuffer.delete(Operation::Infallible)
}
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
fn DeleteTexture(&self, texture: Option<&WebGLTexture>) {
if let Some(texture) = texture {
- handle_object_deletion!(self, self.bound_texture_2d, texture,
- Some(WebGLCommand::BindTexture(constants::TEXTURE_2D, None)));
- handle_object_deletion!(self, self.bound_texture_cube_map, texture,
- Some(WebGLCommand::BindTexture(constants::TEXTURE_CUBE_MAP, None)));
+ handle_potential_webgl_error!(self, self.validate_ownership(texture), return);
- // From the GLES 2.0.25 spec, page 113:
+ // From the GLES 2.0.25 spec, page 85:
+ //
+ // "If a texture that is currently bound to one of the targets
+ // TEXTURE_2D, or TEXTURE_CUBE_MAP is deleted, it is as though
+ // BindTexture had been executed with the same target and texture
+ // zero."
//
- // "If a texture object is deleted while its image is
- // attached to the currently bound framebuffer, then
- // it is as if FramebufferTexture2D had been called,
- // with a texture of 0, for each attachment point to
- // which this image was attached in the currently
- // bound framebuffer."
- if let Some(fb) = self.bound_framebuffer.get() {
- fb.detach_texture(texture);
+ // The same texture may be bound to multiple texture units.
+ let mut active_unit_enum = self.textures.active_unit_enum();
+ for (unit_enum, slot) in self.textures.iter() {
+ if let Some(target) = slot.unbind(texture) {
+ if unit_enum != active_unit_enum {
+ self.send_command(WebGLCommand::ActiveTexture(unit_enum));
+ active_unit_enum = unit_enum;
+ }
+ self.send_command(WebGLCommand::BindTexture(target, None));
+ }
+ }
+
+ // Restore bound texture unit if it has been changed.
+ if active_unit_enum != self.textures.active_unit_enum() {
+ self.send_command(WebGLCommand::ActiveTexture(
+ self.textures.active_unit_enum(),
+ ));
}
- texture.delete()
+
+ texture.delete(Operation::Infallible)
}
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
fn DeleteProgram(&self, program: Option<&WebGLProgram>) {
if let Some(program) = program {
- // FIXME: We should call glUseProgram(0), but
- // WebGLCommand::UseProgram() doesn't take an Option
- // currently. This is also a problem for useProgram(null)
- handle_object_deletion!(self, self.current_program, program, None);
- program.delete()
+ handle_potential_webgl_error!(self, self.validate_ownership(program), return);
+ program.mark_for_deletion(Operation::Infallible)
}
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
fn DeleteShader(&self, shader: Option<&WebGLShader>) {
if let Some(shader) = shader {
- shader.delete()
+ handle_potential_webgl_error!(self, self.validate_ownership(shader), return);
+ shader.mark_for_deletion(Operation::Infallible)
}
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11
fn DrawArrays(&self, mode: u32, first: i32, count: i32) {
- match mode {
- constants::POINTS | constants::LINE_STRIP |
- constants::LINE_LOOP | constants::LINES |
- constants::TRIANGLE_STRIP | constants::TRIANGLE_FAN |
- constants::TRIANGLES => {
- if self.current_program.get().is_none() {
- return self.webgl_error(InvalidOperation);
- }
-
- if first < 0 || count < 0 {
- return self.webgl_error(InvalidValue);
- }
-
- if !self.validate_framebuffer_complete() {
- return;
- }
-
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::DrawArrays(mode, first, count)))
- .unwrap();
- self.mark_as_dirty();
- },
- _ => self.webgl_error(InvalidEnum),
- }
+ handle_potential_webgl_error!(self, self.draw_arrays_instanced(mode, first, count, 1));
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11
fn DrawElements(&self, mode: u32, count: i32, type_: u32, offset: i64) {
- // From the GLES 2.0.25 spec, page 21:
- //
- // "type must be one of UNSIGNED_BYTE or UNSIGNED_SHORT"
- 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);
- }
-
- if count < 0 {
- return self.webgl_error(InvalidValue);
- }
-
- if offset < 0 {
- 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);
- }
-
- 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);
- }
-
- if !self.validate_framebuffer_complete() {
- return;
- }
-
- match mode {
- constants::POINTS | constants::LINE_STRIP |
- constants::LINE_LOOP | constants::LINES |
- constants::TRIANGLE_STRIP | constants::TRIANGLE_FAN |
- constants::TRIANGLES => {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::DrawElements(mode, count, type_, offset)))
- .unwrap();
- self.mark_as_dirty();
- },
- _ => self.webgl_error(InvalidEnum),
- }
+ handle_potential_webgl_error!(
+ self,
+ self.draw_elements_instanced(mode, count, type_, offset, 1)
+ );
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
fn EnableVertexAttribArray(&self, attrib_id: u32) {
- if attrib_id > self.limits.max_vertex_attribs {
+ if attrib_id >= self.limits.max_vertex_attribs {
return self.webgl_error(InvalidValue);
}
-
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::EnableVertexAttribArray(attrib_id)))
- .unwrap()
+ match self.webgl_version() {
+ WebGLVersion::WebGL1 => self
+ .current_vao()
+ .enabled_vertex_attrib_array(attrib_id, true),
+ WebGLVersion::WebGL2 => self
+ .current_vao_webgl2()
+ .enabled_vertex_attrib_array(attrib_id, true),
+ };
+ self.send_command(WebGLCommand::EnableVertexAttribArray(attrib_id));
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
fn DisableVertexAttribArray(&self, attrib_id: u32) {
- if attrib_id > self.limits.max_vertex_attribs {
+ if attrib_id >= self.limits.max_vertex_attribs {
return self.webgl_error(InvalidValue);
}
-
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::DisableVertexAttribArray(attrib_id)))
- .unwrap()
+ match self.webgl_version() {
+ WebGLVersion::WebGL1 => self
+ .current_vao()
+ .enabled_vertex_attrib_array(attrib_id, false),
+ WebGLVersion::WebGL2 => self
+ .current_vao_webgl2()
+ .enabled_vertex_attrib_array(attrib_id, false),
+ };
+ self.send_command(WebGLCommand::DisableVertexAttribArray(attrib_id));
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- fn GetActiveUniform(&self, program: Option<&WebGLProgram>, index: u32) -> Option<Root<WebGLActiveInfo>> {
- let program = match program {
- Some(program) => program,
- None => {
- // Reasons to generate InvalidValue error
- // From the GLES 2.0 spec
- //
- // "INVALID_VALUE is generated if index is greater than or equal
- // to the number of active uniform variables in program"
- //
- // A null program has no uniforms so any index is always greater than the active uniforms
- // WebGl conformance expects error with null programs. Check tests in get-active-test.html
- self.webgl_error(InvalidValue);
- return None;
- }
- };
-
+ fn GetActiveUniform(
+ &self,
+ program: &WebGLProgram,
+ index: u32,
+ ) -> Option<DomRoot<WebGLActiveInfo>> {
+ handle_potential_webgl_error!(self, self.validate_ownership(program), return None);
match program.get_active_uniform(index) {
Ok(ret) => Some(ret),
Err(e) => {
self.webgl_error(e);
return None;
- }
+ },
}
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- fn GetActiveAttrib(&self, program: Option<&WebGLProgram>, index: u32) -> Option<Root<WebGLActiveInfo>> {
- let program = match program {
- Some(program) => program,
- None => {
- // Reasons to generate InvalidValue error
- // From the GLES 2.0 spec
- //
- // "INVALID_VALUE is generated if index is greater than or equal
- // to the number of active attribute variables in program"
- //
- // A null program has no attributes so any index is always greater than the active uniforms
- // WebGl conformance expects error with null programs. Check tests in get-active-test.html
- self.webgl_error(InvalidValue);
- return None;
- }
+ fn GetActiveAttrib(
+ &self,
+ program: &WebGLProgram,
+ index: u32,
+ ) -> Option<DomRoot<WebGLActiveInfo>> {
+ handle_potential_webgl_error!(self, self.validate_ownership(program), 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, self.validate_ownership(program), return -1);
+ handle_potential_webgl_error!(self, program.get_attrib_location(name), -1)
+ }
+
+ #[allow(unsafe_code)]
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6
+ fn GetFramebufferAttachmentParameter(
+ &self,
+ cx: SafeJSContext,
+ target: u32,
+ attachment: u32,
+ pname: u32,
+ ) -> JSVal {
+ // Check if currently bound framebuffer is non-zero as per spec.
+ if let Some(fb) = self.bound_draw_framebuffer.get() {
+ // Opaque framebuffers cannot have their attachments inspected
+ // https://immersive-web.github.io/webxr/#opaque-framebuffer
+ handle_potential_webgl_error!(self, fb.validate_transparent(), return NullValue());
+ } else {
+ self.webgl_error(InvalidOperation);
+ return NullValue();
+ }
+
+ // Note: commented out stuff is for the WebGL2 standard.
+ let target_matches = match target {
+ // constants::READ_FRAMEBUFFER |
+ // constants::DRAW_FRAMEBUFFER => true,
+ constants::FRAMEBUFFER => true,
+ _ => false,
+ };
+ let attachment_matches = match attachment {
+ // constants::MAX_COLOR_ATTACHMENTS ... gl::COLOR_ATTACHMENT0 |
+ // constants::BACK |
+ constants::COLOR_ATTACHMENT0 |
+ constants::DEPTH_STENCIL_ATTACHMENT |
+ constants::DEPTH_ATTACHMENT |
+ constants::STENCIL_ATTACHMENT => true,
+ _ => false,
+ };
+ let pname_matches = match pname {
+ // constants::FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE |
+ // constants::FRAMEBUFFER_ATTACHMENT_BLUE_SIZE |
+ // constants::FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING |
+ // constants::FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE |
+ // constants::FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE |
+ // constants::FRAMEBUFFER_ATTACHMENT_GREEN_SIZE |
+ // constants::FRAMEBUFFER_ATTACHMENT_RED_SIZE |
+ // constants::FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE |
+ // constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER |
+ constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME |
+ constants::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE |
+ constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE |
+ constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL => true,
+ _ => false,
};
- match program.get_active_attrib(index) {
- Ok(ret) => Some(ret),
- Err(e) => {
- self.webgl_error(e);
- return None;
+ let bound_attachment_matches = match self
+ .bound_draw_framebuffer
+ .get()
+ .unwrap()
+ .attachment(attachment)
+ {
+ Some(attachment_root) => match attachment_root {
+ WebGLFramebufferAttachmentRoot::Renderbuffer(_) => match pname {
+ constants::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE |
+ constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME => true,
+ _ => false,
+ },
+ WebGLFramebufferAttachmentRoot::Texture(_) => match pname {
+ constants::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE |
+ constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME |
+ constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL |
+ constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE => true,
+ _ => false,
+ },
+ },
+ _ => match pname {
+ constants::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE => true,
+ _ => false,
+ },
+ };
+
+ if !target_matches || !attachment_matches || !pname_matches || !bound_attachment_matches {
+ self.webgl_error(InvalidEnum);
+ return NullValue();
+ }
+
+ // From the GLES2 spec:
+ //
+ // If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is NONE,
+ // then querying any other pname will generate INVALID_ENUM.
+ //
+ // otherwise, return `WebGLRenderbuffer` or `WebGLTexture` dom object
+ if pname == constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME {
+ // if fb is None, an INVALID_OPERATION is returned
+ // at the beggining of the function, so `.unwrap()` will never panic
+ let fb = self.bound_draw_framebuffer.get().unwrap();
+ if let Some(webgl_attachment) = fb.attachment(attachment) {
+ match webgl_attachment {
+ WebGLFramebufferAttachmentRoot::Renderbuffer(rb) => unsafe {
+ rooted!(in(*cx) let mut rval = NullValue());
+ rb.to_jsval(*cx, rval.handle_mut());
+ return rval.get();
+ },
+ WebGLFramebufferAttachmentRoot::Texture(texture) => unsafe {
+ rooted!(in(*cx) let mut rval = NullValue());
+ texture.to_jsval(*cx, rval.handle_mut());
+ return rval.get();
+ },
+ }
}
+ self.webgl_error(InvalidEnum);
+ return NullValue();
}
+
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.send_command(WebGLCommand::GetFramebufferAttachmentParameter(
+ target, attachment, pname, sender,
+ ));
+
+ Int32Value(receiver.recv().unwrap())
}
- // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- fn GetAttribLocation(&self, program: Option<&WebGLProgram>, name: DOMString) -> i32 {
- if let Some(program) = program {
- handle_potential_webgl_error!(self, program.get_attrib_location(name), None).unwrap_or(-1)
- } else {
- -1
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7
+ fn GetRenderbufferParameter(&self, _cx: SafeJSContext, target: u32, pname: u32) -> JSVal {
+ // We do not check to see if the renderbuffer came from an opaque framebuffer
+ // https://github.com/immersive-web/webxr/issues/862
+ let target_matches = target == constants::RENDERBUFFER;
+
+ let pname_matches = match pname {
+ constants::RENDERBUFFER_WIDTH |
+ constants::RENDERBUFFER_HEIGHT |
+ constants::RENDERBUFFER_INTERNAL_FORMAT |
+ constants::RENDERBUFFER_RED_SIZE |
+ constants::RENDERBUFFER_GREEN_SIZE |
+ constants::RENDERBUFFER_BLUE_SIZE |
+ constants::RENDERBUFFER_ALPHA_SIZE |
+ constants::RENDERBUFFER_DEPTH_SIZE |
+ constants::RENDERBUFFER_STENCIL_SIZE => true,
+ _ => false,
+ };
+
+ if !target_matches || !pname_matches {
+ self.webgl_error(InvalidEnum);
+ return NullValue();
+ }
+
+ if self.bound_renderbuffer.get().is_none() {
+ self.webgl_error(InvalidOperation);
+ return NullValue();
}
+
+ let result = if pname == constants::RENDERBUFFER_INTERNAL_FORMAT {
+ let rb = self.bound_renderbuffer.get().unwrap();
+ rb.internal_format() as i32
+ } else {
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.send_command(WebGLCommand::GetRenderbufferParameter(
+ target, pname, sender,
+ ));
+ receiver.recv().unwrap()
+ };
+
+ Int32Value(result)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
- fn GetProgramInfoLog(&self, program: Option<&WebGLProgram>) -> Option<DOMString> {
- if let Some(program) = program {
- match program.get_info_log() {
- Ok(value) => Some(DOMString::from(value)),
- Err(e) => {
- self.webgl_error(e);
- None
- }
- }
- } else {
- self.webgl_error(WebGLError::InvalidValue);
- None
+ fn GetProgramInfoLog(&self, program: &WebGLProgram) -> Option<DOMString> {
+ handle_potential_webgl_error!(self, self.validate_ownership(program), return None);
+ match program.get_info_log() {
+ Ok(value) => Some(DOMString::from(value)),
+ Err(e) => {
+ self.webgl_error(e);
+ None
+ },
}
}
- #[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
- unsafe fn GetProgramParameter(&self, _: *mut JSContext, program: Option<&WebGLProgram>, param_id: u32) -> JSVal {
- if let Some(program) = program {
- match handle_potential_webgl_error!(self, program.parameter(param_id), WebGLParameter::Invalid) {
- WebGLParameter::Int(val) => Int32Value(val),
- WebGLParameter::Bool(val) => BooleanValue(val),
- WebGLParameter::String(_) => panic!("Program parameter should not be string"),
- WebGLParameter::Float(_) => panic!("Program parameter should not be float"),
- WebGLParameter::FloatArray(_) => {
- panic!("Program paramenter should not be float array")
- }
- WebGLParameter::Invalid => NullValue(),
- }
- } else {
- NullValue()
+ fn GetProgramParameter(&self, _: SafeJSContext, program: &WebGLProgram, param: u32) -> JSVal {
+ handle_potential_webgl_error!(self, self.validate_ownership(program), return NullValue());
+ if program.is_deleted() {
+ self.webgl_error(InvalidOperation);
+ return NullValue();
+ }
+ match param {
+ constants::DELETE_STATUS => BooleanValue(program.is_marked_for_deletion()),
+ 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::GetProgramValidateStatus(program.id(), sender));
+ BooleanValue(receiver.recv().unwrap())
+ },
+ 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 => Int32Value(program.active_uniforms().len() as i32),
+ _ => {
+ self.webgl_error(InvalidEnum);
+ NullValue()
+ },
}
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
- fn GetShaderInfoLog(&self, shader: Option<&WebGLShader>) -> Option<DOMString> {
- shader.and_then(|s| s.info_log()).map(DOMString::from)
+ fn GetShaderInfoLog(&self, shader: &WebGLShader) -> Option<DOMString> {
+ handle_potential_webgl_error!(self, self.validate_ownership(shader), return None);
+ Some(shader.info_log())
}
- #[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
- unsafe fn GetShaderParameter(&self, _: *mut JSContext, shader: Option<&WebGLShader>, param_id: u32) -> JSVal {
- if let Some(shader) = shader {
- match handle_potential_webgl_error!(self, shader.parameter(param_id), WebGLParameter::Invalid) {
- WebGLParameter::Int(val) => Int32Value(val),
- WebGLParameter::Bool(val) => BooleanValue(val),
- WebGLParameter::String(_) => panic!("Shader parameter should not be string"),
- WebGLParameter::Float(_) => panic!("Shader parameter should not be float"),
- WebGLParameter::FloatArray(_) => {
- panic!("Shader paramenter should not be float array")
- }
- WebGLParameter::Invalid => NullValue(),
- }
- } else {
- NullValue()
+ fn GetShaderParameter(&self, _: SafeJSContext, shader: &WebGLShader, param: u32) -> JSVal {
+ handle_potential_webgl_error!(self, self.validate_ownership(shader), return NullValue());
+ if shader.is_deleted() {
+ self.webgl_error(InvalidValue);
+ return NullValue();
+ }
+ match param {
+ constants::DELETE_STATUS => BooleanValue(shader.is_marked_for_deletion()),
+ constants::COMPILE_STATUS => BooleanValue(shader.successfully_compiled()),
+ constants::SHADER_TYPE => UInt32Value(shader.gl_type()),
+ _ => {
+ self.webgl_error(InvalidEnum);
+ NullValue()
+ },
}
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
- fn GetShaderPrecisionFormat(&self,
- shader_type: u32,
- precision_type: u32)
- -> Option<Root<WebGLShaderPrecisionFormat>> {
- let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap();
- self.ipc_renderer.send(CanvasMsg::WebGL(WebGLCommand::GetShaderPrecisionFormat(shader_type,
- precision_type,
- sender)))
- .unwrap();
- match receiver.recv().unwrap() {
- Ok((range_min, range_max, precision)) => {
- Some(WebGLShaderPrecisionFormat::new(self.global().as_window(), range_min, range_max, precision))
- },
- Err(error) => {
- self.webgl_error(error);
- None
- }
+ fn GetShaderPrecisionFormat(
+ &self,
+ shader_type: u32,
+ precision_type: u32,
+ ) -> Option<DomRoot<WebGLShaderPrecisionFormat>> {
+ match shader_type {
+ constants::FRAGMENT_SHADER | constants::VERTEX_SHADER => (),
+ _ => {
+ self.webgl_error(InvalidEnum);
+ return None;
+ },
}
+
+ match precision_type {
+ constants::LOW_FLOAT |
+ constants::MEDIUM_FLOAT |
+ constants::HIGH_FLOAT |
+ constants::LOW_INT |
+ constants::MEDIUM_INT |
+ constants::HIGH_INT => (),
+ _ => {
+ self.webgl_error(InvalidEnum);
+ return None;
+ },
+ }
+
+ let (sender, receiver) = webgl_channel().unwrap();
+ self.send_command(WebGLCommand::GetShaderPrecisionFormat(
+ shader_type,
+ precision_type,
+ sender,
+ ));
+
+ let (range_min, range_max, precision) = receiver.recv().unwrap();
+ Some(WebGLShaderPrecisionFormat::new(
+ self.global().as_window(),
+ range_min,
+ range_max,
+ precision,
+ ))
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- fn GetUniformLocation(&self,
- program: Option<&WebGLProgram>,
- name: DOMString) -> Option<Root<WebGLUniformLocation>> {
- program.and_then(|p| {
- handle_potential_webgl_error!(self, p.get_uniform_location(name), None)
- .map(|location| WebGLUniformLocation::new(self.global().as_window(), location, p.id()))
- })
+ fn GetUniformLocation(
+ &self,
+ program: &WebGLProgram,
+ name: DOMString,
+ ) -> Option<DomRoot<WebGLUniformLocation>> {
+ handle_potential_webgl_error!(self, self.validate_ownership(program), return None);
+ handle_potential_webgl_error!(self, program.get_uniform_location(name), None)
}
#[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, pname: u32) -> JSVal {
- if index == 0 && pname == 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 (sender, receiver) = webrender_traits::channel::msg_channel().unwrap();
- self.ipc_renderer.send(CanvasMsg::WebGL(WebGLCommand::GetVertexAttrib(index, pname, sender))).unwrap();
-
- match handle_potential_webgl_error!(self, receiver.recv().unwrap(), WebGLParameter::Invalid) {
- WebGLParameter::Int(val) => Int32Value(val),
- WebGLParameter::Bool(val) => BooleanValue(val),
- WebGLParameter::String(_) => panic!("Vertex attrib should not be string"),
- WebGLParameter::Float(_) => panic!("Vertex attrib should not be float"),
- WebGLParameter::FloatArray(val) => {
- rooted!(in(cx) let mut result = UndefinedValue());
- val.to_jsval(cx, result.handle_mut());
- result.get()
+ fn GetVertexAttrib(&self, cx: SafeJSContext, index: u32, param: u32) -> JSVal {
+ let get_attrib = |data: Ref<VertexAttribData>| -> JSVal {
+ if param == constants::CURRENT_VERTEX_ATTRIB {
+ let attrib = self.current_vertex_attribs.borrow()[index as usize];
+ match attrib {
+ VertexAttrib::Float(x, y, z, w) => {
+ let value = [x, y, z, w];
+ unsafe {
+ 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());
+ }
+ },
+ VertexAttrib::Int(x, y, z, w) => {
+ let value = [x, y, z, w];
+ unsafe {
+ rooted!(in(*cx) let mut result = ptr::null_mut::<JSObject>());
+ let _ = Int32Array::create(
+ *cx,
+ CreateWith::Slice(&value),
+ result.handle_mut(),
+ )
+ .unwrap();
+ return ObjectValue(result.get());
+ }
+ },
+ VertexAttrib::Uint(x, y, z, w) => {
+ let value = [x, y, z, w];
+ unsafe {
+ rooted!(in(*cx) let mut result = ptr::null_mut::<JSObject>());
+ let _ = Uint32Array::create(
+ *cx,
+ CreateWith::Slice(&value),
+ result.handle_mut(),
+ )
+ .unwrap();
+ return ObjectValue(result.get());
+ }
+ },
+ };
+ }
+ if !self
+ .extension_manager
+ .is_get_vertex_attrib_name_enabled(param)
+ {
+ self.webgl_error(WebGLError::InvalidEnum);
+ return NullValue();
}
- WebGLParameter::Invalid => NullValue(),
+
+ 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 => unsafe {
+ rooted!(in(*cx) let mut jsval = NullValue());
+ if let Some(buffer) = data.buffer() {
+ buffer.to_jsval(*cx, jsval.handle_mut());
+ }
+ jsval.get()
+ },
+ ANGLEInstancedArraysConstants::VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE => {
+ UInt32Value(data.divisor)
+ },
+ _ => {
+ self.webgl_error(InvalidEnum);
+ NullValue()
+ },
+ }
+ };
+
+ match self.webgl_version() {
+ WebGLVersion::WebGL1 => {
+ let current_vao = self.current_vao();
+ let data = handle_potential_webgl_error!(
+ self,
+ current_vao.get_vertex_attrib(index).ok_or(InvalidValue),
+ return NullValue()
+ );
+ get_attrib(data)
+ },
+ WebGLVersion::WebGL2 => {
+ let current_vao = self.current_vao_webgl2();
+ let data = handle_potential_webgl_error!(
+ self,
+ current_vao.get_vertex_attrib(index).ok_or(InvalidValue),
+ return NullValue()
+ );
+ get_attrib(data)
+ },
+ }
+ }
+
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
+ fn GetVertexAttribOffset(&self, index: u32, pname: u32) -> i64 {
+ if pname != constants::VERTEX_ATTRIB_ARRAY_POINTER {
+ self.webgl_error(InvalidEnum);
+ return 0;
+ }
+ match self.webgl_version() {
+ WebGLVersion::WebGL1 => {
+ let current_vao = self.current_vao();
+ let data = handle_potential_webgl_error!(
+ self,
+ current_vao.get_vertex_attrib(index).ok_or(InvalidValue),
+ return 0
+ );
+ data.offset as i64
+ },
+ WebGLVersion::WebGL2 => {
+ let current_vao = self.current_vao_webgl2();
+ let data = handle_potential_webgl_error!(
+ self,
+ current_vao.get_vertex_attrib(index).ok_or(InvalidValue),
+ return 0
+ );
+ data.offset as i64
+ },
}
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn Hint(&self, target: u32, mode: u32) {
- if target != constants::GENERATE_MIPMAP_HINT {
+ if target != constants::GENERATE_MIPMAP_HINT &&
+ !self.extension_manager.is_hint_target_enabled(target)
+ {
return self.webgl_error(InvalidEnum);
}
match mode {
- constants::FASTEST |
- constants::NICEST |
- constants::DONT_CARE => (),
+ constants::FASTEST | constants::NICEST | constants::DONT_CARE => (),
_ => return self.webgl_error(InvalidEnum),
}
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Hint(target, mode)))
- .unwrap()
+ self.send_command(WebGLCommand::Hint(target, mode));
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5
fn IsBuffer(&self, buffer: Option<&WebGLBuffer>) -> bool {
- buffer.map_or(false, |buf| buf.target().is_some() && !buf.is_deleted())
+ buffer.map_or(false, |buf| {
+ self.validate_ownership(buf).is_ok() && buf.target().is_some() && !buf.is_deleted()
+ })
}
- // TODO: We could write this without IPC, recording the calls to `enable` and `disable`.
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn IsEnabled(&self, cap: u32) -> bool {
- if self.validate_feature_enum(cap) {
- let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap();
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::IsEnabled(cap, sender)))
- .unwrap();
- return receiver.recv().unwrap();
- }
-
- false
+ handle_potential_webgl_error!(self, self.capabilities.is_enabled(cap), false)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6
fn IsFramebuffer(&self, frame_buffer: Option<&WebGLFramebuffer>) -> bool {
- frame_buffer.map_or(false, |buf| buf.target().is_some() && !buf.is_deleted())
+ frame_buffer.map_or(false, |buf| {
+ self.validate_ownership(buf).is_ok() && buf.target().is_some() && !buf.is_deleted()
+ })
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
fn IsProgram(&self, program: Option<&WebGLProgram>) -> bool {
- program.map_or(false, |p| !p.is_deleted())
+ program.map_or(false, |p| {
+ self.validate_ownership(p).is_ok() && !p.is_deleted()
+ })
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7
fn IsRenderbuffer(&self, render_buffer: Option<&WebGLRenderbuffer>) -> bool {
- render_buffer.map_or(false, |buf| buf.ever_bound() && !buf.is_deleted())
+ render_buffer.map_or(false, |buf| {
+ self.validate_ownership(buf).is_ok() && buf.ever_bound() && !buf.is_deleted()
+ })
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
fn IsShader(&self, shader: Option<&WebGLShader>) -> bool {
- shader.map_or(false, |s| !s.is_deleted() || s.is_attached())
+ shader.map_or(false, |s| {
+ self.validate_ownership(s).is_ok() && !s.is_deleted()
+ })
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
fn IsTexture(&self, texture: Option<&WebGLTexture>) -> bool {
- texture.map_or(false, |tex| tex.target().is_some() && !tex.is_deleted())
+ texture.map_or(false, |tex| {
+ self.validate_ownership(tex).is_ok() && tex.target().is_some() && !tex.is_invalid()
+ })
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
@@ -2031,9 +3573,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
return self.webgl_error(InvalidValue);
}
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::LineWidth(width)))
- .unwrap()
+ self.send_command(WebGLCommand::LineWidth(width))
}
// NOTE: Usage of this function could affect rendering while we keep using
@@ -2043,196 +3583,184 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
let mut texture_settings = self.texture_unpacking_settings.get();
match param_name {
constants::UNPACK_FLIP_Y_WEBGL => {
- if param_value != 0 {
- texture_settings.insert(FLIP_Y_AXIS)
- } else {
- texture_settings.remove(FLIP_Y_AXIS)
- }
-
- self.texture_unpacking_settings.set(texture_settings);
- return;
+ texture_settings.set(TextureUnpacking::FLIP_Y_AXIS, param_value != 0);
},
constants::UNPACK_PREMULTIPLY_ALPHA_WEBGL => {
- if param_value != 0 {
- texture_settings.insert(PREMULTIPLY_ALPHA)
- } else {
- texture_settings.remove(PREMULTIPLY_ALPHA)
- }
-
- self.texture_unpacking_settings.set(texture_settings);
- return;
+ texture_settings.set(TextureUnpacking::PREMULTIPLY_ALPHA, param_value != 0);
},
constants::UNPACK_COLORSPACE_CONVERSION_WEBGL => {
- match param_value as u32 {
- constants::BROWSER_DEFAULT_WEBGL
- => texture_settings.insert(CONVERT_COLORSPACE),
- constants::NONE
- => texture_settings.remove(CONVERT_COLORSPACE),
+ let convert = match param_value as u32 {
+ constants::BROWSER_DEFAULT_WEBGL => true,
+ constants::NONE => false,
_ => return self.webgl_error(InvalidEnum),
+ };
+ texture_settings.set(TextureUnpacking::CONVERT_COLORSPACE, convert);
+ },
+ constants::UNPACK_ALIGNMENT => {
+ match param_value {
+ 1 | 2 | 4 | 8 => (),
+ _ => return self.webgl_error(InvalidValue),
}
-
- self.texture_unpacking_settings.set(texture_settings);
+ self.texture_unpacking_alignment.set(param_value as u32);
return;
},
- constants::UNPACK_ALIGNMENT |
constants::PACK_ALIGNMENT => {
match param_value {
1 | 2 | 4 | 8 => (),
_ => return self.webgl_error(InvalidValue),
}
- self.texture_unpacking_alignment.set(param_value as u32);
+ // We never actually change the actual value on the GL side
+ // because it's better to receive the pixels without the padding
+ // and then write the result at the right place in ReadPixels.
+ self.texture_packing_alignment.set(param_value as u8);
return;
},
_ => return self.webgl_error(InvalidEnum),
}
+ self.texture_unpacking_settings.set(texture_settings);
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn PolygonOffset(&self, factor: f32, units: f32) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::PolygonOffset(factor, units)))
- .unwrap()
+ self.send_command(WebGLCommand::PolygonOffset(factor, units))
}
- #[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.12
- unsafe fn ReadPixels(&self, cx: *mut JSContext, x: i32, y: i32, width: i32, height: i32,
- format: u32, pixel_type: u32, pixels: *mut JSObject) -> Fallible<()> {
- if pixels.is_null() {
- return Ok(self.webgl_error(InvalidValue));
- }
-
- typedarray!(in(cx) let mut pixels_data: ArrayBufferView = pixels);
- let (array_type, mut data) = match { pixels_data.as_mut() } {
- Ok(data) => (data.get_array_type(), data.as_mut_slice()),
- Err(_) => return Err(Error::Type("Not an ArrayBufferView".to_owned())),
- };
-
- if !self.validate_framebuffer_complete() {
- return Ok(());
- }
+ #[allow(unsafe_code)]
+ fn ReadPixels(
+ &self,
+ x: i32,
+ y: i32,
+ width: i32,
+ height: i32,
+ format: u32,
+ pixel_type: u32,
+ mut pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
+ ) {
+ handle_potential_webgl_error!(self, self.validate_framebuffer(), return);
+
+ let pixels =
+ handle_potential_webgl_error!(self, pixels.as_mut().ok_or(InvalidValue), return);
- match array_type {
- Type::Uint8 => (),
- _ => return Ok(self.webgl_error(InvalidOperation)),
+ if width < 0 || height < 0 {
+ return self.webgl_error(InvalidValue);
}
- // From the WebGL specification, 5.14.12 Reading back pixels
- //
- // "Only two combinations of format and type are
- // accepted. The first is format RGBA and type
- // UNSIGNED_BYTE. The second is an implementation-chosen
- // format. The values of format and type for this format
- // may be determined by calling getParameter with the
- // symbolic constants IMPLEMENTATION_COLOR_READ_FORMAT
- // and IMPLEMENTATION_COLOR_READ_TYPE, respectively. The
- // implementation-chosen format may vary depending on the
- // format of the currently bound rendering
- // surface. Unsupported combinations of format and type
- // will generate an INVALID_OPERATION error."
- //
- // To avoid having to support general format packing math, we
- // always report RGBA/UNSIGNED_BYTE as our only supported
- // format.
if format != constants::RGBA || pixel_type != constants::UNSIGNED_BYTE {
- return Ok(self.webgl_error(InvalidOperation));
+ return self.webgl_error(InvalidOperation);
}
- let cpp = 4;
-
- // "If pixels is non-null, but is not large enough to
- // retrieve all of the pixels in the specified rectangle
- // taking into account pixel store modes, an
- // INVALID_OPERATION error is generated."
- let stride = match width.checked_mul(cpp) {
- Some(stride) => stride,
- _ => return Ok(self.webgl_error(InvalidOperation)),
- };
- match height.checked_mul(stride) {
- Some(size) if size <= data.len() as i32 => {}
- _ => return Ok(self.webgl_error(InvalidOperation)),
+ if pixels.get_array_type() != Type::Uint8 {
+ return self.webgl_error(InvalidOperation);
}
- // "For any pixel lying outside the frame buffer, the
- // corresponding destination buffer range remains
- // untouched; see Reading Pixels Outside the
- // Framebuffer."
- let mut x = x;
- let mut y = y;
- let mut width = width;
- let mut height = height;
- let mut dst_offset = 0;
+ let (fb_width, fb_height) = handle_potential_webgl_error!(
+ self,
+ self.get_current_framebuffer_size().ok_or(InvalidOperation),
+ return
+ );
- if x < 0 {
- dst_offset += cpp * -x;
- width += x;
- x = 0;
+ if width == 0 || height == 0 {
+ return;
}
- if y < 0 {
- dst_offset += stride * -y;
- height += y;
- y = 0;
- }
+ let bytes_per_pixel = 4;
- if width < 0 || height < 0 {
- return Ok(self.webgl_error(InvalidValue));
- }
+ let row_len = handle_potential_webgl_error!(
+ self,
+ width.checked_mul(bytes_per_pixel).ok_or(InvalidOperation),
+ return
+ );
- match self.get_current_framebuffer_size() {
- Some((fb_width, fb_height)) => {
- if x + width > fb_width {
- width = fb_width - x;
- }
- if y + height > fb_height {
- height = fb_height - y;
- }
- }
- _ => return Ok(self.webgl_error(InvalidOperation)),
+ let pack_alignment = self.texture_packing_alignment.get() as i32;
+ let dest_padding = match row_len % pack_alignment {
+ 0 => 0,
+ remainder => pack_alignment - remainder,
};
+ let dest_stride = row_len + dest_padding;
+
+ let full_rows_len = handle_potential_webgl_error!(
+ self,
+ dest_stride.checked_mul(height - 1).ok_or(InvalidOperation),
+ return
+ );
+ let required_dest_len = handle_potential_webgl_error!(
+ self,
+ full_rows_len.checked_add(row_len).ok_or(InvalidOperation),
+ return
+ );
+
+ let dest = unsafe { pixels.as_mut_slice() };
+ if dest.len() < required_dest_len as usize {
+ return self.webgl_error(InvalidOperation);
+ }
- let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap();
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::ReadPixels(x, y, width, height, format, pixel_type, sender)))
- .unwrap();
+ let src_origin = Point2D::new(x, y);
+ let src_size = Size2D::new(width as u32, height as u32);
+ let fb_size = Size2D::new(fb_width as u32, fb_height as u32);
+ let src_rect = match pixels::clip(src_origin, src_size.to_u64(), fb_size.to_u64()) {
+ Some(rect) => rect,
+ None => return,
+ };
- let result = receiver.recv().unwrap();
+ // Note: we're casting a Rect<u64> back into a Rect<u32> here, but it's okay because
+ // it used u32 data types to begin with. It just got converted to Rect<u64> in
+ // pixels::clip
+ let src_rect = src_rect.to_u32();
- for i in 0..height {
- for j in 0..(width * cpp) {
- data[(dst_offset + i * stride + j) as usize] =
- result[(i * width * cpp + j) as usize];
- }
+ let mut dest_offset = 0;
+ if x < 0 {
+ dest_offset += -x * bytes_per_pixel;
+ }
+ if y < 0 {
+ dest_offset += -y * row_len;
}
- Ok(())
+ let (sender, receiver) = ipc::bytes_channel().unwrap();
+ self.send_command(WebGLCommand::ReadPixels(
+ src_rect, format, pixel_type, sender,
+ ));
+ let src = receiver.recv().unwrap();
+
+ let src_row_len = src_rect.size.width as usize * bytes_per_pixel as usize;
+ for i in 0..src_rect.size.height {
+ let dest_start = dest_offset as usize + i as usize * dest_stride as usize;
+ let dest_end = dest_start + src_row_len;
+ let src_start = i as usize * src_row_len;
+ let src_end = src_start + src_row_len;
+ dest[dest_start..dest_end].copy_from_slice(&src[src_start..src_end]);
+ }
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn SampleCoverage(&self, value: f32, invert: bool) {
- self.ipc_renderer.send(CanvasMsg::WebGL(WebGLCommand::SampleCoverage(value, invert))).unwrap();
+ self.send_command(WebGLCommand::SampleCoverage(value, invert));
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.4
fn Scissor(&self, x: i32, y: i32, width: i32, height: i32) {
if width < 0 || height < 0 {
- return self.webgl_error(InvalidValue)
+ return self.webgl_error(InvalidValue);
}
+ let width = width as u32;
+ let height = height as u32;
+
self.current_scissor.set((x, y, width, height));
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Scissor(x, y, width, height)))
- .unwrap()
+ self.send_command(WebGLCommand::Scissor(x, y, width, height));
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn StencilFunc(&self, func: u32, ref_: i32, mask: u32) {
match func {
- constants::NEVER | constants::LESS | constants::EQUAL | constants::LEQUAL |
- constants::GREATER | constants::NOTEQUAL | constants::GEQUAL | constants::ALWAYS =>
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::StencilFunc(func, ref_, mask)))
- .unwrap(),
+ constants::NEVER |
+ constants::LESS |
+ constants::EQUAL |
+ constants::LEQUAL |
+ constants::GREATER |
+ constants::NOTEQUAL |
+ constants::GEQUAL |
+ constants::ALWAYS => self.send_command(WebGLCommand::StencilFunc(func, ref_, mask)),
_ => self.webgl_error(InvalidEnum),
}
}
@@ -2245,40 +3773,42 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
}
match func {
- constants::NEVER | constants::LESS | constants::EQUAL | constants::LEQUAL |
- constants::GREATER | constants::NOTEQUAL | constants::GEQUAL | constants::ALWAYS =>
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::StencilFuncSeparate(face, func, ref_, mask)))
- .unwrap(),
+ constants::NEVER |
+ constants::LESS |
+ constants::EQUAL |
+ constants::LEQUAL |
+ constants::GREATER |
+ constants::NOTEQUAL |
+ constants::GEQUAL |
+ constants::ALWAYS => {
+ self.send_command(WebGLCommand::StencilFuncSeparate(face, func, ref_, mask))
+ },
_ => self.webgl_error(InvalidEnum),
}
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn StencilMask(&self, mask: u32) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::StencilMask(mask)))
- .unwrap()
+ self.send_command(WebGLCommand::StencilMask(mask))
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn StencilMaskSeparate(&self, face: u32, mask: u32) {
match face {
- constants::FRONT | constants::BACK | constants::FRONT_AND_BACK =>
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::StencilMaskSeparate(face, mask)))
- .unwrap(),
+ constants::FRONT | constants::BACK | constants::FRONT_AND_BACK => {
+ self.send_command(WebGLCommand::StencilMaskSeparate(face, mask))
+ },
_ => return self.webgl_error(InvalidEnum),
- }
+ };
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn StencilOp(&self, fail: u32, zfail: u32, zpass: u32) {
- if self.validate_stencil_actions(fail) && self.validate_stencil_actions(zfail) &&
- self.validate_stencil_actions(zpass) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::StencilOp(fail, zfail, zpass)))
- .unwrap()
+ if self.validate_stencil_actions(fail) &&
+ self.validate_stencil_actions(zfail) &&
+ self.validate_stencil_actions(zpass)
+ {
+ self.send_command(WebGLCommand::StencilOp(fail, zfail, zpass));
} else {
self.webgl_error(InvalidEnum)
}
@@ -2291,368 +3821,316 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
_ => return self.webgl_error(InvalidEnum),
}
- if self.validate_stencil_actions(fail) && self.validate_stencil_actions(zfail) &&
- self.validate_stencil_actions(zpass) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::StencilOpSeparate(face, fail, zfail, zpass)))
- .unwrap()
+ if self.validate_stencil_actions(fail) &&
+ self.validate_stencil_actions(zfail) &&
+ self.validate_stencil_actions(zpass)
+ {
+ self.send_command(WebGLCommand::StencilOpSeparate(face, fail, zfail, zpass))
} else {
self.webgl_error(InvalidEnum)
}
}
// 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) {
+ handle_potential_webgl_error!(self, self.validate_ownership(program), return);
+ if program.is_deleted() {
+ return self.webgl_error(InvalidValue);
}
+ handle_potential_webgl_error!(self, program.link());
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
- fn ShaderSource(&self, shader: Option<&WebGLShader>, source: DOMString) {
- if let Some(shader) = shader {
- shader.set_source(source)
- }
+ fn ShaderSource(&self, shader: &WebGLShader, source: DOMString) {
+ handle_potential_webgl_error!(self, self.validate_ownership(shader), return);
+ shader.set_source(source)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
- fn GetShaderSource(&self, shader: Option<&WebGLShader>) -> Option<DOMString> {
- shader.and_then(|s| s.source())
+ fn GetShaderSource(&self, shader: &WebGLShader) -> Option<DOMString> {
+ handle_potential_webgl_error!(self, self.validate_ownership(shader), return None);
+ Some(shader.source())
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- fn Uniform1f(&self,
- uniform: Option<&WebGLUniformLocation>,
- val: f32) {
- if self.validate_uniform_parameters(uniform, UniformSetterType::Float, &[val]) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Uniform1f(uniform.unwrap().id(), val)))
- .unwrap()
- }
+ fn Uniform1f(&self, location: Option<&WebGLUniformLocation>, val: f32) {
+ self.with_location(location, |location| {
+ match location.type_() {
+ constants::BOOL | constants::FLOAT => {},
+ _ => return Err(InvalidOperation),
+ }
+ self.send_command(WebGLCommand::Uniform1f(location.id(), val));
+ Ok(())
+ });
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- fn Uniform1i(&self,
- uniform: Option<&WebGLUniformLocation>,
- val: i32) {
- if self.validate_uniform_parameters(uniform, UniformSetterType::Int, &[val]) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Uniform1i(uniform.unwrap().id(), val)))
- .unwrap()
- }
+ fn Uniform1i(&self, location: Option<&WebGLUniformLocation>, val: i32) {
+ self.with_location(location, |location| {
+ match location.type_() {
+ constants::BOOL | constants::INT => {},
+ constants::SAMPLER_2D | constants::SAMPLER_CUBE => {
+ if val < 0 || val as u32 >= self.limits.max_combined_texture_image_units {
+ return Err(InvalidValue);
+ }
+ },
+ _ => return Err(InvalidOperation),
+ }
+ self.send_command(WebGLCommand::Uniform1i(location.id(), val));
+ Ok(())
+ });
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- #[allow(unsafe_code)]
- unsafe fn Uniform1iv(&self,
- cx: *mut JSContext,
- uniform: Option<&WebGLUniformLocation>,
- data: *mut JSObject) -> Fallible<()> {
- assert!(!data.is_null());
- let data_vec = try!(typed_array_or_sequence_to_vec::<Int32>(cx, data, ConversionBehavior::Default));
-
- if self.validate_uniform_parameters(uniform, UniformSetterType::Int, &data_vec) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Uniform1iv(uniform.unwrap().id(), data_vec)))
- .unwrap()
- }
-
- Ok(())
+ fn Uniform1iv(&self, location: Option<&WebGLUniformLocation>, val: Int32ArrayOrLongSequence) {
+ self.uniform1iv(location, val, 0, 0)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- #[allow(unsafe_code)]
- unsafe fn Uniform1fv(&self,
- cx: *mut JSContext,
- uniform: Option<&WebGLUniformLocation>,
- data: *mut JSObject) -> Fallible<()> {
- assert!(!data.is_null());
- let data_vec = try!(typed_array_or_sequence_to_vec::<Float32>(cx, data, ()));
-
- if self.validate_uniform_parameters(uniform, UniformSetterType::Float, &data_vec) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Uniform1fv(uniform.unwrap().id(), data_vec)))
- .unwrap()
- }
-
- Ok(())
+ fn Uniform1fv(
+ &self,
+ location: Option<&WebGLUniformLocation>,
+ val: Float32ArrayOrUnrestrictedFloatSequence,
+ ) {
+ self.uniform1fv(location, val, 0, 0)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- fn Uniform2f(&self,
- uniform: Option<&WebGLUniformLocation>,
- x: f32, y: f32) {
- if self.validate_uniform_parameters(uniform, UniformSetterType::FloatVec2, &[x, y]) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Uniform2f(uniform.unwrap().id(), x, y)))
- .unwrap()
- }
+ fn Uniform2f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32) {
+ self.with_location(location, |location| {
+ match location.type_() {
+ constants::BOOL_VEC2 | constants::FLOAT_VEC2 => {},
+ _ => return Err(InvalidOperation),
+ }
+ self.send_command(WebGLCommand::Uniform2f(location.id(), x, y));
+ Ok(())
+ });
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- #[allow(unsafe_code)]
- unsafe fn Uniform2fv(&self,
- cx: *mut JSContext,
- uniform: Option<&WebGLUniformLocation>,
- data: *mut JSObject) -> Fallible<()> {
- assert!(!data.is_null());
- let data_vec = try!(typed_array_or_sequence_to_vec::<Float32>(cx, data, ()));
-
- if self.validate_uniform_parameters(uniform,
- UniformSetterType::FloatVec2,
- &data_vec) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Uniform2fv(uniform.unwrap().id(), data_vec)))
- .unwrap()
- }
-
- Ok(())
+ fn Uniform2fv(
+ &self,
+ location: Option<&WebGLUniformLocation>,
+ val: Float32ArrayOrUnrestrictedFloatSequence,
+ ) {
+ self.uniform2fv(location, val, 0, 0)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- fn Uniform2i(&self,
- uniform: Option<&WebGLUniformLocation>,
- x: i32, y: i32) {
- if self.validate_uniform_parameters(uniform,
- UniformSetterType::IntVec2,
- &[x, y]) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Uniform2i(uniform.unwrap().id(), x, y)))
- .unwrap()
- }
+ fn Uniform2i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32) {
+ self.with_location(location, |location| {
+ match location.type_() {
+ constants::BOOL_VEC2 | constants::INT_VEC2 => {},
+ _ => return Err(InvalidOperation),
+ }
+ self.send_command(WebGLCommand::Uniform2i(location.id(), x, y));
+ Ok(())
+ });
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- #[allow(unsafe_code)]
- unsafe fn Uniform2iv(&self,
- cx: *mut JSContext,
- uniform: Option<&WebGLUniformLocation>,
- data: *mut JSObject) -> Fallible<()> {
- assert!(!data.is_null());
- let data_vec = try!(typed_array_or_sequence_to_vec::<Int32>(cx, data, ConversionBehavior::Default));
-
- if self.validate_uniform_parameters(uniform,
- UniformSetterType::IntVec2,
- &data_vec) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Uniform2iv(uniform.unwrap().id(), data_vec)))
- .unwrap()
- }
-
- Ok(())
+ fn Uniform2iv(&self, location: Option<&WebGLUniformLocation>, val: Int32ArrayOrLongSequence) {
+ self.uniform2iv(location, val, 0, 0)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- fn Uniform3f(&self,
- uniform: Option<&WebGLUniformLocation>,
- x: f32, y: f32, z: f32) {
- if self.validate_uniform_parameters(uniform,
- UniformSetterType::FloatVec3,
- &[x, y, z]) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Uniform3f(uniform.unwrap().id(), x, y, z)))
- .unwrap()
- }
+ fn Uniform3f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32, z: f32) {
+ self.with_location(location, |location| {
+ match location.type_() {
+ constants::BOOL_VEC3 | constants::FLOAT_VEC3 => {},
+ _ => return Err(InvalidOperation),
+ }
+ self.send_command(WebGLCommand::Uniform3f(location.id(), x, y, z));
+ Ok(())
+ });
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- #[allow(unsafe_code)]
- unsafe fn Uniform3fv(&self,
- cx: *mut JSContext,
- uniform: Option<&WebGLUniformLocation>,
- data: *mut JSObject) -> Fallible<()> {
- assert!(!data.is_null());
- let data_vec = try!(typed_array_or_sequence_to_vec::<Float32>(cx, data, ()));
-
- if self.validate_uniform_parameters(uniform,
- UniformSetterType::FloatVec3,
- &data_vec) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Uniform3fv(uniform.unwrap().id(), data_vec)))
- .unwrap()
- }
-
- Ok(())
+ fn Uniform3fv(
+ &self,
+ location: Option<&WebGLUniformLocation>,
+ val: Float32ArrayOrUnrestrictedFloatSequence,
+ ) {
+ self.uniform3fv(location, val, 0, 0)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- fn Uniform3i(&self,
- uniform: Option<&WebGLUniformLocation>,
- x: i32, y: i32, z: i32) {
- if self.validate_uniform_parameters(uniform,
- UniformSetterType::IntVec3,
- &[x, y, z]) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Uniform3i(uniform.unwrap().id(), x, y, z)))
- .unwrap()
- }
+ fn Uniform3i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32, z: i32) {
+ self.with_location(location, |location| {
+ match location.type_() {
+ constants::BOOL_VEC3 | constants::INT_VEC3 => {},
+ _ => return Err(InvalidOperation),
+ }
+ self.send_command(WebGLCommand::Uniform3i(location.id(), x, y, z));
+ Ok(())
+ });
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- #[allow(unsafe_code)]
- unsafe fn Uniform3iv(&self,
- cx: *mut JSContext,
- uniform: Option<&WebGLUniformLocation>,
- data: *mut JSObject) -> Fallible<()> {
- assert!(!data.is_null());
- let data_vec = try!(typed_array_or_sequence_to_vec::<Int32>(cx, data, ConversionBehavior::Default));
-
- if self.validate_uniform_parameters(uniform,
- UniformSetterType::IntVec3,
- &data_vec) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Uniform3iv(uniform.unwrap().id(), data_vec)))
- .unwrap()
- }
-
- Ok(())
+ fn Uniform3iv(&self, location: Option<&WebGLUniformLocation>, val: Int32ArrayOrLongSequence) {
+ self.uniform3iv(location, val, 0, 0)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- fn Uniform4i(&self,
- uniform: Option<&WebGLUniformLocation>,
- x: i32, y: i32, z: i32, w: i32) {
- if self.validate_uniform_parameters(uniform,
- UniformSetterType::IntVec4,
- &[x, y, z, w]) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Uniform4i(uniform.unwrap().id(), x, y, z, w)))
- .unwrap()
- }
+ fn Uniform4i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32, z: i32, w: i32) {
+ self.with_location(location, |location| {
+ match location.type_() {
+ constants::BOOL_VEC4 | constants::INT_VEC4 => {},
+ _ => return Err(InvalidOperation),
+ }
+ self.send_command(WebGLCommand::Uniform4i(location.id(), x, y, z, w));
+ Ok(())
+ });
}
-
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- #[allow(unsafe_code)]
- unsafe fn Uniform4iv(&self,
- cx: *mut JSContext,
- uniform: Option<&WebGLUniformLocation>,
- data: *mut JSObject) -> Fallible<()> {
- assert!(!data.is_null());
- let data_vec = try!(typed_array_or_sequence_to_vec::<Int32>(cx, data, ConversionBehavior::Default));
-
- if self.validate_uniform_parameters(uniform,
- UniformSetterType::IntVec4,
- &data_vec) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Uniform4iv(uniform.unwrap().id(), data_vec)))
- .unwrap()
- }
-
- Ok(())
+ fn Uniform4iv(&self, location: Option<&WebGLUniformLocation>, val: Int32ArrayOrLongSequence) {
+ self.uniform4iv(location, val, 0, 0)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- fn Uniform4f(&self,
- uniform: Option<&WebGLUniformLocation>,
- x: f32, y: f32, z: f32, w: f32) {
- if self.validate_uniform_parameters(uniform,
- UniformSetterType::FloatVec4,
- &[x, y, z, w]) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Uniform4f(uniform.unwrap().id(), x, y, z, w)))
- .unwrap()
- }
+ fn Uniform4f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32, z: f32, w: f32) {
+ self.with_location(location, |location| {
+ match location.type_() {
+ constants::BOOL_VEC4 | constants::FLOAT_VEC4 => {},
+ _ => return Err(InvalidOperation),
+ }
+ self.send_command(WebGLCommand::Uniform4f(location.id(), x, y, z, w));
+ Ok(())
+ });
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- #[allow(unsafe_code)]
- unsafe fn Uniform4fv(&self,
- cx: *mut JSContext,
- uniform: Option<&WebGLUniformLocation>,
- data: *mut JSObject) -> Fallible<()> {
- assert!(!data.is_null());
- let data_vec = try!(typed_array_or_sequence_to_vec::<Float32>(cx, data, ()));
-
- if self.validate_uniform_parameters(uniform,
- UniformSetterType::FloatVec4,
- &data_vec) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Uniform4fv(uniform.unwrap().id(), data_vec)))
- .unwrap()
- }
-
- Ok(())
+ fn Uniform4fv(
+ &self,
+ location: Option<&WebGLUniformLocation>,
+ val: Float32ArrayOrUnrestrictedFloatSequence,
+ ) {
+ self.uniform4fv(location, val, 0, 0)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- #[allow(unsafe_code)]
- unsafe fn UniformMatrix2fv(&self,
- cx: *mut JSContext,
- uniform: Option<&WebGLUniformLocation>,
- transpose: bool,
- data: *mut JSObject) -> Fallible<()> {
- assert!(!data.is_null());
- let data_vec = try!(typed_array_or_sequence_to_vec::<Float32>(cx, data, ()));
- if self.validate_uniform_parameters(uniform,
- UniformSetterType::FloatMat2,
- &data_vec) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::UniformMatrix2fv(uniform.unwrap().id(), transpose, data_vec)))
- .unwrap()
- }
-
- Ok(())
+ fn UniformMatrix2fv(
+ &self,
+ location: Option<&WebGLUniformLocation>,
+ transpose: bool,
+ val: Float32ArrayOrUnrestrictedFloatSequence,
+ ) {
+ self.uniform_matrix_2fv(location, transpose, val, 0, 0)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- #[allow(unsafe_code)]
- unsafe fn UniformMatrix3fv(&self,
- cx: *mut JSContext,
- uniform: Option<&WebGLUniformLocation>,
- transpose: bool,
- data: *mut JSObject) -> Fallible<()> {
- assert!(!data.is_null());
- let data_vec = try!(typed_array_or_sequence_to_vec::<Float32>(cx, data, ()));
- if self.validate_uniform_parameters(uniform,
- UniformSetterType::FloatMat3,
- &data_vec) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::UniformMatrix3fv(uniform.unwrap().id(), transpose, data_vec)))
- .unwrap()
- }
+ fn UniformMatrix3fv(
+ &self,
+ location: Option<&WebGLUniformLocation>,
+ transpose: bool,
+ val: Float32ArrayOrUnrestrictedFloatSequence,
+ ) {
+ self.uniform_matrix_3fv(location, transpose, val, 0, 0)
+ }
- Ok(())
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
+ fn UniformMatrix4fv(
+ &self,
+ location: Option<&WebGLUniformLocation>,
+ transpose: bool,
+ val: Float32ArrayOrUnrestrictedFloatSequence,
+ ) {
+ self.uniform_matrix_4fv(location, transpose, val, 0, 0)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
#[allow(unsafe_code)]
- unsafe fn UniformMatrix4fv(&self,
- cx: *mut JSContext,
- uniform: Option<&WebGLUniformLocation>,
- transpose: bool,
- data: *mut JSObject) -> Fallible<()> {
- assert!(!data.is_null());
- let data_vec = try!(typed_array_or_sequence_to_vec::<Float32>(cx, data, ()));
- if self.validate_uniform_parameters(uniform,
- UniformSetterType::FloatMat4,
- &data_vec) {
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::UniformMatrix4fv(uniform.unwrap().id(), transpose, data_vec)))
- .unwrap()
+ fn GetUniform(
+ &self,
+ cx: SafeJSContext,
+ program: &WebGLProgram,
+ location: &WebGLUniformLocation,
+ ) -> JSVal {
+ handle_potential_webgl_error!(
+ self,
+ self.uniform_check_program(program, location),
+ return NullValue()
+ );
+
+ let triple = (self, program.id(), location.id());
+
+ match location.type_() {
+ constants::BOOL => BooleanValue(uniform_get(triple, WebGLCommand::GetUniformBool)),
+ constants::BOOL_VEC2 => unsafe {
+ rooted!(in(*cx) let mut rval = NullValue());
+ uniform_get(triple, WebGLCommand::GetUniformBool2).to_jsval(*cx, rval.handle_mut());
+ rval.get()
+ },
+ constants::BOOL_VEC3 => unsafe {
+ rooted!(in(*cx) let mut rval = NullValue());
+ uniform_get(triple, WebGLCommand::GetUniformBool3).to_jsval(*cx, rval.handle_mut());
+ rval.get()
+ },
+ constants::BOOL_VEC4 => unsafe {
+ rooted!(in(*cx) let mut rval = NullValue());
+ uniform_get(triple, WebGLCommand::GetUniformBool4).to_jsval(*cx, rval.handle_mut());
+ rval.get()
+ },
+ constants::INT | constants::SAMPLER_2D | constants::SAMPLER_CUBE => {
+ Int32Value(uniform_get(triple, WebGLCommand::GetUniformInt))
+ },
+ constants::INT_VEC2 => unsafe {
+ uniform_typed::<Int32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformInt2))
+ },
+ constants::INT_VEC3 => unsafe {
+ uniform_typed::<Int32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformInt3))
+ },
+ constants::INT_VEC4 => unsafe {
+ uniform_typed::<Int32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformInt4))
+ },
+ constants::FLOAT => {
+ DoubleValue(uniform_get(triple, WebGLCommand::GetUniformFloat) as f64)
+ },
+ constants::FLOAT_VEC2 => unsafe {
+ uniform_typed::<Float32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformFloat2))
+ },
+ constants::FLOAT_VEC3 => unsafe {
+ uniform_typed::<Float32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformFloat3))
+ },
+ constants::FLOAT_VEC4 | constants::FLOAT_MAT2 => unsafe {
+ uniform_typed::<Float32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformFloat4))
+ },
+ constants::FLOAT_MAT3 => unsafe {
+ uniform_typed::<Float32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformFloat9))
+ },
+ constants::FLOAT_MAT4 => unsafe {
+ uniform_typed::<Float32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformFloat16))
+ },
+ _ => panic!("wrong uniform type"),
}
-
- Ok(())
}
// 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),
+ handle_potential_webgl_error!(self, self.validate_ownership(program), return);
+ if program.is_deleted() || !program.is_linked() {
+ return self.webgl_error(InvalidOperation);
+ }
+ if program.is_in_use() {
+ return;
}
+ program.in_use(true);
}
+ match self.current_program.get() {
+ Some(ref current) if program != Some(&**current) => current.in_use(false),
+ _ => {},
+ }
+ 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
- fn ValidateProgram(&self, program: Option<&WebGLProgram>) {
- if let Some(program) = program {
- if let Err(e) = program.validate() {
- self.webgl_error(e);
- }
+ fn ValidateProgram(&self, program: &WebGLProgram) {
+ handle_potential_webgl_error!(self, self.validate_ownership(program), return);
+ if let Err(e) = program.validate() {
+ self.webgl_error(e);
}
}
@@ -2662,15 +4140,16 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- #[allow(unsafe_code)]
- unsafe fn VertexAttrib1fv(&self, cx: *mut JSContext, indx: u32, data: *mut JSObject) -> Fallible<()> {
- assert!(!data.is_null());
- let data_vec = try!(typed_array_or_sequence_to_vec::<Float32>(cx, data, ()));
- if data_vec.len() < 1 {
- return Ok(self.webgl_error(InvalidOperation));
+ fn VertexAttrib1fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
+ let values = match v {
+ Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
+ Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
+ };
+ if values.len() < 1 {
+ // https://github.com/KhronosGroup/WebGL/issues/2700
+ return self.webgl_error(InvalidValue);
}
- self.vertex_attrib(indx, data_vec[0], 0f32, 0f32, 1f32);
- Ok(())
+ self.vertex_attrib(indx, values[0], 0f32, 0f32, 1f32);
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
@@ -2679,15 +4158,16 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- #[allow(unsafe_code)]
- unsafe fn VertexAttrib2fv(&self, cx: *mut JSContext, indx: u32, data: *mut JSObject) -> Fallible<()> {
- assert!(!data.is_null());
- let data_vec = try!(typed_array_or_sequence_to_vec::<Float32>(cx, data, ()));
- if data_vec.len() < 2 {
- return Ok(self.webgl_error(InvalidOperation));
+ fn VertexAttrib2fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
+ let values = match v {
+ Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
+ Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
+ };
+ if values.len() < 2 {
+ // https://github.com/KhronosGroup/WebGL/issues/2700
+ return self.webgl_error(InvalidValue);
}
- self.vertex_attrib(indx, data_vec[0], data_vec[1], 0f32, 1f32);
- Ok(())
+ self.vertex_attrib(indx, values[0], values[1], 0f32, 1f32);
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
@@ -2696,15 +4176,16 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- #[allow(unsafe_code)]
- unsafe fn VertexAttrib3fv(&self, cx: *mut JSContext, indx: u32, data: *mut JSObject) -> Fallible<()> {
- assert!(!data.is_null());
- let data_vec = try!(typed_array_or_sequence_to_vec::<Float32>(cx, data, ()));
- if data_vec.len() < 3 {
- return Ok(self.webgl_error(InvalidOperation));
+ fn VertexAttrib3fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
+ let values = match v {
+ Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
+ Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
+ };
+ if values.len() < 3 {
+ // https://github.com/KhronosGroup/WebGL/issues/2700
+ return self.webgl_error(InvalidValue);
}
- self.vertex_attrib(indx, data_vec[0], data_vec[1], data_vec[2], 1f32);
- Ok(())
+ self.vertex_attrib(indx, values[0], values[1], values[2], 1f32);
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
@@ -2713,92 +4194,77 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
- #[allow(unsafe_code)]
- unsafe fn VertexAttrib4fv(&self, cx: *mut JSContext, indx: u32, data: *mut JSObject) -> Fallible<()> {
- assert!(!data.is_null());
- let data_vec = try!(typed_array_or_sequence_to_vec::<Float32>(cx, data, ()));
- if data_vec.len() < 4 {
- return Ok(self.webgl_error(InvalidOperation));
+ fn VertexAttrib4fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
+ let values = match v {
+ Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
+ Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
+ };
+ if values.len() < 4 {
+ // https://github.com/KhronosGroup/WebGL/issues/2700
+ return self.webgl_error(InvalidValue);
}
-
- self.vertex_attrib(indx, data_vec[0], data_vec[1], data_vec[2], data_vec[3]);
- Ok(())
+ self.vertex_attrib(indx, values[0], values[1], values[2], values[3]);
}
// 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);
- }
- if self.bound_buffer_array.get().is_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),
-
- }
-
- let msg = CanvasMsg::WebGL(
- WebGLCommand::VertexAttribPointer(attrib_id, size, data_type, normalized, stride, offset as u32));
- self.ipc_renderer.send(msg).unwrap()
+ fn VertexAttribPointer(
+ &self,
+ index: u32,
+ size: i32,
+ type_: u32,
+ normalized: bool,
+ stride: i32,
+ offset: i64,
+ ) {
+ let res = match self.webgl_version() {
+ WebGLVersion::WebGL1 => self
+ .current_vao()
+ .vertex_attrib_pointer(index, size, type_, normalized, stride, offset),
+ WebGLVersion::WebGL2 => self
+ .current_vao_webgl2()
+ .vertex_attrib_pointer(index, size, type_, normalized, stride, offset),
+ };
+ handle_potential_webgl_error!(self, res);
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.4
fn Viewport(&self, x: i32, y: i32, width: i32, height: i32) {
if width < 0 || height < 0 {
- return self.webgl_error(InvalidValue)
+ return self.webgl_error(InvalidValue);
}
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Viewport(x, y, width, height)))
- .unwrap()
+ self.send_command(WebGLCommand::SetViewport(x, y, width, height))
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
#[allow(unsafe_code)]
- unsafe fn TexImage2D(&self,
- cx: *mut JSContext,
- target: u32,
- level: i32,
- internal_format: u32,
- width: i32,
- height: i32,
- border: i32,
- format: u32,
- data_type: u32,
- data_ptr: *mut JSObject) -> Fallible<()> {
- let data = if data_ptr.is_null() {
- None
- } else {
- Some(try!(fallible_array_buffer_view_to_vec(cx, data_ptr)))
- };
-
- let validator = TexImage2DValidator::new(self, target, level,
- internal_format, width, height,
- border, format, data_type);
+ fn TexImage2D(
+ &self,
+ target: u32,
+ level: i32,
+ internal_format: i32,
+ width: i32,
+ height: i32,
+ border: i32,
+ format: u32,
+ data_type: u32,
+ pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
+ ) -> ErrorResult {
+ if !self.extension_manager.is_tex_type_enabled(data_type) {
+ return Ok(self.webgl_error(InvalidEnum));
+ }
+
+ let validator = TexImage2DValidator::new(
+ self,
+ target,
+ level,
+ internal_format as u32,
+ width,
+ height,
+ border,
+ format,
+ data_type,
+ );
let TexImage2DValidatorResult {
texture,
@@ -2807,6 +4273,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
height,
level,
border,
+ internal_format,
format,
data_type,
} = match validator.validate() {
@@ -2814,21 +4281,34 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
Err(_) => return Ok(()), // NB: The validator sets the correct error for us.
};
+ if !internal_format.compatible_data_types().contains(&data_type) {
+ return Ok(self.webgl_error(InvalidOperation));
+ }
+ if texture.is_immutable() {
+ return Ok(self.webgl_error(InvalidOperation));
+ }
+
let unpacking_alignment = self.texture_unpacking_alignment.get();
- let expected_byte_length =
- match { self.validate_tex_image_2d_data(width, height,
- format, data_type,
- unpacking_alignment, data_ptr, cx) } {
- Ok(byte_length) => byte_length,
- Err(()) => return Ok(()),
- };
+ let expected_byte_length = match {
+ self.validate_tex_image_2d_data(
+ width,
+ height,
+ format,
+ data_type,
+ unpacking_alignment,
+ pixels.as_ref(),
+ )
+ } {
+ Ok(byte_length) => byte_length,
+ Err(()) => return Ok(()),
+ };
// If data is null, a buffer of sufficient size
// initialized to 0 is passed.
- let buff = match data {
- None => vec![0u8; expected_byte_length as usize],
- Some(data) => data,
+ let buff = match *pixels {
+ None => IpcSharedMemory::from_bytes(&vec![0u8; expected_byte_length as usize]),
+ Some(ref data) => IpcSharedMemory::from_bytes(unsafe { data.as_slice() }),
};
// From the WebGL spec:
@@ -2841,75 +4321,190 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
return Ok(self.webgl_error(InvalidOperation));
}
- self.tex_image_2d(texture, target, data_type, format,
- level, width, height, border, unpacking_alignment, buff);
+ let size = Size2D::new(width, height);
+
+ if !self.validate_filterable_texture(
+ &texture,
+ target,
+ level,
+ internal_format,
+ size,
+ data_type,
+ ) {
+ // FIXME(nox): What is the spec for this? No error is emitted ever
+ // by validate_filterable_texture.
+ return Ok(());
+ }
+
+ let size = Size2D::new(width, height);
+
+ self.tex_image_2d(
+ &texture,
+ target,
+ data_type,
+ internal_format,
+ format,
+ level,
+ border,
+ unpacking_alignment,
+ size,
+ TexSource::Pixels(TexPixels::from_array(buff, size)),
+ );
Ok(())
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
- fn TexImage2D_(&self,
- target: u32,
- level: i32,
- internal_format: u32,
- format: u32,
- data_type: u32,
- source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>) -> Fallible<()> {
- // Get pixels from image source
- let (pixels, size) = match self.get_image_pixels(source) {
- Ok((pixels, size)) => (pixels, size),
- Err(_) => return Ok(()),
+ fn TexImage2D_(
+ &self,
+ target: u32,
+ level: i32,
+ internal_format: i32,
+ format: u32,
+ data_type: u32,
+ source: TexImageSource,
+ ) -> ErrorResult {
+ if !self.extension_manager.is_tex_type_enabled(data_type) {
+ return Ok(self.webgl_error(InvalidEnum));
+ }
+
+ let pixels = match self.get_image_pixels(source)? {
+ Some(pixels) => pixels,
+ None => return Ok(()),
};
- let validator = TexImage2DValidator::new(self,
- target, level, internal_format,
- size.width, size.height,
- 0, format, data_type);
+ let validator = TexImage2DValidator::new(
+ self,
+ target,
+ level,
+ internal_format as u32,
+ pixels.size().width as i32,
+ pixels.size().height as i32,
+ 0,
+ format,
+ data_type,
+ );
let TexImage2DValidatorResult {
texture,
target,
- width,
- height,
level,
border,
+ internal_format,
format,
data_type,
+ ..
} = match validator.validate() {
Ok(result) => result,
Err(_) => return Ok(()), // NB: The validator sets the correct error for us.
};
- let pixels = self.rgba8_image_to_tex_image_data(format, data_type, pixels);
+ if !internal_format.compatible_data_types().contains(&data_type) {
+ return Ok(self.webgl_error(InvalidOperation));
+ }
+ if texture.is_immutable() {
+ return Ok(self.webgl_error(InvalidOperation));
+ }
- self.tex_image_2d(texture, target, data_type, format,
- level, width, height, border, 1, pixels);
+ if !self.validate_filterable_texture(
+ &texture,
+ target,
+ level,
+ internal_format,
+ pixels.size(),
+ data_type,
+ ) {
+ // FIXME(nox): What is the spec for this? No error is emitted ever
+ // by validate_filterable_texture.
+ return Ok(());
+ }
+
+ self.tex_image_2d(
+ &texture,
+ target,
+ data_type,
+ internal_format,
+ format,
+ level,
+ border,
+ 1,
+ pixels.size(),
+ TexSource::Pixels(pixels),
+ );
Ok(())
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
- #[allow(unsafe_code)]
- unsafe fn TexSubImage2D(&self,
- cx: *mut JSContext,
- target: u32,
- level: i32,
- xoffset: i32,
- yoffset: i32,
- width: i32,
- height: i32,
- format: u32,
- data_type: u32,
- data_ptr: *mut JSObject) -> Fallible<()> {
- let data = if data_ptr.is_null() {
- None
- } else {
- Some(try!(fallible_array_buffer_view_to_vec(cx, data_ptr)))
- };
+ fn TexImageDOM(
+ &self,
+ target: u32,
+ level: i32,
+ internal_format: u32,
+ width: i32,
+ height: i32,
+ format: u32,
+ data_type: u32,
+ source: &HTMLIFrameElement,
+ ) -> ErrorResult {
+ // Currently DOMToTexture only supports TEXTURE_2D, RGBA, UNSIGNED_BYTE and no levels.
+ if target != constants::TEXTURE_2D ||
+ level != 0 ||
+ internal_format != constants::RGBA ||
+ format != constants::RGBA ||
+ data_type != constants::UNSIGNED_BYTE
+ {
+ return Ok(self.webgl_error(InvalidValue));
+ }
+ // Get bound texture
+ let texture = handle_potential_webgl_error!(
+ self,
+ self.textures
+ .active_texture_slot(constants::TEXTURE_2D, self.webgl_version())
+ .unwrap()
+ .get()
+ .ok_or(InvalidOperation),
+ return Ok(())
+ );
+
+ let pipeline_id = source.pipeline_id().ok_or(Error::InvalidState)?;
+ let document_id = self
+ .global()
+ .downcast::<Window>()
+ .ok_or(Error::InvalidState)?
+ .webrender_document();
+
+ texture.set_attached_to_dom();
+
+ let command = DOMToTextureCommand::Attach(
+ self.webgl_sender.context_id(),
+ texture.id(),
+ document_id,
+ pipeline_id.to_webrender(),
+ Size2D::new(width, height),
+ );
+ self.webgl_sender.send_dom_to_texture(command).unwrap();
- let validator = TexImage2DValidator::new(self, target, level,
- format, width, height,
- 0, format, data_type);
+ Ok(())
+ }
+
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
+ #[allow(unsafe_code)]
+ fn TexSubImage2D(
+ &self,
+ target: u32,
+ level: i32,
+ xoffset: i32,
+ yoffset: i32,
+ width: i32,
+ height: i32,
+ format: u32,
+ data_type: u32,
+ pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
+ ) -> ErrorResult {
+ let validator = TexImage2DValidator::new(
+ self, target, level, format, width, height, 0, format, data_type,
+ );
let TexImage2DValidatorResult {
texture,
target,
@@ -2926,21 +4521,29 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
let unpacking_alignment = self.texture_unpacking_alignment.get();
- let expected_byte_length =
- match { self.validate_tex_image_2d_data(width, height,
- format, data_type,
- unpacking_alignment, data_ptr, cx) } {
- Ok(byte_length) => byte_length,
- Err(()) => return Ok(()),
- };
-
- // If data is null, a buffer of sufficient size
- // initialized to 0 is passed.
- let buff = match data {
- None => vec![0u8; expected_byte_length as usize],
- Some(data) => data,
+ let expected_byte_length = match {
+ self.validate_tex_image_2d_data(
+ width,
+ height,
+ format,
+ data_type,
+ unpacking_alignment,
+ pixels.as_ref(),
+ )
+ } {
+ Ok(byte_length) => byte_length,
+ Err(()) => return Ok(()),
};
+ let buff = handle_potential_webgl_error!(
+ self,
+ pixels
+ .as_ref()
+ .map(|p| IpcSharedMemory::from_bytes(unsafe { p.as_slice() }))
+ .ok_or(InvalidValue),
+ return Ok(())
+ );
+
// From the WebGL spec:
//
// "If pixels is non-null but its size is less than what
@@ -2951,34 +4554,50 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
return Ok(self.webgl_error(InvalidOperation));
}
- self.tex_sub_image_2d(texture, target, level, xoffset, yoffset,
- width, height, format, data_type, unpacking_alignment, buff);
+ self.tex_sub_image_2d(
+ texture,
+ target,
+ level,
+ xoffset,
+ yoffset,
+ format,
+ data_type,
+ unpacking_alignment,
+ TexPixels::from_array(buff, Size2D::new(width, height)),
+ );
Ok(())
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
- fn TexSubImage2D_(&self,
- target: u32,
- level: i32,
- xoffset: i32,
- yoffset: i32,
- format: u32,
- data_type: u32,
- source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>)
- -> Fallible<()> {
- let (pixels, size) = match self.get_image_pixels(source) {
- Ok((pixels, size)) => (pixels, size),
- Err(_) => return Ok(()),
+ fn TexSubImage2D_(
+ &self,
+ target: u32,
+ level: i32,
+ xoffset: i32,
+ yoffset: i32,
+ format: u32,
+ data_type: u32,
+ source: TexImageSource,
+ ) -> ErrorResult {
+ let pixels = match self.get_image_pixels(source)? {
+ Some(pixels) => pixels,
+ None => return Ok(()),
};
- let validator = TexImage2DValidator::new(self, target, level, format,
- size.width, size.height,
- 0, format, data_type);
+ let validator = TexImage2DValidator::new(
+ self,
+ target,
+ level,
+ format,
+ pixels.size().width as i32,
+ pixels.size().height as i32,
+ 0,
+ format,
+ data_type,
+ );
let TexImage2DValidatorResult {
texture,
target,
- width,
- height,
level,
format,
data_type,
@@ -2988,10 +4607,9 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
Err(_) => return Ok(()), // NB: The validator sets the correct error for us.
};
- let pixels = self.rgba8_image_to_tex_image_data(format, data_type, pixels);
-
- self.tex_sub_image_2d(texture, target, level, xoffset, yoffset,
- width, height, format, data_type, 1, pixels);
+ self.tex_sub_image_2d(
+ texture, target, level, xoffset, yoffset, format, data_type, 1, pixels,
+ );
Ok(())
}
@@ -3005,6 +4623,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
self.tex_parameter(target, name, TexParameterValue::Int(value))
}
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6
fn CheckFramebufferStatus(&self, target: u32) -> u32 {
// From the GLES 2.0.25 spec, 4.4 ("Framebuffer Objects"):
//
@@ -3016,118 +4635,414 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
return 0;
}
- match self.bound_framebuffer.get() {
+ match self.bound_draw_framebuffer.get() {
Some(fb) => return fb.check_status(),
None => return constants::FRAMEBUFFER_COMPLETE,
}
}
- fn RenderbufferStorage(&self, target: u32, internal_format: u32,
- width: i32, height: i32) {
- // From the GLES 2.0.25 spec:
- //
- // "target must be RENDERBUFFER."
- if target != constants::RENDERBUFFER {
- return self.webgl_error(InvalidOperation)
- }
+ // 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) {
+ self.renderbuffer_storage(target, 0, internal_format, width, height)
+ }
- // 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 {
- return self.webgl_error(InvalidValue);
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6
+ fn FramebufferRenderbuffer(
+ &self,
+ target: u32,
+ attachment: u32,
+ renderbuffertarget: u32,
+ rb: Option<&WebGLRenderbuffer>,
+ ) {
+ if let Some(rb) = rb {
+ handle_potential_webgl_error!(self, self.validate_ownership(rb), return);
}
- match self.bound_renderbuffer.get() {
- Some(rb) => {
- handle_potential_webgl_error!(self, rb.storage(internal_format, width, height));
- if let Some(fb) = self.bound_framebuffer.get() {
- fb.invalidate_renderbuffer(&*rb);
- }
- }
- None => self.webgl_error(InvalidOperation),
- };
-
- // FIXME: We need to clear the renderbuffer before it can be
- // accessed. See https://github.com/servo/servo/issues/13710
- }
-
- fn FramebufferRenderbuffer(&self, target: u32, attachment: u32,
- renderbuffertarget: u32,
- rb: Option<&WebGLRenderbuffer>) {
if target != constants::FRAMEBUFFER || renderbuffertarget != constants::RENDERBUFFER {
return self.webgl_error(InvalidEnum);
}
- match self.bound_framebuffer.get() {
+ match self.bound_draw_framebuffer.get() {
Some(fb) => handle_potential_webgl_error!(self, fb.renderbuffer(attachment, rb)),
None => self.webgl_error(InvalidOperation),
};
}
- fn FramebufferTexture2D(&self, target: u32, attachment: u32,
- textarget: u32, texture: Option<&WebGLTexture>,
- level: i32) {
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6
+ fn FramebufferTexture2D(
+ &self,
+ target: u32,
+ attachment: u32,
+ textarget: u32,
+ texture: Option<&WebGLTexture>,
+ level: i32,
+ ) {
+ if let Some(texture) = texture {
+ handle_potential_webgl_error!(self, self.validate_ownership(texture), return);
+ }
+
if target != constants::FRAMEBUFFER {
return self.webgl_error(InvalidEnum);
}
- match self.bound_framebuffer.get() {
- Some(fb) => handle_potential_webgl_error!(self, fb.texture2d(attachment, textarget, texture, level)),
+ // From the GLES 2.0.25 spec, page 113:
+ //
+ // "level specifies the mipmap level of the texture image
+ // to be attached to the framebuffer and must be
+ // 0. Otherwise, INVALID_VALUE is generated."
+ if level != 0 {
+ return self.webgl_error(InvalidValue);
+ }
+
+ match self.bound_draw_framebuffer.get() {
+ Some(fb) => handle_potential_webgl_error!(
+ self,
+ fb.texture2d(attachment, textarget, texture, level)
+ ),
None => self.webgl_error(InvalidOperation),
};
}
+
+ /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
+ fn GetAttachedShaders(&self, program: &WebGLProgram) -> Option<Vec<DomRoot<WebGLShader>>> {
+ handle_potential_webgl_error!(self, self.validate_ownership(program), return None);
+ handle_potential_webgl_error!(self, program.attached_shaders().map(Some), None)
+ }
+
+ /// https://immersive-web.github.io/webxr/#dom-webglrenderingcontextbase-makexrcompatible
+ fn MakeXRCompatible(&self) -> Rc<Promise> {
+ // XXXManishearth Fill in with compatibility checks when rust-webxr supports this
+ let p = Promise::new(&self.global());
+ p.resolve_native(&());
+ p
+ }
}
-pub trait LayoutCanvasWebGLRenderingContextHelpers {
+impl LayoutCanvasRenderingContextHelpers for LayoutDom<'_, WebGLRenderingContext> {
#[allow(unsafe_code)]
- unsafe fn get_ipc_renderer(&self) -> IpcSender<CanvasMsg>;
+ unsafe fn canvas_data_source(self) -> HTMLCanvasDataSource {
+ (*self.unsafe_get()).layout_handle()
+ }
}
-impl LayoutCanvasWebGLRenderingContextHelpers for LayoutJS<WebGLRenderingContext> {
- #[allow(unsafe_code)]
- unsafe fn get_ipc_renderer(&self) -> IpcSender<CanvasMsg> {
- (*self.unsafe_get()).ipc_renderer.clone()
+#[derive(Default, JSTraceable, MallocSizeOf)]
+struct Capabilities {
+ value: Cell<CapFlags>,
+}
+
+impl Capabilities {
+ fn set(&self, cap: u32, set: bool) -> WebGLResult<bool> {
+ let cap = CapFlags::from_enum(cap)?;
+ let mut value = self.value.get();
+ if value.contains(cap) == set {
+ return Ok(false);
+ }
+ value.set(cap, set);
+ self.value.set(value);
+ Ok(true)
+ }
+
+ fn is_enabled(&self, cap: u32) -> WebGLResult<bool> {
+ Ok(self.value.get().contains(CapFlags::from_enum(cap)?))
+ }
+}
+
+impl Default for CapFlags {
+ fn default() -> Self {
+ CapFlags::DITHER
+ }
+}
+
+macro_rules! capabilities {
+ ($name:ident, $next:ident, $($rest:ident,)*) => {
+ capabilities!($name, $next, $($rest,)* [$name = 1;]);
+ };
+ ($prev:ident, $name:ident, $($rest:ident,)* [$($tt:tt)*]) => {
+ capabilities!($name, $($rest,)* [$($tt)* $name = Self::$prev.bits << 1;]);
+ };
+ ($prev:ident, [$($name:ident = $value:expr;)*]) => {
+ bitflags! {
+ #[derive(JSTraceable, MallocSizeOf)]
+ struct CapFlags: u16 {
+ $(const $name = $value;)*
+ }
+ }
+
+ impl CapFlags {
+ fn from_enum(cap: u32) -> WebGLResult<Self> {
+ match cap {
+ $(constants::$name => Ok(Self::$name),)*
+ _ => Err(InvalidEnum),
+ }
+ }
+ }
+ };
+}
+
+capabilities! {
+ BLEND,
+ CULL_FACE,
+ DEPTH_TEST,
+ DITHER,
+ POLYGON_OFFSET_FILL,
+ SAMPLE_ALPHA_TO_COVERAGE,
+ SAMPLE_COVERAGE,
+ SCISSOR_TEST,
+ STENCIL_TEST,
+}
+
+#[unrooted_must_root_lint::must_root]
+#[derive(JSTraceable, MallocSizeOf)]
+pub struct Textures {
+ active_unit: Cell<u32>,
+ units: Box<[TextureUnit]>,
+}
+
+impl Textures {
+ fn new(max_combined_textures: u32) -> Self {
+ Self {
+ active_unit: Default::default(),
+ units: (0..max_combined_textures)
+ .map(|_| Default::default())
+ .collect::<Vec<_>>()
+ .into(),
+ }
+ }
+
+ pub fn active_unit_enum(&self) -> u32 {
+ self.active_unit.get() + constants::TEXTURE0
+ }
+
+ fn set_active_unit_enum(&self, index: u32) -> WebGLResult<()> {
+ if index < constants::TEXTURE0 || (index - constants::TEXTURE0) as usize > self.units.len()
+ {
+ return Err(InvalidEnum);
+ }
+ self.active_unit.set(index - constants::TEXTURE0);
+ Ok(())
+ }
+
+ pub fn active_texture_slot(
+ &self,
+ target: u32,
+ webgl_version: WebGLVersion,
+ ) -> WebGLResult<&MutNullableDom<WebGLTexture>> {
+ let active_unit = self.active_unit();
+ let is_webgl2 = webgl_version == WebGLVersion::WebGL2;
+ match target {
+ constants::TEXTURE_2D => Ok(&active_unit.tex_2d),
+ constants::TEXTURE_CUBE_MAP => Ok(&active_unit.tex_cube_map),
+ WebGL2RenderingContextConstants::TEXTURE_2D_ARRAY if is_webgl2 => {
+ Ok(&active_unit.tex_2d_array)
+ },
+ WebGL2RenderingContextConstants::TEXTURE_3D if is_webgl2 => Ok(&active_unit.tex_3d),
+ _ => Err(InvalidEnum),
+ }
+ }
+
+ pub fn active_texture_for_image_target(
+ &self,
+ target: TexImageTarget,
+ ) -> Option<DomRoot<WebGLTexture>> {
+ let active_unit = self.active_unit();
+ match target {
+ TexImageTarget::Texture2D => active_unit.tex_2d.get(),
+ TexImageTarget::Texture2DArray => active_unit.tex_2d_array.get(),
+ TexImageTarget::Texture3D => active_unit.tex_3d.get(),
+ TexImageTarget::CubeMap |
+ TexImageTarget::CubeMapPositiveX |
+ TexImageTarget::CubeMapNegativeX |
+ TexImageTarget::CubeMapPositiveY |
+ TexImageTarget::CubeMapNegativeY |
+ TexImageTarget::CubeMapPositiveZ |
+ TexImageTarget::CubeMapNegativeZ => active_unit.tex_cube_map.get(),
+ }
+ }
+
+ fn active_unit(&self) -> &TextureUnit {
+ &self.units[self.active_unit.get() as usize]
+ }
+
+ fn iter(&self) -> impl Iterator<Item = (u32, &TextureUnit)> {
+ self.units
+ .iter()
+ .enumerate()
+ .map(|(index, unit)| (index as u32 + constants::TEXTURE0, unit))
}
}
-#[derive(Debug, PartialEq)]
-pub enum UniformSetterType {
- Int,
- IntVec2,
- IntVec3,
- IntVec4,
- Float,
- FloatVec2,
- FloatVec3,
- FloatVec4,
- FloatMat2,
- FloatMat3,
- FloatMat4,
+#[unrooted_must_root_lint::must_root]
+#[derive(Default, JSTraceable, MallocSizeOf)]
+struct TextureUnit {
+ tex_2d: MutNullableDom<WebGLTexture>,
+ tex_cube_map: MutNullableDom<WebGLTexture>,
+ tex_2d_array: MutNullableDom<WebGLTexture>,
+ tex_3d: MutNullableDom<WebGLTexture>,
}
-impl UniformSetterType {
- pub fn element_count(&self) -> usize {
- match *self {
- UniformSetterType::Int => 1,
- UniformSetterType::IntVec2 => 2,
- UniformSetterType::IntVec3 => 3,
- UniformSetterType::IntVec4 => 4,
- UniformSetterType::Float => 1,
- UniformSetterType::FloatVec2 => 2,
- UniformSetterType::FloatVec3 => 3,
- UniformSetterType::FloatVec4 => 4,
- UniformSetterType::FloatMat2 => 4,
- UniformSetterType::FloatMat3 => 9,
- UniformSetterType::FloatMat4 => 16,
+impl TextureUnit {
+ fn unbind(&self, texture: &WebGLTexture) -> Option<u32> {
+ let fields = [
+ (&self.tex_2d, constants::TEXTURE_2D),
+ (&self.tex_cube_map, constants::TEXTURE_CUBE_MAP),
+ (
+ &self.tex_2d_array,
+ WebGL2RenderingContextConstants::TEXTURE_2D_ARRAY,
+ ),
+ (&self.tex_3d, WebGL2RenderingContextConstants::TEXTURE_3D),
+ ];
+ for &(slot, target) in &fields {
+ if slot.get().map_or(false, |t| texture == &*t) {
+ slot.set(None);
+ return Some(target);
+ }
}
+ None
+ }
+}
+
+pub struct TexPixels {
+ data: IpcSharedMemory,
+ size: Size2D<u32>,
+ pixel_format: Option<PixelFormat>,
+ premultiplied: bool,
+}
+
+impl TexPixels {
+ fn new(
+ data: IpcSharedMemory,
+ size: Size2D<u32>,
+ pixel_format: PixelFormat,
+ premultiplied: bool,
+ ) -> Self {
+ Self {
+ data,
+ size,
+ pixel_format: Some(pixel_format),
+ premultiplied,
+ }
+ }
+
+ pub fn from_array(data: IpcSharedMemory, size: Size2D<u32>) -> Self {
+ Self {
+ data,
+ size,
+ pixel_format: None,
+ premultiplied: false,
+ }
+ }
+
+ pub fn size(&self) -> Size2D<u32> {
+ self.size
+ }
+}
+
+pub enum TexSource {
+ Pixels(TexPixels),
+ BufferOffset(i64),
+}
+
+#[derive(JSTraceable)]
+pub struct WebGLCommandSender {
+ sender: WebGLChan,
+ waker: Option<Box<dyn EventLoopWaker>>,
+}
+
+impl WebGLCommandSender {
+ pub fn new(sender: WebGLChan, waker: Option<Box<dyn EventLoopWaker>>) -> WebGLCommandSender {
+ WebGLCommandSender { sender, waker }
+ }
+
+ pub fn send(&self, msg: WebGLMsg) -> WebGLSendResult {
+ let result = self.sender.send(msg);
+ if let Some(ref waker) = self.waker {
+ waker.wake();
+ }
+ result
+ }
+}
+
+#[derive(JSTraceable, MallocSizeOf)]
+pub(crate) struct WebGLMessageSender {
+ sender: WebGLMsgSender,
+ #[ignore_malloc_size_of = "traits are cumbersome"]
+ waker: Option<Box<dyn EventLoopWaker>>,
+}
+
+impl Clone for WebGLMessageSender {
+ fn clone(&self) -> WebGLMessageSender {
+ WebGLMessageSender {
+ sender: self.sender.clone(),
+ waker: self.waker.as_ref().map(|w| (*w).clone_box()),
+ }
+ }
+}
+
+impl WebGLMessageSender {
+ fn wake_after_send<F: FnOnce() -> WebGLSendResult>(&self, f: F) -> WebGLSendResult {
+ let result = f();
+ if let Some(ref waker) = self.waker {
+ waker.wake();
+ }
+ result
+ }
+
+ pub fn new(
+ sender: WebGLMsgSender,
+ waker: Option<Box<dyn EventLoopWaker>>,
+ ) -> WebGLMessageSender {
+ WebGLMessageSender { sender, waker }
+ }
+
+ pub fn context_id(&self) -> WebGLContextId {
+ self.sender.context_id()
+ }
+
+ pub fn send(&self, msg: WebGLCommand, backtrace: WebGLCommandBacktrace) -> WebGLSendResult {
+ self.wake_after_send(|| self.sender.send(msg, backtrace))
+ }
+
+ pub fn send_resize(
+ &self,
+ size: Size2D<u32>,
+ sender: WebGLSender<Result<(), String>>,
+ ) -> WebGLSendResult {
+ self.wake_after_send(|| self.sender.send_resize(size, sender))
+ }
+
+ pub fn send_remove(&self) -> WebGLSendResult {
+ self.wake_after_send(|| self.sender.send_remove())
+ }
+
+ pub fn send_dom_to_texture(&self, command: DOMToTextureCommand) -> WebGLSendResult {
+ self.wake_after_send(|| self.sender.send_dom_to_texture(command))
+ }
+}
+
+pub trait Size2DExt {
+ fn to_u64(&self) -> Size2D<u64>;
+}
+
+impl Size2DExt for Size2D<u32> {
+ fn to_u64(&self) -> Size2D<u64> {
+ return Size2D::new(self.width as u64, self.height as u64);
+ }
+}
+
+fn array_buffer_type_to_sized_type(type_: Type) -> Option<SizedDataType> {
+ match type_ {
+ Type::Uint8 | Type::Uint8Clamped => Some(SizedDataType::Uint8),
+ Type::Uint16 => Some(SizedDataType::Uint16),
+ Type::Uint32 => Some(SizedDataType::Uint32),
+ Type::Int8 => Some(SizedDataType::Int8),
+ Type::Int16 => Some(SizedDataType::Int16),
+ Type::Int32 => Some(SizedDataType::Int32),
+ Type::Float32 => Some(SizedDataType::Float32),
+ Type::Float64 |
+ Type::BigInt64 |
+ Type::BigUint64 |
+ Type::MaxTypedArrayViewType |
+ Type::Int64 |
+ Type::Simd128 => None,
}
}