Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / dom / canvas / WebGLTexture.cpp
blobb0ed282c9bded569d3486260e061422166efa20f
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "WebGLTexture.h"
8 #include "GLContext.h"
9 #include "mozilla/dom/WebGLRenderingContextBinding.h"
10 #include "mozilla/Scoped.h"
11 #include "ScopedGLHelpers.h"
12 #include "WebGLContext.h"
13 #include "WebGLContextUtils.h"
14 #include "WebGLTexelConversions.h"
16 #include <algorithm>
18 using namespace mozilla;
20 JSObject*
21 WebGLTexture::WrapObject(JSContext *cx) {
22 return dom::WebGLTextureBinding::Wrap(cx, this);
25 WebGLTexture::WebGLTexture(WebGLContext *context)
26 : WebGLBindableName()
27 , WebGLContextBoundObject(context)
28 , mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR)
29 , mMagFilter(LOCAL_GL_LINEAR)
30 , mWrapS(LOCAL_GL_REPEAT)
31 , mWrapT(LOCAL_GL_REPEAT)
32 , mFacesCount(0)
33 , mMaxLevelWithCustomImages(0)
34 , mHaveGeneratedMipmap(false)
35 , mFakeBlackStatus(WebGLTextureFakeBlackStatus::IncompleteTexture)
37 SetIsDOMBinding();
38 mContext->MakeContextCurrent();
39 mContext->gl->fGenTextures(1, &mGLName);
40 mContext->mTextures.insertBack(this);
43 void
44 WebGLTexture::Delete() {
45 mImageInfos.Clear();
46 mContext->MakeContextCurrent();
47 mContext->gl->fDeleteTextures(1, &mGLName);
48 LinkedListElement<WebGLTexture>::removeFrom(mContext->mTextures);
51 int64_t
52 WebGLTexture::ImageInfo::MemoryUsage() const {
53 if (mImageDataStatus == WebGLImageDataStatus::NoImageData)
54 return 0;
55 int64_t bitsPerTexel = WebGLContext::GetBitsPerTexel(mWebGLFormat, mWebGLType);
56 return int64_t(mWidth) * int64_t(mHeight) * bitsPerTexel/8;
59 int64_t
60 WebGLTexture::MemoryUsage() const {
61 if (IsDeleted())
62 return 0;
63 int64_t result = 0;
64 for(size_t face = 0; face < mFacesCount; face++) {
65 if (mHaveGeneratedMipmap) {
66 // Each mipmap level is 1/4 the size of the previous level
67 // 1 + x + x^2 + ... = 1/(1-x)
68 // for x = 1/4, we get 1/(1-1/4) = 4/3
69 result += ImageInfoAtFace(face, 0).MemoryUsage() * 4 / 3;
70 } else {
71 for(size_t level = 0; level <= mMaxLevelWithCustomImages; level++)
72 result += ImageInfoAtFace(face, level).MemoryUsage();
75 return result;
78 bool
79 WebGLTexture::DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(GLenum texImageTarget) const {
80 if (mHaveGeneratedMipmap)
81 return true;
83 // We want a copy here so we can modify it temporarily.
84 ImageInfo expected = ImageInfoAt(texImageTarget, 0);
86 // checks if custom level>0 images are all defined up to the highest level defined
87 // and have the expected dimensions
88 for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) {
89 const ImageInfo& actual = ImageInfoAt(texImageTarget, level);
90 if (actual != expected)
91 return false;
92 expected.mWidth = std::max(1, expected.mWidth >> 1);
93 expected.mHeight = std::max(1, expected.mHeight >> 1);
95 // if the current level has size 1x1, we can stop here: the spec doesn't seem to forbid the existence
96 // of extra useless levels.
97 if (actual.mWidth == 1 && actual.mHeight == 1)
98 return true;
101 // if we're here, we've exhausted all levels without finding a 1x1 image
102 return false;
105 void
106 WebGLTexture::Bind(GLenum aTarget) {
107 // this function should only be called by bindTexture().
108 // it assumes that the GL context is already current.
110 bool firstTimeThisTextureIsBound = !HasEverBeenBound();
112 if (firstTimeThisTextureIsBound) {
113 BindTo(aTarget);
114 } else if (aTarget != Target()) {
115 mContext->ErrorInvalidOperation("bindTexture: this texture has already been bound to a different target");
116 // very important to return here before modifying texture state! This was the place when I lost a whole day figuring
117 // very strange 'invalid write' crashes.
118 return;
121 GLuint name = GLName();
122 GLenum target = Target();
124 mContext->gl->fBindTexture(target, name);
126 if (firstTimeThisTextureIsBound) {
127 mFacesCount = (mTarget == LOCAL_GL_TEXTURE_2D) ? 1 : 6;
128 EnsureMaxLevelWithCustomImagesAtLeast(0);
129 SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown);
131 // thanks to the WebKit people for finding this out: GL_TEXTURE_WRAP_R is not
132 // present in GLES 2, but is present in GL and it seems as if for cube maps
133 // we need to set it to GL_CLAMP_TO_EDGE to get the expected GLES behavior.
134 if (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP && !mContext->gl->IsGLES())
135 mContext->gl->fTexParameteri(mTarget, LOCAL_GL_TEXTURE_WRAP_R, LOCAL_GL_CLAMP_TO_EDGE);
139 void
140 WebGLTexture::SetImageInfo(GLenum aTarget, GLint aLevel,
141 GLsizei aWidth, GLsizei aHeight,
142 GLenum aFormat, GLenum aType, WebGLImageDataStatus aStatus)
144 // TODO(djg): I suspected the following ASSERT and check are
145 // trying to express more than they're saying, probably
146 // to do with cubemap targets. We should do this
147 // properly. https://bugzilla.mozilla.org/show_bug.cgi?id=1006908
148 MOZ_ASSERT((aTarget == LOCAL_GL_TEXTURE_2D) == (mTarget == LOCAL_GL_TEXTURE_2D));
149 if ((aTarget == LOCAL_GL_TEXTURE_2D) != (mTarget == LOCAL_GL_TEXTURE_2D)) {
150 return;
153 EnsureMaxLevelWithCustomImagesAtLeast(aLevel);
155 ImageInfoAt(aTarget, aLevel) = ImageInfo(aWidth, aHeight, aFormat, aType, aStatus);
157 if (aLevel > 0)
158 SetCustomMipmap();
160 // Invalidate framebuffer status cache
161 NotifyFBsStatusChanged();
163 SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown);
166 void
167 WebGLTexture::SetGeneratedMipmap() {
168 if (!mHaveGeneratedMipmap) {
169 mHaveGeneratedMipmap = true;
170 SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown);
174 void
175 WebGLTexture::SetCustomMipmap() {
176 if (mHaveGeneratedMipmap) {
177 // if we were in GeneratedMipmap mode and are now switching to CustomMipmap mode,
178 // we need to compute now all the mipmap image info.
180 // since we were in GeneratedMipmap mode, we know that the level 0 images all have the same info,
181 // and are power-of-two.
182 ImageInfo imageInfo = ImageInfoAtFace(0, 0);
183 NS_ASSERTION(imageInfo.IsPowerOfTwo(), "this texture is NPOT, so how could GenerateMipmap() ever accept it?");
185 GLsizei size = std::max(imageInfo.mWidth, imageInfo.mHeight);
187 // so, the size is a power of two, let's find its log in base 2.
188 size_t maxLevel = 0;
189 for (GLsizei n = size; n > 1; n >>= 1)
190 ++maxLevel;
192 EnsureMaxLevelWithCustomImagesAtLeast(maxLevel);
194 for (size_t level = 1; level <= maxLevel; ++level) {
195 // again, since the sizes are powers of two, no need for any max(1,x) computation
196 imageInfo.mWidth >>= 1;
197 imageInfo.mHeight >>= 1;
198 for(size_t face = 0; face < mFacesCount; ++face)
199 ImageInfoAtFace(face, level) = imageInfo;
202 mHaveGeneratedMipmap = false;
205 bool
206 WebGLTexture::AreAllLevel0ImageInfosEqual() const {
207 for (size_t face = 1; face < mFacesCount; ++face) {
208 if (ImageInfoAtFace(face, 0) != ImageInfoAtFace(0, 0))
209 return false;
211 return true;
214 bool
215 WebGLTexture::IsMipmapTexture2DComplete() const {
216 if (mTarget != LOCAL_GL_TEXTURE_2D)
217 return false;
218 if (!ImageInfoAt(LOCAL_GL_TEXTURE_2D, 0).IsPositive())
219 return false;
220 if (mHaveGeneratedMipmap)
221 return true;
222 return DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(LOCAL_GL_TEXTURE_2D);
225 bool
226 WebGLTexture::IsCubeComplete() const {
227 if (mTarget != LOCAL_GL_TEXTURE_CUBE_MAP)
228 return false;
229 const ImageInfo &first = ImageInfoAt(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0);
230 if (!first.IsPositive() || !first.IsSquare())
231 return false;
232 return AreAllLevel0ImageInfosEqual();
235 static GLenum
236 GLCubeMapFaceById(int id)
238 GLenum result = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + id;
239 MOZ_ASSERT(result >= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X &&
240 result <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);
241 return result;
244 bool
245 WebGLTexture::IsMipmapCubeComplete() const {
246 if (!IsCubeComplete()) // in particular, this checks that this is a cube map
247 return false;
248 for (int i = 0; i < 6; i++) {
249 GLenum face = GLCubeMapFaceById(i);
250 if (!DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(face))
251 return false;
253 return true;
256 WebGLTextureFakeBlackStatus
257 WebGLTexture::ResolvedFakeBlackStatus() {
258 if (MOZ_LIKELY(mFakeBlackStatus != WebGLTextureFakeBlackStatus::Unknown)) {
259 return mFakeBlackStatus;
262 // Determine if the texture needs to be faked as a black texture.
263 // See 3.8.2 Shader Execution in the OpenGL ES 2.0.24 spec.
265 for (size_t face = 0; face < mFacesCount; ++face) {
266 if (ImageInfoAtFace(face, 0).mImageDataStatus == WebGLImageDataStatus::NoImageData) {
267 // In case of undefined texture image, we don't print any message because this is a very common
268 // and often legitimate case (asynchronous texture loading).
269 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
270 return mFakeBlackStatus;
274 const char *msg_rendering_as_black
275 = "A texture is going to be rendered as if it were black, as per the OpenGL ES 2.0.24 spec section 3.8.2, "
276 "because it";
278 if (mTarget == LOCAL_GL_TEXTURE_2D)
280 if (DoesMinFilterRequireMipmap())
282 if (!IsMipmapTexture2DComplete()) {
283 mContext->GenerateWarning
284 ("%s is a 2D texture, with a minification filter requiring a mipmap, "
285 "and is not mipmap complete (as defined in section 3.7.10).", msg_rendering_as_black);
286 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
287 } else if (!ImageInfoAt(mTarget, 0).IsPowerOfTwo()) {
288 mContext->GenerateWarning
289 ("%s is a 2D texture, with a minification filter requiring a mipmap, "
290 "and either its width or height is not a power of two.", msg_rendering_as_black);
291 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
294 else // no mipmap required
296 if (!ImageInfoAt(mTarget, 0).IsPositive()) {
297 mContext->GenerateWarning
298 ("%s is a 2D texture and its width or height is equal to zero.",
299 msg_rendering_as_black);
300 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
301 } else if (!AreBothWrapModesClampToEdge() && !ImageInfoAt(mTarget, 0).IsPowerOfTwo()) {
302 mContext->GenerateWarning
303 ("%s is a 2D texture, with a minification filter not requiring a mipmap, "
304 "with its width or height not a power of two, and with a wrap mode "
305 "different from CLAMP_TO_EDGE.", msg_rendering_as_black);
306 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
310 else // cube map
312 bool areAllLevel0ImagesPOT = true;
313 for (size_t face = 0; face < mFacesCount; ++face)
314 areAllLevel0ImagesPOT &= ImageInfoAtFace(face, 0).IsPowerOfTwo();
316 if (DoesMinFilterRequireMipmap())
318 if (!IsMipmapCubeComplete()) {
319 mContext->GenerateWarning("%s is a cube map texture, with a minification filter requiring a mipmap, "
320 "and is not mipmap cube complete (as defined in section 3.7.10).",
321 msg_rendering_as_black);
322 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
323 } else if (!areAllLevel0ImagesPOT) {
324 mContext->GenerateWarning("%s is a cube map texture, with a minification filter requiring a mipmap, "
325 "and either the width or the height of some level 0 image is not a power of two.",
326 msg_rendering_as_black);
327 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
330 else // no mipmap required
332 if (!IsCubeComplete()) {
333 mContext->GenerateWarning("%s is a cube map texture, with a minification filter not requiring a mipmap, "
334 "and is not cube complete (as defined in section 3.7.10).",
335 msg_rendering_as_black);
336 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
337 } else if (!AreBothWrapModesClampToEdge() && !areAllLevel0ImagesPOT) {
338 mContext->GenerateWarning("%s is a cube map texture, with a minification filter not requiring a mipmap, "
339 "with some level 0 image having width or height not a power of two, and with a wrap mode "
340 "different from CLAMP_TO_EDGE.", msg_rendering_as_black);
341 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
346 if (ImageInfoBase().mWebGLType == LOCAL_GL_FLOAT &&
347 !Context()->IsExtensionEnabled(WebGLExtensionID::OES_texture_float_linear))
349 if (mMinFilter == LOCAL_GL_LINEAR ||
350 mMinFilter == LOCAL_GL_LINEAR_MIPMAP_LINEAR ||
351 mMinFilter == LOCAL_GL_LINEAR_MIPMAP_NEAREST ||
352 mMinFilter == LOCAL_GL_NEAREST_MIPMAP_LINEAR)
354 mContext->GenerateWarning("%s is a texture with a linear minification filter, "
355 "which is not compatible with gl.FLOAT by default. "
356 "Try enabling the OES_texture_float_linear extension if supported.", msg_rendering_as_black);
357 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
359 else if (mMagFilter == LOCAL_GL_LINEAR)
361 mContext->GenerateWarning("%s is a texture with a linear magnification filter, "
362 "which is not compatible with gl.FLOAT by default. "
363 "Try enabling the OES_texture_float_linear extension if supported.", msg_rendering_as_black);
364 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
366 } else if (ImageInfoBase().mWebGLType == LOCAL_GL_HALF_FLOAT_OES &&
367 !Context()->IsExtensionEnabled(WebGLExtensionID::OES_texture_half_float_linear))
369 if (mMinFilter == LOCAL_GL_LINEAR ||
370 mMinFilter == LOCAL_GL_LINEAR_MIPMAP_LINEAR ||
371 mMinFilter == LOCAL_GL_LINEAR_MIPMAP_NEAREST ||
372 mMinFilter == LOCAL_GL_NEAREST_MIPMAP_LINEAR)
374 mContext->GenerateWarning("%s is a texture with a linear minification filter, "
375 "which is not compatible with gl.HALF_FLOAT by default. "
376 "Try enabling the OES_texture_half_float_linear extension if supported.", msg_rendering_as_black);
377 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
379 else if (mMagFilter == LOCAL_GL_LINEAR)
381 mContext->GenerateWarning("%s is a texture with a linear magnification filter, "
382 "which is not compatible with gl.HALF_FLOAT by default. "
383 "Try enabling the OES_texture_half_float_linear extension if supported.", msg_rendering_as_black);
384 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
388 // We have exhausted all cases of incomplete textures, where we would need opaque black.
389 // We may still need transparent black in case of uninitialized image data.
390 bool hasUninitializedImageData = false;
391 for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) {
392 for (size_t face = 0; face < mFacesCount; ++face) {
393 hasUninitializedImageData |= (ImageInfoAtFace(face, level).mImageDataStatus == WebGLImageDataStatus::UninitializedImageData);
397 if (hasUninitializedImageData) {
398 bool hasAnyInitializedImageData = false;
399 for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) {
400 for (size_t face = 0; face < mFacesCount; ++face) {
401 if (ImageInfoAtFace(face, level).mImageDataStatus == WebGLImageDataStatus::InitializedImageData) {
402 hasAnyInitializedImageData = true;
403 break;
406 if (hasAnyInitializedImageData) {
407 break;
411 if (hasAnyInitializedImageData) {
412 // The texture contains some initialized image data, and some uninitialized image data.
413 // In this case, we have no choice but to initialize all image data now. Fortunately,
414 // in this case we know that we can't be dealing with a depth texture per WEBGL_depth_texture
415 // and ANGLE_depth_texture (which allow only one image per texture) so we can assume that
416 // glTexImage2D is able to upload data to images.
417 for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) {
418 for (size_t face = 0; face < mFacesCount; ++face) {
419 GLenum imageTarget = mTarget == LOCAL_GL_TEXTURE_2D
420 ? LOCAL_GL_TEXTURE_2D
421 : LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
422 const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level);
423 if (imageInfo.mImageDataStatus == WebGLImageDataStatus::UninitializedImageData) {
424 DoDeferredImageInitialization(imageTarget, level);
428 mFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded;
429 } else {
430 // The texture only contains uninitialized image data. In this case,
431 // we can use a black texture for it.
432 mFakeBlackStatus = WebGLTextureFakeBlackStatus::UninitializedImageData;
436 // we have exhausted all cases where we do need fakeblack, so if the status is still unknown,
437 // that means that we do NOT need it.
438 if (mFakeBlackStatus == WebGLTextureFakeBlackStatus::Unknown) {
439 mFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded;
442 MOZ_ASSERT(mFakeBlackStatus != WebGLTextureFakeBlackStatus::Unknown);
443 return mFakeBlackStatus;
447 static bool
448 ClearByMask(WebGLContext* context, GLbitfield mask)
450 gl::GLContext* gl = context->GL();
451 MOZ_ASSERT(gl->IsCurrent());
453 GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
454 if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
455 return false;
457 bool colorAttachmentsMask[WebGLContext::kMaxColorAttachments] = {false};
458 if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
459 colorAttachmentsMask[0] = true;
462 context->ForceClearFramebufferWithDefaultValues(mask, colorAttachmentsMask);
463 return true;
466 // `mask` from glClear.
467 static bool
468 ClearWithTempFB(WebGLContext* context, GLuint tex,
469 GLenum texImageTarget, GLint level,
470 GLenum baseInternalFormat,
471 GLsizei width, GLsizei height)
473 if (texImageTarget != LOCAL_GL_TEXTURE_2D)
474 return false;
476 gl::GLContext* gl = context->GL();
477 MOZ_ASSERT(gl->IsCurrent());
479 gl::ScopedFramebuffer fb(gl);
480 gl::ScopedBindFramebuffer autoFB(gl, fb.FB());
481 GLbitfield mask = 0;
483 switch (baseInternalFormat) {
484 case LOCAL_GL_LUMINANCE:
485 case LOCAL_GL_LUMINANCE_ALPHA:
486 case LOCAL_GL_ALPHA:
487 case LOCAL_GL_RGB:
488 case LOCAL_GL_RGBA:
489 case LOCAL_GL_BGR:
490 case LOCAL_GL_BGRA:
491 mask = LOCAL_GL_COLOR_BUFFER_BIT;
492 gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
493 texImageTarget, tex, level);
494 break;
496 case LOCAL_GL_DEPTH_COMPONENT:
497 mask = LOCAL_GL_DEPTH_BUFFER_BIT;
498 gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT,
499 texImageTarget, tex, level);
500 break;
502 case LOCAL_GL_DEPTH_STENCIL:
503 mask = LOCAL_GL_DEPTH_BUFFER_BIT |
504 LOCAL_GL_STENCIL_BUFFER_BIT;
505 gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT,
506 texImageTarget, tex, level);
507 gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT,
508 texImageTarget, tex, level);
509 break;
511 default:
512 return false;
514 MOZ_ASSERT(mask);
516 if (ClearByMask(context, mask))
517 return true;
519 // Failed to simply build an FB from the tex, but maybe it needs a
520 // color buffer to be complete.
522 if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
523 // Nope, it already had one.
524 return false;
527 gl::ScopedRenderbuffer rb(gl);
529 gl::ScopedBindRenderbuffer(gl, rb.RB());
530 gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER,
531 LOCAL_GL_RGBA4,
532 width, height);
535 gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
536 LOCAL_GL_RENDERBUFFER, rb.RB());
537 mask |= LOCAL_GL_COLOR_BUFFER_BIT;
539 // Last chance!
540 return ClearByMask(context, mask);
544 void
545 WebGLTexture::DoDeferredImageInitialization(GLenum imageTarget, GLint level)
547 const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level);
548 MOZ_ASSERT(imageInfo.mImageDataStatus == WebGLImageDataStatus::UninitializedImageData);
550 mContext->MakeContextCurrent();
552 // Try to clear with glCLear.
553 GLenum format = imageInfo.mWebGLFormat;
554 GLenum type = imageInfo.mWebGLType;
555 WebGLTexelFormat texelformat = GetWebGLTexelFormat(format, type);
557 bool cleared = ClearWithTempFB(mContext, GLName(),
558 imageTarget, level,
559 format, imageInfo.mHeight, imageInfo.mWidth);
560 if (cleared) {
561 SetImageDataStatus(imageTarget, level, WebGLImageDataStatus::InitializedImageData);
562 return;
565 // That didn't work. Try uploading zeros then.
566 gl::ScopedBindTexture autoBindTex(mContext->gl, GLName(), mTarget);
568 uint32_t texelsize = WebGLTexelConversions::TexelBytesForFormat(texelformat);
569 CheckedUint32 checked_byteLength
570 = WebGLContext::GetImageSize(
571 imageInfo.mHeight,
572 imageInfo.mWidth,
573 texelsize,
574 mContext->mPixelStoreUnpackAlignment);
575 MOZ_ASSERT(checked_byteLength.isValid()); // should have been checked earlier
577 UniquePtr<uint8_t> zeros((uint8_t*)moz_xcalloc(1, checked_byteLength.value())); // Infallible for now.
579 gl::GLContext* gl = mContext->gl;
580 GLenum driverType = DriverTypeFromType(gl, type);
581 GLenum driverInternalFormat = LOCAL_GL_NONE;
582 GLenum driverFormat = LOCAL_GL_NONE;
583 DriverFormatsFromFormatAndType(gl, format, type, &driverInternalFormat, &driverFormat);
585 mContext->GetAndFlushUnderlyingGLErrors();
586 gl->fTexImage2D(imageTarget, level, driverInternalFormat,
587 imageInfo.mWidth, imageInfo.mHeight,
588 0, driverFormat, driverType,
589 zeros.get());
590 GLenum error = mContext->GetAndFlushUnderlyingGLErrors();
591 if (error) {
592 // Should only be OUT_OF_MEMORY. Anyway, there's no good way to recover from this here.
593 printf_stderr("Error: 0x%4x\n", error);
594 MOZ_CRASH(); // errors on texture upload have been related to video memory exposure in the past.
597 SetImageDataStatus(imageTarget, level, WebGLImageDataStatus::InitializedImageData);
600 void
601 WebGLTexture::SetFakeBlackStatus(WebGLTextureFakeBlackStatus x)
603 mFakeBlackStatus = x;
604 mContext->SetFakeBlackStatus(WebGLContextFakeBlackStatus::Unknown);
607 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTexture)
609 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTexture, AddRef)
610 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLTexture, Release)