1 //
2 // Copyright 2015 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // BufferGL.cpp: Implements the class methods for BufferGL.
8
9 #include "libANGLE/renderer/gl/BufferGL.h"
10
11 #include "common/debug.h"
12 #include "common/utilities.h"
13 #include "libANGLE/Context.h"
14 #include "libANGLE/angletypes.h"
15 #include "libANGLE/formatutils.h"
16 #include "libANGLE/renderer/gl/ContextGL.h"
17 #include "libANGLE/renderer/gl/FunctionsGL.h"
18 #include "libANGLE/renderer/gl/StateManagerGL.h"
19 #include "libANGLE/renderer/gl/renderergl_utils.h"
20
21 namespace rx
22 {
23
24 namespace
25 {
26
KeepBufferShadowCopy(const gl::Context * context,gl::WebGLBufferType webglType)27 bool KeepBufferShadowCopy(const gl::Context *context, gl::WebGLBufferType webglType)
28 {
29 // Always keep a shadow copy if the feature is enabled. This usually means there is no other way
30 // to read back the data.
31 const angle::FeaturesGL &features = GetFeaturesGL(context);
32 if (features.keepBufferShadowCopy.enabled)
33 {
34 return true;
35 }
36
37 // Shadow WebGL index buffers when the driver is unable to provide robust access.
38 // WebGL element array buffers cannot be bound to other binding points or written to on the GPU
39 // so the shadowed data will never be invalidated.
40 if (context->isWebGL() && context->isBufferAccessValidationEnabled() &&
41 webglType == gl::WebGLBufferType::ElementArray)
42 {
43 return true;
44 }
45
46 return false;
47 }
48
49 } // namespace
50
51 // Use the GL_COPY_READ_BUFFER binding when two buffers need to be bound simultaneously.
52 // GL_ELEMENT_ARRAY_BUFFER is supported on more versions but can modify the state of the currently
53 // bound VAO. Two simultaneous buffer bindings are only needed for glCopyBufferSubData which also
54 // adds the GL_COPY_READ_BUFFER binding.
55 static constexpr gl::BufferBinding SourceBufferOperationTarget = gl::BufferBinding::CopyRead;
56
57 // Use the GL_ELEMENT_ARRAY_BUFFER binding for most operations since it's available on all
58 // supported GL versions and doesn't affect any current state when it changes.
59 static constexpr gl::BufferBinding DestBufferOperationTarget = gl::BufferBinding::Array;
60
BufferGL(const gl::BufferState & state,GLuint buffer)61 BufferGL::BufferGL(const gl::BufferState &state, GLuint buffer)
62 : BufferImpl(state),
63 mIsMapped(false),
64 mMapOffset(0),
65 mMapSize(0),
66 mShadowCopy(),
67 mBufferSize(0),
68 mBufferID(buffer)
69 {}
70
~BufferGL()71 BufferGL::~BufferGL()
72 {
73 ASSERT(mBufferID == 0);
74 }
75
destroy(const gl::Context * context)76 void BufferGL::destroy(const gl::Context *context)
77 {
78 StateManagerGL *stateManager = GetStateManagerGL(context);
79 stateManager->deleteBuffer(mBufferID);
80 mBufferID = 0;
81 }
82
setData(const gl::Context * context,gl::BufferBinding target,const void * data,size_t size,gl::BufferUsage usage)83 angle::Result BufferGL::setData(const gl::Context *context,
84 gl::BufferBinding target,
85 const void *data,
86 size_t size,
87 gl::BufferUsage usage)
88 {
89 ContextGL *contextGL = GetImplAs<ContextGL>(context);
90 const FunctionsGL *functions = GetFunctionsGL(context);
91 StateManagerGL *stateManager = GetStateManagerGL(context);
92
93 stateManager->bindBuffer(DestBufferOperationTarget, mBufferID);
94 ANGLE_GL_TRY(context, functions->bufferData(gl::ToGLenum(DestBufferOperationTarget), size, data,
95 ToGLenum(usage)));
96
97 // Initialize the shadow buffer if needed. Don't delete existing shadow data. WebGL allows users
98 // to bind as an element array buffer first and then copy source/dest later (but not the other
99 // way around).
100 if (KeepBufferShadowCopy(context, mState.getWebGLType()) && !mShadowCopy.has_value())
101 {
102 mShadowCopy = angle::MemoryBuffer();
103 }
104
105 if (mShadowCopy.has_value())
106 {
107 ANGLE_CHECK_GL_ALLOC(contextGL, mShadowCopy->resize(size));
108
109 if (size > 0 && data != nullptr)
110 {
111 memcpy(mShadowCopy->data(), data, size);
112 }
113 }
114
115 mBufferSize = size;
116
117 contextGL->markWorkSubmitted();
118
119 return angle::Result::Continue;
120 }
121
setSubData(const gl::Context * context,gl::BufferBinding target,const void * data,size_t size,size_t offset)122 angle::Result BufferGL::setSubData(const gl::Context *context,
123 gl::BufferBinding target,
124 const void *data,
125 size_t size,
126 size_t offset)
127 {
128 ContextGL *contextGL = GetImplAs<ContextGL>(context);
129 const FunctionsGL *functions = GetFunctionsGL(context);
130 StateManagerGL *stateManager = GetStateManagerGL(context);
131
132 stateManager->bindBuffer(DestBufferOperationTarget, mBufferID);
133 ANGLE_GL_TRY(context, functions->bufferSubData(gl::ToGLenum(DestBufferOperationTarget), offset,
134 size, data));
135
136 if (mShadowCopy.has_value() && size > 0)
137 {
138 memcpy(mShadowCopy->data() + offset, data, size);
139 }
140
141 contextGL->markWorkSubmitted();
142
143 return angle::Result::Continue;
144 }
145
copySubData(const gl::Context * context,BufferImpl * source,GLintptr sourceOffset,GLintptr destOffset,GLsizeiptr size)146 angle::Result BufferGL::copySubData(const gl::Context *context,
147 BufferImpl *source,
148 GLintptr sourceOffset,
149 GLintptr destOffset,
150 GLsizeiptr size)
151 {
152 ContextGL *contextGL = GetImplAs<ContextGL>(context);
153 const FunctionsGL *functions = GetFunctionsGL(context);
154 StateManagerGL *stateManager = GetStateManagerGL(context);
155
156 BufferGL *sourceGL = GetAs<BufferGL>(source);
157
158 stateManager->bindBuffer(DestBufferOperationTarget, mBufferID);
159 stateManager->bindBuffer(SourceBufferOperationTarget, sourceGL->getBufferID());
160
161 ANGLE_GL_TRY(context, functions->copyBufferSubData(gl::ToGLenum(SourceBufferOperationTarget),
162 gl::ToGLenum(DestBufferOperationTarget),
163 sourceOffset, destOffset, size));
164
165 if (mShadowCopy.has_value() && size > 0)
166 {
167 // WebGL only allows copying between buffers that are marked as the same type. Both buffers
168 // would have to be element array buffers and have shadow data.
169 ASSERT(sourceGL->mShadowCopy.has_value());
170
171 ASSERT(sourceGL->mShadowCopy->size() >= static_cast<size_t>(sourceOffset + size));
172 memcpy(mShadowCopy->data() + destOffset, sourceGL->mShadowCopy->data() + sourceOffset,
173 size);
174 }
175
176 contextGL->markWorkSubmitted();
177
178 return angle::Result::Continue;
179 }
180
map(const gl::Context * context,GLenum access,void ** mapPtr)181 angle::Result BufferGL::map(const gl::Context *context, GLenum access, void **mapPtr)
182 {
183 ContextGL *contextGL = GetImplAs<ContextGL>(context);
184 const FunctionsGL *functions = GetFunctionsGL(context);
185 StateManagerGL *stateManager = GetStateManagerGL(context);
186
187 if (mShadowCopy.has_value())
188 {
189 *mapPtr = mShadowCopy->data();
190 }
191 else
192 {
193 stateManager->bindBuffer(DestBufferOperationTarget, mBufferID);
194 if (functions->mapBuffer)
195 {
196 *mapPtr = ANGLE_GL_TRY(
197 context, functions->mapBuffer(gl::ToGLenum(DestBufferOperationTarget), access));
198 }
199 else
200 {
201 ASSERT(functions->mapBufferRange && access == GL_WRITE_ONLY_OES);
202 *mapPtr = ANGLE_GL_TRY(
203 context, functions->mapBufferRange(gl::ToGLenum(DestBufferOperationTarget), 0,
204 mBufferSize, GL_MAP_WRITE_BIT));
205 }
206
207 // Unbind the mapped buffer from the array buffer binding. Some drivers generate errors if
208 // any mapped buffer is bound to array buffer bindings.
209 // crbug.com/1345777
210 stateManager->bindBuffer(DestBufferOperationTarget, 0);
211 }
212
213 mIsMapped = true;
214 mMapOffset = 0;
215 mMapSize = mBufferSize;
216
217 contextGL->markWorkSubmitted();
218
219 return angle::Result::Continue;
220 }
221
mapRange(const gl::Context * context,size_t offset,size_t length,GLbitfield access,void ** mapPtr)222 angle::Result BufferGL::mapRange(const gl::Context *context,
223 size_t offset,
224 size_t length,
225 GLbitfield access,
226 void **mapPtr)
227 {
228 ContextGL *contextGL = GetImplAs<ContextGL>(context);
229 const FunctionsGL *functions = GetFunctionsGL(context);
230 StateManagerGL *stateManager = GetStateManagerGL(context);
231
232 if (mShadowCopy.has_value())
233 {
234 *mapPtr = mShadowCopy->data() + offset;
235 }
236 else
237 {
238 stateManager->bindBuffer(DestBufferOperationTarget, mBufferID);
239 *mapPtr =
240 ANGLE_GL_TRY(context, functions->mapBufferRange(gl::ToGLenum(DestBufferOperationTarget),
241 offset, length, access));
242
243 // Unbind the mapped buffer from the array buffer binding. Some drivers generate errors if
244 // any mapped buffer is bound to array buffer bindings.
245 // crbug.com/1345777
246 stateManager->bindBuffer(DestBufferOperationTarget, 0);
247 }
248
249 mIsMapped = true;
250 mMapOffset = offset;
251 mMapSize = length;
252
253 contextGL->markWorkSubmitted();
254
255 return angle::Result::Continue;
256 }
257
unmap(const gl::Context * context,GLboolean * result)258 angle::Result BufferGL::unmap(const gl::Context *context, GLboolean *result)
259 {
260 ContextGL *contextGL = GetImplAs<ContextGL>(context);
261 const FunctionsGL *functions = GetFunctionsGL(context);
262 StateManagerGL *stateManager = GetStateManagerGL(context);
263
264 ASSERT(result);
265 ASSERT(mIsMapped);
266
267 if (mShadowCopy.has_value())
268 {
269 stateManager->bindBuffer(DestBufferOperationTarget, mBufferID);
270 ANGLE_GL_TRY(context,
271 functions->bufferSubData(gl::ToGLenum(DestBufferOperationTarget), mMapOffset,
272 mMapSize, mShadowCopy->data() + mMapOffset));
273 *result = GL_TRUE;
274 }
275 else
276 {
277 stateManager->bindBuffer(DestBufferOperationTarget, mBufferID);
278 *result =
279 ANGLE_GL_TRY(context, functions->unmapBuffer(gl::ToGLenum(DestBufferOperationTarget)));
280 }
281
282 mIsMapped = false;
283
284 contextGL->markWorkSubmitted();
285
286 return angle::Result::Continue;
287 }
288
getIndexRange(const gl::Context * context,gl::DrawElementsType type,size_t offset,size_t count,bool primitiveRestartEnabled,gl::IndexRange * outRange)289 angle::Result BufferGL::getIndexRange(const gl::Context *context,
290 gl::DrawElementsType type,
291 size_t offset,
292 size_t count,
293 bool primitiveRestartEnabled,
294 gl::IndexRange *outRange)
295 {
296 ContextGL *contextGL = GetImplAs<ContextGL>(context);
297 const FunctionsGL *functions = GetFunctionsGL(context);
298 StateManagerGL *stateManager = GetStateManagerGL(context);
299
300 ASSERT(!mIsMapped);
301
302 if (mShadowCopy.has_value())
303 {
304 *outRange = gl::ComputeIndexRange(type, mShadowCopy->data() + offset, count,
305 primitiveRestartEnabled);
306 }
307 else
308 {
309 stateManager->bindBuffer(DestBufferOperationTarget, mBufferID);
310
311 const GLuint typeBytes = gl::GetDrawElementsTypeSize(type);
312 const uint8_t *bufferData =
313 MapBufferRangeWithFallback(functions, gl::ToGLenum(DestBufferOperationTarget), offset,
314 count * typeBytes, GL_MAP_READ_BIT);
315 if (bufferData)
316 {
317 *outRange = gl::ComputeIndexRange(type, bufferData, count, primitiveRestartEnabled);
318 ANGLE_GL_TRY(context, functions->unmapBuffer(gl::ToGLenum(DestBufferOperationTarget)));
319 }
320 else
321 {
322 // Workaround the null driver not having map support.
323 *outRange = gl::IndexRange(0, 0, 1);
324 }
325 }
326
327 contextGL->markWorkSubmitted();
328
329 return angle::Result::Continue;
330 }
331
getBufferSize() const332 size_t BufferGL::getBufferSize() const
333 {
334 return mBufferSize;
335 }
336
getBufferID() const337 GLuint BufferGL::getBufferID() const
338 {
339 return mBufferID;
340 }
341 } // namespace rx
342