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. [OpenGL ES 2.0.24] section 3.7 page 63. 18 19 #include "Texture.h" 20 21 #include "main.h" 22 #include "mathutil.h" 23 #include "Framebuffer.h" 24 #include "Device.hpp" 25 #include "libEGL/Display.h" 26 #include "common/Surface.hpp" 27 #include "common/debug.h" 28 29 #include <algorithm> 30 31 namespace es1 32 { 33 34 Texture::Texture(GLuint name) : egl::Texture(name) 35 { 36 mMinFilter = GL_NEAREST_MIPMAP_LINEAR; 37 mMagFilter = GL_LINEAR; 38 mWrapS = GL_REPEAT; 39 mWrapT = GL_REPEAT; 40 mMaxAnisotropy = 1.0f; 41 generateMipmap = GL_FALSE; 42 cropRectU = 0; 43 cropRectV = 0; 44 cropRectW = 0; 45 cropRectH = 0; 46 47 resource = new sw::Resource(0); 48 } 49 50 Texture::~Texture() 51 { 52 resource->destruct(); 53 } 54 55 sw::Resource *Texture::getResource() const 56 { 57 return resource; 58 } 59 60 // Returns true on successful filter state update (valid enum parameter) 61 bool Texture::setMinFilter(GLenum filter) 62 { 63 switch(filter) 64 { 65 case GL_NEAREST_MIPMAP_NEAREST: 66 case GL_LINEAR_MIPMAP_NEAREST: 67 case GL_NEAREST_MIPMAP_LINEAR: 68 case GL_LINEAR_MIPMAP_LINEAR: 69 if(getTarget() == GL_TEXTURE_EXTERNAL_OES) 70 { 71 return false; 72 } 73 // Fall through 74 case GL_NEAREST: 75 case GL_LINEAR: 76 mMinFilter = filter; 77 return true; 78 default: 79 return false; 80 } 81 } 82 83 // Returns true on successful filter state update (valid enum parameter) 84 bool Texture::setMagFilter(GLenum filter) 85 { 86 switch(filter) 87 { 88 case GL_NEAREST: 89 case GL_LINEAR: 90 mMagFilter = filter; 91 return true; 92 default: 93 return false; 94 } 95 } 96 97 // Returns true on successful wrap state update (valid enum parameter) 98 bool Texture::setWrapS(GLenum wrap) 99 { 100 switch(wrap) 101 { 102 case GL_REPEAT: 103 case GL_MIRRORED_REPEAT_OES: 104 if(getTarget() == GL_TEXTURE_EXTERNAL_OES) 105 { 106 return false; 107 } 108 // Fall through 109 case GL_CLAMP_TO_EDGE: 110 mWrapS = wrap; 111 return true; 112 default: 113 return false; 114 } 115 } 116 117 // Returns true on successful wrap state update (valid enum parameter) 118 bool Texture::setWrapT(GLenum wrap) 119 { 120 switch(wrap) 121 { 122 case GL_REPEAT: 123 case GL_MIRRORED_REPEAT_OES: 124 if(getTarget() == GL_TEXTURE_EXTERNAL_OES) 125 { 126 return false; 127 } 128 // Fall through 129 case GL_CLAMP_TO_EDGE: 130 mWrapT = wrap; 131 return true; 132 default: 133 return false; 134 } 135 } 136 137 // Returns true on successful max anisotropy update (valid anisotropy value) 138 bool Texture::setMaxAnisotropy(float textureMaxAnisotropy) 139 { 140 textureMaxAnisotropy = std::min(textureMaxAnisotropy, MAX_TEXTURE_MAX_ANISOTROPY); 141 142 if(textureMaxAnisotropy < 1.0f) 143 { 144 return false; 145 } 146 147 if(mMaxAnisotropy != textureMaxAnisotropy) 148 { 149 mMaxAnisotropy = textureMaxAnisotropy; 150 } 151 152 return true; 153 } 154 155 void Texture::setGenerateMipmap(GLboolean enable) 156 { 157 generateMipmap = enable; 158 } 159 160 void Texture::setCropRect(GLint u, GLint v, GLint w, GLint h) 161 { 162 cropRectU = u; 163 cropRectV = v; 164 cropRectW = w; 165 cropRectH = h; 166 } 167 168 GLenum Texture::getMinFilter() const 169 { 170 return mMinFilter; 171 } 172 173 GLenum Texture::getMagFilter() const 174 { 175 return mMagFilter; 176 } 177 178 GLenum Texture::getWrapS() const 179 { 180 return mWrapS; 181 } 182 183 GLenum Texture::getWrapT() const 184 { 185 return mWrapT; 186 } 187 188 GLfloat Texture::getMaxAnisotropy() const 189 { 190 return mMaxAnisotropy; 191 } 192 193 GLboolean Texture::getGenerateMipmap() const 194 { 195 return generateMipmap; 196 } 197 198 GLint Texture::getCropRectU() const 199 { 200 return cropRectU; 201 } 202 203 GLint Texture::getCropRectV() const 204 { 205 return cropRectV; 206 } 207 208 GLint Texture::getCropRectW() const 209 { 210 return cropRectW; 211 } 212 213 GLint Texture::getCropRectH() const 214 { 215 return cropRectH; 216 } 217 218 egl::Image *Texture::createSharedImage(GLenum target, unsigned int level) 219 { 220 egl::Image *image = getRenderTarget(target, level); // Increments reference count 221 222 if(image) 223 { 224 image->markShared(); 225 } 226 227 return image; 228 } 229 230 void Texture::setImage(GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, egl::Image *image) 231 { 232 if(pixels && image) 233 { 234 gl::PixelStorageModes unpackParameters; 235 unpackParameters.alignment = unpackAlignment; 236 image->loadImageData(0, 0, 0, image->getWidth(), image->getHeight(), 1, format, type, unpackParameters, pixels); 237 } 238 } 239 240 void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, egl::Image *image) 241 { 242 if(pixels && image && (imageSize > 0)) // imageSize's correlation to width and height is already validated with gl::ComputeCompressedSize() at the API level 243 { 244 image->loadCompressedData(0, 0, 0, image->getWidth(), image->getHeight(), 1, imageSize, pixels); 245 } 246 } 247 248 void Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, egl::Image *image) 249 { 250 if(!image) 251 { 252 return error(GL_INVALID_OPERATION); 253 } 254 255 if(pixels) 256 { 257 gl::PixelStorageModes unpackParameters; 258 unpackParameters.alignment = unpackAlignment; 259 image->loadImageData(xoffset, yoffset, 0, width, height, 1, format, type, unpackParameters, pixels); 260 } 261 } 262 263 void Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, egl::Image *image) 264 { 265 if(!image) 266 { 267 return error(GL_INVALID_OPERATION); 268 } 269 270 if(pixels && (imageSize > 0)) // imageSize's correlation to width and height is already validated with gl::ComputeCompressedSize() at the API level 271 { 272 image->loadCompressedData(xoffset, yoffset, 0, width, height, 1, imageSize, pixels); 273 } 274 } 275 276 bool Texture::copy(egl::Image *source, const sw::Rect &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, egl::Image *dest) 277 { 278 Device *device = getDevice(); 279 280 sw::SliceRect destRect(xoffset, yoffset, xoffset + (sourceRect.x1 - sourceRect.x0), yoffset + (sourceRect.y1 - sourceRect.y0), 0); 281 sw::SliceRect sourceSliceRect(sourceRect); 282 bool success = device->stretchRect(source, &sourceSliceRect, dest, &destRect, false); 283 284 if(!success) 285 { 286 return error(GL_OUT_OF_MEMORY, false); 287 } 288 289 return true; 290 } 291 292 bool Texture::isMipmapFiltered() const 293 { 294 switch(mMinFilter) 295 { 296 case GL_NEAREST: 297 case GL_LINEAR: 298 return false; 299 case GL_NEAREST_MIPMAP_NEAREST: 300 case GL_LINEAR_MIPMAP_NEAREST: 301 case GL_NEAREST_MIPMAP_LINEAR: 302 case GL_LINEAR_MIPMAP_LINEAR: 303 return true; 304 default: UNREACHABLE(mMinFilter); 305 } 306 307 return false; 308 } 309 310 Texture2D::Texture2D(GLuint name) : Texture(name) 311 { 312 for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) 313 { 314 image[i] = nullptr; 315 } 316 317 mSurface = nullptr; 318 319 mColorbufferProxy = nullptr; 320 mProxyRefs = 0; 321 } 322 323 Texture2D::~Texture2D() 324 { 325 for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) 326 { 327 if(image[i]) 328 { 329 image[i]->unbind(this); 330 image[i] = nullptr; 331 } 332 } 333 334 if(mSurface) 335 { 336 mSurface->setBoundTexture(nullptr); 337 mSurface = nullptr; 338 } 339 340 mColorbufferProxy = nullptr; 341 } 342 343 // We need to maintain a count of references to renderbuffers acting as 344 // proxies for this texture, so that we do not attempt to use a pointer 345 // to a renderbuffer proxy which has been deleted. 346 void Texture2D::addProxyRef(const Renderbuffer *proxy) 347 { 348 mProxyRefs++; 349 } 350 351 void Texture2D::releaseProxy(const Renderbuffer *proxy) 352 { 353 if(mProxyRefs > 0) 354 { 355 mProxyRefs--; 356 } 357 358 if(mProxyRefs == 0) 359 { 360 mColorbufferProxy = nullptr; 361 } 362 } 363 364 void Texture2D::sweep() 365 { 366 int imageCount = 0; 367 368 for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) 369 { 370 if(image[i] && image[i]->isChildOf(this)) 371 { 372 if(!image[i]->hasSingleReference()) 373 { 374 return; 375 } 376 377 imageCount++; 378 } 379 } 380 381 if(imageCount == referenceCount) 382 { 383 destroy(); 384 } 385 } 386 387 GLenum Texture2D::getTarget() const 388 { 389 return GL_TEXTURE_2D; 390 } 391 392 GLsizei Texture2D::getWidth(GLenum target, GLint level) const 393 { 394 ASSERT(target == GL_TEXTURE_2D); 395 return image[level] ? image[level]->getWidth() : 0; 396 } 397 398 GLsizei Texture2D::getHeight(GLenum target, GLint level) const 399 { 400 ASSERT(target == GL_TEXTURE_2D); 401 return image[level] ? image[level]->getHeight() : 0; 402 } 403 404 GLint Texture2D::getFormat(GLenum target, GLint level) const 405 { 406 ASSERT(target == GL_TEXTURE_2D); 407 return image[level] ? image[level]->getFormat() : GL_NONE; 408 } 409 410 int Texture2D::getTopLevel() const 411 { 412 ASSERT(isSamplerComplete()); 413 int level = 0; 414 415 while(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[level]) 416 { 417 level++; 418 } 419 420 return level - 1; 421 } 422 423 void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLint internalformat, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) 424 { 425 if(image[level]) 426 { 427 image[level]->release(); 428 } 429 430 image[level] = egl::Image::create(this, width, height, internalformat); 431 432 if(!image[level]) 433 { 434 return error(GL_OUT_OF_MEMORY); 435 } 436 437 Texture::setImage(format, type, unpackAlignment, pixels, image[level]); 438 } 439 440 void Texture2D::bindTexImage(gl::Surface *surface) 441 { 442 for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) 443 { 444 if(image[level]) 445 { 446 image[level]->release(); 447 image[level] = nullptr; 448 } 449 } 450 451 image[0] = surface->getRenderTarget(); 452 453 mSurface = surface; 454 mSurface->setBoundTexture(this); 455 } 456 457 void Texture2D::releaseTexImage() 458 { 459 for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) 460 { 461 if(image[level]) 462 { 463 image[level]->release(); 464 image[level] = nullptr; 465 } 466 } 467 } 468 469 void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels) 470 { 471 if(image[level]) 472 { 473 image[level]->release(); 474 } 475 476 image[level] = egl::Image::create(this, width, height, format); 477 478 if(!image[level]) 479 { 480 return error(GL_OUT_OF_MEMORY); 481 } 482 483 Texture::setCompressedImage(imageSize, pixels, image[level]); 484 } 485 486 void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) 487 { 488 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, image[level]); 489 } 490 491 void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels) 492 { 493 Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, image[level]); 494 } 495 496 void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) 497 { 498 egl::Image *renderTarget = source->getRenderTarget(); 499 500 if(!renderTarget) 501 { 502 ERR("Failed to retrieve the render target."); 503 return error(GL_OUT_OF_MEMORY); 504 } 505 506 if(image[level]) 507 { 508 image[level]->release(); 509 } 510 511 image[level] = egl::Image::create(this, width, height, format); 512 513 if(!image[level]) 514 { 515 return error(GL_OUT_OF_MEMORY); 516 } 517 518 if(width != 0 && height != 0) 519 { 520 sw::Rect sourceRect = {x, y, x + width, y + height}; 521 sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight()); 522 523 copy(renderTarget, sourceRect, format, 0, 0, image[level]); 524 } 525 526 renderTarget->release(); 527 } 528 529 void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) 530 { 531 if(!image[level]) 532 { 533 return error(GL_INVALID_OPERATION); 534 } 535 536 if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight()) 537 { 538 return error(GL_INVALID_VALUE); 539 } 540 541 egl::Image *renderTarget = source->getRenderTarget(); 542 543 if(!renderTarget) 544 { 545 ERR("Failed to retrieve the render target."); 546 return error(GL_OUT_OF_MEMORY); 547 } 548 549 sw::Rect sourceRect = {x, y, x + width, y + height}; 550 sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight()); 551 552 copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, image[level]); 553 554 renderTarget->release(); 555 } 556 557 void Texture2D::setSharedImage(egl::Image *sharedImage) 558 { 559 sharedImage->addRef(); 560 561 if(image[0]) 562 { 563 image[0]->release(); 564 } 565 566 image[0] = sharedImage; 567 } 568 569 // Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85. 570 bool Texture2D::isSamplerComplete() const 571 { 572 if(!image[0]) 573 { 574 return false; 575 } 576 577 GLsizei width = image[0]->getWidth(); 578 GLsizei height = image[0]->getHeight(); 579 580 if(width <= 0 || height <= 0) 581 { 582 return false; 583 } 584 585 if(isMipmapFiltered()) 586 { 587 if(!generateMipmap && !isMipmapComplete()) 588 { 589 return false; 590 } 591 } 592 593 return true; 594 } 595 596 // Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81. 597 bool Texture2D::isMipmapComplete() const 598 { 599 GLsizei width = image[0]->getWidth(); 600 GLsizei height = image[0]->getHeight(); 601 602 int q = log2(std::max(width, height)); 603 604 for(int level = 1; level <= q; level++) 605 { 606 if(!image[level]) 607 { 608 return false; 609 } 610 611 if(image[level]->getFormat() != image[0]->getFormat()) 612 { 613 return false; 614 } 615 616 if(image[level]->getWidth() != std::max(1, width >> level)) 617 { 618 return false; 619 } 620 621 if(image[level]->getHeight() != std::max(1, height >> level)) 622 { 623 return false; 624 } 625 } 626 627 return true; 628 } 629 630 bool Texture2D::isCompressed(GLenum target, GLint level) const 631 { 632 return IsCompressed(getFormat(target, level)); 633 } 634 635 bool Texture2D::isDepth(GLenum target, GLint level) const 636 { 637 return IsDepthTexture(getFormat(target, level)); 638 } 639 640 void Texture2D::generateMipmaps() 641 { 642 if(!image[0]) 643 { 644 return; // FIXME: error? 645 } 646 647 unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight())); 648 649 for(unsigned int i = 1; i <= q; i++) 650 { 651 if(image[i]) 652 { 653 image[i]->release(); 654 } 655 656 image[i] = egl::Image::create(this, std::max(image[0]->getWidth() >> i, 1), std::max(image[0]->getHeight() >> i, 1), image[0]->getFormat()); 657 658 if(!image[i]) 659 { 660 return error(GL_OUT_OF_MEMORY); 661 } 662 663 getDevice()->stretchRect(image[i - 1], 0, image[i], 0, true); 664 } 665 } 666 667 void Texture2D::autoGenerateMipmaps() 668 { 669 if(generateMipmap && image[0]->hasDirtyContents()) 670 { 671 generateMipmaps(); 672 image[0]->markContentsClean(); 673 } 674 } 675 676 egl::Image *Texture2D::getImage(unsigned int level) 677 { 678 return image[level]; 679 } 680 681 Renderbuffer *Texture2D::getRenderbuffer(GLenum target) 682 { 683 if(target != GL_TEXTURE_2D) 684 { 685 return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr); 686 } 687 688 if(!mColorbufferProxy) 689 { 690 mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2D(this)); 691 } 692 693 return mColorbufferProxy; 694 } 695 696 egl::Image *Texture2D::getRenderTarget(GLenum target, unsigned int level) 697 { 698 ASSERT(target == GL_TEXTURE_2D); 699 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS); 700 701 if(image[level]) 702 { 703 image[level]->addRef(); 704 } 705 706 return image[level]; 707 } 708 709 bool Texture2D::isShared(GLenum target, unsigned int level) const 710 { 711 ASSERT(target == GL_TEXTURE_2D); 712 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS); 713 714 if(mSurface) // Bound to an EGLSurface 715 { 716 return true; 717 } 718 719 if(!image[level]) 720 { 721 return false; 722 } 723 724 return image[level]->isShared(); 725 } 726 727 TextureExternal::TextureExternal(GLuint name) : Texture2D(name) 728 { 729 mMinFilter = GL_LINEAR; 730 mMagFilter = GL_LINEAR; 731 mWrapS = GL_CLAMP_TO_EDGE; 732 mWrapT = GL_CLAMP_TO_EDGE; 733 } 734 735 TextureExternal::~TextureExternal() 736 { 737 } 738 739 GLenum TextureExternal::getTarget() const 740 { 741 return GL_TEXTURE_EXTERNAL_OES; 742 } 743 744 } 745 746 egl::Image *createBackBuffer(int width, int height, sw::Format format, int multiSampleDepth) 747 { 748 if(width > es1::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE || height > es1::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE) 749 { 750 ERR("Invalid parameters: %dx%d", width, height); 751 return nullptr; 752 } 753 754 GLenum internalformat = sw2es::ConvertBackBufferFormat(format); 755 756 return egl::Image::create(width, height, internalformat, multiSampleDepth, false); 757 } 758 759 egl::Image *createDepthStencil(int width, int height, sw::Format format, int multiSampleDepth) 760 { 761 if(width > es1::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE || height > es1::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE) 762 { 763 ERR("Invalid parameters: %dx%d", width, height); 764 return nullptr; 765 } 766 767 bool lockable = true; 768 769 switch(format) 770 { 771 // case sw::FORMAT_D15S1: 772 case sw::FORMAT_D24S8: 773 case sw::FORMAT_D24X8: 774 // case sw::FORMAT_D24X4S4: 775 case sw::FORMAT_D24FS8: 776 case sw::FORMAT_D32: 777 case sw::FORMAT_D16: 778 lockable = false; 779 break; 780 // case sw::FORMAT_S8_LOCKABLE: 781 // case sw::FORMAT_D16_LOCKABLE: 782 case sw::FORMAT_D32F_LOCKABLE: 783 // case sw::FORMAT_D32_LOCKABLE: 784 case sw::FORMAT_DF24S8: 785 case sw::FORMAT_DF16S8: 786 lockable = true; 787 break; 788 default: 789 UNREACHABLE(format); 790 } 791 792 GLenum internalformat = sw2es::ConvertDepthStencilFormat(format); 793 794 egl::Image *surface = egl::Image::create(width, height, internalformat, multiSampleDepth, lockable); 795 796 if(!surface) 797 { 798 ERR("Out of memory"); 799 return nullptr; 800 } 801 802 return surface; 803 } 804