mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
sceAtrac: Charge setting data and decoding what the ME takes
Setting data decodes and throws away the frames before the first sample, so on hardware it costs a decoder setup plus that decode, with the caller waiting: ~900us for mono Atrac3 and ~3.5ms for stereo Atrac3+, whatever the buffer size (pspautotests threads/scheduling/callcosts). It was charged 100us. Decoding a frame now costs what the same frame costs through sceAudiocodecDecode, through the shared ME queue, instead of a flat 2300us. That's about the same for stereo Atrac3+ and less for Atrac3 (685us mono, ~1100us stereo). The first Atrac3+ frames after setup still come out ~500us short. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
c1e70c9686
commit
4ba981b5b3
5 files changed
+54
-18
No files matched your search
@@ -222,6 +222,8 @@ public:
|
||||
virtual int ResetPlayPosition(int sample, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf, bool *delay) = 0;
|
||||
virtual int GetBufferInfoForResetting(AtracResetBufferInfo *bufferInfo, int sample, bool *delay) = 0; // NOTE: Not const! This can cause SkipFrames!
|
||||
virtual int SetData(const Track &track, u32 buffer, u32 readSize, u32 bufferSize, u32 fileSize, int outputChannels, bool isAA3) = 0;
|
||||
// How many frames the last SetData decoded and threw away, to get to the first sample.
|
||||
int SkippedFramesOnSetData() const { return setDataSkippedFrames_; }
|
||||
|
||||
virtual int GetSecondBufferInfo(u32 *fileOffset, u32 *desiredSize) const = 0;
|
||||
virtual int SetSecondBuffer(u32 secondBuffer, u32 secondBufferSize) = 0;
|
||||
@@ -242,6 +244,7 @@ public:
|
||||
|
||||
protected:
|
||||
u16 outputChannels_ = 2;
|
||||
int setDataSkippedFrames_ = 0;
|
||||
|
||||
// TODO: Save the internal state of this, now technically possible.
|
||||
AudioDecoder *decoder_ = nullptr;
|
||||
|
||||
@@ -1019,8 +1019,9 @@ int Atrac2::SetData(const Track &track, u32 bufferAddr, u32 readSize, u32 buffer
|
||||
info.fileDataEnd, info.decodePos, info.numSkipFrames, info.numChan
|
||||
);
|
||||
|
||||
int skipCount = 0; // TODO: use for delay
|
||||
int skipCount = 0;
|
||||
retval = SkipFrames(&skipCount);
|
||||
setDataSkippedFrames_ = skipCount;
|
||||
|
||||
// Seen in Mui Mui house. Things go very wrong after this..
|
||||
if (retval == SCE_ERROR_ATRAC_API_FAIL) {
|
||||
|
||||
+33
-15
@@ -32,8 +32,11 @@
|
||||
|
||||
#include "Core/HLE/sceKernel.h"
|
||||
#include "Core/HLE/sceUtility.h"
|
||||
#include "Core/HLE/sceVideocodec.h"
|
||||
#include "Core/HLE/sceKernelMemory.h"
|
||||
#include "Core/HLE/scePower.h"
|
||||
#include "Core/HLE/sceAtrac.h"
|
||||
#include "Core/HLE/sceAudiocodec.h"
|
||||
#include "Core/HLE/AtracCtx.h"
|
||||
#include "Core/HLE/AtracCtx2.h"
|
||||
#include "Core/System.h"
|
||||
@@ -81,7 +84,21 @@
|
||||
|
||||
// TODO: We should add checks that the utility module is loaded.
|
||||
|
||||
static const int atracDecodeDelay = 2300;
|
||||
// The Media Engine does the decoding while the caller waits. Setting data decodes the frames before
|
||||
// the first sample (thrown away), which on hardware makes it cost a decoder setup plus a frame
|
||||
// decode: ~900us for mono Atrac3, ~3.5ms for stereo Atrac3+ (pspautotests threads/scheduling/callcosts).
|
||||
static int AtracFrameUs(const AtracBase *atrac) {
|
||||
return AudioCodecDecodeUs(atrac->CodecType(), atrac->Channels(), atrac->BytesPerFrame());
|
||||
}
|
||||
|
||||
static int AtracSetDataDelay(const AtracBase *atrac) {
|
||||
const int us = AudioCodecInitUs(atrac->CodecType(), atrac->Channels() == 1) + atrac->SkippedFramesOnSetData() * AtracFrameUs(atrac);
|
||||
return MEScheduleJob(PowerScaleFromDefaultClock(us));
|
||||
}
|
||||
|
||||
static int AtracDecodeDelay(const AtracBase *atrac) {
|
||||
return MEScheduleJob(PowerScaleFromDefaultClock(AtracFrameUs(atrac)));
|
||||
}
|
||||
|
||||
static bool atracInited = true;
|
||||
static AtracBase *atracContexts[PSP_MAX_ATRAC_IDS];
|
||||
@@ -94,7 +111,7 @@ static int g_atracBSS = 0;
|
||||
static bool g_muteFlag[PSP_MAX_ATRAC_IDS]{}; // Not saved, just for debugging.
|
||||
|
||||
// On a PSP, the Media Engine does the decoding, and the samples land in the output buffer when
|
||||
// sceAtracDecodeData returns, about atracDecodeDelay later. A game can still be playing out of that
|
||||
// sceAtracDecodeData returns, a frame decode later. A game can still be playing out of that
|
||||
// memory in the meantime: Fired Up decodes into a buffer that overlaps the first 16 samples of the
|
||||
// one it has just handed to sceAudio, and relies on the mixer having read them first. So the
|
||||
// samples are written just before the thread wakes rather than when the call is made.
|
||||
@@ -423,6 +440,7 @@ static u32 sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32
|
||||
}
|
||||
|
||||
if (ret == 0 || ret == SCE_ERROR_ATRAC_API_FAIL) {
|
||||
const int delay = AtracDecodeDelay(atrac);
|
||||
const u32 written = std::min((u32)(atrac->GetOutputChannels() * 2 * numSamplesWritten), (u32)previous.size());
|
||||
if (outPtr && written != 0) {
|
||||
AtracPendingOutput pending{ ++g_pendingOutputId, outAddr };
|
||||
@@ -430,10 +448,10 @@ static u32 sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32
|
||||
memcpy(outPtr, previous.data(), previous.size());
|
||||
g_pendingOutput.push_back(std::move(pending));
|
||||
// Just ahead of the thread waking up.
|
||||
CoreTiming::ScheduleEvent(usToCycles(atracDecodeDelay) - 1, g_atracOutputEvent, g_pendingOutputId);
|
||||
CoreTiming::ScheduleEvent(usToCycles(delay) - 1, g_atracOutputEvent, g_pendingOutputId);
|
||||
}
|
||||
// Decoded or at least attempted to decode data, delay thread
|
||||
return hleDelayResult(hleNoLog(ret), "atrac decode data", atracDecodeDelay);
|
||||
return hleDelayResult(hleNoLog(ret), "atrac decode data", delay);
|
||||
}
|
||||
|
||||
return hleNoLog(ret);
|
||||
@@ -753,7 +771,7 @@ static u32 sceAtracSetHalfwayBuffer(int atracID, u32 buffer, u32 readSize, u32 b
|
||||
}
|
||||
|
||||
// not sure the real delay time
|
||||
return hleDelayResult(hleLogDebug(Log::Atrac, ret), "atrac set data", 100);
|
||||
return hleDelayResult(hleLogDebug(Log::Atrac, ret), "atrac set data", AtracSetDataDelay(atrac));
|
||||
}
|
||||
|
||||
static u32 sceAtracSetSecondBuffer(int atracID, u32 secondBuffer, u32 secondBufferSize) {
|
||||
@@ -788,7 +806,7 @@ static u32 sceAtracSetData(int atracID, u32 buffer, u32 bufferSize) {
|
||||
return hleLogError(Log::Atrac, ret);
|
||||
}
|
||||
|
||||
return hleDelayResult(hleLogDebug(Log::Atrac, ret), "atrac set data", 100);
|
||||
return hleDelayResult(hleLogDebug(Log::Atrac, ret), "atrac set data", AtracSetDataDelay(atrac));
|
||||
}
|
||||
|
||||
static int sceAtracSetDataAndGetID(u32 buffer, int bufferSize) {
|
||||
@@ -819,7 +837,7 @@ static int sceAtracSetDataAndGetID(u32 buffer, int bufferSize) {
|
||||
return hleLogError(Log::Atrac, ret);
|
||||
}
|
||||
|
||||
return hleDelayResult(hleLogDebug(Log::Atrac, atracID), "atrac set data", 100);
|
||||
return hleDelayResult(hleLogDebug(Log::Atrac, atracID), "atrac set data", AtracSetDataDelay(atracContexts[atracID]));
|
||||
}
|
||||
|
||||
static int sceAtracSetHalfwayBufferAndGetID(u32 buffer, u32 readSize, u32 bufferSize) {
|
||||
@@ -845,7 +863,7 @@ static int sceAtracSetHalfwayBufferAndGetID(u32 buffer, u32 readSize, u32 buffer
|
||||
return hleLogError(Log::Atrac, ret);
|
||||
}
|
||||
|
||||
return hleDelayResult(hleLogDebug(Log::Atrac, atracID), "atrac set data", 100);
|
||||
return hleDelayResult(hleLogDebug(Log::Atrac, atracID), "atrac set data", AtracSetDataDelay(atracContexts[atracID]));
|
||||
}
|
||||
|
||||
static u32 sceAtracStartEntry() {
|
||||
@@ -959,7 +977,7 @@ static int sceAtracSetMOutHalfwayBuffer(int atracID, u32 buffer, u32 readSize, u
|
||||
// Must not delay.
|
||||
return hleLogError(Log::Atrac, ret);
|
||||
}
|
||||
return hleDelayResult(hleLogDebugOrError(Log::Atrac, ret), "atrac set data mono", 100);
|
||||
return hleDelayResult(hleLogDebugOrError(Log::Atrac, ret), "atrac set data mono", AtracSetDataDelay(atrac));
|
||||
}
|
||||
|
||||
// Note: This doesn't seem to be part of any available libatrac3plus library.
|
||||
@@ -985,7 +1003,7 @@ static u32 sceAtracSetMOutData(int atracID, u32 buffer, u32 bufferSize) {
|
||||
return hleLogError(Log::Atrac, ret);
|
||||
}
|
||||
// It's OK if this fails, at least with NO_MONO...
|
||||
return hleDelayResult(hleLogDebugOrError(Log::Atrac, ret), "atrac set data mono", 100);
|
||||
return hleDelayResult(hleLogDebugOrError(Log::Atrac, ret), "atrac set data mono", AtracSetDataDelay(atrac));
|
||||
}
|
||||
|
||||
// Note: This doesn't seem to be part of any available libatrac3plus library.
|
||||
@@ -1012,7 +1030,7 @@ static int sceAtracSetMOutDataAndGetID(u32 buffer, u32 bufferSize) {
|
||||
UnregisterAndDeleteAtrac(atracID);
|
||||
return hleLogError(Log::Atrac, ret);
|
||||
}
|
||||
return hleDelayResult(hleLogDebugOrError(Log::Atrac, atracID), "atrac set data", 100);
|
||||
return hleDelayResult(hleLogDebugOrError(Log::Atrac, atracID), "atrac set data", AtracSetDataDelay(atracContexts[atracID]));
|
||||
}
|
||||
|
||||
static int sceAtracSetMOutHalfwayBufferAndGetID(u32 buffer, u32 readSize, u32 bufferSize) {
|
||||
@@ -1041,7 +1059,7 @@ static int sceAtracSetMOutHalfwayBufferAndGetID(u32 buffer, u32 readSize, u32 bu
|
||||
UnregisterAndDeleteAtrac(atracID);
|
||||
return hleLogError(Log::Atrac, ret);
|
||||
}
|
||||
return hleDelayResult(hleLogDebug(Log::Atrac, atracID), "atrac set data", 100);
|
||||
return hleDelayResult(hleLogDebug(Log::Atrac, atracID), "atrac set data", AtracSetDataDelay(atracContexts[atracID]));
|
||||
}
|
||||
|
||||
static int sceAtracSetAA3DataAndGetID(u32 buffer, u32 bufferSize, u32 fileSize, u32 metadataSizeAddr) {
|
||||
@@ -1063,7 +1081,7 @@ static int sceAtracSetAA3DataAndGetID(u32 buffer, u32 bufferSize, u32 fileSize,
|
||||
return hleLogError(Log::Atrac, ret);
|
||||
}
|
||||
|
||||
return hleDelayResult(hleLogDebug(Log::Atrac, atracID), "atrac set aa3 data", 100);
|
||||
return hleDelayResult(hleLogDebug(Log::Atrac, atracID), "atrac set aa3 data", AtracSetDataDelay(atracContexts[atracID]));
|
||||
}
|
||||
|
||||
static int sceAtracSetAA3HalfwayBufferAndGetID(u32 buffer, u32 readSize, u32 bufferSize, u32 fileSize) {
|
||||
@@ -1089,7 +1107,7 @@ static int sceAtracSetAA3HalfwayBufferAndGetID(u32 buffer, u32 readSize, u32 buf
|
||||
return hleLogError(Log::Atrac, ret);
|
||||
}
|
||||
|
||||
return hleDelayResult(hleLogDebug(Log::Atrac, atracID), "atrac set data", 100);
|
||||
return hleDelayResult(hleLogDebug(Log::Atrac, atracID), "atrac set data", AtracSetDataDelay(atracContexts[atracID]));
|
||||
}
|
||||
|
||||
// TODO: Should see if these are stored contiguously in memory somewhere, or if there really are
|
||||
@@ -1155,7 +1173,7 @@ static int sceAtracLowLevelDecode(int atracID, u32 sourceAddr, u32 sourceBytesCo
|
||||
}
|
||||
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, samplesAddr, bytesWritten, "AtracLowLevelDecode");
|
||||
return hleDelayResult(hleLogDebug(Log::Atrac, retval), "low level atrac decode data", atracDecodeDelay);
|
||||
return hleDelayResult(hleLogDebug(Log::Atrac, retval), "low level atrac decode data", AtracDecodeDelay(atrac));
|
||||
}
|
||||
|
||||
// These three are the external interface used by sceSas' AT3 integration.
|
||||
|
||||
@@ -239,15 +239,19 @@ static int MECall(int result, int us) {
|
||||
return hleDelayResult(result, "audiocodec", MEScheduleJob(PowerScaleFromDefaultClock(us)));
|
||||
}
|
||||
|
||||
static int InitUs(int codec, const SceAudiocodecCodec *ctx) {
|
||||
int AudioCodecInitUs(int codec, bool monoAt3Plus) {
|
||||
switch (codec) {
|
||||
case PSP_CODEC_AT3PLUS: return ((ctx->fmt.at3.formatByte1 >> 2) & 7) == 1 ? 524 : 646;
|
||||
case PSP_CODEC_AT3PLUS: return monoAt3Plus ? 524 : 646;
|
||||
case PSP_CODEC_AT3: return 210;
|
||||
case PSP_CODEC_MP3: return 517;
|
||||
default: return 230;
|
||||
}
|
||||
}
|
||||
|
||||
static int InitUs(int codec, const SceAudiocodecCodec *ctx) {
|
||||
return AudioCodecInitUs(codec, codec == PSP_CODEC_AT3PLUS && ((ctx->fmt.at3.formatByte1 >> 2) & 7) == 1);
|
||||
}
|
||||
|
||||
// libmp4.prx puts the sample rate here. On hardware 22050 and 44100 are accepted, 0 and 12345
|
||||
// aren't; that the rest of the standard AAC rates are accepted is an assumption. 0 if not valid.
|
||||
static int AacSampleRateFromContext(const SceAudiocodecCodec *ctx) {
|
||||
@@ -395,6 +399,11 @@ static int EstimateDecodeUs(int codec, int channels, int frameBytes, const SceAu
|
||||
}
|
||||
}
|
||||
|
||||
int AudioCodecDecodeUs(int codec, int channels, int frameBytes) {
|
||||
_dbg_assert_(codec == PSP_CODEC_AT3PLUS || codec == PSP_CODEC_AT3);
|
||||
return EstimateDecodeUs(codec, channels, frameBytes, nullptr);
|
||||
}
|
||||
|
||||
static int sceAudiocodecInit(u32 ctxPtr, int codec) {
|
||||
return __AudioCodecInitCommon(ctxPtr, codec, false);
|
||||
}
|
||||
|
||||
@@ -128,3 +128,8 @@ class AudioDecoder;
|
||||
extern std::map<u32, AudioDecoder *> g_audioDecoderContexts;
|
||||
|
||||
bool IsAtrac3StreamJointStereo(int codecType, int bytesPerFrame, int channels);
|
||||
|
||||
// ME time at the default clock, for sceAtrac too (it drives the same decoder): setting up a decoder,
|
||||
// and decoding one frame. AudioCodecDecodeUs takes Atrac3 and Atrac3+ only.
|
||||
int AudioCodecInitUs(int codec, bool monoAt3Plus);
|
||||
int AudioCodecDecodeUs(int codec, int channels, int frameBytes);
|
||||
Reference in new issue
Block a user