Fix some Clazy warnings
[openal-soft.git] / alc / context.h
blobf45c5d53040d30dde8710591987c20117ec0976e
1 #ifndef ALC_CONTEXT_H
2 #define ALC_CONTEXT_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>
16 #include "AL/al.h"
17 #include "AL/alc.h"
18 #include "AL/alext.h"
20 #include "al/listener.h"
21 #include "althreads.h"
22 #include "core/context.h"
23 #include "intrusive_ptr.h"
24 #include "opthelpers.h"
26 #ifdef ALSOFT_EAX
27 #include "al/eax/api.h"
28 #include "al/eax/exception.h"
29 #include "al/eax/fx_slot_index.h"
30 #include "al/eax/fx_slots.h"
31 #include "al/eax/utils.h"
33 class EaxCall;
34 #endif // ALSOFT_EAX
36 struct ALeffect;
37 struct ALeffectslot;
38 struct DebugGroup;
39 struct EffectSlotSubList;
40 struct SourceSubList;
42 enum class DebugSource : std::uint8_t;
43 enum class DebugType : std::uint8_t;
44 enum class DebugSeverity : std::uint8_t;
46 using uint = unsigned int;
49 enum ContextFlags {
50 DebugBit = 0, /* ALC_CONTEXT_DEBUG_BIT_EXT */
52 using ContextFlagBitset = std::bitset<sizeof(ALuint)*8>;
55 struct DebugLogEntry {
56 const DebugSource mSource;
57 const DebugType mType;
58 const DebugSeverity mSeverity;
59 const uint mId;
61 std::string mMessage;
63 template<typename T>
64 DebugLogEntry(DebugSource source, DebugType type, uint id, DebugSeverity severity, T&& message)
65 : mSource{source}, mType{type}, mSeverity{severity}, mId{id}
66 , mMessage{std::forward<T>(message)}
67 { }
68 DebugLogEntry(const DebugLogEntry&) = default;
69 DebugLogEntry(DebugLogEntry&&) = default;
73 struct ALCcontext final : public al::intrusive_ref<ALCcontext>, ContextBase {
74 const al::intrusive_ptr<ALCdevice> mALDevice;
77 bool mPropsDirty{true};
78 bool mDeferUpdates{false};
80 std::mutex mPropLock;
82 al::tss<ALenum> mLastThreadError{AL_NO_ERROR};
84 const ContextFlagBitset mContextFlags;
85 std::atomic<bool> mDebugEnabled{false};
87 DistanceModel mDistanceModel{DistanceModel::Default};
88 bool mSourceDistanceModel{false};
90 float mDopplerFactor{1.0f};
91 float mDopplerVelocity{1.0f};
92 float mSpeedOfSound{SpeedOfSoundMetersPerSec};
93 float mAirAbsorptionGainHF{AirAbsorbGainHF};
95 std::mutex mEventCbLock;
96 ALEVENTPROCSOFT mEventCb{};
97 void *mEventParam{nullptr};
99 std::mutex mDebugCbLock;
100 ALDEBUGPROCEXT mDebugCb{};
101 void *mDebugParam{nullptr};
102 std::vector<DebugGroup> mDebugGroups;
103 std::deque<DebugLogEntry> mDebugLog;
105 ALlistener mListener{};
107 std::vector<SourceSubList> mSourceList;
108 ALuint mNumSources{0};
109 std::mutex mSourceLock;
111 std::vector<EffectSlotSubList> mEffectSlotList;
112 ALuint mNumEffectSlots{0u};
113 std::mutex mEffectSlotLock;
115 /* Default effect slot */
116 std::unique_ptr<ALeffectslot> mDefaultSlot;
118 std::vector<std::string_view> mExtensions;
119 std::string mExtensionsString{};
121 std::unordered_map<ALuint,std::string> mSourceNames;
122 std::unordered_map<ALuint,std::string> mEffectSlotNames;
124 ALCcontext(al::intrusive_ptr<ALCdevice> device, ContextFlagBitset flags);
125 ALCcontext(const ALCcontext&) = delete;
126 ALCcontext& operator=(const ALCcontext&) = delete;
127 ~ALCcontext() final;
129 void init();
131 * Removes the context from its device and removes it from being current on
132 * the running thread or globally. Stops device playback if this was the
133 * last context on its device.
135 void deinit();
138 * Defers/suspends updates for the given context's listener and sources.
139 * This does *NOT* stop mixing, but rather prevents certain property
140 * changes from taking effect. mPropLock must be held when called.
142 void deferUpdates() noexcept { mDeferUpdates = true; }
145 * Resumes update processing after being deferred. mPropLock must be held
146 * when called.
148 void processUpdates()
150 if(std::exchange(mDeferUpdates, false))
151 applyAllUpdates();
155 * Applies all pending updates for the context, listener, effect slots, and
156 * sources.
158 void applyAllUpdates();
160 #ifdef __MINGW32__
161 [[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]]
162 #else
163 [[gnu::format(printf, 3, 4)]]
164 #endif
165 void setError(ALenum errorCode, const char *msg, ...);
167 void sendDebugMessage(std::unique_lock<std::mutex> &debuglock, DebugSource source,
168 DebugType type, ALuint id, DebugSeverity severity, std::string_view message);
170 void debugMessage(DebugSource source, DebugType type, ALuint id, DebugSeverity severity,
171 std::string_view message)
173 if(!mDebugEnabled.load(std::memory_order_relaxed)) LIKELY
174 return;
175 std::unique_lock<std::mutex> debuglock{mDebugCbLock};
176 sendDebugMessage(debuglock, source, type, id, severity, message);
179 /* Process-wide current context */
180 static std::atomic<bool> sGlobalContextLock;
181 static std::atomic<ALCcontext*> sGlobalContext;
183 private:
184 /* Thread-local current context. */
185 static inline thread_local ALCcontext *sLocalContext{};
187 /* Thread-local context handling. This handles attempting to release the
188 * context which may have been left current when the thread is destroyed.
190 class ThreadCtx {
191 public:
192 ThreadCtx() = default;
193 ThreadCtx(const ThreadCtx&) = delete;
194 auto operator=(const ThreadCtx&) -> ThreadCtx& = delete;
196 ~ThreadCtx();
197 /* NOLINTBEGIN(readability-convert-member-functions-to-static)
198 * This should be non-static to invoke construction of the thread-local
199 * sThreadContext, so that it's destructor gets run at thread exit to
200 * clear sLocalContext (which isn't a member variable to make read
201 * access efficient).
203 void set(ALCcontext *ctx) const noexcept { sLocalContext = ctx; }
204 /* NOLINTEND(readability-convert-member-functions-to-static) */
206 static thread_local ThreadCtx sThreadContext;
208 public:
209 static ALCcontext *getThreadContext() noexcept { return sLocalContext; }
210 static void setThreadContext(ALCcontext *context) noexcept { sThreadContext.set(context); }
212 /* Default effect that applies to sources that don't have an effect on send 0. */
213 static ALeffect sDefaultEffect;
215 #ifdef ALSOFT_EAX
216 bool hasEax() const noexcept { return mEaxIsInitialized; }
217 bool eaxIsCapable() const noexcept;
219 void eaxUninitialize() noexcept;
221 ALenum eax_eax_set(
222 const GUID* property_set_id,
223 ALuint property_id,
224 ALuint property_source_id,
225 ALvoid* property_value,
226 ALuint property_value_size);
228 ALenum eax_eax_get(
229 const GUID* property_set_id,
230 ALuint property_id,
231 ALuint property_source_id,
232 ALvoid* property_value,
233 ALuint property_value_size);
235 void eaxSetLastError() noexcept;
237 [[nodiscard]]
238 auto eaxGetDistanceFactor() const noexcept -> float { return mEax.flDistanceFactor; }
240 [[nodiscard]]
241 auto eaxGetPrimaryFxSlotIndex() const noexcept -> EaxFxSlotIndex
242 { return mEaxPrimaryFxSlotIndex; }
244 const ALeffectslot& eaxGetFxSlot(EaxFxSlotIndexValue fx_slot_index) const
245 { return mEaxFxSlots.get(fx_slot_index); }
246 ALeffectslot& eaxGetFxSlot(EaxFxSlotIndexValue fx_slot_index)
247 { return mEaxFxSlots.get(fx_slot_index); }
249 bool eaxNeedsCommit() const noexcept { return mEaxNeedsCommit; }
250 void eaxCommit();
252 void eaxCommitFxSlots()
253 { mEaxFxSlots.commit(); }
255 private:
256 static constexpr auto eax_primary_fx_slot_id_dirty_bit = EaxDirtyFlags{1} << 0;
257 static constexpr auto eax_distance_factor_dirty_bit = EaxDirtyFlags{1} << 1;
258 static constexpr auto eax_air_absorption_hf_dirty_bit = EaxDirtyFlags{1} << 2;
259 static constexpr auto eax_hf_reference_dirty_bit = EaxDirtyFlags{1} << 3;
260 static constexpr auto eax_macro_fx_factor_dirty_bit = EaxDirtyFlags{1} << 4;
262 using Eax4Props = EAX40CONTEXTPROPERTIES;
264 struct Eax4State {
265 Eax4Props i; // Immediate.
266 Eax4Props d; // Deferred.
269 using Eax5Props = EAX50CONTEXTPROPERTIES;
271 struct Eax5State {
272 Eax5Props i; // Immediate.
273 Eax5Props d; // Deferred.
276 class ContextException final : public EaxException {
277 public:
278 explicit ContextException(const char *message)
279 : EaxException{"EAX_CONTEXT", message}
283 struct Eax4PrimaryFxSlotIdValidator {
284 void operator()(const GUID& guidPrimaryFXSlotID) const
286 if(guidPrimaryFXSlotID != EAX_NULL_GUID &&
287 guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot0 &&
288 guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot1 &&
289 guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot2 &&
290 guidPrimaryFXSlotID != EAXPROPERTYID_EAX40_FXSlot3)
292 eax_fail_unknown_primary_fx_slot_id();
297 struct Eax4DistanceFactorValidator {
298 void operator()(float flDistanceFactor) const
300 eax_validate_range<ContextException>(
301 "Distance Factor",
302 flDistanceFactor,
303 EAXCONTEXT_MINDISTANCEFACTOR,
304 EAXCONTEXT_MAXDISTANCEFACTOR);
308 struct Eax4AirAbsorptionHfValidator {
309 void operator()(float flAirAbsorptionHF) const
311 eax_validate_range<ContextException>(
312 "Air Absorption HF",
313 flAirAbsorptionHF,
314 EAXCONTEXT_MINAIRABSORPTIONHF,
315 EAXCONTEXT_MAXAIRABSORPTIONHF);
319 struct Eax4HfReferenceValidator {
320 void operator()(float flHFReference) const
322 eax_validate_range<ContextException>(
323 "HF Reference",
324 flHFReference,
325 EAXCONTEXT_MINHFREFERENCE,
326 EAXCONTEXT_MAXHFREFERENCE);
330 struct Eax4AllValidator {
331 void operator()(const EAX40CONTEXTPROPERTIES& all) const
333 Eax4PrimaryFxSlotIdValidator{}(all.guidPrimaryFXSlotID);
334 Eax4DistanceFactorValidator{}(all.flDistanceFactor);
335 Eax4AirAbsorptionHfValidator{}(all.flAirAbsorptionHF);
336 Eax4HfReferenceValidator{}(all.flHFReference);
340 struct Eax5PrimaryFxSlotIdValidator {
341 void operator()(const GUID& guidPrimaryFXSlotID) const
343 if(guidPrimaryFXSlotID != EAX_NULL_GUID &&
344 guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot0 &&
345 guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot1 &&
346 guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot2 &&
347 guidPrimaryFXSlotID != EAXPROPERTYID_EAX50_FXSlot3)
349 eax_fail_unknown_primary_fx_slot_id();
354 struct Eax5MacroFxFactorValidator {
355 void operator()(float flMacroFXFactor) const
357 eax_validate_range<ContextException>(
358 "Macro FX Factor",
359 flMacroFXFactor,
360 EAXCONTEXT_MINMACROFXFACTOR,
361 EAXCONTEXT_MAXMACROFXFACTOR);
365 struct Eax5AllValidator {
366 void operator()(const EAX50CONTEXTPROPERTIES& all) const
368 Eax5PrimaryFxSlotIdValidator{}(all.guidPrimaryFXSlotID);
369 Eax4DistanceFactorValidator{}(all.flDistanceFactor);
370 Eax4AirAbsorptionHfValidator{}(all.flAirAbsorptionHF);
371 Eax4HfReferenceValidator{}(all.flHFReference);
372 Eax5MacroFxFactorValidator{}(all.flMacroFXFactor);
376 struct Eax5EaxVersionValidator {
377 void operator()(unsigned long ulEAXVersion) const
379 eax_validate_range<ContextException>(
380 "EAX version",
381 ulEAXVersion,
382 EAXCONTEXT_MINEAXSESSION,
383 EAXCONTEXT_MAXEAXSESSION);
387 struct Eax5MaxActiveSendsValidator {
388 void operator()(unsigned long ulMaxActiveSends) const
390 eax_validate_range<ContextException>(
391 "Max Active Sends",
392 ulMaxActiveSends,
393 EAXCONTEXT_MINMAXACTIVESENDS,
394 EAXCONTEXT_MAXMAXACTIVESENDS);
398 struct Eax5SessionAllValidator {
399 void operator()(const EAXSESSIONPROPERTIES& all) const
401 Eax5EaxVersionValidator{}(all.ulEAXVersion);
402 Eax5MaxActiveSendsValidator{}(all.ulMaxActiveSends);
406 struct Eax5SpeakerConfigValidator {
407 void operator()(unsigned long ulSpeakerConfig) const
409 eax_validate_range<ContextException>(
410 "Speaker Config",
411 ulSpeakerConfig,
412 EAXCONTEXT_MINSPEAKERCONFIG,
413 EAXCONTEXT_MAXSPEAKERCONFIG);
417 bool mEaxIsInitialized{};
418 bool mEaxIsTried{};
420 long mEaxLastError{};
421 unsigned long mEaxSpeakerConfig{};
423 EaxFxSlotIndex mEaxPrimaryFxSlotIndex{};
424 EaxFxSlots mEaxFxSlots{};
426 int mEaxVersion{}; // Current EAX version.
427 bool mEaxNeedsCommit{};
428 EaxDirtyFlags mEaxDf{}; // Dirty flags for the current EAX version.
429 Eax5State mEax123{}; // EAX1/EAX2/EAX3 state.
430 Eax4State mEax4{}; // EAX4 state.
431 Eax5State mEax5{}; // EAX5 state.
432 Eax5Props mEax{}; // Current EAX state.
433 EAXSESSIONPROPERTIES mEaxSession{};
435 [[noreturn]] static void eax_fail(const char* message);
436 [[noreturn]] static void eax_fail_unknown_property_set_id();
437 [[noreturn]] static void eax_fail_unknown_primary_fx_slot_id();
438 [[noreturn]] static void eax_fail_unknown_property_id();
439 [[noreturn]] static void eax_fail_unknown_version();
441 // Gets a value from EAX call,
442 // validates it,
443 // and updates the current value.
444 template<typename TValidator, typename TProperty>
445 static void eax_set(const EaxCall& call, TProperty& property)
447 const auto& value = call.get_value<ContextException, const TProperty>();
448 TValidator{}(value);
449 property = value;
452 // Gets a new value from EAX call,
453 // validates it,
454 // updates the deferred value,
455 // updates a dirty flag.
456 template<
457 typename TValidator,
458 EaxDirtyFlags TDirtyBit,
459 typename TMemberResult,
460 typename TProps,
461 typename TState>
462 void eax_defer(const EaxCall& call, TState& state, TMemberResult TProps::*member)
464 const auto& src = call.get_value<ContextException, const TMemberResult>();
465 TValidator{}(src);
466 const auto& dst_i = state.i.*member;
467 auto& dst_d = state.d.*member;
468 dst_d = src;
470 if(dst_i != dst_d)
471 mEaxDf |= TDirtyBit;
474 template<
475 EaxDirtyFlags TDirtyBit,
476 typename TMemberResult,
477 typename TProps,
478 typename TState>
479 void eax_context_commit_property(TState& state, EaxDirtyFlags& dst_df,
480 TMemberResult TProps::*member) noexcept
482 if((mEaxDf & TDirtyBit) != EaxDirtyFlags{})
484 dst_df |= TDirtyBit;
485 const auto& src_d = state.d.*member;
486 state.i.*member = src_d;
487 mEax.*member = src_d;
491 void eax_initialize_extensions();
492 void eax_initialize();
494 bool eax_has_no_default_effect_slot() const noexcept;
495 void eax_ensure_no_default_effect_slot() const;
496 bool eax_has_enough_aux_sends() const noexcept;
497 void eax_ensure_enough_aux_sends() const;
498 void eax_ensure_compatibility();
500 unsigned long eax_detect_speaker_configuration() const;
501 void eax_update_speaker_configuration();
503 void eax_set_last_error_defaults() noexcept;
504 void eax_session_set_defaults() noexcept;
505 static void eax4_context_set_defaults(Eax4Props& props) noexcept;
506 static void eax4_context_set_defaults(Eax4State& state) noexcept;
507 static void eax5_context_set_defaults(Eax5Props& props) noexcept;
508 static void eax5_context_set_defaults(Eax5State& state) noexcept;
509 void eax_context_set_defaults();
510 void eax_set_defaults();
512 void eax_dispatch_fx_slot(const EaxCall& call);
513 void eax_dispatch_source(const EaxCall& call);
515 void eax_get_misc(const EaxCall& call);
516 void eax4_get(const EaxCall& call, const Eax4Props& props);
517 void eax5_get(const EaxCall& call, const Eax5Props& props);
518 void eax_get(const EaxCall& call);
520 void eax_context_commit_primary_fx_slot_id();
521 void eax_context_commit_distance_factor();
522 void eax_context_commit_air_absorbtion_hf();
523 void eax_context_commit_hf_reference();
524 void eax_context_commit_macro_fx_factor();
526 void eax_initialize_fx_slots();
528 void eax_update_sources();
530 void eax_set_misc(const EaxCall& call);
531 void eax4_defer_all(const EaxCall& call, Eax4State& state);
532 void eax4_defer(const EaxCall& call, Eax4State& state);
533 void eax5_defer_all(const EaxCall& call, Eax5State& state);
534 void eax5_defer(const EaxCall& call, Eax5State& state);
535 void eax_set(const EaxCall& call);
537 void eax4_context_commit(Eax4State& state, EaxDirtyFlags& dst_df);
538 void eax5_context_commit(Eax5State& state, EaxDirtyFlags& dst_df);
539 void eax_context_commit();
540 #endif // ALSOFT_EAX
543 using ContextRef = al::intrusive_ptr<ALCcontext>;
545 ContextRef GetContextRef() noexcept;
547 void UpdateContextProps(ALCcontext *context);
550 inline bool TrapALError{false};
553 #ifdef ALSOFT_EAX
554 auto AL_APIENTRY EAXSet(const GUID *property_set_id, ALuint property_id,
555 ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum;
557 auto AL_APIENTRY EAXGet(const GUID *property_set_id, ALuint property_id,
558 ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum;
559 #endif // ALSOFT_EAX
561 #endif /* ALC_CONTEXT_H */