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 pub(crate) unsafe fn new(
31 gl: &glow::Context,
32 vbo: glow::Buffer,
33 buffer_infos: Vec<BufferInfo>,
34 ) -> Self {
35 let vao = if supports_vao(gl) {
36 unsafe {
37 let vao = gl.create_vertex_array().unwrap();
38 check_for_gl_error!(gl, "create_vertex_array");
39
40 gl.bind_vertex_array(Some(vao));
42 gl.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
43
44 for attribute in &buffer_infos {
45 gl.vertex_attrib_pointer_f32(
46 attribute.location,
47 attribute.vector_size,
48 attribute.data_type,
49 attribute.normalized,
50 attribute.stride,
51 attribute.offset,
52 );
53 check_for_gl_error!(gl, "vertex_attrib_pointer_f32");
54 gl.enable_vertex_attrib_array(attribute.location);
55 check_for_gl_error!(gl, "enable_vertex_attrib_array");
56 }
57
58 gl.bind_vertex_array(None);
59
60 Some(vao)
61 }
62 } else {
63 log::debug!("VAO not supported");
64 None
65 };
66
67 Self {
68 vao,
69 vbo,
70 buffer_infos,
71 }
72 }
73
74 pub(crate) unsafe fn bind(&self, gl: &glow::Context) {
75 unsafe {
76 if let Some(vao) = self.vao {
77 gl.bind_vertex_array(Some(vao));
78 check_for_gl_error!(gl, "bind_vertex_array");
79 } else {
80 gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vbo));
81 check_for_gl_error!(gl, "bind_buffer");
82
83 for attribute in &self.buffer_infos {
84 gl.vertex_attrib_pointer_f32(
85 attribute.location,
86 attribute.vector_size,
87 attribute.data_type,
88 attribute.normalized,
89 attribute.stride,
90 attribute.offset,
91 );
92 check_for_gl_error!(gl, "vertex_attrib_pointer_f32");
93 gl.enable_vertex_attrib_array(attribute.location);
94 check_for_gl_error!(gl, "enable_vertex_attrib_array");
95 }
96 }
97 }
98 }
99
100 pub(crate) unsafe fn unbind(&self, gl: &glow::Context) {
101 unsafe {
102 if self.vao.is_some() {
103 gl.bind_vertex_array(None);
104 } else {
105 gl.bind_buffer(glow::ARRAY_BUFFER, None);
106 for attribute in &self.buffer_infos {
107 gl.disable_vertex_attrib_array(attribute.location);
108 }
109 }
110 }
111 }
112}
113
114fn supports_vao(gl: &glow::Context) -> bool {
117 const WEBGL_PREFIX: &str = "WebGL ";
118 const OPENGL_ES_PREFIX: &str = "OpenGL ES ";
119
120 let version_string = unsafe { gl.get_parameter_string(glow::VERSION) };
121 log::debug!("GL version: {version_string:?}.");
122
123 if let Some(pos) = version_string.rfind(WEBGL_PREFIX) {
128 let version_str = &version_string[pos + WEBGL_PREFIX.len()..];
129 if version_str.contains("1.0") {
130 let supported_extensions = gl.supported_extensions();
132 log::debug!("Supported OpenGL extensions: {supported_extensions:?}");
133 supported_extensions.contains("OES_vertex_array_object")
134 || supported_extensions.contains("GL_OES_vertex_array_object")
135 } else {
136 true
137 }
138 } else if version_string.contains(OPENGL_ES_PREFIX) {
139 if version_string.contains("2.0") {
141 let supported_extensions = gl.supported_extensions();
143 log::debug!("Supported OpenGL extensions: {supported_extensions:?}");
144 supported_extensions.contains("OES_vertex_array_object")
145 || supported_extensions.contains("GL_OES_vertex_array_object")
146 } else {
147 true
148 }
149 } else {
150 if version_string.starts_with('2') {
152 let supported_extensions = gl.supported_extensions();
155 log::debug!("Supported OpenGL extensions: {supported_extensions:?}");
156 supported_extensions.contains("ARB_vertex_array_object")
157 || supported_extensions.contains("GL_ARB_vertex_array_object")
158 } else {
159 true
160 }
161 }
162}