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