Scale up non-multiple-of-4 dxt textures

D3D doesn't support DXT textures that aren't a multiple of 4 in size, so multiply the sizes by 2 or 4 so they work. Then ensure sampler parameters are set up correctly so they don't sample the unuploaded miplevels.

BUG=https://code.google.com/p/angleproject/issues/detail?id=237
TEST=

Review URL: https://codereview.appspot.com/6287045

git-svn-id: https://angleproject.googlecode.com/svn/trunk@1222 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/libGLESv2/Context.cpp b/src/libGLESv2/Context.cpp
index 1c2179e..f542399 100644
--- a/src/libGLESv2/Context.cpp
+++ b/src/libGLESv2/Context.cpp
@@ -2410,6 +2410,7 @@
                         es2dx::ConvertMinFilter(minFilter, &d3dMinFilter, &d3dMipFilter, maxAnisotropy);
                         mDevice->SetSamplerState(d3dSampler, D3DSAMP_MINFILTER, d3dMinFilter);
                         mDevice->SetSamplerState(d3dSampler, D3DSAMP_MIPFILTER, d3dMipFilter);
+                        mDevice->SetSamplerState(d3dSampler, D3DSAMP_MAXMIPLEVEL, texture->getLodOffset());
 
                         if (supportsTextureFilterAnisotropy())
                         {
diff --git a/src/libGLESv2/Texture.cpp b/src/libGLESv2/Texture.cpp
index cd4d637..9cb9c98 100644
--- a/src/libGLESv2/Texture.cpp
+++ b/src/libGLESv2/Texture.cpp
@@ -118,6 +118,26 @@
     return d3dusage;
 }
 
+static void MakeValidSize(bool isImage, bool isCompressed, GLsizei *requestWidth, GLsizei *requestHeight, int *levelOffset) {
+    int upsampleCount = 0;
+
+    if (isCompressed)
+    {
+        // Don't expand the size of full textures that are at least 4x4
+        // already.
+        if (isImage || *requestWidth < 4 || *requestHeight < 4)
+        {
+            while (*requestWidth % 4 != 0 || *requestHeight % 4 != 0)
+            {
+                *requestWidth <<= 1;
+                *requestHeight <<= 1;
+                upsampleCount++;
+            }
+        }
+    }
+    *levelOffset = upsampleCount;
+}
+
 Image::Image()
 {
     mWidth = 0; 
@@ -186,22 +206,7 @@
         int levelToFetch = 0;
         GLsizei requestWidth = mWidth;
         GLsizei requestHeight = mHeight;
-        if (IsCompressed(mFormat) && (mWidth % 4 != 0 || mHeight % 4 != 0))
-        {
-            bool isMult4 = false;
-            int upsampleCount = 0;
-            while (!isMult4)
-            {
-                requestWidth <<= 1;
-                requestHeight <<= 1;
-                upsampleCount++;
-                if (requestWidth % 4 == 0 && requestHeight % 4 == 0)
-                {
-                    isMult4 = true;
-                }
-            }
-            levelToFetch = upsampleCount;
-        }
+        MakeValidSize(true, IsCompressed(mFormat), &requestWidth, &requestHeight, &levelToFetch);
 
         HRESULT result = getDevice()->CreateTexture(requestWidth, requestHeight, levelToFetch + 1, NULL, d3dFormat,
                                                     poolToUse, &newTexture, NULL);
@@ -1111,7 +1116,8 @@
 TextureStorage::TextureStorage(DWORD usage)
     : mD3DUsage(usage),
       mD3DPool(getDisplay()->getTexturePool(usage)),
-      mTextureSerial(issueTextureSerial())
+      mTextureSerial(issueTextureSerial()),
+      mLodOffset(0)
 {
 }
 
@@ -1149,6 +1155,11 @@
     return mCurrentTextureSerial++;
 }
 
+int TextureStorage::getLodOffset() const
+{
+    return mLodOffset;
+}
+
 Texture::Texture(GLuint id) : RefCountObject(id)
 {
     mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
@@ -1405,6 +1416,12 @@
     return mImmutable;
 }
 
+int Texture::getLodOffset()
+{
+    TextureStorage *texture = getStorage(false);
+    return texture ? texture->getLodOffset() : 0;
+}
+
 GLint Texture::creationLevels(GLsizei width, GLsizei height) const
 {
     if ((isPow2(width) && isPow2(height)) || getContext()->supportsNonPower2Texture())
@@ -1477,7 +1494,8 @@
     if (width > 0 && height > 0)
     {
         IDirect3DDevice9 *device = getDevice();
-        HRESULT result = device->CreateTexture(width, height, levels, getUsage(), format, getPool(), &mTexture, NULL);
+        MakeValidSize(false, dx2es::IsCompressedD3DFormat(format), &width, &height, &mLodOffset);
+        HRESULT result = device->CreateTexture(width, height, levels + mLodOffset, getUsage(), format, getPool(), &mTexture, NULL);
 
         if (FAILED(result))
         {
@@ -1503,7 +1521,7 @@
 
     if (mTexture)
     {
-        HRESULT result = mTexture->GetSurfaceLevel(level, &surface);
+        HRESULT result = mTexture->GetSurfaceLevel(level + mLodOffset, &surface);
         ASSERT(SUCCEEDED(result));
     }
 
@@ -2197,7 +2215,9 @@
     if (size > 0)
     {
         IDirect3DDevice9 *device = getDevice();
-        HRESULT result = device->CreateCubeTexture(size, levels, getUsage(), format, getPool(), &mTexture, NULL);
+        int height = size;
+        MakeValidSize(false, dx2es::IsCompressedD3DFormat(format), &size, &height, &mLodOffset);
+        HRESULT result = device->CreateCubeTexture(size, levels + mLodOffset, getUsage(), format, getPool(), &mTexture, NULL);
 
         if (FAILED(result))
         {
@@ -2223,7 +2243,7 @@
 
     if (mTexture)
     {
-        HRESULT result = mTexture->GetCubeMapSurface(es2dx::ConvertCubeFace(faceTarget), level, &surface);
+        HRESULT result = mTexture->GetCubeMapSurface(es2dx::ConvertCubeFace(faceTarget), level + mLodOffset, &surface);
         ASSERT(SUCCEEDED(result));
     }
 
diff --git a/src/libGLESv2/Texture.h b/src/libGLESv2/Texture.h
index ccddf57..6d67df7 100644
--- a/src/libGLESv2/Texture.h
+++ b/src/libGLESv2/Texture.h
@@ -150,6 +150,10 @@
     DWORD getUsage() const;
     unsigned int getTextureSerial() const;
     virtual unsigned int getRenderTargetSerial(GLenum target) const = 0;
+    int getLodOffset() const;
+
+  protected:
+    int mLodOffset;
 
   private:
     DISALLOW_COPY_AND_ASSIGN(TextureStorage);
@@ -204,6 +208,7 @@
     unsigned int getRenderTargetSerial(GLenum target);
 
     bool isImmutable() const;
+    int getLodOffset();
 
     static const GLuint INCOMPLETE_TEXTURE_ID = static_cast<GLuint>(-1);   // Every texture takes an id at creation time. The value is arbitrary because it is never registered with the resource manager.
 
@@ -427,4 +432,4 @@
 };
 }
 
-#endif   // LIBGLESV2_TEXTURE_H_
\ No newline at end of file
+#endif   // LIBGLESV2_TEXTURE_H_
diff --git a/src/libGLESv2/utilities.cpp b/src/libGLESv2/utilities.cpp
index 5cf005d..e6f8993 100644
--- a/src/libGLESv2/utilities.cpp
+++ b/src/libGLESv2/utilities.cpp
@@ -965,6 +965,21 @@
     return (surfaceFormat == D3DFMT_INTZ);
 }
 
+bool IsCompressedD3DFormat(D3DFORMAT surfaceFormat)
+{
+    switch(surfaceFormat)
+    {
+      case D3DFMT_DXT1:
+      case D3DFMT_DXT2:
+      case D3DFMT_DXT3:
+      case D3DFMT_DXT4:
+      case D3DFMT_DXT5:
+        return true;
+      default:
+        return false;
+    }
+}
+
 GLsizei GetSamplesFromMultisampleType(D3DMULTISAMPLE_TYPE type)
 {
     if (type == D3DMULTISAMPLE_NONMASKABLE)
diff --git a/src/libGLESv2/utilities.h b/src/libGLESv2/utilities.h
index 5174680..3357eb0 100644
--- a/src/libGLESv2/utilities.h
+++ b/src/libGLESv2/utilities.h
@@ -84,6 +84,7 @@
 bool IsFloat16Format(D3DFORMAT surfaceFormat);
 bool IsDepthTextureFormat(D3DFORMAT surfaceFormat);
 bool IsStencilTextureFormat(D3DFORMAT surfaceFormat);
+bool IsCompressedD3DFormat(D3DFORMAT format);
 
 GLsizei GetSamplesFromMultisampleType(D3DMULTISAMPLE_TYPE type);