1#![allow(unsafe_code)]
2
3use glow::HasContext as _;
4
5use crate::check_for_gl_error;
6
7#[derive(Debug)]
10pub(crate) struct BufferInfo {
11 pub location: u32, pub vector_size: i32,
13 pub data_type: u32, pub normalized: bool,
15 pub stride: i32,
16 pub offset: i32,
17}
18
19pub(crate) struct VertexArrayObject {
23 vao: Option<crate::glow::VertexArray>,
25 vbo: glow::Buffer,
26 buffer_infos: Vec<BufferInfo>,
27}
28
29impl VertexArrayObject {
30 #[allow(clippy::needless_pass_by_value)] pub(crate) unsafe fn new(
32 gl: &glow::Context,
33 vbo: glow::Buffer,
34 buffer_infos: Vec<BufferInfo>,
35 ) -> Self {
36 let vao = if supports_vao(gl) {
37 unsafe {
38 let vao = gl.create_vertex_array().unwrap();
39 check_for_gl_error!(gl, "create_vertex_array");
40
41 gl.bind_vertex_array(Some(vao));
43 gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
44
45 for attribute in &buffer_infos {
46 gl.vertex_attrib_pointer_f32(
47 attribute.location,
48 attribute.vector_size,
49 attribute.data_type,
50 attribute.normalized,
51 attribute.stride,
52 attribute.offset,
53 );
54 check_for_gl_error!(gl, "vertex_attrib_pointer_f32");
55 gl.enable_vertex_attrib_array(attribute.location);
56 check_for_gl_error!(gl, "enable_vertex_attrib_array");
57 }
58
59 gl.bind_vertex_array(None);
60
61 Some(vao)
62 }
63 } else {
64 log::debug!("VAO not supported");
65 None
66 };
67
68 Self {
69 vao,
70 vbo,
71 buffer_infos,
72 }
73 }
74
75 pub(crate) unsafe fn bind(&self, gl: &glow::Context) {
76 unsafe {
77 if let Some(vao) = self.vao {
78 gl.bind_vertex_array(Some(vao));
79 check_for_gl_error!(gl, "bind_vertex_array");
80 } else {
81 gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vbo));
82 check_for_gl_error!(gl, "bind_buffer");
83
84 for attribute in &self.buffer_infos {
85 gl.vertex_attrib_pointer_f32(
86 attribute.location,
87 attribute.vector_size,
88 attribute.data_type,
89 attribute.normalized,
90 attribute.stride,
91 attribute.offset,
92 );
93 check_for_gl_error!(gl, "vertex_attrib_pointer_f32");
94 gl.enable_vertex_attrib_array(attribute.location);
95 check_for_gl_error!(gl, "enable_vertex_attrib_array");
96 }
97 }
98 }
99 }
100
101 pub(crate) unsafe fn unbind(&self, gl: &glow::Context) {
102 unsafe {
103 if self.vao.is_some() {
104 gl.bind_vertex_array(None);
105 } else {
106 gl.bind_buffer(glow::ARRAY_BUFFER, None);
107 for attribute in &self.buffer_infos {
108 gl.disable_vertex_attrib_array(attribute.location);
109 }
110 }
111 }
112 }
113}
114
115fn supports_vao(gl: &glow::Context) -> bool {
118 const WEBGL_PREFIX: &str = "WebGL ";
119 const OPENGL_ES_PREFIX: &str = "OpenGL ES ";
120
121 let version_string = unsafe { gl.get_parameter_string(glow::VERSION) };
122 log::debug!("GL version: {:?}.", version_string);
123
124 if let Some(pos) = version_string.rfind(WEBGL_PREFIX) {
129 let version_str = &version_string[pos + WEBGL_PREFIX.len()..];
130 if version_str.contains("1.0") {
131 let supported_extensions = gl.supported_extensions();
133 log::debug!("Supported OpenGL extensions: {:?}", supported_extensions);
134 supported_extensions.contains("OES_vertex_array_object")
135 || supported_extensions.contains("GL_OES_vertex_array_object")
136 } else {
137 true
138 }
139 } else if version_string.contains(OPENGL_ES_PREFIX) {
140 if version_string.contains("2.0") {
142 let supported_extensions = gl.supported_extensions();
144 log::debug!("Supported OpenGL extensions: {:?}", supported_extensions);
145 supported_extensions.contains("OES_vertex_array_object")
146 || supported_extensions.contains("GL_OES_vertex_array_object")
147 } else {
148 true
149 }
150 } else {
151 if version_string.starts_with('2') {
153 let supported_extensions = gl.supported_extensions();
156 log::debug!("Supported OpenGL extensions: {:?}", supported_extensions);
157 supported_extensions.contains("ARB_vertex_array_object")
158 || supported_extensions.contains("GL_ARB_vertex_array_object")
159 } else {
160 true
161 }
162 }
163}