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            self.upcast().send_with_fallibility(
107                WebGLCommand::DeleteQuery(self.gl_id),
108                operation_fallibility,
109            );
110        }
111    }
112
113    pub(crate) fn is_valid(&self) -> bool {
114        !self.marked_for_deletion.get() && self.target().is_some()
115    }
116
117    pub(crate) fn target(&self) -> Option<u32> {
118        self.gl_target.get()
119    }
120
121    fn update_results(&self, context: &WebGLRenderingContext) {
122        let (sender, receiver) = webgl_channel().unwrap();
123        context.send_command(WebGLCommand::GetQueryState(
124            sender,
125            self.gl_id,
126            constants::QUERY_RESULT_AVAILABLE,
127        ));
128        let is_available = receiver.recv().unwrap();
129        if is_available == 0 {
130            self.query_result_available.set(None);
131            return;
132        }
133
134        let (sender, receiver) = webgl_channel().unwrap();
135        context.send_command(WebGLCommand::GetQueryState(
136            sender,
137            self.gl_id,
138            constants::QUERY_RESULT,
139        ));
140
141        self.query_result.set(receiver.recv().unwrap());
142        self.query_result_available.set(Some(is_available));
143    }
144
145    #[rustfmt::skip]
146    pub(crate) fn get_parameter(
147        &self,
148        context: &WebGLRenderingContext,
149        pname: u32,
150    ) -> Result<u32, canvas_traits::webgl::WebGLError> {
151        if !self.is_valid() {
152            return Err(InvalidOperation);
153        }
154        match pname {
155            constants::QUERY_RESULT |
156            constants::QUERY_RESULT_AVAILABLE => {},
157            _ => return Err(InvalidEnum),
158        }
159
160        if self.query_result_available.get().is_none() {
161            self.query_result_available.set(Some(0));
162
163            let this = Trusted::new(self);
164            let context = Trusted::new(context);
165            let task = task!(request_query_state: move || {
166                let this = this.root();
167                let context = context.root();
168                this.update_results(&context);
169            });
170
171            self.global()
172                .task_manager()
173                .dom_manipulation_task_source()
174                .queue(task);
175        }
176
177        match pname {
178            constants::QUERY_RESULT => Ok(self.query_result.get()),
179            constants::QUERY_RESULT_AVAILABLE => Ok(self.query_result_available.get().unwrap()),
180            _ => unreachable!(),
181        }
182    }
183}
184
185impl Drop for WebGLQuery {
186    fn drop(&mut self) {
187        self.delete(Operation::Fallible);
188    }
189}