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