https://github.com/KhronosGroup/Vulkan-Samples

先介绍下本项目的框架。

如何构建

按项目的 readme 教程 ,windows 下推荐用 Visual Studio,不需要额外配置即可配置构建调试项目,缺点是太慢了,不管是构建还是 IDE 使用方面; 使用 VsCode 的话配置比较麻烦,可以安装 cmake-tools 插件,然后在 ui 中配置 build kit,其实也比较简单,缺点是构建也很慢

用 CLion 配置 Visual Studio 工具链,构建速度相对较快,且调试也更方便,推荐使用;

vulkan_samples 可执行文件源码

其源文件位于 app/main.cpp,其主要流程为:

	auto code = platform.initialize(plugins::get_all());
 
	if (code == vkb::ExitCode::Success)
	{
		code = platform.main_loop();
	}
 
	platform.terminate(code);

第一步是初始化 plugins, 这里的 插件 实际是 vlukan_samples 的各功能模块,如运行 sample 子命令的就是 StartSample 插件,截屏的是 Screenshot 等。

主循环函数 main_loop (framework/platform/platform.cpp):

ExitCode Platform::main_loop()  
{  
    ExitCode exit_code = ExitCode::Success;  
    while (exit_code == ExitCode::Success)  
    {  
       exit_code = main_loop_frame();  
    }  
  
    return exit_code;  
}

进一步 main_loop_frame:

  
ExitCode Platform::main_loop_frame()  
{  
    try  
    {  
       // Load the requested app  
       if (app_requested())  
       {  
          if (!start_app())  
          {  
             throw std::runtime_error("Failed to load requested application");  
          }  
  
          // Compensate for load times of the app by rendering the first frame pre-emptively  
          timer.tick<Timer::Seconds>();  
          active_app->update(0.01667f);  
       }  
  
       if (!active_app)  
       {  
          return ExitCode::NoSample;  
       }  
  
       update();  
  
       if (active_app->should_close())  
       {  
          std::string id = active_app->get_name();  
          on_app_close(id);  
          active_app->finish();  
       }  
  
       window->process_events();  
  
       if (window->should_close() || close_requested)  
       {  
          return ExitCode::Close;  
       }  
    }  
    catch (std::exception &e)  
    {  
       LOGE("Error Message: {}", e.what());  
       LOGE("Failed when running application {}", active_app->get_name());  
  
       on_app_error(active_app->get_name());  
  
       if (app_requested())  
       {  
          LOGI("Attempting to load next application");  
       }  
       else  
       {  
          set_last_error(e.what());  
          return ExitCode::FatalError;  
       }  
    }  
  
    return ExitCode::Success;  
}

进一步阅读各个函数,

Platform::start_app

  
bool Platform::start_app()  
{  
    auto *requested_app_info = requested_app;  
    // Reset early incase error in preparation stage  
    requested_app = nullptr;  
  
    if (active_app)  
    {  
       auto execution_time = timer.stop();  
       LOGI("Closing App (Runtime: {:.1f})", execution_time);  
  
       auto app_id = active_app->get_name();  
  
       active_app->finish();  
    }  
  
    // Reset target environment to default prior to each sample to properly support batch mode  
    vkb::GLSLCompiler::reset_target_environment();  
  
    active_app = requested_app_info->create();  
  
    if (!active_app)  
    {  
       LOGE("Failed to create a valid vulkan app.");  
       return false;  
    }  
    auto sample_info = static_cast<const apps::SampleInfo *>(requested_app_info);  
    active_app->set_name(sample_info->name);  
  
    if (!active_app->prepare({false, window.get()}))  
    {  
       LOGE("Failed to prepare vulkan app.");  
       return false;  
    }  
  
    on_app_start(requested_app_info->id);  
  
    return true;  
}
  1. 调用 create 函数(即每个 sample 中的 create_xxx )创建该类型 app 实例,
  2. 调用 app 的 prepare 函数
  3. 触发相关插件的 on_app_start

Platform::update

  
void Platform::update()  
{  
    auto delta_time = static_cast<float>(timer.tick<Timer::Seconds>());  
  
    if (focused || always_render)  
    {  
       on_update(delta_time);  
  
       if (fixed_simulation_fps)  
       {  
          delta_time = simulation_frame_time;  
       }  
  
       active_app->update_overlay(delta_time, [=, this]() {  
          on_update_ui_overlay(*active_app->get_drawer());  
       });  
       active_app->update(delta_time);  
  
       if (auto *app = dynamic_cast<VulkanSampleCpp *>(active_app.get()))  
       {  
          if (app->has_render_context())  
          {  
             on_post_draw(reinterpret_cast<vkb::RenderContext &>(app->get_render_context()));  
          }  
       }  
       else if (auto *app = dynamic_cast<VulkanSampleC *>(active_app.get()))  
       {  
          if (app->has_render_context())  
          {  
             on_post_draw(app->get_render_context());  
          }  
       }  
    }  
}
  1. 触发相关插件的 on_update
  2. 调用 app 的 update_overlay 和 update
  3. 触发相关插件的 on_post_draw

框架提供了两种不同的示例基类:

  • 高级示例基类(framework/vulkan_sample.h):此基类抽象了大部分 Vulkan API 调用,因此大量使用了框架的 Vulkan 对象包装器类。使用基类编写示例代码更简洁。
  • API 示例基类(framework/api_vulkan_sample.h):这个基类使用较少的抽象,让您更明确地使用 API。 另两个对应的 C++ 变体基类

项目结构

  • components:组件封装了所有与特定功能相关的代码。这里的功能主要有公共 utils 和各平台支持。
  • framework:示例框架代码
  • samples:示例代码

此文件夹下有1条笔记。