// Copyright 2016 The SwiftShader Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // VertexDataManager.h: Defines the VertexDataManager, a class that // runs the Buffer translation process. #include "VertexDataManager.h" #include "Buffer.h" #include "Program.h" #include "IndexDataManager.h" #include "common/debug.h" namespace { enum {INITIAL_STREAM_BUFFER_SIZE = 1024 * 1024}; } namespace es2 { VertexDataManager::VertexDataManager(Context *context) : mContext(context) { for(int i = 0; i < MAX_VERTEX_ATTRIBS; i++) { mDirtyCurrentValue[i] = true; mCurrentValueBuffer[i] = nullptr; } mStreamingBuffer = new StreamingVertexBuffer(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]; } } unsigned int VertexDataManager::writeAttributeData(StreamingVertexBuffer *vertexBuffer, GLint start, GLsizei count, const VertexAttribute &attribute) { Buffer *buffer = attribute.mBoundBuffer; int inputStride = attribute.stride(); int elementSize = attribute.typeSize(); unsigned int streamOffset = 0; char *output = nullptr; if(vertexBuffer) { output = (char*)vertexBuffer->map(attribute, attribute.typeSize() * count, &streamOffset); } if(!output) { ERR("Failed to map vertex buffer."); return ~0u; } const char *input = nullptr; if(buffer) { input = static_cast<const char*>(buffer->data()) + attribute.mOffset; } 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, GLsizei instanceId) { if(!mStreamingBuffer) { return GL_OUT_OF_MEMORY; } const VertexAttributeArray &attribs = mContext->getVertexArrayAttributes(); const VertexAttributeArray ¤tAttribs = mContext->getCurrentVertexAttributes(); Program *program = mContext->getCurrentProgram(); // Determine the required storage size per used buffer for(int i = 0; i < MAX_VERTEX_ATTRIBS; i++) { const VertexAttribute &attrib = attribs[i].mArrayEnabled ? attribs[i] : currentAttribs[i]; if(program->getAttributeStream(i) != -1 && attrib.mArrayEnabled) { if(!attrib.mBoundBuffer) { const bool isInstanced = attrib.mDivisor > 0; mStreamingBuffer->addRequiredSpace(attrib.typeSize() * (isInstanced ? 1 : count)); } } } mStreamingBuffer->reserveRequiredSpace(); // Perform the vertex data translations for(int i = 0; i < MAX_VERTEX_ATTRIBS; i++) { if(program->getAttributeStream(i) != -1) { const VertexAttribute &attrib = attribs[i].mArrayEnabled ? attribs[i] : currentAttribs[i]; if(attrib.mArrayEnabled) { const bool isInstanced = attrib.mDivisor > 0; // Instanced vertices do not apply the 'start' offset GLint firstVertexIndex = isInstanced ? instanceId / attrib.mDivisor : start; Buffer *buffer = attrib.mBoundBuffer; if((!buffer && attrib.mPointer == nullptr) || (buffer && !buffer->data())) { // 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() : nullptr; if(staticBuffer) { translated[i].vertexBuffer = staticBuffer; translated[i].offset = firstVertexIndex * attrib.stride() + static_cast<int>(attrib.mOffset); translated[i].stride = isInstanced ? 0 : attrib.stride(); } else { unsigned int streamOffset = writeAttributeData(mStreamingBuffer, firstVertexIndex, isInstanced ? 1 : count, attrib); if(streamOffset == ~0u) { return GL_OUT_OF_MEMORY; } translated[i].vertexBuffer = mStreamingBuffer->getResource(); translated[i].offset = streamOffset; translated[i].stride = isInstanced ? 0 : attrib.typeSize(); } switch(attrib.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_INT: translated[i].type = sw::STREAMTYPE_INT; break; case GL_UNSIGNED_INT: translated[i].type = sw::STREAMTYPE_UINT; break; case GL_FIXED: translated[i].type = sw::STREAMTYPE_FIXED; break; case GL_FLOAT: translated[i].type = sw::STREAMTYPE_FLOAT; break; case GL_HALF_FLOAT: translated[i].type = sw::STREAMTYPE_HALF; break; case GL_HALF_FLOAT_OES: translated[i].type = sw::STREAMTYPE_HALF; break; case GL_INT_2_10_10_10_REV: translated[i].type = sw::STREAMTYPE_2_10_10_10_INT; break; case GL_UNSIGNED_INT_2_10_10_10_REV: translated[i].type = sw::STREAMTYPE_2_10_10_10_UINT; break; default: UNREACHABLE(attrib.mType); translated[i].type = sw::STREAMTYPE_FLOAT; break; } translated[i].count = attrib.mSize; translated[i].normalized = attrib.mNormalized; } else { if(mDirtyCurrentValue[i]) { delete mCurrentValueBuffer[i]; mCurrentValueBuffer[i] = new ConstantVertexBuffer(attrib.getCurrentValueBitsAsFloat(0), attrib.getCurrentValueBitsAsFloat(1), attrib.getCurrentValueBitsAsFloat(2), attrib.getCurrentValueBitsAsFloat(3)); mDirtyCurrentValue[i] = false; } translated[i].vertexBuffer = mCurrentValueBuffer[i]->getResource(); switch(attrib.currentValueType()) { case GL_INT: translated[i].type = sw::STREAMTYPE_INT; break; case GL_UNSIGNED_INT: translated[i].type = sw::STREAMTYPE_UINT; break; default: translated[i].type = sw::STREAMTYPE_FLOAT; break; } translated[i].count = 4; translated[i].stride = 0; translated[i].offset = 0; translated[i].normalized = false; } } } return GL_NO_ERROR; } VertexBuffer::VertexBuffer(unsigned int size) : mVertexBuffer(nullptr) { if(size > 0) { mVertexBuffer = new sw::Resource(size + 1024); if(!mVertexBuffer) { ERR("Out of memory allocating a vertex buffer of size %u.", size); } } } VertexBuffer::~VertexBuffer() { if(mVertexBuffer) { mVertexBuffer->destruct(); } } void VertexBuffer::unmap() { if(mVertexBuffer) { mVertexBuffer->unlock(); } } sw::Resource *VertexBuffer::getResource() const { return mVertexBuffer; } ConstantVertexBuffer::ConstantVertexBuffer(float x, float y, float z, float w) : VertexBuffer(4 * sizeof(float)) { if(mVertexBuffer) { float *vector = (float*)mVertexBuffer->lock(sw::PUBLIC); vector[0] = x; vector[1] = y; vector[2] = z; vector[3] = w; mVertexBuffer->unlock(); } } ConstantVertexBuffer::~ConstantVertexBuffer() { } StreamingVertexBuffer::StreamingVertexBuffer(unsigned int size) : VertexBuffer(size) { mBufferSize = size; mWritePosition = 0; mRequiredSpace = 0; } StreamingVertexBuffer::~StreamingVertexBuffer() { } void StreamingVertexBuffer::addRequiredSpace(unsigned int requiredSpace) { mRequiredSpace += requiredSpace; } void *StreamingVertexBuffer::map(const VertexAttribute &attribute, unsigned int requiredSpace, unsigned int *offset) { void *mapPtr = nullptr; if(mVertexBuffer) { // We can use a private lock because we never overwrite the content mapPtr = (char*)mVertexBuffer->lock(sw::PRIVATE) + mWritePosition; *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 %u.", mBufferSize); } mWritePosition = 0; } else if(mWritePosition + mRequiredSpace > mBufferSize) // Recycle { if(mVertexBuffer) { mVertexBuffer->destruct(); mVertexBuffer = new sw::Resource(mBufferSize); } mWritePosition = 0; } mRequiredSpace = 0; } }