symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/fb_render_engine.c
author johnathan.white@2718R8BGH51.accenture.com
Mon, 08 Mar 2010 18:45:03 +0000
changeset 46 b6935a90ca64
parent 1 2fb8b9db1c86
permissions -rw-r--r--
Modify framebuffer and NGA framebuffer to read screen size from board model dtb file. Optimise memory usuage of frame buffer Add example minigui application with hooks to profiler (which writes results to S:\). Modified NGA framebuffer to run its own dfc queue at high priority

/*
 *  Render Engine for framebuffer devices
 *
 *  Copyright (c) 2008 CodeSourcery
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "hw.h"
//#include "console.h"
#include "gui.h"
#include "fb_render_engine.h"
#include "pixel_ops.h"

typedef void (*row_draw_fn)(uint32_t *, uint8_t *, const uint8_t *, int, int);

enum fb_dest_bpp_mode
{
    BPP_DEST_8,
    BPP_DEST_15,
    BPP_DEST_16,
    BPP_DEST_24,
    BPP_DEST_32
};

struct render_data_t {
    int base_is_in_target;
    union {
        void* base_host;
        target_phys_addr_t base_target;
    };
    uint8_t *dest;
    uint32_t cols;
    uint32_t rows;
    uint32_t row_pitch;
    /* The following attributes refer to the guest side.
       They should be renamed once they are also variable
       at the host side.
    */
    enum fb_pixel_order pixel_order;
    enum fb_byte_order  byte_order;
    enum fb_color_order color_order;
    enum fb_src_bpp_mode  src_bpp_mode;

    enum fb_dest_bpp_mode dest_bpp_mode;
    /* pending:
        pixel_order, byte_order, and color_order, for the dest (host).
    */
    enum fb_rotation orientation;
    uint32_t blank : 1;
    uint32_t need_internal_update : 1;
    uint32_t raw_palette[256];

    /* ====== cached internal data ===== */
    uint32_t inter_src_row_gap;
    uint32_t bytes_per_src_row; /* Does not include padding.  */
    row_draw_fn fn;
    /* rotation info */
    uint32_t dest_start_offset;
    uint32_t bytes_per_dest_row;
    int      dest_row_step;  /* in bytes */
    int      dest_col_step;  /* in bytes */
    int      swap_width_height;
    /* color info */
    uint32_t palette[256];

};


#define NOT_ASSIGNED -1

#include "fb_render_def.h"
#include "fb_render_decl.h"

/* getters */
uint32_t get_cols(const render_data *rd)
{
    return rd->cols;
}

uint32_t get_rows(const render_data *rd)
{
    return rd->rows;
}

uint32_t get_screen_width(const render_data *rd)
{
    return (!rd->swap_width_height) ? rd->cols : rd->rows;
}

uint32_t get_screen_height(const render_data *rd)
{
    return (!rd->swap_width_height) ? rd->rows : rd->cols;
}

enum fb_rotation get_orientation(const render_data *rd)
{
    return rd->orientation;
}

void* get_fb_base_in_host(const render_data *rd)
{
    return rd->base_is_in_target ? 0 : rd->base_host;
}

target_phys_addr_t get_fb_base_in_target(const render_data *rd)
{
    return rd->base_is_in_target ? rd->base_target : 0;
}

uint32_t get_blank_mode(const render_data *rd)
{
    return rd->blank;
}

enum fb_color_order get_color_order(const render_data *rd)
{
    return rd->color_order;
}

enum fb_byte_order get_byte_order(const render_data *rd)
{
    return rd->byte_order;
}

enum fb_pixel_order get_pixel_order(const render_data *rd)
{
    return rd->pixel_order;
}

enum fb_src_bpp_mode get_src_bpp(const render_data *rd)
{
    return rd->src_bpp_mode;
}

uint32_t get_row_pitch(const render_data *rd)
{
    return rd->row_pitch;
}

uint32_t get_palette_value(const render_data *rd, uint32_t n)
{
    return rd->raw_palette[n];
}


/* setters */
void set_cols(render_data *rd, uint32_t cols)
{
    rd->cols = cols; rd->need_internal_update = 1;
}

void set_rows(render_data *rd, uint32_t rows)
{
    rd->rows = rows; rd->need_internal_update = 1;
}

void set_orientation(render_data *rd, enum fb_rotation rotation)
{
    rd->orientation = rotation; rd->need_internal_update = 1;
}

void set_fb_base_from_host(render_data *rd, void* base)
{
    rd->base_is_in_target = 0;
    rd->base_host = base; rd->need_internal_update = 1;
}

void set_fb_base_from_target(render_data *rd, target_phys_addr_t base)
{
    rd->base_is_in_target = 1;
    /* ensure alignment */
    rd->base_target = (base & (~3)); rd->need_internal_update = 1;
}

void set_blank_mode(render_data *rd, int on_off)
{
    rd->blank = on_off; rd->need_internal_update = 1;
}

void set_pixel_order(render_data *rd, enum fb_pixel_order pixel_order)
{
    rd->pixel_order = pixel_order; rd->need_internal_update = 1;
}

void set_byte_order(render_data *rd, enum fb_byte_order byte_order)
{
    rd->byte_order = byte_order; rd->need_internal_update = 1;
}

void set_color_order(render_data *rd, enum fb_color_order color_order)
{
    rd->color_order = color_order; rd->need_internal_update = 1;
}

void set_src_bpp(render_data *rd, enum fb_src_bpp_mode src_bpp_mode)
{
    rd->src_bpp_mode = src_bpp_mode; rd->need_internal_update = 1;
}

void set_row_pitch(render_data *rd, uint32_t pitch)
{
    rd->row_pitch = pitch & ~3; rd->need_internal_update = 1;
}

static void set_dest_bpp(render_data *rd, enum fb_dest_bpp_mode dest_bpp_mode)
{
    rd->dest_bpp_mode = dest_bpp_mode; rd->need_internal_update = 1;
}

static void set_dest_bpp_from_ds(render_data *rd, const DisplayState *ds)
{
    enum fb_dest_bpp_mode new_dest_bpp_mode;

    switch (ds_get_bits_per_pixel(ds)) {
    case  8: new_dest_bpp_mode = BPP_DEST_8;  break;
    case 15: new_dest_bpp_mode = BPP_DEST_15; break;
    case 16: new_dest_bpp_mode = BPP_DEST_16; break;
    case 24: new_dest_bpp_mode = BPP_DEST_24; break;
    case 32: new_dest_bpp_mode = BPP_DEST_32; break;
    default:
        fprintf(stderr, "fb_render_engine: Bad color depth\n");
        exit(1);
    }

    if (rd->dest_bpp_mode != new_dest_bpp_mode)
        set_dest_bpp(rd, new_dest_bpp_mode);
}

static void set_dest_data_from_ds(render_data *rd, const DisplayState *ds)
{
    set_dest_bpp_from_ds(rd, ds);

    /* calculate row size */
    rd->bytes_per_dest_row = ds_get_linesize(ds);
}

/* this function rounds down odd widths */
static uint32_t calc_bytes_per_src_row(enum fb_src_bpp_mode bpp, uint32_t cols)
{
    uint32_t bytes_per_row = cols;

    switch (bpp) {
    case BPP_SRC_1:
        bytes_per_row >>= 3;
        break;

    case BPP_SRC_2:
        bytes_per_row >>= 2;
        break;

    case BPP_SRC_4:
        bytes_per_row >>= 1;
        break;

    case BPP_SRC_8:
        break;

    case BPP_SRC_15:
    case BPP_SRC_16:
        bytes_per_row <<= 1;
        break;

    case BPP_SRC_24:
        bytes_per_row *= 3;
        break;

    case BPP_SRC_32:
        bytes_per_row <<= 2;
        break;
    }

    if (bpp != BPP_SRC_24 && (bytes_per_row % 4) != 0) {
        fprintf(stderr, "Warning: setting an invalid width/depth combination, rounding down.\n");
        bytes_per_row &= ~3;
    }

    return bytes_per_row;
}

static uint32_t calc_bytes_per_dest_row(enum fb_dest_bpp_mode bpp, uint32_t cols)
{
    uint32_t bytes_per_row = cols;

    switch (bpp) {
    case BPP_DEST_8:
        break;
    case BPP_DEST_15:
    case BPP_DEST_16:
        bytes_per_row <<= 1;
        break;
    case BPP_DEST_24:
        bytes_per_row *= 3;
        break;
    case BPP_DEST_32:
        bytes_per_row <<= 2;
        break;
    }

    return bytes_per_row;
}

static row_draw_fn get_draw_fn(const render_data * rd)
{
    return fb_draw_fn[rd->dest_bpp_mode][rd->color_order][rd->byte_order][rd->pixel_order][rd->src_bpp_mode];
}

inline static void* calc_src_row_address_host(const render_data * rd, uint32_t row)
{
    return (void*)((char*)(rd->base_host) + row * (rd->bytes_per_src_row + rd->inter_src_row_gap));
}

inline static ram_addr_t calc_src_row_address_target(const render_data * rd, uint32_t row)
{
    target_phys_addr_t phys;
    phys = rd->base_target + row * (rd->bytes_per_src_row + rd->inter_src_row_gap);
    return get_ram_offset_phys(phys);
}

inline static uint8_t *calc_dest_row_address(const render_data * rd, int row)
{
    return rd->dest + rd->dest_start_offset + row * rd->dest_col_step;
}

static int is_dirty_row(const render_data * rd, uint32_t row)
{
    int dirty;
    uint32_t addr;
    uint32_t end;

    addr = calc_src_row_address_target(rd, row);
    end = (addr + rd->bytes_per_src_row - 1) & TARGET_PAGE_MASK;
    addr &= TARGET_PAGE_MASK;

    /* Iterate over all the pages belonging to this row */

    do {
        dirty = cpu_physical_memory_get_dirty(addr, VGA_DIRTY_FLAG);
        addr += TARGET_PAGE_SIZE;
    } while (!dirty && addr <= end);

    return dirty;
}

static void draw_blank_row(uint8_t *dest, uint32_t bytes)
{
    memset(dest, 0, bytes);
}


static void decode_rgb_for_palette(uint32_t data, unsigned int *r, unsigned int *g, unsigned int *b)
{
    *r = (data >> 16) & 0xff;
    *g = (data >> 8) & 0xff;
    *b = data & 0xff;
}

static void update_complete_palette(render_data * rd)
{
    int n, i;
    uint32_t raw;
    unsigned int r, g, b;

    switch (rd->src_bpp_mode) {
    case BPP_SRC_1: n = 2; break;
    case BPP_SRC_2: n = 4; break;
    case BPP_SRC_4: n = 16; break;
    case BPP_SRC_8: n = 256; break;
    default: return;
    }

    for (i=0; i<n; i++) {
        raw = rd->raw_palette[i];
        decode_rgb_for_palette(raw, &r, &g, &b);
        switch (rd->dest_bpp_mode) {
        case BPP_DEST_8:
            rd->palette[i] = rgb_to_pixel8(r, g, b);
            break;
        case BPP_DEST_15:
            rd->palette[i] = rgb_to_pixel15(r, g, b);
            break;
        case BPP_DEST_16:
            rd->palette[i] = rgb_to_pixel16(r, g, b);
            break;
        case BPP_DEST_24:
        case BPP_DEST_32:
            rd->palette[i] = rgb_to_pixel32(r, g, b);
            break;
        }
    }
}

/* Mapping from guest (row/col) to host (x/y) coordinates.  */ 
typedef struct {
    int row_x;
    int row_y;
    int col_x;
    int col_y;
} rotation_data;

static const rotation_data rotations[8] = {
      { 1,  0,  0,  1},
      { 0, -1,  1,  0},
      {-1,  0,  0, -1},
      { 0,  1, -1,  0},
      { 1,  0,  0, -1},
      { 0,  1,  1,  0},
      {-1,  0,  0,  1},
      { 0, -1, -1,  0},
};

static void update_rotation_data(render_data *rd)
{
    uint32_t bytes_per_dest_pixel;
    const rotation_data *r;

    r = &rotations[rd->orientation];

    /*rd->swap_width_height = rd->orientation & 1;*/

#if 0
    TODO DFG
    rd->bytes_per_dest_row = calc_bytes_per_dest_row(rd->dest_bpp_mode, get_screen_width(rd));
#endif
    bytes_per_dest_pixel = calc_bytes_per_dest_row(rd->dest_bpp_mode, get_screen_width(rd)) / get_screen_width(rd);

    /* Offset the start position if we are rendering backwards.  */
    rd->dest_start_offset = 0;
    if (r->row_x + r->col_x < 0)
        rd->dest_start_offset += bytes_per_dest_pixel * (get_screen_width(rd) - 1);
        /*rd->dest_start_offset += rd->bytes_per_dest_row - bytes_per_dest_pixel;*/

    if (r->row_y + r->col_y < 0)
        rd->dest_start_offset += rd->bytes_per_dest_row * (get_screen_height(rd) - 1);

    rd->dest_row_step = rd->bytes_per_dest_row * r->row_y
                        + bytes_per_dest_pixel * r->row_x;
    rd->dest_col_step = rd->bytes_per_dest_row * r->col_y
                        + bytes_per_dest_pixel * r->col_x;
}

static void update_render_data(render_data *rd)
{
    if (rd->need_internal_update) {
        rd->fn = get_draw_fn(rd);
        rd->bytes_per_src_row = calc_bytes_per_src_row(rd->src_bpp_mode,
                                                       rd->cols);
        update_complete_palette(rd);
        if (rd->row_pitch == 0)
            rd->inter_src_row_gap = 0;
        else
            rd->inter_src_row_gap = rd->row_pitch - rd->bytes_per_src_row;
        update_rotation_data(rd); /* updates bytes_per_dest_row too */
        rd->need_internal_update = 0;
    }
}


void set_palette_value(render_data *rd, uint32_t n, uint32_t value)
{
    rd->raw_palette[n] = value;
    rd->need_internal_update = 1;
}

static void render_blank_screen(render_data * rdata)
{
    int i;
    uint8_t* addr = rdata->dest;
    const uint32_t rows = get_screen_height(rdata);

    for (i = 0; i < rows; i++) {
        draw_blank_row(addr, rdata->bytes_per_dest_row);
        addr += rdata->bytes_per_dest_row;
    }
}

static void render_from_host(DisplayState *ds, const render_data *rdata)
{
    int i;

    for (i = 0; i < rdata->rows; i++) {
        rdata->fn(rdata->palette, 
                  calc_dest_row_address(rdata, i),
                  /*(uint8_t *)*/calc_src_row_address_host(rdata, i),
                  rdata->cols, 
                  rdata->dest_row_step);
    }

    if (!rdata->swap_width_height)
        dpy_update(ds, 0, 0, rdata->cols, rdata->rows);
    else
        dpy_update(ds, 0, 0, rdata->rows, rdata->cols);

}

static void render_from_target(DisplayState *ds, const render_data *rdata, int full_update)
{
    int first_dirty_row = NOT_ASSIGNED, last_dirty_row = 0;
    int i;

    for (i = 0; i < rdata->rows; i++) {
        if (full_update || is_dirty_row(rdata, i)) {
            /* FIXME: This is broken if it spans multiple RAM regions.  */
            rdata->fn(rdata->palette, 
                      calc_dest_row_address(rdata, i),
                      host_ram_addr(calc_src_row_address_target(rdata, i)),
                      rdata->cols, 
                      rdata->dest_row_step);

            if (first_dirty_row == NOT_ASSIGNED)
                first_dirty_row = i;
            last_dirty_row = i;
        }
    }

    if (first_dirty_row != NOT_ASSIGNED) {
        cpu_physical_memory_reset_dirty(
            calc_src_row_address_target(rdata, first_dirty_row), /* first row byte */
            calc_src_row_address_target(rdata, last_dirty_row + 1) - 1, /* last row byte */
            VGA_DIRTY_FLAG);

        if (!rdata->swap_width_height)
            dpy_update(ds, 0, first_dirty_row, rdata->cols, last_dirty_row - first_dirty_row + 1);
        else
            dpy_update(ds, first_dirty_row, 0, last_dirty_row - first_dirty_row + 1, rdata->cols);
    }
}

static int prepare_ds_for_rendering(DisplayState *ds, render_data *rdata)
{
    rdata->swap_width_height = rdata->orientation & 1;

    if (ds_get_width(ds) == get_screen_width(rdata) && 
        ds_get_height(ds) == get_screen_height(rdata))
        return 1;
    else
        return gui_resize_vt(ds, get_screen_width(rdata), get_screen_height(rdata));
}

void render(DisplayState *ds, render_data * rdata, int full_update)
{
    if (prepare_ds_for_rendering(ds, rdata)) {

        rdata->dest = ds_get_data(ds);
        
        set_dest_data_from_ds(rdata, ds);

        rdata->need_internal_update |= full_update;
        update_render_data(rdata);

        if (rdata->blank) {
            render_blank_screen(rdata);
        } else {
            if (rdata->base_is_in_target)
                render_from_target(ds, rdata, full_update);
            else
                render_from_host(ds, rdata);
        }
    }
}

render_data *create_render_data()
{
   render_data *rdata = qemu_mallocz(sizeof(render_data));

   /* set some defaults */

   return rdata;
}

void destroy_render_data(render_data *rd)
{
    qemu_free(rd);
}

void qemu_put_render_data(QEMUFile *f, const render_data *s)
{
    int i;

    qemu_put_be32(f, s->base_is_in_target);
    if (s->base_is_in_target)
        qemu_put_be64(f, s->base_target);
    qemu_put_be32(f, s->cols);
    qemu_put_be32(f, s->rows);
    qemu_put_be32(f, s->row_pitch);
    qemu_put_be32(f, s->pixel_order);
    qemu_put_be32(f, s->byte_order);
    qemu_put_be32(f, s->color_order);
    qemu_put_be32(f, s->src_bpp_mode);
    qemu_put_be32(f, s->orientation);
    qemu_put_be32(f, s->blank);
    for (i = 0; i < 256; i++)
        qemu_put_be32(f, s->raw_palette[i]);
}

void qemu_get_render_data(QEMUFile *f, render_data *s)
{
    int i;

    s->base_is_in_target = qemu_get_be32(f);
    if (s->base_is_in_target)
        s->base_target = qemu_get_be64(f);
    s->cols = qemu_get_be32(f);
    s->rows = qemu_get_be32(f);
    s->row_pitch = qemu_get_be32(f);
    s->pixel_order = qemu_get_be32(f);
    s->byte_order = qemu_get_be32(f);
    s->color_order = qemu_get_be32(f);
    s->src_bpp_mode = qemu_get_be32(f);
    s->orientation = qemu_get_be32(f);
    s->blank = qemu_get_be32(f);
    for (i = 0; i < 256; i++)
        s->raw_palette[i] = qemu_get_be32(f);
    s->need_internal_update = 1;
}