1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt3D module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "commandthread_p.h" |
41 | #include <Qt3DRender/private/glcommands_p.h> |
42 | #include <Qt3DRender/private/offscreensurfacehelper_p.h> |
43 | #include <Qt3DRender/private/graphicscontext_p.h> |
44 | #include <Qt3DRender/private/shadercache_p.h> |
45 | #include <QOpenGLContext> |
46 | #include <QOffscreenSurface> |
47 | #include <QDebug> |
48 | |
49 | QT_BEGIN_NAMESPACE |
50 | |
51 | namespace Qt3DRender { |
52 | |
53 | namespace Render { |
54 | |
55 | CommandThread::CommandThread(Renderer *renderer) |
56 | : QThread() |
57 | , m_renderer(renderer) |
58 | , m_waitForStartSemaphore(0) |
59 | , m_initializedSemaphore(0) |
60 | , m_commandRequestedSemaphore(0) |
61 | , m_commandExecutionSemaphore(0) |
62 | , m_mainContext(nullptr) |
63 | , m_shaderCache(nullptr) |
64 | , m_offsreenSurfaceHelper(nullptr) |
65 | , m_currentCommand(nullptr) |
66 | , m_running(0) |
67 | { |
68 | } |
69 | |
70 | CommandThread::~CommandThread() |
71 | { |
72 | Q_ASSERT(!isRunning()); |
73 | } |
74 | |
75 | void CommandThread::setShaderCache(ShaderCache *shaderCache) |
76 | { |
77 | m_shaderCache = shaderCache; |
78 | } |
79 | |
80 | // Called by RenderThread or MainThread (Scene3d) |
81 | void CommandThread::initialize(QOpenGLContext *mainContext, OffscreenSurfaceHelper *offsreenSurfaceHelper) |
82 | { |
83 | // Start the thread |
84 | start(); |
85 | |
86 | // Wait for thread to be started |
87 | m_waitForStartSemaphore.acquire(); |
88 | |
89 | m_mainContext = mainContext; |
90 | m_offsreenSurfaceHelper = offsreenSurfaceHelper; |
91 | Q_ASSERT(m_mainContext && offsreenSurfaceHelper); |
92 | |
93 | // Initialize shared context and resources for the thread. This must be |
94 | // done here since some platforms do not allow context sharing to be set up |
95 | // with contexts created on different threads. (Windows with WGL, in |
96 | // particular; resource sharing works fine later on, what matters is the |
97 | // thread the wglShareLists call is made on) |
98 | m_localContext.reset(new QOpenGLContext()); |
99 | m_localContext->setFormat(m_mainContext->format()); |
100 | m_localContext->setShareContext(m_mainContext); |
101 | if (!m_localContext->create()) |
102 | qWarning("CommandThread: Failed to create local context" ); |
103 | m_localContext->moveToThread(this); |
104 | |
105 | m_running.fetchAndStoreOrdered(1); |
106 | |
107 | // Allow thread to proceed |
108 | m_initializedSemaphore.release(); |
109 | } |
110 | |
111 | // Called by RenderThread or MainThread (Scene3D) |
112 | void CommandThread::shutdown() |
113 | { |
114 | m_running.fetchAndStoreOrdered(0); |
115 | |
116 | // Unblock thread |
117 | m_commandRequestedSemaphore.release(1); |
118 | |
119 | // Wait for thread to exit |
120 | wait(); |
121 | |
122 | // Reset semaphores (in case we ever want to restart) |
123 | m_waitForStartSemaphore.acquire(m_waitForStartSemaphore.available()); |
124 | m_initializedSemaphore.acquire(m_initializedSemaphore.available()); |
125 | m_commandRequestedSemaphore.acquire(m_commandRequestedSemaphore.available()); |
126 | m_commandExecutionSemaphore.acquire(m_commandExecutionSemaphore.available()); |
127 | m_localContext.reset(); |
128 | } |
129 | |
130 | // Any thread can call this, this is a blocking command |
131 | void CommandThread::executeCommand(GLCommand *command) |
132 | { |
133 | if (!isRunning()) |
134 | return; |
135 | |
136 | // We lock to prevent any other call to executeCommand to be executed |
137 | // before we have received the result of our command |
138 | m_blockingCallerMutex.lock(); |
139 | |
140 | // Store command to be executed |
141 | m_currentCommand = command; |
142 | |
143 | // Allow thread to proceed and execute command |
144 | m_commandRequestedSemaphore.release(); |
145 | |
146 | // Wait for thread to be done |
147 | m_commandExecutionSemaphore.acquire(); |
148 | |
149 | // Reset command |
150 | m_currentCommand = nullptr; |
151 | |
152 | // Unlock blocking semaphore so that other calls to executeCommand |
153 | // can proceed |
154 | m_blockingCallerMutex.unlock(); |
155 | } |
156 | |
157 | void CommandThread::run() |
158 | { |
159 | // Allow initialize to proceed |
160 | m_waitForStartSemaphore.release(); |
161 | |
162 | // Wait for initialize to be completed |
163 | m_initializedSemaphore.acquire(); |
164 | |
165 | Q_ASSERT(m_mainContext && m_shaderCache); |
166 | |
167 | // Initialize GraphicsContext |
168 | m_graphicsContext.reset(new GraphicsContext()); |
169 | m_graphicsContext->setShaderCache(m_shaderCache); |
170 | m_graphicsContext->setOpenGLContext(m_localContext.data()); |
171 | |
172 | bool initialized = false; |
173 | while (true) { |
174 | |
175 | // Wait for command |
176 | m_commandRequestedSemaphore.acquire(); |
177 | |
178 | // Are we still running? |
179 | if (!m_running.load()) { |
180 | m_graphicsContext->doneCurrent(); |
181 | // to prevent executeCommand being locked |
182 | m_commandExecutionSemaphore.release(); |
183 | break; |
184 | } |
185 | |
186 | if (Q_UNLIKELY(!initialized)) { |
187 | QOffscreenSurface *offscreenSurface = m_offsreenSurfaceHelper->offscreenSurface(); |
188 | Q_ASSERT(offscreenSurface); |
189 | m_graphicsContext->makeCurrent(offscreenSurface); |
190 | initialized = true; |
191 | } |
192 | |
193 | m_currentCommand->execute(m_renderer, m_graphicsContext.data()); |
194 | |
195 | // Allow caller to proceed as we are done with the command |
196 | m_commandExecutionSemaphore.release(); |
197 | } |
198 | } |
199 | |
200 | } // Render |
201 | |
202 | } // Qt3DRender |
203 | |
204 | QT_END_NAMESPACE |
205 | |