Improve D3D11 FL9_3 zero-LOD workaround (e.g. TextureCubes)

D3D11 Feature Level 9_3 can't disable mipmaps on a mipmapped
texture, and sample from level zero of it. A previous commit
added a workaround for this in ANGLE to Texture2Ds. This
commit fixes some minor issues in that commit, and extends
the workaround to apply to TextureCubes too.

Change-Id: Ic97321af6f8bbf7ad5d96e58655c342db3978a6a
Reviewed-on: https://chromium-review.googlesource.com/241944
Tested-by: Austin Kinross <aukinros@microsoft.com>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/renderer/d3d/RendererD3D.h b/src/libANGLE/renderer/d3d/RendererD3D.h
index 13548ee..d80b4d1 100644
--- a/src/libANGLE/renderer/d3d/RendererD3D.h
+++ b/src/libANGLE/renderer/d3d/RendererD3D.h
@@ -135,7 +135,7 @@
     virtual gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) = 0;
     virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain) = 0;
     virtual TextureStorage *createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly) = 0;
-    virtual TextureStorage *createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels) = 0;
+    virtual TextureStorage *createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly) = 0;
     virtual TextureStorage *createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels) = 0;
     virtual TextureStorage *createTextureStorage2DArray(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels) = 0;
 
diff --git a/src/libANGLE/renderer/d3d/TextureD3D.cpp b/src/libANGLE/renderer/d3d/TextureD3D.cpp
index 2173462..7b7c822 100644
--- a/src/libANGLE/renderer/d3d/TextureD3D.cpp
+++ b/src/libANGLE/renderer/d3d/TextureD3D.cpp
@@ -352,9 +352,6 @@
         return gl::Error(GL_NO_ERROR); // no-op
     }
 
-    // Set up proper mipmap chain in our Image array.
-    initMipmapsImages();
-
     if (mTexStorage && mRenderer->getWorkarounds().zeroMaxLodWorkaround)
     {
         // Switch to using the mipmapped texture.
@@ -365,6 +362,9 @@
         }
     }
 
+    // Set up proper mipmap chain in our Image array.
+    initMipmapsImages();
+
     // We know that all layers have the same dimension, for the texture to be complete
     GLint layerCount = static_cast<GLint>(getLayerCount(0));
 
@@ -728,7 +728,9 @@
     gl::ImageIndex index = gl::ImageIndex::Make2D(level);
     gl::Offset destOffset(0, 0, 0);
 
-    if (!canCreateRenderTargetForImage(index))
+    // If the zero max LOD workaround is active, then we can't sample from individual layers of the framebuffer in shaders,
+    // so we should use the non-rendering copy path.
+    if (!canCreateRenderTargetForImage(index) || mRenderer->getWorkarounds().zeroMaxLodWorkaround)
     {
         gl::Error error = mImageArray[level]->copy(destOffset, sourceArea, source);
         if (error.isError())
@@ -771,7 +773,9 @@
 
     gl::ImageIndex index = gl::ImageIndex::Make2D(level);
 
-    if (!canCreateRenderTargetForImage(index))
+    // If the zero max LOD workaround is active, then we can't sample from individual layers of the framebuffer in shaders,
+    // so we should use the non-rendering copy path.
+    if (!canCreateRenderTargetForImage(index) || mRenderer->getWorkarounds().zeroMaxLodWorkaround)
     {
         gl::Error error = mImageArray[level]->copy(destOffset, sourceArea, source);
         if (error.isError())
@@ -1030,11 +1034,9 @@
         // If any of the CPU images (levels >= 1) are dirty, then the textureStorage2D should use the mipped texture to begin with.
         // Otherwise, it should use the level-zero-only texture.
         hintLevelZeroOnly = true;
-        int level = 1;
-        while (hintLevelZeroOnly && level < levels)
+        for (int level = 1; level < levels && hintLevelZeroOnly; level++)
         {
             hintLevelZeroOnly = !(mImageArray[level]->isDirty() && isLevelComplete(level));
-            level += 1;
         }
     }
 
@@ -1381,7 +1383,8 @@
 
     // TODO(geofflang): Verify storage creation had no errors
     bool renderTarget = IsRenderTargetUsage(mUsage);
-    TextureStorage *storage = mRenderer->createTextureStorageCube(internalFormat, renderTarget, size.width, levels);
+
+    TextureStorage *storage = mRenderer->createTextureStorageCube(internalFormat, renderTarget, size.width, levels, false);
 
     gl::Error error = setCompleteTexStorage(storage);
     if (error.isError())
@@ -1524,8 +1527,23 @@
     // use existing storage level count, when previously specified by TexStorage*D
     GLint levels = (mTexStorage ? mTexStorage->getLevelCount() : creationLevels(size, size, 1));
 
+    bool hintLevelZeroOnly = false;
+    if (mRenderer->getWorkarounds().zeroMaxLodWorkaround)
+    {
+        // If any of the CPU images (levels >= 1) are dirty, then the textureStorage should use the mipped texture to begin with.
+        // Otherwise, it should use the level-zero-only texture.
+        hintLevelZeroOnly = true;
+        for (int faceIndex = 0; faceIndex < 6 && hintLevelZeroOnly; faceIndex++)
+        {
+            for (int level = 1; level < levels && hintLevelZeroOnly; level++)
+            {
+                hintLevelZeroOnly = !(mImageArray[faceIndex][level]->isDirty() && isFaceLevelComplete(faceIndex, level));
+            }
+        }
+    }
+
     // TODO (geofflang): detect if storage creation succeeded
-    *outTexStorage = mRenderer->createTextureStorageCube(getBaseLevelInternalFormat(), renderTarget, size, levels);
+    *outTexStorage = mRenderer->createTextureStorageCube(getBaseLevelInternalFormat(), renderTarget, size, levels, hintLevelZeroOnly);
 
     return gl::Error(GL_NO_ERROR);
 }
diff --git a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
index 87786a4..c472707 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
@@ -2829,9 +2829,9 @@
     return new TextureStorage11_2D(this, internalformat, renderTarget, width, height, levels, hintLevelZeroOnly);
 }
 
-TextureStorage *Renderer11::createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels)
+TextureStorage *Renderer11::createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly)
 {
-    return new TextureStorage11_Cube(this, internalformat, renderTarget, size, levels);
+    return new TextureStorage11_Cube(this, internalformat, renderTarget, size, levels, hintLevelZeroOnly);
 }
 
 TextureStorage *Renderer11::createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels)
diff --git a/src/libANGLE/renderer/d3d/d3d11/Renderer11.h b/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
index 4deda39..94e62ca 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
+++ b/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
@@ -155,7 +155,7 @@
     gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) override;
     virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain);
     virtual TextureStorage *createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly);
-    virtual TextureStorage *createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels);
+    virtual TextureStorage *createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly);
     virtual TextureStorage *createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels);
     virtual TextureStorage *createTextureStorage2DArray(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels);
 
diff --git a/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp b/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
index 7d1261c..58d756f 100644
--- a/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
@@ -426,8 +426,28 @@
     unsigned int srcSubresource = getSubresourceIndex(index);
 
     ID3D11DeviceContext *context = mRenderer->getDeviceContext();
+
+    // D3D11 can't perform partial CopySubresourceRegion on depth/stencil textures, so pSrcBox should be NULL.
+    D3D11_BOX srcBox;
+    D3D11_BOX *pSrcBox = NULL;
+    if (mRenderer->getFeatureLevel() <= D3D_FEATURE_LEVEL_9_3)
+    {
+        // However, D3D10Level9 doesn't always perform CopySubresourceRegion correctly unless the source box
+        // is specified. This is okay, since we don't perform CopySubresourceRegion on depth/stencil
+        // textures on 9_3.
+        ASSERT(d3d11::GetDXGIFormatInfo(mTextureFormat).depthBits == 0);
+        ASSERT(d3d11::GetDXGIFormatInfo(mTextureFormat).stencilBits == 0);
+        srcBox.left = region.x;
+        srcBox.right = region.x + region.width;
+        srcBox.top = region.y;
+        srcBox.bottom = region.y + region.height;
+        srcBox.front = region.z;
+        srcBox.back = region.z + region.depth;
+        pSrcBox = &srcBox;
+    }
+
     context->CopySubresourceRegion(dstTexture, dstSubresource, region.x, region.y, region.z,
-                                   srcTexture, srcSubresource, NULL);
+                                   srcTexture, srcSubresource, pSrcBox);
 
     return gl::Error(GL_NO_ERROR);
 }
@@ -662,7 +682,7 @@
     mTextureHeight = height;
     mTextureDepth = 1;
 
-    if (hintLevelZeroOnly)
+    if (hintLevelZeroOnly && levels > 1)
     {
         //The LevelZeroOnly hint should only be true if the zero max LOD workaround is active.
         ASSERT(mRenderer->getWorkarounds().zeroMaxLodWorkaround);
@@ -787,7 +807,7 @@
 
 gl::Error TextureStorage11_2D::useLevelZeroWorkaroundTexture(bool useLevelZeroTexture)
 {
-    if (useLevelZeroTexture)
+    if (useLevelZeroTexture && mMipLevels > 1)
     {
         if (!mUseLevelZeroTexture && mTexture)
         {
@@ -798,8 +818,8 @@
             }
 
             // Pull data back from the mipped texture if necessary.
-            ASSERT(mTexture);
-            ID3D11DeviceContext* context = mRenderer->getDeviceContext();
+            ASSERT(mLevelZeroTexture);
+            ID3D11DeviceContext *context = mRenderer->getDeviceContext();
             context->CopySubresourceRegion(mLevelZeroTexture, 0, 0, 0, 0, mTexture, 0, NULL);
         }
 
@@ -817,7 +837,7 @@
 
             // Pull data back from the level zero texture if necessary.
             ASSERT(mTexture);
-            ID3D11DeviceContext* context = mRenderer->getDeviceContext();
+            ID3D11DeviceContext *context = mRenderer->getDeviceContext();
             context->CopySubresourceRegion(mTexture, 0, 0, 0, 0, mLevelZeroTexture, 0, NULL);
         }
 
@@ -906,7 +926,7 @@
 
 gl::Error TextureStorage11_2D::getResource(ID3D11Resource **outResource)
 {
-    if (mUseLevelZeroTexture)
+    if (mUseLevelZeroTexture && mMipLevels > 1)
     {
         gl::Error error = ensureTextureExists(1);
         if (error.isError())
@@ -947,7 +967,9 @@
 
 gl::Error TextureStorage11_2D::ensureTextureExists(int mipLevels)
 {
-    ID3D11Texture2D** outputTexture = (mipLevels == 1 && mRenderer->getWorkarounds().zeroMaxLodWorkaround) ? &mLevelZeroTexture : &mTexture;
+    // If mMipLevels = 1 then always use mTexture rather than mLevelZeroTexture.
+    bool useLevelZeroTexture = mRenderer->getWorkarounds().zeroMaxLodWorkaround ? (mipLevels == 1) && (mMipLevels > 1) : false;
+    ID3D11Texture2D **outputTexture = useLevelZeroTexture ? &mLevelZeroTexture : &mTexture;
 
     // if the width or height is not positive this should be treated as an incomplete texture
     // we handle that here by skipping the d3d texture creation
@@ -1126,7 +1148,7 @@
         ASSERT(baseLevel == 0);
         // This code also assumes that the incoming texture equals either mLevelZeroTexture or mTexture.
 
-        if (mipLevels == 1)
+        if (mipLevels == 1 && mMipLevels > 1)
         {
             // We must use a SRV on the level-zero-only texture.
             ASSERT(mLevelZeroTexture != NULL && texture == mLevelZeroTexture);
@@ -1220,7 +1242,7 @@
     return gl::Error(GL_NO_ERROR);
 }
 
-TextureStorage11_Cube::TextureStorage11_Cube(Renderer11 *renderer, GLenum internalformat, bool renderTarget, int size, int levels)
+TextureStorage11_Cube::TextureStorage11_Cube(Renderer11 *renderer, GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly)
     : TextureStorage11(renderer, GetTextureBindFlags(internalformat, renderer->getFeatureLevel(), renderTarget))
 {
     mTexture = NULL;
@@ -1236,6 +1258,14 @@
         }
     }
 
+    mLevelZeroTexture = NULL;
+    mUseLevelZeroTexture = false;
+
+    for (unsigned int face = 0; face < CUBE_FACE_COUNT; face++)
+    {
+        mLevelZeroRenderTarget[face] = NULL;
+    }
+
     mInternalFormat = internalformat;
 
     const d3d11::TextureFormat &formatInfo = d3d11::GetTextureFormatInfo(internalformat, renderer->getFeatureLevel());
@@ -1256,6 +1286,13 @@
     mTextureHeight = size;
     mTextureDepth = 1;
 
+    if (hintLevelZeroOnly && levels > 1)
+    {
+        //The LevelZeroOnly hint should only be true if the zero max LOD workaround is active.
+        ASSERT(mRenderer->getWorkarounds().zeroMaxLodWorkaround);
+        mUseLevelZeroTexture = true;
+    }
+
     initializeSerials(getLevelCount() * CUBE_FACE_COUNT, CUBE_FACE_COUNT);
 }
 
@@ -1281,6 +1318,12 @@
 
     SafeRelease(mTexture);
     SafeRelease(mSwizzleTexture);
+    SafeRelease(mLevelZeroTexture);
+
+    for (unsigned int face = 0; face < CUBE_FACE_COUNT; face++)
+    {
+        SafeDelete(mLevelZeroRenderTarget[face]);
+    }
 
     for (unsigned int level = 0; level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
     {
@@ -1298,6 +1341,147 @@
     return static_cast<TextureStorage11_Cube*>(storage);
 }
 
+UINT TextureStorage11_Cube::getSubresourceIndex(const gl::ImageIndex &index) const
+{
+    if (mRenderer->getWorkarounds().zeroMaxLodWorkaround && mUseLevelZeroTexture && index.mipIndex == 0)
+    {
+        UINT arraySlice = static_cast<UINT>(index.hasLayer() ? index.layerIndex : 0);
+        UINT subresource = D3D11CalcSubresource(0, arraySlice, 1);
+        ASSERT(subresource != std::numeric_limits<UINT>::max());
+        return subresource;
+    }
+    else
+    {
+        UINT mipSlice = static_cast<UINT>(index.mipIndex + mTopLevel);
+        UINT arraySlice = static_cast<UINT>(index.hasLayer() ? index.layerIndex : 0);
+        UINT subresource = D3D11CalcSubresource(mipSlice, arraySlice, mMipLevels);
+        ASSERT(subresource != std::numeric_limits<UINT>::max());
+        return subresource;
+    }
+}
+
+gl::Error TextureStorage11_Cube::copyToStorage(TextureStorage *destStorage)
+{
+    ASSERT(destStorage);
+
+    TextureStorage11_Cube *dest11 = TextureStorage11_Cube::makeTextureStorage11_Cube(destStorage);
+
+    if (mRenderer->getWorkarounds().zeroMaxLodWorkaround)
+    {
+        ID3D11DeviceContext *immediateContext = mRenderer->getDeviceContext();
+
+        // If either mTexture or mLevelZeroTexture exist, then we need to copy them into the corresponding textures in destStorage.
+        if (mTexture)
+        {
+            gl::Error error = dest11->useLevelZeroWorkaroundTexture(false);
+            if (error.isError())
+            {
+                return error;
+            }
+
+            ID3D11Resource *destResource = NULL;
+            error = dest11->getResource(&destResource);
+            if (error.isError())
+            {
+                return error;
+            }
+
+            immediateContext->CopyResource(destResource, mTexture);
+        }
+
+        if (mLevelZeroTexture)
+        {
+            gl::Error error = dest11->useLevelZeroWorkaroundTexture(true);
+            if (error.isError())
+            {
+                return error;
+            }
+
+            ID3D11Resource *destResource = NULL;
+            error = dest11->getResource(&destResource);
+            if (error.isError())
+            {
+                return error;
+            }
+
+            immediateContext->CopyResource(destResource, mLevelZeroTexture);
+        }
+    }
+    else
+    {
+        ID3D11Resource *sourceResouce = NULL;
+        gl::Error error = getResource(&sourceResouce);
+        if (error.isError())
+        {
+            return error;
+        }
+
+        ID3D11Resource *destResource = NULL;
+        error = dest11->getResource(&destResource);
+        if (error.isError())
+        {
+            return error;
+        }
+
+        ID3D11DeviceContext *immediateContext = mRenderer->getDeviceContext();
+        immediateContext->CopyResource(destResource, sourceResouce);
+    }
+
+    dest11->invalidateSwizzleCache();
+
+    return gl::Error(GL_NO_ERROR);
+}
+
+gl::Error TextureStorage11_Cube::useLevelZeroWorkaroundTexture(bool useLevelZeroTexture)
+{
+    if (useLevelZeroTexture && mMipLevels > 1)
+    {
+        if (!mUseLevelZeroTexture && mTexture)
+        {
+            gl::Error error = ensureTextureExists(1);
+            if (error.isError())
+            {
+                return error;
+            }
+
+            // Pull data back from the mipped texture if necessary.
+            ASSERT(mLevelZeroTexture);
+            ID3D11DeviceContext *context = mRenderer->getDeviceContext();
+
+            for (int face = 0; face < 6; face++)
+            {
+                context->CopySubresourceRegion(mLevelZeroTexture, D3D11CalcSubresource(0, face, 1), 0, 0, 0, mTexture, face * mMipLevels, NULL);
+            }
+        }
+
+        mUseLevelZeroTexture = true;
+    }
+    else
+    {
+        if (mUseLevelZeroTexture && mLevelZeroTexture)
+        {
+            gl::Error error = ensureTextureExists(mMipLevels);
+            if (error.isError())
+            {
+                return error;
+            }
+
+            // Pull data back from the level zero texture if necessary.
+            ASSERT(mTexture);
+            ID3D11DeviceContext *context = mRenderer->getDeviceContext();
+
+            for (int face = 0; face < 6; face++)
+            {
+                context->CopySubresourceRegion(mTexture, D3D11CalcSubresource(0, face, mMipLevels), 0, 0, 0, mLevelZeroTexture, face, NULL);
+            }
+        }
+
+        mUseLevelZeroTexture = false;
+    }
+
+    return gl::Error(GL_NO_ERROR);
+}
+
 void TextureStorage11_Cube::associateImage(Image11* image, const gl::ImageIndex &index)
 {
     GLint level = index.mipIndex;
@@ -1396,9 +1580,54 @@
 
 gl::Error TextureStorage11_Cube::getResource(ID3D11Resource **outResource)
 {
+    if (mUseLevelZeroTexture && mMipLevels > 1)
+    {
+        gl::Error error = ensureTextureExists(1);
+        if (error.isError())
+        {
+            return error;
+        }
+
+        *outResource = mLevelZeroTexture;
+        return gl::Error(GL_NO_ERROR);
+    }
+    else
+    {
+        gl::Error error = ensureTextureExists(mMipLevels);
+        if (error.isError())
+        {
+            return error;
+        }
+
+        *outResource = mTexture;
+        return gl::Error(GL_NO_ERROR);
+    }
+}
+
+gl::Error TextureStorage11_Cube::getMippedResource(ID3D11Resource **outResource)
+{
+    // This shouldn't be called unless the zero max LOD workaround is active.
+    ASSERT(mRenderer->getWorkarounds().zeroMaxLodWorkaround);
+
+    gl::Error error = ensureTextureExists(mMipLevels);
+    if (error.isError())
+    {
+        return error;
+    }
+
+    *outResource = mTexture;
+    return gl::Error(GL_NO_ERROR);
+}
+
+gl::Error TextureStorage11_Cube::ensureTextureExists(int mipLevels)
+{
+    // If mMipLevels = 1 then always use mTexture rather than mLevelZeroTexture.
+    bool useLevelZeroTexture = mRenderer->getWorkarounds().zeroMaxLodWorkaround ? (mipLevels == 1) && (mMipLevels > 1) : false;
+    ID3D11Texture2D **outputTexture = useLevelZeroTexture ? &mLevelZeroTexture : &mTexture;
+
     // if the size is not positive this should be treated as an incomplete texture
     // we handle that here by skipping the d3d texture creation
-    if (mTexture == NULL && mTextureWidth > 0 && mTextureHeight > 0)
+    if (*outputTexture == NULL && mTextureWidth > 0 && mTextureHeight > 0)
     {
         ASSERT(mMipLevels > 0);
 
@@ -1407,7 +1636,7 @@
         D3D11_TEXTURE2D_DESC desc;
         desc.Width = mTextureWidth;
         desc.Height = mTextureHeight;
-        desc.MipLevels = mMipLevels;
+        desc.MipLevels = mipLevels;
         desc.ArraySize = CUBE_FACE_COUNT;
         desc.Format = mTextureFormat;
         desc.SampleDesc.Count = 1;
@@ -1417,7 +1646,7 @@
         desc.CPUAccessFlags = 0;
         desc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;
 
-        HRESULT result = device->CreateTexture2D(&desc, NULL, &mTexture);
+        HRESULT result = device->CreateTexture2D(&desc, NULL, outputTexture);
 
         // this can happen from windows TDR
         if (d3d11::isDeviceLostError(result))
@@ -1432,7 +1661,6 @@
         }
     }
 
-    *outResource = mTexture;
     return gl::Error(GL_NO_ERROR);
 }
 
@@ -1456,14 +1684,53 @@
             return error;
         }
 
+        if (mUseLevelZeroTexture)
+        {
+            if (!mLevelZeroRenderTarget[faceIndex])
+            {
+                D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
+                rtvDesc.Format = mRenderTargetFormat;
+                rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
+                rtvDesc.Texture2DArray.MipSlice = mTopLevel + level;
+                rtvDesc.Texture2DArray.FirstArraySlice = faceIndex;
+                rtvDesc.Texture2DArray.ArraySize = 1;
+
+                ID3D11RenderTargetView *rtv;
+                result = device->CreateRenderTargetView(mLevelZeroTexture, &rtvDesc, &rtv);
+
+                if (result == E_OUTOFMEMORY)
+                {
+                    return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal render target view for texture storage, result: 0x%X.", result);
+                }
+                ASSERT(SUCCEEDED(result));
+
+                mLevelZeroRenderTarget[faceIndex] = new TextureRenderTarget11(rtv, mLevelZeroTexture, NULL, mInternalFormat, getLevelWidth(level), getLevelHeight(level), 1, 0);
+
+                // RenderTarget will take ownership of these resources
+                SafeRelease(rtv);
+            }
+
+            ASSERT(outRT);
+            *outRT = mLevelZeroRenderTarget[faceIndex];
+            return gl::Error(GL_NO_ERROR);
+        }
+
         D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
         srvDesc.Format = mShaderResourceFormat;
-        srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; // Will be used with Texture2D sampler, not TextureCube
         srvDesc.Texture2DArray.MostDetailedMip = mTopLevel + level;
         srvDesc.Texture2DArray.MipLevels = 1;
         srvDesc.Texture2DArray.FirstArraySlice = faceIndex;
         srvDesc.Texture2DArray.ArraySize = 1;
 
+        if (mRenderer->getFeatureLevel() <= D3D_FEATURE_LEVEL_9_3)
+        {
+            srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
+        }
+        else
+        {
+            srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; // Will be used with Texture2D sampler, not TextureCube
+        }
+
         ID3D11ShaderResourceView *srv;
         result = device->CreateShaderResourceView(texture, &srvDesc, &srv);
 
@@ -1560,8 +1827,30 @@
         srvDesc.TextureCube.MostDetailedMip = mTopLevel + baseLevel;
     }
 
+    ID3D11Resource *srvTexture = texture;
+
+    if (mRenderer->getWorkarounds().zeroMaxLodWorkaround)
+    {
+        ASSERT(mTopLevel == 0);
+        ASSERT(baseLevel == 0);
+        // This code also assumes that the incoming texture equals either mLevelZeroTexture or mTexture.
+
+        if (mipLevels == 1 && mMipLevels > 1)
+        {
+            // We must use a SRV on the level-zero-only texture.
+            ASSERT(mLevelZeroTexture != NULL && texture == mLevelZeroTexture);
+            srvTexture = mLevelZeroTexture;
+        }
+        else
+        {
+            ASSERT(mipLevels == static_cast<int>(mMipLevels));
+            ASSERT(mTexture != NULL && texture == mTexture);
+            srvTexture = mTexture;
+        }
+    }
+
     ID3D11Device *device = mRenderer->getDevice();
-    HRESULT result = device->CreateShaderResourceView(texture, &srvDesc, outSRV);
+    HRESULT result = device->CreateShaderResourceView(srvTexture, &srvDesc, outSRV);
 
     ASSERT(result == E_OUTOFMEMORY || SUCCEEDED(result));
     if (FAILED(result))
diff --git a/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.h b/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.h
index 97f0d76..4b3760b 100644
--- a/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.h
+++ b/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.h
@@ -50,7 +50,7 @@
     virtual bool isRenderTarget() const;
     virtual bool isManaged() const;
     virtual int getLevelCount() const;
-    UINT getSubresourceIndex(const gl::ImageIndex &index) const;
+    virtual UINT getSubresourceIndex(const gl::ImageIndex &index) const;
 
     gl::Error generateSwizzles(GLenum swizzleRed, GLenum swizzleGreen, GLenum swizzleBlue, GLenum swizzleAlpha);
     void invalidateSwizzleCacheLevel(int mipLevel);
@@ -201,23 +201,32 @@
 class TextureStorage11_Cube : public TextureStorage11
 {
   public:
-    TextureStorage11_Cube(Renderer11 *renderer, GLenum internalformat, bool renderTarget, int size, int levels);
+    TextureStorage11_Cube(Renderer11 *renderer, GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly);
     virtual ~TextureStorage11_Cube();
 
     static TextureStorage11_Cube *makeTextureStorage11_Cube(TextureStorage *storage);
 
+    virtual UINT getSubresourceIndex(const gl::ImageIndex &index) const;
+
     virtual gl::Error getResource(ID3D11Resource **outResource);
+    virtual gl::Error getMippedResource(ID3D11Resource **outResource);
     virtual gl::Error getRenderTarget(const gl::ImageIndex &index, RenderTargetD3D **outRT);
 
+    virtual gl::Error copyToStorage(TextureStorage *destStorage);
+
     virtual void associateImage(Image11* image, const gl::ImageIndex &index);
     virtual void disassociateImage(const gl::ImageIndex &index, Image11* expectedImage);
     virtual bool isAssociatedImageValid(const gl::ImageIndex &index, Image11* expectedImage);
     virtual gl::Error releaseAssociatedImage(const gl::ImageIndex &index, Image11* incomingImage);
 
+    virtual gl::Error useLevelZeroWorkaroundTexture(bool useLevelZeroTexture);
+
   protected:
     virtual gl::Error getSwizzleTexture(ID3D11Resource **outTexture);
     virtual gl::Error getSwizzleRenderTarget(int mipLevel, ID3D11RenderTargetView **outRTV);
 
+    gl::Error ensureTextureExists(int mipLevels);
+
   private:
     DISALLOW_COPY_AND_ASSIGN(TextureStorage11_Cube);
 
@@ -229,6 +238,11 @@
     ID3D11Texture2D *mTexture;
     RenderTarget11 *mRenderTarget[CUBE_FACE_COUNT][gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS];
 
+    // Level-zero workaround members. See TextureStorage11_2D's workaround members for a description.
+    ID3D11Texture2D *mLevelZeroTexture;
+    RenderTarget11 *mLevelZeroRenderTarget[CUBE_FACE_COUNT];
+    bool mUseLevelZeroTexture;
+
     ID3D11Texture2D *mSwizzleTexture;
     ID3D11RenderTargetView *mSwizzleRenderTargets[gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS];
 
diff --git a/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp b/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
index 1fc9874..7125e2b 100644
--- a/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
+++ b/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
@@ -2832,9 +2832,9 @@
     return new TextureStorage9_2D(this, internalformat, renderTarget, width, height, levels);
 }
 
-TextureStorage *Renderer9::createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels)
+TextureStorage *Renderer9::createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly)
 {
-    return new TextureStorage9_Cube(this, internalformat, renderTarget, size, levels);
+    return new TextureStorage9_Cube(this, internalformat, renderTarget, size, levels, hintLevelZeroOnly);
 }
 
 TextureStorage *Renderer9::createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels)
diff --git a/src/libANGLE/renderer/d3d/d3d9/Renderer9.h b/src/libANGLE/renderer/d3d/d3d9/Renderer9.h
index 82e2e68..5d8c698 100644
--- a/src/libANGLE/renderer/d3d/d3d9/Renderer9.h
+++ b/src/libANGLE/renderer/d3d/d3d9/Renderer9.h
@@ -164,7 +164,7 @@
     gl::Error generateMipmap(ImageD3D *dest, ImageD3D *source) override;
     virtual TextureStorage *createTextureStorage2D(SwapChainD3D *swapChain);
     virtual TextureStorage *createTextureStorage2D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, bool hintLevelZeroOnly);
-    virtual TextureStorage *createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels);
+    virtual TextureStorage *createTextureStorageCube(GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly);
     virtual TextureStorage *createTextureStorage3D(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels);
     virtual TextureStorage *createTextureStorage2DArray(GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, GLsizei depth, int levels);
 
diff --git a/src/libANGLE/renderer/d3d/d3d9/TextureStorage9.cpp b/src/libANGLE/renderer/d3d/d3d9/TextureStorage9.cpp
index b33645b..139cb3e 100644
--- a/src/libANGLE/renderer/d3d/d3d9/TextureStorage9.cpp
+++ b/src/libANGLE/renderer/d3d/d3d9/TextureStorage9.cpp
@@ -286,7 +286,7 @@
     return gl::Error(GL_NO_ERROR);
 }
 
-TextureStorage9_Cube::TextureStorage9_Cube(Renderer9 *renderer, GLenum internalformat, bool renderTarget, int size, int levels)
+TextureStorage9_Cube::TextureStorage9_Cube(Renderer9 *renderer, GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly)
     : TextureStorage9(renderer, GetTextureUsage(internalformat, renderTarget))
 {
     mTexture = NULL;
diff --git a/src/libANGLE/renderer/d3d/d3d9/TextureStorage9.h b/src/libANGLE/renderer/d3d/d3d9/TextureStorage9.h
index 0b5d9cc..49667e1 100644
--- a/src/libANGLE/renderer/d3d/d3d9/TextureStorage9.h
+++ b/src/libANGLE/renderer/d3d/d3d9/TextureStorage9.h
@@ -88,7 +88,7 @@
 class TextureStorage9_Cube : public TextureStorage9
 {
   public:
-    TextureStorage9_Cube(Renderer9 *renderer, GLenum internalformat, bool renderTarget, int size, int levels);
+    TextureStorage9_Cube(Renderer9 *renderer, GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly);
     virtual ~TextureStorage9_Cube();
 
     static TextureStorage9_Cube *makeTextureStorage9_Cube(TextureStorage *storage);