diff --git a/Source/Editor/Viewport/Previews/AssetPreview.cs b/Source/Editor/Viewport/Previews/AssetPreview.cs index 78626be9c..2c797a1b3 100644 --- a/Source/Editor/Viewport/Previews/AssetPreview.cs +++ b/Source/Editor/Viewport/Previews/AssetPreview.cs @@ -171,7 +171,6 @@ namespace FlaxEditor.Viewport.Previews // EnvProbe = new EnvironmentProbe { - AutoUpdate = false, CustomProbe = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.DefaultSkyCubeTexture) }; // @@ -222,7 +221,7 @@ namespace FlaxEditor.Viewport.Previews } /// - public override bool HasLoadedAssets => base.HasLoadedAssets && Sky.HasContentLoaded && EnvProbe.Probe.IsLoaded && PostFxVolume.HasContentLoaded; + public override bool HasLoadedAssets => base.HasLoadedAssets && Sky.HasContentLoaded && EnvProbe.HasContentLoaded && PostFxVolume.HasContentLoaded; /// public override void OnDestroy() diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp index 44e3af4c6..a64fd3605 100644 --- a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp @@ -89,8 +89,7 @@ void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, SpanGetProbe()) { probe->SetupProbeData(params.RenderContext, &data.EnvironmentProbe); - const auto texture = probe->GetProbe()->GetTexture(); - context->BindSR(envProbeShaderRegisterIndex, GET_TEXTURE_VIEW_SAFE(texture)); + context->BindSR(envProbeShaderRegisterIndex, probe->GetProbe()); } else { diff --git a/Source/Engine/Level/Actors/EnvironmentProbe.cpp b/Source/Engine/Level/Actors/EnvironmentProbe.cpp index 746d87679..3ce8c8102 100644 --- a/Source/Engine/Level/Actors/EnvironmentProbe.cpp +++ b/Source/Engine/Level/Actors/EnvironmentProbe.cpp @@ -4,6 +4,7 @@ #include "Engine/Platform/FileSystem.h" #include "Engine/Graphics/RenderView.h" #include "Engine/Graphics/RenderTask.h" +#include "Engine/Graphics/Textures/GPUTexture.h" #include "Engine/Graphics/Textures/TextureData.h" #include "Engine/Renderer/RenderList.h" #include "Engine/Renderer/ProbesRenderer.h" @@ -13,18 +14,21 @@ #include "Engine/ContentImporters/AssetsImportingManager.h" #include "Engine/Serialization/Serialization.h" #include "Engine/Level/Scene/Scene.h" -#include "Engine/Graphics/Graphics.h" EnvironmentProbe::EnvironmentProbe(const SpawnParams& params) : Actor(params) , _radius(3000.0f) , _isUsingCustomProbe(false) - , _probe(nullptr) { _sphere = BoundingSphere(Vector3::Zero, _radius); BoundingBox::FromSphere(_sphere, _box); } +EnvironmentProbe::~EnvironmentProbe() +{ + SAFE_DELETE_GPU_RESOURCE(_probeTexture); +} + float EnvironmentProbe::GetRadius() const { return _radius; @@ -45,19 +49,9 @@ float EnvironmentProbe::GetScaledRadius() const return _radius * _transform.Scale.MaxValue(); } -bool EnvironmentProbe::HasProbe() const +GPUTexture* EnvironmentProbe::GetProbe() const { - return _probe != nullptr; -} - -bool EnvironmentProbe::HasProbeLoaded() const -{ - return _probe != nullptr && _probe->IsLoaded(); -} - -CubeTexture* EnvironmentProbe::GetProbe() const -{ - return _probe; + return _probe ? _probe->GetTexture() : _probeTexture; } bool EnvironmentProbe::IsUsingCustomProbe() const @@ -74,9 +68,7 @@ void EnvironmentProbe::SetupProbeData(const RenderContext& renderContext, ProbeD CubeTexture* EnvironmentProbe::GetCustomProbe() const { - if (IsUsingCustomProbe()) - return GetProbe(); - return nullptr; + return _isUsingCustomProbe ? _probe : nullptr; } void EnvironmentProbe::SetCustomProbe(CubeTexture* probe) @@ -99,6 +91,34 @@ void EnvironmentProbe::Bake(float timeout) #endif } +void EnvironmentProbe::SetProbeData(GPUContext* context, GPUTexture* data) +{ + // Remove any probe asset + _isUsingCustomProbe = false; + _probe = nullptr; + + // Allocate probe texture manually + if (!_probeTexture) + { + _probeTexture = GPUTexture::New(); +#if !BUILD_RELEASE + _probeTexture->SetName(GetNamePath()); +#endif + } + if (_probeTexture->Width() != data->Width() || _probeTexture->Format() != data->Format()) + { + auto desc = data->GetDescription(); + desc.Usage = GPUResourceUsage::Default; + desc.Flags = GPUTextureFlags::ShaderResource; + if (_probeTexture->Init(desc)) + return; + _probeTexture->SetResidentMipLevels(_probeTexture->MipLevels()); + } + + // Copy probe texture data + context->CopyResource(_probeTexture, data); +} + void EnvironmentProbe::SetProbeData(TextureData& data) { // Remove custom probe (if used) @@ -109,7 +129,6 @@ void EnvironmentProbe::SetProbeData(TextureData& data) } Guid id = Guid::New(); - #if COMPILE_WITH_ASSETS_IMPORTER // Create asset file const String path = GetScene()->GetDataFolderPath() / TEXT("EnvProbes/") / GetID().ToString(Guid::FormatType::N) + ASSET_FILES_EXTENSION_WITH_DOT; @@ -121,6 +140,9 @@ void EnvironmentProbe::SetProbeData(TextureData& data) LOG(Error, "Cannot import generated env probe!"); return; } + + // Remove probe texture + SAFE_DELETE_GPU_RESOURCE(_probeTexture); #else // TODO: create or reuse virtual texture and use it LOG(Error, "Changing probes at runtime in game is not supported."); @@ -151,10 +173,16 @@ void EnvironmentProbe::Draw(RenderContext& renderContext) { if (Brightness > ZeroTolerance && (renderContext.View.Flags & ViewFlags::Reflections) != 0 && - renderContext.View.Pass & DrawPass::GBuffer && - HasProbeLoaded()) + renderContext.View.Pass & DrawPass::GBuffer) { - renderContext.List->EnvironmentProbes.Add(this); +#if COMPILE_WITH_PROBES_BAKING + if (UpdateMode == ProbeUpdateMode::Realtime) + ProbesRenderer::Bake(this, 0.0f); +#endif + if ((_probe != nullptr && _probe->IsLoaded()) || _probeTexture != nullptr) + { + renderContext.List->EnvironmentProbes.Add(this); + } } } @@ -189,7 +217,7 @@ void EnvironmentProbe::Serialize(SerializeStream& stream, const void* otherObj) SERIALIZE_MEMBER(Radius, _radius); SERIALIZE(CubemapResolution); SERIALIZE(Brightness); - SERIALIZE(AutoUpdate); + SERIALIZE(UpdateMode); SERIALIZE(CaptureNearPlane); SERIALIZE_MEMBER(IsCustomProbe, _isUsingCustomProbe); SERIALIZE_MEMBER(ProbeID, _probe); @@ -203,10 +231,18 @@ void EnvironmentProbe::Deserialize(DeserializeStream& stream, ISerializeModifier DESERIALIZE_MEMBER(Radius, _radius); DESERIALIZE(CubemapResolution); DESERIALIZE(Brightness); - DESERIALIZE(AutoUpdate); + DESERIALIZE(UpdateMode); DESERIALIZE(CaptureNearPlane); DESERIALIZE_MEMBER(IsCustomProbe, _isUsingCustomProbe); DESERIALIZE_MEMBER(ProbeID, _probe); + + // [Deprecated on 18.07.2022, expires on 18.07.2022] + if (modifier->EngineBuild <= 6332) + { + const auto member = stream.FindMember("AutoUpdate"); + if (member != stream.MemberEnd() && member->value.IsBool() && member->value.GetBool()) + UpdateMode = ProbeUpdateMode::WhenMoved; + } } bool EnvironmentProbe::HasContentLoaded() const @@ -248,7 +284,7 @@ void EnvironmentProbe::OnTransformChanged() UpdateBounds(); - if (AutoUpdate && IsDuringPlay()) + if (IsDuringPlay() && UpdateMode == ProbeUpdateMode::WhenMoved) { Bake(1.0f); } diff --git a/Source/Engine/Level/Actors/EnvironmentProbe.h b/Source/Engine/Level/Actors/EnvironmentProbe.h index cf3bc2874..9284ba328 100644 --- a/Source/Engine/Level/Actors/EnvironmentProbe.h +++ b/Source/Engine/Level/Actors/EnvironmentProbe.h @@ -13,11 +13,29 @@ API_CLASS() class FLAXENGINE_API EnvironmentProbe : public Actor { DECLARE_SCENE_OBJECT(EnvironmentProbe); +public: + /// + /// The environment probe update mode. + /// + API_ENUM() enum class ProbeUpdateMode + { + // Probe can be updated manually (eg. in Editor or from script). + Manual = 0, + // Probe will be automatically updated when is moved. + WhenMoved = 1, + // Probe will be automatically updated in real-time (only if in view and frequency depending on distance to the camera). + Realtime = 2, + }; + private: float _radius; bool _isUsingCustomProbe; int32 _sceneRenderingKey = -1; AssetReference _probe; + GPUTexture* _probeTexture = nullptr; + +public: + ~EnvironmentProbe(); public: /// @@ -33,10 +51,10 @@ public: float Brightness = 1.0f; /// - /// Value indicating if probe should be updated automatically on change. + /// The probe update mode. /// API_FIELD(Attributes="EditorOrder(30), EditorDisplay(\"Probe\")") - bool AutoUpdate = false; + ProbeUpdateMode UpdateMode = ProbeUpdateMode::Manual; /// /// The probe capture camera near plane distance. @@ -61,20 +79,10 @@ public: /// API_PROPERTY() float GetScaledRadius() const; - /// - /// Returns true if env probe has cube texture assigned. - /// - API_PROPERTY() bool HasProbe() const; - - /// - /// Returns true if env probe has cube texture assigned. - /// - API_PROPERTY() bool HasProbeLoaded() const; - /// /// Gets the probe texture used during rendering (baked or custom one). /// - API_PROPERTY() CubeTexture* GetProbe() const; + API_PROPERTY() GPUTexture* GetProbe() const; /// /// True if probe is using custom cube texture (not baked). @@ -108,7 +116,14 @@ public: API_FUNCTION() void Bake(float timeout = 0); /// - /// Action fired when probe has been baked. + /// Action fired when probe has been baked. Copies data to the texture memory (GPU-only for real-time probes). + /// + /// The GPU context to use for probe data copying. + /// The new probe data (GPU texture). + void SetProbeData(GPUContext* context, GPUTexture* data); + + /// + /// Action fired when probe has been baked. Imports data to the texture asset (virtual if running in game). /// /// The new probe data. void SetProbeData(TextureData& data); diff --git a/Source/Engine/Renderer/ProbesRenderer.cpp b/Source/Engine/Renderer/ProbesRenderer.cpp index 26832871a..2ad951024 100644 --- a/Source/Engine/Renderer/ProbesRenderer.cpp +++ b/Source/Engine/Renderer/ProbesRenderer.cpp @@ -157,7 +157,8 @@ void ProbesRenderer::Bake(EnvironmentProbe* probe, float timeout) _probesToBake.Add(e); // Fire event - OnRegisterBake(e); + if (e.UseTextureData()) + OnRegisterBake(e); } void ProbesRenderer::Bake(SkyLight* probe, float timeout) @@ -183,7 +184,21 @@ void ProbesRenderer::Bake(SkyLight* probe, float timeout) _probesToBake.Add(e); // Fire event - OnRegisterBake(e); + if (e.UseTextureData()) + OnRegisterBake(e); +} + +bool ProbesRenderer::Entry::UseTextureData() const +{ + if (Type == EntryType::EnvProbe && Actor) + { + switch (Actor.As()->UpdateMode) + { + case EnvironmentProbe::ProbeUpdateMode::Realtime: + return false; + } + } + return true; } int32 ProbesRenderer::Entry::GetResolution() const @@ -358,7 +373,7 @@ void ProbesRendererService::Update() texture = _probe; break; } - ASSERT(texture); + ASSERT(texture && _current.UseTextureData()); auto taskB = New(texture, _current); auto taskA = texture->DownloadDataAsync(taskB->GetData()); if (taskA == nullptr) @@ -462,7 +477,6 @@ void ProbesRenderer::onRender(RenderTask* task, GPUContext* context) if (_current.Type == EntryType::EnvProbe) { auto envProbe = (EnvironmentProbe*)_current.Actor.Get(); - LOG(Info, "Updating Env probe '{0}' (resolution: {1})...", envProbe->ToString(), probeResolution); Vector3 position = envProbe->GetPosition(); float radius = envProbe->GetScaledRadius(); float nearPlane = Math::Max(0.1f, envProbe->CaptureNearPlane); @@ -479,7 +493,6 @@ void ProbesRenderer::onRender(RenderTask* task, GPUContext* context) else if (_current.Type == EntryType::SkyLight) { auto skyLight = (SkyLight*)_current.Actor.Get(); - LOG(Info, "Updating sky light '{0}' (resolution: {1})...", skyLight->ToString(), probeResolution); Vector3 position = skyLight->GetPosition(); float nearPlane = 10.0f; float farPlane = Math::Max(nearPlane + 1000.0f, skyLight->SkyDistanceThreshold * 2.0f); @@ -575,6 +588,19 @@ void ProbesRenderer::onRender(RenderTask* task, GPUContext* context) // Mark as rendered _updateFrameNumber = Engine::FrameCount; _task->Enabled = false; + + // Real-time probes don't use TextureData (for streaming) but copy generated probe directly to GPU memory + if (!_current.UseTextureData()) + { + if (_current.Type == EntryType::EnvProbe && _current.Actor) + { + _current.Actor.As()->SetProbeData(context, _probe); + } + + // Clear flag + _updateFrameNumber = 0; + _current.Type = EntryType::Invalid; + } } #endif diff --git a/Source/Engine/Renderer/ProbesRenderer.h b/Source/Engine/Renderer/ProbesRenderer.h index 3de952db4..a4ab131c8 100644 --- a/Source/Engine/Renderer/ProbesRenderer.h +++ b/Source/Engine/Renderer/ProbesRenderer.h @@ -46,6 +46,7 @@ public: Timeout = other.Timeout; } + bool UseTextureData() const; int32 GetResolution() const; }; diff --git a/Source/Engine/Renderer/ReflectionsPass.cpp b/Source/Engine/Renderer/ReflectionsPass.cpp index 6ab22cf49..f453e1a41 100644 --- a/Source/Engine/Renderer/ReflectionsPass.cpp +++ b/Source/Engine/Renderer/ReflectionsPass.cpp @@ -412,8 +412,6 @@ void ReflectionsPass::Render(RenderContext& renderContext, GPUTextureView* light { // Cache data auto probe = renderContext.List->EnvironmentProbes[probeIndex]; - if (!probe->HasProbeLoaded()) - continue; float probeRadius = probe->GetScaledRadius(); Float3 probePosition = probe->GetPosition() - renderContext.View.Origin; @@ -436,7 +434,7 @@ void ReflectionsPass::Render(RenderContext& renderContext, GPUTextureView* light // Render reflections context->UpdateCB(cb, &data); context->BindCB(0, cb); - context->BindSR(4, probe->GetProbe()->GetTexture()); + context->BindSR(4, probe->GetProbe()); context->SetState(isViewInside ? _psProbeInverted : _psProbeNormal); _sphereModel->Render(context);