1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 "ClientWebGLContext.h"
7
8 #include <limits>
9
10 #include "GLContext.h"
11 #include "WebGLBuffer.h"
12 #include "WebGLTransformFeedback.h"
13 #include "WebGLVertexArray.h"
14
15 namespace mozilla {
16
ValidateBufferSlot(GLenum target)17 RefPtr<WebGLBuffer>* WebGLContext::ValidateBufferSlot(GLenum target) {
18 RefPtr<WebGLBuffer>* slot = nullptr;
19
20 switch (target) {
21 case LOCAL_GL_ARRAY_BUFFER:
22 slot = &mBoundArrayBuffer;
23 break;
24
25 case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
26 slot = &(mBoundVertexArray->mElementArrayBuffer);
27 break;
28 }
29
30 if (IsWebGL2()) {
31 switch (target) {
32 case LOCAL_GL_COPY_READ_BUFFER:
33 slot = &mBoundCopyReadBuffer;
34 break;
35
36 case LOCAL_GL_COPY_WRITE_BUFFER:
37 slot = &mBoundCopyWriteBuffer;
38 break;
39
40 case LOCAL_GL_PIXEL_PACK_BUFFER:
41 slot = &mBoundPixelPackBuffer;
42 break;
43
44 case LOCAL_GL_PIXEL_UNPACK_BUFFER:
45 slot = &mBoundPixelUnpackBuffer;
46 break;
47
48 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
49 slot = &mBoundTransformFeedbackBuffer;
50 break;
51
52 case LOCAL_GL_UNIFORM_BUFFER:
53 slot = &mBoundUniformBuffer;
54 break;
55 }
56 }
57
58 if (!slot) {
59 ErrorInvalidEnumInfo("target", target);
60 return nullptr;
61 }
62
63 return slot;
64 }
65
ValidateBufferSelection(GLenum target) const66 WebGLBuffer* WebGLContext::ValidateBufferSelection(GLenum target) const {
67 const auto& slot =
68 const_cast<WebGLContext*>(this)->ValidateBufferSlot(target);
69 if (!slot) return nullptr;
70 const auto& buffer = *slot;
71
72 if (!buffer) {
73 ErrorInvalidOperation("Buffer for `target` is null.");
74 return nullptr;
75 }
76
77 if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER) {
78 if (mBoundTransformFeedback->IsActiveAndNotPaused()) {
79 ErrorInvalidOperation(
80 "Cannot select TRANSFORM_FEEDBACK_BUFFER when"
81 " transform feedback is active and unpaused.");
82 return nullptr;
83 }
84 const auto tfBuffers = std::vector<webgl::BufferAndIndex>{{
85 {buffer},
86 }};
87
88 if (!ValidateBuffersForTf(tfBuffers)) return nullptr;
89 } else {
90 if (mBoundTransformFeedback && !ValidateBufferForNonTf(buffer, target))
91 return nullptr;
92 }
93
94 return buffer.get();
95 }
96
ValidateIndexedBufferSlot(GLenum target,GLuint index)97 IndexedBufferBinding* WebGLContext::ValidateIndexedBufferSlot(GLenum target,
98 GLuint index) {
99 decltype(mIndexedUniformBufferBindings)* bindings;
100 const char* maxIndexEnum;
101 switch (target) {
102 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
103 if (mBoundTransformFeedback->mIsActive &&
104 !mBoundTransformFeedback->mIsPaused) {
105 ErrorInvalidOperation("Transform feedback active and not paused.");
106 return nullptr;
107 }
108 bindings = &(mBoundTransformFeedback->mIndexedBindings);
109 maxIndexEnum = "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS";
110 break;
111
112 case LOCAL_GL_UNIFORM_BUFFER:
113 bindings = &mIndexedUniformBufferBindings;
114 maxIndexEnum = "MAX_UNIFORM_BUFFER_BINDINGS";
115 break;
116
117 default:
118 ErrorInvalidEnumInfo("target", target);
119 return nullptr;
120 }
121
122 if (index >= bindings->size()) {
123 ErrorInvalidValue("`index` >= %s.", maxIndexEnum);
124 return nullptr;
125 }
126
127 return &(*bindings)[index];
128 }
129
130 ////////////////////////////////////////
131
BindBuffer(GLenum target,WebGLBuffer * buffer)132 void WebGLContext::BindBuffer(GLenum target, WebGLBuffer* buffer) {
133 FuncScope funcScope(*this, "bindBuffer");
134 if (IsContextLost()) return;
135 funcScope.mBindFailureGuard = true;
136
137 if (buffer && !ValidateObject("buffer", *buffer)) return;
138
139 const auto& slot = ValidateBufferSlot(target);
140 if (!slot) return;
141
142 if (buffer && !buffer->ValidateCanBindToTarget(target)) return;
143
144 if (!IsBufferTargetLazilyBound(target)) {
145 gl->fBindBuffer(target, buffer ? buffer->mGLName : 0);
146 }
147
148 *slot = buffer;
149 if (buffer) {
150 buffer->SetContentAfterBind(target);
151 }
152
153 funcScope.mBindFailureGuard = false;
154 }
155
156 ////////////////////////////////////////
157
ValidateIndexedBufferBinding(GLenum target,GLuint index,RefPtr<WebGLBuffer> ** const out_genericBinding,IndexedBufferBinding ** const out_indexedBinding)158 bool WebGLContext::ValidateIndexedBufferBinding(
159 GLenum target, GLuint index, RefPtr<WebGLBuffer>** const out_genericBinding,
160 IndexedBufferBinding** const out_indexedBinding) {
161 *out_genericBinding = ValidateBufferSlot(target);
162 if (!*out_genericBinding) return false;
163
164 *out_indexedBinding = ValidateIndexedBufferSlot(target, index);
165 if (!*out_indexedBinding) return false;
166
167 if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER &&
168 mBoundTransformFeedback->mIsActive) {
169 ErrorInvalidOperation(
170 "Cannot update indexed buffer bindings on active"
171 " transform feedback objects.");
172 return false;
173 }
174
175 return true;
176 }
177
BindBufferRange(GLenum target,GLuint index,WebGLBuffer * buffer,uint64_t offset,uint64_t size)178 void WebGLContext::BindBufferRange(GLenum target, GLuint index,
179 WebGLBuffer* buffer, uint64_t offset,
180 uint64_t size) {
181 FuncScope funcScope(*this, "bindBufferBase/Range");
182 if (buffer && !ValidateObject("buffer", *buffer)) return;
183 funcScope.mBindFailureGuard = true;
184
185 RefPtr<WebGLBuffer>* genericBinding;
186 IndexedBufferBinding* indexedBinding;
187 if (!ValidateIndexedBufferBinding(target, index, &genericBinding,
188 &indexedBinding)) {
189 return;
190 }
191
192 if (buffer && !buffer->ValidateCanBindToTarget(target)) return;
193
194 const auto& limits = Limits();
195 auto err =
196 CheckBindBufferRange(target, index, bool(buffer), offset, size, limits);
197 if (err) return;
198
199 ////
200
201 bool needsPrebind = false;
202 needsPrebind |= gl->IsANGLE();
203 #ifdef XP_MACOSX
204 needsPrebind = true;
205 #endif
206
207 if (gl->WorkAroundDriverBugs() && buffer && needsPrebind) {
208 // BindBufferBase/Range will fail (on some drivers) if the buffer name has
209 // never been bound. (GenBuffers makes a name, but BindBuffer initializes
210 // that name as a real buffer object)
211 gl->fBindBuffer(target, buffer->mGLName);
212 }
213
214 if (size) {
215 gl->fBindBufferRange(target, index, buffer ? buffer->mGLName : 0, offset,
216 size);
217 } else {
218 gl->fBindBufferBase(target, index, buffer ? buffer->mGLName : 0);
219 }
220
221 if (buffer) {
222 gl->fBindBuffer(target, 0); // Reset generic.
223 }
224
225 ////
226
227 *genericBinding = buffer;
228 indexedBinding->mBufferBinding = buffer;
229 indexedBinding->mRangeStart = offset;
230 indexedBinding->mRangeSize = size;
231
232 if (buffer) {
233 buffer->SetContentAfterBind(target);
234 }
235
236 funcScope.mBindFailureGuard = false;
237 }
238
239 ////////////////////////////////////////
240
BufferData(GLenum target,uint64_t dataLen,const uint8_t * data,GLenum usage) const241 void WebGLContext::BufferData(GLenum target, uint64_t dataLen,
242 const uint8_t* data, GLenum usage) const {
243 // `data` may be null.
244 const FuncScope funcScope(*this, "bufferData");
245 if (IsContextLost()) return;
246
247 const auto& buffer = ValidateBufferSelection(target);
248 if (!buffer) return;
249
250 buffer->BufferData(target, dataLen, data, usage);
251 }
252
253 ////////////////////////////////////////
254
BufferSubData(GLenum target,uint64_t dstByteOffset,uint64_t dataLen,const uint8_t * data) const255 void WebGLContext::BufferSubData(GLenum target, uint64_t dstByteOffset,
256 uint64_t dataLen, const uint8_t* data) const {
257 MOZ_ASSERT(data || !dataLen);
258 const FuncScope funcScope(*this, "bufferSubData");
259 if (IsContextLost()) return;
260
261 const auto& buffer = ValidateBufferSelection(target);
262 if (!buffer) return;
263 buffer->BufferSubData(target, dstByteOffset, dataLen, data);
264 }
265
266 ////////////////////////////////////////
267
CreateBuffer()268 RefPtr<WebGLBuffer> WebGLContext::CreateBuffer() {
269 const FuncScope funcScope(*this, "createBuffer");
270 if (IsContextLost()) return nullptr;
271
272 GLuint buf = 0;
273 gl->fGenBuffers(1, &buf);
274
275 return new WebGLBuffer(this, buf);
276 }
277
278 } // namespace mozilla
279