michael@0: #include "precompiled.h" michael@0: // michael@0: // Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: // michael@0: michael@0: // IndexDataManager.cpp: Defines the IndexDataManager, a class that michael@0: // runs the Buffer translation process for index buffers. michael@0: michael@0: #include "libGLESv2/renderer/IndexDataManager.h" michael@0: #include "libGLESv2/renderer/BufferStorage.h" michael@0: michael@0: #include "libGLESv2/Buffer.h" michael@0: #include "libGLESv2/main.h" michael@0: #include "libGLESv2/utilities.h" michael@0: #include "libGLESv2/renderer/IndexBuffer.h" michael@0: michael@0: namespace rx michael@0: { michael@0: michael@0: IndexDataManager::IndexDataManager(Renderer *renderer) : mRenderer(renderer) michael@0: { michael@0: mStreamingBufferShort = new StreamingIndexBufferInterface(mRenderer); michael@0: if (!mStreamingBufferShort->reserveBufferSpace(INITIAL_INDEX_BUFFER_SIZE, GL_UNSIGNED_SHORT)) michael@0: { michael@0: delete mStreamingBufferShort; michael@0: mStreamingBufferShort = NULL; michael@0: } michael@0: michael@0: mStreamingBufferInt = new StreamingIndexBufferInterface(mRenderer); michael@0: if (!mStreamingBufferInt->reserveBufferSpace(INITIAL_INDEX_BUFFER_SIZE, GL_UNSIGNED_INT)) michael@0: { michael@0: delete mStreamingBufferInt; michael@0: mStreamingBufferInt = NULL; michael@0: } michael@0: michael@0: if (!mStreamingBufferShort) michael@0: { michael@0: // Make sure both buffers are deleted. michael@0: delete mStreamingBufferInt; michael@0: mStreamingBufferInt = NULL; michael@0: michael@0: ERR("Failed to allocate the streaming index buffer(s)."); michael@0: } michael@0: michael@0: mCountingBuffer = NULL; michael@0: } michael@0: michael@0: IndexDataManager::~IndexDataManager() michael@0: { michael@0: delete mStreamingBufferShort; michael@0: delete mStreamingBufferInt; michael@0: delete mCountingBuffer; michael@0: } michael@0: michael@0: static void convertIndices(GLenum type, const void *input, GLsizei count, void *output) michael@0: { michael@0: if (type == GL_UNSIGNED_BYTE) michael@0: { michael@0: const GLubyte *in = static_cast(input); michael@0: GLushort *out = static_cast(output); michael@0: michael@0: for (GLsizei i = 0; i < count; i++) michael@0: { michael@0: out[i] = in[i]; michael@0: } michael@0: } michael@0: else if (type == GL_UNSIGNED_INT) michael@0: { michael@0: memcpy(output, input, count * sizeof(GLuint)); michael@0: } michael@0: else if (type == GL_UNSIGNED_SHORT) michael@0: { michael@0: memcpy(output, input, count * sizeof(GLushort)); michael@0: } michael@0: else UNREACHABLE(); michael@0: } michael@0: michael@0: template michael@0: static void computeRange(const IndexType *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex) michael@0: { michael@0: *minIndex = indices[0]; michael@0: *maxIndex = indices[0]; michael@0: michael@0: for (GLsizei i = 0; i < count; i++) michael@0: { michael@0: if (*minIndex > indices[i]) *minIndex = indices[i]; michael@0: if (*maxIndex < indices[i]) *maxIndex = indices[i]; michael@0: } michael@0: } michael@0: michael@0: static void computeRange(GLenum type, const GLvoid *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex) michael@0: { michael@0: if (type == GL_UNSIGNED_BYTE) michael@0: { michael@0: computeRange(static_cast(indices), count, minIndex, maxIndex); michael@0: } michael@0: else if (type == GL_UNSIGNED_INT) michael@0: { michael@0: computeRange(static_cast(indices), count, minIndex, maxIndex); michael@0: } michael@0: else if (type == GL_UNSIGNED_SHORT) michael@0: { michael@0: computeRange(static_cast(indices), count, minIndex, maxIndex); michael@0: } michael@0: else UNREACHABLE(); michael@0: } michael@0: michael@0: GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, gl::Buffer *buffer, const GLvoid *indices, TranslatedIndexData *translated) michael@0: { michael@0: if (!mStreamingBufferShort) michael@0: { michael@0: return GL_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: GLenum destinationIndexType = (type == GL_UNSIGNED_INT) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; michael@0: intptr_t offset = reinterpret_cast(indices); michael@0: bool alignedOffset = false; michael@0: michael@0: BufferStorage *storage = NULL; michael@0: michael@0: if (buffer != NULL) michael@0: { michael@0: storage = buffer->getStorage(); michael@0: michael@0: switch (type) michael@0: { michael@0: case GL_UNSIGNED_BYTE: alignedOffset = (offset % sizeof(GLubyte) == 0); break; michael@0: case GL_UNSIGNED_SHORT: alignedOffset = (offset % sizeof(GLushort) == 0); break; michael@0: case GL_UNSIGNED_INT: alignedOffset = (offset % sizeof(GLuint) == 0); break; michael@0: default: UNREACHABLE(); alignedOffset = false; michael@0: } michael@0: michael@0: unsigned int typeSize = gl::ComputeTypeSize(type); michael@0: michael@0: // check for integer overflows and underflows michael@0: if (static_cast(offset) > (std::numeric_limits::max() / typeSize) || michael@0: static_cast(count) > ((std::numeric_limits::max() / typeSize) - offset)) michael@0: { michael@0: return GL_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: if (typeSize * static_cast(count) + offset > storage->getSize()) michael@0: { michael@0: return GL_INVALID_OPERATION; michael@0: } michael@0: michael@0: indices = static_cast(storage->getData()) + offset; michael@0: } michael@0: michael@0: StreamingIndexBufferInterface *streamingBuffer = (type == GL_UNSIGNED_INT) ? mStreamingBufferInt : mStreamingBufferShort; michael@0: michael@0: StaticIndexBufferInterface *staticBuffer = buffer ? buffer->getStaticIndexBuffer() : NULL; michael@0: IndexBufferInterface *indexBuffer = streamingBuffer; michael@0: bool directStorage = alignedOffset && storage && storage->supportsDirectBinding() && michael@0: destinationIndexType == type; michael@0: unsigned int streamOffset = 0; michael@0: michael@0: if (directStorage) michael@0: { michael@0: indexBuffer = streamingBuffer; michael@0: streamOffset = offset; michael@0: storage->markBufferUsage(); michael@0: michael@0: if (!buffer->getIndexRangeCache()->findRange(type, offset, count, &translated->minIndex, michael@0: &translated->maxIndex, NULL)) michael@0: { michael@0: computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex); michael@0: buffer->getIndexRangeCache()->addRange(type, offset, count, translated->minIndex, michael@0: translated->maxIndex, offset); michael@0: } michael@0: } michael@0: else if (staticBuffer && staticBuffer->getBufferSize() != 0 && staticBuffer->getIndexType() == type && alignedOffset) michael@0: { michael@0: indexBuffer = staticBuffer; michael@0: if (!staticBuffer->getIndexRangeCache()->findRange(type, offset, count, &translated->minIndex, michael@0: &translated->maxIndex, &streamOffset)) michael@0: { michael@0: streamOffset = (offset / gl::ComputeTypeSize(type)) * gl::ComputeTypeSize(destinationIndexType); michael@0: computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex); michael@0: staticBuffer->getIndexRangeCache()->addRange(type, offset, count, translated->minIndex, michael@0: translated->maxIndex, streamOffset); michael@0: } michael@0: } michael@0: else michael@0: { michael@0: unsigned int convertCount = count; michael@0: michael@0: if (staticBuffer) michael@0: { michael@0: if (staticBuffer->getBufferSize() == 0 && alignedOffset) michael@0: { michael@0: indexBuffer = staticBuffer; michael@0: convertCount = storage->getSize() / gl::ComputeTypeSize(type); michael@0: } michael@0: else michael@0: { michael@0: buffer->invalidateStaticData(); michael@0: staticBuffer = NULL; michael@0: } michael@0: } michael@0: michael@0: if (!indexBuffer) michael@0: { michael@0: ERR("No valid index buffer."); michael@0: return GL_INVALID_OPERATION; michael@0: } michael@0: michael@0: unsigned int indexTypeSize = gl::ComputeTypeSize(destinationIndexType); michael@0: if (convertCount > std::numeric_limits::max() / indexTypeSize) michael@0: { michael@0: ERR("Reserving %u indicies of %u bytes each exceeds the maximum buffer size.", convertCount, indexTypeSize); michael@0: return GL_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: unsigned int bufferSizeRequired = convertCount * indexTypeSize; michael@0: if (!indexBuffer->reserveBufferSpace(bufferSizeRequired, type)) michael@0: { michael@0: ERR("Failed to reserve %u bytes in an index buffer.", bufferSizeRequired); michael@0: return GL_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: void* output = NULL; michael@0: if (!indexBuffer->mapBuffer(bufferSizeRequired, &output, &streamOffset)) michael@0: { michael@0: ERR("Failed to map index buffer."); michael@0: return GL_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: convertIndices(type, staticBuffer ? storage->getData() : indices, convertCount, output); michael@0: michael@0: if (!indexBuffer->unmapBuffer()) michael@0: { michael@0: ERR("Failed to unmap index buffer."); michael@0: return GL_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex); michael@0: michael@0: if (staticBuffer) michael@0: { michael@0: streamOffset = (offset / gl::ComputeTypeSize(type)) * gl::ComputeTypeSize(destinationIndexType); michael@0: staticBuffer->getIndexRangeCache()->addRange(type, offset, count, translated->minIndex, michael@0: translated->maxIndex, streamOffset); michael@0: } michael@0: } michael@0: michael@0: translated->storage = directStorage ? storage : NULL; michael@0: translated->indexBuffer = indexBuffer->getIndexBuffer(); michael@0: translated->serial = directStorage ? storage->getSerial() : indexBuffer->getSerial(); michael@0: translated->startIndex = streamOffset / gl::ComputeTypeSize(destinationIndexType); michael@0: translated->startOffset = streamOffset; michael@0: michael@0: if (buffer) michael@0: { michael@0: buffer->promoteStaticUsage(count * gl::ComputeTypeSize(type)); michael@0: } michael@0: michael@0: return GL_NO_ERROR; michael@0: } michael@0: michael@0: StaticIndexBufferInterface *IndexDataManager::getCountingIndices(GLsizei count) michael@0: { michael@0: if (count <= 65536) // 16-bit indices michael@0: { michael@0: const unsigned int spaceNeeded = count * sizeof(unsigned short); michael@0: michael@0: if (!mCountingBuffer || mCountingBuffer->getBufferSize() < spaceNeeded) michael@0: { michael@0: delete mCountingBuffer; michael@0: mCountingBuffer = new StaticIndexBufferInterface(mRenderer); michael@0: mCountingBuffer->reserveBufferSpace(spaceNeeded, GL_UNSIGNED_SHORT); michael@0: michael@0: void* mappedMemory = NULL; michael@0: if (!mCountingBuffer->mapBuffer(spaceNeeded, &mappedMemory, NULL)) michael@0: { michael@0: ERR("Failed to map counting buffer."); michael@0: return NULL; michael@0: } michael@0: michael@0: unsigned short *data = reinterpret_cast(mappedMemory); michael@0: for(int i = 0; i < count; i++) michael@0: { michael@0: data[i] = i; michael@0: } michael@0: michael@0: if (!mCountingBuffer->unmapBuffer()) michael@0: { michael@0: ERR("Failed to unmap counting buffer."); michael@0: return NULL; michael@0: } michael@0: } michael@0: } michael@0: else if (mStreamingBufferInt) // 32-bit indices supported michael@0: { michael@0: const unsigned int spaceNeeded = count * sizeof(unsigned int); michael@0: michael@0: if (!mCountingBuffer || mCountingBuffer->getBufferSize() < spaceNeeded) michael@0: { michael@0: delete mCountingBuffer; michael@0: mCountingBuffer = new StaticIndexBufferInterface(mRenderer); michael@0: mCountingBuffer->reserveBufferSpace(spaceNeeded, GL_UNSIGNED_INT); michael@0: michael@0: void* mappedMemory = NULL; michael@0: if (!mCountingBuffer->mapBuffer(spaceNeeded, &mappedMemory, NULL)) michael@0: { michael@0: ERR("Failed to map counting buffer."); michael@0: return NULL; michael@0: } michael@0: michael@0: unsigned int *data = reinterpret_cast(mappedMemory); michael@0: for(int i = 0; i < count; i++) michael@0: { michael@0: data[i] = i; michael@0: } michael@0: michael@0: if (!mCountingBuffer->unmapBuffer()) michael@0: { michael@0: ERR("Failed to unmap counting buffer."); michael@0: return NULL; michael@0: } michael@0: } michael@0: } michael@0: else michael@0: { michael@0: return NULL; michael@0: } michael@0: michael@0: return mCountingBuffer; michael@0: } michael@0: michael@0: }