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