Any way to speed up SaveBuffer?

Blitz3D Forums/Blitz3D Beginners Area/Any way to speed up SaveBuffer?

codermax(Posted 2014) [#1]
So here's the thing. I wanted to include a realtime video capturing feature for my games, to help anyone who wants to record their runs without the hassle of using various video capturing software that may impose limits or get on people's nerves or just won't work on their system(many of those). Studio X64's BlitzAVI is very good for this!

The bottleneck lies in SaveBuffer though - it's slow when used every frame. Or more specifically, ANY common Blitz-code method for saving graphics content to an image on the HDD is pretty expensive. I've sped things up some, but there's always that "hiccup" due to the expensive process.

I've wondered, is there a faster way to save graphics content to the HDD? I don't want super-fast realtime capture, just wanna record at a CONSTANT framerate of at least 30-40 FPS. My current method keeps the game running, but the resulting video has to be frameskipped to heck for this to happen and the hiccup is still very noticeable.


Yasha(Posted 2014) [#2]
As a general guideline across all languages and engines: file operations are not realtime. Do not attempt to save things in your main loop and expect it not to affect performance. (File ops can be made fast, but most of them are designed with the expectation of not being used for realtime in the first place.)

My best guess would be, instead of trying to save one frame every frame, copy the image data to an expandable buffer and have a second thread (i.e. process - don't mess around trying to make threads work in B3D, they don't) save frames and drop them from the buffer in its own time.


Possible implementations:

- block copy image to a bank with a system function like memmove/RtlMoveMemory, use WriteBytes to write to a stream, have the second process read blocks from the stream into another bank, and save from that (you can probably do this in fewer steps with more system functions)

- investigate process shared memory

I have no idea if these would actually be faster, but they seem like they should be.


codermax(Posted 2014) [#3]
Hey Yasha, glad you're still around! :)

Right, I probably need to manipulate a memory block during gameplay instead.

So let me get this straight...
First, I need to place the contents of the backbuffer into another buffer like an image/texture. I heard reading directly from the backbuffer is much slower than copying the screen to an image, so I'll do that.

For reference, "RFrame" refers to the image in memory that I'll copy the screen contents to. Of course its size is the window's resolution.


;copy the backbuffer's current contents into an image/texture.
CopyRect 0,0,GraphicsWidth(),GraphicsHeight(),0,0,BackBuffer(),ImageBuffer(RFrame)



Just to confirm the image got the backbuffer's contents, I used SaveImage. It doesn't affect my game's framerate at all when it's being run at every frame either!

So now I have a copy of the screen in an image. I need to transfer the contents to a bank. Here's my attempt at doing so:


	;---------------------------------------------------------------
	; Getting image buffer in a bank
	;---------------------------------------------------------------
	;set the size we'll need the buffer to be.
	bsize=GraphicsWidth()*GraphicsHeight()*4
	;get the buffer.
	buf = ImageBuffer(RFrame)
	;lock the buffer so we can manipulate it.
	LockBuffer buf
	;create a bank the same size for transfering.
	LocBnk = CreateBank(bsize)
	;move the block of memory from one location to the other.
	MoveMemoryObjInt(LocBnk,buf,bsize)
	;unlock the buffer.
	UnlockBuffer buf



I have the required decls/dll, but unfortunately I get a MAV. The cause of the error was "MoveMemoryObjInt".
I have freeimage installed, and I tried using the equivalent FreeImage function as well and that gives me a MAV too.

I'm kind of stuck now. Is there anything else I can try?


Yasha(Posted 2014) [#4]
The image buffer handle that you get isn't the raw data to copy - Blitz is still hiding that under one more layer of indirection (I think "buffers" might actually be DirectDrawSurfaces? been a long time). According to this old thread, you should use buffer+72 as the data pointer. And of course be careful that you're using the right combination of * vs. % on your decls (needs to be * for the bank and % for the buffer, I think).


RemiD(Posted 2014) [#5]
Maybe you could simply have a dim array to store all the images of all the renders (with copyrect) and at the end use these images to either create a replay of the screen capture (you draw the images one after the other at a certain speed) or to export a video ?
But if you do that you will need to define a maximum allowed fps and a maximum allowed time for the screen capture depending on the remaining free memory.

If the screen capture is only for your game, another approach is to capture the state of each component at each frame and then for the replay turn/move/animate each component depending on its state at each frame.


GfK(Posted 2014) [#6]
If you're trying to record video/frames at your game's logic framerate, it's going to look bloody awful when you attempt to play it back later at the standard 24 or 30FPS.

Seriously, you're trying to solve a problem that has already been solved by way of external video capture software. There are plenty of them about, although I can't really suggest what to do about the ones that just "get on your nerves".


_PJ_(Posted 2014) [#7]
It may be "better" in many respects to approach the recording differently. Depending on the requirement for video recordings and their use beyond the game itself (i.e. if the intention is for players to post videos to youtube etc.)

Rather than record the entire display buffer and save frames to disk, it may be more advantageous to "simply" store the player input and/or object information (position/orientation etc. whatever is relevant) and then perhaps even write this to disk at game-over.
Provided the right information is recorded in this way, then it should follow that to replay the 'recording' would involve pseudo 'running the game' but instead of taking actual input, just apply the recordeed inputs at the right times and any non-random *(use and store RndSeed values where applicable) can be repeated as in the actual play through.

Although this limits the actual potentials of re-playing and sharing of the video data to those with a copy of the game, it should be much more economic.

This is a similar technique to that used by Valve for their HLTV demo recordings.