// Copyright 2013 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 "cc/raster/zero_copy_tile_task_worker_pool.h"

#include <stdint.h>

#include <algorithm>

#include "base/macros.h"
#include "base/strings/stringprintf.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_event_argument.h"
#include "cc/debug/traced_value.h"
#include "cc/raster/raster_buffer.h"
#include "cc/resources/platform_color.h"
#include "cc/resources/resource.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/gpu_memory_buffer.h"

namespace cc {
namespace {

class RasterBufferImpl : public RasterBuffer {
 public:
  RasterBufferImpl(ResourceProvider* resource_provider,
                   const Resource* resource)
      : lock_(resource_provider, resource->id()), resource_(resource) {}

  // Overridden from RasterBuffer:
  void Playback(const DisplayListRasterSource* raster_source,
                const gfx::Rect& raster_full_rect,
                const gfx::Rect& raster_dirty_rect,
                uint64_t new_content_id,
                float scale,
                bool include_images) override {
    gfx::GpuMemoryBuffer* buffer = lock_.GetGpuMemoryBuffer();
    if (!buffer)
      return;

    DCHECK_EQ(1u, gfx::NumberOfPlanesForBufferFormat(buffer->GetFormat()));
    bool rv = buffer->Map();
    DCHECK(rv);
    DCHECK(buffer->memory(0));
    // TileTaskWorkerPool::PlaybackToMemory only supports unsigned strides.
    DCHECK_GE(buffer->stride(0), 0);

    // TODO(danakj): Implement partial raster with raster_dirty_rect.
    TileTaskWorkerPool::PlaybackToMemory(
        buffer->memory(0), resource_->format(), resource_->size(),
        buffer->stride(0), raster_source, raster_full_rect, raster_full_rect,
        scale, include_images);
    buffer->Unmap();
  }

 private:
  ResourceProvider::ScopedWriteLockGpuMemoryBuffer lock_;
  const Resource* resource_;

  DISALLOW_COPY_AND_ASSIGN(RasterBufferImpl);
};

}  // namespace

// static
scoped_ptr<TileTaskWorkerPool> ZeroCopyTileTaskWorkerPool::Create(
    base::SequencedTaskRunner* task_runner,
    TaskGraphRunner* task_graph_runner,
    ResourceProvider* resource_provider,
    bool use_rgba_4444_texture_format) {
  return make_scoped_ptr<TileTaskWorkerPool>(new ZeroCopyTileTaskWorkerPool(
      task_runner, task_graph_runner, resource_provider,
      use_rgba_4444_texture_format));
}

ZeroCopyTileTaskWorkerPool::ZeroCopyTileTaskWorkerPool(
    base::SequencedTaskRunner* task_runner,
    TaskGraphRunner* task_graph_runner,
    ResourceProvider* resource_provider,
    bool use_rgba_4444_texture_format)
    : task_runner_(task_runner),
      task_graph_runner_(task_graph_runner),
      namespace_token_(task_graph_runner->GetNamespaceToken()),
      resource_provider_(resource_provider),
      use_rgba_4444_texture_format_(use_rgba_4444_texture_format) {}

ZeroCopyTileTaskWorkerPool::~ZeroCopyTileTaskWorkerPool() {
}

TileTaskRunner* ZeroCopyTileTaskWorkerPool::AsTileTaskRunner() {
  return this;
}

void ZeroCopyTileTaskWorkerPool::Shutdown() {
  TRACE_EVENT0("cc", "ZeroCopyTileTaskWorkerPool::Shutdown");

  TaskGraph empty;
  task_graph_runner_->ScheduleTasks(namespace_token_, &empty);
  task_graph_runner_->WaitForTasksToFinishRunning(namespace_token_);
}

void ZeroCopyTileTaskWorkerPool::ScheduleTasks(TaskGraph* graph) {
  TRACE_EVENT0("cc", "ZeroCopyTileTaskWorkerPool::ScheduleTasks");

  ScheduleTasksOnOriginThread(this, graph);
  task_graph_runner_->ScheduleTasks(namespace_token_, graph);
}

void ZeroCopyTileTaskWorkerPool::CheckForCompletedTasks() {
  TRACE_EVENT0("cc", "ZeroCopyTileTaskWorkerPool::CheckForCompletedTasks");

  task_graph_runner_->CollectCompletedTasks(namespace_token_,
                                            &completed_tasks_);
  for (Task::Vector::const_iterator it = completed_tasks_.begin();
       it != completed_tasks_.end(); ++it) {
    TileTask* task = static_cast<TileTask*>(it->get());

    task->WillComplete();
    task->CompleteOnOriginThread(this);
    task->DidComplete();
  }
  completed_tasks_.clear();
}

ResourceFormat ZeroCopyTileTaskWorkerPool::GetResourceFormat(
    bool must_support_alpha) const {
  return use_rgba_4444_texture_format_
             ? RGBA_4444
             : resource_provider_->best_texture_format();
}

bool ZeroCopyTileTaskWorkerPool::GetResourceRequiresSwizzle(
    bool must_support_alpha) const {
  return !PlatformColor::SameComponentOrder(
      GetResourceFormat(must_support_alpha));
}

scoped_ptr<RasterBuffer> ZeroCopyTileTaskWorkerPool::AcquireBufferForRaster(
    const Resource* resource,
    uint64_t resource_content_id,
    uint64_t previous_content_id) {
  return make_scoped_ptr<RasterBuffer>(
      new RasterBufferImpl(resource_provider_, resource));
}

void ZeroCopyTileTaskWorkerPool::ReleaseBufferForRaster(
    scoped_ptr<RasterBuffer> buffer) {
  // Nothing to do here. RasterBufferImpl destructor cleans up after itself.
}

}  // namespace cc