Ticket #931: snd_remove_source_suballoc+disable_buf_free.patch

File snd_remove_source_suballoc+disable_buf_free.patch, 8.6 KB (added by Jan Wassenberg, 13 years ago)
  • snd_mgr.cpp

     
    349349// verify that all buffers have been freed at exit.
    350350//-----------------------------------------------------------------------------
    351351
    352 static int al_bufs_outstanding;
     352static intptr_t al_buf_inUse;
    353353
    354354
    355355/**
     
    370370    AL_CHECK;
    371371
    372372    ENSURE(alIsBuffer(al_buf));
    373     al_bufs_outstanding++;
     373    cpu_AtomicAdd(&al_buf_inUse, +1);
    374374
    375375    return al_buf;
    376376}
     
    394394    alDeleteBuffers(1, &al_buf);
    395395    AL_CHECK;
    396396
    397     al_bufs_outstanding--;
     397    cpu_AtomicAdd(&al_buf_inUse, -1);
    398398}
    399399
    400400
     
    404404 */
    405405static void al_buf_shutdown()
    406406{
    407     ENSURE(al_bufs_outstanding == 0);
     407    ENSURE(al_buf_inUse == 0);
    408408}
    409409
    410410
    411411//-----------------------------------------------------------------------------
    412 // AL source suballocator: allocate all available sources up-front and
    413 // pass them out as needed (alGenSources is quite slow, taking 3..5 ms per
    414 // source returned). also responsible for enforcing user-specified limit
    415 // on total number of sources (to reduce mixing cost on low-end systems).
     412// AL source suballocator
    416413//-----------------------------------------------------------------------------
    417414
    418 // regardless of HW capabilities, we won't use more than this ("enough").
    419 // necessary in case OpenAL doesn't limit #sources (e.g. if SW mixing).
    420 static const size_t AL_SRC_MAX = 64;
     415// a suballocator used to be necessary because alGenSources took 3-5 ms per
     416// source returned on NVidia's native OpenAL. however, OS X CoreAudio can't
     417// deal with immediate source reuse (race condition?), and the easiest way
     418// to get around this is to generate/delete sources on demand. fortunately,
     419// OpenALsoft only takes a few microseconds to do this.
     420// this layer is also responsible for enforcing the user-specified limit on
     421// the total number of sources (to reduce mixing cost on low-end systems).
    421422
    422 // (allow changing at runtime)
    423 static size_t al_src_maxNumToUse = AL_SRC_MAX;
     423// 64 is probably "enough", but the value can be changed at runtime.
     424static size_t al_src_maxNumToUse = 64;
    424425
    425 static size_t al_src_numPreallocated;
     426static volatile intptr_t al_src_inUse;
    426427
    427 enum AllocationState
    428 {
    429     kAvailable = 0, // (must match zero-initialization of al_srcs_allocationStates)
    430     kInUse
    431 };
    432428
    433 // note: we want to catch double-free bugs and ensure all sources
    434 // are released at exit, but OpenAL doesn't specify an always-invalid
    435 // source name, so we need a separate array of AllocationState.
    436 static ALuint al_srcs[AL_SRC_MAX];
    437 static intptr_t al_srcs_allocationStates[AL_SRC_MAX];
    438 
    439 /**
    440  * grab as many sources as possible up to the limit.
    441  * called from al_init.
    442  */
    443429static void al_src_init()
    444430{
    445     // grab as many sources as possible and count how many we get.
    446     for(size_t i = 0; i < al_src_maxNumToUse; i++)
    447     {
    448         ALuint al_src;
    449         alGenSources(1, &al_src);
    450         // we've reached the limit, no more are available.
    451         if(alGetError() != AL_NO_ERROR)
    452             break;
    453         ENSURE(alIsSource(al_src));
    454         al_srcs[i] = al_src;
    455         al_src_numPreallocated++;
    456     }
    457 
    458     // limit user's cap to what we actually got.
    459     // (in case snd_set_max_src was called before this)
    460     if(al_src_maxNumToUse > al_src_numPreallocated)
    461         al_src_maxNumToUse = al_src_numPreallocated;
    462 
    463     // make sure we got the minimum guaranteed by OpenAL.
    464     ENSURE(al_src_numPreallocated >= 16);
    465431}
    466432
    467433
    468434/**
    469  * release all sources on free list.
    470  * all sources must already have been released via al_src_free.
    471435 * called from al_shutdown.
     436 * all sources must already have been released via al_src_free.
    472437 */
    473438static void al_src_shutdown()
    474439{
    475     for(size_t i = 0; i < al_src_numPreallocated; i++)
    476         ENSURE(al_srcs_allocationStates[i] == kAvailable);
    477 
    478     AL_CHECK;
    479     alDeleteSources((ALsizei)al_src_numPreallocated, al_srcs);
    480     AL_CHECK;
    481 
    482     al_src_numPreallocated = 0;
     440    // (we no longer bother keeping a list of extant sources,
     441    // because they will be freed at exit anyway.)
     442    ENSURE(al_src_inUse == 0);
    483443}
    484444
    485445
     446TIMER_ADD_CLIENT(tc_OpenAL_source);
     447
    486448/**
    487449 * try to allocate a source.
    488450 *
    489  * @return whether a source was allocated (see al_srcs_allocationStates).
     451 * @return whether a source was allocated.
    490452 * @param al_src receives the new source name iff true is returned.
    491453 */
    492454static bool al_src_alloc(ALuint& al_src)
    493455{
    494     for(size_t i = 0; i < al_src_numPreallocated; i++)
    495     {
    496         if(cpu_CAS(&al_srcs_allocationStates[i], kAvailable, kInUse))
    497         {
    498             al_src = al_srcs[i];
    499             return true;
    500         }
    501     }
     456TIMER_ACCRUE(tc_OpenAL_source);
    502457
    503     return false;   // no more to give
     458    if((size_t)al_src_inUse >= al_src_maxNumToUse)
     459        return false;   // allocated too many already
     460    cpu_AtomicAdd(&al_src_inUse, +1);
     461
     462    AL_CHECK;
     463    alGenSources(1, &al_src);
     464    AL_CHECK;
     465    return true;
    504466}
    505467
    506468
     
    511473 */
    512474static void al_src_free(ALuint al_src)
    513475{
     476TIMER_ACCRUE(tc_OpenAL_source);
    514477    ENSURE(alIsSource(al_src));
    515478
    516     const ALuint* pos = std::find(al_srcs, al_srcs+al_src_numPreallocated, al_src);
    517     if(pos != al_srcs+al_src_numPreallocated)   // found it
    518     {
    519         const size_t i = pos - al_srcs;
    520         ENSURE(cpu_CAS(&al_srcs_allocationStates[i], kInUse, kAvailable));
    521     }
    522     else
    523         DEBUG_WARN_ERR(ERR::LOGIC); // al_src wasn't in al_srcs
     479    cpu_AtomicAdd(&al_src_inUse, -1);
     480
     481    AL_CHECK;
     482    alDeleteSources(1, &al_src);
     483    AL_CHECK;
    524484}
    525485
    526486
     
    528488 * set maximum number of voices to play simultaneously,
    529489 * to reduce mixing cost on low-end systems.
    530490 * this limit may be ignored if e.g. there's a stricter
    531  * implementation- defined ceiling anyway.
     491 * implementation-defined ceiling anyway.
    532492 *
    533493 * @param limit max. number of sources
    534494 * @return Status
    535495 */
    536496Status snd_set_max_voices(size_t limit)
    537497{
    538     // valid if cap is legit (less than what we allocated in al_src_init),
    539     // or if al_src_init hasn't been called yet. note: we accept anything
    540     // in the second case, as al_src_init will sanity-check al_src_cap.
    541     if(!al_src_numPreallocated || limit < al_src_numPreallocated)
    542     {
    543         al_src_maxNumToUse = limit;
    544         return INFO::OK;
    545     }
    546     // user is requesting a cap higher than what we actually allocated.
    547     // that's fine (not an error), but we won't set the cap, since it
    548     // determines how many sources may be returned.
    549     // there's no return value to indicate this because the cap is
    550     // precisely that - an upper limit only, we don't care if it can't be met.
    551     else
    552         return INFO::OK;
     498    al_src_maxNumToUse = limit;
     499    return INFO::OK;
    553500}
    554501
    555502
     
    15361483 * @param VSrc*
    15371484 * @return int number of entries that were removed.
    15381485 */
    1539 static int vsrc_deque_finished_bufs(VSrc* vs)
     1486static ALint vsrc_dequeue_finished_bufs(VSrc* vs)
    15401487{
    15411488    ENSURE(vs->HasSource());    // (otherwise there's no sense in calling this function)
    15421489
    15431490    AL_CHECK;
    1544     int num_processed;
     1491    ALint num_processed;
    15451492    alGetSourcei(vs->al_src, AL_BUFFERS_PROCESSED, &num_processed);
    15461493    AL_CHECK;
    15471494
    1548     for(int i = 0; i < num_processed; i++)
     1495    for(ALint i = 0; i < num_processed; i++)
    15491496    {
    15501497        ALuint al_buf;
    15511498        alSourceUnqueueBuffers(vs->al_src, 1, &al_buf);
     
    15911538        alGetSourcei(vs->al_src, AL_BUFFERS_QUEUED, &num_queued);
    15921539        AL_CHECK;
    15931540
    1594         int num_processed = vsrc_deque_finished_bufs(vs);
     1541        ALint num_processed = vsrc_dequeue_finished_bufs(vs);
    15951542        UNUSED2(num_processed);
    15961543
    15971544        if(vs->flags & VS_EOF)
     
    16731620    if(!vs->HasSource())
    16741621        return ERR::FAIL;   // NOWARN
    16751622
     1623    // apparently even the following contortions aren't enough to
     1624    // prevent buffer(s) from remaining attached to the source
     1625    // on OS X when looping is enabled, so we will just have
     1626    // to let the buffer leak (or "GC" it later).
     1627    const bool avoidFreeingBufHack = OS_MACOSX && vs->loop;
     1628
    16761629    // clear the source's buffer queue (necessary because buffers cannot
    16771630    // be deleted at shutdown while still attached to a source).
    16781631    // note: OpenAL 1.1 says all buffers become "processed" when the
    1679     // source is stopped (so vsrc_deque_finished_bufs ought to have the
    1680     // desired effect), but that isn't the case on some Linux
     1632    // source is stopped (so vsrc_dequeue_finished_bufs ought to have the
     1633    // desired effect), but that isn't the case on some OS X / Linux
    16811634    // implementations (OpenALsoft and PulseAudio with on-board NVidia).
    16821635    // wiping out the entire queue by attaching the null buffer is safer,
    16831636    // but still doesn't cause versions of OpenALsoft older than 2009-08-11
     
    16931646
    16941647    alSourceStop(vs->al_src);
    16951648
    1696     vsrc_deque_finished_bufs(vs);
     1649    if(!avoidFreeingBufHack)
     1650        (void)vsrc_dequeue_finished_bufs(vs);
    16971651    alSourcei(vs->al_src, AL_BUFFER, AL_NONE);
    16981652
     1653    // resets the sample position and changes state to AL_STOPPED
     1654    // (which clears the buffer queue) and then AL_INITIAL.
    16991655    alSourceRewind(vs->al_src);
    17001656
    17011657    al_src_free(vs->al_src);