1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/at_exit.h"
6#include "base/command_line.h"
7#include "base/message_loop/message_loop.h"
8#include "base/run_loop.h"
9#include "base/timer/timer.h"
10#include "third_party/skia/include/core/SkCanvas.h"
11#include "third_party/skia/include/core/SkColor.h"
12#include "ui/gfx/geometry/size.h"
13#include "ui/gl/gl_bindings.h"
14#include "ui/gl/gl_context.h"
15#include "ui/gl/gl_surface.h"
16#include "ui/ozone/public/ozone_platform.h"
17#include "ui/ozone/public/surface_factory_ozone.h"
18#include "ui/ozone/public/surface_ozone_canvas.h"
19#include "ui/ozone/public/ui_thread_gpu.h"
20#include "ui/platform_window/platform_window.h"
21#include "ui/platform_window/platform_window_delegate.h"
22
23const int kTestWindowWidth = 800;
24const int kTestWindowHeight = 600;
25
26const int kFrameDelayMilliseconds = 16;
27
28const int kAnimationSteps = 240;
29
30const char kDisableGpu[] = "disable-gpu";
31
32class DemoWindow : public ui::PlatformWindowDelegate {
33 public:
34  DemoWindow() : widget_(gfx::kNullAcceleratedWidget), iteration_(0) {
35    platform_window_ = ui::OzonePlatform::GetInstance()->CreatePlatformWindow(
36        this, gfx::Rect(kTestWindowWidth, kTestWindowHeight));
37  }
38  virtual ~DemoWindow() {}
39
40  gfx::AcceleratedWidget GetAcceleratedWidget() {
41    // TODO(spang): We should start rendering asynchronously.
42    DCHECK_NE(widget_, gfx::kNullAcceleratedWidget)
43        << "Widget not available synchronously";
44    return widget_;
45  }
46
47  gfx::Size GetSize() { return platform_window_->GetBounds().size(); }
48
49  void Start() {
50    if (!CommandLine::ForCurrentProcess()->HasSwitch(kDisableGpu) &&
51        gfx::GLSurface::InitializeOneOff() && StartInProcessGpu() &&
52        InitializeGLSurface()) {
53      StartAnimationGL();
54    } else if (InitializeSoftwareSurface()) {
55      StartAnimationSoftware();
56    } else {
57      LOG(ERROR) << "Failed to create drawing surface";
58      Quit();
59    }
60  }
61
62  void Quit() {
63    StopAnimation();
64    base::MessageLoop::current()->PostTask(
65        FROM_HERE, base::Bind(&base::DeletePointer<DemoWindow>, this));
66   }
67
68  // PlatformWindowDelegate:
69  virtual void OnBoundsChanged(const gfx::Rect& new_bounds) OVERRIDE {}
70  virtual void OnDamageRect(const gfx::Rect& damaged_region) OVERRIDE {}
71  virtual void DispatchEvent(ui::Event* event) OVERRIDE {}
72  virtual void OnCloseRequest() OVERRIDE {
73    Quit();
74  }
75  virtual void OnClosed() OVERRIDE {}
76  virtual void OnWindowStateChanged(
77      ui::PlatformWindowState new_state) OVERRIDE {}
78  virtual void OnLostCapture() OVERRIDE {}
79  virtual void OnAcceleratedWidgetAvailable(
80      gfx::AcceleratedWidget widget) OVERRIDE {
81    DCHECK_NE(widget, gfx::kNullAcceleratedWidget);
82    widget_ = widget;
83  }
84  virtual void OnActivationChanged(bool active) OVERRIDE {}
85
86 private:
87  bool InitializeGLSurface() {
88    surface_ = gfx::GLSurface::CreateViewGLSurface(GetAcceleratedWidget());
89    if (!surface_.get()) {
90      LOG(ERROR) << "Failed to create GL surface";
91      return false;
92    }
93
94    context_ = gfx::GLContext::CreateGLContext(
95        NULL, surface_.get(), gfx::PreferIntegratedGpu);
96    if (!context_.get()) {
97      LOG(ERROR) << "Failed to create GL context";
98      surface_ = NULL;
99      return false;
100    }
101
102    surface_->Resize(GetSize());
103
104    if (!context_->MakeCurrent(surface_.get())) {
105      LOG(ERROR) << "Failed to make GL context current";
106      surface_ = NULL;
107      context_ = NULL;
108      return false;
109    }
110
111    return true;
112  }
113
114  bool InitializeSoftwareSurface() {
115    software_surface_ =
116        ui::SurfaceFactoryOzone::GetInstance()->CreateCanvasForWidget(
117            GetAcceleratedWidget());
118    if (!software_surface_) {
119      LOG(ERROR) << "Failed to create software surface";
120      return false;
121    }
122
123    software_surface_->ResizeCanvas(GetSize());
124    return true;
125  }
126
127  void StartAnimationGL() {
128    timer_.Start(FROM_HERE,
129                 base::TimeDelta::FromMicroseconds(kFrameDelayMilliseconds),
130                 this,
131                 &DemoWindow::RenderFrameGL);
132  }
133
134  void StartAnimationSoftware() {
135    timer_.Start(FROM_HERE,
136                 base::TimeDelta::FromMicroseconds(kFrameDelayMilliseconds),
137                 this,
138                 &DemoWindow::RenderFrameSoftware);
139  }
140
141  void StopAnimation() { timer_.Stop(); }
142
143  float NextFraction() {
144    float fraction = (sinf(iteration_ * 2 * M_PI / kAnimationSteps) + 1) / 2;
145
146    iteration_++;
147    iteration_ %= kAnimationSteps;
148
149    return fraction;
150  }
151
152  void RenderFrameGL() {
153    float fraction = NextFraction();
154    gfx::Size window_size = GetSize();
155
156    glViewport(0, 0, window_size.width(), window_size.height());
157    glClearColor(1 - fraction, fraction, 0.0, 1.0);
158    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
159
160    if (!surface_->SwapBuffers())
161      LOG(FATAL) << "Failed to swap buffers";
162  }
163
164  void RenderFrameSoftware() {
165    float fraction = NextFraction();
166    gfx::Size window_size = GetSize();
167
168    skia::RefPtr<SkCanvas> canvas = software_surface_->GetCanvas();
169
170    SkColor color =
171        SkColorSetARGB(0xff, 0, 0xff * fraction, 0xff * (1 - fraction));
172
173    canvas->clear(color);
174
175    software_surface_->PresentCanvas(gfx::Rect(window_size));
176  }
177
178  bool StartInProcessGpu() { return ui_thread_gpu_.Initialize(); }
179
180  // Timer for animation.
181  base::RepeatingTimer<DemoWindow> timer_;
182
183  // Bits for GL rendering.
184  scoped_refptr<gfx::GLSurface> surface_;
185  scoped_refptr<gfx::GLContext> context_;
186
187  // Bits for software rendeirng.
188  scoped_ptr<ui::SurfaceOzoneCanvas> software_surface_;
189
190  // Window-related state.
191  scoped_ptr<ui::PlatformWindow> platform_window_;
192  gfx::AcceleratedWidget widget_;
193
194  // Helper for applications that do GL on main thread.
195  ui::UiThreadGpu ui_thread_gpu_;
196
197  // Animation state.
198  int iteration_;
199
200  DISALLOW_COPY_AND_ASSIGN(DemoWindow);
201};
202
203int main(int argc, char** argv) {
204  CommandLine::Init(argc, argv);
205  base::AtExitManager exit_manager;
206
207  // Build UI thread message loop. This is used by platform
208  // implementations for event polling & running background tasks.
209  base::MessageLoopForUI message_loop;
210
211  ui::OzonePlatform::InitializeForUI();
212
213  DemoWindow* window = new DemoWindow;
214  window->Start();
215
216  // Run the message loop until there's nothing left to do.
217  // TODO(spang): Should we use QuitClosure instead?
218  base::RunLoop run_loop;
219  run_loop.RunUntilIdle();
220
221  return 0;
222}
223