script/dom/webgl/
webglquery.rs1use std::cell::Cell;
6
7use dom_struct::dom_struct;
8use script_bindings::reflector::reflect_dom_object;
9use script_bindings::weakref::WeakRef;
10use servo_canvas_traits::webgl::WebGLError::*;
11use servo_canvas_traits::webgl::{WebGLCommand, WebGLQueryId, webgl_channel};
12
13use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants;
14use crate::dom::bindings::refcounted::Trusted;
15use crate::dom::bindings::reflector::DomGlobal;
16use crate::dom::bindings::root::DomRoot;
17use crate::dom::webgl::webglobject::WebGLObject;
18use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
19use crate::dom::webglrenderingcontext::capture_webgl_backtrace;
20use crate::script_runtime::CanGc;
21
22#[derive(JSTraceable, MallocSizeOf)]
23struct DroppableWebGLQuery {
24 context: WeakRef<WebGLRenderingContext>,
25 #[no_trace]
26 gl_id: WebGLQueryId,
27 marked_for_deletion: Cell<bool>,
28}
29
30impl DroppableWebGLQuery {
31 fn send_with_fallibility(&self, command: WebGLCommand, fallibility: Operation) {
32 if let Some(root) = self.context.root() {
33 let result = root.sender().send(command, capture_webgl_backtrace());
34 if matches!(fallibility, Operation::Infallible) {
35 result.expect("Operation failed");
36 }
37 }
38 }
39
40 fn delete(&self, operation_fallibility: Operation) {
41 if !self.marked_for_deletion.get() {
42 self.marked_for_deletion.set(true);
43 self.send_with_fallibility(
44 WebGLCommand::DeleteQuery(self.gl_id),
45 operation_fallibility,
46 );
47 }
48 }
49}
50
51impl Drop for DroppableWebGLQuery {
52 fn drop(&mut self) {
53 self.delete(Operation::Fallible);
54 }
55}
56
57#[dom_struct(associated_memory)]
58pub(crate) struct WebGLQuery {
59 webgl_object: WebGLObject,
60 gl_target: Cell<Option<u32>>,
61 query_result_available: Cell<Option<u32>>,
62 query_result: Cell<u32>,
63 droppable: DroppableWebGLQuery,
64}
65
66impl WebGLQuery {
67 fn new_inherited(context: &WebGLRenderingContext, id: WebGLQueryId) -> Self {
68 Self {
69 webgl_object: WebGLObject::new_inherited(context),
70 gl_target: Cell::new(None),
71 query_result_available: Cell::new(None),
72 query_result: Cell::new(0),
73 droppable: DroppableWebGLQuery {
74 context: WeakRef::new(context),
75 gl_id: id,
76 marked_for_deletion: Cell::new(false),
77 },
78 }
79 }
80
81 pub(crate) fn new(context: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<Self> {
82 let (sender, receiver) = webgl_channel().unwrap();
83 context.send_command(WebGLCommand::GenerateQuery(sender));
84 let id = receiver.recv().unwrap();
85
86 reflect_dom_object(
87 Box::new(Self::new_inherited(context, id)),
88 &*context.global(),
89 can_gc,
90 )
91 }
92
93 pub(crate) fn begin(
94 &self,
95 context: &WebGLRenderingContext,
96 target: u32,
97 ) -> Result<(), servo_canvas_traits::webgl::WebGLError> {
98 if self.droppable.marked_for_deletion.get() {
99 return Err(InvalidOperation);
100 }
101 if let Some(current_target) = self.gl_target.get() &&
102 current_target != target
103 {
104 return Err(InvalidOperation);
105 }
106 match target {
107 constants::ANY_SAMPLES_PASSED |
108 constants::ANY_SAMPLES_PASSED_CONSERVATIVE |
109 constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => (),
110 _ => return Err(InvalidEnum),
111 }
112 self.gl_target.set(Some(target));
113
114 context.send_command(WebGLCommand::BeginQuery(target, self.droppable.gl_id));
115 Ok(())
116 }
117
118 pub(crate) fn end(
119 &self,
120 context: &WebGLRenderingContext,
121 target: u32,
122 ) -> Result<(), servo_canvas_traits::webgl::WebGLError> {
123 if self.droppable.marked_for_deletion.get() {
124 return Err(InvalidOperation);
125 }
126 if let Some(current_target) = self.gl_target.get() &&
127 current_target != target
128 {
129 return Err(InvalidOperation);
130 }
131 match target {
132 constants::ANY_SAMPLES_PASSED |
133 constants::ANY_SAMPLES_PASSED_CONSERVATIVE |
134 constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => (),
135 _ => return Err(InvalidEnum),
136 }
137 context.send_command(WebGLCommand::EndQuery(target));
138 Ok(())
139 }
140
141 pub(crate) fn delete(&self, operation_fallibility: Operation) {
142 self.droppable.delete(operation_fallibility);
143 }
144
145 pub(crate) fn is_valid(&self) -> bool {
146 !self.droppable.marked_for_deletion.get() && self.target().is_some()
147 }
148
149 pub(crate) fn target(&self) -> Option<u32> {
150 self.gl_target.get()
151 }
152
153 fn update_results(&self, context: &WebGLRenderingContext) {
154 let (sender, receiver) = webgl_channel().unwrap();
155 context.send_command(WebGLCommand::GetQueryState(
156 sender,
157 self.droppable.gl_id,
158 constants::QUERY_RESULT_AVAILABLE,
159 ));
160 let is_available = receiver.recv().unwrap();
161 if is_available == 0 {
162 self.query_result_available.set(None);
163 return;
164 }
165
166 let (sender, receiver) = webgl_channel().unwrap();
167 context.send_command(WebGLCommand::GetQueryState(
168 sender,
169 self.droppable.gl_id,
170 constants::QUERY_RESULT,
171 ));
172
173 self.query_result.set(receiver.recv().unwrap());
174 self.query_result_available.set(Some(is_available));
175 }
176
177 #[rustfmt::skip]
178 pub(crate) fn get_parameter(
179 &self,
180 context: &WebGLRenderingContext,
181 pname: u32,
182 ) -> Result<u32, servo_canvas_traits::webgl::WebGLError> {
183 if !self.is_valid() {
184 return Err(InvalidOperation);
185 }
186 match pname {
187 constants::QUERY_RESULT |
188 constants::QUERY_RESULT_AVAILABLE => {},
189 _ => return Err(InvalidEnum),
190 }
191
192 if self.query_result_available.get().is_none() {
193 self.query_result_available.set(Some(0));
194
195 let this = Trusted::new(self);
196 let context = Trusted::new(context);
197 let task = task!(request_query_state: move || {
198 let this = this.root();
199 let context = context.root();
200 this.update_results(&context);
201 });
202
203 self.global()
204 .task_manager()
205 .dom_manipulation_task_source()
206 .queue(task);
207 }
208
209 match pname {
210 constants::QUERY_RESULT => Ok(self.query_result.get()),
211 constants::QUERY_RESULT_AVAILABLE => Ok(self.query_result_available.get().unwrap()),
212 _ => unreachable!(),
213 }
214 }
215}