text:
// Copyright 2012 The Chromium 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 "components/viz/service/display/gl_renderer.h"

#include <stdint.h>

#include <memory>
#include <set>
#include <tuple>
#include <utility>
#include <vector>

#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/containers/cxx20_erase.h"
#include "base/location.h"
#include "base/strings/stringprintf.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "cc/base/math_util.h"
#include "cc/test/fake_impl_task_runner_provider.h"
#include "cc/test/fake_layer_tree_host_impl.h"
#include "cc/test/fake_output_surface_client.h"
#include "cc/test/pixel_test.h"
#include "cc/test/render_pass_test_utils.h"
#include "cc/test/resource_provider_test_utils.h"
#include "components/viz/client/client_resource_provider.h"
#include "components/viz/common/display/renderer_settings.h"
#include "components/viz/common/features.h"
#include "components/viz/common/frame_sinks/copy_output_request.h"
#include "components/viz/common/frame_sinks/copy_output_result.h"
#include "components/viz/common/quads/texture_draw_quad.h"
#include "components/viz/common/resources/platform_color.h"
#include "components/viz/common/resources/transferable_resource.h"
#include "components/viz/service/display/display_resource_provider_gl.h"
#include "components/viz/test/fake_output_surface.h"
#include "components/viz/test/test_gles2_interface.h"
#include "components/viz/test/viz_test_suite.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/context_support.h"
#include "gpu/config/gpu_finch_features.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkMatrix.h"
#include "third_party/skia/include/effects/SkColorMatrixFilter.h"
#include "ui/base/ui_base_features.h"
#include "ui/gfx/color_transform.h"
#include "ui/gfx/geometry/transform.h"
#include "ui/latency/latency_info.h"

#if defined(OS_WIN)
#include "components/viz/service/display/overlay_processor_win.h"
#elif defined(OS_APPLE)
#include "components/viz/service/display/overlay_processor_mac.h"
#elif defined(OS_ANDROID) || defined(USE_OZONE)
#include "components/viz/service/display/overlay_processor_using_strategy.h"
#include "components/viz/service/display/overlay_strategy_single_on_top.h"
#include "components/viz/service/display/overlay_strategy_underlay.h"
#else  // Default
#include "components/viz/service/display/overlay_processor_stub.h"
#endif

using testing::_;
using testing::AnyNumber;
using testing::Args;
using testing::AtLeast;
using testing::Contains;
using testing::ElementsAre;
using testing::Expectation;
using testing::InSequence;
using testing::Invoke;
using testing::Mock;
using testing::Not;
using testing::Pointee;
using testing::Return;
using testing::StrictMock;

namespace viz {

MATCHER_P(MatchesSyncToken, sync_token, "") {
  gpu::SyncToken other;
  memcpy(&other, arg, sizeof(other));
  return other == sync_token;
}

class GLRendererTest : public testing::Test {
 protected:
  ~GLRendererTest() override {
    // Some tests create CopyOutputRequests which will PostTask ensure
    // they are all cleaned up and completed before destroying the test.
    VizTestSuite::RunUntilIdle();
  }
  AggregatedRenderPass* root_render_pass() {
    return render_passes_in_draw_order_.back().get();
  }
  void DrawFrame(GLRenderer* renderer,
                 const gfx::Size& viewport_size,
                 const gfx::DisplayColorSpaces& display_color_spaces =
                     gfx::DisplayColorSpaces()) {
    SurfaceDamageRectList surface_damage_rect_list;
    renderer->DrawFrame(&render_passes_in_draw_order_, 1.f, viewport_size,
                        display_color_spaces,
                        std::move(surface_damage_rect_list));
  }

  static const Program* current_program(GLRenderer* renderer) {
    return renderer->current_program_;
  }

  static TexCoordPrecision get_cached_tex_coord_precision(
      GLRenderer* renderer) {
    return renderer->draw_cache_.program_key.tex_coord_precision();
  }

  DebugRendererSettings debug_settings_;
  AggregatedRenderPassList render_passes_in_draw_order_;
};

#define EXPECT_PROGRAM_VALID(program_binding)      \
  do {                                             \
    ASSERT_TRUE(program_binding);                  \
    EXPECT_TRUE((program_binding)->program());     \
    EXPECT_TRUE((program_binding)->initialized()); \
  } while (false)

static inline SkBlendMode BlendModeToSkXfermode(BlendMode blend_mode) {
  switch (blend_mode) {
    case BLEND_MODE_NONE:
    case BLEND_MODE_NORMAL:
      return SkBlendMode::kSrcOver;
    case BLEND_MODE_DESTINATION_IN:
      return SkBlendMode::kDstIn;
    case BLEND_MODE_SCREEN:
      return SkBlendMode::kScreen;
    case BLEND_MODE_OVERLAY:
      return SkBlendMode::kOverlay;
    case BLEND_MODE_DARKEN:
      return SkBlendMode::kDarken;
    case BLEND_MODE_LIGHTEN:
      return SkBlendMode::kLighten;
    case BLEND_MODE_COLOR_DODGE:
      return SkBlendMode::kColorDodge;
    case BLEND_MODE_COLOR_BURN:
      return SkBlendMode::kColorBurn;
    case BLEND_MODE_HARD_LIGHT:
      return SkBlendMode::kHardLight;
    case BLEND_MODE_SOFT_LIGHT:
      return SkBlendMode::kSoftLight;
    case BLEND_MODE_DIFFERENCE:
      return SkBlendMode::kDifference;
    case BLEND_MODE_EXCLUSION:
      return SkBlendMode::kExclusion;
    case BLEND_MODE_MULTIPLY:
      return SkBlendMode::kMultiply;
    case BLEND_MODE_HUE:
      return SkBlendMode::kHue;
    case BLEND_MODE_SATURATION:
      return SkBlendMode::kSaturation;
    case BLEND_MODE_COLOR:
      return SkBlendMode::kColor;
    case BLEND_MODE_LUMINOSITY:
      return SkBlendMode::kLuminosity;
  }
  return SkBlendMode::kSrcOver;
}

// Explicitly named to be a friend in GLRenderer for shader access.
class GLRendererShaderPixelTest : public cc::PixelTest {
 public:
  void SetUp() override {
    SetUpGLRenderer(gfx::SurfaceOrigin::kBottomLeft);
    ASSERT_FALSE(renderer()->IsContextLost());
  }

  void TearDown() override {
    cc::PixelTest::TearDown();
    ASSERT_FALSE(renderer());
  }

  GLRenderer* renderer() { return static_cast<GLRenderer*>(renderer_.get()); }

  void TestShaderWithDrawingFrame(
      const ProgramKey& program_key,
      const DirectRenderer::DrawingFrame& drawing_frame,
      bool validate_output_color_matrix) {
    renderer()->SetCurrentFrameForTesting(drawing_frame);
    const gfx::ColorSpace kSrcColorSpaces[] = {
        gfx::ColorSpace::CreateSRGB(),
        gfx::ColorSpace(gfx::ColorSpace::PrimaryID::ADOBE_RGB,
                        gfx::ColorSpace::TransferID::GAMMA28),
        gfx::ColorSpace::CreateREC709(),
        gfx::ColorSpace::CreateExtendedSRGB(),
        // This will be adjusted to the display's SDR white level, because no
        // level was specified.
        gfx::ColorSpace::CreateSCRGBLinear(),
        // This won't be, because it has a set SDR white level.
        gfx::ColorSpace::CreateSCRGBLinear(123.0f),
        // This will be adjusted to the display's SDR white level, because no
        // level was specified.
        gfx::ColorSpace::CreateHDR10(),
        // This won't be, because it has a set SDR white level.
        gfx::ColorSpace::CreateHDR10(123.0f),
    };
    const gfx::ColorSpace kDstColorSpaces[] = {
        gfx::ColorSpace::CreateSRGB(),
        gfx::ColorSpace(gfx::ColorSpace::PrimaryID::ADOBE_RGB,
                        gfx::ColorSpace::TransferID::GAMMA18),
        gfx::ColorSpace::CreateExtendedSRGB(),
        gfx::ColorSpace::CreateSCRGBLinear(),
    };
    // Note: Use ASSERT_XXX() and not EXPECT_XXX() below since the size of the
    // loop will lead to useless timeout failures on the bots otherwise.
    for (const auto& src_color_space : kSrcColorSpaces) {
      for (const auto& dst_color_space : kDstColorSpaces) {
        renderer()->SetUseProgram(program_key, src_color_space, dst_color_space,
                                  /*adjust_src_white_level=*/true);
        ASSERT_TRUE(renderer()->current_program_->initialized());

        if (src_color_space != dst_color_space) {
          auto adjusted_color_space = src_color_space;
          if (src_color_space.IsHDR()) {
            adjusted_color_space = src_color_space.GetWithSDRWhiteLevel(
                drawing_frame.display_color_spaces.GetSDRWhiteLevel());
          }
          SCOPED_TRACE(
              base::StringPrintf("adjusted_color_space=%s, dst_color_space=%s",
                                 adjusted_color_space.ToString().c_str(),
                                 dst_color_space.ToString().c_str()));

          auto color_transform = gfx::ColorTransform::NewColorTransform(
              adjusted_color_space, dst_color_space);

          ASSERT_EQ(color_transform->GetShaderSource(),
                    renderer()
                        ->current_program_->color_transform_for_testing()
                        ->GetShaderSource());
        }

        if (validate_output_color_matrix) {
          if (program_key.type() == ProgramType::PROGRAM_TYPE_SOLID_COLOR) {
            ASSERT_EQ(
                -1,
                renderer()->current_program_->output_color_matrix_location());
          } else {
            ASSERT_NE(
                -1,
                renderer()->current_program_->output_color_matrix_location());
          }
        }
      }
    }
  }

  void TestShader(const ProgramKey& program_key) {
    TestShaderWithDrawingFrame(program_key, GLRenderer::DrawingFrame(), false);
  }

  void TestShadersWithOutputColorMatrix(const ProgramKey& program_key) {
    GLRenderer::DrawingFrame frame;

    AggregatedRenderPassList render_passes_in_draw_order;
    gfx::Size viewport_size(100, 100);
    AggregatedRenderPassId root_pass_id{1};
    auto* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    root_pass->damage_rect = gfx::Rect(0, 0, 25, 25);

    frame.root_render_pass = root_pass;
    frame.current_render_pass = root_pass;
    frame.render_passes_in_draw_order = &render_passes_in_draw_order;

    // Set a non-identity color matrix on the output surface.
    skia::Matrix44 color_matrix(skia::Matrix44::kIdentity_Constructor);
    color_matrix.set(0, 0, 0.7f);
    color_matrix.set(1, 1, 0.4f);
    color_matrix.set(2, 2, 0.5f);
    renderer()->output_surface_->set_color_matrix(color_matrix);

    TestShaderWithDrawingFrame(program_key, frame, true);
  }

  void TestShadersWithSDRWhiteLevel(const ProgramKey& program_key,
                                    float sdr_white_level) {
    GLRenderer::DrawingFrame frame;
    frame.display_color_spaces.SetSDRWhiteLevel(sdr_white_level);
    TestShaderWithDrawingFrame(program_key, frame, false);
  }

  void TestBasicShaders() {
    TestShader(ProgramKey::DebugBorder());
    TestShader(ProgramKey::SolidColor(NO_AA, false, false));
    TestShader(ProgramKey::SolidColor(USE_AA, false, false));
    TestShader(ProgramKey::SolidColor(NO_AA, true, false));

    TestShadersWithOutputColorMatrix(ProgramKey::DebugBorder());
    TestShadersWithOutputColorMatrix(
        ProgramKey::SolidColor(NO_AA, false, false));
    TestShadersWithOutputColorMatrix(
        ProgramKey::SolidColor(USE_AA, false, false));
    TestShadersWithOutputColorMatrix(
        ProgramKey::SolidColor(NO_AA, true, false));

    TestShadersWithSDRWhiteLevel(ProgramKey::DebugBorder(), 200.f);
    TestShadersWithSDRWhiteLevel(ProgramKey::SolidColor(NO_AA, false, false),
                                 200.f);
    TestShadersWithSDRWhiteLevel(ProgramKey::SolidColor(USE_AA, false, false),
                                 200.f);
    TestShadersWithSDRWhiteLevel(ProgramKey::SolidColor(NO_AA, true, false),
                                 200.f);
  }

  void TestColorShaders() {
    const size_t kNumTransferFns = 7;
    skcms_TransferFunction transfer_fns[kNumTransferFns] = {
        // The identity.
        {1.f, 1.f, 0.f, 1.f, 0.f, 0.f, 0.f},
        // The identity, with an if statement.
        {1.f, 1.f, 0.f, 1.f, 0.5f, 0.f, 0.f},
        // Just the power function.
        {1.1f, 1.f, 0.f, 1.f, 0.f, 0.f, 0.f},
        // Everything but the power function, nonlinear only.
        {1.f, 0.9f, 0.1f, 0.9f, 0.f, 0.1f, 0.1f},
        // Everything, nonlinear only.
        {1.1f, 0.9f, 0.1f, 0.9f, 0.f, 0.1f, 0.1f},
        // Everything but the power function.
        {1.f, 0.9f, 0.1f, 0.9f, 0.5f, 0.1f, 0.1f},
        // Everything.
        {1.1f, 0.9f, 0.1f, 0.9f, 0.5f, 0.1f, 0.1f},
    };

    for (size_t i = 0; i < kNumTransferFns; ++i) {
      skcms_Matrix3x3 primaries;
      gfx::ColorSpace::CreateSRGB().GetPrimaryMatrix(&primaries);
      gfx::ColorSpace src =
          gfx::ColorSpace::CreateCustom(primaries, transfer_fns[i]);

      renderer()->SetCurrentFrameForTesting(GLRenderer::DrawingFrame());
      renderer()->SetUseProgram(ProgramKey::SolidColor(NO_AA, false, false),
                                src, gfx::ColorSpace::CreateXYZD50());
      EXPECT_TRUE(renderer()->current_program_->initialized());
    }
  }

  void TestShadersWithPrecision(TexCoordPrecision precision) {
    // This program uses external textures and sampler, so it won't compile
    // everywhere.
    if (context_provider()->ContextCapabilities().egl_image_external) {
      TestShader(ProgramKey::VideoStream(precision, false));
    }
  }

  void TestShadersWithPrecisionAndBlend(TexCoordPrecision precision,
                                        BlendMode blend_mode) {
    TestShader(ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode,
                                      NO_AA, NO_MASK, false, false, false,
                                      false));
    TestShader(ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode,
                                      USE_AA, NO_MASK, false, false, false,
                                      false));
  }

  void TestShadersWithPrecisionAndSampler(
      TexCoordPrecision precision,
      SamplerType sampler,
      PremultipliedAlphaMode premultipliedAlpha,
      bool has_background_color,
      bool has_tex_clamp_rect) {
    TestShader(ProgramKey::Texture(precision, sampler, premultipliedAlpha,
                                   has_background_color, has_tex_clamp_rect,
                                   false, false));
  }

  void TestShadersWithPrecisionAndSamplerTiledAA(
      TexCoordPrecision precision,
      SamplerType sampler,
      PremultipliedAlphaMode premultipliedAlpha) {
    TestShader(ProgramKey::Tile(precision, sampler, USE_AA, premultipliedAlpha,
                                false, false, false, false));
  }

  void TestShadersWithPrecisionAndSamplerTiled(
      TexCoordPrecision precision,
      SamplerType sampler,
      PremultipliedAlphaMode premultipliedAlpha,
      bool is_opaque,
      bool has_tex_clamp_rect) {
    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, premultipliedAlpha,
                                is_opaque, has_tex_clamp_rect, false, false));
  }

  void TestYUVShadersWithPrecisionAndSampler(TexCoordPrecision precision,
                                             SamplerType sampler) {
    // Iterate over alpha plane and nv12 parameters.
    UVTextureMode uv_modes[2] = {UV_TEXTURE_MODE_UV, UV_TEXTURE_MODE_U_V};
    YUVAlphaTextureMode a_modes[2] = {YUV_NO_ALPHA_TEXTURE,
                                      YUV_HAS_ALPHA_TEXTURE};
    for (auto uv_mode : uv_modes) {
      SCOPED_TRACE(uv_mode);
      for (auto a_mode : a_modes) {
        SCOPED_TRACE(a_mode);
        TestShader(ProgramKey::YUVVideo(precision, sampler, a_mode, uv_mode,
                                        false, false));
      }
    }
  }

  void TestShadersWithMasks(TexCoordPrecision precision,
                            SamplerType sampler,
                            BlendMode blend_mode,
                            bool mask_for_background) {
    TestShader(ProgramKey::RenderPass(precision, sampler, blend_mode, NO_AA,
                                      HAS_MASK, mask_for_background, false,
                                      false, false));
    TestShader(ProgramKey::RenderPass(precision, sampler, blend_mode, NO_AA,
                                      HAS_MASK, mask_for_background, true,
                                      false, false));
    TestShader(ProgramKey::RenderPass(precision, sampler, blend_mode, USE_AA,
                                      HAS_MASK, mask_for_background, false,
                                      false, false));
    TestShader(ProgramKey::RenderPass(precision, sampler, blend_mode, USE_AA,
                                      HAS_MASK, mask_for_background, true,
                                      false, false));
  }
};

namespace {

#if !defined(OS_ANDROID)
static const TexCoordPrecision kPrecisionList[] = {TEX_COORD_PRECISION_MEDIUM,
                                                   TEX_COORD_PRECISION_HIGH};

static const BlendMode kBlendModeList[LAST_BLEND_MODE + 1] = {
    BLEND_MODE_NONE,       BLEND_MODE_NORMAL,      BLEND_MODE_DESTINATION_IN,
    BLEND_MODE_SCREEN,     BLEND_MODE_OVERLAY,     BLEND_MODE_DARKEN,
    BLEND_MODE_LIGHTEN,    BLEND_MODE_COLOR_DODGE, BLEND_MODE_COLOR_BURN,
    BLEND_MODE_HARD_LIGHT, BLEND_MODE_SOFT_LIGHT,  BLEND_MODE_DIFFERENCE,
    BLEND_MODE_EXCLUSION,  BLEND_MODE_MULTIPLY,    BLEND_MODE_HUE,
    BLEND_MODE_SATURATION, BLEND_MODE_COLOR,       BLEND_MODE_LUMINOSITY,
};

static const SamplerType kSamplerList[] = {
    SAMPLER_TYPE_2D, SAMPLER_TYPE_2D_RECT, SAMPLER_TYPE_EXTERNAL_OES,
};

static const PremultipliedAlphaMode kPremultipliedAlphaModeList[] = {
    PREMULTIPLIED_ALPHA, NON_PREMULTIPLIED_ALPHA};

TEST_F(GLRendererShaderPixelTest, BasicShadersCompile) {
  TestBasicShaders();
}

TEST_F(GLRendererShaderPixelTest, TestColorShadersCompile) {
  TestColorShaders();
}

class PrecisionShaderPixelTest
    : public GLRendererShaderPixelTest,
      public ::testing::WithParamInterface<TexCoordPrecision> {};

TEST_P(PrecisionShaderPixelTest, ShadersCompile) {
  TestShadersWithPrecision(GetParam());
}

INSTANTIATE_TEST_SUITE_P(PrecisionShadersCompile,
                         PrecisionShaderPixelTest,
                         ::testing::ValuesIn(kPrecisionList));

class PrecisionBlendShaderPixelTest
    : public GLRendererShaderPixelTest,
      public ::testing::WithParamInterface<
          std::tuple<TexCoordPrecision, BlendMode>> {};

TEST_P(PrecisionBlendShaderPixelTest, ShadersCompile) {
  TestShadersWithPrecisionAndBlend(std::get<0>(GetParam()),
                                   std::get<1>(GetParam()));
}

INSTANTIATE_TEST_SUITE_P(
    PrecisionBlendShadersCompile,
    PrecisionBlendShaderPixelTest,
    ::testing::Combine(::testing::ValuesIn(kPrecisionList),
                       ::testing::ValuesIn(kBlendModeList)));

class PrecisionSamplerShaderPixelTest
    : public GLRendererShaderPixelTest,
      public ::testing::WithParamInterface<
          std::tuple<TexCoordPrecision,
                     SamplerType,
                     PremultipliedAlphaMode,
                     bool,       // has_background_color
                     bool>> {};  // has_tex_clamp_rect

TEST_P(PrecisionSamplerShaderPixelTest, ShadersCompile) {
  SamplerType sampler = std::get<1>(GetParam());
  if (sampler != SAMPLER_TYPE_2D_RECT ||
      context_provider()->ContextCapabilities().texture_rectangle) {
    TestShadersWithPrecisionAndSampler(
        std::get<0>(GetParam()),  // TexCoordPrecision
        sampler,
        std::get<2>(GetParam()),   // PremultipliedAlphaMode
        std::get<3>(GetParam()),   // has_background_color
        std::get<4>(GetParam()));  // has_tex_clamp_rect
  }
}

INSTANTIATE_TEST_SUITE_P(
    PrecisionSamplerShadersCompile,
    PrecisionSamplerShaderPixelTest,
    ::testing::Combine(::testing::ValuesIn(kPrecisionList),
                       ::testing::ValuesIn(kSamplerList),
                       ::testing::ValuesIn(kPremultipliedAlphaModeList),
                       ::testing::Bool(),    // has_background_color
                       ::testing::Bool()));  // has_tex_clamp_rect

class PrecisionSamplerShaderPixelTestTiled
    : public GLRendererShaderPixelTest,
      public ::testing::WithParamInterface<
          std::tuple<TexCoordPrecision,
                     SamplerType,
                     PremultipliedAlphaMode,
                     bool,   // is_opaque
                     bool>>  // has_tex_clamp_rect
{};

TEST_P(PrecisionSamplerShaderPixelTestTiled, ShadersCompile) {
  SamplerType sampler = std::get<1>(GetParam());
  if (sampler != SAMPLER_TYPE_2D_RECT ||
      context_provider()->ContextCapabilities().texture_rectangle) {
    TestShadersWithPrecisionAndSamplerTiled(
        std::get<0>(GetParam()),  // TexCoordPrecision
        sampler,
        std::get<2>(GetParam()),   // PremultipliedAlphaMode
        std::get<3>(GetParam()),   // is_opaque
        std::get<4>(GetParam()));  // has_tex_clamp_rect
  }
}

INSTANTIATE_TEST_SUITE_P(
    PrecisionSamplerShadersCompile,
    PrecisionSamplerShaderPixelTestTiled,
    ::testing::Combine(::testing::ValuesIn(kPrecisionList),
                       ::testing::ValuesIn(kSamplerList),
                       ::testing::ValuesIn(kPremultipliedAlphaModeList),
                       ::testing::Bool(),    // is_opaque
                       ::testing::Bool()));  // has_tex_clamp_rect

class PrecisionSamplerShaderPixelTestTiledAA
    : public GLRendererShaderPixelTest,
      public ::testing::WithParamInterface<
          std::tuple<TexCoordPrecision, SamplerType, PremultipliedAlphaMode>> {
};

TEST_P(PrecisionSamplerShaderPixelTestTiledAA, ShadersCompile) {
  SamplerType sampler = std::get<1>(GetParam());
  if (sampler != SAMPLER_TYPE_2D_RECT ||
      context_provider()->ContextCapabilities().texture_rectangle) {
    TestShadersWithPrecisionAndSamplerTiledAA(
        std::get<0>(GetParam()),  // TexCoordPrecision
        sampler,
        std::get<2>(GetParam()));  // PremultipliedAlphaMode
  }
}

INSTANTIATE_TEST_SUITE_P(
    PrecisionSamplerShadersCompile,
    PrecisionSamplerShaderPixelTestTiledAA,
    ::testing::Combine(::testing::ValuesIn(kPrecisionList),
                       ::testing::ValuesIn(kSamplerList),
                       ::testing::ValuesIn(kPremultipliedAlphaModeList)));

class PrecisionSamplerYUVShaderPixelTest
    : public GLRendererShaderPixelTest,
      public ::testing::WithParamInterface<
          std::tuple<TexCoordPrecision, SamplerType>> {};

TEST_P(PrecisionSamplerYUVShaderPixelTest, ShadersCompile) {
  SamplerType sampler = std::get<1>(GetParam());
  if (sampler != SAMPLER_TYPE_2D_RECT ||
      context_provider()->ContextCapabilities().texture_rectangle) {
    TestYUVShadersWithPrecisionAndSampler(
        std::get<0>(GetParam()),  // TexCoordPrecision
        sampler);
  }
}

INSTANTIATE_TEST_SUITE_P(PrecisionSamplerShadersCompile,
                         PrecisionSamplerYUVShaderPixelTest,
                         ::testing::Combine(::testing::ValuesIn(kPrecisionList),
                                            ::testing::ValuesIn(kSamplerList)));

class MaskShaderPixelTest
    : public GLRendererShaderPixelTest,
      public ::testing::WithParamInterface<
          std::tuple<TexCoordPrecision, SamplerType, BlendMode, bool>> {};

TEST_P(MaskShaderPixelTest, ShadersCompile) {
  SamplerType sampler = std::get<1>(GetParam());
  if (sampler != SAMPLER_TYPE_2D_RECT ||
      context_provider()->ContextCapabilities().texture_rectangle) {
    TestShadersWithMasks(std::get<0>(GetParam()), sampler,
                         std::get<2>(GetParam()), std::get<3>(GetParam()));
  }
}

INSTANTIATE_TEST_SUITE_P(MaskShadersCompile,
                         MaskShaderPixelTest,
                         ::testing::Combine(::testing::ValuesIn(kPrecisionList),
                                            ::testing::ValuesIn(kSamplerList),
                                            ::testing::ValuesIn(kBlendModeList),
                                            ::testing::Bool()));

#endif

class FakeRendererGL : public GLRenderer {
 public:
  FakeRendererGL(const RendererSettings* settings,
                 const DebugRendererSettings* debug_settings,
                 OutputSurface* output_surface,
                 DisplayResourceProviderGL* resource_provider)
      : GLRenderer(settings,
                   debug_settings,
                   output_surface,
                   resource_provider,
                   nullptr,
                   nullptr) {}

  FakeRendererGL(const RendererSettings* settings,
                 const DebugRendererSettings* debug_settings,
                 OutputSurface* output_surface,
                 DisplayResourceProviderGL* resource_provider,
                 OverlayProcessorInterface* overlay_processor)
      : GLRenderer(settings,
                   debug_settings,
                   output_surface,
                   resource_provider,
                   overlay_processor,
                   nullptr) {}

  FakeRendererGL(
      const RendererSettings* settings,
      const DebugRendererSettings* debug_settings,
      OutputSurface* output_surface,
      DisplayResourceProviderGL* resource_provider,
      OverlayProcessorInterface* overlay_processor,
      scoped_refptr<base::SingleThreadTaskRunner> current_task_runner)
      : GLRenderer(settings,
                   debug_settings,
                   output_surface,
                   resource_provider,
                   overlay_processor,
                   std::move(current_task_runner)) {}

  // GLRenderer methods.

  // Changing visibility to public.
  using GLRenderer::stencil_enabled;
};

class GLRendererWithDefaultHarnessTest : public GLRendererTest {
 protected:
  GLRendererWithDefaultHarnessTest() {
    output_surface_ = FakeOutputSurface::Create3d();
    output_surface_->BindToClient(&output_surface_client_);

    resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
        output_surface_->context_provider());
    renderer_ = std::make_unique<FakeRendererGL>(&settings_, &debug_settings_,
                                                 output_surface_.get(),
                                                 resource_provider_.get());
    renderer_->Initialize();
    renderer_->SetVisible(true);
  }

  void SwapBuffers() { renderer_->SwapBuffers({}); }

  RendererSettings settings_;
  cc::FakeOutputSurfaceClient output_surface_client_;
  std::unique_ptr<FakeOutputSurface> output_surface_;
  std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
  std::unique_ptr<FakeRendererGL> renderer_;
};

// Closing the namespace here so that GLRendererShaderTest can take advantage
// of the friend relationship with GLRenderer and all of the mock classes
// declared above it.
}  // namespace

class GLRendererShaderTest : public GLRendererTest {
 protected:
  GLRendererShaderTest() {
    output_surface_ = FakeOutputSurface::Create3d();
    output_surface_->BindToClient(&output_surface_client_);

    resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
        output_surface_->context_provider());
    renderer_ = std::make_unique<FakeRendererGL>(
        &settings_, &debug_settings_, output_surface_.get(),
        resource_provider_.get(), nullptr);
    renderer_->Initialize();
    renderer_->SetVisible(true);

    child_context_provider_ = TestContextProvider::Create();
    child_context_provider_->BindToCurrentThread();
    child_resource_provider_ = std::make_unique<ClientResourceProvider>();
  }

  ~GLRendererShaderTest() override {
    child_resource_provider_->ShutdownAndReleaseAllResources();
  }

  void TestRenderPassProgram(TexCoordPrecision precision,
                             BlendMode blend_mode) {
    const Program* program = renderer_->GetProgramIfInitialized(
        ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode, NO_AA,
                               NO_MASK, false, false, false, false));
    EXPECT_PROGRAM_VALID(program);
    EXPECT_EQ(program, renderer_->current_program_);
  }

  void TestRenderPassColorMatrixProgram(TexCoordPrecision precision,
                                        BlendMode blend_mode) {
    const Program* program = renderer_->GetProgramIfInitialized(
        ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode, NO_AA,
                               NO_MASK, false, true, false, false));
    EXPECT_PROGRAM_VALID(program);
    EXPECT_EQ(program, renderer_->current_program_);
  }

  void TestRenderPassMaskProgram(TexCoordPrecision precision,
                                 SamplerType sampler,
                                 BlendMode blend_mode) {
    const Program* program = renderer_->GetProgramIfInitialized(
        ProgramKey::RenderPass(precision, sampler, blend_mode, NO_AA, HAS_MASK,
                               false, false, false, false));
    EXPECT_PROGRAM_VALID(program);
    EXPECT_EQ(program, renderer_->current_program_);
  }

  void TestRenderPassMaskColorMatrixProgram(TexCoordPrecision precision,
                                            SamplerType sampler,
                                            BlendMode blend_mode) {
    const Program* program = renderer_->GetProgramIfInitialized(
        ProgramKey::RenderPass(precision, sampler, blend_mode, NO_AA, HAS_MASK,
                               false, true, false, false));
    EXPECT_PROGRAM_VALID(program);
    EXPECT_EQ(program, renderer_->current_program_);
  }

  void TestRenderPassProgramAA(TexCoordPrecision precision,
                               BlendMode blend_mode) {
    const Program* program = renderer_->GetProgramIfInitialized(
        ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode, USE_AA,
                               NO_MASK, false, false, false, false));
    EXPECT_PROGRAM_VALID(program);
    EXPECT_EQ(program, renderer_->current_program_);
  }

  void TestRenderPassColorMatrixProgramAA(TexCoordPrecision precision,
                                          BlendMode blend_mode) {
    const Program* program = renderer_->GetProgramIfInitialized(
        ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode, USE_AA,
                               NO_MASK, false, true, false, false));
    EXPECT_PROGRAM_VALID(program);
    EXPECT_EQ(program, renderer_->current_program_);
  }

  void TestRenderPassMaskProgramAA(TexCoordPrecision precision,
                                   SamplerType sampler,
                                   BlendMode blend_mode) {
    const Program* program = renderer_->GetProgramIfInitialized(
        ProgramKey::RenderPass(precision, sampler, blend_mode, USE_AA, HAS_MASK,
                               false, false, false, false));
    EXPECT_PROGRAM_VALID(program);
    EXPECT_EQ(program, renderer_->current_program_);
  }

  void TestRenderPassMaskColorMatrixProgramAA(TexCoordPrecision precision,
                                              SamplerType sampler,
                                              BlendMode blend_mode) {
    const Program* program = renderer_->GetProgramIfInitialized(
        ProgramKey::RenderPass(precision, sampler, blend_mode, USE_AA, HAS_MASK,
                               false, true, false, false));
    EXPECT_PROGRAM_VALID(program);
    EXPECT_EQ(program, renderer_->current_program_);
  }

  void TestSolidColorProgramAA() {
    const Program* program = renderer_->GetProgramIfInitialized(
        ProgramKey::SolidColor(USE_AA, false, false));
    EXPECT_PROGRAM_VALID(program);
    EXPECT_EQ(program, renderer_->current_program_);
  }

  RendererSettings settings_;
  cc::FakeOutputSurfaceClient output_surface_client_;
  std::unique_ptr<FakeOutputSurface> output_surface_;
  std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
  scoped_refptr<TestContextProvider> child_context_provider_;
  std::unique_ptr<ClientResourceProvider> child_resource_provider_;
  std::unique_ptr<FakeRendererGL> renderer_;
};

namespace {

TEST_F(GLRendererWithDefaultHarnessTest, ExternalStencil) {
  gfx::Size viewport_size(1, 1);
  EXPECT_FALSE(renderer_->stencil_enabled());

  output_surface_->set_has_external_stencil_test(true);

  auto* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, AggregatedRenderPassId{1},
      gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
  root_pass->has_transparent_background = false;

  DrawFrame(renderer_.get(), viewport_size);
  EXPECT_TRUE(renderer_->stencil_enabled());
}

TEST_F(GLRendererWithDefaultHarnessTest, TextureDrawQuadShaderPrecisionHigh) {
  // TestContextProvider, used inside FakeOuputSurfaceClient, redefines
  // GetShaderPrecisionFormat() and sets the resolution of mediump with
  // 10-bits (1024). So any value higher than 1024 should use highp.
  // The goal is to make sure the fragment shaders used in DoDrawQuad() use
  // the correct precision qualifier.

  const gfx::Size viewport_size(1, 1);
  auto* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, AggregatedRenderPassId{1},
      gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());

  const bool needs_blending = false;
  const bool premultiplied_alpha = false;
  const bool flipped = false;
  const bool nearest_neighbor = false;
  const float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
  const gfx::PointF uv_top_left(0, 0);
  const gfx::PointF uv_bottom_right(1, 1);

  auto child_context_provider = TestContextProvider::Create();
  child_context_provider->BindToCurrentThread();

  auto child_resource_provider = std::make_unique<ClientResourceProvider>();

  // Here is where the texture is created. Any value bigger than 1024 should use
  // a highp.
  auto transfer_resource = TransferableResource::MakeGL(
      gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(),
      gfx::Size(1025, 1025), true);
  ResourceId client_resource_id = child_resource_provider->ImportResource(
      transfer_resource, base::DoNothing());

  std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
      cc::SendResourceAndGetChildToParentMap(
          {client_resource_id}, resource_provider_.get(),
          child_resource_provider.get(), child_context_provider.get());
  ResourceId resource_id = resource_map[client_resource_id];

  // The values defined here should not alter the size of the already created
  // texture.
  TextureDrawQuad* overlay_quad =
      root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
  SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState();
  shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size),
                       gfx::Rect(1023, 1023), gfx::MaskFilterInfo(),
                       absl::nullopt, false, 1, SkBlendMode::kSrcOver, 0);
  overlay_quad->SetNew(shared_state, gfx::Rect(1023, 1023),
                       gfx::Rect(1023, 1023), needs_blending, resource_id,
                       premultiplied_alpha, uv_top_left, uv_bottom_right,
                       SK_ColorTRANSPARENT, vertex_opacity, flipped,
                       nearest_neighbor, /*secure_output_only=*/false,
                       gfx::ProtectedVideoType::kClear);

  DrawFrame(renderer_.get(), viewport_size);

  TexCoordPrecision precision = get_cached_tex_coord_precision(renderer_.get());
  EXPECT_EQ(precision, TEX_COORD_PRECISION_HIGH);

  child_resource_provider->ShutdownAndReleaseAllResources();
}

TEST_F(GLRendererWithDefaultHarnessTest, TextureDrawQuadShaderPrecisionMedium) {
  // TestContextProvider, used inside FakeOuputSurfaceClient, redefines
  // GetShaderPrecisionFormat() and sets the resolution of mediump with
  // 10-bits (1024). So any value higher than 1024 should use highp.
  // The goal is to make sure the fragment shaders used in DoDrawQuad() use
  // the correct precision qualifier.

  const gfx::Size viewport_size(1, 1);
  auto* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, AggregatedRenderPassId{1},
      gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());

  const bool needs_blending = false;
  const bool premultiplied_alpha = false;
  const bool flipped = false;
  const bool nearest_neighbor = false;
  const float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
  const gfx::PointF uv_top_left(0, 0);
  const gfx::PointF uv_bottom_right(1, 1);

  auto child_context_provider = TestContextProvider::Create();
  child_context_provider->BindToCurrentThread();

  auto child_resource_provider = std::make_unique<ClientResourceProvider>();

  // Here is where the texture is created. Any value smaller than 1024 should
  // use a mediump.
  auto transfer_resource = TransferableResource::MakeGL(
      gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(),
      gfx::Size(1023, 1023), true);
  ResourceId client_resource_id = child_resource_provider->ImportResource(
      transfer_resource, base::DoNothing());

  std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
      cc::SendResourceAndGetChildToParentMap(
          {client_resource_id}, resource_provider_.get(),
          child_resource_provider.get(), child_context_provider.get());
  ResourceId resource_id = resource_map[client_resource_id];

  // The values defined here should not alter the size of the already created
  // texture.
  TextureDrawQuad* overlay_quad =
      root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
  SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState();
  shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size),
                       gfx::Rect(1025, 1025), gfx::MaskFilterInfo(),
                       absl::nullopt, false, 1, SkBlendMode::kSrcOver, 0);
  overlay_quad->SetNew(shared_state, gfx::Rect(1025, 1025),
                       gfx::Rect(1025, 1025), needs_blending, resource_id,
                       premultiplied_alpha, uv_top_left, uv_bottom_right,
                       SK_ColorTRANSPARENT, vertex_opacity, flipped,
                       nearest_neighbor, /*secure_output_only=*/false,
                       gfx::ProtectedVideoType::kClear);

  DrawFrame(renderer_.get(), viewport_size);

  TexCoordPrecision precision = get_cached_tex_coord_precision(renderer_.get());
  EXPECT_EQ(precision, TEX_COORD_PRECISION_MEDIUM);

  child_resource_provider->ShutdownAndReleaseAllResources();
}

class GLRendererTextureDrawQuadHDRTest
    : public GLRendererWithDefaultHarnessTest {
 protected:
  void RunTest(bool is_video_frame) {
    const gfx::Size viewport_size(10, 10);
    auto* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, AggregatedRenderPassId{1},
        gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());

    const bool needs_blending = false;
    const bool premultiplied_alpha = false;
    const bool flipped = false;
    const bool nearest_neighbor = false;
    const float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
    const gfx::PointF uv_top_left(0, 0);
    const gfx::PointF uv_bottom_right(1, 1);

    auto child_context_provider = TestContextProvider::Create();
    child_context_provider->BindToCurrentThread();

    auto child_resource_provider = std::make_unique<ClientResourceProvider>();

    constexpr gfx::Size kTextureSize = gfx::Size(10, 10);
    auto transfer_resource = TransferableResource::MakeGL(
        gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(),
        kTextureSize, true);
    transfer_resource.color_space = gfx::ColorSpace::CreateSCRGBLinear();
    ResourceId client_resource_id = child_resource_provider->ImportResource(
        transfer_resource, base::DoNothing());

    std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
        cc::SendResourceAndGetChildToParentMap(
            {client_resource_id}, resource_provider_.get(),
            child_resource_provider.get(), child_context_provider.get());
    ResourceId resource_id = resource_map[client_resource_id];

    TextureDrawQuad* overlay_quad =
        root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
    SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState();
    shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size),
                         gfx::Rect(kTextureSize), gfx::MaskFilterInfo(),
                         absl::nullopt, false, 1, SkBlendMode::kSrcOver, 0);
    overlay_quad->SetNew(shared_state, gfx::Rect(kTextureSize),
                         gfx::Rect(kTextureSize), needs_blending, resource_id,
                         premultiplied_alpha, uv_top_left, uv_bottom_right,
                         SK_ColorTRANSPARENT, vertex_opacity, flipped,
                         nearest_neighbor, /*secure_output_only=*/false,
                         gfx::ProtectedVideoType::kClear);
    overlay_quad->is_video_frame = is_video_frame;

    constexpr float kSDRWhiteLevel = 123.0f;
    gfx::DisplayColorSpaces display_color_spaces;
    display_color_spaces.SetSDRWhiteLevel(kSDRWhiteLevel);

    DrawFrame(renderer_.get(), viewport_size, display_color_spaces);

    const Program* program = current_program(renderer_.get());
    DCHECK(program);
    DCHECK(program->color_transform_for_testing())
        << program->fragment_shader().GetShaderString();

    const gfx::ColorSpace expected_src_color_space =
        is_video_frame
            ? gfx::ColorSpace::CreateSCRGBLinear().GetWithSDRWhiteLevel(
                  kSDRWhiteLevel)
            : gfx::ColorSpace::CreateSCRGBLinear();
    EXPECT_EQ(program->color_transform_for_testing()->GetSrcColorSpace(),
              expected_src_color_space);

    child_resource_provider->ShutdownAndReleaseAllResources();
  }
};

TEST_F(GLRendererTextureDrawQuadHDRTest, VideoFrame) {
  RunTest(/*is_video_frame=*/true);
}

TEST_F(GLRendererTextureDrawQuadHDRTest, NotVideoFrame) {
  RunTest(/*is_video_frame=*/false);
}

class ForbidSynchronousCallGLES2Interface : public TestGLES2Interface {
 public:
  ForbidSynchronousCallGLES2Interface() = default;

  void GetAttachedShaders(GLuint program,
                          GLsizei max_count,
                          GLsizei* count,
                          GLuint* shaders) override {
    ADD_FAILURE();
  }

  GLint GetAttribLocation(GLuint program, const GLchar* name) override {
    ADD_FAILURE();
    return 0;
  }

  void GetBooleanv(GLenum pname, GLboolean* value) override { ADD_FAILURE(); }

  void GetBufferParameteriv(GLenum target,
                            GLenum pname,
                            GLint* value) override {
    ADD_FAILURE();
  }

  GLenum GetError() override {
    ADD_FAILURE();
    return GL_NO_ERROR;
  }

  void GetFloatv(GLenum pname, GLfloat* value) override { ADD_FAILURE(); }

  void GetFramebufferAttachmentParameteriv(GLenum target,
                                           GLenum attachment,
                                           GLenum pname,
                                           GLint* value) override {
    ADD_FAILURE();
  }

  void GetIntegerv(GLenum pname, GLint* value) override {
    if (pname == GL_MAX_TEXTURE_SIZE) {
      // MAX_TEXTURE_SIZE is cached client side, so it's OK to query.
      *value = 1024;
    } else {
      ADD_FAILURE();
    }
  }

  // We allow querying the shader compilation and program link status in debug
  // mode, but not release.
  void GetProgramiv(GLuint program, GLenum pname, GLint* value) override {
    ADD_FAILURE();
  }

  void GetShaderiv(GLuint shader, GLenum pname, GLint* value) override {
    ADD_FAILURE();
  }

  void GetRenderbufferParameteriv(GLenum target,
                                  GLenum pname,
                                  GLint* value) override {
    ADD_FAILURE();
  }

  void GetShaderPrecisionFormat(GLenum shadertype,
                                GLenum precisiontype,
                                GLint* range,
                                GLint* precision) override {
    ADD_FAILURE();
  }

  void GetTexParameterfv(GLenum target, GLenum pname, GLfloat* value) override {
    ADD_FAILURE();
  }

  void GetTexParameteriv(GLenum target, GLenum pname, GLint* value) override {
    ADD_FAILURE();
  }

  void GetUniformfv(GLuint program, GLint location, GLfloat* value) override {
    ADD_FAILURE();
  }

  void GetUniformiv(GLuint program, GLint location, GLint* value) override {
    ADD_FAILURE();
  }

  GLint GetUniformLocation(GLuint program, const GLchar* name) override {
    ADD_FAILURE();
    return 0;
  }

  void GetVertexAttribfv(GLuint index, GLenum pname, GLfloat* value) override {
    ADD_FAILURE();
  }

  void GetVertexAttribiv(GLuint index, GLenum pname, GLint* value) override {
    ADD_FAILURE();
  }

  void GetVertexAttribPointerv(GLuint index,
                               GLenum pname,
                               void** pointer) override {
    ADD_FAILURE();
  }
};

TEST_F(GLRendererTest, InitializationDoesNotMakeSynchronousCalls) {
  auto gl_owned = std::make_unique<ForbidSynchronousCallGLES2Interface>();
  auto provider = TestContextProvider::Create(std::move(gl_owned));
  provider->BindToCurrentThread();

  cc::FakeOutputSurfaceClient output_surface_client;
  std::unique_ptr<OutputSurface> output_surface(
      FakeOutputSurface::Create3d(std::move(provider)));
  output_surface->BindToClient(&output_surface_client);

  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
      output_surface->context_provider());

  RendererSettings settings;
  FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                          resource_provider.get());
}

class LoseContextOnFirstGetGLES2Interface : public TestGLES2Interface {
 public:
  LoseContextOnFirstGetGLES2Interface() {}

  void GetProgramiv(GLuint program, GLenum pname, GLint* value) override {
    LoseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
                        GL_INNOCENT_CONTEXT_RESET_ARB);
    *value = 0;
  }

  void GetShaderiv(GLuint shader, GLenum pname, GLint* value) override {
    LoseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
                        GL_INNOCENT_CONTEXT_RESET_ARB);
    *value = 0;
  }
};

TEST_F(GLRendererTest, InitializationWithQuicklyLostContextDoesNotAssert) {
  auto gl_owned = std::make_unique<LoseContextOnFirstGetGLES2Interface>();
  auto provider = TestContextProvider::Create(std::move(gl_owned));
  provider->BindToCurrentThread();

  cc::FakeOutputSurfaceClient output_surface_client;
  std::unique_ptr<OutputSurface> output_surface(
      FakeOutputSurface::Create3d(std::move(provider)));
  output_surface->BindToClient(&output_surface_client);

  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
      output_surface->context_provider());

  RendererSettings settings;
  FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                          resource_provider.get());
}

class ClearCountingGLES2Interface : public TestGLES2Interface {
 public:
  ClearCountingGLES2Interface() = default;

  MOCK_METHOD3(DiscardFramebufferEXT,
               void(GLenum target,
                    GLsizei numAttachments,
                    const GLenum* attachments));
  MOCK_METHOD1(Clear, void(GLbitfield mask));
};

TEST_F(GLRendererTest, OpaqueBackground) {
  auto gl_owned = std::make_unique<ClearCountingGLES2Interface>();
  gl_owned->set_have_discard_framebuffer(true);

  auto* gl = gl_owned.get();

  auto provider = TestContextProvider::Create(std::move(gl_owned));
  provider->BindToCurrentThread();

  cc::FakeOutputSurfaceClient output_surface_client;
  std::unique_ptr<OutputSurface> output_surface(
      FakeOutputSurface::Create3d(std::move(provider)));
  output_surface->BindToClient(&output_surface_client);

  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
      output_surface->context_provider());

  RendererSettings settings;
  FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                          resource_provider.get());
  renderer.Initialize();
  renderer.SetVisible(true);

  gfx::Size viewport_size(1, 1);
  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, AggregatedRenderPassId{1},
      gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
  root_pass->has_transparent_background = false;

  // On DEBUG builds, render passes with opaque background clear to blue to
  // easily see regions that were not drawn on the screen.
  EXPECT_CALL(*gl, DiscardFramebufferEXT(GL_FRAMEBUFFER, _, _))
      .With(Args<2, 1>(ElementsAre(GL_COLOR_EXT)))
      .Times(1);
#ifdef NDEBUG
  EXPECT_CALL(*gl, Clear(_)).Times(0);
#else
  EXPECT_CALL(*gl, Clear(_)).Times(1);
#endif
  DrawFrame(&renderer, viewport_size);
  Mock::VerifyAndClearExpectations(gl);
}

TEST_F(GLRendererTest, TransparentBackground) {
  auto gl_owned = std::make_unique<ClearCountingGLES2Interface>();
  auto* gl = gl_owned.get();
  gl_owned->set_have_discard_framebuffer(true);

  auto provider = TestContextProvider::Create(std::move(gl_owned));
  provider->BindToCurrentThread();

  cc::FakeOutputSurfaceClient output_surface_client;
  std::unique_ptr<OutputSurface> output_surface(
      FakeOutputSurface::Create3d(std::move(provider)));
  output_surface->BindToClient(&output_surface_client);

  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
      output_surface->context_provider());

  RendererSettings settings;
  FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                          resource_provider.get());
  renderer.Initialize();
  renderer.SetVisible(true);

  gfx::Size viewport_size(1, 1);
  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, AggregatedRenderPassId{1},
      gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
  root_pass->has_transparent_background = true;

  EXPECT_CALL(*gl, DiscardFramebufferEXT(GL_FRAMEBUFFER, 1, _)).Times(1);
  EXPECT_CALL(*gl, Clear(_)).Times(1);
  DrawFrame(&renderer, viewport_size);

  Mock::VerifyAndClearExpectations(gl);
}

TEST_F(GLRendererTest, OffscreenOutputSurface) {
  auto gl_owned = std::make_unique<ClearCountingGLES2Interface>();
  auto* gl = gl_owned.get();
  gl_owned->set_have_discard_framebuffer(true);

  auto provider = TestContextProvider::Create(std::move(gl_owned));
  provider->BindToCurrentThread();

  cc::FakeOutputSurfaceClient output_surface_client;
  std::unique_ptr<OutputSurface> output_surface(
      FakeOutputSurface::CreateOffscreen(std::move(provider)));
  output_surface->BindToClient(&output_surface_client);

  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
      output_surface->context_provider());

  RendererSettings settings;
  FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                          resource_provider.get());
  renderer.Initialize();
  renderer.SetVisible(true);

  gfx::Size viewport_size(1, 1);
  cc::AddRenderPass(&render_passes_in_draw_order_, AggregatedRenderPassId{1},
                    gfx::Rect(viewport_size), gfx::Transform(),
                    cc::FilterOperations());

  EXPECT_CALL(*gl, DiscardFramebufferEXT(GL_FRAMEBUFFER, _, _))
      .With(Args<2, 1>(ElementsAre(GL_COLOR_ATTACHMENT0)))
      .Times(1);
  EXPECT_CALL(*gl, Clear(_)).Times(AnyNumber());
  DrawFrame(&renderer, viewport_size);
  Mock::VerifyAndClearExpectations(gl);
}

class TextureStateTrackingGLES2Interface : public TestGLES2Interface {
 public:
  TextureStateTrackingGLES2Interface() : active_texture_(GL_INVALID_ENUM) {}

  MOCK_METHOD1(WaitSyncTokenCHROMIUM, void(const GLbyte* sync_token));
  MOCK_METHOD3(TexParameteri, void(GLenum target, GLenum pname, GLint param));
  MOCK_METHOD4(
      DrawElements,
      void(GLenum mode, GLsizei count, GLenum type, const void* indices));

  void ActiveTexture(GLenum texture) override {
    EXPECT_NE(texture, active_texture_);
    active_texture_ = texture;
  }

  GLenum active_texture() const { return active_texture_; }

 private:
  GLenum active_texture_;
};

#define EXPECT_FILTER_CALL(filter)                                          \
  EXPECT_CALL(*gl,                                                          \
              TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter)); \
  EXPECT_CALL(*gl, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter));

TEST_F(GLRendererTest, ActiveTextureState) {
  auto child_gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>();
  auto child_context_provider =
      TestContextProvider::Create(std::move(child_gl_owned));
  child_context_provider->BindToCurrentThread();
  auto child_resource_provider = std::make_unique<ClientResourceProvider>();

  auto gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>();
  gl_owned->set_have_extension_egl_image(true);
  auto* gl = gl_owned.get();

  auto provider = TestContextProvider::Create(std::move(gl_owned));
  provider->BindToCurrentThread();

  cc::FakeOutputSurfaceClient output_surface_client;
  std::unique_ptr<OutputSurface> output_surface(
      FakeOutputSurface::Create3d(std::move(provider)));
  output_surface->BindToClient(&output_surface_client);

  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
      output_surface->context_provider());

  RendererSettings settings;
  FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                          resource_provider.get());
  renderer.Initialize();
  renderer.SetVisible(true);

  // During initialization we are allowed to set any texture parameters.
  EXPECT_CALL(*gl, TexParameteri(_, _, _)).Times(AnyNumber());

  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, AggregatedRenderPassId{1},
      gfx::Rect(100, 100), gfx::Transform(), cc::FilterOperations());
  gpu::SyncToken mailbox_sync_token;
  cc::AddOneOfEveryQuadTypeInDisplayResourceProvider(
      root_pass, resource_provider.get(), child_resource_provider.get(),
      child_context_provider.get(), AggregatedRenderPassId{0},
      &mailbox_sync_token);

  EXPECT_EQ(12u, resource_provider->num_resources());
  renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);

  // Set up expected texture filter state transitions that match the quads
  // created in AppendOneOfEveryQuadType().
  Mock::VerifyAndClearExpectations(gl);
  {
    InSequence sequence;
    // The verified flush flag will be set by
    // ClientResourceProvider::PrepareSendToParent. Before checking if
    // the gpu::SyncToken matches, set this flag first.
    mailbox_sync_token.SetVerifyFlush();
    // In AddOneOfEveryQuadTypeInDisplayResourceProvider, resources are added
    // into RenderPass with the below order: resource6, resource1, resource8
    // (with mailbox), resource2, resource3, resource4, resource9, resource10,
    // resource11, resource12. resource8 has its own mailbox mailbox_sync_token.
    // The rest resources share a common default sync token.
    EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(2);
    EXPECT_CALL(*gl,
                WaitSyncTokenCHROMIUM(MatchesSyncToken(mailbox_sync_token)))
        .Times(1);
    EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(7);

    // yuv_quad is drawn with the default linear filter.
    for (int i = 0; i < 4; ++i) {
      EXPECT_FILTER_CALL(GL_LINEAR);
    }
    EXPECT_CALL(*gl, DrawElements(_, _, _, _));

    // tile_quad is drawn with GL_NEAREST because it is not transformed or
    // scaled.
    EXPECT_FILTER_CALL(GL_NEAREST);
    EXPECT_CALL(*gl, DrawElements(_, _, _, _));

    // transformed tile_quad
    EXPECT_FILTER_CALL(GL_LINEAR);
    EXPECT_CALL(*gl, DrawElements(_, _, _, _));

    // scaled tile_quad
    EXPECT_FILTER_CALL(GL_LINEAR);
    EXPECT_CALL(*gl, DrawElements(_, _, _, _));

    // texture_quad without nearest neighbor
    EXPECT_FILTER_CALL(GL_LINEAR);
    EXPECT_CALL(*gl, DrawElements(_, _, _, _));

    // texture_quad without nearest neighbor
    EXPECT_FILTER_CALL(GL_LINEAR);
    EXPECT_CALL(*gl, DrawElements(_, _, _, _));

    if (features::IsUsingFastPathForSolidColorQuad()) {
      // stream video and debug draw quads
      EXPECT_CALL(*gl, DrawElements(_, _, _, _)).Times(2);
    } else {
      // stream video, solid color, and debug draw quads
      EXPECT_CALL(*gl, DrawElements(_, _, _, _)).Times(3);
    }
  }

  gfx::Size viewport_size(100, 100);
  DrawFrame(&renderer, viewport_size);
  Mock::VerifyAndClearExpectations(gl);

  child_resource_provider->ShutdownAndReleaseAllResources();
}

class BufferSubDataTrackingGLES2Interface : public TestGLES2Interface {
 public:
  BufferSubDataTrackingGLES2Interface() = default;
  ~BufferSubDataTrackingGLES2Interface() override = default;

  void BufferSubData(GLenum target,
                     GLintptr offset,
                     GLsizeiptr size,
                     const void* data) override {
    if (target != GL_ARRAY_BUFFER)
      return;
    DCHECK_EQ(0, offset);
    last_array_data.resize(size);
    memcpy(last_array_data.data(), data, size);
  }

  std::vector<uint8_t> last_array_data;
};

TEST_F(GLRendererTest, DrawYUVVideoDrawQuadWithVisibleRect) {
  gfx::Size viewport_size(100, 100);

  auto mock_gl_owned = std::make_unique<BufferSubDataTrackingGLES2Interface>();
  BufferSubDataTrackingGLES2Interface* mock_gl = mock_gl_owned.get();
  auto provider = TestContextProvider::Create(std::move(mock_gl_owned));
  provider->BindToCurrentThread();

  cc::FakeOutputSurfaceClient output_surface_client;
  std::unique_ptr<OutputSurface> output_surface(
      FakeOutputSurface::Create3d(std::move(provider)));
  output_surface->BindToClient(&output_surface_client);

  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
      output_surface->context_provider());

  RendererSettings settings;
  FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                          resource_provider.get());
  renderer.Initialize();
  renderer.SetVisible(true);

  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, AggregatedRenderPassId{1},
      gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
  root_pass->has_transparent_background = false;

  gfx::Rect rect(viewport_size);
  gfx::Rect visible_rect(rect);
  gfx::RectF tex_coord_rect(0, 0, 1, 1);
  visible_rect.Inset(10, 20, 30, 40);

  SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState();
  shared_state->SetAll(gfx::Transform(), gfx::Rect(), rect,
                       gfx::MaskFilterInfo(), absl::nullopt, false, 1,
                       SkBlendMode::kSrcOver, 0);

  YUVVideoDrawQuad* quad =
      root_pass->CreateAndAppendDrawQuad<YUVVideoDrawQuad>();
  quad->SetNew(shared_state, rect, visible_rect, /*needs_blending=*/false,
               tex_coord_rect, tex_coord_rect, rect.size(), rect.size(),
               ResourceId(1), ResourceId(1), ResourceId(1), ResourceId(1),
               gfx::ColorSpace(), 0, 1.0, 8);

  DrawFrame(&renderer, viewport_size);

  ASSERT_EQ(96u, mock_gl->last_array_data.size());
  float* geometry_binding_vertexes =
      reinterpret_cast<float*>(mock_gl->last_array_data.data());

  const double kEpsilon = 1e-6;
  EXPECT_NEAR(-0.4f, geometry_binding_vertexes[0], kEpsilon);
  EXPECT_NEAR(-0.3f, geometry_binding_vertexes[1], kEpsilon);
  EXPECT_NEAR(0.1f, geometry_binding_vertexes[3], kEpsilon);
  EXPECT_NEAR(0.2f, geometry_binding_vertexes[4], kEpsilon);

  EXPECT_NEAR(0.2f, geometry_binding_vertexes[12], kEpsilon);
  EXPECT_NEAR(0.1f, geometry_binding_vertexes[13], kEpsilon);
  EXPECT_NEAR(0.7f, geometry_binding_vertexes[15], kEpsilon);
  EXPECT_NEAR(0.6f, geometry_binding_vertexes[16], kEpsilon);
}

class NoClearRootRenderPassMockGLES2Interface : public TestGLES2Interface {
 public:
  MOCK_METHOD1(Clear, void(GLbitfield mask));
  MOCK_METHOD4(
      DrawElements,
      void(GLenum mode, GLsizei count, GLenum type, const void* indices));
};

TEST_F(GLRendererTest, ShouldClearRootRenderPass) {
  auto mock_gl_owned =
      std::make_unique<NoClearRootRenderPassMockGLES2Interface>();
  NoClearRootRenderPassMockGLES2Interface* mock_gl = mock_gl_owned.get();

  auto provider = TestContextProvider::Create(std::move(mock_gl_owned));
  provider->BindToCurrentThread();

  cc::FakeOutputSurfaceClient output_surface_client;
  std::unique_ptr<OutputSurface> output_surface(
      FakeOutputSurface::Create3d(std::move(provider)));
  output_surface->BindToClient(&output_surface_client);

  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
      output_surface->context_provider());

  RendererSettings settings;
  settings.should_clear_root_render_pass = false;

  FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                          resource_provider.get());
  renderer.Initialize();
  renderer.SetVisible(true);

  gfx::Size viewport_size(10, 10);

  AggregatedRenderPassId child_pass_id{2};
  AggregatedRenderPass* child_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, child_pass_id, gfx::Rect(viewport_size),
      gfx::Transform(), cc::FilterOperations());
  cc::AddQuad(child_pass, gfx::Rect(viewport_size), SK_ColorBLUE);

  AggregatedRenderPassId root_pass_id{1};
  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
      gfx::Transform(), cc::FilterOperations());
  cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorGREEN);

  cc::AddRenderPassQuad(root_pass, child_pass);

#ifdef NDEBUG
  GLint clear_bits = GL_COLOR_BUFFER_BIT;
#else
  GLint clear_bits = GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
#endif

  // First render pass is not the root one, clearing should happen.
  EXPECT_CALL(*mock_gl, Clear(clear_bits)).Times(AtLeast(1));

  Expectation first_render_pass =
      EXPECT_CALL(*mock_gl, DrawElements(_, _, _, _)).Times(1);

  if (features::IsUsingFastPathForSolidColorQuad()) {
    // The second render pass is the root one, clearing should be prevented. The
    // one call is expected due to the solid color draw quad which uses glClear
    // to draw the quad.
    EXPECT_CALL(*mock_gl, Clear(clear_bits)).Times(1).After(first_render_pass);
  } else {
    // The second render pass is the root one, clearing should be prevented.
    EXPECT_CALL(*mock_gl, Clear(clear_bits)).Times(0).After(first_render_pass);
  }

  EXPECT_CALL(*mock_gl, DrawElements(_, _, _, _))
      .Times(AnyNumber())
      .After(first_render_pass);

  renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
  DrawFrame(&renderer, viewport_size);

  // In multiple render passes all but the root pass should clear the
  // framebuffer.
  Mock::VerifyAndClearExpectations(&mock_gl);
}

class ScissorTestOnClearCheckingGLES2Interface : public TestGLES2Interface {
 public:
  ScissorTestOnClearCheckingGLES2Interface() = default;

  void ClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) override {
    // RGBA - {0, 0, 0, 0} is used to clear the buffer before drawing onto the
    // render target. Any other color means a solid color draw quad is being
    // drawn.
    if (features::IsUsingFastPathForSolidColorQuad())
      is_drawing_solid_color_quad_ = !(r == 0 && g == 0 && b == 0 && a == 0);
  }

  void Clear(GLbitfield bits) override {
    // GL clear is also used to draw solid color draw quads.
    if ((bits & GL_COLOR_BUFFER_BIT) && is_drawing_solid_color_quad_)
      return;
    EXPECT_FALSE(scissor_enabled_);
  }

  void Enable(GLenum cap) override {
    if (cap == GL_SCISSOR_TEST)
      scissor_enabled_ = true;
  }

  void Disable(GLenum cap) override {
    if (cap == GL_SCISSOR_TEST)
      scissor_enabled_ = false;
  }

 private:
  bool scissor_enabled_ = false;
  bool is_drawing_solid_color_quad_ = false;
};

TEST_F(GLRendererTest, ScissorTestWhenClearing) {
  auto gl_owned = std::make_unique<ScissorTestOnClearCheckingGLES2Interface>();

  auto provider = TestContextProvider::Create(std::move(gl_owned));
  provider->BindToCurrentThread();

  cc::FakeOutputSurfaceClient output_surface_client;
  std::unique_ptr<OutputSurface> output_surface(
      FakeOutputSurface::Create3d(std::move(provider)));
  output_surface->BindToClient(&output_surface_client);

  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
      output_surface->context_provider());

  RendererSettings settings;
  FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                          resource_provider.get());
  renderer.Initialize();
  EXPECT_FALSE(renderer.use_partial_swap());
  renderer.SetVisible(true);

  gfx::Size viewport_size(100, 100);

  gfx::Rect grand_child_rect(25, 25);
  AggregatedRenderPassId grand_child_pass_id{3};
  AggregatedRenderPass* grand_child_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, grand_child_pass_id, grand_child_rect,
      gfx::Transform(), cc::FilterOperations());
  cc::AddClippedQuad(grand_child_pass, grand_child_rect, SK_ColorYELLOW);

  gfx::Rect child_rect(50, 50);
  AggregatedRenderPassId child_pass_id{2};
  AggregatedRenderPass* child_pass =
      cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                        child_rect, gfx::Transform(), cc::FilterOperations());
  cc::AddQuad(child_pass, child_rect, SK_ColorBLUE);

  AggregatedRenderPassId root_pass_id{1};
  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
      gfx::Transform(), cc::FilterOperations());
  cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorGREEN);

  cc::AddRenderPassQuad(root_pass, child_pass);
  cc::AddRenderPassQuad(child_pass, grand_child_pass);

  renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
  DrawFrame(&renderer, viewport_size);
}

class DiscardCheckingGLES2Interface : public TestGLES2Interface {
 public:
  DiscardCheckingGLES2Interface() = default;

  void DiscardFramebufferEXT(GLenum target,
                             GLsizei numAttachments,
                             const GLenum* attachments) override {
    ++discarded_;
  }

  int discarded() const { return discarded_; }
  void reset_discarded() { discarded_ = 0; }

 private:
  int discarded_ = 0;
};

TEST_F(GLRendererTest, NoDiscardOnPartialUpdates) {
  auto gl_owned = std::make_unique<DiscardCheckingGLES2Interface>();
  gl_owned->set_have_post_sub_buffer(true);
  gl_owned->set_have_discard_framebuffer(true);

  auto* gl = gl_owned.get();

  auto provider = TestContextProvider::Create(std::move(gl_owned));
  provider->BindToCurrentThread();

  cc::FakeOutputSurfaceClient output_surface_client;
  auto output_surface = FakeOutputSurface::Create3d(std::move(provider));
  output_surface->BindToClient(&output_surface_client);

  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
      output_surface->context_provider());

  RendererSettings settings;
  settings.partial_swap_enabled = true;
  FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                          resource_provider.get());
  renderer.Initialize();
  EXPECT_TRUE(renderer.use_partial_swap());
  renderer.SetVisible(true);

  gfx::Size viewport_size(100, 100);
  {
    // Draw one black frame to make sure the output surface is reshaped before
    // testes.
    AggregatedRenderPassId root_pass_id{1};
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorBLACK);
    root_pass->damage_rect = gfx::Rect(viewport_size);

    renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
    DrawFrame(&renderer, viewport_size);
    gl->reset_discarded();
  }
  {
    // Partial frame, should not discard.
    AggregatedRenderPassId root_pass_id{1};
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorGREEN);
    root_pass->damage_rect = gfx::Rect(2, 2, 3, 3);

    renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
    DrawFrame(&renderer, viewport_size);
    EXPECT_EQ(0, gl->discarded());
    gl->reset_discarded();
  }
  {
    // Full frame, should discard.
    AggregatedRenderPassId root_pass_id{1};
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorGREEN);
    root_pass->damage_rect = root_pass->output_rect;

    renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
    DrawFrame(&renderer, viewport_size);
    EXPECT_EQ(1, gl->discarded());
    gl->reset_discarded();
  }
  {
    // Full frame, external scissor is set, should not discard.
    output_surface->set_has_external_stencil_test(true);
    AggregatedRenderPassId root_pass_id{1};
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorGREEN);
    root_pass->damage_rect = root_pass->output_rect;
    root_pass->has_transparent_background = false;

    renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
    DrawFrame(&renderer, viewport_size);
    EXPECT_EQ(0, gl->discarded());
    gl->reset_discarded();
    output_surface->set_has_external_stencil_test(false);
  }
}

class ResourceTrackingGLES2Interface : public TestGLES2Interface {
 public:
  ResourceTrackingGLES2Interface() = default;
  ~ResourceTrackingGLES2Interface() override { CheckNoResources(); }

  void CheckNoResources() {
    EXPECT_TRUE(textures_.empty());
    EXPECT_TRUE(buffers_.empty());
    EXPECT_TRUE(framebuffers_.empty());
    EXPECT_TRUE(renderbuffers_.empty());
    EXPECT_TRUE(queries_.empty());
    EXPECT_TRUE(shaders_.empty());
    EXPECT_TRUE(programs_.empty());
  }

  void GenTextures(GLsizei n, GLuint* textures) override {
    GenIds(&textures_, n, textures);
  }

  void GenBuffers(GLsizei n, GLuint* buffers) override {
    GenIds(&buffers_, n, buffers);
  }

  void GenFramebuffers(GLsizei n, GLuint* framebuffers) override {
    GenIds(&framebuffers_, n, framebuffers);
  }

  void GenRenderbuffers(GLsizei n, GLuint* renderbuffers) override {
    GenIds(&renderbuffers_, n, renderbuffers);
  }

  void GenQueriesEXT(GLsizei n, GLuint* queries) override {
    GenIds(&queries_, n, queries);
  }

  GLuint CreateProgram() override { return GenId(&programs_); }

  GLuint CreateShader(GLenum type) override { return GenId(&shaders_); }

  void BindTexture(GLenum target, GLuint texture) override {
    CheckId(&textures_, texture);
  }

  void BindBuffer(GLenum target, GLuint buffer) override {
    CheckId(&buffers_, buffer);
  }

  void BindRenderbuffer(GLenum target, GLuint renderbuffer) override {
    CheckId(&renderbuffers_, renderbuffer);
  }

  void BindFramebuffer(GLenum target, GLuint framebuffer) override {
    CheckId(&framebuffers_, framebuffer);
  }

  void UseProgram(GLuint program) override { CheckId(&programs_, program); }

  void DeleteTextures(GLsizei n, const GLuint* textures) override {
    DeleteIds(&textures_, n, textures);
  }

  void DeleteBuffers(GLsizei n, const GLuint* buffers) override {
    DeleteIds(&buffers_, n, buffers);
  }

  void DeleteFramebuffers(GLsizei n, const GLuint* framebuffers) override {
    DeleteIds(&framebuffers_, n, framebuffers);
  }

  void DeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers) override {
    DeleteIds(&renderbuffers_, n, renderbuffers);
  }

  void DeleteQueriesEXT(GLsizei n, const GLuint* queries) override {
    DeleteIds(&queries_, n, queries);
  }

  void DeleteProgram(GLuint program) override { DeleteId(&programs_, program); }

  void DeleteShader(GLuint shader) override { DeleteId(&shaders_, shader); }

  void BufferData(GLenum target,
                  GLsizeiptr size,
                  const void* data,
                  GLenum usage) override {}

 private:
  GLuint GenId(std::set<GLuint>* resource_set) {
    GLuint id = next_id_++;
    resource_set->insert(id);
    return id;
  }

  void GenIds(std::set<GLuint>* resource_set, GLsizei n, GLuint* ids) {
    for (GLsizei i = 0; i < n; ++i)
      ids[i] = GenId(resource_set);
  }

  void CheckId(std::set<GLuint>* resource_set, GLuint id) {
    if (id == 0)
      return;
    EXPECT_TRUE(resource_set->find(id) != resource_set->end());
  }

  void DeleteId(std::set<GLuint>* resource_set, GLuint id) {
    if (id == 0)
      return;
    size_t num_erased = resource_set->erase(id);
    EXPECT_EQ(1u, num_erased);
  }

  void DeleteIds(std::set<GLuint>* resource_set, GLsizei n, const GLuint* ids) {
    for (GLsizei i = 0; i < n; ++i)
      DeleteId(resource_set, ids[i]);
  }

  GLuint next_id_ = 1;
  std::set<GLuint> textures_;
  std::set<GLuint> buffers_;
  std::set<GLuint> framebuffers_;
  std::set<GLuint> renderbuffers_;
  std::set<GLuint> queries_;
  std::set<GLuint> shaders_;
  std::set<GLuint> programs_;
};

TEST_F(GLRendererTest, NoResourceLeak) {
  auto gl_owned = std::make_unique<ResourceTrackingGLES2Interface>();
  auto* gl = gl_owned.get();

  auto provider = TestContextProvider::Create(std::move(gl_owned));
  provider->BindToCurrentThread();

  cc::FakeOutputSurfaceClient output_surface_client;
  auto output_surface = FakeOutputSurface::Create3d(std::move(provider));
  output_surface->BindToClient(&output_surface_client);

  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
      output_surface->context_provider());

  {
    RendererSettings settings;
    FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                            resource_provider.get());
    renderer.Initialize();
    renderer.SetVisible(true);

    gfx::Size viewport_size(100, 100);

    AggregatedRenderPassId root_pass_id{1};
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorGREEN);
    root_pass->damage_rect = gfx::Rect(2, 2, 3, 3);

    renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
    DrawFrame(&renderer, viewport_size);
  }
  gl->CheckNoResources();
}

class DrawElementsGLES2Interface : public TestGLES2Interface {
 public:
  MOCK_METHOD4(
      DrawElements,
      void(GLenum mode, GLsizei count, GLenum type, const void* indices));
};

class GLRendererSkipTest : public GLRendererTest {
 protected:
  GLRendererSkipTest() {
    auto gl_owned = std::make_unique<StrictMock<DrawElementsGLES2Interface>>();
    gl_owned->set_have_post_sub_buffer(true);
    gl_ = gl_owned.get();

    auto provider = TestContextProvider::Create(std::move(gl_owned));
    provider->BindToCurrentThread();

    output_surface_ = FakeOutputSurface::Create3d(std::move(provider));
    output_surface_->BindToClient(&output_surface_client_);

    resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
        output_surface_->context_provider());
    settings_.partial_swap_enabled = true;
    renderer_ = std::make_unique<FakeRendererGL>(&settings_, &debug_settings_,
                                                 output_surface_.get(),
                                                 resource_provider_.get());
    renderer_->Initialize();
    renderer_->SetVisible(true);
  }

  void DrawBlackFrame(const gfx::Size& viewport_size) {
    // The feature enables a faster path to draw solid color quads that does not
    // use GL draw calls but instead uses glClear.
    if (!features::IsUsingFastPathForSolidColorQuad())
      EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(1);

    AggregatedRenderPassId root_pass_id{1};
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    root_pass->damage_rect = gfx::Rect(viewport_size);
    cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorBLACK);
    renderer_->DecideRenderPassAllocationsForFrame(
        render_passes_in_draw_order_);
    DrawFrame(renderer_.get(), viewport_size);
    Mock::VerifyAndClearExpectations(gl_);
  }

  StrictMock<DrawElementsGLES2Interface>* gl_;
  RendererSettings settings_;
  cc::FakeOutputSurfaceClient output_surface_client_;
  std::unique_ptr<FakeOutputSurface> output_surface_;
  std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
  std::unique_ptr<FakeRendererGL> renderer_;
};

TEST_F(GLRendererSkipTest, DrawQuad) {
  gfx::Size viewport_size(100, 100);
  gfx::Rect quad_rect = gfx::Rect(20, 20, 20, 20);

  // Draw the a black frame to make sure output surface is reshaped before
  // tests.
  DrawBlackFrame(viewport_size);

  EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(1);

  AggregatedRenderPassId root_pass_id{1};
  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
      gfx::Transform(), cc::FilterOperations());
  root_pass->damage_rect = gfx::Rect(0, 0, 25, 25);
  cc::AddQuad(root_pass, quad_rect, SK_ColorGREEN);

  // Add rounded corners to the solid color draw quad so that the fast path
  // of drawing using glClear is not used.
  root_pass->shared_quad_state_list.front()->mask_filter_info =
      gfx::MaskFilterInfo(gfx::RRectF(gfx::RectF(quad_rect), 2.f));

  renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
  DrawFrame(renderer_.get(), viewport_size);
}

TEST_F(GLRendererSkipTest, SkipVisibleRect) {
  gfx::Size viewport_size(100, 100);
  gfx::Rect quad_rect = gfx::Rect(0, 0, 40, 40);

  // Draw the a black frame to make sure output surface is reshaped before
  // tests.
  DrawBlackFrame(viewport_size);

  AggregatedRenderPassId root_pass_id{1};
  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
      gfx::Transform(), cc::FilterOperations());
  root_pass->damage_rect = gfx::Rect(0, 0, 10, 10);
  cc::AddQuad(root_pass, quad_rect, SK_ColorGREEN);
  root_pass->shared_quad_state_list.front()->clip_rect =
      gfx::Rect(0, 0, 40, 40);
  root_pass->quad_list.front()->visible_rect = gfx::Rect(20, 20, 20, 20);

  // Add rounded corners to the solid color draw quad so that the fast path
  // of drawing using glClear is not used.
  root_pass->shared_quad_state_list.front()->mask_filter_info =
      gfx::MaskFilterInfo(gfx::RRectF(gfx::RectF(quad_rect), 1.f));

  renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
  DrawFrame(renderer_.get(), viewport_size);
  // DrawElements should not be called because the visible rect is outside the
  // scissor, even though the clip rect and quad rect intersect the scissor.
}

TEST_F(GLRendererSkipTest, SkipClippedQuads) {
  gfx::Size viewport_size(100, 100);
  gfx::Rect quad_rect = gfx::Rect(25, 25, 90, 90);

  // Draw the a black frame to make sure output surface is reshaped before
  // tests.
  DrawBlackFrame(viewport_size);

  AggregatedRenderPassId root_pass_id{1};

  auto* root_pass = cc::AddRenderPass(&render_passes_in_draw_order_,
                                      root_pass_id, gfx::Rect(viewport_size),
                                      gfx::Transform(), cc::FilterOperations());
  root_pass->damage_rect = gfx::Rect(0, 0, 25, 25);
  cc::AddClippedQuad(root_pass, quad_rect, SK_ColorGREEN);
  root_pass->quad_list.front()->rect = gfx::Rect(20, 20, 20, 20);

  renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
  DrawFrame(renderer_.get(), viewport_size);
  // DrawElements should not be called because the clip rect is outside the
  // scissor.
}

TEST_F(GLRendererTest, DrawFramePreservesFramebuffer) {
  // When using render-to-FBO to display the surface, all rendering is done
  // to a non-zero FBO. Make sure that the framebuffer is always restored to
  // the correct framebuffer during rendering, if changed.
  // Note: there is one path that will set it to 0, but that is after the render
  // has finished.
  cc::FakeOutputSurfaceClient output_surface_client;
  std::unique_ptr<FakeOutputSurface> output_surface(
      FakeOutputSurface::Create3d());
  output_surface->BindToClient(&output_surface_client);

  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
      output_surface->context_provider());

  RendererSettings settings;
  FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                          resource_provider.get());
  renderer.Initialize();
  EXPECT_FALSE(renderer.use_partial_swap());
  renderer.SetVisible(true);

  gfx::Size viewport_size(100, 100);
  gfx::Rect quad_rect = gfx::Rect(20, 20, 20, 20);

  AggregatedRenderPassId root_pass_id{1};
  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
      gfx::Transform(), cc::FilterOperations());
  cc::AddClippedQuad(root_pass, quad_rect, SK_ColorGREEN);

  unsigned fbo;
  gpu::gles2::GLES2Interface* gl =
      output_surface->context_provider()->ContextGL();
  gl->GenFramebuffers(1, &fbo);
  output_surface->set_framebuffer(fbo, GL_RGB);

  renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
  DrawFrame(&renderer, viewport_size);

  int bound_fbo;
  gl->GetIntegerv(GL_FRAMEBUFFER_BINDING, &bound_fbo);
  EXPECT_EQ(static_cast<int>(fbo), bound_fbo);
}

TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) {
  gfx::Size viewport_size(60, 75);

  gfx::Rect child_rect(50, 50);
  AggregatedRenderPassId child_pass_id{2};
  AggregatedRenderPass* child_pass;

  AggregatedRenderPassId root_pass_id{1};
  AggregatedRenderPass* root_pass;

  auto transfer_resource = TransferableResource::MakeGL(
      gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(),
      child_rect.size(), false /* is_overlay_candidate */);
  ResourceId mask = child_resource_provider_->ImportResource(transfer_resource,
                                                             base::DoNothing());

  // Return the mapped resource id.
  std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
      cc::SendResourceAndGetChildToParentMap({mask}, resource_provider_.get(),
                                             child_resource_provider_.get(),
                                             child_context_provider_.get());
  ResourceId mapped_mask = resource_map[mask];

  float matrix[20];
  float amount = 0.5f;
  matrix[0] = 0.213f + 0.787f * amount;
  matrix[1] = 0.715f - 0.715f * amount;
  matrix[2] = 1.f - (matrix[0] + matrix[1]);
  matrix[3] = matrix[4] = 0;
  matrix[5] = 0.213f - 0.213f * amount;
  matrix[6] = 0.715f + 0.285f * amount;
  matrix[7] = 1.f - (matrix[5] + matrix[6]);
  matrix[8] = matrix[9] = 0;
  matrix[10] = 0.213f - 0.213f * amount;
  matrix[11] = 0.715f - 0.715f * amount;
  matrix[12] = 1.f - (matrix[10] + matrix[11]);
  matrix[13] = matrix[14] = 0;
  matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0;
  matrix[18] = 1;
  cc::FilterOperations filters;
  filters.Append(cc::FilterOperation::CreateReferenceFilter(
      sk_make_sp<cc::ColorFilterPaintFilter>(SkColorFilters::Matrix(matrix),
                                             nullptr)));

  gfx::Transform transform_causing_aa;
  transform_causing_aa.Rotate(20.0);

  for (int i = 0; i <= LAST_BLEND_MODE; ++i) {
    BlendMode blend_mode = static_cast<BlendMode>(i);
    SkBlendMode xfer_mode = BlendModeToSkXfermode(blend_mode);
    settings_.force_blending_with_shaders = (blend_mode != BLEND_MODE_NONE);
    // RenderPassProgram
    render_passes_in_draw_order_.clear();
    child_pass =
        cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                          child_rect, gfx::Transform(), cc::FilterOperations());

    root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
                                  gfx::Rect(viewport_size), gfx::Transform(),
                                  cc::FilterOperations());

    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          gfx::Transform(), xfer_mode);

    renderer_->DecideRenderPassAllocationsForFrame(
        render_passes_in_draw_order_);
    DrawFrame(renderer_.get(), viewport_size);
    TestRenderPassProgram(TEX_COORD_PRECISION_MEDIUM, blend_mode);

    // RenderPassColorMatrixProgram
    render_passes_in_draw_order_.clear();

    child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                                   child_rect, transform_causing_aa, filters);

    root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
                                  gfx::Rect(viewport_size), gfx::Transform(),
                                  cc::FilterOperations());

    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          gfx::Transform(), xfer_mode);

    renderer_->DecideRenderPassAllocationsForFrame(
        render_passes_in_draw_order_);
    DrawFrame(renderer_.get(), viewport_size);
    TestRenderPassColorMatrixProgram(TEX_COORD_PRECISION_MEDIUM, blend_mode);

    // RenderPassMaskProgram
    render_passes_in_draw_order_.clear();

    child_pass =
        cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                          child_rect, gfx::Transform(), cc::FilterOperations());

    root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
                                  gfx::Rect(viewport_size), gfx::Transform(),
                                  cc::FilterOperations());

    cc::AddRenderPassQuad(root_pass, child_pass, mapped_mask, gfx::Transform(),
                          xfer_mode);

    renderer_->DecideRenderPassAllocationsForFrame(
        render_passes_in_draw_order_);
    DrawFrame(renderer_.get(), viewport_size);
    TestRenderPassMaskProgram(TEX_COORD_PRECISION_MEDIUM, SAMPLER_TYPE_2D,
                              blend_mode);

    // RenderPassMaskColorMatrixProgram
    render_passes_in_draw_order_.clear();

    child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                                   child_rect, gfx::Transform(), filters);

    root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
                                  gfx::Rect(viewport_size), gfx::Transform(),
                                  cc::FilterOperations());

    cc::AddRenderPassQuad(root_pass, child_pass, mapped_mask, gfx::Transform(),
                          xfer_mode);

    renderer_->DecideRenderPassAllocationsForFrame(
        render_passes_in_draw_order_);
    DrawFrame(renderer_.get(), viewport_size);
    TestRenderPassMaskColorMatrixProgram(TEX_COORD_PRECISION_MEDIUM,
                                         SAMPLER_TYPE_2D, blend_mode);

    // RenderPassProgramAA
    render_passes_in_draw_order_.clear();

    child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                                   child_rect, transform_causing_aa,
                                   cc::FilterOperations());

    root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
                                  gfx::Rect(viewport_size), gfx::Transform(),
                                  cc::FilterOperations());

    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          transform_causing_aa, xfer_mode);

    renderer_->DecideRenderPassAllocationsForFrame(
        render_passes_in_draw_order_);
    DrawFrame(renderer_.get(), viewport_size);
    TestRenderPassProgramAA(TEX_COORD_PRECISION_MEDIUM, blend_mode);

    // RenderPassColorMatrixProgramAA
    render_passes_in_draw_order_.clear();

    child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                                   child_rect, transform_causing_aa, filters);

    root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
                                  gfx::Rect(viewport_size), gfx::Transform(),
                                  cc::FilterOperations());

    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          transform_causing_aa, xfer_mode);

    renderer_->DecideRenderPassAllocationsForFrame(
        render_passes_in_draw_order_);
    DrawFrame(renderer_.get(), viewport_size);
    TestRenderPassColorMatrixProgramAA(TEX_COORD_PRECISION_MEDIUM, blend_mode);

    // RenderPassMaskProgramAA
    render_passes_in_draw_order_.clear();

    child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                                   child_rect, transform_causing_aa,
                                   cc::FilterOperations());

    root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
                                  gfx::Rect(viewport_size), gfx::Transform(),
                                  cc::FilterOperations());

    cc::AddRenderPassQuad(root_pass, child_pass, mapped_mask,
                          transform_causing_aa, xfer_mode);

    renderer_->DecideRenderPassAllocationsForFrame(
        render_passes_in_draw_order_);
    DrawFrame(renderer_.get(), viewport_size);
    TestRenderPassMaskProgramAA(TEX_COORD_PRECISION_MEDIUM, SAMPLER_TYPE_2D,
                                blend_mode);

    // RenderPassMaskColorMatrixProgramAA
    render_passes_in_draw_order_.clear();

    child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                                   child_rect, transform_causing_aa, filters);

    root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
                                  gfx::Rect(viewport_size),
                                  transform_causing_aa, cc::FilterOperations());

    cc::AddRenderPassQuad(root_pass, child_pass, mapped_mask,
                          transform_causing_aa, xfer_mode);

    renderer_->DecideRenderPassAllocationsForFrame(
        render_passes_in_draw_order_);
    DrawFrame(renderer_.get(), viewport_size);
    TestRenderPassMaskColorMatrixProgramAA(TEX_COORD_PRECISION_MEDIUM,
                                           SAMPLER_TYPE_2D, blend_mode);
  }
}

// At this time, the AA code path cannot be taken if the surface's rect would
// project incorrectly by the given transform, because of w<0 clipping.
TEST_F(GLRendererShaderTest, DrawRenderPassQuadSkipsAAForClippingTransform) {
  gfx::Rect child_rect(50, 50);
  AggregatedRenderPassId child_pass_id{2};
  AggregatedRenderPass* child_pass;

  gfx::Size viewport_size(100, 100);
  AggregatedRenderPassId root_pass_id{1};
  AggregatedRenderPass* root_pass;

  gfx::Transform transform_preventing_aa;
  transform_preventing_aa.ApplyPerspectiveDepth(40.0);
  transform_preventing_aa.RotateAboutYAxis(-20.0);
  transform_preventing_aa.Scale(30.0, 1.0);

  // Verify that the test transform and test rect actually do cause the clipped
  // flag to trigger. Otherwise we are not testing the intended scenario.
  bool clipped = false;
  cc::MathUtil::MapQuad(transform_preventing_aa,
                        gfx::QuadF(gfx::RectF(child_rect)), &clipped);
  ASSERT_TRUE(clipped);

  child_pass = cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                                 child_rect, transform_preventing_aa,
                                 cc::FilterOperations());

  root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
                                gfx::Rect(viewport_size), gfx::Transform(),
                                cc::FilterOperations());

  cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                        transform_preventing_aa, SkBlendMode::kSrcOver);

  renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
  DrawFrame(renderer_.get(), viewport_size);

  // If use_aa incorrectly ignores clipping, it will use the
  // RenderPassProgramAA shader instead of the RenderPassProgram.
  TestRenderPassProgram(TEX_COORD_PRECISION_MEDIUM, BLEND_MODE_NONE);
}

TEST_F(GLRendererShaderTest, DrawSolidColorShader) {
  gfx::Size viewport_size(30, 30);  // Don't translate out of the viewport.
  gfx::Size quad_size(3, 3);
  AggregatedRenderPassId root_pass_id{1};
  AggregatedRenderPass* root_pass;

  gfx::Transform pixel_aligned_transform_causing_aa;
  pixel_aligned_transform_causing_aa.Translate(25.5f, 25.5f);
  pixel_aligned_transform_causing_aa.Scale(0.5f, 0.5f);

  root_pass = cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
                                gfx::Rect(viewport_size), gfx::Transform(),
                                cc::FilterOperations());
  cc::AddTransformedQuad(root_pass, gfx::Rect(quad_size), SK_ColorYELLOW,
                         pixel_aligned_transform_causing_aa);

  renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
  DrawFrame(renderer_.get(), viewport_size);

  TestSolidColorProgramAA();
}

class OutputSurfaceMockGLES2Interface : public TestGLES2Interface {
 public:
  OutputSurfaceMockGLES2Interface() = default;

  // Specifically override methods even if they are unused (used in conjunction
  // with StrictMock). We need to make sure that GLRenderer does not issue
  // framebuffer-related GLuint calls directly. Instead these are supposed to go
  // through the OutputSurface abstraction.
  MOCK_METHOD2(BindFramebuffer, void(GLenum target, GLuint framebuffer));
  MOCK_METHOD5(ResizeCHROMIUM,
               void(GLuint width,
                    GLuint height,
                    float device_scale,
                    GLcolorSpace color_space,
                    GLboolean has_alpha));
  MOCK_METHOD4(
      DrawElements,
      void(GLenum mode, GLsizei count, GLenum type, const void* indices));
};

class MockOutputSurface : public OutputSurface {
 public:
  explicit MockOutputSurface(scoped_refptr<ContextProvider> provider)
      : OutputSurface(std::move(provider)) {}
  ~MockOutputSurface() override {}

  void BindToClient(OutputSurfaceClient*) override {}
  unsigned UpdateGpuFence() override { return 0; }

  MOCK_METHOD0(EnsureBackbuffer, void());
  MOCK_METHOD0(DiscardBackbuffer, void());
  MOCK_METHOD5(Reshape,
               void(const gfx::Size& size,
                    float scale_factor,
                    const gfx::ColorSpace& color_space,
                    gfx::BufferFormat format,
                    bool use_stencil));
  MOCK_METHOD0(BindFramebuffer, void());
  MOCK_METHOD1(SetDrawRectangle, void(const gfx::Rect&));
  MOCK_METHOD1(SetEnableDCLayers, void(bool));
  MOCK_METHOD0(GetFramebufferCopyTextureFormat, GLenum());
  MOCK_METHOD1(SwapBuffers_, void(OutputSurfaceFrame& frame));  // NOLINT
  void SwapBuffers(OutputSurfaceFrame frame) override { SwapBuffers_(frame); }
  MOCK_CONST_METHOD0(IsDisplayedAsOverlayPlane, bool());
  MOCK_CONST_METHOD0(GetOverlayTextureId, unsigned());
  MOCK_CONST_METHOD0(HasExternalStencilTest, bool());
  MOCK_METHOD0(ApplyExternalStencil, void());
  MOCK_METHOD1(SetUpdateVSyncParametersCallback,
               void(UpdateVSyncParametersCallback));
  MOCK_METHOD1(SetDisplayTransformHint, void(gfx::OverlayTransform));

  gfx::OverlayTransform GetDisplayTransform() override {
    return gfx::OVERLAY_TRANSFORM_NONE;
  }
};

class MockOutputSurfaceTest : public GLRendererTest {
 protected:
  void SetUp() override {
    auto gl = std::make_unique<StrictMock<OutputSurfaceMockGLES2Interface>>();
    gl->set_have_post_sub_buffer(true);
    gl_ = gl.get();
    auto provider = TestContextProvider::Create(std::move(gl));
    provider->BindToCurrentThread();
    output_surface_ =
        std::make_unique<StrictMock<MockOutputSurface>>(std::move(provider));

    output_surface_->BindToClient(&output_surface_client_);

    resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
        output_surface_->context_provider());

    renderer_ = std::make_unique<FakeRendererGL>(&settings_, &debug_settings_,
                                                 output_surface_.get(),
                                                 resource_provider_.get());
    renderer_->Initialize();

    EXPECT_CALL(*output_surface_, EnsureBackbuffer()).Times(1);
    renderer_->SetVisible(true);
    Mock::VerifyAndClearExpectations(output_surface_.get());
  }

  void SwapBuffers() {
    renderer_->SwapBuffers(DirectRenderer::SwapFrameData());
  }

  void DrawFrame(float device_scale_factor,
                 const gfx::Size& viewport_size,
                 bool transparent) {
    gfx::BufferFormat format = transparent ? gfx::BufferFormat::RGBA_8888
                                           : gfx::BufferFormat::RGBX_8888;
    AggregatedRenderPassId render_pass_id{1};
    AggregatedRenderPass* render_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, render_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddQuad(render_pass, gfx::Rect(viewport_size), SK_ColorGREEN);
    render_pass->has_transparent_background = transparent;

    EXPECT_CALL(*output_surface_, EnsureBackbuffer()).WillRepeatedly(Return());

    EXPECT_CALL(*output_surface_,
                Reshape(viewport_size, device_scale_factor, _, format, _))
        .Times(1);

    EXPECT_CALL(*output_surface_, BindFramebuffer()).Times(1);

    EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(1);

    renderer_->DecideRenderPassAllocationsForFrame(
        render_passes_in_draw_order_);
    SurfaceDamageRectList surface_damage_rect_list;
    renderer_->DrawFrame(&render_passes_in_draw_order_, device_scale_factor,
                         viewport_size, gfx::DisplayColorSpaces(),
                         std::move(surface_damage_rect_list));
  }

  RendererSettings settings_;
  cc::FakeOutputSurfaceClient output_surface_client_;
  OutputSurfaceMockGLES2Interface* gl_ = nullptr;
  std::unique_ptr<StrictMock<MockOutputSurface>> output_surface_;
  std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
  std::unique_ptr<FakeRendererGL> renderer_;
};

TEST_F(MockOutputSurfaceTest, BackbufferDiscard) {
  // Drop backbuffer on hide.
  EXPECT_CALL(*output_surface_, DiscardBackbuffer()).Times(1);
  renderer_->SetVisible(false);
  Mock::VerifyAndClearExpectations(output_surface_.get());

  // Restore backbuffer on show.
  EXPECT_CALL(*output_surface_, EnsureBackbuffer()).Times(1);
  renderer_->SetVisible(true);
  Mock::VerifyAndClearExpectations(output_surface_.get());
}

#if defined(OS_WIN)
class MockDCLayerOverlayProcessor : public DCLayerOverlayProcessor {
 public:
  MockDCLayerOverlayProcessor()
      : DCLayerOverlayProcessor(&debug_settings_,
                                /*allowed_yuv_overlay_count=*/1,
                                true) {}
  ~MockDCLayerOverlayProcessor() override = default;
  MOCK_METHOD8(Process,
               void(DisplayResourceProvider* resource_provider,
                    const gfx::RectF& display_rect,
                    const FilterOperationsMap& render_pass_filters,
                    const FilterOperationsMap& render_pass_backdrop_filters,
                    AggregatedRenderPassList* render_passes,
                    gfx::Rect* damage_rect,
                    SurfaceDamageRectList surface_damage_rect_list,
                    DCLayerOverlayList* dc_layer_overlays));

 protected:
  DebugRendererSettings debug_settings_;
};
class TestOverlayProcessor : public OverlayProcessorWin {
 public:
  explicit TestOverlayProcessor(OutputSurface* output_surface)
      : OverlayProcessorWin(output_surface,
                            std::make_unique<MockDCLayerOverlayProcessor>()) {}
  ~TestOverlayProcessor() override = default;

  MockDCLayerOverlayProcessor* GetTestProcessor() {
    return static_cast<MockDCLayerOverlayProcessor*>(GetOverlayProcessor());
  }
};
#elif defined(OS_APPLE)
class MockCALayerOverlayProcessor : public CALayerOverlayProcessor {
 public:
  MockCALayerOverlayProcessor() : CALayerOverlayProcessor(true) {}
  ~MockCALayerOverlayProcessor() override = default;

  MOCK_METHOD6(
      ProcessForCALayerOverlays,
      bool(AggregatedRenderPass* render_pass,
           DisplayResourceProvider* resource_provider,
           const gfx::RectF& display_rect,
           const base::flat_map<AggregatedRenderPassId, cc::FilterOperations*>&
               render_pass_filters,
           const base::flat_map<AggregatedRenderPassId, cc::FilterOperations*>&
               render_pass_backdrop_filters,
           CALayerOverlayList* ca_layer_overlays));
};

class TestOverlayProcessor : public OverlayProcessorMac {
 public:
  explicit TestOverlayProcessor(OutputSurface* output_surface)
      : OverlayProcessorMac(std::make_unique<MockCALayerOverlayProcessor>()) {}
  ~TestOverlayProcessor() override = default;

  MockCALayerOverlayProcessor* GetTestProcessor() {
    return static_cast<MockCALayerOverlayProcessor*>(GetOverlayProcessor());
  }
};

#elif defined(OS_ANDROID) || defined(USE_OZONE)

class TestOverlayProcessor : public OverlayProcessorUsingStrategy {
 public:
  class Strategy : public OverlayProcessorUsingStrategy::Strategy {
   public:
    Strategy() = default;
    ~Strategy() override = default;

    MOCK_METHOD8(
        Attempt,
        bool(const skia::Matrix44& output_color_matrix,
             const OverlayProcessorInterface::FilterOperationsMap&
                 render_pass_backdrop_filters,
             DisplayResourceProvider* resource_provider,
             AggregatedRenderPassList* render_pass_list,
             SurfaceDamageRectList* surface_damage_rect_list,
             const OverlayProcessorInterface::OutputSurfaceOverlayPlane*
                 primary_surface,
             OverlayCandidateList* candidates,
             std::vector<gfx::Rect>* content_bounds));

    void ProposePrioritized(
        const skia::Matrix44& output_color_matrix,
        const FilterOperationsMap& render_pass_backdrop_filters,
        DisplayResourceProvider* resource_provider,
        AggregatedRenderPassList* render_pass_list,
        SurfaceDamageRectList* surface_damage_rect_list,
        const PrimaryPlane* primary_plane,
        OverlayProposedCandidateList* candidates,
        std::vector<gfx::Rect>* content_bounds) override {
      auto* render_pass = render_pass_list->back().get();
      QuadList& quad_list = render_pass->quad_list;
      OverlayCandidate candidate;
      candidates->push_back({quad_list.end(), candidate, this});
    }

    MOCK_METHOD9(AttemptPrioritized,
                 bool(const skia::Matrix44& output_color_matrix,
                      const FilterOperationsMap& render_pass_backdrop_filters,
                      DisplayResourceProvider* resource_provider,
                      AggregatedRenderPassList* render_pass_list,
                      SurfaceDamageRectList* surface_damage_rect_list,
                      const PrimaryPlane* primary_plane,
                      OverlayCandidateList* candidates,
                      std::vector<gfx::Rect>* content_bounds,
                      const OverlayProposedCandidate& proposed_candidate));

    MOCK_METHOD2(CommitCandidate,
                 void(const OverlayProposedCandidate& proposed_candidate,
                      AggregatedRenderPass* render_pass));
  };

  bool IsOverlaySupported() const override { return true; }

  // A list of possible overlay candidates is presented to this function.
  // The expected result is that those candidates that can be in a separate
  // plane are marked with |overlay_handled| set to true, otherwise they are
  // to be traditionally composited. Candidates with |overlay_handled| set to
  // true must also have their |display_rect| converted to integer
  // coordinates if necessary.
  void CheckOverlaySupport(
      const OverlayProcessorInterface::OutputSurfaceOverlayPlane* primary_plane,
      OverlayCandidateList* surfaces) override {}

  Strategy& strategy() {
    auto* strategy = strategies_.back().get();
    return *(static_cast<Strategy*>(strategy));
  }

  MOCK_CONST_METHOD0(NeedsSurfaceDamageRectList, bool());
  explicit TestOverlayProcessor(OutputSurface* output_surface)
      : OverlayProcessorUsingStrategy() {
    strategies_.push_back(std::make_unique<Strategy>());
    prioritization_config_.changing_threshold = false;
    prioritization_config_.damage_rate_threshold = false;
  }
  ~TestOverlayProcessor() override = default;
};
#else  // Default to no overlay.
class TestOverlayProcessor : public OverlayProcessorStub {
 public:
  explicit TestOverlayProcessor(OutputSurface* output_surface)
      : OverlayProcessorStub() {}
  ~TestOverlayProcessor() override = default;
};
#endif

void MailboxReleased(const gpu::SyncToken& sync_token, bool lost_resource) {}

static void CollectResources(std::vector<ReturnedResource>* array,
                             std::vector<ReturnedResource> returned) {
  array->insert(array->end(), std::make_move_iterator(returned.begin()),
                std::make_move_iterator(returned.end()));
}

TEST_F(GLRendererTest, DontOverlayWithCopyRequests) {
  cc::FakeOutputSurfaceClient output_surface_client;
  std::unique_ptr<FakeOutputSurface> output_surface(
      FakeOutputSurface::Create3d());
#if defined(OS_WIN)
  output_surface->set_supports_dc_layers(true);
#endif
  output_surface->BindToClient(&output_surface_client);

  auto parent_resource_provider = std::make_unique<DisplayResourceProviderGL>(
      output_surface->context_provider());

  auto child_context_provider = TestContextProvider::Create();
  child_context_provider->BindToCurrentThread();
  auto child_resource_provider = std::make_unique<ClientResourceProvider>();

  auto transfer_resource = TransferableResource::MakeGL(
      gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(),
      gfx::Size(256, 256), true);
  auto release_callback = base::BindOnce(&MailboxReleased);
  ResourceId resource_id = child_resource_provider->ImportResource(
      transfer_resource, std::move(release_callback));

  std::vector<ReturnedResource> returned_to_child;
  int child_id = parent_resource_provider->CreateChild(
      base::BindRepeating(&CollectResources, &returned_to_child), SurfaceId());

  // Transfer resource to the parent.
  std::vector<ResourceId> resource_ids_to_transfer;
  resource_ids_to_transfer.push_back(resource_id);
  std::vector<TransferableResource> list;
  child_resource_provider->PrepareSendToParent(
      resource_ids_to_transfer, &list,
      static_cast<RasterContextProvider*>(child_context_provider.get()));
  parent_resource_provider->ReceiveFromChild(child_id, list);

  // In DisplayResourceProvider's namespace, use the mapped resource id.
  std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
      parent_resource_provider->GetChildToParentMap(child_id);
  ResourceId parent_resource_id = resource_map[list[0].id];

  auto processor = std::make_unique<TestOverlayProcessor>(output_surface.get());

  RendererSettings settings;
  FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                          parent_resource_provider.get(), processor.get(),
                          base::ThreadTaskRunnerHandle::Get());
  renderer.Initialize();
  renderer.SetVisible(true);

#if defined(OS_APPLE)
  MockCALayerOverlayProcessor* mock_ca_processor =
      processor->GetTestProcessor();
#elif defined(OS_WIN)
  MockDCLayerOverlayProcessor* dc_processor = processor->GetTestProcessor();
#endif

  gfx::Size viewport_size(1, 1);
  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, AggregatedRenderPassId{1},
      gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
  root_pass->has_transparent_background = false;
  root_pass->copy_requests.push_back(CopyOutputRequest::CreateStubForTesting());

  bool needs_blending = false;
  bool premultiplied_alpha = false;
  bool flipped = false;
  bool nearest_neighbor = false;
  float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};

  TextureDrawQuad* overlay_quad =
      root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
  overlay_quad->SetNew(
      root_pass->CreateAndAppendSharedQuadState(), gfx::Rect(viewport_size),
      gfx::Rect(viewport_size), needs_blending, parent_resource_id,
      premultiplied_alpha, gfx::PointF(0, 0), gfx::PointF(1, 1),
      SK_ColorTRANSPARENT, vertex_opacity, flipped, nearest_neighbor,
      /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear);

  // DirectRenderer::DrawFrame calls into OverlayProcessor::ProcessForOverlays.
  // Attempt will be called for each strategy in OverlayProcessor. We have
  // added a fake strategy, so checking for Attempt calls checks if there was
  // any attempt to overlay, which there shouldn't be. We can't use the quad
  // list because the render pass is cleaned up by DrawFrame.
#if defined(USE_OZONE) || defined(OS_ANDROID)
  if (features::IsOverlayPrioritizationEnabled()) {
    EXPECT_CALL(processor->strategy(),
                AttemptPrioritized(_, _, _, _, _, _, _, _, _))
        .Times(0);
  } else {
    EXPECT_CALL(processor->strategy(), Attempt(_, _, _, _, _, _, _, _))
        .Times(0);
  }
#elif defined(OS_APPLE)
  EXPECT_CALL(*mock_ca_processor, ProcessForCALayerOverlays(_, _, _, _, _, _))
      .WillOnce(Return(false));
#elif defined(OS_WIN)
  EXPECT_CALL(*dc_processor, Process(_, _, _, _, _, _, _, _)).Times(0);
#endif
  DrawFrame(&renderer, viewport_size);
#if defined(USE_OZONE) || defined(OS_ANDROID)
  Mock::VerifyAndClearExpectations(&processor->strategy());
#elif defined(OS_APPLE)
  Mock::VerifyAndClearExpectations(
      const_cast<MockCALayerOverlayProcessor*>(mock_ca_processor));
#elif defined(OS_WIN)
  Mock::VerifyAndClearExpectations(
      const_cast<MockDCLayerOverlayProcessor*>(dc_processor));
#endif

  // Without a copy request Attempt() should be called once.
  root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, AggregatedRenderPassId{1},
      gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
  root_pass->has_transparent_background = false;

  overlay_quad = root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
  overlay_quad->SetNew(
      root_pass->CreateAndAppendSharedQuadState(), gfx::Rect(viewport_size),
      gfx::Rect(viewport_size), needs_blending, parent_resource_id,
      premultiplied_alpha, gfx::PointF(0, 0), gfx::PointF(1, 1),
      SK_ColorTRANSPARENT, vertex_opacity, flipped, nearest_neighbor,
      /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear);
#if defined(USE_OZONE) || defined(OS_ANDROID)
  if (features::IsOverlayPrioritizationEnabled()) {
    EXPECT_CALL(processor->strategy(),
                AttemptPrioritized(_, _, _, _, _, _, _, _, _))
        .Times(1);
  } else {
    EXPECT_CALL(processor->strategy(), Attempt(_, _, _, _, _, _, _, _))
        .Times(1);
  }
#elif defined(OS_APPLE)
  EXPECT_CALL(*mock_ca_processor, ProcessForCALayerOverlays(_, _, _, _, _, _))
      .WillOnce(Return(true));
#elif defined(OS_WIN)
  EXPECT_CALL(*dc_processor, Process(_, _, _, _, _, _, _, _)).Times(1);
#endif
  DrawFrame(&renderer, viewport_size);

  // Transfer resources back from the parent to the child. Set no resources as
  // being in use.
  parent_resource_provider->DeclareUsedResourcesFromChild(child_id,
                                                          ResourceIdSet());

  child_resource_provider->RemoveImportedResource(resource_id);
  child_resource_provider->ShutdownAndReleaseAllResources();
}

#if defined(OS_ANDROID) || defined(USE_OZONE)
class SingleOverlayOnTopProcessor : public OverlayProcessorUsingStrategy {
 public:
  SingleOverlayOnTopProcessor() : OverlayProcessorUsingStrategy() {
    strategies_.push_back(std::make_unique<OverlayStrategySingleOnTop>(this));
    strategies_.push_back(std::make_unique<OverlayStrategyUnderlay>(this));
    prioritization_config_.changing_threshold = false;
    prioritization_config_.damage_rate_threshold = false;
  }

  bool NeedsSurfaceDamageRectList() const override { return true; }
  bool IsOverlaySupported() const override { return true; }

  void CheckOverlaySupport(
      const OverlayProcessorInterface::OutputSurfaceOverlayPlane* primary_plane,
      OverlayCandidateList* surfaces) override {
    if (!multiple_candidates_)
      ASSERT_EQ(1U, surfaces->size());
    OverlayCandidate& candidate = surfaces->back();
    candidate.overlay_handled = true;
  }

  void AllowMultipleCandidates() { multiple_candidates_ = true; }

 private:
  bool multiple_candidates_ = false;
};

class WaitSyncTokenCountingGLES2Interface : public TestGLES2Interface {
 public:
  MOCK_METHOD1(WaitSyncTokenCHROMIUM, void(const GLbyte* sync_token));
};

class MockOverlayScheduler {
 public:
  MOCK_METHOD7(Schedule,
               void(int plane_z_order,
                    gfx::OverlayTransform plane_transform,
                    unsigned overlay_texture_id,
                    const gfx::Rect& display_bounds,
                    const gfx::RectF& uv_rect,
                    bool enable_blend,
                    unsigned gpu_fence_id));
};

TEST_F(GLRendererTest, OverlaySyncTokensAreProcessed) {
  auto gl_owned = std::make_unique<WaitSyncTokenCountingGLES2Interface>();
  WaitSyncTokenCountingGLES2Interface* gl = gl_owned.get();

  auto provider = TestContextProvider::Create(std::move(gl_owned));
  provider->BindToCurrentThread();

  MockOverlayScheduler overlay_scheduler;
  provider->support()->SetScheduleOverlayPlaneCallback(base::BindRepeating(
      &MockOverlayScheduler::Schedule, base::Unretained(&overlay_scheduler)));

  cc::FakeOutputSurfaceClient output_surface_client;
  std::unique_ptr<OutputSurface> output_surface(
      FakeOutputSurface::Create3d(std::move(provider)));
  output_surface->BindToClient(&output_surface_client);

  auto parent_resource_provider = std::make_unique<DisplayResourceProviderGL>(
      output_surface->context_provider());

  auto child_context_provider = TestContextProvider::Create();
  child_context_provider->BindToCurrentThread();
  auto child_resource_provider = std::make_unique<ClientResourceProvider>();

  gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
                            gpu::CommandBufferId::FromUnsafeValue(0x123), 29);
  auto transfer_resource = TransferableResource::MakeGL(
      gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, sync_token,
      gfx::Size(256, 256), true);
  auto release_callback = base::BindOnce(&MailboxReleased);
  ResourceId resource_id = child_resource_provider->ImportResource(
      transfer_resource, std::move(release_callback));

  std::vector<ReturnedResource> returned_to_child;
  int child_id = parent_resource_provider->CreateChild(
      base::BindRepeating(&CollectResources, &returned_to_child), SurfaceId());

  // Transfer resource to the parent.
  std::vector<ResourceId> resource_ids_to_transfer;
  resource_ids_to_transfer.push_back(resource_id);
  std::vector<TransferableResource> list;
  child_resource_provider->PrepareSendToParent(
      resource_ids_to_transfer, &list,
      static_cast<RasterContextProvider*>(child_context_provider.get()));
  parent_resource_provider->ReceiveFromChild(child_id, list);

  // In DisplayResourceProvider's namespace, use the mapped resource id.
  std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
      parent_resource_provider->GetChildToParentMap(child_id);
  ResourceId parent_resource_id = resource_map[list[0].id];

  RendererSettings settings;
  auto processor = std::make_unique<SingleOverlayOnTopProcessor>();
  FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                          parent_resource_provider.get(), processor.get(),
                          base::ThreadTaskRunnerHandle::Get());
  renderer.Initialize();
  renderer.SetVisible(true);

  gfx::Size viewport_size(1, 1);
  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, AggregatedRenderPassId{1},
      gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
  root_pass->has_transparent_background = false;

  bool needs_blending = false;
  bool premultiplied_alpha = false;
  bool flipped = false;
  bool nearest_neighbor = false;
  float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
  gfx::PointF uv_top_left(0, 0);
  gfx::PointF uv_bottom_right(1, 1);

  TextureDrawQuad* overlay_quad =
      root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
  SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState();
  shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size),
                       gfx::Rect(viewport_size), gfx::MaskFilterInfo(),
                       absl::nullopt, false, 1, SkBlendMode::kSrcOver, 0);
  overlay_quad->SetNew(shared_state, gfx::Rect(viewport_size),
                       gfx::Rect(viewport_size), needs_blending,
                       parent_resource_id, premultiplied_alpha, uv_top_left,
                       uv_bottom_right, SK_ColorTRANSPARENT, vertex_opacity,
                       flipped, nearest_neighbor, /*secure_output_only=*/false,
                       gfx::ProtectedVideoType::kClear);

  // The verified flush flag will be set by
  // ClientResourceProvider::PrepareSendToParent. Before checking if the
  // gpu::SyncToken matches, set this flag first.
  sync_token.SetVerifyFlush();

  // Verify that overlay_quad actually gets turned into an overlay, and even
  // though it's not drawn, that its sync point is waited on.
  EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(MatchesSyncToken(sync_token)))
      .Times(1);

  EXPECT_CALL(
      overlay_scheduler,
      Schedule(1, gfx::OVERLAY_TRANSFORM_NONE, _, gfx::Rect(viewport_size),
               BoundingRect(uv_top_left, uv_bottom_right), _, _))
      .Times(1);

  DrawFrame(&renderer, viewport_size);

  // Transfer resources back from the parent to the child. Set no resources as
  // being in use.
  parent_resource_provider->DeclareUsedResourcesFromChild(child_id,
                                                          ResourceIdSet());

  child_resource_provider->RemoveImportedResource(resource_id);
  child_resource_provider->ShutdownAndReleaseAllResources();
}
#endif  // defined(USE_OZONE) || defined(OS_ANDROID)

class OutputColorMatrixMockGLES2Interface : public TestGLES2Interface {
 public:
  OutputColorMatrixMockGLES2Interface() = default;

  MOCK_METHOD4(UniformMatrix4fv,
               void(GLint location,
                    GLsizei count,
                    GLboolean transpose,
                    const GLfloat* value));
};

TEST_F(GLRendererTest, OutputColorMatrixTest) {
  // Initialize the mock GL interface, the output surface and the renderer.
  auto gl_owned = std::make_unique<OutputColorMatrixMockGLES2Interface>();
  auto* gl = gl_owned.get();
  auto provider = TestContextProvider::Create(std::move(gl_owned));
  provider->BindToCurrentThread();
  std::unique_ptr<FakeOutputSurface> output_surface(
      FakeOutputSurface::Create3d(std::move(provider)));
  cc::FakeOutputSurfaceClient output_surface_client;
  output_surface->BindToClient(&output_surface_client);
  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
      output_surface->context_provider());
  RendererSettings settings;
  FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                          resource_provider.get());
  renderer.Initialize();
  renderer.SetVisible(true);

  // Set a non-identity color matrix on the output surface.
  skia::Matrix44 color_matrix(skia::Matrix44::kIdentity_Constructor);
  color_matrix.set(0, 0, 0.7f);
  color_matrix.set(1, 1, 0.4f);
  color_matrix.set(2, 2, 0.5f);
  output_surface->set_color_matrix(color_matrix);

  // Create a root and a child passes to test that the output color matrix is
  // registered only for the root pass.
  gfx::Size viewport_size(100, 100);
  AggregatedRenderPassId child_pass_id{2};
  AggregatedRenderPass* child_pass =
      cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                        gfx::Rect(viewport_size) + gfx::Vector2d(1, 2),
                        gfx::Transform(), cc::FilterOperations());
  AggregatedRenderPassId root_pass_id{1};
  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
      gfx::Transform(), cc::FilterOperations());
  root_pass->damage_rect = gfx::Rect(0, 0, 25, 25);
  cc::AddRenderPassQuad(root_pass, child_pass);

  // Verify that UniformMatrix4fv() is called only once on the root pass with
  // the correct matrix values.
  int call_count = 0;
  bool output_color_matrix_invoked = false;
  EXPECT_CALL(*gl, UniformMatrix4fv(_, 1, false, _))
      .WillRepeatedly(testing::WithArgs<0, 3>(testing::Invoke(
          [&color_matrix, &renderer, &call_count, &output_color_matrix_invoked](
              int matrix_location, const GLfloat* gl_matrix) {
            DCHECK(current_program(&renderer));
            const int color_matrix_location =
                current_program(&renderer)->output_color_matrix_location();

            if (matrix_location != color_matrix_location)
              return;

            call_count++;
            output_color_matrix_invoked = true;
            float expected_matrix[16];
            color_matrix.asColMajorf(expected_matrix);
            for (int i = 0; i < 16; ++i)
              EXPECT_FLOAT_EQ(expected_matrix[i], gl_matrix[i]);
          })));

  renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
  DrawFrame(&renderer, viewport_size);

  EXPECT_EQ(1, call_count);
  EXPECT_TRUE(output_color_matrix_invoked);
}

class GenerateMipmapMockGLESInterface : public TestGLES2Interface {
 public:
  GenerateMipmapMockGLESInterface() = default;

  MOCK_METHOD3(TexParameteri, void(GLenum target, GLenum pname, GLint param));
  MOCK_METHOD1(GenerateMipmap, void(GLenum target));
};

// TODO(crbug.com/803286): Currently npot texture always return false on ubuntu
// desktop.  The npot texture check is probably failing on desktop GL. This test
// crashes DCHECK npot texture to catch this. When
// GLRendererPixelTest.DISABLED_TrilinearFiltering got passed, can remove this.
TEST_F(GLRendererTest, GenerateMipmap) {
  // Initialize the mock GL interface, the output surface and the renderer.
  auto gl_owned = std::make_unique<GenerateMipmapMockGLESInterface>();
  gl_owned->set_support_texture_npot(true);

  auto* gl = gl_owned.get();
  auto provider = TestContextProvider::Create(std::move(gl_owned));
  provider->BindToCurrentThread();

  std::unique_ptr<FakeOutputSurface> output_surface(
      FakeOutputSurface::Create3d(std::move(provider)));
  cc::FakeOutputSurfaceClient output_surface_client;
  output_surface->BindToClient(&output_surface_client);
  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
      output_surface->context_provider());
  RendererSettings settings;
  FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                          resource_provider.get());
  renderer.Initialize();
  renderer.SetVisible(true);

  gfx::Size viewport_size(100, 100);
  AggregatedRenderPassId child_pass_id{2};
  // Create a child pass with mipmap to verify that npot texture is enabled.
  AggregatedRenderPass* child_pass =
      cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                        gfx::Rect(viewport_size) + gfx::Vector2d(1, 2),
                        gfx::Transform(), cc::FilterOperations());
  child_pass->generate_mipmap = true;

  AggregatedRenderPassId root_pass_id{1};
  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
      gfx::Transform(), cc::FilterOperations());
  root_pass->damage_rect = gfx::Rect(0, 0, 25, 25);
  cc::AddRenderPassQuad(root_pass, child_pass);
  renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);

  EXPECT_CALL(*gl, TexParameteri(_, _, _)).Times(4);
  EXPECT_CALL(*gl, GenerateMipmap(GL_TEXTURE_2D)).Times(1);
  // When generate_mipmap enabled, the GL_TEXTURE_MIN_FILTER should be
  // GL_LINEAR_MIPMAP_LINEAR.
  EXPECT_CALL(*gl, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                                 GL_LINEAR_MIPMAP_LINEAR));
  DrawFrame(&renderer, viewport_size);
}

class FastSolidColorMockGLES2Interface : public TestGLES2Interface {
 public:
  FastSolidColorMockGLES2Interface() = default;

  MOCK_METHOD1(Enable, void(GLenum cap));
  MOCK_METHOD1(Disable, void(GLenum cap));
  MOCK_METHOD4(ClearColor,
               void(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha));
  MOCK_METHOD4(Scissor, void(GLint x, GLint y, GLsizei width, GLsizei height));
};

class GLRendererFastSolidColorTest : public GLRendererTest {
 public:
  void SetUp() override {
    feature_list_.InitAndEnableFeature(features::kFastSolidColorDraw);
    GLRendererTest::SetUp();

    auto gl_owned = std::make_unique<FastSolidColorMockGLES2Interface>();
    gl_owned->set_have_post_sub_buffer(true);
    gl_ = gl_owned.get();

    auto provider = TestContextProvider::Create(std::move(gl_owned));
    provider->BindToCurrentThread();

    output_surface_ = FakeOutputSurface::Create3d(std::move(provider));
    output_surface_->BindToClient(&output_surface_client_);

    resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
        output_surface_->context_provider());

    settings_.partial_swap_enabled = true;
    settings_.slow_down_compositing_scale_factor = 1;
    settings_.allow_antialiasing = true;

    fake_renderer_ = std::make_unique<FakeRendererGL>(
        &settings_, &debug_settings_, output_surface_.get(),
        resource_provider_.get());
    fake_renderer_->Initialize();
    EXPECT_TRUE(fake_renderer_->use_partial_swap());
    fake_renderer_->SetVisible(true);
  }

  void TearDown() override {
    resource_provider_.reset();
    fake_renderer_.reset();
    output_surface_.reset();
    gl_ = nullptr;

    GLRendererTest::TearDown();
  }

  FastSolidColorMockGLES2Interface* gl_ptr() { return gl_; }

  FakeOutputSurface* output_surface() { return output_surface_.get(); }

 protected:
  void AddExpectations(bool use_fast_path,
                       const gfx::Rect& scissor_rect,
                       SkColor color = SK_ColorBLACK,
                       bool enable_stencil = false) {
    auto* gl = gl_ptr();

    InSequence seq;

    // Restore GL state method calls
    EXPECT_CALL(*gl, Disable(GL_DEPTH_TEST));
    EXPECT_CALL(*gl, Disable(GL_CULL_FACE));
    EXPECT_CALL(*gl, Disable(GL_STENCIL_TEST));
    EXPECT_CALL(*gl, Enable(GL_BLEND));
    EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST));
    EXPECT_CALL(*gl, Scissor(0, 0, 0, 0));

    if (!enable_stencil)
      EXPECT_CALL(*gl, ClearColor(0, 0, 0, 0));

    if (use_fast_path) {
      EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST));
      EXPECT_CALL(*gl, Scissor(scissor_rect.x(), scissor_rect.y(),
                               scissor_rect.width(), scissor_rect.height()));

      SkColor4f color_f = SkColor4f::FromColor(color);
      EXPECT_CALL(*gl,
                  ClearColor(color_f.fR, color_f.fG, color_f.fB, color_f.fA));

      EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST));
      EXPECT_CALL(*gl, Scissor(0, 0, 0, 0));
    }

    if (enable_stencil) {
      EXPECT_CALL(*gl, Enable(GL_STENCIL_TEST));
      EXPECT_CALL(*gl, Disable(GL_BLEND));
    }

    EXPECT_CALL(*gl, Disable(GL_BLEND));
  }

  void RunTest(const gfx::Size& viewport_size) {
    fake_renderer_->DecideRenderPassAllocationsForFrame(
        render_passes_in_draw_order_);
    DrawFrame(fake_renderer_.get(), viewport_size);

    auto* gl = gl_ptr();
    ASSERT_TRUE(gl);
    Mock::VerifyAndClearExpectations(gl);
  }

 private:
  FastSolidColorMockGLES2Interface* gl_ = nullptr;
  std::unique_ptr<FakeRendererGL> fake_renderer_;
  std::unique_ptr<FakeOutputSurface> output_surface_;
  std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
  cc::FakeOutputSurfaceClient output_surface_client_;
  RendererSettings settings_;
  base::test::ScopedFeatureList feature_list_;
};

TEST_F(GLRendererFastSolidColorTest, RoundedCorners) {
  gfx::Size viewport_size(500, 500);
  gfx::Rect root_pass_output_rect(400, 400);
  gfx::Rect root_pass_damage_rect(10, 20, 300, 200);
  gfx::Rect quad_rect(0, 50, 100, 100);

  AggregatedRenderPassId root_pass_id{1};
  AggregatedRenderPass* root_pass = cc::AddRenderPassWithDamage(
      &render_passes_in_draw_order_, root_pass_id, root_pass_output_rect,
      root_pass_damage_rect, gfx::Transform(), cc::FilterOperations());
  root_pass->damage_rect = root_pass_damage_rect;
  cc::AddQuad(root_pass, quad_rect, SK_ColorRED);

  root_pass->shared_quad_state_list.front()->mask_filter_info =
      gfx::MaskFilterInfo(gfx::RRectF(gfx::RectF(quad_rect), 5.f));

  // Fast Solid color draw quads should not be executed.
  AddExpectations(false /*use_fast_path*/, gfx::Rect());

  RunTest(viewport_size);
}

TEST_F(GLRendererFastSolidColorTest, Transform3DSlowPath) {
  gfx::Size viewport_size(500, 500);
  gfx::Rect root_pass_damage_rect(10, 20, 300, 200);
  gfx::Rect quad_rect(0, 50, 100, 100);

  AggregatedRenderPassId root_pass_id{1};
  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
      gfx::Transform(), cc::FilterOperations());
  root_pass->damage_rect = root_pass_damage_rect;
  cc::AddQuad(root_pass, quad_rect, SK_ColorRED);

  gfx::Transform tm_3d;
  tm_3d.RotateAboutYAxis(30.0);
  ASSERT_FALSE(tm_3d.IsFlat());

  root_pass->shared_quad_state_list.front()->quad_to_target_transform = tm_3d;

  AddExpectations(false /*use_fast_path*/, gfx::Rect());

  RunTest(viewport_size);
}

TEST_F(GLRendererFastSolidColorTest, NonTransform3DFastPath) {
  gfx::Size viewport_size(500, 500);
  gfx::Rect root_pass_damage_rect(10, 20, 300, 200);
  gfx::Rect quad_rect(0, 0, 200, 200);

  AggregatedRenderPassId root_pass_id{1};
  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
      gfx::Transform(), cc::FilterOperations());
  root_pass->damage_rect = root_pass_damage_rect;
  cc::AddQuad(root_pass, quad_rect, SK_ColorRED);

  gfx::Transform tm_non_3d;
  tm_non_3d.Translate(10.f, 10.f);
  ASSERT_TRUE(tm_non_3d.IsFlat());

  root_pass->shared_quad_state_list.front()->quad_to_target_transform =
      tm_non_3d;

  AddExpectations(true /*use_fast_path*/, gfx::Rect(10, 290, 200, 200),
                  SK_ColorRED);

  RunTest(viewport_size);
}

TEST_F(GLRendererFastSolidColorTest, NonAxisAlignSlowPath) {
  gfx::Size viewport_size(500, 500);
  gfx::Rect root_pass_damage_rect(10, 20, 300, 200);
  gfx::Rect quad_rect(0, 0, 200, 200);

  AggregatedRenderPassId root_pass_id{1};
  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
      gfx::Transform(), cc::FilterOperations());
  root_pass->damage_rect = root_pass_damage_rect;
  cc::AddQuad(root_pass, quad_rect, SK_ColorRED);

  gfx::Transform tm_non_axis_align;
  tm_non_axis_align.RotateAboutZAxis(45.0);
  ASSERT_TRUE(tm_non_axis_align.IsFlat());

  root_pass->shared_quad_state_list.front()->quad_to_target_transform =
      tm_non_axis_align;

  AddExpectations(false /*use_fast_path*/, gfx::Rect());

  RunTest(viewport_size);
}

TEST_F(GLRendererFastSolidColorTest, StencilSlowPath) {
  gfx::Size viewport_size(500, 500);
  gfx::Rect root_pass_damage_rect(10, 20, 300, 200);
  gfx::Rect quad_rect(0, 0, 200, 200);

  AggregatedRenderPassId root_pass_id{1};
  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
      gfx::Transform(), cc::FilterOperations());
  root_pass->damage_rect = root_pass_damage_rect;
  root_pass->has_transparent_background = false;

  cc::AddQuad(root_pass, quad_rect, SK_ColorRED);

  AddExpectations(false /*use_fast_path*/, gfx::Rect(), SK_ColorRED,
                  true /*enable_stencil*/);
  output_surface()->set_has_external_stencil_test(true);

  RunTest(viewport_size);
}

TEST_F(GLRendererFastSolidColorTest, NeedsBlendingSlowPath) {
  gfx::Size viewport_size(500, 500);
  gfx::Rect root_pass_damage_rect(2, 3, 300, 200);
  gfx::Rect full_quad_rect(0, 0, 50, 50);
  gfx::Rect quad_rect_1(0, 0, 20, 20);
  gfx::Rect quad_rect_2(20, 0, 20, 20);
  gfx::Rect quad_rect_3(0, 20, 20, 20);

  AggregatedRenderPassId root_pass_id{1};
  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
      gfx::Transform(), cc::FilterOperations());
  root_pass->damage_rect = root_pass_damage_rect;

  cc::AddQuad(root_pass, quad_rect_1, SkColorSetARGB(0x33, 0xFF, 0, 0));

  cc::AddQuad(root_pass, quad_rect_2, SK_ColorBLUE);
  root_pass->shared_quad_state_list.back()->opacity = 0.5f;

  cc::AddQuad(root_pass, quad_rect_3, SK_ColorGREEN);
  root_pass->shared_quad_state_list.back()->blend_mode = SkBlendMode::kDstIn;

  cc::AddQuad(root_pass, full_quad_rect, SK_ColorBLACK);

  // The first solid color quad would use a fast path, but the other quads that
  // require blending will use the slower method.
  AddExpectations(true /*use_fast_path*/, gfx::Rect(0, 450, 50, 50),
                  SK_ColorBLACK, false /*enable_stencil*/);

  RunTest(viewport_size);
}

TEST_F(GLRendererFastSolidColorTest, NeedsBlendingFastPath) {
  gfx::Size viewport_size(500, 500);
  gfx::Rect root_pass_damage_rect(2, 3, 300, 200);
  gfx::Rect quad_rect_1(0, 0, 20, 20);
  gfx::Rect quad_rect_2(20, 0, 20, 20);
  gfx::Rect quad_rect_3(0, 20, 20, 20);

  AggregatedRenderPassId root_pass_id{1};
  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
      gfx::Transform(), cc::FilterOperations());
  root_pass->damage_rect = root_pass_damage_rect;

  cc::AddQuad(root_pass, quad_rect_1, SkColorSetARGB(0x33, 0xFF, 0, 0));

  cc::AddQuad(root_pass, quad_rect_2, SK_ColorBLUE);
  root_pass->shared_quad_state_list.back()->opacity = 0.5f;

  cc::AddQuad(root_pass, quad_rect_3, SK_ColorGREEN);
  root_pass->shared_quad_state_list.back()->blend_mode = SkBlendMode::kSrc;

  auto* gl = gl_ptr();

  // The quads here despite having blend requirements can still use fast path
  // because they do not intersect with any other quad that has already been
  // drawn onto the render target.
  InSequence seq;

  // // Restore GL state method calls
  EXPECT_CALL(*gl, Disable(GL_DEPTH_TEST));
  EXPECT_CALL(*gl, Disable(GL_CULL_FACE));
  EXPECT_CALL(*gl, Disable(GL_STENCIL_TEST));
  EXPECT_CALL(*gl, Enable(GL_BLEND));
  EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST));
  EXPECT_CALL(*gl, Scissor(0, 0, 0, 0));
  EXPECT_CALL(*gl, ClearColor(0, 0, 0, 0));

  // Fast path draw used for green quad.
  EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST));
  EXPECT_CALL(*gl, Scissor(0, 460, 20, 20));
  EXPECT_CALL(*gl, ClearColor(0, 1, 0, 1));
  EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST));
  EXPECT_CALL(*gl, Scissor(0, 0, 0, 0));

  // Fast path draw used for blue quad.
  EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST));
  EXPECT_CALL(*gl, Scissor(20, 480, 20, 20));
  EXPECT_CALL(*gl, ClearColor(0, 0, 0.5f, 0.5f));
  EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST));
  EXPECT_CALL(*gl, Scissor(0, 0, 0, 0));

  // Fast path draw used for red quad.
  EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST));
  EXPECT_CALL(*gl, Scissor(0, 480, 20, 20));
  EXPECT_CALL(*gl, ClearColor(::testing::FloatEq(0.2f), 0, 0,
                              ::testing::FloatEq(0.2f)));
  EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST));
  EXPECT_CALL(*gl, Scissor(0, 0, 0, 0));

  EXPECT_CALL(*gl, Disable(GL_BLEND));

  RunTest(viewport_size);
}

TEST_F(GLRendererFastSolidColorTest, AntiAliasSlowPath) {
  gfx::Size viewport_size(500, 500);
  gfx::Rect root_pass_damage_rect(10, 20, 300, 200);
  gfx::Rect quad_rect(0, 0, 200, 200);

  AggregatedRenderPassId root_pass_id{1};
  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
      gfx::Transform(), cc::FilterOperations());
  root_pass->damage_rect = root_pass_damage_rect;
  cc::AddQuad(root_pass, quad_rect, SK_ColorRED);

  gfx::Transform tm_aa;
  tm_aa.Translate(0.1f, 0.1f);
  ASSERT_TRUE(tm_aa.IsFlat());

  root_pass->shared_quad_state_list.front()->quad_to_target_transform = tm_aa;

  AddExpectations(false /*use_fast_path*/, gfx::Rect());

  RunTest(viewport_size);
}

class PartialSwapMockGLES2Interface : public TestGLES2Interface {
 public:
  PartialSwapMockGLES2Interface() = default;

  MOCK_METHOD1(Enable, void(GLenum cap));
  MOCK_METHOD1(Disable, void(GLenum cap));
  MOCK_METHOD4(Scissor, void(GLint x, GLint y, GLsizei width, GLsizei height));
  MOCK_METHOD1(SetEnableDCLayersCHROMIUM, void(GLboolean enable));
};

class GLRendererPartialSwapTest : public GLRendererTest {
 public:
  void SetUp() override {
    // Force enable fast solid color draw path.
    scoped_feature_list_.InitAndEnableFeature(features::kFastSolidColorDraw);
    GLRendererTest::SetUp();
  }

 protected:
  void RunTest(bool partial_swap, bool set_draw_rectangle) {
    auto gl_owned = std::make_unique<PartialSwapMockGLES2Interface>();
    gl_owned->set_have_post_sub_buffer(true);

    auto* gl = gl_owned.get();

    auto provider = TestContextProvider::Create(std::move(gl_owned));
    provider->BindToCurrentThread();

    cc::FakeOutputSurfaceClient output_surface_client;
    std::unique_ptr<FakeOutputSurface> output_surface(
        FakeOutputSurface::Create3d(std::move(provider)));
    output_surface->set_supports_dc_layers(set_draw_rectangle);
    output_surface->BindToClient(&output_surface_client);

    auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
        output_surface->context_provider());

    RendererSettings settings;
    settings.partial_swap_enabled = partial_swap;
    FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                            resource_provider.get());
    renderer.Initialize();
    EXPECT_EQ(partial_swap, renderer.use_partial_swap());
    renderer.SetVisible(true);

    gfx::Size viewport_size(100, 100);
    gfx::Rect root_pass_output_rect(80, 80);
    gfx::Rect root_pass_damage_rect(2, 2, 3, 3);

    // Draw one black frame to make sure the output surface is reshaped before
    // tests.
    EXPECT_CALL(*gl, Disable(GL_DEPTH_TEST)).Times(1);
    EXPECT_CALL(*gl, Disable(GL_CULL_FACE)).Times(1);
    EXPECT_CALL(*gl, Disable(GL_STENCIL_TEST)).Times(1);
    EXPECT_CALL(*gl, Enable(GL_BLEND)).Times(1);

    if (output_surface->capabilities().supports_dc_layers) {
      EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)).Times(1);
      EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)).Times(1);

      // Root render pass requires a scissor if the output surface supports
      // dc layers.
      EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)).Times(3);
      EXPECT_CALL(*gl, Scissor(0, 0, 100, 100)).Times(3);
    } else {
      EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST)).Times(2);
      EXPECT_CALL(*gl, Scissor(0, 0, 0, 0)).Times(2);
      if (set_draw_rectangle) {
        EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)).Times(2);
        EXPECT_CALL(*gl, Scissor(0, 0, 100, 100)).Times(2);
      } else {
        EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST)).Times(1);
        EXPECT_CALL(*gl, Scissor(0, 0, 100, 100)).Times(1);
      }
    }

    EXPECT_CALL(*gl, Disable(GL_BLEND)).Times(1);

    AggregatedRenderPassId root_pass_id{1};
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    root_pass->damage_rect = gfx::Rect(viewport_size);
    cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorBLACK);

    renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
    DrawFrame(&renderer, viewport_size);
    Mock::VerifyAndClearExpectations(gl);

    for (int i = 0; i < 2; ++i) {
      root_pass = cc::AddRenderPassWithDamage(
          &render_passes_in_draw_order_, root_pass_id, root_pass_output_rect,
          root_pass_damage_rect, gfx::Transform(), cc::FilterOperations());
      cc::AddQuad(root_pass, gfx::Rect(root_pass_output_rect), SK_ColorGREEN);

      InSequence seq;

      // A bunch of initialization that happens.
      EXPECT_CALL(*gl, Disable(GL_DEPTH_TEST));
      EXPECT_CALL(*gl, Disable(GL_CULL_FACE));
      EXPECT_CALL(*gl, Disable(GL_STENCIL_TEST));
      EXPECT_CALL(*gl, Enable(GL_BLEND));
      EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST));
      EXPECT_CALL(*gl, Scissor(0, 0, 0, 0));

      // Partial frame, we should use a scissor to swap only that part when
      // partial swap is enabled.
      gfx::Rect output_rectangle =
          partial_swap ? root_pass_damage_rect : gfx::Rect(viewport_size);

      // The scissor is flipped, so subtract the y coord and height from the
      // bottom of the GL viewport.
      gfx::Rect scissor_rect(output_rectangle.x(),
                             viewport_size.height() - output_rectangle.y() -
                                 output_rectangle.height(),
                             output_rectangle.width(),
                             output_rectangle.height());

      // Drawing the solid color quad using glClear and scissor rect.
      EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST));
      EXPECT_CALL(*gl, Scissor(scissor_rect.x(), scissor_rect.y(),
                               scissor_rect.width(), scissor_rect.height()));

      if (partial_swap || set_draw_rectangle) {
        EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST));
        EXPECT_CALL(*gl, Scissor(scissor_rect.x(), scissor_rect.y(),
                                 scissor_rect.width(), scissor_rect.height()));
      }

      // Restore GL state after solid color draw quad.
      if (partial_swap || set_draw_rectangle) {
        EXPECT_CALL(*gl, Enable(GL_SCISSOR_TEST));
        EXPECT_CALL(*gl, Scissor(scissor_rect.x(), scissor_rect.y(),
                                 scissor_rect.width(), scissor_rect.height()));
      } else {
        EXPECT_CALL(*gl, Disable(GL_SCISSOR_TEST));
        EXPECT_CALL(*gl, Scissor(0, 0, 0, 0));
      }

      // Blending is disabled at the end of the frame.
      EXPECT_CALL(*gl, Disable(GL_BLEND));

      renderer.DecideRenderPassAllocationsForFrame(
          render_passes_in_draw_order_);
      DrawFrame(&renderer, viewport_size);

      if (set_draw_rectangle) {
        EXPECT_EQ(output_rectangle, output_surface->last_set_draw_rectangle());
      }

      Mock::VerifyAndClearExpectations(gl);
    }
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

TEST_F(GLRendererPartialSwapTest, PartialSwap) {
  RunTest(true, false);
}

TEST_F(GLRendererPartialSwapTest, NoPartialSwap) {
  RunTest(false, false);
}

#if defined(OS_WIN)
TEST_F(GLRendererPartialSwapTest, SetDrawRectangle_PartialSwap) {
  RunTest(true, true);
}

TEST_F(GLRendererPartialSwapTest, SetDrawRectangle_NoPartialSwap) {
  RunTest(false, true);
}

// Test that SetEnableDCLayersCHROMIUM is properly called when enabling
// and disabling DC layers.
TEST_F(GLRendererTest, DCLayerOverlaySwitch) {
  auto gl_owned = std::make_unique<PartialSwapMockGLES2Interface>();
  gl_owned->set_have_post_sub_buffer(true);
  auto* gl = gl_owned.get();

  auto provider = TestContextProvider::Create(std::move(gl_owned));
  provider->BindToCurrentThread();

  cc::FakeOutputSurfaceClient output_surface_client;
  std::unique_ptr<FakeOutputSurface> output_surface(
      FakeOutputSurface::Create3d(std::move(provider)));
  output_surface->set_supports_dc_layers(true);
  output_surface->BindToClient(&output_surface_client);

  auto parent_resource_provider = std::make_unique<DisplayResourceProviderGL>(
      output_surface->context_provider());

  auto child_context_provider = TestContextProvider::Create();
  child_context_provider->BindToCurrentThread();
  auto child_resource_provider = std::make_unique<ClientResourceProvider>();

  auto transfer_resource = TransferableResource::MakeGL(
      gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(),
      gfx::Size(256, 256), true);
  auto release_callback = base::BindOnce(&MailboxReleased);
  ResourceId resource_id = child_resource_provider->ImportResource(
      transfer_resource, std::move(release_callback));

  std::vector<ReturnedResource> returned_to_child;
  int child_id = parent_resource_provider->CreateChild(
      base::BindRepeating(&CollectResources, &returned_to_child), SurfaceId());

  // Transfer resource to the parent.
  std::vector<ResourceId> resource_ids_to_transfer;
  resource_ids_to_transfer.push_back(resource_id);
  std::vector<TransferableResource> list;
  child_resource_provider->PrepareSendToParent(
      resource_ids_to_transfer, &list,
      static_cast<RasterContextProvider*>(child_context_provider.get()));
  parent_resource_provider->ReceiveFromChild(child_id, list);
  // In DisplayResourceProvider's namespace, use the mapped resource id.
  std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
      parent_resource_provider->GetChildToParentMap(child_id);
  ResourceId parent_resource_id = resource_map[list[0].id];

  auto processor = std::make_unique<OverlayProcessorWin>(
      output_surface.get(),
      std::make_unique<DCLayerOverlayProcessor>(
          &debug_settings_, /*allowed_yuv_overlay_count=*/1, true));

  RendererSettings settings;
  settings.partial_swap_enabled = true;
  FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                          parent_resource_provider.get(), processor.get());
  renderer.Initialize();
  renderer.SetVisible(true);

  gfx::Size viewport_size(100, 100);

  for (int i = 0; i < 65; i++) {
    AggregatedRenderPassId root_pass_id{1};
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    if (i == 0) {
      gfx::Rect rect(0, 0, 100, 100);
      bool needs_blending = false;
      gfx::RectF tex_coord_rect(0, 0, 1, 1);
      SharedQuadState* shared_state =
          root_pass->CreateAndAppendSharedQuadState();
      shared_state->SetAll(gfx::Transform(), rect, rect, gfx::MaskFilterInfo(),
                           absl::nullopt, false, 1, SkBlendMode::kSrcOver, 0);
      YUVVideoDrawQuad* quad =
          root_pass->CreateAndAppendDrawQuad<YUVVideoDrawQuad>();
      quad->SetNew(shared_state, rect, rect, needs_blending, tex_coord_rect,
                   tex_coord_rect, rect.size(), rect.size(), parent_resource_id,
                   parent_resource_id, parent_resource_id, parent_resource_id,
                   gfx::ColorSpace(), 0, 1.0, 8);
    }

    // A bunch of initialization that happens.
    EXPECT_CALL(*gl, Disable(_)).Times(AnyNumber());
    EXPECT_CALL(*gl, Enable(_)).Times(AnyNumber());
    EXPECT_CALL(*gl, Scissor(_, _, _, _)).Times(AnyNumber());

    // Partial frame, we should use a scissor to swap only that part when
    // partial swap is enabled.
    root_pass->damage_rect = gfx::Rect(2, 2, 3, 3);
    // Frame 0 should be completely damaged because it's the first.
    // Frame 1 should be because it changed. Frame 60 should be
    // because it's disabling DC layers.
    gfx::Rect output_rectangle = (i == 0 || i == 1 || i == 60)
                                     ? root_pass->output_rect
                                     : root_pass->damage_rect;

    // Frame 0 should have DC Layers enabled because of the overlay.
    // After 60 frames of no overlays DC layers should be disabled again.
    if (i == 0)
      EXPECT_CALL(*gl, SetEnableDCLayersCHROMIUM(GL_TRUE));
    else if (i == 60)
      EXPECT_CALL(*gl, SetEnableDCLayersCHROMIUM(GL_FALSE));

    renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
    DrawFrame(&renderer, viewport_size);
    EXPECT_EQ(output_rectangle, output_surface->last_set_draw_rectangle());
    testing::Mock::VerifyAndClearExpectations(gl);
  }

  // Transfer resources back from the parent to the child. Set no resources as
  // being in use.
  parent_resource_provider->DeclareUsedResourcesFromChild(child_id,
                                                          ResourceIdSet());

  child_resource_provider->RemoveImportedResource(resource_id);
  child_resource_provider->ShutdownAndReleaseAllResources();
}
#endif

class GLRendererWithMockContextTest : public ::testing::Test {
 protected:
  class MockContextSupport : public TestContextSupport {
   public:
    MockContextSupport() {}
    MOCK_METHOD1(SetAggressivelyFreeResources,
                 void(bool aggressively_free_resources));
  };

  void SetUp() override {
    auto context_support = std::make_unique<MockContextSupport>();
    context_support_ptr_ = context_support.get();
    auto context_provider =
        TestContextProvider::Create(std::move(context_support));
    ASSERT_EQ(context_provider->BindToCurrentThread(),
              gpu::ContextResult::kSuccess);
    output_surface_ = FakeOutputSurface::Create3d(std::move(context_provider));
    output_surface_->BindToClient(&output_surface_client_);
    resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
        output_surface_->context_provider());
    renderer_ = std::make_unique<GLRenderer>(
        &settings_, &debug_settings_, output_surface_.get(),
        resource_provider_.get(), nullptr, nullptr);
    renderer_->Initialize();
  }

  RendererSettings settings_;
  DebugRendererSettings debug_settings_;
  cc::FakeOutputSurfaceClient output_surface_client_;
  MockContextSupport* context_support_ptr_;
  std::unique_ptr<OutputSurface> output_surface_;
  std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
  std::unique_ptr<GLRenderer> renderer_;
};

TEST_F(GLRendererWithMockContextTest,
       ContextPurgedWhenRendererBecomesInvisible) {
  EXPECT_CALL(*context_support_ptr_, SetAggressivelyFreeResources(false));
  renderer_->SetVisible(true);
  Mock::VerifyAndClearExpectations(context_support_ptr_);

  EXPECT_CALL(*context_support_ptr_, SetAggressivelyFreeResources(true));
  renderer_->SetVisible(false);
  Mock::VerifyAndClearExpectations(context_support_ptr_);
}

#if defined(USE_OZONE) || defined(OS_ANDROID)
class ContentBoundsOverlayProcessor : public OverlayProcessorUsingStrategy {
 public:
  class Strategy : public OverlayProcessorUsingStrategy::Strategy {
   public:
    explicit Strategy(const std::vector<gfx::Rect>& content_bounds)
        : content_bounds_(content_bounds) {}
    ~Strategy() override = default;

    bool Attempt(const skia::Matrix44& output_color_matrix,
                 const OverlayProcessorInterface::FilterOperationsMap&
                     render_pass_backdrop_filters,
                 DisplayResourceProvider* resource_provider,
                 AggregatedRenderPassList* render_pass_list,
                 SurfaceDamageRectList* surface_damage_rect_list,
                 const PrimaryPlane* primary_plane,
                 OverlayCandidateList* candidates,
                 std::vector<gfx::Rect>* content_bounds) override {
      content_bounds->insert(content_bounds->end(), content_bounds_.begin(),
                             content_bounds_.end());
      return true;
    }

    void ProposePrioritized(
        const skia::Matrix44& output_color_matrix,
        const FilterOperationsMap& render_pass_backdrop_filters,
        DisplayResourceProvider* resource_provider,
        AggregatedRenderPassList* render_pass_list,
        SurfaceDamageRectList* surface_damage_rect_list,
        const PrimaryPlane* primary_plane,
        OverlayProposedCandidateList* candidates,
        std::vector<gfx::Rect>* content_bounds) override {
      auto* render_pass = render_pass_list->back().get();
      QuadList& quad_list = render_pass->quad_list;
      OverlayCandidate candidate;
      // Adding a mock candidate to the propose list so that
      // 'AttemptPrioritized' will be called.
      candidates->push_back({quad_list.end(), candidate, this});
    }

    bool AttemptPrioritized(
        const skia::Matrix44& output_color_matrix,
        const FilterOperationsMap& render_pass_backdrop_filters,
        DisplayResourceProvider* resource_provider,
        AggregatedRenderPassList* render_pass_list,
        SurfaceDamageRectList* surface_damage_rect_list,
        const PrimaryPlane* primary_plane,
        OverlayCandidateList* candidates,
        std::vector<gfx::Rect>* content_bounds,
        const OverlayProposedCandidate& proposed_candidate) override {
      content_bounds->insert(content_bounds->end(), content_bounds_.begin(),
                             content_bounds_.end());
      return true;
    }

    void CommitCandidate(const OverlayProposedCandidate& proposed_candidate,
                         AggregatedRenderPass* render_pass) override {}

   private:
    const std::vector<gfx::Rect> content_bounds_;
  };

  explicit ContentBoundsOverlayProcessor(
      const std::vector<gfx::Rect>& content_bounds)
      : OverlayProcessorUsingStrategy(), content_bounds_(content_bounds) {
    strategies_.push_back(
        std::make_unique<Strategy>(std::move(content_bounds_)));
    prioritization_config_.changing_threshold = false;
    prioritization_config_.damage_rate_threshold = false;
  }

  Strategy& strategy() { return static_cast<Strategy&>(*strategies_.back()); }
  // Empty mock methods since this test set up uses strategies, which are only
  // for ozone and android.
  MOCK_CONST_METHOD0(NeedsSurfaceDamageRectList, bool());
  bool IsOverlaySupported() const override { return true; }

  // A list of possible overlay candidates is presented to this function.
  // The expected result is that those candidates that can be in a separate
  // plane are marked with |overlay_handled| set to true, otherwise they are
  // to be traditionally composited. Candidates with |overlay_handled| set to
  // true must also have their |display_rect| converted to integer
  // coordinates if necessary.
  void CheckOverlaySupport(
      const OverlayProcessorInterface::OutputSurfaceOverlayPlane* primary_plane,
      OverlayCandidateList* surfaces) override {}

 private:
  std::vector<gfx::Rect> content_bounds_;
};

class GLRendererSwapWithBoundsTest : public GLRendererTest {
 protected:
  void RunTest(const std::vector<gfx::Rect>& content_bounds) {
    auto gl_owned = std::make_unique<TestGLES2Interface>();
    gl_owned->set_have_swap_buffers_with_bounds(true);

    auto provider = TestContextProvider::Create(std::move(gl_owned));
    provider->BindToCurrentThread();

    cc::FakeOutputSurfaceClient output_surface_client;
    std::unique_ptr<FakeOutputSurface> output_surface(
        FakeOutputSurface::Create3d(std::move(provider)));
    output_surface->BindToClient(&output_surface_client);

    auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
        output_surface->context_provider());

    RendererSettings settings;
    auto processor =
        std::make_unique<ContentBoundsOverlayProcessor>(content_bounds);
    FakeRendererGL renderer(&settings, &debug_settings_, output_surface.get(),
                            resource_provider.get(), processor.get());
    renderer.Initialize();
    EXPECT_EQ(true, renderer.use_swap_with_bounds());
    renderer.SetVisible(true);

    gfx::Size viewport_size(100, 100);

    {
      AggregatedRenderPassId root_pass_id{1};
      cc::AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
                        gfx::Rect(viewport_size), gfx::Transform(),
                        cc::FilterOperations());

      renderer.DecideRenderPassAllocationsForFrame(
          render_passes_in_draw_order_);
      DrawFrame(&renderer, viewport_size);
      renderer.SwapBuffers({});

      std::vector<gfx::Rect> expected_content_bounds;
      EXPECT_EQ(content_bounds,
                output_surface->last_sent_frame()->content_bounds);
    }
  }
};

TEST_F(GLRendererSwapWithBoundsTest, EmptyContent) {
  std::vector<gfx::Rect> content_bounds;
  RunTest(content_bounds);
}

TEST_F(GLRendererSwapWithBoundsTest, NonEmpty) {
  std::vector<gfx::Rect> content_bounds;
  content_bounds.push_back(gfx::Rect(0, 0, 10, 10));
  content_bounds.push_back(gfx::Rect(20, 20, 30, 30));
  RunTest(content_bounds);
}
#endif  // defined(USE_OZONE) || defined(OS_ANDROID)

#if defined(OS_APPLE)
class MockCALayerGLES2Interface : public TestGLES2Interface {
 public:
  MOCK_METHOD6(ScheduleCALayerSharedStateCHROMIUM,
               void(GLfloat opacity,
                    GLboolean is_clipped,
                    const GLfloat* clip_rect,
                    const GLfloat* rounded_corner_bounds,
                    GLint sorting_context_id,
                    const GLfloat* transform));
  MOCK_METHOD6(ScheduleCALayerCHROMIUM,
               void(GLuint contents_texture_id,
                    const GLfloat* contents_rect,
                    GLuint background_color,
                    GLuint edge_aa_mask,
                    const GLfloat* bounds_rect,
                    GLuint filter));
  MOCK_METHOD2(ScheduleCALayerInUseQueryCHROMIUM,
               void(GLsizei count, const GLuint* textures));
  MOCK_METHOD5(
      Uniform4f,
      void(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w));
};

class CALayerGLRendererTest : public GLRendererTest {
 protected:
  void SetUp() override {
    // A mock GLES2Interface that can watch CALayer stuff happen.
    auto gles2_interface = std::make_unique<MockCALayerGLES2Interface>();
    // Support image storage for GpuMemoryBuffers, needed for
    // CALayers/IOSurfaces backed by textures.
    gles2_interface->set_support_texture_storage_image(true);
    // Allow the renderer to make an empty SwapBuffers - skipping even the
    // root RenderPass.
    gles2_interface->set_have_commit_overlay_planes(true);

    gl_ = gles2_interface.get();

    auto provider = TestContextProvider::Create(std::move(gles2_interface));
    provider->BindToCurrentThread();

    cc::FakeOutputSurfaceClient output_surface_client;
    output_surface_ = FakeOutputSurface::Create3d(std::move(provider));
    output_surface_->BindToClient(&output_surface_client);

    display_resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
        output_surface_->context_provider());

    settings_ = std::make_unique<RendererSettings>();
    // This setting is enabled to use CALayer overlays.
    settings_->release_overlay_resources_after_gpu_query = true;
    // The Mac TestOverlayProcessor default to enable CALayer overlays, then all
    // damage is removed and we can skip the root RenderPass, swapping empty.
    overlay_processor_ = std::make_unique<OverlayProcessorMac>(
        std::make_unique<CALayerOverlayProcessor>(true));
    renderer_ = std::make_unique<FakeRendererGL>(
        settings_.get(), &debug_settings_, output_surface_.get(),
        display_resource_provider_.get(), overlay_processor_.get(),
        base::ThreadTaskRunnerHandle::Get());
    renderer_->Initialize();
    renderer_->SetVisible(true);
  }

  void TearDown() override {
    renderer_.reset();
    display_resource_provider_.reset();
    output_surface_.reset();
  }

  void DrawBlackFrame(const gfx::Size& viewport_size) {
    AggregatedRenderPassId root_pass_id{1};

    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorBLACK);

    renderer().DecideRenderPassAllocationsForFrame(
        render_passes_in_draw_order_);

    DrawFrame(&renderer(), viewport_size);
    renderer().SwapBuffers(DirectRenderer::SwapFrameData());
    renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle());
    Mock::VerifyAndClearExpectations(&gl());
  }

  MockCALayerGLES2Interface& gl() const { return *gl_; }
  FakeRendererGL& renderer() const { return *renderer_; }
  FakeOutputSurface& output_surface() const { return *output_surface_; }

 private:
  MockCALayerGLES2Interface* gl_;
  std::unique_ptr<FakeOutputSurface> output_surface_;
  std::unique_ptr<DisplayResourceProviderGL> display_resource_provider_;
  std::unique_ptr<RendererSettings> settings_;
  std::unique_ptr<OverlayProcessorInterface> overlay_processor_;
  std::unique_ptr<FakeRendererGL> renderer_;
};

TEST_F(CALayerGLRendererTest, CALayerOverlaysWithAllQuadsPromoted) {
  gfx::Size viewport_size(10, 10);

  // Draw an empty frame to make sure output surface is reshaped before tests.
  DrawBlackFrame(viewport_size);

  // This frame has a root pass with a CompositorRenderPassDrawQuad pointing to
  // a child pass that is at 1,2 to make it identifiable.
  AggregatedRenderPassId child_pass_id{2};
  AggregatedRenderPassId root_pass_id{1};
  {
    AggregatedRenderPass* child_pass =
        cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                          gfx::Rect(viewport_size) + gfx::Vector2d(1, 2),
                          gfx::Transform(), cc::FilterOperations());
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          gfx::Transform(), SkBlendMode::kSrcOver);
  }

  renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);

  // The child pass is drawn, promoted to an overlay, and scheduled as a
  // CALayer.
  {
    InSequence sequence;
    EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
    EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
        .WillOnce(
            Invoke([](GLuint contents_texture_id, const GLfloat* contents_rect,
                      GLuint background_color, GLuint edge_aa_mask,
                      const GLfloat* bounds_rect, GLuint filter) {
              // This is the child CompositorRenderPassDrawQuad.
              EXPECT_EQ(1, bounds_rect[0]);
              EXPECT_EQ(2, bounds_rect[1]);
            }));
  }
  DrawFrame(&renderer(), viewport_size);
  Mock::VerifyAndClearExpectations(&gl());

  renderer().SwapBuffers(DirectRenderer::SwapFrameData());

  // The damage was eliminated when everything was promoted to CALayers.
  ASSERT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect);
  EXPECT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect->IsEmpty());

  // Frame number 2. Same inputs, except...
  {
    AggregatedRenderPass* child_pass =
        cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                          gfx::Rect(viewport_size) + gfx::Vector2d(1, 2),
                          gfx::Transform(), cc::FilterOperations());
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          gfx::Transform(), SkBlendMode::kSrcOver);

    // Use a cached RenderPass for the child.
    child_pass->cache_render_pass = true;
  }

  renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);

  // The child CompositorRenderPassDrawQuad gets promoted again, but importantly
  // it did not itself have to be drawn this time as it can use the cached
  // texture. Because we can skip the child pass, and the root pass (all quads
  // were promoted), this exposes edge cases in GLRenderer if it assumes we draw
  // at least one RenderPass. This still works, doesn't crash, etc, and the
  // CompositorRenderPassDrawQuad is emitted.
  {
    InSequence sequence;
    EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
    EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _));
  }
  DrawFrame(&renderer(), viewport_size);
  Mock::VerifyAndClearExpectations(&gl());

  renderer().SwapBuffers(DirectRenderer::SwapFrameData());
}

TEST_F(CALayerGLRendererTest, CALayerRoundRects) {
  gfx::Size viewport_size(10, 10);

  // Draw an empty frame to make sure output surface is reshaped before tests.
  DrawBlackFrame(viewport_size);

  for (size_t subtest = 0; subtest < 3; ++subtest) {
    AggregatedRenderPass* child_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, AggregatedRenderPassId{1},
        gfx::Rect(250, 250), gfx::Transform(), cc::FilterOperations());

    AggregatedRenderPassId root_pass_id{1};
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    auto* quad = cc::AddRenderPassQuad(root_pass, child_pass);
    SharedQuadState* sqs =
        const_cast<SharedQuadState*>(quad->shared_quad_state);

    sqs->clip_rect = gfx::Rect(2, 2, 6, 6);
    const float radius = 2;
    sqs->mask_filter_info =
        gfx::MaskFilterInfo(gfx::RRectF(gfx::RectF(*sqs->clip_rect), radius));

    switch (subtest) {
      case 0:
        // Subtest 0 is a simple round rect that matches the clip rect, and
        // should be handled by CALayers.
        EXPECT_CALL(gl(), Uniform4f(_, _, _, _, _)).Times(1);
        EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _))
            .Times(1);
        EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)).Times(1);
        break;
      case 1:
        // Subtest 1 doesn't match clip and rounded rect, but we can still
        // use CALayers.
        sqs->clip_rect = gfx::Rect(3, 3, 4, 4);
        EXPECT_CALL(gl(), Uniform4f(_, _, _, _, _)).Times(1);
        EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)).Times(1);
        break;
      case 2:
        // Subtest 2 has a non-simple rounded rect.
        gfx::RRectF rounded_corner_bounds =
            sqs->mask_filter_info.rounded_corner_bounds();
        rounded_corner_bounds.SetCornerRadii(gfx::RRectF::Corner::kUpperLeft, 1,
                                             1);
        sqs->mask_filter_info = gfx::MaskFilterInfo(rounded_corner_bounds);
        // Called 2 extra times in order to set up the rounded corner
        // parameters in the shader, because the CALayer is not handling
        // the rounded corners.
        EXPECT_CALL(gl(), Uniform4f(_, _, _, _, _)).Times(3);
        EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)).Times(0);
        break;
    }

    renderer().DecideRenderPassAllocationsForFrame(
        render_passes_in_draw_order_);
    DrawFrame(&renderer(), viewport_size);
    Mock::VerifyAndClearExpectations(&gl());
  }
}

TEST_F(CALayerGLRendererTest, CALayerOverlaysReusesTextureWithDifferentSizes) {
  gfx::Size viewport_size(300, 300);

  // Draw an empty frame to make sure output surface is reshaped before tests.
  DrawBlackFrame(viewport_size);

  // This frame has a root pass with a CompositorRenderPassDrawQuad pointing to
  // a child pass that is at 1,2 to make it identifiable. The child's size is
  // 250x251, but it will be rounded up to a multiple of 64 in order to promote
  // easier texture reuse. See https://crbug.com/146070.
  AggregatedRenderPassId child_pass_id{2};
  AggregatedRenderPassId root_pass_id{1};
  {
    AggregatedRenderPass* child_pass =
        cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                          gfx::Rect(250, 251) + gfx::Vector2d(1, 2),
                          gfx::Transform(), cc::FilterOperations());
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          gfx::Transform(), SkBlendMode::kSrcOver);
  }

  renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);

  // The child pass is drawn, promoted to an overlay, and scheduled as a
  // CALayer. The bounds of the texture are rounded up to 256x256. We save the
  // texture ID to make sure we reuse it correctly.
  uint32_t saved_texture_id = 0;
  {
    InSequence sequence;
    EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
    EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
        .WillOnce(
            Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
                       GLuint background_color, GLuint edge_aa_mask,
                       const GLfloat* bounds_rect, GLuint filter) {
              // This is the child CompositorRenderPassDrawQuad.
              EXPECT_EQ(1, bounds_rect[0]);
              EXPECT_EQ(2, bounds_rect[1]);
              // The size is rounded to a multiple of 64.
              EXPECT_EQ(256, bounds_rect[2]);
              EXPECT_EQ(256, bounds_rect[3]);
              saved_texture_id = contents_texture_id;
            }));
  }
  DrawFrame(&renderer(), viewport_size);
  Mock::VerifyAndClearExpectations(&gl());
  renderer().SwapBuffers(DirectRenderer::SwapFrameData());

  // ScheduleCALayerCHROMIUM happened and used a non-0 texture.
  EXPECT_NE(saved_texture_id, 0u);

  // The damage was eliminated when everything was promoted to CALayers.
  ASSERT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect);
  EXPECT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect->IsEmpty());

  // The texture will be checked to verify if it is free yet.
  EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(1, _));
  renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle());
  Mock::VerifyAndClearExpectations(&gl());

  // Frame number 2. We change the size of the child RenderPass to be smaller
  // than the next multiple of 64, but larger than half the previous size so
  // that our texture reuse heuristics will reuse the texture if it is free.
  // For now, it is not.
  {
    AggregatedRenderPass* child_pass =
        cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                          gfx::Rect(190, 191) + gfx::Vector2d(1, 2),
                          gfx::Transform(), cc::FilterOperations());
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          gfx::Transform(), SkBlendMode::kSrcOver);
  }

  renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);

  // The child RenderPass will use a new 192x192 texture, since the last texture
  // is still in use.
  {
    InSequence sequence;
    EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
    EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
        .WillOnce(
            Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
                       GLuint background_color, GLuint edge_aa_mask,
                       const GLfloat* bounds_rect, GLuint filter) {
              // New texture id.
              EXPECT_NE(saved_texture_id, contents_texture_id);
              EXPECT_EQ(1, bounds_rect[0]);
              EXPECT_EQ(2, bounds_rect[1]);
              // The texture is 192x192 since we snap up to multiples of 64.
              EXPECT_EQ(192, bounds_rect[2]);
              EXPECT_EQ(192, bounds_rect[3]);
            }));
  }
  DrawFrame(&renderer(), viewport_size);
  Mock::VerifyAndClearExpectations(&gl());
  renderer().SwapBuffers(DirectRenderer::SwapFrameData());

  // There are now 2 textures to check if they are free.
  EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(2, _));
  renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle());
  Mock::VerifyAndClearExpectations(&gl());

  // The first (256x256) texture is returned to the GLRenderer.
  renderer().DidReceiveTextureInUseResponses({{saved_texture_id, false}});

  // Frame number 3 looks just like frame number 2. The child RenderPass is
  // smaller than the next multiple of 64 from the released texture, but larger
  // than half of its size so that our texture reuse heuristics will kick in.
  {
    AggregatedRenderPass* child_pass =
        cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                          gfx::Rect(190, 191) + gfx::Vector2d(1, 2),
                          gfx::Transform(), cc::FilterOperations());
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          gfx::Transform(), SkBlendMode::kSrcOver);
  }

  renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);

  // The child RenderPass would try to use a 192x192 texture, but since we have
  // an existing 256x256 texture, we can reuse that.
  {
    InSequence sequence;
    EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
    EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
        .WillOnce(
            Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
                       GLuint background_color, GLuint edge_aa_mask,
                       const GLfloat* bounds_rect, GLuint filter) {
              // The first texture is reused.
              EXPECT_EQ(saved_texture_id, contents_texture_id);
              // This is the child CompositorRenderPassDrawQuad.
              EXPECT_EQ(1, bounds_rect[0]);
              EXPECT_EQ(2, bounds_rect[1]);
              // The size here is the size of the texture being used, not
              // the size we tried to use (192x192).
              EXPECT_EQ(256, bounds_rect[2]);
              EXPECT_EQ(256, bounds_rect[3]);
            }));
  }
  DrawFrame(&renderer(), viewport_size);
  Mock::VerifyAndClearExpectations(&gl());
  renderer().SwapBuffers(DirectRenderer::SwapFrameData());
}

TEST_F(CALayerGLRendererTest, CALayerOverlaysDontReuseTooBigTexture) {
  gfx::Size viewport_size(300, 300);

  // Draw an empty frame to make sure output surface is reshaped before tests.
  DrawBlackFrame(viewport_size);

  // This frame has a root pass with a CompositorRenderPassDrawQuad pointing to
  // a child pass that is at 1,2 to make it identifiable. The child's size is
  // 250x251, but it will be rounded up to a multiple of 64 in order to promote
  // easier texture reuse. See https://crbug.com/146070.
  AggregatedRenderPassId child_pass_id{2};
  AggregatedRenderPassId root_pass_id{1};
  {
    AggregatedRenderPass* child_pass =
        cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                          gfx::Rect(250, 251) + gfx::Vector2d(1, 2),
                          gfx::Transform(), cc::FilterOperations());
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          gfx::Transform(), SkBlendMode::kSrcOver);
  }

  renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);

  // The child pass is drawn, promoted to an overlay, and scheduled as a
  // CALayer. The bounds of the texture are rounded up to 256x256. We save the
  // texture ID to make sure we reuse it correctly.
  uint32_t saved_texture_id = 0;
  {
    InSequence sequence;
    EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
    EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
        .WillOnce(
            Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
                       GLuint background_color, GLuint edge_aa_mask,
                       const GLfloat* bounds_rect, GLuint filter) {
              // This is the child CompositorRenderPassDrawQuad.
              EXPECT_EQ(1, bounds_rect[0]);
              EXPECT_EQ(2, bounds_rect[1]);
              // The size is rounded to a multiple of 64.
              EXPECT_EQ(256, bounds_rect[2]);
              EXPECT_EQ(256, bounds_rect[3]);
              saved_texture_id = contents_texture_id;
            }));
  }
  DrawFrame(&renderer(), viewport_size);
  Mock::VerifyAndClearExpectations(&gl());
  renderer().SwapBuffers(DirectRenderer::SwapFrameData());

  // ScheduleCALayerCHROMIUM happened and used a non-0 texture.
  EXPECT_NE(saved_texture_id, 0u);

  // The damage was eliminated when everything was promoted to CALayers.
  ASSERT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect);
  EXPECT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect->IsEmpty());

  // The texture will be checked to verify if it is free yet.
  EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(1, _));
  renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle());
  Mock::VerifyAndClearExpectations(&gl());

  // Frame number 2. We change the size of the child RenderPass to be much
  // smaller.
  {
    AggregatedRenderPass* child_pass =
        cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                          gfx::Rect(20, 21) + gfx::Vector2d(1, 2),
                          gfx::Transform(), cc::FilterOperations());
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          gfx::Transform(), SkBlendMode::kSrcOver);
  }

  renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);

  // The child RenderPass will use a new 64x64 texture, since the last texture
  // is still in use.
  {
    InSequence sequence;
    EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
    EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
        .WillOnce(
            Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
                       GLuint background_color, GLuint edge_aa_mask,
                       const GLfloat* bounds_rect, GLuint filter) {
              // New texture id.
              EXPECT_NE(saved_texture_id, contents_texture_id);
              EXPECT_EQ(1, bounds_rect[0]);
              EXPECT_EQ(2, bounds_rect[1]);
              // The texture is 64x64 since we snap up to multiples of 64.
              EXPECT_EQ(64, bounds_rect[2]);
              EXPECT_EQ(64, bounds_rect[3]);
            }));
  }
  DrawFrame(&renderer(), viewport_size);
  Mock::VerifyAndClearExpectations(&gl());
  renderer().SwapBuffers(DirectRenderer::SwapFrameData());

  // There are now 2 textures to check if they are free.
  EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(2, _));
  renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle());
  Mock::VerifyAndClearExpectations(&gl());

  // The first (256x256) texture is returned to the GLRenderer.
  renderer().DidReceiveTextureInUseResponses({{saved_texture_id, false}});

  // Frame number 3 looks just like frame number 2. The child RenderPass is
  // too small to reuse the old texture.
  {
    AggregatedRenderPass* child_pass =
        cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                          gfx::Rect(20, 21) + gfx::Vector2d(1, 2),
                          gfx::Transform(), cc::FilterOperations());
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          gfx::Transform(), SkBlendMode::kSrcOver);
  }

  renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);

  // The child RenderPass would try to use a 64x64 texture. We have a free and
  // existing 256x256 texture, but it's too large for us to reuse it.
  {
    InSequence sequence;
    EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
    EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
        .WillOnce(
            Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
                       GLuint background_color, GLuint edge_aa_mask,
                       const GLfloat* bounds_rect, GLuint filter) {
              // The first texture is not reused.
              EXPECT_NE(saved_texture_id, contents_texture_id);
              // This is the child CompositorRenderPassDrawQuad.
              EXPECT_EQ(1, bounds_rect[0]);
              EXPECT_EQ(2, bounds_rect[1]);
              // The new texture has a smaller size.
              EXPECT_EQ(64, bounds_rect[2]);
              EXPECT_EQ(64, bounds_rect[3]);
            }));
  }
  DrawFrame(&renderer(), viewport_size);
  Mock::VerifyAndClearExpectations(&gl());
  renderer().SwapBuffers(DirectRenderer::SwapFrameData());
}

TEST_F(CALayerGLRendererTest, CALayerOverlaysReuseAfterNoSwapBuffers) {
  gfx::Size viewport_size(300, 300);

  // This frame has a root pass with a CompositorRenderPassDrawQuad pointing to
  // a child pass that is at 1,2 to make it identifiable.
  AggregatedRenderPassId child_pass_id{2};
  AggregatedRenderPassId root_pass_id{1};
  {
    AggregatedRenderPass* child_pass =
        cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                          gfx::Rect(100, 100) + gfx::Vector2d(1, 2),
                          gfx::Transform(), cc::FilterOperations());
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          gfx::Transform(), SkBlendMode::kSrcOver);
  }

  renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);

  // The child pass is drawn, promoted to an overlay, and scheduled as a
  // CALayer. We save the texture ID to make sure we reuse it correctly.
  uint32_t saved_texture_id = 0;
  {
    InSequence sequence;
    EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
    EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
        .WillOnce(
            Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
                       GLuint background_color, GLuint edge_aa_mask,
                       const GLfloat* bounds_rect, GLuint filter) {
              // This is the child CompositorRenderPassDrawQuad.
              EXPECT_EQ(1, bounds_rect[0]);
              EXPECT_EQ(2, bounds_rect[1]);
              saved_texture_id = contents_texture_id;
            }));
  }
  DrawFrame(&renderer(), viewport_size);
  Mock::VerifyAndClearExpectations(&gl());

  // ScheduleCALayerCHROMIUM happened and used a non-0 texture.
  EXPECT_NE(saved_texture_id, 0u);

  // SwapBuffers() is *not* called though! Display can do this sometimes.

  // Frame number 2. We can not reuse the texture since the last one isn't
  // returned yet. We use a different size so we can control which texture gets
  // reused later.
  {
    AggregatedRenderPass* child_pass =
        cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                          gfx::Rect(200, 200) + gfx::Vector2d(1, 2),
                          gfx::Transform(), cc::FilterOperations());
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          gfx::Transform(), SkBlendMode::kSrcOver);
  }

  renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);

  uint32_t second_saved_texture_id = 0;
  {
    InSequence sequence;
    EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
    EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
        .WillOnce(
            Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
                       GLuint background_color, GLuint edge_aa_mask,
                       const GLfloat* bounds_rect, GLuint filter) {
              // New texture id.
              EXPECT_NE(saved_texture_id, contents_texture_id);
              EXPECT_EQ(1, bounds_rect[0]);
              EXPECT_EQ(2, bounds_rect[1]);
              second_saved_texture_id = contents_texture_id;
            }));
  }
  DrawFrame(&renderer(), viewport_size);
  Mock::VerifyAndClearExpectations(&gl());

  // SwapBuffers() *does* happen this time.
  renderer().SwapBuffers(DirectRenderer::SwapFrameData());

  // There are 2 textures to check if they are free.
  EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(2, _));
  renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle());
  Mock::VerifyAndClearExpectations(&gl());

  // Both textures get returned and the 2nd one can be reused.
  renderer().DidReceiveTextureInUseResponses(
      {{saved_texture_id, false}, {second_saved_texture_id, false}});

  // Frame number 3 looks just like frame number 2.
  {
    AggregatedRenderPass* child_pass =
        cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                          gfx::Rect(200, 200) + gfx::Vector2d(1, 2),
                          gfx::Transform(), cc::FilterOperations());
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          gfx::Transform(), SkBlendMode::kSrcOver);
  }

  renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);

  // The 2nd texture that we sent has been returned so we can reuse it. We
  // verify that happened.
  {
    InSequence sequence;
    EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
    EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
        .WillOnce(
            Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
                       GLuint background_color, GLuint edge_aa_mask,
                       const GLfloat* bounds_rect, GLuint filter) {
              // The second texture is reused.
              EXPECT_EQ(second_saved_texture_id, contents_texture_id);
              // This is the child CompositorRenderPassDrawQuad.
              EXPECT_EQ(1, bounds_rect[0]);
              EXPECT_EQ(2, bounds_rect[1]);
            }));
  }
  DrawFrame(&renderer(), viewport_size);
  Mock::VerifyAndClearExpectations(&gl());
  renderer().SwapBuffers(DirectRenderer::SwapFrameData());
}

TEST_F(CALayerGLRendererTest, CALayerOverlaysReuseManyIfReturnedSlowly) {
  gfx::Size viewport_size(300, 300);

  // Draw an empty frame to make sure output surface is reshaped before tests.
  DrawBlackFrame(viewport_size);

  // Each frame has a root pass with a CompositorRenderPassDrawQuad pointing to
  // a child pass. We generate a bunch of frames and swap them, each with a
  // different child RenderPass id, without getting any of the resources back
  // from the OS.
  AggregatedRenderPassId root_pass_id{1};

  // The number is at least 2 larger than the number of textures we expect to
  // reuse, so that we can leave one in the OS, and have 1 texture returned but
  // not reused.
  const int kNumSendManyTextureIds = 7;
  uint32_t sent_texture_ids[kNumSendManyTextureIds];
  for (int i = 0; i < kNumSendManyTextureIds; ++i) {
    AggregatedRenderPass* child_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, AggregatedRenderPassId{i + 2},
        gfx::Rect(250, 251) + gfx::Vector2d(1, 2), gfx::Transform(),
        cc::FilterOperations());
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          gfx::Transform(), SkBlendMode::kSrcOver);

    renderer().DecideRenderPassAllocationsForFrame(
        render_passes_in_draw_order_);

    InSequence sequence;
    EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
    EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
        .WillOnce(
            Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
                       GLuint background_color, GLuint edge_aa_mask,
                       const GLfloat* bounds_rect, GLuint filter) {
              // This is the child CompositorRenderPassDrawQuad.
              EXPECT_EQ(1, bounds_rect[0]);
              EXPECT_EQ(2, bounds_rect[1]);
              sent_texture_ids[i] = contents_texture_id;
            }));
    DrawFrame(&renderer(), viewport_size);
    Mock::VerifyAndClearExpectations(&gl());
    renderer().SwapBuffers(DirectRenderer::SwapFrameData());

    // ScheduleCALayerCHROMIUM happened and used a non-0 texture.
    EXPECT_NE(sent_texture_ids[i], 0u);

    // The damage was eliminated when everything was promoted to CALayers.
    ASSERT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect);
    EXPECT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect->IsEmpty());

    // All sent textures will be checked to verify if they are free yet.
    EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(i + 1, _));
    renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle());
    Mock::VerifyAndClearExpectations(&gl());
  }

  // Now all but 1 texture get returned by the OS, so they are all inserted
  // into the cache for reuse.
  std::vector<uint32_t> returned_texture_ids;
  for (int i = 0; i < kNumSendManyTextureIds - 1; ++i) {
    uint32_t id = sent_texture_ids[i];
    renderer().DidReceiveTextureInUseResponses({{id, false}});
    returned_texture_ids.push_back(id);
  }

  // We should keep *some* of these textures around to reuse them across
  // multiple frames. https://crbug.com/146070 motivates this, and empirical
  // testing found 5 to be a good number.
  const int kNumSendReusedTextures = 5;
  // See comment on |kNumSendManyTextureIds|.
  ASSERT_LT(kNumSendReusedTextures, kNumSendManyTextureIds - 1);

  for (int i = 0; i < kNumSendReusedTextures + 1; ++i) {
    // We use different RenderPass ids to ensure that the cache allows reuse
    // even if they don't match.
    AggregatedRenderPass* child_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, AggregatedRenderPassId{i + 100},
        gfx::Rect(250, 251) + gfx::Vector2d(1, 2), gfx::Transform(),
        cc::FilterOperations());
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          gfx::Transform(), SkBlendMode::kSrcOver);

    renderer().DecideRenderPassAllocationsForFrame(
        render_passes_in_draw_order_);

    InSequence sequence;
    EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
    EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
        .WillOnce(Invoke([&](GLuint contents_texture_id,
                             const GLfloat* contents_rect,
                             GLuint background_color, GLuint edge_aa_mask,
                             const GLfloat* bounds_rect, GLuint filter) {
          // This is the child CompositorRenderPassDrawQuad.
          EXPECT_EQ(1, bounds_rect[0]);
          EXPECT_EQ(2, bounds_rect[1]);

          if (i < kNumSendReusedTextures) {
            // The texture id should be from the set of returned ones.
            EXPECT_THAT(returned_texture_ids, Contains(contents_texture_id));
            base::Erase(returned_texture_ids, contents_texture_id);
          } else {
            // More textures were returned at once than we expect to reuse
            // so eventually we should be making a new texture to show we're
            // not just keeping infinity textures in the cache.
            EXPECT_THAT(returned_texture_ids,
                        Not(Contains(contents_texture_id)));
            // This shows that there was some returned id that we didn't use.
            EXPECT_FALSE(returned_texture_ids.empty());
          }
        }));
    DrawFrame(&renderer(), viewport_size);
    Mock::VerifyAndClearExpectations(&gl());
    renderer().SwapBuffers(DirectRenderer::SwapFrameData());

    // All sent textures will be checked to verify if they are free yet. There's
    // also 1 outstanding texture to check for that wasn't returned yet from the
    // above loop.
    EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(i + 2, _));
    renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle());
    Mock::VerifyAndClearExpectations(&gl());
  }
}

TEST_F(CALayerGLRendererTest, CALayerOverlaysCachedTexturesAreFreed) {
  gfx::Size viewport_size(300, 300);

  // Draw an empty frame to make sure output surface is reshaped before tests.
  DrawBlackFrame(viewport_size);

  // Each frame has a root pass with a CompositorRenderPassDrawQuad pointing to
  // a child pass. We generate a bunch of frames and swap them, each with a
  // different child RenderPass id, without getting any of the resources back
  // from the OS.
  AggregatedRenderPassId child_pass_id{2};
  AggregatedRenderPassId root_pass_id{1};

  // We send a whole bunch of textures as overlays to the OS.
  const int kNumSendManyTextureIds = 7;
  uint32_t sent_texture_ids[kNumSendManyTextureIds];
  for (int i = 0; i < kNumSendManyTextureIds; ++i) {
    AggregatedRenderPass* child_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, AggregatedRenderPassId{i + 2},
        gfx::Rect(250, 251) + gfx::Vector2d(1, 2), gfx::Transform(),
        cc::FilterOperations());
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          gfx::Transform(), SkBlendMode::kSrcOver);

    renderer().DecideRenderPassAllocationsForFrame(
        render_passes_in_draw_order_);

    InSequence sequence;
    EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
    EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
        .WillOnce(
            Invoke([&](GLuint contents_texture_id, const GLfloat* contents_rect,
                       GLuint background_color, GLuint edge_aa_mask,
                       const GLfloat* bounds_rect, GLuint filter) {
              // This is the child CompositorRenderPassDrawQuad.
              EXPECT_EQ(1, bounds_rect[0]);
              EXPECT_EQ(2, bounds_rect[1]);
              sent_texture_ids[i] = contents_texture_id;
            }));
    DrawFrame(&renderer(), viewport_size);
    Mock::VerifyAndClearExpectations(&gl());
    renderer().SwapBuffers(DirectRenderer::SwapFrameData());

    // ScheduleCALayerCHROMIUM happened and used a non-0 texture.
    EXPECT_NE(sent_texture_ids[i], 0u);

    // The damage was eliminated when everything was promoted to CALayers.
    ASSERT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect);
    EXPECT_TRUE(output_surface().last_sent_frame()->sub_buffer_rect->IsEmpty());

    // All sent textures will be checked to verify if they are free yet.
    EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(i + 1, _));
    renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle());
    Mock::VerifyAndClearExpectations(&gl());
  }

  // Now all but 1 texture get returned by the OS, so they are all inserted
  // into the cache for reuse.
  std::vector<uint32_t> returned_texture_ids;
  for (int i = 0; i < kNumSendManyTextureIds - 1; ++i) {
    uint32_t id = sent_texture_ids[i];
    renderer().DidReceiveTextureInUseResponses({{id, false}});
    returned_texture_ids.push_back(id);
  }

  // We generate a bunch of frames that don't use the cache, one less than the
  // number of textures returned.
  for (int i = 0; i < kNumSendManyTextureIds - 2; ++i) {
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddQuad(root_pass, gfx::Rect(100, 100), SK_ColorRED);

    renderer().DecideRenderPassAllocationsForFrame(
        render_passes_in_draw_order_);

    InSequence sequence;
    EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
    EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _));
    DrawFrame(&renderer(), viewport_size);
    Mock::VerifyAndClearExpectations(&gl());
    renderer().SwapBuffers(DirectRenderer::SwapFrameData());

    // There's just 1 outstanding RenderPass texture to query for.
    EXPECT_CALL(gl(), ScheduleCALayerInUseQueryCHROMIUM(1, _));
    renderer().SwapBuffersComplete(/*release_fence=*/gfx::GpuFenceHandle());
    Mock::VerifyAndClearExpectations(&gl());
  }

  // By now the cache should be empty, to show that we don't keep cached
  // textures that won't be used forever. We generate a frame with a
  // CompositorRenderPassDrawQuad and verify that it does not reuse a texture
  // from the (empty) cache.
  {
    AggregatedRenderPass* child_pass =
        cc::AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                          gfx::Rect(250, 251) + gfx::Vector2d(1, 2),
                          gfx::Transform(), cc::FilterOperations());
    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, root_pass_id, gfx::Rect(viewport_size),
        gfx::Transform(), cc::FilterOperations());
    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          gfx::Transform(), SkBlendMode::kSrcOver);
  }

  renderer().DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);

  InSequence sequence;
  EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _));
  EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _))
      .WillOnce(Invoke([&](GLuint contents_texture_id,
                           const GLfloat* contents_rect,
                           GLuint background_color, GLuint edge_aa_mask,
                           const GLfloat* bounds_rect, GLuint filter) {
        // This is the child CompositorRenderPassDrawQuad.
        EXPECT_EQ(1, bounds_rect[0]);
        EXPECT_EQ(2, bounds_rect[1]);

        // More textures were returned at once than we expect to reuse
        // so eventually we should be making a new texture to show we're
        // not just keeping infinity textures in the cache.
        EXPECT_THAT(returned_texture_ids, Not(Contains(contents_texture_id)));
        // This shows that there was some returned id that we didn't use.
        EXPECT_FALSE(returned_texture_ids.empty());
      }));
  DrawFrame(&renderer(), viewport_size);
  Mock::VerifyAndClearExpectations(&gl());
  renderer().SwapBuffers(DirectRenderer::SwapFrameData());
}
#endif

class FramebufferWatchingGLRenderer : public FakeRendererGL {
 public:
  FramebufferWatchingGLRenderer(RendererSettings* settings,
                                const DebugRendererSettings* debug_settings,
                                OutputSurface* output_surface,
                                DisplayResourceProviderGL* resource_provider)
      : FakeRendererGL(settings,
                       debug_settings,
                       output_surface,
                       resource_provider) {}

  void BindFramebufferToOutputSurface() override {
    ++bind_root_framebuffer_calls_;
    FakeRendererGL::BindFramebufferToOutputSurface();
  }

  void BindFramebufferToTexture(
      const AggregatedRenderPassId render_pass_id) override {
    ++bind_child_framebuffer_calls_;
    FakeRendererGL::BindFramebufferToTexture(render_pass_id);
  }

  int bind_root_framebuffer_calls() const {
    return bind_root_framebuffer_calls_;
  }
  int bind_child_framebuffer_calls() const {
    return bind_child_framebuffer_calls_;
  }

  void ResetBindCalls() {
    bind_root_framebuffer_calls_ = bind_child_framebuffer_calls_ = 0;
  }

 private:
  int bind_root_framebuffer_calls_ = 0;
  int bind_child_framebuffer_calls_ = 0;
};

TEST_F(GLRendererTest, UndamagedRenderPassStillDrawnWhenNoPartialSwap) {
  auto provider = TestContextProvider::Create();
  provider->UnboundTestContextGL()->set_have_post_sub_buffer(true);
  provider->BindToCurrentThread();

  cc::FakeOutputSurfaceClient output_surface_client;
  auto output_surface = FakeOutputSurface::Create3d(std::move(provider));
  output_surface->BindToClient(&output_surface_client);

  auto resource_provider = std::make_unique<DisplayResourceProviderGL>(
      output_surface->context_provider());

  for (int i = 0; i < 2; ++i) {
    bool use_partial_swap = i == 0;
    SCOPED_TRACE(use_partial_swap);

    RendererSettings settings;
    settings.partial_swap_enabled = use_partial_swap;
    FramebufferWatchingGLRenderer renderer(&settings, &debug_settings_,
                                           output_surface.get(),
                                           resource_provider.get());
    renderer.Initialize();
    EXPECT_EQ(use_partial_swap, renderer.use_partial_swap());
    renderer.SetVisible(true);

    gfx::Size viewport_size(100, 100);
    gfx::Rect child_rect(10, 10);

    // First frame, the child and root RenderPass each have damage.
    AggregatedRenderPass* child_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, AggregatedRenderPassId{2}, child_rect,
        gfx::Transform(), cc::FilterOperations());
    cc::AddQuad(child_pass, child_rect, SK_ColorGREEN);
    child_pass->damage_rect = child_rect;

    AggregatedRenderPass* root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, AggregatedRenderPassId{1},
        gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
    cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorRED);
    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          gfx::Transform(), SkBlendMode::kSrcOver);
    root_pass->damage_rect = gfx::Rect(viewport_size);

    EXPECT_EQ(0, renderer.bind_root_framebuffer_calls());
    EXPECT_EQ(0, renderer.bind_child_framebuffer_calls());

    renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
    DrawFrame(&renderer, viewport_size);

    // We had to draw the root, and the child.
    EXPECT_EQ(1, renderer.bind_child_framebuffer_calls());
    // When the CompositorRenderPassDrawQuad in the root is drawn, we may
    // re-bind the root framebuffer. So it can be bound more than once.
    EXPECT_GE(renderer.bind_root_framebuffer_calls(), 1);

    // Reset counting.
    renderer.ResetBindCalls();

    // Second frame, the child RenderPass has no damage in it.
    child_pass = cc::AddRenderPass(&render_passes_in_draw_order_,
                                   AggregatedRenderPassId{2}, child_rect,
                                   gfx::Transform(), cc::FilterOperations());
    cc::AddQuad(child_pass, child_rect, SK_ColorGREEN);
    child_pass->damage_rect = gfx::Rect();

    // Root RenderPass has some damage that doesn't intersect the child.
    root_pass = cc::AddRenderPass(
        &render_passes_in_draw_order_, AggregatedRenderPassId{1},
        gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
    cc::AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorRED);
    cc::AddRenderPassQuad(root_pass, child_pass, kInvalidResourceId,
                          gfx::Transform(), SkBlendMode::kSrcOver);
    root_pass->damage_rect = gfx::Rect(child_rect.right(), 0, 10, 10);

    EXPECT_EQ(0, renderer.bind_root_framebuffer_calls());
    EXPECT_EQ(0, renderer.bind_child_framebuffer_calls());

    renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
    DrawFrame(&renderer, viewport_size);

    if (use_partial_swap) {
      // Without damage overlapping the child, it didn't need to be drawn (it
      // may choose to anyway but that'd be a waste). So we don't check for
      // |bind_child_framebuffer_calls|. But the root should have been drawn.
      EXPECT_EQ(renderer.bind_root_framebuffer_calls(), 1);
    } else {
      // Without partial swap, we have to draw the child still, this means
      // the child is bound as the framebuffer.
      EXPECT_EQ(1, renderer.bind_child_framebuffer_calls());
      // When the CompositorRenderPassDrawQuad in the root is drawn, as it must
      // be since we must draw the entire output, we may re-bind the root
      // framebuffer. So it can be bound more than once.
      EXPECT_GE(renderer.bind_root_framebuffer_calls(), 1);
    }
  }
}

#if defined(USE_OZONE) || defined(OS_ANDROID)
class GLRendererWithGpuFenceTest : public GLRendererTest {
 protected:
  GLRendererWithGpuFenceTest() {
    auto provider = TestContextProvider::Create();
    provider->BindToCurrentThread();
    provider->TestContextGL()->set_have_commit_overlay_planes(true);
    test_context_support_ = provider->support();

    output_surface_ = FakeOutputSurface::Create3d(std::move(provider));
    output_surface_->set_overlay_texture_id(kSurfaceOverlayTextureId);
    output_surface_->set_gpu_fence_id(kGpuFenceId);
    resource_provider_ = std::make_unique<DisplayResourceProviderGL>(
        output_surface_->context_provider());
    overlay_processor_ = std::make_unique<SingleOverlayOnTopProcessor>();
    overlay_processor_->AllowMultipleCandidates();
    renderer_ = std::make_unique<FakeRendererGL>(
        &settings_, &debug_settings_, output_surface_.get(),
        resource_provider_.get(), overlay_processor_.get(),
        base::ThreadTaskRunnerHandle::Get());
    renderer_->Initialize();
    renderer_->SetVisible(true);

    test_context_support_->SetScheduleOverlayPlaneCallback(
        base::BindRepeating(&MockOverlayScheduler::Schedule,
                            base::Unretained(&overlay_scheduler_)));
  }

  ~GLRendererWithGpuFenceTest() override {
    if (child_resource_provider_)
      child_resource_provider_->ShutdownAndReleaseAllResources();
  }

  ResourceId create_overlay_resource() {
    child_context_provider_ = TestContextProvider::Create();
    child_context_provider_->BindToCurrentThread();

    child_resource_provider_ = std::make_unique<ClientResourceProvider>();
    auto transfer_resource = TransferableResource::MakeGL(
        gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(),
        gfx::Size(256, 256), true);
    ResourceId client_resource_id = child_resource_provider_->ImportResource(
        transfer_resource, base::DoNothing());

    std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
        cc::SendResourceAndGetChildToParentMap(
            {client_resource_id}, resource_provider_.get(),
            child_resource_provider_.get(), child_context_provider_.get());
    return resource_map[client_resource_id];
  }

  static constexpr unsigned kSurfaceOverlayTextureId = 33;
  static constexpr unsigned kGpuFenceId = 66;
  static constexpr unsigned kGpuNoFenceId = 0;

  TestContextSupport* test_context_support_;

  cc::FakeOutputSurfaceClient output_surface_client_;
  std::unique_ptr<FakeOutputSurface> output_surface_;
  std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
  scoped_refptr<TestContextProvider> child_context_provider_;
  std::unique_ptr<ClientResourceProvider> child_resource_provider_;
  RendererSettings settings_;
  std::unique_ptr<SingleOverlayOnTopProcessor> overlay_processor_;
  std::unique_ptr<FakeRendererGL> renderer_;
  MockOverlayScheduler overlay_scheduler_;
};

TEST_F(GLRendererWithGpuFenceTest, GpuFenceIdIsUsedWithRootRenderPassOverlay) {
  gfx::Size viewport_size(100, 100);
  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, AggregatedRenderPassId{1},
      gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
  root_pass->has_transparent_background = false;

  EXPECT_CALL(overlay_scheduler_,
              Schedule(0, gfx::OVERLAY_TRANSFORM_NONE, kSurfaceOverlayTextureId,
                       gfx::Rect(viewport_size), _, _, kGpuFenceId))
      .Times(1);
  DrawFrame(renderer_.get(), viewport_size);
}

TEST_F(GLRendererWithGpuFenceTest,
       GpuFenceIdIsUsedOnlyForRootRenderPassOverlay) {
  gfx::Size viewport_size(100, 100);
  AggregatedRenderPass* root_pass = cc::AddRenderPass(
      &render_passes_in_draw_order_, AggregatedRenderPassId{1},
      gfx::Rect(viewport_size), gfx::Transform(), cc::FilterOperations());
  root_pass->has_transparent_background = false;

  bool needs_blending = false;
  bool premultiplied_alpha = false;
  bool flipped = false;
  bool nearest_neighbor = false;
  float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
  gfx::PointF uv_top_left(0, 0);
  gfx::PointF uv_bottom_right(1, 1);

  TextureDrawQuad* overlay_quad =
      root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
  SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState();
  shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size),
                       gfx::Rect(50, 50), gfx::MaskFilterInfo(), absl::nullopt,
                       false, 1, SkBlendMode::kSrcOver, 0);
  overlay_quad->SetNew(
      shared_state, gfx::Rect(viewport_size), gfx::Rect(viewport_size),
      needs_blending, create_overlay_resource(), premultiplied_alpha,
      uv_top_left, uv_bottom_right, SK_ColorTRANSPARENT, vertex_opacity,
      flipped, nearest_neighbor,
      /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear);

  EXPECT_CALL(overlay_scheduler_,
              Schedule(0, gfx::OVERLAY_TRANSFORM_NONE, kSurfaceOverlayTextureId,
                       gfx::Rect(viewport_size), _, _, kGpuFenceId))
      .Times(1);
  EXPECT_CALL(overlay_scheduler_,
              Schedule(1, gfx::OVERLAY_TRANSFORM_NONE, _,
                       gfx::Rect(viewport_size), _, _, kGpuNoFenceId))
      .Times(1);
  DrawFrame(renderer_.get(), viewport_size);
}
#endif  // defined(USE_OZONE) || defined(OS_ANDROID)

}  // namespace
}  // namespace viz

redpajama_set_name: RedPajamaGithub