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::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}