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 <LibGL/GLContext.h>
10#include <LibGL/Image.h>
11#include <LibGPU/ImageDataLayout.h>
12
13namespace GL {
14
15// Helper functions to handle type casting.
16static u16 max_texture_size(GPU::DeviceInfo const& device_info)
17{
18 return static_cast<u16>(device_info.max_texture_size);
19}
20
21static u8 log2_max_texture_size(GPU::DeviceInfo const& device_info)
22{
23 return static_cast<u8>(AK::log2(device_info.max_texture_size));
24}
25
26void GLContext::gl_active_texture(GLenum texture)
27{
28 RETURN_WITH_ERROR_IF(texture < GL_TEXTURE0 || texture >= GL_TEXTURE0 + m_device_info.num_texture_units, GL_INVALID_ENUM);
29
30 m_active_texture_unit_index = texture - GL_TEXTURE0;
31 m_active_texture_unit = &m_texture_units.at(m_active_texture_unit_index);
32
33 if (m_current_matrix_mode == GL_TEXTURE) {
34 m_current_matrix_stack = &m_active_texture_unit->texture_matrix_stack();
35 m_current_matrix = &m_current_matrix_stack->last();
36 }
37}
38
39void GLContext::gl_bind_texture(GLenum target, GLuint texture)
40{
41 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
42 RETURN_WITH_ERROR_IF(target != GL_TEXTURE_1D
43 && target != GL_TEXTURE_2D
44 && target != GL_TEXTURE_3D
45 && target != GL_TEXTURE_1D_ARRAY
46 && target != GL_TEXTURE_2D_ARRAY
47 && target != GL_TEXTURE_CUBE_MAP,
48 GL_INVALID_ENUM);
49
50 // FIXME: We only support GL_TEXTURE_2D for now
51 if (target != GL_TEXTURE_2D) {
52 dbgln("gl_bind_texture(target = {:#x}): currently only GL_TEXTURE_2D is supported", target);
53 return;
54 }
55
56 RefPtr<Texture2D> texture_2d;
57
58 if (texture == 0) {
59 // Texture name 0 refers to the default texture
60 texture_2d = get_default_texture<Texture2D>(target);
61 } else {
62 // Find this texture name in our previously allocated textures
63 auto it = m_allocated_textures.find(texture);
64 if (it != m_allocated_textures.end()) {
65 auto texture_object = it->value;
66 if (!texture_object.is_null()) {
67 // Texture must have been created with the same target
68 RETURN_WITH_ERROR_IF(!texture_object->is_texture_2d(), GL_INVALID_OPERATION);
69 texture_2d = static_cast<Texture2D*>(texture_object.ptr());
70 }
71 }
72
73 // OpenGL 1.x supports binding texture names that were not previously generated by glGenTextures.
74 // If there is not an allocated texture, meaning it was not previously generated by glGenTextures,
75 // we can keep texture_object null to both allocate and bind the texture with the passed in texture name.
76 // FIXME: Later OpenGL versions such as 4.x enforce that texture names being bound were previously generated
77 // by glGenTextures.
78 if (!texture_2d) {
79 texture_2d = adopt_ref(*new Texture2D());
80 m_allocated_textures.set(texture, texture_2d);
81 }
82 }
83
84 m_active_texture_unit->set_texture_2d_target_texture(texture_2d);
85 m_sampler_config_is_dirty = true;
86}
87
88void GLContext::gl_client_active_texture(GLenum target)
89{
90 RETURN_WITH_ERROR_IF(target < GL_TEXTURE0 || target >= GL_TEXTURE0 + m_device_info.num_texture_units, GL_INVALID_ENUM);
91
92 m_client_active_texture = target - GL_TEXTURE0;
93}
94
95void GLContext::gl_copy_tex_image_2d(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border)
96{
97 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_copy_tex_image_2d, target, level, internalformat, x, y, width, height, border);
98 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
99
100 RETURN_WITH_ERROR_IF(internalformat == GL_NONE, GL_INVALID_ENUM);
101 auto pixel_type_or_error = get_validated_pixel_type(target, internalformat, GL_NONE, GL_NONE);
102 RETURN_WITH_ERROR_IF(pixel_type_or_error.is_error(), pixel_type_or_error.release_error().code());
103
104 RETURN_WITH_ERROR_IF(level < 0 || level > log2_max_texture_size(m_device_info), GL_INVALID_VALUE);
105 RETURN_WITH_ERROR_IF(width < 0 || height < 0 || width > (2 + max_texture_size(m_device_info)) || height > (2 + max_texture_size(m_device_info)), GL_INVALID_VALUE);
106 if (!m_device_info.supports_npot_textures)
107 RETURN_WITH_ERROR_IF(!is_power_of_two(width) || !is_power_of_two(height), GL_INVALID_VALUE);
108 RETURN_WITH_ERROR_IF(border != 0, GL_INVALID_VALUE);
109
110 auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
111 VERIFY(!texture_2d.is_null());
112
113 auto internal_pixel_format = pixel_format_for_internal_format(internalformat);
114 if (level == 0) {
115 texture_2d->set_device_image(m_rasterizer->create_image(internal_pixel_format, width, height, 1, log2_max_texture_size(m_device_info)));
116 m_sampler_config_is_dirty = true;
117 }
118
119 auto pixel_type = pixel_type_or_error.release_value();
120 if (pixel_type.format == GPU::PixelFormat::DepthComponent) {
121 m_rasterizer->blit_from_depth_buffer(
122 *texture_2d->device_image(),
123 level,
124 { static_cast<u32>(width), static_cast<u32>(height) },
125 { x, y },
126 { 0, 0, 0 });
127 } else if (pixel_type.format == GPU::PixelFormat::StencilIndex) {
128 dbgln("{}: GL_STENCIL_INDEX is not yet supported", __FUNCTION__);
129 } else {
130 m_rasterizer->blit_from_color_buffer(
131 *texture_2d->device_image(),
132 level,
133 { static_cast<u32>(width), static_cast<u32>(height) },
134 { x, y },
135 { 0, 0, 0 });
136 }
137}
138
139void GLContext::gl_copy_tex_sub_image_2d(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height)
140{
141 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_copy_tex_sub_image_2d, target, level, xoffset, yoffset, x, y, width, height);
142 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
143
144 RETURN_WITH_ERROR_IF(level < 0 || level > log2_max_texture_size(m_device_info), GL_INVALID_VALUE);
145 RETURN_WITH_ERROR_IF(width < 0 || height < 0 || width > (2 + max_texture_size(m_device_info)) || height > (2 + max_texture_size(m_device_info)), GL_INVALID_VALUE);
146
147 auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
148 VERIFY(!texture_2d.is_null());
149 RETURN_WITH_ERROR_IF(texture_2d->device_image().is_null(), GL_INVALID_OPERATION);
150
151 m_rasterizer->blit_from_color_buffer(
152 *texture_2d->device_image(),
153 level,
154 { static_cast<u32>(width), static_cast<u32>(height) },
155 { x, y },
156 { xoffset, yoffset, 0 });
157
158 // FIXME: use GPU::PixelFormat for Texture2D's internal format
159 if (texture_2d->internal_format() == GL_DEPTH_COMPONENT) {
160 m_rasterizer->blit_from_depth_buffer(
161 *texture_2d->device_image(),
162 level,
163 { static_cast<u32>(width), static_cast<u32>(height) },
164 { x, y },
165 { 0, 0, 0 });
166 } else if (texture_2d->internal_format() == GL_STENCIL_INDEX) {
167 dbgln("{}: GL_STENCIL_INDEX is not yet supported", __FUNCTION__);
168 } else {
169 m_rasterizer->blit_from_color_buffer(
170 *texture_2d->device_image(),
171 level,
172 { static_cast<u32>(width), static_cast<u32>(height) },
173 { x, y },
174 { 0, 0, 0 });
175 }
176}
177
178void GLContext::gl_delete_textures(GLsizei n, GLuint const* textures)
179{
180 RETURN_WITH_ERROR_IF(n < 0, GL_INVALID_VALUE);
181 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
182
183 for (auto i = 0; i < n; i++) {
184 GLuint name = textures[i];
185 if (name == 0)
186 continue;
187
188 auto texture_object = m_allocated_textures.find(name);
189 if (texture_object == m_allocated_textures.end() || texture_object->value.is_null())
190 continue;
191
192 m_texture_name_allocator.free(name);
193
194 auto texture = texture_object->value;
195
196 // Check all texture units
197 for (auto& texture_unit : m_texture_units) {
198 if (texture->is_texture_2d() && texture_unit.texture_2d_target_texture() == texture) {
199 // If a texture that is currently bound is deleted, the binding reverts to 0 (the default texture)
200 texture_unit.set_texture_2d_target_texture(get_default_texture<Texture2D>(GL_TEXTURE_2D));
201 }
202 }
203
204 m_allocated_textures.remove(name);
205 }
206}
207
208void GLContext::gl_gen_textures(GLsizei n, GLuint* textures)
209{
210 RETURN_WITH_ERROR_IF(n < 0, GL_INVALID_VALUE);
211 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
212
213 m_texture_name_allocator.allocate(n, textures);
214
215 // Initialize all texture names with a nullptr
216 for (auto i = 0; i < n; ++i) {
217 GLuint name = textures[i];
218 m_allocated_textures.set(name, nullptr);
219 }
220}
221
222void GLContext::gl_get_tex_image(GLenum target, GLint level, GLenum format, GLenum type, void* pixels)
223{
224 RETURN_WITH_ERROR_IF(level < 0 || level > log2_max_texture_size(m_device_info), GL_INVALID_VALUE);
225 RETURN_WITH_ERROR_IF(format == GL_NONE || type == GL_NONE, GL_INVALID_ENUM);
226 auto pixel_type_or_error = get_validated_pixel_type(target, GL_NONE, format, type);
227 RETURN_WITH_ERROR_IF(pixel_type_or_error.is_error(), pixel_type_or_error.release_error().code());
228
229 auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
230 VERIFY(!texture_2d.is_null());
231
232 u32 width = texture_2d->width_at_lod(level);
233 u32 height = texture_2d->height_at_lod(level);
234
235 GPU::ImageDataLayout output_layout = {
236 .pixel_type = pixel_type_or_error.release_value(),
237 .packing = get_packing_specification(PackingType::Pack),
238 .dimensions = {
239 .width = width,
240 .height = height,
241 .depth = 1,
242 },
243 .selection = {
244 .width = width,
245 .height = height,
246 .depth = 1,
247 },
248 };
249
250 texture_2d->download_texture_data(level, output_layout, pixels);
251}
252
253void GLContext::gl_get_tex_parameter_integerv(GLenum target, GLint level, GLenum pname, GLint* params)
254{
255 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
256 // FIXME: support targets other than GL_TEXTURE_2D
257 RETURN_WITH_ERROR_IF(target != GL_TEXTURE_2D, GL_INVALID_ENUM);
258 // FIXME: support other parameter names
259 RETURN_WITH_ERROR_IF(pname < GL_TEXTURE_WIDTH || pname > GL_TEXTURE_HEIGHT, GL_INVALID_ENUM);
260 RETURN_WITH_ERROR_IF(level < 0 || level > log2_max_texture_size(m_device_info), GL_INVALID_VALUE);
261 // FIXME: GL_INVALID_VALUE is generated if target is GL_TEXTURE_BUFFER and level is not zero
262 // FIXME: GL_INVALID_OPERATION is generated if GL_TEXTURE_COMPRESSED_IMAGE_SIZE is queried on texture images with an uncompressed internal format or on proxy targets
263
264 VERIFY(!m_active_texture_unit->texture_2d_target_texture().is_null());
265 auto const texture_2d = m_active_texture_unit->texture_2d_target_texture();
266
267 switch (pname) {
268 case GL_TEXTURE_HEIGHT:
269 *params = texture_2d->height_at_lod(level);
270 break;
271 case GL_TEXTURE_WIDTH:
272 *params = texture_2d->width_at_lod(level);
273 break;
274 }
275}
276
277GLboolean GLContext::gl_is_texture(GLuint texture)
278{
279 RETURN_VALUE_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION, GL_FALSE);
280
281 if (texture == 0)
282 return GL_FALSE;
283
284 auto it = m_allocated_textures.find(texture);
285 if (it == m_allocated_textures.end())
286 return GL_FALSE;
287
288 return it->value.is_null() ? GL_FALSE : GL_TRUE;
289}
290
291void GLContext::gl_multi_tex_coord(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q)
292{
293 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_multi_tex_coord, target, s, t, r, q);
294
295 RETURN_WITH_ERROR_IF(target < GL_TEXTURE0 || target >= GL_TEXTURE0 + m_device_info.num_texture_units, GL_INVALID_ENUM);
296
297 m_current_vertex_tex_coord[target - GL_TEXTURE0] = { s, t, r, q };
298}
299
300void GLContext::gl_tex_coord(GLfloat s, GLfloat t, GLfloat r, GLfloat q)
301{
302 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_tex_coord, s, t, r, q);
303
304 m_current_vertex_tex_coord[0] = { s, t, r, q };
305}
306
307void GLContext::gl_tex_env(GLenum target, GLenum pname, FloatVector4 params)
308{
309 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_tex_env, target, pname, params);
310 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
311
312 RETURN_WITH_ERROR_IF(target != GL_TEXTURE_ENV && target != GL_TEXTURE_FILTER_CONTROL, GL_INVALID_ENUM);
313 RETURN_WITH_ERROR_IF(target == GL_TEXTURE_FILTER_CONTROL && pname != GL_TEXTURE_LOD_BIAS, GL_INVALID_ENUM);
314
315 auto const param = params[0];
316 switch (target) {
317 case GL_TEXTURE_ENV:
318 switch (pname) {
319 case GL_ALPHA_SCALE:
320 RETURN_WITH_ERROR_IF(param != 1.f && param != 2.f && param != 4.f, GL_INVALID_VALUE);
321 m_active_texture_unit->set_alpha_scale(param);
322 break;
323 case GL_COMBINE_ALPHA: {
324 auto param_enum = static_cast<GLenum>(param);
325 switch (param_enum) {
326 case GL_ADD:
327 case GL_ADD_SIGNED:
328 case GL_INTERPOLATE:
329 case GL_MODULATE:
330 case GL_REPLACE:
331 case GL_SUBTRACT:
332 m_active_texture_unit->set_alpha_combinator(param_enum);
333 break;
334 default:
335 RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
336 }
337 break;
338 }
339 case GL_COMBINE_RGB: {
340 auto param_enum = static_cast<GLenum>(param);
341 switch (param_enum) {
342 case GL_ADD:
343 case GL_ADD_SIGNED:
344 case GL_DOT3_RGB:
345 case GL_DOT3_RGBA:
346 case GL_INTERPOLATE:
347 case GL_MODULATE:
348 case GL_REPLACE:
349 case GL_SUBTRACT:
350 m_active_texture_unit->set_rgb_combinator(param_enum);
351 break;
352 default:
353 RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
354 }
355 break;
356 }
357 case GL_OPERAND0_ALPHA:
358 case GL_OPERAND1_ALPHA:
359 case GL_OPERAND2_ALPHA: {
360 auto param_enum = static_cast<GLenum>(param);
361 switch (param_enum) {
362 case GL_ONE_MINUS_SRC_ALPHA:
363 case GL_SRC_ALPHA:
364 m_active_texture_unit->set_alpha_operand(pname - GL_OPERAND0_ALPHA, param_enum);
365 break;
366 default:
367 RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
368 }
369 break;
370 }
371 case GL_OPERAND0_RGB:
372 case GL_OPERAND1_RGB:
373 case GL_OPERAND2_RGB: {
374 auto param_enum = static_cast<GLenum>(param);
375 switch (param_enum) {
376 case GL_ONE_MINUS_SRC_ALPHA:
377 case GL_ONE_MINUS_SRC_COLOR:
378 case GL_SRC_ALPHA:
379 case GL_SRC_COLOR:
380 m_active_texture_unit->set_rgb_operand(pname - GL_OPERAND0_RGB, param_enum);
381 break;
382 default:
383 RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
384 }
385 break;
386 }
387 case GL_RGB_SCALE:
388 RETURN_WITH_ERROR_IF(param != 1.f && param != 2.f && param != 4.f, GL_INVALID_VALUE);
389 m_active_texture_unit->set_rgb_scale(param);
390 break;
391 case GL_SRC0_ALPHA:
392 case GL_SRC1_ALPHA:
393 case GL_SRC2_ALPHA: {
394 auto param_enum = static_cast<GLenum>(param);
395 switch (param_enum) {
396 case GL_CONSTANT:
397 case GL_PREVIOUS:
398 case GL_PRIMARY_COLOR:
399 case GL_TEXTURE:
400 case GL_TEXTURE0 ... GL_TEXTURE31:
401 m_active_texture_unit->set_alpha_source(pname - GL_SRC0_ALPHA, param_enum);
402 break;
403 default:
404 RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
405 }
406 break;
407 }
408 case GL_SRC0_RGB:
409 case GL_SRC1_RGB:
410 case GL_SRC2_RGB: {
411 auto param_enum = static_cast<GLenum>(param);
412 switch (param_enum) {
413 case GL_CONSTANT:
414 case GL_PREVIOUS:
415 case GL_PRIMARY_COLOR:
416 case GL_TEXTURE:
417 case GL_TEXTURE0 ... GL_TEXTURE31:
418 m_active_texture_unit->set_rgb_source(pname - GL_SRC0_RGB, param_enum);
419 break;
420 default:
421 RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
422 }
423 break;
424 }
425 case GL_TEXTURE_ENV_COLOR:
426 m_active_texture_unit->set_color(params);
427 break;
428 case GL_TEXTURE_ENV_MODE: {
429 auto param_enum = static_cast<GLenum>(param);
430 switch (param_enum) {
431 case GL_ADD:
432 case GL_BLEND:
433 case GL_COMBINE:
434 case GL_DECAL:
435 case GL_MODULATE:
436 case GL_REPLACE:
437 m_active_texture_unit->set_env_mode(param_enum);
438 break;
439 default:
440 RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
441 }
442 break;
443 }
444 default:
445 RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
446 }
447 break;
448 case GL_TEXTURE_FILTER_CONTROL:
449 switch (pname) {
450 case GL_TEXTURE_LOD_BIAS:
451 m_active_texture_unit->set_level_of_detail_bias(param);
452 break;
453 default:
454 VERIFY_NOT_REACHED();
455 }
456 break;
457 default:
458 VERIFY_NOT_REACHED();
459 }
460
461 m_sampler_config_is_dirty = true;
462}
463
464void GLContext::gl_tex_envv(GLenum target, GLenum pname, void const* params, GLenum type)
465{
466 VERIFY(type == GL_FLOAT || type == GL_INT);
467
468 auto parameters_to_vector = [&]<typename T>(T const* params) -> FloatVector4 {
469 auto parameters = (target == GL_TEXTURE_ENV && pname == GL_TEXTURE_ENV_COLOR)
470 ? Vector4<T> { params[0], params[1], params[2], params[3] }
471 : Vector4<T> { params[0], 0, 0, 0 };
472 return parameters.template to_type<float>();
473 };
474
475 auto tex_env_parameters = (type == GL_FLOAT)
476 ? parameters_to_vector(reinterpret_cast<GLfloat const*>(params))
477 : parameters_to_vector(reinterpret_cast<GLint const*>(params));
478
479 // Normalize integers to -1..1
480 if (target == GL_TEXTURE_ENV && pname == GL_TEXTURE_ENV_COLOR && type == GL_INT)
481 tex_env_parameters = (tex_env_parameters + 2147483648.f) / 2147483647.5f - 1.f;
482
483 gl_tex_env(target, pname, tex_env_parameters);
484}
485
486void GLContext::gl_tex_gen(GLenum coord, GLenum pname, GLint param)
487{
488 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_tex_gen, coord, pname, param);
489 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
490
491 RETURN_WITH_ERROR_IF(coord < GL_S || coord > GL_Q, GL_INVALID_ENUM);
492 RETURN_WITH_ERROR_IF(pname != GL_TEXTURE_GEN_MODE, GL_INVALID_ENUM);
493 RETURN_WITH_ERROR_IF(param != GL_EYE_LINEAR
494 && param != GL_OBJECT_LINEAR
495 && param != GL_SPHERE_MAP
496 && param != GL_NORMAL_MAP
497 && param != GL_REFLECTION_MAP,
498 GL_INVALID_ENUM);
499 RETURN_WITH_ERROR_IF((coord == GL_R || coord == GL_Q) && param == GL_SPHERE_MAP, GL_INVALID_ENUM);
500 RETURN_WITH_ERROR_IF(coord == GL_Q && (param == GL_REFLECTION_MAP || param == GL_NORMAL_MAP), GL_INVALID_ENUM);
501
502 GLenum const capability = GL_TEXTURE_GEN_S + (coord - GL_S);
503 texture_coordinate_generation(m_active_texture_unit_index, capability).generation_mode = param;
504 m_texture_units_dirty = true;
505}
506
507void GLContext::gl_tex_gen_floatv(GLenum coord, GLenum pname, GLfloat const* params)
508{
509 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_tex_gen_floatv, coord, pname, params);
510 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
511
512 RETURN_WITH_ERROR_IF(coord < GL_S || coord > GL_Q, GL_INVALID_ENUM);
513 RETURN_WITH_ERROR_IF(pname != GL_TEXTURE_GEN_MODE
514 && pname != GL_OBJECT_PLANE
515 && pname != GL_EYE_PLANE,
516 GL_INVALID_ENUM);
517
518 GLenum const capability = GL_TEXTURE_GEN_S + (coord - GL_S);
519
520 switch (pname) {
521 case GL_TEXTURE_GEN_MODE: {
522 auto param = static_cast<GLenum>(params[0]);
523 RETURN_WITH_ERROR_IF(param != GL_EYE_LINEAR
524 && param != GL_OBJECT_LINEAR
525 && param != GL_SPHERE_MAP
526 && param != GL_NORMAL_MAP
527 && param != GL_REFLECTION_MAP,
528 GL_INVALID_ENUM);
529 RETURN_WITH_ERROR_IF((coord == GL_R || coord == GL_Q) && param == GL_SPHERE_MAP, GL_INVALID_ENUM);
530 RETURN_WITH_ERROR_IF(coord == GL_Q && (param == GL_REFLECTION_MAP || param == GL_NORMAL_MAP), GL_INVALID_ENUM);
531
532 texture_coordinate_generation(m_active_texture_unit_index, capability).generation_mode = param;
533 break;
534 }
535 case GL_OBJECT_PLANE:
536 texture_coordinate_generation(m_active_texture_unit_index, capability).object_plane_coefficients = { params[0], params[1], params[2], params[3] };
537 break;
538 case GL_EYE_PLANE: {
539 auto const& inverse_model_view = model_view_matrix().inverse();
540 auto input_coefficients = FloatVector4 { params[0], params[1], params[2], params[3] };
541
542 // Note: we are allowed to store transformed coefficients here, according to the documentation on
543 // `glGetTexGen`:
544 //
545 // "The returned values are those maintained in eye coordinates. They are not equal to the values
546 // specified using glTexGen, unless the modelview matrix was identity when glTexGen was called."
547
548 texture_coordinate_generation(m_active_texture_unit_index, capability).eye_plane_coefficients = inverse_model_view * input_coefficients;
549 break;
550 }
551 default:
552 VERIFY_NOT_REACHED();
553 }
554
555 m_texture_units_dirty = true;
556}
557
558void GLContext::gl_tex_image_2d(GLenum target, GLint level, GLint internal_format, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, GLvoid const* data)
559{
560 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
561
562 RETURN_WITH_ERROR_IF(internal_format == GL_NONE || format == GL_NONE || type == GL_NONE, GL_INVALID_ENUM);
563 auto pixel_type_or_error = get_validated_pixel_type(target, internal_format, format, type);
564 RETURN_WITH_ERROR_IF(pixel_type_or_error.is_error(), pixel_type_or_error.release_error().code());
565
566 RETURN_WITH_ERROR_IF(level < 0 || level > log2_max_texture_size(m_device_info), GL_INVALID_VALUE);
567 RETURN_WITH_ERROR_IF(width < 0 || height < 0 || width > (2 + max_texture_size(m_device_info)) || height > (2 + max_texture_size(m_device_info)), GL_INVALID_VALUE);
568 if (!m_device_info.supports_npot_textures)
569 RETURN_WITH_ERROR_IF(!is_power_of_two(width) || !is_power_of_two(height), GL_INVALID_VALUE);
570 RETURN_WITH_ERROR_IF(border != 0, GL_INVALID_VALUE);
571
572 auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
573 VERIFY(!texture_2d.is_null());
574
575 if (level == 0) {
576 // FIXME: OpenGL has the concept of texture and mipmap completeness. A texture has to fulfill certain criteria to be considered complete.
577 // Trying to render while an incomplete texture is bound will result in an error.
578 // Here we simply create a complete device image when mipmap level 0 is attached to the texture object. This has the unfortunate side effect
579 // that constructing GL textures in any but the default mipmap order, going from level 0 upwards will cause mip levels to stay uninitialized.
580 // To be spec compliant we should create the device image once the texture has become complete and is used for rendering the first time.
581 // All images that were attached before the device image was created need to be stored somewhere to be used to initialize the device image once complete.
582 auto internal_pixel_format = pixel_format_for_internal_format(internal_format);
583 texture_2d->set_device_image(m_rasterizer->create_image(internal_pixel_format, width, height, 1, log2_max_texture_size(m_device_info)));
584 m_sampler_config_is_dirty = true;
585 }
586
587 GPU::ImageDataLayout input_layout = {
588 .pixel_type = pixel_type_or_error.release_value(),
589 .packing = get_packing_specification(PackingType::Unpack),
590 .dimensions = {
591 .width = static_cast<u32>(width),
592 .height = static_cast<u32>(height),
593 .depth = 1,
594 },
595 .selection = {
596 .width = static_cast<u32>(width),
597 .height = static_cast<u32>(height),
598 .depth = 1,
599 },
600 };
601
602 texture_2d->upload_texture_data(level, internal_format, input_layout, data);
603}
604
605void GLContext::gl_tex_parameter(GLenum target, GLenum pname, GLfloat param)
606{
607 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_tex_parameter, target, pname, param);
608
609 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
610
611 // FIXME: We currently only support GL_TETXURE_2D targets. 1D, 3D and CUBE should also be supported (https://docs.gl/gl2/glTexParameter)
612 RETURN_WITH_ERROR_IF(target != GL_TEXTURE_2D, GL_INVALID_ENUM);
613
614 // FIXME: implement the remaining parameters. (https://docs.gl/gl2/glTexParameter)
615 RETURN_WITH_ERROR_IF(pname != GL_GENERATE_MIPMAP
616 && pname != GL_TEXTURE_LOD_BIAS
617 && pname != GL_TEXTURE_MIN_FILTER
618 && pname != GL_TEXTURE_MAG_FILTER
619 && pname != GL_TEXTURE_WRAP_S
620 && pname != GL_TEXTURE_WRAP_T,
621 GL_INVALID_ENUM);
622
623 // We assume GL_TEXTURE_2D (see above)
624 auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
625 VERIFY(!texture_2d.is_null());
626
627 switch (pname) {
628 case GL_GENERATE_MIPMAP:
629 RETURN_WITH_ERROR_IF(param != GL_TRUE && param != GL_FALSE, GL_INVALID_ENUM);
630 texture_2d->set_generate_mipmaps(param == GL_TRUE);
631 break;
632 case GL_TEXTURE_LOD_BIAS:
633 texture_2d->set_level_of_detail_bias(param);
634 break;
635 case GL_TEXTURE_MIN_FILTER:
636 RETURN_WITH_ERROR_IF(!(param == GL_NEAREST
637 || param == GL_LINEAR
638 || param == GL_NEAREST_MIPMAP_NEAREST
639 || param == GL_LINEAR_MIPMAP_NEAREST
640 || param == GL_NEAREST_MIPMAP_LINEAR
641 || param == GL_LINEAR_MIPMAP_LINEAR),
642 GL_INVALID_ENUM);
643
644 texture_2d->sampler().set_min_filter(param);
645 break;
646
647 case GL_TEXTURE_MAG_FILTER:
648 RETURN_WITH_ERROR_IF(!(param == GL_NEAREST
649 || param == GL_LINEAR),
650 GL_INVALID_ENUM);
651
652 texture_2d->sampler().set_mag_filter(param);
653 break;
654
655 case GL_TEXTURE_WRAP_S:
656 RETURN_WITH_ERROR_IF(!(param == GL_CLAMP
657 || param == GL_CLAMP_TO_BORDER
658 || param == GL_CLAMP_TO_EDGE
659 || param == GL_MIRRORED_REPEAT
660 || param == GL_REPEAT),
661 GL_INVALID_ENUM);
662
663 texture_2d->sampler().set_wrap_s_mode(param);
664 break;
665
666 case GL_TEXTURE_WRAP_T:
667 RETURN_WITH_ERROR_IF(!(param == GL_CLAMP
668 || param == GL_CLAMP_TO_BORDER
669 || param == GL_CLAMP_TO_EDGE
670 || param == GL_MIRRORED_REPEAT
671 || param == GL_REPEAT),
672 GL_INVALID_ENUM);
673
674 texture_2d->sampler().set_wrap_t_mode(param);
675 break;
676
677 default:
678 VERIFY_NOT_REACHED();
679 }
680
681 m_sampler_config_is_dirty = true;
682}
683
684void GLContext::gl_tex_parameterfv(GLenum target, GLenum pname, GLfloat const* params)
685{
686 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_tex_parameterfv, target, pname, params);
687
688 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
689
690 // FIXME: We currently only support GL_TETXURE_2D targets. 1D, 3D and CUBE should also be supported (https://docs.gl/gl2/glTexParameter)
691 RETURN_WITH_ERROR_IF(target != GL_TEXTURE_2D, GL_INVALID_ENUM);
692
693 // FIXME: implement the remaining parameters. (https://docs.gl/gl2/glTexParameter)
694 RETURN_WITH_ERROR_IF(pname != GL_TEXTURE_BORDER_COLOR, GL_INVALID_ENUM);
695
696 // We assume GL_TEXTURE_2D (see above)
697 auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
698 RETURN_WITH_ERROR_IF(texture_2d.is_null(), GL_INVALID_OPERATION);
699
700 switch (pname) {
701 case GL_TEXTURE_BORDER_COLOR:
702 texture_2d->sampler().set_border_color(params[0], params[1], params[2], params[3]);
703 break;
704 default:
705 VERIFY_NOT_REACHED();
706 }
707
708 m_sampler_config_is_dirty = true;
709}
710
711void GLContext::gl_tex_sub_image_2d(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid const* data)
712{
713 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
714
715 // We only support symbolic constants for now
716 RETURN_WITH_ERROR_IF(level < 0 || level > log2_max_texture_size(m_device_info), GL_INVALID_VALUE);
717 RETURN_WITH_ERROR_IF(width < 0 || height < 0 || width > (2 + max_texture_size(m_device_info)) || height > (2 + max_texture_size(m_device_info)), GL_INVALID_VALUE);
718
719 // A 2D texture array must have been defined by a previous glTexImage2D operation
720 auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
721 VERIFY(!texture_2d.is_null());
722 RETURN_WITH_ERROR_IF(texture_2d->device_image().is_null(), GL_INVALID_OPERATION);
723
724 RETURN_WITH_ERROR_IF(format == GL_NONE || type == GL_NONE, GL_INVALID_ENUM);
725 auto pixel_type_or_error = get_validated_pixel_type(target, texture_2d->internal_format(), format, type);
726 RETURN_WITH_ERROR_IF(pixel_type_or_error.is_error(), pixel_type_or_error.release_error().code());
727
728 RETURN_WITH_ERROR_IF(xoffset < 0 || yoffset < 0 || xoffset + width > texture_2d->width_at_lod(level) || yoffset + height > texture_2d->height_at_lod(level), GL_INVALID_VALUE);
729
730 GPU::ImageDataLayout input_layout = {
731 .pixel_type = pixel_type_or_error.release_value(),
732 .packing = get_packing_specification(PackingType::Unpack),
733 .dimensions = {
734 .width = static_cast<u32>(width),
735 .height = static_cast<u32>(height),
736 .depth = 1,
737 },
738 .selection = {
739 .width = static_cast<u32>(width),
740 .height = static_cast<u32>(height),
741 .depth = 1,
742 },
743 };
744
745 texture_2d->replace_sub_texture_data(level, input_layout, { xoffset, yoffset, 0 }, data);
746}
747
748void GLContext::sync_device_sampler_config()
749{
750 if (!m_sampler_config_is_dirty)
751 return;
752
753 m_sampler_config_is_dirty = false;
754
755 for (unsigned i = 0; i < m_texture_units.size(); ++i) {
756 auto const& texture_unit = m_texture_units[i];
757 if (!texture_unit.texture_2d_enabled())
758 continue;
759
760 GPU::SamplerConfig config;
761
762 auto texture_2d = texture_unit.texture_2d_target_texture();
763 VERIFY(!texture_2d.is_null());
764 config.bound_image = texture_2d->device_image();
765 config.level_of_detail_bias = texture_2d->level_of_detail_bias() + texture_unit.level_of_detail_bias();
766
767 auto const& sampler = texture_2d->sampler();
768
769 switch (sampler.min_filter()) {
770 case GL_NEAREST:
771 config.texture_min_filter = GPU::TextureFilter::Nearest;
772 config.mipmap_filter = GPU::MipMapFilter::None;
773 break;
774 case GL_LINEAR:
775 config.texture_min_filter = GPU::TextureFilter::Linear;
776 config.mipmap_filter = GPU::MipMapFilter::None;
777 break;
778 case GL_NEAREST_MIPMAP_NEAREST:
779 config.texture_min_filter = GPU::TextureFilter::Nearest;
780 config.mipmap_filter = GPU::MipMapFilter::Nearest;
781 break;
782 case GL_LINEAR_MIPMAP_NEAREST:
783 config.texture_min_filter = GPU::TextureFilter::Linear;
784 config.mipmap_filter = GPU::MipMapFilter::Nearest;
785 break;
786 case GL_NEAREST_MIPMAP_LINEAR:
787 config.texture_min_filter = GPU::TextureFilter::Nearest;
788 config.mipmap_filter = GPU::MipMapFilter::Linear;
789 break;
790 case GL_LINEAR_MIPMAP_LINEAR:
791 config.texture_min_filter = GPU::TextureFilter::Linear;
792 config.mipmap_filter = GPU::MipMapFilter::Linear;
793 break;
794 default:
795 VERIFY_NOT_REACHED();
796 }
797
798 switch (sampler.mag_filter()) {
799 case GL_NEAREST:
800 config.texture_mag_filter = GPU::TextureFilter::Nearest;
801 break;
802 case GL_LINEAR:
803 config.texture_mag_filter = GPU::TextureFilter::Linear;
804 break;
805 default:
806 VERIFY_NOT_REACHED();
807 }
808
809 switch (sampler.wrap_s_mode()) {
810 case GL_CLAMP:
811 config.texture_wrap_u = GPU::TextureWrapMode::Clamp;
812 break;
813 case GL_CLAMP_TO_BORDER:
814 config.texture_wrap_u = GPU::TextureWrapMode::ClampToBorder;
815 break;
816 case GL_CLAMP_TO_EDGE:
817 config.texture_wrap_u = GPU::TextureWrapMode::ClampToEdge;
818 break;
819 case GL_REPEAT:
820 config.texture_wrap_u = GPU::TextureWrapMode::Repeat;
821 break;
822 case GL_MIRRORED_REPEAT:
823 config.texture_wrap_u = GPU::TextureWrapMode::MirroredRepeat;
824 break;
825 default:
826 VERIFY_NOT_REACHED();
827 }
828
829 switch (sampler.wrap_t_mode()) {
830 case GL_CLAMP:
831 config.texture_wrap_v = GPU::TextureWrapMode::Clamp;
832 break;
833 case GL_CLAMP_TO_BORDER:
834 config.texture_wrap_v = GPU::TextureWrapMode::ClampToBorder;
835 break;
836 case GL_CLAMP_TO_EDGE:
837 config.texture_wrap_v = GPU::TextureWrapMode::ClampToEdge;
838 break;
839 case GL_REPEAT:
840 config.texture_wrap_v = GPU::TextureWrapMode::Repeat;
841 break;
842 case GL_MIRRORED_REPEAT:
843 config.texture_wrap_v = GPU::TextureWrapMode::MirroredRepeat;
844 break;
845 default:
846 VERIFY_NOT_REACHED();
847 }
848
849 auto& fixed_function_env = config.fixed_function_texture_environment;
850 fixed_function_env.color = texture_unit.color();
851
852 auto get_env_mode = [](GLenum mode) {
853 switch (mode) {
854 case GL_ADD:
855 return GPU::TextureEnvMode::Add;
856 case GL_BLEND:
857 return GPU::TextureEnvMode::Blend;
858 case GL_COMBINE:
859 return GPU::TextureEnvMode::Combine;
860 case GL_DECAL:
861 return GPU::TextureEnvMode::Decal;
862 case GL_MODULATE:
863 return GPU::TextureEnvMode::Modulate;
864 case GL_REPLACE:
865 return GPU::TextureEnvMode::Replace;
866 default:
867 VERIFY_NOT_REACHED();
868 }
869 };
870 fixed_function_env.env_mode = get_env_mode(texture_unit.env_mode());
871
872 fixed_function_env.alpha_scale = texture_unit.alpha_scale();
873 fixed_function_env.rgb_scale = texture_unit.rgb_scale();
874
875 auto get_combinator = [](GLenum combinator) {
876 switch (combinator) {
877 case GL_ADD:
878 return GPU::TextureCombinator::Add;
879 case GL_ADD_SIGNED:
880 return GPU::TextureCombinator::AddSigned;
881 case GL_DOT3_RGB:
882 return GPU::TextureCombinator::Dot3RGB;
883 case GL_DOT3_RGBA:
884 return GPU::TextureCombinator::Dot3RGBA;
885 case GL_INTERPOLATE:
886 return GPU::TextureCombinator::Interpolate;
887 case GL_MODULATE:
888 return GPU::TextureCombinator::Modulate;
889 case GL_REPLACE:
890 return GPU::TextureCombinator::Replace;
891 case GL_SUBTRACT:
892 return GPU::TextureCombinator::Subtract;
893 default:
894 VERIFY_NOT_REACHED();
895 }
896 };
897 fixed_function_env.alpha_combinator = get_combinator(texture_unit.alpha_combinator());
898 fixed_function_env.rgb_combinator = get_combinator(texture_unit.rgb_combinator());
899
900 auto get_operand = [](GLenum operand) {
901 switch (operand) {
902 case GL_ONE_MINUS_SRC_ALPHA:
903 return GPU::TextureOperand::OneMinusSourceAlpha;
904 case GL_ONE_MINUS_SRC_COLOR:
905 return GPU::TextureOperand::OneMinusSourceColor;
906 case GL_SRC_ALPHA:
907 return GPU::TextureOperand::SourceAlpha;
908 case GL_SRC_COLOR:
909 return GPU::TextureOperand::SourceColor;
910 default:
911 VERIFY_NOT_REACHED();
912 }
913 };
914 auto get_source = [](GLenum source) {
915 switch (source) {
916 case GL_CONSTANT:
917 return GPU::TextureSource::Constant;
918 case GL_PREVIOUS:
919 return GPU::TextureSource::Previous;
920 case GL_PRIMARY_COLOR:
921 return GPU::TextureSource::PrimaryColor;
922 case GL_TEXTURE:
923 return GPU::TextureSource::Texture;
924 case GL_TEXTURE0 ... GL_TEXTURE31:
925 return GPU::TextureSource::TextureStage;
926 default:
927 VERIFY_NOT_REACHED();
928 }
929 };
930 for (size_t j = 0; j < 3; ++j) {
931 fixed_function_env.alpha_operand[j] = get_operand(texture_unit.alpha_operand(j));
932 fixed_function_env.alpha_source[j] = get_source(texture_unit.alpha_source(j));
933 if (fixed_function_env.alpha_source[j] == GPU::TextureSource::TextureStage)
934 fixed_function_env.alpha_source_texture_stage = texture_unit.alpha_source(j) - GL_TEXTURE0;
935
936 fixed_function_env.rgb_operand[j] = get_operand(texture_unit.rgb_operand(j));
937 fixed_function_env.rgb_source[j] = get_source(texture_unit.rgb_source(j));
938 if (fixed_function_env.rgb_source[j] == GPU::TextureSource::TextureStage)
939 fixed_function_env.rgb_source_texture_stage = texture_unit.rgb_source(j) - GL_TEXTURE0;
940 }
941
942 config.border_color = sampler.border_color();
943 m_rasterizer->set_sampler_config(i, config);
944 }
945}
946
947void GLContext::sync_device_texture_units()
948{
949 if (!m_texture_units_dirty)
950 return;
951 m_texture_units_dirty = false;
952
953 for (GPU::TextureUnitIndex i = 0; i < m_device_info.num_texture_units; ++i) {
954 GPU::TextureUnitConfiguration texture_unit_configuration;
955 texture_unit_configuration.enabled = m_texture_units[i].texture_2d_enabled();
956 texture_unit_configuration.transformation_matrix = m_texture_units[i].texture_matrix();
957
958 // Tex coord generation
959 u8 enabled_coordinates = GPU::TexCoordGenerationCoordinate::None;
960 for (GLenum capability = GL_TEXTURE_GEN_S; capability <= GL_TEXTURE_GEN_Q; ++capability) {
961 auto const context_coordinate_config = texture_coordinate_generation(i, capability);
962 if (!context_coordinate_config.enabled)
963 continue;
964
965 GPU::TexCoordGeneration* texcoord_generation;
966 switch (capability) {
967 case GL_TEXTURE_GEN_S:
968 enabled_coordinates |= GPU::TexCoordGenerationCoordinate::S;
969 texcoord_generation = &texture_unit_configuration.tex_coord_generation[0];
970 break;
971 case GL_TEXTURE_GEN_T:
972 enabled_coordinates |= GPU::TexCoordGenerationCoordinate::T;
973 texcoord_generation = &texture_unit_configuration.tex_coord_generation[1];
974 break;
975 case GL_TEXTURE_GEN_R:
976 enabled_coordinates |= GPU::TexCoordGenerationCoordinate::R;
977 texcoord_generation = &texture_unit_configuration.tex_coord_generation[2];
978 break;
979 case GL_TEXTURE_GEN_Q:
980 enabled_coordinates |= GPU::TexCoordGenerationCoordinate::Q;
981 texcoord_generation = &texture_unit_configuration.tex_coord_generation[3];
982 break;
983 default:
984 VERIFY_NOT_REACHED();
985 }
986
987 switch (context_coordinate_config.generation_mode) {
988 case GL_OBJECT_LINEAR:
989 texcoord_generation->mode = GPU::TexCoordGenerationMode::ObjectLinear;
990 texcoord_generation->coefficients = context_coordinate_config.object_plane_coefficients;
991 break;
992 case GL_EYE_LINEAR:
993 texcoord_generation->mode = GPU::TexCoordGenerationMode::EyeLinear;
994 texcoord_generation->coefficients = context_coordinate_config.eye_plane_coefficients;
995 break;
996 case GL_SPHERE_MAP:
997 texcoord_generation->mode = GPU::TexCoordGenerationMode::SphereMap;
998 break;
999 case GL_REFLECTION_MAP:
1000 texcoord_generation->mode = GPU::TexCoordGenerationMode::ReflectionMap;
1001 break;
1002 case GL_NORMAL_MAP:
1003 texcoord_generation->mode = GPU::TexCoordGenerationMode::NormalMap;
1004 break;
1005 default:
1006 VERIFY_NOT_REACHED();
1007 }
1008 }
1009 texture_unit_configuration.tex_coord_generation_enabled = enabled_coordinates;
1010
1011 m_rasterizer->set_texture_unit_configuration(i, texture_unit_configuration);
1012 }
1013}
1014
1015}