Sorry your browser is not supported!

You are using an outdated browser that does not support modern web technologies, in order to use this site please update to a new browser.

Browsers supported include Chrome, FireFox, Safari, Opera, Internet Explorer 10+ or Microsoft Edge.

AppGameKit Classic Chat / Squeezing out all the possible performance out of a CPU cycle. Am I missing something? [Tier 2]

Author
Message
Maindric
14
Years of Service
User Offline
Joined: 22nd Jul 2009
Location:
Posted: 31st Aug 2018 03:19 Edited at: 31st Aug 2018 03:21
Doing some digging, I see that AppGameKit commands are not completely thread safe, at least now how I would have wanted them to be. I wanted to stream in Tiled tilemaps into the engine in the background while the character plays to minimize, or even eliminate, loading screens while playing. Here is an idea that I came up with:



I added this to the Wrapper.h file as a public command based off of what I saw in the agk::Swap() code to sync. I then wrote a "custom" render pipeline to ONLY render what I need (for now):



Here, count ranges from 70,000-100,000(90,000-120,000 with 3 ms) cycles while maintaining 60 FPS no issues on my i3.

Based off of this, I can make a pipeline to load assets one at a time inside of the while loop (I will change up the values after further testing). Is there any downsides that I am missing to this approach? If there is nothing apparent, this is the best way I can come up with to make up for the lack of asset loading on other threads for projects that might require it.
Bengismo
6
Years of Service
User Offline
Joined: 20th Nov 2017
Location: Yorkshire, England
Posted: 31st Aug 2018 08:52 Edited at: 31st Aug 2018 09:15
You can already do what you are doing above in teir 1 or teir 2 by simply setting the FPS to unlimited and delaying the swap() or sync() command until a certain amount of time has passed (to set your FPS manually). Instead of delaying you do something useful in this time period- like loading tilemaps or some other calculation. Its just manually attempting to set a frame rate.

It doesnt really even need any more functions or custom render code to do that....just a timer so you know when you should swap() the backbuffer.

For example this is doing the same thing in teir 1


The real issue is that whatever your loading in the small space of time you have (5mS in your example) isnt guaranteed to load in this time. All your doing in that loop is incrementing a number so it will easily complete very close to 5mS boundary. A small file would load in that time no problem but a heavily compressed large image wouldnt necessarily do so. So your code would still stutter as by the time the Timer() is checked you have gone waaaaay beyond the 5mS you were meant to wait.

Its not a bad idea at all - it works in teir 1 and does give you some small windows of time to do something. But it is currently doable in both Teir1 and 2 without the need for any additional code and doesnt guaratee a frame rate at all unless you know that the work your attempting to do would take a small fraction of 5mS.

You could load data from a file in a separate thread then pass the data over to AppGameKit but you just cant use AppGameKit functions in the separate thread. So you could open a file and load the data but then your would have to pass this data to the main thread and let it put it into a memblock of manually fill an image etc...
Maindric
14
Years of Service
User Offline
Joined: 22nd Jul 2009
Location:
Posted: 31st Aug 2018 16:01 Edited at: 31st Aug 2018 16:08
Interesting implementation. I decided to take it and see what I can do with it. When I tested your version, due to the time it takes to get to the Swap() command in Sync, the framerate does suffer, which is what we want to avoid using this method. Essentially, to reduce load times and potential future stutter, we should only do things when there is plentiful time. I tested in Tier 2 while rendering 10,000 tiles and 3 Spine skeletons with a default animation playing and got 3~4ms render time from my Update2D() Render2DFront() commands alone. I do not know what the time cost is for the rest of the Sync() command. However, I did take a look at your code in Tier 1 and modified it a bit to stabilize the framerate.



If you check the framerate and frame time from your example to this one, you should notice that it sticks to 60fps and 0.01666667ms far more regularly, which is the goal. We cannot allow the GPU to lag the time behind by using all the processing time before Sync(), so we need to handle it manually to get the desired results. This could be further optimized by removing the elements of Sync not needed for a particular project. For example, in a pure 2D game, we do not need to update any 3D elements, which could save a few CPU cycles. I noticed I gained a fair bit of headroom with no (currently) changes if I just use Render2DFront() and remove Render2DBack(). Granted, for 99% of AppGameKit projects, that is all unneeded. I am seeking extra cycles just to push the limits of what I think AppGameKit is capable of.

I also added functions to increase readability of the main loop and processes.

After a minute of digging, I did the Update() part wrong, should be changed to:


I may need to do this in my project too. It seems that Update(0) will update based off of actual elapsed time, which is what Tier2 defaults to using... I will need to do more tests...
Bengismo
6
Years of Service
User Offline
Joined: 20th Nov 2017
Location: Yorkshire, England
Posted: 31st Aug 2018 16:34 Edited at: 31st Aug 2018 16:36
Using SetVSync(1) will lock updates to a monitor refresh rate and so as long as you swap before the next frame there wont be a problem.

Using up the spare time between frames to load something is useful and is just part of utilising all the resources we have.

The real problem was that you have a fixed time period (5ms) and you dont know how long AppGameKit functions or even your own load functions will take to complete. They will both block until completion too. An image load might be 500uS all the way up to 10mS for someone with a slow old hard drive. 10mS+ hard drive seek times aren't uncommon at least in old machines with non SSD drives. Ive no idea what seek and retrieval times are on mobiles but would imagine that with EMMC memory etc that it would be quicker possibly?? You have no way of guaranteeing the load time and AGK's load functions will delay until they are complete.

So no matter how you measure the time or what workaround you put into place you would have to ensure that whatever your calling would complete in much less than the time you have to spare.

Thats why I suggested a second worker thread to do the loading in and even filling the CImage class with data. This will still be thread safe.

If its loading game data to be used inside of AppGameKit then it will have to be done really carefully (small chunks of data at a time).

Its easily doable but id use a separate lower priority loader thread so nothing blocks. That way your guaranteed a frame rate even if a file load takes 100ms! You set it off going and simply check when its complete. Thats real asynchronous processing. Also that way...you can begin to use all 4 cores in the machine....not just 1.
Maindric
14
Years of Service
User Offline
Joined: 22nd Jul 2009
Location:
Posted: 31st Aug 2018 17:08
I see what you are saying. The processes I intend is not to load files, but it make sprites set up their properties. I fully intend on loading files through a separate thread (I just need to work out how to do that for images). The tasks I intend to do each cycle is create one sub image from an already loaded tilemap (agk::CopyImage(…)) and then turn them into sprites, as I said, a single step at a time. That is the intended use case for this, and to create such a to-do list I am going to use a LinkedList as a queue. I am unsure if I am calling Update correctly for this use case, but I plan to figure that out.


Quote: "The real problem was that you have a fixed time period (5ms)"


That is a placeholder value while I get the length of time needed to complete the tasks. It is not meant to be a be all and end all value, but more a proof of concept. The problem with Tier1 (which is why I wasn't thinking of it when I came up with this option) is I intended to do file loading from a separate thread. Most of my use case will be from a larger Tiled map that I sectioned off, only keeping certain "regions" loaded. As I reach a neighboring region, I will start loading that region in. I hope that by the time the player reaches the new region, it will already be loaded in and transition immediately. This can also be done for games such as procedurally generated games where the next level could be generated while playing the current level, so when the player finishes there is no load screen. These are the use cases I intended, not loading files.

For example, in Zelda:LTTP the overworld was 256x256 tiles wide. There are a few ways this large of a map could be created: One large tilemap where the tiles are loaded in as they are approached (rendering 65536 tiles is too much, this is before layering) or many smaller tilemaps. My goal is the former, where I make the whole map in one file, but stream the tiles in as I need them. Most (if not all) of the tiles will be in just a few image files, so loading those in is not the problem (except when handling house/dungeon atlases), it's loading the next screen in as I approach it. I do not want to load the ENTIRE map then hide them, instead I will only load what is needed. Then the idle cycles can create tiles and configure them while the player is playing.

My whole goal of this is to let the CPU keep working, not the hard disk. I am still working on a viable solution to that problem. As you suggest, I will likely load the files in a seperate thread.

I posted this with 2 goals: Offer an additional tool to projects that use the CPU to dynamically load or generate levels to reduce load times and increase CPU efficiency, and see if there is any major process I am overlooking (Such as the Update(time) line, which I now realize is not correct).
GarBenjamin
AGK Developer
7
Years of Service
User Offline
Joined: 30th Nov 2016
Location: USA
Posted: 31st Aug 2018 17:28
Just throwing out that although I think the technical aspects of what you are working on are very cool you don't need to actually render an entire tile map and instead only the on screen section of it. I made a demo in Tier 1 a year or maybe close to two years ago now of using a fixed number of sprites to scroll through a tile map of any size. Basically you just cover the screen with sprites and then you have an extra row of sprites at the top and bottom and an extra column of sprites at the sides. You update the sprites images as needed as you move through the tile map world.
Maindric
14
Years of Service
User Offline
Joined: 22nd Jul 2009
Location:
Posted: 31st Aug 2018 17:40
Quote: "Just throwing out that although I think the technical aspects of what you are working on are very cool you don't need to actually render an entire tile map and instead only the on screen section of it. I made a demo in Tier 1 a year or maybe close to two years ago now of using a fixed number of sprites to scroll through a tile map of any size. Basically you just cover the screen with sprites and then you have an extra row of sprites at the top and bottom and an extra column of sprites at the sides. You update the sprites images as needed as you move through the tile map world."


Interesting... I could implement a pooling system to make this a bit more efficient... Add that to the list of things to test.
GarBenjamin
AGK Developer
7
Years of Service
User Offline
Joined: 30th Nov 2016
Location: USA
Posted: 31st Aug 2018 18:01 Edited at: 31st Aug 2018 23:16
I forgot about this project and should add it to my signature. Anyway I couldn't find it by searching because this was back when I first got into AGK2 and was actually posting to ask what map editors people use etc. I ended up just using Tiled as usual.

But when I made the scrolling engine I had in mind to make it handle "unlimited" scrolling (well by memory but tile map data itself is tiny even with the multiple layers I use for meta data). First I tried using SetViewOffset and posted a demo project of that then updated the engine to achieve same thing without using SetViewOffset.

The posts are here. The version NOT using SetViewOffset is a few posts below that one. I since removed the demo from GameJolt because I think I should only post games there but maybe I will put it back since people post assets, tools and loads of game dev stuff these days.

Keep in mind though this was one of my first AppGameKit projects and I wasn't very familiar with the api at the time so code may be much messier than I would do it now.
Maindric
14
Years of Service
User Offline
Joined: 22nd Jul 2009
Location:
Posted: 31st Aug 2018 18:32
Interestingly, I cannot get your project to run but it does compile properly. I will review it to see your implementation and (eventually) do some tests with it. How did you get the display to scroll without "SetViewOffset"?
Bengismo
6
Years of Service
User Offline
Joined: 20th Nov 2017
Location: Yorkshire, England
Posted: 31st Aug 2018 18:50 Edited at: 31st Aug 2018 18:54
The tiling system i wrote works in an interesting way that gets round these issues

It uses one sprite to cover the screen and the shader works out which tile to draw at what position. It uses a look up to figure out the tile at the position and then draws the pixel. No dynamic creation of sprites, no moving of sprites needed. The tilemap that it referenced was 512x512 tiles but obviously only an area of around 40x32 tiles were ever on the screen.

I found this to be the fastest way of rendering the tilemap as it never attempts to render something not on the screen and only accesses data that it needs to(data which is on the screen). It still supported layers too (by rendering each layer seperately) and used the tiled format. Even tile animations work too.

The only thing you need to worry about then is encoding the tilemap(tile indices) into an image that the shader can use. Thats was the hardest bit to code but works great. One image containing all the tiles and another is used to lookup what tile is where (Ie...a tileset image and a tilemap image).

Fast and easy to use and doesnt involve any setting of screen offsets or multiple sprites. Downside.....sprite collision had to be done in code as the sprite collicion commands wont work if youonly have one sprite but that was fairly easy as its just 2d squares colliding with a player.


I then moved the whole thing into drawing in 3d instead....Ie a mesh with an orthgraphic camera. Thats was to enable lighting to be used in 2d which adds to the look a bit.
Maindric
14
Years of Service
User Offline
Joined: 22nd Jul 2009
Location:
Posted: 31st Aug 2018 19:34
Interesting idea to use shaders. I do not think that would be a great solution for my use case as I intend to use sprite animations and collisions, as well as non-grid conforming objects, as configured in Tiled. Do you have an example of how your process functions?
GarBenjamin
AGK Developer
7
Years of Service
User Offline
Joined: 30th Nov 2016
Location: USA
Posted: 31st Aug 2018 19:40 Edited at: 31st Aug 2018 19:44
Quote: "Interestingly, I cannot get your project to run but it does compile properly. I will review it to see your implementation and (eventually) do some tests with it. How did you get the display to scroll without "SetViewOffset"?"

Hmm... I will have to revisit it my only guess is some update since that time broke something because the project is what I used to record the video that I made into a gif and posted over there.

The scrolling is achieved by manually doing the work SetViewOffset was doing. When moving around the tile map the sprite positions are all updated accordingly. This is the most expensive task in this method and the one that would benefit the most being written in C where it would execute ~50 times faster. This was already plenty fast in BASIC in my tests just saying doing it in C would be all the faster.

@Bengismo that is a very cool solution you designed.
Bengismo
6
Years of Service
User Offline
Joined: 20th Nov 2017
Location: Yorkshire, England
Posted: 31st Aug 2018 19:50 Edited at: 31st Aug 2018 19:54
Animated tiles can be used in the implementation I made as this isn't too hard to cover in the shader (a time variable is used to simply offset the which image is drawn and repeat)

You can still put sprites on top of the tilemap with animations on that sprite. So the player sprite and bullets etc can still be sprites. thats what i did. All items on the objects layer in tiled become a sprite of there own in my implementation so they can be any size and non grid conforming. its the reguar tilemaps in tiled that were covered by a single sprite implementation.

I had one layer for colidable tiles, 1 layer for background tiles (non coliidable), one layer for objects, another layer for effects....

Process is simple really

1) Load in all the tile indices 512x512 of them = 262 thousand tile indices.
2) Encode these into a 512x512 image (i personally use the Red and green colour as x and y offsets into the tileset image) I use the blue colour to store details of the animation (IsAnimating) and the alpha to store some game information
3) Load the tileset image (the one with all your tiles on) mine was 640x512 and each tile was 32x32 pixels - 320 tiles on that image....of course this can be expanded to much more if wanted. Each layer has its own tileset just like in tiled
4) shader simply looks up what tile should be displayed at each pixel using the first image then looks up the pixel in the tile.

if you simply offset the UV variables in the first lookup then the tilemap scrolls.

As I said, this was all really easy. Doing collision was a bit harder but that was just checking for collision between the player and the 9 closest tiles on the collision layer. square on square collision so its ludicrously fast and easy. faster than AppGameKit checking 262144 sprites for a collision with a player sprite Bullets also had to be check for collisions with walls but again, that wasnt hard.

Anyway, sorry i didnt want to derrail this....its just good to see some implementations that are waaaaay outside the box. Time to go out for a beer now
Icerion
5
Years of Service
User Offline
Joined: 3rd Aug 2018
Location:
Posted: 31st Aug 2018 20:07
Hi, @Bengismo! Can you post an example of how to do the above? Walking with a character on a tilemap?

Thanks!
GarBenjamin
AGK Developer
7
Years of Service
User Offline
Joined: 30th Nov 2016
Location: USA
Posted: 31st Aug 2018 20:12 Edited at: 31st Aug 2018 20:13
Sounds very good!

For me at the time I was thinking ah UNLIMITED (til ram empty) scrolling world is what I need so that is why I explored that. After that my view changed because I no longer see the need for it. In fact because I am most interested in low res and ultra low res games I now just pre-render an area in full into a single image.

I have a 2048x2048 image for each visual layer. One of the layers in the tile map defines area boundaries in the world. I load the entire tile map world in at once build a prerendered image of the current are (which even at 256x144 is enough for 8x14 =112 screens or maybe a 1x14 screen vertical area or a 8x1 horizontal area or any size within 8x114). I made what I call warp points in another Tiled layer two different kinds... one entered via manual interaction keypress (i.e E for Enter) and others that just happen such as walking out of the current area... then presto portal destination is looked up and the section of tile map making up the new area are stored (the bounds) this area is prerendered and now all good to roam around here again.

Basically I came to the conclusion that while unlimited scrolling world is cool technically from a gameplay perspective I think providing areas of various dimensions is more interesting.
Icerion
5
Years of Service
User Offline
Joined: 3rd Aug 2018
Location:
Posted: 31st Aug 2018 20:15
@GarBenjamin : I second that! Can someone make a demo of this?

Thanks!
GarBenjamin
AGK Developer
7
Years of Service
User Offline
Joined: 30th Nov 2016
Location: USA
Posted: 31st Aug 2018 21:06 Edited at: 31st Aug 2018 21:10
Make a demo of what? If you mean using a single map to represent a huge world with multiple areas of various sizes this goes into some detail on it. And this shows defining multiple areas of different sizes. Granted this was a 3D game project but I do it the same way in 2D. Basically in a tile map you can store whatever you need. In my 3D FPS I have a layer for Floor, Walls, Ceiling, Enemies & Player positioning, Lights, Paths (waypoints) for enemy movement, Area boundaries, etc.

I don't want to throw his thread off track so that is it. Lol And you may have meant something else.

Login to post a reply

Server time is: 2024-04-16 15:03:01
Your offset time is: 2024-04-16 15:03:01