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