| 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 | }} |