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 "gpu/command_buffer/service/texture_definition.h"
6
7#include <list>
8
9#include "base/memory/linked_ptr.h"
10#include "base/memory/scoped_ptr.h"
11#include "base/synchronization/lock.h"
12#include "gpu/command_buffer/service/texture_manager.h"
13#include "ui/gl/gl_image.h"
14#include "ui/gl/gl_implementation.h"
15#include "ui/gl/scoped_binders.h"
16
17#if !defined(OS_MACOSX)
18#include "ui/gl/gl_surface_egl.h"
19#endif
20
21namespace gpu {
22namespace gles2 {
23
24namespace {
25
26class GLImageSync : public gfx::GLImage {
27 public:
28  explicit GLImageSync(const scoped_refptr<NativeImageBuffer>& buffer,
29                       const gfx::Size& size);
30
31  // Implement GLImage.
32  virtual void Destroy(bool have_context) OVERRIDE;
33  virtual gfx::Size GetSize() OVERRIDE;
34  virtual bool BindTexImage(unsigned target) OVERRIDE;
35  virtual void ReleaseTexImage(unsigned target) OVERRIDE;
36  virtual bool CopyTexImage(unsigned target) OVERRIDE;
37  virtual void WillUseTexImage() OVERRIDE;
38  virtual void WillModifyTexImage() OVERRIDE;
39  virtual void DidModifyTexImage() OVERRIDE;
40  virtual void DidUseTexImage() OVERRIDE;
41  virtual bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
42                                    int z_order,
43                                    gfx::OverlayTransform transform,
44                                    const gfx::Rect& bounds_rect,
45                                    const gfx::RectF& crop_rect) OVERRIDE;
46  virtual void SetReleaseAfterUse() OVERRIDE;
47
48 protected:
49  virtual ~GLImageSync();
50
51 private:
52  scoped_refptr<NativeImageBuffer> buffer_;
53  gfx::Size size_;
54
55  DISALLOW_COPY_AND_ASSIGN(GLImageSync);
56};
57
58GLImageSync::GLImageSync(const scoped_refptr<NativeImageBuffer>& buffer,
59                         const gfx::Size& size)
60    : buffer_(buffer), size_(size) {
61  if (buffer.get())
62    buffer->AddClient(this);
63}
64
65GLImageSync::~GLImageSync() {
66  if (buffer_.get())
67    buffer_->RemoveClient(this);
68}
69
70void GLImageSync::Destroy(bool have_context) {
71}
72
73gfx::Size GLImageSync::GetSize() {
74  return size_;
75}
76
77bool GLImageSync::BindTexImage(unsigned target) {
78  NOTREACHED();
79  return false;
80}
81
82void GLImageSync::ReleaseTexImage(unsigned target) {
83  NOTREACHED();
84}
85
86bool GLImageSync::CopyTexImage(unsigned target) {
87  return false;
88}
89
90void GLImageSync::WillUseTexImage() {
91}
92
93void GLImageSync::DidUseTexImage() {
94}
95
96void GLImageSync::WillModifyTexImage() {
97}
98
99void GLImageSync::DidModifyTexImage() {
100}
101
102bool GLImageSync::ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
103                                       int z_order,
104                                       gfx::OverlayTransform transform,
105                                       const gfx::Rect& bounds_rect,
106                                       const gfx::RectF& crop_rect) {
107  NOTREACHED();
108  return false;
109}
110
111void GLImageSync::SetReleaseAfterUse() {
112  NOTREACHED();
113}
114
115#if !defined(OS_MACOSX)
116class NativeImageBufferEGL : public NativeImageBuffer {
117 public:
118  static scoped_refptr<NativeImageBufferEGL> Create(GLuint texture_id);
119
120 private:
121  NativeImageBufferEGL(EGLDisplay display, EGLImageKHR image);
122  virtual ~NativeImageBufferEGL();
123  virtual void AddClient(gfx::GLImage* client) OVERRIDE;
124  virtual void RemoveClient(gfx::GLImage* client) OVERRIDE;
125  virtual bool IsClient(gfx::GLImage* client) OVERRIDE;
126  virtual void BindToTexture(GLenum target) OVERRIDE;
127
128  EGLDisplay egl_display_;
129  EGLImageKHR egl_image_;
130
131  base::Lock lock_;
132
133  struct ClientInfo {
134    ClientInfo(gfx::GLImage* client);
135    ~ClientInfo();
136
137    gfx::GLImage* client;
138    bool needs_wait_before_read;
139  };
140  std::list<ClientInfo> client_infos_;
141  gfx::GLImage* write_client_;
142
143  DISALLOW_COPY_AND_ASSIGN(NativeImageBufferEGL);
144};
145
146scoped_refptr<NativeImageBufferEGL> NativeImageBufferEGL::Create(
147    GLuint texture_id) {
148  EGLDisplay egl_display = gfx::GLSurfaceEGL::GetHardwareDisplay();
149  EGLContext egl_context = eglGetCurrentContext();
150
151  DCHECK_NE(EGL_NO_CONTEXT, egl_context);
152  DCHECK_NE(EGL_NO_DISPLAY, egl_display);
153  DCHECK(glIsTexture(texture_id));
154
155  DCHECK(gfx::g_driver_egl.ext.b_EGL_KHR_image_base &&
156         gfx::g_driver_egl.ext.b_EGL_KHR_gl_texture_2D_image &&
157         gfx::g_driver_gl.ext.b_GL_OES_EGL_image);
158
159  const EGLint egl_attrib_list[] = {
160      EGL_GL_TEXTURE_LEVEL_KHR, 0, EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
161  EGLClientBuffer egl_buffer = reinterpret_cast<EGLClientBuffer>(texture_id);
162  EGLenum egl_target = EGL_GL_TEXTURE_2D_KHR; // TODO
163
164  EGLImageKHR egl_image = eglCreateImageKHR(
165      egl_display, egl_context, egl_target, egl_buffer, egl_attrib_list);
166
167  if (egl_image == EGL_NO_IMAGE_KHR)
168    return NULL;
169
170  return new NativeImageBufferEGL(egl_display, egl_image);
171}
172
173NativeImageBufferEGL::ClientInfo::ClientInfo(gfx::GLImage* client)
174    : client(client), needs_wait_before_read(true) {}
175
176NativeImageBufferEGL::ClientInfo::~ClientInfo() {}
177
178NativeImageBufferEGL::NativeImageBufferEGL(EGLDisplay display,
179                                           EGLImageKHR image)
180    : NativeImageBuffer(),
181      egl_display_(display),
182      egl_image_(image),
183      write_client_(NULL) {
184  DCHECK(egl_display_ != EGL_NO_DISPLAY);
185  DCHECK(egl_image_ != EGL_NO_IMAGE_KHR);
186}
187
188NativeImageBufferEGL::~NativeImageBufferEGL() {
189  DCHECK(client_infos_.empty());
190  if (egl_image_ != EGL_NO_IMAGE_KHR)
191    eglDestroyImageKHR(egl_display_, egl_image_);
192}
193
194void NativeImageBufferEGL::AddClient(gfx::GLImage* client) {
195  base::AutoLock lock(lock_);
196  client_infos_.push_back(ClientInfo(client));
197}
198
199void NativeImageBufferEGL::RemoveClient(gfx::GLImage* client) {
200  base::AutoLock lock(lock_);
201  if (write_client_ == client)
202    write_client_ = NULL;
203  for (std::list<ClientInfo>::iterator it = client_infos_.begin();
204       it != client_infos_.end();
205       it++) {
206    if (it->client == client) {
207      client_infos_.erase(it);
208      return;
209    }
210  }
211  NOTREACHED();
212}
213
214bool NativeImageBufferEGL::IsClient(gfx::GLImage* client) {
215  base::AutoLock lock(lock_);
216  for (std::list<ClientInfo>::iterator it = client_infos_.begin();
217       it != client_infos_.end();
218       it++) {
219    if (it->client == client)
220      return true;
221  }
222  return false;
223}
224
225void NativeImageBufferEGL::BindToTexture(GLenum target) {
226  DCHECK(egl_image_ != EGL_NO_IMAGE_KHR);
227  glEGLImageTargetTexture2DOES(target, egl_image_);
228  DCHECK_EQ(static_cast<EGLint>(EGL_SUCCESS), eglGetError());
229  DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
230}
231
232#endif
233
234class NativeImageBufferStub : public NativeImageBuffer {
235 public:
236  NativeImageBufferStub() : NativeImageBuffer() {}
237
238 private:
239  virtual ~NativeImageBufferStub() {}
240  virtual void AddClient(gfx::GLImage* client) OVERRIDE {}
241  virtual void RemoveClient(gfx::GLImage* client) OVERRIDE {}
242  virtual bool IsClient(gfx::GLImage* client) OVERRIDE { return true; }
243  virtual void BindToTexture(GLenum target) OVERRIDE {}
244
245  DISALLOW_COPY_AND_ASSIGN(NativeImageBufferStub);
246};
247
248}  // anonymous namespace
249
250// static
251scoped_refptr<NativeImageBuffer> NativeImageBuffer::Create(GLuint texture_id) {
252  switch (gfx::GetGLImplementation()) {
253#if !defined(OS_MACOSX)
254    case gfx::kGLImplementationEGLGLES2:
255      return NativeImageBufferEGL::Create(texture_id);
256#endif
257    case gfx::kGLImplementationMockGL:
258      return new NativeImageBufferStub;
259    default:
260      NOTREACHED();
261      return NULL;
262  }
263}
264
265TextureDefinition::LevelInfo::LevelInfo(GLenum target,
266                                        GLenum internal_format,
267                                        GLsizei width,
268                                        GLsizei height,
269                                        GLsizei depth,
270                                        GLint border,
271                                        GLenum format,
272                                        GLenum type,
273                                        bool cleared)
274    : target(target),
275      internal_format(internal_format),
276      width(width),
277      height(height),
278      depth(depth),
279      border(border),
280      format(format),
281      type(type),
282      cleared(cleared) {}
283
284TextureDefinition::LevelInfo::~LevelInfo() {}
285
286TextureDefinition::TextureDefinition(
287    GLenum target,
288    Texture* texture,
289    unsigned int version,
290    const scoped_refptr<NativeImageBuffer>& image_buffer)
291    : version_(version),
292      target_(target),
293      image_buffer_(image_buffer.get()
294                        ? image_buffer
295                        : NativeImageBuffer::Create(texture->service_id())),
296      min_filter_(texture->min_filter()),
297      mag_filter_(texture->mag_filter()),
298      wrap_s_(texture->wrap_s()),
299      wrap_t_(texture->wrap_t()),
300      usage_(texture->usage()),
301      immutable_(texture->IsImmutable()) {
302  // TODO
303  DCHECK(!texture->level_infos_.empty());
304  DCHECK(!texture->level_infos_[0].empty());
305  DCHECK(!texture->NeedsMips());
306  DCHECK(texture->level_infos_[0][0].width);
307  DCHECK(texture->level_infos_[0][0].height);
308
309  scoped_refptr<gfx::GLImage> gl_image(
310      new GLImageSync(image_buffer_,
311                      gfx::Size(texture->level_infos_[0][0].width,
312                                texture->level_infos_[0][0].height)));
313  texture->SetLevelImage(NULL, target, 0, gl_image.get());
314
315  // TODO: all levels
316  level_infos_.clear();
317  const Texture::LevelInfo& level = texture->level_infos_[0][0];
318  LevelInfo info(level.target,
319                 level.internal_format,
320                 level.width,
321                 level.height,
322                 level.depth,
323                 level.border,
324                 level.format,
325                 level.type,
326                 level.cleared);
327  std::vector<LevelInfo> infos;
328  infos.push_back(info);
329  level_infos_.push_back(infos);
330}
331
332TextureDefinition::~TextureDefinition() {
333}
334
335Texture* TextureDefinition::CreateTexture() const {
336  if (!image_buffer_.get())
337    return NULL;
338
339  GLuint texture_id;
340  glGenTextures(1, &texture_id);
341
342  Texture* texture(new Texture(texture_id));
343  UpdateTexture(texture);
344
345  return texture;
346}
347
348void TextureDefinition::UpdateTexture(Texture* texture) const {
349  gfx::ScopedTextureBinder texture_binder(target_, texture->service_id());
350  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter_);
351  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter_);
352  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s_);
353  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t_);
354  if (image_buffer_.get())
355    image_buffer_->BindToTexture(target_);
356  // We have to make sure the changes are visible to other clients in this share
357  // group. As far as the clients are concerned, the mailbox semantics only
358  // demand a single flush from the client after changes are first made,
359  // and it is not visible to them when another share group boundary is crossed.
360  // We could probably track this and be a bit smarter about when to flush
361  // though.
362  glFlush();
363
364  texture->level_infos_.resize(1);
365  for (size_t i = 0; i < level_infos_.size(); i++) {
366    const LevelInfo& base_info = level_infos_[i][0];
367    const size_t levels_needed = TextureManager::ComputeMipMapCount(
368        base_info.target, base_info.width, base_info.height, base_info.depth);
369    DCHECK(level_infos_.size() <= levels_needed);
370    texture->level_infos_[0].resize(levels_needed);
371    for (size_t n = 0; n < level_infos_.size(); n++) {
372      const LevelInfo& info = level_infos_[i][n];
373      texture->SetLevelInfo(NULL,
374                            info.target,
375                            i,
376                            info.internal_format,
377                            info.width,
378                            info.height,
379                            info.depth,
380                            info.border,
381                            info.format,
382                            info.type,
383                            info.cleared);
384    }
385  }
386  if (image_buffer_.get()) {
387    texture->SetLevelImage(
388        NULL,
389        target_,
390        0,
391        new GLImageSync(
392            image_buffer_,
393            gfx::Size(level_infos_[0][0].width, level_infos_[0][0].height)));
394  }
395
396  texture->target_ = target_;
397  texture->SetImmutable(immutable_);
398  texture->min_filter_ = min_filter_;
399  texture->mag_filter_ = mag_filter_;
400  texture->wrap_s_ = wrap_s_;
401  texture->wrap_t_ = wrap_t_;
402  texture->usage_ = usage_;
403}
404
405bool TextureDefinition::Matches(const Texture* texture) const {
406  DCHECK(target_ == texture->target());
407  if (texture->min_filter_ != min_filter_ ||
408      texture->mag_filter_ != mag_filter_ ||
409      texture->wrap_s_ != wrap_s_ ||
410      texture->wrap_t_ != wrap_t_) {
411    return false;
412  }
413
414  // All structural changes should have orphaned the texture.
415  if (image_buffer_.get() && !texture->GetLevelImage(texture->target(), 0))
416    return false;
417
418  return true;
419}
420
421}  // namespace gles2
422}  // namespace gpu
423