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 "BsRenderBeast.h" |
4 | #include "BsCoreApplication.h" |
5 | #include "CoreThread/BsCoreThread.h" |
6 | #include "CoreThread/BsCoreObjectManager.h" |
7 | #include "Material/BsMaterial.h" |
8 | #include "Material/BsShader.h" |
9 | #include "Material/BsPass.h" |
10 | #include "RenderAPI/BsViewport.h" |
11 | #include "RenderAPI/BsRenderTarget.h" |
12 | #include "RenderAPI/BsGpuParamBlockBuffer.h" |
13 | #include "Profiling/BsProfilerCPU.h" |
14 | #include "Profiling/BsProfilerGPU.h" |
15 | #include "Utility/BsTime.h" |
16 | #include "Animation/BsAnimationManager.h" |
17 | #include "Animation/BsSkeleton.h" |
18 | #include "Renderer/BsLight.h" |
19 | #include "Renderer/BsRendererExtension.h" |
20 | #include "Renderer/BsReflectionProbe.h" |
21 | #include "Renderer/BsRenderSettings.h" |
22 | #include "Renderer/BsIBLUtility.h" |
23 | #include "Renderer/BsSkybox.h" |
24 | #include "Renderer/BsCamera.h" |
25 | #include "Renderer/BsRendererUtility.h" |
26 | #include "Utility/BsRendererTextures.h" |
27 | #include "Renderer/BsGpuResourcePool.h" |
28 | #include "Renderer/BsRendererManager.h" |
29 | #include "Shading/BsShadowRendering.h" |
30 | #include "Shading/BsStandardDeferred.h" |
31 | #include "Shading/BsTiledDeferred.h" |
32 | #include "BsRenderBeastOptions.h" |
33 | #include "BsRenderBeastIBLUtility.h" |
34 | #include "BsRenderCompositor.h" |
35 | #include "Shading/BsGpuParticleSimulation.h" |
36 | |
37 | using namespace std::placeholders; |
38 | |
39 | namespace bs { namespace ct |
40 | { |
41 | RenderBeast::RenderBeast() |
42 | { |
43 | mOptions = bs_shared_ptr_new<RenderBeastOptions>(); |
44 | } |
45 | |
46 | const StringID& RenderBeast::getName() const |
47 | { |
48 | static StringID name = "RenderBeast" ; |
49 | return name; |
50 | } |
51 | |
52 | void RenderBeast::initialize() |
53 | { |
54 | Renderer::initialize(); |
55 | |
56 | gCoreThread().queueCommand(std::bind(&RenderBeast::initializeCore, this), CTQF_InternalQueue); |
57 | } |
58 | |
59 | void RenderBeast::destroy() |
60 | { |
61 | Renderer::destroy(); |
62 | |
63 | gCoreThread().queueCommand(std::bind(&RenderBeast::destroyCore, this)); |
64 | gCoreThread().submit(true); |
65 | } |
66 | |
67 | void RenderBeast::initializeCore() |
68 | { |
69 | const RenderAPICapabilities& caps = gCaps(); |
70 | |
71 | if( |
72 | !caps.hasCapability(RSC_COMPUTE_PROGRAM) || |
73 | !caps.hasCapability(RSC_LOAD_STORE) || |
74 | !caps.hasCapability(RSC_TEXTURE_VIEWS)) |
75 | { |
76 | mFeatureSet = RenderBeastFeatureSet::DesktopMacOS; |
77 | } |
78 | |
79 | // Ensure profiler methods can be called from start-up methods |
80 | gProfilerGPU().beginFrame(); |
81 | |
82 | RendererUtility::startUp(); |
83 | GpuSort::startUp(); |
84 | GpuResourcePool::startUp(); |
85 | IBLUtility::startUp<RenderBeastIBLUtility>(); |
86 | RendererTextures::startUp(); |
87 | |
88 | mCoreOptions = bs_shared_ptr_new<RenderBeastOptions>(); |
89 | mScene = bs_shared_ptr_new<RendererScene>(mCoreOptions); |
90 | |
91 | mMainViewGroup = bs_new<RendererViewGroup>(nullptr, 0, true); |
92 | |
93 | StandardDeferred::startUp(); |
94 | ParticleRenderer::startUp(); |
95 | GpuParticleSimulation::startUp(); |
96 | |
97 | gProfilerGPU().endFrame(); |
98 | |
99 | RenderCompositor::registerNodeType<RCNodeSceneDepth>(); |
100 | RenderCompositor::registerNodeType<RCNodeBasePass>(); |
101 | RenderCompositor::registerNodeType<RCNodeLightAccumulation>(); |
102 | RenderCompositor::registerNodeType<RCNodeSceneColor>(); |
103 | RenderCompositor::registerNodeType<RCNodeDeferredDirectLighting>(); |
104 | RenderCompositor::registerNodeType<RCNodeIndirectDiffuseLighting>(); |
105 | RenderCompositor::registerNodeType<RCNodeDeferredIndirectSpecularLighting>(); |
106 | RenderCompositor::registerNodeType<RCNodeFinalResolve>(); |
107 | RenderCompositor::registerNodeType<RCNodeSkybox>(); |
108 | RenderCompositor::registerNodeType<RCNodePostProcess>(); |
109 | RenderCompositor::registerNodeType<RCNodeTonemapping>(); |
110 | RenderCompositor::registerNodeType<RCNodeGaussianDOF>(); |
111 | RenderCompositor::registerNodeType<RCNodeFXAA>(); |
112 | RenderCompositor::registerNodeType<RCNodeResolvedSceneDepth>(); |
113 | RenderCompositor::registerNodeType<RCNodeHiZ>(); |
114 | RenderCompositor::registerNodeType<RCNodeSSAO>(); |
115 | RenderCompositor::registerNodeType<RCNodeClusteredForward>(); |
116 | RenderCompositor::registerNodeType<RCNodeSSR>(); |
117 | RenderCompositor::registerNodeType<RCNodeMSAACoverage>(); |
118 | RenderCompositor::registerNodeType<RCNodeParticleSimulate>(); |
119 | RenderCompositor::registerNodeType<RCNodeParticleSort>(); |
120 | RenderCompositor::registerNodeType<RCNodeHalfSceneColor>(); |
121 | RenderCompositor::registerNodeType<RCNodeBloom>(); |
122 | RenderCompositor::registerNodeType<RCNodeEyeAdaptation>(); |
123 | } |
124 | |
125 | void RenderBeast::destroyCore() |
126 | { |
127 | // Make sure all tasks finish first |
128 | processTasks(true); |
129 | |
130 | mScene = nullptr; |
131 | |
132 | RenderCompositor::cleanUp(); |
133 | |
134 | GpuParticleSimulation::shutDown(); |
135 | ParticleRenderer::shutDown(); |
136 | StandardDeferred::shutDown(); |
137 | |
138 | bs_delete(mMainViewGroup); |
139 | |
140 | RendererTextures::shutDown(); |
141 | IBLUtility::shutDown(); |
142 | GpuResourcePool::shutDown(); |
143 | GpuSort::shutDown(); |
144 | RendererUtility::shutDown(); |
145 | } |
146 | |
147 | void RenderBeast::notifyRenderableAdded(Renderable* renderable) |
148 | { |
149 | mScene->registerRenderable(renderable); |
150 | } |
151 | |
152 | void RenderBeast::notifyRenderableRemoved(Renderable* renderable) |
153 | { |
154 | mScene->unregisterRenderable(renderable); |
155 | } |
156 | |
157 | void RenderBeast::notifyRenderableUpdated(Renderable* renderable) |
158 | { |
159 | mScene->updateRenderable(renderable); |
160 | } |
161 | |
162 | void RenderBeast::notifyLightAdded(Light* light) |
163 | { |
164 | mScene->registerLight(light); |
165 | } |
166 | |
167 | void RenderBeast::notifyLightUpdated(Light* light) |
168 | { |
169 | mScene->updateLight(light); |
170 | } |
171 | |
172 | void RenderBeast::notifyLightRemoved(Light* light) |
173 | { |
174 | mScene->unregisterLight(light); |
175 | } |
176 | |
177 | void RenderBeast::notifyCameraAdded(Camera* camera) |
178 | { |
179 | mScene->registerCamera(camera); |
180 | } |
181 | |
182 | void RenderBeast::notifyCameraUpdated(Camera* camera, UINT32 updateFlag) |
183 | { |
184 | mScene->updateCamera(camera, updateFlag); |
185 | } |
186 | |
187 | void RenderBeast::notifyCameraRemoved(Camera* camera) |
188 | { |
189 | mScene->unregisterCamera(camera); |
190 | } |
191 | |
192 | void RenderBeast::notifyReflectionProbeAdded(ReflectionProbe* probe) |
193 | { |
194 | mScene->registerReflectionProbe(probe); |
195 | } |
196 | |
197 | void RenderBeast::notifyReflectionProbeUpdated(ReflectionProbe* probe, bool texture) |
198 | { |
199 | mScene->updateReflectionProbe(probe, texture); |
200 | } |
201 | |
202 | void RenderBeast::notifyReflectionProbeRemoved(ReflectionProbe* probe) |
203 | { |
204 | mScene->unregisterReflectionProbe(probe); |
205 | } |
206 | |
207 | void RenderBeast::notifyLightProbeVolumeAdded(LightProbeVolume* volume) |
208 | { |
209 | mScene->registerLightProbeVolume(volume); |
210 | } |
211 | |
212 | void RenderBeast::notifyLightProbeVolumeUpdated(LightProbeVolume* volume) |
213 | { |
214 | mScene->updateLightProbeVolume(volume); |
215 | } |
216 | |
217 | void RenderBeast::notifyLightProbeVolumeRemoved(LightProbeVolume* volume) |
218 | { |
219 | mScene->unregisterLightProbeVolume(volume); |
220 | } |
221 | |
222 | void RenderBeast::notifySkyboxAdded(Skybox* skybox) |
223 | { |
224 | mScene->registerSkybox(skybox); |
225 | } |
226 | |
227 | void RenderBeast::notifySkyboxRemoved(Skybox* skybox) |
228 | { |
229 | mScene->unregisterSkybox(skybox); |
230 | } |
231 | |
232 | void RenderBeast::notifyParticleSystemAdded(ParticleSystem* particleSystem) |
233 | { |
234 | mScene->registerParticleSystem(particleSystem); |
235 | } |
236 | |
237 | void RenderBeast::notifyParticleSystemUpdated(ParticleSystem* particleSystem, bool tfrmOnly) |
238 | { |
239 | mScene->updateParticleSystem(particleSystem, tfrmOnly); |
240 | } |
241 | |
242 | void RenderBeast::notifyParticleSystemRemoved(ParticleSystem* particleSystem) |
243 | { |
244 | mScene->unregisterParticleSystem(particleSystem); |
245 | } |
246 | |
247 | void RenderBeast::notifyDecalAdded(Decal* decal) |
248 | { |
249 | mScene->registerDecal(decal); |
250 | } |
251 | |
252 | void RenderBeast::notifyDecalRemoved(Decal* decal) |
253 | { |
254 | mScene->unregisterDecal(decal); |
255 | } |
256 | |
257 | void RenderBeast::notifyDecalUpdated(Decal* decal) |
258 | { |
259 | mScene->updateDecal(decal); |
260 | } |
261 | void RenderBeast::setOptions(const SPtr<RendererOptions>& options) |
262 | { |
263 | mOptions = std::static_pointer_cast<RenderBeastOptions>(options); |
264 | mOptionsDirty = true; |
265 | } |
266 | |
267 | SPtr<RendererOptions> RenderBeast::getOptions() const |
268 | { |
269 | return mOptions; |
270 | } |
271 | |
272 | void RenderBeast::syncOptions(const RenderBeastOptions& options) |
273 | { |
274 | bool filteringChanged = mCoreOptions->filtering != options.filtering; |
275 | if (options.filtering == RenderBeastFiltering::Anisotropic) |
276 | filteringChanged |= mCoreOptions->anisotropyMax != options.anisotropyMax; |
277 | |
278 | if (filteringChanged) |
279 | mScene->refreshSamplerOverrides(true); |
280 | |
281 | *mCoreOptions = options; |
282 | |
283 | mScene->setOptions(mCoreOptions); |
284 | |
285 | ShadowRendering& shadowRenderer = mMainViewGroup->getShadowRenderer(); |
286 | shadowRenderer.setShadowMapSize(mCoreOptions->shadowMapSize); |
287 | } |
288 | |
289 | ShaderExtensionPointInfo RenderBeast::getShaderExtensionPointInfo(const String& name) |
290 | { |
291 | if(name == "DeferredDirectLighting" ) |
292 | { |
293 | ShaderExtensionPointInfo info; |
294 | |
295 | ExtensionShaderInfo tiledDeferredInfo; |
296 | tiledDeferredInfo.name = "TiledDeferredDirectLighting" ; |
297 | tiledDeferredInfo.path = TiledDeferredLightingMat::getShaderPath(); |
298 | tiledDeferredInfo.defines = TiledDeferredLightingMat::getShaderDefines(); |
299 | info.shaders.push_back(tiledDeferredInfo); |
300 | |
301 | ExtensionShaderInfo standardDeferredPointInfo; |
302 | standardDeferredPointInfo.name = "StandardDeferredPointDirectLighting" ; |
303 | standardDeferredPointInfo.path = DeferredPointLightMat::getShaderPath(); |
304 | standardDeferredPointInfo.defines = DeferredPointLightMat::getShaderDefines(); |
305 | info.shaders.push_back(standardDeferredPointInfo); |
306 | |
307 | ExtensionShaderInfo standardDeferredDirInfo; |
308 | standardDeferredDirInfo.name = "StandardDeferredDirDirectLighting" ; |
309 | standardDeferredDirInfo.path = DeferredDirectionalLightMat::getShaderPath(); |
310 | standardDeferredDirInfo.defines = DeferredDirectionalLightMat::getShaderDefines(); |
311 | info.shaders.push_back(standardDeferredPointInfo); |
312 | |
313 | return info; |
314 | } |
315 | |
316 | return ShaderExtensionPointInfo(); |
317 | } |
318 | |
319 | void RenderBeast::setGlobalShaderOverride(const String& name, const SPtr<bs::Shader>& shader) |
320 | { |
321 | SPtr<ct::Shader> shaderCore; |
322 | if(shader) |
323 | shaderCore = shader->getCore(); |
324 | |
325 | auto setShaderOverride = [name, shaderCore]() |
326 | { |
327 | if (name == "TiledDeferredDirectLighting" ) |
328 | TiledDeferredLightingMat::setOverride(shaderCore); |
329 | else if(name == "StandardDeferredPointDirectLighting" ) |
330 | DeferredPointLightMat::setOverride(shaderCore); |
331 | else if(name == "StandardDeferredDirDirectLighting" ) |
332 | DeferredDirectionalLightMat::setOverride(shaderCore); |
333 | }; |
334 | |
335 | gCoreThread().queueCommand(setShaderOverride); |
336 | } |
337 | |
338 | void RenderBeast::renderAll(PerFrameData perFrameData) |
339 | { |
340 | // Sync all dirty sim thread CoreObject data to core thread |
341 | PROFILE_CALL(CoreObjectManager::instance().syncToCore(), "Sync to core" ) |
342 | |
343 | if (mOptionsDirty) |
344 | { |
345 | gCoreThread().queueCommand(std::bind(&RenderBeast::syncOptions, this, *mOptions)); |
346 | mOptionsDirty = false; |
347 | } |
348 | |
349 | FrameTimings timings; |
350 | timings.time = gTime().getTime(); |
351 | timings.timeDelta = gTime().getFrameDelta(); |
352 | timings.frameIdx = gTime().getFrameIdx(); |
353 | |
354 | gCoreThread().queueCommand(std::bind(&RenderBeast::renderAllCore, this, timings, perFrameData)); |
355 | } |
356 | |
357 | void RenderBeast::renderAllCore(FrameTimings timings, PerFrameData perFrameData) |
358 | { |
359 | THROW_IF_NOT_CORE_THREAD; |
360 | |
361 | gProfilerGPU().beginFrame(); |
362 | gProfilerCPU().beginSample("Render" ); |
363 | |
364 | const SceneInfo& sceneInfo = mScene->getSceneInfo(); |
365 | |
366 | // Note: I'm iterating over all sampler states every frame. If this ends up being a performance |
367 | // issue consider handling this internally in ct::Material which can only do it when sampler states |
368 | // are actually modified after sync |
369 | mScene->refreshSamplerOverrides(); |
370 | |
371 | // Update global per-frame hardware buffers |
372 | mScene->setParamFrameParams(timings.time); |
373 | |
374 | // Update bounds for all particle systems |
375 | if(perFrameData.particles) |
376 | PROFILE_CALL(mScene->updateParticleSystemBounds(perFrameData.particles), "Particle bounds" ) |
377 | |
378 | sceneInfo.renderableReady.resize(sceneInfo.renderables.size(), false); |
379 | sceneInfo.renderableReady.assign(sceneInfo.renderables.size(), false); |
380 | |
381 | FrameInfo frameInfo(timings, perFrameData); |
382 | |
383 | // Make sure any renderer tasks finish first, as rendering might depend on them |
384 | processTasks(false, timings.frameIdx); |
385 | |
386 | // If any reflection probes were updated or added, we need to copy them over in the global reflection probe array |
387 | updateReflProbeArray(); |
388 | |
389 | // Update material parameters & animation times for all renderables |
390 | for (UINT32 i = 0; i < sceneInfo.renderables.size(); i++) |
391 | { |
392 | RendererRenderable* renderable = sceneInfo.renderables[i]; |
393 | for (auto& element : renderable->elements) |
394 | element.materialAnimationTime += timings.timeDelta; |
395 | } |
396 | |
397 | for (UINT32 i = 0; i < sceneInfo.particleSystems.size(); i++) |
398 | mScene->prepareParticleSystem(i, frameInfo); |
399 | |
400 | for (UINT32 i = 0; i < sceneInfo.decals.size(); i++) |
401 | { |
402 | const RendererDecal& decal = sceneInfo.decals[i]; |
403 | decal.renderElement.materialAnimationTime += timings.timeDelta; |
404 | |
405 | mScene->prepareDecal(i, frameInfo); |
406 | } |
407 | |
408 | // Gather all views |
409 | for (auto& rtInfo : sceneInfo.renderTargets) |
410 | { |
411 | Vector<RendererView*> views; |
412 | SPtr<RenderTarget> target = rtInfo.target; |
413 | const Vector<Camera*>& cameras = rtInfo.cameras; |
414 | |
415 | UINT32 numCameras = (UINT32)cameras.size(); |
416 | for (UINT32 i = 0; i < numCameras; i++) |
417 | { |
418 | UINT32 viewIdx = sceneInfo.cameraToView.at(cameras[i]); |
419 | RendererView* viewInfo = sceneInfo.views[viewIdx]; |
420 | views.push_back(viewInfo); |
421 | } |
422 | |
423 | mMainViewGroup->setViews(views.data(), (UINT32)views.size()); |
424 | PROFILE_CALL(mMainViewGroup->determineVisibility(sceneInfo), "Determine visibility" ) |
425 | |
426 | // Render everything |
427 | renderViews(*mMainViewGroup, frameInfo); |
428 | |
429 | if(rtInfo.target->getProperties().isWindow) |
430 | PROFILE_CALL(RenderAPI::instance().swapBuffers(rtInfo.target), "Swap buffers" ); |
431 | } |
432 | |
433 | gProfilerGPU().endFrame(); |
434 | gProfilerCPU().endSample("Render" ); |
435 | } |
436 | |
437 | void RenderBeast::renderViews(RendererViewGroup& viewGroup, const FrameInfo& frameInfo) |
438 | { |
439 | const SceneInfo& sceneInfo = mScene->getSceneInfo(); |
440 | const VisibilityInfo& visibility = viewGroup.getVisibilityInfo(); |
441 | |
442 | // Render shadow maps |
443 | ShadowRendering& shadowRenderer = viewGroup.getShadowRenderer(); |
444 | shadowRenderer.renderShadowMaps(*mScene, viewGroup, frameInfo); |
445 | |
446 | // Update various buffers required by each renderable |
447 | UINT32 numRenderables = (UINT32)sceneInfo.renderables.size(); |
448 | for (UINT32 i = 0; i < numRenderables; i++) |
449 | { |
450 | if (!visibility.renderables[i]) |
451 | continue; |
452 | |
453 | mScene->prepareRenderable(i, frameInfo); |
454 | } |
455 | |
456 | UINT32 numViews = viewGroup.getNumViews(); |
457 | for (UINT32 i = 0; i < numViews; i++) |
458 | { |
459 | RendererView* view = viewGroup.getView(i); |
460 | const RenderSettings& settings = view->getRenderSettings(); |
461 | |
462 | if (settings.overlayOnly) |
463 | renderOverlay(*view); |
464 | else |
465 | renderView(viewGroup, *view, frameInfo); |
466 | } |
467 | } |
468 | |
469 | void RenderBeast::renderView(const RendererViewGroup& viewGroup, RendererView& view, const FrameInfo& frameInfo) |
470 | { |
471 | gProfilerCPU().beginSample("Render view" ); |
472 | |
473 | const SceneInfo& sceneInfo = mScene->getSceneInfo(); |
474 | auto& viewProps = view.getProperties(); |
475 | |
476 | SPtr<GpuParamBlockBuffer> perCameraBuffer = view.getPerViewBuffer(); |
477 | perCameraBuffer->flushToGPU(); |
478 | |
479 | // Make sure light probe data is up to date |
480 | if(view.getRenderSettings().enableIndirectLighting) |
481 | mScene->updateLightProbes(); |
482 | |
483 | view.beginFrame(); |
484 | |
485 | RenderCompositorNodeInputs inputs(viewGroup, view, sceneInfo, *mCoreOptions, frameInfo, mFeatureSet); |
486 | |
487 | // Register callbacks |
488 | if (viewProps.triggerCallbacks) |
489 | { |
490 | for(auto& extension : mCallbacks) |
491 | { |
492 | RenderLocation location = extension->getLocation(); |
493 | switch(location) |
494 | { |
495 | case RenderLocation::Prepare: |
496 | inputs.extPrepare.add(extension); |
497 | break; |
498 | case RenderLocation::PreBasePass: |
499 | inputs.extPreBasePass.add(extension); |
500 | break; |
501 | case RenderLocation::PostBasePass: |
502 | inputs.extPostBasePass.add(extension); |
503 | break; |
504 | case RenderLocation::PostLightPass: |
505 | inputs.extPostLighting.add(extension); |
506 | break; |
507 | case RenderLocation::Overlay: |
508 | inputs.extOverlay.add(extension); |
509 | break; |
510 | } |
511 | } |
512 | } |
513 | |
514 | const RenderCompositor& compositor = view.getCompositor(); |
515 | PROFILE_CALL(compositor.execute(inputs), "Compositor" ) |
516 | |
517 | view.endFrame(); |
518 | |
519 | gProfilerCPU().endSample("Render view" ); |
520 | } |
521 | |
522 | void RenderBeast::renderOverlay(RendererView& view) |
523 | { |
524 | gProfilerCPU().beginSample("Render overlay" ); |
525 | |
526 | view.getPerViewBuffer()->flushToGPU(); |
527 | view.beginFrame(); |
528 | |
529 | auto& viewProps = view.getProperties(); |
530 | const Camera* camera = view.getSceneCamera(); |
531 | SPtr<RenderTarget> target = viewProps.target.target; |
532 | SPtr<Viewport> viewport = camera->getViewport(); |
533 | |
534 | ClearFlags clearFlags = viewport->getClearFlags(); |
535 | UINT32 clearBuffers = 0; |
536 | if (clearFlags.isSet(ClearFlagBits::Color)) |
537 | clearBuffers |= FBT_COLOR; |
538 | |
539 | if (clearFlags.isSet(ClearFlagBits::Depth)) |
540 | clearBuffers |= FBT_DEPTH; |
541 | |
542 | if (clearFlags.isSet(ClearFlagBits::Stencil)) |
543 | clearBuffers |= FBT_STENCIL; |
544 | |
545 | if (clearBuffers != 0) |
546 | { |
547 | RenderAPI::instance().setRenderTarget(target); |
548 | RenderAPI::instance().clearViewport(clearBuffers, viewport->getClearColorValue(), |
549 | viewport->getClearDepthValue(), viewport->getClearStencilValue()); |
550 | } |
551 | else |
552 | RenderAPI::instance().setRenderTarget(target, 0, RT_COLOR0); |
553 | |
554 | RenderAPI::instance().setViewport(viewport->getArea()); |
555 | |
556 | // Trigger overlay callbacks |
557 | auto iterRenderCallback = mCallbacks.begin(); |
558 | while (iterRenderCallback != mCallbacks.end()) |
559 | { |
560 | RendererExtension* extension = *iterRenderCallback; |
561 | if (extension->getLocation() != RenderLocation::Overlay) |
562 | { |
563 | ++iterRenderCallback; |
564 | continue; |
565 | } |
566 | |
567 | if (extension->check(*camera)) |
568 | extension->render(*camera); |
569 | |
570 | ++iterRenderCallback; |
571 | } |
572 | |
573 | view.endFrame(); |
574 | |
575 | gProfilerCPU().endSample("Render overlay" ); |
576 | } |
577 | |
578 | void RenderBeast::updateReflProbeArray() |
579 | { |
580 | SceneInfo& sceneInfo = mScene->_getSceneInfo(); |
581 | UINT32 numProbes = (UINT32)sceneInfo.reflProbes.size(); |
582 | |
583 | bs_frame_mark(); |
584 | { |
585 | UINT32 currentCubeArraySize = 0; |
586 | |
587 | if(sceneInfo.reflProbeCubemapsTex != nullptr) |
588 | currentCubeArraySize = sceneInfo.reflProbeCubemapsTex->getProperties().getNumArraySlices(); |
589 | |
590 | bool forceArrayUpdate = false; |
591 | if(sceneInfo.reflProbeCubemapsTex == nullptr || (currentCubeArraySize < numProbes && currentCubeArraySize != MaxReflectionCubemaps)) |
592 | { |
593 | TEXTURE_DESC cubeMapDesc; |
594 | cubeMapDesc.type = TEX_TYPE_CUBE_MAP; |
595 | cubeMapDesc.format = PF_RG11B10F; |
596 | cubeMapDesc.width = IBLUtility::REFLECTION_CUBEMAP_SIZE; |
597 | cubeMapDesc.height = IBLUtility::REFLECTION_CUBEMAP_SIZE; |
598 | cubeMapDesc.numMips = PixelUtil::getMaxMipmaps(cubeMapDesc.width, cubeMapDesc.height, 1, cubeMapDesc.format); |
599 | cubeMapDesc.numArraySlices = std::min(MaxReflectionCubemaps, numProbes + 4); // Keep a few empty entries |
600 | |
601 | sceneInfo.reflProbeCubemapsTex = Texture::create(cubeMapDesc); |
602 | |
603 | forceArrayUpdate = true; |
604 | } |
605 | |
606 | auto& cubemapArrayProps = sceneInfo.reflProbeCubemapsTex->getProperties(); |
607 | |
608 | FrameQueue<UINT32> emptySlots; |
609 | for (UINT32 i = 0; i < numProbes; i++) |
610 | { |
611 | const RendererReflectionProbe& probeInfo = sceneInfo.reflProbes[i]; |
612 | |
613 | if (probeInfo.arrayIdx > MaxReflectionCubemaps) |
614 | continue; |
615 | |
616 | if(probeInfo.arrayDirty || forceArrayUpdate) |
617 | { |
618 | SPtr<Texture> texture = probeInfo.probe->getFilteredTexture(); |
619 | if (texture == nullptr) |
620 | continue; |
621 | |
622 | auto& srcProps = texture->getProperties(); |
623 | bool isValid = srcProps.getWidth() == IBLUtility::REFLECTION_CUBEMAP_SIZE && |
624 | srcProps.getHeight() == IBLUtility::REFLECTION_CUBEMAP_SIZE && |
625 | srcProps.getNumMipmaps() == cubemapArrayProps.getNumMipmaps() && |
626 | srcProps.getTextureType() == TEX_TYPE_CUBE_MAP; |
627 | |
628 | if(!isValid) |
629 | { |
630 | if (!probeInfo.errorFlagged) |
631 | { |
632 | String errMsg = StringUtil::format("Cubemap texture invalid to use as a reflection cubemap. " |
633 | "Check texture size (must be {0}x{0}) and mip-map count" , |
634 | IBLUtility::REFLECTION_CUBEMAP_SIZE); |
635 | |
636 | LOGERR(errMsg); |
637 | probeInfo.errorFlagged = true; |
638 | } |
639 | } |
640 | else |
641 | { |
642 | for(UINT32 face = 0; face < 6; face++) |
643 | { |
644 | for(UINT32 mip = 0; mip <= srcProps.getNumMipmaps(); mip++) |
645 | { |
646 | TEXTURE_COPY_DESC copyDesc; |
647 | copyDesc.srcFace = face; |
648 | copyDesc.srcMip = mip; |
649 | copyDesc.dstFace = probeInfo.arrayIdx * 6 + face; |
650 | copyDesc.dstMip = mip; |
651 | |
652 | texture->copy(sceneInfo.reflProbeCubemapsTex, copyDesc); |
653 | } |
654 | } |
655 | } |
656 | |
657 | mScene->setReflectionProbeArrayIndex(i, probeInfo.arrayIdx, true); |
658 | } |
659 | |
660 | // Note: Consider pruning the reflection cubemap array if empty slot count becomes too high |
661 | } |
662 | } |
663 | bs_frame_clear(); |
664 | } |
665 | |
666 | void RenderBeast::captureSceneCubeMap(const SPtr<Texture>& cubemap, const Vector3& position, |
667 | const CaptureSettings& settings) |
668 | { |
669 | const SceneInfo& sceneInfo = mScene->getSceneInfo(); |
670 | auto& texProps = cubemap->getProperties(); |
671 | |
672 | Matrix4 projTransform = Matrix4::projectionPerspective(Degree(90.0f), 1.0f, 0.05f, 1000.0f); |
673 | ConvexVolume localFrustum(projTransform); |
674 | RenderAPI::instance().convertProjectionMatrix(projTransform, projTransform); |
675 | |
676 | RENDERER_VIEW_DESC viewDesc; |
677 | viewDesc.target.clearFlags = FBT_COLOR | FBT_DEPTH; |
678 | viewDesc.target.clearColor = Color::Black; |
679 | viewDesc.target.clearDepthValue = 1.0f; |
680 | viewDesc.target.clearStencilValue = 0; |
681 | |
682 | viewDesc.target.nrmViewRect = Rect2(0, 0, 1.0f, 1.0f); |
683 | viewDesc.target.viewRect = Rect2I(0, 0, texProps.getWidth(), texProps.getHeight()); |
684 | viewDesc.target.targetWidth = texProps.getWidth(); |
685 | viewDesc.target.targetHeight = texProps.getHeight(); |
686 | viewDesc.target.numSamples = 1; |
687 | |
688 | viewDesc.mainView = false; |
689 | viewDesc.triggerCallbacks = false; |
690 | viewDesc.runPostProcessing = false; |
691 | viewDesc.capturingReflections = true; |
692 | viewDesc.encodeDepth = settings.encodeDepth; |
693 | viewDesc.depthEncodeNear = settings.depthEncodeNear; |
694 | viewDesc.depthEncodeFar = settings.depthEncodeFar; |
695 | |
696 | viewDesc.visibleLayers = 0xFFFFFFFFFFFFFFFF; |
697 | viewDesc.nearPlane = 0.5f; |
698 | viewDesc.farPlane = 1000.0f; |
699 | viewDesc.flipView = gCaps().conventions.uvYAxis != Conventions::Axis::Up; |
700 | |
701 | viewDesc.viewOrigin = position; |
702 | viewDesc.projTransform = projTransform; |
703 | viewDesc.projType = PT_PERSPECTIVE; |
704 | |
705 | viewDesc.stateReduction = mCoreOptions->stateReductionMode; |
706 | viewDesc.sceneCamera = nullptr; |
707 | |
708 | SPtr<RenderSettings> renderSettings = bs_shared_ptr_new<RenderSettings>(); |
709 | renderSettings->enableHDR = settings.hdr; |
710 | renderSettings->enableShadows = true; |
711 | renderSettings->enableIndirectLighting = false; |
712 | renderSettings->screenSpaceReflections.enabled = false; |
713 | renderSettings->ambientOcclusion.enabled = false; |
714 | |
715 | Matrix4 viewOffsetMat = Matrix4::translation(-position); |
716 | |
717 | // Note: We render upside down, then flip the image vertically, which results in a horizontal flip. The horizontal |
718 | // flip is required due to the fact how cubemap faces are defined. Another option would be to change the view |
719 | // orientation matrix, but that also requires a culling mode flip which is inconvenient to do globally. |
720 | RendererView views[6]; |
721 | for(UINT32 i = 0; i < 6; i++) |
722 | { |
723 | // Calculate view matrix |
724 | Vector3 forward; |
725 | Vector3 up = Vector3::UNIT_Y; |
726 | |
727 | switch (i) |
728 | { |
729 | case CF_PositiveX: |
730 | forward = -Vector3::UNIT_X; |
731 | up = -Vector3::UNIT_Y; |
732 | break; |
733 | case CF_NegativeX: |
734 | forward = Vector3::UNIT_X; |
735 | up = -Vector3::UNIT_Y; |
736 | break; |
737 | case CF_PositiveY: |
738 | forward = Vector3::UNIT_Y; |
739 | up = -Vector3::UNIT_Z; |
740 | break; |
741 | case CF_NegativeY: |
742 | forward = -Vector3::UNIT_Y; |
743 | up = Vector3::UNIT_Z; |
744 | break; |
745 | case CF_PositiveZ: |
746 | forward = -Vector3::UNIT_Z; |
747 | up = -Vector3::UNIT_Y; |
748 | break; |
749 | case CF_NegativeZ: |
750 | forward = Vector3::UNIT_Z; |
751 | up = -Vector3::UNIT_Y; |
752 | break; |
753 | } |
754 | |
755 | Vector3 right = Vector3::cross(up, forward); |
756 | Matrix3 viewRotationMat = Matrix3(right, up, forward); |
757 | |
758 | viewDesc.viewDirection = -forward; |
759 | viewDesc.viewTransform = Matrix4(viewRotationMat) * viewOffsetMat; |
760 | |
761 | // Calculate world frustum for culling |
762 | const Vector<Plane>& frustumPlanes = localFrustum.getPlanes(); |
763 | Matrix4 worldMatrix = viewDesc.viewTransform.transpose(); |
764 | |
765 | Vector<Plane> worldPlanes(frustumPlanes.size()); |
766 | UINT32 j = 0; |
767 | for (auto& plane : frustumPlanes) |
768 | { |
769 | worldPlanes[j] = worldMatrix.multiplyAffine(plane); |
770 | j++; |
771 | } |
772 | |
773 | viewDesc.cullFrustum = ConvexVolume(worldPlanes); |
774 | |
775 | // Set up face render target |
776 | RENDER_TEXTURE_DESC cubeFaceRTDesc; |
777 | cubeFaceRTDesc.colorSurfaces[0].texture = cubemap; |
778 | cubeFaceRTDesc.colorSurfaces[0].face = i; |
779 | cubeFaceRTDesc.colorSurfaces[0].numFaces = 1; |
780 | |
781 | viewDesc.target.target = RenderTexture::create(cubeFaceRTDesc); |
782 | |
783 | views[i].setView(viewDesc); |
784 | views[i].setRenderSettings(renderSettings); |
785 | views[i].updatePerViewBuffer(); |
786 | } |
787 | |
788 | RendererView* viewPtrs[] = { &views[0], &views[1], &views[2], &views[3], &views[4], &views[5] }; |
789 | |
790 | RendererViewGroup viewGroup(viewPtrs, 6, false, mCoreOptions->shadowMapSize); |
791 | viewGroup.determineVisibility(sceneInfo); |
792 | |
793 | FrameInfo frameInfo({ 0.0f, 1.0f / 60.0f, 0 }, PerFrameData()); |
794 | renderViews(viewGroup, frameInfo); |
795 | |
796 | // Make sure the render texture is available for reads |
797 | RenderAPI::instance().setRenderTarget(nullptr); |
798 | } |
799 | |
800 | SPtr<RenderBeast> gRenderBeast() |
801 | { |
802 | return std::static_pointer_cast<RenderBeast>(RendererManager::instance().getActive()); |
803 | } |
804 | }} |