diff --git a/src/omv/common/sensor_utils.c b/src/omv/common/sensor_utils.c index 0f623e172..3c9097413 100644 --- a/src/omv/common/sensor_utils.c +++ b/src/omv/common/sensor_utils.c @@ -622,11 +622,11 @@ __weak int sensor_set_pixformat(pixformat_t pixformat) { // Set pixel format sensor.pixformat = pixformat; - // Skip the first frame. + // Reset pixel format to skip the first frame. MAIN_FB()->pixfmt = PIXFORMAT_INVALID; - // Pickout a good buffer count for the user. - framebuffer_auto_adjust_buffers(); + // Auto-adjust the number of frame buffers. + sensor_set_framebuffers(-1); // Reconfigure the hardware if needed. return sensor_config(SENSOR_CONFIG_PIXFORMAT); @@ -660,17 +660,20 @@ __weak int sensor_set_framesize(framesize_t framesize) { // Set framebuffer size sensor.framesize = framesize; - // Skip the first frame. - MAIN_FB()->pixfmt = PIXFORMAT_INVALID; - - // Set MAIN FB x offset, y offset, width, height, backup width, and backup height. + // Set x and y offsets. MAIN_FB()->x = 0; MAIN_FB()->y = 0; - MAIN_FB()->w = MAIN_FB()->u = resolution[framesize][0]; - MAIN_FB()->h = MAIN_FB()->v = resolution[framesize][1]; + // Set width and height. + MAIN_FB()->w = resolution[framesize][0]; + MAIN_FB()->h = resolution[framesize][1]; + // Set backup width and height. + MAIN_FB()->u = resolution[framesize][0]; + MAIN_FB()->v = resolution[framesize][1]; + // Reset pixel format to skip the first frame. + MAIN_FB()->pixfmt = PIXFORMAT_INVALID; - // Pickout a good buffer count for the user. - framebuffer_auto_adjust_buffers(); + // Auto-adjust the number of frame buffers. + sensor_set_framebuffers(-1); // Reconfigure the hardware if needed. return sensor_config(SENSOR_CONFIG_FRAMESIZE); @@ -731,14 +734,14 @@ __weak uint32_t sensor_get_src_bpp() { return 1; } switch (sensor.pixformat) { - case PIXFORMAT_GRAYSCALE: - return sensor.hw_flags.gs_bpp; - case PIXFORMAT_RGB565: - case PIXFORMAT_YUV422: - return 2; case PIXFORMAT_BAYER: case PIXFORMAT_JPEG: return 1; + case PIXFORMAT_RGB565: + case PIXFORMAT_YUV422: + return 2; + case PIXFORMAT_GRAYSCALE: + return sensor.hw_flags.gs_bpp; default: return 0; } @@ -774,16 +777,20 @@ __weak int sensor_set_windowing(int x, int y, int w, int h) { // Flush previous frame. framebuffer_update_jpeg_buffer(); - // Skip the first frame. - MAIN_FB()->pixfmt = PIXFORMAT_INVALID; - + // Set x and y offsets. MAIN_FB()->x = x; MAIN_FB()->y = y; - MAIN_FB()->w = MAIN_FB()->u = w; - MAIN_FB()->h = MAIN_FB()->v = h; + // Set width and height. + MAIN_FB()->w = w; + MAIN_FB()->h = h; + // Set backup width and height. + MAIN_FB()->u = w; + MAIN_FB()->v = h; + // Reset pixel format to skip the first frame. + MAIN_FB()->pixfmt = PIXFORMAT_INVALID; - // Pickout a good buffer count for the user. - framebuffer_auto_adjust_buffers(); + // Auto-adjust the number of frame buffers. + sensor_set_framebuffers(-1); // Reconfigure the hardware if needed. return sensor_config(SENSOR_CONFIG_WINDOWING); @@ -1114,6 +1121,22 @@ __weak int sensor_set_framebuffers(int count) { // Flush previous frame. framebuffer_update_jpeg_buffer(); + if (sensor.pixformat == PIXFORMAT_INVALID) { + return SENSOR_ERROR_INVALID_PIXFORMAT; + } + + if (sensor.framesize == FRAMESIZE_INVALID) { + return SENSOR_ERROR_INVALID_FRAMESIZE; + } + + uint32_t bpp = IM_MAX(sensor_get_src_bpp(), sensor_get_dst_bpp()); + #if OMV_CSI_HW_CROP_ENABLE + // If hardware cropping is supported, use window size. + MAIN_FB()->frame_size = MAIN_FB()->u * MAIN_FB()->v * bpp; + #else + // Otherwise, use the real frame size. + MAIN_FB()->frame_size = resolution[sensor.framesize][0] * resolution[sensor.framesize][1] * bpp; + #endif return framebuffer_set_buffers(count); } @@ -1279,8 +1302,8 @@ __weak int sensor_auto_crop_framebuffer() { MAIN_FB()->y -= 1; } - // Pickout a good buffer count for the user. - framebuffer_auto_adjust_buffers(); + // Auto-adjust the number of frame buffers. + sensor_set_framebuffers(-1); return 0; } diff --git a/src/omv/imlib/framebuffer.c b/src/omv/imlib/framebuffer.c index de69436da..8148af8d3 100644 --- a/src/omv/imlib/framebuffer.c +++ b/src/omv/imlib/framebuffer.c @@ -262,45 +262,28 @@ int32_t framebuffer_get_depth() { return framebuffer->bpp; } -// Returns the number of bytes the frame buffer could be at the current moment it time. -static uint32_t framebuffer_raw_buffer_size() { - uint32_t size = (uint32_t) (fb_alloc_stack_pointer() - ((char *) framebuffer->data)); - // We don't want to give all of the frame buffer RAM to the frame buffer. So, we will limit - // the maximum amount of RAM we return. - uint32_t raw_buf_size = (&_fb_memory_end - &_fb_memory_start - sizeof(framebuffer_t)); - return IM_MIN(size, raw_buf_size); +// Returns the current frame buffer size, factoring in the space taken by fb_alloc. +static uint32_t framebuffer_max_buffer_size() { + uint32_t fb_total_size = FB_ALIGN_SIZE_ROUND_DOWN(&_fb_memory_end - (char *) framebuffer->data); + uint32_t fb_avail_size = FB_ALIGN_SIZE_ROUND_DOWN(fb_alloc_stack_pointer() - (char *) framebuffer->data); + return IM_MIN(fb_total_size, fb_avail_size); } uint32_t framebuffer_get_buffer_size() { uint32_t size; if (framebuffer->n_buffers == 1) { - // With only 1 vbuffer it's fine to allow the frame buffer size to change given fb_alloc(). - size = framebuffer_raw_buffer_size(); + // With only 1 vbuffer the frame buffer size can change given fb_alloc(). + size = framebuffer_max_buffer_size(); } else { // Whatever the raw size was when the number of buffers were set is locked in. - size = framebuffer->raw_buffer_size; + size = framebuffer->buff_size; } // Remove the size of the state header plus alignment padding. size -= sizeof(vbuffer_t); - #if (OMV_CSI_HW_CROP_ENABLE == 1) - // If the frame size is set, the memory for each buffer can be reduced, - // freeing up space for fb_alloc(). Note that this can only be done if - // the camera interface supports hardware cropping, i.e., the actual - // frame size will match the specified window size. - if ((framebuffer->n_buffers != 1) && framebuffer->u && framebuffer->v) { - // Typically a framebuffer will not need more than u*v*2 bytes. - uint32_t size_guess = framebuffer->u * framebuffer->v * 2; - // Add in extra bytes to prevent round down from shrinking buffer too small. - size_guess += FRAMEBUFFER_ALIGNMENT - 1; - // Limit the frame buffer size. - size = IM_MIN(size, size_guess); - } - #endif - - // Needs to be a multiple of FRAMEBUFFER_ALIGNMENT for DMA transfers... + // Needs to be a multiple of FRAMEBUFFER_ALIGNMENT for DMA transfers. return FB_ALIGN_SIZE_ROUND_DOWN(size); } @@ -314,7 +297,7 @@ vbuffer_t *framebuffer_get_buffer(int32_t index) { void framebuffer_flush_buffers(bool fifo_flush) { if (fifo_flush) { // Drop all frame buffers. - for (int32_t i = 0; i < framebuffer->n_buffers; i++) { + for (uint32_t i = 0; i < framebuffer->n_buffers; i++) { memset(framebuffer_get_buffer(i), 0, sizeof(vbuffer_t)); } } @@ -326,24 +309,21 @@ void framebuffer_flush_buffers(bool fifo_flush) { } int framebuffer_set_buffers(int32_t n_buffers) { - uint32_t total_size = framebuffer_raw_buffer_size(); - uint32_t size = total_size / n_buffers; + uint32_t avail_size = FB_ALIGN_SIZE_ROUND_DOWN(framebuffer_max_buffer_size()); + uint32_t frame_size = FB_ALIGN_SIZE_ROUND_UP(framebuffer->frame_size + sizeof(vbuffer_t)); + uint32_t vbuff_size = (n_buffers == 1) ? avail_size : frame_size; + uint32_t vbuff_count = IM_MIN((avail_size / vbuff_size), (n_buffers == -1) ? 3 : (uint32_t) n_buffers); - // Error out if frame buffers are smaller than this... - if (size < (sizeof(vbuffer_t) + FRAMEBUFFER_ALIGNMENT)) { + if (vbuff_count == 0 || vbuff_size < sizeof(vbuffer_t)) { return -1; } - // Invalidate frame. + framebuffer->head = 0; + framebuffer->buff_size = vbuff_size; + framebuffer->n_buffers = vbuff_count; framebuffer->pixfmt = PIXFORMAT_INVALID; - // Cache the maximum size we can allocate for the frame buffer when vbuffers are greater than 1. - framebuffer->raw_buffer_size = size; - framebuffer->n_buffers = n_buffers; - framebuffer->head = 0; - framebuffer_flush_buffers(true); - return 0; } @@ -360,23 +340,6 @@ static uint32_t framebuffer_total_buffer_size() { } } -void framebuffer_auto_adjust_buffers() { - // Keep same buffer count in video fifo mode but resize buffer sizes. - if (framebuffer->n_buffers > 3) { - framebuffer_set_buffers(framebuffer->n_buffers); - return; - } - - for (int i = 3; i > 0; i--) { - framebuffer_set_buffers(i); - - // Find a buffering size automatically that doesn't use more than half. - if (fb_avail() >= framebuffer_total_buffer_size()) { - return; - } - } -} - void framebuffer_free_current_buffer() { vbuffer_t *buffer = framebuffer_get_buffer(framebuffer->head); diff --git a/src/omv/imlib/framebuffer.h b/src/omv/imlib/framebuffer.h index 66bba58fc..664ed0da2 100644 --- a/src/omv/imlib/framebuffer.h +++ b/src/omv/imlib/framebuffer.h @@ -28,8 +28,9 @@ typedef struct framebuffer { int32_t u, v; PIXFORMAT_STRUCT; int32_t streaming_enabled; - uint32_t raw_buffer_size; - int32_t n_buffers; + uint32_t buff_size; + uint32_t n_buffers; + uint32_t frame_size; int32_t head; volatile int32_t tail; bool check_head; @@ -106,12 +107,11 @@ void framebuffer_update_jpeg_buffer(); // otherwise, retain the last frame in the fifo. void framebuffer_flush_buffers(bool fifo_flush); -// Controls the number of virtual buffers in the frame buffer. +// Set the number of virtual buffers in the frame buffer. +// If n_buffers = -1 the number of virtual buffers will be set to 3 each if possible. +// If n_buffers = 1 the whole framebuffer is used. In this case, `frame_size` is ignored. int framebuffer_set_buffers(int32_t n_buffers); -// Automatically finds the best buffering size given RAM. -void framebuffer_auto_adjust_buffers(); - // Call when done with the current vbuffer to mark it as free. void framebuffer_free_current_buffer();