You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/Vegetation/Code/Source/Debugger/DebugComponent.h

203 lines
8.6 KiB
C++

/*
* 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
*
*/
#pragma once
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Math/Vector3.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzFramework/Visibility/BoundsBus.h>
#include <AzCore/PlatformDef.h>
#include <Vegetation/Ebuses/InstanceSystemRequestBus.h>
#include <Vegetation/Ebuses/SystemConfigurationBus.h>
#include <Vegetation/Ebuses/DebugRequestsBus.h>
#include <Vegetation/Ebuses/DebugNotificationBus.h>
#include <Vegetation/Ebuses/DebugSystemDataBus.h>
#include <Vegetation/InstanceData.h>
namespace LmbrCentral
{
template<typename, typename>
class EditorWrappedComponentBase;
}
namespace Vegetation
{
//////////////////////////////////////////////////////////////////////////
class DebugConfig
: public AZ::ComponentConfig
{
public:
AZ_CLASS_ALLOCATOR(DebugConfig, AZ::SystemAllocator, 0);
AZ_RTTI(DebugConfig, "{10750041-ABCA-4515-8D5D-B3E4769C3829}", AZ::ComponentConfig);
static void Reflect(AZ::ReflectContext* context);
DebugRequests::FilterTypeLevel m_filterLevel = DebugRequests::FilterTypeLevel::Warning;
DebugRequests::SortType m_sortType = DebugRequests::SortType::BySector;
AZ::u32 m_collectionFrequencyUs = 500000;
AZ::u32 m_minThresholdUs = 500;
AZ::u32 m_maxThresholdUs = 1500;
AZ::u32 m_maxLabelDisplayDistance = 40;
AZ::u32 m_maxDatapointDisplayCount = 1000;
bool m_showVisualization = {};
bool m_showDebugStats = {};
bool m_showInstanceVisualization = {};
};
//////////////////////////////////////////////////////////////////////////
class DebugComponent
: public AZ::Component
, private AzFramework::EntityDebugDisplayEventBus::Handler
, private AzFramework::BoundsRequestBus::Handler
, private DebugRequestBus::Handler
, private DebugNotificationBus::Handler
, private SystemConfigurationRequestBus::Handler
{
public:
template<typename, typename> friend class LmbrCentral::EditorWrappedComponentBase;
AZ_COMPONENT(DebugComponent, "{E62A9E15-E763-4069-8AE5-93276F1E7AC7}");
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void Reflect(AZ::ReflectContext* context);
DebugComponent(const DebugConfig& configuration) : m_configuration(configuration) {}
DebugComponent() = default;
~DebugComponent() = default;
//////////////////////////////////////////////////////////////////////////
// AZ::Component interface implementation
void Activate() override;
void Deactivate() override;
bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override;
bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override;
//////////////////////////////////////////////////////////////////////////
// EntityDebugDisplayEventBus
// Ideally this would use ViewportDebugDisplayEventBus::DisplayViewport, but that doesn't currently work in game mode,
// so instead we use this plus the BoundsRequestBus with a large AABB to get ourselves rendered.
void DisplayEntityViewport(
const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override;
//////////////////////////////////////////////////////////////////////////
// DebugNotifications
void FillSectorStart(int sectorX, int sectorY, TimePoint timePoint) override;
void FillSectorEnd(int sectorX, int sectorY, TimePoint timePoint, AZ::u32 unusedClaimPointCount) override;
void FillAreaStart(AZ::EntityId areaId, TimePoint timePoint) override;
void MarkAreaRejectedByMask(AZ::EntityId areaId) override;
void FillAreaEnd(AZ::EntityId areaId, TimePoint timePoint, AZ::u32 unusedClaimPointCount) override;
void FilterInstance(AZ::EntityId areaId, AZStd::string_view filterReason) override;
void CreateInstance(InstanceId instanceId, AZ::Vector3 position, AZ::EntityId areaId) override;
void DeleteInstance(InstanceId instanceId) override;
void DeleteAllInstances() override;
void ExportCurrentReport() override;
void ToggleVisualization() override;
//////////////////////////////////////////////////////////////////////////
// DebugRequests
void GetPerformanceReport(PerformanceReport& report) const override;
void DumpPerformanceReport(const PerformanceReport& report, FilterTypeLevel filter, SortType sort) const override;
void ClearPerformanceReport() override;
//////////////////////////////////////////////////////////////////////////
// SystemConfigurationRequestBus
void UpdateSystemConfig(const AZ::ComponentConfig* config) override;
void GetSystemConfig([[maybe_unused]] AZ::ComponentConfig* config) const override {}; // ignore this call
//////////////////////////////////////////////////////////////////////////
// BoundsRequestBus
AZ::Aabb GetWorldBounds() override
{
// DisplayEntityViewport relies on the BoundsRequestBus to get the entity bounds to determine when to call debug drawing
// for that entity. Since this is a level component that can draw infinitely far in every direction, we return an
// effectively infinite AABB so that it always draws.
return AZ::Aabb::CreateFromMinMax(AZ::Vector3(-AZ::Constants::FloatMax), AZ::Vector3(AZ::Constants::FloatMax));
}
AZ::Aabb GetLocalBounds() override
{
// The local and world bounds will be the same for this component.
return GetWorldBounds();
}
protected:
void PrepareNextReport();
void CopyReportToSortedList();
void DrawSectorTimingData(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay);
void DrawDebugStats(AzFramework::DebugDisplayRequests& debugDisplay);
void DrawInstanceDebug(AzFramework::DebugDisplayRequests& debugDisplay);
private:
AZStd::atomic_bool m_exportCurrentReport{ false };
mutable AZStd::recursive_mutex m_reportMutex;
DebugRequests::PerformanceReport m_thePerformanceReport;
DebugConfig m_configuration;
TimePoint m_lastCollectionTime = {};
// internal tracking
struct SectorAreaData
{
TimePoint m_start;
TimePoint m_end;
size_t m_numInstancesCreated = 0; // number of instances in this sector/area combination
FilterReasonCount m_numInstancesRejectedByFilters;
bool m_filteredByMasks = false; // if this area was filtered because of the inclusive/exclusive masks in this sector/area combination
};
struct SectorTracker
{
SectorId m_id;
TimePoint m_start;
TimePoint m_end;
size_t m_numInstancesCreated = 0;// number of instances in the sector over all areas.
size_t m_numClaimPointsRemaining = 0;
AZStd::unordered_map<AreaId, SectorAreaData> m_perAreaTracking;
};
using SectorData = AZStd::vector<SectorTracker>;
SectorTracker m_currentSectorTiming;
SectorData m_sectorData;
struct AreaTracker
{
AreaId m_id;
TimePoint m_start;
TimePoint m_end;
SectorId m_sectorId;
size_t m_numInstancesCreated; // number of instances in this area over all sectors
FilterReasonCount m_numInstancesRejectedByFilters;
size_t m_numClaimPointsRemaining = 0;
bool m_filteredByMasks; // true if this area was always filtered
};
using AreaData = AZStd::vector<AreaTracker>;
AZStd::size_t MakeAreaSectorKey(AZ::EntityId areaId, SectorId sectorId);
AZStd::unordered_map<uint64_t, AreaTracker> m_currentAreasTiming;
AreaData m_areaData;
AZStd::vector<SectorTiming> m_currentSortedTimingList;
//! Cached pointer to the veg system debug data
DebugData* m_debugData = nullptr;
struct DebugInstanceData
{
AZ::Vector3 m_position;
AreaId m_areaId;
};
AZStd::unordered_map<InstanceId, DebugInstanceData> m_activeInstances;
};
} // namespace Vegetation