Source code

Revision control

Other Tools

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/. */
6
7
#include "TextureSync.h"
8
9
#include <unordered_set>
10
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"
17
18
#ifdef DEBUG
19
# define LOG_ERROR(str, args...) \
20
PR_BEGIN_MACRO \
21
mozilla::SmprintfPointer msg = mozilla::Smprintf(str, ##args); \
22
NS_WARNING(msg.get()); \
23
PR_END_MACRO
24
#else
25
# define LOG_ERROR(str, args...) \
26
do { /* nothing */ \
27
} while (0)
28
#endif
29
30
namespace mozilla {
31
32
namespace layers {
33
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
37
// error anyway.
38
StaticAutoPtr<nsTArray<TextureSourceProvider*>> gTextureSourceProviders;
39
40
static std::map<pid_t, std::unordered_set<uint64_t>> gProcessTextureIds;
41
static StaticMonitor gTextureLockMonitor;
42
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.
47
48
struct WaitForTexturesReply {
49
bool success;
50
};
51
52
struct WaitForTexturesRequest {
53
pid_t pid;
54
};
55
56
static std::unordered_set<uint64_t>* GetLockedTextureIdsForProcess(pid_t pid) {
57
gTextureLockMonitor.AssertCurrentThreadOwns();
58
59
if (gProcessTextureIds.find(pid) == gProcessTextureIds.end()) {
60
gProcessTextureIds[pid] = std::unordered_set<uint64_t>();
61
}
62
63
return &gProcessTextureIds.at(pid);
64
}
65
66
static bool WaitForTextureIdsToUnlock(pid_t pid,
67
const Span<const uint64_t>& textureIds) {
68
{
69
StaticMonitorAutoLock lock(gTextureLockMonitor);
70
std::unordered_set<uint64_t>* freedTextureIds =
71
GetLockedTextureIdsForProcess(pid);
72
73
TimeStamp start = TimeStamp::Now();
74
while (true) {
75
bool allCleared = true;
76
for (uint64_t textureId : textureIds) {
77
if (freedTextureIds->find(textureId) != freedTextureIds->end()) {
78
allCleared = false;
79
}
80
}
81
82
if (allCleared) {
83
return true;
84
}
85
86
if (lock.Wait(TimeDuration::FromMilliseconds(kTextureLockTimeout)) ==
87
CVStatus::Timeout) {
88
return false;
89
}
90
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) {
96
return false;
97
}
98
}
99
}
100
}
101
102
static void CheckTexturesForUnlock() {
103
if (gTextureSourceProviders) {
104
for (auto it = gTextureSourceProviders->begin();
105
it != gTextureSourceProviders->end(); ++it) {
106
(*it)->TryUnlockTextures();
107
}
108
}
109
}
110
111
void TextureSync::DispatchCheckTexturesForUnlock() {
112
RefPtr<Runnable> task =
113
NS_NewRunnableFunction("CheckTexturesForUnlock", &CheckTexturesForUnlock);
114
CompositorThreadHolder::Loop()->PostTask(task.forget());
115
}
116
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)) /
124
sizeof(uint64_t);
125
126
bool success = WaitForTextureIdsToUnlock(
127
req->pid, MakeSpan<uint64_t>(textureIds, textureIdsLength));
128
129
if (!success) {
130
LOG_ERROR("Waiting for textures to unlock failed.\n");
131
}
132
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));
140
}
141
}
142
143
void TextureSync::RegisterTextureSourceProvider(
144
TextureSourceProvider* textureSourceProvider) {
145
if (!gTextureSourceProviders) {
146
gTextureSourceProviders = new nsTArray<TextureSourceProvider*>();
147
}
148
MOZ_RELEASE_ASSERT(!gTextureSourceProviders->Contains(textureSourceProvider));
149
gTextureSourceProviders->AppendElement(textureSourceProvider);
150
}
151
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;
159
}
160
}
161
}
162
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);
170
}
171
}
172
173
void TextureSync::SetTexturesUnlocked(pid_t pid,
174
const nsTArray<uint64_t>& textureIds) {
175
bool oneErased = false;
176
{
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)) {
182
oneErased = true;
183
}
184
}
185
}
186
if (oneErased) {
187
gTextureLockMonitor.NotifyAll();
188
}
189
}
190
191
void TextureSync::Shutdown() {
192
{
193
StaticMonitorAutoLock lock(gTextureLockMonitor);
194
195
for (auto& lockedTextureIds : gProcessTextureIds) {
196
lockedTextureIds.second.clear();
197
}
198
}
199
200
gTextureLockMonitor.NotifyAll();
201
202
{
203
StaticMonitorAutoLock lock(gTextureLockMonitor);
204
gProcessTextureIds.clear();
205
}
206
}
207
208
void TextureSync::UpdateTextureLocks(base::ProcessId aProcessId) {
209
if (aProcessId == base::GetCurrentProcId()) {
210
DispatchCheckTexturesForUnlock();
211
return;
212
}
213
214
MachSendMessage smsg(ipc::kUpdateTextureLocksMsg);
215
smsg.SetData(&aProcessId, sizeof(aProcessId));
216
ipc::SharedMemoryBasic::SendMachMessage(aProcessId, smsg, NULL);
217
}
218
219
bool TextureSync::WaitForTextures(base::ProcessId aProcessId,
220
const nsTArray<uint64_t>& textureIds) {
221
if (aProcessId == base::GetCurrentProcId()) {
222
bool success =
223
WaitForTextureIdsToUnlock(aProcessId, MakeSpan<uint64_t>(textureIds));
224
if (!success) {
225
LOG_ERROR("Failed waiting for textures to unlock.\n");
226
}
227
228
return success;
229
}
230
231
MachSendMessage smsg(ipc::kWaitForTexturesMsg);
232
size_t messageSize =
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);
237
238
for (uint32_t i = 0; i < textureIds.Length(); ++i) {
239
reqTextureIds[i] = textureIds[i];
240
}
241
242
req->pid = base::GetCurrentProcId();
243
bool dataWasSet = smsg.SetData(req, messageSize);
244
245
if (!dataWasSet) {
246
LOG_ERROR("Data was too large: %zu\n", messageSize);
247
return false;
248
}
249
250
MachReceiveMessage msg;
251
bool success =
252
ipc::SharedMemoryBasic::SendMachMessage(aProcessId, smsg, &msg);
253
if (!success) {
254
return false;
255
}
256
257
if (msg.GetDataLength() != sizeof(WaitForTexturesReply)) {
258
LOG_ERROR("Improperly formatted reply\n");
259
return false;
260
}
261
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");
266
return false;
267
}
268
269
return true;
270
}
271
272
void TextureSync::CleanupForPid(base::ProcessId aProcessId) {
273
{
274
StaticMonitorAutoLock lock(gTextureLockMonitor);
275
std::unordered_set<uint64_t>* lockedTextureIds =
276
GetLockedTextureIdsForProcess(aProcessId);
277
lockedTextureIds->clear();
278
}
279
gTextureLockMonitor.NotifyAll();
280
}
281
282
} // namespace layers
283
284
} // namespace mozilla