Add VertexArrayObject support for WebGL2

The implementation was already in place for OpenGL ES.
My approach with this patch is to add support for WebGL2 by
sharing the implementation between the WebGL2 and GLES.
This commit is contained in:
Istvan Miklos 2020-03-05 15:51:18 +01:00 committed by Josh Matthews
parent 4b1bb895a3
commit 1b4a3d8987
20 changed files with 815 additions and 371 deletions

View file

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::dom::bindings::cell::Ref;
use crate::dom::bindings::codegen::Bindings::ANGLEInstancedArraysBinding::ANGLEInstancedArraysConstants;
use crate::dom::bindings::codegen::Bindings::EXTBlendMinmaxBinding::EXTBlendMinmaxConstants;
use crate::dom::bindings::codegen::Bindings::OESVertexArrayObjectBinding::OESVertexArrayObjectConstants;
@ -26,6 +27,7 @@ use crate::dom::htmlcanvaselement::HTMLCanvasElement;
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,
@ -47,6 +49,7 @@ 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;
@ -183,6 +186,8 @@ pub struct WebGLRenderingContext {
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,
}
@ -242,6 +247,8 @@ impl WebGLRenderingContext {
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,
}
@ -294,6 +301,15 @@ impl WebGLRenderingContext {
})
}
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 recreate(&self, size: Size2D<u32>) {
let (sender, receiver) = webgl_channel().unwrap();
self.webgl_sender.send_resize(size, sender).unwrap();
@ -889,11 +905,18 @@ impl WebGLRenderingContext {
0
};
self.current_vao().validate_for_draw(
required_len,
primcount as u32,
&current_program.active_attribs(),
)?;
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(),
)?,
};
self.validate_framebuffer()?;
@ -950,11 +973,11 @@ impl WebGLRenderingContext {
}
let current_program = self.current_program.get().ok_or(InvalidOperation)?;
let array_buffer = self
.current_vao()
.element_array_buffer()
.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)?;
if count > 0 && primcount > 0 {
// This operation cannot overflow in u64 and we know all those values are nonnegative.
@ -965,11 +988,18 @@ impl WebGLRenderingContext {
}
// TODO(nox): Pass the correct number of vertices required.
self.current_vao().validate_for_draw(
0,
primcount as u32,
&current_program.active_attribs(),
)?;
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()?;
@ -1003,7 +1033,12 @@ impl WebGLRenderingContext {
return self.webgl_error(InvalidValue);
}
self.current_vao().vertex_attrib_divisor(index, divisor);
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 });
}
@ -1066,6 +1101,15 @@ impl WebGLRenderingContext {
.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);
@ -1084,6 +1128,24 @@ impl WebGLRenderingContext {
}
}
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(false);
}
}
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.
@ -1092,6 +1154,14 @@ impl WebGLRenderingContext {
})
}
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()
})
}
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.
@ -1108,6 +1178,22 @@ impl WebGLRenderingContext {
self.current_vao.set(vao);
}
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 validate_blend_mode(&self, mode: u32) -> WebGLResult<()> {
match mode {
constants::FUNC_ADD | constants::FUNC_SUBTRACT | constants::FUNC_REVERSE_SUBTRACT => {
@ -2552,9 +2638,14 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
if attrib_id >= self.limits.max_vertex_attribs {
return self.webgl_error(InvalidValue);
}
self.current_vao()
.enabled_vertex_attrib_array(attrib_id, true);
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));
}
@ -2563,9 +2654,14 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
if attrib_id >= self.limits.max_vertex_attribs {
return self.webgl_error(InvalidValue);
}
self.current_vao()
.enabled_vertex_attrib_array(attrib_id, false);
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));
}
@ -2890,56 +2986,73 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
#[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
fn GetVertexAttrib(&self, cx: SafeJSContext, index: u32, param: u32) -> JSVal {
let current_vao = self.current_vao();
let data = handle_potential_webgl_error!(
self,
current_vao.get_vertex_attrib(index).ok_or(InvalidValue),
return NullValue()
);
if param == constants::CURRENT_VERTEX_ATTRIB {
let value = if index == 0 {
let (x, y, z, w) = self.current_vertex_attrib_0.get();
[x, y, z, w]
} else {
let (sender, receiver) = webgl_channel().unwrap();
self.send_command(WebGLCommand::GetCurrentVertexAttrib(index, sender));
receiver.recv().unwrap()
};
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());
}
}
if !self
.extension_manager
.is_get_vertex_attrib_name_enabled(param)
{
self.webgl_error(WebGLError::InvalidEnum);
return 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());
let get_attrib = |data: Ref<VertexAttribData>| -> JSVal {
if param == constants::CURRENT_VERTEX_ATTRIB {
let value = if index == 0 {
let (x, y, z, w) = self.current_vertex_attrib_0.get();
[x, y, z, w]
} else {
let (sender, receiver) = webgl_channel().unwrap();
self.send_command(WebGLCommand::GetCurrentVertexAttrib(index, sender));
receiver.recv().unwrap()
};
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());
}
jsval.get()
}
if !self
.extension_manager
.is_get_vertex_attrib_name_enabled(param)
{
self.webgl_error(WebGLError::InvalidEnum);
return 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)
},
ANGLEInstancedArraysConstants::VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE => {
UInt32Value(data.divisor)
},
_ => {
self.webgl_error(InvalidEnum);
NullValue()
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)
},
}
}
@ -2950,13 +3063,26 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
self.webgl_error(InvalidEnum);
return 0;
}
let vao = self.current_vao();
let data = handle_potential_webgl_error!(
self,
vao.get_vertex_attrib(index).ok_or(InvalidValue),
return 0
);
data.offset as i64
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
@ -3819,11 +3945,15 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
stride: i32,
offset: i64,
) {
handle_potential_webgl_error!(
self,
self.current_vao()
.vertex_attrib_pointer(index, size, type_, normalized, stride, offset)
);
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