script/dom/webgl/
webgltransformfeedback.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
5use std::cell::Cell;
6
7use dom_struct::dom_struct;
8use script_bindings::weakref::WeakRef;
9use servo_canvas_traits::webgl::{WebGLCommand, webgl_channel};
10
11use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
12use crate::dom::bindings::root::DomRoot;
13use crate::dom::webgl::webglobject::WebGLObject;
14use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
15use crate::dom::webglrenderingcontext::capture_webgl_backtrace;
16use crate::script_runtime::CanGc;
17
18#[derive(JSTraceable, MallocSizeOf)]
19struct DroppableWebGLTransformFeedback {
20    context: WeakRef<WebGLRenderingContext>,
21    id: u32,
22    marked_for_deletion: Cell<bool>,
23}
24
25impl DroppableWebGLTransformFeedback {
26    fn send_with_fallibility(&self, command: WebGLCommand, fallibility: Operation) {
27        if let Some(root) = self.context.root() {
28            let result = root.sender().send(command, capture_webgl_backtrace());
29            if matches!(fallibility, Operation::Infallible) {
30                result.expect("Operation failed");
31            }
32        }
33    }
34
35    fn delete(&self, operation_fallibility: Operation) {
36        if self.is_valid() && self.id() != 0 {
37            self.marked_for_deletion.set(true);
38            self.send_with_fallibility(
39                WebGLCommand::DeleteTransformFeedback(self.id()),
40                operation_fallibility,
41            );
42        }
43    }
44
45    fn id(&self) -> u32 {
46        self.id
47    }
48
49    fn is_valid(&self) -> bool {
50        !self.marked_for_deletion.get()
51    }
52}
53
54impl Drop for DroppableWebGLTransformFeedback {
55    fn drop(&mut self) {
56        self.delete(Operation::Fallible);
57    }
58}
59
60#[dom_struct(associated_memory)]
61pub(crate) struct WebGLTransformFeedback {
62    webgl_object: WebGLObject,
63    has_been_bound: Cell<bool>,
64    is_active: Cell<bool>,
65    is_paused: Cell<bool>,
66    droppable: DroppableWebGLTransformFeedback,
67}
68
69impl WebGLTransformFeedback {
70    fn new_inherited(context: &WebGLRenderingContext, id: u32) -> Self {
71        Self {
72            webgl_object: WebGLObject::new_inherited(context),
73            has_been_bound: Cell::new(false),
74            is_active: Cell::new(false),
75            is_paused: Cell::new(false),
76            droppable: DroppableWebGLTransformFeedback {
77                context: WeakRef::new(context),
78                id,
79                marked_for_deletion: Cell::new(false),
80            },
81        }
82    }
83
84    pub(crate) fn new(context: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<Self> {
85        let (sender, receiver) = webgl_channel().unwrap();
86        context.send_command(WebGLCommand::CreateTransformFeedback(sender));
87        let id = receiver.recv().unwrap();
88
89        reflect_dom_object(
90            Box::new(WebGLTransformFeedback::new_inherited(context, id)),
91            &*context.global(),
92            can_gc,
93        )
94    }
95}
96
97impl WebGLTransformFeedback {
98    pub(crate) fn bind(&self, context: &WebGLRenderingContext, target: u32) {
99        context.send_command(WebGLCommand::BindTransformFeedback(target, self.id()));
100        self.has_been_bound.set(true);
101    }
102
103    pub(crate) fn begin(&self, context: &WebGLRenderingContext, primitive_mode: u32) {
104        if self.has_been_bound.get() && !self.is_active() {
105            context.send_command(WebGLCommand::BeginTransformFeedback(primitive_mode));
106            self.set_active(true);
107        }
108    }
109
110    pub(crate) fn end(&self, context: &WebGLRenderingContext) {
111        if self.has_been_bound.get() && self.is_active() {
112            if self.is_paused() {
113                context.send_command(WebGLCommand::ResumeTransformFeedback());
114            }
115            context.send_command(WebGLCommand::EndTransformFeedback());
116            self.set_active(false);
117        }
118    }
119
120    pub(crate) fn resume(&self, context: &WebGLRenderingContext) {
121        if self.is_active() && self.is_paused() {
122            context.send_command(WebGLCommand::ResumeTransformFeedback());
123            self.set_pause(false);
124        }
125    }
126
127    pub(crate) fn pause(&self, context: &WebGLRenderingContext) {
128        if self.is_active() && !self.is_paused() {
129            context.send_command(WebGLCommand::PauseTransformFeedback());
130            self.set_pause(true);
131        }
132    }
133
134    pub(crate) fn id(&self) -> u32 {
135        self.droppable.id()
136    }
137
138    pub(crate) fn is_valid(&self) -> bool {
139        self.droppable.is_valid()
140    }
141
142    pub(crate) fn is_active(&self) -> bool {
143        self.is_active.get()
144    }
145
146    pub(crate) fn is_paused(&self) -> bool {
147        self.is_paused.get()
148    }
149
150    pub(crate) fn delete(&self, operation_fallibility: Operation) {
151        self.droppable.delete(operation_fallibility);
152    }
153
154    pub(crate) fn set_active(&self, value: bool) {
155        if self.is_valid() && self.has_been_bound.get() {
156            self.is_active.set(value);
157        }
158    }
159
160    pub(crate) fn set_pause(&self, value: bool) {
161        if self.is_valid() && self.is_active() {
162            self.is_active.set(value);
163        }
164    }
165}