From 2592c72cb59802607b97da562d4ccf369599d6b2 Mon Sep 17 00:00:00 2001 From: vanfanel Date: Sat, 21 Mar 2015 14:17:59 +0100 Subject: RASPBERRYPI: Rewrote buffering code of the dispmanx graphics driver for stability. --- .../graphics/dispmanxsdl/dispmanxsdl-graphics.cpp | 130 ++++++--------------- .../graphics/dispmanxsdl/dispmanxsdl-graphics.h | 2 - 2 files changed, 35 insertions(+), 97 deletions(-) (limited to 'backends/graphics') diff --git a/backends/graphics/dispmanxsdl/dispmanxsdl-graphics.cpp b/backends/graphics/dispmanxsdl/dispmanxsdl-graphics.cpp index 08379d2a34..b3d03f6336 100644 --- a/backends/graphics/dispmanxsdl/dispmanxsdl-graphics.cpp +++ b/backends/graphics/dispmanxsdl/dispmanxsdl-graphics.cpp @@ -40,7 +40,6 @@ struct dispvarsStruct { DISPMANX_DISPLAY_HANDLE_T display; DISPMANX_MODEINFO_T amode; DISPMANX_UPDATE_HANDLE_T update; - DISPMANX_RESOURCE_HANDLE_T resources[numpages]; DISPMANX_ELEMENT_HANDLE_T element; VC_IMAGE_TYPE_T pixFormat; VC_DISPMANX_ALPHA_T *alpha; @@ -50,26 +49,19 @@ struct dispvarsStruct { uint vcImagePtr; uint screen; uint pitch; - uint flipPage; bool aspectRatioCorrection; void *pixmem; struct dispmanxPage *pages; - struct dispmanxPage *currentPage; - int pageflipPending; + struct dispmanxPage *nextPage; + bool pageflipPending; pthread_cond_t vsyncCondition; pthread_mutex_t pendingMutex; - - // Mutex to isolate the vsync condition signaling - pthread_mutex_t vsyncCondMutex; }; struct dispmanxPage { - unsigned int numpage; - struct dispvarsStruct *dispvars; - pthread_mutex_t pageUseMutex; - bool used; + DISPMANX_RESOURCE_HANDLE_T resource; }; DispmanXSdlGraphicsManager::DispmanXSdlGraphicsManager(SdlEventSource *sdlEventSource) @@ -89,21 +81,12 @@ void DispmanXSdlGraphicsManager::DispmanXInit () { _dispvars->vcImagePtr = 0; _dispvars->pages = (struct dispmanxPage*)calloc(numpages, sizeof(struct dispmanxPage)); _dispvars->pageflipPending = 0; - _dispvars->currentPage = NULL; + _dispvars->nextPage = &_dispvars->pages[0]; // Initialize mutex and condition variable objects pthread_mutex_init(&_dispvars->pendingMutex, NULL); - pthread_mutex_init(&_dispvars->vsyncCondMutex, NULL); pthread_cond_init(&_dispvars->vsyncCondition, NULL); - int i; - for (i = 0; i < numpages; i++) { - _dispvars->pages[i].numpage = i; - _dispvars->pages[i].used = false; - _dispvars->pages[i].dispvars = _dispvars; - pthread_mutex_init(&_dispvars->pages[i].pageUseMutex, NULL); - } - // Before we call any vc_* function, we need to call this one. bcm_host_init(); @@ -144,101 +127,64 @@ void DispmanXSdlGraphicsManager::DispmanXSetup (int width, int height, int bpp) vc_dispmanx_rect_set(&(_dispvars->bmpRect), 0, 0, width, height); vc_dispmanx_rect_set(&(_dispvars->srcRect), 0, 0, width << 16, height << 16); - // We create the resources for multiple buffering int i; for (i = 0; i < numpages; i++) - _dispvars->resources[i] = vc_dispmanx_resource_create(_dispvars->pixFormat, width, height, + _dispvars->pages[i].resource = vc_dispmanx_resource_create(_dispvars->pixFormat, width, height, &(_dispvars->vcImagePtr)); // Add element _dispvars->update = vc_dispmanx_update_start(0); _dispvars->element = vc_dispmanx_element_add(_dispvars->update, _dispvars->display, 0, - &(_dispvars->dstRect), _dispvars->resources[0], &(_dispvars->srcRect), + &(_dispvars->dstRect), _dispvars->pages[0].resource, &(_dispvars->srcRect), DISPMANX_PROTECTION_NONE, _dispvars->alpha, 0, (DISPMANX_TRANSFORM_T)0); vc_dispmanx_update_submit_sync(_dispvars->update); } -// Find a free page, and return the page. If no free page is available when called, wait for a page flip. -struct dispmanxPage *DispmanXSdlGraphicsManager::DispmanXGetFreePage() -{ - struct dispmanxPage *page = NULL; - unsigned i; - while (page == NULL) { - // Try to find a free page - for (i = 0; i < numpages; ++i) { - if (!_dispvars->pages[i].used) { - page = (_dispvars->pages) + i; - break; - } - } - // If no page is free ATM, wait until a free page is freed by vsync CB - if (page == NULL) { - pthread_mutex_lock(&_dispvars->vsyncCondMutex); - pthread_cond_wait(&_dispvars->vsyncCondition, &_dispvars->vsyncCondMutex); - pthread_mutex_unlock(&_dispvars->vsyncCondMutex); - } - } - pthread_mutex_lock(&page->pageUseMutex); - page->used = true; - pthread_mutex_unlock(&page->pageUseMutex); - return page; -} +void DispmanXVSyncCallback (DISPMANX_UPDATE_HANDLE_T u, void * arg){ + struct dispvarsStruct *_dispvars = (struct dispvarsStruct*) arg; -void DispmanXSdlGraphicsManager::DispmanXUpdate() { - // Triple buffer update function - struct dispmanxPage *page; - page = DispmanXGetFreePage(); + // Changing the page to write must be done before the signaling + // so we have the right page in nextPage when update_main continues + if (_dispvars->nextPage == &_dispvars->pages[0]) + _dispvars->nextPage = &_dispvars->pages[1]; + else + _dispvars->nextPage = &_dispvars->pages[0]; + + // These two things must be isolated "atomically" to avoid getting + // a false positive in the pending_mutex test in update_main. + pthread_mutex_lock(&_dispvars->pendingMutex); - // Frame blitting - vc_dispmanx_resource_write_data(_dispvars->resources[page->numpage], _dispvars->pixFormat, - _dispvars->pitch, _dispvars->pixmem, &(_dispvars->bmpRect)); + pthread_cond_signal(&_dispvars->vsyncCondition); + _dispvars->pageflipPending = false; - // Page flipping: we send the page to the dispmanx API internal flipping FIFO stack. - DispmanXFlip(page); + pthread_mutex_unlock(&_dispvars->pendingMutex); + } -void DispmanXVSyncCallback (DISPMANX_UPDATE_HANDLE_T u, void * arg){ - struct dispmanxPage *page = (struct dispmanxPage *) arg; - - // We signal the vsync condition, just in case we're waiting for it somewhere (no free pages, etc) - pthread_mutex_lock(&page->dispvars->vsyncCondMutex); - pthread_cond_signal(&page->dispvars->vsyncCondition); - pthread_mutex_unlock(&page->dispvars->vsyncCondMutex); +void DispmanXSdlGraphicsManager::DispmanXUpdate() { + pthread_mutex_lock(&_dispvars->pendingMutex); - pthread_mutex_lock(&page->dispvars->pendingMutex); - page->dispvars->pageflipPending--; - pthread_mutex_unlock(&page->dispvars->pendingMutex); - - // We mark as free the page that was visible until now - if (page->dispvars->currentPage != NULL) { - pthread_mutex_lock(&page->dispvars->currentPage->pageUseMutex); - page->dispvars->currentPage->used = false; - pthread_mutex_unlock(&page->dispvars->currentPage->pageUseMutex); + if (_dispvars->pageflipPending) { + pthread_cond_wait(&_dispvars->vsyncCondition, &_dispvars->pendingMutex); } - // The page on which we just issued the flip that caused this callback becomes the visible one - page->dispvars->currentPage = page; -} - -void DispmanXSdlGraphicsManager::DispmanXFlip (struct dispmanxPage *page) { - // We don't queue multiple page flips because dispmanx doesn't support it. - if (_dispvars->pageflipPending > 0) { - pthread_mutex_lock(&_dispvars->vsyncCondMutex); - pthread_cond_wait(&_dispvars->vsyncCondition, &_dispvars->vsyncCondMutex); - pthread_mutex_unlock(&_dispvars->vsyncCondMutex); - } + pthread_mutex_unlock(&_dispvars->pendingMutex); + // Frame blitting + vc_dispmanx_resource_write_data(_dispvars->nextPage->resource, _dispvars->pixFormat, + _dispvars->pitch, _dispvars->pixmem, &(_dispvars->bmpRect)); + // Issue a page flip at the next vblank interval (will be done at vsync anyway). _dispvars->update = vc_dispmanx_update_start(0); vc_dispmanx_element_change_source(_dispvars->update, _dispvars->element, - _dispvars->resources[page->numpage]); - vc_dispmanx_update_submit(_dispvars->update, &DispmanXVSyncCallback, page); + _dispvars->nextPage->resource); + vc_dispmanx_update_submit(_dispvars->update, &DispmanXVSyncCallback, _dispvars); pthread_mutex_lock(&_dispvars->pendingMutex); - _dispvars->pageflipPending++; + _dispvars->pageflipPending = true; pthread_mutex_unlock(&_dispvars->pendingMutex); } @@ -247,7 +193,7 @@ void DispmanXSdlGraphicsManager::DispmanXFreeResources(void) { _dispvars->update = vc_dispmanx_update_start(0); for (i = 0; i < numpages; i++) - vc_dispmanx_resource_delete(_dispvars->resources[i]); + vc_dispmanx_resource_delete(_dispvars->pages[i].resource); vc_dispmanx_element_remove(_dispvars->update, _dispvars->element); @@ -262,14 +208,8 @@ void DispmanXSdlGraphicsManager::DispmanXVideoQuit() { // Destroy mutexes and conditions pthread_mutex_destroy(&_dispvars->pendingMutex); - pthread_mutex_destroy(&_dispvars->vsyncCondMutex); pthread_cond_destroy(&_dispvars->vsyncCondition); - int i; - for (i = 0; i < numpages; i++) { - pthread_mutex_destroy(&_dispvars->pages[i].pageUseMutex); - } - free(_dispvars->pages); } diff --git a/backends/graphics/dispmanxsdl/dispmanxsdl-graphics.h b/backends/graphics/dispmanxsdl/dispmanxsdl-graphics.h index b10a158ef3..23a091fd04 100644 --- a/backends/graphics/dispmanxsdl/dispmanxsdl-graphics.h +++ b/backends/graphics/dispmanxsdl/dispmanxsdl-graphics.h @@ -44,8 +44,6 @@ protected: void DispmanXSetup(int width, int height, int bpp); void DispmanXInit(); void DispmanXUpdate(); - void DispmanXFlip(struct dispmanxPage *page); - struct dispmanxPage *DispmanXGetFreePage(); void DispmanXFreeResources(); void DispmanXVideoQuit(); struct dispvarsStruct *_dispvars; -- cgit v1.2.3