mozart: Port the spinning cube example.

Initial port of the spinning cube example to the Mozart view system.

Note: This version does not support input yet.

R=abarth@google.com, jamesr@chromium.org

Review URL: https://codereview.chromium.org/1411103004 .
diff --git a/examples/BUILD.gn b/examples/BUILD.gn
index 03860ca..b4fe608 100644
--- a/examples/BUILD.gn
+++ b/examples/BUILD.gn
@@ -46,7 +46,10 @@
   }
 
   if (is_linux || is_android) {
-    deps += [ "//examples/surfaces_app" ]
+    deps += [
+      "//examples/ui/spinning_cube",
+      "//examples/surfaces_app",
+    ]
   }
 
   if (defined(go_build_tool) && go_build_tool != "") {
diff --git a/examples/ui/spinning_cube/BUILD.gn b/examples/ui/spinning_cube/BUILD.gn
new file mode 100644
index 0000000..db71418
--- /dev/null
+++ b/examples/ui/spinning_cube/BUILD.gn
@@ -0,0 +1,35 @@
+# Copyright 2015 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.
+
+import("//mojo/public/mojo_application.gni")
+
+mojo_native_application("spinning_cube") {
+  output_name = "spinning_cube_view"
+
+  sources = [
+    "main.cc",
+    "spinning_cube_app.cc",
+    "spinning_cube_app.h",
+    "spinning_cube_view.cc",
+    "spinning_cube_view.h",
+  ]
+
+  deps = [
+    "//base",
+    "//examples/spinning_cube:lib",
+    "//mojo/application",
+    "//mojo/common",
+    "//mojo/environment:chromium",
+    "//mojo/gpu",
+    "//mojo/public/c/gpu",
+    "//mojo/public/c/gpu:gpu_onscreen",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/environment",
+    "//mojo/public/cpp/system",
+    "//mojo/services/geometry/interfaces",
+    "//mojo/services/input_events/interfaces",
+    "//mojo/services/surfaces/interfaces",
+    "//mojo/services/ui/views/interfaces",
+  ]
+}
diff --git a/examples/ui/spinning_cube/README.md b/examples/ui/spinning_cube/README.md
new file mode 100644
index 0000000..1df5fa8
--- /dev/null
+++ b/examples/ui/spinning_cube/README.md
@@ -0,0 +1,8 @@
+# Mozart Spinning Cube Example
+
+This directory contains a simple application which draws a spinning cube
+into a View.
+
+## USAGE
+
+  out/Debug/mojo_shell "mojo:launcher mojo:spinning_cube_view"
diff --git a/examples/ui/spinning_cube/main.cc b/examples/ui/spinning_cube/main.cc
new file mode 100644
index 0000000..fcf0bc2
--- /dev/null
+++ b/examples/ui/spinning_cube/main.cc
@@ -0,0 +1,12 @@
+// Copyright 2015 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 "examples/ui/spinning_cube/spinning_cube_app.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/public/c/system/main.h"
+
+MojoResult MojoMain(MojoHandle application_request) {
+  mojo::ApplicationRunnerChromium runner(new examples::SpinningCubeApp);
+  return runner.Run(application_request);
+}
diff --git a/examples/ui/spinning_cube/spinning_cube_app.cc b/examples/ui/spinning_cube/spinning_cube_app.cc
new file mode 100644
index 0000000..6bee8d4
--- /dev/null
+++ b/examples/ui/spinning_cube/spinning_cube_app.cc
@@ -0,0 +1,37 @@
+// Copyright 2015 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 "examples/ui/spinning_cube/spinning_cube_app.h"
+#include "examples/ui/spinning_cube/spinning_cube_view.h"
+
+namespace examples {
+
+SpinningCubeApp::SpinningCubeApp() {}
+
+SpinningCubeApp::~SpinningCubeApp() {}
+
+void SpinningCubeApp::Initialize(mojo::ApplicationImpl* app_impl) {
+  app_impl_ = app_impl;
+}
+
+bool SpinningCubeApp::ConfigureIncomingConnection(
+    mojo::ApplicationConnection* connection) {
+  connection->AddService<mojo::ui::ViewProvider>(this);
+  return true;
+}
+
+void SpinningCubeApp::Create(
+    mojo::ApplicationConnection* connection,
+    mojo::InterfaceRequest<mojo::ui::ViewProvider> request) {
+  bindings_.AddBinding(this, request.Pass());
+}
+
+void SpinningCubeApp::CreateView(
+    mojo::InterfaceRequest<mojo::ServiceProvider> services,
+    mojo::ServiceProviderPtr exposed_services,
+    const CreateViewCallback& callback) {
+  new SpinningCubeView(app_impl_, callback);
+}
+
+}  // namespace examples
diff --git a/examples/ui/spinning_cube/spinning_cube_app.h b/examples/ui/spinning_cube/spinning_cube_app.h
new file mode 100644
index 0000000..d1e99e7
--- /dev/null
+++ b/examples/ui/spinning_cube/spinning_cube_app.h
@@ -0,0 +1,50 @@
+// Copyright 2015 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.
+
+#ifndef EXAMPLES_UI_SPINNING_CUBE_SPINNING_CUBE_APP_H_
+#define EXAMPLES_UI_SPINNING_CUBE_SPINNING_CUBE_APP_H_
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/common/binding_set.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/ui/views/interfaces/view_provider.mojom.h"
+
+namespace examples {
+
+class SpinningCubeApp : public mojo::ApplicationDelegate,
+                        public mojo::InterfaceFactory<mojo::ui::ViewProvider>,
+                        public mojo::ui::ViewProvider {
+ public:
+  SpinningCubeApp();
+  ~SpinningCubeApp() override;
+
+ private:
+  // |ApplicationDelegate|:
+  void Initialize(mojo::ApplicationImpl* app) override;
+  bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override;
+
+  // |InterfaceFactory<mojo::ui::ViewProvider>|:
+  void Create(mojo::ApplicationConnection* connection,
+              mojo::InterfaceRequest<mojo::ui::ViewProvider> request) override;
+
+  // |ViewProvider|:
+  void CreateView(mojo::InterfaceRequest<mojo::ServiceProvider> services,
+                  mojo::ServiceProviderPtr exposed_services,
+                  const CreateViewCallback& callback) override;
+
+  mojo::ApplicationImpl* app_impl_;
+  mojo::BindingSet<mojo::ui::ViewProvider> bindings_;
+
+  DISALLOW_COPY_AND_ASSIGN(SpinningCubeApp);
+};
+
+}  // namespace examples
+
+#endif  // EXAMPLES_UI_SPINNING_CUBE_SPINNING_CUBE_APP_H_
diff --git a/examples/ui/spinning_cube/spinning_cube_view.cc b/examples/ui/spinning_cube/spinning_cube_view.cc
new file mode 100644
index 0000000..8c3ce7a
--- /dev/null
+++ b/examples/ui/spinning_cube/spinning_cube_view.cc
@@ -0,0 +1,162 @@
+// Copyright 2015 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 <GLES2/gl2.h>
+#include <GLES2/gl2extmojo.h>
+#include <MGL/mgl.h>
+
+#include "base/message_loop/message_loop.h"
+#include "examples/ui/spinning_cube/spinning_cube_view.h"
+
+namespace examples {
+
+SpinningCubeView::SpinningCubeView(
+    mojo::ApplicationImpl* app,
+    const mojo::ui::ViewProvider::CreateViewCallback& callback)
+    : callback_(callback),
+      binding_(this),
+      context_owner_(app->shell()),
+      texture_cache_(context_owner_.context(), &resource_returner_),
+      surface_id_namespace_(0),
+      draw_scheduled_(false),
+      weak_ptr_factory_(this) {
+  app->ConnectToService("mojo:surfaces_service", &surfaces_);
+  app->ConnectToService("mojo:view_manager_service", &view_manager_);
+
+  surfaces_->SetResourceReturner(resource_returner_.Pass());
+  surfaces_->GetIdNamespace(
+      base::Bind(&SpinningCubeView::OnSurfaceIdNamespaceAvailable,
+                 base::Unretained(this)));
+
+  InitCube();
+}
+
+SpinningCubeView::~SpinningCubeView() {}
+
+void SpinningCubeView::OnSurfaceIdNamespaceAvailable(uint32_t id_namespace) {
+  surface_id_namespace_ = id_namespace;
+  InitView();
+}
+
+void SpinningCubeView::InitView() {
+  mojo::ui::ViewPtr view;
+  binding_.Bind(mojo::GetProxy(&view));
+  view_manager_->RegisterView(view.Pass(), mojo::GetProxy(&view_host_),
+                              callback_);
+
+  view_host_->GetServiceProvider(mojo::GetProxy(&view_service_provider_));
+}
+
+void SpinningCubeView::OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params,
+                                mojo::Array<uint32_t> children_needing_layout,
+                                const OnLayoutCallback& callback) {
+  // Create a new surface the first time or if the size has changed.
+  mojo::Size new_size;
+  new_size.width = layout_params->constraints->max_width;
+  new_size.height = layout_params->constraints->max_height;
+  if (!surface_id_ || !size_.Equals(new_size)) {
+    if (!surface_id_) {
+      surface_id_ = mojo::SurfaceId::New();
+      surface_id_->id_namespace = surface_id_namespace_;
+    } else {
+      surfaces_->DestroySurface(surface_id_->local);
+    }
+    surface_id_->local++;
+    size_ = new_size;
+    surfaces_->CreateSurface(surface_id_->local);
+  }
+
+  // Submit the new layout information.
+  auto info = mojo::ui::ViewLayoutInfo::New();
+  info->size = size_.Clone();
+  info->surface_id = surface_id_->Clone();
+  callback.Run(info.Pass());
+
+  // Draw!
+  ScheduleDraw();
+}
+
+void SpinningCubeView::OnChildUnavailable(
+    uint32_t child_key,
+    const OnChildUnavailableCallback& callback) {
+  callback.Run();
+}
+
+void SpinningCubeView::InitCube() {
+  context_owner_.context()->MakeCurrent();
+  cube_.Init();
+  last_draw_ = mojo::GetTimeTicksNow();
+}
+
+void SpinningCubeView::DrawCube() {
+  draw_scheduled_ = false;
+
+  scoped_ptr<mojo::TextureCache::TextureInfo> texture_info =
+      texture_cache_.GetTexture(size_);
+  if (!texture_info) {
+    LOG(ERROR) << "Could not allocate texture of size " << size_.width << "x"
+               << size_.height;
+    return;
+  }
+
+  context_owner_.context()->MakeCurrent();
+  scoped_ptr<mojo::GLTexture> texture = texture_info->TakeTexture();
+
+  GLuint fbo = 0u;
+  glGenFramebuffers(1, &fbo);
+  glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+  GLuint depth_buffer = 0u;
+  glGenRenderbuffers(1, &depth_buffer);
+  glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
+  glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size_.width,
+                        size_.height);
+  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                         texture->texture_id(), 0);
+  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+                            GL_RENDERBUFFER, depth_buffer);
+  DCHECK_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
+            glCheckFramebufferStatus(GL_FRAMEBUFFER));
+  glClearColor(1, 0, 0, 0.5);
+
+  cube_.set_size(size_.width, size_.height);
+
+  MojoTimeTicks now = mojo::GetTimeTicksNow();
+  MojoTimeTicks offset = now - last_draw_;
+  cube_.UpdateForTimeDelta(offset * 0.000001f);
+  last_draw_ = now;
+
+  cube_.Draw();
+
+  glDeleteFramebuffers(1, &fbo);
+  glDeleteRenderbuffers(1, &depth_buffer);
+
+  mojo::FramePtr frame = mojo::TextureUploader::GetUploadFrame(
+      context_owner_.context(), texture_info->resource_id(), texture);
+  surfaces_->SubmitFrame(surface_id_->local, frame.Pass(),
+                         base::Bind(&SpinningCubeView::OnSurfaceSubmitted,
+                                    base::Unretained(this)));
+
+  texture_cache_.NotifyPendingResourceReturn(texture_info->resource_id(),
+                                             texture.Pass());
+}
+
+void SpinningCubeView::OnSurfaceSubmitted() {
+  ScheduleDraw();
+}
+
+void SpinningCubeView::ScheduleDraw() {
+  if (!draw_scheduled_) {
+    draw_scheduled_ = true;
+
+    // TODO(jeffbrown): For now, we need to throttle this down because
+    // drawing as fast as we can appears to cause starvation of the
+    // Mojo message loop and makes X11 unhappy.
+    base::MessageLoop::current()->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&SpinningCubeView::DrawCube, weak_ptr_factory_.GetWeakPtr()),
+        base::TimeDelta::FromMilliseconds(30));
+  }
+}
+
+}  // namespace examples
diff --git a/examples/ui/spinning_cube/spinning_cube_view.h b/examples/ui/spinning_cube/spinning_cube_view.h
new file mode 100644
index 0000000..1a5b7fe
--- /dev/null
+++ b/examples/ui/spinning_cube/spinning_cube_view.h
@@ -0,0 +1,83 @@
+// Copyright 2015 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.
+
+#ifndef EXAMPLES_UI_SPINNING_CUBE_SPINNING_CUBE_VIEW_H_
+#define EXAMPLES_UI_SPINNING_CUBE_SPINNING_CUBE_VIEW_H_
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/memory/weak_ptr.h"
+#include "examples/spinning_cube/spinning_cube.h"
+#include "mojo/gpu/gl_context.h"
+#include "mojo/gpu/gl_context_owner.h"
+#include "mojo/gpu/gl_texture.h"
+#include "mojo/gpu/texture_cache.h"
+#include "mojo/gpu/texture_uploader.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/surfaces/interfaces/quads.mojom.h"
+#include "mojo/services/surfaces/interfaces/surfaces.mojom.h"
+#include "mojo/services/ui/views/interfaces/view_manager.mojom.h"
+#include "mojo/services/ui/views/interfaces/view_provider.mojom.h"
+#include "mojo/services/ui/views/interfaces/views.mojom.h"
+
+namespace examples {
+
+class SpinningCubeView : public mojo::ui::View {
+ public:
+  SpinningCubeView(mojo::ApplicationImpl* app,
+                   const mojo::ui::ViewProvider::CreateViewCallback& callback);
+
+  ~SpinningCubeView() override;
+
+ private:
+  // |View|:
+  void OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params,
+                mojo::Array<uint32_t> children_needing_layout,
+                const OnLayoutCallback& callback) override;
+  void OnChildUnavailable(uint32_t child_key,
+                          const OnChildUnavailableCallback& callback) override;
+
+  void OnSurfaceIdNamespaceAvailable(uint32_t id_namespace);
+
+  void InitView();
+
+  void InitCube();
+  void DrawCube();
+  void OnSurfaceSubmitted();
+  void ScheduleDraw();
+
+  mojo::ui::ViewProvider::CreateViewCallback callback_;
+  mojo::StrongBinding<mojo::ui::View> binding_;
+
+  mojo::GLContextOwner context_owner_;
+  mojo::ResourceReturnerPtr resource_returner_;  // must be before texture cache
+  mojo::TextureCache texture_cache_;
+
+  mojo::SurfacePtr surfaces_;
+  mojo::SurfaceIdPtr surface_id_;
+  uint32_t surface_id_namespace_;
+
+  mojo::ui::ViewManagerPtr view_manager_;
+  mojo::ui::ViewHostPtr view_host_;
+  mojo::ServiceProviderPtr view_service_provider_;
+
+  mojo::Size size_;
+
+  SpinningCube cube_;
+  MojoTimeTicks last_draw_;
+  bool draw_scheduled_;
+
+  base::WeakPtrFactory<SpinningCubeView> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SpinningCubeView);
+};
+
+}  // namespace examples
+
+#endif  // EXAMPLES_UI_SPINNING_CUBE_SPINNING_CUBE_VIEW_H_
diff --git a/mojo/gpu/texture_cache.cc b/mojo/gpu/texture_cache.cc
index f97a2ed..a76fedb 100644
--- a/mojo/gpu/texture_cache.cc
+++ b/mojo/gpu/texture_cache.cc
@@ -56,6 +56,7 @@
     // If the texture is the right size, use it.
     if (texture_size.width == requested_size.width &&
         texture_size.height == requested_size.height) {
+      gl_context_->MakeCurrent();
       glWaitSyncPointCHROMIUM(sync_point);
       return texture_info;
     }