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
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}