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 nullptr
56 #endif // DEQP_SUPPORT_WAYLAND
57
58 #if !defined(DEQP_VULKAN_LIBRARY_PATH)
59 #ifdef CTS_USES_VULKANSC
60 #define DEQP_VULKAN_LIBRARY_PATH "libvulkansc.so.1"
61 #else
62 #define DEQP_VULKAN_LIBRARY_PATH "libvulkan.so.1"
63 #endif
64 #endif
65
66 namespace tcu
67 {
68 namespace lnx
69 {
70
71 #if defined(DEQP_SUPPORT_X11)
72
73 class VulkanWindowXlib : public vk::wsi::XlibWindowInterface
74 {
75 public:
VulkanWindowXlib(MovePtr<x11::XlibWindow> window)76 VulkanWindowXlib(MovePtr<x11::XlibWindow> window)
77 : vk::wsi::XlibWindowInterface(vk::pt::XlibWindow(window->getXID()))
78 , m_window(window)
79 {
80 }
81
setVisible(bool visible)82 void setVisible(bool visible)
83 {
84 m_window->setVisibility(visible);
85 }
86
resize(const UVec2 & newSize)87 void resize(const UVec2 &newSize)
88 {
89 m_window->setDimensions((int)newSize.x(), (int)newSize.y());
90 }
91
setMinimized(bool minimized)92 void setMinimized(bool minimized)
93 {
94 DE_UNREF(minimized);
95 TCU_THROW(NotSupportedError, "Minimized on X11 is not implemented");
96 }
97
98 private:
99 UniquePtr<x11::XlibWindow> m_window;
100 };
101
102 class VulkanDisplayXlib : public vk::wsi::XlibDisplayInterface
103 {
104 public:
VulkanDisplayXlib(MovePtr<x11::DisplayBase> display)105 VulkanDisplayXlib(MovePtr<x11::DisplayBase> display)
106 : vk::wsi::XlibDisplayInterface(vk::pt::XlibDisplayPtr(((x11::XlibDisplay *)display.get())->getXDisplay()))
107 , m_display(display)
108 {
109 }
110
createWindow(const Maybe<UVec2> & initialSize) const111 vk::wsi::Window *createWindow(const Maybe<UVec2> &initialSize) const
112 {
113 x11::XlibDisplay *instance = (x11::XlibDisplay *)(m_display.get());
114 const uint32_t height = !initialSize ? (uint32_t)DEFAULT_WINDOW_HEIGHT : initialSize->y();
115 const uint32_t width = !initialSize ? (uint32_t)DEFAULT_WINDOW_WIDTH : initialSize->x();
116 return new VulkanWindowXlib(
117 MovePtr<x11::XlibWindow>(new x11::XlibWindow(*instance, (int)width, (int)height, instance->getVisual(0))));
118 }
119
120 private:
121 MovePtr<x11::DisplayBase> m_display;
122 };
123
124 #endif // DEQP_SUPPORT_X11
125
126 #if defined(DEQP_SUPPORT_XCB)
127
128 class VulkanWindowXcb : public vk::wsi::XcbWindowInterface
129 {
130 public:
VulkanWindowXcb(MovePtr<x11::XcbWindow> window)131 VulkanWindowXcb(MovePtr<x11::XcbWindow> window)
132 : vk::wsi::XcbWindowInterface(vk::pt::XcbWindow(window->getXID()))
133 , m_window(window)
134 {
135 }
136
setVisible(bool visible)137 void setVisible(bool visible)
138 {
139 m_window->setVisibility(visible);
140 }
141
resize(const UVec2 & newSize)142 void resize(const UVec2 &newSize)
143 {
144 m_window->setDimensions((int)newSize.x(), (int)newSize.y());
145 }
146
setMinimized(bool minimized)147 void setMinimized(bool minimized)
148 {
149 DE_UNREF(minimized);
150 TCU_THROW(NotSupportedError, "Minimized on xcb is not implemented");
151 }
152
153 private:
154 UniquePtr<x11::XcbWindow> m_window;
155 };
156
157 class VulkanDisplayXcb : public vk::wsi::XcbDisplayInterface
158 {
159 public:
VulkanDisplayXcb(MovePtr<x11::DisplayBase> display)160 VulkanDisplayXcb(MovePtr<x11::DisplayBase> display)
161 : vk::wsi::XcbDisplayInterface(vk::pt::XcbConnectionPtr(((x11::XcbDisplay *)display.get())->getConnection()))
162 , m_display(display)
163 {
164 }
165
createWindow(const Maybe<UVec2> & initialSize) const166 vk::wsi::Window *createWindow(const Maybe<UVec2> &initialSize) const
167 {
168 x11::XcbDisplay *instance = (x11::XcbDisplay *)(m_display.get());
169 const uint32_t height = !initialSize ? (uint32_t)DEFAULT_WINDOW_HEIGHT : initialSize->y();
170 const uint32_t width = !initialSize ? (uint32_t)DEFAULT_WINDOW_WIDTH : initialSize->x();
171 return new VulkanWindowXcb(
172 MovePtr<x11::XcbWindow>(new x11::XcbWindow(*instance, (int)width, (int)height, nullptr)));
173 }
174
175 private:
176 MovePtr<x11::DisplayBase> m_display;
177 };
178 #endif // DEQP_SUPPORT_XCB
179
180 #if defined(DEQP_SUPPORT_WAYLAND)
181 class VulkanWindowWayland : public vk::wsi::WaylandWindowInterface
182 {
183 public:
VulkanWindowWayland(MovePtr<wayland::Window> window)184 VulkanWindowWayland(MovePtr<wayland::Window> window)
185 : vk::wsi::WaylandWindowInterface(vk::pt::WaylandSurfacePtr(window->getSurface()))
186 , m_window(window)
187 {
188 }
189
setVisible(bool visible)190 void setVisible(bool visible)
191 {
192 m_window->setVisibility(visible);
193 }
194
resize(const UVec2 & newSize)195 void resize(const UVec2 &newSize)
196 {
197 m_window->setDimensions((int)newSize.x(), (int)newSize.y());
198 }
199
setMinimized(bool minimized)200 void setMinimized(bool minimized)
201 {
202 DE_UNREF(minimized);
203 TCU_THROW(NotSupportedError, "Minimized on wayland is not implemented");
204 }
205
206 private:
207 UniquePtr<wayland::Window> m_window;
208 };
209
210 class VulkanDisplayWayland : public vk::wsi::WaylandDisplayInterface
211 {
212 public:
VulkanDisplayWayland(MovePtr<wayland::Display> display)213 VulkanDisplayWayland(MovePtr<wayland::Display> display)
214 : vk::wsi::WaylandDisplayInterface(vk::pt::WaylandDisplayPtr(display->getDisplay()))
215 , m_display(display)
216 {
217 }
218
createWindow(const Maybe<UVec2> & initialSize) const219 vk::wsi::Window *createWindow(const Maybe<UVec2> &initialSize) const
220 {
221 const uint32_t height = !initialSize ? (uint32_t)DEFAULT_WINDOW_HEIGHT : initialSize->y();
222 const uint32_t width = !initialSize ? (uint32_t)DEFAULT_WINDOW_WIDTH : initialSize->x();
223 return new VulkanWindowWayland(
224 MovePtr<wayland::Window>(new wayland::Window(*m_display, (int)width, (int)height)));
225 }
226
227 private:
228 MovePtr<wayland::Display> m_display;
229 };
230 #endif // DEQP_SUPPORT_WAYLAND
231
232 #if defined(DEQP_SUPPORT_HEADLESS)
233
234 struct VulkanWindowHeadless : public vk::wsi::Window
235 {
236 public:
resizetcu::lnx::VulkanWindowHeadless237 void resize(const UVec2 &)
238 {
239 }
240 };
241
242 class VulkanDisplayHeadless : public vk::wsi::Display
243 {
244 public:
VulkanDisplayHeadless()245 VulkanDisplayHeadless()
246 {
247 }
248
createWindow(const Maybe<UVec2> &) const249 vk::wsi::Window *createWindow(const Maybe<UVec2> &) const
250 {
251 return new VulkanWindowHeadless();
252 }
253 };
254
255 #endif // DEQP_SUPPORT_HEADLESS
256
257 #if DEQP_SUPPORT_DRM && !defined(CTS_USES_VULKANSC)
258
259 struct VulkanWindowDirectDrm : public vk::wsi::Window
260 {
261 public:
setVisibletcu::lnx::VulkanWindowDirectDrm262 void setVisible(bool visible) override
263 {
264 DE_UNREF(visible);
265 }
resizetcu::lnx::VulkanWindowDirectDrm266 void resize(const UVec2 &) override
267 {
268 }
269 };
270
271 class VulkanDisplayDirectDrm : public vk::wsi::DirectDrmDisplayInterface
272 {
273 public:
VulkanDisplayDirectDrm(void)274 VulkanDisplayDirectDrm(void)
275 {
276 }
277
createWindow(const Maybe<UVec2> &) const278 vk::wsi::Window *createWindow(const Maybe<UVec2> &) const override
279 {
280 return new VulkanWindowDirectDrm();
281 }
282
initializeDisplay(const vk::InstanceInterface & vki,vk::VkInstance instance,const tcu::CommandLine & cmdLine)283 void initializeDisplay(const vk::InstanceInterface &vki, vk::VkInstance instance,
284 const tcu::CommandLine &cmdLine) override
285 {
286 if (m_initialized)
287 return;
288
289 vk::VkPhysicalDevice physDevice = vk::chooseDevice(vki, instance, cmdLine);
290
291 /* Get a Drm fd that matches the device. */
292
293 vk::VkPhysicalDeviceProperties2 deviceProperties2;
294 vk::VkPhysicalDeviceDrmPropertiesEXT deviceDrmProperties;
295
296 deMemset(&deviceDrmProperties, 0, sizeof(deviceDrmProperties));
297 deviceDrmProperties.sType = vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT;
298 deviceDrmProperties.pNext = nullptr;
299
300 deMemset(&deviceProperties2, 0, sizeof(deviceProperties2));
301 deviceProperties2.sType = vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
302 deviceProperties2.pNext = &deviceDrmProperties;
303
304 vki.getPhysicalDeviceProperties2(physDevice, &deviceProperties2);
305
306 if (!deviceDrmProperties.hasPrimary)
307 TCU_THROW(NotSupportedError, "No DRM primary device.");
308
309 LibDrm libDrm;
310 int numDrmDevices;
311 drmDevicePtr *drmDevices = libDrm.getDevices(&numDrmDevices);
312 const char *drmNode = libDrm.findDeviceNode(drmDevices, numDrmDevices, deviceDrmProperties.primaryMajor,
313 deviceDrmProperties.primaryMinor);
314
315 if (!drmNode)
316 TCU_THROW(NotSupportedError, "No DRM node.");
317
318 m_fdPtr = libDrm.openFd(drmNode).move();
319 if (!m_fdPtr)
320 TCU_THROW(NotSupportedError, "Could not open DRM.");
321 int fd = *m_fdPtr;
322
323 /* Get a connector to the display. */
324
325 LibDrm::ResPtr res = libDrm.getResources(fd);
326 if (!res)
327 TCU_THROW(NotSupportedError, "Could not get DRM resources.");
328
329 uint32_t connectorId = 0;
330 for (int i = 0; i < res->count_connectors; ++i)
331 {
332 LibDrm::ConnectorPtr conn = libDrm.getConnector(fd, res->connectors[i]);
333
334 if (conn && conn->connection == DRM_MODE_CONNECTED)
335 {
336 connectorId = res->connectors[i];
337 break;
338 }
339 }
340 if (!connectorId)
341 TCU_THROW(NotSupportedError, "Could not find a DRM connector.");
342
343 /* Get and acquire the display for the connector. */
344
345 vk::VkDisplayKHR *display = const_cast<vk::VkDisplayKHR *>(&m_native);
346 VK_CHECK_SUPPORTED(vki.getDrmDisplayEXT(physDevice, fd, connectorId, display));
347
348 if (m_native == VK_NULL_HANDLE)
349 TCU_THROW(NotSupportedError, "vkGetDrmDisplayEXT did not set display.");
350
351 VK_CHECK_SUPPORTED(vki.acquireDrmDisplayEXT(physDevice, fd, m_native));
352 m_initialized = true;
353 }
354
355 MovePtr<LibDrm::FdPtr::element_type, LibDrm::FdPtr::deleter_type> m_fdPtr;
356 bool m_initialized = false;
357 };
358
359 #endif // DEQP_SUPPORT_DRM && !defined (CTS_USES_VULKANSC)
360
361 class VulkanLibrary : public vk::Library
362 {
363 public:
VulkanLibrary(const char * libraryPath)364 VulkanLibrary(const char *libraryPath)
365 : m_library(libraryPath != nullptr ? libraryPath : DEQP_VULKAN_LIBRARY_PATH)
366 , m_driver(m_library)
367 {
368 }
369
getPlatformInterface(void) const370 const vk::PlatformInterface &getPlatformInterface(void) const
371 {
372 return m_driver;
373 }
374
getFunctionLibrary(void) const375 const tcu::FunctionLibrary &getFunctionLibrary(void) const
376 {
377 return m_library;
378 }
379
380 private:
381 const DynamicFunctionLibrary m_library;
382 const vk::PlatformDriver m_driver;
383 };
384
VulkanPlatform(EventState & eventState)385 VulkanPlatform::VulkanPlatform(EventState &eventState) : m_eventState(eventState)
386 {
387 }
388
createWsiDisplay(vk::wsi::Type wsiType) const389 vk::wsi::Display *VulkanPlatform::createWsiDisplay(vk::wsi::Type wsiType) const
390 {
391 if (!hasDisplay(wsiType))
392 {
393 throw NotSupportedError("This display type is not available: ", NULL, __FILE__, __LINE__);
394 }
395
396 switch (wsiType)
397 {
398 #if defined(DEQP_SUPPORT_X11)
399 case vk::wsi::TYPE_XLIB:
400 return new VulkanDisplayXlib(MovePtr<x11::DisplayBase>(new x11::XlibDisplay(m_eventState, X11_DISPLAY)));
401 #endif // DEQP_SUPPORT_X11
402 #if defined(DEQP_SUPPORT_XCB)
403 case vk::wsi::TYPE_XCB:
404 return new VulkanDisplayXcb(MovePtr<x11::DisplayBase>(new x11::XcbDisplay(m_eventState, X11_DISPLAY)));
405 #endif // DEQP_SUPPORT_XCB
406 #if defined(DEQP_SUPPORT_WAYLAND)
407 case vk::wsi::TYPE_WAYLAND:
408 return new VulkanDisplayWayland(MovePtr<wayland::Display>(new wayland::Display(m_eventState, WAYLAND_DISPLAY)));
409 #endif // DEQP_SUPPORT_WAYLAND
410 #if defined(DEQP_SUPPORT_HEADLESS)
411 case vk::wsi::TYPE_HEADLESS:
412 return new VulkanDisplayHeadless();
413 #endif // DEQP_SUPPORT_HEADLESS
414 #if DEQP_SUPPORT_DRM && !defined(CTS_USES_VULKANSC)
415 case vk::wsi::TYPE_DIRECT_DRM:
416 return new VulkanDisplayDirectDrm();
417 #endif // DEQP_SUPPORT_DRM && !defined (CTS_USES_VULKANSC)
418
419 default:
420 TCU_THROW(NotSupportedError, "WSI type not supported");
421 }
422 }
hasDisplay(vk::wsi::Type wsiType) const423 bool VulkanPlatform::hasDisplay(vk::wsi::Type wsiType) const
424 {
425 switch (wsiType)
426 {
427 #if defined(DEQP_SUPPORT_X11)
428 case vk::wsi::TYPE_XLIB:
429 return x11::XlibDisplay::hasDisplay(X11_DISPLAY);
430 #endif // DEQP_SUPPORT_X11
431 #if defined(DEQP_SUPPORT_XCB)
432 case vk::wsi::TYPE_XCB:
433 return x11::XcbDisplay::hasDisplay(X11_DISPLAY);
434 #endif // DEQP_SUPPORT_XCB
435 #if defined(DEQP_SUPPORT_WAYLAND)
436 case vk::wsi::TYPE_WAYLAND:
437 return wayland::Display::hasDisplay(WAYLAND_DISPLAY);
438 #endif // DEQP_SUPPORT_WAYLAND
439 #if defined(DEQP_SUPPORT_HEADLESS)
440 case vk::wsi::TYPE_HEADLESS:
441 return true;
442 #endif // DEQP_SUPPORT_HEADLESS
443 #if DEQP_SUPPORT_DRM && !defined(CTS_USES_VULKANSC)
444 case vk::wsi::TYPE_DIRECT_DRM:
445 return true;
446 #endif // DEQP_SUPPORT_DRM && !defined (CTS_USES_VULKANSC)
447 default:
448 return false;
449 }
450 }
451
createLibrary(LibraryType libraryType,const char * libraryPath) const452 vk::Library *VulkanPlatform::createLibrary(LibraryType libraryType, const char *libraryPath) const
453 {
454 switch (libraryType)
455 {
456 case LIBRARY_TYPE_VULKAN:
457 return new VulkanLibrary(libraryPath);
458
459 default:
460 TCU_THROW(InternalError, "Unknown library type requested");
461 }
462 }
463
describePlatform(std::ostream & dst) const464 void VulkanPlatform::describePlatform(std::ostream &dst) const
465 {
466 utsname sysInfo;
467 deMemset(&sysInfo, 0, sizeof(sysInfo));
468
469 if (uname(&sysInfo) != 0)
470 throw std::runtime_error("uname() failed");
471
472 dst << "OS: " << sysInfo.sysname << " " << sysInfo.release << " " << sysInfo.version << "\n";
473 dst << "CPU: " << sysInfo.machine << "\n";
474 }
475
476 } // namespace lnx
477 } // namespace tcu
478