diff --git a/gfx/drivers/dispmanx_gfx.c b/gfx/drivers/dispmanx_gfx.c index 30a6b44b89..e277161283 100644 --- a/gfx/drivers/dispmanx_gfx.c +++ b/gfx/drivers/dispmanx_gfx.c @@ -21,11 +21,6 @@ #include "../video_context_driver.h" #include "../font_renderer_driver.h" -#include -#include -#include -#include - #include #include @@ -33,342 +28,354 @@ #include "config.h" #endif -#define NUMPAGES 2 +/* We have two "drawing surfaces" (abstract), one for running + * core and other for menu, each one backed by a dispmanx + * element and a set of buffers (resources in dispmanx terms). */ +enum dispmanx_surface_type { + MAIN_SURFACE, + MENU_SURFACE, + BACK_SURFACE +}; + +struct dispmanx_page +{ + /* Each page contains it's own resource handler + * instead of pointing to in by page number */ + DISPMANX_RESOURCE_HANDLE_T resource; + bool used; + /* Each page has it's own mutex for + * isolating it's used flag access. */ + slock_t *page_used_mutex; + + /* This field will allow us to access the + * main _dispvars struct from the vsync CB function */ + struct dispmanx_video *dispvars; +}; + +struct dispmanx_surface +{ + /* main surface has 3 pages, menu surface has 1 */ + unsigned int numpages; + struct dispmanx_page *pages; + unsigned int bpp; + + VC_RECT_T src_rect; + VC_RECT_T dst_rect; + VC_RECT_T bmp_rect; + + /* Each surface has it's own element, and the resources are contained one in each page */ + DISPMANX_ELEMENT_HANDLE_T element; + VC_DISPMANX_ALPHA_T alpha; + VC_IMAGE_TYPE_T pixformat; + + /* Surfaces with a higher layer will be on top of the ones with lower. Default is 0. */ + int layer; + + /* Internal frame dimensions */ + int width; + int height; + int pitch; + + /* External aspect for scaling */ + float aspect; +}; struct dispmanx_video { uint64_t frame_count; DISPMANX_DISPLAY_HANDLE_T display; - DISPMANX_MODEINFO_T amode; DISPMANX_UPDATE_HANDLE_T update; - DISPMANX_ELEMENT_HANDLE_T element; - VC_IMAGE_TYPE_T pixFormat; - VC_DISPMANX_ALPHA_T *alpha; - VC_RECT_T srcRect; - VC_RECT_T dstRect; - VC_RECT_T bmpRect; - unsigned int vcImagePtr; - unsigned int screen; - unsigned int pitch; - unsigned int flipPage; + uint32_t vc_image_ptr; + + struct dispmanx_page *current_page; - /* Internal frame dimensions */ - unsigned int width; - unsigned int height; - unsigned int bytes_per_pixel; - /* Some cores render things we don't need between scanlines */ - unsigned int visible_width; - - bool aspectRatioCorrection; - void *pixmem; - - struct dispmanx_page *pages; - struct dispmanx_page *currentPage; - struct dispmanx_page *nextPage; - bool pageflip_pending; + /* We abstract three "surfaces": main surface, menu surface and black back surface. */ + struct dispmanx_surface surfaces[3]; /* For console blanking */ - int fd; - char *fbp; + int fb_fd; + uint8_t *fb_addr; unsigned int screensize; - char *screen_bck; + uint8_t *screen_bck; + + /* Total dispmanx video dimensions. Not counting overscan settings. */ + unsigned int dispmanx_width; + unsigned int dispmanx_height; /* For threading */ scond_t *vsync_condition; + slock_t *vsync_cond_mutex; slock_t *pending_mutex; + unsigned int pageflip_pending; /* Menu */ bool menu_active; - DISPMANX_ELEMENT_HANDLE_T menu_element; - DISPMANX_RESOURCE_HANDLE_T menu_resource; - VC_IMAGE_TYPE_T menu_pixFormat; - VC_DISPMANX_ALPHA_T *menu_alpha; - VC_RECT_T menu_srcRect; - VC_RECT_T menu_dstRect; - VC_RECT_T menu_bmpRect; - unsigned int menu_width; - unsigned int menu_height; - unsigned int menu_pitch; - unsigned int menu_flip_page; - /* External aspect ratio */ - float aspect; + unsigned int numsurfaces; }; -struct dispmanx_page -{ - DISPMANX_RESOURCE_HANDLE_T resource; -}; - -static void dispmanx_blank_console(void *data) -{ - struct fb_var_screeninfo vinfo; - unsigned int fb_bytes_per_pixel; +/* If no free page is available when called, wait for a page flip. */ +static struct dispmanx_page *dispmanx_get_free_page(void *data, struct dispmanx_surface *surface) { + unsigned i; struct dispmanx_video *_dispvars = data; + struct dispmanx_page *page = NULL; - if (!_dispvars) - return; + while (!page) + { + /* Try to find a free page */ + for (i = 0; i < surface->numpages; ++i) { + if (!surface->pages[i].used) + { + page = (surface->pages) + i; + break; + } + } - _dispvars->fd = open("/dev/fb0", O_RDWR); + /* If no page is free at the moment, + * wait until a free page is freed by vsync CB. */ + if (!page) { + slock_lock(_dispvars->vsync_cond_mutex); + scond_wait(_dispvars->vsync_condition, _dispvars->vsync_cond_mutex); + slock_unlock(_dispvars->vsync_cond_mutex); + } + } + + /* We mark the choosen page as used */ + slock_lock(page->page_used_mutex); + page->used = true; + slock_unlock(page->page_used_mutex); - /* We need this just to know the framebuffer - * color depth, which vc_get_display_info() doesn't provide. */ - ioctl(_dispvars->fd, FBIOGET_VSCREENINFO, &vinfo); - fb_bytes_per_pixel = vinfo.bits_per_pixel / 8; - - _dispvars->screensize = _dispvars->amode.width * _dispvars->amode.height * fb_bytes_per_pixel; - _dispvars->fbp = (char *)mmap(0, _dispvars->screensize, PROT_READ | PROT_WRITE, MAP_SHARED, _dispvars->fd, 0); - - /* Disable cursor blinking */ - system("setterm -cursor off"); - - /* Backup console screen contents */ - _dispvars->screen_bck = (char*)malloc(_dispvars->screensize * sizeof(char)); - - if (!_dispvars->screen_bck) - goto end; - - memcpy((char*)_dispvars->screen_bck, (char*)_dispvars->fbp, _dispvars->screensize); - - /* Blank console */ - memset((char*)(_dispvars->fbp), 0x00, _dispvars->screensize); - -end: - /* Unmap and close */ - munmap(&_dispvars->fd, _dispvars->screensize); - close (_dispvars->fd); -} - -static void dispmanx_restore_console(void *data) -{ - struct dispmanx_video *_dispvars = data; - - if (!_dispvars) - return; - - _dispvars->fd = open("/dev/fb0", O_RDWR); - _dispvars->fbp = (char *)mmap(0, _dispvars->screensize, PROT_READ | PROT_WRITE, MAP_SHARED, _dispvars->fd, 0); - - /* Restore console screen contents. */ - memcpy((char*)_dispvars->fbp, (char*)_dispvars->screen_bck, _dispvars->screensize); - free(_dispvars->screen_bck); - - /* Unmap and close. */ - munmap(&_dispvars->fd, _dispvars->screensize); - close (_dispvars->fd); - - /* Restore cursor blinking. */ - system("setterm -cursor on"); + return page; } static void dispmanx_vsync_callback(DISPMANX_UPDATE_HANDLE_T u, void *data) { - struct dispmanx_video *_dispvars = data; + struct dispmanx_page *page = data; - /* Changing the page to write must be done before the signaling - * so we have the right page in nextPage when update_main continues */ + /* Marking the page as free must be done before the signaling + * so when update_main continues (it won't continue until we signal) + * we can chose this page as free */ + if (page->dispvars->current_page) { + slock_lock(page->dispvars->current_page->page_used_mutex); - if (_dispvars->nextPage == &_dispvars->pages[0]) - _dispvars->nextPage = &_dispvars->pages[1]; - else - _dispvars->nextPage = &_dispvars->pages[0]; + /* We mark as free the page that was visible until now */ + page->dispvars->current_page->used = false; + + slock_unlock(page->dispvars->current_page->page_used_mutex); + } + + /* The page on which we issued the flip that + * caused this callback becomes the visible one */ + page->dispvars->current_page = page; /* These two things must be isolated "atomically" to avoid getting * a false positive in the pending_mutex test in update_main. */ - slock_lock(_dispvars->pending_mutex); + slock_lock(page->dispvars->pending_mutex); - _dispvars->pageflip_pending = false; - scond_signal(_dispvars->vsync_condition); + page->dispvars->pageflip_pending--; + scond_signal(page->dispvars->vsync_condition); - slock_unlock(_dispvars->pending_mutex); + slock_unlock(page->dispvars->pending_mutex); } -static void dispmanx_free_main_resources(void *data) +static void dispmanx_surface_free_resources(void *data, struct dispmanx_surface *surface) { int i; struct dispmanx_video *_dispvars = data; - - if (!_dispvars) - return; - + _dispvars->update = vc_dispmanx_update_start(0); - - for (i = 0; i < NUMPAGES; i++) - vc_dispmanx_resource_delete(_dispvars->pages[i].resource); - - vc_dispmanx_element_remove(_dispvars->update, _dispvars->element); + + for (i = 0; i < surface->numpages; i++) { + if (surface->pages[i].resource != 0){ + vc_dispmanx_resource_delete(surface->pages[i].resource); + } + } + + vc_dispmanx_element_remove(_dispvars->update, surface->element); vc_dispmanx_update_submit_sync(_dispvars->update); } -static bool dispmanx_setup_scale(void *data, unsigned width, - unsigned height, unsigned pitch) +static bool dispmanx_surface_setup(void *data, int width, int height, int pitch, float aspect, + struct dispmanx_surface *surface) { - int i, dst_ypos; - VC_DISPMANX_ALPHA_T layerAlpha; struct dispmanx_video *_dispvars = data; - settings_t *settings = config_get_ptr(); + int i, dst_width, dst_height, dst_xpos, dst_ypos; - if (!_dispvars) - return false; - - /* Since internal frame resolution seems to have changed, we change the - * internal frame resolution in use, and call dispmanx_setup_scale() - * again to set the rects, etc. */ - _dispvars->width = width; - _dispvars->height = height; - - /* Total pitch, including things the cores - * render between "visible" scanlines. */ - _dispvars->pitch = pitch; - - /* The "visible" width obtained from the core pitch. */ - _dispvars->visible_width = pitch / _dispvars->bytes_per_pixel; - - dispmanx_free_main_resources(_dispvars); - vc_dispmanx_display_get_info(_dispvars->display, &(_dispvars->amode)); - - /* We choose the pixel format depending on the bpp of the frame. */ - switch (_dispvars->bytes_per_pixel) - { - case 2: - _dispvars->pixFormat = VC_IMAGE_RGB565; - break; - case 4: - _dispvars->pixFormat = VC_IMAGE_XRGB8888; - break; - default: - RARCH_ERR("video_dispmanx: wrong pixel format\n"); - return NULL; - } - - /* Transparency disabled */ - layerAlpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS; - layerAlpha.opacity = 255; - layerAlpha.mask = 0; - _dispvars->alpha = &layerAlpha; - - switch (settings->video.aspect_ratio_idx) - { - case ASPECT_RATIO_4_3: - _dispvars->aspect = (float)4 / (float)3; - break; - case ASPECT_RATIO_16_9: - _dispvars->aspect = (float)16 / (float)9; - break; - case ASPECT_RATIO_16_10: - _dispvars->aspect = (float)16 / (float)10; - break; - case ASPECT_RATIO_16_15: - _dispvars->aspect = (float)16 / (float)15; - break; - case ASPECT_RATIO_CORE: - _dispvars->aspect = (float)_dispvars->width / (float)_dispvars->height; - break; - default: - _dispvars->aspect = (float)_dispvars->width / (float)_dispvars->height; - break; - } - - int dst_width = _dispvars->amode.height * _dispvars->aspect; - + /* Internal frame dimensions. Pitch is total pitch including info + * between scanlines */ + surface->width = width; + surface->height = height; + surface->pitch = pitch; + surface->aspect = aspect; + + /* The "visible" width obtained from the core pitch. We blit based on + * the "visible" width, for cores with things between scanlines. */ + int visible_width = pitch / surface->bpp; + + dst_width = _dispvars->dispmanx_height * aspect; + dst_height = _dispvars->dispmanx_height; + /* If we obtain a scaled image width that is bigger than the physical screen width, * then we keep the physical screen width as our maximun width. */ -#if 0 - if (dst_width > _dispvars->amode.width) - dst_width = _dispvars->amode.width; -#endif - dst_ypos = (_dispvars->amode.width - dst_width) / 2; - - vc_dispmanx_rect_set(&(_dispvars->dstRect), dst_ypos, 0, - dst_width, _dispvars->amode.height); + if (dst_width > _dispvars->dispmanx_width) + dst_width = _dispvars->dispmanx_width; + + dst_xpos = (_dispvars->dispmanx_width - dst_width) / 2; + dst_ypos = (_dispvars->dispmanx_height - dst_height) / 2; /* We configure the rects now. */ - vc_dispmanx_rect_set(&(_dispvars->bmpRect), 0, 0, _dispvars->width, _dispvars->height); - vc_dispmanx_rect_set(&(_dispvars->srcRect), 0, 0, _dispvars->width << 16, _dispvars->height << 16); - - /* We create as many resources as pages */ - for (i = 0; i < NUMPAGES; i++) - _dispvars->pages[i].resource = vc_dispmanx_resource_create(_dispvars->pixFormat, - _dispvars->visible_width, _dispvars->height, &(_dispvars->vcImagePtr)); + vc_dispmanx_rect_set(&surface->dst_rect, dst_xpos, dst_ypos, dst_width, dst_height); + vc_dispmanx_rect_set(&surface->bmp_rect, 0, 0, width, height); + vc_dispmanx_rect_set(&surface->src_rect, 0, 0, width << 16, height << 16); + for (i = 0; i < surface->numpages; i++) { + surface->pages[i].resource = + vc_dispmanx_resource_create(surface->pixformat, + visible_width, height, &(_dispvars->vc_image_ptr)); + } /* Add element. */ _dispvars->update = vc_dispmanx_update_start(0); - _dispvars->element = vc_dispmanx_element_add(_dispvars->update, _dispvars->display, 0, - &(_dispvars->dstRect), _dispvars->pages[0].resource, &(_dispvars->srcRect), - DISPMANX_PROTECTION_NONE, _dispvars->alpha, 0, (DISPMANX_TRANSFORM_T)0); + surface->element = vc_dispmanx_element_add( + _dispvars->update,_dispvars->display, surface->layer, + &surface->dst_rect, surface->pages[0].resource, + &surface->src_rect, DISPMANX_PROTECTION_NONE, + &surface->alpha, 0, (DISPMANX_TRANSFORM_T)0); vc_dispmanx_update_submit_sync(_dispvars->update); return true; } -static void dispmanx_update_main(void *data, const void *frame) +static void dispmanx_surface_init(void *data, + int bpp, + int pixformat, + int layer, + unsigned alpha, + int numpages, + struct dispmanx_surface *surface) { struct dispmanx_video *_dispvars = data; + int i; + + /* Setup surface parameters */ + surface->numpages = numpages; + surface->bpp = bpp; + surface->layer = layer; + surface->pixformat = pixformat; - /* Wait until last issued flip completes to get the free page - * that the vsync cb will put in nextPage. Also, dispmanx doesn't - * support issuing more than one pageflip.*/ + /* Internal frame dimensions. We leave them as 0 until we get + * to the gfx_frame function where we get image width and height. */ + surface->width = 0; + surface->height = 0; + surface->pitch = 0; + + /* Transparency disabled */ + surface->alpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS; + surface->alpha.opacity = alpha; + surface->alpha.mask = 0; + + /* Allocate memory for all the pages in each surface + * and initialize variables inside each page's struct. */ + surface->pages = calloc(surface->numpages, sizeof(struct dispmanx_surface)); + for (i = 0; i < surface->numpages; i++) { + surface->pages[i].used = false; + surface->pages[i].dispvars = _dispvars; + surface->pages[i].page_used_mutex = slock_new(); + } +} + +static void dispmanx_surface_update(void *data, const void *frame, struct dispmanx_surface *surface) +{ + struct dispmanx_video *_dispvars = data; + struct dispmanx_page *page = NULL; + + /* Wait until last issued flip completes to get a free page. Also, + dispmanx doesn't support issuing more than one pageflip.*/ slock_lock(_dispvars->pending_mutex); - if (_dispvars->pageflip_pending) + if (_dispvars->pageflip_pending > 0) { scond_wait(_dispvars->vsync_condition, _dispvars->pending_mutex); } slock_unlock(_dispvars->pending_mutex); - + + page = dispmanx_get_free_page(_dispvars, surface); + /* Frame blitting */ - vc_dispmanx_resource_write_data(_dispvars->nextPage->resource, _dispvars->pixFormat, - _dispvars->pitch, (void *)frame, &(_dispvars->bmpRect)); - + vc_dispmanx_resource_write_data(page->resource, surface->pixformat, + surface->pitch, (void*)frame, &(surface->bmp_rect)); + /* Issue a page flip that will be done at the next vsync. */ _dispvars->update = vc_dispmanx_update_start(0); - vc_dispmanx_element_change_source(_dispvars->update, _dispvars->element, - _dispvars->nextPage->resource); + vc_dispmanx_element_change_source(_dispvars->update, surface->element, + page->resource); - vc_dispmanx_update_submit(_dispvars->update, dispmanx_vsync_callback, (void*)_dispvars); + vc_dispmanx_update_submit(_dispvars->update, dispmanx_vsync_callback, (void*)page); slock_lock(_dispvars->pending_mutex); - _dispvars->pageflip_pending = true; + _dispvars->pageflip_pending++; slock_unlock(_dispvars->pending_mutex); } +static void dispmanx_blank_console (void *data) +{ + /* Note that a 2-pixels array is needed to accomplish console blanking because with 1-pixel + * only the write data function doesn't work well, so when we do the only resource + * change in the surface update function, we will be seeing a distorted console. */ + struct dispmanx_video *_dispvars = data; + uint16_t image[2] = {0x0000, 0x0000}; + float aspect = (float)_dispvars->dispmanx_width / (float)_dispvars->dispmanx_height; + + dispmanx_surface_init(_dispvars, + 1, + VC_IMAGE_RGB565, + -1, + 255, + 1, + &_dispvars->surfaces[BACK_SURFACE]); + dispmanx_surface_setup(_dispvars, 2, 2, 4, aspect, &_dispvars->surfaces[BACK_SURFACE]); + dispmanx_surface_update(_dispvars, &image, &_dispvars->surfaces[BACK_SURFACE]); +} + static void *dispmanx_gfx_init(const video_info_t *video, const input_driver_t **input, void **input_data) { - int i; struct dispmanx_video *_dispvars = calloc(1, sizeof(struct dispmanx_video)); - if (!_dispvars) - return NULL; + bcm_host_init(); + _dispvars->display = vc_dispmanx_display_open(0 /* LCD */); + /* If the console framebuffer has active overscan settings, + * the user must have overscan_scale=1 in config.txt to have + * the same size for both fb console and dispmanx. */ + graphics_get_display_size(_dispvars->display, &_dispvars->dispmanx_width, &_dispvars->dispmanx_height); - _dispvars->bytes_per_pixel = video->rgb32 ? 4 : 2; - _dispvars->screen = 0; - _dispvars->vcImagePtr = 0; - _dispvars->pageflip_pending = false; - _dispvars->currentPage = NULL; - _dispvars->pages = calloc(NUMPAGES, sizeof(struct dispmanx_page)); + /* Setup surface parameters */ + _dispvars->numsurfaces = 3; + + _dispvars->vc_image_ptr = 0; + _dispvars->pageflip_pending = 0; + _dispvars->current_page = NULL; _dispvars->menu_active = false; - _dispvars->nextPage = &_dispvars->pages[0]; - - if (!_dispvars->pages) - { - free(_dispvars); - return NULL; - } - + /* Initialize the rest of the mutexes and conditions. */ _dispvars->vsync_condition = scond_new(); + _dispvars->vsync_cond_mutex = slock_new(); _dispvars->pending_mutex = slock_new(); - bcm_host_init(); - - _dispvars->display = vc_dispmanx_display_open(_dispvars->screen); + dispmanx_surface_init(_dispvars, + video->rgb32 ? 4 : 2, + video->rgb32 ? VC_IMAGE_XRGB8888 : VC_IMAGE_RGB565, + 0 /* layer */, + 255 /* alpha */, + 3, /* numpages */ + &_dispvars->surfaces[MAIN_SURFACE]); if (input && input_data) *input = NULL; - + + dispmanx_blank_console(_dispvars); + return _dispvars; } @@ -377,142 +384,72 @@ static bool dispmanx_gfx_frame(void *data, const void *frame, unsigned width, { struct dispmanx_video *_dispvars = data; - /* Check if neither menu nor core framebuffer is to be displayed. */ - if (!_dispvars->menu_active && !frame) - return true; - - if (width != _dispvars->width || height != _dispvars->height) + if (width != _dispvars->surfaces[MAIN_SURFACE].width || height != _dispvars->surfaces[MAIN_SURFACE].height) { /* Sanity check. */ if (width == 0 || height == 0) return true; - - RARCH_LOG("video_dispmanx: internal frame resolution changed by core\n"); - - if (!dispmanx_setup_scale(_dispvars, width, height, pitch)) - { - RARCH_ERR("video_dispmanx: frame resolution set failed\n"); - return false; - } - - dispmanx_blank_console (_dispvars); + + dispmanx_surface_free_resources(_dispvars, &_dispvars->surfaces[MAIN_SURFACE]); + float aspect = video_driver_get_aspect_ratio(); + /* Reconfiguring internal dimensions of the main surface is needed. */ + dispmanx_surface_setup(_dispvars, width, height, pitch, aspect, &_dispvars->surfaces[MAIN_SURFACE]); } - + if (_dispvars->menu_active) { - char buf[128] = {0}; + char buf[128]; video_monitor_get_fps(buf, sizeof(buf), NULL, 0); } - /* Update main game screen: locate free page, blit and flip. */ - dispmanx_update_main(_dispvars, frame); - + /* Update main surface: locate free page, blit and flip. */ + dispmanx_surface_update(_dispvars, frame, &_dispvars->surfaces[MAIN_SURFACE]); _dispvars->frame_count++; - return true; } -static void dispmanx_free_menu_resources (void *data) -{ - struct dispmanx_video *_dispvars = data; - - if (!_dispvars) - return; - - _dispvars->update = vc_dispmanx_update_start(0); - - vc_dispmanx_resource_delete(_dispvars->menu_resource); - vc_dispmanx_element_remove(_dispvars->update, _dispvars->menu_element); - - vc_dispmanx_update_submit_sync(_dispvars->update); - - _dispvars->menu_width = 0; - _dispvars->menu_height = 0; -} - static void dispmanx_set_texture_enable(void *data, bool state, bool full_screen) { struct dispmanx_video *_dispvars = data; - - if (_dispvars) - _dispvars->menu_active = state; - if (!_dispvars->menu_active) - dispmanx_free_menu_resources(_dispvars); + + /* If it was active but it's not anymore... */ + if (!state && _dispvars->menu_active) { + dispmanx_surface_free_resources(_dispvars, &_dispvars->surfaces[MENU_SURFACE]); + /* This is needed so we enter thru surface_setup on + * set_texture_frame() next time menu is active. */ + _dispvars->surfaces[MENU_SURFACE].width = 0; + _dispvars->surfaces[MENU_SURFACE].height = 0; + } + _dispvars->menu_active = state; } static void dispmanx_set_texture_frame(void *data, const void *frame, bool rgb32, - unsigned width, unsigned height, float alpha) + unsigned width, unsigned height, float alpha) { struct dispmanx_video *_dispvars = data; - - if (!_dispvars) - return; - + /* If we're entering the menu in this frame, * we must setup rects, resources and menu element. */ - if (width != _dispvars->menu_width || height != _dispvars->menu_height) + if (width != _dispvars->surfaces[MENU_SURFACE].width || height != _dispvars->surfaces[MENU_SURFACE].height) { - int i, dst_width, dst_ypos; - VC_DISPMANX_ALPHA_T layerAlpha; - /* Sanity check */ if (width == 0 || height == 0) return; + int pitch = width * (rgb32 ? 4 : 2); + + dispmanx_surface_init(_dispvars, + rgb32 ? 4 : 2, + VC_IMAGE_RGBA16, + 0 /* layer */, + 210 /* alpha, hardcoded */, + 3, /* numpages */ + &_dispvars->surfaces[MENU_SURFACE]); - _dispvars->menu_width = width; - _dispvars->menu_height = height; - - _dispvars->menu_pitch = width * (rgb32 ? 4 : 2); - - _dispvars->menu_pixFormat = VC_IMAGE_RGBA16; - _dispvars->menu_flip_page = 0; - - /* Menu transparency has a fixed value for now. */ - layerAlpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS; -#if 0 - layerAlpha.opacity = (unsigned char)(255.0f * alpha); -#endif - layerAlpha.opacity = 210; - layerAlpha.mask = 0; - _dispvars->menu_alpha = &layerAlpha; - - dst_width = _dispvars->amode.height * _dispvars->aspect; - - /* If we obtain a scaled image width that is - * bigger than the physical screen width, - * then we keep the physical screen width as our maximun width. */ -#if 0 - if (dst_width > _dispvars->amode.width) - dst_width = _dispvars->amode.width; -#endif - dst_ypos = (_dispvars->amode.width - dst_width) / 2; - vc_dispmanx_rect_set(&(_dispvars->menu_dstRect), dst_ypos, 0, - dst_width, _dispvars->amode.height); - - /* We configure the rects now. */ - vc_dispmanx_rect_set(&(_dispvars->menu_bmpRect), 0, 0, _dispvars->menu_width, _dispvars->menu_height); - vc_dispmanx_rect_set(&(_dispvars->menu_srcRect), 0, 0, _dispvars->menu_width << 16, _dispvars->menu_height << 16); - - /* Create resource for the menu element. */ - _dispvars->menu_resource = vc_dispmanx_resource_create(_dispvars->menu_pixFormat, - _dispvars->menu_width, _dispvars->menu_height, &(_dispvars->vcImagePtr)); - - /* Add the menu element. */ - _dispvars->update = vc_dispmanx_update_start(0); - - _dispvars->menu_element = vc_dispmanx_element_add(_dispvars->update, _dispvars->display, 0, - &(_dispvars->menu_dstRect), _dispvars->menu_resource, &(_dispvars->menu_srcRect), - DISPMANX_PROTECTION_NONE, _dispvars->menu_alpha, 0, (DISPMANX_TRANSFORM_T)0); - - vc_dispmanx_update_submit_sync(_dispvars->update); + dispmanx_surface_setup(_dispvars, width, height, pitch, + _dispvars->surfaces[MAIN_SURFACE].aspect, + &_dispvars->surfaces[MENU_SURFACE]); } - - /* Frame blitting. */ - vc_dispmanx_resource_write_data(_dispvars->menu_resource, - _dispvars->menu_pixFormat, _dispvars->menu_pitch, (void *)frame, &(_dispvars->menu_bmpRect)); - - /* We don't need to flip buffers: one is enough for menu. We sync in gfx_frame, on the - * dispmanx_update_main() call, that is called with menu active, too. */ + dispmanx_surface_update(_dispvars, frame, &_dispvars->surfaces[MENU_SURFACE]); } static void dispmanx_gfx_set_nonblock_state(void *data, bool state) @@ -546,8 +483,8 @@ static void dispmanx_gfx_viewport_info(void *data, struct video_viewport *vp) vp->x = vp->y = 0; - vp->width = vp->full_width = vid->width; - vp->height = vp->full_height = vid->height; + vp->width = vp->full_width = vid->surfaces[MAIN_SURFACE].width; + vp->height = vp->full_height = vid->surfaces[MAIN_SURFACE].height; } static bool dispmanx_gfx_suppress_screensaver(void *data, bool enable) @@ -592,11 +529,26 @@ static bool dispmanx_gfx_read_viewport(void *data, uint8_t *buffer) static uint64_t dispmanx_gfx_get_frame_count(void *data) { struct dispmanx_video *_dispvars = data; - if (!_dispvars) - return 0; return _dispvars->frame_count; } +static void dispmanx_set_aspect_ratio (void *data, unsigned aspect_ratio_idx) +{ + struct dispmanx_video *_dispvars = data; + struct dispmanx_surface *surface; + /* Here we obtain the new aspect ratio. */ + + float aspect = aspectratio_lut[aspect_ratio_idx].value; + + surface = &_dispvars->surfaces[MAIN_SURFACE]; + dispmanx_surface_free_resources(_dispvars, surface); + dispmanx_surface_setup(_dispvars, surface->width, surface->height, surface->pitch, aspect, surface); + + surface = &_dispvars->surfaces[MENU_SURFACE]; + dispmanx_surface_free_resources(_dispvars, surface); + dispmanx_surface_setup(_dispvars, surface->width, surface->height, surface->pitch, aspect, surface); +} + static const video_poke_interface_t dispmanx_poke_interface = { dispmanx_gfx_get_frame_count, NULL, /* set_video_mode */ @@ -606,7 +558,7 @@ static const video_poke_interface_t dispmanx_poke_interface = { NULL, /* get_video_output_next */ NULL, /* get_current_framebuffer */ NULL, /* get_proc_address */ - NULL, /* dispmanx_set_aspect_ratio */ + dispmanx_set_aspect_ratio, NULL, /* dispmanx_apply_state_changes */ #ifdef HAVE_MENU dispmanx_set_texture_frame, @@ -623,19 +575,27 @@ static void dispmanx_gfx_get_poke_interface(void *data, *iface = &dispmanx_poke_interface; } -static void dispmanx_gfx_free(void *data) +static void dispmanx_surface_free_pages (struct dispmanx_surface *surface) { int i; + for (i = 0; i < surface->numpages; i++) { + surface->pages[i].used = false; + slock_free(surface->pages[i].page_used_mutex); + } + free(surface->pages); +} + +static void dispmanx_gfx_free(void *data) +{ struct dispmanx_video *_dispvars = data; - if (!_dispvars) - return; + dispmanx_surface_free_resources(_dispvars, &_dispvars->surfaces[MENU_SURFACE]); + dispmanx_surface_free_resources(_dispvars, &_dispvars->surfaces[MAIN_SURFACE]); + dispmanx_surface_free_resources(_dispvars, &_dispvars->surfaces[BACK_SURFACE]); - /* Free menu resurces if we are in menu when exiting. */ - if (_dispvars->menu_active) - dispmanx_free_menu_resources(_dispvars); - - dispmanx_free_main_resources(_dispvars); + dispmanx_surface_free_pages(&_dispvars->surfaces[MAIN_SURFACE]); + dispmanx_surface_free_pages(&_dispvars->surfaces[MENU_SURFACE]); + dispmanx_surface_free_pages(&_dispvars->surfaces[BACK_SURFACE]); /* Close display and deinitialize. */ vc_dispmanx_display_close(_dispvars->display); @@ -643,34 +603,31 @@ static void dispmanx_gfx_free(void *data) /* Destroy mutexes and conditions. */ slock_free(_dispvars->pending_mutex); + slock_free(_dispvars->vsync_cond_mutex); scond_free(_dispvars->vsync_condition); - if (_dispvars->pages) - free (_dispvars->pages); - _dispvars->pages = NULL; - - dispmanx_restore_console(_dispvars); + free(_dispvars); } video_driver_t video_dispmanx = { - dispmanx_gfx_init, - dispmanx_gfx_frame, - dispmanx_gfx_set_nonblock_state, - dispmanx_gfx_alive, - dispmanx_gfx_focus, - dispmanx_gfx_suppress_screensaver, - dispmanx_gfx_has_windowed, - dispmanx_gfx_set_shader, - dispmanx_gfx_free, - "dispmanx", + dispmanx_gfx_init, + dispmanx_gfx_frame, + dispmanx_gfx_set_nonblock_state, + dispmanx_gfx_alive, + dispmanx_gfx_focus, + dispmanx_gfx_suppress_screensaver, + dispmanx_gfx_has_windowed, + dispmanx_gfx_set_shader, + dispmanx_gfx_free, + "dispmanx", NULL, /* set_viewport */ - dispmanx_gfx_set_rotation, - dispmanx_gfx_viewport_info, - dispmanx_gfx_read_viewport, + dispmanx_gfx_set_rotation, + dispmanx_gfx_viewport_info, + dispmanx_gfx_read_viewport, NULL, /* read_frame_raw */ -#ifdef HAVE_OVERLAY - NULL, /* overlay_interface */ -#endif - dispmanx_gfx_get_poke_interface + #ifdef HAVE_OVERLAY + NULL, /* overlay_interface */ + #endif + dispmanx_gfx_get_poke_interface };