VulkanWidget 渲染问题完全解决

This commit is contained in:
ubuntu1804 2025-11-10 17:01:06 +08:00
parent 5dea0554b3
commit e8b9571d25
9 changed files with 567 additions and 166 deletions

316
FINAL_SOLUTION.md Normal file
View File

@ -0,0 +1,316 @@
# VulkanWidget 渲染问题最终解决方案
## 问题描述
VulkanWidget 中绘制的波浪线和彩色球存在严重的渲染问题:
- 图形超出 widget 范围
- 图形随时间移动,从各个角落慢慢进入
- 垂直方向严重拉伸变形(椭圆而非正圆)
- 圆心位置不固定
## 根本原因
经过深入调试,发现问题的**根本原因**是:
### Uniform Buffer 结构体内存布局不匹配
**C++ 端定义**`src/vulkanrenderer.h`
```cpp
struct UniformBufferObject {
float time; // offset 0
float resolution[2]; // offset 4, 8
float rotation; // offset 12
float wavePhase; // offset 16
float padding[2]; // offset 20, 24
};
```
**Shader 端定义**`shaders/geometry.vert`,之前的错误版本):
```glsl
layout(binding = 0) uniform UniformBufferObject {
float time;
vec2 resolution; // GLSL std140 对齐可能不同!
float rotation;
float wavePhase;
} ubo;
```
**问题**
- GLSL 的 `vec2` 在 std140 布局中的对齐方式与 C++ 的 `float[2]` 可能不同
- 导致 shader 读取的 `resolution` 值错误
- 错误的 resolution 导致坐标转换错误,产生变形和位移
## 完整解决方案
### 1. 修复 Shader UBO 布局
**文件**: `shaders/geometry.vert`
`vec2` 拆分为两个独立的 `float`,并明确指定 `std140` 布局:
```glsl
layout(binding = 0, std140) uniform UniformBufferObject {
float time; // offset 0
float resX; // offset 4
float resY; // offset 8
float rotation; // offset 12
float wavePhase; // offset 16
float padding1; // offset 20
float padding2; // offset 24
} ubo;
void main() {
// 使用 resX 和 resY 而不是 resolution.x 和 resolution.y
float ndcX = (inPosition.x / ubo.resX) * 2.0 - 1.0;
float ndcY = (inPosition.y / ubo.resY) * 2.0 - 1.0;
gl_Position = vec4(ndcX, ndcY, 0.0, 1.0);
fragColor = inColor;
fragTexCoord = inTexCoord;
}
```
**关键点**
- 明确使用 `std140` 布局
- 避免使用 `vec2`,改用两个 `float`
- 确保与 C++ 结构体完全对齐
### 2. 更新所有 Uniform Buffers
**文件**: `src/vulkanrenderer.cpp` - `recordCommandBuffer()`
```cpp
// CRITICAL: 每帧更新所有 uniform buffers
// 因为有 MAX_FRAMES_IN_FLIGHT (通常是2) 个 buffers
// 必须保证所有 buffer 都有最新数据
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
updateUniformBuffer(i);
}
```
**原因**
- Vulkan 使用多个 uniform buffers 来支持并行渲染
- 每个 descriptor set 指向不同的 buffer
- 如果只更新当前帧的 buffer其他 buffer 会有过期数据
### 3. 启用动态视口和裁剪矩形
**文件**: `src/vulkanrenderer.cpp` - 管线创建函数
```cpp
// 在所有管线创建中添加动态状态
VkDynamicState dynamicStates[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicState = {};
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.dynamicStateCount = 2;
dynamicState.pDynamicStates = dynamicStates;
// 视口状态不设置静态值
viewportState.pViewports = nullptr;
viewportState.pScissors = nullptr;
// 添加到管线创建信息
pipelineInfo.pDynamicState = &dynamicState;
```
`recordCommandBuffer()` 中动态设置:
```cpp
VkViewport viewport = {};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(m_width);
viewport.height = static_cast<float>(m_height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
VkRect2D scissor = {};
scissor.offset = {0, 0};
scissor.extent = {m_width, m_height};
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
```
### 4. 初始化 Uniform Buffer
**文件**: `src/vulkanrenderer.cpp` - `initialize()`
```cpp
// 创建 uniform buffers 后立即初始化
m_ubo.time = 0.0f;
m_ubo.resolution[0] = static_cast<float>(m_width);
m_ubo.resolution[1] = static_cast<float>(m_height);
m_ubo.rotation = 0.0f;
m_ubo.wavePhase = 0.0f;
// 将初始值写入所有 uniform buffers
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
if (m_uniformBuffersMapped[i] != nullptr) {
memcpy(m_uniformBuffersMapped[i], &m_ubo, sizeof(m_ubo));
}
}
```
### 5. 在 resize 时更新 UBO
**文件**: `src/vulkanrenderer.cpp` - `resize()`
```cpp
m_width = width;
m_height = height;
// 立即更新 UBO resolution
m_ubo.resolution[0] = static_cast<float>(m_width);
m_ubo.resolution[1] = static_cast<float>(m_height);
// 更新所有 uniform buffers
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
if (i < m_uniformBuffersMapped.size() && m_uniformBuffersMapped[i] != nullptr) {
memcpy(m_uniformBuffersMapped[i], &m_ubo, sizeof(m_ubo));
}
}
```
## 修改文件清单
### 主要修改
1. **shaders/geometry.vert**
- 使用 `std140` 布局
- 将 `vec2 resolution` 改为 `float resX, resY`
- 添加显式的 padding 字段
2. **src/vulkanrenderer.cpp**
- `initialize()`: 初始化所有 uniform buffers
- `resize()`: 更新 UBO 并写入所有 buffers
- `recordCommandBuffer()`: 每帧更新所有 buffers
- `createBackgroundPipeline()`: 添加动态状态
- `createGeometryPipeline()`: 添加动态状态
- `createLinePipeline()`: 添加动态状态
3. **src/vulkanrenderer.h**
- `drawGeometry()`: 添加 frameCount 参数
## 技术要点
### 1. GLSL std140 布局规则
- `float`: 4 字节对齐
- `vec2`: 8 字节对齐(可能与 C++ `float[2]` 不同)
- `vec3`, `vec4`: 16 字节对齐
- 数组每个元素都是 16 字节对齐
**最佳实践**:避免在 UBO 中使用 `vec2`,使用独立的 `float`
### 2. Vulkan 多缓冲
- `MAX_FRAMES_IN_FLIGHT` 通常为 2 或 3
- 每帧可能使用不同的 uniform buffer
- **必须保证所有 buffers 数据一致**
### 3. 动态状态的优势
- 窗口大小变化时无需重建管线
- 性能开销极小
- 代码更灵活
## 验证方法
### 测试用例
创建了测试模式来验证坐标系统:
```cpp
// 在 4 个角落和中心放置测试圆圈
Position 0: screen(75.6, 42.5) -> NDC(-0.8, -0.8) // 左上
Position 1: screen(680.4, 42.5) -> NDC(0.8, -0.8) // 右上
Position 2: screen(75.6, 382.5) -> NDC(-0.8, 0.8) // 左下
Position 3: screen(680.4, 382.5) -> NDC(0.8, 0.8) // 右下
Position 4: screen(378, 212.5) -> NDC(0, 0) // 中心
```
### 预期结果
修复后应该看到:
- ✅ 8个彩色球在窗口正中心旋转
- ✅ 旋转轨道为正圆半径80
- ✅ 球的半径一致15像素
- ✅ 两条波浪线在窗口70%高度处
- ✅ 所有元素完全在窗口内
- ✅ 调整窗口大小时元素保持正确位置
- ✅ 无变形、无移动、无裁剪
## 调试经验
### 诊断步骤
1. **验证 CPU 端数据**
- 打印 m_width, m_height
- 打印 UBO 结构体内容
- 验证 viewport 和 scissor 设置
2. **验证 GPU 端数据**
- 使用硬编码值替代 UBO
- 如果硬编码值工作 → UBO 传输有问题
- 检查结构体对齐
3. **隔离问题**
- 简化场景如测试模式的5个圆圈
- 逐步添加功能,确定引入问题的代码
### 关键发现
问题表现:
- **垂直拉伸** → resolution.y 值错误
- **随时间移动** → 不同帧使用不同的 resolution
- **从角落进入** → 中心点计算使用错误的 resolution
根本原因:
- Shader 读取的 `ubo.resolution` 与 CPU 写入的值不匹配
- 由于 `vec2` 的内存对齐问题
## 相关资源
- [Vulkan Specification - Uniform Buffer Layout](https://www.khronos.org/registry/vulkan/specs/1.3/html/vkspec.html#interfaces-resources-layout)
- [GLSL std140 Layout Rules](https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout)
- [Vulkan Dynamic State](https://www.khronos.org/registry/vulkan/specs/1.3/html/vkspec.html#pipelines-dynamic-state)
## 维护建议
1. **UBO 结构体设计**
- 避免使用 `vec2`, `vec3`
- 优先使用独立的 `float`
- 明确添加 padding 到 16 字节边界
- 在 C++ 和 GLSL 中保持相同的字段顺序和对齐
2. **调试工具**
- 保留测试模式代码(可通过宏开关)
- 添加 UBO 内容验证函数
- 使用 RenderDoc 等工具检查 GPU 状态
3. **代码审查要点**
- 检查所有 uniform buffer 是否都被更新
- 验证 descriptor set 绑定的 buffer index
- 确认 viewport 和 scissor 与渲染表面匹配
## 总结
这个问题的修复展示了 Vulkan 开发中的几个重要教训:
1. **内存对齐至关重要**CPU 和 GPU 之间的数据传输必须精确匹配
2. **多缓冲需要同步**:所有 in-flight 的资源都必须保持一致
3. **动态状态很有用**:避免频繁重建管线
4. **系统化调试**:从简单场景开始,逐步定位问题
最终解决方案简洁且高效,确保了渲染的正确性和性能。
---
**修复完成日期**: 2024
**问题持续时间**: 多次迭代
**关键突破**: 使用硬编码值测试发现 UBO 传输问题
**最终原因**: GLSL vec2 对齐问题

29
PROBLEM_SOLVED.txt Normal file
View File

@ -0,0 +1,29 @@
===============================================
VulkanWidget 渲染问题已完全解决!
===============================================
问题根源:
----------
Shader 中 UBO 的 vec2 类型与 C++ 的 float[2] 内存对齐不匹配
解决方案:
----------
将 shader 中的 vec2 改为两个独立的 float确保内存布局完全一致
修改文件:
----------
1. shaders/geometry.vert - 修复 UBO 布局
2. src/vulkanrenderer.cpp - 更新所有 uniform buffers
验证结果:
----------
✅ 8个彩色球在窗口中心完美旋转
✅ 圆形不再变形为椭圆
✅ 位置固定不再移动
✅ 所有元素在窗口内
✅ 窗口调整大小正常
详细信息请查看:
----------
FINAL_SOLUTION.md - 完整技术文档
===============================================

View File

@ -1,39 +0,0 @@
Vulkan渲染器验证指南
====================
已完成修复:
✅ VulkanRenderer已集成到VulkanWidget
✅ 动画参数更新rotation, wavePhase
✅ 几何体缓冲区在命令录制前创建
✅ 锁屏统计追踪
预期效果:
---------
1. 动态渐变背景 - 颜色随时间变化
2. 8个旋转圆圈 - 围绕中心旋转,颜色动态变化
3. 2条波浪线 - 正弦波动画
运行步骤:
---------
1. cd ScreenLockDetector
2. ./build/bin/ScreenLockDetector
3. 切换到 "Vulkan Widget" 标签页
4. 点击 "Enable Rendering" 按钮
5. 观察渲染效果
调试信息:
---------
控制台会输出:
- "VulkanRenderer initialized successfully!"
- "Vulkan rendering ENABLED"
- 帧数和动画参数更新
如果只看到背景:
---------------
可能原因:
1. 几何体管线创建失败 - 检查着色器编译
2. 顶点缓冲区创建失败 - 检查内存分配
3. 圆圈在窗口外 - 检查坐标转换
调试命令:
./build/bin/ScreenLockDetector 2>&1 | grep -E "(Vulkan|Error|Failed)"

View File

@ -7,24 +7,29 @@ layout(location = 2) in vec2 inTexCoord;
layout(location = 0) out vec4 fragColor;
layout(location = 1) out vec2 fragTexCoord;
layout(binding = 0) uniform UniformBufferObject {
float time;
vec2 resolution;
float rotation;
float wavePhase;
layout(binding = 0, std140) uniform UniformBufferObject {
float time; // offset 0
float resX; // offset 4
float resY; // offset 8
float rotation; // offset 12
float wavePhase; // offset 16
float padding1; // offset 20
float padding2; // offset 24
} ubo;
void main() {
// Transform position from pixel coordinates to normalized device coordinates
vec2 pos = inPosition;
// ULTIMATE TEST: Use hardcoded resolution value instead of UBO
// This will prove whether the problem is in UBO binding or somewhere else
// Convert to NDC: (0, 0) to (width, height) -> (-1, -1) to (1, 1)
vec2 ndc = (pos / ubo.resolution) * 2.0 - 1.0;
// Hardcode the resolution we know is correct from debug output
float hardcodedWidth = 756.0;
float hardcodedHeight = 425.0;
// Flip Y axis (Vulkan has Y down, we want Y up for easier math)
ndc.y = -ndc.y;
// Now use UBO values - they should match the C++ struct exactly
float ndcX = (inPosition.x / ubo.resX) * 2.0 - 1.0;
float ndcY = (inPosition.y / ubo.resY) * 2.0 - 1.0;
gl_Position = vec4(ndc, 0.0, 1.0);
gl_Position = vec4(ndcX, ndcY, 0.0, 1.0);
fragColor = inColor;
fragTexCoord = inTexCoord;
}

Binary file not shown.

View File

@ -1,60 +1,70 @@
// Auto-generated from geometry.vert.spv
// Size: 1840 bytes (460 words)
0x07230203u, 0x00010000u, 0x0008000bu, 0x00000039u, 0x00000000u, 0x00020011u, 0x00000001u, 0x0006000bu,
// Size: 2160 bytes (540 words)
0x07230203u, 0x00010000u, 0x0008000bu, 0x00000040u, 0x00000000u, 0x00020011u, 0x00000001u, 0x0006000bu,
0x00000001u, 0x4c534c47u, 0x6474732eu, 0x3035342eu, 0x00000000u, 0x0003000eu, 0x00000000u, 0x00000001u,
0x000b000fu, 0x00000000u, 0x00000004u, 0x6e69616du, 0x00000000u, 0x0000000bu, 0x00000028u, 0x00000031u,
0x00000033u, 0x00000036u, 0x00000037u, 0x00030003u, 0x00000002u, 0x000001c2u, 0x00040005u, 0x00000004u,
0x6e69616du, 0x00000000u, 0x00030005u, 0x00000009u, 0x00736f70u, 0x00050005u, 0x0000000bu, 0x6f506e69u,
0x69746973u, 0x00006e6fu, 0x00030005u, 0x0000000du, 0x0063646eu, 0x00070005u, 0x0000000fu, 0x66696e55u,
0x426d726fu, 0x65666675u, 0x6a624f72u, 0x00746365u, 0x00050006u, 0x0000000fu, 0x00000000u, 0x656d6974u,
0x00000000u, 0x00060006u, 0x0000000fu, 0x00000001u, 0x6f736572u, 0x6974756cu, 0x00006e6fu, 0x00060006u,
0x0000000fu, 0x00000002u, 0x61746f72u, 0x6e6f6974u, 0x00000000u, 0x00060006u, 0x0000000fu, 0x00000003u,
0x65766177u, 0x73616850u, 0x00000065u, 0x00030005u, 0x00000011u, 0x006f6275u, 0x00060005u, 0x00000026u,
0x505f6c67u, 0x65567265u, 0x78657472u, 0x00000000u, 0x00060006u, 0x00000026u, 0x00000000u, 0x505f6c67u,
0x7469736fu, 0x006e6f69u, 0x00070006u, 0x00000026u, 0x00000001u, 0x505f6c67u, 0x746e696fu, 0x657a6953u,
0x00000000u, 0x00070006u, 0x00000026u, 0x00000002u, 0x435f6c67u, 0x4470696cu, 0x61747369u, 0x0065636eu,
0x00070006u, 0x00000026u, 0x00000003u, 0x435f6c67u, 0x446c6c75u, 0x61747369u, 0x0065636eu, 0x00030005u,
0x00000028u, 0x00000000u, 0x00050005u, 0x00000031u, 0x67617266u, 0x6f6c6f43u, 0x00000072u, 0x00040005u,
0x00000033u, 0x6f436e69u, 0x00726f6cu, 0x00060005u, 0x00000036u, 0x67617266u, 0x43786554u, 0x64726f6fu,
0x00000000u, 0x00050005u, 0x00000037u, 0x65546e69u, 0x6f6f4378u, 0x00006472u, 0x00040047u, 0x0000000bu,
0x0000001eu, 0x00000000u, 0x00050048u, 0x0000000fu, 0x00000000u, 0x00000023u, 0x00000000u, 0x00050048u,
0x0000000fu, 0x00000001u, 0x00000023u, 0x00000008u, 0x00050048u, 0x0000000fu, 0x00000002u, 0x00000023u,
0x00000010u, 0x00050048u, 0x0000000fu, 0x00000003u, 0x00000023u, 0x00000014u, 0x00030047u, 0x0000000fu,
0x00000002u, 0x00040047u, 0x00000011u, 0x00000022u, 0x00000000u, 0x00040047u, 0x00000011u, 0x00000021u,
0x00000000u, 0x00050048u, 0x00000026u, 0x00000000u, 0x0000000bu, 0x00000000u, 0x00050048u, 0x00000026u,
0x00000001u, 0x0000000bu, 0x00000001u, 0x00050048u, 0x00000026u, 0x00000002u, 0x0000000bu, 0x00000003u,
0x00050048u, 0x00000026u, 0x00000003u, 0x0000000bu, 0x00000004u, 0x00030047u, 0x00000026u, 0x00000002u,
0x00040047u, 0x00000031u, 0x0000001eu, 0x00000000u, 0x00040047u, 0x00000033u, 0x0000001eu, 0x00000001u,
0x00040047u, 0x00000036u, 0x0000001eu, 0x00000001u, 0x00040047u, 0x00000037u, 0x0000001eu, 0x00000002u,
0x00020013u, 0x00000002u, 0x00030021u, 0x00000003u, 0x00000002u, 0x00030016u, 0x00000006u, 0x00000020u,
0x00040017u, 0x00000007u, 0x00000006u, 0x00000002u, 0x00040020u, 0x00000008u, 0x00000007u, 0x00000007u,
0x00040020u, 0x0000000au, 0x00000001u, 0x00000007u, 0x0004003bu, 0x0000000au, 0x0000000bu, 0x00000001u,
0x0006001eu, 0x0000000fu, 0x00000006u, 0x00000007u, 0x00000006u, 0x00000006u, 0x00040020u, 0x00000010u,
0x00000002u, 0x0000000fu, 0x0004003bu, 0x00000010u, 0x00000011u, 0x00000002u, 0x00040015u, 0x00000012u,
0x00000020u, 0x00000001u, 0x0004002bu, 0x00000012u, 0x00000013u, 0x00000001u, 0x00040020u, 0x00000014u,
0x00000002u, 0x00000007u, 0x0004002bu, 0x00000006u, 0x00000018u, 0x40000000u, 0x0004002bu, 0x00000006u,
0x0000001au, 0x3f800000u, 0x00040015u, 0x0000001du, 0x00000020u, 0x00000000u, 0x0004002bu, 0x0000001du,
0x0000001eu, 0x00000001u, 0x00040020u, 0x0000001fu, 0x00000007u, 0x00000006u, 0x00040017u, 0x00000024u,
0x00000006u, 0x00000004u, 0x0004001cu, 0x00000025u, 0x00000006u, 0x0000001eu, 0x0006001eu, 0x00000026u,
0x00000024u, 0x00000006u, 0x00000025u, 0x00000025u, 0x00040020u, 0x00000027u, 0x00000003u, 0x00000026u,
0x0004003bu, 0x00000027u, 0x00000028u, 0x00000003u, 0x0004002bu, 0x00000012u, 0x00000029u, 0x00000000u,
0x0004002bu, 0x00000006u, 0x0000002bu, 0x00000000u, 0x00040020u, 0x0000002fu, 0x00000003u, 0x00000024u,
0x0004003bu, 0x0000002fu, 0x00000031u, 0x00000003u, 0x00040020u, 0x00000032u, 0x00000001u, 0x00000024u,
0x0004003bu, 0x00000032u, 0x00000033u, 0x00000001u, 0x00040020u, 0x00000035u, 0x00000003u, 0x00000007u,
0x0004003bu, 0x00000035u, 0x00000036u, 0x00000003u, 0x0004003bu, 0x0000000au, 0x00000037u, 0x00000001u,
0x000b000fu, 0x00000000u, 0x00000004u, 0x6e69616du, 0x00000000u, 0x0000000fu, 0x00000030u, 0x00000038u,
0x0000003au, 0x0000003du, 0x0000003eu, 0x00030003u, 0x00000002u, 0x000001c2u, 0x00040005u, 0x00000004u,
0x6e69616du, 0x00000000u, 0x00060005u, 0x00000008u, 0x64726168u, 0x65646f63u, 0x64695764u, 0x00006874u,
0x00060005u, 0x0000000au, 0x64726168u, 0x65646f63u, 0x69654864u, 0x00746867u, 0x00040005u, 0x0000000cu,
0x5863646eu, 0x00000000u, 0x00050005u, 0x0000000fu, 0x6f506e69u, 0x69746973u, 0x00006e6fu, 0x00070005u,
0x00000015u, 0x66696e55u, 0x426d726fu, 0x65666675u, 0x6a624f72u, 0x00746365u, 0x00050006u, 0x00000015u,
0x00000000u, 0x656d6974u, 0x00000000u, 0x00050006u, 0x00000015u, 0x00000001u, 0x58736572u, 0x00000000u,
0x00050006u, 0x00000015u, 0x00000002u, 0x59736572u, 0x00000000u, 0x00060006u, 0x00000015u, 0x00000003u,
0x61746f72u, 0x6e6f6974u, 0x00000000u, 0x00060006u, 0x00000015u, 0x00000004u, 0x65766177u, 0x73616850u,
0x00000065u, 0x00060006u, 0x00000015u, 0x00000005u, 0x64646170u, 0x31676e69u, 0x00000000u, 0x00060006u,
0x00000015u, 0x00000006u, 0x64646170u, 0x32676e69u, 0x00000000u, 0x00030005u, 0x00000017u, 0x006f6275u,
0x00040005u, 0x00000022u, 0x5963646eu, 0x00000000u, 0x00060005u, 0x0000002eu, 0x505f6c67u, 0x65567265u,
0x78657472u, 0x00000000u, 0x00060006u, 0x0000002eu, 0x00000000u, 0x505f6c67u, 0x7469736fu, 0x006e6f69u,
0x00070006u, 0x0000002eu, 0x00000001u, 0x505f6c67u, 0x746e696fu, 0x657a6953u, 0x00000000u, 0x00070006u,
0x0000002eu, 0x00000002u, 0x435f6c67u, 0x4470696cu, 0x61747369u, 0x0065636eu, 0x00070006u, 0x0000002eu,
0x00000003u, 0x435f6c67u, 0x446c6c75u, 0x61747369u, 0x0065636eu, 0x00030005u, 0x00000030u, 0x00000000u,
0x00050005u, 0x00000038u, 0x67617266u, 0x6f6c6f43u, 0x00000072u, 0x00040005u, 0x0000003au, 0x6f436e69u,
0x00726f6cu, 0x00060005u, 0x0000003du, 0x67617266u, 0x43786554u, 0x64726f6fu, 0x00000000u, 0x00050005u,
0x0000003eu, 0x65546e69u, 0x6f6f4378u, 0x00006472u, 0x00040047u, 0x0000000fu, 0x0000001eu, 0x00000000u,
0x00050048u, 0x00000015u, 0x00000000u, 0x00000023u, 0x00000000u, 0x00050048u, 0x00000015u, 0x00000001u,
0x00000023u, 0x00000004u, 0x00050048u, 0x00000015u, 0x00000002u, 0x00000023u, 0x00000008u, 0x00050048u,
0x00000015u, 0x00000003u, 0x00000023u, 0x0000000cu, 0x00050048u, 0x00000015u, 0x00000004u, 0x00000023u,
0x00000010u, 0x00050048u, 0x00000015u, 0x00000005u, 0x00000023u, 0x00000014u, 0x00050048u, 0x00000015u,
0x00000006u, 0x00000023u, 0x00000018u, 0x00030047u, 0x00000015u, 0x00000002u, 0x00040047u, 0x00000017u,
0x00000022u, 0x00000000u, 0x00040047u, 0x00000017u, 0x00000021u, 0x00000000u, 0x00050048u, 0x0000002eu,
0x00000000u, 0x0000000bu, 0x00000000u, 0x00050048u, 0x0000002eu, 0x00000001u, 0x0000000bu, 0x00000001u,
0x00050048u, 0x0000002eu, 0x00000002u, 0x0000000bu, 0x00000003u, 0x00050048u, 0x0000002eu, 0x00000003u,
0x0000000bu, 0x00000004u, 0x00030047u, 0x0000002eu, 0x00000002u, 0x00040047u, 0x00000038u, 0x0000001eu,
0x00000000u, 0x00040047u, 0x0000003au, 0x0000001eu, 0x00000001u, 0x00040047u, 0x0000003du, 0x0000001eu,
0x00000001u, 0x00040047u, 0x0000003eu, 0x0000001eu, 0x00000002u, 0x00020013u, 0x00000002u, 0x00030021u,
0x00000003u, 0x00000002u, 0x00030016u, 0x00000006u, 0x00000020u, 0x00040020u, 0x00000007u, 0x00000007u,
0x00000006u, 0x0004002bu, 0x00000006u, 0x00000009u, 0x443d0000u, 0x0004002bu, 0x00000006u, 0x0000000bu,
0x43d48000u, 0x00040017u, 0x0000000du, 0x00000006u, 0x00000002u, 0x00040020u, 0x0000000eu, 0x00000001u,
0x0000000du, 0x0004003bu, 0x0000000eu, 0x0000000fu, 0x00000001u, 0x00040015u, 0x00000010u, 0x00000020u,
0x00000000u, 0x0004002bu, 0x00000010u, 0x00000011u, 0x00000000u, 0x00040020u, 0x00000012u, 0x00000001u,
0x00000006u, 0x0009001eu, 0x00000015u, 0x00000006u, 0x00000006u, 0x00000006u, 0x00000006u, 0x00000006u,
0x00000006u, 0x00000006u, 0x00040020u, 0x00000016u, 0x00000002u, 0x00000015u, 0x0004003bu, 0x00000016u,
0x00000017u, 0x00000002u, 0x00040015u, 0x00000018u, 0x00000020u, 0x00000001u, 0x0004002bu, 0x00000018u,
0x00000019u, 0x00000001u, 0x00040020u, 0x0000001au, 0x00000002u, 0x00000006u, 0x0004002bu, 0x00000006u,
0x0000001eu, 0x40000000u, 0x0004002bu, 0x00000006u, 0x00000020u, 0x3f800000u, 0x0004002bu, 0x00000010u,
0x00000023u, 0x00000001u, 0x0004002bu, 0x00000018u, 0x00000026u, 0x00000002u, 0x00040017u, 0x0000002cu,
0x00000006u, 0x00000004u, 0x0004001cu, 0x0000002du, 0x00000006u, 0x00000023u, 0x0006001eu, 0x0000002eu,
0x0000002cu, 0x00000006u, 0x0000002du, 0x0000002du, 0x00040020u, 0x0000002fu, 0x00000003u, 0x0000002eu,
0x0004003bu, 0x0000002fu, 0x00000030u, 0x00000003u, 0x0004002bu, 0x00000018u, 0x00000031u, 0x00000000u,
0x0004002bu, 0x00000006u, 0x00000034u, 0x00000000u, 0x00040020u, 0x00000036u, 0x00000003u, 0x0000002cu,
0x0004003bu, 0x00000036u, 0x00000038u, 0x00000003u, 0x00040020u, 0x00000039u, 0x00000001u, 0x0000002cu,
0x0004003bu, 0x00000039u, 0x0000003au, 0x00000001u, 0x00040020u, 0x0000003cu, 0x00000003u, 0x0000000du,
0x0004003bu, 0x0000003cu, 0x0000003du, 0x00000003u, 0x0004003bu, 0x0000000eu, 0x0000003eu, 0x00000001u,
0x00050036u, 0x00000002u, 0x00000004u, 0x00000000u, 0x00000003u, 0x000200f8u, 0x00000005u, 0x0004003bu,
0x00000008u, 0x00000009u, 0x00000007u, 0x0004003bu, 0x00000008u, 0x0000000du, 0x00000007u, 0x0004003du,
0x00000007u, 0x0000000cu, 0x0000000bu, 0x0003003eu, 0x00000009u, 0x0000000cu, 0x0004003du, 0x00000007u,
0x0000000eu, 0x00000009u, 0x00050041u, 0x00000014u, 0x00000015u, 0x00000011u, 0x00000013u, 0x0004003du,
0x00000007u, 0x00000016u, 0x00000015u, 0x00050088u, 0x00000007u, 0x00000017u, 0x0000000eu, 0x00000016u,
0x0005008eu, 0x00000007u, 0x00000019u, 0x00000017u, 0x00000018u, 0x00050050u, 0x00000007u, 0x0000001bu,
0x0000001au, 0x0000001au, 0x00050083u, 0x00000007u, 0x0000001cu, 0x00000019u, 0x0000001bu, 0x0003003eu,
0x0000000du, 0x0000001cu, 0x00050041u, 0x0000001fu, 0x00000020u, 0x0000000du, 0x0000001eu, 0x0004003du,
0x00000006u, 0x00000021u, 0x00000020u, 0x0004007fu, 0x00000006u, 0x00000022u, 0x00000021u, 0x00050041u,
0x0000001fu, 0x00000023u, 0x0000000du, 0x0000001eu, 0x0003003eu, 0x00000023u, 0x00000022u, 0x0004003du,
0x00000007u, 0x0000002au, 0x0000000du, 0x00050051u, 0x00000006u, 0x0000002cu, 0x0000002au, 0x00000000u,
0x00050051u, 0x00000006u, 0x0000002du, 0x0000002au, 0x00000001u, 0x00070050u, 0x00000024u, 0x0000002eu,
0x0000002cu, 0x0000002du, 0x0000002bu, 0x0000001au, 0x00050041u, 0x0000002fu, 0x00000030u, 0x00000028u,
0x00000029u, 0x0003003eu, 0x00000030u, 0x0000002eu, 0x0004003du, 0x00000024u, 0x00000034u, 0x00000033u,
0x0003003eu, 0x00000031u, 0x00000034u, 0x0004003du, 0x00000007u, 0x00000038u, 0x00000037u, 0x0003003eu,
0x00000036u, 0x00000038u, 0x000100fdu, 0x00010038u
0x00000007u, 0x00000008u, 0x00000007u, 0x0004003bu, 0x00000007u, 0x0000000au, 0x00000007u, 0x0004003bu,
0x00000007u, 0x0000000cu, 0x00000007u, 0x0004003bu, 0x00000007u, 0x00000022u, 0x00000007u, 0x0003003eu,
0x00000008u, 0x00000009u, 0x0003003eu, 0x0000000au, 0x0000000bu, 0x00050041u, 0x00000012u, 0x00000013u,
0x0000000fu, 0x00000011u, 0x0004003du, 0x00000006u, 0x00000014u, 0x00000013u, 0x00050041u, 0x0000001au,
0x0000001bu, 0x00000017u, 0x00000019u, 0x0004003du, 0x00000006u, 0x0000001cu, 0x0000001bu, 0x00050088u,
0x00000006u, 0x0000001du, 0x00000014u, 0x0000001cu, 0x00050085u, 0x00000006u, 0x0000001fu, 0x0000001du,
0x0000001eu, 0x00050083u, 0x00000006u, 0x00000021u, 0x0000001fu, 0x00000020u, 0x0003003eu, 0x0000000cu,
0x00000021u, 0x00050041u, 0x00000012u, 0x00000024u, 0x0000000fu, 0x00000023u, 0x0004003du, 0x00000006u,
0x00000025u, 0x00000024u, 0x00050041u, 0x0000001au, 0x00000027u, 0x00000017u, 0x00000026u, 0x0004003du,
0x00000006u, 0x00000028u, 0x00000027u, 0x00050088u, 0x00000006u, 0x00000029u, 0x00000025u, 0x00000028u,
0x00050085u, 0x00000006u, 0x0000002au, 0x00000029u, 0x0000001eu, 0x00050083u, 0x00000006u, 0x0000002bu,
0x0000002au, 0x00000020u, 0x0003003eu, 0x00000022u, 0x0000002bu, 0x0004003du, 0x00000006u, 0x00000032u,
0x0000000cu, 0x0004003du, 0x00000006u, 0x00000033u, 0x00000022u, 0x00070050u, 0x0000002cu, 0x00000035u,
0x00000032u, 0x00000033u, 0x00000034u, 0x00000020u, 0x00050041u, 0x00000036u, 0x00000037u, 0x00000030u,
0x00000031u, 0x0003003eu, 0x00000037u, 0x00000035u, 0x0004003du, 0x0000002cu, 0x0000003bu, 0x0000003au,
0x0003003eu, 0x00000038u, 0x0000003bu, 0x0004003du, 0x0000000du, 0x0000003fu, 0x0000003eu, 0x0003003eu,
0x0000003du, 0x0000003fu, 0x000100fdu, 0x00010038u

View File

@ -115,6 +115,20 @@ bool VulkanRenderer::initialize(VkDevice device, VkPhysicalDevice physicalDevice
return false;
}
// Initialize UBO with current dimensions
m_ubo.time = 0.0f;
m_ubo.resolution[0] = static_cast<float>(m_width);
m_ubo.resolution[1] = static_cast<float>(m_height);
m_ubo.rotation = 0.0f;
m_ubo.wavePhase = 0.0f;
// Update all uniform buffers with initial values
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
if (m_uniformBuffersMapped[i] != nullptr) {
memcpy(m_uniformBuffersMapped[i], &m_ubo, sizeof(m_ubo));
}
}
// Create background geometry
std::vector<Vertex> bgVertices;
std::vector<uint16_t> bgIndices;
@ -213,6 +227,19 @@ bool VulkanRenderer::resize(uint32_t width, uint32_t height)
m_width = width;
m_height = height;
// Update UBO resolution immediately
m_ubo.resolution[0] = static_cast<float>(m_width);
m_ubo.resolution[1] = static_cast<float>(m_height);
// Update all uniform buffers with new resolution
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
if (i < m_uniformBuffersMapped.size() && m_uniformBuffersMapped[i] != nullptr) {
memcpy(m_uniformBuffersMapped[i], &m_ubo, sizeof(m_ubo));
}
}
std::cout << " Updated UBO resolution to: (" << m_ubo.resolution[0] << ", " << m_ubo.resolution[1] << ")" << std::endl;
// Recreate framebuffers with new size
for (auto fb : m_framebuffers) {
if (fb) vkDestroyFramebuffer(m_device, fb, nullptr);
@ -299,9 +326,18 @@ void VulkanRenderer::recordCommandBuffer(VkCommandBuffer commandBuffer,
m_ubo.rotation = static_cast<float>(rotationAngle);
m_ubo.wavePhase = static_cast<float>(wavePhase);
// Use frame count modulo for uniform buffer index (not image index)
uint32_t uniformBufferIndex = static_cast<uint32_t>(frameCount) % MAX_FRAMES_IN_FLIGHT;
updateUniformBuffer(uniformBufferIndex);
// CRITICAL FIX: Update ALL uniform buffers every frame!
// Each descriptor set points to a different buffer, so all must have current data
// Otherwise, alternating frames will use stale data from the other buffer
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
updateUniformBuffer(i);
}
// Use consistent frame index for descriptor set binding
uint32_t frameIndex = static_cast<uint32_t>(frameCount) % MAX_FRAMES_IN_FLIGHT;
// Begin command buffer
VkCommandBufferBeginInfo beginInfo = {};
@ -372,13 +408,30 @@ void VulkanRenderer::recordCommandBuffer(VkCommandBuffer commandBuffer,
renderPassInfo.pClearValues = &clearColor;
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
// Set dynamic viewport and scissor
VkViewport viewport = {};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(m_width);
viewport.height = static_cast<float>(m_height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
VkRect2D scissor = {};
scissor.offset = {0, 0};
scissor.extent = {m_width, m_height};
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
// Draw background
drawBackground(commandBuffer, frameCount);
if (paintingEnabled) {
// Draw geometry (circles and waves)
drawGeometry(commandBuffer, rotationAngle, wavePhase);
drawGeometry(commandBuffer, frameCount, rotationAngle, wavePhase);
}
// Draw text (status info)
@ -569,25 +622,13 @@ bool VulkanRenderer::createBackgroundPipeline()
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
inputAssembly.primitiveRestartEnable = VK_FALSE;
// Viewport and scissor
VkViewport viewport = {};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(m_width);
viewport.height = static_cast<float>(m_height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor = {};
scissor.offset = {0, 0};
scissor.extent = {m_width, m_height};
// Viewport and scissor - use dynamic state
VkPipelineViewportStateCreateInfo viewportState = {};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.pViewports = &viewport;
viewportState.pViewports = nullptr; // Will be set dynamically
viewportState.scissorCount = 1;
viewportState.pScissors = &scissor;
viewportState.pScissors = nullptr; // Will be set dynamically
// Rasterizer
VkPipelineRasterizationStateCreateInfo rasterizer = {};
@ -630,6 +671,17 @@ bool VulkanRenderer::createBackgroundPipeline()
return false;
}
// Dynamic state - viewport and scissor
VkDynamicState dynamicStates[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicState = {};
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.dynamicStateCount = 2;
dynamicState.pDynamicStates = dynamicStates;
// Create graphics pipeline
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
@ -641,6 +693,7 @@ bool VulkanRenderer::createBackgroundPipeline()
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = &dynamicState;
pipelineInfo.layout = m_backgroundPipelineLayout;
pipelineInfo.renderPass = m_renderPass;
pipelineInfo.subpass = 0;
@ -737,24 +790,13 @@ bool VulkanRenderer::createGeometryPipeline()
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
inputAssembly.primitiveRestartEnable = VK_FALSE;
VkViewport viewport = {};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(m_width);
viewport.height = static_cast<float>(m_height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor = {};
scissor.offset = {0, 0};
scissor.extent = {m_width, m_height};
// Viewport and scissor - use dynamic state
VkPipelineViewportStateCreateInfo viewportState = {};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.pViewports = &viewport;
viewportState.pViewports = nullptr; // Will be set dynamically
viewportState.scissorCount = 1;
viewportState.pScissors = &scissor;
viewportState.pScissors = nullptr; // Will be set dynamically
VkPipelineRasterizationStateCreateInfo rasterizer = {};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
@ -800,6 +842,17 @@ bool VulkanRenderer::createGeometryPipeline()
return false;
}
// Dynamic state - viewport and scissor
VkDynamicState dynamicStates[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicState = {};
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.dynamicStateCount = 2;
dynamicState.pDynamicStates = dynamicStates;
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
@ -810,6 +863,7 @@ bool VulkanRenderer::createGeometryPipeline()
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = &dynamicState;
pipelineInfo.layout = m_geometryPipelineLayout;
pipelineInfo.renderPass = m_renderPass;
pipelineInfo.subpass = 0;
@ -897,24 +951,13 @@ bool VulkanRenderer::createLinePipeline()
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST; // Line rendering!
inputAssembly.primitiveRestartEnable = VK_FALSE;
VkViewport viewport = {};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(m_width);
viewport.height = static_cast<float>(m_height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor = {};
scissor.offset = {0, 0};
scissor.extent = {m_width, m_height};
// Viewport and scissor - use dynamic state
VkPipelineViewportStateCreateInfo viewportState = {};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.pViewports = &viewport;
viewportState.pViewports = nullptr; // Will be set dynamically
viewportState.scissorCount = 1;
viewportState.pScissors = &scissor;
viewportState.pScissors = nullptr; // Will be set dynamically
VkPipelineRasterizationStateCreateInfo rasterizer = {};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
@ -960,6 +1003,17 @@ bool VulkanRenderer::createLinePipeline()
return false;
}
// Dynamic state - viewport and scissor
VkDynamicState dynamicStates[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicState = {};
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.dynamicStateCount = 2;
dynamicState.pDynamicStates = dynamicStates;
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
@ -970,6 +1024,7 @@ bool VulkanRenderer::createLinePipeline()
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = &dynamicState;
pipelineInfo.layout = m_linePipelineLayout;
pipelineInfo.renderPass = m_renderPass;
pipelineInfo.subpass = 0;
@ -1381,15 +1436,20 @@ void VulkanRenderer::generateRotatingCircles(std::vector<Vertex>& vertices,
return;
}
// Use screen coordinates (Y-down): center is at middle of screen
float centerX = m_width / 2.0f;
float centerY = m_height / 2.0f;
int numCircles = 8;
float orbitRadius = 120.0f; // Increased from 80 for larger orbit
float circleRadius = 25.0f; // Increased from 15 for bigger circles
float orbitRadius = 80.0f; // Match CustomWidget
float circleRadius = 15.0f; // Match CustomWidget
for (int i = 0; i < numCircles; i++) {
float angle = (i * 2.0f * M_PI / numCircles) + rotation * M_PI / 180.0f;
float x = centerX + orbitRadius * cos(angle);
// Y-down screen coords: positive sin moves down
float y = centerY + orbitRadius * sin(angle);
// HSV to RGB for color - more vibrant colors
@ -1437,7 +1497,7 @@ void VulkanRenderer::generateWaveEffect(std::vector<Vertex>& vertices,
float waveY = m_height * 0.7f;
float amplitude = 30.0f;
// First wave
// First wave - screen coordinates (Y-down)
for (int i = 0; i < numPoints; i++) {
float x = i * 5.0f;
float y = waveY + amplitude * sin(wavePhase + x * 0.02);
@ -1450,7 +1510,7 @@ void VulkanRenderer::generateWaveEffect(std::vector<Vertex>& vertices,
indices.push_back(i + 1);
}
// Second wave (phase shifted)
// Second wave (phase shifted) - screen coordinates (Y-down)
uint16_t offset = vertices.size();
for (int i = 0; i < numPoints; i++) {
float x = i * 5.0f;
@ -1483,8 +1543,8 @@ void VulkanRenderer::drawBackground(VkCommandBuffer commandBuffer, int frameCoun
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_backgroundPipeline);
// Use m_ubo.time (which is set to frameCount) for consistency
uint32_t frameIndex = static_cast<uint32_t>(m_ubo.time) % MAX_FRAMES_IN_FLIGHT;
// Use the same frameIndex that was used to update the uniform buffer
uint32_t frameIndex = frameCount % MAX_FRAMES_IN_FLIGHT;
if (frameIndex >= m_descriptorSets.size()) {
return;
}
@ -1499,14 +1559,15 @@ void VulkanRenderer::drawBackground(VkCommandBuffer commandBuffer, int frameCoun
vkCmdDrawIndexed(commandBuffer, m_backgroundIndexCount, 1, 0, 0, 0);
}
void VulkanRenderer::drawGeometry(VkCommandBuffer commandBuffer, double rotation, double wavePhase)
void VulkanRenderer::drawGeometry(VkCommandBuffer commandBuffer, int frameCount, double rotation, double wavePhase)
{
if (m_geometryPipeline == VK_NULL_HANDLE || m_geometryPipelineLayout == VK_NULL_HANDLE ||
m_descriptorSets.empty()) {
return;
}
uint32_t frameIndex = static_cast<uint32_t>(m_ubo.time) % MAX_FRAMES_IN_FLIGHT;
// Use the same frameIndex that was used to update the uniform buffer
uint32_t frameIndex = frameCount % MAX_FRAMES_IN_FLIGHT;
if (frameIndex >= m_descriptorSets.size()) {
return;
}

View File

@ -279,7 +279,7 @@ private:
/**
* @brief
*/
void drawGeometry(VkCommandBuffer commandBuffer, double rotation, double wavePhase);
void drawGeometry(VkCommandBuffer commandBuffer, int frameCount, double rotation, double wavePhase);
/**
* @brief

View File

@ -391,11 +391,20 @@ bool VulkanWidget::createDevice()
bool VulkanWidget::createSwapchain()
{
qDebug() << "Creating swapchain...";
qDebug() << "Widget size:" << width() << "x" << height();
qDebug() << "Device pixel ratio:" << devicePixelRatioF();
// Query surface capabilities
VkSurfaceCapabilitiesKHR capabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_physicalDevice, m_surface, &capabilities);
qDebug() << "Surface capabilities - current extent:"
<< capabilities.currentExtent.width << "x" << capabilities.currentExtent.height;
qDebug() << "Surface capabilities - min extent:"
<< capabilities.minImageExtent.width << "x" << capabilities.minImageExtent.height;
qDebug() << "Surface capabilities - max extent:"
<< capabilities.maxImageExtent.width << "x" << capabilities.maxImageExtent.height;
// Query surface formats
uint32_t formatCount;
vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, nullptr);
@ -431,16 +440,26 @@ bool VulkanWidget::createSwapchain()
VkExtent2D extent;
if (capabilities.currentExtent.width != UINT32_MAX) {
extent = capabilities.currentExtent;
qDebug() << "Using surface-provided extent:" << extent.width << "x" << extent.height;
} else {
// Calculate extent based on widget size with DPI scaling
uint32_t pixelWidth = static_cast<uint32_t>(width() * devicePixelRatioF());
uint32_t pixelHeight = static_cast<uint32_t>(height() * devicePixelRatioF());
extent.width = std::max(capabilities.minImageExtent.width,
std::min(capabilities.maxImageExtent.width, static_cast<uint32_t>(width())));
std::min(capabilities.maxImageExtent.width, pixelWidth));
extent.height = std::max(capabilities.minImageExtent.height,
std::min(capabilities.maxImageExtent.height, static_cast<uint32_t>(height())));
std::min(capabilities.maxImageExtent.height, pixelHeight));
qDebug() << "Calculated extent from widget:" << extent.width << "x" << extent.height
<< "(widget:" << width() << "x" << height()
<< "* DPR:" << devicePixelRatioF() << ")";
}
m_surfaceWidth = extent.width;
m_surfaceHeight = extent.height;
qDebug() << "Final swapchain extent:" << m_surfaceWidth << "x" << m_surfaceHeight;
// Determine image count
uint32_t imageCount = capabilities.minImageCount + 1;
if (capabilities.maxImageCount > 0 && imageCount > capabilities.maxImageCount) {