EVN Map Editor

QUOTE (Tycho @ May 19 2010, 10:50 PM) <{POST_SNAPBACK}>

flickering occurs on the windows build because that OS does not double buffer its display.
the realbasic canvas class i am using to draw the map has a property called doubleBuffer which I set to true if the target platform is win32. I'm assuming this will be ignored on win64.
i may end up having to implement my own offscreen buffer for the canvas if i can't resolve tidily.

Just create an off-screen Picture of the same dimensions as the display, draw to it, and then draw it to the display. That's what I did in MissionComputer until it became Mac OS X-only, and will be doing again if the Windows version progresses.

When I was writing in SDL (linux amd64), I had flickering problems. Adding SDL_Delay(1) (sleep for 1 ms) to my video thread fixed the problem. My computer doesn't support SDL_HWSURFACEs, so SDL_DOUBLEBUF has no effect (unless the docs are lying again).

thanks krugeruwsp, the mockup and description is exactly what i needed. i will use it as a basis for milestone 1.

i'll keep spob editing in mind while creating the syst editor. i should be able to reuse code. having dudes/pers fly around in an editor sounds like fun!
i will look into using quicktime to handle all images. i expect EVN does this. however, your idea of just using place holders instead of picts is a great one.

re: graphics buffer
thanks DA. that's what i was thinking, great to have confirmation from your experience.
i was also thinking on using a timer control for canvas refreshes. i currently am refreshing only if the mouse does something within the canvas. the thoughts of using a timer would be future proofing the code, in the case that more controlled(timed) refreshes of the canvas are required for future developments. the negative is the extra load on CPU/GPU.
q: do you think this is a valid thought worth implementing now?

NC: how is 64-bit support for SDL these days?

This post has been edited by Tycho : 20 May 2010 - 10:00 PM

You should probably redraw the screen (except when iconized) several times a second at least. This is cheap, especially if you have your offscreen buffer there.

To avoid delays, you actually need 3 buffers: one to blit to the screen, one to write to, and one to queue. Then just have flip and tick routines that lock a mutex and change which buffer is which. You can write a throttle into your tick.

CODE

//program start
screen is displaying from buffer 0//which is garbage
renderer finishes an update to buffer 1, selects buffer 2
screen finishes blit, uses buffer 1
screen finishes, uses buffer 1
renderer finishes update to buffer 2, switches to buffer 0
screen finishes, uses buffer 2
renderer finishes updating to buffer 0, selects buffer 1
renderer finishes updating to buffer 1, selects buffer 0
screen finishes update to buffer 2

You need 2 threads:
screen updater
renderer
You need 3 variables:
Buffer being written
Buffer to be read next
Buffer being read
The screen updater unconditionally sets being_read = read_next (which might already be the same value)
When the renderer finishes, it sets read_next = being_written; being_written = 3 - being_written - being_read, to ensure that the screen updater has the newest possible buffer.
(You can do it with 2, but then you need to write a delay. Hardware double buffering does this, but it knows magic about screen updating. I actually developed this to safely transfer nonvideo data, btw)

SDL 1.2 works just fine on normal 64-bit system. Performance is just fine without hardware support. (OpenGL can be unreasonable)

Though it has some awful code dealing with systems with no 64-bit integral type... (from SDL 1.3 svn/hg)

CODE

#ifdef SDL_HAS_64BIT_TYPE
//...
#else
/* This is really just a hack to prevent the compiler from complaining /
typedef Sint32 Sint64;
typedef Uint32 Uint64;
#endif
//...
#define SDL_COMPILE_TIME_ASSERT(name, x) \
typedef int SDL_dummy_ ## name((x) * 2 - 1)
//...
#ifndef NINTENDODS /
TODO: figure out why the following happens:
include/SDL_stdinc.h:150: error: size of array 'SDL_dummy_uint64' is negative
include/SDL_stdinc.h:151: error: size of array 'SDL_dummy_sint64' is negative */
SDL_COMPILE_TIME_ASSERT(uint64, sizeof(Uint64) == 8);
SDL_COMPILE_TIME_ASSERT(sint64, sizeof(Sint64) == 8);
#endif

are you writing stuff for the DS? i'd like to get started on that platform.

cheers for the buffer stuff. i'll implement what you describe.

source code of 0.3a is now in the google project svn repository.
builds can be downloaded there as well.

send me a message if you need help getting the code and compiling it.
-----------------------------------
~later that day~
here's 3 different implementations of display buffering.
1. Default RealBasic doubleBuffer
2. David Arthur's doubleBuffer
3. Nonconventional's quadBuffer
----------------------------------------
1. no change
2. no flicker, bad responsiveness as syst count increases
3. no flicker, bad responsiveness as syst count increases
i will look into improving my draw code.

This post has been edited by Tycho : 22 May 2010 - 07:29 AM

QUOTE (Tycho @ May 20 2010, 06:42 PM) <{POST_SNAPBACK}>

was also thinking on using a timer control for canvas refreshes. i currently am refreshing only if the mouse does something within the canvas.

Redrawing when something changes has always been enough with any of the purposes for which I've used it

QUOTE (Tycho @ May 22 2010, 01:05 AM) <{POST_SNAPBACK}>

2. no flicker, bad responsiveness as syst count increases

This just means your code needs optimisation — MissionComputer's galaxy editor took forever to render when I first wrote it, but now it happens more or less instantly whatever the number of systems, thanks to the compartmentalisation of the code and the avoidance of slow functions.

QUOTE (David Arthur @ May 22 2010, 02:53 PM) <{POST_SNAPBACK}>

This just means your code needs optimisation — MissionComputer's galaxy editor took forever to render when I first wrote it, but now it happens more or less instantly whatever the number of systems, thanks to the compartmentalisation of the code and the avoidance of slow functions.

is this on windows? mac speeds are fine

QUOTE (Tycho @ May 22 2010, 12:38 PM) <{POST_SNAPBACK}>

is this on windows? mac speeds are fine

I haven't tested it on actual Windows, but it's perfect on WINE, and I never had any trouble related to double-buffering in Mac OS 9 (which in this respect has more in common with the other systems than with Mac OS X).

REALbasic code is very susceptible to optimisation. There are many examples of two functions that serve the same purpose but take different amounts of time. Even concentrating your use of individual functions (for example all the data, draw all the systems, then draw all the links, rather than loading and drawing one complete system at a time) can make a huge difference.

if you get some spare time, would you mind checking out the latest code and looking at the paint event? it calls a series of draw* methods. ie drawsyst. sorry for lack of/poor comments. i will amend that.
here's a free svn client if you need it:
http://code.google.com/p/svnx/
thanks

i will try out a timer based system, but only refresh the canvas when needed or 25fps.

0.4a released. link up top.
Added multicolour psychedelic hyperspace connections.
Minor optimisations.

hopefully rectified in next release:
syst names are currently truncated
duplicate syst detection(i've noticed duplicates overlapping in the nova plug)
-i'm assuming higher ID takes precedence
----------------------

0.4.1a released. link up top.
Added a timer control to update the map. I notice no difference.
Syst names truncated properly
-----------------------
May change the way the map scrolls. Add keyboard controls

This post has been edited by Tycho : 23 May 2010 - 07:16 AM

QUOTE (Tycho @ May 23 2010, 02:12 AM) <{POST_SNAPBACK}>

if you get some spare time, would you mind checking out the latest code and looking at the paint event?

It's taking me a bit of time to get my mind around your code because it's so different from anything I do in MissionComputer, but here are a few things I've noticed:

Store your system data in arrays, not dictionaries. Even if the actual value is a number, reading from a dictionary always involves extensive string parsing behind the scenes, which is slow.

Declare all of your variables at the beginning of the method. A Dim statement that's inside a loop gets executed repeatedly, to no purpose. (As a general rule, don't put anything in a loop that doesn't need to be there.)

Don't keep calling UBound(canvasBuffer) in your loops. Call it once at the beginning of the method, and then assign its value to a variable and use that for the rest of the method. (As a general rule, try to avoid doing the same calculation more than once per method.)

When you say 'while systIndex < fileReference.syst.Count', fileReference.Syst.Count is evaluated every time the loop repeats. Copy it into a local variable, and then compare systIndex with that.

Graphics.DrawString is slower than all the other drawing methods (as a general rule, anything involving strings is slow). Avoid drawing text when possible, and when it's absolutely necessary, do it all at once. MissionComputer renders the system names as an entirely separate stage, after all the circles and links have already been drawn.

Using a timer to redraw the canvas really is unnecessary: the Paint even will fire whenever a redraw is needed.

What confuses me most is why canvasBuffer is an array. Unless I'm missing something, your timer seems to append a whole new picture to it every time it executes, and making new pictures takes time. I would just make canvasBuffer a single Picture, say 'canvasBuffer = NewPicture(' etc. etc. when the programme opens and whenever the window is resized, and get rid of all the UBound(canvasBuffer) stuff.

thanks for looking at the code. it's much appreciated.

QUOTE (David Arthur @ May 23 2010, 03:07 PM) <{POST_SNAPBACK}>

It's taking me a bit of time to get my mind around your code because it's so different from anything I do in MissionComputer

i code in too many languages, so I have a sort of international-english style.

QUOTE (David Arthur @ May 23 2010, 03:07 PM) <{POST_SNAPBACK}>

Store your system data in arrays, not dictionaries. Even if the actual value is a number, reading from a dictionary always involves extensive string parsing behind the scenes, which is slow.

ah, i didn't know that. a cocoa dictionary isn't much slower than an array.

QUOTE (David Arthur @ May 23 2010, 03:07 PM) <{POST_SNAPBACK}>

Declare all of your variables at the beginning of the method. A Dim statement that's inside a loop gets executed repeatedly, to no purpose. (As a general rule, don't put anything in a loop that doesn't need to be there.)

this is a bit of me thinking ahead of myself, and a bit of objective-c. i'll move them out and redim if necessary.

QUOTE (David Arthur @ May 23 2010, 03:07 PM) <{POST_SNAPBACK}>

Don't keep calling UBound(canvasBuffer) in your loops. Call it once at the beginning of the method, and then assign its value to a variable and use that for the rest of the method. (As a general rule, try to avoid doing the same calculation more than once per method.)
When you say 'while systIndex < fileReference.syst.Count', fileReference.Syst.Count is evaluated every time the loop repeats. Copy it into a local variable, and then compare systIndex with that.

ah, another objective-c nuance. i didn't realise these were actually calculated every time they are called. i thought it was a property. the 'ubound' makes sense, the 'count' doesn't. i think dictionary.count should just be a variable.

QUOTE (David Arthur @ May 23 2010, 03:07 PM) <{POST_SNAPBACK}>

Graphics.DrawString is slower than all the other drawing methods (as a general rule, anything involving strings is slow). Avoid drawing text when possible, and when it's absolutely necessary, do it all at once. MissionComputer renders the system names as an entirely separate stage, after all the circles and links have already been drawn.

good point. i'll add drawSystNames to the buffer loop.

QUOTE (David Arthur @ May 23 2010, 03:07 PM) <{POST_SNAPBACK}>

Using a timer to redraw the canvas really is unnecessary.

Currently, yes. thinking ahead again.

QUOTE (David Arthur @ May 23 2010, 03:07 PM) <{POST_SNAPBACK}>

the Paint event will fire whenever a redraw is needed.

how so? i have to call canvas.refresh to get paint to fire.

QUOTE (David Arthur @ May 23 2010, 03:07 PM) <{POST_SNAPBACK}>

What confuses me most is why canvasBuffer is an array.

This is when i merged the DA doublebuffer branch and the NC quadBuffer. It's an experiment mostly. i can theoretically define how many buffers i want, making a dynamicBuffer. The timer will help with this.

QUOTE (David Arthur @ May 23 2010, 03:07 PM) <{POST_SNAPBACK}>

Unless I'm missing something, your timer seems to append a whole new picture to it every time it executes, and making new pictures takes time. I would just make canvasBuffer a single Picture, say 'canvasBuffer = NewPicture(' etc. etc. when the programme opens and whenever the window is resized, and get rid of all the UBound(canvasBuffer) stuff.

I will test out the impact of creating a new picture. It's required for the dynamicBuffer. Perhaps I can create 1 newPicture, then copy it each time into the buffer array.

I'll make another branch with your suggestions.

This post has been edited by Tycho : 23 May 2010 - 10:41 PM

QUOTE (Tycho @ May 23 2010, 11:38 PM) <{POST_SNAPBACK}>

a cocoa dictionary isn't much slower than an array.

That's the trick, though; few of these things are much slower, but when you're doing them multiple time for every system and refreshing every pixel the user drags the mouse, the savings add up quickly, especially on slow systems. It was things like this which turned MissionComputer's galaxy editor from something so slow it needed a progress bar and 'Time remaining' indicator into something so fast that updating the progress bar was slowing it down.

QUOTE (Tycho @ May 23 2010, 11:38 PM) <{POST_SNAPBACK}>

how so? i have to call canvas.refresh to get paint to fire.

Sorry, I realised after posting that I hadn't made this clear. Any time that the system has done something that makes a refresh necessary, Paint will be called without your intervention. This means that you only need to initiate a refresh yourself if your own programme has done something to make it necessary, such as changing the data or adjusting the viewpoint.

Here's what I do: create a method called Redraw, which draws the data to the buffer, and then draws the buffer to the Canvas. Call Redraw in Canvas.Paint, and also call it whenever you do something that changes the data (including moving the scrollbars). Never call Canvas.Refresh — it's prone to unexplained flickering no matter what you do.

For that matter, you can also use conditional compilation in Redraw so that it draws directly to the canvas on Mac OS X, but uses the buffer on other systems. That way, you don't waste time buffering something that's already buffered.

QUOTE (Tycho @ May 23 2010, 11:38 PM) <{POST_SNAPBACK}>

It's an experiment mostly. i can theoretically define how many buffers i want, making a dynamicBuffer.

How does this help you?

....... Maybe I can begin working on my TC again. It's been at a standstill for months- I'm in desperate need for something like this for windows! (preferably one that will run on windows ME. gasp I know, it's horrible.)

QUOTE (REDchigh @ May 25 2010, 01:06 AM) <{POST_SNAPBACK}>

....... Maybe I can begin working on my TC again. It's been at a standstill for months- I'm in desperate need for something like this for windows! (preferably one that will run on windows ME. gasp I know, it's horrible.)

you can compile your applications for Linux, Mac OS X and all 32-bit versions of Windows, including Windows 98, NT, ME, 2000 and XP.
try out the latest build.

QUOTE (David Arthur @ May 24 2010, 03:07 PM) <{POST_SNAPBACK}>

That's the trick, though; few of these things are much slower, but when you're doing them multiple time for every system and refreshing every pixel the user drags the mouse, the savings add up quickly, especially on slow systems. It was things like this which turned MissionComputer's galaxy editor from something so slow it needed a progress bar and 'Time remaining' indicator into something so fast that updating the progress bar was slowing it down.

and yet dragging a syst turns it into a grey box, and moving it to the edge of the canvas does nothing. in fact it switched desktops on me and i was stuck with a grey box 🙂 your canvas does very little updating, but the user is penalised in function/rewarded by speed. I completely understand why as well.
I like the zoom effect. why is the zoom control a menu and not a slider?

QUOTE (David Arthur @ May 24 2010, 03:07 PM) <{POST_SNAPBACK}>

Sorry, I realised after posting that I hadn't made this clear. Any time that the system has done something that makes a refresh necessary, Paint will be called without your intervention. This means that you only need to initiate a refresh yourself if your own programme has done something to make it necessary, such as changing the data or adjusting the viewpoint.

Here's what I do: create a method called Redraw, which draws the data to the buffer, and then draws the buffer to the Canvas. Call Redraw in Canvas.Paint, and also call it whenever you do something that changes the data (including moving the scrollbars). Never call Canvas.Refresh — it's prone to unexplained flickering no matter what you do.

thom mcgrath recommended using canvas.refresh(false). i don' think we can argue with him.
http://forums.realsoftware.com/viewtopic.php?f=4&t=27029

QUOTE (David Arthur @ May 24 2010, 03:07 PM) <{POST_SNAPBACK}>

For that matter, you can also use conditional compilation in Redraw so that it draws directly to the canvas on Mac OS X, but uses the buffer on other systems. That way, you don't waste time buffering something that's already buffered.

good point.

QUOTE (David Arthur @ May 24 2010, 03:07 PM) <{POST_SNAPBACK}>

How does this help you?

re: dynamicBuffer
i don't think it helps me, but i can't notice the performance hit.
also if i create canvasBuffer(0) as big as the users current screen, if the user increases screen size, the buffer isn't increased. this was the idea of creating a newpicture for each buffer. the user could resize the window and the buffer would be exactly the right size.

This post has been edited by Tycho : 24 May 2010 - 08:13 PM

I forgot to mention down here.

0.4.2a DA remix
this branch includes most of the suggestions DA made a few posts ago.
each change improved performance, but the main improvement was finding that i was drawing everything a couple times every second to the same buffer. fixt now.

also added syst name editing.

i'm thinking the map looks rather boring. maybe aim for spore?:
http://www.youtube.com/watch?v=DjEWm0lsdJ4

< lol, 10 years ago.

This post has been edited by Tycho : 24 May 2010 - 08:31 PM

QUOTE (Tycho @ May 24 2010, 09:07 PM) <{POST_SNAPBACK}>

and yet dragging a syst turns it into a grey box, and moving it to the edge of the canvas does nothing. in fact it switched desktops on me and i was stuck with a grey box 🙂

When the user drags a system, I hand it over to the Drag Manager rather than duplicating it with my own code (as I did for planets in the sĂżst editor). I could probably draw a little image of the system into the DragItem, but I don't really see the point since the difference would be entirely aesthetic.

None of this is motivated by the buffering system; in fact, I don't even do any special buffering on Mac OS X (I only recently reactivated my Mac OS 9-era buffering code to prevent flickering in the experimental Windows version). Refreshes are cheap on Mac OS X, as the new zooming effect proves.

I suppose I'm avoiding refreshes by calling on the Drag Manager, but that wasn't my reason for using it. And apart from the fact that I haven't yet implemented scrolling when you reach the edge of the screen, I don't see how functionality is affected.

QUOTE (Tycho @ May 24 2010, 09:07 PM) <{POST_SNAPBACK}>

I like the zoom effect. why is the zoom control a menu and not a slider?

EV Nova 's zoom feature is quantised, so mine is too. Since this is an editor for an existing game rather than an original programme, one of my key goals has been to precisely replicate the appearance in the original game's map window — next on the list is to mimic its government borders rather than just re-colouring the systems.

The zoom effect, on the other hand, is pure showing off. 🙂

QUOTE (Tycho @ May 24 2010, 09:07 PM) <{POST_SNAPBACK}>

thom mcgrath recommended using canvas.refresh(false). i don' think we can argue with him.
http://forums.realsoftware.com/viewtopic.php?f=4&t=27029

Perhaps it's improved in recent versions, but in my experience Refresh does not work as advertised, no matter what parameters you pass.

QUOTE (Tycho @ May 24 2010, 09:07 PM) <{POST_SNAPBACK}>

tif the user increases screen size, the buffer isn't increased. this was the idea of creating a newpicture for each buffer. the user could resize the window and the buffer would be exactly the right size.

Oh, you replace the buffer if the window is resized — still faster and less memory-intensive than creating them constantly,

QUOTE (David Arthur @ May 25 2010, 08:35 AM) <{POST_SNAPBACK}>

(I only recently reactivated my Mac OS 9-era buffering code to prevent flickering in the experimental Windows version).

I like the sound of this...

QUOTE (krugeruwsp @ May 25 2010, 12:28 PM) <{POST_SNAPBACK}>

I like the sound of this...

It's very experimental, although some of the underlying technology has been around for years. Whether anything will come of it is anyone's guess, but I posted a few screenshots of it at the beginning of the month to prove that it at least exists:
http://davidarthur.evula.net/beta/duessa/rled_6may.png
http://davidarthur.evula.net/beta/duessa/starmap_6may.png
(You'll notice that it's running in WINE for want of an actual Windows installation on which to test it.)