blob: 26b1377b14a62fbd39cd8df0cf4e3d6e44545786 [file] [log] [blame]
Jesse Halld02edcb2015-09-08 07:44:48 -07001/*
2 * Copyright 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Jesse Hall04f4f472015-08-16 19:51:04 -070017// module header
18#include "loader.h"
19// standard C headers
20#include <inttypes.h>
21#include <malloc.h>
22#include <pthread.h>
23#include <string.h>
24// standard C++ headers
25#include <algorithm>
26#include <mutex>
27// platform/library headers
28#include <hardware/hwvulkan.h>
29#include <log/log.h>
30
31using namespace vulkan;
32
33static const uint32_t kMaxPhysicalDevices = 4;
34
35struct VkInstance_T {
36 VkInstance_T(const VkAllocCallbacks* alloc_callbacks)
37 : vtbl(&vtbl_storage), alloc(alloc_callbacks), num_physical_devices(0) {
38 memset(&vtbl_storage, 0, sizeof(vtbl_storage));
39 memset(physical_devices, 0, sizeof(physical_devices));
40 memset(&drv.vtbl, 0, sizeof(drv.vtbl));
41 drv.GetDeviceProcAddr = nullptr;
42 drv.num_physical_devices = 0;
43 }
44
45 InstanceVtbl* vtbl;
46 InstanceVtbl vtbl_storage;
47
48 const VkAllocCallbacks* alloc;
49 uint32_t num_physical_devices;
50 VkPhysicalDevice physical_devices[kMaxPhysicalDevices];
51
52 struct Driver {
53 // Pointers to driver entry points. Used explicitly by the loader; not
54 // set as the dispatch table for any objects.
55 InstanceVtbl vtbl;
56
57 // Pointer to the driver's get_device_proc_addr, must be valid for any
58 // of the driver's physical devices. Not part of the InstanceVtbl since
59 // it's not an Instance/PhysicalDevice function.
60 PFN_vkGetDeviceProcAddr GetDeviceProcAddr;
61
62 // Number of physical devices owned by this driver.
63 uint32_t num_physical_devices;
64 } drv; // may eventually be an array
65};
66
67// -----------------------------------------------------------------------------
68
69namespace {
70
71typedef VkInstance_T Instance;
72
73struct Device {
74 Device(const VkAllocCallbacks* alloc_callbacks) : alloc(alloc_callbacks) {
75 memset(&vtbl_storage, 0, sizeof(vtbl_storage));
76 vtbl_storage.device = this;
77 }
78 DeviceVtbl vtbl_storage;
79 const VkAllocCallbacks* alloc;
80};
81
82// -----------------------------------------------------------------------------
83// Utility Code
84
85inline const InstanceVtbl* GetVtbl(VkPhysicalDevice physicalDevice) {
86 return *reinterpret_cast<InstanceVtbl**>(physicalDevice);
87}
88
89inline const DeviceVtbl* GetVtbl(VkDevice device) {
90 return *reinterpret_cast<DeviceVtbl**>(device);
91}
92
93void* DefaultAlloc(void*, size_t size, size_t alignment, VkSystemAllocType) {
94 return memalign(alignment, size);
95}
96
97void DefaultFree(void*, void* pMem) {
98 free(pMem);
99}
100
101const VkAllocCallbacks kDefaultAllocCallbacks = {
102 .pUserData = nullptr,
103 .pfnAlloc = DefaultAlloc,
104 .pfnFree = DefaultFree,
105};
106
107hwvulkan_device_t* g_hwdevice;
108bool EnsureInitialized() {
109 static std::once_flag once_flag;
110 static const hwvulkan_module_t* module;
111
112 std::call_once(once_flag, []() {
113 int result;
114 result = hw_get_module("vulkan",
115 reinterpret_cast<const hw_module_t**>(&module));
116 if (result != 0) {
117 ALOGE("failed to load vulkan hal: %s (%d)", strerror(-result),
118 result);
119 return;
120 }
121 result = module->common.methods->open(
122 &module->common, HWVULKAN_DEVICE_0,
123 reinterpret_cast<hw_device_t**>(&g_hwdevice));
124 if (result != 0) {
125 ALOGE("failed to open vulkan driver: %s (%d)", strerror(-result),
126 result);
127 module = nullptr;
128 return;
129 }
130 });
131
132 return module != nullptr && g_hwdevice != nullptr;
133}
134
135void DestroyDevice(Device* device) {
136 const VkAllocCallbacks* alloc = device->alloc;
137 device->~Device();
138 alloc->pfnFree(alloc->pUserData, device);
139}
140
141// -----------------------------------------------------------------------------
142// "Bottom" functions. These are called at the end of the instance dispatch
143// chain.
144
145VkResult DestroyInstanceBottom(VkInstance instance) {
146 // These checks allow us to call DestroyInstanceBottom from any error path
147 // in CreateInstanceBottom, before the driver instance is fully initialized.
148 if (instance->drv.vtbl.instance != VK_NULL_HANDLE &&
149 instance->drv.vtbl.DestroyInstance) {
150 instance->drv.vtbl.DestroyInstance(instance->drv.vtbl.instance);
151 }
152 const VkAllocCallbacks* alloc = instance->alloc;
153 instance->~VkInstance_T();
154 alloc->pfnFree(alloc->pUserData, instance);
155 return VK_SUCCESS;
156}
157
158VkResult CreateInstanceBottom(const VkInstanceCreateInfo* create_info,
159 VkInstance* instance_ptr) {
160 Instance* instance = *instance_ptr;
161 VkResult result;
162
163 result =
164 g_hwdevice->CreateInstance(create_info, &instance->drv.vtbl.instance);
165 if (result != VK_SUCCESS) {
166 DestroyInstanceBottom(instance);
167 return result;
168 }
169
170 if (!LoadInstanceVtbl(instance->drv.vtbl.instance,
171 g_hwdevice->GetInstanceProcAddr,
172 instance->drv.vtbl)) {
173 DestroyInstanceBottom(instance);
174 return VK_ERROR_INITIALIZATION_FAILED;
175 }
176
177 // vkGetDeviceProcAddr has a bootstrapping problem. We require that it be
178 // queryable from the Instance, and that the resulting function work for any
179 // VkDevice created from the instance.
180 instance->drv.GetDeviceProcAddr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(
181 g_hwdevice->GetInstanceProcAddr(instance->drv.vtbl.instance,
182 "vkGetDeviceProcAddr"));
183 if (!instance->drv.GetDeviceProcAddr) {
184 ALOGE("missing instance proc: \"%s\"", "vkGetDeviceProcAddr");
185 DestroyInstanceBottom(instance);
186 return VK_ERROR_INITIALIZATION_FAILED;
187 }
188
189 hwvulkan_dispatch_t* dispatch =
190 reinterpret_cast<hwvulkan_dispatch_t*>(instance->drv.vtbl.instance);
191 if (dispatch->magic == HWVULKAN_DISPATCH_MAGIC) {
192 // Skip setting dispatch->vtbl on the driver instance handle, since we
193 // never intentionally call through it; we go through Instance::drv.vtbl
194 // instead.
195 } else {
196 ALOGE("invalid VkInstance dispatch magic: 0x%" PRIxPTR,
197 dispatch->magic);
198 DestroyInstanceBottom(instance);
199 return VK_ERROR_INITIALIZATION_FAILED;
200 }
201
202 uint32_t num_physical_devices = 0;
203 result = instance->drv.vtbl.EnumeratePhysicalDevices(
204 instance->drv.vtbl.instance, &num_physical_devices, nullptr);
205 if (result != VK_SUCCESS) {
206 DestroyInstanceBottom(instance);
207 return VK_ERROR_INITIALIZATION_FAILED;
208 }
209 num_physical_devices = std::min(num_physical_devices, kMaxPhysicalDevices);
210 result = instance->drv.vtbl.EnumeratePhysicalDevices(
211 instance->drv.vtbl.instance, &num_physical_devices,
212 instance->physical_devices);
213 if (result != VK_SUCCESS) {
214 DestroyInstanceBottom(instance);
215 return VK_ERROR_INITIALIZATION_FAILED;
216 }
217 for (uint32_t i = 0; i < num_physical_devices; i++) {
218 dispatch = reinterpret_cast<hwvulkan_dispatch_t*>(
219 instance->physical_devices[i]);
220 if (dispatch->magic != HWVULKAN_DISPATCH_MAGIC) {
221 ALOGE("invalid VkPhysicalDevice dispatch magic: 0x%" PRIxPTR,
222 dispatch->magic);
223 DestroyInstanceBottom(instance);
224 return VK_ERROR_INITIALIZATION_FAILED;
225 }
226 dispatch->vtbl = instance->vtbl;
227 }
228 instance->drv.num_physical_devices = num_physical_devices;
229
230 instance->num_physical_devices = instance->drv.num_physical_devices;
231 return VK_SUCCESS;
232}
233
234VkResult EnumeratePhysicalDevicesBottom(VkInstance instance,
235 uint32_t* pdev_count,
236 VkPhysicalDevice* pdevs) {
237 uint32_t count = instance->num_physical_devices;
238 if (pdevs) {
239 count = std::min(count, *pdev_count);
240 std::copy(instance->physical_devices,
241 instance->physical_devices + count, pdevs);
242 }
243 *pdev_count = count;
244 return VK_SUCCESS;
245}
246
247VkResult GetPhysicalDeviceFeaturesBottom(VkPhysicalDevice pdev,
248 VkPhysicalDeviceFeatures* features) {
249 return GetVtbl(pdev)
250 ->instance->drv.vtbl.GetPhysicalDeviceFeatures(pdev, features);
251}
252
253VkResult GetPhysicalDeviceFormatPropertiesBottom(
254 VkPhysicalDevice pdev,
255 VkFormat format,
256 VkFormatProperties* properties) {
257 return GetVtbl(pdev)->instance->drv.vtbl.GetPhysicalDeviceFormatProperties(
258 pdev, format, properties);
259}
260
261VkResult GetPhysicalDeviceImageFormatPropertiesBottom(
262 VkPhysicalDevice pdev,
263 VkFormat format,
264 VkImageType type,
265 VkImageTiling tiling,
266 VkImageUsageFlags usage,
267 VkImageFormatProperties* properties) {
268 return GetVtbl(pdev)
269 ->instance->drv.vtbl.GetPhysicalDeviceImageFormatProperties(
270 pdev, format, type, tiling, usage, properties);
271}
272
273VkResult GetPhysicalDeviceLimitsBottom(VkPhysicalDevice pdev,
274 VkPhysicalDeviceLimits* limits) {
275 return GetVtbl(pdev)
276 ->instance->drv.vtbl.GetPhysicalDeviceLimits(pdev, limits);
277}
278
279VkResult GetPhysicalDevicePropertiesBottom(
280 VkPhysicalDevice pdev,
281 VkPhysicalDeviceProperties* properties) {
282 return GetVtbl(pdev)
283 ->instance->drv.vtbl.GetPhysicalDeviceProperties(pdev, properties);
284}
285
286VkResult GetPhysicalDeviceQueueCountBottom(VkPhysicalDevice pdev,
287 uint32_t* count) {
288 return GetVtbl(pdev)
289 ->instance->drv.vtbl.GetPhysicalDeviceQueueCount(pdev, count);
290}
291
292VkResult GetPhysicalDeviceQueuePropertiesBottom(
293 VkPhysicalDevice pdev,
294 uint32_t count,
295 VkPhysicalDeviceQueueProperties* properties) {
296 return GetVtbl(pdev)->instance->drv.vtbl.GetPhysicalDeviceQueueProperties(
297 pdev, count, properties);
298}
299
300VkResult GetPhysicalDeviceMemoryPropertiesBottom(
301 VkPhysicalDevice pdev,
302 VkPhysicalDeviceMemoryProperties* properties) {
303 return GetVtbl(pdev)->instance->drv.vtbl.GetPhysicalDeviceMemoryProperties(
304 pdev, properties);
305}
306
307VkResult CreateDeviceBottom(VkPhysicalDevice pdev,
308 const VkDeviceCreateInfo* create_info,
309 VkDevice* out_device) {
310 const Instance& instance = *static_cast<Instance*>(GetVtbl(pdev)->instance);
311 VkResult result;
312
313 void* mem = instance.alloc->pfnAlloc(instance.alloc->pUserData,
314 sizeof(Device), alignof(Device),
315 VK_SYSTEM_ALLOC_TYPE_API_OBJECT);
316 if (!mem)
317 return VK_ERROR_OUT_OF_HOST_MEMORY;
318 Device* device = new (mem) Device(instance.alloc);
319
320 VkDevice drv_device;
321 result = instance.drv.vtbl.CreateDevice(pdev, create_info, &drv_device);
322 if (result != VK_SUCCESS) {
323 DestroyDevice(device);
324 return result;
325 }
326
327 if (!LoadDeviceVtbl(drv_device, instance.drv.GetDeviceProcAddr,
328 device->vtbl_storage)) {
329 if (device->vtbl_storage.DestroyDevice)
330 device->vtbl_storage.DestroyDevice(drv_device);
331 DestroyDevice(device);
332 return VK_ERROR_INITIALIZATION_FAILED;
333 }
334
335 hwvulkan_dispatch_t* dispatch =
336 reinterpret_cast<hwvulkan_dispatch_t*>(drv_device);
337 if (dispatch->magic != HWVULKAN_DISPATCH_MAGIC) {
338 ALOGE("invalid VkDevice dispatch magic: 0x%" PRIxPTR, dispatch->magic);
339 device->vtbl_storage.DestroyDevice(drv_device);
340 DestroyDevice(device);
341 return VK_ERROR_INITIALIZATION_FAILED;
342 }
343 dispatch->vtbl = &device->vtbl_storage;
344
Jesse Hallb1352bc2015-09-04 16:12:33 -0700345 device->vtbl_storage.GetSurfacePropertiesKHR = GetSurfacePropertiesKHR;
346 device->vtbl_storage.GetSurfaceFormatsKHR = GetSurfaceFormatsKHR;
347 device->vtbl_storage.GetSurfacePresentModesKHR = GetSurfacePresentModesKHR;
348 device->vtbl_storage.CreateSwapchainKHR = CreateSwapchainKHR;
349 device->vtbl_storage.DestroySwapchainKHR = DestroySwapchainKHR;
350 device->vtbl_storage.GetSwapchainImagesKHR = GetSwapchainImagesKHR;
351 device->vtbl_storage.AcquireNextImageKHR = AcquireNextImageKHR;
352 device->vtbl_storage.QueuePresentKHR = QueuePresentKHR;
353
Jesse Hall04f4f472015-08-16 19:51:04 -0700354 // TODO: insert device layer entry points into device->vtbl_storage here?
355
356 *out_device = drv_device;
357 return VK_SUCCESS;
358}
359
360VkResult GetPhysicalDeviceExtensionPropertiesBottom(
361 VkPhysicalDevice pdev,
362 const char* layer_name,
363 uint32_t* properties_count,
364 VkExtensionProperties* properties) {
365 // TODO: what are we supposed to do with layer_name here?
366 return GetVtbl(pdev)
367 ->instance->drv.vtbl.GetPhysicalDeviceExtensionProperties(
368 pdev, layer_name, properties_count, properties);
369}
370
371VkResult GetPhysicalDeviceLayerPropertiesBottom(VkPhysicalDevice pdev,
372 uint32_t* properties_count,
373 VkLayerProperties* properties) {
374 return GetVtbl(pdev)->instance->drv.vtbl.GetPhysicalDeviceLayerProperties(
375 pdev, properties_count, properties);
376}
377
378VkResult GetPhysicalDeviceSparseImageFormatPropertiesBottom(
379 VkPhysicalDevice pdev,
380 VkFormat format,
381 VkImageType type,
382 uint32_t samples,
383 VkImageUsageFlags usage,
384 VkImageTiling tiling,
385 uint32_t* properties_count,
386 VkSparseImageFormatProperties* properties) {
387 return GetVtbl(pdev)
388 ->instance->drv.vtbl.GetPhysicalDeviceSparseImageFormatProperties(
389 pdev, format, type, samples, usage, tiling, properties_count,
390 properties);
391}
392
393PFN_vkVoidFunction GetInstanceProcAddrBottom(VkInstance, const char*);
394
395const InstanceVtbl kBottomInstanceFunctions = {
396 // clang-format off
397 .instance = nullptr,
398 .CreateInstance = CreateInstanceBottom,
399 .DestroyInstance = DestroyInstanceBottom,
400 .GetInstanceProcAddr = GetInstanceProcAddrBottom,
401 .EnumeratePhysicalDevices = EnumeratePhysicalDevicesBottom,
402 .GetPhysicalDeviceFeatures = GetPhysicalDeviceFeaturesBottom,
403 .GetPhysicalDeviceFormatProperties = GetPhysicalDeviceFormatPropertiesBottom,
404 .GetPhysicalDeviceImageFormatProperties = GetPhysicalDeviceImageFormatPropertiesBottom,
405 .GetPhysicalDeviceLimits = GetPhysicalDeviceLimitsBottom,
406 .GetPhysicalDeviceProperties = GetPhysicalDevicePropertiesBottom,
407 .GetPhysicalDeviceQueueCount = GetPhysicalDeviceQueueCountBottom,
408 .GetPhysicalDeviceQueueProperties = GetPhysicalDeviceQueuePropertiesBottom,
409 .GetPhysicalDeviceMemoryProperties = GetPhysicalDeviceMemoryPropertiesBottom,
410 .CreateDevice = CreateDeviceBottom,
411 .GetPhysicalDeviceExtensionProperties = GetPhysicalDeviceExtensionPropertiesBottom,
412 .GetPhysicalDeviceLayerProperties = GetPhysicalDeviceLayerPropertiesBottom,
413 .GetPhysicalDeviceSparseImageFormatProperties = GetPhysicalDeviceSparseImageFormatPropertiesBottom,
Jesse Hallb1352bc2015-09-04 16:12:33 -0700414 .GetPhysicalDeviceSurfaceSupportKHR = GetPhysicalDeviceSurfaceSupportKHR,
Jesse Hall04f4f472015-08-16 19:51:04 -0700415 // clang-format on
416};
417
418PFN_vkVoidFunction GetInstanceProcAddrBottom(VkInstance, const char* name) {
419 // The bottom GetInstanceProcAddr is only called by the innermost layer,
420 // when there is one, when it initializes its own dispatch table.
421 return GetSpecificInstanceProcAddr(&kBottomInstanceFunctions, name);
422}
423
424} // namespace
425
426// -----------------------------------------------------------------------------
427// Global functions. These are called directly from the loader entry points,
428// without going through a dispatch table.
429
430namespace vulkan {
431
432VkResult GetGlobalExtensionProperties(const char* /*layer_name*/,
433 uint32_t* count,
434 VkExtensionProperties* /*properties*/) {
435 if (!count)
436 return VK_ERROR_INVALID_POINTER;
437 if (!EnsureInitialized())
438 return VK_ERROR_UNAVAILABLE;
439
440 // TODO: not yet implemented
441 ALOGW("vkGetGlobalExtensionProperties not implemented");
442
443 *count = 0;
444 return VK_SUCCESS;
445}
446
447VkResult GetGlobalLayerProperties(uint32_t* count,
448 VkLayerProperties* /*properties*/) {
449 if (!count)
450 return VK_ERROR_INVALID_POINTER;
451 if (!EnsureInitialized())
452 return VK_ERROR_UNAVAILABLE;
453
454 // TODO: not yet implemented
455 ALOGW("vkGetGlobalLayerProperties not implemented");
456
457 *count = 0;
458 return VK_SUCCESS;
459}
460
461VkResult CreateInstance(const VkInstanceCreateInfo* create_info,
462 VkInstance* out_instance) {
463 VkResult result;
464
465 if (!EnsureInitialized())
466 return VK_ERROR_UNAVAILABLE;
467
468 VkInstanceCreateInfo local_create_info = *create_info;
469 if (!local_create_info.pAllocCb)
470 local_create_info.pAllocCb = &kDefaultAllocCallbacks;
471 create_info = &local_create_info;
472
473 void* instance_mem = create_info->pAllocCb->pfnAlloc(
474 create_info->pAllocCb->pUserData, sizeof(Instance), alignof(Instance),
475 VK_SYSTEM_ALLOC_TYPE_API_OBJECT);
476 if (!instance_mem)
477 return VK_ERROR_OUT_OF_HOST_MEMORY;
478 Instance* instance = new (instance_mem) Instance(create_info->pAllocCb);
479
480 instance->vtbl_storage = kBottomInstanceFunctions;
481 instance->vtbl_storage.instance = instance;
482
483 // TODO: Insert enabled layers into instance->dispatch_vtbl here.
484
485 // TODO: We'll want to call CreateInstance through the dispatch table
486 // instead of calling the loader's terminator
487 *out_instance = instance;
488 result = CreateInstanceBottom(create_info, out_instance);
489 if (result <= 0) {
490 // For every layer, including the loader top and bottom layers:
491 // - If a call to the next CreateInstance fails, the layer must clean
492 // up anything it has successfully done so far, and propagate the
493 // error upwards.
494 // - If a layer successfully calls the next layer's CreateInstance, and
495 // afterwards must fail for some reason, it must call the next layer's
496 // DestroyInstance before returning.
497 // - The layer must not call the next layer's DestroyInstance if that
498 // layer's CreateInstance wasn't called, or returned failure.
499
500 // On failure, CreateInstanceBottom frees the instance struct, so it's
501 // already gone at this point. Nothing to do.
502 }
503
504 return result;
505}
506
507PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* name) {
508 if (!instance)
509 return GetGlobalInstanceProcAddr(name);
510 // For special-case functions we always return the loader entry
511 if (strcmp(name, "vkGetInstanceProcAddr") == 0 ||
512 strcmp(name, "vkGetDeviceProcAddr") == 0) {
513 return GetGlobalInstanceProcAddr(name);
514 }
515 return GetSpecificInstanceProcAddr(instance->vtbl, name);
516}
517
518PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* name) {
519 if (!device)
520 return GetGlobalDeviceProcAddr(name);
521 // For special-case functions we always return the loader entry
522 if (strcmp(name, "vkGetDeviceQueue") == 0 ||
Jesse Hallc7a6eb52015-08-31 12:52:03 -0700523 strcmp(name, "vkCreateCommandBuffer") == 0 ||
Jesse Hall04f4f472015-08-16 19:51:04 -0700524 strcmp(name, "vkDestroyDevice") == 0) {
525 return GetGlobalDeviceProcAddr(name);
526 }
527 return GetSpecificDeviceProcAddr(GetVtbl(device), name);
528}
529
530VkResult GetDeviceQueue(VkDevice drv_device,
531 uint32_t family,
532 uint32_t index,
533 VkQueue* out_queue) {
534 VkResult result;
535 VkQueue queue;
536 const DeviceVtbl* vtbl = GetVtbl(drv_device);
537 result = vtbl->GetDeviceQueue(drv_device, family, index, &queue);
538 if (result != VK_SUCCESS)
539 return result;
540 hwvulkan_dispatch_t* dispatch =
541 reinterpret_cast<hwvulkan_dispatch_t*>(queue);
542 if (dispatch->magic != HWVULKAN_DISPATCH_MAGIC && dispatch->vtbl != &vtbl) {
543 ALOGE("invalid VkQueue dispatch magic: 0x%" PRIxPTR, dispatch->magic);
544 return VK_ERROR_INITIALIZATION_FAILED;
545 }
546 dispatch->vtbl = vtbl;
547 *out_queue = queue;
548 return VK_SUCCESS;
549}
550
Jesse Hallc7a6eb52015-08-31 12:52:03 -0700551VkResult CreateCommandBuffer(VkDevice drv_device,
552 const VkCmdBufferCreateInfo* create_info,
553 VkCmdBuffer* out_cmdbuf) {
554 const DeviceVtbl* vtbl = GetVtbl(drv_device);
555 VkCmdBuffer cmdbuf;
556 VkResult result =
557 vtbl->CreateCommandBuffer(drv_device, create_info, &cmdbuf);
558 if (result != VK_SUCCESS)
559 return result;
560 hwvulkan_dispatch_t* dispatch =
561 reinterpret_cast<hwvulkan_dispatch_t*>(cmdbuf);
562 if (dispatch->magic != HWVULKAN_DISPATCH_MAGIC) {
563 ALOGE("invalid VkCmdBuffer dispatch magic: 0x%" PRIxPTR,
564 dispatch->magic);
565 return VK_ERROR_INITIALIZATION_FAILED;
566 }
567 dispatch->vtbl = vtbl;
568 *out_cmdbuf = cmdbuf;
569 return VK_SUCCESS;
570}
571
Jesse Hall04f4f472015-08-16 19:51:04 -0700572VkResult DestroyDevice(VkDevice drv_device) {
573 const DeviceVtbl* vtbl = GetVtbl(drv_device);
574 Device* device = static_cast<Device*>(vtbl->device);
575 vtbl->DestroyDevice(drv_device);
576 DestroyDevice(device);
577 return VK_SUCCESS;
578}
579
580} // namespace vulkan