gl::Error IndexDataManager::prepareIndexData(GLenum type, GLsizei count, gl::Buffer *buffer, const GLvoid *indices, TranslatedIndexData *translated) { const gl::Type &typeInfo = gl::GetTypeInfo(type); GLenum destinationIndexType = (type == GL_UNSIGNED_INT) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; unsigned int offset = 0; bool alignedOffset = false; BufferD3D *storage = NULL; if (buffer != NULL) { offset = static_cast<unsigned int>(reinterpret_cast<uintptr_t>(indices)); storage = GetImplAs<BufferD3D>(buffer); switch (type) { case GL_UNSIGNED_BYTE: alignedOffset = (offset % sizeof(GLubyte) == 0); break; case GL_UNSIGNED_SHORT: alignedOffset = (offset % sizeof(GLushort) == 0); break; case GL_UNSIGNED_INT: alignedOffset = (offset % sizeof(GLuint) == 0); break; default: UNREACHABLE(); alignedOffset = false; } ASSERT(typeInfo.bytes * static_cast<unsigned int>(count) + offset <= storage->getSize()); const uint8_t *bufferData = NULL; gl::Error error = storage->getData(&bufferData); if (error.isError()) { return error; } indices = bufferData + offset; } StaticIndexBufferInterface *staticBuffer = storage ? storage->getStaticIndexBuffer() : NULL; IndexBufferInterface *indexBuffer = NULL; bool directStorage = alignedOffset && storage && storage->supportsDirectBinding() && destinationIndexType == type; unsigned int streamOffset = 0; if (directStorage) { streamOffset = offset; if (!buffer->getIndexRangeCache()->findRange(type, offset, count, NULL, NULL)) { buffer->getIndexRangeCache()->addRange(type, offset, count, translated->indexRange, offset); } } else if (staticBuffer && staticBuffer->getBufferSize() != 0 && staticBuffer->getIndexType() == type && alignedOffset) { indexBuffer = staticBuffer; if (!staticBuffer->getIndexRangeCache()->findRange(type, offset, count, NULL, &streamOffset)) { streamOffset = (offset / typeInfo.bytes) * gl::GetTypeInfo(destinationIndexType).bytes; staticBuffer->getIndexRangeCache()->addRange(type, offset, count, translated->indexRange, streamOffset); } } // Avoid D3D11's primitive restart index value // see http://msdn.microsoft.com/en-us/library/windows/desktop/bb205124(v=vs.85).aspx if (translated->indexRange.end == 0xFFFF && type == GL_UNSIGNED_SHORT && mRenderer->getMajorShaderModel() > 3) { destinationIndexType = GL_UNSIGNED_INT; directStorage = false; indexBuffer = NULL; } const gl::Type &destTypeInfo = gl::GetTypeInfo(destinationIndexType); if (!directStorage && !indexBuffer) { gl::Error error = getStreamingIndexBuffer(destinationIndexType, &indexBuffer); if (error.isError()) { return error; } unsigned int convertCount = count; if (staticBuffer) { if (staticBuffer->getBufferSize() == 0 && alignedOffset) { indexBuffer = staticBuffer; convertCount = storage->getSize() / typeInfo.bytes; } else { storage->invalidateStaticData(); staticBuffer = NULL; } } ASSERT(indexBuffer); if (convertCount > std::numeric_limits<unsigned int>::max() / destTypeInfo.bytes) { return gl::Error(GL_OUT_OF_MEMORY, "Reserving %u indices of %u bytes each exceeds the maximum buffer size.", convertCount, destTypeInfo.bytes); } unsigned int bufferSizeRequired = convertCount * destTypeInfo.bytes; error = indexBuffer->reserveBufferSpace(bufferSizeRequired, type); if (error.isError()) { return error; } void* output = NULL; error = indexBuffer->mapBuffer(bufferSizeRequired, &output, &streamOffset); if (error.isError()) { return error; } const uint8_t *dataPointer = reinterpret_cast<const uint8_t*>(indices); if (staticBuffer) { error = storage->getData(&dataPointer); if (error.isError()) { return error; } } ConvertIndices(type, destinationIndexType, dataPointer, convertCount, output); error = indexBuffer->unmapBuffer(); if (error.isError()) { return error; } if (staticBuffer) { streamOffset = (offset / typeInfo.bytes) * destTypeInfo.bytes; staticBuffer->getIndexRangeCache()->addRange(type, offset, count, translated->indexRange, streamOffset); } } translated->storage = directStorage ? storage : NULL; translated->indexBuffer = indexBuffer ? indexBuffer->getIndexBuffer() : NULL; translated->serial = directStorage ? storage->getSerial() : indexBuffer->getSerial(); translated->startIndex = streamOffset / destTypeInfo.bytes; translated->startOffset = streamOffset; translated->indexType = destinationIndexType; if (storage) { storage->promoteStaticUsage(count * typeInfo.bytes); } return gl::Error(GL_NO_ERROR); }
GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, gl::Buffer *buffer, const GLvoid *indices, TranslatedIndexData *translated) { if (!mStreamingBufferShort) { return GL_OUT_OF_MEMORY; } GLenum destinationIndexType = (type == GL_UNSIGNED_INT) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; unsigned int offset = 0; bool alignedOffset = false; BufferD3D *storage = NULL; if (buffer != NULL) { if (reinterpret_cast<uintptr_t>(indices) > std::numeric_limits<unsigned int>::max()) { return GL_OUT_OF_MEMORY; } offset = static_cast<unsigned int>(reinterpret_cast<uintptr_t>(indices)); storage = BufferD3D::makeBufferD3D(buffer->getImplementation()); switch (type) { case GL_UNSIGNED_BYTE: alignedOffset = (offset % sizeof(GLubyte) == 0); break; case GL_UNSIGNED_SHORT: alignedOffset = (offset % sizeof(GLushort) == 0); break; case GL_UNSIGNED_INT: alignedOffset = (offset % sizeof(GLuint) == 0); break; default: UNREACHABLE(); alignedOffset = false; } unsigned int typeSize = gl::GetTypeBytes(type); // check for integer overflows if (static_cast<unsigned int>(count) > (std::numeric_limits<unsigned int>::max() / typeSize) || typeSize * static_cast<unsigned int>(count) + offset < offset) { return GL_OUT_OF_MEMORY; } if (typeSize * static_cast<unsigned int>(count) + offset > storage->getSize()) { return GL_INVALID_OPERATION; } indices = static_cast<const GLubyte*>(storage->getData()) + offset; } StreamingIndexBufferInterface *streamingBuffer = (type == GL_UNSIGNED_INT) ? mStreamingBufferInt : mStreamingBufferShort; StaticIndexBufferInterface *staticBuffer = storage ? storage->getStaticIndexBuffer() : NULL; IndexBufferInterface *indexBuffer = streamingBuffer; bool directStorage = alignedOffset && storage && storage->supportsDirectBinding() && destinationIndexType == type; unsigned int streamOffset = 0; if (directStorage) { indexBuffer = streamingBuffer; streamOffset = offset; if (!storage->getIndexRangeCache()->findRange(type, offset, count, &translated->minIndex, &translated->maxIndex, NULL)) { computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex); storage->getIndexRangeCache()->addRange(type, offset, count, translated->minIndex, translated->maxIndex, offset); } } else if (staticBuffer && staticBuffer->getBufferSize() != 0 && staticBuffer->getIndexType() == type && alignedOffset) { indexBuffer = staticBuffer; if (!staticBuffer->getIndexRangeCache()->findRange(type, offset, count, &translated->minIndex, &translated->maxIndex, &streamOffset)) { streamOffset = (offset / gl::GetTypeBytes(type)) * gl::GetTypeBytes(destinationIndexType); computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex); staticBuffer->getIndexRangeCache()->addRange(type, offset, count, translated->minIndex, translated->maxIndex, streamOffset); } } else { unsigned int convertCount = count; if (staticBuffer) { if (staticBuffer->getBufferSize() == 0 && alignedOffset) { indexBuffer = staticBuffer; convertCount = storage->getSize() / gl::GetTypeBytes(type); } else { storage->invalidateStaticData(); staticBuffer = NULL; } } if (!indexBuffer) { ERR("No valid index buffer."); return GL_INVALID_OPERATION; } unsigned int indexTypeSize = gl::GetTypeBytes(destinationIndexType); if (convertCount > std::numeric_limits<unsigned int>::max() / indexTypeSize) { ERR("Reserving %u indicies of %u bytes each exceeds the maximum buffer size.", convertCount, indexTypeSize); return GL_OUT_OF_MEMORY; } unsigned int bufferSizeRequired = convertCount * indexTypeSize; if (!indexBuffer->reserveBufferSpace(bufferSizeRequired, type)) { ERR("Failed to reserve %u bytes in an index buffer.", bufferSizeRequired); return GL_OUT_OF_MEMORY; } void* output = NULL; if (!indexBuffer->mapBuffer(bufferSizeRequired, &output, &streamOffset)) { ERR("Failed to map index buffer."); return GL_OUT_OF_MEMORY; } convertIndices(type, staticBuffer ? storage->getData() : indices, convertCount, output); if (!indexBuffer->unmapBuffer()) { ERR("Failed to unmap index buffer."); return GL_OUT_OF_MEMORY; } computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex); if (staticBuffer) { streamOffset = (offset / gl::GetTypeBytes(type)) * gl::GetTypeBytes(destinationIndexType); staticBuffer->getIndexRangeCache()->addRange(type, offset, count, translated->minIndex, translated->maxIndex, streamOffset); } } translated->storage = directStorage ? storage : NULL; translated->indexBuffer = indexBuffer->getIndexBuffer(); translated->serial = directStorage ? storage->getSerial() : indexBuffer->getSerial(); translated->startIndex = streamOffset / gl::GetTypeBytes(destinationIndexType); translated->startOffset = streamOffset; if (storage) { storage->promoteStaticUsage(count * gl::GetTypeBytes(type)); } return GL_NO_ERROR; }