blob: a8227a8f253da28c408af082c783ca95ef03da01 [file] [log] [blame]
//
// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// geometry/IndexDataManager.cpp: Defines the IndexDataManager, a class that
// runs the Buffer translation process for index buffers.
#include "libGLESv2/geometry/IndexDataManager.h"
#include "common/debug.h"
#include "libGLESv2/Buffer.h"
#include "libGLESv2/mathutil.h"
#include "libGLESv2/geometry/backend.h"
namespace
{
enum { INITIAL_INDEX_BUFFER_SIZE = 4096 * sizeof(GLuint) };
}
namespace gl
{
IndexDataManager::IndexDataManager(Context *context, BufferBackEnd *backend)
: mContext(context), mBackend(backend), mIntIndicesSupported(backend->supportIntIndices())
{
mCountingBuffer = NULL;
mCountingBufferSize = 0;
mLineLoopBuffer = NULL;
mStreamBufferShort = mBackend->createIndexBuffer(INITIAL_INDEX_BUFFER_SIZE, GL_UNSIGNED_SHORT);
if (mIntIndicesSupported)
{
mStreamBufferInt = mBackend->createIndexBuffer(INITIAL_INDEX_BUFFER_SIZE, GL_UNSIGNED_INT);
}
else
{
mStreamBufferInt = NULL;
}
}
IndexDataManager::~IndexDataManager()
{
delete mStreamBufferShort;
delete mStreamBufferInt;
delete mCountingBuffer;
delete mLineLoopBuffer;
}
namespace
{
template <class InputIndexType, class OutputIndexType>
void copyIndices(const InputIndexType *in, GLsizei count, OutputIndexType *out, GLuint *minIndex, GLuint *maxIndex)
{
InputIndexType first = *in;
GLuint minIndexSoFar = first;
GLuint maxIndexSoFar = first;
for (GLsizei i = 0; i < count; i++)
{
if (minIndexSoFar > *in) minIndexSoFar = *in;
if (maxIndexSoFar < *in) maxIndexSoFar = *in;
*out++ = *in++;
}
// It might be a line loop, so copy the loop index.
*out = first;
*minIndex = minIndexSoFar;
*maxIndex = maxIndexSoFar;
}
}
GLenum IndexDataManager::preRenderValidate(GLenum mode, GLenum type, GLsizei count, Buffer *arrayElementBuffer, const void *indices, TranslatedIndexData *translated)
{
ASSERT(type == GL_UNSIGNED_SHORT || type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_INT);
ASSERT(count > 0);
if (arrayElementBuffer != NULL)
{
GLsizei offset = reinterpret_cast<GLsizei>(indices);
if (typeSize(type) * count + offset > static_cast<std::size_t>(arrayElementBuffer->size()))
{
return GL_INVALID_OPERATION;
}
indices = static_cast<const GLubyte*>(arrayElementBuffer->data()) + offset;
}
translated->count = count;
std::size_t requiredSpace = spaceRequired(type, count);
TranslatedIndexBuffer *streamIb = prepareIndexBuffer(type, requiredSpace);
size_t offset;
void *output = streamIb->map(requiredSpace, &offset);
if (output == NULL)
{
ERR(" failed to map index buffer.");
return GL_OUT_OF_MEMORY;
}
translated->buffer = streamIb;
translated->offset = offset;
translated->indexSize = indexSize(type);
if (type == GL_UNSIGNED_BYTE)
{
const GLubyte *in = static_cast<const GLubyte*>(indices);
GLushort *out = static_cast<GLushort*>(output);
copyIndices(in, count, out, &translated->minIndex, &translated->maxIndex);
}
else if (type == GL_UNSIGNED_INT)
{
const GLuint *in = static_cast<const GLuint*>(indices);
if (mIntIndicesSupported)
{
GLuint *out = static_cast<GLuint*>(output);
copyIndices(in, count, out, &translated->minIndex, &translated->maxIndex);
}
else
{
// When 32-bit indices are unsupported, fake them by truncating to 16-bit.
GLushort *out = static_cast<GLushort*>(output);
copyIndices(in, count, out, &translated->minIndex, &translated->maxIndex);
}
}
else
{
const GLushort *in = static_cast<const GLushort*>(indices);
GLushort *out = static_cast<GLushort*>(output);
copyIndices(in, count, out, &translated->minIndex, &translated->maxIndex);
}
streamIb->unmap();
return GL_NO_ERROR;
}
std::size_t IndexDataManager::indexSize(GLenum type) const
{
return (type == GL_UNSIGNED_INT && mIntIndicesSupported) ? sizeof(GLuint) : sizeof(GLushort);
}
std::size_t IndexDataManager::typeSize(GLenum type) const
{
switch (type)
{
case GL_UNSIGNED_INT: return sizeof(GLuint);
case GL_UNSIGNED_SHORT: return sizeof(GLushort);
default: UNREACHABLE();
case GL_UNSIGNED_BYTE: return sizeof(GLubyte);
}
}
std::size_t IndexDataManager::spaceRequired(GLenum type, GLsizei count) const
{
return (count + 1) * indexSize(type); // +1 because we always leave an extra for line loops
}
TranslatedIndexBuffer *IndexDataManager::prepareIndexBuffer(GLenum type, std::size_t requiredSpace)
{
bool use32 = (type == GL_UNSIGNED_INT && mIntIndicesSupported);
TranslatedIndexBuffer *streamIb = use32 ? mStreamBufferInt : mStreamBufferShort;
if (requiredSpace > streamIb->size())
{
std::size_t newSize = std::max(requiredSpace, 2 * streamIb->size());
TranslatedIndexBuffer *newStreamBuffer = mBackend->createIndexBuffer(newSize, use32 ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT);
delete streamIb;
streamIb = newStreamBuffer;
if (use32)
{
mStreamBufferInt = streamIb;
}
else
{
mStreamBufferShort = streamIb;
}
}
streamIb->reserveSpace(requiredSpace);
return streamIb;
}
GLenum IndexDataManager::preRenderValidateUnindexed(GLenum mode, GLsizei count, TranslatedIndexData *indexInfo)
{
if (count >= 65535) return GL_OUT_OF_MEMORY;
if (mode == GL_LINE_LOOP)
{
// For line loops, create a single-use buffer that runs 0 - count-1, 0.
delete mLineLoopBuffer;
mLineLoopBuffer = mBackend->createIndexBuffer((count+1) * sizeof(unsigned short), GL_UNSIGNED_SHORT);
unsigned short *indices = static_cast<unsigned short *>(mLineLoopBuffer->map());
if (indices == NULL)
{
ERR(" failed to map index buffer.");
return GL_OUT_OF_MEMORY;
}
for (int i = 0; i < count; i++)
{
indices[i] = i;
}
indices[count] = 0;
mLineLoopBuffer->unmap();
indexInfo->buffer = mLineLoopBuffer;
indexInfo->count = count + 1;
indexInfo->maxIndex = count - 1;
}
else if (mCountingBufferSize < count)
{
mCountingBufferSize = std::max(static_cast<GLsizei>(ceilPow2(count)), mCountingBufferSize*2);
delete mCountingBuffer;
mCountingBuffer = mBackend->createIndexBuffer(count * sizeof(unsigned short), GL_UNSIGNED_SHORT);
unsigned short *indices = static_cast<unsigned short *>(mCountingBuffer->map());
if (indices == NULL)
{
ERR(" failed to map index buffer.");
return GL_OUT_OF_MEMORY;
}
for (int i = 0; i < count; i++)
{
indices[i] = i;
}
mCountingBuffer->unmap();
indexInfo->buffer = mCountingBuffer;
indexInfo->count = count;
indexInfo->maxIndex = count - 1;
}
else
{
indexInfo->buffer = mCountingBuffer;
indexInfo->count = count;
indexInfo->maxIndex = count - 1;
}
indexInfo->indexSize = sizeof(unsigned short);
indexInfo->minIndex = 0;
indexInfo->offset = 0;
return GL_NO_ERROR;
}
}