File: HOWTO-DIRTYRECT

package info (click to toggle)
atari800 5.2.0-2
  • links: PTS, VCS
  • area: contrib
  • in suites: forky, sid, trixie
  • size: 7,196 kB
  • sloc: ansic: 86,829; asm: 18,694; sh: 3,173; cpp: 2,798; java: 2,453; xml: 957; makefile: 727; perl: 334; pascal: 178
file content (116 lines) | stat: -rw-r--r-- 5,506 bytes parent folder | download | duplicates (11)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
USING DIRTY UPDATE SCHEME

1. Why dirty updates?
Most Atari 800 games and applications update only very small part of
the screen every frame. Typical numbers range from 1% to 5%. On the
other hand many ports have to convert screen image from internal
emulator format to port native format and copy it to platform video
memory. Both conversion and copy (or blit) are typically slow.

2. How to enable it?
To enable dirty update tracking the symbol DIRTYRECT must be #defined
globally (in CONFIG.H or in project settings). When it is defined the
array screen_dirty[] gets allocated. This is UBYTE array of the size
ATARI_HEIGHT*ATARI_WIDTH/8. In current implementation every element
is either 0 or 1. Initially the entire array is initialized with 0.
Each element in this array corresponds to eight consequtive pixels in
atari_screen[]. When any of this pixels is being changed the element
in screen_dirty[] is getting set to 1. Port's implementation of
Atari_DisplayScreen() can use this information and draw only those
pixels belonging to "changed" octets. Port must reset screen_dirty[]
at the end of its Atari_DisplayScreen(). Emulator core _never_ resets
screen_dirty[] elements.

3. How to use it?
The simplest implementation would be this: every time your code is
attempting to process pixel at pointer src (which must point somewhere
_inside_ atari_screen[]) do a check:

    if(screen_dirty[((ULONG)src-(ULONG)atari_screen)/8]) ...

If the condition succeeds, update the pixel. Otherwise, skip it. More
advanced implementation would do a check only once per eight pixels
but the check basically remains the same. The best use is to redesign
the entire update loop so it moves through the array screen_dirty[]
instead of typical loop through atari_screen[] or double loop through
screen coordinates x and y. Essentially, if screen_dirty[m]==1 then
eight pixels starting at atari_screen[m*8] needs to be refreshed. Or,
if you want these in screen coordinates: y=m/ATARI_WIDTH, eight pixels
starting at m%ATARI_WIDTH. Note that pixels are guaranteed to be at
the same scanline. Properly written loop would not even use division
there:

    for(m=0, x=0, y=0; m<ATARI_HEIGHT*ATARI_WIDTH/8; m++)
    {
        if(screen_dirty[m])
        {
            /* Draw eight pixels (x,y), (x+1,y),...,(x+7,y) here */
            ...
            screen_dirty[m] = 0;
        }
        x += 8;
        if(x >= ATARI_WIDTH)
        {
            x = 0;
            y ++;
        }
    }

The code in that last example also resets screen_dirty[] as it goes
and does it in very efficient way. If your code does not do that you
can just use:

    memset(screen_dirty, 0, ATARI_HEIGHT * ATARI_WIDTH/8);

at the end of Atari_DisplayScreen().

4. Is it worth a trouble?
Believe me, yes, unless your target platform is so fast that you don't
care about the performance. I tested this implementation on PocketPC
(WinCE) port on iPaq. Old implementation was too slow even with
frameskip of 3 (anything higher tends to break PM graphics). With the
new implementation I am getting better speed at frameskip 1 than I had
with the old implementation at frameskip 3, and I finally got 100%
speed at frameskip 2! At the same time I am using linear filtering
with the new implemenation, something that was too expensive with the
old one. On the top of this, I am not using the most efficient
(described above) way to use the scheme.
There are only a few little catches there. First, you should use
compiler with at least semi-decent optimizer. Otherwise you will get
a few extra memory moves every time you access video memory. It's not
a big problem but it is something to be aware of. Second, there is
slight performance hit in ANTIC.C: changed pixels are slightly more
expensive (unchanged pixels stay the same, may be even cheaper than
before depending on platform memory controller). That only means that
if you #define DIRTYRECT you should definitely implement proper dirty
update support in your port or you'll burn CPU cycles. Then again, the
impact is not that big.

5. Changing core emulator code.
If you need to draw anything on the screen from the emulator core you
need to follow certain protocol. First of all, you cannot use
atari_screen[] directly anymore. If your code resides in ANTIC.C you
should use macros WRITE_VIDEO_BYTE(), WRITE_VIDEO() (writes a word),
WRITE_VIDEO_LONG(), and FILL_VIDEO(). There is one case when code
reads from atari_screen[], that one uses macro READ_VIDEO_LONG() but
I would strongly discourage everybody from doing tricks like that.
For code outside of ANTIC.C there are two simple functions:
video_putbyte() and video_memset(). Those should suffice in most
situations.

6. Other notes.
With current set of macros the emulator core is shielded from actual
atari_screen[] implementation. By all means, atari_screen can be fake
pointer now. It is possible to create set of macros that will cause
port code (outside of emulator core) to draw pixels directly to the
screen as emulator generates them, potentially saving some CPU clocks
(just replace screen_dirty[] changes with calls to client functions).
It is also possible to move atari_screen[] allocation completely to
the client side. Not sure if anybody needs these features, but they
come free. Another interesting use of dirty update implementation
would be very efficient way to record movies straight from the
emulator: no need to calculate diff frames anymore. This is rather
fancy feature but you never know.

Vasyl
04/07/2002