普通文本  |  223行  |  6.35 KB

// Copyright 2014 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 "base/at_exit.h"
#include "base/command_line.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/timer/timer.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_surface.h"
#include "ui/ozone/public/ozone_platform.h"
#include "ui/ozone/public/surface_factory_ozone.h"
#include "ui/ozone/public/surface_ozone_canvas.h"
#include "ui/ozone/public/ui_thread_gpu.h"
#include "ui/platform_window/platform_window.h"
#include "ui/platform_window/platform_window_delegate.h"

const int kTestWindowWidth = 800;
const int kTestWindowHeight = 600;

const int kFrameDelayMilliseconds = 16;

const int kAnimationSteps = 240;

const char kDisableGpu[] = "disable-gpu";

class DemoWindow : public ui::PlatformWindowDelegate {
 public:
  DemoWindow() : widget_(gfx::kNullAcceleratedWidget), iteration_(0) {
    platform_window_ = ui::OzonePlatform::GetInstance()->CreatePlatformWindow(
        this, gfx::Rect(kTestWindowWidth, kTestWindowHeight));
  }
  virtual ~DemoWindow() {}

  gfx::AcceleratedWidget GetAcceleratedWidget() {
    // TODO(spang): We should start rendering asynchronously.
    DCHECK_NE(widget_, gfx::kNullAcceleratedWidget)
        << "Widget not available synchronously";
    return widget_;
  }

  gfx::Size GetSize() { return platform_window_->GetBounds().size(); }

  void Start() {
    if (!CommandLine::ForCurrentProcess()->HasSwitch(kDisableGpu) &&
        gfx::GLSurface::InitializeOneOff() && StartInProcessGpu() &&
        InitializeGLSurface()) {
      StartAnimationGL();
    } else if (InitializeSoftwareSurface()) {
      StartAnimationSoftware();
    } else {
      LOG(ERROR) << "Failed to create drawing surface";
      Quit();
    }
  }

  void Quit() {
    StopAnimation();
    base::MessageLoop::current()->PostTask(
        FROM_HERE, base::Bind(&base::DeletePointer<DemoWindow>, this));
   }

  // PlatformWindowDelegate:
  virtual void OnBoundsChanged(const gfx::Rect& new_bounds) OVERRIDE {}
  virtual void OnDamageRect(const gfx::Rect& damaged_region) OVERRIDE {}
  virtual void DispatchEvent(ui::Event* event) OVERRIDE {}
  virtual void OnCloseRequest() OVERRIDE {
    Quit();
  }
  virtual void OnClosed() OVERRIDE {}
  virtual void OnWindowStateChanged(
      ui::PlatformWindowState new_state) OVERRIDE {}
  virtual void OnLostCapture() OVERRIDE {}
  virtual void OnAcceleratedWidgetAvailable(
      gfx::AcceleratedWidget widget) OVERRIDE {
    DCHECK_NE(widget, gfx::kNullAcceleratedWidget);
    widget_ = widget;
  }
  virtual void OnActivationChanged(bool active) OVERRIDE {}

 private:
  bool InitializeGLSurface() {
    surface_ = gfx::GLSurface::CreateViewGLSurface(GetAcceleratedWidget());
    if (!surface_.get()) {
      LOG(ERROR) << "Failed to create GL surface";
      return false;
    }

    context_ = gfx::GLContext::CreateGLContext(
        NULL, surface_.get(), gfx::PreferIntegratedGpu);
    if (!context_.get()) {
      LOG(ERROR) << "Failed to create GL context";
      surface_ = NULL;
      return false;
    }

    surface_->Resize(GetSize());

    if (!context_->MakeCurrent(surface_.get())) {
      LOG(ERROR) << "Failed to make GL context current";
      surface_ = NULL;
      context_ = NULL;
      return false;
    }

    return true;
  }

  bool InitializeSoftwareSurface() {
    software_surface_ =
        ui::SurfaceFactoryOzone::GetInstance()->CreateCanvasForWidget(
            GetAcceleratedWidget());
    if (!software_surface_) {
      LOG(ERROR) << "Failed to create software surface";
      return false;
    }

    software_surface_->ResizeCanvas(GetSize());
    return true;
  }

  void StartAnimationGL() {
    timer_.Start(FROM_HERE,
                 base::TimeDelta::FromMicroseconds(kFrameDelayMilliseconds),
                 this,
                 &DemoWindow::RenderFrameGL);
  }

  void StartAnimationSoftware() {
    timer_.Start(FROM_HERE,
                 base::TimeDelta::FromMicroseconds(kFrameDelayMilliseconds),
                 this,
                 &DemoWindow::RenderFrameSoftware);
  }

  void StopAnimation() { timer_.Stop(); }

  float NextFraction() {
    float fraction = (sinf(iteration_ * 2 * M_PI / kAnimationSteps) + 1) / 2;

    iteration_++;
    iteration_ %= kAnimationSteps;

    return fraction;
  }

  void RenderFrameGL() {
    float fraction = NextFraction();
    gfx::Size window_size = GetSize();

    glViewport(0, 0, window_size.width(), window_size.height());
    glClearColor(1 - fraction, fraction, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    if (!surface_->SwapBuffers())
      LOG(FATAL) << "Failed to swap buffers";
  }

  void RenderFrameSoftware() {
    float fraction = NextFraction();
    gfx::Size window_size = GetSize();

    skia::RefPtr<SkCanvas> canvas = software_surface_->GetCanvas();

    SkColor color =
        SkColorSetARGB(0xff, 0, 0xff * fraction, 0xff * (1 - fraction));

    canvas->clear(color);

    software_surface_->PresentCanvas(gfx::Rect(window_size));
  }

  bool StartInProcessGpu() { return ui_thread_gpu_.Initialize(); }

  // Timer for animation.
  base::RepeatingTimer<DemoWindow> timer_;

  // Bits for GL rendering.
  scoped_refptr<gfx::GLSurface> surface_;
  scoped_refptr<gfx::GLContext> context_;

  // Bits for software rendeirng.
  scoped_ptr<ui::SurfaceOzoneCanvas> software_surface_;

  // Window-related state.
  scoped_ptr<ui::PlatformWindow> platform_window_;
  gfx::AcceleratedWidget widget_;

  // Helper for applications that do GL on main thread.
  ui::UiThreadGpu ui_thread_gpu_;

  // Animation state.
  int iteration_;

  DISALLOW_COPY_AND_ASSIGN(DemoWindow);
};

int main(int argc, char** argv) {
  CommandLine::Init(argc, argv);
  base::AtExitManager exit_manager;

  // Build UI thread message loop. This is used by platform
  // implementations for event polling & running background tasks.
  base::MessageLoopForUI message_loop;

  ui::OzonePlatform::InitializeForUI();

  DemoWindow* window = new DemoWindow;
  window->Start();

  // Run the message loop until there's nothing left to do.
  // TODO(spang): Should we use QuitClosure instead?
  base::RunLoop run_loop;
  run_loop.RunUntilIdle();

  return 0;
}