summaryrefslogtreecommitdiffstats
path: root/src/glsl
diff options
context:
space:
mode:
Diffstat (limited to 'src/glsl')
-rw-r--r--src/glsl/glslang.cc121
-rw-r--r--src/glsl/glslang.h57
-rw-r--r--src/glsl/glslang_resources.c132
-rw-r--r--src/glsl/meson.build73
-rw-r--r--src/glsl/spirv.c64
-rw-r--r--src/glsl/spirv.h50
-rw-r--r--src/glsl/spirv_glslang.c112
-rw-r--r--src/glsl/spirv_shaderc.c174
-rw-r--r--src/glsl/utils.h52
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,
+};