Bug 1861709 replace AudioCallbackDriver::ThreadRunning() assertions that mean to...
[gecko.git] / widget / gtk / DMABufLibWrapper.cpp
blob3db0219f8879cd8da244f250968c0ac102156865
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=2:tabstop=2:
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/. */
8 #include "DMABufLibWrapper.h"
9 #ifdef MOZ_WAYLAND
10 # include "nsWaylandDisplay.h"
11 #endif
12 #include "base/message_loop.h" // for MessageLoop
13 #include "mozilla/StaticPrefs_widget.h"
14 #include "mozilla/StaticPrefs_media.h"
15 #include "mozilla/gfx/gfxVars.h"
16 #include "WidgetUtilsGtk.h"
17 #include "gfxConfig.h"
18 #include "nsIGfxInfo.h"
19 #include "mozilla/Components.h"
20 #include "mozilla/ClearOnShutdown.h"
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <dlfcn.h>
26 #include <mutex>
27 #include <unistd.h>
28 #include "gbm.h"
30 using namespace mozilla::gfx;
32 namespace mozilla {
33 namespace widget {
35 bool sUseWebGLDmabufBackend = true;
37 #define GBMLIB_NAME "libgbm.so.1"
38 #define DRMLIB_NAME "libdrm.so.2"
40 // Use static lock to protect dri operation as
41 // gbm_dri.c is not thread safe.
42 // https://gitlab.freedesktop.org/mesa/mesa/-/issues/4422
43 mozilla::StaticMutex GbmLib::sDRILock MOZ_UNANNOTATED;
45 bool GbmLib::sLoaded = false;
46 void* GbmLib::sGbmLibHandle = nullptr;
47 void* GbmLib::sXf86DrmLibHandle = nullptr;
48 CreateDeviceFunc GbmLib::sCreateDevice;
49 DestroyDeviceFunc GbmLib::sDestroyDevice;
50 CreateFunc GbmLib::sCreate;
51 CreateWithModifiersFunc GbmLib::sCreateWithModifiers;
52 GetModifierFunc GbmLib::sGetModifier;
53 GetStrideFunc GbmLib::sGetStride;
54 GetFdFunc GbmLib::sGetFd;
55 DestroyFunc GbmLib::sDestroy;
56 MapFunc GbmLib::sMap;
57 UnmapFunc GbmLib::sUnmap;
58 GetPlaneCountFunc GbmLib::sGetPlaneCount;
59 GetHandleForPlaneFunc GbmLib::sGetHandleForPlane;
60 GetStrideForPlaneFunc GbmLib::sGetStrideForPlane;
61 GetOffsetFunc GbmLib::sGetOffset;
62 DeviceIsFormatSupportedFunc GbmLib::sDeviceIsFormatSupported;
63 DrmPrimeHandleToFDFunc GbmLib::sDrmPrimeHandleToFD;
64 CreateSurfaceFunc GbmLib::sCreateSurface;
65 DestroySurfaceFunc GbmLib::sDestroySurface;
67 bool GbmLib::IsLoaded() {
68 return sCreateDevice != nullptr && sDestroyDevice != nullptr &&
69 sCreate != nullptr && sCreateWithModifiers != nullptr &&
70 sGetModifier != nullptr && sGetStride != nullptr &&
71 sGetFd != nullptr && sDestroy != nullptr && sMap != nullptr &&
72 sUnmap != nullptr && sGetPlaneCount != nullptr &&
73 sGetHandleForPlane != nullptr && sGetStrideForPlane != nullptr &&
74 sGetOffset != nullptr && sDeviceIsFormatSupported != nullptr &&
75 sDrmPrimeHandleToFD != nullptr && sCreateSurface != nullptr &&
76 sDestroySurface != nullptr;
79 bool GbmLib::Load() {
80 static bool sTriedToLoad = false;
81 if (sTriedToLoad) {
82 return sLoaded;
85 sTriedToLoad = true;
87 MOZ_ASSERT(!sGbmLibHandle);
88 MOZ_ASSERT(!sLoaded);
90 LOGDMABUF(("Loading DMABuf system library %s ...\n", GBMLIB_NAME));
92 sGbmLibHandle = dlopen(GBMLIB_NAME, RTLD_LAZY | RTLD_LOCAL);
93 if (!sGbmLibHandle) {
94 LOGDMABUF(("Failed to load %s, dmabuf isn't available.\n", GBMLIB_NAME));
95 return false;
98 sCreateDevice = (CreateDeviceFunc)dlsym(sGbmLibHandle, "gbm_create_device");
99 sDestroyDevice =
100 (DestroyDeviceFunc)dlsym(sGbmLibHandle, "gbm_device_destroy");
101 sCreate = (CreateFunc)dlsym(sGbmLibHandle, "gbm_bo_create");
102 sCreateWithModifiers = (CreateWithModifiersFunc)dlsym(
103 sGbmLibHandle, "gbm_bo_create_with_modifiers");
104 sGetModifier = (GetModifierFunc)dlsym(sGbmLibHandle, "gbm_bo_get_modifier");
105 sGetStride = (GetStrideFunc)dlsym(sGbmLibHandle, "gbm_bo_get_stride");
106 sGetFd = (GetFdFunc)dlsym(sGbmLibHandle, "gbm_bo_get_fd");
107 sDestroy = (DestroyFunc)dlsym(sGbmLibHandle, "gbm_bo_destroy");
108 sMap = (MapFunc)dlsym(sGbmLibHandle, "gbm_bo_map");
109 sUnmap = (UnmapFunc)dlsym(sGbmLibHandle, "gbm_bo_unmap");
110 sGetPlaneCount =
111 (GetPlaneCountFunc)dlsym(sGbmLibHandle, "gbm_bo_get_plane_count");
112 sGetHandleForPlane = (GetHandleForPlaneFunc)dlsym(
113 sGbmLibHandle, "gbm_bo_get_handle_for_plane");
114 sGetStrideForPlane = (GetStrideForPlaneFunc)dlsym(
115 sGbmLibHandle, "gbm_bo_get_stride_for_plane");
116 sGetOffset = (GetOffsetFunc)dlsym(sGbmLibHandle, "gbm_bo_get_offset");
117 sDeviceIsFormatSupported = (DeviceIsFormatSupportedFunc)dlsym(
118 sGbmLibHandle, "gbm_device_is_format_supported");
119 sCreateSurface =
120 (CreateSurfaceFunc)dlsym(sGbmLibHandle, "gbm_surface_create");
121 sDestroySurface =
122 (DestroySurfaceFunc)dlsym(sGbmLibHandle, "gbm_surface_destroy");
124 sXf86DrmLibHandle = dlopen(DRMLIB_NAME, RTLD_LAZY | RTLD_LOCAL);
125 if (!sXf86DrmLibHandle) {
126 LOGDMABUF(("Failed to load %s, dmabuf isn't available.\n", DRMLIB_NAME));
127 return false;
129 sDrmPrimeHandleToFD =
130 (DrmPrimeHandleToFDFunc)dlsym(sXf86DrmLibHandle, "drmPrimeHandleToFD");
131 sLoaded = IsLoaded();
132 if (!sLoaded) {
133 LOGDMABUF(("Failed to load all symbols from %s\n", GBMLIB_NAME));
135 return sLoaded;
138 int DMABufDevice::GetDmabufFD(uint32_t aGEMHandle) {
139 int fd;
140 return GbmLib::DrmPrimeHandleToFD(mDRMFd, aGEMHandle, 0, &fd) < 0 ? -1 : fd;
143 gbm_device* DMABufDevice::GetGbmDevice() {
144 std::call_once(mFlagGbmDevice, [&] {
145 mGbmDevice = (mDRMFd != -1) ? GbmLib::CreateDevice(mDRMFd) : nullptr;
147 return mGbmDevice;
150 int DMABufDevice::OpenDRMFd() { return open(mDrmRenderNode.get(), O_RDWR); }
152 bool DMABufDevice::IsEnabled(nsACString& aFailureId) {
153 if (mDRMFd == -1) {
154 aFailureId = mFailureId;
156 return mDRMFd != -1;
159 DMABufDevice::DMABufDevice()
160 : mXRGBFormat({true, false, GBM_FORMAT_XRGB8888, {}}),
161 mARGBFormat({true, true, GBM_FORMAT_ARGB8888, {}}) {
162 Configure();
165 DMABufDevice::~DMABufDevice() {
166 if (mGbmDevice) {
167 GbmLib::DestroyDevice(mGbmDevice);
168 mGbmDevice = nullptr;
170 if (mDRMFd != -1) {
171 close(mDRMFd);
172 mDRMFd = -1;
176 void DMABufDevice::Configure() {
177 LOGDMABUF(("DMABufDevice::Configure()"));
179 if (!GbmLib::IsAvailable()) {
180 LOGDMABUF(("GbmLib is not available!"));
181 mFailureId = "FEATURE_FAILURE_NO_LIBGBM";
182 return;
185 mDrmRenderNode = nsAutoCString(getenv("MOZ_DRM_DEVICE"));
186 if (mDrmRenderNode.IsEmpty()) {
187 mDrmRenderNode.Assign(gfx::gfxVars::DrmRenderDevice());
189 if (mDrmRenderNode.IsEmpty()) {
190 LOGDMABUF(("We're missing DRM render device!\n"));
191 mFailureId = "FEATURE_FAILURE_NO_DRM_DEVICE";
192 return;
195 LOGDMABUF(("Using DRM device %s", mDrmRenderNode.get()));
196 mDRMFd = open(mDrmRenderNode.get(), O_RDWR);
197 if (mDRMFd < 0) {
198 LOGDMABUF(("Failed to open drm render node %s error %s\n",
199 mDrmRenderNode.get(), strerror(errno)));
200 mFailureId = "FEATURE_FAILURE_NO_DRM_DEVICE";
201 return;
204 #ifdef MOZ_WAYLAND
205 LoadFormatModifiers();
206 #endif
208 LOGDMABUF(("DMABuf is enabled"));
211 #ifdef NIGHTLY_BUILD
212 bool DMABufDevice::IsDMABufTexturesEnabled() {
213 return gfx::gfxVars::UseDMABuf() &&
214 StaticPrefs::widget_dmabuf_textures_enabled();
216 #else
217 bool DMABufDevice::IsDMABufTexturesEnabled() { return false; }
218 #endif
219 bool DMABufDevice::IsDMABufWebGLEnabled() {
220 LOGDMABUF(
221 ("DMABufDevice::IsDMABufWebGLEnabled: UseDMABuf %d "
222 "sUseWebGLDmabufBackend %d "
223 "widget_dmabuf_webgl_enabled %d\n",
224 gfx::gfxVars::UseDMABuf(), sUseWebGLDmabufBackend,
225 StaticPrefs::widget_dmabuf_webgl_enabled()));
226 return gfx::gfxVars::UseDMABuf() && sUseWebGLDmabufBackend &&
227 StaticPrefs::widget_dmabuf_webgl_enabled();
230 #ifdef MOZ_WAYLAND
231 void DMABufDevice::SetModifiersToGfxVars() {
232 gfxVars::SetDMABufModifiersXRGB(mXRGBFormat.mModifiers);
233 gfxVars::SetDMABufModifiersARGB(mARGBFormat.mModifiers);
236 void DMABufDevice::GetModifiersFromGfxVars() {
237 mXRGBFormat.mModifiers = gfxVars::DMABufModifiersXRGB().Clone();
238 mARGBFormat.mModifiers = gfxVars::DMABufModifiersARGB().Clone();
240 #endif
242 void DMABufDevice::DisableDMABufWebGL() { sUseWebGLDmabufBackend = false; }
244 GbmFormat* DMABufDevice::GetGbmFormat(bool aHasAlpha) {
245 GbmFormat* format = aHasAlpha ? &mARGBFormat : &mXRGBFormat;
246 return format->mIsSupported ? format : nullptr;
249 #ifdef MOZ_WAYLAND
250 void DMABufDevice::AddFormatModifier(bool aHasAlpha, int aFormat,
251 uint32_t mModifierHi,
252 uint32_t mModifierLo) {
253 GbmFormat* format = aHasAlpha ? &mARGBFormat : &mXRGBFormat;
254 format->mIsSupported = true;
255 format->mHasAlpha = aHasAlpha;
256 format->mFormat = aFormat;
257 format->mModifiers.AppendElement(((uint64_t)mModifierHi << 32) | mModifierLo);
260 static void dmabuf_modifiers(void* data,
261 struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf,
262 uint32_t format, uint32_t modifier_hi,
263 uint32_t modifier_lo) {
264 // skip modifiers marked as invalid
265 if (modifier_hi == (DRM_FORMAT_MOD_INVALID >> 32) &&
266 modifier_lo == (DRM_FORMAT_MOD_INVALID & 0xffffffff)) {
267 return;
270 auto* device = static_cast<DMABufDevice*>(data);
271 switch (format) {
272 case GBM_FORMAT_ARGB8888:
273 device->AddFormatModifier(true, format, modifier_hi, modifier_lo);
274 break;
275 case GBM_FORMAT_XRGB8888:
276 device->AddFormatModifier(false, format, modifier_hi, modifier_lo);
277 break;
278 default:
279 break;
283 static void dmabuf_format(void* data,
284 struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf,
285 uint32_t format) {
286 // XXX: deprecated
289 static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = {
290 dmabuf_format, dmabuf_modifiers};
292 static void global_registry_handler(void* data, wl_registry* registry,
293 uint32_t id, const char* interface,
294 uint32_t version) {
295 if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0 && version > 2) {
296 auto* dmabuf = WaylandRegistryBind<zwp_linux_dmabuf_v1>(
297 registry, id, &zwp_linux_dmabuf_v1_interface, 3);
298 LOGDMABUF(("zwp_linux_dmabuf_v1 is available."));
299 zwp_linux_dmabuf_v1_add_listener(dmabuf, &dmabuf_listener, data);
300 } else if (strcmp(interface, "wl_drm") == 0) {
301 LOGDMABUF(("wl_drm is available."));
305 static void global_registry_remover(void* data, wl_registry* registry,
306 uint32_t id) {}
308 static const struct wl_registry_listener registry_listener = {
309 global_registry_handler, global_registry_remover};
311 void DMABufDevice::LoadFormatModifiers() {
312 if (!GdkIsWaylandDisplay()) {
313 return;
315 if (XRE_IsParentProcess()) {
316 MOZ_ASSERT(NS_IsMainThread());
317 wl_display* display = WaylandDisplayGetWLDisplay();
318 wl_registry* registry = wl_display_get_registry(display);
319 wl_registry_add_listener(registry, &registry_listener, this);
320 wl_display_roundtrip(display);
321 wl_display_roundtrip(display);
322 wl_registry_destroy(registry);
323 SetModifiersToGfxVars();
324 } else {
325 GetModifiersFromGfxVars();
328 #endif
330 DMABufDevice* GetDMABufDevice() {
331 static StaticAutoPtr<DMABufDevice> sDmaBufDevice;
332 static std::once_flag onceFlag;
333 std::call_once(onceFlag, [] {
334 sDmaBufDevice = new DMABufDevice();
335 if (NS_IsMainThread()) {
336 ClearOnShutdown(&sDmaBufDevice);
337 } else {
338 NS_DispatchToMainThread(NS_NewRunnableFunction(
339 "ClearDmaBufDevice", [] { ClearOnShutdown(&sDmaBufDevice); }));
342 return sDmaBufDevice.get();
345 } // namespace widget
346 } // namespace mozilla