// SwiftShader Software Renderer | |
// | |
// Copyright(c) 2005-2012 TransGaming Inc. | |
// | |
// All rights reserved. No part of this software may be copied, distributed, transmitted, | |
// transcribed, stored in a retrieval system, translated into any human or computer | |
// language by any means, or disclosed to third parties without the explicit written | |
// agreement of TransGaming Inc. Without such an agreement, no rights or licenses, express | |
// or implied, including but not limited to any patent rights, are granted to you. | |
// | |
// 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(); | |
} | |
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(); | |
} | |
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 != NULL) | |
{ | |
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; | |
} | |
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() : NULL; | |
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 == NULL) | |
{ | |
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(); return sizeof(GLushort); | |
} | |
} | |
StreamingIndexBuffer::StreamingIndexBuffer(unsigned int initialSize) : mBufferSize(initialSize), mIndexBuffer(NULL) | |
{ | |
if(initialSize > 0) | |
{ | |
mIndexBuffer = new sw::Resource(initialSize + 16); | |
if(!mIndexBuffer) | |
{ | |
ERR("Out of memory allocating an index buffer of size %lu.", initialSize); | |
} | |
} | |
mWritePosition = 0; | |
} | |
StreamingIndexBuffer::~StreamingIndexBuffer() | |
{ | |
if(mIndexBuffer) | |
{ | |
mIndexBuffer->destruct(); | |
} | |
} | |
void *StreamingIndexBuffer::map(unsigned int requiredSpace, unsigned int *offset) | |
{ | |
void *mapPtr = NULL; | |
if(mIndexBuffer) | |
{ | |
mapPtr = (char*)mIndexBuffer->lock(sw::PUBLIC) + mWritePosition; | |
if(!mapPtr) | |
{ | |
ERR(" Lock failed"); | |
return NULL; | |
} | |
*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 %lu.", 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; | |
} | |
} |