| // | |
| // Copyright 2002 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. | |
| // | |
| // VertexBuffer.cpp: Defines the abstract VertexBuffer class and VertexBufferInterface | |
| // class with derivations, classes that perform graphics API agnostic vertex buffer operations. | |
| #include "libANGLE/renderer/d3d/VertexBuffer.h" | |
| #include "common/mathutil.h" | |
| #include "libANGLE/Context.h" | |
| #include "libANGLE/VertexAttribute.h" | |
| #include "libANGLE/renderer/d3d/BufferD3D.h" | |
| #include "libANGLE/renderer/d3d/ContextD3D.h" | |
| namespace rx | |
| { | |
| // VertexBuffer Implementation | |
| unsigned int VertexBuffer::mNextSerial = 1; | |
| VertexBuffer::VertexBuffer() : mRefCount(1) | |
| { | |
| updateSerial(); | |
| } | |
| VertexBuffer::~VertexBuffer() {} | |
| void VertexBuffer::updateSerial() | |
| { | |
| mSerial = mNextSerial++; | |
| } | |
| unsigned int VertexBuffer::getSerial() const | |
| { | |
| return mSerial; | |
| } | |
| void VertexBuffer::addRef() | |
| { | |
| mRefCount++; | |
| } | |
| void VertexBuffer::release() | |
| { | |
| ASSERT(mRefCount > 0); | |
| mRefCount--; | |
| if (mRefCount == 0) | |
| { | |
| delete this; | |
| } | |
| } | |
| // VertexBufferInterface Implementation | |
| VertexBufferInterface::VertexBufferInterface(BufferFactoryD3D *factory, bool dynamic) | |
| : mFactory(factory), mVertexBuffer(factory->createVertexBuffer()), mDynamic(dynamic) | |
| {} | |
| VertexBufferInterface::~VertexBufferInterface() | |
| { | |
| if (mVertexBuffer) | |
| { | |
| mVertexBuffer->release(); | |
| mVertexBuffer = nullptr; | |
| } | |
| } | |
| unsigned int VertexBufferInterface::getSerial() const | |
| { | |
| ASSERT(mVertexBuffer); | |
| return mVertexBuffer->getSerial(); | |
| } | |
| unsigned int VertexBufferInterface::getBufferSize() const | |
| { | |
| ASSERT(mVertexBuffer); | |
| return mVertexBuffer->getBufferSize(); | |
| } | |
| angle::Result VertexBufferInterface::setBufferSize(const gl::Context *context, unsigned int size) | |
| { | |
| ASSERT(mVertexBuffer); | |
| if (mVertexBuffer->getBufferSize() == 0) | |
| { | |
| return mVertexBuffer->initialize(context, size, mDynamic); | |
| } | |
| return mVertexBuffer->setBufferSize(context, size); | |
| } | |
| angle::Result VertexBufferInterface::getSpaceRequired(const gl::Context *context, | |
| const gl::VertexAttribute &attrib, | |
| const gl::VertexBinding &binding, | |
| size_t count, | |
| GLsizei instances, | |
| unsigned int *spaceInBytesOut) const | |
| { | |
| unsigned int spaceRequired = 0; | |
| ANGLE_TRY(mFactory->getVertexSpaceRequired(context, attrib, binding, count, instances, | |
| &spaceRequired)); | |
| // Align to 16-byte boundary | |
| unsigned int alignedSpaceRequired = roundUpPow2(spaceRequired, 16u); | |
| ANGLE_CHECK_GL_ALLOC(GetImplAs<ContextD3D>(context), alignedSpaceRequired >= spaceRequired); | |
| *spaceInBytesOut = alignedSpaceRequired; | |
| return angle::Result::Continue; | |
| } | |
| angle::Result VertexBufferInterface::discard(const gl::Context *context) | |
| { | |
| ASSERT(mVertexBuffer); | |
| return mVertexBuffer->discard(context); | |
| } | |
| VertexBuffer *VertexBufferInterface::getVertexBuffer() const | |
| { | |
| ASSERT(mVertexBuffer); | |
| return mVertexBuffer; | |
| } | |
| // StreamingVertexBufferInterface Implementation | |
| StreamingVertexBufferInterface::StreamingVertexBufferInterface(BufferFactoryD3D *factory) | |
| : VertexBufferInterface(factory, true), mWritePosition(0), mReservedSpace(0) | |
| {} | |
| angle::Result StreamingVertexBufferInterface::initialize(const gl::Context *context, | |
| std::size_t initialSize) | |
| { | |
| return setBufferSize(context, static_cast<unsigned int>(initialSize)); | |
| } | |
| void StreamingVertexBufferInterface::reset() | |
| { | |
| if (mVertexBuffer) | |
| { | |
| mVertexBuffer->release(); | |
| mVertexBuffer = mFactory->createVertexBuffer(); | |
| } | |
| } | |
| StreamingVertexBufferInterface::~StreamingVertexBufferInterface() {} | |
| angle::Result StreamingVertexBufferInterface::reserveSpace(const gl::Context *context, | |
| unsigned int size) | |
| { | |
| unsigned int curBufferSize = getBufferSize(); | |
| if (size > curBufferSize) | |
| { | |
| ANGLE_TRY(setBufferSize(context, std::max(size, 3 * curBufferSize / 2))); | |
| mWritePosition = 0; | |
| } | |
| else if (mWritePosition + size > curBufferSize) | |
| { | |
| ANGLE_TRY(discard(context)); | |
| mWritePosition = 0; | |
| } | |
| mReservedSpace = size; | |
| return angle::Result::Continue; | |
| } | |
| angle::Result StreamingVertexBufferInterface::storeDynamicAttribute( | |
| const gl::Context *context, | |
| const gl::VertexAttribute &attrib, | |
| const gl::VertexBinding &binding, | |
| gl::VertexAttribType currentValueType, | |
| GLint start, | |
| size_t count, | |
| GLsizei instances, | |
| unsigned int *outStreamOffset, | |
| const uint8_t *sourceData) | |
| { | |
| unsigned int spaceRequired = 0; | |
| ANGLE_TRY(getSpaceRequired(context, attrib, binding, count, instances, &spaceRequired)); | |
| // Protect against integer overflow | |
| angle::CheckedNumeric<unsigned int> checkedPosition(mWritePosition); | |
| checkedPosition += spaceRequired; | |
| ANGLE_CHECK_GL_ALLOC(GetImplAs<ContextD3D>(context), checkedPosition.IsValid()); | |
| mReservedSpace = 0; | |
| ANGLE_TRY(mVertexBuffer->storeVertexAttributes(context, attrib, binding, currentValueType, | |
| start, count, instances, mWritePosition, | |
| sourceData)); | |
| if (outStreamOffset) | |
| { | |
| *outStreamOffset = mWritePosition; | |
| } | |
| mWritePosition += spaceRequired; | |
| return angle::Result::Continue; | |
| } | |
| angle::Result StreamingVertexBufferInterface::reserveVertexSpace(const gl::Context *context, | |
| const gl::VertexAttribute &attrib, | |
| const gl::VertexBinding &binding, | |
| size_t count, | |
| GLsizei instances) | |
| { | |
| unsigned int requiredSpace = 0; | |
| ANGLE_TRY(mFactory->getVertexSpaceRequired(context, attrib, binding, count, instances, | |
| &requiredSpace)); | |
| // Align to 16-byte boundary | |
| auto alignedRequiredSpace = rx::CheckedRoundUp(requiredSpace, 16u); | |
| alignedRequiredSpace += mReservedSpace; | |
| // Protect against integer overflow | |
| ANGLE_CHECK_GL_ALLOC(GetImplAs<ContextD3D>(context), alignedRequiredSpace.IsValid()); | |
| ANGLE_TRY(reserveSpace(context, alignedRequiredSpace.ValueOrDie())); | |
| return angle::Result::Continue; | |
| } | |
| // StaticVertexBufferInterface Implementation | |
| StaticVertexBufferInterface::AttributeSignature::AttributeSignature() | |
| : formatID(angle::FormatID::NONE), stride(0), offset(0) | |
| {} | |
| bool StaticVertexBufferInterface::AttributeSignature::matchesAttribute( | |
| const gl::VertexAttribute &attrib, | |
| const gl::VertexBinding &binding) const | |
| { | |
| size_t attribStride = ComputeVertexAttributeStride(attrib, binding); | |
| if (formatID != attrib.format->id || static_cast<GLuint>(stride) != attribStride) | |
| { | |
| return false; | |
| } | |
| size_t attribOffset = | |
| (static_cast<size_t>(ComputeVertexAttributeOffset(attrib, binding)) % attribStride); | |
| return (offset == attribOffset); | |
| } | |
| void StaticVertexBufferInterface::AttributeSignature::set(const gl::VertexAttribute &attrib, | |
| const gl::VertexBinding &binding) | |
| { | |
| formatID = attrib.format->id; | |
| offset = stride = static_cast<GLuint>(ComputeVertexAttributeStride(attrib, binding)); | |
| offset = static_cast<size_t>(ComputeVertexAttributeOffset(attrib, binding)) % | |
| ComputeVertexAttributeStride(attrib, binding); | |
| } | |
| StaticVertexBufferInterface::StaticVertexBufferInterface(BufferFactoryD3D *factory) | |
| : VertexBufferInterface(factory, false) | |
| {} | |
| StaticVertexBufferInterface::~StaticVertexBufferInterface() {} | |
| bool StaticVertexBufferInterface::matchesAttribute(const gl::VertexAttribute &attrib, | |
| const gl::VertexBinding &binding) const | |
| { | |
| return mSignature.matchesAttribute(attrib, binding); | |
| } | |
| void StaticVertexBufferInterface::setAttribute(const gl::VertexAttribute &attrib, | |
| const gl::VertexBinding &binding) | |
| { | |
| return mSignature.set(attrib, binding); | |
| } | |
| angle::Result StaticVertexBufferInterface::storeStaticAttribute(const gl::Context *context, | |
| const gl::VertexAttribute &attrib, | |
| const gl::VertexBinding &binding, | |
| GLint start, | |
| GLsizei count, | |
| GLsizei instances, | |
| const uint8_t *sourceData) | |
| { | |
| unsigned int spaceRequired = 0; | |
| ANGLE_TRY(getSpaceRequired(context, attrib, binding, count, instances, &spaceRequired)); | |
| ANGLE_TRY(setBufferSize(context, spaceRequired)); | |
| ASSERT(attrib.enabled); | |
| ANGLE_TRY(mVertexBuffer->storeVertexAttributes(context, attrib, binding, | |
| gl::VertexAttribType::InvalidEnum, start, count, | |
| instances, 0, sourceData)); | |
| mSignature.set(attrib, binding); | |
| mVertexBuffer->hintUnmapResource(); | |
| return angle::Result::Continue; | |
| } | |
| } // namespace rx |