Merge branch 'development' into Atom/jromnoa/assert-for-screenshot-comparisons
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Component/ComponentApplicationLifecycle.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/NativeUI/NativeUIRequests.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
@@ -132,7 +133,6 @@ namespace AZ
|
||||
m_createDefaultScene = false;
|
||||
}
|
||||
|
||||
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
|
||||
TickBus::Handler::BusConnect();
|
||||
|
||||
// Listen for window system requests (e.g. requests for default window handle)
|
||||
@@ -143,6 +143,20 @@ namespace AZ
|
||||
|
||||
Render::Bootstrap::DefaultWindowBus::Handler::BusConnect();
|
||||
Render::Bootstrap::RequestBus::Handler::BusConnect();
|
||||
|
||||
// If the settings registry isn't available, something earlier in startup will report that failure.
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
// Automatically register the event if it's not registered, because
|
||||
// this system is initialized before the settings registry has loaded the event list.
|
||||
AZ::ComponentApplicationLifecycle::RegisterHandler(
|
||||
*settingsRegistry, m_componentApplicationLifecycleHandler,
|
||||
[this](AZStd::string_view /*path*/, AZ::SettingsRegistryInterface::Type /*type*/)
|
||||
{
|
||||
Initialize();
|
||||
},
|
||||
"LegacySystemInterfaceCreated");
|
||||
}
|
||||
}
|
||||
|
||||
void BootstrapSystemComponent::Deactivate()
|
||||
@@ -153,7 +167,6 @@ namespace AZ
|
||||
AzFramework::WindowSystemRequestBus::Handler::BusDisconnect();
|
||||
AzFramework::WindowSystemNotificationBus::Handler::BusDisconnect();
|
||||
TickBus::Handler::BusDisconnect();
|
||||
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
|
||||
|
||||
m_brdfTexture = nullptr;
|
||||
RemoveRenderPipeline();
|
||||
@@ -164,14 +177,14 @@ namespace AZ
|
||||
m_windowHandle = nullptr;
|
||||
}
|
||||
|
||||
void BootstrapSystemComponent::OnCatalogLoaded(const char* /*catalogFile*/)
|
||||
void BootstrapSystemComponent::Initialize()
|
||||
{
|
||||
if (m_isAssetCatalogLoaded)
|
||||
if (m_isInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_isAssetCatalogLoaded = true;
|
||||
m_isInitialized = true;
|
||||
|
||||
if (!RPI::RPISystemInterface::Get()->IsInitialized())
|
||||
{
|
||||
@@ -216,7 +229,7 @@ namespace AZ
|
||||
{
|
||||
m_windowHandle = windowHandle;
|
||||
|
||||
if (m_isAssetCatalogLoaded)
|
||||
if (m_isInitialized)
|
||||
{
|
||||
CreateWindowContext();
|
||||
if (m_createDefaultScene)
|
||||
@@ -259,6 +272,7 @@ namespace AZ
|
||||
|
||||
// Create and register a scene with all available feature processors
|
||||
RPI::SceneDescriptor sceneDesc;
|
||||
sceneDesc.m_nameId = AZ::Name("Main");
|
||||
AZ::RPI::ScenePtr atomScene = RPI::Scene::CreateScene(sceneDesc);
|
||||
atomScene->EnableAllFeatureProcessors();
|
||||
atomScene->Activate();
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
||||
|
||||
#include <AzFramework/Asset/AssetCatalogBus.h>
|
||||
#include <AzFramework/Scene/Scene.h>
|
||||
#include <AzFramework/Scene/SceneSystemInterface.h>
|
||||
#include <AzFramework/Windowing/NativeWindow.h>
|
||||
@@ -29,7 +29,6 @@
|
||||
#include <Atom/Bootstrap/DefaultWindowBus.h>
|
||||
#include <Atom/Bootstrap/BootstrapRequestBus.h>
|
||||
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
@@ -40,7 +39,6 @@ namespace AZ
|
||||
: public Component
|
||||
, public TickBus::Handler
|
||||
, public AzFramework::WindowNotificationBus::Handler
|
||||
, public AzFramework::AssetCatalogEventBus::Handler
|
||||
, public AzFramework::WindowSystemNotificationBus::Handler
|
||||
, public AzFramework::WindowSystemRequestBus::Handler
|
||||
, public Render::Bootstrap::DefaultWindowBus::Handler
|
||||
@@ -82,13 +80,12 @@ namespace AZ
|
||||
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
|
||||
int GetTickOrder() override;
|
||||
|
||||
// AzFramework::AssetCatalogEventBus::Handler overrides ...
|
||||
void OnCatalogLoaded(const char* catalogFile) override;
|
||||
|
||||
// AzFramework::WindowSystemNotificationBus::Handler overrides ...
|
||||
void OnWindowCreated(AzFramework::NativeWindowHandle windowHandle) override;
|
||||
|
||||
private:
|
||||
void Initialize();
|
||||
|
||||
void CreateDefaultRenderPipeline();
|
||||
void CreateDefaultScene();
|
||||
void DestroyDefaultScene();
|
||||
@@ -105,7 +102,7 @@ namespace AZ
|
||||
RPI::ScenePtr m_defaultScene = nullptr;
|
||||
AZStd::shared_ptr<AzFramework::Scene> m_defaultFrameworkScene = nullptr;
|
||||
|
||||
bool m_isAssetCatalogLoaded = false;
|
||||
bool m_isInitialized = false;
|
||||
|
||||
// The id of the render pipeline created by this component
|
||||
RPI::RenderPipelineId m_renderPipelineId;
|
||||
@@ -119,6 +116,8 @@ namespace AZ
|
||||
|
||||
// Maps AZ scenes to RPI scene weak pointers to allow looking up a ScenePtr instead of a raw Scene*
|
||||
AZStd::unordered_map<AzFramework::Scene*, AZStd::weak_ptr<AZ::RPI::Scene>> m_azSceneToAtomSceneMap;
|
||||
|
||||
AZ::SettingsRegistryInterface::NotifyEventHandler m_componentApplicationLifecycleHandler;
|
||||
};
|
||||
} // namespace Bootstrap
|
||||
} // namespace Render
|
||||
|
||||
@@ -1574,15 +1574,6 @@
|
||||
"shaderOption": "o_baseColor_useTexture"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "UseTexture",
|
||||
"args": {
|
||||
"textureProperty": "metallic.textureMap",
|
||||
"useTextureProperty": "metallic.useTexture",
|
||||
"dependentProperties": ["metallic.textureMapUv"],
|
||||
"shaderOption": "o_metallic_useTexture"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "UseTexture",
|
||||
"args": {
|
||||
@@ -1649,6 +1640,12 @@
|
||||
"file": "StandardPBR_Roughness.lua"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Lua",
|
||||
"args": {
|
||||
"file": "StandardPBR_Metallic.lua"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Lua",
|
||||
"args": {
|
||||
|
||||
+17
-29
@@ -2698,16 +2698,12 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "UseTexture",
|
||||
"type": "Lua",
|
||||
"args": {
|
||||
"textureProperty": "layer1_metallic.textureMap",
|
||||
"useTextureProperty": "layer1_metallic.useTexture",
|
||||
"dependentProperties": ["layer1_metallic.textureMapUv"],
|
||||
"shaderTags": [
|
||||
"ForwardPass",
|
||||
"ForwardPass_EDS"
|
||||
],
|
||||
"shaderOption": "o_layer1_o_metallic_useTexture"
|
||||
"file": "StandardPBR_Metallic.lua",
|
||||
"propertyNamePrefix": "layer1_",
|
||||
"srgNamePrefix": "m_layer1_",
|
||||
"optionsNamePrefix": "o_layer1_"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2835,16 +2831,12 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "UseTexture",
|
||||
"type": "Lua",
|
||||
"args": {
|
||||
"textureProperty": "layer2_metallic.textureMap",
|
||||
"useTextureProperty": "layer2_metallic.useTexture",
|
||||
"dependentProperties": ["layer2_metallic.textureMapUv"],
|
||||
"shaderTags": [
|
||||
"ForwardPass",
|
||||
"ForwardPass_EDS"
|
||||
],
|
||||
"shaderOption": "o_layer2_o_metallic_useTexture"
|
||||
"file": "StandardPBR_Metallic.lua",
|
||||
"propertyNamePrefix": "layer2_",
|
||||
"srgNamePrefix": "m_layer2_",
|
||||
"optionsNamePrefix": "o_layer2_"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2963,8 +2955,8 @@
|
||||
"args": {
|
||||
"textureProperty": "layer3_baseColor.textureMap",
|
||||
"useTextureProperty": "layer3_baseColor.useTexture",
|
||||
"dependentProperties": ["layer3_baseColor.textureMapUv", "layer3_baseColor.textureBlendMode"],
|
||||
"shaderTags": [
|
||||
"dependentProperties": [ "layer3_baseColor.textureMapUv", "layer3_baseColor.textureBlendMode" ],
|
||||
"shaderTags": [
|
||||
"ForwardPass",
|
||||
"ForwardPass_EDS"
|
||||
],
|
||||
@@ -2972,16 +2964,12 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "UseTexture",
|
||||
"type": "Lua",
|
||||
"args": {
|
||||
"textureProperty": "layer3_metallic.textureMap",
|
||||
"useTextureProperty": "layer3_metallic.useTexture",
|
||||
"dependentProperties": ["layer3_metallic.textureMapUv"],
|
||||
"shaderTags": [
|
||||
"ForwardPass",
|
||||
"ForwardPass_EDS"
|
||||
],
|
||||
"shaderOption": "o_layer3_o_metallic_useTexture"
|
||||
"file": "StandardPBR_Metallic.lua",
|
||||
"propertyNamePrefix": "layer3_",
|
||||
"srgNamePrefix": "m_layer3_",
|
||||
"optionsNamePrefix": "o_layer3_"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1104,15 +1104,6 @@
|
||||
"shaderOption": "o_baseColor_useTexture"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "UseTexture",
|
||||
"args": {
|
||||
"textureProperty": "metallic.textureMap",
|
||||
"useTextureProperty": "metallic.useTexture",
|
||||
"dependentProperties": ["metallic.textureMapUv"],
|
||||
"shaderOption": "o_metallic_useTexture"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "UseTexture",
|
||||
"args": {
|
||||
@@ -1179,6 +1170,12 @@
|
||||
"file": "StandardPBR_Roughness.lua"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Lua",
|
||||
"args": {
|
||||
"file": "StandardPBR_Metallic.lua"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Lua",
|
||||
"args": {
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
--------------------------------------------------------------------------------------
|
||||
--
|
||||
-- Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
-- For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
--
|
||||
-- SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
--
|
||||
--
|
||||
--
|
||||
----------------------------------------------------------------------------------------------------
|
||||
|
||||
function GetMaterialPropertyDependencies()
|
||||
return {"metallic.textureMap", "metallic.useTexture"}
|
||||
end
|
||||
|
||||
function GetShaderOptionDependencies()
|
||||
return {"o_metallic_useTexture"}
|
||||
end
|
||||
|
||||
function Process(context)
|
||||
local textureMap = context:GetMaterialPropertyValue_Image("metallic.textureMap")
|
||||
local useTexture = context:GetMaterialPropertyValue_bool("metallic.useTexture")
|
||||
context:SetShaderOptionValue_bool("o_metallic_useTexture", useTexture and textureMap ~= nil)
|
||||
end
|
||||
|
||||
function ProcessEditor(context)
|
||||
local textureMap = context:GetMaterialPropertyValue_Image("metallic.textureMap")
|
||||
local useTexture = context:GetMaterialPropertyValue_bool("metallic.useTexture")
|
||||
|
||||
if(nil == textureMap) then
|
||||
context:SetMaterialPropertyVisibility("metallic.useTexture", MaterialPropertyVisibility_Hidden)
|
||||
context:SetMaterialPropertyVisibility("metallic.textureMapUv", MaterialPropertyVisibility_Hidden)
|
||||
context:SetMaterialPropertyVisibility("metallic.factor", MaterialPropertyVisibility_Enabled)
|
||||
elseif(not useTexture) then
|
||||
context:SetMaterialPropertyVisibility("metallic.useTexture", MaterialPropertyVisibility_Enabled)
|
||||
context:SetMaterialPropertyVisibility("metallic.textureMapUv", MaterialPropertyVisibility_Disabled)
|
||||
context:SetMaterialPropertyVisibility("metallic.factor", MaterialPropertyVisibility_Enabled)
|
||||
else
|
||||
context:SetMaterialPropertyVisibility("metallic.useTexture", MaterialPropertyVisibility_Enabled)
|
||||
context:SetMaterialPropertyVisibility("metallic.textureMapUv", MaterialPropertyVisibility_Enabled)
|
||||
context:SetMaterialPropertyVisibility("metallic.factor", MaterialPropertyVisibility_Hidden)
|
||||
end
|
||||
end
|
||||
@@ -19,7 +19,6 @@
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/EditContextConstants.inl>
|
||||
#include <AzCore/Script/ScriptAsset.h>
|
||||
|
||||
#include <AssetBuilderSDK/AssetBuilderSDK.h>
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
@@ -101,13 +100,6 @@ namespace AZ
|
||||
materialFunctorRegistration->RegisterMaterialFunctor("ConvertEmissiveUnit", azrtti_typeid<ConvertEmissiveUnitFunctorSourceData>());
|
||||
materialFunctorRegistration->RegisterMaterialFunctor("HandleSubsurfaceScatteringParameters", azrtti_typeid<SubsurfaceTransmissionParameterFunctorSourceData>());
|
||||
materialFunctorRegistration->RegisterMaterialFunctor("Lua", azrtti_typeid<RPI::LuaMaterialFunctorSourceData>());
|
||||
|
||||
// Add asset types and extensions to AssetCatalog. Uses "AssetCatalogService".
|
||||
auto assetCatalog = AZ::Data::AssetCatalogRequestBus::FindFirstHandler();
|
||||
if (assetCatalog)
|
||||
{
|
||||
assetCatalog->EnableCatalogForAsset(AZ::AzTypeInfo<AZ::ScriptAsset>::Uuid());
|
||||
}
|
||||
}
|
||||
|
||||
void EditorCommonSystemComponent::Deactivate()
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
#include <AzCore/Script/ScriptContextAttributes.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <AzCore/Task/TaskGraph.h>
|
||||
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
|
||||
@@ -50,6 +52,15 @@ namespace AZ
|
||||
"Sets the compression level for saving png screenshots. Valid values are from 0 to 8"
|
||||
);
|
||||
|
||||
AZ_CVAR(int,
|
||||
r_pngCompressionNumThreads,
|
||||
8, // Number of threads to use for the png r<->b channel data swap
|
||||
nullptr,
|
||||
ConsoleFunctorFlags::Null,
|
||||
"Sets the number of threads for saving png screenshots. Valid values are from 1 to 128, although less than or equal the number of hw threads is recommended"
|
||||
);
|
||||
|
||||
|
||||
FrameCaptureOutputResult PngFrameCaptureOutput(
|
||||
const AZStd::string& outputFilePath, const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult)
|
||||
{
|
||||
@@ -65,33 +76,67 @@ namespace AZ
|
||||
|
||||
buffer = AZStd::make_shared<AZStd::vector<uint8_t>>(readbackResult.m_dataBuffer->size());
|
||||
AZStd::copy(readbackResult.m_dataBuffer->begin(), readbackResult.m_dataBuffer->end(), buffer->begin());
|
||||
|
||||
AZ::JobCompletion jobCompletion;
|
||||
const int numThreads = 8;
|
||||
const int numThreads = r_pngCompressionNumThreads;
|
||||
const int numPixelsPerThread = static_cast<int>(buffer->size() / numChannels / numThreads);
|
||||
for (int i = 0; i < numThreads; ++i)
|
||||
|
||||
AZ::TaskGraphActiveInterface* taskGraphActiveInterface = AZ::Interface<AZ::TaskGraphActiveInterface>::Get();
|
||||
bool taskGraphActive = taskGraphActiveInterface && taskGraphActiveInterface->IsTaskGraphActive();
|
||||
|
||||
if (taskGraphActive)
|
||||
{
|
||||
int startPixel = i * numPixelsPerThread;
|
||||
static const AZ::TaskDescriptor pngTaskDescriptor{"PngWriteOutChannelSwap", "Graphics"};
|
||||
AZ::TaskGraph taskGraph;
|
||||
for (int i = 0; i < numThreads; ++i)
|
||||
{
|
||||
int startPixel = i * numPixelsPerThread;
|
||||
|
||||
AZ::Job* job = AZ::CreateJobFunction(
|
||||
[&, startPixel, numPixelsPerThread]()
|
||||
{
|
||||
for (int pixelOffset = 0; pixelOffset < numPixelsPerThread; ++pixelOffset)
|
||||
taskGraph.AddTask(
|
||||
pngTaskDescriptor,
|
||||
[&, startPixel]()
|
||||
{
|
||||
if (startPixel * numChannels + numChannels < buffer->size())
|
||||
for (int pixelOffset = 0; pixelOffset < numPixelsPerThread; ++pixelOffset)
|
||||
{
|
||||
AZStd::swap(
|
||||
buffer->data()[(startPixel + pixelOffset) * numChannels],
|
||||
buffer->data()[(startPixel + pixelOffset) * numChannels + 2]
|
||||
);
|
||||
if (startPixel * numChannels + numChannels < buffer->size())
|
||||
{
|
||||
AZStd::swap(
|
||||
buffer->data()[(startPixel + pixelOffset) * numChannels],
|
||||
buffer->data()[(startPixel + pixelOffset) * numChannels + 2]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, true, nullptr);
|
||||
|
||||
job->SetDependent(&jobCompletion);
|
||||
job->Start();
|
||||
});
|
||||
}
|
||||
AZ::TaskGraphEvent taskGraphFinishedEvent;
|
||||
taskGraph.Submit(&taskGraphFinishedEvent);
|
||||
taskGraphFinishedEvent.Wait();
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ::JobCompletion jobCompletion;
|
||||
for (int i = 0; i < numThreads; ++i)
|
||||
{
|
||||
int startPixel = i * numPixelsPerThread;
|
||||
|
||||
AZ::Job* job = AZ::CreateJobFunction(
|
||||
[&, startPixel]()
|
||||
{
|
||||
for (int pixelOffset = 0; pixelOffset < numPixelsPerThread; ++pixelOffset)
|
||||
{
|
||||
if (startPixel * numChannels + numChannels < buffer->size())
|
||||
{
|
||||
AZStd::swap(
|
||||
buffer->data()[(startPixel + pixelOffset) * numChannels],
|
||||
buffer->data()[(startPixel + pixelOffset) * numChannels + 2]
|
||||
);
|
||||
}
|
||||
}
|
||||
}, true, nullptr);
|
||||
|
||||
job->SetDependent(&jobCompletion);
|
||||
job->Start();
|
||||
}
|
||||
jobCompletion.StartAndWaitForCompletion();
|
||||
}
|
||||
jobCompletion.StartAndWaitForCompletion();
|
||||
}
|
||||
|
||||
Utils::PngFile image = Utils::PngFile::Create(readbackResult.m_imageDescriptor.m_size, format, *buffer);
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace AZ
|
||||
{
|
||||
if (m_rtPipeline)
|
||||
{
|
||||
AZ::RPI::RPISystemInterface::Get()->GetDefaultScene()->RemoveRenderPipeline(m_rtPipeline->GetId());
|
||||
m_rtPipeline->RemoveFromScene();
|
||||
m_rtPipeline = nullptr;
|
||||
}
|
||||
|
||||
@@ -111,8 +111,12 @@ namespace AZ
|
||||
parentPass->SetSourceTexture(m_texture, RHI::Format::R8G8B8A8_UNORM);
|
||||
break;
|
||||
}
|
||||
|
||||
AZ::RPI::RPISystemInterface::Get()->GetDefaultScene()->AddRenderPipeline(m_rtPipeline);
|
||||
|
||||
const auto mainScene = AZ::RPI::RPISystemInterface::Get()->GetSceneByName(AZ::Name("RPI"));
|
||||
if (mainScene)
|
||||
{
|
||||
mainScene->AddRenderPipeline(m_rtPipeline);
|
||||
}
|
||||
}
|
||||
|
||||
bool LuxCoreTexture::IsIBLTexture()
|
||||
|
||||
@@ -60,6 +60,11 @@ def activate_look_modification_lut(look_modification_component, asset_relative_p
|
||||
LOOK_MODIFICATION_ENABLE_PROPERTY_PATH,
|
||||
True
|
||||
)
|
||||
azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast,
|
||||
"EnableComponents",
|
||||
[look_modification_component]
|
||||
)
|
||||
|
||||
def activate_lut_asset(entity_id, asset_relative_path):
|
||||
disable_hdr_color_grading_component(entity_id)
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
namespace AZ
|
||||
{
|
||||
class Job;
|
||||
class TaskGraphActiveInterface;
|
||||
class TaskGraph;
|
||||
|
||||
namespace RHI
|
||||
{
|
||||
@@ -256,7 +258,13 @@ namespace AZ
|
||||
//! Must be called between BeginCulling() and EndCulling(), once for each active scene/view pair.
|
||||
//! Will create child jobs under the parentJob to do the processing in parallel.
|
||||
//! Can be called in parallel (i.e. to perform culling on multiple views at the same time).
|
||||
void ProcessCullables(const Scene& scene, View& view, AZ::Job& parentJob);
|
||||
void ProcessCullablesJobs(const Scene& scene, View& view, AZ::Job& parentJob);
|
||||
|
||||
//! Performs render culling and lod selection for a View, then adds the visible renderpackets to that View.
|
||||
//! Must be called between BeginCulling() and EndCulling(), once for each active scene/view pair.
|
||||
//! Will create child task graphs that signal the TaskGraphEvent to do the processing in parallel.
|
||||
//! Can be called in parallel (i.e. to perform culling on multiple views at the same time).
|
||||
void ProcessCullablesTG(const Scene& scene, View& view, AZ::TaskGraph& taskGraph);
|
||||
|
||||
//! Adds a Cullable to the underlying visibility system(s).
|
||||
//! Must be called at least once on initialization and whenever a Cullable's position or bounds is changed.
|
||||
@@ -276,17 +284,20 @@ namespace AZ
|
||||
return m_debugCtx;
|
||||
}
|
||||
|
||||
static const size_t WorkListCapacity = 5;
|
||||
using WorkListType = AZStd::fixed_vector<AzFramework::IVisibilityScene::NodeData, WorkListCapacity>;
|
||||
|
||||
protected:
|
||||
size_t CountObjectsInScene();
|
||||
|
||||
private:
|
||||
void BeginCullingTaskGraph(const AZStd::vector<ViewPtr>& views);
|
||||
void BeginCullingJobs(const AZStd::vector<ViewPtr>& views);
|
||||
void ProcessCullablesCommon(const Scene& scene, View& view, AZ::Frustum& frustum, void*& maskedOcclusionCulling);
|
||||
|
||||
const Scene* m_parentScene = nullptr;
|
||||
AzFramework::IVisibilityScene* m_visScene = nullptr;
|
||||
CullingDebugContext m_debugCtx;
|
||||
AZStd::concurrency_checker m_cullDataConcurrencyCheck;
|
||||
OcclusionPlaneVector m_occlusionPlanes;
|
||||
AZ::TaskGraphActiveInterface* m_taskGraphActive = nullptr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -70,7 +70,8 @@ namespace AZ
|
||||
void InitializeSystemAssets() override;
|
||||
void RegisterScene(ScenePtr scene) override;
|
||||
void UnregisterScene(ScenePtr scene) override;
|
||||
ScenePtr GetScene(const SceneId& sceneId) const override;
|
||||
Scene* GetScene(const SceneId& sceneId) const override;
|
||||
Scene* GetSceneByName(const AZ::Name& name) const override;
|
||||
ScenePtr GetDefaultScene() const override;
|
||||
RenderPipelinePtr GetRenderPipelineForWindow(AzFramework::NativeWindowHandle windowHandle) override;
|
||||
Data::Asset<ShaderAsset> GetCommonShaderAssetForSrgs() const override;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include <Atom/RPI.Public/Base.h>
|
||||
|
||||
#include <AzCore/Name/Name.h>
|
||||
#include <AzFramework/Windowing/WindowBus.h>
|
||||
|
||||
namespace AZ
|
||||
@@ -46,11 +47,14 @@ namespace AZ
|
||||
//! Unregister a scene from RPISystem. The scene won't be simulated or rendered.
|
||||
virtual void UnregisterScene(ScenePtr scene) = 0;
|
||||
|
||||
// [GFX TODO] to be removed when we have scene setup in AZ Core
|
||||
virtual ScenePtr GetDefaultScene() const = 0;
|
||||
|
||||
//! Deprecated. Use GetSceneByName(name), GetSceneForEntityContextId(entityContextId) or Scene::GetSceneForEntityId(AZ::EntityId entityId) instead
|
||||
AZ_DEPRECATED(virtual ScenePtr GetDefaultScene() const = 0;, "This method has been deprecated. Please use GetSceneByName(name), GetSceneForEntityContextId(entityContextId) or Scene::GetSceneForEntityId(AZ::EntityId entityId) instead.");
|
||||
|
||||
//! Get scene by using scene id.
|
||||
virtual ScenePtr GetScene(const SceneId& sceneId) const = 0;
|
||||
virtual Scene* GetScene(const SceneId& sceneId) const = 0;
|
||||
|
||||
//! Get scene by using scene name.
|
||||
virtual Scene* GetSceneByName(const AZ::Name& name) const = 0;
|
||||
|
||||
//! Get the render pipeline created for a window
|
||||
virtual RenderPipelinePtr GetRenderPipelineForWindow(AzFramework::NativeWindowHandle windowHandle) = 0;
|
||||
|
||||
@@ -80,6 +80,9 @@ namespace AZ
|
||||
//! Gets the RPI::Scene for a given entityContextId.
|
||||
//! May return nullptr if there is no RPI::Scene created for that entityContext.
|
||||
static Scene* GetSceneForEntityContextId(AzFramework::EntityContextId entityContextId);
|
||||
|
||||
//! Gets the RPI::Scene for a given entityId.
|
||||
static Scene* GetSceneForEntityId(AZ::EntityId entityId);
|
||||
|
||||
~Scene();
|
||||
|
||||
@@ -135,6 +138,8 @@ namespace AZ
|
||||
|
||||
const SceneId& GetId() const;
|
||||
|
||||
AZ::Name GetName() const;
|
||||
|
||||
//! Set default pipeline by render pipeline ID.
|
||||
//! It returns true if the default render pipeline was set from the input ID.
|
||||
//! If the specified render pipeline doesn't exist in this scene then it won't do anything and returns false.
|
||||
@@ -195,8 +200,8 @@ namespace AZ
|
||||
// This function is called every time scene's render pipelines change.
|
||||
void RebuildPipelineStatesLookup();
|
||||
|
||||
// Helper function to wait for end of TaskGraph
|
||||
void WaitTGEvent(AZ::TaskGraphEvent& completionTGEvent, AZStd::atomic_bool* workToWaitOn = nullptr);
|
||||
// Helper function to wait for end of TaskGraph and then delete the TaskGraphEvent
|
||||
void WaitAndCleanTGEvent(AZStd::unique_ptr<AZ::TaskGraphEvent>&& completionTGEvent);
|
||||
|
||||
// Helper function for wait and clean up a completion job
|
||||
void WaitAndCleanCompletionJob(AZ::JobCompletion*& completionJob);
|
||||
@@ -225,8 +230,7 @@ namespace AZ
|
||||
AZStd::vector<RenderPipelinePtr> m_pipelines;
|
||||
|
||||
// CPU simulation TaskGraphEvent to wait for completion of all the simulation tasks
|
||||
AZ::TaskGraphEvent m_simulationFinishedTGEvent;
|
||||
AZStd::atomic_bool m_simulationFinishedWorkActive = false;
|
||||
AZStd::unique_ptr<AZ::TaskGraphEvent> m_simulationFinishedTGEvent;
|
||||
|
||||
// CPU simulation job completion for track all feature processors' simulation jobs
|
||||
AZ::JobCompletion* m_simulationCompletion = nullptr;
|
||||
@@ -245,6 +249,9 @@ namespace AZ
|
||||
// The uuid to identify this scene.
|
||||
SceneId m_id;
|
||||
|
||||
// Scene's name which is set at initialization. Can be empty
|
||||
AZ::Name m_name;
|
||||
|
||||
bool m_activated = false;
|
||||
bool m_taskGraphActive = false; // update during tick, to ensure it only changes on frame boundaries
|
||||
|
||||
@@ -286,13 +293,10 @@ namespace AZ
|
||||
template<typename FeatureProcessorType>
|
||||
FeatureProcessorType* Scene::GetFeatureProcessorForEntity(AZ::EntityId entityId)
|
||||
{
|
||||
// Find the entity context for the entity ID.
|
||||
AzFramework::EntityContextId entityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
AzFramework::EntityIdContextQueryBus::EventResult(entityContextId, entityId, &AzFramework::EntityIdContextQueryBus::Events::GetOwningContextId);
|
||||
|
||||
if (!entityContextId.IsNull())
|
||||
RPI::Scene* renderScene = GetSceneForEntityId(entityId);
|
||||
if (renderScene)
|
||||
{
|
||||
return GetFeatureProcessorForEntityContextId<FeatureProcessorType>(entityContextId);
|
||||
return renderScene->GetFeatureProcessor<FeatureProcessorType>();
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
@@ -154,6 +154,8 @@ namespace AZ
|
||||
|
||||
ConstPtr<RHI::PipelineLibraryData> LoadPipelineLibrary() const;
|
||||
void SavePipelineLibrary() const;
|
||||
|
||||
const ShaderVariant& GetVariantInternal(ShaderVariantStableId shaderVariantStableId);
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
/// AssetBus overrides
|
||||
|
||||
@@ -24,6 +24,9 @@ namespace AZ
|
||||
class ShaderReloadDebugTracker final
|
||||
{
|
||||
public:
|
||||
static void Init();
|
||||
static void Shutdown();
|
||||
|
||||
static bool IsEnabled();
|
||||
|
||||
//! Begin a code section. Will print a "[BEGIN] <sectionName>" header, and all subsequent calls will be indented.
|
||||
@@ -34,8 +37,8 @@ namespace AZ
|
||||
if (IsEnabled())
|
||||
{
|
||||
const AZStd::string sectionName = AZStd::string::format(sectionNameFormat, args...);
|
||||
AZ_TracePrintf("ShaderReloadDebug", "%*s [BEGIN] %s \n", s_indent, "", sectionName.c_str());
|
||||
s_indent += IndentSpaces;
|
||||
AZ_TracePrintf("ShaderReloadDebug", "%*s [BEGIN] %s \n", GetIndent(), "", sectionName.c_str());
|
||||
AddIndent();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -48,8 +51,8 @@ namespace AZ
|
||||
if (IsEnabled())
|
||||
{
|
||||
const AZStd::string sectionName = AZStd::string::format(sectionNameFormat, args...);
|
||||
s_indent -= IndentSpaces;
|
||||
AZ_TracePrintf("ShaderReloadDebug", "%*s [_END_] %s \n", s_indent, "", sectionName.c_str());
|
||||
RemoveIndent();
|
||||
AZ_TracePrintf("ShaderReloadDebug", "%*s [_END_] %s \n", GetIndent(), "", sectionName.c_str());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -63,7 +66,7 @@ namespace AZ
|
||||
{
|
||||
const AZStd::string message = AZStd::string::format(format, args...);
|
||||
|
||||
AZ_TracePrintf("ShaderReloadDebug", "%*s %s \n", s_indent, "", message.c_str());
|
||||
AZ_TracePrintf("ShaderReloadDebug", "%*s %s \n", GetIndent(), "", message.c_str());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -86,9 +89,12 @@ namespace AZ
|
||||
};
|
||||
|
||||
private:
|
||||
static bool s_enabled;
|
||||
static int s_indent;
|
||||
static constexpr int IndentSpaces = 4;
|
||||
|
||||
static void MakeReady();
|
||||
static void AddIndent();
|
||||
static void RemoveIndent();
|
||||
static int GetIndent();
|
||||
};
|
||||
|
||||
} // namespace RPI
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace AZ
|
||||
|
||||
//! Return the timestamp when the shader asset was built.
|
||||
//! This is used to synchronize versions of the ShaderAsset and ShaderVariantTreeAsset, especially during hot-reload.
|
||||
AZStd::sys_time_t GetShaderAssetBuildTimestamp() const;
|
||||
AZStd::sys_time_t GetBuildTimestamp() const;
|
||||
|
||||
//! Returns the shader option group layout.
|
||||
const ShaderOptionGroupLayout* GetShaderOptionGroupLayout() const;
|
||||
@@ -297,7 +297,7 @@ namespace AZ
|
||||
Name m_drawListName;
|
||||
|
||||
//! Use to synchronize versions of the ShaderAsset and ShaderVariantTreeAsset, especially during hot-reload.
|
||||
AZStd::sys_time_t m_shaderAssetBuildTimestamp = 0;
|
||||
AZStd::sys_time_t m_buildTimestamp = 0;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Name/Name.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
@@ -25,6 +26,9 @@ namespace AZ
|
||||
|
||||
//! List of feature processors which the scene will initially enable.
|
||||
AZStd::vector<AZStd::string> m_featureProcessorNames;
|
||||
|
||||
//! A name used as scene id. It can be used to search a registered scene via RPISystemInterface::GetScene()
|
||||
AZ::Name m_nameId;
|
||||
};
|
||||
} // namespace RPI
|
||||
} // namespace AZ
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include <AzCore/Debug/Timer.h>
|
||||
#include <AzCore/Jobs/JobFunction.h>
|
||||
#include <AzCore/Jobs/Job.h>
|
||||
#include <AzCore/Task/TaskGraph.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <Atom_RPI_Traits_Platform.h>
|
||||
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
@@ -265,89 +267,71 @@ namespace AZ
|
||||
return m_visScene->GetEntryCount();
|
||||
}
|
||||
|
||||
class AddObjectsToViewJob final
|
||||
: public Job
|
||||
|
||||
struct WorklistData
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(AddObjectsToViewJob, ThreadPoolAllocator, 0);
|
||||
|
||||
struct JobData
|
||||
{
|
||||
CullingDebugContext* m_debugCtx = nullptr;
|
||||
const Scene* m_scene = nullptr;
|
||||
View* m_view = nullptr;
|
||||
Frustum m_frustum;
|
||||
CullingDebugContext* m_debugCtx = nullptr;
|
||||
const Scene* m_scene = nullptr;
|
||||
View* m_view = nullptr;
|
||||
Frustum m_frustum;
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
MaskedOcclusionCulling* m_maskedOcclusionCulling = nullptr;
|
||||
MaskedOcclusionCulling* m_maskedOcclusionCulling = nullptr;
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
private:
|
||||
const AZStd::shared_ptr<JobData> m_jobData;
|
||||
CullingScene::WorkListType m_worklist;
|
||||
static AZStd::shared_ptr<WorklistData> MakeWorklistData(
|
||||
CullingDebugContext& debugCtx,
|
||||
const Scene& scene,
|
||||
View& view,
|
||||
Frustum& frustum,
|
||||
void* maskedOcclusionCulling)
|
||||
{
|
||||
AZStd::shared_ptr<WorklistData> worklistData = AZStd::make_shared<WorklistData>();
|
||||
worklistData->m_debugCtx = &debugCtx;
|
||||
worklistData->m_scene = &scene;
|
||||
worklistData->m_view = &view;
|
||||
worklistData->m_frustum = frustum;
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
worklistData->m_maskedOcclusionCulling = static_cast<MaskedOcclusionCulling*>(maskedOcclusionCulling);
|
||||
#endif
|
||||
return worklistData;
|
||||
}
|
||||
|
||||
constexpr size_t WorkListCapacity = 5;
|
||||
using WorkListType = AZStd::fixed_vector<AzFramework::IVisibilityScene::NodeData, WorkListCapacity>;
|
||||
|
||||
public:
|
||||
AddObjectsToViewJob(const AZStd::shared_ptr<AddObjectsToViewJob::JobData>& jobData, CullingScene::WorkListType& worklist)
|
||||
: Job(true, nullptr) //auto-deletes, no JobContext
|
||||
, m_jobData(jobData)
|
||||
, m_worklist(worklist)
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
static MaskedOcclusionCulling::CullingResult TestOcclusionCulling(
|
||||
const AZStd::shared_ptr<WorklistData>& worklistData,
|
||||
AzFramework::VisibilityEntry* visibleEntry);
|
||||
#endif
|
||||
|
||||
static void ProcessWorklist(const AZStd::shared_ptr<WorklistData>& worklistData, const WorkListType& worklist)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "AddObjectsToViewJob: Process");
|
||||
|
||||
const View::UsageFlags viewFlags = worklistData->m_view->GetUsageFlags();
|
||||
const RHI::DrawListMask drawListMask = worklistData->m_view->GetDrawListMask();
|
||||
uint32_t numDrawPackets = 0;
|
||||
uint32_t numVisibleCullables = 0;
|
||||
|
||||
AZ_Assert(worklist.size() > 0, "Received empty worklist in ProcessWorklist");
|
||||
|
||||
for (const AzFramework::IVisibilityScene::NodeData& nodeData : worklist)
|
||||
{
|
||||
}
|
||||
|
||||
//work function
|
||||
void Process() override
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "AddObjectsToViewJob: Process");
|
||||
|
||||
const View::UsageFlags viewFlags = m_jobData->m_view->GetUsageFlags();
|
||||
const RHI::DrawListMask drawListMask = m_jobData->m_view->GetDrawListMask();
|
||||
uint32_t numDrawPackets = 0;
|
||||
uint32_t numVisibleCullables = 0;
|
||||
|
||||
for (const AzFramework::IVisibilityScene::NodeData& nodeData : m_worklist)
|
||||
{
|
||||
//If a node is entirely contained within the frustum, then we can skip the fine grained culling.
|
||||
bool nodeIsContainedInFrustum = ShapeIntersection::Contains(m_jobData->m_frustum, nodeData.m_bounds);
|
||||
//If a node is entirely contained within the frustum, then we can skip the fine grained culling.
|
||||
bool nodeIsContainedInFrustum = ShapeIntersection::Contains(worklistData->m_frustum, nodeData.m_bounds);
|
||||
|
||||
#ifdef AZ_CULL_PROFILE_VERBOSE
|
||||
AZ_PROFILE_SCOPE(RPI, "process node (view: %s, skip fine cull: %d",
|
||||
m_view->GetName().GetCStr(), nodeIsContainedInFrustum ? 1 : 0);
|
||||
AZ_PROFILE_SCOPE(RPI, "process node (view: %s, skip fine cull: %d",
|
||||
m_view->GetName().GetCStr(), nodeIsContainedInFrustum ? 1 : 0);
|
||||
#endif
|
||||
|
||||
if (nodeIsContainedInFrustum || !m_jobData->m_debugCtx->m_enableFrustumCulling)
|
||||
if (nodeIsContainedInFrustum || !worklistData->m_debugCtx->m_enableFrustumCulling)
|
||||
{
|
||||
//Add all objects within this node to the view, without any extra culling
|
||||
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
|
||||
{
|
||||
//Add all objects within this node to the view, without any extra culling
|
||||
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
|
||||
{
|
||||
{
|
||||
if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable)
|
||||
{
|
||||
Cullable* c = static_cast<Cullable*>(visibleEntry->m_userData);
|
||||
|
||||
if ((c->m_cullData.m_drawListMask & drawListMask).none() ||
|
||||
c->m_cullData.m_hideFlags & viewFlags ||
|
||||
c->m_cullData.m_scene != m_jobData->m_scene || //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this
|
||||
c->m_isHidden)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE)
|
||||
#endif
|
||||
{
|
||||
numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view);
|
||||
++numVisibleCullables;
|
||||
c->m_isVisible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Do fine-grained culling before adding objects to the view
|
||||
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
|
||||
{
|
||||
if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable)
|
||||
{
|
||||
@@ -355,160 +339,188 @@ namespace AZ
|
||||
|
||||
if ((c->m_cullData.m_drawListMask & drawListMask).none() ||
|
||||
c->m_cullData.m_hideFlags & viewFlags ||
|
||||
c->m_cullData.m_scene != m_jobData->m_scene || //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this
|
||||
c->m_cullData.m_scene != worklistData->m_scene || //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this
|
||||
c->m_isHidden)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
IntersectResult res = ShapeIntersection::Classify(m_jobData->m_frustum, c->m_cullData.m_boundingSphere);
|
||||
if (res == IntersectResult::Exterior)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (res == IntersectResult::Interior || ShapeIntersection::Overlaps(m_jobData->m_frustum, c->m_cullData.m_boundingObb))
|
||||
{
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE)
|
||||
if (TestOcclusionCulling(worklistData, visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE)
|
||||
#endif
|
||||
{
|
||||
numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view);
|
||||
++numVisibleCullables;
|
||||
c->m_isVisible = true;
|
||||
}
|
||||
{
|
||||
numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *worklistData->m_view);
|
||||
++numVisibleCullables;
|
||||
c->m_isVisible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_jobData->m_debugCtx->m_debugDraw && (m_jobData->m_view->GetName() == m_jobData->m_debugCtx->m_currentViewSelectionName))
|
||||
}
|
||||
else
|
||||
{
|
||||
//Do fine-grained culling before adding objects to the view
|
||||
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "debug draw culling");
|
||||
|
||||
AuxGeomDrawPtr auxGeomPtr = AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(m_jobData->m_scene);
|
||||
if (auxGeomPtr)
|
||||
if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable)
|
||||
{
|
||||
//Draw the node bounds
|
||||
// "Fully visible" nodes are nodes that are fully inside the frustum. "Partially visible" nodes intersect the edges of the frustum.
|
||||
// Since the nodes of an octree have lots of overlapping boxes with coplanar edges, it's easier to view these separately, so
|
||||
// we have a few debug booleans to toggle which ones to draw.
|
||||
if (nodeIsContainedInFrustum && m_jobData->m_debugCtx->m_drawFullyVisibleNodes)
|
||||
Cullable* c = static_cast<Cullable*>(visibleEntry->m_userData);
|
||||
|
||||
if ((c->m_cullData.m_drawListMask & drawListMask).none() ||
|
||||
c->m_cullData.m_hideFlags & viewFlags ||
|
||||
c->m_cullData.m_scene != worklistData->m_scene || //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this
|
||||
c->m_isHidden)
|
||||
{
|
||||
auxGeomPtr->DrawAabb(nodeData.m_bounds, Colors::Lime, RPI::AuxGeomDraw::DrawStyle::Line, RPI::AuxGeomDraw::DepthTest::Off);
|
||||
}
|
||||
else if (!nodeIsContainedInFrustum && m_jobData->m_debugCtx->m_drawPartiallyVisibleNodes)
|
||||
{
|
||||
auxGeomPtr->DrawAabb(nodeData.m_bounds, Colors::Yellow, RPI::AuxGeomDraw::DrawStyle::Line, RPI::AuxGeomDraw::DepthTest::Off);
|
||||
continue;
|
||||
}
|
||||
|
||||
//Draw bounds on individual objects
|
||||
if (m_jobData->m_debugCtx->m_drawBoundingBoxes || m_jobData->m_debugCtx->m_drawBoundingSpheres || m_jobData->m_debugCtx->m_drawLodRadii)
|
||||
IntersectResult res = ShapeIntersection::Classify(worklistData->m_frustum, c->m_cullData.m_boundingSphere);
|
||||
if (res == IntersectResult::Exterior)
|
||||
{
|
||||
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
|
||||
continue;
|
||||
}
|
||||
else if (res == IntersectResult::Interior || ShapeIntersection::Overlaps(worklistData->m_frustum, c->m_cullData.m_boundingObb))
|
||||
{
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
if (TestOcclusionCulling(worklistData, visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE)
|
||||
#endif
|
||||
{
|
||||
if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable)
|
||||
{
|
||||
Cullable* c = static_cast<Cullable*>(visibleEntry->m_userData);
|
||||
if (m_jobData->m_debugCtx->m_drawBoundingBoxes)
|
||||
{
|
||||
auxGeomPtr->DrawObb(c->m_cullData.m_boundingObb, Matrix3x4::Identity(),
|
||||
nodeIsContainedInFrustum ? Colors::Lime : Colors::Yellow, AuxGeomDraw::DrawStyle::Line);
|
||||
}
|
||||
|
||||
if (m_jobData->m_debugCtx->m_drawBoundingSpheres)
|
||||
{
|
||||
auxGeomPtr->DrawSphere(c->m_cullData.m_boundingSphere.GetCenter(), c->m_cullData.m_boundingSphere.GetRadius(),
|
||||
Color(0.5f, 0.5f, 0.5f, 0.3f), AuxGeomDraw::DrawStyle::Shaded);
|
||||
}
|
||||
|
||||
if (m_jobData->m_debugCtx->m_drawLodRadii)
|
||||
{
|
||||
auxGeomPtr->DrawSphere(c->m_cullData.m_boundingSphere.GetCenter(),
|
||||
c->m_lodData.m_lodSelectionRadius,
|
||||
Color(1.0f, 0.5f, 0.0f, 0.3f), RPI::AuxGeomDraw::DrawStyle::Shaded);
|
||||
}
|
||||
}
|
||||
numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *worklistData->m_view);
|
||||
++numVisibleCullables;
|
||||
c->m_isVisible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_jobData->m_debugCtx->m_enableStats)
|
||||
if (worklistData->m_debugCtx->m_debugDraw && (worklistData->m_view->GetName() == worklistData->m_debugCtx->m_currentViewSelectionName))
|
||||
{
|
||||
CullingDebugContext::CullStats& cullStats = m_jobData->m_debugCtx->GetCullStatsForView(m_jobData->m_view);
|
||||
AZ_PROFILE_SCOPE(RPI, "debug draw culling");
|
||||
|
||||
//no need for mutex here since these are all atomics
|
||||
cullStats.m_numVisibleDrawPackets += numDrawPackets;
|
||||
cullStats.m_numVisibleCullables += numVisibleCullables;
|
||||
++cullStats.m_numJobs;
|
||||
AuxGeomDrawPtr auxGeomPtr = AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(worklistData->m_scene);
|
||||
if (auxGeomPtr)
|
||||
{
|
||||
//Draw the node bounds
|
||||
// "Fully visible" nodes are nodes that are fully inside the frustum. "Partially visible" nodes intersect the edges of the frustum.
|
||||
// Since the nodes of an octree have lots of overlapping boxes with coplanar edges, it's easier to view these separately, so
|
||||
// we have a few debug booleans to toggle which ones to draw.
|
||||
if (nodeIsContainedInFrustum && worklistData->m_debugCtx->m_drawFullyVisibleNodes)
|
||||
{
|
||||
auxGeomPtr->DrawAabb(nodeData.m_bounds, Colors::Lime, RPI::AuxGeomDraw::DrawStyle::Line, RPI::AuxGeomDraw::DepthTest::Off);
|
||||
}
|
||||
else if (!nodeIsContainedInFrustum && worklistData->m_debugCtx->m_drawPartiallyVisibleNodes)
|
||||
{
|
||||
auxGeomPtr->DrawAabb(nodeData.m_bounds, Colors::Yellow, RPI::AuxGeomDraw::DrawStyle::Line, RPI::AuxGeomDraw::DepthTest::Off);
|
||||
}
|
||||
|
||||
//Draw bounds on individual objects
|
||||
if (worklistData->m_debugCtx->m_drawBoundingBoxes || worklistData->m_debugCtx->m_drawBoundingSpheres || worklistData->m_debugCtx->m_drawLodRadii)
|
||||
{
|
||||
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
|
||||
{
|
||||
if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable)
|
||||
{
|
||||
Cullable* c = static_cast<Cullable*>(visibleEntry->m_userData);
|
||||
if (worklistData->m_debugCtx->m_drawBoundingBoxes)
|
||||
{
|
||||
auxGeomPtr->DrawObb(c->m_cullData.m_boundingObb, Matrix3x4::Identity(),
|
||||
nodeIsContainedInFrustum ? Colors::Lime : Colors::Yellow, AuxGeomDraw::DrawStyle::Line);
|
||||
}
|
||||
|
||||
if (worklistData->m_debugCtx->m_drawBoundingSpheres)
|
||||
{
|
||||
auxGeomPtr->DrawSphere(c->m_cullData.m_boundingSphere.GetCenter(), c->m_cullData.m_boundingSphere.GetRadius(),
|
||||
Color(0.5f, 0.5f, 0.5f, 0.3f), AuxGeomDraw::DrawStyle::Shaded);
|
||||
}
|
||||
|
||||
if (worklistData->m_debugCtx->m_drawLodRadii)
|
||||
{
|
||||
auxGeomPtr->DrawSphere(c->m_cullData.m_boundingSphere.GetCenter(),
|
||||
c->m_lodData.m_lodSelectionRadius,
|
||||
Color(1.0f, 0.5f, 0.0f, 0.3f), RPI::AuxGeomDraw::DrawStyle::Shaded);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
MaskedOcclusionCulling::CullingResult TestOcclusionCulling(AzFramework::VisibilityEntry* visibleEntry)
|
||||
if (worklistData->m_debugCtx->m_enableStats)
|
||||
{
|
||||
if (!m_jobData->m_maskedOcclusionCulling)
|
||||
{
|
||||
return MaskedOcclusionCulling::CullingResult::VISIBLE;
|
||||
}
|
||||
CullingDebugContext::CullStats& cullStats = worklistData->m_debugCtx->GetCullStatsForView(worklistData->m_view);
|
||||
|
||||
if (visibleEntry->m_boundingVolume.Contains(m_jobData->m_view->GetCameraTransform().GetTranslation()))
|
||||
{
|
||||
// camera is inside bounding volume
|
||||
return MaskedOcclusionCulling::CullingResult::VISIBLE;
|
||||
}
|
||||
//no need for mutex here since these are all atomics
|
||||
cullStats.m_numVisibleDrawPackets += numDrawPackets;
|
||||
cullStats.m_numVisibleCullables += numVisibleCullables;
|
||||
++cullStats.m_numJobs;
|
||||
}
|
||||
}
|
||||
|
||||
const Vector3& minBound = visibleEntry->m_boundingVolume.GetMin();
|
||||
const Vector3& maxBound = visibleEntry->m_boundingVolume.GetMax();
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
static MaskedOcclusionCulling::CullingResult TestOcclusionCulling(
|
||||
const AZStd::shared_ptr<WorklistData>& worklistData,
|
||||
AzFramework::VisibilityEntry* visibleEntry)
|
||||
{
|
||||
if (!worklistData->m_maskedOcclusionCulling)
|
||||
{
|
||||
return MaskedOcclusionCulling::CullingResult::VISIBLE;
|
||||
}
|
||||
|
||||
// compute bounding volume corners
|
||||
Vector4 corners[8];
|
||||
corners[0] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), minBound.GetY(), minBound.GetZ(), 1.0f);
|
||||
corners[1] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), minBound.GetY(), maxBound.GetZ(), 1.0f);
|
||||
corners[2] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), minBound.GetY(), maxBound.GetZ(), 1.0f);
|
||||
corners[3] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), minBound.GetY(), minBound.GetZ(), 1.0f);
|
||||
corners[4] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f);
|
||||
corners[5] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), maxBound.GetY(), maxBound.GetZ(), 1.0f);
|
||||
corners[6] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), maxBound.GetZ(), 1.0f);
|
||||
corners[7] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f);
|
||||
if (visibleEntry->m_boundingVolume.Contains(worklistData->m_view->GetCameraTransform().GetTranslation()))
|
||||
{
|
||||
// camera is inside bounding volume
|
||||
return MaskedOcclusionCulling::CullingResult::VISIBLE;
|
||||
}
|
||||
|
||||
// find min clip-space depth and NDC min/max
|
||||
float minDepth = FLT_MAX;
|
||||
float ndcMinX = FLT_MAX;
|
||||
float ndcMinY = FLT_MAX;
|
||||
float ndcMaxX = -FLT_MAX;
|
||||
float ndcMaxY = -FLT_MAX;
|
||||
for (uint32_t index = 0; index < 8; ++index)
|
||||
{
|
||||
minDepth = AZStd::min(minDepth, corners[index].GetW());
|
||||
const Vector3& minBound = visibleEntry->m_boundingVolume.GetMin();
|
||||
const Vector3& maxBound = visibleEntry->m_boundingVolume.GetMax();
|
||||
|
||||
// convert to NDC
|
||||
corners[index] /= corners[index].GetW();
|
||||
|
||||
ndcMinX = AZStd::min(ndcMinX, corners[index].GetX());
|
||||
ndcMinY = AZStd::min(ndcMinY, corners[index].GetY());
|
||||
ndcMaxX = AZStd::max(ndcMaxX, corners[index].GetX());
|
||||
ndcMaxY = AZStd::max(ndcMaxY, corners[index].GetY());
|
||||
}
|
||||
// compute bounding volume corners
|
||||
Vector4 corners[8];
|
||||
corners[0] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), minBound.GetY(), minBound.GetZ(), 1.0f);
|
||||
corners[1] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), minBound.GetY(), maxBound.GetZ(), 1.0f);
|
||||
corners[2] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), minBound.GetY(), maxBound.GetZ(), 1.0f);
|
||||
corners[3] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), minBound.GetY(), minBound.GetZ(), 1.0f);
|
||||
corners[4] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f);
|
||||
corners[5] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), maxBound.GetY(), maxBound.GetZ(), 1.0f);
|
||||
corners[6] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), maxBound.GetZ(), 1.0f);
|
||||
corners[7] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f);
|
||||
|
||||
// find min clip-space depth and NDC min/max
|
||||
float minDepth = FLT_MAX;
|
||||
float ndcMinX = FLT_MAX;
|
||||
float ndcMinY = FLT_MAX;
|
||||
float ndcMaxX = -FLT_MAX;
|
||||
float ndcMaxY = -FLT_MAX;
|
||||
for (uint32_t index = 0; index < 8; ++index)
|
||||
{
|
||||
minDepth = AZStd::min(minDepth, corners[index].GetW());
|
||||
if (minDepth < 0.00000001f)
|
||||
{
|
||||
return MaskedOcclusionCulling::VISIBLE;
|
||||
return MaskedOcclusionCulling::CullingResult::VISIBLE;
|
||||
}
|
||||
|
||||
// test against the occlusion buffer, which contains only the manually placed occlusion planes
|
||||
return m_jobData->m_maskedOcclusionCulling->TestRect(ndcMinX, ndcMinY, ndcMaxX, ndcMaxY, minDepth);
|
||||
|
||||
// convert to NDC
|
||||
corners[index] /= corners[index].GetW();
|
||||
|
||||
ndcMinX = AZStd::min(ndcMinX, corners[index].GetX());
|
||||
ndcMinY = AZStd::min(ndcMinY, corners[index].GetY());
|
||||
ndcMaxX = AZStd::max(ndcMaxX, corners[index].GetX());
|
||||
ndcMaxY = AZStd::max(ndcMaxY, corners[index].GetY());
|
||||
}
|
||||
|
||||
// test against the occlusion buffer, which contains only the manually placed occlusion planes
|
||||
return worklistData->m_maskedOcclusionCulling->TestRect(ndcMinX, ndcMinY, ndcMaxX, ndcMaxY, minDepth);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
void CullingScene::ProcessCullables(const Scene& scene, View& view, AZ::Job& parentJob)
|
||||
void CullingScene::ProcessCullablesCommon(const Scene& scene, View& view, AZ::Frustum& frustum, void*& maskedOcclusionCulling)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "CullingScene::ProcessCullables() - %s", view.GetName().GetCStr());
|
||||
AZ_PROFILE_SCOPE(RPI, "CullingScene::ProcessCullablesCommon() - %s", view.GetName().GetCStr());
|
||||
|
||||
const Matrix4x4& worldToClip = view.GetWorldToClipMatrix();
|
||||
Frustum frustum = Frustum::CreateFromMatrixColumnMajor(worldToClip);
|
||||
if (m_debugCtx.m_freezeFrustums)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_debugCtx.m_frozenFrustumsMutex);
|
||||
@@ -536,7 +548,7 @@ namespace AZ
|
||||
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
// setup occlusion culling, if necessary
|
||||
MaskedOcclusionCulling* maskedOcclusionCulling = m_occlusionPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling();
|
||||
maskedOcclusionCulling = m_occlusionPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling();
|
||||
if (maskedOcclusionCulling)
|
||||
{
|
||||
// frustum cull occlusion planes
|
||||
@@ -578,23 +590,27 @@ namespace AZ
|
||||
static uint32_t indices[6] = { 0, 1, 2, 2, 3, 0 };
|
||||
|
||||
// render into the occlusion buffer, specifying BACKFACE_NONE so it functions as a double-sided occluder
|
||||
maskedOcclusionCulling->RenderTriangles((float*)verts, indices, 2, nullptr, MaskedOcclusionCulling::BACKFACE_NONE);
|
||||
static_cast<MaskedOcclusionCulling*>(maskedOcclusionCulling)->RenderTriangles(verts, indices, 2, nullptr, MaskedOcclusionCulling::BACKFACE_NONE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CullingScene::ProcessCullablesJobs(const Scene& scene, View& view, AZ::Job& parentJob)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "CullingScene::ProcessCullablesJobs() - %s", view.GetName().GetCStr());
|
||||
|
||||
const Matrix4x4& worldToClip = view.GetWorldToClipMatrix();
|
||||
AZ::Frustum frustum = Frustum::CreateFromMatrixColumnMajor(worldToClip);
|
||||
|
||||
void* maskedOcclusionCulling = nullptr;
|
||||
ProcessCullablesCommon(scene, view, frustum, maskedOcclusionCulling);
|
||||
|
||||
WorkListType worklist;
|
||||
|
||||
AZStd::shared_ptr<AddObjectsToViewJob::JobData> jobData = AZStd::make_shared<AddObjectsToViewJob::JobData>();
|
||||
jobData->m_debugCtx = &m_debugCtx;
|
||||
jobData->m_scene = &scene;
|
||||
jobData->m_view = &view;
|
||||
jobData->m_frustum = frustum;
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
jobData->m_maskedOcclusionCulling = maskedOcclusionCulling;
|
||||
#endif
|
||||
AZStd::shared_ptr<WorklistData> worklistData = MakeWorklistData(m_debugCtx, scene, view, frustum, maskedOcclusionCulling);
|
||||
|
||||
auto nodeVisitorLambda = [jobData, &parentJob, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void
|
||||
auto nodeVisitorLambda = [worklistData, &parentJob, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "nodeVisitorLambda()");
|
||||
AZ_Assert(nodeData.m_entries.size() > 0, "should not get called with 0 entries");
|
||||
@@ -606,8 +622,13 @@ namespace AZ
|
||||
|
||||
if (worklist.size() == worklist.capacity())
|
||||
{
|
||||
// capture worklistData & worklist by value
|
||||
auto processWorklist = [worklistData, worklist]()
|
||||
{
|
||||
ProcessWorklist(worklistData, worklist);
|
||||
};
|
||||
//Kick off a job to process the (full) worklist
|
||||
AddObjectsToViewJob* job = aznew AddObjectsToViewJob(jobData, worklist); //pool allocated (cheap), auto-deletes when job finishes
|
||||
AZ::Job* job = AZ::CreateJobFunction(processWorklist, true);
|
||||
worklist.clear();
|
||||
parentJob.SetContinuation(job);
|
||||
job->Start();
|
||||
@@ -616,7 +637,7 @@ namespace AZ
|
||||
|
||||
if (m_debugCtx.m_enableFrustumCulling)
|
||||
{
|
||||
m_visScene->Enumerate(frustum, nodeVisitorLambda);
|
||||
m_visScene->Enumerate(frustum, nodeVisitorLambda);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -625,21 +646,76 @@ namespace AZ
|
||||
|
||||
if (worklist.size() > 0)
|
||||
{
|
||||
AZStd::shared_ptr<AddObjectsToViewJob::JobData> remainingJobData = AZStd::make_shared<AddObjectsToViewJob::JobData>();
|
||||
remainingJobData->m_debugCtx = &m_debugCtx;
|
||||
remainingJobData->m_scene = &scene;
|
||||
remainingJobData->m_view = &view;
|
||||
remainingJobData->m_frustum = frustum;
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
remainingJobData->m_maskedOcclusionCulling = maskedOcclusionCulling;
|
||||
#endif
|
||||
//Kick off a job to process any remaining workitems
|
||||
AddObjectsToViewJob* job = aznew AddObjectsToViewJob(remainingJobData, worklist); //pool allocated (cheap), auto-deletes when job finishes
|
||||
// capture worklistData & worklist by value
|
||||
auto processWorklist = [worklistData, worklist]()
|
||||
{
|
||||
ProcessWorklist(worklistData, worklist);
|
||||
};
|
||||
//Kick off a job to process the (full) worklist
|
||||
AZ::Job* job = AZ::CreateJobFunction(processWorklist, true);
|
||||
parentJob.SetContinuation(job);
|
||||
job->Start();
|
||||
}
|
||||
}
|
||||
|
||||
void CullingScene::ProcessCullablesTG(const Scene& scene, View& view, AZ::TaskGraph& taskGraph)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "CullingScene::ProcessCullablesTG() - %s", view.GetName().GetCStr());
|
||||
|
||||
const Matrix4x4& worldToClip = view.GetWorldToClipMatrix();
|
||||
AZ::Frustum frustum = Frustum::CreateFromMatrixColumnMajor(worldToClip);
|
||||
|
||||
void* maskedOcclusionCulling = nullptr;
|
||||
ProcessCullablesCommon(scene, view, frustum, maskedOcclusionCulling);
|
||||
|
||||
AZStd::unique_ptr<WorkListType> worklist = AZStd::make_unique<WorkListType>();
|
||||
|
||||
AZStd::shared_ptr<WorklistData> worklistData = MakeWorklistData(m_debugCtx, scene, view, frustum, maskedOcclusionCulling);
|
||||
static const AZ::TaskDescriptor descriptor{ "AZ::RPI::ProcessWorklist", "Graphics" };
|
||||
|
||||
auto nodeVisitorLambda = [worklistData, &taskGraph, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "nodeVisitorLambda()");
|
||||
AZ_Assert(nodeData.m_entries.size() > 0, "should not get called with 0 entries");
|
||||
AZ_Assert(worklist->size() < worklist->capacity(), "we should always have room to push a node on the queue");
|
||||
|
||||
//Queue up a small list of work items (NodeData*) which will be pushed to a worker task once the queue is full.
|
||||
//This reduces the number of tasks in flight, reducing task-system overhead.
|
||||
worklist->emplace_back(AZStd::move(nodeData));
|
||||
|
||||
if (worklist->size() == worklist->capacity())
|
||||
{
|
||||
//Task takes ownership of the worklist unique ptr
|
||||
taskGraph.AddTask( descriptor, [worklistData, worklist = AZStd::move(worklist)]()
|
||||
{
|
||||
ProcessWorklist(worklistData, *worklist.get());
|
||||
// allow worklist to go out of scope and be deleted
|
||||
});
|
||||
worklist = AZStd::make_unique<WorkListType>();
|
||||
}
|
||||
};
|
||||
|
||||
if (m_debugCtx.m_enableFrustumCulling)
|
||||
{
|
||||
m_visScene->Enumerate(frustum, nodeVisitorLambda);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_visScene->EnumerateNoCull(nodeVisitorLambda);
|
||||
}
|
||||
|
||||
if (worklist->size() > 0)
|
||||
{
|
||||
//Task takes ownership of the worklist unique ptr
|
||||
taskGraph.AddTask( descriptor, [worklistData, worklist = AZStd::move(worklist)]()
|
||||
{
|
||||
ProcessWorklist(worklistData, *worklist.get());
|
||||
// allow worklist to go out of scope and be deleted
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t AddLodDataToView(const Vector3& pos, const Cullable::LodData& lodData, RPI::View& view)
|
||||
{
|
||||
#ifdef AZ_CULL_PROFILE_DETAILED
|
||||
@@ -699,11 +775,11 @@ namespace AZ
|
||||
m_parentScene = parentScene;
|
||||
|
||||
AZ_Assert(m_visScene == nullptr, "IVisibilityScene already created for this RPI::Scene");
|
||||
char sceneIdBuf[40] = "";
|
||||
m_parentScene->GetId().ToString(sceneIdBuf);
|
||||
AZ::Name visSceneName(AZStd::string::format("RenderCullScene[%s]", sceneIdBuf));
|
||||
AZ::Name visSceneName(AZStd::string::format("RenderCullScene[%s]", m_parentScene->GetName().GetCStr()));
|
||||
m_visScene = AZ::Interface<AzFramework::IVisibilitySystem>::Get()->CreateVisibilityScene(visSceneName);
|
||||
|
||||
m_taskGraphActive = AZ::Interface<AZ::TaskGraphActiveInterface>::Get();
|
||||
|
||||
#ifdef AZ_CULL_DEBUG_ENABLED
|
||||
AZ_Assert(CountObjectsInScene() == 0, "The culling system should start with 0 entries in this scene.");
|
||||
#endif
|
||||
@@ -721,13 +797,27 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
void CullingScene::BeginCulling(const AZStd::vector<ViewPtr>& views)
|
||||
void CullingScene::BeginCullingTaskGraph(const AZStd::vector<ViewPtr>& views)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "CullingScene: BeginCulling");
|
||||
m_cullDataConcurrencyCheck.soft_lock();
|
||||
AZ::TaskGraph taskGraph;
|
||||
AZ::TaskDescriptor beginCullingDescriptor{"RPI_CullingScene_BeginCullingView", "Graphics"};
|
||||
for (auto& view : views)
|
||||
{
|
||||
taskGraph.AddTask(
|
||||
beginCullingDescriptor,
|
||||
[&view]()
|
||||
{
|
||||
view->BeginCulling();
|
||||
});
|
||||
}
|
||||
|
||||
m_debugCtx.ResetCullStats();
|
||||
m_debugCtx.m_numCullablesInScene = GetNumCullables();
|
||||
AZ::TaskGraphEvent waitForCompletion;
|
||||
taskGraph.Submit(&waitForCompletion);
|
||||
waitForCompletion.Wait();
|
||||
}
|
||||
|
||||
void CullingScene::BeginCullingJobs(const AZStd::vector<ViewPtr>& views)
|
||||
{
|
||||
AZ::JobCompletion beginCullingCompletion;
|
||||
|
||||
for (auto& view : views)
|
||||
@@ -743,6 +833,26 @@ namespace AZ
|
||||
}
|
||||
|
||||
beginCullingCompletion.StartAndWaitForCompletion();
|
||||
}
|
||||
|
||||
void CullingScene::BeginCulling(const AZStd::vector<ViewPtr>& views)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "CullingScene: BeginCulling");
|
||||
m_cullDataConcurrencyCheck.soft_lock();
|
||||
|
||||
m_debugCtx.ResetCullStats();
|
||||
m_debugCtx.m_numCullablesInScene = GetNumCullables();
|
||||
|
||||
m_taskGraphActive = AZ::Interface<AZ::TaskGraphActiveInterface>::Get();
|
||||
|
||||
if (m_taskGraphActive && m_taskGraphActive->IsTaskGraphActive())
|
||||
{
|
||||
BeginCullingTaskGraph(views);
|
||||
}
|
||||
else
|
||||
{
|
||||
BeginCullingJobs(views);
|
||||
}
|
||||
|
||||
AuxGeomDrawPtr auxGeom;
|
||||
if (m_debugCtx.m_debugDraw)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <Atom/RPI.Public/Pass/FullscreenTrianglePass.h>
|
||||
#include <Atom/RPI.Public/Pass/PassUtils.h>
|
||||
#include <Atom/RPI.Public/RPIUtils.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Pass/FullscreenTrianglePassData.h>
|
||||
#include <Atom/RPI.Reflect/Pass/PassTemplate.h>
|
||||
@@ -46,16 +47,19 @@ namespace AZ
|
||||
|
||||
void FullscreenTrianglePass::OnShaderReinitialized(const Shader&)
|
||||
{
|
||||
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->FullscreenTrianglePass::OnShaderReinitialized", this);
|
||||
LoadShader();
|
||||
}
|
||||
|
||||
void FullscreenTrianglePass::OnShaderAssetReinitialized(const Data::Asset<ShaderAsset>&)
|
||||
{
|
||||
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->FullscreenTrianglePass::OnShaderAssetReinitialized", this);
|
||||
LoadShader();
|
||||
}
|
||||
|
||||
void FullscreenTrianglePass::OnShaderVariantReinitialized(const ShaderVariant&)
|
||||
{
|
||||
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->FullscreenTrianglePass::OnShaderVariantReinitialized", this);
|
||||
LoadShader();
|
||||
}
|
||||
|
||||
@@ -129,6 +133,8 @@ namespace AZ
|
||||
void FullscreenTrianglePass::InitializeInternal()
|
||||
{
|
||||
RenderPass::InitializeInternal();
|
||||
|
||||
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->FullscreenTrianglePass::InitializeInternal", this);
|
||||
|
||||
// This draw item purposefully does not reference any geometry buffers.
|
||||
// Instead it's expected that the extended class uses a vertex shader
|
||||
|
||||
@@ -159,6 +159,11 @@ namespace AZ
|
||||
AZ_Assert(false, "Scene was already registered");
|
||||
return;
|
||||
}
|
||||
else if (!scene->GetName().IsEmpty() && scene->GetName() == sceneItem->GetName())
|
||||
{
|
||||
// only report a warning if there is a scene with duplicated name
|
||||
AZ_Warning("RPISystem", false, "There is a registered scene with same name [%s]", scene->GetName().GetCStr());
|
||||
}
|
||||
}
|
||||
|
||||
m_scenes.push_back(scene);
|
||||
@@ -177,11 +182,35 @@ namespace AZ
|
||||
AZ_Assert(false, "Can't unregister scene which wasn't registered");
|
||||
}
|
||||
|
||||
ScenePtr RPISystem::GetScene(const SceneId& sceneId) const
|
||||
Scene* RPISystem::GetScene(const SceneId& sceneId) const
|
||||
{
|
||||
for (const auto& scene : m_scenes)
|
||||
{
|
||||
if (scene->GetId() == sceneId)
|
||||
{
|
||||
return scene.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Scene* RPISystem::GetSceneByName(const AZ::Name& name) const
|
||||
{
|
||||
for (const auto& scene : m_scenes)
|
||||
{
|
||||
if (scene->GetName() == name)
|
||||
{
|
||||
return scene.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ScenePtr RPISystem::GetDefaultScene() const
|
||||
{
|
||||
for (const auto& scene : m_scenes)
|
||||
{
|
||||
if (scene->GetName() == AZ::Name("Main"))
|
||||
{
|
||||
return scene;
|
||||
}
|
||||
@@ -189,16 +218,6 @@ namespace AZ
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ScenePtr RPISystem::GetDefaultScene() const
|
||||
{
|
||||
if (m_scenes.size() > 0)
|
||||
{
|
||||
return m_scenes[0];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
RenderPipelinePtr RPISystem::GetRenderPipelineForWindow(AzFramework::NativeWindowHandle windowHandle)
|
||||
{
|
||||
RenderPipelinePtr renderPipeline;
|
||||
|
||||
@@ -45,7 +45,9 @@ namespace AZ
|
||||
auto shaderAsset = RPISystemInterface::Get()->GetCommonShaderAssetForSrgs();
|
||||
scene->m_srg = ShaderResourceGroup::Create(shaderAsset, sceneSrgLayout->GetName());
|
||||
}
|
||||
|
||||
|
||||
scene->m_name = sceneDescriptor.m_nameId;
|
||||
|
||||
return ScenePtr(scene);
|
||||
}
|
||||
|
||||
@@ -83,10 +85,23 @@ namespace AZ
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Scene* Scene::GetSceneForEntityId(AZ::EntityId entityId)
|
||||
{
|
||||
// Find the entity context for the entity ID.
|
||||
AzFramework::EntityContextId entityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
AzFramework::EntityIdContextQueryBus::EventResult(entityContextId, entityId, &AzFramework::EntityIdContextQueryBus::Events::GetOwningContextId);
|
||||
|
||||
if (!entityContextId.IsNull())
|
||||
{
|
||||
return GetSceneForEntityContextId(entityContextId);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
Scene::Scene()
|
||||
{
|
||||
m_id = Uuid::CreateRandom();
|
||||
m_id = AZ::Uuid::CreateRandom();
|
||||
m_cullingScene = aznew CullingScene();
|
||||
SceneRequestBus::Handler::BusConnect(m_id);
|
||||
m_drawFilterTagRegistry = RHI::DrawFilterTagRegistry::Create();
|
||||
@@ -96,7 +111,7 @@ namespace AZ
|
||||
{
|
||||
if (m_taskGraphActive)
|
||||
{
|
||||
WaitTGEvent(m_simulationFinishedTGEvent, &m_simulationFinishedWorkActive);
|
||||
WaitAndCleanTGEvent(AZStd::move(m_simulationFinishedTGEvent));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -299,7 +314,6 @@ namespace AZ
|
||||
// Force to update the lookup table since adding render pipeline would effect any pipeline states created before pass system tick
|
||||
RebuildPipelineStatesLookup();
|
||||
|
||||
AZ_Assert(!m_id.IsNull(), "RPI::Scene needs to have a valid uuid.");
|
||||
SceneNotificationBus::Event(m_id, &SceneNotification::OnRenderPipelineAdded, pipeline);
|
||||
}
|
||||
|
||||
@@ -371,8 +385,8 @@ namespace AZ
|
||||
});
|
||||
}
|
||||
simulationTG.Detach();
|
||||
m_simulationFinishedWorkActive = true;
|
||||
simulationTG.Submit(&m_simulationFinishedTGEvent);
|
||||
m_simulationFinishedTGEvent = AZStd::make_unique<TaskGraphEvent>();
|
||||
simulationTG.Submit(m_simulationFinishedTGEvent.get());
|
||||
}
|
||||
|
||||
void Scene::SimulateJobs()
|
||||
@@ -405,7 +419,7 @@ namespace AZ
|
||||
// If previous simulation job wasn't done, wait for it to finish.
|
||||
if (m_taskGraphActive)
|
||||
{
|
||||
WaitTGEvent(m_simulationFinishedTGEvent, &m_simulationFinishedWorkActive);
|
||||
WaitAndCleanTGEvent(AZStd::move(m_simulationFinishedTGEvent));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -435,17 +449,14 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::WaitTGEvent(AZ::TaskGraphEvent& completionTGEvent, AZStd::atomic_bool* workToWaitOn )
|
||||
void Scene::WaitAndCleanTGEvent(AZStd::unique_ptr<AZ::TaskGraphEvent>&& completionTGEvent)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "Scene: WaitAndCleanCompletionJob");
|
||||
if (!workToWaitOn || workToWaitOn->load())
|
||||
AZ_PROFILE_SCOPE(RPI, "Scene: WaitAndCleanTGEvent");
|
||||
if (completionTGEvent)
|
||||
{
|
||||
completionTGEvent.Wait();
|
||||
}
|
||||
if (workToWaitOn)
|
||||
{
|
||||
workToWaitOn->store(false);
|
||||
completionTGEvent->Wait();
|
||||
}
|
||||
// allow completionTGEvent to go out of scope and be deleted
|
||||
}
|
||||
|
||||
void Scene::WaitAndCleanCompletionJob(AZ::JobCompletion*& completionJob)
|
||||
@@ -485,12 +496,12 @@ namespace AZ
|
||||
|
||||
void Scene::CollectDrawPacketsTaskGraph()
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "CollectDrawPackets");
|
||||
AZ_PROFILE_SCOPE(RPI, "CollectDrawPacketsTaskGraph");
|
||||
AZ::TaskGraphEvent collectDrawPacketsTGEvent;
|
||||
static const AZ::TaskDescriptor collectDrawPacketsTGDesc{"RPI_Scene_PrepareRender_CollectDrawPackets", "Graphics"};
|
||||
|
||||
AZ::TaskGraph collectDrawPacketsTG;
|
||||
// Launch FeatureProcessor::Render() jobs
|
||||
|
||||
// Launch FeatureProcessor::Render() taskgraphs
|
||||
for (auto& fp : m_featureProcessors)
|
||||
{
|
||||
collectDrawPacketsTG.AddTask(
|
||||
@@ -506,32 +517,48 @@ namespace AZ
|
||||
// Launch CullingSystem::ProcessCullables() jobs (will run concurrently with FeatureProcessor::Render() jobs if m_parallelOctreeTraversal)
|
||||
bool parallelOctreeTraversal = m_cullingScene->GetDebugContext().m_parallelOctreeTraversal;
|
||||
m_cullingScene->BeginCulling(m_renderPacket.m_views);
|
||||
AZ::JobCompletion processCullablesCompletion;
|
||||
for (ViewPtr& viewPtr : m_renderPacket.m_views)
|
||||
static const AZ::TaskDescriptor processCullablesDescriptor{"AZ::RPI::Scene::ProcessCullables", "Graphics"};
|
||||
AZ::TaskGraphEvent processCullablesTGEvent;
|
||||
AZ::TaskGraph processCullablesTG;
|
||||
if (parallelOctreeTraversal)
|
||||
{
|
||||
AZ::Job* processCullablesJob = AZ::CreateJobFunction([this, &viewPtr](AZ::Job& thisJob)
|
||||
{
|
||||
m_cullingScene->ProcessCullables(*this, *viewPtr, thisJob); // can't call directly because ProcessCullables needs a parent job
|
||||
},
|
||||
true, nullptr); //auto-deletes
|
||||
if (parallelOctreeTraversal)
|
||||
for (ViewPtr& viewPtr : m_renderPacket.m_views)
|
||||
{
|
||||
processCullablesJob->SetDependent(&processCullablesCompletion);
|
||||
processCullablesJob->Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
processCullablesJob->StartAndWaitForCompletion();
|
||||
processCullablesTG.AddTask(processCullablesDescriptor, [this, &viewPtr, &processCullablesTGEvent]()
|
||||
{
|
||||
AZ::TaskGraph subTaskGraph;
|
||||
m_cullingScene->ProcessCullablesTG(*this, *viewPtr, subTaskGraph);
|
||||
if (!subTaskGraph.IsEmpty())
|
||||
{
|
||||
subTaskGraph.Detach();
|
||||
subTaskGraph.Submit(&processCullablesTGEvent);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ViewPtr& viewPtr : m_renderPacket.m_views)
|
||||
{
|
||||
m_cullingScene->ProcessCullablesTG(*this, *viewPtr, processCullablesTG);
|
||||
}
|
||||
}
|
||||
bool processCullablesHasWork = !processCullablesTG.IsEmpty();
|
||||
if (processCullablesHasWork)
|
||||
{
|
||||
processCullablesTG.Submit(&processCullablesTGEvent);
|
||||
}
|
||||
|
||||
WaitTGEvent(collectDrawPacketsTGEvent);
|
||||
processCullablesCompletion.StartAndWaitForCompletion();
|
||||
collectDrawPacketsTGEvent.Wait();
|
||||
if (processCullablesHasWork) // skip the wait if there is no work to do
|
||||
{
|
||||
processCullablesTGEvent.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::CollectDrawPacketsJobs()
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "CollectDrawPackets");
|
||||
AZ_PROFILE_SCOPE(RPI, "CollectDrawPacketsJobs");
|
||||
AZ::JobCompletion* collectDrawPacketsCompletion = aznew AZ::JobCompletion();
|
||||
|
||||
// Launch FeatureProcessor::Render() jobs
|
||||
@@ -553,7 +580,7 @@ namespace AZ
|
||||
{
|
||||
AZ::Job* processCullablesJob = AZ::CreateJobFunction([this, &viewPtr](AZ::Job& thisJob)
|
||||
{
|
||||
m_cullingScene->ProcessCullables(*this, *viewPtr, thisJob); // can't call directly because ProcessCullables needs a parent job
|
||||
m_cullingScene->ProcessCullablesJobs(*this, *viewPtr, thisJob); // can't call directly because ProcessCullables needs a parent job
|
||||
},
|
||||
true, nullptr); //auto-deletes
|
||||
if (m_cullingScene->GetDebugContext().m_parallelOctreeTraversal)
|
||||
@@ -586,7 +613,7 @@ namespace AZ
|
||||
});
|
||||
}
|
||||
finalizeDrawListsTG.Submit(&finalizeDrawListsTGEvent);
|
||||
WaitTGEvent(finalizeDrawListsTGEvent);
|
||||
finalizeDrawListsTGEvent.Wait();
|
||||
}
|
||||
|
||||
void Scene::FinalizeDrawListsJobs()
|
||||
@@ -612,7 +639,7 @@ namespace AZ
|
||||
|
||||
if (m_taskGraphActive)
|
||||
{
|
||||
WaitTGEvent(m_simulationFinishedTGEvent, &m_simulationFinishedWorkActive);
|
||||
WaitAndCleanTGEvent(AZStd::move(m_simulationFinishedTGEvent));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -785,6 +812,11 @@ namespace AZ
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
AZ::Name Scene::GetName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
bool Scene::SetDefaultRenderPipeline(const RenderPipelineId& pipelineId)
|
||||
{
|
||||
|
||||
@@ -320,6 +320,30 @@ namespace AZ
|
||||
}
|
||||
|
||||
const ShaderVariant& Shader::GetVariant(ShaderVariantStableId shaderVariantStableId)
|
||||
{
|
||||
const ShaderVariant& variant = GetVariantInternal(shaderVariantStableId);
|
||||
|
||||
if (ShaderReloadDebugTracker::IsEnabled())
|
||||
{
|
||||
auto makeTimeString = [](AZStd::sys_time_t timestamp, AZStd::sys_time_t now)
|
||||
{
|
||||
AZStd::sys_time_t elapsedMicroseconds = now - timestamp;
|
||||
double elapsedSeconds = aznumeric_cast<double>(elapsedMicroseconds / 1'000'000);
|
||||
AZStd::string timeString = AZStd::string::format("%lld (%f seconds ago)", timestamp, elapsedSeconds);
|
||||
return timeString;
|
||||
};
|
||||
|
||||
AZStd::sys_time_t now = AZStd::GetTimeNowMicroSecond();
|
||||
|
||||
ShaderReloadDebugTracker::Printf("{%p}->Shader::GetVariant for shader '%s' [build time %s] found variant '%s' [build time %s]", this,
|
||||
m_asset.GetHint().c_str(), makeTimeString(m_asset->GetBuildTimestamp(), now).c_str(),
|
||||
variant.GetShaderVariantAsset().GetHint().c_str(), makeTimeString(variant.GetShaderVariantAsset()->GetBuildTimestamp(), now).c_str());
|
||||
}
|
||||
|
||||
return variant;
|
||||
}
|
||||
|
||||
const ShaderVariant& Shader::GetVariantInternal(ShaderVariantStableId shaderVariantStableId)
|
||||
{
|
||||
if (!shaderVariantStableId.IsValid() || shaderVariantStableId == ShaderAsset::RootShaderVariantStableId)
|
||||
{
|
||||
@@ -336,7 +360,7 @@ namespace AZ
|
||||
// reloaded, but some (or all) shader variants haven't been built yet. Since we want to use the latest version of the
|
||||
// shader code, ignore the old variants and fall back to the newer root variant instead. There's no need to report a
|
||||
// warning here because m_asset->GetVariant below will report one.
|
||||
if (findIt->second.GetBuildTimestamp() >= m_asset->GetShaderAssetBuildTimestamp())
|
||||
if (findIt->second.GetBuildTimestamp() >= m_asset->GetBuildTimestamp())
|
||||
{
|
||||
return findIt->second;
|
||||
}
|
||||
@@ -359,7 +383,7 @@ namespace AZ
|
||||
auto findIt = m_shaderVariants.find(shaderVariantStableId);
|
||||
if (findIt != m_shaderVariants.end())
|
||||
{
|
||||
if (findIt->second.GetBuildTimestamp() >= m_asset->GetShaderAssetBuildTimestamp())
|
||||
if (findIt->second.GetBuildTimestamp() >= m_asset->GetBuildTimestamp())
|
||||
{
|
||||
return findIt->second;
|
||||
}
|
||||
|
||||
@@ -7,24 +7,75 @@
|
||||
*/
|
||||
|
||||
#include <Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h>
|
||||
#include <AzCore/Module/Environment.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RPI
|
||||
{
|
||||
bool ShaderReloadDebugTracker::s_enabled = false;
|
||||
int ShaderReloadDebugTracker::s_indent = 0;
|
||||
namespace ShaderReloadDebugTrackerInternal
|
||||
{
|
||||
static const char EnabledVariableName[] = "ShaderReloadDebugTracker enabled";
|
||||
static const char IndentVariableName[] = "ShaderReloadDebugTracker indent";
|
||||
|
||||
static EnvironmentVariable<bool> s_enabled;
|
||||
static EnvironmentVariable<int> s_indent;
|
||||
}
|
||||
|
||||
void ShaderReloadDebugTracker::Init()
|
||||
{
|
||||
ShaderReloadDebugTrackerInternal::s_enabled = AZ::Environment::CreateVariable<bool>(ShaderReloadDebugTrackerInternal::EnabledVariableName);
|
||||
ShaderReloadDebugTrackerInternal::s_indent = AZ::Environment::CreateVariable<int>(ShaderReloadDebugTrackerInternal::IndentVariableName);
|
||||
|
||||
ShaderReloadDebugTrackerInternal::s_enabled.Get() = false;
|
||||
ShaderReloadDebugTrackerInternal::s_indent.Get() = 0;
|
||||
}
|
||||
|
||||
void ShaderReloadDebugTracker::Shutdown()
|
||||
{
|
||||
ShaderReloadDebugTrackerInternal::s_enabled.Reset();
|
||||
ShaderReloadDebugTrackerInternal::s_indent.Reset();
|
||||
}
|
||||
|
||||
void ShaderReloadDebugTracker::MakeReady()
|
||||
{
|
||||
if (!ShaderReloadDebugTrackerInternal::s_enabled.IsValid())
|
||||
{
|
||||
ShaderReloadDebugTrackerInternal::s_enabled = AZ::Environment::FindVariable<bool>(ShaderReloadDebugTrackerInternal::EnabledVariableName);
|
||||
ShaderReloadDebugTrackerInternal::s_indent = AZ::Environment::FindVariable<int>(ShaderReloadDebugTrackerInternal::IndentVariableName);
|
||||
}
|
||||
}
|
||||
|
||||
bool ShaderReloadDebugTracker::IsEnabled()
|
||||
{
|
||||
#ifdef AZ_ENABLE_SHADER_RELOAD_DEBUG_TRACKER
|
||||
MakeReady();
|
||||
|
||||
// Set this to true in the debugger to turn on hot reload tracing.
|
||||
// If needed, we could hook this up to a CVar.
|
||||
return s_enabled;
|
||||
return ShaderReloadDebugTrackerInternal::s_enabled.Get();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ShaderReloadDebugTracker::AddIndent()
|
||||
{
|
||||
MakeReady();
|
||||
ShaderReloadDebugTrackerInternal::s_indent.Get() += IndentSpaces;
|
||||
}
|
||||
|
||||
void ShaderReloadDebugTracker::RemoveIndent()
|
||||
{
|
||||
MakeReady();
|
||||
ShaderReloadDebugTrackerInternal::s_indent.Get() -= IndentSpaces;
|
||||
}
|
||||
|
||||
int ShaderReloadDebugTracker::GetIndent()
|
||||
{
|
||||
MakeReady();
|
||||
return ShaderReloadDebugTrackerInternal::s_indent.Get();
|
||||
}
|
||||
|
||||
ShaderReloadDebugTracker::ScopedSection::~ScopedSection()
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <Atom/RPI.Public/Shader/Shader.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderResourceGroupPool.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Asset/AssetHandler.h>
|
||||
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
|
||||
@@ -86,10 +87,13 @@ namespace AZ
|
||||
};
|
||||
Data::InstanceDatabase<ShaderResourceGroupPool>::Create(azrtti_typeid<ShaderResourceGroupPool>(), handler, false);
|
||||
}
|
||||
|
||||
ShaderReloadDebugTracker::Init();
|
||||
}
|
||||
|
||||
void ShaderSystem::Shutdown()
|
||||
{
|
||||
ShaderReloadDebugTracker::Shutdown();
|
||||
Data::InstanceDatabase<Shader>::Destroy();
|
||||
Data::InstanceDatabase<ShaderResourceGroup>::Destroy();
|
||||
Data::InstanceDatabase<ShaderResourceGroupPool>::Destroy();
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace AZ
|
||||
->Field("pipelineStateType", &ShaderAsset::m_pipelineStateType)
|
||||
->Field("shaderOptionGroupLayout", &ShaderAsset::m_shaderOptionGroupLayout)
|
||||
->Field("drawListName", &ShaderAsset::m_drawListName)
|
||||
->Field("shaderAssetBuildTimestamp", &ShaderAsset::m_shaderAssetBuildTimestamp)
|
||||
->Field("shaderAssetBuildTimestamp", &ShaderAsset::m_buildTimestamp)
|
||||
->Field("perAPIShaderData", &ShaderAsset::m_perAPIShaderData)
|
||||
;
|
||||
}
|
||||
@@ -134,11 +134,11 @@ namespace AZ
|
||||
return m_drawListName;
|
||||
}
|
||||
|
||||
AZStd::sys_time_t ShaderAsset::GetShaderAssetBuildTimestamp() const
|
||||
AZStd::sys_time_t ShaderAsset::GetBuildTimestamp() const
|
||||
{
|
||||
return m_shaderAssetBuildTimestamp;
|
||||
return m_buildTimestamp;
|
||||
}
|
||||
|
||||
|
||||
void ShaderAsset::SetReady()
|
||||
{
|
||||
m_status = AssetStatus::Ready;
|
||||
@@ -256,7 +256,7 @@ namespace AZ
|
||||
}
|
||||
return GetRootVariant(supervariantIndex);
|
||||
}
|
||||
else if (variant->GetBuildTimestamp() >= m_shaderAssetBuildTimestamp)
|
||||
else if (variant->GetBuildTimestamp() >= m_buildTimestamp)
|
||||
{
|
||||
return variant;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace AZ
|
||||
{
|
||||
if (ValidateIsReady())
|
||||
{
|
||||
m_asset->m_shaderAssetBuildTimestamp = shaderAssetBuildTimestamp;
|
||||
m_asset->m_buildTimestamp = shaderAssetBuildTimestamp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,7 +390,7 @@ namespace AZ
|
||||
m_asset->m_pipelineStateType = sourceShaderAsset.m_pipelineStateType;
|
||||
m_asset->m_drawListName = sourceShaderAsset.m_drawListName;
|
||||
m_asset->m_shaderOptionGroupLayout = sourceShaderAsset.m_shaderOptionGroupLayout;
|
||||
m_asset->m_shaderAssetBuildTimestamp = sourceShaderAsset.m_shaderAssetBuildTimestamp;
|
||||
m_asset->m_buildTimestamp = sourceShaderAsset.m_buildTimestamp;
|
||||
|
||||
// copy root variant assets
|
||||
for (auto& perAPIShaderData : sourceShaderAsset.m_perAPIShaderData)
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace AtomToolsFramework
|
||||
&PreviewerFeatureProcessorProviderBus::Handler::GetRequiredFeatureProcessors, featureProcessors);
|
||||
|
||||
AZ::RPI::SceneDescriptor sceneDesc;
|
||||
sceneDesc.m_nameId = AZ::Name("PreviewRenderer");
|
||||
sceneDesc.m_featureProcessorNames.assign(featureProcessors.begin(), featureProcessors.end());
|
||||
m_scene = AZ::RPI::Scene::CreateScene(sceneDesc);
|
||||
|
||||
|
||||
+10
-13
@@ -47,30 +47,27 @@ namespace AtomToolsFramework
|
||||
|
||||
void PreviewRendererSystemComponent::Activate()
|
||||
{
|
||||
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
|
||||
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusConnect();
|
||||
PreviewRendererSystemRequestBus::Handler::BusConnect();
|
||||
|
||||
AZ::TickBus::QueueFunction(
|
||||
[this]()
|
||||
{
|
||||
if (!m_previewRenderer)
|
||||
{
|
||||
m_previewRenderer.reset(aznew AtomToolsFramework::PreviewRenderer(
|
||||
"PreviewRendererSystemComponent Preview Scene", "PreviewRendererSystemComponent Preview Pipeline"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void PreviewRendererSystemComponent::Deactivate()
|
||||
{
|
||||
PreviewRendererSystemRequestBus::Handler::BusDisconnect();
|
||||
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusDisconnect();
|
||||
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
|
||||
m_previewRenderer.reset();
|
||||
}
|
||||
|
||||
void PreviewRendererSystemComponent::OnCatalogLoaded([[maybe_unused]] const char* catalogFile)
|
||||
{
|
||||
AZ::TickBus::QueueFunction([this](){
|
||||
if (!m_previewRenderer)
|
||||
{
|
||||
m_previewRenderer.reset(aznew AtomToolsFramework::PreviewRenderer(
|
||||
"PreviewRendererSystemComponent Preview Scene", "PreviewRendererSystemComponent Preview Pipeline"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void PreviewRendererSystemComponent::OnApplicationAboutToStop()
|
||||
{
|
||||
m_previewRenderer.reset();
|
||||
|
||||
-5
@@ -9,7 +9,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <AtomToolsFramework/PreviewRenderer/PreviewRendererSystemRequestBus.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzFramework/Application/Application.h>
|
||||
#include <PreviewRenderer/PreviewRenderer.h>
|
||||
@@ -19,7 +18,6 @@ namespace AtomToolsFramework
|
||||
//! System component that manages a global PreviewRenderer.
|
||||
class PreviewRendererSystemComponent final
|
||||
: public AZ::Component
|
||||
, public AzFramework::AssetCatalogEventBus::Handler
|
||||
, public AzFramework::ApplicationLifecycleEvents::Bus::Handler
|
||||
, public PreviewRendererSystemRequestBus::Handler
|
||||
{
|
||||
@@ -38,9 +36,6 @@ namespace AtomToolsFramework
|
||||
void Deactivate() override;
|
||||
|
||||
private:
|
||||
// AzFramework::AssetCatalogEventBus::Handler overrides ...
|
||||
void OnCatalogLoaded(const char* catalogFile) override;
|
||||
|
||||
// AzFramework::ApplicationLifecycleEvents overrides...
|
||||
void OnApplicationAboutToStop() override;
|
||||
|
||||
|
||||
+2
-2
@@ -279,9 +279,9 @@ namespace MaterialEditor
|
||||
// reset environment
|
||||
AZ::Transform iblTransform = AZ::Transform::CreateIdentity();
|
||||
AZ::TransformBus::Event(m_iblEntityId, &AZ::TransformBus::Events::SetLocalTM, iblTransform);
|
||||
|
||||
const AZ::Matrix4x4 rotationMatrix = AZ::Matrix4x4::CreateIdentity();
|
||||
AZ::RPI::ScenePtr scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene();
|
||||
auto skyBoxFeatureProcessorInterface = scene->GetFeatureProcessor<AZ::Render::SkyBoxFeatureProcessorInterface>();
|
||||
auto skyBoxFeatureProcessorInterface = AZ::RPI::Scene::GetFeatureProcessorForEntity<AZ::Render::SkyBoxFeatureProcessorInterface>(m_iblEntityId);
|
||||
skyBoxFeatureProcessorInterface->SetCubemapRotationMatrix(rotationMatrix);
|
||||
|
||||
if (m_behavior)
|
||||
|
||||
+1
-2
@@ -25,8 +25,7 @@ namespace MaterialEditor
|
||||
m_iblEntityId,
|
||||
&MaterialEditorViewportInputControllerRequestBus::Handler::GetIblEntityId);
|
||||
AZ_Assert(m_iblEntityId.IsValid(), "Failed to find m_iblEntityId");
|
||||
AZ::RPI::ScenePtr scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene();
|
||||
m_skyBoxFeatureProcessorInterface = scene->GetFeatureProcessor<AZ::Render::SkyBoxFeatureProcessorInterface>();
|
||||
m_skyBoxFeatureProcessorInterface = AZ::RPI::Scene::GetFeatureProcessorForEntity<AZ::Render::SkyBoxFeatureProcessorInterface>(m_iblEntityId);
|
||||
}
|
||||
|
||||
void RotateEnvironmentBehavior::TickInternal(float x, float y, float z)
|
||||
|
||||
@@ -67,6 +67,7 @@ namespace MaterialEditor
|
||||
|
||||
// Create and register a scene with all available feature processors
|
||||
AZ::RPI::SceneDescriptor sceneDesc;
|
||||
sceneDesc.m_nameId = AZ::Name("MaterialViewport");
|
||||
m_scene = AZ::RPI::Scene::CreateScene(sceneDesc);
|
||||
m_scene->EnableAllFeatureProcessors();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user