Skip to main content

script/dom/webgl/
webglsampler.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::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}