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 #include "WebGLElementArrayCache.h"
12
13 namespace mozilla {
14
WebGLBuffer(WebGLContext * webgl,GLuint buf)15 WebGLBuffer::WebGLBuffer(WebGLContext* webgl, GLuint buf)
16 : WebGLRefCountedObject(webgl)
17 , mGLName(buf)
18 , mContent(Kind::Undefined)
19 , mUsage(LOCAL_GL_STATIC_DRAW)
20 , mByteLength(0)
21 , mTFBindCount(0)
22 , mNonTFBindCount(0)
23 {
24 mContext->mBuffers.insertBack(this);
25 }
26
~WebGLBuffer()27 WebGLBuffer::~WebGLBuffer()
28 {
29 DeleteOnce();
30 }
31
32 void
SetContentAfterBind(GLenum target)33 WebGLBuffer::SetContentAfterBind(GLenum target)
34 {
35 if (mContent != Kind::Undefined)
36 return;
37
38 switch (target) {
39 case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
40 mContent = Kind::ElementArray;
41 if (!mCache) {
42 mCache.reset(new WebGLElementArrayCache);
43 }
44 break;
45
46 case LOCAL_GL_ARRAY_BUFFER:
47 case LOCAL_GL_PIXEL_PACK_BUFFER:
48 case LOCAL_GL_PIXEL_UNPACK_BUFFER:
49 case LOCAL_GL_UNIFORM_BUFFER:
50 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
51 case LOCAL_GL_COPY_READ_BUFFER:
52 case LOCAL_GL_COPY_WRITE_BUFFER:
53 mContent = Kind::OtherData;
54 break;
55
56 default:
57 MOZ_CRASH("GFX: invalid target");
58 }
59 }
60
61 void
Delete()62 WebGLBuffer::Delete()
63 {
64 mContext->MakeContextCurrent();
65 mContext->gl->fDeleteBuffers(1, &mGLName);
66 mByteLength = 0;
67 mCache = nullptr;
68 LinkedListElement<WebGLBuffer>::remove(); // remove from mContext->mBuffers
69 }
70
71 ////////////////////////////////////////
72
73 static bool
ValidateBufferUsageEnum(WebGLContext * webgl,const char * funcName,GLenum usage)74 ValidateBufferUsageEnum(WebGLContext* webgl, const char* funcName, GLenum usage)
75 {
76 switch (usage) {
77 case LOCAL_GL_STREAM_DRAW:
78 case LOCAL_GL_STATIC_DRAW:
79 case LOCAL_GL_DYNAMIC_DRAW:
80 return true;
81
82 case LOCAL_GL_DYNAMIC_COPY:
83 case LOCAL_GL_DYNAMIC_READ:
84 case LOCAL_GL_STATIC_COPY:
85 case LOCAL_GL_STATIC_READ:
86 case LOCAL_GL_STREAM_COPY:
87 case LOCAL_GL_STREAM_READ:
88 if (MOZ_LIKELY(webgl->IsWebGL2()))
89 return true;
90 break;
91
92 default:
93 break;
94 }
95
96 webgl->ErrorInvalidEnum("%s: Invalid `usage`: 0x%04x", funcName, usage);
97 return false;
98 }
99
100 void
BufferData(GLenum target,size_t size,const void * data,GLenum usage)101 WebGLBuffer::BufferData(GLenum target, size_t size, const void* data, GLenum usage)
102 {
103 const char funcName[] = "bufferData";
104
105 // Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr
106 // is like intptr_t.
107 if (!CheckedInt<GLsizeiptr>(size).isValid())
108 return mContext->ErrorOutOfMemory("%s: bad size", funcName);
109
110 if (!ValidateBufferUsageEnum(mContext, funcName, usage))
111 return;
112
113 const auto& gl = mContext->gl;
114 gl->MakeCurrent();
115 const ScopedLazyBind lazyBind(gl, target, this);
116 mContext->InvalidateBufferFetching();
117
118 #ifdef XP_MACOSX
119 // bug 790879
120 if (gl->WorkAroundDriverBugs() &&
121 size > INT32_MAX)
122 {
123 mContext->ErrorOutOfMemory("%s: Allocation size too large.", funcName);
124 return;
125 }
126 #endif
127
128 const bool sizeChanges = (size != ByteLength());
129 if (sizeChanges) {
130 gl::GLContext::LocalErrorScope errorScope(*gl);
131 gl->fBufferData(target, size, data, usage);
132 const auto error = errorScope.GetError();
133
134 if (error) {
135 MOZ_ASSERT(error == LOCAL_GL_OUT_OF_MEMORY);
136 mContext->ErrorOutOfMemory("%s: Error from driver: 0x%04x", funcName, error);
137 return;
138 }
139 } else {
140 gl->fBufferData(target, size, data, usage);
141 }
142
143 mUsage = usage;
144 mByteLength = size;
145
146 // Warning: Possibly shared memory. See bug 1225033.
147 if (!ElementArrayCacheBufferData(data, size)) {
148 mByteLength = 0;
149 mContext->ErrorOutOfMemory("%s: Failed update index buffer cache.", funcName);
150 }
151 }
152
153 bool
ValidateRange(const char * funcName,size_t byteOffset,size_t byteLen) const154 WebGLBuffer::ValidateRange(const char* funcName, size_t byteOffset, size_t byteLen) const
155 {
156 auto availLength = mByteLength;
157 if (byteOffset > availLength) {
158 mContext->ErrorInvalidValue("%s: Offset passes the end of the buffer.", funcName);
159 return false;
160 }
161 availLength -= byteOffset;
162
163 if (byteLen > availLength) {
164 mContext->ErrorInvalidValue("%s: Offset+size passes the end of the buffer.",
165 funcName);
166 return false;
167 }
168
169 return true;
170 }
171
172 ////////////////////////////////////////
173
174 bool
ElementArrayCacheBufferData(const void * ptr,size_t bufferSizeInBytes)175 WebGLBuffer::ElementArrayCacheBufferData(const void* ptr,
176 size_t bufferSizeInBytes)
177 {
178 if (mContext->IsWebGL2())
179 return true;
180
181 if (mContent == Kind::ElementArray)
182 return mCache->BufferData(ptr, bufferSizeInBytes);
183
184 return true;
185 }
186
187 void
ElementArrayCacheBufferSubData(size_t pos,const void * ptr,size_t updateSizeInBytes)188 WebGLBuffer::ElementArrayCacheBufferSubData(size_t pos, const void* ptr,
189 size_t updateSizeInBytes)
190 {
191 if (mContext->IsWebGL2())
192 return;
193
194 if (mContent == Kind::ElementArray)
195 mCache->BufferSubData(pos, ptr, updateSizeInBytes);
196 }
197
198 size_t
SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const199 WebGLBuffer::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
200 {
201 size_t sizeOfCache = mCache ? mCache->SizeOfIncludingThis(mallocSizeOf)
202 : 0;
203 return mallocSizeOf(this) + sizeOfCache;
204 }
205
206 bool
Validate(GLenum type,uint32_t maxAllowed,size_t first,size_t count) const207 WebGLBuffer::Validate(GLenum type, uint32_t maxAllowed, size_t first, size_t count) const
208 {
209 if (mContext->IsWebGL2())
210 return true;
211
212 return mCache->Validate(type, maxAllowed, first, count);
213 }
214
215 bool
IsElementArrayUsedWithMultipleTypes() const216 WebGLBuffer::IsElementArrayUsedWithMultipleTypes() const
217 {
218 if (mContext->IsWebGL2())
219 return false;
220
221 return mCache->BeenUsedWithMultipleTypes();
222 }
223
224 ////
225
226 bool
ValidateCanBindToTarget(const char * funcName,GLenum target)227 WebGLBuffer::ValidateCanBindToTarget(const char* funcName, GLenum target)
228 {
229 /* https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.1
230 *
231 * In the WebGL 2 API, buffers have their WebGL buffer type
232 * initially set to undefined. Calling bindBuffer, bindBufferRange
233 * or bindBufferBase with the target argument set to any buffer
234 * binding point except COPY_READ_BUFFER or COPY_WRITE_BUFFER will
235 * then set the WebGL buffer type of the buffer being bound
236 * according to the table above.
237 *
238 * Any call to one of these functions which attempts to bind a
239 * WebGLBuffer that has the element array WebGL buffer type to a
240 * binding point that falls under other data, or bind a
241 * WebGLBuffer which has the other data WebGL buffer type to
242 * ELEMENT_ARRAY_BUFFER will generate an INVALID_OPERATION error,
243 * and the state of the binding point will remain untouched.
244 */
245
246 if (mContent == WebGLBuffer::Kind::Undefined)
247 return true;
248
249 switch (target) {
250 case LOCAL_GL_COPY_READ_BUFFER:
251 case LOCAL_GL_COPY_WRITE_BUFFER:
252 return true;
253
254 case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
255 if (mContent == WebGLBuffer::Kind::ElementArray)
256 return true;
257 break;
258
259 case LOCAL_GL_ARRAY_BUFFER:
260 case LOCAL_GL_PIXEL_PACK_BUFFER:
261 case LOCAL_GL_PIXEL_UNPACK_BUFFER:
262 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
263 case LOCAL_GL_UNIFORM_BUFFER:
264 if (mContent == WebGLBuffer::Kind::OtherData)
265 return true;
266 break;
267
268 default:
269 MOZ_CRASH();
270 }
271
272 const auto dataType = (mContent == WebGLBuffer::Kind::OtherData) ? "other"
273 : "element";
274 mContext->ErrorInvalidOperation("%s: Buffer already contains %s data.", funcName,
275 dataType);
276 return false;
277 }
278
279 JSObject*
WrapObject(JSContext * cx,JS::Handle<JSObject * > givenProto)280 WebGLBuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
281 {
282 return dom::WebGLBufferBinding::Wrap(cx, this, givenProto);
283 }
284
285 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLBuffer)
286
287 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLBuffer, AddRef)
288 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLBuffer, Release)
289
290 } // namespace mozilla
291