context.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. #ifndef ALC_CONTEXT_H
  2. #define ALC_CONTEXT_H
  3. #include "config.h"
  4. #include <atomic>
  5. #include <bitset>
  6. #include <cstdint>
  7. #include <deque>
  8. #include <memory>
  9. #include <mutex>
  10. #include <string>
  11. #include <string_view>
  12. #include <unordered_map>
  13. #include <utility>
  14. #include <vector>
  15. #include "AL/al.h"
  16. #include "AL/alc.h"
  17. #include "AL/alext.h"
  18. #include "al/listener.h"
  19. #include "althreads.h"
  20. #include "core/context.h"
  21. #include "fmt/core.h"
  22. #include "intrusive_ptr.h"
  23. #include "opthelpers.h"
  24. #if ALSOFT_EAX
  25. #include "al/eax/api.h"
  26. #include "al/eax/exception.h"
  27. #include "al/eax/fx_slot_index.h"
  28. #include "al/eax/fx_slots.h"
  29. #include "al/eax/utils.h"
  30. class EaxCall;
  31. #endif // ALSOFT_EAX
  32. struct ALeffect;
  33. struct ALeffectslot;
  34. struct DebugGroup;
  35. struct EffectSlotSubList;
  36. struct SourceSubList;
  37. enum class DebugSource : std::uint8_t;
  38. enum class DebugType : std::uint8_t;
  39. enum class DebugSeverity : std::uint8_t;
  40. using uint = unsigned int;
  41. enum ContextFlags {
  42. DebugBit = 0, /* ALC_CONTEXT_DEBUG_BIT_EXT */
  43. };
  44. using ContextFlagBitset = std::bitset<sizeof(ALuint)*8>;
  45. struct DebugLogEntry {
  46. const DebugSource mSource;
  47. const DebugType mType;
  48. const DebugSeverity mSeverity;
  49. const uint mId;
  50. std::string mMessage;
  51. template<typename T>
  52. DebugLogEntry(DebugSource source, DebugType type, uint id, DebugSeverity severity, T&& message)
  53. : mSource{source}, mType{type}, mSeverity{severity}, mId{id}
  54. , mMessage{std::forward<T>(message)}
  55. { }
  56. DebugLogEntry(const DebugLogEntry&) = default;
  57. DebugLogEntry(DebugLogEntry&&) = default;
  58. };
  59. namespace al {
  60. struct Device;
  61. } // namespace al
  62. struct ALCcontext final : public al::intrusive_ref<ALCcontext>, ContextBase {
  63. const al::intrusive_ptr<al::Device> mALDevice;
  64. bool mPropsDirty{true};
  65. bool mDeferUpdates{false};
  66. std::mutex mPropLock;
  67. al::tss<ALenum> mLastThreadError{AL_NO_ERROR};
  68. const ContextFlagBitset mContextFlags;
  69. std::atomic<bool> mDebugEnabled{false};
  70. DistanceModel mDistanceModel{DistanceModel::Default};
  71. bool mSourceDistanceModel{false};
  72. float mDopplerFactor{1.0f};
  73. float mDopplerVelocity{1.0f};
  74. float mSpeedOfSound{SpeedOfSoundMetersPerSec};
  75. float mAirAbsorptionGainHF{AirAbsorbGainHF};
  76. std::mutex mEventCbLock;
  77. ALEVENTPROCSOFT mEventCb{};
  78. void *mEventParam{nullptr};
  79. std::mutex mDebugCbLock;
  80. ALDEBUGPROCEXT mDebugCb{};
  81. void *mDebugParam{nullptr};
  82. std::vector<DebugGroup> mDebugGroups;
  83. std::deque<DebugLogEntry> mDebugLog;
  84. ALlistener mListener{};
  85. std::vector<SourceSubList> mSourceList;
  86. ALuint mNumSources{0};
  87. std::mutex mSourceLock;
  88. std::vector<EffectSlotSubList> mEffectSlotList;
  89. ALuint mNumEffectSlots{0u};
  90. std::mutex mEffectSlotLock;
  91. /* Default effect slot */
  92. std::unique_ptr<ALeffectslot> mDefaultSlot;
  93. std::vector<std::string_view> mExtensions;
  94. std::string mExtensionsString;
  95. std::unordered_map<ALuint,std::string> mSourceNames;
  96. std::unordered_map<ALuint,std::string> mEffectSlotNames;
  97. ALCcontext(al::intrusive_ptr<al::Device> device, ContextFlagBitset flags);
  98. ALCcontext(const ALCcontext&) = delete;
  99. ALCcontext& operator=(const ALCcontext&) = delete;
  100. ~ALCcontext() final;
  101. void init();
  102. /**
  103. * Removes the context from its device and removes it from being current on
  104. * the running thread or globally. Stops device playback if this was the
  105. * last context on its device.
  106. */
  107. void deinit();
  108. /**
  109. * Defers/suspends updates for the given context's listener and sources.
  110. * This does *NOT* stop mixing, but rather prevents certain property
  111. * changes from taking effect. mPropLock must be held when called.
  112. */
  113. void deferUpdates() noexcept { mDeferUpdates = true; }
  114. /**
  115. * Resumes update processing after being deferred. mPropLock must be held
  116. * when called.
  117. */
  118. void processUpdates()
  119. {
  120. if(std::exchange(mDeferUpdates, false))
  121. applyAllUpdates();
  122. }
  123. /**
  124. * Applies all pending updates for the context, listener, effect slots, and
  125. * sources.
  126. */
  127. void applyAllUpdates();
  128. void setErrorImpl(ALenum errorCode, const fmt::string_view fmt, fmt::format_args args);
  129. template<typename ...Args>
  130. void setError(ALenum errorCode, fmt::format_string<Args...> msg, Args&& ...args)
  131. { setErrorImpl(errorCode, msg, fmt::make_format_args(args...)); }
  132. [[noreturn]]
  133. void throw_error_impl(ALenum errorCode, const fmt::string_view fmt, fmt::format_args args);
  134. template<typename ...Args> [[noreturn]]
  135. void throw_error(ALenum errorCode, fmt::format_string<Args...> fmt, Args&&... args)
  136. { throw_error_impl(errorCode, fmt, fmt::make_format_args(args...)); }
  137. void sendDebugMessage(std::unique_lock<std::mutex> &debuglock, DebugSource source,
  138. DebugType type, ALuint id, DebugSeverity severity, std::string_view message);
  139. void debugMessage(DebugSource source, DebugType type, ALuint id, DebugSeverity severity,
  140. std::string_view message)
  141. {
  142. if(!mDebugEnabled.load(std::memory_order_relaxed)) LIKELY
  143. return;
  144. std::unique_lock<std::mutex> debuglock{mDebugCbLock};
  145. sendDebugMessage(debuglock, source, type, id, severity, message);
  146. }
  147. /* Process-wide current context */
  148. static std::atomic<bool> sGlobalContextLock;
  149. static std::atomic<ALCcontext*> sGlobalContext;
  150. private:
  151. /* Thread-local current context. */
  152. static inline thread_local ALCcontext *sLocalContext{};
  153. /* Thread-local context handling. This handles attempting to release the
  154. * context which may have been left current when the thread is destroyed.
  155. */
  156. class ThreadCtx {
  157. public:
  158. ThreadCtx() = default;
  159. ThreadCtx(const ThreadCtx&) = delete;
  160. auto operator=(const ThreadCtx&) -> ThreadCtx& = delete;
  161. ~ThreadCtx();
  162. /* NOLINTBEGIN(readability-convert-member-functions-to-static)
  163. * This should be non-static to invoke construction of the thread-local
  164. * sThreadContext, so that it's destructor gets run at thread exit to
  165. * clear sLocalContext (which isn't a member variable to make read
  166. * access efficient).
  167. */
  168. void set(ALCcontext *ctx) const noexcept { sLocalContext = ctx; }
  169. /* NOLINTEND(readability-convert-member-functions-to-static) */
  170. };
  171. static thread_local ThreadCtx sThreadContext;
  172. public:
  173. static ALCcontext *getThreadContext() noexcept { return sLocalContext; }
  174. static void setThreadContext(ALCcontext *context) noexcept { sThreadContext.set(context); }
  175. /* Default effect that applies to sources that don't have an effect on send 0. */
  176. static ALeffect sDefaultEffect;
  177. #if ALSOFT_EAX
  178. bool hasEax() const noexcept { return mEaxIsInitialized; }
  179. bool eaxIsCapable() const noexcept;
  180. void eaxUninitialize() noexcept;
  181. ALenum eax_eax_set(
  182. const GUID* property_set_id,
  183. ALuint property_id,
  184. ALuint property_source_id,
  185. ALvoid* property_value,
  186. ALuint property_value_size);
  187. ALenum eax_eax_get(
  188. const GUID* property_set_id,
  189. ALuint property_id,
  190. ALuint property_source_id,
  191. ALvoid* property_value,
  192. ALuint property_value_size);
  193. void eaxSetLastError() noexcept;
  194. [[nodiscard]]
  195. auto eaxGetDistanceFactor() const noexcept -> float { return mEax.flDistanceFactor; }
  196. [[nodiscard]]
  197. auto eaxGetPrimaryFxSlotIndex() const noexcept -> EaxFxSlotIndex
  198. { return mEaxPrimaryFxSlotIndex; }
  199. const ALeffectslot& eaxGetFxSlot(EaxFxSlotIndexValue fx_slot_index) const
  200. { return mEaxFxSlots.get(fx_slot_index); }
  201. ALeffectslot& eaxGetFxSlot(EaxFxSlotIndexValue fx_slot_index)
  202. { return mEaxFxSlots.get(fx_slot_index); }
  203. bool eaxNeedsCommit() const noexcept { return mEaxNeedsCommit; }
  204. void eaxCommit();
  205. void eaxCommitFxSlots()
  206. { mEaxFxSlots.commit(); }
  207. private:
  208. enum {
  209. eax_primary_fx_slot_id_dirty_bit,
  210. eax_distance_factor_dirty_bit,
  211. eax_air_absorption_hf_dirty_bit,
  212. eax_hf_reference_dirty_bit,
  213. eax_macro_fx_factor_dirty_bit,
  214. eax_dirty_bit_count
  215. };
  216. using Eax4Props = EAX40CONTEXTPROPERTIES;
  217. struct Eax4State {
  218. Eax4Props i; // Immediate.
  219. Eax4Props d; // Deferred.
  220. };
  221. using Eax5Props = EAX50CONTEXTPROPERTIES;
  222. struct Eax5State {
  223. Eax5Props i; // Immediate.
  224. Eax5Props d; // Deferred.
  225. };
  226. class ContextException final : public EaxException {
  227. public:
  228. explicit ContextException(const char *message)
  229. : EaxException{"EAX_CONTEXT", message}
  230. { }
  231. };
  232. struct Eax4PrimaryFxSlotIdValidator {
  233. void operator()(const GUID& guidPrimaryFXSlotID) const
  234. {
  235. if(guidPrimaryFXSlotID != EAX_NULL_GUID &&
  236. guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot0 &&
  237. guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot1 &&
  238. guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot2 &&
  239. guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot3)
  240. {
  241. eax_fail_unknown_primary_fx_slot_id();
  242. }
  243. }
  244. };
  245. struct Eax4DistanceFactorValidator {
  246. void operator()(float flDistanceFactor) const
  247. {
  248. eax_validate_range<ContextException>(
  249. "Distance Factor",
  250. flDistanceFactor,
  251. EAXCONTEXT_MINDISTANCEFACTOR,
  252. EAXCONTEXT_MAXDISTANCEFACTOR);
  253. }
  254. };
  255. struct Eax4AirAbsorptionHfValidator {
  256. void operator()(float flAirAbsorptionHF) const
  257. {
  258. eax_validate_range<ContextException>(
  259. "Air Absorption HF",
  260. flAirAbsorptionHF,
  261. EAXCONTEXT_MINAIRABSORPTIONHF,
  262. EAXCONTEXT_MAXAIRABSORPTIONHF);
  263. }
  264. };
  265. struct Eax4HfReferenceValidator {
  266. void operator()(float flHFReference) const
  267. {
  268. eax_validate_range<ContextException>(
  269. "HF Reference",
  270. flHFReference,
  271. EAXCONTEXT_MINHFREFERENCE,
  272. EAXCONTEXT_MAXHFREFERENCE);
  273. }
  274. };
  275. struct Eax4AllValidator {
  276. void operator()(const EAX40CONTEXTPROPERTIES& all) const
  277. {
  278. Eax4PrimaryFxSlotIdValidator{}(all.guidPrimaryFXSlotID);
  279. Eax4DistanceFactorValidator{}(all.flDistanceFactor);
  280. Eax4AirAbsorptionHfValidator{}(all.flAirAbsorptionHF);
  281. Eax4HfReferenceValidator{}(all.flHFReference);
  282. }
  283. };
  284. struct Eax5PrimaryFxSlotIdValidator {
  285. void operator()(const GUID& guidPrimaryFXSlotID) const
  286. {
  287. if(guidPrimaryFXSlotID != EAX_NULL_GUID &&
  288. guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot0 &&
  289. guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot1 &&
  290. guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot2 &&
  291. guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot3)
  292. {
  293. eax_fail_unknown_primary_fx_slot_id();
  294. }
  295. }
  296. };
  297. struct Eax5MacroFxFactorValidator {
  298. void operator()(float flMacroFXFactor) const
  299. {
  300. eax_validate_range<ContextException>(
  301. "Macro FX Factor",
  302. flMacroFXFactor,
  303. EAXCONTEXT_MINMACROFXFACTOR,
  304. EAXCONTEXT_MAXMACROFXFACTOR);
  305. }
  306. };
  307. struct Eax5AllValidator {
  308. void operator()(const EAX50CONTEXTPROPERTIES& all) const
  309. {
  310. Eax5PrimaryFxSlotIdValidator{}(all.guidPrimaryFXSlotID);
  311. Eax4DistanceFactorValidator{}(all.flDistanceFactor);
  312. Eax4AirAbsorptionHfValidator{}(all.flAirAbsorptionHF);
  313. Eax4HfReferenceValidator{}(all.flHFReference);
  314. Eax5MacroFxFactorValidator{}(all.flMacroFXFactor);
  315. }
  316. };
  317. struct Eax5EaxVersionValidator {
  318. void operator()(unsigned long ulEAXVersion) const
  319. {
  320. eax_validate_range<ContextException>(
  321. "EAX version",
  322. ulEAXVersion,
  323. EAXCONTEXT_MINEAXSESSION,
  324. EAXCONTEXT_MAXEAXSESSION);
  325. }
  326. };
  327. struct Eax5MaxActiveSendsValidator {
  328. void operator()(unsigned long ulMaxActiveSends) const
  329. {
  330. eax_validate_range<ContextException>(
  331. "Max Active Sends",
  332. ulMaxActiveSends,
  333. EAXCONTEXT_MINMAXACTIVESENDS,
  334. EAXCONTEXT_MAXMAXACTIVESENDS);
  335. }
  336. };
  337. struct Eax5SessionAllValidator {
  338. void operator()(const EAXSESSIONPROPERTIES& all) const
  339. {
  340. Eax5EaxVersionValidator{}(all.ulEAXVersion);
  341. Eax5MaxActiveSendsValidator{}(all.ulMaxActiveSends);
  342. }
  343. };
  344. struct Eax5SpeakerConfigValidator {
  345. void operator()(unsigned long ulSpeakerConfig) const
  346. {
  347. eax_validate_range<ContextException>(
  348. "Speaker Config",
  349. ulSpeakerConfig,
  350. EAXCONTEXT_MINSPEAKERCONFIG,
  351. EAXCONTEXT_MAXSPEAKERCONFIG);
  352. }
  353. };
  354. bool mEaxIsInitialized{};
  355. bool mEaxIsTried{};
  356. long mEaxLastError{};
  357. unsigned long mEaxSpeakerConfig{};
  358. EaxFxSlotIndex mEaxPrimaryFxSlotIndex{};
  359. EaxFxSlots mEaxFxSlots{};
  360. int mEaxVersion{}; // Current EAX version.
  361. bool mEaxNeedsCommit{};
  362. std::bitset<eax_dirty_bit_count> mEaxDf; // Dirty flags for the current EAX version.
  363. Eax5State mEax123{}; // EAX1/EAX2/EAX3 state.
  364. Eax4State mEax4{}; // EAX4 state.
  365. Eax5State mEax5{}; // EAX5 state.
  366. Eax5Props mEax{}; // Current EAX state.
  367. EAXSESSIONPROPERTIES mEaxSession{};
  368. [[noreturn]] static void eax_fail(const char* message);
  369. [[noreturn]] static void eax_fail_unknown_property_set_id();
  370. [[noreturn]] static void eax_fail_unknown_primary_fx_slot_id();
  371. [[noreturn]] static void eax_fail_unknown_property_id();
  372. [[noreturn]] static void eax_fail_unknown_version();
  373. // Gets a value from EAX call,
  374. // validates it,
  375. // and updates the current value.
  376. template<typename TValidator, typename TProperty>
  377. static void eax_set(const EaxCall& call, TProperty& property)
  378. {
  379. const auto& value = call.get_value<ContextException, const TProperty>();
  380. TValidator{}(value);
  381. property = value;
  382. }
  383. // Gets a new value from EAX call,
  384. // validates it,
  385. // updates the deferred value,
  386. // updates a dirty flag.
  387. template<
  388. typename TValidator,
  389. size_t DirtyBit,
  390. typename TMemberResult,
  391. typename TProps,
  392. typename TState>
  393. void eax_defer(const EaxCall& call, TState& state, TMemberResult TProps::*member)
  394. {
  395. const auto& src = call.get_value<ContextException, const TMemberResult>();
  396. TValidator{}(src);
  397. const auto& dst_i = state.i.*member;
  398. auto& dst_d = state.d.*member;
  399. dst_d = src;
  400. if(dst_i != dst_d)
  401. mEaxDf.set(DirtyBit);
  402. }
  403. template<
  404. size_t DirtyBit,
  405. typename TMemberResult,
  406. typename TProps,
  407. typename TState>
  408. void eax_context_commit_property(TState& state, std::bitset<eax_dirty_bit_count>& dst_df,
  409. TMemberResult TProps::*member) noexcept
  410. {
  411. if(mEaxDf.test(DirtyBit))
  412. {
  413. dst_df.set(DirtyBit);
  414. const auto& src_d = state.d.*member;
  415. state.i.*member = src_d;
  416. mEax.*member = src_d;
  417. }
  418. }
  419. void eax_initialize_extensions();
  420. void eax_initialize();
  421. bool eax_has_no_default_effect_slot() const noexcept;
  422. void eax_ensure_no_default_effect_slot() const;
  423. bool eax_has_enough_aux_sends() const noexcept;
  424. void eax_ensure_enough_aux_sends() const;
  425. void eax_ensure_compatibility();
  426. unsigned long eax_detect_speaker_configuration() const;
  427. void eax_update_speaker_configuration();
  428. void eax_set_last_error_defaults() noexcept;
  429. void eax_session_set_defaults() noexcept;
  430. static void eax4_context_set_defaults(Eax4Props& props) noexcept;
  431. static void eax4_context_set_defaults(Eax4State& state) noexcept;
  432. static void eax5_context_set_defaults(Eax5Props& props) noexcept;
  433. static void eax5_context_set_defaults(Eax5State& state) noexcept;
  434. void eax_context_set_defaults();
  435. void eax_set_defaults();
  436. void eax_dispatch_fx_slot(const EaxCall& call);
  437. void eax_dispatch_source(const EaxCall& call);
  438. void eax_get_misc(const EaxCall& call);
  439. void eax4_get(const EaxCall& call, const Eax4Props& props);
  440. void eax5_get(const EaxCall& call, const Eax5Props& props);
  441. void eax_get(const EaxCall& call);
  442. void eax_context_commit_primary_fx_slot_id();
  443. void eax_context_commit_distance_factor();
  444. void eax_context_commit_air_absorption_hf();
  445. void eax_context_commit_hf_reference();
  446. void eax_context_commit_macro_fx_factor();
  447. void eax_initialize_fx_slots();
  448. void eax_update_sources();
  449. void eax_set_misc(const EaxCall& call);
  450. void eax4_defer_all(const EaxCall& call, Eax4State& state);
  451. void eax4_defer(const EaxCall& call, Eax4State& state);
  452. void eax5_defer_all(const EaxCall& call, Eax5State& state);
  453. void eax5_defer(const EaxCall& call, Eax5State& state);
  454. void eax_set(const EaxCall& call);
  455. void eax4_context_commit(Eax4State& state, std::bitset<eax_dirty_bit_count>& dst_df);
  456. void eax5_context_commit(Eax5State& state, std::bitset<eax_dirty_bit_count>& dst_df);
  457. void eax_context_commit();
  458. #endif // ALSOFT_EAX
  459. };
  460. using ContextRef = al::intrusive_ptr<ALCcontext>;
  461. ContextRef GetContextRef() noexcept;
  462. void UpdateContextProps(ALCcontext *context);
  463. inline bool TrapALError{false};
  464. #if ALSOFT_EAX
  465. auto AL_APIENTRY EAXSet(const GUID *property_set_id, ALuint property_id,
  466. ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum;
  467. auto AL_APIENTRY EAXGet(const GUID *property_set_id, ALuint property_id,
  468. ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum;
  469. #endif // ALSOFT_EAX
  470. #endif /* ALC_CONTEXT_H */