1 /*
2 * Copyright (C) 2016 Intel Corporation. All Rights Reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sub license, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial portions
14 * of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
19 * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
20 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25 #include "test_va_api_fixture.h"
26
27 #include <algorithm>
28 #include <functional>
29
30 #include <fcntl.h> // for O_RDWR
31 #include <limits>
32 #include <string.h>
33
34 #if defined(_WIN32)
35 #include <va/va_win32.h>
36 #include <compat_win32.h>
37 #else
38 #include <unistd.h> // for close()
39 #include <va/va_drm.h>
40 #include <xf86drm.h>
41 #endif
42
43 namespace VAAPI
44 {
45
46 int VAAPIFixtureSharedDisplay::s_drmHandle = -1;
47 VADisplay VAAPIFixtureSharedDisplay::s_vaDisplay = nullptr;
48 VAStatus VAAPIFixtureSharedDisplay::s_initStatus = VA_STATUS_SUCCESS;
49
VAAPIFixture()50 VAAPIFixture::VAAPIFixture()
51 : ::testing::Test::Test()
52 , m_vaDisplay(NULL)
53 , m_drmHandle(-1)
54 , m_configID(VA_INVALID_ID)
55 , m_contextID(VA_INVALID_ID)
56 , m_bufferID(VA_INVALID_ID)
57 , m_skip("")
58 {
59 // If we do not copy the value and use the same pointer returned by getenv to restore the value
60 // in ~VAAPIFixture with setenv, we see garbage memory being set on Windows platforms.
61 char* libva_driver = getenv("LIBVA_DRIVER_NAME");
62 if (libva_driver) m_restoreDriverName = libva_driver;
63 }
64
~VAAPIFixture()65 VAAPIFixture::~VAAPIFixture()
66 {
67 m_vaDisplay = NULL;
68 if (m_drmHandle >= 0)
69 close(m_drmHandle);
70 m_drmHandle = -1;
71
72 // Ensure LIBVA_DRIVER_NAME environment is restored to its original
73 // setting so successive tests use the expected driver.
74 unsetenv("LIBVA_DRIVER_NAME");
75 if (!m_restoreDriverName.empty())
76 setenv("LIBVA_DRIVER_NAME", m_restoreDriverName.c_str(), 1);
77
78 if (!m_skip.empty()) {
79 EXPECT_FALSE(HasFailure())
80 << "skip message is set, but something failed";
81 if (!HasFailure()) {
82 RecordProperty("skipped", true);
83 std::cout << "[ SKIPPED ] " << m_skip << std::endl;
84 }
85 }
86 }
87
88 #if defined(_WIN32)
getWin32Display(LUID * adapter)89 static VADisplay getWin32Display(LUID* adapter)
90 {
91 return vaGetDisplayWin32(adapter);
92 }
93 #else
getDrmDisplay(int & fd)94 static VADisplay getDrmDisplay(int &fd)
95 {
96 drmDevicePtr devices[32];
97 int ret, max_devices = sizeof(devices) / sizeof(devices[0]);
98
99 ret = drmGetDevices2(0, devices, max_devices);
100 EXPECT_TRUE(ret >= 0);
101 if (ret < 0)
102 return NULL;
103 max_devices = ret;
104
105 for (int i = 0; i < max_devices; i++) {
106 for (int j = DRM_NODE_MAX - 1; j >= 0; j--) {
107 drmVersionPtr version;
108
109 if (!(devices[i]->available_nodes & 1 << j))
110 continue;
111
112 fd = open(devices[i]->nodes[j], O_RDWR);
113 if (fd < 0)
114 continue;
115
116 version = drmGetVersion(fd);
117 if (!version) {
118 close(fd);
119 continue;
120 }
121 if (!strncmp(version->name, "vgem", 4)) {
122 drmFreeVersion(version);
123 close(fd);
124 continue;
125 }
126 drmFreeVersion(version);
127
128 VADisplay disp = vaGetDisplayDRM(fd);
129
130 if (disp)
131 return disp;
132
133 close(fd);
134 }
135 }
136
137 return NULL;
138 }
139 #endif
getDisplay()140 VADisplay VAAPIFixture::getDisplay()
141 {
142 #if defined(_WIN32)
143 m_vaDisplay = getWin32Display(NULL);
144 #else
145 m_vaDisplay = getDrmDisplay(m_drmHandle);
146 #endif
147 return m_vaDisplay;
148 }
149
doInitialize()150 VADisplay VAAPIFixture::doInitialize()
151 {
152 VADisplay vaDisplay;
153 VAStatus status;
154 int majorVersion, minorVersion;
155
156 vaDisplay = getDisplay();
157 EXPECT_TRUE(vaDisplay);
158 if (!vaDisplay) {
159 return NULL;
160 }
161
162 status = vaInitialize(vaDisplay, &majorVersion, &minorVersion);
163 EXPECT_STATUS(status) << "failed to initialize vaapi";
164 if (status != VA_STATUS_SUCCESS) {
165 return NULL;
166 }
167
168 return vaDisplay;
169 }
170
queryConfigProfiles(Profiles & profiles) const171 void VAAPIFixture::queryConfigProfiles(Profiles& profiles) const
172 {
173 const int maxProfiles = vaMaxNumProfiles(m_vaDisplay);
174 ASSERT_GT(maxProfiles, 0);
175 profiles.resize(maxProfiles);
176
177 int numProfiles(0);
178 EXPECT_STATUS(
179 vaQueryConfigProfiles(m_vaDisplay, profiles.data(), &numProfiles));
180
181 if (!HasFailure()) {
182 ASSERT_LE(numProfiles, maxProfiles);
183 ASSERT_GT(numProfiles, 0);
184 profiles.resize(numProfiles);
185 } else {
186 profiles.clear();
187 }
188 }
189
queryConfigEntrypoints(const VAProfile & profile,Entrypoints & entrypoints,const VAStatus & expectation) const190 void VAAPIFixture::queryConfigEntrypoints(const VAProfile& profile,
191 Entrypoints& entrypoints, const VAStatus& expectation) const
192 {
193 const int maxEntrypoints = vaMaxNumEntrypoints(m_vaDisplay);
194 ASSERT_GT(maxEntrypoints, 0);
195 entrypoints.resize(maxEntrypoints);
196
197 int numEntrypoints(0);
198 EXPECT_STATUS_EQ(
199 expectation,
200 vaQueryConfigEntrypoints(m_vaDisplay, profile, entrypoints.data(),
201 &numEntrypoints));
202
203 if ((VA_STATUS_SUCCESS == expectation) && !HasFailure()) {
204 ASSERT_LE(numEntrypoints, maxEntrypoints);
205 ASSERT_GT(numEntrypoints, 0);
206 entrypoints.resize(numEntrypoints);
207 } else {
208 entrypoints.clear();
209 }
210 }
211
getSupportStatus(const VAProfile & profile,const VAEntrypoint & entrypoint) const212 VAStatus VAAPIFixture::getSupportStatus(const VAProfile& profile,
213 const VAEntrypoint& entrypoint) const
214 {
215 Profiles profiles;
216 queryConfigProfiles(profiles);
217
218 const auto pBegin(profiles.begin());
219 const auto pEnd(profiles.end());
220 if (std::find(pBegin, pEnd, profile) != pEnd) {
221 Entrypoints entrypoints;
222 queryConfigEntrypoints(profile, entrypoints);
223
224 const auto eBegin(entrypoints.begin());
225 const auto eEnd(entrypoints.end());
226 return (std::find(eBegin, eEnd, entrypoint) != eEnd) ?
227 VA_STATUS_SUCCESS : VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT;
228 }
229
230 return VA_STATUS_ERROR_UNSUPPORTED_PROFILE;
231 }
232
isSupported(const VAProfile & profile,const VAEntrypoint & entrypoint) const233 bool VAAPIFixture::isSupported(const VAProfile& profile,
234 const VAEntrypoint& entrypoint) const
235 {
236 return VA_STATUS_SUCCESS == getSupportStatus(profile, entrypoint);
237 }
238
getConfigAttributes(const VAProfile & profile,const VAEntrypoint & entrypoint,ConfigAttributes & attribs,const VAStatus & expectation) const239 void VAAPIFixture::getConfigAttributes(const VAProfile& profile,
240 const VAEntrypoint& entrypoint, ConfigAttributes& attribs,
241 const VAStatus& expectation) const
242 {
243 const bool defaults(attribs.empty());
244
245 if (defaults) {
246 // fill config attributes with default config attributes
247 const auto op = [](const VAConfigAttribType & t) {
248 return VAConfigAttrib{/*type:*/ t, /*value:*/ VA_ATTRIB_NOT_SUPPORTED};
249 };
250 std::transform(g_vaConfigAttribTypes.begin(),
251 g_vaConfigAttribTypes.end(), std::back_inserter(attribs), op);
252 }
253
254 ASSERT_FALSE(attribs.empty());
255
256 EXPECT_STATUS_EQ(
257 expectation,
258 vaGetConfigAttributes(
259 m_vaDisplay, profile, entrypoint, attribs.data(), attribs.size()));
260
261 if (defaults) {
262 // remove unsupported config attributes
263 const auto begin(attribs.begin());
264 const auto end(attribs.end());
265 const auto predicate = [](const VAConfigAttrib & a) {
266 return a.value == VA_ATTRIB_NOT_SUPPORTED;
267 };
268 attribs.erase(std::remove_if(begin, end, predicate), end);
269 }
270 }
271
createConfig(const VAProfile & profile,const VAEntrypoint & entrypoint,const ConfigAttributes & attribs,const VAStatus & expectation)272 void VAAPIFixture::createConfig(const VAProfile& profile,
273 const VAEntrypoint& entrypoint, const ConfigAttributes& attribs,
274 const VAStatus& expectation)
275 {
276 ASSERT_INVALID_ID(m_configID)
277 << "test logic error: did you forget to call destroyConfig?";
278
279 EXPECT_STATUS_EQ(
280 expectation,
281 vaCreateConfig(m_vaDisplay, profile, entrypoint,
282 (attribs.size() != 0 ?
283 const_cast<VAConfigAttrib*>(attribs.data()) : NULL),
284 attribs.size(), &m_configID))
285 << "profile = " << profile << std::endl
286 << "entrypoint = " << entrypoint << std::endl
287 << "numAttribs = " << attribs.size();
288
289 if (expectation == VA_STATUS_SUCCESS) {
290 EXPECT_ID(m_configID);
291 } else {
292 EXPECT_INVALID_ID(m_configID);
293 }
294 }
295
queryConfigAttributes(const VAProfile & expectedProfile,const VAEntrypoint & expectedEntrypoint,ConfigAttributes & attributes,const VAStatus & expectedStatus) const296 void VAAPIFixture::queryConfigAttributes(
297 const VAProfile& expectedProfile, const VAEntrypoint& expectedEntrypoint,
298 ConfigAttributes& attributes, const VAStatus& expectedStatus) const
299 {
300 VAProfile actualProfile;
301 VAEntrypoint actualEntrypoint;
302 int numAttributes(0);
303
304 ASSERT_TRUE(attributes.empty())
305 << "test logic error: attributes must be empty";
306
307 const int maxAttributes = vaMaxNumConfigAttributes(m_vaDisplay);
308
309 ASSERT_GT(maxAttributes, 0);
310
311 attributes.resize(maxAttributes);
312
313 EXPECT_STATUS_EQ(
314 expectedStatus,
315 vaQueryConfigAttributes(m_vaDisplay, m_configID, &actualProfile,
316 &actualEntrypoint, attributes.data(), &numAttributes));
317
318 if (expectedStatus == VA_STATUS_SUCCESS) {
319 EXPECT_EQ(expectedProfile, actualProfile);
320 EXPECT_EQ(expectedEntrypoint, actualEntrypoint);
321 ASSERT_LE(numAttributes, maxAttributes);
322 ASSERT_GT(numAttributes, 0);
323
324 attributes.resize(numAttributes);
325
326 // reported config attributes should be supported
327 for (const auto& attribute : attributes) {
328 EXPECT_NE(VA_ATTRIB_NOT_SUPPORTED, attribute.value);
329 }
330 } else {
331 attributes.clear();
332 }
333 }
334
destroyConfig(const VAStatus & expectation)335 void VAAPIFixture::destroyConfig(const VAStatus& expectation)
336 {
337 EXPECT_STATUS_EQ(expectation, vaDestroyConfig(m_vaDisplay, m_configID));
338 m_configID = VA_INVALID_ID;
339 }
340
querySurfaceAttributes(SurfaceAttributes & attribs) const341 void VAAPIFixture::querySurfaceAttributes(SurfaceAttributes& attribs) const
342 {
343 ASSERT_TRUE(attribs.empty())
344 << "test logic error: surface attributes must be empty";
345
346 unsigned numAttribs(0);
347
348 ASSERT_STATUS(vaQuerySurfaceAttributes(m_vaDisplay, m_configID, NULL,
349 &numAttribs));
350
351 ASSERT_GT(numAttribs, 0u);
352
353 attribs.resize(numAttribs);
354
355 ASSERT_STATUS(vaQuerySurfaceAttributes(m_vaDisplay, m_configID,
356 attribs.data(), &numAttribs));
357
358 ASSERT_GT(numAttribs, 0u);
359 EXPECT_GE(attribs.size(), numAttribs);
360
361 attribs.resize(numAttribs);
362
363 const uint32_t flags = 0x0 | VA_SURFACE_ATTRIB_GETTABLE
364 | VA_SURFACE_ATTRIB_SETTABLE;
365
366 for (const auto& attrib : attribs) {
367 EXPECT_NE(attrib.flags & flags,
368 (uint32_t)VA_SURFACE_ATTRIB_NOT_SUPPORTED);
369 EXPECT_GE(attrib.value.type, VAGenericValueTypeInteger);
370 EXPECT_LE(attrib.value.type, VAGenericValueTypeFunc);
371 }
372 }
373
getMinMaxSurfaceResolution(Resolution & minRes,Resolution & maxRes) const374 void VAAPIFixture::getMinMaxSurfaceResolution(
375 Resolution& minRes, Resolution& maxRes) const
376 {
377 const Resolution::DataType maxVal =
378 std::numeric_limits<Resolution::DataType>::max();
379
380 // set default resolutions
381 minRes.width = 1;
382 minRes.height = 1;
383 maxRes.width = maxVal;
384 maxRes.height = maxVal;
385
386 SurfaceAttributes attribs;
387 querySurfaceAttributes(attribs);
388
389 SurfaceAttributes::const_iterator match;
390 const SurfaceAttributes::const_iterator begin(attribs.begin());
391 const SurfaceAttributes::const_iterator end(attribs.end());
392
393 // minimum surface width
394 match = std::find_if(begin, end, [](const VASurfaceAttrib & a) {
395 return a.type == VASurfaceAttribMinWidth;
396 });
397 if (match != end) {
398 EXPECT_EQ(VAGenericValueTypeInteger, match->value.type);
399 ASSERT_GE(match->value.value.i, 1);
400 ASSERT_LE((Resolution::DataType)match->value.value.i, maxVal);
401 minRes.width = match->value.value.i;
402 }
403
404 // minimum surface height
405 match = std::find_if(begin, end, [](const VASurfaceAttrib & a) {
406 return a.type == VASurfaceAttribMinHeight;
407 });
408 if (match != end) {
409 EXPECT_EQ(VAGenericValueTypeInteger, match->value.type);
410 ASSERT_GE(match->value.value.i, 1);
411 ASSERT_LE((Resolution::DataType)match->value.value.i, maxVal);
412 minRes.height = match->value.value.i;
413 }
414
415 // maximum surface width
416 match = std::find_if(begin, end, [](const VASurfaceAttrib & a) {
417 return a.type == VASurfaceAttribMaxWidth;
418 });
419 if (match != end) {
420 EXPECT_EQ(VAGenericValueTypeInteger, match->value.type);
421 ASSERT_GE(match->value.value.i, 1);
422 ASSERT_LE((Resolution::DataType)match->value.value.i, maxVal);
423 maxRes.width = match->value.value.i;
424 }
425
426 // maximum surface height
427 match = std::find_if(begin, end, [](const VASurfaceAttrib & a) {
428 return a.type == VASurfaceAttribMaxHeight;
429 });
430 if (match != end) {
431 EXPECT_EQ(VAGenericValueTypeInteger, match->value.type);
432 ASSERT_GE(match->value.value.i, 1);
433 ASSERT_LE((Resolution::DataType)match->value.value.i, maxVal);
434 maxRes.height = match->value.value.i;
435 }
436
437 EXPECT_LE(minRes, maxRes);
438 }
439
createSurfaces(Surfaces & surfaces,const unsigned format,const Resolution & resolution,const SurfaceAttributes & attribs,const VAStatus & expectation) const440 void VAAPIFixture::createSurfaces(Surfaces& surfaces, const unsigned format,
441 const Resolution& resolution, const SurfaceAttributes& attribs,
442 const VAStatus& expectation) const
443 {
444 ASSERT_GT(surfaces.size(), 0u)
445 << "test logic error: surfaces must not be emtpy";
446 for (const auto& surface : surfaces) {
447 ASSERT_INVALID_ID(surface)
448 << "test logic error: surfaces must all be VA_INVALID_SURFACE";
449 }
450
451 ASSERT_STATUS_EQ(
452 expectation,
453 vaCreateSurfaces(m_vaDisplay, format, resolution.width,
454 resolution.height, surfaces.data(), surfaces.size(),
455 (attribs.size() != 0 ?
456 const_cast<VASurfaceAttrib*>(attribs.data()) : NULL),
457 attribs.size()));
458
459 if (expectation == VA_STATUS_SUCCESS) {
460 for (const auto& surface : surfaces) {
461 ASSERT_ID(surface);
462 }
463 }
464 }
465
destroySurfaces(Surfaces & surfaces) const466 void VAAPIFixture::destroySurfaces(Surfaces& surfaces) const
467 {
468 if (surfaces.size() != 0) {
469 EXPECT_STATUS(vaDestroySurfaces(m_vaDisplay, surfaces.data(),
470 surfaces.size()));
471 }
472 }
473
createBuffer(const VABufferType & bufferType,const size_t bufferSize,const VAStatus & expectation)474 void VAAPIFixture::createBuffer(const VABufferType& bufferType,
475 const size_t bufferSize, const VAStatus& expectation)
476 {
477 ASSERT_INVALID_ID(m_bufferID)
478 << "test logic error: did you forget to call destroyBuffer?";
479
480 EXPECT_STATUS_EQ(
481 expectation,
482 vaCreateBuffer(m_vaDisplay, m_contextID, bufferType, bufferSize,
483 1, NULL, &m_bufferID));
484 }
485
destroyBuffer(const VAStatus & expectation)486 void VAAPIFixture::destroyBuffer(const VAStatus& expectation)
487 {
488 EXPECT_STATUS_EQ(expectation, vaDestroyBuffer(m_vaDisplay, m_bufferID));
489 m_bufferID = VA_INVALID_ID;
490 }
491
doCreateContext(const Resolution & resolution,const VAStatus & expectation)492 void VAAPIFixture::doCreateContext(const Resolution& resolution,
493 const VAStatus& expectation)
494 {
495 m_contextID = 0;
496 ASSERT_STATUS_EQ(expectation,
497 vaCreateContext(m_vaDisplay, m_configID, resolution.width,
498 resolution.height, VA_PROGRESSIVE,
499 NULL, 0, &m_contextID));
500 }
501
doDestroyContext(const VAStatus & expectation)502 void VAAPIFixture::doDestroyContext(const VAStatus& expectation)
503 {
504 ASSERT_STATUS_EQ(expectation, vaDestroyContext(m_vaDisplay, m_contextID));
505 }
506
doTerminate()507 void VAAPIFixture::doTerminate()
508 {
509 EXPECT_STATUS(vaTerminate(m_vaDisplay));
510 }
511
skipTest(const std::string & message)512 void VAAPIFixture::skipTest(const std::string& message)
513 {
514 ASSERT_FALSE(message.empty())
515 << "test logic error: skip message cannot be empty";
516 ASSERT_TRUE(m_skip.empty())
517 << "test logic error: test already marked as skipped";
518
519 m_skip = message;
520 }
521
skipTest(const VAProfile & profile,const VAEntrypoint & entrypoint)522 void VAAPIFixture::skipTest(const VAProfile& profile,
523 const VAEntrypoint& entrypoint)
524 {
525 std::ostringstream oss;
526 oss << profile << " / " << entrypoint << " not supported on this hardware";
527 skipTest(oss.str());
528 }
529
TEST_F(VAAPIFixture,getDisplay)530 TEST_F(VAAPIFixture, getDisplay)
531 {
532 VADisplay vaDisplay;
533
534 vaDisplay = getDisplay();
535 ASSERT_TRUE(vaDisplay);
536 EXPECT_STATUS(vaTerminate(vaDisplay));
537 }
538
VAAPIFixtureSharedDisplay()539 VAAPIFixtureSharedDisplay::VAAPIFixtureSharedDisplay() : VAAPIFixture() { }
540
SetUpTestSuite()541 void VAAPIFixtureSharedDisplay::SetUpTestSuite()
542 {
543 if (s_drmHandle < 0) {
544 #if defined(_WIN32)
545 s_vaDisplay = getWin32Display(NULL);
546 #else
547 s_vaDisplay = getDrmDisplay(s_drmHandle);
548 #endif
549 int majorVersion, minorVersion;
550 s_initStatus = vaInitialize(s_vaDisplay, &majorVersion, &minorVersion);
551 }
552 }
553
TearDownTestSuite()554 void VAAPIFixtureSharedDisplay::TearDownTestSuite()
555 {
556 if (s_vaDisplay) {
557 if (s_initStatus == VA_STATUS_SUCCESS) {
558 vaTerminate(s_vaDisplay);
559 s_vaDisplay = nullptr;
560 } else {
561 s_initStatus = VA_STATUS_SUCCESS;
562 }
563 }
564 if (s_drmHandle >= 0) {
565 close(s_drmHandle);
566 s_drmHandle = -1;
567 }
568 }
569
SetUp()570 void VAAPIFixtureSharedDisplay::SetUp()
571 {
572 EXPECT_STATUS(s_initStatus) << "failed to initialize vaapi";
573
574 m_vaDisplay = s_vaDisplay;
575 }
576
577 } // namespace VAAPI
578