Coverage Report

Created: 2018-09-25 14:53

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