1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "TextureSync.h"
9 #include <unordered_set>
11 #include "base/process_util.h"
12 #include "chrome/common/mach_ipc_mac.h"
13 #include "mozilla/ipc/SharedMemoryBasic.h"
14 #include "mozilla/layers/CompositorThread.h"
15 #include "mozilla/StaticMonitor.h"
16 #include "mozilla/StaticPtr.h"
19 # define LOG_ERROR(str, args...) \
21 mozilla::SmprintfPointer msg = mozilla::Smprintf(str, ##args); \
22 NS_WARNING(msg.get()); \
25 # define LOG_ERROR(str, args...) \
34 // Hold raw pointers and trust that TextureSourceProviders will be
35 // unregistered in their destructors - we don't want to keep these
36 // alive, and destroying them from the main thread will be an
38 StaticAutoPtr
<nsTArray
<TextureSourceProvider
*>> gTextureSourceProviders
;
40 static std::map
<pid_t
, std::unordered_set
<uint64_t>> gProcessTextureIds
;
41 static StaticMonitor gTextureLockMonitor
;
43 const int kSendMessageTimeout
= 1000;
44 const int kTextureLockTimeout
= 32; // We really don't want to wait more than
45 // two frames for a texture to unlock. This
46 // will in any case be very uncommon.
48 struct WaitForTexturesReply
{
52 struct WaitForTexturesRequest
{
56 static std::unordered_set
<uint64_t>* GetLockedTextureIdsForProcess(pid_t pid
) {
57 gTextureLockMonitor
.AssertCurrentThreadOwns();
59 if (gProcessTextureIds
.find(pid
) == gProcessTextureIds
.end()) {
60 gProcessTextureIds
[pid
] = std::unordered_set
<uint64_t>();
63 return &gProcessTextureIds
.at(pid
);
66 static bool WaitForTextureIdsToUnlock(pid_t pid
,
67 const Span
<const uint64_t>& textureIds
) {
69 StaticMonitorAutoLock
lock(gTextureLockMonitor
);
70 std::unordered_set
<uint64_t>* freedTextureIds
=
71 GetLockedTextureIdsForProcess(pid
);
73 TimeStamp start
= TimeStamp::Now();
75 bool allCleared
= true;
76 for (uint64_t textureId
: textureIds
) {
77 if (freedTextureIds
->find(textureId
) != freedTextureIds
->end()) {
86 if (lock
.Wait(TimeDuration::FromMilliseconds(kTextureLockTimeout
)) ==
91 // In case the monitor gets signaled multiple times, each less than
92 // kTextureLockTimeout. This ensures that the total time we wait is
93 // < 2 * kTextureLockTimeout
94 if ((TimeStamp::Now() - start
).ToMilliseconds() >
95 (double)kTextureLockTimeout
) {
102 static void CheckTexturesForUnlock() {
103 if (gTextureSourceProviders
) {
104 for (auto it
= gTextureSourceProviders
->begin();
105 it
!= gTextureSourceProviders
->end(); ++it
) {
106 (*it
)->TryUnlockTextures();
111 void TextureSync::DispatchCheckTexturesForUnlock() {
112 RefPtr
<Runnable
> task
=
113 NS_NewRunnableFunction("CheckTexturesForUnlock", &CheckTexturesForUnlock
);
114 CompositorThreadHolder::Loop()->PostTask(task
.forget());
117 void TextureSync::HandleWaitForTexturesMessage(MachReceiveMessage
* rmsg
,
118 ipc::MemoryPorts
* ports
) {
119 WaitForTexturesRequest
* req
=
120 reinterpret_cast<WaitForTexturesRequest
*>(rmsg
->GetData());
121 uint64_t* textureIds
= (uint64_t*)(req
+ 1);
122 uint32_t textureIdsLength
=
123 (rmsg
->GetDataLength() - sizeof(WaitForTexturesRequest
)) /
126 bool success
= WaitForTextureIdsToUnlock(
127 req
->pid
, MakeSpan
<uint64_t>(textureIds
, textureIdsLength
));
130 LOG_ERROR("Waiting for textures to unlock failed.\n");
133 MachSendMessage
msg(ipc::kReturnWaitForTexturesMsg
);
134 WaitForTexturesReply replydata
;
135 replydata
.success
= success
;
136 msg
.SetData(&replydata
, sizeof(WaitForTexturesReply
));
137 kern_return_t err
= ports
->mSender
->SendMessage(msg
, kSendMessageTimeout
);
138 if (KERN_SUCCESS
!= err
) {
139 LOG_ERROR("SendMessage failed 0x%x %s\n", err
, mach_error_string(err
));
143 void TextureSync::RegisterTextureSourceProvider(
144 TextureSourceProvider
* textureSourceProvider
) {
145 if (!gTextureSourceProviders
) {
146 gTextureSourceProviders
= new nsTArray
<TextureSourceProvider
*>();
148 MOZ_RELEASE_ASSERT(!gTextureSourceProviders
->Contains(textureSourceProvider
));
149 gTextureSourceProviders
->AppendElement(textureSourceProvider
);
152 void TextureSync::UnregisterTextureSourceProvider(
153 TextureSourceProvider
* textureSourceProvider
) {
154 if (gTextureSourceProviders
) {
155 MOZ_ASSERT(gTextureSourceProviders
->Contains(textureSourceProvider
));
156 gTextureSourceProviders
->RemoveElement(textureSourceProvider
);
157 if (gTextureSourceProviders
->Length() == 0) {
158 gTextureSourceProviders
= nullptr;
163 void TextureSync::SetTexturesLocked(pid_t pid
,
164 const nsTArray
<uint64_t>& textureIds
) {
165 StaticMonitorAutoLock
mal(gTextureLockMonitor
);
166 std::unordered_set
<uint64_t>* lockedTextureIds
=
167 GetLockedTextureIdsForProcess(pid
);
168 for (uint64_t textureId
: textureIds
) {
169 lockedTextureIds
->insert(textureId
);
173 void TextureSync::SetTexturesUnlocked(pid_t pid
,
174 const nsTArray
<uint64_t>& textureIds
) {
175 bool oneErased
= false;
177 StaticMonitorAutoLock
mal(gTextureLockMonitor
);
178 std::unordered_set
<uint64_t>* lockedTextureIds
=
179 GetLockedTextureIdsForProcess(pid
);
180 for (uint64_t textureId
: textureIds
) {
181 if (lockedTextureIds
->erase(textureId
)) {
187 gTextureLockMonitor
.NotifyAll();
191 void TextureSync::Shutdown() {
193 StaticMonitorAutoLock
lock(gTextureLockMonitor
);
195 for (auto& lockedTextureIds
: gProcessTextureIds
) {
196 lockedTextureIds
.second
.clear();
200 gTextureLockMonitor
.NotifyAll();
203 StaticMonitorAutoLock
lock(gTextureLockMonitor
);
204 gProcessTextureIds
.clear();
208 void TextureSync::UpdateTextureLocks(base::ProcessId aProcessId
) {
209 if (aProcessId
== base::GetCurrentProcId()) {
210 DispatchCheckTexturesForUnlock();
214 MachSendMessage
smsg(ipc::kUpdateTextureLocksMsg
);
215 smsg
.SetData(&aProcessId
, sizeof(aProcessId
));
216 ipc::SharedMemoryBasic::SendMachMessage(aProcessId
, smsg
, NULL
);
219 bool TextureSync::WaitForTextures(base::ProcessId aProcessId
,
220 const nsTArray
<uint64_t>& textureIds
) {
221 if (aProcessId
== base::GetCurrentProcId()) {
223 WaitForTextureIdsToUnlock(aProcessId
, MakeSpan
<uint64_t>(textureIds
));
225 LOG_ERROR("Failed waiting for textures to unlock.\n");
231 MachSendMessage
smsg(ipc::kWaitForTexturesMsg
);
233 sizeof(WaitForTexturesRequest
) + textureIds
.Length() * sizeof(uint64_t);
234 UniquePtr
<uint8_t[]> messageData
= MakeUnique
<uint8_t[]>(messageSize
);
235 WaitForTexturesRequest
* req
= (WaitForTexturesRequest
*)messageData
.get();
236 uint64_t* reqTextureIds
= (uint64_t*)(req
+ 1);
238 for (uint32_t i
= 0; i
< textureIds
.Length(); ++i
) {
239 reqTextureIds
[i
] = textureIds
[i
];
242 req
->pid
= base::GetCurrentProcId();
243 bool dataWasSet
= smsg
.SetData(req
, messageSize
);
246 LOG_ERROR("Data was too large: %zu\n", messageSize
);
250 MachReceiveMessage msg
;
252 ipc::SharedMemoryBasic::SendMachMessage(aProcessId
, smsg
, &msg
);
257 if (msg
.GetDataLength() != sizeof(WaitForTexturesReply
)) {
258 LOG_ERROR("Improperly formatted reply\n");
262 WaitForTexturesReply
* msg_data
=
263 reinterpret_cast<WaitForTexturesReply
*>(msg
.GetData());
264 if (!msg_data
->success
) {
265 LOG_ERROR("Failed waiting for textures to unlock.\n");
272 void TextureSync::CleanupForPid(base::ProcessId aProcessId
) {
274 StaticMonitorAutoLock
lock(gTextureLockMonitor
);
275 std::unordered_set
<uint64_t>* lockedTextureIds
=
276 GetLockedTextureIdsForProcess(aProcessId
);
277 lockedTextureIds
->clear();
279 gTextureLockMonitor
.NotifyAll();
282 } // namespace layers
284 } // namespace mozilla