1#include "precompiled.h" 2// 3// Copyright (c) 2013 The ANGLE Project Authors. All rights reserved. 4// Use of this source code is governed by a BSD-style license that can be 5// found in the LICENSE file. 6// 7 8// validationES3.cpp: Validation functions for OpenGL ES 3.0 entry point parameters 9 10#include "libGLESv2/validationES3.h" 11#include "libGLESv2/validationES.h" 12#include "libGLESv2/Context.h" 13#include "libGLESv2/Texture.h" 14#include "libGLESv2/Framebuffer.h" 15#include "libGLESv2/Renderbuffer.h" 16#include "libGLESv2/formatutils.h" 17#include "libGLESv2/main.h" 18 19#include "common/mathutil.h" 20 21namespace gl 22{ 23 24bool ValidateES3TexImageParameters(gl::Context *context, GLenum target, GLint level, GLenum internalformat, bool isCompressed, bool isSubImage, 25 GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, 26 GLint border, GLenum format, GLenum type, const GLvoid *pixels) 27{ 28 if (!ValidTexture2DDestinationTarget(context, target)) 29 { 30 return gl::error(GL_INVALID_ENUM, false); 31 } 32 33 // Validate image size 34 if (!ValidImageSize(context, target, level, width, height, depth)) 35 { 36 return gl::error(GL_INVALID_VALUE, false); 37 } 38 39 // Verify zero border 40 if (border != 0) 41 { 42 return gl::error(GL_INVALID_VALUE, false); 43 } 44 45 if (xoffset < 0 || yoffset < 0 || zoffset < 0 || 46 std::numeric_limits<GLsizei>::max() - xoffset < width || 47 std::numeric_limits<GLsizei>::max() - yoffset < height || 48 std::numeric_limits<GLsizei>::max() - zoffset < depth) 49 { 50 return gl::error(GL_INVALID_VALUE, false); 51 } 52 53 gl::Texture *texture = NULL; 54 bool textureCompressed = false; 55 GLenum textureInternalFormat = GL_NONE; 56 GLint textureLevelWidth = 0; 57 GLint textureLevelHeight = 0; 58 GLint textureLevelDepth = 0; 59 switch (target) 60 { 61 case GL_TEXTURE_2D: 62 { 63 if (width > (context->getMaximum2DTextureDimension() >> level) || 64 height > (context->getMaximum2DTextureDimension() >> level)) 65 { 66 return gl::error(GL_INVALID_VALUE, false); 67 } 68 69 gl::Texture2D *texture2d = context->getTexture2D(); 70 if (texture2d) 71 { 72 textureCompressed = texture2d->isCompressed(level); 73 textureInternalFormat = texture2d->getInternalFormat(level); 74 textureLevelWidth = texture2d->getWidth(level); 75 textureLevelHeight = texture2d->getHeight(level); 76 textureLevelDepth = 1; 77 texture = texture2d; 78 } 79 } 80 break; 81 82 case GL_TEXTURE_CUBE_MAP_POSITIVE_X: 83 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: 84 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: 85 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: 86 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: 87 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: 88 { 89 if (!isSubImage && width != height) 90 { 91 return gl::error(GL_INVALID_VALUE, false); 92 } 93 94 if (width > (context->getMaximumCubeTextureDimension() >> level)) 95 { 96 return gl::error(GL_INVALID_VALUE, false); 97 } 98 99 gl::TextureCubeMap *textureCube = context->getTextureCubeMap(); 100 if (textureCube) 101 { 102 textureCompressed = textureCube->isCompressed(target, level); 103 textureInternalFormat = textureCube->getInternalFormat(target, level); 104 textureLevelWidth = textureCube->getWidth(target, level); 105 textureLevelHeight = textureCube->getHeight(target, level); 106 textureLevelDepth = 1; 107 texture = textureCube; 108 } 109 } 110 break; 111 112 case GL_TEXTURE_3D: 113 { 114 if (width > (context->getMaximum3DTextureDimension() >> level) || 115 height > (context->getMaximum3DTextureDimension() >> level) || 116 depth > (context->getMaximum3DTextureDimension() >> level)) 117 { 118 return gl::error(GL_INVALID_VALUE, false); 119 } 120 121 gl::Texture3D *texture3d = context->getTexture3D(); 122 if (texture3d) 123 { 124 textureCompressed = texture3d->isCompressed(level); 125 textureInternalFormat = texture3d->getInternalFormat(level); 126 textureLevelWidth = texture3d->getWidth(level); 127 textureLevelHeight = texture3d->getHeight(level); 128 textureLevelDepth = texture3d->getDepth(level); 129 texture = texture3d; 130 } 131 } 132 break; 133 134 case GL_TEXTURE_2D_ARRAY: 135 { 136 if (width > (context->getMaximum2DTextureDimension() >> level) || 137 height > (context->getMaximum2DTextureDimension() >> level) || 138 depth > (context->getMaximum2DArrayTextureLayers() >> level)) 139 { 140 return gl::error(GL_INVALID_VALUE, false); 141 } 142 143 gl::Texture2DArray *texture2darray = context->getTexture2DArray(); 144 if (texture2darray) 145 { 146 textureCompressed = texture2darray->isCompressed(level); 147 textureInternalFormat = texture2darray->getInternalFormat(level); 148 textureLevelWidth = texture2darray->getWidth(level); 149 textureLevelHeight = texture2darray->getHeight(level); 150 textureLevelDepth = texture2darray->getLayers(level); 151 texture = texture2darray; 152 } 153 } 154 break; 155 156 default: 157 return gl::error(GL_INVALID_ENUM, false); 158 } 159 160 if (!texture) 161 { 162 return gl::error(GL_INVALID_OPERATION, false); 163 } 164 165 if (texture->isImmutable() && !isSubImage) 166 { 167 return gl::error(GL_INVALID_OPERATION, false); 168 } 169 170 // Validate texture formats 171 GLenum actualInternalFormat = isSubImage ? textureInternalFormat : internalformat; 172 int clientVersion = context->getClientVersion(); 173 if (isCompressed) 174 { 175 if (!ValidCompressedImageSize(context, actualInternalFormat, width, height)) 176 { 177 return gl::error(GL_INVALID_OPERATION, false); 178 } 179 180 if (!gl::IsFormatCompressed(actualInternalFormat, clientVersion)) 181 { 182 return gl::error(GL_INVALID_ENUM, false); 183 } 184 185 if (target == GL_TEXTURE_3D) 186 { 187 return gl::error(GL_INVALID_OPERATION, false); 188 } 189 } 190 else 191 { 192 // Note: dEQP 2013.4 expects an INVALID_VALUE error for TexImage3D with an invalid 193 // internal format. (dEQP-GLES3.functional.negative_api.texture.teximage3d) 194 if (!gl::IsValidInternalFormat(actualInternalFormat, context) || 195 !gl::IsValidFormat(format, clientVersion) || 196 !gl::IsValidType(type, clientVersion)) 197 { 198 return gl::error(GL_INVALID_ENUM, false); 199 } 200 201 if (!gl::IsValidFormatCombination(actualInternalFormat, format, type, clientVersion)) 202 { 203 return gl::error(GL_INVALID_OPERATION, false); 204 } 205 206 if (target == GL_TEXTURE_3D && (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL)) 207 { 208 return gl::error(GL_INVALID_OPERATION, false); 209 } 210 } 211 212 // Validate sub image parameters 213 if (isSubImage) 214 { 215 if (isCompressed != textureCompressed) 216 { 217 return gl::error(GL_INVALID_OPERATION, false); 218 } 219 220 if (isCompressed) 221 { 222 if ((width % 4 != 0 && width != textureLevelWidth) || 223 (height % 4 != 0 && height != textureLevelHeight)) 224 { 225 return gl::error(GL_INVALID_OPERATION, false); 226 } 227 } 228 229 if (width == 0 || height == 0 || depth == 0) 230 { 231 return false; 232 } 233 234 if (xoffset < 0 || yoffset < 0 || zoffset < 0) 235 { 236 return gl::error(GL_INVALID_VALUE, false); 237 } 238 239 if (std::numeric_limits<GLsizei>::max() - xoffset < width || 240 std::numeric_limits<GLsizei>::max() - yoffset < height || 241 std::numeric_limits<GLsizei>::max() - zoffset < depth) 242 { 243 return gl::error(GL_INVALID_VALUE, false); 244 } 245 246 if (xoffset + width > textureLevelWidth || 247 yoffset + height > textureLevelHeight || 248 zoffset + depth > textureLevelDepth) 249 { 250 return gl::error(GL_INVALID_VALUE, false); 251 } 252 } 253 254 // Check for pixel unpack buffer related API errors 255 gl::Buffer *pixelUnpackBuffer = context->getPixelUnpackBuffer(); 256 if (pixelUnpackBuffer != NULL) 257 { 258 // ...the data would be unpacked from the buffer object such that the memory reads required 259 // would exceed the data store size. 260 size_t widthSize = static_cast<size_t>(width); 261 size_t heightSize = static_cast<size_t>(height); 262 size_t depthSize = static_cast<size_t>(depth); 263 GLenum sizedFormat = gl::IsSizedInternalFormat(actualInternalFormat, clientVersion) ? 264 actualInternalFormat : 265 gl::GetSizedInternalFormat(actualInternalFormat, type, clientVersion); 266 267 size_t pixelBytes = static_cast<size_t>(gl::GetPixelBytes(sizedFormat, clientVersion)); 268 269 if (!rx::IsUnsignedMultiplicationSafe(widthSize, heightSize) || 270 !rx::IsUnsignedMultiplicationSafe(widthSize * heightSize, depthSize) || 271 !rx::IsUnsignedMultiplicationSafe(widthSize * heightSize * depthSize, pixelBytes)) 272 { 273 // Overflow past the end of the buffer 274 return gl::error(GL_INVALID_OPERATION, false); 275 } 276 277 size_t copyBytes = widthSize * heightSize * depthSize * pixelBytes; 278 size_t offset = reinterpret_cast<size_t>(pixels); 279 280 if (!rx::IsUnsignedAdditionSafe(offset, copyBytes) || 281 ((offset + copyBytes) > static_cast<size_t>(pixelUnpackBuffer->size()))) 282 { 283 // Overflow past the end of the buffer 284 return gl::error(GL_INVALID_OPERATION, false); 285 } 286 287 // ...data is not evenly divisible into the number of bytes needed to store in memory a datum 288 // indicated by type. 289 size_t dataBytesPerPixel = static_cast<size_t>(gl::GetTypeBytes(type)); 290 291 if ((offset % dataBytesPerPixel) != 0) 292 { 293 return gl::error(GL_INVALID_OPERATION, false); 294 } 295 296 // ...the buffer object's data store is currently mapped. 297 if (pixelUnpackBuffer->mapped()) 298 { 299 return gl::error(GL_INVALID_OPERATION, false); 300 } 301 } 302 303 return true; 304} 305 306bool ValidateES3CopyTexImageParameters(gl::Context *context, GLenum target, GLint level, GLenum internalformat, 307 bool isSubImage, GLint xoffset, GLint yoffset, GLint zoffset, 308 GLint x, GLint y, GLsizei width, GLsizei height, GLint border) 309{ 310 GLenum textureInternalFormat; 311 if (!ValidateCopyTexImageParametersBase(context, target, level, internalformat, isSubImage, 312 xoffset, yoffset, zoffset, x, y, width, height, 313 border, &textureInternalFormat)) 314 { 315 return false; 316 } 317 318 gl::Framebuffer *framebuffer = context->getReadFramebuffer(); 319 320 if (framebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE) 321 { 322 return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION, false); 323 } 324 325 if (context->getReadFramebufferHandle() != 0 && framebuffer->getSamples() != 0) 326 { 327 return gl::error(GL_INVALID_OPERATION, false); 328 } 329 330 gl::FramebufferAttachment *source = framebuffer->getReadColorbuffer(); 331 GLenum colorbufferInternalFormat = source->getInternalFormat(); 332 333 if (isSubImage) 334 { 335 if (!gl::IsValidCopyTexImageCombination(textureInternalFormat, colorbufferInternalFormat, 336 context->getReadFramebufferHandle(), 337 context->getClientVersion())) 338 { 339 return gl::error(GL_INVALID_OPERATION, false); 340 } 341 } 342 else 343 { 344 if (!gl::IsValidCopyTexImageCombination(internalformat, colorbufferInternalFormat, 345 context->getReadFramebufferHandle(), 346 context->getClientVersion())) 347 { 348 return gl::error(GL_INVALID_OPERATION, false); 349 } 350 } 351 352 // If width or height is zero, it is a no-op. Return false without setting an error. 353 return (width > 0 && height > 0); 354} 355 356bool ValidateES3TexStorageParameters(gl::Context *context, GLenum target, GLsizei levels, GLenum internalformat, 357 GLsizei width, GLsizei height, GLsizei depth) 358{ 359 if (width < 1 || height < 1 || depth < 1 || levels < 1) 360 { 361 return gl::error(GL_INVALID_VALUE, false); 362 } 363 364 if (levels > gl::log2(std::max(std::max(width, height), depth)) + 1) 365 { 366 return gl::error(GL_INVALID_OPERATION, false); 367 } 368 369 gl::Texture *texture = NULL; 370 switch (target) 371 { 372 case GL_TEXTURE_2D: 373 { 374 texture = context->getTexture2D(); 375 376 if (width > (context->getMaximum2DTextureDimension()) || 377 height > (context->getMaximum2DTextureDimension())) 378 { 379 return gl::error(GL_INVALID_VALUE, false); 380 } 381 } 382 break; 383 384 case GL_TEXTURE_CUBE_MAP: 385 { 386 texture = context->getTextureCubeMap(); 387 388 if (width != height) 389 { 390 return gl::error(GL_INVALID_VALUE, false); 391 } 392 393 if (width > (context->getMaximumCubeTextureDimension())) 394 { 395 return gl::error(GL_INVALID_VALUE, false); 396 } 397 } 398 break; 399 400 case GL_TEXTURE_3D: 401 { 402 texture = context->getTexture3D(); 403 404 if (width > (context->getMaximum3DTextureDimension()) || 405 height > (context->getMaximum3DTextureDimension()) || 406 depth > (context->getMaximum3DTextureDimension())) 407 { 408 return gl::error(GL_INVALID_VALUE, false); 409 } 410 } 411 break; 412 413 case GL_TEXTURE_2D_ARRAY: 414 { 415 texture = context->getTexture2DArray(); 416 417 if (width > (context->getMaximum2DTextureDimension()) || 418 height > (context->getMaximum2DTextureDimension()) || 419 depth > (context->getMaximum2DArrayTextureLayers())) 420 { 421 return gl::error(GL_INVALID_VALUE, false); 422 } 423 } 424 break; 425 426 default: 427 return gl::error(GL_INVALID_ENUM, false); 428 } 429 430 if (!texture || texture->id() == 0) 431 { 432 return gl::error(GL_INVALID_OPERATION, false); 433 } 434 435 if (texture->isImmutable()) 436 { 437 return gl::error(GL_INVALID_OPERATION, false); 438 } 439 440 if (!gl::IsValidInternalFormat(internalformat, context)) 441 { 442 return gl::error(GL_INVALID_ENUM, false); 443 } 444 445 if (!gl::IsSizedInternalFormat(internalformat, context->getClientVersion())) 446 { 447 return gl::error(GL_INVALID_ENUM, false); 448 } 449 450 return true; 451} 452 453bool ValidateES3FramebufferTextureParameters(gl::Context *context, GLenum target, GLenum attachment, 454 GLenum textarget, GLuint texture, GLint level, GLint layer, 455 bool layerCall) 456{ 457 if (target != GL_FRAMEBUFFER && target != GL_DRAW_FRAMEBUFFER && target != GL_READ_FRAMEBUFFER) 458 { 459 return gl::error(GL_INVALID_ENUM, false); 460 } 461 462 if (attachment >= GL_COLOR_ATTACHMENT0 && attachment <= GL_COLOR_ATTACHMENT15) 463 { 464 const unsigned int colorAttachment = (attachment - GL_COLOR_ATTACHMENT0); 465 if (colorAttachment >= context->getMaximumRenderTargets()) 466 { 467 return gl::error(GL_INVALID_VALUE, false); 468 } 469 } 470 else 471 { 472 switch (attachment) 473 { 474 case GL_DEPTH_ATTACHMENT: 475 case GL_STENCIL_ATTACHMENT: 476 case GL_DEPTH_STENCIL_ATTACHMENT: 477 break; 478 default: 479 return gl::error(GL_INVALID_ENUM, false); 480 } 481 } 482 483 if (texture != 0) 484 { 485 gl::Texture *tex = context->getTexture(texture); 486 487 if (tex == NULL) 488 { 489 return gl::error(GL_INVALID_OPERATION, false); 490 } 491 492 if (level < 0) 493 { 494 return gl::error(GL_INVALID_VALUE, false); 495 } 496 497 if (layer < 0) 498 { 499 return gl::error(GL_INVALID_VALUE, false); 500 } 501 502 if (!layerCall) 503 { 504 switch (textarget) 505 { 506 case GL_TEXTURE_2D: 507 { 508 if (level > gl::log2(context->getMaximum2DTextureDimension())) 509 { 510 return gl::error(GL_INVALID_VALUE, false); 511 } 512 if (tex->getTarget() != GL_TEXTURE_2D) 513 { 514 return gl::error(GL_INVALID_OPERATION, false); 515 } 516 gl::Texture2D *tex2d = static_cast<gl::Texture2D *>(tex); 517 if (tex2d->isCompressed(level)) 518 { 519 return gl::error(GL_INVALID_OPERATION, false); 520 } 521 break; 522 } 523 524 case GL_TEXTURE_CUBE_MAP_POSITIVE_X: 525 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: 526 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: 527 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: 528 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: 529 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: 530 { 531 if (level > gl::log2(context->getMaximumCubeTextureDimension())) 532 { 533 return gl::error(GL_INVALID_VALUE, false); 534 } 535 if (tex->getTarget() != GL_TEXTURE_CUBE_MAP) 536 { 537 return gl::error(GL_INVALID_OPERATION, false); 538 } 539 gl::TextureCubeMap *texcube = static_cast<gl::TextureCubeMap *>(tex); 540 if (texcube->isCompressed(textarget, level)) 541 { 542 return gl::error(GL_INVALID_OPERATION, false); 543 } 544 break; 545 } 546 547 default: 548 return gl::error(GL_INVALID_ENUM, false); 549 } 550 } 551 else 552 { 553 switch (tex->getTarget()) 554 { 555 case GL_TEXTURE_2D_ARRAY: 556 { 557 if (level > gl::log2(context->getMaximum2DTextureDimension())) 558 { 559 return gl::error(GL_INVALID_VALUE, false); 560 } 561 562 if (layer >= context->getMaximum2DArrayTextureLayers()) 563 { 564 return gl::error(GL_INVALID_VALUE, false); 565 } 566 567 gl::Texture2DArray *texArray = static_cast<gl::Texture2DArray *>(tex); 568 if (texArray->isCompressed(level)) 569 { 570 return gl::error(GL_INVALID_OPERATION, false); 571 } 572 573 break; 574 } 575 576 case GL_TEXTURE_3D: 577 { 578 if (level > gl::log2(context->getMaximum3DTextureDimension())) 579 { 580 return gl::error(GL_INVALID_VALUE, false); 581 } 582 583 if (layer >= context->getMaximum3DTextureDimension()) 584 { 585 return gl::error(GL_INVALID_VALUE, false); 586 } 587 588 gl::Texture3D *tex3d = static_cast<gl::Texture3D *>(tex); 589 if (tex3d->isCompressed(level)) 590 { 591 return gl::error(GL_INVALID_OPERATION, false); 592 } 593 594 break; 595 } 596 597 default: 598 return gl::error(GL_INVALID_OPERATION, false); 599 } 600 } 601 } 602 603 gl::Framebuffer *framebuffer = NULL; 604 GLuint framebufferHandle = 0; 605 if (target == GL_READ_FRAMEBUFFER) 606 { 607 framebuffer = context->getReadFramebuffer(); 608 framebufferHandle = context->getReadFramebufferHandle(); 609 } 610 else 611 { 612 framebuffer = context->getDrawFramebuffer(); 613 framebufferHandle = context->getDrawFramebufferHandle(); 614 } 615 616 if (framebufferHandle == 0 || !framebuffer) 617 { 618 return gl::error(GL_INVALID_OPERATION, false); 619 } 620 621 return true; 622} 623 624bool ValidES3ReadFormatType(gl::Context *context, GLenum internalFormat, GLenum format, GLenum type) 625{ 626 switch (format) 627 { 628 case GL_RGBA: 629 switch (type) 630 { 631 case GL_UNSIGNED_BYTE: 632 break; 633 case GL_UNSIGNED_INT_2_10_10_10_REV: 634 if (internalFormat != GL_RGB10_A2) 635 { 636 return false; 637 } 638 break; 639 case GL_FLOAT: 640 if (gl::GetComponentType(internalFormat, 3) != GL_FLOAT) 641 { 642 return false; 643 } 644 break; 645 default: 646 return false; 647 } 648 break; 649 case GL_RGBA_INTEGER: 650 switch (type) 651 { 652 case GL_INT: 653 if (gl::GetComponentType(internalFormat, 3) != GL_INT) 654 { 655 return false; 656 } 657 break; 658 case GL_UNSIGNED_INT: 659 if (gl::GetComponentType(internalFormat, 3) != GL_UNSIGNED_INT) 660 { 661 return false; 662 } 663 break; 664 default: 665 return false; 666 } 667 break; 668 case GL_BGRA_EXT: 669 switch (type) 670 { 671 case GL_UNSIGNED_BYTE: 672 case GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT: 673 case GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT: 674 break; 675 default: 676 return false; 677 } 678 break; 679 case GL_RG_EXT: 680 case GL_RED_EXT: 681 if (!context->supportsRGTextures()) 682 { 683 return false; 684 } 685 switch (type) 686 { 687 case GL_UNSIGNED_BYTE: 688 break; 689 default: 690 return false; 691 } 692 break; 693 default: 694 return false; 695 } 696 return true; 697} 698 699bool ValidateInvalidateFramebufferParameters(gl::Context *context, GLenum target, GLsizei numAttachments, 700 const GLenum* attachments) 701{ 702 bool defaultFramebuffer = false; 703 704 switch (target) 705 { 706 case GL_DRAW_FRAMEBUFFER: 707 case GL_FRAMEBUFFER: 708 defaultFramebuffer = context->getDrawFramebufferHandle() == 0; 709 break; 710 case GL_READ_FRAMEBUFFER: 711 defaultFramebuffer = context->getReadFramebufferHandle() == 0; 712 break; 713 default: 714 return gl::error(GL_INVALID_ENUM, false); 715 } 716 717 for (int i = 0; i < numAttachments; ++i) 718 { 719 if (attachments[i] >= GL_COLOR_ATTACHMENT0 && attachments[i] <= GL_COLOR_ATTACHMENT15) 720 { 721 if (defaultFramebuffer) 722 { 723 return gl::error(GL_INVALID_ENUM, false); 724 } 725 726 if (attachments[i] >= GL_COLOR_ATTACHMENT0 + context->getMaximumRenderTargets()) 727 { 728 return gl::error(GL_INVALID_OPERATION, false); 729 } 730 } 731 else 732 { 733 switch (attachments[i]) 734 { 735 case GL_DEPTH_ATTACHMENT: 736 case GL_STENCIL_ATTACHMENT: 737 case GL_DEPTH_STENCIL_ATTACHMENT: 738 if (defaultFramebuffer) 739 { 740 return gl::error(GL_INVALID_ENUM, false); 741 } 742 break; 743 case GL_COLOR: 744 case GL_DEPTH: 745 case GL_STENCIL: 746 if (!defaultFramebuffer) 747 { 748 return gl::error(GL_INVALID_ENUM, false); 749 } 750 break; 751 default: 752 return gl::error(GL_INVALID_ENUM, false); 753 } 754 } 755 } 756 757 return true; 758} 759 760} 761