Avoid class templates for the POPCNT64/CTZ64 macros
[openal-soft.git] / alc / alcontext.h
blob73a6617ee23a25f51e5b6e09e3ed3b3f5507971e
1 #ifndef ALCONTEXT_H
2 #define ALCONTEXT_H
4 #include <atomic>
5 #include <cstddef>
6 #include <cstdint>
7 #include <memory>
8 #include <mutex>
9 #include <thread>
10 #include <utility>
12 #include "AL/al.h"
13 #include "AL/alc.h"
15 #include "al/listener.h"
16 #include "almalloc.h"
17 #include "alnumeric.h"
18 #include "alu.h"
19 #include "atomic.h"
20 #include "inprogext.h"
21 #include "intrusive_ptr.h"
22 #include "threads.h"
23 #include "vector.h"
24 #include "voice.h"
26 struct ALeffectslot;
27 struct ALeffectslotProps;
28 struct ALsource;
29 struct RingBuffer;
32 enum class DistanceModel {
33 InverseClamped = AL_INVERSE_DISTANCE_CLAMPED,
34 LinearClamped = AL_LINEAR_DISTANCE_CLAMPED,
35 ExponentClamped = AL_EXPONENT_DISTANCE_CLAMPED,
36 Inverse = AL_INVERSE_DISTANCE,
37 Linear = AL_LINEAR_DISTANCE,
38 Exponent = AL_EXPONENT_DISTANCE,
39 Disable = AL_NONE,
41 Default = InverseClamped
45 struct ALcontextProps {
46 float DopplerFactor;
47 float DopplerVelocity;
48 float SpeedOfSound;
49 bool SourceDistanceModel;
50 DistanceModel mDistanceModel;
52 std::atomic<ALcontextProps*> next;
54 DEF_NEWDEL(ALcontextProps)
58 struct VoiceChange {
59 Voice *mOldVoice{nullptr};
60 Voice *mVoice{nullptr};
61 ALuint mSourceID{0};
62 ALenum mState{0};
64 std::atomic<VoiceChange*> mNext{nullptr};
66 DEF_NEWDEL(VoiceChange)
70 struct SourceSubList {
71 uint64_t FreeMask{~0_u64};
72 ALsource *Sources{nullptr}; /* 64 */
74 SourceSubList() noexcept = default;
75 SourceSubList(const SourceSubList&) = delete;
76 SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources}
77 { rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; }
78 ~SourceSubList();
80 SourceSubList& operator=(const SourceSubList&) = delete;
81 SourceSubList& operator=(SourceSubList&& rhs) noexcept
82 { std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; }
85 struct EffectSlotSubList {
86 uint64_t FreeMask{~0_u64};
87 ALeffectslot *EffectSlots{nullptr}; /* 64 */
89 EffectSlotSubList() noexcept = default;
90 EffectSlotSubList(const EffectSlotSubList&) = delete;
91 EffectSlotSubList(EffectSlotSubList&& rhs) noexcept
92 : FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots}
93 { rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; }
94 ~EffectSlotSubList();
96 EffectSlotSubList& operator=(const EffectSlotSubList&) = delete;
97 EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept
98 { std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; }
101 struct ALCcontext : public al::intrusive_ref<ALCcontext> {
102 al::vector<SourceSubList> mSourceList;
103 ALuint mNumSources{0};
104 std::mutex mSourceLock;
106 al::vector<EffectSlotSubList> mEffectSlotList;
107 ALuint mNumEffectSlots{0u};
108 std::mutex mEffectSlotLock;
110 std::atomic<ALenum> mLastError{AL_NO_ERROR};
112 DistanceModel mDistanceModel{DistanceModel::Default};
113 bool mSourceDistanceModel{false};
115 float mDopplerFactor{1.0f};
116 float mDopplerVelocity{1.0f};
117 float mSpeedOfSound{SPEEDOFSOUNDMETRESPERSEC};
119 std::atomic_flag mPropsClean;
120 std::atomic<bool> mDeferUpdates{false};
122 std::mutex mPropLock;
124 /* Counter for the pre-mixing updates, in 31.1 fixed point (lowest bit
125 * indicates if updates are currently happening).
127 RefCount mUpdateCount{0u};
128 std::atomic<bool> mHoldUpdates{false};
130 float mGainBoost{1.0f};
132 std::atomic<ALcontextProps*> mUpdate{nullptr};
134 /* Linked lists of unused property containers, free to use for future
135 * updates.
137 std::atomic<ALcontextProps*> mFreeContextProps{nullptr};
138 std::atomic<ALlistenerProps*> mFreeListenerProps{nullptr};
139 std::atomic<VoicePropsItem*> mFreeVoiceProps{nullptr};
140 std::atomic<ALeffectslotProps*> mFreeEffectslotProps{nullptr};
142 /* Asynchronous voice change actions are processed as a linked list of
143 * VoiceChange objects by the mixer, which is atomically appended to.
144 * However, to avoid allocating each object individually, they're allocated
145 * in clusters that are stored in a vector for easy automatic cleanup.
147 using VoiceChangeCluster = std::unique_ptr<VoiceChange[]>;
148 al::vector<VoiceChangeCluster> mVoiceChangeClusters;
150 /* The voice change tail is the beginning of the "free" elements, up to and
151 * *excluding* the current. If tail==current, there's no free elements and
152 * new ones need to be allocated. The current voice change is the element
153 * last processed, and any after are pending.
155 VoiceChange *mVoiceChangeTail{};
156 std::atomic<VoiceChange*> mCurrentVoiceChange{};
158 void allocVoiceChanges(size_t addcount);
161 using VoiceCluster = std::unique_ptr<Voice[]>;
162 al::vector<VoiceCluster> mVoiceClusters;
164 using VoiceArray = al::FlexArray<Voice*>;
165 std::atomic<VoiceArray*> mVoices{};
166 std::atomic<size_t> mActiveVoiceCount{};
168 void allocVoices(size_t addcount);
169 al::span<Voice*> getVoicesSpan() const noexcept
171 return {mVoices.load(std::memory_order_relaxed)->data(),
172 mActiveVoiceCount.load(std::memory_order_relaxed)};
174 al::span<Voice*> getVoicesSpanAcquired() const noexcept
176 return {mVoices.load(std::memory_order_acquire)->data(),
177 mActiveVoiceCount.load(std::memory_order_acquire)};
181 using ALeffectslotArray = al::FlexArray<ALeffectslot*>;
182 std::atomic<ALeffectslotArray*> mActiveAuxSlots{nullptr};
184 std::thread mEventThread;
185 al::semaphore mEventSem;
186 std::unique_ptr<RingBuffer> mAsyncEvents;
187 std::atomic<ALbitfieldSOFT> mEnabledEvts{0u};
188 std::mutex mEventCbLock;
189 ALEVENTPROCSOFT mEventCb{};
190 void *mEventParam{nullptr};
192 /* Default effect slot */
193 std::unique_ptr<ALeffectslot> mDefaultSlot;
195 const al::intrusive_ptr<ALCdevice> mDevice;
196 const char *mExtensionList{nullptr};
198 ALlistener mListener{};
201 ALCcontext(al::intrusive_ptr<ALCdevice> device);
202 ALCcontext(const ALCcontext&) = delete;
203 ALCcontext& operator=(const ALCcontext&) = delete;
204 ~ALCcontext();
206 void init();
208 * Removes the context from its device and removes it from being current on
209 * the running thread or globally. Returns true if other contexts still
210 * exist on the device.
212 bool deinit();
215 * Defers/suspends updates for the given context's listener and sources.
216 * This does *NOT* stop mixing, but rather prevents certain property
217 * changes from taking effect.
219 void deferUpdates() noexcept { mDeferUpdates.exchange(true, std::memory_order_acq_rel); }
221 /** Resumes update processing after being deferred. */
222 void processUpdates();
224 [[gnu::format(printf,3,4)]] void setError(ALenum errorCode, const char *msg, ...);
226 DEF_NEWDEL(ALCcontext)
229 #define SETERR_RETURN(ctx, err, retval, ...) do { \
230 (ctx)->setError((err), __VA_ARGS__); \
231 return retval; \
232 } while(0)
235 using ContextRef = al::intrusive_ptr<ALCcontext>;
237 ContextRef GetContextRef(void);
239 void UpdateContextProps(ALCcontext *context);
242 extern bool TrapALError;
244 #endif /* ALCONTEXT_H */