├── docs ├── CNAME ├── style.css └── index.md ├── tools ├── meson.build └── glsl_preproc │ ├── templates │ ├── struct.c.j2 │ ├── glsl_block.c.j2 │ ├── function.c.j2 │ └── call.c.j2 │ ├── meson.build │ ├── templates.py │ ├── main.py │ ├── variables.py │ └── macros.py ├── src ├── version.h.in ├── tests │ ├── include │ │ ├── include_tmpl.c │ │ ├── include_tmpl.cpp │ │ └── meson.build │ ├── fuzz │ │ ├── lut.c │ │ ├── user_shaders.c │ │ └── options.c │ ├── gpu_tests.h │ ├── dither.c │ ├── meson.build │ ├── d3d11.c │ ├── dav1d.c │ ├── dummy.c │ ├── lut.c │ ├── filters.c │ ├── common.c │ └── options.c ├── opengl │ ├── loader_egl.c │ ├── loader_gl.c │ ├── include │ │ └── glad │ │ │ └── meson.build │ ├── formats.h │ ├── common.h │ ├── utils.h │ ├── stubs.c │ ├── meson.build │ ├── gpu.h │ └── utils.c ├── include │ └── libplacebo │ │ ├── meson.build │ │ ├── utils │ │ └── dolbyvision.h │ │ ├── config.h.in │ │ ├── shaders │ │ └── lut.h │ │ ├── dither.h │ │ └── log.h ├── shaders │ ├── meson.build │ ├── film_grain.c │ ├── film_grain.h │ └── custom.c ├── os.h ├── version.py ├── d3d11 │ ├── formats.h │ ├── meson.build │ ├── stubs.c │ ├── common.h │ └── utils.h ├── dispatch.h ├── swapchain.h ├── pl_assert.h ├── vulkan │ ├── formats.h │ ├── meson.build │ ├── malloc.h │ ├── stubs.c │ └── utils_gen.c.j2 ├── glsl │ ├── utils.h │ ├── glslang.h │ ├── spirv.h │ ├── spirv.c │ ├── meson.build │ ├── glslang.cc │ ├── spirv_glslang.c │ └── glslang_resources.c ├── colorspace.h ├── pl_thread.h ├── utils │ └── dolbyvision.c ├── swapchain.c ├── cache.h ├── pl_clock.h ├── filters.h ├── log.h ├── ucrt_math.def ├── pl_thread_pthread.h └── pl_thread_win32.h ├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── compile ├── demos ├── screenshots │ ├── plplay1.png │ ├── plplay2.png │ ├── plplay3.png │ ├── plplay4.png │ ├── plplay5.png │ └── plplay6.png ├── utils.h ├── common.h ├── utils.c ├── ui.h ├── window.h ├── colors.c ├── window.c ├── plplay.h └── meson.build ├── gcovr.cfg ├── .gitignore ├── win32 ├── meson.build ├── demos.manifest ├── libplacebo.manifest ├── demos.rc.in └── libplacebo.rc.in ├── .gitmodules ├── RELEASING.md ├── mkdocs.yml └── meson_options.txt /docs/CNAME: -------------------------------------------------------------------------------- 1 | libplacebo.org 2 | -------------------------------------------------------------------------------- /tools/meson.build: -------------------------------------------------------------------------------- 1 | subdir('glsl_preproc') 2 | -------------------------------------------------------------------------------- /src/version.h.in: -------------------------------------------------------------------------------- 1 | #define BUILD_VERSION "@buildver@" 2 | -------------------------------------------------------------------------------- /docs/style.css: -------------------------------------------------------------------------------- 1 | .md-typeset p { 2 | margin: 1em 1em; 3 | } 4 | -------------------------------------------------------------------------------- /src/tests/include/include_tmpl.c: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: haasn 2 | patreon: haasn 3 | open_collective: haasn 4 | -------------------------------------------------------------------------------- /src/opengl/loader_egl.c: -------------------------------------------------------------------------------- 1 | #define GLAD_EGL_IMPLEMENTATION 2 | #include "common.h" 3 | -------------------------------------------------------------------------------- /src/opengl/loader_gl.c: -------------------------------------------------------------------------------- 1 | #define GLAD_GL_IMPLEMENTATION 2 | #include "common.h" 3 | -------------------------------------------------------------------------------- /compile: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | DIR=./build 3 | [ -d $DIR ] || meson $DIR 4 | ninja -C$DIR 5 | -------------------------------------------------------------------------------- /demos/screenshots/plplay1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haasn/libplacebo/HEAD/demos/screenshots/plplay1.png -------------------------------------------------------------------------------- /demos/screenshots/plplay2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haasn/libplacebo/HEAD/demos/screenshots/plplay2.png -------------------------------------------------------------------------------- /demos/screenshots/plplay3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haasn/libplacebo/HEAD/demos/screenshots/plplay3.png -------------------------------------------------------------------------------- /demos/screenshots/plplay4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haasn/libplacebo/HEAD/demos/screenshots/plplay4.png -------------------------------------------------------------------------------- /demos/screenshots/plplay5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haasn/libplacebo/HEAD/demos/screenshots/plplay5.png -------------------------------------------------------------------------------- /demos/screenshots/plplay6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haasn/libplacebo/HEAD/demos/screenshots/plplay6.png -------------------------------------------------------------------------------- /gcovr.cfg: -------------------------------------------------------------------------------- 1 | exclude = .*/tests/.* 2 | exclude = .*/demos/.* 3 | exclude = .*_gen\.c$ 4 | sort-uncovered = yes 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build* 2 | /tags 3 | /TAGS 4 | /demos/3rdparty 5 | /3rdparty 6 | *.exe 7 | *.o 8 | .cache 9 | __pycache__ 10 | -------------------------------------------------------------------------------- /demos/utils.h: -------------------------------------------------------------------------------- 1 | // License: CC0 / Public Domain 2 | #pragma once 3 | #include "common.h" 4 | 5 | const char *get_cache_dir(char (*buf)[512]); 6 | -------------------------------------------------------------------------------- /src/tests/include/include_tmpl.cpp: -------------------------------------------------------------------------------- 1 | #define PL_LIBAV_IMPLEMENTATION 0 2 | #define PL_DAV1D_IMPLEMENTATION 0 3 | #include 4 | -------------------------------------------------------------------------------- /tools/glsl_preproc/templates/struct.c.j2: -------------------------------------------------------------------------------- 1 | struct __attribute__((__packed__)) { 2 | {% for var in macro.vars %} 3 | {{ var.ctype }} {{ var.name }}; 4 | {% endfor %} 5 | } 6 | -------------------------------------------------------------------------------- /src/include/libplacebo/meson.build: -------------------------------------------------------------------------------- 1 | sources += configure_file( 2 | input: 'config.h.in', 3 | output: 'config.h', 4 | install_dir: get_option('includedir') / meson.project_name(), 5 | configuration: conf_public, 6 | ) 7 | -------------------------------------------------------------------------------- /demos/common.h: -------------------------------------------------------------------------------- 1 | // License: CC0 / Public Domain 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include "config_demos.h" 12 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - pages-test 7 | permissions: 8 | contents: write 9 | jobs: 10 | deploy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions/setup-python@v4 15 | with: 16 | python-version: 3.x 17 | - run: pip install mkdocs-material 18 | - run: mkdocs gh-deploy --force 19 | -------------------------------------------------------------------------------- /tools/glsl_preproc/meson.build: -------------------------------------------------------------------------------- 1 | strip_arg = get_option('debug') ? [] : [ '--strip' ] 2 | glsl_preproc = [ python, join_paths(meson.current_source_dir(), 'main.py') ] + \ 3 | strip_arg + [ '@INPUT@', '@OUTPUT@' ] 4 | glsl_deps = files( 5 | 'macros.py', 6 | 'statement.py', 7 | 'templates.py', 8 | 'templates/call.c.j2', 9 | 'templates/function.c.j2', 10 | 'templates/glsl_block.c.j2', 11 | 'templates/struct.c.j2', 12 | 'variables.py', 13 | ) 14 | -------------------------------------------------------------------------------- /src/shaders/meson.build: -------------------------------------------------------------------------------- 1 | shader_sources = [ 2 | 'colorspace.c', 3 | 'custom.c', 4 | 'custom_mpv.c', 5 | 'deinterlacing.c', 6 | 'dithering.c', 7 | 'film_grain.c', 8 | 'film_grain_av1.c', 9 | 'film_grain_h274.c', 10 | 'icc.c', 11 | 'lut.c', 12 | 'sampling.c', 13 | ] 14 | 15 | foreach s : shader_sources 16 | sources += custom_target(s, 17 | command: glsl_preproc, 18 | depend_files: glsl_deps, 19 | env: python_env, 20 | input: s, 21 | output: s, 22 | ) 23 | endforeach 24 | -------------------------------------------------------------------------------- /tools/glsl_preproc/templates/glsl_block.c.j2: -------------------------------------------------------------------------------- 1 | #line {{ block.linenr }} 2 | {% if block.refs %} 3 | pl_str_append_asprintf_c(alloc, buf, 4 | {% for line in block.lines %} 5 | {{ line.fmtstr }}{{ ',' if loop.last }} 6 | {% endfor %} 7 | {% for ref in block.refs %} 8 | {{ ref }}{{ ',' if not loop.last }} 9 | {% endfor %} 10 | ); 11 | {% else %} 12 | pl_str_append(alloc, buf, pl_str0( 13 | {% for line in block.lines %} 14 | {{ line.rawstr }} 15 | {% endfor %} 16 | )); 17 | {% endif %} 18 | -------------------------------------------------------------------------------- /src/tests/fuzz/lut.c: -------------------------------------------------------------------------------- 1 | #include "../utils.h" 2 | 3 | #include 4 | 5 | __AFL_FUZZ_INIT(); 6 | 7 | #pragma clang optimize off 8 | 9 | int main() 10 | { 11 | struct pl_custom_lut *lut; 12 | 13 | #ifdef __AFL_HAVE_MANUAL_CONTROL 14 | __AFL_INIT(); 15 | #endif 16 | 17 | unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; 18 | 19 | while (__AFL_LOOP(100000)) { 20 | size_t len = __AFL_FUZZ_TESTCASE_LEN; 21 | lut = pl_lut_parse_cube(NULL, (char *) buf, len); 22 | pl_lut_free(&lut); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tools/glsl_preproc/templates.py: -------------------------------------------------------------------------------- 1 | import jinja2 2 | import os.path 3 | 4 | TEMPLATEDIR = os.path.dirname(__file__) + '/templates' 5 | TEMPLATES = jinja2.Environment( 6 | loader = jinja2.FileSystemLoader(searchpath=TEMPLATEDIR), 7 | lstrip_blocks = True, 8 | trim_blocks = True, 9 | ) 10 | 11 | GLSL_BLOCK_TEMPLATE = TEMPLATES.get_template('glsl_block.c.j2') 12 | FUNCTION_TEMPLATE = TEMPLATES.get_template('function.c.j2') 13 | CALL_TEMPLATE = TEMPLATES.get_template('call.c.j2') 14 | STRUCT_TEMPLATE = TEMPLATES.get_template('struct.c.j2') 15 | -------------------------------------------------------------------------------- /tools/glsl_preproc/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import argparse 5 | 6 | from macros import Macro 7 | 8 | parser = argparse.ArgumentParser() 9 | parser.add_argument('input') 10 | parser.add_argument('output') 11 | parser.add_argument('-s', '--strip', default=False, action='store_true') 12 | args = parser.parse_args() 13 | 14 | with open(args.input, encoding='utf-8') as infile: 15 | with open(args.output, 'w', encoding='utf-8') as outfile: 16 | for line in Macro.process_file(infile, strip=args.strip): 17 | outfile.write(line) 18 | -------------------------------------------------------------------------------- /win32/meson.build: -------------------------------------------------------------------------------- 1 | version_arr = meson.project_version().split('.') 2 | version_config = configuration_data() 3 | version_config.set('PL_MAJOR', version_arr[0]) 4 | version_config.set('PL_MINOR', version_arr[1]) 5 | version_config.set('PL_PATCH', version_arr[2]) 6 | 7 | libplacebo_rc = configure_file(input: 'libplacebo.rc.in', 8 | output: 'libplacebo.rc', 9 | configuration: version_config) 10 | demos_rc = configure_file(input: 'demos.rc.in', 11 | output: 'demos.rc', 12 | configuration: version_config) 13 | -------------------------------------------------------------------------------- /tools/glsl_preproc/templates/function.c.j2: -------------------------------------------------------------------------------- 1 | 2 | #line {{ macro.linenr - 1 }} 3 | size_t {{ macro.name }}_fn(void *alloc, pl_str *buf, const uint8_t *ptr); 4 | size_t {{ macro.name }}_fn(void *alloc, pl_str *buf, const uint8_t *ptr) 5 | { 6 | {% if macro.vars %} 7 | {{ macro.render_struct() }} {{ Var.STRUCT_NAME }}; 8 | memcpy(&{{ Var.STRUCT_NAME }}, ptr, sizeof({{ Var.STRUCT_NAME }})); 9 | {% endif %} 10 | 11 | {% for statement in macro.body %} 12 | {{ statement.render() }} 13 | {% endfor %} 14 | 15 | {% if macro.vars %} 16 | return sizeof({{ Var.STRUCT_NAME }}); 17 | {% else %} 18 | return 0; 19 | {% endif %} 20 | } 21 | -------------------------------------------------------------------------------- /tools/glsl_preproc/templates/call.c.j2: -------------------------------------------------------------------------------- 1 | { 2 | {% if macro.vars %} 3 | const {{ macro.render_struct() }} {{ macro.name }}_args = { 4 | {% for var in macro.vars %} 5 | #line {{ var.linenr }} 6 | .{{ var.name }} = {{ var.expr }}, 7 | {% endfor %} 8 | }; 9 | #line {{ macro.linenr }} 10 | {% endif %} 11 | size_t {{ macro.name }}_fn(void *, pl_str *, const uint8_t *); 12 | {% if macro.vars %} 13 | pl_str_builder_append(sh->buffers[{{ macro.buf }}], {{ macro.name }}_fn, 14 | &{{ macro.name }}_args, sizeof({{ macro.name }}_args)); 15 | {% else %} 16 | pl_str_builder_append(sh->buffers[{{ macro.buf }}], {{ macro.name }}_fn, NULL, 0); 17 | {% endif %} 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/tests/fuzz/user_shaders.c: -------------------------------------------------------------------------------- 1 | #include "../utils.h" 2 | 3 | #include 4 | #include 5 | 6 | __AFL_FUZZ_INIT(); 7 | 8 | #pragma clang optimize off 9 | 10 | int main() 11 | { 12 | pl_gpu gpu = pl_gpu_dummy_create(NULL, NULL); 13 | const struct pl_hook *hook; 14 | 15 | #ifdef __AFL_HAVE_MANUAL_CONTROL 16 | __AFL_INIT(); 17 | #endif 18 | 19 | unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; 20 | 21 | while (__AFL_LOOP(100000)) { 22 | size_t len = __AFL_FUZZ_TESTCASE_LEN; 23 | hook = pl_mpv_user_shader_parse(gpu, (char *) buf, len); 24 | pl_mpv_user_shader_destroy(&hook); 25 | } 26 | 27 | pl_gpu_dummy_destroy(&gpu); 28 | } 29 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "demos/3rdparty/nuklear"] 2 | path = demos/3rdparty/nuklear 3 | url = https://github.com/Immediate-Mode-UI/Nuklear.git 4 | [submodule "3rdparty/glad"] 5 | path = 3rdparty/glad 6 | url = https://github.com/Dav1dde/glad 7 | [submodule "3rdparty/jinja"] 8 | path = 3rdparty/jinja 9 | url = https://github.com/pallets/jinja 10 | [submodule "3rdparty/markupsafe"] 11 | path = 3rdparty/markupsafe 12 | url = https://github.com/pallets/markupsafe 13 | [submodule "3rdparty/Vulkan-Headers"] 14 | path = 3rdparty/Vulkan-Headers 15 | url = https://github.com/KhronosGroup/Vulkan-Headers 16 | [submodule "3rdparty/fast_float"] 17 | path = 3rdparty/fast_float 18 | url = https://github.com/fastfloat/fast_float.git 19 | -------------------------------------------------------------------------------- /src/tests/fuzz/options.c: -------------------------------------------------------------------------------- 1 | #include "../utils.h" 2 | 3 | #include 4 | 5 | __AFL_FUZZ_INIT(); 6 | 7 | #pragma clang optimize off 8 | 9 | int main() 10 | { 11 | pl_options opts = pl_options_alloc(NULL); 12 | 13 | #ifdef __AFL_HAVE_MANUAL_CONTROL 14 | __AFL_INIT(); 15 | #endif 16 | 17 | unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; 18 | 19 | while (__AFL_LOOP(100000)) { 20 | size_t len = __AFL_FUZZ_TESTCASE_LEN; 21 | if (!len) 22 | continue; 23 | 24 | buf[len - 1] = '\0'; // ensure proper null termination 25 | pl_options_load(opts, (const char *) buf); 26 | pl_options_save(opts); 27 | pl_options_reset(opts, NULL); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | # New release steps 2 | 3 | ## Pre-release (vX.Y.0-rcN) 4 | 5 | 1. Tag `vX.Y.0-rcN` on `master` 6 | 7 | ## Normal release (vX.Y.0) 8 | 9 | 1. Tag `vX.Y.0` on `master` 10 | 2. Create version branch `vX.Y` 11 | 3. Force-push `release` branch (or fast-forward if possible) 12 | 4. Update topic on IRC #libplacebo 13 | 5. Bump 'X' version number in meson.build, for next release (optional) 14 | 6. Tag release on github 15 | 16 | ## Bugfix release (vX.Y.Z) 17 | 18 | 1. Cherry-pick bug fixes onto version branch (`vX.Y`) 19 | 2. Update `Z` version number in `meson.build` 20 | 3. Tag `vX.Y.Z` on this branch 21 | 4. Fast-forward `release` branch iff this is the latest major release 22 | 5. Update topic on IRC #libplacebo 23 | 6. Tag release on github 24 | -------------------------------------------------------------------------------- /win32/demos.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | libplacebo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/os.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #ifdef __unix__ 21 | #define PL_HAVE_UNIX 22 | #endif 23 | 24 | #ifdef _WIN32 25 | #define PL_HAVE_WIN32 26 | #endif 27 | 28 | #ifdef __APPLE__ 29 | #define PL_HAVE_APPLE 30 | #endif 31 | -------------------------------------------------------------------------------- /src/tests/include/meson.build: -------------------------------------------------------------------------------- 1 | include_tmpl_langs = ['c', 'cpp'] 2 | 3 | # Ensure all headers compile 4 | 5 | test_include_sources = [] 6 | foreach h : headers 7 | 8 | if (h.contains('internal') or 9 | h.contains('dav1d') and not dav1d.found() or 10 | h.contains('libav') and not libav_found or 11 | h.contains('d3d11') and not d3d11_header) 12 | continue 13 | endif 14 | 15 | foreach lang : include_tmpl_langs 16 | 17 | test_include_sources += configure_file( 18 | input: 'include_tmpl.' + lang, 19 | output: 'include_@0@.@1@'.format(h.underscorify(), lang), 20 | configuration: { 21 | 'header': h 22 | }, 23 | ) 24 | 25 | endforeach 26 | 27 | endforeach 28 | 29 | static_library('test_include', test_include_sources, 30 | dependencies: [tdep_static, lavu, lavc, lavf], 31 | include_directories: [inc], 32 | implicit_include_directories: false, 33 | c_args: ['-Wall', '-Wextra', '-Wpedantic'], 34 | cpp_args: ['-Wall', '-Wextra', '-Wpedantic'], 35 | ) 36 | -------------------------------------------------------------------------------- /src/tests/gpu_tests.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include "utils.h" 21 | 22 | #include 23 | 24 | void pl_buffer_tests(pl_gpu gpu); 25 | void pl_texture_tests(pl_gpu gpu); 26 | 27 | void gpu_shader_tests(pl_gpu gpu); 28 | void gpu_interop_tests(pl_gpu gpu); 29 | -------------------------------------------------------------------------------- /src/opengl/include/glad/meson.build: -------------------------------------------------------------------------------- 1 | glad_check = run_command([ python, '-c', 'import glad; print(glad.__version__)' ], 2 | env: python_env, 3 | capture: true, 4 | check: false, 5 | ) 6 | 7 | glad_ver = glad_check.returncode() == 0 ? glad_check.stdout().strip() : 'none' 8 | glad_req = '>= 2.0' 9 | 10 | if not glad_ver.version_compare(glad_req) 11 | error(f'glad (required: @glad_req@, found: @glad_ver@) was not found in ' + 12 | 'PYTHONPATH or `3rdparty`. Please run `git submodule update --init` ' + 13 | 'followed by `meson --wipe`.') 14 | endif 15 | 16 | glad = custom_target('gl.h', 17 | output: 'gl.h', 18 | env: python_env, 19 | command: [ 20 | python, '-m', 'glad', '--out-path=@OUTDIR@/../../', 21 | '--reproducible', '--merge', '--api=gl:core,gles2,egl', 22 | '--extensions=' + ','.join(gl_extensions), 'c', '--header-only', '--mx' 23 | ] + (opengl_link.allowed() ? ['--loader'] : []) 24 | ) 25 | 26 | glad_dep = declare_dependency( 27 | include_directories: include_directories('..'), 28 | sources: glad, 29 | ) 30 | -------------------------------------------------------------------------------- /src/version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import subprocess 5 | 6 | infilename, outfilename, source_dir, project_version_pretty = sys.argv[1:] 7 | 8 | try: 9 | proc = subprocess.run(['git', 'describe', '--dirty'], cwd=source_dir, 10 | capture_output=True, text=True) 11 | proc.check_returncode() 12 | except (FileNotFoundError, subprocess.CalledProcessError): 13 | # No git or no repo. Hopefully a release tarball. 14 | version = project_version_pretty 15 | else: 16 | version = '{} ({})'.format(project_version_pretty, proc.stdout.strip()) 17 | 18 | with open(infilename, 'r') as infile: 19 | output = infile.read().replace('@buildver@', version) 20 | # Avoid touching file (triggering recompilation) if it's already up to date. 21 | try: 22 | with open(outfilename, 'r') as outfile: 23 | write_output = outfile.read() != output 24 | except FileNotFoundError: 25 | write_output = True 26 | if write_output: 27 | with open(outfilename, 'w') as outfile: 28 | outfile.write(output) 29 | -------------------------------------------------------------------------------- /win32/libplacebo.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | libplacebo 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: libplacebo 2 | site_url: https://libplacebo.org/ 3 | repo_url: https://code.videolan.org/videolan/libplacebo 4 | repo_name: videolan/libplacebo 5 | copyright: Copyright © 2017-2022 Niklas Haas 6 | 7 | theme: 8 | name: material 9 | palette: 10 | - scheme: slate 11 | primary: deep purple 12 | accent: deep purple 13 | toggle: 14 | icon: material/brightness-4 15 | name: Switch to light mode 16 | - scheme: default 17 | primary: purple 18 | accent: purple 19 | toggle: 20 | icon: material/brightness-7 21 | name: Switch to dark mode 22 | icon: 23 | repo: fontawesome/brands/gitlab 24 | features: 25 | - content.code.annotate 26 | 27 | extra_css: 28 | - style.css 29 | 30 | markdown_extensions: 31 | - admonition 32 | - footnotes 33 | - pymdownx.highlight: 34 | anchor_linenums: true 35 | - pymdownx.details 36 | - pymdownx.snippets 37 | - pymdownx.superfences 38 | - toc: 39 | toc_depth: 3 40 | 41 | nav: 42 | - 'Using': 43 | - index.md 44 | - basic-rendering.md 45 | - renderer.md 46 | - custom-shaders.md 47 | - options.md 48 | - 'Developing': 49 | - glsl.md 50 | -------------------------------------------------------------------------------- /src/d3d11/formats.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include "common.h" 21 | 22 | struct d3d_format { 23 | DXGI_FORMAT dxfmt; 24 | int minor; // The D3D11 minor version number which supports this format 25 | struct pl_fmt_t fmt; 26 | }; 27 | 28 | extern const struct d3d_format pl_d3d11_formats[]; 29 | 30 | static inline DXGI_FORMAT fmt_to_dxgi(pl_fmt fmt) 31 | { 32 | const struct d3d_format **fmtp = PL_PRIV(fmt); 33 | return (*fmtp)->dxfmt; 34 | } 35 | 36 | void pl_d3d11_setup_formats(struct pl_gpu_t *gpu); 37 | -------------------------------------------------------------------------------- /src/tests/dither.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | 6 | #define SHIFT 4 7 | #define SIZE (1 << SHIFT) 8 | float data[SIZE][SIZE]; 9 | 10 | int main() 11 | { 12 | printf("Ordered dither matrix:\n"); 13 | pl_generate_bayer_matrix(&data[0][0], SIZE); 14 | for (int y = 0; y < SIZE; y++) { 15 | for (int x = 0; x < SIZE; x++) 16 | printf(" %3d,", (int)(data[y][x] * SIZE * SIZE)); 17 | printf("\n"); 18 | } 19 | 20 | printf("Blue noise dither matrix:\n"); 21 | pl_generate_blue_noise(&data[0][0], SIZE); 22 | for (int y = 0; y < SIZE; y++) { 23 | for (int x = 0; x < SIZE; x++) 24 | printf(" %3d,", (int)(data[y][x] * SIZE * SIZE)); 25 | printf("\n"); 26 | } 27 | 28 | // Generate an example of a dither shader 29 | pl_log log = pl_test_logger(); 30 | pl_shader sh = pl_shader_alloc(log, NULL); 31 | pl_shader_obj obj = NULL; 32 | 33 | pl_shader_dither(sh, 8, &obj, NULL); 34 | const struct pl_shader_res *res = pl_shader_finalize(sh); 35 | REQUIRE(res); 36 | printf("Generated dither shader:\n%s\n", res->glsl); 37 | 38 | pl_shader_obj_destroy(&obj); 39 | pl_shader_free(&sh); 40 | pl_log_destroy(&log); 41 | } 42 | -------------------------------------------------------------------------------- /demos/utils.c: -------------------------------------------------------------------------------- 1 | // License: CC0 / Public Domain 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "utils.h" 8 | #include "../src/os.h" 9 | 10 | #ifdef PL_HAVE_WIN32 11 | #include 12 | #else 13 | #include 14 | #include 15 | #include 16 | #include 17 | #endif 18 | 19 | const char *get_cache_dir(char (*buf)[512]) 20 | { 21 | // Check if XDG_CACHE_HOME is set for Linux/BSD 22 | const char* xdg_cache_home = getenv("XDG_CACHE_HOME"); 23 | if (xdg_cache_home) 24 | return xdg_cache_home; 25 | 26 | #ifdef _WIN32 27 | const char* local_app_data = getenv("LOCALAPPDATA"); 28 | if (local_app_data) 29 | return local_app_data; 30 | #endif 31 | 32 | #ifdef __APPLE__ 33 | struct passwd* pw = getpwuid(getuid()); 34 | if (pw) { 35 | int ret = snprintf(*buf, sizeof(*buf), "%s/%s", pw->pw_dir, "Library/Caches"); 36 | if (ret > 0 && ret < sizeof(*buf)) 37 | return *buf; 38 | } 39 | #endif 40 | 41 | const char* home = getenv("HOME"); 42 | if (home) { 43 | int ret = snprintf(*buf, sizeof(*buf), "%s/.cache", home); 44 | if (ret > 0 && ret < sizeof(*buf)) 45 | return *buf; 46 | } 47 | 48 | return NULL; 49 | } 50 | -------------------------------------------------------------------------------- /src/opengl/formats.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include "common.h" 21 | 22 | struct gl_format { 23 | GLint ifmt; // sized internal format (e.g. GL_RGBA16F) 24 | GLenum fmt; // base internal format (e.g. GL_RGBA) 25 | GLenum type; // host-visible type (e.g. GL_FLOAT) 26 | struct pl_fmt_t tmpl; // pl_fmt template 27 | }; 28 | 29 | typedef void (gl_format_cb)(pl_gpu gpu, const struct gl_format *glfmt); 30 | 31 | // Add all supported formats to the `pl_gpu` format list. 32 | bool gl_setup_formats(struct pl_gpu_t *gpu); 33 | -------------------------------------------------------------------------------- /src/include/libplacebo/utils/dolbyvision.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #ifndef LIBPLACEBO_DOLBYVISION_H_ 19 | #define LIBPLACEBO_DOLBYVISION_H_ 20 | 21 | #include 22 | 23 | PL_API_BEGIN 24 | 25 | // Parses the Dolby Vision RPU, and sets the `pl_hdr_metadata` dynamic 26 | // brightness metadata fields accordingly. 27 | // 28 | // Note: requires `PL_HAVE_LIBDOVI` to be defined, no-op otherwise. 29 | PL_API void pl_hdr_metadata_from_dovi_rpu(struct pl_hdr_metadata *out, 30 | const uint8_t *buf, size_t size); 31 | 32 | PL_API_END 33 | 34 | #endif // LIBPLACEBO_DOLBYVISION_H_ 35 | -------------------------------------------------------------------------------- /src/dispatch.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include "common.h" 21 | 22 | // Like `pl_dispatch_begin`, but has an extra `unique` parameter. If this is 23 | // true, the generated shader will be uniquely namespaced `unique` and may be 24 | // freely merged with other shaders (`sh_subpass`). Otherwise, all shaders have 25 | // the same namespace and merging them is an error. 26 | pl_shader pl_dispatch_begin_ex(pl_dispatch dp, bool unique); 27 | 28 | // Set the `dynamic_constants` field for newly created `pl_shader` objects. 29 | // 30 | // This is a private API because it's sort of clunky/stateful. 31 | void pl_dispatch_mark_dynamic(pl_dispatch dp, bool dynamic); 32 | -------------------------------------------------------------------------------- /src/d3d11/meson.build: -------------------------------------------------------------------------------- 1 | d3d11 = get_option('d3d11') 2 | d3d11_header = cc.check_header('d3d11.h', required: false) # needed publicly 3 | d3d11_headers_extra = [ # needed internally 4 | cc.check_header('d3d11_4.h', required: d3d11), 5 | cc.check_header('dxgi1_6.h', required: d3d11), 6 | ] 7 | d3d11_deps = [ 8 | dependency('spirv-cross-c-shared', version: '>=0.29.0', required: d3d11), 9 | cc.find_library('version', required: d3d11), 10 | ] 11 | 12 | d3d11 = d3d11.require(d3d11_header) 13 | foreach h : d3d11_headers_extra 14 | d3d11 = d3d11.require(h) 15 | endforeach 16 | foreach d : d3d11_deps 17 | d3d11 = d3d11.require(d.found()) 18 | endforeach 19 | 20 | components.set('d3d11', d3d11.allowed()) 21 | if d3d11.allowed() 22 | conf_internal.set('PL_HAVE_DXGI_DEBUG', 23 | cc.has_header_symbol('dxgidebug.h', 'IID_IDXGIInfoQueue')) 24 | conf_internal.set('PL_HAVE_DXGI_DEBUG_D3D11', 25 | cc.has_header_symbol('d3d11sdklayers.h', 'DXGI_DEBUG_D3D11')) 26 | add_project_arguments(['-DCOBJMACROS'], language: ['c', 'cpp']) 27 | build_deps += declare_dependency(dependencies: d3d11_deps) 28 | tests += 'd3d11.c' 29 | sources += [ 30 | 'd3d11/context.c', 31 | 'd3d11/formats.c', 32 | 'd3d11/gpu.c', 33 | 'd3d11/gpu_buf.c', 34 | 'd3d11/gpu_tex.c', 35 | 'd3d11/gpu_pass.c', 36 | 'd3d11/swapchain.c', 37 | 'd3d11/utils.c', 38 | ] 39 | elif d3d11_header 40 | sources += 'd3d11/stubs.c' 41 | endif 42 | -------------------------------------------------------------------------------- /src/swapchain.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include "common.h" 21 | 22 | #include 23 | 24 | // This struct must be the first member of the swapchains's priv struct. The 25 | // `pl_swapchain` helpers will cast the priv struct to this struct! 26 | 27 | #define SW_PFN(name) __typeof__(pl_swapchain_##name) *name 28 | struct pl_sw_fns { 29 | // This destructor follows the same rules as `pl_gpu_fns` 30 | void (*destroy)(pl_swapchain sw); 31 | 32 | SW_PFN(latency); // optional 33 | SW_PFN(resize); // optional 34 | SW_PFN(colorspace_hint); // optional 35 | SW_PFN(start_frame); 36 | SW_PFN(submit_frame); 37 | SW_PFN(swap_buffers); 38 | }; 39 | #undef SW_PFN 40 | -------------------------------------------------------------------------------- /src/pl_assert.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | 23 | #ifndef NDEBUG 24 | # define pl_assert assert 25 | #else 26 | # define pl_assert(expr) \ 27 | do { \ 28 | if (!(expr)) { \ 29 | fprintf(stderr, "Assertion failed: %s in %s:%d\n", \ 30 | #expr, __FILE__, __LINE__); \ 31 | abort(); \ 32 | } \ 33 | } while (0) 34 | #endif 35 | 36 | // In C11, static asserts must have a string message 37 | #define pl_static_assert(expr) static_assert(expr, #expr) 38 | -------------------------------------------------------------------------------- /src/tests/meson.build: -------------------------------------------------------------------------------- 1 | gpu_tests = [ 2 | 'vulkan.c', 'opengl_surfaceless.c', 'd3d11.c', 'dummy.c' 3 | ] 4 | 5 | ts = [] 6 | 7 | foreach t : tests 8 | deps = [tdep_static] 9 | if t == 'opengl_surfaceless.c' 10 | deps += glad_dep 11 | endif 12 | if t == 'vulkan.c' 13 | deps += vulkan_headers 14 | endif 15 | sources = [] 16 | if gpu_tests.contains(t) 17 | sources += 'gpu_tests.c' 18 | endif 19 | # TODO: Define objects in tdep_static once Meson 1.1.0 is ok to use 20 | ts += { 'test': t, 21 | 'sources': sources, 22 | 'deps': deps, 23 | 'objects': lib.extract_all_objects(recursive: false) } 24 | endforeach 25 | 26 | dav1d = dependency('dav1d', required: false) 27 | if dav1d.found() 28 | ts += { 'test': 'dav1d.c', 'deps': [dav1d, tdep_shared] } 29 | endif 30 | 31 | lavu = dependency('libavutil', version: '>=55.74.100', required: false) 32 | lavc = dependency('libavcodec', required: false) 33 | lavf = dependency('libavformat', required: false) 34 | libav_found = lavu.found() and lavc.found() and lavf.found() 35 | if libav_found 36 | ts += { 'test': 'libav.c', 'deps': [lavu, lavc, lavf, tdep_shared] } 37 | endif 38 | 39 | foreach t : ts 40 | e = executable('test.' + t['test'], [t['test']] + t.get('sources', []), 41 | objects: t.get('objects', []), 42 | c_args: [ '-Wno-unused-function' ], 43 | dependencies: t.get('deps', []), 44 | link_args: link_args, 45 | link_depends: link_depends, 46 | ) 47 | 48 | test(t['test'], e, timeout: 120) 49 | endforeach 50 | 51 | subdir('include') 52 | -------------------------------------------------------------------------------- /src/vulkan/formats.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include "common.h" 21 | #include "gpu.h" 22 | 23 | struct vk_format { 24 | VkFormat tfmt; // internal vulkan format enum (textures) 25 | struct pl_fmt_t fmt;// pl_fmt template (features will be auto-detected) 26 | int icomps; // internal component count (or 0 to infer from `fmt`) 27 | VkFormat bfmt; // vulkan format for use as buffers (or 0 to use `tfmt`) 28 | const struct vk_format *emufmt; // alternate format for emulation 29 | uint32_t min_ver; // minimum vulkan API version for this format to exist 30 | struct { VkFormat fmt; int sx, sy; } pfmt[4]; // plane formats (for planar textures) 31 | }; 32 | 33 | // Add all supported formats to the `pl_gpu` format list 34 | void vk_setup_formats(struct pl_gpu_t *gpu); 35 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | ## Overview 4 | 5 | This document will serve as an introduction to and usage example for the 6 | [libplacebo](https://code.videolan.org/videolan/libplacebo) API. This is not 7 | intended as a full API reference, for that you should see the repository of 8 | [header 9 | files](https://code.videolan.org/videolan/libplacebo/-/tree/master/src/include/libplacebo), 10 | which are written to be (hopefully) understandable as-is. 11 | 12 | libplacebo exposes large parts of its internal abstractions publicly. This 13 | guide will take the general approach of starting as high level as possible and 14 | diving into the details in later chapters. 15 | 16 | A full listing of currently available APIs and their corresponding header 17 | files can be seen 18 | [here](https://code.videolan.org/videolan/libplacebo#api-overview). 19 | 20 | ## Getting Started 21 | 22 | To get started using libplacebo, you need to install it (and its development 23 | headers) somehow onto your system. On most distributions, this should be as 24 | simple as installing the corresponding `libplacebo-devel` package, or the 25 | appropriate variants. 26 | 27 | You can see a fill list of libplacebo packages and their names [on 28 | repology](https://repology.org/project/libplacebo/versions). 29 | 30 | !!! note "API versions" 31 | 32 | This document is targeting the "v4 API" overhaul, and as such, examples 33 | provided will generally fail to compile on libplacebo versions below v4.x. 34 | 35 | Alternatively, you can install it from the source code. For that, see the 36 | build instructions [located here](https://code.videolan.org/videolan/libplacebo#installing). 37 | -------------------------------------------------------------------------------- /src/d3d11/stubs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #include "../common.h" 19 | #include "log.h" 20 | 21 | #include 22 | 23 | const struct pl_d3d11_params pl_d3d11_default_params = { PL_D3D11_DEFAULTS }; 24 | 25 | pl_d3d11 pl_d3d11_create(pl_log log, const struct pl_d3d11_params *params) 26 | { 27 | pl_fatal(log, "libplacebo compiled without D3D11 support!"); 28 | return NULL; 29 | } 30 | 31 | void pl_d3d11_destroy(pl_d3d11 *pd3d11) 32 | { 33 | pl_d3d11 d3d11 = *pd3d11; 34 | pl_assert(!d3d11); 35 | } 36 | 37 | pl_d3d11 pl_d3d11_get(pl_gpu gpu) 38 | { 39 | return NULL; 40 | } 41 | 42 | pl_swapchain pl_d3d11_create_swapchain(pl_d3d11 d3d11, 43 | const struct pl_d3d11_swapchain_params *params) 44 | { 45 | pl_unreachable(); 46 | } 47 | 48 | IDXGISwapChain *pl_d3d11_swapchain_unwrap(pl_swapchain sw) 49 | { 50 | pl_unreachable(); 51 | } 52 | 53 | pl_tex pl_d3d11_wrap(pl_gpu gpu, const struct pl_d3d11_wrap_params *params) 54 | { 55 | pl_unreachable(); 56 | } 57 | -------------------------------------------------------------------------------- /src/tests/d3d11.c: -------------------------------------------------------------------------------- 1 | #include "gpu_tests.h" 2 | #include "d3d11/gpu.h" 3 | #include 4 | 5 | #include 6 | 7 | int main() 8 | { 9 | pl_log log = pl_test_logger(); 10 | IDXGIFactory1 *factory = NULL; 11 | IDXGIAdapter1 *adapter1 = NULL; 12 | HRESULT hr; 13 | 14 | HMODULE dxgi = LoadLibraryW(L"dxgi.dll"); 15 | if (!dxgi) 16 | return SKIP; 17 | 18 | __typeof__(&CreateDXGIFactory1) pCreateDXGIFactory1 = 19 | (void *) GetProcAddress(dxgi, "CreateDXGIFactory1"); 20 | if (!pCreateDXGIFactory1) 21 | return SKIP; 22 | 23 | hr = pCreateDXGIFactory1(&IID_IDXGIFactory1, (void **) &factory); 24 | if (FAILED(hr)) { 25 | printf("Failed to create DXGI factory\n"); 26 | return SKIP; 27 | } 28 | 29 | // Test all attached devices 30 | for (int i = 0;; i++) { 31 | hr = IDXGIFactory1_EnumAdapters1(factory, i, &adapter1); 32 | if (hr == DXGI_ERROR_NOT_FOUND) 33 | break; 34 | if (FAILED(hr)) { 35 | printf("Failed to enumerate adapters\n"); 36 | return SKIP; 37 | } 38 | 39 | DXGI_ADAPTER_DESC1 desc; 40 | hr = IDXGIAdapter1_GetDesc1(adapter1, &desc); 41 | if (FAILED(hr)) { 42 | printf("Failed to enumerate adapters\n"); 43 | return SKIP; 44 | } 45 | SAFE_RELEASE(adapter1); 46 | 47 | const struct pl_d3d11_t *d3d11 = pl_d3d11_create(log, pl_d3d11_params( 48 | .debug = true, 49 | .adapter_luid = desc.AdapterLuid, 50 | )); 51 | REQUIRE(d3d11); 52 | 53 | gpu_shader_tests(d3d11->gpu); 54 | 55 | pl_d3d11_destroy(&d3d11); 56 | } 57 | 58 | SAFE_RELEASE(factory); 59 | } 60 | -------------------------------------------------------------------------------- /src/glsl/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | #define PL_SPV_VERSION(major, minor) ((major) << 16 | (minor) << 8) 26 | #define PL_VLK_VERSION(major, minor) ((major) << 22 | (minor) << 12) 27 | 28 | // Max version that can be used 29 | #define PL_MAX_SPIRV_VER PL_SPV_VERSION(1, 6) 30 | 31 | struct pl_spirv_version { 32 | uint32_t env_version; 33 | uint32_t spv_version; 34 | }; 35 | 36 | // Returns minimum Vulkan version for given SPIR-V version 37 | static inline uint32_t pl_spirv_version_to_vulkan(uint32_t spirv_ver) 38 | { 39 | if (spirv_ver >= PL_SPV_VERSION(1, 6)) 40 | return PL_VLK_VERSION(1, 3); 41 | if (spirv_ver >= PL_SPV_VERSION(1, 4)) 42 | return PL_VLK_VERSION(1, 2); 43 | if (spirv_ver >= PL_SPV_VERSION(1, 1)) 44 | return PL_VLK_VERSION(1, 1); 45 | return PL_VLK_VERSION(1, 0); 46 | } 47 | 48 | enum glsl_shader_stage { 49 | GLSL_SHADER_VERTEX = 0, 50 | GLSL_SHADER_FRAGMENT, 51 | GLSL_SHADER_COMPUTE, 52 | }; 53 | -------------------------------------------------------------------------------- /src/opengl/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include "../common.h" 21 | #include "../log.h" 22 | #include "../gpu.h" 23 | #include "pl_thread.h" 24 | 25 | #include 26 | 27 | // Collision with llvm-mingw 28 | #undef MemoryBarrier 29 | 30 | #define GLAD_GL 31 | #define GLAD_GLES2 32 | #include 33 | #include 34 | 35 | typedef GladGLContext gl_funcs; 36 | 37 | // PL_PRIV(pl_opengl) 38 | struct gl_ctx { 39 | pl_log log; 40 | struct pl_opengl_params params; 41 | bool is_debug; 42 | bool is_debug_egl; 43 | bool is_gles; 44 | 45 | // For context locking 46 | pl_mutex lock; 47 | int count; 48 | 49 | // Dispatch table 50 | gl_funcs func; 51 | }; 52 | 53 | struct gl_cb { 54 | void (*callback)(void *priv); 55 | void *priv; 56 | GLsync sync; 57 | }; 58 | 59 | struct fbo_format { 60 | pl_fmt fmt; 61 | const struct gl_format *glfmt; 62 | }; 63 | 64 | // For locking/unlocking 65 | bool gl_make_current(pl_opengl gl); 66 | void gl_release_current(pl_opengl gl); 67 | -------------------------------------------------------------------------------- /src/colorspace.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | 22 | // Common constants for SMPTE ST.2084 (PQ) 23 | static const float PQ_M1 = 2610./4096 * 1./4, 24 | PQ_M2 = 2523./4096 * 128, 25 | PQ_C1 = 3424./4096, 26 | PQ_C2 = 2413./4096 * 32, 27 | PQ_C3 = 2392./4096 * 32; 28 | 29 | // Common constants for ARIB STD-B67 (HLG) 30 | static const float HLG_A = 0.17883277, 31 | HLG_B = 0.28466892, 32 | HLG_C = 0.55991073, 33 | HLG_REF = 1000.0 / PL_COLOR_SDR_WHITE; 34 | 35 | // Common constants for Panasonic V-Log 36 | static const float VLOG_B = 0.00873, 37 | VLOG_C = 0.241514, 38 | VLOG_D = 0.598206; 39 | 40 | // Common constants for Sony S-Log 41 | static const float SLOG_A = 0.432699, 42 | SLOG_B = 0.037584, 43 | SLOG_C = 0.616596 + 0.03, 44 | SLOG_P = 3.538813, 45 | SLOG_Q = 0.030001, 46 | SLOG_K2 = 155.0 / 219.0; 47 | -------------------------------------------------------------------------------- /src/tests/dav1d.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include "libplacebo/utils/dav1d.h" 3 | 4 | int main() 5 | { 6 | // Test enum functions 7 | for (enum pl_color_system sys = 0; sys < PL_COLOR_SYSTEM_COUNT; sys++) { 8 | // Exceptions to the rule, due to different handling in dav1d 9 | if (sys == PL_COLOR_SYSTEM_BT_2100_HLG || sys == PL_COLOR_SYSTEM_XYZ) 10 | continue; 11 | 12 | enum Dav1dMatrixCoefficients mc = pl_system_to_dav1d(sys); 13 | enum pl_color_system sys2 = pl_system_from_dav1d(mc); 14 | if (sys2) 15 | REQUIRE_CMP(sys, ==, sys2, "u"); 16 | } 17 | 18 | for (enum pl_color_levels lev = 0; lev < PL_COLOR_LEVELS_COUNT; lev++) { 19 | int range = pl_levels_to_dav1d(lev); 20 | enum pl_color_levels lev2 = pl_levels_from_dav1d(range); 21 | if (lev != PL_COLOR_LEVELS_UNKNOWN) 22 | REQUIRE_CMP(lev, ==, lev2, "u"); 23 | } 24 | 25 | for (enum pl_color_primaries prim = 0; prim < PL_COLOR_PRIM_COUNT; prim++) { 26 | enum Dav1dColorPrimaries dpri = pl_primaries_to_dav1d(prim); 27 | enum pl_color_primaries prim2 = pl_primaries_from_dav1d(dpri); 28 | if (prim2) 29 | REQUIRE_CMP(prim, ==, prim2, "u"); 30 | } 31 | 32 | for (enum pl_color_transfer trc = 0; trc < PL_COLOR_TRC_COUNT; trc++) { 33 | enum Dav1dTransferCharacteristics dtrc = pl_transfer_to_dav1d(trc); 34 | enum pl_color_transfer trc2 = pl_transfer_from_dav1d(dtrc); 35 | if (trc2) 36 | REQUIRE_CMP(trc, ==, trc2, "u"); 37 | } 38 | 39 | for (enum pl_chroma_location loc = 0; loc < PL_CHROMA_COUNT; loc++) { 40 | enum Dav1dChromaSamplePosition dloc = pl_chroma_to_dav1d(loc); 41 | enum pl_chroma_location loc2 = pl_chroma_from_dav1d(dloc); 42 | if (loc2) 43 | REQUIRE_CMP(loc, ==, loc2, "u"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /win32/demos.rc.in: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma code_page(65001) 19 | 20 | #include 21 | #include 22 | #include "version.h" 23 | 24 | VS_VERSION_INFO VERSIONINFO 25 | FILEVERSION @PL_MAJOR@, @PL_MINOR@, @PL_PATCH@ 26 | PRODUCTVERSION @PL_MAJOR@, @PL_MINOR@, @PL_PATCH@ 27 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 28 | FILEFLAGS 0 29 | FILEOS VOS__WINDOWS32 30 | FILETYPE VFT_APP 31 | FILESUBTYPE VFT2_UNKNOWN 32 | { 33 | BLOCK "StringFileInfo" { 34 | BLOCK "000004b0" { 35 | VALUE "Comments", "libplacebo is distributed under the terms of the GNU Lesser General Public License, version 2.1" 36 | VALUE "CompanyName", "libplacebo" 37 | VALUE "FileDescription", "libplacebo" 38 | VALUE "FileVersion", BUILD_VERSION 39 | VALUE "LegalCopyright", L"Copyright \xA9 2017-2024 libplacebo project" 40 | VALUE "ProductName", "libplacebo" 41 | VALUE "ProductVersion", BUILD_VERSION 42 | } 43 | } 44 | BLOCK "VarFileInfo" { 45 | VALUE "Translation", 0, 1200 46 | } 47 | } 48 | 49 | CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "demos.manifest" 50 | -------------------------------------------------------------------------------- /src/glsl/glslang.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | 23 | typedef struct TLimits TLimits; 24 | typedef struct TBuiltInResource TBuiltInResource; 25 | #include 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | #include "utils.h" 32 | 33 | bool pl_glslang_init(void); 34 | void pl_glslang_uninit(void); 35 | 36 | struct pl_glslang_res { 37 | // Compilation status 38 | bool success; 39 | const char *error_msg; 40 | 41 | // Compiled shader memory, or NULL 42 | void *data; 43 | size_t size; 44 | }; 45 | 46 | // Compile GLSL into a SPIRV stream, if possible. The resulting 47 | // pl_glslang_res can simply be freed with pl_free() when done. 48 | struct pl_glslang_res *pl_glslang_compile(struct pl_glsl_version glsl_ver, 49 | struct pl_spirv_version spirv_ver, 50 | enum glsl_shader_stage stage, 51 | const char *shader); 52 | 53 | extern const TBuiltInResource DefaultTBuiltInResource; 54 | 55 | #ifdef __cplusplus 56 | } 57 | #endif 58 | -------------------------------------------------------------------------------- /src/glsl/spirv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include "log.h" 21 | #include "utils.h" 22 | 23 | typedef const struct pl_spirv_t { 24 | const struct spirv_compiler *impl; 25 | pl_log log; 26 | 27 | // SPIR-V version specified at creation time. 28 | struct pl_spirv_version version; 29 | 30 | // For cache invalidation, should uniquely identify everything about this 31 | // spirv compiler and its configuration. 32 | uint64_t signature; 33 | } *pl_spirv; 34 | 35 | // Initialize a SPIR-V compiler instance, or returns NULL on failure. 36 | pl_spirv pl_spirv_create(pl_log log, struct pl_spirv_version spirv_ver); 37 | void pl_spirv_destroy(pl_spirv *spirv); 38 | 39 | // Compile GLSL to SPIR-V. Returns {0} on failure. 40 | pl_str pl_spirv_compile_glsl(pl_spirv spirv, void *alloc, 41 | struct pl_glsl_version glsl_ver, 42 | enum glsl_shader_stage stage, 43 | const char *shader); 44 | 45 | struct spirv_compiler { 46 | const char *name; 47 | void (*destroy)(pl_spirv spirv); 48 | __typeof__(pl_spirv_create) *create; 49 | __typeof__(pl_spirv_compile_glsl) *compile; 50 | }; 51 | -------------------------------------------------------------------------------- /src/opengl/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include "common.h" 21 | 22 | // Iterate through callbacks attached to the `pl_gl` and execute all of the 23 | // ones that have completed. 24 | // 25 | // Thread-safety: Unsafe 26 | void gl_poll_callbacks(pl_gpu gpu); 27 | 28 | // Return a human-readable name for various OpenGL errors 29 | // 30 | // Thread-safety: Safe 31 | const char *gl_err_str(GLenum err); 32 | 33 | // Check for errors and log them + return false if detected 34 | // 35 | // Thread-safety: Unsafe 36 | bool gl_check_err(pl_gpu gpu, const char *fun); 37 | 38 | // Returns true if the context is a suspected software rasterizer 39 | // 40 | // Thread-safety: Unsafe 41 | bool gl_is_software(pl_opengl gl); 42 | 43 | // Returns true if the context is detected as OpenGL ES 44 | // 45 | // Thread-safety: Unsafe 46 | bool gl_is_gles(pl_opengl gl); 47 | 48 | // Check for presence of an extension, alternatively a minimum GL version 49 | // 50 | // Thread-safety: Unsafe 51 | bool gl_test_ext(pl_gpu gpu, const char *ext, int gl_ver, int gles_ver); 52 | 53 | // Thread-safety: Safe 54 | const char *egl_err_str(EGLenum err); 55 | 56 | // Thread-safety: Unsafe 57 | bool egl_check_err(pl_gpu gpu, const char *fun); 58 | -------------------------------------------------------------------------------- /win32/libplacebo.rc.in: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma code_page(65001) 19 | 20 | #include 21 | #include 22 | #include "version.h" 23 | 24 | VS_VERSION_INFO VERSIONINFO 25 | FILEVERSION @PL_MAJOR@, @PL_MINOR@, @PL_PATCH@ 26 | PRODUCTVERSION @PL_MAJOR@, @PL_MINOR@, @PL_PATCH@ 27 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 28 | FILEFLAGS 0 29 | FILEOS VOS__WINDOWS32 30 | FILETYPE VFT_DLL 31 | FILESUBTYPE VFT2_UNKNOWN 32 | { 33 | BLOCK "StringFileInfo" { 34 | BLOCK "000004b0" { 35 | VALUE "Comments", "libplacebo is distributed under the terms of the GNU Lesser General Public License, version 2.1" 36 | VALUE "CompanyName", "libplacebo" 37 | VALUE "FileDescription", "libplacebo" 38 | VALUE "FileVersion", BUILD_VERSION 39 | VALUE "LegalCopyright", L"Copyright \xA9 2017-2024 libplacebo project" 40 | VALUE "OriginalFilename", "libplacebo-@PL_MINOR@.dll" 41 | VALUE "ProductName", "libplacebo" 42 | VALUE "ProductVersion", BUILD_VERSION 43 | } 44 | } 45 | BLOCK "VarFileInfo" { 46 | VALUE "Translation", 0, 1200 47 | } 48 | } 49 | 50 | CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "libplacebo.manifest" 51 | -------------------------------------------------------------------------------- /src/opengl/stubs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #include "../common.h" 19 | #include "log.h" 20 | 21 | #include 22 | 23 | const struct pl_opengl_params pl_opengl_default_params = {0}; 24 | 25 | pl_opengl pl_opengl_create(pl_log log, const struct pl_opengl_params *params) 26 | { 27 | pl_fatal(log, "libplacebo compiled without OpenGL support!"); 28 | return NULL; 29 | } 30 | 31 | void pl_opengl_destroy(pl_opengl *pgl) 32 | { 33 | pl_opengl gl = *pgl; 34 | pl_assert(!gl); 35 | } 36 | 37 | pl_opengl pl_opengl_get(pl_gpu gpu) 38 | { 39 | return NULL; 40 | } 41 | 42 | pl_swapchain pl_opengl_create_swapchain(pl_opengl gl, 43 | const struct pl_opengl_swapchain_params *params) 44 | { 45 | pl_unreachable(); 46 | } 47 | 48 | void pl_opengl_swapchain_update_fb(pl_swapchain sw, 49 | const struct pl_opengl_framebuffer *fb) 50 | { 51 | pl_unreachable(); 52 | } 53 | 54 | pl_tex pl_opengl_wrap(pl_gpu gpu, const struct pl_opengl_wrap_params *params) 55 | { 56 | pl_unreachable(); 57 | } 58 | 59 | unsigned int pl_opengl_unwrap(pl_gpu gpu, pl_tex tex, unsigned int *out_target, 60 | int *out_iformat, unsigned int *out_fbo) 61 | { 62 | pl_unreachable(); 63 | } 64 | -------------------------------------------------------------------------------- /demos/ui.h: -------------------------------------------------------------------------------- 1 | // License: CC0 / Public Domain 2 | #pragma once 3 | 4 | #define NK_INCLUDE_FIXED_TYPES 5 | #define NK_INCLUDE_DEFAULT_ALLOCATOR 6 | #define NK_INCLUDE_STANDARD_IO 7 | #define NK_INCLUDE_STANDARD_BOOL 8 | #define NK_INCLUDE_STANDARD_VARARGS 9 | #define NK_INCLUDE_VERTEX_BUFFER_OUTPUT 10 | #define NK_INCLUDE_FONT_BAKING 11 | #define NK_INCLUDE_DEFAULT_FONT 12 | #define NK_BUTTON_TRIGGER_ON_RELEASE 13 | #define NK_UINT_DRAW_INDEX 14 | #include 15 | 16 | #include "common.h" 17 | #include "window.h" 18 | 19 | struct ui; 20 | 21 | struct ui *ui_create(pl_gpu gpu); 22 | void ui_destroy(struct ui **ui); 23 | 24 | // Update/Logic/Draw cycle 25 | void ui_update_input(struct ui *ui, const struct window *window); 26 | struct nk_context *ui_get_context(struct ui *ui); 27 | bool ui_draw(struct ui *ui, const struct pl_swapchain_frame *frame); 28 | 29 | // Helper function to draw a custom widget for drag&drop operations, returns 30 | // true if the widget is hovered 31 | static inline bool ui_widget_hover(struct nk_context *nk, const char *label) 32 | { 33 | struct nk_rect bounds; 34 | if (!nk_widget(&bounds, nk)) 35 | return false; 36 | 37 | struct nk_command_buffer *canvas = nk_window_get_canvas(nk); 38 | bool hover = nk_input_is_mouse_hovering_rect(&nk->input, bounds); 39 | 40 | float h, s, v; 41 | nk_color_hsv_f(&h, &s, &v, nk->style.window.background); 42 | struct nk_color background = nk_hsv_f(h, s, v + (hover ? 0.1f : -0.02f)); 43 | struct nk_color border = nk_hsv_f(h, s, v + 0.20f); 44 | nk_fill_rect(canvas, bounds, 0.0f, background); 45 | nk_stroke_rect(canvas, bounds, 0.0f, 2.0f, border); 46 | 47 | const float pad = 10.0f; 48 | struct nk_rect text = { 49 | .x = bounds.x + pad, 50 | .y = bounds.y + pad, 51 | .w = bounds.w - 2 * pad, 52 | .h = bounds.h - 2 * pad, 53 | }; 54 | 55 | nk_draw_text(canvas, text, label, nk_strlen(label), nk->style.font, 56 | background, nk->style.text.color); 57 | 58 | return hover; 59 | } 60 | -------------------------------------------------------------------------------- /src/vulkan/meson.build: -------------------------------------------------------------------------------- 1 | vulkan_build = get_option('vulkan') 2 | vulkan_link = get_option('vk-proc-addr') 3 | vulkan_loader = dependency('vulkan', required: false) 4 | vulkan_headers = vulkan_loader.partial_dependency(includes: true, compile_args: true) 5 | registry_xml = get_option('vulkan-registry') 6 | 7 | # Prefer our Vulkan headers for portability 8 | vulkan_headers_dir = thirdparty/'Vulkan-Headers' 9 | if fs.is_dir(vulkan_headers_dir/'include') 10 | vulkan_headers_inc = include_directories('../../3rdparty/Vulkan-Headers/include') 11 | vulkan_headers = declare_dependency(include_directories: vulkan_headers_inc) 12 | # Force the use of this vk.xml because it has to be in sync with the headers 13 | registry_xml = vulkan_headers_dir/'registry/vk.xml' 14 | endif 15 | 16 | vulkan_build = vulkan_build.require( 17 | cc.has_header_symbol('vulkan/vulkan_core.h', 'VK_VERSION_1_4', 18 | dependencies: vulkan_headers), 19 | error_message: 'vulkan.h was not found on the system, nor inside ' + 20 | '`3rdparty/Vulkan-Headers`. Please run `git submodule update --init` ' + 21 | 'followed by `meson --wipe`.') 22 | components.set('vulkan', vulkan_build.allowed()) 23 | 24 | vulkan_link = vulkan_link.require(vulkan_loader.found() and vulkan_build.allowed()) 25 | components.set('vk-proc-addr', vulkan_link.allowed()) 26 | 27 | build_deps += vulkan_headers 28 | 29 | if vulkan_build.allowed() 30 | sources += [ 31 | 'vulkan/command.c', 32 | 'vulkan/context.c', 33 | 'vulkan/formats.c', 34 | 'vulkan/gpu.c', 35 | 'vulkan/gpu_buf.c', 36 | 'vulkan/gpu_tex.c', 37 | 'vulkan/gpu_pass.c', 38 | 'vulkan/malloc.c', 39 | 'vulkan/swapchain.c', 40 | 'vulkan/utils.c', 41 | ] 42 | 43 | datadir = get_option('prefix') / get_option('datadir') 44 | sources += custom_target('utils_gen.c', 45 | input: 'utils_gen.py', 46 | output: 'utils_gen.c', 47 | command: [python, '@INPUT@', datadir, registry_xml, '@OUTPUT@'], 48 | env: python_env, 49 | ) 50 | 51 | if vulkan_link.allowed() 52 | build_deps += vulkan_loader 53 | tests += 'vulkan.c' 54 | endif 55 | else 56 | sources += 'vulkan/stubs.c' 57 | endif 58 | -------------------------------------------------------------------------------- /demos/window.h: -------------------------------------------------------------------------------- 1 | // License: CC0 / Public Domain 2 | #pragma once 3 | 4 | #include 5 | 6 | struct window { 7 | const struct window_impl *impl; 8 | pl_swapchain swapchain; 9 | pl_gpu gpu; 10 | bool window_lost; 11 | }; 12 | 13 | struct window_params { 14 | const char *title; 15 | int width; 16 | int height; 17 | const char *forced_impl; 18 | 19 | // initial color space 20 | struct pl_swapchain_colors colors; 21 | bool alpha; 22 | }; 23 | 24 | struct window *window_create(pl_log log, const struct window_params *params); 25 | void window_destroy(struct window **win); 26 | 27 | // Poll/wait for window events 28 | void window_poll(struct window *win, bool block); 29 | 30 | // Input handling 31 | enum button { 32 | BTN_LEFT, 33 | BTN_RIGHT, 34 | BTN_MIDDLE, 35 | }; 36 | 37 | enum key { 38 | KEY_ESC, 39 | }; 40 | 41 | void window_get_cursor(const struct window *win, int *x, int *y); 42 | void window_get_scroll(const struct window *win, float *dx, float *dy); 43 | bool window_get_button(const struct window *win, enum button); 44 | bool window_get_key(const struct window *win, enum key); 45 | char *window_get_file(const struct window *win); 46 | bool window_toggle_fullscreen(const struct window *win, bool fullscreen); 47 | bool window_is_fullscreen(const struct window *win); 48 | const char *window_get_clipboard(const struct window *win); 49 | void window_set_clipboard(const struct window *win, const char *text); 50 | 51 | // For implementations 52 | struct window_impl { 53 | const char *name; 54 | const char *tag; 55 | __typeof__(window_create) *create; 56 | __typeof__(window_destroy) *destroy; 57 | __typeof__(window_poll) *poll; 58 | __typeof__(window_get_cursor) *get_cursor; 59 | __typeof__(window_get_scroll) *get_scroll; 60 | __typeof__(window_get_button) *get_button; 61 | __typeof__(window_get_key) *get_key; 62 | __typeof__(window_get_file) *get_file; 63 | __typeof__(window_toggle_fullscreen) *toggle_fullscreen; 64 | __typeof__(window_is_fullscreen) *is_fullscreen; 65 | __typeof__(window_get_clipboard) *get_clipboard; 66 | __typeof__(window_set_clipboard) *set_clipboard; 67 | }; 68 | -------------------------------------------------------------------------------- /src/glsl/spirv.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #include "spirv.h" 19 | 20 | extern const struct spirv_compiler pl_spirv_shaderc; 21 | extern const struct spirv_compiler pl_spirv_glslang; 22 | 23 | static const struct spirv_compiler *compilers[] = { 24 | #ifdef PL_HAVE_SHADERC 25 | &pl_spirv_shaderc, 26 | #endif 27 | #ifdef PL_HAVE_GLSLANG 28 | &pl_spirv_glslang, 29 | #endif 30 | }; 31 | 32 | pl_spirv pl_spirv_create(pl_log log, struct pl_spirv_version spirv_ver) 33 | { 34 | for (int i = 0; i < PL_ARRAY_SIZE(compilers); i++) { 35 | pl_spirv spirv = compilers[i]->create(log, spirv_ver); 36 | if (!spirv) 37 | continue; 38 | 39 | pl_info(log, "Initialized SPIR-V compiler '%s'", compilers[i]->name); 40 | return spirv; 41 | } 42 | 43 | pl_fatal(log, "Failed initializing any SPIR-V compiler! Maybe libplacebo " 44 | "was built without support for either libshaderc or glslang?"); 45 | return NULL; 46 | } 47 | 48 | void pl_spirv_destroy(pl_spirv *pspirv) 49 | { 50 | pl_spirv spirv = *pspirv; 51 | if (!spirv) 52 | return; 53 | 54 | spirv->impl->destroy(spirv); 55 | *pspirv = NULL; 56 | } 57 | 58 | pl_str pl_spirv_compile_glsl(pl_spirv spirv, void *alloc, 59 | struct pl_glsl_version glsl, 60 | enum glsl_shader_stage stage, 61 | const char *shader) 62 | { 63 | return spirv->impl->compile(spirv, alloc, glsl, stage, shader); 64 | } 65 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | # Optional components 2 | option('vulkan', type: 'feature', value: 'auto', 3 | description: 'Vulkan-based renderer') 4 | 5 | option('vk-proc-addr', type: 'feature', value: 'auto', 6 | description: 'Link directly against vkGetInstanceProcAddr from libvulkan.so') 7 | 8 | option('vulkan-registry', type: 'string', value: '', 9 | description: 'Path to vulkan XML registry (for code generation)') 10 | 11 | option('opengl', type: 'feature', value: 'auto', 12 | description: 'OpenGL-based renderer') 13 | 14 | option('gl-proc-addr', type: 'feature', value: 'auto', 15 | description: 'Enable built-in OpenGL loader (uses dlopen, dlsym...)') 16 | 17 | option('d3d11', type: 'feature', value: 'auto', 18 | description: 'Direct3D 11 based renderer') 19 | 20 | option('glslang', type: 'feature', value: 'auto', 21 | description: 'glslang SPIR-V compiler') 22 | 23 | option('shaderc', type: 'feature', value: 'auto', 24 | description: 'libshaderc SPIR-V compiler') 25 | 26 | option('lcms', type: 'feature', value: 'auto', 27 | description: 'LittleCMS 2 support') 28 | 29 | option('dovi', type: 'feature', value: 'auto', 30 | description: 'Dolby Vision reshaping support') 31 | 32 | option('libdovi', type: 'feature', value: 'auto', 33 | description: 'libdovi support') 34 | 35 | # Miscellaneous 36 | option('demos', type: 'boolean', value: true, 37 | description: 'Enable building (and installing) the demo programs') 38 | 39 | option('tests', type: 'boolean', value: false, 40 | description: 'Enable building the test cases') 41 | 42 | option('bench', type: 'boolean', value: false, 43 | description: 'Enable building benchmarks (`meson test benchmark`)') 44 | 45 | option('fuzz', type: 'boolean', value: false, 46 | description: 'Enable building fuzzer binaries (`CC=afl-cc`)') 47 | 48 | option('unwind', type: 'feature', value: 'auto', 49 | description: 'Enable linking against libunwind for printing stack traces caused by runtime errors') 50 | 51 | option('xxhash', type: 'feature', value: 'auto', 52 | description: 'Use libxxhash as a faster replacement for internal siphash') 53 | 54 | option('debug-abort', type: 'boolean', value: false, 55 | description: 'abort() on most runtime errors (only for debugging purposes)') 56 | -------------------------------------------------------------------------------- /src/tests/dummy.c: -------------------------------------------------------------------------------- 1 | #include "gpu_tests.h" 2 | 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | pl_log log = pl_test_logger(); 9 | pl_gpu gpu = pl_gpu_dummy_create(log, NULL); 10 | pl_buffer_tests(gpu); 11 | pl_texture_tests(gpu); 12 | 13 | // Attempt creating a shader and accessing the resulting LUT 14 | pl_tex dummy = pl_tex_dummy_create(gpu, pl_tex_dummy_params( 15 | .w = 100, 16 | .h = 100, 17 | .format = pl_find_named_fmt(gpu, "rgba8"), 18 | )); 19 | 20 | struct pl_sample_src src = { 21 | .tex = dummy, 22 | .new_w = 1000, 23 | .new_h = 1000, 24 | }; 25 | 26 | pl_shader_obj lut = NULL; 27 | struct pl_sample_filter_params filter_params = { 28 | .filter = pl_filter_ewa_lanczos, 29 | .lut = &lut, 30 | }; 31 | 32 | pl_shader sh = pl_shader_alloc(log, pl_shader_params( .gpu = gpu )); 33 | REQUIRE(pl_shader_sample_polar(sh, &src, &filter_params)); 34 | const struct pl_shader_res *res = pl_shader_finalize(sh); 35 | REQUIRE(res); 36 | 37 | for (int n = 0; n < res->num_descriptors; n++) { 38 | const struct pl_shader_desc *sd = &res->descriptors[n]; 39 | if (sd->desc.type != PL_DESC_SAMPLED_TEX) 40 | continue; 41 | 42 | pl_tex tex = sd->binding.object; 43 | const float *data = (float *) pl_tex_dummy_data(tex); 44 | if (!data) 45 | continue; // means this was the `dummy` texture 46 | 47 | #ifdef PRINT_LUTS 48 | for (int i = 0; i < tex->params.w; i++) 49 | printf("lut[%d] = %f\n", i, data[i]); 50 | #endif 51 | } 52 | 53 | // Try out generation of the sampler2D interface 54 | src.tex = NULL; 55 | src.tex_w = 100; 56 | src.tex_h = 100; 57 | src.format = PL_FMT_UNORM; 58 | src.sampler = PL_SAMPLER_NORMAL; 59 | src.mode = PL_TEX_SAMPLE_LINEAR; 60 | 61 | pl_shader_reset(sh, pl_shader_params( .gpu = gpu )); 62 | REQUIRE(pl_shader_sample_polar(sh, &src, &filter_params)); 63 | REQUIRE((res = pl_shader_finalize(sh))); 64 | REQUIRE_CMP(res->input, ==, PL_SHADER_SIG_SAMPLER, "u"); 65 | 66 | pl_shader_free(&sh); 67 | pl_shader_obj_destroy(&lut); 68 | pl_tex_destroy(gpu, &dummy); 69 | pl_gpu_dummy_destroy(&gpu); 70 | pl_log_destroy(&log); 71 | } 72 | -------------------------------------------------------------------------------- /src/opengl/meson.build: -------------------------------------------------------------------------------- 1 | opengl_build = get_option('opengl') 2 | opengl_link = get_option('gl-proc-addr') 3 | 4 | if host_machine.system() == 'windows' or host_machine.system().endswith('bsd') or \ 5 | host_machine.system() == 'dragonfly' 6 | libdl = declare_dependency() 7 | else 8 | libdl = cc.find_library('dl', required : opengl_link) 9 | endif 10 | opengl_link = opengl_link.require(libdl.found()) 11 | components.set('opengl', opengl_build.allowed()) 12 | components.set('gl-proc-addr', opengl_link.allowed()) 13 | 14 | if opengl_build.allowed() 15 | sources += [ 16 | 'opengl/context.c', 17 | 'opengl/formats.c', 18 | 'opengl/loader_gl.c', 19 | 'opengl/loader_egl.c', 20 | 'opengl/gpu.c', 21 | 'opengl/gpu_tex.c', 22 | 'opengl/gpu_pass.c', 23 | 'opengl/swapchain.c', 24 | 'opengl/utils.c', 25 | ] 26 | 27 | if opengl_link.allowed() 28 | build_deps += libdl 29 | tests += 'opengl_surfaceless.c' 30 | endif 31 | 32 | gl_extensions = [ 33 | 'GL_AMD_pinned_memory', 34 | 'GL_ARB_buffer_storage', 35 | 'GL_ARB_compute_shader', 36 | 'GL_ARB_framebuffer_object', 37 | 'GL_ARB_get_program_binary', 38 | 'GL_ARB_invalidate_subdata', 39 | 'GL_ARB_pixel_buffer_object', 40 | 'GL_ARB_program_interface_query', 41 | 'GL_ARB_shader_image_load_store', 42 | 'GL_ARB_shader_storage_buffer_object', 43 | 'GL_ARB_sync', 44 | 'GL_ARB_texture_float', 45 | 'GL_ARB_texture_gather', 46 | 'GL_ARB_texture_rg', 47 | 'GL_ARB_timer_query', 48 | 'GL_ARB_uniform_buffer_object', 49 | 'GL_ARB_vertex_array_object', 50 | 'GL_ARB_half_float_pixel', 51 | 'GL_EXT_EGL_image_storage', 52 | 'GL_EXT_color_buffer_float', 53 | 'GL_EXT_texture3D', 54 | 'GL_EXT_texture_format_BGRA8888', 55 | 'GL_EXT_texture_norm16', 56 | 'GL_EXT_texture_rg', 57 | 'GL_EXT_unpack_subimage', 58 | 'GL_KHR_debug', 59 | 'GL_OES_EGL_image', 60 | 'GL_OES_EGL_image_external', 61 | 'EGL_EXT_image_dma_buf_import', 62 | 'EGL_EXT_image_dma_buf_import_modifiers', 63 | 'EGL_EXT_platform_base', 64 | 'EGL_KHR_debug', 65 | 'EGL_KHR_image_base', 66 | 'EGL_MESA_image_dma_buf_export', 67 | 'EGL_MESA_platform_surfaceless', 68 | ] 69 | 70 | # Generate GL loader 71 | subdir('include/glad') 72 | else 73 | glad_dep = [] 74 | sources += 'opengl/stubs.c' 75 | endif 76 | -------------------------------------------------------------------------------- /src/d3d11/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include "../common.h" 21 | #include "../log.h" 22 | 23 | #ifdef PL_HAVE_DXGI_DEBUG 24 | #include 25 | #endif 26 | 27 | #include 28 | 29 | // Shared struct used to hold the D3D11 device and associated interfaces 30 | struct d3d11_ctx { 31 | pl_log log; 32 | pl_d3d11 d3d11; 33 | 34 | // Copy of the device from pl_d3d11 for convenience. Does not hold an 35 | // additional reference. 36 | ID3D11Device *dev; 37 | 38 | // DXGI device. This does hold a reference. 39 | IDXGIDevice1 *dxgi_dev; 40 | 41 | #ifdef PL_HAVE_DXGI_DEBUG 42 | // Debug interfaces 43 | IDXGIDebug *debug; 44 | IDXGIInfoQueue *iqueue; 45 | uint64_t last_discarded; // Last count of discarded messages 46 | DXGI_INFO_QUEUE_MESSAGE *dxgi_msg; 47 | #endif 48 | 49 | // pl_gpu_is_failed (We saw a device removed error!) 50 | bool is_failed; 51 | }; 52 | 53 | // DDK value. Apparently some D3D functions can return this instead of the 54 | // proper user-mode error code. See: 55 | // https://docs.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-idxgiswapchain-present 56 | #define D3DDDIERR_DEVICEREMOVED (0x88760870) 57 | 58 | #ifndef D3D11_FORMAT_SUPPORT2_UAV_TYPED_STORE 59 | #define D3D11_FORMAT_SUPPORT2_UAV_TYPED_STORE (0x80) 60 | #endif 61 | #ifndef D3D11_FORMAT_SUPPORT2_UAV_TYPED_LOAD 62 | #define D3D11_FORMAT_SUPPORT2_UAV_TYPED_LOAD (0x40) 63 | #endif 64 | #ifndef PL_HAVE_DXGI_DEBUG_D3D11 65 | DEFINE_GUID(DXGI_DEBUG_D3D11, 0x4b99317b, 0xac39, 0x4aa6, 0xbb, 0xb, 0xba, 0xa0, 0x47, 0x84, 0x79, 0x8f); 66 | #endif 67 | -------------------------------------------------------------------------------- /src/shaders/film_grain.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #include "shaders.h" 19 | #include "shaders/film_grain.h" 20 | 21 | bool pl_needs_film_grain(const struct pl_film_grain_params *params) 22 | { 23 | switch (params->data.type) { 24 | case PL_FILM_GRAIN_NONE: return false; 25 | case PL_FILM_GRAIN_AV1: return pl_needs_fg_av1(params); 26 | case PL_FILM_GRAIN_H274: return pl_needs_fg_h274(params); 27 | default: pl_unreachable(); 28 | } 29 | } 30 | 31 | struct sh_grain_obj { 32 | pl_shader_obj av1; 33 | pl_shader_obj h274; 34 | }; 35 | 36 | static void sh_grain_uninit(pl_gpu gpu, void *ptr) 37 | { 38 | struct sh_grain_obj *obj = ptr; 39 | pl_shader_obj_destroy(&obj->av1); 40 | pl_shader_obj_destroy(&obj->h274); 41 | } 42 | 43 | bool pl_shader_film_grain(pl_shader sh, pl_shader_obj *grain_state, 44 | const struct pl_film_grain_params *params) 45 | { 46 | if (!pl_needs_film_grain(params)) { 47 | // FIXME: Instead of erroring, sample directly 48 | SH_FAIL(sh, "pl_shader_film_grain called but no film grain needs to be " 49 | "applied, test with `pl_needs_film_grain` first!"); 50 | return false; 51 | } 52 | 53 | struct sh_grain_obj *obj; 54 | obj = SH_OBJ(sh, grain_state, PL_SHADER_OBJ_FILM_GRAIN, 55 | struct sh_grain_obj, sh_grain_uninit); 56 | if (!obj) 57 | return false; 58 | 59 | switch (params->data.type) { 60 | case PL_FILM_GRAIN_NONE: return false; 61 | case PL_FILM_GRAIN_AV1: return pl_shader_fg_av1(sh, &obj->av1, params); 62 | case PL_FILM_GRAIN_H274: return pl_shader_fg_h274(sh, &obj->h274, params); 63 | default: pl_unreachable(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /demos/colors.c: -------------------------------------------------------------------------------- 1 | /* Simplistic demo that just makes the window colorful, including alpha 2 | * transparency if supported by the windowing system. 3 | * 4 | * License: CC0 / Public Domain 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "common.h" 13 | #include "pl_clock.h" 14 | #include "window.h" 15 | 16 | static pl_log logger; 17 | static struct window *win; 18 | 19 | static void uninit(int ret) 20 | { 21 | window_destroy(&win); 22 | pl_log_destroy(&logger); 23 | exit(ret); 24 | } 25 | 26 | int main(int argc, char **argv) 27 | { 28 | logger = pl_log_create(PL_API_VER, pl_log_params( 29 | .log_cb = pl_log_color, 30 | .log_level = PL_LOG_DEBUG, 31 | )); 32 | 33 | win = window_create(logger, &(struct window_params) { 34 | .title = "colors demo", 35 | .width = 640, 36 | .height = 480, 37 | .alpha = true, 38 | }); 39 | if (!win) 40 | uninit(1); 41 | 42 | pl_clock_t ts_start, ts; 43 | if ((ts_start = pl_clock_now()) == 0) { 44 | uninit(1); 45 | } 46 | 47 | while (!win->window_lost) { 48 | if (window_get_key(win, KEY_ESC)) 49 | break; 50 | 51 | struct pl_swapchain_frame frame; 52 | bool ok = pl_swapchain_start_frame(win->swapchain, &frame); 53 | if (!ok) { 54 | // Something unexpected happened, perhaps the window is not 55 | // visible? Wait for events and try again. 56 | window_poll(win, true); 57 | continue; 58 | } 59 | 60 | if ((ts = pl_clock_now()) == 0) 61 | uninit(1); 62 | 63 | const double period = 10.; // in seconds 64 | double secs = fmod(pl_clock_diff(ts, ts_start), period); 65 | 66 | double pos = 2 * M_PI * secs / period; 67 | float alpha = (cos(pos) + 1.0) / 2.0; 68 | 69 | assert(frame.fbo->params.blit_dst); 70 | pl_tex_clear(win->gpu, frame.fbo, (float[4]) { 71 | alpha * (sinf(2 * pos + 0.0) + 1.0) / 2.0, 72 | alpha * (sinf(2 * pos + 2.0) + 1.0) / 2.0, 73 | alpha * (sinf(2 * pos + 4.0) + 1.0) / 2.0, 74 | alpha, 75 | }); 76 | 77 | ok = pl_swapchain_submit_frame(win->swapchain); 78 | if (!ok) { 79 | fprintf(stderr, "libplacebo: failed submitting frame!\n"); 80 | uninit(3); 81 | } 82 | 83 | pl_swapchain_swap_buffers(win->swapchain); 84 | window_poll(win, false); 85 | } 86 | 87 | uninit(0); 88 | } 89 | -------------------------------------------------------------------------------- /src/pl_thread.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include "os.h" 21 | 22 | enum pl_mutex_type { 23 | PL_MUTEX_NORMAL = 0, 24 | PL_MUTEX_RECURSIVE, 25 | }; 26 | 27 | #define pl_mutex_init(mutex) \ 28 | pl_mutex_init_type(mutex, PL_MUTEX_NORMAL) 29 | 30 | // Note: This is never compiled, and only documents the API. The actual 31 | // implementations of these prototypes may be macros. 32 | #ifdef PL_API_REFERENCE 33 | 34 | typedef void pl_mutex; 35 | void pl_mutex_init_type(pl_mutex *mutex, enum pl_mutex_type mtype); 36 | int pl_mutex_destroy(pl_mutex *mutex); 37 | int pl_mutex_lock(pl_mutex *mutex); 38 | int pl_mutex_unlock(pl_mutex *mutex); 39 | 40 | typedef void pl_cond; 41 | int pl_cond_init(pl_cond *cond); 42 | int pl_cond_destroy(pl_cond *cond); 43 | int pl_cond_broadcast(pl_cond *cond); 44 | int pl_cond_signal(pl_cond *cond); 45 | 46 | // `timeout` is in nanoseconds, or UINT64_MAX to block forever 47 | int pl_cond_timedwait(pl_cond *cond, pl_mutex *mutex, uint64_t timeout); 48 | int pl_cond_wait(pl_cond *cond, pl_mutex *mutex); 49 | 50 | typedef void pl_static_mutex; 51 | #define PL_STATIC_MUTEX_INITIALIZER 52 | int pl_static_mutex_lock(pl_static_mutex *mutex); 53 | int pl_static_mutex_unlock(pl_static_mutex *mutex); 54 | 55 | typedef void pl_thread; 56 | #define PL_THREAD_VOID void 57 | #define PL_THREAD_RETURN() return 58 | int pl_thread_create(pl_thread *thread, PL_THREAD_VOID (*fun)(void *), void *arg); 59 | int pl_thread_join(pl_thread thread); 60 | 61 | // Returns true if slept the full time, false otherwise 62 | bool pl_thread_sleep(double t); 63 | 64 | #endif 65 | 66 | // Actual platform-specific implementation 67 | #ifdef PL_HAVE_WIN32 68 | #include "pl_thread_win32.h" 69 | #elif defined(PL_HAVE_PTHREAD) 70 | #include "pl_thread_pthread.h" 71 | #else 72 | #error No threading implementation available! 73 | #endif 74 | -------------------------------------------------------------------------------- /src/utils/dolbyvision.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #include "common.h" 19 | #include 20 | 21 | #ifdef PL_HAVE_LIBDOVI 22 | #include 23 | #include 24 | #endif 25 | 26 | void pl_hdr_metadata_from_dovi_rpu(struct pl_hdr_metadata *out, 27 | const uint8_t *buf, size_t size) 28 | { 29 | #ifdef PL_HAVE_LIBDOVI 30 | if (buf && size) { 31 | DoviRpuOpaque *rpu = 32 | dovi_parse_unspec62_nalu(buf, size); 33 | const DoviRpuDataHeader *header = dovi_rpu_get_header(rpu); 34 | 35 | if (header && header->vdr_dm_metadata_present_flag) { 36 | // Profile 4 reshaping isn't done as it is a dual layer format. 37 | // However there are still unknowns on its EOTF, so it cannot be enabled. 38 | // 39 | // For profile 7, the brightness metadata can still be used as most 40 | // titles are going to have accurate metadata<->image brightness, 41 | // with the exception of some titles that require the enhancement layer 42 | // to be processed to restore the intended brightness, which would then 43 | // match the metadata values. 44 | if (header->guessed_profile == 4) { 45 | goto done; 46 | } 47 | 48 | const DoviVdrDmData *vdr_dm_data = dovi_rpu_get_vdr_dm_data(rpu); 49 | if (vdr_dm_data->dm_data.level1) { 50 | const DoviExtMetadataBlockLevel1 *l1 = vdr_dm_data->dm_data.level1; 51 | out->max_pq_y = l1->max_pq / 4095.0f; 52 | out->avg_pq_y = l1->avg_pq / 4095.0f; 53 | } 54 | 55 | dovi_rpu_free_vdr_dm_data(vdr_dm_data); 56 | } 57 | 58 | done: 59 | dovi_rpu_free_header(header); 60 | dovi_rpu_free(rpu); 61 | } 62 | #endif 63 | } 64 | -------------------------------------------------------------------------------- /tools/glsl_preproc/variables.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | def slugify(value): 4 | value = re.sub(r'[^\w]+', '_', value.lower()).strip('_') 5 | if value[:1].isdigit(): 6 | value = '_' + value 7 | return value 8 | 9 | # A single variable (enclosed by the template) 10 | class Var(object): 11 | STRUCT_NAME = 'vars' 12 | CSIZES = { 13 | # This array doesn't have to be exact, it's only used for sorting 14 | # struct members to save a few bytes of memory here and there 15 | 'int': 4, 16 | 'unsigned': 4, 17 | 'float': 4, 18 | 'ident_t': 2, 19 | 'uint8_t': 1, 20 | 'bool': 1, 21 | } 22 | 23 | def __init__(self, ctype, expr, name, csize=0, linenr=0): 24 | self.ctype = ctype 25 | self.csize = csize or Var.CSIZES[ctype] 26 | self.expr = expr 27 | self.name = name 28 | self.linenr = linenr 29 | 30 | def __str__(self): 31 | return f'{Var.STRUCT_NAME}.{self.name}' 32 | 33 | def is_literal(expr): 34 | return expr.isnumeric() or expr in ['true', 'false'] 35 | 36 | # A (deduplicated) set of variables 37 | class VarSet(object): 38 | def __init__(self): 39 | self.varmap = {} # expr -> cvar 40 | 41 | def __iter__(self): 42 | # Sort from largest to smallest variable to optimize struct padding 43 | yield from sorted(self.varmap.values(), 44 | reverse=True, 45 | key=lambda v: v.csize, 46 | ) 47 | 48 | def __bool__(self): 49 | return True if self.varmap else False 50 | 51 | def add_var_raw(self, var): 52 | # Re-use existing entry for identical expression/type pairs 53 | if old := self.varmap.get(var.expr): 54 | if var.ctype != old.ctype: 55 | raise SyntaxError(f'Conflicting types for expression {var.expr}, ' 56 | f'got {var.ctype}, expected {old.ctype}') 57 | assert old.name == var.name 58 | return old 59 | 60 | names = [ v.name for v in self.varmap.values() ] 61 | while var.name in names: 62 | var.name += '_' 63 | self.varmap[var.expr] = var 64 | return var 65 | 66 | # Returns the added variable 67 | def add_var(self, ctype, expr, name=None, linenr=0): 68 | assert expr 69 | expr = expr.strip() 70 | if is_literal(expr): 71 | return expr 72 | name = name or slugify(expr) 73 | 74 | var = Var(ctype, expr=expr, name=name, linenr=linenr) 75 | return self.add_var_raw(var) 76 | 77 | def merge(self, other): 78 | for var in other: 79 | self.add_var_raw(var) 80 | -------------------------------------------------------------------------------- /src/shaders/film_grain.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include "common.h" 21 | 22 | #include 23 | 24 | bool pl_needs_fg_av1(const struct pl_film_grain_params *); 25 | bool pl_needs_fg_h274(const struct pl_film_grain_params *); 26 | 27 | bool pl_shader_fg_av1(pl_shader, pl_shader_obj *, const struct pl_film_grain_params *); 28 | bool pl_shader_fg_h274(pl_shader, pl_shader_obj *, const struct pl_film_grain_params *); 29 | 30 | // Common helper function 31 | static inline enum pl_channel channel_map(int i, const struct pl_film_grain_params *params) 32 | { 33 | static const enum pl_channel map_rgb[3] = { 34 | [PL_CHANNEL_G] = PL_CHANNEL_Y, 35 | [PL_CHANNEL_B] = PL_CHANNEL_CB, 36 | [PL_CHANNEL_R] = PL_CHANNEL_CR, 37 | }; 38 | 39 | static const enum pl_channel map_xyz[3] = { 40 | [1] = PL_CHANNEL_Y, // Y 41 | [2] = PL_CHANNEL_CB, // Z 42 | [0] = PL_CHANNEL_CR, // X 43 | }; 44 | 45 | if (i >= params->components) 46 | return PL_CHANNEL_NONE; 47 | 48 | int comp = params->component_mapping[i]; 49 | if (comp < 0 || comp > 2) 50 | return PL_CHANNEL_NONE; 51 | 52 | switch (params->repr->sys) { 53 | case PL_COLOR_SYSTEM_UNKNOWN: 54 | case PL_COLOR_SYSTEM_RGB: 55 | return map_rgb[comp]; 56 | case PL_COLOR_SYSTEM_XYZ: 57 | return map_xyz[comp]; 58 | 59 | case PL_COLOR_SYSTEM_BT_601: 60 | case PL_COLOR_SYSTEM_BT_709: 61 | case PL_COLOR_SYSTEM_SMPTE_240M: 62 | case PL_COLOR_SYSTEM_BT_2020_NC: 63 | case PL_COLOR_SYSTEM_BT_2020_C: 64 | case PL_COLOR_SYSTEM_BT_2100_PQ: 65 | case PL_COLOR_SYSTEM_BT_2100_HLG: 66 | case PL_COLOR_SYSTEM_DOLBYVISION: 67 | case PL_COLOR_SYSTEM_YCGCO: 68 | case PL_COLOR_SYSTEM_YCGCO_RE: 69 | case PL_COLOR_SYSTEM_YCGCO_RO: 70 | return comp; 71 | 72 | case PL_COLOR_SYSTEM_COUNT: 73 | break; 74 | } 75 | 76 | pl_unreachable(); 77 | } 78 | -------------------------------------------------------------------------------- /src/swapchain.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #include "common.h" 19 | #include "log.h" 20 | #include "swapchain.h" 21 | 22 | void pl_swapchain_destroy(pl_swapchain *ptr) 23 | { 24 | pl_swapchain sw = *ptr; 25 | if (!sw) 26 | return; 27 | 28 | const struct pl_sw_fns *impl = PL_PRIV(sw); 29 | impl->destroy(sw); 30 | *ptr = NULL; 31 | } 32 | 33 | int pl_swapchain_latency(pl_swapchain sw) 34 | { 35 | const struct pl_sw_fns *impl = PL_PRIV(sw); 36 | if (!impl->latency) 37 | return 0; 38 | 39 | return impl->latency(sw); 40 | } 41 | 42 | bool pl_swapchain_resize(pl_swapchain sw, int *width, int *height) 43 | { 44 | int dummy[2] = {0}; 45 | width = PL_DEF(width, &dummy[0]); 46 | height = PL_DEF(height, &dummy[1]); 47 | 48 | const struct pl_sw_fns *impl = PL_PRIV(sw); 49 | if (!impl->resize) { 50 | *width = *height = 0; 51 | return true; 52 | } 53 | 54 | return impl->resize(sw, width, height); 55 | } 56 | 57 | void pl_swapchain_colorspace_hint(pl_swapchain sw, const struct pl_color_space *csp) 58 | { 59 | const struct pl_sw_fns *impl = PL_PRIV(sw); 60 | if (!impl->colorspace_hint) 61 | return; 62 | 63 | struct pl_swapchain_colors fix = pl_color_space_srgb; 64 | if (csp) { 65 | fix = *csp; 66 | // Ensure we have valid values set for all the fields 67 | pl_color_space_infer(&fix); 68 | } 69 | 70 | impl->colorspace_hint(sw, &fix); 71 | } 72 | 73 | bool pl_swapchain_start_frame(pl_swapchain sw, 74 | struct pl_swapchain_frame *out_frame) 75 | { 76 | *out_frame = (struct pl_swapchain_frame) {0}; // sanity 77 | 78 | const struct pl_sw_fns *impl = PL_PRIV(sw); 79 | return impl->start_frame(sw, out_frame); 80 | } 81 | 82 | bool pl_swapchain_submit_frame(pl_swapchain sw) 83 | { 84 | const struct pl_sw_fns *impl = PL_PRIV(sw); 85 | return impl->submit_frame(sw); 86 | } 87 | 88 | void pl_swapchain_swap_buffers(pl_swapchain sw) 89 | { 90 | const struct pl_sw_fns *impl = PL_PRIV(sw); 91 | impl->swap_buffers(sw); 92 | } 93 | -------------------------------------------------------------------------------- /src/cache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include "common.h" 21 | #include "hash.h" 22 | 23 | #include 24 | 25 | // Convenience wrapper around pl_cache_set 26 | static inline void pl_cache_str(pl_cache cache, uint64_t key, pl_str *str) 27 | { 28 | pl_cache_set(cache, &(pl_cache_obj) { 29 | .key = key, 30 | .data = pl_steal(NULL, str->buf), 31 | .size = str->len, 32 | .free = pl_free, 33 | }); 34 | *str = (pl_str) {0}; 35 | } 36 | 37 | // Steal and insert a cache object 38 | static inline void pl_cache_steal(pl_cache cache, pl_cache_obj *obj) 39 | { 40 | if (obj->free == pl_free) 41 | obj->data = pl_steal(NULL, obj->data); 42 | pl_cache_set(cache, obj); 43 | } 44 | 45 | // Resize `obj->data` to a given size, re-using allocated buffers where possible 46 | static inline void pl_cache_obj_resize(void *alloc, pl_cache_obj *obj, size_t size) 47 | { 48 | if (obj->free != pl_free) { 49 | if (obj->free) 50 | obj->free(obj->data); 51 | obj->data = pl_alloc(alloc, size); 52 | obj->free = pl_free; 53 | } else if (pl_get_size(obj->data) < size) { 54 | obj->data = pl_steal(alloc, obj->data); 55 | obj->data = pl_realloc(alloc, obj->data, size); 56 | } 57 | obj->size = size; 58 | } 59 | 60 | // Internal list of base seeds for different object types, randomly generated 61 | 62 | enum { 63 | CACHE_KEY_SH_LUT = UINT64_C(0x2206183d320352c6), // sh_lut cache 64 | CACHE_KEY_ICC_3DLUT = UINT64_C(0xff703a6dd8a996f6), // ICC 3dlut 65 | CACHE_KEY_DITHER = UINT64_C(0x6fed75eb6dce86cb), // dither matrix 66 | CACHE_KEY_H274 = UINT64_C(0x2fb9adca04b42c4d), // H.274 film grain DB 67 | CACHE_KEY_GAMUT_LUT = UINT64_C(0x6109e47f15d478b1), // gamut mapping 3DLUT 68 | CACHE_KEY_SPIRV = UINT64_C(0x32352f6605ff60a7), // bare SPIR-V module 69 | CACHE_KEY_VK_PIPE = UINT64_C(0x4bdab2817ad02ad4), // VkPipelineCache 70 | CACHE_KEY_GL_PROG = UINT64_C(0x4274c309f4f0477b), // GL_ARB_get_program_binary 71 | CACHE_KEY_D3D_DXBC = UINT64_C(0x5c9e6f43ec73f787), // DXBC bytecode 72 | }; 73 | -------------------------------------------------------------------------------- /src/tests/lut.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | 6 | static const char *luts[] = { 7 | 8 | "TITLE \"1D LUT example\" \n" 9 | "LUT_1D_SIZE 11 \n" 10 | "# Random comment \n" 11 | "0.0 0.0 0.0 \n" 12 | "0.1 0.1 0.1 \n" 13 | "0.2 0.2 0.2 \n" 14 | "0.3 0.3 0.3 \n" 15 | "0.4 0.4 0.4 \n" 16 | "0.5 0.5 0.5 \n" 17 | "0.6 0.6 0.6 \n" 18 | "0.7 0.7 0.7 \n" 19 | "0.8 0.8 0.8 \n" 20 | "0.9 0.9 0.9 \n" 21 | "0.10 0.10 0.10 \n", 22 | 23 | "LUT_3D_SIZE 3 \n" 24 | "TITLE \"3D LUT example\" \n" 25 | "0.0 0.0 0.0 \n" 26 | "0.5 0.0 0.0 \n" 27 | "1.0 0.0 0.0 \n" 28 | "0.0 0.5 0.0 \n" 29 | "0.5 0.5 0.0 \n" 30 | "1.0 0.5 0.0 \n" 31 | "0.0 1.0 0.0 \n" 32 | "0.5 1.0 0.0 \n" 33 | "1.0 1.0 0.0 \n" 34 | "0.0 0.0 0.5 \n" 35 | "0.5 0.0 0.5 \n" 36 | "1.0 0.0 0.5 \n" 37 | "0.0 0.5 0.5 \n" 38 | "0.5 0.5 0.5 \n" 39 | "1.0 0.5 0.5 \n" 40 | "0.0 1.0 0.5 \n" 41 | "0.5 1.0 0.5 \n" 42 | "1.0 1.0 0.5 \n" 43 | "0.0 0.0 1.0 \n" 44 | "0.5 0.0 1.0 \n" 45 | "1.0 0.0 1.0 \n" 46 | "0.0 0.5 1.0 \n" 47 | "0.5 0.5 1.0 \n" 48 | "1.0 0.5 1.0 \n" 49 | "0.0 1.0 1.0 \n" 50 | "0.5 1.0 1.0 \n" 51 | "1.0 1.0 1.0 \n", 52 | 53 | "LUT_1D_SIZE 3 \n" 54 | "TITLE \"custom domain\" \n" 55 | "DOMAIN_MAX 255 255 255 \n" 56 | "0 0 0 \n" 57 | "128 128 128 \n" 58 | "255 255 255 \n" 59 | 60 | }; 61 | 62 | int main() 63 | { 64 | pl_log log = pl_test_logger(); 65 | pl_gpu gpu = pl_gpu_dummy_create(log, NULL); 66 | pl_shader sh = pl_shader_alloc(log, NULL); 67 | pl_shader_obj obj = NULL; 68 | 69 | for (int i = 0; i < PL_ARRAY_SIZE(luts); i++) { 70 | struct pl_custom_lut *lut; 71 | lut = pl_lut_parse_cube(log, luts[i], strlen(luts[i])); 72 | REQUIRE(lut); 73 | 74 | pl_shader_reset(sh, pl_shader_params( .gpu = gpu )); 75 | pl_shader_custom_lut(sh, lut, &obj); 76 | const struct pl_shader_res *res = pl_shader_finalize(sh); 77 | REQUIRE(res); 78 | printf("Generated LUT shader:\n%s\n", res->glsl); 79 | pl_lut_free(&lut); 80 | } 81 | 82 | pl_shader_obj_destroy(&obj); 83 | pl_shader_free(&sh); 84 | pl_gpu_dummy_destroy(&gpu); 85 | pl_log_destroy(&log); 86 | } 87 | -------------------------------------------------------------------------------- /src/pl_clock.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | 23 | #include "os.h" 24 | 25 | #ifdef PL_HAVE_WIN32 26 | # include 27 | # define PL_CLOCK_QPC 28 | #elif defined(PL_HAVE_APPLE) 29 | # include 30 | # if (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200) || \ 31 | (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 100000) || \ 32 | (defined(__TV_OS_VERSION_MIN_REQUIRED) && __TV_OS_VERSION_MIN_REQUIRED < 100000) || \ 33 | (defined(__WATCH_OS_VERSION_MIN_REQUIRED) && __WATCH_OS_VERSION_MIN_REQUIRED < 30000) || \ 34 | !defined(CLOCK_MONOTONIC_RAW) 35 | # include 36 | # define PL_CLOCK_MACH 37 | # else 38 | # define PL_CLOCK_MONOTONIC_RAW 39 | # endif 40 | #elif defined(CLOCK_MONOTONIC_RAW) 41 | # define PL_CLOCK_MONOTONIC_RAW 42 | #elif defined(TIME_UTC) 43 | # define PL_CLOCK_TIMESPEC_GET 44 | #else 45 | # warning "pl_clock not implemented for this platform!" 46 | #endif 47 | 48 | typedef uint64_t pl_clock_t; 49 | 50 | static inline pl_clock_t pl_clock_now(void) 51 | { 52 | #if defined(PL_CLOCK_QPC) 53 | 54 | LARGE_INTEGER counter; 55 | QueryPerformanceCounter(&counter); 56 | return counter.QuadPart; 57 | 58 | #elif defined(PL_CLOCK_MACH) 59 | 60 | return mach_absolute_time(); 61 | 62 | #else 63 | 64 | struct timespec tp = { .tv_sec = 0, .tv_nsec = 0 }; 65 | #if defined(PL_CLOCK_MONOTONIC_RAW) 66 | clock_gettime(CLOCK_MONOTONIC_RAW, &tp); 67 | #elif defined(PL_CLOCK_TIMESPEC_GET) 68 | timespec_get(&tp, TIME_UTC); 69 | #endif 70 | return tp.tv_sec * UINT64_C(1000000000) + tp.tv_nsec; 71 | 72 | #endif 73 | } 74 | 75 | static inline double pl_clock_diff(pl_clock_t a, pl_clock_t b) 76 | { 77 | double frequency = 1e9; 78 | 79 | #if defined(PL_CLOCK_QPC) 80 | 81 | LARGE_INTEGER freq; 82 | QueryPerformanceFrequency(&freq); 83 | frequency = freq.QuadPart; 84 | 85 | #elif defined(PL_CLOCK_MACH) 86 | 87 | mach_timebase_info_data_t time_base; 88 | if (mach_timebase_info(&time_base) != KERN_SUCCESS) 89 | return 0; 90 | frequency = (time_base.denom * 1e9) / time_base.numer; 91 | 92 | #endif 93 | 94 | if (b > a) 95 | return (b - a) / -frequency; 96 | else 97 | return (a - b) / frequency; 98 | } 99 | -------------------------------------------------------------------------------- /src/vulkan/malloc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include "common.h" 21 | 22 | // All memory allocated from a vk_malloc MUST be explicitly released by 23 | // the caller before vk_malloc_destroy is called. 24 | struct vk_malloc *vk_malloc_create(struct vk_ctx *vk); 25 | void vk_malloc_destroy(struct vk_malloc **ma); 26 | 27 | // Get the supported handle types for this malloc instance 28 | pl_handle_caps vk_malloc_handle_caps(const struct vk_malloc *ma, bool import); 29 | 30 | // Represents a single "slice" of generic (non-buffer) memory, plus some 31 | // metadata for accounting. This struct is essentially read-only. 32 | struct vk_memslice { 33 | VkDeviceMemory vkmem; 34 | VkDeviceSize offset; 35 | VkDeviceSize size; 36 | void *priv; 37 | // depending on the type/flags: 38 | struct pl_shared_mem shared_mem; 39 | VkBuffer buf; // associated buffer (when `buf_usage` is nonzero) 40 | void *data; // pointer to slice (for persistently mapped slices) 41 | bool coherent; // whether `data` is coherent 42 | VkDeviceSize map_offset; // can be larger than offset/size 43 | VkDeviceSize map_size; 44 | }; 45 | 46 | struct vk_malloc_params { 47 | VkMemoryRequirements reqs; 48 | VkMemoryPropertyFlags required; 49 | VkMemoryPropertyFlags optimal; 50 | VkBufferUsageFlags buf_usage; 51 | VkImage ded_image; // for dedicated image allocations 52 | enum pl_handle_type export_handle; 53 | enum pl_handle_type import_handle; 54 | struct pl_shared_mem shared_mem; // for `import_handle` 55 | pl_debug_tag debug_tag; 56 | }; 57 | 58 | // Returns the amount of available memory matching a given set of property 59 | // flags. Always returns the highest single allocation, not the combined total. 60 | size_t vk_malloc_avail(struct vk_malloc *ma, VkMemoryPropertyFlags flags); 61 | 62 | bool vk_malloc_slice(struct vk_malloc *ma, struct vk_memslice *out, 63 | const struct vk_malloc_params *params); 64 | 65 | void vk_malloc_free(struct vk_malloc *ma, struct vk_memslice *slice); 66 | 67 | // Clean up unused slabs. Call this roughly once per frame to reduce 68 | // memory pressure / memory leaks. 69 | void vk_malloc_garbage_collect(struct vk_malloc *ma); 70 | 71 | // For debugging purposes. Doesn't include dedicated slab allocations! 72 | void vk_malloc_print_stats(struct vk_malloc *ma, enum pl_log_level); 73 | -------------------------------------------------------------------------------- /src/tests/filters.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | 5 | int main() 6 | { 7 | pl_log log = pl_test_logger(); 8 | 9 | for (int i = 0; i < pl_num_filter_functions; i++) { 10 | const struct pl_filter_function *fun = pl_filter_functions[i]; 11 | if (fun->opaque) 12 | continue; 13 | 14 | printf("Testing filter function '%s'\n", fun->name); 15 | 16 | struct pl_filter_ctx ctx = { .radius = fun->radius }; 17 | memcpy(ctx.params, fun->params, sizeof(ctx.params)); 18 | 19 | // Ensure the kernel is correctly scaled 20 | REQUIRE_FEQ(fun->weight(&ctx, 0.0), 1.0, 1e-7); 21 | 22 | // Only box filters are radius 1, these are unwindowed by design. 23 | // Gaussian technically never reaches 0 even at its preconfigured radius. 24 | if (fun->radius > 1.0 && fun != &pl_filter_function_gaussian) 25 | REQUIRE_FEQ(fun->weight(&ctx, fun->radius), 0.0, 1e-7); 26 | } 27 | 28 | for (int c = 0; c < pl_num_filter_configs; c++) { 29 | const struct pl_filter_config *conf = pl_filter_configs[c]; 30 | if (conf->kernel->opaque) 31 | continue; 32 | 33 | printf("Testing filter config '%s'\n", conf->name); 34 | pl_filter flt = pl_filter_generate(log, pl_filter_params( 35 | .config = *conf, 36 | .lut_entries = 256, 37 | .cutoff = 1e-3, 38 | )); 39 | REQUIRE(flt); 40 | const float radius = PL_DEF(conf->radius, conf->kernel->radius); 41 | REQUIRE_CMP(flt->radius, <=, radius, "f"); 42 | REQUIRE_CMP(flt->radius_zero, >, 0.0, "f"); 43 | REQUIRE_CMP(flt->radius_zero, <=, flt->radius, "f"); 44 | 45 | if (conf->polar) { 46 | 47 | // Test LUT accuracy 48 | const int range = flt->params.lut_entries - 1; 49 | double scale = flt->weights[0] / pl_filter_sample(conf, 0.0); 50 | double err = 0.0; 51 | for (float k = 0.0; k <= 1.0; k += 1e-3f) { 52 | double ref = scale * pl_filter_sample(conf, k * flt->radius); 53 | double idx = k * range; 54 | int base = floorf(idx); 55 | double fpart = idx - base; 56 | int next = PL_MIN(base + 1, range); 57 | double interp = PL_MIX(flt->weights[base], flt->weights[next], fpart); 58 | err = fmaxf(err, fabs(interp - ref)); 59 | } 60 | REQUIRE_CMP(err, <=, 1e-4, "g"); 61 | 62 | } else { 63 | 64 | // Ensure the weights for each row add up to unity 65 | for (int i = 0; i < flt->params.lut_entries; i++) { 66 | const float *row = flt->weights + i * flt->row_stride; 67 | float sum = 0.0; 68 | REQUIRE(flt->row_size); 69 | REQUIRE_CMP(flt->row_stride, >=, flt->row_size, "d"); 70 | for (int n = 0; n < flt->row_size; n++) 71 | sum += row[n]; 72 | REQUIRE_FEQ(sum, 1.0, 1e-6); 73 | } 74 | 75 | } 76 | 77 | pl_filter_free(&flt); 78 | } 79 | 80 | pl_log_destroy(&log); 81 | } 82 | -------------------------------------------------------------------------------- /src/include/libplacebo/config.h.in: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #ifndef LIBPLACEBO_CONFIG_H_ 19 | #define LIBPLACEBO_CONFIG_H_ 20 | 21 | // Increased any time the library changes in a fundamental/major way. 22 | #define PL_MAJOR_VER @majorver@ 23 | 24 | // Increased any time the API changes. (Note: Does not reset when PL_MAJOR_VER 25 | // is increased) 26 | #define PL_API_VER @apiver@ 27 | 28 | // Increased any time a fix is made to a given API version. 29 | #define PL_FIX_VER (pl_fix_ver()) 30 | 31 | // Friendly name (`git describe`) for the overall version of the library 32 | #define PL_VERSION (pl_version()) 33 | 34 | // Feature tests. These aren't described in further detail, but may be useful 35 | // for programmers wanting to programmatically check for feature support 36 | // in their compiled libplacebo versions. 37 | @extra_defs@ 38 | 39 | // Extra compiler-specific stuff 40 | #ifndef PL_DEPRECATED_IN 41 | # if defined(_MSC_VER) 42 | # define PL_DEPRECATED_IN(VER) 43 | # else 44 | # define PL_DEPRECATED_IN(VER) __attribute__((deprecated)) 45 | # endif 46 | #endif 47 | 48 | #ifndef __has_feature 49 | #define __has_feature(x) 0 50 | #endif 51 | 52 | #ifndef PL_DEPRECATED_ENUM_IN 53 | # if (defined(__GNUC__) && (__GNUC__ >= 6)) || __has_feature(enumerator_attributes) 54 | # define PL_DEPRECATED_ENUM_IN(VER) PL_DEPRECATED_IN(VER) 55 | # else 56 | # define PL_DEPRECATED_ENUM_IN(VER) 57 | # endif 58 | #endif 59 | 60 | #if defined(_WIN32) || defined(__CYGWIN__) 61 | # ifdef PL_EXPORT 62 | # define PL_API __declspec(dllexport) 63 | # else 64 | # ifndef PL_STATIC 65 | # define PL_API __declspec(dllimport) 66 | # else 67 | # define PL_API 68 | # endif 69 | # endif 70 | #else 71 | # define PL_API __attribute__ ((visibility ("default"))) 72 | #endif 73 | 74 | // C++ compatibility 75 | #ifdef __cplusplus 76 | # define PL_API_BEGIN extern "C" { 77 | # define PL_API_END } 78 | #else 79 | # define PL_API_BEGIN 80 | # define PL_API_END 81 | #endif 82 | 83 | #ifndef __cplusplus 84 | // Disable this warning because libplacebo's params macros override fields 85 | # pragma GCC diagnostic ignored "-Woverride-init" 86 | #endif 87 | 88 | // Extra helper macros 89 | #define PL_TOSTRING_INNER(x) #x 90 | #define PL_TOSTRING(x) PL_TOSTRING_INNER(x) 91 | 92 | // Deprecated macro for back-compatibility 93 | #define PL_STRUCT(name) struct name##_t 94 | 95 | PL_API_BEGIN 96 | 97 | PL_API int pl_fix_ver(void); 98 | PL_API const char *pl_version(void); 99 | 100 | PL_API_END 101 | 102 | #endif // LIBPLACEBO_CONFIG_H_ 103 | -------------------------------------------------------------------------------- /src/shaders/custom.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #include "shaders.h" 19 | 20 | #include 21 | 22 | bool pl_shader_custom(pl_shader sh, const struct pl_custom_shader *params) 23 | { 24 | if (params->compute) { 25 | int bw = PL_DEF(params->compute_group_size[0], 16); 26 | int bh = PL_DEF(params->compute_group_size[1], 16); 27 | bool flex = !params->compute_group_size[0] || 28 | !params->compute_group_size[1]; 29 | if (!sh_try_compute(sh, bw, bh, flex, params->compute_shmem)) 30 | return false; 31 | } 32 | 33 | if (!sh_require(sh, params->input, params->output_w, params->output_h)) 34 | return false; 35 | 36 | sh->output = params->output; 37 | 38 | for (int i = 0; i < params->num_variables; i++) { 39 | struct pl_shader_var sv = params->variables[i]; 40 | GLSLP("#define %s "$"\n", sv.var.name, sh_var(sh, sv)); 41 | } 42 | 43 | for (int i = 0; i < params->num_descriptors; i++) { 44 | struct pl_shader_desc sd = params->descriptors[i]; 45 | GLSLP("#define %s "$"\n", sd.desc.name, sh_desc(sh, sd)); 46 | } 47 | 48 | for (int i = 0; i < params->num_vertex_attribs; i++) { 49 | struct pl_shader_va sva = params->vertex_attribs[i]; 50 | GLSLP("#define %s "$"\n", sva.attr.name, sh_attr(sh, sva)); 51 | } 52 | 53 | for (int i = 0; i < params->num_constants; i++) { 54 | struct pl_shader_const sc = params->constants[i]; 55 | GLSLP("#define %s "$"\n", sc.name, sh_const(sh, sc)); 56 | } 57 | 58 | if (params->prelude) 59 | GLSLP("// pl_shader_custom prelude: \n%s\n", params->prelude); 60 | if (params->header) 61 | GLSLH("// pl_shader_custom header: \n%s\n", params->header); 62 | 63 | if (params->description) 64 | sh_describef(sh, "%s", params->description); 65 | 66 | if (params->body) { 67 | const char *output_decl = ""; 68 | if (params->output != params->input) { 69 | switch (params->output) { 70 | case PL_SHADER_SIG_NONE: break; 71 | case PL_SHADER_SIG_COLOR: 72 | output_decl = "vec4 color = vec4(0.0);"; 73 | break; 74 | 75 | case PL_SHADER_SIG_SAMPLER: 76 | pl_unreachable(); 77 | } 78 | } 79 | 80 | GLSL("// pl_shader_custom \n" 81 | "%s \n" 82 | "{ \n" 83 | "%s \n" 84 | "} \n", 85 | output_decl, params->body); 86 | } 87 | 88 | return true; 89 | } 90 | -------------------------------------------------------------------------------- /src/vulkan/stubs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #include "../common.h" 19 | #include "log.h" 20 | 21 | #include 22 | 23 | const struct pl_vk_inst_params pl_vk_inst_default_params = {0}; 24 | const struct pl_vulkan_params pl_vulkan_default_params = { PL_VULKAN_DEFAULTS }; 25 | 26 | pl_vk_inst pl_vk_inst_create(pl_log log, const struct pl_vk_inst_params *params) 27 | { 28 | pl_fatal(log, "libplacebo compiled without Vulkan support!"); 29 | return NULL; 30 | } 31 | 32 | void pl_vk_inst_destroy(pl_vk_inst *pinst) 33 | { 34 | pl_vk_inst inst = *pinst; 35 | pl_assert(!inst); 36 | } 37 | 38 | pl_vulkan pl_vulkan_create(pl_log log, const struct pl_vulkan_params *params) 39 | { 40 | pl_fatal(log, "libplacebo compiled without Vulkan support!"); 41 | return NULL; 42 | } 43 | 44 | void pl_vulkan_destroy(pl_vulkan *pvk) 45 | { 46 | pl_vulkan vk = *pvk; 47 | pl_assert(!vk); 48 | } 49 | 50 | pl_vulkan pl_vulkan_get(pl_gpu gpu) 51 | { 52 | return NULL; 53 | } 54 | 55 | VkPhysicalDevice pl_vulkan_choose_device(pl_log log, 56 | const struct pl_vulkan_device_params *params) 57 | { 58 | pl_err(log, "libplacebo compiled without Vulkan support!"); 59 | return NULL; 60 | } 61 | 62 | pl_swapchain pl_vulkan_create_swapchain(pl_vulkan vk, 63 | const struct pl_vulkan_swapchain_params *params) 64 | { 65 | pl_unreachable(); 66 | } 67 | 68 | bool pl_vulkan_swapchain_suboptimal(pl_swapchain sw) 69 | { 70 | pl_unreachable(); 71 | } 72 | 73 | pl_vulkan pl_vulkan_import(pl_log log, const struct pl_vulkan_import_params *params) 74 | { 75 | pl_fatal(log, "libplacebo compiled without Vulkan support!"); 76 | return NULL; 77 | } 78 | 79 | pl_tex pl_vulkan_wrap(pl_gpu gpu, const struct pl_vulkan_wrap_params *params) 80 | { 81 | pl_unreachable(); 82 | } 83 | 84 | VkImage pl_vulkan_unwrap(pl_gpu gpu, pl_tex tex, 85 | VkFormat *out_format, VkImageUsageFlags *out_flags) 86 | { 87 | pl_unreachable(); 88 | } 89 | 90 | bool pl_vulkan_hold_ex(pl_gpu gpu, const struct pl_vulkan_hold_params *params) 91 | { 92 | pl_unreachable(); 93 | } 94 | 95 | void pl_vulkan_release_ex(pl_gpu gpu, const struct pl_vulkan_release_params *params) 96 | { 97 | pl_unreachable(); 98 | } 99 | 100 | VkSemaphore pl_vulkan_sem_create(pl_gpu gpu, const struct pl_vulkan_sem_params *params) 101 | { 102 | pl_unreachable(); 103 | } 104 | 105 | void pl_vulkan_sem_destroy(pl_gpu gpu, VkSemaphore *semaphore) 106 | { 107 | pl_unreachable(); 108 | } 109 | -------------------------------------------------------------------------------- /demos/window.c: -------------------------------------------------------------------------------- 1 | // License: CC0 / Public Domain 2 | 3 | #include 4 | 5 | #include "common.h" 6 | #include "window.h" 7 | 8 | #ifdef _WIN32 9 | #include 10 | #include 11 | #endif 12 | 13 | extern const struct window_impl win_impl_glfw_vk; 14 | extern const struct window_impl win_impl_glfw_gl; 15 | extern const struct window_impl win_impl_glfw_d3d11; 16 | extern const struct window_impl win_impl_sdl_vk; 17 | extern const struct window_impl win_impl_sdl_gl; 18 | 19 | static const struct window_impl *win_impls[] = { 20 | #ifdef HAVE_GLFW_VULKAN 21 | &win_impl_glfw_vk, 22 | #endif 23 | #ifdef HAVE_GLFW_OPENGL 24 | &win_impl_glfw_gl, 25 | #endif 26 | #ifdef HAVE_GLFW_D3D11 27 | &win_impl_glfw_d3d11, 28 | #endif 29 | #ifdef HAVE_SDL_VULKAN 30 | &win_impl_sdl_vk, 31 | #endif 32 | #ifdef HAVE_SDL_OPENGL 33 | &win_impl_sdl_gl, 34 | #endif 35 | NULL 36 | }; 37 | 38 | struct window *window_create(pl_log log, const struct window_params *params) 39 | { 40 | for (const struct window_impl **impl = win_impls; *impl; impl++) { 41 | if (params->forced_impl && strcmp((*impl)->tag, params->forced_impl) != 0) 42 | continue; 43 | 44 | printf("Attempting to initialize API: %s\n", (*impl)->name); 45 | struct window *win = (*impl)->create(log, params); 46 | if (win) { 47 | #ifdef _WIN32 48 | if (timeBeginPeriod(1) != TIMERR_NOERROR) 49 | fprintf(stderr, "timeBeginPeriod failed!\n"); 50 | #endif 51 | return win; 52 | } 53 | } 54 | 55 | if (params->forced_impl) 56 | fprintf(stderr, "'%s' windowing system not compiled or supported!\n", params->forced_impl); 57 | else 58 | fprintf(stderr, "No windowing system / graphical API compiled or supported!\n"); 59 | 60 | exit(1); 61 | } 62 | 63 | void window_destroy(struct window **win) 64 | { 65 | if (!*win) 66 | return; 67 | 68 | (*win)->impl->destroy(win); 69 | 70 | #ifdef _WIN32 71 | timeEndPeriod(1); 72 | #endif 73 | } 74 | 75 | void window_poll(struct window *win, bool block) 76 | { 77 | return win->impl->poll(win, block); 78 | } 79 | 80 | void window_get_cursor(const struct window *win, int *x, int *y) 81 | { 82 | return win->impl->get_cursor(win, x, y); 83 | } 84 | 85 | void window_get_scroll(const struct window *win, float *dx, float *dy) 86 | { 87 | return win->impl->get_scroll(win, dx, dy); 88 | } 89 | 90 | bool window_get_button(const struct window *win, enum button btn) 91 | { 92 | return win->impl->get_button(win, btn); 93 | } 94 | 95 | bool window_get_key(const struct window *win, enum key key) 96 | { 97 | return win->impl->get_key(win, key); 98 | } 99 | 100 | char *window_get_file(const struct window *win) 101 | { 102 | return win->impl->get_file(win); 103 | } 104 | 105 | bool window_toggle_fullscreen(const struct window *win, bool fullscreen) 106 | { 107 | return win->impl->toggle_fullscreen(win, fullscreen); 108 | } 109 | 110 | bool window_is_fullscreen(const struct window *win) 111 | { 112 | return win->impl->is_fullscreen(win); 113 | } 114 | 115 | const char *window_get_clipboard(const struct window *win) 116 | { 117 | return win->impl->get_clipboard(win); 118 | } 119 | 120 | void window_set_clipboard(const struct window *win, const char *text) 121 | { 122 | win->impl->set_clipboard(win, text); 123 | } 124 | -------------------------------------------------------------------------------- /src/glsl/meson.build: -------------------------------------------------------------------------------- 1 | # shaderc 2 | shaderc = dependency('shaderc', version: '>=2019.1', required: get_option('shaderc')) 3 | components.set('shaderc', shaderc.found()) 4 | if shaderc.found() 5 | build_deps += shaderc 6 | sources += 'glsl/spirv_shaderc.c' 7 | 8 | # Version check for shaderc is not possible because everything after v2023.8 9 | # uses this version due to a malformed version line. 10 | # See https://github.com/google/shaderc/issues/1496 11 | if shaderc.type_name() == 'internal' 12 | conf_internal.set('PL_HAVE_SHADERC_VK_1_4', true) 13 | conf_internal.set('PL_HAVE_SHADERC_VK_1_3', true) 14 | else 15 | conf_internal.set('PL_HAVE_SHADERC_VK_1_4', 16 | cc.has_header_symbol('shaderc/shaderc.h', 'shaderc_env_version_vulkan_1_4', dependencies: shaderc)) 17 | conf_internal.set('PL_HAVE_SHADERC_VK_1_3', 18 | cc.has_header_symbol('shaderc/shaderc.h', 'shaderc_env_version_vulkan_1_3', dependencies: shaderc)) 19 | endif 20 | endif 21 | 22 | # glslang 23 | glslang = disabler() 24 | glslang_req = get_option('glslang') 25 | if glslang_req.auto() and shaderc.found() 26 | 27 | # we only need one or the other, and shaderc is preferred 28 | message('Skipping `glslang` because `shaderc` is available') 29 | 30 | elif not glslang_req.disabled() 31 | 32 | glslang_deps = [ 33 | cxx.find_library('glslang-default-resource-limits', required: false) 34 | ] 35 | 36 | # meson doesn't respect generator expressions in INTERFACE_LINK_LIBRARIES 37 | # https://github.com/mesonbuild/meson/issues/8232 38 | # TODO: Use the following once it's fixed 39 | # glslang = dependency('glslang', method: 'cmake', modules: ['glslang::SPIRV']) 40 | 41 | prefer_static = get_option('prefer_static') 42 | found_lib = false 43 | foreach arg : [[prefer_static, false], [not prefer_static, glslang_req]] 44 | static = arg[0] 45 | required = arg[1] 46 | 47 | spirv = cxx.find_library('SPIRV', required: required, static: static) 48 | 49 | if not spirv.found() 50 | continue 51 | endif 52 | 53 | glslang_deps += spirv 54 | 55 | # Glslang 15.0.0 moved some code around, add also linking to glslang, while 56 | # this is not needed for older versions, it will still work. 57 | glslang_deps += cxx.find_library('glslang', required: required, static: static) 58 | 59 | if static 60 | glslang_deps += [ 61 | # Always required for static linking 62 | cxx.find_library('MachineIndependent', required: false, static: true), 63 | cxx.find_library('OSDependent', required: false, static: true), 64 | cxx.find_library('OGLCompiler', required: false, static: true), 65 | cxx.find_library('GenericCodeGen', required: false, static: true), 66 | # SPIRV-Tools are required only if optimizer is enabled in glslang build 67 | cxx.find_library('SPIRV-Tools', required: false, static: true), 68 | cxx.find_library('SPIRV-Tools-opt', required: false, static: true), 69 | ] 70 | endif 71 | 72 | found_lib = true 73 | break 74 | endforeach 75 | 76 | if found_lib and cc.has_header('glslang/build_info.h') 77 | glslang = declare_dependency(dependencies: glslang_deps) 78 | endif 79 | 80 | endif 81 | 82 | components.set('glslang', glslang.found()) 83 | if glslang.found() 84 | build_deps += glslang 85 | sources += [ 86 | 'glsl/glslang.cc', 87 | 'glsl/glslang_resources.c', 88 | 'glsl/spirv_glslang.c', 89 | ] 90 | endif 91 | -------------------------------------------------------------------------------- /src/include/libplacebo/shaders/lut.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #ifndef LIBPLACEBO_SHADERS_LUT_H_ 19 | #define LIBPLACEBO_SHADERS_LUT_H_ 20 | 21 | // Shaders for loading and applying arbitrary custom 1D/3DLUTs 22 | 23 | #include 24 | #include 25 | 26 | PL_API_BEGIN 27 | 28 | // Struct defining custom LUTs 29 | // 30 | // Note: Users may freely create their own instances of this struct, there is 31 | // nothing particularly special about `pl_lut_parse_cube`. 32 | struct pl_custom_lut { 33 | // Some unique signature identifying this LUT, needed to detect state 34 | // changes (for cache invalidation). This should ideally be a hash of the 35 | // file contents. (Which is what `pl_lut_parse_*` will set it to.) 36 | uint64_t signature; 37 | 38 | // Size of each dimension, in the order R, G, B. For 1D LUTs, only the R 39 | // dimension should be specified (the others left as 0). 40 | int size[3]; 41 | 42 | // Raw LUT data itself, in properly scaled floating point format. For 3D 43 | // LUTs, the innermost dimension is the first dimension (R), and the 44 | // outermost dimension is the last dimension (B). Individual color samples 45 | // are in the order R, G, B. 46 | const float *data; 47 | 48 | // Extra input/output shaper matrices. Ignored if equal to {0}. This is 49 | // mostly useful for 1D LUTs, since 3D LUTs can bake the shaper matrix into 50 | // the LUT itself - but it can still help optimize LUT precision. 51 | pl_matrix3x3 shaper_in, shaper_out; 52 | 53 | // Nominal metadata for the input/output of a LUT. Left as {0} if unknown. 54 | // Note: This is purely informative, `pl_shader_custom_lut` ignores it. 55 | struct pl_color_repr repr_in, repr_out; 56 | struct pl_color_space color_in, color_out; 57 | }; 58 | 59 | // Parse a 3DLUT in .cube format. Returns NULL if the file fails parsing. 60 | PL_API struct pl_custom_lut *pl_lut_parse_cube(pl_log log, const char *str, size_t str_len); 61 | 62 | // Frees a LUT created by `pl_lut_parse_*`. 63 | PL_API void pl_lut_free(struct pl_custom_lut **lut); 64 | 65 | // Apply a `pl_custom_lut`. The user is responsible for ensuring colors going 66 | // into the LUT are in the expected format as informed by the LUT metadata. 67 | // 68 | // `lut_state` must be a pointer to a NULL-initialized shader state object that 69 | // will be used to encapsulate any required GPU state. 70 | // 71 | // Note: `lut` does not have to be allocated by `pl_lut_parse_*`. It can be a 72 | // struct filled out by the user. 73 | PL_API void pl_shader_custom_lut(pl_shader sh, const struct pl_custom_lut *lut, 74 | pl_shader_obj *lut_state); 75 | 76 | PL_API_END 77 | 78 | #endif // LIBPLACEBO_SHADERS_LUT_H_ 79 | -------------------------------------------------------------------------------- /src/filters.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | 22 | static inline float pl_filter_radius_bound(const struct pl_filter_config *c) 23 | { 24 | const float r = c->radius && c->kernel->resizable ? c->radius : c->kernel->radius; 25 | return c->blur > 0.0 ? r * c->blur : r; 26 | } 27 | 28 | #define COMMON_FILTER_PRESETS \ 29 | /* Highest priority / recommended filters */ \ 30 | {"bilinear", &pl_filter_bilinear, "Bilinear"}, \ 31 | {"nearest", &pl_filter_nearest, "Nearest neighbour"}, \ 32 | {"bicubic", &pl_filter_bicubic, "Bicubic"}, \ 33 | {"lanczos", &pl_filter_lanczos, "Lanczos"}, \ 34 | {"ewa_lanczos", &pl_filter_ewa_lanczos, "Jinc (EWA Lanczos)"}, \ 35 | {"ewa_lanczossharp", &pl_filter_ewa_lanczossharp, "Sharpened Jinc"}, \ 36 | {"ewa_lanczos4sharpest",&pl_filter_ewa_lanczos4sharpest, "Sharpened Jinc-AR, 4 taps"},\ 37 | {"gaussian", &pl_filter_gaussian, "Gaussian"}, \ 38 | {"spline16", &pl_filter_spline16, "Spline (2 taps)"}, \ 39 | {"spline36", &pl_filter_spline36, "Spline (3 taps)"}, \ 40 | {"spline64", &pl_filter_spline64, "Spline (4 taps)"}, \ 41 | {"mitchell", &pl_filter_mitchell, "Mitchell-Netravali"}, \ 42 | \ 43 | /* Remaining filters */ \ 44 | {"sinc", &pl_filter_sinc, "Sinc (unwindowed)"}, \ 45 | {"ginseng", &pl_filter_ginseng, "Ginseng (Jinc-Sinc)"}, \ 46 | {"ewa_jinc", &pl_filter_ewa_jinc, "EWA Jinc (unwindowed)"}, \ 47 | {"ewa_ginseng", &pl_filter_ewa_ginseng, "EWA Ginseng"}, \ 48 | {"ewa_hann", &pl_filter_ewa_hann, "EWA Hann"}, \ 49 | {"hermite", &pl_filter_hermite, "Hermite"}, \ 50 | {"catmull_rom", &pl_filter_catmull_rom, "Catmull-Rom"}, \ 51 | {"robidoux", &pl_filter_robidoux, "Robidoux"}, \ 52 | {"robidouxsharp", &pl_filter_robidouxsharp, "RobidouxSharp"}, \ 53 | {"ewa_robidoux", &pl_filter_ewa_robidoux, "EWA Robidoux"}, \ 54 | {"ewa_robidouxsharp", &pl_filter_ewa_robidouxsharp, "EWA RobidouxSharp"}, \ 55 | \ 56 | /* Aliases */ \ 57 | {"triangle", &pl_filter_bilinear}, \ 58 | {"ewa_hanning", &pl_filter_ewa_hann} 59 | -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | 22 | #include "common.h" 23 | 24 | #include 25 | 26 | // Internal logging-related functions 27 | 28 | // Warning: Not entirely thread-safe. Exercise caution when using. May result 29 | // in either false positives or false negatives. Make sure to re-run this 30 | // function while `lock` is held, to ensure no race conditions on the check. 31 | static inline bool pl_msg_test(pl_log log, enum pl_log_level lev) 32 | { 33 | return log && log->params.log_cb && log->params.log_level >= lev; 34 | } 35 | 36 | void pl_msg(pl_log log, enum pl_log_level lev, const char *fmt, ...) 37 | PL_PRINTF(3, 4); 38 | 39 | // Convenience macros 40 | #define pl_fatal(log, ...) pl_msg(log, PL_LOG_FATAL, __VA_ARGS__) 41 | #define pl_err(log, ...) pl_msg(log, PL_LOG_ERR, __VA_ARGS__) 42 | #define pl_warn(log, ...) pl_msg(log, PL_LOG_WARN, __VA_ARGS__) 43 | #define pl_info(log, ...) pl_msg(log, PL_LOG_INFO, __VA_ARGS__) 44 | #define pl_debug(log, ...) pl_msg(log, PL_LOG_DEBUG, __VA_ARGS__) 45 | #define pl_trace(log, ...) pl_msg(log, PL_LOG_TRACE, __VA_ARGS__) 46 | 47 | #define PL_MSG(obj, lev, ...) pl_msg((obj)->log, lev, __VA_ARGS__) 48 | 49 | #define PL_FATAL(obj, ...) PL_MSG(obj, PL_LOG_FATAL, __VA_ARGS__) 50 | #define PL_ERR(obj, ...) PL_MSG(obj, PL_LOG_ERR, __VA_ARGS__) 51 | #define PL_WARN(obj, ...) PL_MSG(obj, PL_LOG_WARN, __VA_ARGS__) 52 | #define PL_INFO(obj, ...) PL_MSG(obj, PL_LOG_INFO, __VA_ARGS__) 53 | #define PL_DEBUG(obj, ...) PL_MSG(obj, PL_LOG_DEBUG, __VA_ARGS__) 54 | #define PL_TRACE(obj, ...) PL_MSG(obj, PL_LOG_TRACE, __VA_ARGS__) 55 | 56 | // Log something with line numbers included 57 | void pl_msg_source(pl_log log, enum pl_log_level lev, const char *src); 58 | 59 | // Temporarily cap the log level to a certain verbosity. This is intended for 60 | // things like probing formats, attempting to create buffers that may fail, and 61 | // other types of operations in which we want to suppress errors. Call with 62 | // PL_LOG_NONE to disable this cap. 63 | // 64 | // Warning: This is generally not thread-safe, and only provided as a temporary 65 | // hack until a better solution can be thought of. 66 | void pl_log_level_cap(pl_log log, enum pl_log_level cap); 67 | 68 | // CPU execution time reporting helper 69 | static inline void pl_log_cpu_time(pl_log log, pl_clock_t start, pl_clock_t stop, 70 | const char *operation) 71 | { 72 | double ms = pl_clock_diff(stop, start) * 1e3; 73 | enum pl_log_level lev = PL_LOG_DEBUG; 74 | if (ms > 10) 75 | lev = PL_LOG_INFO; 76 | if (ms > 1000) 77 | lev = PL_LOG_WARN; 78 | 79 | pl_msg(log, lev, "Spent %.3f ms %s%s", ms, operation, 80 | ms > 100 ? " (slow!)" : ""); 81 | } 82 | 83 | // Log stack trace 84 | PL_NOINLINE void pl_log_stack_trace(pl_log log, enum pl_log_level lev); 85 | -------------------------------------------------------------------------------- /src/ucrt_math.def: -------------------------------------------------------------------------------- 1 | LIBRARY api-ms-win-crt-math-l1-1-0 2 | EXPORTS 3 | _Cbuild 4 | _Cmulcc 5 | _Cmulcr 6 | _FCbuild 7 | _FCmulcc 8 | _FCmulcr 9 | _LCbuild 10 | _LCmulcc 11 | _LCmulcr 12 | __setusermatherr 13 | _cabs 14 | _chgsign 15 | _chgsignf 16 | _copysign 17 | _copysignf 18 | _d_int 19 | _dclass 20 | _dexp 21 | _dlog 22 | _dnorm 23 | _dpcomp 24 | _dpoly 25 | _dscale 26 | _dsign 27 | _dsin 28 | _dtest 29 | _dunscale 30 | _except1 31 | _fd_int 32 | _fdclass 33 | _fdexp 34 | _fdlog 35 | _fdnorm 36 | _fdopen 37 | _fdpcomp 38 | _fdpoly 39 | _fdscale 40 | _fdsign 41 | _fdsin 42 | _fdtest 43 | _fdunscale 44 | _finite 45 | _finitef 46 | _fpclass 47 | _fpclassf 48 | _get_FMA3_enable 49 | _hypot 50 | _hypotf 51 | _isnan 52 | _isnanf 53 | _j0 54 | _j1 55 | _jn 56 | _ld_int 57 | _ldclass 58 | _ldexp 59 | _ldlog 60 | _ldpcomp 61 | _ldpoly 62 | _ldscale 63 | _ldsign 64 | _ldsin 65 | _ldtest 66 | _ldunscale 67 | _logb 68 | _logbf 69 | _nextafter 70 | _nextafterf 71 | _scalb 72 | _scalbf 73 | _set_FMA3_enable 74 | _y0 75 | _y1 76 | _yn 77 | acos 78 | acosf 79 | acosh 80 | acoshf 81 | acoshl 82 | asin 83 | asinf 84 | asinh 85 | asinhf 86 | asinhl 87 | atan 88 | atan2 89 | atan2f 90 | atanf 91 | atanh 92 | atanhf 93 | atanhl 94 | cabs 95 | cabsf 96 | cabsl 97 | cacos 98 | cacosf 99 | cacosh 100 | cacoshf 101 | cacoshl 102 | cacosl 103 | carg 104 | cargf 105 | cargl 106 | casin 107 | casinf 108 | casinh 109 | casinhf 110 | casinhl 111 | casinl 112 | catan 113 | catanf 114 | catanh 115 | catanhf 116 | catanhl 117 | catanl 118 | cbrt 119 | cbrtf 120 | cbrtl 121 | ccos 122 | ccosf 123 | ccosh 124 | ccoshf 125 | ccoshl 126 | ccosl 127 | ceil 128 | ceilf 129 | cexp 130 | cexpf 131 | cexpl 132 | cimag 133 | cimagf 134 | cimagl 135 | clog 136 | clog10 137 | clog10f 138 | clog10l 139 | clogf 140 | clogl 141 | conj 142 | conjf 143 | conjl 144 | copysign 145 | copysignf 146 | copysignl 147 | cos 148 | cosf 149 | cosh 150 | coshf 151 | cpow 152 | cpowf 153 | cpowl 154 | cproj 155 | cprojf 156 | cprojl 157 | creal 158 | crealf 159 | creall 160 | csin 161 | csinf 162 | csinh 163 | csinhf 164 | csinhl 165 | csinl 166 | csqrt 167 | csqrtf 168 | csqrtl 169 | ctan 170 | ctanf 171 | ctanh 172 | ctanhf 173 | ctanhl 174 | ctanl 175 | erf 176 | erfc 177 | erfcf 178 | erfcl 179 | erff 180 | erfl 181 | exp 182 | exp2 183 | exp2f 184 | exp2l 185 | expf 186 | expm1 187 | expm1f 188 | expm1l 189 | fabs 190 | fdim 191 | fdimf 192 | fdiml 193 | floor 194 | floorf 195 | fma 196 | fmaf 197 | fmal 198 | fmax 199 | fmaxf 200 | fmaxl 201 | fmin 202 | fminf 203 | fminl 204 | fmod 205 | fmodf 206 | frexp 207 | hypot 208 | ilogb 209 | ilogbf 210 | ilogbl 211 | ldexp 212 | lgamma 213 | lgammaf 214 | lgammal 215 | llrint 216 | llrintf 217 | llrintl 218 | llround 219 | llroundf 220 | llroundl 221 | log 222 | log10 223 | log10f 224 | log1p 225 | log1pf 226 | log1pl 227 | log2 228 | log2f 229 | log2l 230 | logb 231 | logbf 232 | logbl 233 | logf 234 | lrint 235 | lrintf 236 | lrintl 237 | lround 238 | lroundf 239 | lroundl 240 | modf 241 | modff 242 | nan 243 | nanf 244 | nanl 245 | nearbyint 246 | nearbyintf 247 | nearbyintl 248 | nextafter 249 | nextafterf 250 | nextafterl 251 | nexttoward 252 | nexttowardf 253 | nexttowardl 254 | norm 255 | normf 256 | norml 257 | pow 258 | powf 259 | remainder 260 | remainderf 261 | remainderl 262 | remquo 263 | remquof 264 | remquol 265 | rint 266 | rintf 267 | rintl 268 | round 269 | roundf 270 | roundl 271 | scalbln 272 | scalblnf 273 | scalblnl 274 | scalbn 275 | scalbnf 276 | scalbnl 277 | sin 278 | sinf 279 | sinh 280 | sinhf 281 | sqrt 282 | sqrtf 283 | tan 284 | tanf 285 | tanh 286 | tanhf 287 | tgamma 288 | tgammaf 289 | tgammal 290 | trunc 291 | truncf 292 | truncl 293 | -------------------------------------------------------------------------------- /tools/glsl_preproc/macros.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import re 4 | 5 | from variables import Var 6 | from templates import * 7 | from statement import * 8 | 9 | PATTERN_PRAGMA = re.compile(flags=re.VERBOSE, pattern=r''' 10 | \s*\#\s*pragma\s+ # '#pragma' 11 | (?P(?: # pragma name 12 | GLSL[PHF]? 13 | ))\s* 14 | (?P.*)$ # rest of line (pragma body) 15 | ''') 16 | 17 | # Represents a single #pragma macro 18 | class Macro(object): 19 | PRAGMAS = { 20 | 'GLSL': 'SH_BUF_BODY', 21 | 'GLSLP': 'SH_BUF_PRELUDE', 22 | 'GLSLH': 'SH_BUF_HEADER', 23 | 'GLSLF': 'SH_BUF_FOOTER', 24 | } 25 | 26 | def __init__(self, linenr=0, type='GLSL'): 27 | self.linenr = linenr 28 | self.buf = Macro.PRAGMAS[type] 29 | self.name = '_glsl_' + str(linenr) 30 | self.body = [] # list of statements 31 | self.last = None # previous GLSLBlock (if unterminated) 32 | self.vars = VarSet() 33 | 34 | def needs_single_line(self): 35 | if not self.body: 36 | return False 37 | prev = self.body[-1] 38 | return isinstance(prev, BlockStart) and not prev.multiline 39 | 40 | def push_line(self, line): 41 | self.vars.merge(line.vars) 42 | 43 | if isinstance(line, GLSLLine): 44 | if self.last: 45 | self.last.append(line) 46 | elif self.needs_single_line(): 47 | self.body.append(GLSLBlock(line)) 48 | else: 49 | # start new GLSL block 50 | self.last = GLSLBlock(line) 51 | self.body.append(self.last) 52 | else: 53 | self.body.append(line) 54 | self.last = None 55 | 56 | def render_struct(self): 57 | return STRUCT_TEMPLATE.render(macro=self) 58 | 59 | def render_call(self): 60 | return CALL_TEMPLATE.render(macro=self) 61 | 62 | def render_fun(self): 63 | return FUNCTION_TEMPLATE.render(macro=self, Var=Var) 64 | 65 | # yields output lines 66 | @staticmethod 67 | def process_file(lines, strip=False): 68 | macro = None 69 | macros = [] 70 | 71 | for linenr, line_orig in enumerate(lines, start=1): 72 | line = line_orig.rstrip() 73 | 74 | # Strip leading spaces, due to C indent. Skip first pragma line. 75 | if macro and leading_spaces is None: 76 | leading_spaces = len(line) - len(line.lstrip()) 77 | 78 | # check for start of macro 79 | if not macro: 80 | leading_spaces = None 81 | if result := re.match(PATTERN_PRAGMA, line): 82 | macro = Macro(linenr, type=result['pragma']) 83 | line = result['rest'] # strip pragma prefix 84 | 85 | if macro: 86 | if leading_spaces: 87 | line = re.sub(f'^\s{{1,{leading_spaces}}}', '', line) 88 | if more_lines := line.endswith('\\'): 89 | line = line[:-1] 90 | if statement := Statement.parse(line, strip=strip, linenr=linenr): 91 | macro.push_line(statement) 92 | if more_lines: 93 | continue # stay in macro 94 | else: 95 | yield macro.render_call() 96 | yield '#line {}\n'.format(linenr + 1) 97 | macros.append(macro) 98 | macro = None 99 | else: 100 | yield line_orig 101 | 102 | if macros: 103 | yield '\n// Auto-generated template functions:' 104 | for macro in macros: 105 | yield macro.render_fun() 106 | -------------------------------------------------------------------------------- /src/include/libplacebo/dither.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #ifndef LIBPLACEBO_DITHER_H_ 19 | #define LIBPLACEBO_DITHER_H_ 20 | 21 | #include 22 | 23 | PL_API_BEGIN 24 | 25 | // Generates a deterministic NxN bayer (ordered) dither matrix, storing the 26 | // result in `data`. `size` must be a power of two. The resulting matrix will 27 | // be roughly uniformly distributed within the range [0,1). 28 | PL_API void pl_generate_bayer_matrix(float *data, int size); 29 | 30 | // Generates a random NxN blue noise texture. storing the result in `data`. 31 | // `size` must be a positive power of two no larger than 256. The resulting 32 | // texture will be roughly uniformly distributed within the range [0,1). 33 | // 34 | // Note: This function is very, *very* slow for large sizes. Generating a 35 | // dither matrix with size 256 can take several seconds on a modern processor. 36 | PL_API void pl_generate_blue_noise(float *data, int size); 37 | 38 | // Defines the border of all error diffusion kernels 39 | #define PL_EDF_MIN_DX (-2) 40 | #define PL_EDF_MAX_DX (2) 41 | #define PL_EDF_MAX_DY (2) 42 | 43 | struct pl_error_diffusion_kernel { 44 | const char *name; // Short and concise identifier 45 | const char *description; // Longer / friendly name 46 | 47 | // The minimum value such that a (y, x) -> (y, x + y * shift) mapping will 48 | // make all error pushing operations affect next column (and after it) 49 | // only. 50 | // 51 | // Higher shift values are significantly more computationally intensive. 52 | int shift; 53 | 54 | // The diffusion factor for (y, x) is pattern[y][x - PL_EDF_MIN_DX] / divisor. 55 | int pattern[PL_EDF_MAX_DY + 1][PL_EDF_MAX_DX - PL_EDF_MIN_DX + 1]; 56 | int divisor; 57 | }; 58 | 59 | // Algorithms with shift=1: 60 | PL_API extern const struct pl_error_diffusion_kernel pl_error_diffusion_simple; 61 | PL_API extern const struct pl_error_diffusion_kernel pl_error_diffusion_false_fs; 62 | // Algorithms with shift=2: 63 | PL_API extern const struct pl_error_diffusion_kernel pl_error_diffusion_sierra_lite; 64 | PL_API extern const struct pl_error_diffusion_kernel pl_error_diffusion_floyd_steinberg; 65 | PL_API extern const struct pl_error_diffusion_kernel pl_error_diffusion_atkinson; 66 | // Algorithms with shift=3, probably too heavy for low end GPUs: 67 | PL_API extern const struct pl_error_diffusion_kernel pl_error_diffusion_jarvis_judice_ninke; 68 | PL_API extern const struct pl_error_diffusion_kernel pl_error_diffusion_stucki; 69 | PL_API extern const struct pl_error_diffusion_kernel pl_error_diffusion_burkes; 70 | PL_API extern const struct pl_error_diffusion_kernel pl_error_diffusion_sierra2; 71 | PL_API extern const struct pl_error_diffusion_kernel pl_error_diffusion_sierra3; 72 | 73 | // A list of built-in error diffusion kernels, terminated by NULL 74 | PL_API extern const struct pl_error_diffusion_kernel * const pl_error_diffusion_kernels[]; 75 | PL_API extern const int pl_num_error_diffusion_kernels; // excluding trailing NULL 76 | 77 | // Find the error diffusion kernel with the given name, or NULL on failure. 78 | PL_API const struct pl_error_diffusion_kernel *pl_find_error_diffusion_kernel(const char *name); 79 | 80 | PL_API_END 81 | 82 | #endif // LIBPLACEBO_DITHER_H_ 83 | -------------------------------------------------------------------------------- /src/vulkan/utils_gen.c.j2: -------------------------------------------------------------------------------- 1 | #define VK_ENABLE_BETA_EXTENSIONS 2 | #include "vulkan/utils.h" 3 | 4 | const char *vk_driver_id_name(VkDriverId driver) 5 | { 6 | switch (driver) { 7 | {% for driver in vkdrivers %} 8 | case {{ driver }}: return "{{ driver }}"; 9 | {% endfor %} 10 | 11 | default: return "unknown driver"; 12 | } 13 | } 14 | 15 | const char *vk_res_str(VkResult res) 16 | { 17 | switch (res) { 18 | {% for res in vkresults %} 19 | case {{ res }}: return "{{ res }}"; 20 | {% endfor %} 21 | 22 | default: return "unknown error"; 23 | } 24 | } 25 | 26 | const char *vk_fmt_name(VkFormat fmt) 27 | { 28 | switch (fmt) { 29 | {% for fmt in vkformats %} 30 | case {{ fmt }}: return "{{ fmt }}"; 31 | {% endfor %} 32 | 33 | default: return "unknown format"; 34 | } 35 | } 36 | 37 | const char *vk_csp_name(VkColorSpaceKHR csp) 38 | { 39 | switch (csp) { 40 | {% for csp in vkspaces %} 41 | case {{ csp }}: return "{{ csp }}"; 42 | {% endfor %} 43 | 44 | default: return "unknown color space"; 45 | } 46 | } 47 | 48 | const char *vk_handle_name(VkExternalMemoryHandleTypeFlagBitsKHR handle) 49 | { 50 | switch (handle) { 51 | {% for handle in vkhandles %} 52 | case {{ handle }}: return "{{ handle }}"; 53 | {% endfor %} 54 | 55 | default: return "unknown handle type"; 56 | } 57 | } 58 | 59 | const char *vk_alpha_mode(VkCompositeAlphaFlagsKHR alpha) 60 | { 61 | switch (alpha) { 62 | {% for mode in vkalphas %} 63 | case {{ mode }}: return "{{ mode }}"; 64 | {% endfor %} 65 | 66 | default: return "unknown alpha mode"; 67 | } 68 | } 69 | 70 | const char *vk_surface_transform(VkSurfaceTransformFlagsKHR tf) 71 | { 72 | switch (tf) { 73 | {% for tf in vktransforms %} 74 | case {{ tf }}: return "{{ tf }}"; 75 | {% endfor %} 76 | 77 | default: return "unknown surface transform"; 78 | } 79 | } 80 | 81 | 82 | const char *vk_obj_type(VkObjectType obj) 83 | { 84 | switch (obj) { 85 | {% for obj in vkobjects %} 86 | case {{ obj.enum }}: return "{{ obj.name }}"; 87 | {% endfor %} 88 | 89 | default: return "unknown object"; 90 | } 91 | } 92 | 93 | size_t vk_struct_size(VkStructureType stype) 94 | { 95 | switch (stype) { 96 | {% for struct in vkstructs %} 97 | case {{ struct.stype }}: return sizeof({{ struct.name }}); 98 | {% endfor %} 99 | 100 | default: return 0; 101 | } 102 | } 103 | 104 | uint32_t vk_ext_promoted_ver(const char *extension) 105 | { 106 | {% for ext in vkexts %} 107 | {% if ext.promoted_ver %} 108 | if (!strcmp(extension, "{{ ext.name }}")) 109 | return {{ ext.promoted_ver }}; 110 | {% endif %} 111 | {% endfor %} 112 | return 0; 113 | } 114 | 115 | void vk_features_normalize(void *alloc, const VkPhysicalDeviceFeatures2 *fin, 116 | uint32_t api_ver, VkPhysicalDeviceFeatures2 *out) 117 | { 118 | for (const VkBaseInStructure *in = (void *) fin; in; in = in->pNext) { 119 | switch (in->sType) { 120 | default: break; 121 | {% for fs in vkfeatures %} 122 | case {{ fs.stype }}: { 123 | const {{ fs.name }} *i = (const void *) in; 124 | {% for f in fs.features %} 125 | if (i->{{ f.name }}) { 126 | {% for r in f.replacements %} 127 | {% if r.core_ver %} 128 | if (!api_ver || api_ver >= {{ r.core_ver }}) 129 | {% elif r.max_ver %} 130 | if (!api_ver || api_ver < {{ r.max_ver }}) 131 | {% endif %} 132 | {% if fs.is_base %} 133 | out->{{ f.name }} = true; 134 | {% else %} 135 | (({{ r.name }} *) vk_chain_alloc(alloc, out, {{ r.stype }}))->{{ f.name }} = true; 136 | {% endif %} 137 | {% endfor %} 138 | } 139 | {% endfor %} 140 | break; 141 | } 142 | {% endfor %} 143 | } 144 | } 145 | } 146 | 147 | const VkAccessFlags2 vk_access_read = {{ '0x%x' % vkaccess.read }}LLU; 148 | const VkAccessFlags2 vk_access_write = {{ '0x%x' % vkaccess.write }}LLU; 149 | -------------------------------------------------------------------------------- /demos/plplay.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include "common.h" 8 | #include "pl_thread.h" 9 | 10 | #define MAX_FRAME_PASSES 256 11 | #define MAX_BLEND_PASSES 8 12 | #define MAX_BLEND_FRAMES 8 13 | 14 | enum { 15 | ZOOM_PAD = 0, 16 | ZOOM_CROP, 17 | ZOOM_STRETCH, 18 | ZOOM_FIT, 19 | ZOOM_RAW, 20 | ZOOM_400, 21 | ZOOM_200, 22 | ZOOM_100, 23 | ZOOM_50, 24 | ZOOM_25, 25 | ZOOM_COUNT, 26 | }; 27 | 28 | struct plplay_args { 29 | const struct pl_render_params *preset; 30 | enum pl_log_level verbosity; 31 | const char *window_impl; 32 | const char *filename; 33 | bool hwdec; 34 | }; 35 | 36 | bool parse_args(struct plplay_args *args, int argc, char *argv[]); 37 | 38 | struct plplay { 39 | struct plplay_args args; 40 | struct window *win; 41 | struct ui *ui; 42 | char cache_file[512]; 43 | 44 | // libplacebo 45 | pl_log log; 46 | pl_renderer renderer; 47 | pl_queue queue; 48 | pl_cache cache; 49 | uint64_t cache_sig; 50 | 51 | // libav* 52 | AVFormatContext *format; 53 | AVCodecContext *codec; 54 | const AVStream *stream; // points to first video stream of `format` 55 | pl_thread decoder_thread; 56 | bool decoder_thread_created; 57 | bool exit_thread; 58 | 59 | // settings / ui state 60 | pl_options opts; 61 | pl_rotation target_rot; 62 | int target_zoom; 63 | bool colorspace_hint; 64 | bool colorspace_hint_dynamic; 65 | bool ignore_dovi; 66 | bool toggle_fullscreen; 67 | bool advanced_scalers; 68 | 69 | bool target_override; // if false, fields below are ignored 70 | struct pl_color_repr force_repr; 71 | enum pl_color_primaries force_prim; 72 | enum pl_color_transfer force_trc; 73 | struct pl_hdr_metadata force_hdr; 74 | bool force_hdr_enable; 75 | bool fps_override; 76 | float fps; 77 | 78 | // ICC profile 79 | pl_icc_object icc; 80 | char *icc_name; 81 | bool use_icc_luma; 82 | bool force_bpc; 83 | 84 | // custom shaders 85 | const struct pl_hook **shader_hooks; 86 | char **shader_paths; 87 | size_t shader_num; 88 | size_t shader_size; 89 | 90 | // pass metadata 91 | struct pl_dispatch_info blend_info[MAX_BLEND_FRAMES][MAX_BLEND_PASSES]; 92 | struct pl_dispatch_info frame_info[MAX_FRAME_PASSES]; 93 | int num_frame_passes; 94 | int num_blend_passes[MAX_BLEND_FRAMES]; 95 | 96 | // playback statistics 97 | struct { 98 | _Atomic uint32_t decoded; 99 | uint32_t rendered; 100 | uint32_t mapped; 101 | uint32_t dropped; 102 | uint32_t missed; 103 | uint32_t stalled; 104 | double missed_ms; 105 | double stalled_ms; 106 | double current_pts; 107 | 108 | struct timing { 109 | double sum, sum2, peak; 110 | uint64_t count; 111 | } acquire, update, render, draw_ui, sleep, submit, swap, 112 | vsync_interval, pts_interval; 113 | } stats; 114 | }; 115 | 116 | void update_settings(struct plplay *p, const struct pl_frame *target); 117 | 118 | static inline void apply_csp_overrides(struct plplay *p, struct pl_color_space *csp) 119 | { 120 | if (p->force_prim) { 121 | csp->primaries = p->force_prim; 122 | csp->hdr.prim = *pl_raw_primaries_get(csp->primaries); 123 | } 124 | if (p->force_trc) 125 | csp->transfer = p->force_trc; 126 | if (p->force_hdr_enable) { 127 | struct pl_hdr_metadata fix = p->force_hdr; 128 | fix.prim = csp->hdr.prim; 129 | csp->hdr = fix; 130 | } else if (p->colorspace_hint_dynamic) { 131 | pl_color_space_nominal_luma_ex(pl_nominal_luma_params( 132 | .color = csp, 133 | .metadata = PL_HDR_METADATA_ANY, 134 | .scaling = PL_HDR_NITS, 135 | .out_min = &csp->hdr.min_luma, 136 | .out_max = &csp->hdr.max_luma, 137 | )); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/glsl/glslang.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #include "config_internal.h" 19 | 20 | #include 21 | 22 | extern "C" { 23 | #include "pl_alloc.h" 24 | #include "pl_thread.h" 25 | } 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include "glslang.h" 32 | 33 | #if (GLSLANG_VERSION_MAJOR * 1000 + GLSLANG_VERSION_MINOR) >= 11013 34 | #include 35 | #define DefaultTBuiltInResource *GetDefaultResources() 36 | #endif 37 | 38 | using namespace glslang; 39 | 40 | static pl_static_mutex pl_glslang_mutex = PL_STATIC_MUTEX_INITIALIZER; 41 | static int pl_glslang_refcount; 42 | 43 | bool pl_glslang_init(void) 44 | { 45 | bool ret = true; 46 | 47 | pl_static_mutex_lock(&pl_glslang_mutex); 48 | if (pl_glslang_refcount++ == 0) 49 | ret = InitializeProcess(); 50 | pl_static_mutex_unlock(&pl_glslang_mutex); 51 | 52 | return ret; 53 | } 54 | 55 | void pl_glslang_uninit(void) 56 | { 57 | pl_static_mutex_lock(&pl_glslang_mutex); 58 | if (--pl_glslang_refcount == 0) 59 | FinalizeProcess(); 60 | pl_static_mutex_unlock(&pl_glslang_mutex); 61 | } 62 | 63 | struct pl_glslang_res *pl_glslang_compile(struct pl_glsl_version glsl_ver, 64 | struct pl_spirv_version spirv_ver, 65 | enum glsl_shader_stage stage, 66 | const char *text) 67 | { 68 | assert(pl_glslang_refcount); 69 | struct pl_glslang_res *res = pl_zalloc_ptr(NULL, res); 70 | 71 | EShLanguage lang; 72 | switch (stage) { 73 | case GLSL_SHADER_VERTEX: lang = EShLangVertex; break; 74 | case GLSL_SHADER_FRAGMENT: lang = EShLangFragment; break; 75 | case GLSL_SHADER_COMPUTE: lang = EShLangCompute; break; 76 | default: abort(); 77 | } 78 | 79 | TShader *shader = new TShader(lang); 80 | 81 | shader->setEnvClient(EShClientVulkan, (EShTargetClientVersion) spirv_ver.env_version); 82 | shader->setEnvTarget(EShTargetSpv, (EShTargetLanguageVersion) spirv_ver.spv_version); 83 | shader->setStrings(&text, 1); 84 | 85 | TBuiltInResource limits = DefaultTBuiltInResource; 86 | limits.maxComputeWorkGroupSizeX = glsl_ver.max_group_size[0]; 87 | limits.maxComputeWorkGroupSizeY = glsl_ver.max_group_size[1]; 88 | limits.maxComputeWorkGroupSizeZ = glsl_ver.max_group_size[2]; 89 | limits.minProgramTexelOffset = glsl_ver.min_gather_offset; 90 | limits.maxProgramTexelOffset = glsl_ver.max_gather_offset; 91 | 92 | if (!shader->parse(&limits, 0, true, EShMsgDefault)) { 93 | res->error_msg = pl_str0dup0(res, shader->getInfoLog()); 94 | delete shader; 95 | return res; 96 | } 97 | 98 | TProgram *prog = new TProgram(); 99 | prog->addShader(shader); 100 | if (!prog->link(EShMsgDefault)) { 101 | res->error_msg = pl_str0dup0(res, prog->getInfoLog()); 102 | delete shader; 103 | delete prog; 104 | return res; 105 | } 106 | 107 | SpvOptions options; 108 | options.disableOptimizer = false; 109 | options.stripDebugInfo = true; 110 | options.optimizeSize = true; 111 | options.validate = true; 112 | std::vector spirv; 113 | GlslangToSpv(*prog->getIntermediate(lang), spirv, &options); 114 | 115 | res->success = true; 116 | res->size = spirv.size() * sizeof(unsigned int); 117 | res->data = pl_memdup(res, spirv.data(), res->size), 118 | delete shader; 119 | delete prog; 120 | return res; 121 | } 122 | -------------------------------------------------------------------------------- /src/pl_thread_pthread.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | typedef pthread_mutex_t pl_mutex; 28 | typedef pthread_cond_t pl_cond; 29 | typedef pthread_mutex_t pl_static_mutex; 30 | typedef pthread_t pl_thread; 31 | #define PL_STATIC_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER 32 | 33 | static inline int pl_mutex_init_type_internal(pl_mutex *mutex, enum pl_mutex_type mtype) 34 | { 35 | int mutex_type; 36 | switch (mtype) { 37 | case PL_MUTEX_RECURSIVE: 38 | mutex_type = PTHREAD_MUTEX_RECURSIVE; 39 | break; 40 | case PL_MUTEX_NORMAL: 41 | default: 42 | #ifndef NDEBUG 43 | mutex_type = PTHREAD_MUTEX_ERRORCHECK; 44 | #else 45 | mutex_type = PTHREAD_MUTEX_DEFAULT; 46 | #endif 47 | break; 48 | } 49 | 50 | int ret = 0; 51 | pthread_mutexattr_t attr; 52 | ret = pthread_mutexattr_init(&attr); 53 | if (ret != 0) 54 | return ret; 55 | 56 | pthread_mutexattr_settype(&attr, mutex_type); 57 | ret = pthread_mutex_init(mutex, &attr); 58 | pthread_mutexattr_destroy(&attr); 59 | return ret; 60 | } 61 | 62 | #define pl_mutex_init_type(mutex, mtype) \ 63 | pl_assert(!pl_mutex_init_type_internal(mutex, mtype)) 64 | 65 | #define pl_mutex_destroy pthread_mutex_destroy 66 | #define pl_mutex_lock pthread_mutex_lock 67 | #define pl_mutex_unlock pthread_mutex_unlock 68 | 69 | static inline int pl_cond_init(pl_cond *cond) 70 | { 71 | int ret = 0; 72 | pthread_condattr_t attr; 73 | ret = pthread_condattr_init(&attr); 74 | if (ret != 0) 75 | return ret; 76 | 77 | #ifdef PTHREAD_HAS_SETCLOCK 78 | pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); 79 | #endif 80 | ret = pthread_cond_init(cond, &attr); 81 | pthread_condattr_destroy(&attr); 82 | return ret; 83 | } 84 | 85 | #define pl_cond_destroy pthread_cond_destroy 86 | #define pl_cond_broadcast pthread_cond_broadcast 87 | #define pl_cond_signal pthread_cond_signal 88 | #define pl_cond_wait pthread_cond_wait 89 | 90 | static inline int pl_cond_timedwait(pl_cond *cond, pl_mutex *mutex, uint64_t timeout) 91 | { 92 | if (timeout == UINT64_MAX) 93 | return pthread_cond_wait(cond, mutex); 94 | 95 | struct timespec ts; 96 | #ifdef PTHREAD_HAS_SETCLOCK 97 | if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) 98 | return errno; 99 | #else 100 | struct timeval tv; 101 | if (gettimeofday(&tv, NULL) < 0) // equivalent to CLOCK_REALTIME 102 | return errno; 103 | ts.tv_sec = tv.tv_sec; 104 | ts.tv_nsec = tv.tv_usec * 1000; 105 | #endif 106 | 107 | ts.tv_sec += timeout / 1000000000LLU; 108 | ts.tv_nsec += timeout % 1000000000LLU; 109 | 110 | if (ts.tv_nsec > 1000000000L) { 111 | ts.tv_nsec -= 1000000000L; 112 | ts.tv_sec++; 113 | } 114 | 115 | return pthread_cond_timedwait(cond, mutex, &ts); 116 | } 117 | 118 | #define pl_static_mutex_lock pthread_mutex_lock 119 | #define pl_static_mutex_unlock pthread_mutex_unlock 120 | 121 | #define PL_THREAD_VOID void * 122 | #define PL_THREAD_RETURN() return NULL 123 | 124 | #define pl_thread_create(t, f, a) pthread_create(t, NULL, f, a) 125 | #define pl_thread_join(t) pthread_join(t, NULL) 126 | 127 | static inline bool pl_thread_sleep(double t) 128 | { 129 | if (t <= 0.0) 130 | return true; 131 | 132 | struct timespec ts; 133 | ts.tv_sec = (time_t) t; 134 | ts.tv_nsec = (t - ts.tv_sec) * 1e9; 135 | 136 | return nanosleep(&ts, NULL) == 0; 137 | } 138 | -------------------------------------------------------------------------------- /src/d3d11/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include "common.h" 21 | 22 | #define DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P709 ((DXGI_COLOR_SPACE_TYPE)20) 23 | #define DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P2020 ((DXGI_COLOR_SPACE_TYPE)21) 24 | #define DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P709 ((DXGI_COLOR_SPACE_TYPE)22) 25 | #define DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P2020 ((DXGI_COLOR_SPACE_TYPE)23) 26 | #define DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_TOPLEFT_P2020 ((DXGI_COLOR_SPACE_TYPE)24) 27 | 28 | // Flush debug messages from D3D11's info queue to libplacebo's log output. 29 | // Should be called regularly. 30 | void pl_d3d11_flush_message_queue(struct d3d11_ctx *ctx, const char *header); 31 | 32 | // Some D3D11 functions can fail with a set of HRESULT codes which indicate the 33 | // device has been removed. This is equivalent to libplacebo's gpu_is_failed 34 | // state and indicates that the pl_gpu needs to be recreated. This function 35 | // checks for one of those HRESULTs, sets the failed state, and returns a 36 | // specific HRESULT that indicates why the device was removed (eg. GPU hang, 37 | // driver crash, etc.) 38 | HRESULT pl_d3d11_check_device_removed(struct d3d11_ctx *ctx, HRESULT hr); 39 | 40 | // Helper function for the D3D() macro, though it can be called directly when 41 | // handling D3D11 errors if the D3D() macro isn't suitable for some reason. 42 | // Calls `pl_d3d11_check_device_removed` and `pl_d3d11_drain_debug_messages` and 43 | // returns the specific HRESULT from `pl_d3d11_check_device_removed` for logging 44 | // purposes. 45 | HRESULT pl_d3d11_after_error(struct d3d11_ctx *ctx, HRESULT hr); 46 | 47 | // Convenience macro for running DXGI/D3D11 functions and performing appropriate 48 | // actions on failure. Can also be used for any HRESULT-returning function. 49 | #define D3D(call) \ 50 | do { \ 51 | HRESULT hr_ = (call); \ 52 | if (FAILED(hr_)) { \ 53 | hr_ = pl_d3d11_after_error(ctx, hr_); \ 54 | PL_ERR(ctx, "%s: %s (%s:%d)", #call, pl_hresult_to_str(hr_), \ 55 | __FILE__, __LINE__); \ 56 | goto error; \ 57 | } \ 58 | } while (0); 59 | 60 | // Conditionally release a COM interface and set the pointer to NULL 61 | #define SAFE_RELEASE(iface) \ 62 | do { \ 63 | if (iface) \ 64 | (iface)->lpVtbl->Release(iface); \ 65 | (iface) = NULL; \ 66 | } while (0) 67 | 68 | struct dll_version { 69 | uint16_t major; 70 | uint16_t minor; 71 | uint16_t build; 72 | uint16_t revision; 73 | }; 74 | 75 | // Get the version number of a DLL. This calls GetFileVersionInfoW, which should 76 | // call LoadLibraryExW internally, so it should get the same copy of the DLL 77 | // that is loaded into memory if there is a copy in System32 and a copy in the 78 | // %PATH% or application directory. 79 | struct dll_version pl_get_dll_version(const wchar_t *name); 80 | 81 | wchar_t *pl_from_utf8(void *ctx, const char *str); 82 | char *pl_to_utf8(void *ctx, const wchar_t *str); 83 | 84 | #define pl_hresult_to_str(hr) pl_hresult_to_str_buf((char[256]){0}, 256, (hr)) 85 | char *pl_hresult_to_str_buf(char *buf, size_t buf_size, HRESULT hr); 86 | 87 | const char *pl_get_dxgi_csp_name(DXGI_COLOR_SPACE_TYPE csp); 88 | const char *pl_get_dxgi_format_name(DXGI_FORMAT fmt); 89 | -------------------------------------------------------------------------------- /src/opengl/gpu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include "../gpu.h" 21 | #include "common.h" 22 | 23 | // Thread safety: Unsafe, same as pl_gpu_destroy 24 | pl_gpu pl_gpu_create_gl(pl_log log, pl_opengl gl, const struct pl_opengl_params *params); 25 | 26 | // --- pl_gpu internal structs and functions 27 | 28 | struct pl_gl { 29 | struct pl_gpu_fns impl; 30 | pl_opengl gl; 31 | uint64_t sig; // hash of GL_VERSION, GL_VENDOR, GL_RENDERER 32 | bool failed; 33 | 34 | // For import/export 35 | EGLDisplay egl_dpy; 36 | EGLContext egl_ctx; 37 | bool egl_storage; 38 | #ifdef PL_HAVE_UNIX 39 | // List of formats supported by EGL_EXT_image_dma_buf_import 40 | PL_ARRAY(EGLint) egl_formats; 41 | #endif 42 | 43 | // Sync objects and associated callbacks 44 | PL_ARRAY(struct gl_cb) callbacks; 45 | 46 | 47 | // Incrementing counters to keep track of object uniqueness 48 | int buf_id; 49 | 50 | // Cached capabilities 51 | int gl_ver; 52 | int gles_ver; 53 | bool has_storage; 54 | bool has_invalidate_fb; 55 | bool has_invalidate_tex; 56 | bool has_vao; 57 | bool has_queries; 58 | bool has_modifiers; 59 | bool has_readback; 60 | bool has_egl_storage; 61 | bool has_egl_import; 62 | int gather_comps; 63 | }; 64 | 65 | static inline const gl_funcs *gl_funcs_get(pl_gpu gpu) 66 | { 67 | struct pl_gl *p = PL_PRIV(gpu); 68 | struct gl_ctx *glctx = PL_PRIV(p->gl); 69 | return &glctx->func; 70 | } 71 | 72 | void gl_timer_begin(pl_gpu gpu, pl_timer timer); 73 | void gl_timer_end(pl_gpu gpu, pl_timer timer); 74 | 75 | static inline bool _make_current(pl_gpu gpu) 76 | { 77 | struct pl_gl *p = PL_PRIV(gpu); 78 | if (!gl_make_current(p->gl)) { 79 | p->failed = true; 80 | return false; 81 | } 82 | 83 | return true; 84 | } 85 | 86 | static inline void _release_current(pl_gpu gpu) 87 | { 88 | struct pl_gl *p = PL_PRIV(gpu); 89 | gl_release_current(p->gl); 90 | } 91 | 92 | #define MAKE_CURRENT() _make_current(gpu) 93 | #define RELEASE_CURRENT() _release_current(gpu) 94 | 95 | struct pl_tex_gl { 96 | GLenum target; 97 | GLuint texture; 98 | bool wrapped_tex; 99 | GLuint fbo; // or 0 100 | bool wrapped_fb; 101 | GLbitfield barrier; 102 | 103 | // GL format fields 104 | GLenum format; 105 | GLint iformat; 106 | GLenum type; 107 | 108 | // For imported/exported textures 109 | EGLImageKHR image; 110 | int fd; 111 | }; 112 | 113 | pl_tex gl_tex_create(pl_gpu, const struct pl_tex_params *); 114 | void gl_tex_destroy(pl_gpu, pl_tex); 115 | void gl_tex_invalidate(pl_gpu, pl_tex); 116 | void gl_tex_clear_ex(pl_gpu, pl_tex, const union pl_clear_color); 117 | void gl_tex_blit(pl_gpu, const struct pl_tex_blit_params *); 118 | bool gl_tex_upload(pl_gpu, const struct pl_tex_transfer_params *); 119 | bool gl_tex_download(pl_gpu, const struct pl_tex_transfer_params *); 120 | 121 | struct pl_buf_gl { 122 | uint64_t id; // unique per buffer 123 | GLuint buffer; 124 | size_t offset; 125 | GLsync fence; 126 | GLbitfield barrier; 127 | bool mapped; 128 | }; 129 | 130 | pl_buf gl_buf_create(pl_gpu, const struct pl_buf_params *); 131 | void gl_buf_destroy(pl_gpu, pl_buf); 132 | void gl_buf_write(pl_gpu, pl_buf, size_t offset, const void *src, size_t size); 133 | bool gl_buf_read(pl_gpu, pl_buf, size_t offset, void *dst, size_t size); 134 | void gl_buf_copy(pl_gpu, pl_buf dst, size_t dst_offset, 135 | pl_buf src, size_t src_offset, size_t size); 136 | bool gl_buf_poll(pl_gpu, pl_buf, uint64_t timeout); 137 | 138 | struct pl_pass_gl; 139 | int gl_desc_namespace(pl_gpu, enum pl_desc_type type); 140 | pl_pass gl_pass_create(pl_gpu, const struct pl_pass_params *); 141 | void gl_pass_destroy(pl_gpu, pl_pass); 142 | void gl_pass_run(pl_gpu, const struct pl_pass_run_params *); 143 | -------------------------------------------------------------------------------- /src/glsl/spirv_glslang.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #include "hash.h" 19 | #include "spirv.h" 20 | #include "utils.h" 21 | #include "glsl/glslang.h" 22 | 23 | // This header contains only preprocessor definitions 24 | #include 25 | 26 | // This is awkward, but we cannot use upstream macro, it was fixed in 11.11.0 27 | #define PL_GLSLANG_VERSION_GREATER_THAN(major, minor, patch) \ 28 | ((GLSLANG_VERSION_MAJOR) > (major) || ((major) == GLSLANG_VERSION_MAJOR && \ 29 | ((GLSLANG_VERSION_MINOR) > (minor) || ((minor) == GLSLANG_VERSION_MINOR && \ 30 | (GLSLANG_VERSION_PATCH) > (patch))))) 31 | 32 | #if PL_GLSLANG_VERSION_GREATER_THAN(11, 8, 0) 33 | #define GLSLANG_SPV_MAX PL_SPV_VERSION(1, 6) 34 | #elif PL_GLSLANG_VERSION_GREATER_THAN(7, 13, 3496) 35 | #define GLSLANG_SPV_MAX PL_SPV_VERSION(1, 5) 36 | #elif PL_GLSLANG_VERSION_GREATER_THAN(6, 2, 2596) 37 | #define GLSLANG_SPV_MAX PL_SPV_VERSION(1, 3) 38 | #else 39 | #define GLSLANG_SPV_MAX PL_SPV_VERSION(1, 0) 40 | #endif 41 | 42 | const struct spirv_compiler pl_spirv_glslang; 43 | 44 | static void glslang_destroy(pl_spirv spirv) 45 | { 46 | pl_glslang_uninit(); 47 | pl_free((void *) spirv); 48 | } 49 | 50 | static pl_spirv glslang_create(pl_log log, struct pl_spirv_version spirv_ver) 51 | { 52 | if (!pl_glslang_init()) { 53 | pl_fatal(log, "Failed initializing glslang SPIR-V compiler!"); 54 | return NULL; 55 | } 56 | 57 | struct pl_spirv_t *spirv = pl_alloc_ptr(NULL, spirv); 58 | *spirv = (struct pl_spirv_t) { 59 | .signature = pl_str0_hash(pl_spirv_glslang.name), 60 | .impl = &pl_spirv_glslang, 61 | .version = spirv_ver, 62 | .log = log, 63 | }; 64 | 65 | PL_INFO(spirv, "glslang version: %d.%d.%d", 66 | GLSLANG_VERSION_MAJOR, 67 | GLSLANG_VERSION_MINOR, 68 | GLSLANG_VERSION_PATCH); 69 | 70 | // Clamp to supported version by glslang 71 | if (GLSLANG_SPV_MAX < spirv->version.spv_version) { 72 | PL_WARN(spirv, "SPIR-V %u.%u is not supported by the current" 73 | " version of glslang. Falling back to %u.%u!", 74 | spirv->version.spv_version >> 16, (spirv->version.spv_version >> 8) & 0xff, 75 | GLSLANG_SPV_MAX >> 16, (GLSLANG_SPV_MAX >> 8) & 0xff); 76 | spirv->version.spv_version = GLSLANG_SPV_MAX; 77 | spirv->version.env_version = pl_spirv_version_to_vulkan(GLSLANG_SPV_MAX); 78 | } 79 | 80 | pl_hash_merge(&spirv->signature, (uint64_t) spirv->version.spv_version << 32 | 81 | spirv->version.env_version); 82 | pl_hash_merge(&spirv->signature, (GLSLANG_VERSION_MAJOR & 0xFF) << 24 | 83 | (GLSLANG_VERSION_MINOR & 0xFF) << 16 | 84 | (GLSLANG_VERSION_PATCH & 0xFFFF)); 85 | return spirv; 86 | } 87 | 88 | static pl_str glslang_compile(pl_spirv spirv, void *alloc, 89 | struct pl_glsl_version glsl_ver, 90 | enum glsl_shader_stage stage, 91 | const char *shader) 92 | { 93 | struct pl_glslang_res *res; 94 | 95 | res = pl_glslang_compile(glsl_ver, spirv->version, stage, shader); 96 | if (!res || !res->success) { 97 | PL_ERR(spirv, "glslang failed: %s", res ? res->error_msg : "(null)"); 98 | pl_free(res); 99 | return (struct pl_str) {0}; 100 | } 101 | 102 | struct pl_str ret = { 103 | .buf = pl_steal(alloc, res->data), 104 | .len = res->size, 105 | }; 106 | 107 | pl_free(res); 108 | return ret; 109 | } 110 | 111 | const struct spirv_compiler pl_spirv_glslang = { 112 | .name = "glslang", 113 | .destroy = glslang_destroy, 114 | .create = glslang_create, 115 | .compile = glslang_compile, 116 | }; 117 | -------------------------------------------------------------------------------- /src/tests/common.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | static int irand() 4 | { 5 | return rand() - RAND_MAX / 2; 6 | } 7 | 8 | int main() 9 | { 10 | pl_log log = pl_test_logger(); 11 | pl_log_update(log, NULL); 12 | pl_log_destroy(&log); 13 | 14 | // Test some misc helper functions 15 | pl_rect2d rc2 = { 16 | irand(), irand(), 17 | irand(), irand(), 18 | }; 19 | 20 | pl_rect3d rc3 = { 21 | irand(), irand(), irand(), 22 | irand(), irand(), irand(), 23 | }; 24 | 25 | pl_rect2d_normalize(&rc2); 26 | REQUIRE_CMP(rc2.x1, >=, rc2.x0, "d"); 27 | REQUIRE_CMP(rc2.y1, >=, rc2.y0, "d"); 28 | 29 | pl_rect3d_normalize(&rc3); 30 | REQUIRE_CMP(rc3.x1, >=, rc3.x0, "d"); 31 | REQUIRE_CMP(rc3.y1, >=, rc3.y0, "d"); 32 | REQUIRE_CMP(rc3.z1, >=, rc3.z0, "d"); 33 | 34 | pl_rect2df rc2f = { 35 | RANDOM, RANDOM, 36 | RANDOM, RANDOM, 37 | }; 38 | 39 | pl_rect3df rc3f = { 40 | RANDOM, RANDOM, RANDOM, 41 | RANDOM, RANDOM, RANDOM, 42 | }; 43 | 44 | pl_rect2df_normalize(&rc2f); 45 | REQUIRE_CMP(rc2f.x1, >=, rc2f.x0, "f"); 46 | REQUIRE_CMP(rc2f.y1, >=, rc2f.y0, "f"); 47 | 48 | pl_rect3df_normalize(&rc3f); 49 | REQUIRE_CMP(rc3f.x1, >=, rc3f.x0, "f"); 50 | REQUIRE_CMP(rc3f.y1, >=, rc3f.y0, "f"); 51 | REQUIRE_CMP(rc3f.z1, >=, rc3f.z0, "f"); 52 | 53 | pl_rect2d rc2r = pl_rect2df_round(&rc2f); 54 | pl_rect3d rc3r = pl_rect3df_round(&rc3f); 55 | 56 | REQUIRE_CMP(fabs(rc2r.x0 - rc2f.x0), <=, 0.5, "f"); 57 | REQUIRE_CMP(fabs(rc2r.x1 - rc2f.x1), <=, 0.5, "f"); 58 | REQUIRE_CMP(fabs(rc2r.y0 - rc2f.y0), <=, 0.5, "f"); 59 | REQUIRE_CMP(fabs(rc2r.y1 - rc2f.y1), <=, 0.5, "f"); 60 | 61 | REQUIRE_CMP(fabs(rc3r.x0 - rc3f.x0), <=, 0.5, "f"); 62 | REQUIRE_CMP(fabs(rc3r.x1 - rc3f.x1), <=, 0.5, "f"); 63 | REQUIRE_CMP(fabs(rc3r.y0 - rc3f.y0), <=, 0.5, "f"); 64 | REQUIRE_CMP(fabs(rc3r.y1 - rc3f.y1), <=, 0.5, "f"); 65 | REQUIRE_CMP(fabs(rc3r.z0 - rc3f.z0), <=, 0.5, "f"); 66 | REQUIRE_CMP(fabs(rc3r.z1 - rc3f.z1), <=, 0.5, "f"); 67 | 68 | pl_transform3x3 tr = { 69 | .mat = {{ 70 | { RANDOM, RANDOM, RANDOM }, 71 | { RANDOM, RANDOM, RANDOM }, 72 | { RANDOM, RANDOM, RANDOM }, 73 | }}, 74 | .c = { RANDOM, RANDOM, RANDOM }, 75 | }; 76 | 77 | pl_transform3x3 tr2 = tr; 78 | float scale = 1.0 + RANDOM; 79 | pl_transform3x3_scale(&tr2, scale); 80 | pl_transform3x3_invert(&tr2); 81 | pl_transform3x3_invert(&tr2); 82 | pl_transform3x3_scale(&tr2, 1.0 / scale); 83 | 84 | for (int i = 0; i < 3; i++) { 85 | for (int j = 0; j < 3; j++) { 86 | printf("%f %f\n", tr.mat.m[i][j], tr2.mat.m[i][j]); 87 | REQUIRE_FEQ(tr.mat.m[i][j], tr2.mat.m[i][j], 1e-4); 88 | } 89 | REQUIRE_FEQ(tr.c[i], tr2.c[i], 1e-4); 90 | } 91 | 92 | // Test aspect ratio code 93 | const pl_rect2df rc1080p = {0, 0, 1920, 1080}; 94 | const pl_rect2df rc43 = {0, 0, 1024, 768}; 95 | pl_rect2df rc; 96 | 97 | REQUIRE_FEQ(pl_rect2df_aspect(&rc1080p), 16.0/9.0, 1e-8); 98 | REQUIRE_FEQ(pl_rect2df_aspect(&rc43), 4.0/3.0, 1e-8); 99 | 100 | #define pl_rect2df_midx(rc) (((rc).x0 + (rc).x1) / 2.0) 101 | #define pl_rect2df_midy(rc) (((rc).y0 + (rc).y1) / 2.0) 102 | 103 | for (float aspect = 0.2; aspect < 3.0; aspect += 0.4) { 104 | for (float scan = 0.0; scan <= 1.0; scan += 0.5) { 105 | rc = rc1080p; 106 | pl_rect2df_aspect_set(&rc, aspect, scan); 107 | printf("aspect %.2f, panscan %.1f: {%f %f} -> {%f %f}\n", 108 | aspect, scan, rc.x0, rc.y0, rc.x1, rc.y1); 109 | REQUIRE_FEQ(pl_rect2df_aspect(&rc), aspect, 1e-6); 110 | REQUIRE_FEQ(pl_rect2df_midx(rc), pl_rect2df_midx(rc1080p), 1e-6); 111 | REQUIRE_FEQ(pl_rect2df_midy(rc), pl_rect2df_midy(rc1080p), 1e-6); 112 | } 113 | } 114 | 115 | rc = rc1080p; 116 | pl_rect2df_aspect_fit(&rc, &rc43, 0.0); 117 | REQUIRE_FEQ(pl_rect2df_aspect(&rc), pl_rect2df_aspect(&rc43), 1e-6); 118 | REQUIRE_FEQ(pl_rect2df_midx(rc), pl_rect2df_midx(rc1080p), 1e-6); 119 | REQUIRE_FEQ(pl_rect2df_midy(rc), pl_rect2df_midy(rc1080p), 1e-6); 120 | REQUIRE_FEQ(pl_rect_w(rc), pl_rect_w(rc43), 1e-6); 121 | REQUIRE_FEQ(pl_rect_h(rc), pl_rect_h(rc43), 1e-6); 122 | 123 | rc = rc43; 124 | pl_rect2df_aspect_fit(&rc, &rc1080p, 0.0); 125 | REQUIRE_FEQ(pl_rect2df_aspect(&rc), pl_rect2df_aspect(&rc1080p), 1e-6); 126 | REQUIRE_FEQ(pl_rect2df_midx(rc), pl_rect2df_midx(rc43), 1e-6); 127 | REQUIRE_FEQ(pl_rect2df_midy(rc), pl_rect2df_midy(rc43), 1e-6); 128 | REQUIRE_FEQ(pl_rect_w(rc), pl_rect_w(rc43), 1e-6); 129 | 130 | rc = (pl_rect2df) { 1920, 1080, 0, 0 }; 131 | pl_rect2df_offset(&rc, 50, 100); 132 | REQUIRE_FEQ(rc.x0, 1870, 1e-6); 133 | REQUIRE_FEQ(rc.x1, -50, 1e-6); 134 | REQUIRE_FEQ(rc.y0, 980, 1e-6); 135 | REQUIRE_FEQ(rc.y1, -100, 1e-6); 136 | } 137 | -------------------------------------------------------------------------------- /src/opengl/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #include "common.h" 19 | #include "gpu.h" 20 | #include "utils.h" 21 | 22 | const char *gl_err_str(GLenum err) 23 | { 24 | switch (err) { 25 | #define CASE(name) case name: return #name 26 | CASE(GL_NO_ERROR); 27 | CASE(GL_INVALID_ENUM); 28 | CASE(GL_INVALID_VALUE); 29 | CASE(GL_INVALID_OPERATION); 30 | CASE(GL_INVALID_FRAMEBUFFER_OPERATION); 31 | CASE(GL_OUT_OF_MEMORY); 32 | CASE(GL_STACK_UNDERFLOW); 33 | CASE(GL_STACK_OVERFLOW); 34 | #undef CASE 35 | 36 | default: return "unknown error"; 37 | } 38 | } 39 | 40 | void gl_poll_callbacks(pl_gpu gpu) 41 | { 42 | const gl_funcs *gl = gl_funcs_get(gpu); 43 | struct pl_gl *p = PL_PRIV(gpu); 44 | while (p->callbacks.num) { 45 | struct gl_cb cb = p->callbacks.elem[0]; 46 | GLenum res = gl->ClientWaitSync(cb.sync, 0, 0); 47 | switch (res) { 48 | case GL_ALREADY_SIGNALED: 49 | case GL_CONDITION_SATISFIED: 50 | PL_ARRAY_REMOVE_AT(p->callbacks, 0); 51 | cb.callback(cb.priv); 52 | continue; 53 | 54 | case GL_WAIT_FAILED: 55 | PL_ARRAY_REMOVE_AT(p->callbacks, 0); 56 | gl->DeleteSync(cb.sync); 57 | p->failed = true; 58 | gl_check_err(gpu, "gl_poll_callbacks"); // NOTE: will recurse! 59 | return; 60 | 61 | case GL_TIMEOUT_EXPIRED: 62 | return; 63 | 64 | default: 65 | pl_unreachable(); 66 | } 67 | } 68 | } 69 | 70 | bool gl_check_err(pl_gpu gpu, const char *fun) 71 | { 72 | const gl_funcs *gl = gl_funcs_get(gpu); 73 | struct pl_gl *p = PL_PRIV(gpu); 74 | bool ret = true; 75 | 76 | while (true) { 77 | GLenum error = gl->GetError(); 78 | if (error == GL_NO_ERROR) 79 | break; 80 | PL_ERR(gpu, "%s: OpenGL error: %s", fun, gl_err_str(error)); 81 | ret = false; 82 | p->failed = true; 83 | } 84 | 85 | gl_poll_callbacks(gpu); 86 | return ret; 87 | } 88 | 89 | bool gl_is_software(pl_opengl pl_gl) 90 | { 91 | struct gl_ctx *glctx = PL_PRIV(pl_gl); 92 | const gl_funcs *gl = &glctx->func; 93 | const char *renderer = (char *) gl->GetString(GL_RENDERER); 94 | return !renderer || 95 | strcmp(renderer, "Software Rasterizer") == 0 || 96 | strstr(renderer, "llvmpipe") || 97 | strstr(renderer, "softpipe") || 98 | strcmp(renderer, "Mesa X11") == 0 || 99 | strcmp(renderer, "Apple Software Renderer") == 0; 100 | } 101 | 102 | bool gl_is_gles(pl_opengl pl_gl) 103 | { 104 | struct gl_ctx *glctx = PL_PRIV(pl_gl); 105 | const gl_funcs *gl = &glctx->func; 106 | const char *version = (char *) gl->GetString(GL_VERSION); 107 | return pl_str_startswith0(pl_str0(version), "OpenGL ES"); 108 | } 109 | 110 | bool gl_test_ext(pl_gpu gpu, const char *ext, int gl_ver, int gles_ver) 111 | { 112 | struct pl_gl *p = PL_PRIV(gpu); 113 | if (gl_ver && p->gl_ver >= gl_ver) 114 | return true; 115 | if (gles_ver && p->gles_ver >= gles_ver) 116 | return true; 117 | 118 | return ext ? pl_opengl_has_ext(p->gl, ext) : false; 119 | } 120 | 121 | const char *egl_err_str(EGLenum err) 122 | { 123 | switch (err) { 124 | #define CASE(name) case name: return #name 125 | CASE(EGL_SUCCESS); 126 | CASE(EGL_NOT_INITIALIZED); 127 | CASE(EGL_BAD_ACCESS); 128 | CASE(EGL_BAD_ALLOC); 129 | CASE(EGL_BAD_ATTRIBUTE); 130 | CASE(EGL_BAD_CONFIG); 131 | CASE(EGL_BAD_CONTEXT); 132 | CASE(EGL_BAD_CURRENT_SURFACE); 133 | CASE(EGL_BAD_DISPLAY); 134 | CASE(EGL_BAD_MATCH); 135 | CASE(EGL_BAD_NATIVE_PIXMAP); 136 | CASE(EGL_BAD_NATIVE_WINDOW); 137 | CASE(EGL_BAD_PARAMETER); 138 | CASE(EGL_BAD_SURFACE); 139 | #undef CASE 140 | 141 | default: return "unknown error"; 142 | } 143 | } 144 | 145 | bool egl_check_err(pl_gpu gpu, const char *fun) 146 | { 147 | struct pl_gl *p = PL_PRIV(gpu); 148 | bool ret = true; 149 | 150 | while (true) { 151 | GLenum error = eglGetError(); 152 | if (error == EGL_SUCCESS) 153 | return ret; 154 | PL_ERR(gpu, "%s: EGL error: %s", fun, egl_err_str(error)); 155 | ret = false; 156 | p->failed = true; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/include/libplacebo/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #ifndef LIBPLACEBO_LOG_H_ 19 | #define LIBPLACEBO_LOG_H_ 20 | 21 | #include 22 | #include 23 | 24 | PL_API_BEGIN 25 | 26 | // The log level associated with a given log message. 27 | enum pl_log_level { 28 | PL_LOG_NONE = 0, 29 | PL_LOG_FATAL, // results in total loss of function of a major component 30 | PL_LOG_ERR, // serious error; may result in degraded function 31 | PL_LOG_WARN, // warning; potentially bad, probably user-relevant 32 | PL_LOG_INFO, // informational message, also potentially harmless errors 33 | PL_LOG_DEBUG, // verbose debug message, informational 34 | PL_LOG_TRACE, // very noisy trace of activity,, usually benign 35 | PL_LOG_ALL = PL_LOG_TRACE, 36 | }; 37 | 38 | struct pl_log_params { 39 | // Logging callback. All messages, informational or otherwise, will get 40 | // redirected to this callback. The logged messages do not include trailing 41 | // newlines. Optional. 42 | void (*log_cb)(void *log_priv, enum pl_log_level level, const char *msg); 43 | void *log_priv; 44 | 45 | // The current log level. Controls the level of message that will be 46 | // redirected to the log callback. Setting this to PL_LOG_ALL means all 47 | // messages will be forwarded, but doing so indiscriminately can result 48 | // in increased CPU usage as it may enable extra debug paths based on the 49 | // configured log level. 50 | enum pl_log_level log_level; 51 | }; 52 | 53 | #define pl_log_params(...) (&(struct pl_log_params) { __VA_ARGS__ }) 54 | PL_API extern const struct pl_log_params pl_log_default_params; 55 | 56 | // Thread-safety: Safe 57 | // 58 | // Note: In any context in which `pl_log` is used, users may also pass NULL 59 | // to disable logging. In other words, NULL is a valid `pl_log`. 60 | typedef const struct pl_log_t { 61 | struct pl_log_params params; 62 | } *pl_log; 63 | 64 | #define pl_log_glue1(x, y) x##y 65 | #define pl_log_glue2(x, y) pl_log_glue1(x, y) 66 | // Force a link error in the case of linking against an incompatible API 67 | // version. 68 | #define pl_log_create pl_log_glue2(pl_log_create_, PL_API_VER) 69 | // Creates a pl_log. `api_ver` is for historical reasons and ignored currently. 70 | // `params` defaults to `&pl_log_default_params` if left as NULL. 71 | // 72 | // Note: As a general rule, any `params` struct used as an argument to a 73 | // function need only live until the corresponding function returns. 74 | PL_API pl_log pl_log_create(int api_ver, const struct pl_log_params *params); 75 | 76 | // Destroy a `pl_log` object. 77 | // 78 | // Note: As a general rule, all `_destroy` functions take the pointer to the 79 | // object to free as their parameter. This pointer is overwritten by NULL 80 | // afterwards. Calling a _destroy function on &{NULL} is valid, but calling it 81 | // on NULL itself is invalid. 82 | PL_API void pl_log_destroy(pl_log *log); 83 | 84 | // Update the parameters of a `pl_log` without destroying it. This can be 85 | // used to change the log function, log context or log level retroactively. 86 | // `params` defaults to `&pl_log_default_params` if left as NULL. 87 | // 88 | // Returns the previous params, atomically. 89 | PL_API struct pl_log_params pl_log_update(pl_log log, const struct pl_log_params *params); 90 | 91 | // Like `pl_log_update` but only updates the log level, leaving the log 92 | // callback intact. 93 | // 94 | // Returns the previous log level, atomically. 95 | PL_API enum pl_log_level pl_log_level_update(pl_log log, enum pl_log_level level); 96 | 97 | // Two simple, stream-based loggers. You can use these as the log_cb. If you 98 | // also set log_priv to a FILE* (e.g. stdout or stderr) it will be printed 99 | // there; otherwise, it will be printed to stdout or stderr depending on the 100 | // log level. 101 | // 102 | // The version with colors will use ANSI escape sequences to indicate the log 103 | // level. The version without will use explicit prefixes. 104 | PL_API void pl_log_simple(void *stream, enum pl_log_level level, const char *msg); 105 | PL_API void pl_log_color(void *stream, enum pl_log_level level, const char *msg); 106 | 107 | // Backwards compatibility with older versions of libplacebo 108 | #define pl_context pl_log 109 | #define pl_context_params pl_log_params 110 | 111 | PL_API_END 112 | 113 | #endif // LIBPLACEBO_LOG_H_ 114 | -------------------------------------------------------------------------------- /demos/meson.build: -------------------------------------------------------------------------------- 1 | glfw = dependency('glfw3', required: false) 2 | sdl = dependency('sdl2', required: false) 3 | sdl_image = dependency('SDL2_image', required: false) 4 | 5 | ffmpeg_deps = [ 6 | dependency('libavcodec', required: false), 7 | dependency('libavformat', required: false), 8 | dependency('libavutil', required: false), 9 | ] 10 | 11 | ffmpeg_found = true 12 | foreach dep : ffmpeg_deps 13 | ffmpeg_found = ffmpeg_found and dep.found() 14 | endforeach 15 | 16 | nuklear = disabler() 17 | nuklear_inc = include_directories('./3rdparty/nuklear') 18 | if cc.has_header('nuklear.h', include_directories: nuklear_inc) 19 | nuklear_lib = static_library('nuklear', 20 | include_directories: nuklear_inc, 21 | c_args: ['-O2', '-Wno-missing-prototypes'], 22 | dependencies: [ libplacebo, libm ], 23 | sources: 'ui.c', 24 | ) 25 | 26 | nuklear = declare_dependency( 27 | include_directories: nuklear_inc, 28 | link_with: nuklear_lib, 29 | ) 30 | else 31 | warning('Nuklear was not found in `demos/3rdparty`. Please run ' + 32 | '`git submodule update --init` followed by `meson --wipe`.') 33 | endif 34 | 35 | conf_demos = configuration_data() 36 | conf_demos.set('HAVE_NUKLEAR', nuklear.found()) 37 | conf_demos.set('HAVE_EGL', cc.check_header('EGL/egl.h', required: false)) 38 | 39 | apis = [] 40 | 41 | # Enable all supported combinations of API and windowing system 42 | if glfw.found() 43 | if components.get('vulkan') 44 | conf_demos.set('HAVE_GLFW_VULKAN', true) 45 | apis += static_library('glfw-vk', 46 | dependencies: [libplacebo, libm, glfw, vulkan_headers], 47 | sources: 'window_glfw.c', 48 | c_args: ['-DUSE_VK'], 49 | ) 50 | endif 51 | 52 | if components.get('opengl') 53 | conf_demos.set('HAVE_GLFW_OPENGL', true) 54 | apis += static_library('glfw-gl', 55 | dependencies: [libplacebo, glfw], 56 | sources: 'window_glfw.c', 57 | c_args: '-DUSE_GL', 58 | ) 59 | endif 60 | 61 | if components.get('d3d11') 62 | conf_demos.set('HAVE_GLFW_D3D11', true) 63 | apis += static_library('glfw-d3d11', 64 | dependencies: [libplacebo, glfw], 65 | sources: 'window_glfw.c', 66 | c_args: '-DUSE_D3D11', 67 | ) 68 | endif 69 | endif 70 | 71 | if sdl.found() 72 | if components.get('vulkan') 73 | conf_demos.set('HAVE_SDL_VULKAN', true) 74 | apis += static_library('sdl-vk', 75 | dependencies: [libplacebo, sdl, vulkan_headers], 76 | sources: 'window_sdl.c', 77 | c_args: ['-DUSE_VK'], 78 | ) 79 | endif 80 | 81 | if components.get('opengl') 82 | conf_demos.set('HAVE_SDL_OPENGL', true) 83 | apis += static_library('sdl-gl', 84 | dependencies: [libplacebo, sdl], 85 | sources: 'window_sdl.c', 86 | c_args: '-DUSE_GL', 87 | ) 88 | endif 89 | endif 90 | 91 | configure_file( 92 | output: 'config_demos.h', 93 | configuration: conf_demos, 94 | ) 95 | 96 | if apis.length() == 0 97 | warning('Demos enabled but no supported combination of windowing system ' + 98 | 'and graphical APIs was found. Demo programs require either GLFW or ' + 99 | 'SDL and either Vulkan or OpenGL to function.') 100 | else 101 | 102 | additional_dep = [] 103 | if host_machine.system() == 'windows' 104 | additional_dep += cc.find_library('winmm') 105 | endif 106 | 107 | dep = declare_dependency( 108 | dependencies: [ libplacebo, build_deps ] + additional_dep, 109 | sources: ['window.c', 'utils.c'], 110 | link_with: apis, 111 | ) 112 | 113 | # Graphical demo programs 114 | executable('colors', 'colors.c', 115 | dependencies: [ dep, pl_clock, libm ], 116 | link_args: link_args, 117 | link_depends: link_depends, 118 | ) 119 | 120 | if sdl_image.found() 121 | executable('sdlimage', 'sdlimage.c', 122 | dependencies: [ dep, libm, sdl_image ], 123 | link_args: link_args, 124 | link_depends: link_depends, 125 | ) 126 | endif 127 | 128 | if ffmpeg_found 129 | plplay_deps = [ dep, pl_thread, pl_clock ] + ffmpeg_deps 130 | if nuklear.found() 131 | plplay_deps += nuklear 132 | endif 133 | if host_machine.system() == 'windows' 134 | plplay_deps += cc.find_library('shlwapi', required: true) 135 | endif 136 | plplay_sources = ['plplay.c', 'settings.c'] 137 | if host_machine.system() == 'windows' 138 | windows = import('windows') 139 | plplay_sources += windows.compile_resources(demos_rc, depends: version_h, 140 | include_directories: meson.project_source_root()/'win32') 141 | endif 142 | executable('plplay', plplay_sources, 143 | dependencies: plplay_deps, 144 | link_args: link_args, 145 | link_depends: link_depends, 146 | install: true, 147 | ) 148 | endif 149 | 150 | endif 151 | 152 | # Headless vulkan demos 153 | if components.get('vk-proc-addr') 154 | executable('video-filtering', 'video-filtering.c', 155 | dependencies: [ libplacebo, pl_clock, pl_thread, vulkan_loader, vulkan_headers ], 156 | c_args: '-O2', 157 | link_args: link_args, 158 | link_depends: link_depends, 159 | ) 160 | 161 | executable('multigpu-bench', 'multigpu-bench.c', 162 | dependencies: [ libplacebo, pl_clock, vulkan_loader, vulkan_headers ], 163 | c_args: '-O2', 164 | link_args: link_args, 165 | link_depends: link_depends, 166 | ) 167 | endif 168 | -------------------------------------------------------------------------------- /src/pl_thread_win32.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | typedef CRITICAL_SECTION pl_mutex; 28 | typedef CONDITION_VARIABLE pl_cond; 29 | 30 | static inline int pl_mutex_init_type_internal(pl_mutex *mutex, enum pl_mutex_type mtype) 31 | { 32 | (void) mtype; 33 | return !InitializeCriticalSectionEx(mutex, 0, 0); 34 | } 35 | 36 | #define pl_mutex_init_type(mutex, mtype) \ 37 | pl_assert(!pl_mutex_init_type_internal(mutex, mtype)) 38 | 39 | static inline int pl_mutex_destroy(pl_mutex *mutex) 40 | { 41 | DeleteCriticalSection(mutex); 42 | return 0; 43 | } 44 | 45 | static inline int pl_mutex_lock(pl_mutex *mutex) 46 | { 47 | EnterCriticalSection(mutex); 48 | return 0; 49 | } 50 | 51 | static inline int pl_mutex_unlock(pl_mutex *mutex) 52 | { 53 | LeaveCriticalSection(mutex); 54 | return 0; 55 | } 56 | 57 | static inline int pl_cond_init(pl_cond *cond) 58 | { 59 | InitializeConditionVariable(cond); 60 | return 0; 61 | } 62 | 63 | static inline int pl_cond_destroy(pl_cond *cond) 64 | { 65 | // condition variables are not destroyed 66 | (void) cond; 67 | return 0; 68 | } 69 | 70 | static inline int pl_cond_broadcast(pl_cond *cond) 71 | { 72 | WakeAllConditionVariable(cond); 73 | return 0; 74 | } 75 | 76 | static inline int pl_cond_signal(pl_cond *cond) 77 | { 78 | WakeConditionVariable(cond); 79 | return 0; 80 | } 81 | 82 | static inline int pl_cond_wait(pl_cond *cond, pl_mutex *mutex) 83 | { 84 | return !SleepConditionVariableCS(cond, mutex, INFINITE); 85 | } 86 | 87 | static inline int pl_cond_timedwait(pl_cond *cond, pl_mutex *mutex, uint64_t timeout) 88 | { 89 | if (timeout == UINT64_MAX) 90 | return pl_cond_wait(cond, mutex); 91 | 92 | timeout /= UINT64_C(1000000); 93 | if (timeout > INFINITE - 1) 94 | timeout = INFINITE - 1; 95 | 96 | BOOL bRet = SleepConditionVariableCS(cond, mutex, timeout); 97 | if (bRet == FALSE) 98 | { 99 | if (GetLastError() == ERROR_TIMEOUT) 100 | return ETIMEDOUT; 101 | else 102 | return EINVAL; 103 | } 104 | return 0; 105 | } 106 | 107 | typedef SRWLOCK pl_static_mutex; 108 | #define PL_STATIC_MUTEX_INITIALIZER SRWLOCK_INIT 109 | 110 | static inline int pl_static_mutex_lock(pl_static_mutex *mutex) 111 | { 112 | AcquireSRWLockExclusive(mutex); 113 | return 0; 114 | } 115 | 116 | static inline int pl_static_mutex_unlock(pl_static_mutex *mutex) 117 | { 118 | ReleaseSRWLockExclusive(mutex); 119 | return 0; 120 | } 121 | 122 | typedef HANDLE pl_thread; 123 | #define PL_THREAD_VOID unsigned __stdcall 124 | #define PL_THREAD_RETURN() return 0 125 | 126 | static inline int pl_thread_create(pl_thread *thread, 127 | PL_THREAD_VOID (*fun)(void *), 128 | void *__restrict arg) 129 | { 130 | *thread = (HANDLE) _beginthreadex(NULL, 0, fun, arg, 0, NULL); 131 | return *thread ? 0 : -1; 132 | } 133 | 134 | static inline int pl_thread_join(pl_thread thread) 135 | { 136 | DWORD ret = WaitForSingleObject(thread, INFINITE); 137 | if (ret != WAIT_OBJECT_0) 138 | return ret == WAIT_ABANDONED ? EINVAL : EDEADLK; 139 | CloseHandle(thread); 140 | return 0; 141 | } 142 | 143 | static inline bool pl_thread_sleep(double t) 144 | { 145 | // Time is expected in 100 nanosecond intervals. 146 | // Negative values indicate relative time. 147 | LARGE_INTEGER time = { .QuadPart = -(LONGLONG) (t * 1e7) }; 148 | 149 | if (time.QuadPart >= 0) 150 | return true; 151 | 152 | bool ret = false; 153 | 154 | #ifndef CREATE_WAITABLE_TIMER_HIGH_RESOLUTION 155 | # define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION 0x2 156 | #endif 157 | 158 | HANDLE timer = CreateWaitableTimerEx(NULL, NULL, 159 | CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, 160 | TIMER_ALL_ACCESS); 161 | 162 | // CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is supported in Windows 10 1803+, 163 | // retry without it. 164 | if (!timer) 165 | timer = CreateWaitableTimerEx(NULL, NULL, 0, TIMER_ALL_ACCESS); 166 | 167 | if (!timer) 168 | goto end; 169 | 170 | if (!SetWaitableTimer(timer, &time, 0, NULL, NULL, 0)) 171 | goto end; 172 | 173 | if (WaitForSingleObject(timer, INFINITE) != WAIT_OBJECT_0) 174 | goto end; 175 | 176 | ret = true; 177 | 178 | end: 179 | if (timer) 180 | CloseHandle(timer); 181 | return ret; 182 | } 183 | -------------------------------------------------------------------------------- /src/glsl/glslang_resources.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libplacebo. 3 | * 4 | * libplacebo is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * libplacebo is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with libplacebo. If not, see . 16 | */ 17 | 18 | #include "glslang.h" 19 | 20 | // Taken from glslang's examples, which apparently generally bases the choices 21 | // on OpenGL specification limits 22 | // 23 | // Note: This lives in a separate file so we can compile this struct using C99 24 | // designated initializers instead of using C++ struct initializers, because 25 | // the latter will break on every upstream struct extension. 26 | const TBuiltInResource DefaultTBuiltInResource = { 27 | .maxLights = 32, 28 | .maxClipPlanes = 6, 29 | .maxTextureUnits = 32, 30 | .maxTextureCoords = 32, 31 | .maxVertexAttribs = 64, 32 | .maxVertexUniformComponents = 4096, 33 | .maxVaryingFloats = 64, 34 | .maxVertexTextureImageUnits = 32, 35 | .maxCombinedTextureImageUnits = 80, 36 | .maxTextureImageUnits = 32, 37 | .maxFragmentUniformComponents = 4096, 38 | .maxDrawBuffers = 32, 39 | .maxVertexUniformVectors = 128, 40 | .maxVaryingVectors = 8, 41 | .maxFragmentUniformVectors = 16, 42 | .maxVertexOutputVectors = 16, 43 | .maxFragmentInputVectors = 15, 44 | .minProgramTexelOffset = -8, 45 | .maxProgramTexelOffset = 7, 46 | .maxClipDistances = 8, 47 | .maxComputeWorkGroupCountX = 65535, 48 | .maxComputeWorkGroupCountY = 65535, 49 | .maxComputeWorkGroupCountZ = 65535, 50 | .maxComputeWorkGroupSizeX = 1024, 51 | .maxComputeWorkGroupSizeY = 1024, 52 | .maxComputeWorkGroupSizeZ = 64, 53 | .maxComputeUniformComponents = 1024, 54 | .maxComputeTextureImageUnits = 16, 55 | .maxComputeImageUniforms = 8, 56 | .maxComputeAtomicCounters = 8, 57 | .maxComputeAtomicCounterBuffers = 1, 58 | .maxVaryingComponents = 60, 59 | .maxVertexOutputComponents = 64, 60 | .maxGeometryInputComponents = 64, 61 | .maxGeometryOutputComponents = 128, 62 | .maxFragmentInputComponents = 128, 63 | .maxImageUnits = 8, 64 | .maxCombinedImageUnitsAndFragmentOutputs = 8, 65 | .maxCombinedShaderOutputResources = 8, 66 | .maxImageSamples = 0, 67 | .maxVertexImageUniforms = 0, 68 | .maxTessControlImageUniforms = 0, 69 | .maxTessEvaluationImageUniforms = 0, 70 | .maxGeometryImageUniforms = 0, 71 | .maxFragmentImageUniforms = 8, 72 | .maxCombinedImageUniforms = 8, 73 | .maxGeometryTextureImageUnits = 16, 74 | .maxGeometryOutputVertices = 256, 75 | .maxGeometryTotalOutputComponents = 1024, 76 | .maxGeometryUniformComponents = 1024, 77 | .maxGeometryVaryingComponents = 64, 78 | .maxTessControlInputComponents = 128, 79 | .maxTessControlOutputComponents = 128, 80 | .maxTessControlTextureImageUnits = 16, 81 | .maxTessControlUniformComponents = 1024, 82 | .maxTessControlTotalOutputComponents = 4096, 83 | .maxTessEvaluationInputComponents = 128, 84 | .maxTessEvaluationOutputComponents = 128, 85 | .maxTessEvaluationTextureImageUnits = 16, 86 | .maxTessEvaluationUniformComponents = 1024, 87 | .maxTessPatchComponents = 120, 88 | .maxPatchVertices = 32, 89 | .maxTessGenLevel = 64, 90 | .maxViewports = 16, 91 | .maxVertexAtomicCounters = 0, 92 | .maxTessControlAtomicCounters = 0, 93 | .maxTessEvaluationAtomicCounters = 0, 94 | .maxGeometryAtomicCounters = 0, 95 | .maxFragmentAtomicCounters = 8, 96 | .maxCombinedAtomicCounters = 8, 97 | .maxAtomicCounterBindings = 1, 98 | .maxVertexAtomicCounterBuffers = 0, 99 | .maxTessControlAtomicCounterBuffers = 0, 100 | .maxTessEvaluationAtomicCounterBuffers = 0, 101 | .maxGeometryAtomicCounterBuffers = 0, 102 | .maxFragmentAtomicCounterBuffers = 1, 103 | .maxCombinedAtomicCounterBuffers = 1, 104 | .maxAtomicCounterBufferSize = 16384, 105 | .maxTransformFeedbackBuffers = 4, 106 | .maxTransformFeedbackInterleavedComponents = 64, 107 | .maxCullDistances = 8, 108 | .maxCombinedClipAndCullDistances = 8, 109 | .maxSamples = 4, 110 | .maxMeshOutputVerticesNV = 256, 111 | .maxMeshOutputPrimitivesNV = 512, 112 | .maxMeshWorkGroupSizeX_NV = 32, 113 | .maxMeshWorkGroupSizeY_NV = 1, 114 | .maxMeshWorkGroupSizeZ_NV = 1, 115 | .maxTaskWorkGroupSizeX_NV = 32, 116 | .maxTaskWorkGroupSizeY_NV = 1, 117 | .maxTaskWorkGroupSizeZ_NV = 1, 118 | .maxMeshViewCountNV = 4, 119 | .maxDualSourceDrawBuffersEXT = 1, 120 | 121 | .limits = { 122 | .nonInductiveForLoops = 1, 123 | .whileLoops = 1, 124 | .doWhileLoops = 1, 125 | .generalUniformIndexing = 1, 126 | .generalAttributeMatrixVectorIndexing = 1, 127 | .generalVaryingIndexing = 1, 128 | .generalSamplerIndexing = 1, 129 | .generalVariableIndexing = 1, 130 | .generalConstantMatrixVectorIndexing = 1, 131 | }, 132 | }; 133 | -------------------------------------------------------------------------------- /src/tests/options.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | 5 | static void count_cb(void *priv, pl_opt_data data) 6 | { 7 | int *num = priv; 8 | printf("Iterating over option: %s = %s\n", data->opt->key, data->text); 9 | (*num)++; 10 | } 11 | 12 | static void set_cb(void *priv, pl_opt_data data) 13 | { 14 | pl_options dst = priv; 15 | REQUIRE(pl_options_set_str(dst, data->opt->key, data->text)); 16 | } 17 | 18 | int main() 19 | { 20 | pl_log log = pl_test_logger(); 21 | pl_options test = pl_options_alloc(log); 22 | 23 | REQUIRE_STREQ(pl_options_save(test), ""); 24 | REQUIRE(pl_options_load(test, "")); 25 | REQUIRE_STREQ(pl_options_save(test), ""); 26 | 27 | pl_options_reset(test, &pl_render_fast_params); 28 | REQUIRE_STREQ(pl_options_save(test), ""); 29 | REQUIRE(pl_options_load(test, "preset=fast")); 30 | REQUIRE_STREQ(pl_options_save(test), ""); 31 | 32 | const char *def_opts = "upscaler=lanczos,downscaler=hermite,frame_mixer=oversample,sigmoid=yes,peak_detect=yes,dither=yes"; 33 | pl_options_reset(test, &pl_render_default_params); 34 | REQUIRE_STREQ(pl_options_save(test), def_opts); 35 | struct pl_options_t def_pre = *test; 36 | pl_options_reset(test, NULL); 37 | REQUIRE_STREQ(pl_options_save(test), ""); 38 | REQUIRE(pl_options_load(test, def_opts)); 39 | REQUIRE_STREQ(pl_options_save(test), def_opts); 40 | REQUIRE_MEMEQ(test, &def_pre, sizeof(*test)); 41 | pl_options_reset(test, NULL); 42 | REQUIRE(pl_options_load(test, "preset=default")); 43 | REQUIRE_STREQ(pl_options_save(test), def_opts); 44 | REQUIRE_MEMEQ(test, &def_pre, sizeof(*test)); 45 | 46 | int num = 0; 47 | pl_options_iterate(test, count_cb, &num); 48 | REQUIRE_CMP(num, ==, 6, "d"); 49 | 50 | pl_opt_data data; 51 | REQUIRE((data = pl_options_get(test, "tile_size"))); 52 | REQUIRE_STREQ(data->opt->key, "tile_size"); 53 | REQUIRE_CMP(*(int *) data->value, =, pl_render_default_params.tile_size, "d"); 54 | REQUIRE_STREQ(data->text, "32"); 55 | 56 | const char *hq_opts = "upscaler=ewa_lanczossharp,downscaler=hermite,frame_mixer=oversample,deband=yes,sigmoid=yes,peak_detect=yes,peak_percentile=99.99500274658203,contrast_recovery=0.30000001192092896,dither=yes"; 57 | // fallback can produce different precision 58 | const char *hq_opts2 = "upscaler=ewa_lanczossharp,downscaler=hermite,frame_mixer=oversample,deband=yes,sigmoid=yes,peak_detect=yes,peak_percentile=99.99500274658203125,contrast_recovery=0.30000001192092896,dither=yes"; 59 | 60 | pl_options_reset(test, &pl_render_high_quality_params); 61 | const char *opts = pl_options_save(test); 62 | if (!strcmp(opts, hq_opts2)) 63 | hq_opts = hq_opts2; 64 | REQUIRE_STREQ(opts, hq_opts); 65 | struct pl_options_t hq_pre = *test; 66 | pl_options_reset(test, NULL); 67 | REQUIRE_STREQ(pl_options_save(test), ""); 68 | REQUIRE(pl_options_load(test, hq_opts)); 69 | REQUIRE_STREQ(pl_options_save(test), hq_opts); 70 | REQUIRE_MEMEQ(test, &hq_pre, sizeof(*test)); 71 | REQUIRE(pl_options_load(test, "preset=high_quality")); 72 | REQUIRE_STREQ(pl_options_save(test), hq_opts); 73 | REQUIRE_MEMEQ(test, &hq_pre, sizeof(*test)); 74 | 75 | pl_options test2 = pl_options_alloc(log); 76 | pl_options_iterate(test, set_cb, test2); 77 | REQUIRE_STREQ(pl_options_save(test), pl_options_save(test2)); 78 | pl_options_free(&test2); 79 | 80 | // Test custom scalers 81 | pl_options_reset(test, pl_render_params( 82 | .upscaler = &(struct pl_filter_config) { 83 | .kernel = &pl_filter_function_jinc, 84 | .window = &pl_filter_function_jinc, 85 | .radius = 4.0, 86 | .polar = true, 87 | }, 88 | )); 89 | const char *jinc4_opts = "upscaler=custom,upscaler_kernel=jinc,upscaler_window=jinc,upscaler_radius=4,upscaler_polar=yes"; 90 | REQUIRE_STREQ(pl_options_save(test), jinc4_opts); 91 | struct pl_options_t jinc4_pre = *test; 92 | pl_options_reset(test, NULL); 93 | REQUIRE(pl_options_load(test, "upscaler=custom,upscaler_preset=ewa_lanczos,upscaler_radius=4.0,upscaler_clamp=0.0")); 94 | REQUIRE_STREQ(pl_options_save(test), jinc4_opts); 95 | REQUIRE_MEMEQ(test, &jinc4_pre, sizeof(*test)); 96 | 97 | // Test params presets 98 | pl_options_reset(test, NULL); 99 | REQUIRE(pl_options_load(test, "cone=yes,cone_preset=deuteranomaly")); 100 | REQUIRE_STREQ(pl_options_save(test), "cone=yes,cones=m,cone_strength=0.5"); 101 | 102 | // Test error paths 103 | pl_options bad = pl_options_alloc(NULL); 104 | REQUIRE(!pl_options_load(bad, "scale_preset=help")); 105 | REQUIRE(!pl_options_load(bad, "dither_method=invalid")); 106 | REQUIRE(!pl_options_load(bad, "lut_entries=-1")); 107 | REQUIRE(!pl_options_load(bad, "deband_iterations=100")); 108 | REQUIRE(!pl_options_load(bad, "tone_lut_size=abc")); 109 | REQUIRE(!pl_options_load(bad, "show_clipping=hello")); 110 | REQUIRE(!pl_options_load(bad, "brightness=2.0")); 111 | REQUIRE(!pl_options_load(bad, "gamma=oops")); 112 | REQUIRE(!pl_options_load(bad, "invalid")); 113 | REQUIRE(!pl_options_load(bad, "=")); 114 | REQUIRE(!pl_options_load(bad, "preset==bar")); 115 | REQUIRE(!pl_options_load(bad, "peak_percentile=E8203125")); 116 | REQUIRE(!pl_options_get(bad, "invalid")); 117 | REQUIRE_STREQ(pl_options_save(bad), ""); 118 | pl_options_free(&bad); 119 | 120 | pl_options_free(&test); 121 | pl_log_destroy(&log); 122 | return 0; 123 | } 124 | --------------------------------------------------------------------------------