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