script/dom/webgl/
webglquery.rs1use 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}