Change the checks for texture completeness in Texture*::copySubImage to only check if the specified level is complete.
This removes the dependency between copySubImage and isSamplerComplete,
which makes operation with sampler objects simpler.
TRAC #23453
Signed-off-by: Nicolas Capens
Signed-off-by: Shannon Woods
Authored-by: Jamie Madill
diff --git a/src/libGLESv2/Texture.cpp b/src/libGLESv2/Texture.cpp
index 4e97bf2..55210ec 100644
--- a/src/libGLESv2/Texture.cpp
+++ b/src/libGLESv2/Texture.cpp
@@ -599,7 +599,11 @@
return gl::error(GL_INVALID_VALUE);
}
- if (!mImageArray[level]->isRenderableFormat() || (!mTexStorage && !isSamplerComplete()))
+ // can only make our texture storage to a render target if level 0 is defined (with a width & height) and
+ // the current level we're copying to is defined (with appropriate format, width & height)
+ bool canCreateRenderTarget = isLevelComplete(level) && isLevelComplete(0);
+
+ if (!mImageArray[level]->isRenderableFormat() || (!mTexStorage && !canCreateRenderTarget))
{
mImageArray[level]->copy(xoffset, yoffset, 0, x, y, width, height, source);
mDirtyImages = true;
@@ -611,10 +615,10 @@
convertToRenderTarget();
}
- updateTexture();
-
if (level < levelCount())
{
+ updateTextureLevel(level);
+
GLuint clientVersion = mRenderer->getCurrentClientVersion();
gl::Rectangle sourceRect;
@@ -731,37 +735,60 @@
// Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
bool Texture2D::isMipmapComplete() const
{
+ GLsizei width = mImageArray[0]->getWidth();
+ GLsizei height = mImageArray[0]->getHeight();
+
+ int q = log2(std::max(width, height));
+
+ for (int level = 0; level <= q; level++)
+ {
+ if (!isLevelComplete(level))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool Texture2D::isLevelComplete(int level) const
+{
if (isImmutable())
{
return true;
}
- GLsizei width = mImageArray[0]->getWidth();
- GLsizei height = mImageArray[0]->getHeight();
+ const rx::Image *baseImage = mImageArray[0];
+ GLsizei width = baseImage->getWidth();
+ GLsizei height = baseImage->getHeight();
if (width <= 0 || height <= 0)
{
return false;
}
- int q = log2(std::max(width, height));
-
- for (int level = 1; level <= q; level++)
+ // The base image level is complete if the width and height are positive
+ if (level == 0)
{
- if (mImageArray[level]->getInternalFormat() != mImageArray[0]->getInternalFormat())
- {
- return false;
- }
+ return true;
+ }
- if (mImageArray[level]->getWidth() != std::max(1, width >> level))
- {
- return false;
- }
+ ASSERT(level >= 1 && level <= (int)ArraySize(mImageArray) && mImageArray[level] != NULL);
+ rx::Image *image = mImageArray[level];
- if (mImageArray[level]->getHeight() != std::max(1, height >> level))
- {
- return false;
- }
+ if (image->getInternalFormat() != baseImage->getInternalFormat())
+ {
+ return false;
+ }
+
+ if (image->getWidth() != std::max(1, width >> level))
+ {
+ return false;
+ }
+
+ if (image->getHeight() != std::max(1, height >> level))
+ {
+ return false;
}
return true;
@@ -811,12 +838,18 @@
for (int level = 0; level < levels; level++)
{
- rx::Image *image = mImageArray[level];
+ updateTextureLevel(level);
+ }
+}
- if (image->isDirty())
- {
- commitRect(level, 0, 0, mImageArray[level]->getWidth(), mImageArray[level]->getHeight());
- }
+void Texture2D::updateTextureLevel(int level)
+{
+ ASSERT(level <= (int)ArraySize(mImageArray) && mImageArray[level] != NULL);
+ rx::Image *image = mImageArray[level];
+
+ if (image->isDirty())
+ {
+ commitRect(level, 0, 0, mImageArray[level]->getWidth(), mImageArray[level]->getHeight());
}
}
@@ -1201,19 +1234,13 @@
}
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]->getInternalFormat() != mImageArray[0][0]->getInternalFormat())
- {
- return false;
- }
-
- if (mImageArray[face][level]->getWidth() != std::max(1, size >> level))
+ if (!isFaceLevelComplete(face, level))
{
return false;
}
@@ -1223,6 +1250,44 @@
return true;
}
+bool TextureCubeMap::isFaceLevelComplete(int face, int level) const
+{
+ ASSERT(level >= 0 && face < 6 && level < (int)ArraySize(mImageArray[face]) && mImageArray[face][level] != NULL);
+
+ if (isImmutable())
+ {
+ return true;
+ }
+
+ const rx::Image *baseImage = mImageArray[face][0];
+ GLsizei size = baseImage->getWidth();
+
+ if (size <= 0)
+ {
+ return false;
+ }
+
+ // The base image level is complete if the width and height are positive
+ if (level == 0)
+ {
+ return true;
+ }
+
+ rx::Image *image = mImageArray[face][level];
+
+ if (image->getInternalFormat() != baseImage->getInternalFormat())
+ {
+ return false;
+ }
+
+ if (mImageArray[face][level]->getWidth() != std::max(1, size >> level))
+ {
+ return false;
+ }
+
+ return true;
+}
+
bool TextureCubeMap::isCompressed(GLenum target, GLint level) const
{
return IsFormatCompressed(getInternalFormat(target, level), mRenderer->getCurrentClientVersion());
@@ -1266,16 +1331,22 @@
{
for (int level = 0; level < levels; level++)
{
- rx::Image *image = mImageArray[face][level];
-
- if (image->isDirty())
- {
- commitRect(face, level, 0, 0, image->getWidth(), image->getHeight());
- }
+ updateTextureFaceLevel(face, level);
}
}
}
+void TextureCubeMap::updateTextureFaceLevel(int face, int level)
+{
+ ASSERT(level >= 0 && face < 6 && level < (int)ArraySize(mImageArray[face]) && mImageArray[face][level] != NULL);
+ rx::Image *image = mImageArray[face][level];
+
+ if (image->isDirty())
+ {
+ commitRect(face, level, 0, 0, image->getWidth(), image->getHeight());
+ }
+}
+
void TextureCubeMap::convertToRenderTarget()
{
rx::TextureStorageInterfaceCube *newTexStorage = NULL;
@@ -1406,11 +1477,15 @@
return gl::error(GL_INVALID_VALUE);
}
- unsigned int faceindex = faceIndex(target);
+ unsigned int face = faceIndex(target);
- if (!mImageArray[faceindex][level]->isRenderableFormat() || (!mTexStorage && !isSamplerComplete()))
+ // can only make our texture storage to a render target if level 0 is defined (with a width & height) and
+ // the current level we're copying to is defined (with appropriate format, width & height)
+ bool canCreateRenderTarget = isFaceLevelComplete(face, level) && isFaceLevelComplete(face, 0);
+
+ if (!mImageArray[face][level]->isRenderableFormat() || (!mTexStorage && !canCreateRenderTarget))
{
- mImageArray[faceindex][level]->copy(0, 0, 0, x, y, width, height, source);
+ mImageArray[face][level]->copy(0, 0, 0, x, y, width, height, source);
mDirtyImages = true;
}
else
@@ -1420,10 +1495,10 @@
convertToRenderTarget();
}
- updateTexture();
-
if (level < levelCount())
{
+ updateTextureFaceLevel(face, level);
+
GLuint clientVersion = mRenderer->getCurrentClientVersion();
gl::Rectangle sourceRect;
@@ -1761,7 +1836,11 @@
return gl::error(GL_INVALID_VALUE);
}
- if (!mImageArray[level]->isRenderableFormat() || (!mTexStorage && !isSamplerComplete()))
+ // can only make our texture storage to a render target if level 0 is defined (with a width & height) and
+ // the current level we're copying to is defined (with appropriate format, width & height)
+ bool canCreateRenderTarget = isLevelComplete(level) && isLevelComplete(0);
+
+ if (!mImageArray[level]->isRenderableFormat() || (!mTexStorage && !canCreateRenderTarget))
{
mImageArray[level]->copy(xoffset, yoffset, zoffset, x, y, width, height, source);
mDirtyImages = true;
@@ -1773,10 +1852,10 @@
convertToRenderTarget();
}
- updateTexture();
-
if (level < levelCount())
{
+ updateTextureLevel(level);
+
gl::Rectangle sourceRect;
sourceRect.x = x;
sourceRect.width = width;
@@ -1824,43 +1903,68 @@
bool Texture3D::isMipmapComplete() const
{
+ GLsizei width = mImageArray[0]->getWidth();
+ GLsizei height = mImageArray[0]->getHeight();
+ GLsizei depth = mImageArray[0]->getDepth();
+
+ int q = log2(std::max(std::max(width, height), depth));
+
+ for (int level = 0; level <= q; level++)
+ {
+ if (!isLevelComplete(level))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool Texture3D::isLevelComplete(int level) const
+{
+ ASSERT(level >= 0 && level < (int)ArraySize(mImageArray) && mImageArray[level] != NULL);
+
if (isImmutable())
{
return true;
}
- GLsizei width = mImageArray[0]->getWidth();
- GLsizei height = mImageArray[0]->getHeight();
- GLsizei depth = mImageArray[0]->getDepth();
+ rx::Image *baseImage = mImageArray[0];
+
+ GLsizei width = baseImage->getWidth();
+ GLsizei height = baseImage->getHeight();
+ GLsizei depth = baseImage->getDepth();
if (width <= 0 || height <= 0 || depth <= 0)
{
return false;
}
- int q = log2(std::max(std::max(width, height), depth));
-
- for (int level = 1; level <= q; level++)
+ if (level == 0)
{
- if (mImageArray[level]->getInternalFormat() != mImageArray[0]->getInternalFormat())
- {
- return false;
- }
+ return true;
+ }
- if (mImageArray[level]->getWidth() != std::max(1, width >> level))
- {
- return false;
- }
+ rx::Image *levelImage = mImageArray[level];
- if (mImageArray[level]->getHeight() != std::max(1, height >> level))
- {
- return false;
- }
+ if (levelImage->getInternalFormat() != baseImage->getInternalFormat())
+ {
+ return false;
+ }
- if (mImageArray[level]->getDepth() != std::max(1, depth >> level))
- {
- return false;
- }
+ if (levelImage->getWidth() != std::max(1, width >> level))
+ {
+ return false;
+ }
+
+ if (levelImage->getHeight() != std::max(1, height >> level))
+ {
+ return false;
+ }
+
+ if (levelImage->getDepth() != std::max(1, depth >> level))
+ {
+ return false;
}
return true;
@@ -1911,12 +2015,19 @@
for (int level = 0; level < levels; level++)
{
- rx::Image *image = mImageArray[level];
+ updateTextureLevel(level);
+ }
+}
- if (image->isDirty())
- {
- commitRect(level, 0, 0, 0, mImageArray[level]->getWidth(), mImageArray[level]->getHeight(), mImageArray[level]->getDepth());
- }
+void Texture3D::updateTextureLevel(int level)
+{
+ ASSERT(level >= 0 && level < (int)ArraySize(mImageArray) && mImageArray[level] != NULL);
+
+ rx::Image *image = mImageArray[level];
+
+ if (image->isDirty())
+ {
+ commitRect(level, 0, 0, 0, mImageArray[level]->getWidth(), mImageArray[level]->getHeight(), mImageArray[level]->getDepth());
}
}
@@ -2255,7 +2366,11 @@
return gl::error(GL_INVALID_VALUE);
}
- if (!mImageArray[level][0]->isRenderableFormat() || (!mTexStorage && !isSamplerComplete()))
+ // can only make our texture storage to a render target if level 0 is defined (with a width & height) and
+ // the current level we're copying to is defined (with appropriate format, width & height)
+ bool canCreateRenderTarget = isLevelComplete(level) && isLevelComplete(0);
+
+ if (!mImageArray[level][0]->isRenderableFormat() || (!mTexStorage && !canCreateRenderTarget))
{
mImageArray[level][zoffset]->copy(xoffset, yoffset, 0, x, y, width, height, source);
mDirtyImages = true;
@@ -2267,10 +2382,10 @@
convertToRenderTarget();
}
- updateTexture();
-
if (level < levelCount())
{
+ updateTextureLevel(level);
+
GLuint clientVersion = mRenderer->getCurrentClientVersion();
gl::Rectangle sourceRect;
@@ -2317,6 +2432,26 @@
bool Texture2DArray::isMipmapComplete() const
{
+ GLsizei width = getWidth(0);
+ GLsizei height = getHeight(0);
+
+ int q = log2(std::max(width, height));
+
+ for (int level = 1; level <= q; level++)
+ {
+ if (!isLevelComplete(level))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool Texture2DArray::isLevelComplete(int level) const
+{
+ ASSERT(level >= 0 && level < (int)ArraySize(mImageArray) && mImageArray[level] != NULL);
+
if (isImmutable())
{
return true;
@@ -2331,29 +2466,29 @@
return false;
}
- int q = log2(std::max(width, height));
-
- for (int level = 1; level <= q; level++)
+ if (level == 0)
{
- if (getInternalFormat(level) != getInternalFormat(0))
- {
- return false;
- }
+ return true;
+ }
- if (getWidth(level) != std::max(1, width >> level))
- {
- return false;
- }
+ if (getInternalFormat(level) != getInternalFormat(0))
+ {
+ return false;
+ }
- if (getHeight(level) != std::max(1, height >> level))
- {
- return false;
- }
+ if (getWidth(level) != std::max(1, width >> level))
+ {
+ return false;
+ }
- if (getDepth(level) != depth)
- {
- return false;
- }
+ if (getHeight(level) != std::max(1, height >> level))
+ {
+ return false;
+ }
+
+ if (getDepth(level) != depth)
+ {
+ return false;
}
return true;
@@ -2407,14 +2542,19 @@
int levels = (isMipmapComplete() ? levelCount() : 1);
for (int level = 0; level < levels; level++)
{
- for (int layer = 0; layer < mLayerCounts[level]; layer++)
- {
- rx::Image *image = mImageArray[level][layer];
+ updateTextureLevel(level);
+ }
+}
- if (image->isDirty())
- {
- commitRect(level, 0, 0, layer, image->getWidth(), image->getHeight());
- }
+void Texture2DArray::updateTextureLevel(int level)
+{
+ for (int layer = 0; layer < mLayerCounts[level]; layer++)
+ {
+ rx::Image *image = mImageArray[level][layer];
+
+ if (image->isDirty())
+ {
+ commitRect(level, 0, 0, layer, image->getWidth(), image->getHeight());
}
}
}
diff --git a/src/libGLESv2/Texture.h b/src/libGLESv2/Texture.h
index 28ce151..2a17b78 100644
--- a/src/libGLESv2/Texture.h
+++ b/src/libGLESv2/Texture.h
@@ -190,6 +190,8 @@
virtual rx::TextureStorageInterface *getStorage(bool renderTarget);
bool isMipmapComplete() const;
+ bool isLevelComplete(int level) const;
+ void updateTextureLevel(int level);
void redefineImage(GLint level, GLint internalformat, GLsizei width, GLsizei height);
void commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height);
@@ -264,6 +266,8 @@
bool isCubeComplete() const;
bool isMipmapCubeComplete() const;
+ bool isFaceLevelComplete(int face, int level) const;
+ void updateTextureFaceLevel(int face, int level);
void setImage(int faceIndex, GLint level, GLsizei width, GLsizei height, GLint internalFormat, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels);
void commitRect(int faceIndex, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height);
@@ -332,6 +336,9 @@
void redefineImage(GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth);
void commitRect(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth);
+ bool isLevelComplete(int level) const;
+ void updateTextureLevel(int level);
+
rx::Image *mImageArray[IMPLEMENTATION_MAX_TEXTURE_LEVELS];
rx::TextureStorageInterface3D *mTexStorage;
@@ -395,6 +402,9 @@
void redefineImage(GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth);
void commitRect(GLint level, GLint xoffset, GLint yoffset, GLint layerTarget, GLsizei width, GLsizei height);
+ bool isLevelComplete(int level) const;
+ void updateTextureLevel(int level);
+
// Storing images as an array of single depth textures since D3D11 treats each array level of a
// Texture2D object as a separate subresource. Each layer would have to be looped over
// to update all the texture layers since they cannot all be updated at once and it makes the most