1// Copyright (c) 2017-2023, The Khronos Group Inc.
2// Copyright (c) 2017-2019 Valve Corporation
3// Copyright (c) 2017-2019 LunarG, Inc.
4// Copyright (c) 2019 Collabora, Ltd.
5//
6// SPDX-License-Identifier: Apache-2.0 OR MIT
7//
8// Initial Authors: Mark Young <marky@lunarg.com>
9// Ryan Pavlik <ryan.pavlik@collabora.com>
10// Dave Houlton <daveh@lunarg.com>
11//
12
13#include "object_info.h"
14
15#include "extra_algorithms.h"
16#include "hex_and_handles.h"
17
18#include <openxr/openxr.h>
19
20#include <algorithm>
21#include <iterator>
22#include <memory>
23#include <sstream>
24#include <string>
25#include <vector>
26
27#include "memory.h"
28
29std::string XrSdkLogObjectInfo::ToString() const {
30 std::ostringstream oss;
31 oss << Uint64ToHexString(handle);
32 if (!name.empty()) {
33 oss << " (" << name << ")";
34 }
35 return oss.str();
36}
37
38void ObjectInfoCollection::AddObjectName(uint64_t object_handle, XrObjectType object_type, const std::string& object_name) {
39 // If name is empty, we should erase it
40 if (object_name.empty()) {
41 RemoveObject(object_handle, object_type);
42 return;
43 }
44
45 // Otherwise, add it or update the name
46 XrSdkLogObjectInfo new_obj = {object_handle, object_type};
47
48 // If it already exists, update the name
49 auto lookup_info = LookUpStoredObjectInfo(new_obj);
50 if (lookup_info != nullptr) {
51 lookup_info->name = object_name;
52 return;
53 }
54
55 // It doesn't exist, so add a new info block
56 new_obj.name = object_name;
57 object_info_.push_back(new_obj);
58}
59
60void ObjectInfoCollection::RemoveObject(uint64_t object_handle, XrObjectType object_type) {
61 vector_remove_if_and_erase(
62 object_info_, [=](XrSdkLogObjectInfo const& info) { return info.handle == object_handle && info.type == object_type; });
63}
64
65XrSdkLogObjectInfo const* ObjectInfoCollection::LookUpStoredObjectInfo(XrSdkLogObjectInfo const& info) const {
66 auto e = object_info_.end();
67 auto it = std::find_if(object_info_.begin(), e, [&](XrSdkLogObjectInfo const& stored) { return Equivalent(stored, info); });
68 if (it != e) {
69 return &(*it);
70 }
71 return nullptr;
72}
73
74XrSdkLogObjectInfo* ObjectInfoCollection::LookUpStoredObjectInfo(XrSdkLogObjectInfo const& info) {
75 auto e = object_info_.end();
76 auto it = std::find_if(object_info_.begin(), e, [&](XrSdkLogObjectInfo const& stored) { return Equivalent(stored, info); });
77 if (it != e) {
78 return &(*it);
79 }
80 return nullptr;
81}
82
83bool ObjectInfoCollection::LookUpObjectName(XrDebugUtilsObjectNameInfoEXT& info) const {
84 auto info_lookup = LookUpStoredObjectInfo(info.objectHandle, info.objectType);
85 if (info_lookup != nullptr) {
86 info.objectName = info_lookup->name.c_str();
87 return true;
88 }
89 return false;
90}
91
92bool ObjectInfoCollection::LookUpObjectName(XrSdkLogObjectInfo& info) const {
93 auto info_lookup = LookUpStoredObjectInfo(info);
94 if (info_lookup != nullptr) {
95 info.name = info_lookup->name;
96 return true;
97 }
98 return false;
99}
100
101static std::vector<XrDebugUtilsObjectNameInfoEXT> PopulateObjectNameInfo(std::vector<XrSdkLogObjectInfo> const& obj) {
102 std::vector<XrDebugUtilsObjectNameInfoEXT> ret;
103 ret.reserve(obj.size());
104 std::transform(obj.begin(), obj.end(), std::back_inserter(ret), [](XrSdkLogObjectInfo const& info) {
105 return XrDebugUtilsObjectNameInfoEXT{XR_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, nullptr, info.type, info.handle,
106 info.name.c_str()};
107 });
108 return ret;
109}
110
111NamesAndLabels::NamesAndLabels(std::vector<XrSdkLogObjectInfo> obj, std::vector<XrDebugUtilsLabelEXT> lab)
112 : sdk_objects(std::move(obj)), objects(PopulateObjectNameInfo(sdk_objects)), labels(std::move(lab)) {}
113
114void NamesAndLabels::PopulateCallbackData(XrDebugUtilsMessengerCallbackDataEXT& callback_data) const {
115 callback_data.objects = objects.empty() ? nullptr : const_cast<XrDebugUtilsObjectNameInfoEXT*>(objects.data());
116 callback_data.objectCount = static_cast<uint32_t>(objects.size());
117 callback_data.sessionLabels = labels.empty() ? nullptr : const_cast<XrDebugUtilsLabelEXT*>(labels.data());
118 callback_data.sessionLabelCount = static_cast<uint32_t>(labels.size());
119}
120
121void DebugUtilsData::LookUpSessionLabels(XrSession session, std::vector<XrDebugUtilsLabelEXT>& labels) const {
122 auto session_label_iterator = session_labels_.find(session);
123 if (session_label_iterator != session_labels_.end()) {
124 auto& XrSdkSessionLabels = *session_label_iterator->second;
125 // Copy the debug utils labels in reverse order in the the labels vector.
126 std::transform(XrSdkSessionLabels.rbegin(), XrSdkSessionLabels.rend(), std::back_inserter(labels),
127 [](XrSdkSessionLabelPtr const& label) { return label->debug_utils_label; });
128 }
129}
130
131XrSdkSessionLabel::XrSdkSessionLabel(const XrDebugUtilsLabelEXT& label_info, bool individual)
132 : label_name(label_info.labelName), debug_utils_label(label_info), is_individual_label(individual) {
133 // Update the c string pointer to the one we hold.
134 debug_utils_label.labelName = label_name.c_str();
135 // Zero out the next pointer to avoid a dangling pointer
136 debug_utils_label.next = nullptr;
137}
138
139XrSdkSessionLabelPtr XrSdkSessionLabel::make(const XrDebugUtilsLabelEXT& label_info, bool individual) {
140 XrSdkSessionLabelPtr ret(new XrSdkSessionLabel(label_info, individual));
141 return ret;
142}
143void DebugUtilsData::AddObjectName(uint64_t object_handle, XrObjectType object_type, const std::string& object_name) {
144 object_info_.AddObjectName(object_handle, object_type, object_name);
145}
146
147// We always want to remove the old individual label before we do anything else.
148// So, do that in its own method
149void DebugUtilsData::RemoveIndividualLabel(XrSdkSessionLabelList& label_vec) {
150 if (!label_vec.empty() && label_vec.back()->is_individual_label) {
151 label_vec.pop_back();
152 }
153}
154
155XrSdkSessionLabelList* DebugUtilsData::GetSessionLabelList(XrSession session) {
156 auto session_label_iterator = session_labels_.find(session);
157 if (session_label_iterator == session_labels_.end()) {
158 return nullptr;
159 }
160 return session_label_iterator->second.get();
161}
162
163XrSdkSessionLabelList& DebugUtilsData::GetOrCreateSessionLabelList(XrSession session) {
164 XrSdkSessionLabelList* vec_ptr = GetSessionLabelList(session);
165 if (vec_ptr == nullptr) {
166 std::unique_ptr<XrSdkSessionLabelList> vec(new XrSdkSessionLabelList);
167 vec_ptr = vec.get();
168 session_labels_[session] = std::move(vec);
169 }
170 return *vec_ptr;
171}
172
173void DebugUtilsData::BeginLabelRegion(XrSession session, const XrDebugUtilsLabelEXT& label_info) {
174 auto& vec = GetOrCreateSessionLabelList(session);
175
176 // Individual labels do not stay around in the transition into a new label region
177 RemoveIndividualLabel(vec);
178
179 // Start the new label region
180 vec.emplace_back(XrSdkSessionLabel::make(label_info, false));
181}
182
183void DebugUtilsData::EndLabelRegion(XrSession session) {
184 XrSdkSessionLabelList* vec_ptr = GetSessionLabelList(session);
185 if (vec_ptr == nullptr) {
186 return;
187 }
188
189 // Individual labels do not stay around in the transition out of label region
190 RemoveIndividualLabel(*vec_ptr);
191
192 // Remove the last label region
193 if (!vec_ptr->empty()) {
194 vec_ptr->pop_back();
195 }
196}
197
198void DebugUtilsData::InsertLabel(XrSession session, const XrDebugUtilsLabelEXT& label_info) {
199 auto& vec = GetOrCreateSessionLabelList(session);
200
201 // Remove any individual layer that might already be there
202 RemoveIndividualLabel(vec);
203
204 // Insert a new individual label
205 vec.emplace_back(XrSdkSessionLabel::make(label_info, true));
206}
207
208void DebugUtilsData::DeleteObject(uint64_t object_handle, XrObjectType object_type) {
209 object_info_.RemoveObject(object_handle, object_type);
210
211 if (object_type == XR_OBJECT_TYPE_SESSION) {
212 auto session = TreatIntegerAsHandle<XrSession>(object_handle);
213 XrSdkSessionLabelList* vec_ptr = GetSessionLabelList(session);
214 if (vec_ptr != nullptr) {
215 session_labels_.erase(session);
216 }
217 }
218}
219
220void DebugUtilsData::DeleteSessionLabels(XrSession session) { session_labels_.erase(session); }
221
222NamesAndLabels DebugUtilsData::PopulateNamesAndLabels(std::vector<XrSdkLogObjectInfo> objects) const {
223 std::vector<XrDebugUtilsLabelEXT> labels;
224 for (auto& obj : objects) {
225 // Check for any names that have been associated with the objects and set them up here
226 object_info_.LookUpObjectName(obj);
227 // If this is a session, see if there are any labels associated with it for us to add
228 // to the callback content.
229 if (XR_OBJECT_TYPE_SESSION == obj.type) {
230 LookUpSessionLabels(obj.GetTypedHandle<XrSession>(), labels);
231 }
232 }
233
234 return {objects, labels};
235}
236
237void DebugUtilsData::WrapCallbackData(AugmentedCallbackData* aug_data,
238 const XrDebugUtilsMessengerCallbackDataEXT* callback_data) const {
239 // If there's nothing to add, just return the original data as the augmented copy
240 aug_data->exported_data = callback_data;
241 if (object_info_.Empty() || callback_data->objectCount == 0) {
242 return;
243 }
244
245 // Inspect each of the callback objects
246 bool name_found = false;
247 for (uint32_t obj = 0; obj < callback_data->objectCount; ++obj) {
248 auto& current_obj = callback_data->objects[obj];
249 name_found |= (nullptr != object_info_.LookUpStoredObjectInfo(current_obj.objectHandle, current_obj.objectType));
250
251 // If this is a session, record any labels associated with it
252 if (XR_OBJECT_TYPE_SESSION == current_obj.objectType) {
253 XrSession session = TreatIntegerAsHandle<XrSession>(current_obj.objectHandle);
254 LookUpSessionLabels(session, aug_data->labels);
255 }
256 }
257
258 // If we found nothing to add, return the original data
259 if (!name_found && aug_data->labels.empty()) {
260 return;
261 }
262
263 // Found additional data - modify an internal copy and return that as the exported data
264 memcpy(&aug_data->modified_data, callback_data, sizeof(XrDebugUtilsMessengerCallbackDataEXT));
265 aug_data->new_objects.assign(callback_data->objects, callback_data->objects + callback_data->objectCount);
266
267 // Record (overwrite) the names of all incoming objects provided in our internal list
268 for (auto& obj : aug_data->new_objects) {
269 object_info_.LookUpObjectName(obj);
270 }
271
272 // Update local copy & point export to it
273 aug_data->modified_data.objects = aug_data->new_objects.data();
274 aug_data->modified_data.sessionLabelCount = static_cast<uint32_t>(aug_data->labels.size());
275 aug_data->modified_data.sessionLabels = aug_data->labels.empty() ? nullptr : aug_data->labels.data();
276 aug_data->exported_data = &aug_data->modified_data;
277 return;
278}
279