/* 2018-11-27 classroom refactoring exercise - replaced char* with const char* in all arguments - removed dependency on "TextBox", replacing it with std::string outputBuffer. - inverted needlessly long branches to reduce the amount of nested scopes - turned the global functions into static class methods - made "internal" functions private - changed all const char* arguments to const std::string& - fixed smell: multiple output parameters (GetProfileFromHistory(const string&, float*, float*, float*)) ------by creating a SamplePoint type and making use of structured bindings - Changed char* members in ProfileSampleHistory and ProfileSample for std::string --- allowing us to remove all strcmp and strln-calls. */ #ifndef _PROFILE_H #define _PROFILE_H #include class Profile { struct SamplePoint{ float ave{ 0 }, min{ 0 }, max{ 0 }; }; static void StoreProfileInHistory(const std::string& name, float percent); static SamplePoint GetProfileFromHistory(const std::string& name); public: static void Init(void); static void Begin(const std::string& name); static void End(const std::string& name); static void DumpOutputToBuffer(void); static void Draw(void); }; #endif //_PROFILE_H /* Copyright (C) Steve Rabin, 2000. * All rights reserved worldwide. * * This software is provided "as is" without express or implied * warranties. You may freely copy and compile this source into * applications you distribute provided that the copyright text * below is included in the resulting source code, for example: * "Portions Copyright (C) Steve Rabin, 2000" */ #include "profile.h" #include "custom_time.h" #include #include #include #include #include #include #include typedef struct { bool bValid; //Whether this data is valid unsigned int iProfileInstances; //# of times ProfileBegin called int iOpenProfiles; //# of times ProfileBegin w/o ProfileEnd std::string szName; //Name of sample float fStartTime; //The current open profile start time float fAccumulator; //All samples this frame added together float fChildrenSampleTime; //Time taken by all children unsigned int iNumParents; //Number of profile parents } ProfileSample; typedef struct { bool bValid; //Whether the data is valid std::string szName; //Name of the sample float fAve; //Average time per frame (percentage) float fMin; //Minimum time per frame (percentage) float fMax; //Maximum time per frame (percentage) } ProfileSampleHistory; #define NUM_PROFILE_SAMPLES 50 ProfileSample g_samples[NUM_PROFILE_SAMPLES]; ProfileSampleHistory g_history[NUM_PROFILE_SAMPLES]; float g_startProfile = 0.0f; float g_endProfile = 0.0f; std::string outputBuffer = ""; void Profile::Init(void) { for (unsigned int i = 0; i < NUM_PROFILE_SAMPLES; i++) { g_samples[i].bValid = false; g_history[i].bValid = false; } g_startProfile = GetExactTime(); } void Profile::Begin(const std::string& name) { unsigned int i = 0; while (i < NUM_PROFILE_SAMPLES && g_samples[i].bValid == true) { if (g_samples[i].szName == name) { //Found the sample g_samples[i].iOpenProfiles++; g_samples[i].iProfileInstances++; g_samples[i].fStartTime = GetExactTime(); assert(g_samples[i].iOpenProfiles == 1); //max 1 open at once return; } i++; } if (i >= NUM_PROFILE_SAMPLES) { assert(!"Exceeded Max Available Profile Samples"); return; } g_samples[i].szName = name; g_samples[i].bValid = true; g_samples[i].iOpenProfiles = 1; g_samples[i].iProfileInstances = 1; g_samples[i].fAccumulator = 0.0f; g_samples[i].fStartTime = GetExactTime(); g_samples[i].fChildrenSampleTime = 0.0f; } void Profile::End(const std::string& name) { unsigned int i = 0; unsigned int numParents = 0; while (i < NUM_PROFILE_SAMPLES && g_samples[i].bValid == true) { if (g_samples[i].szName != name) { i++; continue; // not the sample we want, go to the next } unsigned int inner = 0; int parent = -1; float fEndTime = GetExactTime(); g_samples[i].iOpenProfiles--; //Count all parents and find the immediate parent while (g_samples[inner].bValid == true) { if (g_samples[inner].iOpenProfiles > 0) { //Found a parent (any open profiles are parents) numParents++; if (parent < 0) { //Replace invalid parent (index) parent = inner; } else if (g_samples[inner].fStartTime >= g_samples[parent].fStartTime) { //Replace with more immediate parent parent = inner; } } inner++; } g_samples[i].iNumParents = numParents; //Remember the current number of parents of the sample if (parent >= 0) { //Record this time in fChildrenSampleTime (add it in) g_samples[parent].fChildrenSampleTime += fEndTime - g_samples[i].fStartTime; } //Save sample time in accumulator g_samples[i].fAccumulator += fEndTime - g_samples[i].fStartTime; return; } } void Profile::DumpOutputToBuffer(void) { unsigned int i = 0; g_endProfile = GetExactTime(); outputBuffer.clear(); outputBuffer += " Ave : Min : Max : # : Profile Name\n"; outputBuffer += "--------------------------------------------\n"; while (i < NUM_PROFILE_SAMPLES && g_samples[i].bValid == true) { unsigned int indent = 0; float sampleTime, percentTime; char line[256], name[256]; std::string indentedName; char ave[16], min[16], max[16], num[16]; if (g_samples[i].iOpenProfiles < 0) { assert(!"ProfileEnd() called without a ProfileBegin()"); } else if (g_samples[i].iOpenProfiles > 0) { assert(!"ProfileBegin() called without a ProfileEnd()"); } sampleTime = g_samples[i].fAccumulator - g_samples[i].fChildrenSampleTime; percentTime = (sampleTime / (g_endProfile - g_startProfile)) * 100.0f; //Add new measurement into the history and get the ave, min, and max StoreProfileInHistory(g_samples[i].szName, percentTime); auto [aveTime, minTime, maxTime] = GetProfileFromHistory(g_samples[i].szName); //TODO Format the data /*std::stringstream output; output << std::setprecision(4) <<;*/ sprintf_s(ave, "%3.1f", aveTime); sprintf_s(min, "%3.1f", minTime); sprintf_s(max, "%3.1f", maxTime); sprintf_s(num, "%3d", g_samples[i].iProfileInstances); indentedName = g_samples[i].szName; for (indent = 0; indent < g_samples[i].iNumParents; indent++) { sprintf_s(name, " %s", indentedName.c_str()); indentedName = name; } sprintf_s(line, "%3s : %3s : %3s : %3s : %s\n", ave, min, max, num, indentedName.c_str()); outputBuffer += line; //Send the line to text buffer i++; } { //Reset samples for next frame unsigned int j; for (j = 0; j < NUM_PROFILE_SAMPLES; j++) { g_samples[j].bValid = false; } g_startProfile = GetExactTime(); } } void Profile::StoreProfileInHistory(const std::string& name, float percent) { unsigned int i = 0; float oldRatio; float newRatio = 0.8f * GetElapsedTime(); if (newRatio > 1.0f) { newRatio = 1.0f; } oldRatio = 1.0f - newRatio; // TODO: replace with std::find for (auto& sample : g_history) { if (!sample.bValid) { break; } if (sample.szName != name) { i++; continue; } //Found the sample sample.fAve = (sample.fAve*oldRatio) + (percent*newRatio); if (percent > sample.fMax) { sample.fMax = percent; } if (percent < sample.fMin) { sample.fMin = percent; } else { sample.fMin = (sample.fMin*oldRatio) + (percent*newRatio); } if (sample.fMin < 0.0f) { sample.fMin = 0.0f; } else { sample.fMax = (sample.fMax*oldRatio) + (percent*newRatio); } return; } if (i < NUM_PROFILE_SAMPLES) { //Add to history g_history[i].szName = name; g_history[i].bValid = true; g_history[i].fAve = g_history[i].fMin = g_history[i].fMax = percent; } else { assert(!"Exceeded Max Available Profile Samples!"); } } Profile::SamplePoint Profile::GetProfileFromHistory(const std::string& name) { SamplePoint result{}; for (auto& sample : g_history){ if (sample.bValid == true && sample.szName == name) { result.ave = sample.fAve; result.max = sample.fMax; result.min = sample.fMin; return result; } } return result; } void Profile::Draw(void) { std::cout << outputBuffer << '\n'; }