| // |
| // 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; |
| } |
| |
| } |