1 // 2 // Copyright (c) 2002-2010 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 // geometry/IndexDataManager.cpp: Defines the IndexDataManager, a class that 8 // runs the Buffer translation process for index buffers. 9 10 #include "libGLESv2/geometry/IndexDataManager.h" 11 12 #include "common/debug.h" 13 14 #include "libGLESv2/Buffer.h" 15 #include "libGLESv2/mathutil.h" 16 #include "libGLESv2/main.h" 17 18 namespace 19 { 20 enum { INITIAL_INDEX_BUFFER_SIZE = 4096 * sizeof(GLuint) }; 21 } 22 23 namespace gl 24 { 25 26 IndexDataManager::IndexDataManager(Context *context, IDirect3DDevice9 *device) : mDevice(device) 27 { 28 mStreamingBufferShort = new StreamingIndexBuffer(mDevice, INITIAL_INDEX_BUFFER_SIZE, D3DFMT_INDEX16); 29 30 if (context->supports32bitIndices()) 31 { 32 mStreamingBufferInt = new StreamingIndexBuffer(mDevice, INITIAL_INDEX_BUFFER_SIZE, D3DFMT_INDEX32); 33 } 34 else 35 { 36 mStreamingBufferInt = NULL; 37 } 38 } 39 40 IndexDataManager::~IndexDataManager() 41 { 42 delete mStreamingBufferShort; 43 delete mStreamingBufferInt; 44 } 45 46 void convertIndices(GLenum type, const void *input, GLsizei count, void *output) 47 { 48 if (type == GL_UNSIGNED_BYTE) 49 { 50 const GLubyte *in = static_cast<const GLubyte*>(input); 51 GLushort *out = static_cast<GLushort*>(output); 52 53 for (GLsizei i = 0; i < count; i++) 54 { 55 out[i] = in[i]; 56 } 57 } 58 else if (type == GL_UNSIGNED_INT) 59 { 60 memcpy(output, input, count * sizeof(GLuint)); 61 } 62 else if (type == GL_UNSIGNED_SHORT) 63 { 64 memcpy(output, input, count * sizeof(GLushort)); 65 } 66 else UNREACHABLE(); 67 } 68 69 template <class IndexType> 70 void computeRange(const IndexType *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex) 71 { 72 *minIndex = indices[0]; 73 *maxIndex = indices[0]; 74 75 for (GLsizei i = 0; i < count; i++) 76 { 77 if (*minIndex > indices[i]) *minIndex = indices[i]; 78 if (*maxIndex < indices[i]) *maxIndex = indices[i]; 79 } 80 } 81 82 void computeRange(GLenum type, const void *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex) 83 { 84 if (type == GL_UNSIGNED_BYTE) 85 { 86 computeRange(static_cast<const GLubyte*>(indices), count, minIndex, maxIndex); 87 } 88 else if (type == GL_UNSIGNED_INT) 89 { 90 computeRange(static_cast<const GLuint*>(indices), count, minIndex, maxIndex); 91 } 92 else if (type == GL_UNSIGNED_SHORT) 93 { 94 computeRange(static_cast<const GLushort*>(indices), count, minIndex, maxIndex); 95 } 96 else UNREACHABLE(); 97 } 98 99 GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, Buffer *buffer, const void *indices, TranslatedIndexData *translated) 100 { 101 D3DFORMAT format = (type == GL_UNSIGNED_INT) ? D3DFMT_INDEX32 : D3DFMT_INDEX16; 102 intptr_t offset = reinterpret_cast<intptr_t>(indices); 103 bool alignedOffset = false; 104 105 if (buffer != NULL) 106 { 107 switch (type) 108 { 109 case GL_UNSIGNED_BYTE: alignedOffset = (offset % sizeof(GLubyte) == 0); break; 110 case GL_UNSIGNED_SHORT: alignedOffset = (offset % sizeof(GLushort) == 0); break; 111 case GL_UNSIGNED_INT: alignedOffset = (offset % sizeof(GLuint) == 0); break; 112 default: UNREACHABLE(); alignedOffset = false; 113 } 114 115 if (typeSize(type) * count + offset > static_cast<std::size_t>(buffer->size())) 116 { 117 return GL_INVALID_OPERATION; 118 } 119 120 indices = static_cast<const GLubyte*>(buffer->data()) + offset; 121 } 122 123 StreamingIndexBuffer *streamingBuffer = (type == GL_UNSIGNED_INT) ? mStreamingBufferInt : mStreamingBufferShort; 124 125 StaticIndexBuffer *staticBuffer = buffer ? buffer->getIndexBuffer() : NULL; 126 IndexBuffer *indexBuffer = streamingBuffer; 127 UINT streamOffset = 0; 128 129 if (staticBuffer && staticBuffer->lookupType(type) && alignedOffset) 130 { 131 indexBuffer = staticBuffer; 132 streamOffset = staticBuffer->lookupRange(offset, count, &translated->minIndex, &translated->maxIndex); 133 134 if (streamOffset == -1) 135 { 136 streamOffset = (offset / typeSize(type)) * indexSize(format); 137 computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex); 138 staticBuffer->addRange(offset, count, translated->minIndex, translated->maxIndex, streamOffset); 139 } 140 } 141 else 142 { 143 int convertCount = count; 144 145 if (staticBuffer) 146 { 147 if (staticBuffer->size() == 0 && alignedOffset) 148 { 149 indexBuffer = staticBuffer; 150 convertCount = buffer->size() / typeSize(type); 151 } 152 else 153 { 154 buffer->invalidateStaticData(); 155 staticBuffer = NULL; 156 } 157 } 158 159 void *output = NULL; 160 161 if (indexBuffer) 162 { 163 indexBuffer->reserveSpace(convertCount * indexSize(format), type); 164 output = indexBuffer->map(indexSize(format) * convertCount, &streamOffset); 165 } 166 167 if (output == NULL) 168 { 169 ERR("Failed to map index buffer."); 170 return GL_OUT_OF_MEMORY; 171 } 172 173 convertIndices(type, staticBuffer ? buffer->data() : indices, convertCount, output); 174 indexBuffer->unmap(); 175 176 computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex); 177 178 if (staticBuffer) 179 { 180 streamOffset = (offset / typeSize(type)) * indexSize(format); 181 staticBuffer->addRange(offset, count, translated->minIndex, translated->maxIndex, streamOffset); 182 } 183 } 184 185 translated->indexBuffer = indexBuffer->getBuffer(); 186 translated->startIndex = streamOffset / indexSize(format); 187 188 return GL_NO_ERROR; 189 } 190 191 std::size_t IndexDataManager::indexSize(D3DFORMAT format) const 192 { 193 return (format == D3DFMT_INDEX32) ? sizeof(unsigned int) : sizeof(unsigned short); 194 } 195 196 std::size_t IndexDataManager::typeSize(GLenum type) const 197 { 198 switch (type) 199 { 200 case GL_UNSIGNED_INT: return sizeof(GLuint); 201 case GL_UNSIGNED_SHORT: return sizeof(GLushort); 202 case GL_UNSIGNED_BYTE: return sizeof(GLubyte); 203 default: UNREACHABLE(); return sizeof(GLushort); 204 } 205 } 206 207 IndexBuffer::IndexBuffer(IDirect3DDevice9 *device, UINT size, D3DFORMAT format) : mDevice(device), mBufferSize(size), mIndexBuffer(NULL) 208 { 209 if (size > 0) 210 { 211 D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY); 212 HRESULT result = device->CreateIndexBuffer(size, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, format, pool, &mIndexBuffer, NULL); 213 214 if (FAILED(result)) 215 { 216 ERR("Out of memory allocating an index buffer of size %lu.", size); 217 } 218 } 219 } 220 221 IndexBuffer::~IndexBuffer() 222 { 223 if (mIndexBuffer) 224 { 225 mIndexBuffer->Release(); 226 } 227 } 228 229 IDirect3DIndexBuffer9 *IndexBuffer::getBuffer() const 230 { 231 return mIndexBuffer; 232 } 233 234 void IndexBuffer::unmap() 235 { 236 if (mIndexBuffer) 237 { 238 mIndexBuffer->Unlock(); 239 } 240 } 241 242 StreamingIndexBuffer::StreamingIndexBuffer(IDirect3DDevice9 *device, UINT initialSize, D3DFORMAT format) : IndexBuffer(device, initialSize, format) 243 { 244 mWritePosition = 0; 245 } 246 247 StreamingIndexBuffer::~StreamingIndexBuffer() 248 { 249 } 250 251 void *StreamingIndexBuffer::map(UINT requiredSpace, UINT *offset) 252 { 253 void *mapPtr = NULL; 254 255 if (mIndexBuffer) 256 { 257 HRESULT result = mIndexBuffer->Lock(mWritePosition, requiredSpace, &mapPtr, D3DLOCK_NOOVERWRITE); 258 259 if (FAILED(result)) 260 { 261 ERR(" Lock failed with error 0x%08x", result); 262 return NULL; 263 } 264 265 *offset = mWritePosition; 266 mWritePosition += requiredSpace; 267 } 268 269 return mapPtr; 270 } 271 272 void StreamingIndexBuffer::reserveSpace(UINT requiredSpace, GLenum type) 273 { 274 if (requiredSpace > mBufferSize) 275 { 276 if (mIndexBuffer) 277 { 278 mIndexBuffer->Release(); 279 mIndexBuffer = NULL; 280 } 281 282 mBufferSize = std::max(requiredSpace, 2 * mBufferSize); 283 284 D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY); 285 HRESULT result = mDevice->CreateIndexBuffer(mBufferSize, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, type == GL_UNSIGNED_INT ? D3DFMT_INDEX32 : D3DFMT_INDEX16, pool, &mIndexBuffer, NULL); 286 287 if (FAILED(result)) 288 { 289 ERR("Out of memory allocating a vertex buffer of size %lu.", mBufferSize); 290 } 291 292 mWritePosition = 0; 293 } 294 else if (mWritePosition + requiredSpace > mBufferSize) // Recycle 295 { 296 void *dummy; 297 mIndexBuffer->Lock(0, 1, &dummy, D3DLOCK_DISCARD); 298 mIndexBuffer->Unlock(); 299 300 mWritePosition = 0; 301 } 302 } 303 304 StaticIndexBuffer::StaticIndexBuffer(IDirect3DDevice9 *device) : IndexBuffer(device, 0, D3DFMT_UNKNOWN) 305 { 306 mCacheType = GL_NONE; 307 } 308 309 StaticIndexBuffer::~StaticIndexBuffer() 310 { 311 } 312 313 void *StaticIndexBuffer::map(UINT requiredSpace, UINT *offset) 314 { 315 void *mapPtr = NULL; 316 317 if (mIndexBuffer) 318 { 319 HRESULT result = mIndexBuffer->Lock(0, requiredSpace, &mapPtr, 0); 320 321 if (FAILED(result)) 322 { 323 ERR(" Lock failed with error 0x%08x", result); 324 return NULL; 325 } 326 327 *offset = 0; 328 } 329 330 return mapPtr; 331 } 332 333 void StaticIndexBuffer::reserveSpace(UINT requiredSpace, GLenum type) 334 { 335 if (!mIndexBuffer && mBufferSize == 0) 336 { 337 D3DPOOL pool = getDisplay()->getBufferPool(D3DUSAGE_WRITEONLY); 338 HRESULT result = mDevice->CreateIndexBuffer(requiredSpace, D3DUSAGE_WRITEONLY, type == GL_UNSIGNED_INT ? D3DFMT_INDEX32 : D3DFMT_INDEX16, pool, &mIndexBuffer, NULL); 339 340 if (FAILED(result)) 341 { 342 ERR("Out of memory allocating a vertex buffer of size %lu.", mBufferSize); 343 } 344 345 mBufferSize = requiredSpace; 346 mCacheType = type; 347 } 348 else if (mIndexBuffer && mBufferSize >= requiredSpace && mCacheType == type) 349 { 350 // Already allocated 351 } 352 else UNREACHABLE(); // Static index buffers can't be resized 353 } 354 355 bool StaticIndexBuffer::lookupType(GLenum type) 356 { 357 return mCacheType == type; 358 } 359 360 UINT StaticIndexBuffer::lookupRange(intptr_t offset, GLsizei count, UINT *minIndex, UINT *maxIndex) 361 { 362 for (unsigned int range = 0; range < mCache.size(); range++) 363 { 364 if (mCache[range].offset == offset && mCache[range].count == count) 365 { 366 *minIndex = mCache[range].minIndex; 367 *maxIndex = mCache[range].maxIndex; 368 369 return mCache[range].streamOffset; 370 } 371 } 372 373 return -1; 374 } 375 376 void StaticIndexBuffer::addRange(intptr_t offset, GLsizei count, UINT minIndex, UINT maxIndex, UINT streamOffset) 377 { 378 IndexRange indexRange = {offset, count, minIndex, maxIndex, streamOffset}; 379 mCache.push_back(indexRange); 380 } 381 382 } 383