Files
HelloVulkan/App/main.cpp
T
atdunbg d8274e8bdc feat: 帧率控制——手动限帧 + VSync 开关
- Application 帧率控制 API:SetLimitFps/SetTargetFps/SetVSync。
  限帧目标最低 24fps(防止过低刷新导致交互卡死),无上限;
  vsync 开启时 present 自带节奏,手动限帧不生效
- 限帧策略:sleep 到剩 ~2ms 后自旋,精度高且不烧 CPU
- Swapchain 加 VSync 开关:ChoosePresentMode 按开关选
  Mailbox(低延迟垂直同步)/ Immediate(不限速可撕裂),
  FIFO 兜底;切换经 SetVSync → 重建 swapchain(复用 HandleResize)
- Scene Debug 面板新增 Frame 区块(vsync/limit/目标滑条)
- Run 循环新增 CPU 归属遥测(update/ui/render/total,每 120 帧)
2026-09-22 20:33:10 +08:00

240 lines
7.4 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// Created by atdunbg on 2026/9/20.
//
// SandboxApp:第一个基于 Application 框架的程序。
//
// main 的全部职责:定义一个 Application 派生类并运行它。
// 窗口、渲染器、事件泵、帧计时、场景容器全部由框架管理;
// 这里只写"程序逻辑":创建实体、逐帧更新组件、事件响应。
//
#include <Engine/Core/Application.h>
#include <Engine/Core/Log.h>
#include <Engine/Core/Stats.h>
#include <Engine/ECS/Scene.h>
#include <Engine/ECS/EntityHandle.h>
#include <Engine/ECS/Components.h>
#include <Engine/Assets/AssetManager.h>
#include <Engine/Renderer/Renderer.h>
#include <imgui.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <GLFW/glfw3.h>
using namespace Engine;
using namespace Engine::ECS;
class SandboxApp final : public Application
{
public:
SandboxApp()
: Application("HelloVulkan", 800, 600)
{
}
protected:
// ---- 生命周期 ----
void OnInit() override
{
Scene& scene = GetScene();
// ImGui 叠加层(框架只做管线桥接,UI 内容全部在这里定义)
EnableImGui();
// 模型资产(AssetManager 负责加载/缓存/生命周期,返回句柄)
auto backpack = GetAssets().LoadModel("assets/models/backpack/backpack.obj");
// 地面:程序化纯色平面(半边长 15,中心 y=-2)
auto ground = GetAssets().CreatePlane(15.0f, 140, 140, 145);
m_Ground = scene.CreateEntity("Ground");
m_Ground.AddComponent<TransformComponent>()
.SetPosition(glm::vec3(0.0f, -0.5f, 0.0f))
.Compose();
m_Ground.AddComponent<MeshRendererComponent>(MeshRendererComponent{ground});
// 相机实体(primary = 当前渲染视角)
m_Camera = scene.CreateEntity("MainCamera");
auto& camera = m_Camera.AddComponent<CameraComponent>();
camera.primary = true;
camera.camera.SetPosition(glm::vec3(0.0f, 0.0f, 7.0f));
// 背包实体 × 3(悬浮在地面上方)
auto size = 10;
for (int i = 0; i < size; ++i) {
for (int j = 0 ; j < size; j ++)
{
auto e = scene.CreateEntity("Backpack" + std::to_string(i));
e.AddComponent<TransformComponent>()
.SetPosition(glm::vec3((i - size * 0.5) * 1.5f, 0.0f, (j - size * 0.5) * 1.5f))
.SetScale(glm::vec3(0.5f))
.Compose();
e.AddComponent<MeshRendererComponent>(MeshRendererComponent{backpack});
m_Spinners.push_back(e);
}
}
// 灯光实体(第一个灯 = 阴影光源)
m_KeyLight = scene.CreateEntity("KeyLight");
m_KeyLight.AddComponent<LightComponent>(
LightComponent{glm::vec3(-0.5f, -0.6f, -0.35f),
glm::vec4(1.0f, 0.95f, 0.9f, 1.0f)});
scene.CreateEntity("FillLight")
.AddComponent<LightComponent>(
LightComponent{glm::vec3(0.5f, -0.5f, 0.3f),
glm::vec4(0.3f, 0.5f, 1.0f, 0.8f)});
}
void OnUpdate(f32 deltaTime) override
{
m_Time += deltaTime;
// 相机(WASD/Space/Shift 移动)
m_Camera.GetComponent<CameraComponent>()
.camera.Update(GetWindow(), deltaTime);
// 旋转动画:编辑 Transform 分量后 Compose 重算 world
for (size_t i = 0; i < m_Spinners.size(); ++i) {
auto& t = m_Spinners[i].GetComponent<TransformComponent>();
t.rotation.y = m_Time * 60.0f + static_cast<f32>(i) * 40.0f;
t.scale = glm::vec3(0.5f);
t.Compose();
}
}
// ---- 事件 ----
// ---- 叠加层 UI(唯一允许出现 ImGui 控件的地方)----
void OnRenderOverlay() override
{
if (!m_OverlayVisible) return;
ImGui::ShowDemoWindow();
ImGui::Begin("Scene Debug");
// 阴影光源方向:拖动滑条实时看影子变化
if (auto* light = m_KeyLight.TryGetComponent<LightComponent>()) {
ImGui::Text("Key Light (shadow caster)");
ImGui::SliderFloat3("direction",
&light->direction.x, -1.0f, 1.0f);
if (ImGui::Button("reset")) {
light->direction = glm::vec3(-0.5f, -0.6f, -0.35f);
}
}
// Tonemap + Bloom 参数(HDR 后处理输出端)
auto& renderer = GetRenderer();
ImGui::Separator();
ImGui::Text("Tonemap");
f32 exposure = renderer.GetExposure();
if (ImGui::SliderFloat("exposure", &exposure, 0.1f, 4.0f)) {
renderer.SetExposure(exposure);
}
bool aces = renderer.GetUseACES();
if (ImGui::Checkbox("ACES", &aces)) {
renderer.SetUseACES(aces);
}
ImGui::Text("Bloom");
f32 threshold = renderer.GetBloomThreshold();
if (ImGui::SliderFloat("threshold", &threshold, 0.0f, 2.0f)) {
renderer.SetBloomThreshold(threshold);
}
f32 strength = renderer.GetBloomStrength();
if (ImGui::SliderFloat("strength", &strength, 0.0f, 2.0f)) {
renderer.SetBloomStrength(strength);
}
// Pass 级 GPU 耗时(RG timestamp)
ImGui::Separator();
ImGui::Text("GPU Timings");
const auto& names = renderer.GetPassNames();
const auto& times = renderer.GetPassGpuMs();
for (size_t i = 0; i < names.size() && i < times.size(); ++i) {
ImGui::Text(" %-12s %.3f ms", names[i].c_str(), times[i]);
}
// 帧率控制
auto& app = *this;
ImGui::Separator();
ImGui::Text("Frame");
bool vsync = app.GetVSync();
if (ImGui::Checkbox("VSync", &vsync)) {
app.SetVSync(vsync);
}
bool limit = app.GetLimitFps();
if (ImGui::Checkbox("Limit FPS", &limit)) {
app.SetLimitFps(limit);
}
if (limit) {
if (vsync) {
ImGui::TextDisabled("(vsync 开启时限帧无效)");
} else {
f32 target = app.GetTargetFps();
if (ImGui::SliderFloat("target fps", &target, 24.0f, 360.0f, "%.0f")) {
app.SetTargetFps(target);
}
}
}
ImGui::Separator();
ImGui::Text("fps: %.1f frame: %llu",
1.0f / GetDeltaTime(),
static_cast<unsigned long long>(GetFrameCount()));
ImGui::End();
}
void OnKeyPress(const Event& event) override
{
if (event.key == GLFW_KEY_GRAVE_ACCENT) {
m_OverlayVisible = !m_OverlayVisible; // ` 反折叠加层
}
if (event.key == GLFW_KEY_F1) {
STATS.PrintComparison();
}
if (event.key == GLFW_KEY_ESCAPE) {
Close();
}
}
void OnWindowResize(const Event& event) override
{
ENGINE_INFO("App: window resized to {}x{}", event.width, event.height);
}
private:
EntityHandle m_Camera{}; // Scene::CreateEntity 之前为空
EntityHandle m_Ground{};
EntityHandle m_KeyLight{};
std::vector<EntityHandle> m_Spinners;
bool m_OverlayVisible = true;
f32 m_Time = 0.0f;
};
int main()
{
SandboxApp app;
try {
app.Run();
}
catch (const std::exception& e) {
ENGINE_FATAL("Exception: {}", e.what());
return -1;
}
return 0;
}