context.h 17 KB

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