//
// 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.
//

// TransformFeedbackD3D.cpp is a no-op implementation for both the D3D9 and D3D11 renderers.

#include "libANGLE/renderer/d3d/d3d11/TransformFeedback11.h"

#include "libANGLE/Buffer.h"
#include "libANGLE/renderer/d3d/d3d11/Buffer11.h"
#include "libANGLE/renderer/d3d/d3d11/Renderer11.h"

namespace rx
{

TransformFeedback11::TransformFeedback11(const gl::TransformFeedbackState &state,
                                         Renderer11 *renderer)
    : TransformFeedbackImpl(state),
      mRenderer(renderer),
      mIsDirty(true),
      mBuffers(state.getIndexedBuffers().size(), nullptr),
      mBufferOffsets(state.getIndexedBuffers().size(), 0),
      mSerial(mRenderer->generateSerial())
{}

TransformFeedback11::~TransformFeedback11() {}

angle::Result TransformFeedback11::begin(const gl::Context *context,
                                         gl::PrimitiveMode primitiveMode)
{
    // Reset all the cached offsets to the binding offsets
    mIsDirty = true;
    for (size_t bindingIdx = 0; bindingIdx < mBuffers.size(); bindingIdx++)
    {
        const auto &binding = mState.getIndexedBuffer(bindingIdx);
        if (binding.get() != nullptr)
        {
            mBufferOffsets[bindingIdx] = static_cast<UINT>(binding.getOffset());
        }
        else
        {
            mBufferOffsets[bindingIdx] = 0;
        }
    }
    mRenderer->getStateManager()->invalidateTransformFeedback();
    return angle::Result::Continue;
}

angle::Result TransformFeedback11::end(const gl::Context *context)
{
    mRenderer->getStateManager()->invalidateTransformFeedback();
    if (mRenderer->getWorkarounds().flushAfterEndingTransformFeedback.enabled)
    {
        mRenderer->getDeviceContext()->Flush();
    }
    return angle::Result::Continue;
}

angle::Result TransformFeedback11::pause(const gl::Context *context)
{
    mRenderer->getStateManager()->invalidateTransformFeedback();
    return angle::Result::Continue;
}

angle::Result TransformFeedback11::resume(const gl::Context *context)
{
    mRenderer->getStateManager()->invalidateTransformFeedback();
    return angle::Result::Continue;
}

angle::Result TransformFeedback11::bindIndexedBuffer(
    const gl::Context *context,
    size_t index,
    const gl::OffsetBindingPointer<gl::Buffer> &binding)
{
    mIsDirty              = true;
    mBufferOffsets[index] = static_cast<UINT>(binding.getOffset());
    mRenderer->getStateManager()->invalidateTransformFeedback();
    return angle::Result::Continue;
}

void TransformFeedback11::onApply()
{
    mIsDirty = false;

    // Change all buffer offsets to -1 so that if any of them need to be re-applied, the are set to
    // append
    std::fill(mBufferOffsets.begin(), mBufferOffsets.end(), -1);
}

bool TransformFeedback11::isDirty() const
{
    return mIsDirty;
}

UINT TransformFeedback11::getNumSOBuffers() const
{
    return static_cast<UINT>(mBuffers.size());
}

angle::Result TransformFeedback11::getSOBuffers(const gl::Context *context,
                                                const std::vector<ID3D11Buffer *> **buffersOut)
{
    for (size_t bindingIdx = 0; bindingIdx < mBuffers.size(); bindingIdx++)
    {
        const auto &binding = mState.getIndexedBuffer(bindingIdx);
        if (binding.get() != nullptr)
        {
            Buffer11 *storage = GetImplAs<Buffer11>(binding.get());
            ANGLE_TRY(storage->getBuffer(context, BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK,
                                         &mBuffers[bindingIdx]));
        }
    }

    *buffersOut = &mBuffers;
    return angle::Result::Continue;
}

const std::vector<UINT> &TransformFeedback11::getSOBufferOffsets() const
{
    return mBufferOffsets;
}

Serial TransformFeedback11::getSerial() const
{
    return mSerial;
}

}  // namespace rx
