Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix simulated camera stalling #47

Merged
merged 8 commits into from
Jul 30, 2024
Merged
115 changes: 85 additions & 30 deletions acquire-driver-common/src/simcams/simulated.camera.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,14 @@ struct SimulatedCamera

struct
{
void* data;
void* frame_data; // for storing the image
void* render_data; // for rendering the image
struct ImageShape shape;
struct lock lock;
int64_t frame_id;
int64_t last_emitted_frame_id;
struct condition_variable frame_ready;
uint8_t frame_wanted;
} im;

struct
Expand Down Expand Up @@ -192,6 +194,19 @@ compute_strides(struct ImageShape* shape)
st[i] = st[i - 1] * dims[i - 1];
}

static void*
checked_realloc(void* in, size_t nbytes)
{
void* out = realloc(in, nbytes);
EXPECT(out, "Allocation of %llu bytes failed.", nbytes);

Finalize:
return out;
Error:
free(in);
goto Finalize;
}

static void
compute_full_resolution_shape_and_offset(const struct SimulatedCamera* self,
struct ImageShape* shape,
Expand All @@ -217,20 +232,38 @@ simulated_camera_streamer_thread(struct SimulatedCamera* self)
{
clock_init(&self->streamer.throttle);

int64_t frame_id = self->im.frame_id;
while (self->streamer.is_running) {
struct ImageShape full = { 0 };
uint32_t origin[2] = { 0, 0 };

ECHO(lock_acquire(&self->im.lock));
while (self->properties.input_triggers.frame_start.enable &&
!self->software_trigger.triggered) {
ECHO(condition_variable_wait(&self->software_trigger.trigger_ready,
&self->im.lock));
}
self->software_trigger.triggered = 0;

// compute the full resolution shape and offset
ECHO(compute_full_resolution_shape_and_offset(self, &full, origin));

const float exposure_time_ms =
self->properties.exposure_time_us * 1e-3f;
ECHO(lock_release(&self->im.lock));

clock_tic(&self->streamer.throttle);

// generate the image
switch (self->kind) {
case BasicDevice_Camera_Random:
im_fill_rand(&full, self->im.data);
im_fill_rand(&full, self->im.render_data);
break;
case BasicDevice_Camera_Sin:
ECHO(im_fill_pattern(
&full, (float)origin[0], (float)origin[1], self->im.data));
ECHO(im_fill_pattern(&full,
(float)origin[0],
(float)origin[1],
self->im.render_data));
break;
case BasicDevice_Camera_Empty:
break; // do nothing
Expand All @@ -239,35 +272,44 @@ simulated_camera_streamer_thread(struct SimulatedCamera* self)
"Unexpected index for the kind of simulated camera. Got: %d",
self->kind);
}
{

// apply binning if applicable
if (self->properties.binning > 1) {
nclack marked this conversation as resolved.
Show resolved Hide resolved
int w = full.dims.width;
int h = full.dims.height;
int b = self->properties.binning >> 1;
while (b) {
ECHO(bin2(self->im.data, w, h));
ECHO(bin2(self->im.render_data, w, h));
b >>= 1;
w >>= 1;
h >>= 1;
}
}

if (self->properties.input_triggers.frame_start.enable) {
while (!self->software_trigger.triggered) {
ECHO(condition_variable_wait(
&self->software_trigger.trigger_ready, &self->im.lock));
}
self->software_trigger.triggered = 0;
++frame_id;

// sleep for the remainder of the exposure time
float toc = (float)clock_toc_ms(&self->streamer.throttle);
if (self->streamer.is_running) {
clock_sleep_ms(&self->streamer.throttle, exposure_time_ms - toc);
}

self->hardware_timestamp = clock_tic(0);
++self->im.frame_id;
if (self->im.frame_wanted) {
ECHO(lock_acquire(&self->im.lock));

ECHO(condition_variable_notify_all(&self->im.frame_ready));
ECHO(lock_release(&self->im.lock));
{
void* const tmp = self->im.frame_data;
self->im.frame_data = self->im.render_data;
self->im.render_data = tmp;
}

self->hardware_timestamp = clock_tic(0);
self->im.frame_id = frame_id;
self->im.frame_wanted = 0;

if (self->streamer.is_running)
clock_sleep_ms(&self->streamer.throttle,
self->properties.exposure_time_us * 1e-3f);
ECHO(condition_variable_notify_all(&self->im.frame_ready));
ECHO(lock_release(&self->im.lock));
}
}
}

Expand Down Expand Up @@ -335,16 +377,20 @@ simcam_set(struct Camera* camera, struct CameraProperties* settings)
if (!settings->binning)
settings->binning = 1;

EXPECT(popcount_u8(settings->binning) == 1,
"Binning must be a power of two. Got %d.",
settings->binning);
if (popcount_u8(settings->binning) != 1) {
LOGE("Binning must be a power of two. Got %d.", settings->binning);
return Device_Err;
}

if (self->properties.input_triggers.frame_start.enable &&
!settings->input_triggers.frame_start.enable) {
// fire if disabling the software trigger while live
simcam_execute_trigger(camera);
}

enum DeviceStatusCode status = Device_Ok;

ECHO(lock_acquire(&self->im.lock));
self->properties = *settings;
self->properties.pixel_type = settings->pixel_type;
self->properties.input_triggers = (struct camera_properties_input_triggers_s){
Expand Down Expand Up @@ -376,12 +422,15 @@ simcam_set(struct Camera* camera, struct CameraProperties* settings)
};

size_t nbytes = aligned_bytes_of_image(shape);
self->im.data = malloc(nbytes);
EXPECT(self->im.data, "Allocation of %llu bytes failed.", nbytes);
CHECK(self->im.frame_data = checked_realloc(self->im.frame_data, nbytes));
CHECK(self->im.render_data = checked_realloc(self->im.render_data, nbytes));

return Device_Ok;
Finalize:
lock_release(&self->im.lock);
return status;
Error:
return Device_Err;
status = Device_Err;
goto Finalize;
}

static enum DeviceStatusCode
Expand Down Expand Up @@ -426,6 +475,7 @@ simcam_execute_trigger(struct Camera* camera)
containerof(camera, struct SimulatedCamera, camera);

lock_acquire(&self->im.lock);
self->im.frame_wanted = 1;
self->software_trigger.triggered = 1;
condition_variable_notify_all(&self->software_trigger.trigger_ready);
lock_release(&self->im.lock);
Expand Down Expand Up @@ -464,6 +514,8 @@ simcam_get_frame(struct Camera* camera,
self->im.last_emitted_frame_id,
self->im.frame_id);
ECHO(lock_acquire(&self->im.lock));
self->im.frame_wanted = 1;

while (self->streamer.is_running &&
self->im.last_emitted_frame_id >= self->im.frame_id) {
ECHO(condition_variable_wait(&self->im.frame_ready, &self->im.lock));
Expand All @@ -473,7 +525,7 @@ simcam_get_frame(struct Camera* camera,
goto Shutdown;
}

memcpy(im, self->im.data, bytes_of_image(&self->im.shape)); // NOLINT
memcpy(im, self->im.frame_data, bytes_of_image(&self->im.shape)); // NOLINT
info_out->shape = self->im.shape;
info_out->hardware_frame_id = self->im.frame_id;
info_out->hardware_timestamp = self->hardware_timestamp;
Expand All @@ -491,8 +543,9 @@ simcam_close_camera(struct Camera* camera_)
containerof(camera_, struct SimulatedCamera, camera);
EXPECT(camera_, "Invalid NULL parameter");
simcam_stop(&camera->camera);
if (camera->im.data)
free(camera->im.data);

free(camera->im.frame_data);
free(camera->im.render_data);
free(camera);
return Device_Ok;
Error:
Expand Down Expand Up @@ -521,7 +574,8 @@ simcam_make_camera(enum BasicDeviceKind kind)
.properties = properties,
.kind=kind,
.im={
.data=0,
.frame_data=0,
.render_data=0,
.shape = {
.dims = {
.channels = 1,
Expand All @@ -537,6 +591,7 @@ simcam_make_camera(enum BasicDeviceKind kind)
},
.type=properties.pixel_type
},
.frame_wanted = 0,
},
.camera={
.state = DeviceState_AwaitingConfiguration,
Expand Down
1 change: 1 addition & 0 deletions acquire-driver-common/tests/integration/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ else ()
can-set-with-file-uri
configure-triggering
list-digital-lines
simcam-will-not-stall
software-trigger-acquires-single-frames
switch-storage-identifier
write-side-by-side-tiff
Expand Down
Loading
Loading