<commit_before>// 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/resources/raster_worker_pool.h"

#include "base/time/time.h"
#include "cc/resources/direct_raster_worker_pool.h"
#include "cc/resources/image_raster_worker_pool.h"
#include "cc/resources/pixel_buffer_raster_worker_pool.h"
#include "cc/resources/resource_provider.h"
#include "cc/resources/scoped_resource.h"
#include "cc/test/fake_output_surface.h"
#include "cc/test/fake_output_surface_client.h"
#include "cc/test/lap_timer.h"
#include "cc/test/test_web_graphics_context_3d.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/perf/perf_test.h"
#include "third_party/khronos/GLES2/gl2.h"

namespace cc {
namespace {

enum RasterWorkerPoolType {
  RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER,
  RASTER_WORKER_POOL_TYPE_IMAGE,
  RASTER_WORKER_POOL_TYPE_DIRECT
};

static const int kTimeLimitMillis = 2000;
static const int kWarmupRuns = 5;
static const int kTimeCheckInterval = 10;

class PerfWorkerPoolTaskImpl : public internal::WorkerPoolTask {
 public:
  PerfWorkerPoolTaskImpl() {}

  // Overridden from internal::Task:
  virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE {}

  // Overridden from internal::WorkerPoolTask:
  virtual void ScheduleOnOriginThread(internal::WorkerPoolTaskClient* client)
      OVERRIDE {}
  virtual void RunOnOriginThread() OVERRIDE {}
  virtual void CompleteOnOriginThread(internal::WorkerPoolTaskClient* client)
      OVERRIDE {}
  virtual void RunReplyOnOriginThread() OVERRIDE { Reset(); }

  void Reset() {
    did_run_ = false;
    did_complete_ = false;
  }

 protected:
  virtual ~PerfWorkerPoolTaskImpl() {}

 private:
  DISALLOW_COPY_AND_ASSIGN(PerfWorkerPoolTaskImpl);
};

class PerfRasterWorkerPoolTaskImpl : public internal::RasterWorkerPoolTask {
 public:
  PerfRasterWorkerPoolTaskImpl(scoped_ptr<ScopedResource> resource,
                               internal::WorkerPoolTask::Vector* dependencies)
      : internal::RasterWorkerPoolTask(resource.get(), dependencies),
        resource_(resource.Pass()) {}

  // Overridden from internal::Task:
  virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE {}

  // Overridden from internal::WorkerPoolTask:
  virtual void ScheduleOnOriginThread(internal::WorkerPoolTaskClient* client)
      OVERRIDE {
    client->AcquireCanvasForRaster(this);
  }
  virtual void RunOnOriginThread() OVERRIDE {}
  virtual void CompleteOnOriginThread(internal::WorkerPoolTaskClient* client)
      OVERRIDE {
    client->OnRasterCompleted(this, PicturePileImpl::Analysis());
  }
  virtual void RunReplyOnOriginThread() OVERRIDE { Reset(); }

  void Reset() {
    did_run_ = false;
    did_complete_ = false;
  }

 protected:
  virtual ~PerfRasterWorkerPoolTaskImpl() {}

 private:
  scoped_ptr<ScopedResource> resource_;

  DISALLOW_COPY_AND_ASSIGN(PerfRasterWorkerPoolTaskImpl);
};

class PerfTaskGraphRunnerImpl : public internal::TaskGraphRunner {
 public:
  PerfTaskGraphRunnerImpl() : internal::TaskGraphRunner(0, "Perf") {}
};

class PerfPixelBufferRasterWorkerPoolImpl : public PixelBufferRasterWorkerPool {
 public:
  PerfPixelBufferRasterWorkerPoolImpl(
      internal::TaskGraphRunner* task_graph_runner,
      ResourceProvider* resource_provider)
      : PixelBufferRasterWorkerPool(task_graph_runner,
                                    resource_provider,
                                    std::numeric_limits<size_t>::max()) {}
};

class PerfImageRasterWorkerPoolImpl : public ImageRasterWorkerPool {
 public:
  PerfImageRasterWorkerPoolImpl(internal::TaskGraphRunner* task_graph_runner,
                                ResourceProvider* resource_provider)
      : ImageRasterWorkerPool(task_graph_runner,
                              resource_provider,
                              GL_TEXTURE_2D) {}
};

class PerfDirectRasterWorkerPoolImpl : public DirectRasterWorkerPool {
 public:
  PerfDirectRasterWorkerPoolImpl(ResourceProvider* resource_provider,
                                 ContextProvider* context_provider)
      : DirectRasterWorkerPool(resource_provider, context_provider) {}
};

class RasterWorkerPoolPerfTest
    : public testing::TestWithParam<RasterWorkerPoolType>,
      public RasterWorkerPoolClient {
 public:
  typedef std::vector<scoped_refptr<internal::RasterWorkerPoolTask> >
      RasterTaskVector;

  RasterWorkerPoolPerfTest()
      : context_provider_(TestContextProvider::Create()),
        task_graph_runner_(new PerfTaskGraphRunnerImpl),
        timer_(kWarmupRuns,
               base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
               kTimeCheckInterval) {
    output_surface_ = FakeOutputSurface::Create3d(context_provider_).Pass();
    CHECK(output_surface_->BindToClient(&output_surface_client_));

    resource_provider_ = ResourceProvider::Create(
                             output_surface_.get(), NULL, 0, false, 1).Pass();

    switch (GetParam()) {
      case RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER:
        raster_worker_pool_.reset(new PerfPixelBufferRasterWorkerPoolImpl(
            task_graph_runner_.get(), resource_provider_.get()));
        break;
      case RASTER_WORKER_POOL_TYPE_IMAGE:
        raster_worker_pool_.reset(new PerfImageRasterWorkerPoolImpl(
            task_graph_runner_.get(), resource_provider_.get()));
        break;
      case RASTER_WORKER_POOL_TYPE_DIRECT:
        raster_worker_pool_.reset(new PerfDirectRasterWorkerPoolImpl(
            resource_provider_.get(), context_provider_));
        break;
    }

    DCHECK(raster_worker_pool_);
    raster_worker_pool_->SetClient(this);
  }
  virtual ~RasterWorkerPoolPerfTest() { resource_provider_.reset(); }

  // Overridden from testing::Test:
  virtual void TearDown() OVERRIDE {
    raster_worker_pool_->Shutdown();
    raster_worker_pool_->CheckForCompletedTasks();
  }

  // Overriden from RasterWorkerPoolClient:
  virtual bool ShouldForceTasksRequiredForActivationToComplete()
      const OVERRIDE {
    return false;
  }
  virtual void DidFinishRunningTasks() OVERRIDE {
    raster_worker_pool_->CheckForCompletedTasks();
    base::MessageLoop::current()->Quit();
  }
  virtual void DidFinishRunningTasksRequiredForActivation() OVERRIDE {}

  void RunMessageLoopUntilAllTasksHaveCompleted() {
    while (task_graph_runner_->RunTaskForTesting())
      continue;
    base::MessageLoop::current()->Run();
  }

  void CreateImageDecodeTasks(
      unsigned num_image_decode_tasks,
      internal::WorkerPoolTask::Vector* image_decode_tasks) {
    for (unsigned i = 0; i < num_image_decode_tasks; ++i)
      image_decode_tasks->push_back(new PerfWorkerPoolTaskImpl);
  }

  void CreateRasterTasks(
      unsigned num_raster_tasks,
      const internal::WorkerPoolTask::Vector& image_decode_tasks,
      RasterTaskVector* raster_tasks) {
    const gfx::Size size(1, 1);

    for (unsigned i = 0; i < num_raster_tasks; ++i) {
      scoped_ptr<ScopedResource> resource(
          ScopedResource::Create(resource_provider_.get()));
      resource->Allocate(size, ResourceProvider::TextureUsageAny, RGBA_8888);

      internal::WorkerPoolTask::Vector dependencies = image_decode_tasks;
      raster_tasks->push_back(
          new PerfRasterWorkerPoolTaskImpl(resource.Pass(), &dependencies));
    }
  }

  void BuildTaskQueue(RasterTaskQueue* queue,
                      const RasterTaskVector& raster_tasks) {
    for (RasterTaskVector::const_iterator it = raster_tasks.begin();
         it != raster_tasks.end();
         ++it)
      queue->items.push_back(RasterTaskQueue::Item(it->get(), false));
  }

  void RunScheduleTasksTest(const std::string& test_name,
                            unsigned num_raster_tasks,
                            unsigned num_image_decode_tasks) {
    internal::WorkerPoolTask::Vector image_decode_tasks;
    RasterTaskVector raster_tasks;
    CreateImageDecodeTasks(num_image_decode_tasks, &image_decode_tasks);
    CreateRasterTasks(num_raster_tasks, image_decode_tasks, &raster_tasks);

    // Avoid unnecessary heap allocations by reusing the same queue.
    RasterTaskQueue queue;

    timer_.Reset();
    do {
      queue.Reset();
      BuildTaskQueue(&queue, raster_tasks);
      raster_worker_pool_->ScheduleTasks(&queue);
      raster_worker_pool_->CheckForCompletedTasks();
      timer_.NextLap();
    } while (!timer_.HasTimeLimitExpired());

    RasterTaskQueue empty;
    raster_worker_pool_->ScheduleTasks(&empty);
    RunMessageLoopUntilAllTasksHaveCompleted();

    perf_test::PrintResult("schedule_tasks",
                           TestModifierString(),
                           test_name,
                           timer_.LapsPerSecond(),
                           "runs/s",
                           true);
  }

  void RunScheduleAlternateTasksTest(const std::string& test_name,
                                     unsigned num_raster_tasks,
                                     unsigned num_image_decode_tasks) {
    const size_t kNumVersions = 2;
    internal::WorkerPoolTask::Vector image_decode_tasks[kNumVersions];
    RasterTaskVector raster_tasks[kNumVersions];
    for (size_t i = 0; i < kNumVersions; ++i) {
      CreateImageDecodeTasks(num_image_decode_tasks, &image_decode_tasks[i]);
      CreateRasterTasks(
          num_raster_tasks, image_decode_tasks[i], &raster_tasks[i]);
    }

    // Avoid unnecessary heap allocations by reusing the same queue.
    RasterTaskQueue queue;

    size_t count = 0;
    timer_.Reset();
    do {
      queue.Reset();
      BuildTaskQueue(&queue, raster_tasks[count % kNumVersions]);
      raster_worker_pool_->ScheduleTasks(&queue);
      raster_worker_pool_->CheckForCompletedTasks();
      ++count;
      timer_.NextLap();
    } while (!timer_.HasTimeLimitExpired());

    RasterTaskQueue empty;
    raster_worker_pool_->ScheduleTasks(&empty);
    RunMessageLoopUntilAllTasksHaveCompleted();

    perf_test::PrintResult("schedule_alternate_tasks",
                           TestModifierString(),
                           test_name,
                           timer_.LapsPerSecond(),
                           "runs/s",
                           true);
  }

  void RunScheduleAndExecuteTasksTest(const std::string& test_name,
                                      unsigned num_raster_tasks,
                                      unsigned num_image_decode_tasks) {
    internal::WorkerPoolTask::Vector image_decode_tasks;
    RasterTaskVector raster_tasks;
    CreateImageDecodeTasks(num_image_decode_tasks, &image_decode_tasks);
    CreateRasterTasks(num_raster_tasks, image_decode_tasks, &raster_tasks);

    // Avoid unnecessary heap allocations by reusing the same queue.
    RasterTaskQueue queue;

    timer_.Reset();
    do {
      queue.Reset();
      BuildTaskQueue(&queue, raster_tasks);
      raster_worker_pool_->ScheduleTasks(&queue);
      RunMessageLoopUntilAllTasksHaveCompleted();
      timer_.NextLap();
    } while (!timer_.HasTimeLimitExpired());

    RasterTaskQueue empty;
    raster_worker_pool_->ScheduleTasks(&empty);
    RunMessageLoopUntilAllTasksHaveCompleted();

    perf_test::PrintResult("schedule_and_execute_tasks",
                           TestModifierString(),
                           test_name,
                           timer_.LapsPerSecond(),
                           "runs/s",
                           true);
  }

 private:
  std::string TestModifierString() const {
    switch (GetParam()) {
      case RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER:
        return std::string("_pixel_raster_worker_pool");
      case RASTER_WORKER_POOL_TYPE_IMAGE:
        return std::string("_image_raster_worker_pool");
      case RASTER_WORKER_POOL_TYPE_DIRECT:
        return std::string("_direct_raster_worker_pool");
    }
    NOTREACHED();
    return std::string();
  }

  scoped_refptr<TestContextProvider> context_provider_;
  FakeOutputSurfaceClient output_surface_client_;
  scoped_ptr<FakeOutputSurface> output_surface_;
  scoped_ptr<ResourceProvider> resource_provider_;
  scoped_ptr<internal::TaskGraphRunner> task_graph_runner_;
  scoped_ptr<RasterWorkerPool> raster_worker_pool_;
  LapTimer timer_;
};

TEST_P(RasterWorkerPoolPerfTest, ScheduleTasks) {
  RunScheduleTasksTest("1_0", 1, 0);
  RunScheduleTasksTest("32_0", 32, 0);
  RunScheduleTasksTest("1_1", 1, 1);
  RunScheduleTasksTest("32_1", 32, 1);
  RunScheduleTasksTest("1_4", 1, 4);
  RunScheduleTasksTest("32_4", 32, 4);
}

TEST_P(RasterWorkerPoolPerfTest, ScheduleAlternateTasks) {
  RunScheduleAlternateTasksTest("1_0", 1, 0);
  RunScheduleAlternateTasksTest("32_0", 32, 0);
  RunScheduleAlternateTasksTest("1_1", 1, 1);
  RunScheduleAlternateTasksTest("32_1", 32, 1);
  RunScheduleAlternateTasksTest("1_4", 1, 4);
  RunScheduleAlternateTasksTest("32_4", 32, 4);
}

TEST_P(RasterWorkerPoolPerfTest, ScheduleAndExecuteTasks) {
  RunScheduleAndExecuteTasksTest("1_0", 1, 0);
  RunScheduleAndExecuteTasksTest("32_0", 32, 0);
  RunScheduleAndExecuteTasksTest("1_1", 1, 1);
  RunScheduleAndExecuteTasksTest("32_1", 32, 1);
  RunScheduleAndExecuteTasksTest("1_4", 1, 4);
  RunScheduleAndExecuteTasksTest("32_4", 32, 4);
}

INSTANTIATE_TEST_CASE_P(RasterWorkerPoolPerfTests,
                        RasterWorkerPoolPerfTest,
                        ::testing::Values(RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER,
                                          RASTER_WORKER_POOL_TYPE_IMAGE,
                                          RASTER_WORKER_POOL_TYPE_DIRECT));

}  // namespace
}  // namespace cc
<commit_msg>cc: Improve RasterWorkerPool perftests.<commit_after>// 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/resources/raster_worker_pool.h"

#include "base/time/time.h"
#include "cc/resources/direct_raster_worker_pool.h"
#include "cc/resources/image_raster_worker_pool.h"
#include "cc/resources/pixel_buffer_raster_worker_pool.h"
#include "cc/resources/resource_provider.h"
#include "cc/resources/scoped_resource.h"
#include "cc/test/fake_output_surface.h"
#include "cc/test/fake_output_surface_client.h"
#include "cc/test/lap_timer.h"
#include "cc/test/test_web_graphics_context_3d.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/perf/perf_test.h"
#include "third_party/khronos/GLES2/gl2.h"

namespace cc {
namespace {

enum RasterWorkerPoolType {
  RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER,
  RASTER_WORKER_POOL_TYPE_IMAGE,
  RASTER_WORKER_POOL_TYPE_DIRECT
};

static const int kTimeLimitMillis = 2000;
static const int kWarmupRuns = 5;
static const int kTimeCheckInterval = 10;

class PerfWorkerPoolTaskImpl : public internal::WorkerPoolTask {
 public:
  PerfWorkerPoolTaskImpl() {}

  // Overridden from internal::Task:
  virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE {}

  // Overridden from internal::WorkerPoolTask:
  virtual void ScheduleOnOriginThread(internal::WorkerPoolTaskClient* client)
      OVERRIDE {}
  virtual void RunOnOriginThread() OVERRIDE {}
  virtual void CompleteOnOriginThread(internal::WorkerPoolTaskClient* client)
      OVERRIDE {}
  virtual void RunReplyOnOriginThread() OVERRIDE { Reset(); }

  void Reset() {
    did_run_ = false;
    did_complete_ = false;
  }

 protected:
  virtual ~PerfWorkerPoolTaskImpl() {}

 private:
  DISALLOW_COPY_AND_ASSIGN(PerfWorkerPoolTaskImpl);
};

class PerfRasterWorkerPoolTaskImpl : public internal::RasterWorkerPoolTask {
 public:
  PerfRasterWorkerPoolTaskImpl(scoped_ptr<ScopedResource> resource,
                               internal::WorkerPoolTask::Vector* dependencies)
      : internal::RasterWorkerPoolTask(resource.get(), dependencies),
        resource_(resource.Pass()) {}

  // Overridden from internal::Task:
  virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE {}

  // Overridden from internal::WorkerPoolTask:
  virtual void ScheduleOnOriginThread(internal::WorkerPoolTaskClient* client)
      OVERRIDE {
    client->AcquireCanvasForRaster(this);
  }
  virtual void RunOnOriginThread() OVERRIDE {}
  virtual void CompleteOnOriginThread(internal::WorkerPoolTaskClient* client)
      OVERRIDE {
    client->OnRasterCompleted(this, PicturePileImpl::Analysis());
  }
  virtual void RunReplyOnOriginThread() OVERRIDE { Reset(); }

  void Reset() {
    did_run_ = false;
    did_complete_ = false;
  }

 protected:
  virtual ~PerfRasterWorkerPoolTaskImpl() {}

 private:
  scoped_ptr<ScopedResource> resource_;

  DISALLOW_COPY_AND_ASSIGN(PerfRasterWorkerPoolTaskImpl);
};

class PerfTaskGraphRunnerImpl : public internal::TaskGraphRunner {
 public:
  PerfTaskGraphRunnerImpl() : internal::TaskGraphRunner(0, "Perf") {}
};

class PerfPixelBufferRasterWorkerPoolImpl : public PixelBufferRasterWorkerPool {
 public:
  PerfPixelBufferRasterWorkerPoolImpl(
      internal::TaskGraphRunner* task_graph_runner,
      ResourceProvider* resource_provider)
      : PixelBufferRasterWorkerPool(task_graph_runner,
                                    resource_provider,
                                    std::numeric_limits<size_t>::max()) {}
};

class PerfImageRasterWorkerPoolImpl : public ImageRasterWorkerPool {
 public:
  PerfImageRasterWorkerPoolImpl(internal::TaskGraphRunner* task_graph_runner,
                                ResourceProvider* resource_provider)
      : ImageRasterWorkerPool(task_graph_runner,
                              resource_provider,
                              GL_TEXTURE_2D) {}
};

class PerfDirectRasterWorkerPoolImpl : public DirectRasterWorkerPool {
 public:
  PerfDirectRasterWorkerPoolImpl(ResourceProvider* resource_provider,
                                 ContextProvider* context_provider)
      : DirectRasterWorkerPool(resource_provider, context_provider) {}
};

class RasterWorkerPoolPerfTestBase {
 public:
  typedef std::vector<scoped_refptr<internal::RasterWorkerPoolTask> >
      RasterTaskVector;

  RasterWorkerPoolPerfTestBase()
      : context_provider_(TestContextProvider::Create()),
        timer_(kWarmupRuns,
               base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
               kTimeCheckInterval) {
    output_surface_ = FakeOutputSurface::Create3d(context_provider_).Pass();
    CHECK(output_surface_->BindToClient(&output_surface_client_));

    resource_provider_ = ResourceProvider::Create(
                             output_surface_.get(), NULL, 0, false, 1).Pass();
  }
  virtual ~RasterWorkerPoolPerfTestBase() { resource_provider_.reset(); }

  void CreateImageDecodeTasks(
      unsigned num_image_decode_tasks,
      internal::WorkerPoolTask::Vector* image_decode_tasks) {
    for (unsigned i = 0; i < num_image_decode_tasks; ++i)
      image_decode_tasks->push_back(new PerfWorkerPoolTaskImpl);
  }

  void CreateRasterTasks(
      unsigned num_raster_tasks,
      const internal::WorkerPoolTask::Vector& image_decode_tasks,
      RasterTaskVector* raster_tasks) {
    const gfx::Size size(1, 1);

    for (unsigned i = 0; i < num_raster_tasks; ++i) {
      scoped_ptr<ScopedResource> resource(
          ScopedResource::Create(resource_provider_.get()));
      resource->Allocate(size, ResourceProvider::TextureUsageAny, RGBA_8888);

      internal::WorkerPoolTask::Vector dependencies = image_decode_tasks;
      raster_tasks->push_back(
          new PerfRasterWorkerPoolTaskImpl(resource.Pass(), &dependencies));
    }
  }

  void BuildRasterTaskQueue(RasterTaskQueue* queue,
                            const RasterTaskVector& raster_tasks) {
    for (size_t i = 0u; i < raster_tasks.size(); ++i) {
      bool required_for_activation = (i % 2) == 0;
      queue->items.push_back(RasterTaskQueue::Item(raster_tasks[i].get(),
                                                   required_for_activation));
      queue->required_for_activation_count += required_for_activation;
    }
  }

 protected:
  scoped_refptr<TestContextProvider> context_provider_;
  FakeOutputSurfaceClient output_surface_client_;
  scoped_ptr<FakeOutputSurface> output_surface_;
  scoped_ptr<ResourceProvider> resource_provider_;
  LapTimer timer_;
};

class RasterWorkerPoolPerfTest
    : public RasterWorkerPoolPerfTestBase,
      public testing::TestWithParam<RasterWorkerPoolType>,
      public RasterWorkerPoolClient {
 public:
  RasterWorkerPoolPerfTest() : task_graph_runner_(new PerfTaskGraphRunnerImpl) {
    switch (GetParam()) {
      case RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER:
        raster_worker_pool_.reset(new PerfPixelBufferRasterWorkerPoolImpl(
            task_graph_runner_.get(), resource_provider_.get()));
        break;
      case RASTER_WORKER_POOL_TYPE_IMAGE:
        raster_worker_pool_.reset(new PerfImageRasterWorkerPoolImpl(
            task_graph_runner_.get(), resource_provider_.get()));
        break;
      case RASTER_WORKER_POOL_TYPE_DIRECT:
        raster_worker_pool_.reset(new PerfDirectRasterWorkerPoolImpl(
            resource_provider_.get(), context_provider_));
        break;
    }

    DCHECK(raster_worker_pool_);
    raster_worker_pool_->SetClient(this);
  }

  // Overridden from testing::Test:
  virtual void TearDown() OVERRIDE {
    raster_worker_pool_->Shutdown();
    raster_worker_pool_->CheckForCompletedTasks();
  }

  // Overriden from RasterWorkerPoolClient:
  virtual bool ShouldForceTasksRequiredForActivationToComplete()
      const OVERRIDE {
    return false;
  }
  virtual void DidFinishRunningTasks() OVERRIDE {
    raster_worker_pool_->CheckForCompletedTasks();
    base::MessageLoop::current()->Quit();
  }
  virtual void DidFinishRunningTasksRequiredForActivation() OVERRIDE {}

  void RunMessageLoopUntilAllTasksHaveCompleted() {
    while (task_graph_runner_->RunTaskForTesting())
      continue;
    base::MessageLoop::current()->Run();
  }

  void RunScheduleTasksTest(const std::string& test_name,
                            unsigned num_raster_tasks,
                            unsigned num_image_decode_tasks) {
    internal::WorkerPoolTask::Vector image_decode_tasks;
    RasterTaskVector raster_tasks;
    CreateImageDecodeTasks(num_image_decode_tasks, &image_decode_tasks);
    CreateRasterTasks(num_raster_tasks, image_decode_tasks, &raster_tasks);

    // Avoid unnecessary heap allocations by reusing the same queue.
    RasterTaskQueue queue;

    timer_.Reset();
    do {
      queue.Reset();
      BuildRasterTaskQueue(&queue, raster_tasks);
      raster_worker_pool_->ScheduleTasks(&queue);
      raster_worker_pool_->CheckForCompletedTasks();
      timer_.NextLap();
    } while (!timer_.HasTimeLimitExpired());

    RasterTaskQueue empty;
    raster_worker_pool_->ScheduleTasks(&empty);
    RunMessageLoopUntilAllTasksHaveCompleted();

    perf_test::PrintResult("schedule_tasks",
                           TestModifierString(),
                           test_name,
                           timer_.LapsPerSecond(),
                           "runs/s",
                           true);
  }

  void RunScheduleAlternateTasksTest(const std::string& test_name,
                                     unsigned num_raster_tasks,
                                     unsigned num_image_decode_tasks) {
    const size_t kNumVersions = 2;
    internal::WorkerPoolTask::Vector image_decode_tasks[kNumVersions];
    RasterTaskVector raster_tasks[kNumVersions];
    for (size_t i = 0; i < kNumVersions; ++i) {
      CreateImageDecodeTasks(num_image_decode_tasks, &image_decode_tasks[i]);
      CreateRasterTasks(
          num_raster_tasks, image_decode_tasks[i], &raster_tasks[i]);
    }

    // Avoid unnecessary heap allocations by reusing the same queue.
    RasterTaskQueue queue;

    size_t count = 0;
    timer_.Reset();
    do {
      queue.Reset();
      BuildRasterTaskQueue(&queue, raster_tasks[count % kNumVersions]);
      raster_worker_pool_->ScheduleTasks(&queue);
      raster_worker_pool_->CheckForCompletedTasks();
      ++count;
      timer_.NextLap();
    } while (!timer_.HasTimeLimitExpired());

    RasterTaskQueue empty;
    raster_worker_pool_->ScheduleTasks(&empty);
    RunMessageLoopUntilAllTasksHaveCompleted();

    perf_test::PrintResult("schedule_alternate_tasks",
                           TestModifierString(),
                           test_name,
                           timer_.LapsPerSecond(),
                           "runs/s",
                           true);
  }

  void RunScheduleAndExecuteTasksTest(const std::string& test_name,
                                      unsigned num_raster_tasks,
                                      unsigned num_image_decode_tasks) {
    internal::WorkerPoolTask::Vector image_decode_tasks;
    RasterTaskVector raster_tasks;
    CreateImageDecodeTasks(num_image_decode_tasks, &image_decode_tasks);
    CreateRasterTasks(num_raster_tasks, image_decode_tasks, &raster_tasks);

    // Avoid unnecessary heap allocations by reusing the same queue.
    RasterTaskQueue queue;

    timer_.Reset();
    do {
      queue.Reset();
      BuildRasterTaskQueue(&queue, raster_tasks);
      raster_worker_pool_->ScheduleTasks(&queue);
      RunMessageLoopUntilAllTasksHaveCompleted();
      timer_.NextLap();
    } while (!timer_.HasTimeLimitExpired());

    RasterTaskQueue empty;
    raster_worker_pool_->ScheduleTasks(&empty);
    RunMessageLoopUntilAllTasksHaveCompleted();

    perf_test::PrintResult("schedule_and_execute_tasks",
                           TestModifierString(),
                           test_name,
                           timer_.LapsPerSecond(),
                           "runs/s",
                           true);
  }

 private:
  std::string TestModifierString() const {
    switch (GetParam()) {
      case RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER:
        return std::string("_pixel_raster_worker_pool");
      case RASTER_WORKER_POOL_TYPE_IMAGE:
        return std::string("_image_raster_worker_pool");
      case RASTER_WORKER_POOL_TYPE_DIRECT:
        return std::string("_direct_raster_worker_pool");
    }
    NOTREACHED();
    return std::string();
  }

  scoped_ptr<internal::TaskGraphRunner> task_graph_runner_;
  scoped_ptr<RasterWorkerPool> raster_worker_pool_;
};

TEST_P(RasterWorkerPoolPerfTest, ScheduleTasks) {
  RunScheduleTasksTest("1_0", 1, 0);
  RunScheduleTasksTest("32_0", 32, 0);
  RunScheduleTasksTest("1_1", 1, 1);
  RunScheduleTasksTest("32_1", 32, 1);
  RunScheduleTasksTest("1_4", 1, 4);
  RunScheduleTasksTest("32_4", 32, 4);
}

TEST_P(RasterWorkerPoolPerfTest, ScheduleAlternateTasks) {
  RunScheduleAlternateTasksTest("1_0", 1, 0);
  RunScheduleAlternateTasksTest("32_0", 32, 0);
  RunScheduleAlternateTasksTest("1_1", 1, 1);
  RunScheduleAlternateTasksTest("32_1", 32, 1);
  RunScheduleAlternateTasksTest("1_4", 1, 4);
  RunScheduleAlternateTasksTest("32_4", 32, 4);
}

TEST_P(RasterWorkerPoolPerfTest, ScheduleAndExecuteTasks) {
  RunScheduleAndExecuteTasksTest("1_0", 1, 0);
  RunScheduleAndExecuteTasksTest("32_0", 32, 0);
  RunScheduleAndExecuteTasksTest("1_1", 1, 1);
  RunScheduleAndExecuteTasksTest("32_1", 32, 1);
  RunScheduleAndExecuteTasksTest("1_4", 1, 4);
  RunScheduleAndExecuteTasksTest("32_4", 32, 4);
}

INSTANTIATE_TEST_CASE_P(RasterWorkerPoolPerfTests,
                        RasterWorkerPoolPerfTest,
                        ::testing::Values(RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER,
                                          RASTER_WORKER_POOL_TYPE_IMAGE,
                                          RASTER_WORKER_POOL_TYPE_DIRECT));

class RasterWorkerPoolCommonPerfTest : public RasterWorkerPoolPerfTestBase,
                                       public testing::Test {
 public:
  void RunBuildRasterTaskQueueTest(const std::string& test_name,
                                   unsigned num_raster_tasks,
                                   unsigned num_image_decode_tasks) {
    internal::WorkerPoolTask::Vector image_decode_tasks;
    RasterTaskVector raster_tasks;
    CreateImageDecodeTasks(num_image_decode_tasks, &image_decode_tasks);
    CreateRasterTasks(num_raster_tasks, image_decode_tasks, &raster_tasks);

    // Avoid unnecessary heap allocations by reusing the same queue.
    RasterTaskQueue queue;

    timer_.Reset();
    do {
      queue.Reset();
      BuildRasterTaskQueue(&queue, raster_tasks);
      timer_.NextLap();
    } while (!timer_.HasTimeLimitExpired());

    perf_test::PrintResult("build_raster_task_queue",
                           "",
                           test_name,
                           timer_.LapsPerSecond(),
                           "runs/s",
                           true);
  }
};

TEST_F(RasterWorkerPoolCommonPerfTest, BuildRasterTaskQueue) {
  RunBuildRasterTaskQueueTest("1_0", 1, 0);
  RunBuildRasterTaskQueueTest("32_0", 32, 0);
  RunBuildRasterTaskQueueTest("1_1", 1, 1);
  RunBuildRasterTaskQueueTest("32_1", 32, 1);
  RunBuildRasterTaskQueueTest("1_4", 1, 4);
  RunBuildRasterTaskQueueTest("32_4", 32, 4);
}

}  // namespace
}  // namespace cc
<|endoftext|>


<commit_before>/***********************************************************************
    filename:   CEGUIOgreTextureTarget.cpp
    created:    Tue Feb 17 2009
    author:     Paul D Turner
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2009 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/
#include "CEGUIOgreTextureTarget.h"
#include "CEGUIOgreTexture.h"

#include <OgreTextureManager.h>
#include <OgreHardwarePixelBuffer.h>
#include <OgreRenderTexture.h>
#include <OgreRenderSystem.h>

// Start of CEGUI namespace section
namespace CEGUI
{
//----------------------------------------------------------------------------//
const float OgreTextureTarget::DEFAULT_SIZE = 128.0f;

//----------------------------------------------------------------------------//
OgreTextureTarget::OgreTextureTarget(OgreRenderer& owner,
                                     Ogre::RenderSystem& rs) :
    OgreRenderTarget(owner, rs),
    d_CEGUITexture(0)
{
    d_CEGUITexture = static_cast<OgreTexture*>(&d_owner.createTexture());

    // setup area and cause the initial texture to be generated.
    declareRenderSize(Size(DEFAULT_SIZE, DEFAULT_SIZE));
}

//----------------------------------------------------------------------------//
OgreTextureTarget::~OgreTextureTarget()
{
    d_owner.destroyTexture(*d_CEGUITexture);
}

//----------------------------------------------------------------------------//
bool OgreTextureTarget::isImageryCache() const
{
    return true;
}

//----------------------------------------------------------------------------//
void OgreTextureTarget::clear()
{
    if (!d_viewportValid)
        updateViewport();

    Ogre::Viewport* old_vp = d_renderSystem._getViewport();
    d_renderSystem._setViewport(d_viewport);
    d_renderSystem.clearFrameBuffer(Ogre::FBT_COLOUR,
                                    Ogre::ColourValue(0, 0, 0, 0));
    if (old_vp)
        d_renderSystem._setViewport(old_vp);
}

//----------------------------------------------------------------------------//
Texture& OgreTextureTarget::getTexture() const
{
    return *d_CEGUITexture;
}

//----------------------------------------------------------------------------//
void OgreTextureTarget::declareRenderSize(const Size& sz)
{
    // exit if current size is enough
    if ((d_area.getWidth() >= sz.d_width) && (d_area.getHeight() >=sz.d_height))
        return;

    Ogre::TexturePtr rttTex = Ogre::TextureManager::getSingleton().createManual(
        OgreTexture::getUniqueName(),
        Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
        Ogre::TEX_TYPE_2D, sz.d_width, sz.d_height, 1, 0, Ogre::PF_A8R8G8B8,
        Ogre::TU_RENDERTARGET);

    d_renderTarget = rttTex->getBuffer()->getRenderTarget();

    Rect init_area(
        Vector2(0, 0),
        Size(d_renderTarget->getWidth(), d_renderTarget->getHeight())
    );

    setArea(init_area);

    // delete viewport and reset ptr so a new one is generated.  This is
    // required because we have changed d_renderTarget so need a new VP also.
    delete d_viewport;
    d_viewport = 0;

    // because Texture takes ownership, the act of setting the new ogre texture
    // also ensures any previous ogre texture is released.
    d_CEGUITexture->setOgreTexture(rttTex, true);

    clear();
}

//----------------------------------------------------------------------------//
bool OgreTextureTarget::isRenderingInverted() const
{
    return false;
}

//----------------------------------------------------------------------------//

} // End of  CEGUI namespace section
<commit_msg>FIX: Crash in Ogre based texture target, where we may inadvertently restore a deleted viewport to the render system.<commit_after>/***********************************************************************
    filename:   CEGUIOgreTextureTarget.cpp
    created:    Tue Feb 17 2009
    author:     Paul D Turner
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2009 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/
#include "CEGUIOgreTextureTarget.h"
#include "CEGUIOgreTexture.h"

#include <OgreTextureManager.h>
#include <OgreHardwarePixelBuffer.h>
#include <OgreRenderTexture.h>
#include <OgreRenderSystem.h>

// Start of CEGUI namespace section
namespace CEGUI
{
//----------------------------------------------------------------------------//
const float OgreTextureTarget::DEFAULT_SIZE = 128.0f;

//----------------------------------------------------------------------------//
OgreTextureTarget::OgreTextureTarget(OgreRenderer& owner,
                                     Ogre::RenderSystem& rs) :
    OgreRenderTarget(owner, rs),
    d_CEGUITexture(0)
{
    d_CEGUITexture = static_cast<OgreTexture*>(&d_owner.createTexture());

    // setup area and cause the initial texture to be generated.
    declareRenderSize(Size(DEFAULT_SIZE, DEFAULT_SIZE));
}

//----------------------------------------------------------------------------//
OgreTextureTarget::~OgreTextureTarget()
{
    d_owner.destroyTexture(*d_CEGUITexture);
}

//----------------------------------------------------------------------------//
bool OgreTextureTarget::isImageryCache() const
{
    return true;
}

//----------------------------------------------------------------------------//
void OgreTextureTarget::clear()
{
    if (!d_viewportValid)
        updateViewport();

    d_renderSystem._setViewport(d_viewport);
    d_renderSystem.clearFrameBuffer(Ogre::FBT_COLOUR,
                                    Ogre::ColourValue(0, 0, 0, 0));
}

//----------------------------------------------------------------------------//
Texture& OgreTextureTarget::getTexture() const
{
    return *d_CEGUITexture;
}

//----------------------------------------------------------------------------//
void OgreTextureTarget::declareRenderSize(const Size& sz)
{
    // exit if current size is enough
    if ((d_area.getWidth() >= sz.d_width) && (d_area.getHeight() >=sz.d_height))
        return;

    Ogre::TexturePtr rttTex = Ogre::TextureManager::getSingleton().createManual(
        OgreTexture::getUniqueName(),
        Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
        Ogre::TEX_TYPE_2D, sz.d_width, sz.d_height, 1, 0, Ogre::PF_A8R8G8B8,
        Ogre::TU_RENDERTARGET);

    d_renderTarget = rttTex->getBuffer()->getRenderTarget();

    Rect init_area(
        Vector2(0, 0),
        Size(d_renderTarget->getWidth(), d_renderTarget->getHeight())
    );

    setArea(init_area);

    // delete viewport and reset ptr so a new one is generated.  This is
    // required because we have changed d_renderTarget so need a new VP also.
    delete d_viewport;
    d_viewport = 0;

    // because Texture takes ownership, the act of setting the new ogre texture
    // also ensures any previous ogre texture is released.
    d_CEGUITexture->setOgreTexture(rttTex, true);

    clear();
}

//----------------------------------------------------------------------------//
bool OgreTextureTarget::isRenderingInverted() const
{
    return false;
}

//----------------------------------------------------------------------------//

} // End of  CEGUI namespace section
<|endoftext|>


<commit_before>#include "_DroneBox.h"

namespace kai
{

_DroneBox::_DroneBox()
{
    m_pMB = NULL;
    m_iSlave = 1;
    m_lastCMD = dbx_unknown;
}

_DroneBox::~_DroneBox()
{
}

bool _DroneBox::init ( void* pKiss )
{
    IF_F ( !this->_GCSbase::init ( pKiss ) );
    Kiss* pK = ( Kiss* ) pKiss;

    pK->v ( "iSlave", &m_iSlave );

    string n;

    n = "";
    pK->v ( "_Modbus", &n );
    m_pMB = ( _Modbus* ) ( pK->getInst ( n ) );
    IF_Fl ( !m_pMB, n + ": not found" );

    return true;
}

bool _DroneBox::start ( void )
{
    NULL_F(m_pT);
	return m_pT->start(getUpdate, this);
}

int _DroneBox::check ( void )
{
    NULL__ ( m_pMB, -1 );
    IF__ ( !m_pMB->bOpen(), -1 );

    return this->_GCSbase::check();
}

void _DroneBox::update ( void )
{
    while(m_pT->bRun())
    {
        m_pT->autoFPSfrom();
        this->_GCSbase::update();

        updateGCS();

        m_pT->autoFPSto();
    }
}

void _DroneBox::updateGCS ( void )
{
    IF_ ( check() <0 );

    this->_GCSbase::updateGCS();

    if(m_state.bSTANDBY())
    {
        boxLandingComplete();
    }
    else if(m_state.bTAKEOFF_REQUEST())
    {
        boxTakeoffPrepare();

        if( bBoxTakeoffReady())
        {
            m_pSC->transit(m_state.TAKEOFF_READY);
            return;
        }
    }
    else if(m_state.bTAKEOFF_READY())
    {

    }
    else if(m_state.bAIRBORNE())
    {
        boxTakeoffComplete();
    }
    else if(m_state.bLANDING_REQUEST())
    {
        boxLandingPrepare();

        if( bBoxLandingReady())
        {
            m_pSC->transit(m_state.LANDING_READY);
            return;
        }
    }
    else if(m_state.bLANDING_READY())
    {

    }
}

void _DroneBox::boxLandingPrepare ( void )
{
    IF_ ( check() <0 );
    IF_ ( m_lastCMD == dbx_landingPrepare );
    IF_ ( m_lastCMD == dbx_bTakeoffReady );
    IF_ ( m_lastCMD == dbx_bLandingReady );

    //01 06 00 00 00 01 48 0A
    int r = m_pMB->writeRegister ( m_iSlave, 0, 1 );
    LOG_I("boxLandingPrepare: " + i2str(r));
    IF_(r <= 0);

    m_lastCMD = dbx_landingPrepare;
}

bool _DroneBox::bBoxLandingReady ( void )
{
    IF_F ( check() <0 );
    IF_F ( m_lastCMD != dbx_landingPrepare && m_lastCMD != dbx_bLandingReady );

    //01 03 00 01 00 01 D5 CA
    uint16_t b = 0;
    int r = m_pMB->readRegisters ( m_iSlave, 1, 1, &b );
    LOG_I("bBoxLandingReady: " + i2str(r) + ", " + i2str(b));
    IF_F ( r != 1 );

    m_lastCMD = dbx_bLandingReady;
    return ( b==1 ) ?true:false;
}

void _DroneBox::boxLandingComplete ( void )
{
    IF_ ( check() <0 );
    IF_ ( m_lastCMD == dbx_landingComplete );
    if ( m_lastCMD != dbx_bLandingReady )
    {
        boxRecover();
        return;
    }

    //01 06 00 02 00 01 E9 CA
    int r = m_pMB->writeRegister ( m_iSlave, 2, 1 );
    LOG_I("boxLandingComplete: " + i2str(r));
    IF_(r <= 0);

    m_lastCMD = dbx_landingComplete;
}

void _DroneBox::boxTakeoffPrepare ( void )
{
    IF_ ( check() <0 );
    IF_ ( m_lastCMD == dbx_takeoffPrepare );
    IF_ ( m_lastCMD == dbx_bTakeoffReady );

    //01 06 00 03 00 01 B8 0A
    int r = m_pMB->writeRegister ( m_iSlave, 3, 1 );
    LOG_I("boxTakeoffPrepare: " + i2str(r));
    IF_(r <= 0);

    m_lastCMD = dbx_takeoffPrepare;
}

bool _DroneBox::bBoxTakeoffReady ( void )
{
    IF_F ( check() <0 );
    IF_F ( m_lastCMD != dbx_takeoffPrepare && m_lastCMD != dbx_bTakeoffReady );

    //01 03 00 04 00 01 C5 CB
    uint16_t b = 0;
    int r = m_pMB->readRegisters ( m_iSlave, 4, 1, &b );
    LOG_I("bBoxTakeoffReady: " + i2str(r) + ", " + i2str(b));
    IF_F ( r != 1 );

    m_lastCMD = dbx_bTakeoffReady;
    return ( b==1 ) ?true:false;
}

void _DroneBox::boxTakeoffComplete ( void )
{
    IF_ ( check() <0 );
    IF_ ( m_lastCMD != dbx_bTakeoffReady );
    IF_ ( m_lastCMD == dbx_takeoffComplete );

    //01 06 00 05 00 01 58 0B
    int r = m_pMB->writeRegister ( m_iSlave, 5, 1 );
    LOG_I("boxTakeoffComplete: " + i2str(r));
    IF_( r <= 0 );

    m_lastCMD = dbx_takeoffComplete;
}

void _DroneBox::boxRecover ( void )
{
    IF_ ( check() <0 );
    IF_ ( m_lastCMD == dbx_boxRecover );

    //01 06 00 06 00 01 A8 0B
    int r = m_pMB->writeRegister ( m_iSlave, 6, 1 );
    LOG_I("boxRecover: " + i2str(r));
    IF_( r <= 0 );

    m_lastCMD = dbx_boxRecover;
}

void _DroneBox::draw ( void )
{
    this->_GCSbase::draw();
}

}
<commit_msg>Updated DroneBox<commit_after>#include "_DroneBox.h"

namespace kai
{

_DroneBox::_DroneBox()
{
    m_pMB = NULL;
    m_iSlave = 1;
    m_lastCMD = dbx_unknown;
}

_DroneBox::~_DroneBox()
{
}

bool _DroneBox::init ( void* pKiss )
{
    IF_F ( !this->_GCSbase::init ( pKiss ) );
    Kiss* pK = ( Kiss* ) pKiss;

    pK->v ( "iSlave", &m_iSlave );

    string n;

    n = "";
    pK->v ( "_Modbus", &n );
    m_pMB = ( _Modbus* ) ( pK->getInst ( n ) );
    IF_Fl ( !m_pMB, n + ": not found" );

    return true;
}

bool _DroneBox::start ( void )
{
    NULL_F(m_pT);
	return m_pT->start(getUpdate, this);
}

int _DroneBox::check ( void )
{
    NULL__ ( m_pMB, -1 );
    IF__ ( !m_pMB->bOpen(), -1 );

    return this->_GCSbase::check();
}

void _DroneBox::update ( void )
{
    while(m_pT->bRun())
    {
        m_pT->autoFPSfrom();
        this->_GCSbase::update();

        updateGCS();

        m_pT->autoFPSto();
    }
}

void _DroneBox::updateGCS ( void )
{
    IF_ ( check() <0 );

    this->_GCSbase::updateGCS();

    if(m_state.bSTANDBY())
    {
        boxLandingComplete();
        return;
    }

    if(m_state.bTAKEOFF_REQUEST())
    {
        boxTakeoffPrepare();

        if( bBoxTakeoffReady())
            m_pSC->transit(m_state.TAKEOFF_READY);

        return;
    }

    if(m_state.bTAKEOFF_READY())
    {
        return;
    }

    if(m_state.bAIRBORNE())
    {
        boxTakeoffComplete();
        return;
    }

    if(m_state.bLANDING_REQUEST())
    {
        boxLandingPrepare();

        if( bBoxLandingReady())
            m_pSC->transit(m_state.LANDING_READY);

        return;
    }

    if(m_state.bLANDING_READY())
    {
        return;
    }
}

void _DroneBox::boxLandingPrepare ( void )
{
    IF_ ( check() <0 );
    IF_(m_lastCMD == dbx_landingPrepare);

    //01 06 00 00 00 01 48 0A
    int r = m_pMB->writeRegister ( m_iSlave, 0, 1 );
    LOG_I("boxLandingPrepare: " + i2str(r));
    IF_(r <= 0);

    m_lastCMD = dbx_landingPrepare;
}

bool _DroneBox::bBoxLandingReady ( void )
{
    IF_F ( check() <0 );

    //01 03 00 01 00 01 D5 CA
    uint16_t b = 0;
    int r = m_pMB->readRegisters ( m_iSlave, 1, 1, &b );
    LOG_I("bBoxLandingReady: " + i2str(r) + ", " + i2str(b));
    IF_F ( r != 1 );

    return ( b==1 ) ?true:false;
}

void _DroneBox::boxLandingComplete ( void )
{
    IF_ ( check() <0 );
    IF_(m_lastCMD == dbx_landingComplete);

    //01 06 00 02 00 01 E9 CA
    int r = m_pMB->writeRegister ( m_iSlave, 2, 1 );
    LOG_I("boxLandingComplete: " + i2str(r));
    IF_(r <= 0);

    m_lastCMD = dbx_landingComplete;
}

void _DroneBox::boxTakeoffPrepare ( void )
{
    IF_ ( check() <0 );
    IF_(m_lastCMD == dbx_takeoffPrepare);

    //01 06 00 03 00 01 B8 0A
    int r = m_pMB->writeRegister ( m_iSlave, 3, 1 );
    LOG_I("boxTakeoffPrepare: " + i2str(r));
    IF_(r <= 0);

    m_lastCMD = dbx_takeoffPrepare;
}

bool _DroneBox::bBoxTakeoffReady ( void )
{
    IF_F ( check() <0 );

    //01 03 00 04 00 01 C5 CB
    uint16_t b = 0;
    int r = m_pMB->readRegisters ( m_iSlave, 4, 1, &b );
    LOG_I("bBoxTakeoffReady: " + i2str(r) + ", " + i2str(b));
    IF_F ( r != 1 );

    return ( b==1 ) ?true:false;
}

void _DroneBox::boxTakeoffComplete ( void )
{
    IF_ ( check() <0 );
    IF_(m_lastCMD == dbx_takeoffComplete);

    //01 06 00 05 00 01 58 0B
    int r = m_pMB->writeRegister ( m_iSlave, 5, 1 );
    LOG_I("boxTakeoffComplete: " + i2str(r));
    IF_( r <= 0 );

    m_lastCMD = dbx_takeoffComplete;
}

void _DroneBox::boxRecover ( void )
{
    IF_ ( check() <0 );

    //01 06 00 06 00 01 A8 0B
    int r = m_pMB->writeRegister ( m_iSlave, 6, 1 );
    LOG_I("boxRecover: " + i2str(r));
    IF_( r <= 0 );

    m_lastCMD = dbx_boxRecover;
}

void _DroneBox::draw ( void )
{
    this->_GCSbase::draw();
}

}
<|endoftext|>


<commit_before>/*
 * libMaia - maiaObject.cpp
 * Copyright (c) 2003 Frerich Raabe <raabe@kde.org> and
 *                    Ian Reinhart Geiser <geiseri@kde.org>
 * Copyright (c) 2007 Sebastian Wiedenroth <wiedi@frubar.net>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "maiaObject.h"
#include <cfloat>

MaiaObject::MaiaObject(QObject* parent) : QObject(parent){
	QDomImplementation::setInvalidDataPolicy(QDomImplementation::DropInvalidChars);
}

QDomElement MaiaObject::toXml(QVariant arg) {

	//dummy document
	QDomDocument doc;
	//value element, we need this in each case
	QDomElement tagValue = doc.createElement("value");

	switch(arg.type()) {
	case QVariant::String: {

		QDomElement tagString = doc.createElement("string");
		QDomText textString = doc.createTextNode(arg.toString());

		tagValue.appendChild(tagString);
		tagString.appendChild(textString);

		return tagValue;

	} case QVariant::Int: {

		QDomElement tagInt = doc.createElement("int");
		QDomText textInt = doc.createTextNode(QString::number(arg.toInt()));

		tagValue.appendChild(tagInt);
		tagInt.appendChild(textInt);

		return tagValue;

	} case QVariant::Double: {

		QDomElement tagDouble = doc.createElement("double");
		QDomText textDouble = doc.createTextNode(QString::number(arg.toDouble(), 'g', DBL_DIG));

		tagValue.appendChild(tagDouble);
		tagDouble.appendChild(textDouble);

		return tagValue;

	} case QVariant::Bool: {

		QString textValue = arg.toBool() ? "1" : "0";

		QDomElement tag = doc.createElement("boolean");
		QDomText text = doc.createTextNode(textValue);

		tagValue.appendChild(tag);
		tag.appendChild(text);

		return tagValue;

	} case QVariant::ByteArray: {

		QString textValue = arg.toByteArray().toBase64();

		QDomElement tag = doc.createElement("base64");
		QDomText text = doc.createTextNode(textValue);

		tagValue.appendChild(tag);
		tag.appendChild(text);

		return tagValue;

	} case QVariant::DateTime: {

		QString textValue = arg.toDateTime().toString("yyyyMMddThh:mm:ss");

		QDomElement tag = doc.createElement("datetime.iso8601");
		QDomText text = doc.createTextNode(textValue);

		tagValue.appendChild(tag);
		tag.appendChild(text);

		return tagValue;

	} case QVariant::Invalid: {
		// Non-standard extension: https://web.archive.org/web/20050911054235/http://ontosys.com/xml-rpc/extensions.php

		QDomElement tag = doc.createElement("nil");
		tagValue.appendChild(tag);

		return tagValue;

	} case QVariant::List: {

		QDomElement tagArray = doc.createElement("array");
		QDomElement tagData = doc.createElement("data");
		tagArray.appendChild(tagData);
		tagValue.appendChild(tagArray);

		const QList<QVariant> args = arg.toList();
		for(int i = 0; i < args.size(); ++i) {
			tagData.appendChild(toXml(args.at(i)));
		}

		return tagValue;

	} case QVariant::Map: {

		QDomElement tagStruct = doc.createElement("struct");
		QDomElement member;
		QDomElement name;

		tagValue.appendChild(tagStruct);

		QMap<QString, QVariant> map = arg.toMap();
		QMapIterator<QString, QVariant> i(map);
		while(i.hasNext()) {
			i.next();

			member = doc.createElement("member");
			name = doc.createElement("name");

			// (key) -> name -> member -> struct
			tagStruct.appendChild(member);
			member.appendChild(name);
			name.appendChild(doc.createTextNode(i.key()));

			// add variables by recursion
			member.appendChild(toXml(i.value()));
		}

		return tagValue;

	} default:
		qDebug() << "Failed to marshal unknown variant type: " << arg.type() << endl;
	}
	return QDomElement(); //QString::null;
}

QVariant MaiaObject::fromXml(const QDomElement &elem) {
	if(elem.tagName().toLower() != "value") {
		return QVariant();
	}

	// If no type is indicated, the type is string.
	if(!elem.firstChild().isElement()) {
		return QVariant(elem.text());
	}

	const QDomElement typeElement = elem.firstChild().toElement();
	const QString typeName = typeElement.tagName().toLower();

	if(typeName == "string")
		return QVariant(typeElement.text());
	else if(typeName == "i4" || typeName == "int")
		return QVariant(typeElement.text().toInt());
	else if(typeName == "double")
		return QVariant(typeElement.text().toDouble());
	else if (typeName == "boolean") {
		if(typeElement.text().toLower() == "true" || typeElement.text() == "1")
			return QVariant(true);
		else
			return QVariant(false);
	} else if(typeName == "base64")
		return QVariant(QByteArray::fromBase64( typeElement.text().toLatin1()));
	else if(typeName == "datetime" || typeName == "datetime.iso8601")
		return QVariant(QDateTime::fromString(typeElement.text(), "yyyyMMddThh:mm:ss"));
	// Non-standard extension: https://web.archive.org/web/20050911054235/http://ontosys.com/xml-rpc/extensions.php
	else if(typeName == "nil")
		return QVariant();
	else if ( typeName == "array" ) {
		QList<QVariant> values;
		QDomNode valueNode = typeElement.firstChild().firstChild();
		while(!valueNode.isNull()) {
			values << fromXml(valueNode.toElement());
			valueNode = valueNode.nextSibling();
		}
		return QVariant(values);
	}
	else if ( typeName == "struct" ) {
		QMap<QString, QVariant> map;
		QDomNode memberNode = typeElement.firstChild();
		while(!memberNode.isNull())	{
			const QString key = memberNode.toElement().elementsByTagName("name").item(0).toElement().text();
			const QVariant data = fromXml(memberNode.toElement().elementsByTagName("value").item(0).toElement());
			map[key] = data;
			memberNode = memberNode.nextSibling();
		}
		return QVariant(map);
	} else {
		qDebug() << "Cannot demarshal unknown type " << typeElement.tagName().toLower();
	}
	return QVariant();
}

QString MaiaObject::prepareCall(QString method, QList<QVariant> args) {

	QDomDocument doc;

	QDomProcessingInstruction header = doc.createProcessingInstruction( "xml", QString("version=\"1.0\" encoding=\"UTF-8\"" ));
	doc.appendChild(header);

	QDomElement methodCall = doc.createElement("methodCall");
	QDomElement methodName = doc.createElement("methodName");
	QDomElement params = doc.createElement("params");
	QDomElement param;

	doc.appendChild(methodCall);
	methodCall.appendChild(methodName);
	methodName.appendChild(doc.createTextNode(method));

	methodCall.appendChild(params);

	for(int i = 0; i < args.size(); ++i) {
		param = doc.createElement("param");
		param.appendChild(toXml(args.at(i)));
		params.appendChild(param);
	}

	return doc.toString();
}

QString MaiaObject::prepareResponse(QVariant arg) {

	QDomDocument doc;

	QDomProcessingInstruction header = doc.createProcessingInstruction( "xml", QString("version=\"1.0\" encoding=\"UTF-8\"" ));
	doc.appendChild(header);

	QDomElement methodResponse = doc.createElement("methodResponse");
	QDomElement params = doc.createElement("params");
	QDomElement param;

	doc.appendChild(methodResponse);

	methodResponse.appendChild(params);

	if(!arg.isNull()) {
		param = doc.createElement("param");
		param.appendChild(toXml(arg));
		params.appendChild(param);
	}
	return doc.toString();
}

void MaiaObject::parseResponse(QString response, QNetworkReply* reply) {
	QDomDocument doc;
	QVariant arg;
	QString errorMsg;
	int errorLine;
	int errorColumn;
	if(!doc.setContent(response, &errorMsg, &errorLine, &errorColumn)) {
		emit fault(-32700, QString("parse error: response not well formed at line %1: %2").arg(errorLine).arg(errorMsg), reply);
		delete this;
		return;
	}
	if(doc.documentElement().firstChild().toElement().tagName().toLower() == "params") {
		QDomNode paramNode = doc.documentElement().firstChild().firstChild();
		if(!paramNode.isNull()) {
			arg = fromXml( paramNode.firstChild().toElement() );
		}
		emit aresponse(arg, reply);
	} else if(doc.documentElement().firstChild().toElement().tagName().toLower() == "fault") {
		const QVariant errorVariant = fromXml(doc.documentElement().firstChild().firstChild().toElement());
		emit fault(errorVariant.toMap() [ "faultCode" ].toInt(),
		           errorVariant.toMap() [ "faultString" ].toString(),
		           reply);
	} else {
		emit fault(-32600,
		           tr("parse error: invalid xml-rpc. not conforming to spec."),
		           reply);
	}
	delete this;
	return;
}

<commit_msg>maiaObject: adapt to deprecation in Qt 5.15<commit_after>/*
 * libMaia - maiaObject.cpp
 * Copyright (c) 2003 Frerich Raabe <raabe@kde.org> and
 *                    Ian Reinhart Geiser <geiseri@kde.org>
 * Copyright (c) 2007 Sebastian Wiedenroth <wiedi@frubar.net>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "maiaObject.h"
#include <cfloat>

MaiaObject::MaiaObject(QObject* parent) : QObject(parent){
	QDomImplementation::setInvalidDataPolicy(QDomImplementation::DropInvalidChars);
}

QDomElement MaiaObject::toXml(QVariant arg) {

	//dummy document
	QDomDocument doc;
	//value element, we need this in each case
	QDomElement tagValue = doc.createElement("value");

	switch(arg.type()) {
	case QVariant::String: {

		QDomElement tagString = doc.createElement("string");
		QDomText textString = doc.createTextNode(arg.toString());

		tagValue.appendChild(tagString);
		tagString.appendChild(textString);

		return tagValue;

	} case QVariant::Int: {

		QDomElement tagInt = doc.createElement("int");
		QDomText textInt = doc.createTextNode(QString::number(arg.toInt()));

		tagValue.appendChild(tagInt);
		tagInt.appendChild(textInt);

		return tagValue;

	} case QVariant::Double: {

		QDomElement tagDouble = doc.createElement("double");
		QDomText textDouble = doc.createTextNode(QString::number(arg.toDouble(), 'g', DBL_DIG));

		tagValue.appendChild(tagDouble);
		tagDouble.appendChild(textDouble);

		return tagValue;

	} case QVariant::Bool: {

		QString textValue = arg.toBool() ? "1" : "0";

		QDomElement tag = doc.createElement("boolean");
		QDomText text = doc.createTextNode(textValue);

		tagValue.appendChild(tag);
		tag.appendChild(text);

		return tagValue;

	} case QVariant::ByteArray: {

		QString textValue = arg.toByteArray().toBase64();

		QDomElement tag = doc.createElement("base64");
		QDomText text = doc.createTextNode(textValue);

		tagValue.appendChild(tag);
		tag.appendChild(text);

		return tagValue;

	} case QVariant::DateTime: {

		QString textValue = arg.toDateTime().toString("yyyyMMddThh:mm:ss");

		QDomElement tag = doc.createElement("datetime.iso8601");
		QDomText text = doc.createTextNode(textValue);

		tagValue.appendChild(tag);
		tag.appendChild(text);

		return tagValue;

	} case QVariant::Invalid: {
		// Non-standard extension: https://web.archive.org/web/20050911054235/http://ontosys.com/xml-rpc/extensions.php

		QDomElement tag = doc.createElement("nil");
		tagValue.appendChild(tag);

		return tagValue;

	} case QVariant::List: {

		QDomElement tagArray = doc.createElement("array");
		QDomElement tagData = doc.createElement("data");
		tagArray.appendChild(tagData);
		tagValue.appendChild(tagArray);

		const QList<QVariant> args = arg.toList();
		for(int i = 0; i < args.size(); ++i) {
			tagData.appendChild(toXml(args.at(i)));
		}

		return tagValue;

	} case QVariant::Map: {

		QDomElement tagStruct = doc.createElement("struct");
		QDomElement member;
		QDomElement name;

		tagValue.appendChild(tagStruct);

		QMap<QString, QVariant> map = arg.toMap();
		QMapIterator<QString, QVariant> i(map);
		while(i.hasNext()) {
			i.next();

			member = doc.createElement("member");
			name = doc.createElement("name");

			// (key) -> name -> member -> struct
			tagStruct.appendChild(member);
			member.appendChild(name);
			name.appendChild(doc.createTextNode(i.key()));

			// add variables by recursion
			member.appendChild(toXml(i.value()));
		}

		return tagValue;

	} default:
		qDebug() << "Failed to marshal unknown variant type: " << arg.type()
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
		         << Qt::endl;
#else
		         << endl;
#endif
	}
	return QDomElement(); //QString::null;
}

QVariant MaiaObject::fromXml(const QDomElement &elem) {
	if(elem.tagName().toLower() != "value") {
		return QVariant();
	}

	// If no type is indicated, the type is string.
	if(!elem.firstChild().isElement()) {
		return QVariant(elem.text());
	}

	const QDomElement typeElement = elem.firstChild().toElement();
	const QString typeName = typeElement.tagName().toLower();

	if(typeName == "string")
		return QVariant(typeElement.text());
	else if(typeName == "i4" || typeName == "int")
		return QVariant(typeElement.text().toInt());
	else if(typeName == "double")
		return QVariant(typeElement.text().toDouble());
	else if (typeName == "boolean") {
		if(typeElement.text().toLower() == "true" || typeElement.text() == "1")
			return QVariant(true);
		else
			return QVariant(false);
	} else if(typeName == "base64")
		return QVariant(QByteArray::fromBase64( typeElement.text().toLatin1()));
	else if(typeName == "datetime" || typeName == "datetime.iso8601")
		return QVariant(QDateTime::fromString(typeElement.text(), "yyyyMMddThh:mm:ss"));
	// Non-standard extension: https://web.archive.org/web/20050911054235/http://ontosys.com/xml-rpc/extensions.php
	else if(typeName == "nil")
		return QVariant();
	else if ( typeName == "array" ) {
		QList<QVariant> values;
		QDomNode valueNode = typeElement.firstChild().firstChild();
		while(!valueNode.isNull()) {
			values << fromXml(valueNode.toElement());
			valueNode = valueNode.nextSibling();
		}
		return QVariant(values);
	}
	else if ( typeName == "struct" ) {
		QMap<QString, QVariant> map;
		QDomNode memberNode = typeElement.firstChild();
		while(!memberNode.isNull())	{
			const QString key = memberNode.toElement().elementsByTagName("name").item(0).toElement().text();
			const QVariant data = fromXml(memberNode.toElement().elementsByTagName("value").item(0).toElement());
			map[key] = data;
			memberNode = memberNode.nextSibling();
		}
		return QVariant(map);
	} else {
		qDebug() << "Cannot demarshal unknown type " << typeElement.tagName().toLower();
	}
	return QVariant();
}

QString MaiaObject::prepareCall(QString method, QList<QVariant> args) {

	QDomDocument doc;

	QDomProcessingInstruction header = doc.createProcessingInstruction( "xml", QString("version=\"1.0\" encoding=\"UTF-8\"" ));
	doc.appendChild(header);

	QDomElement methodCall = doc.createElement("methodCall");
	QDomElement methodName = doc.createElement("methodName");
	QDomElement params = doc.createElement("params");
	QDomElement param;

	doc.appendChild(methodCall);
	methodCall.appendChild(methodName);
	methodName.appendChild(doc.createTextNode(method));

	methodCall.appendChild(params);

	for(int i = 0; i < args.size(); ++i) {
		param = doc.createElement("param");
		param.appendChild(toXml(args.at(i)));
		params.appendChild(param);
	}

	return doc.toString();
}

QString MaiaObject::prepareResponse(QVariant arg) {

	QDomDocument doc;

	QDomProcessingInstruction header = doc.createProcessingInstruction( "xml", QString("version=\"1.0\" encoding=\"UTF-8\"" ));
	doc.appendChild(header);

	QDomElement methodResponse = doc.createElement("methodResponse");
	QDomElement params = doc.createElement("params");
	QDomElement param;

	doc.appendChild(methodResponse);

	methodResponse.appendChild(params);

	if(!arg.isNull()) {
		param = doc.createElement("param");
		param.appendChild(toXml(arg));
		params.appendChild(param);
	}
	return doc.toString();
}

void MaiaObject::parseResponse(QString response, QNetworkReply* reply) {
	QDomDocument doc;
	QVariant arg;
	QString errorMsg;
	int errorLine;
	int errorColumn;
	if(!doc.setContent(response, &errorMsg, &errorLine, &errorColumn)) {
		emit fault(-32700, QString("parse error: response not well formed at line %1: %2").arg(errorLine).arg(errorMsg), reply);
		delete this;
		return;
	}
	if(doc.documentElement().firstChild().toElement().tagName().toLower() == "params") {
		QDomNode paramNode = doc.documentElement().firstChild().firstChild();
		if(!paramNode.isNull()) {
			arg = fromXml( paramNode.firstChild().toElement() );
		}
		emit aresponse(arg, reply);
	} else if(doc.documentElement().firstChild().toElement().tagName().toLower() == "fault") {
		const QVariant errorVariant = fromXml(doc.documentElement().firstChild().firstChild().toElement());
		emit fault(errorVariant.toMap() [ "faultCode" ].toInt(),
		           errorVariant.toMap() [ "faultString" ].toString(),
		           reply);
	} else {
		emit fault(-32600,
		           tr("parse error: invalid xml-rpc. not conforming to spec."),
		           reply);
	}
	delete this;
	return;
}

<|endoftext|>


<commit_before>// Copyright (c) 2011 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 "chrome/browser/extensions/extension_tts_api_controller.h"

#include <string>
#include <vector>

#include "base/float_util.h"
#include "base/json/json_writer.h"
#include "base/values.h"
#include "chrome/browser/extensions/extension_event_router.h"
#include "chrome/browser/extensions/extension_tts_api.h"
#include "chrome/browser/extensions/extension_tts_api_constants.h"
#include "chrome/browser/extensions/extension_tts_api_platform.h"
#include "chrome/browser/extensions/extension_tts_engine_api.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/extension.h"

namespace constants = extension_tts_api_constants;

namespace events {
const char kOnEvent[] = "tts.onEvent";
};  // namespace events

std::string TtsEventTypeToString(TtsEventType event_type) {
  switch (event_type) {
    case TTS_EVENT_START:
      return constants::kEventTypeStart;
    case TTS_EVENT_END:
      return constants::kEventTypeEnd;
    case TTS_EVENT_WORD:
      return constants::kEventTypeWord;
    case TTS_EVENT_SENTENCE:
      return constants::kEventTypeSentence;
    case TTS_EVENT_MARKER:
      return constants::kEventTypeMarker;
    case TTS_EVENT_INTERRUPTED:
      return constants::kEventTypeInterrupted;
    case TTS_EVENT_CANCELLED:
      return constants::kEventTypeCancelled;
    case TTS_EVENT_ERROR:
      return constants::kEventTypeError;
    default:
      NOTREACHED();
      return std::string();
  }
}

//
// UtteranceContinuousParameters
//

UtteranceContinuousParameters::UtteranceContinuousParameters()
    : rate(-1),
      pitch(-1),
      volume(-1) {}

//
// Utterance
//

// static
int Utterance::next_utterance_id_ = 0;

Utterance::Utterance(Profile* profile)
    : profile_(profile),
      id_(next_utterance_id_++),
      src_id_(-1),
      can_enqueue_(false),
      char_index_(0),
      finished_(false) {
  options_.reset(new DictionaryValue());
}

Utterance::~Utterance() {
  DCHECK(finished_);
}

void Utterance::OnTtsEvent(TtsEventType event_type,
                           int char_index,
                           const std::string& error_message) {
  std::string event_type_string = TtsEventTypeToString(event_type);
  if (char_index >= 0)
    char_index_ = char_index;
  if (event_type == TTS_EVENT_END ||
      event_type == TTS_EVENT_INTERRUPTED ||
      event_type == TTS_EVENT_CANCELLED ||
      event_type == TTS_EVENT_ERROR) {
    finished_ = true;
  }
  if (desired_event_types_.size() > 0 &&
      desired_event_types_.find(event_type_string) ==
      desired_event_types_.end()) {
    return;
  }

  if (src_id_ < 0)
    return;

  ListValue args;
  DictionaryValue* event = new DictionaryValue();
  event->SetInteger(constants::kCharIndexKey, char_index);
  event->SetString(constants::kEventTypeKey, event_type_string);
  if (event_type == TTS_EVENT_ERROR) {
    event->SetString(constants::kErrorMessageKey, error_message);
  }
  event->SetInteger(constants::kSrcIdKey, src_id_);
  event->SetBoolean(constants::kIsFinalEventKey, finished_);
  args.Set(0, event);
  std::string json_args;
  base::JSONWriter::Write(&args, false, &json_args);

  profile_->GetExtensionEventRouter()->DispatchEventToExtension(
      src_extension_id_,
      events::kOnEvent,
      json_args,
      profile_,
      src_url_);
}

void Utterance::Finish() {
  finished_ = true;
}

void Utterance::set_options(const Value* options) {
  options_.reset(options->DeepCopy());
}

//
// ExtensionTtsController
//

// static
ExtensionTtsController* ExtensionTtsController::GetInstance() {
  return Singleton<ExtensionTtsController>::get();
}

ExtensionTtsController::ExtensionTtsController()
    : current_utterance_(NULL),
      platform_impl_(NULL) {
}

ExtensionTtsController::~ExtensionTtsController() {
  FinishCurrentUtterance();
  ClearUtteranceQueue();
}

void ExtensionTtsController::SpeakOrEnqueue(Utterance* utterance) {
  if (IsSpeaking() && utterance->can_enqueue()) {
    utterance_queue_.push(utterance);
  } else {
    Stop();
    SpeakNow(utterance);
  }
}

void ExtensionTtsController::SpeakNow(Utterance* utterance) {
  const Extension* extension;
  size_t voice_index;
  if (GetMatchingExtensionVoice(utterance, &extension, &voice_index)) {
    current_utterance_ = utterance;
    utterance->set_extension_id(extension->id());

    ExtensionTtsEngineSpeak(utterance, extension, voice_index);

    const std::set<std::string> event_types =
        extension->tts_voices()[voice_index].event_types;
    bool sends_end_event =
        (event_types.find(constants::kEventTypeEnd) != event_types.end());
    if (!sends_end_event) {
      utterance->Finish();
      delete utterance;
      current_utterance_ = NULL;
      SpeakNextUtterance();
    }
    return;
  }

  GetPlatformImpl()->clear_error();
  bool success = GetPlatformImpl()->Speak(
      utterance->id(),
      utterance->text(),
      utterance->lang(),
      utterance->continuous_parameters());
  if (!success) {
    utterance->OnTtsEvent(TTS_EVENT_ERROR, -1, GetPlatformImpl()->error());
    delete utterance;
    return;
  }
  current_utterance_ = utterance;
}

void ExtensionTtsController::Stop() {
  if (current_utterance_ && !current_utterance_->extension_id().empty()) {
    ExtensionTtsEngineStop(current_utterance_);
  } else {
    GetPlatformImpl()->clear_error();
    GetPlatformImpl()->StopSpeaking();
  }

  if (current_utterance_)
    current_utterance_->OnTtsEvent(TTS_EVENT_INTERRUPTED, -1, std::string());
  FinishCurrentUtterance();
  ClearUtteranceQueue();
}

void ExtensionTtsController::OnTtsEvent(int utterance_id,
                                        TtsEventType event_type,
                                        int char_index,
                                        const std::string& error_message) {
  // We may sometimes receive completion callbacks "late", after we've
  // already finished the utterance (for example because another utterance
  // interrupted or we got a call to Stop). This is normal and we can
  // safely just ignore these events.
  if (!current_utterance_ || utterance_id != current_utterance_->id())
    return;

  current_utterance_->OnTtsEvent(event_type, char_index, error_message);
  if (current_utterance_->finished()) {
    FinishCurrentUtterance();
    SpeakNextUtterance();
  }
}

ListValue* ExtensionTtsController::GetVoices(Profile* profile) {
  ListValue* result_voices = new ListValue();
  if (platform_impl_ && platform_impl_->PlatformImplAvailable()) {
    DictionaryValue* result_voice = new DictionaryValue();
    result_voice->SetString(
        constants::kVoiceNameKey, constants::kNativeVoiceName);
    if (!platform_impl_->gender().empty())
      result_voice->SetString(constants::kGenderKey, platform_impl_->gender());
    ListValue* event_types = new ListValue();

    // All platforms must send end events, and cancelled and interrupted
    // events are generated from the controller.
    DCHECK(platform_impl_->SendsEvent(TTS_EVENT_END));
    event_types->Append(Value::CreateStringValue(constants::kEventTypeEnd));
    event_types->Append(Value::CreateStringValue(
        constants::kEventTypeCancelled));
    event_types->Append(Value::CreateStringValue(
        constants::kEventTypeInterrupted));

    if (platform_impl_->SendsEvent(TTS_EVENT_START))
      event_types->Append(Value::CreateStringValue(constants::kEventTypeStart));
    if (platform_impl_->SendsEvent(TTS_EVENT_WORD))
      event_types->Append(Value::CreateStringValue(constants::kEventTypeWord));
    if (platform_impl_->SendsEvent(TTS_EVENT_SENTENCE))
      event_types->Append(Value::CreateStringValue(
          constants::kEventTypeSentence));
    if (platform_impl_->SendsEvent(TTS_EVENT_MARKER))
      event_types->Append(Value::CreateStringValue(
          constants::kEventTypeMarker));
    if (platform_impl_->SendsEvent(TTS_EVENT_ERROR))
      event_types->Append(Value::CreateStringValue(
          constants::kEventTypeError));
    result_voice->Set(constants::kEventTypesKey, event_types);
    result_voices->Append(result_voice);
  }

  GetExtensionVoices(profile, result_voices);

  return result_voices;
}

bool ExtensionTtsController::IsSpeaking() const {
  return current_utterance_ != NULL;
}

void ExtensionTtsController::FinishCurrentUtterance() {
  if (current_utterance_) {
    if (!current_utterance_->finished())
      current_utterance_->OnTtsEvent(TTS_EVENT_INTERRUPTED, -1, std::string());
    delete current_utterance_;
    current_utterance_ = NULL;
  }
}

void ExtensionTtsController::SpeakNextUtterance() {
  // Start speaking the next utterance in the queue.  Keep trying in case
  // one fails but there are still more in the queue to try.
  while (!utterance_queue_.empty() && !current_utterance_) {
    Utterance* utterance = utterance_queue_.front();
    utterance_queue_.pop();
    SpeakNow(utterance);
  }
}

void ExtensionTtsController::ClearUtteranceQueue() {
  while (!utterance_queue_.empty()) {
    Utterance* utterance = utterance_queue_.front();
    utterance_queue_.pop();
    utterance->OnTtsEvent(TTS_EVENT_CANCELLED, -1, std::string());
    delete utterance;
  }
}

void ExtensionTtsController::SetPlatformImpl(
    ExtensionTtsPlatformImpl* platform_impl) {
  platform_impl_ = platform_impl;
}

int ExtensionTtsController::QueueSize() {
  return static_cast<int>(utterance_queue_.size());
}

ExtensionTtsPlatformImpl* ExtensionTtsController::GetPlatformImpl() {
  if (!platform_impl_)
    platform_impl_ = ExtensionTtsPlatformImpl::GetInstance();
  return platform_impl_;
}
<commit_msg>Fix a bug in the TTS extension API where the getVoices function wouldn't return a native voice if no other extension API functions had been called, due to accessing a variable that's supposed to be initialized on demand.<commit_after>// Copyright (c) 2011 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 "chrome/browser/extensions/extension_tts_api_controller.h"

#include <string>
#include <vector>

#include "base/float_util.h"
#include "base/json/json_writer.h"
#include "base/values.h"
#include "chrome/browser/extensions/extension_event_router.h"
#include "chrome/browser/extensions/extension_tts_api.h"
#include "chrome/browser/extensions/extension_tts_api_constants.h"
#include "chrome/browser/extensions/extension_tts_api_platform.h"
#include "chrome/browser/extensions/extension_tts_engine_api.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/extension.h"

namespace constants = extension_tts_api_constants;

namespace events {
const char kOnEvent[] = "tts.onEvent";
};  // namespace events

std::string TtsEventTypeToString(TtsEventType event_type) {
  switch (event_type) {
    case TTS_EVENT_START:
      return constants::kEventTypeStart;
    case TTS_EVENT_END:
      return constants::kEventTypeEnd;
    case TTS_EVENT_WORD:
      return constants::kEventTypeWord;
    case TTS_EVENT_SENTENCE:
      return constants::kEventTypeSentence;
    case TTS_EVENT_MARKER:
      return constants::kEventTypeMarker;
    case TTS_EVENT_INTERRUPTED:
      return constants::kEventTypeInterrupted;
    case TTS_EVENT_CANCELLED:
      return constants::kEventTypeCancelled;
    case TTS_EVENT_ERROR:
      return constants::kEventTypeError;
    default:
      NOTREACHED();
      return std::string();
  }
}

//
// UtteranceContinuousParameters
//

UtteranceContinuousParameters::UtteranceContinuousParameters()
    : rate(-1),
      pitch(-1),
      volume(-1) {}

//
// Utterance
//

// static
int Utterance::next_utterance_id_ = 0;

Utterance::Utterance(Profile* profile)
    : profile_(profile),
      id_(next_utterance_id_++),
      src_id_(-1),
      can_enqueue_(false),
      char_index_(0),
      finished_(false) {
  options_.reset(new DictionaryValue());
}

Utterance::~Utterance() {
  DCHECK(finished_);
}

void Utterance::OnTtsEvent(TtsEventType event_type,
                           int char_index,
                           const std::string& error_message) {
  std::string event_type_string = TtsEventTypeToString(event_type);
  if (char_index >= 0)
    char_index_ = char_index;
  if (event_type == TTS_EVENT_END ||
      event_type == TTS_EVENT_INTERRUPTED ||
      event_type == TTS_EVENT_CANCELLED ||
      event_type == TTS_EVENT_ERROR) {
    finished_ = true;
  }
  if (desired_event_types_.size() > 0 &&
      desired_event_types_.find(event_type_string) ==
      desired_event_types_.end()) {
    return;
  }

  if (src_id_ < 0)
    return;

  ListValue args;
  DictionaryValue* event = new DictionaryValue();
  event->SetInteger(constants::kCharIndexKey, char_index);
  event->SetString(constants::kEventTypeKey, event_type_string);
  if (event_type == TTS_EVENT_ERROR) {
    event->SetString(constants::kErrorMessageKey, error_message);
  }
  event->SetInteger(constants::kSrcIdKey, src_id_);
  event->SetBoolean(constants::kIsFinalEventKey, finished_);
  args.Set(0, event);
  std::string json_args;
  base::JSONWriter::Write(&args, false, &json_args);

  profile_->GetExtensionEventRouter()->DispatchEventToExtension(
      src_extension_id_,
      events::kOnEvent,
      json_args,
      profile_,
      src_url_);
}

void Utterance::Finish() {
  finished_ = true;
}

void Utterance::set_options(const Value* options) {
  options_.reset(options->DeepCopy());
}

//
// ExtensionTtsController
//

// static
ExtensionTtsController* ExtensionTtsController::GetInstance() {
  return Singleton<ExtensionTtsController>::get();
}

ExtensionTtsController::ExtensionTtsController()
    : current_utterance_(NULL),
      platform_impl_(NULL) {
}

ExtensionTtsController::~ExtensionTtsController() {
  FinishCurrentUtterance();
  ClearUtteranceQueue();
}

void ExtensionTtsController::SpeakOrEnqueue(Utterance* utterance) {
  if (IsSpeaking() && utterance->can_enqueue()) {
    utterance_queue_.push(utterance);
  } else {
    Stop();
    SpeakNow(utterance);
  }
}

void ExtensionTtsController::SpeakNow(Utterance* utterance) {
  const Extension* extension;
  size_t voice_index;
  if (GetMatchingExtensionVoice(utterance, &extension, &voice_index)) {
    current_utterance_ = utterance;
    utterance->set_extension_id(extension->id());

    ExtensionTtsEngineSpeak(utterance, extension, voice_index);

    const std::set<std::string> event_types =
        extension->tts_voices()[voice_index].event_types;
    bool sends_end_event =
        (event_types.find(constants::kEventTypeEnd) != event_types.end());
    if (!sends_end_event) {
      utterance->Finish();
      delete utterance;
      current_utterance_ = NULL;
      SpeakNextUtterance();
    }
    return;
  }

  GetPlatformImpl()->clear_error();
  bool success = GetPlatformImpl()->Speak(
      utterance->id(),
      utterance->text(),
      utterance->lang(),
      utterance->continuous_parameters());
  if (!success) {
    utterance->OnTtsEvent(TTS_EVENT_ERROR, -1, GetPlatformImpl()->error());
    delete utterance;
    return;
  }
  current_utterance_ = utterance;
}

void ExtensionTtsController::Stop() {
  if (current_utterance_ && !current_utterance_->extension_id().empty()) {
    ExtensionTtsEngineStop(current_utterance_);
  } else {
    GetPlatformImpl()->clear_error();
    GetPlatformImpl()->StopSpeaking();
  }

  if (current_utterance_)
    current_utterance_->OnTtsEvent(TTS_EVENT_INTERRUPTED, -1, std::string());
  FinishCurrentUtterance();
  ClearUtteranceQueue();
}

void ExtensionTtsController::OnTtsEvent(int utterance_id,
                                        TtsEventType event_type,
                                        int char_index,
                                        const std::string& error_message) {
  // We may sometimes receive completion callbacks "late", after we've
  // already finished the utterance (for example because another utterance
  // interrupted or we got a call to Stop). This is normal and we can
  // safely just ignore these events.
  if (!current_utterance_ || utterance_id != current_utterance_->id())
    return;

  current_utterance_->OnTtsEvent(event_type, char_index, error_message);
  if (current_utterance_->finished()) {
    FinishCurrentUtterance();
    SpeakNextUtterance();
  }
}

ListValue* ExtensionTtsController::GetVoices(Profile* profile) {
  ListValue* result_voices = new ListValue();
  ExtensionTtsPlatformImpl* platform_impl = GetPlatformImpl();
  if (platform_impl && platform_impl->PlatformImplAvailable()) {
    DictionaryValue* result_voice = new DictionaryValue();
    result_voice->SetString(
        constants::kVoiceNameKey, constants::kNativeVoiceName);
    if (!platform_impl->gender().empty())
      result_voice->SetString(constants::kGenderKey, platform_impl->gender());
    ListValue* event_types = new ListValue();

    // All platforms must send end events, and cancelled and interrupted
    // events are generated from the controller.
    DCHECK(platform_impl->SendsEvent(TTS_EVENT_END));
    event_types->Append(Value::CreateStringValue(constants::kEventTypeEnd));
    event_types->Append(Value::CreateStringValue(
        constants::kEventTypeCancelled));
    event_types->Append(Value::CreateStringValue(
        constants::kEventTypeInterrupted));

    if (platform_impl->SendsEvent(TTS_EVENT_START))
      event_types->Append(Value::CreateStringValue(constants::kEventTypeStart));
    if (platform_impl->SendsEvent(TTS_EVENT_WORD))
      event_types->Append(Value::CreateStringValue(constants::kEventTypeWord));
    if (platform_impl->SendsEvent(TTS_EVENT_SENTENCE))
      event_types->Append(Value::CreateStringValue(
          constants::kEventTypeSentence));
    if (platform_impl->SendsEvent(TTS_EVENT_MARKER))
      event_types->Append(Value::CreateStringValue(
          constants::kEventTypeMarker));
    if (platform_impl->SendsEvent(TTS_EVENT_ERROR))
      event_types->Append(Value::CreateStringValue(
          constants::kEventTypeError));
    result_voice->Set(constants::kEventTypesKey, event_types);
    result_voices->Append(result_voice);
  }

  GetExtensionVoices(profile, result_voices);

  return result_voices;
}

bool ExtensionTtsController::IsSpeaking() const {
  return current_utterance_ != NULL;
}

void ExtensionTtsController::FinishCurrentUtterance() {
  if (current_utterance_) {
    if (!current_utterance_->finished())
      current_utterance_->OnTtsEvent(TTS_EVENT_INTERRUPTED, -1, std::string());
    delete current_utterance_;
    current_utterance_ = NULL;
  }
}

void ExtensionTtsController::SpeakNextUtterance() {
  // Start speaking the next utterance in the queue.  Keep trying in case
  // one fails but there are still more in the queue to try.
  while (!utterance_queue_.empty() && !current_utterance_) {
    Utterance* utterance = utterance_queue_.front();
    utterance_queue_.pop();
    SpeakNow(utterance);
  }
}

void ExtensionTtsController::ClearUtteranceQueue() {
  while (!utterance_queue_.empty()) {
    Utterance* utterance = utterance_queue_.front();
    utterance_queue_.pop();
    utterance->OnTtsEvent(TTS_EVENT_CANCELLED, -1, std::string());
    delete utterance;
  }
}

void ExtensionTtsController::SetPlatformImpl(
    ExtensionTtsPlatformImpl* platform_impl) {
  platform_impl_ = platform_impl;
}

int ExtensionTtsController::QueueSize() {
  return static_cast<int>(utterance_queue_.size());
}

ExtensionTtsPlatformImpl* ExtensionTtsController::GetPlatformImpl() {
  if (!platform_impl_)
    platform_impl_ = ExtensionTtsPlatformImpl::GetInstance();
  return platform_impl_;
}
<|endoftext|>