Texture.cpp revision bf7a8145635e7dc6625596be127733ac7418cf21
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 30namespace gl 31{ 32 33Texture::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 45Texture::~Texture() 46{ 47 resource->destruct(); 48} 49 50sw::Resource *Texture::getResource() const 51{ 52 return resource; 53} 54 55// Returns true on successful filter state update (valid enum parameter) 56bool 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) 74bool 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) 88bool 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) 104bool 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) 120bool 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 137bool Texture::setMaxLevel(int level) 138{ 139 if(level < 0) 140 { 141 return false; 142 } 143 144 mMaxLevel = level; 145 146 return true; 147} 148 149GLenum Texture::getMinFilter() const 150{ 151 return mMinFilter; 152} 153 154GLenum Texture::getMagFilter() const 155{ 156 return mMagFilter; 157} 158 159GLenum Texture::getWrapS() const 160{ 161 return mWrapS; 162} 163 164GLenum Texture::getWrapT() const 165{ 166 return mWrapT; 167} 168 169GLfloat Texture::getMaxAnisotropy() const 170{ 171 return mMaxAnisotropy; 172} 173 174void 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 182void 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 190void 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 218void 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 241bool 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 257bool 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 275Texture2D::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 286Texture2D::~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. 303void Texture2D::addProxyRef(const Renderbuffer *proxy) 304{ 305 mProxyRefs++; 306} 307 308void 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 321GLenum Texture2D::getTarget() const 322{ 323 return GL_TEXTURE_2D; 324} 325 326GLsizei 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 332GLsizei 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 338GLenum 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 344GLenum 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 350sw::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 356int Texture2D::getLevelCount() 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 369void 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 386void 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 403void 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 408void 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 413void 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 446void 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 474void 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. 487bool 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. 514bool 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 552bool Texture2D::isCompressed(GLenum target, GLint level) const 553{ 554 return IsCompressed(getFormat(target, level)); 555} 556 557bool Texture2D::isDepth(GLenum target, GLint level) const 558{ 559 return IsDepthTexture(getFormat(target, level)); 560} 561 562void 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 589Image *Texture2D::getImage(unsigned int level) 590{ 591 return image[level]; 592} 593 594Renderbuffer *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 609Image *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 622TextureCubeMap::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 639TextureCubeMap::~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. 664void 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 675void 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 694GLenum TextureCubeMap::getTarget() const 695{ 696 return GL_TEXTURE_CUBE_MAP; 697} 698 699GLsizei 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 705GLsizei 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 711GLenum 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 717GLenum 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 723sw::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 729int TextureCubeMap::getLevelCount() 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 742void 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 761void 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 766void 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. 772bool 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. 808bool 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 829bool 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 868bool TextureCubeMap::isCompressed(GLenum target, GLint level) const 869{ 870 return IsCompressed(getFormat(target, level)); 871} 872 873bool TextureCubeMap::isDepth(GLenum target, GLint level) const 874{ 875 return IsDepthTexture(getFormat(target, level)); 876} 877 878void 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 897void 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 932Image *TextureCubeMap::getImage(int face, unsigned int level) 933{ 934 return image[face][level]; 935} 936 937Image *TextureCubeMap::getImage(GLenum face, unsigned int level) 938{ 939 return image[CubeFaceIndex(face)][level]; 940} 941 942void 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 974void 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 1004Renderbuffer *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 1021Image *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