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.
5 #include "gpu/command_buffer/service/mailbox_manager_sync.h"
10 #include "base/memory/linked_ptr.h"
11 #include "base/synchronization/lock.h"
12 #include "gpu/command_buffer/service/texture_manager.h"
13 #include "ui/gl/gl_fence.h"
14 #include "ui/gl/gl_implementation.h"
16 #if !defined(OS_MACOSX)
17 #include "ui/gl/gl_fence_egl.h"
25 base::LazyInstance
<base::Lock
> g_lock
= LAZY_INSTANCE_INITIALIZER
;
27 typedef std::map
<uint32
, linked_ptr
<gfx::GLFence
>> SyncPointToFenceMap
;
28 base::LazyInstance
<SyncPointToFenceMap
> g_sync_point_to_fence
=
29 LAZY_INSTANCE_INITIALIZER
;
30 #if !defined(OS_MACOSX)
31 base::LazyInstance
<std::queue
<SyncPointToFenceMap::iterator
>> g_sync_points
=
32 LAZY_INSTANCE_INITIALIZER
;
35 void CreateFenceLocked(uint32 sync_point
) {
36 g_lock
.Get().AssertAcquired();
37 if (gfx::GetGLImplementation() == gfx::kGLImplementationMockGL
)
40 #if !defined(OS_MACOSX)
41 std::queue
<SyncPointToFenceMap::iterator
>& sync_points
= g_sync_points
.Get();
42 SyncPointToFenceMap
& sync_point_to_fence
= g_sync_point_to_fence
.Get();
44 while (!sync_points
.empty() &&
45 sync_points
.front()->second
->HasCompleted()) {
46 sync_point_to_fence
.erase(sync_points
.front());
49 // Need to use EGL fences since we are likely not in a single share group.
50 linked_ptr
<gfx::GLFence
> fence(make_linked_ptr(new gfx::GLFenceEGL
));
52 std::pair
<SyncPointToFenceMap::iterator
, bool> result
=
53 sync_point_to_fence
.insert(std::make_pair(sync_point
, fence
));
54 DCHECK(result
.second
);
55 sync_points
.push(result
.first
);
57 DCHECK(sync_points
.size() == sync_point_to_fence
.size());
62 void AcquireFenceLocked(uint32 sync_point
) {
63 g_lock
.Get().AssertAcquired();
64 SyncPointToFenceMap::iterator fence_it
=
65 g_sync_point_to_fence
.Get().find(sync_point
);
66 if (fence_it
!= g_sync_point_to_fence
.Get().end()) {
67 fence_it
->second
->ServerWait();
71 static const unsigned kNewTextureVersion
= 1;
73 } // anonymous namespace
75 base::LazyInstance
<MailboxManagerSync::TextureGroup::MailboxToGroupMap
>
76 MailboxManagerSync::TextureGroup::mailbox_to_group_
=
77 LAZY_INSTANCE_INITIALIZER
;
80 MailboxManagerSync::TextureGroup
* MailboxManagerSync::TextureGroup::FromName(
81 const Mailbox
& name
) {
82 MailboxToGroupMap::iterator it
= mailbox_to_group_
.Get().find(name
);
83 if (it
== mailbox_to_group_
.Get().end())
86 return it
->second
.get();
89 MailboxManagerSync::TextureGroup::TextureGroup(
90 const TextureDefinition
& definition
)
91 : definition_(definition
) {
94 MailboxManagerSync::TextureGroup::~TextureGroup() {
97 void MailboxManagerSync::TextureGroup::AddName(const Mailbox
& name
) {
98 g_lock
.Get().AssertAcquired();
99 DCHECK(std::find(names_
.begin(), names_
.end(), name
) == names_
.end());
100 names_
.push_back(name
);
101 DCHECK(mailbox_to_group_
.Get().find(name
) == mailbox_to_group_
.Get().end());
102 mailbox_to_group_
.Get()[name
] = this;
105 void MailboxManagerSync::TextureGroup::RemoveName(const Mailbox
& name
) {
106 g_lock
.Get().AssertAcquired();
107 std::vector
<Mailbox
>::iterator names_it
=
108 std::find(names_
.begin(), names_
.end(), name
);
109 DCHECK(names_it
!= names_
.end());
110 names_
.erase(names_it
);
111 MailboxToGroupMap::iterator it
= mailbox_to_group_
.Get().find(name
);
112 DCHECK(it
!= mailbox_to_group_
.Get().end());
113 mailbox_to_group_
.Get().erase(it
);
116 void MailboxManagerSync::TextureGroup::AddTexture(MailboxManagerSync
* manager
,
118 g_lock
.Get().AssertAcquired();
119 DCHECK(std::find(textures_
.begin(), textures_
.end(),
120 std::make_pair(manager
, texture
)) == textures_
.end());
121 textures_
.push_back(std::make_pair(manager
, texture
));
124 bool MailboxManagerSync::TextureGroup::RemoveTexture(
125 MailboxManagerSync
* manager
,
127 g_lock
.Get().AssertAcquired();
128 TextureGroup::TextureList::iterator tex_list_it
= std::find(
129 textures_
.begin(), textures_
.end(), std::make_pair(manager
, texture
));
130 DCHECK(tex_list_it
!= textures_
.end());
131 if (textures_
.size() == 1) {
132 // This is the last texture so the group is going away.
133 for (size_t n
= 0; n
< names_
.size(); n
++) {
134 const Mailbox
& name
= names_
[n
];
135 MailboxToGroupMap::iterator mbox_it
=
136 mailbox_to_group_
.Get().find(name
);
137 DCHECK(mbox_it
!= mailbox_to_group_
.Get().end());
138 DCHECK(mbox_it
->second
.get() == this);
139 mailbox_to_group_
.Get().erase(mbox_it
);
143 textures_
.erase(tex_list_it
);
148 Texture
* MailboxManagerSync::TextureGroup::FindTexture(
149 MailboxManagerSync
* manager
) {
150 g_lock
.Get().AssertAcquired();
151 for (TextureGroup::TextureList::iterator it
= textures_
.begin();
152 it
!= textures_
.end(); it
++) {
153 if (it
->first
== manager
)
159 MailboxManagerSync::TextureGroupRef::TextureGroupRef(unsigned version
,
161 : version(version
), group(group
) {
164 MailboxManagerSync::TextureGroupRef::~TextureGroupRef() {
167 MailboxManagerSync::MailboxManagerSync() {
170 MailboxManagerSync::~MailboxManagerSync() {
171 DCHECK_EQ(0U, texture_to_group_
.size());
175 bool MailboxManagerSync::SkipTextureWorkarounds(const Texture
* texture
) {
176 // Cannot support mips due to support mismatch between
177 // EGL_KHR_gl_texture_2D_image and glEGLImageTargetTexture2DOES for
179 bool has_mips
= texture
->NeedsMips() && texture
->texture_complete();
180 return texture
->target() != GL_TEXTURE_2D
|| has_mips
;
183 bool MailboxManagerSync::UsesSync() {
187 Texture
* MailboxManagerSync::ConsumeTexture(const Mailbox
& mailbox
) {
188 base::AutoLock
lock(g_lock
.Get());
189 TextureGroup
* group
= TextureGroup::FromName(mailbox
);
193 // Check if a texture already exists in this share group.
194 Texture
* texture
= group
->FindTexture(this);
198 // Otherwise create a new texture.
199 texture
= group
->GetDefinition().CreateTexture();
201 DCHECK(!SkipTextureWorkarounds(texture
));
202 texture
->SetMailboxManager(this);
203 group
->AddTexture(this, texture
);
205 TextureGroupRef new_ref
=
206 TextureGroupRef(group
->GetDefinition().version(), group
);
207 texture_to_group_
.insert(std::make_pair(texture
, new_ref
));
213 void MailboxManagerSync::ProduceTexture(const Mailbox
& mailbox
,
215 base::AutoLock
lock(g_lock
.Get());
217 TextureToGroupMap::iterator tex_it
= texture_to_group_
.find(texture
);
218 TextureGroup
* group_for_mailbox
= TextureGroup::FromName(mailbox
);
219 TextureGroup
* group_for_texture
= NULL
;
221 if (tex_it
!= texture_to_group_
.end()) {
222 group_for_texture
= tex_it
->second
.group
.get();
223 DCHECK(group_for_texture
);
224 if (group_for_mailbox
== group_for_texture
) {
225 // The texture is already known under this name.
230 if (group_for_mailbox
) {
231 // Unlink the mailbox from its current group.
232 group_for_mailbox
->RemoveName(mailbox
);
235 if (group_for_texture
) {
236 group_for_texture
->AddName(mailbox
);
238 // This is a new texture, so create a new group.
239 texture
->SetMailboxManager(this);
240 TextureDefinition definition
;
241 if (!SkipTextureWorkarounds(texture
)) {
242 base::AutoUnlock
unlock(g_lock
.Get());
243 definition
= TextureDefinition(texture
, kNewTextureVersion
, NULL
);
245 group_for_texture
= new TextureGroup(definition
);
246 group_for_texture
->AddTexture(this, texture
);
247 group_for_texture
->AddName(mailbox
);
248 texture_to_group_
.insert(std::make_pair(
249 texture
, TextureGroupRef(kNewTextureVersion
, group_for_texture
)));
252 DCHECK(texture
->mailbox_manager_
== this);
255 void MailboxManagerSync::TextureDeleted(Texture
* texture
) {
256 base::AutoLock
lock(g_lock
.Get());
257 TextureToGroupMap::iterator tex_it
= texture_to_group_
.find(texture
);
258 DCHECK(tex_it
!= texture_to_group_
.end());
259 TextureGroup
* group_for_texture
= tex_it
->second
.group
.get();
260 if (group_for_texture
->RemoveTexture(this, texture
))
261 UpdateDefinitionLocked(texture
, &tex_it
->second
);
262 texture_to_group_
.erase(tex_it
);
265 void MailboxManagerSync::UpdateDefinitionLocked(
267 TextureGroupRef
* group_ref
) {
268 g_lock
.Get().AssertAcquired();
270 if (SkipTextureWorkarounds(texture
))
273 gfx::GLImage
* gl_image
= texture
->GetLevelImage(texture
->target(), 0);
274 TextureGroup
* group
= group_ref
->group
.get();
275 const TextureDefinition
& definition
= group
->GetDefinition();
276 scoped_refptr
<NativeImageBuffer
> image_buffer
= definition
.image();
278 // Make sure we don't clobber with an older version
279 if (!definition
.IsOlderThan(group_ref
->version
))
282 // Also don't push redundant updates. Note that it would break the
284 if (definition
.Matches(texture
))
287 DCHECK_IMPLIES(gl_image
, image_buffer
.get());
288 if (gl_image
&& !image_buffer
->IsClient(gl_image
)) {
289 LOG(ERROR
) << "MailboxSync: Incompatible attachment";
293 group
->SetDefinition(TextureDefinition(texture
, ++group_ref
->version
,
294 gl_image
? image_buffer
: NULL
));
297 void MailboxManagerSync::PushTextureUpdates(uint32 sync_point
) {
298 base::AutoLock
lock(g_lock
.Get());
300 for (TextureToGroupMap::iterator it
= texture_to_group_
.begin();
301 it
!= texture_to_group_
.end(); it
++) {
302 UpdateDefinitionLocked(it
->first
, &it
->second
);
304 CreateFenceLocked(sync_point
);
307 void MailboxManagerSync::PullTextureUpdates(uint32 sync_point
) {
308 using TextureUpdatePair
= std::pair
<Texture
*, TextureDefinition
>;
309 std::vector
<TextureUpdatePair
> needs_update
;
311 base::AutoLock
lock(g_lock
.Get());
312 AcquireFenceLocked(sync_point
);
314 for (TextureToGroupMap::iterator it
= texture_to_group_
.begin();
315 it
!= texture_to_group_
.end(); it
++) {
316 const TextureDefinition
& definition
= it
->second
.group
->GetDefinition();
317 Texture
* texture
= it
->first
;
318 unsigned& texture_version
= it
->second
.version
;
319 if (texture_version
== definition
.version() ||
320 definition
.IsOlderThan(texture_version
))
322 texture_version
= definition
.version();
323 needs_update
.push_back(TextureUpdatePair(texture
, definition
));
327 if (!needs_update
.empty()) {
328 for (const TextureUpdatePair
& pair
: needs_update
) {
329 pair
.second
.UpdateTexture(pair
.first
);