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, &registry_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