Ticket #931: snd_remove_source_suballoc+disable_buf_free.patch
File snd_remove_source_suballoc+disable_buf_free.patch, 8.6 KB (added by , 13 years ago) |
---|
-
snd_mgr.cpp
349 349 // verify that all buffers have been freed at exit. 350 350 //----------------------------------------------------------------------------- 351 351 352 static int al_bufs_outstanding;352 static intptr_t al_buf_inUse; 353 353 354 354 355 355 /** … … 370 370 AL_CHECK; 371 371 372 372 ENSURE(alIsBuffer(al_buf)); 373 al_bufs_outstanding++;373 cpu_AtomicAdd(&al_buf_inUse, +1); 374 374 375 375 return al_buf; 376 376 } … … 394 394 alDeleteBuffers(1, &al_buf); 395 395 AL_CHECK; 396 396 397 al_bufs_outstanding--;397 cpu_AtomicAdd(&al_buf_inUse, -1); 398 398 } 399 399 400 400 … … 404 404 */ 405 405 static void al_buf_shutdown() 406 406 { 407 ENSURE(al_buf s_outstanding== 0);407 ENSURE(al_buf_inUse == 0); 408 408 } 409 409 410 410 411 411 //----------------------------------------------------------------------------- 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 416 413 //----------------------------------------------------------------------------- 417 414 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). 421 422 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. 424 static size_t al_src_maxNumToUse = 64; 424 425 425 static size_t al_src_numPreallocated;426 static volatile intptr_t al_src_inUse; 426 427 427 enum AllocationState428 {429 kAvailable = 0, // (must match zero-initialization of al_srcs_allocationStates)430 kInUse431 };432 428 433 // note: we want to catch double-free bugs and ensure all sources434 // are released at exit, but OpenAL doesn't specify an always-invalid435 // 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 */443 429 static void al_src_init() 444 430 { 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);465 431 } 466 432 467 433 468 434 /** 469 * release all sources on free list.470 * all sources must already have been released via al_src_free.471 435 * called from al_shutdown. 436 * all sources must already have been released via al_src_free. 472 437 */ 473 438 static void al_src_shutdown() 474 439 { 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); 483 443 } 484 444 485 445 446 TIMER_ADD_CLIENT(tc_OpenAL_source); 447 486 448 /** 487 449 * try to allocate a source. 488 450 * 489 * @return whether a source was allocated (see al_srcs_allocationStates).451 * @return whether a source was allocated. 490 452 * @param al_src receives the new source name iff true is returned. 491 453 */ 492 454 static bool al_src_alloc(ALuint& al_src) 493 455 { 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 } 456 TIMER_ACCRUE(tc_OpenAL_source); 502 457 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; 504 466 } 505 467 506 468 … … 511 473 */ 512 474 static void al_src_free(ALuint al_src) 513 475 { 476 TIMER_ACCRUE(tc_OpenAL_source); 514 477 ENSURE(alIsSource(al_src)); 515 478 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; 524 484 } 525 485 526 486 … … 528 488 * set maximum number of voices to play simultaneously, 529 489 * to reduce mixing cost on low-end systems. 530 490 * this limit may be ignored if e.g. there's a stricter 531 * implementation- 491 * implementation-defined ceiling anyway. 532 492 * 533 493 * @param limit max. number of sources 534 494 * @return Status 535 495 */ 536 496 Status snd_set_max_voices(size_t limit) 537 497 { 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; 553 500 } 554 501 555 502 … … 1536 1483 * @param VSrc* 1537 1484 * @return int number of entries that were removed. 1538 1485 */ 1539 static int vsrc_deque_finished_bufs(VSrc* vs)1486 static ALint vsrc_dequeue_finished_bufs(VSrc* vs) 1540 1487 { 1541 1488 ENSURE(vs->HasSource()); // (otherwise there's no sense in calling this function) 1542 1489 1543 1490 AL_CHECK; 1544 int num_processed;1491 ALint num_processed; 1545 1492 alGetSourcei(vs->al_src, AL_BUFFERS_PROCESSED, &num_processed); 1546 1493 AL_CHECK; 1547 1494 1548 for( int i = 0; i < num_processed; i++)1495 for(ALint i = 0; i < num_processed; i++) 1549 1496 { 1550 1497 ALuint al_buf; 1551 1498 alSourceUnqueueBuffers(vs->al_src, 1, &al_buf); … … 1591 1538 alGetSourcei(vs->al_src, AL_BUFFERS_QUEUED, &num_queued); 1592 1539 AL_CHECK; 1593 1540 1594 int num_processed = vsrc_deque_finished_bufs(vs);1541 ALint num_processed = vsrc_dequeue_finished_bufs(vs); 1595 1542 UNUSED2(num_processed); 1596 1543 1597 1544 if(vs->flags & VS_EOF) … … 1673 1620 if(!vs->HasSource()) 1674 1621 return ERR::FAIL; // NOWARN 1675 1622 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 1676 1629 // clear the source's buffer queue (necessary because buffers cannot 1677 1630 // be deleted at shutdown while still attached to a source). 1678 1631 // note: OpenAL 1.1 says all buffers become "processed" when the 1679 // source is stopped (so vsrc_deque _finished_bufs ought to have the1680 // desired effect), but that isn't the case on some Linux1632 // 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 1681 1634 // implementations (OpenALsoft and PulseAudio with on-board NVidia). 1682 1635 // wiping out the entire queue by attaching the null buffer is safer, 1683 1636 // but still doesn't cause versions of OpenALsoft older than 2009-08-11 … … 1693 1646 1694 1647 alSourceStop(vs->al_src); 1695 1648 1696 vsrc_deque_finished_bufs(vs); 1649 if(!avoidFreeingBufHack) 1650 (void)vsrc_dequeue_finished_bufs(vs); 1697 1651 alSourcei(vs->al_src, AL_BUFFER, AL_NONE); 1698 1652 1653 // resets the sample position and changes state to AL_STOPPED 1654 // (which clears the buffer queue) and then AL_INITIAL. 1699 1655 alSourceRewind(vs->al_src); 1700 1656 1701 1657 al_src_free(vs->al_src);