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
17 namespace gpu {
18 namespace gles2 {
19
20 namespace {
21
22 MailboxSynchronizer* g_instance = NULL;
23
24 } // anonymous namespace
25
26 // static
Initialize()27 bool 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
Terminate()57 void MailboxSynchronizer::Terminate() {
58 DCHECK(g_instance);
59 delete g_instance;
60 g_instance = NULL;
61 }
62
63 // static
GetInstance()64 MailboxSynchronizer* MailboxSynchronizer::GetInstance() {
65 return g_instance;
66 }
67
TargetName(unsigned target,const Mailbox & mailbox)68 MailboxSynchronizer::TargetName::TargetName(unsigned target,
69 const Mailbox& mailbox)
70 : target(target), mailbox(mailbox) {}
71
TextureGroup(const TextureDefinition & definition)72 MailboxSynchronizer::TextureGroup::TextureGroup(
73 const TextureDefinition& definition)
74 : definition(definition) {}
75
~TextureGroup()76 MailboxSynchronizer::TextureGroup::~TextureGroup() {}
77
TextureVersion(linked_ptr<TextureGroup> group)78 MailboxSynchronizer::TextureVersion::TextureVersion(
79 linked_ptr<TextureGroup> group)
80 : version(group->definition.version()), group(group) {}
81
~TextureVersion()82 MailboxSynchronizer::TextureVersion::~TextureVersion() {}
83
MailboxSynchronizer()84 MailboxSynchronizer::MailboxSynchronizer() {}
85
~MailboxSynchronizer()86 MailboxSynchronizer::~MailboxSynchronizer() {
87 DCHECK_EQ(0U, textures_.size());
88 }
89
ReassociateMailboxLocked(const TargetName & target_name,TextureGroup * group)90 void 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
106 linked_ptr<MailboxSynchronizer::TextureGroup>
GetGroupForMailboxLocked(const TargetName & target_name)107 MailboxSynchronizer::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
CreateTextureFromMailbox(unsigned target,const Mailbox & mailbox)119 Texture* 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
TextureDeleted(Texture * texture)134 void 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
PushTextureUpdates(MailboxManager * manager,uint32 sync_point)144 void 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
CreateFenceLocked(uint32 sync_point)192 void 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
UpdateTextureLocked(Texture * texture,TextureVersion & texture_version)217 void 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
AcquireFenceLocked(uint32 sync_point)244 void 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
PullTextureUpdates(MailboxManager * manager,uint32 sync_point)253 void 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