diff --git a/local/recipes/libs/mesa/recipe.toml b/local/recipes/libs/mesa/recipe.toml index db273176ce..d1cae2c3c6 100644 --- a/local/recipes/libs/mesa/recipe.toml +++ b/local/recipes/libs/mesa/recipe.toml @@ -93,8 +93,8 @@ cookbook_meson \ -Dglx=disabled \ -Dgallium-drivers=softpipe,llvmpipe,virgl,iris,radeonsi \ -Dllvm=enabled \ - -Dplatforms=wayland \ - -Degl-native-platform=surfaceless \ + -Dplatforms=wayland,redox \ + -Degl-native-platform=redox \ -Dshader-cache=disabled \ -Dc_args="['-include','alloca.h','-DCLOCK_MONOTONIC_RAW=(CLOCK_MONOTONIC+1000)','-Wno-error=implicit-function-declaration','-Wno-error=missing-prototypes','-Wno-error=return-type','-Wno-error=empty-body','-Wno-error=incompatible-pointer-types','-Wno-error=int-conversion','-Wno-error=format','-Wno-error','-std=gnu11','-Dstatic_assert=_Static_assert']" \ -Dcpp_args="['-include','alloca.h','-Wno-error=implicit-function-declaration','-Wno-error=missing-prototypes','-Wno-error=return-type','-Wno-error=empty-body','-Wno-error=incompatible-pointer-types','-Wno-error=int-conversion','-Wno-error=format','-Wno-error']" \ diff --git a/local/recipes/libs/mesa/source/meson.build b/local/recipes/libs/mesa/source/meson.build new file mode 100644 index 0000000000..9d81d1e148 --- /dev/null +++ b/local/recipes/libs/mesa/source/meson.build @@ -0,0 +1,2582 @@ +# Copyright © 2017-2020 Intel Corporation +# SPDX-License-Identifier: MIT + +project( + 'mesa', + ['c', 'cpp'], + version : files('VERSION'), + license : 'MIT', + meson_version : '>= 1.4.0', + default_options : [ + 'buildtype=debugoptimized', + 'b_ndebug=if-release', + 'c_std=c11', + 'cpp_std=c++17', + 'rust_std=2021', + 'build.rust_std=2021', + ], +) + +project_version = meson.project_version() + +if host_machine.system() == 'darwin' + add_languages('objc', native : false) + libname_prefix = 'lib' + libname_suffix = 'dylib' +elif host_machine.system() == 'windows' + libname_prefix = '' + libname_suffix = 'dll' +else + libname_prefix = 'lib' + libname_suffix = 'so' +endif + +cc = meson.get_compiler('c') +cpp = meson.get_compiler('cpp') +fs = import('fs') + +sizeof_pointer = cc.sizeof('void*').to_string() + +null_dep = dependency('', required : false) + +if get_option('layout') != 'mirror' + error('`mirror` is the only build directory layout supported') +endif + +if get_option('b_lto') + project_version += '-lto' + # LTO causes random issues that are nearly impossible to debug, so bug reports from LTO builds have been + # rejected for a long time now. + # Explicitly reject LTO when building to prevent more people from running into bugs that end up inactionable. + if not get_option('allow-broken-lto') + error('Building Mesa with LTO is not supported. Please disable LTO for building Mesa.') + endif +endif + +with_llvm = get_option('llvm') +amd_with_llvm = with_llvm.allowed() and get_option('amd-use-llvm') + +with_mesa_debug = get_option('buildtype') == 'debug' + +# This means the final value of b_ndebug==true +with_mesa_ndebug = get_option('b_ndebug') == 'true' or (get_option('buildtype') == 'release' and get_option('b_ndebug') == 'if-release') + +# We need to set -fmacro-prefix-map properly for driver CL reproducability. +relative_dir = fs.relative_to(meson.project_source_root(), meson.project_build_root()) + +cl_args = [ + '-fmacro-prefix-map=@0@/='.format(relative_dir), + '-fmacro-prefix-map=@0@/='.format(meson.project_source_root()), + '-fmacro-prefix-map=@0@/='.format(meson.project_build_root()), + + # Set the OpenCL standard to CL 2.0, enabling everything at a frontend. + # Drivers may not actually support everything but that's ok. + '-cl-std=cl2.0', + '-D__OPENCL_VERSION__=200', + + # Declare supported clang builtins since we don't autodetect for OpenCL + '-DHAVE___BUILTIN_FFS', + '-DHAVE___BUILTIN_CLZ', +] + +if with_mesa_ndebug + cl_args += ['-DNDEBUG'] +endif + +# Arguments for the preprocessor, put these in a separate array from the C and +# C++ (cpp in meson terminology) arguments since they need to be added to the +# default arguments for both C and C++. +pre_args = [ + '-D__STDC_CONSTANT_MACROS', + '-D__STDC_FORMAT_MACROS', + '-D__STDC_LIMIT_MACROS', + '-DPACKAGE_VERSION="@0@"'.format(project_version), + '-DPACKAGE_BUGREPORT="https://gitlab.freedesktop.org/mesa/mesa/-/issues"', +] +# Arguments for c or cpp compiler, can be compiler options +c_cpp_args = [] + +c_args = [] +cpp_args = [] + +with_moltenvk_dir = get_option('moltenvk-dir') +with_vulkan_icd_dir = get_option('vulkan-icd-dir') +with_tests = get_option('build-tests') +with_glcpp_tests = get_option('enable-glcpp-tests') +with_radv_tests = get_option('build-radv-tests') +with_aco_tests = get_option('build-aco-tests') +with_glx_read_only_text = get_option('glx-read-only-text') +with_glx_direct = get_option('glx-direct') +with_vulkan_overlay_layer = get_option('vulkan-layers').contains('overlay') +with_vulkan_device_select_layer = get_option('vulkan-layers').contains('device-select') +with_vulkan_screenshot_layer = get_option('vulkan-layers').contains('screenshot') +with_vulkan_vram_report_limit_layer = get_option('vulkan-layers').contains('vram-report-limit') +with_vulkan_anti_lag_layer = get_option('vulkan-layers').contains('anti-lag') +with_tools = get_option('tools') +if with_tools.contains('all') + with_tools = [ + 'asahi', + 'dlclose-skip', + 'drm-shim', + 'etnaviv', + 'freedreno', + 'glsl', + 'imagination', + 'intel', + 'intel-ui', + 'lima', + 'nir', + 'nouveau', + 'panfrost', + 'zink', + ] +endif + +with_any_vulkan_layers = get_option('vulkan-layers').length() != 0 +with_intel_tools = with_tools.contains('intel') or with_tools.contains('intel-ui') +with_imgui = with_intel_tools or with_vulkan_overlay_layer + +dri_drivers_path = get_option('dri-drivers-path') +if dri_drivers_path == '' + dri_drivers_path = join_paths(get_option('prefix'), get_option('libdir'), 'dri') +endif + +gbm_backends_path = get_option('gbm-backends-path') +if gbm_backends_path == '' + gbm_backends_path = join_paths(get_option('prefix'), get_option('libdir'), 'gbm') +endif + +with_opengl = get_option('opengl') +with_gles1 = get_option('gles1').allowed() +with_gles2 = get_option('gles2').allowed() + +pre_args += '-DHAVE_OPENGL=@0@'.format(with_opengl.to_int()) +pre_args += '-DHAVE_OPENGL_ES_1=@0@'.format(with_gles1.to_int()) +pre_args += '-DHAVE_OPENGL_ES_2=@0@'.format(with_gles2.to_int()) + +with_any_opengl = with_opengl or with_gles1 or with_gles2 + +system_has_kms_drm = ['openbsd', 'netbsd', 'freebsd', 'gnu/kfreebsd', 'dragonfly', 'linux', 'sunos', 'android', 'managarm', 'redox'].contains(host_machine.system()) + +gallium_drivers = get_option('gallium-drivers') +if gallium_drivers.contains('auto') + if system_has_kms_drm + # TODO: Sparc + # TODO: handle llvm being disabled or auto when setting default_drivers + if ['x86', 'x86_64'].contains(host_machine.cpu_family()) + gallium_drivers = [ + 'r300', 'r600', 'radeonsi', 'nouveau', 'virgl', 'svga', 'llvmpipe', 'softpipe', + 'iris', 'crocus', 'i915', 'zink' + ] + elif ['aarch64'].contains(host_machine.cpu_family()) + gallium_drivers = [ + 'v3d', 'vc4', 'freedreno', 'etnaviv', 'nouveau', 'svga', + 'tegra', 'virgl', 'lima', 'panfrost', 'llvmpipe', 'softpipe', 'iris', + 'zink', 'asahi' + ] + elif ['arm'].contains(host_machine.cpu_family()) + gallium_drivers = [ + 'v3d', 'vc4', 'freedreno', 'etnaviv', 'nouveau', 'svga', + 'tegra', 'virgl', 'lima', 'panfrost', 'llvmpipe', 'softpipe', 'iris', + 'zink' + ] + elif ['mips', 'mips64', 'ppc', 'ppc64', 'riscv32', 'riscv64'].contains(host_machine.cpu_family()) + gallium_drivers = [ + 'r300', 'r600', 'radeonsi', 'nouveau', 'virgl', 'llvmpipe', 'softpipe', 'zink' + ] + elif ['loongarch64'].contains(host_machine.cpu_family()) + gallium_drivers = [ + 'r300', 'r600', 'radeonsi', 'nouveau', 'virgl', 'etnaviv', 'llvmpipe', 'softpipe', 'zink' + ] + else + error('Unknown architecture @0@. Please pass -Dgallium-drivers to set driver options. Patches gladly accepted to fix this.'.format( + host_machine.cpu_family())) + endif + elif ['windows'].contains(host_machine.system()) + gallium_drivers = ['llvmpipe', 'softpipe', 'zink', 'd3d12'] + elif ['darwin', 'cygwin', 'haiku'].contains(host_machine.system()) + gallium_drivers = ['llvmpipe', 'softpipe'] + else + error('Unknown OS @0@. Please pass -Dgallium-drivers to set driver options. Patches gladly accepted to fix this.'.format( + host_machine.system())) + endif +elif gallium_drivers.contains('all') + gallium_drivers = [ + 'r300', 'r600', 'radeonsi', 'crocus', 'v3d', 'vc4', 'freedreno', 'etnaviv', 'i915', + 'nouveau', 'svga', 'tegra', 'virgl', 'lima', 'panfrost', 'llvmpipe', 'softpipe', 'iris', + 'zink', 'd3d12', 'asahi', 'rocket', 'ethosu' + ] +endif + +with_amdgpu_virtio = get_option('amdgpu-virtio') + +with_gallium_radeonsi = gallium_drivers.contains('radeonsi') +with_gallium_r300 = gallium_drivers.contains('r300') +with_gallium_r600 = gallium_drivers.contains('r600') +with_gallium_nouveau = gallium_drivers.contains('nouveau') +with_gallium_freedreno = gallium_drivers.contains('freedreno') +with_gallium_softpipe = gallium_drivers.contains('softpipe') +with_gallium_llvmpipe = gallium_drivers.contains('llvmpipe') +with_gallium_vc4 = gallium_drivers.contains('vc4') +with_gallium_v3d = gallium_drivers.contains('v3d') +with_gallium_panfrost = gallium_drivers.contains('panfrost') +with_gallium_etnaviv = gallium_drivers.contains('etnaviv') +with_gallium_tegra = gallium_drivers.contains('tegra') +with_gallium_crocus = gallium_drivers.contains('crocus') +with_gallium_iris = gallium_drivers.contains('iris') +with_gallium_i915 = gallium_drivers.contains('i915') +with_gallium_svga = gallium_drivers.contains('svga') +with_gallium_virgl = gallium_drivers.contains('virgl') +with_gallium_lima = gallium_drivers.contains('lima') +with_gallium_zink = gallium_drivers.contains('zink') +with_gallium_d3d12 = gallium_drivers.contains('d3d12') +with_gallium_asahi = gallium_drivers.contains('asahi') +with_gallium_rocket = gallium_drivers.contains('rocket') +with_gallium_ethosu = gallium_drivers.contains('ethosu') +foreach gallium_driver : gallium_drivers + pre_args += '-DHAVE_@0@'.format(gallium_driver.to_upper()) +endforeach +draw_with_llvm = with_llvm.allowed() and get_option('draw-use-llvm') +with_llvm = with_llvm \ + .enable_if(with_gallium_i915, error_message : 'i915 Gallium driver requires LLVM for vertex shaders') \ + .enable_if(with_gallium_llvmpipe, error_message : 'LLVMPipe Gallium driver requires LLVM') \ + .enable_if(with_gallium_r300 and draw_with_llvm, error_message : 'R300 Gallium driver requires LLVM for vertex shaders on IGP parts') \ + .enable_if(with_gallium_radeonsi and amd_with_llvm, error_message : 'RadeonSI Gallium driver configured to require LLVM') + +# compatibility for "swrast" as an internal-ish driver name +with_gallium_swrast = with_gallium_softpipe or with_gallium_llvmpipe +if with_gallium_swrast + pre_args += '-DHAVE_SWRAST' +endif + +with_gallium = gallium_drivers.length() != 0 +with_gallium_drm = system_has_kms_drm and [ + with_gallium_asahi, + with_gallium_etnaviv, + with_gallium_freedreno, + with_gallium_lima, + with_gallium_panfrost, + with_gallium_v3d, + with_gallium_vc4, +].contains(true) + +_vulkan_drivers = get_option('vulkan-drivers') +if _vulkan_drivers.contains('auto') + # TODO: handle swrat driver if llvm is auto/disabled + if system_has_kms_drm + if host_machine.cpu_family().startswith('x86') + _vulkan_drivers = ['amd', 'intel', 'intel_hasvk', 'nouveau', 'swrast'] + elif ['aarch64'].contains(host_machine.cpu_family()) + _vulkan_drivers = ['swrast', 'intel', 'panfrost', 'freedreno', 'asahi'] + elif ['arm'].contains(host_machine.cpu_family()) + _vulkan_drivers = ['swrast', 'intel', 'panfrost', 'freedreno'] + elif ['mips', 'mips64', 'ppc', 'ppc64', 'riscv32', 'riscv64'].contains(host_machine.cpu_family()) + _vulkan_drivers = ['amd', 'swrast'] + elif ['loongarch64'].contains(host_machine.cpu_family()) + _vulkan_drivers = ['amd', 'swrast'] + else + error('Unknown architecture @0@. Please pass -Dvulkan-drivers to set driver options. Patches gladly accepted to fix this.'.format( + host_machine.cpu_family())) + endif + elif ['darwin', 'windows', 'cygwin', 'haiku'].contains(host_machine.system()) + # No vulkan driver supports windows or macOS currently + _vulkan_drivers = [] + else + error('Unknown OS @0@. Please pass -Dvulkan-drivers to set driver options. Patches gladly accepted to fix this.'.format( + host_machine.system())) + endif +elif _vulkan_drivers.contains('all') + # Build every vulkan driver regardless of architecture. + _vulkan_drivers = ['amd', 'intel', 'intel_hasvk', 'swrast', + 'freedreno', 'panfrost', 'virtio', 'broadcom', + 'imagination', 'microsoft-experimental', + 'nouveau', 'asahi', 'gfxstream', 'kosmickrisp'] +endif + +with_intel_vk = _vulkan_drivers.contains('intel') +with_intel_hasvk = _vulkan_drivers.contains('intel_hasvk') +with_amd_vk = _vulkan_drivers.contains('amd') +with_freedreno_vk = _vulkan_drivers.contains('freedreno') +with_panfrost_vk = _vulkan_drivers.contains('panfrost') +with_swrast_vk = _vulkan_drivers.contains('swrast') +with_virtio_vk = _vulkan_drivers.contains('virtio') +with_broadcom_vk = _vulkan_drivers.contains('broadcom') +with_imagination_vk = _vulkan_drivers.contains('imagination') +with_imagination_srv = get_option('imagination-srv') +with_microsoft_vk = _vulkan_drivers.contains('microsoft-experimental') +with_nouveau_vk = _vulkan_drivers.contains('nouveau') +with_asahi_vk = _vulkan_drivers.contains('asahi') +with_gfxstream_vk = _vulkan_drivers.contains('gfxstream') +with_kosmickrisp_vk = _vulkan_drivers.contains('kosmickrisp') +with_any_vk = _vulkan_drivers.length() != 0 + +with_llvm = with_llvm \ + .enable_if(with_swrast_vk, error_message : 'Lavapipe Vulkan driver requires LLVM') \ + .enable_if(with_amd_vk and with_aco_tests, error_message : 'RadV ACO tests require LLVM') + +with_any_llvmpipe = with_gallium_llvmpipe or with_swrast_vk +with_gallium_or_lvp = with_gallium or with_swrast_vk + +freedreno_kmds = get_option('freedreno-kmds') +if freedreno_kmds.length() != 0 and freedreno_kmds != [ 'msm' ] and with_freedreno_vk + if freedreno_kmds.contains('msm') + warning('Turnip with the DRM KMD will require libdrm to always be present at runtime which may not always be the case on platforms such as Android.') + elif with_gallium_drm + warning('Turnip is forced to link with libdrm when built alongside Gallium DRM drivers which platforms such as Android may not have available at runtime.') + elif _vulkan_drivers != [ 'freedreno' ] + warning('Turnip is forced to link with libdrm when built alongside other Vulkan drivers which platforms such as Android may not have available at runtime.') + else + # If DRM support isn't needed, we can get rid of it since linking + # to libdrm can be a potential compatibility hazard. + system_has_kms_drm = false + endif +endif + +# This has to be below the freedreno_kmds check since that can change system_has_kms_drm +with_gallium_kmsro = with_gallium_drm or (system_has_kms_drm and with_gallium_zink) + +pre_args += ['-DMESA_SYSTEM_HAS_KMS_DRM=@0@'.format(system_has_kms_drm.to_int())] +if host_machine.cpu_family() == 'x86' and with_glx_read_only_text + pre_args += ['-DGLX_X86_READONLY_TEXT'] +endif + +with_intel_virtio = get_option('intel-virtio-experimental') +if with_intel_virtio + pre_args += '-DHAVE_INTEL_VIRTIO' +endif + +with_vdrm = [ + with_amdgpu_virtio, + freedreno_kmds.contains('virtio'), + with_gallium_asahi, + with_asahi_vk, + with_intel_virtio, +].contains(true) + +with_dri = false +if with_gallium and (system_has_kms_drm or (freedreno_kmds.contains('kgsl') and with_gallium_zink)) + _glx = get_option('glx') + _egl = get_option('egl') + if _glx == 'dri' or _egl.enabled() or (_glx == 'disabled' and _egl.allowed()) + with_dri = true + endif +endif + +with_any_broadcom = [ + with_gallium_vc4, + with_gallium_v3d, + with_broadcom_vk, +].contains(true) + +with_intel_vk_rt = get_option('intel-rt') \ + .disable_auto_if(not with_intel_vk) \ + .disable_if(sizeof_pointer != '8', \ + error_message: 'Intel Ray Tracing requires 64-bit architectures') \ + .allowed() + +with_any_intel = [ + with_gallium_crocus, + with_gallium_i915, + with_gallium_iris, + with_intel_hasvk, + with_intel_tools, + with_intel_vk, +].contains(true) +with_any_nouveau = with_gallium_nouveau or with_nouveau_vk + +# needed in the loader +if with_nouveau_vk + pre_args += '-DHAVE_NVK' +endif + +if with_gallium_tegra and not with_gallium_nouveau + error('tegra driver requires nouveau driver') +endif +if with_aco_tests and not with_amd_vk + error('ACO tests require Radv') +endif +if with_aco_tests and not with_tools.contains('drm-shim') + error('ACO tests require AMDGPU drm-shim') +endif +if with_radv_tests and not with_amd_vk + error('RADV tests require RADV') +endif +if with_radv_tests and not with_tools.contains('drm-shim') + error('RADV tests require AMDGPU drm-shim') +endif + +with_microsoft_clc = get_option('microsoft-clc').enabled() +with_spirv_to_dxil = get_option('spirv-to-dxil') + +if host_machine.system() == 'darwin' + with_dri_platform = 'apple' + pre_args += '-DBUILDING_MESA' +elif ['windows', 'cygwin'].contains(host_machine.system()) + with_dri_platform = 'windows' +elif system_has_kms_drm + with_dri_platform = 'drm' +elif host_machine.system() == 'gnu' or (freedreno_kmds.contains('kgsl') and with_gallium_zink) + with_dri_platform = 'pseudo-drm' +else + # FIXME: haiku doesn't use dri, and xlib doesn't use dri, probably should + # assert here that one of those cases has been met. + # FIXME: illumos ends up here as well + with_dri_platform = 'none' +endif + +with_vulkan_beta = get_option('vulkan-beta') +if host_machine.system() == 'darwin' + #macOS seems to need beta extensions to build for now: + with_vulkan_beta = true +endif +if with_vulkan_beta + pre_args += '-DVK_ENABLE_BETA_EXTENSIONS' +endif + +_codecs = get_option('video-codecs') +patent_codecs = ['vc1dec', 'h264dec', 'h264enc', 'h265dec', 'h265enc'] +free_codecs = ['av1dec', 'av1enc', 'vp9dec', 'mpeg12dec', 'jpegdec'] +all_codecs = patent_codecs + free_codecs + +if _codecs.contains('all') + _codecs = all_codecs +elif _codecs.contains('all_free') + _codecs += free_codecs +endif +foreach c : all_codecs + pre_args += '-DVIDEO_CODEC_@0@=@1@'.format(c.to_upper(), _codecs.contains(c).to_int()) +endforeach + +_platforms = get_option('platforms') +if _platforms.contains('auto') + if system_has_kms_drm + _platforms = ['x11', 'wayland'] + elif host_machine.system() == 'cygwin' + _platforms = ['x11'] + elif host_machine.system() == 'haiku' + _platforms = ['haiku'] + elif host_machine.system() == 'windows' + _platforms = ['windows'] + elif host_machine.system() == 'darwin' + _platforms = ['x11', 'macos'] + else + error('Unknown OS @0@. Please pass -Dplatforms to set platforms. Patches gladly accepted to fix this.'.format( + host_machine.system())) + endif +endif + +with_platform_android = _platforms.contains('android') +with_platform_x11 = _platforms.contains('x11') +with_platform_xcb = _platforms.contains('xcb') +with_platform_wayland = _platforms.contains('wayland') +with_platform_haiku = _platforms.contains('haiku') +with_platform_windows = _platforms.contains('windows') +with_platform_macos = _platforms.contains('macos') +with_platform_redox = _platforms.contains('redox') + +with_gfxstream_emulated_android = with_gfxstream_vk and with_platform_android and host_machine.system() == 'linux' +if with_gfxstream_emulated_android + # When building the android_stub with a non-Bionic sysroot/target, we to have + # define macros that are typically in when using a Bionic + # sysroot + pre_args += '-D__INTRODUCED_IN(api_level)=' + + # We don't want to build Vulkan common code when running the emulated + # gfxstream Android platform. This causes issues when building in + # tandem with host Lavapipe. + # + # TODO(gfxstream-devs@): Modifying the user-provided platform can be removed + # when Cuttlefish moves it's host library builds off Soong, and on to Github. + # For now, add basic sanity checks before modifying user arguments. + if _platforms.contains('android') and _platforms.length() != 1 + error('You cannot enable platform "android" at the same time as another platform') + endif + _platforms = [] + with_platform_android = false +endif + +with_glx = get_option('glx') +if with_glx == 'auto' + if not with_opengl + with_glx = 'disabled' + elif with_platform_android + with_glx = 'disabled' + elif with_dri + with_glx = 'dri' + elif with_platform_haiku + with_glx = 'disabled' + elif host_machine.system() == 'windows' + with_glx = 'disabled' + elif with_gallium + # Even when building just gallium drivers the user probably wants dri + with_glx = 'dri' + elif with_platform_x11 and with_any_opengl and not with_any_vk + # The automatic behavior should not be to turn on xlib based glx when + # building only vulkan drivers + with_glx = 'xlib' + else + with_glx = 'disabled' + endif +endif +if with_glx == 'dri' + if with_gallium + with_dri = true + endif +endif + +if not with_opengl and with_glx != 'disabled' + error('Building GLX without OpenGL is not supported.') +endif + +if not (with_dri or with_gallium or with_glx != 'disabled') + with_gles1 = false + with_gles2 = false + with_opengl = false + with_any_opengl = false +endif + +with_gbm = get_option('gbm') \ + .require(system_has_kms_drm, error_message : 'GBM only supports DRM/KMS platforms') \ + .disable_auto_if(not with_dri) \ + .allowed() + +with_xlib_lease = get_option('xlib-lease') \ + .require(with_platform_x11 and (system_has_kms_drm or with_dri_platform == 'apple' or with_dri_platform == 'pseudo-drm'), error_message : 'xlib-lease requires X11 and KMS/DRM support') \ + .allowed() + +with_egl = get_option('egl') \ + .require(with_platform_windows or with_platform_haiku or with_dri or with_platform_android, error_message : 'EGL requires DRI, Haiku, Windows or Android') \ + .require(with_glx != 'xlib', error_message :'EGL requires DRI, but GLX is being built with xlib support') \ + .disable_auto_if(with_platform_haiku) \ + .allowed() + +if with_gbm + pre_args += '-DHAVE_LIBGBM' +endif + +if with_egl + _platforms += 'surfaceless' + if with_gbm and not with_platform_android + _platforms += 'drm' + endif + + egl_native_platform = get_option('egl-native-platform') + if egl_native_platform.contains('auto') + egl_native_platform = _platforms[0] + endif +endif + +if with_egl and not _platforms.contains(egl_native_platform) + error('-Degl-native-platform does not specify an enabled platform') +endif + +if 'x11' in _platforms + _platforms += 'xcb' +endif + +foreach platform : _platforms + pre_args += '-DHAVE_@0@_PLATFORM'.format(platform.to_upper()) +endforeach + +if with_platform_android and get_option('platform-sdk-version') >= 29 + # By default the NDK compiler, at least, emits emutls references instead of + # ELF TLS, even when building targeting newer API levels. Make it actually do + # ELF TLS instead. + c_cpp_args += '-fno-emulated-tls' + add_project_link_arguments('-Wl,-plugin-opt=-emulated-tls=0', language: ['c', 'cpp']) +endif + +# -mtls-dialect=gnu2 speeds up non-initial-exec TLS significantly but requires +# full toolchain (including libc) support. +have_mtls_dialect = false +foreach c_arg : get_option('c_args') + if c_arg.startswith('-mtls-dialect=') + have_mtls_dialect = true + break + endif +endforeach +if not have_mtls_dialect + # need .run to check libc support. meson aborts when calling .run when + # cross-compiling, but because this is just an optimization we can skip it + if meson.is_cross_build() and not meson.can_run_host_binaries() + warning('cannot auto-detect -mtls-dialect when cross-compiling, using compiler default') + elif host_machine.system() == 'freebsd' + warning('cannot use -mtls-dialect for FreeBSD, using compiler default') + else + # The way to specify the TLSDESC dialect is architecture-specific. + # We probe both because there is not a fallback guaranteed to work for all + # future architectures. + foreach tlsdesc_arg : ['-mtls-dialect=gnu2', '-mtls-dialect=desc'] + # -fpic to force dynamic tls, otherwise TLS relaxation defeats check + tlsdesc_test = cc.run('int __thread x; int main() { return x; }', + args: [tlsdesc_arg, '-fpic'], + name: tlsdesc_arg) + if tlsdesc_test.returncode() == 0 and ( + # check for lld 13 bug: https://gitlab.freedesktop.org/mesa/mesa/-/issues/5665 + host_machine.cpu_family() != 'x86_64' or + # get_linker_id misses LDFLAGS=-fuse-ld=lld: https://github.com/mesonbuild/meson/issues/6377 + #cc.get_linker_id() != 'ld.lld' or + cc.links('''int __thread x; int y; int main() { __asm__( + "leaq x@TLSDESC(%rip), %rax\n" + "movq y@GOTPCREL(%rip), %rdx\n" + "call *x@TLSCALL(%rax)\n"); }''', name: 'split TLSDESC') + ) + c_cpp_args += tlsdesc_arg + break + endif + endforeach + endif +endif + +if with_glx != 'disabled' + if not (with_platform_x11 and with_any_opengl) + error('Cannot build GLX support without X11 platform support and at least one OpenGL API') + elif with_glx == 'xlib' + if not with_gallium + error('xlib based GLX requires at least one gallium driver') + elif not with_gallium_swrast + error('xlib based GLX requires softpipe or llvmpipe.') + elif with_dri + error('xlib conflicts with any dri driver') + endif + endif +endif + +_glvnd = get_option('glvnd') \ + .require(not with_platform_windows, + error_message: 'glvnd cannot be used on Windows') \ + .require(with_glx != 'xlib', + error_message: 'Cannot build glvnd support for GLX that is not DRI based.') \ + .require(with_glx != 'disabled' or with_egl, + error_message: 'glvnd requires DRI based GLX and/or EGL') \ + .require(get_option('egl-lib-suffix') == '', + error_message: '''EGL lib suffix can't be used with libglvnd''') +dep_glvnd = dependency('libglvnd', version : '>= 1.3.2', required : _glvnd) +with_glvnd = dep_glvnd.found() +pre_args += '-DUSE_LIBGLVND=@0@'.format(with_glvnd.to_int()) +glvnd_vendor_name = get_option('glvnd-vendor-name') + +if with_vulkan_icd_dir == '' + with_vulkan_icd_dir = join_paths(get_option('datadir'), 'vulkan/icd.d') +endif + +with_dri2 = (with_dri or with_any_vk) and (with_dri_platform == 'drm' or with_dri_platform == 'apple' or with_dri_platform == 'pseudo-drm') + +if with_dri + if with_glx == 'disabled' and not with_egl and not with_gbm + error('building dri drivers require at least one windowing system') + endif +endif + +dep_dxheaders = null_dep +if with_gallium_d3d12 or with_microsoft_clc or with_microsoft_vk or (with_any_vk and host_machine.system() == 'windows') + dep_dxheaders = dependency('directx-headers', required : false) + if not dep_dxheaders.found() + dep_dxheaders = dependency('DirectX-Headers', + version : '>= 1.619.1', + fallback : ['DirectX-Headers', 'dep_dxheaders'], + required : with_gallium_d3d12 or with_microsoft_vk + ) + endif +endif + +if dep_dxheaders.found() + if (dep_dxheaders.version().version_compare('>= 1.700.0')) + pre_args += '-DUSE_D3D12_PREVIEW_HEADERS=1' + else + pre_args += '-DUSE_D3D12_PREVIEW_HEADERS=0' + endif +endif + +_with_gallium_d3d12_video = get_option('gallium-d3d12-video') +with_gallium_d3d12_video = false +if with_gallium_d3d12 and not _with_gallium_d3d12_video.disabled() + with_gallium_d3d12_video = true + pre_args += '-DHAVE_GALLIUM_D3D12_VIDEO' +endif + +with_bvh = with_amd_vk or with_intel_vk or with_swrast_vk or with_freedreno_vk +_glslang_preamble_version = '12.2' + +# GLSL has interesting version output and Meson doesn't parse it correctly as of +# Meson 1.4.0 +prog_glslang = find_program( + 'glslangValidator', + native : true, + required : with_vulkan_overlay_layer or with_aco_tests or with_bvh +) + +if prog_glslang.found() + _glslang_version = run_command(prog_glslang, ['--version'], check : false).stdout().split(':')[2] + # Check if glslang has depfile support. Support was added in 11.3.0, but + # Windows path support was broken until 11.9.0. + # + # It is intentional to check the build machine, since we need to ensure that + # glslang will output valid paths on the build platform + _glslang_check = build_machine.system() == 'windows' ? '>= 11.9.0' : '>= 11.3.0' + if _glslang_version.version_compare(_glslang_check) + glslang_depfile = ['--depfile', '@DEPFILE@'] + else + glslang_depfile = [] + endif + if with_bvh and _glslang_version.version_compare('< ' + _glslang_preamble_version) + error('glslang >= ' + _glslang_preamble_version + ' is required.') + endif + if run_command(prog_glslang, [ '--quiet', '--version' ], check : false).returncode() == 0 + glslang_quiet = ['--quiet'] + else + glslang_quiet = [] + endif +endif + +_va_drivers = [ + with_gallium_d3d12_video, + with_gallium_nouveau, + with_gallium_r600, + with_gallium_radeonsi, + with_gallium_virgl, +] + +allow_fallback_for_libva = get_option('allow-fallback-for').contains('libva') +fallback_libva_options = [] +_va = get_option('gallium-va') \ + .require(_va_drivers.contains(true), + error_message : 'VA state tracker requires at least one of the following gallium drivers: r600, radeonsi, nouveau, d3d12 (with option gallium-d3d12-video), virgl.') +_dep_va_name = host_machine.system() == 'windows' ? 'libva-win32' : 'libva' +dep_va = dependency( + _dep_va_name, version : '>= 1.8.0', + required : _va, + allow_fallback: allow_fallback_for_libva, + default_options: fallback_libva_options +) +if dep_va.found() + dep_va_headers = dep_va.partial_dependency(compile_args : true, includes : true) + if cc.has_header_symbol('va/va.h', 'VASurfaceAttribDRMFormatModifiers', + dependencies: dep_va_headers) + pre_args += '-DHAVE_VA_SURFACE_ATTRIB_DRM_FORMAT_MODIFIERS' + endif + libva_version = cc.get_define('VA_VERSION', prefix: '#include "va/va_version.h"', + dependencies: dep_va_headers).split('.') +endif +with_gallium_va = dep_va.found() + +va_drivers_path = get_option('va-libs-path') +if va_drivers_path == '' + va_drivers_path = join_paths(get_option('libdir'), 'dri') +endif + +_mediafoundation_drivers = [ + with_gallium_d3d12_video, +] + +with_gallium_mediafoundation = get_option('gallium-mediafoundation') \ + .require(host_machine.system() == 'windows', error_message : 'mediafoundation only supported on Windows') \ + .require(_mediafoundation_drivers.contains(true), + error_message : 'Media foundation state tracker requires at least one of the following gallium drivers: d3d12 (with option gallium-d3d12-video).') \ + .enabled() + +with_gallium_mediafoundation_test = get_option('gallium-mediafoundation-test') +if with_gallium_mediafoundation_test + if not with_gallium_mediafoundation + error('The mediafoundation test requires mediafoundation.') + endif +endif + +d3d_drivers_path = get_option('d3d-drivers-path') +if d3d_drivers_path == '' + d3d_drivers_path = join_paths(get_option('prefix'), get_option('libdir'), 'd3d') +endif + +with_gallium_st_d3d10umd = get_option('gallium-d3d10umd') +if with_gallium_st_d3d10umd + if not with_gallium_swrast + error('The d3d10umd state tracker requires gallium softpipe/llvmpipe.') + endif +endif + +if host_machine.cpu_family() == 'ppc64' and host_machine.endian() == 'little' + if cc.compiles(''' + #include + int main() { + vector unsigned char r; + vector unsigned int v = vec_splat_u32 (1); + r = __builtin_vec_vgbbd ((vector unsigned char) v); + return 0; + }''', + name : 'POWER8 intrinsics') + pre_args += ['-D_ARCH_PWR8'] + endif +endif + +if get_option('vmware-mks-stats') + if not with_gallium_svga + error('vmware-mks-stats requires gallium VMware/svga driver.') + endif + pre_args += '-DVMX86_STATS=1' +endif + +_rtti = get_option('cpp_rtti') + +with_gallium_rusticl = get_option('gallium-rusticl') +if with_gallium_rusticl + if not with_gallium + error('rusticl requires at least one gallium driver.') + endif +endif + +# Do built drivers need to include gfx/compute queues support? +# TODO: add a separate option to allow to build a VA-API driver without the features +# that depend on these queues. When added a lightweight decode-only VA-API driver could be built +with_gfx_compute = with_any_opengl or get_option('vulkan-drivers').length() > 0 or with_gallium_rusticl or with_gallium_va +if with_gfx_compute + pre_args += '-DHAVE_GFX_COMPUTE' +endif + +with_virtgpu_kumquat = get_option('virtgpu_kumquat') and with_gfxstream_vk +if with_gallium_rusticl or with_nouveau_vk or with_tools.contains('etnaviv') or with_virtgpu_kumquat + # rust.bindgen() does not pass `--rust-target` to bindgen until 1.7.0. + if meson.version().version_compare('< 1.7.0') + error('Mesa Rust support requires Meson 1.7.0 or newer') + endif + + add_languages('rust', required: true) + rustc = meson.get_compiler('rust') + rust = import('rust') + + # Keep this in sync with the `msrv` in clippy.toml + if rustc.version().version_compare('< 1.82') + error('Mesa requires Rust 1.82.0 or newer') + endif + + # bindgen 0.71 is the first version that knows about `editions` + bindgen_version = find_program('bindgen').version() + bindgen_bad_versions = [ + # unknown covers buggy 0.69.0, 0.71.0 + 'unknown', + '0.72.0', + ] + if bindgen_version in bindgen_bad_versions + error('Your bingden version is known to be buggy, please upgrade it. ' + + 'If your distribution does not ship a more recent version, ' + + 'you can install the latest version for your user with `cargo install bindgen-cli`.') + elif bindgen_version.version_compare('< 0.71.1') + error('Mesa requires bindgen 0.71.1 or newer. ' + + 'If your distribution does not ship a recent enough version, ' + + 'you can install the latest version for your user with `cargo install bindgen-cli`.') + endif + + bindgen_output_args = [ + # This is needed to generate 2024-safe code + '--wrap-unsafe-ops', + # can't do anything about it anyway + '--raw-line', '#![allow(clippy::all)]', + '--raw-line', '#![allow(improper_ctypes)]', + # Some bindgen versions assume `unsafe_op_in_unsafe_fn` + '--raw-line', '#![allow(unused_unsafe)]', + '--raw-line', '#![allow(non_camel_case_types)]', + '--raw-line', '#![allow(non_snake_case)]', + '--raw-line', '#![allow(non_upper_case_globals)]', + # This is fixed in 0.72 but we don't have that in CI yet + '--raw-line', '#![allow(unsafe_op_in_unsafe_fn)]', + ] + if rustc.version().version_compare('>= 1.88') + bindgen_output_args += ['--raw-line', '#![allow(unnecessary_transmutes)]'] + endif + + rust_global_args = [ + # We want to be able to write `else { if {} }` when it makes more sense than + # collapsing it into `else if {}`. + '-Aclippy::collapsible_else_if', + + # "Needless lifetimes" might be needless but they're harmless and we + # prefer being explicit, so allow them. + '-Aclippy::needless_lifetimes', + + # Being explicit has value, allow it. + '-Aclippy::question_mark', + ] + + rust_2024_lint_args = [ + '-Dboxed_slice_into_iter', + '-Ddeprecated_safe_2024', + '-Dimpl_trait_overcaptures', + '-Dkeyword_idents_2024', + '-Dmissing_unsafe_on_extern', + '-Dnever_type_fallback_flowing_into_unsafe', + '-Drust_2024_prelude_collisions', + '-Dstatic_mut_refs', + '-Dunsafe_op_in_unsafe_fn', + + # This requires cbindgen >= 0.28 + # '-Dunsafe_attr_outside_unsafe', + + # 1.83+ + # '-Dif_let_rescope', + # '-Drust_2024_guarded_string_incompatible_syntax', + # '-Drust_2024_incompatible_pat', + ] +endif + +if get_option('precomp-compiler') != 'system' + with_drivers_clc = get_option('precomp-compiler') == 'enabled' +else + with_drivers_clc = false +endif + +with_driver_using_cl = [ + with_gallium_iris, with_gallium_crocus, with_intel_vk, with_intel_hasvk, + with_gallium_asahi, with_asahi_vk, with_tools.contains('asahi'), + with_gallium_panfrost, with_panfrost_vk, + with_nouveau_vk, with_imagination_vk, + with_kosmickrisp_vk, +].contains(true) + +if get_option('mesa-clc') == 'system' + prog_mesa_clc = find_program('mesa_clc', native : true) + prog_vtn_bindgen2 = find_program('vtn_bindgen2', native : true) + # Even with mesa-clc already built, rusticl still needs clc. + with_clc = with_gallium_rusticl +else + with_clc = get_option('mesa-clc') != 'auto' or \ + with_microsoft_clc or with_gallium_rusticl or \ + with_drivers_clc or with_driver_using_cl +endif + +with_llvm = with_llvm \ + .enable_if(with_clc, error_message : 'CLC requires LLVM') + +with_poly = [ + with_gallium_asahi, with_asahi_vk, with_tools.contains('asahi'), +].contains(true) + +dep_clc = null_dep +if with_clc + dep_clc = dependency('libclc') +endif + +gl_pkgconfig_c_flags = [] +with_glx_indirect_rendering = false +if with_platform_x11 + if with_glx == 'xlib' + pre_args += '-DUSE_XSHM' + else + with_glx_indirect_rendering = true + pre_args += '-DGLX_INDIRECT_RENDERING' + if with_glx_direct + pre_args += '-DGLX_DIRECT_RENDERING' + endif + if with_dri_platform == 'drm' + pre_args += '-DGLX_USE_DRM' + elif with_dri_platform == 'apple' + pre_args += '-DGLX_USE_APPLEGL' + # Check to see if more than just the default 'swrast' is required + if (not with_gallium_softpipe) or 1 < gallium_drivers.length() + # Switch the MacOS code from "forwarding to the OpenGL.framework" mode + # and into actual Gallium Driver mode + pre_args += '-DGLX_USE_APPLE' + endif + elif with_dri_platform == 'windows' + pre_args += '-DGLX_USE_WINDOWSGL' + endif + endif +endif + +with_glapi_export_proto_entry_points = false +if not with_glx_indirect_rendering + # Imply !defined(GLX_INDIRECT_RENDERING) + with_glapi_export_proto_entry_points = true +endif +pre_args += '-DGLAPI_EXPORT_PROTO_ENTRY_POINTS=@0@'.format(with_glapi_export_proto_entry_points.to_int()) + +with_android_stub = get_option('android-stub') +need_android_stub = with_platform_android or with_gfxstream_emulated_android +if with_android_stub and not need_android_stub + error('`-D android-stub=true` makes no sense without `-D platforms=android` or emulated Android') +endif + +with_libbacktrace = get_option('android-libbacktrace') \ + .require(with_platform_android, error_message : '`-D android-libbacktrace=enabled` makes no sense without `-D platforms=android`') \ + .disable_auto_if(not with_platform_android) \ + .allowed() + +if with_platform_android + dep_android_ui = null_dep + dep_android_mapper4 = null_dep + if not with_android_stub + dep_android = [ + dependency('cutils'), + dependency('hardware'), + dependency('log'), + dependency('sync'), + ] + if with_libbacktrace and get_option('platform-sdk-version') < 34 + cpp_args += '-DWITH_LIBBACKTRACE' + dep_android += dependency('backtrace') + endif + if get_option('platform-sdk-version') >= 26 + dep_android += dependency('nativewindow') + endif + if get_option('platform-sdk-version') >= 30 + dep_android_mapper4 = dependency('android.hardware.graphics.mapper', version : '>= 4.0', required : false) + endif + if get_option('platform-sdk-version') >= 35 + dep_android_ui = dependency('ui', required : false) + endif + dep_hwvulkan_headers = dependency('android-hwvulkan-headers', required : false) + if dep_hwvulkan_headers.found() and with_any_vk + dep_android += dep_hwvulkan_headers + endif + endif + pre_args += '-DANDROID_API_LEVEL=' + get_option('platform-sdk-version').to_string() + if get_option('android-strict') + pre_args += '-DANDROID_STRICT' + endif +endif + +# On Android, seccomp kills the process on kernels without +# CONFIG_KCMP/CONFIG_CHECKPOINT_RESTORE if it attemps to use KCMP. +# Since we can't detect that, err on the side of caution and disable +# KCMP by default on Android. +if get_option('allow-kcmp') \ + .disable_auto_if(with_platform_android) \ + .allowed() + pre_args += '-DALLOW_KCMP' +endif + +# Find a python executable that meets our version requirement. +# - On Windows, a venv has no versioned aliased to 'python'. +# - On RHEL 9, python3 is 3.9, so we must use python3.12. +python_version_req = '>= 3.10' +python_exec_list = ['python3.16', 'python3.15', 'python3.14', 'python3.13', + 'python3.12', 'python3.11', 'python3.10', 'python3', 'python'] + +foreach p : python_exec_list + prog_python = find_program(p, required : false, version : python_version_req) + if not prog_python.found() + continue + endif + + has_mako = run_command( + prog_python, '-c', + ''' +import sys + +try: + try: + from packaging.version import Version + except: + from distutils.version import StrictVersion as Version +except: + sys.exit(2) + +try: + import mako +except: + sys.exit(1) + +if Version(mako.__version__) < Version("0.8.0"): + sys.exit(1) +''', check: false) + if has_mako.returncode() != 0 + continue + endif + + has_yaml = run_command(prog_python, '-c', 'import yaml', check: false) + if has_yaml.returncode() != 0 + continue + endif + + break +endforeach + +if not prog_python.found() + error('Python ' + python_version_req + ' not found') +endif + +if has_mako.returncode() == 1 + error('Python (3.x) mako module >= 0.8.0 required to build mesa.') +elif has_mako.returncode() == 2 + error('One of Python (3.x) packaging or distutils module is required.') +endif + +if has_yaml.returncode() != 0 + error('Python (3.x) yaml module (PyYAML) required to build mesa.') +endif + +if cc.get_id() == 'gcc' and cc.version().version_compare('< 4.4.6') + error('When using GCC, version 4.4.6 or later is required.') +endif + +# Support systems without ETIME (e.g. FreeBSD) +if cc.get_define('ETIME', prefix : '#include ') == '' + pre_args += '-DETIME=ETIMEDOUT' +endif + +# Define MESA_DEBUG to 1 for debug builds only (debugoptimized is not included on this one); +# otherwise define MESA_DEBUG to 0 +pre_args += '-DMESA_DEBUG=@0@'.format(with_mesa_debug.to_int()) + +with_split_debug = get_option('split-debug') \ + .disable_if(not cc.has_argument('-gsplit-dwarf'), + error_message : 'split-debug requires compiler -gsplit-dwarf support') \ + .disable_if(not cc.has_link_argument('-Wl,--gdb-index'), + error_message : 'split-debug requires the linker argument -Wl,--gdb-index') + +if with_split_debug.allowed() and get_option('debug') + add_project_arguments('-gsplit-dwarf', language : ['c', 'cpp']) + add_project_link_arguments('-Wl,--gdb-index', language : ['c', 'cpp']) +endif + +with_shader_cache = get_option('shader-cache') \ + .require(host_machine.system() != 'windows', error_message : 'Shader Cache does not currently work on Windows') \ + .allowed() + +if with_shader_cache + pre_args += '-DENABLE_SHADER_CACHE' + if not get_option('shader-cache-default') + pre_args += '-DSHADER_CACHE_DISABLE_BY_DEFAULT' + endif + + shader_cache_max_size = get_option('shader-cache-max-size') + if shader_cache_max_size != '' + pre_args += '-DMESA_SHADER_CACHE_MAX_SIZE="@0@"'.format(shader_cache_max_size) + endif +endif + +# Check for GCC style builtins +foreach b : ['bswap32', 'bswap64', 'clz', 'clzll', 'ctz', 'expect', 'ffs', + 'ffsll', 'popcount', 'popcountll', 'unreachable', 'types_compatible_p'] + if cc.has_function(b) + pre_args += '-DHAVE___BUILTIN_@0@'.format(b.to_upper()) + endif +endforeach + +# Check for GCC overflow intrinsics +foreach b : ['add_overflow', 'add_overflow_p', 'sub_overflow_p'] + if cc.has_function('__builtin_' + b) + pre_args += '-DHAVE___BUILTIN_@0@'.format(b.to_upper()) + endif +endforeach + +# Check for ARM FP state builtins +foreach b : ['arm_get_fpscr', 'arm_set_fpscr', 'aarch64_get_fpcr', 'aarch64_set_fpcr'] + if cc.has_function('__builtin_' + b) + pre_args += '-DHAVE___BUILTIN_@0@'.format(b.to_upper()) + endif +endforeach + +# check for GCC __attribute__ +_attributes = [ + 'const', 'flatten', 'malloc', 'pure', 'unused', 'warn_unused_result', + 'weak', 'format', 'packed', 'returns_nonnull', 'alias', 'noreturn', + 'optimize', 'cold', +] +foreach a : cc.get_supported_function_attributes(_attributes) + pre_args += '-DHAVE_FUNC_ATTRIBUTE_@0@'.format(a.to_upper()) +endforeach +if cc.has_function_attribute('visibility:hidden') + pre_args += '-DHAVE_FUNC_ATTRIBUTE_VISIBILITY' +endif + +_no_sanitize_flags = [] +if get_option('b_sanitize').split(',').contains('address') + _no_sanitize_flags += ['address'] +endif +if get_option('b_sanitize').split(',').contains('undefined') + _no_sanitize_flags += ['vptr'] +endif +foreach flag: _no_sanitize_flags + if cc.compiles('__attribute__((no_sanitize("@0@"))) int foo(void) { return 0; }'.format(flag), + name : 'no_sanitize(@0@)'.format(flag), + werror: true) + pre_args += '-DHAVE_FUNC_ATTRIBUTE_NO_SANITIZE_@0@'.format(flag.to_upper()) + endif +endforeach + +if cc.compiles('__uint128_t foo(void) { return 0; }', + name : '__uint128_t') + pre_args += '-DHAVE_UINT128' +endif + +if cc.has_function('reallocarray') + pre_args += '-DHAVE_REALLOCARRAY' +endif +if cc.has_function('fmemopen') + pre_args += '-DHAVE_FMEMOPEN' +endif + +# TODO: this is very incomplete +if ['linux', 'cygwin', 'gnu', 'freebsd', 'gnu/kfreebsd', 'haiku', 'android', 'managarm', 'redox'].contains(host_machine.system()) + pre_args += '-D_GNU_SOURCE' +elif host_machine.system() == 'sunos' + pre_args += '-D__EXTENSIONS__' +elif host_machine.system() == 'windows' + pre_args += [ + '-D_WIN32_WINNT=0x0A00', + '-DWINVER=0x0A00', + ] + if cc.get_argument_syntax() == 'msvc' + pre_args += [ + '-D_USE_MATH_DEFINES', + '-D_CRT_NONSTDC_NO_DEPRECATE', + '-D_CRT_SECURE_NO_WARNINGS', + '-D_CRT_SECURE_NO_DEPRECATE', + '-D_SCL_SECURE_NO_WARNINGS', + '-D_SCL_SECURE_NO_DEPRECATE', + '-D_ALLOW_KEYWORD_MACROS', + '-D_HAS_EXCEPTIONS=0', # Tell C++ STL to not use exceptions + '-DNOMINMAX', + '-D_UCRT_NOISY_NAN', # https://developercommunity.visualstudio.com/t/NAN-is-no-longer-compile-time-constant-i/10688907?viewtype=all + ] + else + # MINGW also accept _GNU_SOURCE + pre_args += '-D_GNU_SOURCE' + + # When the target is not (mingw with ucrt) + # NOTE: clang's stddef.h are conflict with mingw or ucrt's stddef.h + # So do not include headers that defined in clang for detecting + # _UCRT + if cc.compiles(''' + #include + #if defined(__MINGW32__) && defined(_UCRT) + #error + #endif + int main(void) { return 0; }''') + pre_args += ['-D__MSVCRT_VERSION__=0x0700'] + endif + endif +elif host_machine.system() == 'openbsd' + pre_args += '-D_ISOC11_SOURCE' +endif + +# Check for generic C arguments +c_msvc_compat_args = [] +no_override_init_args = [] +cpp_msvc_compat_args = [] +ld_args_gc_sections = [] +if cc.get_argument_syntax() == 'msvc' + _trial = [ + '/wd4018', # signed/unsigned mismatch + '/wd4056', # overflow in floating-point constant arithmetic + '/wd4244', # conversion from 'type1' to 'type2', possible loss of data + '/wd4267', # 'var' : conversion from 'size_t' to 'type', possible loss of data + '/wd4305', # truncation from 'type1' to 'type2' + '/wd4351', # new behavior: elements of array 'array' will be default initialized + '/wd4756', # overflow in constant arithmetic + '/wd4800', # forcing value to bool 'true' or 'false' (performance warning) + '/wd4291', # no matching operator delete found + '/wd4146', # unary minus operator applied to unsigned type, result still unsigned + '/wd4200', # nonstandard extension used: zero-sized array in struct/union + '/wd4624', # destructor was implicitly defined as deleted [from LLVM] + '/wd4309', # 'initializing': truncation of constant value + '/wd4838', # conversion from 'int' to 'const char' requires a narrowing conversion + '/wd5105', # macro expansion producing 'defined' has undefined behavior (winbase.h, need Windows SDK upgrade) + '/we4020', # Error when passing the wrong number of parameters + '/we4024', # Error when passing different type of parameter + '/we4189', # 'identifier' : local variable is initialized but not referenced + '/Zc:__cplusplus', #Set __cplusplus macro to match the /std:c++ on the command line + '/Zc:enumTypes', # Enables C++ conforming enum underlying type and enumerator type deduction + '/Zc:preprocessor', # Use the standards-conforming preprocessor + ] + c_args += cc.get_supported_arguments(_trial) + cpp_args += cpp.get_supported_arguments(_trial) +else + _trial_c = [ + '-Werror=implicit-function-declaration', + '-Werror=missing-prototypes', + '-Werror=return-type', + '-Werror=empty-body', + '-Werror=incompatible-pointer-types', + '-Werror=int-conversion', + '-Wimplicit-fallthrough', + '-Wmisleading-indentation', + '-Wno-error=maybe-uninitialized', + '-Wno-missing-field-initializers', + '-Wno-format-truncation', + '-Wno-nonnull-compare', + '-fno-math-errno', + '-fno-trapping-math', + '-Qunused-arguments', + '-fno-common', + '-Wno-unknown-pragmas', + # Clang + '-Wno-microsoft-enum-value', + '-Wno-unused-function', + ] + _trial_cpp = [ + '-Werror=return-type', + '-Werror=empty-body', + '-Wmisleading-indentation', + '-Wno-error=maybe-uninitialized', + '-Wno-non-virtual-dtor', + '-Wno-missing-field-initializers', + '-Wno-format-truncation', + '-fno-math-errno', + '-fno-trapping-math', + '-Qunused-arguments', + # Some classes use custom new operator which zeroes memory, however + # gcc does aggressive dead-store elimination which threats all writes + # to the memory before the constructor as "dead stores". + # For now we disable this optimization. + '-flifetime-dse=1', + '-Wno-unknown-pragmas', + # Clang + '-Wno-microsoft-enum-value', + ] + + # MinGW chokes on format specifiers and I can't get it all working + if not (cc.get_argument_syntax() == 'gcc' and host_machine.system() == 'windows') + _trial_c += ['-Werror=format', '-Wformat-security'] + _trial_cpp += ['-Werror=format', '-Wformat-security'] + endif + + # FreeBSD annotated but Mesa isn't ready + if not (cc.get_id() == 'clang' and host_machine.system() == 'freebsd') + _trial_c += ['-Werror=thread-safety'] + endif + + # If the compiler supports it, put function and data symbols in their + # own sections and GC the sections after linking. This lets drivers + # drop shared code unused by that specific driver (particularly + # relevant for Vulkan drivers). + if cc.links('static char unused() { return 5; } int main() { return 0; }', + args : '-Wl,--gc-sections', name : 'gc-sections') + ld_args_gc_sections += '-Wl,--gc-sections' + _trial_c += ['-ffunction-sections', '-fdata-sections'] + _trial_cpp += ['-ffunction-sections', '-fdata-sections'] + endif + + # Variables that are only used for assertions are considered unused when assertions + # are disabled. Don't treat this as an error, since we build with -Werror even if + # assertions are disabled. + if with_mesa_ndebug + _trial_c += ['-Wno-unused-variable', '-Wno-unused-but-set-variable', '/wd4189'] + _trial_cpp += ['-Wno-unused-variable', '-Wno-unused-but-set-variable', '/wd4189'] + endif + + c_args += cc.get_supported_arguments(_trial_c) + cpp_args += cpp.get_supported_arguments(_trial_cpp) + + no_override_init_args += cc.get_supported_arguments( + ['-Wno-override-init', '-Wno-initializer-overrides'] + ) + + # Check for C and C++ arguments for MSVC compatibility. These are only used + # in parts of the mesa code base that need to compile with MSVC, mainly + # common code + _trial_msvc = [ + '-Werror=pointer-arith', + '-Werror=vla', + '-Werror=gnu-empty-initializer', + '-Wgnu-pointer-arith', + ] + c_msvc_compat_args += cc.get_supported_arguments(_trial_msvc) + cpp_msvc_compat_args += cpp.get_supported_arguments(_trial_msvc) +endif + +# set linker arguments +if host_machine.system() == 'windows' + if cc.get_argument_syntax() == 'msvc' + add_project_link_arguments( + '/fixed:no', + '/dynamicbase', + '/nxcompat', + language : ['c', 'cpp'], + ) + if get_option('buildtype') != 'debug' + add_project_link_arguments( + '/incremental:no', + language : ['c', 'cpp'], + ) + endif + else + add_project_link_arguments( + cc.get_supported_link_arguments( + '-Wl,--nxcompat', + '-Wl,--dynamicbase', + '-static-libgcc', + '-static-libstdc++', + ), + language : ['c'], + ) + add_project_link_arguments( + cpp.get_supported_link_arguments( + '-Wl,--nxcompat', + '-Wl,--dynamicbase', + '-static-libgcc', + '-static-libstdc++', + ), + language : ['cpp'], + ) + endif +endif + +sse2_arg = [] +sse2_args = [] +sse41_args = [] +with_sse41 = false +if host_machine.cpu_family().startswith('x86') + pre_args += '-DUSE_SSE41' + with_sse41 = true + + if cc.get_id() != 'msvc' + sse41_args = ['-msse4.1'] + + if host_machine.cpu_family() == 'x86' + # x86_64 have sse2 by default, so sse2 args only for x86 + sse2_arg = ['-msse2', '-mfpmath=sse'] + sse2_args = [sse2_arg, '-mstackrealign'] + if get_option('sse2') + # These settings make generated GCC code match MSVC and follow + # GCC advice on https://gcc.gnu.org/wiki/FloatingPointMath#x86note + # + # NOTE: We need to ensure stack is realigned given that we + # produce shared objects, and have no control over the stack + # alignment policy of the application. Therefore we need + # -mstackrealign or -mincoming-stack-boundary=2. + # + # XXX: We could have SSE without -mstackrealign if we always used + # __attribute__((force_align_arg_pointer)), but that's not + # always the case. + c_cpp_args += sse2_args + # sse2_args are adopted into c_cpp_args to avoid duplicated sse2 command line args + sse2_arg = [] + sse2_args = [] + else + # GCC on x86 (not x86_64) with -msse* assumes a 16 byte aligned stack, but + # that's not guaranteed + sse41_args += '-mstackrealign' + endif + endif + endif +endif + +# Detect __builtin_ia32_clflushopt support +if cc.has_function('__builtin_ia32_clflushopt', args : '-mclflushopt') + pre_args += '-DHAVE___BUILTIN_IA32_CLFLUSHOPT' + clflushopt_args = ['-mclflushopt'] + with_clflushopt = true +else + clflushopt_args = [] + with_clflushopt = false +endif + +# Check for GCC style atomics +dep_atomic = null_dep + +if cc.compiles('''#include + int main() { + struct { + uint64_t *v; + } x; + return (int)__atomic_load_n(x.v, __ATOMIC_ACQUIRE) & + (int)__atomic_add_fetch(x.v, (uint64_t)1, __ATOMIC_ACQ_REL); + + }''', + name : 'GCC atomic builtins') + pre_args += '-DUSE_GCC_ATOMIC_BUILTINS' + + # Not all atomic calls can be turned into lock-free instructions, in which + # GCC will make calls into the libatomic library. Check whether we need to + # link with -latomic. + # + # This can happen for 64-bit atomic operations on 32-bit architectures such + # as ARM. + if not cc.links('''#include + int main() { + struct { + uint64_t *v; + } x; + return (int)__atomic_load_n(x.v, __ATOMIC_ACQUIRE) & + (int)__atomic_add_fetch(x.v, (uint64_t)1, __ATOMIC_ACQ_REL); + }''', + name : 'GCC atomic builtins required -latomic') + dep_atomic = cc.find_library('atomic') + endif +endif +if not cc.links('''#include + uint64_t v; + int main() { + return __sync_add_and_fetch(&v, (uint64_t)1); + }''', + dependencies : dep_atomic, + name : 'GCC 64bit atomics') + pre_args += '-DMISSING_64BIT_ATOMICS' +endif + +dep_ws2_32 = cc.find_library('ws2_32', required : with_platform_windows) + +# Check for standard headers and functions +if (cc.has_header_symbol('sys/sysmacros.h', 'major') and + cc.has_header_symbol('sys/sysmacros.h', 'minor') and + cc.has_header_symbol('sys/sysmacros.h', 'makedev')) + pre_args += '-DMAJOR_IN_SYSMACROS' +endif +if (cc.has_header_symbol('sys/mkdev.h', 'major') and + cc.has_header_symbol('sys/mkdev.h', 'minor') and + cc.has_header_symbol('sys/mkdev.h', 'makedev')) + pre_args += '-DMAJOR_IN_MKDEV' +endif + +if cc.check_header('sched.h') + pre_args += '-DHAS_SCHED_H' + if cc.has_function('sched_getaffinity') + pre_args += '-DHAS_SCHED_GETAFFINITY' + endif +endif + +if not ['linux'].contains(host_machine.system()) + # Deprecated on Linux and requires on FreeBSD and OpenBSD + if cc.check_header('sys/sysctl.h', prefix : '#include ') + pre_args += '-DHAVE_SYS_SYSCTL_H' + endif +endif + +foreach h : ['xlocale.h', 'linux/futex.h', 'endian.h', 'dlfcn.h', 'sys/shm.h', + 'cet.h', 'pthread_np.h', 'poll.h', 'sys/inotify.h', + 'linux/udmabuf.h'] + if cc.check_header(h) + pre_args += '-DHAVE_@0@'.format(h.to_upper().underscorify()) + endif +endforeach + +functions_to_detect = { + 'strtof': '', + 'mkostemp': '', + 'memfd_create': '#include ', + 'random_r': '', + 'flock': '', + 'strtok_r': '', + 'getrandom': '#include ', + 'qsort_s': '', + 'posix_fallocate': '', + 'secure_getenv': '', + 'sysconf': '#include ', +} + +foreach f, prefix: functions_to_detect + if cc.has_function(f, args: pre_args, prefix: prefix) + pre_args += '-DHAVE_@0@'.format(f.to_upper()) + endif +endforeach + +if cpp.links(''' + #define _GNU_SOURCE + #include + + static int dcomp(const void *l, const void *r, void *t) { return 0; } + + int main(int ac, char **av) { + int arr[] = { 1 }; + void *t = NULL; + qsort_r((void*)&arr[0], 1, 1, dcomp, t); + return (0); + }''', + args : pre_args, + name : 'GNU qsort_r') + pre_args += '-DHAVE_GNU_QSORT_R' +elif cpp.links(''' + #include + + static int dcomp(void *t, const void *l, const void *r) { return 0; } + + int main(int ac, char **av) { + int arr[] = { 1 }; + void *t = NULL; + qsort_r((void*)&arr[0], 1, 1, t, dcomp); + return (0); + }''', + args : pre_args, + name : 'BSD qsort_r') + pre_args += '-DHAVE_BSD_QSORT_R' +endif + +if cc.has_header_symbol('time.h', 'struct timespec') + pre_args += '-DHAVE_STRUCT_TIMESPEC' +endif + +with_c11_threads = false +if cc.has_function('thrd_create', prefix: '#include ') + if with_platform_android + # Current only Android's c11 are verified + pre_args += '-DHAVE_THRD_CREATE' + with_c11_threads = true + endif +endif + +if cc.has_header_symbol('errno.h', 'program_invocation_name', + args : '-D_GNU_SOURCE') + pre_args += '-DHAVE_PROGRAM_INVOCATION_NAME' +elif with_tools.contains('intel') and not with_platform_android + # Intel tools is supported on Android where the program name is from + # `getprogname()` without `program_invocation_name` in glibc. + # See `src/util/u_process.c` for more details. + error('Intel tools require the program_invocation_name variable') +endif + +if cc.has_header_symbol('math.h', 'issignaling', + args : '-D_GNU_SOURCE') + pre_args += '-DHAVE_ISSIGNALING' +endif + +# MinGW provides a __builtin_posix_memalign function, but not a posix_memalign. +# This means that this check will succeed, but then compilation will later +# fail. MSVC doesn't have this function at all, so only check for it on +# non-windows platforms. +if host_machine.system() != 'windows' + if cc.has_function('posix_memalign') + pre_args += '-DHAVE_POSIX_MEMALIGN' + endif +endif + +if cc.has_member('struct dirent', 'd_type', prefix: '''#include + #include ''') + pre_args += '-DHAVE_DIRENT_D_TYPE' +endif + +# strtod locale support +if cc.links(''' + #define _GNU_SOURCE + #include + #include + #ifdef HAVE_XLOCALE_H + #include + #endif + int main() { + locale_t loc = newlocale(LC_CTYPE_MASK, "C", NULL); + const char *s = "1.0"; + char *end; + double d = strtod_l(s, &end, loc); + float f = strtof_l(s, &end, loc); + freelocale(loc); + return 0; + }''', + args : pre_args, + name : 'strtod has locale support') + pre_args += '-DHAVE_STRTOD_L' +endif + +# Check for some linker flags +ld_args_bsymbolic = [] +if cc.links('int main() { return 0; }', args : '-Wl,-Bsymbolic', name : 'Bsymbolic') + ld_args_bsymbolic += '-Wl,-Bsymbolic' +endif +with_ld_version_script = false +if host_machine.system() != 'windows' and cc.links('int main() { return 0; }', + args : '-Wl,--version-script=@0@'.format( + join_paths(meson.current_source_dir(), 'build-support/conftest.map')), + name : 'version-script') + with_ld_version_script = true +endif +with_ld_dynamic_list = false +if cc.links('int main() { return 0; }', + args : '-Wl,--dynamic-list=@0@'.format( + join_paths(meson.current_source_dir(), 'build-support/conftest.dyn')), + name : 'dynamic-list') + with_ld_dynamic_list = true +endif + +ld_args_build_id = cc.get_supported_link_arguments('-Wl,--build-id=sha1') + +# check for dl support +dep_dl = null_dep +if host_machine.system() != 'windows' + if not cc.has_function('dlopen') + dep_dl = cc.find_library('dl', required : true) + endif + if cc.has_function('dladdr', dependencies : dep_dl) + # This is required for src/util + pre_args += '-DHAVE_DLADDR' + endif +endif + +if cc.has_function('dl_iterate_phdr') + pre_args += '-DHAVE_DL_ITERATE_PHDR' +elif with_intel_vk or with_intel_hasvk + error('Intel "Anvil" Vulkan driver requires the dl_iterate_phdr function') +endif + +if with_any_intel and ['x86', 'x86_64'].contains(host_machine.cpu_family()) + pre_args += '-DSUPPORT_INTEL_INTEGRATED_GPUS' +endif + +# Determine whether or not the rt library is needed for time functions +if host_machine.system() == 'windows' or cc.has_function('clock_gettime') + dep_clock = null_dep +else + dep_clock = cc.find_library('rt') +endif + +# IMPORTANT: We can't upgrade Zlib beyond 1.2.5 because it would break Viewperf. +dep_zlib = dependency('zlib', version : '>= 1.2.3', + allow_fallback: true, + required : get_option('zlib')) +if dep_zlib.found() + pre_args += '-DHAVE_ZLIB' +endif + +if host_machine.system() == 'windows' or with_platform_android + dep_display_info = null_dep +else + dep_display_info = dependency('libdisplay-info', version : '>= 0.1.1', + required : get_option('display-info')) + if dep_display_info.found() + pre_args += '-DHAVE_LIBDISPLAY_INFO' + endif +endif + +dep_zstd = dependency('libzstd', required : get_option('zstd')) +if dep_zstd.found() + pre_args += '-DHAVE_ZSTD' +endif + +with_compression = dep_zlib.found() or dep_zstd.found() +if with_compression + pre_args += '-DHAVE_COMPRESSION' +elif with_shader_cache + error('Shader Cache requires compression') +endif + +if host_machine.system() == 'windows' + # For MSVC and MinGW we aren't using pthreads, and dependency('threads') will add linkage + # to pthread for MinGW, so leave the dependency null_dep for Windows. For Windows linking to + # kernel32 is enough for c11/threads.h and it's already linked by meson by default + dep_thread = null_dep +else + dep_thread = dependency('threads') +endif +if dep_thread.found() + pre_args += '-DHAVE_PTHREAD' + if host_machine.system() != 'netbsd' and cc.has_function( + 'pthread_setaffinity_np', + dependencies : dep_thread, + prefix : '#include ', + args : '-D_GNU_SOURCE') + pre_args += '-DHAVE_PTHREAD_SETAFFINITY' + endif +endif + +with_expat = get_option('expat') \ + .disable_auto_if(with_platform_windows) \ + .enable_if(with_intel_tools, error_message : 'Intel tools require expat') + +if host_machine.system() == 'darwin' + dep_expat = meson.get_compiler('c').find_library('expat', required : with_expat) +else + dep_expat = dependency('expat', allow_fallback: true, + required : with_expat) +endif + +# We don't require expat on Android or Windows +use_xmlconfig = get_option('xmlconfig') \ + .require(not (with_platform_android or with_platform_windows), + error_message : 'xmlconfig not available on Android or Windows') \ + .require(dep_expat.found(), + error_message : 'requires expat') \ + .allowed() + +# Predefined macros for windows +if host_machine.system() == 'windows' + pre_args += '-DWIN32_LEAN_AND_MEAN' # http://msdn2.microsoft.com/en-us/library/6dwk3a1z.aspx +endif +# this only exists on linux so either this is linux and it will be found, or +# it's not linux and wont +dep_m = cc.find_library('m', required : false) + +if host_machine.system() == 'windows' + dep_regex = meson.get_compiler('c').find_library('regex', required : false) + if not dep_regex.found() + dep_regex = declare_dependency(compile_args : ['-DNO_REGEX']) + endif +else + dep_regex = null_dep +endif + +if with_platform_haiku + dep_network = cc.find_library('network') +endif + +dep_futex = null_dep +if host_machine.system() == 'windows' + if (get_option('min-windows-version') < 8) + pre_args += '-DWINDOWS_NO_FUTEX' + else + dep_futex = cc.find_library('synchronization', required : true) + endif +endif + +# Check for libdrm. Various drivers have different libdrm version requirements, +# but we always want to use the same version for all libdrm modules. That means +# even if driver foo requires 2.4.0 and driver bar requires 2.4.3, if foo and +# bar are both on use 2.4.3 for both of them +dep_libdrm_amdgpu = null_dep +dep_libdrm_intel = null_dep + +_drm_amdgpu_ver = '2.4.121' +_drm_intel_ver = '2.4.75' +_drm_ver = '2.4.109' + +_libdrm_checks = [ + ['intel', with_gallium_i915], + ['amdgpu', (with_amd_vk and not with_platform_windows) or with_gallium_radeonsi], +] + +# Loop over the enables versions and get the highest libdrm requirement for all +# active drivers. +_drm_blame = '' +foreach d : _libdrm_checks + ver = get_variable('_drm_@0@_ver'.format(d[0])) + if d[1] and ver.version_compare('>' + _drm_ver) + _drm_ver = ver + _drm_blame = d[0] + endif +endforeach +if _drm_blame != '' + message('libdrm @0@ needed because @1@ has the highest requirement'.format(_drm_ver, _drm_blame)) +endif + +allow_fallback_for_libdrm = get_option('allow-fallback-for').contains('libdrm') + +with_gallium_drisw_kms = false +if system_has_kms_drm + dep_libdrm = dependency( + 'libdrm', version : '>=' + _drm_ver, + required : with_dri2 or with_dri or with_gbm, + allow_fallback: allow_fallback_for_libdrm, + ) +else + # We should prevent libdrm from being available when the target doesn't have it to avoid transitive + # dependencies (such as vk-runtime) linking to it + dep_libdrm = null_dep +endif +if dep_libdrm.found() + pre_args += '-DHAVE_LIBDRM' + if with_dri_platform == 'drm' and with_dri + with_gallium_drisw_kms = true + endif +endif + +# Then get each libdrm module +foreach d : _libdrm_checks + if d[1] + set_variable( + 'dep_libdrm_' + d[0], + dependency( + 'libdrm_' + d[0], + version : '>=' + _drm_ver, + allow_fallback: allow_fallback_for_libdrm, + ) + ) + endif +endforeach + +dep_libudev = dependency('libudev', required : false) +if dep_libudev.found() + pre_args += '-DHAVE_LIBUDEV' +endif + +llvm_modules = ['bitwriter', 'engine', 'mcdisassembler', 'mcjit', 'core', 'executionengine', 'scalaropts', 'transformutils', 'instcombine'] +llvm_optional_modules = ['coroutines'] +if with_amd_vk or with_gallium_radeonsi + llvm_modules += ['amdgpu', 'bitreader', 'ipo'] +endif +if with_clc + llvm_modules += ['coverage', 'target', 'linker', 'irreader', 'option', 'libdriver', 'lto'] + # all-targets is needed to support static linking LLVM build with multiple targets. + # windowsdriver is needded with LLVM>=15 and frontendhlsl is needed with LLVM>=16, + # but we don't know what LLVM version we are using yet + llvm_optional_modules += ['all-targets', 'windowsdriver', 'frontendhlsl', 'frontenddriver'] +endif +if draw_with_llvm + llvm_modules += 'native' + # lto is needded with LLVM>=15, but we don't know what LLVM verrsion we are using yet + llvm_optional_modules += ['lto'] +endif + +# MCJIT is deprecated in LLVM and will not accept new architecture ports, +# so any architecture not in the exhaustive list will have to rely on LLVM +# ORCJIT for llvmpipe functionality. +llvm_has_mcjit = host_machine.cpu_family() in ['aarch64', 'arm', 'ppc', 'ppc64', 's390x', 'x86', 'x86_64'] +llvm_with_orcjit = get_option('llvm-orcjit') or not llvm_has_mcjit + +if (with_amd_vk or with_gallium_radeonsi) and amd_with_llvm + _llvm_version = '>= 18.0.0' +elif with_clc or llvm_with_orcjit + _llvm_version = '>= 15.0.0' +else + _llvm_version = '>= 8.0.0' +endif + +_shared_llvm = get_option('shared-llvm') \ + .disable_auto_if(host_machine.system() == 'windows') \ + .allowed() + +dep_llvm = dependency( + 'llvm', + method : host_machine.system() == 'windows' ? 'auto' : 'config-tool', + version : _llvm_version, + modules : llvm_modules, + optional_modules : llvm_optional_modules, + required : with_llvm, + static : not _shared_llvm, + fallback : ['llvm', 'dep_llvm'], + include_type : 'system', +) +if dep_llvm.found() + pre_args += '-DMESA_LLVM_VERSION_STRING="@0@"'.format(dep_llvm.version()) + pre_args += '-DLLVM_IS_SHARED=@0@'.format(_shared_llvm.to_int()) + + if (with_swrast_vk or with_gallium_llvmpipe) and not draw_with_llvm + error('Lavapipe and llvmpipe require LLVM draw support.') + endif + + if with_gallium_i915 and not draw_with_llvm + error('i915 requires LLVM draw support for vertex shaders.') + endif + + if with_gallium_r300 and not draw_with_llvm and host_machine.cpu_family() == 'x86' + error('r300 requires LLVM draw support for vertex shaders.') + endif + + if host_machine.system() != 'windows' + # LLVM can be built without rtti, turning off rtti changes the ABI of C++ + # programs, so we need to build all C++ code in mesa without rtti as well to + # ensure that linking works. Note that Win32 compilers does handle mismatching RTTI + # without issues, so only apply this for other compilers. + if dep_llvm.type_name() == 'internal' + _llvm_rtti = subproject('llvm').get_variable('has_rtti', true) + else + # The CMake finder will return 'ON', the llvm-config will return 'YES' + _llvm_rtti = ['ON', 'YES'].contains(dep_llvm.get_variable(cmake : 'LLVM_ENABLE_RTTI', configtool: 'has-rtti')) + endif + if _rtti != _llvm_rtti + if _llvm_rtti + error('LLVM was built with RTTI, cannot build Mesa with RTTI disabled. Remove cpp_rtti disable switch or use LLVM built without LLVM_ENABLE_RTTI.') + else + error('LLVM was built without RTTI, so Mesa must also disable RTTI. Use an LLVM built with LLVM_ENABLE_RTTI or add cpp_rtti=false.') + endif + endif + endif + + if cc.get_argument_syntax() == 'msvc' + # Suppress "/DELAYLOAD:ole32.dll/shell32.dll ignored" warnings that LLVM adds + add_project_link_arguments( + '/ignore:4199', + language : ['c', 'cpp'], + ) + endif +else + draw_with_llvm = false +endif +amd_with_llvm = amd_with_llvm and dep_llvm.found() +pre_args += '-DDRAW_LLVM_AVAILABLE=@0@'.format(draw_with_llvm.to_int()) +pre_args += '-DAMD_LLVM_AVAILABLE=@0@'.format(amd_with_llvm.to_int()) +pre_args += '-DGALLIVM_USE_ORCJIT=@0@'.format((dep_llvm.found() and llvm_with_orcjit).to_int()) + +if with_clc + chosen_llvm_version_array = dep_llvm.version().split('.') + chosen_llvm_version_major = chosen_llvm_version_array[0].to_int() + chosen_llvm_version_minor = chosen_llvm_version_array[1].to_int() + + # Require an SPIRV-LLVM-Translator version compatible with the chosen LLVM + # one. + + _llvmspirvlib_min_version = '>= 15.0.0.0' + + _llvmspirvlib_version = [ + _llvmspirvlib_min_version, + '>= @0@.@1@'.format(chosen_llvm_version_major, chosen_llvm_version_minor), + '< @0@.@1@'.format(chosen_llvm_version_major, chosen_llvm_version_minor + 1) ] + + # LLVMSPIRVLib is available at https://github.com/KhronosGroup/SPIRV-LLVM-Translator + dep_llvmspirvlib = dependency('LLVMSPIRVLib', required : true, version : _llvmspirvlib_version) +else + dep_llvmspirvlib = null_dep +endif + +with_spirv_tools = get_option('spirv-tools') \ + .enable_if(with_clc, error_message : 'CLC requires SPIRV-Tools') + +dep_spirv_tools = dependency( + 'SPIRV-Tools', + required : with_spirv_tools, + version : '>= 2024.1', +) +if dep_spirv_tools.found() + pre_args += '-DHAVE_SPIRV_TOOLS' +endif + +dep_clang = null_dep +if with_clc + llvm_libdir = dep_llvm.get_variable(cmake : 'LLVM_LIBRARY_DIR', configtool: 'libdir') + + dep_clang = cpp.find_library('clang-cpp', dirs : llvm_libdir, required : false) + + if not dep_clang.found() or not _shared_llvm + clang_modules = [ + 'clangBasic', 'clangAST', 'clangCodeGen', 'clangLex', + 'clangDriver', 'clangFrontend', 'clangFrontendTool', + 'clangHandleCXX', 'clangHandleLLVM', 'clangSerialization', + 'clangSema', 'clangParse', 'clangEdit', 'clangAnalysis' + ] + if dep_llvm.version().version_compare('>= 15.0') + clang_modules += 'clangSupport' + endif + if dep_llvm.version().version_compare('>= 16.0') + clang_modules += 'clangASTMatchers' + endif + if dep_llvm.version().version_compare('>= 18.0') + clang_modules += 'clangAPINotes' + endif + if dep_llvm.version().version_compare('>= 22.0') + clang_modules += ['clangAnalysisLifetimeSafety', 'clangOptions'] + endif + + dep_clang = [] + foreach m : clang_modules + dep_clang += cpp.find_library(m, dirs : llvm_libdir, required : true) + endforeach + endif +endif + +dep_lua = dependency('lua54', 'lua5.4', 'lua-5.4', + 'lua53', 'lua5.3', 'lua-5.3', + 'lua', required: false, + allow_fallback: with_tools.contains('freedreno'), + disabler : true, + version: '>=5.3') + +# Be explicit about only using this lib on Windows, to avoid picking +# up random libs with the generic name 'libversion' +dep_version = null_dep +if host_machine.system() == 'windows' + dep_version = cpp.find_library('version') +endif + +dep_elf = dependency('libelf', required : false) +if not with_platform_windows and not dep_elf.found() + dep_elf = cc.find_library('elf', required : false) +endif +if dep_elf.found() + pre_args += '-DUSE_LIBELF' +elif with_gallium_radeonsi + error('Gallium driver radeonsi requires libelf') +endif + +dep_valgrind = dependency('valgrind', required : get_option('valgrind')) +if dep_valgrind.found() + pre_args += '-DHAVE_VALGRIND' +endif + +# AddressSanitizer's leak reports need all the symbols to be present at exit to +# decode well, which runs afoul of our dlopen()/dlclose()ing of the DRI drivers. +# Set a flag so we can skip the dlclose for asan builds. +if get_option('b_sanitize').split(',').contains('address') + asan_c_args = ['-DBUILT_WITH_ASAN=1'] +else + asan_c_args = ['-DBUILT_WITH_ASAN=0'] +endif + +# ThreadSanitizer can't deal with futexes, and reports races for cases we don't care about +# so add a define to work silence these issues. +if get_option('b_sanitize').contains('thread') + pre_args += '-DTHREAD_SANITIZER=1' + tsan_blacklist = '-fsanitize-blacklist=@0@'.format(join_paths(meson.project_source_root(), 'build-support', 'tsan-blacklist.txt')) + if cc.has_argument(tsan_blacklist) + pre_args += tsan_blacklist + else + warning('Compiler does not support "-fsanitize-blacklist", expected race conditions will not be surpressed') + endif +else + pre_args += '-DTHREAD_SANITIZER=0' +endif + +yacc_is_bison = true +needs_flex_bison = with_any_opengl or with_freedreno_vk or with_intel_tools or with_gallium + +if build_machine.system() == 'windows' + # Prefer the winflexbison versions, they're much easier to install and have + # better windows support. + + prog_flex = find_program('win_flex', required : false) + if prog_flex.found() + # windows compatibility (uses instead of and _isatty, + # _fileno functions) + prog_flex = [prog_flex, '--wincompat'] + else + prog_flex = [find_program('flex', 'lex', required : needs_flex_bison, disabler : true)] + endif + # Force flex to use const keyword in prototypes, as relies on __cplusplus or + # __STDC__ macro to determine whether it's safe to use const keyword + prog_flex += '-DYY_USE_CONST=' + + prog_flex_cpp = prog_flex + # Convince win_flex to use for C++ files + # Note that we are using a C99 version here rather than C11, + # because using a C11 version can cause the MSVC CRT headers to define + # static_assert to _Static_assert, which breaks other parts of the CRT + prog_flex_cpp += '-D__STDC_VERSION__=199901' + + prog_bison = find_program('win_bison', required : false) + if not prog_bison.found() + prog_bison = find_program('bison', 'yacc', required : needs_flex_bison, disabler : true) + endif +else + prog_bison = find_program('bison', required : false) + + if not prog_bison.found() + prog_bison = find_program('byacc', required : needs_flex_bison, disabler : true) + yacc_is_bison = false + endif + + # Disable deprecated keyword warnings, since we have to use them for + # old-bison compat. See discussion in + # https://gitlab.freedesktop.org/mesa/mesa/merge_requests/2161 + if find_program('bison', required : false, version : '> 2.3').found() + prog_bison = [prog_bison, '-Wno-deprecated'] + endif + + prog_flex = find_program('flex', required : needs_flex_bison, disabler : true) + prog_flex_cpp = prog_flex +endif + +prog_ln = find_program('ln', required : false) + +_libunwind = get_option('libunwind') \ + .require(not with_platform_android, error_message : 'Android requires the use of the backtrace library, not libunwind') +if host_machine.system() == 'darwin' + dep_unwind = meson.get_compiler('c').find_library('System', required : _libunwind) +else + dep_unwind = dependency('libunwind', required : _libunwind) +endif +if dep_unwind.found() + pre_args += '-DHAVE_LIBUNWIND' +endif + +# TODO: symbol mangling + +with_wayland_bind_display = with_platform_wayland and get_option('legacy-wayland').contains('bind-wayland-display') +if with_platform_wayland + dep_wl_protocols = dependency('wayland-protocols', version : '>= 1.41', default_options: [ 'tests=false' ]) + dep_wayland_client = dependency('wayland-client', version : '>=1.18') + dep_wayland_server = dependency('wayland-server', version : '>=1.18') + if with_egl + dep_wayland_egl = dependency('wayland-egl-backend', version : '>= 3') + dep_wayland_egl_headers = dep_wayland_egl.partial_dependency(compile_args : true) + endif + pre_args += '-DWL_HIDE_DEPRECATED' + if cc.has_function( + 'wl_display_dispatch_queue_timeout', + prefix : '#include ', + dependencies: dep_wayland_client) + pre_args += ['-DHAVE_WL_DISPATCH_QUEUE_TIMEOUT'] + endif + if cc.has_function( + 'wl_display_create_queue_with_name', + prefix : '#include ', + dependencies: dep_wayland_client) + pre_args += ['-DHAVE_WL_CREATE_QUEUE_WITH_NAME'] + endif + # This can be renamed just `wayland` when we require at least 1.8 + mod_wl = import('unstable-wayland') +endif + +# Even if we find OpenMP, Gitlab CI fails to link with gcc/i386 and clang/anyarch. +dep_openmp = null_dep +if host_machine.cpu_family() == 'x86_64' and cc.get_id() == 'gcc' + dep_openmp = dependency('openmp', required : false) + if dep_openmp.found() + pre_args += ['-DHAVE_OPENMP'] + endif +endif + +dep_x11 = null_dep +dep_xext = null_dep +dep_x11_xcb = null_dep +dep_xcb = null_dep +dep_xcb_keysyms = null_dep +dep_xcb_glx = null_dep +dep_xcb_dri3 = null_dep +dep_glproto = null_dep +dep_xxf86vm = null_dep +dep_xcb_present = null_dep +dep_xcb_sync = null_dep +dep_xcb_xfixes = null_dep +dep_xshmfence = null_dep +dep_xcb_xrandr = null_dep +dep_xcb_shm = null_dep +dep_xlib_xrandr = null_dep + +dep_glproto_version = '>= 1.4.14' +dep_xcb_dri3_version = '>= 1.13' +dep_xcb_glx_version = '>= 1.8.1' +dep_xcb_present_version = '>= 1.13' +dep_xlib_version = '>= 1.6' # RHEL8 has 1.6, RHEL9 has 1.7, U22.04 has 1.7 +dep_xlib_xrandr_version = '>= 1.3' +dep_xshmfence_version = '>= 1.1' + +with_dri3_explicit_sync = false +with_xcb_keysyms = false +if with_platform_x11 + dep_xcb = dependency('xcb') + dep_xcb_xrandr = dependency('xcb-randr') + if with_glx == 'xlib' + dep_x11 = dependency('x11', version : dep_xlib_version) + dep_xext = dependency('xext') + elif with_glx == 'dri' + dep_x11 = dependency('x11', version : dep_xlib_version) + dep_xext = dependency('xext') + dep_xcb_glx = dependency('xcb-glx', version : dep_xcb_glx_version) + dep_xcb_shm = dependency('xcb-shm') + elif with_gallium_rusticl + # needed for GL sharing extension + dep_x11 = dependency('x11', version : dep_xlib_version) + endif + if (with_any_vk or with_glx == 'dri' or with_egl or with_gallium_va) + dep_xcb = dependency('xcb') + dep_xcb_keysyms = dependency('xcb-keysyms', required : false) + with_xcb_keysyms = dep_xcb_keysyms.found() + if with_xcb_keysyms + pre_args += '-DXCB_KEYSYMS_AVAILABLE' + endif + dep_x11_xcb = dependency('x11-xcb') + if with_dri_platform == 'drm' and not dep_libdrm.found() + error('libdrm required for gallium video statetrackers when using x11') + endif + endif + if with_dri_platform == 'drm' or with_dri_platform == 'pseudo-drm' + dep_xcb_dri3 = dependency('xcb-dri3', version : dep_xcb_dri3_version) + dep_xcb_present = dependency('xcb-present', version : dep_xcb_present_version) + if (dep_xcb_dri3.version().version_compare('>= 1.17') and + dep_xcb_present.version().version_compare('>= 1.17')) + with_dri3_explicit_sync = true + endif + dep_xcb_shm = dependency('xcb-shm') + dep_xcb_sync = dependency('xcb-sync') + dep_xshmfence = dependency('xshmfence', version : dep_xshmfence_version) + pre_args += '-DHAVE_X11_DRM' + endif + if with_glx == 'dri' or with_glx == 'xlib' + dep_glproto = dependency('glproto', version : dep_glproto_version) + endif + if with_glx == 'dri' + if with_dri_platform == 'drm' or with_dri_platform == 'pseudo-drm' + if with_glx_direct + dep_xxf86vm = dependency('xxf86vm') + endif + endif + endif + if (with_egl or + with_dri or + with_any_vk) + dep_xcb_xfixes = dependency('xcb-xfixes') + endif + if with_any_vk + dep_xcb_dri3 = dependency('xcb-dri3', version : dep_xcb_dri3_version) + dep_xcb_present = dependency('xcb-present', version : dep_xcb_present_version) + dep_xcb_shm = dependency('xcb-shm') + dep_xshmfence = dependency('xshmfence', version : dep_xshmfence_version) + endif + if with_xlib_lease or with_any_vk + dep_xcb_xrandr = dependency('xcb-randr') + endif + if with_xlib_lease + dep_xlib_xrandr = dependency('xrandr', version : dep_xlib_xrandr_version) + endif +endif + +if with_dri + pre_args += '-DHAVE_DRI' +endif +if with_dri2 + pre_args += '-DHAVE_DRI2' +endif +if with_dri3_explicit_sync + pre_args += '-DHAVE_DRI3_EXPLICIT_SYNC' +endif +if with_gallium_drisw_kms + pre_args += '-DHAVE_DRISW_KMS' +endif + +if get_option('gallium-extra-hud') + pre_args += '-DHAVE_GALLIUM_EXTRA_HUD=1' +endif + +dep_lmsensors = cc.find_library('sensors', required : get_option('lmsensors')) +if dep_lmsensors.found() + pre_args += '-DHAVE_LIBSENSORS=1' +endif + +_shader_replacement = get_option('custom-shader-replacement') +if _shader_replacement == '' +else + pre_args += '-DCUSTOM_SHADER_REPLACEMENT' +endif + +allow_fallback_for_perfetto = get_option('allow-fallback-for').contains('perfetto') +with_perfetto = get_option('perfetto') +with_datasources = get_option('datasources') +with_any_datasource = with_datasources.length() != 0 +if with_perfetto + dep_perfetto = dependency( + 'perfetto', + fallback: allow_fallback_for_perfetto ? ['perfetto', 'dep_perfetto'] : [], + ) + pre_args += '-DHAVE_PERFETTO' +endif + +if get_option('android-libperfetto').enabled() + pre_args += '-DANDROID_LIBPERFETTO' +endif + +# Platforms where RenderDoc integration actually works +renderdoc_integration_supported = [ + 'linux', + 'android', +].contains(host_machine.system()) +pre_args += '-DHAVE_RENDERDOC_INTEGRATION=@0@'.format(renderdoc_integration_supported.to_int()) + +with_teflon = get_option('teflon') +if with_teflon and with_tests + dep_flatbuffers = dependency('flatbuffers') + prog_flatc = find_program('flatc') +endif + +with_gpuvis = get_option('gpuvis') +if with_gpuvis + pre_args += '-DHAVE_GPUVIS' +endif + +with_sysprof = get_option('sysprof') +if with_sysprof + dep_sysprof = dependency('sysprof-capture-4', version: '>= 49.0') + pre_args += '-DHAVE_SYSPROF' +endif + +add_project_arguments(pre_args, language : ['c', 'cpp']) +add_project_arguments(c_cpp_args, language : ['c', 'cpp']) +if host_machine.system() == 'darwin' + add_project_arguments(pre_args, language : ['objc']) +endif + +add_project_arguments(c_args, language : ['c']) +add_project_arguments(cpp_args, language : ['cpp']) + +gl_priv_reqs = [] + +if with_glx == 'xlib' + gl_priv_reqs += ['x11', 'xext', 'xcb'] +elif with_glx == 'dri' + gl_priv_reqs += [ + 'x11', 'xext', 'xfixes', 'x11-xcb', 'xcb', + 'xcb-glx >= 1.8.1'] + if with_dri_platform == 'drm' or with_dri_platform == 'pseudo-drm' + gl_priv_reqs += 'xcb-dri2 >= 1.8' + if with_glx_direct + gl_priv_reqs += 'xxf86vm' + endif + endif +endif +if dep_libdrm.found() + gl_priv_reqs += 'libdrm >= 2.4.75' +endif + +gl_priv_libs = [] +if dep_thread.found() + gl_priv_libs += ['-lpthread', '-pthread'] +endif +if dep_m.found() + gl_priv_libs += '-lm' +endif +if dep_dl.found() + gl_priv_libs += '-ldl' +endif + +# FIXME: autotools lists this as incomplete +gbm_priv_libs = [] +if dep_dl.found() + gbm_priv_libs += '-ldl' +endif + +pkg = import('pkgconfig') + +if host_machine.system() == 'windows' and cc.get_argument_syntax() == 'msvc' + prog_dumpbin = find_program('dumpbin', required : false) + with_symbols_check = prog_dumpbin.found() and with_tests + if with_symbols_check + symbols_check_args = ['--dumpbin', prog_dumpbin.full_path()] + endif +elif host_machine.system() == 'windows' and cc.get_argument_syntax() != 'msvc' + # mingw + prog_gendef = find_program('gendef', required : false) + with_symbols_check = prog_gendef.found() and with_tests + if with_symbols_check + symbols_check_args = ['--gendef', prog_gendef.full_path()] + endif +else + prog_nm = find_program('nm') + with_symbols_check = prog_nm.found() and with_tests + if with_symbols_check + symbols_check_args = ['--nm', prog_nm.full_path()] + endif +endif + +# This quirk needs to be applied to sources with functions defined in assembly +# as GCC LTO drops them. See: https://bugs.freedesktop.org/show_bug.cgi?id=109391 +gcc_lto_quirk = (cc.get_id() == 'gcc') ? ['-fno-lto'] : [] + +devenv = environment() + +dir_compiler_nir = join_paths(meson.current_source_dir(), 'src/compiler/nir/') +dir_source_root = meson.project_source_root() + +if host_machine.system() == 'windows' + vulkan_icd_lib_path = import('fs').relative_to(get_option('bindir'), with_vulkan_icd_dir) +else + vulkan_icd_lib_path = get_option('prefix') / get_option('libdir') +endif + +vulkan_manifest_per_architecture = get_option('vulkan-manifest-per-architecture') + +if vulkan_manifest_per_architecture + vulkan_manifest_suffix = '@0@.json'.format(host_machine.cpu()) +else + vulkan_manifest_suffix = 'json' +endif + +subdir('include') +subdir('bin') +subdir('src') + +meson.add_devenv(devenv) + +sphinx = find_program('sphinx-build', version : '>= 4.3', + required: get_option('html-docs')) +if sphinx.found() + subdir('docs') +endif + +summary( + { + 'prefix': get_option('prefix'), + 'libdir': get_option('libdir'), + 'includedir': get_option('includedir'), + }, + section: 'Directories' +) + +summary( + { + 'c_cpp_args': c_cpp_args, + }, + section: 'Common C and C++ arguments' +) + +summary( + { + 'OpenGL': with_opengl, + 'ES1': with_gles1, + 'ES2': with_gles2, + 'GLVND': with_glvnd, + }, + section: 'OpenGL', bool_yn: true +) + +summary( + { + 'Platform': with_dri_platform, + 'Driver dir': dri_drivers_path, + }, + section: 'DRI', bool_yn: true, list_sep: ' ' +) + +summary( + { + 'Enabled': with_glx != 'disabled', + 'Provider': with_glx == 'disabled' ? 'None' : with_glx + }, + section: 'GLX', bool_yn: true, list_sep: ' ' +) + +egl_summary = {'Enabled': with_egl} +if with_egl + egl_drivers = [] + if with_dri + egl_drivers += 'builtin:egl_dri2' + endif + if with_dri_platform == 'drm' or with_dri_platform == 'pseudo-drm' + egl_drivers += 'builtin:egl_dri3' + endif + if with_platform_windows + egl_drivers += 'builtin:wgl' + endif + egl_summary += {'Drivers': egl_drivers} + egl_summary += {'Platforms': _platforms} +endif +summary(egl_summary, section: 'EGL', bool_yn: true, list_sep: ' ') + +gbm_summary = {'Enabled': with_gbm} +if with_gbm + gbm_summary += {'External libgbm': get_option('libgbm-external'), 'Backends path': gbm_backends_path} +endif +summary(gbm_summary, section: 'GBM', bool_yn: true, list_sep: ' ') + +vulkan_summary = {'Drivers': _vulkan_drivers.length() != 0 ? _vulkan_drivers : false } +if with_any_vk + vulkan_summary += {'Platforms': _platforms} + vulkan_summary += {'ICD dir': with_vulkan_icd_dir} + if with_any_vulkan_layers + vulkan_summary += {'Layers': get_option('vulkan-layers')} + endif + vulkan_summary += {'Intel Ray tracing': with_intel_vk_rt} +endif +summary(vulkan_summary, section: 'Vulkan', bool_yn: true, list_sep: ' ') + +video_summary = {'Codecs': _codecs.length() != 0 ? _codecs : false} +video_apis = [] +if with_gallium_va + video_apis += 'va' +endif +if with_gallium_mediafoundation + video_apis += 'mediafoundation' +endif +if with_any_vk + video_apis += 'vulkan' +endif +video_summary += {'APIs': video_apis.length() != 0 ? video_apis : false} +summary(video_summary, section: 'Video', bool_yn: true, list_sep: ' ') + +llvm_summary = {'Required': with_llvm} +if dep_llvm.found() + llvm_summary += {'Version': dep_llvm.version()} +endif +summary(llvm_summary, section: 'LLVM', bool_yn: true, list_sep: ' ') + +gallium_summary = {'Enabled': with_gallium} +if with_gallium + gallium_summary += {'Drivers': gallium_drivers} + gallium_summary += {'Platforms': _platforms} + + gallium_frontends = ['mesa'] + if with_gallium_va + gallium_frontends += 'va' + endif + if with_gallium_mediafoundation + gallium_frontends += 'mediafoundation' + endif + if with_gallium_rusticl + gallium_frontends += 'rusticl' + endif + gallium_summary += {'Frontends': gallium_frontends} + gallium_summary += {'HUD lm-sensors': dep_lmsensors.found()} +endif +summary(gallium_summary, section: 'Gallium', bool_yn: true, list_sep: ' ') + +perfetto_summary = {'Enabled': with_perfetto} +if with_perfetto and with_any_datasource + perfetto_summary += {'Data source': with_datasources} +endif +summary(perfetto_summary, section: 'Perfetto', bool_yn: true, list_sep: ' ') + +teflon_summary = {'Enabled': with_teflon} +summary(teflon_summary, section: 'Teflon (TensorFlow Lite delegate)', bool_yn: true, list_sep: ' ') diff --git a/local/recipes/libs/mesa/source/meson.options b/local/recipes/libs/mesa/source/meson.options new file mode 100644 index 0000000000..2a3a5d085a --- /dev/null +++ b/local/recipes/libs/mesa/source/meson.options @@ -0,0 +1,881 @@ +# Copyright © 2017-2019 Intel Corporation +# SPDX-License-Identifier: MIT + +option( + 'split-debug', + type : 'feature', + value : 'disabled', + description : 'split debug information (-gsplit-dwarf compile flag) and debug information in the gdb index format (--gdb-index)', +) +option( + 'platforms', + type : 'array', + value : ['auto'], + choices : [ + 'auto', 'x11', 'wayland', 'haiku', 'android', 'windows', 'macos', 'redox', + ], + description : 'window systems to support. If this is set to `auto`, all ' + + 'platforms applicable will be enabled.' +) + +option( + 'egl-native-platform', + type : 'combo', + value : 'auto', + choices : [ + 'auto', 'x11', 'wayland', 'haiku', 'android', 'windows', + 'surfaceless', 'drm', 'redox', + ], + description : 'the window system EGL assumes for EGL_DEFAULT_DISPLAY', +) + +option( + 'android-stub', + type : 'boolean', + value : false, + description : 'Build against android-stub', +) + +option( + 'android-strict', + type : 'boolean', + value : true, + description : 'Enable strict Android compliance. Disabling may cause CTS ' + + 'failures or other problems, but allows drivers to expose ' + + 'capabilities that are normally hidden. Default: true' +) + +option( + 'android-libbacktrace', + type : 'feature', + description : 'Use Android\'s libbacktrace', +) + +option( + 'android-libperfetto', + type : 'feature', + description : 'Use Android\'s libperfetto', +) + +option( + 'dri-drivers-path', + type : 'string', + value : '', + description : 'Location to install dri drivers. Default: $libdir/dri.' +) + +option( + 'unversion-libgallium', + type : 'boolean', + value : false, + description : 'Do not include mesa version in libgallium DSO filename. ' + + 'Do not enable unless you know what you are doing. Default: false' +) + +option( + 'expat', + type : 'feature', + value : 'auto', + description : 'Controls the use of expat. ' + + 'Cannot be disabled if xmlconfig is enabled.' +) + +option( + 'gallium-drivers', + type : 'array', + value : ['auto'], + choices : [ + 'all', 'auto', + 'asahi', 'crocus', 'd3d12', 'ethosu', 'etnaviv', 'freedreno', 'i915', 'iris', + 'lima', 'llvmpipe', 'nouveau', 'panfrost', 'r300', 'r600', 'radeonsi', + 'rocket', 'softpipe', 'svga', 'tegra', 'v3d', 'vc4', 'virgl', 'zink', + ], + description : 'List of gallium drivers to build. If this is set to auto ' + + 'all drivers applicable to the target OS/architecture ' + + 'will be built.' +) + +option( + 'gallium-extra-hud', + type : 'boolean', + value : false, + description : 'Enable HUD block/NIC I/O HUD status support', +) + +option( + 'gallium-va', + type : 'feature', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + description : 'enable gallium va frontend.', +) + +option( + 'gallium-mediafoundation', + type : 'feature', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + description : 'enable gallium mediafoundation frontend.', +) + +option( + 'gallium-mediafoundation-test', + type : 'boolean', + value : false, + description : 'enable gallium mediafoundation frontend tests.', +) + +option( + 'va-libs-path', + type : 'string', + value : '', + description : 'path to put va libraries. defaults to $libdir/dri.' +) + +option( + 'gallium-d3d10umd', + type : 'boolean', + value : false, + description : 'build gallium D3D10 WDDM UMD frontend.', +) + +option( + 'gallium-rusticl', + type : 'boolean', + value : false, + description : 'build gallium "rusticl" OpenCL frontend.', +) + +option( + 'gallium-rusticl-enable-drivers', + type : 'array', + value : ['auto'], + choices : ['auto', 'asahi', 'freedreno', 'radeonsi'], + description : 'List of gallium drivers for which rusticl will be enabled ' + + 'by default', +) + +option( + 'gallium-wgl-dll-name', + type : 'string', + value : 'libgallium_wgl', + description : 'name of gallium wgl target DLL built for Windows. ' + + 'defaults to libgallium_wgl.dll to match DRI', +) + +option( + 'gallium-d3d10-dll-name', + type : 'string', + value : 'libgallium_d3d10', + description : 'name of gallium d3d10 target DLL built for Windows. ' + + 'defaults to libgallium_d3d10.dll to match DRI', +) + +option( + 'mediafoundation-store-dll', + type : 'boolean', + value : false, + description : 'Selects whether the gallium mediafoundation DLL is built for the store. ', +) + +option( + 'mediafoundation-codecs', + type : 'array', + value : ['all'], + choices: [ + 'all', 'h264enc', 'h265enc', 'av1enc' + ], + description : 'List of codecs to build mediafoundation frontend DLLs for. ' + + 'These will generate different MFT DLLs per codec and link' + + 'against the gallium drivers which uses the video-codecs option' +) + +option( + 'static-libclc', + type : 'array', + value : [], + choices : ['spirv', 'spirv64', 'all'], + description : 'Link libclc SPIR-V statically.', +) + +option( + 'd3d-drivers-path', + type : 'string', + value : '', + description : 'Location of D3D drivers. Default: $libdir/d3d', +) + +option( + 'vulkan-drivers', + type : 'array', + value : ['auto'], + choices : ['auto', 'amd', 'broadcom', 'freedreno', 'intel', 'intel_hasvk', + 'panfrost', 'swrast', 'virtio', 'imagination', + 'microsoft-experimental', 'nouveau', 'asahi', 'gfxstream', + 'kosmickrisp', 'all'], + description : 'List of vulkan drivers to build. If this is set to auto ' + + 'all drivers applicable to the target OS/architecture ' + + 'will be built' +) + +# Note that the freedreno gallium driver doesn't support KGSL. +# On those systems, zink with Turnip should be used instead. +# WSL is only supported for the replay tool. +option( + 'freedreno-kmds', + type : 'array', + value : ['msm'], + choices : ['msm', 'kgsl', 'virtio', 'wsl'], + description : 'List of kernel-mode drivers to enable for freedreno ' + + 'gallium and vulkan driver', +) + +option( + 'amdgpu-virtio', + type : 'boolean', + value : false, + description : 'use experimental virtio backend for radeonsi/radv', +) +option( + 'imagination-srv', + type : 'boolean', + value : false, + description : 'Enable Services backend for Imagination Technologies ' + + 'vulkan driver', +) + +option('imagination-uscgen-devices', + type : 'array', + value : [], + choices : ['axe-1-16m', 'bxs-4-64', 'gx6250'], + description : 'List of devices for which to pre-build USC program binaries.', +) + +option( + 'shader-cache', + type : 'feature', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + description : 'Build with on-disk shader cache support.', +) + +option( + 'shader-cache-default', + type : 'boolean', + value : true, + description : 'If set to false, the feature is only activated when ' + + 'environment variable MESA_SHADER_CACHE_DISABLE is set ' + + 'to false', +) + +option( + 'shader-cache-max-size', + type : 'string', + value : '', + description : 'Default value for MESA_SHADER_CACHE_MAX_SIZE enviroment ' + + 'variable. If set, determines the maximum size of the ' + + 'on-disk cache of compiled shader programs, can be overriden ' + + 'by enviroment variable if needed. Should be set to a number ' + + 'optionally followed by ``K``, ``M``, or ``G`` to specify ' + + 'a size in kilobytes, megabytes, or gigabytes. By default, ' + + 'gigabytes will be assumed. And if unset, a maximum size of ' + + '1GB will be used.' +) + +option( + 'vulkan-icd-dir', + type : 'string', + value : '', + description : 'Location relative to prefix to put vulkan icds on install. ' + + 'Default: $datadir/vulkan/icd.d' +) + +option( + 'vulkan-manifest-per-architecture', + type : 'boolean', + value : true, + description : 'If true, Vulkan ICDs have a separate JSON manifest per ' + + 'architecture, for example lvp_icd.x86_64.json. ' + + '(Recommended for non-default ${prefix}.) ' + + 'If false, all architectures share a single JSON manifest, ' + + 'for example lvp_icd.json, referencing the library by its ' + + 'basename. ' + + '(Recommended for Unix OS distros installing into /usr.)' +) + +option( + 'moltenvk-dir', + type : 'string', + value : '', + description : 'Location of the MoltenVk SDK. Default: ' +) + +option( + 'vulkan-layers', + type : 'array', + value : [], + choices : [ + 'device-select', 'intel-nullhw', 'overlay', 'screenshot', 'anti-lag', + 'vram-report-limit', + ], + description : 'List of vulkan layers to build' +) + +option( + 'shared-glapi', + type : 'feature', + deprecated: true, + description : 'Does nothing, left here for a while to avoid build breakages.', +) + +option( + 'gles1', + type : 'feature', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + description : 'Build support for OpenGL ES 1.x' +) + +option( + 'gles2', + type : 'feature', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + description : 'Build support for OpenGL ES 2.x and 3.x' +) + +option( + 'opengl', + type : 'boolean', + value : true, + description : 'Build support for desktop OpenGL' +) + +option( + 'gbm', + type : 'feature', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + description : 'Build support for gbm platform' +) + +option( + 'libgbm-external', + type: 'boolean', + value: false, + description: 'Whether to use external libgbm (default: use in-tree copy)' +) + +option( + 'gbm-backends-path', + type : 'string', + value : '', + description : 'Locations to search for gbm backends, passed as colon ' + + 'separated list. Default: $libdir/gbm.' +) + +option( + 'glx', + type : 'combo', + value : 'auto', + choices : ['auto', 'disabled', 'dri', 'xlib'], + description : 'Build support for GLX platform' +) + +option( + 'egl', + type : 'feature', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + description : 'Build support for EGL platform' +) + +option( + 'glvnd', + type : 'feature', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + description : 'Enable GLVND support.' +) + +option( + 'microsoft-clc', + type : 'feature', + value : 'auto', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + description : 'Build support for the Microsoft CLC to DXIL compiler' +) + +option( + 'spirv-to-dxil', + type : 'boolean', + value : false, + description : 'Build support for the SPIR-V to DXIL library' +) + +option( + 'glvnd-vendor-name', + type : 'string', + value : 'mesa', + description : 'Vendor name string to use for glvnd libraries' +) + +option( + 'glx-read-only-text', + type : 'boolean', + value : false, + description : 'Disable writable .text section on x86 (decreases performance)' +) + +option( + 'llvm', + type : 'feature', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + description : 'Build with LLVM support.' +) + +option( + 'shared-llvm', + type : 'feature', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + description : 'Whether to link LLVM shared or statically.' +) + +option( + 'draw-use-llvm', + type : 'boolean', + value : true, + description : 'Whether to use LLVM for the Gallium draw module, if LLVM ' + + 'is included.' +) + +option( + 'amd-use-llvm', + type : 'boolean', + value : true, + description : 'Whether to use LLVM for the AMD drivers, if LLVM ' + + 'is included.' +) + +option ( + 'llvm-orcjit', + type : 'boolean', + value : false, + description: 'Build llvmpipe with LLVM ORCJIT support. Has no effect when ' + + 'building for architectures without LLVM MCJIT support -- ' + + 'ORCJIT is the only choice on such architectures and will ' + + 'always be enabled.' +) + +option( + 'valgrind', + type : 'feature', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + description : 'Build with valgrind support' +) + +option( + 'libunwind', + type : 'feature', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + description : 'Use libunwind for stack-traces' +) + +option( + 'lmsensors', + type : 'feature', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + description : 'Enable HUD lmsensors support.' +) + +option( + 'build-tests', + type : 'boolean', + value : false, + description : 'Build unit tests. Currently this will build *all* unit ' + + 'tests except the ACO tests, which may build more than expected.' +) + +option( + 'enable-glcpp-tests', + type : 'boolean', + value : true, + description : 'Build glcpp unit tests. These are flaky on CI.' +) + +option( + 'build-radv-tests', + type : 'boolean', + value : false, + description : 'Build RADV tests. These do not require an AMD GPU.' +) + +option( + 'build-aco-tests', + type : 'boolean', + value : false, + description : 'Build ACO tests. These require RADV and glslang but not ' + + 'an AMD GPU.' +) + +option( + 'install-intel-gpu-tests', + type : 'boolean', + value : false, + description : 'Build and install Intel unit tests which require the GPU. ' + + 'This option is for developers and the Intel CI system only.' +) + +option( + 'html-docs', + type : 'feature', + value : 'disabled', + description : 'Build HTML documentation.' +) + +option( + 'html-docs-path', + type : 'string', + value : '', + description : 'Location to install HTML documentation. Default: $datadir/doc/mesa.' +) + +option( + 'selinux', + type : 'boolean', + deprecated : true, + description : 'Does nothing, left here for a while to avoid build breakages.', +) + +option( + 'execmem', + type : 'boolean', + deprecated : true, + description : 'Does nothing, left here for a while to avoid build breakages.', +) + +option( + 'tools', + type : 'array', + value : [], + choices : ['drm-shim', 'etnaviv', 'freedreno', 'glsl', 'intel', 'intel-ui', + 'nir', 'nouveau', 'lima', 'panfrost', 'asahi', 'imagination', + 'zink', 'all', 'dlclose-skip'], + description : 'List of tools to build. (Note: `intel-ui` selects `intel`)', +) + +option( + 'power8', + type : 'feature', + description : 'Does nothing, left here for a while to avoid build breakages.', + deprecated: true, +) + +option( + 'xlib-lease', + type : 'feature', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + description : 'Enable VK_EXT_acquire_xlib_display.' +) + +option( + 'glx-direct', + type : 'boolean', + value : true, + description : 'Enable direct rendering in GLX and EGL for DRI', +) + +option('egl-lib-suffix', + type : 'string', + value : '', + description : 'Suffix to append to EGL library name. Default: none.' +) + +option( + 'gles-lib-suffix', + type : 'string', + value : '', + description : 'Suffix to append to GLES library names. Default: none.' +) + +option( + 'platform-sdk-version', + type : 'integer', + min : 25, + max : 10000, + value : 35, + description : 'Android Platform SDK version. Default: Android15 version.' +) + +option( + 'allow-kcmp', + type : 'feature', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + description : 'Allow using KCMP_FILE to compare file descriptions. ' + + 'auto = allowed everywhere except on Android' +) + +option( + 'zstd', + type : 'feature', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + description : 'Use ZSTD instead of ZLIB in some cases.' +) + +option( + 'zlib', + type : 'feature', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + value : 'enabled', + description : 'Use ZLIB to build driver. Default: enabled' +) + +option( + 'display-info', + type : 'feature', + description : 'Use libdisplay-info to build driver.' +) + +option( + 'sse2', + type : 'boolean', + value : true, + description : 'use msse2 flag for x86. Uses sse/sse2 instead of x87. Default: true', +) + +option( + 'perfetto', + type : 'boolean', + value : false, + description : 'Enable performance analysis with Perfetto. Default: false' +) + +option( + 'datasources', + type : 'array', + value : ['auto'], + choices : ['auto', 'panfrost', 'intel', 'freedreno'], + description : 'List of Perfetto datasources to build. If this is set to ' + + '`auto`, datasources that can not be build are skipped. ' + + 'Default: [`auto`]' +) + +option( + 'teflon', + type : 'boolean', + value : false, + description : 'Enable TensorFlow Lite delegate. Default: false' +) + +option( + 'gpuvis', + type : 'boolean', + value : false, + description : 'Enable tracing markers for gpuvis. Default: false' +) + +option( + 'sysprof', + type : 'boolean', + value : false, + description : 'Enable tracing markers for sysprof. Default: false' +) + +option( + 'custom-shader-replacement', + type : 'string', + value : '', + description : 'Enable a custom shader replacement mechanism. Note that ' + + 'enabling this option requires adding/generating a ' + + 'shader_replacement.h file that can be included (see ' + + 'shaderapi.c).' +) + +option( + 'vmware-mks-stats', + type : 'boolean', + value : false, + description : 'Build gallium VMware/svga driver with mksGuestStats ' + + 'instrumentation.' +) + +option( + 'vulkan-beta', + type : 'boolean', + value : false, + description : 'Build vulkan drivers with BETA extensions enabled.' +) + +option( + 'intel-rt', + type : 'feature', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + description : 'Build Ray Tracing on supported hardware.' +) + +option( + 'intel-elk', + type : 'boolean', + value : true, + description : 'Build ELK compiler (used for Gfx8 and earlier). ' + + 'This is required for Crocus and Hasvk, and it is ' + + 'optional for Iris and various developer tools.' +) + +option( + 'video-codecs', + type : 'array', + value : ['all_free'], + choices: [ + 'all', 'all_free', 'vc1dec', 'h264dec', 'h264enc', 'h265dec', 'h265enc', + 'av1dec', 'av1enc', 'vp9dec', 'mpeg12dec', 'jpegdec' + ], + description : 'List of codecs to build support for. ' + + 'Distros might want to consult their legal department before ' + + 'enabling these. This is used for all video APIs (vaapi, ' + + 'vulkan). Non-patent encumbered codecs will be ' + + 'enabled by default with the all_free default value.' +) + +option( + 'gallium-d3d12-video', + type : 'feature', + value : 'auto', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + description : 'build gallium d3d12 with video support.', +) + +option( + 'gallium-d3d12-graphics', + type : 'feature', + value : 'auto', + description : 'build gallium d3d12 with graphics pipeline support.', +) + +option( + 'radv-build-id', + type : 'string', + value : '', + description : 'Override build id for shader cache keys (hex string). ' + + 'Can be extracted with readelf -x .note.gnu.build-id' +) + +option( + 'radeonsi-build-id', + type : 'string', + value : '', + description : 'Override build id for shader cache keys (hex string). ' + + 'Can be extracted with readelf -x .note.gnu.build-id' +) + +option( + 'min-windows-version', + type : 'integer', + min : 7, + max : 11, + value : 8, + description : 'Minimum Windows version to support. Defaults to Windows 8.' +) + +option( + 'xmlconfig', + type : 'feature', + value : 'auto', + deprecated: {'true': 'enabled', 'false': 'disabled'}, + description : 'Build custom xmlconfig (driconf) support. If disabled, ' + + 'the default driconf file is hardcoded into Mesa. ' + + 'Requires expat.' +) + +option( + 'legacy-wayland', + type : 'array', + value : [], + description : 'Build legacy Wayland support features.', + choices : [ + 'bind-wayland-display', + ], +) + +option( + 'legacy-x11', + type : 'array', + value : [], + description : 'Build legacy X11 support features.', + deprecated : true, + choices : [ + ], +) + +option( + 'mesa-clc', + type : 'combo', + value : 'auto', + choices : [ + 'enabled', 'system', 'auto' + ], + description : 'Build the mesa-clc compiler or use a system version.' +) + +option( + 'install-mesa-clc', + type : 'boolean', + value : false, + description : 'Install the mesa-clc compiler (if needed for cross builds).' +) + +option( + 'mesa-clc-bundle-headers', + type : 'combo', + value : 'auto', + choices : [ + 'enabled', 'auto' + ], + description : 'Bundle the OpenCL headers into the mesa-clc binary (default to bundle if static LLVM is used). Note, it might require rebuilding mesa-clc if opencl-c.h or opencl-c-base.h are changed (e.g. on Clang upgrades).' +) + +option( + 'precomp-compiler', + type : 'combo', + value : 'auto', + choices : [ + 'enabled', 'system', 'auto' + ], + description : 'Build drivers internal shader compilers or use a system version' +) + +option( + 'install-precomp-compiler', + type : 'boolean', + value : false, + description : 'Install the drivers internal shader compilers (if needed for cross builds).' +) + +option( + 'allow-fallback-for', + type : 'array', + value : ['perfetto'], + choices : [ + 'libdrm', 'libva', 'perfetto', + ], + description : 'Allows the fallback mechanism if the dependency is not available on the system, or too old.' +) + +option( + 'virtgpu_kumquat', + type : 'boolean', + value : false, + description : 'Build virtgpu_kumquat (only useful with gfxstream currently)' +) + +option( + 'spirv-tools', + type : 'feature', + description : 'Use SPIRV-Tools for dumping SPIR-V for debugging purposes (required by CLC)' +) + +option( + 'allow-broken-lto', + type : 'boolean', + value : false, + description : 'Manual override switch to enable LTO, which is unsupported due to being broken. WARNING: This option may break your driver randomly!' +) + +option( + 'intel-virtio-experimental', + type : 'boolean', + value : false, + description : 'use experimental virtio backend for intel driver', +) diff --git a/local/recipes/libs/mesa/source/src/egl/drivers/dri2/egl_dri2.c b/local/recipes/libs/mesa/source/src/egl/drivers/dri2/egl_dri2.c new file mode 100644 index 0000000000..295a8bedb6 --- /dev/null +++ b/local/recipes/libs/mesa/source/src/egl/drivers/dri2/egl_dri2.c @@ -0,0 +1,3211 @@ +/* + * Copyright © 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Kristian Høgsberg + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIBDRM +#include +#include "drm-uapi/drm_fourcc.h" +#endif +#include +#include "mesa_interface.h" +#include +#include +#include "dri_screen.h" + +#ifdef HAVE_WAYLAND_PLATFORM +#include "linux-dmabuf-unstable-v1-client-protocol.h" +#if HAVE_BIND_WL_DISPLAY +#include "wayland-drm-client-protocol.h" +#include "wayland-drm.h" +#endif +#include +#endif + +#ifdef HAVE_X11_PLATFORM +#include "X11/Xlibint.h" +#include "x11_dri3.h" +#include "x11_display.h" +#endif + +#include "GL/mesa_glinterop.h" +#include "pipe-loader/pipe_loader.h" +#include "loader/loader.h" +#include "mesa/glapi/glapi/glapi.h" +#include "pipe/p_screen.h" +#include "util/bitscan.h" +#include "util/driconf.h" +#include "util/libsync.h" +#include "util/os_file.h" +#include "util/u_atomic.h" +#include "util/u_call_once.h" +#include "util/u_math.h" +#include "util/u_vector.h" +#include "egl_dri2.h" +#include "egldefines.h" +#include "dispatch.h" + +#define NUM_ATTRIBS 16 + +static const enum pipe_format dri2_pbuffer_visuals[] = { + PIPE_FORMAT_R16G16B16A16_FLOAT, + PIPE_FORMAT_R16G16B16X16_FLOAT, + PIPE_FORMAT_R16G16B16A16_UNORM, + PIPE_FORMAT_R16G16B16X16_UNORM, + PIPE_FORMAT_B10G10R10A2_UNORM, + PIPE_FORMAT_B10G10R10X2_UNORM, + PIPE_FORMAT_BGRA8888_UNORM, + PIPE_FORMAT_BGRX8888_UNORM, + PIPE_FORMAT_B5G6R5_UNORM, +}; + +static void +dri2_gl_flush() +{ + CALL_Flush(GET_DISPATCH(), ()); +} + +static void +dri2_get_pbuffer_drawable_info(struct dri_drawable *draw, int *x, int *y, int *w, + int *h, void *loaderPrivate) +{ + struct dri2_egl_surface *dri2_surf = loaderPrivate; + + *x = *y = 0; + *w = dri2_surf->base.Width; + *h = dri2_surf->base.Height; +} + +static void +dri2_kopper_get_pbuffer_drawable_info(struct dri_drawable *draw, + int *w, int *h, void *loaderPrivate) +{ + struct dri2_egl_surface *dri2_surf = loaderPrivate; + + *w = dri2_surf->base.Width; + *h = dri2_surf->base.Height; +} + +static int +dri2_get_bytes_per_pixel(struct dri2_egl_surface *dri2_surf) +{ + const int depth = dri2_surf->base.Config->BufferSize; + return depth ? util_next_power_of_two(depth / 8) : 0; +} + +static void +dri2_put_image(struct dri_drawable *draw, int op, int x, int y, int w, int h, + char *data, void *loaderPrivate) +{ + struct dri2_egl_surface *dri2_surf = loaderPrivate; + const int bpp = dri2_get_bytes_per_pixel(dri2_surf); + const int width = dri2_surf->base.Width; + const int height = dri2_surf->base.Height; + const int dst_stride = width * bpp; + const int src_stride = w * bpp; + const int x_offset = x * bpp; + int copy_width = src_stride; + + if (!dri2_surf->swrast_device_buffer) + dri2_surf->swrast_device_buffer = malloc(height * dst_stride); + + if (dri2_surf->swrast_device_buffer) { + const char *src = data; + char *dst = dri2_surf->swrast_device_buffer; + + dst += x_offset; + dst += y * dst_stride; + + /* Drivers are allowed to submit OOB PutImage requests, so clip here. */ + if (copy_width > dst_stride - x_offset) + copy_width = dst_stride - x_offset; + if (h > height - y) + h = height - y; + + for (; 0 < h; --h) { + memcpy(dst, src, copy_width); + dst += dst_stride; + src += src_stride; + } + } +} + +static void +dri2_get_image(struct dri_drawable *read, int x, int y, int w, int h, char *data, + void *loaderPrivate) +{ + struct dri2_egl_surface *dri2_surf = loaderPrivate; + const int bpp = dri2_get_bytes_per_pixel(dri2_surf); + const int width = dri2_surf->base.Width; + const int height = dri2_surf->base.Height; + const int src_stride = width * bpp; + const int dst_stride = w * bpp; + const int x_offset = x * bpp; + int copy_width = dst_stride; + const char *src = dri2_surf->swrast_device_buffer; + char *dst = data; + + if (!src) { + memset(data, 0, copy_width * h); + return; + } + + src += x_offset; + src += y * src_stride; + + /* Drivers are allowed to submit OOB GetImage requests, so clip here. */ + if (copy_width > src_stride - x_offset) + copy_width = src_stride - x_offset; + if (h > height - y) + h = height - y; + + for (; 0 < h; --h) { + memcpy(dst, src, copy_width); + src += src_stride; + dst += dst_stride; + } +} + +/* HACK: technically we should have swrast_null, instead of these. + */ +const __DRIswrastLoaderExtension swrast_pbuffer_loader_extension = { + .base = {__DRI_SWRAST_LOADER, 1}, + .getDrawableInfo = dri2_get_pbuffer_drawable_info, + .putImage = dri2_put_image, + .getImage = dri2_get_image, +}; + +const __DRIkopperLoaderExtension kopper_pbuffer_loader_extension = { + .base = {__DRI_KOPPER_LOADER, 1}, + .GetDrawableInfo = dri2_kopper_get_pbuffer_drawable_info, + .SetSurfaceCreateInfo = NULL, +}; + +static const EGLint dri2_to_egl_attribute_map[__DRI_ATTRIB_MAX] = { + [__DRI_ATTRIB_BUFFER_SIZE] = EGL_BUFFER_SIZE, + [__DRI_ATTRIB_LEVEL] = EGL_LEVEL, + [__DRI_ATTRIB_LUMINANCE_SIZE] = EGL_LUMINANCE_SIZE, + [__DRI_ATTRIB_DEPTH_SIZE] = EGL_DEPTH_SIZE, + [__DRI_ATTRIB_STENCIL_SIZE] = EGL_STENCIL_SIZE, + [__DRI_ATTRIB_SAMPLE_BUFFERS] = EGL_SAMPLE_BUFFERS, + [__DRI_ATTRIB_SAMPLES] = EGL_SAMPLES, + [__DRI_ATTRIB_MAX_PBUFFER_WIDTH] = EGL_MAX_PBUFFER_WIDTH, + [__DRI_ATTRIB_MAX_PBUFFER_HEIGHT] = EGL_MAX_PBUFFER_HEIGHT, + [__DRI_ATTRIB_MAX_PBUFFER_PIXELS] = EGL_MAX_PBUFFER_PIXELS, + [__DRI_ATTRIB_MAX_SWAP_INTERVAL] = EGL_MAX_SWAP_INTERVAL, + [__DRI_ATTRIB_MIN_SWAP_INTERVAL] = EGL_MIN_SWAP_INTERVAL, + [__DRI_ATTRIB_YINVERTED] = EGL_Y_INVERTED_NOK, +}; + +const struct dri_config * +dri2_get_dri_config(struct dri2_egl_config *conf, EGLint surface_type, + EGLenum colorspace) +{ + const bool double_buffer = surface_type == EGL_WINDOW_BIT; + const bool srgb = colorspace == EGL_GL_COLORSPACE_SRGB_KHR; + + return conf->dri_config[double_buffer][srgb]; +} + +static EGLBoolean +dri2_match_config(const _EGLConfig *conf, const _EGLConfig *criteria) +{ +#ifdef HAVE_X11_PLATFORM + if (conf->Display->Platform == _EGL_PLATFORM_X11 && + conf->AlphaSize > 0 && + conf->NativeVisualID != criteria->NativeVisualID) + return EGL_FALSE; +#endif + + if (_eglCompareConfigs(conf, criteria, NULL, EGL_FALSE) != 0) + return EGL_FALSE; + + if (!_eglMatchConfig(conf, criteria)) + return EGL_FALSE; + + return EGL_TRUE; +} + +void +dri2_get_shifts_and_sizes(const struct dri_config *config, int *shifts, + unsigned int *sizes) +{ + driGetConfigAttrib(config, __DRI_ATTRIB_RED_SHIFT, + (unsigned int *)&shifts[0]); + driGetConfigAttrib(config, __DRI_ATTRIB_GREEN_SHIFT, + (unsigned int *)&shifts[1]); + driGetConfigAttrib(config, __DRI_ATTRIB_BLUE_SHIFT, + (unsigned int *)&shifts[2]); + driGetConfigAttrib(config, __DRI_ATTRIB_ALPHA_SHIFT, + (unsigned int *)&shifts[3]); + driGetConfigAttrib(config, __DRI_ATTRIB_RED_SIZE, &sizes[0]); + driGetConfigAttrib(config, __DRI_ATTRIB_GREEN_SIZE, &sizes[1]); + driGetConfigAttrib(config, __DRI_ATTRIB_BLUE_SIZE, &sizes[2]); + driGetConfigAttrib(config, __DRI_ATTRIB_ALPHA_SIZE, &sizes[3]); +} + +enum pipe_format +dri2_image_format_for_pbuffer_config(struct dri2_egl_display *dri2_dpy, + const struct dri_config *config) +{ + struct gl_config *gl_config = (struct gl_config *) config; + return gl_config->color_format; +} + +struct dri2_egl_config * +dri2_add_config(_EGLDisplay *disp, const struct dri_config *dri_config, + EGLint surface_type, const EGLint *attr_list) +{ + struct dri2_egl_config *conf; + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + _EGLConfig base; + unsigned int attrib, value, double_buffer; + bool srgb = false; + EGLint key, bind_to_texture_rgb, bind_to_texture_rgba; + _EGLConfig *matching_config; + EGLint num_configs = 0; + EGLint config_id; + + _eglInitConfig(&base, disp, _eglGetArraySize(disp->Configs) + 1); + + double_buffer = 0; + bind_to_texture_rgb = 0; + bind_to_texture_rgba = 0; + + for (int i = 0; i < __DRI_ATTRIB_MAX; ++i) { + if (!driIndexConfigAttrib(dri_config, i, &attrib, &value)) + break; + + switch (attrib) { + case __DRI_ATTRIB_RENDER_TYPE: + if (value & __DRI_ATTRIB_FLOAT_BIT) + base.ComponentType = EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT; + if (value & __DRI_ATTRIB_RGBA_BIT) + value = EGL_RGB_BUFFER; + else if (value & __DRI_ATTRIB_LUMINANCE_BIT) + value = EGL_LUMINANCE_BUFFER; + else + return NULL; + base.ColorBufferType = value; + break; + + case __DRI_ATTRIB_CONFIG_CAVEAT: + if (value & __DRI_ATTRIB_NON_CONFORMANT_CONFIG) + value = EGL_NON_CONFORMANT_CONFIG; + else if (value & __DRI_ATTRIB_SLOW_BIT) + value = EGL_SLOW_CONFIG; + else + value = EGL_NONE; + base.ConfigCaveat = value; + break; + + case __DRI_ATTRIB_BIND_TO_TEXTURE_RGB: + bind_to_texture_rgb = value; + break; + + case __DRI_ATTRIB_BIND_TO_TEXTURE_RGBA: + bind_to_texture_rgba = value; + break; + + case __DRI_ATTRIB_DOUBLE_BUFFER: + double_buffer = value; + break; + + case __DRI_ATTRIB_RED_SIZE: + base.RedSize = value; + break; + + case __DRI_ATTRIB_GREEN_SIZE: + base.GreenSize = value; + break; + + case __DRI_ATTRIB_BLUE_SIZE: + base.BlueSize = value; + break; + + case __DRI_ATTRIB_ALPHA_SIZE: + base.AlphaSize = value; + break; + + case __DRI_ATTRIB_ACCUM_RED_SIZE: + case __DRI_ATTRIB_ACCUM_GREEN_SIZE: + case __DRI_ATTRIB_ACCUM_BLUE_SIZE: + case __DRI_ATTRIB_ACCUM_ALPHA_SIZE: + /* Don't expose visuals with the accumulation buffer. */ + if (value > 0) + return NULL; + break; + + case __DRI_ATTRIB_FRAMEBUFFER_SRGB_CAPABLE: + srgb = value != 0; + if (!disp->Extensions.KHR_gl_colorspace && srgb) + return NULL; + break; + + case __DRI_ATTRIB_MAX_PBUFFER_WIDTH: + base.MaxPbufferWidth = _EGL_MAX_PBUFFER_WIDTH; + break; + case __DRI_ATTRIB_MAX_PBUFFER_HEIGHT: + base.MaxPbufferHeight = _EGL_MAX_PBUFFER_HEIGHT; + break; + case __DRI_ATTRIB_MUTABLE_RENDER_BUFFER: + if (disp->Extensions.KHR_mutable_render_buffer) + surface_type |= EGL_MUTABLE_RENDER_BUFFER_BIT_KHR; + break; + default: + key = dri2_to_egl_attribute_map[attrib]; + if (key != 0) + _eglSetConfigKey(&base, key, value); + break; + } + } + + if (attr_list) + for (int i = 0; attr_list[i] != EGL_NONE; i += 2) + _eglSetConfigKey(&base, attr_list[i], attr_list[i + 1]); + + base.NativeRenderable = EGL_TRUE; + + base.SurfaceType = surface_type; + if (surface_type & + (EGL_PBUFFER_BIT | + (disp->Extensions.NOK_texture_from_pixmap ? EGL_PIXMAP_BIT : 0))) { + base.BindToTextureRGB = bind_to_texture_rgb; + if (base.AlphaSize > 0) + base.BindToTextureRGBA = bind_to_texture_rgba; + } + + if (double_buffer) { + surface_type &= ~EGL_PIXMAP_BIT; + } else { + surface_type &= ~EGL_WINDOW_BIT; + } + + if (!surface_type) + return NULL; + + base.RenderableType = disp->ClientAPIs; + base.Conformant = disp->ClientAPIs; + + base.MinSwapInterval = dri2_dpy->min_swap_interval; + base.MaxSwapInterval = dri2_dpy->max_swap_interval; + + if (!_eglValidateConfig(&base, EGL_FALSE)) { + _eglLog(_EGL_DEBUG, "DRI2: failed to validate config %d", base.ConfigID); + return NULL; + } + + config_id = base.ConfigID; + base.ConfigID = EGL_DONT_CARE; + base.SurfaceType = EGL_DONT_CARE; + num_configs = _eglFilterArray(disp->Configs, (void **)&matching_config, 1, + (_EGLArrayForEach)dri2_match_config, &base); + + if (num_configs == 1) { + conf = (struct dri2_egl_config *)matching_config; + + if (!conf->dri_config[double_buffer][srgb]) + conf->dri_config[double_buffer][srgb] = dri_config; + else + /* a similar config type is already added (unlikely) => discard */ + return NULL; + } else if (num_configs == 0) { + conf = calloc(1, sizeof *conf); + if (conf == NULL) + return NULL; + + conf->dri_config[double_buffer][srgb] = dri_config; + + memcpy(&conf->base, &base, sizeof base); + conf->base.SurfaceType = 0; + conf->base.ConfigID = config_id; + + _eglLinkConfig(&conf->base); + } else { + UNREACHABLE("duplicates should not be possible"); + return NULL; + } + + conf->base.SurfaceType |= surface_type; + + return conf; +} + +static int +dri2_pbuffer_visual_index(enum pipe_format format) +{ + for (unsigned i = 0; i < ARRAY_SIZE(dri2_pbuffer_visuals); i++) { + if (dri2_pbuffer_visuals[i] == format) + return i; + } + + return -1; +} + +void +dri2_add_pbuffer_configs_for_visuals(_EGLDisplay *disp) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + unsigned int format_count[ARRAY_SIZE(dri2_pbuffer_visuals)] = {0}; + + for (unsigned i = 0; dri2_dpy->driver_configs[i] != NULL; i++) { + struct dri2_egl_config *dri2_conf; + EGLint config_group = 0; + struct gl_config *gl_config = + (struct gl_config *) dri2_dpy->driver_configs[i]; + int idx = dri2_pbuffer_visual_index(gl_config->color_format); + + if (idx == -1) + continue; + + /* Put the 16 bpc rgb[a] unorm formats into a lower priority EGL config + * group 1, so they don't get preferably chosen by eglChooseConfig(). + */ + if (util_format_is_unorm16(util_format_description(gl_config->color_format))) + config_group = 1; + + const EGLint attr_list[] = { + EGL_CONFIG_SELECT_GROUP_EXT, + config_group, + EGL_NONE, + }; + + dri2_conf = dri2_add_config(disp, dri2_dpy->driver_configs[i], + EGL_PBUFFER_BIT, attr_list); + if (dri2_conf) + format_count[idx]++; + } + + for (unsigned i = 0; i < ARRAY_SIZE(format_count); i++) { + if (!format_count[i]) { + _eglLog(_EGL_DEBUG, "No DRI config supports native format %s", + util_format_name(dri2_pbuffer_visuals[i])); + } + } +} + +GLboolean +dri2_validate_egl_image(void *image, void *data) +{ + _EGLDisplay *disp = _eglLockDisplay(data); + _EGLImage *img = _eglLookupImage(image, disp); + _eglUnlockDisplay(disp); + + if (img == NULL) { + _eglError(EGL_BAD_PARAMETER, "dri2_validate_egl_image"); + return false; + } + + return true; +} + +struct dri_image * +dri2_lookup_egl_image_validated(void *image, void *data) +{ + struct dri2_egl_image *dri2_img; + + (void)data; + + dri2_img = dri2_egl_image(image); + + return dri2_img->dri_image; +} + +const __DRIimageLookupExtension image_lookup_extension = { + .base = {__DRI_IMAGE_LOOKUP, 2}, + + .validateEGLImage = dri2_validate_egl_image, + .lookupEGLImageValidated = dri2_lookup_egl_image_validated, +}; + +void +dri2_detect_swrast_kopper(_EGLDisplay *disp) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + + /* Kopper won't work on Android without extra platform level support. */ + dri2_dpy->kopper = dri2_dpy->driver_name && !strcmp(dri2_dpy->driver_name, "zink") && + !debug_get_bool_option("LIBGL_KOPPER_DISABLE", false) && disp->Platform != _EGL_PLATFORM_ANDROID; + dri2_dpy->swrast = (disp->Options.ForceSoftware && !dri2_dpy->kopper && strcmp(dri2_dpy->driver_name, "vmwgfx")) || + !dri2_dpy->driver_name || strstr(dri2_dpy->driver_name, "swrast"); + dri2_dpy->swrast_not_kms = dri2_dpy->swrast && (!dri2_dpy->driver_name || strcmp(dri2_dpy->driver_name, "kms_swrast")); +} + +static const char * +dri2_query_driver_name(_EGLDisplay *disp) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + return dri2_dpy->driver_name; +} + +static char * +dri2_query_driver_config(_EGLDisplay *disp) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + char *ret; + + ret = driGetDriInfoXML(dri2_dpy->driver_name); + + mtx_unlock(&dri2_dpy->lock); + + return ret; +} + +static bool +dri2_query_device_info(const void* driver_device_identifier, + struct egl_device_info *device_info) +{ + const char* drm_device_name = (const char*)driver_device_identifier; + + /* We have information cached already. */ + if (device_info->vendor_name) + return true; + + return dri_get_drm_device_info( + drm_device_name, device_info->device_uuid, device_info->driver_uuid, + &device_info->vendor_name, &device_info->renderer_name, &device_info->driver_name); +} + +void +dri2_setup_screen(_EGLDisplay *disp) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri_screen *screen = dri2_dpy->dri_screen_render_gpu; + struct pipe_screen *pscreen = screen->base.screen; + unsigned int api_mask = screen->api_mask; + +#ifdef HAVE_LIBDRM + unsigned caps = pscreen->caps.dmabuf; + dri2_dpy->has_dmabuf_import = (caps & DRM_PRIME_CAP_IMPORT) > 0; + dri2_dpy->has_dmabuf_export = (caps & DRM_PRIME_CAP_EXPORT) > 0; +#endif +#ifdef HAVE_ANDROID_PLATFORM + dri2_dpy->has_native_fence_fd = pscreen->caps.native_fence_fd; +#endif + dri2_dpy->has_compression_modifiers = pscreen->query_compression_rates && + (pscreen->query_compression_modifiers || dri2_dpy->kopper); + + /* + * EGL 1.5 specification defines the default value to 1. Moreover, + * eglSwapInterval() is required to clamp requested value to the supported + * range. Since the default value is implicitly assumed to be supported, + * use it as both minimum and maximum for the platforms that do not allow + * changing the interval. Platforms, which allow it (e.g. x11, wayland) + * override these values already. + */ + dri2_dpy->min_swap_interval = 1; + dri2_dpy->max_swap_interval = 1; + dri2_dpy->default_swap_interval = 1; + + disp->ClientAPIs = 0; + if ((api_mask & (1 << __DRI_API_OPENGL)) && _eglIsApiValid(EGL_OPENGL_API)) + disp->ClientAPIs |= EGL_OPENGL_BIT; + if ((api_mask & (1 << __DRI_API_GLES)) && _eglIsApiValid(EGL_OPENGL_ES_API)) + disp->ClientAPIs |= EGL_OPENGL_ES_BIT; + if ((api_mask & (1 << __DRI_API_GLES2)) && _eglIsApiValid(EGL_OPENGL_ES_API)) + disp->ClientAPIs |= EGL_OPENGL_ES2_BIT; + if ((api_mask & (1 << __DRI_API_GLES3)) && _eglIsApiValid(EGL_OPENGL_ES_API)) + disp->ClientAPIs |= EGL_OPENGL_ES3_BIT_KHR; + + disp->Extensions.KHR_create_context = EGL_TRUE; + disp->Extensions.KHR_create_context_no_error = EGL_TRUE; + disp->Extensions.KHR_no_config_context = EGL_TRUE; + disp->Extensions.KHR_surfaceless_context = EGL_TRUE; + + disp->Extensions.MESA_gl_interop = EGL_TRUE; + + disp->Extensions.MESA_query_driver = EGL_TRUE; + + /* Report back to EGL the bitmask of priorities supported */ + disp->Extensions.IMG_context_priority = pscreen->caps.context_priority_mask; + + /** + * FIXME: Some drivers currently misreport what context priorities the user + * can use and fail context creation. This cause issues on Android where the + * display process would try to use realtime priority. This is also a spec + * violation for IMG_context_priority. + */ +#ifndef HAVE_ANDROID_PLATFORM + disp->Extensions.NV_context_priority_realtime = + disp->Extensions.IMG_context_priority & + (1 << __EGL_CONTEXT_PRIORITY_REALTIME_BIT); +#endif + + disp->Extensions.EXT_pixel_format_float = EGL_TRUE; + + if (pscreen->is_format_supported(pscreen, PIPE_FORMAT_B8G8R8A8_SRGB, + PIPE_TEXTURE_2D, 0, 0, + PIPE_BIND_RENDER_TARGET)) { + disp->Extensions.KHR_gl_colorspace = EGL_TRUE; + } + + disp->Extensions.EXT_config_select_group = EGL_TRUE; + + disp->Extensions.EXT_create_context_robustness = + pscreen->caps.device_reset_status_query; + disp->RobustBufferAccess = pscreen->caps.robust_buffer_access_behavior; + + /* EXT_query_reset_notification_strategy complements and requires + * EXT_create_context_robustness. */ + disp->Extensions.EXT_query_reset_notification_strategy = + disp->Extensions.EXT_create_context_robustness; + + disp->Extensions.KHR_fence_sync = EGL_TRUE; + disp->Extensions.KHR_wait_sync = EGL_TRUE; + disp->Extensions.KHR_cl_event2 = EGL_TRUE; + if (dri_fence_get_caps(dri2_dpy->dri_screen_render_gpu) + & __DRI_FENCE_CAP_NATIVE_FD) + disp->Extensions.ANDROID_native_fence_sync = EGL_TRUE; + + if (dri_get_pipe_screen(dri2_dpy->dri_screen_render_gpu)->get_disk_shader_cache) + disp->Extensions.ANDROID_blob_cache = EGL_TRUE; + + disp->Extensions.KHR_reusable_sync = EGL_TRUE; + +#ifdef HAVE_LIBDRM + if (pscreen->caps.dmabuf & DRM_PRIME_CAP_EXPORT) + disp->Extensions.MESA_image_dma_buf_export = true; + + if (dri2_dpy->has_dmabuf_import) { + disp->Extensions.EXT_image_dma_buf_import = EGL_TRUE; + disp->Extensions.EXT_image_dma_buf_import_modifiers = EGL_TRUE; + } +#endif + disp->Extensions.MESA_x11_native_visual_id = EGL_TRUE; + disp->Extensions.EXT_surface_compression = EGL_TRUE; + disp->Extensions.KHR_image_base = EGL_TRUE; + disp->Extensions.KHR_gl_renderbuffer_image = EGL_TRUE; + disp->Extensions.KHR_gl_texture_2D_image = EGL_TRUE; + disp->Extensions.KHR_gl_texture_cubemap_image = EGL_TRUE; + + if (pscreen->caps.max_texture_3d_levels != 0) + disp->Extensions.KHR_gl_texture_3D_image = EGL_TRUE; + + disp->Extensions.KHR_context_flush_control = EGL_TRUE; + + if (dri_get_pipe_screen(dri2_dpy->dri_screen_render_gpu)->set_damage_region) + disp->Extensions.KHR_partial_update = EGL_TRUE; + + disp->Extensions.EXT_protected_surface = pscreen->caps.device_protected_surface; + disp->Extensions.EXT_protected_content = pscreen->caps.device_protected_context; +} + +void +dri2_setup_swap_interval(_EGLDisplay *disp, int max_swap_interval) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + GLint vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1; + + /* Allow driconf to override applications.*/ + dri2GalliumConfigQueryi(dri2_dpy->dri_screen_render_gpu, "vblank_mode", &vblank_mode); + + switch (vblank_mode) { + case DRI_CONF_VBLANK_NEVER: + dri2_dpy->min_swap_interval = 0; + dri2_dpy->max_swap_interval = 0; + dri2_dpy->default_swap_interval = 0; + break; + case DRI_CONF_VBLANK_ALWAYS_SYNC: + dri2_dpy->min_swap_interval = 1; + dri2_dpy->max_swap_interval = max_swap_interval; + dri2_dpy->default_swap_interval = 1; + break; + case DRI_CONF_VBLANK_DEF_INTERVAL_0: + dri2_dpy->min_swap_interval = 0; + dri2_dpy->max_swap_interval = max_swap_interval; + dri2_dpy->default_swap_interval = 0; + break; + default: + case DRI_CONF_VBLANK_DEF_INTERVAL_1: + dri2_dpy->min_swap_interval = 0; + dri2_dpy->max_swap_interval = max_swap_interval; + dri2_dpy->default_swap_interval = 1; + break; + } +} + +/* All platforms but DRM call this function to create the screen and populate + * the driver_configs. DRM inherits that information from its display - GBM. + */ +EGLBoolean +dri2_create_screen(_EGLDisplay *disp) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + char *driver_name_display_gpu; + enum dri_screen_type type = DRI_SCREEN_DRI3; + + if (dri2_dpy->kopper) + type = DRI_SCREEN_KOPPER; + else if (dri2_dpy->swrast_not_kms) + type = DRI_SCREEN_SWRAST; + else if (dri2_dpy->swrast) + type = DRI_SCREEN_KMS_SWRAST; + + if (dri2_dpy->fd_render_gpu != dri2_dpy->fd_display_gpu) { + driver_name_display_gpu = + loader_get_driver_for_fd(dri2_dpy->fd_display_gpu); + if (driver_name_display_gpu) { + /* check if driver name is matching so that non mesa drivers + * will not crash. + */ + if (strcmp(dri2_dpy->driver_name, driver_name_display_gpu) == 0) { + dri2_dpy->dri_screen_display_gpu = driCreateNewScreen3( + 0, dri2_dpy->fd_display_gpu, dri2_dpy->loader_extensions, + type, &dri2_dpy->driver_configs, false, dri2_dpy->multibuffers_available, disp); + } + free(driver_name_display_gpu); + } + } + + int screen_fd = dri2_dpy->swrast_not_kms ? -1 : dri2_dpy->fd_render_gpu; + dri2_dpy->dri_screen_render_gpu = driCreateNewScreen3( + 0, screen_fd, dri2_dpy->loader_extensions, type, + &dri2_dpy->driver_configs, false, dri2_dpy->multibuffers_available, disp); + + if (dri2_dpy->dri_screen_render_gpu == NULL) { + _eglLog(_EGL_WARNING, "egl: failed to create dri2 screen"); + return EGL_FALSE; + } + + if (dri2_dpy->fd_render_gpu == dri2_dpy->fd_display_gpu) + dri2_dpy->dri_screen_display_gpu = dri2_dpy->dri_screen_render_gpu; + + dri2_dpy->own_dri_screen = true; + return EGL_TRUE; +} + +EGLBoolean +dri2_setup_device(_EGLDisplay *disp, EGLBoolean software) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + _EGLDevice *dev; + int render_fd; + + /* If we're not software, we need a DRM node FD */ + assert(software || dri2_dpy->fd_render_gpu >= 0); + + /* fd_render_gpu is what we got from WSI, so might actually be a lie and + * not a render node... */ + if (software) { + render_fd = -1; + } else if (loader_is_device_render_capable(dri2_dpy->fd_render_gpu)) { + render_fd = dri2_dpy->fd_render_gpu; + } else { + render_fd = dri_query_compatible_render_only_device_fd( + dri2_dpy->fd_render_gpu); + if (render_fd < 0) + return EGL_FALSE; + } + + dev = _eglFindDevice(render_fd, software); + + if (render_fd >= 0 && render_fd != dri2_dpy->fd_render_gpu) + close(render_fd); + + if (!dev) + return EGL_FALSE; + + disp->Device = dev; + return EGL_TRUE; +} + +/** + * Called via eglInitialize(), drv->Initialize(). + * + * This must be guaranteed to be called exactly once, even if eglInitialize is + * called many times (without a eglTerminate in between). + */ +static EGLBoolean +dri2_initialize(_EGLDisplay *disp) +{ + EGLBoolean ret = EGL_FALSE; + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + + /* In the case where the application calls eglMakeCurrent(context1), + * eglTerminate, then eglInitialize again (without a call to eglReleaseThread + * or eglMakeCurrent(NULL) before that), dri2_dpy structure is still + * initialized, as we need it to be able to free context1 correctly. + * + * It would probably be safest to forcibly release the display with + * dri2_display_release, to make sure the display is reinitialized correctly. + * However, the EGL spec states that we need to keep a reference to the + * current context (so we cannot call dri2_make_current(NULL)), and therefore + * we would leak context1 as we would be missing the old display connection + * to free it up correctly. + */ + if (dri2_dpy) { + p_atomic_inc(&dri2_dpy->ref_count); + return EGL_TRUE; + } + dri2_dpy = dri2_display_create(disp); + if (!dri2_dpy) + return EGL_FALSE; + + loader_set_logger(_eglLog); + + switch (disp->Platform) { + case _EGL_PLATFORM_SURFACELESS: + ret = dri2_initialize_surfaceless(disp); + break; + case _EGL_PLATFORM_DEVICE: + ret = dri2_initialize_device(disp); + break; + case _EGL_PLATFORM_X11: + case _EGL_PLATFORM_XCB: + ret = dri2_initialize_x11(disp); + break; + case _EGL_PLATFORM_DRM: + ret = dri2_initialize_drm(disp); + break; + case _EGL_PLATFORM_WAYLAND: + ret = dri2_initialize_wayland(disp); + break; + case _EGL_PLATFORM_ANDROID: + ret = dri2_initialize_android(disp); + break; +#ifdef HAVE_REDOX_PLATFORM + case _EGL_PLATFORM_REDOX: + ret = dri2_initialize_redox(disp); + break; +#endif + default: + UNREACHABLE("Callers ensure we cannot get here."); + return EGL_FALSE; + } + + if (!ret) { + dri2_display_destroy(disp); + return EGL_FALSE; + } + + if (_eglGetArraySize(disp->Configs) == 0) { + _eglError(EGL_NOT_INITIALIZED, "failed to add any EGLConfigs"); + dri2_display_destroy(disp); + return EGL_FALSE; + } + + dri2_dpy = dri2_egl_display(disp); + p_atomic_inc(&dri2_dpy->ref_count); + + mtx_init(&dri2_dpy->lock, mtx_plain); + + return EGL_TRUE; +} + +/** + * Decrement display reference count, and free up display if necessary. + */ +static void +dri2_display_release(_EGLDisplay *disp) +{ + struct dri2_egl_display *dri2_dpy; + + if (!disp) + return; + + dri2_dpy = dri2_egl_display(disp); + + assert(dri2_dpy->ref_count > 0); + + if (!p_atomic_dec_zero(&dri2_dpy->ref_count)) + return; + + _eglCleanupDisplay(disp); + dri2_display_destroy(disp); +} + +void +dri2_display_destroy(_EGLDisplay *disp) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + + if (dri2_dpy->own_dri_screen) { + if (dri2_dpy->vtbl && dri2_dpy->vtbl->close_screen_notify) + dri2_dpy->vtbl->close_screen_notify(disp); + + driDestroyScreen(dri2_dpy->dri_screen_render_gpu); + + if (dri2_dpy->dri_screen_display_gpu && + dri2_dpy->fd_render_gpu != dri2_dpy->fd_display_gpu) + driDestroyScreen(dri2_dpy->dri_screen_display_gpu); + } + if (dri2_dpy->fd_display_gpu >= 0 && + dri2_dpy->fd_render_gpu != dri2_dpy->fd_display_gpu) + close(dri2_dpy->fd_display_gpu); + if (dri2_dpy->fd_render_gpu >= 0) + close(dri2_dpy->fd_render_gpu); + + free(dri2_dpy->driver_name); + +#ifdef HAVE_WAYLAND_PLATFORM + free(dri2_dpy->device_name); +#endif + + switch (disp->Platform) { + case _EGL_PLATFORM_X11: + case _EGL_PLATFORM_XCB: + dri2_teardown_x11(dri2_dpy); + break; + case _EGL_PLATFORM_DRM: + dri2_teardown_drm(dri2_dpy); + break; + case _EGL_PLATFORM_WAYLAND: + dri2_teardown_wayland(dri2_dpy); + break; + case _EGL_PLATFORM_ANDROID: +#ifdef HAVE_ANDROID_PLATFORM + u_gralloc_destroy(&dri2_dpy->gralloc); +#endif + break; + case _EGL_PLATFORM_SURFACELESS: + break; + case _EGL_PLATFORM_DEVICE: + break; + default: + UNREACHABLE("Platform teardown is not properly hooked."); + break; + } + + /* The drm platform does not create the screen/driver_configs but reuses + * the ones from the gbm device. As such the gbm itself is responsible + * for the cleanup. + */ + if (disp->Platform != _EGL_PLATFORM_DRM && dri2_dpy->driver_configs) { + for (unsigned i = 0; dri2_dpy->driver_configs[i]; i++) + free((struct dri_config *)dri2_dpy->driver_configs[i]); + free(dri2_dpy->driver_configs); + } + free(dri2_dpy); + disp->DriverData = NULL; +} + +struct dri2_egl_display * +dri2_display_create(_EGLDisplay *disp) +{ + struct dri2_egl_display *dri2_dpy = calloc(1, sizeof *dri2_dpy); + if (!dri2_dpy) { + _eglError(EGL_BAD_ALLOC, "eglInitialize"); + return NULL; + } + + dri2_dpy->fd_render_gpu = -1; + dri2_dpy->fd_display_gpu = -1; + dri2_dpy->multibuffers_available = true; + disp->DriverData = (void *)dri2_dpy; + + return dri2_dpy; +} + +/** + * Called via eglTerminate(), drv->Terminate(). + * + * This must be guaranteed to be called exactly once, even if eglTerminate is + * called many times (without a eglInitialize in between). + */ +static EGLBoolean +dri2_terminate(_EGLDisplay *disp) +{ + /* Release all non-current Context/Surfaces. */ + _eglReleaseDisplayResources(disp); + + dri2_display_release(disp); + + return EGL_TRUE; +} + +/** + * Set the error code after a call to + * dri2_egl_display::dri2::createContextAttribs. + */ +static void +dri2_create_context_attribs_error(int dri_error) +{ + EGLint egl_error; + + switch (dri_error) { + case __DRI_CTX_ERROR_SUCCESS: + return; + + case __DRI_CTX_ERROR_NO_MEMORY: + egl_error = EGL_BAD_ALLOC; + break; + + /* From the EGL_KHR_create_context spec, section "Errors": + * + * * If does not support a client API context compatible + * with the requested API major and minor version, [...] context + * flags, and context reset notification behavior (for client API types + * where these attributes are supported), then an EGL_BAD_MATCH error is + * generated. + * + * * If an OpenGL ES context is requested and the values for + * attributes EGL_CONTEXT_MAJOR_VERSION_KHR and + * EGL_CONTEXT_MINOR_VERSION_KHR specify an OpenGL ES version that + * is not defined, than an EGL_BAD_MATCH error is generated. + * + * * If an OpenGL context is requested, the requested version is + * greater than 3.2, and the value for attribute + * EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR has no bits set; has any + * bits set other than EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR and + * EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR; has more than + * one of these bits set; or if the implementation does not support + * the requested profile, then an EGL_BAD_MATCH error is generated. + */ + case __DRI_CTX_ERROR_BAD_API: + case __DRI_CTX_ERROR_BAD_VERSION: + case __DRI_CTX_ERROR_BAD_FLAG: + egl_error = EGL_BAD_MATCH; + break; + + /* From the EGL_KHR_create_context spec, section "Errors": + * + * * If an attribute name or attribute value in is not + * recognized (including unrecognized bits in bitmask attributes), + * then an EGL_BAD_ATTRIBUTE error is generated." + */ + case __DRI_CTX_ERROR_UNKNOWN_ATTRIBUTE: + case __DRI_CTX_ERROR_UNKNOWN_FLAG: + egl_error = EGL_BAD_ATTRIBUTE; + break; + + default: + assert(!"unknown dri_error code"); + egl_error = EGL_BAD_MATCH; + break; + } + + _eglError(egl_error, "dri2_create_context"); +} + +static bool +dri2_fill_context_attribs(struct dri2_egl_context *dri2_ctx, + struct dri2_egl_display *dri2_dpy, + uint32_t *ctx_attribs, unsigned *num_attribs) +{ + int pos = 0; + + assert(*num_attribs >= NUM_ATTRIBS); + + ctx_attribs[pos++] = __DRI_CTX_ATTRIB_MAJOR_VERSION; + ctx_attribs[pos++] = dri2_ctx->base.ClientMajorVersion; + ctx_attribs[pos++] = __DRI_CTX_ATTRIB_MINOR_VERSION; + ctx_attribs[pos++] = dri2_ctx->base.ClientMinorVersion; + + if (dri2_ctx->base.Flags != 0) { + ctx_attribs[pos++] = __DRI_CTX_ATTRIB_FLAGS; + ctx_attribs[pos++] = dri2_ctx->base.Flags; + } + + if (dri2_ctx->base.ResetNotificationStrategy != + EGL_NO_RESET_NOTIFICATION_KHR) { + ctx_attribs[pos++] = __DRI_CTX_ATTRIB_RESET_STRATEGY; + ctx_attribs[pos++] = __DRI_CTX_RESET_LOSE_CONTEXT; + } + + if (dri2_ctx->base.ContextPriority != EGL_CONTEXT_PRIORITY_MEDIUM_IMG) { + unsigned val; + + switch (dri2_ctx->base.ContextPriority) { + case EGL_CONTEXT_PRIORITY_REALTIME_NV: + val = __DRI_CTX_PRIORITY_REALTIME; + break; + case EGL_CONTEXT_PRIORITY_HIGH_IMG: + val = __DRI_CTX_PRIORITY_HIGH; + break; + case EGL_CONTEXT_PRIORITY_MEDIUM_IMG: + val = __DRI_CTX_PRIORITY_MEDIUM; + break; + case EGL_CONTEXT_PRIORITY_LOW_IMG: + val = __DRI_CTX_PRIORITY_LOW; + break; + default: + _eglError(EGL_BAD_CONFIG, "eglCreateContext"); + return false; + } + + ctx_attribs[pos++] = __DRI_CTX_ATTRIB_PRIORITY; + ctx_attribs[pos++] = val; + } + + if (dri2_ctx->base.ReleaseBehavior == + EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR) { + ctx_attribs[pos++] = __DRI_CTX_ATTRIB_RELEASE_BEHAVIOR; + ctx_attribs[pos++] = __DRI_CTX_RELEASE_BEHAVIOR_NONE; + } + + if (dri2_ctx->base.NoError) { + ctx_attribs[pos++] = __DRI_CTX_ATTRIB_NO_ERROR; + ctx_attribs[pos++] = true; + } + + if (dri2_ctx->base.Protected) { + ctx_attribs[pos++] = __DRI_CTX_ATTRIB_PROTECTED; + ctx_attribs[pos++] = true; + } + + *num_attribs = pos; + + return true; +} + +/** + * Called via eglCreateContext(), drv->CreateContext(). + */ +static _EGLContext * +dri2_create_context(_EGLDisplay *disp, _EGLConfig *conf, + _EGLContext *share_list, const EGLint *attrib_list) +{ + struct dri2_egl_context *dri2_ctx; + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + struct dri2_egl_context *dri2_ctx_shared = dri2_egl_context(share_list); + struct dri_context *shared = dri2_ctx_shared ? dri2_ctx_shared->dri_context : NULL; + struct dri2_egl_config *dri2_config = dri2_egl_config(conf); + const struct dri_config *dri_config; + int api; + unsigned error; + unsigned num_attribs = NUM_ATTRIBS; + uint32_t ctx_attribs[NUM_ATTRIBS]; + + dri2_ctx = malloc(sizeof *dri2_ctx); + if (!dri2_ctx) { + dri2_egl_error_unlock(dri2_dpy, EGL_BAD_ALLOC, "eglCreateContext"); + return NULL; + } + + if (!_eglInitContext(&dri2_ctx->base, disp, conf, share_list, attrib_list)) + goto cleanup; + + switch (dri2_ctx->base.ClientAPI) { + case EGL_OPENGL_ES_API: + switch (dri2_ctx->base.ClientMajorVersion) { + case 1: + api = __DRI_API_GLES; + break; + case 2: + api = __DRI_API_GLES2; + break; + case 3: + api = __DRI_API_GLES3; + break; + default: + _eglError(EGL_BAD_PARAMETER, "eglCreateContext"); + goto cleanup; + } + break; + case EGL_OPENGL_API: + if ((dri2_ctx->base.ClientMajorVersion >= 4 || + (dri2_ctx->base.ClientMajorVersion == 3 && + dri2_ctx->base.ClientMinorVersion >= 2)) && + dri2_ctx->base.Profile == EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR) + api = __DRI_API_OPENGL_CORE; + else if (dri2_ctx->base.ClientMajorVersion == 3 && + dri2_ctx->base.ClientMinorVersion == 1) + api = __DRI_API_OPENGL_CORE; + else + api = __DRI_API_OPENGL; + break; + default: + _eglError(EGL_BAD_PARAMETER, "eglCreateContext"); + goto cleanup; + } + + if (conf != NULL) { + /* The config chosen here isn't necessarily + * used for surfaces later. + * A pixmap surface will use the single config. + * This opportunity depends on disabling the + * doubleBufferMode check in + * src/mesa/main/context.c:check_compatible() + */ + if (dri2_config->dri_config[1][0]) + dri_config = dri2_config->dri_config[1][0]; + else + dri_config = dri2_config->dri_config[0][0]; + } else + dri_config = NULL; + + if (!dri2_fill_context_attribs(dri2_ctx, dri2_dpy, ctx_attribs, + &num_attribs)) + goto cleanup; + + bool thread_safe = true; + +#ifdef HAVE_X11_PLATFORM + if (disp->Platform == _EGL_PLATFORM_X11) { + if (disp->PlatformDisplay != EGL_DEFAULT_DISPLAY) { + thread_safe = x11_xlib_display_is_thread_safe(disp->PlatformDisplay); + } else { + Display *display = XOpenDisplay(NULL); + if (display) { + thread_safe = x11_xlib_display_is_thread_safe(display); + XCloseDisplay(display); + } + } + } +#endif + + dri2_ctx->dri_context = driCreateContextAttribs( + dri2_dpy->dri_screen_render_gpu, api, dri_config, shared, num_attribs / 2, + ctx_attribs, &error, dri2_ctx, thread_safe); + dri2_create_context_attribs_error(error); + + if (!dri2_ctx->dri_context) + goto cleanup; + + mtx_unlock(&dri2_dpy->lock); + + return &dri2_ctx->base; + +cleanup: + mtx_unlock(&dri2_dpy->lock); + free(dri2_ctx); + return NULL; +} + +/** + * Called via eglDestroyContext(), drv->DestroyContext(). + */ +static EGLBoolean +dri2_destroy_context(_EGLDisplay *disp, _EGLContext *ctx) +{ + struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); + + if (_eglPutContext(ctx)) { + driDestroyContext(dri2_ctx->dri_context); + free(dri2_ctx); + } + + return EGL_TRUE; +} + +EGLBoolean +dri2_init_surface(_EGLSurface *surf, _EGLDisplay *disp, EGLint type, + _EGLConfig *conf, const EGLint *attrib_list, + EGLBoolean enable_out_fence, void *native_surface) +{ + struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); + + dri2_surf->out_fence_fd = -1; + dri2_surf->enable_out_fence = false; + if (disp->Extensions.ANDROID_native_fence_sync) { + dri2_surf->enable_out_fence = enable_out_fence; + } + + return _eglInitSurface(surf, disp, type, conf, attrib_list, native_surface); +} + +static void +dri2_surface_set_out_fence_fd(_EGLSurface *surf, int fence_fd) +{ + struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); + + if (dri2_surf->out_fence_fd >= 0) + close(dri2_surf->out_fence_fd); + + dri2_surf->out_fence_fd = fence_fd; +} + +void +dri2_fini_surface(_EGLSurface *surf) +{ + struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); + + dri2_surface_set_out_fence_fd(surf, -1); + dri2_surf->enable_out_fence = false; +} + +static EGLBoolean +dri2_destroy_surface(_EGLDisplay *disp, _EGLSurface *surf) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + EGLBoolean ret = EGL_TRUE; + + if (_eglPutSurface(surf)) + ret = dri2_dpy->vtbl->destroy_surface(disp, surf); + + return ret; +} + +static void +dri2_surf_update_fence_fd(_EGLContext *ctx, _EGLDisplay *disp, + _EGLSurface *surf) +{ + struct dri_context *dri_ctx = dri2_egl_context(ctx)->dri_context; + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); + int fence_fd = -1; + void *fence; + + if (!dri2_surf->enable_out_fence) + return; + + fence = dri_create_fence_fd(dri_ctx, -1); + if (fence) { + fence_fd = dri_get_fence_fd(dri2_dpy->dri_screen_render_gpu, fence); + dri_destroy_fence(dri2_dpy->dri_screen_render_gpu, fence); + } + dri2_surface_set_out_fence_fd(surf, fence_fd); +} + +EGLBoolean +dri2_create_drawable(struct dri2_egl_display *dri2_dpy, + const struct dri_config *config, + struct dri2_egl_surface *dri2_surf, void *loaderPrivate) +{ + bool is_pixmap = dri2_surf->base.Type == EGL_PBUFFER_BIT || + dri2_surf->base.Type == EGL_PIXMAP_BIT; + dri2_surf->dri_drawable = dri_create_drawable(dri2_dpy->dri_screen_render_gpu, config, is_pixmap, loaderPrivate); + if (dri2_surf->dri_drawable == NULL) + return _eglError(EGL_BAD_ALLOC, "createNewDrawable"); + + return EGL_TRUE; +} + +/** + * Called via eglMakeCurrent(), drv->MakeCurrent(). + */ +static EGLBoolean +dri2_make_current(_EGLDisplay *disp, _EGLSurface *dsurf, _EGLSurface *rsurf, + _EGLContext *ctx) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); + _EGLDisplay *old_disp = NULL; + struct dri2_egl_display *old_dri2_dpy = NULL; + _EGLContext *old_ctx; + _EGLSurface *old_dsurf, *old_rsurf; + _EGLSurface *tmp_dsurf, *tmp_rsurf; + struct dri_drawable *ddraw, *rdraw; + struct dri_context *cctx; + EGLint egl_error = EGL_SUCCESS; + + if (!dri2_dpy) + return _eglError(EGL_NOT_INITIALIZED, "eglMakeCurrent"); + + /* make new bindings, set the EGL error otherwise */ + if (!_eglBindContext(ctx, dsurf, rsurf, &old_ctx, &old_dsurf, &old_rsurf)) + return EGL_FALSE; + + if (old_ctx == ctx && old_dsurf == dsurf && old_rsurf == rsurf) { + _eglPutSurface(old_dsurf); + _eglPutSurface(old_rsurf); + _eglPutContext(old_ctx); + return EGL_TRUE; + } + + if (old_ctx) { + struct dri_context *old_cctx = dri2_egl_context(old_ctx)->dri_context; + old_disp = old_ctx->Resource.Display; + old_dri2_dpy = dri2_egl_display(old_disp); + + /* Disable shared buffer mode */ + if (old_dsurf && _eglSurfaceInSharedBufferMode(old_dsurf) && + old_dri2_dpy->vtbl->set_shared_buffer_mode) { + old_dri2_dpy->vtbl->set_shared_buffer_mode(old_disp, old_dsurf, false); + } + + driUnbindContext(old_cctx); + + if (old_dsurf) + dri2_surf_update_fence_fd(old_ctx, old_disp, old_dsurf); + } + + ddraw = (dsurf) ? dri2_dpy->vtbl->get_dri_drawable(dsurf) : NULL; + rdraw = (rsurf) ? dri2_dpy->vtbl->get_dri_drawable(rsurf) : NULL; + cctx = (dri2_ctx) ? dri2_ctx->dri_context : NULL; + + if (cctx) { + if (!driBindContext(cctx, ddraw, rdraw)) { + _EGLContext *tmp_ctx; + + /* driBindContext failed. We cannot tell for sure why, but + * setting the error to EGL_BAD_MATCH is surely better than leaving it + * as EGL_SUCCESS. + */ + egl_error = EGL_BAD_MATCH; + + /* undo the previous _eglBindContext */ + _eglBindContext(old_ctx, old_dsurf, old_rsurf, &ctx, &tmp_dsurf, + &tmp_rsurf); + assert(&dri2_ctx->base == ctx && tmp_dsurf == dsurf && + tmp_rsurf == rsurf); + + _eglPutSurface(dsurf); + _eglPutSurface(rsurf); + _eglPutContext(ctx); + + _eglPutSurface(old_dsurf); + _eglPutSurface(old_rsurf); + _eglPutContext(old_ctx); + + ddraw = + (old_dsurf) ? dri2_dpy->vtbl->get_dri_drawable(old_dsurf) : NULL; + rdraw = + (old_rsurf) ? dri2_dpy->vtbl->get_dri_drawable(old_rsurf) : NULL; + cctx = (old_ctx) ? dri2_egl_context(old_ctx)->dri_context : NULL; + + /* undo the previous driUnbindContext */ + if (driBindContext(cctx, ddraw, rdraw)) { + if (old_dsurf && _eglSurfaceInSharedBufferMode(old_dsurf) && + old_dri2_dpy->vtbl->set_shared_buffer_mode) { + old_dri2_dpy->vtbl->set_shared_buffer_mode(old_disp, old_dsurf, + true); + } + + return _eglError(egl_error, "eglMakeCurrent"); + } + + /* We cannot restore the same state as it was before calling + * eglMakeCurrent() and the spec isn't clear about what to do. We + * can prevent EGL from calling into the DRI driver with no DRI + * context bound. + */ + dsurf = rsurf = NULL; + ctx = NULL; + + _eglBindContext(ctx, dsurf, rsurf, &tmp_ctx, &tmp_dsurf, &tmp_rsurf); + assert(tmp_ctx == old_ctx && tmp_dsurf == old_dsurf && + tmp_rsurf == old_rsurf); + + _eglLog(_EGL_WARNING, "DRI2: failed to rebind the previous context"); + } else { + /* driBindContext succeeded, so take a reference on the + * dri2_dpy. This prevents dri2_dpy from being reinitialized when a + * EGLDisplay is terminated and then initialized again while a + * context is still bound. See dri2_initialize() for a more in depth + * explanation. */ + p_atomic_inc(&dri2_dpy->ref_count); + } + } + + dri2_destroy_surface(disp, old_dsurf); + dri2_destroy_surface(disp, old_rsurf); + + if (old_ctx) { + dri2_destroy_context(disp, old_ctx); + dri2_display_release(old_disp); + } + + if (egl_error != EGL_SUCCESS) + return _eglError(egl_error, "eglMakeCurrent"); + + if (dsurf && _eglSurfaceHasMutableRenderBuffer(dsurf) && + dri2_dpy->vtbl->set_shared_buffer_mode) { + /* Always update the shared buffer mode. This is obviously needed when + * the active EGL_RENDER_BUFFER is EGL_SINGLE_BUFFER. When + * EGL_RENDER_BUFFER is EGL_BACK_BUFFER, the update protects us in the + * case where external non-EGL API may have changed window's shared + * buffer mode since we last saw it. + */ + bool mode = (dsurf->ActiveRenderBuffer == EGL_SINGLE_BUFFER); + dri2_dpy->vtbl->set_shared_buffer_mode(disp, dsurf, mode); + } + + return EGL_TRUE; +} + +struct dri_drawable * +dri2_surface_get_dri_drawable(_EGLSurface *surf) +{ + struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); + + return dri2_surf->dri_drawable; +} + +static _EGLSurface * +dri2_create_window_surface(_EGLDisplay *disp, _EGLConfig *conf, + void *native_window, const EGLint *attrib_list) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + _EGLSurface *ret = dri2_dpy->vtbl->create_window_surface( + disp, conf, native_window, attrib_list); + mtx_unlock(&dri2_dpy->lock); + return ret; +} + +static _EGLSurface * +dri2_create_pixmap_surface(_EGLDisplay *disp, _EGLConfig *conf, + void *native_pixmap, const EGLint *attrib_list) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + _EGLSurface *ret = NULL; + + if (dri2_dpy->vtbl->create_pixmap_surface) + ret = dri2_dpy->vtbl->create_pixmap_surface(disp, conf, native_pixmap, + attrib_list); + + mtx_unlock(&dri2_dpy->lock); + + return ret; +} + +static _EGLSurface * +dri2_create_pbuffer_surface(_EGLDisplay *disp, _EGLConfig *conf, + const EGLint *attrib_list) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + _EGLSurface *ret = NULL; + + if (dri2_dpy->vtbl->create_pbuffer_surface) + ret = dri2_dpy->vtbl->create_pbuffer_surface(disp, conf, attrib_list); + + mtx_unlock(&dri2_dpy->lock); + + return ret; +} + +static EGLBoolean +dri2_swap_interval(_EGLDisplay *disp, _EGLSurface *surf, EGLint interval) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + EGLBoolean ret = EGL_TRUE; + + if (dri2_dpy->vtbl->swap_interval) + ret = dri2_dpy->vtbl->swap_interval(disp, surf, interval); + + mtx_unlock(&dri2_dpy->lock); + + return ret; +} + +/** + * Asks the client API to flush any rendering to the drawable so that we can + * do our swapbuffers. + */ +void +dri2_flush_drawable_for_swapbuffers_flags( + _EGLDisplay *disp, _EGLSurface *draw, + enum __DRI2throttleReason throttle_reason) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri_drawable *dri_drawable = dri2_dpy->vtbl->get_dri_drawable(draw); + + /* flush not available for swrast */ + if (dri2_dpy->swrast_not_kms) + return; + + /* We know there's a current context because: + * + * "If surface is not bound to the calling thread’s current + * context, an EGL_BAD_SURFACE error is generated." + */ + _EGLContext *ctx = _eglGetCurrentContext(); + struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); + + /* From the EGL 1.4 spec (page 52): + * + * "The contents of ancillary buffers are always undefined + * after calling eglSwapBuffers." + */ + dri_flush(dri2_ctx->dri_context, dri_drawable, + __DRI2_FLUSH_DRAWABLE | __DRI2_FLUSH_INVALIDATE_ANCILLARY, + throttle_reason); +} + +void +dri2_flush_drawable_for_swapbuffers(_EGLDisplay *disp, _EGLSurface *draw) +{ + dri2_flush_drawable_for_swapbuffers_flags(disp, draw, + __DRI2_THROTTLE_SWAPBUFFER); +} + +static EGLBoolean +dri2_swap_buffers(_EGLDisplay *disp, _EGLSurface *surf) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri_drawable *dri_drawable = dri2_dpy->vtbl->get_dri_drawable(surf); + _EGLContext *ctx = _eglGetCurrentContext(); + EGLBoolean ret; + + if (ctx && surf) + dri2_surf_update_fence_fd(ctx, disp, surf); + ret = dri2_dpy->vtbl->swap_buffers(disp, surf); + + /* SwapBuffers marks the end of the frame; reset the damage region for + * use again next time. + */ + if (ret && disp->Extensions.KHR_partial_update) + dri_set_damage_region(dri_drawable, 0, NULL); + + return ret; +} + +static EGLBoolean +dri2_swap_buffers_with_damage(_EGLDisplay *disp, _EGLSurface *surf, + const EGLint *rects, EGLint n_rects) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri_drawable *dri_drawable = dri2_dpy->vtbl->get_dri_drawable(surf); + _EGLContext *ctx = _eglGetCurrentContext(); + EGLBoolean ret; + + if (ctx && surf) + dri2_surf_update_fence_fd(ctx, disp, surf); + if (dri2_dpy->vtbl->swap_buffers_with_damage) + ret = + dri2_dpy->vtbl->swap_buffers_with_damage(disp, surf, rects, n_rects); + else + ret = dri2_dpy->vtbl->swap_buffers(disp, surf); + + /* SwapBuffers marks the end of the frame; reset the damage region for + * use again next time. + */ + if (ret && disp->Extensions.KHR_partial_update) + dri_set_damage_region(dri_drawable, 0, NULL); + + return ret; +} + +static EGLBoolean +dri2_set_damage_region(_EGLDisplay *disp, _EGLSurface *surf, EGLint *rects, + EGLint n_rects) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + struct dri_drawable *drawable = dri2_dpy->vtbl->get_dri_drawable(surf); + + if (!disp->Extensions.KHR_partial_update) { + mtx_unlock(&dri2_dpy->lock); + return EGL_FALSE; + } + + dri_set_damage_region(drawable, n_rects, rects); + mtx_unlock(&dri2_dpy->lock); + return EGL_TRUE; +} + +static EGLBoolean +dri2_copy_buffers(_EGLDisplay *disp, _EGLSurface *surf, + void *native_pixmap_target) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + if (!dri2_dpy->vtbl->copy_buffers) + return dri2_egl_error_unlock(dri2_dpy, EGL_BAD_NATIVE_PIXMAP, + "no support for native pixmaps"); + EGLBoolean ret = + dri2_dpy->vtbl->copy_buffers(disp, surf, native_pixmap_target); + mtx_unlock(&dri2_dpy->lock); + return ret; +} + +static EGLint +dri2_query_buffer_age(_EGLDisplay *disp, _EGLSurface *surf) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + if (!dri2_dpy->vtbl->query_buffer_age) + return 0; + return dri2_dpy->vtbl->query_buffer_age(disp, surf); +} + +static EGLBoolean +dri2_wait_client(_EGLDisplay *disp, _EGLContext *ctx) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + _EGLSurface *surf = ctx->DrawSurface; + struct dri_drawable *dri_drawable = dri2_dpy->vtbl->get_dri_drawable(surf); + + /* FIXME: If EGL allows frontbuffer rendering for window surfaces, + * we need to copy fake to real here.*/ + + if (!dri2_dpy->swrast_not_kms) + dri_flush_drawable(dri_drawable); + + return EGL_TRUE; +} + +static EGLBoolean +dri2_wait_native(EGLint engine) +{ + if (engine != EGL_CORE_NATIVE_ENGINE) + return _eglError(EGL_BAD_PARAMETER, "eglWaitNative"); + /* glXWaitX(); */ + + return EGL_TRUE; +} + +static EGLBoolean +dri2_bind_tex_image(_EGLDisplay *disp, _EGLSurface *surf, EGLint buffer) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + struct dri2_egl_context *dri2_ctx; + _EGLContext *ctx; + GLint format, target; + struct dri_drawable *dri_drawable = dri2_dpy->vtbl->get_dri_drawable(surf); + + ctx = _eglGetCurrentContext(); + dri2_ctx = dri2_egl_context(ctx); + + if (!_eglBindTexImage(disp, surf, buffer)) { + mtx_unlock(&dri2_dpy->lock); + return EGL_FALSE; + } + + switch (surf->TextureFormat) { + case EGL_TEXTURE_RGB: + format = __DRI_TEXTURE_FORMAT_RGB; + break; + case EGL_TEXTURE_RGBA: + format = __DRI_TEXTURE_FORMAT_RGBA; + break; + default: + assert(!"Unexpected texture format in dri2_bind_tex_image()"); + format = __DRI_TEXTURE_FORMAT_RGBA; + } + + switch (surf->TextureTarget) { + case EGL_TEXTURE_2D: + target = GL_TEXTURE_2D; + break; + default: + target = GL_TEXTURE_2D; + assert(!"Unexpected texture target in dri2_bind_tex_image()"); + } + + dri_set_tex_buffer2(dri2_ctx->dri_context, target, format, dri_drawable); + + mtx_unlock(&dri2_dpy->lock); + + return EGL_TRUE; +} + +static EGLBoolean +dri2_release_tex_image(_EGLDisplay *disp, _EGLSurface *surf, EGLint buffer) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + + if (!_eglReleaseTexImage(disp, surf, buffer)) { + mtx_unlock(&dri2_dpy->lock); + return EGL_FALSE; + } + + mtx_unlock(&dri2_dpy->lock); + + return EGL_TRUE; +} + +static _EGLImage * +dri2_create_image(_EGLDisplay *disp, _EGLContext *ctx, EGLenum target, + EGLClientBuffer buffer, const EGLint *attr_list) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + _EGLImage *ret = + dri2_dpy->vtbl->create_image(disp, ctx, target, buffer, attr_list); + mtx_unlock(&dri2_dpy->lock); + return ret; +} + +_EGLImage * +dri2_create_image_from_dri(_EGLDisplay *disp, struct dri_image *dri_image) +{ + struct dri2_egl_image *dri2_img; + + if (dri_image == NULL) { + _eglError(EGL_BAD_ALLOC, "dri2_create_image"); + return NULL; + } + + dri2_img = malloc(sizeof *dri2_img); + if (!dri2_img) { + _eglError(EGL_BAD_ALLOC, "dri2_create_image"); + return NULL; + } + + _eglInitImage(&dri2_img->base, disp); + + dri2_img->dri_image = dri_image; + + return &dri2_img->base; +} + +/** + * Translate a DRI Image extension error code into an EGL error code. + */ +static EGLint +egl_error_from_dri_image_error(int dri_error) +{ + switch (dri_error) { + case __DRI_IMAGE_ERROR_SUCCESS: + return EGL_SUCCESS; + case __DRI_IMAGE_ERROR_BAD_ALLOC: + return EGL_BAD_ALLOC; + case __DRI_IMAGE_ERROR_BAD_MATCH: + return EGL_BAD_MATCH; + case __DRI_IMAGE_ERROR_BAD_PARAMETER: + return EGL_BAD_PARAMETER; + case __DRI_IMAGE_ERROR_BAD_ACCESS: + return EGL_BAD_ACCESS; + default: + assert(!"unknown dri_error code"); + return EGL_BAD_ALLOC; + } +} + +static _EGLImage * +dri2_create_image_khr_renderbuffer(_EGLDisplay *disp, _EGLContext *ctx, + EGLClientBuffer buffer, + const EGLint *attr_list) +{ + struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); + GLuint renderbuffer = (GLuint)(uintptr_t)buffer; + struct dri_image *dri_image; + + if (renderbuffer == 0) { + _eglError(EGL_BAD_PARAMETER, "dri2_create_image_khr"); + return EGL_NO_IMAGE_KHR; + } + + if (!disp->Extensions.KHR_gl_renderbuffer_image) { + _eglError(EGL_BAD_PARAMETER, "dri2_create_image_khr"); + return EGL_NO_IMAGE_KHR; + } + + unsigned error = ~0; + dri_image = dri_create_image_from_renderbuffer( + dri2_ctx->dri_context, renderbuffer, NULL, &error); + + assert(!!dri_image == (error == __DRI_IMAGE_ERROR_SUCCESS)); + + if (!dri_image) { + _eglError(egl_error_from_dri_image_error(error), "dri2_create_image_khr"); + return EGL_NO_IMAGE_KHR; + } + + return dri2_create_image_from_dri(disp, dri_image); +} + +#ifdef HAVE_BIND_WL_DISPLAY +static _EGLImage * +dri2_create_image_wayland_wl_buffer(_EGLDisplay *disp, _EGLContext *ctx, + EGLClientBuffer _buffer, + const EGLint *attr_list) +{ + struct wl_drm_buffer *buffer; + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri_image *dri_image; + _EGLImageAttribs attrs; + int32_t plane; + + buffer = wayland_drm_buffer_get(dri2_dpy->wl_server_drm, + (struct wl_resource *)_buffer); + if (!buffer) + return NULL; + + if (!_eglParseImageAttribList(&attrs, disp, attr_list)) + return NULL; + + plane = attrs.PlaneWL; + + dri_image = dri2_from_planar(buffer->driver_buffer, plane, NULL); + if (dri_image == NULL && plane == 0) + dri_image = dri2_dup_image(buffer->driver_buffer, NULL); + if (dri_image == NULL) { + _eglError(EGL_BAD_PARAMETER, "dri2_create_image_wayland_wl_buffer"); + return NULL; + } + + return dri2_create_image_from_dri(disp, dri_image); +} +#endif + +static EGLBoolean +dri2_get_sync_values_chromium(_EGLDisplay *disp, _EGLSurface *surf, + EGLuint64KHR *ust, EGLuint64KHR *msc, + EGLuint64KHR *sbc) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + EGLBoolean ret = EGL_FALSE; + + if (dri2_dpy->vtbl->get_sync_values) + ret = dri2_dpy->vtbl->get_sync_values(disp, surf, ust, msc, sbc); + + return ret; +} + +static EGLBoolean +dri2_get_msc_rate_angle(_EGLDisplay *disp, _EGLSurface *surf, EGLint *numerator, + EGLint *denominator) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + if (!dri2_dpy->vtbl->get_msc_rate) + return EGL_FALSE; + return dri2_dpy->vtbl->get_msc_rate(disp, surf, numerator, denominator); +} + +/** + * Set the error code after a call to + * dri2_egl_image::dri_image::createImageFromTexture. + */ +static void +dri2_create_image_khr_texture_error(int dri_error) +{ + EGLint egl_error = egl_error_from_dri_image_error(dri_error); + + if (egl_error != EGL_SUCCESS) + _eglError(egl_error, "dri2_create_image_khr_texture"); +} + +static _EGLImage * +dri2_create_image_khr_texture(_EGLDisplay *disp, _EGLContext *ctx, + EGLenum target, EGLClientBuffer buffer, + const EGLint *attr_list) +{ + struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); + struct dri2_egl_image *dri2_img; + GLuint texture = (GLuint)(uintptr_t)buffer; + _EGLImageAttribs attrs; + GLuint depth; + GLenum gl_target; + unsigned error = __DRI_IMAGE_ERROR_SUCCESS; + + if (texture == 0) { + _eglError(EGL_BAD_PARAMETER, "dri2_create_image_khr"); + return EGL_NO_IMAGE_KHR; + } + + if (!_eglParseImageAttribList(&attrs, disp, attr_list)) + return EGL_NO_IMAGE_KHR; + + switch (target) { + case EGL_GL_TEXTURE_2D_KHR: + if (!disp->Extensions.KHR_gl_texture_2D_image) { + _eglError(EGL_BAD_PARAMETER, "dri2_create_image_khr"); + return EGL_NO_IMAGE_KHR; + } + depth = 0; + gl_target = GL_TEXTURE_2D; + break; + case EGL_GL_TEXTURE_3D_KHR: + if (!disp->Extensions.KHR_gl_texture_3D_image) { + _eglError(EGL_BAD_PARAMETER, "dri2_create_image_khr"); + return EGL_NO_IMAGE_KHR; + } + + depth = attrs.GLTextureZOffset; + gl_target = GL_TEXTURE_3D; + break; + case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR: + if (!disp->Extensions.KHR_gl_texture_cubemap_image) { + _eglError(EGL_BAD_PARAMETER, "dri2_create_image_khr"); + return EGL_NO_IMAGE_KHR; + } + + depth = target - EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR; + gl_target = GL_TEXTURE_CUBE_MAP; + break; + default: + UNREACHABLE("Unexpected target in dri2_create_image_khr_texture()"); + return EGL_NO_IMAGE_KHR; + } + + dri2_img = malloc(sizeof *dri2_img); + if (!dri2_img) { + _eglError(EGL_BAD_ALLOC, "dri2_create_image_khr"); + return EGL_NO_IMAGE_KHR; + } + + _eglInitImage(&dri2_img->base, disp); + + dri2_img->dri_image = dri2_create_from_texture( + dri2_ctx->dri_context, gl_target, texture, depth, attrs.GLTextureLevel, + &error, NULL); + dri2_create_image_khr_texture_error(error); + + if (!dri2_img->dri_image) { + free(dri2_img); + return EGL_NO_IMAGE_KHR; + } + return &dri2_img->base; +} + +static EGLBoolean +dri2_query_surface(_EGLDisplay *disp, _EGLSurface *surf, EGLint attribute, + EGLint *value) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + EGLBoolean ret; + + if (!dri2_dpy->vtbl->query_surface) { + ret = _eglQuerySurface(disp, surf, attribute, value); + } else { + ret = dri2_dpy->vtbl->query_surface(disp, surf, attribute, value); + } + + return ret; +} + +static struct wl_buffer * +dri2_create_wayland_buffer_from_image(_EGLDisplay *disp, _EGLImage *img) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + struct wl_buffer *ret = NULL; + + if (dri2_dpy->vtbl->create_wayland_buffer_from_image) + ret = dri2_dpy->vtbl->create_wayland_buffer_from_image(disp, img); + + mtx_unlock(&dri2_dpy->lock); + + return ret; +} + +#ifdef HAVE_LIBDRM +static EGLBoolean +dri2_check_dma_buf_attribs(const _EGLImageAttribs *attrs) +{ + /** + * The spec says: + * + * "Required attributes and their values are as follows: + * + * * EGL_WIDTH & EGL_HEIGHT: The logical dimensions of the buffer in pixels + * + * * EGL_LINUX_DRM_FOURCC_EXT: The pixel format of the buffer, as specified + * by drm_fourcc.h and used as the pixel_format parameter of the + * drm_mode_fb_cmd2 ioctl." + * + * and + * + * "* If is EGL_LINUX_DMA_BUF_EXT, and the list of attributes is + * incomplete, EGL_BAD_PARAMETER is generated." + */ + if (attrs->Width <= 0 || attrs->Height <= 0 || + !attrs->DMABufFourCC.IsPresent) + return _eglError(EGL_BAD_PARAMETER, "attribute(s) missing"); + + /** + * Also: + * + * "If is EGL_LINUX_DMA_BUF_EXT and one or more of the values + * specified for a plane's pitch or offset isn't supported by EGL, + * EGL_BAD_ACCESS is generated." + */ + for (unsigned i = 0; i < ARRAY_SIZE(attrs->DMABufPlanePitches); ++i) { + if (attrs->DMABufPlanePitches[i].IsPresent && + attrs->DMABufPlanePitches[i].Value <= 0) + return _eglError(EGL_BAD_ACCESS, "invalid pitch"); + } + + /** + * If is EGL_LINUX_DMA_BUF_EXT, both or neither of the following + * attribute values may be given. + * + * This is referring to EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT and + * EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, and the same for other planes. + */ + for (unsigned i = 0; i < DMA_BUF_MAX_PLANES; ++i) { + if (attrs->DMABufPlaneModifiersLo[i].IsPresent != + attrs->DMABufPlaneModifiersHi[i].IsPresent) + return _eglError(EGL_BAD_PARAMETER, + "modifier attribute lo or hi missing"); + } + + /* Although the EGL_EXT_image_dma_buf_import_modifiers spec doesn't + * mandate it, we only accept the same modifier across all planes. */ + for (unsigned i = 1; i < DMA_BUF_MAX_PLANES; ++i) { + if (attrs->DMABufPlaneFds[i].IsPresent) { + if ((attrs->DMABufPlaneModifiersLo[0].IsPresent != + attrs->DMABufPlaneModifiersLo[i].IsPresent) || + (attrs->DMABufPlaneModifiersLo[0].Value != + attrs->DMABufPlaneModifiersLo[i].Value) || + (attrs->DMABufPlaneModifiersHi[0].Value != + attrs->DMABufPlaneModifiersHi[i].Value)) + return _eglError(EGL_BAD_PARAMETER, + "modifier attributes not equal"); + } + } + + return EGL_TRUE; +} + +/* Returns the total number of planes for the format or zero if it isn't a + * valid fourcc format. + */ +static unsigned +dri2_num_fourcc_format_planes(EGLint format) +{ + switch (format) { + case DRM_FORMAT_R8: + case DRM_FORMAT_RG88: + case DRM_FORMAT_GR88: + case DRM_FORMAT_R16: + case DRM_FORMAT_R16F: + case DRM_FORMAT_R32F: + case DRM_FORMAT_GR1616: + case DRM_FORMAT_GR1616F: + case DRM_FORMAT_GR3232F: + case DRM_FORMAT_BGR161616: + case DRM_FORMAT_BGR161616F: + case DRM_FORMAT_BGR323232F: + case DRM_FORMAT_ABGR32323232F: + case DRM_FORMAT_RGB332: + case DRM_FORMAT_BGR233: + case DRM_FORMAT_XRGB4444: + case DRM_FORMAT_XBGR4444: + case DRM_FORMAT_RGBX4444: + case DRM_FORMAT_BGRX4444: + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_ABGR4444: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_BGRA4444: + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_XBGR1555: + case DRM_FORMAT_RGBX5551: + case DRM_FORMAT_BGRX5551: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_ABGR1555: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_BGRA5551: + case DRM_FORMAT_RGB565: + case DRM_FORMAT_BGR565: + case DRM_FORMAT_RGB888: + case DRM_FORMAT_BGR888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_XBGR2101010: + case DRM_FORMAT_RGBX1010102: + case DRM_FORMAT_BGRX1010102: + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_RGBA1010102: + case DRM_FORMAT_BGRA1010102: + case DRM_FORMAT_ABGR16161616: + case DRM_FORMAT_XBGR16161616: + case DRM_FORMAT_XBGR16161616F: + case DRM_FORMAT_ABGR16161616F: + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_AYUV: + case DRM_FORMAT_XYUV8888: + case DRM_FORMAT_Y210: + case DRM_FORMAT_Y212: + case DRM_FORMAT_Y216: + case DRM_FORMAT_Y410: + case DRM_FORMAT_Y412: + case DRM_FORMAT_Y416: + case DRM_FORMAT_YUV420_8BIT: + case DRM_FORMAT_YUV420_10BIT: + return 1; + + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + case DRM_FORMAT_NV24: + case DRM_FORMAT_NV42: + case DRM_FORMAT_NV15: + case DRM_FORMAT_NV20: + case DRM_FORMAT_NV30: + case DRM_FORMAT_P010: + case DRM_FORMAT_P012: + case DRM_FORMAT_P016: + case DRM_FORMAT_P030: + return 2; + + case DRM_FORMAT_YUV410: + case DRM_FORMAT_YVU410: + case DRM_FORMAT_YUV411: + case DRM_FORMAT_YVU411: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YVU422: + case DRM_FORMAT_YUV444: + case DRM_FORMAT_YVU444: + case DRM_FORMAT_S010: + case DRM_FORMAT_S210: + case DRM_FORMAT_S410: + case DRM_FORMAT_S012: + case DRM_FORMAT_S212: + case DRM_FORMAT_S412: + case DRM_FORMAT_S016: + case DRM_FORMAT_S216: + case DRM_FORMAT_S416: + return 3; + + default: + return 0; + } +} + +/* Returns the total number of file descriptors. Zero indicates an error. */ +static unsigned +dri2_check_dma_buf_format(const _EGLImageAttribs *attrs) +{ + unsigned plane_n = dri2_num_fourcc_format_planes(attrs->DMABufFourCC.Value); + if (plane_n == 0) { + _eglError(EGL_BAD_MATCH, "unknown drm fourcc format"); + return 0; + } + + for (unsigned i = plane_n; i < DMA_BUF_MAX_PLANES; i++) { + /** + * The modifiers extension spec says: + * + * "Modifiers may modify any attribute of a buffer import, including + * but not limited to adding extra planes to a format which + * otherwise does not have those planes. As an example, a modifier + * may add a plane for an external compression buffer to a + * single-plane format. The exact meaning and effect of any + * modifier is canonically defined by drm_fourcc.h, not as part of + * this extension." + */ + if (attrs->DMABufPlaneModifiersLo[i].IsPresent && + attrs->DMABufPlaneModifiersHi[i].IsPresent) { + plane_n = i + 1; + } + } + + /** + * The spec says: + * + * "* If is EGL_LINUX_DMA_BUF_EXT, and the list of attributes is + * incomplete, EGL_BAD_PARAMETER is generated." + */ + for (unsigned i = 0; i < plane_n; ++i) { + if (!attrs->DMABufPlaneFds[i].IsPresent || + !attrs->DMABufPlaneOffsets[i].IsPresent || + !attrs->DMABufPlanePitches[i].IsPresent) { + _eglError(EGL_BAD_PARAMETER, "plane attribute(s) missing"); + return 0; + } + } + + /** + * The spec also says: + * + * "If is EGL_LINUX_DMA_BUF_EXT, and the EGL_LINUX_DRM_FOURCC_EXT + * attribute indicates a single-plane format, EGL_BAD_ATTRIBUTE is + * generated if any of the EGL_DMA_BUF_PLANE1_* or EGL_DMA_BUF_PLANE2_* + * or EGL_DMA_BUF_PLANE3_* attributes are specified." + */ + for (unsigned i = plane_n; i < DMA_BUF_MAX_PLANES; ++i) { + if (attrs->DMABufPlaneFds[i].IsPresent || + attrs->DMABufPlaneOffsets[i].IsPresent || + attrs->DMABufPlanePitches[i].IsPresent) { + _eglError(EGL_BAD_ATTRIBUTE, "too many plane attributes"); + return 0; + } + } + + return plane_n; +} + +static EGLBoolean +dri2_query_dma_buf_formats(_EGLDisplay *disp, EGLint max, EGLint *formats, + EGLint *count) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + if (max < 0 || (max > 0 && formats == NULL)) { + _eglError(EGL_BAD_PARAMETER, "invalid value for max count of formats"); + goto fail; + } + + if (!dri2_dpy->has_dmabuf_import) + goto fail; + + if (!dri_query_dma_buf_formats(dri2_dpy->dri_screen_render_gpu, + max, formats, count)) + goto fail; + + if (max > 0) { + /* Assert that all of the formats returned are actually fourcc formats. + * Some day, if we want the internal interface function to be able to + * return the fake fourcc formats defined in mesa_interface.h, we'll have + * to do something more clever here to pair the list down to just real + * fourcc formats so that we don't leak the fake internal ones. + */ + for (int i = 0; i < *count; i++) { + assert(dri2_num_fourcc_format_planes(formats[i]) > 0); + } + } + + mtx_unlock(&dri2_dpy->lock); + + return EGL_TRUE; + +fail: + mtx_unlock(&dri2_dpy->lock); + return EGL_FALSE; +} + +static EGLBoolean +dri2_query_dma_buf_modifiers(_EGLDisplay *disp, EGLint format, EGLint max, + EGLuint64KHR *modifiers, EGLBoolean *external_only, + EGLint *count) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + + if (dri2_num_fourcc_format_planes(format) == 0) + return dri2_egl_error_unlock(dri2_dpy, EGL_BAD_PARAMETER, + "invalid fourcc format"); + + if (max < 0) + return dri2_egl_error_unlock(dri2_dpy, EGL_BAD_PARAMETER, + "invalid value for max count of formats"); + + if (max > 0 && modifiers == NULL) + return dri2_egl_error_unlock(dri2_dpy, EGL_BAD_PARAMETER, + "invalid modifiers array"); + + if (!dri2_dpy->has_dmabuf_import) { + mtx_unlock(&dri2_dpy->lock); + return EGL_FALSE; + } + + if (dri_query_dma_buf_modifiers( + dri2_dpy->dri_screen_render_gpu, format, max, modifiers, + (unsigned int *)external_only, count) == false) + return dri2_egl_error_unlock(dri2_dpy, EGL_BAD_PARAMETER, + "invalid format"); + + mtx_unlock(&dri2_dpy->lock); + + return EGL_TRUE; +} + +/** + * The spec says: + * + * "If eglCreateImageKHR is successful for a EGL_LINUX_DMA_BUF_EXT target, the + * EGL will take a reference to the dma_buf(s) which it will release at any + * time while the EGLDisplay is initialized. It is the responsibility of the + * application to close the dma_buf file descriptors." + * + * Therefore we must never close or otherwise modify the file descriptors. + */ +_EGLImage * +dri2_create_image_dma_buf(_EGLDisplay *disp, _EGLContext *ctx, + EGLClientBuffer buffer, const EGLint *attr_list) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + _EGLImage *res; + _EGLImageAttribs attrs; + struct dri_image *dri_image; + unsigned num_fds; + int fds[DMA_BUF_MAX_PLANES]; + int pitches[DMA_BUF_MAX_PLANES]; + int offsets[DMA_BUF_MAX_PLANES]; + uint64_t modifier; + unsigned error = __DRI_IMAGE_ERROR_SUCCESS; + EGLint egl_error; + + /** + * The spec says: + * + * ""* If is EGL_LINUX_DMA_BUF_EXT and is not NULL, the + * error EGL_BAD_PARAMETER is generated." + */ + if (buffer != NULL) { + _eglError(EGL_BAD_PARAMETER, "buffer not NULL"); + return NULL; + } + + if (!_eglParseImageAttribList(&attrs, disp, attr_list)) + return NULL; + + if (!dri2_check_dma_buf_attribs(&attrs)) + return NULL; + + num_fds = dri2_check_dma_buf_format(&attrs); + if (!num_fds) + return NULL; + + for (unsigned i = 0; i < num_fds; ++i) { + fds[i] = attrs.DMABufPlaneFds[i].Value; + pitches[i] = attrs.DMABufPlanePitches[i].Value; + offsets[i] = attrs.DMABufPlaneOffsets[i].Value; + } + + /* dri2_check_dma_buf_attribs ensures that the modifier, if available, + * will be present in attrs.DMABufPlaneModifiersLo[0] and + * attrs.DMABufPlaneModifiersHi[0] */ + if (attrs.DMABufPlaneModifiersLo[0].IsPresent) { + modifier = combine_u32_into_u64(attrs.DMABufPlaneModifiersHi[0].Value, + attrs.DMABufPlaneModifiersLo[0].Value); + } else { + modifier = DRM_FORMAT_MOD_INVALID; + } + + uint32_t flags = 0; + if (attrs.ProtectedContent) + flags |= __DRI_IMAGE_PROTECTED_CONTENT_FLAG; + + dri_image = dri2_from_dma_bufs( + dri2_dpy->dri_screen_render_gpu, attrs.Width, attrs.Height, + attrs.DMABufFourCC.Value, modifier, fds, num_fds, pitches, offsets, + attrs.DMABufYuvColorSpaceHint.Value, attrs.DMABufSampleRangeHint.Value, + attrs.DMABufChromaHorizontalSiting.Value, + attrs.DMABufChromaVerticalSiting.Value, + flags, &error, NULL); + + egl_error = egl_error_from_dri_image_error(error); + if (egl_error != EGL_SUCCESS) + _eglError(egl_error, "createImageFromDmaBufs failed"); + + if (!dri_image) + return EGL_NO_IMAGE_KHR; + + res = dri2_create_image_from_dri(disp, dri_image); + + return res; +} + +/** + * Checks if we can support EGL_MESA_image_dma_buf_export on this image. + + * The spec provides a boolean return for the driver to reject exporting for + * basically any reason, but doesn't specify any particular error cases. For + * now, we just fail if we don't have a DRM fourcc for the format. + */ +static bool +dri2_can_export_dma_buf_image(_EGLDisplay *disp, _EGLImage *img) +{ + struct dri2_egl_image *dri2_img = dri2_egl_image(img); + EGLint fourcc; + + if (!dri2_query_image(dri2_img->dri_image, + __DRI_IMAGE_ATTRIB_FOURCC, &fourcc)) { + return false; + } + + return true; +} + +static EGLBoolean +dri2_export_dma_buf_image_query_mesa(_EGLDisplay *disp, _EGLImage *img, + EGLint *fourcc, EGLint *nplanes, + EGLuint64KHR *modifiers) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + struct dri2_egl_image *dri2_img = dri2_egl_image(img); + int num_planes; + + if (!dri2_can_export_dma_buf_image(disp, img)) { + mtx_unlock(&dri2_dpy->lock); + return EGL_FALSE; + } + + dri2_query_image(dri2_img->dri_image, + __DRI_IMAGE_ATTRIB_NUM_PLANES, &num_planes); + if (nplanes) + *nplanes = num_planes; + + if (fourcc) + dri2_query_image(dri2_img->dri_image, + __DRI_IMAGE_ATTRIB_FOURCC, fourcc); + + if (modifiers) { + int mod_hi, mod_lo; + uint64_t modifier = DRM_FORMAT_MOD_INVALID; + bool query; + + query = dri2_query_image( + dri2_img->dri_image, __DRI_IMAGE_ATTRIB_MODIFIER_UPPER, &mod_hi); + query &= dri2_query_image( + dri2_img->dri_image, __DRI_IMAGE_ATTRIB_MODIFIER_LOWER, &mod_lo); + if (query) + modifier = combine_u32_into_u64(mod_hi, mod_lo); + + for (int i = 0; i < num_planes; i++) + modifiers[i] = modifier; + } + + mtx_unlock(&dri2_dpy->lock); + + return EGL_TRUE; +} + +static EGLBoolean +dri2_export_dma_buf_image_mesa(_EGLDisplay *disp, _EGLImage *img, int *fds, + EGLint *strides, EGLint *offsets) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + struct dri2_egl_image *dri2_img = dri2_egl_image(img); + + if (!dri2_can_export_dma_buf_image(disp, img)) { + mtx_unlock(&dri2_dpy->lock); + return EGL_FALSE; + } + + int nplanes; + /* Query nplanes so that we know how big the given array is. */ + dri2_query_image(dri2_img->dri_image, __DRI_IMAGE_ATTRIB_NUM_PLANES, &nplanes); + + /* For driver which does not implement disjoint query, still uses + * single fd to match previous behavior. + */ + int is_disjoint = false; + if (nplanes > 1) { + dri2_query_image(dri2_img->dri_image, __DRI_IMAGE_ATTRIB_DISJOINT_PLANES, + &is_disjoint); + } + + for (int i = 0; i < nplanes; i++) { + struct dri_image *image = dri2_img->dri_image; + if (i) + image = dri2_from_planar(image, i, NULL); + + if (fds) { + /* EGL_MESA_image_dma_buf_export spec says: + * "If the number of fds is less than the number of planes, then + * subsequent fd slots should contain -1." + */ + if (i == 0 || is_disjoint) + dri2_query_image(image, __DRI_IMAGE_ATTRIB_FD, &fds[i]); + else + fds[i] = -1; + } + + if (strides && !dri2_query_image(image, __DRI_IMAGE_ATTRIB_STRIDE, &strides[i])) + strides[i] = 0; + + if (offsets && !dri2_query_image(image, __DRI_IMAGE_ATTRIB_OFFSET, &offsets[i])) + offsets[i] = 0; + + if (i) + dri2_destroy_image(image); + } + + mtx_unlock(&dri2_dpy->lock); + + return EGL_TRUE; +} + +#endif + +_EGLImage * +dri2_create_image_khr(_EGLDisplay *disp, _EGLContext *ctx, EGLenum target, + EGLClientBuffer buffer, const EGLint *attr_list) +{ + switch (target) { + case EGL_GL_TEXTURE_2D_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR: + case EGL_GL_TEXTURE_3D_KHR: + return dri2_create_image_khr_texture(disp, ctx, target, buffer, + attr_list); + case EGL_GL_RENDERBUFFER_KHR: + return dri2_create_image_khr_renderbuffer(disp, ctx, buffer, attr_list); +#ifdef HAVE_LIBDRM + case EGL_LINUX_DMA_BUF_EXT: + return dri2_create_image_dma_buf(disp, ctx, buffer, attr_list); +#endif +#ifdef HAVE_BIND_WL_DISPLAY + case EGL_WAYLAND_BUFFER_WL: + return dri2_create_image_wayland_wl_buffer(disp, ctx, buffer, attr_list); +#endif + default: + _eglError(EGL_BAD_PARAMETER, "dri2_create_image_khr"); + return EGL_NO_IMAGE_KHR; + } +} + +static EGLBoolean +dri2_destroy_image_khr(_EGLDisplay *disp, _EGLImage *image) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + struct dri2_egl_image *dri2_img = dri2_egl_image(image); + + dri2_destroy_image(dri2_img->dri_image); + free(dri2_img); + + mtx_unlock(&dri2_dpy->lock); + + return EGL_TRUE; +} + +#ifdef HAVE_BIND_WL_DISPLAY + +static void +dri2_wl_reference_buffer(void *user_data, int fd, struct wl_drm_buffer *buffer) +{ + _EGLDisplay *disp = user_data; + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + + buffer->driver_buffer = dri2_from_dma_bufs( + dri2_dpy->dri_screen_render_gpu, buffer->width, buffer->height, + buffer->format, DRM_FORMAT_MOD_INVALID, &fd, 1, buffer->stride, + buffer->offset, 0, 0, 0, 0, 0, NULL, NULL); +} + +static void +dri2_wl_release_buffer(void *user_data, struct wl_drm_buffer *buffer) +{ + dri2_destroy_image(buffer->driver_buffer); +} + +static EGLBoolean +dri2_bind_wayland_display_wl(_EGLDisplay *disp, struct wl_display *wl_dpy) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + const struct wayland_drm_callbacks wl_drm_callbacks = { + .authenticate = (int (*)(void *, uint32_t))dri2_dpy->vtbl->authenticate, + .reference_buffer = dri2_wl_reference_buffer, + .release_buffer = dri2_wl_release_buffer, + .is_format_supported = dri2_wl_is_format_supported, + }; + char *device_name; + + if (dri2_dpy->wl_server_drm) + goto fail; + + device_name = drmGetRenderDeviceNameFromFd(dri2_dpy->fd_render_gpu); + if (!device_name) + device_name = strdup(dri2_dpy->device_name); + if (!device_name) + goto fail; + + if (!dri2_dpy->has_dmabuf_import || !dri2_dpy->has_dmabuf_export) + goto fail; + + dri2_dpy->wl_server_drm = + wayland_drm_init(wl_dpy, device_name, &wl_drm_callbacks, disp); + + free(device_name); + + if (!dri2_dpy->wl_server_drm) + goto fail; + +#ifdef HAVE_DRM_PLATFORM + /* We have to share the wl_drm instance with gbm, so gbm can convert + * wl_buffers to gbm bos. */ + if (dri2_dpy->gbm_dri) + dri2_dpy->gbm_dri->wl_drm = dri2_dpy->wl_server_drm; +#endif + + mtx_unlock(&dri2_dpy->lock); + return EGL_TRUE; + +fail: + mtx_unlock(&dri2_dpy->lock); + return EGL_FALSE; +} + +static EGLBoolean +dri2_unbind_wayland_display_wl(_EGLDisplay *disp, struct wl_display *wl_dpy) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + + if (!dri2_dpy->wl_server_drm) + return EGL_FALSE; + + wayland_drm_uninit(dri2_dpy->wl_server_drm); + dri2_dpy->wl_server_drm = NULL; + + return EGL_TRUE; +} + +static EGLBoolean +dri2_query_wayland_buffer_wl(_EGLDisplay *disp, + struct wl_resource *buffer_resource, + EGLint attribute, EGLint *value) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct wl_drm_buffer *buffer; + + buffer = wayland_drm_buffer_get(dri2_dpy->wl_server_drm, buffer_resource); + if (!buffer) + return EGL_FALSE; + + switch (attribute) { + case EGL_TEXTURE_FORMAT: + *value = buffer->egl_components; + return EGL_TRUE; + case EGL_WIDTH: + *value = buffer->width; + return EGL_TRUE; + case EGL_HEIGHT: + *value = buffer->height; + return EGL_TRUE; + } + + return EGL_FALSE; +} +#endif + +static void +dri2_egl_ref_sync(struct dri2_egl_sync *sync) +{ + p_atomic_inc(&sync->refcount); +} + +static void +dri2_egl_unref_sync(struct dri2_egl_display *dri2_dpy, + struct dri2_egl_sync *dri2_sync) +{ + if (p_atomic_dec_zero(&dri2_sync->refcount)) { + switch (dri2_sync->base.Type) { + case EGL_SYNC_REUSABLE_KHR: + cnd_destroy(&dri2_sync->cond); + break; + case EGL_SYNC_NATIVE_FENCE_ANDROID: + if (dri2_sync->base.SyncFd != EGL_NO_NATIVE_FENCE_FD_ANDROID) + close(dri2_sync->base.SyncFd); + break; + default: + break; + } + + if (dri2_sync->fence) + dri_destroy_fence(dri2_dpy->dri_screen_render_gpu, + dri2_sync->fence); + + free(dri2_sync); + } +} + +static _EGLSync * +dri2_create_sync(_EGLDisplay *disp, EGLenum type, const EGLAttrib *attrib_list) +{ + _EGLContext *ctx = _eglGetCurrentContext(); + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); + struct dri2_egl_sync *dri2_sync; + EGLint ret; + pthread_condattr_t attr; + + dri2_sync = calloc(1, sizeof(struct dri2_egl_sync)); + if (!dri2_sync) { + _eglError(EGL_BAD_ALLOC, "eglCreateSyncKHR"); + goto fail; + } + + if (!_eglInitSync(&dri2_sync->base, disp, type, attrib_list)) { + goto fail; + } + + switch (type) { + case EGL_SYNC_FENCE_KHR: + dri2_sync->fence = dri_create_fence(dri2_ctx->dri_context); + if (!dri2_sync->fence) { + /* Why did it fail? DRI doesn't return an error code, so we emit + * a generic EGL error that doesn't communicate user error. + */ + _eglError(EGL_BAD_ALLOC, "eglCreateSyncKHR"); + goto fail; + } + break; + + case EGL_SYNC_CL_EVENT_KHR: + dri2_sync->fence = dri_get_fence_from_cl_event( + dri2_dpy->dri_screen_render_gpu, dri2_sync->base.CLEvent); + /* this can only happen if the cl_event passed in is invalid. */ + if (!dri2_sync->fence) { + _eglError(EGL_BAD_ATTRIBUTE, "eglCreateSyncKHR"); + goto fail; + } + + /* the initial status must be "signaled" if the cl_event is signaled */ + if (dri_client_wait_sync(dri2_ctx->dri_context, + dri2_sync->fence, 0, 0)) + dri2_sync->base.SyncStatus = EGL_SIGNALED_KHR; + break; + + case EGL_SYNC_REUSABLE_KHR: + /* initialize attr */ + ret = pthread_condattr_init(&attr); + + if (ret) { + _eglError(EGL_BAD_ACCESS, "eglCreateSyncKHR"); + goto fail; + } + +#if !defined(__APPLE__) && !defined(__MACOSX) + /* change clock attribute to CLOCK_MONOTONIC */ + ret = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); + + if (ret) { + _eglError(EGL_BAD_ACCESS, "eglCreateSyncKHR"); + goto fail; + } +#endif + + ret = pthread_cond_init(&dri2_sync->cond, &attr); + + if (ret) { + _eglError(EGL_BAD_ACCESS, "eglCreateSyncKHR"); + goto fail; + } + + /* initial status of reusable sync must be "unsignaled" */ + dri2_sync->base.SyncStatus = EGL_UNSIGNALED_KHR; + break; + + case EGL_SYNC_NATIVE_FENCE_ANDROID: + dri2_sync->fence = dri_create_fence_fd( + dri2_ctx->dri_context, dri2_sync->base.SyncFd); + if (!dri2_sync->fence) { + _eglError(EGL_BAD_ATTRIBUTE, "eglCreateSyncKHR"); + goto fail; + } + break; + } + + p_atomic_set(&dri2_sync->refcount, 1); + mtx_unlock(&dri2_dpy->lock); + + return &dri2_sync->base; + +fail: + free(dri2_sync); + mtx_unlock(&dri2_dpy->lock); + return NULL; +} + +static EGLBoolean +dri2_destroy_sync(_EGLDisplay *disp, _EGLSync *sync) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + struct dri2_egl_sync *dri2_sync = dri2_egl_sync(sync); + EGLint ret = EGL_TRUE; + EGLint err; + + /* if type of sync is EGL_SYNC_REUSABLE_KHR and it is not signaled yet, + * then unlock all threads possibly blocked by the reusable sync before + * destroying it. + */ + if (dri2_sync->base.Type == EGL_SYNC_REUSABLE_KHR && + dri2_sync->base.SyncStatus == EGL_UNSIGNALED_KHR) { + dri2_sync->base.SyncStatus = EGL_SIGNALED_KHR; + /* unblock all threads currently blocked by sync */ + err = cnd_broadcast(&dri2_sync->cond); + + if (err) { + _eglError(EGL_BAD_ACCESS, "eglDestroySyncKHR"); + ret = EGL_FALSE; + } + } + + dri2_egl_unref_sync(dri2_dpy, dri2_sync); + + mtx_unlock(&dri2_dpy->lock); + + return ret; +} + +static EGLint +dri2_dup_native_fence_fd(_EGLDisplay *disp, _EGLSync *sync) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + struct dri2_egl_sync *dri2_sync = dri2_egl_sync(sync); + + assert(sync->Type == EGL_SYNC_NATIVE_FENCE_ANDROID); + + if (sync->SyncFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + /* try to retrieve the actual native fence fd.. if rendering is + * not flushed this will just return -1, aka NO_NATIVE_FENCE_FD: + */ + sync->SyncFd = dri_get_fence_fd( + dri2_dpy->dri_screen_render_gpu, dri2_sync->fence); + } + + mtx_unlock(&dri2_dpy->lock); + + if (sync->SyncFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + /* if native fence fd still not created, return an error: */ + _eglError(EGL_BAD_PARAMETER, "eglDupNativeFenceFDANDROID"); + return EGL_NO_NATIVE_FENCE_FD_ANDROID; + } + + assert(sync_valid_fd(sync->SyncFd)); + + return os_dupfd_cloexec(sync->SyncFd); +} + +static void +dri2_set_blob_cache_funcs(_EGLDisplay *disp, EGLSetBlobFuncANDROID set, + EGLGetBlobFuncANDROID get) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display_lock(disp); + dri_set_blob_cache_funcs(dri2_dpy->dri_screen_render_gpu, set, get); + mtx_unlock(&dri2_dpy->lock); +} + +static EGLint +dri2_client_wait_sync(_EGLDisplay *disp, _EGLSync *sync, EGLint flags, + EGLTime timeout) +{ + _EGLContext *ctx = _eglGetCurrentContext(); + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); + struct dri2_egl_sync *dri2_sync = dri2_egl_sync(sync); + unsigned wait_flags = 0; + + EGLint ret = EGL_CONDITION_SATISFIED_KHR; + + /* The EGL_KHR_fence_sync spec states: + * + * "If no context is current for the bound API, + * the EGL_SYNC_FLUSH_COMMANDS_BIT_KHR bit is ignored. + */ + if (dri2_ctx && flags & EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) + wait_flags |= __DRI2_FENCE_FLAG_FLUSH_COMMANDS; + + /* the sync object should take a reference while waiting */ + dri2_egl_ref_sync(dri2_sync); + + switch (sync->Type) { + case EGL_SYNC_FENCE_KHR: + case EGL_SYNC_NATIVE_FENCE_ANDROID: + case EGL_SYNC_CL_EVENT_KHR: + if (dri_client_wait_sync( + dri2_ctx ? dri2_ctx->dri_context : NULL, dri2_sync->fence, + wait_flags, timeout)) + dri2_sync->base.SyncStatus = EGL_SIGNALED_KHR; + else + ret = EGL_TIMEOUT_EXPIRED_KHR; + break; + + case EGL_SYNC_REUSABLE_KHR: + if (dri2_ctx && dri2_sync->base.SyncStatus == EGL_UNSIGNALED_KHR && + (flags & EGL_SYNC_FLUSH_COMMANDS_BIT_KHR)) { + /* flush context if EGL_SYNC_FLUSH_COMMANDS_BIT_KHR is set */ + dri2_gl_flush(); + } + + /* if timeout is EGL_FOREVER_KHR, it should wait without any timeout.*/ + if (timeout == EGL_FOREVER_KHR) { + mtx_lock(&dri2_sync->mutex); + cnd_wait(&dri2_sync->cond, &dri2_sync->mutex); + mtx_unlock(&dri2_sync->mutex); + } else { + /* if reusable sync has not been yet signaled */ + if (dri2_sync->base.SyncStatus != EGL_SIGNALED_KHR) { + /* timespecs for cnd_timedwait */ + struct timespec current; + struct timespec expire; + + /* We override the clock to monotonic when creating the condition + * variable. */ + clock_gettime(CLOCK_MONOTONIC, ¤t); + + /* calculating when to expire */ + expire.tv_nsec = timeout % 1000000000L; + expire.tv_sec = timeout / 1000000000L; + + expire.tv_nsec += current.tv_nsec; + expire.tv_sec += current.tv_sec; + + /* expire.nsec now is a number between 0 and 1999999998 */ + if (expire.tv_nsec > 999999999L) { + expire.tv_sec++; + expire.tv_nsec -= 1000000000L; + } + + mtx_lock(&dri2_sync->mutex); + ret = cnd_timedwait(&dri2_sync->cond, &dri2_sync->mutex, &expire); + mtx_unlock(&dri2_sync->mutex); + + if (ret == thrd_timedout) { + if (dri2_sync->base.SyncStatus == EGL_UNSIGNALED_KHR) { + ret = EGL_TIMEOUT_EXPIRED_KHR; + } else { + _eglError(EGL_BAD_ACCESS, "eglClientWaitSyncKHR"); + ret = EGL_FALSE; + } + } + } + } + break; + } + + dri2_egl_unref_sync(dri2_dpy, dri2_sync); + + return ret; +} + +static EGLBoolean +dri2_signal_sync(_EGLDisplay *disp, _EGLSync *sync, EGLenum mode) +{ + struct dri2_egl_sync *dri2_sync = dri2_egl_sync(sync); + EGLint ret; + + if (sync->Type != EGL_SYNC_REUSABLE_KHR) + return _eglError(EGL_BAD_MATCH, "eglSignalSyncKHR"); + + if (mode != EGL_SIGNALED_KHR && mode != EGL_UNSIGNALED_KHR) + return _eglError(EGL_BAD_ATTRIBUTE, "eglSignalSyncKHR"); + + dri2_sync->base.SyncStatus = mode; + + if (mode == EGL_SIGNALED_KHR) { + ret = cnd_broadcast(&dri2_sync->cond); + + /* fail to broadcast */ + if (ret) + return _eglError(EGL_BAD_ACCESS, "eglSignalSyncKHR"); + } + + return EGL_TRUE; +} + +static EGLint +dri2_server_wait_sync(_EGLDisplay *disp, _EGLSync *sync) +{ + _EGLContext *ctx = _eglGetCurrentContext(); + struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); + struct dri2_egl_sync *dri2_sync = dri2_egl_sync(sync); + + dri_server_wait_sync(dri2_ctx->dri_context, dri2_sync->fence, + 0); + return EGL_TRUE; +} + +static int +dri2_interop_query_device_info(_EGLDisplay *disp, _EGLContext *ctx, + struct mesa_glinterop_device_info *out) +{ + struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); + + return dri_interop_query_device_info(dri2_ctx->dri_context, out); +} + +static int +dri2_interop_export_object(_EGLDisplay *disp, _EGLContext *ctx, + struct mesa_glinterop_export_in *in, + struct mesa_glinterop_export_out *out) +{ + struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); + + return dri_interop_export_object(dri2_ctx->dri_context, in, out); +} + +static int +dri2_interop_flush_objects(_EGLDisplay *disp, _EGLContext *ctx, unsigned count, + struct mesa_glinterop_export_in *objects, + struct mesa_glinterop_flush_out *out) +{ + struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); + + return dri_interop_flush_objects(dri2_ctx->dri_context, count, + objects, out); +} + +static EGLBoolean +dri2_query_supported_compression_rates(_EGLDisplay *disp, _EGLConfig *config, + const EGLAttrib *attr_list, + EGLint *rates, EGLint rate_size, + EGLint *num_rate) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri2_egl_config *conf = dri2_egl_config(config); + enum __DRIFixedRateCompression dri_rates[rate_size]; + + if (dri2_dpy->has_compression_modifiers) { + const struct dri_config *dri_conf = + dri2_get_dri_config(conf, EGL_WINDOW_BIT, EGL_GL_COLORSPACE_LINEAR); + if (!dri2_query_compression_rates( + dri2_dpy->dri_screen_render_gpu, dri_conf, rate_size, dri_rates, + num_rate)) + return EGL_FALSE; + + for (int i = 0; i < *num_rate && i < rate_size; ++i) + rates[i] = dri_rates[i]; + return EGL_TRUE; + } + *num_rate = 0; + return EGL_TRUE; +} + +const _EGLDriver _eglDriver = { + .Initialize = dri2_initialize, + .Terminate = dri2_terminate, + .CreateContext = dri2_create_context, + .DestroyContext = dri2_destroy_context, + .MakeCurrent = dri2_make_current, + .CreateWindowSurface = dri2_create_window_surface, + .CreatePixmapSurface = dri2_create_pixmap_surface, + .CreatePbufferSurface = dri2_create_pbuffer_surface, + .DestroySurface = dri2_destroy_surface, + .WaitClient = dri2_wait_client, + .WaitNative = dri2_wait_native, + .BindTexImage = dri2_bind_tex_image, + .ReleaseTexImage = dri2_release_tex_image, + .SwapInterval = dri2_swap_interval, + .SwapBuffers = dri2_swap_buffers, + .SwapBuffersWithDamageEXT = dri2_swap_buffers_with_damage, + .SetDamageRegion = dri2_set_damage_region, + .CopyBuffers = dri2_copy_buffers, + .QueryBufferAge = dri2_query_buffer_age, + .CreateImageKHR = dri2_create_image, + .DestroyImageKHR = dri2_destroy_image_khr, + .QuerySurface = dri2_query_surface, + .QueryDriverName = dri2_query_driver_name, + .QueryDriverConfig = dri2_query_driver_config, + .QueryDeviceInfo = dri2_query_device_info, +#ifdef HAVE_LIBDRM + .ExportDMABUFImageQueryMESA = dri2_export_dma_buf_image_query_mesa, + .ExportDMABUFImageMESA = dri2_export_dma_buf_image_mesa, + .QueryDmaBufFormatsEXT = dri2_query_dma_buf_formats, + .QueryDmaBufModifiersEXT = dri2_query_dma_buf_modifiers, +#endif +#ifdef HAVE_BIND_WL_DISPLAY + .BindWaylandDisplayWL = dri2_bind_wayland_display_wl, + .UnbindWaylandDisplayWL = dri2_unbind_wayland_display_wl, + .QueryWaylandBufferWL = dri2_query_wayland_buffer_wl, + .CreateWaylandBufferFromImageWL = dri2_create_wayland_buffer_from_image, +#endif + .GetSyncValuesCHROMIUM = dri2_get_sync_values_chromium, + .GetMscRateANGLE = dri2_get_msc_rate_angle, + .CreateSyncKHR = dri2_create_sync, + .ClientWaitSyncKHR = dri2_client_wait_sync, + .SignalSyncKHR = dri2_signal_sync, + .WaitSyncKHR = dri2_server_wait_sync, + .DestroySyncKHR = dri2_destroy_sync, + .GLInteropQueryDeviceInfo = dri2_interop_query_device_info, + .GLInteropExportObject = dri2_interop_export_object, + .GLInteropFlushObjects = dri2_interop_flush_objects, + .DupNativeFenceFDANDROID = dri2_dup_native_fence_fd, + .SetBlobCacheFuncsANDROID = dri2_set_blob_cache_funcs, + .QuerySupportedCompressionRatesEXT = dri2_query_supported_compression_rates, +}; diff --git a/local/recipes/libs/mesa/source/src/egl/drivers/dri2/platform_redox.c b/local/recipes/libs/mesa/source/src/egl/drivers/dri2/platform_redox.c new file mode 100644 index 0000000000..16cdd3e379 --- /dev/null +++ b/local/recipes/libs/mesa/source/src/egl/drivers/dri2/platform_redox.c @@ -0,0 +1,298 @@ +/* + * Mesa 3-D graphics library + * + * Copyright © 2026 Red Bear OS + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include +#include +#include + +#include "egl_dri2.h" +#include "eglglobals.h" +#include "kopper_interface.h" +#include "loader.h" +#include "loader_dri_helper.h" +#include "dri_util.h" +#include "dri_screen.h" + +#define REDOX_DRM_DEVICE_PATH "/scheme/drm/card0" + +static struct dri_image * +redox_alloc_image(struct dri2_egl_display *dri2_dpy, + struct dri2_egl_surface *dri2_surf) +{ + return dri_create_image(dri2_dpy->dri_screen_render_gpu, + dri2_surf->base.Width, + dri2_surf->base.Height, + dri2_surf->visual, NULL, 0, 0, NULL); +} + +static void +redox_free_images(struct dri2_egl_surface *dri2_surf) +{ + if (dri2_surf->front) { + dri2_destroy_image(dri2_surf->front); + dri2_surf->front = NULL; + } + + free(dri2_surf->swrast_device_buffer); + dri2_surf->swrast_device_buffer = NULL; +} + +static int +redox_image_get_buffers(__DRIdrawable *driDrawable, unsigned int format, + uint32_t *stamp, void *loaderPrivate, + uint32_t buffer_mask, struct __DRIimageList *buffers) +{ + struct dri2_egl_surface *dri2_surf = loaderPrivate; + struct dri2_egl_display *dri2_dpy = + dri2_egl_display(dri2_surf->base.Resource.Display); + + buffers->image_mask = 0; + buffers->front = NULL; + buffers->back = NULL; + + if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) { + if (!dri2_surf->front) + dri2_surf->front = redox_alloc_image(dri2_dpy, dri2_surf); + buffers->image_mask |= __DRI_IMAGE_BUFFER_FRONT; + buffers->front = dri2_surf->front; + } + + return 1; +} + +static void +redox_flush_front_buffer(__DRIdrawable *driDrawable, void *loaderPrivate) +{ +} + +static unsigned +redox_get_capability(void *loaderPrivate, enum dri_loader_cap cap) +{ + switch (cap) { + case DRI_LOADER_CAP_FP16: + return 1; + case DRI_LOADER_CAP_RGBA_ORDERING: + return 1; + default: + return 0; + } +} + +static const __DRIimageLoaderExtension redox_image_loader_extension = { + .base = {__DRI_IMAGE_LOADER, 2}, + .getBuffers = redox_image_get_buffers, + .flushFrontBuffer = redox_flush_front_buffer, + .getCapability = redox_get_capability, +}; + +static const __DRIextension *image_loader_extensions[] = { + &redox_image_loader_extension.base, + &image_lookup_extension.base, + &use_invalidate.base, + NULL, +}; + +static const __DRIextension *swrast_loader_extensions[] = { + &swrast_pbuffer_loader_extension.base, + &redox_image_loader_extension.base, + &image_lookup_extension.base, + &use_invalidate.base, + NULL, +}; + +static const __DRIextension *kopper_loader_extensions[] = { + &kopper_pbuffer_loader_extension.base, + &image_lookup_extension.base, + NULL, +}; + +static _EGLSurface * +redox_create_pbuffer_surface(_EGLDisplay *disp, _EGLConfig *conf, + const EGLint *attrib_list) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri2_egl_config *dri2_conf = dri2_egl_config(conf); + struct dri2_egl_surface *dri2_surf; + const struct dri_config *config; + + dri2_surf = calloc(1, sizeof *dri2_surf); + if (!dri2_surf) { + _eglError(EGL_BAD_ALLOC, "eglCreatePbufferSurface"); + return NULL; + } + + if (!dri2_init_surface(&dri2_surf->base, disp, EGL_PBUFFER_BIT, conf, + attrib_list, false, NULL)) + goto cleanup_surface; + + config = dri2_get_dri_config(dri2_conf, EGL_PBUFFER_BIT, + dri2_surf->base.GLColorspace); + if (!config) { + _eglError(EGL_BAD_MATCH, + "Unsupported surfacetype/colorspace configuration"); + goto cleanup_surface; + } + + dri2_surf->visual = dri2_image_format_for_pbuffer_config(dri2_dpy, config); + if (dri2_surf->visual == PIPE_FORMAT_NONE) + goto cleanup_surface; + + if (!dri2_create_drawable(dri2_dpy, config, dri2_surf, dri2_surf)) + goto cleanup_surface; + + return &dri2_surf->base; + +cleanup_surface: + free(dri2_surf); + return NULL; +} + +static EGLBoolean +redox_destroy_surface(_EGLDisplay *disp, _EGLSurface *surf) +{ + struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); + + redox_free_images(dri2_surf); + driDestroyDrawable(dri2_surf->dri_drawable); + dri2_fini_surface(surf); + free(dri2_surf); + return EGL_TRUE; +} + +static const struct dri2_egl_display_vtbl dri2_redox_display_vtbl = { + .create_pbuffer_surface = redox_create_pbuffer_surface, + .destroy_surface = redox_destroy_surface, + .create_image = dri2_create_image_khr, + .get_dri_drawable = dri2_surface_get_dri_drawable, + .flush_get_images = dri2_flush_get_images_for_swap_buffers_with_damage, + .image_get_buffers = redox_image_get_buffers, +}; + +static bool +redox_probe_device_hw(_EGLDisplay *disp) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + int fd = -1; + char *driver_name = NULL; + + fd = open(REDOX_DRM_DEVICE_PATH, O_RDWR | O_CLOEXEC); + if (fd < 0) { + _eglLog(_EGL_DEBUG, "redox-egl: no DRM device at %s", + REDOX_DRM_DEVICE_PATH); + return false; + } + + driver_name = loader_get_driver_for_fd(fd); + if (!driver_name) { + _eglLog(_EGL_WARNING, "redox-egl: loader_get_driver_for_fd failed"); + close(fd); + return false; + } + + if (strcmp(driver_name, "swrast") == 0 || + strcmp(driver_name, "kms_swrast") == 0) { + _eglLog(_EGL_DEBUG, "redox-egl: driver is swrast, skipping HW path"); + free(driver_name); + close(fd); + return false; + } + + dri2_dpy->fd_render_gpu = fd; + dri2_dpy->driver_name = driver_name; + + if (!dri2_load_driver_dri3(disp)) { + _eglLog(_EGL_WARNING, "redox-egl: dri2_load_driver_dri3 failed"); + free(dri2_dpy->driver_name); + dri2_dpy->driver_name = NULL; + close(dri2_dpy->fd_render_gpu); + dri2_dpy->fd_render_gpu = -1; + return false; + } + + dri2_dpy->loader_extensions = kopper_loader_extensions; + return true; +} + +static bool +redox_probe_device_sw(_EGLDisplay *disp) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + + dri2_dpy->driver_name = strdup("swrast"); + if (!dri2_dpy->driver_name) + return false; + + dri2_detect_swrast_kopper(disp); + + if (dri2_dpy->kopper) + dri2_dpy->loader_extensions = kopper_loader_extensions; + else + dri2_dpy->loader_extensions = swrast_loader_extensions; + + dri2_dpy->fd_render_gpu = -1; + dri2_dpy->fd_display_gpu = -1; + + if (!dri2_create_screen(disp)) { + _eglLog(_EGL_WARNING, "DRI2: failed to create swrast screen"); + free(dri2_dpy->driver_name); + dri2_dpy->driver_name = NULL; + return false; + } + + return true; +} + +EGLBoolean +dri2_initialize_redox(_EGLDisplay *disp) +{ + struct dri2_egl_display *dri2_dpy; + bool driver_loaded = false; + + dri2_dpy = dri2_display_create(); + if (!dri2_dpy) + return _eglError(EGL_BAD_ALLOC, "dri2_display_create"); + + disp->DriverData = (void *)dri2_dpy; + + driver_loaded = redox_probe_device_hw(disp); + if (!driver_loaded) { + _eglLog(_EGL_DEBUG, "redox-egl: HW probe failed, falling back to swrast"); + driver_loaded = redox_probe_device_sw(disp); + } + + if (!driver_loaded) { + _eglError(EGL_NOT_INITIALIZED, "DRI2: failed to load any driver"); + goto cleanup; + } + + dri2_dpy->fd_display_gpu = dri2_dpy->fd_render_gpu; + + if (dri2_dpy->fd_render_gpu >= 0 && !dri2_create_screen(disp)) { + _eglError(EGL_NOT_INITIALIZED, "DRI2: failed to create screen"); + goto cleanup; + } + + if (!dri2_setup_extensions(disp)) { + _eglError(EGL_NOT_INITIALIZED, "DRI2: failed to find required DRI extensions"); + goto cleanup; + } + + dri2_setup_screen(disp); + dri2_add_pbuffer_configs_for_visuals(disp); + + dri2_dpy->vtbl = &dri2_redox_display_vtbl; + + return EGL_TRUE; + +cleanup: + dri2_display_destroy(disp); + return EGL_FALSE; +} diff --git a/local/recipes/libs/mesa/source/src/egl/main/eglapi.c b/local/recipes/libs/mesa/source/src/egl/main/eglapi.c new file mode 100644 index 0000000000..633fc59357 --- /dev/null +++ b/local/recipes/libs/mesa/source/src/egl/main/eglapi.c @@ -0,0 +1,2888 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * Copyright 2009-2010 Chia-I Wu + * Copyright 2010-2011 LunarG, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * Public EGL API entrypoints + * + * Generally, we use the EGLDisplay parameter as a key to lookup the + * appropriate device driver handle, then jump though the driver's + * dispatch table to handle the function. + * + * That allows us the option of supporting multiple, simultaneous, + * heterogeneous hardware devices in the future. + * + * The EGLDisplay, EGLConfig, EGLContext and EGLSurface types are + * opaque handles. Internal objects are linked to a display to + * create the handles. + * + * For each public API entry point, the opaque handles are looked up + * before being dispatched to the drivers. When it fails to look up + * a handle, one of + * + * EGL_BAD_DISPLAY + * EGL_BAD_CONFIG + * EGL_BAD_CONTEXT + * EGL_BAD_SURFACE + * EGL_BAD_SCREEN_MESA + * EGL_BAD_MODE_MESA + * + * is generated and the driver function is not called. An + * uninitialized EGLDisplay has no driver associated with it. When + * such display is detected, + * + * EGL_NOT_INITIALIZED + * + * is generated. + * + * Some of the entry points use current display, context, or surface + * implicitly. For such entry points, the implicit objects are also + * checked before calling the driver function. Other than the + * errors listed above, + * + * EGL_BAD_CURRENT_SURFACE + * + * may also be generated. + * + * Notes on naming conventions: + * + * eglFooBar - public EGL function + * EGL_FOO_BAR - public EGL token + * EGLDatatype - public EGL datatype + * + * _eglFooBar - private EGL function + * _EGLDatatype - private EGL datatype, typedef'd struct + * _egl_struct - private EGL struct, non-typedef'd + * + */ + +#include +#include +#include +#include +#include "c11/threads.h" +#include "mesa/glapi/glapi/glapi.h" +#include "util/detect_os.h" +#include "util/macros.h" +#include "util/perf/cpu_trace.h" +#include "util/u_debug.h" + +#include "eglconfig.h" +#include "eglcontext.h" +#include "eglcurrent.h" +#include "egldefines.h" +#include "egldevice.h" +#include "egldisplay.h" +#include "egldriver.h" +#include "eglglobals.h" +#include "eglimage.h" +#include "egllog.h" +#include "eglsurface.h" +#include "eglsync.h" +#include "egltypedefs.h" + +#include "GL/mesa_glinterop.h" + +/** + * Macros to help return an API entrypoint. + * + * These macros will unlock the display and record the error code. + */ +#define RETURN_EGL_ERROR(disp, err, ret) \ + do { \ + if (disp) \ + _eglUnlockDisplay(disp); \ + /* EGL error codes are non-zero */ \ + if (err) \ + _eglError(err, __func__); \ + return ret; \ + } while (0) + +#define RETURN_EGL_SUCCESS(disp, ret) RETURN_EGL_ERROR(disp, EGL_SUCCESS, ret) + +/* record EGL_SUCCESS only when ret evaluates to true */ +#define RETURN_EGL_EVAL(disp, ret) \ + RETURN_EGL_ERROR(disp, (ret) ? EGL_SUCCESS : 0, ret) + +/* + * A bunch of macros and checks to simplify error checking. + */ + +#define _EGL_CHECK_DISPLAY(disp, ret) \ + do { \ + if (!_eglCheckDisplay(disp, __func__)) \ + RETURN_EGL_ERROR(disp, 0, ret); \ + } while (0) + +#define _EGL_CHECK_OBJECT(disp, type, obj, ret) \ + do { \ + if (!_eglCheck##type(disp, obj, __func__)) \ + RETURN_EGL_ERROR(disp, 0, ret); \ + } while (0) + +#define _EGL_CHECK_SURFACE(disp, surf, ret) \ + _EGL_CHECK_OBJECT(disp, Surface, surf, ret) + +#define _EGL_CHECK_CONTEXT(disp, context, ret) \ + _EGL_CHECK_OBJECT(disp, Context, context, ret) + +#define _EGL_CHECK_CONFIG(disp, conf, ret) \ + _EGL_CHECK_OBJECT(disp, Config, conf, ret) + +#define _EGL_CHECK_SYNC(disp, s, ret) _EGL_CHECK_OBJECT(disp, Sync, s, ret) + +static _EGLResource ** +_egl_relax_begin(_EGLDisplay *disp, _EGLResource **rs, unsigned rs_count) +{ + for (unsigned i = 0; i < rs_count; i++) + if (rs[i]) + _eglGetResource(rs[i]); + simple_mtx_unlock(&disp->Mutex); + return rs; +} + +static _EGLResource ** +_egl_relax_end(_EGLDisplay *disp, _EGLResource **rs, unsigned rs_count) +{ + simple_mtx_lock(&disp->Mutex); + for (unsigned i = 0; i < rs_count; i++) + if (rs[i]) + _eglPutResource(rs[i]); + return NULL; +} + +/** + * Helper to relax (drop) the EGL BDL over it's body, optionally holding + * a reference to a list of _EGLResource's until the lock is re-acquired, + * protecting the resources from destruction while the BDL is dropped. + */ +#define egl_relax(disp, ...) \ + for (_EGLResource * __rs[] = {NULL /* for vs2019 */, __VA_ARGS__}, \ + **__rsp = \ + _egl_relax_begin(disp, __rs, ARRAY_SIZE(__rs)); \ + __rsp; __rsp = _egl_relax_end(disp, __rs, ARRAY_SIZE(__rs))) + +extern const _EGLDriver _eglDriver; + +struct _egl_entrypoint { + const char *name; + _EGLProc function; +}; + +static inline bool +_eglCheckDisplay(_EGLDisplay *disp, const char *msg) +{ + if (!disp) { + _eglError(EGL_BAD_DISPLAY, msg); + return false; + } + if (!disp->Initialized) { + _eglError(EGL_NOT_INITIALIZED, msg); + return false; + } + return true; +} + +static inline bool +_eglCheckSurface(_EGLDisplay *disp, _EGLSurface *surf, const char *msg) +{ + if (!_eglCheckDisplay(disp, msg)) + return false; + if (!surf) { + _eglError(EGL_BAD_SURFACE, msg); + return false; + } + return true; +} + +static inline bool +_eglCheckContext(_EGLDisplay *disp, _EGLContext *context, const char *msg) +{ + if (!_eglCheckDisplay(disp, msg)) + return false; + if (!context) { + _eglError(EGL_BAD_CONTEXT, msg); + return false; + } + return true; +} + +static inline bool +_eglCheckConfig(_EGLDisplay *disp, _EGLConfig *conf, const char *msg) +{ + if (!_eglCheckDisplay(disp, msg)) + return false; + if (!conf) { + _eglError(EGL_BAD_CONFIG, msg); + return false; + } + return true; +} + +static inline bool +_eglCheckSync(_EGLDisplay *disp, _EGLSync *s, const char *msg) +{ + if (!_eglCheckDisplay(disp, msg)) + return false; + if (!s) { + _eglError(EGL_BAD_PARAMETER, msg); + return false; + } + return true; +} + +/** + * Lookup a handle to find the linked display. + * Return NULL if the handle has no corresponding linked display. + */ +static _EGLDisplay * +_eglLookupDisplay(EGLDisplay dpy) +{ + simple_mtx_lock(_eglGlobal.Mutex); + + _EGLDisplay *cur = _eglGlobal.DisplayList; + while (cur) { + if (cur == (_EGLDisplay *)dpy) + break; + cur = cur->Next; + } + simple_mtx_unlock(_eglGlobal.Mutex); + + return cur; +} + +/** + * Lookup and lock a display. + */ +_EGLDisplay * +_eglLockDisplay(EGLDisplay dpy) +{ + _EGLDisplay *disp = _eglLookupDisplay(dpy); + if (disp) { + u_rwlock_rdlock(&disp->TerminateLock); + simple_mtx_lock(&disp->Mutex); + } + return disp; +} + +/** + * Lookup and write-lock a display. Should only be called from + * eglTerminate. + */ +static _EGLDisplay * +_eglWriteLockDisplay(EGLDisplay dpy) +{ + _EGLDisplay *disp = _eglLookupDisplay(dpy); + if (disp) { + u_rwlock_wrlock(&disp->TerminateLock); + simple_mtx_lock(&disp->Mutex); + } + return disp; +} + +/** + * Unlock a display. + */ +void +_eglUnlockDisplay(_EGLDisplay *disp) +{ + simple_mtx_unlock(&disp->Mutex); + u_rwlock_rdunlock(&disp->TerminateLock); +} + +static void +_eglSetFuncName(const char *funcName, _EGLDisplay *disp, EGLenum objectType, + _EGLResource *object) +{ + _EGLThreadInfo *thr = _eglGetCurrentThread(); + thr->CurrentFuncName = funcName; + thr->CurrentObjectLabel = NULL; + + if (objectType == EGL_OBJECT_THREAD_KHR) + thr->CurrentObjectLabel = thr->Label; + else if (objectType == EGL_OBJECT_DISPLAY_KHR && disp) + thr->CurrentObjectLabel = disp->Label; + else if (object) + thr->CurrentObjectLabel = object->Label; +} + +#define _EGL_FUNC_START(disp, objectType, object) \ + do { \ + MESA_TRACE_FUNC(); \ + _eglSetFuncName(__func__, disp, objectType, (_EGLResource *)object); \ + } while (0) + +/** + * Convert an attribute list from EGLint[] to EGLAttrib[]. + * + * Return an EGL error code. The output parameter out_attrib_list is modified + * only on success. + */ +static EGLint +_eglConvertIntsToAttribs(const EGLint *int_list, EGLAttrib **out_attrib_list) +{ + size_t len = 0; + EGLAttrib *attrib_list; + + if (int_list) { + while (int_list[2 * len] != EGL_NONE) + ++len; + } + + if (len == 0) { + *out_attrib_list = NULL; + return EGL_SUCCESS; + } + + if (2 * len + 1 > SIZE_MAX / sizeof(EGLAttrib)) + return EGL_BAD_ALLOC; + + attrib_list = malloc((2 * len + 1) * sizeof(EGLAttrib)); + if (!attrib_list) + return EGL_BAD_ALLOC; + + for (size_t i = 0; i < len; ++i) { + attrib_list[2 * i + 0] = int_list[2 * i + 0]; + attrib_list[2 * i + 1] = int_list[2 * i + 1]; + } + + attrib_list[2 * len] = EGL_NONE; + + *out_attrib_list = attrib_list; + return EGL_SUCCESS; +} + +static EGLint * +_eglConvertAttribsToInt(const EGLAttrib *attr_list) +{ + size_t size = _eglNumAttribs(attr_list); + EGLint *int_attribs = NULL; + + /* Convert attributes from EGLAttrib[] to EGLint[] */ + if (size) { + int_attribs = calloc(size, sizeof(int_attribs[0])); + if (!int_attribs) + return NULL; + + for (size_t i = 0; i < size; i++) + int_attribs[i] = attr_list[i]; + } + return int_attribs; +} + +/** + * This is typically the first EGL function that an application calls. + * It associates a private _EGLDisplay object to the native display. + */ +PUBLIC EGLDisplay EGLAPIENTRY +eglGetDisplay(EGLNativeDisplayType nativeDisplay) +{ + _EGLPlatformType plat; + _EGLDisplay *disp; + void *native_display_ptr; + +#if !DETECT_OS_ANDROID + util_cpu_trace_init(); +#endif + _EGL_FUNC_START(NULL, EGL_OBJECT_THREAD_KHR, NULL); + + STATIC_ASSERT(sizeof(void *) >= sizeof(nativeDisplay)); + native_display_ptr = (void *)nativeDisplay; + + plat = _eglGetNativePlatform(native_display_ptr); + disp = _eglFindDisplay(plat, native_display_ptr, NULL); + return _eglGetDisplayHandle(disp); +} + +static EGLDisplay +_eglGetPlatformDisplayCommon(EGLenum platform, void *native_display, + const EGLAttrib *attrib_list) +{ + _EGLDisplay *disp; + +#ifndef EGL_PLATFORM_REDOX_REDBEAR +#define EGL_PLATFORM_REDOX_REDBEAR 0x31E0 +#endif + + switch (platform) { +#ifdef HAVE_X11_PLATFORM + case EGL_PLATFORM_X11_EXT: + disp = _eglGetX11Display((Display *)native_display, attrib_list); + break; +#endif +#ifdef HAVE_XCB_PLATFORM + case EGL_PLATFORM_XCB_EXT: + disp = _eglGetXcbDisplay((xcb_connection_t *)native_display, attrib_list); + break; +#endif +#ifdef HAVE_DRM_PLATFORM + case EGL_PLATFORM_GBM_MESA: + disp = + _eglGetGbmDisplay((struct gbm_device *)native_display, attrib_list); + break; +#endif +#ifdef HAVE_WAYLAND_PLATFORM + case EGL_PLATFORM_WAYLAND_EXT: + disp = _eglGetWaylandDisplay((struct wl_display *)native_display, + attrib_list); + break; +#endif + case EGL_PLATFORM_SURFACELESS_MESA: + disp = _eglGetSurfacelessDisplay(native_display, attrib_list); + break; +#ifdef HAVE_ANDROID_PLATFORM + case EGL_PLATFORM_ANDROID_KHR: + disp = _eglGetAndroidDisplay(native_display, attrib_list); + break; +#endif + case EGL_PLATFORM_DEVICE_EXT: + disp = _eglGetDeviceDisplay(native_display, attrib_list); + break; +#ifdef HAVE_REDOX_PLATFORM + case EGL_PLATFORM_REDOX_REDBEAR: + disp = _eglGetRedoxDisplay(native_display, attrib_list); + break; +#endif + default: + RETURN_EGL_ERROR(NULL, EGL_BAD_PARAMETER, NULL); + } + + return _eglGetDisplayHandle(disp); +} + +static EGLDisplay EGLAPIENTRY +eglGetPlatformDisplayEXT(EGLenum platform, void *native_display, + const EGLint *int_attribs) +{ + EGLAttrib *attrib_list; + EGLDisplay disp; + +#if !DETECT_OS_ANDROID + util_cpu_trace_init(); +#endif + _EGL_FUNC_START(NULL, EGL_OBJECT_THREAD_KHR, NULL); + + if (_eglConvertIntsToAttribs(int_attribs, &attrib_list) != EGL_SUCCESS) + RETURN_EGL_ERROR(NULL, EGL_BAD_ALLOC, NULL); + + disp = _eglGetPlatformDisplayCommon(platform, native_display, attrib_list); + free(attrib_list); + return disp; +} + +PUBLIC EGLDisplay EGLAPIENTRY +eglGetPlatformDisplay(EGLenum platform, void *native_display, + const EGLAttrib *attrib_list) +{ +#if !DETECT_OS_ANDROID + util_cpu_trace_init(); +#endif + _EGL_FUNC_START(NULL, EGL_OBJECT_THREAD_KHR, NULL); + return _eglGetPlatformDisplayCommon(platform, native_display, attrib_list); +} + +/** + * Copy the extension into the string and update the string pointer. + */ +static EGLint +_eglAppendExtension(char **str, const char *ext) +{ + char *s = *str; + size_t len = strlen(ext); + + if (s) { + memcpy(s, ext, len); + s[len++] = ' '; + s[len] = '\0'; + + *str += len; + } else { + len++; + } + + return (EGLint)len; +} + +/** + * Examine the individual extension enable/disable flags and recompute + * the driver's Extensions string. + */ +static void +_eglCreateExtensionsString(_EGLDisplay *disp) +{ +#define _EGL_CHECK_EXTENSION(ext) \ + do { \ + if (disp->Extensions.ext) { \ + _eglAppendExtension(&exts, "EGL_" #ext); \ + assert(exts <= disp->ExtensionsString + _EGL_MAX_EXTENSIONS_LEN); \ + } \ + } while (0) + + char *exts = disp->ExtensionsString; + + /* Please keep these sorted alphabetically. */ + _EGL_CHECK_EXTENSION(ANDROID_blob_cache); + _EGL_CHECK_EXTENSION(ANDROID_framebuffer_target); + _EGL_CHECK_EXTENSION(ANDROID_image_native_buffer); + _EGL_CHECK_EXTENSION(ANDROID_native_fence_sync); + _EGL_CHECK_EXTENSION(ANDROID_recordable); + + _EGL_CHECK_EXTENSION(CHROMIUM_sync_control); + _EGL_CHECK_EXTENSION(ANGLE_sync_control_rate); + + _EGL_CHECK_EXTENSION(EXT_buffer_age); + _EGL_CHECK_EXTENSION(EXT_config_select_group); + _EGL_CHECK_EXTENSION(EXT_create_context_robustness); + _EGL_CHECK_EXTENSION(EXT_image_dma_buf_import); + _EGL_CHECK_EXTENSION(EXT_image_dma_buf_import_modifiers); + _EGL_CHECK_EXTENSION(EXT_present_opaque); + _EGL_CHECK_EXTENSION(EXT_protected_content); + _EGL_CHECK_EXTENSION(EXT_protected_surface); + _EGL_CHECK_EXTENSION(EXT_query_reset_notification_strategy); + _EGL_CHECK_EXTENSION(EXT_surface_compression); + _EGL_CHECK_EXTENSION(EXT_surface_CTA861_3_metadata); + _EGL_CHECK_EXTENSION(EXT_surface_SMPTE2086_metadata); + _EGL_CHECK_EXTENSION(EXT_swap_buffers_with_damage); + + _EGL_CHECK_EXTENSION(IMG_context_priority); + + _EGL_CHECK_EXTENSION(KHR_cl_event2); + _EGL_CHECK_EXTENSION(KHR_config_attribs); + _EGL_CHECK_EXTENSION(KHR_context_flush_control); + _EGL_CHECK_EXTENSION(KHR_create_context); + _EGL_CHECK_EXTENSION(KHR_create_context_no_error); + _EGL_CHECK_EXTENSION(KHR_fence_sync); + _EGL_CHECK_EXTENSION(KHR_get_all_proc_addresses); + _EGL_CHECK_EXTENSION(KHR_gl_colorspace); + _EGL_CHECK_EXTENSION(KHR_gl_renderbuffer_image); + _EGL_CHECK_EXTENSION(KHR_gl_texture_2D_image); + _EGL_CHECK_EXTENSION(KHR_gl_texture_3D_image); + _EGL_CHECK_EXTENSION(KHR_gl_texture_cubemap_image); + if (disp->Extensions.KHR_image_base && disp->Extensions.KHR_image_pixmap) + disp->Extensions.KHR_image = EGL_TRUE; + _EGL_CHECK_EXTENSION(KHR_image); + _EGL_CHECK_EXTENSION(KHR_image_base); + _EGL_CHECK_EXTENSION(KHR_image_pixmap); + _EGL_CHECK_EXTENSION(KHR_mutable_render_buffer); + _EGL_CHECK_EXTENSION(KHR_no_config_context); + _EGL_CHECK_EXTENSION(KHR_partial_update); + _EGL_CHECK_EXTENSION(KHR_reusable_sync); + _EGL_CHECK_EXTENSION(KHR_surfaceless_context); + if (disp->Extensions.EXT_swap_buffers_with_damage) + _eglAppendExtension(&exts, "EGL_KHR_swap_buffers_with_damage"); + _EGL_CHECK_EXTENSION(EXT_pixel_format_float); + _EGL_CHECK_EXTENSION(KHR_wait_sync); + + if (disp->Extensions.KHR_no_config_context) + _eglAppendExtension(&exts, "EGL_MESA_configless_context"); + _EGL_CHECK_EXTENSION(MESA_gl_interop); + _EGL_CHECK_EXTENSION(MESA_image_dma_buf_export); + _EGL_CHECK_EXTENSION(MESA_query_driver); + _EGL_CHECK_EXTENSION(MESA_x11_native_visual_id); + + _EGL_CHECK_EXTENSION(NOK_texture_from_pixmap); + + _EGL_CHECK_EXTENSION(NV_context_priority_realtime); + + _EGL_CHECK_EXTENSION(WL_bind_wayland_display); + _EGL_CHECK_EXTENSION(WL_create_wayland_buffer_from_image); + +#undef _EGL_CHECK_EXTENSION +} + +static void +_eglCreateAPIsString(_EGLDisplay *disp) +{ +#define addstr(str) \ + { \ + const size_t old_len = strlen(disp->ClientAPIsString); \ + const size_t add_len = sizeof(str); \ + const size_t max_len = sizeof(disp->ClientAPIsString) - 1; \ + if (old_len + add_len <= max_len) \ + strcat(disp->ClientAPIsString, str " "); \ + else \ + assert(!"disp->ClientAPIsString is not large enough"); \ + } + + if (disp->ClientAPIs & EGL_OPENGL_BIT) + addstr("OpenGL"); + + if (disp->ClientAPIs & EGL_OPENGL_ES_BIT || + disp->ClientAPIs & EGL_OPENGL_ES2_BIT || + disp->ClientAPIs & EGL_OPENGL_ES3_BIT_KHR) { + addstr("OpenGL_ES"); + } + + if (disp->ClientAPIs & EGL_OPENVG_BIT) + addstr("OpenVG"); + +#undef addstr +} + +static void +_eglComputeVersion(_EGLDisplay *disp) +{ + disp->Version = 14; + + if (disp->Extensions.KHR_fence_sync && disp->Extensions.KHR_cl_event2 && + disp->Extensions.KHR_wait_sync && disp->Extensions.KHR_image_base && + disp->Extensions.KHR_gl_texture_2D_image && + disp->Extensions.KHR_gl_texture_cubemap_image && + disp->Extensions.KHR_gl_renderbuffer_image && + disp->Extensions.KHR_create_context && + disp->Extensions.KHR_get_all_proc_addresses && + disp->Extensions.KHR_gl_colorspace && + disp->Extensions.KHR_surfaceless_context) + disp->Version = 15; + + /* For Android P and below limit the EGL version to 1.4 */ +#if DETECT_OS_ANDROID && ANDROID_API_LEVEL <= 28 + disp->Version = 14; +#endif +} + +/** + * This is typically the second EGL function that an application calls. + * Here we load/initialize the actual hardware driver. + */ +PUBLIC EGLBoolean EGLAPIENTRY +eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + + util_cpu_trace_init(); + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + + _eglDeviceRefreshList(); + _eglRegisterAtExit(); + + if (!disp) + RETURN_EGL_ERROR(NULL, EGL_BAD_DISPLAY, EGL_FALSE); + + if (!disp->Initialized) { + /* set options */ + disp->Options.ForceSoftware = + debug_get_bool_option("LIBGL_ALWAYS_SOFTWARE", false); + if (disp->Options.ForceSoftware) + _eglLog(_EGL_DEBUG, + "Found 'LIBGL_ALWAYS_SOFTWARE' set, will use a CPU renderer"); + + const char *env = os_get_option("MESA_LOADER_DRIVER_OVERRIDE"); + disp->Options.Zink = env && !strcmp(env, "zink"); + + const char *gallium_hud_env = os_get_option("GALLIUM_HUD"); + disp->Options.GalliumHudWarn = + gallium_hud_env && gallium_hud_env[0] != '\0'; + + /** + * Initialize the display using the driver's function. + * If the initialisation fails, try again using only software rendering. + */ + if (!_eglDriver.Initialize(disp)) { + if (disp->Options.ForceSoftware) + RETURN_EGL_ERROR(disp, EGL_NOT_INITIALIZED, EGL_FALSE); + else { + bool success = false; + if (!disp->Options.Zink && !os_get_option("GALLIUM_DRIVER")) { + disp->Options.Zink = EGL_TRUE; + success = _eglDriver.Initialize(disp); + } + if (!success) { + disp->Options.Zink = EGL_FALSE; + disp->Options.ForceSoftware = EGL_TRUE; + if (!_eglDriver.Initialize(disp)) + RETURN_EGL_ERROR(disp, EGL_NOT_INITIALIZED, EGL_FALSE); + } + } + } + + disp->Initialized = EGL_TRUE; + disp->Driver = &_eglDriver; + + /* limit to APIs supported by core */ + disp->ClientAPIs &= _EGL_API_ALL_BITS; + + /* EGL_KHR_get_all_proc_addresses is a corner-case extension. The spec + * classifies it as an EGL display extension, though conceptually it's an + * EGL client extension. + * + * From the EGL_KHR_get_all_proc_addresses spec: + * + * The EGL implementation must expose the name + * EGL_KHR_client_get_all_proc_addresses if and only if it exposes + * EGL_KHR_get_all_proc_addresses and supports + * EGL_EXT_client_extensions. + * + * Mesa unconditionally exposes both client extensions mentioned above, + * so the spec requires that each EGLDisplay unconditionally expose + * EGL_KHR_get_all_proc_addresses also. + */ + disp->Extensions.KHR_get_all_proc_addresses = EGL_TRUE; + + /* Extensions is used to provide EGL 1.3 functionality for 1.2 aware + * programs. It is driver agnostic and handled in the main EGL code. + */ + disp->Extensions.KHR_config_attribs = EGL_TRUE; + + _eglComputeVersion(disp); + _eglCreateExtensionsString(disp); + _eglCreateAPIsString(disp); + snprintf(disp->VersionString, sizeof(disp->VersionString), "%d.%d", + disp->Version / 10, disp->Version % 10); + } + + /* Update applications version of major and minor if not NULL */ + if ((major != NULL) && (minor != NULL)) { + *major = disp->Version / 10; + *minor = disp->Version % 10; + } + + RETURN_EGL_SUCCESS(disp, EGL_TRUE); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglTerminate(EGLDisplay dpy) +{ + _EGLDisplay *disp = _eglWriteLockDisplay(dpy); + + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + + if (!disp) + RETURN_EGL_ERROR(NULL, EGL_BAD_DISPLAY, EGL_FALSE); + + if (disp->Initialized) { + disp->Driver->Terminate(disp); + /* do not reset disp->Driver */ + disp->ClientAPIsString[0] = 0; + disp->Initialized = EGL_FALSE; + + /* Reset blob cache funcs on terminate. */ + disp->BlobCacheSet = NULL; + disp->BlobCacheGet = NULL; + } + + simple_mtx_unlock(&disp->Mutex); + u_rwlock_wrunlock(&disp->TerminateLock); + + RETURN_EGL_SUCCESS(NULL, EGL_TRUE); +} + +PUBLIC const char *EGLAPIENTRY +eglQueryString(EGLDisplay dpy, EGLint name) +{ + _EGLDisplay *disp; + +#if !USE_LIBGLVND + if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS) { + RETURN_EGL_SUCCESS(NULL, _eglGlobal.ClientExtensionString); + } +#endif + + disp = _eglLockDisplay(dpy); + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + _EGL_CHECK_DISPLAY(disp, NULL); + + switch (name) { + case EGL_VENDOR: + RETURN_EGL_SUCCESS(disp, _EGL_VENDOR_STRING); + case EGL_VERSION: + RETURN_EGL_SUCCESS(disp, disp->VersionString); + case EGL_EXTENSIONS: + RETURN_EGL_SUCCESS(disp, disp->ExtensionsString); + case EGL_CLIENT_APIS: + RETURN_EGL_SUCCESS(disp, disp->ClientAPIsString); + default: + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, NULL); + } +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglGetConfigs(EGLDisplay dpy, EGLConfig *configs, EGLint config_size, + EGLint *num_config) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + EGLBoolean ret; + + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + + _EGL_CHECK_DISPLAY(disp, EGL_FALSE); + + if (!num_config) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, EGL_FALSE); + + ret = _eglGetConfigs(disp, configs, config_size, num_config); + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, + EGLint config_size, EGLint *num_config) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + EGLBoolean ret; + + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + + _EGL_CHECK_DISPLAY(disp, EGL_FALSE); + + if (!num_config) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, EGL_FALSE); + + ret = _eglChooseConfig(disp, attrib_list, configs, config_size, num_config); + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, + EGLint *value) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLConfig *conf = _eglLookupConfig(config, disp); + EGLBoolean ret; + + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + + _EGL_CHECK_CONFIG(disp, conf, EGL_FALSE); + + ret = _eglGetConfigAttrib(disp, conf, attribute, value); + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLContext EGLAPIENTRY +eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_list, + const EGLint *attrib_list) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLConfig *conf = _eglLookupConfig(config, disp); + _EGLContext *share = _eglLookupContext(share_list, disp); + _EGLContext *context; + EGLContext ret; + + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + + _EGL_CHECK_DISPLAY(disp, EGL_NO_CONTEXT); + + if (config != EGL_NO_CONFIG_KHR) + _EGL_CHECK_CONFIG(disp, conf, EGL_NO_CONTEXT); + else if (!disp->Extensions.KHR_no_config_context) + RETURN_EGL_ERROR(disp, EGL_BAD_CONFIG, EGL_NO_CONTEXT); + + if (!share && share_list != EGL_NO_CONTEXT) + RETURN_EGL_ERROR(disp, EGL_BAD_CONTEXT, EGL_NO_CONTEXT); + else if (share && share->Resource.Display != disp) { + /* From the spec. + * + * "An EGL_BAD_MATCH error is generated if an OpenGL or OpenGL ES + * context is requested and any of: [...] + * + * * share context was created on a different display + * than the one reference by config." + */ + RETURN_EGL_ERROR(disp, EGL_BAD_MATCH, EGL_NO_CONTEXT); + } + + context = disp->Driver->CreateContext(disp, conf, share, attrib_list); + ret = (context) ? _eglLinkContext(context) : EGL_NO_CONTEXT; + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglDestroyContext(EGLDisplay dpy, EGLContext ctx) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLContext *context = _eglLookupContext(ctx, disp); + EGLBoolean ret; + + _EGL_FUNC_START(disp, EGL_OBJECT_CONTEXT_KHR, context); + + _EGL_CHECK_CONTEXT(disp, context, EGL_FALSE); + _eglUnlinkContext(context); + ret = disp->Driver->DestroyContext(disp, context); + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLContext *context = _eglLookupContext(ctx, disp); + _EGLSurface *draw_surf = _eglLookupSurface(draw, disp); + _EGLSurface *read_surf = _eglLookupSurface(read, disp); + EGLBoolean ret = EGL_FALSE; + + _EGL_FUNC_START(disp, EGL_OBJECT_CONTEXT_KHR, context); + + if (!disp) + RETURN_EGL_ERROR(disp, EGL_BAD_DISPLAY, EGL_FALSE); + + /* display is allowed to be uninitialized under certain condition */ + if (!disp->Initialized) { + if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE || + ctx != EGL_NO_CONTEXT) + RETURN_EGL_ERROR(disp, EGL_BAD_DISPLAY, EGL_FALSE); + } + if (!disp->Driver) + RETURN_EGL_SUCCESS(disp, EGL_TRUE); + + if (!context && ctx != EGL_NO_CONTEXT) + RETURN_EGL_ERROR(disp, EGL_BAD_CONTEXT, EGL_FALSE); + if (!draw_surf || !read_surf) { + /* From the EGL 1.4 (20130211) spec: + * + * To release the current context without assigning a new one, set ctx + * to EGL_NO_CONTEXT and set draw and read to EGL_NO_SURFACE. + */ + if (!disp->Extensions.KHR_surfaceless_context && ctx != EGL_NO_CONTEXT) + RETURN_EGL_ERROR(disp, EGL_BAD_SURFACE, EGL_FALSE); + + if ((!draw_surf && draw != EGL_NO_SURFACE) || + (!read_surf && read != EGL_NO_SURFACE)) + RETURN_EGL_ERROR(disp, EGL_BAD_SURFACE, EGL_FALSE); + if (draw_surf || read_surf) + RETURN_EGL_ERROR(disp, EGL_BAD_MATCH, EGL_FALSE); + } + + /* If a native window underlying either draw or read is no longer valid, + * an EGL_BAD_NATIVE_WINDOW error is generated. + */ + if (draw_surf && draw_surf->Lost) + RETURN_EGL_ERROR(disp, EGL_BAD_NATIVE_WINDOW, EGL_FALSE); + if (read_surf && read_surf->Lost) + RETURN_EGL_ERROR(disp, EGL_BAD_NATIVE_WINDOW, EGL_FALSE); + /* EGL_EXT_protected_surface spec says: + * If EGL_PROTECTED_CONTENT_EXT attributes of read is EGL_TRUE and + * EGL_PROTECTED_CONTENT_EXT attributes of draw is EGL_FALSE, an + * EGL_BAD_ACCESS error is generated. + */ + if (read_surf && read_surf->ProtectedContent && draw_surf && + !draw_surf->ProtectedContent) + RETURN_EGL_ERROR(disp, EGL_BAD_ACCESS, EGL_FALSE); + + egl_relax (disp, + draw_surf ? &draw_surf->Resource : NULL, + read_surf ? &read_surf->Resource : NULL, + context ? &context->Resource : NULL) { + ret = disp->Driver->MakeCurrent(disp, draw_surf, read_surf, context); + } + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLContext *context = _eglLookupContext(ctx, disp); + EGLBoolean ret; + + _EGL_FUNC_START(disp, EGL_OBJECT_CONTEXT_KHR, context); + + _EGL_CHECK_CONTEXT(disp, context, EGL_FALSE); + + ret = _eglQueryContext(context, attribute, value); + + RETURN_EGL_EVAL(disp, ret); +} + +/* In EGL specs 1.4 and 1.5, at the end of sections 3.5.1 and 3.5.4, it says + * that if native_surface was already used to create a window or pixmap, we + * can't create a new one. This is what this function checks for. + */ +static bool +_eglNativeSurfaceAlreadyUsed(_EGLDisplay *disp, void *native_surface) +{ + _EGLResource *list; + + simple_mtx_assert_locked(&disp->Mutex); + + list = disp->ResourceLists[_EGL_RESOURCE_SURFACE]; + while (list) { + _EGLSurface *surf = (_EGLSurface *)list; + + list = list->Next; + + if (surf->Type == EGL_PBUFFER_BIT) + continue; + + if (surf->NativeSurface == native_surface) + return true; + } + + return false; +} + +static EGLSurface +_eglCreateWindowSurfaceCommon(_EGLDisplay *disp, EGLConfig config, + void *native_window, const EGLint *attrib_list) +{ + _EGLConfig *conf = _eglLookupConfig(config, disp); + _EGLSurface *surf = NULL; + EGLSurface ret; + + if (native_window == NULL) + RETURN_EGL_ERROR(disp, EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); + + if (disp && (disp->Platform == _EGL_PLATFORM_SURFACELESS || + disp->Platform == _EGL_PLATFORM_DEVICE)) { + /* From the EGL_MESA_platform_surfaceless spec (v1): + * + * eglCreatePlatformWindowSurface fails when called with a + * that belongs to the surfaceless platform. It returns + * EGL_NO_SURFACE and generates EGL_BAD_NATIVE_WINDOW. The + * justification for this unconditional failure is that the + * surfaceless platform has no native windows, and therefore the + * parameter is always invalid. + * + * This check must occur before checking the EGLConfig, which emits + * EGL_BAD_CONFIG. + */ + RETURN_EGL_ERROR(disp, EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); + } + + _EGL_CHECK_CONFIG(disp, conf, EGL_NO_SURFACE); + + if ((conf->SurfaceType & EGL_WINDOW_BIT) == 0) + RETURN_EGL_ERROR(disp, EGL_BAD_MATCH, EGL_NO_SURFACE); + + if (_eglNativeSurfaceAlreadyUsed(disp, native_window)) + RETURN_EGL_ERROR(disp, EGL_BAD_ALLOC, EGL_NO_SURFACE); + + egl_relax (disp) { + surf = disp->Driver->CreateWindowSurface(disp, conf, native_window, + attrib_list); + } + ret = (surf) ? _eglLinkSurface(surf) : EGL_NO_SURFACE; + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLSurface EGLAPIENTRY +eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, + EGLNativeWindowType window, const EGLint *attrib_list) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + STATIC_ASSERT(sizeof(void *) == sizeof(window)); + return _eglCreateWindowSurfaceCommon(disp, config, (void *)window, + attrib_list); +} + +static void * +_fixupNativeWindow(_EGLDisplay *disp, void *native_window) +{ +#ifdef HAVE_X11_PLATFORM + if (disp && disp->Platform == _EGL_PLATFORM_X11 && native_window != NULL) { + /* The `native_window` parameter for the X11 platform differs between + * eglCreateWindowSurface() and eglCreatePlatformPixmapSurfaceEXT(). In + * eglCreateWindowSurface(), the type of `native_window` is an Xlib + * `Window`. In eglCreatePlatformWindowSurfaceEXT(), the type is + * `Window*`. Convert `Window*` to `Window` because that's what + * dri2_x11_create_window_surface() expects. + */ + return (void *)(*(Window *)native_window); + } +#endif +#ifdef HAVE_XCB_PLATFORM + if (disp && disp->Platform == _EGL_PLATFORM_XCB && native_window != NULL) { + /* Similar to with X11, we need to convert (xcb_window_t *) + * (i.e., uint32_t *) to xcb_window_t. We have to do an intermediate cast + * to uintptr_t, since uint32_t may be smaller than a pointer. + */ + return (void *)(uintptr_t)(*(uint32_t *)native_window); + } +#endif + return native_window; +} + +static EGLSurface EGLAPIENTRY +eglCreatePlatformWindowSurfaceEXT(EGLDisplay dpy, EGLConfig config, + void *native_window, + const EGLint *attrib_list) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + + native_window = _fixupNativeWindow(disp, native_window); + + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + return _eglCreateWindowSurfaceCommon(disp, config, native_window, + attrib_list); +} + +PUBLIC EGLSurface EGLAPIENTRY +eglCreatePlatformWindowSurface(EGLDisplay dpy, EGLConfig config, + void *native_window, + const EGLAttrib *attrib_list) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + EGLSurface surface; + EGLint *int_attribs; + + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + + int_attribs = _eglConvertAttribsToInt(attrib_list); + if (attrib_list && !int_attribs) + RETURN_EGL_ERROR(disp, EGL_BAD_ALLOC, EGL_NO_SURFACE); + + native_window = _fixupNativeWindow(disp, native_window); + surface = + _eglCreateWindowSurfaceCommon(disp, config, native_window, int_attribs); + free(int_attribs); + return surface; +} + +static void * +_fixupNativePixmap(_EGLDisplay *disp, void *native_pixmap) +{ +#ifdef HAVE_X11_PLATFORM + /* The `native_pixmap` parameter for the X11 platform differs between + * eglCreatePixmapSurface() and eglCreatePlatformPixmapSurfaceEXT(). In + * eglCreatePixmapSurface(), the type of `native_pixmap` is an Xlib + * `Pixmap`. In eglCreatePlatformPixmapSurfaceEXT(), the type is + * `Pixmap*`. Convert `Pixmap*` to `Pixmap` because that's what + * dri2_x11_create_pixmap_surface() expects. + */ + if (disp && disp->Platform == _EGL_PLATFORM_X11 && native_pixmap != NULL) + return (void *)(*(Pixmap *)native_pixmap); +#endif +#ifdef HAVE_XCB_PLATFORM + if (disp && disp->Platform == _EGL_PLATFORM_XCB && native_pixmap != NULL) { + /* Similar to with X11, we need to convert (xcb_pixmap_t *) + * (i.e., uint32_t *) to xcb_pixmap_t. We have to do an intermediate cast + * to uintptr_t, since uint32_t may be smaller than a pointer. + */ + return (void *)(uintptr_t)(*(uint32_t *)native_pixmap); + } +#endif + return native_pixmap; +} + +static EGLSurface +_eglCreatePixmapSurfaceCommon(_EGLDisplay *disp, EGLConfig config, + void *native_pixmap, const EGLint *attrib_list) +{ + _EGLConfig *conf = _eglLookupConfig(config, disp); + _EGLSurface *surf = NULL; + EGLSurface ret; + + if (disp && (disp->Platform == _EGL_PLATFORM_SURFACELESS || + disp->Platform == _EGL_PLATFORM_DEVICE)) { + /* From the EGL_MESA_platform_surfaceless spec (v1): + * + * [Like eglCreatePlatformWindowSurface,] eglCreatePlatformPixmapSurface + * also fails when called with a that belongs to the + * surfaceless platform. It returns EGL_NO_SURFACE and generates + * EGL_BAD_NATIVE_PIXMAP. + * + * This check must occur before checking the EGLConfig, which emits + * EGL_BAD_CONFIG. + */ + RETURN_EGL_ERROR(disp, EGL_BAD_NATIVE_PIXMAP, EGL_NO_SURFACE); + } + + _EGL_CHECK_CONFIG(disp, conf, EGL_NO_SURFACE); + + if ((conf->SurfaceType & EGL_PIXMAP_BIT) == 0) + RETURN_EGL_ERROR(disp, EGL_BAD_MATCH, EGL_NO_SURFACE); + + if (native_pixmap == NULL) + RETURN_EGL_ERROR(disp, EGL_BAD_NATIVE_PIXMAP, EGL_NO_SURFACE); + + if (_eglNativeSurfaceAlreadyUsed(disp, native_pixmap)) + RETURN_EGL_ERROR(disp, EGL_BAD_ALLOC, EGL_NO_SURFACE); + + egl_relax (disp) { + surf = disp->Driver->CreatePixmapSurface(disp, conf, native_pixmap, + attrib_list); + } + ret = (surf) ? _eglLinkSurface(surf) : EGL_NO_SURFACE; + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLSurface EGLAPIENTRY +eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, + EGLNativePixmapType pixmap, const EGLint *attrib_list) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + STATIC_ASSERT(sizeof(void *) == sizeof(pixmap)); + return _eglCreatePixmapSurfaceCommon(disp, config, (void *)pixmap, + attrib_list); +} + +static EGLSurface EGLAPIENTRY +eglCreatePlatformPixmapSurfaceEXT(EGLDisplay dpy, EGLConfig config, + void *native_pixmap, + const EGLint *attrib_list) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + native_pixmap = _fixupNativePixmap(disp, native_pixmap); + return _eglCreatePixmapSurfaceCommon(disp, config, native_pixmap, + attrib_list); +} + +PUBLIC EGLSurface EGLAPIENTRY +eglCreatePlatformPixmapSurface(EGLDisplay dpy, EGLConfig config, + void *native_pixmap, + const EGLAttrib *attrib_list) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + EGLSurface surface; + EGLint *int_attribs; + + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + + int_attribs = _eglConvertAttribsToInt(attrib_list); + if (attrib_list && !int_attribs) + RETURN_EGL_ERROR(disp, EGL_BAD_ALLOC, EGL_NO_SURFACE); + + native_pixmap = _fixupNativePixmap(disp, native_pixmap); + surface = + _eglCreatePixmapSurfaceCommon(disp, config, native_pixmap, int_attribs); + free(int_attribs); + return surface; +} + +PUBLIC EGLSurface EGLAPIENTRY +eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, + const EGLint *attrib_list) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLConfig *conf = _eglLookupConfig(config, disp); + _EGLSurface *surf = NULL; + EGLSurface ret; + + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + _EGL_CHECK_CONFIG(disp, conf, EGL_NO_SURFACE); + + if ((conf->SurfaceType & EGL_PBUFFER_BIT) == 0) + RETURN_EGL_ERROR(disp, EGL_BAD_MATCH, EGL_NO_SURFACE); + + egl_relax (disp) { + surf = disp->Driver->CreatePbufferSurface(disp, conf, attrib_list); + } + ret = (surf) ? _eglLinkSurface(surf) : EGL_NO_SURFACE; + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglDestroySurface(EGLDisplay dpy, EGLSurface surface) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSurface *surf = _eglLookupSurface(surface, disp); + EGLBoolean ret = EGL_FALSE; + + _EGL_FUNC_START(disp, EGL_OBJECT_SURFACE_KHR, surf); + _EGL_CHECK_SURFACE(disp, surf, EGL_FALSE); + _eglUnlinkSurface(surf); + egl_relax (disp) { + ret = disp->Driver->DestroySurface(disp, surf); + } + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, + EGLint *value) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSurface *surf = _eglLookupSurface(surface, disp); + EGLBoolean ret; + + _EGL_FUNC_START(disp, EGL_OBJECT_SURFACE_KHR, surf); + _EGL_CHECK_SURFACE(disp, surf, EGL_FALSE); + + if (disp->Driver->QuerySurface) + ret = disp->Driver->QuerySurface(disp, surf, attribute, value); + else + ret = _eglQuerySurface(disp, surf, attribute, value); + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, + EGLint value) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSurface *surf = _eglLookupSurface(surface, disp); + EGLBoolean ret; + + _EGL_FUNC_START(disp, EGL_OBJECT_SURFACE_KHR, surf); + _EGL_CHECK_SURFACE(disp, surf, EGL_FALSE); + + ret = _eglSurfaceAttrib(disp, surf, attribute, value); + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSurface *surf = _eglLookupSurface(surface, disp); + EGLBoolean ret = EGL_FALSE; + + _EGL_FUNC_START(disp, EGL_OBJECT_SURFACE_KHR, surf); + _EGL_CHECK_SURFACE(disp, surf, EGL_FALSE); + + egl_relax (disp, &surf->Resource) { + ret = disp->Driver->BindTexImage(disp, surf, buffer); + } + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSurface *surf = _eglLookupSurface(surface, disp); + EGLBoolean ret = EGL_FALSE; + + _EGL_FUNC_START(disp, EGL_OBJECT_SURFACE_KHR, surf); + _EGL_CHECK_SURFACE(disp, surf, EGL_FALSE); + + egl_relax (disp) { + ret = disp->Driver->ReleaseTexImage(disp, surf, buffer); + } + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglSwapInterval(EGLDisplay dpy, EGLint interval) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLContext *ctx = _eglGetCurrentContext(); + _EGLSurface *surf = ctx ? ctx->DrawSurface : NULL; + EGLBoolean ret = EGL_FALSE; + + _EGL_FUNC_START(disp, EGL_OBJECT_SURFACE_KHR, surf); + _EGL_CHECK_DISPLAY(disp, EGL_FALSE); + + if (_eglGetContextHandle(ctx) == EGL_NO_CONTEXT || + ctx->Resource.Display != disp) + RETURN_EGL_ERROR(disp, EGL_BAD_CONTEXT, EGL_FALSE); + + if (_eglGetSurfaceHandle(surf) == EGL_NO_SURFACE) + RETURN_EGL_ERROR(disp, EGL_BAD_SURFACE, EGL_FALSE); + + if (surf->Type != EGL_WINDOW_BIT) + RETURN_EGL_EVAL(disp, EGL_TRUE); + + interval = CLAMP(interval, surf->Config->MinSwapInterval, + surf->Config->MaxSwapInterval); + + if (surf->SwapInterval != interval && disp->Driver->SwapInterval) { + egl_relax (disp, &surf->Resource) { + ret = disp->Driver->SwapInterval(disp, surf, interval); + } + } else { + ret = EGL_TRUE; + } + + if (ret) + surf->SwapInterval = interval; + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) +{ + _EGLContext *ctx = _eglGetCurrentContext(); + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSurface *surf = _eglLookupSurface(surface, disp); + EGLBoolean ret = EGL_FALSE; + + _EGL_FUNC_START(disp, EGL_OBJECT_SURFACE_KHR, surf); + _EGL_CHECK_SURFACE(disp, surf, EGL_FALSE); + +/* surface must be bound to current context in EGL 1.4 */ +#ifndef _EGL_BUILT_IN_DRIVER_HAIKU + if (_eglGetContextHandle(ctx) == EGL_NO_CONTEXT || surf != ctx->DrawSurface) + RETURN_EGL_ERROR(disp, EGL_BAD_SURFACE, EGL_FALSE); +#endif + + if (surf->Type != EGL_WINDOW_BIT) + RETURN_EGL_EVAL(disp, EGL_TRUE); + + /* From the EGL 1.5 spec: + * + * If eglSwapBuffers is called and the native window associated with + * surface is no longer valid, an EGL_BAD_NATIVE_WINDOW error is + * generated. + */ + if (surf->Lost) + RETURN_EGL_ERROR(disp, EGL_BAD_NATIVE_WINDOW, EGL_FALSE); + + egl_relax (disp, &surf->Resource) { + ret = disp->Driver->SwapBuffers(disp, surf); + } + + /* EGL_KHR_partial_update + * Frame boundary successfully reached, + * reset damage region and reset BufferAgeRead + */ + if (ret) { + surf->SetDamageRegionCalled = EGL_FALSE; + surf->BufferAgeRead = EGL_FALSE; + } + + RETURN_EGL_EVAL(disp, ret); +} + +static EGLBoolean +_eglSwapBuffersWithDamageCommon(_EGLDisplay *disp, _EGLSurface *surf, + const EGLint *rects, EGLint n_rects) +{ + _EGLContext *ctx = _eglGetCurrentContext(); + EGLBoolean ret = EGL_FALSE; + + _EGL_CHECK_SURFACE(disp, surf, EGL_FALSE); + + /* surface must be bound to current context in EGL 1.4 */ + if (_eglGetContextHandle(ctx) == EGL_NO_CONTEXT || surf != ctx->DrawSurface) + RETURN_EGL_ERROR(disp, EGL_BAD_SURFACE, EGL_FALSE); + + if (surf->Type != EGL_WINDOW_BIT) + RETURN_EGL_EVAL(disp, EGL_TRUE); + + if ((n_rects > 0 && rects == NULL) || n_rects < 0) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, EGL_FALSE); + + egl_relax (disp, &surf->Resource) { + ret = disp->Driver->SwapBuffersWithDamageEXT(disp, surf, rects, n_rects); + } + + /* EGL_KHR_partial_update + * Frame boundary successfully reached, + * reset damage region and reset BufferAgeRead + */ + if (ret) { + surf->SetDamageRegionCalled = EGL_FALSE; + surf->BufferAgeRead = EGL_FALSE; + } + + RETURN_EGL_EVAL(disp, ret); +} + +static EGLBoolean EGLAPIENTRY +eglSwapBuffersWithDamageEXT(EGLDisplay dpy, EGLSurface surface, + const EGLint *rects, EGLint n_rects) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSurface *surf = _eglLookupSurface(surface, disp); + _EGL_FUNC_START(disp, EGL_OBJECT_SURFACE_KHR, surf); + return _eglSwapBuffersWithDamageCommon(disp, surf, rects, n_rects); +} + +static EGLBoolean EGLAPIENTRY +eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface surface, + const EGLint *rects, EGLint n_rects) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSurface *surf = _eglLookupSurface(surface, disp); + _EGL_FUNC_START(disp, EGL_OBJECT_SURFACE_KHR, surf); + return _eglSwapBuffersWithDamageCommon(disp, surf, rects, n_rects); +} + +/** + * Clamp the rectangles so that they lie within the surface. + */ + +static void +_eglSetDamageRegionKHRClampRects(_EGLSurface *surf, EGLint *rects, + EGLint n_rects) +{ + EGLint i; + EGLint surf_height = surf->Height; + EGLint surf_width = surf->Width; + + for (i = 0; i < (4 * n_rects); i += 4) { + EGLint x1, y1, x2, y2; + x1 = rects[i]; + y1 = rects[i + 1]; + x2 = rects[i + 2] + x1; + y2 = rects[i + 3] + y1; + + rects[i] = CLAMP(x1, 0, surf_width); + rects[i + 1] = CLAMP(y1, 0, surf_height); + rects[i + 2] = CLAMP(x2, 0, surf_width) - rects[i]; + rects[i + 3] = CLAMP(y2, 0, surf_height) - rects[i + 1]; + } +} + +static EGLBoolean EGLAPIENTRY +eglSetDamageRegionKHR(EGLDisplay dpy, EGLSurface surface, EGLint *rects, + EGLint n_rects) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSurface *surf = _eglLookupSurface(surface, disp); + _EGL_FUNC_START(disp, EGL_OBJECT_SURFACE_KHR, surf); + _EGLContext *ctx = _eglGetCurrentContext(); + EGLBoolean ret; + _EGL_CHECK_SURFACE(disp, surf, EGL_FALSE); + + if (_eglGetContextHandle(ctx) == EGL_NO_CONTEXT || + surf->Type != EGL_WINDOW_BIT || ctx->DrawSurface != surf || + surf->SwapBehavior != EGL_BUFFER_DESTROYED) + RETURN_EGL_ERROR(disp, EGL_BAD_MATCH, EGL_FALSE); + + /* If the damage region is already set or + * buffer age is not queried between + * frame boundaries, throw bad access error + */ + + if (surf->SetDamageRegionCalled || !surf->BufferAgeRead) + RETURN_EGL_ERROR(disp, EGL_BAD_ACCESS, EGL_FALSE); + + _eglSetDamageRegionKHRClampRects(surf, rects, n_rects); + ret = disp->Driver->SetDamageRegion(disp, surf, rects, n_rects); + + if (ret) + surf->SetDamageRegionCalled = EGL_TRUE; + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSurface *surf = _eglLookupSurface(surface, disp); + EGLBoolean ret = EGL_FALSE; + void *native_pixmap_ptr; + + _EGL_FUNC_START(disp, EGL_OBJECT_SURFACE_KHR, surf); + STATIC_ASSERT(sizeof(void *) == sizeof(target)); + native_pixmap_ptr = (void *)target; + + _EGL_CHECK_SURFACE(disp, surf, EGL_FALSE); + if (surf->ProtectedContent) + RETURN_EGL_ERROR(disp, EGL_BAD_ACCESS, EGL_FALSE); + + egl_relax (disp, &surf->Resource) { + ret = disp->Driver->CopyBuffers(disp, surf, native_pixmap_ptr); + } + + RETURN_EGL_EVAL(disp, ret); +} + +static EGLBoolean +_eglWaitClientCommon(void) +{ + _EGLContext *ctx = _eglGetCurrentContext(); + _EGLDisplay *disp; + EGLBoolean ret = EGL_FALSE; + + if (!ctx) + RETURN_EGL_SUCCESS(NULL, EGL_TRUE); + + disp = _eglLockDisplay(ctx->Resource.Display); + + /* let bad current context imply bad current surface */ + if (_eglGetContextHandle(ctx) == EGL_NO_CONTEXT || + _eglGetSurfaceHandle(ctx->DrawSurface) == EGL_NO_SURFACE) + RETURN_EGL_ERROR(disp, EGL_BAD_CURRENT_SURFACE, EGL_FALSE); + + /* a valid current context implies an initialized current display */ + assert(disp->Initialized); + + egl_relax (disp, &ctx->Resource) { + ret = disp->Driver->WaitClient(disp, ctx); + } + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglWaitClient(void) +{ + _EGL_FUNC_START(NULL, EGL_OBJECT_CONTEXT_KHR, _eglGetCurrentContext()); + return _eglWaitClientCommon(); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglWaitGL(void) +{ + /* Since we only support OpenGL and GLES, eglWaitGL is equivalent to + * eglWaitClient. */ + _EGL_FUNC_START(NULL, EGL_OBJECT_CONTEXT_KHR, _eglGetCurrentContext()); + return _eglWaitClientCommon(); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglWaitNative(EGLint engine) +{ + _EGLContext *ctx = _eglGetCurrentContext(); + _EGLDisplay *disp; + EGLBoolean ret = EGL_FALSE; + + if (!ctx) + RETURN_EGL_SUCCESS(NULL, EGL_TRUE); + + _EGL_FUNC_START(NULL, EGL_OBJECT_THREAD_KHR, NULL); + + disp = _eglLockDisplay(ctx->Resource.Display); + + /* let bad current context imply bad current surface */ + if (_eglGetContextHandle(ctx) == EGL_NO_CONTEXT || + _eglGetSurfaceHandle(ctx->DrawSurface) == EGL_NO_SURFACE) + RETURN_EGL_ERROR(disp, EGL_BAD_CURRENT_SURFACE, EGL_FALSE); + + /* a valid current context implies an initialized current display */ + assert(disp->Initialized); + + egl_relax (disp) { + ret = disp->Driver->WaitNative(engine); + } + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLDisplay EGLAPIENTRY +eglGetCurrentDisplay(void) +{ + _EGLContext *ctx = _eglGetCurrentContext(); + EGLDisplay ret; + + ret = (ctx) ? _eglGetDisplayHandle(ctx->Resource.Display) : EGL_NO_DISPLAY; + + RETURN_EGL_SUCCESS(NULL, ret); +} + +PUBLIC EGLContext EGLAPIENTRY +eglGetCurrentContext(void) +{ + _EGLContext *ctx = _eglGetCurrentContext(); + EGLContext ret; + + ret = _eglGetContextHandle(ctx); + + RETURN_EGL_SUCCESS(NULL, ret); +} + +PUBLIC EGLSurface EGLAPIENTRY +eglGetCurrentSurface(EGLint readdraw) +{ + _EGLContext *ctx = _eglGetCurrentContext(); + EGLint err = EGL_SUCCESS; + _EGLSurface *surf; + EGLSurface ret; + + _EGL_FUNC_START(NULL, EGL_NONE, NULL); + + if (!ctx) + RETURN_EGL_SUCCESS(NULL, EGL_NO_SURFACE); + + switch (readdraw) { + case EGL_DRAW: + surf = ctx->DrawSurface; + break; + case EGL_READ: + surf = ctx->ReadSurface; + break; + default: + surf = NULL; + err = EGL_BAD_PARAMETER; + break; + } + + ret = _eglGetSurfaceHandle(surf); + + RETURN_EGL_ERROR(NULL, err, ret); +} + +PUBLIC EGLint EGLAPIENTRY +eglGetError(void) +{ + _EGLThreadInfo *t = _eglGetCurrentThread(); + EGLint e = t->LastError; + t->LastError = EGL_SUCCESS; + return e; +} + +/** + ** EGL 1.2 + **/ + +/** + * Specify the client API to use for subsequent calls including: + * eglCreateContext() + * eglGetCurrentContext() + * eglGetCurrentDisplay() + * eglGetCurrentSurface() + * eglMakeCurrent(when the ctx parameter is EGL NO CONTEXT) + * eglWaitClient() + * eglWaitNative() + * See section 3.7 "Rendering Context" in the EGL specification for details. + */ +PUBLIC EGLBoolean EGLAPIENTRY +eglBindAPI(EGLenum api) +{ + _EGLThreadInfo *t; + + _EGL_FUNC_START(NULL, EGL_OBJECT_THREAD_KHR, NULL); + + t = _eglGetCurrentThread(); + + if (!_eglIsApiValid(api)) + RETURN_EGL_ERROR(NULL, EGL_BAD_PARAMETER, EGL_FALSE); + + t->CurrentAPI = api; + + RETURN_EGL_SUCCESS(NULL, EGL_TRUE); +} + +/** + * Return the last value set with eglBindAPI(). + */ +PUBLIC EGLenum EGLAPIENTRY +eglQueryAPI(void) +{ + _EGLThreadInfo *t = _eglGetCurrentThread(); + EGLenum ret; + + /* returns one of EGL_OPENGL_API, EGL_OPENGL_ES_API or EGL_OPENVG_API */ + ret = t->CurrentAPI; + + RETURN_EGL_SUCCESS(NULL, ret); +} + +PUBLIC EGLSurface EGLAPIENTRY +eglCreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, + EGLClientBuffer buffer, EGLConfig config, + const EGLint *attrib_list) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLConfig *conf = _eglLookupConfig(config, disp); + + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + + _EGL_CHECK_CONFIG(disp, conf, EGL_NO_SURFACE); + + /* OpenVG is not supported */ + RETURN_EGL_ERROR(disp, EGL_BAD_ALLOC, EGL_NO_SURFACE); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglReleaseThread(void) +{ + /* unbind current contexts */ + _EGLThreadInfo *t = _eglGetCurrentThread(); + _EGLContext *ctx = t->CurrentContext; + + _EGL_FUNC_START(NULL, EGL_OBJECT_THREAD_KHR, NULL); + + if (ctx) { + _EGLDisplay *disp = ctx->Resource.Display; + + u_rwlock_rdlock(&disp->TerminateLock); + (void)disp->Driver->MakeCurrent(disp, NULL, NULL, NULL); + u_rwlock_rdunlock(&disp->TerminateLock); + } + + _eglDestroyCurrentThread(); + + RETURN_EGL_SUCCESS(NULL, EGL_TRUE); +} + +static EGLImage +_eglCreateImageCommon(_EGLDisplay *disp, EGLContext ctx, EGLenum target, + EGLClientBuffer buffer, const EGLint *attr_list) +{ + _EGLContext *context = _eglLookupContext(ctx, disp); + _EGLImage *img = NULL; + EGLImage ret; + + _EGL_CHECK_DISPLAY(disp, EGL_NO_IMAGE_KHR); + if (!disp->Extensions.KHR_image_base) + RETURN_EGL_EVAL(disp, EGL_NO_IMAGE_KHR); + if (!context && ctx != EGL_NO_CONTEXT) + RETURN_EGL_ERROR(disp, EGL_BAD_CONTEXT, EGL_NO_IMAGE_KHR); + /* "If is EGL_LINUX_DMA_BUF_EXT, must be a valid display, + * must be EGL_NO_CONTEXT..." + */ + if (ctx != EGL_NO_CONTEXT && target == EGL_LINUX_DMA_BUF_EXT) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR); + + egl_relax (disp, context ? &context->Resource : NULL) { + img = + disp->Driver->CreateImageKHR(disp, context, target, buffer, attr_list); + } + + ret = (img) ? _eglLinkImage(img) : EGL_NO_IMAGE_KHR; + + RETURN_EGL_EVAL(disp, ret); +} + +static EGLImage EGLAPIENTRY +eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, + EGLClientBuffer buffer, const EGLint *attr_list) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + return _eglCreateImageCommon(disp, ctx, target, buffer, attr_list); +} + +PUBLIC EGLImage EGLAPIENTRY +eglCreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, + EGLClientBuffer buffer, const EGLAttrib *attr_list) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + EGLImage image; + EGLint *int_attribs; + + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + + int_attribs = _eglConvertAttribsToInt(attr_list); + if (attr_list && !int_attribs) + RETURN_EGL_ERROR(disp, EGL_BAD_ALLOC, EGL_NO_IMAGE); + + image = _eglCreateImageCommon(disp, ctx, target, buffer, int_attribs); + free(int_attribs); + return image; +} + +static EGLBoolean +_eglDestroyImageCommon(_EGLDisplay *disp, _EGLImage *img) +{ + EGLBoolean ret; + + _EGL_CHECK_DISPLAY(disp, EGL_FALSE); + if (!disp->Extensions.KHR_image_base) + RETURN_EGL_EVAL(disp, EGL_FALSE); + if (!img) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, EGL_FALSE); + + _eglUnlinkImage(img); + ret = disp->Driver->DestroyImageKHR(disp, img); + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglDestroyImage(EGLDisplay dpy, EGLImage image) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLImage *img = _eglLookupImage(image, disp); + _EGL_FUNC_START(disp, EGL_OBJECT_IMAGE_KHR, img); + return _eglDestroyImageCommon(disp, img); +} + +static EGLBoolean EGLAPIENTRY +eglDestroyImageKHR(EGLDisplay dpy, EGLImage image) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLImage *img = _eglLookupImage(image, disp); + _EGL_FUNC_START(disp, EGL_OBJECT_IMAGE_KHR, img); + return _eglDestroyImageCommon(disp, img); +} + +static EGLSync +_eglCreateSync(_EGLDisplay *disp, EGLenum type, const EGLAttrib *attrib_list, + EGLBoolean orig_is_EGLAttrib, EGLenum invalid_type_error) +{ + _EGLContext *ctx = _eglGetCurrentContext(); + _EGLSync *sync = NULL; + EGLSync ret; + + _EGL_CHECK_DISPLAY(disp, EGL_NO_SYNC_KHR); + + if (!disp->Extensions.KHR_cl_event2 && orig_is_EGLAttrib) { + /* There exist two EGLAttrib variants of eglCreateSync*: + * eglCreateSync64KHR which requires EGL_KHR_cl_event2, and eglCreateSync + * which requires EGL 1.5. Here we use the presence of EGL_KHR_cl_event2 + * support as a proxy for EGL 1.5 support, even though that's not + * entirely correct (though _eglComputeVersion does the same). + * + * The EGL spec provides no guidance on how to handle unsupported + * functions. EGL_BAD_MATCH seems reasonable. + */ + RETURN_EGL_ERROR(disp, EGL_BAD_MATCH, EGL_NO_SYNC_KHR); + } + + /* If type is EGL_SYNC_FENCE and no context is current for the bound API + * (i.e., eglGetCurrentContext returns EGL_NO_CONTEXT ), an EGL_BAD_MATCH + * error is generated. + */ + if (!ctx && + (type == EGL_SYNC_FENCE_KHR || type == EGL_SYNC_NATIVE_FENCE_ANDROID)) + RETURN_EGL_ERROR(disp, EGL_BAD_MATCH, EGL_NO_SYNC_KHR); + + if (ctx && (ctx->Resource.Display != disp)) + RETURN_EGL_ERROR(disp, EGL_BAD_MATCH, EGL_NO_SYNC_KHR); + + switch (type) { + case EGL_SYNC_FENCE_KHR: + if (!disp->Extensions.KHR_fence_sync) + RETURN_EGL_ERROR(disp, invalid_type_error, EGL_NO_SYNC_KHR); + break; + case EGL_SYNC_REUSABLE_KHR: + if (!disp->Extensions.KHR_reusable_sync) + RETURN_EGL_ERROR(disp, invalid_type_error, EGL_NO_SYNC_KHR); + break; + case EGL_SYNC_CL_EVENT_KHR: + if (!disp->Extensions.KHR_cl_event2) + RETURN_EGL_ERROR(disp, invalid_type_error, EGL_NO_SYNC_KHR); + break; + case EGL_SYNC_NATIVE_FENCE_ANDROID: + if (!disp->Extensions.ANDROID_native_fence_sync) + RETURN_EGL_ERROR(disp, invalid_type_error, EGL_NO_SYNC_KHR); + break; + default: + RETURN_EGL_ERROR(disp, invalid_type_error, EGL_NO_SYNC_KHR); + } + + egl_relax (disp) { + sync = disp->Driver->CreateSyncKHR(disp, type, attrib_list); + } + + ret = (sync) ? _eglLinkSync(sync) : EGL_NO_SYNC_KHR; + + RETURN_EGL_EVAL(disp, ret); +} + +static EGLSync EGLAPIENTRY +eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *int_list) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + + EGLSync sync; + EGLAttrib *attrib_list; + EGLint err; + + if (sizeof(int_list[0]) == sizeof(attrib_list[0])) { + attrib_list = (EGLAttrib *)int_list; + } else { + err = _eglConvertIntsToAttribs(int_list, &attrib_list); + if (err != EGL_SUCCESS) + RETURN_EGL_ERROR(disp, err, EGL_NO_SYNC); + } + + sync = _eglCreateSync(disp, type, attrib_list, EGL_FALSE, EGL_BAD_ATTRIBUTE); + + if (sizeof(int_list[0]) != sizeof(attrib_list[0])) + free(attrib_list); + + /* Don't double-unlock the display. _eglCreateSync already unlocked it. */ + return sync; +} + +static EGLSync EGLAPIENTRY +eglCreateSync64KHR(EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + return _eglCreateSync(disp, type, attrib_list, EGL_TRUE, EGL_BAD_ATTRIBUTE); +} + +PUBLIC EGLSync EGLAPIENTRY +eglCreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + return _eglCreateSync(disp, type, attrib_list, EGL_TRUE, EGL_BAD_PARAMETER); +} + +static EGLBoolean +_eglDestroySync(_EGLDisplay *disp, _EGLSync *s) +{ + EGLBoolean ret = EGL_FALSE; + + _EGL_CHECK_SYNC(disp, s, EGL_FALSE); + assert(disp->Extensions.KHR_reusable_sync || + disp->Extensions.KHR_fence_sync || + disp->Extensions.ANDROID_native_fence_sync); + + _eglUnlinkSync(s); + + egl_relax (disp) { + ret = disp->Driver->DestroySyncKHR(disp, s); + } + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglDestroySync(EGLDisplay dpy, EGLSync sync) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSync *s = _eglLookupSync(sync, disp); + _EGL_FUNC_START(disp, EGL_OBJECT_SYNC_KHR, s); + return _eglDestroySync(disp, s); +} + +static EGLBoolean EGLAPIENTRY +eglDestroySyncKHR(EGLDisplay dpy, EGLSync sync) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSync *s = _eglLookupSync(sync, disp); + _EGL_FUNC_START(disp, EGL_OBJECT_SYNC_KHR, s); + return _eglDestroySync(disp, s); +} + +static EGLint +_eglClientWaitSyncCommon(_EGLDisplay *disp, _EGLSync *s, EGLint flags, + EGLTime timeout) +{ + EGLint ret = EGL_FALSE; + + _EGL_CHECK_SYNC(disp, s, EGL_FALSE); + assert(disp->Extensions.KHR_reusable_sync || + disp->Extensions.KHR_fence_sync || + disp->Extensions.ANDROID_native_fence_sync); + + if (s->SyncStatus == EGL_SIGNALED_KHR) + RETURN_EGL_EVAL(disp, EGL_CONDITION_SATISFIED_KHR); + + egl_relax (disp, &s->Resource) { + ret = disp->Driver->ClientWaitSyncKHR(disp, s, flags, timeout); + } + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLint EGLAPIENTRY +eglClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSync *s = _eglLookupSync(sync, disp); + _EGL_FUNC_START(disp, EGL_OBJECT_SYNC_KHR, s); + return _eglClientWaitSyncCommon(disp, s, flags, timeout); +} + +static EGLint EGLAPIENTRY +eglClientWaitSyncKHR(EGLDisplay dpy, EGLSync sync, EGLint flags, + EGLTime timeout) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSync *s = _eglLookupSync(sync, disp); + _EGL_FUNC_START(disp, EGL_OBJECT_SYNC_KHR, s); + return _eglClientWaitSyncCommon(disp, s, flags, timeout); +} + +static EGLint +_eglWaitSyncCommon(_EGLDisplay *disp, _EGLSync *s, EGLint flags) +{ + _EGLContext *ctx = _eglGetCurrentContext(); + EGLint ret = EGL_FALSE; + + _EGL_CHECK_SYNC(disp, s, EGL_FALSE); + assert(disp->Extensions.KHR_wait_sync); + + if (ctx == EGL_NO_CONTEXT) + RETURN_EGL_ERROR(disp, EGL_BAD_MATCH, EGL_FALSE); + + /* the API doesn't allow any flags yet */ + if (flags != 0) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, EGL_FALSE); + + egl_relax (disp, &s->Resource) { + ret = disp->Driver->WaitSyncKHR(disp, s); + } + + RETURN_EGL_EVAL(disp, ret); +} + +static EGLint EGLAPIENTRY +eglWaitSyncKHR(EGLDisplay dpy, EGLSync sync, EGLint flags) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSync *s = _eglLookupSync(sync, disp); + _EGL_FUNC_START(disp, EGL_OBJECT_SYNC_KHR, s); + return _eglWaitSyncCommon(disp, s, flags); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags) +{ + /* The KHR version returns EGLint, while the core version returns + * EGLBoolean. In both cases, the return values can only be EGL_FALSE and + * EGL_TRUE. + */ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSync *s = _eglLookupSync(sync, disp); + _EGL_FUNC_START(disp, EGL_OBJECT_SYNC_KHR, s); + return _eglWaitSyncCommon(disp, s, flags); +} + +static EGLBoolean EGLAPIENTRY +eglSignalSyncKHR(EGLDisplay dpy, EGLSync sync, EGLenum mode) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSync *s = _eglLookupSync(sync, disp); + EGLBoolean ret = EGL_FALSE; + + _EGL_FUNC_START(disp, EGL_OBJECT_SYNC_KHR, s); + + _EGL_CHECK_SYNC(disp, s, EGL_FALSE); + assert(disp->Extensions.KHR_reusable_sync); + + egl_relax (disp, &s->Resource) { + ret = disp->Driver->SignalSyncKHR(disp, s, mode); + } + + RETURN_EGL_EVAL(disp, ret); +} + +static EGLBoolean +_eglGetSyncAttribCommon(_EGLDisplay *disp, _EGLSync *s, EGLint attribute, + EGLAttrib *value) +{ + EGLBoolean ret; + + _EGL_CHECK_SYNC(disp, s, EGL_FALSE); + assert(disp->Extensions.KHR_reusable_sync || + disp->Extensions.KHR_fence_sync || + disp->Extensions.ANDROID_native_fence_sync); + + ret = _eglGetSyncAttrib(disp, s, attribute, value); + + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC EGLBoolean EGLAPIENTRY +eglGetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, + EGLAttrib *value) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSync *s = _eglLookupSync(sync, disp); + _EGL_FUNC_START(disp, EGL_OBJECT_SYNC_KHR, s); + + if (!value) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, EGL_FALSE); + + return _eglGetSyncAttribCommon(disp, s, attribute, value); +} + +static EGLBoolean EGLAPIENTRY +eglGetSyncAttribKHR(EGLDisplay dpy, EGLSync sync, EGLint attribute, + EGLint *value) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSync *s = _eglLookupSync(sync, disp); + EGLAttrib attrib; + EGLBoolean result; + + _EGL_FUNC_START(disp, EGL_OBJECT_SYNC_KHR, s); + + if (!value) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, EGL_FALSE); + + attrib = *value; + result = _eglGetSyncAttribCommon(disp, s, attribute, &attrib); + + /* The EGL_KHR_fence_sync spec says this about eglGetSyncAttribKHR: + * + * If any error occurs, <*value> is not modified. + */ + if (result == EGL_FALSE) + return result; + + *value = attrib; + return result; +} + +static EGLint EGLAPIENTRY +eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSync sync) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSync *s = _eglLookupSync(sync, disp); + EGLint ret = EGL_NO_NATIVE_FENCE_FD_ANDROID; + + _EGL_FUNC_START(disp, EGL_OBJECT_SYNC_KHR, s); + + /* the spec doesn't seem to specify what happens if the fence + * type is not EGL_SYNC_NATIVE_FENCE_ANDROID, but this seems + * sensible: + */ + if (!(s && (s->Type == EGL_SYNC_NATIVE_FENCE_ANDROID))) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, EGL_NO_NATIVE_FENCE_FD_ANDROID); + + _EGL_CHECK_SYNC(disp, s, EGL_NO_NATIVE_FENCE_FD_ANDROID); + assert(disp->Extensions.ANDROID_native_fence_sync); + + egl_relax (disp, &s->Resource) { + ret = disp->Driver->DupNativeFenceFDANDROID(disp, s); + } + + RETURN_EGL_SUCCESS(disp, ret); +} + +#ifdef HAVE_BIND_WL_DISPLAY +struct wl_display; + +static EGLBoolean EGLAPIENTRY +eglBindWaylandDisplayWL(EGLDisplay dpy, struct wl_display *display) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + EGLBoolean ret = EGL_FALSE; + + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + + _EGL_CHECK_DISPLAY(disp, EGL_FALSE); + assert(disp->Extensions.WL_bind_wayland_display); + + if (!display) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, EGL_FALSE); + + egl_relax (disp) { + ret = disp->Driver->BindWaylandDisplayWL(disp, display); + } + + RETURN_EGL_EVAL(disp, ret); +} + +static EGLBoolean EGLAPIENTRY +eglUnbindWaylandDisplayWL(EGLDisplay dpy, struct wl_display *display) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + EGLBoolean ret = EGL_FALSE; + + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + + _EGL_CHECK_DISPLAY(disp, EGL_FALSE); + assert(disp->Extensions.WL_bind_wayland_display); + + if (!display) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, EGL_FALSE); + + egl_relax (disp) { + ret = disp->Driver->UnbindWaylandDisplayWL(disp, display); + } + + RETURN_EGL_EVAL(disp, ret); +} + +static EGLBoolean EGLAPIENTRY +eglQueryWaylandBufferWL(EGLDisplay dpy, struct wl_resource *buffer, + EGLint attribute, EGLint *value) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + EGLBoolean ret = EGL_FALSE; + + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + + _EGL_CHECK_DISPLAY(disp, EGL_FALSE); + assert(disp->Extensions.WL_bind_wayland_display); + + if (!buffer) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, EGL_FALSE); + + egl_relax (disp) { + ret = disp->Driver->QueryWaylandBufferWL(disp, buffer, attribute, value); + } + + RETURN_EGL_EVAL(disp, ret); +} + +static struct wl_buffer *EGLAPIENTRY +eglCreateWaylandBufferFromImageWL(EGLDisplay dpy, EGLImage image) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLImage *img; + struct wl_buffer *ret; + + _EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL); + + _EGL_CHECK_DISPLAY(disp, NULL); + if (!disp->Extensions.WL_create_wayland_buffer_from_image) + RETURN_EGL_EVAL(disp, NULL); + + img = _eglLookupImage(image, disp); + + if (!img) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, NULL); + + ret = disp->Driver->CreateWaylandBufferFromImageWL(disp, img); + + RETURN_EGL_EVAL(disp, ret); +} +#endif + +static EGLBoolean EGLAPIENTRY +eglGetSyncValuesCHROMIUM(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR *ust, + EGLuint64KHR *msc, EGLuint64KHR *sbc) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSurface *surf = _eglLookupSurface(surface, disp); + EGLBoolean ret = EGL_FALSE; + + _EGL_FUNC_START(disp, EGL_OBJECT_SURFACE_KHR, surf); + + _EGL_CHECK_SURFACE(disp, surf, EGL_FALSE); + if (!disp->Extensions.CHROMIUM_sync_control) + RETURN_EGL_EVAL(disp, EGL_FALSE); + + if (!ust || !msc || !sbc) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, EGL_FALSE); + + egl_relax (disp, &surf->Resource) { + ret = disp->Driver->GetSyncValuesCHROMIUM(disp, surf, ust, msc, sbc); + } + + RETURN_EGL_EVAL(disp, ret); +} + +static EGLBoolean EGLAPIENTRY +eglGetMscRateANGLE(EGLDisplay dpy, EGLSurface surface, EGLint *numerator, + EGLint *denominator) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLSurface *surf = _eglLookupSurface(surface, disp); + EGLBoolean ret; + + _EGL_FUNC_START(disp, EGL_OBJECT_SURFACE_KHR, surf); + + _EGL_CHECK_SURFACE(disp, surf, EGL_FALSE); + if (!disp->Extensions.ANGLE_sync_control_rate) + RETURN_EGL_EVAL(disp, EGL_FALSE); + + if (!numerator || !denominator) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, EGL_FALSE); + + ret = disp->Driver->GetMscRateANGLE(disp, surf, numerator, denominator); + + RETURN_EGL_EVAL(disp, ret); +} + +static EGLBoolean EGLAPIENTRY +eglExportDMABUFImageQueryMESA(EGLDisplay dpy, EGLImage image, EGLint *fourcc, + EGLint *nplanes, EGLuint64KHR *modifiers) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLImage *img = _eglLookupImage(image, disp); + EGLBoolean ret = EGL_FALSE; + + _EGL_FUNC_START(disp, EGL_OBJECT_IMAGE_KHR, img); + + _EGL_CHECK_DISPLAY(disp, EGL_FALSE); + assert(disp->Extensions.MESA_image_dma_buf_export); + + if (!img) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, EGL_FALSE); + + egl_relax (disp, &img->Resource) { + ret = disp->Driver->ExportDMABUFImageQueryMESA(disp, img, fourcc, nplanes, + modifiers); + } + + RETURN_EGL_EVAL(disp, ret); +} + +static EGLBoolean EGLAPIENTRY +eglExportDMABUFImageMESA(EGLDisplay dpy, EGLImage image, int *fds, + EGLint *strides, EGLint *offsets) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLImage *img = _eglLookupImage(image, disp); + EGLBoolean ret = EGL_FALSE; + + _EGL_FUNC_START(disp, EGL_OBJECT_IMAGE_KHR, img); + + _EGL_CHECK_DISPLAY(disp, EGL_FALSE); + assert(disp->Extensions.MESA_image_dma_buf_export); + + if (!img) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, EGL_FALSE); + + egl_relax (disp, &img->Resource) { + ret = + disp->Driver->ExportDMABUFImageMESA(disp, img, fds, strides, offsets); + } + + RETURN_EGL_EVAL(disp, ret); +} + +static EGLint EGLAPIENTRY +eglLabelObjectKHR(EGLDisplay dpy, EGLenum objectType, EGLObjectKHR object, + EGLLabelKHR label) +{ + _EGLDisplay *disp = NULL; + _EGLResourceType type; + + _EGL_FUNC_START(NULL, EGL_NONE, NULL); + + if (objectType == EGL_OBJECT_THREAD_KHR) { + _EGLThreadInfo *t = _eglGetCurrentThread(); + + t->Label = label; + return EGL_SUCCESS; + } + + disp = _eglLockDisplay(dpy); + if (disp == NULL) + RETURN_EGL_ERROR(disp, EGL_BAD_DISPLAY, EGL_BAD_DISPLAY); + + if (objectType == EGL_OBJECT_DISPLAY_KHR) { + if (dpy != (EGLDisplay)object) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, EGL_BAD_PARAMETER); + + disp->Label = label; + RETURN_EGL_EVAL(disp, EGL_SUCCESS); + } + + switch (objectType) { + case EGL_OBJECT_CONTEXT_KHR: + type = _EGL_RESOURCE_CONTEXT; + break; + case EGL_OBJECT_SURFACE_KHR: + type = _EGL_RESOURCE_SURFACE; + break; + case EGL_OBJECT_IMAGE_KHR: + type = _EGL_RESOURCE_IMAGE; + break; + case EGL_OBJECT_SYNC_KHR: + type = _EGL_RESOURCE_SYNC; + break; + case EGL_OBJECT_STREAM_KHR: + default: + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, EGL_BAD_PARAMETER); + } + + if (_eglCheckResource(object, type, disp)) { + _EGLResource *res = (_EGLResource *)object; + + res->Label = label; + RETURN_EGL_EVAL(disp, EGL_SUCCESS); + } + + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, EGL_BAD_PARAMETER); +} + +static EGLint EGLAPIENTRY +eglDebugMessageControlKHR(EGLDEBUGPROCKHR callback, + const EGLAttrib *attrib_list) +{ + unsigned int newEnabled; + + _EGL_FUNC_START(NULL, EGL_NONE, NULL); + + simple_mtx_lock(_eglGlobal.Mutex); + + newEnabled = _eglGlobal.debugTypesEnabled; + if (attrib_list != NULL) { + int i; + + for (i = 0; attrib_list[i] != EGL_NONE; i += 2) { + switch (attrib_list[i]) { + case EGL_DEBUG_MSG_CRITICAL_KHR: + case EGL_DEBUG_MSG_ERROR_KHR: + case EGL_DEBUG_MSG_WARN_KHR: + case EGL_DEBUG_MSG_INFO_KHR: + if (attrib_list[i + 1]) + newEnabled |= DebugBitFromType(attrib_list[i]); + else + newEnabled &= ~DebugBitFromType(attrib_list[i]); + break; + default: + // On error, set the last error code, call the current + // debug callback, and return the error code. + simple_mtx_unlock(_eglGlobal.Mutex); + _eglDebugReport(EGL_BAD_ATTRIBUTE, NULL, EGL_DEBUG_MSG_ERROR_KHR, + "Invalid attribute 0x%04lx", + (unsigned long)attrib_list[i]); + return EGL_BAD_ATTRIBUTE; + } + } + } + + if (callback != NULL) { + _eglGlobal.debugCallback = callback; + _eglGlobal.debugTypesEnabled = newEnabled; + } else { + _eglGlobal.debugCallback = NULL; + _eglGlobal.debugTypesEnabled = + _EGL_DEBUG_BIT_CRITICAL | _EGL_DEBUG_BIT_ERROR; + } + + simple_mtx_unlock(_eglGlobal.Mutex); + return EGL_SUCCESS; +} + +static EGLBoolean EGLAPIENTRY +eglQueryDebugKHR(EGLint attribute, EGLAttrib *value) +{ + _EGL_FUNC_START(NULL, EGL_NONE, NULL); + + simple_mtx_lock(_eglGlobal.Mutex); + + switch (attribute) { + case EGL_DEBUG_MSG_CRITICAL_KHR: + case EGL_DEBUG_MSG_ERROR_KHR: + case EGL_DEBUG_MSG_WARN_KHR: + case EGL_DEBUG_MSG_INFO_KHR: + if (_eglGlobal.debugTypesEnabled & DebugBitFromType(attribute)) + *value = EGL_TRUE; + else + *value = EGL_FALSE; + break; + case EGL_DEBUG_CALLBACK_KHR: + *value = (EGLAttrib)_eglGlobal.debugCallback; + break; + default: + simple_mtx_unlock(_eglGlobal.Mutex); + _eglDebugReport(EGL_BAD_ATTRIBUTE, NULL, EGL_DEBUG_MSG_ERROR_KHR, + "Invalid attribute 0x%04lx", (unsigned long)attribute); + return EGL_FALSE; + } + + simple_mtx_unlock(_eglGlobal.Mutex); + return EGL_TRUE; +} + +static int +_eglFunctionCompare(const void *key, const void *elem) +{ + const char *procname = key; + const struct _egl_entrypoint *entrypoint = elem; + return strcmp(procname, entrypoint->name); +} + +static EGLBoolean EGLAPIENTRY +eglQueryDmaBufFormatsEXT(EGLDisplay dpy, EGLint max_formats, EGLint *formats, + EGLint *num_formats) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + EGLBoolean ret = EGL_FALSE; + + _EGL_FUNC_START(NULL, EGL_NONE, NULL); + + _EGL_CHECK_DISPLAY(disp, EGL_FALSE); + + egl_relax (disp) { + ret = disp->Driver->QueryDmaBufFormatsEXT(disp, max_formats, formats, + num_formats); + } + + RETURN_EGL_EVAL(disp, ret); +} + +static EGLBoolean EGLAPIENTRY +eglQueryDmaBufModifiersEXT(EGLDisplay dpy, EGLint format, EGLint max_modifiers, + EGLuint64KHR *modifiers, EGLBoolean *external_only, + EGLint *num_modifiers) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + EGLBoolean ret = EGL_FALSE; + + _EGL_FUNC_START(NULL, EGL_NONE, NULL); + + _EGL_CHECK_DISPLAY(disp, EGL_FALSE); + + egl_relax (disp) { + ret = disp->Driver->QueryDmaBufModifiersEXT( + disp, format, max_modifiers, modifiers, external_only, num_modifiers); + } + + RETURN_EGL_EVAL(disp, ret); +} + +static EGLBoolean EGLAPIENTRY +eglQuerySupportedCompressionRatesEXT(EGLDisplay dpy, EGLConfig config, + const EGLAttrib *attrib_list, + EGLint *rates, EGLint rate_size, + EGLint *num_rates) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLConfig *conf = _eglLookupConfig(config, disp); + EGLBoolean ret = EGL_FALSE; + + _EGL_FUNC_START(NULL, EGL_NONE, NULL); + + _EGL_CHECK_DISPLAY(disp, EGL_FALSE); + _EGL_CHECK_CONFIG(disp, conf, EGL_FALSE); + + egl_relax (disp) { + if (disp->Driver->QuerySupportedCompressionRatesEXT) { + ret = disp->Driver->QuerySupportedCompressionRatesEXT( + disp, conf, attrib_list, rates, rate_size, num_rates); + } else { + *num_rates = 0; + ret = EGL_TRUE; + } + } + + RETURN_EGL_EVAL(disp, ret); +} + +static void EGLAPIENTRY +eglSetBlobCacheFuncsANDROID(EGLDisplay *dpy, EGLSetBlobFuncANDROID set, + EGLGetBlobFuncANDROID get) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGL_FUNC_START(disp, EGL_NONE, NULL); + + _EGL_CHECK_DISPLAY(disp, /* void */); + + if (!set || !get) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, /* void */); + + if (disp->BlobCacheSet) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, /* void */); + + disp->BlobCacheSet = set; + disp->BlobCacheGet = get; + + disp->Driver->SetBlobCacheFuncsANDROID(disp, set, get); + + RETURN_EGL_SUCCESS(disp, /* void */); +} + +static EGLBoolean EGLAPIENTRY +eglQueryDeviceAttribEXT(EGLDeviceEXT device, EGLint attribute, EGLAttrib *value) +{ + _EGLDevice *dev = _eglLookupDevice(device); + EGLBoolean ret; + + _EGL_FUNC_START(NULL, EGL_NONE, NULL); + if (!dev) + RETURN_EGL_ERROR(NULL, EGL_BAD_DEVICE_EXT, EGL_FALSE); + + ret = _eglQueryDeviceAttribEXT(dev, attribute, value); + RETURN_EGL_EVAL(NULL, ret); +} + +static EGLBoolean EGLAPIENTRY +eglQueryDeviceBinaryEXT(EGLDeviceEXT device, + EGLint name, + EGLint max_size, + void *value, + EGLint *size) +{ + _EGLDevice *dev = _eglLookupDevice(device); + + _EGL_FUNC_START(NULL, EGL_NONE, NULL); + if (!dev) + RETURN_EGL_ERROR(NULL, EGL_BAD_DEVICE_EXT, EGL_FALSE); + + RETURN_EGL_EVAL(NULL, _eglQueryDeviceBinaryEXT(dev, name, max_size, value, size)); +} + +static const char *EGLAPIENTRY +eglQueryDeviceStringEXT(EGLDeviceEXT device, EGLint name) +{ + _EGLDevice *dev = _eglLookupDevice(device); + + _EGL_FUNC_START(NULL, EGL_NONE, NULL); + if (!dev) + RETURN_EGL_ERROR(NULL, EGL_BAD_DEVICE_EXT, NULL); + + RETURN_EGL_EVAL(NULL, _eglQueryDeviceStringEXT(dev, name)); +} + +static EGLBoolean EGLAPIENTRY +eglQueryDevicesEXT(EGLint max_devices, EGLDeviceEXT *devices, + EGLint *num_devices) +{ + EGLBoolean ret; + + _EGL_FUNC_START(NULL, EGL_NONE, NULL); + ret = _eglQueryDevicesEXT(max_devices, (_EGLDevice **)devices, num_devices); + RETURN_EGL_EVAL(NULL, ret); +} + +static EGLBoolean EGLAPIENTRY +eglQueryDisplayAttribEXT(EGLDisplay dpy, EGLint attribute, EGLAttrib *value) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + + _EGL_FUNC_START(NULL, EGL_NONE, NULL); + _EGL_CHECK_DISPLAY(disp, EGL_FALSE); + + switch (attribute) { + case EGL_DEVICE_EXT: + *value = (EGLAttrib)disp->Device; + break; + default: + RETURN_EGL_ERROR(disp, EGL_BAD_ATTRIBUTE, EGL_FALSE); + } + RETURN_EGL_SUCCESS(disp, EGL_TRUE); +} + +static char *EGLAPIENTRY +eglGetDisplayDriverConfig(EGLDisplay dpy) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + char *ret; + + _EGL_FUNC_START(disp, EGL_NONE, NULL); + _EGL_CHECK_DISPLAY(disp, NULL); + + assert(disp->Extensions.MESA_query_driver); + + ret = disp->Driver->QueryDriverConfig(disp); + RETURN_EGL_EVAL(disp, ret); +} + +static const char *EGLAPIENTRY +eglGetDisplayDriverName(EGLDisplay dpy) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + const char *ret; + + _EGL_FUNC_START(disp, EGL_NONE, NULL); + _EGL_CHECK_DISPLAY(disp, NULL); + + assert(disp->Extensions.MESA_query_driver); + + ret = disp->Driver->QueryDriverName(disp); + RETURN_EGL_EVAL(disp, ret); +} + +PUBLIC __eglMustCastToProperFunctionPointerType EGLAPIENTRY +eglGetProcAddress(const char *procname) +{ + static const struct _egl_entrypoint egl_functions[] = { +#define EGL_ENTRYPOINT(f) {.name = #f, .function = (_EGLProc)f}, +#define EGL_ENTRYPOINT2(n, f) {.name = #n, .function = (_EGLProc)f}, +#include "eglentrypoint.h" +#undef EGL_ENTRYPOINT2 +#undef EGL_ENTRYPOINT + }; + _EGLProc ret = NULL; + + if (!procname) + RETURN_EGL_SUCCESS(NULL, NULL); + + _EGL_FUNC_START(NULL, EGL_NONE, NULL); + + if (strncmp(procname, "egl", 3) == 0) { + const struct _egl_entrypoint *entrypoint = + bsearch(procname, egl_functions, ARRAY_SIZE(egl_functions), + sizeof(egl_functions[0]), _eglFunctionCompare); + if (entrypoint) + ret = entrypoint->function; + } + + if (!ret) + ret = _mesa_glapi_get_proc_address(procname); + + RETURN_EGL_SUCCESS(NULL, ret); +} + +static int +_eglLockDisplayInterop(EGLDisplay dpy, EGLContext context, _EGLDisplay **disp, + _EGLContext **ctx) +{ + + *disp = _eglLockDisplay(dpy); + if (!*disp || !(*disp)->Initialized || !(*disp)->Driver) { + if (*disp) + _eglUnlockDisplay(*disp); + return MESA_GLINTEROP_INVALID_DISPLAY; + } + + *ctx = _eglLookupContext(context, *disp); + if (!*ctx) { + _eglUnlockDisplay(*disp); + return MESA_GLINTEROP_INVALID_CONTEXT; + } + + return MESA_GLINTEROP_SUCCESS; +} + +PUBLIC int +MesaGLInteropEGLQueryDeviceInfo(EGLDisplay dpy, EGLContext context, + struct mesa_glinterop_device_info *out) +{ + _EGLDisplay *disp; + _EGLContext *ctx; + int ret; + + ret = _eglLockDisplayInterop(dpy, context, &disp, &ctx); + if (ret != MESA_GLINTEROP_SUCCESS) + return ret; + + if (disp->Driver->GLInteropQueryDeviceInfo) + ret = disp->Driver->GLInteropQueryDeviceInfo(disp, ctx, out); + else + ret = MESA_GLINTEROP_UNSUPPORTED; + + _eglUnlockDisplay(disp); + return ret; +} + +PUBLIC int +MesaGLInteropEGLExportObject(EGLDisplay dpy, EGLContext context, + struct mesa_glinterop_export_in *in, + struct mesa_glinterop_export_out *out) +{ + _EGLDisplay *disp; + _EGLContext *ctx; + int ret; + + ret = _eglLockDisplayInterop(dpy, context, &disp, &ctx); + if (ret != MESA_GLINTEROP_SUCCESS) + return ret; + + if (disp->Driver->GLInteropExportObject) + ret = disp->Driver->GLInteropExportObject(disp, ctx, in, out); + else + ret = MESA_GLINTEROP_UNSUPPORTED; + + _eglUnlockDisplay(disp); + return ret; +} + +PUBLIC int +MesaGLInteropEGLFlushObjects(EGLDisplay dpy, EGLContext context, unsigned count, + struct mesa_glinterop_export_in *objects, + struct mesa_glinterop_flush_out *out) +{ + _EGLDisplay *disp; + _EGLContext *ctx; + int ret; + + ret = _eglLockDisplayInterop(dpy, context, &disp, &ctx); + if (ret != MESA_GLINTEROP_SUCCESS) + return ret; + + if (disp->Driver->GLInteropFlushObjects) + ret = disp->Driver->GLInteropFlushObjects(disp, ctx, count, objects, out); + else + ret = MESA_GLINTEROP_UNSUPPORTED; + + _eglUnlockDisplay(disp); + return ret; +} diff --git a/local/recipes/libs/mesa/source/src/egl/main/egldisplay.c b/local/recipes/libs/mesa/source/src/egl/main/egldisplay.c new file mode 100644 index 0000000000..2029845bb1 --- /dev/null +++ b/local/recipes/libs/mesa/source/src/egl/main/egldisplay.c @@ -0,0 +1,767 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * Copyright 2009-2010 Chia-I Wu + * Copyright 2010-2011 LunarG, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * Functions related to EGLDisplay. + */ + +#include +#include +#include +#ifdef _WIN32 +#include +#else +#include +#endif +#include +#include "c11/threads.h" +#include "util/macros.h" +#include "util/os_file.h" +#include "util/os_misc.h" +#include "util/u_atomic.h" + +#include "eglcontext.h" +#include "eglcurrent.h" +#include "egldevice.h" +#include "egldisplay.h" +#include "egldriver.h" +#include "eglglobals.h" +#include "eglimage.h" +#include "egllog.h" +#include "eglsurface.h" +#include "eglsync.h" + +/* Includes for _eglNativePlatformDetectNativeDisplay */ +#ifdef HAVE_WAYLAND_PLATFORM +#include +#endif +#ifdef HAVE_DRM_PLATFORM +#include +#endif +#ifdef HAVE_WINDOWS_PLATFORM +#include +#endif + +/** + * Map build-system platform names to platform types. + */ +static const struct { + _EGLPlatformType platform; + const char *name; +} egl_platforms[] = { + {_EGL_PLATFORM_X11, "x11"}, + {_EGL_PLATFORM_XCB, "xcb"}, + {_EGL_PLATFORM_WAYLAND, "wayland"}, + {_EGL_PLATFORM_DRM, "drm"}, + {_EGL_PLATFORM_ANDROID, "android"}, + {_EGL_PLATFORM_HAIKU, "haiku"}, + {_EGL_PLATFORM_SURFACELESS, "surfaceless"}, + {_EGL_PLATFORM_DEVICE, "device"}, + {_EGL_PLATFORM_WINDOWS, "windows"}, + {_EGL_PLATFORM_REDOX, "redox"}, +}; + +/** + * Return the native platform by parsing EGL_PLATFORM. + */ +static _EGLPlatformType +_eglGetNativePlatformFromEnv(void) +{ + _EGLPlatformType plat = _EGL_INVALID_PLATFORM; + const char *plat_name; + EGLint i; + + static_assert(ARRAY_SIZE(egl_platforms) == _EGL_NUM_PLATFORMS, + "Missing platform"); + + plat_name = os_get_option("EGL_PLATFORM"); + /* try deprecated env variable */ + if (!plat_name || !plat_name[0]) + plat_name = os_get_option("EGL_DISPLAY"); + if (!plat_name || !plat_name[0]) + return _EGL_INVALID_PLATFORM; + + for (i = 0; i < ARRAY_SIZE(egl_platforms); i++) { + if (strcmp(egl_platforms[i].name, plat_name) == 0) { + plat = egl_platforms[i].platform; + break; + } + } + + if (plat == _EGL_INVALID_PLATFORM) + _eglLog(_EGL_WARNING, "invalid EGL_PLATFORM given"); + + return plat; +} + +/** + * Try detecting native platform with the help of native display characteristics. + */ +static _EGLPlatformType +_eglNativePlatformDetectNativeDisplay(void *nativeDisplay) +{ + if (nativeDisplay == EGL_DEFAULT_DISPLAY) + return _EGL_INVALID_PLATFORM; + +#ifdef HAVE_WINDOWS_PLATFORM + if (GetObjectType(nativeDisplay) == OBJ_DC) + return _EGL_PLATFORM_WINDOWS; +#endif + +#if defined(HAVE_WAYLAND_PLATFORM) || defined(HAVE_DRM_PLATFORM) + if (_eglPointerIsDereferenceable(nativeDisplay)) { + void *first_pointer = *(void **)nativeDisplay; + +#ifdef HAVE_WAYLAND_PLATFORM + /* wl_display is a wl_proxy, which is a wl_object. + * wl_object's first element points to the interfacetype. */ + if (first_pointer == &wl_display_interface) + return _EGL_PLATFORM_WAYLAND; +#endif + +#ifdef HAVE_DRM_PLATFORM + /* gbm has a pointer to its constructor as first element. */ + if (first_pointer == gbm_create_device) + return _EGL_PLATFORM_DRM; +#endif + } +#endif + + return _EGL_INVALID_PLATFORM; +} + +/** + * Return the native platform. It is the platform of the EGL native types. + */ +_EGLPlatformType +_eglGetNativePlatform(void *nativeDisplay) +{ + _EGLPlatformType detected_platform = _eglGetNativePlatformFromEnv(); + const char *detection_method = "environment"; + + if (detected_platform == _EGL_INVALID_PLATFORM) { + detected_platform = _eglNativePlatformDetectNativeDisplay(nativeDisplay); + detection_method = "autodetected"; + } + + if (detected_platform == _EGL_INVALID_PLATFORM) { + detected_platform = _EGL_NATIVE_PLATFORM; + detection_method = "build-time configuration"; + } + + _eglLog(_EGL_DEBUG, "Native platform type: %s (%s)", + egl_platforms[detected_platform].name, detection_method); + + return detected_platform; +} + +/** + * Finish display management. + */ +void +_eglFiniDisplay(void) +{ + _EGLDisplay *dispList, *disp; + + /* atexit function is called with global mutex locked */ + dispList = _eglGlobal.DisplayList; + while (dispList) { + EGLint i; + + /* pop list head */ + disp = dispList; + dispList = dispList->Next; + + for (i = 0; i < _EGL_NUM_RESOURCES; i++) { + if (disp->ResourceLists[i]) { + _eglLog(_EGL_DEBUG, "Display %p is destroyed with resources", disp); + break; + } + } + + /* The fcntl() code in _eglGetDeviceDisplay() ensures that valid fd >= 3, + * and invalid one is 0. + */ + if (disp->Options.fd) + close(disp->Options.fd); + + free(disp->Options.Attribs); + free(disp); + } + _eglGlobal.DisplayList = NULL; +} + +static EGLBoolean +_eglSameAttribs(const EGLAttrib *a, const EGLAttrib *b) +{ + size_t na = _eglNumAttribs(a); + size_t nb = _eglNumAttribs(b); + + /* different numbers of attributes must be different */ + if (na != nb) + return EGL_FALSE; + + /* both lists NULL are the same */ + if (!a && !b) + return EGL_TRUE; + + /* otherwise, compare the lists */ + return memcmp(a, b, na * sizeof(a[0])) == 0 ? EGL_TRUE : EGL_FALSE; +} + +/** + * Find the display corresponding to the specified native display, or create a + * new one. EGL 1.5 says: + * + * Multiple calls made to eglGetPlatformDisplay with the same parameters + * will return the same EGLDisplay handle. + * + * We read this extremely strictly, and treat a call with NULL attribs as + * different from a call with attribs only equal to { EGL_NONE }. Similarly + * we do not sort the attribute list, so even if all attribute _values_ are + * identical, different attribute orders will be considered different + * parameters. + */ +_EGLDisplay * +_eglFindDisplay(_EGLPlatformType plat, void *plat_dpy, + const EGLAttrib *attrib_list) +{ + _EGLDisplay *disp; + size_t num_attribs; + + if (plat == _EGL_INVALID_PLATFORM) + return NULL; + + simple_mtx_lock(_eglGlobal.Mutex); + + /* search the display list first */ + for (disp = _eglGlobal.DisplayList; disp; disp = disp->Next) { + if (disp->Platform == plat && disp->PlatformDisplay == plat_dpy && + _eglSameAttribs(disp->Options.Attribs, attrib_list)) + goto out; + } + + /* create a new display */ + assert(!disp); + disp = calloc(1, sizeof(_EGLDisplay)); + if (!disp) + goto out; + + simple_mtx_init(&disp->Mutex, mtx_plain); + u_rwlock_init(&disp->TerminateLock); + disp->Platform = plat; + disp->PlatformDisplay = plat_dpy; + num_attribs = _eglNumAttribs(attrib_list); + if (num_attribs) { + disp->Options.Attribs = calloc(num_attribs, sizeof(EGLAttrib)); + if (!disp->Options.Attribs) { + free(disp); + disp = NULL; + goto out; + } + memcpy(disp->Options.Attribs, attrib_list, + num_attribs * sizeof(EGLAttrib)); + } + + /* add to the display list */ + disp->Next = _eglGlobal.DisplayList; + _eglGlobal.DisplayList = disp; + +out: + simple_mtx_unlock(_eglGlobal.Mutex); + + return disp; +} + +/** + * Destroy the contexts and surfaces that are linked to the display. + */ +void +_eglReleaseDisplayResources(_EGLDisplay *display) +{ + _EGLResource *list; + const _EGLDriver *drv = display->Driver; + + simple_mtx_assert_locked(&display->Mutex); + + list = display->ResourceLists[_EGL_RESOURCE_CONTEXT]; + while (list) { + _EGLContext *ctx = (_EGLContext *)list; + list = list->Next; + + _eglUnlinkContext(ctx); + drv->DestroyContext(display, ctx); + } + assert(!display->ResourceLists[_EGL_RESOURCE_CONTEXT]); + + list = display->ResourceLists[_EGL_RESOURCE_SURFACE]; + while (list) { + _EGLSurface *surf = (_EGLSurface *)list; + list = list->Next; + + _eglUnlinkSurface(surf); + drv->DestroySurface(display, surf); + } + assert(!display->ResourceLists[_EGL_RESOURCE_SURFACE]); + + list = display->ResourceLists[_EGL_RESOURCE_IMAGE]; + while (list) { + _EGLImage *image = (_EGLImage *)list; + list = list->Next; + + _eglUnlinkImage(image); + drv->DestroyImageKHR(display, image); + } + assert(!display->ResourceLists[_EGL_RESOURCE_IMAGE]); + + list = display->ResourceLists[_EGL_RESOURCE_SYNC]; + while (list) { + _EGLSync *sync = (_EGLSync *)list; + list = list->Next; + + _eglUnlinkSync(sync); + drv->DestroySyncKHR(display, sync); + } + assert(!display->ResourceLists[_EGL_RESOURCE_SYNC]); +} + +/** + * Free all the data hanging of an _EGLDisplay object, but not + * the object itself. + */ +void +_eglCleanupDisplay(_EGLDisplay *disp) +{ + if (disp->Configs) { + _eglDestroyArray(disp->Configs, free); + disp->Configs = NULL; + } + + /* XXX incomplete */ +} + +/** + * Return EGL_TRUE if the given resource is valid. That is, the display does + * own the resource. + */ +EGLBoolean +_eglCheckResource(void *res, _EGLResourceType type, _EGLDisplay *disp) +{ + _EGLResource *list = disp->ResourceLists[type]; + + simple_mtx_assert_locked(&disp->Mutex); + + if (!res) + return EGL_FALSE; + + while (list) { + if (res == (void *)list) { + assert(list->Display == disp); + break; + } + list = list->Next; + } + + return (list != NULL); +} + +/** + * Initialize a display resource. The size of the subclass object is + * specified. + * + * This is supposed to be called from the initializers of subclasses, such as + * _eglInitContext or _eglInitSurface. + */ +void +_eglInitResource(_EGLResource *res, EGLint size, _EGLDisplay *disp) +{ + memset(res, 0, size); + res->Display = disp; + res->RefCount = 1; +} + +/** + * Increment reference count for the resource. + */ +void +_eglGetResource(_EGLResource *res) +{ + assert(res && res->RefCount > 0); + p_atomic_inc(&res->RefCount); +} + +/** + * Decrement reference count for the resource. + */ +EGLBoolean +_eglPutResource(_EGLResource *res) +{ + assert(res && res->RefCount > 0); + return p_atomic_dec_zero(&res->RefCount); +} + +/** + * Link a resource to its display. + */ +void +_eglLinkResource(_EGLResource *res, _EGLResourceType type) +{ + assert(res->Display); + simple_mtx_assert_locked(&res->Display->Mutex); + + res->IsLinked = EGL_TRUE; + res->Next = res->Display->ResourceLists[type]; + res->Display->ResourceLists[type] = res; + _eglGetResource(res); +} + +/** + * Unlink a linked resource from its display. + */ +void +_eglUnlinkResource(_EGLResource *res, _EGLResourceType type) +{ + _EGLResource *prev; + + simple_mtx_assert_locked(&res->Display->Mutex); + + prev = res->Display->ResourceLists[type]; + if (prev != res) { + while (prev) { + if (prev->Next == res) + break; + prev = prev->Next; + } + assert(prev); + prev->Next = res->Next; + } else { + res->Display->ResourceLists[type] = res->Next; + } + + res->Next = NULL; + res->IsLinked = EGL_FALSE; + _eglPutResource(res); + + /* We always unlink before destroy. The driver still owns a reference */ + assert(res->RefCount); +} + +#ifdef HAVE_X11_PLATFORM +_EGLDisplay * +_eglGetX11Display(Display *native_display, const EGLAttrib *attrib_list) +{ + _EGLDisplay *dpy; + _EGLDevice *dev = NULL; + + /* EGL_EXT_platform_x11 adds EGL_PLATFORM_X11_SCREEN_EXT, + * which is optional. + */ + if (attrib_list != NULL) { + for (int i = 0; attrib_list[i] != EGL_NONE; i += 2) { + EGLAttrib attrib = attrib_list[i]; + EGLAttrib value = attrib_list[i + 1]; + + switch (attrib) { + case EGL_DEVICE_EXT: + dev = _eglLookupDevice((void *)value); + if (!dev) { + _eglError(EGL_BAD_DEVICE_EXT, "eglGetPlatformDisplay"); + return NULL; + } + break; + + /* EGL_EXT_platform_x11 adds EGL_PLATFORM_X11_SCREEN_EXT, + * which is optional. + */ + case EGL_PLATFORM_X11_SCREEN_EXT: + break; + + default: + _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay"); + return NULL; + } + } + } + + dpy = _eglFindDisplay(_EGL_PLATFORM_X11, native_display, attrib_list); + if (dpy) { + dpy->Device = dev; + } + + return dpy; +} +#endif /* HAVE_X11_PLATFORM */ + +#ifdef HAVE_XCB_PLATFORM +_EGLDisplay * +_eglGetXcbDisplay(xcb_connection_t *native_display, + const EGLAttrib *attrib_list) +{ + _EGLDisplay *dpy; + _EGLDevice *dev = NULL; + + /* EGL_EXT_platform_xcb recognizes exactly one attribute, + * EGL_PLATFORM_XCB_SCREEN_EXT, which is optional. + */ + if (attrib_list != NULL) { + for (int i = 0; attrib_list[i] != EGL_NONE; i += 2) { + EGLAttrib attrib = attrib_list[i]; + EGLAttrib value = attrib_list[i + 1]; + + switch (attrib) { + case EGL_DEVICE_EXT: + dev = _eglLookupDevice((void *)value); + if (!dev) { + _eglError(EGL_BAD_DEVICE_EXT, "eglGetPlatformDisplay"); + return NULL; + } + break; + + case EGL_PLATFORM_XCB_SCREEN_EXT: + break; + + default: + _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay"); + return NULL; + } + } + } + + dpy = _eglFindDisplay(_EGL_PLATFORM_XCB, native_display, attrib_list); + if (dpy) { + dpy->Device = dev; + } + + return dpy; +} +#endif /* HAVE_XCB_PLATFORM */ + +#ifdef HAVE_DRM_PLATFORM +_EGLDisplay * +_eglGetGbmDisplay(struct gbm_device *native_display, + const EGLAttrib *attrib_list) +{ + _EGLDisplay *dpy; + _EGLDevice *dev = NULL; + + /* This platform recognizes only EXT_explicit_device */ + if (attrib_list) { + for (int i = 0; attrib_list[i] != EGL_NONE; i += 2) { + EGLAttrib attrib = attrib_list[i]; + EGLAttrib value = attrib_list[i + 1]; + + switch (attrib) { + case EGL_DEVICE_EXT: + dev = _eglLookupDevice((void *)value); + if (!dev) { + _eglError(EGL_BAD_DEVICE_EXT, "eglGetPlatformDisplay"); + return NULL; + } + break; + + default: + _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay"); + return NULL; + } + } + } + + dpy = _eglFindDisplay(_EGL_PLATFORM_DRM, native_display, attrib_list); + if (dpy) { + dpy->Device = dev; + } + + return dpy; +} +#endif /* HAVE_DRM_PLATFORM */ + +#ifdef HAVE_WAYLAND_PLATFORM +_EGLDisplay * +_eglGetWaylandDisplay(struct wl_display *native_display, + const EGLAttrib *attrib_list) +{ + _EGLDisplay *dpy; + _EGLDevice *dev = NULL; + + /* This platform recognizes only EXT_explicit_device */ + if (attrib_list) { + for (int i = 0; attrib_list[i] != EGL_NONE; i += 2) { + EGLAttrib attrib = attrib_list[i]; + EGLAttrib value = attrib_list[i + 1]; + + switch (attrib) { + case EGL_DEVICE_EXT: + dev = _eglLookupDevice((void *)value); + if (!dev) { + _eglError(EGL_BAD_DEVICE_EXT, "eglGetPlatformDisplay"); + return NULL; + } + break; + + default: + _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay"); + return NULL; + } + } + } + + dpy = _eglFindDisplay(_EGL_PLATFORM_WAYLAND, native_display, attrib_list); + if (dpy) { + dpy->Device = dev; + } + + return dpy; +} +#endif /* HAVE_WAYLAND_PLATFORM */ + +_EGLDisplay * +_eglGetSurfacelessDisplay(void *native_display, const EGLAttrib *attrib_list) +{ + _EGLDisplay *dpy; + _EGLDevice *dev = NULL; + + /* Any native display must be an EGLDeviceEXT we know about */ + if (native_display != NULL) { + _eglError(EGL_BAD_PARAMETER, "eglGetPlatformDisplay"); + return NULL; + } + + /* This platform recognizes only EXT_explicit_device */ + if (attrib_list) { + for (int i = 0; attrib_list[i] != EGL_NONE; i += 2) { + EGLAttrib attrib = attrib_list[i]; + EGLAttrib value = attrib_list[i + 1]; + + switch (attrib) { + case EGL_DEVICE_EXT: + dev = _eglLookupDevice((void *)value); + if (!dev) { + _eglError(EGL_BAD_DEVICE_EXT, "eglGetPlatformDisplay"); + return NULL; + } + break; + + default: + _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay"); + return NULL; + } + } + } + + dpy = _eglFindDisplay(_EGL_PLATFORM_SURFACELESS, NULL, attrib_list); + if (dpy) { + dpy->Device = dev; + } + + return dpy; +} + +#ifdef HAVE_ANDROID_PLATFORM +_EGLDisplay * +_eglGetAndroidDisplay(void *native_display, const EGLAttrib *attrib_list) +{ + + /* This platform recognizes no display attributes. */ + if (attrib_list != NULL && attrib_list[0] != EGL_NONE) { + _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay"); + return NULL; + } + + return _eglFindDisplay(_EGL_PLATFORM_ANDROID, native_display, attrib_list); +} +#endif /* HAVE_ANDROID_PLATFORM */ + +#ifdef HAVE_REDOX_PLATFORM +_EGLDisplay * +_eglGetRedoxDisplay(void *native_display, const EGLAttrib *attrib_list) +{ + /* Redox has no real display surface; native_display is + * always NULL. The platform probes /scheme/drm/card0 in + * dri2_initialize_redox() and falls back to swrast. + */ + (void) native_display; + return _eglFindDisplay(_EGL_PLATFORM_REDOX, NULL, attrib_list); +} +#endif /* HAVE_REDOX_PLATFORM */ + +_EGLDisplay * +_eglGetDeviceDisplay(void *native_display, const EGLAttrib *attrib_list) +{ + _EGLDevice *dev; + _EGLDisplay *display; + int fd = -1; + + dev = _eglLookupDevice(native_display); + if (!dev) { + _eglError(EGL_BAD_PARAMETER, "eglGetPlatformDisplay"); + return NULL; + } + + if (attrib_list) { + for (int i = 0; attrib_list[i] != EGL_NONE; i += 2) { + EGLAttrib attrib = attrib_list[i]; + EGLAttrib value = attrib_list[i + 1]; + + /* EGL_EXT_platform_device does not recognize any attributes, + * EGL_EXT_device_drm adds the optional EGL_DRM_MASTER_FD_EXT. + */ + + if (!_eglDeviceSupports(dev, _EGL_DEVICE_DRM) || + attrib != EGL_DRM_MASTER_FD_EXT) { + _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay"); + return NULL; + } + + fd = (int)value; + } + } + + display = _eglFindDisplay(_EGL_PLATFORM_DEVICE, native_display, attrib_list); + if (!display) { + _eglError(EGL_BAD_ALLOC, "eglGetPlatformDisplay"); + return NULL; + } + + /* If the fd is explicitly provided and we did not dup() it yet, do so. + * The spec mandates that we do so, since we'll need it past the + * eglGetPlatformDisplay call. + * + * The new fd is guaranteed to be 3 or greater. + */ + if (fd != -1 && display->Options.fd == 0) { + display->Options.fd = os_dupfd_cloexec(fd); + if (display->Options.fd == -1) { + /* Do not (really) need to teardown the display */ + _eglError(EGL_BAD_ALLOC, "eglGetPlatformDisplay"); + return NULL; + } + } + + return display; +} diff --git a/local/recipes/libs/mesa/source/src/egl/main/egldisplay.h b/local/recipes/libs/mesa/source/src/egl/main/egldisplay.h new file mode 100644 index 0000000000..601a2f4b3a --- /dev/null +++ b/local/recipes/libs/mesa/source/src/egl/main/egldisplay.h @@ -0,0 +1,367 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * Copyright 2009-2010 Chia-I Wu + * Copyright 2010-2011 LunarG, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef EGLDISPLAY_INCLUDED +#define EGLDISPLAY_INCLUDED + +#include "util/rwlock.h" +#include "util/simple_mtx.h" + +#include "eglarray.h" +#include "egldefines.h" +#include "egltypedefs.h" + +#ifdef HAVE_X11_PLATFORM +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +enum _egl_platform_type { + _EGL_PLATFORM_X11, + _EGL_PLATFORM_XCB, + _EGL_PLATFORM_WAYLAND, + _EGL_PLATFORM_DRM, + _EGL_PLATFORM_ANDROID, + _EGL_PLATFORM_HAIKU, + _EGL_PLATFORM_SURFACELESS, + _EGL_PLATFORM_DEVICE, + _EGL_PLATFORM_WINDOWS, + _EGL_PLATFORM_REDOX, + + _EGL_NUM_PLATFORMS, + _EGL_INVALID_PLATFORM = -1 +}; +typedef enum _egl_platform_type _EGLPlatformType; + +enum _egl_resource_type { + _EGL_RESOURCE_CONTEXT, + _EGL_RESOURCE_SURFACE, + _EGL_RESOURCE_IMAGE, + _EGL_RESOURCE_SYNC, + + _EGL_NUM_RESOURCES +}; +/* this cannot and need not go into egltypedefs.h */ +typedef enum _egl_resource_type _EGLResourceType; + +/** + * A resource of a display. + */ +struct _egl_resource { + /* which display the resource belongs to */ + _EGLDisplay *Display; + EGLBoolean IsLinked; + EGLint RefCount; + + EGLLabelKHR Label; + + /* used to link resources of the same type */ + _EGLResource *Next; +}; + +/** + * Optional EGL extensions info. + */ +struct _egl_extensions { + /* Please keep these sorted alphabetically. */ + EGLBoolean ANDROID_blob_cache; + EGLBoolean ANDROID_framebuffer_target; + EGLBoolean ANDROID_image_native_buffer; + EGLBoolean ANDROID_native_fence_sync; + EGLBoolean ANDROID_recordable; + + EGLBoolean ANGLE_sync_control_rate; + EGLBoolean CHROMIUM_sync_control; + + EGLBoolean EXT_buffer_age; + EGLBoolean EXT_config_select_group; + EGLBoolean EXT_create_context_robustness; + EGLBoolean EXT_image_dma_buf_import; + EGLBoolean EXT_image_dma_buf_import_modifiers; + EGLBoolean EXT_pixel_format_float; + EGLBoolean EXT_present_opaque; + EGLBoolean EXT_protected_content; + EGLBoolean EXT_protected_surface; + EGLBoolean EXT_query_reset_notification_strategy; + EGLBoolean EXT_surface_compression; + EGLBoolean EXT_surface_CTA861_3_metadata; + EGLBoolean EXT_surface_SMPTE2086_metadata; + EGLBoolean EXT_swap_buffers_with_damage; + + unsigned int IMG_context_priority; +#define __EGL_CONTEXT_PRIORITY_LOW_BIT 0 +#define __EGL_CONTEXT_PRIORITY_MEDIUM_BIT 1 +#define __EGL_CONTEXT_PRIORITY_HIGH_BIT 2 +#define __EGL_CONTEXT_PRIORITY_REALTIME_BIT 3 + + EGLBoolean KHR_cl_event2; + EGLBoolean KHR_config_attribs; + EGLBoolean KHR_context_flush_control; + EGLBoolean KHR_create_context; + EGLBoolean KHR_create_context_no_error; + EGLBoolean KHR_fence_sync; + EGLBoolean KHR_get_all_proc_addresses; + EGLBoolean KHR_gl_colorspace; + EGLBoolean KHR_gl_renderbuffer_image; + EGLBoolean KHR_gl_texture_2D_image; + EGLBoolean KHR_gl_texture_3D_image; + EGLBoolean KHR_gl_texture_cubemap_image; + EGLBoolean KHR_image; + EGLBoolean KHR_image_base; + EGLBoolean KHR_image_pixmap; + EGLBoolean KHR_mutable_render_buffer; + EGLBoolean KHR_no_config_context; + EGLBoolean KHR_partial_update; + EGLBoolean KHR_reusable_sync; + EGLBoolean KHR_surfaceless_context; + EGLBoolean KHR_wait_sync; + + EGLBoolean MESA_gl_interop; + EGLBoolean MESA_image_dma_buf_export; + EGLBoolean MESA_query_driver; + EGLBoolean MESA_x11_native_visual_id; + + EGLBoolean NOK_texture_from_pixmap; + + EGLBoolean NV_context_priority_realtime; + + EGLBoolean WL_bind_wayland_display; + EGLBoolean WL_create_wayland_buffer_from_image; +}; + +struct _egl_display { + /* used to link displays */ + _EGLDisplay *Next; + + /** + * The big-display-lock (BDL) which protects our internal state. EGL + * drivers should use their own locking, as needed, to protect their + * own state, rather than relying on this. + */ + simple_mtx_t Mutex; + + /** + * The spec appears to allow eglTerminate() to race with more or less + * any other egl call. To allow for this, while relaxing the BDL to + * allow other egl calls to happen in parallel, a rwlock is used. All + * points where the BDL lock is acquired also acquire TerminateLock + * for reading, while eglTerminate() itself acquires the TerminateLock + * for writing. + * + * Note, we could conceivably just replace the BDL with a single + * rwlock. But there are a couple shortcomings of u_rwlock: + * + * 1) The WIN32 implementation does not allow promoting a read- + * lock to write-lock, nor recursive locking, whereas the + * pthread based implementation does. Because of this, it + * would be difficult to keep the eglapi layer portable if + * we depended on any less-than-trivial rwlock usage. + * + * 2) We'd lose simple_mtx_assert_locked(). + */ + struct u_rwlock TerminateLock; + + _EGLPlatformType Platform; /**< The type of the platform display */ + void *PlatformDisplay; /**< A pointer to the platform display */ + + _EGLDevice *Device; /**< Device backing the display */ + const _EGLDriver *Driver; /**< Matched driver of the display */ + EGLBoolean Initialized; /**< True if the display is initialized */ + + /* options that affect how the driver initializes the display */ + struct { + EGLBoolean Zink; /**< Use kopper only */ + EGLBoolean ForceSoftware; /**< Use software path only */ + EGLBoolean GalliumHudWarn; /**< Using hud, warn when querying buffer age */ + EGLAttrib *Attribs; /**< Platform-specific options */ + int fd; /**< Platform device specific, local fd */ + } Options; + + /* these fields are set by the driver during init */ + void *DriverData; /**< Driver private data */ + EGLint Version; /**< EGL version major*10+minor */ + EGLint ClientAPIs; /**< Bitmask of APIs supported (EGL_xxx_BIT) */ + _EGLExtensions Extensions; /**< Extensions supported */ + EGLBoolean RobustBufferAccess; /**< Supports robust buffer access behavior */ + + /* these fields are derived from above */ + char VersionString[100]; /**< EGL_VERSION */ + char ClientAPIsString[100]; /**< EGL_CLIENT_APIS */ + char ExtensionsString[_EGL_MAX_EXTENSIONS_LEN]; /**< EGL_EXTENSIONS */ + + _EGLArray *Configs; + + /* lists of resources */ + _EGLResource *ResourceLists[_EGL_NUM_RESOURCES]; + + EGLLabelKHR Label; + + EGLSetBlobFuncANDROID BlobCacheSet; + EGLGetBlobFuncANDROID BlobCacheGet; +}; + +extern _EGLDisplay * +_eglLockDisplay(EGLDisplay dpy); + +extern void +_eglUnlockDisplay(_EGLDisplay *disp); + +extern _EGLPlatformType +_eglGetNativePlatform(void *nativeDisplay); + +extern void +_eglFiniDisplay(void); + +extern _EGLDisplay * +_eglFindDisplay(_EGLPlatformType plat, void *plat_dpy, const EGLAttrib *attr); + +extern void +_eglReleaseDisplayResources(_EGLDisplay *disp); + +extern void +_eglCleanupDisplay(_EGLDisplay *disp); + +extern EGLBoolean +_eglCheckResource(void *res, _EGLResourceType type, _EGLDisplay *disp); + +/** + * Return the handle of a linked display, or EGL_NO_DISPLAY. + */ +static inline EGLDisplay +_eglGetDisplayHandle(_EGLDisplay *disp) +{ + return (EGLDisplay)((disp) ? disp : EGL_NO_DISPLAY); +} + +static inline EGLBoolean +_eglHasAttrib(_EGLDisplay *disp, EGLAttrib attrib) +{ + EGLAttrib *attribs = disp->Options.Attribs; + + if (!attribs) { + return EGL_FALSE; + } + + for (int i = 0; attribs[i] != EGL_NONE; i += 2) { + if (attrib == attribs[i]) { + return EGL_TRUE; + } + } + return EGL_FALSE; +} + +extern void +_eglInitResource(_EGLResource *res, EGLint size, _EGLDisplay *disp); + +extern void +_eglGetResource(_EGLResource *res); + +extern EGLBoolean +_eglPutResource(_EGLResource *res); + +extern void +_eglLinkResource(_EGLResource *res, _EGLResourceType type); + +extern void +_eglUnlinkResource(_EGLResource *res, _EGLResourceType type); + +/** + * Return true if the resource is linked. + */ +static inline EGLBoolean +_eglIsResourceLinked(_EGLResource *res) +{ + return res->IsLinked; +} + +static inline size_t +_eglNumAttribs(const EGLAttrib *attribs) +{ + size_t len = 0; + + if (attribs) { + while (attribs[len] != EGL_NONE) + len += 2; + len++; + } + return len; +} + +#ifdef HAVE_X11_PLATFORM +_EGLDisplay * +_eglGetX11Display(Display *native_display, const EGLAttrib *attrib_list); +#endif + +#ifdef HAVE_XCB_PLATFORM +typedef struct xcb_connection_t xcb_connection_t; +_EGLDisplay * +_eglGetXcbDisplay(xcb_connection_t *native_display, + const EGLAttrib *attrib_list); +#endif + +#ifdef HAVE_DRM_PLATFORM +struct gbm_device; + +_EGLDisplay * +_eglGetGbmDisplay(struct gbm_device *native_display, + const EGLAttrib *attrib_list); +#endif + +#ifdef HAVE_WAYLAND_PLATFORM +struct wl_display; + +_EGLDisplay * +_eglGetWaylandDisplay(struct wl_display *native_display, + const EGLAttrib *attrib_list); +#endif + +_EGLDisplay * +_eglGetSurfacelessDisplay(void *native_display, const EGLAttrib *attrib_list); + +#ifdef HAVE_ANDROID_PLATFORM +_EGLDisplay * +_eglGetAndroidDisplay(void *native_display, const EGLAttrib *attrib_list); +#endif + +#ifdef HAVE_REDOX_PLATFORM +_EGLDisplay * +_eglGetRedoxDisplay(void *native_display, const EGLAttrib *attrib_list); +#endif + +_EGLDisplay * +_eglGetDeviceDisplay(void *native_display, const EGLAttrib *attrib_list); + +#ifdef __cplusplus +} +#endif + +#endif /* EGLDISPLAY_INCLUDED */ diff --git a/local/recipes/libs/mesa/source/src/egl/meson.build b/local/recipes/libs/mesa/source/src/egl/meson.build new file mode 100644 index 0000000000..658de8e6cf --- /dev/null +++ b/local/recipes/libs/mesa/source/src/egl/meson.build @@ -0,0 +1,276 @@ +# Copyright © 2017-2019 Intel Corporation +# SPDX-License-Identifier: MIT + +inc_egl = include_directories('.', 'main') + +c_args_for_egl = [asan_c_args] +cpp_args_for_egl = [] +link_args_for_egl = [] +link_deps_for_egl = [] +link_for_egl = [] +if with_dri + link_for_egl += libgallium_dri +endif +if with_platform_windows + link_for_egl += libgallium_wgl +endif +deps_for_egl = [] +incs_for_egl = [inc_include, inc_src, inc_egl] + +files_egl = files( + 'main/eglapi.c', + 'main/eglarray.c', + 'main/eglarray.h', + 'main/eglconfigdebug.c', + 'main/eglconfigdebug.h', + 'main/eglconfig.c', + 'main/eglconfig.h', + 'main/eglcontext.c', + 'main/eglcontext.h', + 'main/eglcurrent.c', + 'main/eglcurrent.h', + 'main/egldefines.h', + 'main/egldevice.c', + 'main/egldevice.h', + 'main/egldisplay.c', + 'main/egldisplay.h', + 'main/egldriver.h', + 'main/eglglobals.c', + 'main/eglglobals.h', + 'main/eglimage.c', + 'main/eglimage.h', + 'main/egllog.c', + 'main/egllog.h', + 'main/eglsurface.c', + 'main/eglsurface.h', + 'main/eglsync.c', + 'main/eglsync.h', + 'main/eglentrypoint.h', + 'main/egltypedefs.h', +) + +# Remove the argument parsing from gen_egl_dispatch.py when removing this +# option +gen_args = [] +if with_wayland_bind_display + gen_args += [ '--bind-wl-display' ] +endif + +g_egldispatchstubs_c = custom_target( + 'g_egldispatchstubs.c', + input : [ + 'generate/gen_egl_dispatch.py', + 'generate/egl.xml', 'generate/egl_other.xml' + ], + output : 'g_egldispatchstubs.c', + command : [ + prog_python, '@INPUT0@', gen_args, 'source', '@INPUT1@', '@INPUT2@' + ], + depend_files : [ 'generate/eglFunctionList.py', glapi_xml_py_deps], + capture : true, +) + +g_egldispatchstubs_h = custom_target( + 'g_egldispatchstubs.h', + input : [ + 'generate/gen_egl_dispatch.py', + 'generate/egl.xml', 'generate/egl_other.xml' + ], + output : 'g_egldispatchstubs.h', + command : [ + prog_python, '@INPUT0@', gen_args, 'header', '@INPUT1@', '@INPUT2@' + ], + depend_files : [ 'generate/eglFunctionList.py', glapi_xml_py_deps], + capture : true, +) + +if with_dri + files_egl += files( + 'drivers/dri2/egl_dri2.c', + 'drivers/dri2/egl_dri2.h', + ) + files_egl += sha1_h + files_egl += main_dispatch_h + deps_for_egl += idep_xmlconfig + link_for_egl += libloader + incs_for_egl += inc_loader + incs_for_egl += inc_gallium + incs_for_egl += inc_gallium_aux + incs_for_egl += inc_mesa + incs_for_egl += inc_st_dri + + files_egl += files( + 'drivers/dri2/platform_device.c', + 'drivers/dri2/platform_surfaceless.c', + ) + + if with_platform_x11 + files_egl += files('drivers/dri2/platform_x11.c') + incs_for_egl += inc_loader_x11 + link_for_egl += libloader_x11 + if with_dri_platform == 'drm' or with_dri_platform == 'pseudo-drm' + files_egl += files('drivers/dri2/platform_x11_dri3.c') + endif + deps_for_egl += [dep_x11_xcb, dep_xcb_xrandr, dep_xcb_xfixes, dep_xcb_shm] + endif + if with_platform_redox + files_egl += files('drivers/dri2/platform_redox.c') + c_args_for_egl += ['-DHAVE_REDOX_PLATFORM'] + endif + if with_gbm and not with_platform_android + files_egl += files('drivers/dri2/platform_drm.c') + incs_for_egl += [include_directories('../gbm/backends/dri')] + deps_for_egl += [dep_libdrm, dep_gbm] + endif + if with_platform_wayland + deps_for_egl += [dep_wayland_client, dep_wayland_server, dep_wayland_egl_headers] + link_for_egl += libloader_wayland_helper + files_egl += files('drivers/dri2/platform_wayland.c') + files_egl += wp_files['linux-dmabuf-unstable-v1'] + files_egl += wp_files['presentation-time'] + if with_wayland_bind_display + files_egl += [wayland_drm_client_protocol_h] + link_for_egl += [libwayland_drm] + incs_for_egl += include_directories('wayland/wayland-drm') + c_args_for_egl += [ + '-DHAVE_BIND_WL_DISPLAY' + ] + endif + endif + if with_platform_android + deps_for_egl += [dep_android, idep_u_gralloc] + files_egl += files('drivers/dri2/platform_android.c') + endif +elif with_platform_haiku + c_args_for_egl += [ + '-D_EGL_BUILT_IN_DRIVER_HAIKU', + ] + files_egl += files('drivers/haiku/egl_haiku.cpp') + + incs_for_egl += [inc_gallium, inc_gallium_aux, inc_gallium_drivers, inc_gallium_winsys, inc_gallium_winsys_sw] + incs_for_egl += [inc_mesa] + incs_for_egl += [include_directories('../gallium/frontends/hgl')] + + link_for_egl += [libmesa, libgallium, libswhgl, libsthgl, libglapi] + deps_for_egl += [cpp.find_library('be'), driver_swrast] +elif with_platform_windows + c_args_for_egl += [ + '-DEGLAPI=', '-DPUBLIC=' + ] + files_egl += files('drivers/wgl/egl_wgl.c') + files_egl += main_dispatch_h + incs_for_egl += [inc_wgl, inc_gallium, inc_gallium_aux, inc_mesa] + link_for_egl += libgallium_wgl + +else + error('No EGL driver available.') +endif + +if cc.has_function('mincore') + c_args_for_egl += '-DHAVE_MINCORE' +endif + +if not with_glvnd + egl_lib_name = 'EGL' + get_option('egl-lib-suffix') + egl_lib_version = '1.0.0' + egl_lib_soversion = host_machine.system() == 'windows' ? '' : '1' +else + egl_lib_name = 'EGL_@0@'.format(glvnd_vendor_name) + egl_lib_version = '0.0.0' + egl_lib_soversion = '0' + deps_for_egl += dep_glvnd + files_egl += [g_egldispatchstubs_h, g_egldispatchstubs_c] + files_egl += files('main/eglglvnd.c', 'main/egldispatchstubs.c') +endif + +if with_ld_version_script + if with_glvnd + link_args_for_egl += [ + '-Wl,--version-script', join_paths(meson.current_source_dir(), 'egl-glvnd.sym') + ] + link_deps_for_egl += files('egl-glvnd.sym') + else + link_args_for_egl += [ + '-Wl,--version-script', join_paths(meson.current_source_dir(), 'egl.sym') + ] + link_deps_for_egl += files('egl.sym') + endif +endif + +egl_def = custom_target( + 'egl.def', + input: 'main/egl.def.in', + output : 'egl.def', + command : gen_vs_module_defs_normal_command, +) + +libegl = shared_library( + egl_lib_name, + files_egl, + c_args : [ + c_args_for_egl, + '-D_EGL_NATIVE_PLATFORM=_EGL_PLATFORM_@0@'.format(egl_native_platform.to_upper()), + ], + cpp_args : [cpp_args_for_egl], + gnu_symbol_visibility : 'hidden', + include_directories : incs_for_egl, + link_with : [link_for_egl], + link_args : [ld_args_bsymbolic, ld_args_gc_sections, link_args_for_egl], + link_depends : [link_deps_for_egl], + dependencies : [deps_for_egl, dep_dl, dep_libdrm, dep_clock, dep_thread, idep_mesautil], + install : true, + version : egl_lib_version, + soversion : egl_lib_soversion, + name_prefix : host_machine.system() == 'windows' ? 'lib' : [], # always use lib, but avoid warnings on !windows + vs_module_defs : egl_def +) + +if with_glvnd + configure_file( + configuration: {'library_path' : 'libEGL_@0@.so.0'.format(glvnd_vendor_name)}, + input : 'main/50_mesa.json', + output: '50_@0@.json'.format(glvnd_vendor_name), + install : true, + install_tag : 'runtime', + install_dir : join_paths(get_option('datadir'), 'glvnd', 'egl_vendor.d') + ) + + devenv_glvnd_egl_json_file = configure_file( + configuration: {'library_path' : libegl.full_path()}, + input : 'main/50_mesa.json', + output: 'devenv_glvnd_egl.json', + ) + devenv.set('__EGL_VENDOR_LIBRARY_FILENAMES', devenv_glvnd_egl_json_file.full_path()) +else + pkg.generate( + name : 'egl', + description : 'Mesa EGL Library', + version : meson.project_version(), + libraries : libegl, + libraries_private: gl_priv_libs, + requires_private : gl_priv_reqs, + extra_cflags : gl_pkgconfig_c_flags, + ) +endif + +if with_symbols_check + if with_glvnd + egl_symbols = files('egl-glvnd-symbols.txt') + else + egl_symbols = files('egl-symbols.txt') + endif + test('egl-symbols-check', + symbols_check, + args : [ + '--lib', libegl, + '--symbols-file', egl_symbols, + symbols_check_args, + ], + suite : ['egl'], + ) + test('egl-entrypoint-check', + prog_python, + args : files('egl-entrypoint-check.py', 'main/eglentrypoint.h'), + suite : ['egl'], + ) +endif diff --git a/local/scripts/build-redbear.sh b/local/scripts/build-redbear.sh index 4005c79e73..a8fd8bc372 100755 --- a/local/scripts/build-redbear.sh +++ b/local/scripts/build-redbear.sh @@ -739,8 +739,36 @@ if [ "$NO_CACHE" != "1" ]; then *) return 1 ;; esac } + # ── Config-scoped ABI invalidation ────────────────────────────────── + # repo/ is shared across configs, so a `redbear-mini` build must NOT nuke + # packages that only `redbear-full` builds (Qt, mesa, sddm, ...) just because + # their pkgars sit in the repo older than relibc.pkgar. Resolve the closure + # of the config being built and only ever invalidate packages inside it. + # No env flag needed — you named the config, so we know it's mini not full. + declare -A _config_pkg_set + _config_scoped=0 + _cfg_file="$PROJECT_ROOT/config/$CONFIG.toml" + if [ -f "$_cfg_file" ]; then + while read -r _p; do + [ -n "$_p" ] && _config_pkg_set["$_p"]=1 + done < <(timeout 120 "$PROJECT_ROOT/target/release/repo" push-tree \ + --filesystem="$_cfg_file" 2>/dev/null \ + | sed -E 's/^[^A-Za-z0-9_]+//' | awk '{print $1}' | grep -E '^[A-Za-z0-9]') + [ "${#_config_pkg_set[@]}" -gt 0 ] && _config_scoped=1 + fi + if [ "$_config_scoped" = "1" ]; then + echo ">>> ABI-staleness scoped to $CONFIG (${#_config_pkg_set[@]} pkgs); other configs' repo packages untouched." + else + echo ">>> NOTE: could not resolve $CONFIG package closure; ABI sweep is repo-wide (fallback)." + fi + _pkg_in_config() { + # true unless we have a config closure and pkg is outside it + [ "$_config_scoped" != "1" ] && return 0 + [ -n "${_config_pkg_set[$1]:-}" ] + } invalidate_consumer() { local pkg="$1" + _pkg_in_config "$pkg" || return 0 rm -f "$REPO_DIR/$pkg".* find "$PROJECT_ROOT/recipes" "$PROJECT_ROOT/local/recipes" \ -path "*/$pkg/target" -type d -exec rm -rf {} + 2>/dev/null || true @@ -762,6 +790,7 @@ if [ "$NO_CACHE" != "1" ]; then for _pkgar in "$REPO_DIR"/*.pkgar; do [ -f "$_pkgar" ] || continue _pkg="$(basename "$_pkgar" .pkgar)" + _pkg_in_config "$_pkg" || continue if _rb_preserve_toolchain "$_pkg"; then echo ">>> preserving ABI-inert toolchain pkg '$_pkg' (consumer relinks;" \ "set REDBEAR_FORCE_TOOLCHAIN_RECOOK=1 to force a full re-cook)." @@ -775,6 +804,7 @@ if [ "$NO_CACHE" != "1" ]; then [ -f "$_pkgar" ] || continue _pkg="$(basename "$_pkgar" .pkgar)" [ "$_pkg" = "relibc" ] && continue + _pkg_in_config "$_pkg" || continue _rb_preserve_toolchain "$_pkg" && continue _pkg_ts=$(stat -c %Y "$_pkgar" 2>/dev/null || echo "0") if [ "$relibc_ts" != "0" ] && [ "$relibc_ts" -gt "$_pkg_ts" ]; then