webgl_rc/
gl.rs

1use js_sys::JsString;
2use std::cell::RefCell;
3use std::rc::Rc;
4use wasm_bindgen::{JsCast, JsValue};
5use web_sys::{
6    AngleInstancedArrays, ExtColorBufferHalfFloat, HtmlCanvasElement, OesElementIndexUint,
7    OesTextureHalfFloat, OesTextureHalfFloatLinear, WebGlRenderingContext as Context,
8};
9
10use super::data_buffer::ItemsBuffer;
11use super::program::Program;
12use super::settings::{EmptySetting, Settings, SettingsCache};
13use super::texture::{Texture, TextureContent, TextureFormat, TextureType};
14use crate::buffer_usage::BufferUsage;
15use crate::{DepthBuffer, ElementsBuffer, FrameBuffer};
16
17#[derive(Clone, Debug, PartialEq, Eq)]
18pub enum GlError {
19    UnknownError(Option<String>),
20    ShaderCompilationError {
21        source: String,
22        info: Option<String>,
23    },
24    ProgramLinkingError {
25        vertex: String,
26        fragment: String,
27        info: Option<String>,
28    },
29    ExtensionNotFound(String),
30    UnsupportedType(Option<String>),
31    BufferAllocationError,
32    ReadPixelsError(Option<String>),
33    WritePixelsError(Option<String>),
34    InitTextureBufferError(Option<String>),
35    InvalidBufferSize {
36        expected: u32,
37        received: u32,
38    },
39    DepthBufferError,
40    FrameBufferError,
41}
42
43impl From<GlError> for js_sys::Error {
44    fn from(error: GlError) -> Self {
45        js_sys::Error::new(&format!("{:?}", error))
46    }
47}
48
49impl From<GlError> for JsValue {
50    fn from(error: GlError) -> Self {
51        let js_error: js_sys::Error = error.into();
52        js_error.into()
53    }
54}
55
56impl Into<GlError> for js_sys::Error {
57    fn into(self) -> GlError {
58        GlError::UnknownError(Some(self.message().into()))
59    }
60}
61
62impl Into<GlError> for JsValue {
63    fn into(self) -> GlError {
64        let error: js_sys::Error = self.into();
65        error.into()
66    }
67}
68
69#[derive(Debug)]
70pub(self) struct GlInfo {
71    pub(crate) context: Context,
72    pub(self) settings_cache: RefCell<SettingsCache>,
73    pub(self) ex_instanced_arrays: AngleInstancedArrays,
74    pub(self) ex_color_buffer_half_float: ExtColorBufferHalfFloat,
75    pub(self) ex_texture_half_float: OesTextureHalfFloat,
76    pub(self) ex_texture_half_float_linear: OesTextureHalfFloatLinear,
77    pub(self) ex_element_index_uint: OesElementIndexUint,
78}
79
80#[derive(Clone, Debug)]
81pub struct Gl {
82    data: Rc<GlInfo>,
83}
84
85impl Gl {
86    fn get_extension<Ex: JsCast>(context: &Context, name: &str) -> Result<Ex, GlError> {
87        context
88            .get_extension(name)
89            .map_err(|_| GlError::ExtensionNotFound(name.into()))
90            .map(|value| match value {
91                Some(extension) => Ok(Ex::unchecked_from_js(extension.into())),
92                None => Err(GlError::ExtensionNotFound(name.into())),
93            })
94            .unwrap_or_else(|error| Err(error))
95    }
96
97    pub fn new(canvas: &HtmlCanvasElement) -> Result<Gl, GlError> {
98        let context = canvas
99            .get_context("webgl")
100            .map_err(|err| GlError::UnknownError(Some(JsString::from(err).into())))?
101            .map(|context| Context::from(JsValue::from(context)))
102            .ok_or_else(|| GlError::UnknownError(None))?;
103
104        Ok(Gl {
105            data: Rc::new(GlInfo {
106                ex_instanced_arrays: Gl::get_extension(&context, "ANGLE_instanced_arrays")?,
107                ex_color_buffer_half_float: Gl::get_extension(
108                    &context,
109                    "EXT_color_buffer_half_float",
110                )?,
111                ex_texture_half_float: Gl::get_extension(&context, "OES_texture_half_float")?,
112                ex_texture_half_float_linear: Gl::get_extension(
113                    &context,
114                    "OES_texture_half_float_linear",
115                )?,
116                ex_element_index_uint: Gl::get_extension(&context, "OES_element_index_uint")?,
117                settings_cache: Default::default(),
118                context,
119            }),
120        })
121    }
122
123    pub fn context(&self) -> &Context {
124        &self.data.context
125    }
126
127    pub fn instanced_arrays(&self) -> &AngleInstancedArrays {
128        &self.data.ex_instanced_arrays
129    }
130
131    pub fn settings() -> impl Settings {
132        EmptySetting {}
133    }
134
135    pub fn apply<R>(&self, settings: impl Settings, callback: impl FnOnce() -> R) -> R {
136        settings.apply(self, &self.data.settings_cache, callback)
137    }
138
139    pub fn program(&self, fragment: &str, vertex: &str) -> Result<Program, GlError> {
140        Program::new(self.clone(), fragment, vertex)
141    }
142
143    pub fn items_buffer<I>(&self, data: &[I], usage: BufferUsage) -> Result<ItemsBuffer<I>, GlError>
144    where
145        I: super::data_buffer::Item,
146    {
147        ItemsBuffer::new(self.clone(), data, usage)
148    }
149
150    pub fn elements_buffer(
151        &self,
152        data: &[u32],
153        usage: BufferUsage,
154    ) -> Result<ElementsBuffer, GlError> {
155        ElementsBuffer::new(self.clone(), data, usage)
156    }
157
158    pub fn clear_color_buffer(&self) {
159        self.context().clear(Context::COLOR_BUFFER_BIT);
160    }
161
162    pub fn clear_depth_buffer(&self) {
163        self.context().clear(Context::DEPTH_BUFFER_BIT);
164    }
165
166    pub fn clear_buffers(&self) {
167        self.context()
168            .clear(Context::COLOR_BUFFER_BIT | Context::DEPTH_BUFFER_BIT);
169    }
170
171    pub fn texture(
172        &self,
173        width: u32,
174        height: u32,
175        data_type: TextureType,
176        format: TextureFormat,
177        data: TextureContent,
178    ) -> Result<Texture, GlError> {
179        Texture::new(self.clone(), width, height, data_type, format, data)
180    }
181
182    pub fn depth_buffer(&self, width: u32, height: u32) -> Result<DepthBuffer, GlError> {
183        DepthBuffer::new(self.clone(), width, height)
184    }
185
186    pub fn frame_buffer(&self) -> Result<FrameBuffer, GlError> {
187        FrameBuffer::new(self.clone())
188    }
189
190    pub fn frame_buffer_with_color(&self, texture: Texture) -> Result<FrameBuffer, GlError> {
191        let mut result = FrameBuffer::new(self.clone())?;
192        result.set_color_buffer(Some(texture));
193        Ok(result)
194    }
195
196    pub fn frame_buffer_with_depth(
197        &self,
198        texture: Texture,
199        depth_buffer: DepthBuffer,
200    ) -> Result<FrameBuffer, GlError> {
201        let mut result = FrameBuffer::new(self.clone())?;
202        result.set_color_buffer(Some(texture));
203        result.set_depth_buffer(Some(depth_buffer));
204        Ok(result)
205    }
206}