Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/canvas/WebGLFramebuffer.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 "WebGLFramebuffer.h"
7
8
// You know it's going to be fun when these two show up:
9
#include <algorithm>
10
#include <iterator>
11
12
#include "GLContext.h"
13
#include "GLScreenBuffer.h"
14
#include "mozilla/dom/WebGLRenderingContextBinding.h"
15
#include "nsPrintfCString.h"
16
#include "WebGLContext.h"
17
#include "WebGLContextUtils.h"
18
#include "WebGLExtensions.h"
19
#include "WebGLRenderbuffer.h"
20
#include "WebGLTexture.h"
21
22
namespace mozilla {
23
24
WebGLFBAttachPoint::WebGLFBAttachPoint()
25
    : mFB(nullptr)
26
    , mAttachmentPoint(0)
27
    , mTexImageTarget(LOCAL_GL_NONE)
28
    , mTexImageLayer(0)
29
    , mTexImageLevel(0)
30
0
{ }
31
32
WebGLFBAttachPoint::WebGLFBAttachPoint(WebGLFramebuffer* fb, GLenum attachmentPoint)
33
    : mFB(fb)
34
    , mAttachmentPoint(attachmentPoint)
35
    , mTexImageTarget(LOCAL_GL_NONE)
36
    , mTexImageLayer(0)
37
    , mTexImageLevel(0)
38
0
{ }
39
40
WebGLFBAttachPoint::~WebGLFBAttachPoint()
41
0
{
42
0
    MOZ_ASSERT(mFB, "Should have been Init'd.");
43
0
    MOZ_ASSERT(!mRenderbufferPtr);
44
0
    MOZ_ASSERT(!mTexturePtr);
45
0
}
46
47
void
48
WebGLFBAttachPoint::Unlink()
49
0
{
50
0
    Clear();
51
0
}
52
53
bool
54
WebGLFBAttachPoint::IsDeleteRequested() const
55
0
{
56
0
    return Texture() ? Texture()->IsDeleteRequested()
57
0
         : Renderbuffer() ? Renderbuffer()->IsDeleteRequested()
58
0
         : false;
59
0
}
60
61
bool
62
WebGLFBAttachPoint::IsDefined() const
63
0
{
64
0
    /*
65
0
    return (Renderbuffer() && Renderbuffer()->IsDefined()) ||
66
0
           (Texture() && Texture()->ImageInfoAt(mTexImageTarget,
67
0
                                                mTexImageLevel).IsDefined());
68
0
    */
69
0
    return (Renderbuffer() || Texture());
70
0
}
71
72
const webgl::FormatUsageInfo*
73
WebGLFBAttachPoint::Format() const
74
0
{
75
0
    MOZ_ASSERT(IsDefined());
76
0
77
0
    if (Texture())
78
0
        return Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).mFormat;
79
0
80
0
    if (Renderbuffer())
81
0
        return Renderbuffer()->Format();
82
0
83
0
    return nullptr;
84
0
}
85
86
uint32_t
87
WebGLFBAttachPoint::Samples() const
88
0
{
89
0
    MOZ_ASSERT(IsDefined());
90
0
91
0
    if (mRenderbufferPtr)
92
0
        return mRenderbufferPtr->Samples();
93
0
94
0
    return 0;
95
0
}
96
97
bool
98
WebGLFBAttachPoint::HasAlpha() const
99
0
{
100
0
    return Format()->format->a;
101
0
}
102
103
bool
104
WebGLFBAttachPoint::IsReadableFloat() const
105
0
{
106
0
    auto formatUsage = Format();
107
0
    MOZ_ASSERT(formatUsage);
108
0
109
0
    auto format = formatUsage->format;
110
0
    if (!format->IsColorFormat())
111
0
        return false;
112
0
113
0
    return format->componentType == webgl::ComponentType::Float;
114
0
}
115
116
void
117
WebGLFBAttachPoint::Clear()
118
0
{
119
0
    if (mRenderbufferPtr) {
120
0
        MOZ_ASSERT(!mTexturePtr);
121
0
        mRenderbufferPtr->UnmarkAttachment(*this);
122
0
    } else if (mTexturePtr) {
123
0
        mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel).RemoveAttachPoint(this);
124
0
    }
125
0
126
0
    mTexturePtr = nullptr;
127
0
    mRenderbufferPtr = nullptr;
128
0
129
0
    OnBackingStoreRespecified();
130
0
}
131
132
void
133
WebGLFBAttachPoint::SetTexImage(WebGLTexture* tex,
134
                                TexImageTarget target, GLint level, GLint layer)
135
0
{
136
0
    Clear();
137
0
138
0
    mTexturePtr = tex;
139
0
    mTexImageTarget = target;
140
0
    mTexImageLevel = level;
141
0
    mTexImageLayer = layer;
142
0
143
0
    if (mTexturePtr) {
144
0
        mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel).AddAttachPoint(this);
145
0
    }
146
0
}
147
148
void
149
WebGLFBAttachPoint::SetRenderbuffer(WebGLRenderbuffer* rb)
150
0
{
151
0
    Clear();
152
0
153
0
    mRenderbufferPtr = rb;
154
0
155
0
    if (mRenderbufferPtr) {
156
0
        mRenderbufferPtr->MarkAttachment(*this);
157
0
    }
158
0
}
159
160
bool
161
WebGLFBAttachPoint::HasUninitializedImageData() const
162
0
{
163
0
    if (!HasImage())
164
0
        return false;
165
0
166
0
    if (mRenderbufferPtr)
167
0
        return mRenderbufferPtr->HasUninitializedImageData();
168
0
169
0
    MOZ_ASSERT(mTexturePtr);
170
0
171
0
    auto& imageInfo = mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel);
172
0
    MOZ_ASSERT(imageInfo.IsDefined());
173
0
174
0
    return !imageInfo.IsDataInitialized();
175
0
}
176
177
void
178
WebGLFBAttachPoint::SetImageDataStatus(WebGLImageDataStatus newStatus) const
179
0
{
180
0
    if (!HasImage())
181
0
        return;
182
0
183
0
    if (mRenderbufferPtr) {
184
0
        mRenderbufferPtr->mImageDataStatus = newStatus;
185
0
        return;
186
0
    }
187
0
188
0
    MOZ_ASSERT(mTexturePtr);
189
0
190
0
    auto& imageInfo = mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel);
191
0
    MOZ_ASSERT(imageInfo.IsDefined());
192
0
193
0
    const bool isDataInitialized = (newStatus == WebGLImageDataStatus::InitializedImageData);
194
0
    imageInfo.SetIsDataInitialized(isDataInitialized, mTexturePtr);
195
0
}
196
197
bool
198
WebGLFBAttachPoint::HasImage() const
199
0
{
200
0
    if (Texture() && Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).IsDefined())
201
0
        return true;
202
0
203
0
    if (Renderbuffer() && Renderbuffer()->IsDefined())
204
0
        return true;
205
0
206
0
    return false;
207
0
}
208
209
void
210
WebGLFBAttachPoint::Size(uint32_t* const out_width, uint32_t* const out_height) const
211
0
{
212
0
    MOZ_ASSERT(HasImage());
213
0
214
0
    if (Renderbuffer()) {
215
0
        *out_width = Renderbuffer()->Width();
216
0
        *out_height = Renderbuffer()->Height();
217
0
        return;
218
0
    }
219
0
220
0
    MOZ_ASSERT(Texture());
221
0
    MOZ_ASSERT(Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).IsDefined());
222
0
    const auto& imageInfo = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel);
223
0
224
0
    *out_width = imageInfo.mWidth;
225
0
    *out_height = imageInfo.mHeight;
226
0
}
227
228
void
229
WebGLFBAttachPoint::OnBackingStoreRespecified() const
230
0
{
231
0
    mFB->InvalidateFramebufferStatus();
232
0
}
233
234
void
235
WebGLFBAttachPoint::AttachmentName(nsCString* out) const
236
0
{
237
0
    switch (mAttachmentPoint) {
238
0
    case LOCAL_GL_DEPTH_ATTACHMENT:
239
0
        out->AssignLiteral("DEPTH_ATTACHMENT");
240
0
        return;
241
0
242
0
    case LOCAL_GL_STENCIL_ATTACHMENT:
243
0
        out->AssignLiteral("STENCIL_ATTACHMENT");
244
0
        return;
245
0
246
0
    case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
247
0
        out->AssignLiteral("DEPTH_STENCIL_ATTACHMENT");
248
0
        return;
249
0
250
0
    default:
251
0
        MOZ_ASSERT(mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0);
252
0
        out->AssignLiteral("COLOR_ATTACHMENT");
253
0
        const uint32_t n = mAttachmentPoint - LOCAL_GL_COLOR_ATTACHMENT0;
254
0
        out->AppendInt(n);
255
0
        return;
256
0
    }
257
0
}
258
259
bool
260
WebGLFBAttachPoint::IsComplete(WebGLContext* webgl, nsCString* const out_info) const
261
0
{
262
0
    MOZ_ASSERT(IsDefined());
263
0
264
0
    if (!HasImage()) {
265
0
        AttachmentName(out_info);
266
0
        out_info->AppendLiteral("'s image is not defined");
267
0
        return false;
268
0
    }
269
0
270
0
    uint32_t width;
271
0
    uint32_t height;
272
0
    Size(&width, &height);
273
0
    if (!width || !height) {
274
0
        AttachmentName(out_info);
275
0
        out_info->AppendLiteral(" has no width or height");
276
0
        return false;
277
0
    }
278
0
279
0
    const auto formatUsage = Format();
280
0
    if (!formatUsage->IsRenderable()) {
281
0
        nsAutoCString attachName;
282
0
        AttachmentName(&attachName);
283
0
284
0
        *out_info = nsPrintfCString("%s has an effective format of %s, which is not"
285
0
                                    " renderable",
286
0
                                    attachName.BeginReading(), formatUsage->format->name);
287
0
        return false;
288
0
    }
289
0
290
0
    if (webgl->IsWebGL2() && Texture() &&
291
0
        Texture()->IsCubeMap() && !Texture()->IsCubeComplete())
292
0
    {
293
0
        AttachmentName(out_info);
294
0
        out_info->AppendLiteral(" is not cube complete");
295
0
        return false;
296
0
    }
297
0
298
0
    const auto format = formatUsage->format;
299
0
300
0
    bool hasRequiredBits;
301
0
302
0
    switch (mAttachmentPoint) {
303
0
    case LOCAL_GL_DEPTH_ATTACHMENT:
304
0
        hasRequiredBits = format->d;
305
0
        break;
306
0
307
0
    case LOCAL_GL_STENCIL_ATTACHMENT:
308
0
        hasRequiredBits = format->s;
309
0
        break;
310
0
311
0
    case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
312
0
        MOZ_ASSERT(!webgl->IsWebGL2());
313
0
        hasRequiredBits = (format->d && format->s);
314
0
        break;
315
0
316
0
    default:
317
0
        MOZ_ASSERT(mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0);
318
0
        hasRequiredBits = format->IsColorFormat();
319
0
        break;
320
0
    }
321
0
322
0
    if (!hasRequiredBits) {
323
0
        AttachmentName(out_info);
324
0
        out_info->AppendLiteral("'s format is missing required color/depth/stencil bits");
325
0
        return false;
326
0
    }
327
0
328
0
    if (!webgl->IsWebGL2()) {
329
0
        bool hasSurplusPlanes = false;
330
0
331
0
        switch (mAttachmentPoint) {
332
0
        case LOCAL_GL_DEPTH_ATTACHMENT:
333
0
            hasSurplusPlanes = format->s;
334
0
            break;
335
0
336
0
        case LOCAL_GL_STENCIL_ATTACHMENT:
337
0
            hasSurplusPlanes = format->d;
338
0
            break;
339
0
        }
340
0
341
0
        if (hasSurplusPlanes) {
342
0
            AttachmentName(out_info);
343
0
            out_info->AppendLiteral("'s format has depth or stencil bits when it"
344
0
                                    " shouldn't");
345
0
            return false;
346
0
        }
347
0
    }
348
0
349
0
    return true;
350
0
}
351
352
void
353
WebGLFBAttachPoint::Resolve(gl::GLContext* gl) const
354
0
{
355
0
    if (!HasImage())
356
0
        return;
357
0
358
0
    if (Renderbuffer()) {
359
0
        Renderbuffer()->DoFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, mAttachmentPoint);
360
0
        return;
361
0
    }
362
0
    MOZ_ASSERT(Texture());
363
0
364
0
    MOZ_ASSERT(gl == Texture()->mContext->GL());
365
0
366
0
    const auto& texName = Texture()->mGLName;
367
0
368
0
    ////
369
0
370
0
    const auto fnAttach2D = [&](GLenum attachmentPoint) {
371
0
        gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, attachmentPoint,
372
0
                                  mTexImageTarget.get(), texName, mTexImageLevel);
373
0
    };
374
0
375
0
    ////
376
0
377
0
    switch (mTexImageTarget.get()) {
378
0
    case LOCAL_GL_TEXTURE_2D:
379
0
    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
380
0
    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
381
0
    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
382
0
    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
383
0
    case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
384
0
    case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
385
0
        if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
386
0
            fnAttach2D(LOCAL_GL_DEPTH_ATTACHMENT);
387
0
            fnAttach2D(LOCAL_GL_STENCIL_ATTACHMENT);
388
0
        } else {
389
0
            fnAttach2D(mAttachmentPoint);
390
0
        }
391
0
        break;
392
0
393
0
    case LOCAL_GL_TEXTURE_2D_ARRAY:
394
0
    case LOCAL_GL_TEXTURE_3D:
395
0
        // If we have fFramebufferTextureLayer, we can rely on having
396
0
        // DEPTH_STENCIL_ATTACHMENT.
397
0
        gl->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER, mAttachmentPoint, texName,
398
0
                                     mTexImageLevel, mTexImageLayer);
399
0
        break;
400
0
    }
401
0
}
402
403
JS::Value
404
WebGLFBAttachPoint::GetParameter(WebGLContext* webgl, JSContext* cx,
405
                                 GLenum target, GLenum attachment, GLenum pname,
406
                                 ErrorResult* const out_error) const
407
0
{
408
0
    const bool hasAttachment = (mTexturePtr || mRenderbufferPtr);
409
0
    if (!hasAttachment) {
410
0
        // Divergent between GLES 3 and 2.
411
0
412
0
        // GLES 2.0.25 p127:
413
0
        // "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is NONE, then querying any
414
0
        //  other pname will generate INVALID_ENUM."
415
0
416
0
        // GLES 3.0.4 p240:
417
0
        // "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is NONE, no framebuffer is
418
0
        //  bound to target. In this case querying pname
419
0
        //  FRAMEBUFFER_ATTACHMENT_OBJECT_NAME will return zero, and all other queries
420
0
        //  will generate an INVALID_OPERATION error."
421
0
        switch (pname) {
422
0
        case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
423
0
            return JS::Int32Value(LOCAL_GL_NONE);
424
0
425
0
        case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
426
0
            if (webgl->IsWebGL2())
427
0
                return JS::NullValue();
428
0
429
0
            break;
430
0
431
0
        default:
432
0
            break;
433
0
        }
434
0
        nsCString attachmentName;
435
0
        WebGLContext::EnumName(attachment, &attachmentName);
436
0
        if (webgl->IsWebGL2()) {
437
0
            webgl->ErrorInvalidOperation("No attachment at %s.",
438
0
                                         attachmentName.BeginReading());
439
0
        } else {
440
0
            webgl->ErrorInvalidEnum("No attachment at %s.",
441
0
                                    attachmentName.BeginReading());
442
0
        }
443
0
        return JS::NullValue();
444
0
    }
445
0
446
0
    bool isPNameValid = false;
447
0
    switch (pname) {
448
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
449
0
        return JS::Int32Value(mTexturePtr ? LOCAL_GL_TEXTURE
450
0
                                          : LOCAL_GL_RENDERBUFFER);
451
0
452
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
453
0
        return (mTexturePtr ? webgl->WebGLObjectAsJSValue(cx, mTexturePtr.get(),
454
0
                                                          *out_error)
455
0
                            : webgl->WebGLObjectAsJSValue(cx, mRenderbufferPtr.get(),
456
0
                                                          *out_error));
457
0
458
0
        //////
459
0
460
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:
461
0
        if (mTexturePtr)
462
0
            return JS::Int32Value(MipLevel());
463
0
        break;
464
0
465
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE:
466
0
        if (mTexturePtr) {
467
0
            GLenum face = 0;
468
0
            if (mTexturePtr->Target() == LOCAL_GL_TEXTURE_CUBE_MAP) {
469
0
                face = ImageTarget().get();
470
0
            }
471
0
            return JS::Int32Value(face);
472
0
        }
473
0
        break;
474
0
475
0
        //////
476
0
477
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER:
478
0
        if (webgl->IsWebGL2() && mTexturePtr) {
479
0
            int32_t layer = 0;
480
0
            if (ImageTarget() == LOCAL_GL_TEXTURE_2D_ARRAY ||
481
0
                ImageTarget() == LOCAL_GL_TEXTURE_3D)
482
0
            {
483
0
                layer = Layer();
484
0
            }
485
0
            return JS::Int32Value(layer);
486
0
        }
487
0
        break;
488
0
489
0
        //////
490
0
491
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
492
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
493
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
494
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
495
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
496
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
497
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
498
0
        isPNameValid = webgl->IsWebGL2();
499
0
        break;
500
0
501
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
502
0
        isPNameValid = (webgl->IsWebGL2() ||
503
0
                        webgl->IsExtensionEnabled(WebGLExtensionID::EXT_sRGB));
504
0
        break;
505
0
    }
506
0
507
0
    if (!isPNameValid) {
508
0
        webgl->ErrorInvalidEnum("Invalid pname: 0x%04x", pname);
509
0
        return JS::NullValue();
510
0
    }
511
0
512
0
    const auto usage = Format();
513
0
    if (!usage) {
514
0
        if (pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)
515
0
            return JS::NumberValue(LOCAL_GL_LINEAR);
516
0
517
0
        return JS::NullValue();
518
0
    }
519
0
520
0
    auto format = usage->format;
521
0
522
0
    GLint ret = 0;
523
0
    switch (pname) {
524
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
525
0
        ret = format->r;
526
0
        break;
527
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
528
0
        ret = format->g;
529
0
        break;
530
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
531
0
        ret = format->b;
532
0
        break;
533
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
534
0
        ret = format->a;
535
0
        break;
536
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
537
0
        ret = format->d;
538
0
        break;
539
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
540
0
        ret = format->s;
541
0
        break;
542
0
543
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
544
0
        ret = (format->isSRGB ? LOCAL_GL_SRGB
545
0
                              : LOCAL_GL_LINEAR);
546
0
        break;
547
0
548
0
    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
549
0
        MOZ_ASSERT(attachment != LOCAL_GL_DEPTH_STENCIL_ATTACHMENT);
550
0
551
0
        if (format->componentType == webgl::ComponentType::Special) {
552
0
            // Special format is used for DS mixed format(e.g. D24S8 and D32FS8).
553
0
            MOZ_ASSERT(format->unsizedFormat == webgl::UnsizedFormat::DEPTH_STENCIL);
554
0
            MOZ_ASSERT(attachment == LOCAL_GL_DEPTH_ATTACHMENT ||
555
0
                       attachment == LOCAL_GL_STENCIL_ATTACHMENT);
556
0
557
0
            if (attachment == LOCAL_GL_DEPTH_ATTACHMENT) {
558
0
                switch (format->effectiveFormat) {
559
0
                case webgl::EffectiveFormat::DEPTH24_STENCIL8:
560
0
                    format = webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT24);
561
0
                    break;
562
0
                case webgl::EffectiveFormat::DEPTH32F_STENCIL8:
563
0
                    format = webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT32F);
564
0
                    break;
565
0
                default:
566
0
                    MOZ_ASSERT(false, "no matched DS format");
567
0
                    break;
568
0
                }
569
0
            } else if (attachment == LOCAL_GL_STENCIL_ATTACHMENT) {
570
0
                switch (format->effectiveFormat) {
571
0
                case webgl::EffectiveFormat::DEPTH24_STENCIL8:
572
0
                case webgl::EffectiveFormat::DEPTH32F_STENCIL8:
573
0
                    format = webgl::GetFormat(webgl::EffectiveFormat::STENCIL_INDEX8);
574
0
                    break;
575
0
                default:
576
0
                    MOZ_ASSERT(false, "no matched DS format");
577
0
                    break;
578
0
                }
579
0
            }
580
0
        }
581
0
582
0
        switch (format->componentType) {
583
0
        case webgl::ComponentType::None:
584
0
            ret = LOCAL_GL_NONE;
585
0
            break;
586
0
        case webgl::ComponentType::Int:
587
0
            ret = LOCAL_GL_INT;
588
0
            break;
589
0
        case webgl::ComponentType::UInt:
590
0
            ret = LOCAL_GL_UNSIGNED_INT;
591
0
            break;
592
0
        case webgl::ComponentType::NormInt:
593
0
            ret = LOCAL_GL_SIGNED_NORMALIZED;
594
0
            break;
595
0
        case webgl::ComponentType::NormUInt:
596
0
            ret = LOCAL_GL_UNSIGNED_NORMALIZED;
597
0
            break;
598
0
        case webgl::ComponentType::Float:
599
0
            ret = LOCAL_GL_FLOAT;
600
0
            break;
601
0
        default:
602
0
            MOZ_ASSERT(false, "No matched component type");
603
0
            break;
604
0
        }
605
0
        break;
606
0
607
0
    default:
608
0
        MOZ_ASSERT(false, "Missing case.");
609
0
        break;
610
0
    }
611
0
612
0
    return JS::Int32Value(ret);
613
0
}
614
615
////////////////////////////////////////////////////////////////////////////////
616
////////////////////////////////////////////////////////////////////////////////
617
// WebGLFramebuffer
618
619
WebGLFramebuffer::WebGLFramebuffer(WebGLContext* webgl, GLuint fbo)
620
    : WebGLRefCountedObject(webgl)
621
    , mGLName(fbo)
622
    , mNumFBStatusInvals(0)
623
#ifdef ANDROID
624
    , mIsFB(false)
625
#endif
626
    , mDepthAttachment(this, LOCAL_GL_DEPTH_ATTACHMENT)
627
    , mStencilAttachment(this, LOCAL_GL_STENCIL_ATTACHMENT)
628
    , mDepthStencilAttachment(this, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
629
0
{
630
0
    mContext->mFramebuffers.insertBack(this);
631
0
632
0
    size_t i = 0;
633
0
    for (auto& cur : mColorAttachments) {
634
0
        new (&cur) WebGLFBAttachPoint(this, LOCAL_GL_COLOR_ATTACHMENT0 + i);
635
0
        i++;
636
0
    }
637
0
638
0
    mColorDrawBuffers.push_back(&mColorAttachments[0]);
639
0
    mColorReadBuffer = &mColorAttachments[0];
640
0
}
641
642
void
643
WebGLFramebuffer::Delete()
644
0
{
645
0
    InvalidateFramebufferStatus();
646
0
647
0
    mDepthAttachment.Clear();
648
0
    mStencilAttachment.Clear();
649
0
    mDepthStencilAttachment.Clear();
650
0
651
0
    for (auto& cur : mColorAttachments) {
652
0
        cur.Clear();
653
0
    }
654
0
655
0
    mContext->gl->fDeleteFramebuffers(1, &mGLName);
656
0
657
0
    LinkedListElement<WebGLFramebuffer>::removeFrom(mContext->mFramebuffers);
658
0
659
#ifdef ANDROID
660
    mIsFB = false;
661
#endif
662
}
663
664
////
665
666
Maybe<WebGLFBAttachPoint*>
667
WebGLFramebuffer::GetColorAttachPoint(GLenum attachPoint)
668
0
{
669
0
    if (attachPoint == LOCAL_GL_NONE)
670
0
        return Some<WebGLFBAttachPoint*>(nullptr);
671
0
672
0
    if (attachPoint < LOCAL_GL_COLOR_ATTACHMENT0)
673
0
        return Nothing();
674
0
675
0
    const size_t colorId = attachPoint - LOCAL_GL_COLOR_ATTACHMENT0;
676
0
677
0
    MOZ_ASSERT(mContext->mGLMaxColorAttachments <= kMaxColorAttachments);
678
0
    if (colorId >= mContext->mGLMaxColorAttachments)
679
0
        return Nothing();
680
0
681
0
    return Some(&mColorAttachments[colorId]);
682
0
}
683
684
Maybe<WebGLFBAttachPoint*>
685
WebGLFramebuffer::GetAttachPoint(GLenum attachPoint)
686
0
{
687
0
    switch (attachPoint) {
688
0
    case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
689
0
        return Some(&mDepthStencilAttachment);
690
0
691
0
    case LOCAL_GL_DEPTH_ATTACHMENT:
692
0
        return Some(&mDepthAttachment);
693
0
694
0
    case LOCAL_GL_STENCIL_ATTACHMENT:
695
0
        return Some(&mStencilAttachment);
696
0
697
0
    default:
698
0
        return GetColorAttachPoint(attachPoint);
699
0
    }
700
0
}
701
702
#define FOR_EACH_ATTACHMENT(X)            \
703
0
    X(mDepthAttachment);                  \
704
0
    X(mStencilAttachment);                \
705
0
    X(mDepthStencilAttachment);           \
706
0
                                          \
707
0
    for (auto& cur : mColorAttachments) { \
708
0
        X(cur);                           \
709
0
    }
710
711
void
712
WebGLFramebuffer::DetachTexture(const WebGLTexture* tex)
713
0
{
714
0
    const auto fnDetach = [&](WebGLFBAttachPoint& attach) {
715
0
        if (attach.Texture() == tex) {
716
0
            attach.Clear();
717
0
        }
718
0
    };
719
0
720
0
    FOR_EACH_ATTACHMENT(fnDetach)
721
0
}
722
723
void
724
WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer* rb)
725
0
{
726
0
    const auto fnDetach = [&](WebGLFBAttachPoint& attach) {
727
0
        if (attach.Renderbuffer() == rb) {
728
0
            attach.Clear();
729
0
        }
730
0
    };
731
0
732
0
    FOR_EACH_ATTACHMENT(fnDetach)
733
0
}
734
735
////////////////////////////////////////////////////////////////////////////////
736
// Completeness
737
738
bool
739
WebGLFramebuffer::HasDuplicateAttachments() const
740
0
{
741
0
   std::set<WebGLFBAttachPoint::Ordered> uniqueAttachSet;
742
0
743
0
   for (const auto& attach : mColorAttachments) {
744
0
      if (!attach.IsDefined())
745
0
          continue;
746
0
747
0
      const WebGLFBAttachPoint::Ordered ordered(attach);
748
0
749
0
      const bool didInsert = uniqueAttachSet.insert(ordered).second;
750
0
      if (!didInsert)
751
0
         return true;
752
0
   }
753
0
754
0
   return false;
755
0
}
756
757
bool
758
WebGLFramebuffer::HasDefinedAttachments() const
759
0
{
760
0
    bool hasAttachments = false;
761
0
    const auto func = [&](const WebGLFBAttachPoint& attach) {
762
0
        hasAttachments |= attach.IsDefined();
763
0
    };
764
0
765
0
    FOR_EACH_ATTACHMENT(func)
766
0
    return hasAttachments;
767
0
}
768
769
bool
770
WebGLFramebuffer::HasIncompleteAttachments(nsCString* const out_info) const
771
0
{
772
0
    bool hasIncomplete = false;
773
0
    const auto func = [&](const WebGLFBAttachPoint& cur) {
774
0
        if (!cur.IsDefined())
775
0
            return; // Not defined, so can't count as incomplete.
776
0
777
0
        hasIncomplete |= !cur.IsComplete(mContext, out_info);
778
0
    };
779
0
780
0
    FOR_EACH_ATTACHMENT(func)
781
0
    return hasIncomplete;
782
0
}
783
784
bool
785
WebGLFramebuffer::AllImageRectsMatch() const
786
0
{
787
0
    MOZ_ASSERT(HasDefinedAttachments());
788
0
    DebugOnly<nsCString> fbStatusInfo;
789
0
    MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo));
790
0
791
0
    bool needsInit = true;
792
0
    uint32_t width = 0;
793
0
    uint32_t height = 0;
794
0
795
0
    bool hasMismatch = false;
796
0
    const auto func = [&](const WebGLFBAttachPoint& attach) {
797
0
        if (!attach.HasImage())
798
0
            return;
799
0
800
0
        uint32_t curWidth;
801
0
        uint32_t curHeight;
802
0
        attach.Size(&curWidth, &curHeight);
803
0
804
0
        if (needsInit) {
805
0
            needsInit = false;
806
0
            width = curWidth;
807
0
            height = curHeight;
808
0
            return;
809
0
        }
810
0
811
0
        hasMismatch |= (curWidth != width ||
812
0
                        curHeight != height);
813
0
    };
814
0
815
0
    FOR_EACH_ATTACHMENT(func)
816
0
    return !hasMismatch;
817
0
}
818
819
bool
820
WebGLFramebuffer::AllImageSamplesMatch() const
821
0
{
822
0
    MOZ_ASSERT(HasDefinedAttachments());
823
0
    DebugOnly<nsCString> fbStatusInfo;
824
0
    MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo));
825
0
826
0
    bool needsInit = true;
827
0
    uint32_t samples = 0;
828
0
829
0
    bool hasMismatch = false;
830
0
    const auto func = [&](const WebGLFBAttachPoint& attach) {
831
0
        if (!attach.HasImage())
832
0
          return;
833
0
834
0
        const uint32_t curSamples = attach.Samples();
835
0
836
0
        if (needsInit) {
837
0
            needsInit = false;
838
0
            samples = curSamples;
839
0
            return;
840
0
        }
841
0
842
0
        hasMismatch |= (curSamples != samples);
843
0
    };
844
0
845
0
    FOR_EACH_ATTACHMENT(func)
846
0
    return !hasMismatch;
847
0
}
848
849
#undef FOR_EACH_ATTACHMENT
850
851
FBStatus
852
WebGLFramebuffer::PrecheckFramebufferStatus(nsCString* const out_info) const
853
0
{
854
0
    MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
855
0
               mContext->mBoundReadFramebuffer == this);
856
0
857
0
    if (!HasDefinedAttachments())
858
0
        return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; // No attachments
859
0
860
0
    if (HasIncompleteAttachments(out_info))
861
0
        return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
862
0
863
0
    if (!AllImageRectsMatch())
864
0
        return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; // Inconsistent sizes
865
0
866
0
    if (!AllImageSamplesMatch())
867
0
        return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE; // Inconsistent samples
868
0
869
0
    if (HasDuplicateAttachments())
870
0
       return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
871
0
872
0
    if (mContext->IsWebGL2()) {
873
0
        MOZ_ASSERT(!mDepthStencilAttachment.IsDefined());
874
0
    } else {
875
0
        const auto depthOrStencilCount = int(mDepthAttachment.IsDefined()) +
876
0
                                         int(mStencilAttachment.IsDefined()) +
877
0
                                         int(mDepthStencilAttachment.IsDefined());
878
0
        if (depthOrStencilCount > 1)
879
0
            return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
880
0
    }
881
0
882
0
    return LOCAL_GL_FRAMEBUFFER_COMPLETE;
883
0
}
884
885
////////////////////////////////////////
886
// Validation
887
888
bool
889
WebGLFramebuffer::ValidateAndInitAttachments() const
890
0
{
891
0
    MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
892
0
               mContext->mBoundReadFramebuffer == this);
893
0
894
0
    const auto fbStatus = CheckFramebufferStatus();
895
0
    if (fbStatus == LOCAL_GL_FRAMEBUFFER_COMPLETE)
896
0
        return true;
897
0
898
0
    mContext->ErrorInvalidFramebufferOperation("Framebuffer must be complete.");
899
0
    return false;
900
0
}
901
902
bool
903
WebGLFramebuffer::ValidateClearBufferType(GLenum buffer,
904
                                          uint32_t drawBuffer, GLenum funcType) const
905
0
{
906
0
    if (buffer != LOCAL_GL_COLOR)
907
0
        return true;
908
0
909
0
    const auto& attach = mColorAttachments[drawBuffer];
910
0
    if (!attach.IsDefined())
911
0
        return true;
912
0
913
0
    if (!count(mColorDrawBuffers.begin(), mColorDrawBuffers.end(), &attach))
914
0
        return true; // DRAW_BUFFERi set to NONE.
915
0
916
0
    GLenum attachType;
917
0
    switch (attach.Format()->format->componentType) {
918
0
    case webgl::ComponentType::Int:
919
0
        attachType = LOCAL_GL_INT;
920
0
        break;
921
0
    case webgl::ComponentType::UInt:
922
0
        attachType = LOCAL_GL_UNSIGNED_INT;
923
0
        break;
924
0
    default:
925
0
        attachType = LOCAL_GL_FLOAT;
926
0
        break;
927
0
    }
928
0
929
0
    if (attachType != funcType) {
930
0
        mContext->ErrorInvalidOperation("This attachment is of type 0x%04x, but"
931
0
                                        " this function is of type 0x%04x.",
932
0
                                        attachType, funcType);
933
0
        return false;
934
0
    }
935
0
936
0
    return true;
937
0
}
938
939
bool
940
WebGLFramebuffer::ValidateForColorRead(const webgl::FormatUsageInfo** const out_format,
941
                                       uint32_t* const out_width,
942
                                       uint32_t* const out_height) const
943
0
{
944
0
    if (!mColorReadBuffer) {
945
0
        mContext->ErrorInvalidOperation("READ_BUFFER must not be NONE.");
946
0
        return false;
947
0
    }
948
0
949
0
    if (!mColorReadBuffer->IsDefined()) {
950
0
        mContext->ErrorInvalidOperation("The READ_BUFFER attachment is not defined.");
951
0
        return false;
952
0
    }
953
0
954
0
    if (mColorReadBuffer->Samples()) {
955
0
        mContext->ErrorInvalidOperation("The READ_BUFFER attachment is multisampled.");
956
0
        return false;
957
0
    }
958
0
959
0
    *out_format = mColorReadBuffer->Format();
960
0
    mColorReadBuffer->Size(out_width, out_height);
961
0
    return true;
962
0
}
963
964
////////////////////////////////////////////////////////////////////////////////
965
// Resolution and caching
966
967
void
968
WebGLFramebuffer::ResolveAttachments() const
969
0
{
970
0
    const auto& gl = mContext->gl;
971
0
972
0
    ////
973
0
    // Nuke attachment points.
974
0
975
0
    for (uint32_t i = 0; i < mContext->mGLMaxColorAttachments; i++) {
976
0
        const GLenum attachEnum = LOCAL_GL_COLOR_ATTACHMENT0 + i;
977
0
        gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, attachEnum,
978
0
                                     LOCAL_GL_RENDERBUFFER, 0);
979
0
    }
980
0
981
0
    gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT,
982
0
                                 LOCAL_GL_RENDERBUFFER, 0);
983
0
    gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT,
984
0
                                 LOCAL_GL_RENDERBUFFER, 0);
985
0
986
0
    ////
987
0
988
0
    for (const auto& attach : mColorAttachments) {
989
0
        attach.Resolve(gl);
990
0
    }
991
0
992
0
    mDepthAttachment.Resolve(gl);
993
0
    mStencilAttachment.Resolve(gl);
994
0
    mDepthStencilAttachment.Resolve(gl);
995
0
}
996
997
bool
998
WebGLFramebuffer::ResolveAttachmentData() const
999
0
{
1000
0
    //////
1001
0
    // Check if we need to initialize anything
1002
0
1003
0
    const auto fnIs3D = [&](const WebGLFBAttachPoint& attach) {
1004
0
        const auto& tex = attach.Texture();
1005
0
        if (!tex)
1006
0
            return false;
1007
0
1008
0
        const auto& info = tex->ImageInfoAt(attach.ImageTarget(), attach.MipLevel());
1009
0
        if (info.mDepth == 1)
1010
0
            return false;
1011
0
1012
0
        return true;
1013
0
    };
1014
0
1015
0
    uint32_t clearBits = 0;
1016
0
    std::vector<const WebGLFBAttachPoint*> attachmentsToClear;
1017
0
    std::vector<const WebGLFBAttachPoint*> colorAttachmentsToClear;
1018
0
    std::vector<const WebGLFBAttachPoint*> tex3DAttachmentsToInit;
1019
0
1020
0
    const auto fnGather = [&](const WebGLFBAttachPoint& attach, GLenum attachClearBits) {
1021
0
        if (!attach.HasUninitializedImageData())
1022
0
            return false;
1023
0
1024
0
        if (fnIs3D(attach)) {
1025
0
            tex3DAttachmentsToInit.push_back(&attach);
1026
0
            return false;
1027
0
        }
1028
0
1029
0
        clearBits |= attachClearBits;
1030
0
        attachmentsToClear.push_back(&attach);
1031
0
        return true;
1032
0
    };
1033
0
1034
0
    //////
1035
0
1036
0
    for (auto& cur : mColorDrawBuffers) {
1037
0
        if (fnGather(*cur, LOCAL_GL_COLOR_BUFFER_BIT)) {
1038
0
            colorAttachmentsToClear.push_back(cur);
1039
0
        }
1040
0
    }
1041
0
1042
0
    fnGather(mDepthAttachment, LOCAL_GL_DEPTH_BUFFER_BIT);
1043
0
    fnGather(mStencilAttachment, LOCAL_GL_STENCIL_BUFFER_BIT);
1044
0
    fnGather(mDepthStencilAttachment, LOCAL_GL_DEPTH_BUFFER_BIT |
1045
0
                                      LOCAL_GL_STENCIL_BUFFER_BIT);
1046
0
1047
0
    //////
1048
0
1049
0
    for (const auto& attach : tex3DAttachmentsToInit) {
1050
0
        const auto& tex = attach->Texture();
1051
0
        if (!tex->InitializeImageData(attach->ImageTarget(),
1052
0
                                      attach->MipLevel()))
1053
0
        {
1054
0
            return false;
1055
0
        }
1056
0
    }
1057
0
1058
0
    if (clearBits) {
1059
0
        const auto fnDrawBuffers = [&](const std::vector<const WebGLFBAttachPoint*>& src)
1060
0
        {
1061
0
            std::vector<GLenum> enumList;
1062
0
1063
0
            for (const auto& cur : src) {
1064
0
                const auto& attachEnum = cur->mAttachmentPoint;
1065
0
                const GLenum attachId = attachEnum - LOCAL_GL_COLOR_ATTACHMENT0;
1066
0
1067
0
                while (enumList.size() < attachId) {
1068
0
                    enumList.push_back(LOCAL_GL_NONE);
1069
0
                }
1070
0
                enumList.push_back(attachEnum);
1071
0
            }
1072
0
1073
0
            mContext->gl->fDrawBuffers(enumList.size(), enumList.data());
1074
0
        };
1075
0
1076
0
        ////
1077
0
        // Clear
1078
0
1079
0
        const bool hasDrawBuffers = mContext->HasDrawBuffers();
1080
0
        if (hasDrawBuffers) {
1081
0
            fnDrawBuffers(colorAttachmentsToClear);
1082
0
        }
1083
0
1084
0
        {
1085
0
            gl::ScopedBindFramebuffer autoBind(mContext->gl, mGLName);
1086
0
1087
0
            mContext->ForceClearFramebufferWithDefaultValues(clearBits, false);
1088
0
        }
1089
0
1090
0
        if (hasDrawBuffers) {
1091
0
            RefreshDrawBuffers();
1092
0
        }
1093
0
1094
0
        // Mark initialized.
1095
0
        for (const auto& cur : attachmentsToClear) {
1096
0
            cur->SetImageDataStatus(WebGLImageDataStatus::InitializedImageData);
1097
0
        }
1098
0
    }
1099
0
1100
0
    return true;
1101
0
}
1102
1103
WebGLFramebuffer::ResolvedData::ResolvedData(const WebGLFramebuffer& parent)
1104
0
{
1105
0
1106
0
    texDrawBuffers.reserve(parent.mColorDrawBuffers.size() + 2); // +2 for depth+stencil.
1107
0
1108
0
    const auto fnCommon = [&](const WebGLFBAttachPoint& attach) {
1109
0
        if (!attach.IsDefined())
1110
0
            return false;
1111
0
1112
0
        if (attach.Texture()) {
1113
0
            texDrawBuffers.push_back(&attach);
1114
0
        }
1115
0
        return true;
1116
0
    };
1117
0
1118
0
    ////
1119
0
1120
0
    const auto fnDepthStencil = [&](const WebGLFBAttachPoint& attach) {
1121
0
        if (!fnCommon(attach))
1122
0
            return;
1123
0
1124
0
        drawSet.insert(WebGLFBAttachPoint::Ordered(attach));
1125
0
        readSet.insert(WebGLFBAttachPoint::Ordered(attach));
1126
0
    };
1127
0
1128
0
    fnDepthStencil(parent.mDepthAttachment);
1129
0
    fnDepthStencil(parent.mStencilAttachment);
1130
0
    fnDepthStencil(parent.mDepthStencilAttachment);
1131
0
1132
0
    ////
1133
0
1134
0
    for (const auto& pAttach : parent.mColorDrawBuffers) {
1135
0
        const auto& attach = *pAttach;
1136
0
        if (!fnCommon(attach))
1137
0
            return;
1138
0
1139
0
        drawSet.insert(WebGLFBAttachPoint::Ordered(attach));
1140
0
    }
1141
0
1142
0
    if (parent.mColorReadBuffer) {
1143
0
        const auto& attach = *parent.mColorReadBuffer;
1144
0
        if (!fnCommon(attach))
1145
0
            return;
1146
0
1147
0
        readSet.insert(WebGLFBAttachPoint::Ordered(attach));
1148
0
    }
1149
0
}
1150
1151
void
1152
WebGLFramebuffer::InvalidateFramebufferStatus()
1153
0
{
1154
0
    if (mResolvedCompleteData) {
1155
0
        mNumFBStatusInvals++;
1156
0
        if (mNumFBStatusInvals > mContext->mMaxAcceptableFBStatusInvals) {
1157
0
            mContext->GeneratePerfWarning("FB was invalidated after being complete %u"
1158
0
                                          " times.",
1159
0
                                          uint32_t(mNumFBStatusInvals));
1160
0
        }
1161
0
    }
1162
0
1163
0
    mResolvedCompleteData = nullptr;
1164
0
}
1165
1166
void
1167
WebGLFramebuffer::RefreshResolvedData()
1168
0
{
1169
0
    if (mResolvedCompleteData) {
1170
0
        mResolvedCompleteData.reset(new ResolvedData(*this));
1171
0
    }
1172
0
}
1173
1174
////////////////////////////////////////////////////////////////////////////////
1175
// Entrypoints
1176
1177
FBStatus
1178
WebGLFramebuffer::CheckFramebufferStatus() const
1179
0
{
1180
0
    if (IsResolvedComplete())
1181
0
        return LOCAL_GL_FRAMEBUFFER_COMPLETE;
1182
0
1183
0
    // Ok, let's try to resolve it!
1184
0
1185
0
    nsCString statusInfo;
1186
0
    FBStatus ret = PrecheckFramebufferStatus(&statusInfo);
1187
0
    do {
1188
0
        if (ret != LOCAL_GL_FRAMEBUFFER_COMPLETE)
1189
0
            break;
1190
0
1191
0
        // Looks good on our end. Let's ask the driver.
1192
0
        gl::GLContext* const gl = mContext->gl;
1193
0
1194
0
        const ScopedFBRebinder autoFB(mContext);
1195
0
        gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mGLName);
1196
0
1197
0
        ////
1198
0
1199
0
        ResolveAttachments(); // OK, attach everything properly!
1200
0
        RefreshDrawBuffers();
1201
0
        RefreshReadBuffer();
1202
0
1203
0
        ret = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
1204
0
1205
0
        ////
1206
0
1207
0
        if (ret != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
1208
0
            const nsPrintfCString text("Bad status according to the driver: 0x%04x",
1209
0
                                       ret.get());
1210
0
            statusInfo = text;
1211
0
            break;
1212
0
        }
1213
0
1214
0
        if (!ResolveAttachmentData()) {
1215
0
            ret = LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
1216
0
            statusInfo.AssignLiteral("Failed to lazily-initialize attachment data.");
1217
0
            break;
1218
0
        }
1219
0
1220
0
        mResolvedCompleteData.reset(new ResolvedData(*this));
1221
0
        return LOCAL_GL_FRAMEBUFFER_COMPLETE;
1222
0
    } while (false);
1223
0
1224
0
    MOZ_ASSERT(ret != LOCAL_GL_FRAMEBUFFER_COMPLETE);
1225
0
    mContext->GenerateWarning("Framebuffer not complete. (status: 0x%04x) %s",
1226
0
                              ret.get(), statusInfo.BeginReading());
1227
0
    return ret;
1228
0
}
1229
1230
////
1231
1232
void
1233
WebGLFramebuffer::RefreshDrawBuffers() const
1234
0
{
1235
0
    const auto& gl = mContext->gl;
1236
0
    if (!gl->IsSupported(gl::GLFeature::draw_buffers))
1237
0
        return;
1238
0
1239
0
    // Prior to GL4.1, having a no-image FB attachment that's selected by DrawBuffers
1240
0
    // yields a framebuffer status of FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER.
1241
0
    // We could workaround this only on affected versions, but it's easier be
1242
0
    // unconditional.
1243
0
    std::vector<GLenum> driverBuffers(mContext->mGLMaxDrawBuffers, LOCAL_GL_NONE);
1244
0
    for (const auto& attach : mColorDrawBuffers) {
1245
0
        if (attach->HasImage()) {
1246
0
            const uint32_t index = attach->mAttachmentPoint - LOCAL_GL_COLOR_ATTACHMENT0;
1247
0
            driverBuffers[index] = attach->mAttachmentPoint;
1248
0
        }
1249
0
    }
1250
0
1251
0
    gl->fDrawBuffers(driverBuffers.size(), driverBuffers.data());
1252
0
}
1253
1254
void
1255
WebGLFramebuffer::RefreshReadBuffer() const
1256
0
{
1257
0
    const auto& gl = mContext->gl;
1258
0
    if (!gl->IsSupported(gl::GLFeature::read_buffer))
1259
0
        return;
1260
0
1261
0
    // Prior to GL4.1, having a no-image FB attachment that's selected by ReadBuffer
1262
0
    // yields a framebuffer status of FRAMEBUFFER_INCOMPLETE_READ_BUFFER.
1263
0
    // We could workaround this only on affected versions, but it's easier be
1264
0
    // unconditional.
1265
0
    GLenum driverBuffer = LOCAL_GL_NONE;
1266
0
    if (mColorReadBuffer && mColorReadBuffer->HasImage()) {
1267
0
        driverBuffer = mColorReadBuffer->mAttachmentPoint;
1268
0
    }
1269
0
1270
0
    gl->fReadBuffer(driverBuffer);
1271
0
}
1272
1273
////
1274
1275
void
1276
WebGLFramebuffer::DrawBuffers(const dom::Sequence<GLenum>& buffers)
1277
0
{
1278
0
    if (buffers.Length() > mContext->mGLMaxDrawBuffers) {
1279
0
        // "An INVALID_VALUE error is generated if `n` is greater than MAX_DRAW_BUFFERS."
1280
0
        mContext->ErrorInvalidValue("`buffers` must have a length <="
1281
0
                                    " MAX_DRAW_BUFFERS.");
1282
0
        return;
1283
0
    }
1284
0
1285
0
    std::vector<const WebGLFBAttachPoint*> newColorDrawBuffers;
1286
0
    newColorDrawBuffers.reserve(buffers.Length());
1287
0
1288
0
    for (size_t i = 0; i < buffers.Length(); i++) {
1289
0
        // "If the GL is bound to a draw framebuffer object, the `i`th buffer listed in
1290
0
        //  bufs must be COLOR_ATTACHMENTi or NONE. Specifying a buffer out of order,
1291
0
        //  BACK, or COLOR_ATTACHMENTm where `m` is greater than or equal to the value of
1292
0
        // MAX_COLOR_ATTACHMENTS, will generate the error INVALID_OPERATION.
1293
0
1294
0
        // WEBGL_draw_buffers:
1295
0
        // "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater than or
1296
0
        //  equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter."
1297
0
        // This means that if buffers.Length() isn't larger than MaxDrawBuffers, it won't
1298
0
        // be larger than MaxColorAttachments.
1299
0
        const auto& cur = buffers[i];
1300
0
        if (cur == LOCAL_GL_COLOR_ATTACHMENT0 + i) {
1301
0
            const auto& attach = mColorAttachments[i];
1302
0
            newColorDrawBuffers.push_back(&attach);
1303
0
        } else if (cur != LOCAL_GL_NONE) {
1304
0
            const bool isColorEnum = (cur >= LOCAL_GL_COLOR_ATTACHMENT0 &&
1305
0
                                      cur < mContext->LastColorAttachmentEnum());
1306
0
            if (cur != LOCAL_GL_BACK &&
1307
0
                !isColorEnum)
1308
0
            {
1309
0
                mContext->ErrorInvalidEnum("Unexpected enum in buffers.");
1310
0
                return;
1311
0
            }
1312
0
1313
0
            mContext->ErrorInvalidOperation("`buffers[i]` must be NONE or"
1314
0
                                            " COLOR_ATTACHMENTi.");
1315
0
            return;
1316
0
        }
1317
0
    }
1318
0
1319
0
    ////
1320
0
1321
0
    mColorDrawBuffers.swap(newColorDrawBuffers);
1322
0
    RefreshDrawBuffers(); // Calls glDrawBuffers.
1323
0
    RefreshResolvedData();
1324
0
}
1325
1326
void
1327
WebGLFramebuffer::ReadBuffer(GLenum attachPoint)
1328
0
{
1329
0
    const auto& maybeAttach = GetColorAttachPoint(attachPoint);
1330
0
    if (!maybeAttach) {
1331
0
        const char text[] = "`mode` must be a COLOR_ATTACHMENTi, for 0 <= i <"
1332
0
                            " MAX_DRAW_BUFFERS.";
1333
0
        if (attachPoint == LOCAL_GL_BACK) {
1334
0
            mContext->ErrorInvalidOperation(text);
1335
0
        } else {
1336
0
            mContext->ErrorInvalidEnum(text);
1337
0
        }
1338
0
        return;
1339
0
    }
1340
0
    const auto& attach = maybeAttach.value(); // Might be nullptr.
1341
0
1342
0
    ////
1343
0
1344
0
    mColorReadBuffer = attach;
1345
0
    RefreshReadBuffer(); // Calls glReadBuffer.
1346
0
    RefreshResolvedData();
1347
0
}
1348
1349
////
1350
1351
void
1352
WebGLFramebuffer::FramebufferRenderbuffer(GLenum attachEnum,
1353
                                          GLenum rbtarget, WebGLRenderbuffer* rb)
1354
0
{
1355
0
    MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
1356
0
               mContext->mBoundReadFramebuffer == this);
1357
0
1358
0
    // `attachment`
1359
0
    const auto maybeAttach = GetAttachPoint(attachEnum);
1360
0
    if (!maybeAttach || !maybeAttach.value()) {
1361
0
        mContext->ErrorInvalidEnum("Bad `attachment`: 0x%x.", attachEnum);
1362
0
        return;
1363
0
    }
1364
0
    const auto& attach = maybeAttach.value();
1365
0
1366
0
    // `rbTarget`
1367
0
    if (rbtarget != LOCAL_GL_RENDERBUFFER) {
1368
0
        mContext->ErrorInvalidEnumInfo("rbtarget", rbtarget);
1369
0
        return;
1370
0
    }
1371
0
1372
0
    // `rb`
1373
0
    if (rb) {
1374
0
        if (!mContext->ValidateObject("rb", *rb))
1375
0
            return;
1376
0
1377
0
        if (!rb->mHasBeenBound) {
1378
0
            mContext->ErrorInvalidOperation("bindRenderbuffer must be called before"
1379
0
                                            " attachment to %04x",
1380
0
                                            attachEnum);
1381
0
            return;
1382
0
      }
1383
0
    }
1384
0
1385
0
    // End of validation.
1386
0
1387
0
    if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
1388
0
        mDepthAttachment.SetRenderbuffer(rb);
1389
0
        mStencilAttachment.SetRenderbuffer(rb);
1390
0
    } else {
1391
0
        attach->SetRenderbuffer(rb);
1392
0
    }
1393
0
1394
0
    InvalidateFramebufferStatus();
1395
0
}
1396
1397
void
1398
WebGLFramebuffer::FramebufferTexture2D(GLenum attachEnum,
1399
                                       GLenum texImageTarget, WebGLTexture* tex,
1400
                                       GLint level)
1401
0
{
1402
0
    MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
1403
0
               mContext->mBoundReadFramebuffer == this);
1404
0
1405
0
    // `attachment`
1406
0
    const auto maybeAttach = GetAttachPoint(attachEnum);
1407
0
    if (!maybeAttach || !maybeAttach.value()) {
1408
0
        mContext->ErrorInvalidEnum("Bad `attachment`: 0x%x.", attachEnum);
1409
0
        return;
1410
0
    }
1411
0
    const auto& attach = maybeAttach.value();
1412
0
1413
0
    // `texImageTarget`
1414
0
    if (texImageTarget != LOCAL_GL_TEXTURE_2D &&
1415
0
        (texImageTarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X ||
1416
0
         texImageTarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z))
1417
0
    {
1418
0
        mContext->ErrorInvalidEnumInfo("texImageTarget",
1419
0
                                       texImageTarget);
1420
0
        return;
1421
0
    }
1422
0
1423
0
    // `texture`
1424
0
    if (tex) {
1425
0
        if (!mContext->ValidateObject("texture", *tex))
1426
0
            return;
1427
0
1428
0
        if (!tex->HasEverBeenBound()) {
1429
0
            mContext->ErrorInvalidOperation("`texture` has never been bound.");
1430
0
            return;
1431
0
        }
1432
0
1433
0
        const TexTarget destTexTarget = TexImageTargetToTexTarget(texImageTarget);
1434
0
        if (tex->Target() != destTexTarget) {
1435
0
            mContext->ErrorInvalidOperation("Mismatched texture and texture target.");
1436
0
            return;
1437
0
        }
1438
0
    }
1439
0
1440
0
    // `level`
1441
0
    if (level < 0)
1442
0
        return mContext->ErrorInvalidValue("`level` must not be negative.");
1443
0
1444
0
    if (mContext->IsWebGL2()) {
1445
0
        /* GLES 3.0.4 p208:
1446
0
         *   If textarget is one of TEXTURE_CUBE_MAP_POSITIVE_X,
1447
0
         *   TEXTURE_CUBE_MAP_POSITIVE_Y, TEXTURE_CUBE_MAP_POSITIVE_Z,
1448
0
         *   TEXTURE_CUBE_MAP_NEGATIVE_X, TEXTURE_CUBE_MAP_NEGATIVE_Y,
1449
0
         *   or TEXTURE_CUBE_MAP_NEGATIVE_Z, then level must be greater
1450
0
         *   than or equal to zero and less than or equal to log2 of the
1451
0
         *   value of MAX_CUBE_MAP_TEXTURE_SIZE. If textarget is TEXTURE_2D,
1452
0
         *   level must be greater than or equal to zero and no larger than
1453
0
         *   log2 of the value of MAX_TEXTURE_SIZE. Otherwise, an
1454
0
         *   INVALID_VALUE error is generated.
1455
0
         */
1456
0
1457
0
        if (texImageTarget == LOCAL_GL_TEXTURE_2D) {
1458
0
            if (uint32_t(level) > FloorLog2(mContext->mGLMaxTextureSize))
1459
0
                return mContext->ErrorInvalidValue("`level` is too large.");
1460
0
        } else {
1461
0
            MOZ_ASSERT(texImageTarget >= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X &&
1462
0
                       texImageTarget <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);
1463
0
1464
0
            if (uint32_t(level) > FloorLog2(mContext->mGLMaxCubeMapTextureSize))
1465
0
                return mContext->ErrorInvalidValue("`level` is too large.");
1466
0
        }
1467
0
    } else if (level != 0) {
1468
0
        return mContext->ErrorInvalidValue("`level` must be 0.");
1469
0
    }
1470
0
1471
0
    // End of validation.
1472
0
1473
0
    if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
1474
0
        mDepthAttachment.SetTexImage(tex, texImageTarget, level);
1475
0
        mStencilAttachment.SetTexImage(tex, texImageTarget, level);
1476
0
    } else {
1477
0
        attach->SetTexImage(tex, texImageTarget, level);
1478
0
    }
1479
0
1480
0
    InvalidateFramebufferStatus();
1481
0
}
1482
1483
void
1484
WebGLFramebuffer::FramebufferTextureLayer(GLenum attachEnum,
1485
                                          WebGLTexture* tex, GLint level, GLint layer)
1486
0
{
1487
0
    MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
1488
0
               mContext->mBoundReadFramebuffer == this);
1489
0
1490
0
    // `attachment`
1491
0
    const auto maybeAttach = GetAttachPoint(attachEnum);
1492
0
    if (!maybeAttach || !maybeAttach.value()) {
1493
0
        mContext->ErrorInvalidEnum("Bad `attachment`: 0x%x.", attachEnum);
1494
0
        return;
1495
0
    }
1496
0
    const auto& attach = maybeAttach.value();
1497
0
1498
0
    // `level`, `layer`
1499
0
    if (layer < 0)
1500
0
        return mContext->ErrorInvalidValue("`layer` must be >= 0.");
1501
0
1502
0
    if (level < 0)
1503
0
        return mContext->ErrorInvalidValue("`level` must be >= 0.");
1504
0
1505
0
    // `texture`
1506
0
    GLenum texImageTarget = LOCAL_GL_TEXTURE_3D;
1507
0
    if (tex) {
1508
0
        if (!mContext->ValidateObject("texture", *tex))
1509
0
            return;
1510
0
1511
0
        if (!tex->HasEverBeenBound()) {
1512
0
            mContext->ErrorInvalidOperation("`texture` has never been bound.");
1513
0
            return;
1514
0
        }
1515
0
1516
0
        texImageTarget = tex->Target().get();
1517
0
        switch (texImageTarget) {
1518
0
        case LOCAL_GL_TEXTURE_3D:
1519
0
            if (uint32_t(layer) >= mContext->mGLMax3DTextureSize) {
1520
0
                mContext->ErrorInvalidValue("`layer` must be < %s.",
1521
0
                                            "MAX_3D_TEXTURE_SIZE");
1522
0
                return;
1523
0
            }
1524
0
1525
0
            if (uint32_t(level) > FloorLog2(mContext->mGLMax3DTextureSize)) {
1526
0
                mContext->ErrorInvalidValue("`level` must be <= log2(%s).",
1527
0
                                            "MAX_3D_TEXTURE_SIZE");
1528
0
                return;
1529
0
            }
1530
0
            break;
1531
0
1532
0
        case LOCAL_GL_TEXTURE_2D_ARRAY:
1533
0
            if (uint32_t(layer) >= mContext->mGLMaxArrayTextureLayers) {
1534
0
                mContext->ErrorInvalidValue("`layer` must be < %s.",
1535
0
                                            "MAX_ARRAY_TEXTURE_LAYERS");
1536
0
                return;
1537
0
            }
1538
0
1539
0
            if (uint32_t(level) > FloorLog2(mContext->mGLMaxTextureSize)) {
1540
0
                mContext->ErrorInvalidValue("`level` must be <= log2(%s).",
1541
0
                                            "MAX_TEXTURE_SIZE");
1542
0
                return;
1543
0
            }
1544
0
            break;
1545
0
1546
0
        default:
1547
0
            mContext->ErrorInvalidOperation("`texture` must be a TEXTURE_3D or"
1548
0
                                            " TEXTURE_2D_ARRAY.");
1549
0
            return;
1550
0
        }
1551
0
    }
1552
0
1553
0
    // End of validation.
1554
0
1555
0
    if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
1556
0
        mDepthAttachment.SetTexImage(tex, texImageTarget, level, layer);
1557
0
        mStencilAttachment.SetTexImage(tex, texImageTarget, level, layer);
1558
0
    } else {
1559
0
        attach->SetTexImage(tex, texImageTarget, level, layer);
1560
0
    }
1561
0
1562
0
    InvalidateFramebufferStatus();
1563
0
}
1564
1565
JS::Value
1566
WebGLFramebuffer::GetAttachmentParameter(JSContext* cx,
1567
                                         GLenum target, GLenum attachEnum, GLenum pname,
1568
                                         ErrorResult* const out_error)
1569
0
{
1570
0
    const auto maybeAttach = GetAttachPoint(attachEnum);
1571
0
    if (!maybeAttach || attachEnum == LOCAL_GL_NONE) {
1572
0
        mContext->ErrorInvalidEnum("Can only query COLOR_ATTACHMENTi,"
1573
0
                                   " DEPTH_ATTACHMENT, DEPTH_STENCIL_ATTACHMENT, or"
1574
0
                                   " STENCIL_ATTACHMENT for a framebuffer.");
1575
0
        return JS::NullValue();
1576
0
    }
1577
0
    auto attach = maybeAttach.value();
1578
0
1579
0
    if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
1580
0
        // There are a couple special rules for this one.
1581
0
1582
0
        if (pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE) {
1583
0
            mContext->ErrorInvalidOperation("Querying"
1584
0
                                            " FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE"
1585
0
                                            " against DEPTH_STENCIL_ATTACHMENT is an"
1586
0
                                            " error.");
1587
0
            return JS::NullValue();
1588
0
        }
1589
0
1590
0
        if (mDepthAttachment.Renderbuffer() != mStencilAttachment.Renderbuffer() ||
1591
0
            mDepthAttachment.Texture() != mStencilAttachment.Texture())
1592
0
        {
1593
0
            mContext->ErrorInvalidOperation("DEPTH_ATTACHMENT and STENCIL_ATTACHMENT"
1594
0
                                            " have different objects bound.");
1595
0
            return JS::NullValue();
1596
0
        }
1597
0
1598
0
        attach = &mDepthAttachment;
1599
0
    }
1600
0
1601
0
    return attach->GetParameter(mContext, cx, target, attachEnum, pname,
1602
0
                                out_error);
1603
0
}
1604
1605
////////////////////
1606
1607
static void
1608
GetBackbufferFormats(const WebGLContext* webgl,
1609
                     const webgl::FormatInfo** const out_color,
1610
                     const webgl::FormatInfo** const out_depth,
1611
                     const webgl::FormatInfo** const out_stencil)
1612
0
{
1613
0
    const auto& options = webgl->Options();
1614
0
1615
0
    const auto effFormat = (options.alpha ? webgl::EffectiveFormat::RGBA8
1616
0
                                          : webgl::EffectiveFormat::RGB8);
1617
0
    *out_color = webgl::GetFormat(effFormat);
1618
0
1619
0
    *out_depth = nullptr;
1620
0
    *out_stencil = nullptr;
1621
0
    if (options.depth && options.stencil) {
1622
0
        *out_depth = webgl::GetFormat(webgl::EffectiveFormat::DEPTH24_STENCIL8);
1623
0
        *out_stencil = *out_depth;
1624
0
    } else {
1625
0
        if (options.depth) {
1626
0
            *out_depth = webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT16);
1627
0
        }
1628
0
        if (options.stencil) {
1629
0
            *out_stencil = webgl::GetFormat(webgl::EffectiveFormat::STENCIL_INDEX8);
1630
0
        }
1631
0
    }
1632
0
}
1633
1634
/*static*/ void
1635
WebGLFramebuffer::BlitFramebuffer(WebGLContext* webgl,
1636
                                  GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
1637
                                  GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
1638
                                  GLbitfield mask, GLenum filter)
1639
0
{
1640
0
    const auto& gl = webgl->gl;
1641
0
1642
0
    const auto& srcFB = webgl->mBoundReadFramebuffer;
1643
0
    const auto& dstFB = webgl->mBoundDrawFramebuffer;
1644
0
1645
0
    ////
1646
0
    // Collect data
1647
0
1648
0
    const auto fnGetDepthAndStencilAttach = [](const WebGLFramebuffer* fb,
1649
0
                                               const WebGLFBAttachPoint** const out_depth,
1650
0
                                               const WebGLFBAttachPoint** const out_stencil)
1651
0
    {
1652
0
        *out_depth = nullptr;
1653
0
        *out_stencil = nullptr;
1654
0
1655
0
        if (!fb)
1656
0
            return;
1657
0
1658
0
        if (fb->mDepthStencilAttachment.IsDefined()) {
1659
0
            *out_depth = *out_stencil = &fb->mDepthStencilAttachment;
1660
0
            return;
1661
0
        }
1662
0
        if (fb->mDepthAttachment.IsDefined()) {
1663
0
            *out_depth = &fb->mDepthAttachment;
1664
0
        }
1665
0
        if (fb->mStencilAttachment.IsDefined()) {
1666
0
            *out_stencil = &fb->mStencilAttachment;
1667
0
        }
1668
0
    };
1669
0
1670
0
    const WebGLFBAttachPoint* srcDepthAttach;
1671
0
    const WebGLFBAttachPoint* srcStencilAttach;
1672
0
    fnGetDepthAndStencilAttach(srcFB, &srcDepthAttach, &srcStencilAttach);
1673
0
    const WebGLFBAttachPoint* dstDepthAttach;
1674
0
    const WebGLFBAttachPoint* dstStencilAttach;
1675
0
    fnGetDepthAndStencilAttach(dstFB, &dstDepthAttach, &dstStencilAttach);
1676
0
1677
0
    ////
1678
0
1679
0
    const auto fnGetFormat = [](const WebGLFBAttachPoint* cur,
1680
0
                                bool* const out_hasSamples) -> const webgl::FormatInfo*
1681
0
    {
1682
0
        if (!cur || !cur->IsDefined())
1683
0
            return nullptr;
1684
0
1685
0
        *out_hasSamples |= bool(cur->Samples());
1686
0
        return cur->Format()->format;
1687
0
    };
1688
0
1689
0
    const auto fnNarrowComponentType = [&](const webgl::FormatInfo* format) {
1690
0
        switch (format->componentType) {
1691
0
        case webgl::ComponentType::NormInt:
1692
0
        case webgl::ComponentType::NormUInt:
1693
0
            return webgl::ComponentType::Float;
1694
0
1695
0
        default:
1696
0
            return format->componentType;
1697
0
        }
1698
0
    };
1699
0
1700
0
    bool srcHasSamples;
1701
0
    const webgl::FormatInfo* srcColorFormat;
1702
0
    webgl::ComponentType srcColorType = webgl::ComponentType::None;
1703
0
    const webgl::FormatInfo* srcDepthFormat;
1704
0
    const webgl::FormatInfo* srcStencilFormat;
1705
0
1706
0
    if (srcFB) {
1707
0
        srcHasSamples = false;
1708
0
        srcColorFormat = fnGetFormat(srcFB->mColorReadBuffer, &srcHasSamples);
1709
0
        srcDepthFormat = fnGetFormat(srcDepthAttach, &srcHasSamples);
1710
0
        srcStencilFormat = fnGetFormat(srcStencilAttach, &srcHasSamples);
1711
0
    } else {
1712
0
        srcHasSamples = false; // Always false.
1713
0
1714
0
        GetBackbufferFormats(webgl, &srcColorFormat, &srcDepthFormat, &srcStencilFormat);
1715
0
    }
1716
0
1717
0
    if (srcColorFormat) {
1718
0
        srcColorType = fnNarrowComponentType(srcColorFormat);
1719
0
    }
1720
0
1721
0
    ////
1722
0
1723
0
    bool dstHasSamples;
1724
0
    const webgl::FormatInfo* dstDepthFormat;
1725
0
    const webgl::FormatInfo* dstStencilFormat;
1726
0
    bool dstHasColor = false;
1727
0
    bool colorFormatsMatch = true;
1728
0
    bool colorTypesMatch = true;
1729
0
1730
0
    const auto fnCheckColorFormat = [&](const webgl::FormatInfo* dstFormat) {
1731
0
        MOZ_ASSERT(dstFormat->r || dstFormat->g || dstFormat->b || dstFormat->a);
1732
0
        dstHasColor = true;
1733
0
        colorFormatsMatch &= (dstFormat == srcColorFormat);
1734
0
        colorTypesMatch &= ( fnNarrowComponentType(dstFormat) == srcColorType );
1735
0
    };
1736
0
1737
0
    if (dstFB) {
1738
0
        dstHasSamples = false;
1739
0
1740
0
        for (const auto& cur : dstFB->mColorDrawBuffers) {
1741
0
            const auto& format = fnGetFormat(cur, &dstHasSamples);
1742
0
            if (!format)
1743
0
                continue;
1744
0
1745
0
            fnCheckColorFormat(format);
1746
0
        }
1747
0
1748
0
        dstDepthFormat = fnGetFormat(dstDepthAttach, &dstHasSamples);
1749
0
        dstStencilFormat = fnGetFormat(dstStencilAttach, &dstHasSamples);
1750
0
    } else {
1751
0
        dstHasSamples = bool(gl->Screen()->Samples());
1752
0
1753
0
        const webgl::FormatInfo* dstColorFormat;
1754
0
        GetBackbufferFormats(webgl, &dstColorFormat, &dstDepthFormat, &dstStencilFormat);
1755
0
1756
0
        fnCheckColorFormat(dstColorFormat);
1757
0
    }
1758
0
1759
0
    ////
1760
0
    // Clear unused buffer bits
1761
0
1762
0
    if (mask & LOCAL_GL_COLOR_BUFFER_BIT &&
1763
0
        !srcColorFormat && !dstHasColor)
1764
0
    {
1765
0
        mask ^= LOCAL_GL_COLOR_BUFFER_BIT;
1766
0
    }
1767
0
1768
0
    if (mask & LOCAL_GL_DEPTH_BUFFER_BIT &&
1769
0
        !srcDepthFormat && !dstDepthFormat)
1770
0
    {
1771
0
        mask ^= LOCAL_GL_DEPTH_BUFFER_BIT;
1772
0
    }
1773
0
1774
0
    if (mask & LOCAL_GL_STENCIL_BUFFER_BIT &&
1775
0
        !srcStencilFormat && !dstStencilFormat)
1776
0
    {
1777
0
        mask ^= LOCAL_GL_STENCIL_BUFFER_BIT;
1778
0
    }
1779
0
1780
0
    ////
1781
0
    // Validation
1782
0
1783
0
    if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
1784
0
        if (srcColorFormat && filter == LOCAL_GL_LINEAR) {
1785
0
            const auto& type = srcColorFormat->componentType;
1786
0
            if (type == webgl::ComponentType::Int ||
1787
0
                type == webgl::ComponentType::UInt)
1788
0
            {
1789
0
                webgl->ErrorInvalidOperation("`filter` is LINEAR and READ_BUFFER"
1790
0
                                             " contains integer data.");
1791
0
                return;
1792
0
            }
1793
0
        }
1794
0
1795
0
        if (!colorTypesMatch) {
1796
0
            webgl->ErrorInvalidOperation("Color component types (fixed/float/uint/"
1797
0
                                         "int) must match.");
1798
0
            return;
1799
0
        }
1800
0
    }
1801
0
1802
0
    const GLbitfield depthAndStencilBits = LOCAL_GL_DEPTH_BUFFER_BIT |
1803
0
                                           LOCAL_GL_STENCIL_BUFFER_BIT;
1804
0
    if (bool(mask & depthAndStencilBits) &&
1805
0
        filter != LOCAL_GL_NEAREST)
1806
0
    {
1807
0
        webgl->ErrorInvalidOperation("DEPTH_BUFFER_BIT and STENCIL_BUFFER_BIT can"
1808
0
                                     " only be used with NEAREST filtering.");
1809
0
        return;
1810
0
    }
1811
0
1812
0
    /* GLES 3.0.4, p199:
1813
0
     *   Calling BlitFramebuffer will result in an INVALID_OPERATION error if
1814
0
     *   mask includes DEPTH_BUFFER_BIT or STENCIL_BUFFER_BIT, and the source
1815
0
     *   and destination depth and stencil buffer formats do not match.
1816
0
     *
1817
0
     * jgilbert: The wording is such that if only DEPTH_BUFFER_BIT is specified,
1818
0
     * the stencil formats must match. This seems wrong. It could be a spec bug,
1819
0
     * or I could be missing an interaction in one of the earlier paragraphs.
1820
0
     */
1821
0
    if (mask & LOCAL_GL_DEPTH_BUFFER_BIT &&
1822
0
        dstDepthFormat && dstDepthFormat != srcDepthFormat)
1823
0
    {
1824
0
        webgl->ErrorInvalidOperation("Depth buffer formats must match if selected.");
1825
0
        return;
1826
0
    }
1827
0
1828
0
    if (mask & LOCAL_GL_STENCIL_BUFFER_BIT &&
1829
0
        dstStencilFormat && dstStencilFormat != srcStencilFormat)
1830
0
    {
1831
0
        webgl->ErrorInvalidOperation("Stencil buffer formats must match if selected.");
1832
0
        return;
1833
0
    }
1834
0
1835
0
    ////
1836
0
1837
0
    if (dstHasSamples) {
1838
0
        webgl->ErrorInvalidOperation("DRAW_FRAMEBUFFER may not have multiple"
1839
0
                                     " samples.");
1840
0
        return;
1841
0
    }
1842
0
1843
0
    if (srcHasSamples) {
1844
0
        if (mask & LOCAL_GL_COLOR_BUFFER_BIT &&
1845
0
            dstHasColor && !colorFormatsMatch)
1846
0
        {
1847
0
            webgl->ErrorInvalidOperation("Color buffer formats must match if"
1848
0
                                         " selected, when reading from a multisampled"
1849
0
                                         " source.");
1850
0
            return;
1851
0
        }
1852
0
1853
0
        if (dstX0 != srcX0 ||
1854
0
            dstX1 != srcX1 ||
1855
0
            dstY0 != srcY0 ||
1856
0
            dstY1 != srcY1)
1857
0
        {
1858
0
            webgl->ErrorInvalidOperation("If the source is multisampled, then the"
1859
0
                                         " source and dest regions must match exactly.");
1860
0
            return;
1861
0
        }
1862
0
    }
1863
0
1864
0
    ////
1865
0
    // Check for feedback
1866
0
1867
0
    if (srcFB && dstFB) {
1868
0
        const WebGLFBAttachPoint* feedback = nullptr;
1869
0
1870
0
        if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
1871
0
            MOZ_ASSERT(srcFB->mColorReadBuffer->IsDefined());
1872
0
            for (const auto& cur : dstFB->mColorDrawBuffers) {
1873
0
                if (srcFB->mColorReadBuffer->IsEquivalentForFeedback(*cur)) {
1874
0
                    feedback = cur;
1875
0
                    break;
1876
0
                }
1877
0
            }
1878
0
        }
1879
0
1880
0
        if (mask & LOCAL_GL_DEPTH_BUFFER_BIT &&
1881
0
            srcDepthAttach->IsEquivalentForFeedback(*dstDepthAttach))
1882
0
        {
1883
0
            feedback = dstDepthAttach;
1884
0
        }
1885
0
1886
0
        if (mask & LOCAL_GL_STENCIL_BUFFER_BIT &&
1887
0
            srcStencilAttach->IsEquivalentForFeedback(*dstStencilAttach))
1888
0
        {
1889
0
            feedback = dstStencilAttach;
1890
0
        }
1891
0
1892
0
        if (feedback) {
1893
0
            webgl->ErrorInvalidOperation("Feedback detected into DRAW_FRAMEBUFFER's"
1894
0
                                         " 0x%04x attachment.",
1895
0
                                         feedback->mAttachmentPoint);
1896
0
            return;
1897
0
        }
1898
0
    } else if (!srcFB && !dstFB) {
1899
0
        webgl->ErrorInvalidOperation("Feedback with default framebuffer.");
1900
0
        return;
1901
0
    }
1902
0
1903
0
    ////
1904
0
1905
0
    const ScopedDrawCallWrapper wrapper(*webgl);
1906
0
    gl->fBlitFramebuffer(srcX0, srcY0, srcX1, srcY1,
1907
0
                         dstX0, dstY0, dstX1, dstY1,
1908
0
                         mask, filter);
1909
0
}
1910
1911
////////////////////////////////////////////////////////////////////////////////
1912
// Goop.
1913
1914
JSObject*
1915
WebGLFramebuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
1916
0
{
1917
0
    return dom::WebGLFramebuffer_Binding::Wrap(cx, this, givenProto);
1918
0
}
1919
1920
inline void
1921
ImplCycleCollectionUnlink(mozilla::WebGLFBAttachPoint& field)
1922
0
{
1923
0
    field.Unlink();
1924
0
}
1925
1926
inline void
1927
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
1928
                            const mozilla::WebGLFBAttachPoint& field,
1929
                            const char* name,
1930
                            uint32_t flags = 0)
1931
0
{
1932
0
    CycleCollectionNoteChild(callback, field.Texture(), name, flags);
1933
0
    CycleCollectionNoteChild(callback, field.Renderbuffer(), name, flags);
1934
0
}
1935
1936
template<typename C>
1937
inline void
1938
ImplCycleCollectionUnlink(C& field)
1939
0
{
1940
0
    for (auto& cur : field) {
1941
0
        cur.Unlink();
1942
0
    }
1943
0
}
1944
1945
template<typename C>
1946
inline void
1947
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
1948
                            const C& field,
1949
                            const char* name,
1950
                            uint32_t flags = 0)
1951
0
{
1952
0
    for (auto& cur : field) {
1953
0
        ImplCycleCollectionTraverse(callback, cur, name, flags);
1954
0
    }
1955
0
}
1956
1957
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLFramebuffer,
1958
                                      mDepthAttachment,
1959
                                      mStencilAttachment,
1960
                                      mDepthStencilAttachment,
1961
                                      mColorAttachments)
1962
1963
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLFramebuffer, AddRef)
1964
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLFramebuffer, Release)
1965
1966
} // namespace mozilla