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