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