Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/canvas/WebGLContextBuffers.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 "WebGLContext.h"
7
8
#include "GLContext.h"
9
#include "WebGLBuffer.h"
10
#include "WebGLTransformFeedback.h"
11
#include "WebGLVertexArray.h"
12
13
namespace mozilla {
14
15
WebGLRefPtr<WebGLBuffer>*
16
WebGLContext::ValidateBufferSlot(GLenum target)
17
0
{
18
0
    WebGLRefPtr<WebGLBuffer>* slot = nullptr;
19
0
20
0
    switch (target) {
21
0
    case LOCAL_GL_ARRAY_BUFFER:
22
0
        slot = &mBoundArrayBuffer;
23
0
        break;
24
0
25
0
    case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
26
0
        slot = &(mBoundVertexArray->mElementArrayBuffer);
27
0
        break;
28
0
    }
29
0
30
0
    if (IsWebGL2()) {
31
0
        switch (target) {
32
0
        case LOCAL_GL_COPY_READ_BUFFER:
33
0
            slot = &mBoundCopyReadBuffer;
34
0
            break;
35
0
36
0
        case LOCAL_GL_COPY_WRITE_BUFFER:
37
0
            slot = &mBoundCopyWriteBuffer;
38
0
            break;
39
0
40
0
        case LOCAL_GL_PIXEL_PACK_BUFFER:
41
0
            slot = &mBoundPixelPackBuffer;
42
0
            break;
43
0
44
0
        case LOCAL_GL_PIXEL_UNPACK_BUFFER:
45
0
            slot = &mBoundPixelUnpackBuffer;
46
0
            break;
47
0
48
0
        case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
49
0
            slot = &mBoundTransformFeedbackBuffer;
50
0
            break;
51
0
52
0
        case LOCAL_GL_UNIFORM_BUFFER:
53
0
            slot = &mBoundUniformBuffer;
54
0
            break;
55
0
        }
56
0
    }
57
0
58
0
    if (!slot) {
59
0
        ErrorInvalidEnumInfo("target", target);
60
0
        return nullptr;
61
0
    }
62
0
63
0
    return slot;
64
0
}
65
66
WebGLBuffer*
67
WebGLContext::ValidateBufferSelection(GLenum target)
68
0
{
69
0
    const auto& slot = ValidateBufferSlot(target);
70
0
    if (!slot)
71
0
        return nullptr;
72
0
    const auto& buffer = *slot;
73
0
74
0
    if (!buffer) {
75
0
        ErrorInvalidOperation("Buffer for `target` is null.");
76
0
        return nullptr;
77
0
    }
78
0
79
0
    if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER) {
80
0
        if (mBoundTransformFeedback->IsActiveAndNotPaused()) {
81
0
            ErrorInvalidOperation("Cannot select TRANSFORM_FEEDBACK_BUFFER when"
82
0
                                  " transform feedback is active and unpaused.");
83
0
            return nullptr;
84
0
        }
85
0
        if (buffer->IsBoundForNonTF()) {
86
0
            ErrorInvalidOperation("Specified WebGLBuffer is currently bound for"
87
0
                                  " non-transform-feedback.");
88
0
            return nullptr;
89
0
        }
90
0
    } else {
91
0
        if (buffer->IsBoundForTF()) {
92
0
            ErrorInvalidOperation("Specified WebGLBuffer is currently bound for"
93
0
                                  " transform feedback.");
94
0
            return nullptr;
95
0
        }
96
0
    }
97
0
98
0
    return buffer.get();
99
0
}
100
101
IndexedBufferBinding*
102
WebGLContext::ValidateIndexedBufferSlot(GLenum target, GLuint index)
103
0
{
104
0
    decltype(mIndexedUniformBufferBindings)* bindings;
105
0
    const char* maxIndexEnum;
106
0
    switch (target) {
107
0
    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
108
0
        bindings = &(mBoundTransformFeedback->mIndexedBindings);
109
0
        maxIndexEnum = "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS";
110
0
        break;
111
0
112
0
    case LOCAL_GL_UNIFORM_BUFFER:
113
0
        bindings = &mIndexedUniformBufferBindings;
114
0
        maxIndexEnum = "MAX_UNIFORM_BUFFER_BINDINGS";
115
0
        break;
116
0
117
0
    default:
118
0
        ErrorInvalidEnumInfo("target", target);
119
0
        return nullptr;
120
0
    }
121
0
122
0
    if (index >= bindings->size()) {
123
0
        ErrorInvalidValue("`index` >= %s.", maxIndexEnum);
124
0
        return nullptr;
125
0
    }
126
0
127
0
    return &(*bindings)[index];
128
0
}
129
130
////////////////////////////////////////
131
132
void
133
WebGLContext::BindBuffer(GLenum target, WebGLBuffer* buffer)
134
0
{
135
0
    const FuncScope funcScope(*this, "bindBuffer");
136
0
    if (IsContextLost())
137
0
        return;
138
0
139
0
    if (buffer && !ValidateObject("buffer", *buffer))
140
0
        return;
141
0
142
0
    const auto& slot = ValidateBufferSlot(target);
143
0
    if (!slot)
144
0
        return;
145
0
146
0
    if (buffer && !buffer->ValidateCanBindToTarget(target))
147
0
        return;
148
0
149
0
    gl->fBindBuffer(target, buffer ? buffer->mGLName : 0);
150
0
151
0
    WebGLBuffer::SetSlot(target, buffer, slot);
152
0
    if (buffer) {
153
0
        buffer->SetContentAfterBind(target);
154
0
    }
155
0
156
0
    switch (target) {
157
0
    case LOCAL_GL_PIXEL_PACK_BUFFER:
158
0
    case LOCAL_GL_PIXEL_UNPACK_BUFFER:
159
0
        gl->fBindBuffer(target, 0);
160
0
        break;
161
0
    }
162
0
}
163
164
////////////////////////////////////////
165
166
bool
167
WebGLContext::ValidateIndexedBufferBinding(GLenum target, GLuint index,
168
                                           WebGLRefPtr<WebGLBuffer>** const out_genericBinding,
169
                                           IndexedBufferBinding** const out_indexedBinding)
170
0
{
171
0
    *out_genericBinding = ValidateBufferSlot(target);
172
0
    if (!*out_genericBinding)
173
0
        return false;
174
0
175
0
    *out_indexedBinding = ValidateIndexedBufferSlot(target, index);
176
0
    if (!*out_indexedBinding)
177
0
        return false;
178
0
179
0
    if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER &&
180
0
        mBoundTransformFeedback->mIsActive)
181
0
    {
182
0
        ErrorInvalidOperation("Cannot update indexed buffer bindings on active"
183
0
                              " transform feedback objects.");
184
0
        return false;
185
0
    }
186
0
187
0
    return true;
188
0
}
189
190
void
191
WebGLContext::BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer)
192
0
{
193
0
    const FuncScope funcScope(*this, "bindBufferBase");
194
0
    if (IsContextLost())
195
0
        return;
196
0
197
0
    if (buffer && !ValidateObject("buffer", *buffer))
198
0
        return;
199
0
200
0
    WebGLRefPtr<WebGLBuffer>* genericBinding;
201
0
    IndexedBufferBinding* indexedBinding;
202
0
    if (!ValidateIndexedBufferBinding(target, index, &genericBinding,
203
0
                                      &indexedBinding))
204
0
    {
205
0
        return;
206
0
    }
207
0
208
0
    if (buffer && !buffer->ValidateCanBindToTarget(target))
209
0
        return;
210
0
211
0
    ////
212
0
213
0
    gl->fBindBufferBase(target, index, buffer ? buffer->mGLName : 0);
214
0
215
0
    ////
216
0
217
0
    WebGLBuffer::SetSlot(target, buffer, genericBinding);
218
0
    WebGLBuffer::SetSlot(target, buffer, &indexedBinding->mBufferBinding);
219
0
    indexedBinding->mRangeStart = 0;
220
0
    indexedBinding->mRangeSize = 0;
221
0
222
0
    if (buffer) {
223
0
        buffer->SetContentAfterBind(target);
224
0
    }
225
0
}
226
227
void
228
WebGLContext::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer,
229
                              WebGLintptr offset, WebGLsizeiptr size)
230
0
{
231
0
    const FuncScope funcScope(*this, "bindBufferRange");
232
0
    if (IsContextLost())
233
0
        return;
234
0
235
0
    if (buffer && !ValidateObject("buffer", *buffer))
236
0
        return;
237
0
238
0
    if (!ValidateNonNegative("offset", offset) ||
239
0
        !ValidateNonNegative("size", size))
240
0
    {
241
0
        return;
242
0
    }
243
0
244
0
    WebGLRefPtr<WebGLBuffer>* genericBinding;
245
0
    IndexedBufferBinding* indexedBinding;
246
0
    if (!ValidateIndexedBufferBinding(target, index, &genericBinding,
247
0
                                      &indexedBinding))
248
0
    {
249
0
        return;
250
0
    }
251
0
252
0
    if (buffer && !buffer->ValidateCanBindToTarget(target))
253
0
        return;
254
0
255
0
    if (buffer && !size) {
256
0
        ErrorInvalidValue("Size must be non-zero for non-null buffer.");
257
0
        return;
258
0
    }
259
0
260
0
    ////
261
0
262
0
    switch (target) {
263
0
    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
264
0
        if (offset % 4 != 0 || size % 4 != 0) {
265
0
            ErrorInvalidValue("For %s, `offset` and `size` must be multiples of 4.",
266
0
                              "TRANSFORM_FEEDBACK_BUFFER");
267
0
            return;
268
0
        }
269
0
        break;
270
0
271
0
    case LOCAL_GL_UNIFORM_BUFFER:
272
0
        {
273
0
            GLuint offsetAlignment = 0;
274
0
            gl->GetUIntegerv(LOCAL_GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &offsetAlignment);
275
0
            if (offset % offsetAlignment != 0) {
276
0
                ErrorInvalidValue("For %s, `offset` must be a multiple of %s.",
277
0
                                  "UNIFORM_BUFFER",
278
0
                                  "UNIFORM_BUFFER_OFFSET_ALIGNMENT");
279
0
                return;
280
0
            }
281
0
        }
282
0
        break;
283
0
    }
284
0
285
0
    ////
286
0
287
#ifdef XP_MACOSX
288
    if (buffer && buffer->Content() == WebGLBuffer::Kind::Undefined &&
289
        gl->WorkAroundDriverBugs())
290
    {
291
        // BindBufferRange will fail if the buffer's contents is undefined.
292
        // Bind so driver initializes the buffer.
293
        gl->fBindBuffer(target, buffer->mGLName);
294
    }
295
#endif
296
297
0
    gl->fBindBufferRange(target, index, buffer ? buffer->mGLName : 0, offset, size);
298
0
299
0
    ////
300
0
301
0
    WebGLBuffer::SetSlot(target, buffer, genericBinding);
302
0
    WebGLBuffer::SetSlot(target, buffer, &indexedBinding->mBufferBinding);
303
0
    indexedBinding->mRangeStart = offset;
304
0
    indexedBinding->mRangeSize = size;
305
0
306
0
    if (buffer) {
307
0
        buffer->SetContentAfterBind(target);
308
0
    }
309
0
}
310
311
////////////////////////////////////////
312
313
void
314
WebGLContext::BufferDataImpl(GLenum target, size_t dataLen, const uint8_t* data,
315
                             GLenum usage)
316
0
{
317
0
318
0
    const auto& buffer = ValidateBufferSelection(target);
319
0
    if (!buffer)
320
0
        return;
321
0
322
0
    buffer->BufferData(target, dataLen, data, usage);
323
0
}
324
325
////
326
327
void
328
WebGLContext::BufferData(GLenum target, WebGLsizeiptr size, GLenum usage)
329
0
{
330
0
    const FuncScope funcScope(*this, "bufferData");
331
0
    if (IsContextLost())
332
0
        return;
333
0
334
0
    if (!ValidateNonNegative("size", size))
335
0
        return;
336
0
337
0
    ////
338
0
339
0
    const UniqueBuffer zeroBuffer(calloc(size, 1));
340
0
    if (!zeroBuffer)
341
0
        return ErrorOutOfMemory("Failed to allocate zeros.");
342
0
343
0
    BufferDataImpl(target, size_t(size), (const uint8_t*)zeroBuffer.get(), usage);
344
0
}
345
346
void
347
WebGLContext::BufferData(GLenum target, const dom::Nullable<dom::ArrayBuffer>& maybeSrc,
348
                         GLenum usage)
349
0
{
350
0
    const FuncScope funcScope(*this, "bufferData");
351
0
    if (IsContextLost())
352
0
        return;
353
0
354
0
    if (!ValidateNonNull("src", maybeSrc))
355
0
        return;
356
0
    const auto& src = maybeSrc.Value();
357
0
358
0
    src.ComputeLengthAndData();
359
0
    BufferDataImpl(target, src.LengthAllowShared(), src.DataAllowShared(), usage);
360
0
}
361
362
void
363
WebGLContext::BufferData(GLenum target, const dom::ArrayBufferView& src, GLenum usage,
364
                         GLuint srcElemOffset, GLuint srcElemCountOverride)
365
0
{
366
0
    const FuncScope funcScope(*this, "bufferData");
367
0
    if (IsContextLost())
368
0
        return;
369
0
370
0
    uint8_t* bytes;
371
0
    size_t byteLen;
372
0
    if (!ValidateArrayBufferView(src, srcElemOffset, srcElemCountOverride,
373
0
                                 &bytes, &byteLen))
374
0
    {
375
0
        return;
376
0
    }
377
0
378
0
    BufferDataImpl(target, byteLen, bytes, usage);
379
0
}
380
381
////////////////////////////////////////
382
383
void
384
WebGLContext::BufferSubDataImpl(GLenum target, WebGLsizeiptr dstByteOffset,
385
                                size_t dataLen, const uint8_t* data)
386
0
{
387
0
    const FuncScope funcScope(*this, "bufferSubData");
388
0
389
0
    if (!ValidateNonNegative("byteOffset", dstByteOffset))
390
0
        return;
391
0
392
0
    const auto& buffer = ValidateBufferSelection(target);
393
0
    if (!buffer)
394
0
        return;
395
0
396
0
    buffer->BufferSubData(target, size_t(dstByteOffset), dataLen, data);
397
0
}
398
399
////
400
401
void
402
WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset,
403
                            const dom::ArrayBuffer& src)
404
0
{
405
0
    const FuncScope funcScope(*this, "bufferSubData");
406
0
    if (IsContextLost())
407
0
        return;
408
0
409
0
    src.ComputeLengthAndData();
410
0
    BufferSubDataImpl(target, dstByteOffset, src.LengthAllowShared(),
411
0
                      src.DataAllowShared());
412
0
}
413
414
void
415
WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset,
416
                            const dom::ArrayBufferView& src, GLuint srcElemOffset,
417
                            GLuint srcElemCountOverride)
418
0
{
419
0
    const FuncScope funcScope(*this, "bufferSubData");
420
0
    if (IsContextLost())
421
0
        return;
422
0
423
0
    uint8_t* bytes;
424
0
    size_t byteLen;
425
0
    if (!ValidateArrayBufferView(src, srcElemOffset, srcElemCountOverride,
426
0
                                 &bytes, &byteLen))
427
0
    {
428
0
        return;
429
0
    }
430
0
431
0
    BufferSubDataImpl(target, dstByteOffset, byteLen, bytes);
432
0
}
433
434
////////////////////////////////////////
435
436
already_AddRefed<WebGLBuffer>
437
WebGLContext::CreateBuffer()
438
0
{
439
0
    const FuncScope funcScope(*this, "createBuffer");
440
0
    if (IsContextLost())
441
0
        return nullptr;
442
0
443
0
    GLuint buf = 0;
444
0
    gl->fGenBuffers(1, &buf);
445
0
446
0
    RefPtr<WebGLBuffer> globj = new WebGLBuffer(this, buf);
447
0
    return globj.forget();
448
0
}
449
450
void
451
WebGLContext::DeleteBuffer(WebGLBuffer* buffer)
452
0
{
453
0
    const FuncScope funcScope(*this, "deleteBuffer");
454
0
    if (!ValidateDeleteObject(buffer))
455
0
        return;
456
0
457
0
    ////
458
0
459
0
    const auto fnClearIfBuffer = [&](GLenum target, WebGLRefPtr<WebGLBuffer>& bindPoint) {
460
0
        if (bindPoint == buffer) {
461
0
            WebGLBuffer::SetSlot(target, nullptr, &bindPoint);
462
0
        }
463
0
    };
464
0
465
0
    fnClearIfBuffer(0, mBoundArrayBuffer);
466
0
    fnClearIfBuffer(0, mBoundVertexArray->mElementArrayBuffer);
467
0
468
0
    for (auto& cur : mBoundVertexArray->mAttribs) {
469
0
        fnClearIfBuffer(0, cur.mBuf);
470
0
    }
471
0
472
0
    // WebGL binding points
473
0
    if (IsWebGL2()) {
474
0
        fnClearIfBuffer(0, mBoundCopyReadBuffer);
475
0
        fnClearIfBuffer(0, mBoundCopyWriteBuffer);
476
0
        fnClearIfBuffer(0, mBoundPixelPackBuffer);
477
0
        fnClearIfBuffer(0, mBoundPixelUnpackBuffer);
478
0
        fnClearIfBuffer(0, mBoundUniformBuffer);
479
0
        fnClearIfBuffer(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER,
480
0
                        mBoundTransformFeedbackBuffer);
481
0
482
0
        if (!mBoundTransformFeedback->mIsActive) {
483
0
            for (auto& binding : mBoundTransformFeedback->mIndexedBindings) {
484
0
                fnClearIfBuffer(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER,
485
0
                                binding.mBufferBinding);
486
0
            }
487
0
        }
488
0
489
0
        for (auto& binding : mIndexedUniformBufferBindings) {
490
0
            fnClearIfBuffer(0, binding.mBufferBinding);
491
0
        }
492
0
    }
493
0
494
0
    ////
495
0
496
0
    buffer->RequestDelete();
497
0
}
498
499
} // namespace mozilla