/* * This file is part of libplacebo. * * libplacebo is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * libplacebo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with libplacebo. If not, see . */ #include #include #include "hash.h" #include "spirv.h" #include "utils.h" const struct spirv_compiler pl_spirv_shaderc; struct priv { shaderc_compiler_t compiler; }; static void shaderc_destroy(pl_spirv spirv) { struct priv *p = PL_PRIV(spirv); shaderc_compiler_release(p->compiler); pl_free((void *) spirv); } static pl_spirv shaderc_create(pl_log log, struct pl_spirv_version spirv_ver) { struct pl_spirv_t *spirv = pl_alloc_obj(NULL, spirv, struct priv); *spirv = (struct pl_spirv_t) { .signature = pl_str0_hash(pl_spirv_shaderc.name), .impl = &pl_spirv_shaderc, .version = spirv_ver, .log = log, }; struct priv *p = PL_PRIV(spirv); p->compiler = shaderc_compiler_initialize(); if (!p->compiler) goto error; unsigned int ver = 0, rev = 0; shaderc_get_spv_version(&ver, &rev); PL_INFO(spirv, "shaderc SPIR-V version %u.%u rev %u", ver >> 16, (ver >> 8) & 0xff, rev); // Clamp to supported version by shaderc if (ver < spirv->version.spv_version) { spirv->version.spv_version = ver; spirv->version.env_version = pl_spirv_version_to_vulkan(ver); } pl_hash_merge(&spirv->signature, (uint64_t) spirv->version.spv_version << 32 | spirv->version.env_version); pl_hash_merge(&spirv->signature, (uint64_t) ver << 32 | rev); return spirv; error: shaderc_destroy(spirv); return NULL; } static pl_str shaderc_compile(pl_spirv spirv, void *alloc, struct pl_glsl_version glsl_ver, enum glsl_shader_stage stage, const char *shader) { struct priv *p = PL_PRIV(spirv); const size_t len = strlen(shader); shaderc_compile_options_t opts = shaderc_compile_options_initialize(); if (!opts) return (pl_str) {0}; shaderc_compile_options_set_optimization_level(opts, shaderc_optimization_level_performance); shaderc_compile_options_set_target_spirv(opts, spirv->version.spv_version); shaderc_compile_options_set_target_env(opts, shaderc_target_env_vulkan, spirv->version.env_version); for (int i = 0; i < 3; i++) { shaderc_compile_options_set_limit(opts, shaderc_limit_max_compute_work_group_size_x + i, glsl_ver.max_group_size[i]); } shaderc_compile_options_set_limit(opts, shaderc_limit_min_program_texel_offset, glsl_ver.min_gather_offset); shaderc_compile_options_set_limit(opts, shaderc_limit_max_program_texel_offset, glsl_ver.max_gather_offset); static const shaderc_shader_kind kinds[] = { [GLSL_SHADER_VERTEX] = shaderc_glsl_vertex_shader, [GLSL_SHADER_FRAGMENT] = shaderc_glsl_fragment_shader, [GLSL_SHADER_COMPUTE] = shaderc_glsl_compute_shader, }; static const char * const file_name = "input"; static const char * const entry_point = "main"; shaderc_compilation_result_t res; res = shaderc_compile_into_spv(p->compiler, shader, len, kinds[stage], file_name, entry_point, opts); int errs = shaderc_result_get_num_errors(res), warn = shaderc_result_get_num_warnings(res); enum pl_log_level lev = errs ? PL_LOG_ERR : warn ? PL_LOG_INFO : PL_LOG_DEBUG; int s = shaderc_result_get_compilation_status(res); bool success = s == shaderc_compilation_status_success; if (!success) lev = PL_LOG_ERR; const char *msg = shaderc_result_get_error_message(res); if (msg[0]) PL_MSG(spirv, lev, "shaderc output:\n%s", msg); static const char *results[] = { [shaderc_compilation_status_success] = "success", [shaderc_compilation_status_invalid_stage] = "invalid stage", [shaderc_compilation_status_compilation_error] = "error", [shaderc_compilation_status_internal_error] = "internal error", [shaderc_compilation_status_null_result_object] = "no result", [shaderc_compilation_status_invalid_assembly] = "invalid assembly", }; const char *status = s < PL_ARRAY_SIZE(results) ? results[s] : "unknown"; PL_MSG(spirv, lev, "shaderc compile status '%s' (%d errors, %d warnings)", status, errs, warn); pl_str ret = {0}; if (success) { void *bytes = (void *) shaderc_result_get_bytes(res); pl_assert(bytes); ret.len = shaderc_result_get_length(res); ret.buf = pl_memdup(alloc, bytes, ret.len); if (pl_msg_test(spirv->log, PL_LOG_TRACE)) { shaderc_compilation_result_t dis; dis = shaderc_compile_into_spv_assembly(p->compiler, shader, len, kinds[stage], file_name, entry_point, opts); PL_TRACE(spirv, "Generated SPIR-V:\n%.*s", (int) shaderc_result_get_length(dis), shaderc_result_get_bytes(dis)); shaderc_result_release(dis); } } shaderc_result_release(res); shaderc_compile_options_release(opts); return ret; } const struct spirv_compiler pl_spirv_shaderc = { .name = "shaderc", .destroy = shaderc_destroy, .create = shaderc_create, .compile = shaderc_compile, };