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

#include "ui/gl/dual_gpu_state_mac.h"

#include <OpenGL/CGLRenderers.h>
#include <OpenGL/CGLTypes.h>

#include "base/cancelable_callback.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/no_destructor.h"
#include "base/synchronization/lock.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "ui/gl/gl_bindings.h"

namespace gl {

namespace {

const unsigned int kDelayLengthSeconds = 10;

}  // namespace

// static
DualGPUStateMac* DualGPUStateMac::GetInstance() {
  static base::NoDestructor<DualGPUStateMac> instance;
  return instance.get();
}

DualGPUStateMac::DualGPUStateMac() {}

DualGPUStateMac::~DualGPUStateMac() {}

void DualGPUStateMac::SwitchToHighPerformanceGPUIfNeeded() {
  AllocateDiscretePixelFormatObjectIfNeeded();
}

// This function is called by a delayed task posted in
// DualGPUStateMac::AttemptSwitchToLowPowerGPUWithDelay.
void DualGPUStateMac::SwitchToLowPowerGPU() {
  ReleaseDiscretePixelFormatObjectIfNeeded();
}

void DualGPUStateMac::AttemptSwitchToLowPowerGPUWithDelay() {
  if (base::SingleThreadTaskRunner::HasCurrentDefault()) {
    cancelable_delay_callback_.Reset(base::BindOnce(
        []() { DualGPUStateMac::GetInstance()->SwitchToLowPowerGPU(); }));

    base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
        FROM_HERE, cancelable_delay_callback_.callback(),
        base::Seconds(kDelayLengthSeconds));
  } else {
    SwitchToLowPowerGPU();
  }
}

void DualGPUStateMac::CancelDelayedSwitchToLowPowerGPU() {
  cancelable_delay_callback_.Cancel();
}

void DualGPUStateMac::AllocateDiscretePixelFormatObjectIfNeeded() {
  if (discrete_pixelformat_)
    return;
  CGLPixelFormatAttribute attribs[1];
  attribs[0] = static_cast<CGLPixelFormatAttribute>(0);
  GLint num_pixel_formats = 0;
  CGLChoosePixelFormat(attribs, &discrete_pixelformat_, &num_pixel_formats);
}

void DualGPUStateMac::ReleaseDiscretePixelFormatObjectIfNeeded() {
  if (!discrete_pixelformat_)
    return;
  CGLReleasePixelFormat(discrete_pixelformat_);
  discrete_pixelformat_ = nullptr;
}

}  // namespace gl