script/dom/webgl/
webglbuffer.rs1use std::cell::Cell;
7
8use canvas_traits::webgl::{WebGLBufferId, WebGLCommand, WebGLError, WebGLResult, webgl_channel};
9use dom_struct::dom_struct;
10use ipc_channel::ipc;
11use script_bindings::weakref::WeakRef;
12
13use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants;
14use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants;
15use crate::dom::bindings::inheritance::Castable;
16use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
17use crate::dom::bindings::root::DomRoot;
18use crate::dom::webgl::webglobject::WebGLObject;
19use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
20use crate::script_runtime::CanGc;
21
22fn target_is_copy_buffer(target: u32) -> bool {
23 target == WebGL2RenderingContextConstants::COPY_READ_BUFFER ||
24 target == WebGL2RenderingContextConstants::COPY_WRITE_BUFFER
25}
26
27#[derive(JSTraceable, MallocSizeOf)]
28struct DroppableWebGLBuffer {
29 #[no_trace]
30 id: WebGLBufferId,
31 marked_for_deletion: Cell<bool>,
32 attached_counter: Cell<u32>,
33 context: WeakRef<WebGLRenderingContext>,
34}
35
36impl DroppableWebGLBuffer {
37 pub(crate) fn new(
38 id: WebGLBufferId,
39 marked_for_deletion: Cell<bool>,
40 attached_counter: Cell<u32>,
41 context: WeakRef<WebGLRenderingContext>,
42 ) -> Self {
43 Self {
44 id,
45 marked_for_deletion,
46 attached_counter,
47 context,
48 }
49 }
50}
51
52impl DroppableWebGLBuffer {
53 pub(crate) fn is_marked_for_deletion(&self) -> bool {
54 self.marked_for_deletion.get()
55 }
56
57 pub(crate) fn set_marked_for_deletion(&self, marked_for_deletion: bool) {
58 self.marked_for_deletion.set(marked_for_deletion);
59 }
60
61 pub(crate) fn get_attached_counter(&self) -> u32 {
62 self.attached_counter.get()
63 }
64
65 pub(crate) fn set_attached_counter(&self, attached_counter: u32) {
66 self.attached_counter.set(attached_counter);
67 }
68
69 pub(crate) fn id(&self) -> WebGLBufferId {
70 self.id
71 }
72
73 pub(crate) fn is_attached(&self) -> bool {
74 self.get_attached_counter() != 0
75 }
76
77 pub(crate) fn is_deleted(&self) -> bool {
78 self.is_marked_for_deletion() && !self.is_attached()
79 }
80
81 pub(crate) fn delete(&self, operation_fallibility: Operation) {
82 assert!(self.is_deleted());
83 if let Some(context) = self.context.root() {
84 let cmd = WebGLCommand::DeleteBuffer(self.id);
85 match operation_fallibility {
86 Operation::Fallible => context.send_command_ignored(cmd),
87 Operation::Infallible => context.send_command(cmd),
88 }
89 }
90 }
91
92 pub(crate) fn mark_for_deletion(&self, operation_fallibility: Operation) {
93 if self.is_marked_for_deletion() {
94 return;
95 }
96 self.set_marked_for_deletion(true);
97 if self.is_deleted() {
98 self.delete(operation_fallibility);
99 }
100 }
101}
102
103impl Drop for DroppableWebGLBuffer {
104 fn drop(&mut self) {
105 self.mark_for_deletion(Operation::Fallible);
106 }
107}
108
109#[dom_struct]
110pub(crate) struct WebGLBuffer {
111 webgl_object: WebGLObject,
112 target: Cell<Option<u32>>,
114 capacity: Cell<usize>,
115 usage: Cell<u32>,
117 droppable: DroppableWebGLBuffer,
118}
119
120impl WebGLBuffer {
121 fn new_inherited(context: &WebGLRenderingContext, id: WebGLBufferId) -> Self {
122 Self {
123 webgl_object: WebGLObject::new_inherited(context),
124 target: Default::default(),
125 capacity: Default::default(),
126 usage: Cell::new(WebGLRenderingContextConstants::STATIC_DRAW),
127 droppable: DroppableWebGLBuffer::new(
128 id,
129 Default::default(),
130 Default::default(),
131 WeakRef::new(context),
132 ),
133 }
134 }
135
136 pub(crate) fn maybe_new(
137 context: &WebGLRenderingContext,
138 can_gc: CanGc,
139 ) -> Option<DomRoot<Self>> {
140 let (sender, receiver) = webgl_channel().unwrap();
141 context.send_command(WebGLCommand::CreateBuffer(sender));
142 receiver
143 .recv()
144 .unwrap()
145 .map(|id| WebGLBuffer::new(context, id, can_gc))
146 }
147
148 pub(crate) fn new(
149 context: &WebGLRenderingContext,
150 id: WebGLBufferId,
151 can_gc: CanGc,
152 ) -> DomRoot<Self> {
153 reflect_dom_object(
154 Box::new(WebGLBuffer::new_inherited(context, id)),
155 &*context.global(),
156 can_gc,
157 )
158 }
159}
160
161impl WebGLBuffer {
162 pub(crate) fn id(&self) -> WebGLBufferId {
163 self.droppable.id()
164 }
165
166 pub(crate) fn buffer_data(&self, target: u32, data: &[u8], usage: u32) -> WebGLResult<()> {
167 match usage {
168 WebGLRenderingContextConstants::STREAM_DRAW |
169 WebGLRenderingContextConstants::STATIC_DRAW |
170 WebGLRenderingContextConstants::DYNAMIC_DRAW |
171 WebGL2RenderingContextConstants::STATIC_READ |
172 WebGL2RenderingContextConstants::DYNAMIC_READ |
173 WebGL2RenderingContextConstants::STREAM_READ |
174 WebGL2RenderingContextConstants::STATIC_COPY |
175 WebGL2RenderingContextConstants::DYNAMIC_COPY |
176 WebGL2RenderingContextConstants::STREAM_COPY => (),
177 _ => return Err(WebGLError::InvalidEnum),
178 }
179
180 self.capacity.set(data.len());
181 self.usage.set(usage);
182 let (sender, receiver) = ipc::bytes_channel().unwrap();
183 self.upcast::<WebGLObject>()
184 .context()
185 .send_command(WebGLCommand::BufferData(target, receiver, usage));
186 sender.send(data).unwrap();
187 Ok(())
188 }
189
190 pub(crate) fn capacity(&self) -> usize {
191 self.capacity.get()
192 }
193
194 pub(crate) fn mark_for_deletion(&self, operation_fallibility: Operation) {
195 self.droppable.mark_for_deletion(operation_fallibility);
196 }
197
198 fn delete(&self, operation_fallibility: Operation) {
199 self.droppable.delete(operation_fallibility);
200 }
201
202 pub(crate) fn is_marked_for_deletion(&self) -> bool {
203 self.droppable.is_marked_for_deletion()
204 }
205
206 fn get_attached_counter(&self) -> u32 {
207 self.droppable.get_attached_counter()
208 }
209
210 fn set_attached_counter(&self, attached_counter: u32) {
211 self.droppable.set_attached_counter(attached_counter);
212 }
213
214 pub(crate) fn is_deleted(&self) -> bool {
215 self.droppable.is_deleted()
216 }
217
218 pub(crate) fn target(&self) -> Option<u32> {
219 self.target.get()
220 }
221
222 fn can_bind_to(&self, new_target: u32) -> bool {
223 if let Some(current_target) = self.target.get() {
224 if [current_target, new_target]
225 .contains(&WebGLRenderingContextConstants::ELEMENT_ARRAY_BUFFER)
226 {
227 return target_is_copy_buffer(new_target) || new_target == current_target;
228 }
229 }
230 true
231 }
232
233 pub(crate) fn set_target_maybe(&self, target: u32) -> WebGLResult<()> {
234 if !self.can_bind_to(target) {
235 return Err(WebGLError::InvalidOperation);
236 }
237 if !target_is_copy_buffer(target) {
238 self.target.set(Some(target));
239 }
240 Ok(())
241 }
242
243 pub(crate) fn increment_attached_counter(&self) {
244 self.set_attached_counter(
245 self.get_attached_counter()
246 .checked_add(1)
247 .expect("refcount overflowed"),
248 );
249 }
250
251 pub(crate) fn decrement_attached_counter(&self, operation_fallibility: Operation) {
252 self.set_attached_counter(
253 self.get_attached_counter()
254 .checked_sub(1)
255 .expect("refcount underflowed"),
256 );
257 if self.is_deleted() {
258 self.delete(operation_fallibility);
259 }
260 }
261
262 pub(crate) fn usage(&self) -> u32 {
263 self.usage.get()
264 }
265}