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