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 #pragma once
16
17 #include <gmock/gmock.h>
18 #include <gtest/gtest.h>
19
20 #include <inttypes.h>
21
22 #include <future>
23 #include <memory>
24 #include <optional>
25 #include <string>
26 #include <thread>
27 #include <unordered_set>
28 #include <variant>
29
30 // clang-format off
31 #include <EGL/egl.h>
32 #include <EGL/eglext.h>
33 #include "OpenGLESDispatch/gldefs.h"
34 #include "OpenGLESDispatch/gles_functions.h"
35 #include "OpenGLESDispatch/RenderEGL_functions.h"
36 #include "OpenGLESDispatch/RenderEGL_extensions_functions.h"
37
38 #define VULKAN_HPP_NAMESPACE vkhpp
39 #define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
40 #define VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL 1
41 #define VULKAN_HPP_NO_CONSTRUCTORS
42 #define VULKAN_HPP_NO_EXCEPTIONS
43 #include <vulkan/vulkan.hpp>
44 #include <vulkan/vk_android_native_buffer.h>
45 // clang-format on
46
47 #include "KumquatInstance.h"
48 #include "Sync.h"
49 #include "drm_fourcc.h"
50 #include "gfxstream/Expected.h"
51 #include "gfxstream/guest/ANativeWindow.h"
52 #include "gfxstream/guest/GfxStreamGralloc.h"
53 #include "gfxstream/guest/RenderControlApi.h"
54
55 namespace gfxstream {
56 namespace tests {
57
58 constexpr const bool kSaveImagesIfComparisonFailed = false;
59
60 MATCHER(IsOk, "an ok result") {
61 auto& result = arg;
62 if (!result.ok()) {
63 *result_listener << "which is an error with message: \""
64 << result.error()
65 << "\"";
66 return false;
67 }
68 return true;
69 }
70
71 MATCHER(IsError, "an error result") {
72 auto& result = arg;
73 if (result.ok()) {
74 *result_listener << "which is an ok result";
75 return false;
76 }
77 return true;
78 }
79
80 MATCHER(IsVkSuccess, "is VK_SUCCESS") {
81 auto& result = arg;
82 if (result != vkhpp::Result::eSuccess) {
83 *result_listener << "which is " << vkhpp::to_string(result);
84 return false;
85 }
86 return true;
87 }
88
89 MATCHER(IsValidHandle, "a non-null handle") {
90 auto& result = arg;
91 if (!result) {
92 *result_listener << "which is a VK_NULL_HANDLE";
93 return false;
94 }
95 return true;
96 }
97
98 struct Ok {};
99
100 template <typename T>
101 using Result = gfxstream::expected<T, std::string>;
102
103 #define GFXSTREAM_ASSERT(x) \
104 ({ \
105 auto gfxstream_expected = (x); \
106 if (!gfxstream_expected.ok()) { \
107 ASSERT_THAT(gfxstream_expected.ok(), ::testing::IsTrue()) \
108 << "Assertion failed at line " << __LINE__ \
109 << ": error was: " << gfxstream_expected.error(); \
110 } \
111 std::move(gfxstream_expected.value()); \
112 })
113
114 #define GFXSTREAM_ASSERT_VKHPP_RV(x) \
115 ({ \
116 auto vkhpp_result_value = (x); \
117 ASSERT_THAT(vkhpp_result_value.result, IsVkSuccess()) \
118 << "Assertion failed at line " << __LINE__ << ": VkResult was " \
119 << to_string(vkhpp_result_value.result); \
120 std::move(vkhpp_result_value.value); \
121 })
122
123 #define GFXSTREAM_EXPECT_VKHPP_RESULT(x) \
124 ({ \
125 auto vkhpp_result = (x); \
126 if (vkhpp_result != vkhpp::Result::eSuccess) { \
127 return gfxstream::unexpected("Found " + vkhpp::to_string(vkhpp_result) + " at line " + \
128 std::to_string(__LINE__)); \
129 } \
130 })
131
132 #define GFXSTREAM_EXPECT_VKHPP_RV(x) \
133 ({ \
134 auto vkhpp_result_value = (x); \
135 if (vkhpp_result_value.result != vkhpp::Result::eSuccess) { \
136 return gfxstream::unexpected("Found " + vkhpp::to_string(vkhpp_result_value.result) + \
137 " at line " + std::to_string(__LINE__)); \
138 } \
139 std::move(vkhpp_result_value.value); \
140 })
141
142 struct GuestGlDispatchTable {
143 #define DECLARE_EGL_FUNCTION(return_type, function_name, signature) \
144 return_type(*function_name) signature = nullptr;
145
146 #define DECLARE_GLES_FUNCTION(return_type, function_name, signature, args) \
147 return_type(*function_name) signature = nullptr;
148
149 LIST_RENDER_EGL_FUNCTIONS(DECLARE_EGL_FUNCTION)
150 LIST_RENDER_EGL_EXTENSIONS_FUNCTIONS(DECLARE_EGL_FUNCTION)
151 LIST_GLES_FUNCTIONS(DECLARE_GLES_FUNCTION, DECLARE_GLES_FUNCTION)
152 };
153
154 struct GuestRenderControlDispatchTable {
155 PFN_rcCreateDevice rcCreateDevice = nullptr;
156 PFN_rcDestroyDevice rcDestroyDevice = nullptr;
157 PFN_rcCompose rcCompose = nullptr;
158 };
159
160 class ScopedRenderControlDevice {
161 public:
ScopedRenderControlDevice()162 ScopedRenderControlDevice() {}
163
ScopedRenderControlDevice(GuestRenderControlDispatchTable & dispatch)164 ScopedRenderControlDevice(GuestRenderControlDispatchTable& dispatch) : mDispatch(&dispatch) {
165 mDevice = dispatch.rcCreateDevice();
166 }
167
168 ScopedRenderControlDevice(const ScopedRenderControlDevice& rhs) = delete;
169 ScopedRenderControlDevice& operator=(const ScopedRenderControlDevice& rhs) = delete;
170
ScopedRenderControlDevice(ScopedRenderControlDevice && rhs)171 ScopedRenderControlDevice(ScopedRenderControlDevice&& rhs)
172 : mDispatch(rhs.mDispatch), mDevice(rhs.mDevice) {
173 rhs.mDevice = nullptr;
174 }
175
176 ScopedRenderControlDevice& operator=(ScopedRenderControlDevice&& rhs) {
177 mDispatch = rhs.mDispatch;
178 std::swap(mDevice, rhs.mDevice);
179 return *this;
180 }
181
~ScopedRenderControlDevice()182 ~ScopedRenderControlDevice() {
183 if (mDevice != nullptr) {
184 mDispatch->rcDestroyDevice(mDevice);
185 mDevice = nullptr;
186 }
187 }
188
189 operator RenderControlDevice*() { return mDevice; }
190 operator RenderControlDevice*() const { return mDevice; }
191
192 private:
193 GuestRenderControlDispatchTable* mDispatch = nullptr;
194 RenderControlDevice* mDevice = nullptr;
195 };
196
197 class ScopedGlType {
198 public:
199 using GlDispatch = GuestGlDispatchTable;
200 using GlDispatchGenFunc = void (*GuestGlDispatchTable::*)(GLsizei, GLuint*);
201 using GlDispatchDelFunc = void (*GuestGlDispatchTable::*)(GLsizei, const GLuint*);
202
ScopedGlType()203 ScopedGlType() {}
204
ScopedGlType(GlDispatch & glDispatch,GlDispatchGenFunc glGenFunc,GlDispatchDelFunc glDelFunc)205 ScopedGlType(GlDispatch& glDispatch, GlDispatchGenFunc glGenFunc, GlDispatchDelFunc glDelFunc)
206 : mGlDispatch(&glDispatch), mGlGenFunc(glGenFunc), mGlDelFunc(glDelFunc) {
207 (mGlDispatch->*mGlGenFunc)(1, &mHandle);
208 }
209
210 ScopedGlType(const ScopedGlType& rhs) = delete;
211 ScopedGlType& operator=(const ScopedGlType& rhs) = delete;
212
ScopedGlType(ScopedGlType && rhs)213 ScopedGlType(ScopedGlType&& rhs)
214 : mGlDispatch(rhs.mGlDispatch),
215 mGlGenFunc(rhs.mGlGenFunc),
216 mGlDelFunc(rhs.mGlDelFunc),
217 mHandle(rhs.mHandle) {
218 rhs.mHandle = 0;
219 }
220
221 ScopedGlType& operator=(ScopedGlType&& rhs) {
222 mGlDispatch = rhs.mGlDispatch;
223 mGlGenFunc = rhs.mGlGenFunc;
224 mGlDelFunc = rhs.mGlDelFunc;
225 std::swap(mHandle, rhs.mHandle);
226 return *this;
227 }
228
~ScopedGlType()229 ~ScopedGlType() { Reset(); }
230
GLuint()231 operator GLuint() { return mHandle; }
GLuint()232 operator GLuint() const { return mHandle; }
233
Reset()234 void Reset() {
235 if (mHandle != 0) {
236 (mGlDispatch->*mGlDelFunc)(1, &mHandle);
237 mHandle = 0;
238 }
239 }
240
241 private:
242 GlDispatch* mGlDispatch = nullptr;
243 GlDispatchGenFunc mGlGenFunc = nullptr;
244 GlDispatchDelFunc mGlDelFunc = nullptr;
245 GLuint mHandle = 0;
246 };
247
248 class ScopedGlBuffer : public ScopedGlType {
249 public:
ScopedGlBuffer(GlDispatch & dispatch)250 ScopedGlBuffer(GlDispatch& dispatch)
251 : ScopedGlType(dispatch, &GlDispatch::glGenBuffers, &GlDispatch::glDeleteBuffers) {}
252 };
253
254 class ScopedGlTexture : public ScopedGlType {
255 public:
ScopedGlTexture(GlDispatch & dispatch)256 ScopedGlTexture(GlDispatch& dispatch)
257 : ScopedGlType(dispatch, &GlDispatch::glGenTextures, &GlDispatch::glDeleteTextures) {}
258 };
259
260 class ScopedGlFramebuffer : public ScopedGlType {
261 public:
ScopedGlFramebuffer(GlDispatch & dispatch)262 ScopedGlFramebuffer(GlDispatch& dispatch)
263 : ScopedGlType(dispatch, &GlDispatch::glGenFramebuffers,
264 &GlDispatch::glDeleteFramebuffers) {}
265 };
266
267 class ScopedGlShader {
268 public:
269 using GlDispatch = GuestGlDispatchTable;
270
271 ScopedGlShader() = default;
272
273 ScopedGlShader(const ScopedGlShader& rhs) = delete;
274 ScopedGlShader& operator=(const ScopedGlShader& rhs) = delete;
275
276 static Result<ScopedGlShader> MakeShader(GlDispatch& dispatch, GLenum type,
277 const std::string& source);
278
ScopedGlShader(ScopedGlShader && rhs)279 ScopedGlShader(ScopedGlShader&& rhs) : mGlDispatch(rhs.mGlDispatch), mHandle(rhs.mHandle) {
280 rhs.mHandle = 0;
281 }
282
283 ScopedGlShader& operator=(ScopedGlShader&& rhs) {
284 mGlDispatch = rhs.mGlDispatch;
285 std::swap(mHandle, rhs.mHandle);
286 return *this;
287 }
288
~ScopedGlShader()289 ~ScopedGlShader() {
290 if (mHandle != 0) {
291 mGlDispatch->glDeleteShader(mHandle);
292 mHandle = 0;
293 }
294 }
295
GLuint()296 operator GLuint() { return mHandle; }
GLuint()297 operator GLuint() const { return mHandle; }
298
299 private:
ScopedGlShader(GlDispatch & dispatch,GLuint handle)300 ScopedGlShader(GlDispatch& dispatch, GLuint handle) : mGlDispatch(&dispatch), mHandle(handle) {}
301
302 GlDispatch* mGlDispatch = nullptr;
303 GLuint mHandle = 0;
304 };
305
306 class ScopedGlProgram {
307 public:
308 using GlDispatch = GuestGlDispatchTable;
309
310 ScopedGlProgram() = default;
311
312 ScopedGlProgram(const ScopedGlProgram& rhs) = delete;
313 ScopedGlProgram& operator=(const ScopedGlProgram& rhs) = delete;
314
315 static Result<ScopedGlProgram> MakeProgram(GlDispatch& dispatch, const std::string& vertShader,
316 const std::string& fragShader);
317
318 static Result<ScopedGlProgram> MakeProgram(GlDispatch& dispatch, GLenum programBinaryFormat,
319 const std::vector<uint8_t>& programBinaryData);
320
ScopedGlProgram(ScopedGlProgram && rhs)321 ScopedGlProgram(ScopedGlProgram&& rhs) : mGlDispatch(rhs.mGlDispatch), mHandle(rhs.mHandle) {
322 rhs.mHandle = 0;
323 }
324
325 ScopedGlProgram& operator=(ScopedGlProgram&& rhs) {
326 mGlDispatch = rhs.mGlDispatch;
327 std::swap(mHandle, rhs.mHandle);
328 return *this;
329 }
330
~ScopedGlProgram()331 ~ScopedGlProgram() {
332 if (mHandle != 0) {
333 mGlDispatch->glDeleteProgram(mHandle);
334 mHandle = 0;
335 }
336 }
337
GLuint()338 operator GLuint() { return mHandle; }
GLuint()339 operator GLuint() const { return mHandle; }
340
341 private:
ScopedGlProgram(GlDispatch & dispatch,GLuint handle)342 ScopedGlProgram(GlDispatch& dispatch, GLuint handle)
343 : mGlDispatch(&dispatch), mHandle(handle) {}
344
345 GlDispatch* mGlDispatch = nullptr;
346 GLuint mHandle = 0;
347 };
348
349 class ScopedAHardwareBuffer {
350 public:
351 ScopedAHardwareBuffer() = default;
352
353 static Result<ScopedAHardwareBuffer> Allocate(Gralloc& gralloc, uint32_t width, uint32_t height,
354 uint32_t format);
355
356 ScopedAHardwareBuffer(const ScopedAHardwareBuffer& rhs) = delete;
357 ScopedAHardwareBuffer& operator=(const ScopedAHardwareBuffer& rhs) = delete;
358
ScopedAHardwareBuffer(ScopedAHardwareBuffer && rhs)359 ScopedAHardwareBuffer(ScopedAHardwareBuffer&& rhs)
360 : mGralloc(rhs.mGralloc), mHandle(rhs.mHandle) {
361 rhs.mHandle = nullptr;
362 }
363
364 ScopedAHardwareBuffer& operator=(ScopedAHardwareBuffer&& rhs) {
365 std::swap(mGralloc, rhs.mGralloc);
366 std::swap(mHandle, rhs.mHandle);
367 return *this;
368 }
369
~ScopedAHardwareBuffer()370 ~ScopedAHardwareBuffer() {
371 if (mHandle != nullptr) {
372 mGralloc->release(mHandle);
373 mHandle = 0;
374 }
375 }
376
GetWidth()377 uint32_t GetWidth() const { return mGralloc->getWidth(mHandle); }
378
GetHeight()379 uint32_t GetHeight() const { return mGralloc->getHeight(mHandle); }
380
GetAHBFormat()381 uint32_t GetAHBFormat() const { return mGralloc->getFormat(mHandle); }
382
GetDrmFormat()383 uint32_t GetDrmFormat() const { return mGralloc->getFormatDrmFourcc(mHandle); }
384
Lock()385 Result<uint8_t*> Lock() {
386 uint8_t* mapped = nullptr;
387 int status = mGralloc->lock(mHandle, &mapped);
388 if (status != 0) {
389 return gfxstream::unexpected("Failed to lock AHB");
390 }
391 return mapped;
392 }
393
LockPlanes()394 Result<std::vector<Gralloc::LockedPlane>> LockPlanes() {
395 std::vector<Gralloc::LockedPlane> planes;
396 int status = mGralloc->lockPlanes(mHandle, &planes);
397 if (status != 0) {
398 return gfxstream::unexpected("Failed to lock AHB");
399 }
400 return planes;
401 }
402
Unlock()403 void Unlock() { mGralloc->unlock(mHandle); }
404
405 operator AHardwareBuffer*() { return mHandle; }
406 operator AHardwareBuffer*() const { return mHandle; }
407
408 private:
ScopedAHardwareBuffer(Gralloc & gralloc,AHardwareBuffer * handle)409 ScopedAHardwareBuffer(Gralloc& gralloc, AHardwareBuffer* handle)
410 : mGralloc(&gralloc), mHandle(handle) {}
411
412 Gralloc* mGralloc = nullptr;
413 AHardwareBuffer* mHandle = nullptr;
414 };
415
416 struct PixelR8G8B8A8 {
417 PixelR8G8B8A8() = default;
418
PixelR8G8B8A8PixelR8G8B8A8419 PixelR8G8B8A8(uint8_t rr, uint8_t gg, uint8_t bb, uint8_t aa) : r(rr), g(gg), b(bb), a(aa) {}
420
PixelR8G8B8A8PixelR8G8B8A8421 PixelR8G8B8A8(int xx, int yy, uint8_t rr, uint8_t gg, uint8_t bb, uint8_t aa)
422 : x(xx), y(yy), r(rr), g(gg), b(bb), a(aa) {}
423
PixelR8G8B8A8PixelR8G8B8A8424 PixelR8G8B8A8(int xx, int yy, uint32_t rgba) : x(xx), y(yy) {
425 const uint8_t* parts = reinterpret_cast<const uint8_t*>(&rgba);
426 r = parts[0];
427 g = parts[1];
428 b = parts[2];
429 a = parts[3];
430 }
431
432 std::optional<int> x;
433 std::optional<int> y;
434
435 uint8_t r = 0;
436 uint8_t g = 0;
437 uint8_t b = 0;
438 uint8_t a = 0;
439
ToStringPixelR8G8B8A8440 std::string ToString() const {
441 std::string ret = std::string("Pixel");
442 if (x) {
443 ret += std::string(" x:") + std::to_string(*x);
444 }
445 if (y) {
446 ret += std::string(" y:") + std::to_string(*y);
447 }
448 ret += std::string(" {");
449 ret += std::string(" r:") + std::to_string(static_cast<int>(r));
450 ret += std::string(" g:") + std::to_string(static_cast<int>(g));
451 ret += std::string(" b:") + std::to_string(static_cast<int>(b));
452 ret += std::string(" a:") + std::to_string(static_cast<int>(a));
453 ret += std::string(" }");
454 return ret;
455 }
456
457 bool operator==(const PixelR8G8B8A8& rhs) const {
458 const auto& lhs = *this;
459 return std::tie(lhs.r, lhs.g, lhs.b, lhs.a) == std::tie(rhs.r, rhs.g, rhs.b, rhs.a);
460 }
461
PrintToPixelR8G8B8A8462 friend void PrintTo(const PixelR8G8B8A8& pixel, std::ostream* os) { *os << pixel.ToString(); }
463 };
464
465 void RGBToYUV(uint8_t r, uint8_t g, uint8_t b, uint8_t* outY, uint8_t* outU, uint8_t* outV);
466
Fill(uint32_t w,uint32_t h,const PixelR8G8B8A8 & pixel)467 constexpr std::vector<uint8_t> Fill(uint32_t w, uint32_t h, const PixelR8G8B8A8& pixel) {
468 std::vector<uint8_t> ret;
469 ret.reserve(w * h * 4);
470 for (uint32_t y = 0; y < h; y++) {
471 for (uint32_t x = 0; x < w; x++) {
472 ret.push_back(pixel.r);
473 ret.push_back(pixel.g);
474 ret.push_back(pixel.b);
475 ret.push_back(pixel.a);
476 }
477 }
478 return ret;
479 }
480
481 struct Image {
482 uint32_t width;
483 uint32_t height;
484 std::vector<uint32_t> pixels;
485 };
486 Image ImageFromColor(uint32_t w, uint32_t h, const PixelR8G8B8A8& pixel);
487
488 enum class GfxstreamTransport {
489 kVirtioGpuAsg,
490 kVirtioGpuPipe,
491 };
492
493 struct TestParams {
494 bool with_gl;
495 bool with_vk;
496 int samples = 1;
497 std::unordered_set<std::string> with_features;
498 GfxstreamTransport with_transport = GfxstreamTransport::kVirtioGpuAsg;
499
500 std::string ToString() const;
501 friend std::ostream& operator<<(std::ostream& os, const TestParams& params);
502 };
503
504 std::string GetTestName(const ::testing::TestParamInfo<TestParams>& info);
505
506 // Generates the cartesian product of params with and without the given features.
507 std::vector<TestParams> WithAndWithoutFeatures(const std::vector<TestParams>& params,
508 const std::vector<std::string>& features);
509
510 struct TypicalVkTestEnvironmentOptions {
511 uint32_t apiVersion{VK_API_VERSION_1_2};
512 std::optional<const void*> instanceCreateInfoPNext;
513 std::optional<std::vector<std::string>> deviceExtensions;
514 std::optional<const void*> deviceCreateInfoPNext;
515 };
516
517 class GfxstreamEnd2EndTest : public ::testing::TestWithParam<TestParams> {
518 public:
519 std::unique_ptr<GuestGlDispatchTable> SetupGuestGl();
520 std::unique_ptr<GuestRenderControlDispatchTable> SetupGuestRc();
521 std::unique_ptr<vkhpp::DynamicLoader> SetupGuestVk();
522
523 void SetUp() override;
524
525 void TearDownGuest();
526 void TearDown() override;
527
528 void SetUpEglContextAndSurface(uint32_t contextVersion,
529 uint32_t width,
530 uint32_t height,
531 EGLDisplay* outDisplay,
532 EGLContext* outContext,
533 EGLSurface* outSurface);
534
535 void TearDownEglContextAndSurface(EGLDisplay display,
536 EGLContext context,
537 EGLSurface surface);
538
539 Result<ScopedGlShader> SetUpShader(GLenum type, const std::string& source);
540
541 Result<ScopedGlProgram> SetUpProgram(const std::string& vertSource,
542 const std::string& fragSource);
543
544 Result<ScopedGlProgram> SetUpProgram(GLenum programBinaryFormat,
545 const std::vector<uint8_t>& programBinaryData);
546
547 struct TypicalVkTestEnvironment {
548 vkhpp::UniqueInstance instance;
549 vkhpp::PhysicalDevice physicalDevice;
550 vkhpp::UniqueDevice device;
551 vkhpp::Queue queue;
552 uint32_t queueFamilyIndex;
553 };
554 Result<TypicalVkTestEnvironment> SetUpTypicalVkTestEnvironment(
555 const TypicalVkTestEnvironmentOptions& opts = {});
556
557 void SnapshotSaveAndLoad();
558
559 Result<Image> LoadImage(const std::string& basename);
560
561 Result<Image> AsImage(ScopedAHardwareBuffer& ahb);
562
563 Result<Ok> FillAhb(ScopedAHardwareBuffer& ahb, PixelR8G8B8A8 color);
564
565 Result<ScopedAHardwareBuffer> CreateAHBFromImage(const std::string& basename);
566
567 bool ArePixelsSimilar(uint32_t expectedPixel, uint32_t actualPixel);
568
569 bool AreImagesSimilar(const Image& expected, const Image& actual);
570
571 Result<Ok> CompareAHBWithGolden(ScopedAHardwareBuffer& ahb, const std::string& goldenBasename);
572
573 std::unique_ptr<ANativeWindowHelper> mAnwHelper;
574 std::unique_ptr<Gralloc> mGralloc;
575 std::unique_ptr<SyncHelper> mSync;
576 std::unique_ptr<GuestGlDispatchTable> mGl;
577 std::unique_ptr<GuestRenderControlDispatchTable> mRc;
578 std::unique_ptr<vkhpp::DynamicLoader> mVk;
579
580 std::unique_ptr<KumquatInstance> mKumquatInstance = nullptr;
581 };
582
583 } // namespace tests
584 } // namespace gfxstream
585