summaryrefslogtreecommitdiffstats
path: root/gfx/angle/src/libANGLE/renderer/d3d/VertexDataManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/angle/src/libANGLE/renderer/d3d/VertexDataManager.cpp')
-rwxr-xr-xgfx/angle/src/libANGLE/renderer/d3d/VertexDataManager.cpp589
1 files changed, 589 insertions, 0 deletions
diff --git a/gfx/angle/src/libANGLE/renderer/d3d/VertexDataManager.cpp b/gfx/angle/src/libANGLE/renderer/d3d/VertexDataManager.cpp
new file mode 100755
index 000000000..89dd4faf2
--- /dev/null
+++ b/gfx/angle/src/libANGLE/renderer/d3d/VertexDataManager.cpp
@@ -0,0 +1,589 @@
+//
+// Copyright (c) 2002-2012 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 "libANGLE/renderer/d3d/VertexDataManager.h"
+
+#include "common/BitSetIterator.h"
+#include "libANGLE/Buffer.h"
+#include "libANGLE/formatutils.h"
+#include "libANGLE/Program.h"
+#include "libANGLE/State.h"
+#include "libANGLE/VertexAttribute.h"
+#include "libANGLE/VertexArray.h"
+#include "libANGLE/renderer/d3d/BufferD3D.h"
+#include "libANGLE/renderer/d3d/RendererD3D.h"
+#include "libANGLE/renderer/d3d/VertexBuffer.h"
+
+using namespace angle;
+
+namespace rx
+{
+namespace
+{
+enum
+{
+ INITIAL_STREAM_BUFFER_SIZE = 1024 * 1024
+};
+// This has to be at least 4k or else it fails on ATI cards.
+enum
+{
+ CONSTANT_VERTEX_BUFFER_SIZE = 4096
+};
+
+int ElementsInBuffer(const gl::VertexAttribute &attrib, unsigned int size)
+{
+ // Size cannot be larger than a GLsizei
+ if (size > static_cast<unsigned int>(std::numeric_limits<int>::max()))
+ {
+ size = static_cast<unsigned int>(std::numeric_limits<int>::max());
+ }
+
+ GLsizei stride = static_cast<GLsizei>(ComputeVertexAttributeStride(attrib));
+ return (size - attrib.offset % stride +
+ (stride - static_cast<GLsizei>(ComputeVertexAttributeTypeSize(attrib)))) /
+ stride;
+}
+
+bool DirectStoragePossible(const gl::VertexAttribute &attrib)
+{
+ // Current value attribs may not use direct storage.
+ if (!attrib.enabled)
+ {
+ return false;
+ }
+
+ gl::Buffer *buffer = attrib.buffer.get();
+ if (!buffer)
+ {
+ return false;
+ }
+
+ BufferD3D *bufferD3D = GetImplAs<BufferD3D>(buffer);
+ ASSERT(bufferD3D);
+ if (!bufferD3D->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;
+
+ if (attrib.type != GL_FLOAT)
+ {
+ gl::VertexFormatType vertexFormatType = gl::GetVertexFormatType(attrib);
+
+ // TODO(jmadill): add VertexFormatCaps
+ BufferFactoryD3D *factory = bufferD3D->getFactory();
+
+ auto errorOrElementSize = factory->getVertexSpaceRequired(attrib, 1, 0);
+ if (errorOrElementSize.isError())
+ {
+ ERR("Unlogged error in DirectStoragePossible.");
+ return false;
+ }
+
+ alignment = std::min<size_t>(errorOrElementSize.getResult(), 4);
+
+ // CPU-converted vertex data must be converted (naturally).
+ if ((factory->getVertexConversionType(vertexFormatType) & VERTEX_CONVERT_CPU) != 0)
+ {
+ return false;
+ }
+ }
+
+ // Final alignment check - unaligned data must be converted.
+ return (static_cast<size_t>(ComputeVertexAttributeStride(attrib)) % alignment == 0) &&
+ (static_cast<size_t>(attrib.offset) % alignment == 0);
+}
+} // anonymous namespace
+
+TranslatedAttribute::TranslatedAttribute()
+ : active(false),
+ attribute(nullptr),
+ currentValueType(GL_NONE),
+ baseOffset(0),
+ usesFirstVertexOffset(false),
+ stride(0),
+ vertexBuffer(),
+ storage(nullptr),
+ serial(0),
+ divisor(0)
+{
+}
+
+gl::ErrorOrResult<unsigned int> TranslatedAttribute::computeOffset(GLint startVertex) const
+{
+ if (!usesFirstVertexOffset)
+ {
+ return baseOffset;
+ }
+
+ CheckedNumeric<unsigned int> offset;
+
+ offset = baseOffset + stride * static_cast<unsigned int>(startVertex);
+ ANGLE_TRY_CHECKED_MATH(offset);
+ return offset.ValueOrDie();
+}
+
+VertexStorageType ClassifyAttributeStorage(const gl::VertexAttribute &attrib)
+{
+ // If attribute is disabled, we use the current value.
+ if (!attrib.enabled)
+ {
+ return VertexStorageType::CURRENT_VALUE;
+ }
+
+ // If specified with immediate data, we must use dynamic storage.
+ auto *buffer = attrib.buffer.get();
+ if (!buffer)
+ {
+ return VertexStorageType::DYNAMIC;
+ }
+
+ // Check if the buffer supports direct storage.
+ if (DirectStoragePossible(attrib))
+ {
+ return VertexStorageType::DIRECT;
+ }
+
+ // Otherwise the storage is static or dynamic.
+ BufferD3D *bufferD3D = GetImplAs<BufferD3D>(buffer);
+ ASSERT(bufferD3D);
+ switch (bufferD3D->getUsage())
+ {
+ case D3DBufferUsage::DYNAMIC:
+ return VertexStorageType::DYNAMIC;
+ case D3DBufferUsage::STATIC:
+ return VertexStorageType::STATIC;
+ default:
+ UNREACHABLE();
+ return VertexStorageType::UNKNOWN;
+ }
+}
+
+VertexDataManager::CurrentValueState::CurrentValueState()
+ : buffer(nullptr),
+ offset(0)
+{
+ data.FloatValues[0] = std::numeric_limits<float>::quiet_NaN();
+ data.FloatValues[1] = std::numeric_limits<float>::quiet_NaN();
+ data.FloatValues[2] = std::numeric_limits<float>::quiet_NaN();
+ data.FloatValues[3] = std::numeric_limits<float>::quiet_NaN();
+ data.Type = GL_FLOAT;
+}
+
+VertexDataManager::CurrentValueState::~CurrentValueState()
+{
+ SafeDelete(buffer);
+}
+
+VertexDataManager::VertexDataManager(BufferFactoryD3D *factory)
+ : mFactory(factory),
+ mStreamingBuffer(nullptr),
+ // TODO(jmadill): use context caps
+ mCurrentValueCache(gl::MAX_VERTEX_ATTRIBS)
+{
+ mStreamingBuffer = new StreamingVertexBufferInterface(factory, INITIAL_STREAM_BUFFER_SIZE);
+
+ if (!mStreamingBuffer)
+ {
+ ERR("Failed to allocate the streaming vertex buffer.");
+ }
+}
+
+VertexDataManager::~VertexDataManager()
+{
+ SafeDelete(mStreamingBuffer);
+}
+
+gl::Error VertexDataManager::prepareVertexData(const gl::State &state,
+ GLint start,
+ GLsizei count,
+ std::vector<TranslatedAttribute> *translatedAttribs,
+ GLsizei instances)
+{
+ ASSERT(mStreamingBuffer);
+
+ const gl::VertexArray *vertexArray = state.getVertexArray();
+ const auto &vertexAttributes = vertexArray->getVertexAttributes();
+
+ mDynamicAttribsMaskCache.reset();
+ const gl::Program *program = state.getProgram();
+
+ translatedAttribs->clear();
+
+ for (size_t attribIndex = 0; attribIndex < vertexAttributes.size(); ++attribIndex)
+ {
+ // Skip attrib locations the program doesn't use.
+ if (!program->isAttribLocationActive(attribIndex))
+ continue;
+
+ const auto &attrib = vertexAttributes[attribIndex];
+
+ // Resize automatically puts in empty attribs
+ translatedAttribs->resize(attribIndex + 1);
+
+ TranslatedAttribute *translated = &(*translatedAttribs)[attribIndex];
+ auto currentValueData =
+ state.getVertexAttribCurrentValue(static_cast<unsigned int>(attribIndex));
+
+ // Record the attribute now
+ translated->active = true;
+ translated->attribute = &attrib;
+ translated->currentValueType = currentValueData.Type;
+ translated->divisor = attrib.divisor;
+
+ switch (ClassifyAttributeStorage(attrib))
+ {
+ case VertexStorageType::STATIC:
+ {
+ // Store static attribute.
+ ANGLE_TRY(StoreStaticAttrib(translated, count, instances));
+ break;
+ }
+ case VertexStorageType::DYNAMIC:
+ // Dynamic attributes must be handled together.
+ mDynamicAttribsMaskCache.set(attribIndex);
+ break;
+ case VertexStorageType::DIRECT:
+ // Update translated data for direct attributes.
+ StoreDirectAttrib(translated);
+ break;
+ case VertexStorageType::CURRENT_VALUE:
+ {
+ ANGLE_TRY(storeCurrentValue(currentValueData, translated, attribIndex));
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+
+ if (mDynamicAttribsMaskCache.none())
+ {
+ return gl::NoError();
+ }
+
+ ANGLE_TRY(
+ storeDynamicAttribs(translatedAttribs, mDynamicAttribsMaskCache, start, count, instances));
+
+ PromoteDynamicAttribs(*translatedAttribs, mDynamicAttribsMaskCache, count);
+
+ return gl::NoError();
+}
+
+// static
+void VertexDataManager::StoreDirectAttrib(TranslatedAttribute *directAttrib)
+{
+ const auto &attrib = *directAttrib->attribute;
+ gl::Buffer *buffer = attrib.buffer.get();
+ BufferD3D *bufferD3D = buffer ? GetImplAs<BufferD3D>(buffer) : nullptr;
+
+ ASSERT(DirectStoragePossible(attrib));
+ directAttrib->vertexBuffer.set(nullptr);
+ directAttrib->storage = bufferD3D;
+ directAttrib->serial = bufferD3D->getSerial();
+ directAttrib->stride = static_cast<unsigned int>(ComputeVertexAttributeStride(attrib));
+ directAttrib->baseOffset = static_cast<unsigned int>(attrib.offset);
+
+ // Instanced vertices do not apply the 'start' offset
+ directAttrib->usesFirstVertexOffset = (attrib.divisor == 0);
+}
+
+// static
+gl::Error VertexDataManager::StoreStaticAttrib(TranslatedAttribute *translated,
+ GLsizei count,
+ GLsizei instances)
+{
+ const gl::VertexAttribute &attrib = *translated->attribute;
+
+ gl::Buffer *buffer = attrib.buffer.get();
+ ASSERT(buffer && attrib.enabled && !DirectStoragePossible(attrib));
+ BufferD3D *bufferD3D = GetImplAs<BufferD3D>(buffer);
+
+ // Compute source data pointer
+ const uint8_t *sourceData = nullptr;
+
+ ANGLE_TRY(bufferD3D->getData(&sourceData));
+ sourceData += static_cast<int>(attrib.offset);
+
+ unsigned int streamOffset = 0;
+
+ translated->storage = nullptr;
+ ANGLE_TRY_RESULT(bufferD3D->getFactory()->getVertexSpaceRequired(attrib, 1, 0),
+ translated->stride);
+
+ auto *staticBuffer = bufferD3D->getStaticVertexBuffer(attrib);
+ ASSERT(staticBuffer);
+
+ if (staticBuffer->empty())
+ {
+ // Convert the entire buffer
+ int totalCount = ElementsInBuffer(attrib, static_cast<unsigned int>(bufferD3D->getSize()));
+ int startIndex = static_cast<int>(attrib.offset) /
+ static_cast<int>(ComputeVertexAttributeStride(attrib));
+
+ ANGLE_TRY(
+ staticBuffer->storeStaticAttribute(attrib, -startIndex, totalCount, 0, sourceData));
+ }
+
+ unsigned int firstElementOffset =
+ (static_cast<unsigned int>(attrib.offset) /
+ static_cast<unsigned int>(ComputeVertexAttributeStride(attrib))) *
+ translated->stride;
+
+ VertexBuffer *vertexBuffer = staticBuffer->getVertexBuffer();
+
+ CheckedNumeric<unsigned int> checkedOffset(streamOffset);
+ checkedOffset += firstElementOffset;
+
+ if (!checkedOffset.IsValid())
+ {
+ return gl::Error(GL_INVALID_OPERATION,
+ "Integer overflow in VertexDataManager::StoreStaticAttrib");
+ }
+
+ translated->vertexBuffer.set(vertexBuffer);
+ translated->serial = vertexBuffer->getSerial();
+ translated->baseOffset = streamOffset + firstElementOffset;
+
+ // Instanced vertices do not apply the 'start' offset
+ translated->usesFirstVertexOffset = (attrib.divisor == 0);
+
+ return gl::NoError();
+}
+
+gl::Error VertexDataManager::storeDynamicAttribs(
+ std::vector<TranslatedAttribute> *translatedAttribs,
+ const gl::AttributesMask &dynamicAttribsMask,
+ GLint start,
+ GLsizei count,
+ GLsizei instances)
+{
+ // Instantiating this class will ensure the streaming buffer is never left mapped.
+ class StreamingBufferUnmapper final : NonCopyable
+ {
+ public:
+ StreamingBufferUnmapper(StreamingVertexBufferInterface *streamingBuffer)
+ : mStreamingBuffer(streamingBuffer)
+ {
+ ASSERT(mStreamingBuffer);
+ }
+ ~StreamingBufferUnmapper() { mStreamingBuffer->getVertexBuffer()->hintUnmapResource(); }
+
+ private:
+ StreamingVertexBufferInterface *mStreamingBuffer;
+ };
+
+ // Will trigger unmapping on return.
+ StreamingBufferUnmapper localUnmapper(mStreamingBuffer);
+
+ // Reserve the required space for the dynamic buffers.
+ for (auto attribIndex : IterateBitSet(dynamicAttribsMask))
+ {
+ const auto &dynamicAttrib = (*translatedAttribs)[attribIndex];
+ ANGLE_TRY(reserveSpaceForAttrib(dynamicAttrib, count, instances));
+ }
+
+ // Store dynamic attributes
+ for (auto attribIndex : IterateBitSet(dynamicAttribsMask))
+ {
+ auto *dynamicAttrib = &(*translatedAttribs)[attribIndex];
+ ANGLE_TRY(storeDynamicAttrib(dynamicAttrib, start, count, instances));
+ }
+
+ return gl::NoError();
+}
+
+void VertexDataManager::PromoteDynamicAttribs(
+ const std::vector<TranslatedAttribute> &translatedAttribs,
+ const gl::AttributesMask &dynamicAttribsMask,
+ GLsizei count)
+{
+ for (auto attribIndex : IterateBitSet(dynamicAttribsMask))
+ {
+ const auto &dynamicAttrib = translatedAttribs[attribIndex];
+ gl::Buffer *buffer = dynamicAttrib.attribute->buffer.get();
+ if (buffer)
+ {
+ BufferD3D *bufferD3D = GetImplAs<BufferD3D>(buffer);
+ size_t typeSize = ComputeVertexAttributeTypeSize(*dynamicAttrib.attribute);
+ bufferD3D->promoteStaticUsage(count * static_cast<int>(typeSize));
+ }
+ }
+}
+
+gl::Error VertexDataManager::reserveSpaceForAttrib(const TranslatedAttribute &translatedAttrib,
+ GLsizei count,
+ GLsizei instances) const
+{
+ const gl::VertexAttribute &attrib = *translatedAttrib.attribute;
+ ASSERT(!DirectStoragePossible(attrib));
+
+ gl::Buffer *buffer = attrib.buffer.get();
+ BufferD3D *bufferD3D = buffer ? GetImplAs<BufferD3D>(buffer) : nullptr;
+ ASSERT(!bufferD3D || bufferD3D->getStaticVertexBuffer(attrib) == nullptr);
+
+ size_t totalCount = ComputeVertexAttributeElementCount(attrib, count, instances);
+ ASSERT(!bufferD3D ||
+ ElementsInBuffer(attrib, static_cast<unsigned int>(bufferD3D->getSize())) >=
+ static_cast<int>(totalCount));
+
+ return mStreamingBuffer->reserveVertexSpace(attrib, static_cast<GLsizei>(totalCount),
+ instances);
+}
+
+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)
+ {
+ ANGLE_TRY(storage->getData(&sourceData));
+ sourceData += static_cast<int>(attrib.offset);
+ }
+ else
+ {
+ sourceData = static_cast<const uint8_t*>(attrib.pointer);
+ }
+
+ unsigned int streamOffset = 0;
+
+ translated->storage = nullptr;
+ ANGLE_TRY_RESULT(mFactory->getVertexSpaceRequired(attrib, 1, 0), translated->stride);
+
+ size_t totalCount = ComputeVertexAttributeElementCount(attrib, count, instances);
+
+ ANGLE_TRY(mStreamingBuffer->storeDynamicAttribute(
+ attrib, 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 gl::NoError();
+}
+
+gl::Error VertexDataManager::storeCurrentValue(const gl::VertexAttribCurrentValueData &currentValue,
+ TranslatedAttribute *translated,
+ size_t attribIndex)
+{
+ CurrentValueState *cachedState = &mCurrentValueCache[attribIndex];
+ auto *&buffer = cachedState->buffer;
+
+ if (!buffer)
+ {
+ buffer = new StreamingVertexBufferInterface(mFactory, CONSTANT_VERTEX_BUFFER_SIZE);
+ }
+
+ if (cachedState->data != currentValue)
+ {
+ const gl::VertexAttribute &attrib = *translated->attribute;
+
+ ANGLE_TRY(buffer->reserveVertexSpace(attrib, 1, 0));
+
+ const uint8_t *sourceData = reinterpret_cast<const uint8_t*>(currentValue.FloatValues);
+ unsigned int streamOffset;
+ ANGLE_TRY(buffer->storeDynamicAttribute(attrib, currentValue.Type, 0, 1, 0, &streamOffset,
+ sourceData));
+
+ buffer->getVertexBuffer()->hintUnmapResource();
+
+ cachedState->data = currentValue;
+ cachedState->offset = streamOffset;
+ }
+
+ translated->vertexBuffer.set(buffer->getVertexBuffer());
+
+ translated->storage = nullptr;
+ translated->serial = buffer->getSerial();
+ translated->divisor = 0;
+ translated->stride = 0;
+ translated->baseOffset = static_cast<unsigned int>(cachedState->offset);
+ translated->usesFirstVertexOffset = false;
+
+ return gl::NoError();
+}
+
+// VertexBufferBinding implementation
+VertexBufferBinding::VertexBufferBinding() : mBoundVertexBuffer(nullptr)
+{
+}
+
+VertexBufferBinding::VertexBufferBinding(const VertexBufferBinding &other)
+ : mBoundVertexBuffer(other.mBoundVertexBuffer)
+{
+ if (mBoundVertexBuffer)
+ {
+ mBoundVertexBuffer->addRef();
+ }
+}
+
+VertexBufferBinding::~VertexBufferBinding()
+{
+ if (mBoundVertexBuffer)
+ {
+ mBoundVertexBuffer->release();
+ }
+}
+
+VertexBufferBinding &VertexBufferBinding::operator=(const VertexBufferBinding &other)
+{
+ mBoundVertexBuffer = other.mBoundVertexBuffer;
+ if (mBoundVertexBuffer)
+ {
+ mBoundVertexBuffer->addRef();
+ }
+ return *this;
+}
+
+void VertexBufferBinding::set(VertexBuffer *vertexBuffer)
+{
+ if (mBoundVertexBuffer == vertexBuffer)
+ return;
+
+ if (mBoundVertexBuffer)
+ {
+ mBoundVertexBuffer->release();
+ }
+ if (vertexBuffer)
+ {
+ vertexBuffer->addRef();
+ }
+
+ mBoundVertexBuffer = vertexBuffer;
+}
+
+VertexBuffer *VertexBufferBinding::get() const
+{
+ return mBoundVertexBuffer;
+}
+
+} // namespace rx