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 
IndexDataManager()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 
~IndexDataManager()44 IndexDataManager::~IndexDataManager()
45 {
46 	delete mStreamingBuffer;
47 }
48 
copyIndices(GLenum type,const void * input,GLsizei count,void * output)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 
getNumIndices(const std::vector<GLsizei> & restartIndices,size_t i,GLsizei count)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 
copyIndices(GLenum mode,GLenum type,const std::vector<GLsizei> & restartIndices,const void * input,GLsizei count,void * output)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>
computeRange(const IndexType * indices,GLsizei count,GLuint * minIndex,GLuint * maxIndex,std::vector<GLsizei> * restartIndices)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 
computeRange(GLenum type,const void * indices,GLsizei count,GLuint * minIndex,GLuint * maxIndex,std::vector<GLsizei> * restartIndices)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 
recomputePrimitiveCount(GLenum mode,GLsizei count,const std::vector<GLsizei> & restartIndices,unsigned int * primitiveCount)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 
prepareIndexData(GLenum mode,GLenum type,GLuint start,GLuint end,GLsizei count,Buffer * buffer,const void * indices,TranslatedIndexData * translated,bool primitiveRestart)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 
typeSize(GLenum type)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 
StreamingIndexBuffer(size_t initialSize)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 
~StreamingIndexBuffer()388 StreamingIndexBuffer::~StreamingIndexBuffer()
389 {
390 	if(mIndexBuffer)
391 	{
392 		mIndexBuffer->destruct();
393 	}
394 }
395 
map(size_t requiredSpace,size_t * offset)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 
unmap()417 void StreamingIndexBuffer::unmap()
418 {
419 	if(mIndexBuffer)
420 	{
421 		mIndexBuffer->unlock();
422 	}
423 }
424 
reserveSpace(size_t requiredSpace,GLenum type)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 
getResource() const458 sw::Resource *StreamingIndexBuffer::getResource() const
459 {
460 	return mIndexBuffer;
461 }
462 
463 }
464