GPU: Shader Builder Filter

This PR introduced some filters to improve the workflow when using
shader_builder. Shader builder is used to validate shader compilation
during buildtime and can be enabled using `WITH_GPU_BUILDTIME_SHADER_BUILDER`.

During backend development shader builder is also handy as you can
pin-point it to the shader/backend you're focusing on. Without filters
you would insert temporary code to break on a specific shader.

* `--gpu-backend` can be used to only check a specific backend.
  possible values are `vulkan`, `metal` or `opengl`. When argument
  isn't passed, all backends will be validated.
* `--gpu-shader-filter` can be used to only check a subset or indivisual
  shader. The filter is a name starts with filter. Use
  `--gpu-shader-filter eevee` to validate all eevee shaders

Pull Request: https://projects.blender.org/blender/blender/pulls/115888
This commit is contained in:
Jeroen Bakker 2023-12-11 10:44:09 +01:00
parent fb5d03b5ba
commit 1b99987043
4 changed files with 116 additions and 27 deletions

View File

@ -16,6 +16,7 @@
#include "GPU_init_exit.h"
#include "gpu_shader_create_info_private.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
#include "CLG_log.h"
@ -29,19 +30,29 @@ class ShaderBuilder {
GPUContext *gpu_context_ = nullptr;
public:
void init();
bool bake_create_infos();
void exit();
void init_system();
bool init_context();
bool bake_create_infos(const char *name_starts_with_filter);
void exit_context();
void exit_system();
};
bool ShaderBuilder::bake_create_infos()
bool ShaderBuilder::bake_create_infos(const char *name_starts_with_filter)
{
return gpu_shader_create_info_compile_all();
return gpu_shader_create_info_compile(name_starts_with_filter);
}
void ShaderBuilder::init()
void ShaderBuilder::init_system()
{
CLG_init();
ghost_system_ = GHOST_CreateSystemBackground();
}
bool ShaderBuilder::init_context()
{
BLI_assert(ghost_system_);
BLI_assert(ghost_context_ == nullptr);
BLI_assert(gpu_context_ == nullptr);
GHOST_GPUSettings gpuSettings = {0};
switch (GPU_backend_type_selection_get()) {
@ -68,23 +79,33 @@ void ShaderBuilder::init()
break;
}
ghost_system_ = GHOST_CreateSystemBackground();
ghost_context_ = GHOST_CreateGPUContext(ghost_system_, gpuSettings);
if (ghost_context_ == nullptr) {
GHOST_DisposeSystem(ghost_system_);
return false;
}
GHOST_ActivateGPUContext(ghost_context_);
gpu_context_ = GPU_context_create(nullptr, ghost_context_);
GPU_init();
return true;
}
void ShaderBuilder::exit()
void ShaderBuilder::exit_context()
{
BLI_assert(ghost_context_);
BLI_assert(gpu_context_);
GPU_exit();
GPU_context_discard(gpu_context_);
GHOST_DisposeGPUContext(ghost_system_, ghost_context_);
GHOST_DisposeSystem(ghost_system_);
gpu_context_ = nullptr;
ghost_context_ = nullptr;
}
void ShaderBuilder::exit_system()
{
GHOST_DisposeSystem(ghost_system_);
CLG_exit();
}
@ -93,13 +114,58 @@ void ShaderBuilder::exit()
/** \brief Entry point for the shader_builder. */
int main(int argc, const char *argv[])
{
if (argc < 2) {
printf("Usage: %s <data_file_to>\n", argv[0]);
std::string gpu_backend_arg;
std::string shader_name_starts_with_filter_arg;
std::string result_file_arg;
int arg = 1;
while (arg < argc) {
if (arg < argc - 2) {
blender::StringRefNull argument = argv[arg];
if (argument == "--gpu-backend") {
gpu_backend_arg = std::string(argv[arg + 1]);
arg += 2;
}
else if (argument == "--gpu-shader-filter") {
shader_name_starts_with_filter_arg = std::string(argv[arg + 1]);
arg += 2;
}
else {
break;
}
}
else if (arg == argc - 1) {
result_file_arg = argv[arg];
arg += 1;
}
else {
break;
}
}
if (result_file_arg.empty() || (!ELEM(gpu_backend_arg, "", "vulkan", "metal", "opengl"))) {
std::cout << "Usage: " << argv[0];
std::cout << " [--gpu-backend ";
#ifdef WITH_METAL_BACKEND
std::cout << "metal";
#endif
#ifdef WITH_OPENGL_BACKEND
std::cout << "opengl";
#endif
#ifdef WITH_VULKAN_BACKEND
std::cout << ",vulkan";
#endif
std::cout << "]";
std::cout << " [--gpu-shader-filter <shader-name>]";
std::cout << " <data_file_out>\n";
exit(1);
}
int exit_code = 0;
blender::gpu::shader_builder::ShaderBuilder builder;
builder.init_system();
struct NamedBackend {
std::string name;
eGPUBackendType backend;
@ -107,14 +173,21 @@ int main(int argc, const char *argv[])
blender::Vector<NamedBackend> backends_to_validate;
#ifdef WITH_OPENGL_BACKEND
backends_to_validate.append({"OpenGL", GPU_BACKEND_OPENGL});
if (ELEM(gpu_backend_arg, "", "opengl")) {
backends_to_validate.append({"OpenGL", GPU_BACKEND_OPENGL});
}
#endif
#ifdef WITH_METAL_BACKEND
backends_to_validate.append({"Metal", GPU_BACKEND_METAL});
if (ELEM(gpu_backend_arg, "", "metal")) {
backends_to_validate.append({"Metal", GPU_BACKEND_METAL});
}
#endif
#ifdef WITH_VULKAN_BACKEND
backends_to_validate.append({"Vulkan", GPU_BACKEND_VULKAN});
if (ELEM(gpu_backend_arg, "", "vulkan")) {
backends_to_validate.append({"Vulkan", GPU_BACKEND_VULKAN});
}
#endif
for (NamedBackend &backend : backends_to_validate) {
GPU_backend_type_selection_set(backend.backend);
if (!GPU_backend_supported()) {
@ -122,18 +195,24 @@ int main(int argc, const char *argv[])
backend.name.c_str());
continue;
}
blender::gpu::shader_builder::ShaderBuilder builder;
builder.init();
if (!builder.bake_create_infos()) {
printf("Shader compilation failed for %s backend\n", backend.name.c_str());
exit_code = 1;
if (builder.init_context()) {
if (!builder.bake_create_infos(shader_name_starts_with_filter_arg.c_str())) {
printf("Shader compilation failed for %s backend\n", backend.name.c_str());
exit_code = 1;
}
else {
printf("%s backend shader compilation succeeded.\n", backend.name.c_str());
}
builder.exit_context();
}
else {
printf("%s backend shader compilation succeeded.\n", backend.name.c_str());
printf("Shader compilation skipped for %s backend. Context could not be created.\n",
backend.name.c_str());
}
builder.exit();
}
builder.exit_system();
exit(exit_code);
return exit_code;
}

View File

@ -524,7 +524,7 @@ void gpu_shader_create_info_init()
}
/* TEST */
// gpu_shader_create_info_compile_all();
// gpu_shader_create_info_compile(nullptr);
}
void gpu_shader_create_info_exit()
@ -540,15 +540,22 @@ void gpu_shader_create_info_exit()
delete g_interfaces;
}
bool gpu_shader_create_info_compile_all()
bool gpu_shader_create_info_compile(const char *name_starts_with_filter)
{
using namespace blender::gpu;
int success = 0;
int skipped_filter = 0;
int skipped = 0;
int total = 0;
for (ShaderCreateInfo *info : g_create_infos->values()) {
info->finalize();
if (info->do_static_compilation_) {
if (name_starts_with_filter &&
!info->name_.startswith(blender::StringRefNull(name_starts_with_filter)))
{
skipped_filter++;
continue;
}
if ((info->metal_backend_only_ && GPU_backend_get_type() != GPU_BACKEND_METAL) ||
(GPU_compute_shader_support() == false && info->compute_source_ != nullptr) ||
(GPU_geometry_shader_support() == false && info->geometry_source_ != nullptr) ||
@ -614,6 +621,9 @@ bool gpu_shader_create_info_compile_all()
}
}
printf("Shader Test compilation result: %d / %d passed", success, total);
if (skipped_filter > 0) {
printf(" (skipped %d when filtering)", skipped_filter);
}
if (skipped > 0) {
printf(" (skipped %d for compatibility reasons)", skipped);
}

View File

@ -22,7 +22,7 @@ extern "C" {
void gpu_shader_create_info_init();
void gpu_shader_create_info_exit();
bool gpu_shader_create_info_compile_all();
bool gpu_shader_create_info_compile(const char *name_starts_with_filter);
/** Runtime create infos are not registered in the dictionary and cannot be searched. */
const GPUShaderCreateInfo *gpu_shader_create_info_get(const char *info_name);

View File

@ -31,7 +31,7 @@ using namespace blender::gpu::shader;
static void test_shader_compile_statically_defined()
{
EXPECT_TRUE(gpu_shader_create_info_compile_all());
EXPECT_TRUE(gpu_shader_create_info_compile(nullptr));
}
GPU_TEST(shader_compile_statically_defined)