Bug 922810 - Part 8: Defer/avoid initialization of texture images - r=jgilbert

This commit is contained in:
Benoit Jacob 2013-10-11 09:16:43 -04:00
parent 7ae9b37393
commit ad878d159b
3 changed files with 96 additions and 29 deletions

View File

@ -680,6 +680,10 @@ WebGLContext::CopyTexSubImage2D(GLenum target,
if (!mBoundFramebuffer->CheckAndInitializeAttachments())
return ErrorInvalidFramebufferOperation("copyTexSubImage2D: incomplete framebuffer");
if (imageInfo.HasUninitializedImageData()) {
tex->DoDeferredImageInitialization(target, level);
}
return CopyTexSubImage2D_base(target, level, format, xoffset, yoffset, x, y, width, height, true);
}
@ -1035,14 +1039,15 @@ WebGLContext::BindFakeBlackTexturesHelper(
continue;
}
bool opaque = s == WebGLTextureFakeBlackStatus::IncompleteTexture;
bool alpha = s == WebGLTextureFakeBlackStatus::UninitializedImageData &&
FormatHasAlpha(boundTexturesArray[i]->ImageInfoBase().Format());
ScopedDeletePtr<FakeBlackTexture>&
blackTexturePtr = opaque
? opaqueTextureScopedPtr
: transparentTextureScopedPtr;
blackTexturePtr = alpha
? transparentTextureScopedPtr
: opaqueTextureScopedPtr;
if (!blackTexturePtr) {
GLenum format = opaque ? LOCAL_GL_RGB : LOCAL_GL_RGBA;
GLenum format = alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
blackTexturePtr
= new FakeBlackTexture(gl, target, format);
}
@ -3470,6 +3475,10 @@ WebGLContext::CompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset,
}
}
if (imageInfo.HasUninitializedImageData()) {
tex->DoDeferredImageInitialization(target, level);
}
gl->fCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, byteLength, view.Data());
return;
@ -3810,28 +3819,9 @@ WebGLContext::TexImage2D_base(GLenum target, GLint level, GLenum internalformat,
}
imageInfoStatusIfSuccess = WebGLImageDataStatus::InitializedImageData;
} else {
if (isDepthTexture) {
// When ANGLE_depth_texture is all we have, we cannot use texImage2D
// to upload anything to a depth texture. Fortunately, the WEBGL_depth_texture
// spec guarantees that there is no other image on this texture, and that
// texSubImage2D cannot be used on it. So we just do not initialize this
// image, and instead mark it as uninitialized so that a fake black texture
// will be sampled from instead.
error = CheckedTexImage2D(target, level, internalformat,
width, height, border, format, type, nullptr);
imageInfoStatusIfSuccess = WebGLImageDataStatus::UninitializedImageData;
} else {
// We need some zero pages, because GL doesn't guarantee the
// contents of a texture allocated with nullptr data.
// Hopefully calloc will just mmap zero pages here.
void *tempZeroData = calloc(1, bytesNeeded);
if (!tempZeroData)
return ErrorOutOfMemory("texImage2D: could not allocate %d bytes (for zero fill)", bytesNeeded);
error = CheckedTexImage2D(target, level, internalformat,
width, height, border, format, type, tempZeroData);
free(tempZeroData);
imageInfoStatusIfSuccess = WebGLImageDataStatus::InitializedImageData;
}
error = CheckedTexImage2D(target, level, internalformat,
width, height, border, format, type, nullptr);
imageInfoStatusIfSuccess = WebGLImageDataStatus::UninitializedImageData;
}
if (error) {
@ -3967,6 +3957,10 @@ WebGLContext::TexSubImage2D_base(GLenum target, GLint level,
if (imageInfo.Format() != format || imageInfo.Type() != type)
return ErrorInvalidOperation("texSubImage2D: format or type doesn't match the existing texture");
if (imageInfo.HasUninitializedImageData()) {
tex->DoDeferredImageInitialization(target, level);
}
MakeContextCurrent();
size_t srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();

View File

@ -6,6 +6,7 @@
#include "WebGLContext.h"
#include "WebGLTexture.h"
#include "GLContext.h"
#include "WebGLTexelConversions.h"
#include "mozilla/dom/WebGLRenderingContextBinding.h"
#include <algorithm>
@ -358,9 +359,44 @@ WebGLTexture::ResolvedFakeBlackStatus() {
hasUninitializedImageData |= (ImageInfoAtFace(face, level).mImageDataStatus == WebGLImageDataStatus::UninitializedImageData);
}
}
if (hasUninitializedImageData) {
mFakeBlackStatus = WebGLTextureFakeBlackStatus::UninitializedImageData;
return mFakeBlackStatus;
bool hasAnyInitializedImageData = false;
for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) {
for (size_t face = 0; face < mFacesCount; ++face) {
if (ImageInfoAtFace(face, level).mImageDataStatus == WebGLImageDataStatus::InitializedImageData) {
hasAnyInitializedImageData = true;
break;
}
}
if (hasAnyInitializedImageData) {
break;
}
}
if (hasAnyInitializedImageData) {
// The texture contains some initialized image data, and some uninitialized image data.
// In this case, we have no choice but to initialize all image data now. Fortunately,
// in this case we know that we can't be dealing with a depth texture per WEBGL_depth_texture
// and ANGLE_depth_texture (which allow only one image per texture) so we can assume that
// glTexImage2D is able to upload data to images.
for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) {
for (size_t face = 0; face < mFacesCount; ++face) {
GLenum imageTarget = mTarget == LOCAL_GL_TEXTURE_2D
? LOCAL_GL_TEXTURE_2D
: LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level);
if (imageInfo.mImageDataStatus == WebGLImageDataStatus::UninitializedImageData) {
DoDeferredImageInitialization(imageTarget, level);
}
}
}
mFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded;
} else {
// The texture only contains uninitialized image data. In this case,
// we can use a black texture for it.
mFakeBlackStatus = WebGLTextureFakeBlackStatus::UninitializedImageData;
}
}
// we have exhausted all cases where we do need fakeblack, so if the status is still unknown,
@ -373,6 +409,41 @@ WebGLTexture::ResolvedFakeBlackStatus() {
return mFakeBlackStatus;
}
void
WebGLTexture::DoDeferredImageInitialization(GLenum imageTarget, GLint level)
{
const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level);
MOZ_ASSERT(imageInfo.mImageDataStatus == WebGLImageDataStatus::UninitializedImageData);
mContext->MakeContextCurrent();
gl::ScopedBindTexture autoBindTex(mContext->gl, GLName(), mTarget);
WebGLTexelFormat texelformat = GetWebGLTexelFormat(imageInfo.mFormat, imageInfo.mType);
uint32_t texelsize = WebGLTexelConversions::TexelBytesForFormat(texelformat);
CheckedUint32 checked_byteLength
= WebGLContext::GetImageSize(
imageInfo.mHeight,
imageInfo.mWidth,
texelsize,
mContext->mPixelStoreUnpackAlignment);
MOZ_ASSERT(checked_byteLength.isValid()); // should have been checked earlier
void *zeros = calloc(1, checked_byteLength.value());
GLenum error
= mContext->CheckedTexImage2D(imageTarget, level, imageInfo.mFormat,
imageInfo.mWidth, imageInfo.mHeight,
0, imageInfo.mFormat, imageInfo.mType,
zeros);
free(zeros);
SetImageDataStatus(imageTarget, level, WebGLImageDataStatus::InitializedImageData);
if (error) {
// Should only be OUT_OF_MEMORY. Anyway, there's no good way to recover from this here.
MOZ_CRASH(); // errors on texture upload have been related to video memory exposure in the past.
return;
}
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTexture)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTexture, AddRef)

View File

@ -196,6 +196,8 @@ public:
imageInfo.mImageDataStatus = newStatus;
}
void DoDeferredImageInitialization(GLenum imageTarget, GLint level);
protected:
GLenum mTarget;