diff options
Diffstat (limited to 'src/glsl')
-rw-r--r-- | src/glsl/glslang.cc | 121 | ||||
-rw-r--r-- | src/glsl/glslang.h | 57 | ||||
-rw-r--r-- | src/glsl/glslang_resources.c | 132 | ||||
-rw-r--r-- | src/glsl/meson.build | 73 | ||||
-rw-r--r-- | src/glsl/spirv.c | 64 | ||||
-rw-r--r-- | src/glsl/spirv.h | 50 | ||||
-rw-r--r-- | src/glsl/spirv_glslang.c | 112 | ||||
-rw-r--r-- | src/glsl/spirv_shaderc.c | 174 | ||||
-rw-r--r-- | src/glsl/utils.h | 52 |
9 files changed, 835 insertions, 0 deletions
diff --git a/src/glsl/glslang.cc b/src/glsl/glslang.cc new file mode 100644 index 0000000..2bc923c --- /dev/null +++ b/src/glsl/glslang.cc @@ -0,0 +1,121 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "config_internal.h" + +#include <assert.h> + +extern "C" { +#include "pl_alloc.h" +#include "pl_thread.h" +} + +#include <glslang/Public/ShaderLang.h> +#include <glslang/SPIRV/GlslangToSpv.h> +#include <glslang/build_info.h> + +#include "glslang.h" + +#if (GLSLANG_VERSION_MAJOR * 1000 + GLSLANG_VERSION_MINOR) >= 11013 +#include <glslang/Public/ResourceLimits.h> +#define DefaultTBuiltInResource *GetDefaultResources() +#endif + +using namespace glslang; + +static pl_static_mutex pl_glslang_mutex = PL_STATIC_MUTEX_INITIALIZER; +static int pl_glslang_refcount; + +bool pl_glslang_init(void) +{ + bool ret = true; + + pl_static_mutex_lock(&pl_glslang_mutex); + if (pl_glslang_refcount++ == 0) + ret = InitializeProcess(); + pl_static_mutex_unlock(&pl_glslang_mutex); + + return ret; +} + +void pl_glslang_uninit(void) +{ + pl_static_mutex_lock(&pl_glslang_mutex); + if (--pl_glslang_refcount == 0) + FinalizeProcess(); + pl_static_mutex_unlock(&pl_glslang_mutex); +} + +struct pl_glslang_res *pl_glslang_compile(struct pl_glsl_version glsl_ver, + struct pl_spirv_version spirv_ver, + enum glsl_shader_stage stage, + const char *text) +{ + assert(pl_glslang_refcount); + struct pl_glslang_res *res = pl_zalloc_ptr(NULL, res); + + EShLanguage lang; + switch (stage) { + case GLSL_SHADER_VERTEX: lang = EShLangVertex; break; + case GLSL_SHADER_FRAGMENT: lang = EShLangFragment; break; + case GLSL_SHADER_COMPUTE: lang = EShLangCompute; break; + default: abort(); + } + + TShader *shader = new TShader(lang); + + shader->setEnvClient(EShClientVulkan, (EShTargetClientVersion) spirv_ver.env_version); + shader->setEnvTarget(EShTargetSpv, (EShTargetLanguageVersion) spirv_ver.spv_version); + shader->setStrings(&text, 1); + + TBuiltInResource limits = DefaultTBuiltInResource; + limits.maxComputeWorkGroupSizeX = glsl_ver.max_group_size[0]; + limits.maxComputeWorkGroupSizeY = glsl_ver.max_group_size[1]; + limits.maxComputeWorkGroupSizeZ = glsl_ver.max_group_size[2]; + limits.minProgramTexelOffset = glsl_ver.min_gather_offset; + limits.maxProgramTexelOffset = glsl_ver.max_gather_offset; + + if (!shader->parse(&limits, 0, true, EShMsgDefault)) { + res->error_msg = pl_str0dup0(res, shader->getInfoLog()); + delete shader; + return res; + } + + TProgram *prog = new TProgram(); + prog->addShader(shader); + if (!prog->link(EShMsgDefault)) { + res->error_msg = pl_str0dup0(res, prog->getInfoLog()); + delete shader; + delete prog; + return res; + } + + SpvOptions options; + options.disableOptimizer = false; + options.stripDebugInfo = true; + options.optimizeSize = true; + options.validate = true; + std::vector<unsigned int> spirv; + GlslangToSpv(*prog->getIntermediate(lang), spirv, &options); + + res->success = true; + res->size = spirv.size() * sizeof(unsigned int); + res->data = pl_memdup(res, spirv.data(), res->size), + delete shader; + delete prog; + return res; +} diff --git a/src/glsl/glslang.h b/src/glsl/glslang.h new file mode 100644 index 0000000..a5965a5 --- /dev/null +++ b/src/glsl/glslang.h @@ -0,0 +1,57 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdlib.h> +#include <stdbool.h> + +typedef struct TLimits TLimits; +typedef struct TBuiltInResource TBuiltInResource; +#include <glslang/Include/ResourceLimits.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include "utils.h" + +bool pl_glslang_init(void); +void pl_glslang_uninit(void); + +struct pl_glslang_res { + // Compilation status + bool success; + const char *error_msg; + + // Compiled shader memory, or NULL + void *data; + size_t size; +}; + +// Compile GLSL into a SPIRV stream, if possible. The resulting +// pl_glslang_res can simply be freed with pl_free() when done. +struct pl_glslang_res *pl_glslang_compile(struct pl_glsl_version glsl_ver, + struct pl_spirv_version spirv_ver, + enum glsl_shader_stage stage, + const char *shader); + +extern const TBuiltInResource DefaultTBuiltInResource; + +#ifdef __cplusplus +} +#endif diff --git a/src/glsl/glslang_resources.c b/src/glsl/glslang_resources.c new file mode 100644 index 0000000..a111c15 --- /dev/null +++ b/src/glsl/glslang_resources.c @@ -0,0 +1,132 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "glslang.h" + +// Taken from glslang's examples, which apparently generally bases the choices +// on OpenGL specification limits +// +// Note: This lives in a separate file so we can compile this struct using C99 +// designated initializers instead of using C++ struct initializers, because +// the latter will break on every upstream struct extension. +const TBuiltInResource DefaultTBuiltInResource = { + .maxLights = 32, + .maxClipPlanes = 6, + .maxTextureUnits = 32, + .maxTextureCoords = 32, + .maxVertexAttribs = 64, + .maxVertexUniformComponents = 4096, + .maxVaryingFloats = 64, + .maxVertexTextureImageUnits = 32, + .maxCombinedTextureImageUnits = 80, + .maxTextureImageUnits = 32, + .maxFragmentUniformComponents = 4096, + .maxDrawBuffers = 32, + .maxVertexUniformVectors = 128, + .maxVaryingVectors = 8, + .maxFragmentUniformVectors = 16, + .maxVertexOutputVectors = 16, + .maxFragmentInputVectors = 15, + .minProgramTexelOffset = -8, + .maxProgramTexelOffset = 7, + .maxClipDistances = 8, + .maxComputeWorkGroupCountX = 65535, + .maxComputeWorkGroupCountY = 65535, + .maxComputeWorkGroupCountZ = 65535, + .maxComputeWorkGroupSizeX = 1024, + .maxComputeWorkGroupSizeY = 1024, + .maxComputeWorkGroupSizeZ = 64, + .maxComputeUniformComponents = 1024, + .maxComputeTextureImageUnits = 16, + .maxComputeImageUniforms = 8, + .maxComputeAtomicCounters = 8, + .maxComputeAtomicCounterBuffers = 1, + .maxVaryingComponents = 60, + .maxVertexOutputComponents = 64, + .maxGeometryInputComponents = 64, + .maxGeometryOutputComponents = 128, + .maxFragmentInputComponents = 128, + .maxImageUnits = 8, + .maxCombinedImageUnitsAndFragmentOutputs = 8, + .maxCombinedShaderOutputResources = 8, + .maxImageSamples = 0, + .maxVertexImageUniforms = 0, + .maxTessControlImageUniforms = 0, + .maxTessEvaluationImageUniforms = 0, + .maxGeometryImageUniforms = 0, + .maxFragmentImageUniforms = 8, + .maxCombinedImageUniforms = 8, + .maxGeometryTextureImageUnits = 16, + .maxGeometryOutputVertices = 256, + .maxGeometryTotalOutputComponents = 1024, + .maxGeometryUniformComponents = 1024, + .maxGeometryVaryingComponents = 64, + .maxTessControlInputComponents = 128, + .maxTessControlOutputComponents = 128, + .maxTessControlTextureImageUnits = 16, + .maxTessControlUniformComponents = 1024, + .maxTessControlTotalOutputComponents = 4096, + .maxTessEvaluationInputComponents = 128, + .maxTessEvaluationOutputComponents = 128, + .maxTessEvaluationTextureImageUnits = 16, + .maxTessEvaluationUniformComponents = 1024, + .maxTessPatchComponents = 120, + .maxPatchVertices = 32, + .maxTessGenLevel = 64, + .maxViewports = 16, + .maxVertexAtomicCounters = 0, + .maxTessControlAtomicCounters = 0, + .maxTessEvaluationAtomicCounters = 0, + .maxGeometryAtomicCounters = 0, + .maxFragmentAtomicCounters = 8, + .maxCombinedAtomicCounters = 8, + .maxAtomicCounterBindings = 1, + .maxVertexAtomicCounterBuffers = 0, + .maxTessControlAtomicCounterBuffers = 0, + .maxTessEvaluationAtomicCounterBuffers = 0, + .maxGeometryAtomicCounterBuffers = 0, + .maxFragmentAtomicCounterBuffers = 1, + .maxCombinedAtomicCounterBuffers = 1, + .maxAtomicCounterBufferSize = 16384, + .maxTransformFeedbackBuffers = 4, + .maxTransformFeedbackInterleavedComponents = 64, + .maxCullDistances = 8, + .maxCombinedClipAndCullDistances = 8, + .maxSamples = 4, + .maxMeshOutputVerticesNV = 256, + .maxMeshOutputPrimitivesNV = 512, + .maxMeshWorkGroupSizeX_NV = 32, + .maxMeshWorkGroupSizeY_NV = 1, + .maxMeshWorkGroupSizeZ_NV = 1, + .maxTaskWorkGroupSizeX_NV = 32, + .maxTaskWorkGroupSizeY_NV = 1, + .maxTaskWorkGroupSizeZ_NV = 1, + .maxMeshViewCountNV = 4, + .maxDualSourceDrawBuffersEXT = 1, + + .limits = { + .nonInductiveForLoops = 1, + .whileLoops = 1, + .doWhileLoops = 1, + .generalUniformIndexing = 1, + .generalAttributeMatrixVectorIndexing = 1, + .generalVaryingIndexing = 1, + .generalSamplerIndexing = 1, + .generalVariableIndexing = 1, + .generalConstantMatrixVectorIndexing = 1, + }, +}; diff --git a/src/glsl/meson.build b/src/glsl/meson.build new file mode 100644 index 0000000..5cebfb8 --- /dev/null +++ b/src/glsl/meson.build @@ -0,0 +1,73 @@ +# shaderc +shaderc = dependency('shaderc', version: '>=2019.1', required: get_option('shaderc')) +components.set('shaderc', shaderc.found()) +if shaderc.found() + build_deps += shaderc + sources += 'glsl/spirv_shaderc.c' +endif + +# glslang +glslang = disabler() +glslang_req = get_option('glslang') +if glslang_req.auto() and shaderc.found() + + # we only need one or the other, and shaderc is preferred + message('Skipping `glslang` because `shaderc` is available') + +elif not glslang_req.disabled() + + glslang_deps = [ + cxx.find_library('glslang-default-resource-limits', required: false) + ] + + # meson doesn't respect generator expressions in INTERFACE_LINK_LIBRARIES + # https://github.com/mesonbuild/meson/issues/8232 + # TODO: Use the following once it's fixed + # glslang = dependency('glslang', method: 'cmake', modules: ['glslang::SPIRV']) + + prefer_static = get_option('prefer_static') + found_lib = false + foreach arg : [[prefer_static, false], [not prefer_static, glslang_req]] + static = arg[0] + required = arg[1] + + spirv = cxx.find_library('SPIRV', required: required, static: static) + + if not spirv.found() + continue + endif + + glslang_deps += spirv + + if static + glslang_deps += [ + # Always required for static linking + cxx.find_library('MachineIndependent', required: true, static: true), + cxx.find_library('OSDependent', required: true, static: true), + cxx.find_library('OGLCompiler', required: true, static: true), + cxx.find_library('GenericCodeGen', required: true, static: true), + # SPIRV-Tools are required only if optimizer is enabled in glslang build + cxx.find_library('SPIRV-Tools', required: false, static: true), + cxx.find_library('SPIRV-Tools-opt', required: false, static: true), + ] + endif + + found_lib = true + break + endforeach + + if found_lib and cc.has_header('glslang/build_info.h') + glslang = declare_dependency(dependencies: glslang_deps) + endif + +endif + +components.set('glslang', glslang.found()) +if glslang.found() + build_deps += glslang + sources += [ + 'glsl/glslang.cc', + 'glsl/glslang_resources.c', + 'glsl/spirv_glslang.c', + ] +endif diff --git a/src/glsl/spirv.c b/src/glsl/spirv.c new file mode 100644 index 0000000..8317ed7 --- /dev/null +++ b/src/glsl/spirv.c @@ -0,0 +1,64 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "spirv.h" + +extern const struct spirv_compiler pl_spirv_shaderc; +extern const struct spirv_compiler pl_spirv_glslang; + +static const struct spirv_compiler *compilers[] = { +#ifdef PL_HAVE_SHADERC + &pl_spirv_shaderc, +#endif +#ifdef PL_HAVE_GLSLANG + &pl_spirv_glslang, +#endif +}; + +pl_spirv pl_spirv_create(pl_log log, struct pl_spirv_version spirv_ver) +{ + for (int i = 0; i < PL_ARRAY_SIZE(compilers); i++) { + pl_spirv spirv = compilers[i]->create(log, spirv_ver); + if (!spirv) + continue; + + pl_info(log, "Initialized SPIR-V compiler '%s'", compilers[i]->name); + return spirv; + } + + pl_fatal(log, "Failed initializing any SPIR-V compiler! Maybe libplacebo " + "was built without support for either libshaderc or glslang?"); + return NULL; +} + +void pl_spirv_destroy(pl_spirv *pspirv) +{ + pl_spirv spirv = *pspirv; + if (!spirv) + return; + + spirv->impl->destroy(spirv); + *pspirv = NULL; +} + +pl_str pl_spirv_compile_glsl(pl_spirv spirv, void *alloc, + struct pl_glsl_version glsl, + enum glsl_shader_stage stage, + const char *shader) +{ + return spirv->impl->compile(spirv, alloc, glsl, stage, shader); +} diff --git a/src/glsl/spirv.h b/src/glsl/spirv.h new file mode 100644 index 0000000..fa4494a --- /dev/null +++ b/src/glsl/spirv.h @@ -0,0 +1,50 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "log.h" +#include "utils.h" + +typedef const struct pl_spirv_t { + const struct spirv_compiler *impl; + pl_log log; + + // SPIR-V version specified at creation time. + struct pl_spirv_version version; + + // For cache invalidation, should uniquely identify everything about this + // spirv compiler and its configuration. + uint64_t signature; +} *pl_spirv; + +// Initialize a SPIR-V compiler instance, or returns NULL on failure. +pl_spirv pl_spirv_create(pl_log log, struct pl_spirv_version spirv_ver); +void pl_spirv_destroy(pl_spirv *spirv); + +// Compile GLSL to SPIR-V. Returns {0} on failure. +pl_str pl_spirv_compile_glsl(pl_spirv spirv, void *alloc, + struct pl_glsl_version glsl_ver, + enum glsl_shader_stage stage, + const char *shader); + +struct spirv_compiler { + const char *name; + void (*destroy)(pl_spirv spirv); + __typeof__(pl_spirv_create) *create; + __typeof__(pl_spirv_compile_glsl) *compile; +}; diff --git a/src/glsl/spirv_glslang.c b/src/glsl/spirv_glslang.c new file mode 100644 index 0000000..ffb8f55 --- /dev/null +++ b/src/glsl/spirv_glslang.c @@ -0,0 +1,112 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "hash.h" +#include "spirv.h" +#include "utils.h" +#include "glsl/glslang.h" + +// This header contains only preprocessor definitions +#include <glslang/build_info.h> + +// This is awkward, but we cannot use upstream macro, it was fixed in 11.11.0 +#define PL_GLSLANG_VERSION_GREATER_THAN(major, minor, patch) \ + ((GLSLANG_VERSION_MAJOR) > (major) || ((major) == GLSLANG_VERSION_MAJOR && \ + ((GLSLANG_VERSION_MINOR) > (minor) || ((minor) == GLSLANG_VERSION_MINOR && \ + (GLSLANG_VERSION_PATCH) > (patch))))) + +#if PL_GLSLANG_VERSION_GREATER_THAN(11, 8, 0) +#define GLSLANG_SPV_MAX PL_SPV_VERSION(1, 6) +#elif PL_GLSLANG_VERSION_GREATER_THAN(7, 13, 3496) +#define GLSLANG_SPV_MAX PL_SPV_VERSION(1, 5) +#elif PL_GLSLANG_VERSION_GREATER_THAN(6, 2, 2596) +#define GLSLANG_SPV_MAX PL_SPV_VERSION(1, 3) +#else +#define GLSLANG_SPV_MAX PL_SPV_VERSION(1, 0) +#endif + +const struct spirv_compiler pl_spirv_glslang; + +static void glslang_destroy(pl_spirv spirv) +{ + pl_glslang_uninit(); + pl_free((void *) spirv); +} + +static pl_spirv glslang_create(pl_log log, struct pl_spirv_version spirv_ver) +{ + if (!pl_glslang_init()) { + pl_fatal(log, "Failed initializing glslang SPIR-V compiler!"); + return NULL; + } + + struct pl_spirv_t *spirv = pl_alloc_ptr(NULL, spirv); + *spirv = (struct pl_spirv_t) { + .signature = pl_str0_hash(pl_spirv_glslang.name), + .impl = &pl_spirv_glslang, + .version = spirv_ver, + .log = log, + }; + + PL_INFO(spirv, "glslang version: %d.%d.%d", + GLSLANG_VERSION_MAJOR, + GLSLANG_VERSION_MINOR, + GLSLANG_VERSION_PATCH); + + // Clamp to supported version by glslang + if (GLSLANG_SPV_MAX < spirv->version.spv_version) { + spirv->version.spv_version = GLSLANG_SPV_MAX; + spirv->version.env_version = pl_spirv_version_to_vulkan(GLSLANG_SPV_MAX); + } + + pl_hash_merge(&spirv->signature, (uint64_t) spirv->version.spv_version << 32 | + spirv->version.env_version); + pl_hash_merge(&spirv->signature, (GLSLANG_VERSION_MAJOR & 0xFF) << 24 | + (GLSLANG_VERSION_MINOR & 0xFF) << 16 | + (GLSLANG_VERSION_PATCH & 0xFFFF)); + return spirv; +} + +static pl_str glslang_compile(pl_spirv spirv, void *alloc, + struct pl_glsl_version glsl_ver, + enum glsl_shader_stage stage, + const char *shader) +{ + struct pl_glslang_res *res; + + res = pl_glslang_compile(glsl_ver, spirv->version, stage, shader); + if (!res || !res->success) { + PL_ERR(spirv, "glslang failed: %s", res ? res->error_msg : "(null)"); + pl_free(res); + return (struct pl_str) {0}; + } + + struct pl_str ret = { + .buf = pl_steal(alloc, res->data), + .len = res->size, + }; + + pl_free(res); + return ret; +} + +const struct spirv_compiler pl_spirv_glslang = { + .name = "glslang", + .destroy = glslang_destroy, + .create = glslang_create, + .compile = glslang_compile, +}; diff --git a/src/glsl/spirv_shaderc.c b/src/glsl/spirv_shaderc.c new file mode 100644 index 0000000..e384382 --- /dev/null +++ b/src/glsl/spirv_shaderc.c @@ -0,0 +1,174 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <shaderc/shaderc.h> + +#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, +}; diff --git a/src/glsl/utils.h b/src/glsl/utils.h new file mode 100644 index 0000000..965ea9e --- /dev/null +++ b/src/glsl/utils.h @@ -0,0 +1,52 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdbool.h> +#include <stdint.h> + +#include <libplacebo/gpu.h> + +#define PL_SPV_VERSION(major, minor) ((major) << 16 | (minor) << 8) +#define PL_VLK_VERSION(major, minor) ((major) << 22 | (minor) << 12) + +// Max version that can be used +#define PL_MAX_SPIRV_VER PL_SPV_VERSION(1, 6) + +struct pl_spirv_version { + uint32_t env_version; + uint32_t spv_version; +}; + +// Returns minimum Vulkan version for given SPIR-V version +static inline uint32_t pl_spirv_version_to_vulkan(uint32_t spirv_ver) +{ + if (spirv_ver >= PL_SPV_VERSION(1, 6)) + return PL_VLK_VERSION(1, 3); + if (spirv_ver >= PL_SPV_VERSION(1, 4)) + return PL_VLK_VERSION(1, 2); + if (spirv_ver >= PL_SPV_VERSION(1, 1)) + return PL_VLK_VERSION(1, 1); + return PL_VLK_VERSION(1, 0); +} + +enum glsl_shader_stage { + GLSL_SHADER_VERTEX = 0, + GLSL_SHADER_FRAGMENT, + GLSL_SHADER_COMPUTE, +}; |