tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TransformFeedback.cpp (10571B)


      1 //
      2 // Copyright 2014 The ANGLE Project Authors. All rights reserved.
      3 // Use of this source code is governed by a BSD-style license that can be
      4 // found in the LICENSE file.
      5 //
      6 
      7 #include "libANGLE/TransformFeedback.h"
      8 
      9 #include "common/mathutil.h"
     10 #include "libANGLE/Buffer.h"
     11 #include "libANGLE/Caps.h"
     12 #include "libANGLE/Context.h"
     13 #include "libANGLE/Program.h"
     14 #include "libANGLE/State.h"
     15 #include "libANGLE/renderer/GLImplFactory.h"
     16 #include "libANGLE/renderer/TransformFeedbackImpl.h"
     17 
     18 #include <limits>
     19 
     20 namespace gl
     21 {
     22 
     23 angle::CheckedNumeric<GLsizeiptr> GetVerticesNeededForDraw(PrimitiveMode primitiveMode,
     24                                                           GLsizei count,
     25                                                           GLsizei primcount)
     26 {
     27    if (count < 0 || primcount < 0)
     28    {
     29        return 0;
     30    }
     31    // Transform feedback only outputs complete primitives, so we need to round down to the nearest
     32    // complete primitive before multiplying by the number of instances.
     33    angle::CheckedNumeric<GLsizeiptr> checkedCount     = count;
     34    angle::CheckedNumeric<GLsizeiptr> checkedPrimcount = primcount;
     35    switch (primitiveMode)
     36    {
     37        case PrimitiveMode::Triangles:
     38            return checkedPrimcount * (checkedCount - checkedCount % 3);
     39        case PrimitiveMode::Lines:
     40            return checkedPrimcount * (checkedCount - checkedCount % 2);
     41        case PrimitiveMode::Points:
     42            return checkedPrimcount * checkedCount;
     43        default:
     44            UNREACHABLE();
     45            return checkedPrimcount * checkedCount;
     46    }
     47 }
     48 
     49 TransformFeedbackState::TransformFeedbackState(size_t maxIndexedBuffers)
     50    : mLabel(),
     51      mActive(false),
     52      mPrimitiveMode(PrimitiveMode::InvalidEnum),
     53      mPaused(false),
     54      mVerticesDrawn(0),
     55      mVertexCapacity(0),
     56      mProgram(nullptr),
     57      mIndexedBuffers(maxIndexedBuffers)
     58 {}
     59 
     60 TransformFeedbackState::~TransformFeedbackState() {}
     61 
     62 const OffsetBindingPointer<Buffer> &TransformFeedbackState::getIndexedBuffer(size_t idx) const
     63 {
     64    return mIndexedBuffers[idx];
     65 }
     66 
     67 const std::vector<OffsetBindingPointer<Buffer>> &TransformFeedbackState::getIndexedBuffers() const
     68 {
     69    return mIndexedBuffers;
     70 }
     71 
     72 GLsizeiptr TransformFeedbackState::getPrimitivesDrawn() const
     73 {
     74    switch (mPrimitiveMode)
     75    {
     76        case gl::PrimitiveMode::Points:
     77            return mVerticesDrawn;
     78        case gl::PrimitiveMode::Lines:
     79            return mVerticesDrawn / 2;
     80        case gl::PrimitiveMode::Triangles:
     81            return mVerticesDrawn / 3;
     82        default:
     83            return 0;
     84    }
     85 }
     86 
     87 TransformFeedback::TransformFeedback(rx::GLImplFactory *implFactory,
     88                                     TransformFeedbackID id,
     89                                     const Caps &caps)
     90    : RefCountObject(implFactory->generateSerial(), id),
     91      mState(caps.maxTransformFeedbackSeparateAttributes),
     92      mImplementation(implFactory->createTransformFeedback(mState))
     93 {
     94    ASSERT(mImplementation != nullptr);
     95 }
     96 
     97 void TransformFeedback::onDestroy(const Context *context)
     98 {
     99    ASSERT(!context || !context->isCurrentTransformFeedback(this));
    100    if (mState.mProgram)
    101    {
    102        mState.mProgram->release(context);
    103        mState.mProgram = nullptr;
    104    }
    105 
    106    ASSERT(!mState.mProgram);
    107    for (size_t i = 0; i < mState.mIndexedBuffers.size(); i++)
    108    {
    109        mState.mIndexedBuffers[i].set(context, nullptr, 0, 0);
    110    }
    111 
    112    if (mImplementation)
    113    {
    114        mImplementation->onDestroy(context);
    115    }
    116 }
    117 
    118 TransformFeedback::~TransformFeedback()
    119 {
    120    SafeDelete(mImplementation);
    121 }
    122 
    123 angle::Result TransformFeedback::setLabel(const Context *context, const std::string &label)
    124 {
    125    mState.mLabel = label;
    126 
    127    if (mImplementation)
    128    {
    129        return mImplementation->onLabelUpdate(context);
    130    }
    131    return angle::Result::Continue;
    132 }
    133 
    134 const std::string &TransformFeedback::getLabel() const
    135 {
    136    return mState.mLabel;
    137 }
    138 
    139 angle::Result TransformFeedback::begin(const Context *context,
    140                                       PrimitiveMode primitiveMode,
    141                                       Program *program)
    142 {
    143    // TODO: http://anglebug.com/5486: This method should take in as parameter a
    144    // ProgramExecutable instead of a Program.
    145 
    146    ANGLE_TRY(mImplementation->begin(context, primitiveMode));
    147    mState.mActive        = true;
    148    mState.mPrimitiveMode = primitiveMode;
    149    mState.mPaused        = false;
    150    mState.mVerticesDrawn = 0;
    151    bindProgram(context, program);
    152 
    153    // In one of the angle_unittests - "TransformFeedbackTest.SideEffectsOfStartAndStop"
    154    // there is a code path where <context> is a nullptr, account for that possiblity.
    155    const ProgramExecutable *programExecutable =
    156        context ? context->getState().getLinkedProgramExecutable(context) : nullptr;
    157    if (programExecutable)
    158    {
    159        // Compute the number of vertices we can draw before overflowing the bound buffers.
    160        auto strides = programExecutable->getTransformFeedbackStrides();
    161        ASSERT(strides.size() <= mState.mIndexedBuffers.size() && !strides.empty());
    162        GLsizeiptr minCapacity = std::numeric_limits<GLsizeiptr>::max();
    163        for (size_t index = 0; index < strides.size(); index++)
    164        {
    165            GLsizeiptr capacity =
    166                GetBoundBufferAvailableSize(mState.mIndexedBuffers[index]) / strides[index];
    167            minCapacity = std::min(minCapacity, capacity);
    168        }
    169        mState.mVertexCapacity = minCapacity;
    170    }
    171    else
    172    {
    173        mState.mVertexCapacity = 0;
    174    }
    175    return angle::Result::Continue;
    176 }
    177 
    178 angle::Result TransformFeedback::end(const Context *context)
    179 {
    180    ANGLE_TRY(mImplementation->end(context));
    181    mState.mActive         = false;
    182    mState.mPrimitiveMode  = PrimitiveMode::InvalidEnum;
    183    mState.mPaused         = false;
    184    mState.mVerticesDrawn  = 0;
    185    mState.mVertexCapacity = 0;
    186    if (mState.mProgram)
    187    {
    188        mState.mProgram->release(context);
    189        mState.mProgram = nullptr;
    190    }
    191    return angle::Result::Continue;
    192 }
    193 
    194 angle::Result TransformFeedback::pause(const Context *context)
    195 {
    196    ANGLE_TRY(mImplementation->pause(context));
    197    mState.mPaused = true;
    198    return angle::Result::Continue;
    199 }
    200 
    201 angle::Result TransformFeedback::resume(const Context *context)
    202 {
    203    ANGLE_TRY(mImplementation->resume(context));
    204    mState.mPaused = false;
    205    return angle::Result::Continue;
    206 }
    207 
    208 bool TransformFeedback::isPaused() const
    209 {
    210    return mState.mPaused;
    211 }
    212 
    213 PrimitiveMode TransformFeedback::getPrimitiveMode() const
    214 {
    215    return mState.mPrimitiveMode;
    216 }
    217 
    218 bool TransformFeedback::checkBufferSpaceForDraw(GLsizei count, GLsizei primcount) const
    219 {
    220    auto vertices =
    221        mState.mVerticesDrawn + GetVerticesNeededForDraw(mState.mPrimitiveMode, count, primcount);
    222    return vertices.IsValid() && vertices.ValueOrDie() <= mState.mVertexCapacity;
    223 }
    224 
    225 void TransformFeedback::onVerticesDrawn(const Context *context, GLsizei count, GLsizei primcount)
    226 {
    227    ASSERT(mState.mActive && !mState.mPaused);
    228    // All draws should be validated with checkBufferSpaceForDraw so ValueOrDie should never fail.
    229    mState.mVerticesDrawn =
    230        (mState.mVerticesDrawn + GetVerticesNeededForDraw(mState.mPrimitiveMode, count, primcount))
    231            .ValueOrDie();
    232 
    233    for (auto &buffer : mState.mIndexedBuffers)
    234    {
    235        if (buffer.get() != nullptr)
    236        {
    237            buffer->onDataChanged();
    238        }
    239    }
    240 }
    241 
    242 void TransformFeedback::bindProgram(const Context *context, Program *program)
    243 {
    244    if (mState.mProgram != program)
    245    {
    246        if (mState.mProgram != nullptr)
    247        {
    248            mState.mProgram->release(context);
    249        }
    250        mState.mProgram = program;
    251        if (mState.mProgram != nullptr)
    252        {
    253            mState.mProgram->addRef();
    254        }
    255    }
    256 }
    257 
    258 bool TransformFeedback::hasBoundProgram(ShaderProgramID program) const
    259 {
    260    return mState.mProgram != nullptr && mState.mProgram->id().value == program.value;
    261 }
    262 
    263 angle::Result TransformFeedback::detachBuffer(const Context *context, BufferID bufferID)
    264 {
    265    bool isBound = context->isCurrentTransformFeedback(this);
    266    for (size_t index = 0; index < mState.mIndexedBuffers.size(); index++)
    267    {
    268        if (mState.mIndexedBuffers[index].id() == bufferID)
    269        {
    270            if (isBound)
    271            {
    272                mState.mIndexedBuffers[index]->onTFBindingChanged(context, false, true);
    273            }
    274            mState.mIndexedBuffers[index].set(context, nullptr, 0, 0);
    275            ANGLE_TRY(
    276                mImplementation->bindIndexedBuffer(context, index, mState.mIndexedBuffers[index]));
    277        }
    278    }
    279 
    280    return angle::Result::Continue;
    281 }
    282 
    283 angle::Result TransformFeedback::bindIndexedBuffer(const Context *context,
    284                                                   size_t index,
    285                                                   Buffer *buffer,
    286                                                   size_t offset,
    287                                                   size_t size)
    288 {
    289    ASSERT(index < mState.mIndexedBuffers.size());
    290    bool isBound = context && context->isCurrentTransformFeedback(this);
    291    if (isBound && mState.mIndexedBuffers[index].get())
    292    {
    293        mState.mIndexedBuffers[index]->onTFBindingChanged(context, false, true);
    294    }
    295    mState.mIndexedBuffers[index].set(context, buffer, offset, size);
    296    if (isBound && buffer)
    297    {
    298        buffer->onTFBindingChanged(context, true, true);
    299    }
    300 
    301    return mImplementation->bindIndexedBuffer(context, index, mState.mIndexedBuffers[index]);
    302 }
    303 
    304 const OffsetBindingPointer<Buffer> &TransformFeedback::getIndexedBuffer(size_t index) const
    305 {
    306    ASSERT(index < mState.mIndexedBuffers.size());
    307    return mState.mIndexedBuffers[index];
    308 }
    309 
    310 size_t TransformFeedback::getIndexedBufferCount() const
    311 {
    312    return mState.mIndexedBuffers.size();
    313 }
    314 
    315 bool TransformFeedback::buffersBoundForOtherUseInWebGL() const
    316 {
    317    for (auto &buffer : mState.mIndexedBuffers)
    318    {
    319        if (buffer.get() && buffer->hasWebGLXFBBindingConflict(true))
    320        {
    321            return true;
    322        }
    323    }
    324    return false;
    325 }
    326 
    327 rx::TransformFeedbackImpl *TransformFeedback::getImplementation() const
    328 {
    329    return mImplementation;
    330 }
    331 
    332 void TransformFeedback::onBindingChanged(const Context *context, bool bound)
    333 {
    334    for (auto &buffer : mState.mIndexedBuffers)
    335    {
    336        if (buffer.get())
    337        {
    338            buffer->onTFBindingChanged(context, bound, true);
    339        }
    340    }
    341 }
    342 
    343 const std::vector<OffsetBindingPointer<Buffer>> &TransformFeedback::getIndexedBuffers() const
    344 {
    345    return mState.mIndexedBuffers;
    346 }
    347 }  // namespace gl