Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/canvas/WebGLBuffer.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 "WebGLBuffer.h"
7
8
#include "GLContext.h"
9
#include "mozilla/dom/WebGLRenderingContextBinding.h"
10
#include "WebGLContext.h"
11
12
namespace mozilla {
13
14
WebGLBuffer::WebGLBuffer(WebGLContext* webgl, GLuint buf)
15
    : WebGLRefCountedObject(webgl)
16
    , mGLName(buf)
17
    , mContent(Kind::Undefined)
18
    , mUsage(LOCAL_GL_STATIC_DRAW)
19
    , mByteLength(0)
20
    , mTFBindCount(0)
21
    , mNonTFBindCount(0)
22
0
{
23
0
    mContext->mBuffers.insertBack(this);
24
0
}
25
26
WebGLBuffer::~WebGLBuffer()
27
0
{
28
0
    DeleteOnce();
29
0
}
30
31
void
32
WebGLBuffer::SetContentAfterBind(GLenum target)
33
0
{
34
0
    if (mContent != Kind::Undefined)
35
0
        return;
36
0
37
0
    switch (target) {
38
0
    case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
39
0
        mContent = Kind::ElementArray;
40
0
        break;
41
0
42
0
    case LOCAL_GL_ARRAY_BUFFER:
43
0
    case LOCAL_GL_PIXEL_PACK_BUFFER:
44
0
    case LOCAL_GL_PIXEL_UNPACK_BUFFER:
45
0
    case LOCAL_GL_UNIFORM_BUFFER:
46
0
    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
47
0
    case LOCAL_GL_COPY_READ_BUFFER:
48
0
    case LOCAL_GL_COPY_WRITE_BUFFER:
49
0
        mContent = Kind::OtherData;
50
0
        break;
51
0
52
0
    default:
53
0
        MOZ_CRASH("GFX: invalid target");
54
0
    }
55
0
}
56
57
void
58
WebGLBuffer::Delete()
59
0
{
60
0
    mContext->gl->fDeleteBuffers(1, &mGLName);
61
0
62
0
    mByteLength = 0;
63
0
    mFetchInvalidator.InvalidateCaches();
64
0
65
0
    mIndexCache = nullptr;
66
0
    mIndexRanges.clear();
67
0
    LinkedListElement<WebGLBuffer>::remove(); // remove from mContext->mBuffers
68
0
}
69
70
////////////////////////////////////////
71
72
static bool
73
ValidateBufferUsageEnum(WebGLContext* webgl, GLenum usage)
74
0
{
75
0
    switch (usage) {
76
0
    case LOCAL_GL_STREAM_DRAW:
77
0
    case LOCAL_GL_STATIC_DRAW:
78
0
    case LOCAL_GL_DYNAMIC_DRAW:
79
0
        return true;
80
0
81
0
    case LOCAL_GL_DYNAMIC_COPY:
82
0
    case LOCAL_GL_DYNAMIC_READ:
83
0
    case LOCAL_GL_STATIC_COPY:
84
0
    case LOCAL_GL_STATIC_READ:
85
0
    case LOCAL_GL_STREAM_COPY:
86
0
    case LOCAL_GL_STREAM_READ:
87
0
        if (MOZ_LIKELY(webgl->IsWebGL2()))
88
0
            return true;
89
0
        break;
90
0
91
0
    default:
92
0
        break;
93
0
    }
94
0
95
0
    webgl->ErrorInvalidEnumInfo("usage", usage);
96
0
    return false;
97
0
}
98
99
void
100
WebGLBuffer::BufferData(GLenum target, size_t size, const void* data, GLenum usage)
101
0
{
102
0
    // Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr
103
0
    // is like intptr_t.
104
0
    if (!CheckedInt<GLsizeiptr>(size).isValid())
105
0
        return mContext->ErrorOutOfMemory("bad size");
106
0
107
0
    if (!ValidateBufferUsageEnum(mContext, usage))
108
0
        return;
109
0
110
#ifdef XP_MACOSX
111
    // bug 790879
112
    if (mContext->gl->WorkAroundDriverBugs() &&
113
        size > INT32_MAX)
114
    {
115
        mContext->ErrorOutOfMemory("Allocation size too large.");
116
        return;
117
    }
118
#endif
119
120
0
    const void* uploadData = data;
121
0
122
0
    UniqueBuffer newIndexCache;
123
0
    if (target == LOCAL_GL_ELEMENT_ARRAY_BUFFER &&
124
0
        mContext->mNeedsIndexValidation)
125
0
    {
126
0
        newIndexCache = malloc(size);
127
0
        if (!newIndexCache) {
128
0
            mContext->ErrorOutOfMemory("Failed to alloc index cache.");
129
0
            return;
130
0
        }
131
0
        memcpy(newIndexCache.get(), data, size);
132
0
        uploadData = newIndexCache.get();
133
0
    }
134
0
135
0
    const auto& gl = mContext->gl;
136
0
    const ScopedLazyBind lazyBind(gl, target, this);
137
0
138
0
    const bool sizeChanges = (size != ByteLength());
139
0
    if (sizeChanges) {
140
0
        gl::GLContext::LocalErrorScope errorScope(*gl);
141
0
        gl->fBufferData(target, size, uploadData, usage);
142
0
        const auto error = errorScope.GetError();
143
0
144
0
        if (error) {
145
0
            MOZ_ASSERT(error == LOCAL_GL_OUT_OF_MEMORY);
146
0
            mContext->ErrorOutOfMemory("Error from driver: 0x%04x", error);
147
0
            return;
148
0
        }
149
0
    } else {
150
0
        gl->fBufferData(target, size, uploadData, usage);
151
0
    }
152
0
153
0
    mContext->OnDataAllocCall();
154
0
155
0
    mUsage = usage;
156
0
    mByteLength = size;
157
0
    mFetchInvalidator.InvalidateCaches();
158
0
    mIndexCache = std::move(newIndexCache);
159
0
160
0
    if (mIndexCache) {
161
0
        if (!mIndexRanges.empty()) {
162
0
            mContext->GeneratePerfWarning("[%p] Invalidating %u ranges.", this,
163
0
                                          uint32_t(mIndexRanges.size()));
164
0
            mIndexRanges.clear();
165
0
        }
166
0
    }
167
0
168
0
    ResetLastUpdateFenceId();
169
0
}
170
171
void
172
WebGLBuffer::BufferSubData(GLenum target, size_t dstByteOffset, size_t dataLen,
173
                           const void* data) const
174
0
{
175
0
    if (!ValidateRange(dstByteOffset, dataLen))
176
0
        return;
177
0
178
0
    if (!CheckedInt<GLintptr>(dataLen).isValid())
179
0
        return mContext->ErrorOutOfMemory("Size too large.");
180
0
181
0
    ////
182
0
183
0
    const void* uploadData = data;
184
0
    if (mIndexCache) {
185
0
        const auto cachedDataBegin = (uint8_t*)mIndexCache.get() + dstByteOffset;
186
0
        memcpy(cachedDataBegin, data, dataLen);
187
0
        uploadData = cachedDataBegin;
188
0
189
0
        InvalidateCacheRange(dstByteOffset, dataLen);
190
0
    }
191
0
192
0
    ////
193
0
194
0
    const auto& gl = mContext->gl;
195
0
    const ScopedLazyBind lazyBind(gl, target, this);
196
0
197
0
    gl->fBufferSubData(target, dstByteOffset, dataLen, uploadData);
198
0
199
0
    ResetLastUpdateFenceId();
200
0
}
201
202
bool
203
WebGLBuffer::ValidateRange(size_t byteOffset, size_t byteLen) const
204
0
{
205
0
    auto availLength = mByteLength;
206
0
    if (byteOffset > availLength) {
207
0
        mContext->ErrorInvalidValue("Offset passes the end of the buffer.");
208
0
        return false;
209
0
    }
210
0
    availLength -= byteOffset;
211
0
212
0
    if (byteLen > availLength) {
213
0
        mContext->ErrorInvalidValue("Offset+size passes the end of the buffer.");
214
0
        return false;
215
0
    }
216
0
217
0
    return true;
218
0
}
219
220
////////////////////////////////////////
221
222
static uint8_t
223
IndexByteSizeByType(GLenum type)
224
0
{
225
0
    switch (type) {
226
0
    case LOCAL_GL_UNSIGNED_BYTE:  return 1;
227
0
    case LOCAL_GL_UNSIGNED_SHORT: return 2;
228
0
    case LOCAL_GL_UNSIGNED_INT:   return 4;
229
0
    default:
230
0
        MOZ_CRASH();
231
0
    }
232
0
}
233
234
void
235
WebGLBuffer::InvalidateCacheRange(uint64_t byteOffset, uint64_t byteLength) const
236
0
{
237
0
    MOZ_ASSERT(mIndexCache);
238
0
239
0
    std::vector<IndexRange> invalids;
240
0
    const uint64_t updateBegin = byteOffset;
241
0
    const uint64_t updateEnd = updateBegin + byteLength;
242
0
    for (const auto& cur : mIndexRanges) {
243
0
        const auto& range = cur.first;
244
0
        const auto& indexByteSize = IndexByteSizeByType(range.type);
245
0
        const auto rangeBegin = range.byteOffset * indexByteSize;
246
0
        const auto rangeEnd = rangeBegin + uint64_t(range.indexCount) * indexByteSize;
247
0
        if (rangeBegin >= updateEnd || rangeEnd <= updateBegin)
248
0
            continue;
249
0
        invalids.push_back(range);
250
0
    }
251
0
252
0
    if (!invalids.empty()) {
253
0
        mContext->GeneratePerfWarning("[%p] Invalidating %u/%u ranges.", this,
254
0
                                      uint32_t(invalids.size()),
255
0
                                      uint32_t(mIndexRanges.size()));
256
0
257
0
        for (const auto& cur : invalids) {
258
0
            mIndexRanges.erase(cur);
259
0
        }
260
0
    }
261
0
}
262
263
size_t
264
WebGLBuffer::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
265
0
{
266
0
    size_t size = mallocSizeOf(this);
267
0
    if (mIndexCache) {
268
0
        size += mByteLength;
269
0
    }
270
0
    return size;
271
0
}
272
273
template<typename T>
274
static Maybe<uint32_t>
275
MaxForRange(const void* const start, const uint32_t count,
276
            const Maybe<uint32_t>& untypedIgnoredVal)
277
0
{
278
0
    const Maybe<T> ignoredVal = (untypedIgnoredVal ? Some(T(untypedIgnoredVal.value()))
279
0
                                                   : Nothing());
280
0
    Maybe<uint32_t> maxVal;
281
0
282
0
    auto itr = (const T*)start;
283
0
    const auto end = itr + count;
284
0
285
0
    for (; itr != end; ++itr) {
286
0
        const auto& val = *itr;
287
0
        if (ignoredVal && val == ignoredVal.value())
288
0
            continue;
289
0
290
0
        if (maxVal && val <= maxVal.value())
291
0
            continue;
292
0
293
0
        maxVal = Some(val);
294
0
    }
295
0
296
0
    return maxVal;
297
0
}
Unexecuted instantiation: Unified_cpp_dom_canvas1.cpp:mozilla::Maybe<unsigned int> mozilla::MaxForRange<unsigned char>(void const*, unsigned int, mozilla::Maybe<unsigned int> const&)
Unexecuted instantiation: Unified_cpp_dom_canvas1.cpp:mozilla::Maybe<unsigned int> mozilla::MaxForRange<unsigned short>(void const*, unsigned int, mozilla::Maybe<unsigned int> const&)
Unexecuted instantiation: Unified_cpp_dom_canvas1.cpp:mozilla::Maybe<unsigned int> mozilla::MaxForRange<unsigned int>(void const*, unsigned int, mozilla::Maybe<unsigned int> const&)
298
299
static const uint32_t kMaxIndexRanges = 256;
300
301
Maybe<uint32_t>
302
WebGLBuffer::GetIndexedFetchMaxVert(const GLenum type, const uint64_t byteOffset,
303
                                    const uint32_t indexCount) const
304
0
{
305
0
    if (!mIndexCache)
306
0
        return Nothing();
307
0
308
0
    const IndexRange range = { type, byteOffset, indexCount };
309
0
    auto res = mIndexRanges.insert({ range, Nothing() });
310
0
    if (mIndexRanges.size() > kMaxIndexRanges) {
311
0
        mContext->GeneratePerfWarning("[%p] Clearing mIndexRanges after exceeding %u.",
312
0
                                      this, kMaxIndexRanges);
313
0
        mIndexRanges.clear();
314
0
        res = mIndexRanges.insert({ range, Nothing() });
315
0
    }
316
0
317
0
    const auto& itr = res.first;
318
0
    const auto& didInsert = res.second;
319
0
320
0
    auto& maxFetchIndex = itr->second;
321
0
    if (didInsert) {
322
0
        const auto& data = mIndexCache.get();
323
0
324
0
        const auto start = (const uint8_t*)data + byteOffset;
325
0
326
0
        Maybe<uint32_t> ignoredVal;
327
0
        if (mContext->IsWebGL2()) {
328
0
            ignoredVal = Some(UINT32_MAX);
329
0
        }
330
0
331
0
        switch (type) {
332
0
        case LOCAL_GL_UNSIGNED_BYTE:
333
0
            maxFetchIndex = MaxForRange<uint8_t>(start, indexCount, ignoredVal);
334
0
            break;
335
0
        case LOCAL_GL_UNSIGNED_SHORT:
336
0
            maxFetchIndex = MaxForRange<uint16_t>(start, indexCount, ignoredVal);
337
0
            break;
338
0
        case LOCAL_GL_UNSIGNED_INT:
339
0
            maxFetchIndex = MaxForRange<uint32_t>(start, indexCount, ignoredVal);
340
0
            break;
341
0
        default:
342
0
            MOZ_CRASH();
343
0
        }
344
0
        const auto displayMaxVertIndex = maxFetchIndex ? int64_t(maxFetchIndex.value())
345
0
                                                       : -1;
346
0
        mContext->GeneratePerfWarning("[%p] New range #%u: (0x%04x, %" PRIu64 ", %u):"
347
0
                                      " %" PRIi64,
348
0
                                      this, uint32_t(mIndexRanges.size()), range.type,
349
0
                                      range.byteOffset, range.indexCount,
350
0
                                      displayMaxVertIndex);
351
0
    }
352
0
353
0
    return maxFetchIndex;
354
0
}
355
356
////
357
358
bool
359
WebGLBuffer::ValidateCanBindToTarget(GLenum target)
360
0
{
361
0
    /* https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.1
362
0
     *
363
0
     * In the WebGL 2 API, buffers have their WebGL buffer type
364
0
     * initially set to undefined. Calling bindBuffer, bindBufferRange
365
0
     * or bindBufferBase with the target argument set to any buffer
366
0
     * binding point except COPY_READ_BUFFER or COPY_WRITE_BUFFER will
367
0
     * then set the WebGL buffer type of the buffer being bound
368
0
     * according to the table above.
369
0
     *
370
0
     * Any call to one of these functions which attempts to bind a
371
0
     * WebGLBuffer that has the element array WebGL buffer type to a
372
0
     * binding point that falls under other data, or bind a
373
0
     * WebGLBuffer which has the other data WebGL buffer type to
374
0
     * ELEMENT_ARRAY_BUFFER will generate an INVALID_OPERATION error,
375
0
     * and the state of the binding point will remain untouched.
376
0
     */
377
0
378
0
    if (mContent == WebGLBuffer::Kind::Undefined)
379
0
        return true;
380
0
381
0
    switch (target) {
382
0
    case LOCAL_GL_COPY_READ_BUFFER:
383
0
    case LOCAL_GL_COPY_WRITE_BUFFER:
384
0
        return true;
385
0
386
0
    case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
387
0
        if (mContent == WebGLBuffer::Kind::ElementArray)
388
0
            return true;
389
0
        break;
390
0
391
0
    case LOCAL_GL_ARRAY_BUFFER:
392
0
    case LOCAL_GL_PIXEL_PACK_BUFFER:
393
0
    case LOCAL_GL_PIXEL_UNPACK_BUFFER:
394
0
    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
395
0
    case LOCAL_GL_UNIFORM_BUFFER:
396
0
        if (mContent == WebGLBuffer::Kind::OtherData)
397
0
            return true;
398
0
        break;
399
0
400
0
    default:
401
0
        MOZ_CRASH();
402
0
    }
403
0
404
0
    const auto dataType = (mContent == WebGLBuffer::Kind::OtherData) ? "other"
405
0
                                                                     : "element";
406
0
    mContext->ErrorInvalidOperation("Buffer already contains %s data.",
407
0
                                    dataType);
408
0
    return false;
409
0
}
410
411
void
412
WebGLBuffer::ResetLastUpdateFenceId() const
413
0
{
414
0
    mLastUpdateFenceId = mContext->mNextFenceId;
415
0
}
416
417
JSObject*
418
WebGLBuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
419
0
{
420
0
    return dom::WebGLBuffer_Binding::Wrap(cx, this, givenProto);
421
0
}
422
423
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLBuffer)
424
425
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLBuffer, AddRef)
426
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLBuffer, Release)
427
428
} // namespace mozilla