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