1 | // Copyright 2016 The SwiftShader Authors. All Rights Reserved. |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | // you may not use this file except in compliance with the License. |
5 | // You may obtain a copy of the License at |
6 | // |
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software |
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | // See the License for the specific language governing permissions and |
13 | // limitations under the License. |
14 | |
15 | // IndexDataManager.cpp: Defines the IndexDataManager, a class that |
16 | // runs the Buffer translation process for index buffers. |
17 | |
18 | #include "IndexDataManager.h" |
19 | |
20 | #include "Buffer.h" |
21 | #include "common/debug.h" |
22 | |
23 | #include <string.h> |
24 | #include <algorithm> |
25 | |
26 | namespace |
27 | { |
28 | enum { INITIAL_INDEX_BUFFER_SIZE = 4096 * sizeof(GLuint) }; |
29 | } |
30 | |
31 | namespace es2 |
32 | { |
33 | |
34 | IndexDataManager::IndexDataManager() |
35 | { |
36 | mStreamingBuffer = new StreamingIndexBuffer(INITIAL_INDEX_BUFFER_SIZE); |
37 | |
38 | if(!mStreamingBuffer) |
39 | { |
40 | ERR("Failed to allocate the streaming index buffer." ); |
41 | } |
42 | } |
43 | |
44 | IndexDataManager::~IndexDataManager() |
45 | { |
46 | delete mStreamingBuffer; |
47 | } |
48 | |
49 | void copyIndices(GLenum type, const void *input, GLsizei count, void *output) |
50 | { |
51 | if(type == GL_UNSIGNED_BYTE) |
52 | { |
53 | memcpy(output, input, count * sizeof(GLubyte)); |
54 | } |
55 | else if(type == GL_UNSIGNED_INT) |
56 | { |
57 | memcpy(output, input, count * sizeof(GLuint)); |
58 | } |
59 | else if(type == GL_UNSIGNED_SHORT) |
60 | { |
61 | memcpy(output, input, count * sizeof(GLushort)); |
62 | } |
63 | else UNREACHABLE(type); |
64 | } |
65 | |
66 | inline GLsizei getNumIndices(const std::vector<GLsizei>& restartIndices, size_t i, GLsizei count) |
67 | { |
68 | return restartIndices.empty() ? count : |
69 | ((i == 0) ? restartIndices[0] : ((i == restartIndices.size()) ? (count - restartIndices[i - 1] - 1) : (restartIndices[i] - restartIndices[i - 1] - 1))); |
70 | } |
71 | |
72 | void copyIndices(GLenum mode, GLenum type, const std::vector<GLsizei>& restartIndices, const void *input, GLsizei count, void* output) |
73 | { |
74 | size_t bytesPerIndex = 0; |
75 | const unsigned char* inPtr = static_cast<const unsigned char*>(input); |
76 | unsigned char* outPtr = static_cast<unsigned char*>(output); |
77 | switch(type) |
78 | { |
79 | case GL_UNSIGNED_BYTE: |
80 | bytesPerIndex = sizeof(GLubyte); |
81 | break; |
82 | case GL_UNSIGNED_INT: |
83 | bytesPerIndex = sizeof(GLuint); |
84 | break; |
85 | case GL_UNSIGNED_SHORT: |
86 | bytesPerIndex = sizeof(GLushort); |
87 | break; |
88 | default: |
89 | UNREACHABLE(type); |
90 | } |
91 | |
92 | size_t numRestarts = restartIndices.size(); |
93 | switch(mode) |
94 | { |
95 | case GL_TRIANGLES: |
96 | case GL_LINES: |
97 | case GL_POINTS: |
98 | { |
99 | GLsizei verticesPerPrimitive = (mode == GL_TRIANGLES) ? 3 : ((mode == GL_LINES) ? 2 : 1); |
100 | for(size_t i = 0; i <= numRestarts; ++i) |
101 | { |
102 | GLsizei numIndices = getNumIndices(restartIndices, i, count); |
103 | size_t numBytes = (numIndices / verticesPerPrimitive) * verticesPerPrimitive * bytesPerIndex; |
104 | if(numBytes > 0) |
105 | { |
106 | memcpy(outPtr, inPtr, numBytes); |
107 | outPtr += numBytes; |
108 | } |
109 | inPtr += (numIndices + 1) * bytesPerIndex; |
110 | } |
111 | } |
112 | break; |
113 | case GL_TRIANGLE_FAN: |
114 | for(size_t i = 0; i <= numRestarts; ++i) |
115 | { |
116 | GLsizei numIndices = getNumIndices(restartIndices, i, count); |
117 | GLsizei numTriangles = (numIndices - 2); |
118 | for(GLsizei tri = 0; tri < numTriangles; ++tri) |
119 | { |
120 | memcpy(outPtr, inPtr, bytesPerIndex); |
121 | outPtr += bytesPerIndex; |
122 | memcpy(outPtr, inPtr + ((tri + 1) * bytesPerIndex), bytesPerIndex + bytesPerIndex); |
123 | outPtr += bytesPerIndex + bytesPerIndex; |
124 | } |
125 | inPtr += (numIndices + 1) * bytesPerIndex; |
126 | } |
127 | break; |
128 | case GL_TRIANGLE_STRIP: |
129 | for(size_t i = 0; i <= numRestarts; ++i) |
130 | { |
131 | GLsizei numIndices = getNumIndices(restartIndices, i, count); |
132 | GLsizei numTriangles = (numIndices - 2); |
133 | for(GLsizei tri = 0; tri < numTriangles; ++tri) |
134 | { |
135 | if(tri & 1) // Reverse odd triangles |
136 | { |
137 | memcpy(outPtr, inPtr + ((tri + 1) * bytesPerIndex), bytesPerIndex); |
138 | outPtr += bytesPerIndex; |
139 | memcpy(outPtr, inPtr + ((tri + 0) * bytesPerIndex), bytesPerIndex); |
140 | outPtr += bytesPerIndex; |
141 | memcpy(outPtr, inPtr + ((tri + 2) * bytesPerIndex), bytesPerIndex); |
142 | outPtr += bytesPerIndex; |
143 | } |
144 | else |
145 | { |
146 | size_t numBytes = 3 * bytesPerIndex; |
147 | memcpy(outPtr, inPtr + (tri * bytesPerIndex), numBytes); |
148 | outPtr += numBytes; |
149 | } |
150 | } |
151 | inPtr += (numIndices + 1) * bytesPerIndex; |
152 | } |
153 | break; |
154 | case GL_LINE_LOOP: |
155 | for(size_t i = 0; i <= numRestarts; ++i) |
156 | { |
157 | GLsizei numIndices = getNumIndices(restartIndices, i, count); |
158 | if(numIndices >= 2) |
159 | { |
160 | GLsizei numLines = numIndices; |
161 | memcpy(outPtr, inPtr + (numIndices - 1) * bytesPerIndex, bytesPerIndex); // Last vertex |
162 | outPtr += bytesPerIndex; |
163 | memcpy(outPtr, inPtr, bytesPerIndex); // First vertex |
164 | outPtr += bytesPerIndex; |
165 | size_t bytesPerLine = 2 * bytesPerIndex; |
166 | for(GLsizei tri = 0; tri < (numLines - 1); ++tri) |
167 | { |
168 | memcpy(outPtr, inPtr + tri * bytesPerIndex, bytesPerLine); |
169 | outPtr += bytesPerLine; |
170 | } |
171 | } |
172 | inPtr += (numIndices + 1) * bytesPerIndex; |
173 | } |
174 | break; |
175 | case GL_LINE_STRIP: |
176 | for(size_t i = 0; i <= numRestarts; ++i) |
177 | { |
178 | GLsizei numIndices = getNumIndices(restartIndices, i, count); |
179 | GLsizei numLines = numIndices - 1; |
180 | size_t bytesPerLine = 2 * bytesPerIndex; |
181 | for(GLsizei tri = 0; tri < numLines; ++tri) |
182 | { |
183 | memcpy(outPtr, inPtr + tri * bytesPerIndex, bytesPerLine); |
184 | outPtr += bytesPerLine; |
185 | } |
186 | inPtr += (numIndices + 1) * bytesPerIndex; |
187 | } |
188 | break; |
189 | default: |
190 | UNREACHABLE(mode); |
191 | break; |
192 | } |
193 | } |
194 | |
195 | template<class IndexType> |
196 | void computeRange(const IndexType *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex, std::vector<GLsizei>* restartIndices) |
197 | { |
198 | *maxIndex = 0; |
199 | *minIndex = MAX_ELEMENTS_INDICES; |
200 | |
201 | for(GLsizei i = 0; i < count; i++) |
202 | { |
203 | if(restartIndices && indices[i] == IndexType(-1)) |
204 | { |
205 | restartIndices->push_back(i); |
206 | continue; |
207 | } |
208 | if(*minIndex > indices[i]) *minIndex = indices[i]; |
209 | if(*maxIndex < indices[i]) *maxIndex = indices[i]; |
210 | } |
211 | } |
212 | |
213 | void computeRange(GLenum type, const void *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex, std::vector<GLsizei>* restartIndices) |
214 | { |
215 | if(type == GL_UNSIGNED_BYTE) |
216 | { |
217 | computeRange(static_cast<const GLubyte*>(indices), count, minIndex, maxIndex, restartIndices); |
218 | } |
219 | else if(type == GL_UNSIGNED_INT) |
220 | { |
221 | computeRange(static_cast<const GLuint*>(indices), count, minIndex, maxIndex, restartIndices); |
222 | } |
223 | else if(type == GL_UNSIGNED_SHORT) |
224 | { |
225 | computeRange(static_cast<const GLushort*>(indices), count, minIndex, maxIndex, restartIndices); |
226 | } |
227 | else UNREACHABLE(type); |
228 | } |
229 | |
230 | int recomputePrimitiveCount(GLenum mode, GLsizei count, const std::vector<GLsizei>& restartIndices, unsigned int* primitiveCount) |
231 | { |
232 | size_t numRestarts = restartIndices.size(); |
233 | *primitiveCount = 0; |
234 | |
235 | unsigned int countOffset = 0; |
236 | unsigned int vertexPerPrimitive = 0; |
237 | |
238 | switch(mode) |
239 | { |
240 | case GL_TRIANGLES: // 3 vertex per primitive |
241 | ++vertexPerPrimitive; |
242 | case GL_LINES: // 2 vertex per primitive |
243 | vertexPerPrimitive += 2; |
244 | for(size_t i = 0; i <= numRestarts; ++i) |
245 | { |
246 | unsigned int nbIndices = getNumIndices(restartIndices, i, count); |
247 | *primitiveCount += nbIndices / vertexPerPrimitive; |
248 | } |
249 | return vertexPerPrimitive; |
250 | case GL_TRIANGLE_FAN: |
251 | case GL_TRIANGLE_STRIP: // (N - 2) polygons, 3 vertex per primitive |
252 | ++vertexPerPrimitive; |
253 | --countOffset; |
254 | case GL_LINE_STRIP: // (N - 1) polygons, 2 vertex per primitive |
255 | --countOffset; |
256 | case GL_LINE_LOOP: // N polygons, 2 vertex per primitive |
257 | vertexPerPrimitive += 2; |
258 | for(size_t i = 0; i <= numRestarts; ++i) |
259 | { |
260 | unsigned int nbIndices = getNumIndices(restartIndices, i, count); |
261 | *primitiveCount += (nbIndices >= vertexPerPrimitive) ? (nbIndices + countOffset) : 0; |
262 | } |
263 | return vertexPerPrimitive; |
264 | case GL_POINTS: |
265 | *primitiveCount = static_cast<unsigned int>(count - restartIndices.size()); |
266 | return 1; |
267 | default: |
268 | UNREACHABLE(mode); |
269 | return -1; |
270 | } |
271 | } |
272 | |
273 | GLenum IndexDataManager::prepareIndexData(GLenum mode, GLenum type, GLuint start, GLuint end, GLsizei count, Buffer *buffer, const void *indices, TranslatedIndexData *translated, bool primitiveRestart) |
274 | { |
275 | if(!mStreamingBuffer) |
276 | { |
277 | return GL_OUT_OF_MEMORY; |
278 | } |
279 | |
280 | intptr_t offset = reinterpret_cast<intptr_t>(indices); |
281 | |
282 | if(buffer != NULL) |
283 | { |
284 | if(typeSize(type) * count + offset > static_cast<std::size_t>(buffer->size())) |
285 | { |
286 | return GL_INVALID_OPERATION; |
287 | } |
288 | |
289 | indices = static_cast<const GLubyte*>(buffer->data()) + offset; |
290 | } |
291 | |
292 | std::vector<GLsizei>* restartIndices = primitiveRestart ? new std::vector<GLsizei>() : nullptr; |
293 | computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex, restartIndices); |
294 | |
295 | StreamingIndexBuffer *streamingBuffer = mStreamingBuffer; |
296 | |
297 | sw::Resource *staticBuffer = buffer ? buffer->getResource() : NULL; |
298 | |
299 | if(restartIndices) |
300 | { |
301 | int vertexPerPrimitive = recomputePrimitiveCount(mode, count, *restartIndices, &translated->primitiveCount); |
302 | if(vertexPerPrimitive == -1) |
303 | { |
304 | delete restartIndices; |
305 | return GL_INVALID_ENUM; |
306 | } |
307 | |
308 | size_t streamOffset = 0; |
309 | int convertCount = translated->primitiveCount * vertexPerPrimitive; |
310 | |
311 | streamingBuffer->reserveSpace(convertCount * typeSize(type), type); |
312 | void *output = streamingBuffer->map(typeSize(type) * convertCount, &streamOffset); |
313 | |
314 | if(output == NULL) |
315 | { |
316 | delete restartIndices; |
317 | ERR("Failed to map index buffer." ); |
318 | return GL_OUT_OF_MEMORY; |
319 | } |
320 | |
321 | copyIndices(mode, type, *restartIndices, indices, count, output); |
322 | streamingBuffer->unmap(); |
323 | |
324 | translated->indexBuffer = streamingBuffer->getResource(); |
325 | translated->indexOffset = static_cast<unsigned int>(streamOffset); |
326 | delete restartIndices; |
327 | } |
328 | else if(staticBuffer) |
329 | { |
330 | translated->indexBuffer = staticBuffer; |
331 | translated->indexOffset = static_cast<unsigned int>(offset); |
332 | } |
333 | else |
334 | { |
335 | size_t streamOffset = 0; |
336 | int convertCount = count; |
337 | |
338 | streamingBuffer->reserveSpace(convertCount * typeSize(type), type); |
339 | void *output = streamingBuffer->map(typeSize(type) * convertCount, &streamOffset); |
340 | |
341 | if(output == NULL) |
342 | { |
343 | ERR("Failed to map index buffer." ); |
344 | return GL_OUT_OF_MEMORY; |
345 | } |
346 | |
347 | copyIndices(type, indices, convertCount, output); |
348 | streamingBuffer->unmap(); |
349 | |
350 | translated->indexBuffer = streamingBuffer->getResource(); |
351 | translated->indexOffset = static_cast<unsigned int>(streamOffset); |
352 | } |
353 | |
354 | if(translated->minIndex < start || translated->maxIndex > end) |
355 | { |
356 | ERR("glDrawRangeElements: out of range access. Range provided: [%d -> %d]. Range used: [%d -> %d]." , start, end, translated->minIndex, translated->maxIndex); |
357 | } |
358 | |
359 | return GL_NO_ERROR; |
360 | } |
361 | |
362 | std::size_t IndexDataManager::typeSize(GLenum type) |
363 | { |
364 | switch(type) |
365 | { |
366 | case GL_UNSIGNED_INT: return sizeof(GLuint); |
367 | case GL_UNSIGNED_SHORT: return sizeof(GLushort); |
368 | case GL_UNSIGNED_BYTE: return sizeof(GLubyte); |
369 | default: UNREACHABLE(type); return sizeof(GLushort); |
370 | } |
371 | } |
372 | |
373 | StreamingIndexBuffer::StreamingIndexBuffer(size_t initialSize) : mIndexBuffer(NULL), mBufferSize(initialSize) |
374 | { |
375 | if(initialSize > 0) |
376 | { |
377 | mIndexBuffer = new sw::Resource(initialSize + 16); |
378 | |
379 | if(!mIndexBuffer) |
380 | { |
381 | ERR("Out of memory allocating an index buffer of size %u." , initialSize); |
382 | } |
383 | } |
384 | |
385 | mWritePosition = 0; |
386 | } |
387 | |
388 | StreamingIndexBuffer::~StreamingIndexBuffer() |
389 | { |
390 | if(mIndexBuffer) |
391 | { |
392 | mIndexBuffer->destruct(); |
393 | } |
394 | } |
395 | |
396 | void *StreamingIndexBuffer::map(size_t requiredSpace, size_t *offset) |
397 | { |
398 | void *mapPtr = NULL; |
399 | |
400 | if(mIndexBuffer) |
401 | { |
402 | mapPtr = (char*)mIndexBuffer->lock(sw::PUBLIC) + mWritePosition; |
403 | |
404 | if(!mapPtr) |
405 | { |
406 | ERR(" Lock failed" ); |
407 | return NULL; |
408 | } |
409 | |
410 | *offset = mWritePosition; |
411 | mWritePosition += requiredSpace; |
412 | } |
413 | |
414 | return mapPtr; |
415 | } |
416 | |
417 | void StreamingIndexBuffer::unmap() |
418 | { |
419 | if(mIndexBuffer) |
420 | { |
421 | mIndexBuffer->unlock(); |
422 | } |
423 | } |
424 | |
425 | void StreamingIndexBuffer::reserveSpace(size_t requiredSpace, GLenum type) |
426 | { |
427 | if(requiredSpace > mBufferSize) |
428 | { |
429 | if(mIndexBuffer) |
430 | { |
431 | mIndexBuffer->destruct(); |
432 | mIndexBuffer = 0; |
433 | } |
434 | |
435 | mBufferSize = std::max(requiredSpace, 2 * mBufferSize); |
436 | |
437 | mIndexBuffer = new sw::Resource(mBufferSize + 16); |
438 | |
439 | if(!mIndexBuffer) |
440 | { |
441 | ERR("Out of memory allocating an index buffer of size %u." , mBufferSize); |
442 | } |
443 | |
444 | mWritePosition = 0; |
445 | } |
446 | else if(mWritePosition + requiredSpace > mBufferSize) // Recycle |
447 | { |
448 | if(mIndexBuffer) |
449 | { |
450 | mIndexBuffer->destruct(); |
451 | mIndexBuffer = new sw::Resource(mBufferSize + 16); |
452 | } |
453 | |
454 | mWritePosition = 0; |
455 | } |
456 | } |
457 | |
458 | sw::Resource *StreamingIndexBuffer::getResource() const |
459 | { |
460 | return mIndexBuffer; |
461 | } |
462 | |
463 | } |
464 | |