spirv: add support for glslang

We can now use glslang directly instead of relying on libshaderc for
everything. This requires a bit of bending over backwards, but it's
doable.

Fixes #36
parent 4d43189e
project('libplacebo', 'c',
project('libplacebo', ['c', 'cpp'],
license: 'LGPL2.1+',
default_options: ['c_std=c99'],
)
......
......@@ -2,6 +2,9 @@
option('vulkan', type: 'combo', choices: ['auto', 'true', 'false'],
description: 'Vulkan-based renderer')
option('glslang', type: 'combo', choices: ['auto', 'true', 'false'],
description: 'glslang SPIR-V compiler')
option('shaderc', type: 'combo', choices: ['auto', 'true', 'false'],
description: 'libshaderc SPIR-V compiler')
......
/*
* 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 <assert.h>
#include <pthread.h>
extern "C" {
#include "ta/talloc.h"
}
#include <glslang/Include/ResourceLimits.h>
#include <glslang/Include/revision.h>
#include <glslang/Public/ShaderLang.h>
#include <SPIRV/GlslangToSpv.h>
#include "glslang.h"
using namespace glslang;
static pthread_mutex_t pl_glslang_mutex = PTHREAD_MUTEX_INITIALIZER;
static int pl_glslang_refcount;
int pl_glslang_version()
{
return GLSLANG_PATCH_LEVEL;
}
bool pl_glslang_init()
{
bool ret = true;
pthread_mutex_lock(&pl_glslang_mutex);
if (pl_glslang_refcount++ == 0)
ret = InitializeProcess();
pthread_mutex_unlock(&pl_glslang_mutex);
return ret;
}
void pl_glslang_uninit()
{
pthread_mutex_lock(&pl_glslang_mutex);
if (--pl_glslang_refcount == 0)
FinalizeProcess();
pthread_mutex_unlock(&pl_glslang_mutex);
}
#define GLSL_VERSION EShTargetVulkan_1_0
#define SPIRV_VERSION EShTargetSpv_1_0
extern const TBuiltInResource DefaultTBuiltInResource;
struct pl_glslang_res *pl_glslang_compile(const char *glsl,
enum pl_glslang_stage stage)
{
struct pl_glslang_res *res = talloc_zero(NULL, struct pl_glslang_res);
static const EShLanguage lang[] = {
[PL_GLSLANG_VERTEX] = EShLangVertex,
[PL_GLSLANG_FRAGMENT] = EShLangFragment,
[PL_GLSLANG_COMPUTE] = EShLangCompute,
};
assert(pl_glslang_refcount);
TShader *shader = new TShader(lang[stage]);
shader->setEnvClient(EShClientVulkan, GLSL_VERSION);
shader->setEnvTarget(EShTargetSpv, SPIRV_VERSION);
shader->setStrings(&glsl, 1);
if (!shader->parse(&DefaultTBuiltInResource, GLSL_VERSION, true, EShMsgDefault)) {
res->error_msg = talloc_strdup(res, shader->getInfoLog());
delete shader;
return res;
}
TProgram *prog = new TProgram();
prog->addShader(shader);
if (!prog->link(EShMsgDefault)) {
res->error_msg = talloc_strdup(res, prog->getInfoLog());
delete shader;
delete prog;
return res;
}
std::vector<unsigned int> spirv;
GlslangToSpv(*prog->getIntermediate(lang[stage]), spirv);
res->success = true;
res->size = spirv.size() * sizeof(unsigned int);
res->data = talloc_memdup(res, spirv.data(), res->size),
delete shader;
delete prog;
return res;
}
// Taken from glslang's examples, which apparently generally bases the choices
// on OpenGL specification limits
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,
/* .limits = */ {
/* .nonInductiveForLoops = */ 1,
/* .whileLoops = */ 1,
/* .doWhileLoops = */ 1,
/* .generalUniformIndexing = */ 1,
/* .generalAttributeMatrixVectorIndexing = */ 1,
/* .generalVaryingIndexing = */ 1,
/* .generalSamplerIndexing = */ 1,
/* .generalVariableIndexing = */ 1,
/* .generalConstantMatrixVectorIndexing = */ 1,
}
};
/*
* 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>
#ifdef __cplusplus
extern "C" {
#endif
int pl_glslang_version();
bool pl_glslang_init();
void pl_glslang_uninit();
struct pl_glslang_res {
// Compilation status
bool success;
const char *error_msg;
// Compiled shader memory, or NULL
void *data;
size_t size;
};
enum pl_glslang_stage {
PL_GLSLANG_VERTEX,
PL_GLSLANG_FRAGMENT,
PL_GLSLANG_COMPUTE,
};
// Compile GLSL into a SPIRV stream, if possible. The resulting
// pl_glslang_res can simply be freed with talloc_free() when done.
struct pl_glslang_res *pl_glslang_compile(const char *glsl,
enum pl_glslang_stage stage);
#ifdef __cplusplus
}
#endif
......@@ -18,6 +18,8 @@ build_opts = [
]
cc = meson.get_compiler('c')
cxx = meson.get_compiler('cpp')
if cc.has_argument('-Wincompatible-pointer-types')
build_opts += ['-Werror=incompatible-pointer-types']
endif
......@@ -69,12 +71,49 @@ tests = [
'utils.c',
]
# Work-arounds for glslang braindeath
glslang_deps = [
cxx.find_library('glslang', required: false),
cxx.find_library('HLSL', required: false),
cxx.find_library('OGLCompiler', required: false),
cxx.find_library('OSDependent', required: false),
cxx.find_library('SPIRV', required: false),
cxx.find_library('SPVRemapper', required: false),
]
glslang_found = true
foreach d : glslang_deps
glslang_found = glslang_found and d.found()
endforeach
glslang_combined = disabler()
glslang_ver_test = '''
#include <glslang/Include/revision.h>
#if GLSLANG_PATCH_LEVEL < 2763
#error glslang version too old
#endif
'''
if glslang_found
if cxx.compiles(glslang_ver_test, dependencies: glslang_deps)
glslang_combined = declare_dependency(dependencies: glslang_deps)
else
error('glslang version too old! GLSLANG_PATCH_LEVEL must be at least 2763')
endif
endif
# Optional components, in the following format:
# [ name, dependency, extra_sources, extra_tests ]
components = [
[ 'lcms',
dependency('lcms2', version: '>=2.6', required: false),
[ 'lcms.c' ],
'lcms.c',
], [
'glslang',
glslang_combined,
[ 'glsl/glslang.cc',
'spirv_glslang.c',
],
], [
'shaderc',
cc.find_library('shaderc_shared', required: false),
......
......@@ -18,11 +18,15 @@
#include "spirv.h"
extern const struct spirv_compiler_fns spirv_shaderc;
extern const struct spirv_compiler_fns spirv_glslang;
static const struct spirv_compiler_fns *compilers[] = {
#if PL_HAVE_SHADERC
&spirv_shaderc,
#endif
#if PL_HAVE_GLSLANG
&spirv_glslang,
#endif
};
struct spirv_compiler *spirv_compiler_create(struct pl_context *ctx)
......@@ -41,8 +45,8 @@ struct spirv_compiler *spirv_compiler_create(struct pl_context *ctx)
talloc_free(spirv);
}
pl_fatal(ctx, "Failed initializing any SPIR-V compiler! Maybe "
"libplacebo was built without support for libshaderc?");
pl_fatal(ctx, "Failed initializing any SPIR-V compiler! Maybe libplacebo "
"was built without support for either libshaderc or glslang?");
return NULL;
}
......
/*
* 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"
#include "glsl/glslang.h"
static void glslang_uninit(struct spirv_compiler *spirv)
{
pl_glslang_uninit();
}
static bool glslang_init(struct spirv_compiler *spirv)
{
if (!pl_glslang_init()) {
PL_FATAL(spirv, "Failed initializing glslang SPIR-V compiler!");
return false;
}
spirv->compiler_version = pl_glslang_version();
spirv->glsl = (struct pl_glsl_desc) {
.version = 450,
.vulkan = true,
};
return true;
}
static bool glslang_compile(struct spirv_compiler *spirv, void *tactx,
enum glsl_shader_stage type, const char *glsl,
struct bstr *out_spirv)
{
static const enum pl_glslang_stage stages[] = {
[GLSL_SHADER_VERTEX] = PL_GLSLANG_VERTEX,
[GLSL_SHADER_FRAGMENT] = PL_GLSLANG_FRAGMENT,
[GLSL_SHADER_COMPUTE] = PL_GLSLANG_COMPUTE,
};
struct pl_glslang_res *res = pl_glslang_compile(glsl, stages[type]);
if (!res || !res->success) {
PL_ERR(spirv, "glslang failed: %s", res ? res->error_msg : "(null)");
talloc_free(res);
return false;
}
out_spirv->start = talloc_steal(tactx, res->data);
out_spirv->len = res->size;
talloc_free(res);
return true;
}
const struct spirv_compiler_fns spirv_glslang = {
.name = "glslang",
.compile_glsl = glslang_compile,
.init = glslang_init,
.uninit = glslang_uninit,
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment