|
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/. */ |
|
5 |
|
6 #include "WebGLContext.h" |
|
7 #include "WebGLContextUtils.h" |
|
8 #include "WebGLTexture.h" |
|
9 #include "GLContext.h" |
|
10 #include "ScopedGLHelpers.h" |
|
11 #include "WebGLTexelConversions.h" |
|
12 #include "mozilla/dom/WebGLRenderingContextBinding.h" |
|
13 #include <algorithm> |
|
14 |
|
15 using namespace mozilla; |
|
16 |
|
17 JSObject* |
|
18 WebGLTexture::WrapObject(JSContext *cx) { |
|
19 return dom::WebGLTextureBinding::Wrap(cx, this); |
|
20 } |
|
21 |
|
22 WebGLTexture::WebGLTexture(WebGLContext *context) |
|
23 : WebGLContextBoundObject(context) |
|
24 , mHasEverBeenBound(false) |
|
25 , mTarget(0) |
|
26 , mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR) |
|
27 , mMagFilter(LOCAL_GL_LINEAR) |
|
28 , mWrapS(LOCAL_GL_REPEAT) |
|
29 , mWrapT(LOCAL_GL_REPEAT) |
|
30 , mFacesCount(0) |
|
31 , mMaxLevelWithCustomImages(0) |
|
32 , mHaveGeneratedMipmap(false) |
|
33 , mFakeBlackStatus(WebGLTextureFakeBlackStatus::IncompleteTexture) |
|
34 { |
|
35 SetIsDOMBinding(); |
|
36 mContext->MakeContextCurrent(); |
|
37 mContext->gl->fGenTextures(1, &mGLName); |
|
38 mContext->mTextures.insertBack(this); |
|
39 } |
|
40 |
|
41 void |
|
42 WebGLTexture::Delete() { |
|
43 mImageInfos.Clear(); |
|
44 mContext->MakeContextCurrent(); |
|
45 mContext->gl->fDeleteTextures(1, &mGLName); |
|
46 LinkedListElement<WebGLTexture>::removeFrom(mContext->mTextures); |
|
47 } |
|
48 |
|
49 int64_t |
|
50 WebGLTexture::ImageInfo::MemoryUsage() const { |
|
51 if (mImageDataStatus == WebGLImageDataStatus::NoImageData) |
|
52 return 0; |
|
53 int64_t bitsPerTexel = WebGLContext::GetBitsPerTexel(mWebGLFormat, mWebGLType); |
|
54 return int64_t(mWidth) * int64_t(mHeight) * bitsPerTexel/8; |
|
55 } |
|
56 |
|
57 int64_t |
|
58 WebGLTexture::MemoryUsage() const { |
|
59 if (IsDeleted()) |
|
60 return 0; |
|
61 int64_t result = 0; |
|
62 for(size_t face = 0; face < mFacesCount; face++) { |
|
63 if (mHaveGeneratedMipmap) { |
|
64 // Each mipmap level is 1/4 the size of the previous level |
|
65 // 1 + x + x^2 + ... = 1/(1-x) |
|
66 // for x = 1/4, we get 1/(1-1/4) = 4/3 |
|
67 result += ImageInfoAtFace(face, 0).MemoryUsage() * 4 / 3; |
|
68 } else { |
|
69 for(size_t level = 0; level <= mMaxLevelWithCustomImages; level++) |
|
70 result += ImageInfoAtFace(face, level).MemoryUsage(); |
|
71 } |
|
72 } |
|
73 return result; |
|
74 } |
|
75 |
|
76 bool |
|
77 WebGLTexture::DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(GLenum texImageTarget) const { |
|
78 if (mHaveGeneratedMipmap) |
|
79 return true; |
|
80 |
|
81 // We want a copy here so we can modify it temporarily. |
|
82 ImageInfo expected = ImageInfoAt(texImageTarget, 0); |
|
83 |
|
84 // checks if custom level>0 images are all defined up to the highest level defined |
|
85 // and have the expected dimensions |
|
86 for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) { |
|
87 const ImageInfo& actual = ImageInfoAt(texImageTarget, level); |
|
88 if (actual != expected) |
|
89 return false; |
|
90 expected.mWidth = std::max(1, expected.mWidth >> 1); |
|
91 expected.mHeight = std::max(1, expected.mHeight >> 1); |
|
92 |
|
93 // if the current level has size 1x1, we can stop here: the spec doesn't seem to forbid the existence |
|
94 // of extra useless levels. |
|
95 if (actual.mWidth == 1 && actual.mHeight == 1) |
|
96 return true; |
|
97 } |
|
98 |
|
99 // if we're here, we've exhausted all levels without finding a 1x1 image |
|
100 return false; |
|
101 } |
|
102 |
|
103 void |
|
104 WebGLTexture::Bind(GLenum aTarget) { |
|
105 // this function should only be called by bindTexture(). |
|
106 // it assumes that the GL context is already current. |
|
107 |
|
108 bool firstTimeThisTextureIsBound = !mHasEverBeenBound; |
|
109 |
|
110 if (!firstTimeThisTextureIsBound && aTarget != mTarget) { |
|
111 mContext->ErrorInvalidOperation("bindTexture: this texture has already been bound to a different target"); |
|
112 // very important to return here before modifying texture state! This was the place when I lost a whole day figuring |
|
113 // very strange 'invalid write' crashes. |
|
114 return; |
|
115 } |
|
116 |
|
117 mTarget = aTarget; |
|
118 |
|
119 mContext->gl->fBindTexture(mTarget, mGLName); |
|
120 |
|
121 if (firstTimeThisTextureIsBound) { |
|
122 mFacesCount = (mTarget == LOCAL_GL_TEXTURE_2D) ? 1 : 6; |
|
123 EnsureMaxLevelWithCustomImagesAtLeast(0); |
|
124 SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); |
|
125 |
|
126 // thanks to the WebKit people for finding this out: GL_TEXTURE_WRAP_R is not |
|
127 // present in GLES 2, but is present in GL and it seems as if for cube maps |
|
128 // we need to set it to GL_CLAMP_TO_EDGE to get the expected GLES behavior. |
|
129 if (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP && !mContext->gl->IsGLES()) |
|
130 mContext->gl->fTexParameteri(mTarget, LOCAL_GL_TEXTURE_WRAP_R, LOCAL_GL_CLAMP_TO_EDGE); |
|
131 } |
|
132 |
|
133 mHasEverBeenBound = true; |
|
134 } |
|
135 |
|
136 void |
|
137 WebGLTexture::SetImageInfo(GLenum aTarget, GLint aLevel, |
|
138 GLsizei aWidth, GLsizei aHeight, |
|
139 GLenum aFormat, GLenum aType, WebGLImageDataStatus aStatus) |
|
140 { |
|
141 if ( (aTarget == LOCAL_GL_TEXTURE_2D) != (mTarget == LOCAL_GL_TEXTURE_2D) ) |
|
142 return; |
|
143 |
|
144 EnsureMaxLevelWithCustomImagesAtLeast(aLevel); |
|
145 |
|
146 ImageInfoAt(aTarget, aLevel) = ImageInfo(aWidth, aHeight, aFormat, aType, aStatus); |
|
147 |
|
148 if (aLevel > 0) |
|
149 SetCustomMipmap(); |
|
150 |
|
151 // Invalidate framebuffer status cache |
|
152 NotifyFBsStatusChanged(); |
|
153 |
|
154 SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); |
|
155 } |
|
156 |
|
157 void |
|
158 WebGLTexture::SetGeneratedMipmap() { |
|
159 if (!mHaveGeneratedMipmap) { |
|
160 mHaveGeneratedMipmap = true; |
|
161 SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); |
|
162 } |
|
163 } |
|
164 |
|
165 void |
|
166 WebGLTexture::SetCustomMipmap() { |
|
167 if (mHaveGeneratedMipmap) { |
|
168 // if we were in GeneratedMipmap mode and are now switching to CustomMipmap mode, |
|
169 // we need to compute now all the mipmap image info. |
|
170 |
|
171 // since we were in GeneratedMipmap mode, we know that the level 0 images all have the same info, |
|
172 // and are power-of-two. |
|
173 ImageInfo imageInfo = ImageInfoAtFace(0, 0); |
|
174 NS_ASSERTION(imageInfo.IsPowerOfTwo(), "this texture is NPOT, so how could GenerateMipmap() ever accept it?"); |
|
175 |
|
176 GLsizei size = std::max(imageInfo.mWidth, imageInfo.mHeight); |
|
177 |
|
178 // so, the size is a power of two, let's find its log in base 2. |
|
179 size_t maxLevel = 0; |
|
180 for (GLsizei n = size; n > 1; n >>= 1) |
|
181 ++maxLevel; |
|
182 |
|
183 EnsureMaxLevelWithCustomImagesAtLeast(maxLevel); |
|
184 |
|
185 for (size_t level = 1; level <= maxLevel; ++level) { |
|
186 // again, since the sizes are powers of two, no need for any max(1,x) computation |
|
187 imageInfo.mWidth >>= 1; |
|
188 imageInfo.mHeight >>= 1; |
|
189 for(size_t face = 0; face < mFacesCount; ++face) |
|
190 ImageInfoAtFace(face, level) = imageInfo; |
|
191 } |
|
192 } |
|
193 mHaveGeneratedMipmap = false; |
|
194 } |
|
195 |
|
196 bool |
|
197 WebGLTexture::AreAllLevel0ImageInfosEqual() const { |
|
198 for (size_t face = 1; face < mFacesCount; ++face) { |
|
199 if (ImageInfoAtFace(face, 0) != ImageInfoAtFace(0, 0)) |
|
200 return false; |
|
201 } |
|
202 return true; |
|
203 } |
|
204 |
|
205 bool |
|
206 WebGLTexture::IsMipmapTexture2DComplete() const { |
|
207 if (mTarget != LOCAL_GL_TEXTURE_2D) |
|
208 return false; |
|
209 if (!ImageInfoAt(LOCAL_GL_TEXTURE_2D, 0).IsPositive()) |
|
210 return false; |
|
211 if (mHaveGeneratedMipmap) |
|
212 return true; |
|
213 return DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(LOCAL_GL_TEXTURE_2D); |
|
214 } |
|
215 |
|
216 bool |
|
217 WebGLTexture::IsCubeComplete() const { |
|
218 if (mTarget != LOCAL_GL_TEXTURE_CUBE_MAP) |
|
219 return false; |
|
220 const ImageInfo &first = ImageInfoAt(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0); |
|
221 if (!first.IsPositive() || !first.IsSquare()) |
|
222 return false; |
|
223 return AreAllLevel0ImageInfosEqual(); |
|
224 } |
|
225 |
|
226 static GLenum |
|
227 GLCubeMapFaceById(int id) |
|
228 { |
|
229 GLenum result = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + id; |
|
230 MOZ_ASSERT(result >= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X && |
|
231 result <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z); |
|
232 return result; |
|
233 } |
|
234 |
|
235 bool |
|
236 WebGLTexture::IsMipmapCubeComplete() const { |
|
237 if (!IsCubeComplete()) // in particular, this checks that this is a cube map |
|
238 return false; |
|
239 for (int i = 0; i < 6; i++) { |
|
240 GLenum face = GLCubeMapFaceById(i); |
|
241 if (!DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(face)) |
|
242 return false; |
|
243 } |
|
244 return true; |
|
245 } |
|
246 |
|
247 WebGLTextureFakeBlackStatus |
|
248 WebGLTexture::ResolvedFakeBlackStatus() { |
|
249 if (MOZ_LIKELY(mFakeBlackStatus != WebGLTextureFakeBlackStatus::Unknown)) { |
|
250 return mFakeBlackStatus; |
|
251 } |
|
252 |
|
253 // Determine if the texture needs to be faked as a black texture. |
|
254 // See 3.8.2 Shader Execution in the OpenGL ES 2.0.24 spec. |
|
255 |
|
256 for (size_t face = 0; face < mFacesCount; ++face) { |
|
257 if (ImageInfoAtFace(face, 0).mImageDataStatus == WebGLImageDataStatus::NoImageData) { |
|
258 // In case of undefined texture image, we don't print any message because this is a very common |
|
259 // and often legitimate case (asynchronous texture loading). |
|
260 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; |
|
261 return mFakeBlackStatus; |
|
262 } |
|
263 } |
|
264 |
|
265 const char *msg_rendering_as_black |
|
266 = "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, " |
|
267 "because it"; |
|
268 |
|
269 if (mTarget == LOCAL_GL_TEXTURE_2D) |
|
270 { |
|
271 if (DoesMinFilterRequireMipmap()) |
|
272 { |
|
273 if (!IsMipmapTexture2DComplete()) { |
|
274 mContext->GenerateWarning |
|
275 ("%s is a 2D texture, with a minification filter requiring a mipmap, " |
|
276 "and is not mipmap complete (as defined in section 3.7.10).", msg_rendering_as_black); |
|
277 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; |
|
278 } else if (!ImageInfoAt(mTarget, 0).IsPowerOfTwo()) { |
|
279 mContext->GenerateWarning |
|
280 ("%s is a 2D texture, with a minification filter requiring a mipmap, " |
|
281 "and either its width or height is not a power of two.", msg_rendering_as_black); |
|
282 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; |
|
283 } |
|
284 } |
|
285 else // no mipmap required |
|
286 { |
|
287 if (!ImageInfoAt(mTarget, 0).IsPositive()) { |
|
288 mContext->GenerateWarning |
|
289 ("%s is a 2D texture and its width or height is equal to zero.", |
|
290 msg_rendering_as_black); |
|
291 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; |
|
292 } else if (!AreBothWrapModesClampToEdge() && !ImageInfoAt(mTarget, 0).IsPowerOfTwo()) { |
|
293 mContext->GenerateWarning |
|
294 ("%s is a 2D texture, with a minification filter not requiring a mipmap, " |
|
295 "with its width or height not a power of two, and with a wrap mode " |
|
296 "different from CLAMP_TO_EDGE.", msg_rendering_as_black); |
|
297 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; |
|
298 } |
|
299 } |
|
300 } |
|
301 else // cube map |
|
302 { |
|
303 bool areAllLevel0ImagesPOT = true; |
|
304 for (size_t face = 0; face < mFacesCount; ++face) |
|
305 areAllLevel0ImagesPOT &= ImageInfoAtFace(face, 0).IsPowerOfTwo(); |
|
306 |
|
307 if (DoesMinFilterRequireMipmap()) |
|
308 { |
|
309 if (!IsMipmapCubeComplete()) { |
|
310 mContext->GenerateWarning("%s is a cube map texture, with a minification filter requiring a mipmap, " |
|
311 "and is not mipmap cube complete (as defined in section 3.7.10).", |
|
312 msg_rendering_as_black); |
|
313 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; |
|
314 } else if (!areAllLevel0ImagesPOT) { |
|
315 mContext->GenerateWarning("%s is a cube map texture, with a minification filter requiring a mipmap, " |
|
316 "and either the width or the height of some level 0 image is not a power of two.", |
|
317 msg_rendering_as_black); |
|
318 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; |
|
319 } |
|
320 } |
|
321 else // no mipmap required |
|
322 { |
|
323 if (!IsCubeComplete()) { |
|
324 mContext->GenerateWarning("%s is a cube map texture, with a minification filter not requiring a mipmap, " |
|
325 "and is not cube complete (as defined in section 3.7.10).", |
|
326 msg_rendering_as_black); |
|
327 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; |
|
328 } else if (!AreBothWrapModesClampToEdge() && !areAllLevel0ImagesPOT) { |
|
329 mContext->GenerateWarning("%s is a cube map texture, with a minification filter not requiring a mipmap, " |
|
330 "with some level 0 image having width or height not a power of two, and with a wrap mode " |
|
331 "different from CLAMP_TO_EDGE.", msg_rendering_as_black); |
|
332 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; |
|
333 } |
|
334 } |
|
335 } |
|
336 |
|
337 if (ImageInfoBase().mWebGLType == LOCAL_GL_FLOAT && |
|
338 !Context()->IsExtensionEnabled(WebGLExtensionID::OES_texture_float_linear)) |
|
339 { |
|
340 if (mMinFilter == LOCAL_GL_LINEAR || |
|
341 mMinFilter == LOCAL_GL_LINEAR_MIPMAP_LINEAR || |
|
342 mMinFilter == LOCAL_GL_LINEAR_MIPMAP_NEAREST || |
|
343 mMinFilter == LOCAL_GL_NEAREST_MIPMAP_LINEAR) |
|
344 { |
|
345 mContext->GenerateWarning("%s is a texture with a linear minification filter, " |
|
346 "which is not compatible with gl.FLOAT by default. " |
|
347 "Try enabling the OES_texture_float_linear extension if supported.", msg_rendering_as_black); |
|
348 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; |
|
349 } |
|
350 else if (mMagFilter == LOCAL_GL_LINEAR) |
|
351 { |
|
352 mContext->GenerateWarning("%s is a texture with a linear magnification filter, " |
|
353 "which is not compatible with gl.FLOAT by default. " |
|
354 "Try enabling the OES_texture_float_linear extension if supported.", msg_rendering_as_black); |
|
355 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; |
|
356 } |
|
357 } else if (ImageInfoBase().mWebGLType == LOCAL_GL_HALF_FLOAT_OES && |
|
358 !Context()->IsExtensionEnabled(WebGLExtensionID::OES_texture_half_float_linear)) |
|
359 { |
|
360 if (mMinFilter == LOCAL_GL_LINEAR || |
|
361 mMinFilter == LOCAL_GL_LINEAR_MIPMAP_LINEAR || |
|
362 mMinFilter == LOCAL_GL_LINEAR_MIPMAP_NEAREST || |
|
363 mMinFilter == LOCAL_GL_NEAREST_MIPMAP_LINEAR) |
|
364 { |
|
365 mContext->GenerateWarning("%s is a texture with a linear minification filter, " |
|
366 "which is not compatible with gl.HALF_FLOAT by default. " |
|
367 "Try enabling the OES_texture_half_float_linear extension if supported.", msg_rendering_as_black); |
|
368 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; |
|
369 } |
|
370 else if (mMagFilter == LOCAL_GL_LINEAR) |
|
371 { |
|
372 mContext->GenerateWarning("%s is a texture with a linear magnification filter, " |
|
373 "which is not compatible with gl.HALF_FLOAT by default. " |
|
374 "Try enabling the OES_texture_half_float_linear extension if supported.", msg_rendering_as_black); |
|
375 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; |
|
376 } |
|
377 } |
|
378 |
|
379 // We have exhausted all cases of incomplete textures, where we would need opaque black. |
|
380 // We may still need transparent black in case of uninitialized image data. |
|
381 bool hasUninitializedImageData = false; |
|
382 for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) { |
|
383 for (size_t face = 0; face < mFacesCount; ++face) { |
|
384 hasUninitializedImageData |= (ImageInfoAtFace(face, level).mImageDataStatus == WebGLImageDataStatus::UninitializedImageData); |
|
385 } |
|
386 } |
|
387 |
|
388 if (hasUninitializedImageData) { |
|
389 bool hasAnyInitializedImageData = false; |
|
390 for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) { |
|
391 for (size_t face = 0; face < mFacesCount; ++face) { |
|
392 if (ImageInfoAtFace(face, level).mImageDataStatus == WebGLImageDataStatus::InitializedImageData) { |
|
393 hasAnyInitializedImageData = true; |
|
394 break; |
|
395 } |
|
396 } |
|
397 if (hasAnyInitializedImageData) { |
|
398 break; |
|
399 } |
|
400 } |
|
401 |
|
402 if (hasAnyInitializedImageData) { |
|
403 // The texture contains some initialized image data, and some uninitialized image data. |
|
404 // In this case, we have no choice but to initialize all image data now. Fortunately, |
|
405 // in this case we know that we can't be dealing with a depth texture per WEBGL_depth_texture |
|
406 // and ANGLE_depth_texture (which allow only one image per texture) so we can assume that |
|
407 // glTexImage2D is able to upload data to images. |
|
408 for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) { |
|
409 for (size_t face = 0; face < mFacesCount; ++face) { |
|
410 GLenum imageTarget = mTarget == LOCAL_GL_TEXTURE_2D |
|
411 ? LOCAL_GL_TEXTURE_2D |
|
412 : LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + face; |
|
413 const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level); |
|
414 if (imageInfo.mImageDataStatus == WebGLImageDataStatus::UninitializedImageData) { |
|
415 DoDeferredImageInitialization(imageTarget, level); |
|
416 } |
|
417 } |
|
418 } |
|
419 mFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded; |
|
420 } else { |
|
421 // The texture only contains uninitialized image data. In this case, |
|
422 // we can use a black texture for it. |
|
423 mFakeBlackStatus = WebGLTextureFakeBlackStatus::UninitializedImageData; |
|
424 } |
|
425 } |
|
426 |
|
427 // we have exhausted all cases where we do need fakeblack, so if the status is still unknown, |
|
428 // that means that we do NOT need it. |
|
429 if (mFakeBlackStatus == WebGLTextureFakeBlackStatus::Unknown) { |
|
430 mFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded; |
|
431 } |
|
432 |
|
433 MOZ_ASSERT(mFakeBlackStatus != WebGLTextureFakeBlackStatus::Unknown); |
|
434 return mFakeBlackStatus; |
|
435 } |
|
436 |
|
437 void |
|
438 WebGLTexture::DoDeferredImageInitialization(GLenum imageTarget, GLint level) |
|
439 { |
|
440 const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level); |
|
441 MOZ_ASSERT(imageInfo.mImageDataStatus == WebGLImageDataStatus::UninitializedImageData); |
|
442 |
|
443 mContext->MakeContextCurrent(); |
|
444 gl::ScopedBindTexture autoBindTex(mContext->gl, GLName(), mTarget); |
|
445 |
|
446 GLenum format = imageInfo.mWebGLFormat; |
|
447 GLenum type = imageInfo.mWebGLType; |
|
448 WebGLTexelFormat texelformat = GetWebGLTexelFormat(format, type); |
|
449 uint32_t texelsize = WebGLTexelConversions::TexelBytesForFormat(texelformat); |
|
450 CheckedUint32 checked_byteLength |
|
451 = WebGLContext::GetImageSize( |
|
452 imageInfo.mHeight, |
|
453 imageInfo.mWidth, |
|
454 texelsize, |
|
455 mContext->mPixelStoreUnpackAlignment); |
|
456 MOZ_ASSERT(checked_byteLength.isValid()); // should have been checked earlier |
|
457 void *zeros = calloc(1, checked_byteLength.value()); |
|
458 |
|
459 gl::GLContext* gl = mContext->gl; |
|
460 GLenum driverType = DriverTypeFromType(gl, type); |
|
461 GLenum driverInternalFormat = LOCAL_GL_NONE; |
|
462 GLenum driverFormat = LOCAL_GL_NONE; |
|
463 DriverFormatsFromFormatAndType(gl, format, type, &driverInternalFormat, &driverFormat); |
|
464 |
|
465 mContext->GetAndFlushUnderlyingGLErrors(); |
|
466 gl->fTexImage2D(imageTarget, level, driverInternalFormat, |
|
467 imageInfo.mWidth, imageInfo.mHeight, |
|
468 0, driverFormat, driverType, |
|
469 zeros); |
|
470 GLenum error = mContext->GetAndFlushUnderlyingGLErrors(); |
|
471 |
|
472 free(zeros); |
|
473 SetImageDataStatus(imageTarget, level, WebGLImageDataStatus::InitializedImageData); |
|
474 |
|
475 if (error) { |
|
476 // Should only be OUT_OF_MEMORY. Anyway, there's no good way to recover from this here. |
|
477 MOZ_CRASH(); // errors on texture upload have been related to video memory exposure in the past. |
|
478 return; |
|
479 } |
|
480 } |
|
481 |
|
482 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTexture) |
|
483 |
|
484 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTexture, AddRef) |
|
485 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLTexture, Release) |