blob: 1605590259f198bdad0afe9998bb6cc5730f7bb7 [file] [log] [blame]
//
// Copyright (c) 2002-2014 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.
//
// IndexDataManager.cpp: Defines the IndexDataManager, a class that
// runs the Buffer translation process for index buffers.
#include "libANGLE/renderer/d3d/IndexDataManager.h"
#include "libANGLE/renderer/d3d/BufferD3D.h"
#include "libANGLE/renderer/d3d/IndexBuffer.h"
#include "libANGLE/Buffer.h"
#include "libANGLE/formatutils.h"
namespace rx
{
static void ConvertIndices(GLenum sourceType, GLenum destinationType, const void *input,
GLsizei count, void *output)
{
if (sourceType == GL_UNSIGNED_BYTE)
{
ASSERT(destinationType == GL_UNSIGNED_SHORT);
const GLubyte *in = static_cast<const GLubyte*>(input);
GLushort *out = static_cast<GLushort*>(output);
for (GLsizei i = 0; i < count; i++)
{
out[i] = in[i];
}
}
else if (sourceType == GL_UNSIGNED_INT)
{
ASSERT(destinationType == GL_UNSIGNED_INT);
memcpy(output, input, count * sizeof(GLuint));
}
else if (sourceType == GL_UNSIGNED_SHORT)
{
if (destinationType == GL_UNSIGNED_SHORT)
{
memcpy(output, input, count * sizeof(GLushort));
}
else if (destinationType == GL_UNSIGNED_INT)
{
const GLushort *in = static_cast<const GLushort*>(input);
GLuint *out = static_cast<GLuint*>(output);
for (GLsizei i = 0; i < count; i++)
{
out[i] = in[i];
}
}
else UNREACHABLE();
}
else UNREACHABLE();
}
static gl::Error StreamInIndexBuffer(IndexBufferInterface *buffer, const GLvoid *data,
unsigned int count, GLenum srcType, GLenum dstType,
unsigned int *offset)
{
const gl::Type &dstTypeInfo = gl::GetTypeInfo(dstType);
if (count > (std::numeric_limits<unsigned int>::max() >> dstTypeInfo.bytesShift))
{
return gl::Error(GL_OUT_OF_MEMORY,
"Reserving %u indices of %u bytes each exceeds the maximum buffer size.",
count, dstTypeInfo.bytes);
}
unsigned int bufferSizeRequired = count << dstTypeInfo.bytesShift;
gl::Error error = buffer->reserveBufferSpace(bufferSizeRequired, dstType);
if (error.isError())
{
return error;
}
void *output = nullptr;
error = buffer->mapBuffer(bufferSizeRequired, &output, offset);
if (error.isError())
{
return error;
}
ConvertIndices(srcType, dstType, data, count, output);
error = buffer->unmapBuffer();
if (error.isError())
{
return error;
}
return gl::Error(GL_NO_ERROR);
}
IndexDataManager::IndexDataManager(BufferFactoryD3D *factory, RendererClass rendererClass)
: mFactory(factory),
mRendererClass(rendererClass),
mStreamingBufferShort(nullptr),
mStreamingBufferInt(nullptr)
{
}
IndexDataManager::~IndexDataManager()
{
SafeDelete(mStreamingBufferShort);
SafeDelete(mStreamingBufferInt);
}
// This function translates a GL-style indices into DX-style indices, with their description
// returned in translated.
// GL can specify vertex data in immediate mode (pointer to CPU array of indices), which is not
// possible in DX and requires streaming (Case 1). If the GL indices are specified with a buffer
// (Case 2), in a format supported by DX (subcase a) then all is good.
// When we have a buffer with an unsupported format (subcase b) then we need to do some translation:
// we will start by falling back to streaming, and after a while will start using a static translated
// copy of the index buffer.
gl::Error IndexDataManager::prepareIndexData(GLenum srcType, GLsizei count, gl::Buffer *glBuffer,
const GLvoid *indices, TranslatedIndexData *translated,
SourceIndexData *sourceData)
{
// Avoid D3D11's primitive restart index value
// see http://msdn.microsoft.com/en-us/library/windows/desktop/bb205124(v=vs.85).aspx
bool primitiveRestartWorkaround = mRendererClass == RENDERER_D3D11 &&
translated->indexRange.end == 0xFFFF &&
srcType == GL_UNSIGNED_SHORT;
const GLenum dstType = (srcType == GL_UNSIGNED_INT || primitiveRestartWorkaround) ?
GL_UNSIGNED_INT : GL_UNSIGNED_SHORT;
const gl::Type &srcTypeInfo = gl::GetTypeInfo(srcType);
const gl::Type &dstTypeInfo = gl::GetTypeInfo(dstType);
BufferD3D *buffer = glBuffer ? GetImplAs<BufferD3D>(glBuffer) : nullptr;
translated->indexType = dstType;
if (sourceData)
{
sourceData->srcBuffer = buffer;
sourceData->srcIndices = indices;
sourceData->srcIndexType = srcType;
sourceData->srcCount = count;
}
// Case 1: the indices are passed by pointer, which forces the streaming of index data
if (glBuffer == nullptr)
{
translated->storage = nullptr;
return streamIndexData(indices, count, srcType, dstType, translated);
}
// Case 2: the indices are already in a buffer
unsigned int offset = static_cast<unsigned int>(reinterpret_cast<uintptr_t>(indices));
ASSERT(srcTypeInfo.bytes * static_cast<unsigned int>(count) + offset <= buffer->getSize());
bool offsetAligned;
switch (srcType)
{
case GL_UNSIGNED_BYTE: offsetAligned = (offset % sizeof(GLubyte) == 0); break;
case GL_UNSIGNED_SHORT: offsetAligned = (offset % sizeof(GLushort) == 0); break;
case GL_UNSIGNED_INT: offsetAligned = (offset % sizeof(GLuint) == 0); break;
default: UNREACHABLE(); offsetAligned = false;
}
// Case 2a: the buffer can be used directly
if (offsetAligned && buffer->supportsDirectBinding() &&
dstType == srcType && !primitiveRestartWorkaround)
{
translated->storage = buffer;
translated->indexBuffer = nullptr;
translated->serial = buffer->getSerial();
translated->startIndex = (offset >> srcTypeInfo.bytesShift);
translated->startOffset = offset;
buffer->promoteStaticUsage(count << srcTypeInfo.bytesShift);
return gl::Error(GL_NO_ERROR);
}
else
{
translated->storage = nullptr;
}
// Case 2b: use a static translated copy or fall back to streaming
StaticIndexBufferInterface *staticBuffer = buffer->getStaticIndexBuffer();
bool staticBufferInitialized = staticBuffer && staticBuffer->getBufferSize() != 0;
bool staticBufferUsable = staticBuffer &&
offsetAligned && staticBuffer->getIndexType() == dstType;
if (staticBufferInitialized && !staticBufferUsable)
{
buffer->invalidateStaticData();
staticBuffer = nullptr;
}
if (staticBuffer == nullptr || !offsetAligned)
{
const uint8_t *bufferData = nullptr;
gl::Error error = buffer->getData(&bufferData);
if (error.isError())
{
return error;
}
ASSERT(bufferData != nullptr);
error = streamIndexData(bufferData + offset, count, srcType, dstType, translated);
if (error.isError())
{
return error;
}
}
else
{
if (!staticBufferInitialized)
{
const uint8_t *bufferData = nullptr;
gl::Error error = buffer->getData(&bufferData);
if (error.isError())
{
return error;
}
ASSERT(bufferData != nullptr);
unsigned int convertCount =
static_cast<unsigned int>(buffer->getSize()) >> srcTypeInfo.bytesShift;
error = StreamInIndexBuffer(staticBuffer, bufferData, convertCount,
srcType, dstType, nullptr);
if (error.isError())
{
return error;
}
}
ASSERT(offsetAligned && staticBuffer->getIndexType() == dstType);
translated->indexBuffer = staticBuffer->getIndexBuffer();
translated->serial = staticBuffer->getSerial();
translated->startIndex = (offset >> srcTypeInfo.bytesShift);
translated->startOffset = (offset >> srcTypeInfo.bytesShift) << dstTypeInfo.bytesShift;
}
return gl::Error(GL_NO_ERROR);
}
gl::Error IndexDataManager::streamIndexData(const GLvoid *data, unsigned int count, GLenum srcType,
GLenum dstType, TranslatedIndexData *translated)
{
const gl::Type &dstTypeInfo = gl::GetTypeInfo(dstType);
IndexBufferInterface *indexBuffer = nullptr;
gl::Error error = getStreamingIndexBuffer(dstType, &indexBuffer);
if (error.isError())
{
return error;
}
ASSERT(indexBuffer != nullptr);
unsigned int offset;
StreamInIndexBuffer(indexBuffer, data, count, srcType, dstType, &offset);
translated->indexBuffer = indexBuffer->getIndexBuffer();
translated->serial = indexBuffer->getSerial();
translated->startIndex = (offset >> dstTypeInfo.bytesShift);
translated->startOffset = offset;
return gl::Error(GL_NO_ERROR);
}
gl::Error IndexDataManager::getStreamingIndexBuffer(GLenum destinationIndexType,
IndexBufferInterface **outBuffer)
{
ASSERT(outBuffer);
if (destinationIndexType == GL_UNSIGNED_INT)
{
if (!mStreamingBufferInt)
{
mStreamingBufferInt = new StreamingIndexBufferInterface(mFactory);
gl::Error error = mStreamingBufferInt->reserveBufferSpace(INITIAL_INDEX_BUFFER_SIZE,
GL_UNSIGNED_INT);
if (error.isError())
{
SafeDelete(mStreamingBufferInt);
return error;
}
}
*outBuffer = mStreamingBufferInt;
return gl::Error(GL_NO_ERROR);
}
else
{
ASSERT(destinationIndexType == GL_UNSIGNED_SHORT);
if (!mStreamingBufferShort)
{
mStreamingBufferShort = new StreamingIndexBufferInterface(mFactory);
gl::Error error = mStreamingBufferShort->reserveBufferSpace(INITIAL_INDEX_BUFFER_SIZE,
GL_UNSIGNED_SHORT);
if (error.isError())
{
SafeDelete(mStreamingBufferShort);
return error;
}
}
*outBuffer = mStreamingBufferShort;
return gl::Error(GL_NO_ERROR);
}
}
}