Skip to main content

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