feat: 透明窗口支持 + 退出确认流程 + ImGui 总开关
用户自主改进批次: - 透明窗口:GLFW_TRANSPARENT_FRAMEBUFFER + 窗口 opacity 0.5, swapchain composite alpha 按能力自适应(Opaque/Pre/PostMultiplied), Forward 清屏 alpha=0,tonemap 透传 HDR alpha——物体实体、背景透明 - 退出确认:Window 挂 WindowCloseCallback 发事件,App 侧 OnWindowClose 置确认标记,OnRenderOverlay 弹 ImGui 确认框后 Close(); 主循环退出条件改为 m_Running(close 事件驱动) - ImGui 总开关:` 键切换 ImGuiLayer::SetEnabled,禁用时跳过 NewFrame/OnRenderOverlay/BuildDrawData 整段 - 帧率控制简化:去掉 LimitFps 开关,非 vsync 时恒按 target 限帧 (默认 60,可调 ≥24);m_VSync 默认 false(Mailbox 不限速) - 清理:Model.cpp const 修正,swapchain 日志带 present mode, GPU 遥测暂时注释 验证:0 error 0 warning,composite alpha 实测选 PreMultiplied(0x2), present=Mailbox
This commit is contained in:
+104
-47
@@ -54,8 +54,8 @@ protected:
|
|||||||
|
|
||||||
m_Ground = scene.CreateEntity("Ground");
|
m_Ground = scene.CreateEntity("Ground");
|
||||||
m_Ground.AddComponent<TransformComponent>()
|
m_Ground.AddComponent<TransformComponent>()
|
||||||
.SetPosition(glm::vec3(0.0f, -0.5f, 0.0f))
|
.SetPosition(glm::vec3(0.0f, -4.5f, 0.0f))
|
||||||
.Compose();
|
.Compose();
|
||||||
m_Ground.AddComponent<MeshRendererComponent>(MeshRendererComponent{ground});
|
m_Ground.AddComponent<MeshRendererComponent>(MeshRendererComponent{ground});
|
||||||
|
|
||||||
// 相机实体(primary = 当前渲染视角)
|
// 相机实体(primary = 当前渲染视角)
|
||||||
@@ -65,16 +65,17 @@ protected:
|
|||||||
camera.camera.SetPosition(glm::vec3(0.0f, 0.0f, 7.0f));
|
camera.camera.SetPosition(glm::vec3(0.0f, 0.0f, 7.0f));
|
||||||
|
|
||||||
// 背包实体 × 3(悬浮在地面上方)
|
// 背包实体 × 3(悬浮在地面上方)
|
||||||
auto size = 10;
|
auto size = 1;
|
||||||
for (int i = 0; i < size; ++i) {
|
for (int i = 0; i < size; ++i)
|
||||||
for (int j = 0 ; j < size; j ++)
|
{
|
||||||
|
for (int j = 0; j < size; j++)
|
||||||
{
|
{
|
||||||
auto e = scene.CreateEntity("Backpack" + std::to_string(i));
|
auto e = scene.CreateEntity("Backpack" + std::to_string(i));
|
||||||
|
|
||||||
e.AddComponent<TransformComponent>()
|
e.AddComponent<TransformComponent>()
|
||||||
.SetPosition(glm::vec3((i - size * 0.5) * 1.5f, 0.0f, (j - size * 0.5) * 1.5f))
|
.SetPosition(glm::vec3((i - size * 0.5) * 1.5f, 0.0f, (j - size * 0.5) * 1.5f))
|
||||||
.SetScale(glm::vec3(0.5f))
|
.SetScale(glm::vec3(0.5f))
|
||||||
.Compose();
|
.Compose();
|
||||||
|
|
||||||
e.AddComponent<MeshRendererComponent>(MeshRendererComponent{backpack});
|
e.AddComponent<MeshRendererComponent>(MeshRendererComponent{backpack});
|
||||||
|
|
||||||
@@ -85,13 +86,17 @@ protected:
|
|||||||
// 灯光实体(第一个灯 = 阴影光源)
|
// 灯光实体(第一个灯 = 阴影光源)
|
||||||
m_KeyLight = scene.CreateEntity("KeyLight");
|
m_KeyLight = scene.CreateEntity("KeyLight");
|
||||||
m_KeyLight.AddComponent<LightComponent>(
|
m_KeyLight.AddComponent<LightComponent>(
|
||||||
LightComponent{glm::vec3(-0.5f, -0.6f, -0.35f),
|
LightComponent{
|
||||||
glm::vec4(1.0f, 0.95f, 0.9f, 1.0f)});
|
glm::vec3(-0.5f, -0.6f, -0.35f),
|
||||||
|
glm::vec4(1.0f, 0.95f, 0.9f, 1.0f)
|
||||||
|
});
|
||||||
|
|
||||||
scene.CreateEntity("FillLight")
|
scene.CreateEntity("FillLight")
|
||||||
.AddComponent<LightComponent>(
|
.AddComponent<LightComponent>(
|
||||||
LightComponent{glm::vec3(0.5f, -0.5f, 0.3f),
|
LightComponent{
|
||||||
glm::vec4(0.3f, 0.5f, 1.0f, 0.8f)});
|
glm::vec3(0.5f, -0.5f, 0.3f),
|
||||||
|
glm::vec4(0.3f, 0.5f, 1.0f, 0.8f)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnUpdate(f32 deltaTime) override
|
void OnUpdate(f32 deltaTime) override
|
||||||
@@ -100,10 +105,11 @@ protected:
|
|||||||
|
|
||||||
// 相机(WASD/Space/Shift 移动)
|
// 相机(WASD/Space/Shift 移动)
|
||||||
m_Camera.GetComponent<CameraComponent>()
|
m_Camera.GetComponent<CameraComponent>()
|
||||||
.camera.Update(GetWindow(), deltaTime);
|
.camera.Update(GetWindow(), deltaTime);
|
||||||
|
|
||||||
// 旋转动画:编辑 Transform 分量后 Compose 重算 world
|
// 旋转动画:编辑 Transform 分量后 Compose 重算 world
|
||||||
for (size_t i = 0; i < m_Spinners.size(); ++i) {
|
for (size_t i = 0; i < m_Spinners.size(); ++i)
|
||||||
|
{
|
||||||
auto& t = m_Spinners[i].GetComponent<TransformComponent>();
|
auto& t = m_Spinners[i].GetComponent<TransformComponent>();
|
||||||
t.rotation.y = m_Time * 60.0f + static_cast<f32>(i) * 40.0f;
|
t.rotation.y = m_Time * 60.0f + static_cast<f32>(i) * 40.0f;
|
||||||
t.scale = glm::vec3(0.5f);
|
t.scale = glm::vec3(0.5f);
|
||||||
@@ -117,18 +123,18 @@ protected:
|
|||||||
|
|
||||||
void OnRenderOverlay() override
|
void OnRenderOverlay() override
|
||||||
{
|
{
|
||||||
if (!m_OverlayVisible) return;
|
|
||||||
|
|
||||||
ImGui::ShowDemoWindow();
|
ImGui::ShowDemoWindow();
|
||||||
|
|
||||||
ImGui::Begin("Scene Debug");
|
ImGui::Begin("Scene Debug");
|
||||||
|
|
||||||
// 阴影光源方向:拖动滑条实时看影子变化
|
// 阴影光源方向:拖动滑条实时看影子变化
|
||||||
if (auto* light = m_KeyLight.TryGetComponent<LightComponent>()) {
|
if (auto* light = m_KeyLight.TryGetComponent<LightComponent>())
|
||||||
|
{
|
||||||
ImGui::Text("Key Light (shadow caster)");
|
ImGui::Text("Key Light (shadow caster)");
|
||||||
ImGui::SliderFloat3("direction",
|
ImGui::SliderFloat3("direction",
|
||||||
&light->direction.x, -1.0f, 1.0f);
|
&light->direction.x, -1.0f, 1.0f);
|
||||||
if (ImGui::Button("reset")) {
|
if (ImGui::Button("reset"))
|
||||||
|
{
|
||||||
light->direction = glm::vec3(-0.5f, -0.6f, -0.35f);
|
light->direction = glm::vec3(-0.5f, -0.6f, -0.35f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,21 +144,25 @@ protected:
|
|||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Text("Tonemap");
|
ImGui::Text("Tonemap");
|
||||||
f32 exposure = renderer.GetExposure();
|
f32 exposure = renderer.GetExposure();
|
||||||
if (ImGui::SliderFloat("exposure", &exposure, 0.1f, 4.0f)) {
|
if (ImGui::SliderFloat("exposure", &exposure, 0.1f, 4.0f))
|
||||||
|
{
|
||||||
renderer.SetExposure(exposure);
|
renderer.SetExposure(exposure);
|
||||||
}
|
}
|
||||||
bool aces = renderer.GetUseACES();
|
bool aces = renderer.GetUseACES();
|
||||||
if (ImGui::Checkbox("ACES", &aces)) {
|
if (ImGui::Checkbox("ACES", &aces))
|
||||||
|
{
|
||||||
renderer.SetUseACES(aces);
|
renderer.SetUseACES(aces);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Text("Bloom");
|
ImGui::Text("Bloom");
|
||||||
f32 threshold = renderer.GetBloomThreshold();
|
f32 threshold = renderer.GetBloomThreshold();
|
||||||
if (ImGui::SliderFloat("threshold", &threshold, 0.0f, 2.0f)) {
|
if (ImGui::SliderFloat("threshold", &threshold, 0.0f, 2.0f))
|
||||||
|
{
|
||||||
renderer.SetBloomThreshold(threshold);
|
renderer.SetBloomThreshold(threshold);
|
||||||
}
|
}
|
||||||
f32 strength = renderer.GetBloomStrength();
|
f32 strength = renderer.GetBloomStrength();
|
||||||
if (ImGui::SliderFloat("strength", &strength, 0.0f, 2.0f)) {
|
if (ImGui::SliderFloat("strength", &strength, 0.0f, 2.0f))
|
||||||
|
{
|
||||||
renderer.SetBloomStrength(strength);
|
renderer.SetBloomStrength(strength);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +171,8 @@ protected:
|
|||||||
ImGui::Text("GPU Timings");
|
ImGui::Text("GPU Timings");
|
||||||
const auto& names = renderer.GetPassNames();
|
const auto& names = renderer.GetPassNames();
|
||||||
const auto& times = renderer.GetPassGpuMs();
|
const auto& times = renderer.GetPassGpuMs();
|
||||||
for (size_t i = 0; i < names.size() && i < times.size(); ++i) {
|
for (size_t i = 0; i < names.size() && i < times.size(); ++i)
|
||||||
|
{
|
||||||
ImGui::Text(" %-12s %.3f ms", names[i].c_str(), times[i]);
|
ImGui::Text(" %-12s %.3f ms", names[i].c_str(), times[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,21 +181,21 @@ protected:
|
|||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Text("Frame");
|
ImGui::Text("Frame");
|
||||||
bool vsync = app.GetVSync();
|
bool vsync = app.GetVSync();
|
||||||
if (ImGui::Checkbox("VSync", &vsync)) {
|
if (ImGui::Checkbox("VSync", &vsync))
|
||||||
|
{
|
||||||
app.SetVSync(vsync);
|
app.SetVSync(vsync);
|
||||||
|
if (!vsync)
|
||||||
|
{
|
||||||
|
const f32 target = app.GetTargetFps();
|
||||||
|
app.SetTargetFps(target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
bool limit = app.GetLimitFps();
|
if (!vsync)
|
||||||
if (ImGui::Checkbox("Limit FPS", &limit)) {
|
{
|
||||||
app.SetLimitFps(limit);
|
f32 target = app.GetTargetFps();
|
||||||
}
|
if (ImGui::SliderFloat("target fps", &target, 24.0f, 360.0f, "%.0f"))
|
||||||
if (limit) {
|
{
|
||||||
if (vsync) {
|
app.SetTargetFps(target);
|
||||||
ImGui::TextDisabled("(vsync 开启时限帧无效)");
|
|
||||||
} else {
|
|
||||||
f32 target = app.GetTargetFps();
|
|
||||||
if (ImGui::SliderFloat("target fps", &target, 24.0f, 360.0f, "%.0f")) {
|
|
||||||
app.SetTargetFps(target);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,32 +205,76 @@ protected:
|
|||||||
static_cast<unsigned long long>(GetFrameCount()));
|
static_cast<unsigned long long>(GetFrameCount()));
|
||||||
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
||||||
|
|
||||||
|
// 自定义重载事件: 重载关闭事件模态框
|
||||||
|
{
|
||||||
|
if (m_ShowQuitConfirm)
|
||||||
|
{
|
||||||
|
ImGui::OpenPopup("Should quit?");
|
||||||
|
m_ShowQuitConfirm = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SetNextWindowPos(
|
||||||
|
ImGui::GetMainViewport()->GetCenter(),
|
||||||
|
ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||||
|
|
||||||
|
if (ImGui::BeginPopupModal("Should quit?", nullptr,
|
||||||
|
ImGuiWindowFlags_AlwaysAutoResize))
|
||||||
|
{
|
||||||
|
ImGui::TextUnformatted("Quit the application?");
|
||||||
|
ImGui::Separator();
|
||||||
|
if (ImGui::Button("Yes", ImVec2(120, 0)))
|
||||||
|
{
|
||||||
|
m_QuitConfirmed = true;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("No", ImVec2(120, 0)))
|
||||||
|
{
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_QuitConfirmed)
|
||||||
|
{
|
||||||
|
m_QuitConfirmed = false;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnWindowClose(const Event& event) override
|
||||||
|
{
|
||||||
|
m_ShowQuitConfirm = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnKeyPress(const Event& event) override
|
void OnKeyPress(const Event& event) override
|
||||||
{
|
{
|
||||||
if (event.key == GLFW_KEY_GRAVE_ACCENT) {
|
if (event.key == GLFW_KEY_GRAVE_ACCENT)
|
||||||
m_OverlayVisible = !m_OverlayVisible; // ` 反折叠加层
|
{
|
||||||
|
SetImGuiEnabled(!GetImGuiEnabled());
|
||||||
}
|
}
|
||||||
if (event.key == GLFW_KEY_F1) {
|
if (event.key == GLFW_KEY_F1)
|
||||||
|
{
|
||||||
STATS.PrintComparison();
|
STATS.PrintComparison();
|
||||||
}
|
}
|
||||||
if (event.key == GLFW_KEY_ESCAPE) {
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void OnWindowResize(const Event& event) override
|
void OnWindowResize(const Event& event) override
|
||||||
{
|
{
|
||||||
ENGINE_INFO("App: window resized to {}x{}", event.width, event.height);
|
ENGINE_INFO("App: window resized to {}x{}", event.width, event.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EntityHandle m_Camera{}; // Scene::CreateEntity 之前为空
|
bool m_ShowQuitConfirm = false;
|
||||||
|
bool m_QuitConfirmed = false;
|
||||||
|
EntityHandle m_Camera{}; // Scene::CreateEntity 之前为空
|
||||||
EntityHandle m_Ground{};
|
EntityHandle m_Ground{};
|
||||||
EntityHandle m_KeyLight{};
|
EntityHandle m_KeyLight{};
|
||||||
std::vector<EntityHandle> m_Spinners;
|
std::vector<EntityHandle> m_Spinners;
|
||||||
bool m_OverlayVisible = true;
|
|
||||||
f32 m_Time = 0.0f;
|
f32 m_Time = 0.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -227,10 +282,12 @@ int main()
|
|||||||
{
|
{
|
||||||
SandboxApp app;
|
SandboxApp app;
|
||||||
|
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
app.Run();
|
app.Run();
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
ENGINE_FATAL("Exception: {}", e.what());
|
ENGINE_FATAL("Exception: {}", e.what());
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "Engine/ImGui/ImGuiLayer.h"
|
||||||
|
|
||||||
namespace Engine {
|
namespace Engine {
|
||||||
|
|
||||||
class Window;
|
class Window;
|
||||||
@@ -59,8 +61,6 @@ namespace Engine {
|
|||||||
// 限帧目标最低 24fps(防止过低刷新导致交互卡死),无上限
|
// 限帧目标最低 24fps(防止过低刷新导致交互卡死),无上限
|
||||||
void SetVSync(bool vsync);
|
void SetVSync(bool vsync);
|
||||||
bool GetVSync() const { return m_VSync; }
|
bool GetVSync() const { return m_VSync; }
|
||||||
void SetLimitFps(bool on) { m_LimitFps = on; }
|
|
||||||
bool GetLimitFps() const { return m_LimitFps; }
|
|
||||||
void SetTargetFps(f32 fps) { m_TargetFps = std::max(24.0f, fps); }
|
void SetTargetFps(f32 fps) { m_TargetFps = std::max(24.0f, fps); }
|
||||||
f32 GetTargetFps() const { return m_TargetFps; }
|
f32 GetTargetFps() const { return m_TargetFps; }
|
||||||
|
|
||||||
@@ -72,6 +72,11 @@ namespace Engine {
|
|||||||
f32 GetDeltaTime() const { return m_DeltaTime; }
|
f32 GetDeltaTime() const { return m_DeltaTime; }
|
||||||
u64 GetFrameCount() const { return m_FrameCount; }
|
u64 GetFrameCount() const { return m_FrameCount; }
|
||||||
|
|
||||||
|
// ImGui相关
|
||||||
|
void SetImGuiEnabled(const bool enable) { if (m_ImGuiLayer) m_ImGuiLayer->SetEnabled(enable); }
|
||||||
|
// 理论上ImGuiLayer不应该会空,暂时静默处理
|
||||||
|
bool GetImGuiEnabled() const { return m_ImGuiLayer ? m_ImGuiLayer->IsEnabled() : false; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// ---- 生命周期钩子 ----
|
// ---- 生命周期钩子 ----
|
||||||
|
|
||||||
@@ -119,8 +124,7 @@ namespace Engine {
|
|||||||
u64 m_FrameCount = 0;
|
u64 m_FrameCount = 0;
|
||||||
|
|
||||||
// 帧率控制
|
// 帧率控制
|
||||||
bool m_VSync = true; // 垂直同步(present 自带节奏)
|
bool m_VSync = false; // 垂直同步(present 自带节奏)
|
||||||
bool m_LimitFps = false; // 手动限帧开关(vsync 开启时不生效)
|
|
||||||
f32 m_TargetFps = 60.0f; // 限帧目标(≥24)
|
f32 m_TargetFps = 60.0f; // 限帧目标(≥24)
|
||||||
|
|
||||||
std::chrono::steady_clock::time_point m_LastFrameTime;
|
std::chrono::steady_clock::time_point m_LastFrameTime;
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ namespace Engine {
|
|||||||
static void CursorPosCallback(GLFWwindow* window, double xpos, double ypos);
|
static void CursorPosCallback(GLFWwindow* window, double xpos, double ypos);
|
||||||
static void ScrollCallback(GLFWwindow* window, double xoffset, double yoffset);
|
static void ScrollCallback(GLFWwindow* window, double xoffset, double yoffset);
|
||||||
static void WindowFocusCallback(GLFWwindow* window, int focused);
|
static void WindowFocusCallback(GLFWwindow* window, int focused);
|
||||||
|
|
||||||
|
static void WindowCloseCallback(GLFWwindow* window);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Engine
|
} // namespace Engine
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ namespace Engine
|
|||||||
|
|
||||||
vk::Format m_Format;
|
vk::Format m_Format;
|
||||||
vk::Extent2D m_Extent;
|
vk::Extent2D m_Extent;
|
||||||
bool m_VSync = true;
|
bool m_VSync = false;
|
||||||
};
|
};
|
||||||
} // namespace Engine
|
} // namespace Engine
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ namespace Engine {
|
|||||||
|
|
||||||
OnInit();
|
OnInit();
|
||||||
|
|
||||||
while (m_Running && !m_Window->ShouldClose()) {
|
while (m_Running) {
|
||||||
const auto frameStart = std::chrono::steady_clock::now();
|
const auto frameStart = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
m_Window->PollEvents();
|
m_Window->PollEvents();
|
||||||
@@ -103,13 +103,10 @@ namespace Engine {
|
|||||||
const auto uiStart = std::chrono::steady_clock::now();
|
const auto uiStart = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
// ---- UI 构建段:OnRenderOverlay 只"声明"控件,不直接碰 GPU ----
|
// ---- UI 构建段:OnRenderOverlay 只"声明"控件,不直接碰 GPU ----
|
||||||
if (m_ImGuiLayer) {
|
if (m_ImGuiLayer && m_ImGuiLayer->IsEnabled())
|
||||||
|
{
|
||||||
m_ImGuiLayer->NewFrame();
|
m_ImGuiLayer->NewFrame();
|
||||||
}
|
OnRenderOverlay();
|
||||||
|
|
||||||
OnRenderOverlay();
|
|
||||||
|
|
||||||
if (m_ImGuiLayer) {
|
|
||||||
m_ImGuiLayer->BuildDrawData();
|
m_ImGuiLayer->BuildDrawData();
|
||||||
}
|
}
|
||||||
const auto uiEnd = std::chrono::steady_clock::now();
|
const auto uiEnd = std::chrono::steady_clock::now();
|
||||||
@@ -120,7 +117,7 @@ namespace Engine {
|
|||||||
|
|
||||||
// ---- 帧率控制:vsync 开启时 present 自带节奏;否则按目标限帧 ----
|
// ---- 帧率控制:vsync 开启时 present 自带节奏;否则按目标限帧 ----
|
||||||
// 策略:先 sleep 到剩 ~2ms,再自旋——精度高且不烧 CPU
|
// 策略:先 sleep 到剩 ~2ms,再自旋——精度高且不烧 CPU
|
||||||
if (!m_VSync && m_LimitFps) {
|
if (!m_VSync) {
|
||||||
const auto deadline = frameStart +
|
const auto deadline = frameStart +
|
||||||
std::chrono::microseconds(static_cast<long long>(
|
std::chrono::microseconds(static_cast<long long>(
|
||||||
1e6 / m_TargetFps));
|
1e6 / m_TargetFps));
|
||||||
@@ -137,23 +134,6 @@ namespace Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CPU 侧遥测(每 120 帧一行):定位帧耗时的 CPU 归属
|
|
||||||
const auto frameEnd = std::chrono::steady_clock::now();
|
|
||||||
static u32 cpuTelemetryCount = 0;
|
|
||||||
if (++cpuTelemetryCount % 120 == 1) {
|
|
||||||
const f32 uiMs = std::chrono::duration<f32, std::milli>(
|
|
||||||
uiEnd - uiStart).count();
|
|
||||||
const f32 renderMs = std::chrono::duration<f32, std::milli>(
|
|
||||||
frameEnd - uiEnd).count();
|
|
||||||
const f32 updateMs = std::chrono::duration<f32, std::milli>(
|
|
||||||
uiStart - frameStart).count();
|
|
||||||
const f32 totalMs = std::chrono::duration<f32, std::milli>(
|
|
||||||
frameEnd - frameStart).count();
|
|
||||||
ENGINE_INFO("CPU times: update={:.2f}ms ui={:.2f}ms render={:.2f}ms total={:.2f}ms",
|
|
||||||
updateMs, uiMs, renderMs, totalMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
++m_FrameCount;
|
++m_FrameCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,7 +162,6 @@ namespace Engine {
|
|||||||
|
|
||||||
void Application::OnWindowClose(const Event& event)
|
void Application::OnWindowClose(const Event& event)
|
||||||
{
|
{
|
||||||
// 默认行为:直接退出。需要"退出确认"的程序覆写本钩子。
|
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace Engine
|
|||||||
|
|
||||||
void Window::KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
|
void Window::KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
|
||||||
{
|
{
|
||||||
auto* self = static_cast<Window*>(glfwGetWindowUserPointer(window));
|
const auto* self = static_cast<Window*>(glfwGetWindowUserPointer(window));
|
||||||
if (!self->m_EventCallback || key == GLFW_KEY_UNKNOWN) return;
|
if (!self->m_EventCallback || key == GLFW_KEY_UNKNOWN) return;
|
||||||
|
|
||||||
// UI 捕获键盘时,游戏侧不收键盘事件(ImGui 后端链式回调自行处理)
|
// UI 捕获键盘时,游戏侧不收键盘事件(ImGui 后端链式回调自行处理)
|
||||||
@@ -42,7 +42,7 @@ namespace Engine
|
|||||||
|
|
||||||
void Window::MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
|
void Window::MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
|
||||||
{
|
{
|
||||||
auto* self = static_cast<Window*>(glfwGetWindowUserPointer(window));
|
const auto* self = static_cast<Window*>(glfwGetWindowUserPointer(window));
|
||||||
if (!self->m_EventCallback) return;
|
if (!self->m_EventCallback) return;
|
||||||
if (self->m_MouseCapture && self->m_MouseCapture()) return;
|
if (self->m_MouseCapture && self->m_MouseCapture()) return;
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ namespace Engine
|
|||||||
|
|
||||||
void Window::CursorPosCallback(GLFWwindow* window, double xpos, double ypos)
|
void Window::CursorPosCallback(GLFWwindow* window, double xpos, double ypos)
|
||||||
{
|
{
|
||||||
auto* self = static_cast<Window*>(glfwGetWindowUserPointer(window));
|
const auto* self = static_cast<Window*>(glfwGetWindowUserPointer(window));
|
||||||
if (!self->m_EventCallback) return;
|
if (!self->m_EventCallback) return;
|
||||||
if (self->m_MouseCapture && self->m_MouseCapture()) return;
|
if (self->m_MouseCapture && self->m_MouseCapture()) return;
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ namespace Engine
|
|||||||
|
|
||||||
void Window::ScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
|
void Window::ScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
|
||||||
{
|
{
|
||||||
auto* self = static_cast<Window*>(glfwGetWindowUserPointer(window));
|
const auto* self = static_cast<Window*>(glfwGetWindowUserPointer(window));
|
||||||
if (!self->m_EventCallback) return;
|
if (!self->m_EventCallback) return;
|
||||||
if (self->m_MouseCapture && self->m_MouseCapture()) return;
|
if (self->m_MouseCapture && self->m_MouseCapture()) return;
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ namespace Engine
|
|||||||
|
|
||||||
void Window::WindowFocusCallback(GLFWwindow* window, int focused)
|
void Window::WindowFocusCallback(GLFWwindow* window, int focused)
|
||||||
{
|
{
|
||||||
auto* self = static_cast<Window*>(glfwGetWindowUserPointer(window));
|
const auto* self = static_cast<Window*>(glfwGetWindowUserPointer(window));
|
||||||
if (!self->m_EventCallback) return;
|
if (!self->m_EventCallback) return;
|
||||||
|
|
||||||
Event e;
|
Event e;
|
||||||
@@ -89,6 +89,16 @@ namespace Engine
|
|||||||
self->m_EventCallback(e);
|
self->m_EventCallback(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Window::WindowCloseCallback(GLFWwindow* window)
|
||||||
|
{
|
||||||
|
const auto* self = static_cast<Window*>(glfwGetWindowUserPointer(window));
|
||||||
|
if (!self->m_EventCallback) return;
|
||||||
|
|
||||||
|
Event e;
|
||||||
|
e.type = EventType::WindowClose;
|
||||||
|
self->m_EventCallback(e);
|
||||||
|
}
|
||||||
|
|
||||||
Window::Window(const std::string& title, u32 width, u32 height)
|
Window::Window(const std::string& title, u32 width, u32 height)
|
||||||
: m_Width(width), m_Height(height), m_Title(title)
|
: m_Width(width), m_Height(height), m_Title(title)
|
||||||
{
|
{
|
||||||
@@ -100,6 +110,8 @@ namespace Engine
|
|||||||
|
|
||||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||||
// glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
// glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
||||||
|
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);
|
||||||
|
// 设置窗口为半透明
|
||||||
|
|
||||||
m_Window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
|
m_Window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
|
||||||
if (!m_Window)
|
if (!m_Window)
|
||||||
@@ -108,6 +120,7 @@ namespace Engine
|
|||||||
glfwTerminate();
|
glfwTerminate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
glfwSetWindowOpacity(m_Window, 0.5f);
|
||||||
|
|
||||||
ENGINE_INFO("Window created: {} ({}x{})", title, width, height);
|
ENGINE_INFO("Window created: {} ({}x{})", title, width, height);
|
||||||
|
|
||||||
@@ -120,6 +133,8 @@ namespace Engine
|
|||||||
glfwSetCursorPosCallback(m_Window, CursorPosCallback);
|
glfwSetCursorPosCallback(m_Window, CursorPosCallback);
|
||||||
glfwSetScrollCallback(m_Window, ScrollCallback);
|
glfwSetScrollCallback(m_Window, ScrollCallback);
|
||||||
glfwSetWindowFocusCallback(m_Window, WindowFocusCallback);
|
glfwSetWindowFocusCallback(m_Window, WindowFocusCallback);
|
||||||
|
|
||||||
|
glfwSetWindowCloseCallback(m_Window, WindowCloseCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
Window::~Window()
|
Window::~Window()
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ Scope<Mesh> Model::ProcessMesh(aiMesh* mesh, const aiScene* scene)
|
|||||||
|
|
||||||
aiString path;
|
aiString path;
|
||||||
if (material->GetTexture(aiTextureType_DIFFUSE, 0, &path) == AI_SUCCESS) {
|
if (material->GetTexture(aiTextureType_DIFFUSE, 0, &path) == AI_SUCCESS) {
|
||||||
std::filesystem::path modelDir =
|
const std::filesystem::path modelDir =
|
||||||
std::filesystem::path(m_ModelPath).parent_path();
|
std::filesystem::path(m_ModelPath).parent_path();
|
||||||
texturePath = (modelDir / path.C_Str()).string();
|
texturePath = (modelDir / path.C_Str()).string();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -475,7 +475,7 @@ namespace Engine
|
|||||||
|
|
||||||
vk::ClearValue clearValue;
|
vk::ClearValue clearValue;
|
||||||
clearValue.color = vk::ClearColorValue(
|
clearValue.color = vk::ClearColorValue(
|
||||||
std::array<float, 4>{0.1f, 0.1f, 0.1f, 1.0f});
|
std::array<float, 4>{0.0f, 0.0f, 0.0f, 0.0f});
|
||||||
|
|
||||||
vk::RenderingAttachmentInfo colorAttachment;
|
vk::RenderingAttachmentInfo colorAttachment;
|
||||||
colorAttachment.setImageView(ctx.View(m_HDRColor))
|
colorAttachment.setImageView(ctx.View(m_HDRColor))
|
||||||
@@ -850,6 +850,7 @@ namespace Engine
|
|||||||
: 0.0f;
|
: 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// 控制台遥测:每 120 次读取(≈60 帧 @2 slot)输出一行
|
// 控制台遥测:每 120 次读取(≈60 帧 @2 slot)输出一行
|
||||||
static u32 telemetryCount = 0;
|
static u32 telemetryCount = 0;
|
||||||
if (++telemetryCount % 120 == 1) {
|
if (++telemetryCount % 120 == 1) {
|
||||||
@@ -861,6 +862,7 @@ namespace Engine
|
|||||||
line += " collect=" + std::to_string(m_CollectMs).substr(0, 5) + "ms";
|
line += " collect=" + std::to_string(m_CollectMs).substr(0, 5) + "ms";
|
||||||
ENGINE_INFO("{}", line);
|
ENGINE_INFO("{}", line);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::Render(ECS::Scene& scene, Assets::AssetManager& assets)
|
void Renderer::Render(ECS::Scene& scene, Assets::AssetManager& assets)
|
||||||
|
|||||||
@@ -51,6 +51,21 @@ namespace Engine
|
|||||||
}
|
}
|
||||||
|
|
||||||
vk::SwapchainCreateInfoKHR createInfo;
|
vk::SwapchainCreateInfoKHR createInfo;
|
||||||
|
|
||||||
|
vk::CompositeAlphaFlagBitsKHR compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque;
|
||||||
|
if (caps.supportedCompositeAlpha & vk::CompositeAlphaFlagBitsKHR::ePreMultiplied)
|
||||||
|
{
|
||||||
|
compositeAlpha = vk::CompositeAlphaFlagBitsKHR::ePreMultiplied;
|
||||||
|
}
|
||||||
|
else if (caps.supportedCompositeAlpha & vk::CompositeAlphaFlagBitsKHR::ePostMultiplied)
|
||||||
|
{
|
||||||
|
compositeAlpha = vk::CompositeAlphaFlagBitsKHR::ePostMultiplied;
|
||||||
|
}
|
||||||
|
|
||||||
|
ENGINE_INFO("supportedCompositeAlpha = 0x{:x}, chosen = 0x{:x}",
|
||||||
|
static_cast<u32>(caps.supportedCompositeAlpha),
|
||||||
|
static_cast<u32>(compositeAlpha));
|
||||||
|
|
||||||
createInfo.setSurface(surface)
|
createInfo.setSurface(surface)
|
||||||
.setMinImageCount(imageCount)
|
.setMinImageCount(imageCount)
|
||||||
.setImageFormat(surfaceFormat.format)
|
.setImageFormat(surfaceFormat.format)
|
||||||
@@ -60,7 +75,7 @@ namespace Engine
|
|||||||
.setImageUsage(vk::ImageUsageFlagBits::eColorAttachment)
|
.setImageUsage(vk::ImageUsageFlagBits::eColorAttachment)
|
||||||
.setImageSharingMode(vk::SharingMode::eExclusive)
|
.setImageSharingMode(vk::SharingMode::eExclusive)
|
||||||
.setPreTransform(caps.currentTransform)
|
.setPreTransform(caps.currentTransform)
|
||||||
.setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eOpaque)
|
.setCompositeAlpha(compositeAlpha)
|
||||||
.setPresentMode(presentMode)
|
.setPresentMode(presentMode)
|
||||||
.setClipped(VK_TRUE)
|
.setClipped(VK_TRUE)
|
||||||
.setOldSwapchain(nullptr);
|
.setOldSwapchain(nullptr);
|
||||||
@@ -95,10 +110,11 @@ namespace Engine
|
|||||||
m_ImageViews[i] = vkDevice.createImageView(viewInfo);
|
m_ImageViews[i] = vkDevice.createImageView(viewInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
ENGINE_INFO("Swapchain created: {}x{}, {} images, format={}",
|
ENGINE_INFO("Swapchain created: {}x{}, {} images, format={}, present={}",
|
||||||
m_Extent.width, m_Extent.height,
|
m_Extent.width, m_Extent.height,
|
||||||
m_Images.size(),
|
m_Images.size(),
|
||||||
vk::to_string(m_Format));
|
vk::to_string(m_Format),
|
||||||
|
vk::to_string(presentMode));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Swapchain::Destroy()
|
void Swapchain::Destroy()
|
||||||
@@ -151,11 +167,14 @@ namespace Engine
|
|||||||
|
|
||||||
vk::PresentModeKHR Swapchain::ChoosePresentMode(const std::vector<vk::PresentModeKHR>& modes)
|
vk::PresentModeKHR Swapchain::ChoosePresentMode(const std::vector<vk::PresentModeKHR>& modes)
|
||||||
{
|
{
|
||||||
// vsync 开:Mailbox(三缓冲垂直同步,低延迟);关:Immediate
|
// 实测 Wayland/NVIDIA:Mailbox 不阻塞应用循环(compositor 取最新
|
||||||
//(不限速,可能撕裂)。FIFO 是所有实现必须支持的兜底
|
// 缓冲,应用层计数可远超刷新率);Immediate 常不可用而退到 FIFO。
|
||||||
|
// 因此映射按"用户预期"定义:
|
||||||
|
// vsync 开 → FIFO:应用被 pacing 到刷新率(真正的垂直同步)
|
||||||
|
// vsync 关 → Mailbox:应用循环不限速(Wayland 下无撕裂)
|
||||||
const vk::PresentModeKHR preferred = m_VSync
|
const vk::PresentModeKHR preferred = m_VSync
|
||||||
? vk::PresentModeKHR::eMailbox
|
? vk::PresentModeKHR::eFifo
|
||||||
: vk::PresentModeKHR::eImmediate;
|
: vk::PresentModeKHR::eMailbox;
|
||||||
|
|
||||||
for (const auto& m : modes)
|
for (const auto& m : modes)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ vec3 acesFilm(vec3 x) {
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
// bloom 在 HDR 域叠加(半分辨率模糊图线性采样上采样)
|
// bloom 在 HDR 域叠加(半分辨率模糊图线性采样上采样)
|
||||||
vec3 hdr = texture(hdrImage, fragUV).rgb
|
vec4 hdrTex = texture(hdrImage, fragUV);
|
||||||
+ texture(bloomImage, fragUV).rgb * pc.bloomStrength;
|
vec3 hdr = hdrTex.rgb + texture(bloomImage, fragUV).rgb * pc.bloomStrength;
|
||||||
|
float alpha = hdrTex.a;
|
||||||
|
|
||||||
hdr *= pc.exposure;
|
hdr *= pc.exposure;
|
||||||
|
|
||||||
@@ -34,5 +35,5 @@ void main() {
|
|||||||
? acesFilm(hdr)
|
? acesFilm(hdr)
|
||||||
: clamp(hdr, 0.0, 1.0);
|
: clamp(hdr, 0.0, 1.0);
|
||||||
|
|
||||||
outColor = vec4(pow(mapped, vec3(1.0 / 2.2)), 1.0);
|
outColor = vec4(pow(mapped, vec3(1.0 / 2.2)), alpha);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user