Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/canvas/WebGL2ContextFramebuffers.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; 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 "WebGL2Context.h"
7
8
#include "GLContext.h"
9
#include "GLScreenBuffer.h"
10
#include "mozilla/CheckedInt.h"
11
#include "WebGLContextUtils.h"
12
#include "WebGLFormats.h"
13
#include "WebGLFramebuffer.h"
14
15
namespace mozilla {
16
17
void
18
WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
19
                               GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
20
                               GLbitfield mask, GLenum filter)
21
0
{
22
0
    const FuncScope funcScope(*this, "blitFramebuffer");
23
0
    if (IsContextLost())
24
0
        return;
25
0
26
0
    const GLbitfield validBits = LOCAL_GL_COLOR_BUFFER_BIT |
27
0
                                 LOCAL_GL_DEPTH_BUFFER_BIT |
28
0
                                 LOCAL_GL_STENCIL_BUFFER_BIT;
29
0
    if ((mask | validBits) != validBits) {
30
0
        ErrorInvalidValue("Invalid bit set in mask.");
31
0
        return;
32
0
    }
33
0
34
0
    switch (filter) {
35
0
    case LOCAL_GL_NEAREST:
36
0
    case LOCAL_GL_LINEAR:
37
0
        break;
38
0
    default:
39
0
        ErrorInvalidEnumInfo("filter", filter);
40
0
        return;
41
0
    }
42
0
43
0
    // --
44
0
45
0
    const auto fnLikelyOverflow = [](GLint p0, GLint p1) {
46
0
        auto checked = CheckedInt<GLint>(p1) - p0;
47
0
        checked = -checked; // And check the negation!
48
0
        return !checked.isValid();
49
0
    };
50
0
51
0
    if (fnLikelyOverflow(srcX0, srcX1) || fnLikelyOverflow(srcY0, srcY1) ||
52
0
        fnLikelyOverflow(dstX0, dstX1) || fnLikelyOverflow(dstY0, dstY1))
53
0
    {
54
0
        ErrorInvalidValue("Likely-to-overflow large ranges are forbidden.");
55
0
        return;
56
0
    }
57
0
58
0
    // --
59
0
60
0
    if (!ValidateAndInitFB(mBoundReadFramebuffer) ||
61
0
        !ValidateAndInitFB(mBoundDrawFramebuffer))
62
0
    {
63
0
        return;
64
0
    }
65
0
66
0
    DoBindFB(mBoundReadFramebuffer, LOCAL_GL_READ_FRAMEBUFFER);
67
0
    DoBindFB(mBoundDrawFramebuffer, LOCAL_GL_DRAW_FRAMEBUFFER);
68
0
69
0
    WebGLFramebuffer::BlitFramebuffer(this,
70
0
                                      srcX0, srcY0, srcX1, srcY1,
71
0
                                      dstX0, dstY0, dstX1, dstY1,
72
0
                                      mask, filter);
73
0
}
74
75
void
76
WebGL2Context::FramebufferTextureLayer(GLenum target, GLenum attachment,
77
                                       WebGLTexture* texture, GLint level, GLint layer)
78
0
{
79
0
    const FuncScope funcScope(*this, "framebufferTextureLayer");
80
0
    if (IsContextLost())
81
0
        return;
82
0
83
0
    if (!ValidateFramebufferTarget(target))
84
0
        return;
85
0
86
0
    WebGLFramebuffer* fb;
87
0
    switch (target) {
88
0
    case LOCAL_GL_FRAMEBUFFER:
89
0
    case LOCAL_GL_DRAW_FRAMEBUFFER:
90
0
        fb = mBoundDrawFramebuffer;
91
0
        break;
92
0
93
0
    case LOCAL_GL_READ_FRAMEBUFFER:
94
0
        fb = mBoundReadFramebuffer;
95
0
        break;
96
0
97
0
    default:
98
0
        MOZ_CRASH("GFX: Bad target.");
99
0
    }
100
0
101
0
    if (!fb)
102
0
        return ErrorInvalidOperation("Cannot modify framebuffer 0.");
103
0
104
0
    fb->FramebufferTextureLayer(attachment, texture, level, layer);
105
0
}
106
107
JS::Value
108
WebGL2Context::GetFramebufferAttachmentParameter(JSContext* cx,
109
                                                 GLenum target,
110
                                                 GLenum attachment,
111
                                                 GLenum pname,
112
                                                 ErrorResult& out_error)
113
0
{
114
0
    return WebGLContext::GetFramebufferAttachmentParameter(cx, target, attachment, pname,
115
0
                                                           out_error);
116
0
}
117
118
////
119
120
static bool
121
ValidateBackbufferAttachmentEnum(WebGLContext* webgl, GLenum attachment)
122
0
{
123
0
    switch (attachment) {
124
0
    case LOCAL_GL_COLOR:
125
0
    case LOCAL_GL_DEPTH:
126
0
    case LOCAL_GL_STENCIL:
127
0
        return true;
128
0
129
0
    default:
130
0
        webgl->ErrorInvalidEnumInfo("attachment", attachment);
131
0
        return false;
132
0
    }
133
0
}
134
135
static bool
136
ValidateFramebufferAttachmentEnum(WebGLContext* webgl,
137
                                  GLenum attachment)
138
0
{
139
0
    switch (attachment) {
140
0
    case LOCAL_GL_DEPTH_ATTACHMENT:
141
0
    case LOCAL_GL_STENCIL_ATTACHMENT:
142
0
    case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
143
0
        return true;
144
0
    }
145
0
146
0
    if (attachment < LOCAL_GL_COLOR_ATTACHMENT0) {
147
0
        webgl->ErrorInvalidEnumInfo("attachment", attachment);
148
0
        return false;
149
0
    }
150
0
151
0
    if (attachment > webgl->LastColorAttachmentEnum()) {
152
0
        // That these errors have different types is ridiculous.
153
0
        webgl->ErrorInvalidOperation("Too-large LOCAL_GL_COLOR_ATTACHMENTn.");
154
0
        return false;
155
0
    }
156
0
157
0
    return true;
158
0
}
159
160
bool
161
WebGLContext::ValidateInvalidateFramebuffer(GLenum target,
162
                                            const dom::Sequence<GLenum>& attachments,
163
                                            ErrorResult* const out_rv,
164
                                            std::vector<GLenum>* const scopedVector,
165
                                            GLsizei* const out_glNumAttachments,
166
                                            const GLenum** const out_glAttachments)
167
0
{
168
0
    if (IsContextLost())
169
0
        return false;
170
0
171
0
    if (!ValidateFramebufferTarget(target))
172
0
        return false;
173
0
174
0
    const WebGLFramebuffer* fb;
175
0
    bool isDefaultFB = false;
176
0
    switch (target) {
177
0
    case LOCAL_GL_FRAMEBUFFER:
178
0
    case LOCAL_GL_DRAW_FRAMEBUFFER:
179
0
        fb = mBoundDrawFramebuffer;
180
0
        break;
181
0
182
0
    case LOCAL_GL_READ_FRAMEBUFFER:
183
0
        fb = mBoundReadFramebuffer;
184
0
        break;
185
0
186
0
    default:
187
0
        MOZ_CRASH("GFX: Bad target.");
188
0
    }
189
0
190
0
    if (fb) {
191
0
        const auto fbStatus = fb->CheckFramebufferStatus();
192
0
        if (fbStatus != LOCAL_GL_FRAMEBUFFER_COMPLETE)
193
0
            return false; // Not an error, but don't run forward to driver either.
194
0
    } else {
195
0
        if (!EnsureDefaultFB())
196
0
            return false;
197
0
    }
198
0
    DoBindFB(fb, target);
199
0
200
0
    *out_glNumAttachments = attachments.Length();
201
0
    *out_glAttachments = attachments.Elements();
202
0
203
0
    if (fb) {
204
0
        for (const auto& attachment : attachments) {
205
0
            if (!ValidateFramebufferAttachmentEnum(this, attachment))
206
0
                return false;
207
0
        }
208
0
    } else {
209
0
        for (const auto& attachment : attachments) {
210
0
            if (!ValidateBackbufferAttachmentEnum(this, attachment))
211
0
                return false;
212
0
        }
213
0
214
0
        if (!isDefaultFB) {
215
0
            MOZ_ASSERT(scopedVector->empty());
216
0
            scopedVector->reserve(attachments.Length());
217
0
            for (const auto& attachment : attachments) {
218
0
                switch (attachment) {
219
0
                case LOCAL_GL_COLOR:
220
0
                    scopedVector->push_back(LOCAL_GL_COLOR_ATTACHMENT0);
221
0
                    break;
222
0
223
0
                case LOCAL_GL_DEPTH:
224
0
                    scopedVector->push_back(LOCAL_GL_DEPTH_ATTACHMENT);
225
0
                    break;
226
0
227
0
                case LOCAL_GL_STENCIL:
228
0
                    scopedVector->push_back(LOCAL_GL_STENCIL_ATTACHMENT);
229
0
                    break;
230
0
231
0
                default:
232
0
                    MOZ_CRASH();
233
0
                }
234
0
            }
235
0
            *out_glNumAttachments = scopedVector->size();
236
0
            *out_glAttachments = scopedVector->data();
237
0
        }
238
0
    }
239
0
240
0
    ////
241
0
242
0
    if (!fb) {
243
0
        mDefaultFB_IsInvalid = true;
244
0
        mResolvedDefaultFB = nullptr;
245
0
    }
246
0
    return true;
247
0
}
248
249
void
250
WebGL2Context::InvalidateFramebuffer(GLenum target,
251
                                     const dom::Sequence<GLenum>& attachments,
252
                                     ErrorResult& rv)
253
0
{
254
0
    const FuncScope funcScope(*this, "invalidateFramebuffer");
255
0
256
0
    std::vector<GLenum> scopedVector;
257
0
    GLsizei glNumAttachments;
258
0
    const GLenum* glAttachments;
259
0
    if (!ValidateInvalidateFramebuffer(target, attachments, &rv, &scopedVector,
260
0
                                       &glNumAttachments, &glAttachments))
261
0
    {
262
0
        return;
263
0
    }
264
0
265
0
    ////
266
0
267
0
    // Some drivers (like OSX 10.9 GL) just don't support invalidate_framebuffer.
268
0
    const bool useFBInvalidation = (mAllowFBInvalidation &&
269
0
                                    gl->IsSupported(gl::GLFeature::invalidate_framebuffer));
270
0
    if (useFBInvalidation) {
271
0
        gl->fInvalidateFramebuffer(target, glNumAttachments, glAttachments);
272
0
        return;
273
0
    }
274
0
275
0
    // Use clear instead?
276
0
    // No-op for now.
277
0
}
278
279
void
280
WebGL2Context::InvalidateSubFramebuffer(GLenum target, const dom::Sequence<GLenum>& attachments,
281
                                        GLint x, GLint y, GLsizei width, GLsizei height,
282
                                        ErrorResult& rv)
283
0
{
284
0
    const FuncScope funcScope(*this, "invalidateSubFramebuffer");
285
0
286
0
    std::vector<GLenum> scopedVector;
287
0
    GLsizei glNumAttachments;
288
0
    const GLenum* glAttachments;
289
0
    if (!ValidateInvalidateFramebuffer(target, attachments, &rv, &scopedVector,
290
0
                                       &glNumAttachments, &glAttachments))
291
0
    {
292
0
        return;
293
0
    }
294
0
295
0
    if (!ValidateNonNegative("width", width) ||
296
0
        !ValidateNonNegative("height", height))
297
0
    {
298
0
        return;
299
0
    }
300
0
301
0
    ////
302
0
303
0
    // Some drivers (like OSX 10.9 GL) just don't support invalidate_framebuffer.
304
0
    const bool useFBInvalidation = (mAllowFBInvalidation &&
305
0
                                    gl->IsSupported(gl::GLFeature::invalidate_framebuffer));
306
0
    if (useFBInvalidation) {
307
0
        gl->fInvalidateSubFramebuffer(target, glNumAttachments, glAttachments, x, y,
308
0
                                      width, height);
309
0
        return;
310
0
    }
311
0
312
0
    // Use clear instead?
313
0
    // No-op for now.
314
0
}
315
316
void
317
WebGL2Context::ReadBuffer(GLenum mode)
318
0
{
319
0
    const FuncScope funcScope(*this, "readBuffer");
320
0
    if (IsContextLost())
321
0
        return;
322
0
323
0
    if (mBoundReadFramebuffer) {
324
0
        mBoundReadFramebuffer->ReadBuffer(mode);
325
0
        return;
326
0
    }
327
0
328
0
    // Operating on the default framebuffer.
329
0
    if (mode != LOCAL_GL_NONE &&
330
0
        mode != LOCAL_GL_BACK)
331
0
    {
332
0
        nsCString enumName;
333
0
        EnumName(mode, &enumName);
334
0
        ErrorInvalidOperation("If READ_FRAMEBUFFER is null, `mode` must be BACK or"
335
0
                              " NONE. Was %s.",
336
0
                              enumName.BeginReading());
337
0
        return;
338
0
    }
339
0
340
0
    mDefaultFB_ReadBuffer = mode;
341
0
}
342
343
} // namespace mozilla