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 "libEGL/Surface.h"
27 #include "common/debug.h"
28 
29 #include <algorithm>
30 
31 namespace es1
32 {
33 
Texture(GLuint name)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 
~Texture()50 Texture::~Texture()
51 {
52 	resource->destruct();
53 }
54 
getResource() const55 sw::Resource *Texture::getResource() const
56 {
57 	return resource;
58 }
59 
60 // Returns true on successful filter state update (valid enum parameter)
setMinFilter(GLenum filter)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)
setMagFilter(GLenum filter)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)
setWrapS(GLenum wrap)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)
setWrapT(GLenum wrap)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)
setMaxAnisotropy(float textureMaxAnisotropy)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 
setGenerateMipmap(GLboolean enable)155 void Texture::setGenerateMipmap(GLboolean enable)
156 {
157 	generateMipmap = enable;
158 }
159 
setCropRect(GLint u,GLint v,GLint w,GLint h)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 
getMinFilter() const168 GLenum Texture::getMinFilter() const
169 {
170 	return mMinFilter;
171 }
172 
getMagFilter() const173 GLenum Texture::getMagFilter() const
174 {
175 	return mMagFilter;
176 }
177 
getWrapS() const178 GLenum Texture::getWrapS() const
179 {
180 	return mWrapS;
181 }
182 
getWrapT() const183 GLenum Texture::getWrapT() const
184 {
185 	return mWrapT;
186 }
187 
getMaxAnisotropy() const188 GLfloat Texture::getMaxAnisotropy() const
189 {
190 	return mMaxAnisotropy;
191 }
192 
getGenerateMipmap() const193 GLboolean Texture::getGenerateMipmap() const
194 {
195 	return generateMipmap;
196 }
197 
getCropRectU() const198 GLint Texture::getCropRectU() const
199 {
200 	return cropRectU;
201 }
202 
getCropRectV() const203 GLint Texture::getCropRectV() const
204 {
205 	return cropRectV;
206 }
207 
getCropRectW() const208 GLint Texture::getCropRectW() const
209 {
210 	return cropRectW;
211 }
212 
getCropRectH() const213 GLint Texture::getCropRectH() const
214 {
215 	return cropRectH;
216 }
217 
createSharedImage(GLenum target,unsigned int level)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 
setImage(GLenum format,GLenum type,GLint unpackAlignment,const void * pixels,egl::Image * image)230 void Texture::setImage(GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, egl::Image *image)
231 {
232 	if(pixels && image)
233 	{
234 		egl::Image::UnpackInfo unpackInfo;
235 		unpackInfo.alignment = unpackAlignment;
236 		image->loadImageData(0, 0, 0, image->getWidth(), image->getHeight(), 1, format, type, unpackInfo, pixels);
237 	}
238 }
239 
setCompressedImage(GLsizei imageSize,const void * pixels,egl::Image * image)240 void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, egl::Image *image)
241 {
242 	if(pixels && image)
243 	{
244 		image->loadCompressedData(0, 0, 0, image->getWidth(), image->getHeight(), 1, imageSize, pixels);
245 	}
246 }
247 
subImage(GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLenum type,GLint unpackAlignment,const void * pixels,egl::Image * image)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(width + xoffset > image->getWidth() || height + yoffset > image->getHeight())
256 	{
257 		return error(GL_INVALID_VALUE);
258 	}
259 
260 	if(IsCompressed(image->getFormat()))
261 	{
262 		return error(GL_INVALID_OPERATION);
263 	}
264 
265 	if(format != image->getFormat())
266 	{
267 		return error(GL_INVALID_OPERATION);
268 	}
269 
270 	if(pixels)
271 	{
272 		egl::Image::UnpackInfo unpackInfo;
273 		unpackInfo.alignment = unpackAlignment;
274 		image->loadImageData(xoffset, yoffset, 0, width, height, 1, format, type, unpackInfo, pixels);
275 	}
276 }
277 
subImageCompressed(GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLsizei imageSize,const void * pixels,egl::Image * image)278 void Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, egl::Image *image)
279 {
280 	if(!image)
281 	{
282 		return error(GL_INVALID_OPERATION);
283 	}
284 
285 	if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight())
286 	{
287 		return error(GL_INVALID_VALUE);
288 	}
289 
290 	if(format != image->getFormat())
291 	{
292 		return error(GL_INVALID_OPERATION);
293 	}
294 
295 	if(pixels)
296 	{
297 		image->loadCompressedData(xoffset, yoffset, 0, width, height, 1, imageSize, pixels);
298 	}
299 }
300 
copy(egl::Image * source,const sw::Rect & sourceRect,GLenum destFormat,GLint xoffset,GLint yoffset,egl::Image * dest)301 bool Texture::copy(egl::Image *source, const sw::Rect &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, egl::Image *dest)
302 {
303 	Device *device = getDevice();
304 
305 	sw::SliceRect destRect(xoffset, yoffset, xoffset + (sourceRect.x1 - sourceRect.x0), yoffset + (sourceRect.y1 - sourceRect.y0), 0);
306 	sw::SliceRect sourceSliceRect(sourceRect);
307 	bool success = device->stretchRect(source, &sourceSliceRect, dest, &destRect, false);
308 
309 	if(!success)
310 	{
311 		return error(GL_OUT_OF_MEMORY, false);
312 	}
313 
314 	return true;
315 }
316 
isMipmapFiltered() const317 bool Texture::isMipmapFiltered() const
318 {
319 	switch(mMinFilter)
320 	{
321 	case GL_NEAREST:
322 	case GL_LINEAR:
323 		return false;
324 	case GL_NEAREST_MIPMAP_NEAREST:
325 	case GL_LINEAR_MIPMAP_NEAREST:
326 	case GL_NEAREST_MIPMAP_LINEAR:
327 	case GL_LINEAR_MIPMAP_LINEAR:
328 		return true;
329 	default: UNREACHABLE(mMinFilter);
330 	}
331 
332 	return false;
333 }
334 
Texture2D(GLuint name)335 Texture2D::Texture2D(GLuint name) : Texture(name)
336 {
337 	for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
338 	{
339 		image[i] = nullptr;
340 	}
341 
342 	mSurface = nullptr;
343 
344 	mColorbufferProxy = nullptr;
345 	mProxyRefs = 0;
346 }
347 
~Texture2D()348 Texture2D::~Texture2D()
349 {
350 	resource->lock(sw::DESTRUCT);
351 
352 	for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
353 	{
354 		if(image[i])
355 		{
356 			image[i]->unbind(this);
357 			image[i] = nullptr;
358 		}
359 	}
360 
361 	resource->unlock();
362 
363 	if(mSurface)
364 	{
365 		mSurface->setBoundTexture(nullptr);
366 		mSurface = nullptr;
367 	}
368 
369 	mColorbufferProxy = nullptr;
370 }
371 
372 // We need to maintain a count of references to renderbuffers acting as
373 // proxies for this texture, so that we do not attempt to use a pointer
374 // to a renderbuffer proxy which has been deleted.
addProxyRef(const Renderbuffer * proxy)375 void Texture2D::addProxyRef(const Renderbuffer *proxy)
376 {
377 	mProxyRefs++;
378 }
379 
releaseProxy(const Renderbuffer * proxy)380 void Texture2D::releaseProxy(const Renderbuffer *proxy)
381 {
382 	if(mProxyRefs > 0)
383 	{
384 		mProxyRefs--;
385 	}
386 
387 	if(mProxyRefs == 0)
388 	{
389 		mColorbufferProxy = nullptr;
390 	}
391 }
392 
sweep()393 void Texture2D::sweep()
394 {
395 	int imageCount = 0;
396 
397 	for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
398 	{
399 		if(image[i] && image[i]->isChildOf(this))
400 		{
401 			if(!image[i]->hasSingleReference())
402 			{
403 				return;
404 			}
405 
406 			imageCount++;
407 		}
408 	}
409 
410 	if(imageCount == referenceCount)
411 	{
412 		destroy();
413 	}
414 }
415 
getTarget() const416 GLenum Texture2D::getTarget() const
417 {
418 	return GL_TEXTURE_2D;
419 }
420 
getWidth(GLenum target,GLint level) const421 GLsizei Texture2D::getWidth(GLenum target, GLint level) const
422 {
423 	ASSERT(target == GL_TEXTURE_2D);
424 	return image[level] ? image[level]->getWidth() : 0;
425 }
426 
getHeight(GLenum target,GLint level) const427 GLsizei Texture2D::getHeight(GLenum target, GLint level) const
428 {
429 	ASSERT(target == GL_TEXTURE_2D);
430 	return image[level] ? image[level]->getHeight() : 0;
431 }
432 
getFormat(GLenum target,GLint level) const433 GLenum Texture2D::getFormat(GLenum target, GLint level) const
434 {
435 	ASSERT(target == GL_TEXTURE_2D);
436 	return image[level] ? image[level]->getFormat() : GL_NONE;
437 }
438 
getType(GLenum target,GLint level) const439 GLenum Texture2D::getType(GLenum target, GLint level) const
440 {
441 	ASSERT(target == GL_TEXTURE_2D);
442 	return image[level] ? image[level]->getType() : GL_NONE;
443 }
444 
getInternalFormat(GLenum target,GLint level) const445 sw::Format Texture2D::getInternalFormat(GLenum target, GLint level) const
446 {
447 	ASSERT(target == GL_TEXTURE_2D);
448 	return image[level] ? image[level]->getInternalFormat() : sw::FORMAT_NULL;
449 }
450 
getLevelCount() const451 int Texture2D::getLevelCount() const
452 {
453 	ASSERT(isSamplerComplete());
454 	int levels = 0;
455 
456 	while(levels < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[levels])
457 	{
458 		levels++;
459 	}
460 
461 	return levels;
462 }
463 
setImage(GLint level,GLsizei width,GLsizei height,GLenum format,GLenum type,GLint unpackAlignment,const void * pixels)464 void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
465 {
466 	if(image[level])
467 	{
468 		image[level]->release();
469 	}
470 
471 	image[level] = new egl::Image(this, width, height, format, type);
472 
473 	if(!image[level])
474 	{
475 		return error(GL_OUT_OF_MEMORY);
476 	}
477 
478 	Texture::setImage(format, type, unpackAlignment, pixels, image[level]);
479 }
480 
bindTexImage(egl::Surface * surface)481 void Texture2D::bindTexImage(egl::Surface *surface)
482 {
483 	GLenum format;
484 
485 	switch(surface->getInternalFormat())
486 	{
487 	case sw::FORMAT_A8R8G8B8:
488 		format = GL_BGRA_EXT;
489 		break;
490 	case sw::FORMAT_A8B8G8R8:
491 		format = GL_RGBA;
492 		break;
493 	case sw::FORMAT_X8B8G8R8:
494 	case sw::FORMAT_X8R8G8B8:
495 		format = GL_RGB;
496 		break;
497 	default:
498 		UNIMPLEMENTED();
499 		return;
500 	}
501 
502 	for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
503 	{
504 		if(image[level])
505 		{
506 			image[level]->release();
507 			image[level] = nullptr;
508 		}
509 	}
510 
511 	image[0] = surface->getRenderTarget();
512 
513 	mSurface = surface;
514 	mSurface->setBoundTexture(this);
515 }
516 
releaseTexImage()517 void Texture2D::releaseTexImage()
518 {
519 	for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
520 	{
521 		if(image[level])
522 		{
523 			image[level]->release();
524 			image[level] = nullptr;
525 		}
526 	}
527 }
528 
setCompressedImage(GLint level,GLenum format,GLsizei width,GLsizei height,GLsizei imageSize,const void * pixels)529 void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
530 {
531 	if(image[level])
532 	{
533 		image[level]->release();
534 	}
535 
536 	image[level] = new egl::Image(this, width, height, format, GL_UNSIGNED_BYTE);
537 
538 	if(!image[level])
539 	{
540 		return error(GL_OUT_OF_MEMORY);
541 	}
542 
543 	Texture::setCompressedImage(imageSize, pixels, image[level]);
544 }
545 
subImage(GLint level,GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLenum type,GLint unpackAlignment,const void * pixels)546 void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
547 {
548 	Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, image[level]);
549 }
550 
subImageCompressed(GLint level,GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLsizei imageSize,const void * pixels)551 void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
552 {
553 	Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, image[level]);
554 }
555 
copyImage(GLint level,GLenum format,GLint x,GLint y,GLsizei width,GLsizei height,Framebuffer * source)556 void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
557 {
558 	egl::Image *renderTarget = source->getRenderTarget();
559 
560 	if(!renderTarget)
561 	{
562 		ERR("Failed to retrieve the render target.");
563 		return error(GL_OUT_OF_MEMORY);
564 	}
565 
566 	if(image[level])
567 	{
568 		image[level]->release();
569 	}
570 
571 	image[level] = new egl::Image(this, width, height, format, GL_UNSIGNED_BYTE);
572 
573 	if(!image[level])
574 	{
575 		return error(GL_OUT_OF_MEMORY);
576 	}
577 
578 	if(width != 0 && height != 0)
579 	{
580 		sw::Rect sourceRect = {x, y, x + width, y + height};
581 		sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());
582 
583 		copy(renderTarget, sourceRect, format, 0, 0, image[level]);
584 	}
585 
586 	renderTarget->release();
587 }
588 
copySubImage(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLint x,GLint y,GLsizei width,GLsizei height,Framebuffer * source)589 void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
590 {
591 	if(!image[level])
592 	{
593 		return error(GL_INVALID_OPERATION);
594 	}
595 
596 	if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight())
597 	{
598 		return error(GL_INVALID_VALUE);
599 	}
600 
601 	egl::Image *renderTarget = source->getRenderTarget();
602 
603 	if(!renderTarget)
604 	{
605 		ERR("Failed to retrieve the render target.");
606 		return error(GL_OUT_OF_MEMORY);
607 	}
608 
609 	sw::Rect sourceRect = {x, y, x + width, y + height};
610 	sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());
611 
612 	copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, image[level]);
613 
614 	renderTarget->release();
615 }
616 
setImage(egl::Image * sharedImage)617 void Texture2D::setImage(egl::Image *sharedImage)
618 {
619 	sharedImage->addRef();
620 
621 	if(image[0])
622 	{
623 		image[0]->release();
624 	}
625 
626 	image[0] = sharedImage;
627 }
628 
629 // Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.
isSamplerComplete() const630 bool Texture2D::isSamplerComplete() const
631 {
632 	if(!image[0])
633 	{
634 		return false;
635 	}
636 
637 	GLsizei width = image[0]->getWidth();
638 	GLsizei height = image[0]->getHeight();
639 
640 	if(width <= 0 || height <= 0)
641 	{
642 		return false;
643 	}
644 
645 	if(isMipmapFiltered())
646 	{
647 		if(!generateMipmap && !isMipmapComplete())
648 		{
649 			return false;
650 		}
651 	}
652 
653 	return true;
654 }
655 
656 // Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
isMipmapComplete() const657 bool Texture2D::isMipmapComplete() const
658 {
659 	GLsizei width = image[0]->getWidth();
660 	GLsizei height = image[0]->getHeight();
661 
662 	int q = log2(std::max(width, height));
663 
664 	for(int level = 1; level <= q; level++)
665 	{
666 		if(!image[level])
667 		{
668 			return false;
669 		}
670 
671 		if(image[level]->getFormat() != image[0]->getFormat())
672 		{
673 			return false;
674 		}
675 
676 		if(image[level]->getType() != image[0]->getType())
677 		{
678 			return false;
679 		}
680 
681 		if(image[level]->getWidth() != std::max(1, width >> level))
682 		{
683 			return false;
684 		}
685 
686 		if(image[level]->getHeight() != std::max(1, height >> level))
687 		{
688 			return false;
689 		}
690 	}
691 
692 	return true;
693 }
694 
isCompressed(GLenum target,GLint level) const695 bool Texture2D::isCompressed(GLenum target, GLint level) const
696 {
697 	return IsCompressed(getFormat(target, level));
698 }
699 
isDepth(GLenum target,GLint level) const700 bool Texture2D::isDepth(GLenum target, GLint level) const
701 {
702 	return IsDepthTexture(getFormat(target, level));
703 }
704 
generateMipmaps()705 void Texture2D::generateMipmaps()
706 {
707 	if(!image[0])
708 	{
709 		return;   // FIXME: error?
710 	}
711 
712 	unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight()));
713 
714 	for(unsigned int i = 1; i <= q; i++)
715 	{
716 		if(image[i])
717 		{
718 			image[i]->release();
719 		}
720 
721 		image[i] = new egl::Image(this, std::max(image[0]->getWidth() >> i, 1), std::max(image[0]->getHeight() >> i, 1), image[0]->getFormat(), image[0]->getType());
722 
723 		if(!image[i])
724 		{
725 			return error(GL_OUT_OF_MEMORY);
726 		}
727 
728 		getDevice()->stretchRect(image[i - 1], 0, image[i], 0, true);
729 	}
730 }
731 
autoGenerateMipmaps()732 void Texture2D::autoGenerateMipmaps()
733 {
734 	if(generateMipmap && image[0]->hasDirtyMipmaps())
735 	{
736 		generateMipmaps();
737 		image[0]->cleanMipmaps();
738 	}
739 }
740 
getImage(unsigned int level)741 egl::Image *Texture2D::getImage(unsigned int level)
742 {
743 	return image[level];
744 }
745 
getRenderbuffer(GLenum target)746 Renderbuffer *Texture2D::getRenderbuffer(GLenum target)
747 {
748 	if(target != GL_TEXTURE_2D)
749 	{
750 		return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);
751 	}
752 
753 	if(!mColorbufferProxy)
754 	{
755 		mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2D(this));
756 	}
757 
758 	return mColorbufferProxy;
759 }
760 
getRenderTarget(GLenum target,unsigned int level)761 egl::Image *Texture2D::getRenderTarget(GLenum target, unsigned int level)
762 {
763 	ASSERT(target == GL_TEXTURE_2D);
764 	ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
765 
766 	if(image[level])
767 	{
768 		image[level]->addRef();
769 	}
770 
771 	return image[level];
772 }
773 
isShared(GLenum target,unsigned int level) const774 bool Texture2D::isShared(GLenum target, unsigned int level) const
775 {
776 	ASSERT(target == GL_TEXTURE_2D);
777 	ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
778 
779 	if(mSurface)   // Bound to an EGLSurface
780 	{
781 		return true;
782 	}
783 
784 	if(!image[level])
785 	{
786 		return false;
787 	}
788 
789 	return image[level]->isShared();
790 }
791 
TextureExternal(GLuint name)792 TextureExternal::TextureExternal(GLuint name) : Texture2D(name)
793 {
794 	mMinFilter = GL_LINEAR;
795 	mMagFilter = GL_LINEAR;
796 	mWrapS = GL_CLAMP_TO_EDGE;
797 	mWrapT = GL_CLAMP_TO_EDGE;
798 }
799 
~TextureExternal()800 TextureExternal::~TextureExternal()
801 {
802 }
803 
getTarget() const804 GLenum TextureExternal::getTarget() const
805 {
806 	return GL_TEXTURE_EXTERNAL_OES;
807 }
808 
809 }
810 
createBackBuffer(int width,int height,const egl::Config * config)811 egl::Image *createBackBuffer(int width, int height, const egl::Config *config)
812 {
813 	if(config)
814 	{
815 		return new egl::Image(width, height, config->mRenderTargetFormat, config->mSamples, false);
816 	}
817 
818 	return nullptr;
819 }
820 
createDepthStencil(unsigned int width,unsigned int height,sw::Format format,int multiSampleDepth,bool discard)821 egl::Image *createDepthStencil(unsigned int width, unsigned int height, sw::Format format, int multiSampleDepth, bool discard)
822 {
823 	if(height > sw::OUTLINE_RESOLUTION)
824 	{
825 		ERR("Invalid parameters: %dx%d", width, height);
826 		return 0;
827 	}
828 
829 	bool lockable = true;
830 
831 	switch(format)
832 	{
833 //	case sw::FORMAT_D15S1:
834 	case sw::FORMAT_D24S8:
835 	case sw::FORMAT_D24X8:
836 //	case sw::FORMAT_D24X4S4:
837 	case sw::FORMAT_D24FS8:
838 	case sw::FORMAT_D32:
839 	case sw::FORMAT_D16:
840 		lockable = false;
841 		break;
842 //	case sw::FORMAT_S8_LOCKABLE:
843 //	case sw::FORMAT_D16_LOCKABLE:
844 	case sw::FORMAT_D32F_LOCKABLE:
845 //	case sw::FORMAT_D32_LOCKABLE:
846 	case sw::FORMAT_DF24S8:
847 	case sw::FORMAT_DF16S8:
848 		lockable = true;
849 		break;
850 	default:
851 		UNREACHABLE(format);
852 	}
853 
854 	egl::Image *surface = new egl::Image(width, height, format, multiSampleDepth, lockable);
855 
856 	if(!surface)
857 	{
858 		ERR("Out of memory");
859 		return nullptr;
860 	}
861 
862 	return surface;
863 }
864