PPUThread refactoring

`CallbackManager` removed, added _gcm_intr_thread for cellGcmSys
`PPUThread` renamed to `ppu_thread`, inheritance allowed
Added lightweight command queue for `ppu_thread`
Implemented call stack dump for PPU
`get_current_thread_mutex` removed
`thread_ctrl::spawn`: minor initialization fix
`thread_ctrl::wait_for` added
`named_thread`: some methods added
`cpu_thread::run` added
Some bugs fixes, including SPU channels
This commit is contained in:
Nekotekina 2016-07-28 00:43:22 +03:00
parent 33c59fa51b
commit f8719c1230
99 changed files with 4480 additions and 4592 deletions

View file

@ -3,8 +3,6 @@
#include "Emu/IdManager.h"
#include "Emu/Cell/PPUModule.h"
extern std::mutex g_mutex_avcodec_open2;
extern "C"
{
#include "libavcodec/avcodec.h"
@ -15,97 +13,391 @@ extern "C"
#include "cellPamf.h"
#include "cellAdec.h"
#include <mutex>
#include <thread>
extern std::mutex g_mutex_avcodec_open2;
logs::channel cellAdec("cellAdec", logs::level::notice);
AudioDecoder::AudioDecoder(s32 type, u32 addr, u32 size, vm::ptr<CellAdecCbMsg> func, u32 arg)
: type(type)
, memAddr(addr)
, memSize(size)
, memBias(0)
, cbFunc(func)
, cbArg(arg)
, is_closed(false)
, is_finished(false)
, just_started(false)
, just_finished(false)
, codec(nullptr)
, input_format(nullptr)
, ctx(nullptr)
, fmt(nullptr)
class AudioDecoder : public ppu_thread
{
av_register_all();
avcodec_register_all();
public:
squeue_t<AdecTask> job;
volatile bool is_closed;
volatile bool is_finished;
bool just_started;
bool just_finished;
switch (type)
{
case CELL_ADEC_TYPE_ATRACX:
case CELL_ADEC_TYPE_ATRACX_2CH:
case CELL_ADEC_TYPE_ATRACX_6CH:
case CELL_ADEC_TYPE_ATRACX_8CH:
{
codec = avcodec_find_decoder(AV_CODEC_ID_ATRAC3P);
input_format = av_find_input_format("oma");
break;
}
case CELL_ADEC_TYPE_MP3:
{
codec = avcodec_find_decoder(AV_CODEC_ID_MP3);
input_format = av_find_input_format("mp3");
break;
}
default:
{
throw EXCEPTION("Unknown type (0x%x)", type);
}
}
if (!codec)
{
throw EXCEPTION("avcodec_find_decoder() failed");
}
if (!input_format)
{
throw EXCEPTION("av_find_input_format() failed");
}
fmt = avformat_alloc_context();
if (!fmt)
{
throw EXCEPTION("avformat_alloc_context() failed");
}
io_buf = (u8*)av_malloc(4096);
fmt->pb = avio_alloc_context(io_buf, 256, 0, this, adecRead, NULL, NULL);
if (!fmt->pb)
{
throw EXCEPTION("avio_alloc_context() failed");
}
}
AVCodec* codec;
AVInputFormat* input_format;
AVCodecContext* ctx;
AVFormatContext* fmt;
u8* io_buf;
AudioDecoder::~AudioDecoder()
{
// TODO: check finalization
AdecFrame af;
while (frames.try_pop(af))
struct AudioReader
{
av_frame_unref(af.data);
av_frame_free(&af.data);
}
if (ctx)
{
avcodec_close(ctx);
avformat_close_input(&fmt);
}
if (fmt)
{
if (io_buf)
u32 addr;
u32 size;
bool init;
bool has_ats;
AudioReader()
: init(false)
{
av_free(io_buf);
}
if (fmt->pb) av_free(fmt->pb);
avformat_free_context(fmt);
} reader;
squeue_t<AdecFrame> frames;
const s32 type;
const u32 memAddr;
const u32 memSize;
const vm::ptr<CellAdecCbMsg> cbFunc;
const u32 cbArg;
u32 memBias;
AdecTask task;
u64 last_pts, first_pts;
u32 ch_out;
u32 ch_cfg;
u32 frame_size;
u32 sample_rate;
bool use_ats_headers;
AudioDecoder(s32 type, u32 addr, u32 size, vm::ptr<CellAdecCbMsg> func, u32 arg)
: ppu_thread("HLE Audio Decoder")
, type(type)
, memAddr(addr)
, memSize(size)
, memBias(0)
, cbFunc(func)
, cbArg(arg)
, is_closed(false)
, is_finished(false)
, just_started(false)
, just_finished(false)
, codec(nullptr)
, input_format(nullptr)
, ctx(nullptr)
, fmt(nullptr)
{
av_register_all();
avcodec_register_all();
switch (type)
{
case CELL_ADEC_TYPE_ATRACX:
case CELL_ADEC_TYPE_ATRACX_2CH:
case CELL_ADEC_TYPE_ATRACX_6CH:
case CELL_ADEC_TYPE_ATRACX_8CH:
{
codec = avcodec_find_decoder(AV_CODEC_ID_ATRAC3P);
input_format = av_find_input_format("oma");
break;
}
case CELL_ADEC_TYPE_MP3:
{
codec = avcodec_find_decoder(AV_CODEC_ID_MP3);
input_format = av_find_input_format("mp3");
break;
}
default:
{
throw EXCEPTION("Unknown type (0x%x)", type);
}
}
if (!codec)
{
throw EXCEPTION("avcodec_find_decoder() failed");
}
if (!input_format)
{
throw EXCEPTION("av_find_input_format() failed");
}
fmt = avformat_alloc_context();
if (!fmt)
{
throw EXCEPTION("avformat_alloc_context() failed");
}
io_buf = (u8*)av_malloc(4096);
fmt->pb = avio_alloc_context(io_buf, 256, 0, this, adecRead, NULL, NULL);
if (!fmt->pb)
{
throw EXCEPTION("avio_alloc_context() failed");
}
}
}
~AudioDecoder()
{
// TODO: check finalization
AdecFrame af;
while (frames.try_pop(af))
{
av_frame_unref(af.data);
av_frame_free(&af.data);
}
if (ctx)
{
avcodec_close(ctx);
avformat_close_input(&fmt);
}
if (fmt)
{
if (io_buf)
{
av_free(io_buf);
}
if (fmt->pb) av_free(fmt->pb);
avformat_free_context(fmt);
}
}
virtual void cpu_task() override
{
while (true)
{
if (Emu.IsStopped() || is_closed)
{
break;
}
if (!job.pop(task, &is_closed))
{
break;
}
switch (task.type)
{
case adecStartSeq:
{
// TODO: reset data
cellAdec.warning("adecStartSeq:");
reader.addr = 0;
reader.size = 0;
reader.init = false;
reader.has_ats = false;
just_started = true;
if (adecIsAtracX(type))
{
ch_cfg = task.at3p.channel_config;
ch_out = task.at3p.channels;
frame_size = task.at3p.frame_size;
sample_rate = task.at3p.sample_rate;
use_ats_headers = task.at3p.ats_header == 1;
}
break;
}
case adecEndSeq:
{
// TODO: finalize
cellAdec.warning("adecEndSeq:");
cbFunc(*this, id, CELL_ADEC_MSG_TYPE_SEQDONE, CELL_OK, cbArg);
just_finished = true;
break;
}
case adecDecodeAu:
{
int err = 0;
reader.addr = task.au.addr;
reader.size = task.au.size;
reader.has_ats = use_ats_headers;
//LOG_NOTICE(HLE, "Audio AU: size = 0x%x, pts = 0x%llx", task.au.size, task.au.pts);
if (just_started)
{
first_pts = task.au.pts;
last_pts = task.au.pts;
if (adecIsAtracX(type)) last_pts -= 0x10000; // hack
}
struct AVPacketHolder : AVPacket
{
AVPacketHolder(u32 size)
{
av_init_packet(this);
if (size)
{
data = (u8*)av_calloc(1, size + FF_INPUT_BUFFER_PADDING_SIZE);
this->size = size + FF_INPUT_BUFFER_PADDING_SIZE;
}
else
{
data = NULL;
size = 0;
}
}
~AVPacketHolder()
{
av_free(data);
}
} au(0);
if (just_started && just_finished)
{
avcodec_flush_buffers(ctx);
reader.init = true; // wrong
just_finished = false;
just_started = false;
}
else if (just_started) // deferred initialization
{
AVDictionary* opts = nullptr;
av_dict_set(&opts, "probesize", "96", 0);
err = avformat_open_input(&fmt, NULL, input_format, &opts);
if (err || opts)
{
throw EXCEPTION("avformat_open_input() failed (err=0x%x, opts=%d)", err, opts ? 1 : 0);
}
//err = avformat_find_stream_info(fmt, NULL);
//if (err || !fmt->nb_streams)
//{
// ADEC_ERROR("adecDecodeAu: avformat_find_stream_info() failed (err=0x%x, nb_streams=%d)", err, fmt->nb_streams);
//}
if (!avformat_new_stream(fmt, codec))
{
throw EXCEPTION("avformat_new_stream() failed");
}
ctx = fmt->streams[0]->codec; // TODO: check data
opts = nullptr;
av_dict_set(&opts, "refcounted_frames", "1", 0);
{
std::lock_guard<std::mutex> lock(g_mutex_avcodec_open2);
// not multithread-safe (???)
err = avcodec_open2(ctx, codec, &opts);
}
if (err || opts)
{
throw EXCEPTION("avcodec_open2() failed (err=0x%x, opts=%d)", err, opts ? 1 : 0);
}
just_started = false;
}
bool last_frame = false;
while (true)
{
if (Emu.IsStopped() || is_closed)
{
if (Emu.IsStopped()) cellAdec.warning("adecDecodeAu: aborted");
break;
}
last_frame = av_read_frame(fmt, &au) < 0;
if (last_frame)
{
//break;
av_free(au.data);
au.data = NULL;
au.size = 0;
}
struct AdecFrameHolder : AdecFrame
{
AdecFrameHolder()
{
data = av_frame_alloc();
}
~AdecFrameHolder()
{
if (data)
{
av_frame_unref(data);
av_frame_free(&data);
}
}
} frame;
if (!frame.data)
{
throw EXCEPTION("av_frame_alloc() failed");
}
int got_frame = 0;
int decode = avcodec_decode_audio4(ctx, frame.data, &got_frame, &au);
if (decode <= 0)
{
if (decode < 0)
{
cellAdec.error("adecDecodeAu: AU decoding error(0x%x)", decode);
}
if (!got_frame && reader.size == 0) break;
}
if (got_frame)
{
//u64 ts = av_frame_get_best_effort_timestamp(frame.data);
//if (ts != AV_NOPTS_VALUE)
//{
// frame.pts = ts/* - first_pts*/;
// last_pts = frame.pts;
//}
last_pts += ((u64)frame.data->nb_samples) * 90000 / frame.data->sample_rate;
frame.pts = last_pts;
s32 nbps = av_get_bytes_per_sample((AVSampleFormat)frame.data->format);
switch (frame.data->format)
{
case AV_SAMPLE_FMT_FLTP: break;
case AV_SAMPLE_FMT_S16P: break;
default:
{
throw EXCEPTION("Unsupported frame format(%d)", frame.data->format);
}
}
frame.auAddr = task.au.addr;
frame.auSize = task.au.size;
frame.userdata = task.au.userdata;
frame.size = frame.data->nb_samples * frame.data->channels * nbps;
//LOG_NOTICE(HLE, "got audio frame (pts=0x%llx, nb_samples=%d, ch=%d, sample_rate=%d, nbps=%d)",
//frame.pts, frame.data->nb_samples, frame.data->channels, frame.data->sample_rate, nbps);
if (frames.push(frame, &is_closed))
{
frame.data = nullptr; // to prevent destruction
cbFunc(*this, id, CELL_ADEC_MSG_TYPE_PCMOUT, CELL_OK, cbArg);
}
}
}
cbFunc(*this, id, CELL_ADEC_MSG_TYPE_AUDONE, task.au.auInfo_addr, cbArg);
break;
}
case adecClose:
{
break;
}
default:
{
throw EXCEPTION("Unknown task(%d)", task.type);
}
}
}
is_finished = true;
}
};
int adecRead(void* opaque, u8* buf, int buf_size)
{
@ -162,16 +454,16 @@ next:
buf_size = adec.reader.size;
}
break;
case adecDecodeAu:
{
std::memcpy(buf, vm::base(adec.reader.addr), adec.reader.size);
buf += adec.reader.size;
buf_size -= adec.reader.size;
res += adec.reader.size;
adec.cbFunc(*adec.adecCb, adec.id, CELL_ADEC_MSG_TYPE_AUDONE, adec.task.au.auInfo_addr, adec.cbArg);
adec.cbFunc(adec, adec.id, CELL_ADEC_MSG_TYPE_AUDONE, adec.task.au.auInfo_addr, adec.cbArg);
adec.job.pop(adec.task);
@ -181,7 +473,7 @@ next:
//LOG_NOTICE(HLE, "Audio AU: size = 0x%x, pts = 0x%llx", adec.task.au.size, adec.task.au.pts);
}
break;
default:
{
cellAdec.error("adecRawRead(): unknown task (%d)", task.type);
@ -212,267 +504,6 @@ next:
}
}
void adecOpen(u32 adec_id) // TODO: call from the constructor
{
const auto sptr = idm::get<AudioDecoder>(adec_id);
AudioDecoder& adec = *sptr;
adec.id = adec_id;
adec.adecCb = idm::make_ptr<PPUThread>(fmt::format("Demuxer[0x%x] Thread", adec_id));
adec.adecCb->prio = 1001;
adec.adecCb->stack_size = 0x10000;
adec.adecCb->custom_task = [sptr](PPUThread& CPU)
{
AudioDecoder& adec = *sptr;
AdecTask& task = adec.task;
while (true)
{
if (Emu.IsStopped() || adec.is_closed)
{
break;
}
if (!adec.job.pop(task, &adec.is_closed))
{
break;
}
switch (task.type)
{
case adecStartSeq:
{
// TODO: reset data
cellAdec.warning("adecStartSeq:");
adec.reader.addr = 0;
adec.reader.size = 0;
adec.reader.init = false;
adec.reader.has_ats = false;
adec.just_started = true;
if (adecIsAtracX(adec.type))
{
adec.ch_cfg = task.at3p.channel_config;
adec.ch_out = task.at3p.channels;
adec.frame_size = task.at3p.frame_size;
adec.sample_rate = task.at3p.sample_rate;
adec.use_ats_headers = task.at3p.ats_header == 1;
}
break;
}
case adecEndSeq:
{
// TODO: finalize
cellAdec.warning("adecEndSeq:");
adec.cbFunc(CPU, adec.id, CELL_ADEC_MSG_TYPE_SEQDONE, CELL_OK, adec.cbArg);
adec.just_finished = true;
break;
}
case adecDecodeAu:
{
int err = 0;
adec.reader.addr = task.au.addr;
adec.reader.size = task.au.size;
adec.reader.has_ats = adec.use_ats_headers;
//LOG_NOTICE(HLE, "Audio AU: size = 0x%x, pts = 0x%llx", task.au.size, task.au.pts);
if (adec.just_started)
{
adec.first_pts = task.au.pts;
adec.last_pts = task.au.pts;
if (adecIsAtracX(adec.type)) adec.last_pts -= 0x10000; // hack
}
struct AVPacketHolder : AVPacket
{
AVPacketHolder(u32 size)
{
av_init_packet(this);
if (size)
{
data = (u8*)av_calloc(1, size + FF_INPUT_BUFFER_PADDING_SIZE);
this->size = size + FF_INPUT_BUFFER_PADDING_SIZE;
}
else
{
data = NULL;
size = 0;
}
}
~AVPacketHolder()
{
av_free(data);
}
} au(0);
if (adec.just_started && adec.just_finished)
{
avcodec_flush_buffers(adec.ctx);
adec.reader.init = true; // wrong
adec.just_finished = false;
adec.just_started = false;
}
else if (adec.just_started) // deferred initialization
{
AVDictionary* opts = nullptr;
av_dict_set(&opts, "probesize", "96", 0);
err = avformat_open_input(&adec.fmt, NULL, adec.input_format, &opts);
if (err || opts)
{
throw EXCEPTION("avformat_open_input() failed (err=0x%x, opts=%d)", err, opts ? 1 : 0);
}
//err = avformat_find_stream_info(adec.fmt, NULL);
//if (err || !adec.fmt->nb_streams)
//{
// ADEC_ERROR("adecDecodeAu: avformat_find_stream_info() failed (err=0x%x, nb_streams=%d)", err, adec.fmt->nb_streams);
//}
if (!avformat_new_stream(adec.fmt, adec.codec))
{
throw EXCEPTION("avformat_new_stream() failed");
}
adec.ctx = adec.fmt->streams[0]->codec; // TODO: check data
opts = nullptr;
av_dict_set(&opts, "refcounted_frames", "1", 0);
{
std::lock_guard<std::mutex> lock(g_mutex_avcodec_open2);
// not multithread-safe (???)
err = avcodec_open2(adec.ctx, adec.codec, &opts);
}
if (err || opts)
{
throw EXCEPTION("avcodec_open2() failed (err=0x%x, opts=%d)", err, opts ? 1 : 0);
}
adec.just_started = false;
}
bool last_frame = false;
while (true)
{
if (Emu.IsStopped() || adec.is_closed)
{
if (Emu.IsStopped()) cellAdec.warning("adecDecodeAu: aborted");
break;
}
last_frame = av_read_frame(adec.fmt, &au) < 0;
if (last_frame)
{
//break;
av_free(au.data);
au.data = NULL;
au.size = 0;
}
struct AdecFrameHolder : AdecFrame
{
AdecFrameHolder()
{
data = av_frame_alloc();
}
~AdecFrameHolder()
{
if (data)
{
av_frame_unref(data);
av_frame_free(&data);
}
}
} frame;
if (!frame.data)
{
throw EXCEPTION("av_frame_alloc() failed");
}
int got_frame = 0;
int decode = avcodec_decode_audio4(adec.ctx, frame.data, &got_frame, &au);
if (decode <= 0)
{
if (decode < 0)
{
cellAdec.error("adecDecodeAu: AU decoding error(0x%x)", decode);
}
if (!got_frame && adec.reader.size == 0) break;
}
if (got_frame)
{
//u64 ts = av_frame_get_best_effort_timestamp(frame.data);
//if (ts != AV_NOPTS_VALUE)
//{
// frame.pts = ts/* - adec.first_pts*/;
// adec.last_pts = frame.pts;
//}
adec.last_pts += ((u64)frame.data->nb_samples) * 90000 / frame.data->sample_rate;
frame.pts = adec.last_pts;
s32 nbps = av_get_bytes_per_sample((AVSampleFormat)frame.data->format);
switch (frame.data->format)
{
case AV_SAMPLE_FMT_FLTP: break;
case AV_SAMPLE_FMT_S16P: break;
default:
{
throw EXCEPTION("Unsupported frame format(%d)", frame.data->format);
}
}
frame.auAddr = task.au.addr;
frame.auSize = task.au.size;
frame.userdata = task.au.userdata;
frame.size = frame.data->nb_samples * frame.data->channels * nbps;
//LOG_NOTICE(HLE, "got audio frame (pts=0x%llx, nb_samples=%d, ch=%d, sample_rate=%d, nbps=%d)",
//frame.pts, frame.data->nb_samples, frame.data->channels, frame.data->sample_rate, nbps);
if (adec.frames.push(frame, &adec.is_closed))
{
frame.data = nullptr; // to prevent destruction
adec.cbFunc(CPU, adec.id, CELL_ADEC_MSG_TYPE_PCMOUT, CELL_OK, adec.cbArg);
}
}
}
adec.cbFunc(CPU, adec.id, CELL_ADEC_MSG_TYPE_AUDONE, task.au.auInfo_addr, adec.cbArg);
break;
}
case adecClose:
{
break;
}
default:
{
throw EXCEPTION("Unknown task(%d)", task.type);
}
}
}
adec.is_finished = true;
};
adec.adecCb->cpu_init();
adec.adecCb->state -= cpu_state::stop;
(*adec.adecCb)->lock_notify();
}
bool adecCheckType(s32 type)
{
switch (type)
@ -527,7 +558,11 @@ s32 cellAdecOpen(vm::ptr<CellAdecType> type, vm::ptr<CellAdecResource> res, vm::
return CELL_ADEC_ERROR_ARG;
}
adecOpen(*handle = idm::make<AudioDecoder>(type->audioCodecType, res->startAddr, res->totalMemSize, cb->cbFunc, cb->cbArg));
auto&& adec = std::make_shared<AudioDecoder>(type->audioCodecType, res->startAddr, res->totalMemSize, cb->cbFunc, cb->cbArg);
*handle = idm::import_existing<ppu_thread>(adec);
adec->run();
return CELL_OK;
}
@ -541,7 +576,11 @@ s32 cellAdecOpenEx(vm::ptr<CellAdecType> type, vm::ptr<CellAdecResourceEx> res,
return CELL_ADEC_ERROR_ARG;
}
adecOpen(*handle = idm::make<AudioDecoder>(type->audioCodecType, res->startAddr, res->totalMemSize, cb->cbFunc, cb->cbArg));
auto&& adec = std::make_shared<AudioDecoder>(type->audioCodecType, res->startAddr, res->totalMemSize, cb->cbFunc, cb->cbArg);
*handle = idm::import_existing<ppu_thread>(adec);
adec->run();
return CELL_OK;
}
@ -574,8 +613,7 @@ s32 cellAdecClose(u32 handle)
std::this_thread::sleep_for(1ms); // hack
}
idm::remove<PPUThread>(adec->adecCb->id);
idm::remove<AudioDecoder>(handle);
idm::remove<ppu_thread>(handle);
return CELL_OK;
}