1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=4:tabstop=4:
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7
8 #include "nsWaylandDisplay.h"
9 #include "DMABufLibWrapper.h"
10 #include "mozilla/StaticPrefs_widget.h"
11 #include "mozilla/StaticPrefs_media.h"
12 #include "mozilla/gfx/gfxVars.h"
13 #include "WidgetUtilsGtk.h"
14
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #include <dlfcn.h>
19
20 namespace mozilla {
21 namespace widget {
22
23 #define GBMLIB_NAME "libgbm.so.1"
24 #define DRMLIB_NAME "libdrm.so.2"
25
26 // Use static lock to protect dri operation as
27 // gbm_dri.c is not thread safe.
28 // https://gitlab.freedesktop.org/mesa/mesa/-/issues/4422
29 mozilla::StaticMutex nsGbmLib::sDRILock;
30
31 void* nsGbmLib::sGbmLibHandle = nullptr;
32 void* nsGbmLib::sXf86DrmLibHandle = nullptr;
33 bool nsGbmLib::sLibLoaded = false;
34 CreateDeviceFunc nsGbmLib::sCreateDevice;
35 CreateFunc nsGbmLib::sCreate;
36 CreateWithModifiersFunc nsGbmLib::sCreateWithModifiers;
37 GetModifierFunc nsGbmLib::sGetModifier;
38 GetStrideFunc nsGbmLib::sGetStride;
39 GetFdFunc nsGbmLib::sGetFd;
40 DestroyFunc nsGbmLib::sDestroy;
41 MapFunc nsGbmLib::sMap;
42 UnmapFunc nsGbmLib::sUnmap;
43 GetPlaneCountFunc nsGbmLib::sGetPlaneCount;
44 GetHandleForPlaneFunc nsGbmLib::sGetHandleForPlane;
45 GetStrideForPlaneFunc nsGbmLib::sGetStrideForPlane;
46 GetOffsetFunc nsGbmLib::sGetOffset;
47 DeviceIsFormatSupportedFunc nsGbmLib::sDeviceIsFormatSupported;
48 DrmPrimeHandleToFDFunc nsGbmLib::sDrmPrimeHandleToFD;
49
IsLoaded()50 bool nsGbmLib::IsLoaded() {
51 return sCreateDevice != nullptr && sCreate != nullptr &&
52 sCreateWithModifiers != nullptr && sGetModifier != nullptr &&
53 sGetStride != nullptr && sGetFd != nullptr && sDestroy != nullptr &&
54 sMap != nullptr && sUnmap != nullptr && sGetPlaneCount != nullptr &&
55 sGetHandleForPlane != nullptr && sGetStrideForPlane != nullptr &&
56 sGetOffset != nullptr && sDeviceIsFormatSupported != nullptr &&
57 sDrmPrimeHandleToFD != nullptr;
58 }
59
IsAvailable()60 bool nsGbmLib::IsAvailable() {
61 if (!Load()) {
62 return false;
63 }
64 return IsLoaded();
65 }
66
Load()67 bool nsGbmLib::Load() {
68 if (!sGbmLibHandle && !sLibLoaded) {
69 LOGDMABUF(("Loading DMABuf system library %s ...\n", GBMLIB_NAME));
70 sLibLoaded = true;
71
72 sGbmLibHandle = dlopen(GBMLIB_NAME, RTLD_LAZY | RTLD_LOCAL);
73 if (!sGbmLibHandle) {
74 LOGDMABUF(("Failed to load %s, dmabuf isn't available.\n", GBMLIB_NAME));
75 return false;
76 }
77
78 sCreateDevice = (CreateDeviceFunc)dlsym(sGbmLibHandle, "gbm_create_device");
79 sCreate = (CreateFunc)dlsym(sGbmLibHandle, "gbm_bo_create");
80 sCreateWithModifiers = (CreateWithModifiersFunc)dlsym(
81 sGbmLibHandle, "gbm_bo_create_with_modifiers");
82 sGetModifier = (GetModifierFunc)dlsym(sGbmLibHandle, "gbm_bo_get_modifier");
83 sGetStride = (GetStrideFunc)dlsym(sGbmLibHandle, "gbm_bo_get_stride");
84 sGetFd = (GetFdFunc)dlsym(sGbmLibHandle, "gbm_bo_get_fd");
85 sDestroy = (DestroyFunc)dlsym(sGbmLibHandle, "gbm_bo_destroy");
86 sMap = (MapFunc)dlsym(sGbmLibHandle, "gbm_bo_map");
87 sUnmap = (UnmapFunc)dlsym(sGbmLibHandle, "gbm_bo_unmap");
88 sGetPlaneCount =
89 (GetPlaneCountFunc)dlsym(sGbmLibHandle, "gbm_bo_get_plane_count");
90 sGetHandleForPlane = (GetHandleForPlaneFunc)dlsym(
91 sGbmLibHandle, "gbm_bo_get_handle_for_plane");
92 sGetStrideForPlane = (GetStrideForPlaneFunc)dlsym(
93 sGbmLibHandle, "gbm_bo_get_stride_for_plane");
94 sGetOffset = (GetOffsetFunc)dlsym(sGbmLibHandle, "gbm_bo_get_offset");
95 sDeviceIsFormatSupported = (DeviceIsFormatSupportedFunc)dlsym(
96 sGbmLibHandle, "gbm_device_is_format_supported");
97
98 sXf86DrmLibHandle = dlopen(DRMLIB_NAME, RTLD_LAZY | RTLD_LOCAL);
99 if (!sXf86DrmLibHandle) {
100 LOGDMABUF(("Failed to load %s, dmabuf isn't available.\n", DRMLIB_NAME));
101 return false;
102 }
103 sDrmPrimeHandleToFD =
104 (DrmPrimeHandleToFDFunc)dlsym(sXf86DrmLibHandle, "drmPrimeHandleToFD");
105 if (!IsLoaded()) {
106 LOGDMABUF(("Failed to load all symbols from %s\n", GBMLIB_NAME));
107 }
108 }
109
110 return sGbmLibHandle;
111 }
112
GetGbmDevice()113 gbm_device* nsDMABufDevice::GetGbmDevice() {
114 return IsDMABufEnabled() ? mGbmDevice : nullptr;
115 }
116
GetGbmDeviceFd()117 int nsDMABufDevice::GetGbmDeviceFd() { return IsDMABufEnabled() ? mGbmFd : -1; }
118
dmabuf_modifiers(void * data,struct zwp_linux_dmabuf_v1 * zwp_linux_dmabuf,uint32_t format,uint32_t modifier_hi,uint32_t modifier_lo)119 static void dmabuf_modifiers(void* data,
120 struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf,
121 uint32_t format, uint32_t modifier_hi,
122 uint32_t modifier_lo) {
123 // skip modifiers marked as invalid
124 if (modifier_hi == (DRM_FORMAT_MOD_INVALID >> 32) &&
125 modifier_lo == (DRM_FORMAT_MOD_INVALID & 0xffffffff)) {
126 return;
127 }
128
129 auto* device = static_cast<nsDMABufDevice*>(data);
130 switch (format) {
131 case GBM_FORMAT_ARGB8888:
132 device->AddFormatModifier(true, format, modifier_hi, modifier_lo);
133 break;
134 case GBM_FORMAT_XRGB8888:
135 device->AddFormatModifier(false, format, modifier_hi, modifier_lo);
136 break;
137 default:
138 break;
139 }
140 }
141
dmabuf_format(void * data,struct zwp_linux_dmabuf_v1 * zwp_linux_dmabuf,uint32_t format)142 static void dmabuf_format(void* data,
143 struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf,
144 uint32_t format) {
145 // XXX: deprecated
146 }
147
148 static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = {
149 dmabuf_format, dmabuf_modifiers};
150
global_registry_handler(void * data,wl_registry * registry,uint32_t id,const char * interface,uint32_t version)151 static void global_registry_handler(void* data, wl_registry* registry,
152 uint32_t id, const char* interface,
153 uint32_t version) {
154 auto* device = static_cast<nsDMABufDevice*>(data);
155 if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0 && version > 2) {
156 auto* dmabuf = WaylandRegistryBind<zwp_linux_dmabuf_v1>(
157 registry, id, &zwp_linux_dmabuf_v1_interface, 3);
158 LOGDMABUF(("zwp_linux_dmabuf_v1 is available."));
159 device->ResetFormatsModifiers();
160 zwp_linux_dmabuf_v1_add_listener(dmabuf, &dmabuf_listener, data);
161 } else if (strcmp(interface, "wl_drm") == 0) {
162 LOGDMABUF(("wl_drm is available."));
163 }
164 }
165
global_registry_remover(void * data,wl_registry * registry,uint32_t id)166 static void global_registry_remover(void* data, wl_registry* registry,
167 uint32_t id) {}
168
169 static const struct wl_registry_listener registry_listener = {
170 global_registry_handler, global_registry_remover};
171
nsDMABufDevice()172 nsDMABufDevice::nsDMABufDevice()
173 : mXRGBFormat({true, false, GBM_FORMAT_XRGB8888, nullptr, 0}),
174 mARGBFormat({true, true, GBM_FORMAT_ARGB8888, nullptr, 0}),
175 mGbmDevice(nullptr),
176 mGbmFd(-1),
177 mInitialized(false) {
178 if (GdkIsWaylandDisplay()) {
179 wl_display* display = WaylandDisplayGetWLDisplay();
180 wl_registry* registry = wl_display_get_registry(display);
181 wl_registry_add_listener(registry, ®istry_listener, this);
182 wl_display_roundtrip(display);
183 wl_display_roundtrip(display);
184 wl_registry_destroy(registry);
185 }
186 }
187
Configure(nsACString & aFailureId)188 bool nsDMABufDevice::Configure(nsACString& aFailureId) {
189 LOGDMABUF(("nsDMABufDevice::Configure()"));
190
191 MOZ_ASSERT(!mInitialized);
192 mInitialized = true;
193
194 bool isDMABufUsed = (
195 #ifdef NIGHTLY_BUILD
196 StaticPrefs::widget_dmabuf_textures_enabled() ||
197 #endif
198 StaticPrefs::widget_dmabuf_webgl_enabled() ||
199 StaticPrefs::media_ffmpeg_vaapi_enabled() ||
200 StaticPrefs::media_ffmpeg_vaapi_drm_display_enabled());
201
202 if (!isDMABufUsed) {
203 // Disabled by user, just quit.
204 LOGDMABUF(("IsDMABufEnabled(): Disabled by preferences."));
205 aFailureId = "FEATURE_FAILURE_NO_PREFS_ENABLED";
206 return false;
207 }
208
209 if (!nsGbmLib::IsAvailable()) {
210 LOGDMABUF(("nsGbmLib is not available!"));
211 aFailureId = "FEATURE_FAILURE_NO_LIBGBM";
212 return false;
213 }
214
215 nsAutoCString drm_render_node(getenv("MOZ_WAYLAND_DRM_DEVICE"));
216 if (drm_render_node.IsEmpty()) {
217 drm_render_node.Assign(gfx::gfxVars::DrmRenderDevice());
218 if (drm_render_node.IsEmpty()) {
219 LOGDMABUF(("Failed: We're missing DRM render device!\n"));
220 aFailureId = "FEATURE_FAILURE_NO_DRM_RENDER_NODE";
221 return false;
222 }
223 }
224
225 mGbmFd = open(drm_render_node.get(), O_RDWR);
226 if (mGbmFd < 0) {
227 const char* error = strerror(errno);
228 LOGDMABUF(("Failed to open drm render node %s error %s\n",
229 drm_render_node.get(), error));
230 aFailureId = "FEATURE_FAILURE_BAD_DRM_RENDER_NODE";
231 return false;
232 }
233
234 mGbmDevice = nsGbmLib::CreateDevice(mGbmFd);
235 if (!mGbmDevice) {
236 LOGDMABUF(
237 ("Failed to create drm render device %s\n", drm_render_node.get()));
238 aFailureId = "FEATURE_FAILURE_NO_DRM_RENDER_DEVICE";
239 close(mGbmFd);
240 mGbmFd = -1;
241 return false;
242 }
243
244 LOGDMABUF(("DMABuf is enabled, using drm node %s", drm_render_node.get()));
245 return true;
246 }
247
IsDMABufEnabled()248 bool nsDMABufDevice::IsDMABufEnabled() {
249 if (!mInitialized) {
250 MOZ_ASSERT(!XRE_IsParentProcess());
251 nsCString failureId;
252 return Configure(failureId);
253 }
254 return !!mGbmDevice;
255 }
256
257 #ifdef NIGHTLY_BUILD
IsDMABufTexturesEnabled()258 bool nsDMABufDevice::IsDMABufTexturesEnabled() {
259 return gfx::gfxVars::UseDMABuf() && IsDMABufEnabled() &&
260 StaticPrefs::widget_dmabuf_textures_enabled();
261 }
262 #else
IsDMABufTexturesEnabled()263 bool nsDMABufDevice::IsDMABufTexturesEnabled() { return false; }
264 #endif
IsDMABufVideoEnabled()265 bool nsDMABufDevice::IsDMABufVideoEnabled() {
266 LOGDMABUF(
267 ("nsDMABufDevice::IsDMABufVideoEnabled: EGL %d DMABufEnabled %d "
268 "!media_ffmpeg_dmabuf_textures_disabled %d !XRE_IsRDDProcess() %d\n",
269 gfx::gfxVars::UseEGL(), IsDMABufEnabled(),
270 !StaticPrefs::media_ffmpeg_dmabuf_textures_disabled(),
271 !XRE_IsRDDProcess()));
272 return !StaticPrefs::media_ffmpeg_dmabuf_textures_disabled() &&
273 !XRE_IsRDDProcess() && gfx::gfxVars::UseDMABuf() && IsDMABufEnabled();
274 }
IsDMABufVAAPIEnabled()275 bool nsDMABufDevice::IsDMABufVAAPIEnabled() {
276 LOGDMABUF(
277 ("nsDMABufDevice::IsDMABufVAAPIEnabled: EGL %d DMABufEnabled %d "
278 "media_ffmpeg_vaapi_enabled %d CanUseHardwareVideoDecoding %d "
279 "!XRE_IsRDDProcess %d\n",
280 gfx::gfxVars::UseEGL(), IsDMABufEnabled(),
281 StaticPrefs::media_ffmpeg_vaapi_enabled(),
282 gfx::gfxVars::CanUseHardwareVideoDecoding(), !XRE_IsRDDProcess()));
283 return StaticPrefs::media_ffmpeg_vaapi_enabled() && !XRE_IsRDDProcess() &&
284 gfx::gfxVars::UseDMABuf() && IsDMABufEnabled() &&
285 gfx::gfxVars::CanUseHardwareVideoDecoding();
286 }
IsDMABufWebGLEnabled()287 bool nsDMABufDevice::IsDMABufWebGLEnabled() {
288 LOGDMABUF(
289 ("nsDMABufDevice::IsDMABufWebGLEnabled: EGL %d DMABufEnabled %d "
290 "widget_dmabuf_webgl_enabled %d\n",
291 gfx::gfxVars::UseEGL(), IsDMABufEnabled(),
292 StaticPrefs::widget_dmabuf_webgl_enabled()));
293 return gfx::gfxVars::UseDMABuf() && IsDMABufEnabled() &&
294 StaticPrefs::widget_dmabuf_webgl_enabled();
295 }
296
GetGbmFormat(bool aHasAlpha)297 GbmFormat* nsDMABufDevice::GetGbmFormat(bool aHasAlpha) {
298 GbmFormat* format = aHasAlpha ? &mARGBFormat : &mXRGBFormat;
299 return format->mIsSupported ? format : nullptr;
300 }
301
GetExactGbmFormat(int aFormat)302 GbmFormat* nsDMABufDevice::GetExactGbmFormat(int aFormat) {
303 if (aFormat == mARGBFormat.mFormat) {
304 return &mARGBFormat;
305 } else if (aFormat == mXRGBFormat.mFormat) {
306 return &mXRGBFormat;
307 }
308
309 return nullptr;
310 }
311
AddFormatModifier(bool aHasAlpha,int aFormat,uint32_t mModifierHi,uint32_t mModifierLo)312 void nsDMABufDevice::AddFormatModifier(bool aHasAlpha, int aFormat,
313 uint32_t mModifierHi,
314 uint32_t mModifierLo) {
315 GbmFormat* format = aHasAlpha ? &mARGBFormat : &mXRGBFormat;
316 format->mIsSupported = true;
317 format->mHasAlpha = aHasAlpha;
318 format->mFormat = aFormat;
319 format->mModifiersCount++;
320 format->mModifiers =
321 (uint64_t*)realloc(format->mModifiers,
322 format->mModifiersCount * sizeof(*format->mModifiers));
323 format->mModifiers[format->mModifiersCount - 1] =
324 ((uint64_t)mModifierHi << 32) | mModifierLo;
325 }
326
ResetFormatsModifiers()327 void nsDMABufDevice::ResetFormatsModifiers() {
328 mARGBFormat.mModifiersCount = 0;
329 free(mARGBFormat.mModifiers);
330 mARGBFormat.mModifiers = nullptr;
331
332 mXRGBFormat.mModifiersCount = 0;
333 free(mXRGBFormat.mModifiers);
334 mXRGBFormat.mModifiers = nullptr;
335 }
336
GetDMABufDevice()337 nsDMABufDevice* GetDMABufDevice() {
338 static nsDMABufDevice dmaBufDevice;
339 return &dmaBufDevice;
340 }
341
342 } // namespace widget
343 } // namespace mozilla
344