2
\$\begingroup\$

This question seems similar to other questions but no other topics I saw helped. I'm making a game in GML (GameMaker Language).

Regardless of the language/software, this should be a universal topic.

Okay, so I currently barely exceed 60FPS on a game with no code in the tick (step? frame? iteration?) so it should most definitely not be a CPU issue. My computer is fantastic and I can run nearly all (if not, all) games at ultra and high settings. Anyway, my game is isometric and tiles are 32x16.

The terrain data ds_grid (like a 2D array, but can't be jagged) is never changed except when loading other maps. I need the game to be played in a max of 1080p, and higher resolutions won't be implicitly supported.

So, I have a nested for-loop, to loop through a calculated minx/maxx and miny/maxy, basically defining the rectangular area in the terrain data ds_grid that I need to render. However, at the moment, in 1080p, after applying a variable called count to increment every iteration of the nested loop, I have nearly 8000 draw calls every frame, rendering every tile near the view.

Remember, the tiles are both isometric and small, so there are a lot of things needed to be rendered. There are multiple grass tiles to reduce texture repeating. There is also a water tile, and I may add other terrain tiles. So basically, I need a method of rendering tons of tiles (that don't change) with a much higher (preferably at least 300) FPS.

MORE INFO: in GM, a ds_grid is a data structure that is very similar to a 2D array, where the main differences are that it cannot be jagged (it's similar to a table) and any cell can hold any data type, so I could have strings and real numbers etc. all in the same ds_grid.

My ds_grid contains enumeration values, such as tile.grass or tile.water.

The terrain does not fit within the screen borders, and resolution directly affects view size.

\$\endgroup\$
4
  • 2
    \$\begingroup\$ I'm not sure about Game Maker but in sharpDX I just use sprite batch and set the sprite sort mode to deferred and it will use one draw call so long as all the tiles are in one sprite sheet. Last test for 2d ISO drawing was a grid 256*256 and it draws in less than 1ms with five layers of tiles all up per frame I'm drawing around 8-9k depending on camera zoom. Game Maker is very slow. \$\endgroup\$ – Justin William Stanley Bryant Feb 9 '17 at 23:54
  • \$\begingroup\$ You definitely need to add some kind of batching. \$\endgroup\$ – Tetrad Feb 10 '17 at 0:47
  • \$\begingroup\$ We need more info in order to help you. Is the rendered terrain larger than the screen, or is it scaled to be drawn entirely within screen borders? And, what kind of information does your ds_grid store? Sprite indexes? Background indexes? Tiles ones? Or, a subimage index referred to a unique sprite storing any kind of terrain sprite? Please, give as much related info as you can so we can figure out better what can be done to help you improving performances. \$\endgroup\$ – liggiorgio Feb 10 '17 at 2:36
  • \$\begingroup\$ Okay, I'll update the post with more info. \$\endgroup\$ – Anixias Feb 11 '17 at 15:53
1
\$\begingroup\$

You can achieve lighting-fast batching (as Tetrad suggested) by making use Vertex Buffers. I recently answered a similar question on the GameMaker forums: Tips for rendering optimization.

Looking at this more objectively, if all of this data remains static throughout the game, you can pre-build a vertex buffer. Gamemaker will try to do this dynamically whenever you call draw_sprite, however changes in render state will break a rendering batch, slowing down the renderer.

If you are not running in YYC, the actual process of iterating through the loop can also be expensive.

To add a little clarification, to build a vertex buffer, you can simply replace all of your calls to draw_sprite with something that writes data to your vertex buffer. Once a vertex buffer has been built, you can draw it with incredible speed with 1 simple draw call. This will enable you to render millions of tiles.

You can find information on how to create vertex buffers here: GameMaker ShaderOverview - Part 1 (There are 4 parts to this tutorial, starting with shaders, finishing on Vertex Buffers).

Additional Information:

  • The reason rendering in the way you are is slow is because a communication bottleneck exists between the CPU and GPU. Both are sitting idle whilst messages from the CPU are being sent to tell the GPU to do something. As each of these draw calls is simple, the GPU gets it done incredibly fast, then goes back to waiting. A vertex buffer resolves this by essentially merging all of those individual calls into a single message, allowing both devices to get on with what they should be doing.

  • To clarify terminology, batching is the process of merging these draw calls. There exist two types of batching, and GameMaker employs both of these in one way or another, however as it is handled automatically, there is not always enough control:

    • Dynamic Batching: This is the process of dynamically building vertex buffers. It is used when there is a constant change in the number of things being rendered. As work has to be done to construct this buffer, and after construction, the buffer needs to be sent from the CPU to the GPU before it can be drawn, this is slow. GameMaker uses dynamic batching whenever you call any common drawing function, however render state changes such as changing texture, changing rendering colour, blend mode etc; will cause the batch to break, reducing the efficiency of this process.
    • Static Batching: This is the process of pre-creating a vertex buffer containing a collection of static geometry for your scene. You can often create multiple different static buffers, as these are incredibly fast. They get created once, sent and stored in GPU memory, so you only need a simple draw call to trigger the render.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.