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/mailbox_synchronizer.h"
6
7#include "base/bind.h"
8#include "gpu/command_buffer/service/mailbox_manager.h"
9#include "gpu/command_buffer/service/texture_manager.h"
10#include "ui/gl/gl_fence.h"
11#include "ui/gl/gl_implementation.h"
12
13#if !defined(OS_MACOSX)
14#include "ui/gl/gl_fence_egl.h"
15#endif
16
17namespace gpu {
18namespace gles2 {
19
20namespace {
21
22MailboxSynchronizer* g_instance = NULL;
23
24}  // anonymous namespace
25
26// static
27bool MailboxSynchronizer::Initialize() {
28  DCHECK(!g_instance);
29  DCHECK(gfx::GetGLImplementation() != gfx::kGLImplementationNone)
30      << "GL bindings not initialized";
31  switch (gfx::GetGLImplementation()) {
32    case gfx::kGLImplementationMockGL:
33      break;
34    case gfx::kGLImplementationEGLGLES2:
35#if !defined(OS_MACOSX)
36      {
37        if (!gfx::g_driver_egl.ext.b_EGL_KHR_image_base ||
38            !gfx::g_driver_egl.ext.b_EGL_KHR_gl_texture_2D_image ||
39            !gfx::g_driver_gl.ext.b_GL_OES_EGL_image ||
40            !gfx::g_driver_egl.ext.b_EGL_KHR_fence_sync) {
41          LOG(WARNING) << "MailboxSync not supported due to missing EGL "
42                          "image/fence support";
43          return false;
44        }
45      }
46      break;
47#endif
48    default:
49      NOTREACHED();
50      return false;
51  }
52  g_instance = new MailboxSynchronizer;
53  return true;
54}
55
56// static
57void MailboxSynchronizer::Terminate() {
58  DCHECK(g_instance);
59  delete g_instance;
60  g_instance = NULL;
61}
62
63// static
64MailboxSynchronizer* MailboxSynchronizer::GetInstance() {
65  return g_instance;
66}
67
68MailboxSynchronizer::TargetName::TargetName(unsigned target,
69                                            const Mailbox& mailbox)
70    : target(target), mailbox(mailbox) {}
71
72MailboxSynchronizer::TextureGroup::TextureGroup(
73    const TextureDefinition& definition)
74    : definition(definition) {}
75
76MailboxSynchronizer::TextureGroup::~TextureGroup() {}
77
78MailboxSynchronizer::TextureVersion::TextureVersion(
79    linked_ptr<TextureGroup> group)
80    : version(group->definition.version()), group(group) {}
81
82MailboxSynchronizer::TextureVersion::~TextureVersion() {}
83
84MailboxSynchronizer::MailboxSynchronizer() {}
85
86MailboxSynchronizer::~MailboxSynchronizer() {
87  DCHECK_EQ(0U, textures_.size());
88}
89
90void MailboxSynchronizer::ReassociateMailboxLocked(
91    const TargetName& target_name,
92    TextureGroup* group) {
93  lock_.AssertAcquired();
94  for (TextureMap::iterator it = textures_.begin(); it != textures_.end();
95       it++) {
96    std::set<TargetName>::iterator mb_it =
97        it->second.group->mailboxes.find(target_name);
98    if (it->second.group != group &&
99        mb_it != it->second.group->mailboxes.end()) {
100      it->second.group->mailboxes.erase(mb_it);
101    }
102  }
103  group->mailboxes.insert(target_name);
104}
105
106linked_ptr<MailboxSynchronizer::TextureGroup>
107MailboxSynchronizer::GetGroupForMailboxLocked(const TargetName& target_name) {
108  lock_.AssertAcquired();
109  for (TextureMap::iterator it = textures_.begin(); it != textures_.end();
110       it++) {
111    std::set<TargetName>::const_iterator mb_it =
112        it->second.group->mailboxes.find(target_name);
113    if (mb_it != it->second.group->mailboxes.end())
114      return it->second.group;
115  }
116  return make_linked_ptr<MailboxSynchronizer::TextureGroup>(NULL);
117}
118
119Texture* MailboxSynchronizer::CreateTextureFromMailbox(unsigned target,
120                                                       const Mailbox& mailbox) {
121  base::AutoLock lock(lock_);
122  TargetName target_name(target, mailbox);
123  linked_ptr<TextureGroup> group = GetGroupForMailboxLocked(target_name);
124  if (group.get()) {
125    Texture* new_texture = group->definition.CreateTexture();
126    if (new_texture)
127      textures_.insert(std::make_pair(new_texture, TextureVersion(group)));
128    return new_texture;
129  }
130
131  return NULL;
132}
133
134void MailboxSynchronizer::TextureDeleted(Texture* texture) {
135  base::AutoLock lock(lock_);
136  TextureMap::iterator it = textures_.find(texture);
137  if (it != textures_.end()) {
138    // TODO: We could avoid the update if this was the last ref.
139    UpdateTextureLocked(it->first, it->second);
140    textures_.erase(it);
141  }
142}
143
144void MailboxSynchronizer::PushTextureUpdates(MailboxManager* manager,
145                                             uint32 sync_point) {
146  base::AutoLock lock(lock_);
147  for (MailboxManager::MailboxToTextureMap::const_iterator texture_it =
148           manager->mailbox_to_textures_.begin();
149       texture_it != manager->mailbox_to_textures_.end();
150       texture_it++) {
151    TargetName target_name(texture_it->first.target, texture_it->first.mailbox);
152    Texture* texture = texture_it->second->first;
153    // TODO(sievers): crbug.com/352274
154    // Should probably only fail if it already *has* mipmaps, while allowing
155    // incomplete textures here. Also reconsider how to fail otherwise.
156    bool needs_mips = texture->min_filter() != GL_NEAREST &&
157                      texture->min_filter() != GL_LINEAR;
158    if (target_name.target != GL_TEXTURE_2D || needs_mips)
159      continue;
160
161    TextureMap::iterator it = textures_.find(texture);
162    if (it != textures_.end()) {
163      TextureVersion& texture_version = it->second;
164      TextureGroup* group = texture_version.group.get();
165      std::set<TargetName>::const_iterator mb_it =
166          group->mailboxes.find(target_name);
167      if (mb_it == group->mailboxes.end()) {
168        // We previously did not associate this texture with the given mailbox.
169        // Unlink other texture groups from the mailbox.
170        ReassociateMailboxLocked(target_name, group);
171      }
172      UpdateTextureLocked(texture, texture_version);
173
174    } else {
175      // Skip compositor resources/tile textures.
176      // TODO: Remove this, see crbug.com/399226.
177      if (texture->pool() == GL_TEXTURE_POOL_MANAGED_CHROMIUM)
178        continue;
179
180      linked_ptr<TextureGroup> group = make_linked_ptr(new TextureGroup(
181          TextureDefinition(target_name.target, texture, 1, NULL)));
182
183      // Unlink other textures from this mailbox in case the name is not new.
184      ReassociateMailboxLocked(target_name, group.get());
185      textures_.insert(std::make_pair(texture, TextureVersion(group)));
186    }
187  }
188
189  CreateFenceLocked(sync_point);
190}
191
192void MailboxSynchronizer::CreateFenceLocked(uint32 sync_point) {
193  lock_.AssertAcquired();
194  if (gfx::GetGLImplementation() == gfx::kGLImplementationMockGL)
195    return;
196
197#if !defined(OS_MACOSX)
198  if (sync_point) {
199    while (!sync_points_.empty() &&
200           sync_points_.front()->second->HasCompleted()) {
201      sync_point_to_fence_.erase(sync_points_.front());
202      sync_points_.pop();
203    }
204    // Need to use EGL fences since we are likely not in a single share group.
205    linked_ptr<gfx::GLFence> fence(make_linked_ptr(new gfx::GLFenceEGL(true)));
206    if (fence.get()) {
207      std::pair<SyncPointToFenceMap::iterator, bool> result =
208          sync_point_to_fence_.insert(std::make_pair(sync_point, fence));
209      DCHECK(result.second);
210      sync_points_.push(result.first);
211    }
212    DCHECK(sync_points_.size() == sync_point_to_fence_.size());
213  }
214#endif
215}
216
217void MailboxSynchronizer::UpdateTextureLocked(Texture* texture,
218                                              TextureVersion& texture_version) {
219  lock_.AssertAcquired();
220  gfx::GLImage* gl_image = texture->GetLevelImage(texture->target(), 0);
221  TextureGroup* group = texture_version.group.get();
222  scoped_refptr<NativeImageBuffer> image_buffer = group->definition.image();
223
224  // Make sure we don't clobber with an older version
225  if (!group->definition.IsOlderThan(texture_version.version))
226    return;
227
228  // Also don't push redundant updates. Note that it would break the
229  // versioning.
230  if (group->definition.Matches(texture))
231    return;
232
233  if (gl_image && !image_buffer->IsClient(gl_image)) {
234    LOG(ERROR) << "MailboxSync: Incompatible attachment";
235    return;
236  }
237
238  group->definition = TextureDefinition(texture->target(),
239                                        texture,
240                                        ++texture_version.version,
241                                        gl_image ? image_buffer : NULL);
242}
243
244void MailboxSynchronizer::AcquireFenceLocked(uint32 sync_point) {
245  lock_.AssertAcquired();
246  SyncPointToFenceMap::iterator fence_it =
247      sync_point_to_fence_.find(sync_point);
248  if (fence_it != sync_point_to_fence_.end()) {
249    fence_it->second->ServerWait();
250  }
251}
252
253void MailboxSynchronizer::PullTextureUpdates(MailboxManager* manager,
254                                             uint32 sync_point) {
255  base::AutoLock lock(lock_);
256  AcquireFenceLocked(sync_point);
257
258  for (MailboxManager::MailboxToTextureMap::const_iterator texture_it =
259           manager->mailbox_to_textures_.begin();
260       texture_it != manager->mailbox_to_textures_.end();
261       texture_it++) {
262    Texture* texture = texture_it->second->first;
263    TextureMap::iterator it = textures_.find(texture);
264    if (it != textures_.end()) {
265      TextureDefinition& definition = it->second.group->definition;
266      if (it->second.version == definition.version() ||
267          definition.IsOlderThan(it->second.version))
268        continue;
269      it->second.version = definition.version();
270      definition.UpdateTexture(texture);
271    }
272  }
273}
274
275}  // namespace gles2
276}  // namespace gpu
277