1 // Copyright 2016 The SwiftShader Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Texture.cpp: Implements the Texture class and its derived classes 16 // Texture2D and TextureCubeMap. Implements GL texture objects and related 17 // functionality. 18 19 #include "Texture.h" 20 21 #include "main.h" 22 #include "mathutil.h" 23 #include "Framebuffer.h" 24 #include "Device.hpp" 25 #include "Display.h" 26 #include "common/debug.h" 27 28 #include <algorithm> 29 30 namespace gl 31 { 32 33 Texture::Texture(GLuint name) : NamedObject(name) 34 { 35 mMinFilter = GL_NEAREST_MIPMAP_LINEAR; 36 mMagFilter = GL_LINEAR; 37 mWrapS = GL_REPEAT; 38 mWrapT = GL_REPEAT; 39 mMaxAnisotropy = 1.0f; 40 mMaxLevel = 1000; 41 42 resource = new sw::Resource(0); 43 } 44 45 Texture::~Texture() 46 { 47 resource->destruct(); 48 } 49 50 sw::Resource *Texture::getResource() const 51 { 52 return resource; 53 } 54 55 // Returns true on successful filter state update (valid enum parameter) 56 bool Texture::setMinFilter(GLenum filter) 57 { 58 switch(filter) 59 { 60 case GL_NEAREST: 61 case GL_LINEAR: 62 case GL_NEAREST_MIPMAP_NEAREST: 63 case GL_LINEAR_MIPMAP_NEAREST: 64 case GL_NEAREST_MIPMAP_LINEAR: 65 case GL_LINEAR_MIPMAP_LINEAR: 66 mMinFilter = filter; 67 return true; 68 default: 69 return false; 70 } 71 } 72 73 // Returns true on successful filter state update (valid enum parameter) 74 bool Texture::setMagFilter(GLenum filter) 75 { 76 switch(filter) 77 { 78 case GL_NEAREST: 79 case GL_LINEAR: 80 mMagFilter = filter; 81 return true; 82 default: 83 return false; 84 } 85 } 86 87 // Returns true on successful wrap state update (valid enum parameter) 88 bool Texture::setWrapS(GLenum wrap) 89 { 90 switch(wrap) 91 { 92 case GL_CLAMP: 93 case GL_REPEAT: 94 case GL_CLAMP_TO_EDGE: 95 case GL_MIRRORED_REPEAT: 96 mWrapS = wrap; 97 return true; 98 default: 99 return false; 100 } 101 } 102 103 // Returns true on successful wrap state update (valid enum parameter) 104 bool Texture::setWrapT(GLenum wrap) 105 { 106 switch(wrap) 107 { 108 case GL_CLAMP: 109 case GL_REPEAT: 110 case GL_CLAMP_TO_EDGE: 111 case GL_MIRRORED_REPEAT: 112 mWrapT = wrap; 113 return true; 114 default: 115 return false; 116 } 117 } 118 119 // Returns true on successful max anisotropy update (valid anisotropy value) 120 bool Texture::setMaxAnisotropy(float textureMaxAnisotropy) 121 { 122 textureMaxAnisotropy = std::min(textureMaxAnisotropy, MAX_TEXTURE_MAX_ANISOTROPY); 123 124 if(textureMaxAnisotropy < 1.0f) 125 { 126 return false; 127 } 128 129 if(mMaxAnisotropy != textureMaxAnisotropy) 130 { 131 mMaxAnisotropy = textureMaxAnisotropy; 132 } 133 134 return true; 135 } 136 137 bool Texture::setMaxLevel(int level) 138 { 139 if(level < 0) 140 { 141 return false; 142 } 143 144 mMaxLevel = level; 145 146 return true; 147 } 148 149 GLenum Texture::getMinFilter() const 150 { 151 return mMinFilter; 152 } 153 154 GLenum Texture::getMagFilter() const 155 { 156 return mMagFilter; 157 } 158 159 GLenum Texture::getWrapS() const 160 { 161 return mWrapS; 162 } 163 164 GLenum Texture::getWrapT() const 165 { 166 return mWrapT; 167 } 168 169 GLfloat Texture::getMaxAnisotropy() const 170 { 171 return mMaxAnisotropy; 172 } 173 174 void Texture::setImage(GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *image) 175 { 176 if(pixels && image) 177 { 178 image->loadImageData(0, 0, 0, image->getWidth(), image->getHeight(), 1, format, type, unpackAlignment, pixels); 179 } 180 } 181 182 void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, Image *image) 183 { 184 if(pixels && image && (imageSize > 0)) // imageSize's correlation to width and height is already validated with gl::ComputeCompressedSize() at the API level 185 { 186 image->loadCompressedData(0, 0, 0, image->getWidth(), image->getHeight(), 1, imageSize, pixels); 187 } 188 } 189 190 void Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *image) 191 { 192 if(!image) 193 { 194 return error(GL_INVALID_OPERATION); 195 } 196 197 if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight()) 198 { 199 return error(GL_INVALID_VALUE); 200 } 201 202 if(IsCompressed(image->getFormat())) 203 { 204 return error(GL_INVALID_OPERATION); 205 } 206 207 if(format != image->getFormat()) 208 { 209 return error(GL_INVALID_OPERATION); 210 } 211 212 if(pixels) 213 { 214 image->loadImageData(xoffset, yoffset, 0, width, height, 1, format, type, unpackAlignment, pixels); 215 } 216 } 217 218 void Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, Image *image) 219 { 220 if(!image) 221 { 222 return error(GL_INVALID_OPERATION); 223 } 224 225 if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight()) 226 { 227 return error(GL_INVALID_VALUE); 228 } 229 230 if(format != image->getFormat()) 231 { 232 return error(GL_INVALID_OPERATION); 233 } 234 235 if(pixels && (imageSize > 0)) // imageSize's correlation to width and height is already validated with gl::ComputeCompressedSize() at the API level 236 { 237 image->loadCompressedData(xoffset, yoffset, 0, width, height, 1, imageSize, pixels); 238 } 239 } 240 241 bool Texture::copy(Image *source, const sw::Rect &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, Image *dest) 242 { 243 Device *device = getDevice(); 244 245 sw::SliceRect destRect(xoffset, yoffset, xoffset + (sourceRect.x1 - sourceRect.x0), yoffset + (sourceRect.y1 - sourceRect.y0), 0); 246 sw::SliceRect sourceSliceRect(sourceRect); 247 bool success = device->stretchRect(source, &sourceSliceRect, dest, &destRect, false); 248 249 if(!success) 250 { 251 return error(GL_OUT_OF_MEMORY, false); 252 } 253 254 return true; 255 } 256 257 bool Texture::isMipmapFiltered() const 258 { 259 switch(mMinFilter) 260 { 261 case GL_NEAREST: 262 case GL_LINEAR: 263 return false; 264 case GL_NEAREST_MIPMAP_NEAREST: 265 case GL_LINEAR_MIPMAP_NEAREST: 266 case GL_NEAREST_MIPMAP_LINEAR: 267 case GL_LINEAR_MIPMAP_LINEAR: 268 return true; 269 default: UNREACHABLE(mMinFilter); 270 } 271 272 return false; 273 } 274 275 Texture2D::Texture2D(GLuint name) : Texture(name) 276 { 277 for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) 278 { 279 image[i] = 0; 280 } 281 282 mColorbufferProxy = nullptr; 283 mProxyRefs = 0; 284 } 285 286 Texture2D::~Texture2D() 287 { 288 for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) 289 { 290 if(image[i]) 291 { 292 image[i]->unbind(); 293 image[i] = 0; 294 } 295 } 296 297 mColorbufferProxy = nullptr; 298 } 299 300 // We need to maintain a count of references to renderbuffers acting as 301 // proxies for this texture, so that we do not attempt to use a pointer 302 // to a renderbuffer proxy which has been deleted. 303 void Texture2D::addProxyRef(const Renderbuffer *proxy) 304 { 305 mProxyRefs++; 306 } 307 308 void Texture2D::releaseProxy(const Renderbuffer *proxy) 309 { 310 if(mProxyRefs > 0) 311 { 312 mProxyRefs--; 313 } 314 315 if(mProxyRefs == 0) 316 { 317 mColorbufferProxy = nullptr; 318 } 319 } 320 321 GLenum Texture2D::getTarget() const 322 { 323 return GL_TEXTURE_2D; 324 } 325 326 GLsizei Texture2D::getWidth(GLenum target, GLint level) const 327 { 328 ASSERT(target == GL_TEXTURE_2D || target == GL_PROXY_TEXTURE_2D); 329 return image[level] ? image[level]->getWidth() : 0; 330 } 331 332 GLsizei Texture2D::getHeight(GLenum target, GLint level) const 333 { 334 ASSERT(target == GL_TEXTURE_2D || target == GL_PROXY_TEXTURE_2D); 335 return image[level] ? image[level]->getHeight() : 0; 336 } 337 338 GLenum Texture2D::getFormat(GLenum target, GLint level) const 339 { 340 ASSERT(target == GL_TEXTURE_2D || target == GL_PROXY_TEXTURE_2D); 341 return image[level] ? image[level]->getFormat() : GL_NONE; 342 } 343 344 GLenum Texture2D::getType(GLenum target, GLint level) const 345 { 346 ASSERT(target == GL_TEXTURE_2D || target == GL_PROXY_TEXTURE_2D); 347 return image[level] ? image[level]->getType() : GL_NONE; 348 } 349 350 sw::Format Texture2D::getInternalFormat(GLenum target, GLint level) const 351 { 352 ASSERT(target == GL_TEXTURE_2D || target == GL_PROXY_TEXTURE_2D); 353 return image[level] ? image[level]->getInternalFormat() : sw::FORMAT_NULL; 354 } 355 356 int Texture2D::getTopLevel() const 357 { 358 ASSERT(isSamplerComplete()); 359 int levels = 0; 360 361 while(levels < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[levels]) 362 { 363 levels++; 364 } 365 366 return levels; 367 } 368 369 void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) 370 { 371 if(image[level]) 372 { 373 image[level]->unbind(); 374 } 375 376 image[level] = new Image(this, width, height, format, type); 377 378 if(!image[level]) 379 { 380 return error(GL_OUT_OF_MEMORY); 381 } 382 383 Texture::setImage(format, type, unpackAlignment, pixels, image[level]); 384 } 385 386 void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels) 387 { 388 if(image[level]) 389 { 390 image[level]->unbind(); 391 } 392 393 image[level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE); 394 395 if(!image[level]) 396 { 397 return error(GL_OUT_OF_MEMORY); 398 } 399 400 Texture::setCompressedImage(imageSize, pixels, image[level]); 401 } 402 403 void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) 404 { 405 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, image[level]); 406 } 407 408 void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels) 409 { 410 Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, image[level]); 411 } 412 413 void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) 414 { 415 Image *renderTarget = source->getRenderTarget(); 416 417 if(!renderTarget) 418 { 419 ERR("Failed to retrieve the render target."); 420 return error(GL_OUT_OF_MEMORY); 421 } 422 423 if(image[level]) 424 { 425 image[level]->unbind(); 426 } 427 428 image[level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE); 429 430 if(!image[level]) 431 { 432 return error(GL_OUT_OF_MEMORY); 433 } 434 435 if(width != 0 && height != 0) 436 { 437 sw::Rect sourceRect = {x, y, x + width, y + height}; 438 sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight()); 439 440 copy(renderTarget, sourceRect, format, 0, 0, image[level]); 441 } 442 443 renderTarget->release(); 444 } 445 446 void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) 447 { 448 if(!image[level]) 449 { 450 return error(GL_INVALID_OPERATION); 451 } 452 453 if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight()) 454 { 455 return error(GL_INVALID_VALUE); 456 } 457 458 Image *renderTarget = source->getRenderTarget(); 459 460 if(!renderTarget) 461 { 462 ERR("Failed to retrieve the render target."); 463 return error(GL_OUT_OF_MEMORY); 464 } 465 466 sw::Rect sourceRect = {x, y, x + width, y + height}; 467 sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight()); 468 469 copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, image[level]); 470 471 renderTarget->release(); 472 } 473 474 void Texture2D::setImage(Image *sharedImage) 475 { 476 sharedImage->addRef(); 477 478 if(image[0]) 479 { 480 image[0]->unbind(); 481 } 482 483 image[0] = sharedImage; 484 } 485 486 // Tests for 2D texture sampling completeness. 487 bool Texture2D::isSamplerComplete() const 488 { 489 if(!image[0]) 490 { 491 return false; 492 } 493 494 GLsizei width = image[0]->getWidth(); 495 GLsizei height = image[0]->getHeight(); 496 497 if(width <= 0 || height <= 0) 498 { 499 return false; 500 } 501 502 if(isMipmapFiltered()) 503 { 504 if(!isMipmapComplete()) 505 { 506 return false; 507 } 508 } 509 510 return true; 511 } 512 513 // Tests for 2D texture (mipmap) completeness. 514 bool Texture2D::isMipmapComplete() const 515 { 516 GLsizei width = image[0]->getWidth(); 517 GLsizei height = image[0]->getHeight(); 518 519 int q = log2(std::max(width, height)); 520 521 for(int level = 1; level <= q && level <= mMaxLevel; level++) 522 { 523 if(!image[level]) 524 { 525 return false; 526 } 527 528 if(image[level]->getFormat() != image[0]->getFormat()) 529 { 530 return false; 531 } 532 533 if(image[level]->getType() != image[0]->getType()) 534 { 535 return false; 536 } 537 538 if(image[level]->getWidth() != std::max(1, width >> level)) 539 { 540 return false; 541 } 542 543 if(image[level]->getHeight() != std::max(1, height >> level)) 544 { 545 return false; 546 } 547 } 548 549 return true; 550 } 551 552 bool Texture2D::isCompressed(GLenum target, GLint level) const 553 { 554 return IsCompressed(getFormat(target, level)); 555 } 556 557 bool Texture2D::isDepth(GLenum target, GLint level) const 558 { 559 return IsDepthTexture(getFormat(target, level)); 560 } 561 562 void Texture2D::generateMipmaps() 563 { 564 if(!image[0]) 565 { 566 return; // FIXME: error? 567 } 568 569 unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight())); 570 571 for(unsigned int i = 1; i <= q; i++) 572 { 573 if(image[i]) 574 { 575 image[i]->unbind(); 576 } 577 578 image[i] = new Image(this, std::max(image[0]->getWidth() >> i, 1), std::max(image[0]->getHeight() >> i, 1), image[0]->getFormat(), image[0]->getType()); 579 580 if(!image[i]) 581 { 582 return error(GL_OUT_OF_MEMORY); 583 } 584 585 getDevice()->stretchRect(image[i - 1], 0, image[i], 0, true); 586 } 587 } 588 589 Image *Texture2D::getImage(unsigned int level) 590 { 591 return image[level]; 592 } 593 594 Renderbuffer *Texture2D::getRenderbuffer(GLenum target) 595 { 596 if(target != GL_TEXTURE_2D) 597 { 598 return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr); 599 } 600 601 if(!mColorbufferProxy) 602 { 603 mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2D(this)); 604 } 605 606 return mColorbufferProxy; 607 } 608 609 Image *Texture2D::getRenderTarget(GLenum target, unsigned int level) 610 { 611 ASSERT(target == GL_TEXTURE_2D); 612 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS); 613 614 if(image[level]) 615 { 616 image[level]->addRef(); 617 } 618 619 return image[level]; 620 } 621 622 TextureCubeMap::TextureCubeMap(GLuint name) : Texture(name) 623 { 624 for(int f = 0; f < 6; f++) 625 { 626 for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) 627 { 628 image[f][i] = 0; 629 } 630 } 631 632 for(int f = 0; f < 6; f++) 633 { 634 mFaceProxies[f] = nullptr; 635 mFaceProxyRefs[f] = 0; 636 } 637 } 638 639 TextureCubeMap::~TextureCubeMap() 640 { 641 for(int f = 0; f < 6; f++) 642 { 643 for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) 644 { 645 if(image[f][i]) 646 { 647 image[f][i]->unbind(); 648 image[f][i] = 0; 649 } 650 } 651 } 652 653 for(int i = 0; i < 6; i++) 654 { 655 mFaceProxies[i] = nullptr; 656 } 657 } 658 659 // We need to maintain a count of references to renderbuffers acting as 660 // proxies for this texture, so that the texture is not deleted while 661 // proxy references still exist. If the reference count drops to zero, 662 // we set our proxy pointer null, so that a new attempt at referencing 663 // will cause recreation. 664 void TextureCubeMap::addProxyRef(const Renderbuffer *proxy) 665 { 666 for(int f = 0; f < 6; f++) 667 { 668 if(mFaceProxies[f] == proxy) 669 { 670 mFaceProxyRefs[f]++; 671 } 672 } 673 } 674 675 void TextureCubeMap::releaseProxy(const Renderbuffer *proxy) 676 { 677 for(int f = 0; f < 6; f++) 678 { 679 if(mFaceProxies[f] == proxy) 680 { 681 if(mFaceProxyRefs[f] > 0) 682 { 683 mFaceProxyRefs[f]--; 684 } 685 686 if(mFaceProxyRefs[f] == 0) 687 { 688 mFaceProxies[f] = nullptr; 689 } 690 } 691 } 692 } 693 694 GLenum TextureCubeMap::getTarget() const 695 { 696 return GL_TEXTURE_CUBE_MAP; 697 } 698 699 GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const 700 { 701 int face = CubeFaceIndex(target); 702 return image[face][level] ? image[face][level]->getWidth() : 0; 703 } 704 705 GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const 706 { 707 int face = CubeFaceIndex(target); 708 return image[face][level] ? image[face][level]->getHeight() : 0; 709 } 710 711 GLenum TextureCubeMap::getFormat(GLenum target, GLint level) const 712 { 713 int face = CubeFaceIndex(target); 714 return image[face][level] ? image[face][level]->getFormat() : GL_NONE; 715 } 716 717 GLenum TextureCubeMap::getType(GLenum target, GLint level) const 718 { 719 int face = CubeFaceIndex(target); 720 return image[face][level] ? image[face][level]->getType() : GL_NONE; 721 } 722 723 sw::Format TextureCubeMap::getInternalFormat(GLenum target, GLint level) const 724 { 725 int face = CubeFaceIndex(target); 726 return image[face][level] ? image[face][level]->getInternalFormat() : sw::FORMAT_NULL; 727 } 728 729 int TextureCubeMap::getTopLevel() const 730 { 731 ASSERT(isSamplerComplete()); 732 int levels = 0; 733 734 while(levels < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[0][levels]) 735 { 736 levels++; 737 } 738 739 return levels; 740 } 741 742 void TextureCubeMap::setCompressedImage(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels) 743 { 744 int face = CubeFaceIndex(target); 745 746 if(image[face][level]) 747 { 748 image[face][level]->unbind(); 749 } 750 751 image[face][level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE); 752 753 if(!image[face][level]) 754 { 755 return error(GL_OUT_OF_MEMORY); 756 } 757 758 Texture::setCompressedImage(imageSize, pixels, image[face][level]); 759 } 760 761 void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) 762 { 763 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, image[CubeFaceIndex(target)][level]); 764 } 765 766 void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels) 767 { 768 Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, image[CubeFaceIndex(target)][level]); 769 } 770 771 // Tests for cube map sampling completeness. 772 bool TextureCubeMap::isSamplerComplete() const 773 { 774 for(int face = 0; face < 6; face++) 775 { 776 if(!image[face][0]) 777 { 778 return false; 779 } 780 } 781 782 int size = image[0][0]->getWidth(); 783 784 if(size <= 0) 785 { 786 return false; 787 } 788 789 if(!isMipmapFiltered()) 790 { 791 if(!isCubeComplete()) 792 { 793 return false; 794 } 795 } 796 else 797 { 798 if(!isMipmapCubeComplete()) // Also tests for isCubeComplete() 799 { 800 return false; 801 } 802 } 803 804 return true; 805 } 806 807 // Tests for cube texture completeness. 808 bool TextureCubeMap::isCubeComplete() const 809 { 810 if(image[0][0]->getWidth() <= 0 || image[0][0]->getHeight() != image[0][0]->getWidth()) 811 { 812 return false; 813 } 814 815 for(unsigned int face = 1; face < 6; face++) 816 { 817 if(image[face][0]->getWidth() != image[0][0]->getWidth() || 818 image[face][0]->getWidth() != image[0][0]->getHeight() || 819 image[face][0]->getFormat() != image[0][0]->getFormat() || 820 image[face][0]->getType() != image[0][0]->getType()) 821 { 822 return false; 823 } 824 } 825 826 return true; 827 } 828 829 bool TextureCubeMap::isMipmapCubeComplete() const 830 { 831 if(!isCubeComplete()) 832 { 833 return false; 834 } 835 836 GLsizei size = image[0][0]->getWidth(); 837 int q = log2(size); 838 839 for(int face = 0; face < 6; face++) 840 { 841 for(int level = 1; level <= q; level++) 842 { 843 if(!image[face][level]) 844 { 845 return false; 846 } 847 848 if(image[face][level]->getFormat() != image[0][0]->getFormat()) 849 { 850 return false; 851 } 852 853 if(image[face][level]->getType() != image[0][0]->getType()) 854 { 855 return false; 856 } 857 858 if(image[face][level]->getWidth() != std::max(1, size >> level)) 859 { 860 return false; 861 } 862 } 863 } 864 865 return true; 866 } 867 868 bool TextureCubeMap::isCompressed(GLenum target, GLint level) const 869 { 870 return IsCompressed(getFormat(target, level)); 871 } 872 873 bool TextureCubeMap::isDepth(GLenum target, GLint level) const 874 { 875 return IsDepthTexture(getFormat(target, level)); 876 } 877 878 void TextureCubeMap::setImage(GLenum target, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) 879 { 880 int face = CubeFaceIndex(target); 881 882 if(image[face][level]) 883 { 884 image[face][level]->unbind(); 885 } 886 887 image[face][level] = new Image(this, width, height, format, type); 888 889 if(!image[face][level]) 890 { 891 return error(GL_OUT_OF_MEMORY); 892 } 893 894 Texture::setImage(format, type, unpackAlignment, pixels, image[face][level]); 895 } 896 897 void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) 898 { 899 Image *renderTarget = source->getRenderTarget(); 900 901 if(!renderTarget) 902 { 903 ERR("Failed to retrieve the render target."); 904 return error(GL_OUT_OF_MEMORY); 905 } 906 907 int face = CubeFaceIndex(target); 908 909 if(image[face][level]) 910 { 911 image[face][level]->unbind(); 912 } 913 914 image[face][level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE); 915 916 if(!image[face][level]) 917 { 918 return error(GL_OUT_OF_MEMORY); 919 } 920 921 if(width != 0 && height != 0) 922 { 923 sw::Rect sourceRect = {x, y, x + width, y + height}; 924 sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight()); 925 926 copy(renderTarget, sourceRect, format, 0, 0, image[face][level]); 927 } 928 929 renderTarget->release(); 930 } 931 932 Image *TextureCubeMap::getImage(int face, unsigned int level) 933 { 934 return image[face][level]; 935 } 936 937 Image *TextureCubeMap::getImage(GLenum face, unsigned int level) 938 { 939 return image[CubeFaceIndex(face)][level]; 940 } 941 942 void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) 943 { 944 int face = CubeFaceIndex(target); 945 946 if(!image[face][level]) 947 { 948 return error(GL_INVALID_OPERATION); 949 } 950 951 GLsizei size = image[face][level]->getWidth(); 952 953 if(xoffset + width > size || yoffset + height > size) 954 { 955 return error(GL_INVALID_VALUE); 956 } 957 958 Image *renderTarget = source->getRenderTarget(); 959 960 if(!renderTarget) 961 { 962 ERR("Failed to retrieve the render target."); 963 return error(GL_OUT_OF_MEMORY); 964 } 965 966 sw::Rect sourceRect = {x, y, x + width, y + height}; 967 sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight()); 968 969 copy(renderTarget, sourceRect, image[face][level]->getFormat(), xoffset, yoffset, image[face][level]); 970 971 renderTarget->release(); 972 } 973 974 void TextureCubeMap::generateMipmaps() 975 { 976 if(!isCubeComplete()) 977 { 978 return error(GL_INVALID_OPERATION); 979 } 980 981 unsigned int q = log2(image[0][0]->getWidth()); 982 983 for(unsigned int f = 0; f < 6; f++) 984 { 985 for(unsigned int i = 1; i <= q; i++) 986 { 987 if(image[f][i]) 988 { 989 image[f][i]->unbind(); 990 } 991 992 image[f][i] = new Image(this, std::max(image[0][0]->getWidth() >> i, 1), std::max(image[0][0]->getHeight() >> i, 1), image[0][0]->getFormat(), image[0][0]->getType()); 993 994 if(!image[f][i]) 995 { 996 return error(GL_OUT_OF_MEMORY); 997 } 998 999 getDevice()->stretchRect(image[f][i - 1], 0, image[f][i], 0, true); 1000 } 1001 } 1002 } 1003 1004 Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target) 1005 { 1006 if(!IsCubemapTextureTarget(target)) 1007 { 1008 return error(GL_INVALID_OPERATION, (Renderbuffer *)nullptr); 1009 } 1010 1011 int face = CubeFaceIndex(target); 1012 1013 if(!mFaceProxies[face]) 1014 { 1015 mFaceProxies[face] = new Renderbuffer(name, new RenderbufferTextureCubeMap(this, target)); 1016 } 1017 1018 return mFaceProxies[face]; 1019 } 1020 1021 Image *TextureCubeMap::getRenderTarget(GLenum target, unsigned int level) 1022 { 1023 ASSERT(IsCubemapTextureTarget(target)); 1024 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS); 1025 1026 int face = CubeFaceIndex(target); 1027 1028 if(image[face][level]) 1029 { 1030 image[face][level]->addRef(); 1031 } 1032 1033 return image[face][level]; 1034 } 1035 1036 } 1037