// Copyright 2016 The SwiftShader Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // IndexDataManager.cpp: Defines the IndexDataManager, a class that // runs the Buffer translation process for index buffers. #include "IndexDataManager.h" #include "Buffer.h" #include "common/debug.h" #include <string.h> #include <algorithm> namespace { enum { INITIAL_INDEX_BUFFER_SIZE = 4096 * sizeof(GLuint) }; } namespace gl { IndexDataManager::IndexDataManager() { mStreamingBuffer = new StreamingIndexBuffer(INITIAL_INDEX_BUFFER_SIZE); if(!mStreamingBuffer) { ERR("Failed to allocate the streaming index buffer."); } } IndexDataManager::~IndexDataManager() { delete mStreamingBuffer; } void copyIndices(GLenum type, const void *input, GLsizei count, void *output) { if(type == GL_UNSIGNED_BYTE) { memcpy(output, input, count * sizeof(GLubyte)); } else if(type == GL_UNSIGNED_INT) { memcpy(output, input, count * sizeof(GLuint)); } else if(type == GL_UNSIGNED_SHORT) { memcpy(output, input, count * sizeof(GLushort)); } else UNREACHABLE(type); } template<class IndexType> void computeRange(const IndexType *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex) { *minIndex = indices[0]; *maxIndex = indices[0]; for(GLsizei i = 0; i < count; i++) { if(*minIndex > indices[i]) *minIndex = indices[i]; if(*maxIndex < indices[i]) *maxIndex = indices[i]; } } void computeRange(GLenum type, const void *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex) { if(type == GL_UNSIGNED_BYTE) { computeRange(static_cast<const GLubyte*>(indices), count, minIndex, maxIndex); } else if(type == GL_UNSIGNED_INT) { computeRange(static_cast<const GLuint*>(indices), count, minIndex, maxIndex); } else if(type == GL_UNSIGNED_SHORT) { computeRange(static_cast<const GLushort*>(indices), count, minIndex, maxIndex); } else UNREACHABLE(type); } GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, Buffer *buffer, const void *indices, TranslatedIndexData *translated) { if(!mStreamingBuffer) { return GL_OUT_OF_MEMORY; } intptr_t offset = reinterpret_cast<intptr_t>(indices); bool alignedOffset = false; if(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(type); alignedOffset = false; } if(typeSize(type) * count + offset > static_cast<std::size_t>(buffer->size())) { return GL_INVALID_OPERATION; } indices = static_cast<const GLubyte*>(buffer->data()) + offset; } StreamingIndexBuffer *streamingBuffer = mStreamingBuffer; sw::Resource *staticBuffer = buffer ? buffer->getResource() : nullptr; if(staticBuffer) { computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex); translated->indexBuffer = staticBuffer; translated->indexOffset = offset; } else { unsigned int streamOffset = 0; int convertCount = count; streamingBuffer->reserveSpace(convertCount * typeSize(type), type); void *output = streamingBuffer->map(typeSize(type) * convertCount, &streamOffset); if(!output) { ERR("Failed to map index buffer."); return GL_OUT_OF_MEMORY; } copyIndices(type, staticBuffer ? buffer->data() : indices, convertCount, output); streamingBuffer->unmap(); computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex); translated->indexBuffer = streamingBuffer->getResource(); translated->indexOffset = streamOffset; } return GL_NO_ERROR; } std::size_t IndexDataManager::typeSize(GLenum type) { switch(type) { case GL_UNSIGNED_INT: return sizeof(GLuint); case GL_UNSIGNED_SHORT: return sizeof(GLushort); case GL_UNSIGNED_BYTE: return sizeof(GLubyte); default: UNREACHABLE(type); return sizeof(GLushort); } } StreamingIndexBuffer::StreamingIndexBuffer(unsigned int initialSize) : mIndexBuffer(nullptr), mBufferSize(initialSize) { if(initialSize > 0) { mIndexBuffer = new sw::Resource(initialSize + 16); if(!mIndexBuffer) { ERR("Out of memory allocating an index buffer of size %u.", initialSize); } } mWritePosition = 0; } StreamingIndexBuffer::~StreamingIndexBuffer() { if(mIndexBuffer) { mIndexBuffer->destruct(); } } void *StreamingIndexBuffer::map(unsigned int requiredSpace, unsigned int *offset) { void *mapPtr = nullptr; if(mIndexBuffer) { mapPtr = (char*)mIndexBuffer->lock(sw::PUBLIC) + mWritePosition; if(!mapPtr) { ERR(" Lock failed"); return nullptr; } *offset = mWritePosition; mWritePosition += requiredSpace; } return mapPtr; } void StreamingIndexBuffer::unmap() { if(mIndexBuffer) { mIndexBuffer->unlock(); } } void StreamingIndexBuffer::reserveSpace(unsigned int requiredSpace, GLenum type) { if(requiredSpace > mBufferSize) { if(mIndexBuffer) { mIndexBuffer->destruct(); mIndexBuffer = 0; } mBufferSize = std::max(requiredSpace, 2 * mBufferSize); mIndexBuffer = new sw::Resource(mBufferSize + 16); if(!mIndexBuffer) { ERR("Out of memory allocating an index buffer of size %u.", mBufferSize); } mWritePosition = 0; } else if(mWritePosition + requiredSpace > mBufferSize) // Recycle { if(mIndexBuffer) { mIndexBuffer->destruct(); mIndexBuffer = new sw::Resource(mBufferSize + 16); } mWritePosition = 0; } } sw::Resource *StreamingIndexBuffer::getResource() const { return mIndexBuffer; } }