static SDL_Surface *DISPMANX_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags) { //MAC Recuerda que aqui,originalmente, nos llegaban las dimensiones de un modo de video // aproximado en SDL_Video.c de entre los modos de video disponibles. AHORA YA NO. //Ahora lo que hacemos es que nos lleguen directamente la altura y anchura del modo //en el que quiere correr la aplicacion, //Luego se escala ese modo, de cuanta menos resolucion mejor, (ya que hay //que hacer una escritura de ram a grafica en la funcion FlipHWScreen), al modo fisico, que //es en realidad el unico modo grafico que existe, el modo en que hemos arrancado. //Esto explica por que creamos el plano de overlay a parte, //ya que cuando SDL_Video.c llama a SetVideoMode aun no se tienen listos los //offsets horizontal y vertical donde empieza el modo de video pequenio //(el modo en que corre la app internamente) sobre el grande (el modo fisico). //Si nos pasan width=0 y height=0, interpreto que el programa no quiere video sino //que solo necesita entrar en modo grafico, asi que salto alli: if ((width == 0) | (height == 0)) goto go_video_console; //MAC Inicializamos el SOC (bcm_host_init) SOLO si no hemos pasado antes por aqui. Lo mismo con el fondo. //Si ya hemos pasado antes, hacemos limpieza, pero dejamos el fondo sin tocar. if (dispvars->pixmem != NULL){ //Hacemos limpieza de resources, pero dejamos el fondo. No hay problema porque solo lo ponemos //si no hemos pasado por aqui antes. DISPMANX_FreeResources(); } else { uint32_t screen = 0; bcm_host_init(); //MAC Abrimos el display dispmanx printf("Dispmanx: Opening display %i\n", screen ); dispvars->display = vc_dispmanx_display_open( screen ); //MAC Recuperamos algunos datos de la configuracion del buffer actual vc_dispmanx_display_get_info( dispvars->display, &(dispvars->amode)); printf( "Dispmanx: Physical video mode is %d x %d\n", dispvars->amode.width, dispvars->amode.height ); //Ponemos el element de fondo negro tanto si se respeta el ratio como si no, //porque si no, se nos veraa la consola al cambiar de resolucion durante el programa. DISPMANX_BlankBackground(); } //-------Bloque de lista de resoluciones, originalmente en VideoInit-------------- //Para la aplicacion SDL, el unico modo de video disponible va a ser siempre el que pida. DISPMANX_AddMode(this, width, height, (((bpp+7)/8)-1)); //--------------------------------------------------------------------------------- Uint32 Rmask; Uint32 Gmask; Uint32 Bmask; //dispvars->pitch = width * ((bpp+7) /8); //MAC Establecemos el pitch en funcion del bpp deseado //Lo alineamos a 16 porque es el aligment interno de dispmanx(en ejemp) dispvars->bits_per_pixel = bpp; dispvars->pitch = ( ALIGN_UP( width, 16 ) * (bpp/8) ); //Alineamos la atura a 16 por el mismo motivo (ver ejemplo hello_disp) height = ALIGN_UP( height, 16); switch (bpp){ case 8: dispvars->pix_format = VC_IMAGE_8BPP; break; case 16: dispvars->pix_format = VC_IMAGE_RGB565; break; case 32: dispvars->pix_format = VC_IMAGE_XRGB8888; break; default: printf ("\n[ERROR] - wrong bpp: %d\n",bpp); return (NULL); } //MAC blah this->UpdateRects = DISPMANX_DirectUpdate; printf ("\nUsing internal program mode: %d x %d %d bpp", width, height, dispvars->bits_per_pixel); //MAC Por ahora en DISPMANX usamos el mismo modo q ya esta establecido printf ("\nUsing physical mode: %d x %d %d bpp", dispvars->amode.width, dispvars->amode.height, dispvars->bits_per_pixel); //----------------------------------------------------------------------------- //Esta parte no es fundamental, solo sirve para conservar el ratio del juego. //Si no se hace y simplemente quitas estas lineas, se estira al modo fisico y ya, //quedando la imagen deformada si es de 4:3 en una tele de 16:9, que es lo que pasaba antes. //Simplemente hallamos ese ratio y con el hallamos la nueva anchura, considerando //como altura la maxima fisica que tenemos establecida, o sea, la altura del modo fisico establecido. //Tambien se calcula la posicion horizontal en que debe empezar el rect de destino (dst_ypos), //para que no quede pegado a la parte izquierda de la pantalla al ser menor que la resolucion fisica, que //obviamente no cambia. //Queda obsoleto si cambiamos la resolucion a una que tenga el mismo ratio que el modo original del juego. dispvars->ignore_ratio = (int) SDL_getenv("SDL_DISPMANX_IGNORE_RATIO"); if (dispvars->ignore_ratio) vc_dispmanx_rect_set( &(dispvars->dst_rect), 0, 0, dispvars->amode.width , dispvars->amode.height ); else { float orig_ratio = ((float)width / (float)height); int dst_width = dispvars->amode.height * orig_ratio; //Si la anchura de la imagen escalada nos sale mayor que el ancho fisico de pantalla, //mantenemos el ancho fisico de pantalla como anchura maxima. if (dst_width > dispvars->amode.width) dst_width = dispvars->amode.width; int dst_ypos = (dispvars->amode.width - dst_width) / 2; printf ("\nUsing proportion ratio: %d / %d = %f", width, height, orig_ratio); printf ("\nProgram rect, respecting original ratio: %d x %d \n", dst_width, dispvars->amode.height); vc_dispmanx_rect_set( &(dispvars->dst_rect), dst_ypos, 0, dst_width , dispvars->amode.height ); } //---------------------------Dejamos configurados los rects--------------------- //Recuerda que los rects NO contienen ninguna informacion visual, solo son tamanio, rectangulos //descritos para que los entiendan las funciones vc, solo tamanios de areas. // //bmp_rect: se usa solo para el volcado del buffer en RAM al resource que toque. Define el tamanio //del area a copiar de RAM (pixmem) al resource (dispmam->resources[]) usando write_data(), por //eso, y para acabarlo de entender del todo, su altura y anchura son las internas del juego, width y height. // //src_rect y dst_rect: se usan porque un element necesita dos rects definidos: src_rect es el tamanio del area //de entrada,o sea, el tamanio con el que clipeamos la imagen de origen, y dst_rect es el tamanio del area de //salida, o sea, el tamanio con que se vera, escalada por hardware, en el element. // //Por todo esto, src_rect tendra generalmente la altura y anchura de la imagen original, o dicho de otro //modo la altura y anchura que usa el juego internamente (width << 16 y height << 16 por algun rollo de //tamanio de variable), y dst_rect tendra las dimensiones del area de pantalla a la que queremos escalar //esa imagen: si le damos las dimensiones fisicas totales de la pantalla, escalara sin respetar el ratio. //Asique lo he corregido manteniendo la altura maxima de la pantalla fisica, y calculando la anchura //a partir de dicha altura y el ratio de la imagen (de la resolucion del juego) original. // //Debes pensar siempre de la siguiente manera: un element, que es como un cristal-lupa, un resource //(aunque tengas dos, en un momento dado el element solo tiene uno) que es como la imagen original, //muy pequenita al fondo, y un "embudo", cuyo tamanio del extremo inferior pegado a la imagen original //es de tamanio src_rect, y cuyo tamanio del extremo superior, pegado al element, es de tamanio dst_rect. vc_dispmanx_rect_set (&(dispvars->bmp_rect), 0, 0, width, height); vc_dispmanx_rect_set (&(dispvars->src_rect), 0, 0, width << 16, height << 16); //------------------------------------------------------------------------------ //MAC Establecemos alpha. Para transparencia descomentar flags con or. VC_DISPMANX_ALPHA_T layerAlpha; /*layerAlpha.flags = (DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS);*/ layerAlpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS; layerAlpha.opacity = 255; layerAlpha.mask = 0; dispvars->alpha = &layerAlpha; //MAC Creo los resources. Me hacen falta dos para el double buffering dispvars->resources[0] = vc_dispmanx_resource_create( dispvars->pix_format, width, height, &(dispvars->vc_image_ptr) ); dispvars->resources[1] = vc_dispmanx_resource_create( dispvars->pix_format, width, height, &(dispvars->vc_image_ptr) ); //Reservo memoria para el array de pixles en RAM dispvars->pixmem = calloc( 1, dispvars->pitch * height); //dispvars->pixmem=malloc ( dispvars->pitch * dispvars->amode.height ); //MAC Esta llamada a ReallocFormat es lo que impediaa ver algo... Rmask = 0; Gmask = 0; Bmask = 0; if ( ! SDL_ReallocFormat(current, bpp, Rmask, Gmask, Bmask, 0) ) { return(NULL); } //Preparamos SDL para trabajar sobre el nuevo framebuffer //No queremos HWSURFACEs por la manera en que funciona nuestro backend, ya que la app solo //debe conocer el buffer en RAM para que las actualizaciones no sean bloqueantes. //TAMPOCO queremos DOUBLEBUFFER: realmente piensa lo que estas haciendo: actualizas la //superficie de video, que esta en la RAM, copias a VRAM y, saltandote las normas del API, //esperas a evento de vsync para hacer el buffer swapping. Asi que la app NO SABE NADA de //double buffering ni debe saberlo. UpdateRect() debe hacer lo que antes haciaa FlipHWSurface, //ya que de cara a la APP, solo hay una actualizacion del buffer de dibujado, NO de pantalla, //ya que carecemos de acceso directo a la VRAM. //Permitimos HWPALETTEs, cosa que solo se activa si el juego pide un modo de 8bpp porque, //tanto si conseguimos modificar la paleta por hard como si tenemos que indexar los valores //como estamos haciendo hasta ahora emulando asi la paleta, nos interesa que los juegos //entren en SetColors(), y sin paleta por hardware no entran. current->flags |= SDL_FULLSCREEN; if (flags & SDL_DOUBLEBUF){ current->flags &= ~SDL_DOUBLEBUF; } if (flags & SDL_HWSURFACE){ current->flags &= ~SDL_HWSURFACE; current->flags |= SDL_SWSURFACE; } if (flags & SDL_HWPALETTE) current->flags |= SDL_HWPALETTE; current->w = width; current->h = height; current->pitch = dispvars->pitch; current->pixels = dispvars->pixmem; //DISPMANX_FreeHWSurfaces(this); //DISPMANX_InitHWSurfaces(this, current, surfaces_mem, surfaces_len); //this->screen = current; //this->screen = NULL; //Aniadimos el element. dispvars->update = vc_dispmanx_update_start( 0 ); dispvars->element = vc_dispmanx_element_add( dispvars->update, dispvars->display, 0 /*layer*/, &(dispvars->dst_rect), dispvars->resources[flip_page], &(dispvars->src_rect), DISPMANX_PROTECTION_NONE, dispvars->alpha, 0 /*clamp*/, /*VC_IMAGE_ROT0*/ 0 ); vc_dispmanx_update_submit_sync( dispvars->update ); /* We're done */ //MAC Disable graphics 1 //Aqui ponemos la terminal en modo grafico. Ya no se imprimiran mas mensajes en la consola a partir de aqui. go_video_console: if ( DISPMANX_EnterGraphicsMode(this) < 0 ) return(NULL); return(current); }
static SDL_Surface *DISPMANX_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags) { //MAC Recuerda que aquí ya nos llegan las dimensiones de un modo de vídeo // aproximado en SDL_Video.c de entre los modos de vídeo disponibles. //La jugada es dejar que un modo de 320x200 se vea en el modo que se nos ha //aproximado, seguramente mucho mayor, y luego escalar por hardware el área //visible al modo aproximado (de ahí que creemos el plano de overlay a parte, //ya que cuando SDL_Video.c llama a SetVideoMode aún no se tienen listos los //offsets horizontal y vertical donde empieza el modo de vídeo pequeño //(el original, ideal) sobre el grande (que sería el aproximado). //Otra cosa es la tasa de refresco. Tendrás que usar modos físicos //concretos (config.txt) para ajustarte a 50, 60 o 70 Hz. Uint32 Rmask; Uint32 Gmask; Uint32 Bmask; char *surfaces_mem; int surfaces_len; dispvars->bits_per_pixel = bpp; switch (bpp){ case 8: dispvars->pix_format = VC_IMAGE_8BPP; break; case 16: dispvars->pix_format = VC_IMAGE_RGB565; break; case 32: dispvars->pix_format = VC_IMAGE_XRGB8888; break; default: printf ("\nERR - wrong bpp: %d\n",bpp); return (NULL); } //MAC blah this->UpdateRects = DISPMANX_DirectUpdate; //MAC Establecemos los rects para el escalado //this->offset_x = (dispvars->amode.width - width)/2; //this->offset_y = (dispvars->amode.height - height)/2; printf ("\nUsing internal program mode: width=%d height=%d\n", width, height); //MAC Por ahora en DISPMANX usamos el mismo modo q ya está establecido printf ("\nUsing physical mode: %d x %d %d bpp\n", dispvars->amode.width, dispvars->amode.height, dispvars->bits_per_pixel); //MAC Establecemos el pitch en fn del bpp deseado //Lo alineamos a 32 porque es el aligment interno de dispmanx(en ejemp) dispvars->pitch = ( ALIGN_UP( width, 16 ) * (bpp/8) ); //Alineamos la atura a 16 por el mismo motivo (ver ejemplo hello_disp) height = ALIGN_UP( height, 16); //Dejamos configurados los rects vc_dispmanx_rect_set (&(dispvars->bmp_rect), 0, 0, width, height); vc_dispmanx_rect_set (&(dispvars->src_rect), 0, 0, width << 16, height << 16); vc_dispmanx_rect_set( &(dispvars->dst_rect), 0, 0, dispvars->amode.width , dispvars->amode.height ); //MAC Establecemos alpha. Para transparencia descomentar flags con or. VC_DISPMANX_ALPHA_T layerAlpha; layerAlpha.flags = (DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS); /*layerAlpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS;*/ layerAlpha.opacity = 255; layerAlpha.mask = 0; dispvars->alpha = &layerAlpha; //MAC Creo los resources. Me hacen falta dos para el double buffering dispvars->resources[0] = vc_dispmanx_resource_create( dispvars->pix_format, width, height, &(dispvars->vc_image_ptr) ); assert (dispvars->resources[0]); dispvars->resources[1] = vc_dispmanx_resource_create( dispvars->pix_format, width, height, &(dispvars->vc_image_ptr) ); assert (dispvars->resources[1]); //Reservo memoria para el array de pixles en RAM dispvars->pixmem=calloc( 1, dispvars->pitch * height); //dispvars->pixmem=malloc ( dispvars->pitch * dispvars->amode.height ); //MAC Esto se usa, como mínimo y que yo sepa, para DirectUpdate //cache_modinfo = *modinfo; //cache_fbinfo = *(drmModeGetFB (fd, fb_id)); //MAC Esta llamada a ReallocFormat es lo que impedía ver algo... Rmask = 0; Gmask = 0; Bmask = 0; if ( ! SDL_ReallocFormat(current, bpp, Rmask, Gmask, Bmask, 0) ) { return(NULL); } //Preparamos SDL para trabajar sobre el nuevo framebuffer shadow_fb = 0; current->flags |= SDL_HWSURFACE; current->flags |= SDL_FULLSCREEN; current->flags |= SDL_HWPALETTE; if (flags & SDL_DOUBLEBUF) current->flags |= SDL_DOUBLEBUF; current->w = width; current->h = height; mapped_mem = dispvars->pixmem; mapped_memlen = (dispvars->pitch * height); current->pitch = dispvars->pitch; current->pixels = mapped_mem; /* Set up the information for hardware surfaces */ surfaces_mem = (char *)current->pixels + (dispvars->pitch * height); surfaces_len = (mapped_memlen-(surfaces_mem-mapped_mem)); DISPMANX_FreeHWSurfaces(this); DISPMANX_InitHWSurfaces(this, current, surfaces_mem, surfaces_len); /* Update for double-buffering, if we can */ //Recuerda que necesitamos dos buffers porque hay que mantener uno //sin tocar durante todo el ciclo de fotograma (16ms) ya que es el que //el monitor lee como scanout durante ese tiempo. this->screen = current; this->screen = NULL; //Colocamos el element. Alpha 0 estabiliza %uso de cpu dispvars->update = vc_dispmanx_update_start( 0 ); dispvars->element = vc_dispmanx_element_add( dispvars->update, dispvars->display, 2000 /*layer*/, &(dispvars->dst_rect), dispvars->resources[flip_page], &(dispvars->src_rect), DISPMANX_PROTECTION_NONE, dispvars->alpha, 0 /*clamp*/, /*VC_IMAGE_ROT0*/ 0 ); vc_dispmanx_update_submit_sync( dispvars->update ); /* We're done */ //MAC Disable graphics 1 // Set the terminal into graphics mode printf ("\nDISPMANX_SetVideoMode activating keyboard and exiting"); if ( DISPMANX_EnterGraphicsMode(this) < 0 ) return(NULL); return(current); }