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