150 lines
4.7 KiB
Python
150 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
测试 @instance_buffer 注释解析和实例数据生成
|
|
|
|
这个脚本测试以下功能:
|
|
1. parse_shader_annotations 函数正确解析 @instance_buffer 注释
|
|
2. detect_instance_buffer 函数正确检测和验证实例缓冲
|
|
3. generate_instance_data_struct 函数正确生成 C++ 结构体
|
|
|
|
运行方式: python -m tools.test_instance_buffer (从项目根目录)
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
import re
|
|
|
|
# 直接在这里定义注释解析函数(避免相对导入问题)
|
|
def parse_shader_annotations(source_code: str):
|
|
"""解析着色器源码中的特殊注释"""
|
|
annotations = {
|
|
'instance_buffer_binding': None,
|
|
}
|
|
|
|
lines = source_code.split('\n')
|
|
|
|
# 用于匹配 @instance_buffer 注释的正则
|
|
instance_buffer_comment_re = re.compile(
|
|
r'//\s*@instance_buffer|/\*\s*@instance_buffer\s*\*/'
|
|
)
|
|
|
|
# 用于从 layout 声明提取 binding 的正则
|
|
binding_re = re.compile(r'binding\s*=\s*(\d+)')
|
|
|
|
for i, line in enumerate(lines):
|
|
if instance_buffer_comment_re.search(line):
|
|
# 首先检查同一行
|
|
match = binding_re.search(line)
|
|
if match:
|
|
annotations['instance_buffer_binding'] = int(match.group(1))
|
|
continue
|
|
|
|
# 检查接下来的几行
|
|
for j in range(i + 1, min(i + 5, len(lines))):
|
|
next_line = lines[j].strip()
|
|
|
|
if not next_line or next_line.startswith('//'):
|
|
continue
|
|
|
|
match = binding_re.search(next_line)
|
|
if match:
|
|
annotations['instance_buffer_binding'] = int(match.group(1))
|
|
break
|
|
|
|
if 'layout' not in next_line.lower():
|
|
break
|
|
|
|
if annotations['instance_buffer_binding'] is not None:
|
|
break
|
|
|
|
return annotations
|
|
|
|
|
|
def test_parse_shader_annotations():
|
|
"""测试注释解析功能"""
|
|
print("=" * 60)
|
|
print("测试 1: parse_shader_annotations")
|
|
print("=" * 60)
|
|
|
|
# 测试用例 1: 标准格式
|
|
source1 = """
|
|
#version 450 core
|
|
|
|
struct InstanceData {
|
|
vec4 rect;
|
|
vec4 color;
|
|
};
|
|
|
|
// @instance_buffer
|
|
layout(std430, set = 0, binding = 0) readonly buffer InstanceBuffer {
|
|
InstanceData instances[];
|
|
};
|
|
|
|
void main() {}
|
|
"""
|
|
result1 = parse_shader_annotations(source1)
|
|
assert result1['instance_buffer_binding'] == 0, f"Expected binding 0, got {result1['instance_buffer_binding']}"
|
|
print(f" [PASS] Test case 1: binding = {result1['instance_buffer_binding']}")
|
|
|
|
# Test case 2: Different binding
|
|
source2 = """
|
|
// @instance_buffer
|
|
layout(std430, set = 1, binding = 5) readonly buffer MyBuffer {
|
|
SomeData data[];
|
|
};
|
|
"""
|
|
result2 = parse_shader_annotations(source2)
|
|
assert result2['instance_buffer_binding'] == 5, f"Expected binding 5, got {result2['instance_buffer_binding']}"
|
|
print(f" [PASS] Test case 2: binding = {result2['instance_buffer_binding']}")
|
|
|
|
# Test case 3: Block comment format
|
|
source3 = """
|
|
/* @instance_buffer */
|
|
layout(std430, binding = 3) buffer Data { vec4 items[]; };
|
|
"""
|
|
result3 = parse_shader_annotations(source3)
|
|
assert result3['instance_buffer_binding'] == 3, f"Expected binding 3, got {result3['instance_buffer_binding']}"
|
|
print(f" [PASS] Test case 3: binding = {result3['instance_buffer_binding']}")
|
|
|
|
# Test case 4: No @instance_buffer marker
|
|
source4 = """
|
|
layout(std430, set = 0, binding = 0) buffer SomeBuffer {
|
|
vec4 data[];
|
|
};
|
|
"""
|
|
result4 = parse_shader_annotations(source4)
|
|
assert result4['instance_buffer_binding'] is None, "Expected None for source without @instance_buffer"
|
|
print(" [PASS] Test case 4: Returns None when no marker")
|
|
|
|
print("\nAll annotation parsing tests passed!\n")
|
|
|
|
|
|
def main():
|
|
"""Run all tests"""
|
|
print("\n" + "=" * 60)
|
|
print(" @instance_buffer Annotation Parsing Test")
|
|
print("=" * 60 + "\n")
|
|
|
|
try:
|
|
test_parse_shader_annotations()
|
|
|
|
print("=" * 60)
|
|
print(" All tests PASSED!")
|
|
print("=" * 60)
|
|
print("\nNote: Data class and code generation tests require")
|
|
print(" the full CMake build process. Run:")
|
|
print(" cmake --build build --target generate_shader_bindings")
|
|
print("=" * 60 + "\n")
|
|
return 0
|
|
except AssertionError as e:
|
|
print(f"\n[FAIL] Test failed: {e}")
|
|
return 1
|
|
except Exception as e:
|
|
print(f"\n[ERROR] Test error: {type(e).__name__}: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main()) |