1 /*
2  *  Copyright (C) 2017-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "EGLImage.h"
10 
11 #include "ServiceBroker.h"
12 #include "utils/EGLUtils.h"
13 #include "utils/log.h"
14 
15 #include <map>
16 #include <sstream>
17 
18 namespace
19 {
20   const EGLint eglDmabufPlaneFdAttr[CEGLImage::MAX_NUM_PLANES] =
21   {
22     EGL_DMA_BUF_PLANE0_FD_EXT,
23     EGL_DMA_BUF_PLANE1_FD_EXT,
24     EGL_DMA_BUF_PLANE2_FD_EXT,
25   };
26 
27   const EGLint eglDmabufPlaneOffsetAttr[CEGLImage::MAX_NUM_PLANES] =
28   {
29     EGL_DMA_BUF_PLANE0_OFFSET_EXT,
30     EGL_DMA_BUF_PLANE1_OFFSET_EXT,
31     EGL_DMA_BUF_PLANE2_OFFSET_EXT,
32   };
33 
34   const EGLint eglDmabufPlanePitchAttr[CEGLImage::MAX_NUM_PLANES] =
35   {
36     EGL_DMA_BUF_PLANE0_PITCH_EXT,
37     EGL_DMA_BUF_PLANE1_PITCH_EXT,
38     EGL_DMA_BUF_PLANE2_PITCH_EXT,
39   };
40 
41 #if defined(EGL_EXT_image_dma_buf_import_modifiers)
42   const EGLint eglDmabufPlaneModifierLoAttr[CEGLImage::MAX_NUM_PLANES] =
43   {
44     EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT,
45     EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT,
46     EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT,
47   };
48 
49   const EGLint eglDmabufPlaneModifierHiAttr[CEGLImage::MAX_NUM_PLANES] =
50   {
51     EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT,
52     EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT,
53     EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT,
54   };
55 #endif
56 
57 #define X(VAL) std::make_pair(VAL, #VAL)
58 std::map<EGLint, const char*> eglAttributes =
59 {
60   X(EGL_WIDTH),
61   X(EGL_HEIGHT),
62 
63   // please keep attributes in accordance to:
64   // https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import.txt
65   X(EGL_LINUX_DRM_FOURCC_EXT),
66   X(EGL_DMA_BUF_PLANE0_FD_EXT),
67   X(EGL_DMA_BUF_PLANE0_OFFSET_EXT),
68   X(EGL_DMA_BUF_PLANE0_PITCH_EXT),
69   X(EGL_DMA_BUF_PLANE1_FD_EXT),
70   X(EGL_DMA_BUF_PLANE1_OFFSET_EXT),
71   X(EGL_DMA_BUF_PLANE1_PITCH_EXT),
72   X(EGL_DMA_BUF_PLANE2_FD_EXT),
73   X(EGL_DMA_BUF_PLANE2_OFFSET_EXT),
74   X(EGL_DMA_BUF_PLANE2_PITCH_EXT),
75   X(EGL_YUV_COLOR_SPACE_HINT_EXT),
76   X(EGL_SAMPLE_RANGE_HINT_EXT),
77   X(EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT),
78   X(EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT),
79   X(EGL_ITU_REC601_EXT),
80   X(EGL_ITU_REC709_EXT),
81   X(EGL_ITU_REC2020_EXT),
82   X(EGL_YUV_FULL_RANGE_EXT),
83   X(EGL_YUV_NARROW_RANGE_EXT),
84   X(EGL_YUV_CHROMA_SITING_0_EXT),
85   X(EGL_YUV_CHROMA_SITING_0_5_EXT),
86 
87 #if defined(EGL_EXT_image_dma_buf_import_modifiers)
88   // please keep attributes in accordance to:
89   // https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import_modifiers.txt
90   X(EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT),
91   X(EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT),
92   X(EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT),
93   X(EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT),
94   X(EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT),
95   X(EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT),
96   X(EGL_DMA_BUF_PLANE3_FD_EXT),
97   X(EGL_DMA_BUF_PLANE3_OFFSET_EXT),
98   X(EGL_DMA_BUF_PLANE3_PITCH_EXT),
99   X(EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT),
100   X(EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT),
101 #endif
102 };
103 
FourCCToString(uint32_t fourcc)104 std::string FourCCToString(uint32_t fourcc)
105 {
106   std::stringstream ss;
107   ss << static_cast<char>((fourcc & 0x000000FF));
108   ss << static_cast<char>((fourcc & 0x0000FF00) >> 8);
109   ss << static_cast<char>((fourcc & 0x00FF0000) >> 16);
110   ss << static_cast<char>((fourcc & 0xFF000000) >> 24);
111 
112   return ss.str();
113 }
114 
115 } // namespace
116 
CEGLImage(EGLDisplay display)117 CEGLImage::CEGLImage(EGLDisplay display) :
118   m_display(display)
119 {
120   m_eglCreateImageKHR = CEGLUtils::GetRequiredProcAddress<PFNEGLCREATEIMAGEKHRPROC>("eglCreateImageKHR");
121   m_eglDestroyImageKHR = CEGLUtils::GetRequiredProcAddress<PFNEGLDESTROYIMAGEKHRPROC>("eglDestroyImageKHR");
122   m_glEGLImageTargetTexture2DOES = CEGLUtils::GetRequiredProcAddress<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>("glEGLImageTargetTexture2DOES");
123 }
124 
CreateImage(EglAttrs imageAttrs)125 bool CEGLImage::CreateImage(EglAttrs imageAttrs)
126 {
127   CEGLAttributes<22> attribs;
128   attribs.Add({{EGL_WIDTH, imageAttrs.width},
129                {EGL_HEIGHT, imageAttrs.height},
130                {EGL_LINUX_DRM_FOURCC_EXT, static_cast<EGLint>(imageAttrs.format)}});
131 
132   if (imageAttrs.colorSpace != 0 && imageAttrs.colorRange != 0)
133   {
134     attribs.Add({{EGL_YUV_COLOR_SPACE_HINT_EXT, imageAttrs.colorSpace},
135                  {EGL_SAMPLE_RANGE_HINT_EXT, imageAttrs.colorRange},
136                  {EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT, EGL_YUV_CHROMA_SITING_0_EXT},
137                  {EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT, EGL_YUV_CHROMA_SITING_0_EXT}});
138   }
139 
140   for (int i = 0; i < MAX_NUM_PLANES; i++)
141   {
142     if (imageAttrs.planes[i].fd != 0)
143     {
144       attribs.Add({{eglDmabufPlaneFdAttr[i], imageAttrs.planes[i].fd},
145                    {eglDmabufPlaneOffsetAttr[i], imageAttrs.planes[i].offset},
146                    {eglDmabufPlanePitchAttr[i], imageAttrs.planes[i].pitch}});
147 
148 #if defined(EGL_EXT_image_dma_buf_import_modifiers)
149       if (imageAttrs.planes[i].modifier != DRM_FORMAT_MOD_INVALID && imageAttrs.planes[i].modifier != DRM_FORMAT_MOD_LINEAR)
150         attribs.Add({{eglDmabufPlaneModifierLoAttr[i], static_cast<EGLint>(imageAttrs.planes[i].modifier & 0xFFFFFFFF)},
151                      {eglDmabufPlaneModifierHiAttr[i], static_cast<EGLint>(imageAttrs.planes[i].modifier >> 32)}});
152 #endif
153     }
154   }
155 
156   m_image = m_eglCreateImageKHR(m_display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs.Get());
157 
158   if (!m_image || CServiceBroker::GetLogging().CanLogComponent(LOGVIDEO))
159   {
160     const EGLint* attrs = attribs.Get();
161 
162     std::string eglString;
163 
164     for (int i = 0; i < (attribs.Size()); i += 2)
165     {
166       std::string keyStr;
167       std::string valueStr;
168 
169       auto eglAttrKey = eglAttributes.find(attrs[i]);
170       if (eglAttrKey != eglAttributes.end())
171       {
172         keyStr = eglAttrKey->second;
173       }
174       else
175       {
176         keyStr = std::to_string(attrs[i]);
177       }
178 
179       auto eglAttrValue = eglAttributes.find(attrs[i + 1]);
180       if (eglAttrValue != eglAttributes.end())
181       {
182         valueStr = eglAttrValue->second;
183       }
184       else
185       {
186         if (eglAttrKey != eglAttributes.end() && eglAttrKey->first == EGL_LINUX_DRM_FOURCC_EXT)
187           valueStr = FourCCToString(attrs[i + 1]);
188         else
189           valueStr = std::to_string(attrs[i + 1]);
190       }
191 
192       eglString.append(StringUtils::Format("%s: %s\n", keyStr, valueStr));
193     }
194 
195     CLog::Log(LOGDEBUG, "CEGLImage::{} - attributes:\n{}", __FUNCTION__, eglString);
196   }
197 
198   if (!m_image)
199   {
200     CLog::Log(LOGERROR, "CEGLImage::{} - failed to import buffer into EGL image: {:#4x}",
201               __FUNCTION__, eglGetError());
202     return false;
203   }
204 
205   return true;
206 }
207 
UploadImage(GLenum textureTarget)208 void CEGLImage::UploadImage(GLenum textureTarget)
209 {
210   m_glEGLImageTargetTexture2DOES(textureTarget, m_image);
211 }
212 
DestroyImage()213 void CEGLImage::DestroyImage()
214 {
215   m_eglDestroyImageKHR(m_display, m_image);
216 }
217 
218 #if defined(EGL_EXT_image_dma_buf_import_modifiers)
SupportsFormat(uint32_t format)219 bool CEGLImage::SupportsFormat(uint32_t format)
220 {
221   auto eglQueryDmaBufFormatsEXT =
222       CEGLUtils::GetRequiredProcAddress<PFNEGLQUERYDMABUFFORMATSEXTPROC>(
223           "eglQueryDmaBufFormatsEXT");
224 
225   EGLint numFormats;
226   if (eglQueryDmaBufFormatsEXT(m_display, 0, nullptr, &numFormats) != EGL_TRUE)
227   {
228     CLog::Log(LOGERROR,
229               "CEGLImage::{} - failed to query the max number of EGL dma-buf formats: {:#4x}",
230               __FUNCTION__, eglGetError());
231     return false;
232   }
233 
234   std::vector<EGLint> formats(numFormats);
235   if (eglQueryDmaBufFormatsEXT(m_display, numFormats, formats.data(), &numFormats) != EGL_TRUE)
236   {
237     CLog::Log(LOGERROR, "CEGLImage::{} - failed to query EGL dma-buf formats: {:#4x}", __FUNCTION__,
238               eglGetError());
239     return false;
240   }
241 
242   auto foundFormat = std::find(formats.begin(), formats.end(), format);
243   if (foundFormat != formats.end())
244     return true;
245 
246   CLog::Log(LOGDEBUG, "CEGLImage::{} - format not supported: {}", __FUNCTION__,
247             FourCCToString(format));
248 
249   CLog::Log(LOGERROR, "CEGLImage::{} - supported formats:", __FUNCTION__);
250   for (const auto& supportedFormat : formats)
251     CLog::Log(LOGERROR, "CEGLImage::{} -   {}", __FUNCTION__, FourCCToString(supportedFormat));
252 
253   return false;
254 }
255 
SupportsFormatAndModifier(uint32_t format,uint64_t modifier)256 bool CEGLImage::SupportsFormatAndModifier(uint32_t format, uint64_t modifier)
257 {
258   if (!SupportsFormat(format))
259     return false;
260 
261   if (modifier == DRM_FORMAT_MOD_LINEAR)
262     return true;
263 
264   /*
265    * Some broadcom modifiers have parameters encoded which need to be
266    * masked out before comparing with reported modifiers.
267    */
268   if (modifier >> 56 == DRM_FORMAT_MOD_VENDOR_BROADCOM)
269     modifier = fourcc_mod_broadcom_mod(modifier);
270 
271   auto eglQueryDmaBufModifiersEXT =
272       CEGLUtils::GetRequiredProcAddress<PFNEGLQUERYDMABUFMODIFIERSEXTPROC>(
273           "eglQueryDmaBufModifiersEXT");
274 
275   EGLint numFormats;
276   if (eglQueryDmaBufModifiersEXT(m_display, format, 0, nullptr, nullptr, &numFormats) != EGL_TRUE)
277   {
278     CLog::Log(LOGERROR,
279               "CEGLImage::{} - failed to query the max number of EGL dma-buf format modifiers for "
280               "format: {} - {:#4x}",
281               __FUNCTION__, FourCCToString(format), eglGetError());
282     return false;
283   }
284 
285   std::vector<EGLuint64KHR> modifiers(numFormats);
286 
287   if (eglQueryDmaBufModifiersEXT(m_display, format, numFormats, modifiers.data(), nullptr,
288                                  &numFormats) != EGL_TRUE)
289   {
290     CLog::Log(
291         LOGERROR,
292         "CEGLImage::{} - failed to query EGL dma-buf format modifiers for format: {} - {:#4x}",
293         __FUNCTION__, FourCCToString(format), eglGetError());
294     return false;
295   }
296 
297   auto foundModifier = std::find(modifiers.begin(), modifiers.end(), modifier);
298   if (foundModifier != modifiers.end())
299     return true;
300 
301   CLog::Log(LOGDEBUG, "CEGLImage::{} - modifier ({:#x}) not supported for format ({})",
302             __FUNCTION__, modifier, FourCCToString(format));
303 
304   CLog::Log(LOGERROR, "CEGLImage::{} - supported modifiers:", __FUNCTION__);
305   for (const auto& supportedModifier : modifiers)
306     CLog::Log(LOGERROR, "CEGLImage::{} -   {}", __FUNCTION__, supportedModifier);
307 
308   return false;
309 }
310 #endif
311