// Copyright 2018 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/main/viz_compositor_thread_runner.h"

#include <utility>

#include "base/command_line.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "components/viz/common/switches.h"
#include "components/viz/service/display_embedder/gpu_display_provider.h"
#include "components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h"
#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
#include "components/viz/service/gl/gpu_service_impl.h"
#include "gpu/ipc/command_buffer_task_executor.h"
#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
#include "ui/gfx/switches.h"

#if defined(USE_VIZ_DEVTOOLS)
#include "components/ui_devtools/css_agent.h"
#include "components/ui_devtools/devtools_server.h"
#include "components/ui_devtools/viz/dom_agent_viz.h"
#include "components/ui_devtools/viz/overlay_agent_viz.h"
#endif

#if defined(USE_OZONE)
#include "ui/ozone/public/ozone_platform.h"
#endif

namespace viz {
namespace {

const char kThreadName[] = "VizCompositorThread";

std::unique_ptr<VizCompositorThreadType> CreateAndStartCompositorThread() {
#if defined(OS_ANDROID)
  auto thread = std::make_unique<base::android::JavaHandlerThread>(
      kThreadName, base::ThreadPriority::DISPLAY);
  thread->Start();
  return thread;
#else  // !defined(OS_ANDROID)
  auto thread = std::make_unique<base::Thread>(kThreadName);

  base::Thread::Options thread_options;
#if defined(OS_WIN)
  // Windows needs a UI message loop for child HWND.
  thread_options.message_loop_type = base::MessageLoop::TYPE_UI;
#elif defined(USE_OZONE)
  // We may need a non-default message loop type for the platform surface.
  thread_options.message_loop_type =
      ui::OzonePlatform::GetInstance()->GetMessageLoopTypeForGpu();
#endif
#if defined(OS_CHROMEOS)
  thread_options.priority = base::ThreadPriority::DISPLAY;
#endif

  CHECK(thread->StartWithOptions(thread_options));
  return thread;
#endif  // !defined(OS_ANDROID)
}

}  // namespace

VizCompositorThreadRunner::VizCompositorThreadRunner()
    : thread_(CreateAndStartCompositorThread()),
      task_runner_(thread_->task_runner()) {}

VizCompositorThreadRunner::~VizCompositorThreadRunner() {
  task_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(&VizCompositorThreadRunner::TearDownOnCompositorThread,
                     base::Unretained(this)));
  thread_->Stop();
}

void VizCompositorThreadRunner::CreateFrameSinkManager(
    mojom::FrameSinkManagerParamsPtr params) {
  task_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(
          &VizCompositorThreadRunner::CreateFrameSinkManagerOnCompositorThread,
          base::Unretained(this), std::move(params), nullptr, nullptr));
}

void VizCompositorThreadRunner::CreateFrameSinkManager(
    mojom::FrameSinkManagerParamsPtr params,
    scoped_refptr<gpu::CommandBufferTaskExecutor> task_executor,
    GpuServiceImpl* gpu_service) {
  // All of the unretained objects are owned on the GPU thread and destroyed
  // after VizCompositorThread has been shutdown.
  task_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(
          &VizCompositorThreadRunner::CreateFrameSinkManagerOnCompositorThread,
          base::Unretained(this), std::move(params), std::move(task_executor),
          base::Unretained(gpu_service)));
}

#if defined(USE_VIZ_DEVTOOLS)
void VizCompositorThreadRunner::CreateVizDevTools(
    mojom::VizDevToolsParamsPtr params) {
  // It is safe to use Unretained(this) because |this| owns the |task_runner_|,
  // and will outlive it.
  task_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(
          &VizCompositorThreadRunner::CreateVizDevToolsOnCompositorThread,
          base::Unretained(this), std::move(params)));
}
#endif

void VizCompositorThreadRunner::CleanupForShutdown(
    base::OnceClosure cleanup_finished_callback) {
  task_runner_->PostTaskAndReply(
      FROM_HERE,
      base::BindOnce(
          &VizCompositorThreadRunner::CleanupForShutdownOnCompositorThread,
          base::Unretained(this)),
      std::move(cleanup_finished_callback));
}

void VizCompositorThreadRunner::CreateFrameSinkManagerOnCompositorThread(
    mojom::FrameSinkManagerParamsPtr params,
    scoped_refptr<gpu::CommandBufferTaskExecutor> task_executor,
    GpuServiceImpl* gpu_service) {
  DCHECK(task_runner_->BelongsToCurrentThread());
  DCHECK(!frame_sink_manager_);

  server_shared_bitmap_manager_ = std::make_unique<ServerSharedBitmapManager>();
  base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
      server_shared_bitmap_manager_.get(), "ServerSharedBitmapManager",
      task_runner_);

  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
  const bool headless = command_line->HasSwitch(switches::kHeadless);
  const bool run_all_compositor_stages_before_draw =
      command_line->HasSwitch(switches::kRunAllCompositorStagesBeforeDraw);

  if (task_executor) {
    DCHECK(gpu_service);
    // Create DisplayProvider usable for GPU + software compositing.
    auto gpu_memory_buffer_manager =
        std::make_unique<InProcessGpuMemoryBufferManager>(
            gpu_service->gpu_memory_buffer_factory(),
            gpu_service->sync_point_manager());
    auto* image_factory = gpu_service->gpu_image_factory();
    display_provider_ = std::make_unique<GpuDisplayProvider>(
        params->restart_id, gpu_service, std::move(task_executor), gpu_service,
        std::move(gpu_memory_buffer_manager), image_factory,
        server_shared_bitmap_manager_.get(), headless,
        run_all_compositor_stages_before_draw);
  } else {
    // Create DisplayProvider usable for software compositing only.
    display_provider_ = std::make_unique<GpuDisplayProvider>(
        params->restart_id, server_shared_bitmap_manager_.get(), headless,
        run_all_compositor_stages_before_draw);
  }

  // Create FrameSinkManagerImpl.
  base::Optional<uint32_t> activation_deadline_in_frames;
  if (params->use_activation_deadline)
    activation_deadline_in_frames = params->activation_deadline_in_frames;
  frame_sink_manager_ = std::make_unique<FrameSinkManagerImpl>(
      server_shared_bitmap_manager_.get(), activation_deadline_in_frames,
      display_provider_.get());
  frame_sink_manager_->BindAndSetClient(
      std::move(params->frame_sink_manager), nullptr,
      mojom::FrameSinkManagerClientPtr(
          std::move(params->frame_sink_manager_client)));

#if defined(USE_VIZ_DEVTOOLS)
  if (pending_viz_dev_tools_params_)
    InitVizDevToolsOnCompositorThread(std::move(pending_viz_dev_tools_params_));
#endif
}

#if defined(USE_VIZ_DEVTOOLS)
void VizCompositorThreadRunner::CreateVizDevToolsOnCompositorThread(
    mojom::VizDevToolsParamsPtr params) {
  if (!frame_sink_manager_) {
    DCHECK(!pending_viz_dev_tools_params_);
    pending_viz_dev_tools_params_ = std::move(params);
    return;
  }
  InitVizDevToolsOnCompositorThread(std::move(params));
}

void VizCompositorThreadRunner::InitVizDevToolsOnCompositorThread(
    mojom::VizDevToolsParamsPtr params) {
  DCHECK(frame_sink_manager_);
  devtools_server_ = ui_devtools::UiDevToolsServer::CreateForViz(
      network::mojom::TCPServerSocketPtr(std::move(params->server_socket)),
      params->server_port);
  auto dom_agent =
      std::make_unique<ui_devtools::DOMAgentViz>(frame_sink_manager_.get());
  auto css_agent = std::make_unique<ui_devtools::CSSAgent>(dom_agent.get());
  auto overlay_agent =
      std::make_unique<ui_devtools::OverlayAgentViz>(dom_agent.get());
  auto devtools_client = std::make_unique<ui_devtools::UiDevToolsClient>(
      "VizDevToolsClient", devtools_server_.get());
  devtools_client->AddAgent(std::move(dom_agent));
  devtools_client->AddAgent(std::move(css_agent));
  devtools_client->AddAgent(std::move(overlay_agent));
  devtools_server_->AttachClient(std::move(devtools_client));
}
#endif

void VizCompositorThreadRunner::CleanupForShutdownOnCompositorThread() {
  DCHECK(task_runner_->BelongsToCurrentThread());

  if (frame_sink_manager_)
    frame_sink_manager_->ForceShutdown();
}

void VizCompositorThreadRunner::TearDownOnCompositorThread() {
  DCHECK(task_runner_->BelongsToCurrentThread());

  if (server_shared_bitmap_manager_) {
    base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
        server_shared_bitmap_manager_.get());
  }

#if defined(USE_VIZ_DEVTOOLS)
  devtools_server_.reset();
#endif
  frame_sink_manager_.reset();
  display_provider_.reset();
  server_shared_bitmap_manager_.reset();
}

}  // namespace viz
