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 "mojo/examples/surfaces_app/child_gl_impl.h"
6
7#ifndef GL_GLEXT_PROTOTYPES
8#define GL_GLEXT_PROTOTYPES
9#endif
10
11#include "base/bind.h"
12#include "base/message_loop/message_loop.h"
13#include "cc/output/compositor_frame.h"
14#include "cc/output/delegated_frame_data.h"
15#include "cc/quads/render_pass.h"
16#include "cc/quads/texture_draw_quad.h"
17#include "gpu/GLES2/gl2chromium.h"
18#include "gpu/GLES2/gl2extchromium.h"
19#include "mojo/examples/surfaces_app/surfaces_util.h"
20#include "mojo/public/cpp/application/application_connection.h"
21#include "mojo/public/cpp/environment/environment.h"
22#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
23#include "mojo/services/public/cpp/surfaces/surfaces_type_converters.h"
24#include "mojo/services/public/interfaces/surfaces/surface_id.mojom.h"
25#include "mojo/services/public/interfaces/surfaces/surfaces.mojom.h"
26#include "third_party/khronos/GLES2/gl2.h"
27#include "third_party/khronos/GLES2/gl2ext.h"
28#include "ui/gfx/rect.h"
29#include "ui/gfx/transform.h"
30
31namespace mojo {
32namespace examples {
33
34using cc::RenderPass;
35using cc::RenderPassId;
36using cc::DrawQuad;
37using cc::TextureDrawQuad;
38using cc::DelegatedFrameData;
39using cc::CompositorFrame;
40
41static void ContextLostThunk(void*) {
42  LOG(FATAL) << "Context lost";
43}
44
45ChildGLImpl::ChildGLImpl(ApplicationConnection* surfaces_service_connection,
46                         CommandBufferPtr command_buffer)
47    : start_time_(base::TimeTicks::Now()),
48      next_resource_id_(1),
49      weak_factory_(this) {
50  surfaces_service_connection->ConnectToService(&surfaces_service_);
51  surfaces_service_->CreateSurfaceConnection(base::Bind(
52      &ChildGLImpl::SurfaceConnectionCreated, weak_factory_.GetWeakPtr()));
53  context_ =
54      MojoGLES2CreateContext(command_buffer.PassMessagePipe().release().value(),
55                             &ContextLostThunk,
56                             this,
57                             Environment::GetDefaultAsyncWaiter());
58  DCHECK(context_);
59  MojoGLES2MakeCurrent(context_);
60}
61
62ChildGLImpl::~ChildGLImpl() {
63  MojoGLES2DestroyContext(context_);
64  surface_->DestroySurface(mojo::SurfaceId::From(id_));
65}
66
67void ChildGLImpl::ProduceFrame(
68    ColorPtr color,
69    SizePtr size,
70    const mojo::Callback<void(SurfaceIdPtr id)>& callback) {
71  color_ = color.To<SkColor>();
72  size_ = size.To<gfx::Size>();
73  cube_.Init(size_.width(), size_.height());
74  cube_.set_color(
75      SkColorGetR(color_), SkColorGetG(color_), SkColorGetB(color_));
76  produce_callback_ = callback;
77  AllocateSurface();
78}
79
80void ChildGLImpl::SurfaceConnectionCreated(SurfacePtr surface,
81                                           uint32_t id_namespace) {
82  surface_ = surface.Pass();
83  surface_.set_client(this);
84  allocator_.reset(new cc::SurfaceIdAllocator(id_namespace));
85  AllocateSurface();
86}
87
88void ChildGLImpl::ReturnResources(Array<ReturnedResourcePtr> resources) {
89  for (size_t i = 0; i < resources.size(); ++i) {
90    cc::ReturnedResource res = resources[i].To<cc::ReturnedResource>();
91    GLuint returned_texture = id_to_tex_map_[res.id];
92    glDeleteTextures(1, &returned_texture);
93  }
94}
95
96void ChildGLImpl::AllocateSurface() {
97  if (produce_callback_.is_null() || !allocator_)
98    return;
99
100  id_ = allocator_->GenerateId();
101  surface_->CreateSurface(mojo::SurfaceId::From(id_), mojo::Size::From(size_));
102  produce_callback_.Run(SurfaceId::From(id_));
103  Draw();
104}
105
106void ChildGLImpl::Draw() {
107  // First, generate a GL texture and draw the cube into it.
108  GLuint texture = 0u;
109  glGenTextures(1, &texture);
110  glBindTexture(GL_TEXTURE_2D, texture);
111  glTexImage2D(GL_TEXTURE_2D,
112               0,
113               GL_RGBA,
114               size_.width(),
115               size_.height(),
116               0,
117               GL_RGBA,
118               GL_UNSIGNED_BYTE,
119               0);
120  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
121  GLuint fbo = 0u;
122  glGenFramebuffers(1, &fbo);
123  glBindFramebuffer(GL_FRAMEBUFFER, fbo);
124  glFramebufferTexture2D(
125      GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
126  DCHECK_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
127            glCheckFramebufferStatus(GL_FRAMEBUFFER));
128  glClearColor(1, 0, 0, 0.5);
129  cube_.UpdateForTimeDelta(0.16f);
130  cube_.Draw();
131
132  // Then, put the texture into a mailbox.
133  gpu::Mailbox mailbox = gpu::Mailbox::Generate();
134  glProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
135  GLuint sync_point = glInsertSyncPointCHROMIUM();
136  gpu::MailboxHolder holder(mailbox, GL_TEXTURE_2D, sync_point);
137
138  // Then, put the mailbox into a TransferableResource
139  cc::TransferableResource resource;
140  resource.id = next_resource_id_++;
141  id_to_tex_map_[resource.id] = texture;
142  resource.format = cc::RGBA_8888;
143  resource.filter = GL_LINEAR;
144  resource.size = size_;
145  resource.mailbox_holder = holder;
146  resource.is_repeated = false;
147  resource.is_software = false;
148
149  gfx::Rect rect(size_);
150  RenderPassId id(1, 1);
151  scoped_ptr<RenderPass> pass = RenderPass::Create();
152  pass->SetNew(id, rect, rect, gfx::Transform());
153
154  CreateAndAppendSimpleSharedQuadState(pass.get(), gfx::Transform(), size_);
155
156  TextureDrawQuad* texture_quad =
157      pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
158  float vertex_opacity[4] = {1.0f, 1.0f, 0.2f, 1.0f};
159  texture_quad->SetNew(pass->shared_quad_state_list.back(),
160                       rect,
161                       rect,
162                       rect,
163                       resource.id,
164                       true,
165                       gfx::PointF(),
166                       gfx::PointF(1.f, 1.f),
167                       SK_ColorBLUE,
168                       vertex_opacity,
169                       false);
170
171  scoped_ptr<DelegatedFrameData> delegated_frame_data(new DelegatedFrameData);
172  delegated_frame_data->render_pass_list.push_back(pass.Pass());
173  delegated_frame_data->resource_list.push_back(resource);
174
175  scoped_ptr<CompositorFrame> frame(new CompositorFrame);
176  frame->delegated_frame_data = delegated_frame_data.Pass();
177
178  surface_->SubmitFrame(mojo::SurfaceId::From(id_), mojo::Frame::From(*frame));
179
180  base::MessageLoop::current()->PostDelayedTask(
181      FROM_HERE,
182      base::Bind(&ChildGLImpl::Draw, base::Unretained(this)),
183      base::TimeDelta::FromMilliseconds(50));
184}
185
186}  // namespace examples
187}  // namespace mojo
188