diff options
Diffstat (limited to 'Source/ThirdParty/ANGLE/src/libGLESv2/Texture.cpp')
-rw-r--r-- | Source/ThirdParty/ANGLE/src/libGLESv2/Texture.cpp | 2963 |
1 files changed, 1793 insertions, 1170 deletions
diff --git a/Source/ThirdParty/ANGLE/src/libGLESv2/Texture.cpp b/Source/ThirdParty/ANGLE/src/libGLESv2/Texture.cpp index 7c040f8..29177d2 100644 --- a/Source/ThirdParty/ANGLE/src/libGLESv2/Texture.cpp +++ b/Source/ThirdParty/ANGLE/src/libGLESv2/Texture.cpp @@ -13,334 +13,384 @@ #include <d3dx9tex.h> #include <algorithm> +#include <intrin.h> #include "common/debug.h" +#include "libEGL/Display.h" + #include "libGLESv2/main.h" #include "libGLESv2/mathutil.h" #include "libGLESv2/utilities.h" #include "libGLESv2/Blit.h" +#include "libGLESv2/Framebuffer.h" namespace gl { +unsigned int TextureStorage::mCurrentTextureSerial = 1; -Texture::Image::Image() - : width(0), height(0), dirty(false), surface(NULL), format(GL_NONE) +static D3DFORMAT ConvertTextureFormatType(GLenum format, GLenum type) { + if (format == GL_COMPRESSED_RGB_S3TC_DXT1_EXT || + format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) + { + return D3DFMT_DXT1; + } + else if (format == GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE) + { + return D3DFMT_DXT3; + } + else if (format == GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE) + { + return D3DFMT_DXT5; + } + else if (type == GL_FLOAT) + { + return D3DFMT_A32B32G32R32F; + } + else if (type == GL_HALF_FLOAT_OES) + { + return D3DFMT_A16B16G16R16F; + } + else if (type == GL_UNSIGNED_BYTE) + { + if (format == GL_LUMINANCE && getContext()->supportsLuminanceTextures()) + { + return D3DFMT_L8; + } + else if (format == GL_LUMINANCE_ALPHA && getContext()->supportsLuminanceAlphaTextures()) + { + return D3DFMT_A8L8; + } + else if (format == GL_RGB) + { + return D3DFMT_X8R8G8B8; + } + + return D3DFMT_A8R8G8B8; + } + + return D3DFMT_A8R8G8B8; } -Texture::Image::~Image() +static bool IsTextureFormatRenderable(D3DFORMAT format) { - if (surface) surface->Release(); + switch(format) + { + case D3DFMT_L8: + case D3DFMT_A8L8: + case D3DFMT_DXT1: + case D3DFMT_DXT3: + case D3DFMT_DXT5: + return false; + case D3DFMT_A8R8G8B8: + case D3DFMT_X8R8G8B8: + case D3DFMT_A16B16G16R16F: + case D3DFMT_A32B32G32R32F: + return true; + default: + UNREACHABLE(); + } + + return false; } -Texture::Texture(GLuint id) : RefCountObject(id) +Image::Image() { - mMinFilter = GL_NEAREST_MIPMAP_LINEAR; - mMagFilter = GL_LINEAR; - mWrapS = GL_REPEAT; - mWrapT = GL_REPEAT; - - mWidth = 0; + mWidth = 0; mHeight = 0; - - mDirtyMetaData = true; - mDirty = true; - mIsRenderable = false; + mFormat = GL_NONE; mType = GL_UNSIGNED_BYTE; - mBaseTexture = NULL; -} -Texture::~Texture() -{ -} + mSurface = NULL; -Blit *Texture::getBlitter() -{ - Context *context = getContext(); - return context->getBlitter(); + mDirty = false; + + mD3DPool = D3DPOOL_SYSTEMMEM; + mD3DFormat = D3DFMT_UNKNOWN; } -// Returns true on successful filter state update (valid enum parameter) -bool Texture::setMinFilter(GLenum filter) +Image::~Image() { - switch (filter) + if (mSurface) { - 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: - { - if (mMinFilter != filter) - { - mMinFilter = filter; - mDirty = true; - } - return true; - } - default: - return false; + mSurface->Release(); } } -// Returns true on successful filter state update (valid enum parameter) -bool Texture::setMagFilter(GLenum filter) +bool Image::redefine(GLenum format, GLsizei width, GLsizei height, GLenum type, bool forceRelease) { - switch (filter) + if (mWidth != width || + mHeight != height || + mFormat != format || + mType != type || + forceRelease) { - case GL_NEAREST: - case GL_LINEAR: + mWidth = width; + mHeight = height; + mFormat = format; + mType = type; + // compute the d3d format that will be used + mD3DFormat = ConvertTextureFormatType(mFormat, mType); + + if (mSurface) { - if (mMagFilter != filter) - { - mMagFilter = filter; - mDirty = true; - } - return true; + mSurface->Release(); + mSurface = NULL; } - default: - return false; + + return true; } + + return false; } -// Returns true on successful wrap state update (valid enum parameter) -bool Texture::setWrapS(GLenum wrap) +void Image::createSurface() { - switch (wrap) + if(mSurface) { - case GL_REPEAT: - case GL_CLAMP_TO_EDGE: - case GL_MIRRORED_REPEAT: - { - if (mWrapS != wrap) - { - mWrapS = wrap; - mDirty = true; - } - return true; - } - default: - return false; + return; } -} -// Returns true on successful wrap state update (valid enum parameter) -bool Texture::setWrapT(GLenum wrap) -{ - switch (wrap) + IDirect3DTexture9 *newTexture = NULL; + IDirect3DSurface9 *newSurface = NULL; + const D3DPOOL poolToUse = D3DPOOL_SYSTEMMEM; + + if (mWidth != 0 && mHeight != 0) { - case GL_REPEAT: - case GL_CLAMP_TO_EDGE: - case GL_MIRRORED_REPEAT: + int levelToFetch = 0; + GLsizei requestWidth = mWidth; + GLsizei requestHeight = mHeight; + if (IsCompressed(mFormat) && (mWidth % 4 != 0 || mHeight % 4 != 0)) { - if (mWrapT != wrap) + bool isMult4 = false; + int upsampleCount = 0; + while (!isMult4) { - mWrapT = wrap; - mDirty = true; + requestWidth <<= 1; + requestHeight <<= 1; + upsampleCount++; + if (requestWidth % 4 == 0 && requestHeight % 4 == 0) + { + isMult4 = true; + } } - return true; + levelToFetch = upsampleCount; } - default: - return false; + + HRESULT result = getDevice()->CreateTexture(requestWidth, requestHeight, levelToFetch + 1, NULL, getD3DFormat(), + poolToUse, &newTexture, NULL); + + if (FAILED(result)) + { + ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); + ERR("Creating image surface failed."); + return error(GL_OUT_OF_MEMORY); + } + + newTexture->GetSurfaceLevel(levelToFetch, &newSurface); + newTexture->Release(); } -} -GLenum Texture::getMinFilter() const -{ - return mMinFilter; + mSurface = newSurface; + mDirty = false; + mD3DPool = poolToUse; } -GLenum Texture::getMagFilter() const +HRESULT Image::lock(D3DLOCKED_RECT *lockedRect, const RECT *rect) { - return mMagFilter; -} + createSurface(); -GLenum Texture::getWrapS() const -{ - return mWrapS; + HRESULT result = D3DERR_INVALIDCALL; + + if (mSurface) + { + result = mSurface->LockRect(lockedRect, rect, 0); + ASSERT(SUCCEEDED(result)); + + mDirty = true; + } + + return result; } -GLenum Texture::getWrapT() const +void Image::unlock() { - return mWrapT; + if (mSurface) + { + HRESULT result = mSurface->UnlockRect(); + ASSERT(SUCCEEDED(result)); + } } -GLuint Texture::getWidth() const -{ - return mWidth; +bool Image::isRenderableFormat() const +{ + return IsTextureFormatRenderable(getD3DFormat()); } -GLuint Texture::getHeight() const +D3DFORMAT Image::getD3DFormat() const { - return mHeight; + // this should only happen if the image hasn't been redefined first + // which would be a bug by the caller + ASSERT(mD3DFormat != D3DFMT_UNKNOWN); + + return mD3DFormat; } -bool Texture::isFloatingPoint() const +IDirect3DSurface9 *Image::getSurface() { - return (mType == GL_FLOAT || mType == GL_HALF_FLOAT_OES); + createSurface(); + + return mSurface; } -bool Texture::isRenderableFormat() const +void Image::setManagedSurface(IDirect3DSurface9 *surface) { - D3DFORMAT format = getD3DFormat(); - - switch(format) + if (mSurface) { - case D3DFMT_L8: - case D3DFMT_A8L8: - case D3DFMT_DXT1: - return false; - case D3DFMT_A8R8G8B8: - case D3DFMT_X8R8G8B8: - case D3DFMT_A16B16G16R16F: - case D3DFMT_A32B32G32R32F: - return true; - default: - UNREACHABLE(); + D3DXLoadSurfaceFromSurface(surface, NULL, NULL, mSurface, NULL, NULL, D3DX_FILTER_BOX, 0); + mSurface->Release(); } - return false; + D3DSURFACE_DESC desc; + surface->GetDesc(&desc); + ASSERT(desc.Pool == D3DPOOL_MANAGED); + + mSurface = surface; + mD3DPool = desc.Pool; } -// Selects an internal Direct3D 9 format for storing an Image -D3DFORMAT Texture::selectFormat(GLenum format, GLenum type) +void Image::updateSurface(IDirect3DSurface9 *destSurface, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height) { - if (format == GL_COMPRESSED_RGB_S3TC_DXT1_EXT || - format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) - { - return D3DFMT_DXT1; - } - else if (type == GL_FLOAT) - { - return D3DFMT_A32B32G32R32F; - } - else if (type == GL_HALF_FLOAT_OES) - { - return D3DFMT_A16B16G16R16F; - } - else if (type == GL_UNSIGNED_BYTE) + IDirect3DSurface9 *sourceSurface = getSurface(); + + if (sourceSurface != destSurface) { - if (format == GL_LUMINANCE && getContext()->supportsLuminanceTextures()) - { - return D3DFMT_L8; - } - else if (format == GL_LUMINANCE_ALPHA && getContext()->supportsLuminanceAlphaTextures()) + RECT rect = transformPixelRect(xoffset, yoffset, width, height, mHeight); + + if (mD3DPool == D3DPOOL_MANAGED) { - return D3DFMT_A8L8; + HRESULT result = D3DXLoadSurfaceFromSurface(destSurface, NULL, &rect, sourceSurface, NULL, &rect, D3DX_FILTER_BOX, 0); + ASSERT(SUCCEEDED(result)); } - else if (format == GL_RGB) + else { - return D3DFMT_X8R8G8B8; + // UpdateSurface: source must be SYSTEMMEM, dest must be DEFAULT pools + POINT point = {rect.left, rect.top}; + HRESULT result = getDevice()->UpdateSurface(sourceSurface, &rect, destSurface, &point); + ASSERT(SUCCEEDED(result)); } - - return D3DFMT_A8R8G8B8; } - - return D3DFMT_A8R8G8B8; } // 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, D3DSURFACE_DESC *description) const +void Image::loadData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum type, + GLint unpackAlignment, const void *input, size_t outputPitch, void *output) const { - GLsizei inputPitch = ComputePitch(width, format, type, unpackAlignment); + GLsizei inputPitch = -ComputePitch(width, mFormat, type, unpackAlignment); + input = ((char*)input) - inputPitch * (height - 1); switch (type) { case GL_UNSIGNED_BYTE: - switch (format) + switch (mFormat) { case GL_ALPHA: - loadAlphaImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + loadAlphaData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); break; case GL_LUMINANCE: - loadLuminanceImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output, description->Format == D3DFMT_L8); + loadLuminanceData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output, getD3DFormat() == D3DFMT_L8); break; case GL_LUMINANCE_ALPHA: - loadLuminanceAlphaImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output, description->Format == D3DFMT_A8L8); + loadLuminanceAlphaData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output, getD3DFormat() == D3DFMT_A8L8); break; case GL_RGB: - loadRGBUByteImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + loadRGBUByteData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); break; case GL_RGBA: - loadRGBAUByteImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + if (supportsSSE2()) + { + loadRGBAUByteDataSSE2(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + } + else + { + loadRGBAUByteData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + } break; case GL_BGRA_EXT: - loadBGRAImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + loadBGRAData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); break; default: UNREACHABLE(); } break; case GL_UNSIGNED_SHORT_5_6_5: - switch (format) + switch (mFormat) { case GL_RGB: - loadRGB565ImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + loadRGB565Data(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); break; default: UNREACHABLE(); } break; case GL_UNSIGNED_SHORT_4_4_4_4: - switch (format) + switch (mFormat) { case GL_RGBA: - loadRGBA4444ImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + loadRGBA4444Data(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); break; default: UNREACHABLE(); } break; case GL_UNSIGNED_SHORT_5_5_5_1: - switch (format) + switch (mFormat) { case GL_RGBA: - loadRGBA5551ImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + loadRGBA5551Data(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); break; default: UNREACHABLE(); } break; case GL_FLOAT: - switch (format) + switch (mFormat) { // float textures are converted to RGBA, not BGRA, as they're stored that way in D3D case GL_ALPHA: - loadAlphaFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + loadAlphaFloatData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); break; case GL_LUMINANCE: - loadLuminanceFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + loadLuminanceFloatData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); break; case GL_LUMINANCE_ALPHA: - loadLuminanceAlphaFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + loadLuminanceAlphaFloatData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); break; case GL_RGB: - loadRGBFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + loadRGBFloatData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); break; case GL_RGBA: - loadRGBAFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + loadRGBAFloatData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); break; default: UNREACHABLE(); } break; case GL_HALF_FLOAT_OES: - switch (format) + switch (mFormat) { // float textures are converted to RGBA, not BGRA, as they're stored that way in D3D case GL_ALPHA: - loadAlphaHalfFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + loadAlphaHalfFloatData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); break; case GL_LUMINANCE: - loadLuminanceHalfFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + loadLuminanceHalfFloatData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); break; case GL_LUMINANCE_ALPHA: - loadLuminanceAlphaHalfFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + loadLuminanceAlphaHalfFloatData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); break; case GL_RGB: - loadRGBHalfFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + loadRGBHalfFloatData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); break; case GL_RGBA: - loadRGBAHalfFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + loadRGBAHalfFloatData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); break; default: UNREACHABLE(); } @@ -349,8 +399,8 @@ void Texture::loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei } } -void Texture::loadAlphaImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, - size_t inputPitch, const void *input, size_t outputPitch, void *output) const +void Image::loadAlphaData(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; @@ -369,8 +419,8 @@ void Texture::loadAlphaImageData(GLint xoffset, GLint yoffset, GLsizei width, GL } } -void Texture::loadAlphaFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, - size_t inputPitch, const void *input, size_t outputPitch, void *output) const +void Image::loadAlphaFloatData(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; @@ -389,8 +439,8 @@ void Texture::loadAlphaFloatImageData(GLint xoffset, GLint yoffset, GLsizei widt } } -void Texture::loadAlphaHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, - size_t inputPitch, const void *input, size_t outputPitch, void *output) const +void Image::loadAlphaHalfFloatData(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; @@ -409,8 +459,8 @@ void Texture::loadAlphaHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei } } -void Texture::loadLuminanceImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, - size_t inputPitch, const void *input, size_t outputPitch, void *output, bool native) const +void Image::loadLuminanceData(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; @@ -438,8 +488,8 @@ void Texture::loadLuminanceImageData(GLint xoffset, GLint yoffset, GLsizei width } } -void Texture::loadLuminanceFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, - size_t inputPitch, const void *input, size_t outputPitch, void *output) const +void Image::loadLuminanceFloatData(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; @@ -458,8 +508,8 @@ void Texture::loadLuminanceFloatImageData(GLint xoffset, GLint yoffset, GLsizei } } -void Texture::loadLuminanceHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, - size_t inputPitch, const void *input, size_t outputPitch, void *output) const +void Image::loadLuminanceHalfFloatData(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; @@ -478,8 +528,8 @@ void Texture::loadLuminanceHalfFloatImageData(GLint xoffset, GLint yoffset, GLsi } } -void Texture::loadLuminanceAlphaImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, - size_t inputPitch, const void *input, size_t outputPitch, void *output, bool native) const +void Image::loadLuminanceAlphaData(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; @@ -507,8 +557,8 @@ void Texture::loadLuminanceAlphaImageData(GLint xoffset, GLint yoffset, GLsizei } } -void Texture::loadLuminanceAlphaFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, - size_t inputPitch, const void *input, size_t outputPitch, void *output) const +void Image::loadLuminanceAlphaFloatData(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; @@ -527,8 +577,8 @@ void Texture::loadLuminanceAlphaFloatImageData(GLint xoffset, GLint yoffset, GLs } } -void Texture::loadLuminanceAlphaHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, - size_t inputPitch, const void *input, size_t outputPitch, void *output) const +void Image::loadLuminanceAlphaHalfFloatData(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; @@ -547,8 +597,8 @@ void Texture::loadLuminanceAlphaHalfFloatImageData(GLint xoffset, GLint yoffset, } } -void Texture::loadRGBUByteImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, - size_t inputPitch, const void *input, size_t outputPitch, void *output) const +void Image::loadRGBUByteData(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; @@ -567,8 +617,8 @@ void Texture::loadRGBUByteImageData(GLint xoffset, GLint yoffset, GLsizei width, } } -void Texture::loadRGB565ImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, - size_t inputPitch, const void *input, size_t outputPitch, void *output) const +void Image::loadRGB565Data(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; @@ -588,8 +638,8 @@ void Texture::loadRGB565ImageData(GLint xoffset, GLint yoffset, GLsizei width, G } } -void Texture::loadRGBFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, - size_t inputPitch, const void *input, size_t outputPitch, void *output) const +void Image::loadRGBFloatData(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; @@ -608,8 +658,8 @@ void Texture::loadRGBFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, } } -void Texture::loadRGBHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, - size_t inputPitch, const void *input, size_t outputPitch, void *output) const +void Image::loadRGBHalfFloatData(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; @@ -628,28 +678,68 @@ void Texture::loadRGBHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei wi } } -void Texture::loadRGBAUByteImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, - size_t inputPitch, const void *input, size_t outputPitch, void *output) const +void Image::loadRGBAUByteDataSSE2(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; + const unsigned int *source = NULL; + unsigned int *dest = NULL; + __m128i brMask = _mm_set1_epi32(0x00ff00ff); 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; + 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 Image::loadRGBAUByteData(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++) { - dest[4 * x + 0] = source[x * 4 + 2]; - dest[4 * x + 1] = source[x * 4 + 1]; - dest[4 * x + 2] = source[x * 4 + 0]; - dest[4 * x + 3] = source[x * 4 + 3]; + unsigned int rgba = source[x]; + dest[x] = (_rotl(rgba, 16) & 0x00ff00ff) | (rgba & 0xff00ff00); } } } -void Texture::loadRGBA4444ImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, - size_t inputPitch, const void *input, size_t outputPitch, void *output) const +void Image::loadRGBA4444Data(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; @@ -669,8 +759,8 @@ void Texture::loadRGBA4444ImageData(GLint xoffset, GLint yoffset, GLsizei width, } } -void Texture::loadRGBA5551ImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, - size_t inputPitch, const void *input, size_t outputPitch, void *output) const +void Image::loadRGBA5551Data(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; @@ -690,8 +780,8 @@ void Texture::loadRGBA5551ImageData(GLint xoffset, GLint yoffset, GLsizei width, } } -void Texture::loadRGBAFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, - size_t inputPitch, const void *input, size_t outputPitch, void *output) const +void Image::loadRGBAFloatData(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; @@ -704,8 +794,8 @@ void Texture::loadRGBAFloatImageData(GLint xoffset, GLint yoffset, GLsizei width } } -void Texture::loadRGBAHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, - size_t inputPitch, const void *input, size_t outputPitch, void *output) const +void Image::loadRGBAHalfFloatData(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; @@ -718,8 +808,8 @@ void Texture::loadRGBAHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei w } } -void Texture::loadBGRAImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, - size_t inputPitch, const void *input, size_t outputPitch, void *output) const +void Image::loadBGRAData(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; @@ -732,158 +822,718 @@ void Texture::loadBGRAImageData(GLint xoffset, GLint yoffset, GLsizei width, GLs } } -void Texture::createSurface(GLsizei width, GLsizei height, GLenum format, GLenum type, Image *img) +void Image::loadCompressedData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, + int inputPitch, const void *input, size_t outputPitch, void *output) const { + switch (getD3DFormat()) + { + case D3DFMT_DXT1: + loadDXT1Data(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + break; + case D3DFMT_DXT3: + loadDXT3Data(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + break; + case D3DFMT_DXT5: + loadDXT5Data(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output); + break; + } +} + +static void FlipCopyDXT1BlockFull(const unsigned int* source, unsigned int* dest) { + // A DXT1 block layout is: + // [0-1] color0. + // [2-3] color1. + // [4-7] color bitmap, 2 bits per pixel. + // So each of the 4-7 bytes represents one line, flipping a block is just + // flipping those bytes. + + // First 32-bits is two RGB565 colors shared by tile and does not need to be modified. + dest[0] = source[0]; + + // Second 32-bits contains 4 rows of 4 2-bit interpolants between the colors. All rows should be flipped. + dest[1] = (source[1] >> 24) | + ((source[1] << 8) & 0x00FF0000) | + ((source[1] >> 8) & 0x0000FF00) | + (source[1] << 24); +} + +// Flips the first 2 lines of a DXT1 block in the y direction. +static void FlipCopyDXT1BlockHalf(const unsigned int* source, unsigned int* dest) { + // See layout above. + dest[0] = source[0]; + dest[1] = ((source[1] << 8) & 0x0000FF00) | + ((source[1] >> 8) & 0x000000FF); +} + +// Flips a full DXT3 block in the y direction. +static void FlipCopyDXT3BlockFull(const unsigned int* source, unsigned int* dest) { + // A DXT3 block layout is: + // [0-7] alpha bitmap, 4 bits per pixel. + // [8-15] a DXT1 block. + + // First and Second 32 bits are 4bit per pixel alpha and need to be flipped. + dest[0] = (source[1] >> 16) | (source[1] << 16); + dest[1] = (source[0] >> 16) | (source[0] << 16); + + // And flip the DXT1 block using the above function. + FlipCopyDXT1BlockFull(source + 2, dest + 2); +} + +// Flips the first 2 lines of a DXT3 block in the y direction. +static void FlipCopyDXT3BlockHalf(const unsigned int* source, unsigned int* dest) { + // See layout above. + dest[0] = (source[1] >> 16) | (source[1] << 16); + FlipCopyDXT1BlockHalf(source + 2, dest + 2); +} + +// Flips a full DXT5 block in the y direction. +static void FlipCopyDXT5BlockFull(const unsigned int* source, unsigned int* dest) { + // A DXT5 block layout is: + // [0] alpha0. + // [1] alpha1. + // [2-7] alpha bitmap, 3 bits per pixel. + // [8-15] a DXT1 block. + + // The alpha bitmap doesn't easily map lines to bytes, so we have to + // interpret it correctly. Extracted from + // http://www.opengl.org/registry/specs/EXT/texture_compression_s3tc.txt : + // + // The 6 "bits" bytes of the block are decoded into one 48-bit integer: + // + // bits = bits_0 + 256 * (bits_1 + 256 * (bits_2 + 256 * (bits_3 + + // 256 * (bits_4 + 256 * bits_5)))) + // + // bits is a 48-bit unsigned integer, from which a three-bit control code + // is extracted for a texel at location (x,y) in the block using: + // + // code(x,y) = bits[3*(4*y+x)+1..3*(4*y+x)+0] + // + // where bit 47 is the most significant and bit 0 is the least + // significant bit. + const unsigned char* sourceBytes = static_cast<const unsigned char*>(static_cast<const void*>(source)); + unsigned char* destBytes = static_cast<unsigned char*>(static_cast<void*>(dest)); + unsigned int line_0_1 = sourceBytes[2] + 256 * (sourceBytes[3] + 256 * sourceBytes[4]); + unsigned int line_2_3 = sourceBytes[5] + 256 * (sourceBytes[6] + 256 * sourceBytes[7]); + // swap lines 0 and 1 in line_0_1. + unsigned int line_1_0 = ((line_0_1 & 0x000fff) << 12) | + ((line_0_1 & 0xfff000) >> 12); + // swap lines 2 and 3 in line_2_3. + unsigned int line_3_2 = ((line_2_3 & 0x000fff) << 12) | + ((line_2_3 & 0xfff000) >> 12); + destBytes[0] = sourceBytes[0]; + destBytes[1] = sourceBytes[1]; + destBytes[2] = line_3_2 & 0xff; + destBytes[3] = (line_3_2 & 0xff00) >> 8; + destBytes[4] = (line_3_2 & 0xff0000) >> 16; + destBytes[5] = line_1_0 & 0xff; + destBytes[6] = (line_1_0 & 0xff00) >> 8; + destBytes[7] = (line_1_0 & 0xff0000) >> 16; + + // And flip the DXT1 block using the above function. + FlipCopyDXT1BlockFull(source + 2, dest + 2); +} + +// Flips the first 2 lines of a DXT5 block in the y direction. +static void FlipCopyDXT5BlockHalf(const unsigned int* source, unsigned int* dest) { + // See layout above. + const unsigned char* sourceBytes = static_cast<const unsigned char*>(static_cast<const void*>(source)); + unsigned char* destBytes = static_cast<unsigned char*>(static_cast<void*>(dest)); + unsigned int line_0_1 = sourceBytes[2] + 256 * (sourceBytes[3] + 256 * sourceBytes[4]); + unsigned int line_1_0 = ((line_0_1 & 0x000fff) << 12) | + ((line_0_1 & 0xfff000) >> 12); + destBytes[0] = sourceBytes[0]; + destBytes[1] = sourceBytes[1]; + destBytes[2] = line_1_0 & 0xff; + destBytes[3] = (line_1_0 & 0xff00) >> 8; + destBytes[4] = (line_1_0 & 0xff0000) >> 16; + FlipCopyDXT1BlockHalf(source + 2, dest + 2); +} + +void Image::loadDXT1Data(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, + int inputPitch, const void *input, size_t outputPitch, void *output) const +{ + ASSERT(xoffset % 4 == 0); + ASSERT(yoffset % 4 == 0); + ASSERT(width % 4 == 0 || width == 2 || width == 1); + ASSERT(inputPitch % 8 == 0); + ASSERT(outputPitch % 8 == 0); + + const unsigned int *source = reinterpret_cast<const unsigned int*>(input); + unsigned int *dest = reinterpret_cast<unsigned int*>(output); + + // Round width up in case it is less than 4. + int blocksAcross = (width + 3) / 4; + int intsAcross = blocksAcross * 2; + + switch (height) + { + case 1: + for (int x = 0; x < intsAcross; x += 2) + { + // just copy the block + dest[x] = source[x]; + dest[x + 1] = source[x + 1]; + } + break; + case 2: + for (int x = 0; x < intsAcross; x += 2) + { + FlipCopyDXT1BlockHalf(source + x, dest + x); + } + break; + default: + ASSERT(height % 4 == 0); + for (int y = 0; y < height / 4; ++y) + { + const unsigned int *source = reinterpret_cast<const unsigned int*>(static_cast<const unsigned char*>(input) + y * inputPitch); + unsigned int *dest = reinterpret_cast<unsigned int*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8); + + for (int x = 0; x < intsAcross; x += 2) + { + FlipCopyDXT1BlockFull(source + x, dest + x); + } + } + break; + } +} + +void Image::loadDXT3Data(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, + int inputPitch, const void *input, size_t outputPitch, void *output) const { - IDirect3DTexture9 *newTexture = NULL; - IDirect3DSurface9 *newSurface = NULL; + ASSERT(xoffset % 4 == 0); + ASSERT(yoffset % 4 == 0); + ASSERT(width % 4 == 0 || width == 2 || width == 1); + ASSERT(inputPitch % 16 == 0); + ASSERT(outputPitch % 16 == 0); + + const unsigned int *source = reinterpret_cast<const unsigned int*>(input); + unsigned int *dest = reinterpret_cast<unsigned int*>(output); + + // Round width up in case it is less than 4. + int blocksAcross = (width + 3) / 4; + int intsAcross = blocksAcross * 4; - if (width != 0 && height != 0) + switch (height) { - int levelToFetch = 0; - GLsizei requestWidth = width; - GLsizei requestHeight = height; - if (IsCompressed(format) && (width % 4 != 0 || height % 4 != 0)) - { - bool isMult4 = false; - int upsampleCount = 0; - while (!isMult4) + case 1: + for (int x = 0; x < intsAcross; x += 4) { - requestWidth <<= 1; - requestHeight <<= 1; - upsampleCount++; - if (requestWidth % 4 == 0 && requestHeight % 4 == 0) + // just copy the block + dest[x] = source[x]; + dest[x + 1] = source[x + 1]; + dest[x + 2] = source[x + 2]; + dest[x + 3] = source[x + 3]; + } + break; + case 2: + for (int x = 0; x < intsAcross; x += 4) + { + FlipCopyDXT3BlockHalf(source + x, dest + x); + } + break; + default: + ASSERT(height % 4 == 0); + for (int y = 0; y < height / 4; ++y) + { + const unsigned int *source = reinterpret_cast<const unsigned int*>(static_cast<const unsigned char*>(input) + y * inputPitch); + unsigned int *dest = reinterpret_cast<unsigned int*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16); + + for (int x = 0; x < intsAcross; x += 4) { - isMult4 = true; + FlipCopyDXT3BlockFull(source + x, dest + x); } } - levelToFetch = upsampleCount; + break; + } +} + +void Image::loadDXT5Data(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, + int inputPitch, const void *input, size_t outputPitch, void *output) const +{ + ASSERT(xoffset % 4 == 0); + ASSERT(yoffset % 4 == 0); + ASSERT(width % 4 == 0 || width == 2 || width == 1); + ASSERT(inputPitch % 16 == 0); + ASSERT(outputPitch % 16 == 0); + + const unsigned int *source = reinterpret_cast<const unsigned int*>(input); + unsigned int *dest = reinterpret_cast<unsigned int*>(output); + + // Round width up in case it is less than 4. + int blocksAcross = (width + 3) / 4; + int intsAcross = blocksAcross * 4; + + switch (height) + { + case 1: + for (int x = 0; x < intsAcross; x += 4) + { + // just copy the block + dest[x] = source[x]; + dest[x + 1] = source[x + 1]; + dest[x + 2] = source[x + 2]; + dest[x + 3] = source[x + 3]; + } + break; + case 2: + for (int x = 0; x < intsAcross; x += 4) + { + FlipCopyDXT5BlockHalf(source + x, dest + x); + } + break; + default: + ASSERT(height % 4 == 0); + for (int y = 0; y < height / 4; ++y) + { + const unsigned int *source = reinterpret_cast<const unsigned int*>(static_cast<const unsigned char*>(input) + y * inputPitch); + unsigned int *dest = reinterpret_cast<unsigned int*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16); + + for (int x = 0; x < intsAcross; x += 4) + { + FlipCopyDXT5BlockFull(source + x, dest + x); + } + } + break; + } +} + +// This implements glCopyTex[Sub]Image2D for non-renderable internal texture formats and incomplete textures +void Image::copy(GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, IDirect3DSurface9 *renderTarget) +{ + IDirect3DDevice9 *device = getDevice(); + IDirect3DSurface9 *renderTargetData = NULL; + D3DSURFACE_DESC description; + renderTarget->GetDesc(&description); + + HRESULT result = device->CreateOffscreenPlainSurface(description.Width, description.Height, description.Format, D3DPOOL_SYSTEMMEM, &renderTargetData, NULL); + + if (FAILED(result)) + { + ERR("Could not create matching destination surface."); + return error(GL_OUT_OF_MEMORY); + } + + result = device->GetRenderTargetData(renderTarget, renderTargetData); + + if (FAILED(result)) + { + ERR("GetRenderTargetData unexpectedly failed."); + renderTargetData->Release(); + return error(GL_OUT_OF_MEMORY); + } + + RECT sourceRect = transformPixelRect(x, y, width, height, description.Height); + int destYOffset = transformPixelYOffset(yoffset, height, mHeight); + RECT destRect = {xoffset, destYOffset, xoffset + width, destYOffset + height}; + + if (isRenderableFormat()) + { + result = D3DXLoadSurfaceFromSurface(getSurface(), NULL, &destRect, renderTargetData, NULL, &sourceRect, D3DX_FILTER_BOX, 0); + + if (FAILED(result)) + { + ERR("Copying surfaces unexpectedly failed."); + renderTargetData->Release(); + return error(GL_OUT_OF_MEMORY); } + } + else + { + D3DLOCKED_RECT sourceLock = {0}; + result = renderTargetData->LockRect(&sourceLock, &sourceRect, 0); - HRESULT result = getDevice()->CreateTexture(requestWidth, requestHeight, levelToFetch + 1, NULL, selectFormat(format, type), - D3DPOOL_SYSTEMMEM, &newTexture, NULL); + if (FAILED(result)) + { + ERR("Failed to lock the source surface (rectangle might be invalid)."); + renderTargetData->Release(); + return error(GL_OUT_OF_MEMORY); + } + D3DLOCKED_RECT destLock = {0}; + result = lock(&destLock, &destRect); + if (FAILED(result)) { - ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); + ERR("Failed to lock the destination surface (rectangle might be invalid)."); + renderTargetData->UnlockRect(); + renderTargetData->Release(); return error(GL_OUT_OF_MEMORY); } - newTexture->GetSurfaceLevel(levelToFetch, &newSurface); - newTexture->Release(); + if (destLock.pBits && sourceLock.pBits) + { + unsigned char *source = (unsigned char*)sourceLock.pBits; + unsigned char *dest = (unsigned char*)destLock.pBits; + + switch (description.Format) + { + case D3DFMT_X8R8G8B8: + case D3DFMT_A8R8G8B8: + switch(getD3DFormat()) + { + case D3DFMT_L8: + for(int y = 0; y < height; y++) + { + for(int x = 0; x < width; x++) + { + dest[x] = source[x * 4 + 2]; + } + + source += sourceLock.Pitch; + dest += destLock.Pitch; + } + break; + case D3DFMT_A8L8: + for(int y = 0; y < height; y++) + { + for(int x = 0; x < width; x++) + { + dest[x * 2 + 0] = source[x * 4 + 2]; + dest[x * 2 + 1] = source[x * 4 + 3]; + } + + source += sourceLock.Pitch; + dest += destLock.Pitch; + } + break; + default: + UNREACHABLE(); + } + break; + case D3DFMT_R5G6B5: + switch(getD3DFormat()) + { + case D3DFMT_L8: + for(int y = 0; y < height; y++) + { + for(int x = 0; x < width; x++) + { + unsigned char red = source[x * 2 + 1] & 0xF8; + dest[x] = red | (red >> 5); + } + + source += sourceLock.Pitch; + dest += destLock.Pitch; + } + break; + default: + UNREACHABLE(); + } + break; + case D3DFMT_A1R5G5B5: + switch(getD3DFormat()) + { + case D3DFMT_L8: + for(int y = 0; y < height; y++) + { + for(int x = 0; x < width; x++) + { + unsigned char red = source[x * 2 + 1] & 0x7C; + dest[x] = (red << 1) | (red >> 4); + } + + source += sourceLock.Pitch; + dest += destLock.Pitch; + } + break; + case D3DFMT_A8L8: + for(int y = 0; y < height; y++) + { + for(int x = 0; x < width; x++) + { + unsigned char red = source[x * 2 + 1] & 0x7C; + dest[x * 2 + 0] = (red << 1) | (red >> 4); + dest[x * 2 + 1] = (signed char)source[x * 2 + 1] >> 7; + } + + source += sourceLock.Pitch; + dest += destLock.Pitch; + } + break; + default: + UNREACHABLE(); + } + break; + default: + UNREACHABLE(); + } + } + + unlock(); + renderTargetData->UnlockRect(); } - if (img->surface) img->surface->Release(); - img->surface = newSurface; + renderTargetData->Release(); + + mDirty = true; +} + +TextureStorage::TextureStorage(bool renderTarget) + : mRenderTarget(renderTarget), + mD3DPool(getDisplay()->getTexturePool(mRenderTarget)), + mTextureSerial(issueTextureSerial()) +{ +} + +TextureStorage::~TextureStorage() +{ +} + +bool TextureStorage::isRenderTarget() const +{ + return mRenderTarget; +} + +bool TextureStorage::isManaged() const +{ + return (mD3DPool == D3DPOOL_MANAGED); +} + +D3DPOOL TextureStorage::getPool() const +{ + return mD3DPool; +} - img->width = width; - img->height = height; - img->format = format; +unsigned int TextureStorage::getTextureSerial() const +{ + return mTextureSerial; } -void Texture::setImage(GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *img) +unsigned int TextureStorage::issueTextureSerial() { - createSurface(width, height, format, type, img); + return mCurrentTextureSerial++; +} - if (pixels != NULL && img->surface != NULL) +Texture::Texture(GLuint id) : RefCountObject(id) +{ + mMinFilter = GL_NEAREST_MIPMAP_LINEAR; + mMagFilter = GL_LINEAR; + mWrapS = GL_REPEAT; + mWrapT = GL_REPEAT; + mDirtyParameters = true; + mUsage = GL_NONE; + + mDirtyImages = true; + + mImmutable = false; +} + +Texture::~Texture() +{ +} + +// Returns true on successful filter state update (valid enum parameter) +bool Texture::setMinFilter(GLenum filter) +{ + switch (filter) { - D3DSURFACE_DESC description; - img->surface->GetDesc(&description); + 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: + { + if (mMinFilter != filter) + { + mMinFilter = filter; + mDirtyParameters = true; + } + return true; + } + default: + return false; + } +} - D3DLOCKED_RECT locked; - HRESULT result = img->surface->LockRect(&locked, NULL, 0); +// Returns true on successful filter state update (valid enum parameter) +bool Texture::setMagFilter(GLenum filter) +{ + switch (filter) + { + case GL_NEAREST: + case GL_LINEAR: + { + if (mMagFilter != filter) + { + mMagFilter = filter; + mDirtyParameters = true; + } + return true; + } + default: + return false; + } +} - ASSERT(SUCCEEDED(result)); +// 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: + { + if (mWrapS != wrap) + { + mWrapS = wrap; + mDirtyParameters = true; + } + return true; + } + default: + return false; + } +} - if (SUCCEEDED(result)) +// 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: { - loadImageData(0, 0, width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits, &description); - img->surface->UnlockRect(); + if (mWrapT != wrap) + { + mWrapT = wrap; + mDirtyParameters = true; + } + return true; } + default: + return false; + } +} - img->dirty = true; +// Returns true on successful usage state update (valid enum parameter) +bool Texture::setUsage(GLenum usage) +{ + switch (usage) + { + case GL_NONE: + case GL_FRAMEBUFFER_ATTACHMENT_ANGLE: + mUsage = usage; + return true; + default: + return false; } +} - mDirtyMetaData = true; +GLenum Texture::getMinFilter() const +{ + return mMinFilter; } -void Texture::setCompressedImage(GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, Image *img) +GLenum Texture::getMagFilter() const +{ + return mMagFilter; +} + +GLenum Texture::getWrapS() const { - createSurface(width, height, format, GL_UNSIGNED_BYTE, img); + return mWrapS; +} - if (pixels != NULL && img->surface != NULL) +GLenum Texture::getWrapT() const +{ + return mWrapT; +} + +GLenum Texture::getUsage() const +{ + return mUsage; +} + +void Texture::setImage(GLint unpackAlignment, const void *pixels, Image *image) +{ + if (pixels != NULL) { D3DLOCKED_RECT locked; - HRESULT result = img->surface->LockRect(&locked, NULL, 0); - - ASSERT(SUCCEEDED(result)); + HRESULT result = image->lock(&locked, NULL); if (SUCCEEDED(result)) { - memcpy(locked.pBits, pixels, imageSize); - img->surface->UnlockRect(); + image->loadData(0, 0, image->getWidth(), image->getHeight(), image->getType(), unpackAlignment, pixels, locked.Pitch, locked.pBits); + image->unlock(); } - img->dirty = true; + mDirtyImages = true; } +} + +void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, Image *image) +{ + if (pixels != NULL) + { + D3DLOCKED_RECT locked; + HRESULT result = image->lock(&locked, NULL); - mDirtyMetaData = true; + if (SUCCEEDED(result)) + { + int inputPitch = ComputeCompressedPitch(image->getWidth(), image->getFormat()); + int inputSize = ComputeCompressedSize(image->getWidth(), image->getHeight(), image->getFormat()); + image->loadCompressedData(0, 0, image->getWidth(), image->getHeight(), -inputPitch, static_cast<const char*>(pixels) + inputSize - inputPitch, locked.Pitch, locked.pBits); + image->unlock(); + } + + mDirtyImages = true; + } } -bool Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *img) +bool Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *image) { - if (width + xoffset > img->width || height + yoffset > img->height) + if (width + xoffset > image->getWidth() || height + yoffset > image->getHeight()) { error(GL_INVALID_VALUE); return false; } - if (!img->surface) + if (IsCompressed(image->getFormat())) { - createSurface(img->width, img->height, format, type, img); + error(GL_INVALID_OPERATION); + return false; } - if (pixels != NULL && img->surface != NULL) + if (format != image->getFormat()) { - D3DSURFACE_DESC description; - img->surface->GetDesc(&description); + error(GL_INVALID_OPERATION); + return false; + } + if (pixels != NULL) + { D3DLOCKED_RECT locked; - HRESULT result = img->surface->LockRect(&locked, NULL, 0); - - ASSERT(SUCCEEDED(result)); + HRESULT result = image->lock(&locked, NULL); if (SUCCEEDED(result)) { - loadImageData(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits, &description); - img->surface->UnlockRect(); + image->loadData(xoffset, transformPixelYOffset(yoffset, height, image->getHeight()), width, height, type, unpackAlignment, pixels, locked.Pitch, locked.pBits); + image->unlock(); } - img->dirty = true; + mDirtyImages = true; } return true; } -bool Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, Image *img) +bool Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, Image *image) { - if (width + xoffset > img->width || height + yoffset > img->height) + if (width + xoffset > image->getWidth() || height + yoffset > image->getHeight()) { error(GL_INVALID_VALUE); return false; } - if (format != getFormat()) + if (format != getInternalFormat()) { error(GL_INVALID_OPERATION); return false; } - if (!img->surface) - { - createSurface(img->width, img->height, format, GL_UNSIGNED_BYTE, img); - } - - if (pixels != NULL && img->surface != NULL) + if (pixels != NULL) { RECT updateRegion; updateRegion.left = xoffset; @@ -892,419 +1542,354 @@ bool Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GL updateRegion.top = yoffset; D3DLOCKED_RECT locked; - HRESULT result = img->surface->LockRect(&locked, &updateRegion, 0); - - ASSERT(SUCCEEDED(result)); + HRESULT result = image->lock(&locked, &updateRegion); if (SUCCEEDED(result)) { - GLsizei inputPitch = ComputeCompressedPitch(width, format); - int rows = imageSize / inputPitch; - for (int i = 0; i < rows; ++i) - { - memcpy((void*)((BYTE*)locked.pBits + i * locked.Pitch), (void*)((BYTE*)pixels + i * inputPitch), inputPitch); - } - img->surface->UnlockRect(); + int inputPitch = ComputeCompressedPitch(width, format); + int inputSize = ComputeCompressedSize(width, height, format); + image->loadCompressedData(xoffset, transformPixelYOffset(yoffset, height, image->getHeight()), width, height, -inputPitch, static_cast<const char*>(pixels) + inputSize - inputPitch, locked.Pitch, locked.pBits); + image->unlock(); } - img->dirty = true; + mDirtyImages = true; } return true; } -// This implements glCopyTex[Sub]Image2D for non-renderable internal texture formats -void Texture::copyNonRenderable(Image *image, GLenum internalFormat, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, IDirect3DSurface9 *renderTarget) +IDirect3DBaseTexture9 *Texture::getTexture() { - IDirect3DDevice9 *device = getDevice(); - IDirect3DSurface9 *surface = NULL; - D3DSURFACE_DESC description; - renderTarget->GetDesc(&description); - - HRESULT result = device->CreateOffscreenPlainSurface(description.Width, description.Height, description.Format, D3DPOOL_SYSTEMMEM, &surface, NULL); - - if (!SUCCEEDED(result)) + if (!isSamplerComplete()) { - ERR("Could not create matching destination surface."); - return error(GL_OUT_OF_MEMORY); + return NULL; } - result = device->GetRenderTargetData(renderTarget, surface); - - if (!SUCCEEDED(result)) + // ensure the underlying texture is created + if (getStorage(false) == NULL) { - ERR("GetRenderTargetData unexpectedly failed."); - surface->Release(); - return error(GL_OUT_OF_MEMORY); + return NULL; } - D3DLOCKED_RECT sourceLock = {0}; - RECT sourceRect = {x, y, x + width, y + height}; - result = surface->LockRect(&sourceLock, &sourceRect, 0); + updateTexture(); - if (FAILED(result)) - { - ERR("Failed to lock the source surface (rectangle might be invalid)."); - surface->UnlockRect(); - surface->Release(); - return error(GL_OUT_OF_MEMORY); - } + return getBaseTexture(); +} - if (!image->surface) - { - createSurface(width, height, internalFormat, mType, image); - } +bool Texture::hasDirtyParameters() const +{ + return mDirtyParameters; +} - if (image->surface == NULL) - { - ERR("Failed to create an image surface."); - surface->UnlockRect(); - surface->Release(); - return error(GL_OUT_OF_MEMORY); - } +bool Texture::hasDirtyImages() const +{ + return mDirtyImages; +} - D3DLOCKED_RECT destLock = {0}; - RECT destRect = {xoffset, yoffset, xoffset + width, yoffset + height}; - result = image->surface->LockRect(&destLock, &destRect, 0); - - if (FAILED(result)) +void Texture::resetDirty() +{ + mDirtyParameters = false; + mDirtyImages = false; +} + +unsigned int Texture::getTextureSerial() +{ + TextureStorage *texture = getStorage(false); + return texture ? texture->getTextureSerial() : 0; +} + +unsigned int Texture::getRenderTargetSerial(GLenum target) +{ + TextureStorage *texture = getStorage(true); + return texture ? texture->getRenderTargetSerial(target) : 0; +} + +bool Texture::isImmutable() const +{ + return mImmutable; +} + +GLint Texture::creationLevels(GLsizei width, GLsizei height) const +{ + if ((isPow2(width) && isPow2(height)) || getContext()->supportsNonPower2Texture()) { - ERR("Failed to lock the destination surface (rectangle might be invalid)."); - surface->UnlockRect(); - surface->Release(); - return error(GL_OUT_OF_MEMORY); + return 0; // Maximum number of levels } - - if (destLock.pBits && sourceLock.pBits) + else { - unsigned char *source = (unsigned char*)sourceLock.pBits; - unsigned char *dest = (unsigned char*)destLock.pBits; + // OpenGL ES 2.0 without GL_OES_texture_npot does not permit NPOT mipmaps. + return 1; + } +} - switch (description.Format) - { - case D3DFMT_X8R8G8B8: - case D3DFMT_A8R8G8B8: - switch(getD3DFormat()) - { - case D3DFMT_L8: - for(int y = 0; y < height; y++) - { - for(int x = 0; x < width; x++) - { - dest[x] = source[x * 4 + 2]; - } +GLint Texture::creationLevels(GLsizei size) const +{ + return creationLevels(size, size); +} - source += sourceLock.Pitch; - dest += destLock.Pitch; - } - break; - case D3DFMT_A8L8: - for(int y = 0; y < height; y++) - { - for(int x = 0; x < width; x++) - { - dest[x * 2 + 0] = source[x * 4 + 2]; - dest[x * 2 + 1] = source[x * 4 + 3]; - } +int Texture::levelCount() const +{ + return getBaseTexture() ? getBaseTexture()->GetLevelCount() : 0; +} - source += sourceLock.Pitch; - dest += destLock.Pitch; - } - break; - default: - UNREACHABLE(); - } - break; - case D3DFMT_R5G6B5: - switch(getD3DFormat()) - { - case D3DFMT_L8: - for(int y = 0; y < height; y++) - { - for(int x = 0; x < width; x++) - { - unsigned char red = source[x * 2 + 1] & 0xF8; - dest[x] = red | (red >> 5); - } +Blit *Texture::getBlitter() +{ + Context *context = getContext(); + return context->getBlitter(); +} - source += sourceLock.Pitch; - dest += destLock.Pitch; - } - break; - default: - UNREACHABLE(); - } - break; - case D3DFMT_A1R5G5B5: - switch(getD3DFormat()) - { - case D3DFMT_L8: - for(int y = 0; y < height; y++) - { - for(int x = 0; x < width; x++) - { - unsigned char red = source[x * 2 + 1] & 0x7C; - dest[x] = (red << 1) | (red >> 4); - } +bool Texture::copyToRenderTarget(IDirect3DSurface9 *dest, IDirect3DSurface9 *source, bool fromManaged) +{ + if (source && dest) + { + HRESULT result; - source += sourceLock.Pitch; - dest += destLock.Pitch; - } - break; - case D3DFMT_A8L8: - for(int y = 0; y < height; y++) - { - for(int x = 0; x < width; x++) - { - unsigned char red = source[x * 2 + 1] & 0x7C; - dest[x * 2 + 0] = (red << 1) | (red >> 4); - dest[x * 2 + 1] = (signed char)source[x * 2 + 1] >> 7; - } + if (fromManaged) + { + result = D3DXLoadSurfaceFromSurface(dest, NULL, NULL, source, NULL, NULL, D3DX_FILTER_BOX, 0); + } + else + { + egl::Display *display = getDisplay(); + IDirect3DDevice9 *device = display->getDevice(); - source += sourceLock.Pitch; - dest += destLock.Pitch; - } - break; - default: - UNREACHABLE(); - } - break; - default: - UNREACHABLE(); + display->endScene(); + result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE); } - image->dirty = true; - mDirtyMetaData = true; + if (FAILED(result)) + { + ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); + return false; + } } - image->surface->UnlockRect(); - surface->UnlockRect(); - surface->Release(); + return true; } -D3DFORMAT Texture::getD3DFormat() const +TextureStorage2D::TextureStorage2D(IDirect3DTexture9 *surfaceTexture) : TextureStorage(true), mRenderTargetSerial(RenderbufferStorage::issueSerial()) { - return selectFormat(getFormat(), mType); + mTexture = surfaceTexture; } -IDirect3DBaseTexture9 *Texture::getTexture() +TextureStorage2D::TextureStorage2D(int levels, D3DFORMAT format, int width, int height, bool renderTarget) + : TextureStorage(renderTarget), mRenderTargetSerial(RenderbufferStorage::issueSerial()) { - if (!isComplete()) + IDirect3DDevice9 *device = getDevice(); + + mTexture = NULL; + HRESULT result = device->CreateTexture(width, height, levels, isRenderTarget() ? D3DUSAGE_RENDERTARGET : 0, format, getPool(), &mTexture, NULL); + + if (FAILED(result)) { - return NULL; + ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); + error(GL_OUT_OF_MEMORY); } +} - if (mDirtyMetaData) +TextureStorage2D::~TextureStorage2D() +{ + if (mTexture) { - mBaseTexture = createTexture(); - mIsRenderable = false; + mTexture->Release(); } +} + +IDirect3DSurface9 *TextureStorage2D::getSurfaceLevel(int level) +{ + IDirect3DSurface9 *surface = NULL; - if (mDirtyMetaData || dirtyImageData()) + if (mTexture) { - updateTexture(); + HRESULT result = mTexture->GetSurfaceLevel(level, &surface); + ASSERT(SUCCEEDED(result)); } - mDirtyMetaData = false; - ASSERT(!dirtyImageData()); + return surface; +} - return mBaseTexture; +IDirect3DBaseTexture9 *TextureStorage2D::getBaseTexture() const +{ + return mTexture; } -bool Texture::isDirty() const +unsigned int TextureStorage2D::getRenderTargetSerial(GLenum target) const { - return (mDirty || mDirtyMetaData || dirtyImageData()); + return mRenderTargetSerial; } -// Returns the top-level texture surface as a render target -void Texture::needRenderTarget() +Texture2D::Texture2D(GLuint id) : Texture(id) { - if (!mIsRenderable) - { - mBaseTexture = convertToRenderTarget(); - mIsRenderable = true; - } + mTexStorage = NULL; + mSurface = NULL; + mColorbufferProxy = NULL; + mProxyRefs = 0; +} - if (dirtyImageData()) +Texture2D::~Texture2D() +{ + mColorbufferProxy = NULL; + + delete mTexStorage; + mTexStorage = NULL; + + if (mSurface) { - updateTexture(); + mSurface->setBoundTexture(NULL); + mSurface = NULL; } +} - mDirtyMetaData = false; +// We need to maintain a count of references to renderbuffers acting as +// proxies for this texture, so that we do not attempt to use a pointer +// to a renderbuffer proxy which has been deleted. +void Texture2D::addProxyRef(const Renderbuffer *proxy) +{ + mProxyRefs++; } -void Texture::dropTexture() +void Texture2D::releaseProxy(const Renderbuffer *proxy) { - if (mBaseTexture) - { - mBaseTexture = NULL; - } + if (mProxyRefs > 0) + mProxyRefs--; - mIsRenderable = false; + if (mProxyRefs == 0) + mColorbufferProxy = NULL; } -void Texture::pushTexture(IDirect3DBaseTexture9 *newTexture, bool renderable) +GLenum Texture2D::getTarget() const { - mBaseTexture = newTexture; - mDirtyMetaData = false; - mIsRenderable = renderable; - mDirty = true; + return GL_TEXTURE_2D; } - -GLint Texture::creationLevels(GLsizei width, GLsizei height, GLint maxlevel) const +GLsizei Texture2D::getWidth(GLint level) const { - if (isPow2(width) && isPow2(height)) - { - return maxlevel; - } + if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) + return mImageArray[level].getWidth(); else - { - // OpenGL ES 2.0 without GL_OES_texture_npot does not permit NPOT mipmaps. - return 1; - } + return 0; } -GLint Texture::creationLevels(GLsizei size, GLint maxlevel) const +GLsizei Texture2D::getHeight(GLint level) const { - return creationLevels(size, size, maxlevel); + if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) + return mImageArray[level].getHeight(); + else + return 0; } -int Texture::levelCount() const +GLenum Texture2D::getInternalFormat() const { - return mBaseTexture ? mBaseTexture->GetLevelCount() : 0; + return mImageArray[0].getFormat(); } -bool Texture::isRenderable() const +GLenum Texture2D::getType() const { - return mIsRenderable; + return mImageArray[0].getType(); } -Texture2D::Texture2D(GLuint id) : Texture(id) +D3DFORMAT Texture2D::getD3DFormat() const { - mTexture = NULL; + return mImageArray[0].getD3DFormat(); } -Texture2D::~Texture2D() +void Texture2D::redefineImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLenum type) { - mColorbufferProxy.set(NULL); + releaseTexImage(); - if (mTexture) + bool redefined = mImageArray[level].redefine(format, width, height, type, false); + + if (mTexStorage && redefined) { - mTexture->Release(); - mTexture = NULL; + for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) + { + mImageArray[i].markDirty(); + } + + delete mTexStorage; + mTexStorage = NULL; + mDirtyImages = true; } } -GLenum Texture2D::getTarget() const +void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) { - return GL_TEXTURE_2D; -} + redefineImage(level, format, width, height, type); -GLenum Texture2D::getFormat() const -{ - return mImageArray[0].format; + Texture::setImage(unpackAlignment, pixels, &mImageArray[level]); } -// While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture -// for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels. -// Call this when a particular level of the texture must be defined with a specific format, width and height. -// -// Returns true if the existing texture was unsuitable and had to be destroyed. If so, it will also set -// a new height and width for the texture by working backwards from the given width and height. -bool Texture2D::redefineTexture(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum type) +void Texture2D::bindTexImage(egl::Surface *surface) { - bool widthOkay = (mWidth >> level == width); - bool heightOkay = (mHeight >> level == height); + releaseTexImage(); - bool sizeOkay = ((widthOkay && heightOkay) - || (widthOkay && mHeight >> level == 0 && height == 1) - || (heightOkay && mWidth >> level == 0 && width == 1)); + GLenum format; - bool typeOkay = (type == mType); + switch(surface->getFormat()) + { + case D3DFMT_A8R8G8B8: + format = GL_RGBA; + break; + case D3DFMT_X8R8G8B8: + format = GL_RGB; + break; + default: + UNIMPLEMENTED(); + return; + } - bool textureOkay = (sizeOkay && typeOkay && internalFormat == mImageArray[0].format); + mImageArray[0].redefine(format, surface->getWidth(), surface->getHeight(), GL_UNSIGNED_BYTE, true); - if (!textureOkay) - { - TRACE("Redefining 2D texture (%d, 0x%04X, %d, %d => 0x%04X, %d, %d).", level, - mImageArray[0].format, mWidth, mHeight, - internalFormat, width, height); + delete mTexStorage; + mTexStorage = new TextureStorage2D(surface->getOffscreenTexture()); - // Purge all the levels and the texture. + mDirtyImages = true; + mSurface = surface; + mSurface->setBoundTexture(this); +} - for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) - { - if (mImageArray[i].surface != NULL) - { - mImageArray[i].dirty = false; +void Texture2D::releaseTexImage() +{ + if (mSurface) + { + mSurface->setBoundTexture(NULL); + mSurface = NULL; - mImageArray[i].surface->Release(); - mImageArray[i].surface = NULL; - } + if (mTexStorage) + { + delete mTexStorage; + mTexStorage = NULL; } - if (mTexture != NULL) + for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) { - mTexture->Release(); - mTexture = NULL; - dropTexture(); + mImageArray[i].redefine(GL_RGBA, 0, 0, GL_UNSIGNED_BYTE, true); } - - mWidth = width << level; - mHeight = height << level; - mImageArray[0].format = internalFormat; - mType = type; } - - return !textureOkay; } -void Texture2D::setImage(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) +void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels) { - redefineTexture(level, internalFormat, width, height, type); + redefineImage(level, format, width, height, GL_UNSIGNED_BYTE); - Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[level]); -} - -void Texture2D::setCompressedImage(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels) -{ - redefineTexture(level, internalFormat, width, height, GL_UNSIGNED_BYTE); - - Texture::setCompressedImage(width, height, internalFormat, imageSize, pixels, &mImageArray[level]); + Texture::setCompressedImage(imageSize, pixels, &mImageArray[level]); } void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height) { - ASSERT(mImageArray[level].surface != NULL); + ASSERT(mImageArray[level].getSurface() != NULL); if (level < levelCount()) { - IDirect3DSurface9 *destLevel = NULL; - HRESULT result = mTexture->GetSurfaceLevel(level, &destLevel); + IDirect3DSurface9 *destLevel = mTexStorage->getSurfaceLevel(level); - ASSERT(SUCCEEDED(result)); - - if (SUCCEEDED(result)) + if (destLevel) { - Image *img = &mImageArray[level]; - - RECT sourceRect; - sourceRect.left = xoffset; - sourceRect.top = yoffset; - sourceRect.right = xoffset + width; - sourceRect.bottom = yoffset + height; - - POINT destPoint; - destPoint.x = xoffset; - destPoint.y = yoffset; - - result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint); - ASSERT(SUCCEEDED(result)); + Image *image = &mImageArray[level]; + image->updateSurface(destLevel, xoffset, yoffset, width, height); destLevel->Release(); - - img->dirty = false; + image->markClean(); } } } @@ -1325,7 +1910,7 @@ void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GL } } -void Texture2D::copyImage(GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source) +void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) { IDirect3DSurface9 *renderTarget = source->getRenderTarget(); @@ -1335,48 +1920,48 @@ void Texture2D::copyImage(GLint level, GLenum internalFormat, GLint x, GLint y, return error(GL_OUT_OF_MEMORY); } - bool redefined = redefineTexture(level, internalFormat, width, height, mType); + redefineImage(level, format, width, height, GL_UNSIGNED_BYTE); - if (!isRenderableFormat()) + if (!mImageArray[level].isRenderableFormat()) { - copyNonRenderable(&mImageArray[level], internalFormat, 0, 0, x, y, width, height, renderTarget); + mImageArray[level].copy(0, 0, x, y, width, height, renderTarget); + mDirtyImages = true; } else { - if (redefined) + if (!mTexStorage || !mTexStorage->isRenderTarget()) { convertToRenderTarget(); - pushTexture(mTexture, true); - } - else - { - needRenderTarget(); } + + mImageArray[level].markClean(); if (width != 0 && height != 0 && level < levelCount()) { - RECT sourceRect; - sourceRect.left = x; - sourceRect.right = x + width; - sourceRect.top = y; - sourceRect.bottom = y + height; + RECT sourceRect = transformPixelRect(x, y, width, height, source->getColorbuffer()->getHeight()); + sourceRect.left = clamp(sourceRect.left, 0, source->getColorbuffer()->getWidth()); + sourceRect.top = clamp(sourceRect.top, 0, source->getColorbuffer()->getHeight()); + sourceRect.right = clamp(sourceRect.right, 0, source->getColorbuffer()->getWidth()); + sourceRect.bottom = clamp(sourceRect.bottom, 0, source->getColorbuffer()->getHeight()); - IDirect3DSurface9 *dest; - HRESULT hr = mTexture->GetSurfaceLevel(level, &dest); + GLint destYOffset = transformPixelYOffset(0, height, mImageArray[level].getHeight()); + + IDirect3DSurface9 *dest = mTexStorage->getSurfaceLevel(level); - getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest); - dest->Release(); + if (dest) + { + getBlitter()->copy(renderTarget, sourceRect, format, 0, destYOffset, dest); + dest->Release(); + } } } - mImageArray[level].width = width; - mImageArray[level].height = height; - mImageArray[level].format = internalFormat; + renderTarget->Release(); } -void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source) +void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) { - if (xoffset + width > mImageArray[level].width || yoffset + height > mImageArray[level].height) + if (xoffset + width > mImageArray[level].getWidth() || yoffset + height > mImageArray[level].getHeight()) { return error(GL_INVALID_VALUE); } @@ -1389,46 +1974,83 @@ void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yo return error(GL_OUT_OF_MEMORY); } - bool redefined = redefineTexture(0, mImageArray[0].format, mImageArray[0].width, mImageArray[0].height, mType); - - if (!isRenderableFormat()) + if (!mImageArray[level].isRenderableFormat() || (!mTexStorage && !isSamplerComplete())) { - copyNonRenderable(&mImageArray[level], getFormat(), xoffset, yoffset, x, y, width, height, renderTarget); + mImageArray[level].copy(xoffset, yoffset, x, y, width, height, renderTarget); + mDirtyImages = true; } else { - if (redefined) + if (!mTexStorage || !mTexStorage->isRenderTarget()) { convertToRenderTarget(); - pushTexture(mTexture, true); - } - else - { - needRenderTarget(); } + + updateTexture(); if (level < levelCount()) { - RECT sourceRect; - sourceRect.left = x; - sourceRect.right = x + width; - sourceRect.top = y; - sourceRect.bottom = y + height; + RECT sourceRect = transformPixelRect(x, y, width, height, source->getColorbuffer()->getHeight()); + sourceRect.left = clamp(sourceRect.left, 0, source->getColorbuffer()->getWidth()); + sourceRect.top = clamp(sourceRect.top, 0, source->getColorbuffer()->getHeight()); + sourceRect.right = clamp(sourceRect.right, 0, source->getColorbuffer()->getWidth()); + sourceRect.bottom = clamp(sourceRect.bottom, 0, source->getColorbuffer()->getHeight()); + + GLint destYOffset = transformPixelYOffset(yoffset, height, mImageArray[level].getHeight()); - IDirect3DSurface9 *dest; - HRESULT hr = mTexture->GetSurfaceLevel(level, &dest); + IDirect3DSurface9 *dest = mTexStorage->getSurfaceLevel(level); - getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0].format, xoffset, yoffset, dest); - dest->Release(); + if (dest) + { + getBlitter()->copy(renderTarget, sourceRect, mImageArray[0].getFormat(), xoffset, destYOffset, dest); + dest->Release(); + } } } + + renderTarget->Release(); } -// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81. -bool Texture2D::isComplete() const +void Texture2D::storage(GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) { - GLsizei width = mImageArray[0].width; - GLsizei height = mImageArray[0].height; + GLenum format = gl::ExtractFormat(internalformat); + GLenum type = gl::ExtractType(internalformat); + D3DFORMAT d3dfmt = ConvertTextureFormatType(format, type); + const bool renderTarget = IsTextureFormatRenderable(d3dfmt) && (mUsage == GL_FRAMEBUFFER_ATTACHMENT_ANGLE); + + delete mTexStorage; + mTexStorage = new TextureStorage2D(levels, d3dfmt, width, height, renderTarget); + mImmutable = true; + + for (int level = 0; level < levels; level++) + { + mImageArray[level].redefine(format, width, height, type, true); + width = std::max(1, width >> 1); + height = std::max(1, height >> 1); + } + + for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) + { + mImageArray[level].redefine(GL_NONE, 0, 0, GL_UNSIGNED_BYTE, true); + } + + if (mTexStorage->isManaged()) + { + int levels = levelCount(); + + for (int level = 0; level < levels; level++) + { + IDirect3DSurface9 *surface = mTexStorage->getSurfaceLevel(level); + mImageArray[level].setManagedSurface(surface); + } + } +} + +// Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85. +bool Texture2D::isSamplerComplete() const +{ + GLsizei width = mImageArray[0].getWidth(); + GLsizei height = mImageArray[0].getHeight(); if (width <= 0 || height <= 0) { @@ -1449,11 +2071,11 @@ bool Texture2D::isComplete() const case GL_LINEAR_MIPMAP_LINEAR: mipmapping = true; break; - default: UNREACHABLE(); + default: UNREACHABLE(); } - if ((getFormat() == GL_FLOAT && !getContext()->supportsFloatLinearFilter()) || - (getFormat() == GL_HALF_FLOAT_OES && !getContext()->supportsHalfFloatLinearFilter())) + if ((getInternalFormat() == GL_FLOAT && !getContext()->supportsFloat32LinearFilter()) || + (getInternalFormat() == GL_HALF_FLOAT_OES && !getContext()->supportsFloat16LinearFilter())) { if (mMagFilter != GL_NEAREST || (mMinFilter != GL_NEAREST && mMinFilter != GL_NEAREST_MIPMAP_NEAREST)) { @@ -1461,38 +2083,74 @@ bool Texture2D::isComplete() const } } + bool npotSupport = getContext()->supportsNonPower2Texture(); - if ((getWrapS() != GL_CLAMP_TO_EDGE && !isPow2(width)) - || (getWrapT() != GL_CLAMP_TO_EDGE && !isPow2(height))) + if (!npotSupport) { - return false; + if ((getWrapS() != GL_CLAMP_TO_EDGE && !isPow2(width)) || + (getWrapT() != GL_CLAMP_TO_EDGE && !isPow2(height))) + { + return false; + } } if (mipmapping) { - if (!isPow2(width) || !isPow2(height)) + if (!npotSupport) + { + if (!isPow2(width) || !isPow2(height)) + { + return false; + } + } + + if (!isMipmapComplete()) { return false; } + } - int q = log2(std::max(width, height)); + return true; +} - for (int level = 1; level <= q; level++) +// Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81. +bool Texture2D::isMipmapComplete() const +{ + if (isImmutable()) + { + return true; + } + + GLsizei width = mImageArray[0].getWidth(); + GLsizei height = mImageArray[0].getHeight(); + + if (width <= 0 || height <= 0) + { + return false; + } + + int q = log2(std::max(width, height)); + + for (int level = 1; level <= q; level++) + { + if (mImageArray[level].getFormat() != mImageArray[0].getFormat()) { - if (mImageArray[level].format != mImageArray[0].format) - { - return false; - } + return false; + } - if (mImageArray[level].width != std::max(1, width >> level)) - { - return false; - } + if (mImageArray[level].getType() != mImageArray[0].getType()) + { + return false; + } - if (mImageArray[level].height != std::max(1, height >> level)) - { - return false; - } + if (mImageArray[level].getWidth() != std::max(1, width >> level)) + { + return false; + } + + if (mImageArray[level].getHeight() != std::max(1, height >> level)) + { + return false; } } @@ -1501,186 +2159,122 @@ bool Texture2D::isComplete() const bool Texture2D::isCompressed() const { - return IsCompressed(getFormat()); + return IsCompressed(getInternalFormat()); } -// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one -IDirect3DBaseTexture9 *Texture2D::createTexture() +IDirect3DBaseTexture9 *Texture2D::getBaseTexture() const { - IDirect3DTexture9 *texture; - - IDirect3DDevice9 *device = getDevice(); - D3DFORMAT format = selectFormat(mImageArray[0].format, mType); + return mTexStorage ? mTexStorage->getBaseTexture() : NULL; +} - HRESULT result = device->CreateTexture(mWidth, mHeight, creationLevels(mWidth, mHeight, 0), 0, format, D3DPOOL_DEFAULT, &texture, NULL); +// Constructs a Direct3D 9 texture resource from the texture images +void Texture2D::createTexture() +{ + GLsizei width = mImageArray[0].getWidth(); + GLsizei height = mImageArray[0].getHeight(); + GLint levels = creationLevels(width, height); + D3DFORMAT format = mImageArray[0].getD3DFormat(); + const bool renderTarget = IsTextureFormatRenderable(format) && (mUsage == GL_FRAMEBUFFER_ATTACHMENT_ANGLE); - if (FAILED(result)) + delete mTexStorage; + mTexStorage = new TextureStorage2D(levels, format, width, height, renderTarget); + + if (mTexStorage->isManaged()) { - ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); - return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL); + int levels = levelCount(); + + for (int level = 0; level < levels; level++) + { + IDirect3DSurface9 *surface = mTexStorage->getSurfaceLevel(level); + mImageArray[level].setManagedSurface(surface); + } } - if (mTexture) mTexture->Release(); - mTexture = texture; - return texture; + mDirtyImages = true; } void Texture2D::updateTexture() { - IDirect3DDevice9 *device = getDevice(); - int levels = levelCount(); for (int level = 0; level < levels; level++) { - if (mImageArray[level].dirty) - { - IDirect3DSurface9 *levelSurface = NULL; - HRESULT result = mTexture->GetSurfaceLevel(level, &levelSurface); - - ASSERT(SUCCEEDED(result)); - - if (SUCCEEDED(result)) - { - result = device->UpdateSurface(mImageArray[level].surface, NULL, levelSurface, NULL); - ASSERT(SUCCEEDED(result)); + Image *image = &mImageArray[level]; - levelSurface->Release(); - - mImageArray[level].dirty = false; - } + if (image->isDirty()) + { + commitRect(level, 0, 0, mImageArray[level].getWidth(), mImageArray[level].getHeight()); } } } -IDirect3DBaseTexture9 *Texture2D::convertToRenderTarget() +void Texture2D::convertToRenderTarget() { - IDirect3DTexture9 *texture = NULL; + TextureStorage2D *newTexStorage = NULL; - if (mWidth != 0 && mHeight != 0) + if (mImageArray[0].getWidth() != 0 && mImageArray[0].getHeight() != 0) { - egl::Display *display = getDisplay(); - IDirect3DDevice9 *device = getDevice(); - D3DFORMAT format = selectFormat(mImageArray[0].format, mType); - - HRESULT result = device->CreateTexture(mWidth, mHeight, creationLevels(mWidth, mHeight, 0), D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL); + GLsizei width = mImageArray[0].getWidth(); + GLsizei height = mImageArray[0].getHeight(); + GLint levels = creationLevels(width, height); + D3DFORMAT format = mImageArray[0].getD3DFormat(); - if (FAILED(result)) - { - ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); - return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL); - } + newTexStorage = new TextureStorage2D(levels, format, width, height, true); - if (mTexture != NULL) + if (mTexStorage != NULL) { int levels = levelCount(); for (int i = 0; i < levels; i++) { - IDirect3DSurface9 *source; - result = mTexture->GetSurfaceLevel(i, &source); - - if (FAILED(result)) - { - ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); - - texture->Release(); - - return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL); - } - - IDirect3DSurface9 *dest; - result = texture->GetSurfaceLevel(i, &dest); - - if (FAILED(result)) - { - ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); - - texture->Release(); - source->Release(); - - return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL); + IDirect3DSurface9 *source = mTexStorage->getSurfaceLevel(i); + IDirect3DSurface9 *dest = newTexStorage->getSurfaceLevel(i); + + if (!copyToRenderTarget(dest, source, mTexStorage->isManaged())) + { + delete newTexStorage; + source->Release(); + dest->Release(); + return error(GL_OUT_OF_MEMORY); } - display->endScene(); - result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE); - - if (FAILED(result)) - { - ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); - - texture->Release(); - source->Release(); - dest->Release(); - - return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL); - } - - source->Release(); - dest->Release(); + if (source) source->Release(); + if (dest) dest->Release(); } } } - if (mTexture != NULL) - { - mTexture->Release(); - } - - mTexture = texture; - return mTexture; -} - -bool Texture2D::dirtyImageData() const -{ - int q = log2(std::max(mWidth, mHeight)); + delete mTexStorage; + mTexStorage = newTexStorage; - for (int i = 0; i <= q; i++) - { - if (mImageArray[i].dirty) return true; - } - - return false; + mDirtyImages = true; } void Texture2D::generateMipmaps() { - if (!isPow2(mImageArray[0].width) || !isPow2(mImageArray[0].height)) + if (!getContext()->supportsNonPower2Texture()) { - return error(GL_INVALID_OPERATION); + if (!isPow2(mImageArray[0].getWidth()) || !isPow2(mImageArray[0].getHeight())) + { + return error(GL_INVALID_OPERATION); + } } // Purge array levels 1 through q and reset them to represent the generated mipmap levels. - unsigned int q = log2(std::max(mWidth, mHeight)); + unsigned int q = log2(std::max(mImageArray[0].getWidth(), mImageArray[0].getHeight())); for (unsigned int i = 1; i <= q; i++) { - if (mImageArray[i].surface != NULL) - { - mImageArray[i].surface->Release(); - mImageArray[i].surface = NULL; - } - - mImageArray[i].dirty = false; - - mImageArray[i].format = mImageArray[0].format; - mImageArray[i].width = std::max(mImageArray[0].width >> i, 1); - mImageArray[i].height = std::max(mImageArray[0].height >> i, 1); + redefineImage(i, mImageArray[0].getFormat(), + std::max(mImageArray[0].getWidth() >> i, 1), + std::max(mImageArray[0].getHeight() >> i, 1), + mImageArray[0].getType()); } - if (isRenderable()) + if (mTexStorage && mTexStorage->isRenderTarget()) { - if (mTexture == NULL) - { - ERR(" failed because mTexture was null."); - return; - } - for (unsigned int i = 1; i <= q; i++) { - IDirect3DSurface9 *upper = NULL; - IDirect3DSurface9 *lower = NULL; - - mTexture->GetSurfaceLevel(i-1, &upper); - mTexture->GetSurfaceLevel(i, &lower); + IDirect3DSurface9 *upper = mTexStorage->getSurfaceLevel(i - 1); + IDirect3DSurface9 *lower = mTexStorage->getSurfaceLevel(i); if (upper != NULL && lower != NULL) { @@ -1689,78 +2283,169 @@ void Texture2D::generateMipmaps() if (upper != NULL) upper->Release(); if (lower != NULL) lower->Release(); + + mImageArray[i].markClean(); } } else { for (unsigned int i = 1; i <= q; i++) { - createSurface(mImageArray[i].width, mImageArray[i].height, mImageArray[i].format, mType, &mImageArray[i]); - if (mImageArray[i].surface == NULL) + if (mImageArray[i].getSurface() == NULL) { return error(GL_OUT_OF_MEMORY); } - if (FAILED(D3DXLoadSurfaceFromSurface(mImageArray[i].surface, NULL, NULL, mImageArray[i - 1].surface, NULL, NULL, D3DX_FILTER_BOX, 0))) + if (FAILED(D3DXLoadSurfaceFromSurface(mImageArray[i].getSurface(), NULL, NULL, mImageArray[i - 1].getSurface(), NULL, NULL, D3DX_FILTER_BOX, 0))) { ERR(" failed to load filter %d to %d.", i - 1, i); } - mImageArray[i].dirty = true; + mImageArray[i].markDirty(); } - - mDirtyMetaData = true; } } -Renderbuffer *Texture2D::getColorbuffer(GLenum target) +Renderbuffer *Texture2D::getRenderbuffer(GLenum target) { if (target != GL_TEXTURE_2D) { return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL); } - if (mColorbufferProxy.get() == NULL) + if (mColorbufferProxy == NULL) { - mColorbufferProxy.set(new Renderbuffer(id(), new TextureColorbufferProxy(this, target))); + mColorbufferProxy = new Renderbuffer(id(), new RenderbufferTexture(this, target)); } - return mColorbufferProxy.get(); + return mColorbufferProxy; } IDirect3DSurface9 *Texture2D::getRenderTarget(GLenum target) { ASSERT(target == GL_TEXTURE_2D); - needRenderTarget(); - - if (mTexture == NULL) + // ensure the underlying texture is created + if (getStorage(true) == NULL) { return NULL; } + + updateTexture(); - IDirect3DSurface9 *renderTarget = NULL; - mTexture->GetSurfaceLevel(0, &renderTarget); + return mTexStorage->getSurfaceLevel(0); +} - return renderTarget; +TextureStorage *Texture2D::getStorage(bool renderTarget) +{ + if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget())) + { + if (renderTarget) + { + convertToRenderTarget(); + } + else + { + createTexture(); + } + } + + return mTexStorage; } -TextureCubeMap::TextureCubeMap(GLuint id) : Texture(id) +TextureStorageCubeMap::TextureStorageCubeMap(int levels, D3DFORMAT format, int size, bool renderTarget) + : TextureStorage(renderTarget), mFirstRenderTargetSerial(RenderbufferStorage::issueCubeSerials()) { + IDirect3DDevice9 *device = getDevice(); + mTexture = NULL; + HRESULT result = device->CreateCubeTexture(size, levels, isRenderTarget() ? D3DUSAGE_RENDERTARGET : 0, format, getPool(), &mTexture, NULL); + + if (FAILED(result)) + { + ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); + error(GL_OUT_OF_MEMORY); + } +} + +TextureStorageCubeMap::~TextureStorageCubeMap() +{ + if (mTexture) + { + mTexture->Release(); + } +} + +IDirect3DSurface9 *TextureStorageCubeMap::getCubeMapSurface(GLenum faceTarget, int level) +{ + IDirect3DSurface9 *surface = NULL; + + if (mTexture) + { + HRESULT result = mTexture->GetCubeMapSurface(es2dx::ConvertCubeFace(faceTarget), level, &surface); + ASSERT(SUCCEEDED(result)); + } + + return surface; +} + +IDirect3DBaseTexture9 *TextureStorageCubeMap::getBaseTexture() const +{ + return mTexture; +} + +unsigned int TextureStorageCubeMap::getRenderTargetSerial(GLenum target) const +{ + return mFirstRenderTargetSerial + TextureCubeMap::faceIndex(target); +} + +TextureCubeMap::TextureCubeMap(GLuint id) : Texture(id) +{ + mTexStorage = NULL; + for (int i = 0; i < 6; i++) + { + mFaceProxies[i] = NULL; + mFaceProxyRefs[i] = 0; + } } TextureCubeMap::~TextureCubeMap() { for (int i = 0; i < 6; i++) { - mFaceProxies[i].set(NULL); + mFaceProxies[i] = NULL; } - if (mTexture) + delete mTexStorage; + mTexStorage = NULL; +} + +// We need to maintain a count of references to renderbuffers acting as +// proxies for this texture, so that the texture is not deleted while +// proxy references still exist. If the reference count drops to zero, +// we set our proxy pointer NULL, so that a new attempt at referencing +// will cause recreation. +void TextureCubeMap::addProxyRef(const Renderbuffer *proxy) +{ + for (int i = 0; i < 6; i++) { - mTexture->Release(); - mTexture = NULL; + if (mFaceProxies[i] == proxy) + mFaceProxyRefs[i]++; + } +} + +void TextureCubeMap::releaseProxy(const Renderbuffer *proxy) +{ + for (int i = 0; i < 6; i++) + { + if (mFaceProxies[i] == proxy) + { + if (mFaceProxyRefs[i] > 0) + mFaceProxyRefs[i]--; + + if (mFaceProxyRefs[i] == 0) + mFaceProxies[i] = NULL; + } } } @@ -1769,79 +2454,90 @@ GLenum TextureCubeMap::getTarget() const return GL_TEXTURE_CUBE_MAP; } -GLenum TextureCubeMap::getFormat() const +GLsizei TextureCubeMap::getWidth(GLint level) const +{ + if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) + return mImageArray[0][level].getWidth(); + else + return 0; +} + +GLsizei TextureCubeMap::getHeight(GLint level) const +{ + if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS) + return mImageArray[0][level].getHeight(); + else + return 0; +} + +GLenum TextureCubeMap::getInternalFormat() const { - return mImageArray[0][0].format; + return mImageArray[0][0].getFormat(); } -void TextureCubeMap::setImagePosX(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) +GLenum TextureCubeMap::getType() const { - setImage(0, level, internalFormat, width, height, format, type, unpackAlignment, pixels); + return mImageArray[0][0].getType(); } -void TextureCubeMap::setImageNegX(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) +D3DFORMAT TextureCubeMap::getD3DFormat() const { - setImage(1, level, internalFormat, width, height, format, type, unpackAlignment, pixels); + return mImageArray[0][0].getD3DFormat(); } -void TextureCubeMap::setImagePosY(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) +void TextureCubeMap::setImagePosX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) { - setImage(2, level, internalFormat, width, height, format, type, unpackAlignment, pixels); + setImage(0, level, width, height, format, type, unpackAlignment, pixels); } -void TextureCubeMap::setImageNegY(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) +void TextureCubeMap::setImageNegX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) { - setImage(3, level, internalFormat, width, height, format, type, unpackAlignment, pixels); + setImage(1, level, width, height, format, type, unpackAlignment, pixels); } -void TextureCubeMap::setImagePosZ(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) +void TextureCubeMap::setImagePosY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) { - setImage(4, level, internalFormat, width, height, format, type, unpackAlignment, pixels); + setImage(2, level, width, height, format, type, unpackAlignment, pixels); } -void TextureCubeMap::setImageNegZ(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) +void TextureCubeMap::setImageNegY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) { - setImage(5, level, internalFormat, width, height, format, type, unpackAlignment, pixels); + setImage(3, level, width, height, format, type, unpackAlignment, pixels); } -void TextureCubeMap::setCompressedImage(GLenum face, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels) +void TextureCubeMap::setImagePosZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) { - redefineTexture(level, internalFormat, width); + setImage(4, level, width, height, format, type, unpackAlignment, pixels); +} - Texture::setCompressedImage(width, height, internalFormat, imageSize, pixels, &mImageArray[faceIndex(face)][level]); +void TextureCubeMap::setImageNegZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) +{ + setImage(5, level, width, height, format, type, unpackAlignment, pixels); } -void TextureCubeMap::commitRect(GLenum faceTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height) +void TextureCubeMap::setCompressedImage(GLenum face, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels) { - int face = faceIndex(faceTarget); + redefineImage(faceIndex(face), level, format, width, height, GL_UNSIGNED_BYTE); + + Texture::setCompressedImage(imageSize, pixels, &mImageArray[faceIndex(face)][level]); +} - ASSERT(mImageArray[face][level].surface != NULL); +void TextureCubeMap::commitRect(int face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height) +{ + ASSERT(mImageArray[face][level].getSurface() != NULL); if (level < levelCount()) { - IDirect3DSurface9 *destLevel = getCubeMapSurface(face, level); + IDirect3DSurface9 *destLevel = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level); ASSERT(destLevel != NULL); if (destLevel != NULL) { - Image *img = &mImageArray[face][level]; - - RECT sourceRect; - sourceRect.left = xoffset; - sourceRect.top = yoffset; - sourceRect.right = xoffset + width; - sourceRect.bottom = yoffset + height; - - POINT destPoint; - destPoint.x = xoffset; - destPoint.y = yoffset; - - HRESULT result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint); - ASSERT(SUCCEEDED(result)); + Image *image = &mImageArray[face][level]; + image->updateSurface(destLevel, xoffset, yoffset, width, height); destLevel->Release(); - - img->dirty = false; + image->markClean(); } } } @@ -1850,7 +2546,7 @@ void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint y { if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(target)][level])) { - commitRect(target, level, xoffset, yoffset, width, height); + commitRect(faceIndex(target), level, xoffset, yoffset, width, height); } } @@ -1858,19 +2554,14 @@ void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffse { if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[faceIndex(target)][level])) { - commitRect(target, level, xoffset, yoffset, width, height); + commitRect(faceIndex(target), level, xoffset, yoffset, width, height); } } -// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81. -bool TextureCubeMap::isComplete() const +// Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86. +bool TextureCubeMap::isSamplerComplete() const { - int size = mImageArray[0][0].width; - - if (size <= 0) - { - return false; - } + int size = mImageArray[0][0].getWidth(); bool mipmapping; @@ -1886,50 +2577,101 @@ bool TextureCubeMap::isComplete() const case GL_LINEAR_MIPMAP_LINEAR: mipmapping = true; break; - default: UNREACHABLE(); + default: + UNREACHABLE(); + return false; } - for (int face = 0; face < 6; face++) + if ((getInternalFormat() == GL_FLOAT && !getContext()->supportsFloat32LinearFilter()) || + (getInternalFormat() == GL_HALF_FLOAT_OES && !getContext()->supportsFloat16LinearFilter())) { - if (mImageArray[face][0].width != size || mImageArray[face][0].height != size) + if (mMagFilter != GL_NEAREST || (mMinFilter != GL_NEAREST && mMinFilter != GL_NEAREST_MIPMAP_NEAREST)) { return false; } } - if ((getFormat() == GL_FLOAT && !getContext()->supportsFloatLinearFilter()) || - (getFormat() == GL_HALF_FLOAT_OES && !getContext()->supportsHalfFloatLinearFilter())) + if (!isPow2(size) && !getContext()->supportsNonPower2Texture()) { - if (mMagFilter != GL_NEAREST || (mMinFilter != GL_NEAREST && mMinFilter != GL_NEAREST_MIPMAP_NEAREST)) + if (getWrapS() != GL_CLAMP_TO_EDGE || getWrapT() != GL_CLAMP_TO_EDGE || mipmapping) { return false; } } - if (mipmapping) + if (!mipmapping) + { + if (!isCubeComplete()) + { + return false; + } + } + else { - if (!isPow2(size) && (getWrapS() != GL_CLAMP_TO_EDGE || getWrapT() != GL_CLAMP_TO_EDGE)) + if (!isMipmapCubeComplete()) // Also tests for isCubeComplete() { return false; } + } - int q = log2(size); + return true; +} - for (int face = 0; face < 6; face++) +// Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81. +bool TextureCubeMap::isCubeComplete() const +{ + if (mImageArray[0][0].getWidth() <= 0 || mImageArray[0][0].getHeight() != mImageArray[0][0].getWidth()) + { + return false; + } + + for (unsigned int face = 1; face < 6; face++) + { + if (mImageArray[face][0].getWidth() != mImageArray[0][0].getWidth() || + mImageArray[face][0].getWidth() != mImageArray[0][0].getHeight() || + mImageArray[face][0].getFormat() != mImageArray[0][0].getFormat() || + mImageArray[face][0].getType() != mImageArray[0][0].getType()) { - for (int level = 1; level <= q; level++) + return false; + } + } + + return true; +} + +bool TextureCubeMap::isMipmapCubeComplete() const +{ + if (isImmutable()) + { + return true; + } + + if (!isCubeComplete()) + { + return false; + } + + GLsizei size = mImageArray[0][0].getWidth(); + + int q = log2(size); + + for (int face = 0; face < 6; face++) + { + for (int level = 1; level <= q; level++) + { + if (mImageArray[face][level].getFormat() != mImageArray[0][0].getFormat()) { - if (mImageArray[face][level].format != mImageArray[0][0].format) - { - return false; - } + return false; + } - if (mImageArray[face][level].width != std::max(1, size >> level)) - { - return false; - } + if (mImageArray[face][level].getType() != mImageArray[0][0].getType()) + { + return false; + } - ASSERT(mImageArray[face][level].height == mImageArray[face][level].width); + if (mImageArray[face][level].getWidth() != std::max(1, size >> level)) + { + return false; } } } @@ -1939,143 +2681,107 @@ bool TextureCubeMap::isComplete() const bool TextureCubeMap::isCompressed() const { - return IsCompressed(getFormat()); + return IsCompressed(getInternalFormat()); } -// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one -IDirect3DBaseTexture9 *TextureCubeMap::createTexture() +IDirect3DBaseTexture9 *TextureCubeMap::getBaseTexture() const { - IDirect3DDevice9 *device = getDevice(); - D3DFORMAT format = selectFormat(mImageArray[0][0].format, mType); + return mTexStorage ? mTexStorage->getBaseTexture() : NULL; +} - IDirect3DCubeTexture9 *texture; +// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one +void TextureCubeMap::createTexture() +{ + GLsizei size = mImageArray[0][0].getWidth(); + GLint levels = creationLevels(size, 0); + D3DFORMAT format = mImageArray[0][0].getD3DFormat(); + const bool renderTarget = IsTextureFormatRenderable(format) && (mUsage == GL_FRAMEBUFFER_ATTACHMENT_ANGLE); - HRESULT result = device->CreateCubeTexture(mWidth, creationLevels(mWidth, 0), 0, format, D3DPOOL_DEFAULT, &texture, NULL); + delete mTexStorage; + mTexStorage = new TextureStorageCubeMap(levels, format, size, renderTarget); - if (FAILED(result)) + if (mTexStorage->isManaged()) { - ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); - return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL); - } + int levels = levelCount(); - if (mTexture) mTexture->Release(); + for (int face = 0; face < 6; face++) + { + for (int level = 0; level < levels; level++) + { + IDirect3DSurface9 *surface = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level); + mImageArray[face][level].setManagedSurface(surface); + } + } + } - mTexture = texture; - return mTexture; + mDirtyImages = true; } void TextureCubeMap::updateTexture() { - IDirect3DDevice9 *device = getDevice(); - for (int face = 0; face < 6; face++) { int levels = levelCount(); for (int level = 0; level < levels; level++) { - Image *img = &mImageArray[face][level]; + Image *image = &mImageArray[face][level]; - if (img->dirty) + if (image->isDirty()) { - IDirect3DSurface9 *levelSurface = getCubeMapSurface(face, level); - ASSERT(levelSurface != NULL); - - if (levelSurface != NULL) - { - HRESULT result = device->UpdateSurface(img->surface, NULL, levelSurface, NULL); - ASSERT(SUCCEEDED(result)); - - levelSurface->Release(); - - img->dirty = false; - } + commitRect(face, level, 0, 0, image->getWidth(), image->getHeight()); } } } } -IDirect3DBaseTexture9 *TextureCubeMap::convertToRenderTarget() +void TextureCubeMap::convertToRenderTarget() { - IDirect3DCubeTexture9 *texture = NULL; + TextureStorageCubeMap *newTexStorage = NULL; - if (mWidth != 0) + if (mImageArray[0][0].getWidth() != 0) { - egl::Display *display = getDisplay(); - IDirect3DDevice9 *device = getDevice(); - D3DFORMAT format = selectFormat(mImageArray[0][0].format, mType); - - HRESULT result = device->CreateCubeTexture(mWidth, creationLevels(mWidth, 0), D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL); + GLsizei size = mImageArray[0][0].getWidth(); + GLint levels = creationLevels(size, 0); + D3DFORMAT format = mImageArray[0][0].getD3DFormat(); - if (FAILED(result)) - { - ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); - return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL); - } + newTexStorage = new TextureStorageCubeMap(levels, format, size, true); - if (mTexture != NULL) + if (mTexStorage != NULL) { int levels = levelCount(); for (int f = 0; f < 6; f++) { for (int i = 0; i < levels; i++) { - IDirect3DSurface9 *source; - result = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &source); - - if (FAILED(result)) - { - ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); - - texture->Release(); - - return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL); - } - - IDirect3DSurface9 *dest; - result = texture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &dest); + IDirect3DSurface9 *source = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i); + IDirect3DSurface9 *dest = newTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i); - if (FAILED(result)) + if (!copyToRenderTarget(dest, source, mTexStorage->isManaged())) { - ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); - - texture->Release(); - source->Release(); - - return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL); + delete newTexStorage; + source->Release(); + dest->Release(); + return error(GL_OUT_OF_MEMORY); } - display->endScene(); - result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE); - - if (FAILED(result)) - { - ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); - - texture->Release(); - source->Release(); - dest->Release(); - - return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL); - } + if (source) source->Release(); + if (dest) dest->Release(); } } } } - if (mTexture != NULL) - { - mTexture->Release(); - } + delete mTexStorage; + mTexStorage = newTexStorage; - mTexture = texture; - return mTexture; + mDirtyImages = true; } -void TextureCubeMap::setImage(int face, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) +void TextureCubeMap::setImage(int faceIndex, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels) { - redefineTexture(level, internalFormat, width); + redefineImage(faceIndex, level, format, width, height, type); - Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[face][level]); + Texture::setImage(unpackAlignment, pixels, &mImageArray[faceIndex][level]); } unsigned int TextureCubeMap::faceIndex(GLenum face) @@ -2089,74 +2795,28 @@ unsigned int TextureCubeMap::faceIndex(GLenum face) return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X; } -bool TextureCubeMap::dirtyImageData() const +void TextureCubeMap::redefineImage(int face, GLint level, GLenum format, GLsizei width, GLsizei height, GLenum type) { - int q = log2(mWidth); + bool redefined = mImageArray[face][level].redefine(format, width, height, type, false); - for (int f = 0; f < 6; f++) + if (mTexStorage && redefined) { - for (int i = 0; i <= q; i++) - { - if (mImageArray[f][i].dirty) return true; - } - } - - return false; -} - -// While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture -// for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels & faces. -// Call this when a particular level of the texture must be defined with a specific format, width and height. -// -// Returns true if the existing texture was unsuitable had to be destroyed. If so, it will also set -// a new size for the texture by working backwards from the given size. -bool TextureCubeMap::redefineTexture(GLint level, GLenum internalFormat, GLsizei width) -{ - // Are these settings compatible with level 0? - bool sizeOkay = (mImageArray[0][0].width >> level == width); - - bool textureOkay = (sizeOkay && internalFormat == mImageArray[0][0].format); - - if (!textureOkay) - { - TRACE("Redefining cube texture (%d, 0x%04X, %d => 0x%04X, %d).", level, - mImageArray[0][0].format, mImageArray[0][0].width, - internalFormat, width); - - // Purge all the levels and the texture. for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) { for (int f = 0; f < 6; f++) { - if (mImageArray[f][i].surface != NULL) - { - mImageArray[f][i].dirty = false; - - mImageArray[f][i].surface->Release(); - mImageArray[f][i].surface = NULL; - } + mImageArray[f][i].markDirty(); } } - if (mTexture != NULL) - { - mTexture->Release(); - mTexture = NULL; - dropTexture(); - } - - mWidth = width << level; - mImageArray[0][0].width = width << level; - mHeight = width << level; - mImageArray[0][0].height = width << level; + delete mTexStorage; + mTexStorage = NULL; - mImageArray[0][0].format = internalFormat; + mDirtyImages = true; } - - return !textureOkay; } -void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source) +void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) { IDirect3DSurface9 *renderTarget = source->getRenderTarget(); @@ -2167,80 +2827,50 @@ void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum internalFormat } unsigned int faceindex = faceIndex(target); - bool redefined = redefineTexture(level, internalFormat, width); + redefineImage(faceindex, level, format, width, height, GL_UNSIGNED_BYTE); - if (!isRenderableFormat()) + if (!mImageArray[faceindex][level].isRenderableFormat()) { - copyNonRenderable(&mImageArray[faceindex][level], internalFormat, 0, 0, x, y, width, height, renderTarget); + mImageArray[faceindex][level].copy(0, 0, x, y, width, height, renderTarget); + mDirtyImages = true; } else { - if (redefined) + if (!mTexStorage || !mTexStorage->isRenderTarget()) { convertToRenderTarget(); - pushTexture(mTexture, true); - } - else - { - needRenderTarget(); } + + mImageArray[faceindex][level].markClean(); ASSERT(width == height); if (width > 0 && level < levelCount()) { - RECT sourceRect; - sourceRect.left = x; - sourceRect.right = x + width; - sourceRect.top = y; - sourceRect.bottom = y + height; + RECT sourceRect = transformPixelRect(x, y, width, height, source->getColorbuffer()->getHeight()); + sourceRect.left = clamp(sourceRect.left, 0, source->getColorbuffer()->getWidth()); + sourceRect.top = clamp(sourceRect.top, 0, source->getColorbuffer()->getHeight()); + sourceRect.right = clamp(sourceRect.right, 0, source->getColorbuffer()->getWidth()); + sourceRect.bottom = clamp(sourceRect.bottom, 0, source->getColorbuffer()->getHeight()); - IDirect3DSurface9 *dest = getCubeMapSurface(target, level); + GLint destYOffset = transformPixelYOffset(0, height, mImageArray[faceindex][level].getWidth()); - getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest); - dest->Release(); - } - } - - mImageArray[faceindex][level].width = width; - mImageArray[faceindex][level].height = height; - mImageArray[faceindex][level].format = internalFormat; -} - -IDirect3DSurface9 *TextureCubeMap::getCubeMapSurface(unsigned int faceIdentifier, unsigned int level) -{ - unsigned int faceIndex; - - if (faceIdentifier < 6) - { - faceIndex = faceIdentifier; - } - else if (faceIdentifier >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && faceIdentifier <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) - { - faceIndex = faceIdentifier - GL_TEXTURE_CUBE_MAP_POSITIVE_X; - } - else - { - UNREACHABLE(); - faceIndex = 0; - } + IDirect3DSurface9 *dest = mTexStorage->getCubeMapSurface(target, level); - if (mTexture == NULL) - { - UNREACHABLE(); - return NULL; + if (dest) + { + getBlitter()->copy(renderTarget, sourceRect, format, 0, destYOffset, dest); + dest->Release(); + } + } } - IDirect3DSurface9 *surface = NULL; - - HRESULT hr = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex), level, &surface); - - return (SUCCEEDED(hr)) ? surface : NULL; + renderTarget->Release(); } -void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source) +void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source) { - GLsizei size = mImageArray[faceIndex(target)][level].width; + GLsizei size = mImageArray[faceIndex(target)][level].getWidth(); if (xoffset + width > size || yoffset + height > size) { @@ -2256,99 +2886,123 @@ void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLi } unsigned int faceindex = faceIndex(target); - bool redefined = redefineTexture(0, mImageArray[0][0].format, mImageArray[0][0].width); - - if (!isRenderableFormat()) + + if (!mImageArray[faceindex][level].isRenderableFormat() || (!mTexStorage && !isSamplerComplete())) { - copyNonRenderable(&mImageArray[faceindex][level], getFormat(), 0, 0, x, y, width, height, renderTarget); + mImageArray[faceindex][level].copy(0, 0, x, y, width, height, renderTarget); + mDirtyImages = true; } else { - if (redefined) + if (!mTexStorage || !mTexStorage->isRenderTarget()) { convertToRenderTarget(); - pushTexture(mTexture, true); - } - else - { - needRenderTarget(); } + + updateTexture(); if (level < levelCount()) { - RECT sourceRect; - sourceRect.left = x; - sourceRect.right = x + width; - sourceRect.top = y; - sourceRect.bottom = y + height; + RECT sourceRect = transformPixelRect(x, y, width, height, source->getColorbuffer()->getHeight()); + sourceRect.left = clamp(sourceRect.left, 0, source->getColorbuffer()->getWidth()); + sourceRect.top = clamp(sourceRect.top, 0, source->getColorbuffer()->getHeight()); + sourceRect.right = clamp(sourceRect.right, 0, source->getColorbuffer()->getWidth()); + sourceRect.bottom = clamp(sourceRect.bottom, 0, source->getColorbuffer()->getHeight()); + + GLint destYOffset = transformPixelYOffset(yoffset, height, mImageArray[faceindex][level].getWidth()); - IDirect3DSurface9 *dest = getCubeMapSurface(target, level); + IDirect3DSurface9 *dest = mTexStorage->getCubeMapSurface(target, level); - getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0][0].format, xoffset, yoffset, dest); - dest->Release(); + if (dest) + { + getBlitter()->copy(renderTarget, sourceRect, mImageArray[0][0].getFormat(), xoffset, destYOffset, dest); + dest->Release(); + } } } + + renderTarget->Release(); } -bool TextureCubeMap::isCubeComplete() const +void TextureCubeMap::storage(GLsizei levels, GLenum internalformat, GLsizei size) { - if (mImageArray[0][0].width == 0) + GLenum format = gl::ExtractFormat(internalformat); + GLenum type = gl::ExtractType(internalformat); + D3DFORMAT d3dfmt = ConvertTextureFormatType(format, type); + const bool renderTarget = IsTextureFormatRenderable(d3dfmt) && (mUsage == GL_FRAMEBUFFER_ATTACHMENT_ANGLE); + + delete mTexStorage; + mTexStorage = new TextureStorageCubeMap(levels, d3dfmt, size, renderTarget); + mImmutable = true; + + for (int level = 0; level < levels; level++) { - return false; + for (int face = 0; face < 6; face++) + { + mImageArray[face][level].redefine(format, size, size, type, true); + size = std::max(1, size >> 1); + } } - for (unsigned int f = 1; f < 6; f++) + for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++) { - if (mImageArray[f][0].width != mImageArray[0][0].width - || mImageArray[f][0].format != mImageArray[0][0].format) + for (int face = 0; face < 6; face++) { - return false; + mImageArray[face][level].redefine(GL_NONE, 0, 0, GL_UNSIGNED_BYTE, true); } } - return true; + if (mTexStorage->isManaged()) + { + int levels = levelCount(); + + for (int face = 0; face < 6; face++) + { + for (int level = 0; level < levels; level++) + { + IDirect3DSurface9 *surface = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level); + mImageArray[face][level].setManagedSurface(surface); + } + } + } } void TextureCubeMap::generateMipmaps() { - if (!isPow2(mImageArray[0][0].width) || !isCubeComplete()) + if (!isCubeComplete()) { return error(GL_INVALID_OPERATION); } - // Purge array levels 1 through q and reset them to represent the generated mipmap levels. - unsigned int q = log2(mImageArray[0][0].width); - for (unsigned int f = 0; f < 6; f++) + if (!getContext()->supportsNonPower2Texture()) { - for (unsigned int i = 1; i <= q; i++) + if (!isPow2(mImageArray[0][0].getWidth())) { - if (mImageArray[f][i].surface != NULL) - { - mImageArray[f][i].surface->Release(); - mImageArray[f][i].surface = NULL; - } - - mImageArray[f][i].dirty = false; - - mImageArray[f][i].format = mImageArray[f][0].format; - mImageArray[f][i].width = std::max(mImageArray[f][0].width >> i, 1); - mImageArray[f][i].height = mImageArray[f][i].width; + return error(GL_INVALID_OPERATION); } } - if (isRenderable()) + // Purge array levels 1 through q and reset them to represent the generated mipmap levels. + unsigned int q = log2(mImageArray[0][0].getWidth()); + for (unsigned int f = 0; f < 6; f++) { - if (mTexture == NULL) + for (unsigned int i = 1; i <= q; i++) { - return; + redefineImage(f, i, mImageArray[f][0].getFormat(), + std::max(mImageArray[f][0].getWidth() >> i, 1), + std::max(mImageArray[f][0].getWidth() >> i, 1), + mImageArray[f][0].getType()); } + } + if (mTexStorage && mTexStorage->isRenderTarget()) + { for (unsigned int f = 0; f < 6; f++) { for (unsigned int i = 1; i <= q; i++) { - IDirect3DSurface9 *upper = getCubeMapSurface(f, i-1); - IDirect3DSurface9 *lower = getCubeMapSurface(f, i); + IDirect3DSurface9 *upper = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i-1); + IDirect3DSurface9 *lower = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i); if (upper != NULL && lower != NULL) { @@ -2357,6 +3011,8 @@ void TextureCubeMap::generateMipmaps() if (upper != NULL) upper->Release(); if (lower != NULL) lower->Release(); + + mImageArray[f][i].markClean(); } } } @@ -2366,26 +3022,23 @@ void TextureCubeMap::generateMipmaps() { for (unsigned int i = 1; i <= q; i++) { - createSurface(mImageArray[f][i].width, mImageArray[f][i].height, mImageArray[f][i].format, mType, &mImageArray[f][i]); - if (mImageArray[f][i].surface == NULL) + if (mImageArray[f][i].getSurface() == NULL) { return error(GL_OUT_OF_MEMORY); } - if (FAILED(D3DXLoadSurfaceFromSurface(mImageArray[f][i].surface, NULL, NULL, mImageArray[f][i - 1].surface, NULL, NULL, D3DX_FILTER_BOX, 0))) + if (FAILED(D3DXLoadSurfaceFromSurface(mImageArray[f][i].getSurface(), NULL, NULL, mImageArray[f][i - 1].getSurface(), NULL, NULL, D3DX_FILTER_BOX, 0))) { ERR(" failed to load filter %d to %d.", i - 1, i); } - mImageArray[f][i].dirty = true; + mImageArray[f][i].markDirty(); } } - - mDirtyMetaData = true; } } -Renderbuffer *TextureCubeMap::getColorbuffer(GLenum target) +Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target) { if (!IsCubemapTextureTarget(target)) { @@ -2394,74 +3047,44 @@ Renderbuffer *TextureCubeMap::getColorbuffer(GLenum target) unsigned int face = faceIndex(target); - if (mFaceProxies[face].get() == NULL) + if (mFaceProxies[face] == NULL) { - mFaceProxies[face].set(new Renderbuffer(id(), new TextureColorbufferProxy(this, target))); + mFaceProxies[face] = new Renderbuffer(id(), new RenderbufferTexture(this, target)); } - return mFaceProxies[face].get(); + return mFaceProxies[face]; } IDirect3DSurface9 *TextureCubeMap::getRenderTarget(GLenum target) { ASSERT(IsCubemapTextureTarget(target)); - needRenderTarget(); - - if (mTexture == NULL) + // ensure the underlying texture is created + if (getStorage(true) == NULL) { return NULL; } - - IDirect3DSurface9 *renderTarget = NULL; - mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex(target)), 0, &renderTarget); - - return renderTarget; -} - -Texture::TextureColorbufferProxy::TextureColorbufferProxy(Texture *texture, GLenum target) - : Colorbuffer(texture), mTexture(texture), mTarget(target) -{ - ASSERT(IsTextureTarget(target)); -} - -void Texture::TextureColorbufferProxy::addRef() const -{ - mTexture->addRef(); -} - -void Texture::TextureColorbufferProxy::release() const -{ - mTexture->release(); -} - -IDirect3DSurface9 *Texture::TextureColorbufferProxy::getRenderTarget() -{ - if (mRenderTarget) mRenderTarget->Release(); - - mRenderTarget = mTexture->getRenderTarget(mTarget); - return mRenderTarget; -} - -int Texture::TextureColorbufferProxy::getWidth() const -{ - return mTexture->getWidth(); -} - -int Texture::TextureColorbufferProxy::getHeight() const -{ - return mTexture->getHeight(); + updateTexture(); + + return mTexStorage->getCubeMapSurface(target, 0); } -GLenum Texture::TextureColorbufferProxy::getFormat() const +TextureStorage *TextureCubeMap::getStorage(bool renderTarget) { - return mTexture->getFormat(); -} + if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget())) + { + if (renderTarget) + { + convertToRenderTarget(); + } + else + { + createTexture(); + } + } -bool Texture::TextureColorbufferProxy::isFloatingPoint() const -{ - return mTexture->isFloatingPoint(); + return mTexStorage; } -} +}
\ No newline at end of file |