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