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