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
26namespace
27{
28 enum { INITIAL_INDEX_BUFFER_SIZE = 4096 * sizeof(GLuint) };
29}
30
31namespace es2
32{
33
34IndexDataManager::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
44IndexDataManager::~IndexDataManager()
45{
46 delete mStreamingBuffer;
47}
48
49void 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
66inline 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
72void 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
195template<class IndexType>
196void 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
213void 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
230int 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
273GLenum 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
362std::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
373StreamingIndexBuffer::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
388StreamingIndexBuffer::~StreamingIndexBuffer()
389{
390 if(mIndexBuffer)
391 {
392 mIndexBuffer->destruct();
393 }
394}
395
396void *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
417void StreamingIndexBuffer::unmap()
418{
419 if(mIndexBuffer)
420 {
421 mIndexBuffer->unlock();
422 }
423}
424
425void 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
458sw::Resource *StreamingIndexBuffer::getResource() const
459{
460 return mIndexBuffer;
461}
462
463}
464