跟着cherno手搓游戏引擎【26】Profile和Profile网页可视化

封装Profile:

Sandbox2D.h:ProfileResult结构体和ProfileResult容器,存储相应的信息

#pragma once
#include "YOTO.h"
class Sandbox2D :public YOTO::Layer
{public:
	Sandbox2D();
	virtual ~Sandbox2D() = default;
	virtual void OnAttach()override;
	virtual void OnDetach()override;

	void OnUpdate(YOTO::Timestep ts)override;
	virtual void OnImGuiRender() override;
	void OnEvent(YOTO::Event& e)override;
private:
	YOTO::OrthographicCameraController m_CameraController;

	YOTO::Ref<YOTO::Shader> m_FlatColorShader;
	YOTO::Ref<YOTO::VertexArray> m_SquareVA;
	YOTO::Ref<YOTO::Texture2D>m_CheckerboardTexture;

	struct ProfileResult {

		const char* Name;
		float Time;
	};
	std::vector<ProfileResult>m_ProfileResults;
	glm::vec4 m_SquareColor = { 0.2f,0.3f,0.7f,1.0f };
};

 Sandbox2D.cpp:实现timer并定义PROFILE_SCOPE使用(YT_PROFILE_SCOPE为网页可视化的内容,先放到这了)

#include "Sandbox2D.h"
#include <imgui/imgui.h>
#include <glm/gtc/matrix_transform.hpp>
//#include <Platform/OpenGL/OpenGLShader.h>
#include <glm/gtc/type_ptr.hpp>
#include<vector>
#include<chrono>
template<typename Fn>
class Timer {
public:
	Timer(const char* name, Fn&&func)
		:m_Name(name),m_Func(func),m_Stopped(false)
	{
		m_StartTimepoint = std::chrono::high_resolution_clock::now();
	}
	~Timer() {
		if (!m_Stopped) {
			Stop();
		}
	}
	void Stop() {
		auto endTimepoint= std::chrono::high_resolution_clock::now();
		long long start = std::chrono::time_point_cast<std::chrono::microseconds>(m_StartTimepoint).time_since_epoch().count();
		long long end = std::chrono::time_point_cast<std::chrono::microseconds>(endTimepoint).time_since_epoch().count();
		m_Stopped = true;
		float duration = (end - start)*0.001f;
		m_Func({m_Name,duration});
		//std::cout << "Timer:"<< m_Name << "时差:" << duration << "ms" << std::endl;
	}
private:
	const char* m_Name;
	std::chrono::time_point<std::chrono::steady_clock>m_StartTimepoint;
	bool m_Stopped;
	Fn m_Func;
};
//未找到匹配的重载:auto的问题,改回原来的类型就好了
#define PROFILE_SCOPE(name) Timer timer##__LINE__(name,[&](ProfileResult profileResult) {m_ProfileResults.push_back(profileResult);})
Sandbox2D::Sandbox2D()
:Layer("Sandbox2D"), m_CameraController(1280.0f / 720.0f, true) 
{
}
void Sandbox2D::OnAttach()
{
	m_CheckerboardTexture = YOTO::Texture2D::Create("assets/textures/Checkerboard.png");

}
void Sandbox2D::OnDetach()
{
}

void Sandbox2D::OnUpdate(YOTO::Timestep ts)
{
	YT_PROFILE_FUNCTION();
	PROFILE_SCOPE("Sandbox2D::OnUpdate");
	{
		YT_PROFILE_SCOPE("CameraController::OnUpdate");
		PROFILE_SCOPE("CameraController::OnUpdate");
		//update
		m_CameraController.OnUpdate(ts);
	}
	
	{
		YT_PROFILE_SCOPE("Renderer Prep");
		PROFILE_SCOPE("Renderer Prep");
		//Render
		YOTO::RenderCommand::SetClearColor({ 0.2f, 0.2f, 0.2f, 1.0f });
		YOTO::RenderCommand::Clear();
	}
	
	{
		YT_PROFILE_SCOPE("Renderer Draw");
		PROFILE_SCOPE("Renderer Draw");
		YOTO::Renderer2D::BeginScene(m_CameraController.GetCamera());
		{
			static glm::mat4 scale = glm::scale(glm::mat4(1.0f), glm::vec3(0.1f));
			glm::vec4  redColor(0.8f, 0.3f, 0.3f, 1.0f);
			glm::vec4  blueColor(0.2f, 0.3f, 0.8f, 1.0f);


			/*std::dynamic_pointer_cast<YOTO::OpenGLShader>(m_FlatColorShader)->Bind();
			std::dynamic_pointer_cast<YOTO::OpenGLShader>(m_FlatColorShader)->UploadUniformFloat4("u_Color", m_SquareColor);
			YOTO::Renderer::Submit(m_FlatColorShader, m_SquareVA, glm::scale(glm::mat4(1.0f), glm::vec3(1.5f)));*/

			YOTO::Renderer2D::DrawQuad({ -1.0f,0.0f }, { 0.8f,0.8f }, { 0.8f,0.2f,0.3f,1.0f });
			YOTO::Renderer2D::DrawQuad({ 0.5f,-0.5f }, { 0.5f,0.75f }, { 0.2f,0.3f,0.8f,1.0f });
			YOTO::Renderer2D::DrawQuad({ 0.0f,0.0f,-0.1f }, { 10.0f,10.0f }, m_CheckerboardTexture);
			YOTO::Renderer2D::EndScene();
		}
	}
	
}
void Sandbox2D::OnImGuiRender()
{
	ImGui::Begin("Setting");
	ImGui::ColorEdit4("Color", glm::value_ptr(m_SquareColor));
	for (auto& res : m_ProfileResults) {
		char lable[50];
		strcpy(lable, "%.3fms  ");
		strcat(lable, res.Name);
		ImGui::Text(lable, res.Time);
	}
	m_ProfileResults.clear();
	ImGui::End();
}

void Sandbox2D::OnEvent(YOTO::Event& e)
{
	m_CameraController.OnEvent(e);
}

测试: 

Profile网页可视化:

创建.h文件:

 

instrumentor.h:直接粘贴全部,实现跟封装的profile类似,但是多了生成json文件的代码

#pragma once

#include "YOTO/Core/Log.h"

#include <algorithm>
#include <chrono>
#include <fstream>
#include <iomanip>
#include <string>
#include <thread>
#include <mutex>
#include <sstream>

namespace YOTO {

	using FloatingPointMicroseconds = std::chrono::duration<double, std::micro>;

	struct ProfileResult
	{
		std::string Name;

		FloatingPointMicroseconds Start;
		std::chrono::microseconds ElapsedTime;
		std::thread::id ThreadID;
	};

	struct InstrumentationSession
	{
		std::string Name;
	};

	class Instrumentor
	{
	public:
		Instrumentor(const Instrumentor&) = delete;
		Instrumentor(Instrumentor&&) = delete;

		void BeginSession(const std::string& name, const std::string& filepath = "results.json")
		{
			std::lock_guard lock(m_Mutex);
			if (m_CurrentSession)
			{
				// If there is already a current session, then close it before beginning new one.
				// Subsequent profiling output meant for the original session will end up in the
				// newly opened session instead.  That's better than having badly formatted
				// profiling output.
				if (YOTO::Log::GetCoreLogger()) // Edge case: BeginSession() might be before Log::Init()
				{
					YT_CORE_ERROR("Instrumentor::BeginSession('{0}') when session '{1}' already open.", name, m_CurrentSession->Name);
				}
				InternalEndSession();
			}
			m_OutputStream.open(filepath);

			if (m_OutputStream.is_open())
			{
				m_CurrentSession = new InstrumentationSession({ name });
				WriteHeader();
			}
			else
			{
				if (YOTO::Log::GetCoreLogger()) // Edge case: BeginSession() might be before Log::Init()
				{
					YT_CORE_ERROR("Instrumentor could not open results file '{0}'.", filepath);
				}
			}
		}

		void EndSession()
		{
			std::lock_guard lock(m_Mutex);
			InternalEndSession();
		}

		void WriteProfile(const ProfileResult& result)
		{
			std::stringstream json;

			json << std::setprecision(3) << std::fixed;
			json << ",{";
			json << "\"cat\":\"function\",";
			json << "\"dur\":" << (result.ElapsedTime.count()) << ',';
			json << "\"name\":\"" << result.Name << "\",";
			json << "\"ph\":\"X\",";
			json << "\"pid\":0,";
			json << "\"tid\":" << result.ThreadID << ",";
			json << "\"ts\":" << result.Start.count();
			json << "}";

			std::lock_guard lock(m_Mutex);
			if (m_CurrentSession)
			{
				m_OutputStream << json.str();
				m_OutputStream.flush();
			}
		}

		static Instrumentor& Get()
		{
			static Instrumentor instance;
			return instance;
		}
	private:
		Instrumentor()
			: m_CurrentSession(nullptr)
		{
		}

		~Instrumentor()
		{
			EndSession();
		}

		void WriteHeader()
		{
			m_OutputStream << "{\"otherData\": {},\"traceEvents\":[{}";
			m_OutputStream.flush();
		}

		void WriteFooter()
		{
			m_OutputStream << "]}";
			m_OutputStream.flush();
		}

		// Note: you must already own lock on m_Mutex before
		// calling InternalEndSession()
		void InternalEndSession()
		{
			if (m_CurrentSession)
			{
				WriteFooter();
				m_OutputStream.close();
				delete m_CurrentSession;
				m_CurrentSession = nullptr;
			}
		}
	private:
		std::mutex m_Mutex;
		InstrumentationSession* m_CurrentSession;
		std::ofstream m_OutputStream;
	};

	class InstrumentationTimer
	{
	public:
		InstrumentationTimer(const char* name)
			: m_Name(name), m_Stopped(false)
		{
			m_StartTimepoint = std::chrono::steady_clock::now();
		}

		~InstrumentationTimer()
		{
			if (!m_Stopped)
				Stop();
		}

		void Stop()
		{
			auto endTimepoint = std::chrono::steady_clock::now();
			auto highResStart = FloatingPointMicroseconds{ m_StartTimepoint.time_since_epoch() };
			auto elapsedTime = std::chrono::time_point_cast<std::chrono::microseconds>(endTimepoint).time_since_epoch() - std::chrono::time_point_cast<std::chrono::microseconds>(m_StartTimepoint).time_since_epoch();

			Instrumentor::Get().WriteProfile({ m_Name, highResStart, elapsedTime, std::this_thread::get_id() });

			m_Stopped = true;
		}
	private:
		const char* m_Name;
		std::chrono::time_point<std::chrono::steady_clock> m_StartTimepoint;
		bool m_Stopped;
	};

	namespace InstrumentorUtils {

		template <size_t N>
		struct ChangeResult
		{
			char Data[N];
		};

		template <size_t N, size_t K>
		constexpr auto CleanupOutputString(const char(&expr)[N], const char(&remove)[K])
		{
			ChangeResult<N> result = {};

			size_t srcIndex = 0;
			size_t dstIndex = 0;
			while (srcIndex < N)
			{
				size_t matchIndex = 0;
				while (matchIndex < K - 1 && srcIndex + matchIndex < N - 1 && expr[srcIndex + matchIndex] == remove[matchIndex])
					matchIndex++;
				if (matchIndex == K - 1)
					srcIndex += matchIndex;
				result.Data[dstIndex++] = expr[srcIndex] == '"' ? '\'' : expr[srcIndex];
				srcIndex++;
			}
			return result;
		}
	}
}

#define YT_PROFILE 0
#if YT_PROFILE
// Resolve which function signature macro will be used. Note that this only
// is resolved when the (pre)compiler starts, so the syntax highlighting
// could mark the wrong one in your editor!
#if defined(__GNUC__) || (defined(__MWERKS__) && (__MWERKS__ >= 0x3000)) || (defined(__ICC) && (__ICC >= 600)) || defined(__ghs__)
#define YT_FUNC_SIG __PRETTY_FUNCTION__
#elif defined(__DMC__) && (__DMC__ >= 0x810)
#define YT_FUNC_SIG __PRETTY_FUNCTION__
#elif (defined(__FUNCSIG__) || (_MSC_VER))
#define YT_FUNC_SIG __FUNCSIG__
#elif (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 600)) || (defined(__IBMCPP__) && (__IBMCPP__ >= 500))
#define YT_FUNC_SIG __FUNCTION__
#elif defined(__BORLANDC__) && (__BORLANDC__ >= 0x550)
#define YT_FUNC_SIG __FUNC__
#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901)
#define YT_FUNC_SIG __func__
#elif defined(__cplusplus) && (__cplusplus >= 201103)
#define YT_FUNC_SIG __func__
#else
#define YT_FUNC_SIG "YT_FUNC_SIG unknown!"
#endif

#define YT_PROFILE_BEGIN_SESSION(name, filepath) ::YOTO::Instrumentor::Get().BeginSession(name, filepath)
#define YT_PROFILE_END_SESSION() ::YOTO::Instrumentor::Get().EndSession()
#define YT_PROFILE_SCOPE_LINE2(name, line) constexpr auto fixedName##line = ::YOTO::InstrumentorUtils::CleanupOutputString(name, "__cdecl ");\
											   ::YOTO::InstrumentationTimer timer##line(fixedName##line.Data)
#define YT_PROFILE_SCOPE_LINE(name, line) YT_PROFILE_SCOPE_LINE2(name, line)
#define YT_PROFILE_SCOPE(name) YT_PROFILE_SCOPE_LINE(name, __LINE__)
#define YT_PROFILE_FUNCTION() YT_PROFILE_SCOPE(YT_FUNC_SIG)
#else
#define YT_PROFILE_BEGIN_SESSION(name, filepath)
#define YT_PROFILE_END_SESSION()
#define YT_PROFILE_SCOPE(name)
#define YT_PROFILE_FUNCTION()
#endif

 EntryPoint.h:使用定义

#pragma once

#ifdef YT_PLATFORM_WINDOWS

#include "YOTO.h"
void main(int argc,char** argv) {
	//初始化日志
	YOTO::Log::Init();
	//YT_CORE_ERROR("EntryPoint测试警告信息");
	//int test = 1;
	//YT_CLIENT_INFO("EntryPoint测试info:test={0}",test);
	YT_PROFILE_BEGIN_SESSION("Start","YOTOProfile-Startup.json");
	auto app = YOTO::CreateApplication();
	YT_PROFILE_END_SESSION();

	YT_PROFILE_BEGIN_SESSION("Runtime", "YOTOProfile-Runtime.json");
	app->Run();
	YT_PROFILE_END_SESSION();

	YT_PROFILE_BEGIN_SESSION("Shutdown", "YOTOProfile-Shutdown.json");
	delete app;
	YT_PROFILE_END_SESSION();
}
#endif

ytpch.h:

#pragma once
#include<iostream>
#include<memory>
#include<utility>
#include<algorithm>
#include<functional>
#include<string>
#include<vector>
#include<unordered_map>
#include<unordered_set>
#include<sstream>
#include<array>
#include "YOTO/Core/Log.h"

#include "YOTO/Debug/instrumentor.h"
#ifdef YT_PLATFORM_WINDOWS
#include<Windows.h>
#endif // YT_PLATFORM_WINDOWS

测试: 

在谷歌浏览器输入:chrome://tracing

拖入json文件:

cool,虽然看不太懂,但是文件有够大(运行了几秒就2000多k,平时使用还是用自己写的封装的叭) 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/412028.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Docker Volume

"Ice in my vein" Docker Volume(存储卷) 什么是存储卷? 存储卷就是: “将宿主机的本地文件系统中存在的某个目录&#xff0c;与容器内部的文件系统上的某一目录建立绑定关系”。 存储卷与容器本身的联合文件系统&#xff1f; 在宿主机上的这个与容器形成绑定关系…

3D生成式AI模型与工具

当谈到技术炒作时&#xff0c;人工智能正在超越虚拟世界&#xff0c;吸引世界各地企业和消费者的注意力。 但人工智能可以进一步增强虚拟世界&#xff0c;至少在某种意义上&#xff1a;资产创造。 AI 有潜力扩大用于虚拟环境的 3D 资产的创建。 AI 3D生成使用人工智能生成3D模…

能碳双控| AIRIOT智慧能碳管理解决方案

在当前全球气候变化和可持续发展的背景下&#xff0c;建设能碳管理平台成为组织迎接挑战、提升可持续性的重要一环&#xff0c;有助于组织实现可持续发展目标&#xff0c;提高社会责任形象&#xff0c;同时适应未来碳排放管理的挑战。能碳管理是一个涉及跟踪、报告和减少组织碳…

C++面试宝典第32题:零钱兑换

题目 给定不同面额的硬币coins和一个总金额amount,编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,则返回-1。说明:你可以认为每种硬币的数量是无限的。 示例1: 输入:coins = [1, 2, 5], amount = 11 输出:3 解释:11 = …

ETL是什么

一、ETL概念 ETL&#xff0c;是英文Extract-Transform-Load的缩写&#xff0c;用来描述将数据从来源端经过抽取&#xff08;extract&#xff09;、转换&#xff08;transform&#xff09;、加载&#xff08;load&#xff09;至目的端的过程。ETL一词较常用在数据仓库&#xff…

光谱数据处理:1.特征波长优选的不同方法与Python实现

首先&#xff0c;我们要理解为什么要对“光谱数据进行特征波长优选”以及这是在干嘛&#xff0c;光谱数据可以想象成一长串的彩色条纹&#xff0c;每种颜色对应一个波长&#xff0c;就像彩虹一样。这些颜色的条纹代表了从某种物质&#xff08;比如植物、矿石或是食品&#xff0…

计网自顶向下:网络应用层【Web应用与HTTP协议】

目录 Web应用Web页URLWorld Wide Web 超文本传输协议——HTTP超文本C/S结构报文请求报文响应报文HTTP响应状态码try&#xff1a;在命令行里手工给web服务器发送请求 http连接的两种类型非持久&#xff08;http1.0&#xff09;持久&#xff08;http1.1&#xff09;▷ 流水线▷ 非…

【自然语言处理三-自注意self attention】

自然语言处理三-自注意力 self attention 自注意力是什么&#xff1f;自注意力模型出现的原因是什么&#xff1f;词性标注问题解决方法1-扩展window&#xff0c;引用上下文解决方法2-运用seq2seq架构新问题来了&#xff1a;参数量增加、无法并行的顽疾 自注意力self attention模…

C++ list详解以及模拟实现

目录 1.list的使用 1.1list的定义 1.2list的使用 1.3list iterator使用 1.4list capacity 1.5list element access 1.6list增删查改 2.list迭代器失效问题 3.list的模拟实现 1.list的使用 1.1list的定义 1. list是可以在常数范围内在任意位置进行插入和删除的序列式容…

水印相机小程序源码

水印相机前端源码&#xff0c;本程序无需后端&#xff0c;前端直接导入即可&#xff0c;没有添加流量主功能&#xff0c;大家开通后自行添加 源码搜索&#xff1a;源码软件库 注意小程序后台的隐私权限设置&#xff0c;前端需要授权才可使用 真实时间地址拍照记录&#xff0c…

alembic

alembic是sqlalchemy的作者开发的。 用来做OMR模型与数据库的迁移与映射。 第一个&#xff0c;alembic的所有命令都是以alembic开头 第二&#xff0c;alembic的迁移文件也是通过版本进行控制的。首先&#xff0c;通过pip install alembic进行安装。以下将解释alembic的用法 方…

从管易云·奇门到金蝶云星空通过接口配置打通数据

从管易云奇门到金蝶云星空通过接口配置打通数据 对接源平台:管易云奇门 管易云是金蝶旗下专注提供电商企业管理软件服务的子品牌&#xff0c;先后开发了C-ERP、EC-OMS、EC-WMS、E店管家、BBC、B2B、B2C商城网站建设等产品和服务&#xff0c;涵盖电商业务全流程。 目标系统:金蝶…

ZYNQ:串口-CAN协议转换

前言 目前已经实现zynq的PS-CAN和PL-CAN功能。串口-CAN协议转换是实现以太网-CAN功能的过渡&#xff0c;通过这个流程能够减少后期以太网工程出现问题的频率。阶段性功能目标如下&#xff1a; 实现数据在CAN调试助手和串口调试助手之间的来回转换&#xff0c;从而了解中断机制…

Vue前端对请假模块——请假开始时间和请假结束时间的校验处理

开发背景&#xff1a;Vueelement组件开发 业务需求&#xff1a;用户提交请假申请单&#xff0c;请假申请的业务逻辑处理 实现&#xff1a;用户选择开始时间需要大于本地时间&#xff0c;不得大于请假结束时间&#xff0c;请假时长根据每日工作时间实现累加计算 页面布局 在前…

【Excel PDF 系列】EasyExcel + iText 库

你知道的越多&#xff0c;你不知道的越多 点赞再看&#xff0c;养成习惯 如果您有疑问或者见解&#xff0c;欢迎指教&#xff1a; 企鹅&#xff1a;869192208 文章目录 前言转换前后效果引入 pom 配置代码实现定义 ExcelDataVo 对象主方法EasyExcel 监听器 前言 最近遇到生成 …

SQL进阶(三):Join 小技巧:提升数据的处理速度

复杂数据结构处理&#xff1a;Join 小技巧&#xff1a;提升数据的处理速度 本文是在原本sql闯关的基础上总结得来&#xff0c;加入了自己的理解以及疑问解答&#xff08;by GPT4&#xff09; 原活动链接 用到的数据&#xff1a;链接 提取码&#xff1a;l03e 目录 1. 课前小问…

动态规划之第 N 个泰波那契数/三步问题【leetCode】【算法】

动态规划动态规划之第 N 个泰波那契数/三步问题 动态规划LeetCode题目第 N 个泰波那契数求解1求解2&#xff08;滚动数组&#xff09; 三步问题求解1求解2&#xff08;滚动数组&#xff09; 动态规划 如果问题是由重叠的子问题构成的&#xff0c;那就可以用动态规划&#xff08…

JSON简介以及如何在Python中使用JSON

什么是JSON&#xff1f; JSON是"JavaScript Object Notation"的简称&#xff0c;是一种数据交换格式 JSON格式 假设我们有一个对象&#xff0c;这个对象有两个属性&#xff1a;“name”跟“age”。 在JSON中是这样表达的&#xff1a; { "name":"男孩…

【 C++ 】闭散列哈希表的模拟实现

哈希节点状态 我们都很清楚数组里的每一个值无非三种状态&#xff1a; 如果某下标没有值&#xff0c;则代表空EMPTY。如果有值在代表存在EXIST。如果此位置的值被删掉了&#xff0c;则表示为DELETE。 而这三种状态我们可以借助enum枚举来帮助我们表示数组里每个位置的状态。…

RK3568平台开发系列讲解(基础篇)如何快速学习一套 Linux开发板源码

🚀返回专栏总目录 文章目录 一、基础代码二、驱动代码沉淀、分享、成长,让自己和他人都能有所收获!😄 拿到一份源码和一块评估板,如何快速找到与这块板相关的源码,是很多研发人员都曾遇到过的问题。如果对内核源码结构有大概了解,要完成这些事情也不难,通常可按照基础…