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) 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) 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 resource->lock(sw::DESTRUCT); 289 290 for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) 291 { 292 if(image[i]) 293 { 294 image[i]->unbind(); 295 image[i] = 0; 296 } 297 } 298 299 resource->unlock(); 300 301 mColorbufferProxy = nullptr; 302 } 303 304 // We need to maintain a count of references to renderbuffers acting as 305 // proxies for this texture, so that we do not attempt to use a pointer 306 // to a renderbuffer proxy which has been deleted. 307 void Texture2D::addProxyRef(const Renderbuffer *proxy) 308 { 309 mProxyRefs++; 310 } 311 312 void Texture2D::releaseProxy(const Renderbuffer *proxy) 313 { 314 if(mProxyRefs > 0) 315 { 316 mProxyRefs--; 317 } 318 319 if(mProxyRefs == 0) 320 { 321 mColorbufferProxy = nullptr; 322 } 323 } 324 325 GLenum Texture2D::getTarget() const 326 { 327 return GL_TEXTURE_2D; 328 } 329 330 GLsizei Texture2D::getWidth(GLenum target, GLint level) const 331 { 332 ASSERT(target == GL_TEXTURE_2D || target == GL_PROXY_TEXTURE_2D); 333 return image[level] ? image[level]->getWidth() : 0; 334 } 335 336 GLsizei Texture2D::getHeight(GLenum target, GLint level) const 337 { 338 ASSERT(target == GL_TEXTURE_2D || target == GL_PROXY_TEXTURE_2D); 339 return image[level] ? image[level]->getHeight() : 0; 340 } 341 342 GLenum Texture2D::getFormat(GLenum target, GLint level) const 343 { 344 ASSERT(target == GL_TEXTURE_2D || target == GL_PROXY_TEXTURE_2D); 345 return image[level] ? image[level]->getFormat() : GL_NONE; 346 } 347 348 GLenum Texture2D::getType(GLenum target, GLint level) const 349 { 350 ASSERT(target == GL_TEXTURE_2D || target == GL_PROXY_TEXTURE_2D); 351 return image[level] ? image[level]->getType() : GL_NONE; 352 } 353 354 sw::Format Texture2D::getInternalFormat(GLenum target, GLint level) const 355 { 356 ASSERT(target == GL_TEXTURE_2D || target == GL_PROXY_TEXTURE_2D); 357 return image[level] ? image[level]->getInternalFormat() : sw::FORMAT_NULL; 358 } 359 360 int Texture2D::getLevelCount() const 361 { 362 ASSERT(isSamplerComplete()); 363 int levels = 0; 364 365 while(levels < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[levels]) 366 { 367 levels++; 368 } 369 370 return levels; 371 } 372 373 void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) 374 { 375 if(image[level]) 376 { 377 image[level]->unbind(); 378 } 379 380 image[level] = new Image(this, width, height, format, type); 381 382 if(!image[level]) 383 { 384 return error(GL_OUT_OF_MEMORY); 385 } 386 387 Texture::setImage(format, type, unpackAlignment, pixels, image[level]); 388 } 389 390 void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels) 391 { 392 if(image[level]) 393 { 394 image[level]->unbind(); 395 } 396 397 image[level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE); 398 399 if(!image[level]) 400 { 401 return error(GL_OUT_OF_MEMORY); 402 } 403 404 Texture::setCompressedImage(imageSize, pixels, image[level]); 405 } 406 407 void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) 408 { 409 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, image[level]); 410 } 411 412 void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels) 413 { 414 Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, image[level]); 415 } 416 417 void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) 418 { 419 Image *renderTarget = source->getRenderTarget(); 420 421 if(!renderTarget) 422 { 423 ERR("Failed to retrieve the render target."); 424 return error(GL_OUT_OF_MEMORY); 425 } 426 427 if(image[level]) 428 { 429 image[level]->unbind(); 430 } 431 432 image[level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE); 433 434 if(!image[level]) 435 { 436 return error(GL_OUT_OF_MEMORY); 437 } 438 439 if(width != 0 && height != 0) 440 { 441 sw::Rect sourceRect = {x, y, x + width, y + height}; 442 sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight()); 443 444 copy(renderTarget, sourceRect, format, 0, 0, image[level]); 445 } 446 447 renderTarget->release(); 448 } 449 450 void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) 451 { 452 if(!image[level]) 453 { 454 return error(GL_INVALID_OPERATION); 455 } 456 457 if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight()) 458 { 459 return error(GL_INVALID_VALUE); 460 } 461 462 Image *renderTarget = source->getRenderTarget(); 463 464 if(!renderTarget) 465 { 466 ERR("Failed to retrieve the render target."); 467 return error(GL_OUT_OF_MEMORY); 468 } 469 470 sw::Rect sourceRect = {x, y, x + width, y + height}; 471 sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight()); 472 473 copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, image[level]); 474 475 renderTarget->release(); 476 } 477 478 void Texture2D::setImage(Image *sharedImage) 479 { 480 sharedImage->addRef(); 481 482 if(image[0]) 483 { 484 image[0]->unbind(); 485 } 486 487 image[0] = sharedImage; 488 } 489 490 // Tests for 2D texture sampling completeness. 491 bool Texture2D::isSamplerComplete() const 492 { 493 if(!image[0]) 494 { 495 return false; 496 } 497 498 GLsizei width = image[0]->getWidth(); 499 GLsizei height = image[0]->getHeight(); 500 501 if(width <= 0 || height <= 0) 502 { 503 return false; 504 } 505 506 if(isMipmapFiltered()) 507 { 508 if(!isMipmapComplete()) 509 { 510 return false; 511 } 512 } 513 514 return true; 515 } 516 517 // Tests for 2D texture (mipmap) completeness. 518 bool Texture2D::isMipmapComplete() const 519 { 520 GLsizei width = image[0]->getWidth(); 521 GLsizei height = image[0]->getHeight(); 522 523 int q = log2(std::max(width, height)); 524 525 for(int level = 1; level <= q && level <= mMaxLevel; level++) 526 { 527 if(!image[level]) 528 { 529 return false; 530 } 531 532 if(image[level]->getFormat() != image[0]->getFormat()) 533 { 534 return false; 535 } 536 537 if(image[level]->getType() != image[0]->getType()) 538 { 539 return false; 540 } 541 542 if(image[level]->getWidth() != std::max(1, width >> level)) 543 { 544 return false; 545 } 546 547 if(image[level]->getHeight() != std::max(1, height >> level)) 548 { 549 return false; 550 } 551 } 552 553 return true; 554 } 555 556 bool Texture2D::isCompressed(GLenum target, GLint level) const 557 { 558 return IsCompressed(getFormat(target, level)); 559 } 560 561 bool Texture2D::isDepth(GLenum target, GLint level) const 562 { 563 return IsDepthTexture(getFormat(target, level)); 564 } 565 566 void Texture2D::generateMipmaps() 567 { 568 if(!image[0]) 569 { 570 return; // FIXME: error? 571 } 572 573 unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight())); 574 575 for(unsigned int i = 1; i <= q; i++) 576 { 577 if(image[i]) 578 { 579 image[i]->unbind(); 580 } 581 582 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()); 583 584 if(!image[i]) 585 { 586 return error(GL_OUT_OF_MEMORY); 587 } 588 589 getDevice()->stretchRect(image[i - 1], 0, image[i], 0, true); 590 } 591 } 592 593 Image *Texture2D::getImage(unsigned int level) 594 { 595 return image[level]; 596 } 597 598 Renderbuffer *Texture2D::getRenderbuffer(GLenum target) 599 { 600 if(target != GL_TEXTURE_2D) 601 { 602 return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr); 603 } 604 605 if(!mColorbufferProxy) 606 { 607 mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2D(this)); 608 } 609 610 return mColorbufferProxy; 611 } 612 613 Image *Texture2D::getRenderTarget(GLenum target, unsigned int level) 614 { 615 ASSERT(target == GL_TEXTURE_2D); 616 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS); 617 618 if(image[level]) 619 { 620 image[level]->addRef(); 621 } 622 623 return image[level]; 624 } 625 626 TextureCubeMap::TextureCubeMap(GLuint name) : Texture(name) 627 { 628 for(int f = 0; f < 6; f++) 629 { 630 for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) 631 { 632 image[f][i] = 0; 633 } 634 } 635 636 for(int f = 0; f < 6; f++) 637 { 638 mFaceProxies[f] = nullptr; 639 mFaceProxyRefs[f] = 0; 640 } 641 } 642 643 TextureCubeMap::~TextureCubeMap() 644 { 645 resource->lock(sw::DESTRUCT); 646 647 for(int f = 0; f < 6; f++) 648 { 649 for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) 650 { 651 if(image[f][i]) 652 { 653 image[f][i]->unbind(); 654 image[f][i] = 0; 655 } 656 } 657 } 658 659 resource->unlock(); 660 661 for(int i = 0; i < 6; i++) 662 { 663 mFaceProxies[i] = nullptr; 664 } 665 } 666 667 // We need to maintain a count of references to renderbuffers acting as 668 // proxies for this texture, so that the texture is not deleted while 669 // proxy references still exist. If the reference count drops to zero, 670 // we set our proxy pointer null, so that a new attempt at referencing 671 // will cause recreation. 672 void TextureCubeMap::addProxyRef(const Renderbuffer *proxy) 673 { 674 for(int f = 0; f < 6; f++) 675 { 676 if(mFaceProxies[f] == proxy) 677 { 678 mFaceProxyRefs[f]++; 679 } 680 } 681 } 682 683 void TextureCubeMap::releaseProxy(const Renderbuffer *proxy) 684 { 685 for(int f = 0; f < 6; f++) 686 { 687 if(mFaceProxies[f] == proxy) 688 { 689 if(mFaceProxyRefs[f] > 0) 690 { 691 mFaceProxyRefs[f]--; 692 } 693 694 if(mFaceProxyRefs[f] == 0) 695 { 696 mFaceProxies[f] = nullptr; 697 } 698 } 699 } 700 } 701 702 GLenum TextureCubeMap::getTarget() const 703 { 704 return GL_TEXTURE_CUBE_MAP; 705 } 706 707 GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const 708 { 709 int face = CubeFaceIndex(target); 710 return image[face][level] ? image[face][level]->getWidth() : 0; 711 } 712 713 GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const 714 { 715 int face = CubeFaceIndex(target); 716 return image[face][level] ? image[face][level]->getHeight() : 0; 717 } 718 719 GLenum TextureCubeMap::getFormat(GLenum target, GLint level) const 720 { 721 int face = CubeFaceIndex(target); 722 return image[face][level] ? image[face][level]->getFormat() : GL_NONE; 723 } 724 725 GLenum TextureCubeMap::getType(GLenum target, GLint level) const 726 { 727 int face = CubeFaceIndex(target); 728 return image[face][level] ? image[face][level]->getType() : GL_NONE; 729 } 730 731 sw::Format TextureCubeMap::getInternalFormat(GLenum target, GLint level) const 732 { 733 int face = CubeFaceIndex(target); 734 return image[face][level] ? image[face][level]->getInternalFormat() : sw::FORMAT_NULL; 735 } 736 737 int TextureCubeMap::getLevelCount() const 738 { 739 ASSERT(isSamplerComplete()); 740 int levels = 0; 741 742 while(levels < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[0][levels]) 743 { 744 levels++; 745 } 746 747 return levels; 748 } 749 750 void TextureCubeMap::setCompressedImage(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels) 751 { 752 int face = CubeFaceIndex(target); 753 754 if(image[face][level]) 755 { 756 image[face][level]->unbind(); 757 } 758 759 image[face][level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE); 760 761 if(!image[face][level]) 762 { 763 return error(GL_OUT_OF_MEMORY); 764 } 765 766 Texture::setCompressedImage(imageSize, pixels, image[face][level]); 767 } 768 769 void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) 770 { 771 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, image[CubeFaceIndex(target)][level]); 772 } 773 774 void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels) 775 { 776 Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, image[CubeFaceIndex(target)][level]); 777 } 778 779 // Tests for cube map sampling completeness. 780 bool TextureCubeMap::isSamplerComplete() const 781 { 782 for(int face = 0; face < 6; face++) 783 { 784 if(!image[face][0]) 785 { 786 return false; 787 } 788 } 789 790 int size = image[0][0]->getWidth(); 791 792 if(size <= 0) 793 { 794 return false; 795 } 796 797 if(!isMipmapFiltered()) 798 { 799 if(!isCubeComplete()) 800 { 801 return false; 802 } 803 } 804 else 805 { 806 if(!isMipmapCubeComplete()) // Also tests for isCubeComplete() 807 { 808 return false; 809 } 810 } 811 812 return true; 813 } 814 815 // Tests for cube texture completeness. 816 bool TextureCubeMap::isCubeComplete() const 817 { 818 if(image[0][0]->getWidth() <= 0 || image[0][0]->getHeight() != image[0][0]->getWidth()) 819 { 820 return false; 821 } 822 823 for(unsigned int face = 1; face < 6; face++) 824 { 825 if(image[face][0]->getWidth() != image[0][0]->getWidth() || 826 image[face][0]->getWidth() != image[0][0]->getHeight() || 827 image[face][0]->getFormat() != image[0][0]->getFormat() || 828 image[face][0]->getType() != image[0][0]->getType()) 829 { 830 return false; 831 } 832 } 833 834 return true; 835 } 836 837 bool TextureCubeMap::isMipmapCubeComplete() const 838 { 839 if(!isCubeComplete()) 840 { 841 return false; 842 } 843 844 GLsizei size = image[0][0]->getWidth(); 845 int q = log2(size); 846 847 for(int face = 0; face < 6; face++) 848 { 849 for(int level = 1; level <= q; level++) 850 { 851 if(!image[face][level]) 852 { 853 return false; 854 } 855 856 if(image[face][level]->getFormat() != image[0][0]->getFormat()) 857 { 858 return false; 859 } 860 861 if(image[face][level]->getType() != image[0][0]->getType()) 862 { 863 return false; 864 } 865 866 if(image[face][level]->getWidth() != std::max(1, size >> level)) 867 { 868 return false; 869 } 870 } 871 } 872 873 return true; 874 } 875 876 bool TextureCubeMap::isCompressed(GLenum target, GLint level) const 877 { 878 return IsCompressed(getFormat(target, level)); 879 } 880 881 bool TextureCubeMap::isDepth(GLenum target, GLint level) const 882 { 883 return IsDepthTexture(getFormat(target, level)); 884 } 885 886 void TextureCubeMap::setImage(GLenum target, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) 887 { 888 int face = CubeFaceIndex(target); 889 890 if(image[face][level]) 891 { 892 image[face][level]->unbind(); 893 } 894 895 image[face][level] = new Image(this, width, height, format, type); 896 897 if(!image[face][level]) 898 { 899 return error(GL_OUT_OF_MEMORY); 900 } 901 902 Texture::setImage(format, type, unpackAlignment, pixels, image[face][level]); 903 } 904 905 void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) 906 { 907 Image *renderTarget = source->getRenderTarget(); 908 909 if(!renderTarget) 910 { 911 ERR("Failed to retrieve the render target."); 912 return error(GL_OUT_OF_MEMORY); 913 } 914 915 int face = CubeFaceIndex(target); 916 917 if(image[face][level]) 918 { 919 image[face][level]->unbind(); 920 } 921 922 image[face][level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE); 923 924 if(!image[face][level]) 925 { 926 return error(GL_OUT_OF_MEMORY); 927 } 928 929 if(width != 0 && height != 0) 930 { 931 sw::Rect sourceRect = {x, y, x + width, y + height}; 932 sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight()); 933 934 copy(renderTarget, sourceRect, format, 0, 0, image[face][level]); 935 } 936 937 renderTarget->release(); 938 } 939 940 Image *TextureCubeMap::getImage(int face, unsigned int level) 941 { 942 return image[face][level]; 943 } 944 945 Image *TextureCubeMap::getImage(GLenum face, unsigned int level) 946 { 947 return image[CubeFaceIndex(face)][level]; 948 } 949 950 void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) 951 { 952 int face = CubeFaceIndex(target); 953 954 if(!image[face][level]) 955 { 956 return error(GL_INVALID_OPERATION); 957 } 958 959 GLsizei size = image[face][level]->getWidth(); 960 961 if(xoffset + width > size || yoffset + height > size) 962 { 963 return error(GL_INVALID_VALUE); 964 } 965 966 Image *renderTarget = source->getRenderTarget(); 967 968 if(!renderTarget) 969 { 970 ERR("Failed to retrieve the render target."); 971 return error(GL_OUT_OF_MEMORY); 972 } 973 974 sw::Rect sourceRect = {x, y, x + width, y + height}; 975 sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight()); 976 977 copy(renderTarget, sourceRect, image[face][level]->getFormat(), xoffset, yoffset, image[face][level]); 978 979 renderTarget->release(); 980 } 981 982 void TextureCubeMap::generateMipmaps() 983 { 984 if(!isCubeComplete()) 985 { 986 return error(GL_INVALID_OPERATION); 987 } 988 989 unsigned int q = log2(image[0][0]->getWidth()); 990 991 for(unsigned int f = 0; f < 6; f++) 992 { 993 for(unsigned int i = 1; i <= q; i++) 994 { 995 if(image[f][i]) 996 { 997 image[f][i]->unbind(); 998 } 999 1000 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()); 1001 1002 if(!image[f][i]) 1003 { 1004 return error(GL_OUT_OF_MEMORY); 1005 } 1006 1007 getDevice()->stretchRect(image[f][i - 1], 0, image[f][i], 0, true); 1008 } 1009 } 1010 } 1011 1012 Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target) 1013 { 1014 if(!IsCubemapTextureTarget(target)) 1015 { 1016 return error(GL_INVALID_OPERATION, (Renderbuffer *)nullptr); 1017 } 1018 1019 int face = CubeFaceIndex(target); 1020 1021 if(!mFaceProxies[face]) 1022 { 1023 mFaceProxies[face] = new Renderbuffer(name, new RenderbufferTextureCubeMap(this, target)); 1024 } 1025 1026 return mFaceProxies[face]; 1027 } 1028 1029 Image *TextureCubeMap::getRenderTarget(GLenum target, unsigned int level) 1030 { 1031 ASSERT(IsCubemapTextureTarget(target)); 1032 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS); 1033 1034 int face = CubeFaceIndex(target); 1035 1036 if(image[face][level]) 1037 { 1038 image[face][level]->addRef(); 1039 } 1040 1041 return image[face][level]; 1042 } 1043 1044 } 1045