1 Attachment(s)
[RESOLVED] Optimized Cut & Copy in Cairo [L-Plate Learner Edition]
Hi all, another question for the Cairo Squad.
I'm wondering if Cairo has an optimized way to do this operation, speedily :duck: ?.
Attachment 183433
Currently I'm using the Cairo.Imagelist to rebuild larger than needed 'chunks' of 2.5D terrain surfaces that are 'already' drawn elsewhere. I do this to 'clean' areas, but it seems like a terrible way of reprinting sections of the screen, especially since I already have an identical copy of the completed terrain sitting waiting on another surface.
I'm wondering if I can trim out a section of the waiting surface to place it upon the final surface. I have to do this hundreds to thousands of times per second, so copying over the entire surface via a mask or end region I expect would be hideously slow.
The image above is simplified but conveys what is happening. I have to chuck on gradient surfaces (several), and non-uniform lighting operations too (all sitting in complete form on other waiting surfaces).
Thanks for any advice. :)
Re: Optimized Cut & Copy in Cairo [L-Plate Learner Edition]
shouldn't be a problem.
gdi32 and cairo re both equal fast, u can paint on a surface hundreds of times and it should be able to do it in 1/16 of a second.
I used gdi32 with my game, that used multiple layers and hundreds of pictures, and it was fast. from 60 to 300 frames per second.
with direct2d I do the same, but its even faster.
not sure exactly how u do it in cairo, but in gdi32 I use alphablend. maybe thats the question. is cairo rendering onpar with alphablend.
maybe Olaf can answer that.
Re: Optimized Cut & Copy in Cairo [L-Plate Learner Edition]
Quote:
Originally Posted by
-Corso->
I'm wondering if Cairo has an optimized way to do this operation, speedily :duck: ?.
In a simple Cairo-Gameloop, you will have two Surfaces (two "Layers", both having the same Pixel-Size).
1) a SrfBackGround (which does not contain any transparent Pixels)
2) and your SrfFinal (where everything comes together, before Blitted to a Window-DC)
Your MainLoop will be present in a "RefreshScene"-routine like this (calling into several Sub-Routines):
Code:
Sub RefreshScene()
Dim dT As Double: dT = GetPreciseTimeDifferenceToLastCall
EnsurePotentialMovementsOn SrfBackGround, dT 'optional, ...not needed on a static BG
Dim CC As cCairoContext
Set CC = SrfFinal.CreateContext 'derive the CC from the pre-allocated SrfFinal
CC.Paint 1, SrfBackGround.CreateSurfacePattern 'erase SrfFinal with the identical sized content of the BG
DrawAllCrittersOn CC, dT 'here you do all your loops over all the moving Object-Collections
SrfFinal.DrawToDC PicBox.hDC 'final Blit from InMem-Surface to a System-provided RenderArea
End Sub
You can now time this MainLoop-Routine (via New_c.Timing True ... Debug.Print New_c.Timing) -
and find out, what time is needed over-all.
If you have a "Map-based" BackGround, the central routine which triggers "Map-Changes",
could be located in: EnsurePotentialMovementsOn SrfBackGround, dT
From there spreading out into several Sub-Routines or Class-Methods -
e.g. on a Map-Class which knows best, what it should show currently
(depending on the dT TimeDifference - and the State you will hold in several GameState-Classes).
Same thing basically (spreading out into Sub-Routines or Class-methods) from within: DrawAllCrittersOn CC, dT
You will find, that on Surface-Sizes of e.g. 1024x768 (for both, BG- and Main-Surface),
such a RefreshScene-Routine will not take more than about 3-5msec (being good for 200FPS or more).
If you reach "critical timings" later on (>20msec ... <50FPS), you can easily identify the culprit,
by just commenting out a few Routines (or Sub-Routine-Calls), to find where most of the time is spend -
and then optimize only that specific part.
Writing the whole engine based on a lot of SubRoutines (best organized as Methods, behind Classes),
will help you to keep your Main-Loop "clean" (not evolving into spaghetti).
You can take a look into this Demo, which basically reflects the statemnts made above -
showing what is achievable with Cairo as a Rendering-Engine (performance-wise).
https://www.vbforums.com/showthread....hysics-Engine)
Olaf
2 Attachment(s)
Re: Optimized Cut & Copy in Cairo [L-Plate Learner Edition]
Thank you Olaf,
I might add in some more pictures, as I think we might be on a different page.(?)
Currently I work out the Terrain with everything, then the monsters/avatars on a separate surface. I don't work out each individual tile separately and build the monster on top due to the screen wide lighting effects, it never seemed to work out speed wise. At the moment, the screen updates at 6ms because I only rebuild/remake what is in front of the avatar/creatures. But that blows out if I have more creatures on the screen. It is troubling, because I may have to rewrite the way I generate the screens or remove the lighting effects (which are highly wanted).
Attachment 183449
Right now, I work out this process. Each lives on a separate surface.
1. Flat Terrain squares. (Surface)
2. Grass. (Surface)
3. Trees with shadows. (Surface)
4. Lighting/overlays/sunsets. (Surface)
5. Merged surface of all of the above. (All_Terrain (Surface))
Then I work out the Monsters and Avatar positions.
1. Monsters + Avatar with shadows. (Surface)
2. Run math to draw trees with lighting effects, again, in front of said entity and merge via ATOP.
This allows grasses and trees to be splashed over the creatures so they don't appear stuck on the front of trees. I do it this way for speed, instead of working out each individual tile, as it gets messy/slow/bad with lighting.
Attachment 183448
What I would really like to do is take a section of the Grass,Trees,Lighting surfaces and slap that over each monster and character using ATOP. (@ the size of the character.)
Below is some of the code I use to 'rebuild and slap' those 'in front' trees/grass onto said characters.
Sub Draw_Masked_Objects_And_Flora_In_Front_Of_Person(ByVal Icon_Number As Long, ByVal X As Long, ByVal Y As Long)
'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
' Draw Grass, Trees and object in front of monster, person or avatar
'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
'Icon_Name is the String Name of the entity, ie: "Avatar"
'X & Y are the characters location in Local_X and Y
'Mask_Box is the functional box for applying masked trees, grasses and objects
Dim Pre_Mask As cCairoContext
Set Pre_Mask = Cairo.CreateSurface(300, 200, ImageSurface).CreateContext
'Blank the pre mask
Call Blank_My_Surface(Pre_Mask)
'Draw a shadow first from the shadow box created at the beginning of the program
'Use the full shadow weight so as to transfer all of the clipping image over
Pre_Mask.RenderSurfaceContent Shadow_Box.Surface, 0 - 15, 0 + 50, , , CAIRO_FILTER_FAST, 1
'Draw the character into the mask box
Pre_Mask.RenderSurfaceContent Cairo_Tool_Set.ImageList.KeyByIndex(Icon_Number), 0, 0, , , CAIRO_FILTER_FAST
Dim Current_Grass_Icon(4, 3) As Long 'The grass icons in front of you
Dim Current_Tree_Icon(4, 3) As Long 'The tree Icons in front of you
'The icons in front of the character are @Y+0 and Y+1: X, X+1, X+2
'We need all icon values for each
Dim A, B As Integer 'counters
'Grab all the icons in front of the character by looking up the phase maps
For B = 0 To 2
For A = 0 To 2
Current_Grass_Icon(A, B) = 0
Current_Tree_Icon(A, B) = 0
Current_Grass_Icon(A, B) = Lookup_Grass_Icon(A + X, B + Y)
Current_Tree_Icon(A, B) = Lookup_Tree_Icon(A + X, B + Y)
Next A
Next B
'Now we draw each item onto the character's mask_box
Pre_Mask.Operator = CAIRO_OPERATOR_ATOP
....and so on.......
This process seems silly, but it does work well. Especially because of the lighting effects I use. What I use is the Cairo Imagelist to redraw the map again, to mask it onto the avatar and creatures. Seems to me rebuilding the map is adding to overhead when I've already got the surfaces waiting, but they are huge. I just want an avatar sized chunk of those surfaces.
But how is that done? Can it be done?
Oh, do note however, there is only transparent icons/surfaces, no fully solid backgrounds. Only the final surface is fully solid, all the trees, grasses etc are all png's with transparency.
Re: Optimized Cut & Copy in Cairo [L-Plate Learner Edition]
my suggestion, since I work with multilayered games is:
- try to "prepare" as much possible
this means, shadows are also pictures, using a raster format (such as png) with opacity u can create shadows that way
its a good trade off and will save any calculations u need to create on the fly shadows.
I have a "compiler", a tool I created that I use to create data files, containing lots of stuff. item properties, string converted into arrays
(I use bitmap-fonts as well) so all the text is done using safe array or directly from the byte array. and a lot of other stuff.
this so the game-exe will not need to.
to render a picture is fast. but to render a picture with any filter, effect, scaling it will eat speed.
so try to avoid all that if you can by "preparing" the graphics before.
another suggestion is to use The trick: D3DKMTWaitForVerticalBlankEvent
u can use it separately without the need to use directx. he shared the API version
Code:
Private Type LUID
lowpart As Long
highpart As Long
End Type
Private Type D3DKMT_OPENADAPTERFROMHDC
hDC As Long
hAdapter As Long
AdapterLuid As LUID
VidPnSourceId As Long
End Type
Private Type D3DKMT_WAITFORVERTICALBLANKEVENT
hAdapter As Long
hDevice As Long
VidPnSourceId As Long
End Type
Private Type D3DKMT_CLOSEADAPTER
hAdapter As Long
End Type
Private Declare Function D3DKMTOpenAdapterFromHdc Lib "gdi32" (ByRef tAdapter As D3DKMT_OPENADAPTERFROMHDC) As Long
Private Declare Function D3DKMTWaitForVerticalBlankEvent Lib "gdi32" (ByRef tEvt As D3DKMT_WAITFORVERTICALBLANKEVENT) As Long
Private Declare Function D3DKMTCloseAdapter Lib "gdi32" (ByRef tAdapter As D3DKMT_CLOSEADAPTER) As Long
Code:
Dim tAdapterOpen As D3DKMT_OPENADAPTERFROMHDC
Dim tWaitVSync As D3DKMT_WAITFORVERTICALBLANKEVENT
Dim tAdapterClose As D3DKMT_CLOSEADAPTER
tAdapterOpen.hDC = form/picturebox.hDC
D3DKMTOpenAdapterFromHdc tAdapterOpen
tWaitVSync.hAdapter = tAdapterOpen.hAdapter
tWaitVSync.VidPnSourceId = tAdapterOpen.VidPnSourceId
and this u use in your loop:
Code:
D3DKMTWaitForVerticalBlankEvent tWaitVSync
Re: Optimized Cut & Copy in Cairo [L-Plate Learner Edition]
Hi Baka,
Well, the shadows in Cairo, using a transformation matrix don't seem to take any noticeable time. This is because I do work out all the imagery first (and shadows), the screen only changes when you move onto another screen (by walking to the side limit). So the avatar moves around the static screen. Like 'Caves of Qud'.
When I had the whole screen moving, with the avatar centered, it made important things difficult to see, because 'everything' moves in a turn based choppy fashion. So at the moment, it runs pretty well, except for screen updates with swarming enemies. Re-building all the trees/grasses in front of them is slow. :o
Re: Optimized Cut & Copy in Cairo [L-Plate Learner Edition]
hm.
are you using double buffering?
meaning, the picture is prepared in a memory-dc, and when that is done, u make a "full copy" of that picture to the surface.
this is a common issue with GDI as well, as it will be choppy if we are updating the pictures on the DC bit by bit instead of all-in-one-go.
now, I dont work with Cairo, so I dont know the details how it works.
but better I ask so we are in the same page.
Re: Optimized Cut & Copy in Cairo [L-Plate Learner Edition]
Yes, all layers are pre-prepared waiting to be dumped onto the main screen. But so is the entire screen as well.
Tree layer, grass layer, terrain layer, lighting layer, and finally fully drawn screen layer.
Then the monster/avatar layer.
Then I have to update the screen using masks to where each monster/avatar is to put in front foliage ATOP them. (Slow with many swarming creatures.)
So generally, the loop is, dump 'fully drawn screen layer', then monster/avatar (with foliage). Very fast normally.
When you change screens, it works it ALL out, each layer. You don't even notice the 80ms-140ms screen change. :D
Re: Optimized Cut & Copy in Cairo [L-Plate Learner Edition]
my loops consist:
- cls with background color
- create background
- create middlepart
- create front
- (and a lot of other stuff of course, ui, monsters, etc, depending on the screen-mode)
- render to screen
- doevents (using peekmessage)
- loop
Im not using masks, everything is pictures with opacity.
so, this "put in front foliage" u say u are using masks, this so u can check where on the screen they are?
is it not enough with a rectangle? sure its not as accurate, but should go much faster.
Re: Optimized Cut & Copy in Cairo [L-Plate Learner Edition]
Quote:
Originally Posted by
baka
is it not enough with a rectangle? sure its not as accurate, but should go much faster.
Yes, a rectangle copy of those stored foliage surfaces is what I'd like to use. :thumb:
That would be then masked onto the avatar/monsters.
Instead I'm using a slow rebuild of a localized map around every monster/avatar.
1 Attachment(s)
Re: Optimized Cut & Copy in Cairo [L-Plate Learner Edition]
Just adding, it looks like Pycairo can 'blit' sections of surfaces onto another surface. If I'm reading this right.
https://stackoverflow.com/questions/...g-surface-copy
Is that not available to us?
Attachment 183454
Re: Optimized Cut & Copy in Cairo [L-Plate Learner Edition]
why do u mask?
I use:
render background (usually a color first) or a radiant color (blue, orange etc depending on time of day)
and that sometimes animated, clouds, fog, stars, etc
after that I render, with parallax
mountains, far away stuff, could be some flying object, animal etc.
after that I render forest, houses from distant, a hill
after that the main-map (the foothold-area)
after that monsters, characters and other objects.
and after that the front layer, if anything is above it.
and after that if theres any ui, popups, mouseicons
so the point is that I just render stuff above each other
everything is in "layers", so we have layer 0, 1, 2 ,3 ,4 ,5 ,6 etc. and each layer is rendered to the "memory-surface"
and when all is done, it will be rendered to the picturebox. and that includes the characters as well of course
I even have a graphic-mouse pointer that is also rendered together in the layer, but the last one.
1 Attachment(s)
Re: Optimized Cut & Copy in Cairo [L-Plate Learner Edition]
Quote:
Originally Posted by
baka
why do u mask?
Because the only thing that updates on any screen refresh are the characters (and the foliage on them). No other calculations.
My loop:
1. Final surface gets a new copy of the pre-rendered full screen. No calculation, no anything, just an image copy.
2. Characters get a masked copy of the foliage 'ATOP' their bodies.
3. Characters (with foliage/object/grass covering) placed on final surface.
Done. 4 to 6 milliseconds effort.
This is why I mask.
Attachment 183457
Re: Optimized Cut & Copy in Cairo [L-Plate Learner Edition]
even if you are not doing anything, everything is "rendered", but not from you.
maybe the problem is not really the masking but the refresh of the form, creating stuttering.
for me, as I "control" the rendering, I don't have that issue, I disable refresh.
the picturebox I use is set to "autoredraw=off"
I remember in the early stage of my game, I used the form/pictures redraw and it was a pain.
ok, so I think what u should look into is the "redraw" stuff.
check the Paint Event as well.
to mask just characters and monsters should be very fast and not causing any issues,
but something is creating it, and I think it most be the form/picturebox own redraw stuff.
also, as long you are not drawing at the same speed as the monitor refresh rate, it will be stuttering.
I remember when I just used GDI, I needed at least twice (120 fps) to make it somehow smooth.
also, I use a picturebox to draw stuff. it seems more stable and causing less stuttering.
not good to render to form.
Re: Optimized Cut & Copy in Cairo [L-Plate Learner Edition]
also, u do:
1. Final surface gets a new copy of the pre-rendered full screen. No calculation, no anything, just an image copy. to a memory surface right?
2. Characters get a masked copy of the foliage 'ATOP' their bodies. also memory surface
3. Characters (with foliage/object/grass covering) placed on final surface. also -"-
4. Now you render the memory surface to Picturebox?
5. Loop
Re: Optimized Cut & Copy in Cairo [L-Plate Learner Edition]
Blitting parts (Sub-Rectangles) from a Source-Surface onto a Destination-Surface can be done via:
- Clipping the Sub-Rectangle in the right size on the Destination-Surface(Context)
- folowed by setting the Source-Surface (as the - well - SourceSurface)
- followed by Paint (and resetting the Clip)
If we have for example this Base-Setting here:
Code:
Private Fin As cCairoSurface, Lay As cCairoSurface
Private Sub Form_Load()
Set Fin = Cairo.CreateSurface(1024, 768) 'the final Surface (same size as Lay, below)
Set Lay = Cairo.CreateSurface(1024, 768) 'let's pre-construct 3 static Layer-Objects on Lay
Lay.CreateContext.DrawLine 150, 100, 150 + 32, 100, , 32, vbBlue
Lay.CreateContext.DrawLine 150, 150, 150 + 32, 150, , 32, vbMagenta
Lay.CreateContext.DrawLine 150, 200, 150 + 32, 200, , 32, vbRed
With Cairo.CreateSurface(100, 100).CreateContext 'Avatar-Construction
.SelectFont "Arial", 32, vbGreen, True: .TextOut 0, 0, "A"
Cairo.ImageList.AddSurface "Avatar", .Surface
End With
RenderScene Fin.CreateContext 'render a Demo-Scene to the Form
End Sub
Private Sub RenderScene(CC As cCairoContext)
CC.Paint 1, Cairo.CreateCheckerPattern 'clear the BackGround with true statics, like e.g. "a Grass-Layer"
CC.RenderSurfaceContent Lay, 0, 0 'complete "Boulders and Trees" pre-rendering, before painting Avatars and Critters
RenderAvatar CC, 160, 110
Set Me.Picture = CC.Surface.Picture
End Sub
Private Sub RenderAvatar(CC As cCairoContext, ByVal x#, ByVal y#)
CC.RenderSurfaceContent "Avatar", x, y
End Sub
If you run the Code above as it is, it will show what you "do not want"...
the Avatar will be painted on top of the magenta-colored "Boulder-Rectangle".
And I guess what you want to see, is a Method, which repaints (after Avatar-Rendering),
parts of the Z-Orderwise "on-top" sitting Objects from the Layer-Surface.
I tried to write this up in the following routine (which you can use alternatively within RenderScene()):
Code:
Private Sub RenderAvatarZ(CC As cCairoContext, ByVal x#, ByVal y#)
Dim A As cCairoSurface: Set A = Cairo.ImageList("Avatar")
CC.RenderSurfaceContent A, x, y 'paint the Avatar-Surface at x,y
CC.ClipExplicit x, y, A.Width, A.Height 'restrict the following Blend-Op to the Avatar-Rect
CC.SetSourceSurface Lay, 0, 0 'set the Lay-Surface as the Source
CC.Paint 'now (re-)paint only the area of the Clip-Rect (using Lay as the Source)
CC.ResetClip
End Sub
The Clipping in this case ensures, that the Cairo-Blender-Engine will only recalculate the Pixels within that (overlapping) Area.
HTH
Olaf
1 Attachment(s)
Re: Optimized Cut & Copy in Cairo [L-Plate Learner Edition]
ClipExplicit ! Well how about that, learn a new word everyday. Thank you so much Olaf for the clear example, that is EXACTLY what needs to be done.
Attachment 183463
Baka, yes, loads and loads of memory surfaces. They are handy for other effects (aka, nighttime lanterns) and don't appear to chew RAM either.
Ok, now to implement. I'll report back on the speed changes, with multiple monsters. Many, many thanks Olaf. :thumb: :thumb: :thumb:
Re: Optimized Cut & Copy in Cairo [L-Plate Learner Edition]
Pretty surprising result.
All of this is approximate though. (I got tired of finger counting monster swarms on the map).
Old sub: About 100-150 monsters on screen : around 40 to 60ms screen updates.
New ClipExplicit (commands): About 100-150 monsters on screen : around 20ms screen updates. (Very acceptable!)
It's early days as I now want to rewrite the entire graphics engine using the knowledge from the Avatar Builder and the ClipExplicit function. I can see myself getting a whole lot of use out of this command too. Kind of like the old PictureClip thingy.
Even better, as it's just a command it eliminates an entire long-winded subroutine. (@.@) (^.^)
Thank you Olaf, I'm fairly certain the ClipExplicit function would be a high use function for others too. :duck::thumb: