├── LICENSE ├── README.md ├── meson.build ├── meson_options.txt └── src ├── base ├── config-list.c ├── config-list.h ├── glvnd_list.h ├── meson.build ├── platform-base.c ├── platform-base.h ├── platform-impl.h ├── platform-utils.c ├── platform-utils.h ├── refcountobj.c └── refcountobj.h └── x11 ├── 20_nvidia_xcb.json ├── 20_nvidia_xlib.json ├── dma-buf.h ├── driver-platform-surface.h ├── meson.build ├── x11-config.c ├── x11-pixmap.c ├── x11-platform-xcb.c ├── x11-platform-xlib.c ├── x11-platform.c ├── x11-platform.h ├── x11-timeline.c ├── x11-timeline.h └── x11-window.c /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NVIDIA XLib and XCB EGL Platform Library 2 | ================================ 3 | 4 | Overview 5 | -------- 6 | 7 | This is an EGL platform library for the NVIDIA driver to support XWayland via 8 | xlib (using `EGL_KHR_platform_x11`) or xcb (using `EGL_EXT_platform_xcb`). 9 | 10 | Building and Installing 11 | ----------------------- 12 | 13 | This library depends on: 14 | - libxcb, libxcb-present, and libxcb-dri3, version 1.17.0 15 | - libgbm, version 21.2.0 16 | - libdrm, version 2.4.99 17 | - libx11 (only if building the xlib library) 18 | - EGL headers 19 | 20 | In addition, this library depends on a (still somewhat experimental) interface 21 | in the NVIDIA driver, which is supported only in 560 or later series drivers. 22 | 23 | For full functionality, it also needs the explicit sync protocol added to 24 | version 1.4 of the Present and DRI3 extensions, which is available in XWayland 25 | 24.1 and later. Without explicit sync support, you may get reduced performance 26 | and out-of-order frames. 27 | 28 | To build and install, use Meson: 29 | 30 | ```sh 31 | meson builddir 32 | ninja -C builddir 33 | ninja -C builddir install 34 | ``` 35 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | project('egl-x11', 'c', 17 | version : '1.0.3', 18 | default_options : ['c_std=gnu99'], 19 | ) 20 | 21 | dep_libdrm = dependency('libdrm') 22 | dep_threads = dependency('threads') 23 | dep_eglexternal = dependency('eglexternalplatform', version : ['>=1.2', '<2']) 24 | inc_base = include_directories('src/base') 25 | 26 | cc = meson.get_compiler('c') 27 | if cc.compiles('typeof(int *);', name : 'typeof') 28 | add_project_arguments('-DHAVE_TYPEOF', language : ['c']) 29 | endif 30 | 31 | subdir('src/base') 32 | subdir('src/x11') 33 | 34 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option( 2 | 'xlib', 3 | type : 'feature', 4 | description : 'Build a platform library for EGL_PLATFORM_X11' 5 | ) 6 | option( 7 | 'xcb', 8 | type : 'boolean', 9 | description : 'Build a platform library for EGL_PLATFORM_XCB' 10 | ) 11 | -------------------------------------------------------------------------------- /src/base/config-list.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "config-list.h" 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | #include "platform-utils.h" 28 | 29 | const EplFormatInfo FORMAT_INFO_LIST[] = 30 | { 31 | { DRM_FORMAT_ARGB8888, 32, { 8, 8, 8, 8 }, { 16, 8, 0, 24 } }, 32 | { DRM_FORMAT_RGBA8888, 32, { 8, 8, 8, 8 }, { 24, 16, 8, 0 } }, 33 | { DRM_FORMAT_XRGB8888, 32, { 8, 8, 8, 0 }, { 16, 8, 0, 0 } }, 34 | { DRM_FORMAT_RGB888, 24, { 8, 8, 8, 0 }, { 16, 8, 0, 0 } }, 35 | 36 | { DRM_FORMAT_XBGR8888, 32, { 8, 8, 8, 0 }, { 0, 8, 16, 0 } }, 37 | { DRM_FORMAT_ABGR8888, 32, { 8, 8, 8, 8 }, { 0, 8, 16, 24 } }, 38 | { DRM_FORMAT_R8, 8, { 8, 0, 0, 0 }, { 0, 0, 0, 0 } }, 39 | { DRM_FORMAT_RG88, 16, { 8, 8, 0, 0 }, { 8, 0, 0, 0 } }, 40 | { DRM_FORMAT_R16, 16, { 16, 0, 0, 0 }, { 0, 0, 0, 0 } }, 41 | { DRM_FORMAT_RG1616, 32, { 16, 16, 0, 0 }, { 16, 0, 0, 0 } }, 42 | { DRM_FORMAT_ARGB2101010, 32, { 10, 10, 10, 2 }, { 20, 10, 0, 30 } }, 43 | { DRM_FORMAT_ABGR2101010, 32, { 10, 10, 10, 2 }, { 0, 10, 20, 30 } }, 44 | 45 | /* 8 bpp RGB */ 46 | { DRM_FORMAT_RGB332, 8, { 3, 3, 2, 0 }, { 5, 2, 0, 0 } }, 47 | 48 | /* 16 bpp RGB */ 49 | { DRM_FORMAT_ARGB4444, 16, { 4, 4, 4, 4 }, { 8, 4, 0, 12 } }, 50 | { DRM_FORMAT_ABGR4444, 16, { 4, 4, 4, 4 }, { 0, 4, 8, 12 } }, 51 | { DRM_FORMAT_RGBA4444, 16, { 4, 4, 4, 4 }, { 12, 8, 4, 0 } }, 52 | { DRM_FORMAT_BGRA4444, 16, { 4, 4, 4, 4 }, { 4, 8, 12, 0 } }, 53 | { DRM_FORMAT_XRGB4444, 16, { 4, 4, 4, 0 }, { 8, 4, 0, 0 } }, 54 | { DRM_FORMAT_XBGR4444, 16, { 4, 4, 4, 0 }, { 0, 4, 8, 0 } }, 55 | { DRM_FORMAT_RGBX4444, 16, { 4, 4, 4, 0 }, { 12, 8, 4, 0 } }, 56 | { DRM_FORMAT_BGRX4444, 16, { 4, 4, 4, 0 }, { 4, 8, 12, 0 } }, 57 | 58 | { DRM_FORMAT_XRGB1555, 16, { 5, 5, 5, 0 }, { 10, 5, 0, 0 } }, 59 | { DRM_FORMAT_XBGR1555, 16, { 5, 5, 5, 0 }, { 0, 5, 10, 0 } }, 60 | { DRM_FORMAT_RGBX5551, 16, { 5, 5, 5, 0 }, { 11, 6, 1, 0 } }, 61 | { DRM_FORMAT_BGRX5551, 16, { 5, 5, 5, 0 }, { 1, 6, 11, 0 } }, 62 | 63 | { DRM_FORMAT_ARGB1555, 16, { 5, 5, 5, 1 }, { 10, 5, 0, 15 } }, 64 | { DRM_FORMAT_ABGR1555, 16, { 5, 5, 5, 1 }, { 0, 5, 10, 15 } }, 65 | { DRM_FORMAT_RGBA5551, 16, { 5, 5, 5, 1 }, { 11, 6, 1, 0 } }, 66 | { DRM_FORMAT_BGRA5551, 16, { 5, 5, 5, 1 }, { 1, 6, 11, 0 } }, 67 | 68 | { DRM_FORMAT_RGB565, 16, { 5, 6, 5, 0 }, { 11, 5, 0, 0 } }, 69 | { DRM_FORMAT_BGR565, 16, { 5, 6, 5, 0 }, { 0, 5, 11, 0 } }, 70 | 71 | /* 24 bpp RGB */ 72 | //{ DRM_FORMAT_RGB888, 24, { 8, 8, 8, 0 }, { 16, 8, 0, 0 } }, 73 | { DRM_FORMAT_BGR888, 24, { 8, 8, 8, 0 }, { 0, 8, 16, 0 } }, 74 | 75 | /* 32 bpp RGB */ 76 | { DRM_FORMAT_RGBX8888, 32, { 8, 8, 8, 0 }, { 24, 16, 8, 0 } }, 77 | { DRM_FORMAT_BGRX8888, 32, { 8, 8, 8, 0 }, { 8, 16, 24, 0 } }, 78 | //{ DRM_FORMAT_RGBA8888, 32, { 8, 8, 8, 8 }, { 24, 16, 8, 0 } }, 79 | { DRM_FORMAT_BGRA8888, 32, { 8, 8, 8, 8 }, { 8, 16, 24, 0 } }, 80 | { DRM_FORMAT_XRGB2101010, 32, { 10, 10, 10, 0 }, { 20, 10, 0, 0 } }, 81 | { DRM_FORMAT_XBGR2101010, 32, { 10, 10, 10, 0 }, { 0, 10, 20, 0 } }, 82 | { DRM_FORMAT_RGBX1010102, 32, { 10, 10, 10, 0 }, { 22, 12, 2, 0 } }, 83 | { DRM_FORMAT_BGRX1010102, 32, { 10, 10, 10, 0 }, { 2, 12, 22, 0 } }, 84 | { DRM_FORMAT_RGBA1010102, 32, { 10, 10, 10, 2 }, { 22, 12, 2, 0 } }, 85 | { DRM_FORMAT_BGRA1010102, 32, { 10, 10, 10, 2 }, { 2, 12, 22, 0 } }, 86 | 87 | { DRM_FORMAT_INVALID } 88 | }; 89 | const int FORMAT_INFO_COUNT = (sizeof(FORMAT_INFO_LIST) / sizeof(FORMAT_INFO_LIST[0])) - 1; 90 | 91 | // Note: Since the EGLConfig is the first element of EplConfig, this function 92 | // should work for sorting and searching an array of EGLConfig or EplConfig. 93 | static int CompareConfig(const void *p1, const void *p2) 94 | { 95 | uintptr_t v1 = *((const uintptr_t *) p1); 96 | uintptr_t v2 = *((const uintptr_t *) p2); 97 | if (v1 < v2) 98 | { 99 | return -1; 100 | } 101 | else if (v1 > v2) 102 | { 103 | return 1; 104 | } 105 | else 106 | { 107 | return 0; 108 | } 109 | } 110 | 111 | static void LookupConfigInfo(EplPlatformData *platform, EGLDisplay edpy, EGLConfig config, EplConfig *info) 112 | { 113 | EGLint color[4] = { 0, 0, 0, 0 }; 114 | EGLint surfaceMask = 0; 115 | EGLint i; 116 | 117 | memset(info, 0, sizeof(*info)); 118 | info->config = config; 119 | info->nativeVisualID = 0; 120 | info->nativeVisualType = EGL_NONE; 121 | 122 | if (!platform->egl.GetConfigAttrib(edpy, config, EGL_RED_SIZE, &color[0]) 123 | || !platform->egl.GetConfigAttrib(edpy, config, EGL_GREEN_SIZE, &color[1]) 124 | || !platform->egl.GetConfigAttrib(edpy, config, EGL_BLUE_SIZE, &color[2]) 125 | || !platform->egl.GetConfigAttrib(edpy, config, EGL_ALPHA_SIZE, &color[3]) 126 | || !platform->egl.GetConfigAttrib(edpy, config, EGL_SURFACE_TYPE, &surfaceMask)) 127 | { 128 | return; 129 | } 130 | 131 | info->surfaceMask = surfaceMask; 132 | 133 | // For now, just find a format with the right color sizes. 134 | info->fourcc = DRM_FORMAT_INVALID; 135 | for (i=0; ifourcc = FORMAT_INFO_LIST[i].fourcc; 143 | break; 144 | } 145 | } 146 | } 147 | 148 | EplConfigList *eplConfigListCreate(EplPlatformData *platform, EGLDisplay edpy) 149 | { 150 | EplConfigList *list = NULL; 151 | EGLConfig *driverConfigs = NULL; 152 | EGLint numConfigs = 0; 153 | EGLint i; 154 | 155 | if (!platform->egl.GetConfigs(edpy, NULL, 0, &numConfigs) || numConfigs <= 0) 156 | { 157 | return NULL; 158 | } 159 | 160 | driverConfigs = malloc(numConfigs * sizeof(EGLConfig)); 161 | if (driverConfigs == NULL) 162 | { 163 | eplSetError(platform, EGL_BAD_ALLOC, "Out of memory"); 164 | return NULL; 165 | } 166 | if (!platform->egl.GetConfigs(edpy, driverConfigs, numConfigs, &numConfigs) || numConfigs <= 0) 167 | { 168 | free(driverConfigs); 169 | return NULL; 170 | } 171 | qsort(driverConfigs, numConfigs, sizeof(EGLConfig), CompareConfig); 172 | 173 | list = malloc(sizeof(EplConfigList) + numConfigs * sizeof(EplConfig)); 174 | if (list == NULL) 175 | { 176 | eplSetError(platform, EGL_BAD_ALLOC, "Out of memory"); 177 | free(driverConfigs); 178 | return NULL; 179 | } 180 | 181 | list->configs = (EplConfig *) (list + 1); 182 | list->num_configs = numConfigs; 183 | for (i=0; iconfigs[i]); 186 | } 187 | free(driverConfigs); 188 | 189 | return list; 190 | } 191 | 192 | void eplConfigListFree(EplConfigList *list) 193 | { 194 | free(list); 195 | } 196 | 197 | EplConfig *eplConfigListFind(EplConfigList *list, EGLConfig config) 198 | { 199 | EplConfig *found = bsearch(&config, list->configs, 200 | list->num_configs, sizeof(EplConfig), CompareConfig); 201 | return found; 202 | } 203 | 204 | EGLint eplConfigListFindIndex(EplConfigList *list, EGLConfig config) 205 | { 206 | EplConfig *found = eplConfigListFind(list, config); 207 | if (found != NULL) 208 | { 209 | return (EGLint) (found - list->configs); 210 | } 211 | else 212 | { 213 | return -1; 214 | } 215 | } 216 | 217 | EplConfig **eplConfigListChooseConfigs(EplPlatformData *platform, EGLDisplay edpy, 218 | EplConfigList *list, const EGLint *attribs, 219 | EGLint *ret_count, EGLint *ret_native_pixmap) 220 | { 221 | EGLint surfaceMask = EGL_WINDOW_BIT; 222 | EGLint nativeRenderable = EGL_DONT_CARE; 223 | EGLint nativeVisualType = EGL_DONT_CARE; 224 | EGLint numAttribs = eplCountAttribs32(attribs); 225 | 226 | EGLint *attribsCopy = NULL; 227 | EGLConfig *internalConfigs = NULL; 228 | EGLint internalCount = 0; 229 | EplConfig **configs = NULL; 230 | EGLBoolean success = EGL_FALSE; 231 | EGLint matchCount = 0; 232 | EGLint i; 233 | 234 | attribsCopy = malloc((numAttribs + 3) * sizeof(EGLint)); 235 | if (attribsCopy == NULL) 236 | { 237 | eplSetError(platform, EGL_BAD_ALLOC, "Out of memory"); 238 | goto done; 239 | } 240 | 241 | // Copy and filter out any attributes that we need to special case. 242 | numAttribs = 0; 243 | if (attribs != NULL) 244 | { 245 | for (i=0; attribs[i] != EGL_NONE; i += 2) 246 | { 247 | if (attribs[i] == EGL_MATCH_NATIVE_PIXMAP) 248 | { 249 | if (ret_native_pixmap != NULL) 250 | { 251 | *ret_native_pixmap = attribs[i + 1]; 252 | } 253 | } 254 | else if (attribs[i] == EGL_SURFACE_TYPE) 255 | { 256 | surfaceMask = attribs[i + 1]; 257 | } 258 | else if (attribs[i] == EGL_NATIVE_RENDERABLE) 259 | { 260 | nativeRenderable = attribs[i + 1]; 261 | } 262 | else if (attribs[i] == EGL_NATIVE_VISUAL_TYPE) 263 | { 264 | nativeVisualType = attribs[i + 1]; 265 | } 266 | else 267 | { 268 | attribsCopy[numAttribs++] = attribs[i]; 269 | attribsCopy[numAttribs++] = attribs[i + 1]; 270 | } 271 | } 272 | } 273 | // Get configs for all surface types. We'll filter those manually below. 274 | attribsCopy[numAttribs++] = EGL_SURFACE_TYPE; 275 | attribsCopy[numAttribs++] = EGL_DONT_CARE; 276 | attribsCopy[numAttribs] = EGL_NONE; 277 | 278 | if (!platform->egl.ChooseConfig(edpy, attribsCopy, NULL, 0, &internalCount) 279 | || internalCount <= 0) 280 | { 281 | goto done; 282 | } 283 | 284 | internalConfigs = malloc(internalCount * sizeof(EGLConfig)); 285 | configs = malloc((internalCount + 1) * sizeof(EplConfig *)); 286 | if (internalConfigs == NULL || configs == NULL) 287 | { 288 | eplSetError(platform, EGL_BAD_ALLOC, "Out of memory"); 289 | goto done; 290 | } 291 | 292 | if (!platform->egl.ChooseConfig(edpy, attribsCopy, internalConfigs, internalCount, &internalCount) 293 | || internalCount <= 0) 294 | { 295 | goto done; 296 | } 297 | 298 | matchCount = 0; 299 | for (i=0; isurfaceMask & surfaceMask) != surfaceMask) 310 | { 311 | continue; 312 | } 313 | } 314 | if (nativeRenderable != EGL_DONT_CARE) 315 | { 316 | if (info->nativeRenderable != (nativeRenderable != 0)) 317 | { 318 | continue; 319 | } 320 | } 321 | if (nativeVisualType != EGL_DONT_CARE) 322 | { 323 | if (info->nativeVisualType != nativeVisualType) 324 | { 325 | continue; 326 | } 327 | } 328 | 329 | configs[matchCount++] = info; 330 | } 331 | configs[matchCount] = NULL; 332 | success = EGL_TRUE; 333 | 334 | done: 335 | if (success) 336 | { 337 | if (ret_count != NULL) 338 | { 339 | *ret_count = matchCount; 340 | } 341 | } 342 | else 343 | { 344 | free(configs); 345 | configs = NULL; 346 | } 347 | free(attribsCopy); 348 | free(internalConfigs); 349 | return configs; 350 | } 351 | 352 | void eplConfigListReturnConfigs(EplConfig **configs, EGLint count, 353 | EGLConfig *ret_configs, EGLint max, EGLint *ret_count) 354 | { 355 | EGLint num = count; 356 | if (ret_configs != NULL) 357 | { 358 | EGLint i; 359 | if (num > max) 360 | { 361 | num = max; 362 | } 363 | for (i=0; iconfig; 366 | } 367 | } 368 | if (ret_count != NULL) 369 | { 370 | *ret_count = num; 371 | } 372 | } 373 | 374 | EGLBoolean eplConfigListGetAttribute(EplPlatformData *platform, EGLDisplay edpy, 375 | EplConfigList *list, EGLConfig config, EGLint attribute, EGLint *value) 376 | { 377 | const EplConfig *info; 378 | EGLBoolean success = EGL_TRUE; 379 | EGLint val = 0; 380 | 381 | info = eplConfigListFind(list, config); 382 | if (info == NULL) 383 | { 384 | eplSetError(platform, EGL_BAD_CONFIG, "Invalid EGLConfig %p", config); 385 | return EGL_FALSE; 386 | } 387 | 388 | if (attribute == EGL_SURFACE_TYPE) 389 | { 390 | val = info->surfaceMask; 391 | } 392 | else if (attribute == EGL_NATIVE_VISUAL_ID) 393 | { 394 | val = info->nativeVisualID; 395 | } 396 | else if (attribute == EGL_NATIVE_VISUAL_TYPE) 397 | { 398 | val = info->nativeVisualType; 399 | } 400 | else if (attribute == EGL_NATIVE_RENDERABLE) 401 | { 402 | val = info->nativeRenderable; 403 | } 404 | else 405 | { 406 | success = platform->egl.GetConfigAttrib(edpy, config, attribute, &val); 407 | } 408 | if (success && value != NULL) 409 | { 410 | *value = val; 411 | } 412 | 413 | return success; 414 | } 415 | 416 | const EplFormatInfo *eplFormatInfoLookup(uint32_t fourcc) 417 | { 418 | int i; 419 | for (i=0; i 28 | #include 29 | #include 30 | 31 | #include "platform-base.h" 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | /** 38 | * Contains some basic information about a fourcc format. 39 | * 40 | * This is used to match a format to things like an X11 visual. 41 | */ 42 | typedef struct 43 | { 44 | uint32_t fourcc; 45 | int bpp; 46 | int colors[4]; 47 | int offset[4]; 48 | } EplFormatInfo; 49 | 50 | /** 51 | * Contains information about an EGLConfig. 52 | */ 53 | typedef struct 54 | { 55 | /** 56 | * The EGLConfig handle. 57 | * 58 | * Note that this must be the first element in EplConfig. 59 | */ 60 | EGLConfig config; 61 | 62 | /** 63 | * The fourcc format code. Currently set based on the color sizes. 64 | */ 65 | uint32_t fourcc; 66 | 67 | /** 68 | * The value of EGL_SURFACE_TYPE for this config. 69 | * 70 | * This is initially set to whatever the driver hands back. The platform 71 | * library can then add EGL_WINDOW_BIT and EGL_PIXMAP_BIT as appropriate. 72 | */ 73 | EGLint surfaceMask; 74 | 75 | /** 76 | * The value of EGL_NATIVE_VISUAL_ID. Initially set to zero. 77 | */ 78 | EGLint nativeVisualID; 79 | 80 | /** 81 | * The value of EGL_NATIVE_VISUAL_TYPE. 82 | * 83 | * Initially set to EGL_NONE. 84 | */ 85 | EGLint nativeVisualType; 86 | 87 | /** 88 | * The value of EGL_NATIVE_RENDERABLE. 89 | * 90 | * Initially set to EGL_FALSE. 91 | */ 92 | EGLBoolean nativeRenderable; 93 | } EplConfig; 94 | 95 | /** 96 | * An array of known color formats. 97 | * 98 | * Note that this list is *not* sorted by fourcc value. It's sorted based on 99 | * which formats to pick if we don't know/care what the color order is. 100 | */ 101 | extern const EplFormatInfo FORMAT_INFO_LIST[]; 102 | extern const int FORMAT_INFO_COUNT; 103 | 104 | typedef struct 105 | { 106 | /** 107 | * A list of EplConfigs, sorted by the EGLConfig handle. 108 | */ 109 | EplConfig *configs; 110 | EGLint num_configs; 111 | } EplConfigList; 112 | 113 | /** 114 | * Looks up all available EGLConfigs. 115 | * 116 | * \param edpy The internal EGLDisplay. 117 | * \return A new EplConfigList struct. 118 | */ 119 | EplConfigList *eplConfigListCreate(EplPlatformData *platform, EGLDisplay edpy); 120 | 121 | void eplConfigListFree(EplConfigList *list); 122 | 123 | /** 124 | * Looks up an EplConfig by its EGLConfig handle. 125 | * 126 | * \param list The EplConfigList to search. 127 | * \param config The EGLConfig handle to search for. 128 | * \return The matching EplConfig struct, or NULL if \p config was not found. 129 | */ 130 | EplConfig *eplConfigListFind(EplConfigList *list, EGLConfig config); 131 | 132 | /** 133 | * A helper function for handling eglChooseConfig. 134 | * 135 | * This will fetch a list of EGLConfigs from the driver, and then filter that 136 | * list based on the EGL_SURFACE_TYPE, EGL_NATIVE_VISUAL_TYPE, and 137 | * EGL_NATIVE_RENDERABLE attributes. 138 | * 139 | * This does not filter based on EGL_MATCH_NATIVE_PIXMAP, since that requires 140 | * platform-specific code. Instead, if EGL_MATCH_NATIVE_PIXMAP is included in 141 | * \p attribs, then the value is returned in \p ret_native_pixmap. Otherwise, 142 | * \p ret_native_pixmap is left unchanged. 143 | * 144 | * This function returns a NULL-terminated array of EplConfig pointers, so the 145 | * caller can do any additional filtering as needed. You can use 146 | * eplConfigListReturnConfigs to copy the results to an EGLConfig array. 147 | * 148 | * \param platform The platform data. 149 | * \param edpy The internal EGLDisplay. 150 | * \param list The list to search. 151 | * \param attribs The attribute list, as provided by the application. 152 | * \param[out] ret_count Optionally returns the number of matching attributes, 153 | * not including the NULL terminator. 154 | * \param[out] ret_native_pixmap Optionally returns the value of the 155 | * EGL_MATCH_NATIVE_PIXMAP attribute. If that attribtue isn't in he list, 156 | * then this is left unchanged. 157 | * \return A NULL-termianted array of EplConfigs, or NULL on error. 158 | */ 159 | EplConfig **eplConfigListChooseConfigs(EplPlatformData *platform, EGLDisplay edpy, 160 | EplConfigList *list, const EGLint *attribs, 161 | EGLint *ret_count, EGLint *ret_native_pixmap); 162 | 163 | /** 164 | * Copies the EGLConfig handles from an EplConfig array to an EGLConfig array. 165 | * 166 | * This is used in conjunction with eplConfigListChooseConfigs to handle 167 | * eglChooseConfig. 168 | */ 169 | void eplConfigListReturnConfigs(EplConfig **configs, EGLint count, 170 | EGLConfig *ret_configs, EGLint max, EGLint *ret_count); 171 | 172 | /** 173 | * A helper function for handling eglGetConfigAttrib. 174 | * 175 | * This function will fill in results for attributes that are stored in the 176 | * EplConfig struct. 177 | * 178 | * Currently, that includes EGL_SURFACE_TYPE, EGL_NATIVE_VISUAL_ID, and 179 | * EGL_NATIVE_VISUAL_TYPE, and EGL_NATIVE_RENDERABLE. 180 | * 181 | * For anything else, it will call through to the driver. 182 | */ 183 | EGLBoolean eplConfigListGetAttribute(EplPlatformData *platform, EGLDisplay edpy, 184 | EplConfigList *list, EGLConfig config, EGLint attribute, EGLint *value); 185 | 186 | /** 187 | * Returns the index of an EplConfig. 188 | * 189 | * \param list The EplConfigList to search. 190 | * \param config The EGLConfig handle to search for. 191 | * \return The index of the matching EplConfig struct, or -1 if \p config was 192 | * not found. 193 | */ 194 | EGLint eplConfigListFindIndex(EplConfigList *list, EGLConfig config); 195 | 196 | const EplFormatInfo *eplFormatInfoLookup(uint32_t fourcc); 197 | 198 | static inline int eplFormatInfoDepth(const EplFormatInfo *fmt) 199 | { 200 | return fmt->colors[0] + fmt->colors[1] + fmt->colors[2] + fmt->colors[3]; 201 | } 202 | 203 | #ifdef __cplusplus 204 | } 205 | #endif 206 | #endif // CONFIG_LIST_H 207 | -------------------------------------------------------------------------------- /src/base/glvnd_list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2010 Intel Corporation 3 | * Copyright © 2010 Francisco Jerez 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a 6 | * copy of this software and associated documentation files (the "Software"), 7 | * to deal in the Software without restriction, including without limitation 8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | * and/or sell copies of the Software, and to permit persons to whom the 10 | * Software is furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice (including the next 13 | * paragraph) shall be included in all copies or substantial portions of the 14 | * Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef _GLVND_LIST_H_ 27 | #define _GLVND_LIST_H_ 28 | 29 | #include /* offsetof() */ 30 | 31 | /** 32 | * @file Classic doubly-link circular list implementation. 33 | * For real usage examples of the linked list, see the file test/list.c 34 | * 35 | * Example: 36 | * We need to keep a list of struct foo in the parent struct bar, i.e. what 37 | * we want is something like this. 38 | * 39 | * struct bar { 40 | * ... 41 | * struct foo *list_of_foos; -----> struct foo {}, struct foo {}, struct foo{} 42 | * ... 43 | * } 44 | * 45 | * We need one list head in bar and a list element in all list_of_foos (both are of 46 | * data type 'struct glvnd_list'). 47 | * 48 | * struct bar { 49 | * ... 50 | * struct glvnd_list list_of_foos; 51 | * ... 52 | * } 53 | * 54 | * struct foo { 55 | * ... 56 | * struct glvnd_list entry; 57 | * ... 58 | * } 59 | * 60 | * Now we initialize the list head: 61 | * 62 | * struct bar bar; 63 | * ... 64 | * glvnd_list_init(&bar.list_of_foos); 65 | * 66 | * Then we create the first element and add it to this list: 67 | * 68 | * struct foo *foo = malloc(...); 69 | * .... 70 | * glvnd_list_add(&foo->entry, &bar.list_of_foos); 71 | * 72 | * Repeat the above for each element you want to add to the list. Deleting 73 | * works with the element itself. 74 | * glvnd_list_del(&foo->entry); 75 | * free(foo); 76 | * 77 | * Note: calling glvnd_list_del(&bar.list_of_foos) will set bar.list_of_foos to an empty 78 | * list again. 79 | * 80 | * Looping through the list requires a 'struct foo' as iterator and the 81 | * name of the field the subnodes use. 82 | * 83 | * struct foo *iterator; 84 | * glvnd_list_for_each_entry(iterator, &bar.list_of_foos, entry) { 85 | * if (iterator->something == ...) 86 | * ... 87 | * } 88 | * 89 | * Note: You must not call glvnd_list_del() on the iterator if you continue the 90 | * loop. You need to run the safe for-each loop instead: 91 | * 92 | * struct foo *iterator, *next; 93 | * glvnd_list_for_each_entry_safe(iterator, next, &bar.list_of_foos, entry) { 94 | * if (...) 95 | * glvnd_list_del(&iterator->entry); 96 | * } 97 | * 98 | */ 99 | 100 | /** 101 | * The linkage struct for list nodes. This struct must be part of your 102 | * to-be-linked struct. struct glvnd_list is required for both the head of the 103 | * list and for each list node. 104 | * 105 | * Position and name of the struct glvnd_list field is irrelevant. 106 | * There are no requirements that elements of a list are of the same type. 107 | * There are no requirements for a list head, any struct glvnd_list can be a list 108 | * head. 109 | */ 110 | struct glvnd_list { 111 | struct glvnd_list *next, *prev; 112 | }; 113 | 114 | /** 115 | * Initialize the list as an empty list. 116 | * 117 | * Example: 118 | * glvnd_list_init(&bar->list_of_foos); 119 | * 120 | * @param The list to initialized. 121 | */ 122 | static inline void 123 | glvnd_list_init(struct glvnd_list *list) 124 | { 125 | list->next = list->prev = list; 126 | } 127 | 128 | static inline void 129 | __glvnd_list_add(struct glvnd_list *entry, 130 | struct glvnd_list *prev, struct glvnd_list *next) 131 | { 132 | next->prev = entry; 133 | entry->next = next; 134 | entry->prev = prev; 135 | prev->next = entry; 136 | } 137 | 138 | /** 139 | * Insert a new element after the given list head. The new element does not 140 | * need to be initialised as empty list. 141 | * The list changes from: 142 | * head → some element → ... 143 | * to 144 | * head → new element → older element → ... 145 | * 146 | * Example: 147 | * struct foo *newfoo = malloc(...); 148 | * glvnd_list_add(&newfoo->entry, &bar->list_of_foos); 149 | * 150 | * @param entry The new element to prepend to the list. 151 | * @param head The existing list. 152 | */ 153 | static inline void 154 | glvnd_list_add(struct glvnd_list *entry, struct glvnd_list *head) 155 | { 156 | __glvnd_list_add(entry, head, head->next); 157 | } 158 | 159 | /** 160 | * Append a new element to the end of the list given with this list head. 161 | * 162 | * The list changes from: 163 | * head → some element → ... → lastelement 164 | * to 165 | * head → some element → ... → lastelement → new element 166 | * 167 | * Example: 168 | * struct foo *newfoo = malloc(...); 169 | * glvnd_list_append(&newfoo->entry, &bar->list_of_foos); 170 | * 171 | * @param entry The new element to prepend to the list. 172 | * @param head The existing list. 173 | */ 174 | static inline void 175 | glvnd_list_append(struct glvnd_list *entry, struct glvnd_list *head) 176 | { 177 | __glvnd_list_add(entry, head->prev, head); 178 | } 179 | 180 | static inline void 181 | __glvnd_list_del(struct glvnd_list *prev, struct glvnd_list *next) 182 | { 183 | next->prev = prev; 184 | prev->next = next; 185 | } 186 | 187 | /** 188 | * Remove the element from the list it is in. Using this function will reset 189 | * the pointers to/from this element so it is removed from the list. It does 190 | * NOT free the element itself or manipulate it otherwise. 191 | * 192 | * Using glvnd_list_del on a pure list head (like in the example at the top of 193 | * this file) will NOT remove the first element from 194 | * the list but rather reset the list as empty list. 195 | * 196 | * Example: 197 | * glvnd_list_del(&foo->entry); 198 | * 199 | * @param entry The element to remove. 200 | */ 201 | static inline void 202 | glvnd_list_del(struct glvnd_list *entry) 203 | { 204 | __glvnd_list_del(entry->prev, entry->next); 205 | glvnd_list_init(entry); 206 | } 207 | 208 | /** 209 | * Check if the list is empty. 210 | * 211 | * Example: 212 | * glvnd_list_is_empty(&bar->list_of_foos); 213 | * 214 | * @return True if the list contains one or more elements or False otherwise. 215 | */ 216 | static inline int 217 | glvnd_list_is_empty(struct glvnd_list *head) 218 | { 219 | return head->next == head; 220 | } 221 | 222 | /** 223 | * Returns a pointer to the container of this list element. 224 | * 225 | * Example: 226 | * struct foo* f; 227 | * f = glvnd_container_of(&foo->entry, struct foo, entry); 228 | * assert(f == foo); 229 | * 230 | * @param ptr Pointer to the struct glvnd_list. 231 | * @param type Data type of the list element. 232 | * @param member Member name of the struct glvnd_list field in the list element. 233 | * @return A pointer to the data struct containing the list head. 234 | */ 235 | #ifndef glvnd_container_of 236 | #define glvnd_container_of(ptr, type, member) \ 237 | (type *)((char *)(ptr) - offsetof(type, member)) 238 | #endif 239 | 240 | /** 241 | * Alias of glvnd_container_of 242 | */ 243 | #define glvnd_list_entry(ptr, type, member) \ 244 | glvnd_container_of(ptr, type, member) 245 | 246 | /** 247 | * Retrieve the first list entry for the given list pointer. 248 | * 249 | * Example: 250 | * struct foo *first; 251 | * first = glvnd_list_first_entry(&bar->list_of_foos, struct foo, list_of_foos); 252 | * 253 | * @param ptr The list head 254 | * @param type Data type of the list element to retrieve 255 | * @param member Member name of the struct glvnd_list field in the list element. 256 | * @return A pointer to the first list element. 257 | */ 258 | #define glvnd_list_first_entry(ptr, type, member) \ 259 | glvnd_list_entry((ptr)->next, type, member) 260 | 261 | /** 262 | * Retrieve the last list entry for the given listpointer. 263 | * 264 | * Example: 265 | * struct foo *first; 266 | * first = glvnd_list_last_entry(&bar->list_of_foos, struct foo, list_of_foos); 267 | * 268 | * @param ptr The list head 269 | * @param type Data type of the list element to retrieve 270 | * @param member Member name of the struct glvnd_list field in the list element. 271 | * @return A pointer to the last list element. 272 | */ 273 | #define glvnd_list_last_entry(ptr, type, member) \ 274 | glvnd_list_entry((ptr)->prev, type, member) 275 | 276 | #ifdef HAVE_TYPEOF 277 | #define __glvnd_container_of(ptr, sample, member) \ 278 | glvnd_container_of(ptr, typeof(*sample), member) 279 | #else 280 | /* This implementation of __glvnd_container_of has undefined behavior according 281 | * to the C standard, but it works in many cases. If your compiler doesn't 282 | * support typeof() and fails with this implementation, please try a newer 283 | * compiler. 284 | */ 285 | #warning "typeof() is not supported. The fallback for this is undefined behavior." 286 | #define __glvnd_container_of(ptr, sample, member) \ 287 | (void *)((char *)(ptr) \ 288 | - ((char *)&(sample)->member - (char *)(sample))) 289 | #endif 290 | 291 | /** 292 | * Loop through the list given by head and set pos to struct in the list. 293 | * 294 | * Example: 295 | * struct foo *iterator; 296 | * glvnd_list_for_each_entry(iterator, &bar->list_of_foos, entry) { 297 | * [modify iterator] 298 | * } 299 | * 300 | * This macro is not safe for node deletion. Use glvnd_list_for_each_entry_safe 301 | * instead. 302 | * 303 | * @param pos Iterator variable of the type of the list elements. 304 | * @param head List head 305 | * @param member Member name of the struct glvnd_list in the list elements. 306 | * 307 | */ 308 | #define glvnd_list_for_each_entry(pos, head, member) \ 309 | for (pos = __glvnd_container_of((head)->next, pos, member); \ 310 | &pos->member != (head); \ 311 | pos = __glvnd_container_of(pos->member.next, pos, member)) 312 | 313 | /** 314 | * Loop through the list, keeping a backup pointer to the element. This 315 | * macro allows for the deletion of a list element while looping through the 316 | * list. 317 | * 318 | * See glvnd_list_for_each_entry for more details. 319 | */ 320 | #define glvnd_list_for_each_entry_safe(pos, tmp, head, member) \ 321 | for (pos = __glvnd_container_of((head)->next, pos, member), \ 322 | tmp = __glvnd_container_of(pos->member.next, pos, member); \ 323 | &pos->member != (head); \ 324 | pos = tmp, tmp = __glvnd_container_of(pos->member.next, tmp, member)) 325 | 326 | /* NULL-Terminated List Interface 327 | * 328 | * The interface below does _not_ use the struct glvnd_list as described above. 329 | * It is mainly for legacy structures that cannot easily be switched to 330 | * struct glvnd_list. 331 | * 332 | * This interface is for structs like 333 | * struct foo { 334 | * [...] 335 | * struct foo *next; 336 | * [...] 337 | * }; 338 | * 339 | * The position and field name of "next" are arbitrary. 340 | */ 341 | 342 | /** 343 | * Init the element as null-terminated list. 344 | * 345 | * Example: 346 | * struct foo *list = malloc(); 347 | * glvnd_nt_list_init(list, next); 348 | * 349 | * @param list The list element that will be the start of the list 350 | * @param member Member name of the field pointing to next struct 351 | */ 352 | #define glvnd_nt_list_init(_list, _member) \ 353 | (_list)->_member = NULL 354 | 355 | /** 356 | * Returns the next element in the list or NULL on termination. 357 | * 358 | * Example: 359 | * struct foo *element = list; 360 | * while ((element = glvnd_nt_list_next(element, next)) { } 361 | * 362 | * This macro is not safe for node deletion. Use glvnd_nt_list_for_each_entry_safe 363 | * instead. 364 | * 365 | * @param list The list or current element. 366 | * @param member Member name of the field pointing to next struct. 367 | */ 368 | #define glvnd_nt_list_next(_list, _member) \ 369 | (_list)->_member 370 | 371 | /** 372 | * Iterate through each element in the list. 373 | * 374 | * Example: 375 | * struct foo *iterator; 376 | * glvnd_nt_list_for_each_entry(iterator, list, next) { 377 | * [modify iterator] 378 | * } 379 | * 380 | * @param entry Assigned to the current list element 381 | * @param list The list to iterate through. 382 | * @param member Member name of the field pointing to next struct. 383 | */ 384 | #define glvnd_nt_list_for_each_entry(_entry, _list, _member) \ 385 | for (_entry = _list; _entry; _entry = (_entry)->_member) 386 | 387 | /** 388 | * Iterate through each element in the list, keeping a backup pointer to the 389 | * element. This macro allows for the deletion of a list element while 390 | * looping through the list. 391 | * 392 | * See glvnd_nt_list_for_each_entry for more details. 393 | * 394 | * @param entry Assigned to the current list element 395 | * @param tmp The pointer to the next element 396 | * @param list The list to iterate through. 397 | * @param member Member name of the field pointing to next struct. 398 | */ 399 | #define glvnd_nt_list_for_each_entry_safe(_entry, _tmp, _list, _member) \ 400 | for (_entry = _list, _tmp = (_entry) ? (_entry)->_member : NULL;\ 401 | _entry; \ 402 | _entry = _tmp, _tmp = (_tmp) ? (_tmp)->_member: NULL) 403 | 404 | /** 405 | * Append the element to the end of the list. This macro may be used to 406 | * merge two lists. 407 | * 408 | * Example: 409 | * struct foo *elem = malloc(...); 410 | * glvnd_nt_list_init(elem, next) 411 | * glvnd_nt_list_append(elem, list, struct foo, next); 412 | * 413 | * Resulting list order: 414 | * list_item_0 -> list_item_1 -> ... -> elem_item_0 -> elem_item_1 ... 415 | * 416 | * @param entry An entry (or list) to append to the list 417 | * @param list The list to append to. This list must be a valid list, not 418 | * NULL. 419 | * @param type The list type 420 | * @param member Member name of the field pointing to next struct 421 | */ 422 | #define glvnd_nt_list_append(_entry, _list, _type, _member) \ 423 | do { \ 424 | _type *__iterator = _list; \ 425 | while (__iterator->_member) { __iterator = __iterator->_member;}\ 426 | __iterator->_member = _entry; \ 427 | } while (0) 428 | 429 | /** 430 | * Insert the element at the next position in the list. This macro may be 431 | * used to insert a list into a list. 432 | * 433 | * struct foo *elem = malloc(...); 434 | * glvnd_nt_list_init(elem, next) 435 | * glvnd_nt_list_insert(elem, list, struct foo, next); 436 | * 437 | * Resulting list order: 438 | * list_item_0 -> elem_item_0 -> elem_item_1 ... -> list_item_1 -> ... 439 | * 440 | * @param entry An entry (or list) to append to the list 441 | * @param list The list to insert to. This list must be a valid list, not 442 | * NULL. 443 | * @param type The list type 444 | * @param member Member name of the field pointing to next struct 445 | */ 446 | #define glvnd_nt_list_insert(_entry, _list, _type, _member) \ 447 | do { \ 448 | glvnd_nt_list_append((_list)->_member, _entry, _type, _member); \ 449 | (_list)->_member = _entry; \ 450 | } while (0) 451 | 452 | /** 453 | * Delete the entry from the list by iterating through the list and 454 | * removing any reference from the list to the entry. 455 | * 456 | * Example: 457 | * struct foo *elem = 458 | * glvnd_nt_list_del(elem, list, struct foo, next); 459 | * 460 | * @param entry The entry to delete from the list. entry is always 461 | * re-initialized as a null-terminated list. 462 | * @param list The list containing the entry, set to the new list without 463 | * the removed entry. 464 | * @param type The list type 465 | * @param member Member name of the field pointing to the next entry 466 | */ 467 | #define glvnd_nt_list_del(_entry, _list, _type, _member) \ 468 | do { \ 469 | _type *__e = _entry; \ 470 | if (__e == NULL || _list == NULL) break; \ 471 | if ((_list) == __e) { \ 472 | _list = __e->_member; \ 473 | } else { \ 474 | _type *__prev = _list; \ 475 | while (__prev->_member && __prev->_member != __e) \ 476 | __prev = glvnd_nt_list_next(__prev, _member); \ 477 | if (__prev->_member) \ 478 | __prev->_member = __e->_member; \ 479 | } \ 480 | glvnd_nt_list_init(__e, _member); \ 481 | } while(0) 482 | 483 | #endif 484 | -------------------------------------------------------------------------------- /src/base/meson.build: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | platform_base = static_library('platform-base', 17 | [ 18 | 'config-list.c', 19 | 'platform-base.c', 20 | 'platform-utils.c', 21 | 'refcountobj.c', 22 | ], 23 | dependencies: [ 24 | dep_libdrm.partial_dependency(compile_args : true, includes : true), 25 | dep_threads, 26 | dep_eglexternal, 27 | ], 28 | gnu_symbol_visibility: 'hidden', 29 | install: false) 30 | -------------------------------------------------------------------------------- /src/base/platform-base.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef PLATFORM_BASE_H 19 | #define PLATFORM_BASE_H 20 | 21 | /** 22 | * \file 23 | * 24 | * Common bookkeeping and infrastructure for an EGL platform library. 25 | * 26 | * These functions handle the basic tasks of keeping track of internal and 27 | * external EGLDisplays and EGLSurfaces. 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | 36 | #include "glvnd_list.h" 37 | #include "refcountobj.h" 38 | 39 | #define PUBLIC __attribute__((visibility("default"))) 40 | 41 | #ifdef __cplusplus 42 | extern "C" { 43 | #endif 44 | 45 | // Opaque types for private implementation data. 46 | typedef struct _EplImplSurface EplImplSurface; 47 | typedef struct _EplImplDisplay EplImplDisplay; 48 | typedef struct _EplImplPlatform EplImplPlatform; 49 | 50 | typedef enum 51 | { 52 | EPL_SURFACE_TYPE_WINDOW, 53 | EPL_SURFACE_TYPE_PIXMAP, 54 | } EplSurfaceType; 55 | 56 | /** 57 | * Keeps track of an internal EGLDisplay. 58 | */ 59 | typedef struct 60 | { 61 | EplRefCount refcount; 62 | 63 | EGLDisplay edpy; 64 | 65 | /** 66 | * The number of times that this display has been initialized. This is used 67 | * to simulate the EGL_KHR_display_reference extension even if the 68 | * underlying driver doesn't support it. 69 | */ 70 | unsigned int init_count; 71 | EGLint major; 72 | EGLint minor; 73 | 74 | struct glvnd_list entry; 75 | } EplInternalDisplay; 76 | 77 | /** 78 | * Keeps track of an EGLSurface. 79 | */ 80 | typedef struct 81 | { 82 | EplRefCount refcount; 83 | 84 | EGLSurface external_surface; 85 | EGLSurface internal_surface; 86 | EplSurfaceType type; 87 | 88 | EGLBoolean deleted; 89 | 90 | /** 91 | * Private data used by the implementation. 92 | */ 93 | EplImplSurface *priv; 94 | 95 | struct glvnd_list entry; 96 | } EplSurface; 97 | 98 | /** 99 | * Keeps track of data for an external (application-facing) EGLDisplay. 100 | */ 101 | typedef struct 102 | { 103 | /** 104 | * A reference count. This is used so that we know when it's safe to free 105 | * EplDisplay struct. 106 | * 107 | * Since EGLDisplays can't be destroyed (yet), this only really matters if 108 | * we go through teardown while another thread is still using the 109 | * EplDisplay. It'll be more interesting once we add support for 110 | * EGL_EXT_display_alloc. 111 | */ 112 | EplRefCount refcount; 113 | 114 | /** 115 | * The external (application-facing) EGLDisplay handle. 116 | */ 117 | EGLDisplay external_display; 118 | 119 | /** 120 | * The internal EGLDisplay handle. 121 | */ 122 | EGLDisplay internal_display; 123 | 124 | /** 125 | * The platform enum (EGL_PLATFORM_X11_KHR, etc.). 126 | */ 127 | EGLenum platform_enum; 128 | 129 | /** 130 | * The native display that this EplDisplay was created from. 131 | */ 132 | void *native_display; 133 | 134 | /** 135 | * A pointer back to the EplPlatformData struct that owns this EplDisplay. 136 | * 137 | * This is needed because most of the hook functions don't get a separate 138 | * parameter for the EplPlatformData. 139 | */ 140 | struct _EplPlatformData *platform; 141 | 142 | /** 143 | * All of the existing EplSurface structs. 144 | */ 145 | struct glvnd_list surface_list; 146 | 147 | /** 148 | * Private data for the implementation. 149 | */ 150 | EplImplDisplay *priv; 151 | 152 | // Everything after this in EplDisplay should be treated as internal to 153 | // platform-base.c. 154 | 155 | /** 156 | * A mutex to control access to the display. This is a recursive mutex. 157 | */ 158 | pthread_mutex_t mutex; 159 | 160 | /** 161 | * True if this display was created with EGL_TRACK_REFERENCES set. 162 | */ 163 | EGLBoolean track_references; 164 | 165 | /** 166 | * The number of times that the display has been initialized. If this 167 | * display was not created with EGL_TRACK_REFERENCES set, then this is 168 | * capped at 1. 169 | */ 170 | unsigned int init_count; 171 | 172 | /** 173 | * This is a counter to keep track of whether the display is in use or not. 174 | * 175 | * If the app calls eglTerminate, then we defer the termination until the 176 | * display is no longer in use. 177 | */ 178 | unsigned int use_count; 179 | 180 | /// The major version number for eglInitialize in this context. 181 | EGLint major; 182 | /// The minor version number for eglInitialize in this context. 183 | EGLint minor; 184 | /// True if this display has been initialized. 185 | EGLBoolean initialized; 186 | 187 | struct glvnd_list entry; 188 | } EplDisplay; 189 | 190 | typedef struct _EplPlatformData 191 | { 192 | EplRefCount refcount; 193 | 194 | struct { 195 | PFNEGLQUERYSTRINGPROC QueryString; 196 | PFNEGLGETPLATFORMDISPLAYPROC GetPlatformDisplay; 197 | PFNEGLINITIALIZEPROC Initialize; 198 | PFNEGLTERMINATEPROC Terminate; 199 | PFNEGLGETERRORPROC GetError; 200 | PFNEGLCREATEPBUFFERSURFACEPROC CreatePbufferSurface; 201 | PFNEGLDESTROYSURFACEPROC DestroySurface; 202 | PFNEGLSWAPBUFFERSPROC SwapBuffers; 203 | PFNEGLCHOOSECONFIGPROC ChooseConfig; 204 | PFNEGLGETCONFIGATTRIBPROC GetConfigAttrib; 205 | PFNEGLGETCONFIGSPROC GetConfigs; 206 | PFNEGLGETCURRENTDISPLAYPROC GetCurrentDisplay; 207 | PFNEGLGETCURRENTSURFACEPROC GetCurrentSurface; 208 | PFNEGLGETCURRENTCONTEXTPROC GetCurrentContext; 209 | PFNEGLMAKECURRENTPROC MakeCurrent; 210 | PFNEGLWAITGLPROC WaitGL; 211 | PFNEGLWAITCLIENTPROC WaitClient; 212 | PFNEGLWAITNATIVEPROC WaitNative; 213 | 214 | PFNEGLQUERYDEVICEATTRIBEXTPROC QueryDeviceAttribEXT; 215 | PFNEGLQUERYDEVICESTRINGEXTPROC QueryDeviceStringEXT; 216 | PFNEGLQUERYDEVICESEXTPROC QueryDevicesEXT; 217 | PFNEGLQUERYDISPLAYATTRIBEXTPROC QueryDisplayAttribEXT; 218 | 219 | PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC SwapBuffersWithDamage; 220 | PFNEGLCREATESTREAMPRODUCERSURFACEKHRPROC CreateStreamProducerSurfaceKHR; 221 | } egl; 222 | 223 | struct 224 | { 225 | EGLBoolean display_reference; 226 | } extensions; 227 | 228 | struct 229 | { 230 | PEGLEXTFNGETPROCADDRESS getProcAddress; 231 | PEGLEXTFNDEBUGMESSAGE debugMessage; 232 | PEGLEXTFNSETERROR setError; 233 | } callbacks; 234 | 235 | /** 236 | * True if we're going through teardown for this platform. Once we're in 237 | * teardown, it's no longer safe to call into the driver. 238 | * 239 | * Note that if another thread is currently calling an EGL function when 240 | * the platform library gets torn down, then things are likely to break no 241 | * matter what, because the driver will have finished a lot of its teardown 242 | * before the platform library finds out about it. 243 | * 244 | * Thus, this flag is only to make it easier to share cleanup code between 245 | * platform library teardown and eglDestroySurface et. al. 246 | */ 247 | EGLBoolean destroyed; 248 | 249 | /** 250 | * Private data for the implementation. 251 | */ 252 | EplImplPlatform *priv; 253 | 254 | struct glvnd_list internal_display_list; 255 | pthread_mutex_t internal_display_list_mutex; 256 | 257 | EGLenum platform_enum; 258 | const struct _EplImplFuncs *impl; 259 | 260 | struct glvnd_list entry; 261 | } EplPlatformData; 262 | 263 | EPL_REFCOUNT_DECLARE_TYPE_FUNCS(EplPlatformData, eplPlatformData); 264 | EPL_REFCOUNT_DECLARE_TYPE_FUNCS(EplInternalDisplay, eplInternalDisplay); 265 | 266 | /** 267 | * Allocates and initializes an EplPlatformData struct. 268 | * 269 | * This is called from the loadEGLExternalPlatform entrypoint. 270 | * 271 | * After calling eplPlatformBaseAllocate, the caller should perform any 272 | * platform-specific initialization, and then call eplPlatformBaseInitFinish 273 | * (on success) or eplPlatformBaseInitFail (on failure). 274 | * 275 | * \param platform_enum The EGL enum value for this platform. 276 | * \param impl The platform implementation functions. 277 | * \param platform_priv_size If non-zero, then allocate additional space and 278 | * assign it to EplPlatformData::priv. 279 | * \return A EplPlatformData struct, or NULL on error. 280 | */ 281 | EplPlatformData *eplPlatformBaseAllocate(int major, int minor, 282 | const EGLExtDriver *driver, EGLExtPlatform *extplatform, 283 | EGLenum platform_enum, const struct _EplImplFuncs *impl, 284 | size_t platform_priv_size); 285 | 286 | /** 287 | * Finishes initializing a platform. 288 | * 289 | * This function should be called from loadEGLExternalPlatform after any 290 | * platform-specific initialization. 291 | */ 292 | void eplPlatformBaseInitFinish(EplPlatformData *plat); 293 | 294 | /** 295 | * Cleans up a EplPlatformData after an init failure. 296 | * 297 | * This function should be called from loadEGLExternalPlatform if the 298 | * platform-specicic initialization fails. 299 | */ 300 | void eplPlatformBaseInitFail(EplPlatformData *plat); 301 | 302 | /** 303 | * Looks up an EplDisplay struct. 304 | * 305 | * This will look up the display, lock it, and check to make sure that it's 306 | * initialized. 307 | * 308 | * The caller must call eplDisplayRelease to unlock and release the display. 309 | */ 310 | EplDisplay *eplDisplayAcquire(EGLDisplay edpy); 311 | 312 | /** 313 | * Returns the current EGLDisplay for the current thread. 314 | */ 315 | EGLDisplay eplGetCurrentDisplay(void); 316 | 317 | /** 318 | * Releases a display acquired with eplDisplayAcquire. 319 | */ 320 | void eplDisplayRelease(EplDisplay *pdpy); 321 | 322 | /** 323 | * Unlocks the mutex for an EplDisplay, but does not decrement the reference 324 | * count. 325 | * 326 | * This allows a platform library to temporarily release the mutex for an 327 | * EplDisplay, but ensures that the EplDisplay itself sticks around. 328 | * 329 | * The caller must call eplDisplayLock to lock the mutex again before calling 330 | * eplDisplayRelease. 331 | */ 332 | void eplDisplayUnlock(EplDisplay *pdpy); 333 | 334 | /** 335 | * Re-locks the mutex for an EplDisplay. 336 | */ 337 | void eplDisplayLock(EplDisplay *pdpy); 338 | 339 | /** 340 | * Looks up an internal EGLDisplay. If an EplInternalDisplay struct doesn't 341 | * already exist, then it will be created and returned. 342 | */ 343 | EplInternalDisplay *eplLookupInternalDisplay(EplPlatformData *platform, EGLDisplay handle); 344 | 345 | /** 346 | * Returns an EplInternalDisplay struct for a device. 347 | * 348 | * This is just a convenience wrapper which creates an EGLDisplay from the 349 | * device and then calls eplLookupInternalDisplay. 350 | */ 351 | EplInternalDisplay *eplGetDeviceInternalDisplay(EplPlatformData *platform, EGLDeviceEXT dev); 352 | 353 | /** 354 | * Calls eglInitialize on an internal display. 355 | */ 356 | EGLBoolean eplInitializeInternalDisplay(EplPlatformData *platform, 357 | EplInternalDisplay *idpy, EGLint *major, EGLint *minor); 358 | 359 | /** 360 | * Calls eglTerminate on an internal display. 361 | */ 362 | EGLBoolean eplTerminateInternalDisplay(EplPlatformData *platform, EplInternalDisplay *idpy); 363 | 364 | /** 365 | * Sets the current EGL error, and issues a debug message. 366 | */ 367 | void eplSetError(EplPlatformData *platform, EGLint error, const char *fmt, ...); 368 | 369 | /** 370 | * Looks up the EplSurface struct for a surface. 371 | * 372 | * This will lock the surface and increment its refcount. 373 | * 374 | * The caller must release the surface with \c eplSurfaceRelease. 375 | * 376 | * Note that this might return NULL if the surface is a pbuffer or stream. 377 | */ 378 | EplSurface *eplSurfaceAcquire(EplDisplay *pdpy, EGLSurface esurf); 379 | 380 | /** 381 | * Decrements the refcount for an EplSurface and unlocks it. 382 | */ 383 | void eplSurfaceRelease(EplDisplay *pdpy, EplSurface *psurf); 384 | 385 | /** 386 | * Replaces the current surface. 387 | * 388 | * If \p old_surface is the current surface, then this will call eglMakeCurrent 389 | * to switch to \p new_surface. 390 | * 391 | * This is used to deal with stuff like window resizing, where we might need to 392 | * replace the internal EGLSurface handle for a surface. 393 | */ 394 | EGLBoolean eplSwitchCurrentSurface(EplPlatformData *platform, EplDisplay *pdpy, 395 | EGLSurface old_surface, EGLSurface new_surface); 396 | 397 | /** 398 | * Returns a NULL-terminated array of all available EGLDeviceEXT handles. 399 | * 400 | * The caller must free the array using free(). 401 | */ 402 | EGLDeviceEXT *eplGetAllDevices(EplPlatformData *platform, EGLint *ret_count); 403 | 404 | /** 405 | * Locks and returns the list of EplDisplay structs. 406 | * 407 | * This can be used to deal with the application closing a native display out 408 | * from under us. 409 | * 410 | * The caller must call eplUnlockDisplayList after it's finished. 411 | */ 412 | struct glvnd_list *eplLockDisplayList(void); 413 | 414 | void eplUnlockDisplayList(void); 415 | 416 | #ifdef __cplusplus 417 | } 418 | #endif 419 | 420 | #endif // PLATFORM_BASE_H 421 | -------------------------------------------------------------------------------- /src/base/platform-impl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef PLATFORM_IMPL_H 19 | #define PLATFORM_IMPL_H 20 | 21 | /** 22 | * \file 23 | * 24 | * Functions that the platform implementation must implement. 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | #include 31 | 32 | #include "platform-base.h" 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | /** 39 | * A table of functions for the platform-specific implementation. 40 | */ 41 | typedef struct _EplImplFuncs 42 | { 43 | /** 44 | * Cleans up the platform data. 45 | */ 46 | void (* CleanupPlatform) (EplPlatformData *plat); 47 | 48 | /** 49 | * Handles the EGLExtPlatformExports::QueryString export. 50 | * 51 | * Note that \p pdpy will not be NULL. If the driver passes in an unknown 52 | * EGLDisplay, then the base library will simply return without calling 53 | * this function. That means that currently, there's no way to add an 54 | * extension to an EGLDisplay that the platform library doesn't own. 55 | * 56 | * \param pdpy The EplDisplay struct. 57 | */ 58 | const char * (* QueryString) (EplPlatformData *plat, EplDisplay *pdpy, EGLExtPlatformString name); 59 | 60 | /** 61 | * Checks if a pointer looks like a valid native display for a platform. 62 | * 63 | * This function is optional. If it's NULL, then it's equivalent to simply 64 | * returning EGL_FALSE. 65 | * 66 | * \param plat The EplPlatformData struct. 67 | * \param plat The native display pointer. 68 | * \return EGL_TRUE if \p nativeDisplay looks like a valid native display. 69 | */ 70 | EGLBoolean (* IsValidNativeDisplay) (EplPlatformData *plat, void *nativeDisplay); 71 | 72 | /** 73 | * Returns the hook function for an EGL function. 74 | * 75 | * This function is optional. If it's NULL, then there are no 76 | * platform-specific hook functions. 77 | * 78 | * \param plat The EplPlatformData struct. 79 | * \param name The EGL function name. 80 | * \return A function pointer, or NULL if there isn't aa hook for that 81 | * function. 82 | */ 83 | void * (* GetHookFunction) (EplPlatformData *plat, const char *name); 84 | 85 | /** 86 | * Checks if an eglGetPlatformDisplay call matches an existing EGLDisplay. 87 | * 88 | * Two eglGetPlatformDisplay calls with the same parameters are supposed to 89 | * return the same EGLDisplay. The base library checks the platform enum, 90 | * the native display pointer, and any attributes that the base library 91 | * itself handles. 92 | * 93 | * If there are additional platform-specific attributes, then this function 94 | * checks whether those attributes match an existing display, possibly 95 | * taking into account any default attribute values. 96 | * 97 | * If the implementation doesn't recognize an attribute, it may either 98 | * ignore the attribute or return EGL_FALSE. 99 | * 100 | * This function is optional. If it's NULL, then the platform does not 101 | * accept any platform-specific attributes, and so only the base library 102 | * checks described above apply. 103 | * 104 | * \param plat The EplPlatformData struct. 105 | * \param pdpy An existing EplDisplay struct to check. 106 | * \param platform The platform enum. 107 | * \param native_display The native display pointer. 108 | * \param attribs The remaining attributes. This array does not include 109 | * the attributes that the base library handles. 110 | * \return EGL_TRUE if the attributes match \p pdpy, and so 111 | * eglGetPlatformDisplay should return the EGLDisplay handle for it. 112 | */ 113 | EGLBoolean (* IsSameDisplay) (EplPlatformData *plat, EplDisplay *pdpy, EGLint platform, 114 | void *native_display, const EGLAttrib *attribs); 115 | 116 | /** 117 | * Called to implement eglGetPlatformDisplay. 118 | * 119 | * \param plat The EplPlatformData struct. 120 | * \param pdpy The base EplDisplay struct. This will be filled in already, 121 | * except for the \c internal_display member. 122 | * \param platform The platform enum. 123 | * \param native_display The native display pointer. 124 | * \param attribs The remaining attributes. This array does not include 125 | * the attributes that the base library handles. 126 | * 127 | * \return EGL_TRUE on success, or EGL_FALSE on failure. 128 | */ 129 | EGLBoolean (* GetPlatformDisplay) (EplPlatformData *plat, EplDisplay *pdpy, 130 | void *native_display, const EGLAttrib *attribs, 131 | struct glvnd_list *existing_displays); 132 | 133 | /** 134 | * Cleans up any implementation data in an EplDisplay. 135 | * 136 | * Currently, this is only called during teardown, but if/when 137 | * EGL_EXT_display_alloc is available, then this would be called to handle 138 | * destroying an EGLDisplay. 139 | * 140 | * If this is called during teardown, then the \c EplDisplay::platform pointer 141 | * may be NULL. 142 | * 143 | * Note that if a EGLDisplay is still initialized during teardown, then the 144 | * base library will call \c TerminateDisplay before \c CleanupDisplay. 145 | * 146 | * \param pdpy The EplDisplay struct to destroy. 147 | */ 148 | void (* CleanupDisplay) (EplDisplay *pdpy); 149 | 150 | /** 151 | * Called to implement eglInitialize. 152 | * 153 | * Note that the base library handles EGL_KHR_display_reference, so this 154 | * function is only ever called on an uninitialized display. 155 | * 156 | * \param plat The EplPlatformData struct. 157 | * \param pdpy The EplDisplay to initialize. 158 | * \param[out] major Returns the major version number. 159 | * \param[out] minor Returns the minor version number. 160 | * \return EGL_TRUE on success, EGL_FALSE on failure. 161 | */ 162 | EGLBoolean (* InitializeDisplay) (EplPlatformData *plat, EplDisplay *pdpy, EGLint *major, EGLint *minor); 163 | 164 | /** 165 | * Called to implement eglTerminate. 166 | * 167 | * Note that the base library handles EGL_KHR_display_reference, so this 168 | * function is only ever called on an initialized display. 169 | */ 170 | void (* TerminateDisplay) (EplPlatformData *plat, EplDisplay *pdpy); 171 | 172 | /** 173 | * Creates an EGLSurface for a window. 174 | * 175 | * This function is optional. If it's NULL, then the platform does not 176 | * support window surfaces. 177 | * 178 | * \param plat The EplPlatformData struct 179 | * \param pdpy The EplDisplay struct 180 | * \param psurf An EplSurface struct, with the base information filled in. 181 | * \param native_surface The native surface handle. 182 | * \param attribs The attribute list. 183 | * \param create_platform If this is true, then the call is from 184 | * eglCreatePlatformWindowSurface. If false, it's from 185 | * eglCreateWindowSurface. 186 | * \return The internal EGLSurface handle, or EGL_NO_SURFACE on failure. 187 | */ 188 | EGLSurface (* CreateWindowSurface) (EplPlatformData *plat, EplDisplay *pdpy, EplSurface *psurf, 189 | EGLConfig config, void *native_surface, const EGLAttrib *attribs, EGLBoolean create_platform); 190 | 191 | /** 192 | * Creates an EGLSurface for a pixmap. 193 | * 194 | * This function is optional. If it's NULL, then the platform does not 195 | * support pixmap surfaces. 196 | * 197 | * \param plat The EplPlatformData struct 198 | * \param pdpy The EplDisplay struct 199 | * \param psurf An EplSurface struct, with the base information filled in. 200 | * \param native_surface The native surface handle. 201 | * \param attribs The attribute list. 202 | * \param create_platform If this is true, then the call is from 203 | * eglCreatePlatformPixmapSurface. If false, it's from 204 | * eglCreatePixmapSurface. 205 | * \return The internal EGLSurface handle, or EGL_NO_SURFACE on failure. 206 | */ 207 | EGLSurface (* CreatePixmapSurface) (EplPlatformData *plat, EplDisplay *pdpy, EplSurface *psurf, 208 | EGLConfig config, void *native_surface, const EGLAttrib *attribs, EGLBoolean create_platform); 209 | 210 | /** 211 | * Called to handle eglDestroySurface and eglTerminate. 212 | * 213 | * Note that it's possible that the EplSurface struct itself might stick around 214 | * if another thread is holding a reference to it. 215 | * 216 | * \c FreeSurface is called when the refcount actually drops to zero. 217 | * 218 | * \param plat The EplPlatformData struct 219 | * \param pdpy The EplDisplay struct 220 | */ 221 | void (* DestroySurface) (EplDisplay *pdpy, EplSurface *psurf); 222 | 223 | /** 224 | * Called when an EplSurface is about to be freed. 225 | * 226 | * At this point, it's safe to assume that no other thread is going to touch 227 | * the surface, so the platform must free anything that it hasn't already freed 228 | * in \c DestroySurface. 229 | * 230 | * \param plat The EplPlatformData struct 231 | * \param pdpy The EplDisplay struct 232 | */ 233 | void (* FreeSurface) (EplDisplay *pdpy, EplSurface *psurf); 234 | 235 | /** 236 | * Implements eglSwapBuffers and eglSwapBuffersWithDamageEXT. 237 | * 238 | * If the application calls eglSwapBuffers, then \p rects will be NULL and 239 | * \p n_rects will be zero. 240 | * 241 | * \param plat The EplPlatformData struct 242 | * \param pdpy The EplDisplay struct 243 | * \param psurf The EplSurface struct. This will always be the thread's 244 | * current drawing surface. 245 | * \param rects The damage rectangles, or NULL. 246 | * \param n_rects The number of damage rectangles. 247 | * \return EGL_TRUE on success, EGL_FALSE on failure. 248 | */ 249 | EGLBoolean (* SwapBuffers) (EplPlatformData *plat, EplDisplay *pdpy, EplSurface *psurf, 250 | const EGLint *rects, EGLint n_rects); 251 | 252 | /** 253 | * Implements eglWaitGL and eglWaitClient. 254 | * 255 | * This function is optional. If it's NULL, then the base library will 256 | * not provide a hook function eglWaitGL or eglWaitClient, and so the 257 | * driver will follow its default behavior. 258 | * 259 | * \param pdpy The EplDisplay struct 260 | * \param psurf The current EplSurface, or NULL if there isn't a current 261 | * surface. 262 | * \return EGL_TRUE on success, EGL_FALSE on failure. 263 | */ 264 | EGLBoolean (*WaitGL) (EplDisplay *pdpy, EplSurface *psurf); 265 | 266 | /** 267 | * Implements eglWaitNative. 268 | * 269 | * This function is optional. If it's NULL, then the base library will not 270 | * provide a hook function eglWaitNative, and so the driver will follow its 271 | * default behavior. 272 | * 273 | * \param pdpy The EplDisplay struct 274 | * \param psurf The current EplSurface, or NULL if there isn't a current 275 | * surface. 276 | * \return EGL_TRUE on success, EGL_FALSE on failure. 277 | */ 278 | EGLBoolean (*WaitNative) (EplDisplay *pdpy, EplSurface *psurf); 279 | 280 | /** 281 | * Implements eglQueryDisplayAttribKHR/EXT/NV. 282 | * 283 | * This function is optional, if it's NULL, then the base library will 284 | * handle any attributes that it implements internally, and forward the 285 | * rest to the driver. 286 | * 287 | * If the platform doesn't recognize \p attrib, then it should forward the 288 | * function to the driver. 289 | * 290 | * \param pdpy The EplDisplay struct 291 | * \param attrib The attribute to look up. 292 | * \param[out] value Returns the value of the attribute. 293 | * \return EGL_TRUE on success, EGL_FALSE on failure. 294 | */ 295 | EGLBoolean (*QueryDisplayAttrib) (EplDisplay *pdpy, EGLint attrib, EGLAttrib *ret_value); 296 | } EplImplFuncs; 297 | 298 | #ifdef __cplusplus 299 | } 300 | #endif 301 | #endif // PLATFORM_IMPL_H 302 | -------------------------------------------------------------------------------- /src/base/platform-utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "platform-utils.h" 19 | 20 | #include 21 | #include 22 | 23 | static int HookFuncCmp(const void *key, const void *elem) 24 | { 25 | return strcmp((const char *) key, ((const EplHookFunc *) elem)->name); 26 | } 27 | 28 | void *eplFindHookFunction(const EplHookFunc *funcs, size_t count, const char *name) 29 | { 30 | const EplHookFunc *found = bsearch(name, funcs, count, sizeof(EplHookFunc), HookFuncCmp); 31 | if (found != NULL) 32 | { 33 | return found->func; 34 | } 35 | else 36 | { 37 | return NULL; 38 | } 39 | } 40 | 41 | EGLBoolean eplFindExtension(const char *extension, const char *extensions) 42 | { 43 | const char *start; 44 | const char *where, *terminator; 45 | 46 | if (extension == NULL || extensions == NULL) 47 | { 48 | return EGL_FALSE; 49 | } 50 | 51 | start = extensions; 52 | for (;;) { 53 | where = strstr(start, extension); 54 | if (!where) { 55 | break; 56 | } 57 | terminator = where + strlen(extension); 58 | if (where == start || *(where - 1) == ' ') { 59 | if (*terminator == ' ' || *terminator == '\0') { 60 | return EGL_TRUE; 61 | } 62 | } 63 | start = terminator; 64 | } 65 | 66 | return EGL_FALSE; 67 | } 68 | 69 | EGLBoolean eplInitRecursiveMutex(pthread_mutex_t *mutex) 70 | { 71 | pthread_mutexattr_t attr; 72 | 73 | if (pthread_mutexattr_init(&attr) != 0) 74 | { 75 | return EGL_FALSE; 76 | } 77 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 78 | if (pthread_mutex_init(mutex, &attr) != 0) 79 | { 80 | pthread_mutexattr_destroy(&attr); 81 | return EGL_FALSE; 82 | } 83 | 84 | pthread_mutexattr_destroy(&attr); 85 | return EGL_TRUE; 86 | } 87 | 88 | EGLint eplCountAttribs(const EGLAttrib *attribs) 89 | { 90 | EGLint count = 0; 91 | if (attribs != NULL) 92 | { 93 | while (attribs[count] != EGL_NONE) 94 | { 95 | count += 2; 96 | } 97 | } 98 | return count; 99 | } 100 | 101 | EGLint eplCountAttribs32(const EGLint *attribs) 102 | { 103 | EGLint count = 0; 104 | if (attribs != NULL) 105 | { 106 | while (attribs[count] != EGL_NONE) 107 | { 108 | count += 2; 109 | } 110 | } 111 | return count; 112 | } 113 | -------------------------------------------------------------------------------- /src/base/platform-utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef PLATFORM_UTILS_H 19 | #define PLATFORM_UTILS_H 20 | 21 | /** 22 | * \file 23 | * 24 | * Other utility functions that don't fit elsewhere. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | typedef struct 36 | { 37 | const char *name; 38 | void *func; 39 | } EplHookFunc; 40 | 41 | /** 42 | * Looks up a function from an array of EplHookFunc structs. 43 | * 44 | * This will use a binary search, so \p funcs must be sorted by name. 45 | */ 46 | void *eplFindHookFunction(const EplHookFunc *funcs, size_t count, const char *name); 47 | 48 | /** 49 | * Returns true if \p extension is listed in \p extensions. 50 | */ 51 | EGLBoolean eplFindExtension(const char *extension, const char *extensions); 52 | 53 | /** 54 | * Initializes a recursive mutex. 55 | */ 56 | EGLBoolean eplInitRecursiveMutex(pthread_mutex_t *mutex); 57 | 58 | /** 59 | * Returns the length of an attribute array. 60 | * 61 | * \param attribs An EGLAttrib array, or NULL. 62 | * \return The length of the array, not including the EGL_NONE at the end. This 63 | * will always be a multiple of 2. 64 | */ 65 | EGLint eplCountAttribs(const EGLAttrib *attribs); 66 | 67 | EGLint eplCountAttribs32(const EGLint *attribs); 68 | 69 | #ifdef __cplusplus 70 | } 71 | #endif 72 | 73 | #endif // PLATFORM_UTILS_H 74 | -------------------------------------------------------------------------------- /src/base/refcountobj.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "refcountobj.h" 19 | 20 | #include 21 | #include 22 | 23 | void eplRefCountInit(EplRefCount *obj) 24 | { 25 | obj->refcount = 1; 26 | } 27 | 28 | EplRefCount *eplRefCountRef(EplRefCount *obj) 29 | { 30 | if (obj != NULL) 31 | { 32 | __sync_add_and_fetch(&obj->refcount, 1); 33 | } 34 | return obj; 35 | } 36 | 37 | int eplRefCountUnref(EplRefCount *obj) 38 | { 39 | if (obj != NULL) 40 | { 41 | unsigned int prev = __sync_fetch_and_sub(&obj->refcount, 1); 42 | assert(prev > 0); 43 | if (prev == 1) 44 | { 45 | // If we were using a mutex, then this is where we'd destroy it. 46 | return 1; 47 | } 48 | } 49 | return 0; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /src/base/refcountobj.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef REFCOUNTOBJ_H 19 | #define REFCOUNTOBJ_H 20 | 21 | #include 22 | 23 | /** 24 | * \file 25 | * 26 | * Helper functions for reference-counted structures. 27 | */ 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | /** 34 | * A simple atomic refcount struct. 35 | * 36 | * This is intended to be embedded in a larger struct. 37 | */ 38 | typedef struct 39 | { 40 | unsigned int refcount; 41 | } EplRefCount; 42 | 43 | /** 44 | * Initializes an \c EplRefCount struct. 45 | * 46 | * This will set the refcount to 1, and (if necessary) initialize a mutex. 47 | */ 48 | void eplRefCountInit(EplRefCount *obj); 49 | 50 | /** 51 | * Increments the refcount for an \c EplRefCount. Does nothing if \p obj is 52 | * NULL. 53 | * 54 | * \param obj The object to reference, or NULL. 55 | * \return \p obj 56 | */ 57 | EplRefCount *eplRefCountRef(EplRefCount *obj); 58 | 59 | /** 60 | * Decrements the refcount of an \c EplRefCount. Does nothing if \p obj is 61 | * NULL. 62 | * 63 | * If the reference count drops to zero, then this function will clean up the 64 | * \c EplRefCount struct and return non-zero. 65 | * 66 | * \param obj The object to unreference. 67 | * \return non-zero if the reference count is now zero, and so the object 68 | * should be destroyed. 69 | */ 70 | int eplRefCountUnref(EplRefCount *obj); 71 | 72 | /** 73 | * Declares functions to reference and unreference an EplRefCount-based struct. 74 | * 75 | * \param type The name of the larger struct. 76 | * \param prefix The prefix of the ref and unref functions. "Ref" and "Unref" 77 | * will be appended to this. 78 | */ 79 | #define EPL_REFCOUNT_DECLARE_TYPE_FUNCS(type, prefix) \ 80 | type *prefix##Ref(type *obj); \ 81 | void prefix##Unref(type *obj); 82 | 83 | /** 84 | * Defines functions to reference and unreference an EplRefCount-based struct. 85 | * 86 | * \param type The name of the larger struct. 87 | * \param prefix The prefix of the ref and unref functions. "Ref" and "Unref" 88 | * will be appended to this. 89 | * \param member The name of the embedded EplRefCount struct. 90 | * \param freefunc A function to destroy an object when its refcount reaches 91 | * zero. This function should take a single \p type pointer as a parameter. 92 | */ 93 | #define EPL_REFCOUNT_DEFINE_TYPE_FUNCS(type, prefix, member, freefunc) \ 94 | type *prefix##Ref(type *obj) { \ 95 | if (obj != NULL) eplRefCountRef(&obj->member); \ 96 | return obj; \ 97 | } \ 98 | void prefix##Unref(type *obj) { \ 99 | if (obj != NULL && eplRefCountUnref(&obj->member)) { \ 100 | freefunc(obj); \ 101 | } \ 102 | } 103 | 104 | #ifdef __cplusplus 105 | } 106 | #endif 107 | #endif // REFCOUNTOBJ_H 108 | -------------------------------------------------------------------------------- /src/x11/20_nvidia_xcb.json: -------------------------------------------------------------------------------- 1 | { 2 | "file_format_version" : "1.0.0", 3 | "ICD" : { 4 | "library_path" : "libnvidia-egl-xcb.so.1" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/x11/20_nvidia_xlib.json: -------------------------------------------------------------------------------- 1 | { 2 | "file_format_version" : "1.0.0", 3 | "ICD" : { 4 | "library_path" : "libnvidia-egl-xlib.so.1" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/x11/dma-buf.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 | /* 3 | * Framework for buffer objects that can be shared across devices/subsystems. 4 | * 5 | * Copyright(C) 2015 Intel Ltd 6 | * 7 | * This program is free software; you can redistribute it and/or modify it 8 | * under the terms of the GNU General Public License version 2 as published by 9 | * the Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 | * more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * this program. If not, see . 18 | */ 19 | 20 | #ifndef _DMA_BUF_UAPI_H_ 21 | #define _DMA_BUF_UAPI_H_ 22 | 23 | #if defined(__linux__) 24 | 25 | #include 26 | 27 | #else /* One of the BSDs */ 28 | 29 | #include 30 | #include 31 | 32 | typedef int8_t __s8; 33 | typedef uint8_t __u8; 34 | typedef int16_t __s16; 35 | typedef uint16_t __u16; 36 | typedef int32_t __s32; 37 | typedef uint32_t __u32; 38 | typedef int64_t __s64; 39 | typedef uint64_t __u64; 40 | 41 | #endif 42 | 43 | /** 44 | * struct dma_buf_sync - Synchronize with CPU access. 45 | * 46 | * When a DMA buffer is accessed from the CPU via mmap, it is not always 47 | * possible to guarantee coherency between the CPU-visible map and underlying 48 | * memory. To manage coherency, DMA_BUF_IOCTL_SYNC must be used to bracket 49 | * any CPU access to give the kernel the chance to shuffle memory around if 50 | * needed. 51 | * 52 | * Prior to accessing the map, the client must call DMA_BUF_IOCTL_SYNC 53 | * with DMA_BUF_SYNC_START and the appropriate read/write flags. Once the 54 | * access is complete, the client should call DMA_BUF_IOCTL_SYNC with 55 | * DMA_BUF_SYNC_END and the same read/write flags. 56 | * 57 | * The synchronization provided via DMA_BUF_IOCTL_SYNC only provides cache 58 | * coherency. It does not prevent other processes or devices from 59 | * accessing the memory at the same time. If synchronization with a GPU or 60 | * other device driver is required, it is the client's responsibility to 61 | * wait for buffer to be ready for reading or writing before calling this 62 | * ioctl with DMA_BUF_SYNC_START. Likewise, the client must ensure that 63 | * follow-up work is not submitted to GPU or other device driver until 64 | * after this ioctl has been called with DMA_BUF_SYNC_END? 65 | * 66 | * If the driver or API with which the client is interacting uses implicit 67 | * synchronization, waiting for prior work to complete can be done via 68 | * poll() on the DMA buffer file descriptor. If the driver or API requires 69 | * explicit synchronization, the client may have to wait on a sync_file or 70 | * other synchronization primitive outside the scope of the DMA buffer API. 71 | */ 72 | struct dma_buf_sync { 73 | /** 74 | * @flags: Set of access flags 75 | * 76 | * DMA_BUF_SYNC_START: 77 | * Indicates the start of a map access session. 78 | * 79 | * DMA_BUF_SYNC_END: 80 | * Indicates the end of a map access session. 81 | * 82 | * DMA_BUF_SYNC_READ: 83 | * Indicates that the mapped DMA buffer will be read by the 84 | * client via the CPU map. 85 | * 86 | * DMA_BUF_SYNC_WRITE: 87 | * Indicates that the mapped DMA buffer will be written by the 88 | * client via the CPU map. 89 | * 90 | * DMA_BUF_SYNC_RW: 91 | * An alias for DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE. 92 | */ 93 | __u64 flags; 94 | }; 95 | 96 | #define DMA_BUF_SYNC_READ (1 << 0) 97 | #define DMA_BUF_SYNC_WRITE (2 << 0) 98 | #define DMA_BUF_SYNC_RW (DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE) 99 | #define DMA_BUF_SYNC_START (0 << 2) 100 | #define DMA_BUF_SYNC_END (1 << 2) 101 | #define DMA_BUF_SYNC_VALID_FLAGS_MASK \ 102 | (DMA_BUF_SYNC_RW | DMA_BUF_SYNC_END) 103 | 104 | #define DMA_BUF_NAME_LEN 32 105 | 106 | /** 107 | * struct dma_buf_export_sync_file - Get a sync_file from a dma-buf 108 | * 109 | * Userspace can perform a DMA_BUF_IOCTL_EXPORT_SYNC_FILE to retrieve the 110 | * current set of fences on a dma-buf file descriptor as a sync_file. CPU 111 | * waits via poll() or other driver-specific mechanisms typically wait on 112 | * whatever fences are on the dma-buf at the time the wait begins. This 113 | * is similar except that it takes a snapshot of the current fences on the 114 | * dma-buf for waiting later instead of waiting immediately. This is 115 | * useful for modern graphics APIs such as Vulkan which assume an explicit 116 | * synchronization model but still need to inter-operate with dma-buf. 117 | * 118 | * The intended usage pattern is the following: 119 | * 120 | * 1. Export a sync_file with flags corresponding to the expected GPU usage 121 | * via DMA_BUF_IOCTL_EXPORT_SYNC_FILE. 122 | * 123 | * 2. Submit rendering work which uses the dma-buf. The work should wait on 124 | * the exported sync file before rendering and produce another sync_file 125 | * when complete. 126 | * 127 | * 3. Import the rendering-complete sync_file into the dma-buf with flags 128 | * corresponding to the GPU usage via DMA_BUF_IOCTL_IMPORT_SYNC_FILE. 129 | * 130 | * Unlike doing implicit synchronization via a GPU kernel driver's exec ioctl, 131 | * the above is not a single atomic operation. If userspace wants to ensure 132 | * ordering via these fences, it is the respnosibility of userspace to use 133 | * locks or other mechanisms to ensure that no other context adds fences or 134 | * submits work between steps 1 and 3 above. 135 | */ 136 | struct dma_buf_export_sync_file { 137 | /** 138 | * @flags: Read/write flags 139 | * 140 | * Must be DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE, or both. 141 | * 142 | * If DMA_BUF_SYNC_READ is set and DMA_BUF_SYNC_WRITE is not set, 143 | * the returned sync file waits on any writers of the dma-buf to 144 | * complete. Waiting on the returned sync file is equivalent to 145 | * poll() with POLLIN. 146 | * 147 | * If DMA_BUF_SYNC_WRITE is set, the returned sync file waits on 148 | * any users of the dma-buf (read or write) to complete. Waiting 149 | * on the returned sync file is equivalent to poll() with POLLOUT. 150 | * If both DMA_BUF_SYNC_WRITE and DMA_BUF_SYNC_READ are set, this 151 | * is equivalent to just DMA_BUF_SYNC_WRITE. 152 | */ 153 | __u32 flags; 154 | /** @fd: Returned sync file descriptor */ 155 | __s32 fd; 156 | }; 157 | 158 | /** 159 | * struct dma_buf_import_sync_file - Insert a sync_file into a dma-buf 160 | * 161 | * Userspace can perform a DMA_BUF_IOCTL_IMPORT_SYNC_FILE to insert a 162 | * sync_file into a dma-buf for the purposes of implicit synchronization 163 | * with other dma-buf consumers. This allows clients using explicitly 164 | * synchronized APIs such as Vulkan to inter-op with dma-buf consumers 165 | * which expect implicit synchronization such as OpenGL or most media 166 | * drivers/video. 167 | */ 168 | struct dma_buf_import_sync_file { 169 | /** 170 | * @flags: Read/write flags 171 | * 172 | * Must be DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE, or both. 173 | * 174 | * If DMA_BUF_SYNC_READ is set and DMA_BUF_SYNC_WRITE is not set, 175 | * this inserts the sync_file as a read-only fence. Any subsequent 176 | * implicitly synchronized writes to this dma-buf will wait on this 177 | * fence but reads will not. 178 | * 179 | * If DMA_BUF_SYNC_WRITE is set, this inserts the sync_file as a 180 | * write fence. All subsequent implicitly synchronized access to 181 | * this dma-buf will wait on this fence. 182 | */ 183 | __u32 flags; 184 | /** @fd: Sync file descriptor */ 185 | __s32 fd; 186 | }; 187 | 188 | #define DMA_BUF_BASE 'b' 189 | #define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync) 190 | 191 | /* 32/64bitness of this uapi was botched in android, there's no difference 192 | * between them in actual uapi, they're just different numbers. 193 | */ 194 | #define DMA_BUF_SET_NAME _IOW(DMA_BUF_BASE, 1, const char *) 195 | #define DMA_BUF_SET_NAME_A _IOW(DMA_BUF_BASE, 1, __u32) 196 | #define DMA_BUF_SET_NAME_B _IOW(DMA_BUF_BASE, 1, __u64) 197 | #define DMA_BUF_IOCTL_EXPORT_SYNC_FILE _IOWR(DMA_BUF_BASE, 2, struct dma_buf_export_sync_file) 198 | #define DMA_BUF_IOCTL_IMPORT_SYNC_FILE _IOW(DMA_BUF_BASE, 3, struct dma_buf_import_sync_file) 199 | 200 | #endif 201 | -------------------------------------------------------------------------------- /src/x11/driver-platform-surface.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | /** 19 | * \file 20 | * 21 | * The platform surface interface for the NVIDIA driver. 22 | * 23 | * This interface provides a new EGLSurface which renders to caller-allocated 24 | * color buffers. Conceptually, it's similar to an FBO, but at the EGL level 25 | * instead of OpenGL. 26 | * 27 | * Note that this interface is still somewhat experimental, and might change in 28 | * backwards-incompatible ways. 29 | */ 30 | 31 | #ifndef DRIVER_PLATFORM_SURFACE_H 32 | #define DRIVER_PLATFORM_SURFACE_H 33 | 34 | #include 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif 39 | 40 | #define EGL_PLATFORM_SURFACE_UPDATE_CALLBACK_NVX ((EGLAttrib) 0x80000001U) 41 | #define EGL_PLATFORM_SURFACE_UPDATE_CALLBACK_PARAM_NVX ((EGLAttrib) 0x80000002U) 42 | #define EGL_PLATFORM_SURFACE_DAMAGE_CALLBACK_NVX ((EGLAttrib) 0x80000003U) 43 | #define EGL_PLATFORM_SURFACE_DAMAGE_CALLBACK_PARAM_NVX ((EGLAttrib) 0x80000004U) 44 | #define EGL_PLATFORM_SURFACE_BLIT_TARGET_NVX ((EGLAttrib) 0x80000005U) 45 | 46 | /** 47 | * If this attribute is EGL_TRUE, then the surface will treat the origin as the 48 | * top-left corner (the convention used by e.g., x11). 49 | * 50 | * If it's EGL_FALSE, then the origin is in the bottom-left corner (the 51 | * convention used by OpenGL textures). 52 | * 53 | * We re-use the same value as EGL_WAYLAND_Y_INVERTED_WL for this, since it's 54 | * more-or-less the same meaning. 55 | */ 56 | #define EGL_SURFACE_Y_INVERTED_NVX 0x31DB 57 | 58 | #define EGL_PLATFORM_SURFACE_INTERFACE_MAJOR_VERSION 0 59 | #define EGL_PLATFORM_SURFACE_INTERFACE_MINOR_VERSION 1 60 | 61 | static inline EGLint EGL_PLATFORM_SURFACE_INTERFACE_GET_MAJOR_VERSION(EGLint version) 62 | { 63 | return version >> 16; 64 | } 65 | static inline EGLint EGL_PLATFORM_SURFACE_INTERFACE_GET_MINOR_VERSION(EGLint version) 66 | { 67 | return version & 0xFFFF; 68 | } 69 | 70 | /** 71 | * Checks if the version number reported by the driver is compatible. 72 | * 73 | * \param driver_version The version number reported by \c eglPlatformGetVersionNVX. 74 | * \param major_version The major version number that the library expects. 75 | * \param min_minor_version The minimum minor version number that the library requires. 76 | * \return EGL_TRUE if the driver's version is compatible. 77 | */ 78 | static inline EGLBoolean EGL_PLATFORM_SURFACE_INTERFACE_CHECK_VERSION(EGLint driver_version, 79 | EGLint major_version, EGLint min_minor_version) 80 | { 81 | return EGL_PLATFORM_SURFACE_INTERFACE_GET_MAJOR_VERSION(driver_version) == major_version 82 | && EGL_PLATFORM_SURFACE_INTERFACE_GET_MINOR_VERSION(driver_version) >= min_minor_version; 83 | } 84 | 85 | /** 86 | * An opaque handle to a color buffer. 87 | * 88 | * Note that a color buffer may only be attached to one attachment point of one 89 | * EGLSurface at a time. 90 | * 91 | * Detaching a buffer from one surface and then attaching it to another is 92 | * allowed, but may incur a performance cost (e.g., due to reallocating 93 | * multisample buffers). 94 | */ 95 | typedef struct EGLPlatformColorBufferNVXRec *EGLPlatformColorBufferNVX; 96 | 97 | /** 98 | * A callback to update an EGLSurface. This is used for dealing with window 99 | * resizes and similar. 100 | * 101 | * Because this is called from within the driver, it's not safe to call any EGL 102 | * or OpenGL functions from this callback. The callback may only call functions 103 | * that are explicitly defined as safe. Calling any other function may result 104 | * in undefined behavior. 105 | * 106 | * This function is only ever called for the current EGLSurface, or from an 107 | * eglMakeCurrent call that's setting the EGLSurface to be current. Thus, the 108 | * platform library can safely assume that the EGLSurface will not be current 109 | * to any other thread, and that there will not be a concurrent call to 110 | * eglSwapBuffers for this surface. 111 | * 112 | * \param param The pointer that was passed as the 113 | * \c EGL_PLATFORM_SURFACE_UPDATE_CALLBACK_PARAM_NVX attribute to 114 | * \c eglPlatformCreateSurfaceNVX. 115 | */ 116 | typedef void (* EGLExtPlatformSurfaceUpdateCallback) (void *param); 117 | 118 | /** 119 | * A callback to handle front- or single-buffered rendering for EGL platform 120 | * surfaces. 121 | * 122 | * For a double-buffered EGLSurface, this is called when the front buffer 123 | * changed. 124 | * 125 | * For a single-buffered EGLSurface, this is called when single buffer changed. 126 | * 127 | * In both cases, it's called after a flush, so whatever rendering happened is 128 | * in progress. 129 | * 130 | * The callback must not call back into the driver at all. Calling any 131 | * driver function, including the functions that would be safe from 132 | * \c EGLExtPlatformSurfaceUpdateCallback, may result in undefined behavior. 133 | * 134 | * As with \c EGLExtPlatformSurfaceDamageCallback, this function is only called 135 | * for the current thread's current surface. Therefore, the platform library 136 | * can assume that there will not be a concurrent call to eglSwapBuffers for 137 | * the same EGLSurface on any other thread. 138 | * 139 | * \note The driver will close \p syncfd after the callback returns. If the 140 | * platform library needs to hold onto it, then it must call dup(). 141 | * 142 | * \param param A pointer to caller-specific data. This is the pointer passed 143 | * as the EGL_PLATFORM_SURFACE_DAMAGE_CALLBACK_PARAM_NVX attribute to 144 | * \c eglPlatformCreateSurfaceNVX. 145 | * \param syncfd A file descriptor for a binary fence, or -1. If this is -1, 146 | * then the driver will have already waited for any rendering to finish. 147 | * \param flags Currently unused. 148 | */ 149 | typedef void (* EGLExtPlatformSurfaceDamageCallback) (void *param, int syncfd, unsigned int flags); 150 | 151 | /** 152 | * Returns a version number for the platform surface interface. 153 | * 154 | * This version contains a major version number in the high-order 16 bits, and 155 | * a minor version number in the low-order 16 bits. 156 | * 157 | * The major version will change if there's ever an interface change that 158 | * would break backwards compatibility. 159 | * 160 | * The minor version number will be incremented for new, backwards-compatible 161 | * functions or features. 162 | * 163 | * Use \c EGL_PLATFORM_SURFACE_INTERFACE_GET_MAJOR_VERSION and 164 | * \c EGL_PLATFORM_SURFACE_INTERFACE_GET_MINOR_VERSION to extract the major 165 | * and minor version numbers. 166 | * 167 | * As the interface is not yet stable, neither backward nor forward 168 | * compatibility is guaranteed between different versions. 169 | */ 170 | typedef EGLint (* pfn_eglPlatformGetVersionNVX) (void); 171 | 172 | /** 173 | * Creates an EGLPlatformColorBufferNVX from a dma-buf. 174 | * 175 | * After this function returns, the caller may close the file descriptor. 176 | * 177 | * Note that the caller must free the color buffer by calling 178 | * \c eglPlatformFreeColorBufferNVX. The driver will not free the color buffers 179 | * when the display is terminated. 180 | * 181 | * This function may be called from the update callback. 182 | * 183 | * \param dpy The internal EGLDisplay handle. 184 | * \param fd The dma-buf file descriptor to import. 185 | * \param width The width of the image. 186 | * \param height The height of the image. 187 | * \param format The fourcc format code. 188 | * \param stride The stride or pitch of the image. 189 | * \param offset The offset of the image. 190 | * \param modifier The format modifier of the image. 191 | * 192 | * \return A new EGLPlatformColorBufferNVX handle, or NULL on error. 193 | */ 194 | typedef EGLPlatformColorBufferNVX (* pfn_eglPlatformImportColorBufferNVX) (EGLDisplay dpy, 195 | int fd, int width, int height, int format, int stride, int offset, 196 | unsigned long long modifier); 197 | 198 | /** 199 | * Allocates a color buffer. 200 | * 201 | * Note that for a simple renderable buffer, a platform library should 202 | * generally allocate the buffer using libgbm and then import it using 203 | * \c eglPlatformImportColorBufferNVX, because libgbm allows the driver to 204 | * select an optimal format modifier. 205 | * 206 | * However, this function can be used to allocate a buffer in system memory. 207 | * 208 | * The caller can use \c eglPlatformExportColorBufferNVX to export the buffer 209 | * as a dma-buf. 210 | * 211 | * This function may be called from the update callback. 212 | * 213 | * \param dpy The internal EGLDisplay handle. 214 | * \param width The width of the image. 215 | * \param height The height of the image. 216 | * \param format The fourcc format code. 217 | * \param modifier The format modifier of the image. 218 | * \param force_sysmem If true, then allocate the buffer in sysmem, not in 219 | * vidmem. 220 | */ 221 | typedef EGLPlatformColorBufferNVX (* pfn_eglPlatformAllocColorBufferNVX) (EGLDisplay dpy, 222 | int width, int height, int format, unsigned long long modifier, 223 | EGLBoolean force_sysmem); 224 | 225 | /** 226 | * Exports a color buffer as a dma-buf. 227 | * 228 | * This function returns a dma-buf file descriptor for a color buffer, along 229 | * with the necessary parameters to import it elsewhere. 230 | * 231 | * Any of the output parameters may be NULL if the caller does not require 232 | * them. 233 | * 234 | * \param dpy The internal EGLDisplay handle. 235 | * \param buffer The color buffer to export. 236 | * \param[out] ret_fd Returns the dma-buf file descriptor. The caller 237 | * is responsible for closing it. 238 | * \param[out] ret_width Returns the width of the image. 239 | * \param[out] ret_height Returns the height of the image. 240 | * \param[out] ret_format Returns the fourcc format code of the image. 241 | * \param[out] ret_stride Returns the stride of the image. 242 | * \param[out] ret_offset Returns the offset of the image. 243 | * \param[out] ret_modifier Returns the format modifier of the image. 244 | * 245 | * \return EGL_TRUE on success, or EGL_FALSE on error. 246 | */ 247 | typedef EGLBoolean (* pfn_eglPlatformExportColorBufferNVX) (EGLDisplay dpy, EGLPlatformColorBufferNVX buffer, 248 | int *ret_fd, int *ret_width, int *ret_height, int *ret_format, int *ret_stride, int *ret_offset, 249 | unsigned long long *ret_modifier); 250 | 251 | /** 252 | * Copies an image between two buffers. 253 | * 254 | * Currently, this function only supports copying to a pitch linear buffer. 255 | * 256 | * The copy operation occurs as part of the OpenGL command stream for the 257 | * current context, so any rendering that the current context did to \p src 258 | * will complete before the copy. The caller can use normal synchronization 259 | * functions (glFinish, EGLSync objects, etc) to wait for the copy to finish. 260 | * 261 | * This function may NOT be called from the update callback. 262 | * 263 | * \param dpy The internal EGLDisplay handle. The display must be current. 264 | * \param src The source buffer. 265 | * \param dst The destination buffer. Currently, this must be a pitch linear 266 | * image. 267 | * \return EGL_TRUE on success, or EGL_FALSE on failure. 268 | */ 269 | typedef EGLBoolean (* pfn_eglPlatformCopyColorBufferNVX) (EGLDisplay dpy, 270 | EGLPlatformColorBufferNVX src, 271 | EGLPlatformColorBufferNVX dst); 272 | 273 | /** 274 | * Frees a color buffer. 275 | * 276 | * If \p buffer is attached to an EGLSurface, then it will be freed when it is 277 | * detached, or when the EGLSurface is destroyed. 278 | * 279 | * This function may be called from the update callback. 280 | * 281 | * \param dpy The internal EGLDisplay handle. 282 | * \param buffer The EGLPlatformColorBufferNVX to free. 283 | */ 284 | typedef void (* pfn_eglPlatformFreeColorBufferNVX) (EGLDisplay dpy, 285 | EGLPlatformColorBufferNVX buffer); 286 | 287 | /** 288 | * Creates a new platform surface. 289 | * 290 | * The color buffers are specified in the \p platformAttribs array, using 291 | * GL_FRONT and GL_BACK for the front and back buffer. 292 | * 293 | * The update and damage callbacks can be specified in the \p platformAttribs 294 | * as well. 295 | * 296 | * The \p platformAttribs array is a separate EGLAttrib array so that there 297 | * isn't any risk of conflict with existing or future EGL enums. 298 | * 299 | * The driver accepts the same set of EGLConfigs for platform surfaces that it 300 | * does for streams, so \p config must have EGL_STREAM_BIT_KHR set. 301 | * 302 | * This function may NOT be called from the update callback. 303 | * 304 | * \param dpy The internal EGLDisplay handle. 305 | * \param config The EGLConfig to use for the new surface 306 | * \param platformAttribs The color buffers and other platform library attributes. 307 | * \param attribs An array of other attributes. 308 | * 309 | * \return A new EGLSurface, or EGL_NO_SURFACE on error. 310 | */ 311 | typedef EGLSurface (* pfn_eglPlatformCreateSurfaceNVX) (EGLDisplay dpy, EGLConfig config, 312 | const EGLAttrib *platformAttribs, const EGLAttrib *attribs); 313 | 314 | /** 315 | * Sets the color buffers for an EGLSurface. 316 | * 317 | * This function may be called from the update callback, but only for the 318 | * surface that's being updated. 319 | * 320 | * The buffers are specified in an EGLAttrib array, using the same attributes 321 | * as eglPlatformCreateSurfaceNVX. 322 | * 323 | * Outside the update callback, this function may only be called for the 324 | * current thread's current surface (e.g., from eglSwapBuffers). If the surface 325 | * is not current, then the result is undefined. 326 | * 327 | * The EGLSurface must remain single- or double-buffered. For example, if the 328 | * EGLSurface was created with only a back buffer, then the platform library 329 | * may not attach a front buffer using eglPlatformSetColorBuffersNVX. 330 | * 331 | * The platform library may, however, change the EGL_PLATFORM_SURFACE_BLIT_TARGET_NVX 332 | * attachment between NULL and non-NULL. 333 | * 334 | * This function may be called from the update callback. 335 | * 336 | * \param dpy The internal EGLDisplay handle. 337 | * \param surf The EGLSurface handle. 338 | * \param buffers The new buffers, terminated by EGL_NONE. 339 | * 340 | * \return EGL_TRUE on success, or EGL_FALSE on failure. 341 | */ 342 | typedef EGLBoolean (* pfn_eglPlatformSetColorBuffersNVX) (EGLDisplay dpy, 343 | EGLSurface surf, const EGLAttrib *buffers); 344 | 345 | /** 346 | * Returns EGLConfig attributes. 347 | * 348 | * This function is equivalent to eglGetConfigAttrib, but with additional 349 | * attributes. 350 | * 351 | * Currently, this function supports \c EGL_LINUX_DRM_FOURCC_EXT, which returns 352 | * a fourcc format code for the EGLConfig, or DRM_FORMAT_INVALID if the config 353 | * doesn't have a corresponding fourcc code. 354 | * 355 | * \param dpy The internal EGLDisplay handle. 356 | * \param config The EGLConfig to query. 357 | * \param attribute The attribute to look up, which may be 358 | * EGL_LINUX_DRM_FOURCC_EXT or any attribute that eglGetConfigAttrib 359 | * supports. 360 | * \param[out] value Returns the attribute value. 361 | * 362 | * \return EGL_TRUE on success, or EGL_FALSE on error. 363 | */ 364 | typedef EGLBoolean (* pfn_eglPlatformGetConfigAttribNVX) (EGLDisplay dpy, 365 | EGLConfig config, EGLint attribute, EGLint *value); 366 | 367 | #ifdef __cplusplus 368 | } 369 | #endif 370 | #endif // DRIVER_PLATFORM_SURFACE_H 371 | -------------------------------------------------------------------------------- /src/x11/meson.build: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | dep_gbm = dependency('gbm') 17 | dep_x11 = dependency('x11', required: get_option('xlib')) 18 | dep_x11_xcb = dependency('x11-xcb', required: get_option('xlib')) 19 | dep_xcb = dependency('xcb') 20 | dep_xcb_present = dependency('xcb-present') 21 | dep_xcb_dri3 = dependency('xcb-dri3') 22 | dep_dl = meson.get_compiler('c').find_library('dl', required : false) 23 | 24 | enable_xlib = (get_option('xlib').allowed() and dep_x11.found() and dep_x11_xcb.found()) 25 | 26 | x11_deps = [ 27 | dep_libdrm, 28 | dep_threads, 29 | dep_gbm, 30 | dep_xcb, 31 | dep_xcb_present, 32 | dep_xcb_dri3, 33 | dep_dl, 34 | ] 35 | 36 | x11_common_source = [ 37 | 'x11-platform.c', 38 | 'x11-config.c', 39 | 'x11-window.c', 40 | 'x11-pixmap.c', 41 | 'x11-timeline.c', 42 | ] 43 | 44 | if get_option('xcb') 45 | xcb_platform = shared_library('nvidia-egl-xcb', 46 | [ 47 | x11_common_source, 48 | 'x11-platform-xcb.c' 49 | ], 50 | include_directories: [ inc_base ], 51 | c_args : ['-D_GNU_SOURCE'], 52 | dependencies: [ 53 | x11_deps, 54 | dep_eglexternal, 55 | ], 56 | link_with: [ platform_base ], 57 | version : meson.project_version(), 58 | gnu_symbol_visibility: 'hidden', 59 | install: true) 60 | 61 | install_data('20_nvidia_xcb.json', 62 | install_dir: '@0@/egl/egl_external_platform.d'.format(get_option('datadir'))) 63 | endif 64 | 65 | if enable_xlib 66 | xlib_platform = shared_library('nvidia-egl-xlib', 67 | [ 68 | x11_common_source, 69 | 'x11-platform-xlib.c', 70 | ], 71 | include_directories: [ inc_base ], 72 | c_args : ['-D_GNU_SOURCE'], 73 | dependencies: [ 74 | x11_deps, 75 | dep_x11, 76 | dep_x11_xcb, 77 | dep_eglexternal, 78 | ], 79 | link_with: [ platform_base ], 80 | version : meson.project_version(), 81 | gnu_symbol_visibility: 'hidden', 82 | install: true) 83 | 84 | install_data('20_nvidia_xlib.json', 85 | install_dir: '@0@/egl/egl_external_platform.d'.format(get_option('datadir'))) 86 | endif 87 | -------------------------------------------------------------------------------- /src/x11/x11-config.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | /** 19 | * \file 20 | * 21 | * Stuff for handling EGLConfigs and formats. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | #include "x11-platform.h" 32 | #include "config-list.h" 33 | 34 | static int CompareFormatSupportInfo(const void *p1, const void *p2) 35 | { 36 | uint32_t v1 = *((const uint32_t *) p1); 37 | uint32_t v2 = *((const uint32_t *) p2); 38 | if (v1 < v2) 39 | { 40 | return -1; 41 | } 42 | else if (v1 > v2) 43 | { 44 | return 1; 45 | } 46 | else 47 | { 48 | return 0; 49 | } 50 | } 51 | 52 | static EGLBoolean InitDriverFormatModifiers(EplPlatformData *plat, 53 | EGLDisplay internal_display, uint32_t fourcc, X11DriverFormat *support) 54 | { 55 | const EplFormatInfo *fmt = eplFormatInfoLookup(fourcc); 56 | EGLuint64KHR *modifiers = NULL; 57 | EGLBoolean *external = NULL; 58 | EGLint num = 0; 59 | EGLint i; 60 | 61 | if (fmt == NULL) 62 | { 63 | return EGL_FALSE; 64 | } 65 | 66 | if (!plat->priv->egl.QueryDmaBufModifiersEXT(internal_display, fmt->fourcc, 0, NULL, NULL, &num) || num <= 0) 67 | { 68 | return EGL_FALSE; 69 | } 70 | 71 | modifiers = malloc(num * (sizeof(EGLuint64KHR) + sizeof(EGLBoolean))); 72 | if (modifiers == EGL_FALSE) 73 | { 74 | return EGL_FALSE; 75 | } 76 | external = (EGLBoolean *) (modifiers + num); 77 | 78 | if (!plat->priv->egl.QueryDmaBufModifiersEXT(internal_display, fmt->fourcc, num, 79 | modifiers, external, &num) || num <= 0) 80 | { 81 | num = 0; 82 | } 83 | 84 | support->fourcc = fmt->fourcc; 85 | support->fmt = fmt; 86 | support->num_modifiers = 0; 87 | support->external_modifiers = NULL; 88 | support->num_external_modifiers = 0; 89 | support->modifiers = malloc(num * sizeof(uint64_t)); 90 | if (support->modifiers == NULL) 91 | { 92 | free(modifiers); 93 | return EGL_FALSE; 94 | } 95 | 96 | // Split the modifiers into renderable and external-only. 97 | for (i=0; imodifiers[support->num_modifiers++] = modifiers[i]; 102 | } 103 | } 104 | 105 | support->external_modifiers = support->modifiers + support->num_modifiers; 106 | for (i=0; iexternal_modifiers[support->num_external_modifiers++] = modifiers[i]; 111 | } 112 | } 113 | free(modifiers); 114 | 115 | if (support->num_modifiers == 0) 116 | { 117 | free(support->modifiers); 118 | support->modifiers = NULL; 119 | 120 | return EGL_FALSE; 121 | } 122 | 123 | return EGL_TRUE; 124 | } 125 | 126 | EGLBoolean eplX11InitDriverFormats(EplPlatformData *plat, X11DisplayInstance *inst) 127 | { 128 | EGLint *formats = NULL; 129 | EGLint num = 0; 130 | EGLint i; 131 | 132 | if (!plat->priv->egl.QueryDmaBufFormatsEXT(inst->internal_display->edpy, 133 | 0, NULL, &num) || num <= 0) 134 | { 135 | return EGL_FALSE; 136 | } 137 | 138 | formats = malloc(num * sizeof(EGLint)); 139 | if (formats == NULL) 140 | { 141 | return EGL_FALSE; 142 | } 143 | 144 | if (!plat->priv->egl.QueryDmaBufFormatsEXT(inst->internal_display->edpy, 145 | num, formats, &num) || num <= 0) 146 | { 147 | free(formats); 148 | return EGL_FALSE; 149 | } 150 | 151 | inst->driver_formats = calloc(1, num * sizeof(X11DriverFormat)); 152 | if (inst->driver_formats == NULL) 153 | { 154 | free(formats); 155 | return EGL_FALSE; 156 | } 157 | 158 | inst->num_driver_formats = 0; 159 | for (i=0; iinternal_display->edpy, formats[i], 162 | &inst->driver_formats[inst->num_driver_formats])) 163 | { 164 | inst->num_driver_formats++; 165 | } 166 | } 167 | free(formats); 168 | 169 | if (inst->num_driver_formats == 0) 170 | { 171 | free(inst->driver_formats); 172 | inst->driver_formats = NULL; 173 | return EGL_FALSE; 174 | } 175 | 176 | // Sort the list by fourcc code. 177 | qsort(inst->driver_formats, inst->num_driver_formats, 178 | sizeof(X11DriverFormat), CompareFormatSupportInfo); 179 | 180 | return EGL_TRUE; 181 | } 182 | 183 | void eplX11CleanupDriverFormats(X11DisplayInstance *inst) 184 | { 185 | if (inst->driver_formats != NULL) 186 | { 187 | int i; 188 | for (i=0; inum_driver_formats; i++) 189 | { 190 | free(inst->driver_formats[i].modifiers); 191 | } 192 | free(inst->driver_formats); 193 | inst->driver_formats = NULL; 194 | } 195 | inst->num_driver_formats = 0; 196 | } 197 | 198 | X11DriverFormat *eplX11FindDriverFormat(X11DisplayInstance *inst, uint32_t fourcc) 199 | { 200 | return bsearch(&fourcc, inst->driver_formats, inst->num_driver_formats, 201 | sizeof(X11DriverFormat), CompareFormatSupportInfo); 202 | } 203 | 204 | static xcb_visualid_t FindVisualForFormat(EplPlatformData *plat, xcb_connection_t *conn, xcb_screen_t *xscreen, const EplFormatInfo *fmt) 205 | { 206 | xcb_depth_iterator_t depthIter; 207 | int depth = fmt->colors[0] + fmt->colors[1] + fmt->colors[2] + fmt->colors[3]; 208 | uint32_t red_mask = ((1 << fmt->colors[0]) - 1) << fmt->offset[0]; 209 | uint32_t green_mask = ((1 << fmt->colors[1]) - 1) << fmt->offset[1]; 210 | uint32_t blue_mask = ((1 << fmt->colors[2]) - 1) << fmt->offset[2]; 211 | 212 | for (depthIter = xcb_screen_allowed_depths_iterator(xscreen); 213 | depthIter.rem > 0; 214 | xcb_depth_next(&depthIter)) 215 | { 216 | if (depthIter.data->depth == depth) 217 | { 218 | xcb_visualtype_iterator_t visIter = xcb_depth_visuals_iterator(depthIter.data); 219 | 220 | for (visIter = xcb_depth_visuals_iterator(depthIter.data); 221 | visIter.rem > 0; 222 | xcb_visualtype_next(&visIter)) 223 | { 224 | if (visIter.data->_class == XCB_VISUAL_CLASS_TRUE_COLOR 225 | && visIter.data->red_mask == red_mask 226 | && visIter.data->green_mask == green_mask 227 | && visIter.data->blue_mask == blue_mask) 228 | { 229 | return visIter.data->visual_id; 230 | } 231 | } 232 | } 233 | } 234 | 235 | return 0; 236 | } 237 | static void SetupConfig(EplPlatformData *plat, X11DisplayInstance *inst, EplConfig *config) 238 | { 239 | X11DriverFormat *support = NULL; 240 | EGLint fourcc = DRM_FORMAT_INVALID; 241 | xcb_visualid_t visual; 242 | 243 | config->surfaceMask &= ~(EGL_WINDOW_BIT | EGL_PIXMAP_BIT); 244 | 245 | // Query the fourcc code from the driver. 246 | if (plat->priv->egl.PlatformGetConfigAttribNVX(inst->internal_display->edpy, 247 | config->config, EGL_LINUX_DRM_FOURCC_EXT, &fourcc)) 248 | { 249 | config->fourcc = (uint32_t) fourcc; 250 | } 251 | else 252 | { 253 | config->fourcc = DRM_FORMAT_INVALID; 254 | } 255 | 256 | if (config->fourcc == DRM_FORMAT_INVALID) 257 | { 258 | // Without a format, we can't do anything with this config. 259 | return; 260 | } 261 | 262 | support = eplX11FindDriverFormat(inst, fourcc); 263 | if (support == NULL) 264 | { 265 | // The driver doesn't support importing a dma-buf with this format. 266 | return; 267 | } 268 | 269 | // We should be able to support pixmaps with any supported format, as long 270 | // as they have a supported modifier. 271 | config->surfaceMask |= EGL_PIXMAP_BIT; 272 | 273 | visual = FindVisualForFormat(inst->platform, inst->conn, inst->xscreen, support->fmt); 274 | if (visual != 0) 275 | { 276 | config->nativeVisualID = visual; 277 | config->nativeVisualType = XCB_VISUAL_CLASS_TRUE_COLOR; 278 | config->surfaceMask |= EGL_WINDOW_BIT; 279 | } 280 | else 281 | { 282 | config->nativeVisualType = EGL_NONE; 283 | } 284 | } 285 | 286 | EGLBoolean eplX11InitConfigList(EplPlatformData *plat, X11DisplayInstance *inst) 287 | { 288 | int i; 289 | 290 | inst->configs = eplConfigListCreate(plat, inst->internal_display->edpy); 291 | if (inst->configs == NULL) 292 | { 293 | eplSetError(plat, EGL_BAD_ALLOC, "Can't find any usable EGLConfigs"); 294 | return EGL_FALSE; 295 | } 296 | for (i=0; iconfigs->num_configs; i++) 297 | { 298 | SetupConfig(plat, inst, &inst->configs->configs[i]); 299 | } 300 | return EGL_TRUE; 301 | } 302 | 303 | static EGLBoolean FilterNativePixmap(EplDisplay *pdpy, EplConfig **configs, EGLint *count, xcb_pixmap_t xpix) 304 | { 305 | xcb_generic_error_t *error = NULL; 306 | xcb_get_geometry_cookie_t geomCookie = xcb_get_geometry(pdpy->priv->inst->conn, xpix); 307 | xcb_get_geometry_reply_t *geom = xcb_get_geometry_reply(pdpy->priv->inst->conn, geomCookie, &error); 308 | 309 | xcb_dri3_buffers_from_pixmap_cookie_t buffersCookie; 310 | xcb_dri3_buffers_from_pixmap_reply_t *buffers = NULL; 311 | int32_t *fds = NULL; 312 | 313 | EGLint match = 0; 314 | EGLint i; 315 | 316 | if (geom == NULL) 317 | { 318 | eplSetError(pdpy->platform, EGL_BAD_NATIVE_PIXMAP, "Invalid native pixmap 0x%x", xpix); 319 | free(error); 320 | return EGL_FALSE; 321 | } 322 | 323 | if (geom->root != pdpy->priv->inst->xscreen->root) 324 | { 325 | // TODO: Should this be an error, or should it just return zero matching configs? 326 | eplSetError(pdpy->platform, EGL_BAD_NATIVE_PIXMAP, "Pixmap 0x%x is on a different screen", xpix); 327 | free(geom); 328 | return EGL_FALSE; 329 | } 330 | 331 | // Filter out any configs that have the wrong depth. 332 | match = 0; 333 | for (i=0; i < *count; i++) 334 | { 335 | EplConfig *config = configs[i]; 336 | const X11DriverFormat *fmt; 337 | 338 | if (!(config->surfaceMask & EGL_PIXMAP_BIT)) 339 | { 340 | continue; 341 | } 342 | 343 | assert(config->fourcc != DRM_FORMAT_INVALID); 344 | 345 | fmt = eplX11FindDriverFormat(pdpy->priv->inst, config->fourcc); 346 | if (fmt == NULL) 347 | { 348 | // If the driver doesn't support this format, then we should never 349 | // have set EGL_PIXMAP_BIT on it. 350 | assert(!"Can't happen -- no driver support for format"); 351 | continue; 352 | } 353 | 354 | if (eplFormatInfoDepth(fmt->fmt) != geom->depth) 355 | { 356 | continue; 357 | } 358 | 359 | configs[match++] = config; 360 | } 361 | free(geom); 362 | 363 | *count = match; 364 | if (match == 0) 365 | { 366 | return EGL_TRUE; 367 | } 368 | 369 | // To check the BPP and format modifiers, we have to use a 370 | // DRI3BuffersFromPixmap request. 371 | 372 | buffersCookie = xcb_dri3_buffers_from_pixmap(pdpy->priv->inst->conn, xpix); 373 | buffers = xcb_dri3_buffers_from_pixmap_reply(pdpy->priv->inst->conn, buffersCookie, &error); 374 | if (buffers == NULL) 375 | { 376 | eplSetError(pdpy->platform, EGL_BAD_NATIVE_PIXMAP, "Can't look up dma-buf for pixmap 0x%x\n", xpix); 377 | free(error); 378 | return EGL_FALSE; 379 | } 380 | 381 | // We don't need the file descriptors, so close them now before we do 382 | // anything else. 383 | fds = xcb_dri3_buffers_from_pixmap_buffers(buffers); 384 | for (i=0; ipriv->inst, config->fourcc); 402 | 403 | if (fmt->fmt->bpp != buffers->bpp) 404 | { 405 | continue; 406 | } 407 | 408 | // With PRIME, we can support any pixmap in the server by allocating a 409 | // linear pixmap and then sending a CopyArea request. Without PRIME, 410 | // we're limited to whatever we can render to directly. 411 | if (!pdpy->priv->inst->supports_prime) 412 | { 413 | EGLint j; 414 | EGLBoolean supported = EGL_FALSE; 415 | 416 | for (j=0; jnum_modifiers; j++) 417 | { 418 | if (fmt->modifiers[j] == buffers->modifier) 419 | { 420 | supported = EGL_TRUE; 421 | break; 422 | } 423 | } 424 | 425 | if (!supported) 426 | { 427 | continue; 428 | } 429 | } 430 | 431 | configs[match++] = config; 432 | } 433 | *count = match; 434 | free(buffers); 435 | 436 | return EGL_TRUE; 437 | } 438 | 439 | EGLBoolean eplX11HookChooseConfig(EGLDisplay edpy, EGLint const *attribs, 440 | EGLConfig *configs, EGLint configSize, EGLint *numConfig) 441 | { 442 | EplDisplay *pdpy; 443 | EGLint matchNativePixmap = XCB_PIXMAP_NONE; 444 | EGLBoolean success = EGL_FALSE; 445 | EplConfig **found = NULL; 446 | EGLint count = 0; 447 | 448 | pdpy = eplDisplayAcquire(edpy); 449 | if (pdpy == NULL) 450 | { 451 | return EGL_FALSE; 452 | } 453 | 454 | found = eplConfigListChooseConfigs(pdpy->platform, pdpy->internal_display, 455 | pdpy->priv->inst->configs, attribs, &count, &matchNativePixmap); 456 | if (found == NULL) 457 | { 458 | goto done; 459 | } 460 | 461 | if (matchNativePixmap != XCB_PIXMAP_NONE) 462 | { 463 | if (!FilterNativePixmap(pdpy, found, &count, matchNativePixmap)) 464 | { 465 | goto done; 466 | } 467 | } 468 | 469 | success = EGL_TRUE; 470 | 471 | done: 472 | if (success) 473 | { 474 | eplConfigListReturnConfigs(found, count, configs, configSize, numConfig); 475 | } 476 | free(found); 477 | eplDisplayRelease(pdpy); 478 | return success; 479 | } 480 | 481 | EGLBoolean eplX11HookGetConfigAttrib(EGLDisplay edpy, EGLConfig config, 482 | EGLint attribute, EGLint *value) 483 | { 484 | EplDisplay *pdpy = eplDisplayAcquire(edpy); 485 | EGLBoolean success = EGL_TRUE; 486 | 487 | if (pdpy == NULL) 488 | { 489 | return EGL_FALSE; 490 | } 491 | 492 | success = eplConfigListGetAttribute(pdpy->platform, pdpy->internal_display, 493 | pdpy->priv->inst->configs, config, attribute, value); 494 | 495 | eplDisplayRelease(pdpy); 496 | 497 | return success; 498 | } 499 | -------------------------------------------------------------------------------- /src/x11/x11-pixmap.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | /** 19 | * \file 20 | * 21 | * Pixmap handling for X11. 22 | * 23 | * Pixmaps are a lot simpler than windows, since we only have one buffer and we 24 | * never need to reallocate it. 25 | */ 26 | 27 | #include "x11-platform.h" 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | #include 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | /** 46 | * Data for an X11 pixmap. 47 | * 48 | * A pixmap is a lot simpler than a window, because there's only one buffer, 49 | * and we never have to swap or resize it. So, we just import a dma-buf for the 50 | * pixmap when we first create the EGLSurface, and then we don't have to change 51 | * anything after that. 52 | */ 53 | typedef struct 54 | { 55 | X11DisplayInstance *inst; 56 | xcb_pixmap_t xpix; 57 | uint32_t width; 58 | uint32_t height; 59 | EGLPlatformColorBufferNVX buffer; 60 | EGLPlatformColorBufferNVX blit_target; 61 | int prime_dmabuf; 62 | xcb_pixmap_t prime_pixmap; 63 | } X11Pixmap; 64 | 65 | static EGLBoolean CheckDirectSupported(X11DisplayInstance *inst, const X11DriverFormat *fmt, 66 | const xcb_dri3_buffers_from_pixmap_reply_t *reply) 67 | { 68 | int i; 69 | 70 | if (xcb_dri3_buffers_from_pixmap_buffers_length(reply) != 1) 71 | { 72 | return EGL_FALSE; 73 | } 74 | 75 | for (i=0; inum_modifiers; i++) 76 | { 77 | if (fmt->modifiers[i] == reply->modifier) 78 | { 79 | return EGL_TRUE; 80 | } 81 | } 82 | 83 | return EGL_FALSE; 84 | } 85 | 86 | static EGLPlatformColorBufferNVX AllocInternalBuffer(X11DisplayInstance *inst, 87 | const X11DriverFormat *fmt, uint32_t width, uint32_t height) 88 | { 89 | EGLPlatformColorBufferNVX buffer = NULL; 90 | struct gbm_bo *gbo = NULL; 91 | int fd = -1; 92 | 93 | gbo = inst->platform->priv->gbm.bo_create_with_modifiers2(inst->gbmdev, 94 | width, height, fmt->fourcc, fmt->modifiers, fmt->num_modifiers, 0); 95 | if (gbo == NULL) 96 | { 97 | eplSetError(inst->platform, EGL_BAD_ALLOC, "Failed to allocate internal buffer for PRIME pixmap"); 98 | goto done; 99 | } 100 | 101 | fd = gbm_bo_get_fd(gbo); 102 | if (fd < 0) 103 | { 104 | eplSetError(inst->platform, EGL_BAD_ALLOC, "Failed to get internal dma-buf for PRIME pixmap"); 105 | goto done; 106 | } 107 | 108 | buffer = inst->platform->priv->egl.PlatformImportColorBufferNVX(inst->internal_display->edpy, 109 | fd, width, height, gbm_bo_get_format(gbo), 110 | gbm_bo_get_stride(gbo), 111 | gbm_bo_get_offset(gbo, 0), 112 | gbm_bo_get_modifier(gbo)); 113 | if (buffer == NULL) 114 | { 115 | eplSetError(inst->platform, EGL_BAD_ALLOC, "Failed to import internal dma-buf for PRIME pixmap"); 116 | goto done; 117 | } 118 | 119 | done: 120 | if (fd >= 0) 121 | { 122 | close(fd); 123 | } 124 | if (gbo != NULL) 125 | { 126 | gbm_bo_destroy(gbo); 127 | } 128 | return buffer; 129 | } 130 | 131 | static EGLBoolean AllocLinearPixmap(X11DisplayInstance *inst, EplSurface *surf, 132 | const X11DriverFormat *fmt, uint32_t width, uint32_t height) 133 | { 134 | X11Pixmap *ppix = (X11Pixmap *) surf->priv; 135 | int stride = 0; 136 | int offset = 0; 137 | int fd = -1; 138 | xcb_void_cookie_t cookie; 139 | xcb_generic_error_t *error = NULL; 140 | EGLBoolean success = EGL_FALSE; 141 | 142 | assert(ppix->prime_dmabuf < 0); 143 | assert(ppix->blit_target == NULL); 144 | 145 | ppix->blit_target = inst->platform->priv->egl.PlatformAllocColorBufferNVX(inst->internal_display->edpy, 146 | width, height, fmt->fourcc, DRM_FORMAT_MOD_LINEAR, EGL_TRUE); 147 | if (ppix->blit_target == NULL) 148 | { 149 | eplSetError(inst->platform, EGL_BAD_ALLOC, "Failed to allocate internal buffer for linear PRIME pixmap"); 150 | goto done; 151 | } 152 | 153 | // Export the image to a dma-buf. 154 | if (!inst->platform->priv->egl.PlatformExportColorBufferNVX(inst->internal_display->edpy, ppix->blit_target, 155 | &ppix->prime_dmabuf, NULL, NULL, NULL, &stride, &offset, NULL)) 156 | { 157 | eplSetError(inst->platform, EGL_BAD_ALLOC, "Failed to get internal dma-buf for linear PRIME pixmap"); 158 | goto done; 159 | } 160 | if (ppix->prime_dmabuf < 0) 161 | { 162 | eplSetError(inst->platform, EGL_BAD_ALLOC, 163 | "Possible driver error: Failed to get internal dma-buf for linear PRIME pixmap"); 164 | goto done; 165 | } 166 | 167 | // XCB will close the file descriptor after sending the request, so we have 168 | // to duplicate it. 169 | fd = dup(ppix->prime_dmabuf); 170 | if (fd < 0) 171 | { 172 | eplSetError(inst->platform, EGL_BAD_ALLOC, "Failed to dup dmabuf: %s\n", strerror(errno)); 173 | goto done; 174 | } 175 | 176 | ppix->prime_pixmap = xcb_generate_id(inst->conn); 177 | cookie = xcb_dri3_pixmap_from_buffers_checked(inst->conn, 178 | ppix->prime_pixmap, inst->xscreen->root, 1, width, height, stride, offset, 179 | 0, 0, 0, 0, 0, 0, eplFormatInfoDepth(fmt->fmt), fmt->fmt->bpp, 180 | DRM_FORMAT_MOD_LINEAR, &fd); 181 | 182 | error = xcb_request_check(inst->conn, cookie); 183 | if (error != NULL) 184 | { 185 | eplSetError(inst->platform, EGL_BAD_ALLOC, "DRI3PixmapFromBuffers request failed with error %d\n", 186 | (int) error->error_code); 187 | ppix->prime_pixmap = 0; 188 | goto done; 189 | } 190 | 191 | success = EGL_TRUE; 192 | 193 | done: 194 | free(error); 195 | 196 | return success; 197 | } 198 | 199 | /** 200 | * Fetches the dma-buf for a Pixmap from the server and creates an 201 | * EGLExtColorBuffer in the driver for it. 202 | */ 203 | static EGLBoolean ImportPixmap(X11DisplayInstance *inst, EplSurface *surf, 204 | xcb_pixmap_t xpix, const EplFormatInfo *fmt, uint32_t width, uint32_t height) 205 | { 206 | X11Pixmap *ppix = (X11Pixmap *) surf->priv; 207 | const X11DriverFormat *driverFmt = eplX11FindDriverFormat(inst, fmt->fourcc); 208 | xcb_generic_error_t *error = NULL; 209 | xcb_dri3_buffers_from_pixmap_cookie_t cookie; 210 | xcb_dri3_buffers_from_pixmap_reply_t *reply = NULL; 211 | int depth = fmt->colors[0] + fmt->colors[1] + fmt->colors[2] + fmt->colors[3]; 212 | int32_t *fds = NULL; 213 | EGLBoolean prime = EGL_FALSE; 214 | EGLBoolean success = EGL_FALSE; 215 | 216 | if (driverFmt == NULL) 217 | { 218 | // This should never happen: If the format isn't supported, then we 219 | // should have caught that when we looked up the EGLConfig. 220 | eplSetError(inst->platform, EGL_BAD_ALLOC, "Internal error: Unsupported format 0x%08x\n", fmt->fourcc); 221 | goto done; 222 | } 223 | 224 | cookie = xcb_dri3_buffers_from_pixmap(inst->conn, xpix); 225 | reply = xcb_dri3_buffers_from_pixmap_reply(inst->conn, cookie, &error); 226 | if (reply == NULL) 227 | { 228 | free(error); 229 | goto done; 230 | } 231 | fds = xcb_dri3_buffers_from_pixmap_buffers(reply); 232 | 233 | if (reply->depth != depth) 234 | { 235 | eplSetError(inst->platform, EGL_BAD_MATCH, 236 | "Pixmap 0x%x has depth %d, but EGLConfig requires depth %d\n", 237 | xpix, reply->depth, depth); 238 | goto done; 239 | } 240 | if (reply->bpp != fmt->bpp) 241 | { 242 | eplSetError(inst->platform, EGL_BAD_MATCH, 243 | "Pixmap 0x%x has bpp %d, but EGLConfig requires bpp %d\n", 244 | xpix, reply->bpp, fmt->bpp); 245 | goto done; 246 | } 247 | 248 | if (inst->force_prime || !CheckDirectSupported(inst, driverFmt, reply)) 249 | { 250 | prime = EGL_TRUE; 251 | } 252 | 253 | if (!prime) 254 | { 255 | ppix->buffer = inst->platform->priv->egl.PlatformImportColorBufferNVX(inst->internal_display->edpy, 256 | fds[0], width, height, fmt->fourcc, 257 | xcb_dri3_buffers_from_pixmap_strides(reply)[0], 258 | xcb_dri3_buffers_from_pixmap_offsets(reply)[0], 259 | reply->modifier); 260 | if (ppix->buffer == NULL) 261 | { 262 | eplSetError(inst->platform, EGL_BAD_ALLOC, "Failed to import dma-buf for pixmap"); 263 | goto done; 264 | } 265 | ppix->prime_dmabuf = fds[0]; 266 | } 267 | else 268 | { 269 | ppix->buffer = AllocInternalBuffer(inst, driverFmt, width, height); 270 | if (ppix->buffer == NULL) 271 | { 272 | goto done; 273 | } 274 | 275 | if (reply->modifier == DRM_FORMAT_MOD_LINEAR && 276 | xcb_dri3_buffers_from_pixmap_buffers_length(reply) == 1) 277 | { 278 | // We need to use PRIME, but the server is using a linear buffer, so we 279 | // can blit to it directly. 280 | 281 | ppix->blit_target = inst->platform->priv->egl.PlatformImportColorBufferNVX(inst->internal_display->edpy, 282 | fds[0], width, height, fmt->fourcc, 283 | xcb_dri3_buffers_from_pixmap_strides(reply)[0], 284 | xcb_dri3_buffers_from_pixmap_offsets(reply)[0], 285 | reply->modifier); 286 | if (ppix->blit_target == NULL) 287 | { 288 | eplSetError(inst->platform, EGL_BAD_ALLOC, "Failed to import dma-buf for pixmap"); 289 | goto done; 290 | } 291 | ppix->prime_dmabuf = fds[0]; 292 | } 293 | else 294 | { 295 | // Otherwise, create a linear intermediate pixmap. We'll blit to that 296 | // pixmap, and then send a CopyArea request to blit to the caller's 297 | // pixmap. 298 | 299 | if (!AllocLinearPixmap(inst, surf, driverFmt, width, height)) 300 | { 301 | goto done; 302 | } 303 | } 304 | } 305 | 306 | success = EGL_TRUE; 307 | 308 | done: 309 | if (reply != NULL) 310 | { 311 | int i; 312 | for (i=0; iprime_dmabuf) 315 | { 316 | close(fds[i]); 317 | } 318 | } 319 | free(reply); 320 | } 321 | return success; 322 | } 323 | 324 | static void PixmapDamageCallback(void *param, int syncfd, unsigned int flags) 325 | { 326 | EplSurface *surf = param; 327 | X11Pixmap *ppix = (X11Pixmap *) surf->priv; 328 | 329 | /* 330 | * Note that we should be fine even if another thread is trying to call 331 | * eglDestroyPixmap here. 332 | * 333 | * The driver's eglDestroyPixmap gets called before anything else in the 334 | * X11Pixmap struct gets cleaned up. The driver will ensure that any 335 | * callbacks have finished and no new callbacks can start when 336 | * eglDestroyPixmap returns. 337 | */ 338 | 339 | if (syncfd >= 0) 340 | { 341 | /* 342 | * We don't have any form of explicit sync that we can use for pixmap 343 | * rendering, because we're not using PresentPixmap. 344 | * 345 | * If the server (and the kernel) both support it, then try to use 346 | * implicit sync. Otherwise, do a CPU wait so that we can at least get 347 | * a consistent functional result in all cases. 348 | */ 349 | if (ppix->prime_dmabuf < 0 || !eplX11ImportDmaBufSyncFile(ppix->inst, ppix->prime_dmabuf, syncfd)) 350 | { 351 | eplX11WaitForFD(syncfd); 352 | } 353 | } 354 | 355 | if (ppix->prime_pixmap != 0) 356 | { 357 | // TODO: Should we hold on to the GC that we create here? We should be 358 | // able to reuse it for any drawable that has the same depth. 359 | 360 | xcb_create_gc_value_list_t gcvalues; 361 | xcb_gcontext_t gc = xcb_generate_id(ppix->inst->conn); 362 | xcb_create_gc_aux(ppix->inst->conn, gc, ppix->xpix, 0, &gcvalues); 363 | 364 | xcb_copy_area(ppix->inst->conn, ppix->prime_pixmap, ppix->xpix, gc, 365 | 0, 0, 0, 0, ppix->width, ppix->height); 366 | xcb_free_gc(ppix->inst->conn, gc); 367 | } 368 | } 369 | 370 | void eplX11DestroyPixmap(EplSurface *surf) 371 | { 372 | X11Pixmap *ppix = (X11Pixmap *) surf->priv; 373 | surf->priv = NULL; 374 | if (ppix != NULL) 375 | { 376 | if (ppix->inst != NULL) 377 | { 378 | if (surf->internal_surface != EGL_NO_SURFACE) 379 | { 380 | ppix->inst->platform->egl.DestroySurface(ppix->inst->internal_display->edpy, surf->internal_surface); 381 | } 382 | if (ppix->buffer != NULL) 383 | { 384 | ppix->inst->platform->priv->egl.PlatformFreeColorBufferNVX(ppix->inst->internal_display->edpy, ppix->buffer); 385 | } 386 | if (ppix->blit_target != NULL) 387 | { 388 | ppix->inst->platform->priv->egl.PlatformFreeColorBufferNVX(ppix->inst->internal_display->edpy, ppix->blit_target); 389 | } 390 | if (ppix->prime_pixmap != 0 && ppix->inst->conn != NULL) 391 | { 392 | xcb_free_pixmap(ppix->inst->conn, ppix->prime_pixmap); 393 | } 394 | eplX11DisplayInstanceUnref(ppix->inst); 395 | } 396 | if (ppix->prime_dmabuf >= 0) 397 | { 398 | close(ppix->prime_dmabuf); 399 | } 400 | free(ppix); 401 | } 402 | } 403 | 404 | static EGLBoolean CheckExistingPixmap(EplDisplay *pdpy, xcb_pixmap_t xpix) 405 | { 406 | EplSurface *psurf; 407 | 408 | glvnd_list_for_each_entry(psurf, &pdpy->surface_list, entry) 409 | { 410 | if (psurf->type == EPL_SURFACE_TYPE_PIXMAP) 411 | { 412 | X11Pixmap *ppix = (X11Pixmap *) psurf->priv; 413 | if (ppix->xpix == xpix) 414 | { 415 | eplSetError(pdpy->platform, EGL_BAD_ALLOC, 416 | "An EGLSurface already exists for pixmap 0x%x\n", xpix); 417 | return EGL_FALSE; 418 | } 419 | } 420 | } 421 | 422 | return EGL_TRUE; 423 | } 424 | 425 | EGLSurface eplX11CreatePixmapSurface(EplPlatformData *plat, EplDisplay *pdpy, EplSurface *surf, 426 | EGLConfig config, void *native_surface, const EGLAttrib *attribs, EGLBoolean create_platform) 427 | { 428 | X11DisplayInstance *inst = pdpy->priv->inst; 429 | xcb_pixmap_t xpix = eplX11GetNativeXID(pdpy, native_surface, create_platform); 430 | X11Pixmap *ppix = NULL; 431 | const EplConfig *configInfo; 432 | const EplFormatInfo *fmt; 433 | xcb_get_geometry_cookie_t geomCookie; 434 | xcb_get_geometry_reply_t *geomReply = NULL; 435 | xcb_generic_error_t *error = NULL; 436 | EGLSurface esurf = EGL_NO_SURFACE; 437 | EGLAttrib buffers[] = 438 | { 439 | GL_BACK, 0, 440 | EGL_PLATFORM_SURFACE_BLIT_TARGET_NVX, 0, 441 | EGL_PLATFORM_SURFACE_DAMAGE_CALLBACK_NVX, (EGLAttrib) PixmapDamageCallback, 442 | EGL_PLATFORM_SURFACE_DAMAGE_CALLBACK_PARAM_NVX, (EGLAttrib) surf, 443 | EGL_NONE 444 | }; 445 | EGLAttrib *internalAttribs = NULL; 446 | 447 | if (xpix == 0) 448 | { 449 | eplSetError(plat, EGL_BAD_NATIVE_PIXMAP, "Invalid native pixmap %p\n", native_surface); 450 | return EGL_NO_SURFACE; 451 | } 452 | if (!CheckExistingPixmap(pdpy, xpix)) 453 | { 454 | return EGL_NO_SURFACE; 455 | } 456 | 457 | configInfo = eplConfigListFind(inst->configs, config); 458 | if (configInfo == NULL) 459 | { 460 | eplSetError(plat, EGL_BAD_CONFIG, "Invalid EGLConfig %p", config); 461 | return EGL_NO_SURFACE; 462 | } 463 | if (!(configInfo->surfaceMask & EGL_PIXMAP_BIT)) 464 | { 465 | eplSetError(plat, EGL_BAD_CONFIG, "EGLConfig %p does not support pixmaps", config); 466 | return EGL_NO_SURFACE; 467 | } 468 | fmt = eplFormatInfoLookup(configInfo->fourcc); 469 | assert(fmt != NULL); 470 | 471 | internalAttribs = eplX11GetInternalSurfaceAttribs(plat, pdpy, internalAttribs); 472 | if (internalAttribs == NULL) 473 | { 474 | goto done; 475 | } 476 | 477 | geomCookie = xcb_get_geometry(inst->conn, xpix); 478 | geomReply = xcb_get_geometry_reply(inst->conn, geomCookie, &error); 479 | if (geomReply == NULL) 480 | { 481 | eplSetError(plat, EGL_BAD_NATIVE_PIXMAP, "Invalid pixmap 0x%x", xpix); 482 | goto done; 483 | } 484 | if (geomReply->root != inst->xscreen->root) 485 | { 486 | eplSetError(plat, EGL_BAD_NATIVE_PIXMAP, "Pixmap 0x%x is on the wrong screen", xpix); 487 | goto done; 488 | } 489 | if (geomReply->width <= 0 || geomReply->height <= 0) 490 | { 491 | eplSetError(plat, EGL_BAD_NATIVE_PIXMAP, "Invalid pixmap size"); 492 | goto done; 493 | } 494 | 495 | ppix = calloc(1, sizeof(X11Pixmap)); 496 | if (ppix == NULL) 497 | { 498 | eplSetError(plat, EGL_BAD_ALLOC, "Out of memory"); 499 | goto done; 500 | } 501 | surf->priv = (EplImplSurface *) ppix; 502 | ppix->inst = eplX11DisplayInstanceRef(inst); 503 | ppix->xpix = xpix; 504 | ppix->width = geomReply->width; 505 | ppix->height = geomReply->height; 506 | ppix->prime_dmabuf = -1; 507 | 508 | if (!ImportPixmap(inst, surf, xpix, fmt, geomReply->width, geomReply->height)) 509 | { 510 | goto done; 511 | } 512 | 513 | buffers[1] = (EGLAttrib) ppix->buffer; 514 | if (ppix->blit_target != NULL) 515 | { 516 | buffers[3] = (EGLAttrib) ppix->blit_target; 517 | } 518 | else 519 | { 520 | // If we're not using PRIME, then we don't need the damage callback. 521 | buffers[2] = EGL_NONE; 522 | } 523 | esurf = inst->platform->priv->egl.PlatformCreateSurfaceNVX(inst->internal_display->edpy, config, 524 | buffers, internalAttribs); 525 | if (esurf == EGL_NO_SURFACE) 526 | { 527 | eplSetError(plat, EGL_BAD_ALLOC, "Failed to allocate EGLSurface"); 528 | goto done; 529 | } 530 | 531 | done: 532 | if (esurf == EGL_NO_SURFACE) 533 | { 534 | eplX11DestroyPixmap(surf); 535 | } 536 | free(geomReply); 537 | free(error); 538 | free(internalAttribs); 539 | return esurf; 540 | } 541 | -------------------------------------------------------------------------------- /src/x11/x11-platform-xcb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "x11-platform.h" 19 | 20 | #include 21 | 22 | PUBLIC EGLBoolean loadEGLExternalPlatform(int major, int minor, 23 | const EGLExtDriver *driver, 24 | EGLExtPlatform *extplatform) 25 | { 26 | return eplX11LoadEGLExternalPlatformCommon(major, minor, 27 | driver, extplatform, EGL_PLATFORM_XCB_EXT); 28 | } 29 | 30 | xcb_connection_t *eplX11GetXCBConnection(void *native_display, int *ret_screen) 31 | { 32 | return NULL; 33 | } 34 | 35 | X11XlibDisplayClosedData *eplX11AddXlibDisplayClosedCallback(void *xlib_native_display) 36 | { 37 | return NULL; 38 | } 39 | 40 | void eplX11XlibDisplayClosedDataUnref(X11XlibDisplayClosedData *data) 41 | { 42 | // This should never be called with a non-NULL value, because we never 43 | // allocate a X11XlibDisplayClosedData struct. 44 | assert(data == NULL); 45 | } 46 | 47 | EGLBoolean eplX11IsNativeClosed(X11XlibDisplayClosedData *data) 48 | { 49 | return EGL_FALSE; 50 | } 51 | -------------------------------------------------------------------------------- /src/x11/x11-platform-xlib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "x11-platform.h" 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | /** 26 | * This structure keeps track of a callback that we've registered with 27 | * XESetCloseDisplay. 28 | * 29 | * If the same underlying Display is used with more than one EGLDisplay, then 30 | * the EplDisplays will all share the same X11XlibDisplayClosedData struct. 31 | */ 32 | struct _X11XlibDisplayClosedData 33 | { 34 | EplRefCount refcount; 35 | 36 | Display *xdpy; 37 | EGLBoolean closed; 38 | XExtCodes *ext_codes; 39 | 40 | struct glvnd_list entry; 41 | }; 42 | 43 | static void CleanupDisplayClosedCallbacks(void) __attribute__((destructor)); 44 | 45 | static void eplX11DisplayClosedCallbackDataFree(X11XlibDisplayClosedData *callback); 46 | static int OnXlibDisplayClosed(Display *xdpy, XExtCodes *codes); 47 | 48 | /** 49 | * A linked list of all DisplayClosedCallbackData structs. 50 | * 51 | * This has to be a global array, because the callback for XESetCloseDisplay 52 | * doesn't have an extra parameter where we could stash an EplDisplay or 53 | * EplPlatformData pointer. 54 | * 55 | * That also means we need a reference count to know when to clean up this 56 | * list, in case loadEGLExternalPlatform gets called more than once. 57 | */ 58 | static struct glvnd_list display_close_callback_list = { &display_close_callback_list, &display_close_callback_list }; 59 | static pthread_mutex_t display_close_callback_list_mutex = PTHREAD_MUTEX_INITIALIZER; 60 | 61 | EPL_REFCOUNT_DEFINE_TYPE_FUNCS(X11XlibDisplayClosedData, eplX11XlibDisplayClosedData, 62 | refcount, eplX11DisplayClosedCallbackDataFree); 63 | 64 | PUBLIC EGLBoolean loadEGLExternalPlatform(int major, int minor, 65 | const EGLExtDriver *driver, 66 | EGLExtPlatform *extplatform) 67 | { 68 | return eplX11LoadEGLExternalPlatformCommon(major, minor, 69 | driver, extplatform, EGL_PLATFORM_X11_KHR); 70 | } 71 | 72 | xcb_connection_t *eplX11GetXCBConnection(void *native_display, int *ret_screen) 73 | { 74 | Display *xdpy = native_display; 75 | if (ret_screen != NULL) 76 | { 77 | *ret_screen = DefaultScreen(xdpy); 78 | } 79 | return XGetXCBConnection(xdpy); 80 | } 81 | 82 | static void RemoveDisplayClosedCallback(X11XlibDisplayClosedData *callback) 83 | { 84 | glvnd_list_del(&callback->entry); 85 | if (callback->ext_codes != NULL) 86 | { 87 | XESetCloseDisplay(callback->xdpy, callback->ext_codes->extension, NULL); 88 | callback->ext_codes = NULL; 89 | } 90 | eplX11XlibDisplayClosedDataUnref(callback); 91 | } 92 | 93 | static void CleanupDisplayClosedCallbacks(void) 94 | { 95 | pthread_mutex_lock(&display_close_callback_list_mutex); 96 | 97 | while (!glvnd_list_is_empty(&display_close_callback_list)) 98 | { 99 | X11XlibDisplayClosedData *callback = glvnd_list_first_entry(&display_close_callback_list, 100 | X11XlibDisplayClosedData, entry); 101 | RemoveDisplayClosedCallback(callback); 102 | } 103 | 104 | pthread_mutex_unlock(&display_close_callback_list_mutex); 105 | } 106 | 107 | X11XlibDisplayClosedData *eplX11AddXlibDisplayClosedCallback(void *xlib_native_display) 108 | { 109 | Display *xdpy = xlib_native_display; 110 | X11XlibDisplayClosedData *callback; 111 | 112 | pthread_mutex_lock(&display_close_callback_list_mutex); 113 | glvnd_list_for_each_entry(callback, &display_close_callback_list, entry) 114 | { 115 | if (callback->xdpy == xdpy) 116 | { 117 | eplX11XlibDisplayClosedDataRef(callback); 118 | pthread_mutex_unlock(&display_close_callback_list_mutex); 119 | return callback; 120 | } 121 | } 122 | 123 | // We haven't seen this display before, so add a new one. 124 | callback = malloc(sizeof(X11XlibDisplayClosedData)); 125 | if (callback == NULL) 126 | { 127 | pthread_mutex_unlock(&display_close_callback_list_mutex); 128 | return NULL; 129 | } 130 | 131 | callback->ext_codes = XAddExtension(xdpy); 132 | if (callback->ext_codes == NULL) 133 | { 134 | pthread_mutex_unlock(&display_close_callback_list_mutex); 135 | free(callback); 136 | return NULL; 137 | } 138 | 139 | eplRefCountInit(&callback->refcount); 140 | callback->xdpy = xdpy; 141 | callback->closed = EGL_FALSE; 142 | XESetCloseDisplay(callback->xdpy, callback->ext_codes->extension, OnXlibDisplayClosed); 143 | 144 | eplX11XlibDisplayClosedDataRef(callback); 145 | glvnd_list_add(&callback->entry, &display_close_callback_list); 146 | 147 | pthread_mutex_unlock(&display_close_callback_list_mutex); 148 | 149 | return callback; 150 | } 151 | 152 | static int OnXlibDisplayClosed(Display *xdpy, XExtCodes *codes) 153 | { 154 | X11XlibDisplayClosedData *callback, *callbackTmp; 155 | 156 | pthread_mutex_lock(&display_close_callback_list_mutex); 157 | glvnd_list_for_each_entry_safe(callback, callbackTmp, &display_close_callback_list, entry) 158 | { 159 | if (xdpy == callback->xdpy) 160 | { 161 | assert(codes == callback->ext_codes); 162 | assert(!callback->closed); 163 | callback->closed = EGL_TRUE; 164 | RemoveDisplayClosedCallback(callback); 165 | break; 166 | } 167 | } 168 | pthread_mutex_unlock(&display_close_callback_list_mutex); 169 | 170 | return 0; 171 | } 172 | 173 | static void eplX11DisplayClosedCallbackDataFree(X11XlibDisplayClosedData *callback) 174 | { 175 | // We should have already unregistered the callback in OnXlibDisplayClosed 176 | // or eplX11CleanupPlatform. 177 | assert(callback->ext_codes == NULL); 178 | free(callback); 179 | } 180 | 181 | EGLBoolean eplX11IsNativeClosed(X11XlibDisplayClosedData *data) 182 | { 183 | EGLBoolean ret = EGL_FALSE; 184 | 185 | if (data != NULL) 186 | { 187 | pthread_mutex_lock(&display_close_callback_list_mutex); 188 | ret = data->closed; 189 | pthread_mutex_unlock(&display_close_callback_list_mutex); 190 | } 191 | return ret; 192 | } 193 | 194 | -------------------------------------------------------------------------------- /src/x11/x11-platform.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | /** 19 | * \file 20 | * 21 | * Common structs for the Xlib/XCB platform library. 22 | * 23 | * Note that internally, we use XCB for everything, which allows using mostly 24 | * the same code for both EGL_KHR_platform_x11 and EGL_EXT_platform_xcb. 25 | */ 26 | 27 | #ifndef X11_PLATFORM_H 28 | #define X11_PLATFORM_H 29 | 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "platform-impl.h" 41 | #include "platform-utils.h" 42 | #include "driver-platform-surface.h" 43 | #include "config-list.h" 44 | #include "refcountobj.h" 45 | 46 | #ifndef EGL_EXT_platform_xcb 47 | #define EGL_EXT_platform_xcb 1 48 | #define EGL_PLATFORM_XCB_EXT 0x31DC 49 | #define EGL_PLATFORM_XCB_SCREEN_EXT 0x31DE 50 | #endif /* EGL_EXT_platform_xcb */ 51 | 52 | #ifndef XCB_PRESENT_CAPABILITY_SYNCOBJ 53 | #define XCB_PRESENT_CAPABILITY_SYNCOBJ 16 54 | #endif 55 | 56 | /** 57 | * Keeps track of a callback that we've registered with XESetCloseDisplay. 58 | * 59 | * These are used to check whether a native display has been closed. 60 | */ 61 | typedef struct _X11XlibDisplayClosedData X11XlibDisplayClosedData; 62 | 63 | EPL_REFCOUNT_DECLARE_TYPE_FUNCS(X11XlibDisplayClosedData, eplX11XlibDisplayClosedData); 64 | 65 | /** 66 | * Platform-specific stuff for X11. 67 | * 68 | * Currently, this just includes the OpenGL and EGL functions that we'll need. 69 | */ 70 | struct _EplImplPlatform 71 | { 72 | struct 73 | { 74 | PFNEGLQUERYDISPLAYATTRIBKHRPROC QueryDisplayAttribKHR; 75 | PFNEGLSWAPINTERVALPROC SwapInterval; 76 | PFNEGLQUERYDMABUFFORMATSEXTPROC QueryDmaBufFormatsEXT; 77 | PFNEGLQUERYDMABUFMODIFIERSEXTPROC QueryDmaBufModifiersEXT; 78 | PFNEGLCREATESYNCPROC CreateSync; 79 | PFNEGLDESTROYSYNCPROC DestroySync; 80 | PFNEGLWAITSYNCPROC WaitSync; 81 | PFNEGLDUPNATIVEFENCEFDANDROIDPROC DupNativeFenceFDANDROID; 82 | void (* Flush) (void); 83 | void (* Finish) (void); 84 | 85 | pfn_eglPlatformImportColorBufferNVX PlatformImportColorBufferNVX; 86 | pfn_eglPlatformFreeColorBufferNVX PlatformFreeColorBufferNVX; 87 | pfn_eglPlatformCreateSurfaceNVX PlatformCreateSurfaceNVX; 88 | pfn_eglPlatformSetColorBuffersNVX PlatformSetColorBuffersNVX; 89 | pfn_eglPlatformGetConfigAttribNVX PlatformGetConfigAttribNVX; 90 | pfn_eglPlatformCopyColorBufferNVX PlatformCopyColorBufferNVX; 91 | pfn_eglPlatformAllocColorBufferNVX PlatformAllocColorBufferNVX; 92 | pfn_eglPlatformExportColorBufferNVX PlatformExportColorBufferNVX; 93 | } egl; 94 | 95 | struct 96 | { 97 | xcb_void_cookie_t (* dri3_import_syncobj) (xcb_connection_t *c, uint32_t syncobj, xcb_drawable_t drawable, int32_t syncobj_fd); 98 | xcb_void_cookie_t (* dri3_free_syncobj) (xcb_connection_t *c, uint32_t syncobj); 99 | 100 | xcb_void_cookie_t (* present_pixmap_synced) (xcb_connection_t *c, xcb_window_t window, 101 | xcb_pixmap_t pixmap, uint32_t serial, 102 | xcb_xfixes_region_t valid, xcb_xfixes_region_t update, int16_t x_off, int16_t y_off, 103 | xcb_randr_crtc_t target_crtc, 104 | uint32_t acquire_syncobj, uint32_t release_syncobj, 105 | uint64_t acquire_point, uint64_t release_point, 106 | uint32_t options, uint64_t target_msc, uint64_t divisor, uint64_t remainder, 107 | uint32_t notifies_len, const xcb_present_notify_t *notifies); 108 | } xcb; 109 | 110 | struct 111 | { 112 | int (* GetCap) (int fd, uint64_t capability, uint64_t *value); 113 | int (* SyncobjCreate) (int fd, uint32_t flags, uint32_t *handle); 114 | int (* SyncobjDestroy) (int fd, uint32_t handle); 115 | int (* SyncobjHandleToFD) (int fd, uint32_t handle, int *obj_fd); 116 | int (* SyncobjFDToHandle) (int fd, int obj_fd, uint32_t *handle); 117 | int (* SyncobjImportSyncFile) (int fd, uint32_t handle, int sync_file_fd); 118 | int (* SyncobjExportSyncFile) (int fd, uint32_t handle, int *sync_file_fd); 119 | 120 | int (* SyncobjTimelineSignal) (int fd, const uint32_t *handles, 121 | uint64_t *points, uint32_t handle_count); 122 | int (* SyncobjTimelineWait) (int fd, uint32_t *handles, uint64_t *points, 123 | unsigned num_handles, 124 | int64_t timeout_nsec, unsigned flags, 125 | uint32_t *first_signaled); 126 | int (* SyncobjTransfer) (int fd, 127 | uint32_t dst_handle, uint64_t dst_point, 128 | uint32_t src_handle, uint64_t src_point, 129 | uint32_t flags); 130 | } drm; 131 | 132 | struct 133 | { 134 | struct gbm_bo * (* bo_create_with_modifiers2) (struct gbm_device *gbm, 135 | uint32_t width, uint32_t height, 136 | uint32_t format, 137 | const uint64_t *modifiers, 138 | const unsigned int count, 139 | uint32_t flags); 140 | } gbm; 141 | 142 | EGLBoolean timeline_funcs_supported; 143 | }; 144 | 145 | /** 146 | * Keeps track of format and format modifier support in the driver. 147 | * 148 | * This is used to cache the results of eglQueryDmaBufFormatsEXT and 149 | * eglQueryDmaBufModifiersEXT. 150 | */ 151 | typedef struct 152 | { 153 | // Put the fourcc code as the first element so that we can use bsearch 154 | // with just the fourcc code for a key. 155 | uint32_t fourcc; 156 | const EplFormatInfo *fmt; 157 | uint64_t *modifiers; 158 | int num_modifiers; 159 | uint64_t *external_modifiers; 160 | int num_external_modifiers; 161 | } X11DriverFormat; 162 | 163 | /** 164 | * Contains per-display data that's initialized in eglInitialize and then 165 | * doesn't change until eglTerminate. 166 | * 167 | * It's not safe to lock a display during the window update callback, because 168 | * you can end up with a deadlock. 169 | * 170 | * Thankfully, all of the data that the update callback needs is static and 171 | * doesn't change after initialization, so we don't actually need a mutex to 172 | * protect it. 173 | * 174 | * So, this struct contains all of the per-display data that the update 175 | * callback will need. 176 | * 177 | * This struct is also refcounted. If the app calls eglTerminate while another 178 | * thread still holds a reference to an EplSurface, then the X11DisplayInstance 179 | * struct will stick around until it finishes cleanup up the surface. 180 | * 181 | * The one exception to all of this is the xcb_connection_t itself. If the 182 | * app calls XCloseDisplay while another thread is trying to send or receive X 183 | * protocol, then it'll crash. But, if that happens, then that's very 184 | * definitely an app bug. 185 | */ 186 | typedef struct 187 | { 188 | EplRefCount refcount; 189 | 190 | /** 191 | * A reference to the \c EplPlatformData that this display came from. 192 | * 193 | * This is mainly here so that we can access the driver's EGL functions 194 | * without going through an EplDisplay, since this EplDisplayInstance might 195 | * have gone through an eglTerminate and eglInitialize, or might have been 196 | * destroyed entirely if we're going through teardown. 197 | */ 198 | EplPlatformData *platform; 199 | 200 | /** 201 | * The display connection. 202 | */ 203 | xcb_connection_t *conn; 204 | 205 | /** 206 | * True if the application passed NULL for the native display, so we had to 207 | * open our own display connection. 208 | */ 209 | EGLBoolean own_display; 210 | 211 | /** 212 | * The internal (driver) EGLDisplay. 213 | */ 214 | EplInternalDisplay *internal_display; 215 | 216 | /** 217 | * The screen number that we're talking to. 218 | */ 219 | int screen; 220 | 221 | /** 222 | * The xcb_screen_t struct for the screen that we're talking to. 223 | */ 224 | xcb_screen_t *xscreen; 225 | 226 | /** 227 | * The GBM device for whichever GPU we're rendering on. 228 | */ 229 | struct gbm_device *gbmdev; 230 | 231 | /** 232 | * The EGL device that we're rendering on. 233 | */ 234 | EGLDeviceEXT device; 235 | 236 | /** 237 | * If true, then always use of the indirect presentation path for PRIME. 238 | */ 239 | EGLBoolean force_prime; 240 | 241 | /** 242 | * If true, then we can support the PRIME presentation path. 243 | * 244 | * Note that this isn't necessarily the same as 245 | * \c EplImplDisplay::enable_alt_device. The \c supports_prime flag means that 246 | * we can use the PRIME presentation path as needed on a per-window basis, 247 | * even if we're not doing cross-device presentation. 248 | */ 249 | EGLBoolean supports_prime; 250 | 251 | /** 252 | * If true, then the driver supports EGL_ANDROID_native_fence_sync. 253 | */ 254 | EGLBoolean supports_EGL_ANDROID_native_fence_sync; 255 | 256 | /** 257 | * If true, then the server supports implicit sync semantics. 258 | */ 259 | EGLBoolean supports_implicit_sync; 260 | 261 | /** 262 | * If true, then we can use the new PresentPixmapSynced request for 263 | * synchronization on windows that support it. 264 | * 265 | * This means that we have the necessary EGL functions from the driver, 266 | * the necessary functions in libxcb and libdrm, and that the server 267 | * supports the necessary versions of the DRI3 and Present extensions. 268 | * 269 | * This does not account for the capabilities returned by the 270 | * PresentQueryCapabilties request. That's checked separately for each 271 | * window. 272 | */ 273 | EGLBoolean supports_explicit_sync; 274 | 275 | /** 276 | * The list of EGLConfigs. 277 | */ 278 | EplConfigList *configs; 279 | 280 | /** 281 | * The list of formats and modifiers that the driver supports. 282 | */ 283 | X11DriverFormat *driver_formats; 284 | int num_driver_formats; 285 | } X11DisplayInstance; 286 | 287 | /** 288 | * Contains all of the data we need for an EGLDisplay. 289 | */ 290 | struct _EplImplDisplay 291 | { 292 | /** 293 | * A copy of what the DISPLAY environment variable was when 294 | * eglGetPlatformDisplay was first called. 295 | */ 296 | char *display_env; 297 | 298 | /** 299 | * The screen number that the application specified in the 300 | * eglGetPlatformDisplay attribute list, or -1 if the app didn't specify. 301 | */ 302 | int screen_attrib; 303 | 304 | /** 305 | * The EGLDeviceEXT handle that was specified with an EGL_DEVICE_EXT 306 | * attribute. 307 | */ 308 | EGLDeviceEXT device_attrib; 309 | 310 | /** 311 | * The EGLDeviceEXT handle that we should use for rendering, or 312 | * EGL_NO_DEVICE_EXT to pick one during eglInitialize. 313 | * 314 | * This is set based on either the EGL_DEVICE_EXT attribute or based on 315 | * environment variables. 316 | */ 317 | EGLDeviceEXT requested_device; 318 | 319 | /** 320 | * If true, allow picking a different GPU to do rendering. 321 | * 322 | * This is set based on the __NV_PRIME_RENDER_OFFLOAD environment variable. 323 | * 324 | * If the normal device (\c requested_device if it's set, the server's 325 | * device otherwise) isn't usable, then the \c enable_alt_device flag tells 326 | * eplX11DisplayInstanceCreate to pick a different device rather than just 327 | * fail. 328 | * 329 | * Note that this flag doesn't mean that we will use the PRIME presentation 330 | * path. It's possible that we'd pick the same device as the server anyway. 331 | * 332 | * Likewise, if the application passed an EGL_DISPLAY_EXT attribute, then 333 | * we might end up doing cross-device presentation even if the user doesn't 334 | * set __NV_PRIME_RENDER_OFFLOAD. 335 | */ 336 | EGLBoolean enable_alt_device; 337 | 338 | /** 339 | * A pointer to the X11DisplayInstance struct, or NULL if this display isn't initialized. 340 | */ 341 | X11DisplayInstance *inst; 342 | 343 | /** 344 | * A callback to keep track of whether the native display has been closed. 345 | */ 346 | X11XlibDisplayClosedData *closed_callback; 347 | }; 348 | 349 | EPL_REFCOUNT_DECLARE_TYPE_FUNCS(X11DisplayInstance, eplX11DisplayInstance); 350 | 351 | /** 352 | * Returns the xcb_connection_t and the screen number for a native xlib 353 | * Display. 354 | * 355 | * This is a separate function so that we can avoid depending on Xlib in the 356 | * XCB platform library. 357 | */ 358 | xcb_connection_t *eplX11GetXCBConnection(void *native_display, int *ret_screen); 359 | 360 | /** 361 | * Registers a callback for when an Xlib Display is closed. 362 | * 363 | * Note that XCB doesn't have any equivalent to XESetCloseDisplay. 364 | */ 365 | X11XlibDisplayClosedData *eplX11AddXlibDisplayClosedCallback(void *xlib_native_display); 366 | 367 | /** 368 | * Returns the XID for the native surface handle in one of the 369 | * eglCreate*Surface functions. 370 | * 371 | * \param pdpy The EplDisplay struct 372 | * \param native_surface The native surface handle 373 | * \param create_platform True if this is for one of the 374 | * eglCreatePlatform*Surface functions. 375 | * \return The XID value, or 0 if the native handle is invalid. 376 | */ 377 | uint32_t eplX11GetNativeXID(EplDisplay *pdpy, void *native_surface, EGLBoolean create_platform); 378 | 379 | /** 380 | * Returns true if a native display has been closed. 381 | * 382 | * Note that this only works for an Xlib Display, because XCB doesn't have any 383 | * equivalent to XESetCloseDisplay. 384 | */ 385 | EGLBoolean eplX11IsNativeClosed(X11XlibDisplayClosedData *data); 386 | 387 | EGLBoolean eplX11LoadEGLExternalPlatformCommon(int major, int minor, 388 | const EGLExtDriver *driver, EGLExtPlatform *extplatform, 389 | EGLint platform_enum); 390 | 391 | EGLSurface eplX11CreatePixmapSurface(EplPlatformData *plat, EplDisplay *pdpy, EplSurface *surf, 392 | EGLConfig config, void *native_surface, const EGLAttrib *attribs, EGLBoolean create_platform); 393 | 394 | EGLSurface eplX11CreateWindowSurface(EplPlatformData *plat, EplDisplay *pdpy, EplSurface *surf, 395 | EGLConfig config, void *native_surface, const EGLAttrib *attribs, EGLBoolean create_platform); 396 | 397 | EGLBoolean eplX11SwapBuffers(EplPlatformData *plat, EplDisplay *pdpy, EplSurface *surf, 398 | const EGLint *rects, EGLint n_rects); 399 | 400 | /** 401 | * Initializes the list of driver formats. 402 | * 403 | * \param plat The platform data. 404 | * \param inst The X11DisplayInstance to fill in. 405 | * \return EGL_TRUE on success, or EGL_FALSE on failure. 406 | */ 407 | EGLBoolean eplX11InitDriverFormats(EplPlatformData *plat, X11DisplayInstance *inst); 408 | 409 | /** 410 | * Cleans up the format list that was initialized in eplX11InitDriverFormats. 411 | */ 412 | void eplX11CleanupDriverFormats(X11DisplayInstance *inst); 413 | 414 | /** 415 | * Finds the X11DriverFormat struct for a given format. 416 | */ 417 | X11DriverFormat *eplX11FindDriverFormat(X11DisplayInstance *inst, uint32_t fourcc); 418 | 419 | EGLBoolean eplX11InitConfigList(EplPlatformData *plat, X11DisplayInstance *inst); 420 | 421 | /** 422 | * Returns the list of EGL attributes (not the buffers/internal attributes) 423 | * that should be passed to eglPlatformCreateSurfaceNVX. 424 | * 425 | * Currently, this just sets the EGL_WAYLAND_Y_INVERTED_WL flag to true, and 426 | * passes any other attributes through. 427 | * 428 | * \param plat The platform data 429 | * \param pdpy The display data 430 | * \param attribs The attribute list that was passed to eglCreateWindowSurface 431 | * or eglCreatePixmapSurface. 432 | * \return The EGLAttrib array to pass to the driver, or NULL on error. The 433 | * caller must free the array using free(). 434 | */ 435 | 436 | EGLAttrib *eplX11GetInternalSurfaceAttribs(EplPlatformData *plat, EplDisplay *pdpy, const EGLAttrib *attribs); 437 | 438 | EGLBoolean eplX11HookChooseConfig(EGLDisplay edpy, EGLint const *attribs, 439 | EGLConfig *configs, EGLint configSize, EGLint *numConfig); 440 | 441 | EGLBoolean eplX11HookGetConfigAttrib(EGLDisplay edpy, EGLConfig config, 442 | EGLint attribute, EGLint *value); 443 | 444 | void eplX11DestroyPixmap(EplSurface *surf); 445 | 446 | EGLBoolean eplX11SwapInterval(EGLDisplay edpy, EGLint interval); 447 | 448 | void eplX11DestroyWindow(EplSurface *surf); 449 | 450 | void eplX11FreeWindow(EplSurface *surf); 451 | 452 | EGLBoolean eplX11WaitGLWindow(EplDisplay *pdpy, EplSurface *psurf); 453 | 454 | /** 455 | * A wrapper around the DMA_BUF_IOCTL_IMPORT_SYNC_FILE ioctl. 456 | * 457 | * This will check whether implicit sync is supporte, and if so, it will 458 | * plug a syncfd into a dma-buf. 459 | */ 460 | EGLBoolean eplX11ImportDmaBufSyncFile(X11DisplayInstance *inst, int dmabuf, int syncfd); 461 | int eplX11ExportDmaBufSyncFile(X11DisplayInstance *inst, int dmabuf); 462 | 463 | /** 464 | * Waits for a file descriptor to be ready using poll(). 465 | * 466 | * Since this uses poll(), it can work with any arbitrary file descriptor 467 | * (including a syncfd or a dma-buf), but it does so with a CPU stall. 468 | * 469 | * \param syncfd The file descriptor to wait on. 470 | * \return EGL_TRUE on success, or EGL_FALSE on error. 471 | */ 472 | EGLBoolean eplX11WaitForFD(int syncfd); 473 | 474 | #endif // X11_PLATFORM_H 475 | -------------------------------------------------------------------------------- /src/x11/x11-timeline.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "x11-timeline.h" 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | EGLBoolean eplX11TimelineInit(X11DisplayInstance *inst, X11Timeline *timeline) 25 | { 26 | int fd = -1; 27 | int ret; 28 | 29 | memset(timeline, 0, sizeof(*timeline)); 30 | 31 | if (!inst->supports_explicit_sync) 32 | { 33 | assert(inst->supports_explicit_sync); 34 | return EGL_FALSE; 35 | } 36 | 37 | ret = inst->platform->priv->drm.SyncobjCreate( 38 | gbm_device_get_fd(inst->gbmdev), 39 | 0, &timeline->handle); 40 | if (ret != 0) 41 | { 42 | return EGL_FALSE; 43 | } 44 | 45 | ret = inst->platform->priv->drm.SyncobjHandleToFD( 46 | gbm_device_get_fd(inst->gbmdev), 47 | timeline->handle, &fd); 48 | if (ret != 0) 49 | { 50 | inst->platform->priv->drm.SyncobjDestroy( 51 | gbm_device_get_fd(inst->gbmdev), 52 | timeline->handle); 53 | close(fd); 54 | return EGL_FALSE; 55 | } 56 | 57 | timeline->xid = xcb_generate_id(inst->conn); 58 | // Note that libxcb will close the file descriptor after it sends the 59 | // request, so we do *not* close it here. 60 | inst->platform->priv->xcb.dri3_import_syncobj(inst->conn, 61 | timeline->xid, inst->xscreen->root, fd); 62 | return EGL_TRUE; 63 | } 64 | 65 | void eplX11TimelineDestroy(X11DisplayInstance *inst, X11Timeline *timeline) 66 | { 67 | // The XID is non-zero if and only if eplX11TimelineInit was successfully 68 | // called. 69 | if (timeline->xid != 0) 70 | { 71 | inst->platform->priv->xcb.dri3_free_syncobj(inst->conn, timeline->xid); 72 | timeline->xid = 0; 73 | 74 | inst->platform->priv->drm.SyncobjDestroy( 75 | gbm_device_get_fd(inst->gbmdev), 76 | timeline->handle); 77 | timeline->handle = 0; 78 | } 79 | } 80 | 81 | int eplX11TimelinePointToSyncFD(X11DisplayInstance *inst, X11Timeline *timeline) 82 | { 83 | uint32_t tempobj = 0; 84 | int syncfd = -1; 85 | 86 | if (inst->platform->priv->drm.SyncobjCreate( 87 | gbm_device_get_fd(inst->gbmdev), 88 | 0, &tempobj) != 0) 89 | { 90 | return EGL_FALSE; 91 | } 92 | 93 | if (inst->platform->priv->drm.SyncobjTransfer(gbm_device_get_fd(inst->gbmdev), 94 | tempobj, 0, timeline->handle, timeline->point, 0) != 0) 95 | { 96 | goto done; 97 | } 98 | 99 | if (inst->platform->priv->drm.SyncobjExportSyncFile(gbm_device_get_fd(inst->gbmdev), 100 | tempobj, &syncfd) != 0) 101 | { 102 | goto done; 103 | } 104 | 105 | done: 106 | inst->platform->priv->drm.SyncobjDestroy( 107 | gbm_device_get_fd(inst->gbmdev), 108 | tempobj); 109 | return syncfd; 110 | } 111 | 112 | EGLBoolean eplX11TimelineAttachSyncFD(X11DisplayInstance *inst, X11Timeline *timeline, int syncfd) 113 | { 114 | uint32_t tempobj = 0; 115 | EGLBoolean success = EGL_FALSE; 116 | 117 | assert(syncfd >= 0); 118 | 119 | if (inst->platform->priv->drm.SyncobjCreate( 120 | gbm_device_get_fd(inst->gbmdev), 121 | 0, &tempobj) != 0) 122 | { 123 | // TODO: Issue an EGL error here? 124 | return EGL_FALSE; 125 | } 126 | 127 | if (inst->platform->priv->drm.SyncobjImportSyncFile( 128 | gbm_device_get_fd(inst->gbmdev), 129 | tempobj, syncfd) != 0) 130 | { 131 | goto done; 132 | } 133 | 134 | if (inst->platform->priv->drm.SyncobjTransfer( 135 | gbm_device_get_fd(inst->gbmdev), 136 | timeline->handle, timeline->point + 1, 137 | tempobj, 0, 0) != 0) 138 | { 139 | goto done; 140 | } 141 | 142 | timeline->point++; 143 | success = EGL_TRUE; 144 | 145 | done: 146 | inst->platform->priv->drm.SyncobjDestroy( 147 | gbm_device_get_fd(inst->gbmdev), 148 | tempobj); 149 | return success; 150 | } 151 | -------------------------------------------------------------------------------- /src/x11/x11-timeline.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef X11_TIMELINE_H 19 | #define X11_TIMELINE_H 20 | 21 | /** 22 | * \file 23 | * 24 | * Functions for dealing with timeline sync objects. 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | #include "x11-platform.h" 31 | 32 | typedef struct 33 | { 34 | uint32_t handle; 35 | uint32_t xid; 36 | uint64_t point; 37 | } X11Timeline; 38 | 39 | /** 40 | * Creates and initializes a timeline sync object. 41 | * 42 | * This will create a timeline object, and share it with the server using DRI3. 43 | */ 44 | EGLBoolean eplX11TimelineInit(X11DisplayInstance *inst, X11Timeline *timeline); 45 | void eplX11TimelineDestroy(X11DisplayInstance *inst, X11Timeline *timeline); 46 | 47 | /** 48 | * Attaches a sync FD to the next timeline point. 49 | * 50 | * On a successful return, \c timeline->point will be the timeline point where 51 | * the sync FD was attached. 52 | */ 53 | EGLBoolean eplX11TimelineAttachSyncFD(X11DisplayInstance *inst, X11Timeline *timeline, int syncfd); 54 | 55 | /** 56 | * Extracts a sync FD from the current timeline point. 57 | */ 58 | int eplX11TimelinePointToSyncFD(X11DisplayInstance *inst, X11Timeline *timeline); 59 | 60 | #endif // X11_TIMELINE_H 61 | --------------------------------------------------------------------------------