// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "gpu/command_buffer/service/vertex_attrib_manager.h"

#include <stdint.h>

#include "gpu/command_buffer/service/buffer_manager.h"
#include "gpu/command_buffer/service/error_state_mock.h"
#include "gpu/command_buffer/service/feature_info.h"
#include "gpu/command_buffer/service/gpu_service_test.h"
#include "gpu/command_buffer/service/test_helper.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/gl_mock.h"

using ::testing::Pointee;
using ::testing::_;

namespace gpu {
namespace gles2 {

class VertexAttribManagerTest : public GpuServiceTest {
 public:
  static const uint32_t kNumVertexAttribs = 8;

  VertexAttribManagerTest() = default;

  ~VertexAttribManagerTest() override = default;

 protected:
  void SetUp() override {
    GpuServiceTest::SetUp();

    manager_ = base::MakeRefCounted<VertexAttribManager>(false);
    manager_->Initialize(kNumVertexAttribs);
    manager_->SetIsBound(true);
  }

  scoped_refptr<VertexAttribManager> manager_;
};

// GCC requires these declarations, but MSVC requires they not be present
#ifndef COMPILER_MSVC
const uint32_t VertexAttribManagerTest::kNumVertexAttribs;
#endif

TEST_F(VertexAttribManagerTest, Basic) {
  EXPECT_TRUE(manager_->GetVertexAttrib(kNumVertexAttribs) == nullptr);
  EXPECT_FALSE(manager_->HaveFixedAttribs());

  const VertexAttribManager::VertexAttribList& enabled_attribs =
      manager_->GetEnabledVertexAttribs();
  EXPECT_EQ(0u, enabled_attribs.size());

  for (uint32_t ii = 0; ii < kNumVertexAttribs; ii += kNumVertexAttribs - 1) {
    VertexAttrib* attrib = manager_->GetVertexAttrib(ii);
    ASSERT_TRUE(attrib != nullptr);
    EXPECT_EQ(ii, attrib->index());
    EXPECT_TRUE(attrib->buffer() == nullptr);
    EXPECT_EQ(0, attrib->offset());
    EXPECT_EQ(4, attrib->size());
    EXPECT_EQ(static_cast<GLenum>(GL_FLOAT), attrib->type());
    EXPECT_EQ(GL_FALSE, attrib->normalized());
    EXPECT_EQ(0, attrib->gl_stride());
    EXPECT_FALSE(attrib->enabled());
    manager_->Enable(ii, true);
    EXPECT_TRUE(attrib->enabled());
  }
}

TEST_F(VertexAttribManagerTest, Enable) {
  const VertexAttribManager::VertexAttribList& enabled_attribs =
      manager_->GetEnabledVertexAttribs();

  VertexAttrib* attrib1 = manager_->GetVertexAttrib(1);
  VertexAttrib* attrib2 = manager_->GetVertexAttrib(3);

  manager_->Enable(1, true);
  ASSERT_EQ(1u, enabled_attribs.size());
  EXPECT_TRUE(attrib1->enabled());
  manager_->Enable(3, true);
  ASSERT_EQ(2u, enabled_attribs.size());
  EXPECT_TRUE(attrib2->enabled());

  manager_->Enable(1, false);
  ASSERT_EQ(1u, enabled_attribs.size());
  EXPECT_FALSE(attrib1->enabled());

  manager_->Enable(3, false);
  ASSERT_EQ(0u, enabled_attribs.size());
  EXPECT_FALSE(attrib2->enabled());
}

TEST_F(VertexAttribManagerTest, SetAttribInfo) {
  BufferManager buffer_manager(nullptr, nullptr);
  buffer_manager.CreateBuffer(1, 2);
  Buffer* buffer = buffer_manager.GetBuffer(1);
  ASSERT_TRUE(buffer != nullptr);

  VertexAttrib* attrib = manager_->GetVertexAttrib(1);

  manager_->SetAttribInfo(1, buffer, 3, GL_SHORT, GL_TRUE, 32, 32, 4, GL_TRUE);

  EXPECT_EQ(buffer, attrib->buffer());
  EXPECT_EQ(4, attrib->offset());
  EXPECT_EQ(3, attrib->size());
  EXPECT_EQ(static_cast<GLenum>(GL_SHORT), attrib->type());
  EXPECT_EQ(GL_TRUE, attrib->normalized());
  EXPECT_EQ(32, attrib->gl_stride());
  EXPECT_EQ(GL_TRUE, attrib->integer());

  // The VertexAttribManager must be destroyed before the BufferManager
  // so it releases its buffers.
  manager_ = nullptr;
  buffer_manager.MarkContextLost();
  buffer_manager.Destroy();
}

TEST_F(VertexAttribManagerTest, HaveFixedAttribs) {
  EXPECT_FALSE(manager_->HaveFixedAttribs());
  manager_->SetAttribInfo(1, nullptr, 4, GL_FIXED, GL_FALSE, 0, 16, 0,
                          GL_FALSE);
  EXPECT_TRUE(manager_->HaveFixedAttribs());
  manager_->SetAttribInfo(3, nullptr, 4, GL_FIXED, GL_FALSE, 0, 16, 0,
                          GL_FALSE);
  EXPECT_TRUE(manager_->HaveFixedAttribs());
  manager_->SetAttribInfo(1, nullptr, 4, GL_FLOAT, GL_FALSE, 0, 16, 0,
                          GL_FALSE);
  EXPECT_TRUE(manager_->HaveFixedAttribs());
  manager_->SetAttribInfo(3, nullptr, 4, GL_FLOAT, GL_FALSE, 0, 16, 0,
                          GL_FALSE);
  EXPECT_FALSE(manager_->HaveFixedAttribs());
}

TEST_F(VertexAttribManagerTest, CanAccess) {
  const GLenum kTarget = GL_ARRAY_BUFFER;
  MockErrorState error_state;
  BufferManager buffer_manager(nullptr, nullptr);
  buffer_manager.CreateBuffer(1, 2);
  Buffer* buffer = buffer_manager.GetBuffer(1);
  ASSERT_TRUE(buffer != nullptr);

  VertexAttrib* attrib = manager_->GetVertexAttrib(1);

  EXPECT_TRUE(attrib->CanAccess(0));
  manager_->Enable(1, true);

  manager_->SetAttribInfo(1, buffer, 4, GL_FLOAT, GL_FALSE, 0, 16, 0, GL_FALSE);
  EXPECT_FALSE(attrib->CanAccess(0));

  EXPECT_TRUE(buffer_manager.SetTarget(buffer, kTarget));
  TestHelper::DoBufferData(gl_.get(), &error_state, &buffer_manager, buffer,
                           kTarget, 15, GL_STATIC_DRAW, nullptr, GL_NO_ERROR);

  EXPECT_FALSE(attrib->CanAccess(0));
  TestHelper::DoBufferData(gl_.get(), &error_state, &buffer_manager, buffer,
                           kTarget, 16, GL_STATIC_DRAW, nullptr, GL_NO_ERROR);
  EXPECT_TRUE(attrib->CanAccess(0));
  EXPECT_FALSE(attrib->CanAccess(1));

  manager_->SetAttribInfo(1, buffer, 4, GL_FLOAT, GL_FALSE, 0, 16, 1, GL_FALSE);
  EXPECT_FALSE(attrib->CanAccess(0));

  TestHelper::DoBufferData(gl_.get(), &error_state, &buffer_manager, buffer,
                           kTarget, 32, GL_STATIC_DRAW, nullptr, GL_NO_ERROR);
  EXPECT_TRUE(attrib->CanAccess(0));
  EXPECT_FALSE(attrib->CanAccess(1));
  manager_->SetAttribInfo(1, buffer, 4, GL_FLOAT, GL_FALSE, 0, 16, 0, GL_FALSE);
  EXPECT_TRUE(attrib->CanAccess(1));
  manager_->SetAttribInfo(1, buffer, 4, GL_FLOAT, GL_FALSE, 0, 20, 0, GL_FALSE);
  EXPECT_TRUE(attrib->CanAccess(0));
  EXPECT_FALSE(attrib->CanAccess(1));

  // The VertexAttribManager must be destroyed before the BufferManager
  // so it releases its buffers.
  manager_ = nullptr;
  buffer_manager.MarkContextLost();
  buffer_manager.Destroy();
}

TEST_F(VertexAttribManagerTest, CanAccessStrideSmallerThanGroup) {
  // Regression test for bug: CanAccess() over-counts elements when
  // real_stride < group_size, allowing the validating decoder to
  // accept draw calls whose last vertex fetch reads past the end of
  // the bound GL_ARRAY_BUFFER.
  const GLenum kTarget = GL_ARRAY_BUFFER;
  MockErrorState error_state;
  BufferManager buffer_manager(nullptr, nullptr);
  buffer_manager.CreateBuffer(1, 2);
  Buffer* buffer = buffer_manager.GetBuffer(1);
  ASSERT_TRUE(buffer != nullptr);

  VertexAttrib* attrib = manager_->GetVertexAttrib(1);
  manager_->Enable(1, true);

  // size=4, type=GL_FLOAT -> group_size = 16 bytes per vertex fetch.
  // gl_stride=4, real_stride=4 -> overlapping fetches (legal per GLES).
  manager_->SetAttribInfo(1, buffer, 4, GL_FLOAT, GL_FALSE, 4, 4, 0, GL_FALSE);

  EXPECT_TRUE(buffer_manager.SetTarget(buffer, kTarget));
  // 20-byte buffer.
  TestHelper::DoBufferData(gl_.get(), &error_state, &buffer_manager, buffer,
                           kTarget, 20, GL_STATIC_DRAW, nullptr, GL_NO_ERROR);

  // Vertex i fetches bytes [i*4, i*4 + 16). With a 20-byte buffer:
  //   i=0 -> [0,16)  in bounds
  //   i=1 -> [4,20)  in bounds
  //   i=2 -> [8,24)  4 bytes OOB
  //   i=3 -> [12,28) 8 bytes OOB
  //   i=4 -> [16,32) 12 bytes OOB
  EXPECT_TRUE(attrib->CanAccess(0));
  EXPECT_TRUE(attrib->CanAccess(1));
  // *** These three assertions FAIL on unpatched code: CanAccess() incorrectly
  // returns true because num_elements is computed as 20/4 + (0>=16?1:0) = 5.
  // ***
  EXPECT_FALSE(attrib->CanAccess(2));
  EXPECT_FALSE(attrib->CanAccess(3));
  EXPECT_FALSE(attrib->CanAccess(4));
  // Index 5 is rejected even by the buggy formula.
  EXPECT_FALSE(attrib->CanAccess(5));

  // Edge case: buffer smaller than one element. group_size=16, buffer=12.
  // No vertex can be fetched at all, but the buggy formula computes
  // 12/4 + (0>=16?1:0) = 3.
  TestHelper::DoBufferData(gl_.get(), &error_state, &buffer_manager, buffer,
                           kTarget, 12, GL_STATIC_DRAW, nullptr, GL_NO_ERROR);
  EXPECT_FALSE(attrib->CanAccess(0));
  EXPECT_FALSE(attrib->CanAccess(1));
  EXPECT_FALSE(attrib->CanAccess(2));
  EXPECT_FALSE(attrib->CanAccess(3));

  manager_ = nullptr;
  buffer_manager.MarkContextLost();
  buffer_manager.Destroy();
}

TEST_F(VertexAttribManagerTest, CanAccessRemainderAndEdgeCases) {
  const GLenum kTarget = GL_ARRAY_BUFFER;
  MockErrorState error_state;
  BufferManager buffer_manager(nullptr, nullptr);
  buffer_manager.CreateBuffer(1, 2);
  Buffer* buffer = buffer_manager.GetBuffer(1);
  ASSERT_TRUE(buffer != nullptr);

  VertexAttrib* attrib = manager_->GetVertexAttrib(1);
  manager_->Enable(1, true);

  EXPECT_TRUE(buffer_manager.SetTarget(buffer, kTarget));

  // Case 1: offset > buffer_size
  manager_->SetAttribInfo(1, buffer, 4, GL_FLOAT, GL_FALSE, 16, 16, 20,
                          GL_FALSE);
  TestHelper::DoBufferData(gl_.get(), &error_state, &buffer_manager, buffer,
                           kTarget, 16, GL_STATIC_DRAW, nullptr, GL_NO_ERROR);
  EXPECT_FALSE(attrib->CanAccess(0));

  // Case 2: Remainder logic (usable_size % real_stride_ >= group_size)
  // real_stride = 16, group_size = 8 (size=2, GL_FLOAT).
  manager_->SetAttribInfo(1, buffer, 2, GL_FLOAT, GL_FALSE, 16, 16, 0,
                          GL_FALSE);

  // Subcase 2a: Remainder is enough.
  // usable_size = 24. 24 % 16 = 8 >= 8. Should allow 2 elements.
  TestHelper::DoBufferData(gl_.get(), &error_state, &buffer_manager, buffer,
                           kTarget, 24, GL_STATIC_DRAW, nullptr, GL_NO_ERROR);
  EXPECT_TRUE(attrib->CanAccess(0));
  EXPECT_TRUE(attrib->CanAccess(1));
  EXPECT_FALSE(attrib->CanAccess(2));

  // Subcase 2b: Remainder is NOT enough.
  // usable_size = 20. 20 % 16 = 4 < 8. Should allow 1 element.
  TestHelper::DoBufferData(gl_.get(), &error_state, &buffer_manager, buffer,
                           kTarget, 20, GL_STATIC_DRAW, nullptr, GL_NO_ERROR);
  EXPECT_TRUE(attrib->CanAccess(0));
  EXPECT_FALSE(attrib->CanAccess(1));

  manager_ = nullptr;
  buffer_manager.MarkContextLost();
  buffer_manager.Destroy();
}

TEST_F(VertexAttribManagerTest, Unbind) {
  BufferManager buffer_manager(nullptr, nullptr);
  buffer_manager.CreateBuffer(1, 2);
  buffer_manager.CreateBuffer(3, 4);
  Buffer* buffer1 = buffer_manager.GetBuffer(1);
  Buffer* buffer2 = buffer_manager.GetBuffer(3);
  ASSERT_TRUE(buffer1 != nullptr);
  ASSERT_TRUE(buffer2 != nullptr);

  VertexAttrib* attrib1 = manager_->GetVertexAttrib(1);
  VertexAttrib* attrib3 = manager_->GetVertexAttrib(3);

  // Attach to 2 buffers.
  manager_->SetAttribInfo(
      1, buffer1, 3, GL_SHORT, GL_TRUE, 32, 32, 4, GL_FALSE);
  manager_->SetAttribInfo(
      3, buffer1, 3, GL_SHORT, GL_TRUE, 32, 32, 4, GL_FALSE);
  // Check they were attached.
  EXPECT_EQ(buffer1, attrib1->buffer());
  EXPECT_EQ(buffer1, attrib3->buffer());
  // Unbind unattached buffer.
  manager_->Unbind(buffer2, nullptr);
  // Should be no-op.
  EXPECT_EQ(buffer1, attrib1->buffer());
  EXPECT_EQ(buffer1, attrib3->buffer());
  // Unbind buffer.
  manager_->Unbind(buffer1, nullptr);
  // Check they were detached
  EXPECT_TRUE(nullptr == attrib1->buffer());
  EXPECT_TRUE(nullptr == attrib3->buffer());

  // The VertexAttribManager must be destroyed before the BufferManager
  // so it releases its buffers.
  manager_ = nullptr;
  buffer_manager.MarkContextLost();
  buffer_manager.Destroy();
}

// TODO(gman): Test ValidateBindings
// TODO(gman): Test ValidateBindings with client side arrays.

}  // namespace gles2
}  // namespace gpu
