KiCad PCB EDA Suite
Loading...
Searching...
No Matches
render_thread.h
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#ifndef RHI_RENDER_THREAD_H
21#define RHI_RENDER_THREAD_H
22
25
28#include <rhi/rhi_glyph_atlas.h>
29#include <rhi/rhi_compositor.h>
30
31#include <core/sync_queue.h>
32
33#include <algorithm>
34#include <atomic>
35#include <chrono>
36#include <condition_variable>
37#include <deque>
38#include <functional>
39#include <future>
40#include <memory>
41#include <mutex>
42#include <optional>
43#include <thread>
44#include <unordered_map>
45#include <unordered_set>
46#include <vector>
47
48
49class RHI_DEVICE;
50class wxImage;
51
52
53namespace KIGFX
54{
55
59using RHI_DEVICE_FACTORY = std::function<std::unique_ptr<RHI_DEVICE>()>;
60
61
67{
68public:
69 explicit RENDER_THREAD( RHI_DEVICE_FACTORY aDeviceFactory );
70
72
73 RENDER_THREAD( const RENDER_THREAD& ) = delete;
75
81 bool Start();
82
83 bool IsRunning() const;
84
89 void SetGlyphAtlas( std::shared_ptr<const RHI_GLYPH_ATLAS_DATA> aAtlas );
90
95
100
106 bool WaitForFence( std::chrono::milliseconds aTimeout = std::chrono::milliseconds( 2000 ) );
107
111 void Shutdown();
112
116 RHI_BACKEND GetBackendType() const { return m_backendType.load( std::memory_order_acquire ); }
117
121 uint32_t GetMaxTextureSize() const { return m_maxTextureSize.load( std::memory_order_acquire ); }
122
129
134 bool ReadbackWindowPixels( RHI_WINDOW_ID aWindowId, wxImage& aImage );
135
140 int GetLoopPhase() const { return m_loopPhase.load( std::memory_order_relaxed ); }
141
145 bool IsDeviceLost() const { return m_deviceLost.load( std::memory_order_acquire ); }
146
151 bool HasPresented( RHI_WINDOW_ID aWindowId ) const;
152
156 void AddDeviceLostListener( const void* aOwner, std::function<void()> aListener );
157
161 void RemoveDeviceLostListener( const void* aOwner );
162
166 static void ComputeBackingResolution( uint32_t aLogicalW, uint32_t aLogicalH, float aDpiScale,
167 uint32_t& aBackingW, uint32_t& aBackingH );
168
173 void OnDeviceLost();
174
175private:
176 void ThreadMain( std::promise<bool> aStarted );
177 void DrainControlQueue();
178 void DrainUploadQueue();
179
180 void HandleFrameRequest( const RHI_CMD_FRAME_REQUEST& aCmd );
184 void HandleFence( const RHI_CMD_FENCE& aCmd );
185
189 void ProcessActiveWindows( bool aIgnorePacing = false );
190
191 bool EnsureSwapchain( WINDOW_STATE& aWS, const char* aReason );
192
197 void recreateSwapchainAfterOutOfDate( WINDOW_STATE& aWS, bool aSurfaceLost = false );
198
203 bool RenderWindow( WINDOW_STATE& aWS );
204 void RenderGeometry( WINDOW_STATE& aWS );
206 enum class GEOMETRY_RANGES : uint8_t
207 {
211 };
212
218 bool aLogSummary, GEOMETRY_RANGES aRanges = GEOMETRY_RANGES::ALL,
219 uint32_t aStaticDrawMargin = 0 );
225 bool prepareCursor( WINDOW_STATE& aWS );
226
232 bool runFenced( std::function<void()> aWork, std::chrono::milliseconds aTimeout );
233
240 const void* aData, size_t aSize );
241
243 void drawCursor( WINDOW_STATE& aWS );
244
245 void DestroyAllWindows();
246
248 void destroyWindow( WINDOW_STATE& aWS );
249
252
253 void CreateWindowFBOs( WINDOW_STATE& aWS );
254 void DestroyWindowFBOs( WINDOW_STATE& aWS );
256
257 void wakeRenderThread();
258
260
261 std::unique_ptr<RHI_DEVICE> m_device;
262
267 mutable std::mutex m_deviceMutex;
268
269 std::atomic<RHI_BACKEND> m_backendType{ RHI_BACKEND::NONE };
270 std::atomic<uint32_t> m_maxTextureSize{ 0 };
271
274
275 std::thread m_thread;
276
277 std::mutex m_workMutex;
278 std::condition_variable m_workCV;
279
280 std::atomic<bool> m_running{ false };
281 std::atomic<bool> m_shutdownRequested{ false };
282
284 bool m_acceptingWork = false;
285
287 std::atomic<bool> m_uploadsPending{ false };
288 std::atomic<bool> m_deviceLost{ false };
289
292 std::unordered_map<const void*, std::function<void()>> m_deviceLostListeners;
293
295 mutable std::mutex m_presentedMutex;
296 std::unordered_set<RHI_WINDOW_ID> m_presentedWindows;
297
298 std::atomic<int> m_loopPhase{ 0 };
299
300 std::unordered_map<RHI_WINDOW_ID, WINDOW_STATE> m_windows;
301
302 std::vector<WINDOW_STATE*> m_activeWindowOrder;
303
305 std::optional<WINDOW_STATE::Clock::time_point> m_deferredFrameDue;
306
307 void deferFrameUntil( WINDOW_STATE::Clock::time_point aDue )
308 {
309 m_deferredFrameDue = m_deferredFrameDue ? std::min( *m_deferredFrameDue, aDue ) : aDue;
310 }
311
313 std::unique_ptr<RHI_GAL_PIPELINES> m_pipelines;
314 std::unique_ptr<RHI_COMPOSITOR> m_compositor;
318
323 bool m_glyphBGHasAtlas = false;
324
325 mutable std::mutex m_glyphAtlasMutex;
326 std::shared_ptr<const RHI_GLYPH_ATLAS_DATA> m_glyphAtlas;
327
330
334
338};
339
340
341} // namespace KIGFX
342
343#endif // RHI_RENDER_THREAD_H
void CreateWindowFBOs(WINDOW_STATE &aWS)
RHI_BufferHandle m_unitQuadBuffer
RHI_BufferHandle m_quadIndexBuffer
its 2 triangles over those corners
std::mutex m_glyphAtlasMutex
std::unique_ptr< RHI_DEVICE > m_device
SYNC_QUEUE< RHI_UPLOAD_COMMAND > m_uploadQueue
bool prepareCursor(WINDOW_STATE &aWS)
Fill the cursor's uniforms.
std::unordered_set< RHI_WINDOW_ID > m_presentedWindows
bool WaitForFence(std::chrono::milliseconds aTimeout=std::chrono::milliseconds(2000))
Block until every command submitted before this call has been processed.
void ProcessActiveWindows(bool aIgnorePacing=false)
std::unordered_map< RHI_WINDOW_ID, WINDOW_STATE > m_windows
RHI_BufferHandle m_quadCornerBuffer
the unit quad's 4 corners
void ThreadMain(std::promise< bool > aStarted)
RHI_BACKEND GetBackendType() const
RHI_DEVICE_FACTORY m_deviceFactory
void deferFrameUntil(WINDOW_STATE::Clock::time_point aDue)
bool EnsureSwapchain(WINDOW_STATE &aWS, const char *aReason)
RHI_RenderPassHandle m_geometryRenderPass
void HandleSwapchainDestroy(const RHI_CMD_SWAPCHAIN_DESTROY &aCmd)
std::optional< WINDOW_STATE::Clock::time_point > m_deferredFrameDue
Earliest time a window held to its display's refresh rate is due, so the loop wakes for it.
RHI_RenderPassHandle m_geometryDepthLoadRenderPass
clears colour, keeps depth
void AddDeviceLostListener(const void *aOwner, std::function< void()> aListener)
Call aListener on the render thread when the device is lost.
bool runFenced(std::function< void()> aWork, std::chrono::milliseconds aTimeout)
Run work on the render thread after everything queued before it.
std::atomic< bool > m_uploadsPending
Set by producers and cleared by the drain so the loop can sleep without missing uploads.
bool m_acceptingWork
Guarded by m_workMutex.
void HandleSwapchainResize(const RHI_CMD_SWAPCHAIN_RESIZE &aCmd)
std::mutex m_presentedMutex
Window IDs are never reused, so an entry outliving its window is harmless.
std::atomic< bool > m_running
std::mutex m_deviceMutex
Guards the m_device pointer, not the device, against UI thread reads racing its creation or destructi...
void RemoveDeviceLostListener(const void *aOwner)
Once this returns, the owner's listener is not running and will not be called.
RHI_RenderPassHandle m_geometryLoadRenderPass
static void ComputeBackingResolution(uint32_t aLogicalW, uint32_t aLogicalH, float aDpiScale, uint32_t &aBackingW, uint32_t &aBackingH)
Compute the physical pixel backing size from logical size and DPI scale, at least 1x1.
uint32_t GetMaxTextureSize() const
RENDER_THREAD(const RENDER_THREAD &)=delete
void DestroyWindowUniforms(WINDOW_STATE &aWS)
bool IsDeviceLost() const
void SetGlyphAtlas(std::shared_ptr< const RHI_GLYPH_ATLAS_DATA > aAtlas)
Give the track shaders the font atlas glyph instances draw from.
void SubmitUpload(RHI_UPLOAD_COMMAND aCmd)
Callable from any thread.
RHI_TextureHandle m_glyphTexture
The font atlas the track shaders draw glyphs from, its glyph table and their bind group.
bool HasPresented(RHI_WINDOW_ID aWindowId) const
RHI_BufferHandle m_glyphTable
bool ReadbackWindowPixels(RHI_WINDOW_ID aWindowId, wxImage &aImage)
Read back a window's picture at backing resolution without the overlay and cursor.
std::atomic< uint32_t > m_maxTextureSize
std::atomic< bool > m_shutdownRequested
std::atomic< RHI_BACKEND > m_backendType
void HandleFence(const RHI_CMD_FENCE &aCmd)
void HandleSwapchainCreate(const RHI_CMD_SWAPCHAIN_CREATE &aCmd)
bool writeUniform(RHI_BufferHandle &aUBO, RHI_BindGroupHandle &aBG, RHI_BindGroupLayoutHandle aLayout, const void *aData, size_t aSize)
Write a uniform block, making its buffer and bind group on first use.
std::atomic< bool > m_deviceLost
RHI_BindGroupHandle m_glyphBG
RHI_BindGroupHandle glyphBindGroup()
RHI_BufferHandle ReserveBufferHandle()
Reserve a handle without creating the GPU resource, so RHI_DEVICE_PROXY can hand it out on the UI thr...
RHI_TextureHandle ReserveTextureHandle()
RHI_RenderPassHandle m_geometryDepthDiscardRenderPass
As m_geometryRenderPass and m_geometryDepthLoadRenderPass, without storing depth nothing reads later.
GEOMETRY_RANGES
The ranges of a draw descriptor one geometry pass draws.
RENDER_THREAD(RHI_DEVICE_FACTORY aDeviceFactory)
void OnDeviceLost()
Stop drawing on a lost device.
void HandleFrameRequest(const RHI_CMD_FRAME_REQUEST &aCmd)
std::mutex m_deviceLostMutex
Held while a listener runs, so removing one waits for it to return.
void Shutdown()
Block until the render thread exits.
std::unordered_map< const void *, std::function< void()> > m_deviceLostListeners
std::condition_variable m_workCV
void recreateSwapchainAfterOutOfDate(WINDOW_STATE &aWS, bool aSurfaceLost=false)
Retire and recreate an out of date swapchain, and its surface too when aSurfaceLost.
std::unique_ptr< RHI_GAL_PIPELINES > m_pipelines
Shared by all windows and created once with the device.
void RenderGeometryInto(WINDOW_STATE &aWS, const FRAME_DRAW_DESC &aDesc, RHI_FramebufferHandle aFBO, bool aLogSummary, GEOMETRY_RANGES aRanges=GEOMETRY_RANGES::ALL, uint32_t aStaticDrawMargin=0)
bool RenderWindow(WINDOW_STATE &aWS)
std::unique_ptr< RHI_COMPOSITOR > m_compositor
RHI_RenderPassHandle m_geometryDepthLoadDiscardRenderPass
SYNC_QUEUE< RHI_CONTROL_COMMAND > m_controlQueue
bool SubmitControl(RHI_CONTROL_COMMAND aCmd)
void drawCursor(WINDOW_STATE &aWS)
Draw the cursor into the open composite pass.
bool Start()
Block until the render thread has created the device and its pipelines.
std::atomic< int > m_loopPhase
see GetLoopPhase()
void RenderGeometry(WINDOW_STATE &aWS)
RENDER_THREAD & operator=(const RENDER_THREAD &)=delete
std::vector< WINDOW_STATE * > m_activeWindowOrder
std::shared_ptr< const RHI_GLYPH_ATLAS_DATA > m_glyphAtlas
void DestroyWindowFBOs(WINDOW_STATE &aWS)
void destroyWindow(WINDOW_STATE &aWS)
Free a window's targets, uniforms, swapchain and surface.
Creates, uses and destroys GPU resources through one graphics API backend.
Definition rhi_device.h:66
Synchronized, locking queue.
Definition sync_queue.h:32
The Cairo implementation of the graphics abstraction layer.
Definition eda_group.h:30
std::function< std::unique_ptr< RHI_DEVICE >()> RHI_DEVICE_FACTORY
Creates the RHI_DEVICE.
std::variant< RHI_CMD_FRAME_REQUEST, RHI_CMD_SWAPCHAIN_CREATE, RHI_CMD_SWAPCHAIN_RESIZE, RHI_CMD_SWAPCHAIN_DESTROY, RHI_CMD_FENCE > RHI_CONTROL_COMMAND
One entry in the render thread control queue.
uint32_t RHI_WINDOW_ID
Routes control commands to a swapchain.
RHI_HANDLE< RHI_TAG_FRAMEBUFFER > RHI_FramebufferHandle
Definition rhi_handle.h:93
RHI_HANDLE< RHI_TAG_TEXTURE > RHI_TextureHandle
Definition rhi_handle.h:84
RHI_HANDLE< RHI_TAG_BUFFER > RHI_BufferHandle
Definition rhi_handle.h:83
RHI_HANDLE< RHI_TAG_BIND_GROUP > RHI_BindGroupHandle
Definition rhi_handle.h:90
RHI_HANDLE< RHI_TAG_BIND_GROUP_LAYOUT > RHI_BindGroupLayoutHandle
Definition rhi_handle.h:89
RHI_HANDLE< RHI_TAG_RENDER_PASS > RHI_RenderPassHandle
Definition rhi_handle.h:88
RHI_BACKEND
Identifies which graphics backend a device implements.
Definition rhi_types.h:28
std::variant< RHI_GEOMETRY_UPLOAD, RHI_TEXTURE_UPLOAD, RHI_BUFFER_CREATE, RHI_TEXTURE_CREATE, RHI_BUFFER_DESTROY, RHI_TEXTURE_DESTROY > RHI_UPLOAD_COMMAND
One entry in the render thread upload queue.
GPU geometry of one frame, built by GAL_MODERN after EndDrawing() and passed through the FRAME_MAILBO...
Per-window state owned by the render thread.