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