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 18namespace 19{ 20 enum { INITIAL_INDEX_BUFFER_SIZE = 4096 * sizeof(GLuint) }; 21} 22 23namespace gl 24{ 25 26IndexDataManager::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 40IndexDataManager::~IndexDataManager() 41{ 42 delete mStreamingBufferShort; 43 delete mStreamingBufferInt; 44} 45 46void 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 69template <class IndexType> 70void 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 82void 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 99GLenum 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 191std::size_t IndexDataManager::indexSize(D3DFORMAT format) const 192{ 193 return (format == D3DFMT_INDEX32) ? sizeof(unsigned int) : sizeof(unsigned short); 194} 195 196std::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 207IndexBuffer::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 221IndexBuffer::~IndexBuffer() 222{ 223 if (mIndexBuffer) 224 { 225 mIndexBuffer->Release(); 226 } 227} 228 229IDirect3DIndexBuffer9 *IndexBuffer::getBuffer() const 230{ 231 return mIndexBuffer; 232} 233 234void IndexBuffer::unmap() 235{ 236 if (mIndexBuffer) 237 { 238 mIndexBuffer->Unlock(); 239 } 240} 241 242StreamingIndexBuffer::StreamingIndexBuffer(IDirect3DDevice9 *device, UINT initialSize, D3DFORMAT format) : IndexBuffer(device, initialSize, format) 243{ 244 mWritePosition = 0; 245} 246 247StreamingIndexBuffer::~StreamingIndexBuffer() 248{ 249} 250 251void *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 272void 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 304StaticIndexBuffer::StaticIndexBuffer(IDirect3DDevice9 *device) : IndexBuffer(device, 0, D3DFMT_UNKNOWN) 305{ 306 mCacheType = GL_NONE; 307} 308 309StaticIndexBuffer::~StaticIndexBuffer() 310{ 311} 312 313void *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 333void 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 355bool StaticIndexBuffer::lookupType(GLenum type) 356{ 357 return mCacheType == type; 358} 359 360UINT 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 376void 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