Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Buffered console output #5

Closed
ibraimgm opened this issue Mar 6, 2015 · 7 comments
Closed

Buffered console output #5

ibraimgm opened this issue Mar 6, 2015 · 7 comments

Comments

@ibraimgm
Copy link

ibraimgm commented Mar 6, 2015

How about a new APi flavor, that instead of executing the comands on the console buffer, run then on a "backbuffer" and when "flushed" apply the changes just on the needed parts of the screen?

The reason for this is that the Windows console is horribly slow, and sometimes is just much easier to "clear the entire screen and redraw everything" instead of tracking "screen changes" by hand. I actually did implemented something like this for a small game and it worked great. If you guys want, I can fork the project and implement this one when I have free time.

I just need to know what is the best way to structure the API:

  1. Just export new functions (eg. bCursorUp, bSetSGR, etc.). This make the api look consistent, but the b functions can't really be used with the h or the "normal" ones because of the buffer.
  2. Create a namespace System.Console.ANSI.Buffered, exporting the exact same names(except the handle ones) from System.Console.ANSI, but with buffering support.

What do you guys think?

@UnkindPartition
Copy link
Owner

  1. Do the b-versions have the same type as the h-versions, only different semantics? Or will the types differ as well?
  2. Will the b-versions be usable on non-Windows systems (i.e. under ANSI)? What will the semantics be?
  3. Assuming this is implemented, will there be any reason to prefer the old API in some cases, or does the new one completely supersede it?

I wonder if UnkindPartition/tasty#101 is related to this.

@ibraimgm
Copy link
Author

ibraimgm commented Mar 6, 2015

  1. They can have both the h-signatures or the normal ones. IMHO, this buffered output makes way more sense only with stdout, and not with a particular Handle. See the answer below.
  2. Yes. The b-versions are just wraps that instead of actually updating the screen/handle, modify a IORef (think in a big array of chars and colors, for the sake of example). After all the modification, bFlush will compare the modified "array" with the last "flushed" array and only change needed characters.
  3. The old API will be more flexible (h-version) and will have less (zero?) overhead compared with this one.

Just to make this clear: this is a "hack" because proper output buffering on windows simply doesn't work. This idea crossed my mind when I was building a small console game and got really annoyed by all the "flickering" the Windows version had (since every "frame" I clear the screen and draw everything again).

EDIT
This gist shows the idea in practice: https://gist.github.com/ibraimgm/40e307d70feeb4f117cd
Of course, this does not follow the current API (it's just a part of a program I made), but I think is a good enough example. Take a look at screenBuffer and how bSetForeground, bPutChar, etc. just change the array and leave the task to actually sending the commands to blitBuffer.

If you think this kind of thing isn't really "the point" of ansi-terminal, I can create a separate package for this too.

@OlivierSohn
Copy link
Contributor

OlivierSohn commented Nov 17, 2017

Hello, I have exactly the same need but I'm working on OSX, I develop a game and encountered some flickering issues. Using the code of Ibraim fixed it very nicely! I modified the code slightly : https://github.com/OlivierSohn/hamazed/blob/master/src/RenderBackend/Internal/Incremental.hs

I will also make the size of the buffer user defined.

If you want to take a look at the game : https://github.com/OlivierSohn/hamazed

By commenting out line 43 and commenting line 45 of https://github.com/OlivierSohn/hamazed/blob/master/src/Console.hs you can see how the rendering differs with the previous rendering backend : even if buffered output works well it is too much to draw to not flicker on OSX. (use the master branch, not the 1.0 release)

Cheers,
Olivier

EDIT: I think the name "Buffered console output" and namespace "System.Console.ANSI.Buffered" are a bit misleading here. The solution proposed by Ibraim is in fact incremental rendering IMHO (using buffers under the hood). So I would go for System.Console.ANSI.Incremental if I were to chose a name for that functionality.

EDIT2: While there is definitely a need for incremental rendering, I think it would be best to put it in a separate package, to better separate concerns. Also because as it is now it doesn't support all the different types of SGR commands, and I don't see how to support them easily, or if it even makes sense to support some of them in a incremental way...

@UnkindPartition
Copy link
Owner

While there is definitely a need for incremental rendering, I think it would be best to put it in a separate package, to better separate concerns.

I tend to agree, so I'll close this issue if you don't mind. If @ibraimgm and you come up wtih something, I'd be happy to reference it in the ansi-terminal docs.

@OlivierSohn
Copy link
Contributor

OlivierSohn commented Nov 17, 2017

Agreed, and can we continue the discussion on this new module in this closed issue?

I changed my mind about the naming, I was proposing incremental rendering, but differential or delta rendering seems more appropriate to me now (https://en.wikipedia.org/wiki/Delta_encoding)

@ibraimgm do you want to create a hackage package for this code? If yes I'd be happy to contribute, if not I can try to do it.

I think it would make sense to create a package that exposes 2 game-rendering oriented modules which both have the same api, one for "brute force" rendering, and one for "delta" rendering (in the future there could even be more modules with the same api). That is the approach I took for my game, in this folder: https://github.com/OlivierSohn/hamazed/tree/master/src/RenderBackend I put 2 modules that expose the same functions, and in https://github.com/OlivierSohn/hamazed/blob/master/src/Console.hs I can easily switch from one to the other just by changing the import.

And some thoughts on further developments for delta rendering:

  • we could automatically detect the size of the console with a dependency on https://hackage.haskell.org/package/terminal-size to size the buffers appropriately
  • we could also let users specify a size and an origin, so that if they want to render just on a sub rectangle of the screen (which kind of is the case in my game) they can use smaller buffers, which means better time performances (due to a better use if the cache) and less memory used.

Also I noticed @ibraimgm that in the original code for incremental rendering there was no function to reset (clear) the buffer : in the game you were developing maybe you didn't need it because you were writing at every location for every frame? It is not the case for me so I added it.

@ibraimgm
Copy link
Author

ibraimgm commented Dec 7, 2017

@OlivierSohn you got the idea right: IMHO, exposing functions with the same name in two different modules is the way to go. About the function to clear the buffer, I actually did render the entire area every frame, but is unrealistic to not include such function in the API.

If you have the time/need/desire, I think you should create the hackage package. You clearly expanded the original hack into interesting ideas that the community can certainly benefit.

Just remember to put my name somewhere in your LICENSE or CONTRIBUTORS file and we're good :)

@OlivierSohn
Copy link
Contributor

OlivierSohn commented Dec 7, 2017

@ibraimgm For now I quote your name in the Credits sections, with a link to the gist code : https://github.com/OlivierSohn/hamazed/blob/master/README.md

I saw that you made the gist code private, so if you don't want it to be publicly accessible I can remove the link, just tell me.

For the hackage package, I'm thinking about it but I also have other parts of my game that could be of use to anyone developing a game in the console so I'm not sure if I want to do a "Game engine"-like package or just a "Delta rendering" one :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants