debug.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. #include "config.h"
  2. #include "debug.h"
  3. #include <algorithm>
  4. #include <array>
  5. #include <atomic>
  6. #include <cstring>
  7. #include <deque>
  8. #include <mutex>
  9. #include <optional>
  10. #include <stdexcept>
  11. #include <string>
  12. #include <string_view>
  13. #include <unordered_map>
  14. #include <utility>
  15. #include "AL/al.h"
  16. #include "AL/alc.h"
  17. #include "AL/alext.h"
  18. #include "alc/context.h"
  19. #include "alc/device.h"
  20. #include "alnumeric.h"
  21. #include "alspan.h"
  22. #include "auxeffectslot.h"
  23. #include "buffer.h"
  24. #include "core/except.h"
  25. #include "core/logging.h"
  26. #include "core/voice.h"
  27. #include "direct_defs.h"
  28. #include "effect.h"
  29. #include "filter.h"
  30. #include "fmt/core.h"
  31. #include "intrusive_ptr.h"
  32. #include "opthelpers.h"
  33. #include "source.h"
  34. /* Declared here to prevent compilers from thinking it should be inlined, which
  35. * GCC warns about increasing code size.
  36. */
  37. DebugGroup::~DebugGroup() = default;
  38. namespace {
  39. using namespace std::string_view_literals;
  40. static_assert(DebugSeverityBase+DebugSeverityCount <= 32, "Too many debug bits");
  41. template<typename T, T ...Vals>
  42. constexpr auto make_array_sequence(std::integer_sequence<T, Vals...>)
  43. { return std::array<T,sizeof...(Vals)>{Vals...}; }
  44. template<typename T, size_t N>
  45. constexpr auto make_array_sequence()
  46. { return make_array_sequence(std::make_integer_sequence<T,N>{}); }
  47. constexpr auto GetDebugSource(ALenum source) noexcept -> std::optional<DebugSource>
  48. {
  49. switch(source)
  50. {
  51. case AL_DEBUG_SOURCE_API_EXT: return DebugSource::API;
  52. case AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT: return DebugSource::System;
  53. case AL_DEBUG_SOURCE_THIRD_PARTY_EXT: return DebugSource::ThirdParty;
  54. case AL_DEBUG_SOURCE_APPLICATION_EXT: return DebugSource::Application;
  55. case AL_DEBUG_SOURCE_OTHER_EXT: return DebugSource::Other;
  56. }
  57. return std::nullopt;
  58. }
  59. constexpr auto GetDebugType(ALenum type) noexcept -> std::optional<DebugType>
  60. {
  61. switch(type)
  62. {
  63. case AL_DEBUG_TYPE_ERROR_EXT: return DebugType::Error;
  64. case AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT: return DebugType::DeprecatedBehavior;
  65. case AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT: return DebugType::UndefinedBehavior;
  66. case AL_DEBUG_TYPE_PORTABILITY_EXT: return DebugType::Portability;
  67. case AL_DEBUG_TYPE_PERFORMANCE_EXT: return DebugType::Performance;
  68. case AL_DEBUG_TYPE_MARKER_EXT: return DebugType::Marker;
  69. case AL_DEBUG_TYPE_PUSH_GROUP_EXT: return DebugType::PushGroup;
  70. case AL_DEBUG_TYPE_POP_GROUP_EXT: return DebugType::PopGroup;
  71. case AL_DEBUG_TYPE_OTHER_EXT: return DebugType::Other;
  72. }
  73. return std::nullopt;
  74. }
  75. constexpr auto GetDebugSeverity(ALenum severity) noexcept -> std::optional<DebugSeverity>
  76. {
  77. switch(severity)
  78. {
  79. case AL_DEBUG_SEVERITY_HIGH_EXT: return DebugSeverity::High;
  80. case AL_DEBUG_SEVERITY_MEDIUM_EXT: return DebugSeverity::Medium;
  81. case AL_DEBUG_SEVERITY_LOW_EXT: return DebugSeverity::Low;
  82. case AL_DEBUG_SEVERITY_NOTIFICATION_EXT: return DebugSeverity::Notification;
  83. }
  84. return std::nullopt;
  85. }
  86. constexpr auto GetDebugSourceEnum(DebugSource source) -> ALenum
  87. {
  88. switch(source)
  89. {
  90. case DebugSource::API: return AL_DEBUG_SOURCE_API_EXT;
  91. case DebugSource::System: return AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT;
  92. case DebugSource::ThirdParty: return AL_DEBUG_SOURCE_THIRD_PARTY_EXT;
  93. case DebugSource::Application: return AL_DEBUG_SOURCE_APPLICATION_EXT;
  94. case DebugSource::Other: return AL_DEBUG_SOURCE_OTHER_EXT;
  95. }
  96. throw std::runtime_error{fmt::format("Unexpected debug source value: {}",
  97. int{al::to_underlying(source)})};
  98. }
  99. constexpr auto GetDebugTypeEnum(DebugType type) -> ALenum
  100. {
  101. switch(type)
  102. {
  103. case DebugType::Error: return AL_DEBUG_TYPE_ERROR_EXT;
  104. case DebugType::DeprecatedBehavior: return AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT;
  105. case DebugType::UndefinedBehavior: return AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT;
  106. case DebugType::Portability: return AL_DEBUG_TYPE_PORTABILITY_EXT;
  107. case DebugType::Performance: return AL_DEBUG_TYPE_PERFORMANCE_EXT;
  108. case DebugType::Marker: return AL_DEBUG_TYPE_MARKER_EXT;
  109. case DebugType::PushGroup: return AL_DEBUG_TYPE_PUSH_GROUP_EXT;
  110. case DebugType::PopGroup: return AL_DEBUG_TYPE_POP_GROUP_EXT;
  111. case DebugType::Other: return AL_DEBUG_TYPE_OTHER_EXT;
  112. }
  113. throw std::runtime_error{fmt::format("Unexpected debug type value: {}",
  114. int{al::to_underlying(type)})};
  115. }
  116. constexpr auto GetDebugSeverityEnum(DebugSeverity severity) -> ALenum
  117. {
  118. switch(severity)
  119. {
  120. case DebugSeverity::High: return AL_DEBUG_SEVERITY_HIGH_EXT;
  121. case DebugSeverity::Medium: return AL_DEBUG_SEVERITY_MEDIUM_EXT;
  122. case DebugSeverity::Low: return AL_DEBUG_SEVERITY_LOW_EXT;
  123. case DebugSeverity::Notification: return AL_DEBUG_SEVERITY_NOTIFICATION_EXT;
  124. }
  125. throw std::runtime_error{fmt::format("Unexpected debug severity value: {}",
  126. int{al::to_underlying(severity)})};
  127. }
  128. constexpr auto GetDebugSourceName(DebugSource source) noexcept -> std::string_view
  129. {
  130. switch(source)
  131. {
  132. case DebugSource::API: return "API"sv;
  133. case DebugSource::System: return "Audio System"sv;
  134. case DebugSource::ThirdParty: return "Third Party"sv;
  135. case DebugSource::Application: return "Application"sv;
  136. case DebugSource::Other: return "Other"sv;
  137. }
  138. return "<invalid source>"sv;
  139. }
  140. constexpr auto GetDebugTypeName(DebugType type) noexcept -> std::string_view
  141. {
  142. switch(type)
  143. {
  144. case DebugType::Error: return "Error"sv;
  145. case DebugType::DeprecatedBehavior: return "Deprecated Behavior"sv;
  146. case DebugType::UndefinedBehavior: return "Undefined Behavior"sv;
  147. case DebugType::Portability: return "Portability"sv;
  148. case DebugType::Performance: return "Performance"sv;
  149. case DebugType::Marker: return "Marker"sv;
  150. case DebugType::PushGroup: return "Push Group"sv;
  151. case DebugType::PopGroup: return "Pop Group"sv;
  152. case DebugType::Other: return "Other"sv;
  153. }
  154. return "<invalid type>"sv;
  155. }
  156. constexpr auto GetDebugSeverityName(DebugSeverity severity) noexcept -> std::string_view
  157. {
  158. switch(severity)
  159. {
  160. case DebugSeverity::High: return "High"sv;
  161. case DebugSeverity::Medium: return "Medium"sv;
  162. case DebugSeverity::Low: return "Low"sv;
  163. case DebugSeverity::Notification: return "Notification"sv;
  164. }
  165. return "<invalid severity>"sv;
  166. }
  167. } // namespace
  168. void ALCcontext::sendDebugMessage(std::unique_lock<std::mutex> &debuglock, DebugSource source,
  169. DebugType type, ALuint id, DebugSeverity severity, std::string_view message)
  170. {
  171. if(!mDebugEnabled.load(std::memory_order_relaxed)) UNLIKELY
  172. return;
  173. if(message.length() >= MaxDebugMessageLength) UNLIKELY
  174. {
  175. ERR("Debug message too long ({} >= {}):\n-> {}", message.length(),
  176. MaxDebugMessageLength, message);
  177. return;
  178. }
  179. DebugGroup &debug = mDebugGroups.back();
  180. const uint64_t idfilter{(1_u64 << (DebugSourceBase+al::to_underlying(source)))
  181. | (1_u64 << (DebugTypeBase+al::to_underlying(type)))
  182. | (uint64_t{id} << 32)};
  183. auto iditer = std::lower_bound(debug.mIdFilters.cbegin(), debug.mIdFilters.cend(), idfilter);
  184. if(iditer != debug.mIdFilters.cend() && *iditer == idfilter)
  185. return;
  186. const uint filter{(1u << (DebugSourceBase+al::to_underlying(source)))
  187. | (1u << (DebugTypeBase+al::to_underlying(type)))
  188. | (1u << (DebugSeverityBase+al::to_underlying(severity)))};
  189. auto iter = std::lower_bound(debug.mFilters.cbegin(), debug.mFilters.cend(), filter);
  190. if(iter != debug.mFilters.cend() && *iter == filter)
  191. return;
  192. if(mDebugCb)
  193. {
  194. auto callback = mDebugCb;
  195. auto param = mDebugParam;
  196. debuglock.unlock();
  197. callback(GetDebugSourceEnum(source), GetDebugTypeEnum(type), id,
  198. GetDebugSeverityEnum(severity), static_cast<ALsizei>(message.length()), message.data(),
  199. param);
  200. }
  201. else
  202. {
  203. if(mDebugLog.size() < MaxDebugLoggedMessages)
  204. mDebugLog.emplace_back(source, type, id, severity, message);
  205. else UNLIKELY
  206. ERR("Debug message log overflow. Lost message:\n"
  207. " Source: {}\n"
  208. " Type: {}\n"
  209. " ID: {}\n"
  210. " Severity: {}\n"
  211. " Message: \"{}\"",
  212. GetDebugSourceName(source), GetDebugTypeName(type), id,
  213. GetDebugSeverityName(severity), message);
  214. }
  215. }
  216. FORCE_ALIGN DECL_FUNCEXT2(void, alDebugMessageCallback,EXT, ALDEBUGPROCEXT,callback, void*,userParam)
  217. FORCE_ALIGN void AL_APIENTRY alDebugMessageCallbackDirectEXT(ALCcontext *context,
  218. ALDEBUGPROCEXT callback, void *userParam) noexcept
  219. {
  220. std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
  221. context->mDebugCb = callback;
  222. context->mDebugParam = userParam;
  223. }
  224. FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageInsert,EXT, ALenum,source, ALenum,type, ALuint,id, ALenum,severity, ALsizei,length, const ALchar*,message)
  225. FORCE_ALIGN void AL_APIENTRY alDebugMessageInsertDirectEXT(ALCcontext *context, ALenum source,
  226. ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) noexcept
  227. try {
  228. if(!context->mContextFlags.test(ContextFlags::DebugBit))
  229. return;
  230. if(!message)
  231. context->throw_error(AL_INVALID_VALUE, "Null message pointer");
  232. auto msgview = (length < 0) ? std::string_view{message}
  233. : std::string_view{message, static_cast<uint>(length)};
  234. if(msgview.size() >= MaxDebugMessageLength)
  235. context->throw_error(AL_INVALID_VALUE, "Debug message too long ({} >= {})", msgview.size(),
  236. MaxDebugMessageLength);
  237. auto dsource = GetDebugSource(source);
  238. if(!dsource)
  239. context->throw_error(AL_INVALID_ENUM, "Invalid debug source {:#04x}", as_unsigned(source));
  240. if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application)
  241. context->throw_error(AL_INVALID_ENUM, "Debug source {:#04x} not allowed",
  242. as_unsigned(source));
  243. auto dtype = GetDebugType(type);
  244. if(!dtype)
  245. context->throw_error(AL_INVALID_ENUM, "Invalid debug type {:#04x}", as_unsigned(type));
  246. auto dseverity = GetDebugSeverity(severity);
  247. if(!dseverity)
  248. context->throw_error(AL_INVALID_ENUM, "Invalid debug severity {:#04x}",
  249. as_unsigned(severity));
  250. context->debugMessage(*dsource, *dtype, id, *dseverity, msgview);
  251. }
  252. catch(al::base_exception&) {
  253. }
  254. catch(std::exception &e) {
  255. ERR("Caught exception: {}", e.what());
  256. }
  257. FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageControl,EXT, ALenum,source, ALenum,type, ALenum,severity, ALsizei,count, const ALuint*,ids, ALboolean,enable)
  258. FORCE_ALIGN void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, ALenum source,
  259. ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) noexcept
  260. try {
  261. if(count > 0)
  262. {
  263. if(!ids)
  264. context->throw_error(AL_INVALID_VALUE, "IDs is null with non-0 count");
  265. if(source == AL_DONT_CARE_EXT)
  266. context->throw_error(AL_INVALID_OPERATION,
  267. "Debug source cannot be AL_DONT_CARE_EXT with IDs");
  268. if(type == AL_DONT_CARE_EXT)
  269. context->throw_error(AL_INVALID_OPERATION,
  270. "Debug type cannot be AL_DONT_CARE_EXT with IDs");
  271. if(severity != AL_DONT_CARE_EXT)
  272. context->throw_error(AL_INVALID_OPERATION,
  273. "Debug severity must be AL_DONT_CARE_EXT with IDs");
  274. }
  275. if(enable != AL_TRUE && enable != AL_FALSE)
  276. context->throw_error(AL_INVALID_ENUM, "Invalid debug enable {}", enable);
  277. static constexpr size_t ElemCount{DebugSourceCount + DebugTypeCount + DebugSeverityCount};
  278. static constexpr auto Values = make_array_sequence<uint8_t,ElemCount>();
  279. auto srcIndices = al::span{Values}.subspan(DebugSourceBase,DebugSourceCount);
  280. if(source != AL_DONT_CARE_EXT)
  281. {
  282. auto dsource = GetDebugSource(source);
  283. if(!dsource)
  284. context->throw_error(AL_INVALID_ENUM, "Invalid debug source {:#04x}",
  285. as_unsigned(source));
  286. srcIndices = srcIndices.subspan(al::to_underlying(*dsource), 1);
  287. }
  288. auto typeIndices = al::span{Values}.subspan(DebugTypeBase,DebugTypeCount);
  289. if(type != AL_DONT_CARE_EXT)
  290. {
  291. auto dtype = GetDebugType(type);
  292. if(!dtype)
  293. context->throw_error(AL_INVALID_ENUM, "Invalid debug type {:#04x}", as_unsigned(type));
  294. typeIndices = typeIndices.subspan(al::to_underlying(*dtype), 1);
  295. }
  296. auto svrIndices = al::span{Values}.subspan(DebugSeverityBase,DebugSeverityCount);
  297. if(severity != AL_DONT_CARE_EXT)
  298. {
  299. auto dseverity = GetDebugSeverity(severity);
  300. if(!dseverity)
  301. context->throw_error(AL_INVALID_ENUM, "Invalid debug severity {:#04x}",
  302. as_unsigned(severity));
  303. svrIndices = svrIndices.subspan(al::to_underlying(*dseverity), 1);
  304. }
  305. std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
  306. DebugGroup &debug = context->mDebugGroups.back();
  307. if(count > 0)
  308. {
  309. const uint filterbase{(1u<<srcIndices[0]) | (1u<<typeIndices[0])};
  310. for(const uint id : al::span{ids, static_cast<uint>(count)})
  311. {
  312. const uint64_t filter{filterbase | (uint64_t{id} << 32)};
  313. auto iter = std::lower_bound(debug.mIdFilters.cbegin(), debug.mIdFilters.cend(),
  314. filter);
  315. if(!enable && (iter == debug.mIdFilters.cend() || *iter != filter))
  316. debug.mIdFilters.insert(iter, filter);
  317. else if(enable && iter != debug.mIdFilters.cend() && *iter == filter)
  318. debug.mIdFilters.erase(iter);
  319. }
  320. }
  321. else
  322. {
  323. auto apply_filter = [enable,&debug](const uint filter)
  324. {
  325. auto iter = std::lower_bound(debug.mFilters.cbegin(), debug.mFilters.cend(), filter);
  326. if(!enable && (iter == debug.mFilters.cend() || *iter != filter))
  327. debug.mFilters.insert(iter, filter);
  328. else if(enable && iter != debug.mFilters.cend() && *iter == filter)
  329. debug.mFilters.erase(iter);
  330. };
  331. auto apply_severity = [apply_filter,svrIndices](const uint filter)
  332. {
  333. std::for_each(svrIndices.cbegin(), svrIndices.cend(),
  334. [apply_filter,filter](const uint idx){ apply_filter(filter | (1<<idx)); });
  335. };
  336. auto apply_type = [apply_severity,typeIndices](const uint filter)
  337. {
  338. std::for_each(typeIndices.cbegin(), typeIndices.cend(),
  339. [apply_severity,filter](const uint idx){ apply_severity(filter | (1<<idx)); });
  340. };
  341. std::for_each(srcIndices.cbegin(), srcIndices.cend(),
  342. [apply_type](const uint idx){ apply_type(1<<idx); });
  343. }
  344. }
  345. catch(al::base_exception&) {
  346. }
  347. catch(std::exception &e) {
  348. ERR("Caught exception: {}", e.what());
  349. }
  350. FORCE_ALIGN DECL_FUNCEXT4(void, alPushDebugGroup,EXT, ALenum,source, ALuint,id, ALsizei,length, const ALchar*,message)
  351. FORCE_ALIGN void AL_APIENTRY alPushDebugGroupDirectEXT(ALCcontext *context, ALenum source,
  352. ALuint id, ALsizei length, const ALchar *message) noexcept
  353. try {
  354. if(length < 0)
  355. {
  356. size_t newlen{std::strlen(message)};
  357. if(newlen >= MaxDebugMessageLength)
  358. context->throw_error(AL_INVALID_VALUE, "Debug message too long ({} >= {})", newlen,
  359. MaxDebugMessageLength);
  360. length = static_cast<ALsizei>(newlen);
  361. }
  362. else if(length >= MaxDebugMessageLength)
  363. context->throw_error(AL_INVALID_VALUE, "Debug message too long ({} >= {})", length,
  364. MaxDebugMessageLength);
  365. auto dsource = GetDebugSource(source);
  366. if(!dsource)
  367. context->throw_error(AL_INVALID_ENUM, "Invalid debug source {:#04x}", as_unsigned(source));
  368. if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application)
  369. context->throw_error(AL_INVALID_ENUM, "Debug source {:#04x} not allowed",
  370. as_unsigned(source));
  371. std::unique_lock<std::mutex> debuglock{context->mDebugCbLock};
  372. if(context->mDebugGroups.size() >= MaxDebugGroupDepth)
  373. context->throw_error(AL_STACK_OVERFLOW_EXT, "Pushing too many debug groups");
  374. context->mDebugGroups.emplace_back(*dsource, id,
  375. std::string_view{message, static_cast<uint>(length)});
  376. auto &oldback = *(context->mDebugGroups.end()-2);
  377. auto &newback = context->mDebugGroups.back();
  378. newback.mFilters = oldback.mFilters;
  379. newback.mIdFilters = oldback.mIdFilters;
  380. if(context->mContextFlags.test(ContextFlags::DebugBit))
  381. context->sendDebugMessage(debuglock, newback.mSource, DebugType::PushGroup, newback.mId,
  382. DebugSeverity::Notification, newback.mMessage);
  383. }
  384. catch(al::base_exception&) {
  385. }
  386. catch(std::exception &e) {
  387. ERR("Caught exception: {}", e.what());
  388. }
  389. FORCE_ALIGN DECL_FUNCEXT(void, alPopDebugGroup,EXT)
  390. FORCE_ALIGN void AL_APIENTRY alPopDebugGroupDirectEXT(ALCcontext *context) noexcept
  391. try {
  392. std::unique_lock<std::mutex> debuglock{context->mDebugCbLock};
  393. if(context->mDebugGroups.size() <= 1)
  394. context->throw_error(AL_STACK_UNDERFLOW_EXT, "Attempting to pop the default debug group");
  395. DebugGroup &debug = context->mDebugGroups.back();
  396. const auto source = debug.mSource;
  397. const auto id = debug.mId;
  398. std::string message{std::move(debug.mMessage)};
  399. context->mDebugGroups.pop_back();
  400. if(context->mContextFlags.test(ContextFlags::DebugBit))
  401. context->sendDebugMessage(debuglock, source, DebugType::PopGroup, id,
  402. DebugSeverity::Notification, message);
  403. }
  404. catch(al::base_exception&) {
  405. }
  406. catch(std::exception &e) {
  407. ERR("Caught exception: {}", e.what());
  408. }
  409. FORCE_ALIGN DECL_FUNCEXT8(ALuint, alGetDebugMessageLog,EXT, ALuint,count, ALsizei,logBufSize, ALenum*,sources, ALenum*,types, ALuint*,ids, ALenum*,severities, ALsizei*,lengths, ALchar*,logBuf)
  410. FORCE_ALIGN ALuint AL_APIENTRY alGetDebugMessageLogDirectEXT(ALCcontext *context, ALuint count,
  411. ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities,
  412. ALsizei *lengths, ALchar *logBuf) noexcept
  413. try {
  414. if(logBuf && logBufSize < 0)
  415. context->throw_error(AL_INVALID_VALUE, "Negative debug log buffer size");
  416. const auto sourcesSpan = al::span{sources, sources ? count : 0u};
  417. const auto typesSpan = al::span{types, types ? count : 0u};
  418. const auto idsSpan = al::span{ids, ids ? count : 0u};
  419. const auto severitiesSpan = al::span{severities, severities ? count : 0u};
  420. const auto lengthsSpan = al::span{lengths, lengths ? count : 0u};
  421. const auto logSpan = al::span{logBuf, logBuf ? static_cast<ALuint>(logBufSize) : 0u};
  422. auto sourceiter = sourcesSpan.begin();
  423. auto typeiter = typesSpan.begin();
  424. auto iditer = idsSpan.begin();
  425. auto severityiter = severitiesSpan.begin();
  426. auto lengthiter = lengthsSpan.begin();
  427. auto logiter = logSpan.begin();
  428. auto debuglock = std::lock_guard{context->mDebugCbLock};
  429. for(ALuint i{0};i < count;++i)
  430. {
  431. if(context->mDebugLog.empty())
  432. return i;
  433. auto &entry = context->mDebugLog.front();
  434. const auto tocopy = size_t{entry.mMessage.size() + 1};
  435. if(al::to_address(logiter) != nullptr)
  436. {
  437. if(static_cast<size_t>(std::distance(logiter, logSpan.end())) < tocopy)
  438. return i;
  439. logiter = std::copy(entry.mMessage.cbegin(), entry.mMessage.cend(), logiter);
  440. *(logiter++) = '\0';
  441. }
  442. if(al::to_address(sourceiter) != nullptr)
  443. *(sourceiter++) = GetDebugSourceEnum(entry.mSource);
  444. if(al::to_address(typeiter) != nullptr)
  445. *(typeiter++) = GetDebugTypeEnum(entry.mType);
  446. if(al::to_address(iditer) != nullptr)
  447. *(iditer++) = entry.mId;
  448. if(al::to_address(severityiter) != nullptr)
  449. *(severityiter++) = GetDebugSeverityEnum(entry.mSeverity);
  450. if(al::to_address(lengthiter) != nullptr)
  451. *(lengthiter++) = static_cast<ALsizei>(tocopy);
  452. context->mDebugLog.pop_front();
  453. }
  454. return count;
  455. }
  456. catch(al::base_exception&) {
  457. return 0;
  458. }
  459. catch(std::exception &e) {
  460. ERR("Caught exception: {}", e.what());
  461. return 0;
  462. }
  463. FORCE_ALIGN DECL_FUNCEXT4(void, alObjectLabel,EXT, ALenum,identifier, ALuint,name, ALsizei,length, const ALchar*,label)
  464. FORCE_ALIGN void AL_APIENTRY alObjectLabelDirectEXT(ALCcontext *context, ALenum identifier,
  465. ALuint name, ALsizei length, const ALchar *label) noexcept
  466. try {
  467. if(!label && length != 0)
  468. context->throw_error(AL_INVALID_VALUE, "Null label pointer");
  469. auto objname = (length < 0) ? std::string_view{label}
  470. : std::string_view{label, static_cast<uint>(length)};
  471. if(objname.size() >= MaxObjectLabelLength)
  472. context->throw_error(AL_INVALID_VALUE, "Object label length too long ({} >= {})",
  473. objname.size(), MaxObjectLabelLength);
  474. switch(identifier)
  475. {
  476. case AL_SOURCE_EXT: ALsource::SetName(context, name, objname); return;
  477. case AL_BUFFER: ALbuffer::SetName(context, name, objname); return;
  478. case AL_FILTER_EXT: ALfilter::SetName(context, name, objname); return;
  479. case AL_EFFECT_EXT: ALeffect::SetName(context, name, objname); return;
  480. case AL_AUXILIARY_EFFECT_SLOT_EXT: ALeffectslot::SetName(context, name, objname); return;
  481. }
  482. context->throw_error(AL_INVALID_ENUM, "Invalid name identifier {:#04x}",
  483. as_unsigned(identifier));
  484. }
  485. catch(al::base_exception&) {
  486. }
  487. catch(std::exception &e) {
  488. ERR("Caught exception: {}", e.what());
  489. }
  490. FORCE_ALIGN DECL_FUNCEXT5(void, alGetObjectLabel,EXT, ALenum,identifier, ALuint,name, ALsizei,bufSize, ALsizei*,length, ALchar*,label)
  491. FORCE_ALIGN void AL_APIENTRY alGetObjectLabelDirectEXT(ALCcontext *context, ALenum identifier,
  492. ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) noexcept
  493. try {
  494. if(bufSize < 0)
  495. context->throw_error(AL_INVALID_VALUE, "Negative label bufSize");
  496. if(!label && !length)
  497. context->throw_error(AL_INVALID_VALUE, "Null length and label");
  498. if(label && bufSize == 0)
  499. context->throw_error(AL_INVALID_VALUE, "Zero label bufSize");
  500. const auto labelOut = al::span{label, label ? static_cast<ALuint>(bufSize) : 0u};
  501. auto copy_name = [name,length,labelOut](std::unordered_map<ALuint,std::string> &names)
  502. {
  503. std::string_view objname;
  504. auto iter = names.find(name);
  505. if(iter != names.end())
  506. objname = iter->second;
  507. if(labelOut.empty())
  508. *length = static_cast<ALsizei>(objname.size());
  509. else
  510. {
  511. const size_t tocopy{std::min(objname.size(), labelOut.size()-1)};
  512. auto oiter = std::copy_n(objname.cbegin(), tocopy, labelOut.begin());
  513. *oiter = '\0';
  514. if(length)
  515. *length = static_cast<ALsizei>(tocopy);
  516. }
  517. };
  518. if(identifier == AL_SOURCE_EXT)
  519. {
  520. std::lock_guard srclock{context->mSourceLock};
  521. copy_name(context->mSourceNames);
  522. }
  523. else if(identifier == AL_BUFFER)
  524. {
  525. auto *device = context->mALDevice.get();
  526. auto buflock = std::lock_guard{device->BufferLock};
  527. copy_name(device->mBufferNames);
  528. }
  529. else if(identifier == AL_FILTER_EXT)
  530. {
  531. auto *device = context->mALDevice.get();
  532. auto buflock = std::lock_guard{device->FilterLock};
  533. copy_name(device->mFilterNames);
  534. }
  535. else if(identifier == AL_EFFECT_EXT)
  536. {
  537. auto *device = context->mALDevice.get();
  538. auto buflock = std::lock_guard{device->EffectLock};
  539. copy_name(device->mEffectNames);
  540. }
  541. else if(identifier == AL_AUXILIARY_EFFECT_SLOT_EXT)
  542. {
  543. std::lock_guard slotlock{context->mEffectSlotLock};
  544. copy_name(context->mEffectSlotNames);
  545. }
  546. else
  547. context->throw_error(AL_INVALID_ENUM, "Invalid name identifier {:#04x}",
  548. as_unsigned(identifier));
  549. }
  550. catch(al::base_exception&) {
  551. }
  552. catch(std::exception &e) {
  553. ERR("Caught exception: {}", e.what());
  554. }