Serenity Operating System
1/*
2 * Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
3 * Copyright (c) 2021, Stephan Unverwerth <s.unverwerth@serenityos.org>
4 * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
5 *
6 * SPDX-License-Identifier: BSD-2-Clause
7 */
8
9#include <AK/Assertions.h>
10#include <AK/NumericLimits.h>
11#include <LibGL/GLContext.h>
12
13namespace GL {
14
15// General helper function to read arbitrary vertex attribute data into a float array
16static void read_from_vertex_attribute_pointer(VertexAttribPointer const& attrib, int index, float* elements)
17{
18 auto const* byte_ptr = reinterpret_cast<char const*>(attrib.pointer);
19
20 auto read_values = [&]<typename T>() {
21 auto const stride = (attrib.stride == 0) ? sizeof(T) * attrib.size : attrib.stride;
22 for (int i = 0; i < attrib.size; ++i) {
23 elements[i] = *(reinterpret_cast<T const*>(byte_ptr + stride * index) + i);
24 if constexpr (IsIntegral<T>) {
25 if (attrib.normalize)
26 elements[i] /= NumericLimits<T>::max();
27 }
28 }
29 };
30
31 switch (attrib.type) {
32 case GL_BYTE:
33 read_values.operator()<GLbyte>();
34 break;
35 case GL_UNSIGNED_BYTE:
36 read_values.operator()<GLubyte>();
37 break;
38 case GL_SHORT:
39 read_values.operator()<GLshort>();
40 break;
41 case GL_UNSIGNED_SHORT:
42 read_values.operator()<GLushort>();
43 break;
44 case GL_INT:
45 read_values.operator()<GLint>();
46 break;
47 case GL_UNSIGNED_INT:
48 read_values.operator()<GLuint>();
49 break;
50 case GL_FLOAT:
51 read_values.operator()<GLfloat>();
52 break;
53 case GL_DOUBLE:
54 read_values.operator()<GLdouble>();
55 break;
56 }
57}
58
59void GLContext::gl_array_element(GLint i)
60{
61 // NOTE: This always dereferences data; display list support is deferred to the
62 // individual vertex attribute calls such as `gl_color`, `gl_normal` etc.
63 RETURN_WITH_ERROR_IF(i < 0, GL_INVALID_VALUE);
64
65 if (m_client_side_color_array_enabled) {
66 float color[4] { 0.f, 0.f, 0.f, 1.f };
67 read_from_vertex_attribute_pointer(m_client_color_pointer, i, color);
68 gl_color(color[0], color[1], color[2], color[3]);
69 }
70
71 for (size_t t = 0; t < m_client_tex_coord_pointer.size(); ++t) {
72 if (m_client_side_texture_coord_array_enabled[t]) {
73 float tex_coords[4] { 0.f, 0.f, 0.f, 1.f };
74 read_from_vertex_attribute_pointer(m_client_tex_coord_pointer[t], i, tex_coords);
75 gl_multi_tex_coord(GL_TEXTURE0 + t, tex_coords[0], tex_coords[1], tex_coords[2], tex_coords[3]);
76 }
77 }
78
79 if (m_client_side_normal_array_enabled) {
80 float normal[3];
81 read_from_vertex_attribute_pointer(m_client_normal_pointer, i, normal);
82 gl_normal(normal[0], normal[1], normal[2]);
83 }
84
85 if (m_client_side_vertex_array_enabled) {
86 float vertex[4] { 0.f, 0.f, 0.f, 1.f };
87 read_from_vertex_attribute_pointer(m_client_vertex_pointer, i, vertex);
88 gl_vertex(vertex[0], vertex[1], vertex[2], vertex[3]);
89 }
90}
91
92void GLContext::gl_color(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
93{
94 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_color, r, g, b, a);
95
96 m_current_vertex_color = { r, g, b, a };
97}
98
99void GLContext::gl_color_pointer(GLint size, GLenum type, GLsizei stride, void const* pointer)
100{
101 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
102 RETURN_WITH_ERROR_IF(!(size == 3 || size == 4), GL_INVALID_VALUE);
103 RETURN_WITH_ERROR_IF(type != GL_BYTE
104 && type != GL_UNSIGNED_BYTE
105 && type != GL_SHORT
106 && type != GL_UNSIGNED_SHORT
107 && type != GL_INT
108 && type != GL_UNSIGNED_INT
109 && type != GL_FLOAT
110 && type != GL_DOUBLE,
111 GL_INVALID_ENUM);
112 RETURN_WITH_ERROR_IF(stride < 0, GL_INVALID_VALUE);
113
114 void const* data_pointer = pointer;
115 if (m_array_buffer) {
116 size_t data_offset = reinterpret_cast<size_t>(pointer);
117 data_pointer = m_array_buffer->offset_data(data_offset);
118 }
119 m_client_color_pointer = { .size = size, .type = type, .normalize = true, .stride = stride, .pointer = data_pointer };
120}
121
122void GLContext::gl_draw_arrays(GLenum mode, GLint first, GLsizei count)
123{
124 // NOTE: This always dereferences data; display list support is deferred to the
125 // individual vertex attribute calls such as `gl_color`, `gl_normal` etc.
126 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
127
128 // FIXME: Some modes are still missing (GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES)
129 RETURN_WITH_ERROR_IF(!(mode == GL_TRIANGLE_STRIP
130 || mode == GL_TRIANGLE_FAN
131 || mode == GL_TRIANGLES
132 || mode == GL_QUADS
133 || mode == GL_QUAD_STRIP
134 || mode == GL_POLYGON),
135 GL_INVALID_ENUM);
136
137 RETURN_WITH_ERROR_IF(count < 0, GL_INVALID_VALUE);
138
139 auto last = first + count;
140 gl_begin(mode);
141 for (int i = first; i < last; i++) {
142 if (m_client_side_color_array_enabled) {
143 float color[4] { 0.f, 0.f, 0.f, 1.f };
144 read_from_vertex_attribute_pointer(m_client_color_pointer, i, color);
145 gl_color(color[0], color[1], color[2], color[3]);
146 }
147
148 for (size_t t = 0; t < m_client_tex_coord_pointer.size(); ++t) {
149 if (m_client_side_texture_coord_array_enabled[t]) {
150 float tex_coords[4] { 0.f, 0.f, 0.f, 1.f };
151 read_from_vertex_attribute_pointer(m_client_tex_coord_pointer[t], i, tex_coords);
152 gl_multi_tex_coord(GL_TEXTURE0 + t, tex_coords[0], tex_coords[1], tex_coords[2], tex_coords[3]);
153 }
154 }
155
156 if (m_client_side_normal_array_enabled) {
157 float normal[3];
158 read_from_vertex_attribute_pointer(m_client_normal_pointer, i, normal);
159 gl_normal(normal[0], normal[1], normal[2]);
160 }
161
162 if (m_client_side_vertex_array_enabled) {
163 float vertex[4] { 0.f, 0.f, 0.f, 1.f };
164 read_from_vertex_attribute_pointer(m_client_vertex_pointer, i, vertex);
165 gl_vertex(vertex[0], vertex[1], vertex[2], vertex[3]);
166 }
167 }
168 gl_end();
169}
170
171void GLContext::gl_draw_elements(GLenum mode, GLsizei count, GLenum type, void const* indices)
172{
173 // NOTE: This always dereferences data; display list support is deferred to the
174 // individual vertex attribute calls such as `gl_color`, `gl_normal` etc.
175 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
176
177 // FIXME: Some modes are still missing (GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES)
178 RETURN_WITH_ERROR_IF(!(mode == GL_TRIANGLE_STRIP
179 || mode == GL_TRIANGLE_FAN
180 || mode == GL_TRIANGLES
181 || mode == GL_QUADS
182 || mode == GL_QUAD_STRIP
183 || mode == GL_POLYGON),
184 GL_INVALID_ENUM);
185
186 RETURN_WITH_ERROR_IF(!(type == GL_UNSIGNED_BYTE
187 || type == GL_UNSIGNED_SHORT
188 || type == GL_UNSIGNED_INT),
189 GL_INVALID_ENUM);
190
191 RETURN_WITH_ERROR_IF(count < 0, GL_INVALID_VALUE);
192
193 void const* index_data = indices;
194 if (m_element_array_buffer) {
195 size_t data_offset = reinterpret_cast<size_t>(indices);
196 index_data = m_element_array_buffer->offset_data(data_offset);
197 }
198
199 gl_begin(mode);
200 for (int index = 0; index < count; index++) {
201 int i = 0;
202 switch (type) {
203 case GL_UNSIGNED_BYTE:
204 i = reinterpret_cast<GLubyte const*>(index_data)[index];
205 break;
206 case GL_UNSIGNED_SHORT:
207 i = reinterpret_cast<GLushort const*>(index_data)[index];
208 break;
209 case GL_UNSIGNED_INT:
210 i = reinterpret_cast<GLuint const*>(index_data)[index];
211 break;
212 }
213
214 if (m_client_side_color_array_enabled) {
215 float color[4] { 0.f, 0.f, 0.f, 1.f };
216 read_from_vertex_attribute_pointer(m_client_color_pointer, i, color);
217 gl_color(color[0], color[1], color[2], color[3]);
218 }
219
220 for (size_t t = 0; t < m_client_tex_coord_pointer.size(); ++t) {
221 if (m_client_side_texture_coord_array_enabled[t]) {
222 float tex_coords[4] { 0.f, 0.f, 0.f, 1.f };
223 read_from_vertex_attribute_pointer(m_client_tex_coord_pointer[t], i, tex_coords);
224 gl_multi_tex_coord(GL_TEXTURE0 + t, tex_coords[0], tex_coords[1], tex_coords[2], tex_coords[3]);
225 }
226 }
227
228 if (m_client_side_normal_array_enabled) {
229 float normal[3];
230 read_from_vertex_attribute_pointer(m_client_normal_pointer, i, normal);
231 gl_normal(normal[0], normal[1], normal[2]);
232 }
233
234 if (m_client_side_vertex_array_enabled) {
235 float vertex[4] { 0.f, 0.f, 0.f, 1.f };
236 read_from_vertex_attribute_pointer(m_client_vertex_pointer, i, vertex);
237 gl_vertex(vertex[0], vertex[1], vertex[2], vertex[3]);
238 }
239 }
240 gl_end();
241}
242
243void GLContext::gl_normal(GLfloat nx, GLfloat ny, GLfloat nz)
244{
245 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_normal, nx, ny, nz);
246
247 m_current_vertex_normal = { nx, ny, nz };
248}
249
250void GLContext::gl_normal_pointer(GLenum type, GLsizei stride, void const* pointer)
251{
252 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
253 RETURN_WITH_ERROR_IF(type != GL_BYTE
254 && type != GL_SHORT
255 && type != GL_INT
256 && type != GL_FLOAT
257 && type != GL_DOUBLE,
258 GL_INVALID_ENUM);
259 RETURN_WITH_ERROR_IF(stride < 0, GL_INVALID_VALUE);
260
261 void const* data_pointer = pointer;
262 if (m_array_buffer) {
263 size_t data_offset = reinterpret_cast<size_t>(pointer);
264 data_pointer = m_array_buffer->offset_data(data_offset);
265 }
266 m_client_normal_pointer = { .size = 3, .type = type, .normalize = true, .stride = stride, .pointer = data_pointer };
267}
268
269void GLContext::gl_tex_coord_pointer(GLint size, GLenum type, GLsizei stride, void const* pointer)
270{
271 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
272 RETURN_WITH_ERROR_IF(!(size == 1 || size == 2 || size == 3 || size == 4), GL_INVALID_VALUE);
273 RETURN_WITH_ERROR_IF(!(type == GL_SHORT || type == GL_INT || type == GL_FLOAT || type == GL_DOUBLE), GL_INVALID_ENUM);
274 RETURN_WITH_ERROR_IF(stride < 0, GL_INVALID_VALUE);
275
276 auto& tex_coord_pointer = m_client_tex_coord_pointer[m_client_active_texture];
277
278 void const* data_pointer = pointer;
279 if (m_array_buffer) {
280 size_t data_offset = reinterpret_cast<size_t>(pointer);
281 data_pointer = m_array_buffer->offset_data(data_offset);
282 }
283 tex_coord_pointer = { .size = size, .type = type, .normalize = false, .stride = stride, .pointer = data_pointer };
284}
285
286void GLContext::gl_vertex(GLfloat x, GLfloat y, GLfloat z, GLfloat w)
287{
288 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_vertex, x, y, z, w);
289
290 GPU::Vertex vertex;
291
292 vertex.position = { x, y, z, w };
293 vertex.color = m_current_vertex_color;
294 for (size_t i = 0; i < m_device_info.num_texture_units; ++i)
295 vertex.tex_coords[i] = m_current_vertex_tex_coord[i];
296 vertex.normal = m_current_vertex_normal;
297
298 m_vertex_list.append(vertex);
299}
300
301void GLContext::gl_vertex_pointer(GLint size, GLenum type, GLsizei stride, void const* pointer)
302{
303 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
304 RETURN_WITH_ERROR_IF(!(size == 2 || size == 3 || size == 4), GL_INVALID_VALUE);
305 RETURN_WITH_ERROR_IF(!(type == GL_SHORT || type == GL_INT || type == GL_FLOAT || type == GL_DOUBLE), GL_INVALID_ENUM);
306 RETURN_WITH_ERROR_IF(stride < 0, GL_INVALID_VALUE);
307
308 void const* data_pointer = pointer;
309 if (m_array_buffer) {
310 size_t data_offset = reinterpret_cast<size_t>(pointer);
311 data_pointer = m_array_buffer->offset_data(data_offset);
312 }
313 m_client_vertex_pointer = { .size = size, .type = type, .normalize = false, .stride = stride, .pointer = data_pointer };
314}
315
316}