/*
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 <string>
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 <assert.h>
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
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';
}
Comments
0 B
|0 👍
/0 👎
0 B
|0 👍
/0 👎