Skip to content

Commit 12a2aeb

Browse files
LibWeb: Move painting surface allocation into rendering thread
Skia has a check in debug mode to verify that surface is only used within one thread. Before this change we were violating this by allocating surfaces on the main thread while using and destructing them on the rendering thread.
1 parent 3169747 commit 12a2aeb

File tree

6 files changed

+73
-52
lines changed

6 files changed

+73
-52
lines changed

Libraries/LibGfx/SkiaBackendContext.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
#pragma once
88

9+
#include <AK/AtomicRefCounted.h>
910
#include <AK/Noncopyable.h>
10-
#include <AK/RefCounted.h>
1111
#include <LibThreading/Mutex.h>
1212

1313
#ifdef USE_VULKAN
@@ -25,7 +25,7 @@ namespace Gfx {
2525

2626
class MetalContext;
2727

28-
class SkiaBackendContext : public RefCounted<SkiaBackendContext> {
28+
class SkiaBackendContext : public AtomicRefCounted<SkiaBackendContext> {
2929
AK_MAKE_NONCOPYABLE(SkiaBackendContext);
3030
AK_MAKE_NONMOVABLE(SkiaBackendContext);
3131

Libraries/LibWeb/HTML/RenderingThread.cpp

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
#include <LibCore/EventLoop.h>
88
#include <LibWeb/HTML/RenderingThread.h>
9+
#include <LibWeb/HTML/TraversableNavigable.h>
10+
#include <LibWeb/Painting/BackingStore.h>
911

1012
namespace Web::HTML {
1113

@@ -21,8 +23,9 @@ RenderingThread::~RenderingThread()
2123
(void)m_thread->join();
2224
}
2325

24-
void RenderingThread::start()
26+
void RenderingThread::start(DisplayListPlayerType display_list_player_type)
2527
{
28+
m_display_list_player_type = display_list_player_type;
2629
VERIFY(m_skia_player);
2730
m_thread = Threading::Thread::construct([this] {
2831
rendering_thread_loop();
@@ -36,6 +39,10 @@ void RenderingThread::rendering_thread_loop()
3639
while (true) {
3740
auto task = [this]() -> Optional<Task> {
3841
Threading::MutexLocker const locker { m_rendering_task_mutex };
42+
if (m_needs_to_clear_bitmap_to_surface_cache) {
43+
m_bitmap_to_surface.clear();
44+
m_needs_to_clear_bitmap_to_surface_cache = false;
45+
}
3946
while (m_rendering_tasks.is_empty() && !m_exit) {
4047
m_rendering_task_ready_wake_condition.wait();
4148
}
@@ -49,18 +56,56 @@ void RenderingThread::rendering_thread_loop()
4956
break;
5057
}
5158

52-
m_skia_player->execute(*task->display_list, task->painting_surface);
59+
auto painting_surface = painting_surface_for_backing_store(task->backing_store);
60+
m_skia_player->execute(*task->display_list, painting_surface);
5361
m_main_thread_event_loop.deferred_invoke([callback = move(task->callback)] {
5462
callback();
5563
});
5664
}
5765
}
5866

59-
void RenderingThread::enqueue_rendering_task(NonnullRefPtr<Painting::DisplayList> display_list, NonnullRefPtr<Gfx::PaintingSurface> painting_surface, Function<void()>&& callback)
67+
void RenderingThread::enqueue_rendering_task(NonnullRefPtr<Painting::DisplayList> display_list, NonnullRefPtr<Painting::BackingStore> backing_store, Function<void()>&& callback)
6068
{
6169
Threading::MutexLocker const locker { m_rendering_task_mutex };
62-
m_rendering_tasks.enqueue(Task { move(display_list), move(painting_surface), move(callback) });
70+
m_rendering_tasks.enqueue(Task { move(display_list), move(backing_store), move(callback) });
6371
m_rendering_task_ready_wake_condition.signal();
6472
}
6573

74+
NonnullRefPtr<Gfx::PaintingSurface> RenderingThread::painting_surface_for_backing_store(Painting::BackingStore& backing_store)
75+
{
76+
auto& bitmap = backing_store.bitmap();
77+
auto cached_surface = m_bitmap_to_surface.find(&bitmap);
78+
if (cached_surface != m_bitmap_to_surface.end())
79+
return cached_surface->value;
80+
81+
RefPtr<Gfx::PaintingSurface> new_surface;
82+
if (m_display_list_player_type == DisplayListPlayerType::SkiaGPUIfAvailable && m_skia_backend_context) {
83+
#ifdef USE_VULKAN
84+
// Vulkan: Try to create an accelerated surface.
85+
new_surface = Gfx::PaintingSurface::create_with_size(m_skia_backend_context, backing_store.size(), Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied);
86+
new_surface->on_flush = [backing_store = static_cast<NonnullRefPtr<Painting::BackingStore>>(backing_store)](auto& surface) { surface.read_into_bitmap(backing_store->bitmap()); };
87+
#endif
88+
#ifdef AK_OS_MACOS
89+
// macOS: Wrap an IOSurface if available.
90+
if (is<Painting::IOSurfaceBackingStore>(backing_store)) {
91+
auto& iosurface_backing_store = static_cast<Painting::IOSurfaceBackingStore&>(backing_store);
92+
new_surface = Gfx::PaintingSurface::wrap_iosurface(iosurface_backing_store.iosurface_handle(), *m_skia_backend_context);
93+
}
94+
#endif
95+
}
96+
97+
// CPU and fallback: wrap the backing store bitmap directly.
98+
if (!new_surface)
99+
new_surface = Gfx::PaintingSurface::wrap_bitmap(bitmap);
100+
101+
m_bitmap_to_surface.set(&bitmap, *new_surface);
102+
return *new_surface;
103+
}
104+
105+
void RenderingThread::clear_bitmap_to_surface_cache()
106+
{
107+
Threading::MutexLocker const locker { m_rendering_task_mutex };
108+
m_needs_to_clear_bitmap_to_surface_cache = true;
109+
}
110+
66111
}

Libraries/LibWeb/HTML/RenderingThread.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#include <LibThreading/ConditionVariable.h>
1212
#include <LibThreading/Mutex.h>
1313
#include <LibThreading/Thread.h>
14+
#include <LibWeb/Forward.h>
15+
#include <LibWeb/Page/Page.h>
1416
#include <LibWeb/Painting/DisplayListPlayerSkia.h>
1517

1618
namespace Web::HTML {
@@ -23,30 +25,38 @@ class RenderingThread {
2325
RenderingThread();
2426
~RenderingThread();
2527

26-
void start();
28+
void start(DisplayListPlayerType);
2729
void set_skia_player(OwnPtr<Painting::DisplayListPlayerSkia>&& player) { m_skia_player = move(player); }
28-
void enqueue_rendering_task(NonnullRefPtr<Painting::DisplayList>, NonnullRefPtr<Gfx::PaintingSurface>, Function<void()>&& callback);
30+
void set_skia_backend_context(RefPtr<Gfx::SkiaBackendContext> context) { m_skia_backend_context = move(context); }
31+
void enqueue_rendering_task(NonnullRefPtr<Painting::DisplayList>, NonnullRefPtr<Painting::BackingStore>, Function<void()>&& callback);
32+
void clear_bitmap_to_surface_cache();
2933

3034
private:
3135
void rendering_thread_loop();
36+
NonnullRefPtr<Gfx::PaintingSurface> painting_surface_for_backing_store(Painting::BackingStore& backing_store);
3237

3338
Core::EventLoop& m_main_thread_event_loop;
39+
DisplayListPlayerType m_display_list_player_type;
3440

3541
OwnPtr<Painting::DisplayListPlayerSkia> m_skia_player;
42+
RefPtr<Gfx::SkiaBackendContext> m_skia_backend_context;
3643

3744
RefPtr<Threading::Thread> m_thread;
3845
Atomic<bool> m_exit { false };
3946

4047
struct Task {
4148
NonnullRefPtr<Painting::DisplayList> display_list;
42-
NonnullRefPtr<Gfx::PaintingSurface> painting_surface;
49+
NonnullRefPtr<Painting::BackingStore> backing_store;
4350
Function<void()> callback;
4451
};
4552
// NOTE: Queue will only contain multiple items in case tasks were scheduled by screenshot requests.
4653
// Otherwise, it will contain only one item at a time.
4754
Queue<Task> m_rendering_tasks;
4855
Threading::Mutex m_rendering_task_mutex;
4956
Threading::ConditionVariable m_rendering_task_ready_wake_condition { m_rendering_task_mutex };
57+
58+
HashMap<Gfx::Bitmap*, NonnullRefPtr<Gfx::PaintingSurface>> m_bitmap_to_surface;
59+
bool m_needs_to_clear_bitmap_to_surface_cache { false };
5060
};
5161

5262
}

Libraries/LibWeb/HTML/TraversableNavigable.cpp

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ TraversableNavigable::TraversableNavigable(GC::Ref<Page> page)
6262
}
6363

6464
m_rendering_thread.set_skia_player(move(skia_player));
65-
m_rendering_thread.start();
65+
m_rendering_thread.set_skia_backend_context(m_skia_backend_context);
66+
m_rendering_thread.start(display_list_player_type);
6667
}
6768

6869
TraversableNavigable::~TraversableNavigable() = default;
@@ -1399,38 +1400,7 @@ void TraversableNavigable::set_viewport_size(CSSPixelSize size)
13991400
Navigable::set_viewport_size(size);
14001401

14011402
// Invalidate the surface cache if the traversable changed size.
1402-
m_bitmap_to_surface.clear();
1403-
}
1404-
1405-
NonnullRefPtr<Gfx::PaintingSurface> TraversableNavigable::painting_surface_for_backing_store(Painting::BackingStore& backing_store)
1406-
{
1407-
auto& bitmap = backing_store.bitmap();
1408-
auto cached_surface = m_bitmap_to_surface.find(&bitmap);
1409-
if (cached_surface != m_bitmap_to_surface.end())
1410-
return cached_surface->value;
1411-
1412-
RefPtr<Gfx::PaintingSurface> new_surface;
1413-
if (page().client().display_list_player_type() == DisplayListPlayerType::SkiaGPUIfAvailable && m_skia_backend_context) {
1414-
#ifdef USE_VULKAN
1415-
// Vulkan: Try to create an accelerated surface.
1416-
new_surface = Gfx::PaintingSurface::create_with_size(m_skia_backend_context, backing_store.size(), Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied);
1417-
new_surface->on_flush = [backing_store = static_cast<NonnullRefPtr<Painting::BackingStore>>(backing_store)](auto& surface) { surface.read_into_bitmap(backing_store->bitmap()); };
1418-
#endif
1419-
#ifdef AK_OS_MACOS
1420-
// macOS: Wrap an IOSurface if available.
1421-
if (is<Painting::IOSurfaceBackingStore>(backing_store)) {
1422-
auto& iosurface_backing_store = static_cast<Painting::IOSurfaceBackingStore&>(backing_store);
1423-
new_surface = Gfx::PaintingSurface::wrap_iosurface(iosurface_backing_store.iosurface_handle(), *m_skia_backend_context);
1424-
}
1425-
#endif
1426-
}
1427-
1428-
// CPU and fallback: wrap the backing store bitmap directly.
1429-
if (!new_surface)
1430-
new_surface = Gfx::PaintingSurface::wrap_bitmap(bitmap);
1431-
1432-
m_bitmap_to_surface.set(&bitmap, *new_surface);
1433-
return *new_surface;
1403+
m_rendering_thread.clear_bitmap_to_surface_cache();
14341404
}
14351405

14361406
RefPtr<Painting::DisplayList> TraversableNavigable::record_display_list(DevicePixelRect const& content_rect, PaintOptions paint_options)
@@ -1454,9 +1424,9 @@ RefPtr<Painting::DisplayList> TraversableNavigable::record_display_list(DevicePi
14541424
return document->record_display_list(paint_config);
14551425
}
14561426

1457-
void TraversableNavigable::start_display_list_rendering(NonnullRefPtr<Painting::DisplayList> display_list, NonnullRefPtr<Gfx::PaintingSurface> painting_surface, Function<void()>&& callback)
1427+
void TraversableNavigable::start_display_list_rendering(NonnullRefPtr<Painting::DisplayList> display_list, NonnullRefPtr<Painting::BackingStore> backing_store, Function<void()>&& callback)
14581428
{
1459-
m_rendering_thread.enqueue_rendering_task(move(display_list), move(painting_surface), move(callback));
1429+
m_rendering_thread.enqueue_rendering_task(move(display_list), move(backing_store), move(callback));
14601430
}
14611431

14621432
}

Libraries/LibWeb/HTML/TraversableNavigable.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ class TraversableNavigable final : public Navigable {
9898
[[nodiscard]] GC::Ptr<DOM::Node> currently_focused_area();
9999

100100
RefPtr<Painting::DisplayList> record_display_list(DevicePixelRect const&, PaintOptions);
101-
void start_display_list_rendering(NonnullRefPtr<Painting::DisplayList> display_list, NonnullRefPtr<Gfx::PaintingSurface> painting_surface, Function<void()>&& callback);
101+
void start_display_list_rendering(NonnullRefPtr<Painting::DisplayList>, NonnullRefPtr<Painting::BackingStore>, Function<void()>&& callback);
102102

103103
enum class CheckIfUnloadingIsCanceledResult {
104104
CanceledByBeforeUnload,
@@ -117,8 +117,6 @@ class TraversableNavigable final : public Navigable {
117117
bool needs_repaint() const { return m_needs_repaint; }
118118
void set_needs_repaint() { m_needs_repaint = true; }
119119

120-
NonnullRefPtr<Gfx::PaintingSurface> painting_surface_for_backing_store(Painting::BackingStore&);
121-
122120
private:
123121
TraversableNavigable(GC::Ref<Page>);
124122

@@ -140,6 +138,8 @@ class TraversableNavigable final : public Navigable {
140138

141139
[[nodiscard]] bool can_go_forward() const;
142140

141+
RenderingThread m_rendering_thread;
142+
143143
// https://html.spec.whatwg.org/multipage/document-sequences.html#tn-current-session-history-step
144144
int m_current_session_history_step { 0 };
145145

@@ -163,11 +163,8 @@ class TraversableNavigable final : public Navigable {
163163
String m_window_handle;
164164

165165
RefPtr<Gfx::SkiaBackendContext> m_skia_backend_context;
166-
HashMap<Gfx::Bitmap*, NonnullRefPtr<Gfx::PaintingSurface>> m_bitmap_to_surface;
167166

168167
bool m_needs_repaint { true };
169-
170-
RenderingThread m_rendering_thread;
171168
};
172169

173170
struct BrowsingContextAndDocument {

Services/WebContent/PageClient.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,7 @@ void PageClient::start_display_list_rendering(Web::DevicePixelRect const& conten
246246
callback();
247247
return;
248248
}
249-
auto painting_surface = traversable.painting_surface_for_backing_store(target);
250-
traversable.start_display_list_rendering(*display_list, painting_surface, move(callback));
249+
traversable.start_display_list_rendering(*display_list, target, move(callback));
251250
}
252251

253252
Queue<Web::QueuedInputEvent>& PageClient::input_event_queue()

0 commit comments

Comments
 (0)