diff options
-rw-r--r-- | al/buffer.cpp | 297 | ||||
-rw-r--r-- | al/buffer.h | 1 | ||||
-rw-r--r-- | al/state.cpp | 2 | ||||
-rw-r--r-- | alc/device.h | 2 |
4 files changed, 117 insertions, 185 deletions
diff --git a/al/buffer.cpp b/al/buffer.cpp index 3d9caf54..48a19d00 100644 --- a/al/buffer.cpp +++ b/al/buffer.cpp @@ -364,6 +364,40 @@ al::optional<FmtType> FmtFromUserFmt(UserFmtType type) } +#ifdef ALSOFT_EAX +bool eax_x_ram_check_availability(const ALCdevice &device, const ALbuffer &buffer, + const ALuint newsize) noexcept +{ + ALuint freemem{device.eax_x_ram_free_size}; + /* If the buffer is currently in "hardware", add its memory to the free + * pool since it'll be "replaced". + */ + if(buffer.eax_x_ram_is_hardware) + freemem += buffer.OriginalSize; + return freemem >= newsize; +} + +void eax_x_ram_apply(ALCdevice &device, ALbuffer &buffer) noexcept +{ + if(buffer.eax_x_ram_is_hardware) + return; + + if(device.eax_x_ram_free_size >= buffer.OriginalSize) + { + device.eax_x_ram_free_size -= buffer.OriginalSize; + buffer.eax_x_ram_is_hardware = true; + } +} + +void eax_x_ram_clear(ALCdevice& al_device, ALbuffer& al_buffer) +{ + if(al_buffer.eax_x_ram_is_hardware) + al_device.eax_x_ram_free_size += al_buffer.OriginalSize; + al_buffer.eax_x_ram_is_hardware = false; +} +#endif // ALSOFT_EAX + + constexpr ALbitfieldSOFT INVALID_STORAGE_MASK{~unsigned(AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT | AL_MAP_PERSISTENT_BIT_SOFT | AL_PRESERVE_DATA_BIT_SOFT)}; constexpr ALbitfieldSOFT MAP_READ_WRITE_FLAGS{AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT}; @@ -418,12 +452,7 @@ ALbuffer *AllocBuffer(ALCdevice *device) void FreeBuffer(ALCdevice *device, ALbuffer *buffer) { #ifdef ALSOFT_EAX - if (buffer->eax_x_ram_is_hardware) - { - const auto buffer_size = static_cast<ALsizei>(buffer->OriginalSize); - assert((device->eax_x_ram_free_size + buffer_size) <= eax_x_ram_max_size); - device->eax_x_ram_free_size += buffer_size; - } + eax_x_ram_clear(*device, *buffer); #endif // ALSOFT_EAX const ALuint id{buffer->id - 1}; @@ -499,102 +528,6 @@ const ALchar *NameFromUserFmtType(UserFmtType type) return "<internal type error>"; } -#ifdef ALSOFT_EAX -bool eax_x_ram_validate_buffer( - ALCdevice& al_device, - ALbuffer& al_buffer) -{ - switch (al_buffer.eax_x_ram_mode) - { - case AL_STORAGE_HARDWARE: - if (al_buffer.OriginalSize > static_cast<ALuint>(al_device.eax_x_ram_free_size)) - { - return false; - } - - break; - - case AL_STORAGE_AUTOMATIC: - case AL_STORAGE_ACCESSIBLE: - break; - - default: - assert(false && "Unsupported X-RAM mode."); - return false; - } - - return true; -} - -void eax_x_ram_update_buffer( - ALCdevice& al_device, - ALbuffer& al_buffer) -{ - const auto buffer_size = static_cast<ALsizei>(al_buffer.OriginalSize); - - auto is_hardware = al_buffer.eax_x_ram_is_hardware; - auto size_delta = ALsizei{}; - - switch (al_buffer.eax_x_ram_mode) - { - case AL_STORAGE_AUTOMATIC: - if (!al_buffer.eax_x_ram_is_dirty) - { - // First usage. - - if (buffer_size <= al_device.eax_x_ram_free_size) - { - // Have enough X-RAM memory. - - is_hardware = true; - size_delta = -buffer_size; - } - } - else - { - // Used at least once. - // From now on, use only system memory. - - is_hardware = false; - - if (al_buffer.eax_x_ram_is_hardware) - { - // First allocation was in X-RAM. - // Free that block. - - size_delta = buffer_size; - } - } - - break; - - case AL_STORAGE_HARDWARE: - is_hardware = true; - size_delta = buffer_size; - - break; - - case AL_STORAGE_ACCESSIBLE: - // Always use system memory. - is_hardware = false; - break; - - default: - break; - } - - al_buffer.eax_x_ram_is_hardware = is_hardware; - al_buffer.eax_x_ram_is_dirty = true; - - assert( - (al_device.eax_x_ram_free_size + size_delta) >= eax_x_ram_min_size && - (al_device.eax_x_ram_free_size + size_delta) <= eax_x_ram_max_size - ); - - al_device.eax_x_ram_free_size += size_delta; -} -#endif // ALSOFT_EAX - /** Loads the specified data into the buffer, using the specified format. */ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, UserFmtChannels SrcChannels, UserFmtType SrcType, const al::byte *SrcData, @@ -673,6 +606,16 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, "Buffer size overflow, %d frames x %d bytes per frame", frames, FrameSize); size_t newsize{static_cast<size_t>(frames) * FrameSize}; +#ifdef ALSOFT_EAX + if(ALBuf->eax_x_ram_mode == AL_STORAGE_HARDWARE) + { + ALCdevice &device = *context->mALDevice; + if(!eax_x_ram_check_availability(device, *ALBuf, size)) + SETERR_RETURN(context, AL_OUT_OF_MEMORY,, + "Out of X-RAM memory (avail: %u, needed: %u)", device.eax_x_ram_free_size, size); + } +#endif + /* Round up to the next 16-byte multiple. This could reallocate only when * increasing or the new size is less than half the current, but then the * buffer's AL_SIZE would not be very reliable for accounting buffer memory @@ -732,11 +675,9 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, ALBuf->mLoopEnd = ALBuf->mSampleLen; #ifdef ALSOFT_EAX - if (eax_g_is_enabled) - { - eax_x_ram_update_buffer(*context->mALDevice, *ALBuf); - } -#endif // ALSOFT_EAX + if(eax_g_is_enabled && ALBuf->eax_x_ram_mode != AL_STORAGE_ACCESSIBLE) + eax_x_ram_apply(*context->mALDevice, *ALBuf); +#endif } /** Prepares the buffer to use the specified callback, using the specified format. */ @@ -765,6 +706,10 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, al::vector<al::byte,16>(FrameSizeFromFmt(*DstChannels, *DstType, ambiorder) * size_t{line_size}).swap(ALBuf->mData); +#ifdef ALSOFT_EAX + eax_x_ram_clear(*context->mALDevice, *ALBuf); +#endif + ALBuf->mCallback = callback; ALBuf->mUserData = userptr; @@ -1007,18 +952,6 @@ START_API_FUNC context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format); else { -#ifdef ALSOFT_EAX - if (eax_g_is_enabled) - { - const auto is_buffer_valid = eax_x_ram_validate_buffer(*device, *albuf); - - if (!is_buffer_valid) - { - context->setError(AL_OUT_OF_MEMORY, "Out of X-RAM memory."); - return; - } - } -#endif // ALSOFT_EAX LoadData(context.get(), albuf, freq, static_cast<ALuint>(size), usrfmt->channels, usrfmt->type, static_cast<const al::byte*>(data), flags); } @@ -1776,101 +1709,111 @@ BufferSubList::~BufferSubList() #ifdef ALSOFT_EAX -ALboolean AL_APIENTRY EAXSetBufferMode( - ALsizei n, - const ALuint* buffers, - ALint value) +ALboolean AL_APIENTRY EAXSetBufferMode(ALsizei n, const ALuint* buffers, ALint value) START_API_FUNC { #define EAX_PREFIX "[EAXSetBufferMode] " - if (n == 0) - { - return ALC_TRUE; - } - - if (n < 0) + const auto context = ContextRef{GetContextRef()}; + if(!context) { - ERR(EAX_PREFIX "Buffer count %d out of range.\n", n); + ERR(EAX_PREFIX "%s\n", "No current context."); return ALC_FALSE; } - if (!buffers) + if(!eax_g_is_enabled) { - ERR(EAX_PREFIX "%s\n", "Null AL buffers."); + context->setError(AL_INVALID_OPERATION, EAX_PREFIX "%s", "EAX not enabled."); return ALC_FALSE; } - switch (value) + switch(value) { - case AL_STORAGE_AUTOMATIC: - case AL_STORAGE_HARDWARE: - case AL_STORAGE_ACCESSIBLE: - break; + case AL_STORAGE_AUTOMATIC: + case AL_STORAGE_HARDWARE: + case AL_STORAGE_ACCESSIBLE: + break; - default: - ERR(EAX_PREFIX "Unsupported X-RAM mode %d.\n", value); - return ALC_FALSE; + default: + context->setError(AL_INVALID_ENUM, EAX_PREFIX "Unsupported X-RAM mode 0x%x", value); + return ALC_FALSE; } - const auto context = ContextRef{GetContextRef()}; + if(n == 0) + return ALC_TRUE; - if (!context) + if(n < 0) { - ERR(EAX_PREFIX "%s\n", "No current context."); + context->setError(AL_INVALID_VALUE, EAX_PREFIX "Buffer count %d out of range", n); return ALC_FALSE; } - if (!eax_g_is_enabled) + if(!buffers) { - ERR(EAX_PREFIX "%s\n", "EAX not enabled."); + context->setError(AL_INVALID_VALUE, EAX_PREFIX "%s", "Null AL buffers"); return ALC_FALSE; } auto device = context->mALDevice.get(); std::lock_guard<std::mutex> device_lock{device->BufferLock}; + size_t total_needed{0}; // Validate the buffers. // - for (auto i = 0; i < n; ++i) + for(auto i = 0;i < n;++i) { const auto buffer = buffers[i]; - - if (buffer == AL_NONE) - { + if(buffer == AL_NONE) continue; - } const auto al_buffer = LookupBuffer(device, buffer); - if (!al_buffer) { ERR(EAX_PREFIX "Invalid buffer ID %u.\n", buffer); return ALC_FALSE; } - if (al_buffer->eax_x_ram_is_dirty) + /* TODO: Is the store location allowed to change for in-use buffers, or + * only when not set/queued on a source? + */ + + if(value == AL_STORAGE_HARDWARE && !al_buffer->eax_x_ram_is_hardware) { - ERR(EAX_PREFIX "Buffer %u has audio data.\n", buffer); - return ALC_FALSE; + /* FIXME: This doesn't account for duplicate buffers. When the same + * buffer ID is specified multiple times in the provided list, it + * counts each instance as more memory that needs to fit in X-RAM. + */ + if(unlikely(std::numeric_limits<size_t>::max()-al_buffer->OriginalSize < total_needed)) + { + context->setError(AL_OUT_OF_MEMORY, EAX_PREFIX "Buffer size overflow (%u + %zu)\n", + al_buffer->OriginalSize, total_needed); + return ALC_FALSE; + } + total_needed += al_buffer->OriginalSize; } } + if(total_needed > device->eax_x_ram_free_size) + { + context->setError(AL_INVALID_ENUM, EAX_PREFIX "Out of X-RAM memory (need: %zu, avail: %u)", + total_needed, device->eax_x_ram_free_size); + return ALC_FALSE; + } // Update the mode. // - for (auto i = 0; i < n; ++i) + for(auto i = 0;i < n;++i) { const auto buffer = buffers[i]; - - if (buffer == AL_NONE) - { + if(buffer == AL_NONE) continue; - } const auto al_buffer = LookupBuffer(device, buffer); assert(al_buffer); - assert(!al_buffer->eax_x_ram_is_dirty); + if(value != AL_STORAGE_ACCESSIBLE) + eax_x_ram_apply(*device, *al_buffer); + else + eax_x_ram_clear(*device, *al_buffer); al_buffer->eax_x_ram_mode = value; } @@ -1880,36 +1823,27 @@ START_API_FUNC } END_API_FUNC -ALenum AL_APIENTRY EAXGetBufferMode( - ALuint buffer, - ALint* pReserved) +ALenum AL_APIENTRY EAXGetBufferMode(ALuint buffer, ALint* pReserved) START_API_FUNC { #define EAX_PREFIX "[EAXGetBufferMode] " - if (buffer == AL_NONE) - { - ERR(EAX_PREFIX "%s\n", "Null AL buffer."); - return AL_NONE; - } - - if (pReserved) - { - ERR(EAX_PREFIX "%s\n", "Non-null reserved parameter."); - return AL_NONE; - } - const auto context = ContextRef{GetContextRef()}; - - if (!context) + if(!context) { ERR(EAX_PREFIX "%s\n", "No current context."); return AL_NONE; } - if (!eax_g_is_enabled) + if(!eax_g_is_enabled) + { + context->setError(AL_INVALID_OPERATION, EAX_PREFIX "%s", "EAX not enabled."); + return AL_NONE; + } + + if(pReserved) { - ERR(EAX_PREFIX "%s\n", "EAX not enabled."); + context->setError(AL_INVALID_VALUE, EAX_PREFIX "%s", "Non-null reserved parameter"); return AL_NONE; } @@ -1917,10 +1851,9 @@ START_API_FUNC std::lock_guard<std::mutex> device_lock{device->BufferLock}; const auto al_buffer = LookupBuffer(device, buffer); - - if (!al_buffer) + if(!al_buffer) { - ERR(EAX_PREFIX "Invalid buffer ID %u.\n", buffer); + context->setError(AL_INVALID_NAME, EAX_PREFIX "Invalid buffer ID %u", buffer); return AL_NONE; } diff --git a/al/buffer.h b/al/buffer.h index 8c323bea..b3a0f0d8 100644 --- a/al/buffer.h +++ b/al/buffer.h @@ -75,7 +75,6 @@ struct ALbuffer : public BufferStorage { #ifdef ALSOFT_EAX ALenum eax_x_ram_mode{AL_STORAGE_AUTOMATIC}; bool eax_x_ram_is_hardware{}; - bool eax_x_ram_is_dirty{}; #endif // ALSOFT_EAX }; diff --git a/al/state.cpp b/al/state.cpp index 07fd1314..26fbd5b3 100644 --- a/al/state.cpp +++ b/al/state.cpp @@ -456,7 +456,7 @@ START_API_FUNC auto device = context->mALDevice.get(); std::lock_guard<std::mutex> device_lock{device->BufferLock}; - value = device->eax_x_ram_free_size; + value = static_cast<ALint>(device->eax_x_ram_free_size); } else { diff --git a/alc/device.h b/alc/device.h index daade87a..a3522bbe 100644 --- a/alc/device.h +++ b/alc/device.h @@ -111,7 +111,7 @@ struct ALCdevice : public al::intrusive_ref<ALCdevice>, DeviceBase { al::vector<FilterSubList> FilterList; #ifdef ALSOFT_EAX - ALsizei eax_x_ram_free_size{eax_x_ram_max_size}; + ALuint eax_x_ram_free_size{eax_x_ram_max_size}; #endif // ALSOFT_EAX |