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