• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <gtest/gtest.h>
2 
3 #include "CompositorVk.h"
4 
5 #include <algorithm>
6 #include <array>
7 #include <glm/gtx/matrix_transform_2d.hpp>
8 #include <optional>
9 
10 #include "tests/VkTestUtils.h"
11 #include "vulkan/VulkanDispatch.h"
12 #include "vulkan/vk_util.h"
13 
14 class CompositorVkTest : public ::testing::Test {
15    protected:
16     using RenderTarget =
17         emugl::RenderResourceVk<VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
18                                 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT>;
19     using RenderTexture = emugl::RenderTextureVk;
20 
SetUpTestCase()21     static void SetUpTestCase() { k_vk = emugl::vkDispatch(false); }
22 
23     static constexpr uint32_t k_numOfRenderTargets = 10;
24     static constexpr uint32_t k_renderTargetWidth = 255;
25     static constexpr uint32_t k_renderTargetHeight = 255;
26     static constexpr uint32_t k_renderTargetNumOfPixels =
27         k_renderTargetWidth * k_renderTargetHeight;
28 
SetUp()29     void SetUp() override {
30         ASSERT_NE(k_vk, nullptr);
31         createInstance();
32         pickPhysicalDevice();
33         createLogicalDevice();
34 
35         VkFormatProperties formatProperties;
36         k_vk->vkGetPhysicalDeviceFormatProperties(
37             m_vkPhysicalDevice, RenderTarget::k_vkFormat, &formatProperties);
38         if (!(formatProperties.optimalTilingFeatures &
39               VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) {
40             GTEST_SKIP();
41         }
42         k_vk->vkGetPhysicalDeviceFormatProperties(
43             m_vkPhysicalDevice, RenderTexture::k_vkFormat, &formatProperties);
44         if (!(formatProperties.optimalTilingFeatures &
45               VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
46             GTEST_SKIP();
47         }
48 
49         VkCommandPoolCreateInfo commandPoolCi = {
50             .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
51             .queueFamilyIndex = m_compositorQueueFamilyIndex};
52         ASSERT_EQ(k_vk->vkCreateCommandPool(m_vkDevice, &commandPoolCi, nullptr,
53                                             &m_vkCommandPool),
54                   VK_SUCCESS);
55         k_vk->vkGetDeviceQueue(m_vkDevice, m_compositorQueueFamilyIndex, 0,
56                                &m_compositorVkQueue);
57         ASSERT_TRUE(m_compositorVkQueue != VK_NULL_HANDLE);
58 
59         for (uint32_t i = 0; i < k_numOfRenderTargets; i++) {
60             auto renderTarget = RenderTarget::create(
61                 *k_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue,
62                 m_vkCommandPool, k_renderTargetHeight, k_renderTargetWidth);
63             ASSERT_NE(renderTarget, nullptr);
64             m_renderTargets.emplace_back(std::move(renderTarget));
65         }
66 
67         m_renderTargetImageViews.resize(m_renderTargets.size());
68         ASSERT_EQ(
69             std::transform(
70                 m_renderTargets.begin(), m_renderTargets.end(),
71                 m_renderTargetImageViews.begin(),
72                 [](const std::unique_ptr<const RenderTarget> &renderTarget) {
73                     return renderTarget->m_vkImageView;
74                 }),
75             m_renderTargetImageViews.end());
76     }
77 
TearDown()78     void TearDown() override {
79         m_renderTargets.clear();
80         k_vk->vkDestroyCommandPool(m_vkDevice, m_vkCommandPool, nullptr);
81         k_vk->vkDestroyDevice(m_vkDevice, nullptr);
82         m_vkDevice = VK_NULL_HANDLE;
83         k_vk->vkDestroyInstance(m_vkInstance, nullptr);
84         m_vkInstance = VK_NULL_HANDLE;
85     }
86 
createCompositor()87     std::unique_ptr<CompositorVk> createCompositor() {
88         return CompositorVk::create(
89             *k_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue,
90             RenderTarget::k_vkFormat, RenderTarget::k_vkImageLayout,
91             RenderTarget::k_vkImageLayout, k_renderTargetWidth,
92             k_renderTargetHeight, m_renderTargetImageViews, m_vkCommandPool);
93     }
94 
createSampler()95     VkSampler createSampler() {
96         VkSamplerCreateInfo samplerCi = {
97             .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
98             .magFilter = VK_FILTER_NEAREST,
99             .minFilter = VK_FILTER_NEAREST,
100             .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
101             .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
102             .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
103             .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
104             .mipLodBias = 0.0f,
105             .anisotropyEnable = VK_FALSE,
106             .maxAnisotropy = 1.0f,
107             .compareEnable = VK_FALSE,
108             .compareOp = VK_COMPARE_OP_ALWAYS,
109             .minLod = 0.0f,
110             .maxLod = 0.0f,
111             .borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK,
112             .unnormalizedCoordinates = VK_FALSE};
113         VkSampler res;
114         VK_CHECK(k_vk->vkCreateSampler(m_vkDevice, &samplerCi, nullptr, &res));
115         return res;
116     }
117 
118     static const goldfish_vk::VulkanDispatch *k_vk;
119     VkInstance m_vkInstance = VK_NULL_HANDLE;
120     VkPhysicalDevice m_vkPhysicalDevice = VK_NULL_HANDLE;
121     uint32_t m_compositorQueueFamilyIndex = 0;
122     VkDevice m_vkDevice = VK_NULL_HANDLE;
123     std::vector<std::unique_ptr<const RenderTarget>> m_renderTargets;
124     std::vector<VkImageView> m_renderTargetImageViews;
125     VkCommandPool m_vkCommandPool = VK_NULL_HANDLE;
126     VkQueue m_compositorVkQueue = VK_NULL_HANDLE;
127 
128    private:
createInstance()129     void createInstance() {
130         VkApplicationInfo appInfo = {
131             .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
132             .pNext = nullptr,
133             .pApplicationName = "emulator CompositorVk unittest",
134             .applicationVersion = VK_MAKE_VERSION(1, 0, 0),
135             .pEngineName = "No Engine",
136             .engineVersion = VK_MAKE_VERSION(1, 0, 0),
137             .apiVersion = VK_API_VERSION_1_1};
138         VkInstanceCreateInfo instanceCi = {
139             .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
140             .pApplicationInfo = &appInfo,
141             .enabledExtensionCount = 0,
142             .ppEnabledExtensionNames = nullptr};
143         ASSERT_EQ(k_vk->vkCreateInstance(&instanceCi, nullptr, &m_vkInstance),
144                   VK_SUCCESS);
145         ASSERT_TRUE(m_vkInstance != VK_NULL_HANDLE);
146     }
147 
pickPhysicalDevice()148     void pickPhysicalDevice() {
149         uint32_t physicalDeviceCount = 0;
150         ASSERT_EQ(k_vk->vkEnumeratePhysicalDevices(
151                       m_vkInstance, &physicalDeviceCount, nullptr),
152                   VK_SUCCESS);
153         ASSERT_GT(physicalDeviceCount, 0);
154         std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
155         ASSERT_EQ(
156             k_vk->vkEnumeratePhysicalDevices(m_vkInstance, &physicalDeviceCount,
157                                              physicalDevices.data()),
158             VK_SUCCESS);
159         for (const auto &device : physicalDevices) {
160             VkPhysicalDeviceDescriptorIndexingFeaturesEXT descIndexingFeatures = {
161                 .sType =
162                     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT};
163             VkPhysicalDeviceFeatures2 features = {
164                 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
165                 .pNext = &descIndexingFeatures};
166             k_vk->vkGetPhysicalDeviceFeatures2(device, &features);
167             if (!CompositorVk::validatePhysicalDeviceFeatures(features)) {
168                 continue;
169             }
170             uint32_t queueFamilyCount = 0;
171             k_vk->vkGetPhysicalDeviceQueueFamilyProperties(
172                 device, &queueFamilyCount, nullptr);
173             ASSERT_GT(queueFamilyCount, 0);
174             std::vector<VkQueueFamilyProperties> queueFamilyProperties(
175                 queueFamilyCount);
176             k_vk->vkGetPhysicalDeviceQueueFamilyProperties(
177                 device, &queueFamilyCount, queueFamilyProperties.data());
178             uint32_t queueFamilyIndex = 0;
179             for (; queueFamilyIndex < queueFamilyCount; queueFamilyIndex++) {
180                 if (CompositorVk::validateQueueFamilyProperties(
181                         queueFamilyProperties[queueFamilyIndex])) {
182                     break;
183                 }
184             }
185             if (queueFamilyIndex == queueFamilyCount) {
186                 continue;
187             }
188 
189             m_compositorQueueFamilyIndex = queueFamilyIndex;
190             m_vkPhysicalDevice = device;
191             return;
192         }
193         FAIL() << "Can't find a suitable VkPhysicalDevice.";
194     }
195 
createLogicalDevice()196     void createLogicalDevice() {
197         const float queuePriority = 1.0f;
198         VkDeviceQueueCreateInfo queueCi = {
199             .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
200             .queueFamilyIndex = m_compositorQueueFamilyIndex,
201             .queueCount = 1,
202             .pQueuePriorities = &queuePriority};
203         VkPhysicalDeviceDescriptorIndexingFeaturesEXT descIndexingFeatures = {
204             .sType =
205                 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT};
206         VkPhysicalDeviceFeatures2 features = {
207             .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
208             .pNext = &descIndexingFeatures};
209         ASSERT_TRUE(CompositorVk::enablePhysicalDeviceFeatures(features));
210         const std::vector<const char *> enabledDeviceExtensions =
211             CompositorVk::getRequiredDeviceExtensions();
212         VkDeviceCreateInfo deviceCi = {
213             .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
214             .pNext = &features,
215             .queueCreateInfoCount = 1,
216             .pQueueCreateInfos = &queueCi,
217             .enabledLayerCount = 0,
218             .enabledExtensionCount =
219                 static_cast<uint32_t>(enabledDeviceExtensions.size()),
220             .ppEnabledExtensionNames = enabledDeviceExtensions.data()};
221         ASSERT_EQ(k_vk->vkCreateDevice(m_vkPhysicalDevice, &deviceCi, nullptr,
222                                        &m_vkDevice),
223                   VK_SUCCESS);
224         ASSERT_TRUE(m_vkDevice != VK_NULL_HANDLE);
225     }
226 };
227 
228 const goldfish_vk::VulkanDispatch *CompositorVkTest::k_vk = nullptr;
229 
TEST_F(CompositorVkTest,Init)230 TEST_F(CompositorVkTest, Init) { ASSERT_NE(createCompositor(), nullptr); }
231 
TEST_F(CompositorVkTest,ValidatePhysicalDeviceFeatures)232 TEST_F(CompositorVkTest, ValidatePhysicalDeviceFeatures) {
233     VkPhysicalDeviceFeatures2 features = {
234         .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2};
235     ASSERT_FALSE(CompositorVk::validatePhysicalDeviceFeatures(features));
236     VkPhysicalDeviceDescriptorIndexingFeaturesEXT descIndexingFeatures = {
237         .sType =
238             VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT,
239         .pNext = &descIndexingFeatures};
240     ASSERT_FALSE(CompositorVk::validatePhysicalDeviceFeatures(features));
241     descIndexingFeatures.descriptorBindingSampledImageUpdateAfterBind = VK_TRUE;
242     ASSERT_TRUE(CompositorVk::validatePhysicalDeviceFeatures(features));
243 }
244 
TEST_F(CompositorVkTest,ValidateQueueFamilyProperties)245 TEST_F(CompositorVkTest, ValidateQueueFamilyProperties) {
246     VkQueueFamilyProperties properties = {};
247     properties.queueFlags &= ~VK_QUEUE_GRAPHICS_BIT;
248     ASSERT_FALSE(CompositorVk::validateQueueFamilyProperties(properties));
249     properties.queueFlags |= VK_QUEUE_GRAPHICS_BIT;
250     ASSERT_TRUE(CompositorVk::validateQueueFamilyProperties(properties));
251 }
252 
TEST_F(CompositorVkTest,EnablePhysicalDeviceFeatures)253 TEST_F(CompositorVkTest, EnablePhysicalDeviceFeatures) {
254     VkPhysicalDeviceFeatures2 features = {
255         .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2};
256     ASSERT_FALSE(CompositorVk::enablePhysicalDeviceFeatures(features));
257     VkPhysicalDeviceDescriptorIndexingFeaturesEXT descIndexingFeatures = {
258         .sType =
259             VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT,
260         .pNext = &descIndexingFeatures};
261     ASSERT_TRUE(CompositorVk::enablePhysicalDeviceFeatures(features));
262     ASSERT_EQ(descIndexingFeatures.descriptorBindingSampledImageUpdateAfterBind,
263               VK_TRUE);
264 }
265 
TEST_F(CompositorVkTest,EmptyCompositionShouldDrawABlackFrame)266 TEST_F(CompositorVkTest, EmptyCompositionShouldDrawABlackFrame) {
267     std::vector<uint32_t> pixels(k_renderTargetNumOfPixels);
268     for (uint32_t i = 0; i < k_renderTargetNumOfPixels; i++) {
269         uint8_t v = static_cast<uint8_t>((i / 4) & 0xff);
270         uint8_t *pixel = reinterpret_cast<uint8_t *>(&pixels[i]);
271         pixel[0] = v;
272         pixel[1] = v;
273         pixel[2] = v;
274         pixel[3] = 0xff;
275     }
276     for (uint32_t i = 0; i < k_numOfRenderTargets; i++) {
277         ASSERT_TRUE(m_renderTargets[i]->write(pixels));
278         auto maybeImageBytes = m_renderTargets[i]->read();
279         ASSERT_TRUE(maybeImageBytes.has_value());
280         for (uint32_t i = 0; i < k_renderTargetNumOfPixels; i++) {
281             ASSERT_EQ(pixels[i], maybeImageBytes.value()[i]);
282         }
283     }
284 
285     auto compositor = createCompositor();
286     ASSERT_NE(compositor, nullptr);
287 
288     // render to render targets with event index
289     std::vector<VkCommandBuffer> cmdBuffs = {};
290     for (uint32_t i = 0; i < k_numOfRenderTargets; i++) {
291         if (i % 2 == 0) {
292             cmdBuffs.emplace_back(compositor->getCommandBuffer(i));
293         }
294     }
295     VkSubmitInfo submitInfo = {
296         .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
297         .commandBufferCount = static_cast<uint32_t>(cmdBuffs.size()),
298         .pCommandBuffers = cmdBuffs.data()};
299     ASSERT_EQ(k_vk->vkQueueSubmit(m_compositorVkQueue, 1, &submitInfo,
300                                   VK_NULL_HANDLE),
301               VK_SUCCESS);
302 
303     ASSERT_EQ(k_vk->vkQueueWaitIdle(m_compositorVkQueue), VK_SUCCESS);
304     for (uint32_t i = 0; i < k_numOfRenderTargets; i++) {
305         auto maybeImagePixels = m_renderTargets[i]->read();
306         ASSERT_TRUE(maybeImagePixels.has_value());
307         const auto &imagePixels = maybeImagePixels.value();
308         for (uint32_t j = 0; j < k_renderTargetNumOfPixels; j++) {
309             const auto pixel =
310                 reinterpret_cast<const uint8_t *>(&imagePixels[j]);
311             // should only render to render targets with even index
312             if (i % 2 == 0) {
313                 ASSERT_EQ(pixel[0], 0);
314                 ASSERT_EQ(pixel[1], 0);
315                 ASSERT_EQ(pixel[2], 0);
316                 ASSERT_EQ(pixel[3], 0xff);
317             } else {
318                 ASSERT_EQ(pixels[j], imagePixels[j]);
319             }
320         }
321     }
322 }
323 
TEST_F(CompositorVkTest,SimpleComposition)324 TEST_F(CompositorVkTest, SimpleComposition) {
325     constexpr uint32_t textureLeft = 30;
326     constexpr uint32_t textureRight = 50;
327     constexpr uint32_t textureTop = 10;
328     constexpr uint32_t textureBottom = 40;
329     constexpr uint32_t textureWidth = textureRight - textureLeft;
330     constexpr uint32_t textureHeight = textureBottom - textureTop;
331     auto sampler = createSampler();
332     auto texture = RenderTexture::create(*k_vk, m_vkDevice, m_vkPhysicalDevice,
333                                          m_compositorVkQueue, m_vkCommandPool,
334                                          textureWidth, textureHeight);
335     uint32_t textureColor;
336     uint8_t *textureColor_ = reinterpret_cast<uint8_t *>(&textureColor);
337     textureColor_[0] = 0xff;
338     textureColor_[1] = 0;
339     textureColor_[2] = 0;
340     textureColor_[3] = 0xff;
341     std::vector<uint32_t> pixels(textureWidth * textureHeight, textureColor);
342     ASSERT_TRUE(texture->write(pixels));
343     auto compositor = createCompositor();
344     ASSERT_NE(compositor, nullptr);
345     auto composition = std::make_unique<Composition>(
346         texture->m_vkImageView, sampler, textureWidth, textureHeight);
347     auto transform = glm::translate(glm::mat3(1.0f),
348                                     glm::vec2(static_cast<float>(textureLeft),
349                                               static_cast<float>(textureTop)));
350     composition->m_transform = transform;
351     compositor->setComposition(0, std::move(composition));
352     VkCommandBuffer cmdBuff = compositor->getCommandBuffer(0);
353     VkSubmitInfo submitInfo = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
354                                .commandBufferCount = 1,
355                                .pCommandBuffers = &cmdBuff};
356     ASSERT_EQ(k_vk->vkQueueSubmit(m_compositorVkQueue, 1, &submitInfo,
357                                   VK_NULL_HANDLE),
358               VK_SUCCESS);
359     ASSERT_EQ(k_vk->vkQueueWaitIdle(m_compositorVkQueue), VK_SUCCESS);
360 
361     auto maybeImagePixels = m_renderTargets[0]->read();
362     ASSERT_TRUE(maybeImagePixels.has_value());
363     const auto &imagePixels = maybeImagePixels.value();
364 
365     for (uint32_t i = 0; i < k_renderTargetHeight; i++) {
366         for (uint32_t j = 0; j < k_renderTargetWidth; j++) {
367             uint32_t offset = i * k_renderTargetWidth + j;
368             const uint8_t *pixel =
369                 reinterpret_cast<const uint8_t *>(&imagePixels[offset]);
370             EXPECT_EQ(pixel[1], 0);
371             EXPECT_EQ(pixel[2], 0);
372             EXPECT_EQ(pixel[3], 0xff);
373             if (i >= textureTop && i < textureBottom && j >= textureLeft &&
374                 j < textureRight) {
375                 EXPECT_EQ(pixel[0], 0xff);
376             } else {
377                 EXPECT_EQ(pixel[0], 0);
378             }
379         }
380     }
381     k_vk->vkDestroySampler(m_vkDevice, sampler, nullptr);
382 }
383 
TEST_F(CompositorVkTest,CompositingWithDifferentCompositionOnMultipleTargets)384 TEST_F(CompositorVkTest, CompositingWithDifferentCompositionOnMultipleTargets) {
385     constexpr uint32_t textureWidth = 20;
386     constexpr uint32_t textureHeight = 30;
387     struct Rect {
388         uint32_t m_top;
389         uint32_t m_bottom;
390         uint32_t m_left;
391         uint32_t m_right;
392     };
393     std::vector<Rect> texturePositions(k_numOfRenderTargets);
394     for (int i = 0; i < k_numOfRenderTargets; i++) {
395         auto left = (i * 30) % (k_renderTargetWidth - textureWidth);
396         auto top = (i * 20) % (k_renderTargetHeight - textureHeight);
397         texturePositions[i].m_top = top;
398         texturePositions[i].m_bottom = top + textureHeight;
399         texturePositions[i].m_left = left;
400         texturePositions[i].m_right = left + textureWidth;
401     }
402     auto sampler = createSampler();
403     auto texture = RenderTexture::create(*k_vk, m_vkDevice, m_vkPhysicalDevice,
404                                          m_compositorVkQueue, m_vkCommandPool,
405                                          textureWidth, textureHeight);
406     uint32_t textureColor;
407     uint8_t *textureColor_ = reinterpret_cast<uint8_t *>(&textureColor);
408     textureColor_[0] = 0xff;
409     textureColor_[1] = 0;
410     textureColor_[2] = 0;
411     textureColor_[3] = 0xff;
412     std::vector<uint32_t> pixels(textureWidth * textureHeight, textureColor);
413     ASSERT_TRUE(texture->write(pixels));
414     auto compositor = createCompositor();
415     ASSERT_NE(compositor, nullptr);
416     for (int i = 0; i < k_numOfRenderTargets; i++) {
417         const auto &pos = texturePositions[i];
418         auto composition = std::make_unique<Composition>(
419             texture->m_vkImageView, sampler, textureWidth, textureHeight);
420         auto transform = glm::translate(
421             glm::mat3(1.0f), glm::vec2(static_cast<float>(pos.m_left),
422                                        static_cast<float>(pos.m_top)));
423         composition->m_transform = transform;
424         compositor->setComposition(i, std::move(composition));
425         VkCommandBuffer cmdBuff = compositor->getCommandBuffer(i);
426         VkSubmitInfo submitInfo = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
427                                    .commandBufferCount = 1,
428                                    .pCommandBuffers = &cmdBuff};
429         ASSERT_EQ(k_vk->vkQueueSubmit(m_compositorVkQueue, 1, &submitInfo,
430                                       VK_NULL_HANDLE),
431                   VK_SUCCESS);
432         ASSERT_EQ(k_vk->vkQueueWaitIdle(m_compositorVkQueue), VK_SUCCESS);
433 
434         auto maybeImagePixels = m_renderTargets[i]->read();
435         ASSERT_TRUE(maybeImagePixels.has_value());
436         const auto &imagePixels = maybeImagePixels.value();
437 
438         for (uint32_t i = 0; i < k_renderTargetHeight; i++) {
439             for (uint32_t j = 0; j < k_renderTargetWidth; j++) {
440                 uint32_t offset = i * k_renderTargetWidth + j;
441                 const uint8_t *pixel =
442                     reinterpret_cast<const uint8_t *>(&imagePixels[offset]);
443                 EXPECT_EQ(pixel[1], 0);
444                 EXPECT_EQ(pixel[2], 0);
445                 EXPECT_EQ(pixel[3], 0xff);
446                 if (i >= pos.m_top && i < pos.m_bottom && j >= pos.m_left &&
447                     j < pos.m_right) {
448                     EXPECT_EQ(pixel[0], 0xff);
449                 } else {
450                     EXPECT_EQ(pixel[0], 0);
451                 }
452             }
453         }
454     }
455     k_vk->vkDestroySampler(m_vkDevice, sampler, nullptr);
456 }