• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2023 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <gmock/gmock.h>
16 #include <gtest/gtest.h>
17 
18 #include "GfxstreamEnd2EndTests.h"
19 
20 #include <dlfcn.h>
21 #include <log/log.h>
22 
23 #include <filesystem>
24 
25 #include "ProcessPipe.h"
26 #include "RutabagaLayer.h"
27 #include "aemu/base/Path.h"
28 #include "gfxstream/ImageUtils.h"
29 #include "gfxstream/RutabagaLayerTestUtils.h"
30 #include "gfxstream/Strings.h"
31 
32 VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
33 
34 namespace gfxstream {
35 namespace tests {
36 namespace {
37 
38 using testing::AnyOf;
39 using testing::Eq;
40 using testing::Gt;
41 using testing::IsFalse;
42 using testing::IsTrue;
43 using testing::Not;
44 using testing::NotNull;
45 
GetTestDataPath(const std::string & basename)46 std::string GetTestDataPath(const std::string& basename) {
47     const std::filesystem::path testBinaryDirectory = gfxstream::guest::getProgramDirectory();
48     return (testBinaryDirectory / "testdata" / basename).string();
49 }
50 
51 }  // namespace
52 
GfxstreamTransportToEnvVar(GfxstreamTransport transport)53 std::string GfxstreamTransportToEnvVar(GfxstreamTransport transport) {
54     switch (transport) {
55         case GfxstreamTransport::kVirtioGpuAsg: {
56             return "virtio-gpu-asg";
57         }
58         case GfxstreamTransport::kVirtioGpuPipe: {
59             return "virtio-gpu-pipe";
60         }
61     }
62 }
63 
GfxstreamTransportToString(GfxstreamTransport transport)64 std::string GfxstreamTransportToString(GfxstreamTransport transport) {
65     switch (transport) {
66         case GfxstreamTransport::kVirtioGpuAsg: {
67             return "VirtioGpuAsg";
68         }
69         case GfxstreamTransport::kVirtioGpuPipe: {
70             return "VirtioGpuPipe";
71         }
72     }
73 }
74 
ToString() const75 std::string TestParams::ToString() const {
76     std::string ret;
77     ret += (with_gl ? "With" : "Without");
78     ret += "Gl";
79     ret += (with_vk ? "With" : "Without");
80     ret += "Vk";
81     ret += "SampleCount" + std::to_string(samples);
82     if (!with_features.empty()) {
83         ret += "WithFeatures_";
84         ret += Join(with_features, "_");
85         ret += "_";
86     }
87     ret += "Over";
88     ret += GfxstreamTransportToString(with_transport);
89     return ret;
90 }
91 
operator <<(std::ostream & os,const TestParams & params)92 std::ostream& operator<<(std::ostream& os, const TestParams& params) {
93     return os << params.ToString();
94 }
95 
GetTestName(const::testing::TestParamInfo<TestParams> & info)96 std::string GetTestName(const ::testing::TestParamInfo<TestParams>& info) {
97     return info.param.ToString();
98 }
99 
WithAndWithoutFeatures(const std::vector<TestParams> & params,const std::vector<std::string> & features)100 std::vector<TestParams> WithAndWithoutFeatures(const std::vector<TestParams>& params,
101                                                const std::vector<std::string>& features) {
102     std::vector<TestParams> output;
103     output.reserve(params.size() * 2);
104 
105     // Copy of all of the existing test params:
106     output.insert(output.end(), params.begin(), params.end());
107 
108     // Copy of all of the existing test params with the new features:
109     for (TestParams copy : params) {
110         copy.with_features.insert(features.begin(), features.end());
111         output.push_back(copy);
112     }
113 
114     return output;
115 }
116 
SetupGuestGl()117 std::unique_ptr<GuestGlDispatchTable> GfxstreamEnd2EndTest::SetupGuestGl() {
118     const std::filesystem::path testDirectory = gfxstream::guest::getProgramDirectory();
119     const std::string eglLibPath = (testDirectory / "libEGL_emulation_with_host.so").string();
120     const std::string gles2LibPath = (testDirectory / "libGLESv2_emulation_with_host.so").string();
121 
122     void* eglLib = dlopen(eglLibPath.c_str(), RTLD_NOW | RTLD_LOCAL);
123     if (!eglLib) {
124         ALOGE("Failed to load Gfxstream EGL library from %s.", eglLibPath.c_str());
125         return nullptr;
126     }
127 
128     void* gles2Lib = dlopen(gles2LibPath.c_str(), RTLD_NOW | RTLD_LOCAL);
129     if (!gles2Lib) {
130         ALOGE("Failed to load Gfxstream GLES2 library from %s.", gles2LibPath.c_str());
131         return nullptr;
132     }
133 
134     using GenericFnType = void*(void);
135     using GetProcAddrType = GenericFnType*(const char*);
136 
137     auto eglGetAddr = reinterpret_cast<GetProcAddrType*>(dlsym(eglLib, "eglGetProcAddress"));
138     if (!eglGetAddr) {
139         ALOGE("Failed to load Gfxstream EGL library from %s.", eglLibPath.c_str());
140         return nullptr;
141     }
142 
143     auto gl = std::make_unique<GuestGlDispatchTable>();
144 
145     #define LOAD_EGL_FUNCTION(return_type, function_name, signature) \
146         gl-> function_name = reinterpret_cast< return_type (*) signature >(eglGetAddr( #function_name ));
147 
148     LIST_RENDER_EGL_FUNCTIONS(LOAD_EGL_FUNCTION)
149     LIST_RENDER_EGL_EXTENSIONS_FUNCTIONS(LOAD_EGL_FUNCTION)
150 
151 #define LOAD_GLES2_FUNCTION(return_type, function_name, signature, callargs)         \
152     gl->function_name =                                                              \
153         reinterpret_cast<return_type(*) signature>(dlsym(gles2Lib, #function_name)); \
154     if (!gl->function_name) {                                                        \
155         gl->function_name =                                                          \
156             reinterpret_cast<return_type(*) signature>(eglGetAddr(#function_name));  \
157     }
158 
159     LIST_GLES_FUNCTIONS(LOAD_GLES2_FUNCTION, LOAD_GLES2_FUNCTION)
160 
161     return gl;
162 }
163 
SetupGuestRc()164 std::unique_ptr<GuestRenderControlDispatchTable> GfxstreamEnd2EndTest::SetupGuestRc() {
165     const std::filesystem::path testDirectory = gfxstream::guest::getProgramDirectory();
166     const std::string rcLibPath =
167         (testDirectory / "libgfxstream_guest_rendercontrol_with_host.so").string();
168 
169     void* rcLib = dlopen(rcLibPath.c_str(), RTLD_NOW | RTLD_LOCAL);
170     if (!rcLib) {
171         ALOGE("Failed to load Gfxstream RenderControl library from %s.", rcLibPath.c_str());
172         return nullptr;
173     }
174 
175     auto rc = std::make_unique<GuestRenderControlDispatchTable>();
176 
177 #define LOAD_RENDERCONTROL_FUNCTION(name)                         \
178     rc->name = reinterpret_cast<PFN_##name>(dlsym(rcLib, #name)); \
179     if (rc->name == nullptr) {                                    \
180         ALOGE("Failed to load RenderControl function %s", #name); \
181         return nullptr;                                           \
182     }
183 
184     LOAD_RENDERCONTROL_FUNCTION(rcCreateDevice);
185     LOAD_RENDERCONTROL_FUNCTION(rcDestroyDevice);
186     LOAD_RENDERCONTROL_FUNCTION(rcCompose);
187 
188     return rc;
189 }
190 
SetupGuestVk()191 std::unique_ptr<vkhpp::DynamicLoader> GfxstreamEnd2EndTest::SetupGuestVk() {
192     const std::filesystem::path testDirectory = gfxstream::guest::getProgramDirectory();
193     const std::string vkLibPath = (testDirectory / "libgfxstream_guest_vulkan_with_host.so").string();
194 
195     auto dl = std::make_unique<vkhpp::DynamicLoader>(vkLibPath);
196     if (!dl->success()) {
197         ALOGE("Failed to load Vulkan from: %s", vkLibPath.c_str());
198         return nullptr;
199     }
200 
201     auto getInstanceProcAddr = dl->getProcAddress<PFN_vkGetInstanceProcAddr>("vk_icdGetInstanceProcAddr");
202     if (!getInstanceProcAddr) {
203         ALOGE("Failed to load Vulkan vkGetInstanceProcAddr. %s", dlerror());
204         return nullptr;
205     }
206 
207     VULKAN_HPP_DEFAULT_DISPATCHER.init(getInstanceProcAddr);
208 
209     return dl;
210 }
211 
SetUp()212 void GfxstreamEnd2EndTest::SetUp() {
213     const TestParams params = GetParam();
214 
215     const std::string transportValue = GfxstreamTransportToEnvVar(params.with_transport);
216     ASSERT_THAT(setenv("GFXSTREAM_TRANSPORT", transportValue.c_str(), /*overwrite=*/1), Eq(0));
217 
218     ASSERT_THAT(setenv("GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_GL",
219                        params.with_gl ? "Y" : "N", /*overwrite=*/1), Eq(0));
220     ASSERT_THAT(setenv("GFXSTREAM_EMULATED_VIRTIO_GPU_WITH_VK", params.with_vk ? "Y" : "N",
221                        /*overwrite=*/1),
222                 Eq(0));
223 
224     std::vector<std::string> featureEnables;
225     for (const std::string& feature : params.with_features) {
226         featureEnables.push_back(feature + ":enabled");
227     }
228     const std::string features = Join(featureEnables, ",");
229     ASSERT_THAT(setenv("GFXSTREAM_EMULATED_VIRTIO_GPU_RENDERER_FEATURES", features.c_str(),
230                         /*overwrite=*/1),
231                 Eq(0));
232 
233 
234     if (params.with_gl) {
235         mGl = SetupGuestGl();
236         ASSERT_THAT(mGl, NotNull());
237     }
238     if (params.with_vk) {
239         mVk = SetupGuestVk();
240         ASSERT_THAT(mVk, NotNull());
241     }
242 
243     mRc = SetupGuestRc();
244     ASSERT_THAT(mRc, NotNull());
245 
246     mAnwHelper.reset(createPlatformANativeWindowHelper());
247     mGralloc.reset(createPlatformGralloc());
248     mSync.reset(createPlatformSyncHelper());
249 }
250 
TearDownGuest()251 void GfxstreamEnd2EndTest::TearDownGuest() {
252     if (mGl) {
253         EGLDisplay display = mGl->eglGetCurrentDisplay();
254         if (display != EGL_NO_DISPLAY) {
255             mGl->eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
256             mGl->eglTerminate(display);
257         }
258         mGl->eglReleaseThread();
259         mGl.reset();
260     }
261     mVk.reset();
262     mRc.reset();
263 
264     mAnwHelper.reset();
265     mGralloc.reset();
266     mSync.reset();
267 
268     processPipeRestart();
269 }
270 
TearDownHost()271 void GfxstreamEnd2EndTest::TearDownHost() {
272     const uint32_t users = GetNumActiveEmulatedVirtioGpuUsers();
273     if (users != 0) {
274         ALOGE("The EmulationVirtioGpu was found to still be active by %" PRIu32
275               " after the "
276               "end of the test. Please ensure you have fully destroyed all objects created "
277               "during the test (Gralloc allocations, ANW allocations, etc).",
278               users);
279         abort();
280     }
281 }
282 
TearDown()283 void GfxstreamEnd2EndTest::TearDown() {
284     TearDownGuest();
285     TearDownHost();
286 }
287 
SetUpEglContextAndSurface(uint32_t contextVersion,uint32_t width,uint32_t height,EGLDisplay * outDisplay,EGLContext * outContext,EGLSurface * outSurface)288 void GfxstreamEnd2EndTest::SetUpEglContextAndSurface(
289         uint32_t contextVersion,
290         uint32_t width,
291         uint32_t height,
292         EGLDisplay* outDisplay,
293         EGLContext* outContext,
294         EGLSurface* outSurface) {
295     ASSERT_THAT(contextVersion, AnyOf(Eq(2), Eq(3)))
296         << "Invalid context version requested.";
297 
298     EGLDisplay display = mGl->eglGetDisplay(EGL_DEFAULT_DISPLAY);
299     ASSERT_THAT(display, Not(Eq(EGL_NO_DISPLAY)));
300 
301     int versionMajor = 0;
302     int versionMinor = 0;
303     ASSERT_THAT(mGl->eglInitialize(display, &versionMajor, &versionMinor), IsTrue());
304 
305     ASSERT_THAT(mGl->eglBindAPI(EGL_OPENGL_ES_API), IsTrue());
306 
307     // clang-format off
308     static const EGLint configAttributes[] = {
309         EGL_SURFACE_TYPE,    EGL_PBUFFER_BIT,
310         EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
311         EGL_NONE,
312     };
313     // clang-format on
314 
315     int numConfigs = 0;
316     ASSERT_THAT(mGl->eglChooseConfig(display, configAttributes, nullptr, 1, &numConfigs), IsTrue());
317     ASSERT_THAT(numConfigs, Gt(0));
318 
319     EGLConfig config = nullptr;
320     ASSERT_THAT(mGl->eglChooseConfig(display, configAttributes, &config, 1, &numConfigs), IsTrue());
321     ASSERT_THAT(config, Not(Eq(nullptr)));
322 
323     // clang-format off
324     static const EGLint surfaceAttributes[] = {
325         EGL_WIDTH,  static_cast<EGLint>(width),
326         EGL_HEIGHT, static_cast<EGLint>(height),
327         EGL_NONE,
328     };
329     // clang-format on
330 
331     EGLSurface surface = mGl->eglCreatePbufferSurface(display, config, surfaceAttributes);
332     ASSERT_THAT(surface, Not(Eq(EGL_NO_SURFACE)));
333 
334     // clang-format off
335     static const EGLint contextAttribs[] = {
336         EGL_CONTEXT_CLIENT_VERSION, static_cast<EGLint>(contextVersion),
337         EGL_NONE,
338     };
339     // clang-format on
340 
341     EGLContext context = mGl->eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
342     ASSERT_THAT(context, Not(Eq(EGL_NO_CONTEXT)));
343 
344     ASSERT_THAT(mGl->eglMakeCurrent(display, surface, surface, context), IsTrue());
345 
346     *outDisplay = display;
347     *outContext = context;
348     *outSurface = surface;
349 }
350 
TearDownEglContextAndSurface(EGLDisplay display,EGLContext context,EGLSurface surface)351 void GfxstreamEnd2EndTest::TearDownEglContextAndSurface(
352         EGLDisplay display,
353         EGLContext context,
354         EGLSurface surface) {
355     ASSERT_THAT(mGl->eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT), IsTrue());
356     ASSERT_THAT(mGl->eglDestroyContext(display, context), IsTrue());
357     ASSERT_THAT(mGl->eglDestroySurface(display, surface), IsTrue());
358 }
359 
MakeShader(GlDispatch & dispatch,GLenum type,const std::string & source)360 GlExpected<ScopedGlShader> ScopedGlShader::MakeShader(GlDispatch& dispatch, GLenum type,
361                                                       const std::string& source) {
362     GLuint shader = dispatch.glCreateShader(type);
363     if (!shader) {
364         return android::base::unexpected("Failed to create shader.");
365     }
366 
367     const GLchar* sourceTyped = (const GLchar*)source.c_str();
368     const GLint sourceLength = source.size();
369     dispatch.glShaderSource(shader, 1, &sourceTyped, &sourceLength);
370     dispatch.glCompileShader(shader);
371 
372     GLint compileStatus;
373     dispatch.glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus);
374 
375     if (compileStatus != GL_TRUE) {
376         GLint errorLogLength = 0;
377         dispatch.glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &errorLogLength);
378         if (!errorLogLength) {
379             errorLogLength = 512;
380         }
381 
382         std::vector<GLchar> errorLog(errorLogLength);
383         dispatch.glGetShaderInfoLog(shader, errorLogLength, &errorLogLength, errorLog.data());
384 
385         const std::string errorString = errorLogLength == 0 ? "" : errorLog.data();
386         ALOGE("Shader compilation failed with: \"%s\"", errorString.c_str());
387 
388         dispatch.glDeleteShader(shader);
389         return android::base::unexpected(errorString);
390     }
391 
392     return ScopedGlShader(dispatch, shader);
393 }
394 
MakeProgram(GlDispatch & dispatch,const std::string & vertSource,const std::string & fragSource)395 GlExpected<ScopedGlProgram> ScopedGlProgram::MakeProgram(GlDispatch& dispatch,
396                                                          const std::string& vertSource,
397                                                          const std::string& fragSource) {
398     auto vertShader = GL_EXPECT(ScopedGlShader::MakeShader(dispatch, GL_VERTEX_SHADER, vertSource));
399     auto fragShader =
400         GL_EXPECT(ScopedGlShader::MakeShader(dispatch, GL_FRAGMENT_SHADER, fragSource));
401 
402     GLuint program = dispatch.glCreateProgram();
403     dispatch.glAttachShader(program, vertShader);
404     dispatch.glAttachShader(program, fragShader);
405     dispatch.glLinkProgram(program);
406 
407     GLint linkStatus;
408     dispatch.glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
409     if (linkStatus != GL_TRUE) {
410         GLint errorLogLength = 0;
411         dispatch.glGetProgramiv(program, GL_INFO_LOG_LENGTH, &errorLogLength);
412         if (!errorLogLength) {
413             errorLogLength = 512;
414         }
415 
416         std::vector<char> errorLog(errorLogLength, 0);
417         dispatch.glGetProgramInfoLog(program, errorLogLength, nullptr, errorLog.data());
418 
419         const std::string errorString = errorLogLength == 0 ? "" : errorLog.data();
420         ALOGE("Program link failed with: \"%s\"", errorString.c_str());
421 
422         dispatch.glDeleteProgram(program);
423         return android::base::unexpected(errorString);
424     }
425 
426     return ScopedGlProgram(dispatch, program);
427 }
428 
MakeProgram(GlDispatch & dispatch,GLenum programBinaryFormat,const std::vector<uint8_t> & programBinaryData)429 GlExpected<ScopedGlProgram> ScopedGlProgram::MakeProgram(
430     GlDispatch& dispatch, GLenum programBinaryFormat,
431     const std::vector<uint8_t>& programBinaryData) {
432     GLuint program = dispatch.glCreateProgram();
433     dispatch.glProgramBinary(program, programBinaryFormat, programBinaryData.data(),
434                              programBinaryData.size());
435 
436     GLint linkStatus;
437     dispatch.glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
438     if (linkStatus != GL_TRUE) {
439         GLint errorLogLength = 0;
440         dispatch.glGetProgramiv(program, GL_INFO_LOG_LENGTH, &errorLogLength);
441         if (!errorLogLength) {
442             errorLogLength = 512;
443         }
444 
445         std::vector<char> errorLog(errorLogLength, 0);
446         dispatch.glGetProgramInfoLog(program, errorLogLength, nullptr, errorLog.data());
447 
448         const std::string errorString = errorLogLength == 0 ? "" : errorLog.data();
449         ALOGE("Program link failed with: \"%s\"", errorString.c_str());
450 
451         dispatch.glDeleteProgram(program);
452         return android::base::unexpected(errorString);
453     }
454 
455     return ScopedGlProgram(dispatch, program);
456 }
457 
Allocate(Gralloc & gralloc,uint32_t width,uint32_t height,uint32_t format)458 GlExpected<ScopedAHardwareBuffer> ScopedAHardwareBuffer::Allocate(Gralloc& gralloc, uint32_t width,
459                                                                   uint32_t height,
460                                                                   uint32_t format) {
461     AHardwareBuffer* ahb = nullptr;
462     int status = gralloc.allocate(width, height, format, -1, &ahb);
463     if (status != 0) {
464         return android::base::unexpected(std::string("Failed to allocate AHB with width:") +
465                                          std::to_string(width) + std::string(" height:") +
466                                          std::to_string(height) + std::string(" format:") +
467                                          std::to_string(format));
468     }
469 
470     return ScopedAHardwareBuffer(gralloc, ahb);
471 }
472 
SetUpShader(GLenum type,const std::string & source)473 GlExpected<ScopedGlShader> GfxstreamEnd2EndTest::SetUpShader(GLenum type,
474                                                              const std::string& source) {
475     if (!mGl) {
476         return android::base::unexpected("Gl not enabled for this test.");
477     }
478 
479     return ScopedGlShader::MakeShader(*mGl, type, source);
480 }
481 
SetUpProgram(const std::string & vertSource,const std::string & fragSource)482 GlExpected<ScopedGlProgram> GfxstreamEnd2EndTest::SetUpProgram(const std::string& vertSource,
483                                                                const std::string& fragSource) {
484     if (!mGl) {
485         return android::base::unexpected("Gl not enabled for this test.");
486     }
487 
488     return ScopedGlProgram::MakeProgram(*mGl, vertSource, fragSource);
489 }
490 
SetUpProgram(GLenum programBinaryFormat,const std::vector<uint8_t> & programBinaryData)491 GlExpected<ScopedGlProgram> GfxstreamEnd2EndTest::SetUpProgram(
492     GLenum programBinaryFormat, const std::vector<uint8_t>& programBinaryData) {
493     if (!mGl) {
494         return android::base::unexpected("Gl not enabled for this test.");
495     }
496 
497     return ScopedGlProgram::MakeProgram(*mGl, programBinaryFormat, programBinaryData);
498 }
499 
500 VkExpected<GfxstreamEnd2EndTest::TypicalVkTestEnvironment>
SetUpTypicalVkTestEnvironment(const TypicalVkTestEnvironmentOptions & opts)501 GfxstreamEnd2EndTest::SetUpTypicalVkTestEnvironment(const TypicalVkTestEnvironmentOptions& opts) {
502     const auto availableInstanceLayers = vkhpp::enumerateInstanceLayerProperties().value;
503     ALOGV("Available instance layers:");
504     for (const vkhpp::LayerProperties& layer : availableInstanceLayers) {
505         ALOGV(" - %s", layer.layerName.data());
506     }
507 
508     constexpr const bool kEnableValidationLayers = true;
509 
510     std::vector<const char*> requestedInstanceExtensions;
511     std::vector<const char*> requestedInstanceLayers;
512     if (kEnableValidationLayers) {
513         requestedInstanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
514     }
515 
516     const vkhpp::ApplicationInfo applicationInfo{
517         .pApplicationName = ::testing::UnitTest::GetInstance()->current_test_info()->name(),
518         .applicationVersion = 1,
519         .pEngineName = "Gfxstream Testing Engine",
520         .engineVersion = 1,
521         .apiVersion = opts.apiVersion,
522     };
523     const vkhpp::InstanceCreateInfo instanceCreateInfo{
524         .pNext = opts.instanceCreateInfoPNext ? *opts.instanceCreateInfoPNext : nullptr,
525         .pApplicationInfo = &applicationInfo,
526         .enabledLayerCount = static_cast<uint32_t>(requestedInstanceLayers.size()),
527         .ppEnabledLayerNames = requestedInstanceLayers.data(),
528         .enabledExtensionCount = static_cast<uint32_t>(requestedInstanceExtensions.size()),
529         .ppEnabledExtensionNames = requestedInstanceExtensions.data(),
530     };
531 
532     auto instance = VK_EXPECT_RV(vkhpp::createInstanceUnique(instanceCreateInfo));
533 
534     VULKAN_HPP_DEFAULT_DISPATCHER.init(*instance);
535 
536     auto physicalDevices = VK_EXPECT_RV(instance->enumeratePhysicalDevices());
537     ALOGV("Available physical devices:");
538     for (const auto& physicalDevice : physicalDevices) {
539         const auto physicalDeviceProps = physicalDevice.getProperties();
540         ALOGV(" - %s", physicalDeviceProps.deviceName.data());
541     }
542 
543     if (physicalDevices.empty()) {
544         ALOGE("No physical devices available?");
545         return android::base::unexpected(vkhpp::Result::eErrorUnknown);
546     }
547 
548     auto physicalDevice = std::move(physicalDevices[0]);
549     {
550         const auto physicalDeviceProps = physicalDevice.getProperties();
551         ALOGV("Selected physical device: %s", physicalDeviceProps.deviceName.data());
552     }
553     {
554         const auto exts = VK_EXPECT_RV(physicalDevice.enumerateDeviceExtensionProperties());
555         ALOGV("Available physical device extensions:");
556         for (const auto& ext : exts) {
557             ALOGV(" - %s", ext.extensionName.data());
558         }
559     }
560 
561     uint32_t graphicsQueueFamilyIndex = -1;
562     {
563         const auto props = physicalDevice.getQueueFamilyProperties();
564         for (uint32_t i = 0; i < props.size(); i++) {
565             const auto& prop = props[i];
566             if (prop.queueFlags & vkhpp::QueueFlagBits::eGraphics) {
567                 graphicsQueueFamilyIndex = i;
568                 break;
569             }
570         }
571     }
572     if (graphicsQueueFamilyIndex == -1) {
573         ALOGE("Failed to find graphics queue.");
574         return android::base::unexpected(vkhpp::Result::eErrorUnknown);
575     }
576 
577     const float queuePriority = 1.0f;
578     const vkhpp::DeviceQueueCreateInfo deviceQueueCreateInfo = {
579         .queueFamilyIndex = graphicsQueueFamilyIndex,
580         .queueCount = 1,
581         .pQueuePriorities = &queuePriority,
582     };
583     std::vector<const char*> deviceExtensions = {
584         VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME,
585         VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
586     };
587     if (opts.deviceExtensions) {
588         for (const std::string& ext : *opts.deviceExtensions) {
589             deviceExtensions.push_back(ext.c_str());
590         }
591     }
592     const vkhpp::DeviceCreateInfo deviceCreateInfo = {
593         .pNext = opts.deviceCreateInfoPNext ? *opts.deviceCreateInfoPNext : nullptr,
594         .pQueueCreateInfos = &deviceQueueCreateInfo,
595         .queueCreateInfoCount = 1,
596         .enabledLayerCount = 0,
597         .ppEnabledLayerNames = nullptr,
598         .enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size()),
599         .ppEnabledExtensionNames = deviceExtensions.data(),
600     };
601     auto device = VK_EXPECT_RV(physicalDevice.createDeviceUnique(deviceCreateInfo));
602 
603     auto queue = device->getQueue(graphicsQueueFamilyIndex, 0);
604 
605     return TypicalVkTestEnvironment{
606         .instance = std::move(instance),
607         .physicalDevice = std::move(physicalDevice),
608         .device = std::move(device),
609         .queue = std::move(queue),
610         .queueFamilyIndex = graphicsQueueFamilyIndex,
611     };
612 }
613 
SnapshotSaveAndLoad()614 void GfxstreamEnd2EndTest::SnapshotSaveAndLoad() {
615     auto directory = testing::TempDir();
616 
617     std::shared_ptr<gfxstream::EmulatedVirtioGpu> emulation = gfxstream::EmulatedVirtioGpu::Get();
618 
619     emulation->SnapshotSave(directory);
620     emulation->SnapshotRestore(directory);
621 }
622 
LoadImage(const std::string & basename)623 GlExpected<Image> GfxstreamEnd2EndTest::LoadImage(const std::string& basename) {
624     const std::string filepath = GetTestDataPath(basename);
625     if (!std::filesystem::exists(filepath)) {
626         return android::base::unexpected("File " + filepath + " does not exist.");
627     }
628     if (!std::filesystem::is_regular_file(filepath)) {
629         return android::base::unexpected("File " + filepath + " is not a regular file.");
630     }
631 
632     Image image;
633 
634     uint32_t sourceWidth = 0;
635     uint32_t sourceHeight = 0;
636     std::vector<uint32_t> sourcePixels;
637     if (!LoadRGBAFromPng(filepath, &image.width, &image.height, &image.pixels)) {
638         return android::base::unexpected("Failed to load " + filepath + " as RGBA PNG.");
639     }
640 
641     return image;
642 }
643 
AsImage(ScopedAHardwareBuffer & ahb)644 GlExpected<Image> GfxstreamEnd2EndTest::AsImage(ScopedAHardwareBuffer& ahb) {
645     Image actual;
646     actual.width = ahb.GetWidth();
647     if (actual.width == 0) {
648         return android::base::unexpected("Failed to query AHB width.");
649     }
650     actual.height = ahb.GetHeight();
651     if (actual.height == 0) {
652         return android::base::unexpected("Failed to query AHB height.");
653     }
654     actual.pixels.resize(actual.width * actual.height);
655 
656     const uint32_t ahbFormat = ahb.GetAHBFormat();
657     if (ahbFormat != GFXSTREAM_AHB_FORMAT_R8G8B8A8_UNORM &&
658         ahbFormat != GFXSTREAM_AHB_FORMAT_B8G8R8A8_UNORM) {
659         return android::base::unexpected("Unhandled AHB format " + std::to_string(ahbFormat));
660     }
661 
662     {
663         uint8_t* ahbPixels = GL_EXPECT(ahb.Lock());
664         std::memcpy(actual.pixels.data(), ahbPixels, actual.pixels.size() * sizeof(uint32_t));
665         ahb.Unlock();
666     }
667 
668     if (ahbFormat == GFXSTREAM_AHB_FORMAT_B8G8R8A8_UNORM) {
669         for (uint32_t& pixel : actual.pixels) {
670             uint8_t* pixelComponents = reinterpret_cast<uint8_t*>(&pixel);
671             std::swap(pixelComponents[0], pixelComponents[2]);
672         }
673     }
674 
675     return actual;
676 }
677 
CreateAHBFromImage(const std::string & basename)678 GlExpected<ScopedAHardwareBuffer> GfxstreamEnd2EndTest::CreateAHBFromImage(
679     const std::string& basename) {
680     auto image = GL_EXPECT(LoadImage(basename));
681 
682     auto ahb = GL_EXPECT(
683         ScopedAHardwareBuffer::Allocate(*mGralloc, image.width, image.height, GFXSTREAM_AHB_FORMAT_R8G8B8A8_UNORM));
684 
685     {
686         uint8_t* ahbPixels = GL_EXPECT(ahb.Lock());
687         std::memcpy(ahbPixels, image.pixels.data(), image.pixels.size() * sizeof(uint32_t));
688         ahb.Unlock();
689     }
690 
691     return std::move(ahb);
692 }
693 
ArePixelsSimilar(uint32_t expectedPixel,uint32_t actualPixel)694 bool GfxstreamEnd2EndTest::ArePixelsSimilar(uint32_t expectedPixel, uint32_t actualPixel) {
695     const uint8_t* actualRGBA = reinterpret_cast<const uint8_t*>(&actualPixel);
696     const uint8_t* expectedRGBA = reinterpret_cast<const uint8_t*>(&expectedPixel);
697 
698     constexpr const uint32_t kRGBA8888Tolerance = 2;
699     for (uint32_t channel = 0; channel < 4; channel++) {
700         const uint8_t actualChannel = actualRGBA[channel];
701         const uint8_t expectedChannel = expectedRGBA[channel];
702 
703         if ((std::max(actualChannel, expectedChannel) - std::min(actualChannel, expectedChannel)) >
704             kRGBA8888Tolerance) {
705             return false;
706         }
707     }
708     return true;
709 }
710 
AreImagesSimilar(const Image & expected,const Image & actual)711 bool GfxstreamEnd2EndTest::AreImagesSimilar(const Image& expected, const Image& actual) {
712     if (actual.width != expected.width) {
713         ADD_FAILURE() << "Image comparison failed: " << "expected.width " << expected.width << "vs"
714                       << "actual.width " << actual.width;
715         return false;
716     }
717     if (actual.height != expected.height) {
718         ADD_FAILURE() << "Image comparison failed: " << "expected.height " << expected.height
719                       << "vs" << "actual.height " << actual.height;
720         return false;
721     }
722     const uint32_t width = actual.width;
723     const uint32_t height = actual.height;
724     const uint32_t* actualPixels = actual.pixels.data();
725     const uint32_t* expectedPixels = expected.pixels.data();
726 
727     bool imagesSimilar = true;
728 
729     uint32_t reportedIncorrectPixels = 0;
730     constexpr const uint32_t kMaxReportedIncorrectPixels = 5;
731 
732     for (uint32_t y = 0; y < height; y++) {
733         for (uint32_t x = 0; x < width; x++) {
734             const uint32_t actualPixel = actualPixels[y * height + x];
735             const uint32_t expectedPixel = expectedPixels[y * width + x];
736             if (!ArePixelsSimilar(expectedPixel, actualPixel)) {
737                 imagesSimilar = false;
738                 if (reportedIncorrectPixels < kMaxReportedIncorrectPixels) {
739                     reportedIncorrectPixels++;
740                     const uint8_t* actualRGBA = reinterpret_cast<const uint8_t*>(&actualPixel);
741                     const uint8_t* expectedRGBA = reinterpret_cast<const uint8_t*>(&expectedPixel);
742                     // clang-format off
743                     ADD_FAILURE()
744                         << "Pixel comparison failed at (" << x << ", " << y << ") "
745                         << " with actual "
746                         << " r:" << static_cast<int>(actualRGBA[0])
747                         << " g:" << static_cast<int>(actualRGBA[1])
748                         << " b:" << static_cast<int>(actualRGBA[2])
749                         << " a:" << static_cast<int>(actualRGBA[3])
750                         << " but expected "
751                         << " r:" << static_cast<int>(expectedRGBA[0])
752                         << " g:" << static_cast<int>(expectedRGBA[1])
753                         << " b:" << static_cast<int>(expectedRGBA[2])
754                         << " a:" << static_cast<int>(expectedRGBA[3]);
755                     // clang-format on
756                 }
757             }
758         }
759     }
760     return imagesSimilar;
761 }
762 
CompareAHBWithGolden(ScopedAHardwareBuffer & ahb,const std::string & goldenBasename)763 GlExpected<Ok> GfxstreamEnd2EndTest::CompareAHBWithGolden(ScopedAHardwareBuffer& ahb,
764                                                           const std::string& goldenBasename) {
765     Image actual = GL_EXPECT(AsImage(ahb));
766     GlExpected<Image> expected = LoadImage(goldenBasename);
767 
768     bool imagesAreSimilar = false;
769     if (expected.ok()) {
770         imagesAreSimilar = AreImagesSimilar(*expected, actual);
771     } else {
772         imagesAreSimilar = false;
773     }
774 
775     if (!imagesAreSimilar && kSaveImagesIfComparisonFailed) {
776         static uint32_t sImageNumber{1};
777         const std::string outputBasename = std::to_string(sImageNumber++) + "_" + goldenBasename;
778         const std::string output =
779             (std::filesystem::temp_directory_path() / outputBasename).string();
780         SaveRGBAToPng(actual.width, actual.height, actual.pixels.data(), output);
781         ADD_FAILURE() << "Saved image comparison actual image to " << output;
782     }
783 
784     if (!imagesAreSimilar) {
785         return android::base::unexpected(
786             "Image comparison failed (consider setting kSaveImagesIfComparisonFailed to true to "
787             "see the actual image generated).");
788     }
789 
790     return {};
791 }
792 
793 }  // namespace tests
794 }  // namespace gfxstream
795