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 "PaintThread.h"
8
9
#include <algorithm>
10
11
#include "base/task.h"
12
#include "gfxPlatform.h"
13
#include "GeckoProfiler.h"
14
#include "mozilla/layers/CompositorBridgeChild.h"
15
#include "mozilla/layers/ShadowLayers.h"
16
#include "mozilla/layers/SyncObject.h"
17
#include "mozilla/gfx/2D.h"
18
#include "mozilla/Preferences.h"
19
#include "mozilla/StaticPrefs_layers.h"
20
#include "mozilla/SharedThreadPool.h"
21
#include "mozilla/SyncRunnable.h"
22
#ifdef XP_MACOSX
23
# include "nsCocoaFeatures.h"
24
#endif
25
#include "nsIThreadManager.h"
26
#include "nsServiceManagerUtils.h"
27
#include "prsystem.h"
28
29
// Uncomment the following line to dispatch sync runnables when
30
// painting so that rasterization happens synchronously from
31
// the perspective of the main thread
32
// #define OMTP_FORCE_SYNC
33
34
namespace mozilla {
35
namespace layers {
36
37
using namespace gfx;
38
39
void PaintTask::DropTextureClients() { mClients.Clear(); }
40
41
StaticAutoPtr<PaintThread> PaintThread::sSingleton;
42
StaticRefPtr<nsIThread> PaintThread::sThread;
43
PlatformThreadId PaintThread::sThreadId;
44
45
PaintThread::PaintThread() = default;
46
47
void PaintThread::Release() {}
48
49
void PaintThread::AddRef() {}
50
51
/* static */
52
int32_t PaintThread::CalculatePaintWorkerCount() {
53
int32_t cpuCores = PR_GetNumberOfProcessors();
54
int32_t workerCount = StaticPrefs::layers_omtp_paint_workers_AtStartup();
55
56
// If not manually specified, default to (cpuCores * 3) / 4, and clamp
57
// between 1 and 4. If a user wants more, they can manually specify it
58
if (workerCount < 1) {
59
workerCount = std::min(std::max((cpuCores * 3) / 4, 1), 4);
60
}
61
62
return workerCount;
63
}
64
65
/* static */
66
void PaintThread::Start() {
67
PaintThread::sSingleton = new PaintThread();
68
69
if (!PaintThread::sSingleton->Init()) {
70
gfxCriticalNote << "Unable to start paint thread";
71
PaintThread::sSingleton = nullptr;
72
}
73
}
74
75
static uint32_t GetPaintThreadStackSize() {
76
#ifndef XP_MACOSX
77
return nsIThreadManager::DEFAULT_STACK_SIZE;
78
#else
79
// Workaround bug 1578075 by increasing the stack size of paint threads
80
if (nsCocoaFeatures::OnCatalinaOrLater()) {
81
static const uint32_t kCatalinaPaintThreadStackSize = 512 * 1024;
82
static_assert(
83
kCatalinaPaintThreadStackSize >= nsIThreadManager::DEFAULT_STACK_SIZE,
84
"update default stack size of paint "
85
"workers");
86
return kCatalinaPaintThreadStackSize;
87
}
88
return nsIThreadManager::DEFAULT_STACK_SIZE;
89
#endif
90
}
91
92
bool PaintThread::Init() {
93
MOZ_ASSERT(NS_IsMainThread());
94
95
RefPtr<nsIThread> thread;
96
nsresult rv = NS_NewNamedThread("PaintThread", getter_AddRefs(thread),
97
nullptr, GetPaintThreadStackSize());
98
if (NS_FAILED(rv)) {
99
return false;
100
}
101
sThread = thread;
102
103
// Only create paint workers for tiling if we are using tiling or could
104
// expect to dynamically switch to tiling in the future
105
if (gfxPlatform::GetPlatform()->UsesTiling()) {
106
InitPaintWorkers();
107
}
108
109
nsCOMPtr<nsIRunnable> paintInitTask = NewRunnableMethod(
110
"PaintThread::InitOnPaintThread", this, &PaintThread::InitOnPaintThread);
111
SyncRunnable::DispatchToThread(sThread, paintInitTask);
112
return true;
113
}
114
115
void PaintThread::InitOnPaintThread() {
116
MOZ_ASSERT(!NS_IsMainThread());
117
sThreadId = PlatformThread::CurrentId();
118
}
119
120
void PaintThread::InitPaintWorkers() {
121
MOZ_ASSERT(NS_IsMainThread());
122
int32_t count = PaintThread::CalculatePaintWorkerCount();
123
if (count != 1) {
124
mPaintWorkers =
125
SharedThreadPool::Get(NS_LITERAL_CSTRING("PaintWorker"), count);
126
mPaintWorkers->SetThreadStackSize(GetPaintThreadStackSize());
127
}
128
}
129
130
void DestroyPaintThread(UniquePtr<PaintThread>&& pt) {
131
MOZ_ASSERT(PaintThread::IsOnPaintThread());
132
pt->ShutdownOnPaintThread();
133
}
134
135
/* static */
136
void PaintThread::Shutdown() {
137
MOZ_ASSERT(NS_IsMainThread());
138
139
UniquePtr<PaintThread> pt(sSingleton.forget());
140
if (!pt) {
141
return;
142
}
143
144
sThread->Dispatch(NewRunnableFunction("DestroyPaintThreadRunnable",
145
DestroyPaintThread, std::move(pt)));
146
sThread->Shutdown();
147
sThread = nullptr;
148
}
149
150
void PaintThread::ShutdownOnPaintThread() { MOZ_ASSERT(IsOnPaintThread()); }
151
152
/* static */
153
PaintThread* PaintThread::Get() { return PaintThread::sSingleton.get(); }
154
155
/* static */
156
bool PaintThread::IsOnPaintThread() {
157
return sThreadId == PlatformThread::CurrentId();
158
}
159
160
bool PaintThread::IsOnPaintWorkerThread() {
161
return (mPaintWorkers && mPaintWorkers->IsOnCurrentThread()) ||
162
(sThreadId == PlatformThread::CurrentId());
163
}
164
165
void PaintThread::Dispatch(RefPtr<Runnable>& aRunnable) {
166
#ifndef OMTP_FORCE_SYNC
167
sThread->Dispatch(aRunnable.forget());
168
#else
169
SyncRunnable::DispatchToThread(sThread, aRunnable);
170
#endif
171
}
172
173
void PaintThread::UpdateRenderMode() {
174
if (!!mPaintWorkers != gfxPlatform::GetPlatform()->UsesTiling()) {
175
if (mPaintWorkers) {
176
mPaintWorkers = nullptr;
177
} else {
178
InitPaintWorkers();
179
}
180
}
181
}
182
183
void PaintThread::QueuePaintTask(UniquePtr<PaintTask>&& aTask) {
184
MOZ_ASSERT(NS_IsMainThread());
185
MOZ_ASSERT(aTask);
186
187
if (StaticPrefs::layers_omtp_dump_capture() && aTask->mCapture) {
188
aTask->mCapture->Dump();
189
}
190
191
MOZ_RELEASE_ASSERT(aTask->mCapture->hasOneRef());
192
193
RefPtr<CompositorBridgeChild> cbc(CompositorBridgeChild::Get());
194
cbc->NotifyBeginAsyncPaint(aTask.get());
195
196
RefPtr<PaintThread> self = this;
197
RefPtr<Runnable> task =
198
NS_NewRunnableFunction("PaintThread::AsyncPaintTask",
199
[self, cbc, task = std::move(aTask)]() -> void {
200
self->AsyncPaintTask(cbc, task.get());
201
});
202
203
nsIEventTarget* paintThread =
204
mPaintWorkers ? static_cast<nsIEventTarget*>(mPaintWorkers.get())
205
: static_cast<nsIEventTarget*>(sThread.get());
206
207
#ifndef OMTP_FORCE_SYNC
208
paintThread->Dispatch(task.forget());
209
#else
210
SyncRunnable::DispatchToThread(paintThread, task);
211
#endif
212
}
213
214
void PaintThread::AsyncPaintTask(CompositorBridgeChild* aBridge,
215
PaintTask* aTask) {
216
AUTO_PROFILER_LABEL("PaintThread::AsyncPaintTask", GRAPHICS);
217
218
MOZ_ASSERT(IsOnPaintWorkerThread());
219
MOZ_ASSERT(aTask);
220
221
gfx::DrawTargetCapture* capture = aTask->mCapture;
222
gfx::DrawTarget* target = aTask->mTarget;
223
224
if (target->IsValid()) {
225
// Do not replay to invalid targets. This can happen on device resets and
226
// the browser will ensure the graphics stack is reinitialized on the main
227
// thread.
228
target->DrawCapturedDT(capture, Matrix());
229
target->Flush();
230
}
231
232
if (StaticPrefs::layers_omtp_release_capture_on_main_thread()) {
233
// This should ensure the capture drawtarget, which may hold on to
234
// UnscaledFont objects, gets destroyed on the main thread (See bug
235
// 1404742). This assumes (unflushed) target DrawTargets do not themselves
236
// hold on to UnscaledFonts.
237
NS_ReleaseOnMainThreadSystemGroup("PaintTask::DrawTargetCapture",
238
aTask->mCapture.forget());
239
}
240
241
if (aBridge->NotifyFinishedAsyncWorkerPaint(aTask)) {
242
AsyncEndLayerTransaction(aBridge);
243
}
244
}
245
246
void PaintThread::QueueEndLayerTransaction(SyncObjectClient* aSyncObject) {
247
MOZ_ASSERT(NS_IsMainThread());
248
249
RefPtr<CompositorBridgeChild> cbc(CompositorBridgeChild::Get());
250
251
if (cbc->NotifyBeginAsyncEndLayerTransaction(aSyncObject)) {
252
RefPtr<PaintThread> self = this;
253
RefPtr<Runnable> task = NS_NewRunnableFunction(
254
"PaintThread::AsyncEndLayerTransaction",
255
[self, cbc]() -> void { self->AsyncEndLayerTransaction(cbc); });
256
257
#ifndef OMTP_FORCE_SYNC
258
sThread->Dispatch(task.forget());
259
#else
260
SyncRunnable::DispatchToThread(sThread, task);
261
#endif
262
}
263
}
264
265
void PaintThread::AsyncEndLayerTransaction(CompositorBridgeChild* aBridge) {
266
MOZ_ASSERT(IsOnPaintWorkerThread());
267
268
aBridge->NotifyFinishedAsyncEndLayerTransaction();
269
}
270
271
} // namespace layers
272
} // namespace mozilla