diff options
author | Chris Robinson <[email protected]> | 2018-01-20 11:49:01 -0800 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2018-01-20 11:49:01 -0800 |
commit | 03d4e4acc4c55ed87741b2b9960ac1ac3d4cf0bd (patch) | |
tree | ac94a5d8055d76ab6dab32ceb5f53967f2272793 | |
parent | 4e647bda07dd56479ea62de7a53b70c0154734e0 (diff) |
Add methods to "map" a buffer's storage
Requires the MAP_READ_BIT or MAP_WRITE_BIT flags to be OR'd with the format
upon a call to alBufferData, to enable mappable storage for the given access
types. This will fail if the format requires internal conversion and doesn't
resemble the original input data, so the app can be guaranteed the size, type,
and layout of the original data is the same as what's in storage.
Then alMapBufferSOFT may be called with appropriate bit flags to get a readable
and/or writable pointer to the buffer's sample storage. alUnmapBufferSOFT must
be called when access is finished. It is currently invalid to map a buffer that
is attached to a source, or to attach a buffer to a source that is currently
mapped. This restriction may be eased in the future, at least to allow read-
only access while in use (perhaps also to allow writing, if coherency can be
achieved).
Currently the access flags occupy the upper 8 bits of a 32-bit bitfield to
avoid clashing with format enum values, which don't use more than 16 or 17
bits. This means any future formats are limited to 24-bit enum values, and also
means only 8 flags are possible when declaring storage. The alternative would
be to add a new function (alBufferStorage?) with a separate flags parameter.
-rw-r--r-- | OpenAL32/Include/alBuffer.h | 3 | ||||
-rw-r--r-- | OpenAL32/Include/alMain.h | 12 | ||||
-rw-r--r-- | OpenAL32/alBuffer.c | 121 | ||||
-rw-r--r-- | OpenAL32/alSource.c | 13 |
4 files changed, 137 insertions, 12 deletions
diff --git a/OpenAL32/Include/alBuffer.h b/OpenAL32/Include/alBuffer.h index 852a8782..60a047c9 100644 --- a/OpenAL32/Include/alBuffer.h +++ b/OpenAL32/Include/alBuffer.h @@ -76,6 +76,7 @@ typedef struct ALbuffer { ALsizei Frequency; ALenum Format; + ALbitfieldSOFT Access; ALsizei SampleLen; enum FmtChannels FmtChannels; @@ -93,6 +94,8 @@ typedef struct ALbuffer { ATOMIC(ALsizei) UnpackAlign; ATOMIC(ALsizei) PackAlign; + ALbitfieldSOFT MappedAccess; + /* Number of times buffer was attached to a source (deletion can only occur when 0) */ RefCount ref; diff --git a/OpenAL32/Include/alMain.h b/OpenAL32/Include/alMain.h index 50c9decf..dbb4f14b 100644 --- a/OpenAL32/Include/alMain.h +++ b/OpenAL32/Include/alMain.h @@ -47,6 +47,18 @@ #define ALC_N3D_SOFT 0xfff7 #endif +#ifndef AL_SOFT_map_buffer +typedef unsigned int ALbitfieldSOFT; +#define AL_MAP_READ_BIT_SOFT 0x01000000 +#define AL_MAP_WRITE_BIT_SOFT 0x02000000 +typedef void* (AL_APIENTRY*LPALMAPBUFFERSOFT)(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access); +typedef void* (AL_APIENTRY*LPALUNMAPBUFFERSOFT)(ALuint buffer); +#ifdef AL_ALEXT_PROTOTYPES +AL_API void* AL_APIENTRY alMapBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access); +AL_API void AL_APIENTRY alUnmapBufferSOFT(ALuint buffer); +#endif +#endif + #if defined(_WIN64) #define SZFMT "%I64u" diff --git a/OpenAL32/alBuffer.c b/OpenAL32/alBuffer.c index ac755688..1c9b2650 100644 --- a/OpenAL32/alBuffer.c +++ b/OpenAL32/alBuffer.c @@ -45,7 +45,7 @@ extern inline struct ALbuffer *RemoveBuffer(ALCdevice *device, ALuint id); extern inline ALsizei FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type); extern inline ALsizei FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type); -static ALenum LoadData(ALbuffer *buffer, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALsizei align, ALboolean storesrc); +static ALenum LoadData(ALbuffer *buffer, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALsizei align, ALbitfieldSOFT access, ALboolean storesrc); static ALboolean IsValidType(ALenum type); static ALboolean IsValidChannels(ALenum channels); static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans, enum UserFmtType *type); @@ -53,6 +53,11 @@ static ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum Fm static ALsizei SanitizeAlignment(enum UserFmtType type, ALsizei align); +#define FORMAT_MASK 0x00ffffff +#define ACCESS_FLAGS (AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT) +#define INVALID_FLAG_MASK ~(FORMAT_MASK | ACCESS_FLAGS) + + AL_API ALvoid AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers) { ALCcontext *context; @@ -157,9 +162,9 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi LockBuffersRead(device); if((albuf=LookupBuffer(device, buffer)) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - if(!(size >= 0 && freq > 0)) + if(!(size >= 0 && freq > 0) || (format&INVALID_FLAG_MASK) != 0) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - if(DecomposeUserFormat(format, &srcchannels, &srctype) == AL_FALSE) + if(DecomposeUserFormat(format&FORMAT_MASK, &srcchannels, &srctype) == AL_FALSE) SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); align = SanitizeAlignment(srctype, ATOMIC_LOAD_SEQ(&albuf->UnpackAlign)); @@ -178,8 +183,9 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi if((size%framesize) != 0) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); - err = LoadData(albuf, freq, format, size/framesize*align, - srcchannels, srctype, data, align, AL_TRUE); + err = LoadData(albuf, freq, format&FORMAT_MASK, size/framesize*align, + srcchannels, srctype, data, align, format&ACCESS_FLAGS, + AL_TRUE); if(err != AL_NO_ERROR) SET_ERROR_AND_GOTO(context, err, done); break; @@ -204,7 +210,8 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi case UserFmtBFormat3D: newformat = AL_FORMAT_BFORMAT3D_FLOAT32; break; } err = LoadData(albuf, freq, newformat, size/framesize*align, - srcchannels, srctype, data, align, AL_TRUE); + srcchannels, srctype, data, align, format&ACCESS_FLAGS, + AL_TRUE); if(err != AL_NO_ERROR) SET_ERROR_AND_GOTO(context, err, done); break; @@ -228,7 +235,8 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi case UserFmtBFormat3D: newformat = AL_FORMAT_BFORMAT3D_16; break; } err = LoadData(albuf, freq, newformat, size/framesize*align, - srcchannels, srctype, data, align, AL_TRUE); + srcchannels, srctype, data, align, format&ACCESS_FLAGS, + AL_TRUE); if(err != AL_NO_ERROR) SET_ERROR_AND_GOTO(context, err, done); break; @@ -252,7 +260,8 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi case UserFmtBFormat3D: newformat = AL_FORMAT_BFORMAT3D_16; break; } err = LoadData(albuf, freq, newformat, size/framesize*align, - srcchannels, srctype, data, align, AL_TRUE); + srcchannels, srctype, data, align, format&ACCESS_FLAGS, + AL_TRUE); if(err != AL_NO_ERROR) SET_ERROR_AND_GOTO(context, err, done); break; @@ -263,6 +272,80 @@ done: ALCcontext_DecRef(context); } +AL_API void* AL_APIENTRY alMapBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access) +{ + void *retval = NULL; + ALCdevice *device; + ALCcontext *context; + ALbuffer *albuf; + + context = GetContextRef(); + if(!context) return retval; + + device = context->Device; + LockBuffersRead(device); + if((albuf=LookupBuffer(device, buffer)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + if(!access || (access&~(AL_MAP_READ_BIT_SOFT|AL_MAP_WRITE_BIT_SOFT)) != 0) + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + WriteLock(&albuf->lock); + if(((access&AL_MAP_READ_BIT_SOFT) && !(albuf->Access&AL_MAP_READ_BIT_SOFT)) || + ((access&AL_MAP_WRITE_BIT_SOFT) && !(albuf->Access&AL_MAP_WRITE_BIT_SOFT))) + { + WriteUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + } + if(offset < 0 || offset >= albuf->OriginalSize || + length <= 0 || length > albuf->OriginalSize - offset) + { + WriteUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); + } + if(ReadRef(&albuf->ref) != 0 || albuf->MappedAccess != 0) + { + WriteUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + } + + retval = (ALbyte*)albuf->data + offset; + albuf->MappedAccess = access; + if((access&AL_MAP_WRITE_BIT_SOFT) && !(access&AL_MAP_READ_BIT_SOFT)) + memset(retval, 0x55, length); + WriteUnlock(&albuf->lock); + +done: + UnlockBuffersRead(device); + ALCcontext_DecRef(context); + + return retval; +} + +AL_API void AL_APIENTRY alUnmapBufferSOFT(ALuint buffer) +{ + ALCdevice *device; + ALCcontext *context; + ALbuffer *albuf; + + context = GetContextRef(); + if(!context) return; + + device = context->Device; + LockBuffersRead(device); + if((albuf=LookupBuffer(device, buffer)) == NULL) + SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); + + WriteLock(&albuf->lock); + if(albuf->MappedAccess == 0) + alSetError(context, AL_INVALID_OPERATION); + else + albuf->MappedAccess = 0; + WriteUnlock(&albuf->lock); + +done: + UnlockBuffersRead(device); + ALCcontext_DecRef(context); +} + AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) { enum UserFmtChannels srcchannels = UserFmtMono; @@ -304,6 +387,11 @@ AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, cons WriteUnlock(&albuf->lock); SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); } + if(albuf->MappedAccess != 0) + { + WriteUnlock(&albuf->lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done); + } if(albuf->OriginalType == UserFmtIMA4) { @@ -362,7 +450,7 @@ AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, LockBuffersRead(device); if((albuf=LookupBuffer(device, buffer)) == NULL) SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done); - if(!(samples >= 0 && samplerate != 0)) + if(!(samples >= 0 && samplerate != 0) || (internalformat&~FORMAT_MASK) != 0) SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); if(IsValidType(type) == AL_FALSE || IsValidChannels(channels) == AL_FALSE) SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done); @@ -374,7 +462,7 @@ AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done); err = LoadData(albuf, samplerate, internalformat, samples, - channels, type, data, align, AL_FALSE); + channels, type, data, align, 0, AL_FALSE); if(err != AL_NO_ERROR) SET_ERROR_AND_GOTO(context, err, done); @@ -956,7 +1044,7 @@ done: * Currently, the new format must have the same channel configuration as the * original format. */ -static ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALsizei align, ALboolean storesrc) +static ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALsizei align, ALbitfieldSOFT access, ALboolean storesrc) { enum FmtChannels DstChannels = FmtMono; enum FmtType DstType = FmtUByte; @@ -968,6 +1056,12 @@ static ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei f if((long)SrcChannels != (long)DstChannels) return AL_INVALID_ENUM; + if(access != 0) + { + if(!storesrc || (long)SrcType != (long)DstType) + return AL_INVALID_VALUE; + } + NewChannels = ChannelsFromFmt(DstChannels); NewBytes = BytesFromFmt(DstType); @@ -978,7 +1072,7 @@ static ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei f return AL_OUT_OF_MEMORY; WriteLock(&ALBuf->lock); - if(ReadRef(&ALBuf->ref) != 0) + if(ReadRef(&ALBuf->ref) != 0 || ALBuf->MappedAccess != 0) { WriteUnlock(&ALBuf->lock); return AL_INVALID_OPERATION; @@ -1041,6 +1135,7 @@ static ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei f ALBuf->FmtChannels = DstChannels; ALBuf->FmtType = DstType; ALBuf->Format = NewFormat; + ALBuf->Access = access; ALBuf->SampleLen = frames; ALBuf->LoopStart = 0; @@ -1346,6 +1441,8 @@ ALbuffer *NewBuffer(ALCcontext *context) if(!buffer) SET_ERROR_AND_RETURN_VALUE(context, AL_OUT_OF_MEMORY, NULL); RWLockInit(&buffer->lock); + buffer->Access = 0; + buffer->MappedAccess = 0; err = NewThunkEntry(&buffer->id); if(err == AL_NO_ERROR) diff --git a/OpenAL32/alSource.c b/OpenAL32/alSource.c index 83233b3f..5f21b991 100644 --- a/OpenAL32/alSource.c +++ b/OpenAL32/alSource.c @@ -760,6 +760,13 @@ static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp p } WriteLock(&Source->queue_lock); + if(buffer && buffer->MappedAccess != 0) + { + WriteUnlock(&Source->queue_lock); + UnlockBuffersRead(device); + SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_OPERATION, AL_FALSE); + } + else { ALenum state = GetSourceState(Source, GetSourceVoice(Source, Context)); if(state == AL_PLAYING || state == AL_PAUSED) @@ -2881,6 +2888,12 @@ AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALu ReadLock(&buffer->lock); IncrementRef(&buffer->ref); + if(buffer->MappedAccess != 0) + { + WriteUnlock(&source->queue_lock); + SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, buffer_error); + } + if(BufferFmt == NULL) BufferFmt = buffer; else if(BufferFmt->Frequency != buffer->Frequency || |