blob: b5375818bb245519ad6b4fc5bebf55af33fed966 [file] [log] [blame]
// SwiftShader Software Renderer
//
// Copyright(c) 2005-2012 TransGaming Inc.
//
// All rights reserved. No part of this software may be copied, distributed, transmitted,
// transcribed, stored in a retrieval system, translated into any human or computer
// language by any means, or disclosed to third parties without the explicit written
// agreement of TransGaming Inc. Without such an agreement, no rights or licenses, express
// or implied, including but not limited to any patent rights, are granted to you.
//
// Texture.cpp: Implements the Texture class and its derived classes
// Texture2D and TextureCubeMap. Implements GL texture objects and related
// functionality. [OpenGL ES 2.0.24] section 3.7 page 63.
#include "Texture.h"
#include "main.h"
#include "mathutil.h"
#include "utilities.h"
#include "Framebuffer.h"
#include "Device.hpp"
#include "libEGL/Display.h"
#include "common/debug.h"
#include <algorithm>
#include <intrin.h>
namespace gl
{
sw::Format Image::selectInternalFormat(GLenum format, GLenum type)
{
#if S3TC_SUPPORT
if(format == GL_COMPRESSED_RGB_S3TC_DXT1_EXT ||
format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT)
{
return sw::FORMAT_DXT1;
}
else if(type == GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE)
{
return sw::FORMAT_DXT3;
}
else if(type == GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE)
{
return sw::FORMAT_DXT5;
}
else
#endif
if(type == GL_FLOAT)
{
return sw::FORMAT_A32B32G32R32F;
}
else if(type == GL_HALF_FLOAT_OES)
{
return sw::FORMAT_A16B16G16R16F;
}
else if(type == GL_UNSIGNED_BYTE)
{
if(format == GL_LUMINANCE)
{
return sw::FORMAT_L8;
}
else if(format == GL_LUMINANCE_ALPHA)
{
return sw::FORMAT_A8L8;
}
else if(format == GL_RGB)
{
return sw::FORMAT_X8R8G8B8;
}
return sw::FORMAT_A8R8G8B8;
}
return sw::FORMAT_A8R8G8B8;
}
Texture::Texture(GLuint id) : RefCountObject(id)
{
mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
mMagFilter = GL_LINEAR;
mWrapS = GL_REPEAT;
mWrapT = GL_REPEAT;
resource = new sw::Resource(0);
}
Texture::~Texture()
{
resource->destruct();
}
sw::Resource *Texture::getResource() const
{
return resource;
}
bool Texture::isTexture2D()
{
return false;
}
bool Texture::isTextureCubeMap()
{
return false;
}
// Returns true on successful filter state update (valid enum parameter)
bool Texture::setMinFilter(GLenum filter)
{
switch(filter)
{
case GL_NEAREST:
case GL_LINEAR:
case GL_NEAREST_MIPMAP_NEAREST:
case GL_LINEAR_MIPMAP_NEAREST:
case GL_NEAREST_MIPMAP_LINEAR:
case GL_LINEAR_MIPMAP_LINEAR:
mMinFilter = filter;
return true;
default:
return false;
}
}
// Returns true on successful filter state update (valid enum parameter)
bool Texture::setMagFilter(GLenum filter)
{
switch(filter)
{
case GL_NEAREST:
case GL_LINEAR:
mMagFilter = filter;
return true;
default:
return false;
}
}
// Returns true on successful wrap state update (valid enum parameter)
bool Texture::setWrapS(GLenum wrap)
{
switch(wrap)
{
case GL_REPEAT:
case GL_CLAMP_TO_EDGE:
case GL_MIRRORED_REPEAT:
mWrapS = wrap;
return true;
default:
return false;
}
}
// Returns true on successful wrap state update (valid enum parameter)
bool Texture::setWrapT(GLenum wrap)
{
switch(wrap)
{
case GL_REPEAT:
case GL_CLAMP_TO_EDGE:
case GL_MIRRORED_REPEAT:
mWrapT = wrap;
return true;
default:
return false;
}
}
GLenum Texture::getMinFilter() const
{
return mMinFilter;
}
GLenum Texture::getMagFilter() const
{
return mMagFilter;
}
GLenum Texture::getWrapS() const
{
return mWrapS;
}
GLenum Texture::getWrapT() const
{
return mWrapT;
}
// Store the pixel rectangle designated by xoffset,yoffset,width,height with pixels stored as format/type at input
// into the target pixel rectangle at output with outputPitch bytes in between each line.
void Texture::loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type,
GLint unpackAlignment, const void *input, size_t outputPitch, void *output, Image *image) const
{
GLsizei inputPitch = ComputePitch(width, format, type, unpackAlignment);
switch (type)
{
case GL_UNSIGNED_BYTE:
switch (format)
{
case GL_ALPHA:
loadAlphaImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
break;
case GL_LUMINANCE:
loadLuminanceImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output, image->getInternalFormat() == sw::FORMAT_L8);
break;
case GL_LUMINANCE_ALPHA:
loadLuminanceAlphaImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output, image->getInternalFormat() == sw::FORMAT_A8L8);
break;
case GL_RGB:
loadRGBUByteImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
break;
case GL_RGBA:
if(supportsSSE2())
{
loadRGBAUByteImageDataSSE2(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
}
else
{
loadRGBAUByteImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
}
break;
case GL_BGRA_EXT:
loadBGRAImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
break;
default: UNREACHABLE();
}
break;
case GL_UNSIGNED_SHORT_5_6_5:
switch (format)
{
case GL_RGB:
loadRGB565ImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
break;
default: UNREACHABLE();
}
break;
case GL_UNSIGNED_SHORT_4_4_4_4:
switch (format)
{
case GL_RGBA:
loadRGBA4444ImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
break;
default: UNREACHABLE();
}
break;
case GL_UNSIGNED_SHORT_5_5_5_1:
switch (format)
{
case GL_RGBA:
loadRGBA5551ImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
break;
default: UNREACHABLE();
}
break;
case GL_FLOAT:
switch (format)
{
// float textures are converted to RGBA, not BGRA
case GL_ALPHA:
loadAlphaFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
break;
case GL_LUMINANCE:
loadLuminanceFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
break;
case GL_LUMINANCE_ALPHA:
loadLuminanceAlphaFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
break;
case GL_RGB:
loadRGBFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
break;
case GL_RGBA:
loadRGBAFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
break;
default: UNREACHABLE();
}
break;
case GL_HALF_FLOAT_OES:
switch (format)
{
// float textures are converted to RGBA, not BGRA
case GL_ALPHA:
loadAlphaHalfFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
break;
case GL_LUMINANCE:
loadLuminanceHalfFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
break;
case GL_LUMINANCE_ALPHA:
loadLuminanceAlphaHalfFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
break;
case GL_RGB:
loadRGBHalfFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
break;
case GL_RGBA:
loadRGBAHalfFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
break;
default: UNREACHABLE();
}
break;
default: UNREACHABLE();
}
}
void Texture::loadAlphaImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
int inputPitch, const void *input, size_t outputPitch, void *output) const
{
const unsigned char *source = NULL;
unsigned char *dest = NULL;
for(int y = 0; y < height; y++)
{
source = static_cast<const unsigned char*>(input) + y * inputPitch;
dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
for(int x = 0; x < width; x++)
{
dest[4 * x + 0] = 0;
dest[4 * x + 1] = 0;
dest[4 * x + 2] = 0;
dest[4 * x + 3] = source[x];
}
}
}
void Texture::loadAlphaFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
int inputPitch, const void *input, size_t outputPitch, void *output) const
{
const float *source = NULL;
float *dest = NULL;
for(int y = 0; y < height; y++)
{
source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16);
for(int x = 0; x < width; x++)
{
dest[4 * x + 0] = 0;
dest[4 * x + 1] = 0;
dest[4 * x + 2] = 0;
dest[4 * x + 3] = source[x];
}
}
}
void Texture::loadAlphaHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
int inputPitch, const void *input, size_t outputPitch, void *output) const
{
const unsigned short *source = NULL;
unsigned short *dest = NULL;
for(int y = 0; y < height; y++)
{
source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8);
for(int x = 0; x < width; x++)
{
dest[4 * x + 0] = 0;
dest[4 * x + 1] = 0;
dest[4 * x + 2] = 0;
dest[4 * x + 3] = source[x];
}
}
}
void Texture::loadLuminanceImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
int inputPitch, const void *input, size_t outputPitch, void *output, bool native) const
{
const int destBytesPerPixel = native? 1: 4;
const unsigned char *source = NULL;
unsigned char *dest = NULL;
for(int y = 0; y < height; y++)
{
source = static_cast<const unsigned char*>(input) + y * inputPitch;
dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * destBytesPerPixel;
if(!native) // BGRA8 destination format
{
for(int x = 0; x < width; x++)
{
dest[4 * x + 0] = source[x];
dest[4 * x + 1] = source[x];
dest[4 * x + 2] = source[x];
dest[4 * x + 3] = 0xFF;
}
}
else // L8 destination format
{
memcpy(dest, source, width);
}
}
}
void Texture::loadLuminanceFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
int inputPitch, const void *input, size_t outputPitch, void *output) const
{
const float *source = NULL;
float *dest = NULL;
for(int y = 0; y < height; y++)
{
source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16);
for(int x = 0; x < width; x++)
{
dest[4 * x + 0] = source[x];
dest[4 * x + 1] = source[x];
dest[4 * x + 2] = source[x];
dest[4 * x + 3] = 1.0f;
}
}
}
void Texture::loadLuminanceHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
int inputPitch, const void *input, size_t outputPitch, void *output) const
{
const unsigned short *source = NULL;
unsigned short *dest = NULL;
for(int y = 0; y < height; y++)
{
source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8);
for(int x = 0; x < width; x++)
{
dest[4 * x + 0] = source[x];
dest[4 * x + 1] = source[x];
dest[4 * x + 2] = source[x];
dest[4 * x + 3] = 0x3C00; // SEEEEEMMMMMMMMMM, S = 0, E = 15, M = 0: 16bit flpt representation of 1
}
}
}
void Texture::loadLuminanceAlphaImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
int inputPitch, const void *input, size_t outputPitch, void *output, bool native) const
{
const int destBytesPerPixel = native? 2: 4;
const unsigned char *source = NULL;
unsigned char *dest = NULL;
for(int y = 0; y < height; y++)
{
source = static_cast<const unsigned char*>(input) + y * inputPitch;
dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * destBytesPerPixel;
if(!native) // BGRA8 destination format
{
for(int x = 0; x < width; x++)
{
dest[4 * x + 0] = source[2*x+0];
dest[4 * x + 1] = source[2*x+0];
dest[4 * x + 2] = source[2*x+0];
dest[4 * x + 3] = source[2*x+1];
}
}
else
{
memcpy(dest, source, width * 2);
}
}
}
void Texture::loadLuminanceAlphaFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
int inputPitch, const void *input, size_t outputPitch, void *output) const
{
const float *source = NULL;
float *dest = NULL;
for(int y = 0; y < height; y++)
{
source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16);
for(int x = 0; x < width; x++)
{
dest[4 * x + 0] = source[2*x+0];
dest[4 * x + 1] = source[2*x+0];
dest[4 * x + 2] = source[2*x+0];
dest[4 * x + 3] = source[2*x+1];
}
}
}
void Texture::loadLuminanceAlphaHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
int inputPitch, const void *input, size_t outputPitch, void *output) const
{
const unsigned short *source = NULL;
unsigned short *dest = NULL;
for(int y = 0; y < height; y++)
{
source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8);
for(int x = 0; x < width; x++)
{
dest[4 * x + 0] = source[2*x+0];
dest[4 * x + 1] = source[2*x+0];
dest[4 * x + 2] = source[2*x+0];
dest[4 * x + 3] = source[2*x+1];
}
}
}
void Texture::loadRGBUByteImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
int inputPitch, const void *input, size_t outputPitch, void *output) const
{
const unsigned char *source = NULL;
unsigned char *dest = NULL;
for(int y = 0; y < height; y++)
{
source = static_cast<const unsigned char*>(input) + y * inputPitch;
dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
for(int x = 0; x < width; x++)
{
dest[4 * x + 0] = source[x * 3 + 2];
dest[4 * x + 1] = source[x * 3 + 1];
dest[4 * x + 2] = source[x * 3 + 0];
dest[4 * x + 3] = 0xFF;
}
}
}
void Texture::loadRGB565ImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
int inputPitch, const void *input, size_t outputPitch, void *output) const
{
const unsigned short *source = NULL;
unsigned char *dest = NULL;
for(int y = 0; y < height; y++)
{
source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
for(int x = 0; x < width; x++)
{
unsigned short rgba = source[x];
dest[4 * x + 0] = ((rgba & 0x001F) << 3) | ((rgba & 0x001F) >> 2);
dest[4 * x + 1] = ((rgba & 0x07E0) >> 3) | ((rgba & 0x07E0) >> 9);
dest[4 * x + 2] = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
dest[4 * x + 3] = 0xFF;
}
}
}
void Texture::loadRGBFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
int inputPitch, const void *input, size_t outputPitch, void *output) const
{
const float *source = NULL;
float *dest = NULL;
for(int y = 0; y < height; y++)
{
source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16);
for(int x = 0; x < width; x++)
{
dest[4 * x + 0] = source[x * 3 + 0];
dest[4 * x + 1] = source[x * 3 + 1];
dest[4 * x + 2] = source[x * 3 + 2];
dest[4 * x + 3] = 1.0f;
}
}
}
void Texture::loadRGBHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
int inputPitch, const void *input, size_t outputPitch, void *output) const
{
const unsigned short *source = NULL;
unsigned short *dest = NULL;
for(int y = 0; y < height; y++)
{
source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8);
for(int x = 0; x < width; x++)
{
dest[4 * x + 0] = source[x * 3 + 0];
dest[4 * x + 1] = source[x * 3 + 1];
dest[4 * x + 2] = source[x * 3 + 2];
dest[4 * x + 3] = 0x3C00; // SEEEEEMMMMMMMMMM, S = 0, E = 15, M = 0: 16bit flpt representation of 1
}
}
}
void Texture::loadRGBAUByteImageDataSSE2(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
int inputPitch, const void *input, size_t outputPitch, void *output) const
{
const unsigned int *source = NULL;
unsigned int *dest = NULL;
__m128i brMask = _mm_set1_epi32(0x00ff00ff);
for(int y = 0; y < height; y++)
{
source = reinterpret_cast<const unsigned int*>(static_cast<const unsigned char*>(input) + y * inputPitch);
dest = reinterpret_cast<unsigned int*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4);
int x = 0;
// Make output writes aligned
for(x = 0; ((reinterpret_cast<intptr_t>(&dest[x]) & 15) != 0) && x < width; x++)
{
unsigned int rgba = source[x];
dest[x] = (_rotl(rgba, 16) & 0x00ff00ff) | (rgba & 0xff00ff00);
}
for(; x + 3 < width; x += 4)
{
__m128i sourceData = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&source[x]));
// Mask out g and a, which don't change
__m128i gaComponents = _mm_andnot_si128(brMask, sourceData);
// Mask out b and r
__m128i brComponents = _mm_and_si128(sourceData, brMask);
// Swap b and r
__m128i brSwapped = _mm_shufflehi_epi16(_mm_shufflelo_epi16(brComponents, _MM_SHUFFLE(2, 3, 0, 1)), _MM_SHUFFLE(2, 3, 0, 1));
__m128i result = _mm_or_si128(gaComponents, brSwapped);
_mm_store_si128(reinterpret_cast<__m128i*>(&dest[x]), result);
}
// Perform leftover writes
for(; x < width; x++)
{
unsigned int rgba = source[x];
dest[x] = (_rotl(rgba, 16) & 0x00ff00ff) | (rgba & 0xff00ff00);
}
}
}
void Texture::loadRGBAUByteImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
int inputPitch, const void *input, size_t outputPitch, void *output) const
{
const unsigned int *source = NULL;
unsigned int *dest = NULL;
for(int y = 0; y < height; y++)
{
source = reinterpret_cast<const unsigned int*>(static_cast<const unsigned char*>(input) + y * inputPitch);
dest = reinterpret_cast<unsigned int*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4);
for(int x = 0; x < width; x++)
{
unsigned int rgba = source[x];
dest[x] = (_rotl(rgba, 16) & 0x00ff00ff) | (rgba & 0xff00ff00);
}
}
}
void Texture::loadRGBA4444ImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
int inputPitch, const void *input, size_t outputPitch, void *output) const
{
const unsigned short *source = NULL;
unsigned char *dest = NULL;
for(int y = 0; y < height; y++)
{
source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
for(int x = 0; x < width; x++)
{
unsigned short rgba = source[x];
dest[4 * x + 0] = ((rgba & 0x00F0) << 0) | ((rgba & 0x00F0) >> 4);
dest[4 * x + 1] = ((rgba & 0x0F00) >> 4) | ((rgba & 0x0F00) >> 8);
dest[4 * x + 2] = ((rgba & 0xF000) >> 8) | ((rgba & 0xF000) >> 12);
dest[4 * x + 3] = ((rgba & 0x000F) << 4) | ((rgba & 0x000F) >> 0);
}
}
}
void Texture::loadRGBA5551ImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
int inputPitch, const void *input, size_t outputPitch, void *output) const
{
const unsigned short *source = NULL;
unsigned char *dest = NULL;
for(int y = 0; y < height; y++)
{
source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
for(int x = 0; x < width; x++)
{
unsigned short rgba = source[x];
dest[4 * x + 0] = ((rgba & 0x003E) << 2) | ((rgba & 0x003E) >> 3);
dest[4 * x + 1] = ((rgba & 0x07C0) >> 3) | ((rgba & 0x07C0) >> 8);
dest[4 * x + 2] = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
dest[4 * x + 3] = (rgba & 0x0001) ? 0xFF : 0;
}
}
}
void Texture::loadRGBAFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
int inputPitch, const void *input, size_t outputPitch, void *output) const
{
const float *source = NULL;
float *dest = NULL;
for(int y = 0; y < height; y++)
{
source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16);
memcpy(dest, source, width * 16);
}
}
void Texture::loadRGBAHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
int inputPitch, const void *input, size_t outputPitch, void *output) const
{
const unsigned char *source = NULL;
unsigned char *dest = NULL;
for(int y = 0; y < height; y++)
{
source = static_cast<const unsigned char*>(input) + y * inputPitch;
dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8;
memcpy(dest, source, width * 8);
}
}
void Texture::loadBGRAImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
int inputPitch, const void *input, size_t outputPitch, void *output) const
{
const unsigned char *source = NULL;
unsigned char *dest = NULL;
for(int y = 0; y < height; y++)
{
source = static_cast<const unsigned char*>(input) + y * inputPitch;
dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
memcpy(dest, source, width*4);
}
}
void Texture::setImage(GLint unpackAlignment, const void *pixels, Image *image)
{
if(pixels && image)
{
void *buffer = image->lock(0, 0, sw::LOCK_WRITEONLY);
if(buffer)
{
loadImageData(0, 0, image->getWidth(), image->getHeight(), image->getFormat(), image->getType(), unpackAlignment, pixels, image->getPitch(), buffer, image);
image->unlock();
}
}
}
void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, Image *image)
{
if(pixels && image)
{
void *buffer = image->lock(0, 0, sw::LOCK_WRITEONLY);
if(buffer)
{
memcpy(buffer, pixels, imageSize);
image->unlock();
}
}
}
void Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *image)
{
if(!image)
{
return error(GL_INVALID_OPERATION);
}
if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight())
{
return error(GL_INVALID_VALUE);
}
if(IsCompressed(image->getFormat()))
{
return error(GL_INVALID_OPERATION);
}
if(format != image->getFormat())
{
return error(GL_INVALID_OPERATION);
}
if(pixels)
{
void *buffer = image->lock(0, 0, sw::LOCK_WRITEONLY);
loadImageData(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, image->getPitch(), buffer, image);
image->unlock();
}
}
void Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, Image *image)
{
if(!image)
{
return error(GL_INVALID_OPERATION);
}
if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight())
{
return error(GL_INVALID_VALUE);
}
if(format != image->getFormat())
{
return error(GL_INVALID_OPERATION);
}
if(pixels)
{
void *buffer = image->lock(xoffset, yoffset, sw::LOCK_WRITEONLY);
int inputPitch = ComputeCompressedPitch(width, format);
int rows = imageSize / inputPitch;
for(int i = 0; i < rows; i++)
{
memcpy((void*)((BYTE*)buffer + i * image->getPitch()), (void*)((BYTE*)pixels + i * inputPitch), inputPitch);
}
image->unlock();
}
}
bool Texture::copy(Image *source, const sw::Rect &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, Image *dest)
{
Device *device = getDevice();
sw::Rect destRect = {xoffset, yoffset, xoffset + (sourceRect.x1 - sourceRect.x0), yoffset + (sourceRect.y1 - sourceRect.y0)};
bool success = device->stretchRect(source, &sourceRect, dest, &destRect, false);
if(!success)
{
return error(GL_OUT_OF_MEMORY, false);
}
return true;
}
Texture2D::Texture2D(GLuint id) : Texture(id)
{
for(int i = 0; i < MIPMAP_LEVELS; i++)
{
image[i] = 0;
}
mSurface = NULL;
}
Texture2D::~Texture2D()
{
resource->lock(sw::DESTRUCT);
for(int i = 0; i < MIPMAP_LEVELS; i++)
{
if(image[i])
{
image[i]->unbind();
image[i] = 0;
}
}
resource->unlock();
mColorbufferProxy.set(NULL);
if(mSurface)
{
mSurface->setBoundTexture(NULL);
mSurface = NULL;
}
}
bool Texture2D::isTexture2D()
{
return true;
}
GLenum Texture2D::getTarget() const
{
return GL_TEXTURE_2D;
}
GLsizei Texture2D::getWidth(GLint level) const
{
return image[level] ? image[level]->getWidth() : 0;
}
GLsizei Texture2D::getHeight(GLint level) const
{
return image[level] ? image[level]->getHeight() : 0;
}
GLenum Texture2D::getFormat() const
{
return image[0] ? image[0]->getFormat() : 0;
}
GLenum Texture2D::getType() const
{
return image[0] ? image[0]->getType() : 0;
}
sw::Format Texture2D::getInternalFormat() const
{
return image[0] ? image[0]->getInternalFormat() : sw::FORMAT_NULL;
}
int Texture2D::getLevelCount() const
{
int levels = 0;
while(levels < MIPMAP_LEVELS && image[levels])
{
levels++;
}
return levels;
}
void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
{
if(image[level])
{
image[level]->unbind();
}
image[level] = new Image(resource, width, height, format, type);
if(!image[level])
{
return error(GL_OUT_OF_MEMORY);
}
image[level]->bind();
Texture::setImage(unpackAlignment, pixels, image[level]);
}
void Texture2D::bindTexImage(egl::Surface *surface)
{
GLenum format;
switch(surface->getInternalFormat())
{
case sw::FORMAT_A8R8G8B8:
format = GL_RGBA;
break;
case sw::FORMAT_X8R8G8B8:
format = GL_RGB;
break;
default:
UNIMPLEMENTED();
return;
}
for(int level = 0; level < MIPMAP_LEVELS; level++)
{
if(image[level])
{
image[level]->unbind();
image[level] = 0;
}
}
image[0] = surface->getRenderTarget();
image[0]->bind();
image[0]->release();
mSurface = surface;
mSurface->setBoundTexture(this);
}
void Texture2D::releaseTexImage()
{
for(int level = 0; level < MIPMAP_LEVELS; level++)
{
if(image[level])
{
image[level]->unbind();
image[level] = 0;
}
}
}
void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
{
if(image[level])
{
image[level]->unbind();
}
image[level] = new Image(resource, width, height, format, GL_UNSIGNED_BYTE);
if(!image[level])
{
return error(GL_OUT_OF_MEMORY);
}
image[level]->bind();
Texture::setCompressedImage(imageSize, pixels, image[level]);
}
void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
{
Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, image[level]);
}
void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
{
Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, image[level]);
}
void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
{
Image *renderTarget = source->getRenderTarget();
if(!renderTarget)
{
ERR("Failed to retrieve the render target.");
return error(GL_OUT_OF_MEMORY);
}
if(image[level])
{
image[level]->unbind();
}
image[level] = new Image(resource, width, height, format, GL_UNSIGNED_BYTE);
if(!image[level])
{
return error(GL_OUT_OF_MEMORY);
}
image[level]->bind();
if(width != 0 && height != 0)
{
sw::Rect sourceRect = {x, y, x + width, y + height};
sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());
copy(renderTarget, sourceRect, format, 0, 0, image[level]);
}
renderTarget->release();
}
void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
{
if(!image[level])
{
return error(GL_INVALID_OPERATION);
}
if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight())
{
return error(GL_INVALID_VALUE);
}
Image *renderTarget = source->getRenderTarget();
if(!renderTarget)
{
ERR("Failed to retrieve the render target.");
return error(GL_OUT_OF_MEMORY);
}
sw::Rect sourceRect = {x, y, x + width, y + height};
sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());
copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, image[level]);
renderTarget->release();
}
// Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.
bool Texture2D::isSamplerComplete() const
{
if(!image[0])
{
return false;
}
GLsizei width = image[0]->getWidth();
GLsizei height = image[0]->getHeight();
if(width <= 0 || height <= 0)
{
return false;
}
bool mipmapping = false;
switch(mMinFilter)
{
case GL_NEAREST:
case GL_LINEAR:
mipmapping = false;
break;
case GL_NEAREST_MIPMAP_NEAREST:
case GL_LINEAR_MIPMAP_NEAREST:
case GL_NEAREST_MIPMAP_LINEAR:
case GL_LINEAR_MIPMAP_LINEAR:
mipmapping = true;
break;
default: UNREACHABLE();
}
if(mipmapping)
{
if(!isMipmapComplete())
{
return false;
}
}
return true;
}
// Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
bool Texture2D::isMipmapComplete() const
{
GLsizei width = image[0]->getWidth();
GLsizei height = image[0]->getHeight();
int q = log2(std::max(width, height));
for(int level = 1; level <= q; level++)
{
if(!image[level])
{
return false;
}
if(image[level]->getFormat() != image[0]->getFormat())
{
return false;
}
if(image[level]->getType() != image[0]->getType())
{
return false;
}
if(image[level]->getWidth() != std::max(1, width >> level))
{
return false;
}
if(image[level]->getHeight() != std::max(1, height >> level))
{
return false;
}
}
return true;
}
bool Texture2D::isCompressed() const
{
return IsCompressed(getFormat());
}
void Texture2D::generateMipmaps()
{
if(!image[0])
{
return; // FIXME: error?
}
unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight()));
for(unsigned int i = 1; i <= q; i++)
{
if(image[i])
{
image[i]->unbind();
}
image[i] = new Image(resource, std::max(image[0]->getWidth() >> i, 1), std::max(image[0]->getHeight() >> i, 1), image[0]->getFormat(), image[0]->getType());
if(!image[i])
{
return error(GL_OUT_OF_MEMORY);
}
image[i]->bind();
getDevice()->stretchRect(image[i - 1], 0, image[i], 0, true);
}
}
Image *Texture2D::getImage(unsigned int level)
{
return image[level];
}
Renderbuffer *Texture2D::getRenderbuffer(GLenum target)
{
if(target != GL_TEXTURE_2D)
{
return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
}
if(mColorbufferProxy.get() == NULL)
{
mColorbufferProxy.set(new Renderbuffer(id(), new Colorbuffer(this, target)));
}
return mColorbufferProxy.get();
}
Image *Texture2D::getRenderTarget(GLenum target)
{
ASSERT(target == GL_TEXTURE_2D);
if(image[0])
{
image[0]->addRef();
}
return image[0];
}
TextureCubeMap::TextureCubeMap(GLuint id) : Texture(id)
{
for(int f = 0; f < 6; f++)
{
for(int i = 0; i < MIPMAP_LEVELS; i++)
{
image[f][i] = 0;
}
}
}
TextureCubeMap::~TextureCubeMap()
{
resource->lock(sw::DESTRUCT);
for(int f = 0; f < 6; f++)
{
for(int i = 0; i < MIPMAP_LEVELS; i++)
{
if(image[f][i])
{
image[f][i]->unbind();
image[f][i] = 0;
}
}
}
resource->unlock();
for(int i = 0; i < 6; i++)
{
mFaceProxies[i].set(NULL);
}
}
bool TextureCubeMap::isTextureCubeMap()
{
return true;
}
GLenum TextureCubeMap::getTarget() const
{
return GL_TEXTURE_CUBE_MAP;
}
GLsizei TextureCubeMap::getWidth(GLint level) const
{
return image[0][level] ? image[0][level]->getWidth() : 0;
}
GLsizei TextureCubeMap::getHeight(GLint level) const
{
return image[0][level] ? image[0][level]->getHeight() : 0;
}
GLenum TextureCubeMap::getFormat() const
{
return image[0][0] ? image[0][0]->getFormat() : 0;
}
GLenum TextureCubeMap::getType() const
{
return image[0][0] ? image[0][0]->getType() : 0;
}
sw::Format TextureCubeMap::getInternalFormat() const
{
return image[0][0] ? image[0][0]->getInternalFormat() : sw::FORMAT_NULL;
}
int TextureCubeMap::getLevelCount() const
{
int levels = 0;
while(levels < MIPMAP_LEVELS && image[0][levels])
{
levels++;
}
return levels;
}
void TextureCubeMap::setCompressedImage(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
{
CubeFace face = ConvertCubeFace(target);
if(image[face][level])
{
image[face][level]->unbind();
}
image[face][level] = new Image(resource, width, height, format, GL_UNSIGNED_BYTE);
if(!image[face][level])
{
return error(GL_OUT_OF_MEMORY);
}
image[face][level]->bind();
Texture::setCompressedImage(imageSize, pixels, image[face][level]);
}
void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
{
Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, image[ConvertCubeFace(target)][level]);
}
void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
{
Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, image[ConvertCubeFace(target)][level]);
}
// Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86.
bool TextureCubeMap::isSamplerComplete() const
{
for(int face = 0; face < 6; face++)
{
if(!image[face][0])
{
return false;
}
}
int size = image[0][0]->getWidth();
if(size <= 0)
{
return false;
}
bool mipmapping;
switch(mMinFilter)
{
case GL_NEAREST:
case GL_LINEAR:
mipmapping = false;
break;
case GL_NEAREST_MIPMAP_NEAREST:
case GL_LINEAR_MIPMAP_NEAREST:
case GL_NEAREST_MIPMAP_LINEAR:
case GL_LINEAR_MIPMAP_LINEAR:
mipmapping = true;
break;
default: UNREACHABLE();
}
if(!mipmapping)
{
if(!isCubeComplete())
{
return false;
}
}
else
{
if(!isMipmapCubeComplete()) // Also tests for isCubeComplete()
{
return false;
}
}
return true;
}
// Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
bool TextureCubeMap::isCubeComplete() const
{
if(image[0][0]->getWidth() <= 0 || image[0][0]->getHeight() != image[0][0]->getWidth())
{
return false;
}
for(unsigned int face = 1; face < 6; face++)
{
if(image[face][0]->getWidth() != image[0][0]->getWidth() ||
image[face][0]->getWidth() != image[0][0]->getHeight() ||
image[face][0]->getFormat() != image[0][0]->getFormat() ||
image[face][0]->getType() != image[0][0]->getType())
{
return false;
}
}
return true;
}
bool TextureCubeMap::isMipmapCubeComplete() const
{
if(!isCubeComplete())
{
return false;
}
GLsizei size = image[0][0]->getWidth();
int q = log2(size);
for(int face = 0; face < 6; face++)
{
for(int level = 1; level <= q; level++)
{
if(!image[face][level])
{
return false;
}
if(image[face][level]->getFormat() != image[0][0]->getFormat())
{
return false;
}
if(image[face][level]->getType() != image[0][0]->getType())
{
return false;
}
if(image[face][level]->getWidth() != std::max(1, size >> level))
{
return false;
}
}
}
return true;
}
bool TextureCubeMap::isCompressed() const
{
return IsCompressed(getFormat());
}
void TextureCubeMap::setImage(GLenum target, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
{
CubeFace face = ConvertCubeFace(target);
if(image[face][level])
{
image[face][level]->unbind();
}
image[face][level] = new Image(resource, width, height, format, GL_UNSIGNED_BYTE);
if(!image[face][level])
{
return error(GL_OUT_OF_MEMORY);
}
image[face][level]->bind();
Texture::setImage(unpackAlignment, pixels, image[face][level]);
}
void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
{
Image *renderTarget = source->getRenderTarget();
if(!renderTarget)
{
ERR("Failed to retrieve the render target.");
return error(GL_OUT_OF_MEMORY);
}
CubeFace face = ConvertCubeFace(target);
if(image[face][level])
{
image[face][level]->unbind();
}
image[face][level] = new Image(resource, width, height, format, GL_UNSIGNED_BYTE);
if(!image[face][level])
{
return error(GL_OUT_OF_MEMORY);
}
image[face][level]->bind();
if(width != 0 && height != 0)
{
sw::Rect sourceRect = {x, y, x + width, y + height};
sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());
copy(renderTarget, sourceRect, format, 0, 0, image[face][level]);
}
renderTarget->release();
}
Image *TextureCubeMap::getImage(CubeFace face, unsigned int level)
{
return image[face][level];
}
Image *TextureCubeMap::getImage(GLenum face, unsigned int level)
{
return image[ConvertCubeFace(face)][level];
}
void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
{
CubeFace face = ConvertCubeFace(target);
if(!image[face][level])
{
return error(GL_INVALID_OPERATION);
}
GLsizei size = image[face][level]->getWidth();
if(xoffset + width > size || yoffset + height > size)
{
return error(GL_INVALID_VALUE);
}
Image *renderTarget = source->getRenderTarget();
if(!renderTarget)
{
ERR("Failed to retrieve the render target.");
return error(GL_OUT_OF_MEMORY);
}
sw::Rect sourceRect = {x, y, x + width, y + height};
sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());
copy(renderTarget, sourceRect, image[face][level]->getFormat(), xoffset, yoffset, image[face][level]);
renderTarget->release();
}
void TextureCubeMap::generateMipmaps()
{
if(!isCubeComplete())
{
return error(GL_INVALID_OPERATION);
}
unsigned int q = log2(image[0][0]->getWidth());
for(unsigned int f = 0; f < 6; f++)
{
for(unsigned int i = 1; i <= q; i++)
{
if(image[f][i])
{
image[f][i]->unbind();
}
image[f][i] = new Image(resource, std::max(image[0][0]->getWidth() >> i, 1), std::max(image[0][0]->getHeight() >> i, 1), image[0][0]->getFormat(), image[0][0]->getType());
if(!image[f][i])
{
return error(GL_OUT_OF_MEMORY);
}
image[f][i]->bind();
getDevice()->stretchRect(image[f][i - 1], 0, image[f][i], 0, true);
}
}
}
Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target)
{
if(!IsCubemapTextureTarget(target))
{
return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
}
unsigned int face = ConvertCubeFace(target);
if(mFaceProxies[face].get() == NULL)
{
mFaceProxies[face].set(new Renderbuffer(id(), new Colorbuffer(this, target)));
}
return mFaceProxies[face].get();
}
Image *TextureCubeMap::getRenderTarget(GLenum target)
{
ASSERT(IsCubemapTextureTarget(target));
int face = ConvertCubeFace(target);
if(image[face][0])
{
image[face][0]->addRef();
}
return image[face][0];
}
}
extern "C"
{
gl::Image *createBackBuffer(int width, int height, const egl::Config *config)
{
if(config)
{
return new gl::Image(0, width, height, config->mAlphaSize ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE);
}
return 0;
}
}