blob: 44abf4e647f05264e77bc47639ff5a29a739b6f0 [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.
//
// VertexDataManager.h: Defines the VertexDataManager, a class that
// runs the Buffer translation process.
#include "VertexDataManager.h"
#include "Buffer.h"
#include "Program.h"
#include "main.h"
#include "IndexDataManager.h"
#include "common/debug.h"
namespace
{
enum {INITIAL_STREAM_BUFFER_SIZE = 1024 * 1024};
}
namespace gl
{
VertexDataManager::VertexDataManager(Context *context, Device *device) : mContext(context), mDevice(device)
{
for(int i = 0; i < MAX_VERTEX_ATTRIBS; i++)
{
mDirtyCurrentValue[i] = true;
mCurrentValueBuffer[i] = NULL;
}
mStreamingBuffer = new StreamingVertexBuffer(mDevice, INITIAL_STREAM_BUFFER_SIZE);
if(!mStreamingBuffer)
{
ERR("Failed to allocate the streaming vertex buffer.");
}
}
VertexDataManager::~VertexDataManager()
{
delete mStreamingBuffer;
for(int i = 0; i < MAX_VERTEX_ATTRIBS; i++)
{
delete mCurrentValueBuffer[i];
}
}
UINT VertexDataManager::writeAttributeData(ArrayVertexBuffer *vertexBuffer, GLint start, GLsizei count, const VertexAttribute &attribute)
{
Buffer *buffer = attribute.mBoundBuffer.get();
int inputStride = attribute.stride();
int elementSize = attribute.typeSize();
UINT streamOffset = 0;
char *output = NULL;
if(vertexBuffer)
{
output = (char*)vertexBuffer->map(attribute, spaceRequired(attribute, count), &streamOffset);
}
if(output == NULL)
{
ERR("Failed to map vertex buffer.");
return -1;
}
const char *input = NULL;
if(buffer)
{
int offset = attribute.mOffset;
input = static_cast<const char*>(buffer->data()) + offset;
}
else
{
input = static_cast<const char*>(attribute.mPointer);
}
input += inputStride * start;
if(inputStride == elementSize)
{
memcpy(output, input, count * inputStride);
}
else
{
for(int i = 0; i < count; i++)
{
memcpy(output, input, elementSize);
output += elementSize;
input += inputStride;
}
}
vertexBuffer->unmap();
return streamOffset;
}
GLenum VertexDataManager::prepareVertexData(GLint start, GLsizei count, TranslatedAttribute *translated)
{
if(!mStreamingBuffer)
{
return GL_OUT_OF_MEMORY;
}
const VertexAttributeArray &attribs = mContext->getVertexAttributes();
Program *program = mContext->getCurrentProgram();
for(int attributeIndex = 0; attributeIndex < MAX_VERTEX_ATTRIBS; attributeIndex++)
{
translated[attributeIndex].active = (program->getSemanticIndex(attributeIndex) != -1);
}
// Determine the required storage size per used buffer, and invalidate static buffers that don't contain matching attributes
for(int i = 0; i < MAX_VERTEX_ATTRIBS; i++)
{
if(translated[i].active && attribs[i].mArrayEnabled)
{
if(!attribs[i].mBoundBuffer)
{
mStreamingBuffer->addRequiredSpace(spaceRequired(attribs[i], count));
}
}
}
mStreamingBuffer->reserveRequiredSpace();
// Perform the vertex data translations
for(int i = 0; i < MAX_VERTEX_ATTRIBS; i++)
{
if(translated[i].active)
{
if(attribs[i].mArrayEnabled)
{
Buffer *buffer = attribs[i].mBoundBuffer.get();
if(!buffer && attribs[i].mPointer == NULL)
{
// This is an application error that would normally result in a crash, but we catch it and return an error
ERR("An enabled vertex array has no buffer and no pointer.");
return GL_INVALID_OPERATION;
}
sw::Resource *staticBuffer = buffer ? buffer->getResource() : NULL;
if(staticBuffer)
{
translated[i].vertexBuffer = staticBuffer;
translated[i].offset = start * attribs[i].stride() + attribs[i].mOffset;
translated[i].stride = attribs[i].stride();
}
else
{
UINT streamOffset = writeAttributeData(mStreamingBuffer, start, count, attribs[i]);
if(streamOffset == -1)
{
return GL_OUT_OF_MEMORY;
}
translated[i].vertexBuffer = mStreamingBuffer->getResource();
translated[i].offset = streamOffset;
translated[i].stride = attribs[i].typeSize();
}
switch(attribs[i].mType)
{
case GL_BYTE: translated[i].type = sw::STREAMTYPE_SBYTE; break;
case GL_UNSIGNED_BYTE: translated[i].type = sw::STREAMTYPE_BYTE; break;
case GL_SHORT: translated[i].type = sw::STREAMTYPE_SHORT; break;
case GL_UNSIGNED_SHORT: translated[i].type = sw::STREAMTYPE_USHORT; break;
case GL_FIXED: translated[i].type = sw::STREAMTYPE_FIXED; break;
case GL_FLOAT: translated[i].type = sw::STREAMTYPE_FLOAT; break;
default: UNREACHABLE(); translated[i].type = sw::STREAMTYPE_FLOAT; break;
}
translated[i].count = attribs[i].mSize;
translated[i].normalized = attribs[i].mNormalized;
}
else
{
if(mDirtyCurrentValue[i])
{
delete mCurrentValueBuffer[i];
mCurrentValueBuffer[i] = new ConstantVertexBuffer(mDevice, attribs[i].mCurrentValue[0], attribs[i].mCurrentValue[1], attribs[i].mCurrentValue[2], attribs[i].mCurrentValue[3]);
mDirtyCurrentValue[i] = false;
}
translated[i].vertexBuffer = mCurrentValueBuffer[i]->getResource();
translated[i].type = sw::STREAMTYPE_FLOAT;
translated[i].count = 4;
translated[i].stride = 0;
translated[i].offset = 0;
}
}
}
return GL_NO_ERROR;
}
std::size_t VertexDataManager::spaceRequired(const VertexAttribute &attrib, std::size_t count) const
{
return attrib.typeSize() * count;
}
VertexBuffer::VertexBuffer(Device *device, std::size_t size) : mDevice(device), mVertexBuffer(NULL)
{
if(size > 0)
{
mVertexBuffer = new sw::Resource(size + 1024);
if(!mVertexBuffer)
{
ERR("Out of memory allocating a vertex buffer of size %lu.", size);
}
}
}
VertexBuffer::~VertexBuffer()
{
if(mVertexBuffer)
{
mVertexBuffer->destruct();
}
}
void VertexBuffer::unmap()
{
if(mVertexBuffer)
{
mVertexBuffer->unlock();
}
}
sw::Resource *VertexBuffer::getResource() const
{
return mVertexBuffer;
}
ConstantVertexBuffer::ConstantVertexBuffer(Device *device, float x, float y, float z, float w) : VertexBuffer(device, 4 * sizeof(float))
{
void *buffer = NULL;
if(mVertexBuffer)
{
buffer = mVertexBuffer->lock(sw::PUBLIC);
if(!buffer)
{
ERR("Lock failed");
}
}
if(buffer)
{
float *vector = (float*)buffer;
vector[0] = x;
vector[1] = y;
vector[2] = z;
vector[3] = w;
mVertexBuffer->unlock();
}
}
ConstantVertexBuffer::~ConstantVertexBuffer()
{
}
ArrayVertexBuffer::ArrayVertexBuffer(Device *device, std::size_t size) : VertexBuffer(device, size)
{
mBufferSize = size;
mWritePosition = 0;
mRequiredSpace = 0;
}
ArrayVertexBuffer::~ArrayVertexBuffer()
{
}
void ArrayVertexBuffer::addRequiredSpace(UINT requiredSpace)
{
mRequiredSpace += requiredSpace;
}
StreamingVertexBuffer::StreamingVertexBuffer(Device *device, std::size_t initialSize) : ArrayVertexBuffer(device, initialSize)
{
}
StreamingVertexBuffer::~StreamingVertexBuffer()
{
}
void *StreamingVertexBuffer::map(const VertexAttribute &attribute, std::size_t requiredSpace, std::size_t *offset)
{
void *mapPtr = NULL;
if(mVertexBuffer)
{
mapPtr = (char*)mVertexBuffer->lock(sw::PUBLIC) + mWritePosition;
if(!mapPtr)
{
ERR("Lock failed");
return NULL;
}
*offset = mWritePosition;
mWritePosition += requiredSpace;
}
return mapPtr;
}
void StreamingVertexBuffer::reserveRequiredSpace()
{
if(mRequiredSpace > mBufferSize)
{
if(mVertexBuffer)
{
mVertexBuffer->destruct();
mVertexBuffer = 0;
}
mBufferSize = std::max(mRequiredSpace, 3 * mBufferSize / 2); // 1.5 x mBufferSize is arbitrary and should be checked to see we don't have too many reallocations.
mVertexBuffer = new sw::Resource(mBufferSize);
if(!mVertexBuffer)
{
ERR("Out of memory allocating a vertex buffer of size %lu.", mBufferSize);
}
mWritePosition = 0;
}
else if(mWritePosition + mRequiredSpace > mBufferSize) // Recycle
{
if(mVertexBuffer)
{
mVertexBuffer->destruct();
mVertexBuffer = new sw::Resource(mBufferSize);
}
mWritePosition = 0;
}
mRequiredSpace = 0;
}
}