Page 1 of 1

Speed improvement guidelines

Posted: Fri Aug 19, 2016 11:28 pm
by Elwood
Is there some information somewhere about how should one write Hollywood code to reach faster execution?

I have seen some reports saying that an Hollywood program gets slower when a lot of functions are used with variables being passed to the function. And instead, if global variables are used and code is written sequentially without using functions, everything is faster.

Is that true? and why? I mean many other languages use functions and argument passing and they don't slow down in big factors. Is it because of some bad coding writing Hollywood code (not Hollywood itself :-) )?

Thanks.

Re: Speed improvement guidelines

Posted: Sat Aug 20, 2016 12:43 pm
by Bugala
I havent gone too much into speed issues, but here are some things.

1. I recall Andreas have mentioned that It is faster to use Add() function than to use "c=a+b", i suppose this applies to other mathematical functions as well.

2. When it comes to using moving graphics, there comes big difference wether you use Sprites, Layers or something else. You need to decide yourself which suits your program best, as they all have some differences between them.

3. Some of the functions might have difference between other platforms. As example, if you use Layers in Android Tablets, it can become very slow in some situations. I had this system where I was drawing a map by placing tile graphics one by one, as i used layer system so that it would update the graphics every time one graphics tile is put, while it wasnt very noticeable on computer, it was very slow on Android. To fix that problem, was to DoubleBuffer system and only update graphics after all the tiles had been placed. After that, there was no noticeable difference in Android Tablet compared to computer (if i recall right).

4. When it comes to using music files, Ogg is too heavy for Classic Amigas, MP3 will play on classic amigas, but is very heavy. Hence, WAV or MOD are the solutions if you wish for music to play on Classic Amigas as well.

As a general rule to all programming languages of today, to my understanding there is no point in optimising the code the old way anymore, meaning that instead of using:

Code: Select all

Repeat
a=a+1
until a=100 
You would use

Code: Select all

repeat
a=a+1
a=a+1
a=a+1
a=a+1
a=a+1
until a=100
This kind of optimization does not work anymore, as compilers themselves optimize the code, and if you do something like that, it might even limit compilers options on optimizing it better.

Re: Speed improvement guidelines

Posted: Sun Aug 21, 2016 1:06 am
by zylesea
The biggest bottleneck is (according to my experience) the gfx drawing speed. With V6.0 I used disablelinehook()/enablelinehook() quite a bit and got some speed imrovements. On Android use beginrefresh()/endrefresh() to achieve good gfx speeds.

Re: Speed improvement guidelines

Posted: Sun Aug 21, 2016 11:32 am
by Elwood
Thanks. I wasn't aware of disablelinehook(). Will try that.

Re: Speed improvement guidelines

Posted: Mon Aug 22, 2016 11:41 am
by airsoftsoftwair
Bugala wrote:1. I recall Andreas have mentioned that It is faster to use Add() function than to use "c=a+b", i suppose this applies to other mathematical functions as well.
Huh, what? No! :-) Forget about Add(), Mul(), Div() and Sub(). These are relics from Hollywood 1.x which didn't have any support for expressions. By all means, use the operators +, *, /, - directly. Using Add() instead of + will seriously throttle your script because operators are direct opcodes in the VM whereas Add() etc. involves function calls into C and is very, very slow. It's really only there for compatibility.
zylesea wrote: The biggest bottleneck is (according to my experience) the gfx drawing speed
That's true because it is mostly implemented in software. However, you now have the option to use GL Galore if you need optimized drawing speed. Using a hardware double buffer with hardware brushes on GL Galore should give you excellent drawing speed.

Re: Speed improvement guidelines

Posted: Mon Aug 22, 2016 12:00 pm
by Bugala
ah, guess i remembered wrong. Anyway, i had ignored that wrong memory anyway, since as you pointed it out too, it is so much more readable to use a=b+c than putting Add() etc.

Re: Speed improvement guidelines

Posted: Wed Sep 07, 2016 9:16 pm
by TheMartian
Hi

A couple of years ago I wrote an article on the subject (Speeding up Hollywood programs). I don't have the link to the website, where it was published anymore. But it provides some ideas for simple things to do that can pay off nicely. I think I found the draft, and it is included here below...

---ooo0ooo---
Sections of code can often be made to run faster if you put some efforts into it, for instance by investigating alternative ways of expressing statements. A promising place to start is to look for ways of exploiting the simple operators available as a replacement for perhaps more readable but also slower system functions. This article is an example of how much there is to be gained using this approach.

The test program below is a user defined function called TimeSplit, whose purpose is to resolve a specified number of seconds into the number of days, hours, minutes and seconds it represents. A not unlikely scenario.

@DISPLAY{width=1024,height=768}

Const #day=24*60*60 ; 86400 seconds per day
Const #hour=60*60 ; 3600 seconds per hour
Const #minute=60 ; 60 seconds per minute
Const #secs = 400000 ; seconds to be converted in test

Function TimeSplit(testsecs)
d=Int(testsecs/#day)
h=Int((testsecs-d*#day)/#hour)
m=Int((testsecs-d*#day-h*#hour)/#minute)
s=testsecs-d*#day-h*#hour-m*#minute
Return(d,h,m,s)
EndFunction

repetitions=100000

StartTimer(1)
For x=1 to repetitions
days,hours,minutes,seconds=TimeSplit(#secs)
Next
t=GetTimer(1)
StopTimer(1)
NPrint(days,hours,minutes,seconds,"Time spent: "..t.."\n")

WaitLeftMouse
End

On my SAMep 440 it executes in about 3230 milliseconds. So what can be done to make things go faster?

Local variables
===============
It is well known, that local variables are faster than global variables. For this experiment I will not modify the for-next counter (x) to be local allthough it would speed up things. But we are interested in the actual code and not the speed of the test loop as such. So change the code to...

Function TimeSplit(testsecs)
Local d=Int(testsecs/#day)
Local h=Int((testsecs-d*#day)/#hour)
Local m=Int((testsecs-d*#day-h*#hour)/#minute)
Local s=testsecs-d*#day-h*#hour-m*#minute
Return(d,h,m,s)
EndFunction

This brings the execution time down to about 2980 milliseconds. Not bad for the first day - almost a 10% increase with hardly any effort. But more can be gained on this front. Modify the second part of the test code as follows. Notice I have added a Block-EndBlock statement around it to force a 'local' context for the definition of the variables days,hours,minutes,seconds. You cannot put the local statement inside the for-next loop because it is in itself a block, and printing the values of the four variables would then return nil outside of the for-next loop.

Block
Local days,hours,minutes,seconds
StartTimer(1)
For x=1 to repetitions
days,hours,minutes,seconds=TimeSplit(#secs)
Next
t=GetTimer(1)
StopTimer(1)
NPrint(days,hours,minutes,seconds,"Time spent: "..t.."\n")
EndBlock

The modification brings the execution time down to around 2770 milliseconds. So what is next on the agenda?

The Integer division operator (\)
=================================

The code repeatedly divides a certain sum of seconds with another number, and then turns it into an integer using the Int() function. We can do better. Hollywood has an Integer division operator that effectively does the same as the Int() function without the overhead inherent in the use of a function. So change the code like this...

Function TimeSplit(testsecs)
Local d=testsecs\#day
Local h=(testsecs-d*#day)\#hour
Local m=(testsecs-d*#day-h*#hour)\#minute
Local s=testsecs-d*#day-h*#hour-m*#minute
Return(d,h,m,s)
EndFunction

Applause please! Execution time just dropped to around 1790 milliseconds. But have we run out of options yet?

The division remainder operator (%)
===================================

Well, not quite. Hollywood also has another useful operater called division remainder, which returns, not the result of a division, but the remainder, which is indeed what we want to calculate in some of the code lines. We can shorten our intermediate calculations as shown...

Function TimeSplit(testsecs)
Local d=testsecs\#day
Local h=(testsecs%#day)\#hour
Local m=(testsecs%#hour)\#minute
Local s=testsecs%#minute
Return(d,h,m,s)
EndFunction

This brings us down to around 1690 milliseconds.

To get the true picture of the effect of our modifications we now have to calculate the time used to execute the for-next loop instruction itself. So modify the code like this...

For x=1 to repetitions
days,hours,minutes,seconds=TimeSplit(#secs)
days,hours,minutes,seconds=TimeSplit(#secs)
Next

This time it executes at around 3170 milliseconds. The difference is 1480 milliseconds. So 2*1480+looptime=3170 which means the loop itself takes around 210 milliseconds.

The original code then used 3230-210=3020 milliseconds, whereas our final attempt clocked in at 1690-210=1480 milliseconds, or in other words it runs a little more than twice as fast.

There is one final thing to consider in this example. What happens if you use local variables or explicit numbers instead of constants? As far as I can test this will not bring any further speed increases. In fact it slows things down a few milliseconds. So I selected to use constants in my example only.

Oh, another thing... If you run tests like this you should not expect to get exactly the same result every time. My results differ up to 20-30 milliseconds between runs and so will yours probably.

The results you get also depends on the platform used for testing. I ran the same tests on a fast PC. It seems the gains from switching to local variables are greater and the advantage of using simple operators instead of functions somewhat smaller. Since the PC is much faster than the SAMep 440 I multiplied the number of repetitions in the test loop by 10. The table below compares the results from the two platforms:

SAMep 440 PC
100000 repetitions 1000000 repetitions

3220 1360
2980 1180 (local d,h,m,s)
2770 1100 (local days,hours,minutes,seconds)
1790 920 (integer division)
1690 780 (division remainder)

210 50 (Time spent on loop itself)

So if we take out the time used for the loop itself the results are:

3010/1480=49% 1310/730=56%
---ooo0ooo---

I had some other articles in draft versions, which you might find useful. I can't promise they behave similarly in Hollywood 6.1. But again it proves that there is something to be had from experimenting with different approaches.


---ooo0ooo---

The essential functionality of printing to the display is handled by the three commands - Print(), NPrint() and TextOut(). Getting your program to print as fast as possible to the display therefore makes a lot of sense. As it turns out there are ways to code, that can cause dramatic increases in speed - or slow it down. This article will tell you what I have discovered so far about fast printing.

The basic test program used look like this (You can adjust the size of testloops depending on which machine you are testing on. For a fast pc you may try with 10000. On a SAMep440 you should stick to 1000.). Results shown here are from my pc.


testloops=10000

SetFont("Topaz",8,{#FONTENGINE_INBUILT,True})

StartTimer(1)
For x=1 To testloops
Locate(0,0)
NPrint("123456789012345")
NPrint("123456789012345")
NPrint("123456789012345")
NPrint("123456789012345")
Next
t=GetTimer(1)
StopTimer(1)
Locate(0,100)
Print("Time: "..t)

WaitLeftMouse
End

Execution time around 700 .



Replace multiple NPrint() with the '\n' (NEWLINE) control code:
===============================================================
Executing NPrint() for each line is inefficient. You can make do with just one NPrint() command by inserting '\n' (NEWLINE) control codes as follows:

For x=1 to testloops
NPrint("123456789012345\n123456789012345\n123456789012345\n123456789012345")
Next

Execution time around 385.

BEWARE!
There is a potential problem here. If the TextWidth of your string (including control codes) is close to or larger than the width of your display, printing is slowed down a lot. As far as I can see you need to leave space for at least two character to the right, or suffer the consequences. This goes for fixed as well as proportional fonts. So if you use a fixed font of width 8 and you have a 640 pixel display width there is room for 78 characters - but 79 will cause a slow down. In our example if you added another section as in...

For x=1 to testloops
NPrint("123456789012345\n123456789012345\n123456789012345\n123456789012345\n123456789012345")
Next

Execution time around 1230

... you would have 79 characters (yes, control codes have a width even if they are not displayed). You can not fool the system by making a string like...

a$=[[123456789012345
123456789012345
123456789012345
123456789012345
123456789012345]]

...There are NEWLINE characters in the string (Remember you pressed ENTER). So the sum of the characters is still 79.

In the end you will therefore have to use more than one NPrint() statements if you need to print several lines and their total length exceeds the display width.



Choice of font
==============
In recent versions of Hollywood there are three inbuilt Truetype fonts. These fonts together with the basic Topaz,8 font are way faster than any other Truetype font you may select. Since Topaz is a fixed size font it is not useful for anything but basic printing while proportional fonts in size 8 are unreadable. So for the rest of this article I will switch to font size 16 and the test program will look like this:


SetFont(#MONOSPACE,16,{#FONTENGINE_INBUILT,True})

StartTimer(1)
For x=1 To testloops
Locate(0,0)
NPrint("123456789012345\n123456789012345\n\t123456789012345\n123456789012345")
Next
t=GetTimer(1)
StopTimer(1)
Locate(0,100)
Print("Time: "..t)

WaitLeftMouse
End

Execution time around 515 ms.

Of the three inbuilt Truetype fonts #MONOSPACE is by far the fastest to print (515) followed by #SERIF (660) and #SANS.(785). If you select TrueType fonts available on your system like "Arial" on a PC or "DejaVu Serif" on an Amiga, they are much slower. Arial clocks in at (33350) on my PC. On the SAMep440 the #MONOSPACE font wins by an even larger margin compared to other fonts though the pattern among the fonts as to their relative speed remains. But then - #MONOSPACE is a fixed size font while #SERIF and #SANS are proportional. So for real life use you should probably stick with #SERIF or #SANS. They also have the advantage that they work on all platforms.



Font engine & glyphcache
========================
You can use Hollywoods inbuilt font engine (#FONTENGINE_INBUILT) or the font engine of your computer (#FONTENGINE_NATIVE). To be honest I can't see much difference. But again using Hollywoods inbuilt engine makes for better portability.

The same goes for using the glyphcache or not. It is on by default, but the effect is very limited (the manual states that it may be more effective on 'old' Amigas with OS3.). Try it if you run on such a platform.



Using TABs
==========
You can embed other control codes like the TAB '\t' code in your text. It is a flexible approach but also a real time killer. Try this...

SetFont(#MONOSPACE,16,{#FONTENGINE_INBUILT,True})
AddTab(80)
StartTimer(1)
For x=1 To testloops
Locate(0,0)
NPrint("123456789012345\n123456789012345\n\t123456789012345\n123456789012345")
Next
t=GetTimer(1)
StopTimer(1)
Locate(0,100)
Print("Time: "..t)

WaitLeftMouse
End

Execution time around 1760.

Modify the test loop like this...

For x=1 To testloops
Locate(0,0)
NPrint("123456789012345\n123456789012345")
Locate(80,32)
NPrint("123456789012345\n123456789012345")
Next

Execution time arount 645.

So despite having to include a Locate() and a NPrint() statement this second solution was nearly three times as fast. If you know where to locate you text the TAB control code clearly comes in second place.



Embedded formatting codes:
==========================
Embedding formatting codes is very flexible and in some scenarios the right thing to do. But in other cases they slow things down. Let us see some examples...

For x=1 To testloops
Locate(0,0)
NPrint("123456789012345\n[color=#GRAY]123456789012345\n123456789012345\n[color=#WHITE]123456789012345")
Next

Execution time around 1625.


For x=1 To testloops
Locate(0,0)
NPrint("123456789012345")
SetFontColor(#GRAY)
NPrint("123456789012345\n123456789012345")
SetFontColor(#WHITE)
NPrint("123456789012345")
Next

Execution time around 750.

Again we see that from a speed perspective the [color=??] embedded formatting code is bad for business. However if we test the [\b] (BOLD) formatting code it is a better solution than using multiple NPrint() and SetFontStyle() commands. The bad news is that printing in BOLD or iTALICS in general is very slow.

For x=1 To testloops
Locate(0,0)
NPrint("123456789012345\n123456789012345\n123456789012345\n123456789012345")
Next

Execution time around 9000.

For x=1 To testloops
Locate(0,0)
NPrint("123456789012345\n123456789012345")
SetFontStyle(#BOLD)
NPrint("123456789012345")
SetFontStyle(#NORMAL)
NPrint("123456789012345")
Next

Execution time around 11930.


Using TextOut
=============
The TextOut command is preferable to Print or NPrint in almost any way. It has far more options, and one of its few weaknesses is that it does not print to the 'current cursor position' but to specific coordinates. Also it does not work with the '\t' control code as far as I can see. Interestingly it seems to be slightly faster than NPrint.

For x=1 To testloops
TextOut(0,0,"123456789012345\n123456789012345\n123456789012345\n123456789012345")
Next

Execution time around 475. (As compared to around 515 for NPrint).


My conclusion?!.... Stick to using the inbuilt three fonts (or Topaz if you can live with its fixed format). Use '\n' but not '\t' and be careful about embedding formatting codes. Some are good - some are bad. So test first. Use TextOut if possible.


---ooo0ooo---

And another...

There can be a huge difference between what works well on the Amiga SAMep440 and in a Windows environment. The results probably also differ on other platforms supported by Hollywood. Try it out on your favorite platform yourself.



Consider these three code sequences. They all try to act as a for-next loop with one line of code to be repeatedly executed (d=d+1).


a)
Local x,d=1,1
For x=1 To 1000000
d=d+1
Next


b)
Local x,d=1,1
While x>1000001
x=x+1
d=d+1
Wend


c)
Local x,d=1,1
Repeat
x=x+1
d=d+1
Until x>1000000



On the Amiga the For-Next loop runs much faster that the While-Wend loop. It is nearly twice as fast. Also the Repeat-Until loop runs at the same speed as the While-Wend loop. But on my PC the While-Wend loop is nearly 50% faster than the For-Next loop despite the fact that it includes extra code to increment the x-counter. The Repeat-Until loop is also faster than the For-Next loop by about 20%, but noticably slower than While-Wend. Why - I haven't got a clue! The result surprised me so much I tested repeatedly over a couple of days. Yet it was consistent on two different PCs with assorted code between the start and the end of the loop.


Another example that you cannot assume similar effects on different platforms is the following 'pointer' trick, that is very useful because it speeds up your code and saves you some writing as well, especially if you have tables with many items like when you use a table to hold properties and methods of an object.


Assume that you have a multilevel table hierarchy like this:

obj={}
obj.gfx={}
obj.gfx.xbox={}
obj.gfx.xbox.name$="Fred Flintstone"
obj.gfx.xbox.age=40
obj.gfx.xbox.myFunction=Function(arg) Return(arg*2) EndFunction


The code lines...


for x=1 to 1000000
a$=obj.gfx.xbox.name$
b=obj.gfx.xbox.age
c=obj.gfx.xbox.myFunction(33)
next


will run faster if coded like this...

Local ptr=obj.gfx.xbox
for x=1 to 1000000
a$=ptr.name$
b=ptr.age
c=ptr.myFunction(33)
next

On my PC the second solution runs around 25-30% faster. On the Amiga it is 15-20% faster. It usually pays off, if you have a global reference to a table inside a function or other block structure, to make a local variable pointing to the same address and use that instead if there is more than a few items from the table being referenced inside the block. The local variable is ditched afterwards by the garbage collector as you leave the block, and the globally referenced table still has the changes.

---ooo0ooo---

And yet more...

You might think that printing to the display is... just printing. Well, I hope to convince you that there are major speed improvements to be found by thinking about exactly how you do your printing.

You can output text to the display using Print, NPrint and TextOut. The first two differs little apart from a newline character added to the output, so this test will mainly work with NPrint and TextOut.

By default Hollywood will use the "Topaz",8 font on a 640 x 480 display. Topaz is a fixed size font of width 8. There is then room for 80 characters in a line on the display.

Test this program first:

StartTimer(1)
For x=1 To 10000
Locate(0,0)
NPrint("123456789012345")
NPrint("123456789012345")
NPrint("123456789012345")
NPrint("123456789012345")
Next
t=GetTimer(1)
StopTimer(1)

NPrint("time: "..t)

WaitLeftMouse
End


This will print 4 lines of text in about 720 ms. But repeating the NPrint command 4 times is clearly inefficient. We can take advantage of the newline control character to do it with just one NPrint statement like this:

NPrint("123456789012345\n123456789012345\n123456789012345\n123456789012345")

Now it runs at around 380 ms. So it is obviously much better, if you need to print multiple lines, to embed newline characters '\n' instead of using multiple NPrint statements. However there is a potential trap here. Modify the expression by adding an extra section like this:


NPrint("123456789012345\n123456789012345\n123456789012345\n123456789012345\n123456789012345")

Suddenly it runs much slower at 1225 ms. It turns out that if your string is less than three 'character widths' from the right border of the display (as far as I can establish in my tests) it slows down. This happens with proportional fonts too except that the number of characters are different because of their variable width. In our case the string had grown to 75 regular characters plus 4 control characters - all in all 79 characters. That is one too many because there is only space for a total of 80-2=78 characters before we hit the limit. If you just remove one character from the string or if you redefine the display to be wider it runs fast again. it does not help to use the alternative way of defining a string as in:

a$=[[123456789012345
123456789012345
123456789012345
123456789012345
123456789012345]]

In effect the program adds the four newline characters anyway, and so you end up with 79 characters after all.

Let us test another control character. The TAB character '\t'. First we have to set a tab using the AddTab() command, in this case after 10 characters (10 x 8 = 80).


AddTab(80)
StartTimer(1)
For x=1 To 10000
Locate(0,0)
NPrint("123456789012345\n123456789012345\n123456789012345\n\t123456789012345")
Next
t=GetTimer(1)
StopTimer(1)

NPrint("time: "..t)

WaitLeftMouse
End


Execution time is 1090 ms. It turns out that it is much faster to use an explicit Locate() command and an extra NPrint() like this for our test loop:


Locate(0,0)
NPrint("123456789012345\n123456789012345\n123456789012345")
Locate(24,80)
NPrint("123456789012345")

This runs at around 490 ms.

So you should think carefully before using the TAB control code. It is very flexible in use but also very slow.


You can embed formatting codes in your text. Like with the TAB code there is sometimes a price to pay in performance for their flexibility. Try this...

Locate(0,0)
NPrint("[color=#GRAY]123456789012345\n123456789012345\n123456789012345\n123456789012345")

Execution time around 1020 ms

Locate(0,0)
SetFontColor(#GRAY)
NPrint("123456789012345\n123456789012345\n123456789012345\n123456789012345")

Execution time around 380 ms.

If you want to switch colour for an entire string there is no discussion. Even if you have to set the font back to its previous colour, it is much faster to have two SetFontColor() statements and an extra Print statement or two, than using the embedded formatting code. As an example see this where we assume that we originally had a #BLUE font color in effect.

Locate(0,0)
SetFontColor(#GRAY)
NPrint("123456789012345\n123456789012345\n123456789012345\n")
SetFontColor(#BLUE)
Print("123456789012345")

Execution time around 500 ms.

On the other hand it matters little whether you use SetFontStyle(#BOLD) or use the formatting codes. Of course SetFontStyle() affects any text afterwards while the formatting codes only affect a section of a string. So here the choice is a matter of what you want to do. Generally speaking printing in #BOLD or #ITALIC etc. is very slow. As an example this string executes in around 1140 ms...

Locate(0,0)
NPrint("123456789012345\n123456789012345\n123456789012345\n123456789012345")



When using truetype fonts it seems that output via TextOut() is considerably faster than NPrint(). Particularly if you do not use one of the inbuilt fonts.

Using one of the inbuilt truetype fonts is just so much faster than other truetype fonts with #MONOSPACE being the fastest followed by #SERIF and #SANS. The basic bitmap font 'Topaz',8 is also very fast, but of course only available in its fixed size.

Here are some numbers for comparison using this test program (You can comment out the part not in use when testing either NPrint or TextOut):

SetFont("Topaz",8,{#FONTENGINE_INBUILT,True})

StartTimer(1)
For x=1 To 10000
;Locate(0,0)
;NPrint("123456789012345\n123456789012345\n123456789012345\n123456789012345")
Textout(0,24,NPrint("123456789012345\n123456789012345\n123456789012345\n123456789012345")
Next
t=GetTimer(1)
StopTimer(1)
SetFont(#MONOSPACE,16)
NPrint("time: "..t)

WaitLeftMouse
End


To be honest a truetype font in size 8 in too small to be of any use whereas topaz, 8 is easy to read. So truetype fonts are tested with size 16. Size 8 is just shown for comparison. First number is for NPrint/Second number is for TextOut. The effect of the Locate() command when testing NPrint() can be ignored (4 ms). Setting glyphcache to False also has virtually no effect on my test system for this test (a fast PC). Finally there seems to be hardly any difference in speed between using the inbuilt or native font engine. For reasons of portability the inbuilt font engine is probably your best choice.

#MONOSPACE - 517/475 (size 8: 320)
Topaz,8 - 375/369
#SERIF - 672/566 (size 8: 465)
#SANS - 781/661 (size 8: 578)

"Arial" - 33300/25235



TextOut is the superior choice when compared to Print or NPrint in most situations. Just about the only feature about Print or NPrint that TextOut can't deliver is the ability to print where the cursor is currently. Also a control code like TAB does not work whereas NEWLINE do work.


---ooo0ooo---

As I said... These figures are for an older version of Hollywood (Written in 2012 and I am usually updating Hollywood as soon as a new version somes along). But my guess is that most of the tricks can still apply to version 6.1.

NB! Two of the articles deals more or less with the same subject (printing to the screen). They are preliminary drafts and there are some differences. So I included them both anyway.



regards
Jesper