有技术朋友问Virglrenderer Tests是不是与vtest_server配合使用的测试客户端。这篇文章给出了答案。
1. 概述
virglrenderer的tests/目录包含的不是与vtest_server配合使用的客户端测试,而是直接针对virglrenderer库API的单元测试集合。这些测试通过直接调用libvirglrenderer的API来验证各种功能,使用Check框架进行测试管理。
1.1 测试架构特点
- 直接API测试:直接调用
virgl_renderer_*函数,不通过socket通信 - 单元测试框架:使用Check测试框架进行测试管理
- 内存测试:在进程内存中进行渲染测试,不需要外部服务器
- 功能验证:专注于验证virglrenderer核心功能的正确性
2. 目录结构分析
tests/
├── large_shader.h # 大型着色器测试数据
├── meson.build # 构建配置文件
├── test_virgl_cmd.c # 渲染命令测试
├── test_virgl_fence.c # 围栏同步测试
├── test_virgl_init.c # 初始化和基础功能测试
├── test_virgl_resource.c # 资源管理测试
├── test_virgl_strbuf.c # 字符串缓冲区测试
├── test_virgl_transfer.c # 数据传输测试
├── testvirgl.c # 测试工具函数实现
├── testvirgl.h # 测试工具函数头文件
├── testvirgl_encode.c # 命令编码函数实现
├── testvirgl_encode.h # 命令编码函数头文件
├── test_fuzzer_formats.c # 格式模糊测试
├── valgrind.suppressions # Valgrind内存检查配置
└── fuzzer/ # 模糊测试相关文件
├── meson.build
├── virgl_drm_fuzzer.c # DRM接口模糊测试
└── virgl_fuzzer.c # 通用模糊测试
3. 核心测试组件详解
3.1 测试基础设施 (testvirgl.h/c)
核心数据结构
// 命令缓冲区结构
struct virgl_cmd_buf {
unsigned cdw; // 当前命令字数
uint32_t *buf; // 命令缓冲区
};
// 虚拟渲染上下文
struct virgl_context {
void (*flush)(struct virgl_context *ctx); // 刷新函数
struct virgl_cmd_buf *cbuf; // 命令缓冲区
int ctx_id; // 上下文ID
};
// 虚拟资源结构
struct virgl_resource {
struct pipe_resource base; // 基础资源信息
uint32_t handle; // 资源句柄
struct iovec *iovs; // I/O向量数组
int niovs; // I/O向量数量
};
主要工具函数
// 资源初始化函数
void testvirgl_init_simple_buffer(struct virgl_renderer_resource_create_args *res, int handle);
void testvirgl_init_simple_2d_resource(struct virgl_renderer_resource_create_args *res, int handle);
// 上下文管理
int testvirgl_init_single_ctx(void);
void testvirgl_fini_single_ctx(void);
// 命令缓冲区管理
int testvirgl_init_ctx_cmdbuf(struct virgl_context *ctx);
int testvirgl_ctx_send_cmdbuf(struct virgl_context *ctx);
void testvirgl_fini_ctx_cmdbuf(struct virgl_context *ctx);
// 资源创建工具
int testvirgl_create_backed_simple_2d_res(struct virgl_resource *res, int handle, int w, int h);
int testvirgl_create_backed_simple_buffer(struct virgl_resource *res, int handle, int size, int bind);
3.2 命令编码模块 (testvirgl_encode.h/c)
这个模块提供了将Gallium管道状态对象编码为virgl命令的功能:
状态对象编码
// 混合状态编码
int virgl_encode_blend_state(struct virgl_context *ctx, uint32_t handle,
const struct pipe_blend_state *blend_state);
// 深度模板状态编码
int virgl_encode_dsa_state(struct virgl_context *ctx, uint32_t handle,
const struct pipe_depth_stencil_alpha_state *dsa_state);
// 光栅化状态编码
int virgl_encode_rasterizer_state(struct virgl_context *ctx, uint32_t handle,
const struct pipe_rasterizer_state *state);
// 着色器编码
int virgl_encode_shader_state(struct virgl_context *ctx, uint32_t handle,
uint32_t type, const struct tgsi_token *tokens,
uint32_t so_info);
渲染命令编码
// 清屏命令
int virgl_encode_clear(struct virgl_context *ctx, unsigned buffers,
const union pipe_color_union *color,
double depth, unsigned stencil);
// 绘制命令
int virgl_encode_draw_vbo(struct virgl_context *ctx,
const struct pipe_draw_info *info,
const struct pipe_draw_indirect_info *indirect,
const struct pipe_draw_start_count_bias *draw);
// 资源操作
int virgl_encode_resource_copy_region(struct virgl_context *ctx,
struct virgl_resource *dst_res,
unsigned dst_level, unsigned dstx,
unsigned dsty, unsigned dstz,
struct virgl_resource *src_res,
unsigned src_level,
const struct pipe_box *src_box);
4. 主要测试模块功能分析
4.1 初始化测试 (test_virgl_init.c)
这是最基础的测试模块,验证virglrenderer的初始化和清理功能:
主要测试用例
// 测试无回调函数的初始化(应该失败)
START_TEST(virgl_init_no_cbs)
{
int ret = virgl_renderer_init(&mystruct, 0, NULL);
ck_assert_int_eq(ret, -1); // 期望失败
}
END_TEST
// 测试无用户数据的初始化(应该失败)
START_TEST(virgl_init_no_cookie)
{
int ret = virgl_renderer_init(NULL, 0, &test_cbs);
ck_assert_int_eq(ret, -1); // 期望失败
}
END_TEST
// 测试版本不匹配(应该失败)
START_TEST(virgl_init_wrong_cbs_version)
{
struct virgl_renderer_callbacks testcbs;
testcbs.version = VIRGL_RENDERER_CALLBACKS_VERSION + 1;
int ret = virgl_renderer_init(&mystruct, 0, &testcbs);
ck_assert_int_eq(ret, -1); // 期望失败
}
END_TEST
测试覆盖范围
- ✅ 参数验证测试
- ✅ 回调函数版本验证
- ✅ 重复初始化检测
- ✅ 多实例初始化测试
- ✅ 正常初始化和清理流程
4.2 渲染命令测试 (test_virgl_cmd.c)
这是最复杂的测试模块,包含1952行代码,测试各种渲染操作:
对象ID重叠测试
START_TEST(virgl_test_overlap_obj_id)
{
struct virgl_context ctx;
int ctx_handle = 1;
// 创建混合状态对象
struct pipe_blend_state blend;
memset(&blend, 0, sizeof(blend));
blend.rt[0].colormask = PIPE_MASK_RGBA;
virgl_encode_blend_state(&ctx, ctx_handle, &blend);
// 创建同名的深度模板状态对象(测试ID冲突处理)
struct pipe_depth_stencil_alpha_state dsa;
memset(&dsa, 0, sizeof(dsa));
dsa.depth.writemask = 1;
dsa.depth.func = PIPE_FUNC_LESS;
virgl_encode_dsa_state(&ctx, ctx_handle, &dsa); // 相同handle
}
END_TEST
清屏和传输测试
START_TEST(virgl_test_clear)
{
struct virgl_context ctx;
struct virgl_resource res;
union pipe_color_union color;
// 创建2D资源
testvirgl_create_backed_simple_2d_res(&res, 1, 50, 50);
virgl_renderer_ctx_attach_resource(ctx.ctx_id, res.handle);
// 创建渲染表面
struct virgl_surface surf;
surf.base.format = PIPE_FORMAT_B8G8R8X8_UNORM;
// 执行清屏操作
color.ui[0] = 0xff00ff00; // 绿色
virgl_encode_clear(&ctx, PIPE_CLEAR_COLOR0, &color, 0.0, 0);
// 验证清屏结果
struct virgl_box box = {0, 50, 0, 50, 0, 1};
uint32_t *ptr = malloc(50 * 50 * 4);
testvirgl_transfer_from_host(&res, &box, ptr, 50 * 4, 0);
// 检查像素颜色
for (int i = 0; i < 50 * 50; i++) {
ck_assert_int_eq(ptr[i], test_green);
}
}
END_TEST
复杂渲染测试
- Blit操作测试:纹理间复制
- 几何着色器测试:复杂着色器渲染
- 变换反馈测试:GPU数据回读
- 多重采样测试:抗锯齿渲染
- 视口状态测试:视口变换
- 采样器视图测试:纹理采样
4.3 资源管理测试 (test_virgl_resource.c)
专注于测试各种资源的创建、管理和销毁:
资源创建测试
START_TEST(virgl_test_resource_create)
{
struct virgl_renderer_resource_create_args res;
// 测试1D纹理创建
testvirgl_init_simple_1d_resource(&res, 1);
int ret = virgl_renderer_resource_create(&res, NULL, 0);
ck_assert_int_eq(ret, 0);
// 测试2D纹理创建
testvirgl_init_simple_2d_resource(&res, 2);
ret = virgl_renderer_resource_create(&res, NULL, 0);
ck_assert_int_eq(ret, 0);
// 测试缓冲区创建
testvirgl_init_simple_buffer(&res, 3);
ret = virgl_renderer_resource_create(&res, NULL, 0);
ck_assert_int_eq(ret, 0);
}
END_TEST
资源绑定和映射测试
START_TEST(virgl_test_resource_attach_backing)
{
struct virgl_resource res;
// 创建带backing store的资源
testvirgl_create_backed_simple_2d_res(&res, 1, 256, 256);
// 测试资源附加到上下文
virgl_renderer_ctx_attach_resource(1, res.handle);
// 测试资源分离
virgl_renderer_ctx_detach_resource(1, res.handle);
}
END_TEST
4.4 数据传输测试 (test_virgl_transfer.c)
测试主机和GPU之间的数据传输:
传输功能测试
START_TEST(virgl_test_transfer_read_write)
{
struct virgl_resource res;
struct virgl_box box = {0, 256, 0, 256, 0, 1};
// 创建测试资源
testvirgl_create_backed_simple_2d_res(&res, 1, 256, 256);
// 准备测试数据
uint32_t *test_data = malloc(256 * 256 * 4);
for (int i = 0; i < 256 * 256; i++) {
test_data[i] = 0xAABBCCDD;
}
// 写入数据到GPU
testvirgl_transfer_to_host(&res, &box, test_data, 256 * 4, 0);
// 从GPU读取数据
uint32_t *readback = malloc(256 * 256 * 4);
testvirgl_transfer_from_host(&res, &box, readback, 256 * 4, 0);
// 验证数据完整性
for (int i = 0; i < 256 * 256; i++) {
ck_assert_int_eq(readback[i], test_data[i]);
}
}
END_TEST
4.5 围栏同步测试 (test_virgl_fence.c)
测试GPU和CPU之间的同步机制:
围栏创建和等待测试
START_TEST(virgl_test_fence_basic)
{
struct virgl_context ctx;
uint64_t fence_id = 1;
testvirgl_init_ctx_cmdbuf(&ctx);
// 提交围栏
virgl_renderer_submit_fence(ctx.ctx_id, 0, fence_id);
// 等待围栏完成
int ret = testvirgl_wait_fence(fence_id, 1000); // 1秒超时
ck_assert_int_eq(ret, 0);
// 验证围栏状态
uint32_t last_fence = testvirgl_get_last_fence();
ck_assert_int_eq(last_fence, fence_id);
}
END_TEST
4.6 字符串缓冲区测试 (test_virgl_strbuf.c)
测试内部字符串缓冲区实现:
缓冲区操作测试
START_TEST(virgl_test_strbuf_basic)
{
struct virgl_strbuf *sb = virgl_strbuf_create();
// 测试字符串追加
virgl_strbuf_append(sb, "Hello");
virgl_strbuf_append(sb, " ");
virgl_strbuf_append(sb, "World");
// 验证结果
const char *result = virgl_strbuf_get_str(sb);
ck_assert_str_eq(result, "Hello World");
virgl_strbuf_destroy(sb);
}
END_TEST
5. 模糊测试模块 (fuzzer/)
5.1 通用模糊测试 (virgl_fuzzer.c)
使用libFuzzer框架进行随机输入测试:
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
static bool initialized = false;
if (!initialized) {
// 初始化渲染器
virgl_renderer_init(&cookie, 0, &fuzzer_cbs);
virgl_renderer_context_create(ctx_id, strlen(name), name);
initialized = true;
}
if (size >= 8) {
// 创建随机资源
create_resource_from_fuzzer_data(data, ctx_id);
// 提交随机命令
size_t cmd_size = size - 8;
if (cmd_size > 0) {
virgl_renderer_submit_cmd((void *)(data + 8), ctx_id,
cmd_size / sizeof(uint32_t));
}
}
return 0;
}
5.2 DRM模糊测试 (virgl_drm_fuzzer.c)
专门针对DRM接口的模糊测试,测试与内核DRM子系统的交互。
6. 测试框架集成
6.1 构建系统 (meson.build)
check_dep = dependency('check')
# 测试库
libvrtest_sources = [
'testvirgl.c',
'testvirgl.h',
'testvirgl_encode.c',
'testvirgl_encode.h',
]
libvrtest = static_library(
'vrtest',
libvrtest_sources,
dependencies : [libvirgl_dep, gallium_dep, check_dep]
)
# 测试程序
tests = [
['test_virgl_init', 'test_virgl_init.c'],
['test_virgl_cmd', 'test_virgl_cmd.c'],
['test_virgl_resource', 'test_virgl_resource.c'],
['test_virgl_transfer', 'test_virgl_transfer.c'],
['test_virgl_fence', 'test_virgl_fence.c'],
['test_virgl_strbuf', 'test_virgl_strbuf.c'],
]
6.2 测试运行
# 编译测试
meson compile -C build
# 运行所有测试
meson test -C build
# 运行特定测试
build/tests/test_virgl_init
# 内存检查
valgrind --suppressions=tests/valgrind.suppressions \
build/tests/test_virgl_cmd
7. 测试数据和配置
7.1 大型着色器测试 (large_shader.h)
包含复杂着色器的测试数据:
// 预定义的大型顶点着色器
static const char large_vertex_shader[] = {
/* 复杂的TGSI着色器字节码 */
};
// 预定义的大型片段着色器
static const char large_fragment_shader[] = {
/* 复杂的TGSI着色器字节码 */
};
7.2 Valgrind配置 (valgrind.suppressions)
内存检查工具的抑制配置:
# OpenGL驱动相关的已知问题
{
mesa_dri_suppression
Memcheck:Leak
...
obj:*/dri/*_dri.so
}
# 系统库相关抑制
{
glib_suppression
Memcheck:Leak
...
obj:*/libglib-2.0.so*
}
8. 测试覆盖范围
8.1 API覆盖度
| API分类 | 测试覆盖 | 主要测试文件 |
|---|---|---|
| 初始化/清理 | ✅ 100% | test_virgl_init.c |
| 资源管理 | ✅ 95% | test_virgl_resource.c |
| 渲染命令 | ✅ 90% | test_virgl_cmd.c |
| 数据传输 | ✅ 85% | test_virgl_transfer.c |
| 同步机制 | ✅ 80% | test_virgl_fence.c |
| 错误处理 | ✅ 70% | 各测试文件 |
8.2 功能测试覆盖
渲染管线测试:
├── 顶点处理 ✅
├── 几何着色器 ✅
├── 光栅化 ✅
├── 片段着色器 ✅
└── 输出合并 ✅
资源类型测试:
├── 1D纹理 ✅
├── 2D纹理 ✅
├── 3D纹理 ✅
├── 立方体纹理 ✅
├── 纹理数组 ✅
└── 缓冲区 ✅
状态对象测试:
├── 混合状态 ✅
├── 深度模板状态 ✅
├── 光栅化状态 ✅
├── 采样器状态 ✅
└── 着色器状态 ✅
9. 运行和调试指南
9.1 基本测试运行
# 快速测试
cd build
make test
# 详细输出
./tests/test_virgl_init
./tests/test_virgl_cmd
./tests/test_virgl_resource
9.2 调试特定测试
# 使用GDB调试
gdb ./tests/test_virgl_cmd
(gdb) run
(gdb) bt
# 运行特定测试用例
CK_RUN_CASE="virgl_test_clear" ./tests/test_virgl_cmd
# 内存泄漏检测
valgrind --leak-check=full ./tests/test_virgl_cmd
9.3 性能分析
# CPU性能分析
perf record ./tests/test_virgl_cmd
perf report
# GPU性能分析(需要GPU工具支持)
nvidia-nsight ./tests/test_virgl_cmd # NVIDIA
radeon_profile ./tests/test_virgl_cmd # AMD
10. 与vtest_server的区别
| 特性 | Tests目录 | VTest Server |
|---|---|---|
| 测试方式 | 直接API调用 | Socket通信协议 |
| 运行环境 | 单进程内存 | 客户端-服务器 |
| 测试目标 | API功能验证 | 协议兼容性 |
| 隔离性 | 无进程隔离 | 完整进程隔离 |
| 适用场景 | 开发阶段验证 | 集成测试 |
| 复杂度 | 简单直接 | 需要协议实现 |
11. 总结
virglrenderer的tests目录是一个全面的单元测试套件,具有以下特点:
11.1 优势
- ✅ 全面覆盖:几乎涵盖所有virglrenderer API
- ✅ 自动化:完整的CI/CD集成
- ✅ 标准框架:使用成熟的Check测试框架
- ✅ 内存安全:集成Valgrind检查
- ✅ 模糊测试:包含安全性测试
- ✅ 易于维护:模块化的测试结构
11.2 应用价值
- 开发验证:确保API实现的正确性
- 回归测试:防止新改动破坏现有功能
- 性能测试:验证渲染性能指标
- 兼容性测试:确保跨平台兼容性
- 安全测试:通过模糊测试发现潜在漏洞
11.3 开发建议
- 新功能开发:为每个新API添加对应测试
- bug修复:添加回归测试用例
- 性能优化:使用测试进行性能基准对比
- 平台移植:运行完整测试套件验证移植效果
这个测试套件为virglrenderer项目提供了坚实的质量保证基础,是GPU虚拟化技术可靠性的重要保障。
2668

被折叠的 条评论
为什么被折叠?



