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