bool VertexBufferInterface::directStoragePossible(const gl::VertexAttribute &attrib, const gl::VertexAttribCurrentValueData ¤tValue) const { gl::Buffer *buffer = attrib.buffer.get(); BufferD3D *storage = buffer ? BufferD3D::makeBufferD3D(buffer->getImplementation()) : NULL; if (!storage || !storage->supportsDirectBinding()) { return false; } // Alignment restrictions: In D3D, vertex data must be aligned to // the format stride, or to a 4-byte boundary, whichever is smaller. // (Undocumented, and experimentally confirmed) size_t alignment = 4; bool requiresConversion = false; if (attrib.type != GL_FLOAT) { gl::VertexFormat vertexFormat(attrib, currentValue.Type); unsigned int outputElementSize; getVertexBuffer()->getSpaceRequired(attrib, 1, 0, &outputElementSize); alignment = std::min<size_t>(outputElementSize, 4); requiresConversion = (mRenderer->getVertexConversionType(vertexFormat) & VERTEX_CONVERT_CPU) != 0; } bool isAligned = (static_cast<size_t>(ComputeVertexAttributeStride(attrib)) % alignment == 0) && (static_cast<size_t>(attrib.offset) % alignment == 0); return !requiresConversion && isAligned; }
void VertexDataManager::hintUnmapAllResources(const std::vector<gl::VertexAttribute> &vertexAttributes) { mStreamingBuffer->getVertexBuffer()->hintUnmapResource(); for (const TranslatedAttribute *translated : mActiveEnabledAttributes) { gl::Buffer *buffer = translated->attribute->buffer.get(); BufferD3D *storage = buffer ? GetImplAs<BufferD3D>(buffer) : nullptr; StaticVertexBufferInterface *staticBuffer = storage ? storage->getStaticVertexBuffer(*translated->attribute) : nullptr; if (staticBuffer) { staticBuffer->getVertexBuffer()->hintUnmapResource(); } } for (auto ¤tValue : mCurrentValueCache) { if (currentValue.buffer != nullptr) { currentValue.buffer->getVertexBuffer()->hintUnmapResource(); } } }
gl::Error VertexBuffer11::storeVertexAttributes(const gl::VertexAttribute &attrib, const gl::VertexAttribCurrentValueData ¤tValue, GLint start, GLsizei count, GLsizei instances, unsigned int offset) { if (!mBuffer) { return gl::Error(GL_OUT_OF_MEMORY, "Internal vertex buffer is not initialized."); } gl::Buffer *buffer = attrib.buffer.get(); int inputStride = ComputeVertexAttributeStride(attrib); ID3D11DeviceContext *dxContext = mRenderer->getDeviceContext(); D3D11_MAPPED_SUBRESOURCE mappedResource; HRESULT result = dxContext->Map(mBuffer, 0, D3D11_MAP_WRITE_NO_OVERWRITE, 0, &mappedResource); if (FAILED(result)) { return gl::Error(GL_OUT_OF_MEMORY, "Failed to map internal vertex buffer, HRESULT: 0x%08x.", result); } uint8_t *output = reinterpret_cast<uint8_t*>(mappedResource.pData) + offset; const uint8_t *input = NULL; if (attrib.enabled) { if (buffer) { BufferD3D *storage = BufferD3D::makeFromBuffer(buffer); gl::Error error = storage->getData(&input); if (error.isError()) { return error; } input += static_cast<int>(attrib.offset); } else { input = static_cast<const uint8_t*>(attrib.pointer); } } else { input = reinterpret_cast<const uint8_t*>(currentValue.FloatValues); } if (instances == 0 || attrib.divisor == 0) { input += inputStride * start; } gl::VertexFormat vertexFormat(attrib, currentValue.Type); const d3d11::VertexFormat &vertexFormatInfo = d3d11::GetVertexFormatInfo(vertexFormat); ASSERT(vertexFormatInfo.copyFunction != NULL); vertexFormatInfo.copyFunction(input, inputStride, count, output); dxContext->Unmap(mBuffer, 0); return gl::Error(GL_NO_ERROR); }
angle::Result VertexDataManager::storeDynamicAttrib(const gl::Context *context, TranslatedAttribute *translated, GLint start, size_t count, GLsizei instances) { ASSERT(translated->attribute && translated->binding); const auto &attrib = *translated->attribute; const auto &binding = *translated->binding; gl::Buffer *buffer = binding.getBuffer().get(); ASSERT(buffer || attrib.pointer); ASSERT(attrib.enabled); BufferD3D *storage = buffer ? GetImplAs<BufferD3D>(buffer) : nullptr; // Instanced vertices do not apply the 'start' offset GLint firstVertexIndex = (binding.getDivisor() > 0 ? 0 : start); // Compute source data pointer const uint8_t *sourceData = nullptr; if (buffer) { ANGLE_TRY(storage->getData(context, &sourceData)); sourceData += static_cast<int>(ComputeVertexAttributeOffset(attrib, binding)); } else { // Attributes using client memory ignore the VERTEX_ATTRIB_BINDING state. // https://www.opengl.org/registry/specs/ARB/vertex_attrib_binding.txt sourceData = static_cast<const uint8_t*>(attrib.pointer); } unsigned int streamOffset = 0; translated->storage = nullptr; ANGLE_TRY( mFactory->getVertexSpaceRequired(context, attrib, binding, 1, 0, &translated->stride)); size_t totalCount = gl::ComputeVertexBindingElementCount(binding.getDivisor(), count, static_cast<size_t>(instances)); ANGLE_TRY(mStreamingBuffer.storeDynamicAttribute( context, attrib, binding, translated->currentValueType, firstVertexIndex, static_cast<GLsizei>(totalCount), instances, &streamOffset, sourceData)); VertexBuffer *vertexBuffer = mStreamingBuffer.getVertexBuffer(); translated->vertexBuffer.set(vertexBuffer); translated->serial = vertexBuffer->getSerial(); translated->baseOffset = streamOffset; translated->usesFirstVertexOffset = false; return angle::Result::Continue(); }
gl::Error VertexDataManager::reserveSpaceForAttrib(const TranslatedAttribute &translatedAttrib, GLsizei count, GLsizei instances) const { const gl::VertexAttribute &attrib = *translatedAttrib.attribute; gl::Buffer *buffer = attrib.buffer.get(); BufferD3D *bufferImpl = buffer ? GetImplAs<BufferD3D>(buffer) : NULL; StaticVertexBufferInterface *staticBuffer = bufferImpl ? bufferImpl->getStaticVertexBuffer(attrib) : NULL; VertexBufferInterface *vertexBuffer = staticBuffer ? staticBuffer : static_cast<VertexBufferInterface*>(mStreamingBuffer); if (!vertexBuffer->directStoragePossible(attrib, translatedAttrib.currentValueType)) { if (staticBuffer) { if (staticBuffer->getBufferSize() == 0) { int totalCount = ElementsInBuffer(attrib, static_cast<unsigned int>(bufferImpl->getSize())); gl::Error error = staticBuffer->reserveVertexSpace(attrib, totalCount, 0); if (error.isError()) { return error; } } } else { size_t totalCount = ComputeVertexAttributeElementCount(attrib, count, instances); ASSERT(!bufferImpl || ElementsInBuffer(attrib, static_cast<unsigned int>(bufferImpl->getSize())) >= static_cast<int>(totalCount)); gl::Error error = mStreamingBuffer->reserveVertexSpace( attrib, static_cast<GLsizei>(totalCount), instances); if (error.isError()) { return error; } } } return gl::Error(GL_NO_ERROR); }
void VertexDataManager::invalidateMatchingStaticData(const gl::VertexAttribute &attrib, const gl::VertexAttribCurrentValueData ¤tValue) const { gl::Buffer *buffer = attrib.buffer.get(); if (buffer) { BufferD3D *bufferImpl = BufferD3D::makeBufferD3D(buffer->getImplementation()); StaticVertexBufferInterface *staticBuffer = bufferImpl->getStaticVertexBuffer(); if (staticBuffer && staticBuffer->getBufferSize() > 0 && !staticBuffer->lookupAttribute(attrib, NULL) && !staticBuffer->directStoragePossible(attrib, currentValue)) { bufferImpl->invalidateStaticData(); } } }
void VertexDataManager::prepareStaticBufferForAttribute(const gl::VertexAttribute &attrib, const gl::VertexAttribCurrentValueData ¤tValue) const { gl::Buffer *buffer = attrib.buffer.get(); if (buffer) { BufferD3D *bufferImpl = GetImplAs<BufferD3D>(buffer); // This will create the static buffer in the right circumstances StaticVertexBufferInterface *staticBuffer = bufferImpl->getStaticVertexBufferForAttribute(attrib); UNUSED_ASSERTION_VARIABLE(staticBuffer); // This check validates that a valid static vertex buffer was returned above ASSERT(!(staticBuffer && staticBuffer->getBufferSize() > 0 && !staticBuffer->lookupAttribute(attrib, NULL) && !staticBuffer->directStoragePossible(attrib, currentValue))); } }
gl::Error VertexDataManager::reserveSpaceForAttrib(const gl::VertexAttribute &attrib, const gl::VertexAttribCurrentValueData ¤tValue, GLsizei count, GLsizei instances) const { gl::Buffer *buffer = attrib.buffer.get(); BufferD3D *bufferImpl = buffer ? BufferD3D::makeBufferD3D(buffer->getImplementation()) : NULL; StaticVertexBufferInterface *staticBuffer = bufferImpl ? bufferImpl->getStaticVertexBuffer() : NULL; VertexBufferInterface *vertexBuffer = staticBuffer ? staticBuffer : static_cast<VertexBufferInterface*>(mStreamingBuffer); if (!vertexBuffer->directStoragePossible(attrib, currentValue)) { if (staticBuffer) { if (staticBuffer->getBufferSize() == 0) { int totalCount = ElementsInBuffer(attrib, bufferImpl->getSize()); gl::Error error = staticBuffer->reserveVertexSpace(attrib, totalCount, 0); if (error.isError()) { return error; } } } else { int totalCount = StreamingBufferElementCount(attrib, count, instances); ASSERT(!bufferImpl || ElementsInBuffer(attrib, bufferImpl->getSize()) >= totalCount); gl::Error error = mStreamingBuffer->reserveVertexSpace(attrib, totalCount, instances); if (error.isError()) { return error; } } } return gl::Error(GL_NO_ERROR); }
gl::Error VertexBuffer9::storeVertexAttributes(const gl::VertexAttribute &attrib, const gl::VertexAttribCurrentValueData ¤tValue, GLint start, GLsizei count, GLsizei instances, unsigned int offset) { if (!mVertexBuffer) { return gl::Error(GL_OUT_OF_MEMORY, "Internal vertex buffer is not initialized."); } gl::Buffer *buffer = attrib.buffer.get(); int inputStride = gl::ComputeVertexAttributeStride(attrib); int elementSize = gl::ComputeVertexAttributeTypeSize(attrib); DWORD lockFlags = mDynamicUsage ? D3DLOCK_NOOVERWRITE : 0; uint8_t *mapPtr = NULL; unsigned int mapSize; gl::Error error = spaceRequired(attrib, count, instances, &mapSize); if (error.isError()) { return error; } HRESULT result = mVertexBuffer->Lock(offset, mapSize, reinterpret_cast<void**>(&mapPtr), lockFlags); if (FAILED(result)) { return gl::Error(GL_OUT_OF_MEMORY, "Failed to lock internal vertex buffer, HRESULT: 0x%08x.", result); } const uint8_t *input = NULL; if (attrib.enabled) { if (buffer) { BufferD3D *storage = BufferD3D::makeFromBuffer(buffer); ASSERT(storage); gl::Error error = storage->getData(&input); if (error.isError()) { return error; } input += static_cast<int>(attrib.offset); } else { input = static_cast<const uint8_t*>(attrib.pointer); } } else { input = reinterpret_cast<const uint8_t*>(currentValue.FloatValues); } if (instances == 0 || attrib.divisor == 0) { input += inputStride * start; } gl::VertexFormat vertexFormat(attrib, currentValue.Type); const d3d9::VertexFormat &d3dVertexInfo = d3d9::GetVertexFormatInfo(mRenderer->getCapsDeclTypes(), vertexFormat); bool needsConversion = (d3dVertexInfo.conversionType & VERTEX_CONVERT_CPU) > 0; if (!needsConversion && inputStride == elementSize) { size_t copySize = static_cast<size_t>(count) * static_cast<size_t>(inputStride); memcpy(mapPtr, input, copySize); } else { d3dVertexInfo.copyFunction(input, inputStride, count, mapPtr); } mVertexBuffer->Unlock(); return gl::Error(GL_NO_ERROR); }
gl::Error VertexDataManager::storeAttribute(TranslatedAttribute *translated, GLint start, GLsizei count, GLsizei instances) { const gl::VertexAttribute &attrib = *translated->attribute; gl::Buffer *buffer = attrib.buffer.get(); ASSERT(buffer || attrib.pointer); ASSERT(attrib.enabled); BufferD3D *storage = buffer ? GetImplAs<BufferD3D>(buffer) : NULL; StaticVertexBufferInterface *staticBuffer = storage ? storage->getStaticVertexBuffer(attrib) : NULL; VertexBufferInterface *vertexBuffer = staticBuffer ? staticBuffer : static_cast<VertexBufferInterface*>(mStreamingBuffer); bool directStorage = vertexBuffer->directStoragePossible(attrib, translated->currentValueType); // Instanced vertices do not apply the 'start' offset GLint firstVertexIndex = (instances > 0 && attrib.divisor > 0 ? 0 : start); translated->vertexBuffer = vertexBuffer->getVertexBuffer(); if (directStorage) { translated->storage = storage; translated->serial = storage->getSerial(); translated->stride = static_cast<unsigned int>(ComputeVertexAttributeStride(attrib)); translated->offset = static_cast<unsigned int>(attrib.offset + translated->stride * firstVertexIndex); return gl::Error(GL_NO_ERROR); } // Compute source data pointer const uint8_t *sourceData = nullptr; if (buffer) { gl::Error error = storage->getData(&sourceData); if (error.isError()) { return error; } sourceData += static_cast<int>(attrib.offset); } else { sourceData = static_cast<const uint8_t*>(attrib.pointer); } unsigned int streamOffset = 0; unsigned int outputElementSize = 0; if (staticBuffer) { gl::Error error = staticBuffer->getVertexBuffer()->getSpaceRequired(attrib, 1, 0, &outputElementSize); if (error.isError()) { return error; } if (!staticBuffer->lookupAttribute(attrib, &streamOffset)) { // Convert the entire buffer int totalCount = ElementsInBuffer(attrib, static_cast<unsigned int>(storage->getSize())); int startIndex = static_cast<int>(attrib.offset) / static_cast<int>(ComputeVertexAttributeStride(attrib)); error = staticBuffer->storeVertexAttributes(attrib, translated->currentValueType, -startIndex, totalCount, 0, &streamOffset, sourceData); if (error.isError()) { return error; } } unsigned int firstElementOffset = (static_cast<unsigned int>(attrib.offset) / static_cast<unsigned int>(ComputeVertexAttributeStride(attrib))) * outputElementSize; unsigned int startOffset = (instances == 0 || attrib.divisor == 0) ? firstVertexIndex * outputElementSize : 0; if (streamOffset + firstElementOffset + startOffset < streamOffset) { return gl::Error(GL_OUT_OF_MEMORY); } streamOffset += firstElementOffset + startOffset; } else { size_t totalCount = ComputeVertexAttributeElementCount(attrib, count, instances); gl::Error error = mStreamingBuffer->getVertexBuffer()->getSpaceRequired(attrib, 1, 0, &outputElementSize); if (error.isError()) { return error; } error = mStreamingBuffer->storeVertexAttributes( attrib, translated->currentValueType, firstVertexIndex, static_cast<GLsizei>(totalCount), instances, &streamOffset, sourceData); if (error.isError()) { return error; } } translated->storage = nullptr; translated->serial = vertexBuffer->getSerial(); translated->stride = outputElementSize; translated->offset = streamOffset; return gl::Error(GL_NO_ERROR); }
gl::Error VertexDataManager::prepareVertexData(const gl::State &state, GLint start, GLsizei count, std::vector<TranslatedAttribute> *translatedAttribs, GLsizei instances) { if (!mStreamingBuffer) { return gl::Error(GL_OUT_OF_MEMORY, "Internal streaming vertex buffer is unexpectedly NULL."); } // Compute active enabled and active disable attributes, for speed. // TODO(jmadill): don't recompute if there was no state change const gl::VertexArray *vertexArray = state.getVertexArray(); const gl::Program *program = state.getProgram(); const auto &vertexAttributes = vertexArray->getVertexAttributes(); mActiveEnabledAttributes.clear(); mActiveDisabledAttributes.clear(); translatedAttribs->clear(); for (size_t attribIndex = 0; attribIndex < vertexAttributes.size(); ++attribIndex) { if (program->isAttribLocationActive(attribIndex)) { // Resize automatically puts in empty attribs translatedAttribs->resize(attribIndex + 1); TranslatedAttribute *translated = &(*translatedAttribs)[attribIndex]; // Record the attribute now translated->active = true; translated->attribute = &vertexAttributes[attribIndex]; translated->currentValueType = state.getVertexAttribCurrentValue(static_cast<unsigned int>(attribIndex)).Type; translated->divisor = vertexAttributes[attribIndex].divisor; if (vertexAttributes[attribIndex].enabled) { mActiveEnabledAttributes.push_back(translated); } else { mActiveDisabledAttributes.push_back(attribIndex); } } } // Reserve the required space in the buffers for (const TranslatedAttribute *activeAttrib : mActiveEnabledAttributes) { gl::Error error = reserveSpaceForAttrib(*activeAttrib, count, instances); if (error.isError()) { return error; } } // Perform the vertex data translations for (TranslatedAttribute *activeAttrib : mActiveEnabledAttributes) { gl::Error error = storeAttribute(activeAttrib, start, count, instances); if (error.isError()) { hintUnmapAllResources(vertexAttributes); return error; } } for (size_t attribIndex : mActiveDisabledAttributes) { if (mCurrentValueCache[attribIndex].buffer == nullptr) { mCurrentValueCache[attribIndex].buffer = new StreamingVertexBufferInterface(mFactory, CONSTANT_VERTEX_BUFFER_SIZE); } gl::Error error = storeCurrentValue( state.getVertexAttribCurrentValue(static_cast<unsigned int>(attribIndex)), &(*translatedAttribs)[attribIndex], &mCurrentValueCache[attribIndex]); if (error.isError()) { hintUnmapAllResources(vertexAttributes); return error; } } // Commit all the static vertex buffers. This fixes them in size/contents, and forces ANGLE // to use a new static buffer (or recreate the static buffers) next time for (size_t attribIndex = 0; attribIndex < vertexAttributes.size(); ++attribIndex) { const gl::VertexAttribute &attrib = vertexAttributes[attribIndex]; gl::Buffer *buffer = attrib.buffer.get(); BufferD3D *storage = buffer ? GetImplAs<BufferD3D>(buffer) : nullptr; StaticVertexBufferInterface *staticBuffer = storage ? storage->getStaticVertexBuffer(attrib) : nullptr; if (staticBuffer) { staticBuffer->commit(); } } // Hint to unmap all the resources hintUnmapAllResources(vertexAttributes); for (const TranslatedAttribute *activeAttrib : mActiveEnabledAttributes) { gl::Buffer *buffer = activeAttrib->attribute->buffer.get(); if (buffer) { BufferD3D *bufferD3D = GetImplAs<BufferD3D>(buffer); size_t typeSize = ComputeVertexAttributeTypeSize(*activeAttrib->attribute); bufferD3D->promoteStaticUsage(count * static_cast<int>(typeSize)); } } return gl::Error(GL_NO_ERROR); }
gl::Error IndexDataManager::prepareIndexData(GLenum type, GLsizei count, gl::Buffer *buffer, const GLvoid *indices, TranslatedIndexData *translated) { const gl::Type &typeInfo = gl::GetTypeInfo(type); GLenum destinationIndexType = (type == GL_UNSIGNED_INT) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; unsigned int offset = 0; bool alignedOffset = false; BufferD3D *storage = NULL; if (buffer != NULL) { offset = static_cast<unsigned int>(reinterpret_cast<uintptr_t>(indices)); storage = GetImplAs<BufferD3D>(buffer); switch (type) { case GL_UNSIGNED_BYTE: alignedOffset = (offset % sizeof(GLubyte) == 0); break; case GL_UNSIGNED_SHORT: alignedOffset = (offset % sizeof(GLushort) == 0); break; case GL_UNSIGNED_INT: alignedOffset = (offset % sizeof(GLuint) == 0); break; default: UNREACHABLE(); alignedOffset = false; } ASSERT(typeInfo.bytes * static_cast<unsigned int>(count) + offset <= storage->getSize()); const uint8_t *bufferData = NULL; gl::Error error = storage->getData(&bufferData); if (error.isError()) { return error; } indices = bufferData + offset; } StaticIndexBufferInterface *staticBuffer = storage ? storage->getStaticIndexBuffer() : NULL; IndexBufferInterface *indexBuffer = NULL; bool directStorage = alignedOffset && storage && storage->supportsDirectBinding() && destinationIndexType == type; unsigned int streamOffset = 0; if (directStorage) { streamOffset = offset; if (!buffer->getIndexRangeCache()->findRange(type, offset, count, NULL, NULL)) { buffer->getIndexRangeCache()->addRange(type, offset, count, translated->indexRange, offset); } } else if (staticBuffer && staticBuffer->getBufferSize() != 0 && staticBuffer->getIndexType() == type && alignedOffset) { indexBuffer = staticBuffer; if (!staticBuffer->getIndexRangeCache()->findRange(type, offset, count, NULL, &streamOffset)) { streamOffset = (offset / typeInfo.bytes) * gl::GetTypeInfo(destinationIndexType).bytes; staticBuffer->getIndexRangeCache()->addRange(type, offset, count, translated->indexRange, streamOffset); } } // Avoid D3D11's primitive restart index value // see http://msdn.microsoft.com/en-us/library/windows/desktop/bb205124(v=vs.85).aspx if (translated->indexRange.end == 0xFFFF && type == GL_UNSIGNED_SHORT && mRenderer->getMajorShaderModel() > 3) { destinationIndexType = GL_UNSIGNED_INT; directStorage = false; indexBuffer = NULL; } const gl::Type &destTypeInfo = gl::GetTypeInfo(destinationIndexType); if (!directStorage && !indexBuffer) { gl::Error error = getStreamingIndexBuffer(destinationIndexType, &indexBuffer); if (error.isError()) { return error; } unsigned int convertCount = count; if (staticBuffer) { if (staticBuffer->getBufferSize() == 0 && alignedOffset) { indexBuffer = staticBuffer; convertCount = storage->getSize() / typeInfo.bytes; } else { storage->invalidateStaticData(); staticBuffer = NULL; } } ASSERT(indexBuffer); if (convertCount > std::numeric_limits<unsigned int>::max() / destTypeInfo.bytes) { return gl::Error(GL_OUT_OF_MEMORY, "Reserving %u indices of %u bytes each exceeds the maximum buffer size.", convertCount, destTypeInfo.bytes); } unsigned int bufferSizeRequired = convertCount * destTypeInfo.bytes; error = indexBuffer->reserveBufferSpace(bufferSizeRequired, type); if (error.isError()) { return error; } void* output = NULL; error = indexBuffer->mapBuffer(bufferSizeRequired, &output, &streamOffset); if (error.isError()) { return error; } const uint8_t *dataPointer = reinterpret_cast<const uint8_t*>(indices); if (staticBuffer) { error = storage->getData(&dataPointer); if (error.isError()) { return error; } } ConvertIndices(type, destinationIndexType, dataPointer, convertCount, output); error = indexBuffer->unmapBuffer(); if (error.isError()) { return error; } if (staticBuffer) { streamOffset = (offset / typeInfo.bytes) * destTypeInfo.bytes; staticBuffer->getIndexRangeCache()->addRange(type, offset, count, translated->indexRange, streamOffset); } } translated->storage = directStorage ? storage : NULL; translated->indexBuffer = indexBuffer ? indexBuffer->getIndexBuffer() : NULL; translated->serial = directStorage ? storage->getSerial() : indexBuffer->getSerial(); translated->startIndex = streamOffset / destTypeInfo.bytes; translated->startOffset = streamOffset; translated->indexType = destinationIndexType; if (storage) { storage->promoteStaticUsage(count * typeInfo.bytes); } return gl::Error(GL_NO_ERROR); }
gl::Error VertexDataManager::storeAttribute(const gl::VertexAttribute &attrib, const gl::VertexAttribCurrentValueData ¤tValue, TranslatedAttribute *translated, GLint start, GLsizei count, GLsizei instances) { gl::Buffer *buffer = attrib.buffer.get(); ASSERT(buffer || attrib.pointer); BufferD3D *storage = buffer ? GetImplAs<BufferD3D>(buffer) : NULL; StaticVertexBufferInterface *staticBuffer = storage ? storage->getStaticVertexBufferForAttribute(attrib) : NULL; VertexBufferInterface *vertexBuffer = staticBuffer ? staticBuffer : static_cast<VertexBufferInterface*>(mStreamingBuffer); bool directStorage = vertexBuffer->directStoragePossible(attrib, currentValue); unsigned int streamOffset = 0; unsigned int outputElementSize = 0; // Instanced vertices do not apply the 'start' offset GLint firstVertexIndex = (instances > 0 && attrib.divisor > 0 ? 0 : start); if (directStorage) { outputElementSize = ComputeVertexAttributeStride(attrib); streamOffset = static_cast<unsigned int>(attrib.offset + outputElementSize * firstVertexIndex); } else if (staticBuffer) { gl::Error error = staticBuffer->getVertexBuffer()->getSpaceRequired(attrib, 1, 0, &outputElementSize); if (error.isError()) { return error; } if (!staticBuffer->lookupAttribute(attrib, &streamOffset)) { // Convert the entire buffer int totalCount = ElementsInBuffer(attrib, storage->getSize()); int startIndex = attrib.offset / ComputeVertexAttributeStride(attrib); error = staticBuffer->storeVertexAttributes(attrib, currentValue, -startIndex, totalCount, 0, &streamOffset); // Each staticBuffer only contains the data for one attribute, so we know that it won't be modified again. // We can therefore safely unmap it here without hurting perf. staticBuffer->getVertexBuffer()->hintUnmapResource(); if (error.isError()) { return error; } } unsigned int firstElementOffset = (attrib.offset / ComputeVertexAttributeStride(attrib)) * outputElementSize; unsigned int startOffset = (instances == 0 || attrib.divisor == 0) ? firstVertexIndex * outputElementSize : 0; if (streamOffset + firstElementOffset + startOffset < streamOffset) { return gl::Error(GL_OUT_OF_MEMORY); } streamOffset += firstElementOffset + startOffset; } else { int totalCount = StreamingBufferElementCount(attrib, count, instances); gl::Error error = mStreamingBuffer->getVertexBuffer()->getSpaceRequired(attrib, 1, 0, &outputElementSize); if (error.isError()) { return error; } error = mStreamingBuffer->storeVertexAttributes(attrib, currentValue, firstVertexIndex, totalCount, instances, &streamOffset); if (error.isError()) { return error; } } translated->storage = directStorage ? storage : NULL; translated->vertexBuffer = vertexBuffer->getVertexBuffer(); translated->serial = directStorage ? storage->getSerial() : vertexBuffer->getSerial(); translated->divisor = attrib.divisor; translated->attribute = &attrib; translated->currentValueType = currentValue.Type; translated->stride = outputElementSize; translated->offset = streamOffset; return gl::Error(GL_NO_ERROR); }
gl::Error VertexDataManager::storeDynamicAttrib(TranslatedAttribute *translated, GLint start, GLsizei count, GLsizei instances) { const gl::VertexAttribute &attrib = *translated->attribute; gl::Buffer *buffer = attrib.buffer.get(); ASSERT(buffer || attrib.pointer); ASSERT(attrib.enabled); BufferD3D *storage = buffer ? GetImplAs<BufferD3D>(buffer) : nullptr; // Instanced vertices do not apply the 'start' offset GLint firstVertexIndex = (attrib.divisor > 0 ? 0 : start); // Compute source data pointer const uint8_t *sourceData = nullptr; if (buffer) { gl::Error error = storage->getData(&sourceData); if (error.isError()) { return error; } sourceData += static_cast<int>(attrib.offset); } else { sourceData = static_cast<const uint8_t*>(attrib.pointer); } unsigned int streamOffset = 0; auto errorOrOutputElementSize = mFactory->getVertexSpaceRequired(attrib, 1, 0); if (errorOrOutputElementSize.isError()) { return errorOrOutputElementSize.getError(); } translated->storage = nullptr; translated->stride = errorOrOutputElementSize.getResult(); size_t totalCount = ComputeVertexAttributeElementCount(attrib, count, instances); gl::Error error = mStreamingBuffer->storeDynamicAttribute( attrib, translated->currentValueType, firstVertexIndex, static_cast<GLsizei>(totalCount), instances, &streamOffset, sourceData); if (error.isError()) { return error; } VertexBuffer *vertexBuffer = mStreamingBuffer->getVertexBuffer(); translated->vertexBuffer.set(vertexBuffer); translated->serial = vertexBuffer->getSerial(); translated->offset = streamOffset; return gl::Error(GL_NO_ERROR); }
GLenum IndexDataManager::prepareIndexData(GLenum type, GLsizei count, gl::Buffer *buffer, const GLvoid *indices, TranslatedIndexData *translated) { if (!mStreamingBufferShort) { return GL_OUT_OF_MEMORY; } GLenum destinationIndexType = (type == GL_UNSIGNED_INT) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; unsigned int offset = 0; bool alignedOffset = false; BufferD3D *storage = NULL; if (buffer != NULL) { if (reinterpret_cast<uintptr_t>(indices) > std::numeric_limits<unsigned int>::max()) { return GL_OUT_OF_MEMORY; } offset = static_cast<unsigned int>(reinterpret_cast<uintptr_t>(indices)); storage = BufferD3D::makeBufferD3D(buffer->getImplementation()); switch (type) { case GL_UNSIGNED_BYTE: alignedOffset = (offset % sizeof(GLubyte) == 0); break; case GL_UNSIGNED_SHORT: alignedOffset = (offset % sizeof(GLushort) == 0); break; case GL_UNSIGNED_INT: alignedOffset = (offset % sizeof(GLuint) == 0); break; default: UNREACHABLE(); alignedOffset = false; } unsigned int typeSize = gl::GetTypeBytes(type); // check for integer overflows if (static_cast<unsigned int>(count) > (std::numeric_limits<unsigned int>::max() / typeSize) || typeSize * static_cast<unsigned int>(count) + offset < offset) { return GL_OUT_OF_MEMORY; } if (typeSize * static_cast<unsigned int>(count) + offset > storage->getSize()) { return GL_INVALID_OPERATION; } indices = static_cast<const GLubyte*>(storage->getData()) + offset; } StreamingIndexBufferInterface *streamingBuffer = (type == GL_UNSIGNED_INT) ? mStreamingBufferInt : mStreamingBufferShort; StaticIndexBufferInterface *staticBuffer = storage ? storage->getStaticIndexBuffer() : NULL; IndexBufferInterface *indexBuffer = streamingBuffer; bool directStorage = alignedOffset && storage && storage->supportsDirectBinding() && destinationIndexType == type; unsigned int streamOffset = 0; if (directStorage) { indexBuffer = streamingBuffer; streamOffset = offset; if (!storage->getIndexRangeCache()->findRange(type, offset, count, &translated->minIndex, &translated->maxIndex, NULL)) { computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex); storage->getIndexRangeCache()->addRange(type, offset, count, translated->minIndex, translated->maxIndex, offset); } } else if (staticBuffer && staticBuffer->getBufferSize() != 0 && staticBuffer->getIndexType() == type && alignedOffset) { indexBuffer = staticBuffer; if (!staticBuffer->getIndexRangeCache()->findRange(type, offset, count, &translated->minIndex, &translated->maxIndex, &streamOffset)) { streamOffset = (offset / gl::GetTypeBytes(type)) * gl::GetTypeBytes(destinationIndexType); computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex); staticBuffer->getIndexRangeCache()->addRange(type, offset, count, translated->minIndex, translated->maxIndex, streamOffset); } } else { unsigned int convertCount = count; if (staticBuffer) { if (staticBuffer->getBufferSize() == 0 && alignedOffset) { indexBuffer = staticBuffer; convertCount = storage->getSize() / gl::GetTypeBytes(type); } else { storage->invalidateStaticData(); staticBuffer = NULL; } } if (!indexBuffer) { ERR("No valid index buffer."); return GL_INVALID_OPERATION; } unsigned int indexTypeSize = gl::GetTypeBytes(destinationIndexType); if (convertCount > std::numeric_limits<unsigned int>::max() / indexTypeSize) { ERR("Reserving %u indicies of %u bytes each exceeds the maximum buffer size.", convertCount, indexTypeSize); return GL_OUT_OF_MEMORY; } unsigned int bufferSizeRequired = convertCount * indexTypeSize; if (!indexBuffer->reserveBufferSpace(bufferSizeRequired, type)) { ERR("Failed to reserve %u bytes in an index buffer.", bufferSizeRequired); return GL_OUT_OF_MEMORY; } void* output = NULL; if (!indexBuffer->mapBuffer(bufferSizeRequired, &output, &streamOffset)) { ERR("Failed to map index buffer."); return GL_OUT_OF_MEMORY; } convertIndices(type, staticBuffer ? storage->getData() : indices, convertCount, output); if (!indexBuffer->unmapBuffer()) { ERR("Failed to unmap index buffer."); return GL_OUT_OF_MEMORY; } computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex); if (staticBuffer) { streamOffset = (offset / gl::GetTypeBytes(type)) * gl::GetTypeBytes(destinationIndexType); staticBuffer->getIndexRangeCache()->addRange(type, offset, count, translated->minIndex, translated->maxIndex, streamOffset); } } translated->storage = directStorage ? storage : NULL; translated->indexBuffer = indexBuffer->getIndexBuffer(); translated->serial = directStorage ? storage->getSerial() : indexBuffer->getSerial(); translated->startIndex = streamOffset / gl::GetTypeBytes(destinationIndexType); translated->startOffset = streamOffset; if (storage) { storage->promoteStaticUsage(count * gl::GetTypeBytes(type)); } return GL_NO_ERROR; }
gl::Error VertexDataManager::prepareVertexData(const gl::State &state, GLint start, GLsizei count, TranslatedAttribute *translated, GLsizei instances) { if (!mStreamingBuffer) { return gl::Error(GL_OUT_OF_MEMORY, "Internal streaming vertex buffer is unexpectedly NULL."); } // Invalidate static buffers that don't contain matching attributes for (int attributeIndex = 0; attributeIndex < gl::MAX_VERTEX_ATTRIBS; attributeIndex++) { translated[attributeIndex].active = (state.getProgram()->getSemanticIndex(attributeIndex) != -1); const gl::VertexAttribute &curAttrib = state.getVertexAttribState(attributeIndex); if (translated[attributeIndex].active && curAttrib.enabled) { invalidateMatchingStaticData(curAttrib, state.getVertexAttribCurrentValue(attributeIndex)); } } // Reserve the required space in the buffers for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++) { const gl::VertexAttribute &curAttrib = state.getVertexAttribState(i); if (translated[i].active && curAttrib.enabled) { gl::Error error = reserveSpaceForAttrib(curAttrib, state.getVertexAttribCurrentValue(i), count, instances); if (error.isError()) { return error; } } } // Perform the vertex data translations for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++) { const gl::VertexAttribute &curAttrib = state.getVertexAttribState(i); if (translated[i].active) { if (curAttrib.enabled) { gl::Error error = storeAttribute(curAttrib, state.getVertexAttribCurrentValue(i), &translated[i], start, count, instances); if (error.isError()) { return error; } } else { if (!mCurrentValueBuffer[i]) { mCurrentValueBuffer[i] = new StreamingVertexBufferInterface(mRenderer, CONSTANT_VERTEX_BUFFER_SIZE); } gl::Error error = storeCurrentValue(curAttrib, state.getVertexAttribCurrentValue(i), &translated[i], &mCurrentValue[i], &mCurrentValueOffsets[i], mCurrentValueBuffer[i]); if (error.isError()) { return error; } } } } for (int i = 0; i < gl::MAX_VERTEX_ATTRIBS; i++) { const gl::VertexAttribute &curAttrib = state.getVertexAttribState(i); if (translated[i].active && curAttrib.enabled) { gl::Buffer *buffer = curAttrib.buffer.get(); if (buffer) { BufferD3D *bufferImpl = BufferD3D::makeBufferD3D(buffer->getImplementation()); bufferImpl->promoteStaticUsage(count * ComputeVertexAttributeTypeSize(curAttrib)); } } } return gl::Error(GL_NO_ERROR); }
gl::Error VertexDataManager::storeAttribute(const gl::VertexAttribute &attrib, const gl::VertexAttribCurrentValueData ¤tValue, TranslatedAttribute *translated, GLint start, GLsizei count, GLsizei instances) { gl::Buffer *buffer = attrib.buffer.get(); ASSERT(buffer || attrib.pointer); BufferD3D *storage = buffer ? BufferD3D::makeBufferD3D(buffer->getImplementation()) : NULL; StaticVertexBufferInterface *staticBuffer = storage ? storage->getStaticVertexBuffer() : NULL; VertexBufferInterface *vertexBuffer = staticBuffer ? staticBuffer : static_cast<VertexBufferInterface*>(mStreamingBuffer); bool directStorage = vertexBuffer->directStoragePossible(attrib, currentValue); unsigned int streamOffset = 0; unsigned int outputElementSize = 0; if (directStorage) { outputElementSize = ComputeVertexAttributeStride(attrib); streamOffset = attrib.offset + outputElementSize * start; } else if (staticBuffer) { gl::Error error = staticBuffer->getVertexBuffer()->getSpaceRequired(attrib, 1, 0, &outputElementSize); if (error.isError()) { return error; } if (!staticBuffer->lookupAttribute(attrib, &streamOffset)) { // Convert the entire buffer int totalCount = ElementsInBuffer(attrib, storage->getSize()); int startIndex = attrib.offset / ComputeVertexAttributeStride(attrib); gl::Error error = staticBuffer->storeVertexAttributes(attrib, currentValue, -startIndex, totalCount, 0, &streamOffset); if (error.isError()) { return error; } } unsigned int firstElementOffset = (attrib.offset / ComputeVertexAttributeStride(attrib)) * outputElementSize; unsigned int startOffset = (instances == 0 || attrib.divisor == 0) ? start * outputElementSize : 0; if (streamOffset + firstElementOffset + startOffset < streamOffset) { return gl::Error(GL_OUT_OF_MEMORY); } streamOffset += firstElementOffset + startOffset; } else { int totalCount = StreamingBufferElementCount(attrib, count, instances); gl::Error error = mStreamingBuffer->getVertexBuffer()->getSpaceRequired(attrib, 1, 0, &outputElementSize); if (error.isError()) { return error; } error = mStreamingBuffer->storeVertexAttributes(attrib, currentValue, start, totalCount, instances, &streamOffset); if (error.isError()) { return error; } } translated->storage = directStorage ? storage : NULL; translated->vertexBuffer = vertexBuffer->getVertexBuffer(); translated->serial = directStorage ? storage->getSerial() : vertexBuffer->getSerial(); translated->divisor = attrib.divisor; translated->attribute = &attrib; translated->currentValueType = currentValue.Type; translated->stride = outputElementSize; translated->offset = streamOffset; return gl::Error(GL_NO_ERROR); }