Painting. ITexture::Lock() seems too slow

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
Post Reply
Mloren
Posts: 114
Joined: Mon Aug 07, 2006 2:30 am
Location: Australia
Contact:

Painting. ITexture::Lock() seems too slow

Post by Mloren »

Part of the program I'm making involves a paint-like interface.
You can select one of a bunch of different painting tools and use them to draw, works pretty much just like MSPaint.

To do this, I have an IGUIImage which can be scrolled.
I have a pixel array and when the user is painting I get the mouse position, alter the appropriate pixels, call lock() on the IGUIImage's texture, copy the pixel array across and then call unlock().

The image that you can paint on is a huge 2048x2048 and I lock() and unlock() it once per frame.

This causes my framerate to drop from 60 to 15.
It's the lock() and unlock() that's doing it, if I comment those out, the framerate goes back to 60.

I know the size of the image is big and that lock() and unlock() are bound to be slow operations but still this seems like way too big of a performance hit.
Also the CPU isn't above 50% so it must be the GPU that's struggling. My video card is not slow (GeForce series 8 ) so I can't blame that.

Does anyone have a suggestion for how I could do this better? I need to keep the framerate at 60.

I'm also wondering if lock()/unlock() could be optimized, I expect them to be slow but not that slow.
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

Yeah, I've already thought about adding a 'write-only' flag for the lock. This would reduce the data travel by 50% (right now the data is copied from GPU to CPU, changed, and copied back from CPU to GPU). Another optimization would be to change only parts of the texture. So far, we always upload the whole texture. We could introduce a rect parameter to lock, which would upload only that portion. Both seems possible and should reduce the transfer costs significantly.
greenya
Posts: 1012
Joined: Sun Jan 21, 2007 1:46 pm
Location: Ukraine
Contact:

Post by greenya »

Does anyone have a suggestion for how I could do this better? I need to keep the framerate at 60.
Instead of one single 2048x2048 piece you can use couple smaller pieces, like 512x512 or even 256x256. You will have two dimensional array of these pieces; for example if you use 256x256 parts, you need 8x8 array (64 pieces). So each time when you need to draw something at x,y -- you calculate destination piece and lock/unlock only that piece.
slavik262
Posts: 753
Joined: Sun Nov 22, 2009 9:25 pm
Location: Wisconsin, USA

Post by slavik262 »

hybrid wrote:Yeah, I've already thought about adding a 'write-only' flag for the lock. This would reduce the data travel by 50% (right now the data is copied from GPU to CPU, changed, and copied back from CPU to GPU). Another optimization would be to change only parts of the texture. So far, we always upload the whole texture. We could introduce a rect parameter to lock, which would upload only that portion. Both seems possible and should reduce the transfer costs significantly.
This would be extremely useful. How easily could this be implemented?
bitplane
Admin
Posts: 3204
Joined: Mon Mar 28, 2005 3:45 am
Location: England
Contact:

Post by bitplane »

I would "write" to the texture by drawing into a transparent render target overlay using the hardware, then flatten it into a single image every so often.
Submit bugs/patches to the tracker!
Need help right now? Visit the chat room
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

slavik262 wrote:
hybrid wrote:Yeah, I've already thought about adding a 'write-only' flag for the lock. This would reduce the data travel by 50% (right now the data is copied from GPU to CPU, changed, and copied back from CPU to GPU). Another optimization would be to change only parts of the texture. So far, we always upload the whole texture. We could introduce a rect parameter to lock, which would upload only that portion. Both seems possible and should reduce the transfer costs significantly.
This would be extremely useful. How easily could this be implemented?
The wite-only flag should be pretty simple. Just replacing the bool flag by an enum value. The rectangle region could be harder. I have to check for the driver support, and think about proper storing of the areas and data. But both things should be possible within a few changes.
Mloren
Posts: 114
Joined: Mon Aug 07, 2006 2:30 am
Location: Australia
Contact:

Post by Mloren »

bitplane wrote:I would "write" to the texture by drawing into a transparent render target overlay using the hardware, then flatten it into a single image every so often.
I'm not sure what you mean by "render target overlay".
Is this something that irrlicht lets you do? or would I have to go directly to the hardware for this?
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

hybrid wrote:
slavik262 wrote:
hybrid wrote:Yeah, I've already thought about adding a 'write-only' flag for the lock. This would reduce the data travel by 50% (right now the data is copied from GPU to CPU, changed, and copied back from CPU to GPU). Another optimization would be to change only parts of the texture. So far, we always upload the whole texture. We could introduce a rect parameter to lock, which would upload only that portion. Both seems possible and should reduce the transfer costs significantly.
This would be extremely useful. How easily could this be implemented?
The wite-only flag should be pretty simple. Just replacing the bool flag by an enum value. The rectangle region could be harder. I have to check for the driver support, and think about proper storing of the areas and data. But both things should be possible within a few changes.
Argh. It seems that I either don't understand Direct3D or it does neither have WRITE_ONLY, nor easy rectangle locking. The latter exists, but moves the lock region into a new image window. Only the locked rectangle will be returned as an image. This introduces some additional complexity due to the different functionality in the other drivers. So I guess this will only work when we change the lock to return an IImage instead of a raw pointer.
Anyway, the write_only flag is implemented in SVN for OpenGL now.
nespa
Posts: 167
Joined: Wed Feb 24, 2010 12:02 pm

Post by nespa »

My metode to increase the painting speed:

We have 2 textures, the source texture and the destination texture.
The destination texture must be visible , so...must be rendered every frame.The source texture it is not necesary to be visible.
My metode:

outside of the renderer loop:
1. Lock source texture to get a start pointer to pixels;

in the renderer loop:
Start main loop
2.Lock the destination texture;
3.Call a function to blend all the pixels in the rectangle area from the 2 textures(src and dest) and write them to dest texture;
4.UnLock the destination texture;
..........Draw (Render)
End main loop
5.UnLock the source texture;

I used this metode in Clady3dTerrainEditor and in CladyColorMapEditor;
DirectX is faster than OpenGl in my tests;
Mloren
Posts: 114
Joined: Mon Aug 07, 2006 2:30 am
Location: Australia
Contact:

Post by Mloren »

nespa wrote:My metode to increase the painting speed:

We have 2 textures, the source texture and the destination texture.
The destination texture must be visible , so...must be rendered every frame.The source texture it is not necesary to be visible.
My metode:

outside of the renderer loop:
1. Lock source texture to get a start pointer to pixels;

in the renderer loop:
Start main loop
2.Lock the destination texture;
3.Call a function to blend all the pixels in the rectangle area from the 2 textures(src and dest) and write them to dest texture;
4.UnLock the destination texture;
..........Draw (Render)
End main loop
5.UnLock the source texture;

I used this metode in Clady3dTerrainEditor and in CladyColorMapEditor;
DirectX is faster than OpenGl in my tests;
This still involves locking and unlocking the destination texture every frame which is the bottleneck for me. Probably just because the texture is so large. (2048x2048)
I've solved this using a variation of greenya's suggestion, I have multiple textures in strips, 2048x64 pixels each. I just go down the screen, updating them as necessary almost like scanlines on a TV.
disks86
Posts: 15
Joined: Tue Nov 03, 2009 4:04 am
Contact:

copyTo

Post by disks86 »

Alright so maybe I just don't understand but why do we have to lock at all. I'm pretty sure in sdl you can bitblit any sdl surface to any other surface whether it is in video memory or system memory. So my question is this can we have a copyTo method like the one in IImage that can target another ITexture and for that matter another one in IImage that can target an ITexture. Both of which including a rectangle parameter of course.

Guess I never realized how much of a pain dealing with textures was before now.

http://www.gamedev.net/community/forums ... 1&#2646821
I'm not random you just don't get the &.
Mloren
Posts: 114
Joined: Mon Aug 07, 2006 2:30 am
Location: Australia
Contact:

Post by Mloren »

disks86 wrote:Alright so maybe I just don't understand but why do we have to lock at all. I'm pretty sure in sdl you can bitblit any sdl surface to any other surface whether it is in video memory or system memory.
The lock command ensures that the GPU isn't using the texture while the GPU is writing to it, there's no guarantee that the CPU and GPU are doing anything in sync so even if you can write directly to the GPU's version of the texture, there's no guarantee it would be safe to do so.
The GPU could be half way through rendering the texture when you change it which would result in weird graphical corruption for a frame where it was half the old texture and half the new one.
nathanf534
Posts: 199
Joined: Tue Dec 09, 2008 2:55 am

Post by nathanf534 »

The GPU could be half way through rendering the texture when you change it which would result in weird graphical corruption for a frame where it was half the old texture and half the new one.
If thats the only side effect for such an expensive operation, does it really matter? If you are drawing that at about 60 fps, you wouldn't really notice this.
while(signatureEmpty){cout<<wittyComment();}
Mloren
Posts: 114
Joined: Mon Aug 07, 2006 2:30 am
Location: Australia
Contact:

Post by Mloren »

nathanf534 wrote:
The GPU could be half way through rendering the texture when you change it which would result in weird graphical corruption for a frame where it was half the old texture and half the new one.
If thats the only side effect for such an expensive operation, does it really matter? If you are drawing that at about 60 fps, you wouldn't really notice this.
Yes you would. Especially if the old texture and new texture were very different. Also I'm not even sure if its possible to change the GPU texture mid render, it may just corrupt memory and crash. not sure on that.
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

lock and unlock on a texture ensure that the data is sync'ed between CPU and GPU. This involves a lot of data copying, which might be unnecessary as stated above. So far, you could only avoid the upload on unlock (read_only). In SVN/trunk we also have the write_only mode, but so far only for OpenGL. What you cannot do (and what is not possible in general on all graphics cards) is to copy one texture to another without copying it to the CPU. I am planning for such a method, which would be fast on most cards, but which might still involve a full copy-out copy-in via CPU on some cards. This would hold true for SDL as well.
Post Reply