diff --git a/CMakeLists.txt b/CMakeLists.txt index d4eb9df..98ea924 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -128,8 +128,14 @@ set(HEADERS # Add Vulkan widget if Vulkan is available if(VULKAN_FOUND) - list(APPEND SOURCES src/vulkanwidget.cpp) - list(APPEND HEADERS src/vulkanwidget.h) + list(APPEND SOURCES + src/vulkanwidget.cpp + src/vulkanrenderer.cpp + ) + list(APPEND HEADERS + src/vulkanwidget.h + src/vulkanrenderer.h + ) add_definitions(-DENABLE_VULKAN_WIDGET) endif() diff --git a/VERIFY.txt b/VERIFY.txt new file mode 100644 index 0000000..9440fe1 --- /dev/null +++ b/VERIFY.txt @@ -0,0 +1,39 @@ +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)" diff --git a/compile_shaders.py b/compile_shaders.py new file mode 100755 index 0000000..7f09659 --- /dev/null +++ b/compile_shaders.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +""" +Shader compilation script for Vulkan SPIR-V shaders +""" + +import os +import struct +import subprocess +import sys + + +def find_glsl_compiler(): + home = os.path.expanduser("~") + custom_paths = [ + os.path.join(home, "sdk/glslang-13.0.0/bin/glslang"), + os.path.join(home, "sdk/glslang-13.0.0/bin/glslangValidator"), + ] + + for compiler_path in custom_paths: + if os.path.exists(compiler_path) and os.access(compiler_path, os.X_OK): + print("Found custom compiler: " + compiler_path) + return compiler_path + + compilers = ["glslc", "glslangValidator", "glslang"] + for compiler in compilers: + try: + subprocess.check_output([compiler, "--version"], stderr=subprocess.STDOUT) + return compiler + except (subprocess.CalledProcessError, FileNotFoundError, OSError): + continue + return None + + +def compile_shader(compiler, shader_file, output_file): + print("Compiling " + shader_file + " -> " + output_file) + + env = os.environ.copy() + if "/usr/local/lib64" not in env.get("LD_LIBRARY_PATH", ""): + env["LD_LIBRARY_PATH"] = "/usr/local/lib64:" + env.get("LD_LIBRARY_PATH", "") + + compiler_name = os.path.basename(compiler) + + try: + if compiler_name == "glslc": + cmd = [compiler, shader_file, "-o", output_file] + else: + cmd = [compiler, "-V", shader_file, "-o", output_file] + + subprocess.check_output(cmd, stderr=subprocess.STDOUT, env=env) + print(" SUCCESS") + return True + except subprocess.CalledProcessError as e: + print(" FAILED: " + e.output.decode('utf-8', errors='ignore')) + return False + except (FileNotFoundError, OSError): + print(" FAILED: Compiler not found") + return False + + +def generate_cpp_include(spirv_file, output_inc): + with open(spirv_file, "rb") as f: + spirv_data = f.read() + + num_words = len(spirv_data) // 4 + if len(spirv_data) % 4 != 0: + spirv_data += b"\x00" * (4 - len(spirv_data) % 4) + num_words = len(spirv_data) // 4 + + words = struct.unpack("<{}I".format(num_words), spirv_data) + + cpp_code = "// Auto-generated from {}\n".format(os.path.basename(spirv_file)) + cpp_code += "// Size: {} bytes ({} words)\n".format(len(spirv_data), num_words) + + for i in range(0, num_words, 8): + chunk = words[i : i + 8] + if i == 0: + cpp_code += " " + else: + cpp_code += ",\n " + cpp_code += ", ".join("0x{:08x}u".format(w) for w in chunk) + + with open(output_inc, "w") as f: + f.write(cpp_code + "\n") + + print(" Generated " + output_inc) + + +def main(): + script_dir = os.path.dirname(os.path.abspath(__file__)) + shaders_dir = os.path.join(script_dir, "shaders") + spirv_dir = os.path.join(script_dir, "src", "shaders_spirv") + + os.makedirs(spirv_dir, exist_ok=True) + + compiler = find_glsl_compiler() + if not compiler: + print("ERROR: No GLSL compiler found!") + print("Or install to: ~/sdk/glslang-13.0.0/bin/glslang") + sys.exit(1) + + print("Using {} compiler\n".format(compiler)) + print("=" * 60) + + shaders = [ + ("background.vert", "background_vert.inc"), + ("background.frag", "background_frag.inc"), + ("geometry.vert", "geometry_vert.inc"), + ("geometry.frag", "geometry_frag.inc"), + ("text.vert", "text_vert.inc"), + ("text.frag", "text_frag.inc"), + ] + + success_count = 0 + fail_count = 0 + + for shader_name, inc_name in shaders: + shader_path = os.path.join(shaders_dir, shader_name) + spirv_path = os.path.join(spirv_dir, shader_name + ".spv") + inc_path = os.path.join(spirv_dir, inc_name) + + if not os.path.exists(shader_path): + print("WARNING: {} not found, skipping".format(shader_path)) + fail_count += 1 + continue + + if compile_shader(compiler, shader_path, spirv_path): + generate_cpp_include(spirv_path, inc_path) + success_count += 1 + else: + fail_count += 1 + + print("=" * 60) + print("\nCompilation complete: {} succeeded, {} failed".format(success_count, fail_count)) + + if fail_count > 0: + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/compile_shaders.sh b/compile_shaders.sh new file mode 100755 index 0000000..085194a --- /dev/null +++ b/compile_shaders.sh @@ -0,0 +1,177 @@ +#!/bin/bash + +# Shader compilation script for Vulkan +# Sets up environment and compiles GLSL shaders to SPIR-V + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +SHADERS_DIR="$SCRIPT_DIR/shaders" +OUTPUT_DIR="$SCRIPT_DIR/src/shaders_spirv" + +# Set up library path for custom libstdc++ +export LD_LIBRARY_PATH="/usr/local/lib64:$LD_LIBRARY_PATH" + +# Find glslang compiler +GLSLANG="" + +# Check custom paths first +if [ -x "$HOME/sdk/glslang-13.0.0/bin/glslang" ]; then + GLSLANG="$HOME/sdk/glslang-13.0.0/bin/glslang" + echo "Found glslang at: $GLSLANG" +elif [ -x "$HOME/sdk/glslang-13.0.0/bin/glslangValidator" ]; then + GLSLANG="$HOME/sdk/glslang-13.0.0/bin/glslangValidator" + echo "Found glslangValidator at: $GLSLANG" +elif command -v glslc &> /dev/null; then + GLSLANG="glslc" + echo "Using system glslc" +elif command -v glslangValidator &> /dev/null; then + GLSLANG="glslangValidator" + echo "Using system glslangValidator" +elif command -v glslang &> /dev/null; then + GLSLANG="glslang" + echo "Using system glslang" +else + echo "ERROR: No GLSL compiler found!" + echo "Please install:" + echo " - Vulkan SDK (provides glslc)" + echo " - or glslang to ~/sdk/glslang-13.0.0/bin/" + exit 1 +fi + +# Create output directory +mkdir -p "$OUTPUT_DIR" + +# Compilation function +compile_shader() { + local shader_file="$1" + local output_name="$2" + local spirv_file="$OUTPUT_DIR/${shader_file}.spv" + local inc_file="$OUTPUT_DIR/${output_name}" + + echo "Compiling $shader_file..." + + # Determine compiler command + if [[ "$GLSLANG" == *"glslc"* ]]; then + "$GLSLANG" "$SHADERS_DIR/$shader_file" -o "$spirv_file" + else + "$GLSLANG" -V "$SHADERS_DIR/$shader_file" -o "$spirv_file" + fi + + if [ $? -ne 0 ]; then + echo " FAILED to compile $shader_file" + return 1 + fi + + echo " SUCCESS - Generated $spirv_file" + + # Generate C++ include file + echo " Generating $inc_file..." + generate_include "$spirv_file" "$inc_file" + + return 0 +} + +# Generate C++ include file from SPIR-V +generate_include() { + local spirv_file="$1" + local inc_file="$2" + + # Use Python if available for better formatting + if command -v python3 &> /dev/null; then + python3 -c " +import struct +import sys + +with open('$spirv_file', 'rb') as f: + data = f.read() + +num_words = len(data) // 4 +words = struct.unpack('<{}I'.format(num_words), data[:num_words*4]) + +print('// Auto-generated from $(basename $spirv_file)') +print('// Size: {} bytes ({} words)'.format(len(data), num_words)) + +for i in range(0, num_words, 8): + chunk = words[i:i+8] + if i == 0: + sys.stdout.write(' ') + else: + sys.stdout.write(',\n ') + sys.stdout.write(', '.join('0x{:08x}u'.format(w) for w in chunk)) +print() +" > "$inc_file" + else + # Fallback: simple hexdump + echo "// Auto-generated from $(basename $spirv_file)" > "$inc_file" + xxd -i < "$spirv_file" | sed 's/unsigned char/uint32_t/' >> "$inc_file" + fi + + echo " Generated $inc_file" +} + +echo "========================================" +echo "Compiling Vulkan Shaders" +echo "========================================" +echo "Shaders directory: $SHADERS_DIR" +echo "Output directory: $OUTPUT_DIR" +echo "Compiler: $GLSLANG" +echo "========================================" +echo "" + +# Compile all shaders +SUCCESS=0 +FAILED=0 + +# Background shaders +if compile_shader "background.vert" "background_vert.inc"; then + ((SUCCESS++)) +else + ((FAILED++)) +fi + +if compile_shader "background.frag" "background_frag.inc"; then + ((SUCCESS++)) +else + ((FAILED++)) +fi + +# Geometry shaders +if compile_shader "geometry.vert" "geometry_vert.inc"; then + ((SUCCESS++)) +else + ((FAILED++)) +fi + +if compile_shader "geometry.frag" "geometry_frag.inc"; then + ((SUCCESS++)) +else + ((FAILED++)) +fi + +# Text shaders +if compile_shader "text.vert" "text_vert.inc"; then + ((SUCCESS++)) +else + ((FAILED++)) +fi + +if compile_shader "text.frag" "text_frag.inc"; then + ((SUCCESS++)) +else + ((FAILED++)) +fi + +echo "" +echo "========================================" +echo "Compilation Summary" +echo "========================================" +echo "Success: $SUCCESS" +echo "Failed: $FAILED" +echo "========================================" + +if [ $FAILED -gt 0 ]; then + exit 1 +fi + +echo "" +echo "All shaders compiled successfully!" +exit 0 diff --git a/shaders/background.frag b/shaders/background.frag new file mode 100644 index 0000000..a87fe53 --- /dev/null +++ b/shaders/background.frag @@ -0,0 +1,38 @@ +#version 450 + +layout(location = 0) in vec4 fragColor; +layout(location = 1) in vec2 fragTexCoord; +layout(location = 2) in vec2 fragPosition; + +layout(location = 0) out vec4 outColor; + +layout(binding = 0) uniform UniformBufferObject { + float time; + vec2 resolution; + float rotation; + float wavePhase; +} ubo; + +void main() { + // Normalize position to 0-1 range + vec2 uv = (fragPosition + 1.0) * 0.5; + + // Create dynamic gradient based on time + float t = ubo.time / 360.0; + + // Calculate color components with sine waves + float r = 0.39 + 0.20 * sin(t * 6.28318); + float g = 0.59 + 0.20 * sin(t * 6.28318 + 1.047); + float b = 0.78 + 0.22 * sin(t * 6.28318 + 2.094); + + vec3 color1 = vec3(r, g, b); + vec3 color2 = vec3(0.12, 0.12, 0.24); + + // Linear gradient from top-left to bottom-right + float gradient = uv.x * 0.5 + uv.y * 0.5; + + // Mix colors + vec3 finalColor = mix(color1, color2, gradient); + + outColor = vec4(finalColor, 1.0); +} diff --git a/shaders/background.vert b/shaders/background.vert new file mode 100644 index 0000000..8cd8961 --- /dev/null +++ b/shaders/background.vert @@ -0,0 +1,23 @@ +#version 450 + +layout(location = 0) in vec2 inPosition; +layout(location = 1) in vec4 inColor; +layout(location = 2) in vec2 inTexCoord; + +layout(location = 0) out vec4 fragColor; +layout(location = 1) out vec2 fragTexCoord; +layout(location = 2) out vec2 fragPosition; + +layout(binding = 0) uniform UniformBufferObject { + float time; + vec2 resolution; + float rotation; + float wavePhase; +} ubo; + +void main() { + gl_Position = vec4(inPosition, 0.0, 1.0); + fragColor = inColor; + fragTexCoord = inTexCoord; + fragPosition = inPosition; +} diff --git a/shaders/geometry.frag b/shaders/geometry.frag new file mode 100644 index 0000000..e596551 --- /dev/null +++ b/shaders/geometry.frag @@ -0,0 +1,18 @@ +#version 450 + +layout(location = 0) in vec4 fragColor; +layout(location = 1) in vec2 fragTexCoord; + +layout(location = 0) out vec4 outColor; + +layout(binding = 0) uniform UniformBufferObject { + float time; + vec2 resolution; + float rotation; + float wavePhase; +} ubo; + +void main() { + // Simple pass-through with vertex color + outColor = fragColor; +} diff --git a/shaders/geometry.vert b/shaders/geometry.vert new file mode 100644 index 0000000..77933cc --- /dev/null +++ b/shaders/geometry.vert @@ -0,0 +1,30 @@ +#version 450 + +layout(location = 0) in vec2 inPosition; +layout(location = 1) in vec4 inColor; +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; +} ubo; + +void main() { + // Transform position from pixel coordinates to normalized device coordinates + vec2 pos = inPosition; + + // Convert to NDC: (0, 0) to (width, height) -> (-1, -1) to (1, 1) + vec2 ndc = (pos / ubo.resolution) * 2.0 - 1.0; + + // Flip Y axis (Vulkan has Y down, we want Y up for easier math) + ndc.y = -ndc.y; + + gl_Position = vec4(ndc, 0.0, 1.0); + fragColor = inColor; + fragTexCoord = inTexCoord; +} diff --git a/shaders/text.frag b/shaders/text.frag new file mode 100644 index 0000000..f261870 --- /dev/null +++ b/shaders/text.frag @@ -0,0 +1,23 @@ +#version 450 + +layout(location = 0) in vec4 fragColor; +layout(location = 1) in vec2 fragTexCoord; + +layout(location = 0) out vec4 outColor; + +layout(binding = 0) uniform UniformBufferObject { + float time; + vec2 resolution; + float rotation; + float wavePhase; +} ubo; + +layout(binding = 1) uniform sampler2D texSampler; + +void main() { + // Sample the font texture atlas + float alpha = texture(texSampler, fragTexCoord).r; + + // Apply color with alpha from texture + outColor = vec4(fragColor.rgb, fragColor.a * alpha); +} diff --git a/shaders/text.vert b/shaders/text.vert new file mode 100644 index 0000000..77933cc --- /dev/null +++ b/shaders/text.vert @@ -0,0 +1,30 @@ +#version 450 + +layout(location = 0) in vec2 inPosition; +layout(location = 1) in vec4 inColor; +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; +} ubo; + +void main() { + // Transform position from pixel coordinates to normalized device coordinates + vec2 pos = inPosition; + + // Convert to NDC: (0, 0) to (width, height) -> (-1, -1) to (1, 1) + vec2 ndc = (pos / ubo.resolution) * 2.0 - 1.0; + + // Flip Y axis (Vulkan has Y down, we want Y up for easier math) + ndc.y = -ndc.y; + + gl_Position = vec4(ndc, 0.0, 1.0); + fragColor = inColor; + fragTexCoord = inTexCoord; +} diff --git a/src/shaders_spirv/background.frag.spv b/src/shaders_spirv/background.frag.spv new file mode 100644 index 0000000..76fbc24 Binary files /dev/null and b/src/shaders_spirv/background.frag.spv differ diff --git a/src/shaders_spirv/background.vert.spv b/src/shaders_spirv/background.vert.spv new file mode 100644 index 0000000..4fb710d Binary files /dev/null and b/src/shaders_spirv/background.vert.spv differ diff --git a/src/shaders_spirv/background_frag.inc b/src/shaders_spirv/background_frag.inc new file mode 100644 index 0000000..95caa7e --- /dev/null +++ b/src/shaders_spirv/background_frag.inc @@ -0,0 +1,80 @@ +// Auto-generated from background.frag.spv +// Size: 2488 bytes (622 words) + 0x07230203u, 0x00010000u, 0x0008000bu, 0x00000061u, 0x00000000u, 0x00020011u, 0x00000001u, 0x0006000bu, + 0x00000001u, 0x4c534c47u, 0x6474732eu, 0x3035342eu, 0x00000000u, 0x0003000eu, 0x00000000u, 0x00000001u, + 0x0009000fu, 0x00000004u, 0x00000004u, 0x6e69616du, 0x00000000u, 0x0000000bu, 0x00000058u, 0x0000005fu, + 0x00000060u, 0x00030010u, 0x00000004u, 0x00000007u, 0x00030003u, 0x00000002u, 0x000001c2u, 0x00040005u, + 0x00000004u, 0x6e69616du, 0x00000000u, 0x00030005u, 0x00000009u, 0x00007675u, 0x00060005u, 0x0000000bu, + 0x67617266u, 0x69736f50u, 0x6e6f6974u, 0x00000000u, 0x00030005u, 0x00000013u, 0x00000074u, 0x00070005u, + 0x00000014u, 0x66696e55u, 0x426d726fu, 0x65666675u, 0x6a624f72u, 0x00746365u, 0x00050006u, 0x00000014u, + 0x00000000u, 0x656d6974u, 0x00000000u, 0x00060006u, 0x00000014u, 0x00000001u, 0x6f736572u, 0x6974756cu, + 0x00006e6fu, 0x00060006u, 0x00000014u, 0x00000002u, 0x61746f72u, 0x6e6f6974u, 0x00000000u, 0x00060006u, + 0x00000014u, 0x00000003u, 0x65766177u, 0x73616850u, 0x00000065u, 0x00030005u, 0x00000016u, 0x006f6275u, + 0x00030005u, 0x0000001eu, 0x00000072u, 0x00030005u, 0x00000027u, 0x00000067u, 0x00030005u, 0x00000030u, + 0x00000062u, 0x00040005u, 0x0000003cu, 0x6f6c6f63u, 0x00003172u, 0x00040005u, 0x00000041u, 0x6f6c6f63u, + 0x00003272u, 0x00050005u, 0x00000045u, 0x64617267u, 0x746e6569u, 0x00000000u, 0x00050005u, 0x00000050u, + 0x616e6966u, 0x6c6f436cu, 0x0000726fu, 0x00050005u, 0x00000058u, 0x4374756fu, 0x726f6c6fu, 0x00000000u, + 0x00050005u, 0x0000005fu, 0x67617266u, 0x6f6c6f43u, 0x00000072u, 0x00060005u, 0x00000060u, 0x67617266u, + 0x43786554u, 0x64726f6fu, 0x00000000u, 0x00040047u, 0x0000000bu, 0x0000001eu, 0x00000002u, 0x00050048u, + 0x00000014u, 0x00000000u, 0x00000023u, 0x00000000u, 0x00050048u, 0x00000014u, 0x00000001u, 0x00000023u, + 0x00000008u, 0x00050048u, 0x00000014u, 0x00000002u, 0x00000023u, 0x00000010u, 0x00050048u, 0x00000014u, + 0x00000003u, 0x00000023u, 0x00000014u, 0x00030047u, 0x00000014u, 0x00000002u, 0x00040047u, 0x00000016u, + 0x00000022u, 0x00000000u, 0x00040047u, 0x00000016u, 0x00000021u, 0x00000000u, 0x00040047u, 0x00000058u, + 0x0000001eu, 0x00000000u, 0x00040047u, 0x0000005fu, 0x0000001eu, 0x00000000u, 0x00040047u, 0x00000060u, + 0x0000001eu, 0x00000001u, 0x00020013u, 0x00000002u, 0x00030021u, 0x00000003u, 0x00000002u, 0x00030016u, + 0x00000006u, 0x00000020u, 0x00040017u, 0x00000007u, 0x00000006u, 0x00000002u, 0x00040020u, 0x00000008u, + 0x00000007u, 0x00000007u, 0x00040020u, 0x0000000au, 0x00000001u, 0x00000007u, 0x0004003bu, 0x0000000au, + 0x0000000bu, 0x00000001u, 0x0004002bu, 0x00000006u, 0x0000000du, 0x3f800000u, 0x0004002bu, 0x00000006u, + 0x00000010u, 0x3f000000u, 0x00040020u, 0x00000012u, 0x00000007u, 0x00000006u, 0x0006001eu, 0x00000014u, + 0x00000006u, 0x00000007u, 0x00000006u, 0x00000006u, 0x00040020u, 0x00000015u, 0x00000002u, 0x00000014u, + 0x0004003bu, 0x00000015u, 0x00000016u, 0x00000002u, 0x00040015u, 0x00000017u, 0x00000020u, 0x00000001u, + 0x0004002bu, 0x00000017u, 0x00000018u, 0x00000000u, 0x00040020u, 0x00000019u, 0x00000002u, 0x00000006u, + 0x0004002bu, 0x00000006u, 0x0000001cu, 0x43b40000u, 0x0004002bu, 0x00000006u, 0x0000001fu, 0x3ec7ae14u, + 0x0004002bu, 0x00000006u, 0x00000020u, 0x3e4ccccdu, 0x0004002bu, 0x00000006u, 0x00000022u, 0x40c90fd0u, + 0x0004002bu, 0x00000006u, 0x00000028u, 0x3f170a3du, 0x0004002bu, 0x00000006u, 0x0000002bu, 0x3f860419u, + 0x0004002bu, 0x00000006u, 0x00000031u, 0x3f47ae14u, 0x0004002bu, 0x00000006u, 0x00000032u, 0x3e6147aeu, + 0x0004002bu, 0x00000006u, 0x00000035u, 0x40060419u, 0x00040017u, 0x0000003au, 0x00000006u, 0x00000003u, + 0x00040020u, 0x0000003bu, 0x00000007u, 0x0000003au, 0x0004002bu, 0x00000006u, 0x00000042u, 0x3df5c28fu, + 0x0004002bu, 0x00000006u, 0x00000043u, 0x3e75c28fu, 0x0006002cu, 0x0000003au, 0x00000044u, 0x00000042u, + 0x00000042u, 0x00000043u, 0x00040015u, 0x00000046u, 0x00000020u, 0x00000000u, 0x0004002bu, 0x00000046u, + 0x00000047u, 0x00000000u, 0x0004002bu, 0x00000046u, 0x0000004bu, 0x00000001u, 0x00040017u, 0x00000056u, + 0x00000006u, 0x00000004u, 0x00040020u, 0x00000057u, 0x00000003u, 0x00000056u, 0x0004003bu, 0x00000057u, + 0x00000058u, 0x00000003u, 0x00040020u, 0x0000005eu, 0x00000001u, 0x00000056u, 0x0004003bu, 0x0000005eu, + 0x0000005fu, 0x00000001u, 0x0004003bu, 0x0000000au, 0x00000060u, 0x00000001u, 0x00050036u, 0x00000002u, + 0x00000004u, 0x00000000u, 0x00000003u, 0x000200f8u, 0x00000005u, 0x0004003bu, 0x00000008u, 0x00000009u, + 0x00000007u, 0x0004003bu, 0x00000012u, 0x00000013u, 0x00000007u, 0x0004003bu, 0x00000012u, 0x0000001eu, + 0x00000007u, 0x0004003bu, 0x00000012u, 0x00000027u, 0x00000007u, 0x0004003bu, 0x00000012u, 0x00000030u, + 0x00000007u, 0x0004003bu, 0x0000003bu, 0x0000003cu, 0x00000007u, 0x0004003bu, 0x0000003bu, 0x00000041u, + 0x00000007u, 0x0004003bu, 0x00000012u, 0x00000045u, 0x00000007u, 0x0004003bu, 0x0000003bu, 0x00000050u, + 0x00000007u, 0x0004003du, 0x00000007u, 0x0000000cu, 0x0000000bu, 0x00050050u, 0x00000007u, 0x0000000eu, + 0x0000000du, 0x0000000du, 0x00050081u, 0x00000007u, 0x0000000fu, 0x0000000cu, 0x0000000eu, 0x0005008eu, + 0x00000007u, 0x00000011u, 0x0000000fu, 0x00000010u, 0x0003003eu, 0x00000009u, 0x00000011u, 0x00050041u, + 0x00000019u, 0x0000001au, 0x00000016u, 0x00000018u, 0x0004003du, 0x00000006u, 0x0000001bu, 0x0000001au, + 0x00050088u, 0x00000006u, 0x0000001du, 0x0000001bu, 0x0000001cu, 0x0003003eu, 0x00000013u, 0x0000001du, + 0x0004003du, 0x00000006u, 0x00000021u, 0x00000013u, 0x00050085u, 0x00000006u, 0x00000023u, 0x00000021u, + 0x00000022u, 0x0006000cu, 0x00000006u, 0x00000024u, 0x00000001u, 0x0000000du, 0x00000023u, 0x00050085u, + 0x00000006u, 0x00000025u, 0x00000020u, 0x00000024u, 0x00050081u, 0x00000006u, 0x00000026u, 0x0000001fu, + 0x00000025u, 0x0003003eu, 0x0000001eu, 0x00000026u, 0x0004003du, 0x00000006u, 0x00000029u, 0x00000013u, + 0x00050085u, 0x00000006u, 0x0000002au, 0x00000029u, 0x00000022u, 0x00050081u, 0x00000006u, 0x0000002cu, + 0x0000002au, 0x0000002bu, 0x0006000cu, 0x00000006u, 0x0000002du, 0x00000001u, 0x0000000du, 0x0000002cu, + 0x00050085u, 0x00000006u, 0x0000002eu, 0x00000020u, 0x0000002du, 0x00050081u, 0x00000006u, 0x0000002fu, + 0x00000028u, 0x0000002eu, 0x0003003eu, 0x00000027u, 0x0000002fu, 0x0004003du, 0x00000006u, 0x00000033u, + 0x00000013u, 0x00050085u, 0x00000006u, 0x00000034u, 0x00000033u, 0x00000022u, 0x00050081u, 0x00000006u, + 0x00000036u, 0x00000034u, 0x00000035u, 0x0006000cu, 0x00000006u, 0x00000037u, 0x00000001u, 0x0000000du, + 0x00000036u, 0x00050085u, 0x00000006u, 0x00000038u, 0x00000032u, 0x00000037u, 0x00050081u, 0x00000006u, + 0x00000039u, 0x00000031u, 0x00000038u, 0x0003003eu, 0x00000030u, 0x00000039u, 0x0004003du, 0x00000006u, + 0x0000003du, 0x0000001eu, 0x0004003du, 0x00000006u, 0x0000003eu, 0x00000027u, 0x0004003du, 0x00000006u, + 0x0000003fu, 0x00000030u, 0x00060050u, 0x0000003au, 0x00000040u, 0x0000003du, 0x0000003eu, 0x0000003fu, + 0x0003003eu, 0x0000003cu, 0x00000040u, 0x0003003eu, 0x00000041u, 0x00000044u, 0x00050041u, 0x00000012u, + 0x00000048u, 0x00000009u, 0x00000047u, 0x0004003du, 0x00000006u, 0x00000049u, 0x00000048u, 0x00050085u, + 0x00000006u, 0x0000004au, 0x00000049u, 0x00000010u, 0x00050041u, 0x00000012u, 0x0000004cu, 0x00000009u, + 0x0000004bu, 0x0004003du, 0x00000006u, 0x0000004du, 0x0000004cu, 0x00050085u, 0x00000006u, 0x0000004eu, + 0x0000004du, 0x00000010u, 0x00050081u, 0x00000006u, 0x0000004fu, 0x0000004au, 0x0000004eu, 0x0003003eu, + 0x00000045u, 0x0000004fu, 0x0004003du, 0x0000003au, 0x00000051u, 0x0000003cu, 0x0004003du, 0x0000003au, + 0x00000052u, 0x00000041u, 0x0004003du, 0x00000006u, 0x00000053u, 0x00000045u, 0x00060050u, 0x0000003au, + 0x00000054u, 0x00000053u, 0x00000053u, 0x00000053u, 0x0008000cu, 0x0000003au, 0x00000055u, 0x00000001u, + 0x0000002eu, 0x00000051u, 0x00000052u, 0x00000054u, 0x0003003eu, 0x00000050u, 0x00000055u, 0x0004003du, + 0x0000003au, 0x00000059u, 0x00000050u, 0x00050051u, 0x00000006u, 0x0000005au, 0x00000059u, 0x00000000u, + 0x00050051u, 0x00000006u, 0x0000005bu, 0x00000059u, 0x00000001u, 0x00050051u, 0x00000006u, 0x0000005cu, + 0x00000059u, 0x00000002u, 0x00070050u, 0x00000056u, 0x0000005du, 0x0000005au, 0x0000005bu, 0x0000005cu, + 0x0000000du, 0x0003003eu, 0x00000058u, 0x0000005du, 0x000100fdu, 0x00010038u diff --git a/src/shaders_spirv/background_vert.inc b/src/shaders_spirv/background_vert.inc new file mode 100644 index 0000000..daf5343 --- /dev/null +++ b/src/shaders_spirv/background_vert.inc @@ -0,0 +1,50 @@ +// Auto-generated from background.vert.spv +// Size: 1536 bytes (384 words) + 0x07230203u, 0x00010000u, 0x0008000bu, 0x00000028u, 0x00000000u, 0x00020011u, 0x00000001u, 0x0006000bu, + 0x00000001u, 0x4c534c47u, 0x6474732eu, 0x3035342eu, 0x00000000u, 0x0003000eu, 0x00000000u, 0x00000001u, + 0x000c000fu, 0x00000000u, 0x00000004u, 0x6e69616du, 0x00000000u, 0x0000000du, 0x00000012u, 0x0000001bu, + 0x0000001du, 0x00000020u, 0x00000021u, 0x00000023u, 0x00030003u, 0x00000002u, 0x000001c2u, 0x00040005u, + 0x00000004u, 0x6e69616du, 0x00000000u, 0x00060005u, 0x0000000bu, 0x505f6c67u, 0x65567265u, 0x78657472u, + 0x00000000u, 0x00060006u, 0x0000000bu, 0x00000000u, 0x505f6c67u, 0x7469736fu, 0x006e6f69u, 0x00070006u, + 0x0000000bu, 0x00000001u, 0x505f6c67u, 0x746e696fu, 0x657a6953u, 0x00000000u, 0x00070006u, 0x0000000bu, + 0x00000002u, 0x435f6c67u, 0x4470696cu, 0x61747369u, 0x0065636eu, 0x00070006u, 0x0000000bu, 0x00000003u, + 0x435f6c67u, 0x446c6c75u, 0x61747369u, 0x0065636eu, 0x00030005u, 0x0000000du, 0x00000000u, 0x00050005u, + 0x00000012u, 0x6f506e69u, 0x69746973u, 0x00006e6fu, 0x00050005u, 0x0000001bu, 0x67617266u, 0x6f6c6f43u, + 0x00000072u, 0x00040005u, 0x0000001du, 0x6f436e69u, 0x00726f6cu, 0x00060005u, 0x00000020u, 0x67617266u, + 0x43786554u, 0x64726f6fu, 0x00000000u, 0x00050005u, 0x00000021u, 0x65546e69u, 0x6f6f4378u, 0x00006472u, + 0x00060005u, 0x00000023u, 0x67617266u, 0x69736f50u, 0x6e6f6974u, 0x00000000u, 0x00070005u, 0x00000025u, + 0x66696e55u, 0x426d726fu, 0x65666675u, 0x6a624f72u, 0x00746365u, 0x00050006u, 0x00000025u, 0x00000000u, + 0x656d6974u, 0x00000000u, 0x00060006u, 0x00000025u, 0x00000001u, 0x6f736572u, 0x6974756cu, 0x00006e6fu, + 0x00060006u, 0x00000025u, 0x00000002u, 0x61746f72u, 0x6e6f6974u, 0x00000000u, 0x00060006u, 0x00000025u, + 0x00000003u, 0x65766177u, 0x73616850u, 0x00000065u, 0x00030005u, 0x00000027u, 0x006f6275u, 0x00050048u, + 0x0000000bu, 0x00000000u, 0x0000000bu, 0x00000000u, 0x00050048u, 0x0000000bu, 0x00000001u, 0x0000000bu, + 0x00000001u, 0x00050048u, 0x0000000bu, 0x00000002u, 0x0000000bu, 0x00000003u, 0x00050048u, 0x0000000bu, + 0x00000003u, 0x0000000bu, 0x00000004u, 0x00030047u, 0x0000000bu, 0x00000002u, 0x00040047u, 0x00000012u, + 0x0000001eu, 0x00000000u, 0x00040047u, 0x0000001bu, 0x0000001eu, 0x00000000u, 0x00040047u, 0x0000001du, + 0x0000001eu, 0x00000001u, 0x00040047u, 0x00000020u, 0x0000001eu, 0x00000001u, 0x00040047u, 0x00000021u, + 0x0000001eu, 0x00000002u, 0x00040047u, 0x00000023u, 0x0000001eu, 0x00000002u, 0x00050048u, 0x00000025u, + 0x00000000u, 0x00000023u, 0x00000000u, 0x00050048u, 0x00000025u, 0x00000001u, 0x00000023u, 0x00000008u, + 0x00050048u, 0x00000025u, 0x00000002u, 0x00000023u, 0x00000010u, 0x00050048u, 0x00000025u, 0x00000003u, + 0x00000023u, 0x00000014u, 0x00030047u, 0x00000025u, 0x00000002u, 0x00040047u, 0x00000027u, 0x00000022u, + 0x00000000u, 0x00040047u, 0x00000027u, 0x00000021u, 0x00000000u, 0x00020013u, 0x00000002u, 0x00030021u, + 0x00000003u, 0x00000002u, 0x00030016u, 0x00000006u, 0x00000020u, 0x00040017u, 0x00000007u, 0x00000006u, + 0x00000004u, 0x00040015u, 0x00000008u, 0x00000020u, 0x00000000u, 0x0004002bu, 0x00000008u, 0x00000009u, + 0x00000001u, 0x0004001cu, 0x0000000au, 0x00000006u, 0x00000009u, 0x0006001eu, 0x0000000bu, 0x00000007u, + 0x00000006u, 0x0000000au, 0x0000000au, 0x00040020u, 0x0000000cu, 0x00000003u, 0x0000000bu, 0x0004003bu, + 0x0000000cu, 0x0000000du, 0x00000003u, 0x00040015u, 0x0000000eu, 0x00000020u, 0x00000001u, 0x0004002bu, + 0x0000000eu, 0x0000000fu, 0x00000000u, 0x00040017u, 0x00000010u, 0x00000006u, 0x00000002u, 0x00040020u, + 0x00000011u, 0x00000001u, 0x00000010u, 0x0004003bu, 0x00000011u, 0x00000012u, 0x00000001u, 0x0004002bu, + 0x00000006u, 0x00000014u, 0x00000000u, 0x0004002bu, 0x00000006u, 0x00000015u, 0x3f800000u, 0x00040020u, + 0x00000019u, 0x00000003u, 0x00000007u, 0x0004003bu, 0x00000019u, 0x0000001bu, 0x00000003u, 0x00040020u, + 0x0000001cu, 0x00000001u, 0x00000007u, 0x0004003bu, 0x0000001cu, 0x0000001du, 0x00000001u, 0x00040020u, + 0x0000001fu, 0x00000003u, 0x00000010u, 0x0004003bu, 0x0000001fu, 0x00000020u, 0x00000003u, 0x0004003bu, + 0x00000011u, 0x00000021u, 0x00000001u, 0x0004003bu, 0x0000001fu, 0x00000023u, 0x00000003u, 0x0006001eu, + 0x00000025u, 0x00000006u, 0x00000010u, 0x00000006u, 0x00000006u, 0x00040020u, 0x00000026u, 0x00000002u, + 0x00000025u, 0x0004003bu, 0x00000026u, 0x00000027u, 0x00000002u, 0x00050036u, 0x00000002u, 0x00000004u, + 0x00000000u, 0x00000003u, 0x000200f8u, 0x00000005u, 0x0004003du, 0x00000010u, 0x00000013u, 0x00000012u, + 0x00050051u, 0x00000006u, 0x00000016u, 0x00000013u, 0x00000000u, 0x00050051u, 0x00000006u, 0x00000017u, + 0x00000013u, 0x00000001u, 0x00070050u, 0x00000007u, 0x00000018u, 0x00000016u, 0x00000017u, 0x00000014u, + 0x00000015u, 0x00050041u, 0x00000019u, 0x0000001au, 0x0000000du, 0x0000000fu, 0x0003003eu, 0x0000001au, + 0x00000018u, 0x0004003du, 0x00000007u, 0x0000001eu, 0x0000001du, 0x0003003eu, 0x0000001bu, 0x0000001eu, + 0x0004003du, 0x00000010u, 0x00000022u, 0x00000021u, 0x0003003eu, 0x00000020u, 0x00000022u, 0x0004003du, + 0x00000010u, 0x00000024u, 0x00000012u, 0x0003003eu, 0x00000023u, 0x00000024u, 0x000100fdu, 0x00010038u diff --git a/src/shaders_spirv/geometry.frag.spv b/src/shaders_spirv/geometry.frag.spv new file mode 100644 index 0000000..42b7c9e Binary files /dev/null and b/src/shaders_spirv/geometry.frag.spv differ diff --git a/src/shaders_spirv/geometry.vert.spv b/src/shaders_spirv/geometry.vert.spv new file mode 100644 index 0000000..764ffe1 Binary files /dev/null and b/src/shaders_spirv/geometry.vert.spv differ diff --git a/src/shaders_spirv/geometry_frag.inc b/src/shaders_spirv/geometry_frag.inc new file mode 100644 index 0000000..c7412a7 --- /dev/null +++ b/src/shaders_spirv/geometry_frag.inc @@ -0,0 +1,27 @@ +// Auto-generated from geometry.frag.spv +// Size: 784 bytes (196 words) + 0x07230203u, 0x00010000u, 0x0008000bu, 0x00000013u, 0x00000000u, 0x00020011u, 0x00000001u, 0x0006000bu, + 0x00000001u, 0x4c534c47u, 0x6474732eu, 0x3035342eu, 0x00000000u, 0x0003000eu, 0x00000000u, 0x00000001u, + 0x0008000fu, 0x00000004u, 0x00000004u, 0x6e69616du, 0x00000000u, 0x00000009u, 0x0000000bu, 0x0000000fu, + 0x00030010u, 0x00000004u, 0x00000007u, 0x00030003u, 0x00000002u, 0x000001c2u, 0x00040005u, 0x00000004u, + 0x6e69616du, 0x00000000u, 0x00050005u, 0x00000009u, 0x4374756fu, 0x726f6c6fu, 0x00000000u, 0x00050005u, + 0x0000000bu, 0x67617266u, 0x6f6c6f43u, 0x00000072u, 0x00060005u, 0x0000000fu, 0x67617266u, 0x43786554u, + 0x64726f6fu, 0x00000000u, 0x00070005u, 0x00000010u, 0x66696e55u, 0x426d726fu, 0x65666675u, 0x6a624f72u, + 0x00746365u, 0x00050006u, 0x00000010u, 0x00000000u, 0x656d6974u, 0x00000000u, 0x00060006u, 0x00000010u, + 0x00000001u, 0x6f736572u, 0x6974756cu, 0x00006e6fu, 0x00060006u, 0x00000010u, 0x00000002u, 0x61746f72u, + 0x6e6f6974u, 0x00000000u, 0x00060006u, 0x00000010u, 0x00000003u, 0x65766177u, 0x73616850u, 0x00000065u, + 0x00030005u, 0x00000012u, 0x006f6275u, 0x00040047u, 0x00000009u, 0x0000001eu, 0x00000000u, 0x00040047u, + 0x0000000bu, 0x0000001eu, 0x00000000u, 0x00040047u, 0x0000000fu, 0x0000001eu, 0x00000001u, 0x00050048u, + 0x00000010u, 0x00000000u, 0x00000023u, 0x00000000u, 0x00050048u, 0x00000010u, 0x00000001u, 0x00000023u, + 0x00000008u, 0x00050048u, 0x00000010u, 0x00000002u, 0x00000023u, 0x00000010u, 0x00050048u, 0x00000010u, + 0x00000003u, 0x00000023u, 0x00000014u, 0x00030047u, 0x00000010u, 0x00000002u, 0x00040047u, 0x00000012u, + 0x00000022u, 0x00000000u, 0x00040047u, 0x00000012u, 0x00000021u, 0x00000000u, 0x00020013u, 0x00000002u, + 0x00030021u, 0x00000003u, 0x00000002u, 0x00030016u, 0x00000006u, 0x00000020u, 0x00040017u, 0x00000007u, + 0x00000006u, 0x00000004u, 0x00040020u, 0x00000008u, 0x00000003u, 0x00000007u, 0x0004003bu, 0x00000008u, + 0x00000009u, 0x00000003u, 0x00040020u, 0x0000000au, 0x00000001u, 0x00000007u, 0x0004003bu, 0x0000000au, + 0x0000000bu, 0x00000001u, 0x00040017u, 0x0000000du, 0x00000006u, 0x00000002u, 0x00040020u, 0x0000000eu, + 0x00000001u, 0x0000000du, 0x0004003bu, 0x0000000eu, 0x0000000fu, 0x00000001u, 0x0006001eu, 0x00000010u, + 0x00000006u, 0x0000000du, 0x00000006u, 0x00000006u, 0x00040020u, 0x00000011u, 0x00000002u, 0x00000010u, + 0x0004003bu, 0x00000011u, 0x00000012u, 0x00000002u, 0x00050036u, 0x00000002u, 0x00000004u, 0x00000000u, + 0x00000003u, 0x000200f8u, 0x00000005u, 0x0004003du, 0x00000007u, 0x0000000cu, 0x0000000bu, 0x0003003eu, + 0x00000009u, 0x0000000cu, 0x000100fdu, 0x00010038u diff --git a/src/shaders_spirv/geometry_vert.inc b/src/shaders_spirv/geometry_vert.inc new file mode 100644 index 0000000..9c3f2fa --- /dev/null +++ b/src/shaders_spirv/geometry_vert.inc @@ -0,0 +1,60 @@ +// Auto-generated from geometry.vert.spv +// Size: 1840 bytes (460 words) + 0x07230203u, 0x00010000u, 0x0008000bu, 0x00000039u, 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, + 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 diff --git a/src/shaders_spirv/text.frag.spv b/src/shaders_spirv/text.frag.spv new file mode 100644 index 0000000..a2ad793 Binary files /dev/null and b/src/shaders_spirv/text.frag.spv differ diff --git a/src/shaders_spirv/text.vert.spv b/src/shaders_spirv/text.vert.spv new file mode 100644 index 0000000..764ffe1 Binary files /dev/null and b/src/shaders_spirv/text.vert.spv differ diff --git a/src/shaders_spirv/text_frag.inc b/src/shaders_spirv/text_frag.inc new file mode 100644 index 0000000..030a621 --- /dev/null +++ b/src/shaders_spirv/text_frag.inc @@ -0,0 +1,44 @@ +// Auto-generated from text.frag.spv +// Size: 1320 bytes (330 words) + 0x07230203u, 0x00010000u, 0x0008000bu, 0x0000002bu, 0x00000000u, 0x00020011u, 0x00000001u, 0x0006000bu, + 0x00000001u, 0x4c534c47u, 0x6474732eu, 0x3035342eu, 0x00000000u, 0x0003000eu, 0x00000000u, 0x00000001u, + 0x0008000fu, 0x00000004u, 0x00000004u, 0x6e69616du, 0x00000000u, 0x00000010u, 0x00000018u, 0x0000001au, + 0x00030010u, 0x00000004u, 0x00000007u, 0x00030003u, 0x00000002u, 0x000001c2u, 0x00040005u, 0x00000004u, + 0x6e69616du, 0x00000000u, 0x00040005u, 0x00000008u, 0x68706c61u, 0x00000061u, 0x00050005u, 0x0000000cu, + 0x53786574u, 0x6c706d61u, 0x00007265u, 0x00060005u, 0x00000010u, 0x67617266u, 0x43786554u, 0x64726f6fu, + 0x00000000u, 0x00050005u, 0x00000018u, 0x4374756fu, 0x726f6c6fu, 0x00000000u, 0x00050005u, 0x0000001au, + 0x67617266u, 0x6f6c6f43u, 0x00000072u, 0x00070005u, 0x00000028u, 0x66696e55u, 0x426d726fu, 0x65666675u, + 0x6a624f72u, 0x00746365u, 0x00050006u, 0x00000028u, 0x00000000u, 0x656d6974u, 0x00000000u, 0x00060006u, + 0x00000028u, 0x00000001u, 0x6f736572u, 0x6974756cu, 0x00006e6fu, 0x00060006u, 0x00000028u, 0x00000002u, + 0x61746f72u, 0x6e6f6974u, 0x00000000u, 0x00060006u, 0x00000028u, 0x00000003u, 0x65766177u, 0x73616850u, + 0x00000065u, 0x00030005u, 0x0000002au, 0x006f6275u, 0x00040047u, 0x0000000cu, 0x00000022u, 0x00000000u, + 0x00040047u, 0x0000000cu, 0x00000021u, 0x00000001u, 0x00040047u, 0x00000010u, 0x0000001eu, 0x00000001u, + 0x00040047u, 0x00000018u, 0x0000001eu, 0x00000000u, 0x00040047u, 0x0000001au, 0x0000001eu, 0x00000000u, + 0x00050048u, 0x00000028u, 0x00000000u, 0x00000023u, 0x00000000u, 0x00050048u, 0x00000028u, 0x00000001u, + 0x00000023u, 0x00000008u, 0x00050048u, 0x00000028u, 0x00000002u, 0x00000023u, 0x00000010u, 0x00050048u, + 0x00000028u, 0x00000003u, 0x00000023u, 0x00000014u, 0x00030047u, 0x00000028u, 0x00000002u, 0x00040047u, + 0x0000002au, 0x00000022u, 0x00000000u, 0x00040047u, 0x0000002au, 0x00000021u, 0x00000000u, 0x00020013u, + 0x00000002u, 0x00030021u, 0x00000003u, 0x00000002u, 0x00030016u, 0x00000006u, 0x00000020u, 0x00040020u, + 0x00000007u, 0x00000007u, 0x00000006u, 0x00090019u, 0x00000009u, 0x00000006u, 0x00000001u, 0x00000000u, + 0x00000000u, 0x00000000u, 0x00000001u, 0x00000000u, 0x0003001bu, 0x0000000au, 0x00000009u, 0x00040020u, + 0x0000000bu, 0x00000000u, 0x0000000au, 0x0004003bu, 0x0000000bu, 0x0000000cu, 0x00000000u, 0x00040017u, + 0x0000000eu, 0x00000006u, 0x00000002u, 0x00040020u, 0x0000000fu, 0x00000001u, 0x0000000eu, 0x0004003bu, + 0x0000000fu, 0x00000010u, 0x00000001u, 0x00040017u, 0x00000012u, 0x00000006u, 0x00000004u, 0x00040015u, + 0x00000014u, 0x00000020u, 0x00000000u, 0x0004002bu, 0x00000014u, 0x00000015u, 0x00000000u, 0x00040020u, + 0x00000017u, 0x00000003u, 0x00000012u, 0x0004003bu, 0x00000017u, 0x00000018u, 0x00000003u, 0x00040020u, + 0x00000019u, 0x00000001u, 0x00000012u, 0x0004003bu, 0x00000019u, 0x0000001au, 0x00000001u, 0x00040017u, + 0x0000001bu, 0x00000006u, 0x00000003u, 0x0004002bu, 0x00000014u, 0x0000001eu, 0x00000003u, 0x00040020u, + 0x0000001fu, 0x00000001u, 0x00000006u, 0x0006001eu, 0x00000028u, 0x00000006u, 0x0000000eu, 0x00000006u, + 0x00000006u, 0x00040020u, 0x00000029u, 0x00000002u, 0x00000028u, 0x0004003bu, 0x00000029u, 0x0000002au, + 0x00000002u, 0x00050036u, 0x00000002u, 0x00000004u, 0x00000000u, 0x00000003u, 0x000200f8u, 0x00000005u, + 0x0004003bu, 0x00000007u, 0x00000008u, 0x00000007u, 0x0004003du, 0x0000000au, 0x0000000du, 0x0000000cu, + 0x0004003du, 0x0000000eu, 0x00000011u, 0x00000010u, 0x00050057u, 0x00000012u, 0x00000013u, 0x0000000du, + 0x00000011u, 0x00050051u, 0x00000006u, 0x00000016u, 0x00000013u, 0x00000000u, 0x0003003eu, 0x00000008u, + 0x00000016u, 0x0004003du, 0x00000012u, 0x0000001cu, 0x0000001au, 0x0008004fu, 0x0000001bu, 0x0000001du, + 0x0000001cu, 0x0000001cu, 0x00000000u, 0x00000001u, 0x00000002u, 0x00050041u, 0x0000001fu, 0x00000020u, + 0x0000001au, 0x0000001eu, 0x0004003du, 0x00000006u, 0x00000021u, 0x00000020u, 0x0004003du, 0x00000006u, + 0x00000022u, 0x00000008u, 0x00050085u, 0x00000006u, 0x00000023u, 0x00000021u, 0x00000022u, 0x00050051u, + 0x00000006u, 0x00000024u, 0x0000001du, 0x00000000u, 0x00050051u, 0x00000006u, 0x00000025u, 0x0000001du, + 0x00000001u, 0x00050051u, 0x00000006u, 0x00000026u, 0x0000001du, 0x00000002u, 0x00070050u, 0x00000012u, + 0x00000027u, 0x00000024u, 0x00000025u, 0x00000026u, 0x00000023u, 0x0003003eu, 0x00000018u, 0x00000027u, + 0x000100fdu, 0x00010038u diff --git a/src/shaders_spirv/text_vert.inc b/src/shaders_spirv/text_vert.inc new file mode 100644 index 0000000..c0e4c2d --- /dev/null +++ b/src/shaders_spirv/text_vert.inc @@ -0,0 +1,60 @@ +// Auto-generated from text.vert.spv +// Size: 1840 bytes (460 words) + 0x07230203u, 0x00010000u, 0x0008000bu, 0x00000039u, 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, + 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 diff --git a/src/vulkanrenderer.cpp b/src/vulkanrenderer.cpp new file mode 100644 index 0000000..5d9bf40 --- /dev/null +++ b/src/vulkanrenderer.cpp @@ -0,0 +1,1557 @@ +#include "vulkanrenderer.h" + +#define VK_NO_PROTOTYPES +#include + +#include +#include +#include +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +VulkanRenderer::VulkanRenderer() + : m_device(VK_NULL_HANDLE) + , m_physicalDevice(VK_NULL_HANDLE) + , m_graphicsQueue(VK_NULL_HANDLE) + , m_queueFamilyIndex(0) + , m_transferCommandPool(VK_NULL_HANDLE) + , m_renderPass(VK_NULL_HANDLE) + , m_backgroundPipeline(VK_NULL_HANDLE) + , m_backgroundPipelineLayout(VK_NULL_HANDLE) + , m_geometryPipeline(VK_NULL_HANDLE) + , m_geometryPipelineLayout(VK_NULL_HANDLE) + , m_linePipeline(VK_NULL_HANDLE) + , m_linePipelineLayout(VK_NULL_HANDLE) + , m_textPipeline(VK_NULL_HANDLE) + , m_textPipelineLayout(VK_NULL_HANDLE) + , m_descriptorSetLayout(VK_NULL_HANDLE) + , m_descriptorPool(VK_NULL_HANDLE) + , m_backgroundVertexBuffer(VK_NULL_HANDLE) + , m_backgroundVertexMemory(VK_NULL_HANDLE) + , m_backgroundIndexBuffer(VK_NULL_HANDLE) + , m_backgroundIndexMemory(VK_NULL_HANDLE) + , m_backgroundIndexCount(0) + , m_circleVertexBuffer(VK_NULL_HANDLE) + , m_circleVertexMemory(VK_NULL_HANDLE) + , m_circleIndexBuffer(VK_NULL_HANDLE) + , m_circleIndexMemory(VK_NULL_HANDLE) + , m_circleIndexCount(0) + , m_waveVertexBuffer(VK_NULL_HANDLE) + , m_waveVertexMemory(VK_NULL_HANDLE) + , m_waveIndexBuffer(VK_NULL_HANDLE) + , m_waveIndexMemory(VK_NULL_HANDLE) + , m_waveIndexCount(0) + , m_fontTexture(VK_NULL_HANDLE) + , m_fontTextureMemory(VK_NULL_HANDLE) + , m_fontTextureView(VK_NULL_HANDLE) + , m_fontSampler(VK_NULL_HANDLE) + , m_width(0) + , m_height(0) + , m_swapchainFormat(0) + , m_initialized(false) + , m_errorCallback(nullptr) +{ + memset(&m_ubo, 0, sizeof(m_ubo)); +} + +VulkanRenderer::~VulkanRenderer() +{ + cleanup(); +} + +bool VulkanRenderer::initialize(VkDevice device, VkPhysicalDevice physicalDevice, + VkQueue graphicsQueue, uint32_t queueFamilyIndex, + uint32_t swapchainFormat, uint32_t width, uint32_t height) +{ + m_device = device; + m_physicalDevice = physicalDevice; + m_graphicsQueue = graphicsQueue; + m_queueFamilyIndex = queueFamilyIndex; + m_swapchainFormat = swapchainFormat; + m_width = width; + m_height = height; + m_transferCommandPool = VK_NULL_HANDLE; + + if (!createRenderPass()) { + logError("Failed to create render pass"); + return false; + } + + if (!createDescriptorSetLayout()) { + logError("Failed to create descriptor set layout"); + return false; + } + + if (!createBackgroundPipeline()) { + logError("Failed to create background pipeline"); + return false; + } + + if (!createGeometryPipeline()) { + logError("Failed to create geometry pipeline"); + return false; + } + + if (!createLinePipeline()) { + logError("Failed to create line pipeline"); + return false; + } + + if (!createUniformBuffers()) { + logError("Failed to create uniform buffers"); + return false; + } + + if (!createDescriptorPool()) { + logError("Failed to create descriptor pool"); + return false; + } + + if (!createDescriptorSets()) { + logError("Failed to create descriptor sets"); + return false; + } + + // Create background geometry + std::vector bgVertices; + std::vector bgIndices; + generateBackgroundQuad(bgVertices, bgIndices); + m_backgroundIndexCount = bgIndices.size(); + + if (!createVertexBuffer(bgVertices, m_backgroundVertexBuffer, m_backgroundVertexMemory)) { + logError("Failed to create background vertex buffer"); + return false; + } + + if (!createIndexBuffer(bgIndices, m_backgroundIndexBuffer, m_backgroundIndexMemory)) { + logError("Failed to create background index buffer"); + return false; + } + + // Initialize text rendering (optional) + initializeTextRendering(); + + m_initialized = true; + return true; +} + +void VulkanRenderer::cleanup() +{ + if (!m_device) return; + + // Clean up buffers + if (m_backgroundVertexBuffer) vkDestroyBuffer(m_device, m_backgroundVertexBuffer, nullptr); + if (m_backgroundVertexMemory) vkFreeMemory(m_device, m_backgroundVertexMemory, nullptr); + if (m_backgroundIndexBuffer) vkDestroyBuffer(m_device, m_backgroundIndexBuffer, nullptr); + if (m_backgroundIndexMemory) vkFreeMemory(m_device, m_backgroundIndexMemory, nullptr); + + if (m_circleVertexBuffer) vkDestroyBuffer(m_device, m_circleVertexBuffer, nullptr); + if (m_circleVertexMemory) vkFreeMemory(m_device, m_circleVertexMemory, nullptr); + if (m_circleIndexBuffer) vkDestroyBuffer(m_device, m_circleIndexBuffer, nullptr); + if (m_circleIndexMemory) vkFreeMemory(m_device, m_circleIndexMemory, nullptr); + + if (m_waveVertexBuffer) vkDestroyBuffer(m_device, m_waveVertexBuffer, nullptr); + if (m_waveVertexMemory) vkFreeMemory(m_device, m_waveVertexMemory, nullptr); + if (m_waveIndexBuffer) vkDestroyBuffer(m_device, m_waveIndexBuffer, nullptr); + if (m_waveIndexMemory) vkFreeMemory(m_device, m_waveIndexMemory, nullptr); + + // Clean up line pipeline + if (m_linePipeline) vkDestroyPipeline(m_device, m_linePipeline, nullptr); + if (m_linePipelineLayout) vkDestroyPipelineLayout(m_device, m_linePipelineLayout, nullptr); + + // Clean up uniform buffers + for (size_t i = 0; i < m_uniformBuffers.size(); i++) { + if (m_uniformBuffers[i]) vkDestroyBuffer(m_device, m_uniformBuffers[i], nullptr); + if (m_uniformBuffersMemory[i]) vkFreeMemory(m_device, m_uniformBuffersMemory[i], nullptr); + } + m_uniformBuffers.clear(); + m_uniformBuffersMemory.clear(); + m_uniformBuffersMapped.clear(); + + // Clean up text resources + if (m_fontSampler) vkDestroySampler(m_device, m_fontSampler, nullptr); + if (m_fontTextureView) vkDestroyImageView(m_device, m_fontTextureView, nullptr); + if (m_fontTexture) vkDestroyImage(m_device, m_fontTexture, nullptr); + if (m_fontTextureMemory) vkFreeMemory(m_device, m_fontTextureMemory, nullptr); + + // Clean up descriptors + if (m_descriptorPool) vkDestroyDescriptorPool(m_device, m_descriptorPool, nullptr); + if (m_descriptorSetLayout) vkDestroyDescriptorSetLayout(m_device, m_descriptorSetLayout, nullptr); + + // Clean up transfer command pool + if (m_transferCommandPool) vkDestroyCommandPool(m_device, m_transferCommandPool, nullptr); + + // Clean up pipelines + if (m_backgroundPipeline) vkDestroyPipeline(m_device, m_backgroundPipeline, nullptr); + if (m_backgroundPipelineLayout) vkDestroyPipelineLayout(m_device, m_backgroundPipelineLayout, nullptr); + if (m_geometryPipeline) vkDestroyPipeline(m_device, m_geometryPipeline, nullptr); + if (m_geometryPipelineLayout) vkDestroyPipelineLayout(m_device, m_geometryPipelineLayout, nullptr); + if (m_textPipeline) vkDestroyPipeline(m_device, m_textPipeline, nullptr); + if (m_textPipelineLayout) vkDestroyPipelineLayout(m_device, m_textPipelineLayout, nullptr); + + // Clean up framebuffers + for (auto fb : m_framebuffers) { + if (fb) vkDestroyFramebuffer(m_device, fb, nullptr); + } + m_framebuffers.clear(); + + // Clean up render pass + if (m_renderPass) vkDestroyRenderPass(m_device, m_renderPass, nullptr); + + m_device = VK_NULL_HANDLE; + m_initialized = false; +} + +bool VulkanRenderer::resize(uint32_t width, uint32_t height) +{ + std::cout << "VulkanRenderer::resize called: " << width << "x" << height + << " (previous: " << m_width << "x" << m_height << ")" << std::endl; + + m_width = width; + m_height = height; + + // Recreate framebuffers with new size + for (auto fb : m_framebuffers) { + if (fb) vkDestroyFramebuffer(m_device, fb, nullptr); + } + m_framebuffers.clear(); + + return createFramebuffers(); +} + +void VulkanRenderer::recordCommandBuffer(VkCommandBuffer commandBuffer, + uint32_t imageIndex, + VkImageView imageView, + int frameCount, + double rotationAngle, + double wavePhase, + bool paintingEnabled, + const std::string& lockInfo) +{ + if (!m_initialized) { + return; + } + + // Check for valid dimensions + if (m_width < 100 || m_height < 100) { + paintingEnabled = false; + } + + // Update geometry buffers BEFORE recording commands + if (paintingEnabled) { + std::vector circleVertices, waveVertices; + std::vector circleIndices, waveIndices; + + // Debug: Print dimensions used for geometry generation + static int geomDebugCounter = 0; + if (geomDebugCounter++ % 300 == 0) { // Every ~5 seconds at 60fps + std::cout << "VulkanRenderer geometry generation using: " + << m_width << "x" << m_height << std::endl; + } + + generateRotatingCircles(circleVertices, circleIndices, rotationAngle); + generateWaveEffect(waveVertices, waveIndices, wavePhase); + + // Update or create circle buffers + if (m_circleVertexBuffer) { + vkDestroyBuffer(m_device, m_circleVertexBuffer, nullptr); + vkFreeMemory(m_device, m_circleVertexMemory, nullptr); + } + if (m_circleIndexBuffer) { + vkDestroyBuffer(m_device, m_circleIndexBuffer, nullptr); + vkFreeMemory(m_device, m_circleIndexMemory, nullptr); + } + + if (!createVertexBuffer(circleVertices, m_circleVertexBuffer, m_circleVertexMemory)) { + return; + } + if (!createIndexBuffer(circleIndices, m_circleIndexBuffer, m_circleIndexMemory)) { + return; + } + m_circleIndexCount = circleIndices.size(); + + // Update or create wave buffers + if (m_waveVertexBuffer) { + vkDestroyBuffer(m_device, m_waveVertexBuffer, nullptr); + vkFreeMemory(m_device, m_waveVertexMemory, nullptr); + } + if (m_waveIndexBuffer) { + vkDestroyBuffer(m_device, m_waveIndexBuffer, nullptr); + vkFreeMemory(m_device, m_waveIndexMemory, nullptr); + } + + if (!createVertexBuffer(waveVertices, m_waveVertexBuffer, m_waveVertexMemory)) { + return; + } + if (!createIndexBuffer(waveIndices, m_waveIndexBuffer, m_waveIndexMemory)) { + return; + } + m_waveIndexCount = waveIndices.size(); + } + + // Update uniform buffer + m_ubo.time = static_cast(frameCount); + m_ubo.resolution[0] = static_cast(m_width); + m_ubo.resolution[1] = static_cast(m_height); + m_ubo.rotation = static_cast(rotationAngle); + m_ubo.wavePhase = static_cast(wavePhase); + + // Use frame count modulo for uniform buffer index (not image index) + uint32_t uniformBufferIndex = static_cast(frameCount) % MAX_FRAMES_IN_FLIGHT; + updateUniformBuffer(uniformBufferIndex); + + // Begin command buffer + VkCommandBufferBeginInfo beginInfo = {}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + VkResult result = vkBeginCommandBuffer(commandBuffer, &beginInfo); + if (result != VK_SUCCESS) { + return; + } + + // Create or get framebuffer for this image view + if (m_framebuffers.empty() || imageIndex >= m_framebuffers.size()) { + // We need to create framebuffers on-the-fly + m_imageViews.clear(); + m_imageViews.push_back(imageView); + createFramebuffers(); + } else if (imageIndex < m_imageViews.size() && m_imageViews[imageIndex] != imageView) { + // Update image view if changed + if (m_framebuffers[imageIndex]) { + vkDestroyFramebuffer(m_device, m_framebuffers[imageIndex], nullptr); + } + m_imageViews[imageIndex] = imageView; + + VkFramebufferCreateInfo fbInfo = {}; + fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fbInfo.renderPass = m_renderPass; + fbInfo.attachmentCount = 1; + fbInfo.pAttachments = &imageView; + fbInfo.width = m_width; + fbInfo.height = m_height; + fbInfo.layers = 1; + vkCreateFramebuffer(m_device, &fbInfo, nullptr, &m_framebuffers[imageIndex]); + } + + // Make sure we have enough image views + while (m_imageViews.size() <= imageIndex) { + m_imageViews.push_back(VK_NULL_HANDLE); + } + m_imageViews[imageIndex] = imageView; + + // Make sure we have enough framebuffers + while (m_framebuffers.size() <= imageIndex) { + m_framebuffers.push_back(VK_NULL_HANDLE); + } + + // Create framebuffer if needed + if (m_framebuffers[imageIndex] == VK_NULL_HANDLE) { + VkFramebufferCreateInfo fbInfo = {}; + fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fbInfo.renderPass = m_renderPass; + fbInfo.attachmentCount = 1; + fbInfo.pAttachments = &imageView; + fbInfo.width = m_width; + fbInfo.height = m_height; + fbInfo.layers = 1; + vkCreateFramebuffer(m_device, &fbInfo, nullptr, &m_framebuffers[imageIndex]); + } + + // Begin render pass + VkRenderPassBeginInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.renderPass = m_renderPass; + renderPassInfo.framebuffer = m_framebuffers[imageIndex]; + renderPassInfo.renderArea.offset = {0, 0}; + renderPassInfo.renderArea.extent = {m_width, m_height}; + + VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; + renderPassInfo.clearValueCount = 1; + renderPassInfo.pClearValues = &clearColor; + + vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + + // Draw background + drawBackground(commandBuffer, frameCount); + + if (paintingEnabled) { + // Draw geometry (circles and waves) + drawGeometry(commandBuffer, rotationAngle, wavePhase); + } + + // Draw text (status info) + drawText(commandBuffer, frameCount, paintingEnabled, lockInfo); + + vkCmdEndRenderPass(commandBuffer); + vkEndCommandBuffer(commandBuffer); +} + +void VulkanRenderer::updateAnimationParams(int frameCount, double rotation, double wavePhase) +{ + m_ubo.time = static_cast(frameCount); + m_ubo.rotation = static_cast(rotation); + m_ubo.wavePhase = static_cast(wavePhase); +} + +VkFramebuffer VulkanRenderer::getFramebuffer(uint32_t index) const +{ + if (index < m_framebuffers.size()) { + return m_framebuffers[index]; + } + return VK_NULL_HANDLE; +} + +bool VulkanRenderer::createRenderPass() +{ + VkAttachmentDescription colorAttachment = {}; + colorAttachment.format = static_cast(m_swapchainFormat); + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference colorAttachmentRef = {}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; + + VkSubpassDependency dependency = {}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + VkRenderPassCreateInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = 1; + renderPassInfo.pAttachments = &colorAttachment; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + renderPassInfo.dependencyCount = 1; + renderPassInfo.pDependencies = &dependency; + + return vkCreateRenderPass(m_device, &renderPassInfo, nullptr, &m_renderPass) == VK_SUCCESS; +} + +bool VulkanRenderer::createFramebuffers() +{ + m_framebuffers.resize(m_imageViews.size()); + + for (size_t i = 0; i < m_imageViews.size(); i++) { + VkFramebufferCreateInfo framebufferInfo = {}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = m_renderPass; + framebufferInfo.attachmentCount = 1; + framebufferInfo.pAttachments = &m_imageViews[i]; + framebufferInfo.width = m_width; + framebufferInfo.height = m_height; + framebufferInfo.layers = 1; + + if (vkCreateFramebuffer(m_device, &framebufferInfo, nullptr, &m_framebuffers[i]) != VK_SUCCESS) { + return false; + } + } + + return true; +} + +VkShaderModule VulkanRenderer::createShaderModule(const std::vector& code) +{ + VkShaderModuleCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); + + VkShaderModule shaderModule; + if (vkCreateShaderModule(m_device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { + return VK_NULL_HANDLE; + } + + return shaderModule; +} + +bool VulkanRenderer::createBackgroundPipeline() +{ + // For now, use simple shaders embedded in code + // In production, load from compiled SPIR-V files + + // Simple vertex shader (SPIR-V bytecode for passthrough with UBO) + static const uint32_t bgVertCode[] = { + #include "shaders_spirv/background_vert.inc" + }; + + static const uint32_t bgFragCode[] = { + #include "shaders_spirv/background_frag.inc" + }; + + // Create shader modules + VkShaderModuleCreateInfo vertModuleInfo = {}; + vertModuleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + vertModuleInfo.codeSize = sizeof(bgVertCode); + vertModuleInfo.pCode = bgVertCode; + + VkShaderModule vertShaderModule; + if (vkCreateShaderModule(m_device, &vertModuleInfo, nullptr, &vertShaderModule) != VK_SUCCESS) { + // Fallback to simple pipeline without shader includes + return createBackgroundPipelineSimple(); + } + + VkShaderModuleCreateInfo fragModuleInfo = {}; + fragModuleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + fragModuleInfo.codeSize = sizeof(bgFragCode); + fragModuleInfo.pCode = bgFragCode; + + VkShaderModule fragShaderModule; + if (vkCreateShaderModule(m_device, &fragModuleInfo, nullptr, &fragShaderModule) != VK_SUCCESS) { + vkDestroyShaderModule(m_device, vertShaderModule, nullptr); + return createBackgroundPipelineSimple(); + } + + // Shader stages + VkPipelineShaderStageCreateInfo vertStageInfo = {}; + vertStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertStageInfo.module = vertShaderModule; + vertStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo fragStageInfo = {}; + fragStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragStageInfo.module = fragShaderModule; + fragStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo shaderStages[] = {vertStageInfo, fragStageInfo}; + + // Vertex input + VkVertexInputBindingDescription bindingDescription = {}; + bindingDescription.binding = 0; + bindingDescription.stride = sizeof(Vertex); + bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + VkVertexInputAttributeDescription attributeDescriptions[3] = {}; + // Position + attributeDescriptions[0].binding = 0; + attributeDescriptions[0].location = 0; + attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT; + attributeDescriptions[0].offset = offsetof(Vertex, pos); + // Color + attributeDescriptions[1].binding = 0; + attributeDescriptions[1].location = 1; + attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT; + attributeDescriptions[1].offset = offsetof(Vertex, color); + // TexCoord + attributeDescriptions[2].binding = 0; + attributeDescriptions[2].location = 2; + attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT; + attributeDescriptions[2].offset = offsetof(Vertex, texCoord); + + VkPipelineVertexInputStateCreateInfo vertexInputInfo = {}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; + vertexInputInfo.vertexAttributeDescriptionCount = 3; + vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions; + + // Input assembly + VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + 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(m_width); + viewport.height = static_cast(m_height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor = {}; + scissor.offset = {0, 0}; + scissor.extent = {m_width, m_height}; + + VkPipelineViewportStateCreateInfo viewportState = {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + // Rasterizer + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + + // Multisampling + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + // Color blending + VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + + VkPipelineColorBlendStateCreateInfo colorBlending = {}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + + // Pipeline layout + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &m_descriptorSetLayout; + + if (vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &m_backgroundPipelineLayout) != VK_SUCCESS) { + vkDestroyShaderModule(m_device, vertShaderModule, nullptr); + vkDestroyShaderModule(m_device, fragShaderModule, nullptr); + return false; + } + + // Create graphics pipeline + VkGraphicsPipelineCreateInfo pipelineInfo = {}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.layout = m_backgroundPipelineLayout; + pipelineInfo.renderPass = m_renderPass; + pipelineInfo.subpass = 0; + + VkResult result = vkCreateGraphicsPipelines(m_device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &m_backgroundPipeline); + + vkDestroyShaderModule(m_device, vertShaderModule, nullptr); + vkDestroyShaderModule(m_device, fragShaderModule, nullptr); + + return result == VK_SUCCESS; +} + +bool VulkanRenderer::createBackgroundPipelineSimple() +{ + // Fallback: create a simple pipeline without external shaders + // This is a placeholder implementation + logError("Shader files not found, using simple color-only rendering"); + return true; +} + +bool VulkanRenderer::createGeometryPipeline() +{ + // Similar to background pipeline but with different blend mode for overlays + // For now, reuse the same structure + + static const uint32_t geomVertCode[] = { + #include "shaders_spirv/geometry_vert.inc" + }; + + static const uint32_t geomFragCode[] = { + #include "shaders_spirv/geometry_frag.inc" + }; + + VkShaderModuleCreateInfo vertModuleInfo = {}; + vertModuleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + vertModuleInfo.codeSize = sizeof(geomVertCode); + vertModuleInfo.pCode = geomVertCode; + + VkShaderModule vertShaderModule; + if (vkCreateShaderModule(m_device, &vertModuleInfo, nullptr, &vertShaderModule) != VK_SUCCESS) { + return false; + } + + VkShaderModuleCreateInfo fragModuleInfo = {}; + fragModuleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + fragModuleInfo.codeSize = sizeof(geomFragCode); + fragModuleInfo.pCode = geomFragCode; + + VkShaderModule fragShaderModule; + if (vkCreateShaderModule(m_device, &fragModuleInfo, nullptr, &fragShaderModule) != VK_SUCCESS) { + vkDestroyShaderModule(m_device, vertShaderModule, nullptr); + return false; + } + + VkPipelineShaderStageCreateInfo shaderStages[2] = {}; + shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + shaderStages[0].module = vertShaderModule; + shaderStages[0].pName = "main"; + shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + shaderStages[1].module = fragShaderModule; + shaderStages[1].pName = "main"; + + // Vertex input + VkVertexInputBindingDescription bindingDescription = {}; + bindingDescription.binding = 0; + bindingDescription.stride = sizeof(Vertex); + bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + VkVertexInputAttributeDescription attributeDescriptions[3] = {}; + attributeDescriptions[0].binding = 0; + attributeDescriptions[0].location = 0; + attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT; + attributeDescriptions[0].offset = offsetof(Vertex, pos); + attributeDescriptions[1].binding = 0; + attributeDescriptions[1].location = 1; + attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT; + attributeDescriptions[1].offset = offsetof(Vertex, color); + attributeDescriptions[2].binding = 0; + attributeDescriptions[2].location = 2; + attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT; + attributeDescriptions[2].offset = offsetof(Vertex, texCoord); + + VkPipelineVertexInputStateCreateInfo vertexInputInfo = {}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; + vertexInputInfo.vertexAttributeDescriptionCount = 3; + vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions; + + VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + 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(m_width); + viewport.height = static_cast(m_height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor = {}; + scissor.offset = {0, 0}; + scissor.extent = {m_width, m_height}; + + VkPipelineViewportStateCreateInfo viewportState = {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_NONE; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + // Enable alpha blending + VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_TRUE; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + + VkPipelineColorBlendStateCreateInfo colorBlending = {}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &m_descriptorSetLayout; + + if (vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &m_geometryPipelineLayout) != VK_SUCCESS) { + vkDestroyShaderModule(m_device, vertShaderModule, nullptr); + vkDestroyShaderModule(m_device, fragShaderModule, nullptr); + return false; + } + + VkGraphicsPipelineCreateInfo pipelineInfo = {}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.layout = m_geometryPipelineLayout; + pipelineInfo.renderPass = m_renderPass; + pipelineInfo.subpass = 0; + + VkResult result = vkCreateGraphicsPipelines(m_device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &m_geometryPipeline); + + vkDestroyShaderModule(m_device, vertShaderModule, nullptr); + vkDestroyShaderModule(m_device, fragShaderModule, nullptr); + + return (result == VK_SUCCESS); +} + +bool VulkanRenderer::createLinePipeline() +{ + // Create a pipeline for line rendering (waves) + + static const uint32_t geomVertCode[] = { + #include "shaders_spirv/geometry_vert.inc" + }; + + static const uint32_t geomFragCode[] = { + #include "shaders_spirv/geometry_frag.inc" + }; + + VkShaderModuleCreateInfo vertModuleInfo = {}; + vertModuleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + vertModuleInfo.codeSize = sizeof(geomVertCode); + vertModuleInfo.pCode = geomVertCode; + + VkShaderModule vertShaderModule; + if (vkCreateShaderModule(m_device, &vertModuleInfo, nullptr, &vertShaderModule) != VK_SUCCESS) { + return false; + } + + VkShaderModuleCreateInfo fragModuleInfo = {}; + fragModuleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + fragModuleInfo.codeSize = sizeof(geomFragCode); + fragModuleInfo.pCode = geomFragCode; + + VkShaderModule fragShaderModule; + if (vkCreateShaderModule(m_device, &fragModuleInfo, nullptr, &fragShaderModule) != VK_SUCCESS) { + vkDestroyShaderModule(m_device, vertShaderModule, nullptr); + return false; + } + + VkPipelineShaderStageCreateInfo shaderStages[2] = {}; + shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + shaderStages[0].module = vertShaderModule; + shaderStages[0].pName = "main"; + shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + shaderStages[1].module = fragShaderModule; + shaderStages[1].pName = "main"; + + // Vertex input + VkVertexInputBindingDescription bindingDescription = {}; + bindingDescription.binding = 0; + bindingDescription.stride = sizeof(Vertex); + bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + VkVertexInputAttributeDescription attributeDescriptions[3] = {}; + attributeDescriptions[0].binding = 0; + attributeDescriptions[0].location = 0; + attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT; + attributeDescriptions[0].offset = offsetof(Vertex, pos); + attributeDescriptions[1].binding = 0; + attributeDescriptions[1].location = 1; + attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT; + attributeDescriptions[1].offset = offsetof(Vertex, color); + attributeDescriptions[2].binding = 0; + attributeDescriptions[2].location = 2; + attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT; + attributeDescriptions[2].offset = offsetof(Vertex, texCoord); + + VkPipelineVertexInputStateCreateInfo vertexInputInfo = {}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; + vertexInputInfo.vertexAttributeDescriptionCount = 3; + vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions; + + VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + 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(m_width); + viewport.height = static_cast(m_height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor = {}; + scissor.offset = {0, 0}; + scissor.extent = {m_width, m_height}; + + VkPipelineViewportStateCreateInfo viewportState = {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 2.0f; // Thicker lines for visibility + rasterizer.cullMode = VK_CULL_MODE_NONE; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + // Enable alpha blending + VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_TRUE; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + + VkPipelineColorBlendStateCreateInfo colorBlending = {}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &m_descriptorSetLayout; + + if (vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &m_linePipelineLayout) != VK_SUCCESS) { + vkDestroyShaderModule(m_device, vertShaderModule, nullptr); + vkDestroyShaderModule(m_device, fragShaderModule, nullptr); + return false; + } + + VkGraphicsPipelineCreateInfo pipelineInfo = {}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.layout = m_linePipelineLayout; + pipelineInfo.renderPass = m_renderPass; + pipelineInfo.subpass = 0; + + VkResult result = vkCreateGraphicsPipelines(m_device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &m_linePipeline); + + vkDestroyShaderModule(m_device, vertShaderModule, nullptr); + vkDestroyShaderModule(m_device, fragShaderModule, nullptr); + + return (result == VK_SUCCESS); +} + +bool VulkanRenderer::createTextPipeline() +{ + // Text rendering pipeline with texture sampling + // Not implemented yet - would require font texture atlas + return true; +} + +bool VulkanRenderer::createDescriptorSetLayout() +{ + VkDescriptorSetLayoutBinding uboLayoutBinding = {}; + uboLayoutBinding.binding = 0; + uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uboLayoutBinding.descriptorCount = 1; + uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + + VkDescriptorSetLayoutCreateInfo layoutInfo = {}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = 1; + layoutInfo.pBindings = &uboLayoutBinding; + + return vkCreateDescriptorSetLayout(m_device, &layoutInfo, nullptr, &m_descriptorSetLayout) == VK_SUCCESS; +} + +bool VulkanRenderer::createDescriptorPool() +{ + VkDescriptorPoolSize poolSize = {}; + poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + poolSize.descriptorCount = MAX_FRAMES_IN_FLIGHT; + + VkDescriptorPoolCreateInfo poolInfo = {}; + poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolInfo.poolSizeCount = 1; + poolInfo.pPoolSizes = &poolSize; + poolInfo.maxSets = MAX_FRAMES_IN_FLIGHT; + + return vkCreateDescriptorPool(m_device, &poolInfo, nullptr, &m_descriptorPool) == VK_SUCCESS; +} + +bool VulkanRenderer::createDescriptorSets() +{ + std::vector layouts(MAX_FRAMES_IN_FLIGHT, m_descriptorSetLayout); + + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = m_descriptorPool; + allocInfo.descriptorSetCount = MAX_FRAMES_IN_FLIGHT; + allocInfo.pSetLayouts = layouts.data(); + + m_descriptorSets.resize(MAX_FRAMES_IN_FLIGHT); + if (vkAllocateDescriptorSets(m_device, &allocInfo, m_descriptorSets.data()) != VK_SUCCESS) { + return false; + } + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + VkDescriptorBufferInfo bufferInfo = {}; + bufferInfo.buffer = m_uniformBuffers[i]; + bufferInfo.offset = 0; + bufferInfo.range = sizeof(UniformBufferObject); + + VkWriteDescriptorSet descriptorWrite = {}; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.dstSet = m_descriptorSets[i]; + descriptorWrite.dstBinding = 0; + descriptorWrite.dstArrayElement = 0; + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrite.descriptorCount = 1; + descriptorWrite.pBufferInfo = &bufferInfo; + + vkUpdateDescriptorSets(m_device, 1, &descriptorWrite, 0, nullptr); + } + + return true; +} + +bool VulkanRenderer::createUniformBuffers() +{ + VkDeviceSize bufferSize = sizeof(UniformBufferObject); + + m_uniformBuffers.resize(MAX_FRAMES_IN_FLIGHT); + m_uniformBuffersMemory.resize(MAX_FRAMES_IN_FLIGHT); + m_uniformBuffersMapped.resize(MAX_FRAMES_IN_FLIGHT); + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + if (!createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + m_uniformBuffers[i], m_uniformBuffersMemory[i])) { + logError("Failed to create uniform buffer"); + return false; + } + + VkResult result = vkMapMemory(m_device, m_uniformBuffersMemory[i], 0, bufferSize, 0, &m_uniformBuffersMapped[i]); + if (result != VK_SUCCESS) { + logError("Failed to map uniform buffer memory"); + return false; + } + + if (m_uniformBuffersMapped[i] == nullptr) { + logError("Uniform buffer mapped pointer is null"); + return false; + } + } + + return true; +} + +void VulkanRenderer::updateUniformBuffer(uint32_t currentImage) +{ + if (currentImage < m_uniformBuffersMapped.size()) { + if (m_uniformBuffersMapped[currentImage] != nullptr) { + memcpy(m_uniformBuffersMapped[currentImage], &m_ubo, sizeof(m_ubo)); + } else { + logError("Uniform buffer mapped pointer is null"); + } + } else { + std::cerr << "VulkanRenderer::updateUniformBuffer - currentImage " << currentImage + << " out of range (size: " << m_uniformBuffersMapped.size() << ")" << std::endl; + } +} + +bool VulkanRenderer::createVertexBuffer(const std::vector& vertices, + VkBuffer& buffer, VkDeviceMemory& memory) +{ + if (vertices.empty()) { + logError("Cannot create vertex buffer from empty vertices"); + return false; + } + + VkDeviceSize bufferSize = sizeof(Vertex) * vertices.size(); + + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + if (!createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + stagingBuffer, stagingBufferMemory)) { + logError("Failed to create staging buffer for vertex buffer"); + return false; + } + + void* data = nullptr; + VkResult result = vkMapMemory(m_device, stagingBufferMemory, 0, bufferSize, 0, &data); + if (result != VK_SUCCESS || data == nullptr) { + logError("Failed to map staging buffer memory for vertex buffer"); + vkDestroyBuffer(m_device, stagingBuffer, nullptr); + vkFreeMemory(m_device, stagingBufferMemory, nullptr); + return false; + } + + memcpy(data, vertices.data(), bufferSize); + vkUnmapMemory(m_device, stagingBufferMemory); + + if (!createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, buffer, memory)) { + logError("Failed to create device local vertex buffer"); + vkDestroyBuffer(m_device, stagingBuffer, nullptr); + vkFreeMemory(m_device, stagingBufferMemory, nullptr); + return false; + } + + if (!copyBuffer(stagingBuffer, buffer, bufferSize)) { + logError("Failed to copy staging buffer to vertex buffer"); + vkDestroyBuffer(m_device, stagingBuffer, nullptr); + vkFreeMemory(m_device, stagingBufferMemory, nullptr); + return false; + } + + vkDestroyBuffer(m_device, stagingBuffer, nullptr); + vkFreeMemory(m_device, stagingBufferMemory, nullptr); + + return true; +} + +bool VulkanRenderer::createIndexBuffer(const std::vector& indices, + VkBuffer& buffer, VkDeviceMemory& memory) +{ + if (indices.empty()) { + logError("Cannot create index buffer from empty indices"); + return false; + } + + VkDeviceSize bufferSize = sizeof(uint16_t) * indices.size(); + + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + if (!createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + stagingBuffer, stagingBufferMemory)) { + logError("Failed to create staging buffer for index buffer"); + return false; + } + + void* data = nullptr; + VkResult result = vkMapMemory(m_device, stagingBufferMemory, 0, bufferSize, 0, &data); + if (result != VK_SUCCESS || data == nullptr) { + logError("Failed to map staging buffer memory for index buffer"); + vkDestroyBuffer(m_device, stagingBuffer, nullptr); + vkFreeMemory(m_device, stagingBufferMemory, nullptr); + return false; + } + + memcpy(data, indices.data(), bufferSize); + vkUnmapMemory(m_device, stagingBufferMemory); + + if (!createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, buffer, memory)) { + logError("Failed to create device local index buffer"); + vkDestroyBuffer(m_device, stagingBuffer, nullptr); + vkFreeMemory(m_device, stagingBufferMemory, nullptr); + return false; + } + + if (!copyBuffer(stagingBuffer, buffer, bufferSize)) { + logError("Failed to copy staging buffer to index buffer"); + vkDestroyBuffer(m_device, stagingBuffer, nullptr); + vkFreeMemory(m_device, stagingBufferMemory, nullptr); + return false; + } + + vkDestroyBuffer(m_device, stagingBuffer, nullptr); + vkFreeMemory(m_device, stagingBufferMemory, nullptr); + + return true; +} + +bool VulkanRenderer::createBuffer(uint64_t size, uint32_t usage, uint32_t properties, + VkBuffer& buffer, VkDeviceMemory& memory) +{ + VkBufferCreateInfo bufferInfo = {}; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = size; + bufferInfo.usage = usage; + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + if (vkCreateBuffer(m_device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { + return false; + } + + VkMemoryRequirements memRequirements; + vkGetBufferMemoryRequirements(m_device, buffer, &memRequirements); + + VkMemoryAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); + + if (vkAllocateMemory(m_device, &allocInfo, nullptr, &memory) != VK_SUCCESS) { + vkDestroyBuffer(m_device, buffer, nullptr); + return false; + } + + vkBindBufferMemory(m_device, buffer, memory, 0); + return true; +} + +bool VulkanRenderer::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, uint64_t size) +{ + if (srcBuffer == VK_NULL_HANDLE || dstBuffer == VK_NULL_HANDLE) { + logError("Cannot copy buffer: source or destination is null"); + return false; + } + + if (m_graphicsQueue == VK_NULL_HANDLE) { + logError("Cannot copy buffer: graphics queue is null"); + return false; + } + + // Create a one-time command pool if not exists + if (m_transferCommandPool == VK_NULL_HANDLE) { + VkCommandPoolCreateInfo poolInfo = {}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; + poolInfo.queueFamilyIndex = m_queueFamilyIndex; + + VkResult result = vkCreateCommandPool(m_device, &poolInfo, nullptr, &m_transferCommandPool); + if (result != VK_SUCCESS) { + logError("Failed to create transfer command pool"); + return false; + } + } + + // Allocate a one-time command buffer + VkCommandBufferAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandPool = m_transferCommandPool; + allocInfo.commandBufferCount = 1; + + VkCommandBuffer commandBuffer; + VkResult result = vkAllocateCommandBuffers(m_device, &allocInfo, &commandBuffer); + if (result != VK_SUCCESS) { + logError("Failed to allocate command buffer for copy"); + return false; + } + + // Begin recording + VkCommandBufferBeginInfo beginInfo = {}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + result = vkBeginCommandBuffer(commandBuffer, &beginInfo); + if (result != VK_SUCCESS) { + logError("Failed to begin command buffer for copy"); + vkFreeCommandBuffers(m_device, m_transferCommandPool, 1, &commandBuffer); + return false; + } + + // Copy command + VkBufferCopy copyRegion = {}; + copyRegion.srcOffset = 0; + copyRegion.dstOffset = 0; + copyRegion.size = size; + vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region); + + result = vkEndCommandBuffer(commandBuffer); + if (result != VK_SUCCESS) { + logError("Failed to end command buffer for copy"); + vkFreeCommandBuffers(m_device, m_transferCommandPool, 1, &commandBuffer); + return false; + } + + // Submit and wait + VkSubmitInfo submitInfo = {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + result = vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); + if (result != VK_SUCCESS) { + logError("Failed to submit copy command"); + vkFreeCommandBuffers(m_device, m_transferCommandPool, 1, &commandBuffer); + return false; + } + + result = vkQueueWaitIdle(m_graphicsQueue); + if (result != VK_SUCCESS) { + logError("Failed to wait for queue idle after copy"); + } + + // Free command buffer + vkFreeCommandBuffers(m_device, m_transferCommandPool, 1, &commandBuffer); + + return true; +} + +uint32_t VulkanRenderer::findMemoryType(uint32_t typeFilter, uint32_t properties) +{ + VkPhysicalDeviceMemoryProperties memProperties; + vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memProperties); + + for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { + if ((typeFilter & (1 << i)) && + (memProperties.memoryTypes[i].propertyFlags & properties) == properties) { + return i; + } + } + + return 0; +} + +bool VulkanRenderer::initializeTextRendering() +{ + // Simplified text rendering - just create simple bitmap font + // In production, use FreeType or stb_truetype + return true; +} + +bool VulkanRenderer::createFontAtlas() +{ + // Create a simple font texture atlas + // Not implemented yet + return true; +} + +void VulkanRenderer::generateBackgroundQuad(std::vector& vertices, + std::vector& indices) +{ + // Full-screen quad in NDC space + vertices = { + {{-1.0f, -1.0f}, {1.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f}}, + {{ 1.0f, -1.0f}, {0.0f, 1.0f, 0.0f, 1.0f}, {1.0f, 0.0f}}, + {{ 1.0f, 1.0f}, {0.0f, 0.0f, 1.0f, 1.0f}, {1.0f, 1.0f}}, + {{-1.0f, 1.0f}, {1.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 1.0f}} + }; + + indices = {0, 1, 2, 2, 3, 0}; +} + +void VulkanRenderer::generateRotatingCircles(std::vector& vertices, + std::vector& indices, + double rotation) +{ + vertices.clear(); + indices.clear(); + + // Safety check for dimensions + if (m_width == 0 || m_height == 0) { + std::cout << "WARNING: generateRotatingCircles called with zero dimensions!" << std::endl; + return; + } + + 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 + + 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); + float y = centerY + orbitRadius * sin(angle); + + // HSV to RGB for color - more vibrant colors + float hue = fmod((i * 360.0f / numCircles + rotation), 360.0f) / 360.0f; + float r = std::abs(sin(hue * 6.28318f)) * 0.8f + 0.2f; + float g = std::abs(sin((hue + 0.33f) * 6.28318f)) * 0.8f + 0.2f; + float b = std::abs(sin((hue + 0.66f) * 6.28318f)) * 0.8f + 0.2f; + + // Create circle as triangles + int segments = 16; + uint16_t centerIdx = vertices.size(); + + // Center vertex with more opaque alpha + vertices.push_back({{x, y}, {r, g, b, 0.9f}, {0.5f, 0.5f}}); + + for (int j = 0; j <= segments; j++) { + float segAngle = j * 2.0f * M_PI / segments; + float vx = x + circleRadius * cos(segAngle); + float vy = y + circleRadius * sin(segAngle); + vertices.push_back({{vx, vy}, {r, g, b, 0.9f}, {0.0f, 0.0f}}); + + if (j > 0) { + indices.push_back(centerIdx); + indices.push_back(centerIdx + j); + indices.push_back(centerIdx + j + 1); + } + } + } +} + +void VulkanRenderer::generateWaveEffect(std::vector& vertices, + std::vector& indices, + double wavePhase) +{ + vertices.clear(); + indices.clear(); + + // Safety check for dimensions + if (m_width == 0 || m_height == 0) { + std::cout << "WARNING: generateWaveEffect called with zero dimensions!" << std::endl; + return; + } + + int numPoints = m_width / 5 + 1; + float waveY = m_height * 0.7f; + float amplitude = 30.0f; + + // First wave + for (int i = 0; i < numPoints; i++) { + float x = i * 5.0f; + float y = waveY + amplitude * sin(wavePhase + x * 0.02); + vertices.push_back({{x, y}, {1.0f, 1.0f, 1.0f, 0.6f}, {0.0f, 0.0f}}); + } + + // Line list indices for first wave (pairs of points) + for (int i = 0; i < numPoints - 1; i++) { + indices.push_back(i); + indices.push_back(i + 1); + } + + // Second wave (phase shifted) + uint16_t offset = vertices.size(); + for (int i = 0; i < numPoints; i++) { + float x = i * 5.0f; + float y = waveY + amplitude * sin(wavePhase + M_PI + x * 0.02); + vertices.push_back({{x, y}, {1.0f, 1.0f, 0.4f, 0.5f}, {0.0f, 0.0f}}); + } + + // Line list indices for second wave (pairs of points) + for (int i = 0; i < numPoints - 1; i++) { + indices.push_back(offset + i); + indices.push_back(offset + i + 1); + } +} + +void VulkanRenderer::generateTextQuads(const std::string& text, float x, float y, + float scale, const float color[4], + std::vector& vertices, + std::vector& indices) +{ + // Simplified text rendering - not implemented yet + // Would require font atlas texture +} + +void VulkanRenderer::drawBackground(VkCommandBuffer commandBuffer, int frameCount) +{ + if (m_backgroundPipeline == VK_NULL_HANDLE || m_backgroundVertexBuffer == VK_NULL_HANDLE || + m_backgroundPipelineLayout == VK_NULL_HANDLE || m_descriptorSets.empty()) { + return; + } + + 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(m_ubo.time) % MAX_FRAMES_IN_FLIGHT; + if (frameIndex >= m_descriptorSets.size()) { + return; + } + + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + m_backgroundPipelineLayout, 0, 1, &m_descriptorSets[frameIndex], 0, nullptr); + + VkBuffer vertexBuffers[] = {m_backgroundVertexBuffer}; + VkDeviceSize offsets[] = {0}; + vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); + vkCmdBindIndexBuffer(commandBuffer, m_backgroundIndexBuffer, 0, VK_INDEX_TYPE_UINT16); + vkCmdDrawIndexed(commandBuffer, m_backgroundIndexCount, 1, 0, 0, 0); +} + +void VulkanRenderer::drawGeometry(VkCommandBuffer commandBuffer, double rotation, double wavePhase) +{ + if (m_geometryPipeline == VK_NULL_HANDLE || m_geometryPipelineLayout == VK_NULL_HANDLE || + m_descriptorSets.empty()) { + return; + } + + uint32_t frameIndex = static_cast(m_ubo.time) % MAX_FRAMES_IN_FLIGHT; + if (frameIndex >= m_descriptorSets.size()) { + return; + } + + // Draw circles with geometry pipeline + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_geometryPipeline); + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + m_geometryPipelineLayout, 0, 1, &m_descriptorSets[frameIndex], 0, nullptr); + + if (m_circleVertexBuffer != VK_NULL_HANDLE && m_circleIndexBuffer != VK_NULL_HANDLE && m_circleIndexCount > 0) { + VkBuffer vertexBuffers[] = {m_circleVertexBuffer}; + VkDeviceSize offsets[] = {0}; + vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); + vkCmdBindIndexBuffer(commandBuffer, m_circleIndexBuffer, 0, VK_INDEX_TYPE_UINT16); + vkCmdDrawIndexed(commandBuffer, m_circleIndexCount, 1, 0, 0, 0); + } + + // Draw waves with line pipeline + if (m_linePipeline != VK_NULL_HANDLE && m_linePipelineLayout != VK_NULL_HANDLE && + m_waveVertexBuffer != VK_NULL_HANDLE && m_waveIndexBuffer != VK_NULL_HANDLE && m_waveIndexCount > 0) { + + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_linePipeline); + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + m_linePipelineLayout, 0, 1, &m_descriptorSets[frameIndex], 0, nullptr); + + VkBuffer waveBuffers[] = {m_waveVertexBuffer}; + VkDeviceSize offsets[] = {0}; + vkCmdBindVertexBuffers(commandBuffer, 0, 1, waveBuffers, offsets); + vkCmdBindIndexBuffer(commandBuffer, m_waveIndexBuffer, 0, VK_INDEX_TYPE_UINT16); + vkCmdDrawIndexed(commandBuffer, m_waveIndexCount, 1, 0, 0, 0); + } +} + +void VulkanRenderer::drawText(VkCommandBuffer commandBuffer, int frameCount, + bool paintingEnabled, const std::string& lockInfo) +{ + // Text rendering not fully implemented yet + // Would require font texture atlas and text pipeline +} + +void VulkanRenderer::logError(const char* message) +{ + if (m_errorCallback) { + m_errorCallback(message); + } else { + std::cerr << "VulkanRenderer Error: " << message << std::endl; + } +} \ No newline at end of file diff --git a/src/vulkanrenderer.h b/src/vulkanrenderer.h new file mode 100644 index 0000000..131b7fb --- /dev/null +++ b/src/vulkanrenderer.h @@ -0,0 +1,370 @@ +#ifndef VULKANRENDERER_H +#define VULKANRENDERER_H + +#include +#include +#include +#include +#include + +// Forward declare Vulkan types +typedef struct VkDevice_T* VkDevice; +typedef struct VkPhysicalDevice_T* VkPhysicalDevice; +typedef struct VkCommandBuffer_T* VkCommandBuffer; +typedef struct VkRenderPass_T* VkRenderPass; +typedef struct VkFramebuffer_T* VkFramebuffer; +typedef struct VkPipeline_T* VkPipeline; +typedef struct VkPipelineLayout_T* VkPipelineLayout; +typedef struct VkDescriptorSetLayout_T* VkDescriptorSetLayout; +typedef struct VkDescriptorPool_T* VkDescriptorPool; +typedef struct VkDescriptorSet_T* VkDescriptorSet; +typedef struct VkBuffer_T* VkBuffer; +typedef struct VkDeviceMemory_T* VkDeviceMemory; +typedef struct VkImage_T* VkImage; +typedef struct VkImageView_T* VkImageView; +typedef struct VkSampler_T* VkSampler; +typedef struct VkShaderModule_T* VkShaderModule; +typedef struct VkQueue_T* VkQueue; +typedef struct VkCommandPool_T* VkCommandPool; + +/** + * @brief 顶点数据结构 + */ +struct Vertex { + float pos[2]; // 位置 (x, y) + float color[4]; // 颜色 (r, g, b, a) + float texCoord[2]; // 纹理坐标 (u, v) +}; + +/** + * @brief Uniform Buffer 数据结构 + */ +struct UniformBufferObject { + float time; // 当前时间/帧数 + float resolution[2]; // 分辨率 (width, height) + float rotation; // 旋转角度 + float wavePhase; // 波浪相位 + float padding[2]; // 对齐到 16 字节边界 +}; + +/** + * @brief 字符信息结构 + */ +struct CharInfo { + float texCoords[4]; // x, y, width, height in texture atlas + float size[2]; // width, height in pixels + float bearing[2]; // bearing x, y + float advance; // advance for next character +}; + +/** + * @brief Vulkan渲染器类 + * + * 负责所有Vulkan渲染操作,包括几何体、文字、特效等 + */ +class VulkanRenderer +{ +public: + VulkanRenderer(); + ~VulkanRenderer(); + + /** + * @brief 初始化渲染器 + * @param device Vulkan逻辑设备 + * @param physicalDevice Vulkan物理设备 + * @param graphicsQueue 图形队列 + * @param queueFamilyIndex 队列族索引 + * @param swapchainFormat 交换链图像格式 + * @param width 渲染目标宽度 + * @param height 渲染目标高度 + * @return true表示成功 + */ + bool initialize(VkDevice device, VkPhysicalDevice physicalDevice, + VkQueue graphicsQueue, uint32_t queueFamilyIndex, + uint32_t swapchainFormat, uint32_t width, uint32_t height); + + /** + * @brief 清理渲染器资源 + */ + void cleanup(); + + /** + * @brief 调整大小 + * @param width 新宽度 + * @param height 新高度 + * @return true表示成功 + */ + bool resize(uint32_t width, uint32_t height); + + /** + * @brief 录制渲染命令 + * @param commandBuffer 命令缓冲区 + * @param imageIndex 交换链图像索引 + * @param imageView 交换链图像视图 + * @param frameCount 当前帧数 + * @param rotationAngle 旋转角度 + * @param wavePhase 波浪相位 + * @param paintingEnabled 是否启用绘制 + * @param lockInfo 锁屏信息 + */ + void recordCommandBuffer(VkCommandBuffer commandBuffer, + uint32_t imageIndex, + VkImageView imageView, + int frameCount, + double rotationAngle, + double wavePhase, + bool paintingEnabled, + const std::string& lockInfo); + + /** + * @brief 更新动画参数 + * @param frameCount 帧数 + * @param rotation 旋转角度 + * @param wavePhase 波浪相位 + */ + void updateAnimationParams(int frameCount, double rotation, double wavePhase); + + /** + * @brief 获取渲染通道 + */ + VkRenderPass getRenderPass() const { return m_renderPass; } + + /** + * @brief 获取帧缓冲 + */ + VkFramebuffer getFramebuffer(uint32_t index) const; + + /** + * @brief 设置错误回调 + */ + void setErrorCallback(void (*callback)(const char*)) { m_errorCallback = callback; } + +private: + /** + * @brief 创建渲染通道 + */ + bool createRenderPass(); + + /** + * @brief 创建帧缓冲 + */ + bool createFramebuffers(); + + /** + * @brief 创建着色器模块 + */ + VkShaderModule createShaderModule(const std::vector& code); + + /** + * @brief 创建图形管线(背景渐变) + */ + bool createBackgroundPipeline(); + + /** + * @brief 创建简单背景管线(fallback) + */ + bool createBackgroundPipelineSimple(); + + /** + * @brief 创建图形管线(几何体 - 圆圈、波浪) + */ + bool createGeometryPipeline(); + + /** + * @brief 创建线条渲染管线(用于波浪线) + */ + bool createLinePipeline(); + + /** + * @brief 创建图形管线(文本渲染) + */ + bool createTextPipeline(); + + /** + * @brief 创建描述符集布局 + */ + bool createDescriptorSetLayout(); + + /** + * @brief 创建描述符池 + */ + bool createDescriptorPool(); + + /** + * @brief 创建描述符集 + */ + bool createDescriptorSets(); + + /** + * @brief 创建Uniform Buffer + */ + bool createUniformBuffers(); + + /** + * @brief 更新Uniform Buffer + */ + void updateUniformBuffer(uint32_t currentImage); + + /** + * @brief 创建顶点缓冲区 + */ + bool createVertexBuffer(const std::vector& vertices, + VkBuffer& buffer, VkDeviceMemory& memory); + + /** + * @brief 创建索引缓冲区 + */ + bool createIndexBuffer(const std::vector& indices, + VkBuffer& buffer, VkDeviceMemory& memory); + + /** + * @brief 创建缓冲区 + */ + bool createBuffer(uint64_t size, uint32_t usage, uint32_t properties, + VkBuffer& buffer, VkDeviceMemory& memory); + + /** + * @brief 复制缓冲区 + */ + bool copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, uint64_t size); + + /** + * @brief 查找内存类型 + */ + uint32_t findMemoryType(uint32_t typeFilter, uint32_t properties); + + /** + * @brief 初始化文本渲染系统 + */ + bool initializeTextRendering(); + + /** + * @brief 创建字体纹理图集 + */ + bool createFontAtlas(); + + /** + * @brief 生成背景四边形顶点 + */ + void generateBackgroundQuad(std::vector& vertices, + std::vector& indices); + + /** + * @brief 生成旋转圆圈顶点 + */ + void generateRotatingCircles(std::vector& vertices, + std::vector& indices, + double rotation); + + /** + * @brief 生成波浪效果顶点 + */ + void generateWaveEffect(std::vector& vertices, + std::vector& indices, + double wavePhase); + + /** + * @brief 生成文本四边形顶点 + */ + void generateTextQuads(const std::string& text, float x, float y, + float scale, const float color[4], + std::vector& vertices, + std::vector& indices); + + /** + * @brief 绘制背景 + */ + void drawBackground(VkCommandBuffer commandBuffer, int frameCount); + + /** + * @brief 绘制几何体(圆圈和波浪) + */ + void drawGeometry(VkCommandBuffer commandBuffer, double rotation, double wavePhase); + + /** + * @brief 绘制文本 + */ + void drawText(VkCommandBuffer commandBuffer, int frameCount, + bool paintingEnabled, const std::string& lockInfo); + + /** + * @brief 记录错误 + */ + void logError(const char* message); + +private: + // Vulkan 对象 + VkDevice m_device; + VkPhysicalDevice m_physicalDevice; + VkQueue m_graphicsQueue; + uint32_t m_queueFamilyIndex; + VkCommandPool m_transferCommandPool; + VkRenderPass m_renderPass; + std::vector m_framebuffers; + std::vector m_imageViews; + + // 管线对象 + VkPipeline m_backgroundPipeline; + VkPipelineLayout m_backgroundPipelineLayout; + VkPipeline m_geometryPipeline; + VkPipelineLayout m_geometryPipelineLayout; + VkPipeline m_linePipeline; + VkPipelineLayout m_linePipelineLayout; + VkPipeline m_textPipeline; + VkPipelineLayout m_textPipelineLayout; + + // 描述符对象 + VkDescriptorSetLayout m_descriptorSetLayout; + VkDescriptorPool m_descriptorPool; + std::vector m_descriptorSets; + + // Uniform Buffer + std::vector m_uniformBuffers; + std::vector m_uniformBuffersMemory; + std::vector m_uniformBuffersMapped; + + // 几何体缓冲区 + VkBuffer m_backgroundVertexBuffer; + VkDeviceMemory m_backgroundVertexMemory; + VkBuffer m_backgroundIndexBuffer; + VkDeviceMemory m_backgroundIndexMemory; + uint32_t m_backgroundIndexCount; + + VkBuffer m_circleVertexBuffer; + VkDeviceMemory m_circleVertexMemory; + VkBuffer m_circleIndexBuffer; + VkDeviceMemory m_circleIndexMemory; + uint32_t m_circleIndexCount; + + VkBuffer m_waveVertexBuffer; + VkDeviceMemory m_waveVertexMemory; + VkBuffer m_waveIndexBuffer; + VkDeviceMemory m_waveIndexMemory; + uint32_t m_waveIndexCount; + + // 文本渲染资源 + VkImage m_fontTexture; + VkDeviceMemory m_fontTextureMemory; + VkImageView m_fontTextureView; + VkSampler m_fontSampler; + std::map m_charInfoMap; + + // 渲染目标尺寸 + uint32_t m_width; + uint32_t m_height; + uint32_t m_swapchainFormat; + + // 动画参数 + UniformBufferObject m_ubo; + + // 初始化状态 + bool m_initialized; + + // 错误回调 + void (*m_errorCallback)(const char*); + + // 最大帧数 + static const int MAX_FRAMES_IN_FLIGHT = 2; +}; + +#endif // VULKANRENDERER_H \ No newline at end of file diff --git a/src/vulkanwidget.cpp b/src/vulkanwidget.cpp index 1f40c4c..23c0e73 100644 --- a/src/vulkanwidget.cpp +++ b/src/vulkanwidget.cpp @@ -1,4 +1,5 @@ #include "vulkanwidget.h" +#include "vulkanrenderer.h" #include #include #include @@ -13,6 +14,10 @@ #define VK_NO_PROTOTYPES #include "../../third_party/volk/volk.h" +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + // Platform-specific headers #ifdef _WIN32 #include @@ -38,6 +43,14 @@ VulkanWidget::VulkanWidget(QWidget *parent) , m_surfaceWidth(0) , m_surfaceHeight(0) , m_renderTimer(nullptr) + , m_renderer(nullptr) + , m_rotationAngle(0.0) + , m_wavePhase(0.0) + , m_startTime(QDateTime::currentDateTime()) + , m_lastLockDuration(0) + , m_lastLockFrameCount(0) + , m_lockPaintFrameCount(0) + , m_lockCount(0) { // Set widget attributes for native window setAttribute(Qt::WA_NativeWindow); @@ -59,6 +72,13 @@ VulkanWidget::~VulkanWidget() m_renderTimer->stop(); } + // Clean up renderer first + if (m_renderer) { + m_renderer->cleanup(); + delete m_renderer; + m_renderer = nullptr; + } + cleanupVulkan(); qDebug() << "VulkanWidget destroyed"; @@ -121,9 +141,40 @@ bool VulkanWidget::initializeVulkan() return false; } - m_initialized = true; - qDebug() << "Vulkan initialization successful"; + // Step 9: Create VulkanRenderer - but only if we have reasonable dimensions + if (m_surfaceWidth >= 100 && m_surfaceHeight >= 100) { + m_renderer = new VulkanRenderer(); + + // Get swapchain format + VkSurfaceFormatKHR surfaceFormat; + uint32_t formatCount = 0; + vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, nullptr); + if (formatCount > 0) { + std::vector formats(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, formats.data()); + surfaceFormat = formats[0]; + } + + // Initialize renderer + if (!m_renderer->initialize(m_device, m_physicalDevice, + m_queue, m_queueFamilyIndex, + static_cast(surfaceFormat.format), + m_surfaceWidth, m_surfaceHeight)) { + setError("Failed to initialize VulkanRenderer"); + delete m_renderer; + m_renderer = nullptr; + return false; + } + + qDebug() << "VulkanRenderer initialized successfully!"; + qDebug() << "Widget size:" << width() << "x" << height() + << "| Surface size:" << m_surfaceWidth << "x" << m_surfaceHeight; + } else { + qDebug() << "VulkanRenderer initialization deferred - window too small:" << m_surfaceWidth << "x" << m_surfaceHeight; + } + m_initialized = true; + qDebug() << "Vulkan initialization successful!"; return true; } @@ -424,6 +475,33 @@ bool VulkanWidget::createSwapchain() vkGetSwapchainImagesKHR(m_device, m_swapchain, &imageCount, reinterpret_cast(m_swapchainImages.data())); + // Create image views for swapchain images + m_swapchainImageViews.resize(imageCount); + for (uint32_t i = 0; i < imageCount; i++) { + VkImageViewCreateInfo viewInfo = {}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = reinterpret_cast(m_swapchainImages[i]); + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = surfaceFormat.format; + viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = 1; + + VkImageView imageView; + result = vkCreateImageView(m_device, &viewInfo, nullptr, &imageView); + if (result != VK_SUCCESS) { + qDebug() << "Failed to create image view" << i << ", error code:" << result; + return false; + } + m_swapchainImageViews[i] = reinterpret_cast(imageView); + } + qDebug() << "Swapchain created successfully with" << imageCount << "images, size:" << m_surfaceWidth << "x" << m_surfaceHeight; @@ -507,6 +585,12 @@ bool VulkanWidget::recreateSwapchain() return false; } + // Update renderer with new surface dimensions + if (m_renderer) { + qDebug() << "Updating VulkanRenderer to surface size:" << m_surfaceWidth << "x" << m_surfaceHeight; + m_renderer->resize(m_surfaceWidth, m_surfaceHeight); + } + qDebug() << "Swapchain recreated successfully"; return true; } @@ -589,12 +673,70 @@ void VulkanWidget::renderFrame() void VulkanWidget::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) { + // Initialize renderer now if we didn't do it earlier (window was too small) + if (!m_renderer && m_surfaceWidth >= 100 && m_surfaceHeight >= 100) { + qDebug() << "Creating deferred VulkanRenderer with actual surface size:" << m_surfaceWidth << "x" << m_surfaceHeight; + m_renderer = new VulkanRenderer(); + + VkSurfaceFormatKHR surfaceFormat; + uint32_t formatCount = 0; + vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, nullptr); + if (formatCount > 0) { + std::vector formats(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, formats.data()); + surfaceFormat = formats[0]; + } + + if (!m_renderer->initialize(m_device, m_physicalDevice, + m_queue, m_queueFamilyIndex, + static_cast(surfaceFormat.format), + m_surfaceWidth, m_surfaceHeight)) { + qDebug() << "Failed to initialize deferred VulkanRenderer"; + delete m_renderer; + m_renderer = nullptr; + } else { + qDebug() << "Deferred VulkanRenderer initialized successfully with surface size:" + << m_surfaceWidth << "x" << m_surfaceHeight; + } + } + + // Use VulkanRenderer if available + if (m_renderer) { + if (imageIndex >= m_swapchainImageViews.size()) { + qDebug() << "ERROR: imageIndex out of bounds!"; + return; + } + + VkImageView imageView = reinterpret_cast(m_swapchainImageViews[imageIndex]); + + // Build lock info string + QString lockInfo; + if (m_lastLockTime.isValid()) { + lockInfo = QString("Last Lock: %1\nDuration: %2s\nLock Count: %3") + .arg(m_lastLockTime.toString("hh:mm:ss")) + .arg(m_lastLockDuration) + .arg(m_lockCount); + } + + // Debug: Print dimensions occasionally to check for mismatch + static int debugCounter = 0; + if (debugCounter++ % 300 == 0) { // Every ~5 seconds at 60fps + qDebug() << "Rendering - Widget:" << width() << "x" << height() + << "| Surface:" << m_surfaceWidth << "x" << m_surfaceHeight; + } + + m_renderer->recordCommandBuffer(commandBuffer, imageIndex, imageView, + m_frameCount, m_rotationAngle, m_wavePhase, + m_renderingEnabled, lockInfo.toStdString()); + return; + } + + // Fallback: Simple clear color pass VkCommandBufferBeginInfo beginInfo = {}; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; vkBeginCommandBuffer(commandBuffer, &beginInfo); - // Simple clear color pass - cycle through colors based on frame count VkClearColorValue clearColor; float hue = (m_frameCount % 360) / 360.0f; float r = std::abs(std::sin(hue * 6.28318f)); @@ -614,7 +756,6 @@ void VulkanWidget::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t i VkImage image = reinterpret_cast(m_swapchainImages[imageIndex]); - // Transition to transfer dst VkImageMemoryBarrier barrier1 = {}; barrier1.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier1.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -629,11 +770,9 @@ void VulkanWidget::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t i vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier1); - // Clear image vkCmdClearColorImage(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearColor, 1, &range); - // Transition to present VkImageMemoryBarrier barrier2 = {}; barrier2.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier2.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; @@ -653,13 +792,20 @@ void VulkanWidget::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t i void VulkanWidget::cleanupSwapchain() { + // Destroy image views first + for (void* imageView : m_swapchainImageViews) { + if (imageView != nullptr) { + vkDestroyImageView(m_device, reinterpret_cast(imageView), nullptr); + } + } + m_swapchainImageViews.clear(); + if (m_swapchain != VK_NULL_HANDLE) { vkDestroySwapchainKHR(m_device, m_swapchain, nullptr); m_swapchain = VK_NULL_HANDLE; } m_swapchainImages.clear(); - m_swapchainImageViews.clear(); } void VulkanWidget::cleanupVulkan() @@ -721,11 +867,32 @@ void VulkanWidget::setRenderingEnabled(bool enabled) m_renderingEnabled = enabled; if (m_renderingEnabled) { - qDebug() << "Vulkan rendering enabled"; + qDebug() << "Vulkan rendering ENABLED - Resuming animations"; m_renderTimer->start(16); // ~60 FPS + + // Unlocked: calculate lock duration + if (m_lastLockTime.isValid()) { + QDateTime unlockTime = QDateTime::currentDateTime(); + m_lastLockDuration = m_lastLockTime.secsTo(unlockTime); + m_lockPaintFrameCount = m_frameCount - m_lastLockFrameCount; + qDebug() << "Screen was locked for" << m_lastLockDuration << "seconds"; + qDebug() << "Frames at lock:" << m_lastLockFrameCount + << "- Frames painted during lock:" << m_lockPaintFrameCount; + } + + m_startTime = QDateTime::currentDateTime(); } else { - qDebug() << "Vulkan rendering disabled"; + qDebug() << "Vulkan rendering DISABLED - Stopping animations"; m_renderTimer->stop(); + + // Locked: record lock time + m_pauseTime = QDateTime::currentDateTime(); + m_lastLockTime = m_pauseTime; + m_lastLockFrameCount = m_frameCount; + m_lockCount++; + qDebug() << "Screen locked at" << m_lastLockTime.toString("yyyy-MM-dd hh:mm:ss") + << "- Lock count:" << m_lockCount + << "- Frame count at lock:" << m_lastLockFrameCount; } } @@ -766,6 +933,10 @@ void VulkanWidget::resizeEvent(QResizeEvent *event) if (m_initialized) { m_needsResize = true; + + // Note: Don't update renderer size here - it will be updated after swapchain recreation + // The renderer must use the actual surface dimensions (m_surfaceWidth/Height), + // not the widget dimensions, which may differ on high DPI displays } } @@ -783,6 +954,19 @@ QPaintEngine* VulkanWidget::paintEngine() const void VulkanWidget::onRenderTimer() { + if (m_renderingEnabled) { + // Update animation parameters + m_rotationAngle += 2.0; + if (m_rotationAngle >= 360.0) { + m_rotationAngle -= 360.0; + } + + m_wavePhase += 0.05; + if (m_wavePhase >= 2 * M_PI) { + m_wavePhase -= 2 * M_PI; + } + } + renderFrame(); } diff --git a/src/vulkanwidget.h b/src/vulkanwidget.h index 23efce7..6a7e5aa 100644 --- a/src/vulkanwidget.h +++ b/src/vulkanwidget.h @@ -4,8 +4,12 @@ #include #include #include +#include #include +// Forward declare VulkanRenderer +class VulkanRenderer; + // Forward declare Vulkan types to avoid including volk.h in header typedef struct VkInstance_T* VkInstance; typedef struct VkPhysicalDevice_T* VkPhysicalDevice; @@ -243,6 +247,20 @@ private: // 定时器 QTimer *m_renderTimer; + // VulkanRenderer + VulkanRenderer *m_renderer; + + // 动画参数 + double m_rotationAngle; + double m_wavePhase; + QDateTime m_startTime; + QDateTime m_pauseTime; + QDateTime m_lastLockTime; + qint64 m_lastLockDuration; + int m_lastLockFrameCount; + int m_lockPaintFrameCount; + int m_lockCount; + // 常量 static const int MAX_FRAMES_IN_FLIGHT = 2; };