1// Copyright (c) 2013 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 "content/common/gpu/stream_texture_android.h"
6
7#include "base/bind.h"
8#include "content/common/android/surface_texture_peer.h"
9#include "content/common/gpu/gpu_channel.h"
10#include "content/common/gpu/gpu_messages.h"
11#include "gpu/command_buffer/service/context_group.h"
12#include "gpu/command_buffer/service/context_state.h"
13#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
14#include "gpu/command_buffer/service/texture_manager.h"
15#include "ui/gfx/size.h"
16#include "ui/gl/scoped_make_current.h"
17
18namespace content {
19
20using gpu::gles2::ContextGroup;
21using gpu::gles2::GLES2Decoder;
22using gpu::gles2::TextureManager;
23using gpu::gles2::TextureRef;
24
25// static
26bool StreamTexture::Create(
27    GpuCommandBufferStub* owner_stub,
28    uint32 client_texture_id,
29    int stream_id) {
30  GLES2Decoder* decoder = owner_stub->decoder();
31  TextureManager* texture_manager =
32      decoder->GetContextGroup()->texture_manager();
33  TextureRef* texture = texture_manager->GetTexture(client_texture_id);
34
35  if (texture && (!texture->texture()->target() ||
36                  texture->texture()->target() == GL_TEXTURE_EXTERNAL_OES)) {
37
38    // TODO: Ideally a valid image id was returned to the client so that
39    // it could then call glBindTexImage2D() for doing the following.
40    scoped_refptr<gfx::GLImage> gl_image(
41        new StreamTexture(owner_stub, stream_id, texture->service_id()));
42    gfx::Size size = gl_image->GetSize();
43    texture_manager->SetTarget(texture, GL_TEXTURE_EXTERNAL_OES);
44    texture_manager->SetLevelInfo(texture,
45                                  GL_TEXTURE_EXTERNAL_OES,
46                                  0,
47                                  GL_RGBA,
48                                  size.width(),
49                                  size.height(),
50                                  1,
51                                  0,
52                                  GL_RGBA,
53                                  GL_UNSIGNED_BYTE,
54                                  true);
55    texture_manager->SetLevelImage(
56        texture, GL_TEXTURE_EXTERNAL_OES, 0, gl_image);
57    return true;
58  }
59
60  return false;
61}
62
63StreamTexture::StreamTexture(GpuCommandBufferStub* owner_stub,
64                             int32 route_id,
65                             uint32 texture_id)
66    : surface_texture_(gfx::SurfaceTexture::Create(texture_id)),
67      size_(0, 0),
68      has_valid_frame_(false),
69      has_pending_frame_(false),
70      owner_stub_(owner_stub),
71      route_id_(route_id),
72      has_listener_(false),
73      weak_factory_(this) {
74  owner_stub->AddDestructionObserver(this);
75  memset(current_matrix_, 0, sizeof(current_matrix_));
76  owner_stub->channel()->AddRoute(route_id, this);
77  surface_texture_->SetFrameAvailableCallback(base::Bind(
78      &StreamTexture::OnFrameAvailable, weak_factory_.GetWeakPtr()));
79}
80
81StreamTexture::~StreamTexture() {
82  if (owner_stub_) {
83    owner_stub_->RemoveDestructionObserver(this);
84    owner_stub_->channel()->RemoveRoute(route_id_);
85  }
86}
87
88void StreamTexture::OnWillDestroyStub() {
89  owner_stub_->RemoveDestructionObserver(this);
90  owner_stub_->channel()->RemoveRoute(route_id_);
91  owner_stub_ = NULL;
92
93  // If the owner goes away, there is no need to keep the SurfaceTexture around.
94  // The GL texture will keep working regardless with the currently bound frame.
95  surface_texture_ = NULL;
96}
97
98void StreamTexture::Destroy(bool have_context) {
99  NOTREACHED();
100}
101
102void StreamTexture::WillUseTexImage() {
103  if (!owner_stub_ || !surface_texture_.get())
104    return;
105
106  if (has_pending_frame_) {
107    scoped_ptr<ui::ScopedMakeCurrent> scoped_make_current;
108    bool needs_make_current =
109        !owner_stub_->decoder()->GetGLContext()->IsCurrent(NULL);
110    // On Android we should not have to perform a real context switch here when
111    // using virtual contexts.
112    DCHECK(!needs_make_current || !owner_stub_->decoder()
113                                       ->GetContextGroup()
114                                       ->feature_info()
115                                       ->workarounds()
116                                       .use_virtualized_gl_contexts);
117    if (needs_make_current) {
118      scoped_make_current.reset(new ui::ScopedMakeCurrent(
119          owner_stub_->decoder()->GetGLContext(), owner_stub_->surface()));
120    }
121    surface_texture_->UpdateTexImage();
122    has_valid_frame_ = true;
123    has_pending_frame_ = false;
124    if (scoped_make_current.get()) {
125      // UpdateTexImage() implies glBindTexture().
126      // The cmd decoder takes care of restoring the binding for this GLImage as
127      // far as the current context is concerned, but if we temporarily change
128      // it, we have to keep the state intact in *that* context also.
129      const gpu::gles2::ContextState* state =
130          owner_stub_->decoder()->GetContextState();
131      const gpu::gles2::TextureUnit& active_unit =
132          state->texture_units[state->active_texture_unit];
133      glBindTexture(GL_TEXTURE_EXTERNAL_OES,
134                    active_unit.bound_texture_external_oes
135                        ? active_unit.bound_texture_external_oes->service_id()
136                        : 0);
137    }
138  }
139
140  if (has_listener_ && has_valid_frame_) {
141    float mtx[16];
142    surface_texture_->GetTransformMatrix(mtx);
143
144    // Only query the matrix once we have bound a valid frame.
145    if (memcmp(current_matrix_, mtx, sizeof(mtx)) != 0) {
146      memcpy(current_matrix_, mtx, sizeof(mtx));
147
148      GpuStreamTextureMsg_MatrixChanged_Params params;
149      memcpy(&params.m00, mtx, sizeof(mtx));
150      owner_stub_->channel()->Send(
151          new GpuStreamTextureMsg_MatrixChanged(route_id_, params));
152    }
153  }
154}
155
156void StreamTexture::OnFrameAvailable() {
157  has_pending_frame_ = true;
158  if (has_listener_ && owner_stub_) {
159    owner_stub_->channel()->Send(
160        new GpuStreamTextureMsg_FrameAvailable(route_id_));
161  }
162}
163
164gfx::Size StreamTexture::GetSize() {
165  return size_;
166}
167
168bool StreamTexture::OnMessageReceived(const IPC::Message& message) {
169  bool handled = true;
170  IPC_BEGIN_MESSAGE_MAP(StreamTexture, message)
171    IPC_MESSAGE_HANDLER(GpuStreamTextureMsg_StartListening, OnStartListening)
172    IPC_MESSAGE_HANDLER(GpuStreamTextureMsg_EstablishPeer, OnEstablishPeer)
173    IPC_MESSAGE_HANDLER(GpuStreamTextureMsg_SetSize, OnSetSize)
174    IPC_MESSAGE_UNHANDLED(handled = false)
175  IPC_END_MESSAGE_MAP()
176
177  DCHECK(handled);
178  return handled;
179}
180
181void StreamTexture::OnStartListening() {
182  DCHECK(!has_listener_);
183  has_listener_ = true;
184}
185
186void StreamTexture::OnEstablishPeer(int32 primary_id, int32 secondary_id) {
187  if (!owner_stub_)
188    return;
189
190  base::ProcessHandle process = owner_stub_->channel()->renderer_pid();
191
192  SurfaceTexturePeer::GetInstance()->EstablishSurfaceTexturePeer(
193      process, surface_texture_, primary_id, secondary_id);
194}
195
196bool StreamTexture::BindTexImage(unsigned target) {
197  NOTREACHED();
198  return false;
199}
200
201void StreamTexture::ReleaseTexImage(unsigned target) {
202  NOTREACHED();
203}
204
205bool StreamTexture::CopyTexImage(unsigned target) {
206  return false;
207}
208
209bool StreamTexture::ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
210                                         int z_order,
211                                         gfx::OverlayTransform transform,
212                                         const gfx::Rect& bounds_rect,
213                                         const gfx::RectF& crop_rect) {
214  NOTREACHED();
215  return false;
216}
217
218}  // namespace content
219