script/dom/webgl/
webglquery.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::Cell;
6
7use canvas_traits::webgl::WebGLError::*;
8use canvas_traits::webgl::{WebGLCommand, WebGLQueryId, webgl_channel};
9use dom_struct::dom_struct;
10
11use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants;
12use crate::dom::bindings::inheritance::Castable;
13use crate::dom::bindings::refcounted::Trusted;
14use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
15use crate::dom::bindings::root::DomRoot;
16use crate::dom::webgl::webglobject::WebGLObject;
17use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
18use crate::script_runtime::CanGc;
19
20#[dom_struct]
21pub(crate) struct WebGLQuery {
22    webgl_object: WebGLObject,
23    #[no_trace]
24    gl_id: WebGLQueryId,
25    gl_target: Cell<Option<u32>>,
26    marked_for_deletion: Cell<bool>,
27    query_result_available: Cell<Option<u32>>,
28    query_result: Cell<u32>,
29}
30
31impl WebGLQuery {
32    fn new_inherited(context: &WebGLRenderingContext, id: WebGLQueryId) -> Self {
33        Self {
34            webgl_object: WebGLObject::new_inherited(context),
35            gl_id: id,
36            gl_target: Cell::new(None),
37            marked_for_deletion: Cell::new(false),
38            query_result_available: Cell::new(None),
39            query_result: Cell::new(0),
40        }
41    }
42
43    pub(crate) fn new(context: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<Self> {
44        let (sender, receiver) = webgl_channel().unwrap();
45        context.send_command(WebGLCommand::GenerateQuery(sender));
46        let id = receiver.recv().unwrap();
47
48        reflect_dom_object(
49            Box::new(Self::new_inherited(context, id)),
50            &*context.global(),
51            can_gc,
52        )
53    }
54
55    pub(crate) fn begin(
56        &self,
57        context: &WebGLRenderingContext,
58        target: u32,
59    ) -> Result<(), canvas_traits::webgl::WebGLError> {
60        if self.marked_for_deletion.get() {
61            return Err(InvalidOperation);
62        }
63        if let Some(current_target) = self.gl_target.get() {
64            if current_target != target {
65                return Err(InvalidOperation);
66            }
67        }
68        match target {
69            constants::ANY_SAMPLES_PASSED |
70            constants::ANY_SAMPLES_PASSED_CONSERVATIVE |
71            constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => (),
72            _ => return Err(InvalidEnum),
73        }
74        self.gl_target.set(Some(target));
75
76        context.send_command(WebGLCommand::BeginQuery(target, self.gl_id));
77        Ok(())
78    }
79
80    pub(crate) fn end(
81        &self,
82        context: &WebGLRenderingContext,
83        target: u32,
84    ) -> Result<(), canvas_traits::webgl::WebGLError> {
85        if self.marked_for_deletion.get() {
86            return Err(InvalidOperation);
87        }
88        if let Some(current_target) = self.gl_target.get() {
89            if current_target != target {
90                return Err(InvalidOperation);
91            }
92        }
93        match target {
94            constants::ANY_SAMPLES_PASSED |
95            constants::ANY_SAMPLES_PASSED_CONSERVATIVE |
96            constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => (),
97            _ => return Err(InvalidEnum),
98        }
99        context.send_command(WebGLCommand::EndQuery(target));
100        Ok(())
101    }
102
103    pub(crate) fn delete(&self, operation_fallibility: Operation) {
104        if !self.marked_for_deletion.get() {
105            self.marked_for_deletion.set(true);
106
107            let context = self.upcast::<WebGLObject>().context();
108            let command = WebGLCommand::DeleteQuery(self.gl_id);
109            match operation_fallibility {
110                Operation::Fallible => context.send_command_ignored(command),
111                Operation::Infallible => context.send_command(command),
112            }
113        }
114    }
115
116    pub(crate) fn is_valid(&self) -> bool {
117        !self.marked_for_deletion.get() && self.target().is_some()
118    }
119
120    pub(crate) fn target(&self) -> Option<u32> {
121        self.gl_target.get()
122    }
123
124    fn update_results(&self, context: &WebGLRenderingContext) {
125        let (sender, receiver) = webgl_channel().unwrap();
126        context.send_command(WebGLCommand::GetQueryState(
127            sender,
128            self.gl_id,
129            constants::QUERY_RESULT_AVAILABLE,
130        ));
131        let is_available = receiver.recv().unwrap();
132        if is_available == 0 {
133            self.query_result_available.set(None);
134            return;
135        }
136
137        let (sender, receiver) = webgl_channel().unwrap();
138        context.send_command(WebGLCommand::GetQueryState(
139            sender,
140            self.gl_id,
141            constants::QUERY_RESULT,
142        ));
143
144        self.query_result.set(receiver.recv().unwrap());
145        self.query_result_available.set(Some(is_available));
146    }
147
148    #[rustfmt::skip]
149    pub(crate) fn get_parameter(
150        &self,
151        context: &WebGLRenderingContext,
152        pname: u32,
153    ) -> Result<u32, canvas_traits::webgl::WebGLError> {
154        if !self.is_valid() {
155            return Err(InvalidOperation);
156        }
157        match pname {
158            constants::QUERY_RESULT |
159            constants::QUERY_RESULT_AVAILABLE => {},
160            _ => return Err(InvalidEnum),
161        }
162
163        if self.query_result_available.get().is_none() {
164            self.query_result_available.set(Some(0));
165
166            let this = Trusted::new(self);
167            let context = Trusted::new(context);
168            let task = task!(request_query_state: move || {
169                let this = this.root();
170                let context = context.root();
171                this.update_results(&context);
172            });
173
174            self.global()
175                .task_manager()
176                .dom_manipulation_task_source()
177                .queue(task);
178        }
179
180        match pname {
181            constants::QUERY_RESULT => Ok(self.query_result.get()),
182            constants::QUERY_RESULT_AVAILABLE => Ok(self.query_result_available.get().unwrap()),
183            _ => unreachable!(),
184        }
185    }
186}
187
188impl Drop for WebGLQuery {
189    fn drop(&mut self) {
190        self.delete(Operation::Fallible);
191    }
192}