| /* | 
 |  * Copyright 2016 The Android Open Source Project | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 |  | 
 | #include "driver.h" | 
 |  | 
 | namespace vulkan { | 
 | namespace driver { | 
 |  | 
 | DebugReportCallbackList::Node* DebugReportCallbackList::AddCallback( | 
 |     const VkDebugReportCallbackCreateInfoEXT& info, | 
 |     VkDebugReportCallbackEXT driver_handle, | 
 |     const VkAllocationCallbacks& allocator) { | 
 |     void* mem = allocator.pfnAllocation(allocator.pUserData, sizeof(Node), | 
 |                                         alignof(Node), | 
 |                                         VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); | 
 |     if (!mem) | 
 |         return nullptr; | 
 |  | 
 |     // initialize and prepend node to the list | 
 |     std::lock_guard<decltype(rwmutex_)> lock(rwmutex_); | 
 |     head_.next = new (mem) Node{head_.next, info.flags, info.pfnCallback, | 
 |                                 info.pUserData, driver_handle}; | 
 |  | 
 |     return head_.next; | 
 | } | 
 |  | 
 | void DebugReportCallbackList::RemoveCallback( | 
 |     Node* node, | 
 |     const VkAllocationCallbacks& allocator) { | 
 |     // remove node from the list | 
 |     { | 
 |         std::lock_guard<decltype(rwmutex_)> lock(rwmutex_); | 
 |         Node* prev = &head_; | 
 |         while (prev && prev->next != node) | 
 |             prev = prev->next; | 
 |         if (prev) | 
 |             prev->next = node->next; | 
 |     } | 
 |  | 
 |     allocator.pfnFree(allocator.pUserData, node); | 
 | } | 
 |  | 
 | void DebugReportCallbackList::Message(VkDebugReportFlagsEXT flags, | 
 |                                       VkDebugReportObjectTypeEXT object_type, | 
 |                                       uint64_t object, | 
 |                                       size_t location, | 
 |                                       int32_t message_code, | 
 |                                       const char* layer_prefix, | 
 |                                       const char* message) const { | 
 |     std::shared_lock<decltype(rwmutex_)> lock(rwmutex_); | 
 |     const Node* node = &head_; | 
 |     while ((node = node->next)) { | 
 |         if ((node->flags & flags) != 0) { | 
 |             node->callback(flags, object_type, object, location, message_code, | 
 |                            layer_prefix, message, node->user_data); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | void DebugReportLogger::Message(VkDebugReportFlagsEXT flags, | 
 |                                 VkDebugReportObjectTypeEXT object_type, | 
 |                                 uint64_t object, | 
 |                                 size_t location, | 
 |                                 int32_t message_code, | 
 |                                 const char* layer_prefix, | 
 |                                 const char* message) const { | 
 |     const VkDebugReportCallbackCreateInfoEXT* info = | 
 |         reinterpret_cast<const VkDebugReportCallbackCreateInfoEXT*>( | 
 |             instance_pnext_); | 
 |     while (info) { | 
 |         if (info->sType == | 
 |                 VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT && | 
 |             (info->flags & flags) != 0) { | 
 |             info->pfnCallback(flags, object_type, object, location, | 
 |                               message_code, layer_prefix, message, | 
 |                               info->pUserData); | 
 |         } | 
 |  | 
 |         info = reinterpret_cast<const VkDebugReportCallbackCreateInfoEXT*>( | 
 |             info->pNext); | 
 |     } | 
 |  | 
 |     if (callbacks_) { | 
 |         callbacks_->Message(flags, object_type, object, location, message_code, | 
 |                             layer_prefix, message); | 
 |     } | 
 | } | 
 |  | 
 | void DebugReportLogger::PrintV(VkDebugReportFlagsEXT flags, | 
 |                                VkDebugReportObjectTypeEXT object_type, | 
 |                                uint64_t object, | 
 |                                const char* format, | 
 |                                va_list ap) const { | 
 |     char buf[1024]; | 
 |     int len = vsnprintf(buf, sizeof(buf), format, ap); | 
 |  | 
 |     // message truncated | 
 |     if (len >= static_cast<int>(sizeof(buf))) | 
 |         memcpy(buf + sizeof(buf) - 4, "...", 4); | 
 |  | 
 |     Message(flags, object_type, object, 0, 0, LOG_TAG, buf); | 
 | } | 
 |  | 
 | VkResult CreateDebugReportCallbackEXT( | 
 |     VkInstance instance, | 
 |     const VkDebugReportCallbackCreateInfoEXT* create_info, | 
 |     const VkAllocationCallbacks* allocator, | 
 |     VkDebugReportCallbackEXT* callback) { | 
 |     const auto& driver = GetData(instance).driver; | 
 |     VkDebugReportCallbackEXT driver_handle = VK_NULL_HANDLE; | 
 |     if (driver.CreateDebugReportCallbackEXT) { | 
 |         VkResult result = driver.CreateDebugReportCallbackEXT( | 
 |             instance, create_info, allocator, &driver_handle); | 
 |         if (result != VK_SUCCESS) | 
 |             return result; | 
 |     } | 
 |  | 
 |     auto& callbacks = GetData(instance).debug_report_callbacks; | 
 |     auto node = callbacks.AddCallback( | 
 |         *create_info, driver_handle, | 
 |         (allocator) ? *allocator : GetData(instance).allocator); | 
 |     if (!node) { | 
 |         if (driver_handle != VK_NULL_HANDLE) { | 
 |             driver.DestroyDebugReportCallbackEXT(instance, driver_handle, | 
 |                                                  allocator); | 
 |         } | 
 |  | 
 |         return VK_ERROR_OUT_OF_HOST_MEMORY; | 
 |     } | 
 |  | 
 |     *callback = callbacks.GetHandle(node); | 
 |  | 
 |     return VK_SUCCESS; | 
 | } | 
 |  | 
 | void DestroyDebugReportCallbackEXT(VkInstance instance, | 
 |                                    VkDebugReportCallbackEXT callback, | 
 |                                    const VkAllocationCallbacks* allocator) { | 
 |     if (callback == VK_NULL_HANDLE) | 
 |         return; | 
 |  | 
 |     auto& callbacks = GetData(instance).debug_report_callbacks; | 
 |     auto node = callbacks.FromHandle(callback); | 
 |     auto driver_handle = callbacks.GetDriverHandle(node); | 
 |  | 
 |     callbacks.RemoveCallback( | 
 |         node, (allocator) ? *allocator : GetData(instance).allocator); | 
 |  | 
 |     if (driver_handle != VK_NULL_HANDLE) { | 
 |         GetData(instance).driver.DestroyDebugReportCallbackEXT( | 
 |             instance, driver_handle, allocator); | 
 |     } | 
 | } | 
 |  | 
 | void DebugReportMessageEXT(VkInstance instance, | 
 |                            VkDebugReportFlagsEXT flags, | 
 |                            VkDebugReportObjectTypeEXT object_type, | 
 |                            uint64_t object, | 
 |                            size_t location, | 
 |                            int32_t message_code, | 
 |                            const char* layer_prefix, | 
 |                            const char* message) { | 
 |     if (GetData(instance).driver.DebugReportMessageEXT) { | 
 |         GetData(instance).driver.DebugReportMessageEXT( | 
 |             instance, flags, object_type, object, location, message_code, | 
 |             layer_prefix, message); | 
 |     } else { | 
 |         GetData(instance).debug_report_callbacks.Message( | 
 |             flags, object_type, object, location, message_code, layer_prefix, | 
 |             message); | 
 |     } | 
 | } | 
 |  | 
 | }  // namespace driver | 
 | }  // namespace vulkan |