cellCamera: implement CELL_CAMERA_READ_DIRECT

This commit is contained in:
Megamouse 2021-10-17 13:18:45 +02:00
parent 3fac832567
commit e4b242955c
2 changed files with 102 additions and 21 deletions

View File

@ -412,7 +412,7 @@ error_code cellCameraInit()
// TODO: Some other default attributes? Need to check the actual behaviour on a real PS3.
g_camera.is_attached = true;
g_camera.send_attach_state(true);
g_camera.init = 1;
return CELL_OK;
}
@ -537,11 +537,17 @@ error_code cellCameraOpenEx(s32 dev_num, vm::ptr<CellCameraInfoEx> info)
std::lock_guard lock(g_camera.mutex);
if (info->read_mode != CELL_CAMERA_READ_DIRECT && !info->buffer)
if (info->read_mode == CELL_CAMERA_READ_FUNCCALL && !info->buffer)
{
info->buffer = vm::cast(vm::alloc(vbuf_size, vm::main));
info->bytesize = vbuf_size;
}
else if (info->read_mode && !info->pbuf[0] && !info->pbuf[1])
{
info->pbuf[0] = vm::cast(vm::alloc(vbuf_size, vm::main));
info->pbuf[1] = vm::cast(vm::alloc(vbuf_size, vm::main));
info->bytesize = vbuf_size;
}
std::tie(info->width, info->height) = get_video_resolution(*info);
@ -1357,9 +1363,7 @@ error_code cellCameraReadEx(s32 dev_num, vm::ptr<CellCameraReadEx> read)
if (!result)
{
g_camera.is_streaming = false;
g_camera.is_attached = false;
g_camera.is_open = false;
g_camera.send_attach_state(false);
return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND;
}
@ -1380,6 +1384,13 @@ error_code cellCameraReadComplete(s32 dev_num, u32 bufnum, u32 arg2)
{
cellCamera.todo("cellCameraReadComplete(dev_num=%d, bufnum=%d, arg2=%d)", dev_num, bufnum, arg2);
if (bufnum < 2)
{
auto& g_camera = g_fxo->get<camera_thread>();
std::lock_guard lock(g_camera.mutex);
g_camera.pbuf_locked[bufnum] = false;
}
return cellCameraSetAttribute(dev_num, CELL_CAMERA_READFINISH, bufnum, arg2);
}
@ -1595,15 +1606,63 @@ void camera_context::operator()()
const u64 frame_start = get_guest_system_time();
// Get latest frame with CELL_CAMERA_READ_DIRECT.
// With CELL_CAMERA_READ_FUNCCALL the game fetches the buffer in cellCameraRead.
const u64 buffer_number = pbuf_write_index;
bool send_frame_update_event = false;
bool frame_update_event_sent = false;
if (is_streaming)
{
if (read_mode.load() == CELL_CAMERA_READ_DIRECT)
{
{
std::lock_guard lock(mutex);
send_frame_update_event = info.pbuf[pbuf_write_index] && !pbuf_locked[pbuf_write_index];
}
if (handler && send_frame_update_event)
{
atomic_t<bool> wake_up = false;
bool result = false;
Emu.CallAfter([&]()
{
u32 width{};
u32 height{};
u64 frame_number{};
u64 bytes_read{};
result = handler->get_image(info.pbuf[pbuf_write_index].get_ptr(), info.bytesize, width, height, frame_number, bytes_read);
wake_up = true;
wake_up.notify_one();
});
while (!wake_up && !Emu.IsStopped())
{
thread_ctrl::wait_on(wake_up, false);
}
pbuf_write_index = pbuf_next_index();
if (!result)
{
send_attach_state(false);
}
}
}
else
{
send_frame_update_event = true;
}
}
std::unique_lock lock(mutex_notify_data_map);
for (auto const& notify_data_entry : notify_data_map)
for (const auto& [key, evt_data] : notify_data_map)
{
const auto& key = notify_data_entry.first;
const auto& evt_data = notify_data_entry.second;
// handle FRAME_UPDATE
if (is_streaming && evt_data.flag & CELL_CAMERA_EFLAG_FRAME_UPDATE)
if (send_frame_update_event && evt_data.flag & CELL_CAMERA_EFLAG_FRAME_UPDATE)
{
if (auto queue = lv2_event_queue::find(key))
{
@ -1613,26 +1672,33 @@ void camera_context::operator()()
if (read_mode.load() == CELL_CAMERA_READ_DIRECT)
{
const u64 image_data_size = static_cast<u64>(info.bytesize);
const u64 buffer_number = 0;
const u64 camera_id = 0;
data2 = image_data_size << 32 | buffer_number << 16 | camera_id;
data3 = get_guest_system_time() - start_timestamp; // timestamp
data3 = get_guest_system_time() - start_timestamp; // timestamp
}
else // CELL_CAMERA_READ_FUNCCALL, also default
{
data2 = 0; // device id (always 0)
data3 = 0; // unused
data2 = 0; // device id (always 0)
data3 = 0; // unused
}
if (queue->send(evt_data.source, CELL_CAMERA_FRAME_UPDATE, data2, data3) == 0) [[likely]]
if (queue->send(evt_data.source, CELL_CAMERA_FRAME_UPDATE, data2, data3) == CELL_OK) [[likely]]
{
++frame_num;
}
frame_update_event_sent = true;
}
}
}
if (read_mode.load() == CELL_CAMERA_READ_DIRECT && frame_update_event_sent)
{
std::lock_guard lock(mutex);
pbuf_locked[buffer_number] = true;
}
lock.unlock();
for (const u64 frame_target_time = 1000000u / fps;;)
@ -1654,23 +1720,29 @@ void camera_context::reset_state()
is_open = false;
info.framerate = 0;
std::memset(&attr, 0, sizeof(attr));
handler.reset();
pbuf_write_index = 0;
pbuf_locked[0] = false;
pbuf_locked[1] = false;
std::scoped_lock lock(mutex_notify_data_map);
notify_data_map.clear();
handler.reset();
}
void camera_context::send_attach_state(bool attached)
{
if (!attached)
{
is_streaming = false;
is_open = false;
}
std::lock_guard lock(mutex_notify_data_map);
if (!notify_data_map.empty())
{
for (auto const& notify_data_entry : notify_data_map)
for (const auto& [key, evt_data] : notify_data_map)
{
const auto& key = notify_data_entry.first;
const auto& evt_data = notify_data_entry.second;
if (auto queue = lv2_event_queue::find(key))
{
if (queue->send(evt_data.source, attached ? CELL_CAMERA_ATTACH : CELL_CAMERA_DETACH, 0, 0) == 0) [[likely]]
@ -1739,3 +1811,9 @@ void camera_context::remove_queue(u64 key)
notify_data_map.erase(key);
}
}
u32 camera_context::pbuf_next_index() const
{
// The read buffer index cannot be the same as the write index
return (pbuf_write_index + 1u) % 2;
}

View File

@ -426,13 +426,16 @@ public:
atomic_t<bool> is_open{false};
CellCameraInfoEx info{};
atomic_t<u32> pbuf_write_index = 0;
std::array<atomic_t<bool>, 2> pbuf_locked = { false, false };
u32 pbuf_next_index() const;
struct attr_t
{
u32 v1, v2;
};
attr_t attr[500]{};
atomic_t<u32> frame_num;
atomic_t<u32> frame_num = 0;
atomic_t<u32> init = 0;