1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
3 | #include "Renderer/BsRenderer.h" |
4 | #include "CoreThread/BsCoreThread.h" |
5 | #include "RenderAPI/BsRenderAPI.h" |
6 | #include "Mesh/BsMesh.h" |
7 | #include "Material/BsMaterial.h" |
8 | #include "Renderer/BsRendererExtension.h" |
9 | #include "Renderer/BsRendererManager.h" |
10 | #include "CoreThread/BsCoreObjectManager.h" |
11 | #include "Scene/BsSceneManager.h" |
12 | #include "Material/BsShader.h" |
13 | #include "Profiling/BsProfilerGPU.h" |
14 | #include "Profiling/BsProfilerCPU.h" |
15 | |
16 | namespace bs { namespace ct |
17 | { |
18 | Renderer::Renderer() |
19 | :mCallbacks(&compareCallback) |
20 | { } |
21 | |
22 | SPtr<RendererMeshData> Renderer::_createMeshData(UINT32 numVertices, UINT32 numIndices, VertexLayout layout, IndexType indexType) |
23 | { |
24 | return bs_shared_ptr<RendererMeshData>(new (bs_alloc<RendererMeshData>()) |
25 | RendererMeshData(numVertices, numIndices, layout, indexType)); |
26 | } |
27 | |
28 | SPtr<RendererMeshData> Renderer::_createMeshData(const SPtr<MeshData>& meshData) |
29 | { |
30 | return bs_shared_ptr<RendererMeshData>(new (bs_alloc<RendererMeshData>()) |
31 | RendererMeshData(meshData)); |
32 | } |
33 | |
34 | void Renderer::setGlobalShaderOverride(const SPtr<bs::Shader>& shader) |
35 | { |
36 | const Vector<bs::SubShader>& subShaders = shader->getSubShaders(); |
37 | |
38 | for(auto& entry : subShaders) |
39 | setGlobalShaderOverride(entry.name, entry.shader); |
40 | } |
41 | |
42 | bool Renderer::compareCallback(const RendererExtension* a, const RendererExtension* b) |
43 | { |
44 | // Sort by alpha setting first, then by cull mode, then by index |
45 | if (a->getLocation() == b->getLocation()) |
46 | { |
47 | if (a->getPriority() == b->getPriority()) |
48 | return a > b; // Use address, at this point it doesn't matter, but std::set requires us to differentiate |
49 | else |
50 | return a->getPriority() > b->getPriority(); |
51 | } |
52 | else |
53 | return (UINT32)a->getLocation() < (UINT32)b->getLocation(); |
54 | } |
55 | |
56 | void Renderer::update() |
57 | { |
58 | for(auto& entry : mUnresolvedTasks) |
59 | { |
60 | if (entry->isComplete()) |
61 | entry->onComplete(); |
62 | else if (!entry->isCanceled()) |
63 | mRemainingUnresolvedTasks.push_back(entry); |
64 | } |
65 | |
66 | mUnresolvedTasks.clear(); |
67 | std::swap(mRemainingUnresolvedTasks, mUnresolvedTasks); |
68 | } |
69 | |
70 | void Renderer::addTask(const SPtr<RendererTask>& task) |
71 | { |
72 | Lock lock(mTaskMutex); |
73 | |
74 | assert(task->mState != 1 && "Task is already executing, it cannot be executed again until it finishes." ); |
75 | task->mState.store(0); // Reset state in case the task is getting re-queued |
76 | |
77 | mQueuedTasks.push_back(RendererTaskQueuedInfo(task, gTime().getFrameIdx())); |
78 | mUnresolvedTasks.push_back(task); |
79 | } |
80 | |
81 | void Renderer::processTasks(bool forceAll, UINT64 upToFrame) |
82 | { |
83 | // Move all tasks to the core thread queue |
84 | { |
85 | Lock lock(mTaskMutex); |
86 | |
87 | for(UINT32 i = 0; i < (UINT32)mQueuedTasks.size();) |
88 | { |
89 | if(mQueuedTasks[i].frameIdx <= upToFrame) |
90 | { |
91 | mRunningTasks.push_back(mQueuedTasks[i].task); |
92 | bs_swap_and_erase(mQueuedTasks, mQueuedTasks.begin() + i); |
93 | |
94 | continue; |
95 | } |
96 | |
97 | i++; |
98 | } |
99 | } |
100 | |
101 | do |
102 | { |
103 | for (auto& entry : mRunningTasks) |
104 | { |
105 | if (entry->isCanceled() || entry->isComplete()) |
106 | continue; |
107 | |
108 | entry->mState.store(1); |
109 | |
110 | const bool complete = [&entry]() |
111 | { |
112 | ProfileGPUBlock sampleBlock("Renderer task: " + ProfilerString(entry->mName.data(), entry->mName.size())); |
113 | return entry->mTaskWorker(); |
114 | }(); |
115 | |
116 | if (!complete) |
117 | mRemainingTasks.push_back(entry); |
118 | else |
119 | entry->mState.store(2); |
120 | } |
121 | |
122 | mRunningTasks.clear(); |
123 | std::swap(mRemainingTasks, mRunningTasks); |
124 | } while (forceAll && !mRunningTasks.empty()); |
125 | } |
126 | |
127 | void Renderer::processTask(RendererTask& task, bool forceAll) |
128 | { |
129 | // Move task to the core thread queue |
130 | { |
131 | Lock lock(mTaskMutex); |
132 | |
133 | for(UINT32 i = 0; i < (UINT32)mQueuedTasks.size(); i++) |
134 | { |
135 | if(mQueuedTasks[i].task.get() == &task) |
136 | { |
137 | mRunningTasks.push_back(mQueuedTasks[i].task); |
138 | bs_swap_and_erase(mQueuedTasks, mQueuedTasks.begin() + i); |
139 | |
140 | break; |
141 | } |
142 | } |
143 | } |
144 | |
145 | bool complete = task.isCanceled() || task.isComplete(); |
146 | while (!complete) |
147 | { |
148 | task.mState.store(1); |
149 | |
150 | gProfilerGPU().beginFrame(); |
151 | gProfilerCPU().beginThread("RenderTask" ); |
152 | { |
153 | ProfileGPUBlock sampleBlock("Renderer task: " + ProfilerString(task.mName.data(), task.mName.size())); |
154 | complete = task.mTaskWorker(); |
155 | } |
156 | gProfilerCPU().endThread(); |
157 | gProfilerGPU().endFrame(true); |
158 | |
159 | if (complete) |
160 | task.mState.store(2); |
161 | |
162 | if (!forceAll) |
163 | break; |
164 | } |
165 | } |
166 | |
167 | SPtr<Renderer> gRenderer() |
168 | { |
169 | return std::static_pointer_cast<Renderer>(RendererManager::instance().getActive()); |
170 | } |
171 | |
172 | RendererTask::RendererTask(const PrivatelyConstruct& dummy, String name, std::function<bool()> taskWorker) |
173 | :mName(std::move(name)), mTaskWorker(std::move(taskWorker)) |
174 | { } |
175 | |
176 | SPtr<RendererTask> RendererTask::create(String name, std::function<bool()> taskWorker) |
177 | { |
178 | return bs_shared_ptr_new<RendererTask>(PrivatelyConstruct(), std::move(name), std::move(taskWorker)); |
179 | } |
180 | |
181 | bool RendererTask::isComplete() const |
182 | { |
183 | return mState.load() == 2; |
184 | } |
185 | |
186 | bool RendererTask::isCanceled() const |
187 | { |
188 | return mState.load() == 3; |
189 | } |
190 | |
191 | void RendererTask::wait() |
192 | { |
193 | // Task is about to be executed outside of normal rendering workflow. Make sure to manually sync all changes to |
194 | // the core thread first. |
195 | // Note: wait() might only get called during serialization, in which case we might call these methods just once |
196 | // before a level save, instead for every individual component |
197 | gSceneManager()._updateCoreObjectTransforms(); |
198 | CoreObjectManager::instance().syncToCore(); |
199 | |
200 | auto worker = [this]() |
201 | { |
202 | gRenderer()->processTask(*this, true); |
203 | }; |
204 | |
205 | gCoreThread().queueCommand(worker); |
206 | gCoreThread().submit(true); |
207 | |
208 | // Note: Tigger on complete callback and clear it from Renderer? |
209 | } |
210 | |
211 | void RendererTask::cancel() |
212 | { |
213 | mState.store(3); |
214 | } |
215 | }} |
216 | |