Edit

kc3-lang/angle/src/libANGLE/TransformFeedback.cpp

Branch :

  • Show log

    Commit

  • Author : Jamie Madill
    Date : 2019-09-21 14:10:35
    Hash : f703443b
    Message : Use Resource IDs in RefCountObject. This lets us use strongly typed IDs pretty much everywhere. Only one or two additional places still use GLuint IDs. Mostly for external queries and for Framebuffer Attachments. With some clever type reflection helpers lets us define a single template function for handling operator== and != for resource IDs. Refactor in preparation for more Capture/Replay work. Bug: angleproject:3611 Change-Id: I1c0c848e89eb8a4b769714d57686f816daf01634 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1815550 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: Tobin Ehlis <tobine@google.com>

  • src/libANGLE/TransformFeedback.cpp
  • //
    // Copyright 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,
                                         TransformFeedbackID 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->onDataChanged();
            }
        }
    }
    
    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(ShaderProgramID program) const
    {
        return mState.mProgram != nullptr && mState.mProgram->id().value == program.value;
    }
    
    angle::Result TransformFeedback::detachBuffer(const Context *context, BufferID bufferID)
    {
        bool isBound = context->isCurrentTransformFeedback(this);
        for (size_t index = 0; index < mState.mIndexedBuffers.size(); index++)
        {
            if (mState.mIndexedBuffers[index].id() == bufferID)
            {
                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