diff options
Diffstat (limited to 'gfx/angle/checkout/src/libANGLE/TransformFeedback.cpp')
-rw-r--r-- | gfx/angle/checkout/src/libANGLE/TransformFeedback.cpp | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/libANGLE/TransformFeedback.cpp b/gfx/angle/checkout/src/libANGLE/TransformFeedback.cpp new file mode 100644 index 0000000000..3222f14b8f --- /dev/null +++ b/gfx/angle/checkout/src/libANGLE/TransformFeedback.cpp @@ -0,0 +1,322 @@ +// +// Copyright (c) 2014 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. +// + +#include "libANGLE/TransformFeedback.h" + +#include "common/mathutil.h" +#include "libANGLE/Buffer.h" +#include "libANGLE/Caps.h" +#include "libANGLE/Context.h" +#include "libANGLE/Program.h" +#include "libANGLE/State.h" +#include "libANGLE/renderer/GLImplFactory.h" +#include "libANGLE/renderer/TransformFeedbackImpl.h" + +#include <limits> + +namespace gl +{ + +angle::CheckedNumeric<GLsizeiptr> GetVerticesNeededForDraw(PrimitiveMode primitiveMode, + GLsizei count, + GLsizei primcount) +{ + if (count < 0 || primcount < 0) + { + return 0; + } + // Transform feedback only outputs complete primitives, so we need to round down to the nearest + // complete primitive before multiplying by the number of instances. + angle::CheckedNumeric<GLsizeiptr> checkedCount = count; + angle::CheckedNumeric<GLsizeiptr> checkedPrimcount = primcount; + switch (primitiveMode) + { + case PrimitiveMode::Triangles: + return checkedPrimcount * (checkedCount - checkedCount % 3); + case PrimitiveMode::Lines: + return checkedPrimcount * (checkedCount - checkedCount % 2); + case PrimitiveMode::Points: + return checkedPrimcount * checkedCount; + default: + UNREACHABLE(); + return checkedPrimcount * checkedCount; + } +} + +TransformFeedbackState::TransformFeedbackState(size_t maxIndexedBuffers) + : mLabel(), + mActive(false), + mPrimitiveMode(PrimitiveMode::InvalidEnum), + mPaused(false), + mVerticesDrawn(0), + mVertexCapacity(0), + mProgram(nullptr), + mIndexedBuffers(maxIndexedBuffers) +{} + +TransformFeedbackState::~TransformFeedbackState() {} + +const OffsetBindingPointer<Buffer> &TransformFeedbackState::getIndexedBuffer(size_t idx) const +{ + return mIndexedBuffers[idx]; +} + +const std::vector<OffsetBindingPointer<Buffer>> &TransformFeedbackState::getIndexedBuffers() const +{ + return mIndexedBuffers; +} + +GLsizeiptr TransformFeedbackState::getPrimitivesDrawn() const +{ + switch (mPrimitiveMode) + { + case gl::PrimitiveMode::Points: + return mVerticesDrawn; + case gl::PrimitiveMode::Lines: + return mVerticesDrawn / 2; + case gl::PrimitiveMode::Triangles: + return mVerticesDrawn / 3; + default: + return 0; + } +} + +TransformFeedback::TransformFeedback(rx::GLImplFactory *implFactory, GLuint id, const Caps &caps) + : RefCountObject(id), + mState(caps.maxTransformFeedbackSeparateAttributes), + mImplementation(implFactory->createTransformFeedback(mState)) +{ + ASSERT(mImplementation != nullptr); +} + +void TransformFeedback::onDestroy(const Context *context) +{ + ASSERT(!context || !context->isCurrentTransformFeedback(this)); + if (mState.mProgram) + { + mState.mProgram->release(context); + mState.mProgram = nullptr; + } + + ASSERT(!mState.mProgram); + for (size_t i = 0; i < mState.mIndexedBuffers.size(); i++) + { + mState.mIndexedBuffers[i].set(context, nullptr, 0, 0); + } +} + +TransformFeedback::~TransformFeedback() +{ + SafeDelete(mImplementation); +} + +void TransformFeedback::setLabel(const Context *context, const std::string &label) +{ + mState.mLabel = label; +} + +const std::string &TransformFeedback::getLabel() const +{ + return mState.mLabel; +} + +angle::Result TransformFeedback::begin(const Context *context, + PrimitiveMode primitiveMode, + Program *program) +{ + ANGLE_TRY(mImplementation->begin(context, primitiveMode)); + mState.mActive = true; + mState.mPrimitiveMode = primitiveMode; + mState.mPaused = false; + mState.mVerticesDrawn = 0; + bindProgram(context, program); + + if (program) + { + // Compute the number of vertices we can draw before overflowing the bound buffers. + auto strides = program->getTransformFeedbackStrides(); + ASSERT(strides.size() <= mState.mIndexedBuffers.size() && !strides.empty()); + GLsizeiptr minCapacity = std::numeric_limits<GLsizeiptr>::max(); + for (size_t index = 0; index < strides.size(); index++) + { + GLsizeiptr capacity = + GetBoundBufferAvailableSize(mState.mIndexedBuffers[index]) / strides[index]; + minCapacity = std::min(minCapacity, capacity); + } + mState.mVertexCapacity = minCapacity; + } + else + { + mState.mVertexCapacity = 0; + } + return angle::Result::Continue; +} + +angle::Result TransformFeedback::end(const Context *context) +{ + ANGLE_TRY(mImplementation->end(context)); + mState.mActive = false; + mState.mPrimitiveMode = PrimitiveMode::InvalidEnum; + mState.mPaused = false; + mState.mVerticesDrawn = 0; + mState.mVertexCapacity = 0; + if (mState.mProgram) + { + mState.mProgram->release(context); + mState.mProgram = nullptr; + } + return angle::Result::Continue; +} + +angle::Result TransformFeedback::pause(const Context *context) +{ + ANGLE_TRY(mImplementation->pause(context)); + mState.mPaused = true; + return angle::Result::Continue; +} + +angle::Result TransformFeedback::resume(const Context *context) +{ + ANGLE_TRY(mImplementation->resume(context)); + mState.mPaused = false; + return angle::Result::Continue; +} + +bool TransformFeedback::isPaused() const +{ + return mState.mPaused; +} + +PrimitiveMode TransformFeedback::getPrimitiveMode() const +{ + return mState.mPrimitiveMode; +} + +bool TransformFeedback::checkBufferSpaceForDraw(GLsizei count, GLsizei primcount) const +{ + auto vertices = + mState.mVerticesDrawn + GetVerticesNeededForDraw(mState.mPrimitiveMode, count, primcount); + return vertices.IsValid() && vertices.ValueOrDie() <= mState.mVertexCapacity; +} + +void TransformFeedback::onVerticesDrawn(const Context *context, GLsizei count, GLsizei primcount) +{ + ASSERT(mState.mActive && !mState.mPaused); + // All draws should be validated with checkBufferSpaceForDraw so ValueOrDie should never fail. + mState.mVerticesDrawn = + (mState.mVerticesDrawn + GetVerticesNeededForDraw(mState.mPrimitiveMode, count, primcount)) + .ValueOrDie(); + + for (auto &buffer : mState.mIndexedBuffers) + { + if (buffer.get() != nullptr) + { + buffer->onTransformFeedback(); + } + } +} + +void TransformFeedback::bindProgram(const Context *context, Program *program) +{ + if (mState.mProgram != program) + { + if (mState.mProgram != nullptr) + { + mState.mProgram->release(context); + } + mState.mProgram = program; + if (mState.mProgram != nullptr) + { + mState.mProgram->addRef(); + } + } +} + +bool TransformFeedback::hasBoundProgram(GLuint program) const +{ + return mState.mProgram != nullptr && mState.mProgram->id() == program; +} + +angle::Result TransformFeedback::detachBuffer(const Context *context, GLuint bufferName) +{ + bool isBound = context->isCurrentTransformFeedback(this); + for (size_t index = 0; index < mState.mIndexedBuffers.size(); index++) + { + if (mState.mIndexedBuffers[index].id() == bufferName) + { + if (isBound) + { + mState.mIndexedBuffers[index]->onTFBindingChanged(context, false, true); + } + mState.mIndexedBuffers[index].set(context, nullptr, 0, 0); + ANGLE_TRY( + mImplementation->bindIndexedBuffer(context, index, mState.mIndexedBuffers[index])); + } + } + + return angle::Result::Continue; +} + +angle::Result TransformFeedback::bindIndexedBuffer(const Context *context, + size_t index, + Buffer *buffer, + size_t offset, + size_t size) +{ + ASSERT(index < mState.mIndexedBuffers.size()); + bool isBound = context && context->isCurrentTransformFeedback(this); + if (isBound && mState.mIndexedBuffers[index].get()) + { + mState.mIndexedBuffers[index]->onTFBindingChanged(context, false, true); + } + mState.mIndexedBuffers[index].set(context, buffer, offset, size); + if (isBound && buffer) + { + buffer->onTFBindingChanged(context, true, true); + } + + return mImplementation->bindIndexedBuffer(context, index, mState.mIndexedBuffers[index]); +} + +const OffsetBindingPointer<Buffer> &TransformFeedback::getIndexedBuffer(size_t index) const +{ + ASSERT(index < mState.mIndexedBuffers.size()); + return mState.mIndexedBuffers[index]; +} + +size_t TransformFeedback::getIndexedBufferCount() const +{ + return mState.mIndexedBuffers.size(); +} + +bool TransformFeedback::buffersBoundForOtherUse() const +{ + for (auto &buffer : mState.mIndexedBuffers) + { + if (buffer.get() && buffer->isBoundForTransformFeedbackAndOtherUse()) + { + return true; + } + } + return false; +} + +rx::TransformFeedbackImpl *TransformFeedback::getImplementation() const +{ + return mImplementation; +} + +void TransformFeedback::onBindingChanged(const Context *context, bool bound) +{ + for (auto &buffer : mState.mIndexedBuffers) + { + if (buffer.get()) + { + buffer->onTFBindingChanged(context, bound, true); + } + } +} +} // namespace gl |