Commit 326c8944 authored by Niklas Haas's avatar Niklas Haas

spirv: port from mpv

Only supports libshaderc for now, until I have a clearer picture of what
the requirements for getting the nvidia extension detected would be.

Note entirely happy with the meson stuff, but eh; it'll work for now.
parent 2b123863
......@@ -31,4 +31,9 @@
// Friendly name (`git describe`) for the overall version of the library
#define PL_VERSION @version@
// Feature tests. These aren't described in further detail, but may be useful
// for programmers wanting to programmatically check for feature support
// in their compiled libshaderc versions.
#mesondefine PL_HAVE_SHADERC
#endif // LIBPLACEBO_CONTEXT_H_
......@@ -16,12 +16,6 @@ add_global_arguments([
cc = meson.get_compiler('c')
# Dependencies
deps = [
# libm
cc.find_library('m', required: false),
]
# Configuration
conf = configuration_data()
conf.set('majorver', majorver)
......@@ -35,12 +29,10 @@ else
conf.set_quoted('version', 'v' + version)
endif
configure_file(
input: 'config.h.in',
output: 'config.h',
install_dir: 'include/libplacebo',
configuration: conf,
)
# Build dependencies
bdeps = [
cc.find_library('m', required: false)
]
# Source files
sources = [
......@@ -49,6 +41,7 @@ sources = [
'filters.c',
'ra.c',
'shaders.c',
'spirv.c',
# Helpers ported from mpv
'bstr/bstr.c',
......@@ -57,12 +50,6 @@ sources = [
'ta/talloc.c',
]
lib = library('placebo', sources,
install: true,
dependencies: deps,
soversion: apiver,
)
headers = [
'public/colorspace.h',
'public/common.h',
......@@ -72,6 +59,30 @@ headers = [
'public/shaders.h',
]
# Optional features
libshaderc = cc.find_library('shaderc_shared', required: false)
if libshaderc.found() and cc.has_header('shaderc/shaderc.h')
bdeps += libshaderc
sources += [ 'spirv_shaderc.c' ]
conf.set('PL_HAVE_SHADERC', true)
endif
# Build process
configure_file(
input: 'config.h.in',
output: 'config.h',
install_dir: 'include/libplacebo',
configuration: conf,
)
lib = library('placebo', sources,
install: true,
dependencies: bdeps,
soversion: apiver,
)
# Install process
install_headers(headers, subdir: 'libplacebo')
pkg = import('pkgconfig')
......
......@@ -46,7 +46,7 @@ enum {
// Structure which wraps metadata describing GLSL capabilities.
struct ra_glsl_desc {
int glsl_version; // GLSL version (e.g. 450), for #version
int version; // GLSL version (e.g. 450), for #version
bool gles; // GLSL ES semantics (ESSL)
bool vulkan; // GL_KHR_vulkan_glsl semantics
ra_glsl_caps caps; // RA_GLSL_CAP_* bit field
......
/*
* 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_fns spirv_shaderc;
static const struct spirv_compiler_fns *compilers[] = {
#ifdef PL_HAVE_SHADERC
&spirv_shaderc,
#endif
};
struct spirv_compiler *spirv_compiler_create(struct pl_context *ctx)
{
for (int i = 0; i < PL_ARRAY_SIZE(compilers); i++) {
const struct spirv_compiler_fns *impl = compilers[i];
struct spirv_compiler *spirv = talloc_zero(NULL, struct spirv_compiler);
spirv->ctx = ctx;
spirv->impl = impl;
strncpy(spirv->name, impl->name, sizeof(spirv->name));
pl_info(ctx, "Initializing SPIR-V compiler '%s'", impl->name);
if (impl->init(spirv))
return spirv;
talloc_free(spirv);
}
pl_fatal(ctx, "Failed initializing any SPIR-V compiler! Maybe "
"libplacebo was built without support for libshaderc?");
return NULL;
}
void spirv_compiler_destroy(struct spirv_compiler **spirv)
{
if (!*spirv)
return;
(*spirv)->impl->uninit(*spirv);
TA_FREEP(spirv);
}
/*
* 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 "common.h"
#include "context.h"
enum glsl_shader_stage {
GLSL_SHADER_VERTEX,
GLSL_SHADER_FRAGMENT,
GLSL_SHADER_COMPUTE,
};
#define SPIRV_NAME_MAX_LEN 32
struct spirv_compiler {
char name[SPIRV_NAME_MAX_LEN]; // for cache invalidation
struct pl_context *ctx;
const struct spirv_compiler_fns *impl;
// implementation-specific fields
void *priv;
struct ra_glsl_desc glsl; // supported GLSL capabilities
int compiler_version; // for cache invalidation, may be left as 0
};
struct spirv_compiler_fns {
const char *name;
// Compile GLSL to SPIR-V, under GL_KHR_vulkan_glsl semantics.
bool (*compile_glsl)(struct spirv_compiler *spirv, void *tactx,
enum glsl_shader_stage type, const char *glsl,
struct bstr *out_spirv);
// Only needs to initialize the implementation-specific fields
bool (*init)(struct spirv_compiler *spirv);
void (*uninit)(struct spirv_compiler *spirv);
};
// Initialize a SPIR-V compiler instance, or returns NULL on failure.
struct spirv_compiler *spirv_compiler_create(struct pl_context *ctx);
void spirv_compiler_destroy(struct spirv_compiler **spirv);
#include "spirv.h"
#include <shaderc/shaderc.h>
struct priv {
shaderc_compiler_t compiler;
shaderc_compile_options_t opts;
};
static void shaderc_uninit(struct spirv_compiler *spirv)
{
struct priv *p = spirv->priv;
shaderc_compile_options_release(p->opts);
shaderc_compiler_release(p->compiler);
TA_FREEP(&spirv->priv);
}
static bool shaderc_init(struct spirv_compiler *spirv)
{
struct priv *p = spirv->priv = talloc_zero(spirv, struct priv);
p->compiler = shaderc_compiler_initialize();
if (!p->compiler)
goto error;
p->opts = shaderc_compile_options_initialize();
if (!p->opts)
goto error;
shaderc_compile_options_set_optimization_level(p->opts,
shaderc_optimization_level_size);
int ver, rev;
shaderc_get_spv_version(&ver, &rev);
spirv->compiler_version = ver * 100 + rev;
spirv->glsl = (struct ra_glsl_desc) {
.version = 450, // this is impossible to query, so hard-code it
.vulkan = true,
};
return true;
error:
shaderc_uninit(spirv);
return false;
}
static shaderc_compilation_result_t compile(struct priv *p,
enum glsl_shader_stage type,
const char *glsl, bool debug)
{
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,
};
if (debug) {
return shaderc_compile_into_spv_assembly(p->compiler, glsl, strlen(glsl),
kinds[type], "input", "main", p->opts);
} else {
return shaderc_compile_into_spv(p->compiler, glsl, strlen(glsl),
kinds[type], "input", "main", p->opts);
}
}
static bool shaderc_compile(struct spirv_compiler *spirv, void *tactx,
enum glsl_shader_stage type, const char *glsl,
struct bstr *out_spirv)
{
struct priv *p = spirv->priv;
shaderc_compilation_result_t res = compile(p, type, glsl, false);
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;
const char *msg = shaderc_result_get_error_message(res);
if (msg[0])
PL_MSG(spirv, lev, "shaderc output:\n%s", msg);
int s = shaderc_result_get_compilation_status(res);
bool success = s == shaderc_compilation_status_success;
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)\n",
status, errs, warn);
if (success) {
void *bytes = (void *) shaderc_result_get_bytes(res);
out_spirv->len = shaderc_result_get_length(res);
out_spirv->start = talloc_memdup(tactx, bytes, out_spirv->len);
}
// Also print SPIR-V disassembly for debugging purposes. Unfortunately
// there doesn't seem to be a way to get this except compiling the shader
// a second time..
if (pl_msg_test(spirv->ctx, PL_LOG_TRACE)) {
shaderc_compilation_result_t dis = compile(p, type, glsl, true);
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);
return success;
}
const struct spirv_compiler_fns spirv_shaderc = {
.name = "shaderc",
.compile_glsl = shaderc_compile,
.init = shaderc_init,
.uninit = shaderc_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