static void gfx_ctx_vc_swap_buffers(void *data, void *data2) { #ifdef HAVE_EGL vc_ctx_data_t *vc = (vc_ctx_data_t*)data; video_frame_info_t *video_info = (video_frame_info_t*)data2; if (!vc) return; egl_swap_buffers(&vc->egl); /* Wait for vsync immediately if we don't * want egl_swap_buffers to triple-buffer */ if (video_info->max_swapchain_images <= 2) { /* We DON'T wait to wait without callback function ready! */ if (!vc->vsync_callback_set) { vc_dispmanx_vsync_callback(vc->dispman_display, dispmanx_vsync_callback, (void*)vc); vc->vsync_callback_set = true; } slock_lock(vc->vsync_condition_mutex); scond_wait(vc->vsync_condition, vc->vsync_condition_mutex); slock_unlock(vc->vsync_condition_mutex); } /* Stop generating vsync callbacks from now on */ else if (vc->vsync_callback_set) vc_dispmanx_vsync_callback(vc->dispman_display, NULL, NULL); #endif }
static void *display_thread (void *unused) { VC_DISPMANX_ALPHA_T alpha = { (DISPMANX_FLAGS_ALPHA_T)(DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS), 255 /*alpha 0->255*/ , 0 }; uint32_t vc_image_ptr; SDL_Surface *Dummy_prSDLScreen; int width, height; bool callback_registered = false; for(;;) { display_thread_busy = false; uae_u32 signal = read_comm_pipe_u32_blocking(display_pipe); display_thread_busy = true; switch(signal) { case DISPLAY_SIGNAL_SETUP: if(!callback_registered) { bcm_host_init(); dispmanxdisplay = vc_dispmanx_display_open(0); vc_dispmanx_vsync_callback(dispmanxdisplay, vsync_callback, NULL); callback_registered = true; } break; case DISPLAY_SIGNAL_SUBSHUTDOWN: if (DispManXElementpresent == 1) { DispManXElementpresent = 0; dispmanxupdate = vc_dispmanx_update_start(0); vc_dispmanx_element_remove(dispmanxupdate, dispmanxelement); vc_dispmanx_update_submit_sync(dispmanxupdate); } if (dispmanxresource_amigafb_1 != 0) { vc_dispmanx_resource_delete(dispmanxresource_amigafb_1); dispmanxresource_amigafb_1 = 0; } if (dispmanxresource_amigafb_2 != 0) { vc_dispmanx_resource_delete(dispmanxresource_amigafb_2); dispmanxresource_amigafb_2 = 0; } if(prSDLScreen != NULL) { SDL_FreeSurface(prSDLScreen); prSDLScreen = NULL; } uae_sem_post (&display_sem); break; case DISPLAY_SIGNAL_OPEN: width = display_width; height = display_height; Dummy_prSDLScreen = SDL_SetVideoMode(width, height, 16, SDL_SWSURFACE | SDL_FULLSCREEN); prSDLScreen = SDL_CreateRGBSurface(SDL_HWSURFACE, width, height, 16, Dummy_prSDLScreen->format->Rmask, Dummy_prSDLScreen->format->Gmask, Dummy_prSDLScreen->format->Bmask, Dummy_prSDLScreen->format->Amask); SDL_FreeSurface(Dummy_prSDLScreen); vc_dispmanx_display_get_info(dispmanxdisplay, &dispmanxdinfo); dispmanxresource_amigafb_1 = vc_dispmanx_resource_create(VC_IMAGE_RGB565, width, height, &vc_image_ptr); dispmanxresource_amigafb_2 = vc_dispmanx_resource_create(VC_IMAGE_RGB565, width, height, &vc_image_ptr); vc_dispmanx_rect_set(&blit_rect, 0, 0, width, height); vc_dispmanx_resource_write_data(dispmanxresource_amigafb_1, VC_IMAGE_RGB565, prSDLScreen->pitch, prSDLScreen->pixels, &blit_rect); vc_dispmanx_rect_set(&src_rect, 0, 0, width << 16, height << 16); // 16/9 to 4/3 ratio adaptation. if (currprefs.gfx_correct_aspect == 0 || screen_is_picasso) { // Fullscreen. int scaled_width = dispmanxdinfo.width * currprefs.gfx_fullscreen_ratio / 100; int scaled_height = dispmanxdinfo.height * currprefs.gfx_fullscreen_ratio / 100; vc_dispmanx_rect_set( &dst_rect, (dispmanxdinfo.width - scaled_width)/2, (dispmanxdinfo.height - scaled_height)/2, scaled_width, scaled_height); } else { // 4/3 shrink. int scaled_width = dispmanxdinfo.width * currprefs.gfx_fullscreen_ratio / 100; int scaled_height = dispmanxdinfo.height * currprefs.gfx_fullscreen_ratio / 100; vc_dispmanx_rect_set( &dst_rect, (dispmanxdinfo.width - scaled_width / 16 * 12)/2, (dispmanxdinfo.height - scaled_height)/2, scaled_width/16*12, scaled_height); } if (DispManXElementpresent == 0) { DispManXElementpresent = 1; dispmanxupdate = vc_dispmanx_update_start(0); dispmanxelement = vc_dispmanx_element_add(dispmanxupdate, dispmanxdisplay, 2, // layer &dst_rect, dispmanxresource_amigafb_1, &src_rect, DISPMANX_PROTECTION_NONE, &alpha, NULL, DISPMANX_NO_ROTATE); vc_dispmanx_update_submit(dispmanxupdate, NULL, NULL); } uae_sem_post (&display_sem); break; case DISPLAY_SIGNAL_SHOW: if (current_resource_amigafb == 1) { current_resource_amigafb = 0; vc_dispmanx_resource_write_data(dispmanxresource_amigafb_1, VC_IMAGE_RGB565, adisplays.gfxvidinfo.drawbuffer.rowbytes, adisplays.gfxvidinfo.drawbuffer.bufmem, &blit_rect); dispmanxupdate = vc_dispmanx_update_start(0); vc_dispmanx_element_change_source(dispmanxupdate, dispmanxelement, dispmanxresource_amigafb_1); } else { current_resource_amigafb = 1; vc_dispmanx_resource_write_data(dispmanxresource_amigafb_2, VC_IMAGE_RGB565, adisplays.gfxvidinfo.drawbuffer.rowbytes, adisplays.gfxvidinfo.drawbuffer.bufmem, &blit_rect); dispmanxupdate = vc_dispmanx_update_start(0); vc_dispmanx_element_change_source(dispmanxupdate, dispmanxelement, dispmanxresource_amigafb_2); } vc_dispmanx_update_submit(dispmanxupdate, NULL, NULL); break; case DISPLAY_SIGNAL_QUIT: callback_registered = false; vc_dispmanx_vsync_callback(dispmanxdisplay, NULL, NULL); vc_dispmanx_display_close(dispmanxdisplay); bcm_host_deinit(); SDL_VideoQuit(); display_tid = 0; return 0; } } }
static void *gfx_ctx_vc_init(video_frame_info_t *video_info, void *video_driver) { VC_DISPMANX_ALPHA_T alpha; EGLint n, major, minor; DISPMANX_ELEMENT_HANDLE_T dispman_element; DISPMANX_DISPLAY_HANDLE_T dispman_display; DISPMANX_UPDATE_HANDLE_T dispman_update; DISPMANX_MODEINFO_T dispman_modeinfo; VC_RECT_T dst_rect; VC_RECT_T src_rect; #ifdef HAVE_EGL static const EGLint attribute_list[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 16, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; static const EGLint context_attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; #endif settings_t *settings = config_get_ptr(); vc_ctx_data_t *vc = NULL; if (g_egl_inited) { RARCH_ERR("[VC/EGL]: Attempted to re-initialize driver.\n"); return NULL; } vc = (vc_ctx_data_t*)calloc(1, sizeof(*vc)); if (!vc) return NULL; /* If we set this env variable, Broadcom's EGL implementation will block * on vsync with a double buffer when we call eglSwapBuffers. Less input lag! * Has to be done before any EGL call. * NOTE this is commented out because it should be the right way to do it, but * currently it doesn't work, so we are using an vsync callback based solution.*/ /* if (video_info->max_swapchain_images <= 2) setenv("V3D_DOUBLE_BUFFER", "1", 1); else setenv("V3D_DOUBLE_BUFFER", "0", 1); */ bcm_host_init(); #ifdef HAVE_EGL if (!egl_init_context(&vc->egl, EGL_NONE, EGL_DEFAULT_DISPLAY, &major, &minor, &n, attribute_list)) { egl_report_error(); goto error; } if (!egl_create_context(&vc->egl, (vc_api == GFX_CTX_OPENGL_ES_API) ? context_attributes : NULL)) { egl_report_error(); goto error; } #endif /* Create an EGL window surface. */ if (graphics_get_display_size(0 /* LCD */, &vc->fb_width, &vc->fb_height) < 0) goto error; dst_rect.x = 0; dst_rect.y = 0; dst_rect.width = vc->fb_width; dst_rect.height = vc->fb_height; src_rect.x = 0; src_rect.y = 0; /* Use dispmanx upscaling if fullscreen_x * and fullscreen_y are set. */ if ((settings->uints.video_fullscreen_x != 0) && (settings->uints.video_fullscreen_y != 0)) { /* Keep input and output aspect ratio equal. * There are other aspect ratio settings which can be used to stretch video output. */ /* Calculate source and destination aspect ratios. */ float srcAspect = (float)settings->uints.video_fullscreen_x / (float)settings->uints.video_fullscreen_y; float dstAspect = (float)vc->fb_width / (float)vc->fb_height; /* If source and destination aspect ratios are not equal correct source width. */ if (srcAspect != dstAspect) src_rect.width = (unsigned)(settings->uints.video_fullscreen_y * dstAspect) << 16; else src_rect.width = settings->uints.video_fullscreen_x << 16; src_rect.height = settings->uints.video_fullscreen_y << 16; } else { src_rect.width = vc->fb_width << 16; src_rect.height = vc->fb_height << 16; } dispman_display = vc_dispmanx_display_open(0 /* LCD */); vc->dispman_display = dispman_display; vc_dispmanx_display_get_info(dispman_display, &dispman_modeinfo); dispman_update = vc_dispmanx_update_start(0); alpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS; alpha.opacity = 255; alpha.mask = 0; dispman_element = vc_dispmanx_element_add(dispman_update, dispman_display, 0 /*layer*/, &dst_rect, 0 /*src*/, &src_rect, DISPMANX_PROTECTION_NONE, &alpha, 0 /*clamp*/, DISPMANX_NO_ROTATE); vc->native_window.element = dispman_element; /* Use dispmanx upscaling if fullscreen_x and fullscreen_y are set. */ if (settings->uints.video_fullscreen_x != 0 && settings->uints.video_fullscreen_y != 0) { /* Keep input and output aspect ratio equal. * There are other aspect ratio settings which * can be used to stretch video output. */ /* Calculate source and destination aspect ratios. */ float srcAspect = (float)settings->uints.video_fullscreen_x / (float)settings->uints.video_fullscreen_y; float dstAspect = (float)vc->fb_width / (float)vc->fb_height; /* If source and destination aspect ratios are not equal correct source width. */ if (srcAspect != dstAspect) vc->native_window.width = (unsigned)(settings->uints.video_fullscreen_y * dstAspect); else vc->native_window.width = settings->uints.video_fullscreen_x; vc->native_window.height = settings->uints.video_fullscreen_y; } else { vc->native_window.width = vc->fb_width; vc->native_window.height = vc->fb_height; } vc_dispmanx_update_submit_sync(dispman_update); #ifdef HAVE_EGL if (!egl_create_surface(&vc->egl, &vc->native_window)) goto error; #endif /* For vsync after eglSwapBuffers when max_swapchain < 3 */ vc->vsync_condition = scond_new(); vc->vsync_condition_mutex = slock_new(); vc->vsync_callback_set = false; if (video_info->max_swapchain_images <= 2) { /* Start sending vsync callbacks so we can wait for vsync after eglSwapBuffers */ vc_dispmanx_vsync_callback(vc->dispman_display, dispmanx_vsync_callback, (void*)vc); vc->vsync_callback_set = true; } return vc; error: gfx_ctx_vc_destroy(video_driver); return NULL; }
static void gfx_ctx_vc_destroy(void *data) { vc_ctx_data_t *vc = (vc_ctx_data_t*)data; unsigned i; if (!vc) { g_egl_inited = false; return; } if (vc->egl.dpy) { for (i = 0; i < MAX_EGLIMAGE_TEXTURES; i++) { if (vc->eglBuffer[i] && peglDestroyImageKHR) { eglBindAPI(EGL_OPENVG_API); eglMakeCurrent(vc->egl.dpy, vc->pbuff_surf, vc->pbuff_surf, vc->eglimage_ctx); peglDestroyImageKHR(vc->egl.dpy, vc->eglBuffer[i]); } if (vc->vgimage[i]) { eglBindAPI(EGL_OPENVG_API); eglMakeCurrent(vc->egl.dpy, vc->pbuff_surf, vc->pbuff_surf, vc->eglimage_ctx); vgDestroyImage(vc->vgimage[i]); } } if (vc->egl.ctx) { gfx_ctx_vc_bind_api(data, vc_api, 0, 0); eglMakeCurrent(vc->egl.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroyContext(vc->egl.dpy, vc->egl.ctx); } if (vc->egl.hw_ctx) eglDestroyContext(vc->egl.dpy, vc->egl.hw_ctx); if (vc->eglimage_ctx) { eglBindAPI(EGL_OPENVG_API); eglMakeCurrent(vc->egl.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroyContext(vc->egl.dpy, vc->eglimage_ctx); } if (vc->egl.surf) { gfx_ctx_vc_bind_api(data, vc_api, 0, 0); eglDestroySurface(vc->egl.dpy, vc->egl.surf); } if (vc->pbuff_surf) { eglBindAPI(EGL_OPENVG_API); eglDestroySurface(vc->egl.dpy, vc->pbuff_surf); } eglBindAPI(EGL_OPENVG_API); eglMakeCurrent(vc->egl.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); gfx_ctx_vc_bind_api(data, vc_api, 0, 0); eglMakeCurrent(vc->egl.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglTerminate(vc->egl.dpy); } vc->egl.ctx = NULL; vc->egl.hw_ctx = NULL; vc->eglimage_ctx = NULL; vc->egl.surf = NULL; vc->pbuff_surf = NULL; vc->egl.dpy = NULL; vc->egl.config = 0; g_egl_inited = false; for (i = 0; i < MAX_EGLIMAGE_TEXTURES; i++) { vc->eglBuffer[i] = NULL; vc->vgimage[i] = 0; } /* Stop generating vsync callbacks if we are doing so. * Don't destroy the context while cbs are being generated! */ if (vc->vsync_callback_set) vc_dispmanx_vsync_callback(vc->dispman_display, NULL, NULL); /* Destroy mutexes and conditions. */ slock_free(vc->vsync_condition_mutex); scond_free(vc->vsync_condition); }
int main(int argc, char *argv[]) { int ret; VC_RECT_T src_rect; VC_RECT_T dst_rect; DISPMANX_UPDATE_HANDLE_T update; uint32_t vc_image_ptr; bcm_host_init(); display = vc_dispmanx_display_open( 0 ); image = calloc( 1, PITCH * HEIGHT ); // buffer 0 assert(image); // initialize image buffer with clock run in int n, m, clock = 0x275555; for (m=0; m<24; m++) { for (n=0; n<HEIGHT; n++) { ROW(n)[m] = clock&1; } clock = clock >> 1; } // fill up image with filler packets // get_packet will return filler because we haven't loaded the fifo yet. get_packet(ROW(0)+24); for (n=1; n<HEIGHT; n++) { memcpy(ROW(n)+24, ROW(0)+24, 336); } // set up some resources vc_dispmanx_rect_set( &image_rect, 0, 0, WIDTH, HEIGHT); for (n=0;n<3;n++) { resource[n] = vc_dispmanx_resource_create( TYPE, WIDTH, HEIGHT, &vc_image_ptr ); assert( resource[n] ); ret = vc_dispmanx_resource_set_palette( resource[n], palette, 0, sizeof palette ); assert( ret == 0 ); ret = vc_dispmanx_resource_write_data( resource[n], TYPE, PITCH, image, &image_rect ); assert( ret == 0 ); } vc_dispmanx_rect_set( &image_rect, OFFSET+24, 0, 336, HEIGHT); // from now on, only copy the parts that change update = vc_dispmanx_update_start( 10 ); assert( update ); vc_dispmanx_rect_set( &src_rect, 0, 0, WIDTH << 16, HEIGHT << 16 ); vc_dispmanx_rect_set( &dst_rect, 0, 0, 720, HEIGHT ); element = vc_dispmanx_element_add( update, display, 2000, &dst_rect, resource[2], &src_rect, DISPMANX_PROTECTION_NONE, NULL, NULL, VC_IMAGE_ROT0 ); ret = vc_dispmanx_update_submit_sync( update ); assert( ret == 0 ); // BUG: clear any existing callbacks, even to other apps. // https://github.com/raspberrypi/userland/issues/218 vc_dispmanx_vsync_callback(display, NULL, NULL); vc_dispmanx_vsync_callback(display, vsync, NULL); if (argc == 2 && argv[1][0] == '-') { while(read_packets()) { ; } } else { demo(); } vc_dispmanx_vsync_callback(display, NULL, NULL); // disable callback update = vc_dispmanx_update_start( 10 ); assert( update ); ret = vc_dispmanx_element_remove( update, element ); assert( ret == 0 ); ret = vc_dispmanx_update_submit_sync( update ); assert( ret == 0 ); for (n=0; n<3; n++) { ret = vc_dispmanx_resource_delete( resource[0] ); assert( ret == 0 ); } ret = vc_dispmanx_display_close( display ); assert( ret == 0 ); return 0; }