diff -r ffa851df0825 -r 2fb8b9db1c86 symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/fb_render_engine.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/fb_render_engine.c Fri Jul 31 15:01:17 2009 +0100 @@ -0,0 +1,627 @@ +/* + * 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; iraw_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; +}