Results 1 to 18 of 18

Thread: [RESOLVED] Optimized Cut & Copy in Cairo [L-Plate Learner Edition]

  1. #1

    Thread Starter
    Hyperactive Member -Corso->'s Avatar
    Join Date
    Oct 2021
    Posts
    379

    Resolved [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 ?.

    Name:  Cutout Section.jpg
Views: 471
Size:  54.0 KB

    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.
    Last edited by -Corso->; Dec 28th, 2021 at 08:03 AM.

  2. #2
    The Idiot
    Join Date
    Dec 2014
    Posts
    3,001

    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.

  3. #3
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,454

    Re: Optimized Cut & Copy in Cairo [L-Plate Learner Edition]

    Quote Originally Posted by -Corso-> View Post
    I'm wondering if Cairo has an optimized way to do this operation, speedily ?.
    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

  4. #4

    Thread Starter
    Hyperactive Member -Corso->'s Avatar
    Join Date
    Oct 2021
    Posts
    379

    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).

    Name:  Screen Cut.jpg
Views: 440
Size:  49.2 KB

    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.

    Name:  Terrain Removed.jpg
Views: 437
Size:  110.1 KB

    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.
    Last edited by -Corso->; Dec 28th, 2021 at 07:05 PM.

  5. #5
    The Idiot
    Join Date
    Dec 2014
    Posts
    3,001

    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

  6. #6

    Thread Starter
    Hyperactive Member -Corso->'s Avatar
    Join Date
    Oct 2021
    Posts
    379

    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.
    Last edited by -Corso->; Dec 28th, 2021 at 07:53 PM.

  7. #7
    The Idiot
    Join Date
    Dec 2014
    Posts
    3,001

    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.

  8. #8

    Thread Starter
    Hyperactive Member -Corso->'s Avatar
    Join Date
    Oct 2021
    Posts
    379

    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.

  9. #9
    The Idiot
    Join Date
    Dec 2014
    Posts
    3,001

    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.

  10. #10

    Thread Starter
    Hyperactive Member -Corso->'s Avatar
    Join Date
    Oct 2021
    Posts
    379

    Re: Optimized Cut & Copy in Cairo [L-Plate Learner Edition]

    Quote Originally Posted by baka View Post
    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.
    That would be then masked onto the avatar/monsters.

    Instead I'm using a slow rebuild of a localized map around every monster/avatar.

  11. #11

    Thread Starter
    Hyperactive Member -Corso->'s Avatar
    Join Date
    Oct 2021
    Posts
    379

    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?

    Name:  fnj7mlf7aw.jpg
Views: 388
Size:  21.9 KB

  12. #12
    The Idiot
    Join Date
    Dec 2014
    Posts
    3,001

    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.

  13. #13

    Thread Starter
    Hyperactive Member -Corso->'s Avatar
    Join Date
    Oct 2021
    Posts
    379

    Re: Optimized Cut & Copy in Cairo [L-Plate Learner Edition]

    Quote Originally Posted by baka View Post
    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.

    Name:  Speed.jpg
Views: 427
Size:  171.0 KB

  14. #14
    The Idiot
    Join Date
    Dec 2014
    Posts
    3,001

    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.
    Last edited by baka; Dec 29th, 2021 at 05:14 AM.

  15. #15
    The Idiot
    Join Date
    Dec 2014
    Posts
    3,001

    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

  16. #16
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,454

    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

  17. #17

    Thread Starter
    Hyperactive Member -Corso->'s Avatar
    Join Date
    Oct 2021
    Posts
    379

    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.

    Name:  SatHappy.jpg
Views: 405
Size:  133.4 KB

    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.

  18. #18

    Thread Starter
    Hyperactive Member -Corso->'s Avatar
    Join Date
    Oct 2021
    Posts
    379

    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.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width