st/egl: update fbdev backend

Considering fbdev as an in-kernel window system,

 - opening a device opens a connection
 - there is only one window: the framebuffer
 - fb_var_screeninfo decides window position, size, and even color format
 - there is no pixmap

Now EGL is built on top of this window system.  So we should have

 - the fd as the handle of the native display
 - reject all but one native window: NULL
 - no pixmap support

modeset support is still around, but it should be removed soon.
This commit is contained in:
Chia-I Wu 2011-06-28 10:22:01 +09:00
parent a2537bbc95
commit aa281dd392
3 changed files with 242 additions and 174 deletions

View File

@ -26,6 +26,21 @@
* Chia-I Wu <olv@lunarg.com>
*/
/**
* Considering fbdev as an in-kernel window system,
*
* - opening a device opens a connection
* - there is only one window: the framebuffer
* - fb_var_screeninfo decides window position, size, and even color format
* - there is no pixmap
*
* Now EGL is built on top of this window system. So we should have
*
* - the fd as the handle of the native display
* - reject all but one native window: NULL
* - no pixmap support
*/
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
@ -48,13 +63,10 @@ struct fbdev_display {
const struct native_event_handler *event_handler;
struct fb_fix_screeninfo finfo;
struct fb_var_screeninfo vinfo;
struct fb_var_screeninfo config_vinfo;
struct native_config config;
struct native_connector connector;
struct native_mode mode;
struct fbdev_surface *current_surface;
boolean assume_fixed_vinfo;
};
struct fbdev_surface {
@ -66,7 +78,7 @@ struct fbdev_surface {
unsigned int sequence_number;
boolean is_current;
struct fbdev_sw_drawable drawable;
};
static INLINE struct fbdev_display *
@ -103,38 +115,70 @@ fbdev_surface_validate(struct native_surface *nsurf, uint attachment_mask,
return TRUE;
}
static boolean
fbdev_surface_flush_frontbuffer(struct native_surface *nsurf)
static enum pipe_format
vinfo_to_format(const struct fb_var_screeninfo *vinfo)
{
struct fbdev_surface *fbsurf = fbdev_surface(nsurf);
enum pipe_format format = PIPE_FORMAT_NONE;
if (!fbsurf->is_current)
return TRUE;
/* should also check channel offsets... */
switch (vinfo->bits_per_pixel) {
case 32:
if (vinfo->red.length == 8 &&
vinfo->green.length == 8 &&
vinfo->blue.length == 8) {
format = (vinfo->transp.length == 8) ?
PIPE_FORMAT_B8G8R8A8_UNORM : PIPE_FORMAT_B8G8R8X8_UNORM;
}
break;
case 16:
if (vinfo->red.length == 5 &&
vinfo->green.length == 6 &&
vinfo->blue.length == 5 &&
vinfo->transp.length == 0)
format = PIPE_FORMAT_B5G6R5_UNORM;
break;
default:
break;
}
return resource_surface_present(fbsurf->rsurf,
NATIVE_ATTACHMENT_FRONT_LEFT, NULL);
return format;
}
static boolean
fbdev_surface_swap_buffers(struct native_surface *nsurf)
fbdev_surface_update_drawable(struct native_surface *nsurf,
const struct fb_var_screeninfo *vinfo)
{
struct fbdev_surface *fbsurf = fbdev_surface(nsurf);
struct fbdev_display *fbdpy = fbsurf->fbdpy;
boolean ret = TRUE;
unsigned x, y, width, height;
if (fbsurf->is_current) {
ret = resource_surface_present(fbsurf->rsurf,
NATIVE_ATTACHMENT_BACK_LEFT, NULL);
x = vinfo->xoffset;
y = vinfo->yoffset;
width = MIN2(vinfo->xres, fbsurf->width);
height = MIN2(vinfo->yres, fbsurf->height);
/* sanitize the values */
if (x + width > vinfo->xres_virtual) {
if (x > vinfo->xres_virtual)
width = 0;
else
width = vinfo->xres_virtual - x;
}
if (y + height > vinfo->yres_virtual) {
if (y > vinfo->yres_virtual)
height = 0;
else
height = vinfo->yres_virtual - y;
}
resource_surface_swap_buffers(fbsurf->rsurf,
NATIVE_ATTACHMENT_FRONT_LEFT, NATIVE_ATTACHMENT_BACK_LEFT, TRUE);
/* the front/back textures are swapped */
fbsurf->sequence_number++;
fbdpy->event_handler->invalid_surface(&fbdpy->base,
&fbsurf->base, fbsurf->sequence_number);
fbsurf->drawable.format = vinfo_to_format(vinfo);
fbsurf->drawable.x = vinfo->xoffset;
fbsurf->drawable.y = vinfo->yoffset;
fbsurf->drawable.width = vinfo->xres;
fbsurf->drawable.height = vinfo->yres;
return ret;
return (fbsurf->drawable.format != PIPE_FORMAT_NONE &&
fbsurf->drawable.width &&
fbsurf->drawable.height);
}
static boolean
@ -143,21 +187,43 @@ fbdev_surface_present(struct native_surface *nsurf,
boolean preserve,
uint swap_interval)
{
boolean ret;
struct fbdev_surface *fbsurf = fbdev_surface(nsurf);
struct fbdev_display *fbdpy = fbsurf->fbdpy;
boolean ret = FALSE;
if (preserve || swap_interval)
if (swap_interval)
return FALSE;
if (natt != NATIVE_ATTACHMENT_BACK_LEFT)
return FALSE;
switch (natt) {
case NATIVE_ATTACHMENT_FRONT_LEFT:
ret = fbdev_surface_flush_frontbuffer(nsurf);
break;
case NATIVE_ATTACHMENT_BACK_LEFT:
ret = fbdev_surface_swap_buffers(nsurf);
break;
default:
ret = FALSE;
break;
if (!fbdpy->assume_fixed_vinfo) {
struct fb_var_screeninfo vinfo;
memset(&vinfo, 0, sizeof(vinfo));
if (ioctl(fbdpy->fd, FBIOGET_VSCREENINFO, &vinfo))
return FALSE;
/* present the surface */
if (fbdev_surface_update_drawable(&fbsurf->base, &vinfo)) {
ret = resource_surface_present(fbsurf->rsurf,
natt, (void *) &fbsurf->drawable);
}
fbsurf->width = vinfo.xres;
fbsurf->height = vinfo.yres;
if (resource_surface_set_size(fbsurf->rsurf,
fbsurf->width, fbsurf->height)) {
/* surface resized */
fbsurf->sequence_number++;
fbdpy->event_handler->invalid_surface(&fbdpy->base,
&fbsurf->base, fbsurf->sequence_number);
}
}
else {
/* the drawable never changes */
ret = resource_surface_present(fbsurf->rsurf,
natt, (void *) &fbsurf->drawable);
}
return ret;
@ -179,26 +245,48 @@ fbdev_surface_destroy(struct native_surface *nsurf)
}
static struct native_surface *
fbdev_display_create_scanout_surface(struct native_display *ndpy,
const struct native_config *nconf,
uint width, uint height)
fbdev_display_create_window_surface(struct native_display *ndpy,
EGLNativeWindowType win,
const struct native_config *nconf)
{
struct fbdev_display *fbdpy = fbdev_display(ndpy);
struct fbdev_surface *fbsurf;
struct fb_var_screeninfo vinfo;
/* there is only one native window: NULL */
if (win)
return NULL;
fbsurf = CALLOC_STRUCT(fbdev_surface);
if (!fbsurf)
return NULL;
fbsurf->fbdpy = fbdpy;
fbsurf->width = width;
fbsurf->height = height;
/* get current vinfo */
if (fbdpy->assume_fixed_vinfo) {
vinfo = fbdpy->config_vinfo;
}
else {
memset(&vinfo, 0, sizeof(vinfo));
if (ioctl(fbdpy->fd, FBIOGET_VSCREENINFO, &vinfo)) {
FREE(fbsurf);
return NULL;
}
}
fbsurf->width = vinfo.xres;
fbsurf->height = vinfo.yres;
if (!fbdev_surface_update_drawable(&fbsurf->base, &vinfo)) {
FREE(fbsurf);
return NULL;
}
fbsurf->rsurf = resource_surface_create(fbdpy->base.screen,
nconf->color_format,
PIPE_BIND_RENDER_TARGET |
PIPE_BIND_DISPLAY_TARGET |
PIPE_BIND_SCANOUT);
PIPE_BIND_DISPLAY_TARGET);
if (!fbsurf->rsurf) {
FREE(fbsurf);
return NULL;
@ -214,42 +302,43 @@ fbdev_display_create_scanout_surface(struct native_display *ndpy,
return &fbsurf->base;
}
static struct native_surface *
fbdev_display_create_scanout_surface(struct native_display *ndpy,
const struct native_config *nconf,
uint width, uint height)
{
return fbdev_display_create_window_surface(ndpy,
(EGLNativeWindowType) NULL, nconf);
}
static boolean
fbdev_display_program(struct native_display *ndpy, int crtc_idx,
struct native_surface *nsurf, uint x, uint y,
const struct native_connector **nconns, int num_nconns,
const struct native_mode *nmode)
{
struct fbdev_display *fbdpy = fbdev_display(ndpy);
struct fbdev_surface *fbsurf = fbdev_surface(nsurf);
if (x || y)
return FALSE;
if (fbdpy->current_surface) {
if (fbdpy->current_surface == fbsurf)
return TRUE;
fbdpy->current_surface->is_current = FALSE;
}
if (fbsurf)
fbsurf->is_current = TRUE;
fbdpy->current_surface = fbsurf;
return TRUE;
}
static const struct native_mode **
fbdev_display_get_modes(struct native_display *ndpy,
const struct native_connector *nconn,
int *num_modes)
const struct native_connector *nconn,
int *num_modes)
{
struct fbdev_display *fbdpy = fbdev_display(ndpy);
static struct native_mode mode;
const struct native_mode **modes;
if (!mode.desc) {
struct fbdev_display *fbdpy = fbdev_display(ndpy);
mode.desc = "Current Mode";
mode.width = fbdpy->config_vinfo.xres;
mode.height = fbdpy->config_vinfo.yres;
mode.refresh_rate = 60 * 1000; /* dummy */
}
modes = MALLOC(sizeof(*modes));
if (modes) {
modes[0] = &fbdpy->mode;
modes[0] = &mode;
if (num_modes)
*num_modes = 1;
}
@ -261,12 +350,12 @@ static const struct native_connector **
fbdev_display_get_connectors(struct native_display *ndpy, int *num_connectors,
int *num_crtc)
{
struct fbdev_display *fbdpy = fbdev_display(ndpy);
static struct native_connector connector;
const struct native_connector **connectors;
connectors = MALLOC(sizeof(*connectors));
if (connectors) {
connectors[0] = &fbdpy->connector;
connectors[0] = &connector;
if (num_connectors)
*num_connectors = 1;
}
@ -274,7 +363,8 @@ fbdev_display_get_connectors(struct native_display *ndpy, int *num_connectors,
return connectors;
}
static struct native_display_modeset fbdev_display_modeset = {
/* remove modeset support one day! */
static const struct native_display_modeset fbdev_display_modeset = {
.get_connectors = fbdev_display_get_connectors,
.get_modes = fbdev_display_get_modes,
.create_scanout_surface = fbdev_display_create_scanout_surface,
@ -304,8 +394,10 @@ fbdev_display_get_param(struct native_display *ndpy,
int val;
switch (param) {
case NATIVE_PARAM_USE_NATIVE_BUFFER:
case NATIVE_PARAM_PRESERVE_BUFFER:
val = 1;
break;
case NATIVE_PARAM_USE_NATIVE_BUFFER:
case NATIVE_PARAM_MAX_SWAP_INTERVAL:
default:
val = 0;
@ -325,80 +417,13 @@ fbdev_display_destroy(struct native_display *ndpy)
FREE(fbdpy);
}
static boolean
fbdev_display_init_modes(struct native_display *ndpy)
{
struct fbdev_display *fbdpy = fbdev_display(ndpy);
struct native_mode *nmode = &fbdpy->mode;
nmode->desc = "Current Mode";
nmode->width = fbdpy->vinfo.xres;
nmode->height = fbdpy->vinfo.yres;
nmode->refresh_rate = 60 * 1000; /* dummy */
return TRUE;
}
static boolean
fbdev_display_init_connectors(struct native_display *ndpy)
{
return TRUE;
}
static enum pipe_format
vinfo_to_format(const struct fb_var_screeninfo *vinfo)
{
enum pipe_format format = PIPE_FORMAT_NONE;
switch (vinfo->bits_per_pixel) {
case 32:
if (vinfo->red.length == 8 &&
vinfo->green.length == 8 &&
vinfo->blue.length == 8) {
format = (vinfo->transp.length == 8) ?
PIPE_FORMAT_B8G8R8A8_UNORM : PIPE_FORMAT_B8G8R8X8_UNORM;
}
break;
case 16:
if (vinfo->red.length == 5 &&
vinfo->green.length == 6 &&
vinfo->blue.length == 5 &&
vinfo->transp.length == 0)
format = PIPE_FORMAT_B5G6R5_UNORM;
break;
default:
break;
}
return format;
}
static boolean
fbdev_display_init_configs(struct native_display *ndpy)
{
struct fbdev_display *fbdpy = fbdev_display(ndpy);
struct native_config *nconf = &fbdpy->config;
nconf->color_format = vinfo_to_format(&fbdpy->vinfo);
if (nconf->color_format == PIPE_FORMAT_NONE)
return FALSE;
nconf->buffer_mask =
(1 << NATIVE_ATTACHMENT_FRONT_LEFT) |
(1 << NATIVE_ATTACHMENT_BACK_LEFT);
nconf->scanout_bit = TRUE;
return TRUE;
}
static boolean
fbdev_display_init_screen(struct native_display *ndpy)
{
struct fbdev_display *fbdpy = fbdev_display(ndpy);
struct sw_winsys *ws;
ws = fbdev_create_sw_winsys(fbdpy->fd, fbdpy->config.color_format);
ws = fbdev_create_sw_winsys(fbdpy->fd);
if (!ws)
return FALSE;
@ -420,6 +445,26 @@ fbdev_display_init_screen(struct native_display *ndpy)
return TRUE;
}
static boolean
fbdev_display_init_config(struct native_display *ndpy)
{
struct fbdev_display *fbdpy = fbdev_display(ndpy);
struct native_config *nconf = &fbdpy->config;
if (ioctl(fbdpy->fd, FBIOGET_VSCREENINFO, &fbdpy->config_vinfo))
return FALSE;
nconf->color_format = vinfo_to_format(&fbdpy->config_vinfo);
if (nconf->color_format == PIPE_FORMAT_NONE)
return FALSE;
nconf->buffer_mask = (1 << NATIVE_ATTACHMENT_BACK_LEFT);
nconf->window_bit = TRUE;
return TRUE;
}
static struct native_display *
fbdev_display_create(int fd, const struct native_event_handler *event_handler)
{
@ -435,23 +480,24 @@ fbdev_display_create(int fd, const struct native_event_handler *event_handler)
if (ioctl(fbdpy->fd, FBIOGET_FSCREENINFO, &fbdpy->finfo))
goto fail;
if (ioctl(fbdpy->fd, FBIOGET_VSCREENINFO, &fbdpy->vinfo))
goto fail;
if (fbdpy->finfo.visual != FB_VISUAL_TRUECOLOR ||
fbdpy->finfo.type != FB_TYPE_PACKED_PIXELS)
goto fail;
if (!fbdev_display_init_configs(&fbdpy->base) ||
!fbdev_display_init_connectors(&fbdpy->base) ||
!fbdev_display_init_modes(&fbdpy->base))
if (!fbdev_display_init_config(&fbdpy->base))
goto fail;
fbdpy->assume_fixed_vinfo = TRUE;
fbdpy->base.init_screen = fbdev_display_init_screen;
fbdpy->base.destroy = fbdev_display_destroy;
fbdpy->base.get_param = fbdev_display_get_param;
fbdpy->base.get_configs = fbdev_display_get_configs;
fbdpy->base.create_window_surface = fbdev_display_create_window_surface;
/* we'd like to remove modeset support one day */
fbdpy->config.scanout_bit = TRUE;
fbdpy->base.modeset = &fbdev_display_modeset;
return &fbdpy->base;

View File

@ -54,10 +54,8 @@ struct fbdev_sw_winsys
struct sw_winsys base;
int fd;
enum pipe_format format;
struct fb_fix_screeninfo finfo;
void *fbmem;
unsigned rows;
unsigned stride;
};
@ -77,22 +75,53 @@ fbdev_sw_winsys(struct sw_winsys *ws)
static void
fbdev_displaytarget_display(struct sw_winsys *ws,
struct sw_displaytarget *dt,
void *context_private)
void *winsys_private)
{
struct fbdev_sw_winsys *fbdev = fbdev_sw_winsys(ws);
struct fbdev_sw_displaytarget *fbdt = fbdev_sw_displaytarget(dt);
unsigned rows, len, i;
struct fbdev_sw_displaytarget *src = fbdev_sw_displaytarget(dt);
const struct fbdev_sw_drawable *dst =
(const struct fbdev_sw_drawable *) winsys_private;
unsigned height, row_offset, row_len, i;
void *fbmem;
rows = MIN2(fbdt->height, fbdev->rows);
len = util_format_get_stride(fbdt->format, fbdt->width);
len = MIN2(len, fbdev->stride);
for (i = 0; i < rows; i++) {
void *dst = fbdev->fbmem + fbdev->stride * i;
void *src = fbdt->data + fbdt->stride * i;
memcpy(dst, src, len);
/* FIXME format conversion */
if (dst->format != src->format) {
assert(0);
return;
}
height = dst->height;
if (dst->y + dst->height > fbdev->rows) {
/* nothing to copy */
if (dst->y >= fbdev->rows)
return;
height = fbdev->rows - dst->y;
}
row_offset = util_format_get_stride(dst->format, dst->x);
row_len = util_format_get_stride(dst->format, dst->width);
if (row_offset + row_len > fbdev->stride) {
/* nothing to copy */
if (row_offset >= fbdev->stride)
return;
row_len = fbdev->stride - row_offset;
}
fbmem = mmap(0, fbdev->finfo.smem_len,
PROT_WRITE, MAP_SHARED, fbdev->fd, 0);
if (fbmem == MAP_FAILED)
return;
for (i = 0; i < height; i++) {
char *from = (char *) src->data + src->stride * i;
char *to = (char *) fbmem + fbdev->stride * (dst->y + i) + row_offset;
memcpy(to, from, row_len);
}
munmap(fbmem, fbdev->finfo.smem_len);
}
static void
@ -133,13 +162,9 @@ fbdev_displaytarget_create(struct sw_winsys *ws,
unsigned alignment,
unsigned *stride)
{
struct fbdev_sw_winsys *fbdev = fbdev_sw_winsys(ws);
struct fbdev_sw_displaytarget *fbdt;
unsigned nblocksy, size, format_stride;
if (fbdev->format != format)
return NULL;
fbdt = CALLOC_STRUCT(fbdev_sw_displaytarget);
if (!fbdt)
return NULL;
@ -170,8 +195,7 @@ fbdev_is_displaytarget_format_supported(struct sw_winsys *ws,
unsigned tex_usage,
enum pipe_format format)
{
struct fbdev_sw_winsys *fbdev = fbdev_sw_winsys(ws);
return (fbdev->format == format);
return TRUE;
}
static void
@ -179,12 +203,11 @@ fbdev_destroy(struct sw_winsys *ws)
{
struct fbdev_sw_winsys *fbdev = fbdev_sw_winsys(ws);
munmap(fbdev->fbmem, fbdev->finfo.smem_len);
FREE(fbdev);
}
struct sw_winsys *
fbdev_create_sw_winsys(int fd, enum pipe_format format)
fbdev_create_sw_winsys(int fd)
{
struct fbdev_sw_winsys *fbdev;
@ -193,19 +216,11 @@ fbdev_create_sw_winsys(int fd, enum pipe_format format)
return NULL;
fbdev->fd = fd;
fbdev->format = format;
if (ioctl(fbdev->fd, FBIOGET_FSCREENINFO, &fbdev->finfo)) {
FREE(fbdev);
return NULL;
}
fbdev->fbmem = mmap(0, fbdev->finfo.smem_len,
PROT_WRITE, MAP_SHARED, fbdev->fd, 0);
if (fbdev->fbmem == MAP_FAILED) {
FREE(fbdev);
return NULL;
}
fbdev->rows = fbdev->finfo.smem_len / fbdev->finfo.line_length;
fbdev->stride = fbdev->finfo.line_length;

View File

@ -32,7 +32,14 @@
struct sw_winsys;
enum pipe_format;
/* for pipe_screen::flush_frontbuffer */
struct fbdev_sw_drawable {
enum pipe_format format;
unsigned x, y;
unsigned width, height;
};
struct sw_winsys *
fbdev_create_sw_winsys(int fd, enum pipe_format format);
fbdev_create_sw_winsys(int fd);
#endif /* FBDEV_SW_WINSYS */