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