VK-GL-CTS 初步了解
下载
地址:https://github.com/KhronosGroup/VK-GL-CTS
概要说明
VK-GL-CTS提供针对:Volkan,OpenGL的一致性验证。意思是对这些图形库提供的API严重其在不同的平台的实现是否一致。所以概括起来就是VK-GL-CTS提供对不同平台的图形库的一致性验证框架。
源码获取
git clone https://github.com/KhronosGroup/VK-GL-CTS.git
-
目录结构
. ├── Android.mk ├── AndroidGen.mk ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── LICENSE ├── MODULE_LICENSE_APACHE2 ├── NOTICE ├── OWNERS ├── README.md ├── android ├── data ├── doc ├── execserver ├── executor ├── external ├── framework ├── modules ├── scripts └── targets
-
主要内容介绍
- data:测试数据,提供测试图片和测试shader参数;
- execserver:测试服务程序,主要为android和ios平台测试框架提供;
- executor:测试基础程序,测试框架的基础实现,可视作自定义测试框架基础。
- external:依赖的外部库源码和测试case
- framework[^移植]:测试框架,包括测试套装和case的实现框架,wrapper。
- modules:测试目标模块的case
- scrpits:针对不同平台的编译脚本
- targets[^移植]:编译脚本目录
-
测试框架概览
- 通用main入口:framework/platform/tcuMain.cpp
// Implement this in your platform port. tcu::Platform* createPlatform (void); int main (int argc, char** argv) { …… try { tcu::CommandLine cmdLine (argc, argv); tcu::DirArchive archive (cmdLine.getArchiveDir()); tcu::TestLog log (cmdLine.getLogFileName(), cmdLine.getLogFlags()); de::UniquePtr<tcu::Platform> platform (createPlatform()); de::UniquePtr<tcu::App> app (new tcu::App(*platform, archive, log, cmdLine)); // Main loop. …… } catch (const std::exception& e) { tcu::die("%s", e.what()); } return exitStatus; }
- 测试包注册:externl/openglcts/modules/glcTestPackageRegistry.cpp
void registerPackages(void) { tcu::TestPackageRegistry* registry = tcu::TestPackageRegistry::getSingleton(); registry->registerPackage("CTS-Configs", createConfigPackage); #if DE_OS != DE_OS_ANDROID registry->registerPackage("dEQP-EGL", createdEQPEGLPackage); #endif registry->registerPackage("KHR-GLES2", createES2Package); #if DE_OS != DE_OS_ANDROID registry->registerPackage("dEQP-GLES2", createdEQPES2Package); #endif #if defined(DEQP_GTF_AVAILABLE) registry->registerPackage("GTF-GLES2", createES2GTFPackage); #endif registry->registerPackage("KHR-GLES3", createES30Package); #if DE_OS != DE_OS_ANDROID registry->registerPackage("dEQP-GLES3", createdEQPES30Package); #endif #if defined(DEQP_GTF_AVAILABLE) registry->registerPackage("GTF-GLES3", createES30GTFPackage); #endif #if DE_OS != DE_OS_ANDROID registry->registerPackage("dEQP-GLES31", createdEQPES31Package); #endif registry->registerPackage("dEQP-GL45-GLES31", createdEQPGL45ES31Package); registry->registerPackage("dEQP-GL45-GLES3", createdEQPGL45ES3Package); registry->registerPackage("KHR-GLES31", createES31Package); registry->registerPackage("KHR-GLESEXT", createESEXTPackage); #if defined(DEQP_GTF_AVAILABLE) registry->registerPackage("GTF-GLES31", createES31GTFPackage); #endif registry->registerPackage("KHR-GLES32", createES32Package); registry->registerPackage("KHR-NoContext", createNoDefaultCustomContextPackage); registry->registerPackage("KHR-Single-GL43", createSingleConfigGL43TestPackage); registry->registerPackage("KHR-Single-GL44", createSingleConfigGL44TestPackage); registry->registerPackage("KHR-Single-GL45", createSingleConfigGL45TestPackage); registry->registerPackage("KHR-Single-GL46", createSingleConfigGL46TestPackage); registry->registerPackage("KHR-Single-GLES32", createSingleConfigES32TestPackage); registry->registerPackage("KHR-GL30", createGL30Package); registry->registerPackage("KHR-GL31", createGL31Package); registry->registerPackage("KHR-GL32", createGL32Package); registry->registerPackage("KHR-GL33", createGL33Package); registry->registerPackage("KHR-GL40", createGL40Package); registry->registerPackage("KHR-GL41", createGL41Package); registry->registerPackage("KHR-GL42", createGL42Package); registry->registerPackage("KHR-COMPAT-GL42", createGL42CompatPackage); registry->registerPackage("KHR-GL43", createGL43Package); registry->registerPackage("KHR-GL44", createGL44Package); registry->registerPackage("KHR-GL45", createGL45Package); registry->registerPackage("KHR-GL46", createGL46Package); #if defined(DEQP_GTF_AVAILABLE) registry->registerPackage("GTF-GL30", createGL30GTFPackage); registry->registerPackage("GTF-GL31", createGL31GTFPackage); registry->registerPackage("GTF-GL32", createGL32GTFPackage); registry->registerPackage("GTF-GL33", createGL33GTFPackage); registry->registerPackage("GTF-GL40", createGL40GTFPackage); registry->registerPackage("GTF-GL41", createGL41GTFPackage); registry->registerPackage("GTF-GL42", createGL42GTFPackage); registry->registerPackage("GTF-GL43", createGL43GTFPackage); registry->registerPackage("GTF-GL44", createGL44GTFPackage); registry->registerPackage("GTF-GL45", createGL45GTFPackage); registry->registerPackage("GTF-GL46", createGL46GTFPackage); #endif } }
- 不同模块的测试用例:external/openglcts/modules里,如下图展示了modules目录的内容
. ├── CMakeLists.txt ├── common //测试框架相关源码 ├── gl //gl的测试case ├── glcTestPackageEntry.cpp ├── glcTestPackageRegistry.cpp ├── glcTestPackageRegistry.hpp ├── gles2 //gles2的测试case ├── gles3 //gles3的测试case ├── gles31 //gles31的测试case ├── gles32 //gles32的测试case ├── glesext //glesext的测试case ├── pch.cpp ├── pch.h └── runner //glcTestRunner相关文件,不同平台启动的内容也不同,比如glcAndroidTestActivity.cpp也在此文件夹内。
下载第三方软件
进入VK-GL-CTS目录,执行python external/fetch_sources.py.
注意,此部分可能下载困难,需要科学上网
编译[^cmake] 和 测试
-
由于是cmake,所以和下载的文件平行的位置创建build目录:
mkdir x11gl (或者 mkdir gles32,根据不同编译对象建立不同目录)
-
VK-GLS-CTS cmake 有两个选项:
- -DDEQP_TARGET:依赖目标,如android,default,iOS,null,osx,raspi,curfaceless,vulkan_headless,wayland,x11_egl,x11_egl_glx,x11_glx
- -DCLSTS_GTF_TARGET:测试对象,如gles2,gles3,gles31,gles32和gl
-
编译
cmake <path to VK-GL-CTS> -DDEQP_TARGET=x11_glx -DGLCTS_GTF_TARGET=gl (或是:cmake <path to VK-GL-CTS> -DDEQP_TARGET=null -DGLCTS_GTF_TARGET=gl/gles32) cmake --build external/openglcts
-
Debug
cmake <path to VK-GL-CTS> -DDEQP_TARGET=default -DGLCTS_GTF_TARGET=gl -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS=-m64 -DCMAKE_CXX_FLAGS=-m64 cmake --build . -j12
-
测试
./cts-runner --type=gl45 (套装级测试) ./glcts --deqp-case=dEQP-GLES2.info.* (用例级测试)
-
测试结果
在测试位置的本地目录会有个TestResults.qpa文件(默认不改log输出时可见)
编译框架详解
根CMakeLists
根CMakeLists主要定义:
- 版本(cmake>3.10.2,python>3)
- 编译目标(targets目录里的具体对象,默认default及default.cmake)和 项目 (dEQP-Core-default)
- 定义变量,见set(DEQP_XXX);定义构建参数,见add_definitions;定义头文件,见include_directories;定义子cmake,见include;定义宏/宏函数,见macro,
- 定义需要编译的subdirectory,见add_subdirectory,注意,这就是具体的编译步骤 。具体步骤为:
- 编译external里的第三方库,如glslang,spirv-tools,amber等;
- 编译framework里的delibs,如debase,depool等,相当于自己实现的基础库函数以避免对不同平台的依赖;
- 编译execserver和executor,测试框架的基础库
- 编译全部framework,modules,framework主要是针对不同的平台开发的平台适配内容,即为实现接口测试而需要的基础适配代码;modules是针对不同版本gl库开发的接口测试用例;
- 最后编译external/openglcts,生成测试对应的可执行文件 cts-runner 和 glcts 。
script脚本
用python写的工具,README介绍的主要两类,一个就是android生成apk的脚本,一个就是log分析和格式转换相关的。但是这里面其实有些基础的测试相关工具,如build_caselists.py,类似于直接运行的cmake命令。
platform的选择
根据DE_OS_IS_UNIX 和 DEQP_USE_X11确定是使用platform/lnx内的platform实现,如:tcu::Platform* tcu::lnx::LinuxPlatform;并且,由于在cmake编译时使用 -DDEQP_TARGET=default ,所以targets/default 目录里的default.cmake会被调用,然后在此文件里会找 find_package(X11) ,如果找到则会设置:set(DEQP_USE_X11 ON) 和 set(DEQP_SUPPORT_GLX ON)
测试框架详解
cts-runner详解
- 编译:在源码目录CMakeList里定义了add_executable生成cts-runner,主要涉及两个文件:glcTestRunnerMain.cpp和glcTestPackageEntry.cpp
add_executable(cts-runner runner/glcTestRunnerMain.cpp glcTestPackageEntry.cpp)
- 主要调用关系
graph LR
glcTestRunnerMain[cts-runner] -->main(main)
main --> platform(Platform)
main --> glcTestRunner.cpp(TestRunner)
glcTestRunner.cpp(TestRunner) --> init --> |--type| getDefaultConfigList
init --> |--type| getTestRunParam
glcTestRunner.cpp(TestRunner) --> initSession --> createSession --> tcuApp(createApp) --> tcuTestPackage.cpp(TestPackageRegistry)
glcTestRunner.cpp(TestRunner) --> iterateSession --> glcTestRunner(RunSession.iterate) --> tcuApp.cpp(App.iterate)
总结:cts-runner根据参数里的--type初始化对应的配置文件和运行参数,然后生成 tcuAPP,根据注册的TestPackage进行具体测试
所以进一步需要了解的为:
-
Platform:主要对应的是具体的平台框架的实现,如Display和Context实现,对应的实现逻辑可以参考framework/platform内的实现。
├── android ├── CMakeLists.txt ├── ios ├── lnx //linux ├── null ├── nullws ├── osx ├── raspi ├── surfaceless ├── tcuMain.cpp ├── vanilla └── win32
ConfigList:根据传入--type,初始化基本配置,包括EGL,WGL,主要初始化display,render等需要的参数,如尺寸,RGB,YUV等的参数值,其中对应具体运行的case配置也是在ConfigList里,如 --deqp-case
RunParam:初始化基本参数,包括ES,GL等,主要初始化api,screen,surface,randonseed等
TestPackage:参考之前的测试包注册 ,实现对应的测试用例。
glcts详解
-
编译:和 cts-runner 不同,glcts独立的可执行程序是在 *external/openglcts/modules/CMakeLists里定义模块
add_deqp_module(glcts "${GLCTS_SRCS}" "${GLCTS_LIBS}" glcTestPackageEntry.cpp)
然后再在总CMakeLists里根据module名称套用tcuMain生成的可执行程序
add_executable(${MODULE_NAME} ${PROJECT_SOURCE_DIR}/framework/platform/tcuMain.cpp ${ENTRY})
-
主要调用关系
参加之前的 测试框架概览 ,tcuMain主要调用的就是tcuApp,所以glcts其实就是cts-runner最终调用的部分。cts-runner是在由 --type 对应的TestRunner里配置对应的参数。
graph LR tcuMain[glcts] -->tcuApp(App) --> TestPackageRegistry(TestPackageRoot) --> tcuTestSessionExecutor(caseListFilter) --> TestSessionExecutor(iterate) STATE_EXECUTE_TEST_CASE --> RenderCase(executeForConfig) tcu::App::iterate --> tcu::TestSessionExecutor --> tcu::TestHierarchyIterator --> deqp::egl::TestPackage --> deqp::egl::EglTestContext
总结:cts-runner根据参数里的--type初始化对应的配置文件和运行参数,然后生成 tcuAPP,根据注册的TestPackage进行具体测试
target详解
target里存的都是cmake文件,在编译时-DDEQP_TARGET=?选择的对象即为target文件下的子文件夹。
targets
├── android
├── default
├── ios
├── null
├── nullws
├── osx
├── raspi
├── surfaceless
├── vulkan_headless
├── wayland
├── x11_egl
├── x11_egl_glx
└── x11_glx
platform详解
platform里存的是不同框架内的图形库的入口实现,以Debug对应的函数为例,LinuxPlatform实现 m_vkPlatform,m_glPlatform 和 m_eglPlatform 三个platform;
Display详解
graph LR
main --> createPlatform --> tcu::lnx::(LinuxPlatform) --> tcu::lnx::egl::Platform(egl::Platform) --> tcu::lnx::x11::egl::createDisplayFactory(createDisplayFactory) --> tcu::lnx::x11::egl::DisplayFactory(DisplayFactory) --> tcu::lnx::x11::egl::WindowFactory[WindowFactory]
eglw::DefaultLibrary[library]--> eglu::NativeDislay(Display) --> eglu::NativeWindow(Window) --> eglu::NativeWindowFactory(WindowFactory)
移植分析
移植涉及的API文件
framework/common/tcuPlatform.hpp
framework/opengl/gluPlatform.hpp
framework/egl/egluPlatform.hpp
framework/platform/tcuMain.cpp
如何增加测试对象和测试模块
-
测试对象为targets目录内的内容
. ├── android ├── default ├── ios ├── null ├── nullws ├── osx ├── raspi ├── surfaceless ├── vulkan_headless ├── wayland ├── x11_egl ├── x11_egl_glx └── x11_glx
以上为写文章时最新的目录内容。子目录为编译生成具体对象测试程序的cmake脚本,举个例子
message("*** Using null context target") set(DEQP_TARGET_NAME "Null") set(TCUTIL_PLATFORM_SRCS null/tcuNullPlatform.cpp null/tcuNullPlatform.hpp null/tcuNullRenderContext.cpp null/tcuNullRenderContext.hpp null/tcuNullContextFactory.cpp null/tcuNullContextFactory.hpp )
以上是null对象的生成脚步,内部文件对应位置则为framework/platform目录内。对应不同软件平台实现了不同GL接口和初始化实现
-
测试框架
framework/common内是VK-GL-CTS的测试框架,对应生成的cts-runner和glcts是external/openglcts/modules里的文件。cts-runner对应的main就是runner/glcTestRunnerMain.cpp,glcts对应的则是glcTestPackageEntry.cpp(内部依赖glcTestPackageRegistry.cpp)然后根据cmake的编译规则add_module,add_executable,使用tcuapp.cpp作为glcts程序的main入口对模块级别的测试用例进行测试。