Auto merge of #24514 - imiklos:webgl_transform_feedback, r=jdm

Add WebGL Transformfeedback support

Reference: https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15
cc @mmatyas @jdm @zakorgy

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [X] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors

<!-- Either: -->
- [X] There are tests for these changes

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
This commit is contained in:
bors-servo 2019-11-08 13:26:29 -05:00 committed by GitHub
commit 970f7163e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 588 additions and 17 deletions

View file

@ -532,6 +532,7 @@ pub mod webglshader;
pub mod webglshaderprecisionformat;
pub mod webglsync;
pub mod webgltexture;
pub mod webgltransformfeedback;
pub mod webgluniformlocation;
pub mod webglvertexarrayobjectoes;
pub mod websocket;

View file

@ -11,8 +11,8 @@ use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer;
use crate::dom::bindings::codegen::UnionTypes::Float32ArrayOrUnrestrictedFloatSequence;
use crate::dom::bindings::codegen::UnionTypes::ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement;
use crate::dom::bindings::codegen::UnionTypes::Int32ArrayOrLongSequence;
use crate::dom::bindings::conversions::ToJSValConvertible;
use crate::dom::bindings::error::{ErrorResult, Fallible};
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
use crate::dom::bindings::str::DOMString;
@ -32,8 +32,10 @@ use crate::dom::webglshader::WebGLShader;
use crate::dom::webglshaderprecisionformat::WebGLShaderPrecisionFormat;
use crate::dom::webglsync::WebGLSync;
use crate::dom::webgltexture::WebGLTexture;
use crate::dom::webgltransformfeedback::WebGLTransformFeedback;
use crate::dom::webgluniformlocation::WebGLUniformLocation;
use crate::dom::window::Window;
use crate::js::conversions::ToJSValConvertible;
use crate::script_runtime::JSContext;
use canvas_traits::webgl::WebGLError::*;
use canvas_traits::webgl::{
@ -62,6 +64,7 @@ pub struct WebGL2RenderingContext {
bound_pixel_unpack_buffer: MutNullableDom<WebGLBuffer>,
bound_transform_feedback_buffer: MutNullableDom<WebGLBuffer>,
bound_uniform_buffer: MutNullableDom<WebGLBuffer>,
current_transform_feedback: MutNullableDom<WebGLTransformFeedback>,
}
fn typedarray_elem_size(typeid: Type) -> usize {
@ -100,6 +103,7 @@ impl WebGL2RenderingContext {
bound_pixel_unpack_buffer: MutNullableDom::new(None),
bound_transform_feedback_buffer: MutNullableDom::new(None),
bound_uniform_buffer: MutNullableDom::new(None),
current_transform_feedback: MutNullableDom::new(None),
})
}
@ -210,6 +214,9 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext {
constants::UNIFORM_BUFFER_BINDING => unsafe {
optional_root_object_to_js_or_null!(*cx, &self.bound_uniform_buffer.get())
},
constants::TRANSFORM_FEEDBACK_BINDING => unsafe {
optional_root_object_to_js_or_null!(*cx, self.current_transform_feedback.get())
},
_ => self.base.GetParameter(cx, parameter),
}
}
@ -824,7 +831,24 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext {
/// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
fn GetProgramParameter(&self, cx: JSContext, program: &WebGLProgram, param_id: u32) -> JSVal {
self.base.GetProgramParameter(cx, program, param_id)
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(program),
return NullValue()
);
if program.is_deleted() {
self.base.webgl_error(InvalidOperation);
return NullValue();
}
match param_id {
constants::TRANSFORM_FEEDBACK_VARYINGS => {
Int32Value(program.transform_feedback_varyings_length())
},
constants::TRANSFORM_FEEDBACK_BUFFER_MODE => {
Int32Value(program.transform_feedback_buffer_mode())
},
_ => self.base.GetProgramParameter(cx, program, param_id),
}
}
/// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
@ -1721,6 +1745,212 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext {
},
}
}
/// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15
fn CreateTransformFeedback(&self) -> Option<DomRoot<WebGLTransformFeedback>> {
Some(WebGLTransformFeedback::new(&self.base))
}
/// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15
fn DeleteTransformFeedback(&self, tf: Option<&WebGLTransformFeedback>) {
if let Some(tf) = tf {
handle_potential_webgl_error!(self.base, self.base.validate_ownership(tf), return);
if tf.is_active() {
self.base.webgl_error(InvalidOperation);
return;
}
tf.delete(false);
self.current_transform_feedback.set(None);
}
}
/// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15
fn IsTransformFeedback(&self, tf: Option<&WebGLTransformFeedback>) -> bool {
match tf {
Some(tf) => {
if !tf.is_valid() {
return false;
}
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(tf),
return false
);
let (sender, receiver) = webgl_channel().unwrap();
self.base
.send_command(WebGLCommand::IsTransformFeedback(tf.id(), sender));
receiver.recv().unwrap()
},
None => false,
}
}
/// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15
fn BindTransformFeedback(&self, target: u32, tf: Option<&WebGLTransformFeedback>) {
if target != constants::TRANSFORM_FEEDBACK {
self.base.webgl_error(InvalidEnum);
return;
}
match tf {
Some(transform_feedback) => {
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(transform_feedback),
return
);
if !transform_feedback.is_valid() {
self.base.webgl_error(InvalidOperation);
return;
}
if let Some(current_tf) = self.current_transform_feedback.get() {
if current_tf.is_active() && !current_tf.is_paused() {
self.base.webgl_error(InvalidOperation);
return;
}
}
transform_feedback.bind(&self.base, target);
self.current_transform_feedback
.set(Some(transform_feedback));
},
None => self
.base
.send_command(WebGLCommand::BindTransformFeedback(target, 0)),
}
}
/// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15
fn BeginTransformFeedback(&self, primitiveMode: u32) {
match primitiveMode {
constants::POINTS | constants::LINES | constants::TRIANGLES => {},
_ => {
self.base.webgl_error(InvalidEnum);
return;
},
};
let current_tf = match self.current_transform_feedback.get() {
Some(current_tf) => current_tf,
None => {
self.base.webgl_error(InvalidOperation);
return;
},
};
if current_tf.is_active() {
self.base.webgl_error(InvalidOperation);
return;
};
let program = match self.base.current_program() {
Some(program) => program,
None => {
self.base.webgl_error(InvalidOperation);
return;
},
};
if !program.is_linked() || program.transform_feedback_varyings_length() != 0 {
self.base.webgl_error(InvalidOperation);
return;
};
current_tf.begin(&self.base, primitiveMode);
}
/// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15
fn EndTransformFeedback(&self) {
if let Some(current_tf) = self.current_transform_feedback.get() {
if !current_tf.is_active() {
self.base.webgl_error(InvalidOperation);
return;
}
current_tf.end(&self.base);
}
}
/// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15
fn ResumeTransformFeedback(&self) {
if let Some(current_tf) = self.current_transform_feedback.get() {
if !current_tf.is_active() || !current_tf.is_paused() {
self.base.webgl_error(InvalidOperation);
return;
}
current_tf.resume(&self.base);
}
}
/// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15
fn PauseTransformFeedback(&self) {
if let Some(current_tf) = self.current_transform_feedback.get() {
if !current_tf.is_active() || current_tf.is_paused() {
self.base.webgl_error(InvalidOperation);
return;
}
current_tf.pause(&self.base);
}
}
/// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15
fn TransformFeedbackVaryings(
&self,
program: &WebGLProgram,
varyings: Vec<DOMString>,
bufferMode: u32,
) {
handle_potential_webgl_error!(self.base, program.validate(), return);
let strs = varyings
.iter()
.map(|name| String::from(name.to_owned()))
.collect::<Vec<String>>();
match bufferMode {
constants::INTERLEAVED_ATTRIBS => {
self.base
.send_command(WebGLCommand::TransformFeedbackVaryings(
program.id(),
strs,
bufferMode,
));
},
constants::SEPARATE_ATTRIBS => {
let max_tf_sp_att =
self.base.limits().max_transform_feedback_separate_attribs as usize;
if strs.len() >= max_tf_sp_att {
self.base.webgl_error(InvalidValue);
return;
}
self.base
.send_command(WebGLCommand::TransformFeedbackVaryings(
program.id(),
strs,
bufferMode,
));
},
_ => self.base.webgl_error(InvalidEnum),
}
}
/// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15
fn GetTransformFeedbackVarying(
&self,
program: &WebGLProgram,
index: u32,
) -> Option<DomRoot<WebGLActiveInfo>> {
handle_potential_webgl_error!(self.base, program.validate(), return None);
if index >= program.transform_feedback_varyings_length() as u32 {
self.base.webgl_error(InvalidValue);
return None;
}
let (sender, receiver) = webgl_channel().unwrap();
self.base
.send_command(WebGLCommand::GetTransformFeedbackVarying(
program.id(),
index,
sender,
));
let (size, ty, name) = receiver.recv().unwrap();
Some(WebGLActiveInfo::new(
self.base.global().as_window(),
size,
ty,
DOMString::from(name),
))
}
}
impl LayoutCanvasWebGLRenderingContextHelpers for LayoutDom<WebGL2RenderingContext> {

View file

@ -34,6 +34,8 @@ pub struct WebGLProgram {
vertex_shader: MutNullableDom<WebGLShader>,
active_attribs: DomRefCell<Box<[ActiveAttribInfo]>>,
active_uniforms: DomRefCell<Box<[ActiveUniformInfo]>>,
transform_feedback_varyings_length: Cell<i32>,
transform_feedback_mode: Cell<i32>,
}
impl WebGLProgram {
@ -50,6 +52,8 @@ impl WebGLProgram {
vertex_shader: Default::default(),
active_attribs: DomRefCell::new(vec![].into()),
active_uniforms: DomRefCell::new(vec![].into()),
transform_feedback_varyings_length: Default::default(),
transform_feedback_mode: Default::default(),
}
}
@ -187,6 +191,10 @@ impl WebGLProgram {
self.linked.set(link_info.linked);
self.link_called.set(true);
self.transform_feedback_varyings_length
.set(link_info.transform_feedback_length);
self.transform_feedback_mode
.set(link_info.transform_feedback_mode);
*self.active_attribs.borrow_mut() = link_info.active_attribs;
*self.active_uniforms.borrow_mut() = link_info.active_uniforms;
Ok(())
@ -444,6 +452,14 @@ impl WebGLProgram {
pub fn link_generation(&self) -> u64 {
self.link_generation.get()
}
pub fn transform_feedback_varyings_length(&self) -> i32 {
self.transform_feedback_varyings_length.get()
}
pub fn transform_feedback_buffer_mode(&self) -> i32 {
self.transform_feedback_mode.get()
}
}
impl Drop for WebGLProgram {

View file

@ -1165,6 +1165,10 @@ impl WebGLRenderingContext {
slot.set(buffer);
}
pub fn current_program(&self) -> Option<DomRoot<WebGLProgram>> {
self.current_program.get()
}
}
#[cfg(not(feature = "webgl_backtrace"))]

View file

@ -0,0 +1,133 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use crate::dom::bindings::codegen::Bindings::WebGLTransformFeedbackBinding;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webglobject::WebGLObject;
use crate::dom::webglrenderingcontext::WebGLRenderingContext;
use canvas_traits::webgl::{webgl_channel, WebGLCommand};
use dom_struct::dom_struct;
use std::cell::Cell;
#[dom_struct]
pub struct WebGLTransformFeedback {
webgl_object: WebGLObject,
id: u32,
marked_for_deletion: Cell<bool>,
has_been_bound: Cell<bool>,
is_active: Cell<bool>,
is_paused: Cell<bool>,
}
impl WebGLTransformFeedback {
fn new_inherited(context: &WebGLRenderingContext, id: u32) -> Self {
Self {
webgl_object: WebGLObject::new_inherited(context),
id,
marked_for_deletion: Cell::new(false),
has_been_bound: Cell::new(false),
is_active: Cell::new(false),
is_paused: Cell::new(false),
}
}
pub fn new(context: &WebGLRenderingContext) -> DomRoot<Self> {
let (sender, receiver) = webgl_channel().unwrap();
context.send_command(WebGLCommand::CreateTransformFeedback(sender));
let id = receiver.recv().unwrap();
reflect_dom_object(
Box::new(WebGLTransformFeedback::new_inherited(context, id)),
&*context.global(),
WebGLTransformFeedbackBinding::Wrap,
)
}
}
impl WebGLTransformFeedback {
pub fn bind(&self, context: &WebGLRenderingContext, target: u32) {
context.send_command(WebGLCommand::BindTransformFeedback(target, self.id()));
self.has_been_bound.set(true);
}
pub fn begin(&self, context: &WebGLRenderingContext, primitive_mode: u32) {
if self.has_been_bound.get() && !self.is_active() {
context.send_command(WebGLCommand::BeginTransformFeedback(primitive_mode));
self.set_active(true);
}
}
pub fn end(&self, context: &WebGLRenderingContext) {
if self.has_been_bound.get() && self.is_active() {
if self.is_paused() {
context.send_command(WebGLCommand::ResumeTransformFeedback());
}
context.send_command(WebGLCommand::EndTransformFeedback());
self.set_active(false);
}
}
pub fn resume(&self, context: &WebGLRenderingContext) {
if self.is_active() && self.is_paused() {
context.send_command(WebGLCommand::ResumeTransformFeedback());
self.set_pause(false);
}
}
pub fn pause(&self, context: &WebGLRenderingContext) {
if self.is_active() && !self.is_paused() {
context.send_command(WebGLCommand::PauseTransformFeedback());
self.set_pause(true);
}
}
pub fn id(&self) -> u32 {
self.id
}
pub fn is_valid(&self) -> bool {
!self.marked_for_deletion.get()
}
pub fn is_active(&self) -> bool {
self.is_active.get()
}
pub fn is_paused(&self) -> bool {
self.is_paused.get()
}
pub fn delete(&self, fallible: bool) {
if self.is_valid() && self.id() != 0 {
self.marked_for_deletion.set(true);
let context = self.upcast::<WebGLObject>().context();
let cmd = WebGLCommand::DeleteTransformFeedback(self.id);
if fallible {
context.send_command_ignored(cmd);
} else {
context.send_command(cmd);
}
}
}
pub fn set_active(&self, value: bool) {
if self.is_valid() && self.has_been_bound.get() {
self.is_active.set(value);
}
}
pub fn set_pause(&self, value: bool) {
if self.is_valid() && self.is_active() {
self.is_active.set(value);
}
}
}
impl Drop for WebGLTransformFeedback {
fn drop(&mut self) {
self.delete(true);
}
}

View file

@ -12,9 +12,6 @@ typedef long long GLint64;
typedef unsigned long long GLuint64;
// interface WebGLTransformFeedback : WebGLObject {
// };
// interface WebGLVertexArrayObject : WebGLObject {
// };
@ -544,7 +541,7 @@ interface mixin WebGL2RenderingContextBase
any getSyncParameter(WebGLSync sync, GLenum pname);
/* Transform Feedback */
/*WebGLTransformFeedback? createTransformFeedback();
WebGLTransformFeedback? createTransformFeedback();
void deleteTransformFeedback(WebGLTransformFeedback? tf);
[WebGLHandlesContextLoss] GLboolean isTransformFeedback(WebGLTransformFeedback? tf);
void bindTransformFeedback (GLenum target, WebGLTransformFeedback? tf);
@ -553,7 +550,7 @@ interface mixin WebGL2RenderingContextBase
void transformFeedbackVaryings(WebGLProgram program, sequence<DOMString> varyings, GLenum bufferMode);
WebGLActiveInfo? getTransformFeedbackVarying(WebGLProgram program, GLuint index);
void pauseTransformFeedback();
void resumeTransformFeedback();*/
void resumeTransformFeedback();
/* Uniform Buffer Objects and Transform Feedback Buffers */
// void bindBufferBase(GLenum target, GLuint index, WebGLBuffer? buffer);

View file

@ -0,0 +1,11 @@
/* 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 https://mozilla.org/MPL/2.0/. */
//
// WebGL IDL definitions scraped from the Khronos specification:
// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15
//
[Exposed=(Window), Pref="dom.webgl2.enabled"]
interface WebGLTransformFeedback : WebGLObject {
};