1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "DMABufSurface.h"
8
9 #include <fcntl.h>
10 #include <getopt.h>
11 #include <signal.h>
12 #include <stdbool.h>
13 #include <stdint.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <sys/time.h>
19 #include <dlfcn.h>
20 #include <sys/mman.h>
21 #include <sys/eventfd.h>
22 #include <poll.h>
23 #include <sys/ioctl.h>
24
25 #include "mozilla/widget/gbm.h"
26 #include "mozilla/widget/va_drmcommon.h"
27 #include "GLContextTypes.h" // for GLContext, etc
28 #include "GLContextEGL.h"
29 #include "GLContextProvider.h"
30 #include "ScopedGLHelpers.h"
31
32 #include "mozilla/layers/LayersSurfaces.h"
33 #include "mozilla/ScopeExit.h"
34
35 /*
36 TODO:
37 DRM device selection:
38 https://lists.freedesktop.org/archives/wayland-devel/2018-November/039660.html
39 */
40
41 /* C++ / C typecast macros for special EGL handle values */
42 #if defined(__cplusplus)
43 # define EGL_CAST(type, value) (static_cast<type>(value))
44 #else
45 # define EGL_CAST(type, value) ((type)(value))
46 #endif
47
48 using namespace mozilla;
49 using namespace mozilla::widget;
50 using namespace mozilla::gl;
51 using namespace mozilla::layers;
52
53 #define BUFFER_FLAGS 0
54
55 #ifndef VA_FOURCC_NV12
56 # define VA_FOURCC_NV12 0x3231564E
57 #endif
58
59 #ifndef VA_FOURCC_YV12
60 # define VA_FOURCC_YV12 0x32315659
61 #endif
62
63 static Atomic<int> gNewSurfaceUID(1);
64
IsGlobalRefSet() const65 bool DMABufSurface::IsGlobalRefSet() const {
66 if (!mGlobalRefCountFd) {
67 return false;
68 }
69 struct pollfd pfd;
70 pfd.fd = mGlobalRefCountFd;
71 pfd.events = POLLIN;
72 return poll(&pfd, 1, 0) == 1;
73 }
74
GlobalRefRelease()75 void DMABufSurface::GlobalRefRelease() {
76 MOZ_ASSERT(mGlobalRefCountFd);
77 uint64_t counter;
78 if (read(mGlobalRefCountFd, &counter, sizeof(counter)) != sizeof(counter)) {
79 // EAGAIN means the refcount is already zero. It happens when we release
80 // last reference to the surface.
81 if (errno != EAGAIN) {
82 NS_WARNING(nsPrintfCString("Failed to unref dmabuf global ref count: %s",
83 strerror(errno))
84 .get());
85 }
86 }
87 }
88
GlobalRefAdd()89 void DMABufSurface::GlobalRefAdd() {
90 MOZ_ASSERT(mGlobalRefCountFd);
91 uint64_t counter = 1;
92 if (write(mGlobalRefCountFd, &counter, sizeof(counter)) != sizeof(counter)) {
93 NS_WARNING(nsPrintfCString("Failed to ref dmabuf global ref count: %s",
94 strerror(errno))
95 .get());
96 }
97 }
98
GlobalRefCountCreate()99 void DMABufSurface::GlobalRefCountCreate() {
100 MOZ_ASSERT(!mGlobalRefCountFd);
101 mGlobalRefCountFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK | EFD_SEMAPHORE);
102 if (mGlobalRefCountFd < 0) {
103 NS_WARNING(nsPrintfCString("Failed to create dmabuf global ref count: %s",
104 strerror(errno))
105 .get());
106 mGlobalRefCountFd = 0;
107 return;
108 }
109 }
110
GlobalRefCountImport(int aFd)111 void DMABufSurface::GlobalRefCountImport(int aFd) {
112 MOZ_ASSERT(!mGlobalRefCountFd);
113 mGlobalRefCountFd = aFd;
114 GlobalRefAdd();
115 }
116
GlobalRefCountDelete()117 void DMABufSurface::GlobalRefCountDelete() {
118 if (mGlobalRefCountFd) {
119 GlobalRefRelease();
120 close(mGlobalRefCountFd);
121 mGlobalRefCountFd = 0;
122 }
123 }
124
ReleaseDMABuf()125 void DMABufSurface::ReleaseDMABuf() {
126 LOGDMABUF(("DMABufSurface::ReleaseDMABuf() UID %d", mUID));
127 for (int i = 0; i < mBufferPlaneCount; i++) {
128 Unmap(i);
129 }
130
131 MutexAutoLock lockFD(mSurfaceLock);
132 CloseFileDescriptors(/* aForceClose */ true);
133
134 for (int i = 0; i < mBufferPlaneCount; i++) {
135 if (mGbmBufferObject[i]) {
136 nsGbmLib::Destroy(mGbmBufferObject[i]);
137 mGbmBufferObject[i] = nullptr;
138 }
139 }
140 mBufferPlaneCount = 0;
141 }
142
DMABufSurface(SurfaceType aSurfaceType)143 DMABufSurface::DMABufSurface(SurfaceType aSurfaceType)
144 : mSurfaceType(aSurfaceType),
145 mBufferModifier(DRM_FORMAT_MOD_INVALID),
146 mBufferPlaneCount(0),
147 mDrmFormats(),
148 mStrides(),
149 mOffsets(),
150 mGbmBufferObject(),
151 mMappedRegion(),
152 mMappedRegionStride(),
153 mSyncFd(-1),
154 mSync(0),
155 mGlobalRefCountFd(0),
156 mUID(gNewSurfaceUID++),
157 mSurfaceLock("DMABufSurface") {
158 for (auto& slot : mDmabufFds) {
159 slot = -1;
160 }
161 }
162
~DMABufSurface()163 DMABufSurface::~DMABufSurface() {
164 FenceDelete();
165 GlobalRefCountDelete();
166 }
167
CreateDMABufSurface(const mozilla::layers::SurfaceDescriptor & aDesc)168 already_AddRefed<DMABufSurface> DMABufSurface::CreateDMABufSurface(
169 const mozilla::layers::SurfaceDescriptor& aDesc) {
170 const SurfaceDescriptorDMABuf& desc = aDesc.get_SurfaceDescriptorDMABuf();
171 RefPtr<DMABufSurface> surf;
172
173 switch (desc.bufferType()) {
174 case SURFACE_RGBA:
175 surf = new DMABufSurfaceRGBA();
176 break;
177 case SURFACE_NV12:
178 case SURFACE_YUV420:
179 surf = new DMABufSurfaceYUV();
180 break;
181 default:
182 return nullptr;
183 }
184
185 if (!surf->Create(desc)) {
186 return nullptr;
187 }
188 return surf.forget();
189 }
190
FenceDelete()191 void DMABufSurface::FenceDelete() {
192 if (mSyncFd > 0) {
193 close(mSyncFd);
194 mSyncFd = -1;
195 }
196
197 if (!mGL) return;
198 const auto& gle = gl::GLContextEGL::Cast(mGL);
199 const auto& egl = gle->mEgl;
200
201 if (mSync) {
202 egl->fDestroySync(mSync);
203 mSync = nullptr;
204 }
205 }
206
FenceSet()207 void DMABufSurface::FenceSet() {
208 if (!mGL || !mGL->MakeCurrent()) {
209 return;
210 }
211 const auto& gle = gl::GLContextEGL::Cast(mGL);
212 const auto& egl = gle->mEgl;
213
214 if (egl->IsExtensionSupported(EGLExtension::KHR_fence_sync) &&
215 egl->IsExtensionSupported(EGLExtension::ANDROID_native_fence_sync)) {
216 FenceDelete();
217
218 mSync = egl->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
219 if (mSync) {
220 mSyncFd = egl->fDupNativeFenceFDANDROID(mSync);
221 mGL->fFlush();
222 return;
223 }
224 }
225
226 // ANDROID_native_fence_sync may not be supported so call glFinish()
227 // as a slow path.
228 mGL->fFinish();
229 }
230
FenceWait()231 void DMABufSurface::FenceWait() {
232 if (!mGL) return;
233 const auto& gle = gl::GLContextEGL::Cast(mGL);
234 const auto& egl = gle->mEgl;
235
236 if (!mSync && mSyncFd > 0) {
237 FenceImportFromFd();
238 }
239
240 // Wait on the fence, because presumably we're going to want to read this
241 // surface
242 if (mSync) {
243 egl->fClientWaitSync(mSync, 0, LOCAL_EGL_FOREVER);
244 }
245 }
246
FenceImportFromFd()247 bool DMABufSurface::FenceImportFromFd() {
248 if (!mGL) return false;
249 const auto& gle = gl::GLContextEGL::Cast(mGL);
250 const auto& egl = gle->mEgl;
251
252 const EGLint attribs[] = {LOCAL_EGL_SYNC_NATIVE_FENCE_FD_ANDROID, mSyncFd,
253 LOCAL_EGL_NONE};
254 mSync = egl->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
255 close(mSyncFd);
256 mSyncFd = -1;
257
258 if (!mSync) {
259 MOZ_ASSERT(false, "Failed to create GLFence!");
260 return false;
261 }
262
263 return true;
264 }
265
OpenFileDescriptors()266 bool DMABufSurface::OpenFileDescriptors() {
267 for (int i = 0; i < mBufferPlaneCount; i++) {
268 if (!OpenFileDescriptorForPlane(i)) {
269 return false;
270 }
271 }
272 return true;
273 }
274
275 // We can safely close DMABuf file descriptors only when we have a valid
276 // GbmBufferObject. When we don't have a valid GbmBufferObject and a DMABuf
277 // file descriptor is closed, whole surface is released.
CloseFileDescriptors(bool aForceClose)278 void DMABufSurface::CloseFileDescriptors(bool aForceClose) {
279 for (int i = 0; i < DMABUF_BUFFER_PLANES; i++) {
280 CloseFileDescriptorForPlane(i, aForceClose);
281 }
282 }
283
DMABufSurfaceRGBA()284 DMABufSurfaceRGBA::DMABufSurfaceRGBA()
285 : DMABufSurface(SURFACE_RGBA),
286 mSurfaceFlags(0),
287 mWidth(0),
288 mHeight(0),
289 mGmbFormat(nullptr),
290 mEGLImage(LOCAL_EGL_NO_IMAGE),
291 mTexture(0),
292 mGbmBufferFlags(0) {}
293
~DMABufSurfaceRGBA()294 DMABufSurfaceRGBA::~DMABufSurfaceRGBA() { ReleaseSurface(); }
295
OpenFileDescriptorForPlane(int aPlane)296 bool DMABufSurfaceRGBA::OpenFileDescriptorForPlane(int aPlane) {
297 if (mDmabufFds[aPlane] >= 0) {
298 return true;
299 }
300 if (mBufferPlaneCount == 1) {
301 MOZ_ASSERT(aPlane == 0, "DMABuf: wrong surface plane!");
302 mDmabufFds[0] = nsGbmLib::GetFd(mGbmBufferObject[0]);
303 } else {
304 uint32_t handle =
305 nsGbmLib::GetHandleForPlane(mGbmBufferObject[0], aPlane).u32;
306 int ret = nsGbmLib::DrmPrimeHandleToFD(GetDMABufDevice()->GetGbmDeviceFd(),
307 handle, 0, &mDmabufFds[aPlane]);
308 if (ret < 0) {
309 mDmabufFds[aPlane] = -1;
310 }
311 }
312
313 if (mDmabufFds[aPlane] < 0) {
314 CloseFileDescriptors();
315 return false;
316 }
317
318 return true;
319 }
320
CloseFileDescriptorForPlane(int aPlane,bool aForceClose=false)321 void DMABufSurfaceRGBA::CloseFileDescriptorForPlane(int aPlane,
322 bool aForceClose = false) {
323 if ((aForceClose || mGbmBufferObject[0]) && mDmabufFds[aPlane] >= 0) {
324 close(mDmabufFds[aPlane]);
325 mDmabufFds[aPlane] = -1;
326 }
327 }
328
Create(int aWidth,int aHeight,int aDMABufSurfaceFlags)329 bool DMABufSurfaceRGBA::Create(int aWidth, int aHeight,
330 int aDMABufSurfaceFlags) {
331 MOZ_ASSERT(mGbmBufferObject[0] == nullptr, "Already created?");
332
333 mSurfaceFlags = aDMABufSurfaceFlags;
334 mWidth = aWidth;
335 mHeight = aHeight;
336
337 LOGDMABUF(("DMABufSurfaceRGBA::Create() UID %d size %d x %d\n", mUID, mWidth,
338 mHeight));
339
340 mGmbFormat = GetDMABufDevice()->GetGbmFormat(mSurfaceFlags & DMABUF_ALPHA);
341 if (!mGmbFormat) {
342 // Requested DRM format is not supported.
343 return false;
344 }
345
346 bool useModifiers = (aDMABufSurfaceFlags & DMABUF_USE_MODIFIERS) &&
347 mGmbFormat->mModifiersCount > 0;
348 if (useModifiers) {
349 LOGDMABUF((" Creating with modifiers\n"));
350 mGbmBufferObject[0] = nsGbmLib::CreateWithModifiers(
351 GetDMABufDevice()->GetGbmDevice(), mWidth, mHeight, mGmbFormat->mFormat,
352 mGmbFormat->mModifiers, mGmbFormat->mModifiersCount);
353 if (mGbmBufferObject[0]) {
354 mBufferModifier = nsGbmLib::GetModifier(mGbmBufferObject[0]);
355 }
356 }
357
358 if (!mGbmBufferObject[0]) {
359 LOGDMABUF((" Creating without modifiers\n"));
360 mGbmBufferFlags = GBM_BO_USE_LINEAR;
361 mGbmBufferObject[0] =
362 nsGbmLib::Create(GetDMABufDevice()->GetGbmDevice(), mWidth, mHeight,
363 mGmbFormat->mFormat, mGbmBufferFlags);
364 mBufferModifier = DRM_FORMAT_MOD_INVALID;
365 }
366
367 if (!mGbmBufferObject[0]) {
368 LOGDMABUF((" Failed to create GbmBufferObject\n"));
369 return false;
370 }
371
372 if (mBufferModifier != DRM_FORMAT_MOD_INVALID) {
373 mBufferPlaneCount = nsGbmLib::GetPlaneCount(mGbmBufferObject[0]);
374 if (mBufferPlaneCount > DMABUF_BUFFER_PLANES) {
375 LOGDMABUF((" There's too many dmabuf planes!"));
376 ReleaseSurface();
377 return false;
378 }
379
380 for (int i = 0; i < mBufferPlaneCount; i++) {
381 mStrides[i] = nsGbmLib::GetStrideForPlane(mGbmBufferObject[0], i);
382 mOffsets[i] = nsGbmLib::GetOffset(mGbmBufferObject[0], i);
383 }
384 } else {
385 mBufferPlaneCount = 1;
386 mStrides[0] = nsGbmLib::GetStride(mGbmBufferObject[0]);
387 }
388
389 LOGDMABUF((" Success\n"));
390 return true;
391 }
392
ImportSurfaceDescriptor(const SurfaceDescriptor & aDesc)393 bool DMABufSurfaceRGBA::ImportSurfaceDescriptor(
394 const SurfaceDescriptor& aDesc) {
395 const SurfaceDescriptorDMABuf& desc = aDesc.get_SurfaceDescriptorDMABuf();
396
397 mWidth = desc.width()[0];
398 mHeight = desc.height()[0];
399 mBufferModifier = desc.modifier();
400 if (mBufferModifier != DRM_FORMAT_MOD_INVALID) {
401 mGmbFormat = GetDMABufDevice()->GetExactGbmFormat(desc.format()[0]);
402 } else {
403 mDrmFormats[0] = desc.format()[0];
404 }
405 mBufferPlaneCount = desc.fds().Length();
406 mGbmBufferFlags = desc.flags();
407 MOZ_RELEASE_ASSERT(mBufferPlaneCount <= DMABUF_BUFFER_PLANES);
408 mUID = desc.uid();
409
410 LOGDMABUF(
411 ("DMABufSurfaceRGBA::ImportSurfaceDescriptor() UID %d size %d x %d\n",
412 mUID, mWidth, mHeight));
413
414 for (int i = 0; i < mBufferPlaneCount; i++) {
415 mDmabufFds[i] = desc.fds()[i].ClonePlatformHandle().release();
416 if (mDmabufFds[i] < 0) {
417 LOGDMABUF(
418 (" failed to get DMABuf file descriptor: %s", strerror(errno)));
419 return false;
420 }
421 mStrides[i] = desc.strides()[i];
422 mOffsets[i] = desc.offsets()[i];
423 }
424
425 if (desc.fence().Length() > 0) {
426 mSyncFd = desc.fence()[0].ClonePlatformHandle().release();
427 if (mSyncFd < 0) {
428 LOGDMABUF(
429 (" failed to get GL fence file descriptor: %s", strerror(errno)));
430 return false;
431 }
432 }
433
434 if (desc.refCount().Length() > 0) {
435 GlobalRefCountImport(desc.refCount()[0].ClonePlatformHandle().release());
436 }
437
438 return true;
439 }
440
Create(const SurfaceDescriptor & aDesc)441 bool DMABufSurfaceRGBA::Create(const SurfaceDescriptor& aDesc) {
442 return ImportSurfaceDescriptor(aDesc);
443 }
444
Serialize(mozilla::layers::SurfaceDescriptor & aOutDescriptor)445 bool DMABufSurfaceRGBA::Serialize(
446 mozilla::layers::SurfaceDescriptor& aOutDescriptor) {
447 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> width;
448 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> height;
449 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> format;
450 AutoTArray<ipc::FileDescriptor, DMABUF_BUFFER_PLANES> fds;
451 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> strides;
452 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> offsets;
453 AutoTArray<uintptr_t, DMABUF_BUFFER_PLANES> images;
454 AutoTArray<ipc::FileDescriptor, 1> fenceFDs;
455 AutoTArray<ipc::FileDescriptor, 1> refCountFDs;
456
457 LOGDMABUF(("DMABufSurfaceRGBA::Serialize() UID %d\n", mUID));
458
459 MutexAutoLock lockFD(mSurfaceLock);
460 if (!OpenFileDescriptors()) {
461 return false;
462 }
463
464 width.AppendElement(mWidth);
465 height.AppendElement(mHeight);
466 format.AppendElement(mGmbFormat->mFormat);
467 for (int i = 0; i < mBufferPlaneCount; i++) {
468 fds.AppendElement(ipc::FileDescriptor(mDmabufFds[i]));
469 strides.AppendElement(mStrides[i]);
470 offsets.AppendElement(mOffsets[i]);
471 }
472
473 CloseFileDescriptors();
474
475 if (mSync) {
476 fenceFDs.AppendElement(ipc::FileDescriptor(mSyncFd));
477 }
478
479 if (mGlobalRefCountFd) {
480 refCountFDs.AppendElement(ipc::FileDescriptor(mGlobalRefCountFd));
481 }
482
483 aOutDescriptor =
484 SurfaceDescriptorDMABuf(mSurfaceType, mBufferModifier, mGbmBufferFlags,
485 fds, width, height, format, strides, offsets,
486 GetYUVColorSpace(), fenceFDs, mUID, refCountFDs);
487 return true;
488 }
489
CreateTexture(GLContext * aGLContext,int aPlane)490 bool DMABufSurfaceRGBA::CreateTexture(GLContext* aGLContext, int aPlane) {
491 MOZ_ASSERT(!mEGLImage && !mTexture, "EGLImage is already created!");
492
493 nsTArray<EGLint> attribs;
494 attribs.AppendElement(LOCAL_EGL_WIDTH);
495 attribs.AppendElement(mWidth);
496 attribs.AppendElement(LOCAL_EGL_HEIGHT);
497 attribs.AppendElement(mHeight);
498 attribs.AppendElement(LOCAL_EGL_LINUX_DRM_FOURCC_EXT);
499 if (mGmbFormat) {
500 attribs.AppendElement(mGmbFormat->mFormat);
501 } else {
502 attribs.AppendElement(mDrmFormats[0]);
503 }
504 #define ADD_PLANE_ATTRIBS(plane_idx) \
505 { \
506 attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_FD_EXT); \
507 attribs.AppendElement(mDmabufFds[plane_idx]); \
508 attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_OFFSET_EXT); \
509 attribs.AppendElement((int)mOffsets[plane_idx]); \
510 attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_PITCH_EXT); \
511 attribs.AppendElement((int)mStrides[plane_idx]); \
512 if (mBufferModifier != DRM_FORMAT_MOD_INVALID) { \
513 attribs.AppendElement( \
514 LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_MODIFIER_LO_EXT); \
515 attribs.AppendElement(mBufferModifier & 0xFFFFFFFF); \
516 attribs.AppendElement( \
517 LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_MODIFIER_HI_EXT); \
518 attribs.AppendElement(mBufferModifier >> 32); \
519 } \
520 }
521
522 MutexAutoLock lockFD(mSurfaceLock);
523 if (!OpenFileDescriptors()) {
524 return false;
525 }
526 ADD_PLANE_ATTRIBS(0);
527 if (mBufferPlaneCount > 1) ADD_PLANE_ATTRIBS(1);
528 if (mBufferPlaneCount > 2) ADD_PLANE_ATTRIBS(2);
529 if (mBufferPlaneCount > 3) ADD_PLANE_ATTRIBS(3);
530 #undef ADD_PLANE_ATTRIBS
531 attribs.AppendElement(LOCAL_EGL_NONE);
532
533 if (!aGLContext) return false;
534 const auto& gle = gl::GLContextEGL::Cast(aGLContext);
535 const auto& egl = gle->mEgl;
536 mEGLImage =
537 egl->fCreateImage(LOCAL_EGL_NO_CONTEXT, LOCAL_EGL_LINUX_DMA_BUF_EXT,
538 nullptr, attribs.Elements());
539 if (mEGLImage == LOCAL_EGL_NO_IMAGE) {
540 LOGDMABUF(("EGLImageKHR creation failed"));
541 return false;
542 }
543
544 CloseFileDescriptors();
545
546 aGLContext->MakeCurrent();
547 aGLContext->fGenTextures(1, &mTexture);
548 const ScopedBindTexture savedTex(aGLContext, mTexture);
549 aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S,
550 LOCAL_GL_CLAMP_TO_EDGE);
551 aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T,
552 LOCAL_GL_CLAMP_TO_EDGE);
553 aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER,
554 LOCAL_GL_LINEAR);
555 aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER,
556 LOCAL_GL_LINEAR);
557 aGLContext->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D, mEGLImage);
558 mGL = aGLContext;
559
560 return true;
561 }
562
ReleaseTextures()563 void DMABufSurfaceRGBA::ReleaseTextures() {
564 FenceDelete();
565
566 if (!mGL) return;
567 const auto& gle = gl::GLContextEGL::Cast(mGL);
568 const auto& egl = gle->mEgl;
569
570 if (mTexture && mGL->MakeCurrent()) {
571 mGL->fDeleteTextures(1, &mTexture);
572 mTexture = 0;
573 mGL = nullptr;
574 }
575
576 if (mEGLImage) {
577 egl->fDestroyImage(mEGLImage);
578 mEGLImage = nullptr;
579 }
580 }
581
ReleaseSurface()582 void DMABufSurfaceRGBA::ReleaseSurface() {
583 MOZ_ASSERT(!IsMapped(), "We can't release mapped buffer!");
584
585 ReleaseTextures();
586 ReleaseDMABuf();
587 }
588
589 // We should synchronize DMA Buffer object access from CPU to avoid potential
590 // cache incoherency and data loss.
591 // See
592 // https://01.org/linuxgraphics/gfx-docs/drm/driver-api/dma-buf.html#cpu-access-to-dma-buffer-objects
593 struct dma_buf_sync {
594 uint64_t flags;
595 };
596 #define DMA_BUF_SYNC_READ (1 << 0)
597 #define DMA_BUF_SYNC_WRITE (2 << 0)
598 #define DMA_BUF_SYNC_START (0 << 2)
599 #define DMA_BUF_SYNC_END (1 << 2)
600 #define DMA_BUF_BASE 'b'
601 #define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync)
602
SyncDmaBuf(int aFd,uint64_t aFlags)603 static void SyncDmaBuf(int aFd, uint64_t aFlags) {
604 struct dma_buf_sync sync = {0};
605
606 sync.flags = aFlags | DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE;
607 while (true) {
608 int ret;
609 ret = ioctl(aFd, DMA_BUF_IOCTL_SYNC, &sync);
610 if (ret == -1 && errno == EINTR) {
611 continue;
612 } else if (ret == -1) {
613 LOGDMABUF(
614 ("Failed to synchronize DMA buffer: %s FD %d", strerror(errno), aFd));
615 break;
616 } else {
617 break;
618 }
619 }
620 }
621
MapInternal(uint32_t aX,uint32_t aY,uint32_t aWidth,uint32_t aHeight,uint32_t * aStride,int aGbmFlags,int aPlane)622 void* DMABufSurface::MapInternal(uint32_t aX, uint32_t aY, uint32_t aWidth,
623 uint32_t aHeight, uint32_t* aStride,
624 int aGbmFlags, int aPlane) {
625 NS_ASSERTION(!IsMapped(aPlane), "Already mapped!");
626 if (!mGbmBufferObject[aPlane]) {
627 NS_WARNING("We can't map DMABufSurfaceRGBA without mGbmBufferObject");
628 return nullptr;
629 }
630
631 LOGDMABUF(
632 ("DMABufSurfaceRGBA::MapInternal() UID %d plane %d size %d x %d -> %d x "
633 "%d\n",
634 mUID, aPlane, aX, aY, aWidth, aHeight));
635
636 mMappedRegionStride[aPlane] = 0;
637 mMappedRegionData[aPlane] = nullptr;
638 mMappedRegion[aPlane] = nsGbmLib::Map(
639 mGbmBufferObject[aPlane], aX, aY, aWidth, aHeight, aGbmFlags,
640 &mMappedRegionStride[aPlane], &mMappedRegionData[aPlane]);
641 if (!mMappedRegion[aPlane]) {
642 LOGDMABUF((" Surface mapping failed: %s", strerror(errno)));
643 return nullptr;
644 }
645 if (aStride) {
646 *aStride = mMappedRegionStride[aPlane];
647 }
648
649 MutexAutoLock lockFD(mSurfaceLock);
650 if (OpenFileDescriptorForPlane(aPlane)) {
651 SyncDmaBuf(mDmabufFds[aPlane], DMA_BUF_SYNC_START);
652 CloseFileDescriptorForPlane(aPlane);
653 }
654
655 return mMappedRegion[aPlane];
656 }
657
MapReadOnly(uint32_t aX,uint32_t aY,uint32_t aWidth,uint32_t aHeight,uint32_t * aStride)658 void* DMABufSurfaceRGBA::MapReadOnly(uint32_t aX, uint32_t aY, uint32_t aWidth,
659 uint32_t aHeight, uint32_t* aStride) {
660 return MapInternal(aX, aY, aWidth, aHeight, aStride, GBM_BO_TRANSFER_READ);
661 }
662
MapReadOnly(uint32_t * aStride)663 void* DMABufSurfaceRGBA::MapReadOnly(uint32_t* aStride) {
664 return MapInternal(0, 0, mWidth, mHeight, aStride, GBM_BO_TRANSFER_READ);
665 }
666
Map(uint32_t aX,uint32_t aY,uint32_t aWidth,uint32_t aHeight,uint32_t * aStride)667 void* DMABufSurfaceRGBA::Map(uint32_t aX, uint32_t aY, uint32_t aWidth,
668 uint32_t aHeight, uint32_t* aStride) {
669 return MapInternal(aX, aY, aWidth, aHeight, aStride,
670 GBM_BO_TRANSFER_READ_WRITE);
671 }
672
Map(uint32_t * aStride)673 void* DMABufSurfaceRGBA::Map(uint32_t* aStride) {
674 return MapInternal(0, 0, mWidth, mHeight, aStride,
675 GBM_BO_TRANSFER_READ_WRITE);
676 }
677
Unmap(int aPlane)678 void DMABufSurface::Unmap(int aPlane) {
679 if (mMappedRegion[aPlane]) {
680 LOGDMABUF(("DMABufSurface::Unmap() UID %d plane %d\n", mUID, aPlane));
681 MutexAutoLock lockFD(mSurfaceLock);
682 if (OpenFileDescriptorForPlane(aPlane)) {
683 SyncDmaBuf(mDmabufFds[aPlane], DMA_BUF_SYNC_END);
684 CloseFileDescriptorForPlane(aPlane);
685 }
686 nsGbmLib::Unmap(mGbmBufferObject[aPlane], mMappedRegionData[aPlane]);
687 mMappedRegion[aPlane] = nullptr;
688 mMappedRegionData[aPlane] = nullptr;
689 mMappedRegionStride[aPlane] = 0;
690 }
691 }
692
693 #ifdef DEBUG
DumpToFile(const char * pFile)694 void DMABufSurfaceRGBA::DumpToFile(const char* pFile) {
695 uint32_t stride;
696
697 if (!MapReadOnly(&stride)) {
698 return;
699 }
700 cairo_surface_t* surface = nullptr;
701
702 auto unmap = MakeScopeExit([&] {
703 if (surface) {
704 cairo_surface_destroy(surface);
705 }
706 Unmap();
707 });
708
709 surface = cairo_image_surface_create_for_data(
710 (unsigned char*)mMappedRegion[0], CAIRO_FORMAT_ARGB32, mWidth, mHeight,
711 stride);
712 if (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS) {
713 cairo_surface_write_to_png(surface, pFile);
714 }
715 }
716 #endif
717
718 #if 0
719 // Copy from source surface by GL
720 # include "GLBlitHelper.h"
721
722 bool DMABufSurfaceRGBA::CopyFrom(class DMABufSurface* aSourceSurface,
723 GLContext* aGLContext) {
724 MOZ_ASSERT(aSourceSurface->GetTexture());
725 MOZ_ASSERT(GetTexture());
726
727 gfx::IntSize size(GetWidth(), GetHeight());
728 aGLContext->BlitHelper()->BlitTextureToTexture(aSourceSurface->GetTexture(),
729 GetTexture(), size, size);
730 return true;
731 }
732 #endif
733
734 // TODO - Clear the surface by EGL
Clear()735 void DMABufSurfaceRGBA::Clear() {
736 uint32_t destStride;
737 void* destData = Map(&destStride);
738 memset(destData, 0, GetHeight() * destStride);
739 Unmap();
740 }
741
HasAlpha()742 bool DMABufSurfaceRGBA::HasAlpha() {
743 return mGmbFormat ? mGmbFormat->mHasAlpha : true;
744 }
745
GetFormat()746 gfx::SurfaceFormat DMABufSurfaceRGBA::GetFormat() {
747 return HasAlpha() ? gfx::SurfaceFormat::B8G8R8A8
748 : gfx::SurfaceFormat::B8G8R8X8;
749 }
750
751 // GL uses swapped R and B components so report accordingly.
GetFormatGL()752 gfx::SurfaceFormat DMABufSurfaceRGBA::GetFormatGL() {
753 return HasAlpha() ? gfx::SurfaceFormat::R8G8B8A8
754 : gfx::SurfaceFormat::R8G8B8X8;
755 }
756
CreateDMABufSurface(int aWidth,int aHeight,int aDMABufSurfaceFlags)757 already_AddRefed<DMABufSurfaceRGBA> DMABufSurfaceRGBA::CreateDMABufSurface(
758 int aWidth, int aHeight, int aDMABufSurfaceFlags) {
759 RefPtr<DMABufSurfaceRGBA> surf = new DMABufSurfaceRGBA();
760 if (!surf->Create(aWidth, aHeight, aDMABufSurfaceFlags)) {
761 return nullptr;
762 }
763 return surf.forget();
764 }
765
CreateYUVSurface(const VADRMPRIMESurfaceDescriptor & aDesc)766 already_AddRefed<DMABufSurfaceYUV> DMABufSurfaceYUV::CreateYUVSurface(
767 const VADRMPRIMESurfaceDescriptor& aDesc) {
768 RefPtr<DMABufSurfaceYUV> surf = new DMABufSurfaceYUV();
769 LOGDMABUF(("DMABufSurfaceYUV::CreateYUVSurface() UID %d from desc\n",
770 surf->GetUID()));
771 if (!surf->UpdateYUVData(aDesc)) {
772 return nullptr;
773 }
774 return surf.forget();
775 }
776
CreateYUVSurface(int aWidth,int aHeight,void ** aPixelData,int * aLineSizes)777 already_AddRefed<DMABufSurfaceYUV> DMABufSurfaceYUV::CreateYUVSurface(
778 int aWidth, int aHeight, void** aPixelData, int* aLineSizes) {
779 RefPtr<DMABufSurfaceYUV> surf = new DMABufSurfaceYUV();
780 LOGDMABUF(("DMABufSurfaceYUV::CreateYUVSurface() UID %d %d x %d\n",
781 surf->GetUID(), aWidth, aHeight));
782 if (!surf->Create(aWidth, aHeight, aPixelData, aLineSizes)) {
783 return nullptr;
784 }
785 return surf.forget();
786 }
787
DMABufSurfaceYUV()788 DMABufSurfaceYUV::DMABufSurfaceYUV()
789 : DMABufSurface(SURFACE_NV12), mWidth(), mHeight(), mTexture() {
790 for (int i = 0; i < DMABUF_BUFFER_PLANES; i++) {
791 mEGLImage[i] = LOCAL_EGL_NO_IMAGE;
792 }
793 }
794
~DMABufSurfaceYUV()795 DMABufSurfaceYUV::~DMABufSurfaceYUV() { ReleaseSurface(); }
796
OpenFileDescriptorForPlane(int aPlane)797 bool DMABufSurfaceYUV::OpenFileDescriptorForPlane(int aPlane) {
798 // The fd is already opened, no need to reopen.
799 // This can happen when we import dmabuf surface from VA-API decoder,
800 // mGbmBufferObject is null and we don't close
801 // file descriptors for surface as they are our only reference to it.
802 if (mDmabufFds[aPlane] >= 0) {
803 return true;
804 }
805
806 MOZ_RELEASE_ASSERT(mGbmBufferObject[aPlane] != nullptr,
807 "DMABufSurfaceYUV::OpenFileDescriptorForPlane: Missing "
808 "mGbmBufferObject object!");
809
810 mDmabufFds[aPlane] = nsGbmLib::GetFd(mGbmBufferObject[aPlane]);
811 if (mDmabufFds[aPlane] < 0) {
812 CloseFileDescriptors();
813 return false;
814 }
815 return true;
816 }
817
CloseFileDescriptorForPlane(int aPlane,bool aForceClose=false)818 void DMABufSurfaceYUV::CloseFileDescriptorForPlane(int aPlane,
819 bool aForceClose = false) {
820 if ((aForceClose || mGbmBufferObject[aPlane]) && mDmabufFds[aPlane] >= 0) {
821 close(mDmabufFds[aPlane]);
822 mDmabufFds[aPlane] = -1;
823 }
824 }
825
UpdateYUVData(const VADRMPRIMESurfaceDescriptor & aDesc)826 bool DMABufSurfaceYUV::UpdateYUVData(const VADRMPRIMESurfaceDescriptor& aDesc) {
827 if (aDesc.num_layers > DMABUF_BUFFER_PLANES ||
828 aDesc.num_objects > DMABUF_BUFFER_PLANES) {
829 return false;
830 }
831
832 LOGDMABUF(("DMABufSurfaceYUV::UpdateYUVData() UID %d", mUID));
833 if (mDmabufFds[0] >= 0) {
834 LOGDMABUF((" Already created!"));
835 return false;
836 }
837 if (aDesc.fourcc == VA_FOURCC_NV12) {
838 mSurfaceType = SURFACE_NV12;
839 } else if (aDesc.fourcc == VA_FOURCC_YV12) {
840 mSurfaceType = SURFACE_YUV420;
841 } else {
842 LOGDMABUF(("UpdateYUVData(): Can't import surface data of 0x%x format",
843 aDesc.fourcc));
844 return false;
845 }
846
847 mBufferPlaneCount = aDesc.num_layers;
848 mBufferModifier = aDesc.objects[0].drm_format_modifier;
849
850 for (unsigned int i = 0; i < aDesc.num_layers; i++) {
851 // Intel exports VA-API surfaces in one object,planes have the same FD.
852 // AMD exports surfaces in two objects with different FDs.
853 bool dupFD = (aDesc.layers[i].object_index[0] != i);
854 int fd = aDesc.objects[aDesc.layers[i].object_index[0]].fd;
855 mDmabufFds[i] = dupFD ? dup(fd) : fd;
856
857 mDrmFormats[i] = aDesc.layers[i].drm_format;
858 mOffsets[i] = aDesc.layers[i].offset[0];
859 mStrides[i] = aDesc.layers[i].pitch[0];
860 mWidth[i] = aDesc.width >> i;
861 mHeight[i] = aDesc.height >> i;
862
863 LOGDMABUF((" plane %d size %d x %d format %x", i, mWidth[i], mHeight[i],
864 mDrmFormats[i]));
865 }
866
867 return true;
868 }
869
CreateYUVPlane(int aPlane,int aWidth,int aHeight,int aDrmFormat)870 bool DMABufSurfaceYUV::CreateYUVPlane(int aPlane, int aWidth, int aHeight,
871 int aDrmFormat) {
872 LOGDMABUF(("DMABufSurfaceYUV::CreateYUVPlane() UID %d size %d x %d", mUID,
873 aWidth, aHeight));
874
875 mWidth[aPlane] = aWidth;
876 mHeight[aPlane] = aHeight;
877 mDrmFormats[aPlane] = aDrmFormat;
878
879 mGbmBufferObject[aPlane] =
880 nsGbmLib::Create(GetDMABufDevice()->GetGbmDevice(), aWidth, aHeight,
881 aDrmFormat, GBM_BO_USE_LINEAR);
882 if (!mGbmBufferObject[aPlane]) {
883 LOGDMABUF((" Failed to create GbmBufferObject: %s", strerror(errno)));
884 return false;
885 }
886
887 mStrides[aPlane] = nsGbmLib::GetStride(mGbmBufferObject[aPlane]);
888 mDmabufFds[aPlane] = -1;
889
890 return true;
891 }
892
UpdateYUVPlane(int aPlane,void * aPixelData,int aLineSize)893 void DMABufSurfaceYUV::UpdateYUVPlane(int aPlane, void* aPixelData,
894 int aLineSize) {
895 LOGDMABUF(
896 ("DMABufSurfaceYUV::UpdateYUVPlane() UID %d plane %d", mUID, aPlane));
897 if (aLineSize == mWidth[aPlane] &&
898 (int)mMappedRegionStride[aPlane] == mWidth[aPlane]) {
899 memcpy(mMappedRegion[aPlane], aPixelData, aLineSize * mHeight[aPlane]);
900 } else {
901 char* src = (char*)aPixelData;
902 char* dest = (char*)mMappedRegion[aPlane];
903 for (int i = 0; i < mHeight[aPlane]; i++) {
904 memcpy(dest, src, mWidth[aPlane]);
905 src += aLineSize;
906 dest += mMappedRegionStride[aPlane];
907 }
908 }
909 }
910
UpdateYUVData(void ** aPixelData,int * aLineSizes)911 bool DMABufSurfaceYUV::UpdateYUVData(void** aPixelData, int* aLineSizes) {
912 LOGDMABUF(("DMABufSurfaceYUV::UpdateYUVData() UID %d", mUID));
913 if (mSurfaceType != SURFACE_YUV420) {
914 LOGDMABUF((" UpdateYUVData can upload YUV420 surface type only!"));
915 return false;
916 }
917
918 if (mBufferPlaneCount != 3) {
919 LOGDMABUF((" DMABufSurfaceYUV planes does not match!"));
920 return false;
921 }
922
923 auto unmapBuffers = MakeScopeExit([&] {
924 Unmap(0);
925 Unmap(1);
926 Unmap(2);
927 });
928
929 // Map planes
930 for (int i = 0; i < mBufferPlaneCount; i++) {
931 MapInternal(0, 0, mWidth[i], mHeight[i], nullptr, GBM_BO_TRANSFER_WRITE, i);
932 if (!mMappedRegion[i]) {
933 LOGDMABUF((" DMABufSurfaceYUV plane can't be mapped!"));
934 return false;
935 }
936 if ((int)mMappedRegionStride[i] < mWidth[i]) {
937 LOGDMABUF((" DMABufSurfaceYUV plane size stride does not match!"));
938 return false;
939 }
940 }
941
942 // Copy planes
943 for (int i = 0; i < mBufferPlaneCount; i++) {
944 UpdateYUVPlane(i, aPixelData[i], aLineSizes[i]);
945 }
946
947 return true;
948 }
949
Create(int aWidth,int aHeight,void ** aPixelData,int * aLineSizes)950 bool DMABufSurfaceYUV::Create(int aWidth, int aHeight, void** aPixelData,
951 int* aLineSizes) {
952 LOGDMABUF(("DMABufSurfaceYUV::Create() UID %d size %d x %d", mUID, aWidth,
953 aHeight));
954
955 mSurfaceType = SURFACE_YUV420;
956 mBufferPlaneCount = 3;
957
958 if (!CreateYUVPlane(0, aWidth, aHeight, GBM_FORMAT_R8)) {
959 return false;
960 }
961 if (!CreateYUVPlane(1, aWidth >> 1, aHeight >> 1, GBM_FORMAT_R8)) {
962 return false;
963 }
964 if (!CreateYUVPlane(2, aWidth >> 1, aHeight >> 1, GBM_FORMAT_R8)) {
965 return false;
966 }
967
968 return aPixelData != nullptr && aLineSizes != nullptr
969 ? UpdateYUVData(aPixelData, aLineSizes)
970 : true;
971 }
972
Create(const SurfaceDescriptor & aDesc)973 bool DMABufSurfaceYUV::Create(const SurfaceDescriptor& aDesc) {
974 return ImportSurfaceDescriptor(aDesc);
975 }
976
ImportSurfaceDescriptor(const SurfaceDescriptorDMABuf & aDesc)977 bool DMABufSurfaceYUV::ImportSurfaceDescriptor(
978 const SurfaceDescriptorDMABuf& aDesc) {
979 mBufferPlaneCount = aDesc.fds().Length();
980 mSurfaceType = (mBufferPlaneCount == 2) ? SURFACE_NV12 : SURFACE_YUV420;
981 mBufferModifier = aDesc.modifier();
982 mColorSpace = aDesc.yUVColorSpace();
983 mUID = aDesc.uid();
984
985 LOGDMABUF(("DMABufSurfaceYUV::ImportSurfaceDescriptor() UID %d", mUID));
986
987 MOZ_RELEASE_ASSERT(mBufferPlaneCount <= DMABUF_BUFFER_PLANES);
988 for (int i = 0; i < mBufferPlaneCount; i++) {
989 mDmabufFds[i] = aDesc.fds()[i].ClonePlatformHandle().release();
990 if (mDmabufFds[i] < 0) {
991 LOGDMABUF((" failed to get DMABuf plane file descriptor: %s",
992 strerror(errno)));
993 return false;
994 }
995 mWidth[i] = aDesc.width()[i];
996 mHeight[i] = aDesc.height()[i];
997 mDrmFormats[i] = aDesc.format()[i];
998 mStrides[i] = aDesc.strides()[i];
999 mOffsets[i] = aDesc.offsets()[i];
1000 LOGDMABUF((" plane %d fd %d size %d x %d format %x", i, mDmabufFds[i],
1001 mWidth[i], mHeight[i], mDrmFormats[i]));
1002 }
1003
1004 if (aDesc.fence().Length() > 0) {
1005 mSyncFd = aDesc.fence()[0].ClonePlatformHandle().release();
1006 if (mSyncFd < 0) {
1007 LOGDMABUF(
1008 (" failed to get GL fence file descriptor: %s", strerror(errno)));
1009 return false;
1010 }
1011 }
1012
1013 if (aDesc.refCount().Length() > 0) {
1014 GlobalRefCountImport(aDesc.refCount()[0].ClonePlatformHandle().release());
1015 }
1016
1017 return true;
1018 }
1019
Serialize(mozilla::layers::SurfaceDescriptor & aOutDescriptor)1020 bool DMABufSurfaceYUV::Serialize(
1021 mozilla::layers::SurfaceDescriptor& aOutDescriptor) {
1022 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> width;
1023 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> height;
1024 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> format;
1025 AutoTArray<ipc::FileDescriptor, DMABUF_BUFFER_PLANES> fds;
1026 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> strides;
1027 AutoTArray<uint32_t, DMABUF_BUFFER_PLANES> offsets;
1028 AutoTArray<ipc::FileDescriptor, 1> fenceFDs;
1029 AutoTArray<ipc::FileDescriptor, 1> refCountFDs;
1030
1031 LOGDMABUF(("DMABufSurfaceYUV::Serialize() UID %d", mUID));
1032
1033 MutexAutoLock lockFD(mSurfaceLock);
1034 if (!OpenFileDescriptors()) {
1035 return false;
1036 }
1037
1038 for (int i = 0; i < mBufferPlaneCount; i++) {
1039 width.AppendElement(mWidth[i]);
1040 height.AppendElement(mHeight[i]);
1041 format.AppendElement(mDrmFormats[i]);
1042 fds.AppendElement(ipc::FileDescriptor(mDmabufFds[i]));
1043 strides.AppendElement(mStrides[i]);
1044 offsets.AppendElement(mOffsets[i]);
1045 }
1046
1047 CloseFileDescriptors();
1048
1049 if (mSync) {
1050 fenceFDs.AppendElement(ipc::FileDescriptor(mSyncFd));
1051 }
1052
1053 if (mGlobalRefCountFd) {
1054 refCountFDs.AppendElement(ipc::FileDescriptor(mGlobalRefCountFd));
1055 }
1056
1057 aOutDescriptor = SurfaceDescriptorDMABuf(
1058 mSurfaceType, mBufferModifier, 0, fds, width, height, format, strides,
1059 offsets, GetYUVColorSpace(), fenceFDs, mUID, refCountFDs);
1060 return true;
1061 }
1062
CreateTexture(GLContext * aGLContext,int aPlane)1063 bool DMABufSurfaceYUV::CreateTexture(GLContext* aGLContext, int aPlane) {
1064 LOGDMABUF(
1065 ("DMABufSurfaceYUV::CreateTexture() UID %d plane %d", mUID, aPlane));
1066 MOZ_ASSERT(!mEGLImage[aPlane] && !mTexture[aPlane],
1067 "EGLImage/Texture is already created!");
1068
1069 if (!aGLContext) return false;
1070 const auto& gle = gl::GLContextEGL::Cast(aGLContext);
1071 const auto& egl = gle->mEgl;
1072
1073 MutexAutoLock lockFD(mSurfaceLock);
1074 if (!OpenFileDescriptorForPlane(aPlane)) {
1075 return false;
1076 }
1077
1078 nsTArray<EGLint> attribs;
1079 attribs.AppendElement(LOCAL_EGL_WIDTH);
1080 attribs.AppendElement(mWidth[aPlane]);
1081 attribs.AppendElement(LOCAL_EGL_HEIGHT);
1082 attribs.AppendElement(mHeight[aPlane]);
1083 attribs.AppendElement(LOCAL_EGL_LINUX_DRM_FOURCC_EXT);
1084 attribs.AppendElement(mDrmFormats[aPlane]);
1085 #define ADD_PLANE_ATTRIBS_NV12(plane_idx) \
1086 attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_FD_EXT); \
1087 attribs.AppendElement(mDmabufFds[aPlane]); \
1088 attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_OFFSET_EXT); \
1089 attribs.AppendElement((int)mOffsets[aPlane]); \
1090 attribs.AppendElement(LOCAL_EGL_DMA_BUF_PLANE##plane_idx##_PITCH_EXT); \
1091 attribs.AppendElement((int)mStrides[aPlane]);
1092 ADD_PLANE_ATTRIBS_NV12(0);
1093 #undef ADD_PLANE_ATTRIBS_NV12
1094 attribs.AppendElement(LOCAL_EGL_NONE);
1095
1096 mEGLImage[aPlane] =
1097 egl->fCreateImage(LOCAL_EGL_NO_CONTEXT, LOCAL_EGL_LINUX_DMA_BUF_EXT,
1098 nullptr, attribs.Elements());
1099
1100 CloseFileDescriptorForPlane(aPlane);
1101
1102 if (mEGLImage[aPlane] == LOCAL_EGL_NO_IMAGE) {
1103 LOGDMABUF((" EGLImageKHR creation failed"));
1104 return false;
1105 }
1106
1107 aGLContext->MakeCurrent();
1108 aGLContext->fGenTextures(1, &mTexture[aPlane]);
1109 const ScopedBindTexture savedTex(aGLContext, mTexture[aPlane]);
1110 aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S,
1111 LOCAL_GL_CLAMP_TO_EDGE);
1112 aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T,
1113 LOCAL_GL_CLAMP_TO_EDGE);
1114 aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER,
1115 LOCAL_GL_LINEAR);
1116 aGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER,
1117 LOCAL_GL_LINEAR);
1118 aGLContext->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D, mEGLImage[aPlane]);
1119 mGL = aGLContext;
1120 return true;
1121 }
1122
ReleaseTextures()1123 void DMABufSurfaceYUV::ReleaseTextures() {
1124 LOGDMABUF(("DMABufSurfaceYUV::ReleaseTextures() UID %d", mUID));
1125
1126 FenceDelete();
1127
1128 bool textureActive = false;
1129 for (int i = 0; i < mBufferPlaneCount; i++) {
1130 if (mTexture[i]) {
1131 textureActive = true;
1132 break;
1133 }
1134 }
1135
1136 if (!mGL) return;
1137 const auto& gle = gl::GLContextEGL::Cast(mGL);
1138 const auto& egl = gle->mEgl;
1139
1140 if (textureActive && mGL->MakeCurrent()) {
1141 mGL->fDeleteTextures(DMABUF_BUFFER_PLANES, mTexture);
1142 for (int i = 0; i < DMABUF_BUFFER_PLANES; i++) {
1143 mTexture[i] = 0;
1144 }
1145 mGL = nullptr;
1146 }
1147
1148 for (int i = 0; i < mBufferPlaneCount; i++) {
1149 if (mEGLImage[i]) {
1150 egl->fDestroyImage(mEGLImage[i]);
1151 mEGLImage[i] = nullptr;
1152 }
1153 }
1154 }
1155
GetFormat()1156 gfx::SurfaceFormat DMABufSurfaceYUV::GetFormat() {
1157 switch (mSurfaceType) {
1158 case SURFACE_NV12:
1159 return gfx::SurfaceFormat::NV12;
1160 case SURFACE_YUV420:
1161 return gfx::SurfaceFormat::YUV;
1162 default:
1163 NS_WARNING("DMABufSurfaceYUV::GetFormat(): Wrong surface format!");
1164 return gfx::SurfaceFormat::UNKNOWN;
1165 }
1166 }
1167
1168 // GL uses swapped R and B components so report accordingly.
GetFormatGL()1169 gfx::SurfaceFormat DMABufSurfaceYUV::GetFormatGL() { return GetFormat(); }
1170
GetTextureCount()1171 uint32_t DMABufSurfaceYUV::GetTextureCount() {
1172 switch (mSurfaceType) {
1173 case SURFACE_NV12:
1174 return 2;
1175 case SURFACE_YUV420:
1176 return 3;
1177 default:
1178 NS_WARNING("DMABufSurfaceYUV::GetTextureCount(): Wrong surface format!");
1179 return 1;
1180 }
1181 }
1182
ReleaseSurface()1183 void DMABufSurfaceYUV::ReleaseSurface() {
1184 LOGDMABUF(("DMABufSurfaceYUV::ReleaseSurface() UID %d", mUID));
1185 ReleaseTextures();
1186 ReleaseDMABuf();
1187 }
1188