VirtualBox

Changeset 87985 in vbox


Ignore:
Timestamp:
Mar 5, 2021 8:59:57 PM (4 years ago)
Author:
vboxsync
Message:

DevHDA: Increase the internal buffer between the DMA engine and the AIO thread so that underruns should be very unlikely. bugref:9890

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Audio/HDAStream.cpp

    r87983 r87985  
    672672
    673673        /*
    674          * The default size of our internal ring buffer depends on the transfer timing
    675          * we have to reach in order to make the guest driver happy *and* on the I/O timing.
     674         * The default internal ring buffer size must be:
    676675         *
    677          * We always use triple the minimum timing of both timings for safety (triple buffering),
    678          * otherwise we risk running into buffer overflows.
     676         *      - Large enough for at least three periodic DMA transfers.
    679677         *
    680          * Note: Use pCfg->Props as PCM properties here, as we only want to store the samples we actually need,
    681          *       in other words, skipping the interleaved channels we don't support / need to save space.
     678         *        It is critically important that we don't experience underruns
     679         *        in the DMA OUT code, because it will cause the buffer processing
     680         *        to get skewed and possibly overlap with what the guest is updating.
     681         *        At the time of writing (2021-03-05) there is no code for getting
     682         *        back into sync there.
     683         *
     684         *      - Large enough for at least three I/O scheduling hints.
     685         *
     686         *        We want to lag behind a DMA period or two, but there must be
     687         *        sufficent space for the AIO thread to get schedule and shuffle
     688         *        data thru the mixer and onto the host audio hardware.
     689         *
     690         *      - Both above with plenty to spare.
     691         *
     692         * So, just take the longest of the two periods and multipling it by 6.
     693         * We aren't not talking about very large base buffers heres, so size isn't
     694         * an issue.
     695         *
     696         * Note: Use pCfg->Props as PCM properties here, as we only want to store the
     697         *       samples we actually need, in other words, skipping the interleaved
     698         *       channels we don't support / need to save space.
    682699         */
    683 
    684         const unsigned uTransferHzMin   = RT_MIN(uTransferHz, pStreamShared->State.uTimerIoHz);
    685         const uint32_t cbCircBufDefault = DrvAudioHlpMilliToBytes((RT_MS_1SEC / uTransferHzMin) * 3, &pCfg->Props);
    686 
    687         LogRel2(("HDA: Stream #%RU8 default ring buffer size is %RU64ms (%RU32 bytes)\n",
    688                  uSD, DrvAudioHlpBytesToMilli(cbCircBufDefault, &pCfg->Props), cbCircBufDefault));
    689 
    690         uint32_t cbCircBuf;
    691         uint32_t cbCircBufGlobal = DrvAudioHlpMilliToBytes(  hdaGetDirFromSD(uSD) == PDMAUDIODIR_IN
    692                                                            ? pThis->cbCircBufInMs : pThis->cbCircBufOutMs, &pCfg->Props);
    693         if (cbCircBufGlobal) /* Anything set via CFGM? */
    694         {
    695             LogRel2(("HDA: Stream #%RU8 is using a custom ring buffer size of %RU64ms (%RU32 bytes)\n",
    696                      uSD, DrvAudioHlpBytesToMilli(cbCircBufGlobal, &pCfg->Props), cbCircBufGlobal));
    697 
    698             cbCircBuf = cbCircBufGlobal;
    699         }
    700         else
    701             cbCircBuf = cbCircBufDefault;
    702 
     700        uint32_t cbCircBuf = DrvAudioHlpMilliToBytes(RT_MS_1SEC * 6 / RT_MIN(uTransferHz, pStreamShared->State.uTimerIoHz),
     701                                                     &pCfg->Props);
     702        LogRel2(("HDA: Stream #%RU8 default ring buffer size is %RU32 bytes / %RU64 ms\n",
     703                 uSD, cbCircBuf, DrvAudioHlpBytesToMilli(cbCircBuf, &pCfg->Props)));
     704
     705        uint32_t msCircBufCfg = hdaGetDirFromSD(uSD) == PDMAUDIODIR_IN ? pThis->cbCircBufInMs : pThis->cbCircBufOutMs;
     706        if (msCircBufCfg) /* Anything set via CFGM? */
     707        {
     708            cbCircBuf = DrvAudioHlpMilliToBytes(msCircBufCfg, &pCfg->Props);
     709            LogRel2(("HDA: Stream #%RU8 is using a custom ring buffer size of %RU32 bytes / %RU64 ms\n",
     710                     uSD, cbCircBuf, DrvAudioHlpBytesToMilli(cbCircBuf, &pCfg->Props)));
     711        }
     712
     713        /* Serious paranoia: */
    703714        ASSERT_GUEST_LOGREL_MSG_STMT(cbCircBuf % (pCfg->Props.cbSample * pCfg->Props.cChannels) == 0,
    704                                      ("Ring buffer size (%RU32) for stream #%RU8 not aligned to mapping's frame size (%RU8)\n",
     715                                     ("Ring buffer size (%RU32) for stream #%RU8 not aligned to the (host) frame size (%RU8)\n",
    705716                                      cbCircBuf, uSD, pCfg->Props.cbSample * pCfg->Props.cChannels),
    706717                                     rc = VERR_INVALID_PARAMETER);
     
    11741185                               PHDASTREAMR3 pStreamR3, uint32_t cbToProcessMax)
    11751186{
    1176     LogFlowFuncEnter();
    1177 
    11781187    uint8_t const uSD = pStreamShared->u8SD;
     1188    LogFlowFunc(("ENTER - #%u cbToProcessMax=%#x\n", uSD, cbToProcessMax));
     1189
    11791190    hdaStreamLock(pStreamShared);
    11801191
     
    17691780    if (hdaGetDirFromSD(pStreamShared->u8SD) == PDMAUDIODIR_OUT) /* Output (SDO). */
    17701781    {
    1771         bool fDoRead = false; /* Whether to read from the HDA stream or not. */
    1772 
     1782        bool fDoRead = fInTimer; /* Whether to read from the HDA stream or not. */
     1783
     1784        /*
     1785         * Do DMA work.
     1786         */
    17731787# ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
    17741788        if (fInTimer)
     
    17761790        {
    17771791            uint32_t cbStreamFree = hdaR3StreamGetFree(pStreamR3);
    1778             if (!cbStreamFree)
     1792            if (cbStreamFree)
     1793            { /* likely */ }
     1794            else
    17791795            {
    17801796                LogRel2(("HDA: Warning: Hit stream #%RU8 overflow, dropping audio data\n", pStreamShared->u8SD));
     
    17991815                pStreamShared->State.tsLastReadNs = tsNowNs;
    18001816
    1801             /* Only read from the HDA stream at the given scheduling rate. */
     1817            /*
     1818             * Push data to down thru the mixer to and to the host drivers?
     1819             *
     1820             * This is generally done at the rate given by cMsSchedulingHint,
     1821             * however we must also check available DMA buffer space.  There
     1822             * should be at least two periodic transfer units worth of space
     1823             * available now.
     1824             */
    18021825            Assert(tsNowNs >= pStreamShared->State.tsLastReadNs);
    1803             const uint64_t tsDeltaMs = (tsNowNs - pStreamShared->State.tsLastReadNs) / RT_NS_1MS;
    1804             if (tsDeltaMs >= pStreamShared->State.Cfg.Device.cMsSchedulingHint)
     1826            /** @todo convert cMsSchedulingHint to nano seconds and save a div.  */
     1827            const uint64_t msDelta = (tsNowNs - pStreamShared->State.tsLastReadNs) / RT_NS_1MS;
     1828            cbStreamFree = hdaR3StreamGetFree(pStreamR3);
     1829            if (   cbStreamFree < pStreamShared->State.cbTransferSize * 2
     1830                || msDelta >= pStreamShared->State.Cfg.Device.cMsSchedulingHint)
    18051831                fDoRead = true;
    18061832
    1807             Log3Func(("tsDeltaMs=%RU64, fDoRead=%RTbool\n", tsDeltaMs, fDoRead));
    1808         }
    1809 
    1810         if (fDoRead)
    1811         {
     1833            Log3Func(("msDelta=%RU64 (vs %u) cbStreamFree=%#x (vs %#x) => fDoRead=%RTbool\n", msDelta,
     1834                      pStreamShared->State.Cfg.Device.cMsSchedulingHint, cbStreamFree,
     1835                      pStreamShared->State.cbTransferSize * 2, fDoRead));
     1836
     1837            if (fDoRead)
     1838            {
    18121839# ifdef VBOX_WITH_AUDIO_HDA_ASYNC_IO
    1813             /* Notify the async I/O worker thread that there's work to do. */
    1814             rc2 = hdaR3StreamAsyncIONotify(pStreamR3);
    1815             AssertRC(rc2);
     1840                /* Notify the async I/O worker thread that there's work to do. */
     1841                Log5Func(("Notifying AIO thread\n"));
     1842                rc2 = hdaR3StreamAsyncIONotify(pStreamR3);
     1843                AssertRC(rc2);
    18161844# endif
    1817             /* Update last read timestamp so that we know when to run next. */
    1818             pStreamShared->State.tsLastReadNs = tsNowNs;
     1845                /* Update last read timestamp so that we know when to run next. */
     1846                pStreamShared->State.tsLastReadNs = tsNowNs;
     1847            }
    18191848        }
    18201849
     
    19081937            if (tsNowNs - pStreamShared->State.tsLastReadNs >= pStreamShared->State.Cfg.Device.cMsSchedulingHint * RT_NS_1MS)
    19091938            {
     1939                Log5Func(("Notifying AIO thread\n"));
    19101940                rc2 = hdaR3StreamAsyncIONotify(pStreamR3);
    19111941                AssertRC(rc2);
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette