2 * Test egl driver for fb_dri.so
11 #include <sys/ioctl.h>
17 #include "extensions.h"
18 #include "framebuffer.h"
19 #include "renderbuffer.h"
21 #include "swrast/swrast.h"
22 #include "swrast_setup/swrast_setup.h"
24 #include "tnl/t_context.h"
25 #include "tnl/t_pipeline.h"
26 #include "drivers/common/driverfuncs.h"
27 #include "drirenderbuffer.h"
29 #include "eglconfig.h"
30 #include "eglcontext.h"
31 #include "egldisplay.h"
32 #include "egldriver.h"
33 #include "eglglobals.h"
35 #include "eglscreen.h"
36 #include "eglsurface.h"
39 fbSetSpanFunctions(driRenderbuffer *drb, const GLvisual *vis);
42 * fb driver-specific driver class derived from _EGLDriver
44 typedef struct fb_driver
46 _EGLDriver Base; /* base class/object */
51 * fb display-specific driver class derived from _EGLDisplay
53 typedef struct fb_display
55 _EGLDisplay Base; /* base class/object */
60 * fb driver-specific screen class derived from _EGLScreen
62 typedef struct fb_screen
70 * fb driver-specific surface class derived from _EGLSurface
72 typedef struct fb_surface
74 _EGLSurface Base; /* base class/object */
75 struct gl_framebuffer *mesa_framebuffer;
80 * fb driver-specific context class derived from _EGLContext
82 typedef struct fb_context
84 _EGLContext Base; /* base class/object */
87 __DRIcontextPrivate *context;
88 __DRIscreenPrivate *screen;
89 __DRIdrawablePrivate *drawable; /* drawable bound to this ctx */
91 } fbContext, *fbContextPtr;
93 #define FB_CONTEXT(ctx) ((fbContextPtr)(ctx->DriverCtx))
97 fbFillInConfigs(_EGLDisplay *disp, unsigned pixel_bits, unsigned depth_bits,
98 unsigned stencil_bits, GLboolean have_back_buffer) {
101 unsigned int i, num_configs;
102 unsigned int depth_buffer_factor;
103 unsigned int back_buffer_factor;
107 /* Right now GLX_SWAP_COPY_OML isn't supported, but it would be easy
108 * enough to add support. Basically, if a context is created with an
109 * fbconfig where the swap method is GLX_SWAP_COPY_OML, pageflipping
110 * will never be used.
112 static const GLenum back_buffer_modes[] = {
113 GLX_NONE, GLX_SWAP_UNDEFINED_OML /*, GLX_SWAP_COPY_OML */
116 u_int8_t depth_bits_array[2];
117 u_int8_t stencil_bits_array[2];
119 depth_bits_array[0] = 0;
120 depth_bits_array[1] = depth_bits;
122 /* Just like with the accumulation buffer, always provide some modes
123 * with a stencil buffer. It will be a sw fallback, but some apps won't
126 stencil_bits_array[0] = 0;
127 stencil_bits_array[1] = (stencil_bits == 0) ? 8 : stencil_bits;
129 depth_buffer_factor = ((depth_bits != 0) || (stencil_bits != 0)) ? 2 : 1;
130 back_buffer_factor = (have_back_buffer) ? 2 : 1;
132 num_configs = depth_buffer_factor * back_buffer_factor * 2;
134 if (pixel_bits == 16) {
136 fb_type = GL_UNSIGNED_SHORT_5_6_5;
139 fb_type = GL_UNSIGNED_INT_8_8_8_8_REV;
142 configs = calloc(sizeof(*configs), num_configs);
144 if (!_eglFillInConfigs(c, fb_format, fb_type,
145 depth_bits_array, stencil_bits_array, depth_buffer_factor,
146 back_buffer_modes, back_buffer_factor,
148 fprintf(stderr, "[%s:%u] Error creating FBConfig!\n",
153 /* Mark the visual as slow if there are "fake" stencil bits.
155 for (i = 0, c = configs; i < num_configs; i++, c++) {
156 int stencil = GET_CONFIG_ATTRIB(c, EGL_STENCIL_SIZE);
157 if ((stencil != 0) && (stencil != stencil_bits)) {
158 SET_CONFIG_ATTRIB(c, EGL_CONFIG_CAVEAT, EGL_SLOW_CONFIG);
162 for (i = 0, c = configs; i < num_configs; i++, c++)
163 _eglAddConfig(disp, c);
171 fbSetupFramebuffer(fbDisplay *disp, char *fbdev)
175 struct fb_var_screeninfo varInfo;
176 struct fb_fix_screeninfo fixedInfo;
178 snprintf(dev, sizeof(dev), "/dev/%s", fbdev);
180 /* open the framebuffer device */
181 fd = open(dev, O_RDWR);
183 fprintf(stderr, "Error opening %s: %s\n", fbdev, strerror(errno));
187 /* get the original variable screen info */
188 if (ioctl(fd, FBIOGET_VSCREENINFO, &varInfo)) {
189 fprintf(stderr, "error: ioctl(FBIOGET_VSCREENINFO) failed: %s\n",
194 /* Turn off hw accels (otherwise mmap of mmio region will be
197 if (varInfo.accel_flags) {
198 varInfo.accel_flags = 0;
199 if (ioctl(fd, FBIOPUT_VSCREENINFO, &varInfo)) {
200 fprintf(stderr, "error: ioctl(FBIOPUT_VSCREENINFO) failed: %s\n",
206 /* Get the fixed screen info */
207 if (ioctl(fd, FBIOGET_FSCREENINFO, &fixedInfo)) {
208 fprintf(stderr, "error: ioctl(FBIOGET_FSCREENINFO) failed: %s\n",
213 if (fixedInfo.visual == FB_VISUAL_DIRECTCOLOR) {
215 unsigned short red[256], green[256], blue[256];
216 int rcols = 1 << varInfo.red.length;
217 int gcols = 1 << varInfo.green.length;
218 int bcols = 1 << varInfo.blue.length;
228 for (i = 0; i < rcols ; i++)
229 red[i] = (65536/(rcols-1)) * i;
231 for (i = 0; i < gcols ; i++)
232 green[i] = (65536/(gcols-1)) * i;
234 for (i = 0; i < bcols ; i++)
235 blue[i] = (65536/(bcols-1)) * i;
237 if (ioctl(fd, FBIOPUTCMAP, (void *) &cmap) < 0) {
238 fprintf(stderr, "ioctl(FBIOPUTCMAP) failed [%d]\n", i);
243 /* mmap the framebuffer into our address space */
245 disp->pFB = (caddr_t)mmap(0, /* start */
246 fixedInfo.smem_len, /* bytes */
247 PROT_READ | PROT_WRITE, /* prot */
248 MAP_SHARED, /* flags */
251 if (disp->pFB == (caddr_t)-1) {
252 fprintf(stderr, "error: unable to mmap framebuffer: %s\n",
260 const char *sysfs = "/sys/class/graphics";
263 fbInitialize(_EGLDriver *drv, EGLDisplay dpy, EGLint *major, EGLint *minor)
265 _EGLDisplay *disp = _eglLookupDisplay(dpy);
270 unsigned int x, y, r;
273 struct dirent *dirent;
276 /* Switch display structure to one with our private fields */
277 display = calloc(1, sizeof(*display));
278 display->Base = *disp;
279 _eglHashInsert(_eglGlobal.Displays, disp->Handle, display);
285 dir = opendir(sysfs);
287 printf("EGL - %s framebuffer device not found.", sysfs);
291 while ((dirent = readdir(dir))) { /* assignment! */
293 if (dirent->d_name[0] != 'f')
295 if (dirent->d_name[1] != 'b')
298 if (fbSetupFramebuffer(display, dirent->d_name) == EGL_FALSE)
301 /* Create a screen */
302 s = (fbScreen *) calloc(1, sizeof(fbScreen));
306 strncpy(s->fb, dirent->d_name, NAME_MAX);
308 _eglInitScreen(scrn);
309 _eglAddScreen(&display->Base, scrn);
311 snprintf(path, sizeof(path), "%s/%s/modes", sysfs, s->fb);
312 file = fopen(path, "r");
313 while (fgets(path, sizeof(path), file)) {
314 sscanf(path, "%c:%ux%u-%u", &c, &x, &y, &r);
315 _eglAddMode(scrn, x, y, r * 1000, path);
319 fbFillInConfigs(&display->Base, 32, 24, 8, 1);
324 drv->Initialized = EGL_TRUE;
330 Lookup_fbDisplay(EGLDisplay dpy)
332 _EGLDisplay *d = _eglLookupDisplay(dpy);
333 return (fbDisplay *) d;
338 Lookup_fbScreen(EGLDisplay dpy, EGLScreenMESA screen)
340 _EGLScreen *s = _eglLookupScreen(dpy, screen);
341 return (fbScreen *) s;
346 Lookup_fbContext(EGLContext ctx)
348 _EGLContext *c = _eglLookupContext(ctx);
349 return (fbContext *) c;
354 Lookup_fbSurface(EGLSurface surf)
356 _EGLSurface *s = _eglLookupSurface(surf);
357 return (fbSurface *) s;
362 fbTerminate(_EGLDriver *drv, EGLDisplay dpy)
364 fbDisplay *display = Lookup_fbDisplay(dpy);
365 _eglCleanupDisplay(&display->Base);
372 static const GLubyte *
373 get_string(GLcontext *ctx, GLenum pname)
378 return (const GLubyte *) "Mesa dumb framebuffer";
386 update_state( GLcontext *ctx, GLuint new_state )
388 /* not much to do here - pass it on */
389 _swrast_InvalidateState( ctx, new_state );
390 _swsetup_InvalidateState( ctx, new_state );
391 _vbo_InvalidateState( ctx, new_state );
392 _tnl_InvalidateState( ctx, new_state );
397 * Called by ctx->Driver.GetBufferSize from in core Mesa to query the
398 * current framebuffer size.
401 get_buffer_size( GLframebuffer *buffer, GLuint *width, GLuint *height )
403 *width = buffer->Width;
404 *height = buffer->Height;
409 updateFramebufferSize(GLcontext *ctx)
411 fbContextPtr fbmesa = FB_CONTEXT(ctx);
412 struct gl_framebuffer *fb = ctx->WinSysDrawBuffer;
413 if (fbmesa->dri.drawable->w != fb->Width ||
414 fbmesa->dri.drawable->h != fb->Height) {
415 driUpdateFramebufferSize(ctx, fbmesa->dri.drawable);
420 viewport(GLcontext *ctx, GLint x, GLint y, GLsizei w, GLsizei h)
422 /* XXX this should be called after we acquire the DRI lock, not here */
423 updateFramebufferSize(ctx);
428 init_core_functions( struct dd_function_table *functions )
430 functions->GetString = get_string;
431 functions->UpdateState = update_state;
432 functions->GetBufferSize = get_buffer_size;
433 functions->Viewport = viewport;
435 functions->Clear = _swrast_Clear; /* could accelerate with blits */
440 fbCreateContext(_EGLDriver *drv, EGLDisplay dpy, EGLConfig config, EGLContext share_list, const EGLint *attrib_list)
445 _EGLDisplay *disp = _eglLookupDisplay(dpy);
446 struct dd_function_table functions;
450 conf = _eglLookupConfig(drv, dpy, config);
452 _eglError(EGL_BAD_CONFIG, "eglCreateContext");
453 return EGL_NO_CONTEXT;
456 for (i = 0; attrib_list && attrib_list[i] != EGL_NONE; i++) {
457 switch (attrib_list[i]) {
458 /* no attribs defined for now */
460 _eglError(EGL_BAD_ATTRIBUTE, "eglCreateContext");
461 return EGL_NO_CONTEXT;
465 c = (fbContext *) calloc(1, sizeof(fbContext));
467 return EGL_NO_CONTEXT;
469 _eglInitContext(&c->Base);
470 c->Base.Display = disp;
471 c->Base.Config = conf;
472 c->Base.DrawSurface = EGL_NO_SURFACE;
473 c->Base.ReadSurface = EGL_NO_SURFACE;
475 /* generate handle and insert into hash table */
476 _eglSaveContext(&c->Base);
477 assert(c->Base.Handle);
479 /* Init default driver functions then plug in our FBdev-specific functions
481 _mesa_init_driver_functions(&functions);
482 init_core_functions(&functions);
484 _eglConfigToContextModesRec(conf, &vis);
486 ctx = c->glCtx = _mesa_create_context(&vis, NULL, &functions, (void *)c);
492 /* Create module contexts */
493 _swrast_CreateContext( ctx );
494 _vbo_CreateContext( ctx );
495 _tnl_CreateContext( ctx );
496 _swsetup_CreateContext( ctx );
497 _swsetup_Wakeup( ctx );
500 /* use default TCL pipeline */
502 TNLcontext *tnl = TNL_CONTEXT(ctx);
503 tnl->Driver.RunPipeline = _tnl_run_pipeline;
506 _mesa_enable_sw_extensions(ctx);
508 return c->Base.Handle;
513 fbCreateWindowSurface(_EGLDriver *drv, EGLDisplay dpy, EGLConfig config, NativeWindowType window, const EGLint *attrib_list)
516 for (i = 0; attrib_list && attrib_list[i] != EGL_NONE; i++) {
517 switch (attrib_list[i]) {
518 /* no attribs at this time */
520 _eglError(EGL_BAD_ATTRIBUTE, "eglCreateWindowSurface");
521 return EGL_NO_SURFACE;
524 printf("eglCreateWindowSurface()\n");
527 return EGL_NO_SURFACE;
532 fbCreatePixmapSurface(_EGLDriver *drv, EGLDisplay dpy, EGLConfig config, NativePixmapType pixmap, const EGLint *attrib_list)
537 conf = _eglLookupConfig(drv, dpy, config);
539 _eglError(EGL_BAD_CONFIG, "eglCreatePixmapSurface");
540 return EGL_NO_SURFACE;
543 for (i = 0; attrib_list && attrib_list[i] != EGL_NONE; i++) {
544 switch (attrib_list[i]) {
545 /* no attribs at this time */
547 _eglError(EGL_BAD_ATTRIBUTE, "eglCreatePixmapSurface");
548 return EGL_NO_SURFACE;
552 if (conf->Attrib[EGL_SURFACE_TYPE - FIRST_ATTRIB] == 0) {
553 _eglError(EGL_BAD_MATCH, "eglCreatePixmapSurface");
554 return EGL_NO_SURFACE;
557 printf("eglCreatePixmapSurface()\n");
558 return EGL_NO_SURFACE;
563 fbCreatePbufferSurface(_EGLDriver *drv, EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list)
567 surf = (fbSurface *) calloc(1, sizeof(fbSurface));
569 return EGL_NO_SURFACE;
572 if (_eglInitPbufferSurface(&surf->Base, drv, dpy, config, attrib_list) == EGL_NO_SURFACE) {
574 return EGL_NO_SURFACE;
577 /* create software-based pbuffer */
579 GLcontext *ctx = NULL; /* this _should_ be OK */
581 _EGLConfig *conf = _eglLookupConfig(drv, dpy, config);
582 assert(conf); /* bad config should be caught earlier */
583 _eglConfigToContextModesRec(conf, &vis);
585 surf->mesa_framebuffer = _mesa_create_framebuffer(&vis);
586 _mesa_add_soft_renderbuffers(surf->mesa_framebuffer,
587 GL_TRUE, /* color bufs */
589 vis.haveStencilBuffer,
591 GL_FALSE, /* alpha */
592 GL_FALSE /* aux */ );
594 /* set pbuffer/framebuffer size */
595 _mesa_resize_framebuffer(ctx, surf->mesa_framebuffer,
596 surf->Base.Width, surf->Base.Height);
599 return surf->Base.Handle;
604 fbDestroySurface(_EGLDriver *drv, EGLDisplay dpy, EGLSurface surface)
606 fbSurface *fs = Lookup_fbSurface(surface);
607 _eglRemoveSurface(&fs->Base);
608 if (fs->Base.IsBound) {
609 fs->Base.DeletePending = EGL_TRUE;
619 fbDestroyContext(_EGLDriver *drv, EGLDisplay dpy, EGLContext context)
621 fbContext *fc = Lookup_fbContext(context);
622 _eglRemoveContext(&fc->Base);
623 if (fc->Base.IsBound) {
624 fc->Base.DeletePending = EGL_TRUE;
634 fbMakeCurrent(_EGLDriver *drv, EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext context)
636 fbSurface *readSurf = Lookup_fbSurface(read);
637 fbSurface *drawSurf = Lookup_fbSurface(draw);
638 fbContext *ctx = Lookup_fbContext(context);
641 b = _eglMakeCurrent(drv, dpy, draw, read, context);
646 _mesa_make_current( ctx->glCtx,
647 drawSurf->mesa_framebuffer,
648 readSurf->mesa_framebuffer);
650 _mesa_make_current( NULL, NULL, NULL );
657 * Create a drawing surface which can be directly displayed on a screen.
660 fbCreateScreenSurfaceMESA(_EGLDriver *drv, EGLDisplay dpy, EGLConfig cfg,
661 const EGLint *attrib_list)
663 _EGLConfig *config = _eglLookupConfig(drv, dpy, cfg);
664 fbDisplay *display = Lookup_fbDisplay(dpy);
668 GLcontext *ctx = NULL; /* this should be OK */
669 int origin, bytesPerPixel;
670 int width, height, stride;
672 surface = (fbSurface *) malloc(sizeof(*surface));
674 return EGL_NO_SURFACE;
677 /* init base class, error check, etc. */
678 surf = _eglInitScreenSurface(&surface->Base, drv, dpy, cfg, attrib_list);
679 if (surf == EGL_NO_SURFACE) {
681 return EGL_NO_SURFACE;
684 /* convert EGLConfig to GLvisual */
685 _eglConfigToContextModesRec(config, &vis);
687 /* create Mesa framebuffer */
688 surface->mesa_framebuffer = _mesa_create_framebuffer(&vis);
689 if (!surface->mesa_framebuffer) {
691 _eglRemoveSurface(&surface->Base);
692 return EGL_NO_SURFACE;
695 width = surface->Base.Width;
696 height = surface->Base.Height;
697 bytesPerPixel = vis.rgbBits / 8;
698 stride = width * bytesPerPixel;
701 /* front color renderbuffer */
703 driRenderbuffer *drb = driNewRenderbuffer(GL_RGBA, display->pFB,
705 origin, stride, NULL);
706 fbSetSpanFunctions(drb, &vis);
707 _mesa_add_renderbuffer(surface->mesa_framebuffer,
708 BUFFER_FRONT_LEFT, &drb->Base);
711 /* back color renderbuffer */
712 if (vis.doubleBufferMode) {
713 GLubyte *backBuf = _mesa_malloc(stride * height);
714 driRenderbuffer *drb = driNewRenderbuffer(GL_RGBA, backBuf,
716 origin, stride, NULL);
717 fbSetSpanFunctions(drb, &vis);
718 _mesa_add_renderbuffer(surface->mesa_framebuffer,
719 BUFFER_BACK_LEFT, &drb->Base);
722 /* other renderbuffers- software based */
723 _mesa_add_soft_renderbuffers(surface->mesa_framebuffer,
724 GL_FALSE, /* color */
726 vis.haveStencilBuffer,
728 GL_FALSE, /* alpha */
731 _mesa_resize_framebuffer(ctx, surface->mesa_framebuffer, width, height);
738 * Show the given surface on the named screen.
739 * If surface is EGL_NO_SURFACE, disable the screen's output.
742 fbShowSurfaceMESA(_EGLDriver *drv, EGLDisplay dpy, EGLScreenMESA screen,
743 EGLSurface surface, EGLModeMESA m)
745 fbDisplay *display = Lookup_fbDisplay(dpy);
746 fbScreen *scrn = Lookup_fbScreen(dpy, screen);
747 fbSurface *surf = Lookup_fbSurface(surface);
749 char buffer[NAME_MAX];
750 _EGLMode *mode = _eglLookupMode(dpy, m);
753 if (!_eglShowSurfaceMESA(drv, dpy, screen, surface, m))
756 snprintf(buffer, sizeof(buffer), "%s/%s/blank", sysfs, scrn->fb);
758 file = fopen(buffer, "r+");
761 printf("chown all fb sysfs attrib to allow write - %s\n", buffer);
764 snprintf(buffer, sizeof(buffer), "%d", (m == EGL_NO_MODE_MESA ? VESA_POWERDOWN : VESA_VSYNC_SUSPEND));
768 if (m == EGL_NO_MODE_MESA)
771 snprintf(buffer, sizeof(buffer), "%s/%s/mode", sysfs, scrn->fb);
773 file = fopen(buffer, "r+");
776 fputs(mode->Name, file);
779 snprintf(buffer, sizeof(buffer), "%s/%s/bits_per_pixel", sysfs, scrn->fb);
781 file = fopen(buffer, "r+");
784 bits = GET_CONFIG_ATTRIB(surf->Base.Config, EGL_BUFFER_SIZE);
785 snprintf(buffer, sizeof(buffer), "%d", bits);
789 fbSetupFramebuffer(display, scrn->fb);
791 snprintf(buffer, sizeof(buffer), "%s/%s/blank", sysfs, scrn->fb);
793 file = fopen(buffer, "r+");
797 snprintf(buffer, sizeof(buffer), "%d", VESA_NO_BLANKING);
805 /* If the backbuffer is on a videocard, this is extraordinarily slow!
808 fbSwapBuffers(_EGLDriver *drv, EGLDisplay dpy, EGLSurface draw)
810 fbContext *context = (fbContext *)_eglGetCurrentContext();
811 fbSurface *fs = Lookup_fbSurface(draw);
812 struct gl_renderbuffer * front_renderbuffer = fs->mesa_framebuffer->Attachment[BUFFER_FRONT_LEFT].Renderbuffer;
813 void *frontBuffer = front_renderbuffer->Data;
814 int currentPitch = ((driRenderbuffer *)front_renderbuffer)->pitch;
815 void *backBuffer = fs->mesa_framebuffer->Attachment[BUFFER_BACK_LEFT].Renderbuffer->Data;
817 if (!_eglSwapBuffers(drv, dpy, draw))
821 GLcontext *ctx = context->glCtx;
823 if (ctx->Visual.doubleBufferMode) {
826 char *tmp = _mesa_malloc(currentPitch);
828 _mesa_notifySwapBuffers( ctx ); /* flush pending rendering comands */
833 for (i = 0; i < fs->Base.Height; i++) {
834 _mesa_memcpy(tmp, (char *) backBuffer + offset,
836 _mesa_memcpy((char *) frontBuffer + offset, tmp,
838 offset += currentPitch;
845 /* XXX this shouldn't be an error but we can't handle it for now */
846 _mesa_problem(NULL, "fbSwapBuffers: drawable has no context!\n");
854 * The bootstrap function. Return a new fbDriver object and
855 * plug in API functions.
858 _eglMain(_EGLDisplay *dpy)
862 fb = (fbDriver *) calloc(1, sizeof(fbDriver));
867 /* First fill in the dispatch table with defaults */
868 _eglInitDriverFallbacks(&fb->Base);
870 /* then plug in our fb-specific functions */
871 fb->Base.Initialize = fbInitialize;
872 fb->Base.Terminate = fbTerminate;
873 fb->Base.CreateContext = fbCreateContext;
874 fb->Base.MakeCurrent = fbMakeCurrent;
875 fb->Base.CreateWindowSurface = fbCreateWindowSurface;
876 fb->Base.CreatePixmapSurface = fbCreatePixmapSurface;
877 fb->Base.CreatePbufferSurface = fbCreatePbufferSurface;
878 fb->Base.DestroySurface = fbDestroySurface;
879 fb->Base.DestroyContext = fbDestroyContext;
880 fb->Base.CreateScreenSurfaceMESA = fbCreateScreenSurfaceMESA;
881 fb->Base.ShowSurfaceMESA = fbShowSurfaceMESA;
882 fb->Base.SwapBuffers = fbSwapBuffers;
884 /* enable supported extensions */
885 fb->Base.MESA_screen_surface = EGL_TRUE;
886 fb->Base.MESA_copy_context = EGL_TRUE;