script/dom/webgl/
webgltransformfeedback.rs1use 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}