summaryrefslogtreecommitdiff
path: root/Runtime/Profiler/ProfilerImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/Profiler/ProfilerImpl.cpp')
-rw-r--r--Runtime/Profiler/ProfilerImpl.cpp1682
1 files changed, 1682 insertions, 0 deletions
diff --git a/Runtime/Profiler/ProfilerImpl.cpp b/Runtime/Profiler/ProfilerImpl.cpp
new file mode 100644
index 0000000..2abafa5
--- /dev/null
+++ b/Runtime/Profiler/ProfilerImpl.cpp
@@ -0,0 +1,1682 @@
+#include "UnityPrefix.h"
+#include "Profiler.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+
+#include <string.h>
+#if ENABLE_PROFILER
+
+#include "ProfilerImpl.h"
+#include "GPUProfiler.h"
+#include "ProfilerHistory.h"
+#include "ProfilerFrameData.h"
+#include "CollectProfilerStats.h"
+#include "ProfilerConnection.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/Serialize/SwapEndianBytes.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Threads/JobScheduler.h"
+#include "IntelGPAProfiler.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+
+#if UNITY_PS3
+#include "External/libsntuner.h"
+#define PS3_TUNER_SAMPLE_BEGIN(s) snPushMarker((s)->name)
+#define PS3_TUNER_SAMPLE_END() snPopMarker()
+#else
+#define PS3_TUNER_SAMPLE_BEGIN(s)
+#define PS3_TUNER_SAMPLE_END()
+#endif
+
+
+#define DEBUG_AUTO_PROFILER_LOG 0
+#if DEBUG_AUTO_PROFILER_LOG && !UNITY_BUILD_COPY_PROTECTED
+#error "Must disable DEBUG_AUTO_PROFILER_LOG in deployed build"
+#endif
+
+#if UNITY_EDITOR
+#include "Editor/Src/EditorHelper.h"
+#endif
+
+
+#if ENABLE_MONO
+#include "Runtime/Scripting/CommonScriptingClasses.h"
+const int kMonoProfilerDefaultFlags = MONO_PROFILE_GC | MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_EXCEPTIONS;
+#endif // #if ENABLE_MONO
+
+const int kProfilerDataStreamVersion = 0x20122123;
+
+void profiler_begin_frame()
+{
+ if (UnityProfiler::GetPtr())
+ UnityProfiler::Get().BeginFrame();
+}
+
+void profiler_end_frame()
+{
+ if (UnityProfiler::GetPtr())
+ UnityProfiler::Get().EndFrame();
+}
+
+void profiler_start_mode(ProfilerMode flags)
+{
+ if (UnityProfiler::GetPtr())
+ UnityProfiler::Get().StartProfilingMode(flags);
+}
+
+void profiler_end_mode(ProfilerMode flags)
+{
+ if (UnityProfiler::GetPtr())
+ UnityProfiler::Get().EndProfilingMode(flags);
+}
+
+void profiler_begin_thread_safe(ProfilerInformation* info, const Object* obj)
+{
+ PS3_TUNER_SAMPLE_BEGIN(info);
+
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof && prof->m_ProfilerAllowSampling)
+ {
+ SET_ALLOC_OWNER(NULL);
+ prof->BeginSample(info, obj);
+ }
+}
+
+void profiler_end_thread_safe()
+{
+ PS3_TUNER_SAMPLE_END();
+
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof && prof->m_ProfilerAllowSampling)
+ prof->EndSample(START_TIME);
+}
+
+
+void profiler_begin(ProfilerInformation* info, const Object* obj)
+{
+ INTEL_GPA_SAMPLE_BEGIN(info);
+ PS3_TUNER_SAMPLE_BEGIN(info);
+
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof && prof->m_ProfilerAllowSampling)
+ {
+ SET_ALLOC_OWNER(NULL);
+ prof->BeginSample(info, obj);
+ }
+}
+
+void profiler_end()
+{
+ INTEL_GPA_SAMPLE_END();
+ PS3_TUNER_SAMPLE_END();
+
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof && prof->m_ProfilerAllowSampling)
+ prof->EndSample(START_TIME);
+}
+
+GpuSection g_CurrentGPUSection = kGPUSectionOther;
+
+void gpu_time_sample()
+{
+ GPUProfiler::GPUTimeSample();
+}
+
+void profiler_initialize_thread (const char* name, bool separateBeginEnd)
+{
+#if UNITY_EDITOR
+ if(IsDeveloperBuild())
+ UnityProfilerPerThread::Initialize(name, true);
+#endif
+}
+
+void profiler_cleanup_thread ()
+{
+ UnityProfilerPerThread::Cleanup();
+}
+
+void profiler_set_active_seperate_thread (bool enabled)
+{
+ UnityProfiler* prof = UnityProfiler::GetPtr();
+ if (prof)
+ prof->SetActiveSeparateThread(enabled);
+}
+
+void profiler_begin_frame_seperate_thread (ProfilerMode mode)
+{
+ UnityProfiler* prof = UnityProfiler::GetPtr();
+ if (prof)
+ prof->BeginFrameSeparateThread(mode);
+
+}
+
+void profiler_disable_sampling_seperate_thread ()
+{
+ UnityProfiler* prof = UnityProfiler::GetPtr();
+ if (prof)
+ prof->DisableSamplingSeparateThread();
+
+}
+
+void profiler_end_frame_seperate_thread (int frameIDAndValid)
+{
+ UnityProfiler* prof = UnityProfiler::GetPtr();
+ if (prof)
+ prof->EndFrameSeparateThread(frameIDAndValid);
+}
+
+
+// -------------------------------------------------------------------------
+
+
+
+ProfilerInformation::ProfilerInformation (const char* const functionName, ProfilerGroup grp, bool warn)
+: name(functionName)
+, group(grp)
+, flags(kDefault)
+, isWarning(warn)
+{
+ INTEL_GPA_INFORMATION_INITIALIZE();
+}
+
+
+
+// -------------------------------------------------------------------------
+// Per-thread profiler class
+
+const int kDeepProfilingMaxSamples = 1024 * 1024 * 4; // 80 MB on 32bit
+const int kNormalProfilingMaxSamples = 1024 * 512; // 10 MB on 32bit
+
+
+UNITY_TLS_VALUE(UnityProfilerPerThread*) UnityProfilerPerThread::ms_InstanceTLS;
+
+void UnityProfilerPerThread::Initialize(const char* threadName, bool separateBeginEnd)
+{
+ SET_ALLOC_OWNER(UnityProfiler::GetPtr());
+ Assert(ms_InstanceTLS == NULL);
+ ms_InstanceTLS = UNITY_NEW(UnityProfilerPerThread, kMemProfiler)(threadName, separateBeginEnd);
+
+ UnityProfiler::ms_Instance->AddPerThreadProfiler(ms_InstanceTLS);
+}
+
+void UnityProfilerPerThread::Cleanup()
+{
+ if(ms_InstanceTLS == NULL)
+ return;
+
+ UnityProfiler::ms_Instance->RemovePerThreadProfiler(ms_InstanceTLS);
+
+ UnityProfilerPerThread* instance = ms_InstanceTLS;
+ UNITY_DELETE(instance, kMemProfiler);
+ ms_InstanceTLS = NULL;
+}
+
+UnityProfilerPerThread::UnityProfilerPerThread(const char* threadName, bool separateBeginEnd)
+: m_ProfilerAllowSampling(false)
+, m_ActiveGlobalAllocator(1024 * 128, kMemProfiler)
+, m_OutOfSampleMemory(false)
+, m_ErrorDurringFrame(false)
+, m_ActiveSamples(kMemProfiler)
+, m_SampleStack(kMemProfiler)
+, m_SampleTimeBeginStack(kMemProfiler)
+, m_GPUTimeSamples(kMemProfiler)
+, m_InstanceIDSamples(kMemProfiler)
+, m_AllocatedGCMemorySamples(kMemProfiler)
+, m_WarningSamples(kMemProfiler)
+, m_ProfilersListNode(this)
+, m_GCCollectTime(0)
+, m_ThreadName(threadName)
+, m_ThreadIndex(0)
+, m_SeparateBeginEnd(separateBeginEnd)
+{
+ #if ENABLE_THREAD_CHECK_IN_ALLOCS
+ Thread::ThreadID tid = Thread::GetCurrentThreadID();
+ m_ActiveGlobalAllocator.SetThreadIDs(tid, tid);
+ #endif
+}
+
+UnityProfilerPerThread::~UnityProfilerPerThread()
+{
+ m_ActiveGlobalAllocator.purge (true);
+ m_ActiveMethodCache.clear();
+ m_DynamicMethodCache.clear();
+}
+
+void UnityProfilerPerThread::BeginFrame(ProfilerMode mode)
+{
+ if (mode & kProfilerDeepScripts)
+ m_ActiveSamples.resize_uninitialized(kDeepProfilingMaxSamples);
+ else
+ m_ActiveSamples.resize_uninitialized(kNormalProfilingMaxSamples);
+
+ m_ActiveSamples[0] = ProfilerSample();
+ m_ActiveSamples[0].startTimeUS = GetProfileTime(START_TIME)/1000;
+ m_NextSampleIndex = 1;
+
+ m_AllocatedGCMemorySamples.resize_uninitialized(0);
+ m_GPUTimeSamples.resize_uninitialized(0);
+ m_InstanceIDSamples.resize_uninitialized(0);
+ m_WarningSamples.resize_uninitialized(0);
+
+ m_SampleStack.resize_uninitialized(0);
+ m_SampleStack.push_back(0);
+
+ m_SampleTimeBeginStack.push_back(START_TIME);
+
+ m_GCCollectTime = 0;
+}
+
+
+bool UnityProfilerPerThread::EndFrame()
+{
+ Assert (!GetIsActive());
+
+ ProfilerSample* rootSample = GetRoot();
+ if (m_SampleStack.size()>1)
+ {
+ if (!m_ErrorDurringFrame)
+ ErrorString("Too many Profiler.BeginSample (BeginSample and EndSample count must match)");
+ m_ErrorDurringFrame = true;
+ }
+
+ bool ok = (rootSample->nbChildren != 0 && !m_OutOfSampleMemory && !m_ErrorDurringFrame);
+ return ok;
+}
+
+
+void UnityProfilerPerThread::ClearFrame ()
+{
+ m_NextSampleIndex = 0;
+
+ m_SampleStack.resize_uninitialized(0);
+ m_SampleTimeBeginStack.resize_uninitialized(0);
+ m_GPUTimeSamples.resize_uninitialized(0);
+ m_InstanceIDSamples.resize_uninitialized(0);
+ m_AllocatedGCMemorySamples.resize_uninitialized(0);
+ m_WarningSamples.resize_uninitialized(0);
+
+ m_ProfilerAllowSampling = false;
+ m_OutOfSampleMemory = false;
+ m_ErrorDurringFrame = false;
+}
+
+
+void UnityProfilerPerThread::SetIsActive (bool enabled)
+{
+ if (!enabled && m_ProfilerAllowSampling)
+ {
+ if (m_GCCollectTime != 0)
+ InjectGCCollectSample();
+ }
+ m_ProfilerAllowSampling = enabled && !m_SampleStack.empty();
+}
+
+void UnityProfilerPerThread::AddMiscSamplesAfterFrame(ProfileTimeFormat frameDuration, bool addOverhead)
+{
+ // Insert GC.Collect sample if we had one
+ if (m_GCCollectTime != 0)
+ InjectGCCollectSample();
+
+ ProfilerSample* rootSample = GetRoot();
+ if (rootSample)
+ rootSample->timeUS = frameDuration / 1000;
+
+ if (addOverhead)
+ CreateOverheadSample();
+}
+
+
+ProfilerSample* UnityProfilerPerThread::BeginSample(ProfilerInformation* info, const Object* obj)
+{
+ DebugAssert(this);
+
+ DebugAssert(m_ProfilerAllowSampling);
+ DebugAssert(!m_SampleStack.empty() == m_ProfilerAllowSampling);
+
+ // Insert GC.Collect sample if we had one
+ if (m_GCCollectTime != 0)
+ InjectGCCollectSample();
+
+ // Add child to children
+ m_ActiveSamples[m_SampleStack.back()].nbChildren++;
+
+ // Add new sample and insert into stack
+ m_SampleStack.push_back(m_NextSampleIndex);
+ ProfilerSample* sample = &m_ActiveSamples[m_NextSampleIndex];
+ m_NextSampleIndex++;
+ if(m_NextSampleIndex > ((int)m_ActiveSamples.size()-1))
+ {
+ m_NextSampleIndex = m_ActiveSamples.size()-1;
+ if(!m_OutOfSampleMemory)
+ {
+ m_OutOfSampleMemory = true;
+ ErrorString("The profiler has run out of samples for this frame. This frame will be skipped.");
+ }
+ }
+
+ // Initialize sample object
+ sample->information = info;
+
+ if(info->isWarning)
+ m_WarningSamples.push_back(GetActiveSampleIndex ());
+
+ sample->nbChildren = 0;
+ sample->startTimeUS = 0;
+
+ if(obj != NULL)
+ {
+ ProfilerData::InstanceID instanceSample = {GetActiveSampleIndex(), obj->GetInstanceID()};
+ m_InstanceIDSamples.push_back(instanceSample);
+ }
+
+ // Get current processor time and store it in the sample
+ m_SampleTimeBeginStack.push_back(START_TIME);
+ return sample;
+}
+
+
+void UnityProfilerPerThread::EndSample(ABSOLUTE_TIME time)
+{
+ DebugAssert(this);
+
+ DebugAssert(m_ProfilerAllowSampling);
+ if(m_SampleStack.size() <= 1)
+ {
+ if(!m_ErrorDurringFrame)
+ ErrorString("Non matching Profiler.EndSample (BeginSample and EndSample count must match)");
+ m_ErrorDurringFrame = true;
+ return;
+ }
+ DebugAssert(m_SampleStack.empty() != m_ProfilerAllowSampling);
+
+ ProfilerSample* sample = &m_ActiveSamples[m_SampleStack.back()];
+ // Set duration and start time
+ sample->startTimeUS = GetProfileTime(m_SampleTimeBeginStack.back())/1000;
+ sample->timeUS = GetProfileTime(SUBTRACTED_TIME(time, m_SampleTimeBeginStack.back()))/1000;
+
+ // Insert GC.Collect sample if we had one
+ if (m_GCCollectTime != 0)
+ InjectGCCollectSample();
+
+ m_SampleStack.pop_back();
+ m_SampleTimeBeginStack.pop_back();
+}
+
+
+void UnityProfilerPerThread::BeginSampleDynamic(const std::string& name, const Object* obj)
+{
+ DebugAssert(this);
+ if (!m_ProfilerAllowSampling)
+ return;
+
+ DynamicMethodCache::iterator found = m_DynamicMethodCache.find(name);
+ if (found != m_DynamicMethodCache.end())
+ {
+ BeginSample(&found->second, obj);
+ }
+ else
+ {
+ found = m_DynamicMethodCache.insert(make_pair(name, ProfilerInformation(NULL, kProfilerScripts))).first;
+ found->second.name = found->first.c_str();
+ found->second.group = kProfilerScripts;
+ BeginSample(&found->second, obj);
+ }
+}
+
+
+void UnityProfilerPerThread::SaveToFrameData (ProfilerFrameData& dst) const
+{
+ ProfilerFrameData::ThreadData& tdata = dst.m_ThreadData[m_ThreadIndex];
+ tdata.m_ThreadName = m_ThreadName;
+ tdata.m_AllSamples.assign(m_ActiveSamples.begin(), &m_ActiveSamples[m_NextSampleIndex]);
+ tdata.m_GPUTimeSamples.assign(m_GPUTimeSamples.begin(), m_GPUTimeSamples.end());
+ tdata.m_InstanceIDSamples.assign(m_InstanceIDSamples.begin(), m_InstanceIDSamples.end());
+ tdata.m_AllocatedGCMemorySamples.assign(m_AllocatedGCMemorySamples.begin(), m_AllocatedGCMemorySamples.end());
+ tdata.m_WarningSamples.assign(m_WarningSamples.begin(), m_WarningSamples.end());
+}
+
+
+
+// -------------------------------------------------------------------------
+// Global Profiler class
+
+
+
+UnityProfiler* UnityProfiler::ms_Instance = NULL;
+
+PROFILER_INFORMATION(gGCCollect, "GC.Collect", kProfilerGC)
+PROFILER_INFORMATION(gOverheadProfile, "Overhead", kProfilerOverhead)
+
+
+#if SUPPORT_THREADS
+#define IS_MAIN_THREAD(p) Thread::EqualsCurrentThreadID(p->m_MainThreadID)
+#else
+#define IS_MAIN_THREAD(p) (true)
+#endif
+
+
+void UnityProfiler::Initialize ()
+{
+ Assert(ms_Instance == NULL);
+ ms_Instance = UNITY_NEW_AS_ROOT(UnityProfiler,kMemProfiler, "Profiler", "");
+ UnityProfilerPerThread::Initialize("Main Thread");
+}
+
+void UnityProfiler::CleanupGfx ()
+{
+ Assert(ms_Instance != NULL);
+ for (int i = 0; i < kFrameCount; i++)
+ {
+ if (ms_Instance->m_PreviousFrames[i] != NULL)
+ GPUProfiler::ClearTimerQueries(ms_Instance->m_PreviousFrames[i]->m_ThreadData[0].m_GPUTimeSamples);
+ }
+ ProfilerFrameData::FreeAllTimerQueries();
+}
+
+void UnityProfiler::Cleanup ()
+{
+ UnityProfilerPerThread::Cleanup();
+
+ Assert(ms_Instance != NULL);
+ UNITY_DELETE(ms_Instance, kMemProfiler);
+ ms_Instance = NULL;
+}
+
+
+
+UnityProfiler::UnityProfiler()
+: m_ProfilerEnabledThisFrame(false)
+, m_ProfilerEnabledLastFrame(false)
+, m_ProfilerAllowSamplingGlobal(false)
+, m_EnabledCount(0)
+, m_FramesLogged(0)
+, m_TextFile(NULL)
+, m_DataFile(NULL)
+, m_BinaryLogEnabled(false)
+, m_ProfilerCount(0)
+, m_FrameIDCounter(1)
+{
+#if SUPPORT_THREADS
+ m_MainThreadID = Thread::GetCurrentThreadID();
+#endif
+
+ m_PendingProfilerMode = kProfilerGame;
+ m_ProfilerMode = m_PendingProfilerMode;
+ ABSOLUTE_TIME_INIT(m_LastEnabledTime);
+
+ memset(m_PreviousFrames, 0, sizeof(m_PreviousFrames));
+
+ #if DEBUG_AUTO_PROFILER_LOG
+#if UNITY_OSX
+ SetEnabled(true);
+ string result = getenv ("HOME");
+ SetLogPath(AppendPathName( result, "Library/Logs/Unity/Profiler.log"));
+#elif UNITY_WIN
+ SetEnabled(true);
+ const char* tempPath = ::getenv("TEMP");
+ if (!tempPath)
+ tempPath = "C:";
+ SetLogPath(AppendPathName (tempPath, "UnityProfiler.log"));
+#endif
+ #endif // #if DEBUG_AUTO_PROFILER_LOG
+}
+
+
+UnityProfiler::~UnityProfiler()
+{
+ SetLogPath("");
+ UNITY_DELETE(m_TextFile, kMemProfiler);
+ UNITY_DELETE(m_DataFile, kMemProfiler);
+}
+
+void UnityProfiler::AddPerThreadProfiler (UnityProfilerPerThread* prof)
+{
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ m_Profilers.push_back(prof->GetProfilersListNode());
+ ++m_ProfilerCount;
+}
+
+void UnityProfiler::RemovePerThreadProfiler (UnityProfilerPerThread* prof)
+{
+ if (!this)
+ return;
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ prof->GetProfilersListNode().RemoveFromList();
+ --m_ProfilerCount;
+}
+
+
+void UnityProfiler::CheckPro()
+{
+ if (GetEnabled())
+ {
+ BuildSettings* buildSettings = GetBuildSettingsPtr();
+#if UNITY_EDITOR
+ if (buildSettings && !buildSettings->hasPROVersion)
+#else
+ if (buildSettings && !buildSettings->hasAdvancedVersion)
+#endif
+ {
+ ErrorString("Profiler is only supported in Unity Pro.");
+ SetEnabled(false);
+ }
+ }
+}
+
+void UnityProfiler::SetEnabled (bool val)
+{
+ BuildSettings* buildSettings = GetBuildSettingsPtr();
+#if UNITY_EDITOR
+ if (buildSettings && !buildSettings->hasPROVersion)
+ return;
+#else
+ if (buildSettings && !buildSettings->hasAdvancedVersion)
+ return;
+#endif
+
+ if(val)
+ m_PendingProfilerMode |= kProfilerEnabled;
+ else
+ m_PendingProfilerMode &= ~kProfilerEnabled;
+}
+
+void UnityProfiler::RecordPreviousFrame(ProfilerMode mode)
+{
+ UnityProfiler* profiler = UnityProfiler::GetPtr();
+ if(!profiler)
+ return;
+
+ if(profiler->m_ProfilerEnabledLastFrame)
+ {
+ GPUProfiler::EndFrame();
+ profiler->EndProfilingMode(mode);
+ profiler->EndFrame();
+ profiler->m_ProfilerEnabledLastFrame = false;
+ }
+}
+
+bool UnityProfiler::StartNewFrame(ProfilerMode mode)
+{
+ UnityProfiler* profiler = UnityProfiler::GetPtr();
+ if(!profiler)
+ return false;
+
+ UnityProfiler::Get().UpdateEnabled();
+
+ if (UnityProfiler::Get().GetEnabled())
+ {
+ profiler->BeginFrame();
+ profiler->StartProfilingMode(mode);
+ GPUProfiler::BeginFrame();
+ profiler->m_ProfilerEnabledLastFrame = true;
+ }
+
+ return profiler->m_ProfilerEnabledLastFrame;
+}
+
+void UnityProfiler::BeginFrame ()
+{
+ DebugAssert(IS_MAIN_THREAD(this));
+
+ CheckPro();
+
+ GfxDevice& device = GetGfxDevice();
+ device.ProfileControl(GfxDevice::kGfxProfDisableSampling, 0);
+
+ m_ProfilerMode = m_PendingProfilerMode;
+ m_ProfilerEnabledThisFrame = m_ProfilerMode & kProfilerEnabled;
+
+ {
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ for(ProfilersList::iterator it = m_Profilers.begin(); it != m_Profilers.end(); ++it)
+ {
+ UnityProfilerPerThread& prof = **it;
+ if (!prof.IsSeparateBeginEnd())
+ prof.m_ProfilerAllowSampling = false;
+ }
+ }
+ m_ProfilerAllowSamplingGlobal = false;
+
+ if(!m_ProfilerEnabledThisFrame)
+ return;
+
+ device.ProfileControl(GfxDevice::kGfxProfBeginFrame, m_ProfilerMode);
+ {
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ for(ProfilersList::iterator it = m_Profilers.begin(); it != m_Profilers.end(); ++it)
+ {
+ UnityProfilerPerThread& prof = **it;
+ if (!prof.IsSeparateBeginEnd())
+ prof.BeginFrame(m_ProfilerMode);
+ }
+ }
+
+
+ m_TotalProfilerFrameDuration = START_TIME;
+ // accumulates all time from startFrame(N) to startFrame(N+1)
+ m_ProfilerEnabledDuration = 0;
+}
+
+
+void UnityProfiler::BeginFrameSeparateThread(ProfilerMode mode)
+{
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ UnityProfilerPerThread* profTLS = UnityProfilerPerThread::ms_InstanceTLS;
+ if (!profTLS)
+ return;
+ Assert(profTLS->IsSeparateBeginEnd());
+
+ profTLS->BeginFrame(mode);
+}
+
+void UnityProfiler::DisableSamplingSeparateThread()
+{
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ UnityProfilerPerThread* profTLS = UnityProfilerPerThread::ms_InstanceTLS;
+ if (!profTLS)
+ return;
+ Assert(profTLS->IsSeparateBeginEnd());
+
+ profTLS->m_ProfilerAllowSampling = false;
+}
+
+void UnityProfiler::SetActiveSeparateThread(bool enabled)
+{
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ UnityProfilerPerThread* profTLS = UnityProfilerPerThread::ms_InstanceTLS;
+ if (!profTLS)
+ return;
+ Assert(profTLS->IsSeparateBeginEnd());
+
+ profTLS->SetIsActive(enabled);
+}
+
+
+void UnityProfiler::EndFrame ()
+{
+ Assert (m_EnabledCount == 0);
+
+ if (!m_ProfilerEnabledThisFrame)
+ return;
+
+ bool profileFrameValid = true;
+ int threadIdx = 0;
+ {
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ for(ProfilersList::iterator it = m_Profilers.begin(); it != m_Profilers.end(); ++it)
+ {
+ UnityProfilerPerThread& prof = **it;
+ prof.SetThreadIndex(threadIdx);
+ if (!prof.IsSeparateBeginEnd())
+ {
+ bool threadValid = prof.EndFrame();
+ if (threadIdx == 0 && !threadValid)
+ profileFrameValid = false;
+ }
+ ++threadIdx;
+ }
+ }
+
+ int curFrameID = 0;
+ if (profileFrameValid)
+ {
+ StartProfilingMode(kProfilerGame);
+
+ threadIdx = 0;
+ {
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ for(ProfilersList::iterator it = m_Profilers.begin(); it != m_Profilers.end(); ++it)
+ {
+ UnityProfilerPerThread& prof = **it;
+ if (!prof.IsSeparateBeginEnd())
+ prof.AddMiscSamplesAfterFrame(m_ProfilerEnabledDuration, threadIdx==0);
+ ++threadIdx;
+ }
+ }
+
+ EndProfilingMode(kProfilerGame);
+
+
+ // Collect GPU timing for recent frames (non blocking)
+ for (int i = kFrameCount - 2; i >= 0; i--)
+ {
+ if (m_PreviousFrames[i] != NULL)
+ GPUProfiler::CollectGPUTime(m_PreviousFrames[i]->m_ThreadData[0].m_GPUTimeSamples, false);
+ }
+
+ // Compute GPU timing for oldest frames (blocking)
+ ProfilerFrameData* oldestFrame = m_PreviousFrames[kFrameCount-1];
+ if(oldestFrame)
+ {
+ oldestFrame->m_TotalGPUTimeInMicroSec = GPUProfiler::ComputeGPUTime(oldestFrame->m_ThreadData[0].m_GPUTimeSamples);
+
+ Mutex::AutoLock prevFramesLock(m_PrevFramesMutex);
+
+ LogFrame(oldestFrame);
+#if UNITY_EDITOR
+ // profilerhistory takes ownership and pushes pointer on a vector;
+ ProfilerHistory::Get().AddFrameDataAndTransferOwnership (oldestFrame, ProfilerConnection::GetEditorGuid());
+ // allocate new frame
+ oldestFrame = UNITY_NEW(ProfilerFrameData, kMemProfiler) (m_ProfilerCount, ++m_FrameIDCounter);
+#elif ENABLE_PLAYERCONNECTION
+ ProfilerConnection::Get().SendFrameDataToEditor (*oldestFrame); // reuse allocated memory
+ // GPUProfiler::ExtractGPUTime(m_PreviousGPUTimeSamples); // TODO use this sceme instead to reduce memory
+ // ProfilerConnection::Get().TransferPartialData(...)
+ // m_PreviousGPUTimeSamples.swap(m_GPUTimeSamples);
+#endif
+ }
+
+ ProfilerFrameData* curFrame = NULL;
+ {
+ Mutex::AutoLock prevFramesLock(m_PrevFramesMutex);
+ for (int i = 1; i < kFrameCount; i++)
+ m_PreviousFrames[i] = m_PreviousFrames[i - 1];
+
+ m_PreviousFrames[0] = oldestFrame;
+
+ if (m_PreviousFrames[0] == NULL)
+ {
+ m_PreviousFrames[0] = UNITY_NEW(ProfilerFrameData,kMemProfiler) (m_ProfilerCount, ++m_FrameIDCounter);
+ }
+
+ curFrame = m_PreviousFrames[0];
+ }
+
+ curFrameID = curFrame->m_FrameID;
+ {
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ for(ProfilersList::iterator it = m_Profilers.begin(); it != m_Profilers.end(); ++it)
+ {
+ UnityProfilerPerThread& prof = **it;
+ if (!prof.IsSeparateBeginEnd())
+ prof.SaveToFrameData(*curFrame);
+ }
+ }
+
+ CollectProfilerStats(curFrame->allStats);
+ }
+
+ const unsigned frameIDAndValidFlag = curFrameID | (profileFrameValid ? 0x80000000 : 0);
+ GetGfxDevice().ProfileControl(GfxDevice::kGfxProfEndFrame, frameIDAndValidFlag);
+ #if ENABLE_JOB_SCHEDULER
+ GetJobScheduler().EndProfilerFrame (frameIDAndValidFlag);
+ #endif
+
+ ABSOLUTE_TIME frameStartTime = m_TotalProfilerFrameDuration;
+ m_TotalProfilerFrameDuration = SUBTRACTED_TIME(START_TIME, frameStartTime);
+ if (profileFrameValid)
+ {
+ m_PreviousFrames[0]->m_StartTimeUS = GetProfileTime(frameStartTime) / 1000;
+ m_PreviousFrames[0]->m_TotalCPUTimeInMicroSec = GetProfileTime(m_TotalProfilerFrameDuration) / 1000;
+ }
+
+ {
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ for(ProfilersList::iterator it = m_Profilers.begin(); it != m_Profilers.end(); ++it)
+ {
+ UnityProfilerPerThread& prof = **it;
+ if (!prof.IsSeparateBeginEnd())
+ prof.ClearFrame();
+ }
+ }
+ m_ProfilerEnabledThisFrame = false;
+ m_ProfilerAllowSamplingGlobal = false;
+}
+
+
+
+void UnityProfiler::EndFrameSeparateThread(unsigned frameIDAndValid)
+{
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ UnityProfilerPerThread* profTLS = UnityProfilerPerThread::ms_InstanceTLS;
+ if (!profTLS)
+ return;
+ Assert(profTLS->IsSeparateBeginEnd());
+
+ profTLS->EndFrame();
+
+ const bool frameValid = (frameIDAndValid & 0x80000000);
+ if (frameValid)
+ {
+ Mutex::AutoLock prevFramesLock(m_PrevFramesMutex);
+ int frameID = frameIDAndValid & 0x7fffffff;
+ for (int i = 0; i < kFrameCount; ++i)
+ {
+ ProfilerFrameData* frame = m_PreviousFrames[i];
+ if (!frame)
+ continue;
+ if (frame->m_FrameID != frameID)
+ continue;
+ profTLS->SaveToFrameData(*frame);
+ }
+ }
+
+ profTLS->ClearFrame();
+}
+
+
+void UnityProfiler::ClearPendingFrames()
+{
+ Mutex::AutoLock prevFramesLock(m_PrevFramesMutex);
+ for (int i = 0; i < kFrameCount; i++)
+ {
+ UNITY_DELETE(m_PreviousFrames[i],kMemProfiler);
+ m_PreviousFrames[i] = NULL;
+ }
+}
+
+
+const ProfilerSample* UnityProfilerPerThread::GetActiveSample(int parentLevel) const
+{
+ const int indexInStack = m_SampleStack.size()-1 - parentLevel;
+ if (indexInStack < 0 || indexInStack >= m_SampleStack.size())
+ return NULL;
+ const int sampleIndex = m_SampleStack[indexInStack];
+ const ProfilerSample* sample = &m_ActiveSamples[sampleIndex];
+ return sample;
+}
+
+
+void UnityProfilerPerThread::InjectGCCollectSample()
+{
+ ProfileTimeFormat collectTime = m_GCCollectTime;
+ m_GCCollectTime = 0;
+
+ BeginSample(&gGCCollect, NULL);
+ ProfilerSample* gcSample = GetActiveSample();
+ EndSample(START_TIME);
+ gcSample->timeUS = collectTime/1000;
+}
+
+
+void UnityProfilerPerThread::CreateOverheadSample()
+ {
+ BeginSample(&gOverheadProfile, NULL);
+ ProfilerSample* overheadSample = GetActiveSample();
+ EndSample(START_TIME);
+
+ // Calculate overhead time be taking the root time and subtracting all children.
+ ProfilerSample* root = GetRoot();
+ ProfileTimeFormat overheadTime = root->timeUS * 1000;
+ const ProfilerSample* sample = root + 1;
+ for (int i=0;i<root->nbChildren;i++)
+ {
+ overheadTime -= sample->timeUS*1000;
+ sample = SkipSampleRecurse(sample);
+ }
+ overheadSample->timeUS += overheadTime/1000;
+}
+
+
+const ProfilerSample* SkipSampleRecurse (const ProfilerSample* sample)
+{
+ const ProfilerSample* child = sample + 1;
+ for (int i=0;i<sample->nbChildren;i++)
+ child = SkipSampleRecurse(child);
+
+ return child;
+}
+
+
+static void UpdateWithSmallestTime (ABSOLUTE_TIME& val, ABSOLUTE_TIME newval, int iteration)
+{
+ if (iteration == 0 || IsSmallerAbsoluteTime (newval, val))
+ val = newval;
+}
+
+
+void UnityProfiler::StartProfilingMode (ProfilerMode mode)
+{
+ if(m_ProfilerMode & mode)
+ {
+ SetIsActive(true);
+ }
+}
+
+void UnityProfiler::EndProfilingMode (ProfilerMode mode)
+{
+ if(m_ProfilerMode & mode)
+ SetIsActive(false);
+}
+
+
+void UnityProfiler::SetIsActive (bool enabled)
+{
+ if (enabled)
+ {
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ for(ProfilersList::iterator it = m_Profilers.begin(); it != m_Profilers.end(); ++it)
+ {
+ UnityProfilerPerThread& prof = **it;
+ if (!prof.IsSeparateBeginEnd())
+ prof.ClearGCCollectTime();
+ }
+ }
+
+ if(!m_ProfilerEnabledThisFrame)
+ return;
+ // m_EnabledCount can go below 0 (double disable), but will not start before enableCount is 1
+ m_EnabledCount += (enabled?1:-1);
+ if ( enabled && (m_EnabledCount != 1))
+ return;
+
+ if (!enabled && (m_EnabledCount != 0))
+ return;
+
+
+ if (!enabled && m_ProfilerAllowSamplingGlobal)
+ {
+ m_ProfilerEnabledDuration += GetProfileTime(SUBTRACTED_TIME(START_TIME, m_LastEnabledTime));
+ ABSOLUTE_TIME_INIT(m_LastEnabledTime);
+ }
+
+ m_ProfilerAllowSamplingGlobal = enabled;
+ {
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ for(ProfilersList::iterator it = m_Profilers.begin(); it != m_Profilers.end(); ++it)
+ {
+ UnityProfilerPerThread& prof = **it;
+ if (!prof.IsSeparateBeginEnd())
+ prof.SetIsActive (enabled);
+ }
+ }
+ GetGfxDevice().ProfileControl(GfxDevice::kGfxProfSetActive, enabled ? 1 : 0);
+
+ if (enabled && m_ProfilerAllowSamplingGlobal)
+ {
+ Assert(GetProfileTime (m_LastEnabledTime) == 0);
+ m_LastEnabledTime = START_TIME;
+ }
+}
+
+
+void UnityProfiler::GetDebugStats (DebugStats& debugStats)
+{
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ debugStats.m_ProfilerMemoryUsage = 0;
+ debugStats.m_AllocatedProfileSamples = 0;
+
+ debugStats.m_ProfilerMemoryUsageOthers = 0;
+ for(ProfilersList::iterator it = m_Profilers.begin(); it != m_Profilers.end(); ++it)
+ {
+ debugStats.m_ProfilerMemoryUsageOthers += (*it)->GetAllocatedBytes();
+ }
+}
+
+void UnityProfiler::CleanupMonoMethodCaches()
+{
+ Mutex::AutoLock lock(m_ProfilersMutex);
+ for(ProfilersList::iterator it = m_Profilers.begin(); it != m_Profilers.end(); ++it)
+ {
+ (*it)->CleanupMonoMethodCache();
+ }
+}
+
+
+#if ENABLE_MONO || UNITY_WINRT
+
+ProfilerSample* mono_profiler_begin(ScriptingMethodPtr method, ScriptingClassPtr profileKlass, ScriptingObjectPtr instance)
+{
+ UnityProfilerPerThread* profTLS = UnityProfilerPerThread::ms_InstanceTLS;
+ if (!profTLS || !profTLS->m_ProfilerAllowSampling)
+ return NULL;
+ UnityProfiler* profiler = UnityProfiler::ms_Instance;
+
+ if (!IS_MAIN_THREAD(profiler))
+ return NULL;
+
+ // If deep profiling, we return the current sample, which is the one we have to get back to when this call exits
+ if ((profiler->m_ProfilerMode & kProfilerDeepScripts) != 0)
+ return profTLS->GetActiveSample();
+
+ DebugAssert(profTLS->m_SampleStack.empty() != profTLS->m_ProfilerAllowSampling);
+
+ // Extract Object ptr for profiler
+ Object* objPtr = NULL;
+
+#if ENABLE_MONO
+ if (instance)
+ {
+ MonoClass* instanceClass = mono_object_get_class(instance);
+ if (mono_class_is_subclass_of(instanceClass, MONO_COMMON.unityEngineObject, false))
+ objPtr = ScriptingObjectOfType<Object>(instance).GetPtr();
+ }
+#endif
+
+ // Do we have this method's info in the cache?
+ ProfilerInformation* information;
+ UnityProfilerPerThread::MethodInfoCache::iterator it = profTLS->m_ActiveMethodCache.find(method);
+ if (it != profTLS->m_ActiveMethodCache.end())
+ information = it->second;
+ else
+ information = profTLS->CreateProfilerInformationForMethod(instance, method, scripting_method_get_name(method), profileKlass, ProfilerInformation::kScriptMonoRuntimeInvoke);
+
+ // Begin Sample
+ return profTLS->BeginSample(information, objPtr);
+}
+
+void mono_profiler_end(ProfilerSample* beginsample)
+{
+ UnityProfilerPerThread* profTLS = UnityProfilerPerThread::ms_InstanceTLS;
+ if (!profTLS || !profTLS->m_ProfilerAllowSampling)
+ return;
+ UnityProfiler* profiler = UnityProfiler::ms_Instance;
+
+ if (!IS_MAIN_THREAD(profiler))
+ return;
+
+ // roll back the stack if there has been an exception, and some end samples have been skipped
+ while(profTLS->GetActiveSample() != beginsample)
+ profTLS->EndSample(START_TIME);
+
+ // if not deep profiling, end the current sample
+ if ((profiler->m_ProfilerMode & kProfilerDeepScripts) == 0)
+ profTLS->EndSample(START_TIME);
+}
+
+#endif // #if ENABLE_MONO || UNITY_WINRT
+
+
+ProfilerInformation* UnityProfilerPerThread::CreateProfilerInformationForMethod(ScriptingObjectPtr object, ScriptingMethodPtr method, const char* methodName, ScriptingTypePtr profileKlass, int flags)
+{
+#if !ENABLE_MONO && !UNITY_WINRT
+ return 0;
+#else // ENABLE_MONO || UNITY_WINRT
+
+ ProfilerInformation* information = static_cast<ProfilerInformation*> (m_ActiveGlobalAllocator.allocate(sizeof(ProfilerInformation)));
+ information->group = kProfilerScripts;
+ information->flags = flags;
+ information->isWarning = false;
+
+#if ENABLE_MONO
+ const char* klassName = mono_class_get_name(mono_method_get_class(method->monoMethod));
+#else // UNITY_WINRT
+ const char* klassName = "";
+ if (object != SCRIPTING_NULL)
+ {
+ ScriptingTypePtr klass = scripting_object_get_class(object, GetScriptingTypeRegistry());
+ if (klass != NULL)
+ klassName = scripting_class_get_name(klass);
+ }
+#endif
+
+ if (profileKlass == NULL)
+ {
+ // Optimized snprintf(buffer, kNameBufferSize, "%s.%s()", klassName, methodName); to minimize profiler overhead
+ int size = 4;
+ for(int i=0;i<klassName[i] != 0;i++) { size++; }
+ for(int i=0;i<methodName[i] != 0;i++) { size++; }
+
+ char* allocatedBuffer = static_cast<char*> (m_ActiveGlobalAllocator.allocate(size));
+ information->name = allocatedBuffer;
+ char* c = allocatedBuffer;
+ for(int i=0;i<klassName[i] != 0;i++)
+ {
+ *c = klassName[i]; c++;
+ }
+
+ *c = '.'; c++;
+ for(int i=0;i<methodName[i] != 0;i++)
+ {
+ *c = methodName[i]; c++;
+ }
+
+ c[0] = '(';
+ c[1] = ')';
+ c[2] = 0;
+
+ // Put into method cache
+ m_ActiveMethodCache.insert (std::make_pair(method, information));
+
+ return information;
+ }
+ else
+ {
+ enum { kNameBufferSize = 256 };
+ char buffer[kNameBufferSize];
+ char coroutineMethodName[kNameBufferSize] = { 0 };
+
+ // The generated class for a coroutine is called "<Start>Iterator_1"
+ // We want to extract "Start" and use that as the method name.
+ const char* coroutineMethodNameEnd = NULL;
+ if (klassName[0] == '<')
+ coroutineMethodNameEnd = strchr(klassName, '>');
+
+ if (coroutineMethodNameEnd != NULL)
+ strncpy(coroutineMethodName, klassName + 1, std::min((int)(coroutineMethodNameEnd - (klassName + 1)), (int)kNameBufferSize));
+ else
+ strncpy(coroutineMethodName, klassName, kNameBufferSize);
+
+ snprintf(buffer, kNameBufferSize, "%s.%s() [Coroutine: %s]", scripting_class_get_name(profileKlass), coroutineMethodName, methodName);
+
+ int size = strlen(buffer)+1;
+ char* copyBuffer = static_cast<char*> (m_ActiveGlobalAllocator.allocate(size));
+ memcpy(copyBuffer, buffer, size);
+ information->name = copyBuffer;
+
+ // Put into method cache
+ m_ActiveMethodCache.insert (std::make_pair(method, information));
+
+ return information;
+ }
+#endif // ENABLE_MONO || UNITY_WINRT
+}
+
+
+
+ProfilerInformation* UnityProfilerPerThread::GetProfilerInformation (const std::string& name, UInt16 group, UInt16 flags, bool isWarning)
+{
+ DynamicMethodCache::iterator found = m_DynamicMethodCache.find(name);
+ if (found != m_DynamicMethodCache.end())
+ return &found->second;
+ else
+ {
+ // Put into method cache
+ DynamicMethodCache::iterator found = m_DynamicMethodCache.insert (std::make_pair(name, ProfilerInformation(NULL, (ProfilerGroup) group))).first;
+ found->second.name = found->first.c_str();
+ found->second.flags = flags;
+ found->second.isWarning = isWarning;
+ return &found->second;
+ }
+}
+
+
+void UnityProfilerPerThread::EnterMonoMethod(MonoMethod *method)
+{
+#if ENABLE_MONO
+ if (!m_ProfilerAllowSampling)
+ return;
+ const char* methodName = mono_method_get_name(method);
+ if (strncmp (methodName, "runtime_invoke", 14) == 0)
+ return;
+
+ // Do we have this method's info in the cache?
+ ScriptingMethodPtr scriptingMethod = GetScriptingMethodRegistry().GetMethod(method);
+ MethodInfoCache::iterator it = m_ActiveMethodCache.find(scriptingMethod);
+ if (it != m_ActiveMethodCache.end())
+ {
+ BeginSample (it->second, NULL);
+ return;
+ }
+
+ // Method info not in the cache; create and cache it
+ ProfilerInformation* information = CreateProfilerInformationForMethod(NULL, scriptingMethod, methodName, NULL, ProfilerInformation::kScriptEnterLeave);
+
+ // Begin Sample
+ BeginSample(information, NULL);
+ #endif // #if ENABLE_MONO
+}
+
+
+void UnityProfilerPerThread::LeaveMonoMethod(MonoMethod *method)
+{
+#if ENABLE_MONO
+ if (!m_ProfilerAllowSampling)
+ return;
+ const char* methodName = mono_method_get_name(method);
+ if (strncmp (methodName, "runtime_invoke", 14) == 0)
+ return;
+
+ ABSOLUTE_TIME time = START_TIME;
+ EndSample(time);
+ #endif // #if ENABLE_MONO
+}
+
+
+void UnityProfilerPerThread::SampleGCAllocation (MonoObject *obj, MonoClass *klass)
+{
+#if ENABLE_MONO
+ if (!m_ProfilerAllowSampling)
+ return;
+
+#if 0
+ // We can extract the name of the class being allocated here.
+ string info = Format("\n\t\t%s size: %d", mono_class_get_name(klass), size);
+#endif
+
+ int size = mono_object_get_size(obj);
+ ProfilerData::AllocatedGCMemory allocSample = {GetActiveSampleIndex(), size};
+ m_AllocatedGCMemorySamples.push_back(allocSample);
+ #endif // #if ENABLE_MONO
+}
+
+
+static void enter_mono_sample(void* pr, MonoMethod* method)
+{
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof)
+ prof->EnterMonoMethod(method);
+}
+
+static void leave_mono_sample(void* pr, MonoMethod* method)
+{
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof)
+ prof->LeaveMonoMethod(method);
+}
+
+
+#if ENABLE_MONO_MEMORY_PROFILER
+
+void UnityProfiler::SetupProfilerEvents ()
+{
+ int flags = kMonoProfilerDefaultFlags;
+ if (m_PendingProfilerMode & kProfilerDeepScripts)
+ flags |= MONO_PROFILE_ENTER_LEAVE;
+
+ mono_profiler_set_events (flags);
+}
+
+static void sample_mono_shutdown (void *prof)
+{
+}
+
+
+void UnityProfilerPerThread::SampleGCMonoCallback (void* pr, int event, int generation)
+{
+ if (event == 1)
+ {
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof)
+ {
+ prof->m_GCStartTime = START_TIME;
+ }
+ }
+
+ if (event == 4)
+ {
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof)
+ {
+ prof->m_GCCollectTime += GetProfileTime(ELAPSED_TIME(prof->m_GCStartTime));
+ }
+ }
+}
+
+static void sample_gc_resize (void *pr, SInt64 new_size)
+{
+ // printf_console("--- GC resize %d\n", (int)new_size);
+}
+
+static void sample_allocation (void* pr, MonoObject *obj, MonoClass *klass)
+{
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ if (prof)
+ prof->SampleGCAllocation(obj, klass);
+}
+
+
+void mono_profiler_startup ()
+{
+ mono_profiler_install (NULL, sample_mono_shutdown);
+
+ mono_profiler_install_gc(UnityProfilerPerThread::SampleGCMonoCallback, sample_gc_resize);
+ mono_profiler_install_allocation(sample_allocation);
+#if UNITY_EDITOR
+ // Deep profiling is only be available in editor
+ mono_profiler_install_enter_leave (enter_mono_sample, leave_mono_sample);
+#endif
+ int flags = kMonoProfilerDefaultFlags;
+ mono_profiler_set_events (flags);
+}
+
+#endif // ENABLE_MONO_MEMORY_PROFILER
+
+
+
+void UnityProfiler::LogFrame(ProfilerFrameData* data)
+{
+#if !UNITY_PEPPER
+ if (m_LogFile.empty())
+ return;
+
+ float fps = 1000000.0 / data->m_ThreadData[0].GetRoot()->timeUS;
+
+#if DEBUG_AUTO_PROFILER_LOG
+ const int kMinimumFramerate = 20;
+ if (fps > kMinimumFramerate)
+ return;
+#endif
+
+ {
+ string fpsCategory;
+ if (fps < 10)
+ fpsCategory = "Very Low";
+ else if (fps < 20)
+ fpsCategory = "Low";
+ else if (fps < 25)
+ fpsCategory = "Okay";
+ else if (fps < 25)
+ fpsCategory = "Average";
+ else if (fps < 40)
+ fpsCategory = "Good";
+ else
+ fpsCategory = "Very Good";
+ std::string output = Format(" -- Frame %d Framerate: %.1f [%s Framerate]\n", ++m_FramesLogged, fps, fpsCategory.c_str());
+
+ m_TextFile->Write(output.c_str(),output.length());
+ }
+
+ if(m_BinaryLogEnabled)
+ {
+#if ENABLE_PLAYERCONNECTION
+ /// TODO: make async write. Causes stalls
+ dynamic_array<int> buffer;
+
+ SerializeFrameData(*data, buffer);
+ int size = buffer.size()*sizeof(int);
+ m_DataFile->Write(&size,sizeof(size));
+ int threadCount = data->m_ThreadCount;
+ m_DataFile->Write(&threadCount, sizeof(threadCount));
+ m_DataFile->Write(buffer.begin(), size);
+#endif
+ }
+#endif
+}
+
+void UnityProfiler::SetLogPath (std::string logPath)
+{
+#if WEBPLUG
+ if(!logPath.empty())
+ {
+ std::string name(GetConsoleLogPath());
+ ConvertSeparatorsToUnity(name);
+ logPath = DeleteLastPathNameComponent(name)+"/profile.log";
+ }
+#endif
+ if (m_LogFile != logPath)
+ {
+ m_LogFile = logPath;
+#if !UNITY_PEPPER
+ if (!logPath.empty())
+ {
+ m_FramesLogged = 0;
+ if(!m_TextFile)
+ m_TextFile = UNITY_NEW(File, kMemProfiler);
+ if(!m_DataFile)
+ m_DataFile = UNITY_NEW(File, kMemProfiler);
+ m_TextFile->Open(m_LogFile, File::kWritePermission);
+ m_DataFile->Open(m_LogFile+".data", File::kWritePermission);
+ }
+ else
+ {
+ if(m_TextFile)
+ m_TextFile->Close();
+ if(m_DataFile)
+ m_DataFile->Close();
+ }
+#endif
+ }
+}
+void UnityProfiler::AddFramesFromFile(string path)
+{
+#if UNITY_EDITOR
+ File dataFile;
+ dataFile.Open(path+".data", File::kReadPermission);
+ int size;
+ dynamic_array<int> buffer;
+ while(dataFile.Read(&size,sizeof(size)))
+ {
+ int threadCount;
+ dataFile.Read(&threadCount,sizeof(threadCount));
+ buffer.resize_uninitialized(size/sizeof(int));
+ dataFile.Read(buffer.begin(),size);
+ ProfilerFrameData* frame = UNITY_NEW(ProfilerFrameData, kMemProfiler) (threadCount, 0);
+
+ int fileguid = ProfilerConnection::Get().GetConnectedProfiler();
+ if( UnityProfiler::DeserializeFrameData(frame,buffer.begin(),size) )
+ ProfilerHistory::Get().AddFrameDataAndTransferOwnership(frame, fileguid);
+ else
+ UNITY_DELETE(frame, kMemProfiler);
+ }
+ dataFile.Close();
+#endif
+}
+
+void UnityProfiler::SerializeFrameData(ProfilerFrameData& frame, dynamic_array<int>& buffer)
+{
+#if ENABLE_PLAYERCONNECTION
+ buffer.push_back(UNITY_LITTLE_ENDIAN);
+ buffer.push_back(kProfilerDataStreamVersion);
+
+ frame.Serialize(buffer);
+
+ buffer.push_back(0xAFAFAFAF);
+#endif
+}
+
+bool UnityProfiler::DeserializeFrameData(ProfilerFrameData* frame, const void* data, int size)
+{
+#if ENABLE_PLAYERCONNECTION
+ int* buffer = (int*)data;
+ int wordsize = size/sizeof(int);
+ int* endBuffer = buffer + wordsize;
+ int** bitstream = &buffer;
+
+ int dataIsLittleEndian = *((*bitstream)++);
+ bool shouldswap = UNITY_LITTLE_ENDIAN ? dataIsLittleEndian == 0 : dataIsLittleEndian != 0;
+ if(shouldswap)
+ {
+ int* ptr = *bitstream;
+ while(ptr < endBuffer)
+ SwapEndianBytes(*(ptr++));
+ }
+
+ int version = *((*bitstream)++);
+
+ if(version != kProfilerDataStreamVersion)
+ return false;
+
+ frame->Deserialize(bitstream, shouldswap);
+ Assert(**bitstream == 0xAFAFAFAF);
+ return true;
+#else
+ return false;
+#endif
+}
+
+
+// --------------------------------------------------------------------------
+// Profiler serialization
+
+
+#if ENABLE_PLAYERCONNECTION
+template< class T >
+void WriteArray(dynamic_array<int>& bitstream, const dynamic_array<T>& array)
+{
+ bitstream.push_back(array.size());
+ if(array.size() > 0)
+ {
+ int startindex = bitstream.size();
+ bitstream.resize_uninitialized( startindex + array.size() * sizeof(T) / sizeof(int) );
+ memcpy( (char*)&bitstream[startindex], (char*)&array[0], sizeof(T) * array.size() );
+ }
+}
+
+template< class T >
+void ReadArray( int** bitstream, dynamic_array<T>& array)
+{
+ int size = *((*bitstream)++);
+ array.resize_uninitialized(size);
+ if(size > 0)
+ {
+ memcpy((char*)&array[0], (char*)*bitstream, sizeof(T) * size);
+ *bitstream += sizeof(T) * size / sizeof(int);
+ }
+}
+
+template< typename T >
+void ReadArrayFixup( int** bitstream, dynamic_array<T>& array, bool swapdata)
+{
+ ReadArray<T>(bitstream, array);
+ if (swapdata)
+ {
+ for (typename dynamic_array<T>::iterator it = array.begin(); it != array.end(); ++it)
+ {
+ (*it).Fixup();
+ }
+ }
+}
+
+void WriteConditionaly(dynamic_array<int>& bitstream, ProfilerInformation* object)
+{
+ if (object)
+ {
+ bitstream.push_back(1);
+ ProfilerFrameData::SerializeProfilerInformation(*object, bitstream);
+ }
+ else
+ bitstream.push_back(0);
+}
+
+void ReadConditionaly( int** bitstream, ProfilerInformation*& object, bool swapdata)
+{
+ int condition = *((*bitstream)++);
+ if(condition)
+ object = ProfilerFrameData::DeserializeProfilerInformation(bitstream, swapdata);
+}
+
+static void SerializeString (dynamic_array<int>& bitstream, int len, const char* str)
+{
+ int startindex = bitstream.size();
+ bitstream.resize_initialized( startindex + len/4 + 1);
+ memcpy((char*)&bitstream[startindex], str, len+1);
+}
+
+static std::string DeserializeString (int**& bitstream, bool swapdata)
+{
+ char* chars = (char*)*bitstream;
+ if (swapdata)
+ {
+ int wordcount = strlen(chars)/4 + 1;
+ for(int i = 0; i < wordcount; i++)
+ SwapEndianBytes((*bitstream)[i]);
+ }
+ std::string name((char*)*bitstream);
+ (*bitstream) += name.length()/4 + 1;
+ return name;
+}
+
+
+#define HIPART(x) ((x>>32) & 0xFFFFFFFF)
+#define LOPART(x) (x & 0xFFFFFFFF)
+
+void ProfilerFrameData::Serialize( dynamic_array<int>& bitstream )
+{
+ bitstream.push_back(frameIndex);
+ bitstream.push_back(realFrame);
+ bitstream.push_back(m_StartTimeUS);
+ bitstream.push_back(m_TotalCPUTimeInMicroSec);
+ bitstream.push_back(m_TotalGPUTimeInMicroSec);
+ allStats.Serialize(bitstream);
+
+ bitstream.push_back(m_ThreadCount);
+ for (int t = 0; t < m_ThreadCount; ++t)
+ {
+ const ThreadData& tdata = m_ThreadData[t];
+
+ SerializeString(bitstream, tdata.m_ThreadName.size(), tdata.m_ThreadName.c_str());
+ bitstream.push_back(tdata.m_AllSamples.size());
+ for(int i = 0; i < tdata.m_AllSamples.size(); i++)
+ {
+ bitstream.push_back(tdata.m_AllSamples[i].timeUS);
+ bitstream.push_back(tdata.m_AllSamples[i].startTimeUS);
+ bitstream.push_back(tdata.m_AllSamples[i].nbChildren);
+ }
+
+ bitstream.push_back(tdata.m_GPUTimeSamples.size());
+ for(int i = 0; i < tdata.m_GPUTimeSamples.size(); i++)
+ {
+ bitstream.push_back(tdata.m_GPUTimeSamples[i].gpuTimeInMicroSec);
+ bitstream.push_back(tdata.m_GPUTimeSamples[i].relatedSampleIndex);
+ bitstream.push_back(tdata.m_GPUTimeSamples[i].gpuSection);
+ }
+
+ // Don't write m_InstanceIDSamples, since the IDs are not portable
+ WriteArray(bitstream, tdata.m_AllocatedGCMemorySamples);
+ for(int i = 0; i < tdata.m_AllSamples.size(); i++)
+ WriteConditionaly(bitstream,tdata.m_AllSamples[i].information);
+
+ WriteArray(bitstream, tdata.m_WarningSamples);
+ }
+}
+
+void ProfilerFrameData::Deserialize( int** bitstream, bool swapdata )
+{
+ frameIndex = *((*bitstream)++);
+ realFrame = *((*bitstream)++);
+ m_StartTimeUS = *((*bitstream)++);
+ m_TotalCPUTimeInMicroSec = *((*bitstream)++);
+ m_TotalGPUTimeInMicroSec = *((*bitstream)++);
+ allStats.Deserialize(bitstream, swapdata);
+
+ int threadCount = *((*bitstream)++);
+ if (threadCount != m_ThreadCount)
+ {
+ delete[] m_ThreadData;
+ m_ThreadData = new ThreadData[threadCount];
+ m_ThreadCount = threadCount;
+ }
+
+ for (int t = 0; t < m_ThreadCount; ++t)
+ {
+ ThreadData& tdata = m_ThreadData[t];
+
+ tdata.m_ThreadName = DeserializeString (bitstream, swapdata);
+
+ tdata.m_AllSamples.resize_uninitialized(*((*bitstream)++));
+
+ for(int i = 0; i < tdata.m_AllSamples.size(); i++)
+ {
+ tdata.m_AllSamples[i].timeUS = *((*bitstream)++);
+ tdata.m_AllSamples[i].startTimeUS = *((*bitstream)++);
+ tdata.m_AllSamples[i].nbChildren = *((*bitstream)++);
+ tdata.m_AllSamples[i].information = NULL;
+ }
+
+ tdata.m_GPUTimeSamples.resize_uninitialized(*((*bitstream)++));
+ for(int i = 0; i < tdata.m_GPUTimeSamples.size(); i++)
+ {
+ tdata.m_GPUTimeSamples[i].gpuTimeInMicroSec = *((*bitstream)++);
+ tdata.m_GPUTimeSamples[i].relatedSampleIndex = *((*bitstream)++);
+ tdata.m_GPUTimeSamples[i].gpuSection = (GpuSection)(*((*bitstream)++));
+ tdata.m_GPUTimeSamples[i].timerQuery = NULL;
+ }
+
+ // m_InstanceIDSamples are not written, since the IDs are not portable
+ tdata.m_InstanceIDSamples.resize_uninitialized(0);
+ ReadArray(bitstream, tdata.m_AllocatedGCMemorySamples);
+ for(int i = 0; i < tdata.m_AllSamples.size(); i++)
+ ReadConditionaly(bitstream, tdata.m_AllSamples[i].information, swapdata);
+
+ ReadArray(bitstream, tdata.m_WarningSamples);
+ }
+}
+
+
+void ProfilerFrameData::SerializeProfilerInformation( const ProfilerInformation& info, dynamic_array<int>& bitstream )
+{
+ SerializeString (bitstream, strlen(info.name), info.name);
+ bitstream.push_back((info.group << 16) | (info.flags << 8) | info.isWarning);
+}
+
+ProfilerInformation* ProfilerFrameData::DeserializeProfilerInformation( int** bitstream, bool swapdata )
+{
+ std::string name = DeserializeString (bitstream, swapdata);
+
+ int groupFlags = *((*bitstream)++);
+ UInt16 group = groupFlags >> 16;
+ UInt8 flags = (groupFlags & 0xFF00) >> 8;
+ UInt8 warn = groupFlags & 0xFF;
+
+ UnityProfilerPerThread* prof = UnityProfilerPerThread::ms_InstanceTLS;
+ DebugAssert(prof);
+ return prof->GetProfilerInformation(name, group, flags, warn);
+}
+
+#endif // #if ENABLE_PLAYERCONNECTION
+
+
+
+
+#endif // #if ENABLE_PROFILER