script/dom/webgl/
webglbuffer.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
5// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
6use 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    /// The target to which this buffer was bound the first time
113    target: Cell<Option<u32>>,
114    capacity: Cell<usize>,
115    /// <https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glGetBufferParameteriv.xml>
116    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}