mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-15 10:40:01 +01:00
Merge pull request #22299 from hrydgard/real-mpeg-prx-fixes
More fixes for real sceMpeg on top of sceVideocodec (DisableHLE)
This commit is contained in:
7 files changed
+154
-94
No files matched your search
@@ -252,6 +252,17 @@ private:
|
||||
|
||||
But generally follow the surrounding style. Braces are preferred on the same line. Braces are always used even when they could be omitted due the inner part being just a single line.
|
||||
|
||||
### Comments
|
||||
|
||||
Keep comments tight. Say the thing once; don't restate what the code already shows, and don't
|
||||
allude to a previous, now-corrected version ("eight addresses and nothing else" - the "and nothing
|
||||
else" only makes sense against the old wrong layout, which is history). Cut filler like "the two
|
||||
are the same shape" down to "(the same shape)".
|
||||
|
||||
For a parenthetical aside, prefer parentheses over a pair of spaced dashes: write "the audio thread
|
||||
that paces playback", or "the audio thread (which paces playback)", not "the audio thread - which
|
||||
paces playback -". A single trailing dash to tack on an example is fine.
|
||||
|
||||
We've been inconsistent with copyright notices, but for new files, have the year at 2012, and add the "This program is free software..." as in other files.
|
||||
|
||||
`// Copyright (c) 2012- PPSSPP Project.`
|
||||
+15
-24
@@ -96,11 +96,9 @@ static_assert(offsetof(SceAudiocodecCodec, allocMem) == 0x68);
|
||||
|
||||
// AAC (0x1003)
|
||||
// ------------------------------------------------
|
||||
// Sample rate is at offset 0x28.
|
||||
// srcBytesConsumed can be very small the first frames.
|
||||
// 0x1000 is always the frame size.
|
||||
// The firmware sizes AAC from the bytes at 0x2c and 0x2d rather than from 0x28: input is
|
||||
// 0x600 or 0x609, output 0x1000 or 0x2000, depending on those two. Consistent with the above.
|
||||
// Sample rate is at offset 0x28. The input frame size is 0x609 when the byte at 0x2c is nonzero
|
||||
// and 0x600 when it is zero (avcodec.prx decodeUtility, case 0x1003); the output size comes from
|
||||
// the byte at 0x2d and is 0x1000 or 0x2000.
|
||||
|
||||
// MP3 (0x1002)
|
||||
// ------------------------------------------------
|
||||
@@ -141,24 +139,12 @@ void CalculateInputBytesAndChannelsAt3Plus(const SceAudiocodecCodec *ctx, int *i
|
||||
return;
|
||||
}
|
||||
|
||||
int size = formatByte2 * 8 + 8;
|
||||
// No idea if this is accurate, this is just a guess...
|
||||
if (formatByte1 & 8) {
|
||||
*channels = 2;
|
||||
} else {
|
||||
*channels = 1;
|
||||
}
|
||||
switch (size) {
|
||||
case 0x118:
|
||||
case 0x178:
|
||||
case 0x230:
|
||||
case 0x2E8:
|
||||
// These have been seen before, let's return it.
|
||||
*inputBytes = size;
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// bit 3 of the first byte is the channel count. This is a guess, but it fits the data we have.
|
||||
*channels = (formatByte1 & 8) ? 2 : 1;
|
||||
// formatByte2 * 8 + 8 gives the frame size for every bitrate we have data for (0x118, 0x178,
|
||||
// 0x230, 0x2E8), so use it for any other value as well rather than leaving inputBytes at 0,
|
||||
// which the firmware never does and which would fail the decode outright.
|
||||
*inputBytes = formatByte2 * 8 + 8;
|
||||
}
|
||||
|
||||
// Atrac3 (0x1001). Unlike Atrac3+, the context doesn't carry a frame size - libatrac3plus.prx
|
||||
@@ -342,7 +328,11 @@ static int sceAudiocodecDecode(u32 ctxPtr, int codec) {
|
||||
sampleRate = Mp3SampleRateFromContext(ctx);
|
||||
break;
|
||||
case PSP_CODEC_AAC:
|
||||
bytesPerFrame = ctx->srcBytesRead;
|
||||
// avcodec.prx sizes the AAC input frame from the byte at 0x2c: 0x609 when it is nonzero,
|
||||
// 0x600 when it is zero (decodeUtility, case 0x1003). srcBytesRead, which this used to read,
|
||||
// is an output field and is 0 on the first call, so the decoder got nothing to read and
|
||||
// audio never started (seen in Meururu no Atelier Plus running the real mpeg.prx).
|
||||
bytesPerFrame = ctx->fmt.aac.unk2c ? 0x609 : 0x600;
|
||||
sampleRate = ctx->fmt.aac.sampleRate;
|
||||
break;
|
||||
case PSP_CODEC_AT3:
|
||||
@@ -470,6 +460,7 @@ static int sceAudiocodecCheckNeedMem(u32 ctxPtr, int codec) {
|
||||
break;
|
||||
case 0x1003:
|
||||
// Kosmodrones uses sceAudiocodec directly (no intermediate library).
|
||||
ctx->neededMem = 0x18f20;
|
||||
INFO_LOG(Log::ME, "CheckNeedMem for codec %04x: format %02x %02x", codec, ctx->fmt.at3.formatByte1, ctx->fmt.at3.formatByte2);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -100,12 +100,13 @@ struct SceAudiocodecCodec {
|
||||
s32 unk64; // 0x64
|
||||
} mp3;
|
||||
|
||||
// AAC. The hardware sizes its input from the byte at 0x2c and its output from the byte
|
||||
// at 0x2d; the sample rate at 0x28 is our own observation from games.
|
||||
// AAC. The input frame size is 0x609 when unk2c is nonzero and 0x600 when it is zero, and
|
||||
// the output size comes from unk2d (avcodec.prx decodeUtility); the sample rate at 0x28 is
|
||||
// our own observation from games.
|
||||
struct {
|
||||
u32 sampleRate; // 0x28
|
||||
u8 unk2c; // 0x2c
|
||||
u8 unk2d; // 0x2d
|
||||
u8 unk2c; // 0x2c selects the input frame size (see above)
|
||||
u8 unk2d; // 0x2d selects the output size
|
||||
u8 unk2e; // 0x2e
|
||||
u8 unk2f; // 0x2f
|
||||
s32 unk30[14]; // 0x30..0x67
|
||||
|
||||
+84
-44
@@ -132,24 +132,30 @@ std::vector<u8> MpegBaseTakePESPacket(u32 dest) {
|
||||
// mpeg.prx hands the ME's decoded output to these to be converted to RGB. The descriptor it
|
||||
// passes is 48 bytes, which matches the range mpegbase.prx bounds-checks before using it.
|
||||
//
|
||||
// The dimensions appear twice. Death Jr. has the plain macroblock counts in both pairs, but
|
||||
// Thrillville's psmfplayer path has them shifted up by 8 in the first pair and plain in the
|
||||
// second, so the second is the one to trust. The four luma buffers are at 0x20; the chroma ones
|
||||
// aren't in here at all (see the recovery in MpegBaseCscRange).
|
||||
// mpeg.prx builds this on its own stack before each call (1.3 at 08805698, 1.8 at 08805898, the
|
||||
// same shape) from the eight buffer addresses sceVideocodec published and the dimensions from its
|
||||
// own context. mpegbase.prx reads the dimensions at 0x00/0x04 and the eight buffers at 0x10..0x2c.
|
||||
//
|
||||
// The buffers live either in Media Engine memory (a freshly decoded frame) or in the game's
|
||||
// memory (once sceMpegBaseYCrCbCopy has moved one out).
|
||||
struct SceMp4AvcCscStruct {
|
||||
s32_le scaledHeight; // 0x00 height << 8 on some paths, macroblocks on others - unused
|
||||
s32_le scaledWidth; // 0x04
|
||||
s32_le mode0; // 0x08
|
||||
s32_le mode1; // 0x0c
|
||||
s32_le height; // 0x10 in macroblocks
|
||||
s32_le width; // 0x14 in macroblocks
|
||||
s32_le unk18; // 0x18
|
||||
s32_le unk1c; // 0x1c
|
||||
u32_le buffer[4]; // 0x20 luma only
|
||||
s32_le height; // 0x00 in macroblocks
|
||||
s32_le width; // 0x04
|
||||
s32_le unk08; // 0x08
|
||||
s32_le unk0c; // 0x0c
|
||||
u32_le buffer[8]; // 0x10 four luma, then four chroma
|
||||
};
|
||||
static_assert(sizeof(SceMp4AvcCscStruct) == 0x30);
|
||||
|
||||
|
||||
// A frame buffer is either in the Media Engine's memory or, after a copy, in the game's.
|
||||
static const u8 *MpegBaseFramePointer(u32 addr, int size) {
|
||||
if (const u8 *me = VideocodecMEPointer(addr, size)) {
|
||||
return me;
|
||||
}
|
||||
return Memory::GetTypedPointerRange<u8>(addr, size);
|
||||
}
|
||||
|
||||
// The ME doesn't write plain planar YCbCr. The layout below was established by analysing
|
||||
// sceMpegBaseYCrCbCopy output on a real PSP (documented in JPCSP's sceVideocodec), and it uses
|
||||
// all eight buffers in the descriptor:
|
||||
@@ -163,8 +169,8 @@ static_assert(sizeof(SceMp4AvcCscStruct) == 0x30);
|
||||
//
|
||||
// Chroma is one (Cb,Cr) byte pair per 2x2 pixel square, so in chroma coordinates the bands are
|
||||
// 16 wide with 8-pixel halves, and the same even/odd split applies:
|
||||
// buffer4: left half, even chroma rows buffer5: left half, odd chroma rows
|
||||
// buffer6: right half, even chroma rows buffer7: right half, odd chroma rows
|
||||
// buffer4: left half, even chroma rows buffer5: right half, even chroma rows
|
||||
// buffer6: left half, odd chroma rows buffer7: right half, odd chroma rows
|
||||
//
|
||||
// Untangling it into plain planes costs one pass per frame, which keeps the conversion below
|
||||
// readable and is not where the time goes.
|
||||
@@ -178,20 +184,17 @@ static bool ReadTiledYCbCr(const u32 *buffers, int width, int height,
|
||||
const int *ySize = sizes;
|
||||
const int *cSize = sizes + 4;
|
||||
|
||||
// These are addresses in the Media Engine's memory, not in PSP RAM, so they resolve through
|
||||
// sceVideocodec rather than through Memory::. A descriptor that was never filled in holds
|
||||
// small integers instead, and those simply aren't in the ME's range.
|
||||
const u8 *y[4] = {};
|
||||
const u8 *c[4] = {};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (ySize[i] > 0) {
|
||||
y[i] = VideocodecMEPointer(buffers[i], ySize[i]);
|
||||
y[i] = MpegBaseFramePointer(buffers[i], ySize[i]);
|
||||
if (!y[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (cSize[i] > 0) {
|
||||
c[i] = VideocodecMEPointer(buffers[4 + i], cSize[i]);
|
||||
c[i] = MpegBaseFramePointer(buffers[4 + i], cSize[i]);
|
||||
if (!c[i]) {
|
||||
return false;
|
||||
}
|
||||
@@ -226,8 +229,8 @@ static bool ReadTiledYCbCr(const u32 *buffers, int width, int height,
|
||||
if (!c[b]) {
|
||||
continue;
|
||||
}
|
||||
const int xOffset = (b >> 1) ? 8 : 0;
|
||||
const int yStart = (b & 1) ? 1 : 0;
|
||||
const int xOffset = (b & 1) ? 8 : 0;
|
||||
const int yStart = (b >> 1) ? 1 : 0;
|
||||
int j = 0;
|
||||
for (int bandX = xOffset; bandX < width2; bandX += 16) {
|
||||
for (int row = yStart; row < height2; row += 2) {
|
||||
@@ -278,6 +281,10 @@ static int MpegBaseCscRange(u32 bufferRGB, u32 cscAddr, int bufferWidth,
|
||||
bufferWidth = g_mpegBaseBufferWidth;
|
||||
}
|
||||
|
||||
u32 buffers[8]{};
|
||||
for (int i = 0; i < 8; i++) {
|
||||
buffers[i] = csc->buffer[i];
|
||||
}
|
||||
const int width = csc->width << 4;
|
||||
const int height = csc->height << 4;
|
||||
if (width <= 0 || height <= 0 || width > 1024 || height > 1024) {
|
||||
@@ -296,14 +303,6 @@ static int MpegBaseCscRange(u32 bufferRGB, u32 cscAddr, int bufferWidth,
|
||||
return hleLogError(Log::Mpeg, -1, "range outside the frame");
|
||||
}
|
||||
|
||||
// The descriptor only carries the four luma buffers. Recover the full set from the allocation
|
||||
// sceVideocodec handed out - both ends of that are ours.
|
||||
u32 buffers[8]{};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
buffers[i] = csc->buffer[i];
|
||||
}
|
||||
VideocodecGetFrameBuffers(csc->buffer[0], buffers);
|
||||
|
||||
std::vector<u8> luma, cb, cr;
|
||||
if (!ReadTiledYCbCr(buffers, width, height, luma, cb, cr)) {
|
||||
return hleLogError(Log::Mpeg, -1, "YCbCr buffers not readable");
|
||||
@@ -335,13 +334,18 @@ static int MpegBaseCscRange(u32 bufferRGB, u32 cscAddr, int bufferWidth,
|
||||
}
|
||||
}
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, bufferRGB, destSize, "MpegBaseCsc");
|
||||
// The CPU just wrote a video frame straight into what is usually a display buffer. The hardware
|
||||
// backends don't see that on their own, so without telling them, the screen keeps showing the
|
||||
// last frame the GE drew - the same notification our sceMpegAvcCsc HLE does. The pixel mode
|
||||
// numbering matches GEBufferFormat, as it does there.
|
||||
// The CPU just wrote a video frame into what is usually a display buffer. The hardware backends
|
||||
// don't see that on their own, so without telling them the screen keeps showing the last frame
|
||||
// the GE drew (the same notification our sceMpegAvcCsc HLE does). The pixel mode numbering
|
||||
// matches GEBufferFormat, as it does there.
|
||||
gpu->PerformWriteFormattedFromMemory(bufferRGB, destSize, bufferWidth, (GEBufferFormat)g_mpegBasePixelMode);
|
||||
return hleLogDebug(Log::Mpeg, 0, "%dx%d at %d,%d -> %08x stride %d",
|
||||
rangeWidth, rangeHeight, rangeX, rangeY, bufferRGB, bufferWidth);
|
||||
// This runs on the DMACPLUS and takes real time. A psmfplayer game blits the current video
|
||||
// frame every render frame while it waits for the next, so an instant return is a tight loop
|
||||
// that never yields and starves the audio thread that paces playback, and the A/V pipeline
|
||||
// deadlocks a few frames in (SOCOM: Tactical Strike hangs exactly here). Our sceMpeg HLE delays
|
||||
// sceMpegAvcCsc the same way.
|
||||
return hleDelayResult(hleLogDebug(Log::Mpeg, 0, "%dx%d at %d,%d -> %08x stride %d",
|
||||
rangeWidth, rangeHeight, rangeX, rangeY, bufferRGB, bufferWidth), "mpegbase csc", 4000);
|
||||
}
|
||||
|
||||
static int sceMpegBaseCscInit(int bufferWidth) {
|
||||
@@ -372,7 +376,9 @@ static int sceMpegBaseCscAvc(u32 bufferRGB, u32 unknown, int bufferWidth, u32 cs
|
||||
if (!csc.IsValid()) {
|
||||
return hleLogError(Log::Mpeg, -1, "bad csc struct pointer");
|
||||
}
|
||||
return MpegBaseCscRange(bufferRGB, cscAddr, bufferWidth, 0, 0, csc->width << 4, csc->height << 4);
|
||||
// The whole frame. MpegBaseCscRange clamps to the real frame size (from the allocation, not the
|
||||
// descriptor), so pass more than any frame can be.
|
||||
return MpegBaseCscRange(bufferRGB, cscAddr, bufferWidth, 0, 0, 1024, 1024);
|
||||
}
|
||||
|
||||
static u32 sceMpegBaseCscAvcRange(u32 bufferRGB, u32 unknown, u32 rangeAddr, int bufferWidth, u32 cscAddr) {
|
||||
@@ -387,16 +393,50 @@ static u32 sceMpegBaseCscAvcRange(u32 bufferRGB, u32 unknown, u32 rangeAddr, int
|
||||
return MpegBaseCscRange(bufferRGB, cscAddr, bufferWidth, rangeX, rangeY, rangeWidth, rangeHeight);
|
||||
}
|
||||
|
||||
// Copies one 48-byte YCrCb descriptor over another. The hardware copies the buffers themselves
|
||||
// when told to; games seen so far only use it to move the descriptor around, so that is all we
|
||||
// do until something needs more.
|
||||
// Moves a decoded frame between two sets of buffers. Both arguments are descriptors; what gets
|
||||
// copied is the pixels they point at, not the descriptors.
|
||||
//
|
||||
// mpegbase.prx builds a DMA list over the eight buffers (080010f8 in mpegbase_260.prx): flag bit 0
|
||||
// selects buffers 0,1,4,5 and bit 1 selects 2,3,6,7, with the per-buffer sizes sceVideocodec lays
|
||||
// its frame out with. mpeg.prx always passes 3 (all eight). The source is the ME's frame; the
|
||||
// destination is wherever the caller wants it (for psmfplayer, a slot in its output pool).
|
||||
static int sceMpegBaseYCrCbCopy(u32 dstAddr, u32 srcAddr, int flags) {
|
||||
if (!Memory::IsValidRange(dstAddr, sizeof(SceMp4AvcCscStruct)) ||
|
||||
!Memory::IsValidRange(srcAddr, sizeof(SceMp4AvcCscStruct))) {
|
||||
auto dst = PSPPointer<SceMp4AvcCscStruct>::Create(dstAddr);
|
||||
auto src = PSPPointer<SceMp4AvcCscStruct>::Create(srcAddr);
|
||||
if (!dst.IsValid() || !src.IsValid()) {
|
||||
return hleLogError(Log::Mpeg, -1, "bad descriptor pointer");
|
||||
}
|
||||
Memory::Memcpy(dstAddr, srcAddr, (u32)sizeof(SceMp4AvcCscStruct), "MpegBaseYCrCbCopy");
|
||||
return hleLogDebug(Log::Mpeg, 0, "flags %08x - descriptor copied, buffers left alone", flags);
|
||||
|
||||
// The destination descriptor carries the dimensions in macroblocks (as the colour conversion's
|
||||
// does); the stack-built source descriptor states them in pixels, so read the destination.
|
||||
const int width = dst->width << 4;
|
||||
const int height = dst->height << 4;
|
||||
if (width <= 0 || height <= 0 || width > 1024 || height > 1024) {
|
||||
return hleLogError(Log::Mpeg, -1, "unreasonable frame size %dx%d", width, height);
|
||||
}
|
||||
int sizes[8];
|
||||
VideocodecFrameBufferLayout(width, height, sizes, nullptr);
|
||||
|
||||
int copied = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
// Buffers 0,1,4,5 go with bit 0 and 2,3,6,7 with bit 1 (the even and odd row halves of luma
|
||||
// and chroma).
|
||||
const int bit = ((i & 3) < 2) ? 1 : 2;
|
||||
if (!(flags & bit) || sizes[i] <= 0) {
|
||||
continue;
|
||||
}
|
||||
const u8 *from = MpegBaseFramePointer(src->buffer[i], sizes[i]);
|
||||
if (!from) {
|
||||
return hleLogError(Log::Mpeg, -1, "source buffer %d not readable", i);
|
||||
}
|
||||
if (!Memory::IsValidRange(dst->buffer[i], sizes[i])) {
|
||||
return hleLogError(Log::Mpeg, -1, "destination buffer %d (%08x, %d bytes) not writable",
|
||||
i, (u32)dst->buffer[i], sizes[i]);
|
||||
}
|
||||
Memory::MemcpyUnchecked(dst->buffer[i], from, sizes[i]);
|
||||
copied += sizes[i];
|
||||
}
|
||||
return hleLogDebug(Log::Mpeg, 0, "flags %d, %dx%d, %d bytes", flags, width, height, copied);
|
||||
}
|
||||
|
||||
const HLEFunction sceMpegbase[] =
|
||||
|
||||
+33
-15
@@ -227,9 +227,13 @@ u32 VideocodecFrameBufferLayout(int width, int height, int sizes[8], u32 offsets
|
||||
// buffer0/2 take the odd band out when the width isn't a multiple of 32.
|
||||
const int lumaLeft = ((width + 16) >> 5) * (height >> 1) * 16;
|
||||
const int lumaRight = (width >> 5) * (height >> 1) * 16;
|
||||
// Chroma is paired like luma (left/right of a band, then even/odd rows), which is what
|
||||
// sceMpegBaseYCrCbCopy's flags assume: bit 0 selects buffers 0,1,4,5 and bit 1 selects 2,3,6,7.
|
||||
// The sizes have to match that, or a copy writes the wrong count into a buffer someone else
|
||||
// sized.
|
||||
const int local[8] = {
|
||||
lumaLeft, lumaRight, lumaLeft, lumaRight,
|
||||
lumaLeft >> 1, lumaLeft >> 1, lumaRight >> 1, lumaRight >> 1,
|
||||
lumaLeft >> 1, lumaRight >> 1, lumaLeft >> 1, lumaRight >> 1,
|
||||
};
|
||||
u32 total = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
@@ -244,9 +248,8 @@ u32 VideocodecFrameBufferLayout(int width, int height, int sizes[8], u32 offsets
|
||||
return total;
|
||||
}
|
||||
|
||||
// The descriptor mpeg.prx passes in is empty: on hardware the ME owns the frame buffers, and
|
||||
// reports where it put them. So allocate them here and fill the descriptor in the shape
|
||||
// sceMpegBaseCscAvc expects - dimensions in macroblocks, then the eight buffer addresses.
|
||||
// The descriptor mpeg.prx passes in is empty: on hardware the ME owns the frame buffers and
|
||||
// reports where it put them. So allocate them here and fill in the eight buffer addresses.
|
||||
static bool PublishFrameBuffers(VideocodecCtx &vctx, u32 structAddr, int width, int height, u32 buffers[8]) {
|
||||
u32 offsets[8];
|
||||
const u32 total = VideocodecFrameBufferLayout(width, height, nullptr, offsets);
|
||||
@@ -278,13 +281,15 @@ static bool PublishFrameBuffers(VideocodecCtx &vctx, u32 structAddr, int width,
|
||||
buffers[i] = vctx.frameBuffers + offsets[i];
|
||||
}
|
||||
|
||||
if (!Memory::IsValidRange(structAddr, 48)) {
|
||||
// mpeg.prx reads eight buffer addresses off the front of this structure (`lw` at 0x00..0x1C,
|
||||
// verified in 1.3 at 08805698 and 1.8 at 08805898) and takes the frame dimensions from its own
|
||||
// context. So write only the addresses; anything else at the front lands in slots 0 and 1,
|
||||
// which sceMpegBaseYCrCbCopy then DMAs to.
|
||||
if (!Memory::IsValidRange(structAddr, 8 * 4)) {
|
||||
return false;
|
||||
}
|
||||
Memory::WriteUnchecked_U32(height >> 4, structAddr + 0); // macroblocks
|
||||
Memory::WriteUnchecked_U32(width >> 4, structAddr + 4);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
Memory::WriteUnchecked_U32(buffers[i], structAddr + 16 + i * 4);
|
||||
Memory::WriteUnchecked_U32(buffers[i], structAddr + i * 4);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -308,7 +313,7 @@ void VideocodecGetCtxInfo(std::vector<VideocodecCtxInfo> *infos) {
|
||||
}
|
||||
}
|
||||
|
||||
bool VideocodecGetFrameBuffers(u32 firstBuffer, u32 buffers[8]) {
|
||||
bool VideocodecGetFrameBuffers(u32 firstBuffer, u32 buffers[8], int *width, int *height) {
|
||||
// sceMpegbase only has the first of the eight addresses, so find whose allocation it is.
|
||||
const VideocodecCtx *found = nullptr;
|
||||
for (const auto &[addr, ctx] : g_videocodecCtxs) {
|
||||
@@ -322,6 +327,12 @@ bool VideocodecGetFrameBuffers(u32 firstBuffer, u32 buffers[8]) {
|
||||
}
|
||||
u32 offsets[8];
|
||||
VideocodecFrameBufferLayout(found->frameBufferWidth, found->frameBufferHeight, nullptr, offsets);
|
||||
if (width) {
|
||||
*width = found->frameBufferWidth;
|
||||
}
|
||||
if (height) {
|
||||
*height = found->frameBufferHeight;
|
||||
}
|
||||
for (int i = 0; i < 8; i++) {
|
||||
buffers[i] = found->frameBuffers + offsets[i];
|
||||
}
|
||||
@@ -381,8 +392,8 @@ static void WriteTiledYCbCr(const u32 *buffers, const AvcDecoder &dec, int width
|
||||
if (!dst) {
|
||||
continue;
|
||||
}
|
||||
const int xOffset = (b >> 1) ? 8 : 0;
|
||||
const int yStart = (b & 1) ? 1 : 0;
|
||||
const int xOffset = (b & 1) ? 8 : 0;
|
||||
const int yStart = (b >> 1) ? 1 : 0;
|
||||
int j = 0;
|
||||
for (int bandX = xOffset; bandX < width2; bandX += 16) {
|
||||
for (int row = yStart; row < height2; row += 2) {
|
||||
@@ -624,11 +635,18 @@ static int sceVideocodecSetMemory(u32 ctxAddr, int type) {
|
||||
return hleLogDebug(Log::ME, 0);
|
||||
}
|
||||
|
||||
static int sceVideocodec_893B32B1(u32 ctxAddr, int type) {
|
||||
// 0x893B32B1. mpeg.prx runs this from sceMpegCreate, only in mode 1 (the path where the game reads
|
||||
// raw YCbCr out rather than letting sceMpegbase convert to RGB). On hardware it writes back the
|
||||
// 0x28-byte output descriptor at ctx+0x10 and issues ME video op 0x6D68B223 to configure the codec.
|
||||
// Nothing we run reaches it, so it stays a stub - implement it if a mode-1 game needs it.
|
||||
static int sceVideocodecSetMode(u32 ctxAddr, int type) {
|
||||
return hleLogWarning(Log::ME, 0, "UNIMPL");
|
||||
}
|
||||
|
||||
static int sceVideocodec_D95C24D5(u32 ctxAddr, int type) {
|
||||
// 0xD95C24D5. Copies a decoded YCbCr frame between two sets of buffers through the ME (op
|
||||
// 0x21521BE5) - the videocodec-level counterpart of sceMpegBaseYCrCbCopy. mpeg.prx calls it on the
|
||||
// sceMpegAvcCopyYCbCr path. Nothing we run reaches it, so it stays a stub for now.
|
||||
static int sceVideocodecCopyYCbCr(u32 ctxAddr, int type) {
|
||||
return hleLogWarning(Log::ME, 0, "UNIMPL");
|
||||
}
|
||||
|
||||
@@ -645,8 +663,8 @@ const HLEFunction sceVideocodec[] = {
|
||||
{0X17CF7D2C, &WrapI_UI<sceVideocodecGetFrameCrop>, "sceVideocodecGetFrameCrop", 'i', "xi"},
|
||||
{0X26927D19, &WrapI_UI<sceVideocodecGetVersion>, "sceVideocodecGetVersion", 'i', "xi"},
|
||||
{0X627B7D42, &WrapI_UI<sceVideocodecGetSEI>, "sceVideocodecGetSEI", 'i', "xi"},
|
||||
{0X893B32B1, &WrapI_UI<sceVideocodec_893B32B1>, "sceVideocodec_893B32B1", 'i', "xi"},
|
||||
{0XD95C24D5, &WrapI_UI<sceVideocodec_D95C24D5>, "sceVideocodec_D95C24D5", 'i', "xi"},
|
||||
{0X893B32B1, &WrapI_UI<sceVideocodecSetMode>, "sceVideocodecSetMode", 'i', "xi"},
|
||||
{0XD95C24D5, &WrapI_UI<sceVideocodecCopyYCbCr>, "sceVideocodecCopyYCbCr", 'i', "xi"},
|
||||
};
|
||||
|
||||
void Register_sceVideocodec() {
|
||||
|
||||
@@ -52,10 +52,10 @@ void VideocodecGetCtxInfo(std::vector<VideocodecCtxInfo> *infos);
|
||||
// than through Memory:: - the ME's memory is not part of PSP RAM.
|
||||
u8 *VideocodecMEPointer(u32 addr, u32 size);
|
||||
|
||||
// mpeg.prx copies only the four luma buffers into the descriptor it hands sceMpegBaseCscAvc.
|
||||
// Both ends of that are ours, so the conversion can recover the other four from the allocation
|
||||
// they came from. Returns false if `firstBuffer` isn't one we handed out.
|
||||
bool VideocodecGetFrameBuffers(u32 firstBuffer, u32 buffers[8]);
|
||||
// The eight buffers of the frame starting at `firstBuffer`, and its size. Returns false if that
|
||||
// isn't the start of an allocation we handed out (the normal answer once sceMpegBaseYCrCbCopy has
|
||||
// moved a frame into the game's memory).
|
||||
bool VideocodecGetFrameBuffers(u32 firstBuffer, u32 buffers[8], int *width = nullptr, int *height = nullptr);
|
||||
|
||||
// How the eight buffers a frame is delivered in are sized and laid out, in the order the
|
||||
// descriptor lists them: four luma (left/right half of a 32-pixel band, even/odd rows) then four
|
||||
|
||||
@@ -451,9 +451,8 @@ void BlockAllocator::DoState(PointerWrap &p)
|
||||
const bool compact = s >= 2;
|
||||
int count = 0;
|
||||
|
||||
// An allocator that was never Init'd (or has been Shutdown) has no blocks at all. That's a
|
||||
// perfectly good state to save - it's what one that nothing has asked for memory from yet
|
||||
// looks like - so zero blocks is a normal count here, not a corrupt one.
|
||||
// An allocator that was never Init'd (or has been Shutdown) has no blocks, which is a valid
|
||||
// state to save, so a zero block count here is normal rather than corrupt.
|
||||
if (p.mode == p.MODE_READ)
|
||||
{
|
||||
Shutdown();
|
||||
|
||||
Reference in new issue
Block a user