GPU accelerated canvas replacement

I’ve been toying with a turn-based tile game idea on-and-off for years but have never really gotten round to making much headway into it for various reasons.

In the past, I have written a working isometric tile engine using the native Xojo Canvas control but found it way too slow to handle thousands of tiles, sprites, etc even with aggressive culling of offscreen elements. I then switched to using ezgl which is better but doesn’t support Linux. Also I’m hearing that people on this forum think the future of OpenGL is bleak so I’m waring about spending a year or so on a project using the wrong technology. I’m also a little concerned that Xojo’s OpenGL surface seems to be at version 1.1 and the current version is 4.6. I’m guessing Xojo isn’t interested in this area?

All I’m trying to do is get the functionality of the current Canvas control but improve it’s speed. GPU-accelerated libraries seem to be an order of magnitude faster. I’m not looking for 3D functions. This game is a simple isometric 2D game.

I have a couple of questions:

1). What is the best library to use for GPU accelerated drawing on macOS, Windows 10+ and modern Linux? I understand that there is unlikely to be one common library.
2). Is it possible to create wrappers around these libraries (i.e. get a Metal surface to render on instead of an openGL surface)?
3). If it is possible to write a wrappers, is this really feasible for someone (i.e. me) who is proficient in Xojo and mild-competent at understanding C# and Objective-C or would this be way too much of a project?

I’d appreciate any input people might have. Thanks.

A couple of things:

  1. It is possible to use the canvas to draw complex models and games, but it does require some thought and clever design. I.e. Using invalidate and passing in a rectangle on the tightest area possible and only updating what’s needed.

  2. It should be possible to use CALayers on the Mac to create a game environment. CALayers work differently than just drawing, but you can let the OS cache them for you. In basic principle a layer contains an image and you can resize that layer, change its location and what not.

  3. Keep math and location updating code out of the paint event, just paint.

  4. I think it would be a real challenge to create a Wrapper for all the different 3D libraries, but ultimately I think it’s going to be needed moving forwards.

  5. Apple have appeared to fix the bugs that I found and reported for High Sierra; but the writing is on the wall. Metal 2 on HS is quite impressive and quite a bit faster than Metal 1, even with not so impressive hardware.

It would be interesting first to see where most time is spend in your project before optimizing.

Did you check with Activity Monitor or Instruments on Mac to measure how much time is spend where?

@Sam Rowlands Thanks for the detailed reply. My concern about the native canvas is that even with minimal game logic, just drawing a thousand or so tiles of the same image can be a bit slow. I worry that when I add in multiple layers and logic it’ll slow to a crawl. I take on board that whatever I do I need to heavily optimise. It just strikes me as a no-brainer to use the GPU rather than the CPU whenever possible.

@jean-paul devulder SDL looks very interesting. I assume that I could get precompiled libraries (or do it myself) for the various platforms and then bundle them with my game and call into them with declares? Looks like this could be a more reliable solution than OpenGL. Trouble is, I haven’t the slightest idea how I would create a canvas/surface class that used SDL. Can this be done with a declare rather than a plugin (which is how I think Tinrocket’s original OpenGL implementation was achieved).

Yes drawing the same image a 1000x will be slow. There’s two possibilities I can see here.

  1. CoreGraphics has a function for drawing the same image repeated, so you could draw it once, but it will fill an area.
    https://developer.apple.com/documentation/coregraphics/1456240-cgcontextdrawtiledimage

  2. You could create a 1000 CALayers and set their images to be the same. As you’re not responsible for drawing those layers anymore, they’re supposedly cached to the GPU and the OS automatically handles this, in theory it would allow you to focus on the interesting bits.

Apple do also have SceneKit; I’ve never used it myself but it’s designed for games. It would use Metal or OpenGL where appropriate.
https://developer.apple.com/documentation/scenekit?language=objc

As for all of these, they’re Mac only sadly.

I understand that I can access OS library methods with declares (hence I can call into CoreGraphics methods) but can I access a CoreAnimation layer/surface as a control (i.e like the Canvas control) with declares or do I need to investigate writing a plugin to provide a custom surface control. Sorry if that’s a dumb question.

I cannot imagine any scenario in which you should ever have to draw thousands of images in any given paint event. Aren’t you caching 90% of your board into an Picture object and then just drawing the parts that actually moved?

It’s not a dumb question; the answer is Yes… But to which part? Both actually.

If you don’t want to write the declares, I bet Christian already has support for CALayers in his almighty MBS plugin. Otherwise yes, you can use declares to get into CALayers.

Like I said; you sort of have to adjust your thinking with CALayers as you don’t draw them, you create them, position them and attach them to a View (canvas will work). You then modify their properties to do stuff with them, such as frame, content.

Oh, I wouldn’t do it this way unless absolutely necessary. Once you create the picture you have to then start tracking scaling. It gets ugly. Drawing directly into the graphics object is a lot faster and you have to do zero HiDPI changes as Xojo handles it for you. FTC uses the picture approach and it’s. not. fun.