Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial implementation of WebGLQueries #24178

Merged
merged 1 commit into from Oct 1, 2019
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -1593,6 +1593,23 @@ impl WebGLImpl {
depth,
stencil,
} => Self::initialize_framebuffer(ctx.gl(), state, color, depth, stencil),
WebGLCommand::BeginQuery(target, query_id) => {
ctx.gl().begin_query(target, query_id.get());
},
WebGLCommand::EndQuery(target) => {
ctx.gl().end_query(target);
},
WebGLCommand::DeleteQuery(query_id) => {
ctx.gl().delete_queries(&[query_id.get()]);
},
WebGLCommand::GenerateQuery(ref sender) => {
let id = ctx.gl().gen_queries(1)[0];
sender.send(unsafe { WebGLQueryId::new(id) }).unwrap()
},
WebGLCommand::GetQueryState(ref sender, query_id, pname) => {
let value = ctx.gl().get_query_object_uiv(query_id.get(), pname);
sender.send(value).unwrap()
},
}

// TODO: update test expectations in order to enable debug assertions
@@ -427,6 +427,11 @@ pub enum WebGLCommand {
depth: bool,
stencil: bool,
},
BeginQuery(u32, WebGLQueryId),
DeleteQuery(WebGLQueryId),
EndQuery(u32),
GenerateQuery(WebGLSender<WebGLQueryId>),
GetQueryState(WebGLSender<u32>, WebGLQueryId, u32),
}

macro_rules! define_resource_id {
@@ -498,6 +503,7 @@ define_resource_id!(WebGLFramebufferId);
define_resource_id!(WebGLRenderbufferId);
define_resource_id!(WebGLTextureId);
define_resource_id!(WebGLProgramId);
define_resource_id!(WebGLQueryId);
define_resource_id!(WebGLShaderId);
define_resource_id!(WebGLVertexArrayId);

@@ -46,7 +46,7 @@ use canvas_traits::canvas::{
};
use canvas_traits::canvas::{CompositionOrBlending, LineCapStyle, LineJoinStyle, RepetitionStyle};
use canvas_traits::webgl::{ActiveAttribInfo, ActiveUniformInfo, GlType, TexDataType, TexFormat};
use canvas_traits::webgl::{GLFormats, GLLimits};
use canvas_traits::webgl::{GLFormats, GLLimits, WebGLQueryId};
use canvas_traits::webgl::{WebGLBufferId, WebGLChan, WebGLContextShareMode, WebGLError};
use canvas_traits::webgl::{WebGLFramebufferId, WebGLMsgSender, WebGLPipeline, WebGLProgramId};
use canvas_traits::webgl::{WebGLReceiver, WebGLRenderbufferId, WebGLSLVersion, WebGLSender};
@@ -477,6 +477,7 @@ unsafe_no_jsmanaged_fields!(WebGLFramebufferId);
unsafe_no_jsmanaged_fields!(WebGLMsgSender);
unsafe_no_jsmanaged_fields!(WebGLPipeline);
unsafe_no_jsmanaged_fields!(WebGLProgramId);
unsafe_no_jsmanaged_fields!(WebGLQueryId);
unsafe_no_jsmanaged_fields!(WebGLRenderbufferId);
unsafe_no_jsmanaged_fields!(WebGLShaderId);
unsafe_no_jsmanaged_fields!(WebGLTextureId);
@@ -522,6 +522,7 @@ pub mod webglcontextevent;
pub mod webglframebuffer;
pub mod webglobject;
pub mod webglprogram;
pub mod webglquery;
pub mod webglrenderbuffer;
pub mod webglrenderingcontext;
pub mod webglshader;
@@ -3,6 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding;
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants;
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextMethods;
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLContextAttributes;
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextMethods;
@@ -12,14 +13,15 @@ use crate::dom::bindings::codegen::UnionTypes::ImageDataOrHTMLImageElementOrHTML
use crate::dom::bindings::codegen::UnionTypes::Int32ArrayOrLongSequence;
use crate::dom::bindings::error::{ErrorResult, Fallible};
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom};
use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::htmlcanvaselement::HTMLCanvasElement;
use crate::dom::htmliframeelement::HTMLIFrameElement;
use crate::dom::webglactiveinfo::WebGLActiveInfo;
use crate::dom::webglbuffer::WebGLBuffer;
use crate::dom::webglframebuffer::WebGLFramebuffer;
use crate::dom::webglprogram::WebGLProgram;
use crate::dom::webglquery::WebGLQuery;
use crate::dom::webglrenderbuffer::WebGLRenderbuffer;
use crate::dom::webglrenderingcontext::{
LayoutCanvasWebGLRenderingContextHelpers, WebGLRenderingContext,
@@ -31,11 +33,12 @@ use crate::dom::webgluniformlocation::WebGLUniformLocation;
use crate::dom::window::Window;
use crate::script_runtime::JSContext;
/// https://www.khronos.org/registry/webgl/specs/latest/2.0/webgl.idl
use canvas_traits::webgl::WebGLError::*;
use canvas_traits::webgl::{GLContextAttributes, WebGLVersion};
use dom_struct::dom_struct;
use euclid::default::Size2D;
use js::jsapi::JSObject;
use js::jsval::JSVal;
use js::jsval::{BooleanValue, JSVal, NullValue, UInt32Value};
use js::rust::CustomAutoRooterGuard;
use js::typedarray::ArrayBufferView;
use script_layout_interface::HTMLCanvasDataSource;
@@ -45,6 +48,8 @@ use std::ptr::NonNull;
pub struct WebGL2RenderingContext {
reflector_: Reflector,
base: Dom<WebGLRenderingContext>,
occlusion_query: MutNullableDom<WebGLQuery>,
primitives_query: MutNullableDom<WebGLQuery>,
}

impl WebGL2RenderingContext {
@@ -58,6 +63,8 @@ impl WebGL2RenderingContext {
Some(WebGL2RenderingContext {
reflector_: Reflector::new(),
base: Dom::from_ref(&*base),
occlusion_query: MutNullableDom::new(None),
primitives_query: MutNullableDom::new(None),
})
}

@@ -1044,6 +1051,152 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext {
fn VertexAttribDivisor(&self, index: u32, divisor: u32) {
self.base.vertex_attrib_divisor(index, divisor);
}

/// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12
fn CreateQuery(&self) -> Option<DomRoot<WebGLQuery>> {
Some(WebGLQuery::new(&self.base))
}

/// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12
#[cfg_attr(rustfmt, rustfmt_skip)]
fn DeleteQuery(&self, query: Option<&WebGLQuery>) {
if let Some(query) = query {
handle_potential_webgl_error!(self.base, self.base.validate_ownership(query), return);

if let Some(query_target) = query.target() {
let slot = match query_target {
constants::ANY_SAMPLES_PASSED |
constants::ANY_SAMPLES_PASSED_CONSERVATIVE => {
&self.occlusion_query
},
constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => {
&self.primitives_query
},
_ => unreachable!(),
};
if let Some(stored_query) = slot.get() {
if stored_query.target() == query.target() {
slot.set(None);
}
}
}

query.delete(false);
}
}

/// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12
fn IsQuery(&self, query: Option<&WebGLQuery>) -> bool {
match query {
Some(query) => self.base.validate_ownership(query).is_ok() && query.is_valid(),
None => false,
}
}

/// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12
#[cfg_attr(rustfmt, rustfmt_skip)]
fn BeginQuery(&self, target: u32, query: &WebGLQuery) {
handle_potential_webgl_error!(self.base, self.base.validate_ownership(query), return);

let active_query = match target {
constants::ANY_SAMPLES_PASSED |
constants::ANY_SAMPLES_PASSED_CONSERVATIVE => {
&self.occlusion_query
},
constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => {
&self.primitives_query
},
_ => {
self.base.webgl_error(InvalidEnum);
return;
},
};
if active_query.get().is_some() {
self.base.webgl_error(InvalidOperation);
return;
}
let result = query.begin(&self.base, target);
match result {
Ok(_) => active_query.set(Some(query)),
Err(error) => self.base.webgl_error(error),
}
}

/// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12
#[cfg_attr(rustfmt, rustfmt_skip)]
fn EndQuery(&self, target: u32) {
let active_query = match target {
constants::ANY_SAMPLES_PASSED |
constants::ANY_SAMPLES_PASSED_CONSERVATIVE => {
self.occlusion_query.take()
},
constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => {
self.primitives_query.take()
},
_ => {
self.base.webgl_error(InvalidEnum);
return;
},
};
match active_query {
None => self.base.webgl_error(InvalidOperation),
Some(query) => {
let result = query.end(&self.base, target);
if let Err(error) = result {
self.base.webgl_error(error);
}
},
}
}

/// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12
#[cfg_attr(rustfmt, rustfmt_skip)]
fn GetQuery(&self, target: u32, pname: u32) -> Option<DomRoot<WebGLQuery>> {
if pname != constants::CURRENT_QUERY {
self.base.webgl_error(InvalidEnum);
return None;
}
let active_query = match target {
constants::ANY_SAMPLES_PASSED |
constants::ANY_SAMPLES_PASSED_CONSERVATIVE => {
self.occlusion_query.get()
},
constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => {
self.primitives_query.get()
},
_ => {
self.base.webgl_error(InvalidEnum);
None
},
};
if let Some(query) = active_query.as_ref() {
if query.target() != Some(target) {
return None;
}
}
active_query
}

/// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12
#[cfg_attr(rustfmt, rustfmt_skip)]
fn GetQueryParameter(&self, _cx: JSContext, query: &WebGLQuery, pname: u32) -> JSVal {
handle_potential_webgl_error!(
self.base,
self.base.validate_ownership(query),
return NullValue()
);
match query.get_parameter(&self.base, pname) {
Ok(value) => match pname {
constants::QUERY_RESULT => UInt32Value(value),

This comment has been minimized.

@jdm

jdm Sep 16, 2019

Member

To get the event loop integration right, I recommend this strategy:

  • add Option<u32> and Option<bool> members for storing cached versions of these results
  • if both these fields are None, get the query parameter from the GL thread and store the result in the appropriate field, and return the result
  • if either field is Some, get the query parameter from the GL thread, queue a task that will update the cached values, and return the current cached value

This comment has been minimized.

@mmatyas

mmatyas Sep 17, 2019

Author Contributor

I see, but what do you mean exactly by queuing a task? The query results should not change until the next time we're in the event loop; do we have a way for scheduling a callback to run at that time? Or you mean connect it somehow to the DOM's global scheduler?

This comment has been minimized.

@jdm

jdm Sep 17, 2019

Member

I mean using the task! macro to declare a closure that will run asynchronously on the script thread, and using the task source API to enqueue it to run later. This code is one example of many in the script/ code of queueing a task.

constants::QUERY_RESULT_AVAILABLE => BooleanValue(value != 0),
_ => unreachable!(),
},
Err(error) => {
self.base.webgl_error(error);
NullValue()
},
}
}
}

impl LayoutCanvasWebGLRenderingContextHelpers for LayoutDom<WebGL2RenderingContext> {
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.