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, false)),
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;
__anon72f59cc40202(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