1 // copyright (c) 2022 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 "VulkanTestHelper.h"
16 
17 #include "host-common/emugl_vm_operations.h"
18 #include "host-common/feature_control.h"
19 #include "host-common/logging.h"
20 #include "host-common/vm_operations.h"
21 
22 namespace gfxstream {
23 namespace vk {
24 namespace testing {
25 namespace {
26 
27 using ::android::base::BumpPool;
28 
29 bool validationErrorsFound = false;
30 
31 // Called back by the Vulkan validation layer in case of a validation error
validationCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,VkDebugUtilsMessageTypeFlagsEXT type,const VkDebugUtilsMessengerCallbackDataEXT * pCallbackData,void * pUserData)32 VKAPI_ATTR VkBool32 VKAPI_CALL validationCallback(
33     VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT type,
34     const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) {
35     if (severity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
36         ERR("Validation Layer: \"%s\"", pCallbackData->pMessage);
37         validationErrorsFound = true;
38     }
39     return VK_FALSE;
40 }
41 
42 }  // namespace
43 
44 std::mutex VulkanTestHelper::mMutex;
45 
VulkanTestHelper()46 VulkanTestHelper::VulkanTestHelper()
47     : mLock(mMutex),
48       mVk(vkDispatch(/*forTesting=*/true)),
49       mLogger(),
50       mMetricsLogger(android::base::CreateMetricsLogger()),
51       mHealthMonitor(*mMetricsLogger),
52       mVkEmu(createGlobalVkEmulation(mVk)),
53       mBp(std::make_unique<BumpPool>()),
54       mDecoderContext(VkDecoderContext{.processName = "vulkan_test",
55                                        .gfxApiLogger = &mLogger,
56                                        .healthMonitor = &mHealthMonitor,
57                                        .metricsLogger = mMetricsLogger.get()}),
58       mTestDispatch(mVk, mBp.get(), &mDecoderContext) {
59     // Enable so that we can have VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
60     feature_set_enabled_override(kFeature_GLDirectMem, true);
61 
62     // This is used by VkDecoderGlobalState::on_vkCreateInstance()
63     QAndroidVmOperations vmOps;
__anon478a2d890202(bool) 64     vmOps.setSkipSnapshotSave = [](bool) {};
65     set_emugl_vm_operations(vmOps);
66 
67     validationErrorsFound = false;
68 }
69 
destroy()70 void VulkanTestHelper::destroy() {
71     if (mDevice) {
72         vk().vkDeviceWaitIdle(mDevice);
73         if (mCommandPool) vk().vkDestroyCommandPool(mDevice, mCommandPool, nullptr);
74         vk().vkDestroyDevice(mDevice, nullptr);
75     }
76     if (mInstance) {
77         if (mDebugMessenger) {
78             vk().vkDestroyDebugUtilsMessengerEXT(mInstance, mDebugMessenger, nullptr);
79         }
80         vk().vkDestroyInstance(mInstance, nullptr);
81     }
82 
83     mCommandPool = VK_NULL_HANDLE;
84     mDevice = VK_NULL_HANDLE;
85     mInstance = VK_NULL_HANDLE;
86     mDebugMessenger = VK_NULL_HANDLE;
87 
88     VkDecoderGlobalState::reset();
89     teardownGlobalVkEmulation();
90 }
91 
~VulkanTestHelper()92 VulkanTestHelper::~VulkanTestHelper() {
93     destroy();
94     if (mFailOnValidationErrors && validationErrorsFound) {
95         FATAL() << "Validation errors found. Aborting.";
96     }
97 }
98 
initialize(const InitializationOptions & options)99 void VulkanTestHelper::initialize(const InitializationOptions& options) {
100     initVkEmulationFeatures(std::make_unique<VkEmulationFeatures>(VkEmulationFeatures{
101         .astcLdrEmulationMode = options.astcLdrEmulationMode,
102     }));
103 
104     // Check that the validation layer is present
105     const char* validationLayer = "VK_LAYER_KHRONOS_validation";
106     uint32_t layerCount;
107     vk().vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
108     std::vector<VkLayerProperties> availableLayers(layerCount);
109     vk().vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
110 
111     bool layerFound = false;
112     for (const auto& layerProperties : availableLayers) {
113         if (strcmp(validationLayer, layerProperties.layerName) == 0) {
114             layerFound = true;
115             break;
116         }
117     }
118     if (!layerFound) FATAL() << "Vulkan Validation Layer not found";
119 
120     // Create the instance
121     VkApplicationInfo defaultAppInfo = {
122         .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
123         .pApplicationName = "vulkan_test",
124         .pEngineName = "vulkan_test",
125         .apiVersion = VK_API_VERSION_1_1,
126     };
127 
128     VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo = {
129         .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
130         .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
131                            VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
132                            VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
133         .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
134                        VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
135                        VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
136         .pfnUserCallback = validationCallback,
137     };
138 
139     std::vector<const char*> extensions = {VK_EXT_DEBUG_UTILS_EXTENSION_NAME};
140     for (const auto& extName : options.enabledExtensions) {
141         extensions.push_back(extName.c_str());
142     }
143 
144     VkInstanceCreateInfo createInfo = {
145         .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
146         .pNext = (VkDebugUtilsMessengerCreateInfoEXT*)&debugCreateInfo,
147         .pApplicationInfo = options.appInfo ? &options.appInfo.value() : &defaultAppInfo,
148         .enabledLayerCount = 1,
149         .ppEnabledLayerNames = &validationLayer,
150         .enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
151         .ppEnabledExtensionNames = extensions.data(),
152     };
153     VK_CHECK(vk().vkCreateInstance(&createInfo, nullptr, &mInstance));
154 
155     // Setup validation layer callbacks
156     VK_CHECK(vk().vkCreateDebugUtilsMessengerEXT(mInstance, &debugCreateInfo, nullptr,
157                                                  &mDebugMessenger));
158 
159     // Pick a physical device
160     uint32_t deviceCount = 0;
161     vk().vkEnumeratePhysicalDevices(mInstance, &deviceCount, nullptr);
162     if (deviceCount == 0) FATAL() << "No Vulkan device found.";
163     std::vector<VkPhysicalDevice> devices(deviceCount);
164     VK_CHECK(vk().vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data()));
165 
166     mPhysicalDevice = devices[0];
167     assert(mPhysicalDevice != VK_NULL_HANDLE);
168 
169     // Create the logical device
170     float queuePriority = 1.0f;
171     VkDeviceQueueCreateInfo queueCreateInfo = {
172         .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
173         .queueFamilyIndex = getQueueFamilyIndex(VK_QUEUE_GRAPHICS_BIT),
174         .queueCount = 1,
175         .pQueuePriorities = &queuePriority,
176     };
177 
178     VkDeviceCreateInfo deviceCreateInfo = {
179         .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
180         .queueCreateInfoCount = 1,
181         .pQueueCreateInfos = &queueCreateInfo,
182         .enabledLayerCount = 1,
183         .ppEnabledLayerNames = &validationLayer,
184         .pEnabledFeatures = &options.deviceFeatures,
185     };
186     VK_CHECK(vk().vkCreateDevice(mPhysicalDevice, &deviceCreateInfo, nullptr, &mDevice));
187 
188     // Get a graphics queue
189     vk().vkGetDeviceQueue(mDevice, queueCreateInfo.queueFamilyIndex, 0, &mGraphicsQueue);
190     assert(mGraphicsQueue != VK_NULL_HANDLE);
191 
192     // Create command pool
193     VkCommandPoolCreateInfo poolInfo{
194         .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
195         .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
196         .queueFamilyIndex = queueCreateInfo.queueFamilyIndex,
197     };
198     VK_CHECK(vk().vkCreateCommandPool(mDevice, &poolInfo, nullptr, &mCommandPool));
199 }
200 
hasValidationErrors() const201 bool VulkanTestHelper::hasValidationErrors() const { return validationErrorsFound; }
202 
beginCommandBuffer()203 VkCommandBuffer VulkanTestHelper::beginCommandBuffer() {
204     VkCommandBufferAllocateInfo allocInfo = {
205         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
206         .commandPool = unbox_VkCommandPool(mCommandPool),
207         .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
208         .commandBufferCount = 1,
209     };
210     VkCommandBuffer commandBuffer;
211     vk().vkAllocateCommandBuffers(mDevice, &allocInfo, &commandBuffer);
212 
213     VkCommandBufferBeginInfo beginInfo = {
214         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
215         .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
216     };
217     vk().vkBeginCommandBuffer(commandBuffer, &beginInfo);
218     return commandBuffer;
219 }
220 
submitCommandBuffer(VkCommandBuffer commandBuffer)221 void VulkanTestHelper::submitCommandBuffer(VkCommandBuffer commandBuffer) {
222     vk().vkEndCommandBuffer(commandBuffer);
223     auto cmdBuf = unbox_VkCommandBuffer(commandBuffer);
224 
225     VkSubmitInfo submitInfo = {
226         .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
227         .commandBufferCount = 1,
228         .pCommandBuffers = &cmdBuf,
229     };
230     vk().vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
231     vk().vkQueueWaitIdle(mGraphicsQueue);
232     vk().vkFreeCommandBuffers(mDevice, mCommandPool, 1, &cmdBuf);
233 }
234 
getQueueFamilyIndex(VkQueueFlagBits queueFlags)235 uint32_t VulkanTestHelper::getQueueFamilyIndex(VkQueueFlagBits queueFlags) {
236     uint32_t queueFamilyCount = 0;
237     vk().vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueFamilyCount, nullptr);
238 
239     std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
240     vk().vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueFamilyCount,
241                                                   queueFamilies.data());
242     for (uint32_t i = 0; i < static_cast<uint32_t>(queueFamilies.size()); i++) {
243         if (queueFamilies[i].queueFlags & queueFlags) {
244             return i;
245         }
246     }
247 
248     FATAL() << "No queue family found matching the requested flags";
249 }
250 
findMemoryType(uint32_t typeFilter,VkMemoryPropertyFlags properties)251 uint32_t VulkanTestHelper::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) {
252     VkPhysicalDeviceMemoryProperties memProperties;
253     vk().vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &memProperties);
254 
255     for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
256         if ((typeFilter & (1 << i)) &&
257             (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
258             return i;
259         }
260     }
261     FATAL() << "failed to find suitable memory type!";
262 }
263 
createBuffer(VkDeviceSize size,VkBufferUsageFlags usage,VkMemoryPropertyFlags properties,VkBuffer & buffer,VkDeviceMemory & bufferMemory)264 void VulkanTestHelper::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage,
265                                     VkMemoryPropertyFlags properties, VkBuffer& buffer,
266                                     VkDeviceMemory& bufferMemory) {
267     VkBufferCreateInfo bufferInfo = {
268         .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
269         .size = size,
270         .usage = usage,
271         .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
272     };
273     VK_CHECK(vk().vkCreateBuffer(mDevice, &bufferInfo, nullptr, &buffer));
274 
275     VkMemoryRequirements memRequirements;
276     vk().vkGetBufferMemoryRequirements(mDevice, buffer, &memRequirements);
277 
278     VkMemoryAllocateInfo allocInfo = {
279         .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
280         .allocationSize = memRequirements.size,
281         .memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties),
282     };
283     VK_CHECK(vk().vkAllocateMemory(mDevice, &allocInfo, nullptr, &bufferMemory));
284 
285     vk().vkBindBufferMemory(mDevice, buffer, bufferMemory, 0);
286 }
287 
transitionImageLayout(VkCommandBuffer cmdBuf,VkImage image,VkImageLayout oldLayout,VkImageLayout newLayout)288 void VulkanTestHelper::transitionImageLayout(VkCommandBuffer cmdBuf, VkImage image,
289                                              VkImageLayout oldLayout, VkImageLayout newLayout) {
290     VkImageMemoryBarrier barrier = {
291         .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
292         .oldLayout = oldLayout,
293         .newLayout = newLayout,
294         .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
295         .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
296         .image = unbox_VkImage(image),
297         .subresourceRange =
298             {
299                 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
300                 .baseMipLevel = 0,
301                 .levelCount = 1,
302                 .baseArrayLayer = 0,
303                 .layerCount = 1,
304             },
305     };
306 
307     switch (oldLayout) {
308         case VK_IMAGE_LAYOUT_UNDEFINED:
309             barrier.srcAccessMask = 0;
310             break;
311         case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
312             barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
313             break;
314         case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
315             barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
316             break;
317         case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
318             barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
319             break;
320         default:
321             FATAL() << "Unsupported layout transition!";
322     }
323 
324     switch (newLayout) {
325         case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
326             barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
327             break;
328         case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
329             barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
330             break;
331         case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
332             if (barrier.srcAccessMask == 0) {
333                 barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
334             }
335             barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
336             break;
337         default:
338             FATAL() << "Unsupported layout transition!";
339     }
340     vk().vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
341                               VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1,
342                               &barrier);
343 }
344 
345 }  // namespace testing
346 }  // namespace vk
347 }  // namespace gfxstream
348