Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/canvas/WebGLTexture.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
#include "GLContext.h"
10
#include "mozilla/dom/WebGLRenderingContextBinding.h"
11
#include "mozilla/gfx/Logging.h"
12
#include "mozilla/MathAlgorithms.h"
13
#include "mozilla/Scoped.h"
14
#include "mozilla/Unused.h"
15
#include "ScopedGLHelpers.h"
16
#include "WebGLContext.h"
17
#include "WebGLContextUtils.h"
18
#include "WebGLFramebuffer.h"
19
#include "WebGLSampler.h"
20
#include "WebGLTexelConversions.h"
21
22
namespace mozilla {
23
24
/*static*/ const WebGLTexture::ImageInfo WebGLTexture::ImageInfo::kUndefined;
25
26
////////////////////////////////////////
27
28
template <typename T>
29
static inline T&
30
Mutable(const T& x)
31
0
{
32
0
    return const_cast<T&>(x);
33
0
}
Unexecuted instantiation: Unified_cpp_dom_canvas5.cpp:mozilla::webgl::FormatUsageInfo const*& mozilla::Mutable<mozilla::webgl::FormatUsageInfo const*>(mozilla::webgl::FormatUsageInfo const* const&)
Unexecuted instantiation: Unified_cpp_dom_canvas5.cpp:unsigned int& mozilla::Mutable<unsigned int>(unsigned int const&)
34
35
void
36
WebGLTexture::ImageInfo::Clear()
37
0
{
38
0
    if (!IsDefined())
39
0
        return;
40
0
41
0
    OnRespecify();
42
0
43
0
    Mutable(mFormat) = LOCAL_GL_NONE;
44
0
    Mutable(mWidth) = 0;
45
0
    Mutable(mHeight) = 0;
46
0
    Mutable(mDepth) = 0;
47
0
48
0
    MOZ_ASSERT(!IsDefined());
49
0
}
50
51
void
52
WebGLTexture::ImageInfo::Set(const ImageInfo& a)
53
0
{
54
0
    MOZ_ASSERT(a.IsDefined());
55
0
56
0
    Mutable(mFormat) = a.mFormat;
57
0
    Mutable(mWidth) = a.mWidth;
58
0
    Mutable(mHeight) = a.mHeight;
59
0
    Mutable(mDepth) = a.mDepth;
60
0
61
0
    mIsDataInitialized = a.mIsDataInitialized;
62
0
63
0
    // But *don't* transfer mAttachPoints!
64
0
    MOZ_ASSERT(a.mAttachPoints.empty());
65
0
    OnRespecify();
66
0
}
67
68
bool
69
WebGLTexture::ImageInfo::IsPowerOfTwo() const
70
0
{
71
0
    return mozilla::IsPowerOfTwo(mWidth) &&
72
0
           mozilla::IsPowerOfTwo(mHeight) &&
73
0
           mozilla::IsPowerOfTwo(mDepth);
74
0
}
75
76
void
77
WebGLTexture::ImageInfo::AddAttachPoint(WebGLFBAttachPoint* attachPoint)
78
0
{
79
0
    const auto pair = mAttachPoints.insert(attachPoint);
80
0
    DebugOnly<bool> didInsert = pair.second;
81
0
    MOZ_ASSERT(didInsert);
82
0
}
83
84
void
85
WebGLTexture::ImageInfo::RemoveAttachPoint(WebGLFBAttachPoint* attachPoint)
86
0
{
87
0
    DebugOnly<size_t> numElemsErased = mAttachPoints.erase(attachPoint);
88
0
    MOZ_ASSERT_IF(IsDefined(), numElemsErased == 1);
89
0
}
90
91
void
92
WebGLTexture::ImageInfo::OnRespecify() const
93
0
{
94
0
    for (auto cur : mAttachPoints) {
95
0
        cur->OnBackingStoreRespecified();
96
0
    }
97
0
}
98
99
size_t
100
WebGLTexture::ImageInfo::MemoryUsage() const
101
0
{
102
0
    if (!IsDefined())
103
0
        return 0;
104
0
105
0
    const auto bytesPerTexel = mFormat->format->estimatedBytesPerPixel;
106
0
    return size_t(mWidth) * size_t(mHeight) * size_t(mDepth) * bytesPerTexel;
107
0
}
108
109
void
110
WebGLTexture::ImageInfo::SetIsDataInitialized(bool isDataInitialized, WebGLTexture* tex)
111
0
{
112
0
    MOZ_ASSERT(tex);
113
0
    MOZ_ASSERT(this >= &tex->mImageInfoArr[0]);
114
0
    MOZ_ASSERT(this < &tex->mImageInfoArr[kMaxLevelCount * kMaxFaceCount]);
115
0
116
0
    mIsDataInitialized = isDataInitialized;
117
0
    tex->InvalidateResolveCache();
118
0
}
119
120
////////////////////////////////////////
121
122
JSObject*
123
0
WebGLTexture::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) {
124
0
    return dom::WebGLTexture_Binding::Wrap(cx, this, givenProto);
125
0
}
126
127
WebGLTexture::WebGLTexture(WebGLContext* webgl, GLuint tex)
128
    : WebGLRefCountedObject(webgl)
129
    , mGLName(tex)
130
    , mTarget(LOCAL_GL_NONE)
131
    , mFaceCount(0)
132
    , mImmutable(false)
133
    , mImmutableLevelCount(0)
134
    , mBaseMipmapLevel(0)
135
    , mMaxMipmapLevel(1000)
136
    , mIsResolved(false)
137
    , mResolved_FakeBlack(FakeBlackType::None)
138
    , mResolved_Swizzle(nullptr)
139
0
{
140
0
    mContext->mTextures.insertBack(this);
141
0
}
142
143
void
144
WebGLTexture::Delete()
145
0
{
146
0
    for (auto& cur : mImageInfoArr) {
147
0
        cur.Clear();
148
0
    }
149
0
150
0
    mContext->gl->fDeleteTextures(1, &mGLName);
151
0
152
0
    LinkedListElement<WebGLTexture>::removeFrom(mContext->mTextures);
153
0
}
154
155
size_t
156
WebGLTexture::MemoryUsage() const
157
0
{
158
0
    if (IsDeleted())
159
0
        return 0;
160
0
161
0
    size_t accum = 0;
162
0
    for (const auto& cur : mImageInfoArr) {
163
0
        accum += cur.MemoryUsage();
164
0
    }
165
0
    return accum;
166
0
}
167
168
void
169
WebGLTexture::SetImageInfo(ImageInfo* target,
170
                           const ImageInfo& newInfo)
171
0
{
172
0
    target->Set(newInfo);
173
0
174
0
    InvalidateResolveCache();
175
0
}
176
177
void
178
WebGLTexture::SetImageInfosAtLevel(uint32_t level,
179
                                   const ImageInfo& newInfo)
180
0
{
181
0
    for (uint8_t i = 0; i < mFaceCount; i++) {
182
0
        ImageInfoAtFace(i, level).Set(newInfo);
183
0
    }
184
0
185
0
    InvalidateResolveCache();
186
0
}
187
188
bool
189
WebGLTexture::IsMipmapComplete(uint32_t texUnit,
190
                               bool* const out_initFailed)
191
0
{
192
0
    *out_initFailed = false;
193
0
    // GLES 3.0.4, p161
194
0
195
0
    uint32_t maxLevel;
196
0
    if (!MaxEffectiveMipmapLevel(texUnit, &maxLevel))
197
0
        return false;
198
0
199
0
    // "* `level_base <= level_max`"
200
0
    if (mBaseMipmapLevel > maxLevel)
201
0
        return false;
202
0
203
0
    // Make a copy so we can modify it.
204
0
    const ImageInfo& baseImageInfo = BaseImageInfo();
205
0
206
0
    // Reference dimensions based on the current level.
207
0
    uint32_t refWidth = baseImageInfo.mWidth;
208
0
    uint32_t refHeight = baseImageInfo.mHeight;
209
0
    uint32_t refDepth = baseImageInfo.mDepth;
210
0
    MOZ_ASSERT(refWidth && refHeight && refDepth);
211
0
212
0
    for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) {
213
0
        if (!EnsureLevelInitialized(level)) {
214
0
            *out_initFailed = true;
215
0
            return false;
216
0
        }
217
0
218
0
        // "A cube map texture is mipmap complete if each of the six texture images,
219
0
        //  considered individually, is mipmap complete."
220
0
221
0
        for (uint8_t face = 0; face < mFaceCount; face++) {
222
0
            const ImageInfo& cur = ImageInfoAtFace(face, level);
223
0
224
0
            // "* The set of mipmap arrays `level_base` through `q` (where `q` is defined
225
0
            //    the "Mipmapping" discussion of section 3.8.10) were each specified with
226
0
            //    the same effective internal format."
227
0
228
0
            // "* The dimensions of the arrays follow the sequence described in the
229
0
            //    "Mipmapping" discussion of section 3.8.10."
230
0
231
0
            if (cur.mWidth != refWidth ||
232
0
                cur.mHeight != refHeight ||
233
0
                cur.mDepth != refDepth ||
234
0
                cur.mFormat != baseImageInfo.mFormat)
235
0
            {
236
0
                return false;
237
0
            }
238
0
        }
239
0
240
0
        // GLES 3.0.4, p158:
241
0
        // "[...] until the last array is reached with dimension 1 x 1 x 1."
242
0
        if (mTarget == LOCAL_GL_TEXTURE_3D) {
243
0
            if (refWidth == 1 &&
244
0
                refHeight == 1 &&
245
0
                refDepth == 1)
246
0
            {
247
0
                break;
248
0
            }
249
0
250
0
            refDepth = std::max(uint32_t(1), refDepth / 2);
251
0
        } else {
252
0
            // TEXTURE_2D_ARRAY may have depth != 1, but that's normal.
253
0
            if (refWidth == 1 &&
254
0
                refHeight == 1)
255
0
            {
256
0
                break;
257
0
            }
258
0
        }
259
0
260
0
        refWidth  = std::max(uint32_t(1), refWidth  / 2);
261
0
        refHeight = std::max(uint32_t(1), refHeight / 2);
262
0
    }
263
0
264
0
    return true;
265
0
}
266
267
bool
268
WebGLTexture::IsCubeComplete() const
269
0
{
270
0
    // GLES 3.0.4, p161
271
0
    // "[...] a cube map texture is cube complete if the following conditions all hold
272
0
    //  true:
273
0
    //  * The `level_base` arrays of each of the six texture images making up the cube map
274
0
    //    have identical, positive, and square dimensions.
275
0
    //  * The `level_base` arrays were each specified with the same effective internal
276
0
    //    format."
277
0
278
0
    // Note that "cube complete" does not imply "mipmap complete".
279
0
280
0
    const ImageInfo& reference = BaseImageInfo();
281
0
    if (!reference.IsDefined())
282
0
        return false;
283
0
284
0
    auto refWidth = reference.mWidth;
285
0
    auto refFormat = reference.mFormat;
286
0
287
0
    for (uint8_t face = 0; face < mFaceCount; face++) {
288
0
        const ImageInfo& cur = ImageInfoAtFace(face, mBaseMipmapLevel);
289
0
        if (!cur.IsDefined())
290
0
            return false;
291
0
292
0
        MOZ_ASSERT(cur.mDepth == 1);
293
0
        if (cur.mFormat != refFormat || // Check effective formats.
294
0
            cur.mWidth != refWidth ||   // Check both width and height against refWidth to
295
0
            cur.mHeight != refWidth)    // to enforce positive and square dimensions.
296
0
        {
297
0
            return false;
298
0
        }
299
0
    }
300
0
301
0
    return true;
302
0
}
303
304
bool
305
WebGLTexture::IsComplete(uint32_t texUnit,
306
                         const char** const out_reason, bool* const out_initFailed)
307
0
{
308
0
    *out_initFailed = false;
309
0
310
0
    const auto maxLevel = kMaxLevelCount - 1;
311
0
    if (mBaseMipmapLevel > maxLevel) {
312
0
        *out_reason = "`level_base` too high.";
313
0
        return false;
314
0
    }
315
0
316
0
    // Texture completeness is established at GLES 3.0.4, p160-161.
317
0
    // "[A] texture is complete unless any of the following conditions hold true:"
318
0
319
0
    // "* Any dimension of the `level_base` array is not positive."
320
0
    const ImageInfo& baseImageInfo = BaseImageInfo();
321
0
    if (!baseImageInfo.IsDefined()) {
322
0
        // In case of undefined texture image, we don't print any message because this is
323
0
        // a very common and often legitimate case (asynchronous texture loading).
324
0
        *out_reason = nullptr;
325
0
        return false;
326
0
    }
327
0
328
0
    if (!baseImageInfo.mWidth || !baseImageInfo.mHeight || !baseImageInfo.mDepth) {
329
0
        *out_reason = "The dimensions of `level_base` are not all positive.";
330
0
        return false;
331
0
    }
332
0
333
0
    // "* The texture is a cube map texture, and is not cube complete."
334
0
    if (IsCubeMap() && !IsCubeComplete()) {
335
0
        *out_reason = "Cubemaps must be \"cube complete\".";
336
0
        return false;
337
0
    }
338
0
339
0
    const auto* samplingState = &mSamplingState;
340
0
    const auto& sampler = mContext->mBoundSamplers[texUnit];
341
0
    if (sampler) {
342
0
        samplingState = &(sampler->State());
343
0
    }
344
0
345
0
    const auto& minFilter = samplingState->minFilter;
346
0
    const auto& magFilter = samplingState->magFilter;
347
0
348
0
    // "* The minification filter requires a mipmap (is neither NEAREST nor LINEAR) and
349
0
    //    the texture is not mipmap complete."
350
0
    const bool requiresMipmap = (minFilter != LOCAL_GL_NEAREST &&
351
0
                                 minFilter != LOCAL_GL_LINEAR);
352
0
    if (requiresMipmap && !IsMipmapComplete(texUnit, out_initFailed)) {
353
0
        if (*out_initFailed)
354
0
            return false;
355
0
356
0
        *out_reason = "Because the minification filter requires mipmapping, the texture"
357
0
                      " must be \"mipmap complete\".";
358
0
        return false;
359
0
    }
360
0
361
0
    const bool isMinFilteringNearest = (minFilter == LOCAL_GL_NEAREST ||
362
0
                                        minFilter == LOCAL_GL_NEAREST_MIPMAP_NEAREST);
363
0
    const bool isMagFilteringNearest = (magFilter == LOCAL_GL_NEAREST);
364
0
    const bool isFilteringNearestOnly = (isMinFilteringNearest && isMagFilteringNearest);
365
0
    if (!isFilteringNearestOnly) {
366
0
        auto formatUsage = baseImageInfo.mFormat;
367
0
        auto format = formatUsage->format;
368
0
369
0
        bool isFilterable = formatUsage->isFilterable;
370
0
371
0
        // "* The effective internal format specified for the texture arrays is a sized
372
0
        //    internal depth or depth and stencil format, the value of
373
0
        //    TEXTURE_COMPARE_MODE is NONE[1], and either the magnification filter is not
374
0
        //    NEAREST, or the minification filter is neither NEAREST nor
375
0
        //    NEAREST_MIPMAP_NEAREST."
376
0
        // [1]: This sounds suspect, but is explicitly noted in the change log for GLES
377
0
        //      3.0.1:
378
0
        //      "* Clarify that a texture is incomplete if it has a depth component, no
379
0
        //         shadow comparison, and linear filtering (also Bug 9481)."
380
0
        if (format->d && samplingState->compareMode != LOCAL_GL_NONE) {
381
0
            isFilterable = true;
382
0
        }
383
0
384
0
        // "* The effective internal format specified for the texture arrays is a sized
385
0
        //    internal color format that is not texture-filterable, and either the
386
0
        //    magnification filter is not NEAREST or the minification filter is neither
387
0
        //    NEAREST nor NEAREST_MIPMAP_NEAREST."
388
0
        // Since all (GLES3) unsized color formats are filterable just like their sized
389
0
        // equivalents, we don't have to care whether its sized or not.
390
0
        if (!isFilterable) {
391
0
            *out_reason = "Because minification or magnification filtering is not NEAREST"
392
0
                          " or NEAREST_MIPMAP_NEAREST, and the texture's format must be"
393
0
                          " \"texture-filterable\".";
394
0
            return false;
395
0
        }
396
0
    }
397
0
398
0
    // Texture completeness is effectively (though not explicitly) amended for GLES2 by
399
0
    // the "Texture Access" section under $3.8 "Fragment Shaders". This also applies to
400
0
    // vertex shaders, as noted on GLES 2.0.25, p41.
401
0
    if (!mContext->IsWebGL2()) {
402
0
        // GLES 2.0.25, p87-88:
403
0
        // "Calling a sampler from a fragment shader will return (R,G,B,A)=(0,0,0,1) if
404
0
        //  any of the following conditions are true:"
405
0
406
0
        // "* A two-dimensional sampler is called, the minification filter is one that
407
0
        //    requires a mipmap[...], and the sampler's associated texture object is not
408
0
        //    complete[.]"
409
0
        // (already covered)
410
0
411
0
        // "* A two-dimensional sampler is called, the minification filter is not one that
412
0
        //    requires a mipmap (either NEAREST nor[sic] LINEAR), and either dimension of
413
0
        //    the level zero array of the associated texture object is not positive."
414
0
        // (already covered)
415
0
416
0
        // "* A two-dimensional sampler is called, the corresponding texture image is a
417
0
        //    non-power-of-two image[...], and either the texture wrap mode is not
418
0
        //    CLAMP_TO_EDGE, or the minification filter is neither NEAREST nor LINEAR."
419
0
420
0
        // "* A cube map sampler is called, any of the corresponding texture images are
421
0
        //    non-power-of-two images, and either the texture wrap mode is not
422
0
        //    CLAMP_TO_EDGE, or the minification filter is neither NEAREST nor LINEAR."
423
0
        if (!baseImageInfo.IsPowerOfTwo()) {
424
0
            // "either the texture wrap mode is not CLAMP_TO_EDGE"
425
0
            if (samplingState->wrapS != LOCAL_GL_CLAMP_TO_EDGE ||
426
0
                samplingState->wrapT != LOCAL_GL_CLAMP_TO_EDGE)
427
0
            {
428
0
                *out_reason = "Non-power-of-two textures must have a wrap mode of"
429
0
                              " CLAMP_TO_EDGE.";
430
0
                return false;
431
0
            }
432
0
433
0
            // "or the minification filter is neither NEAREST nor LINEAR"
434
0
            if (requiresMipmap) {
435
0
                *out_reason = "Mipmapping requires power-of-two textures.";
436
0
                return false;
437
0
            }
438
0
        }
439
0
440
0
        // "* A cube map sampler is called, and either the corresponding cube map texture
441
0
        //    image is not cube complete, or TEXTURE_MIN_FILTER is one that requires a
442
0
        //    mipmap and the texture is not mipmap cube complete."
443
0
        // (already covered)
444
0
    }
445
0
446
0
    if (!EnsureLevelInitialized(mBaseMipmapLevel)) {
447
0
        *out_initFailed = true;
448
0
        return false;
449
0
    }
450
0
451
0
    return true;
452
0
}
453
454
bool
455
WebGLTexture::MaxEffectiveMipmapLevel(uint32_t texUnit, uint32_t* const out) const
456
0
{
457
0
    const auto* samplingState = &mSamplingState;
458
0
    const auto& sampler = mContext->mBoundSamplers[texUnit];
459
0
    if (sampler) {
460
0
        samplingState = &(sampler->State());
461
0
    }
462
0
463
0
    const auto& minFilter = samplingState->minFilter;
464
0
    if (minFilter == LOCAL_GL_NEAREST ||
465
0
        minFilter == LOCAL_GL_LINEAR)
466
0
    {
467
0
        // No extra mips used.
468
0
        *out = mBaseMipmapLevel;
469
0
        return true;
470
0
    }
471
0
472
0
    const auto& imageInfo = BaseImageInfo();
473
0
    if (!imageInfo.IsDefined())
474
0
        return false;
475
0
476
0
    uint32_t maxLevelBySize = mBaseMipmapLevel + imageInfo.PossibleMipmapLevels() - 1;
477
0
    *out = std::min<uint32_t>(maxLevelBySize, mMaxMipmapLevel);
478
0
    return true;
479
0
}
480
481
bool
482
WebGLTexture::GetFakeBlackType(uint32_t texUnit,
483
                               FakeBlackType* const out_fakeBlack)
484
0
{
485
0
    const char* incompleteReason;
486
0
    bool initFailed = false;
487
0
    if (!IsComplete(texUnit, &incompleteReason, &initFailed)) {
488
0
        if (initFailed) {
489
0
            mContext->ErrorOutOfMemory("Failed to initialize texture data.");
490
0
            return false; // The world just exploded.
491
0
        }
492
0
493
0
        if (incompleteReason) {
494
0
            mContext->GenerateWarning("Active texture %u for target 0x%04x is"
495
0
                                      " 'incomplete', and will be rendered as"
496
0
                                      " RGBA(0,0,0,1), as per the GLES 2.0.24 $3.8.2: %s",
497
0
                                      texUnit, mTarget.get(),
498
0
                                      incompleteReason);
499
0
        }
500
0
        *out_fakeBlack = FakeBlackType::RGBA0001;
501
0
        return true;
502
0
    }
503
0
504
0
505
0
    *out_fakeBlack = FakeBlackType::None;
506
0
    return true;
507
0
}
508
509
static void
510
SetSwizzle(gl::GLContext* gl, TexTarget target, const GLint* swizzle)
511
0
{
512
0
    static const GLint kNoSwizzle[4] = { LOCAL_GL_RED, LOCAL_GL_GREEN, LOCAL_GL_BLUE,
513
0
                                         LOCAL_GL_ALPHA };
514
0
    if (!swizzle) {
515
0
        swizzle = kNoSwizzle;
516
0
    } else if (!gl->IsSupported(gl::GLFeature::texture_swizzle)) {
517
0
        MOZ_CRASH("GFX: Needs swizzle feature to swizzle!");
518
0
    }
519
0
520
0
    gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_R, swizzle[0]);
521
0
    gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_G, swizzle[1]);
522
0
    gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_B, swizzle[2]);
523
0
    gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_A, swizzle[3]);
524
0
}
525
526
bool
527
WebGLTexture::ResolveForDraw(uint32_t texUnit,
528
                             FakeBlackType* const out_fakeBlack)
529
0
{
530
0
    if (!mIsResolved) {
531
0
        if (!GetFakeBlackType(texUnit, &mResolved_FakeBlack))
532
0
            return false;
533
0
534
0
        // Check which swizzle we should use. Since the texture must be complete at this
535
0
        // point, just grab the format off any valid image.
536
0
        const GLint* newSwizzle = nullptr;
537
0
        if (mResolved_FakeBlack == FakeBlackType::None) {
538
0
            const auto& cur = ImageInfoAtFace(0, mBaseMipmapLevel);
539
0
            newSwizzle = cur.mFormat->textureSwizzleRGBA;
540
0
        }
541
0
542
0
        // Only set the swizzle if it changed since last time we did it.
543
0
        if (newSwizzle != mResolved_Swizzle) {
544
0
            mResolved_Swizzle = newSwizzle;
545
0
546
0
            // Set the new swizzle!
547
0
            mContext->gl->fActiveTexture(LOCAL_GL_TEXTURE0 + texUnit);
548
0
            SetSwizzle(mContext->gl, mTarget, mResolved_Swizzle);
549
0
            mContext->gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mContext->mActiveTexture);
550
0
        }
551
0
552
0
        mIsResolved = true;
553
0
    }
554
0
555
0
    *out_fakeBlack = mResolved_FakeBlack;
556
0
    return true;
557
0
}
558
559
bool
560
WebGLTexture::EnsureImageDataInitialized(TexImageTarget target,
561
                                         uint32_t level)
562
0
{
563
0
    auto& imageInfo = ImageInfoAt(target, level);
564
0
    if (!imageInfo.IsDefined())
565
0
        return true;
566
0
567
0
    if (imageInfo.IsDataInitialized())
568
0
        return true;
569
0
570
0
    return InitializeImageData(target, level);
571
0
}
572
573
bool
574
WebGLTexture::EnsureLevelInitialized(uint32_t level)
575
0
{
576
0
    if (mTarget != LOCAL_GL_TEXTURE_CUBE_MAP)
577
0
        return EnsureImageDataInitialized(mTarget.get(), level);
578
0
579
0
    for (GLenum texImageTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
580
0
         texImageTarget <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
581
0
         ++texImageTarget)
582
0
    {
583
0
        if (!EnsureImageDataInitialized(texImageTarget, level))
584
0
            return false;
585
0
    }
586
0
    return true;
587
0
}
588
589
static void
590
ZeroANGLEDepthTexture(WebGLContext* webgl, GLuint tex,
591
                      const webgl::FormatUsageInfo* usage, uint32_t width,
592
                      uint32_t height)
593
0
{
594
0
    const auto& format = usage->format;
595
0
    GLenum attachPoint = 0;
596
0
    GLbitfield clearBits = 0;
597
0
598
0
    if (format->d) {
599
0
        attachPoint = LOCAL_GL_DEPTH_ATTACHMENT;
600
0
        clearBits |= LOCAL_GL_DEPTH_BUFFER_BIT;
601
0
    }
602
0
603
0
    if (format->s) {
604
0
        attachPoint = (format->d ? LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
605
0
                                 : LOCAL_GL_STENCIL_ATTACHMENT);
606
0
        clearBits |= LOCAL_GL_STENCIL_BUFFER_BIT;
607
0
    }
608
0
609
0
    MOZ_RELEASE_ASSERT(attachPoint && clearBits, "GFX: No bits cleared.");
610
0
611
0
    ////
612
0
    const auto& gl = webgl->gl;
613
0
    MOZ_ASSERT(gl->IsCurrent());
614
0
615
0
    gl::ScopedFramebuffer scopedFB(gl);
616
0
    const gl::ScopedBindFramebuffer scopedBindFB(gl, scopedFB.FB());
617
0
618
0
    gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, attachPoint, LOCAL_GL_TEXTURE_2D,
619
0
                              tex, 0);
620
0
621
0
    const auto& status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
622
0
    MOZ_RELEASE_ASSERT(status == LOCAL_GL_FRAMEBUFFER_COMPLETE);
623
0
624
0
    ////
625
0
626
0
    const bool fakeNoAlpha = false;
627
0
    webgl->ForceClearFramebufferWithDefaultValues(clearBits, fakeNoAlpha);
628
0
}
629
630
static bool
631
ZeroTextureData(WebGLContext* webgl, GLuint tex,
632
                TexImageTarget target, uint32_t level,
633
                const webgl::FormatUsageInfo* usage, uint32_t width, uint32_t height,
634
                uint32_t depth)
635
0
{
636
0
    // This has two usecases:
637
0
    // 1. Lazy zeroing of uninitialized textures:
638
0
    //    a. Before draw, when FakeBlack isn't viable. (TexStorage + Draw*)
639
0
    //    b. Before partial upload. (TexStorage + TexSubImage)
640
0
    // 2. Zero subrects from out-of-bounds blits. (CopyTex(Sub)Image)
641
0
642
0
    // We have no sympathy for any of these cases.
643
0
644
0
    // "Doctor, it hurts when I do this!" "Well don't do that!"
645
0
    webgl->GenerateWarning("This operation requires zeroing texture data. This is"
646
0
                           " slow.");
647
0
648
0
    gl::GLContext* gl = webgl->GL();
649
0
650
0
    GLenum scopeBindTarget;
651
0
    switch (target.get()) {
652
0
    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
653
0
    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
654
0
    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
655
0
    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
656
0
    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
657
0
    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
658
0
        scopeBindTarget = LOCAL_GL_TEXTURE_CUBE_MAP;
659
0
        break;
660
0
    default:
661
0
        scopeBindTarget = target.get();
662
0
        break;
663
0
    }
664
0
    const gl::ScopedBindTexture scopeBindTexture(gl, tex, scopeBindTarget);
665
0
    auto compression = usage->format->compression;
666
0
    if (compression) {
667
0
        auto sizedFormat = usage->format->sizedFormat;
668
0
        MOZ_RELEASE_ASSERT(sizedFormat, "GFX: texture sized format not set");
669
0
670
0
        const auto fnSizeInBlocks = [](CheckedUint32 pixels, uint8_t pixelsPerBlock) {
671
0
            return RoundUpToMultipleOf(pixels, pixelsPerBlock) / pixelsPerBlock;
672
0
        };
673
0
674
0
        const auto widthBlocks = fnSizeInBlocks(width, compression->blockWidth);
675
0
        const auto heightBlocks = fnSizeInBlocks(height, compression->blockHeight);
676
0
677
0
        CheckedUint32 checkedByteCount = compression->bytesPerBlock;
678
0
        checkedByteCount *= widthBlocks;
679
0
        checkedByteCount *= heightBlocks;
680
0
        checkedByteCount *= depth;
681
0
682
0
        if (!checkedByteCount.isValid())
683
0
            return false;
684
0
685
0
        const size_t byteCount = checkedByteCount.value();
686
0
687
0
        UniqueBuffer zeros = calloc(1, byteCount);
688
0
        if (!zeros)
689
0
            return false;
690
0
691
0
        ScopedUnpackReset scopedReset(webgl);
692
0
        gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it
693
0
                                                        // well.
694
0
695
0
        const auto error = DoCompressedTexSubImage(gl, target.get(), level, 0, 0, 0,
696
0
                                                   width, height, depth, sizedFormat,
697
0
                                                   byteCount, zeros.get());
698
0
        return !error;
699
0
    }
700
0
701
0
    const auto driverUnpackInfo = usage->idealUnpack;
702
0
    MOZ_RELEASE_ASSERT(driverUnpackInfo, "GFX: ideal unpack info not set.");
703
0
704
0
    if (webgl->IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture) &&
705
0
        gl->IsANGLE() &&
706
0
        usage->format->d)
707
0
    {
708
0
        // ANGLE_depth_texture does not allow uploads, so we have to clear.
709
0
        // (Restriction because of D3D9)
710
0
        MOZ_ASSERT(target == LOCAL_GL_TEXTURE_2D);
711
0
        MOZ_ASSERT(level == 0);
712
0
        ZeroANGLEDepthTexture(webgl, tex, usage, width, height);
713
0
        return true;
714
0
    }
715
0
716
0
    const webgl::PackingInfo packing = driverUnpackInfo->ToPacking();
717
0
718
0
    const auto bytesPerPixel = webgl::BytesPerPixel(packing);
719
0
720
0
    CheckedUint32 checkedByteCount = bytesPerPixel;
721
0
    checkedByteCount *= width;
722
0
    checkedByteCount *= height;
723
0
    checkedByteCount *= depth;
724
0
725
0
    if (!checkedByteCount.isValid())
726
0
        return false;
727
0
728
0
    const size_t byteCount = checkedByteCount.value();
729
0
730
0
    UniqueBuffer zeros = calloc(1, byteCount);
731
0
    if (!zeros)
732
0
        return false;
733
0
734
0
    ScopedUnpackReset scopedReset(webgl);
735
0
    gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it well.
736
0
    const auto error = DoTexSubImage(gl, target, level, 0, 0, 0, width, height, depth,
737
0
                                     packing, zeros.get());
738
0
    return !error;
739
0
}
740
741
bool
742
WebGLTexture::InitializeImageData(TexImageTarget target,
743
                                  uint32_t level)
744
0
{
745
0
    auto& imageInfo = ImageInfoAt(target, level);
746
0
    MOZ_ASSERT(imageInfo.IsDefined());
747
0
    MOZ_ASSERT(!imageInfo.IsDataInitialized());
748
0
749
0
    const auto& usage = imageInfo.mFormat;
750
0
    const auto& width = imageInfo.mWidth;
751
0
    const auto& height = imageInfo.mHeight;
752
0
    const auto& depth = imageInfo.mDepth;
753
0
754
0
    if (!ZeroTextureData(mContext, mGLName, target, level, usage, width, height,
755
0
                         depth))
756
0
    {
757
0
        return false;
758
0
    }
759
0
760
0
    imageInfo.SetIsDataInitialized(true, this);
761
0
    return true;
762
0
}
763
764
void
765
WebGLTexture::ClampLevelBaseAndMax()
766
0
{
767
0
    if (!mImmutable)
768
0
        return;
769
0
770
0
    // GLES 3.0.4, p158:
771
0
    // "For immutable-format textures, `level_base` is clamped to the range
772
0
    //  `[0, levels-1]`, `level_max` is then clamped to the range `
773
0
    //  `[level_base, levels-1]`, where `levels` is the parameter passed to
774
0
    //   TexStorage* for the texture object."
775
0
    mBaseMipmapLevel = Clamp<uint32_t>(mBaseMipmapLevel, 0, mImmutableLevelCount - 1);
776
0
    mMaxMipmapLevel = Clamp<uint32_t>(mMaxMipmapLevel, mBaseMipmapLevel,
777
0
                                      mImmutableLevelCount - 1);
778
0
}
779
780
void
781
WebGLTexture::PopulateMipChain(uint32_t firstLevel,
782
                               uint32_t lastLevel)
783
0
{
784
0
    const ImageInfo& baseImageInfo = ImageInfoAtFace(0, firstLevel);
785
0
    MOZ_ASSERT(baseImageInfo.IsDefined());
786
0
787
0
    uint32_t refWidth = baseImageInfo.mWidth;
788
0
    uint32_t refHeight = baseImageInfo.mHeight;
789
0
    uint32_t refDepth = baseImageInfo.mDepth;
790
0
    if (!refWidth || !refHeight || !refDepth)
791
0
        return;
792
0
793
0
    for (uint32_t level = firstLevel + 1; level <= lastLevel; level++) {
794
0
        bool isMinimal = (refWidth == 1 &&
795
0
                          refHeight == 1);
796
0
        if (mTarget == LOCAL_GL_TEXTURE_3D) {
797
0
            isMinimal &= (refDepth == 1);
798
0
        }
799
0
800
0
        // Higher levels are unaffected.
801
0
        if (isMinimal)
802
0
            break;
803
0
804
0
        refWidth = std::max(uint32_t(1), refWidth / 2);
805
0
        refHeight = std::max(uint32_t(1), refHeight / 2);
806
0
        if (mTarget == LOCAL_GL_TEXTURE_3D) { // But not TEXTURE_2D_ARRAY!
807
0
            refDepth = std::max(uint32_t(1), refDepth / 2);
808
0
        }
809
0
810
0
        const ImageInfo cur(baseImageInfo.mFormat, refWidth, refHeight, refDepth,
811
0
                            baseImageInfo.IsDataInitialized());
812
0
813
0
        SetImageInfosAtLevel(level, cur);
814
0
    }
815
0
}
816
817
//////////////////////////////////////////////////////////////////////////////////////////
818
// GL calls
819
820
bool
821
WebGLTexture::BindTexture(TexTarget texTarget)
822
0
{
823
0
    if (IsDeleted()) {
824
0
        mContext->ErrorInvalidOperation("bindTexture: Cannot bind a deleted object.");
825
0
        return false;
826
0
    }
827
0
828
0
    const bool isFirstBinding = !HasEverBeenBound();
829
0
    if (!isFirstBinding && mTarget != texTarget) {
830
0
        mContext->ErrorInvalidOperation("bindTexture: This texture has already been bound"
831
0
                                        " to a different target.");
832
0
        return false;
833
0
    }
834
0
835
0
    mTarget = texTarget;
836
0
837
0
    mContext->gl->fBindTexture(mTarget.get(), mGLName);
838
0
839
0
    if (isFirstBinding) {
840
0
        mFaceCount = IsCubeMap() ? 6 : 1;
841
0
842
0
        gl::GLContext* gl = mContext->gl;
843
0
844
0
        // Thanks to the WebKit people for finding this out: GL_TEXTURE_WRAP_R
845
0
        // is not present in GLES 2, but is present in GL and it seems as if for
846
0
        // cube maps we need to set it to GL_CLAMP_TO_EDGE to get the expected
847
0
        // GLES behavior.
848
0
        // If we are WebGL 2 though, we'll want to leave it as REPEAT.
849
0
        const bool hasWrapR = gl->IsSupported(gl::GLFeature::texture_3D);
850
0
        if (IsCubeMap() && hasWrapR && !mContext->IsWebGL2()) {
851
0
            gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_WRAP_R,
852
0
                               LOCAL_GL_CLAMP_TO_EDGE);
853
0
        }
854
0
    }
855
0
856
0
    return true;
857
0
}
858
859
860
void
861
WebGLTexture::GenerateMipmap(TexTarget texTarget)
862
0
{\
863
0
    // GLES 3.0.4 p160:
864
0
    // "Mipmap generation replaces texel array levels level base + 1 through q with arrays
865
0
    //  derived from the level base array, regardless of their previous contents. All
866
0
    //  other mipmap arrays, including the level base array, are left unchanged by this
867
0
    //  computation."
868
0
    const ImageInfo& baseImageInfo = BaseImageInfo();
869
0
    if (!baseImageInfo.IsDefined()) {
870
0
        mContext->ErrorInvalidOperation("The base level of the texture is not"
871
0
                                        " defined.");
872
0
        return;
873
0
    }
874
0
875
0
    if (IsCubeMap() && !IsCubeComplete()) {
876
0
      mContext->ErrorInvalidOperation("Cube maps must be \"cube complete\".");
877
0
      return;
878
0
    }
879
0
880
0
    const auto format = baseImageInfo.mFormat->format;
881
0
    if (!mContext->IsWebGL2()) {
882
0
        if (!baseImageInfo.IsPowerOfTwo()) {
883
0
            mContext->ErrorInvalidOperation("The base level of the texture does not"
884
0
                                            " have power-of-two dimensions.");
885
0
            return;
886
0
        }
887
0
        if (format->isSRGB) {
888
0
            mContext->ErrorInvalidOperation("EXT_sRGB forbids GenerateMipmap with"
889
0
                                            " sRGB.");
890
0
            return;
891
0
        }
892
0
    }
893
0
894
0
    if (format->compression) {
895
0
        mContext->ErrorInvalidOperation("Texture data at base level is compressed.");
896
0
        return;
897
0
    }
898
0
899
0
    if (format->d) {
900
0
        mContext->ErrorInvalidOperation("Depth textures are not supported.");
901
0
        return;
902
0
    }
903
0
904
0
    // OpenGL ES 3.0.4 p160:
905
0
    // If the level base array was not specified with an unsized internal format from
906
0
    // table 3.3 or a sized internal format that is both color-renderable and
907
0
    // texture-filterable according to table 3.13, an INVALID_OPERATION error
908
0
    // is generated.
909
0
    const auto usage = baseImageInfo.mFormat;
910
0
    bool canGenerateMipmap = (usage->IsRenderable() && usage->isFilterable);
911
0
    switch (usage->format->effectiveFormat) {
912
0
    case webgl::EffectiveFormat::Luminance8:
913
0
    case webgl::EffectiveFormat::Alpha8:
914
0
    case webgl::EffectiveFormat::Luminance8Alpha8:
915
0
        // Non-color-renderable formats from Table 3.3.
916
0
        canGenerateMipmap = true;
917
0
        break;
918
0
    default:
919
0
        break;
920
0
    }
921
0
922
0
    if (!canGenerateMipmap) {
923
0
        mContext->ErrorInvalidOperation("Texture at base level is not unsized"
924
0
                                        " internal format or is not"
925
0
                                        " color-renderable or texture-filterable.");
926
0
        return;
927
0
    }
928
0
929
0
    // Done with validation. Do the operation.
930
0
931
0
    gl::GLContext* gl = mContext->gl;
932
0
933
0
    if (gl->WorkAroundDriverBugs()) {
934
0
        // bug 696495 - to work around failures in the texture-mips.html test on various drivers, we
935
0
        // set the minification filter before calling glGenerateMipmap. This should not carry a significant performance
936
0
        // overhead so we do it unconditionally.
937
0
        //
938
0
        // note that the choice of GL_NEAREST_MIPMAP_NEAREST really matters. See Chromium bug 101105.
939
0
        gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_MIN_FILTER,
940
0
                           LOCAL_GL_NEAREST_MIPMAP_NEAREST);
941
0
        gl->fGenerateMipmap(texTarget.get());
942
0
        gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_MIN_FILTER,
943
0
                           mSamplingState.minFilter.get());
944
0
    } else {
945
0
        gl->fGenerateMipmap(texTarget.get());
946
0
    }
947
0
948
0
    // Record the results.
949
0
    // Note that we don't use MaxEffectiveMipmapLevel() here, since that returns
950
0
    // mBaseMipmapLevel if the min filter doesn't require mipmaps.
951
0
    const uint32_t maxLevel = mBaseMipmapLevel + baseImageInfo.PossibleMipmapLevels() - 1;
952
0
    PopulateMipChain(mBaseMipmapLevel, maxLevel);
953
0
}
954
955
JS::Value
956
WebGLTexture::GetTexParameter(TexTarget texTarget, GLenum pname)
957
0
{
958
0
    GLint i = 0;
959
0
    GLfloat f = 0.0f;
960
0
961
0
    switch (pname) {
962
0
    case LOCAL_GL_TEXTURE_BASE_LEVEL:
963
0
        return JS::NumberValue(mBaseMipmapLevel);
964
0
965
0
    case LOCAL_GL_TEXTURE_MAX_LEVEL:
966
0
        return JS::NumberValue(mMaxMipmapLevel);
967
0
968
0
    case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT:
969
0
        return JS::BooleanValue(mImmutable);
970
0
971
0
    case LOCAL_GL_TEXTURE_IMMUTABLE_LEVELS:
972
0
        return JS::NumberValue(uint32_t(mImmutableLevelCount));
973
0
974
0
    case LOCAL_GL_TEXTURE_MIN_FILTER:
975
0
    case LOCAL_GL_TEXTURE_MAG_FILTER:
976
0
    case LOCAL_GL_TEXTURE_WRAP_S:
977
0
    case LOCAL_GL_TEXTURE_WRAP_T:
978
0
    case LOCAL_GL_TEXTURE_WRAP_R:
979
0
    case LOCAL_GL_TEXTURE_COMPARE_MODE:
980
0
    case LOCAL_GL_TEXTURE_COMPARE_FUNC:
981
0
        mContext->gl->fGetTexParameteriv(texTarget.get(), pname, &i);
982
0
        return JS::NumberValue(uint32_t(i));
983
0
984
0
    case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
985
0
    case LOCAL_GL_TEXTURE_MAX_LOD:
986
0
    case LOCAL_GL_TEXTURE_MIN_LOD:
987
0
        mContext->gl->fGetTexParameterfv(texTarget.get(), pname, &f);
988
0
        return JS::NumberValue(float(f));
989
0
990
0
    default:
991
0
        MOZ_CRASH("GFX: Unhandled pname.");
992
0
    }
993
0
}
994
995
bool
996
WebGLTexture::IsTexture() const
997
0
{
998
0
    return HasEverBeenBound() && !IsDeleted();
999
0
}
1000
1001
// Here we have to support all pnames with both int and float params.
1002
// See this discussion:
1003
//   https://www.khronos.org/webgl/public-mailing-list/archives/1008/msg00014.html
1004
void
1005
WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname, const FloatOrInt& param)
1006
0
{
1007
0
    bool isPNameValid = false;
1008
0
    switch (pname) {
1009
0
    // GLES 2.0.25 p76:
1010
0
    case LOCAL_GL_TEXTURE_WRAP_S:
1011
0
    case LOCAL_GL_TEXTURE_WRAP_T:
1012
0
    case LOCAL_GL_TEXTURE_MIN_FILTER:
1013
0
    case LOCAL_GL_TEXTURE_MAG_FILTER:
1014
0
        isPNameValid = true;
1015
0
        break;
1016
0
1017
0
    // GLES 3.0.4 p149-150:
1018
0
    case LOCAL_GL_TEXTURE_BASE_LEVEL:
1019
0
    case LOCAL_GL_TEXTURE_COMPARE_MODE:
1020
0
    case LOCAL_GL_TEXTURE_COMPARE_FUNC:
1021
0
    case LOCAL_GL_TEXTURE_MAX_LEVEL:
1022
0
    case LOCAL_GL_TEXTURE_MAX_LOD:
1023
0
    case LOCAL_GL_TEXTURE_MIN_LOD:
1024
0
    case LOCAL_GL_TEXTURE_WRAP_R:
1025
0
        if (mContext->IsWebGL2())
1026
0
            isPNameValid = true;
1027
0
        break;
1028
0
1029
0
    case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
1030
0
        if (mContext->IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic))
1031
0
            isPNameValid = true;
1032
0
        break;
1033
0
    }
1034
0
1035
0
    if (!isPNameValid) {
1036
0
        mContext->ErrorInvalidEnumInfo("texParameter: pname", pname);
1037
0
        return;
1038
0
    }
1039
0
1040
0
    ////////////////
1041
0
    // Validate params and invalidate if needed.
1042
0
1043
0
    bool paramBadEnum = false;
1044
0
    bool paramBadValue = false;
1045
0
1046
0
    switch (pname) {
1047
0
    case LOCAL_GL_TEXTURE_BASE_LEVEL:
1048
0
    case LOCAL_GL_TEXTURE_MAX_LEVEL:
1049
0
        paramBadValue = (param.i < 0);
1050
0
        break;
1051
0
1052
0
    case LOCAL_GL_TEXTURE_COMPARE_MODE:
1053
0
        paramBadValue = (param.i != LOCAL_GL_NONE &&
1054
0
                         param.i != LOCAL_GL_COMPARE_REF_TO_TEXTURE);
1055
0
        break;
1056
0
1057
0
    case LOCAL_GL_TEXTURE_COMPARE_FUNC:
1058
0
        switch (param.i) {
1059
0
        case LOCAL_GL_LEQUAL:
1060
0
        case LOCAL_GL_GEQUAL:
1061
0
        case LOCAL_GL_LESS:
1062
0
        case LOCAL_GL_GREATER:
1063
0
        case LOCAL_GL_EQUAL:
1064
0
        case LOCAL_GL_NOTEQUAL:
1065
0
        case LOCAL_GL_ALWAYS:
1066
0
        case LOCAL_GL_NEVER:
1067
0
            break;
1068
0
1069
0
        default:
1070
0
            paramBadValue = true;
1071
0
            break;
1072
0
        }
1073
0
        break;
1074
0
1075
0
    case LOCAL_GL_TEXTURE_MIN_FILTER:
1076
0
        switch (param.i) {
1077
0
        case LOCAL_GL_NEAREST:
1078
0
        case LOCAL_GL_LINEAR:
1079
0
        case LOCAL_GL_NEAREST_MIPMAP_NEAREST:
1080
0
        case LOCAL_GL_LINEAR_MIPMAP_NEAREST:
1081
0
        case LOCAL_GL_NEAREST_MIPMAP_LINEAR:
1082
0
        case LOCAL_GL_LINEAR_MIPMAP_LINEAR:
1083
0
            break;
1084
0
1085
0
        default:
1086
0
            paramBadEnum = true;
1087
0
            break;
1088
0
        }
1089
0
        break;
1090
0
1091
0
    case LOCAL_GL_TEXTURE_MAG_FILTER:
1092
0
        switch (param.i) {
1093
0
        case LOCAL_GL_NEAREST:
1094
0
        case LOCAL_GL_LINEAR:
1095
0
            break;
1096
0
1097
0
        default:
1098
0
            paramBadEnum = true;
1099
0
            break;
1100
0
        }
1101
0
        break;
1102
0
1103
0
    case LOCAL_GL_TEXTURE_WRAP_S:
1104
0
    case LOCAL_GL_TEXTURE_WRAP_T:
1105
0
    case LOCAL_GL_TEXTURE_WRAP_R:
1106
0
        switch (param.i) {
1107
0
        case LOCAL_GL_CLAMP_TO_EDGE:
1108
0
        case LOCAL_GL_MIRRORED_REPEAT:
1109
0
        case LOCAL_GL_REPEAT:
1110
0
            break;
1111
0
1112
0
        default:
1113
0
            paramBadEnum = true;
1114
0
            break;
1115
0
        }
1116
0
        break;
1117
0
1118
0
    case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
1119
0
        if (param.f < 1.0f)
1120
0
            paramBadValue = true;
1121
0
1122
0
        break;
1123
0
    }
1124
0
1125
0
    if (paramBadEnum) {
1126
0
        if (!param.isFloat) {
1127
0
            mContext->ErrorInvalidEnum("pname 0x%04x: Invalid param"
1128
0
                                       " 0x%04x.",
1129
0
                                       pname, param.i);
1130
0
        } else {
1131
0
            mContext->ErrorInvalidEnum("pname 0x%04x: Invalid param %g.",
1132
0
                                       pname, param.f);
1133
0
        }
1134
0
        return;
1135
0
    }
1136
0
1137
0
    if (paramBadValue) {
1138
0
        if (!param.isFloat) {
1139
0
            mContext->ErrorInvalidValue("pname 0x%04x: Invalid param %i"
1140
0
                                        " (0x%x).",
1141
0
                                        pname, param.i, param.i);
1142
0
        } else {
1143
0
            mContext->ErrorInvalidValue("pname 0x%04x: Invalid param %g.",
1144
0
                                        pname, param.f);
1145
0
        }
1146
0
        return;
1147
0
    }
1148
0
1149
0
    ////////////////
1150
0
    // Store any needed values
1151
0
1152
0
    FloatOrInt clamped = param;
1153
0
    bool invalidateCaches = true;
1154
0
    switch (pname) {
1155
0
    case LOCAL_GL_TEXTURE_BASE_LEVEL:
1156
0
        mBaseMipmapLevel = clamped.i;
1157
0
        ClampLevelBaseAndMax();
1158
0
        clamped = FloatOrInt(GLint(mBaseMipmapLevel));
1159
0
        break;
1160
0
1161
0
    case LOCAL_GL_TEXTURE_MAX_LEVEL:
1162
0
        mMaxMipmapLevel = clamped.i;
1163
0
        ClampLevelBaseAndMax();
1164
0
        clamped = FloatOrInt(GLint(mMaxMipmapLevel));
1165
0
        break;
1166
0
1167
0
    case LOCAL_GL_TEXTURE_MIN_FILTER:
1168
0
        mSamplingState.minFilter = clamped.i;
1169
0
        break;
1170
0
1171
0
    case LOCAL_GL_TEXTURE_MAG_FILTER:
1172
0
        mSamplingState.magFilter = clamped.i;
1173
0
        break;
1174
0
1175
0
    case LOCAL_GL_TEXTURE_WRAP_S:
1176
0
        mSamplingState.wrapS = clamped.i;
1177
0
        break;
1178
0
1179
0
    case LOCAL_GL_TEXTURE_WRAP_T:
1180
0
        mSamplingState.wrapT = clamped.i;
1181
0
        break;
1182
0
1183
0
    case LOCAL_GL_TEXTURE_COMPARE_MODE:
1184
0
        mSamplingState.compareMode = clamped.i;
1185
0
        break;
1186
0
1187
0
    // Only a couple of pnames don't need to invalidate our resolve status cache.
1188
0
    case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
1189
0
    case LOCAL_GL_TEXTURE_WRAP_R:
1190
0
    case LOCAL_GL_TEXTURE_COMPARE_FUNC:
1191
0
        invalidateCaches = false;
1192
0
        break;
1193
0
    }
1194
0
1195
0
    if (invalidateCaches) {
1196
0
        InvalidateResolveCache();
1197
0
    }
1198
0
1199
0
    ////////////////
1200
0
1201
0
    if (!clamped.isFloat)
1202
0
        mContext->gl->fTexParameteri(texTarget.get(), pname, clamped.i);
1203
0
    else
1204
0
        mContext->gl->fTexParameterf(texTarget.get(), pname, clamped.f);
1205
0
}
1206
1207
////////////////////////////////////////////////////////////////////////////////
1208
1209
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTexture)
1210
1211
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTexture, AddRef)
1212
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLTexture, Release)
1213
1214
} // namespace mozilla