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/services/public/cpp/view_manager/lib/bitmap_uploader.h"
6
7#ifndef GL_GLEXT_PROTOTYPES
8#define GL_GLEXT_PROTOTYPES
9#endif  // GL_GLEXT_PROTOTYPES
10
11#include "base/bind.h"
12#include "cc/surfaces/surface_id.h"
13#include "cc/surfaces/surface_id_allocator.h"
14#include "gpu/GLES2/gl2chromium.h"
15#include "gpu/GLES2/gl2extchromium.h"
16#include "gpu/command_buffer/common/mailbox.h"
17#include "mojo/public/c/gles2/gles2.h"
18#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
19#include "mojo/services/public/cpp/surfaces/surfaces_type_converters.h"
20#include "mojo/services/public/cpp/surfaces/surfaces_utils.h"
21#include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h"
22#include "third_party/khronos/GLES2/gl2.h"
23#include "ui/gfx/geometry/rect.h"
24
25namespace mojo {
26
27namespace {
28void LostContext(void*) {
29  DCHECK(false);
30}
31
32uint32_t TextureFormat() {
33  return SK_B32_SHIFT ? GL_RGBA : GL_BGRA_EXT;
34}
35}
36
37BitmapUploader::BitmapUploader(ViewManagerClientImpl* client,
38                               Id view_id,
39                               SurfacesServicePtr surfaces_service,
40                               GpuPtr gpu_service)
41    : client_(client),
42      view_id_(view_id),
43      surfaces_service_(surfaces_service.Pass()),
44      gpu_service_(gpu_service.Pass()),
45      color_(SK_ColorTRANSPARENT),
46      next_resource_id_(1u),
47      weak_factory_(this) {
48  surfaces_service_->CreateSurfaceConnection(base::Bind(
49      &BitmapUploader::OnSurfaceConnectionCreated, weak_factory_.GetWeakPtr()));
50  CommandBufferPtr gles2_client;
51  gpu_service_->CreateOffscreenGLES2Context(Get(&gles2_client));
52  gles2_context_ =
53      MojoGLES2CreateContext(gles2_client.PassMessagePipe().release().value(),
54                             &LostContext,
55                             NULL,
56                             Environment::GetDefaultAsyncWaiter());
57  MojoGLES2MakeCurrent(gles2_context_);
58}
59
60BitmapUploader::~BitmapUploader() {
61  MojoGLES2DestroyContext(gles2_context_);
62}
63
64void BitmapUploader::SetSize(const gfx::Size& size) {
65  if (size_ == size)
66    return;
67  size_ = size;
68}
69
70void BitmapUploader::SetColor(SkColor color) {
71  if (color_ == color)
72    return;
73  color_ = color;
74  if (surface_)
75    Upload();
76}
77
78void BitmapUploader::SetBitmap(SkBitmap bitmap) {
79  bitmap_ = bitmap;
80  if (surface_)
81    Upload();
82}
83
84void BitmapUploader::Upload() {
85  if (size_.width() == 0 || size_.height() == 0) {
86    client_->SetSurfaceId(view_id_, SurfaceId::New());
87    return;
88  }
89  if (!surface_) {  // Can't upload yet, store for later.
90    done_callback_ = done_callback_;
91    return;
92  }
93  if (id_.is_null() || size_ != surface_size_) {
94    if (!id_.is_null())
95      surface_->DestroySurface(SurfaceId::From(id_));
96    id_ = id_allocator_->GenerateId();
97    surface_->CreateSurface(SurfaceId::From(id_), Size::From(size_));
98    client_->SetSurfaceId(view_id_, SurfaceId::From(id_));
99    surface_size_ = size_;
100  }
101
102  gfx::Rect bounds(size_);
103  PassPtr pass = CreateDefaultPass(1, bounds);
104  FramePtr frame = Frame::New();
105  frame->resources.resize(0u);
106
107  pass->quads.resize(0u);
108  pass->shared_quad_states.push_back(CreateDefaultSQS(size_));
109
110  MojoGLES2MakeCurrent(gles2_context_);
111  if (!bitmap_.isNull()) {
112    gfx::Size bitmap_size(bitmap_.width(), bitmap_.height());
113    GLuint texture_id = BindTextureForSize(bitmap_size);
114    bitmap_.lockPixels();
115    glTexSubImage2D(GL_TEXTURE_2D,
116                    0,
117                    0,
118                    0,
119                    bitmap_size.width(),
120                    bitmap_size.height(),
121                    TextureFormat(),
122                    GL_UNSIGNED_BYTE,
123                    bitmap_.getPixels());
124    bitmap_.unlockPixels();
125
126    gpu::Mailbox mailbox = gpu::Mailbox::Generate();
127    glProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
128    GLuint sync_point = glInsertSyncPointCHROMIUM();
129
130    TransferableResourcePtr resource = TransferableResource::New();
131    resource->id = next_resource_id_++;
132    resource_to_texture_id_map_[resource->id] = texture_id;
133    resource->format = mojo::RESOURCE_FORMAT_RGBA_8888;
134    resource->filter = GL_LINEAR;
135    resource->size = Size::From(bitmap_size);
136    MailboxHolderPtr mailbox_holder = MailboxHolder::New();
137    mailbox_holder->mailbox = Mailbox::From(mailbox);
138    mailbox_holder->texture_target = GL_TEXTURE_2D;
139    mailbox_holder->sync_point = sync_point;
140    resource->mailbox_holder = mailbox_holder.Pass();
141    resource->is_repeated = false;
142    resource->is_software = false;
143
144    QuadPtr quad = Quad::New();
145    quad->material = MATERIAL_TEXTURE_CONTENT;
146    quad->rect = Rect::From(bounds);
147    quad->opaque_rect = Rect::From(bounds);
148    quad->visible_rect = Rect::From(bounds);
149    quad->needs_blending = true;
150    quad->shared_quad_state_index = 0u;
151
152    TextureQuadStatePtr texture_state = TextureQuadState::New();
153    texture_state->resource_id = resource->id;
154    texture_state->premultiplied_alpha = true;
155    texture_state->uv_top_left = PointF::From(gfx::PointF(0.f, 0.f));
156    texture_state->uv_bottom_right = PointF::From(gfx::PointF(1.f, 1.f));
157    texture_state->background_color = Color::From(SkColor(SK_ColorTRANSPARENT));
158    for (int i = 0; i < 4; ++i)
159      texture_state->vertex_opacity.push_back(1.f);
160    texture_state->flipped = false;
161
162    frame->resources.push_back(resource.Pass());
163    quad->texture_quad_state = texture_state.Pass();
164    pass->quads.push_back(quad.Pass());
165  }
166
167  if (color_ != SK_ColorTRANSPARENT) {
168    QuadPtr quad = Quad::New();
169    quad->material = MATERIAL_SOLID_COLOR;
170    quad->rect = Rect::From(bounds);
171    quad->opaque_rect = Rect::From(gfx::Rect());
172    quad->visible_rect = Rect::From(bounds);
173    quad->needs_blending = true;
174    quad->shared_quad_state_index = 0u;
175
176    SolidColorQuadStatePtr color_state = SolidColorQuadState::New();
177    color_state->color = Color::From(color_);
178    color_state->force_anti_aliasing_off = false;
179
180    quad->solid_color_quad_state = color_state.Pass();
181    pass->quads.push_back(quad.Pass());
182  }
183
184  frame->passes.push_back(pass.Pass());
185
186  surface_->SubmitFrame(SurfaceId::From(id_), frame.Pass());
187}
188
189void BitmapUploader::ReturnResources(Array<ReturnedResourcePtr> resources) {
190  if (!resources.size())
191    return;
192  MojoGLES2MakeCurrent(gles2_context_);
193  // TODO(jamesr): Recycle.
194  for (size_t i = 0; i < resources.size(); ++i) {
195    ReturnedResourcePtr resource = resources[i].Pass();
196    DCHECK_EQ(1, resource->count);
197    glWaitSyncPointCHROMIUM(resource->sync_point);
198    uint32_t texture_id = resource_to_texture_id_map_[resource->id];
199    DCHECK_NE(0u, texture_id);
200    resource_to_texture_id_map_.erase(resource->id);
201    glDeleteTextures(1, &texture_id);
202  }
203}
204
205void BitmapUploader::OnSurfaceConnectionCreated(SurfacePtr surface,
206                                                uint32_t id_namespace) {
207  surface_ = surface.Pass();
208  surface_.set_client(this);
209  id_allocator_.reset(new cc::SurfaceIdAllocator(id_namespace));
210  if (color_ != SK_ColorTRANSPARENT || !bitmap_.isNull())
211    Upload();
212}
213
214uint32_t BitmapUploader::BindTextureForSize(const gfx::Size size) {
215  // TODO(jamesr): Recycle textures.
216  GLuint texture = 0u;
217  glGenTextures(1, &texture);
218  glBindTexture(GL_TEXTURE_2D, texture);
219  glTexImage2D(GL_TEXTURE_2D,
220               0,
221               TextureFormat(),
222               size.width(),
223               size.height(),
224               0,
225               TextureFormat(),
226               GL_UNSIGNED_BYTE,
227               0);
228  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
229  return texture;
230}
231
232}  // namespace mojo
233