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
16namespace 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