1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program Tester Core
3 * ----------------------------------------
4 *
5 * Copyright (c) 2016 The Khronos Group Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Linux Vulkan Platform.
22 *//*--------------------------------------------------------------------*/
23
24 #include "tcuLnxVulkanPlatform.hpp"
25 #include "tcuLnxPlatform.hpp"
26 #include "vkDefs.hpp"
27 #include "vkDeviceUtil.hpp"
28 #include "vkQueryUtil.hpp"
29 #include "vkWsiPlatform.hpp"
30 #include "gluPlatform.hpp"
31 #include "tcuLibDrm.hpp"
32 #include "tcuLnx.hpp"
33 #include "tcuFunctionLibrary.hpp"
34 #include "deUniquePtr.hpp"
35 #include "deMemory.h"
36
37 #include <sys/utsname.h>
38
39 using de::MovePtr;
40 using de::UniquePtr;
41 #if DEQP_SUPPORT_DRM && !defined(CTS_USES_VULKANSC)
42 using tcu::LibDrm;
43 #endif // DEQP_SUPPORT_DRM && !defined (CTS_USES_VULKANSC)
44
45 #if defined(DEQP_SUPPORT_X11)
46 #include "tcuLnxX11.hpp"
47 #if defined(DEQP_SUPPORT_XCB)
48 #include "tcuLnxX11Xcb.hpp"
49 #endif // DEQP_SUPPORT_XCB
50 #define X11_DISPLAY ""
51 #endif // DEQP_SUPPORT_X11
52
53 #if defined(DEQP_SUPPORT_WAYLAND)
54 #include "tcuLnxWayland.hpp"
55 #define WAYLAND_DISPLAY DE_NULL
56 #endif // DEQP_SUPPORT_WAYLAND
57
58 #if !defined(DEQP_VULKAN_LIBRARY_PATH)
59 #define DEQP_VULKAN_LIBRARY_PATH "libvulkan.so.1"
60 #endif
61
62 namespace tcu
63 {
64 namespace lnx
65 {
66
67 #if defined(DEQP_SUPPORT_X11)
68
69 class VulkanWindowXlib : public vk::wsi::XlibWindowInterface
70 {
71 public:
VulkanWindowXlib(MovePtr<x11::XlibWindow> window)72 VulkanWindowXlib(MovePtr<x11::XlibWindow> window)
73 : vk::wsi::XlibWindowInterface(vk::pt::XlibWindow(window->getXID()))
74 , m_window(window)
75 {
76 }
77
setVisible(bool visible)78 void setVisible(bool visible)
79 {
80 m_window->setVisibility(visible);
81 }
82
resize(const UVec2 & newSize)83 void resize(const UVec2 &newSize)
84 {
85 m_window->setDimensions((int)newSize.x(), (int)newSize.y());
86 }
87
setMinimized(bool minimized)88 void setMinimized(bool minimized)
89 {
90 DE_UNREF(minimized);
91 TCU_THROW(NotSupportedError, "Minimized on X11 is not implemented");
92 }
93
94 private:
95 UniquePtr<x11::XlibWindow> m_window;
96 };
97
98 class VulkanDisplayXlib : public vk::wsi::XlibDisplayInterface
99 {
100 public:
VulkanDisplayXlib(MovePtr<x11::DisplayBase> display)101 VulkanDisplayXlib(MovePtr<x11::DisplayBase> display)
102 : vk::wsi::XlibDisplayInterface(vk::pt::XlibDisplayPtr(((x11::XlibDisplay *)display.get())->getXDisplay()))
103 , m_display(display)
104 {
105 }
106
createWindow(const Maybe<UVec2> & initialSize) const107 vk::wsi::Window *createWindow(const Maybe<UVec2> &initialSize) const
108 {
109 x11::XlibDisplay *instance = (x11::XlibDisplay *)(m_display.get());
110 const uint32_t height = !initialSize ? (uint32_t)DEFAULT_WINDOW_HEIGHT : initialSize->y();
111 const uint32_t width = !initialSize ? (uint32_t)DEFAULT_WINDOW_WIDTH : initialSize->x();
112 return new VulkanWindowXlib(
113 MovePtr<x11::XlibWindow>(new x11::XlibWindow(*instance, (int)width, (int)height, instance->getVisual(0))));
114 }
115
116 private:
117 MovePtr<x11::DisplayBase> m_display;
118 };
119
120 #endif // DEQP_SUPPORT_X11
121
122 #if defined(DEQP_SUPPORT_XCB)
123
124 class VulkanWindowXcb : public vk::wsi::XcbWindowInterface
125 {
126 public:
VulkanWindowXcb(MovePtr<x11::XcbWindow> window)127 VulkanWindowXcb(MovePtr<x11::XcbWindow> window)
128 : vk::wsi::XcbWindowInterface(vk::pt::XcbWindow(window->getXID()))
129 , m_window(window)
130 {
131 }
132
setVisible(bool visible)133 void setVisible(bool visible)
134 {
135 m_window->setVisibility(visible);
136 }
137
resize(const UVec2 & newSize)138 void resize(const UVec2 &newSize)
139 {
140 m_window->setDimensions((int)newSize.x(), (int)newSize.y());
141 }
142
setMinimized(bool minimized)143 void setMinimized(bool minimized)
144 {
145 DE_UNREF(minimized);
146 TCU_THROW(NotSupportedError, "Minimized on xcb is not implemented");
147 }
148
149 private:
150 UniquePtr<x11::XcbWindow> m_window;
151 };
152
153 class VulkanDisplayXcb : public vk::wsi::XcbDisplayInterface
154 {
155 public:
VulkanDisplayXcb(MovePtr<x11::DisplayBase> display)156 VulkanDisplayXcb(MovePtr<x11::DisplayBase> display)
157 : vk::wsi::XcbDisplayInterface(vk::pt::XcbConnectionPtr(((x11::XcbDisplay *)display.get())->getConnection()))
158 , m_display(display)
159 {
160 }
161
createWindow(const Maybe<UVec2> & initialSize) const162 vk::wsi::Window *createWindow(const Maybe<UVec2> &initialSize) const
163 {
164 x11::XcbDisplay *instance = (x11::XcbDisplay *)(m_display.get());
165 const uint32_t height = !initialSize ? (uint32_t)DEFAULT_WINDOW_HEIGHT : initialSize->y();
166 const uint32_t width = !initialSize ? (uint32_t)DEFAULT_WINDOW_WIDTH : initialSize->x();
167 return new VulkanWindowXcb(
168 MovePtr<x11::XcbWindow>(new x11::XcbWindow(*instance, (int)width, (int)height, DE_NULL)));
169 }
170
171 private:
172 MovePtr<x11::DisplayBase> m_display;
173 };
174 #endif // DEQP_SUPPORT_XCB
175
176 #if defined(DEQP_SUPPORT_WAYLAND)
177 class VulkanWindowWayland : public vk::wsi::WaylandWindowInterface
178 {
179 public:
VulkanWindowWayland(MovePtr<wayland::Window> window)180 VulkanWindowWayland(MovePtr<wayland::Window> window)
181 : vk::wsi::WaylandWindowInterface(vk::pt::WaylandSurfacePtr(window->getSurface()))
182 , m_window(window)
183 {
184 }
185
setVisible(bool visible)186 void setVisible(bool visible)
187 {
188 m_window->setVisibility(visible);
189 }
190
resize(const UVec2 & newSize)191 void resize(const UVec2 &newSize)
192 {
193 m_window->setDimensions((int)newSize.x(), (int)newSize.y());
194 }
195
setMinimized(bool minimized)196 void setMinimized(bool minimized)
197 {
198 DE_UNREF(minimized);
199 TCU_THROW(NotSupportedError, "Minimized on wayland is not implemented");
200 }
201
202 private:
203 UniquePtr<wayland::Window> m_window;
204 };
205
206 class VulkanDisplayWayland : public vk::wsi::WaylandDisplayInterface
207 {
208 public:
VulkanDisplayWayland(MovePtr<wayland::Display> display)209 VulkanDisplayWayland(MovePtr<wayland::Display> display)
210 : vk::wsi::WaylandDisplayInterface(vk::pt::WaylandDisplayPtr(display->getDisplay()))
211 , m_display(display)
212 {
213 }
214
createWindow(const Maybe<UVec2> & initialSize) const215 vk::wsi::Window *createWindow(const Maybe<UVec2> &initialSize) const
216 {
217 const uint32_t height = !initialSize ? (uint32_t)DEFAULT_WINDOW_HEIGHT : initialSize->y();
218 const uint32_t width = !initialSize ? (uint32_t)DEFAULT_WINDOW_WIDTH : initialSize->x();
219 return new VulkanWindowWayland(
220 MovePtr<wayland::Window>(new wayland::Window(*m_display, (int)width, (int)height)));
221 }
222
223 private:
224 MovePtr<wayland::Display> m_display;
225 };
226 #endif // DEQP_SUPPORT_WAYLAND
227
228 #if defined(DEQP_SUPPORT_HEADLESS)
229
230 struct VulkanWindowHeadless : public vk::wsi::Window
231 {
232 public:
resizetcu::lnx::VulkanWindowHeadless233 void resize(const UVec2 &)
234 {
235 }
236 };
237
238 class VulkanDisplayHeadless : public vk::wsi::Display
239 {
240 public:
VulkanDisplayHeadless()241 VulkanDisplayHeadless()
242 {
243 }
244
createWindow(const Maybe<UVec2> &) const245 vk::wsi::Window *createWindow(const Maybe<UVec2> &) const
246 {
247 return new VulkanWindowHeadless();
248 }
249 };
250
251 #endif // DEQP_SUPPORT_HEADLESS
252
253 #if DEQP_SUPPORT_DRM && !defined(CTS_USES_VULKANSC)
254
255 struct VulkanWindowDirectDrm : public vk::wsi::Window
256 {
257 public:
setVisibletcu::lnx::VulkanWindowDirectDrm258 void setVisible(bool visible) override
259 {
260 DE_UNREF(visible);
261 }
resizetcu::lnx::VulkanWindowDirectDrm262 void resize(const UVec2 &) override
263 {
264 }
265 };
266
267 class VulkanDisplayDirectDrm : public vk::wsi::DirectDrmDisplayInterface
268 {
269 public:
VulkanDisplayDirectDrm(void)270 VulkanDisplayDirectDrm(void)
271 {
272 }
273
createWindow(const Maybe<UVec2> &) const274 vk::wsi::Window *createWindow(const Maybe<UVec2> &) const override
275 {
276 return new VulkanWindowDirectDrm();
277 }
278
initializeDisplay(const vk::InstanceInterface & vki,vk::VkInstance instance,const tcu::CommandLine & cmdLine)279 void initializeDisplay(const vk::InstanceInterface &vki, vk::VkInstance instance,
280 const tcu::CommandLine &cmdLine) override
281 {
282 if (m_initialized)
283 return;
284
285 vk::VkPhysicalDevice physDevice = vk::chooseDevice(vki, instance, cmdLine);
286
287 /* Get a Drm fd that matches the device. */
288
289 vk::VkPhysicalDeviceProperties2 deviceProperties2;
290 vk::VkPhysicalDeviceDrmPropertiesEXT deviceDrmProperties;
291
292 deMemset(&deviceDrmProperties, 0, sizeof(deviceDrmProperties));
293 deviceDrmProperties.sType = vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT;
294 deviceDrmProperties.pNext = DE_NULL;
295
296 deMemset(&deviceProperties2, 0, sizeof(deviceProperties2));
297 deviceProperties2.sType = vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
298 deviceProperties2.pNext = &deviceDrmProperties;
299
300 vki.getPhysicalDeviceProperties2(physDevice, &deviceProperties2);
301
302 if (!deviceDrmProperties.hasPrimary)
303 TCU_THROW(NotSupportedError, "No DRM primary device.");
304
305 LibDrm libDrm;
306 int numDrmDevices;
307 drmDevicePtr *drmDevices = libDrm.getDevices(&numDrmDevices);
308 const char *drmNode = libDrm.findDeviceNode(drmDevices, numDrmDevices, deviceDrmProperties.primaryMajor,
309 deviceDrmProperties.primaryMinor);
310
311 if (!drmNode)
312 TCU_THROW(NotSupportedError, "No DRM node.");
313
314 m_fdPtr = libDrm.openFd(drmNode).move();
315 if (!m_fdPtr)
316 TCU_THROW(NotSupportedError, "Could not open DRM.");
317 int fd = *m_fdPtr;
318
319 /* Get a connector to the display. */
320
321 LibDrm::ResPtr res = libDrm.getResources(fd);
322 if (!res)
323 TCU_THROW(NotSupportedError, "Could not get DRM resources.");
324
325 uint32_t connectorId = 0;
326 for (int i = 0; i < res->count_connectors; ++i)
327 {
328 LibDrm::ConnectorPtr conn = libDrm.getConnector(fd, res->connectors[i]);
329
330 if (conn && conn->connection == DRM_MODE_CONNECTED)
331 {
332 connectorId = res->connectors[i];
333 break;
334 }
335 }
336 if (!connectorId)
337 TCU_THROW(NotSupportedError, "Could not find a DRM connector.");
338
339 /* Get and acquire the display for the connector. */
340
341 vk::VkDisplayKHR *display = const_cast<vk::VkDisplayKHR *>(&m_native);
342 VK_CHECK_SUPPORTED(vki.getDrmDisplayEXT(physDevice, fd, connectorId, display));
343
344 if (m_native == DE_NULL)
345 TCU_THROW(NotSupportedError, "vkGetDrmDisplayEXT did not set display.");
346
347 VK_CHECK_SUPPORTED(vki.acquireDrmDisplayEXT(physDevice, fd, m_native));
348 m_initialized = true;
349 }
350
351 MovePtr<LibDrm::FdPtr::element_type, LibDrm::FdPtr::deleter_type> m_fdPtr;
352 bool m_initialized = false;
353 };
354
355 #endif // DEQP_SUPPORT_DRM && !defined (CTS_USES_VULKANSC)
356
357 class VulkanLibrary : public vk::Library
358 {
359 public:
VulkanLibrary(const char * libraryPath)360 VulkanLibrary(const char *libraryPath)
361 : m_library(libraryPath != DE_NULL ? libraryPath : DEQP_VULKAN_LIBRARY_PATH)
362 , m_driver(m_library)
363 {
364 }
365
getPlatformInterface(void) const366 const vk::PlatformInterface &getPlatformInterface(void) const
367 {
368 return m_driver;
369 }
370
getFunctionLibrary(void) const371 const tcu::FunctionLibrary &getFunctionLibrary(void) const
372 {
373 return m_library;
374 }
375
376 private:
377 const DynamicFunctionLibrary m_library;
378 const vk::PlatformDriver m_driver;
379 };
380
VulkanPlatform(EventState & eventState)381 VulkanPlatform::VulkanPlatform(EventState &eventState) : m_eventState(eventState)
382 {
383 }
384
createWsiDisplay(vk::wsi::Type wsiType) const385 vk::wsi::Display *VulkanPlatform::createWsiDisplay(vk::wsi::Type wsiType) const
386 {
387 if (!hasDisplay(wsiType))
388 {
389 throw NotSupportedError("This display type is not available: ", NULL, __FILE__, __LINE__);
390 }
391
392 switch (wsiType)
393 {
394 #if defined(DEQP_SUPPORT_X11)
395 case vk::wsi::TYPE_XLIB:
396 return new VulkanDisplayXlib(MovePtr<x11::DisplayBase>(new x11::XlibDisplay(m_eventState, X11_DISPLAY)));
397 #endif // DEQP_SUPPORT_X11
398 #if defined(DEQP_SUPPORT_XCB)
399 case vk::wsi::TYPE_XCB:
400 return new VulkanDisplayXcb(MovePtr<x11::DisplayBase>(new x11::XcbDisplay(m_eventState, X11_DISPLAY)));
401 #endif // DEQP_SUPPORT_XCB
402 #if defined(DEQP_SUPPORT_WAYLAND)
403 case vk::wsi::TYPE_WAYLAND:
404 return new VulkanDisplayWayland(MovePtr<wayland::Display>(new wayland::Display(m_eventState, WAYLAND_DISPLAY)));
405 #endif // DEQP_SUPPORT_WAYLAND
406 #if defined(DEQP_SUPPORT_HEADLESS)
407 case vk::wsi::TYPE_HEADLESS:
408 return new VulkanDisplayHeadless();
409 #endif // DEQP_SUPPORT_HEADLESS
410 #if DEQP_SUPPORT_DRM && !defined(CTS_USES_VULKANSC)
411 case vk::wsi::TYPE_DIRECT_DRM:
412 return new VulkanDisplayDirectDrm();
413 #endif // DEQP_SUPPORT_DRM && !defined (CTS_USES_VULKANSC)
414
415 default:
416 TCU_THROW(NotSupportedError, "WSI type not supported");
417 }
418 }
hasDisplay(vk::wsi::Type wsiType) const419 bool VulkanPlatform::hasDisplay(vk::wsi::Type wsiType) const
420 {
421 switch (wsiType)
422 {
423 #if defined(DEQP_SUPPORT_X11)
424 case vk::wsi::TYPE_XLIB:
425 return x11::XlibDisplay::hasDisplay(X11_DISPLAY);
426 #endif // DEQP_SUPPORT_X11
427 #if defined(DEQP_SUPPORT_XCB)
428 case vk::wsi::TYPE_XCB:
429 return x11::XcbDisplay::hasDisplay(X11_DISPLAY);
430 #endif // DEQP_SUPPORT_XCB
431 #if defined(DEQP_SUPPORT_WAYLAND)
432 case vk::wsi::TYPE_WAYLAND:
433 return wayland::Display::hasDisplay(WAYLAND_DISPLAY);
434 #endif // DEQP_SUPPORT_WAYLAND
435 #if defined(DEQP_SUPPORT_HEADLESS)
436 case vk::wsi::TYPE_HEADLESS:
437 return true;
438 #endif // DEQP_SUPPORT_HEADLESS
439 #if DEQP_SUPPORT_DRM && !defined(CTS_USES_VULKANSC)
440 case vk::wsi::TYPE_DIRECT_DRM:
441 return true;
442 #endif // DEQP_SUPPORT_DRM && !defined (CTS_USES_VULKANSC)
443 default:
444 return false;
445 }
446 }
447
createLibrary(LibraryType libraryType,const char * libraryPath) const448 vk::Library *VulkanPlatform::createLibrary(LibraryType libraryType, const char *libraryPath) const
449 {
450 switch (libraryType)
451 {
452 case LIBRARY_TYPE_VULKAN:
453 return new VulkanLibrary(libraryPath);
454
455 default:
456 TCU_THROW(InternalError, "Unknown library type requested");
457 }
458 }
459
describePlatform(std::ostream & dst) const460 void VulkanPlatform::describePlatform(std::ostream &dst) const
461 {
462 utsname sysInfo;
463 deMemset(&sysInfo, 0, sizeof(sysInfo));
464
465 if (uname(&sysInfo) != 0)
466 throw std::runtime_error("uname() failed");
467
468 dst << "OS: " << sysInfo.sysname << " " << sysInfo.release << " " << sysInfo.version << "\n";
469 dst << "CPU: " << sysInfo.machine << "\n";
470 }
471
472 } // namespace lnx
473 } // namespace tcu
474