summaryrefslogtreecommitdiff
path: root/Runtime/Profiler/ProfilerProperty.cpp
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Profiler/ProfilerProperty.cpp
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/Profiler/ProfilerProperty.cpp')
-rw-r--r--Runtime/Profiler/ProfilerProperty.cpp789
1 files changed, 789 insertions, 0 deletions
diff --git a/Runtime/Profiler/ProfilerProperty.cpp b/Runtime/Profiler/ProfilerProperty.cpp
new file mode 100644
index 0000000..c854908
--- /dev/null
+++ b/Runtime/Profiler/ProfilerProperty.cpp
@@ -0,0 +1,789 @@
+#include "UnityPrefix.h"
+#include "ProfilerProperty.h"
+
+#if ENABLE_PROFILER && UNITY_EDITOR
+
+#include "ProfilerImpl.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+#include <iostream>
+#include <sstream>
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+
+/*
+TODO:
+* Sync time filter does not work
+* non multiple selection in raw hierarch
+
+* select object in double click
+* Tooltip showing all names
+ * deep profiler???
+ */
+
+
+
+// Constant and static variables initialization
+static const char* const kRadixPoint = "0.";
+static const char* const kPercentFmt = "%d.%d%%";
+static const char* kNotAvailable = "N/A";
+
+
+struct ProfilerHierarchy
+{
+ ProfilerHierarchy() : oneSample(~0), sampleIdx(kMemProfiler) {}
+
+ // Can correspond to one or more individual samples. Optimize common use case (especially in timeline view)
+ // when there is exactly one sample: that is stored in 'oneSample'. If there are more samples, they
+ // are put into an array.
+ UInt32 oneSample;
+ dynamic_array<UInt32> sampleIdx;
+
+ UNITY_VECTOR(kMemProfiler,ProfilerHierarchy) children;
+ ProfilerHierarchy* parent;
+
+ ProfilerSampleData data;
+};
+
+// Implement swap to avoid massive amounts of copying during the sort function
+namespace std
+{
+ template<>
+ inline void swap(ProfilerHierarchy& __a, ProfilerHierarchy& __b)
+ {
+ std::swap(__a.oneSample, __b.oneSample);
+ __a.sampleIdx.swap(__b.sampleIdx);
+ __a.children.swap(__b.children);
+ std::swap(__a.parent, __b.parent);
+ std::swap(__a.data, __b.data);
+ }
+}
+
+
+static std::string GetFormattedPercent(ProfileTimeFormat inTime, ProfileTimeFormat totalTime)
+{
+ if (totalTime == 0)
+ return "0.0%";
+
+ // Calculate percentage, format obtained value and convert into string
+ UInt64 time = inTime * (UInt64) 1000;
+ UInt64 integer = time / totalTime;
+
+ return Format(kPercentFmt, (int) integer / 10, (int) integer % 10);
+}
+
+static std::string GetFormattedTime(ProfileTimeFormat time, int decimals = 2)
+{
+ time /= (1000000/Pow(10,decimals));
+
+ std::string value = Format("%u", time);
+
+ int length = value.length();
+
+ if (length > decimals)
+ {
+ value.insert(length - decimals, 1, '.');
+ }
+ else
+ {
+ std::string tmp;
+ tmp.assign(decimals, '0');
+
+ value.copy((char*) tmp.c_str() + (decimals - length), length);
+
+ return kRadixPoint + tmp;
+ }
+
+ return value;
+}
+
+static const char* GetObjectNameFromInstanceID (SInt32 instanceID)
+{
+ Object* obj = dynamic_instanceID_cast<Object*>(instanceID);
+ if (obj)
+ return obj->GetName();
+ else
+ return kNotAvailable;
+}
+
+static const char* GetFunctionName(const ProfilerSampleData& input)
+{
+ if (input.information)
+ return input.information->name;
+ else
+ return "Invalid";
+}
+
+static bool LargerFunctionName(const ProfilerSampleData& lhs, const ProfilerSampleData& rhs)
+{
+
+ if (!lhs.information || !rhs.information)
+ return &lhs < &rhs;
+
+ return StrICmp(lhs.information->name, rhs.information->name) > 0;
+}
+
+
+static void GetSortedInstanceIDs (const dynamic_array<AdditionalProfilerSampleData*>& additionalData, UInt32 oneSample, const dynamic_array<UInt32>& samples, dynamic_array<SInt32>& outputInstanceIDs)
+{
+ // Get sorted list of instanceID's
+ outputInstanceIDs.push_back(additionalData[oneSample]?additionalData[oneSample]->instanceID:0);
+ for (int i=0;i<samples.size();i++)
+ outputInstanceIDs.push_back(additionalData[samples[i]]?additionalData[samples[i]]->instanceID:0);
+ std::sort (outputInstanceIDs.begin(), outputInstanceIDs.end());
+}
+
+static const char* GetObjectNameSummary (UInt32 oneSample, const dynamic_array<UInt32>& samples, const dynamic_array<AdditionalProfilerSampleData*>& additionalData)
+{
+ int instanceID = additionalData[oneSample] ? additionalData[oneSample]->instanceID : 0;
+ const char* name = GetObjectNameFromInstanceID(instanceID);
+
+ for (int i=0;i<samples.size();i++)
+ {
+ int instanceID = additionalData[samples[i]]?additionalData[samples[i]]->instanceID:0;
+ const char* tempName = GetObjectNameFromInstanceID(instanceID);
+ if (name != tempName) //@TODO: this is just comparing pointers?!
+ return "multiple";
+ }
+
+ return name;
+}
+
+static std::string GetProfilerColumn (const ProfilerSampleData& data, UInt32 oneSample, const dynamic_array<UInt32>& samples, const dynamic_array<AdditionalProfilerSampleData*>& additionalData, bool supportsGPUProfiler, ProfilerColumn column)
+{
+ switch (column)
+ {
+ case kFunctionNameColumn:
+ return GetFunctionName(data);
+
+ case kTotalPercentColumn:
+ return GetFormattedPercent(data.time, data.rootTime);
+
+ case kSelfPercentColumn:
+ if (data.time > data.childrenTime)
+ return GetFormattedPercent(data.time - data.childrenTime, data.rootTime);
+ else
+ return GetFormattedPercent(0u, 1u);
+
+ case kTotalGPUPercentColumn:
+ if (!supportsGPUProfiler)
+ return kNotAvailable;
+ else if (supportsGPUProfiler)
+ return GetFormattedPercent(data.gpuTime, data.rootGPUTime);
+ else
+ return kNotAvailable;
+
+ case kSelfGPUPercentColumn:
+ if (!supportsGPUProfiler)
+ return kNotAvailable;
+ else if (data.gpuTime > data.childrenGPUTime)
+ return GetFormattedPercent(data.gpuTime - data.childrenGPUTime, data.rootGPUTime);
+ else
+ return GetFormattedPercent(0u, 1u);
+
+ case kCallsColumn:
+ return Format("%u", data.numberOfCalls);
+
+ case kWarningColumn:
+ if(data.warningCount == 0)
+ return "";
+ return Format("%u", data.warningCount);
+
+ case kGCMemory:
+ return FormatBytes(data.allocatedGCMemory);
+
+ case kTotalTimeColumn:
+ return GetFormattedTime(data.time);
+
+ case kSelfTimeColumn:
+ if (data.time > data.childrenTime)
+ return GetFormattedTime(data.time - data.childrenTime);
+ else
+ return GetFormattedTime(0u);
+
+ case kDrawCallsColumn:
+ if (supportsGPUProfiler)
+ return Format("%u", data.gpuSamplesCount);
+ else
+ return kNotAvailable;
+
+ case kTotalGPUTimeColumn:
+ if (supportsGPUProfiler)
+ return GetFormattedTime(data.gpuTime,3);
+ else
+ return kNotAvailable;
+
+ case kSelfGPUTimeColumn:
+ if (!supportsGPUProfiler)
+ return kNotAvailable;
+ else if (data.gpuTime > data.childrenGPUTime)
+ return GetFormattedTime(data.gpuTime - data.childrenGPUTime);
+ else
+ return GetFormattedTime(0u);
+
+ case kObjectNameColumn:
+
+ return GetObjectNameSummary(oneSample, samples, additionalData);
+
+ default:
+ AssertString("Unimplemented GetProfilerColumn ");
+ return "";
+ }
+}
+
+
+static bool IsSmaller (const ProfilerSampleData& lhs, const ProfilerSampleData& rhs, ProfilerColumn criteria)
+{
+ if (criteria == kTotalTimeColumn || criteria == kTotalPercentColumn)
+ {
+ if (lhs.time != rhs.time)
+ return lhs.time < rhs.time;
+ }
+ else if (criteria == kTotalGPUTimeColumn || criteria == kTotalGPUPercentColumn)
+ {
+ if (lhs.gpuTime != rhs.gpuTime)
+ return lhs.gpuTime < rhs.gpuTime;
+ }
+ else if (criteria == kSelfTimeColumn || criteria == kSelfPercentColumn)
+ {
+ if ((lhs.time - lhs.childrenTime) != (rhs.time - rhs.childrenTime))
+ return (lhs.time - lhs.childrenTime) < (rhs.time - rhs.childrenTime);
+ }
+ else if (criteria == kSelfGPUTimeColumn || criteria == kSelfGPUPercentColumn)
+ {
+ if ((lhs.gpuTime - lhs.childrenGPUTime) != (rhs.gpuTime - rhs.childrenGPUTime))
+ return (lhs.gpuTime - lhs.childrenGPUTime) < (rhs.gpuTime - rhs.childrenGPUTime);
+ }
+ else if (criteria == kGCMemory)
+ {
+ if (lhs.allocatedGCMemory != rhs.allocatedGCMemory)
+ return lhs.allocatedGCMemory < rhs.allocatedGCMemory;
+ }
+ else if (criteria == kDrawCallsColumn)
+ {
+ if (lhs.gpuSamplesCount != rhs.gpuSamplesCount)
+ return lhs.gpuSamplesCount < rhs.gpuSamplesCount;
+ }
+ else if (criteria == kFunctionNameColumn)
+ {
+ return LargerFunctionName(lhs, rhs);
+ }
+ else if (criteria == kWarningColumn)
+ {
+ return lhs.warningCount < rhs.warningCount;
+ }
+ else if (criteria == kCallsColumn)
+ {
+ if (lhs.numberOfCalls != rhs.numberOfCalls)
+ return lhs.numberOfCalls < rhs.numberOfCalls;
+ }
+ else if (criteria == kObjectNameColumn)
+ {
+ // Not supported. Screws with the data flow and not worth it...
+ return LargerFunctionName(lhs, rhs);
+ }
+ else
+ {
+ AssertString("Unsupported sortmode");
+ return false;
+ }
+
+ // As a fallback if the results are the same. Sort by name
+ return LargerFunctionName(lhs, rhs);
+}
+
+
+// ProfilerProperty class implementation
+//
+
+MemoryPool ProfilerProperty::s_AdditionalDataPool(false, "AdditionalData Pool", sizeof (AdditionalProfilerSampleData), 16 * 1024, kMemProfiler);
+
+ProfilerProperty::ProfilerProperty()
+ :m_Root(NULL)
+ ,m_FrameData(NULL)
+ ,m_ProfilerViewType(kViewHierarchy)
+ ,m_ProfilerSortColumn(kTotalTimeColumn)
+ ,m_Depth(0)
+ ,m_ActiveHierarchy(NULL)
+ ,m_OnlyShowGPUSamples(false)
+ ,m_AdditionalData(kMemProfiler)
+ ,m_ThreadIdx(0)
+{
+}
+
+ProfilerProperty::~ProfilerProperty()
+{
+ CleanupProperty();
+}
+
+struct SortByColumn
+{
+ ProfilerColumn column;
+
+ SortByColumn (ProfilerColumn c) : column (c) { }
+
+ bool operator () (const ProfilerHierarchy& lhs, const ProfilerHierarchy& rhs) const
+ {
+ return !IsSmaller(lhs.data, rhs.data, column);
+ }
+};
+
+struct SortByFunctionName
+{
+ const ProfilerFrameData& frameData;
+ int threadIdx;
+
+ SortByFunctionName (const ProfilerFrameData& f, int t) : frameData (f), threadIdx(t) { }
+
+ bool operator () (const UInt32 lhs, const UInt32 rhs) const
+ {
+ const char* lhsName = frameData.m_ThreadData[threadIdx].GetSample(lhs)->information->name;
+ const char* rhsName = frameData.m_ThreadData[threadIdx].GetSample(rhs)->information->name;
+ return strcmp(lhsName, rhsName) < 0;
+ }
+};
+
+static AdditionalProfilerSampleData* AllocateAdditionalData()
+{
+ void* ptr = ProfilerProperty::s_AdditionalDataPool.Allocate();
+ memset(ptr, 0, sizeof(AdditionalProfilerSampleData));
+ return (AdditionalProfilerSampleData*) ptr;
+}
+
+static void InitializeAdditionalProfilerData(const ProfilerFrameData& frameData, int threadIdx, dynamic_array<AdditionalProfilerSampleData*>& additionalData)
+{
+ const ProfilerFrameData::ThreadData& tdata = frameData.m_ThreadData[threadIdx];
+ UInt32 sampleCount = tdata.m_AllSamples.size();
+
+ for(int i = 0; i < additionalData.size(); i++)
+ ProfilerProperty::s_AdditionalDataPool.Deallocate(additionalData[i]);
+
+ additionalData.resize_uninitialized(sampleCount);
+ if (sampleCount > 0)
+ memset(&additionalData[0], 0, sampleCount*sizeof(AdditionalProfilerSampleData*));
+
+ dynamic_array<ProfilerData::AllocatedGCMemory>::const_iterator itGCMem = tdata.m_AllocatedGCMemorySamples.begin();
+ for(; itGCMem != tdata.m_AllocatedGCMemorySamples.end(); ++itGCMem)
+ {
+ if(additionalData[itGCMem->relatedSampleIndex] == 0)
+ additionalData[itGCMem->relatedSampleIndex] = AllocateAdditionalData();
+ additionalData[itGCMem->relatedSampleIndex]->allocatedGCMemory += itGCMem->allocatedGCMemory;
+ }
+
+ dynamic_array<ProfilerData::InstanceID>::const_iterator itID = tdata.m_InstanceIDSamples.begin();
+ for(; itID != tdata.m_InstanceIDSamples.end(); ++itID)
+ {
+ if(additionalData[itID->relatedSampleIndex] == 0)
+ additionalData[itID->relatedSampleIndex] = AllocateAdditionalData();
+ additionalData[itID->relatedSampleIndex]->instanceID = itID->instanceID;
+ }
+ dynamic_array<ProfilerData::GPUTime>::const_iterator itGPUTime = tdata.m_GPUTimeSamples.begin();
+ for(; itGPUTime != tdata.m_GPUTimeSamples.end(); ++itGPUTime)
+ {
+ if(additionalData[itGPUTime->relatedSampleIndex] == 0)
+ additionalData[itGPUTime->relatedSampleIndex] = AllocateAdditionalData();
+ additionalData[itGPUTime->relatedSampleIndex]->gpuTime += itGPUTime->gpuTimeInMicroSec*1000;
+ additionalData[itGPUTime->relatedSampleIndex]->drawCalls++;
+ additionalData[itGPUTime->relatedSampleIndex]->gpuSection = itGPUTime->gpuSection;
+ }
+
+ dynamic_array<UInt32>::const_iterator itWarningSample = tdata.m_WarningSamples.begin();
+ for(; itWarningSample != tdata.m_WarningSamples.end(); ++itWarningSample)
+ {
+ if(additionalData[*itWarningSample] == 0)
+ additionalData[*itWarningSample] = AllocateAdditionalData();
+ additionalData[*itWarningSample]->warningCount += 1;
+ }
+}
+
+
+static UInt32 FillAdditionalProfilerData(const ProfilerFrameData& frameData, int threadIdx, dynamic_array<AdditionalProfilerSampleData*>& additionalData, UInt32 index = 0)
+{
+ const ProfilerFrameData::ThreadData& tdata = frameData.m_ThreadData[threadIdx];
+ if (tdata.m_AllSamples.empty())
+ return 0;
+
+ int childIndex = index+1;
+ for(int i = 0; i < tdata.GetSample(index)->nbChildren; i++)
+ {
+ int nextChild = FillAdditionalProfilerData(frameData, threadIdx, additionalData, childIndex);
+ if(additionalData[childIndex])
+ {
+ if(additionalData[index] == NULL)
+ additionalData[index] = AllocateAdditionalData();
+ additionalData[index]->gpuTime += additionalData[childIndex]->gpuTime;
+ additionalData[index]->drawCalls += additionalData[childIndex]->drawCalls;
+ additionalData[index]->allocatedGCMemory += additionalData[childIndex]->allocatedGCMemory;
+ additionalData[index]->warningCount += additionalData[childIndex]->warningCount;
+ }
+ childIndex = nextChild;
+ }
+ return childIndex;
+}
+
+static void ProcessProfilerOneSample (const ProfilerFrameData::ThreadData& tdata, UInt32 sampleIndex, ProfilerSampleData& data, const dynamic_array<AdditionalProfilerSampleData*>& additionalData)
+{
+ const ProfilerSample* sample = tdata.GetSample(sampleIndex);
+ data.time += sample->timeUS*1000;
+ if(additionalData[sampleIndex])
+ {
+ data.allocatedGCMemory += additionalData[sampleIndex]->allocatedGCMemory;
+ data.gpuTime += additionalData[sampleIndex]->gpuTime;
+ data.gpuSamplesCount += additionalData[sampleIndex]->drawCalls;
+ data.warningCount += additionalData[sampleIndex]->warningCount;
+ }
+ ///@TODO: maybe not depend on data structure of samples here...
+ const ProfilerSample* child = sample + 1;
+ for (int c=0;c<sample->nbChildren;c++)
+ {
+ UInt32 childSampleIndex = child - tdata.GetRoot();
+ data.childrenTime += child->timeUS*1000;
+
+ if(additionalData[childSampleIndex])
+ {
+ data.childrenGPUTime += additionalData[childSampleIndex]->gpuTime;
+ data.childrenGPUSamplesCount += additionalData[childSampleIndex]->drawCalls;
+ }
+ child = SkipSampleRecurse(child);
+ }
+}
+
+static void ProcessProfilerSampleData (const ProfilerFrameData& frameData, int threadIdx, ProfilerHierarchy& hierarchy, const dynamic_array<AdditionalProfilerSampleData*>& additionalData)
+{
+ const ProfilerFrameData::ThreadData& tdata = frameData.m_ThreadData[threadIdx];
+ Assert(hierarchy.oneSample < tdata.m_AllSamples.size());
+
+ ProfilerSampleData& data = hierarchy.data;
+ data.time = 0;
+ data.childrenTime = 0;
+ data.childrenGPUTime = 0;
+ data.childrenGPUSamplesCount = 0;
+ data.allocatedGCMemory = 0;
+ data.gpuTime = 0;
+ data.gpuSamplesCount = 0;
+ data.startTime = 0;
+ data.numberOfCalls = 1 + hierarchy.sampleIdx.size();
+ data.warningCount = 0;
+
+ ProcessProfilerOneSample (tdata, hierarchy.oneSample, data, additionalData);
+ for (int i=0;i<hierarchy.sampleIdx.size();i++)
+ ProcessProfilerOneSample (tdata, hierarchy.sampleIdx[i], data, additionalData);
+
+ UInt32 firstIndex = hierarchy.oneSample;
+ data.rootTime = tdata.GetRoot()->timeUS*1000;
+ if(additionalData[0])
+ data.rootGPUTime = additionalData[0]->gpuTime;
+ data.information = tdata.GetSample(firstIndex)->information;
+ data.startTime = tdata.GetSample(firstIndex)->startTimeUS;
+}
+
+std::string ProfilerProperty::GetProfilerColumn (ProfilerColumn column) const
+{
+ return ::GetProfilerColumn(m_ActiveHierarchy->data, m_ActiveHierarchy->oneSample, m_ActiveHierarchy->sampleIdx, m_AdditionalData, SupportsGPUProfiler(), column);
+}
+
+
+static void BuildHierarchyLevel (const ProfilerFrameData& frameData, int threadIdx, dynamic_array<UInt32>& allSamplesThisLevel, ProfilerHierarchy* parent, ProfilerColumn sortColumn, ProfilerViewType viewType, const dynamic_array<AdditionalProfilerSampleData*>& additionData)
+{
+ /////@TODO: Dont do this.
+ if (!parent->children.empty())
+ return;
+
+ Assert(parent->children.empty()); // should only be called once to build child list
+
+ const ProfilerFrameData::ThreadData& tdata = frameData.m_ThreadData[threadIdx];
+
+ // hierarchy merging
+ if (viewType == kViewHierarchy)
+ {
+ std::sort (allSamplesThisLevel.begin(), allSamplesThisLevel.end(), SortByFunctionName(frameData,threadIdx));
+
+ const char * lastUniqueName = NULL;
+ int count = 0;
+ for (int i=0;i<allSamplesThisLevel.size();i++)
+ {
+ const char* name = tdata.GetSample(allSamplesThisLevel[i])->information->name;
+ if (lastUniqueName == NULL || strcmp(name, lastUniqueName) != 0)
+ count++;
+ }
+ parent->children.reserve(count);
+
+ lastUniqueName = NULL;
+ for (int i=0;i<allSamplesThisLevel.size();i++)
+ {
+ const char* name = tdata.GetSample(allSamplesThisLevel[i])->information->name;
+ if (lastUniqueName == NULL || strcmp(name, lastUniqueName) != 0)
+ {
+ lastUniqueName = name;
+ parent->children.push_back(ProfilerHierarchy());
+ parent->children.back().oneSample = allSamplesThisLevel[i];
+ continue;
+ }
+
+ ProfilerHierarchy& element = parent->children.back();
+ element.sampleIdx.push_back(allSamplesThisLevel[i]);
+ }
+
+ }
+ // raw data. no merging of data is necessary
+ else
+ {
+ const size_t size = allSamplesThisLevel.size();
+ parent->children.resize(size, ProfilerHierarchy());
+ for (size_t i = 0; i < size; ++i)
+ {
+ ProfilerHierarchy& element = parent->children[i];
+ element.oneSample = allSamplesThisLevel[i];
+ }
+ }
+
+ // Calculate profiler display data for all generated children
+ for (int i=0;i<parent->children.size();i++)
+ {
+ ProfilerHierarchy& element = parent->children[i];
+ element.parent = parent;
+ ProcessProfilerSampleData (frameData, threadIdx, element, additionData);
+ }
+
+ // Sort by selected column
+ // IMPORTANT: stable_sort is necessary here. std::sort crashes since it doesn't take advantage of the std::swap of ProfilerHierarchy.
+ if (sortColumn != kDontSortProfilerColumn)
+ std::stable_sort (parent->children.begin(), parent->children.end(), SortByColumn(sortColumn));
+}
+
+static void BuildHierarchyLevel (ProfilerFrameData& frameData, int threadIdx, ProfilerHierarchy* parent, ProfilerColumn sortColumn, ProfilerViewType viewType, const dynamic_array<AdditionalProfilerSampleData*>& additionData)
+{
+ const ProfilerFrameData::ThreadData& tdata = frameData.m_ThreadData[threadIdx];
+
+ int childCount = tdata.GetSample(parent->oneSample)->nbChildren;
+ for(int i = 0; i < parent->sampleIdx.size(); i++)
+ childCount += tdata.GetSample(parent->sampleIdx[i])->nbChildren;
+
+ dynamic_array<UInt32> allSamplesThisLevel(kMemTempAlloc);
+ allSamplesThisLevel.reserve(childCount);
+ tdata.ExtractAllChildSamples(parent->oneSample, allSamplesThisLevel);
+ for(int i = 0; i < parent->sampleIdx.size(); i++)
+ tdata.ExtractAllChildSamples(parent->sampleIdx[i], allSamplesThisLevel);
+
+ BuildHierarchyLevel(frameData, threadIdx, allSamplesThisLevel, parent, sortColumn, viewType, additionData);
+}
+
+
+void ProfilerProperty::GetInstanceIDs(dynamic_array<SInt32>& instanceIDs)
+{
+ GetSortedInstanceIDs (m_AdditionalData, m_ActiveHierarchy->oneSample, m_ActiveHierarchy->sampleIdx, instanceIDs);
+}
+
+std::string ProfilerProperty::GetTooltip (ProfilerColumn column)
+{
+ if (column == kTotalGPUTimeColumn)
+ {
+ if (!SupportsGPUProfiler ())
+ return "The platform you are profiling does not support GPU profiling\nMac OS X 10.7 and higher supports profiling, many mobile platforms have no builtin GPU profiling capabilities.";
+ }
+
+ return "";
+}
+
+
+
+void ProfilerProperty::SetRoot(int frame, ProfilerColumn sortColumn, ProfilerViewType viewType)
+{
+ m_ProfilerViewType = viewType;
+ m_ProfilerSortColumn = sortColumn;
+
+ // @TODO: Dont cleanup here, but call it when done with the data
+ if(m_Root)
+ CleanupProperty();
+ Assert(m_Root == NULL);
+ Assert(m_FrameData == NULL);
+
+ ProfilerFrameData* frameData = ProfilerHistory::Get().GetFrameData(frame);
+ if (frameData == NULL)
+ return;
+
+ m_FrameData = frameData;
+ m_ThreadIdx = 0;
+
+ InitializeAdditionalProfilerData(*m_FrameData, m_ThreadIdx, m_AdditionalData);
+ FillAdditionalProfilerData(*m_FrameData, m_ThreadIdx, m_AdditionalData);
+
+ m_Root = new ProfilerHierarchy();
+ m_Root->oneSample = 0;
+ m_Root->parent = NULL;
+
+ BuildHierarchyLevel(*m_FrameData, m_ThreadIdx, m_Root, m_ProfilerSortColumn, m_ProfilerViewType, m_AdditionalData);
+ ProcessProfilerSampleData(*m_FrameData, m_ThreadIdx, *m_Root, m_AdditionalData);
+
+ m_ActiveHierarchy = m_Root;
+}
+
+void ProfilerProperty::InitializeDetailProperty (const ProfilerProperty& sourceProperty)
+{
+ m_FrameData = sourceProperty.m_FrameData;
+ m_ThreadIdx = sourceProperty.m_ThreadIdx;
+ m_ProfilerViewType = kViewDetailFlat;
+ m_ProfilerSortColumn = sourceProperty.m_ProfilerSortColumn;
+
+ InitializeAdditionalProfilerData(*m_FrameData, m_ThreadIdx, m_AdditionalData);
+ FillAdditionalProfilerData(*m_FrameData, m_ThreadIdx, m_AdditionalData);
+
+ m_Root = new ProfilerHierarchy();
+ m_Root->oneSample = sourceProperty.m_ActiveHierarchy->parent->oneSample;
+ m_Root->sampleIdx = sourceProperty.m_ActiveHierarchy->parent->sampleIdx;
+ m_Root->parent = NULL;
+
+ dynamic_array<UInt32> allSamples = sourceProperty.m_ActiveHierarchy->sampleIdx;
+ allSamples.push_back (sourceProperty.m_ActiveHierarchy->oneSample);
+
+ BuildHierarchyLevel(*m_FrameData, m_ThreadIdx, allSamples, m_Root, m_ProfilerSortColumn, m_ProfilerViewType, m_AdditionalData);
+ ProcessProfilerSampleData(*m_FrameData, m_ThreadIdx, *m_Root, m_AdditionalData);
+
+ m_ActiveHierarchy = m_Root;
+}
+
+std::string ProfilerProperty::GetFrameTime() const
+{
+ if (!m_FrameData)
+ return "--";
+
+ return GetFormattedTime(m_FrameData->m_ThreadData[m_ThreadIdx].GetRoot()->timeUS*1000);
+}
+
+std::string ProfilerProperty::GetFrameGpuTime() const
+{
+ if (m_FrameData){
+ return GetFormattedTime(m_FrameData->m_TotalGPUTimeInMicroSec*1000);
+ }
+ return "--";
+}
+
+std::string ProfilerProperty::GetFrameFPS() const
+{
+ if (m_FrameData)
+ {
+ double frame = 1000000.0 / (double)m_FrameData->m_ThreadData[m_ThreadIdx].GetRoot()->timeUS;
+ return Format("%.1f", (float)frame);
+ }
+ else
+ return "--";
+}
+
+bool ProfilerProperty::HasChildren() const
+{
+ // Detail view does not haven any children.
+ if (m_ProfilerViewType == kViewDetailFlat && m_Root != m_ActiveHierarchy)
+ return false;
+
+ if (m_OnlyShowGPUSamples)
+ return m_ActiveHierarchy->data.childrenGPUSamplesCount != 0;
+
+ if (m_FrameData->m_ThreadData[m_ThreadIdx].GetSample(m_ActiveHierarchy->oneSample)->nbChildren != 0)
+ return true;
+
+ dynamic_array<UInt32>& samples = m_ActiveHierarchy->sampleIdx;
+ for (int i=0;i<samples.size();i++)
+ {
+ if (m_FrameData->m_ThreadData[m_ThreadIdx].GetSample(samples[i])->nbChildren != 0)
+ return true;
+ }
+ return false;
+}
+
+bool ProfilerProperty::GetNext(bool expanded)
+{
+ if (!GetNextInternal (expanded))
+ return false;
+
+ while (m_OnlyShowGPUSamples && m_ActiveHierarchy->data.gpuSamplesCount == 0)
+ {
+ if (!GetNextInternal (expanded))
+ return false;
+ }
+
+ return true;
+}
+
+bool ProfilerProperty::GetNextInternal(bool expanded)
+{
+ if (m_ActiveHierarchy == NULL)
+ return false;
+
+ if (HasChildren() && expanded)
+ {
+ BuildHierarchyLevel(*m_FrameData, m_ThreadIdx, m_ActiveHierarchy, m_ProfilerSortColumn, m_ProfilerViewType, m_AdditionalData);
+
+ // Step into children list
+ m_ActiveHierarchy = &m_ActiveHierarchy->children[0];
+
+ // Increase depth
+ ++m_Depth;
+ }
+ else
+ {
+ if (m_ActiveHierarchy->parent == 0)
+ return false;
+
+ int nextIndex = (m_ActiveHierarchy - &m_ActiveHierarchy->parent->children[0]) + 1;
+
+ // Locating next profile sample object until one is found or end of samples is reached
+ // step out of children list to its parent if end of list is reached
+ while(nextIndex == m_ActiveHierarchy->parent->children.size())
+ {
+ // Go to parent
+ m_ActiveHierarchy = m_ActiveHierarchy->parent;
+
+ if (!m_ActiveHierarchy->parent)
+ {
+ // End of samples
+ return false;
+ }
+ nextIndex = (m_ActiveHierarchy - &m_ActiveHierarchy->parent->children[0]) + 1;
+
+ // Decrease depth
+ --m_Depth;
+ }
+
+ m_ActiveHierarchy = &m_ActiveHierarchy->parent->children[nextIndex];
+ }
+
+ // Read profile sample name
+ m_FunctionPath = m_FunctionName = m_ActiveHierarchy->data.information->name;
+
+ // Assemble path name (used by profile property to handle fold in / fold out)
+ ProfilerHierarchy* sample = m_ActiveHierarchy;
+ sample = sample ? sample->parent : NULL;
+ int depth = m_Depth - 1;
+
+ // Construct path string of all parent names and name of this sample
+ while (sample != NULL && sample->data.information)
+ {
+ // Using Format("%s/%s") takes 80ms to display profiler timeone on Windows, Core i7 2600K.
+ // Replacing that with manual string operations gets time down to 20ms.
+
+ //m_FunctionPath = Format("%s/%s", sample->data.information->name, m_FunctionPath.c_str());
+ const size_t nameLen = strlen(sample->data.information->name);
+ m_FunctionPath.reserve (m_FunctionPath.size() + nameLen + 1); // name length plus '/' char
+ m_FunctionPath.insert (0, sample->data.information->name, nameLen+1); // insert whole name and trailing zero
+ m_FunctionPath[nameLen] = '/'; // replace just inserted trailing zero with '/'
+
+ sample = sample->parent;
+ depth--;
+ }
+
+ // Found profile sample
+ return true;
+}
+
+void ProfilerProperty::CleanupProperty()
+{
+ m_Depth = 0;
+ delete m_Root; m_Root = NULL;
+ m_FrameData = NULL;
+ m_ActiveHierarchy = NULL;
+ for(int i = 0; i < m_AdditionalData.size(); i++)
+ ProfilerProperty::s_AdditionalDataPool.Deallocate(m_AdditionalData[i]);
+ m_AdditionalData.clear();
+}
+
+
+#endif // #if ENABLE_PROFILER && UNITY_EDITOR