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 "WebGLTexture.h"
7 
8 #include <algorithm>
9 
10 #include "CanvasUtils.h"
11 #include "gfxPrefs.h"
12 #include "GLBlitHelper.h"
13 #include "GLContext.h"
14 #include "mozilla/gfx/2D.h"
15 #include "mozilla/dom/HTMLCanvasElement.h"
16 #include "mozilla/dom/HTMLVideoElement.h"
17 #include "mozilla/dom/ImageBitmap.h"
18 #include "mozilla/dom/ImageData.h"
19 #include "mozilla/MathAlgorithms.h"
20 #include "mozilla/Scoped.h"
21 #include "mozilla/Unused.h"
22 #include "ScopedGLHelpers.h"
23 #include "TexUnpackBlob.h"
24 #include "WebGLBuffer.h"
25 #include "WebGLContext.h"
26 #include "WebGLContextUtils.h"
27 #include "WebGLFramebuffer.h"
28 #include "WebGLTexelConversions.h"
29 
30 namespace mozilla {
31 
32 /* This file handles:
33  * TexStorage2D(texTarget, levels, internalFormat, width, height)
34  * TexStorage3D(texTarget, levels, intenralFormat, width, height, depth)
35  *
36  * TexImage2D(texImageTarget, level, internalFormat, width, height, border,
37  *            unpackFormat, unpackType, data)
38  * TexImage3D(texImageTarget, level, internalFormat, width, height, depth,
39  *            border, unpackFormat, unpackType, data)
40  * TexSubImage2D(texImageTarget, level, xOffset, yOffset, width, height,
41  *               unpackFormat, unpackType, data)
42  * TexSubImage3D(texImageTarget, level, xOffset, yOffset, zOffset, width,
43  *               height, depth, unpackFormat, unpackType, data)
44  *
45  * CompressedTexImage2D(texImageTarget, level, internalFormat, width, height,
46  *                      border, imageSize, data)
47  * CompressedTexImage3D(texImageTarget, level, internalFormat, width, height,
48  *                      depth, border, imageSize, data)
49  * CompressedTexSubImage2D(texImageTarget, level, xOffset, yOffset, width,
50  *                         height, sizedUnpackFormat, imageSize, data)
51  * CompressedTexSubImage3D(texImageTarget, level, xOffset, yOffset, zOffset,
52  *                         width, height, depth, sizedUnpackFormat, imageSize,
53  *                         data)
54  *
55  * CopyTexImage2D(texImageTarget, level, internalFormat, x, y, width, height,
56  *                border)
57  * CopyTexImage3D - "Because the framebuffer is inhererntly two-dimensional,
58  *                   there is no CopyTexImage3D command."
59  * CopyTexSubImage2D(texImageTarget, level, xOffset, yOffset, x, y, width,
60  *                   height)
61  * CopyTexSubImage3D(texImageTarget, level, xOffset, yOffset, zOffset, x, y,
62  *                   width, height)
63  */
64 
ValidateExtents(WebGLContext * webgl,const char * funcName,GLsizei width,GLsizei height,GLsizei depth,GLint border,uint32_t * const out_width,uint32_t * const out_height,uint32_t * const out_depth)65 static bool ValidateExtents(WebGLContext* webgl, const char* funcName,
66                             GLsizei width, GLsizei height, GLsizei depth,
67                             GLint border, uint32_t* const out_width,
68                             uint32_t* const out_height,
69                             uint32_t* const out_depth) {
70   // Check border
71   if (border != 0) {
72     webgl->ErrorInvalidValue("%s: `border` must be 0.", funcName);
73     return false;
74   }
75 
76   if (width < 0 || height < 0 || depth < 0) {
77     /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification
78      *   "If wt and ht are the specified image width and height,
79      *   and if either wt or ht are less than zero, then the error
80      *   INVALID_VALUE is generated."
81      */
82     webgl->ErrorInvalidValue("%s: `width`/`height`/`depth` must be >= 0.",
83                              funcName);
84     return false;
85   }
86 
87   *out_width = width;
88   *out_height = height;
89   *out_depth = depth;
90   return true;
91 }
92 
93 ////////////////////////////////////////
94 // ArrayBufferView?
95 
DoesJSTypeMatchUnpackType(GLenum unpackType,js::Scalar::Type jsType)96 static inline bool DoesJSTypeMatchUnpackType(GLenum unpackType,
97                                              js::Scalar::Type jsType) {
98   switch (unpackType) {
99     case LOCAL_GL_BYTE:
100       return jsType == js::Scalar::Type::Int8;
101 
102     case LOCAL_GL_UNSIGNED_BYTE:
103       return jsType == js::Scalar::Type::Uint8 ||
104              jsType == js::Scalar::Type::Uint8Clamped;
105 
106     case LOCAL_GL_SHORT:
107       return jsType == js::Scalar::Type::Int16;
108 
109     case LOCAL_GL_UNSIGNED_SHORT:
110     case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
111     case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
112     case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
113     case LOCAL_GL_HALF_FLOAT:
114     case LOCAL_GL_HALF_FLOAT_OES:
115       return jsType == js::Scalar::Type::Uint16;
116 
117     case LOCAL_GL_INT:
118       return jsType == js::Scalar::Type::Int32;
119 
120     case LOCAL_GL_UNSIGNED_INT:
121     case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
122     case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
123     case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV:
124     case LOCAL_GL_UNSIGNED_INT_24_8:
125       return jsType == js::Scalar::Type::Uint32;
126 
127     case LOCAL_GL_FLOAT:
128       return jsType == js::Scalar::Type::Float32;
129 
130     default:
131       return false;
132   }
133 }
134 
ValidateViewType(WebGLContext * webgl,const char * funcName,GLenum unpackType,const TexImageSource & src)135 static bool ValidateViewType(WebGLContext* webgl, const char* funcName,
136                              GLenum unpackType, const TexImageSource& src) {
137   if (!src.mView) return true;
138   const auto& view = *(src.mView);
139 
140   const auto& jsType = view.Type();
141   if (!DoesJSTypeMatchUnpackType(unpackType, jsType)) {
142     webgl->ErrorInvalidOperation(
143         "%s: ArrayBufferView type not compatible with"
144         " `type`.",
145         funcName);
146     return false;
147   }
148 
149   return true;
150 }
151 
ValidateUnpackInfo(WebGLContext * webgl,const char * funcName,const webgl::PackingInfo & pi)152 static bool ValidateUnpackInfo(WebGLContext* webgl, const char* funcName,
153                                const webgl::PackingInfo& pi) {
154   if (!webgl->mFormatUsage->AreUnpackEnumsValid(pi.format, pi.type)) {
155     webgl->ErrorInvalidEnum("%s: Invalid unpack format/type: 0x%04x/0x%04x",
156                             funcName, pi.format, pi.type);
157     return false;
158   }
159 
160   return true;
161 }
162 
163 ////////////////////////////////////////////////////////////////////////////////
164 
FromView(WebGLContext * webgl,const char * funcName,TexImageTarget target,uint32_t width,uint32_t height,uint32_t depth,const dom::ArrayBufferView * view,GLuint viewElemOffset,GLuint viewElemLengthOverride)165 static UniquePtr<webgl::TexUnpackBytes> FromView(
166     WebGLContext* webgl, const char* funcName, TexImageTarget target,
167     uint32_t width, uint32_t height, uint32_t depth,
168     const dom::ArrayBufferView* view, GLuint viewElemOffset,
169     GLuint viewElemLengthOverride) {
170   const bool isClientData = true;
171   const uint8_t* bytes = nullptr;
172   size_t availByteCount = 0;
173   if (view) {
174     if (!webgl->ValidateArrayBufferView(
175             funcName, *view, viewElemOffset, viewElemLengthOverride,
176             const_cast<uint8_t**>(&bytes), &availByteCount)) {
177       return nullptr;
178     }
179   }
180   return MakeUnique<webgl::TexUnpackBytes>(webgl, target, width, height, depth,
181                                            isClientData, bytes, availByteCount);
182 }
183 
FromPboOffset(WebGLContext * webgl,const char * funcName,TexImageTarget target,uint32_t width,uint32_t height,uint32_t depth,WebGLsizeiptr pboOffset,const Maybe<GLsizei> & expectedImageSize)184 static UniquePtr<webgl::TexUnpackBytes> FromPboOffset(
185     WebGLContext* webgl, const char* funcName, TexImageTarget target,
186     uint32_t width, uint32_t height, uint32_t depth, WebGLsizeiptr pboOffset,
187     const Maybe<GLsizei>& expectedImageSize) {
188   if (pboOffset < 0) {
189     webgl->ErrorInvalidValue("%s: offset cannot be negative.", funcName);
190     return nullptr;
191   }
192 
193   const auto& buffer =
194       webgl->ValidateBufferSelection(funcName, LOCAL_GL_PIXEL_UNPACK_BUFFER);
195   if (!buffer) return nullptr;
196 
197   size_t availBufferBytes = buffer->ByteLength();
198   if (size_t(pboOffset) > availBufferBytes) {
199     webgl->ErrorInvalidOperation("%s: Offset is passed end of buffer.",
200                                  funcName);
201     return nullptr;
202   }
203   availBufferBytes -= pboOffset;
204   if (expectedImageSize.isSome()) {
205     if (expectedImageSize.ref() < 0) {
206       webgl->ErrorInvalidValue("%s: ImageSize can't be less than 0.", funcName);
207       return nullptr;
208     }
209     if (size_t(expectedImageSize.ref()) != availBufferBytes) {
210       webgl->ErrorInvalidOperation(
211           "%s: ImageSize doesn't match the required upload byte size.",
212           funcName);
213       return nullptr;
214     }
215     availBufferBytes = size_t(expectedImageSize.ref());
216   }
217   const bool isClientData = false;
218   const auto ptr = (const uint8_t*)pboOffset;
219   return MakeUnique<webgl::TexUnpackBytes>(webgl, target, width, height, depth,
220                                            isClientData, ptr, availBufferBytes);
221 }
222 
FromImageBitmap(WebGLContext * webgl,const char * funcName,TexImageTarget target,uint32_t width,uint32_t height,uint32_t depth,const dom::ImageBitmap & imageBitmap,ErrorResult * aRv)223 static UniquePtr<webgl::TexUnpackBlob> FromImageBitmap(
224     WebGLContext* webgl, const char* funcName, TexImageTarget target,
225     uint32_t width, uint32_t height, uint32_t depth,
226     const dom::ImageBitmap& imageBitmap, ErrorResult* aRv) {
227     if (imageBitmap.IsWriteOnly()) {
228         aRv->Throw(NS_ERROR_DOM_SECURITY_ERR);
229         return nullptr;
230     }
231 
232   UniquePtr<dom::ImageBitmapCloneData> cloneData =
233       Move(imageBitmap.ToCloneData());
234   const RefPtr<gfx::DataSourceSurface> surf = cloneData->mSurface;
235 
236   if (!width) {
237     width = surf->GetSize().width;
238   }
239 
240   if (!height) {
241     height = surf->GetSize().height;
242   }
243 
244   // WhatWG "HTML Living Standard" (30 October 2015):
245   // "The getImageData(sx, sy, sw, sh) method [...] Pixels must be returned as
246   // non-premultiplied alpha values."
247   return MakeUnique<webgl::TexUnpackSurface>(
248       webgl, target, width, height, depth, surf, cloneData->mAlphaType);
249 }
250 
FromImageData(WebGLContext * webgl,const char * funcName,TexImageTarget target,uint32_t width,uint32_t height,uint32_t depth,const dom::ImageData & imageData,dom::Uint8ClampedArray * scopedArr)251 static UniquePtr<webgl::TexUnpackBlob> FromImageData(
252     WebGLContext* webgl, const char* funcName, TexImageTarget target,
253     uint32_t width, uint32_t height, uint32_t depth,
254     const dom::ImageData& imageData, dom::Uint8ClampedArray* scopedArr) {
255   DebugOnly<bool> inited = scopedArr->Init(imageData.GetDataObject());
256   MOZ_ASSERT(inited);
257 
258   scopedArr->ComputeLengthAndData();
259   const DebugOnly<size_t> dataSize = scopedArr->Length();
260   const void* const data = scopedArr->Data();
261 
262   const gfx::IntSize size(imageData.Width(), imageData.Height());
263   const size_t stride = size.width * 4;
264   const gfx::SurfaceFormat surfFormat = gfx::SurfaceFormat::R8G8B8A8;
265 
266   // WhatWG "HTML Living Standard" (30 October 2015):
267   // "The getImageData(sx, sy, sw, sh) method [...] Pixels must be returned as
268   // non-premultiplied alpha values."
269   const auto alphaType = gfxAlphaType::NonPremult;
270 
271   MOZ_ASSERT(dataSize == stride * size.height);
272 
273   uint8_t* wrappableData = (uint8_t*)data;
274 
275   const RefPtr<gfx::DataSourceSurface> surf =
276       gfx::Factory::CreateWrappingDataSourceSurface(wrappableData, stride, size,
277                                                     surfFormat);
278   if (!surf) {
279     webgl->ErrorOutOfMemory("%s: OOM in FromImageData.", funcName);
280     return nullptr;
281   }
282 
283   ////
284 
285   if (!width) {
286     width = imageData.Width();
287   }
288 
289   if (!height) {
290     height = imageData.Height();
291   }
292 
293   ////
294 
295   return MakeUnique<webgl::TexUnpackSurface>(webgl, target, width, height,
296                                              depth, surf, alphaType);
297 }
298 
FromDomElem(const char * funcName,TexImageTarget target,uint32_t width,uint32_t height,uint32_t depth,const dom::Element & elem,ErrorResult * const out_error)299 UniquePtr<webgl::TexUnpackBlob> WebGLContext::FromDomElem(
300     const char* funcName, TexImageTarget target, uint32_t width,
301     uint32_t height, uint32_t depth, const dom::Element& elem,
302     ErrorResult* const out_error) {
303     if (elem.IsHTMLElement(nsGkAtoms::canvas)) {
304         const dom::HTMLCanvasElement* canvas = static_cast<const dom::HTMLCanvasElement*>(&elem);
305         if (canvas->IsWriteOnly()) {
306             out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
307             return nullptr;
308         }
309     }
310 
311   // The canvas spec says that drawImage should draw the first frame of
312   // animated images. The webgl spec doesn't mention the issue, so we do the
313   // same as drawImage.
314   uint32_t flags = nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE |
315                    nsLayoutUtils::SFE_WANT_IMAGE_SURFACE |
316                    nsLayoutUtils::SFE_USE_ELEMENT_SIZE_IF_VECTOR;
317 
318   if (mPixelStore_ColorspaceConversion == LOCAL_GL_NONE)
319     flags |= nsLayoutUtils::SFE_NO_COLORSPACE_CONVERSION;
320 
321   if (!mPixelStore_PremultiplyAlpha)
322     flags |= nsLayoutUtils::SFE_PREFER_NO_PREMULTIPLY_ALPHA;
323 
324   RefPtr<gfx::DrawTarget> idealDrawTarget = nullptr;  // Don't care for now.
325   auto sfer = nsLayoutUtils::SurfaceFromElement(
326       const_cast<dom::Element*>(&elem), flags, idealDrawTarget);
327 
328   //////
329 
330   uint32_t elemWidth = 0;
331   uint32_t elemHeight = 0;
332   layers::Image* layersImage = nullptr;
333   if (!gfxPrefs::WebGLDisableDOMBlitUploads() && sfer.mLayersImage) {
334     layersImage = sfer.mLayersImage;
335     elemWidth = layersImage->GetSize().width;
336     elemHeight = layersImage->GetSize().height;
337   }
338 
339   RefPtr<gfx::DataSourceSurface> dataSurf;
340   if (!layersImage && sfer.GetSourceSurface()) {
341     const auto surf = sfer.GetSourceSurface();
342     elemWidth = surf->GetSize().width;
343     elemHeight = surf->GetSize().height;
344 
345     // WARNING: OSX can lose our MakeCurrent here.
346     dataSurf = surf->GetDataSurface();
347   }
348 
349   //////
350 
351   if (!width) {
352     width = elemWidth;
353   }
354 
355   if (!height) {
356     height = elemHeight;
357   }
358 
359   ////
360 
361   if (!layersImage && !dataSurf) {
362     const bool isClientData = true;
363     return MakeUnique<webgl::TexUnpackBytes>(this, target, width, height, depth,
364                                              isClientData, nullptr, 0);
365   }
366 
367   //////
368 
369   // While it's counter-intuitive, the shape of the SFEResult API means that we
370   // should try to pull out a surface first, and then, if we do pull out a
371   // surface, check CORS/write-only/etc..
372 
373   if (!sfer.mCORSUsed) {
374     auto& srcPrincipal = sfer.mPrincipal;
375     nsIPrincipal* dstPrincipal = GetCanvas()->NodePrincipal();
376 
377     if (!dstPrincipal->Subsumes(srcPrincipal)) {
378       GenerateWarning("%s: Cross-origin elements require CORS.", funcName);
379       out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
380       return nullptr;
381     }
382   }
383 
384   if (sfer.mIsWriteOnly) {
385     // mIsWriteOnly defaults to true, and so will be true even if SFE merely
386     // failed. Thus we must test mIsWriteOnly after successfully retrieving an
387     // Image or SourceSurface.
388     GenerateWarning("%s: Element is write-only, thus cannot be uploaded.",
389                     funcName);
390     out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
391     return nullptr;
392   }
393 
394   //////
395   // Ok, we're good!
396 
397   if (layersImage) {
398     return MakeUnique<webgl::TexUnpackImage>(this, target, width, height, depth,
399                                              layersImage, sfer.mAlphaType);
400   }
401 
402   MOZ_ASSERT(dataSurf);
403   return MakeUnique<webgl::TexUnpackSurface>(this, target, width, height, depth,
404                                              dataSurf, sfer.mAlphaType);
405 }
406 
407 ////////////////////////////////////////
408 
From(const char * funcName,TexImageTarget target,GLsizei rawWidth,GLsizei rawHeight,GLsizei rawDepth,GLint border,const TexImageSource & src,dom::Uint8ClampedArray * const scopedArr)409 UniquePtr<webgl::TexUnpackBlob> WebGLContext::From(
410     const char* funcName, TexImageTarget target, GLsizei rawWidth,
411     GLsizei rawHeight, GLsizei rawDepth, GLint border,
412     const TexImageSource& src, dom::Uint8ClampedArray* const scopedArr) {
413   uint32_t width, height, depth;
414   if (!ValidateExtents(this, funcName, rawWidth, rawHeight, rawDepth, border,
415                        &width, &height, &depth)) {
416     return nullptr;
417   }
418 
419   if (src.mPboOffset) {
420     return FromPboOffset(this, funcName, target, width, height, depth,
421                          *(src.mPboOffset), Nothing());
422   }
423 
424   if (mBoundPixelUnpackBuffer) {
425     ErrorInvalidOperation("%s: PIXEL_UNPACK_BUFFER must be null.", funcName);
426     return nullptr;
427   }
428 
429   if (src.mImageBitmap) {
430     return FromImageBitmap(this, funcName, target, width, height, depth,
431                            *(src.mImageBitmap), src.mOut_error);
432   }
433 
434   if (src.mImageData) {
435     return FromImageData(this, funcName, target, width, height, depth,
436                          *(src.mImageData), scopedArr);
437   }
438 
439   if (src.mDomElem) {
440     return FromDomElem(funcName, target, width, height, depth, *(src.mDomElem),
441                        src.mOut_error);
442   }
443 
444   return FromView(this, funcName, target, width, height, depth, src.mView,
445                   src.mViewElemOffset, src.mViewElemLengthOverride);
446 }
447 
448 ////////////////////////////////////////////////////////////////////////////////
449 
ValidateTexOrSubImage(WebGLContext * webgl,const char * funcName,TexImageTarget target,GLsizei rawWidth,GLsizei rawHeight,GLsizei rawDepth,GLint border,const webgl::PackingInfo & pi,const TexImageSource & src,dom::Uint8ClampedArray * const scopedArr)450 static UniquePtr<webgl::TexUnpackBlob> ValidateTexOrSubImage(
451     WebGLContext* webgl, const char* funcName, TexImageTarget target,
452     GLsizei rawWidth, GLsizei rawHeight, GLsizei rawDepth, GLint border,
453     const webgl::PackingInfo& pi, const TexImageSource& src,
454     dom::Uint8ClampedArray* const scopedArr) {
455   if (!ValidateUnpackInfo(webgl, funcName, pi)) return nullptr;
456 
457   if (!ValidateViewType(webgl, funcName, pi.type, src)) return nullptr;
458 
459   auto blob = webgl->From(funcName, target, rawWidth, rawHeight, rawDepth,
460                           border, src, scopedArr);
461   if (!blob || !blob->Validate(webgl, funcName, pi)) return nullptr;
462 
463   return Move(blob);
464 }
465 
TexImage(const char * funcName,TexImageTarget target,GLint level,GLenum internalFormat,GLsizei width,GLsizei height,GLsizei depth,GLint border,const webgl::PackingInfo & pi,const TexImageSource & src)466 void WebGLTexture::TexImage(const char* funcName, TexImageTarget target,
467                             GLint level, GLenum internalFormat, GLsizei width,
468                             GLsizei height, GLsizei depth, GLint border,
469                             const webgl::PackingInfo& pi,
470                             const TexImageSource& src) {
471   dom::RootedSpiderMonkeyInterface<dom::Uint8ClampedArray> scopedArr(
472       dom::RootingCx());
473   const auto blob =
474       ValidateTexOrSubImage(mContext, funcName, target, width, height, depth,
475                             border, pi, src, &scopedArr);
476   if (!blob) return;
477 
478   TexImage(funcName, target, level, internalFormat, pi, blob.get());
479 }
480 
TexSubImage(const char * funcName,TexImageTarget target,GLint level,GLint xOffset,GLint yOffset,GLint zOffset,GLsizei width,GLsizei height,GLsizei depth,const webgl::PackingInfo & pi,const TexImageSource & src)481 void WebGLTexture::TexSubImage(const char* funcName, TexImageTarget target,
482                                GLint level, GLint xOffset, GLint yOffset,
483                                GLint zOffset, GLsizei width, GLsizei height,
484                                GLsizei depth, const webgl::PackingInfo& pi,
485                                const TexImageSource& src) {
486   const GLint border = 0;
487   dom::RootedSpiderMonkeyInterface<dom::Uint8ClampedArray> scopedArr(
488       dom::RootingCx());
489   const auto blob =
490       ValidateTexOrSubImage(mContext, funcName, target, width, height, depth,
491                             border, pi, src, &scopedArr);
492   if (!blob) return;
493 
494   if (!blob->HasData()) {
495     mContext->ErrorInvalidValue("%s: Source must not be null.", funcName);
496     return;
497   }
498 
499   TexSubImage(funcName, target, level, xOffset, yOffset, zOffset, pi,
500               blob.get());
501 }
502 
503 //////////////////////////////////////////////////////////////////////////////////////////
504 //////////////////////////////////////////////////////////////////////////////////////////
505 
ValidateTexImage(WebGLContext * webgl,WebGLTexture * texture,const char * funcName,TexImageTarget target,GLint level,WebGLTexture::ImageInfo ** const out_imageInfo)506 static bool ValidateTexImage(WebGLContext* webgl, WebGLTexture* texture,
507                              const char* funcName, TexImageTarget target,
508                              GLint level,
509                              WebGLTexture::ImageInfo** const out_imageInfo) {
510   // Check level
511   if (level < 0) {
512     webgl->ErrorInvalidValue("%s: `level` must be >= 0.", funcName);
513     return false;
514   }
515 
516   if (level >= WebGLTexture::kMaxLevelCount) {
517     webgl->ErrorInvalidValue("%s: `level` is too large.", funcName);
518     return false;
519   }
520 
521   WebGLTexture::ImageInfo& imageInfo = texture->ImageInfoAt(target, level);
522 
523   *out_imageInfo = &imageInfo;
524   return true;
525 }
526 
527 // For *TexImage*
ValidateTexImageSpecification(const char * funcName,TexImageTarget target,GLint rawLevel,uint32_t width,uint32_t height,uint32_t depth,WebGLTexture::ImageInfo ** const out_imageInfo)528 bool WebGLTexture::ValidateTexImageSpecification(
529     const char* funcName, TexImageTarget target, GLint rawLevel, uint32_t width,
530     uint32_t height, uint32_t depth,
531     WebGLTexture::ImageInfo** const out_imageInfo) {
532   if (mImmutable) {
533     mContext->ErrorInvalidOperation("%s: Specified texture is immutable.",
534                                     funcName);
535     return false;
536   }
537 
538   // Do this early to validate `level`.
539   WebGLTexture::ImageInfo* imageInfo;
540   if (!ValidateTexImage(mContext, this, funcName, target, rawLevel, &imageInfo))
541     return false;
542   const uint32_t level(rawLevel);
543 
544   if (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP && width != height) {
545     mContext->ErrorInvalidValue("%s: Cube map images must be square.",
546                                 funcName);
547     return false;
548   }
549 
550   /* GLES 3.0.4, p133-134:
551    * GL_MAX_TEXTURE_SIZE is *not* the max allowed texture size. Rather, it is
552    * the max (width/height) size guaranteed not to generate an INVALID_VALUE for
553    * too-large dimensions. Sizes larger than GL_MAX_TEXTURE_SIZE *may or may
554    * not* result in an INVALID_VALUE, or possibly GL_OOM.
555    *
556    * However, we have needed to set our maximums lower in the past to prevent
557    * resource corruption. Therefore we have mGLMaxTextureSize, which is neither
558    * necessarily lower nor higher than MAX_TEXTURE_SIZE.
559    *
560    * Note that mGLMaxTextureSize must be >= than the advertized
561    * MAX_TEXTURE_SIZE. For simplicity, we advertize MAX_TEXTURE_SIZE as
562    * mGLMaxTextureSize.
563    */
564 
565   uint32_t maxWidthHeight = 0;
566   uint32_t maxDepth = 0;
567   uint32_t maxLevel = 0;
568 
569   MOZ_ASSERT(level <= 31);
570   switch (target.get()) {
571     case LOCAL_GL_TEXTURE_2D:
572       maxWidthHeight = mContext->mGLMaxTextureSize >> level;
573       maxDepth = 1;
574       maxLevel = CeilingLog2(mContext->mGLMaxTextureSize);
575       break;
576 
577     case LOCAL_GL_TEXTURE_3D:
578       maxWidthHeight = mContext->mGLMax3DTextureSize >> level;
579       maxDepth = maxWidthHeight;
580       maxLevel = CeilingLog2(mContext->mGLMax3DTextureSize);
581       break;
582 
583     case LOCAL_GL_TEXTURE_2D_ARRAY:
584       maxWidthHeight = mContext->mGLMaxTextureSize >> level;
585       // "The maximum number of layers for two-dimensional array textures
586       // (depth) must be at least MAX_ARRAY_TEXTURE_LAYERS for all levels."
587       maxDepth = mContext->mGLMaxArrayTextureLayers;
588       maxLevel = CeilingLog2(mContext->mGLMaxTextureSize);
589       break;
590 
591     default:  // cube maps
592       MOZ_ASSERT(IsCubeMap());
593       maxWidthHeight = mContext->mGLMaxCubeMapTextureSize >> level;
594       maxDepth = 1;
595       maxLevel = CeilingLog2(mContext->mGLMaxCubeMapTextureSize);
596       break;
597   }
598 
599   if (level > maxLevel) {
600     mContext->ErrorInvalidValue(
601         "%s: Requested level is not supported for target.", funcName);
602     return false;
603   }
604 
605   if (width > maxWidthHeight || height > maxWidthHeight || depth > maxDepth) {
606     mContext->ErrorInvalidValue(
607         "%s: Requested size at this level is unsupported.", funcName);
608     return false;
609   }
610 
611   {
612     /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification
613      *   "If level is greater than zero, and either width or
614      *   height is not a power-of-two, the error INVALID_VALUE is
615      *   generated."
616      *
617      * This restriction does not apply to GL ES Version 3.0+.
618      */
619     bool requirePOT = (!mContext->IsWebGL2() && level != 0);
620 
621     if (requirePOT) {
622       if (!IsPowerOfTwo(width) || !IsPowerOfTwo(height)) {
623         mContext->ErrorInvalidValue(
624             "%s: For level > 0, width and height must be"
625             " powers of two.",
626             funcName);
627         return false;
628       }
629     }
630   }
631 
632   *out_imageInfo = imageInfo;
633   return true;
634 }
635 
636 // For *TexSubImage*
ValidateTexImageSelection(const char * funcName,TexImageTarget target,GLint level,GLint xOffset,GLint yOffset,GLint zOffset,uint32_t width,uint32_t height,uint32_t depth,WebGLTexture::ImageInfo ** const out_imageInfo)637 bool WebGLTexture::ValidateTexImageSelection(
638     const char* funcName, TexImageTarget target, GLint level, GLint xOffset,
639     GLint yOffset, GLint zOffset, uint32_t width, uint32_t height,
640     uint32_t depth, WebGLTexture::ImageInfo** const out_imageInfo) {
641   // The conformance test wants bad arg checks before imageInfo checks.
642   if (xOffset < 0 || yOffset < 0 || zOffset < 0) {
643     mContext->ErrorInvalidValue("%s: Offsets must be >=0.", funcName);
644     return false;
645   }
646 
647   WebGLTexture::ImageInfo* imageInfo;
648   if (!ValidateTexImage(mContext, this, funcName, target, level, &imageInfo))
649     return false;
650 
651   if (!imageInfo->IsDefined()) {
652     mContext->ErrorInvalidOperation(
653         "%s: The specified TexImage has not yet been"
654         " specified.",
655         funcName);
656     return false;
657   }
658 
659   const auto totalX = CheckedUint32(xOffset) + width;
660   const auto totalY = CheckedUint32(yOffset) + height;
661   const auto totalZ = CheckedUint32(zOffset) + depth;
662 
663   if (!totalX.isValid() || totalX.value() > imageInfo->mWidth ||
664       !totalY.isValid() || totalY.value() > imageInfo->mHeight ||
665       !totalZ.isValid() || totalZ.value() > imageInfo->mDepth) {
666     mContext->ErrorInvalidValue(
667         "%s: Offset+size must be <= the size of the existing"
668         " specified image.",
669         funcName);
670     return false;
671   }
672 
673   *out_imageInfo = imageInfo;
674   return true;
675 }
676 
ValidateCompressedTexUnpack(WebGLContext * webgl,const char * funcName,GLsizei width,GLsizei height,GLsizei depth,const webgl::FormatInfo * format,size_t dataSize)677 static bool ValidateCompressedTexUnpack(WebGLContext* webgl,
678                                         const char* funcName, GLsizei width,
679                                         GLsizei height, GLsizei depth,
680                                         const webgl::FormatInfo* format,
681                                         size_t dataSize) {
682   auto compression = format->compression;
683 
684   auto bytesPerBlock = compression->bytesPerBlock;
685   auto blockWidth = compression->blockWidth;
686   auto blockHeight = compression->blockHeight;
687 
688   auto widthInBlocks = CheckedUint32(width) / blockWidth;
689   auto heightInBlocks = CheckedUint32(height) / blockHeight;
690   if (width % blockWidth) widthInBlocks += 1;
691   if (height % blockHeight) heightInBlocks += 1;
692 
693   const CheckedUint32 blocksPerImage = widthInBlocks * heightInBlocks;
694   const CheckedUint32 bytesPerImage = bytesPerBlock * blocksPerImage;
695   const CheckedUint32 bytesNeeded = bytesPerImage * depth;
696 
697   if (!bytesNeeded.isValid()) {
698     webgl->ErrorOutOfMemory(
699         "%s: Overflow while computing the needed buffer size.", funcName);
700     return false;
701   }
702 
703   if (dataSize != bytesNeeded.value()) {
704     webgl->ErrorInvalidValue(
705         "%s: Provided buffer's size must match expected size."
706         " (needs %u, has %zu)",
707         funcName, bytesNeeded.value(), dataSize);
708     return false;
709   }
710 
711   return true;
712 }
713 
DoChannelsMatchForCopyTexImage(const webgl::FormatInfo * srcFormat,const webgl::FormatInfo * dstFormat)714 static bool DoChannelsMatchForCopyTexImage(const webgl::FormatInfo* srcFormat,
715                                            const webgl::FormatInfo* dstFormat) {
716   // GLES 3.0.4 p140 Table 3.16 "Valid CopyTexImage source
717   // framebuffer/destination texture base internal format combinations."
718 
719   switch (srcFormat->unsizedFormat) {
720     case webgl::UnsizedFormat::RGBA:
721       switch (dstFormat->unsizedFormat) {
722         case webgl::UnsizedFormat::A:
723         case webgl::UnsizedFormat::L:
724         case webgl::UnsizedFormat::LA:
725         case webgl::UnsizedFormat::R:
726         case webgl::UnsizedFormat::RG:
727         case webgl::UnsizedFormat::RGB:
728         case webgl::UnsizedFormat::RGBA:
729           return true;
730         default:
731           return false;
732       }
733 
734     case webgl::UnsizedFormat::RGB:
735       switch (dstFormat->unsizedFormat) {
736         case webgl::UnsizedFormat::L:
737         case webgl::UnsizedFormat::R:
738         case webgl::UnsizedFormat::RG:
739         case webgl::UnsizedFormat::RGB:
740           return true;
741         default:
742           return false;
743       }
744 
745     case webgl::UnsizedFormat::RG:
746       switch (dstFormat->unsizedFormat) {
747         case webgl::UnsizedFormat::L:
748         case webgl::UnsizedFormat::R:
749         case webgl::UnsizedFormat::RG:
750           return true;
751         default:
752           return false;
753       }
754 
755     case webgl::UnsizedFormat::R:
756       switch (dstFormat->unsizedFormat) {
757         case webgl::UnsizedFormat::L:
758         case webgl::UnsizedFormat::R:
759           return true;
760         default:
761           return false;
762       }
763 
764     default:
765       return false;
766   }
767 }
768 
EnsureImageDataInitializedForUpload(WebGLTexture * tex,const char * funcName,TexImageTarget target,GLint level,GLint xOffset,GLint yOffset,GLint zOffset,uint32_t width,uint32_t height,uint32_t depth,WebGLTexture::ImageInfo * imageInfo,bool * const out_uploadWillInitialize)769 static bool EnsureImageDataInitializedForUpload(
770     WebGLTexture* tex, const char* funcName, TexImageTarget target, GLint level,
771     GLint xOffset, GLint yOffset, GLint zOffset, uint32_t width,
772     uint32_t height, uint32_t depth, WebGLTexture::ImageInfo* imageInfo,
773     bool* const out_uploadWillInitialize) {
774   *out_uploadWillInitialize = false;
775 
776   if (!imageInfo->IsDataInitialized()) {
777     const bool isFullUpload =
778         (!xOffset && !yOffset && !zOffset && width == imageInfo->mWidth &&
779          height == imageInfo->mHeight && depth == imageInfo->mDepth);
780     if (isFullUpload) {
781       *out_uploadWillInitialize = true;
782     } else {
783       WebGLContext* webgl = tex->mContext;
784       webgl->GenerateWarning(
785           "%s: Texture has not been initialized prior to a"
786           " partial upload, forcing the browser to clear it."
787           " This may be slow.",
788           funcName);
789       if (!tex->InitializeImageData(funcName, target, level)) {
790         MOZ_ASSERT(false, "Unexpected failure to init image data.");
791         return false;
792       }
793     }
794   }
795 
796   return true;
797 }
798 
799 //////////////////////////////////////////////////////////////////////////////////////////
800 //////////////////////////////////////////////////////////////////////////////////////////
801 // Actual calls
802 
DoTexStorage(gl::GLContext * gl,TexTarget target,GLsizei levels,GLenum sizedFormat,GLsizei width,GLsizei height,GLsizei depth)803 static inline GLenum DoTexStorage(gl::GLContext* gl, TexTarget target,
804                                   GLsizei levels, GLenum sizedFormat,
805                                   GLsizei width, GLsizei height,
806                                   GLsizei depth) {
807   gl::GLContext::LocalErrorScope errorScope(*gl);
808 
809   switch (target.get()) {
810     case LOCAL_GL_TEXTURE_2D:
811     case LOCAL_GL_TEXTURE_CUBE_MAP:
812       MOZ_ASSERT(depth == 1);
813       gl->fTexStorage2D(target.get(), levels, sizedFormat, width, height);
814       break;
815 
816     case LOCAL_GL_TEXTURE_3D:
817     case LOCAL_GL_TEXTURE_2D_ARRAY:
818       gl->fTexStorage3D(target.get(), levels, sizedFormat, width, height,
819                         depth);
820       break;
821 
822     default:
823       MOZ_CRASH("GFX: bad target");
824   }
825 
826   return errorScope.GetError();
827 }
828 
IsTarget3D(TexImageTarget target)829 bool IsTarget3D(TexImageTarget target) {
830   switch (target.get()) {
831     case LOCAL_GL_TEXTURE_2D:
832     case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
833     case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
834     case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
835     case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
836     case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
837     case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
838       return false;
839 
840     case LOCAL_GL_TEXTURE_3D:
841     case LOCAL_GL_TEXTURE_2D_ARRAY:
842       return true;
843 
844     default:
845       MOZ_CRASH("GFX: bad target");
846   }
847 }
848 
DoTexImage(gl::GLContext * gl,TexImageTarget target,GLint level,const webgl::DriverUnpackInfo * dui,GLsizei width,GLsizei height,GLsizei depth,const void * data)849 GLenum DoTexImage(gl::GLContext* gl, TexImageTarget target, GLint level,
850                   const webgl::DriverUnpackInfo* dui, GLsizei width,
851                   GLsizei height, GLsizei depth, const void* data) {
852   const GLint border = 0;
853 
854   gl::GLContext::LocalErrorScope errorScope(*gl);
855 
856   if (IsTarget3D(target)) {
857     gl->fTexImage3D(target.get(), level, dui->internalFormat, width, height,
858                     depth, border, dui->unpackFormat, dui->unpackType, data);
859   } else {
860     MOZ_ASSERT(depth == 1);
861     gl->fTexImage2D(target.get(), level, dui->internalFormat, width, height,
862                     border, dui->unpackFormat, dui->unpackType, data);
863   }
864 
865   return errorScope.GetError();
866 }
867 
DoTexSubImage(gl::GLContext * gl,TexImageTarget target,GLint level,GLint xOffset,GLint yOffset,GLint zOffset,GLsizei width,GLsizei height,GLsizei depth,const webgl::PackingInfo & pi,const void * data)868 GLenum DoTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level,
869                      GLint xOffset, GLint yOffset, GLint zOffset, GLsizei width,
870                      GLsizei height, GLsizei depth,
871                      const webgl::PackingInfo& pi, const void* data) {
872   gl::GLContext::LocalErrorScope errorScope(*gl);
873 
874   if (IsTarget3D(target)) {
875     gl->fTexSubImage3D(target.get(), level, xOffset, yOffset, zOffset, width,
876                        height, depth, pi.format, pi.type, data);
877   } else {
878     MOZ_ASSERT(zOffset == 0);
879     MOZ_ASSERT(depth == 1);
880     gl->fTexSubImage2D(target.get(), level, xOffset, yOffset, width, height,
881                        pi.format, pi.type, data);
882   }
883 
884   return errorScope.GetError();
885 }
886 
DoCompressedTexImage(gl::GLContext * gl,TexImageTarget target,GLint level,GLenum internalFormat,GLsizei width,GLsizei height,GLsizei depth,GLsizei dataSize,const void * data)887 static inline GLenum DoCompressedTexImage(gl::GLContext* gl,
888                                           TexImageTarget target, GLint level,
889                                           GLenum internalFormat, GLsizei width,
890                                           GLsizei height, GLsizei depth,
891                                           GLsizei dataSize, const void* data) {
892   const GLint border = 0;
893 
894   gl::GLContext::LocalErrorScope errorScope(*gl);
895 
896   if (IsTarget3D(target)) {
897     gl->fCompressedTexImage3D(target.get(), level, internalFormat, width,
898                               height, depth, border, dataSize, data);
899   } else {
900     MOZ_ASSERT(depth == 1);
901     gl->fCompressedTexImage2D(target.get(), level, internalFormat, width,
902                               height, border, dataSize, data);
903   }
904 
905   return errorScope.GetError();
906 }
907 
DoCompressedTexSubImage(gl::GLContext * gl,TexImageTarget target,GLint level,GLint xOffset,GLint yOffset,GLint zOffset,GLsizei width,GLsizei height,GLsizei depth,GLenum sizedUnpackFormat,GLsizei dataSize,const void * data)908 GLenum DoCompressedTexSubImage(gl::GLContext* gl, TexImageTarget target,
909                                GLint level, GLint xOffset, GLint yOffset,
910                                GLint zOffset, GLsizei width, GLsizei height,
911                                GLsizei depth, GLenum sizedUnpackFormat,
912                                GLsizei dataSize, const void* data) {
913   gl::GLContext::LocalErrorScope errorScope(*gl);
914 
915   if (IsTarget3D(target)) {
916     gl->fCompressedTexSubImage3D(target.get(), level, xOffset, yOffset, zOffset,
917                                  width, height, depth, sizedUnpackFormat,
918                                  dataSize, data);
919   } else {
920     MOZ_ASSERT(zOffset == 0);
921     MOZ_ASSERT(depth == 1);
922     gl->fCompressedTexSubImage2D(target.get(), level, xOffset, yOffset, width,
923                                  height, sizedUnpackFormat, dataSize, data);
924   }
925 
926   return errorScope.GetError();
927 }
928 
DoCopyTexSubImage(gl::GLContext * gl,TexImageTarget target,GLint level,GLint xOffset,GLint yOffset,GLint zOffset,GLint x,GLint y,GLsizei width,GLsizei height)929 static inline GLenum DoCopyTexSubImage(gl::GLContext* gl, TexImageTarget target,
930                                        GLint level, GLint xOffset,
931                                        GLint yOffset, GLint zOffset, GLint x,
932                                        GLint y, GLsizei width, GLsizei height) {
933   gl::GLContext::LocalErrorScope errorScope(*gl);
934 
935   if (IsTarget3D(target)) {
936     gl->fCopyTexSubImage3D(target.get(), level, xOffset, yOffset, zOffset, x, y,
937                            width, height);
938   } else {
939     MOZ_ASSERT(zOffset == 0);
940     gl->fCopyTexSubImage2D(target.get(), level, xOffset, yOffset, x, y, width,
941                            height);
942   }
943 
944   return errorScope.GetError();
945 }
946 
947 //////////////////////////////////////////////////////////////////////////////////////////
948 //////////////////////////////////////////////////////////////////////////////////////////
949 // Actual (mostly generic) function implementations
950 
ValidateCompressedTexImageRestrictions(const char * funcName,WebGLContext * webgl,TexImageTarget target,uint32_t level,const webgl::FormatInfo * format,uint32_t width,uint32_t height,uint32_t depth)951 static bool ValidateCompressedTexImageRestrictions(
952     const char* funcName, WebGLContext* webgl, TexImageTarget target,
953     uint32_t level, const webgl::FormatInfo* format, uint32_t width,
954     uint32_t height, uint32_t depth) {
955   const auto fnIsDimValid_S3TC = [level](uint32_t size, uint32_t blockSize) {
956     if (size % blockSize == 0) return true;
957 
958     if (level == 0) return false;
959 
960     return (size == 0 || size == 1 || size == 2);
961   };
962 
963   switch (format->compression->family) {
964     case webgl::CompressionFamily::ASTC:
965       if (target == LOCAL_GL_TEXTURE_3D &&
966           !webgl->gl->IsExtensionSupported(
967               gl::GLContext::KHR_texture_compression_astc_hdr)) {
968         webgl->ErrorInvalidOperation(
969             "%s: TEXTURE_3D requires ASTC's hdr profile.", funcName);
970         return false;
971       }
972       break;
973 
974     case webgl::CompressionFamily::PVRTC:
975       if (!IsPowerOfTwo(width) || !IsPowerOfTwo(height)) {
976         webgl->ErrorInvalidValue(
977             "%s: %s requires power-of-two width and height.", funcName,
978             format->name);
979         return false;
980       }
981 
982       break;
983 
984     case webgl::CompressionFamily::S3TC:
985       if (!fnIsDimValid_S3TC(width, format->compression->blockWidth) ||
986           !fnIsDimValid_S3TC(height, format->compression->blockHeight)) {
987         webgl->ErrorInvalidOperation(
988             "%s: %s requires that width and height are"
989             " block-aligned, or, if level>0, equal to 0, 1,"
990             " or 2.",
991             funcName, format->name);
992         return false;
993       }
994 
995       break;
996 
997     // Default: There are no restrictions on CompressedTexImage.
998     default:  // ATC, ETC1, ES3
999       break;
1000   }
1001 
1002   return true;
1003 }
1004 
ValidateTargetForFormat(const char * funcName,WebGLContext * webgl,TexImageTarget target,const webgl::FormatInfo * format)1005 static bool ValidateTargetForFormat(const char* funcName, WebGLContext* webgl,
1006                                     TexImageTarget target,
1007                                     const webgl::FormatInfo* format) {
1008   // GLES 3.0.4 p127:
1009   // "Textures with a base internal format of DEPTH_COMPONENT or DEPTH_STENCIL
1010   // are supported by texture image specification commands only if `target` is
1011   // TEXTURE_2D, TEXTURE_2D_ARRAY, or TEXTURE_CUBE_MAP. Using these formats in
1012   // conjunction with any other `target` will result in an INVALID_OPERATION
1013   // error."
1014 
1015   switch (format->effectiveFormat) {
1016     // TEXTURE_2D_ARRAY but not TEXTURE_3D:
1017     // D and DS formats
1018     case webgl::EffectiveFormat::DEPTH_COMPONENT16:
1019     case webgl::EffectiveFormat::DEPTH_COMPONENT24:
1020     case webgl::EffectiveFormat::DEPTH_COMPONENT32F:
1021     case webgl::EffectiveFormat::DEPTH24_STENCIL8:
1022     case webgl::EffectiveFormat::DEPTH32F_STENCIL8:
1023     // CompressionFamily::ES3
1024     case webgl::EffectiveFormat::COMPRESSED_R11_EAC:
1025     case webgl::EffectiveFormat::COMPRESSED_SIGNED_R11_EAC:
1026     case webgl::EffectiveFormat::COMPRESSED_RG11_EAC:
1027     case webgl::EffectiveFormat::COMPRESSED_SIGNED_RG11_EAC:
1028     case webgl::EffectiveFormat::COMPRESSED_RGB8_ETC2:
1029     case webgl::EffectiveFormat::COMPRESSED_SRGB8_ETC2:
1030     case webgl::EffectiveFormat::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
1031     case webgl::EffectiveFormat::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
1032     case webgl::EffectiveFormat::COMPRESSED_RGBA8_ETC2_EAC:
1033     case webgl::EffectiveFormat::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
1034     // CompressionFamily::S3TC
1035     case webgl::EffectiveFormat::COMPRESSED_RGB_S3TC_DXT1_EXT:
1036     case webgl::EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT1_EXT:
1037     case webgl::EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT3_EXT:
1038     case webgl::EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT5_EXT:
1039       if (target == LOCAL_GL_TEXTURE_3D) {
1040         webgl->ErrorInvalidOperation(
1041             "%s: Format %s cannot be used with TEXTURE_3D.", funcName,
1042             format->name);
1043         return false;
1044       }
1045       break;
1046 
1047     // No 3D targets:
1048     // CompressionFamily::ATC
1049     case webgl::EffectiveFormat::ATC_RGB_AMD:
1050     case webgl::EffectiveFormat::ATC_RGBA_EXPLICIT_ALPHA_AMD:
1051     case webgl::EffectiveFormat::ATC_RGBA_INTERPOLATED_ALPHA_AMD:
1052     // CompressionFamily::PVRTC
1053     case webgl::EffectiveFormat::COMPRESSED_RGB_PVRTC_4BPPV1:
1054     case webgl::EffectiveFormat::COMPRESSED_RGBA_PVRTC_4BPPV1:
1055     case webgl::EffectiveFormat::COMPRESSED_RGB_PVRTC_2BPPV1:
1056     case webgl::EffectiveFormat::COMPRESSED_RGBA_PVRTC_2BPPV1:
1057     // CompressionFamily::ETC1
1058     case webgl::EffectiveFormat::ETC1_RGB8_OES:
1059       if (target == LOCAL_GL_TEXTURE_3D ||
1060           target == LOCAL_GL_TEXTURE_2D_ARRAY) {
1061         webgl->ErrorInvalidOperation(
1062             "%s: Format %s cannot be used with TEXTURE_3D or"
1063             " TEXTURE_2D_ARRAY.",
1064             funcName, format->name);
1065         return false;
1066       }
1067       break;
1068 
1069     default:
1070       break;
1071   }
1072 
1073   return true;
1074 }
1075 
TexStorage(const char * funcName,TexTarget target,GLsizei levels,GLenum sizedFormat,GLsizei width,GLsizei height,GLsizei depth)1076 void WebGLTexture::TexStorage(const char* funcName, TexTarget target,
1077                               GLsizei levels, GLenum sizedFormat, GLsizei width,
1078                               GLsizei height, GLsizei depth) {
1079   // Check levels
1080   if (levels < 1) {
1081     mContext->ErrorInvalidValue("%s: `levels` must be >= 1.", funcName);
1082     return;
1083   }
1084 
1085   if (!width || !height || !depth) {
1086     mContext->ErrorInvalidValue("%s: Dimensions must be non-zero.", funcName);
1087     return;
1088   }
1089 
1090   const TexImageTarget testTarget =
1091       IsCubeMap() ? LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X : target.get();
1092   const GLint testLevel = 0;
1093 
1094   WebGLTexture::ImageInfo* testImageInfo;
1095   if (!ValidateTexImageSpecification(funcName, testTarget, testLevel, width,
1096                                      height, depth, &testImageInfo)) {
1097     return;
1098   }
1099   MOZ_ASSERT(testImageInfo);
1100   mozilla::Unused << testImageInfo;
1101 
1102   auto dstUsage = mContext->mFormatUsage->GetSizedTexUsage(sizedFormat);
1103   if (!dstUsage) {
1104     mContext->ErrorInvalidEnum("%s: Invalid internalformat: 0x%04x", funcName,
1105                                sizedFormat);
1106     return;
1107   }
1108   auto dstFormat = dstUsage->format;
1109 
1110   if (!ValidateTargetForFormat(funcName, mContext, testTarget, dstFormat))
1111     return;
1112 
1113   if (dstFormat->compression) {
1114     if (!ValidateCompressedTexImageRestrictions(funcName, mContext, testTarget,
1115                                                 testLevel, dstFormat, width,
1116                                                 height, depth)) {
1117       return;
1118     }
1119   }
1120 
1121   ////////////////////////////////////
1122 
1123   const auto lastLevel = levels - 1;
1124   MOZ_ASSERT(lastLevel <= 31, "Right-shift is only defined for bits-1.");
1125 
1126   const uint32_t lastLevelWidth = uint32_t(width) >> lastLevel;
1127   const uint32_t lastLevelHeight = uint32_t(height) >> lastLevel;
1128   const uint32_t lastLevelDepth = uint32_t(depth) >> lastLevel;
1129 
1130   // If these are all zero, then some earlier level was the final 1x1x1 level.
1131   if (!lastLevelWidth && !lastLevelHeight && !lastLevelDepth) {
1132     mContext->ErrorInvalidOperation(
1133         "%s: Too many levels requested for the given"
1134         " dimensions. (levels: %u, width: %u, height: %u,"
1135         " depth: %u)",
1136         funcName, levels, width, height, depth);
1137     return;
1138   }
1139 
1140   ////////////////////////////////////
1141   // Do the thing!
1142 
1143   GLenum error = DoTexStorage(mContext->gl, target.get(), levels, sizedFormat,
1144                               width, height, depth);
1145 
1146   mContext->OnDataAllocCall();
1147 
1148   if (error == LOCAL_GL_OUT_OF_MEMORY) {
1149     mContext->ErrorOutOfMemory(
1150         "%s: Ran out of memory during texture allocation.", funcName);
1151     Truncate();
1152     return;
1153   }
1154   if (error) {
1155     MOZ_RELEASE_ASSERT(false, "GFX: We should have caught all other errors.");
1156     mContext->ErrorInvalidOperation(
1157         "%s: Unexpected error during texture allocation.", funcName);
1158     return;
1159   }
1160 
1161   ////////////////////////////////////
1162   // Update our specification data.
1163 
1164   const bool isDataInitialized = false;
1165   const WebGLTexture::ImageInfo newInfo(dstUsage, width, height, depth,
1166                                         isDataInitialized);
1167   SetImageInfosAtLevel(funcName, 0, newInfo);
1168 
1169   PopulateMipChain(funcName, 0, levels - 1);
1170 
1171   mImmutable = true;
1172   mImmutableLevelCount = levels;
1173 }
1174 
1175 ////////////////////////////////////////
1176 // Tex(Sub)Image
1177 
TexImage(const char * funcName,TexImageTarget target,GLint level,GLenum internalFormat,const webgl::PackingInfo & pi,const webgl::TexUnpackBlob * blob)1178 void WebGLTexture::TexImage(const char* funcName, TexImageTarget target,
1179                             GLint level, GLenum internalFormat,
1180                             const webgl::PackingInfo& pi,
1181                             const webgl::TexUnpackBlob* blob) {
1182   ////////////////////////////////////
1183   // Get dest info
1184 
1185   WebGLTexture::ImageInfo* imageInfo;
1186   if (!ValidateTexImageSpecification(funcName, target, level, blob->mWidth,
1187                                      blob->mHeight, blob->mDepth, &imageInfo)) {
1188     return;
1189   }
1190   MOZ_ASSERT(imageInfo);
1191 
1192   const auto& fua = mContext->mFormatUsage;
1193   if (!fua->IsInternalFormatEnumValid(internalFormat)) {
1194     mContext->ErrorInvalidValue("%s: Invalid internalformat: 0x%04x", funcName,
1195                                 internalFormat);
1196     return;
1197   }
1198 
1199   auto dstUsage = fua->GetSizedTexUsage(internalFormat);
1200   if (!dstUsage) {
1201     if (internalFormat != pi.format) {
1202       /* GL ES Version 3.0.4 - 3.8.3 Texture Image Specification
1203        *   "Specifying a combination of values for format, type, and
1204        *   internalformat that is not listed as a valid combination
1205        *   in tables 3.2 or 3.3 generates the error INVALID_OPERATION."
1206        */
1207       mContext->ErrorInvalidOperation(
1208           "%s: Unsized internalFormat must match"
1209           " unpack format.",
1210           funcName);
1211       return;
1212     }
1213 
1214     dstUsage = fua->GetUnsizedTexUsage(pi);
1215   }
1216 
1217   if (!dstUsage) {
1218     mContext->ErrorInvalidOperation(
1219         "%s: Invalid internalformat/format/type:"
1220         " 0x%04x/0x%04x/0x%04x",
1221         funcName, internalFormat, pi.format, pi.type);
1222     return;
1223   }
1224 
1225   const webgl::DriverUnpackInfo* driverUnpackInfo;
1226   if (!dstUsage->IsUnpackValid(pi, &driverUnpackInfo)) {
1227     mContext->ErrorInvalidOperation(
1228         "%s: Mismatched internalFormat and format/type:"
1229         " 0x%04x and 0x%04x/0x%04x",
1230         funcName, internalFormat, pi.format, pi.type);
1231     return;
1232   }
1233 
1234   ////////////////////////////////////
1235   // Check that source and dest info are compatible
1236   auto dstFormat = dstUsage->format;
1237 
1238   if (!ValidateTargetForFormat(funcName, mContext, target, dstFormat)) return;
1239 
1240   if (!mContext->IsWebGL2() && dstFormat->d) {
1241     if (target != LOCAL_GL_TEXTURE_2D || blob->HasData() || level != 0) {
1242       mContext->ErrorInvalidOperation(
1243           "%s: With format %s, this function may only"
1244           " be called with target=TEXTURE_2D,"
1245           " data=null, and level=0.",
1246           funcName, dstFormat->name);
1247       return;
1248     }
1249   }
1250 
1251   ////////////////////////////////////
1252   // Do the thing!
1253 
1254   // It's tempting to do allocation first, and TexSubImage second, but this is
1255   // generally slower.
1256 
1257   const ImageInfo newImageInfo(dstUsage, blob->mWidth, blob->mHeight,
1258                                blob->mDepth, blob->HasData());
1259 
1260   const bool isSubImage = false;
1261   const bool needsRespec = (imageInfo->mWidth != newImageInfo.mWidth ||
1262                             imageInfo->mHeight != newImageInfo.mHeight ||
1263                             imageInfo->mDepth != newImageInfo.mDepth ||
1264                             imageInfo->mFormat != newImageInfo.mFormat);
1265   const GLint xOffset = 0;
1266   const GLint yOffset = 0;
1267   const GLint zOffset = 0;
1268 
1269   GLenum glError;
1270   if (!blob->TexOrSubImage(isSubImage, needsRespec, funcName, this, target,
1271                            level, driverUnpackInfo, xOffset, yOffset, zOffset,
1272                            pi, &glError)) {
1273     return;
1274   }
1275 
1276   mContext->OnDataAllocCall();
1277 
1278   if (glError == LOCAL_GL_OUT_OF_MEMORY) {
1279     mContext->ErrorOutOfMemory("%s: Driver ran out of memory during upload.",
1280                                funcName);
1281     Truncate();
1282     return;
1283   }
1284 
1285   if (glError) {
1286     mContext->ErrorInvalidOperation(
1287         "%s: Unexpected error during upload: 0x%04x", funcName, glError);
1288     printf_stderr("%s: dui: %x/%x/%x\n", funcName,
1289                   driverUnpackInfo->internalFormat,
1290                   driverUnpackInfo->unpackFormat, driverUnpackInfo->unpackType);
1291     MOZ_ASSERT(false, "Unexpected GL error.");
1292     return;
1293   }
1294 
1295   ////////////////////////////////////
1296   // Update our specification data.
1297 
1298   SetImageInfo(funcName, imageInfo, newImageInfo);
1299 }
1300 
TexSubImage(const char * funcName,TexImageTarget target,GLint level,GLint xOffset,GLint yOffset,GLint zOffset,const webgl::PackingInfo & pi,const webgl::TexUnpackBlob * blob)1301 void WebGLTexture::TexSubImage(const char* funcName, TexImageTarget target,
1302                                GLint level, GLint xOffset, GLint yOffset,
1303                                GLint zOffset, const webgl::PackingInfo& pi,
1304                                const webgl::TexUnpackBlob* blob) {
1305   ////////////////////////////////////
1306   // Get dest info
1307 
1308   WebGLTexture::ImageInfo* imageInfo;
1309   if (!ValidateTexImageSelection(funcName, target, level, xOffset, yOffset,
1310                                  zOffset, blob->mWidth, blob->mHeight,
1311                                  blob->mDepth, &imageInfo)) {
1312     return;
1313   }
1314   MOZ_ASSERT(imageInfo);
1315 
1316   auto dstUsage = imageInfo->mFormat;
1317   auto dstFormat = dstUsage->format;
1318 
1319   if (dstFormat->compression) {
1320     mContext->ErrorInvalidEnum("%s: Specified TexImage must not be compressed.",
1321                                funcName);
1322     return;
1323   }
1324 
1325   if (!mContext->IsWebGL2() && dstFormat->d) {
1326     mContext->ErrorInvalidOperation(
1327         "%s: Function may not be called on a texture of"
1328         " format %s.",
1329         funcName, dstFormat->name);
1330     return;
1331   }
1332 
1333   ////////////////////////////////////
1334   // Get source info
1335 
1336   const webgl::DriverUnpackInfo* driverUnpackInfo;
1337   if (!dstUsage->IsUnpackValid(pi, &driverUnpackInfo)) {
1338     mContext->ErrorInvalidOperation(
1339         "%s: Mismatched internalFormat and format/type:"
1340         " %s and 0x%04x/0x%04x",
1341         funcName, dstFormat->name, pi.format, pi.type);
1342     return;
1343   }
1344 
1345   ////////////////////////////////////
1346   // Do the thing!
1347 
1348   bool uploadWillInitialize;
1349   if (!EnsureImageDataInitializedForUpload(
1350           this, funcName, target, level, xOffset, yOffset, zOffset,
1351           blob->mWidth, blob->mHeight, blob->mDepth, imageInfo,
1352           &uploadWillInitialize)) {
1353     return;
1354   }
1355 
1356   const bool isSubImage = true;
1357   const bool needsRespec = false;
1358 
1359   GLenum glError;
1360   if (!blob->TexOrSubImage(isSubImage, needsRespec, funcName, this, target,
1361                            level, driverUnpackInfo, xOffset, yOffset, zOffset,
1362                            pi, &glError)) {
1363     return;
1364   }
1365 
1366   if (glError == LOCAL_GL_OUT_OF_MEMORY) {
1367     mContext->ErrorOutOfMemory("%s: Driver ran out of memory during upload.",
1368                                funcName);
1369     Truncate();
1370     return;
1371   }
1372 
1373   if (glError) {
1374     mContext->ErrorInvalidOperation(
1375         "%s: Unexpected error during upload: 0x%04x", funcName, glError);
1376     MOZ_ASSERT(false, "Unexpected GL error.");
1377     return;
1378   }
1379 
1380   ////////////////////////////////////
1381   // Update our specification data?
1382 
1383   if (uploadWillInitialize) {
1384     imageInfo->SetIsDataInitialized(true, this);
1385   }
1386 }
1387 
1388 ////////////////////////////////////////
1389 // CompressedTex(Sub)Image
1390 
FromCompressed(const char * funcName,TexImageTarget target,GLsizei rawWidth,GLsizei rawHeight,GLsizei rawDepth,GLint border,const TexImageSource & src,const Maybe<GLsizei> & expectedImageSize)1391 UniquePtr<webgl::TexUnpackBytes> WebGLContext::FromCompressed(
1392     const char* funcName, TexImageTarget target, GLsizei rawWidth,
1393     GLsizei rawHeight, GLsizei rawDepth, GLint border,
1394     const TexImageSource& src, const Maybe<GLsizei>& expectedImageSize) {
1395   uint32_t width, height, depth;
1396   if (!ValidateExtents(this, funcName, rawWidth, rawHeight, rawDepth, border,
1397                        &width, &height, &depth)) {
1398     return nullptr;
1399   }
1400 
1401   if (src.mPboOffset) {
1402     return FromPboOffset(this, funcName, target, width, height, depth,
1403                          *(src.mPboOffset), expectedImageSize);
1404   }
1405 
1406   if (mBoundPixelUnpackBuffer) {
1407     ErrorInvalidOperation("%s: PIXEL_UNPACK_BUFFER must be null.", funcName);
1408     return nullptr;
1409   }
1410 
1411   return FromView(this, funcName, target, width, height, depth, src.mView,
1412                   src.mViewElemOffset, src.mViewElemLengthOverride);
1413 }
1414 
CompressedTexImage(const char * funcName,TexImageTarget target,GLint level,GLenum internalFormat,GLsizei rawWidth,GLsizei rawHeight,GLsizei rawDepth,GLint border,const TexImageSource & src,const Maybe<GLsizei> & expectedImageSize)1415 void WebGLTexture::CompressedTexImage(const char* funcName,
1416                                       TexImageTarget target, GLint level,
1417                                       GLenum internalFormat, GLsizei rawWidth,
1418                                       GLsizei rawHeight, GLsizei rawDepth,
1419                                       GLint border, const TexImageSource& src,
1420                                       const Maybe<GLsizei>& expectedImageSize) {
1421   const auto blob =
1422       mContext->FromCompressed(funcName, target, rawWidth, rawHeight, rawDepth,
1423                                border, src, expectedImageSize);
1424   if (!blob) return;
1425 
1426   ////////////////////////////////////
1427   // Get dest info
1428 
1429   WebGLTexture::ImageInfo* imageInfo;
1430   if (!ValidateTexImageSpecification(funcName, target, level, blob->mWidth,
1431                                      blob->mHeight, blob->mDepth, &imageInfo)) {
1432     return;
1433   }
1434   MOZ_ASSERT(imageInfo);
1435 
1436   auto usage = mContext->mFormatUsage->GetSizedTexUsage(internalFormat);
1437   if (!usage) {
1438     mContext->ErrorInvalidEnum("%s: Invalid internalFormat: 0x%04x", funcName,
1439                                internalFormat);
1440     return;
1441   }
1442 
1443   auto format = usage->format;
1444   if (!format->compression) {
1445     mContext->ErrorInvalidEnum(
1446         "%s: Specified internalFormat must be compressed.", funcName);
1447     return;
1448   }
1449 
1450   if (!ValidateTargetForFormat(funcName, mContext, target, format)) return;
1451 
1452   ////////////////////////////////////
1453   // Get source info
1454 
1455   if (!ValidateCompressedTexUnpack(mContext, funcName, blob->mWidth,
1456                                    blob->mHeight, blob->mDepth, format,
1457                                    blob->mAvailBytes)) {
1458     return;
1459   }
1460 
1461   ////////////////////////////////////
1462   // Check that source is compatible with dest
1463 
1464   if (!ValidateCompressedTexImageRestrictions(funcName, mContext, target, level,
1465                                               format, blob->mWidth,
1466                                               blob->mHeight, blob->mDepth)) {
1467     return;
1468   }
1469 
1470   ////////////////////////////////////
1471   // Do the thing!
1472 
1473   const ScopedLazyBind bindPBO(mContext->gl, LOCAL_GL_PIXEL_UNPACK_BUFFER,
1474                                mContext->mBoundPixelUnpackBuffer);
1475 
1476   // Warning: Possibly shared memory.  See bug 1225033.
1477   GLenum error = DoCompressedTexImage(
1478       mContext->gl, target, level, internalFormat, blob->mWidth, blob->mHeight,
1479       blob->mDepth, blob->mAvailBytes, blob->mPtr);
1480   mContext->OnDataAllocCall();
1481   if (error == LOCAL_GL_OUT_OF_MEMORY) {
1482     mContext->ErrorOutOfMemory("%s: Ran out of memory during upload.",
1483                                funcName);
1484     Truncate();
1485     return;
1486   }
1487   if (error) {
1488     MOZ_RELEASE_ASSERT(false, "GFX: We should have caught all other errors.");
1489     mContext->GenerateWarning(
1490         "%s: Unexpected error during texture upload. Context"
1491         " lost.",
1492         funcName);
1493     mContext->ForceLoseContext();
1494     return;
1495   }
1496 
1497   ////////////////////////////////////
1498   // Update our specification data.
1499 
1500   const bool isDataInitialized = true;
1501   const ImageInfo newImageInfo(usage, blob->mWidth, blob->mHeight, blob->mDepth,
1502                                isDataInitialized);
1503   SetImageInfo(funcName, imageInfo, newImageInfo);
1504 }
1505 
IsSubImageBlockAligned(const webgl::CompressedFormatInfo * compression,const WebGLTexture::ImageInfo * imageInfo,GLint xOffset,GLint yOffset,uint32_t width,uint32_t height)1506 static inline bool IsSubImageBlockAligned(
1507     const webgl::CompressedFormatInfo* compression,
1508     const WebGLTexture::ImageInfo* imageInfo, GLint xOffset, GLint yOffset,
1509     uint32_t width, uint32_t height) {
1510   if (xOffset % compression->blockWidth != 0 ||
1511       yOffset % compression->blockHeight != 0) {
1512     return false;
1513   }
1514 
1515   if (width % compression->blockWidth != 0 &&
1516       xOffset + width != imageInfo->mWidth)
1517     return false;
1518 
1519   if (height % compression->blockHeight != 0 &&
1520       yOffset + height != imageInfo->mHeight)
1521     return false;
1522 
1523   return true;
1524 }
1525 
CompressedTexSubImage(const char * funcName,TexImageTarget target,GLint level,GLint xOffset,GLint yOffset,GLint zOffset,GLsizei rawWidth,GLsizei rawHeight,GLsizei rawDepth,GLenum sizedUnpackFormat,const TexImageSource & src,const Maybe<GLsizei> & expectedImageSize)1526 void WebGLTexture::CompressedTexSubImage(
1527     const char* funcName, TexImageTarget target, GLint level, GLint xOffset,
1528     GLint yOffset, GLint zOffset, GLsizei rawWidth, GLsizei rawHeight,
1529     GLsizei rawDepth, GLenum sizedUnpackFormat, const TexImageSource& src,
1530     const Maybe<GLsizei>& expectedImageSize) {
1531   const GLint border = 0;
1532   const auto blob =
1533       mContext->FromCompressed(funcName, target, rawWidth, rawHeight, rawDepth,
1534                                border, src, expectedImageSize);
1535   if (!blob) return;
1536 
1537   ////////////////////////////////////
1538   // Get dest info
1539 
1540   WebGLTexture::ImageInfo* imageInfo;
1541   if (!ValidateTexImageSelection(funcName, target, level, xOffset, yOffset,
1542                                  zOffset, blob->mWidth, blob->mHeight,
1543                                  blob->mDepth, &imageInfo)) {
1544     return;
1545   }
1546   MOZ_ASSERT(imageInfo);
1547 
1548   auto dstUsage = imageInfo->mFormat;
1549   auto dstFormat = dstUsage->format;
1550 
1551   ////////////////////////////////////
1552   // Get source info
1553 
1554   auto srcUsage = mContext->mFormatUsage->GetSizedTexUsage(sizedUnpackFormat);
1555   if (!srcUsage->format->compression) {
1556     mContext->ErrorInvalidEnum("%s: Specified format must be compressed.",
1557                                funcName);
1558     return;
1559   }
1560 
1561   if (srcUsage != dstUsage) {
1562     mContext->ErrorInvalidOperation(
1563         "%s: `format` must match the format of the"
1564         " existing texture image.",
1565         funcName);
1566     return;
1567   }
1568 
1569   auto format = srcUsage->format;
1570   MOZ_ASSERT(format == dstFormat);
1571   if (!ValidateCompressedTexUnpack(mContext, funcName, blob->mWidth,
1572                                    blob->mHeight, blob->mDepth, format,
1573                                    blob->mAvailBytes)) {
1574     return;
1575   }
1576 
1577   ////////////////////////////////////
1578   // Check that source is compatible with dest
1579 
1580   switch (format->compression->family) {
1581     // Forbidden:
1582     case webgl::CompressionFamily::ETC1:
1583     case webgl::CompressionFamily::ATC:
1584       mContext->ErrorInvalidOperation(
1585           "%s: Format does not allow sub-image"
1586           " updates.",
1587           funcName);
1588       return;
1589 
1590     // Block-aligned:
1591     case webgl::CompressionFamily::ES3:  // Yes, the ES3 formats don't match the
1592                                          // ES3
1593     case webgl::CompressionFamily::S3TC:  // default behavior.
1594       if (!IsSubImageBlockAligned(dstFormat->compression, imageInfo, xOffset,
1595                                   yOffset, blob->mWidth, blob->mHeight)) {
1596         mContext->ErrorInvalidOperation(
1597             "%s: Format requires block-aligned sub-image"
1598             " updates.",
1599             funcName);
1600         return;
1601       }
1602       break;
1603 
1604     // Full-only: (The ES3 default)
1605     default:  // PVRTC
1606       if (xOffset || yOffset || blob->mWidth != imageInfo->mWidth ||
1607           blob->mHeight != imageInfo->mHeight) {
1608         mContext->ErrorInvalidOperation(
1609             "%s: Format does not allow partial sub-image"
1610             " updates.",
1611             funcName);
1612         return;
1613       }
1614       break;
1615   }
1616 
1617   ////////////////////////////////////
1618   // Do the thing!
1619 
1620   bool uploadWillInitialize;
1621   if (!EnsureImageDataInitializedForUpload(
1622           this, funcName, target, level, xOffset, yOffset, zOffset,
1623           blob->mWidth, blob->mHeight, blob->mDepth, imageInfo,
1624           &uploadWillInitialize)) {
1625     return;
1626   }
1627 
1628   const ScopedLazyBind bindPBO(mContext->gl, LOCAL_GL_PIXEL_UNPACK_BUFFER,
1629                                mContext->mBoundPixelUnpackBuffer);
1630 
1631   // Warning: Possibly shared memory.  See bug 1225033.
1632   GLenum error = DoCompressedTexSubImage(
1633       mContext->gl, target, level, xOffset, yOffset, zOffset, blob->mWidth,
1634       blob->mHeight, blob->mDepth, sizedUnpackFormat, blob->mAvailBytes,
1635       blob->mPtr);
1636   if (error == LOCAL_GL_OUT_OF_MEMORY) {
1637     mContext->ErrorOutOfMemory("%s: Ran out of memory during upload.",
1638                                funcName);
1639     Truncate();
1640     return;
1641   }
1642   if (error) {
1643     MOZ_RELEASE_ASSERT(false, "GFX: We should have caught all other errors.");
1644     mContext->GenerateWarning(
1645         "%s: Unexpected error during texture upload. Context"
1646         " lost.",
1647         funcName);
1648     mContext->ForceLoseContext();
1649     return;
1650   }
1651 
1652   ////////////////////////////////////
1653   // Update our specification data?
1654 
1655   if (uploadWillInitialize) {
1656     imageInfo->SetIsDataInitialized(true, this);
1657   }
1658 }
1659 
1660 ////////////////////////////////////////
1661 // CopyTex(Sub)Image
1662 
ValidateCopyTexImageFormats(WebGLContext * webgl,const char * funcName,const webgl::FormatInfo * srcFormat,const webgl::FormatInfo * dstFormat)1663 static bool ValidateCopyTexImageFormats(WebGLContext* webgl,
1664                                         const char* funcName,
1665                                         const webgl::FormatInfo* srcFormat,
1666                                         const webgl::FormatInfo* dstFormat) {
1667   MOZ_ASSERT(!srcFormat->compression);
1668   if (dstFormat->compression) {
1669     webgl->ErrorInvalidEnum(
1670         "%s: Specified destination must not have a compressed"
1671         " format.",
1672         funcName);
1673     return false;
1674   }
1675 
1676   if (dstFormat->effectiveFormat == webgl::EffectiveFormat::RGB9_E5) {
1677     webgl->ErrorInvalidOperation(
1678         "%s: RGB9_E5 is an invalid destination for"
1679         " CopyTex(Sub)Image. (GLES 3.0.4 p145)",
1680         funcName);
1681     return false;
1682   }
1683 
1684   if (!DoChannelsMatchForCopyTexImage(srcFormat, dstFormat)) {
1685     webgl->ErrorInvalidOperation(
1686         "%s: Destination channels must be compatible with"
1687         " source channels. (GLES 3.0.4 p140 Table 3.16)",
1688         funcName);
1689     return false;
1690   }
1691 
1692   return true;
1693 }
1694 
1695 ////////////////////////////////////////////////////////////////////////////////
1696 
1697 class ScopedCopyTexImageSource {
1698   WebGLContext* const mWebGL;
1699   GLuint mRB;
1700   GLuint mFB;
1701 
1702  public:
1703   ScopedCopyTexImageSource(WebGLContext* webgl, const char* funcName,
1704                            uint32_t srcWidth, uint32_t srcHeight,
1705                            const webgl::FormatInfo* srcFormat,
1706                            const webgl::FormatUsageInfo* dstUsage);
1707   ~ScopedCopyTexImageSource();
1708 };
1709 
ScopedCopyTexImageSource(WebGLContext * webgl,const char * funcName,uint32_t srcWidth,uint32_t srcHeight,const webgl::FormatInfo * srcFormat,const webgl::FormatUsageInfo * dstUsage)1710 ScopedCopyTexImageSource::ScopedCopyTexImageSource(
1711     WebGLContext* webgl, const char* funcName, uint32_t srcWidth,
1712     uint32_t srcHeight, const webgl::FormatInfo* srcFormat,
1713     const webgl::FormatUsageInfo* dstUsage)
1714     : mWebGL(webgl), mRB(0), mFB(0) {
1715   switch (dstUsage->format->unsizedFormat) {
1716     case webgl::UnsizedFormat::L:
1717     case webgl::UnsizedFormat::A:
1718     case webgl::UnsizedFormat::LA:
1719       webgl->GenerateWarning(
1720           "%s: Copying to a LUMINANCE, ALPHA, or LUMINANCE_ALPHA"
1721           " is deprecated, and has severely reduced performance"
1722           " on some platforms.",
1723           funcName);
1724       break;
1725 
1726     default:
1727       MOZ_ASSERT(!dstUsage->textureSwizzleRGBA);
1728       return;
1729   }
1730 
1731   if (!dstUsage->textureSwizzleRGBA) return;
1732 
1733   gl::GLContext* gl = webgl->gl;
1734 
1735   GLenum sizedFormat;
1736 
1737   switch (srcFormat->componentType) {
1738     case webgl::ComponentType::NormUInt:
1739       sizedFormat = LOCAL_GL_RGBA8;
1740       break;
1741 
1742     case webgl::ComponentType::Float:
1743       if (webgl->IsExtensionEnabled(
1744               WebGLExtensionID::WEBGL_color_buffer_float)) {
1745         sizedFormat = LOCAL_GL_RGBA32F;
1746         break;
1747       }
1748 
1749       if (webgl->IsExtensionEnabled(
1750               WebGLExtensionID::EXT_color_buffer_half_float)) {
1751         sizedFormat = LOCAL_GL_RGBA16F;
1752         break;
1753       }
1754       MOZ_CRASH("GFX: Should be able to request CopyTexImage from Float.");
1755 
1756     default:
1757       MOZ_CRASH("GFX: Should be able to request CopyTexImage from this type.");
1758   }
1759 
1760   gl::ScopedTexture scopedTex(gl);
1761   gl::ScopedBindTexture scopedBindTex(gl, scopedTex.Texture(),
1762                                       LOCAL_GL_TEXTURE_2D);
1763 
1764   gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER,
1765                      LOCAL_GL_NEAREST);
1766   gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER,
1767                      LOCAL_GL_NEAREST);
1768 
1769   GLint blitSwizzle[4] = {LOCAL_GL_ZERO};
1770   switch (dstUsage->format->unsizedFormat) {
1771     case webgl::UnsizedFormat::L:
1772       blitSwizzle[0] = LOCAL_GL_RED;
1773       break;
1774 
1775     case webgl::UnsizedFormat::A:
1776       blitSwizzle[0] = LOCAL_GL_ALPHA;
1777       break;
1778 
1779     case webgl::UnsizedFormat::LA:
1780       blitSwizzle[0] = LOCAL_GL_RED;
1781       blitSwizzle[1] = LOCAL_GL_ALPHA;
1782       break;
1783 
1784     default:
1785       MOZ_CRASH("GFX: Unhandled unsizedFormat.");
1786   }
1787 
1788   gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_R,
1789                      blitSwizzle[0]);
1790   gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_G,
1791                      blitSwizzle[1]);
1792   gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_B,
1793                      blitSwizzle[2]);
1794   gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_A,
1795                      blitSwizzle[3]);
1796 
1797   gl->fCopyTexImage2D(LOCAL_GL_TEXTURE_2D, 0, sizedFormat, 0, 0, srcWidth,
1798                       srcHeight, 0);
1799 
1800   // Now create the swizzled FB we'll be exposing.
1801 
1802   GLuint rgbaRB = 0;
1803   gl->fGenRenderbuffers(1, &rgbaRB);
1804   gl::ScopedBindRenderbuffer scopedRB(gl, rgbaRB);
1805   gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, sizedFormat, srcWidth,
1806                            srcHeight);
1807 
1808   GLuint rgbaFB = 0;
1809   gl->fGenFramebuffers(1, &rgbaFB);
1810   gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, rgbaFB);
1811   gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
1812                                LOCAL_GL_RENDERBUFFER, rgbaRB);
1813 
1814   const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
1815   if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
1816     MOZ_CRASH("GFX: Temp framebuffer is not complete.");
1817   }
1818 
1819   // Restore RB binding.
1820   scopedRB.Unwrap();  // This function should really have a better name.
1821 
1822   // Draw-blit rgbaTex into rgbaFB.
1823   const gfx::IntSize srcSize(srcWidth, srcHeight);
1824   {
1825     const gl::ScopedBindFramebuffer bindFB(gl, rgbaFB);
1826     gl->BlitHelper()->DrawBlitTextureToFramebuffer(scopedTex.Texture(), srcSize,
1827                                                    srcSize);
1828   }
1829 
1830   // Restore Tex2D binding and destroy the temp tex.
1831   scopedBindTex.Unwrap();
1832   scopedTex.Unwrap();
1833 
1834   // Leave RB and FB alive, and FB bound.
1835   mRB = rgbaRB;
1836   mFB = rgbaFB;
1837 }
1838 
1839 template <typename T>
ToGLHandle(const T & obj)1840 static inline GLenum ToGLHandle(const T& obj) {
1841   return (obj ? obj->mGLName : 0);
1842 }
1843 
~ScopedCopyTexImageSource()1844 ScopedCopyTexImageSource::~ScopedCopyTexImageSource() {
1845   if (!mFB) {
1846     MOZ_ASSERT(!mRB);
1847     return;
1848   }
1849   MOZ_ASSERT(mRB);
1850 
1851   gl::GLContext* gl = mWebGL->gl;
1852 
1853   // If we're swizzling, it's because we're on a GL core (3.2+) profile, which
1854   // has split framebuffer support.
1855   gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
1856                        ToGLHandle(mWebGL->mBoundDrawFramebuffer));
1857   gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER,
1858                        ToGLHandle(mWebGL->mBoundReadFramebuffer));
1859 
1860   gl->fDeleteFramebuffers(1, &mFB);
1861   gl->fDeleteRenderbuffers(1, &mRB);
1862 }
1863 
1864 ////////////////////////////////////////////////////////////////////////////////
1865 
GetUnsizedFormatForCopy(GLenum internalFormat,webgl::UnsizedFormat * const out)1866 static bool GetUnsizedFormatForCopy(GLenum internalFormat,
1867                                     webgl::UnsizedFormat* const out) {
1868   switch (internalFormat) {
1869     case LOCAL_GL_RED:
1870       *out = webgl::UnsizedFormat::R;
1871       break;
1872     case LOCAL_GL_RG:
1873       *out = webgl::UnsizedFormat::RG;
1874       break;
1875     case LOCAL_GL_RGB:
1876       *out = webgl::UnsizedFormat::RGB;
1877       break;
1878     case LOCAL_GL_RGBA:
1879       *out = webgl::UnsizedFormat::RGBA;
1880       break;
1881     case LOCAL_GL_LUMINANCE:
1882       *out = webgl::UnsizedFormat::L;
1883       break;
1884     case LOCAL_GL_ALPHA:
1885       *out = webgl::UnsizedFormat::A;
1886       break;
1887     case LOCAL_GL_LUMINANCE_ALPHA:
1888       *out = webgl::UnsizedFormat::LA;
1889       break;
1890 
1891     default:
1892       return false;
1893   }
1894 
1895   return true;
1896 }
1897 
ValidateCopyDestUsage(const char * funcName,WebGLContext * webgl,const webgl::FormatInfo * srcFormat,GLenum internalFormat)1898 static const webgl::FormatUsageInfo* ValidateCopyDestUsage(
1899     const char* funcName, WebGLContext* webgl,
1900     const webgl::FormatInfo* srcFormat, GLenum internalFormat) {
1901   const auto& fua = webgl->mFormatUsage;
1902 
1903   auto dstUsage = fua->GetSizedTexUsage(internalFormat);
1904   if (!dstUsage) {
1905     // Ok, maybe it's unsized.
1906     webgl::UnsizedFormat unsizedFormat;
1907     if (!GetUnsizedFormatForCopy(internalFormat, &unsizedFormat)) {
1908       webgl->ErrorInvalidEnum("%s: Unrecongnized internalFormat 0x%04x.",
1909                               funcName, internalFormat);
1910       return nullptr;
1911     }
1912 
1913     const auto dstFormat = srcFormat->GetCopyDecayFormat(unsizedFormat);
1914     if (dstFormat) {
1915       dstUsage = fua->GetUsage(dstFormat->effectiveFormat);
1916     }
1917     if (!dstUsage) {
1918       webgl->ErrorInvalidOperation(
1919           "%s: 0x%04x is not a valid unsized format for"
1920           " source format %s.",
1921           funcName, internalFormat, srcFormat->name);
1922       return nullptr;
1923     }
1924 
1925     return dstUsage;
1926   }
1927   // Alright, it's sized.
1928 
1929   const auto dstFormat = dstUsage->format;
1930 
1931   const auto fnNarrowType = [&](webgl::ComponentType type) {
1932     switch (type) {
1933       case webgl::ComponentType::NormInt:
1934       case webgl::ComponentType::NormUInt:
1935         // These both count as "fixed-point".
1936         return webgl::ComponentType::NormInt;
1937 
1938       default:
1939         return type;
1940     }
1941   };
1942 
1943   const auto srcType = fnNarrowType(srcFormat->componentType);
1944   const auto dstType = fnNarrowType(dstFormat->componentType);
1945   if (dstType != srcType) {
1946     webgl->ErrorInvalidOperation(
1947         "%s: For sized internalFormats, source and dest"
1948         " component types must match. (source: %s, dest:"
1949         " %s)",
1950         funcName, srcFormat->name, dstFormat->name);
1951     return nullptr;
1952   }
1953 
1954   bool componentSizesMatch = true;
1955   if (dstFormat->r) {
1956     componentSizesMatch &= (dstFormat->r == srcFormat->r);
1957   }
1958   if (dstFormat->g) {
1959     componentSizesMatch &= (dstFormat->g == srcFormat->g);
1960   }
1961   if (dstFormat->b) {
1962     componentSizesMatch &= (dstFormat->b == srcFormat->b);
1963   }
1964   if (dstFormat->a) {
1965     componentSizesMatch &= (dstFormat->a == srcFormat->a);
1966   }
1967 
1968   if (!componentSizesMatch) {
1969     webgl->ErrorInvalidOperation(
1970         "%s: For sized internalFormats, source and dest"
1971         " component sizes must match exactly. (source: %s,"
1972         " dest: %s)",
1973         funcName, srcFormat->name, dstFormat->name);
1974     return nullptr;
1975   }
1976 
1977   return dstUsage;
1978 }
1979 
ValidateCopyTexImageForFeedback(const char * funcName,uint32_t level,GLint layer) const1980 bool WebGLTexture::ValidateCopyTexImageForFeedback(const char* funcName,
1981                                                    uint32_t level,
1982                                                    GLint layer) const {
1983   const auto& fb = mContext->mBoundReadFramebuffer;
1984   if (fb) {
1985     const auto& attach = fb->ColorReadBuffer();
1986     MOZ_ASSERT(attach);
1987 
1988     if (attach->Texture() == this && attach->Layer() == layer &&
1989         uint32_t(attach->MipLevel()) == level) {
1990       // Note that the TexImageTargets *don't* have to match for this to be
1991       // undefined per GLES 3.0.4 p211, thus an INVALID_OP in WebGL.
1992       mContext->ErrorInvalidOperation(
1993           "%s: Feedback loop detected, as this texture"
1994           " is already attached to READ_FRAMEBUFFER's"
1995           " READ_BUFFER-selected COLOR_ATTACHMENT%u.",
1996           funcName, attach->mAttachmentPoint);
1997       return false;
1998     }
1999   }
2000   return true;
2001 }
2002 
DoCopyTexOrSubImage(WebGLContext * webgl,const char * funcName,bool isSubImage,WebGLTexture * tex,TexImageTarget target,GLint level,GLint xWithinSrc,GLint yWithinSrc,uint32_t srcTotalWidth,uint32_t srcTotalHeight,const webgl::FormatUsageInfo * srcUsage,GLint xOffset,GLint yOffset,GLint zOffset,uint32_t dstWidth,uint32_t dstHeight,const webgl::FormatUsageInfo * dstUsage)2003 static bool DoCopyTexOrSubImage(WebGLContext* webgl, const char* funcName,
2004                                 bool isSubImage, WebGLTexture* tex,
2005                                 TexImageTarget target, GLint level,
2006                                 GLint xWithinSrc, GLint yWithinSrc,
2007                                 uint32_t srcTotalWidth, uint32_t srcTotalHeight,
2008                                 const webgl::FormatUsageInfo* srcUsage,
2009                                 GLint xOffset, GLint yOffset, GLint zOffset,
2010                                 uint32_t dstWidth, uint32_t dstHeight,
2011                                 const webgl::FormatUsageInfo* dstUsage) {
2012   const auto& gl = webgl->gl;
2013 
2014   ////
2015 
2016   int32_t readX, readY;
2017   int32_t writeX, writeY;
2018   int32_t rwWidth, rwHeight;
2019   if (!Intersect(srcTotalWidth, xWithinSrc, dstWidth, &readX, &writeX,
2020                  &rwWidth) ||
2021       !Intersect(srcTotalHeight, yWithinSrc, dstHeight, &readY, &writeY,
2022                  &rwHeight)) {
2023     webgl->ErrorOutOfMemory("%s: Bad subrect selection.", funcName);
2024     return false;
2025   }
2026 
2027   writeX += xOffset;
2028   writeY += yOffset;
2029 
2030   ////
2031 
2032   GLenum error = 0;
2033   do {
2034     const auto& idealUnpack = dstUsage->idealUnpack;
2035     if (!isSubImage) {
2036       UniqueBuffer buffer;
2037 
2038       if (uint32_t(rwWidth) != dstWidth || uint32_t(rwHeight) != dstHeight) {
2039         const auto& pi = idealUnpack->ToPacking();
2040         CheckedUint32 byteCount = BytesPerPixel(pi);
2041         byteCount *= dstWidth;
2042         byteCount *= dstHeight;
2043 
2044         if (byteCount.isValid()) {
2045           buffer = calloc(1, byteCount.value());
2046         }
2047 
2048         if (!buffer.get()) {
2049           webgl->ErrorOutOfMemory("%s: Ran out of memory allocating zeros.",
2050                                   funcName);
2051           return false;
2052         }
2053       }
2054 
2055       const ScopedUnpackReset unpackReset(webgl);
2056       gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1);
2057       error = DoTexImage(gl, target, level, idealUnpack, dstWidth, dstHeight, 1,
2058                          buffer.get());
2059       if (error) break;
2060     }
2061 
2062     if (!rwWidth || !rwHeight) {
2063       // There aren't any pixels to copy, so we're 'done'.
2064       return true;
2065     }
2066 
2067     const auto& srcFormat = srcUsage->format;
2068     ScopedCopyTexImageSource maybeSwizzle(webgl, funcName, srcTotalWidth,
2069                                           srcTotalHeight, srcFormat, dstUsage);
2070 
2071     error = DoCopyTexSubImage(gl, target, level, writeX, writeY, zOffset, readX,
2072                               readY, rwWidth, rwHeight);
2073     if (error) break;
2074 
2075     return true;
2076   } while (false);
2077 
2078   if (error == LOCAL_GL_OUT_OF_MEMORY) {
2079     webgl->ErrorOutOfMemory("%s: Ran out of memory during texture copy.",
2080                             funcName);
2081     tex->Truncate();
2082     return false;
2083   }
2084 
2085   if (gl->IsANGLE() && error == LOCAL_GL_INVALID_OPERATION) {
2086     webgl->ErrorImplementationBug(
2087         "%s: ANGLE is particular about CopyTexSubImage"
2088         " formats matching exactly.",
2089         funcName);
2090     return false;
2091   }
2092 
2093   MOZ_RELEASE_ASSERT(false, "GFX: We should have caught all other errors.");
2094   webgl->GenerateWarning(
2095       "%s: Unexpected error during texture copy. Context lost.", funcName);
2096   webgl->ForceLoseContext();
2097   return false;
2098 }
2099 
2100 // There is no CopyTexImage3D.
CopyTexImage2D(TexImageTarget target,GLint level,GLenum internalFormat,GLint x,GLint y,GLsizei rawWidth,GLsizei rawHeight,GLint border)2101 void WebGLTexture::CopyTexImage2D(TexImageTarget target, GLint level,
2102                                   GLenum internalFormat, GLint x, GLint y,
2103                                   GLsizei rawWidth, GLsizei rawHeight,
2104                                   GLint border) {
2105   const char funcName[] = "copyTexImage2D";
2106 
2107   ////////////////////////////////////
2108   // Get dest info
2109 
2110   uint32_t width, height, depth;
2111   if (!ValidateExtents(mContext, funcName, rawWidth, rawHeight, 1, border,
2112                        &width, &height, &depth)) {
2113     return;
2114   }
2115 
2116   WebGLTexture::ImageInfo* imageInfo;
2117   if (!ValidateTexImageSpecification(funcName, target, level, width, height,
2118                                      depth, &imageInfo)) {
2119     return;
2120   }
2121   MOZ_ASSERT(imageInfo);
2122 
2123   ////////////////////////////////////
2124   // Get source info
2125 
2126   const webgl::FormatUsageInfo* srcUsage;
2127   uint32_t srcTotalWidth;
2128   uint32_t srcTotalHeight;
2129   if (!mContext->BindCurFBForColorRead(funcName, &srcUsage, &srcTotalWidth,
2130                                        &srcTotalHeight)) {
2131     return;
2132   }
2133 
2134   if (!ValidateCopyTexImageForFeedback(funcName, level)) return;
2135 
2136   ////////////////////////////////////
2137   // Check that source and dest info are compatible
2138 
2139   const auto& srcFormat = srcUsage->format;
2140   const auto dstUsage =
2141       ValidateCopyDestUsage(funcName, mContext, srcFormat, internalFormat);
2142   if (!dstUsage) return;
2143 
2144   const auto& dstFormat = dstUsage->format;
2145   if (!ValidateTargetForFormat(funcName, mContext, target, dstFormat)) return;
2146 
2147   if (!mContext->IsWebGL2() && dstFormat->d) {
2148     mContext->ErrorInvalidOperation(
2149         "%s: Function may not be called with format %s.", funcName,
2150         dstFormat->name);
2151     return;
2152   }
2153 
2154   if (!ValidateCopyTexImageFormats(mContext, funcName, srcFormat, dstFormat))
2155     return;
2156 
2157   ////////////////////////////////////
2158   // Do the thing!
2159 
2160   const bool isSubImage = false;
2161   if (!DoCopyTexOrSubImage(mContext, funcName, isSubImage, this, target, level,
2162                            x, y, srcTotalWidth, srcTotalHeight, srcUsage, 0, 0,
2163                            0, width, height, dstUsage)) {
2164     return;
2165   }
2166 
2167   mContext->OnDataAllocCall();
2168 
2169   ////////////////////////////////////
2170   // Update our specification data.
2171 
2172   const bool isDataInitialized = true;
2173   const ImageInfo newImageInfo(dstUsage, width, height, depth,
2174                                isDataInitialized);
2175   SetImageInfo(funcName, imageInfo, newImageInfo);
2176 }
2177 
CopyTexSubImage(const char * funcName,TexImageTarget target,GLint level,GLint xOffset,GLint yOffset,GLint zOffset,GLint x,GLint y,GLsizei rawWidth,GLsizei rawHeight)2178 void WebGLTexture::CopyTexSubImage(const char* funcName, TexImageTarget target,
2179                                    GLint level, GLint xOffset, GLint yOffset,
2180                                    GLint zOffset, GLint x, GLint y,
2181                                    GLsizei rawWidth, GLsizei rawHeight) {
2182   uint32_t width, height, depth;
2183   if (!ValidateExtents(mContext, funcName, rawWidth, rawHeight, 1, 0, &width,
2184                        &height, &depth)) {
2185     return;
2186   }
2187 
2188   ////////////////////////////////////
2189   // Get dest info
2190 
2191   WebGLTexture::ImageInfo* imageInfo;
2192   if (!ValidateTexImageSelection(funcName, target, level, xOffset, yOffset,
2193                                  zOffset, width, height, depth, &imageInfo)) {
2194     return;
2195   }
2196   MOZ_ASSERT(imageInfo);
2197 
2198   auto dstUsage = imageInfo->mFormat;
2199   MOZ_ASSERT(dstUsage);
2200 
2201   auto dstFormat = dstUsage->format;
2202   if (!mContext->IsWebGL2() && dstFormat->d) {
2203     mContext->ErrorInvalidOperation(
2204         "%s: Function may not be called on a texture of"
2205         " format %s.",
2206         funcName, dstFormat->name);
2207     return;
2208   }
2209 
2210   ////////////////////////////////////
2211   // Get source info
2212 
2213   const webgl::FormatUsageInfo* srcUsage;
2214   uint32_t srcTotalWidth;
2215   uint32_t srcTotalHeight;
2216   if (!mContext->BindCurFBForColorRead(funcName, &srcUsage, &srcTotalWidth,
2217                                        &srcTotalHeight)) {
2218     return;
2219   }
2220 
2221   if (!ValidateCopyTexImageForFeedback(funcName, level, zOffset)) return;
2222 
2223   ////////////////////////////////////
2224   // Check that source and dest info are compatible
2225 
2226   auto srcFormat = srcUsage->format;
2227   if (!ValidateCopyTexImageFormats(mContext, funcName, srcFormat, dstFormat))
2228     return;
2229 
2230   ////////////////////////////////////
2231   // Do the thing!
2232 
2233   bool uploadWillInitialize;
2234   if (!EnsureImageDataInitializedForUpload(
2235           this, funcName, target, level, xOffset, yOffset, zOffset, width,
2236           height, depth, imageInfo, &uploadWillInitialize)) {
2237     return;
2238   }
2239 
2240   const bool isSubImage = true;
2241   if (!DoCopyTexOrSubImage(mContext, funcName, isSubImage, this, target, level,
2242                            x, y, srcTotalWidth, srcTotalHeight, srcUsage,
2243                            xOffset, yOffset, zOffset, width, height,
2244                            dstUsage)) {
2245     return;
2246   }
2247 
2248   ////////////////////////////////////
2249   // Update our specification data?
2250 
2251   if (uploadWillInitialize) {
2252     imageInfo->SetIsDataInitialized(true, this);
2253   }
2254 }
2255 
2256 }  // namespace mozilla
2257