1 //
2 // Copyright 2021 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // DisplayVkLinux.cpp:
7 // Implements the class methods for DisplayVkLinux.
8 //
9
10 #include "libANGLE/renderer/vulkan/linux/DisplayVkLinux.h"
11
12 #include "common/linux/dma_buf_utils.h"
13 #include "libANGLE/renderer/vulkan/RendererVk.h"
14 #include "libANGLE/renderer/vulkan/linux/DeviceVkLinux.h"
15 #include "libANGLE/renderer/vulkan/linux/DmaBufImageSiblingVkLinux.h"
16
17 namespace rx
18 {
19
DisplayVkLinux(const egl::DisplayState & state)20 DisplayVkLinux::DisplayVkLinux(const egl::DisplayState &state)
21 : DisplayVk(state), mDrmFormatsInitialized(false)
22 {}
23
createDevice()24 DeviceImpl *DisplayVkLinux::createDevice()
25 {
26 return new DeviceVkLinux(this);
27 }
28
createExternalImageSibling(const gl::Context * context,EGLenum target,EGLClientBuffer buffer,const egl::AttributeMap & attribs)29 ExternalImageSiblingImpl *DisplayVkLinux::createExternalImageSibling(
30 const gl::Context *context,
31 EGLenum target,
32 EGLClientBuffer buffer,
33 const egl::AttributeMap &attribs)
34 {
35 switch (target)
36 {
37 case EGL_LINUX_DMA_BUF_EXT:
38 ASSERT(context == nullptr);
39 ASSERT(buffer == nullptr);
40 return new DmaBufImageSiblingVkLinux(attribs);
41
42 default:
43 return DisplayVk::createExternalImageSibling(context, target, buffer, attribs);
44 }
45 }
46
47 // Returns the list of DRM modifiers that a VkFormat supports
GetDrmModifiers(const DisplayVk * displayVk,VkFormat vkFormat)48 std::vector<VkDrmFormatModifierPropertiesEXT> DisplayVkLinux::GetDrmModifiers(
49 const DisplayVk *displayVk,
50 VkFormat vkFormat)
51 {
52 RendererVk *renderer = displayVk->getRenderer();
53
54 // Query list of drm format modifiers compatible with VkFormat.
55 VkDrmFormatModifierPropertiesListEXT formatModifierPropertiesList = {};
56 formatModifierPropertiesList.sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT;
57 formatModifierPropertiesList.drmFormatModifierCount = 0;
58
59 VkFormatProperties2 formatProperties = {};
60 formatProperties.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2;
61 formatProperties.pNext = &formatModifierPropertiesList;
62
63 vkGetPhysicalDeviceFormatProperties2(renderer->getPhysicalDevice(), vkFormat,
64 &formatProperties);
65
66 std::vector<VkDrmFormatModifierPropertiesEXT> formatModifierProperties(
67 formatModifierPropertiesList.drmFormatModifierCount);
68 formatModifierPropertiesList.pDrmFormatModifierProperties = formatModifierProperties.data();
69
70 vkGetPhysicalDeviceFormatProperties2(renderer->getPhysicalDevice(), vkFormat,
71 &formatProperties);
72
73 return formatModifierProperties;
74 }
75
76 // Returns true if that VkFormat has at least on format modifier in its properties
SupportsDrmModifiers(VkPhysicalDevice device,VkFormat vkFormat)77 bool DisplayVkLinux::SupportsDrmModifiers(VkPhysicalDevice device, VkFormat vkFormat)
78 {
79 // Query list of drm format modifiers compatible with VkFormat.
80 VkDrmFormatModifierPropertiesListEXT formatModifierPropertiesList = {};
81 formatModifierPropertiesList.sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT;
82 formatModifierPropertiesList.drmFormatModifierCount = 0;
83
84 VkFormatProperties2 formatProperties = {};
85 formatProperties.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2;
86 formatProperties.pNext = &formatModifierPropertiesList;
87
88 vkGetPhysicalDeviceFormatProperties2(device, vkFormat, &formatProperties);
89
90 // If there is at least one DRM format modifier, it is supported
91 return formatModifierPropertiesList.drmFormatModifierCount > 0;
92 }
93
94 // Returns a list of VkFormats supporting at least one DRM format modifier
GetVkFormatsWithDrmModifiers(const RendererVk * rendererVk)95 std::vector<VkFormat> DisplayVkLinux::GetVkFormatsWithDrmModifiers(const RendererVk *rendererVk)
96 {
97 std::vector<VkFormat> vkFormats;
98 for (size_t formatIndex = 1; formatIndex < angle::kNumANGLEFormats; ++formatIndex)
99 {
100 const vk::Format &format = rendererVk->getFormat(angle::FormatID(formatIndex));
101 VkFormat vkFormat = format.getActualImageVkFormat(rx::vk::ImageAccess::Renderable);
102
103 if (vkFormat != VK_FORMAT_UNDEFINED &&
104 SupportsDrmModifiers(rendererVk->getPhysicalDevice(), vkFormat))
105 {
106 vkFormats.push_back(vkFormat);
107 }
108 }
109
110 return vkFormats;
111 }
112
113 // Returns a list of supported DRM formats
GetDrmFormats(const RendererVk * rendererVk)114 std::vector<EGLint> DisplayVkLinux::GetDrmFormats(const RendererVk *rendererVk)
115 {
116 std::unordered_set<EGLint> drmFormatsSet;
117 for (VkFormat vkFormat : GetVkFormatsWithDrmModifiers(rendererVk))
118 {
119 std::vector<EGLint> drmFormats = angle::VkFormatToDrmFourCCFormat(vkFormat);
120 for (EGLint drmFormat : drmFormats)
121 {
122 drmFormatsSet.insert(drmFormat);
123 }
124 }
125
126 std::vector<EGLint> drmFormats;
127 std::copy(std::begin(drmFormatsSet), std::end(drmFormatsSet), std::back_inserter(drmFormats));
128
129 return drmFormats;
130 }
131
supportsDmaBufFormat(EGLint format) const132 bool DisplayVkLinux::supportsDmaBufFormat(EGLint format) const
133 {
134 return std::find(std::begin(mDrmFormats), std::end(mDrmFormats), format) !=
135 std::end(mDrmFormats);
136 }
137
queryDmaBufFormats(EGLint maxFormats,EGLint * formats,EGLint * numFormats)138 egl::Error DisplayVkLinux::queryDmaBufFormats(EGLint maxFormats,
139 EGLint *formats,
140 EGLint *numFormats)
141 {
142 if (!mDrmFormatsInitialized)
143 {
144 mDrmFormats = GetDrmFormats(getRenderer());
145 mDrmFormatsInitialized = true;
146 }
147
148 EGLint formatsSize = static_cast<EGLint>(mDrmFormats.size());
149 *numFormats = formatsSize;
150 if (maxFormats > 0)
151 {
152 // Do not copy data beyond the limits of the vector
153 maxFormats = std::min(maxFormats, formatsSize);
154 std::memcpy(formats, mDrmFormats.data(), maxFormats * sizeof(EGLint));
155 }
156
157 return egl::NoError();
158 }
159
160 // Queries DRM format modifiers associated to `drmFormat`.
161 // When `maxModifiers` is zero, it will only return the number of modifiers associated to
162 // `drmFormat` using the out parameter `numModifiers`. When `maxModifiers` is greater than zero, it
163 // will put that number of DRM format modifiers into the out parameter `modifiers`.
queryDmaBufModifiers(EGLint drmFormat,EGLint maxModifiers,EGLuint64KHR * modifiers,EGLBoolean * externalOnly,EGLint * numModifiers)164 egl::Error DisplayVkLinux::queryDmaBufModifiers(EGLint drmFormat,
165 EGLint maxModifiers,
166 EGLuint64KHR *modifiers,
167 EGLBoolean *externalOnly,
168 EGLint *numModifiers)
169 {
170 // A DRM format may correspond to multiple Vulkan formats
171 std::vector<VkFormat> vkFormats = angle::DrmFourCCFormatToVkFormats(drmFormat);
172
173 std::vector<EGLuint64KHR> drmModifiers;
174 // Collect DRM format modifiers common to all those Vulkan formats
175 for (size_t i = 0; i < vkFormats.size(); ++i)
176 {
177 VkFormat vkFmt = vkFormats[i];
178
179 std::vector<VkDrmFormatModifierPropertiesEXT> vkDrmMods = GetDrmModifiers(this, vkFmt);
180
181 std::vector<EGLuint64KHR> drmMods(vkDrmMods.size());
182 std::transform(std::begin(vkDrmMods), std::end(vkDrmMods), std::begin(drmMods),
183 [](VkDrmFormatModifierPropertiesEXT props) {
184 return static_cast<EGLuint64KHR>(props.drmFormatModifier);
185 });
186 std::sort(std::begin(drmMods), std::end(drmMods));
187
188 if (i == 0)
189 {
190 // Just take the modifiers for the first format
191 drmModifiers = drmMods;
192 }
193 else
194 {
195 // Intersect the modifiers of all the other associated Vulkan formats
196 std::vector<EGLuint64KHR> prevMods = drmModifiers;
197 drmModifiers.clear();
198 std::set_intersection(std::begin(drmMods), std::end(drmMods), std::begin(prevMods),
199 std::end(prevMods), std::back_inserter(drmModifiers));
200 }
201 }
202
203 EGLint drmModifiersSize = static_cast<EGLint>(drmModifiers.size());
204
205 *numModifiers = drmModifiersSize;
206 if (maxModifiers > 0)
207 {
208 maxModifiers = std::min(maxModifiers, drmModifiersSize);
209 std::memcpy(modifiers, drmModifiers.data(), maxModifiers * sizeof(drmModifiers[0]));
210 }
211
212 return egl::NoError();
213 }
214 } // namespace rx
215