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

Update the reader's tail to its position if no data is mapped #50

Merged
merged 2 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions acquire-video-runtime/src/runtime/channel.c
Original file line number Diff line number Diff line change
Expand Up @@ -191,18 +191,17 @@ channel_read_map(struct channel* self, struct channel_reader* reader)
reader->cycle = *cycle + 1;
}

// Even if nothing is available on the channel, we still need to advance
// this reader's position & cycle bookmarks to the position & cycle of the
// writer's head. Normally this would happen in channel_read_unmap(), but
// because no data is available, we do not set the reader's state to Mapped
// here. A call to channel_read_unmap() would return early and not advance
// the reader's bookmarks in that case, so we need to do it here.
if (!nbytes) {
goto AdvanceToWriterHead;
// If nothing is available to read, we still need to advance this
// reader's position & cycle bookmarks to the beginning of the queue and
// the writer's cycle, respectively.
out = 0;
*pos = 0;
*cycle = self->cycle;
} else {
reader->state = ChannelState_Mapped;
}

reader->state = ChannelState_Mapped;

Finalize:
lock_release(&self->lock);
return (struct slice){ .beg = out, .end = out + nbytes };
Expand Down
1 change: 1 addition & 0 deletions acquire-video-runtime/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ else ()
filter-video-average
repeat-start-no-monitor
aligned-videoframe-pointers
sleep-while-inspecting
)

foreach (name ${tests})
Expand Down
178 changes: 178 additions & 0 deletions acquire-video-runtime/tests/sleep-while-inspecting.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/// @file sleep-while-inspecting.cpp
/// Test that uninspected regions will not be overwritten by the runtime.

#include "acquire.h"
#include "device/hal/device.manager.h"
#include "device/props/components.h"
#include "platform.h"
#include "logger.h"

#include <cstdio>
#include <stdexcept>

void
reporter(int is_error,
const char* file,
int line,
const char* function,
const char* msg)
{
fprintf(is_error ? stderr : stdout,
"%s%s(%d) - %s: %s\n",
is_error ? "ERROR " : "",
file,
line,
function,
msg);
}

static size_t
bytes_of_frame(const VideoFrame* frame)
{
return sizeof(*frame) + bytes_of_image(&frame->shape);
}

/// Helper for passing size static strings as function args.
/// For a function: `f(char*,size_t)` use `f(SIZED("hello"))`.
/// Expands to `f("hello",5)`.
#define SIZED(str) str, sizeof(str)

#define L (aq_logger)
#define LOG(...) L(0, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
#define ERR(...) L(1, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
#define EXPECT(e, ...) \
do { \
if (!(e)) { \
char buf[1 << 8] = { 0 }; \
ERR(__VA_ARGS__); \
snprintf(buf, sizeof(buf) - 1, __VA_ARGS__); \
throw std::runtime_error(buf); \
} \
} while (0)
#define CHECK(e) EXPECT(e, "Expression evaluated as false: %s", #e)
#define DEVOK(e) CHECK(Device_Ok == (e))
#define OK(e) CHECK(AcquireStatus_Ok == (e))

void
configure(AcquireRuntime* runtime)
{
CHECK(runtime);

const DeviceManager* dm = acquire_device_manager(runtime);
CHECK(dm);

AcquireProperties props = {};
OK(acquire_get_configuration(runtime, &props));

DEVOK(device_manager_select(dm,
DeviceKind_Camera,
SIZED("simulated.*empty.*") - 1,
&props.video[0].camera.identifier));
DEVOK(device_manager_select(dm,
DeviceKind_Storage,
SIZED("tiff") - 1,
&props.video[0].storage.identifier));

storage_properties_init(
&props.video[0].storage.settings, 0, SIZED("out.tif"), 0, 0, { 0 }, 0);

OK(acquire_configure(runtime, &props));

AcquirePropertyMetadata metadata = { 0 };
OK(acquire_get_configuration_metadata(runtime, &metadata));

props.video[0].camera.settings.binning = 1;
props.video[0].camera.settings.pixel_type = SampleType_u12;
props.video[0].camera.settings.shape = {
.x = 8192,
.y = 8192,
};
props.video[0].max_frame_count = 20;
props.video[0].camera.settings.exposure_time_us = 1e5;

OK(acquire_configure(runtime, &props));
storage_properties_destroy(&props.video[0].storage.settings);
}

void
acquire(AcquireRuntime* runtime)
{
CHECK(runtime);

AcquireProperties props = {};
OK(acquire_get_configuration(runtime, &props));

const auto next = [](VideoFrame* cur) -> VideoFrame* {
return (VideoFrame*)(((uint8_t*)cur) + bytes_of_frame(cur));
};

const auto consumed_bytes = [](const VideoFrame* const cur,
const VideoFrame* const end) -> size_t {
return (uint8_t*)end - (uint8_t*)cur;
};

struct clock clock = {};
// expected time to acquire frames + 100%
static double time_limit_ms =
(props.video[0].max_frame_count / 6.0) * 1000.0 * 2.0;
clock_init(&clock);
clock_shift_ms(&clock, time_limit_ms);
OK(acquire_start(runtime));
{
uint64_t nframes = 0;
while (nframes < props.video[0].max_frame_count) {
struct clock throttle
{};
clock_init(&throttle);
EXPECT(clock_cmp_now(&clock) < 0,
"Timeout at %f ms",
clock_toc_ms(&clock) + time_limit_ms);
VideoFrame *beg, *end, *cur;
OK(acquire_map_read(runtime, 0, &beg, &end));
for (cur = beg; cur < end; cur = next(cur)) {
LOG("stream %d counting frame w id %d", 0, cur->frame_id);
CHECK(cur->shape.dims.width ==
props.video[0].camera.settings.shape.x);
CHECK(cur->shape.dims.height ==
props.video[0].camera.settings.shape.y);
++nframes;
}
{
uint32_t n = (uint32_t)consumed_bytes(beg, end);
OK(acquire_unmap_read(runtime, 0, n));
if (n)
LOG("stream %d consumed bytes %d", 0, n);
}
clock_sleep_ms(&throttle, 1000.0f);

LOG("stream %d nframes %d. remaining time %f s",
0,
nframes,
-1e-3 * clock_toc_ms(&clock));
}

CHECK(nframes == props.video[0].max_frame_count);
}

OK(acquire_stop(runtime));
}

int
main()
{
int retval = 1;
AcquireRuntime* runtime = acquire_init(reporter);

try {
configure(runtime);
acquire(runtime);
retval = 0;
} catch (const std::exception& e) {
ERR("%s", e.what());
} catch (...) {
ERR("unknown error");
}

acquire_shutdown(runtime);
return retval;
}
Loading