Serenity Operating System
at master 1015 lines 42 kB view raw
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}