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}