1use 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::WebGLError::*;
11use servo_canvas_traits::webgl::{WebGLCommand, WebGLSamplerId, webgl_channel};
12
13use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants;
14use crate::dom::bindings::reflector::DomGlobal;
15use crate::dom::bindings::root::DomRoot;
16use crate::dom::webgl::webglobject::WebGLObject;
17use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
18use crate::dom::webglrenderingcontext::capture_webgl_backtrace;
19use crate::script_runtime::CanGc;
20
21#[derive(JSTraceable, MallocSizeOf)]
22struct DroppableWebGLSampler {
23 context: WeakRef<WebGLRenderingContext>,
24 #[no_trace]
25 gl_id: WebGLSamplerId,
26 marked_for_deletion: Cell<bool>,
27}
28
29impl DroppableWebGLSampler {
30 fn send_with_fallibility(&self, command: WebGLCommand, fallibility: Operation) {
31 if let Some(root) = self.context.root() {
32 let result = root.sender().send(command, capture_webgl_backtrace());
33 if matches!(fallibility, Operation::Infallible) {
34 result.expect("Operation failed");
35 }
36 }
37 }
38
39 fn delete(&self, operation_fallibility: Operation) {
40 if !self.marked_for_deletion.get() {
41 self.marked_for_deletion.set(true);
42 self.send_with_fallibility(
43 WebGLCommand::DeleteSampler(self.gl_id),
44 operation_fallibility,
45 );
46 }
47 }
48}
49
50impl Drop for DroppableWebGLSampler {
51 fn drop(&mut self) {
52 self.delete(Operation::Fallible);
53 }
54}
55
56#[dom_struct(associated_memory)]
57pub(crate) struct WebGLSampler {
58 webgl_object: WebGLObject,
59 droppable: DroppableWebGLSampler,
60}
61
62#[derive(Clone, Copy)]
63pub(crate) enum WebGLSamplerValue {
64 Float(f32),
65 GLenum(u32),
66}
67
68fn validate_params(pname: u32, value: WebGLSamplerValue) -> bool {
69 match value {
70 WebGLSamplerValue::GLenum(value) => {
71 let allowed_values = match pname {
72 constants::TEXTURE_MIN_FILTER => &[
73 constants::NEAREST,
74 constants::LINEAR,
75 constants::NEAREST_MIPMAP_NEAREST,
76 constants::LINEAR_MIPMAP_NEAREST,
77 constants::NEAREST_MIPMAP_LINEAR,
78 constants::LINEAR_MIPMAP_LINEAR,
79 ][..],
80 constants::TEXTURE_MAG_FILTER => &[constants::NEAREST, constants::LINEAR][..],
81 constants::TEXTURE_WRAP_R |
82 constants::TEXTURE_WRAP_S |
83 constants::TEXTURE_WRAP_T => &[
84 constants::CLAMP_TO_EDGE,
85 constants::MIRRORED_REPEAT,
86 constants::REPEAT,
87 ][..],
88 constants::TEXTURE_COMPARE_MODE => {
89 &[constants::NONE, constants::COMPARE_REF_TO_TEXTURE][..]
90 },
91 constants::TEXTURE_COMPARE_FUNC => &[
92 constants::LEQUAL,
93 constants::GEQUAL,
94 constants::LESS,
95 constants::GREATER,
96 constants::EQUAL,
97 constants::NOTEQUAL,
98 constants::ALWAYS,
99 constants::NEVER,
100 ][..],
101 _ => &[][..],
102 };
103 allowed_values.contains(&value)
104 },
105 WebGLSamplerValue::Float(_) => matches!(
106 pname,
107 constants::TEXTURE_MIN_LOD | constants::TEXTURE_MAX_LOD
108 ),
109 }
110}
111
112impl WebGLSampler {
113 fn new_inherited(context: &WebGLRenderingContext, id: WebGLSamplerId) -> Self {
114 Self {
115 webgl_object: WebGLObject::new_inherited(context),
116 droppable: DroppableWebGLSampler {
117 context: WeakRef::new(context),
118 gl_id: id,
119 marked_for_deletion: Cell::new(false),
120 },
121 }
122 }
123
124 pub(crate) fn new(context: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<Self> {
125 let (sender, receiver) = webgl_channel().unwrap();
126 context.send_command(WebGLCommand::GenerateSampler(sender));
127 let id = receiver.recv().unwrap();
128
129 reflect_dom_object(
130 Box::new(Self::new_inherited(context, id)),
131 &*context.global(),
132 can_gc,
133 )
134 }
135
136 fn id(&self) -> WebGLSamplerId {
137 self.droppable.gl_id
138 }
139
140 pub(crate) fn delete(&self, operation_fallibility: Operation) {
141 self.droppable.delete(operation_fallibility);
142 }
143
144 pub(crate) fn is_valid(&self) -> bool {
145 !self.droppable.marked_for_deletion.get()
146 }
147
148 pub(crate) fn bind(
149 &self,
150 context: &WebGLRenderingContext,
151 unit: u32,
152 ) -> Result<(), servo_canvas_traits::webgl::WebGLError> {
153 if !self.is_valid() {
154 return Err(InvalidOperation);
155 }
156 context.send_command(WebGLCommand::BindSampler(unit, self.id()));
157 Ok(())
158 }
159
160 pub(crate) fn set_parameter(
161 &self,
162 context: &WebGLRenderingContext,
163 pname: u32,
164 value: WebGLSamplerValue,
165 ) -> Result<(), servo_canvas_traits::webgl::WebGLError> {
166 if !self.is_valid() {
167 return Err(InvalidOperation);
168 }
169 if !validate_params(pname, value) {
170 return Err(InvalidEnum);
171 }
172 let command = match value {
173 WebGLSamplerValue::GLenum(value) => {
174 WebGLCommand::SetSamplerParameterInt(self.id(), pname, value as i32)
175 },
176 WebGLSamplerValue::Float(value) => {
177 WebGLCommand::SetSamplerParameterFloat(self.id(), pname, value)
178 },
179 };
180 context.send_command(command);
181 Ok(())
182 }
183
184 pub(crate) fn get_parameter(
185 &self,
186 context: &WebGLRenderingContext,
187 pname: u32,
188 ) -> Result<WebGLSamplerValue, servo_canvas_traits::webgl::WebGLError> {
189 if !self.is_valid() {
190 return Err(InvalidOperation);
191 }
192 match pname {
193 constants::TEXTURE_MIN_FILTER |
194 constants::TEXTURE_MAG_FILTER |
195 constants::TEXTURE_WRAP_R |
196 constants::TEXTURE_WRAP_S |
197 constants::TEXTURE_WRAP_T |
198 constants::TEXTURE_COMPARE_FUNC |
199 constants::TEXTURE_COMPARE_MODE => {
200 let (sender, receiver) = webgl_channel().unwrap();
201 context.send_command(WebGLCommand::GetSamplerParameterInt(
202 self.id(),
203 pname,
204 sender,
205 ));
206 Ok(WebGLSamplerValue::GLenum(receiver.recv().unwrap() as u32))
207 },
208 constants::TEXTURE_MIN_LOD | constants::TEXTURE_MAX_LOD => {
209 let (sender, receiver) = webgl_channel().unwrap();
210 context.send_command(WebGLCommand::GetSamplerParameterFloat(
211 self.id(),
212 pname,
213 sender,
214 ));
215 Ok(WebGLSamplerValue::Float(receiver.recv().unwrap()))
216 },
217 _ => Err(InvalidEnum),
218 }
219 }
220}