diff --git a/filament/src/Froxelizer.cpp b/filament/src/Froxelizer.cpp index ddbe6f7d44..a5912b7d18 100644 --- a/filament/src/Froxelizer.cpp +++ b/filament/src/Froxelizer.cpp @@ -560,6 +560,8 @@ void Froxelizer::froxelizeLoop(FEngine& engine, spheres, directions, instances, &viewMatrix, &lcm ] (size_t count, size_t offset, size_t stride) { + SYSTRACE_NAME("FroxelizeLoop Job"); + const mat4f& projection = mProjection; const mat3f& vn = viewMatrix.upperLeft(); diff --git a/filament/src/ShadowMapManager.cpp b/filament/src/ShadowMapManager.cpp index 7a6217c1b3..c77a442f4b 100644 --- a/filament/src/ShadowMapManager.cpp +++ b/filament/src/ShadowMapManager.cpp @@ -70,7 +70,7 @@ void ShadowMapManager::terminate(FEngine& engine) { ShadowMapManager::ShadowTechnique ShadowMapManager::update(FEngine& engine, FView& view, CameraInfo const& cameraInfo, - FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept { + FScene::RenderableSoa& renderableData, FScene::LightSoa const& lightData) noexcept { ShadowTechnique shadowTechnique = {}; calculateTextureRequirements(engine, view, lightData); @@ -436,7 +436,7 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEngine& engine, FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData, - FScene::LightSoa& lightData, ShadowMap::SceneInfo sceneInfo) noexcept { + FScene::LightSoa const& lightData, ShadowMap::SceneInfo sceneInfo) noexcept { FScene* scene = view.getScene(); auto& lcm = engine.getLightManager(); @@ -785,9 +785,13 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap, } ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine& engine, - FScene::LightSoa& lightData) noexcept { + FScene::LightSoa const& lightData) noexcept { - FScene::ShadowInfo* const shadowInfo = lightData.data(); + // The const_cast here is a little ugly, but conceptually lightData should be const, + // it's just that we're using it to store some temporary data. with SoA we can't have + // a `mutable` element, so that's a workaround. + FScene::ShadowInfo* const shadowInfo = const_cast( + lightData.data()); ShadowTechnique shadowTechnique{}; if (!mSpotShadowMaps.empty()) { @@ -821,7 +825,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine } void ShadowMapManager::calculateTextureRequirements(FEngine&, FView& view, - FScene::LightSoa&) noexcept { + FScene::LightSoa const&) noexcept { // Lay out the shadow maps. For now, we take the largest requested dimension and allocate a // texture of that size. Each cascade / shadow map gets its own layer in the array texture. diff --git a/filament/src/ShadowMapManager.h b/filament/src/ShadowMapManager.h index 952cb9e0d4..1fb9602369 100644 --- a/filament/src/ShadowMapManager.h +++ b/filament/src/ShadowMapManager.h @@ -83,7 +83,7 @@ public: // Returns true if any of the shadow maps have visible shadows. ShadowMapManager::ShadowTechnique update(FEngine& engine, FView& view, CameraInfo const& cameraInfo, - FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept; + FScene::RenderableSoa& renderableData, FScene::LightSoa const& lightData) noexcept; // Renders all the shadow maps. FrameGraphId render(FEngine& engine, FrameGraph& fg, RenderPass const& pass, @@ -110,13 +110,13 @@ public: private: ShadowMapManager::ShadowTechnique updateCascadeShadowMaps(FEngine& engine, FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData, - FScene::LightSoa& lightData, ShadowMap::SceneInfo sceneInfo) noexcept; + FScene::LightSoa const& lightData, ShadowMap::SceneInfo sceneInfo) noexcept; ShadowMapManager::ShadowTechnique updateSpotShadowMaps(FEngine& engine, - FScene::LightSoa& lightData) noexcept; + FScene::LightSoa const& lightData) noexcept; - void calculateTextureRequirements(FEngine& engine, FView& view, - FScene::LightSoa& lightData) noexcept; + void calculateTextureRequirements(FEngine&, FView& view, + FScene::LightSoa const&) noexcept; void prepareSpotShadowMap(ShadowMap& shadowMap, FEngine& engine, FView& view, CameraInfo const& mainCameraInfo, diff --git a/filament/src/details/Renderer.cpp b/filament/src/details/Renderer.cpp index 401809ca2c..42e62cf4e7 100644 --- a/filament/src/details/Renderer.cpp +++ b/filament/src/details/Renderer.cpp @@ -610,14 +610,6 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { view.prepareUpscaler(scale); - // start froxelization immediately, it has no dependencies - JobSystem::Job* jobFroxelize = nullptr; - if (view.hasDynamicLighting()) { - jobFroxelize = js.runAndRetain(js.createJob(nullptr, - [&engine, &view, &viewMatrix = cameraInfo.view](JobSystem&, JobSystem::Job*) { - view.froxelize(engine, viewMatrix); })); - } - /* * Allocate command buffer */ @@ -895,8 +887,8 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { // We use a framegraph pass to wait for froxelization to finish (so it can be done // in parallel with .compile() - if (jobFroxelize) { - auto *sync = jobFroxelize; + auto sync = view.getFroxelizerSync(); + if (sync) { js.waitAndRelease(sync); view.commitFroxels(driver); } diff --git a/filament/src/details/View.cpp b/filament/src/details/View.cpp index 6671c83677..deb49e11f6 100644 --- a/filament/src/details/View.cpp +++ b/filament/src/details/View.cpp @@ -268,7 +268,7 @@ bool FView::isSkyboxVisible() const noexcept { } void FView::prepareShadowing(FEngine& engine, FScene::RenderableSoa& renderableData, - FScene::LightSoa& lightData, CameraInfo const& cameraInfo) noexcept { + FScene::LightSoa const& lightData, CameraInfo const& cameraInfo) noexcept { SYSTRACE_CALL(); mHasShadowing = false; @@ -333,8 +333,8 @@ void FView::prepareShadowing(FEngine& engine, FScene::RenderableSoa& renderableD mNeedsShadowMap = any(shadowTechnique & ShadowMapManager::ShadowTechnique::SHADOW_MAP); } -void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaScope& arena, - filament::Viewport const& viewport, CameraInfo const& cameraInfo) noexcept { +void FView::prepareLighting(FEngine& engine, ArenaScope& arena, + CameraInfo const& cameraInfo) noexcept { SYSTRACE_CALL(); SYSTRACE_CONTEXT(); @@ -345,15 +345,8 @@ void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaSc * Dynamic lights */ - mHasDynamicLighting = scene->getLightData().size() > FScene::DIRECTIONAL_LIGHTS_COUNT; - if (mHasDynamicLighting) { + if (hasDynamicLighting()) { scene->prepareDynamicLights(cameraInfo, arena, mLightUbh); - Froxelizer& froxelizer = mFroxelizer; - if (froxelizer.prepare(driver, arena, viewport, - cameraInfo.projection, cameraInfo.zn, cameraInfo.zf)) { - // update our uniform buffer if needed - mPerViewUniforms.prepareDynamicLights(mFroxelizer); - } } // here the array of visible lights has been shrunk to CONFIG_MAX_LIGHT_COUNT @@ -364,7 +357,6 @@ void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaSc */ const float exposure = Exposure::exposure(cameraInfo.ev100); - mPerViewUniforms.prepareExposure(cameraInfo.ev100); /* @@ -382,7 +374,6 @@ void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaSc FSkybox const* const skybox = scene->getSkybox(); intensity = skybox ? skybox->getIntensity() : FIndirectLight::DEFAULT_INTENSITY; } - mPerViewUniforms.prepareAmbientLight(engine, *ibl, intensity, exposure); /* @@ -392,7 +383,6 @@ void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaSc FLightManager::Instance const directionalLight = lightData.elementAt(0); const float3 sceneSpaceDirection = lightData.elementAt(0); // guaranteed normalized mPerViewUniforms.prepareDirectionalLight(engine, exposure, sceneSpaceDirection, directionalLight); - mHasDirectionalLight = directionalLight.isValid(); } CameraInfo FView::computeCameraInfo(FEngine& engine) const noexcept { @@ -428,7 +418,7 @@ CameraInfo FView::computeCameraInfo(FEngine& engine) const noexcept { } void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena, - filament::Viewport const& viewport, CameraInfo const& cameraInfo, + filament::Viewport viewport, CameraInfo cameraInfo, float4 const& userTime, bool needsAlphaChannel) noexcept { SYSTRACE_CALL(); @@ -471,15 +461,23 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena, * Light culling: runs in parallel with Renderable culling (below) */ + JobSystem::Job* froxelizeLightsJob = nullptr; JobSystem::Job* prepareVisibleLightsJob = nullptr; if (scene->getLightData().size() > FScene::DIRECTIONAL_LIGHTS_COUNT) { + // create and start the prepareVisibleLights job + // note: this job updates LightData (non const) prepareVisibleLightsJob = js.runAndRetain(js.createJob(nullptr, - [&cullingFrustum, &engine, &arena, &cameraInfo, scene](JobSystem&, JobSystem::Job*) { + [&engine, &arena, &viewMatrix = cameraInfo.view, &cullingFrustum, + &lightData = scene->getLightData()] + (JobSystem&, JobSystem::Job*) { FView::prepareVisibleLights(engine.getLightManager(), arena, - cameraInfo.view, cullingFrustum, scene->getLightData()); + viewMatrix, cullingFrustum, lightData); })); } + // this is used later (in Renderer.cpp) to wait for froxelization to finishes + setFroxelizerSync(froxelizeLightsJob); + Range merged; FScene::RenderableSoa& renderableData = scene->getRenderableData(); @@ -505,7 +503,40 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena, if (prepareVisibleLightsJob) { js.waitAndRelease(prepareVisibleLightsJob); } - prepareShadowing(engine, renderableData, scene->getLightData(), cameraInfo); + + // lightData is const from this point on (can only happen after prepareVisibleLightsJob) + auto const& lightData = scene->getLightData(); + + // now we know if we have dynamic lighting (i.e.: dynamic lights are visible) + mHasDynamicLighting = lightData.size() > FScene::DIRECTIONAL_LIGHTS_COUNT; + + // we also know if we have a directional light + FLightManager::Instance const directionalLight = + lightData.elementAt(0); + mHasDirectionalLight = directionalLight.isValid(); + + // As soon as prepareVisibleLight finishes, we can kick-off the froxelization + if (hasDynamicLighting()) { + auto& froxelizer = mFroxelizer; + if (froxelizer.prepare(driver, arena, viewport, + cameraInfo.projection, cameraInfo.zn, cameraInfo.zf)) { + // TODO: might be more consistent to do this in prepareLighting(), but it's not + // strictly necessary + mPerViewUniforms.prepareDynamicLights(mFroxelizer); + } + // We need to pass viewMatrix by value here because it extends the scope of this + // function. + std::function froxelizerWork = + [&froxelizer = mFroxelizer, &engine, viewMatrix = cameraInfo.view, &lightData] + (JobSystem&, JobSystem::Job*) { + froxelizer.froxelizeLights(engine, viewMatrix, lightData); + }; + froxelizeLightsJob = js.runAndRetain(js.createJob(nullptr, std::move(froxelizerWork))); + } + + setFroxelizerSync(froxelizeLightsJob); + + prepareShadowing(engine, renderableData, lightData, cameraInfo); /* * Partition the SoA so that renderables are partitioned w.r.t their visibility into the @@ -602,7 +633,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena, * Relies on FScene::prepare() and prepareVisibleLights() */ - prepareLighting(engine, driver, arena, viewport, cameraInfo); + prepareLighting(engine, arena, cameraInfo); /* * Update driver state @@ -733,12 +764,6 @@ void FView::cleanupRenderPasses() const noexcept { mPerViewUniforms.unbindSamplers(); } -void FView::froxelize(FEngine& engine, mat4f const& viewMatrix) const noexcept { - SYSTRACE_CALL(); - assert_invariant(mHasDynamicLighting); - mFroxelizer.froxelizeLights(engine, viewMatrix, mScene->getLightData()); -} - void FView::commitUniforms(DriverApi& driver) const noexcept { mPerViewUniforms.commit(driver); } diff --git a/filament/src/details/View.h b/filament/src/details/View.h index 28e8cfbf3c..bbee179272 100644 --- a/filament/src/details/View.h +++ b/filament/src/details/View.h @@ -88,8 +88,10 @@ public: CameraInfo computeCameraInfo(FEngine& engine) const noexcept; + // note: viewport/cameraInfo are passed by value to make it clear that prepare cannot + // keep references on them that would outlive the scope of prepare() (e.g. with JobSystem). void prepare(FEngine& engine, backend::DriverApi& driver, ArenaScope& arena, - filament::Viewport const& viewport, CameraInfo const& cameraInfo, + filament::Viewport viewport, CameraInfo cameraInfo, math::float4 const& userTime, bool needsAlphaChannel) noexcept; void bindPerViewUniformsAndSamplers(FEngine::DriverApi& driver) const noexcept; @@ -141,9 +143,8 @@ public: const filament::Viewport& logicalViewport) const noexcept; void prepareShadowing(FEngine& engine, FScene::RenderableSoa& renderableData, - FScene::LightSoa& lightData, CameraInfo const& cameraInfo) noexcept; - void prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaScope& arena, - filament::Viewport const& viewport, CameraInfo const &cameraInfo) noexcept; + FScene::LightSoa const& lightData, CameraInfo const& cameraInfo) noexcept; + void prepareLighting(FEngine& engine, ArenaScope& arena, CameraInfo const& cameraInfo) noexcept; void prepareSSAO(backend::Handle ssao) const noexcept; void prepareSSR(backend::Handle ssr, float refractionLodOffset, @@ -153,10 +154,12 @@ public: void prepareShadowMapping(bool highPrecision) const noexcept; void cleanupRenderPasses() const noexcept; - void froxelize(FEngine& engine, math::mat4f const& viewMatrix) const noexcept; void commitUniforms(backend::DriverApi& driver) const noexcept; void commitFroxels(backend::DriverApi& driverApi) const noexcept; + utils::JobSystem::Job* getFroxelizerSync() const noexcept { return mFroxelizerSync; } + void setFroxelizerSync(utils::JobSystem::Job* sync) noexcept { mFroxelizerSync = sync; } + bool hasDirectionalLight() const noexcept { return mHasDirectionalLight; } bool hasDynamicLighting() const noexcept { return mHasDynamicLighting; } bool hasShadowing() const noexcept { return mHasShadowing; } @@ -474,6 +477,7 @@ private: FCamera* mViewingCamera = nullptr; mutable Froxelizer mFroxelizer; + utils::JobSystem::Job* mFroxelizerSync = nullptr; Viewport mViewport; bool mCulling = true;