summaryrefslogtreecommitdiffstats
path: root/video/out/gpu
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/gpu')
-rw-r--r--video/out/gpu/context.c18
-rw-r--r--video/out/gpu/context.h1
-rw-r--r--video/out/gpu/spirv.c78
-rw-r--r--video/out/gpu/spirv.h41
-rw-r--r--video/out/gpu/spirv_shaderc.c123
5 files changed, 256 insertions, 5 deletions
diff --git a/video/out/gpu/context.c b/video/out/gpu/context.c
index 25e2a754bf..69f322c422 100644
--- a/video/out/gpu/context.c
+++ b/video/out/gpu/context.c
@@ -31,6 +31,7 @@
#include "video/out/vo.h"
#include "context.h"
+#include "spirv.h"
extern const struct ra_ctx_fns ra_ctx_glx;
extern const struct ra_ctx_fns ra_ctx_glx_probe;
@@ -185,10 +186,17 @@ struct ra_ctx *ra_ctx_create(struct vo *vo, const char *context_type,
return NULL;
}
-void ra_ctx_destroy(struct ra_ctx **ctx)
+void ra_ctx_destroy(struct ra_ctx **ctx_ptr)
{
- if (*ctx)
- (*ctx)->fns->uninit(*ctx);
- talloc_free(*ctx);
- *ctx = NULL;
+ struct ra_ctx *ctx = *ctx_ptr;
+ if (!ctx)
+ return;
+
+ if (ctx->spirv && ctx->spirv->fns->uninit)
+ ctx->spirv->fns->uninit(ctx);
+
+ ctx->fns->uninit(ctx);
+ talloc_free(ctx);
+
+ *ctx_ptr = NULL;
}
diff --git a/video/out/gpu/context.h b/video/out/gpu/context.h
index f74087592d..78c0441cdf 100644
--- a/video/out/gpu/context.h
+++ b/video/out/gpu/context.h
@@ -22,6 +22,7 @@ struct ra_ctx {
struct ra_ctx_opts opts;
const struct ra_ctx_fns *fns;
struct ra_swapchain *swapchain;
+ struct spirv_compiler *spirv;
void *priv;
};
diff --git a/video/out/gpu/spirv.c b/video/out/gpu/spirv.c
new file mode 100644
index 0000000000..9375fd9508
--- /dev/null
+++ b/video/out/gpu/spirv.c
@@ -0,0 +1,78 @@
+#include "common/msg.h"
+#include "options/m_config.h"
+
+#include "spirv.h"
+#include "config.h"
+
+extern const struct spirv_compiler_fns spirv_shaderc;
+extern const struct spirv_compiler_fns spirv_nvidia_builtin;
+
+// in probe-order
+enum {
+ SPIRV_AUTO = 0,
+ SPIRV_SHADERC, // generally preferred, but not packaged everywhere
+ SPIRV_NVIDIA, // can be useful for testing, only available on nvidia
+};
+
+static const struct spirv_compiler_fns *compilers[] = {
+#if HAVE_SHADERC
+ [SPIRV_SHADERC] = &spirv_shaderc,
+#endif
+#if HAVE_VULKAN
+ [SPIRV_NVIDIA] = &spirv_nvidia_builtin,
+#endif
+};
+
+static const struct m_opt_choice_alternatives compiler_choices[] = {
+ {"auto", SPIRV_AUTO},
+#if HAVE_SHADERC
+ {"shaderc", SPIRV_SHADERC},
+#endif
+#if HAVE_VULKAN
+ {"nvidia", SPIRV_NVIDIA},
+#endif
+ {0}
+};
+
+struct spirv_opts {
+ int compiler;
+};
+
+#define OPT_BASE_STRUCT struct spirv_opts
+const struct m_sub_options spirv_conf = {
+ .opts = (const struct m_option[]) {
+ OPT_CHOICE_C("spirv-compiler", compiler, 0, compiler_choices),
+ {0}
+ },
+ .size = sizeof(struct spirv_opts),
+};
+
+bool spirv_compiler_init(struct ra_ctx *ctx)
+{
+ void *tmp = talloc_new(NULL);
+ struct spirv_opts *opts = mp_get_config_group(tmp, ctx->global, &spirv_conf);
+ int compiler = opts->compiler;
+ talloc_free(tmp);
+
+ for (int i = SPIRV_AUTO+1; i < MP_ARRAY_SIZE(compilers); i++) {
+ if (compiler != SPIRV_AUTO && i != compiler)
+ continue;
+ if (!compilers[i])
+ continue;
+
+ ctx->spirv = talloc_zero(NULL, struct spirv_compiler);
+ ctx->spirv->log = ctx->log,
+ ctx->spirv->fns = compilers[i];
+
+ const char *name = m_opt_choice_str(compiler_choices, i);
+ strncpy(ctx->spirv->name, name, sizeof(ctx->spirv->name));
+ MP_VERBOSE(ctx, "Initializing SPIR-V compiler '%s'\n", name);
+ if (ctx->spirv->fns->init(ctx))
+ return true;
+ talloc_free(ctx->spirv);
+ ctx->spirv = NULL;
+ }
+
+ MP_ERR(ctx, "Failed initializing SPIR-V compiler!\n");
+ return false;
+}
diff --git a/video/out/gpu/spirv.h b/video/out/gpu/spirv.h
new file mode 100644
index 0000000000..e3dbd4f52a
--- /dev/null
+++ b/video/out/gpu/spirv.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include "common/msg.h"
+#include "common/common.h"
+#include "context.h"
+
+enum glsl_shader {
+ GLSL_SHADER_VERTEX,
+ GLSL_SHADER_FRAGMENT,
+ GLSL_SHADER_COMPUTE,
+};
+
+#define SPIRV_NAME_MAX_LEN 32
+
+struct spirv_compiler {
+ char name[SPIRV_NAME_MAX_LEN];
+ const struct spirv_compiler_fns *fns;
+ struct mp_log *log;
+ void *priv;
+
+ const char *required_ext; // or NULL
+ int glsl_version; // GLSL version supported
+ int compiler_version; // for cache invalidation, may be left as 0
+ int ra_caps; // RA_CAP_* provided by this implementation, if any
+};
+
+struct spirv_compiler_fns {
+ // Compile GLSL to SPIR-V, under GL_KHR_vulkan_glsl semantics.
+ bool (*compile_glsl)(struct spirv_compiler *spirv, void *tactx,
+ enum glsl_shader type, const char *glsl,
+ struct bstr *out_spirv);
+
+ // Called by spirv_compiler_init / ra_ctx_destroy. These don't need to
+ // allocate/free ctx->spirv, that is done by the caller
+ bool (*init)(struct ra_ctx *ctx);
+ void (*uninit)(struct ra_ctx *ctx); // optional
+};
+
+// Initializes ctx->spirv to a valid SPIR-V compiler, or returns false on
+// failure. Cleanup will be handled by ra_ctx_destroy.
+bool spirv_compiler_init(struct ra_ctx *ctx);
diff --git a/video/out/gpu/spirv_shaderc.c b/video/out/gpu/spirv_shaderc.c
new file mode 100644
index 0000000000..9b429ca2c2
--- /dev/null
+++ b/video/out/gpu/spirv_shaderc.c
@@ -0,0 +1,123 @@
+#include "common/msg.h"
+
+#include "context.h"
+#include "spirv.h"
+
+#include <shaderc/shaderc.h>
+
+struct priv {
+ shaderc_compiler_t compiler;
+ shaderc_compile_options_t opts;
+};
+
+static void shaderc_uninit(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->spirv->priv;
+ if (!p)
+ return;
+
+ shaderc_compile_options_release(p->opts);
+ shaderc_compiler_release(p->compiler);
+}
+
+static bool shaderc_init(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->spirv->priv = talloc_zero(ctx->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);
+ ctx->spirv->compiler_version = ver * 100 + rev; // forwards compatibility
+ ctx->spirv->glsl_version = 450; // impossible to query?
+ return true;
+
+error:
+ shaderc_uninit(ctx);
+ return false;
+}
+
+static shaderc_compilation_result_t compile(struct priv *p,
+ enum glsl_shader 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 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),
+ msgl = errs ? MSGL_ERR : warn ? MSGL_WARN : MSGL_V;
+
+ const char *msg = shaderc_result_get_error_message(res);
+ if (msg[0])
+ MP_MSG(spirv, msgl, "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 < MP_ARRAY_SIZE(results) ? results[s] : "unknown";
+ MP_MSG(spirv, msgl, "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 (mp_msg_test(spirv->log, MSGL_TRACE)) {
+ shaderc_compilation_result_t dis = compile(p, type, glsl, true);
+ MP_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 = {
+ .compile_glsl = shaderc_compile,
+ .init = shaderc_init,
+ .uninit = shaderc_uninit,
+};