News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Double Buffering

Started by Bieb, March 19, 2005, 10:36:19 PM

Previous topic - Next topic

Bieb

I've attempted to implement double buffering in an application, using the following as the procedure for a static control.

Window1Static1 Proc Private hWnd:HWND, uMsg:ULONG, wParam:WPARAM, lParam:LPARAM

Local ps:PAINTSTRUCT
Local StaticRect:RECT
Local TempLoop:DWord
Local S1:DWord
Local S2:DWord
Local S3:DWord
Local S4:DWord
Local S5:DWord
Local S6:DWord
Local S7:DWord
Local S8:DWord
Local MathTemp1:DWord
Local MathTemp2:DWord
Local dBufferHandle:DWord

.If uMsg == WM_PAINT
Cmp TurnOff, 1
Je @F
Push Seed1
Pop S1
Push Seed2
Pop S2
Push Seed3
Pop S3
Push Seed4
Pop S4
Push Seed5
Pop S5
Push Seed6
Pop S6
Push Seed7
Pop S7
Push Seed8
Pop S8
Invoke BeginPaint, hWnd, Addr ps
Mov StaticHDC, Eax
Invoke CreateCompatibleDC, StaticHDC
Mov dBufferHandle, Eax
Invoke GetClientRect, hWnd, Addr StaticRect
Invoke FillRect, dBufferHandle, Addr StaticRect, BlackPen
Mov Eax, Loops
Mov TempLoop, Eax
.While TempLoop > 0
;BEGIN CUSTOMIZABLE DRAWING LOOP--------------------------------------
Invoke DeleteObject, BlackPen
Invoke CreateSolidBrush, 00000000H
Mov BlackPen, Eax
Invoke SelectObject, dBufferHandle, BlackPen
Invoke DeleteObject, RandomPen
Mov Eax, S7
Add Eax, S1
Invoke CreatePen, PS_SOLID, 1, Eax
Mov RandomPen, Eax
Invoke SelectObject, dBufferHandle, RandomPen
Mov Ecx, S3
Invoke MoveToEx, dBufferHandle, StaticRect.right, StaticRect.top, NULL
Invoke LineTo, dBufferHandle, S1, S5
Invoke DeleteObject, RandomPen
Mov Ecx, S3
Xor Ecx, 00325265H
Invoke CreatePen, PS_SOLID, 1, Ecx
Mov RandomPen, Eax
Invoke SelectObject, dBufferHandle, RandomPen
Invoke MoveToEx, dBufferHandle, StaticRect.left, StaticRect.bottom, NULL
Invoke LineTo, dBufferHandle, S3, S4
Invoke DeleteObject, RandomPen
Mov Eax, S6
Xor Eax, 00FF22AAH
Mov Ecx, S5
Shr Eax, 2
Invoke CreatePen, PS_SOLID, 1, Eax
Mov RandomPen, Eax
Invoke SelectObject, dBufferHandle, RandomPen
Mov Eax, StaticRect.bottom
Xor Edx, Edx
Mov Ecx, 2
Div Ecx
Mov MathTemp1, Eax
Mov Eax, StaticRect.right
Xor Edx, Edx
Mov Ecx, 2
Div Ecx
Mov MathTemp2, Eax
Invoke MoveToEx, dBufferHandle, MathTemp1, MathTemp2, NULL
Invoke LineTo, dBufferHandle, S6, S2
Mov Eax, S3
And Eax, 0000FF11H
Invoke Ellipse, dBufferHandle, S4, S8, S3, S6
Invoke SelectObject, dBufferHandle, BlackPen
Invoke DeleteObject, RandomPen
Mov Ecx, S4
And Ecx, 0022AAFFH
Invoke CreatePen, PS_SOLID, 1, 000FF2200H
Mov RandomPen, Eax
Invoke SelectObject, dBufferHandle, RandomPen
.If TempLoop > 1950
Mov Eax, Offset PointArray
Mov Ecx, S2
Mov [Eax], Ecx
Mov Eax, Offset PointArray + 4
Mov Ecx, S5
Mov [Eax], Ecx
Mov Eax, Offset PointArray + 8
Mov Ecx, S6
Mov [Eax], Ecx
Mov Eax, Offset PointArray + 12
Mov Ecx, S7
Mov [Eax], Ecx
Invoke PolyBezier, dBufferHandle, Addr PointArray, 4
.EndIf
Add S1, 5
Add S2, 5
Mov Eax, S2
Xor Eax, S3
Mov Eax, S2
Add S3, 5
Shr S4, 2
Mov Eax, S5
Add S4, Eax
Mov Eax, S4
Mov Ecx, S2
Xor Edx, Edx
Mul Ecx
Xor Edx, Edx
Mov Eax, S1
Div Ecx
Add S6, 5
Add S7, 5
Add S8, 5
;END CUSTOMIZABLE DRAWING LOOP----------------------------------------
Dec TempLoop
.EndW
Invoke BitBlt, StaticHDC, 0, 0, StaticRect.right, StaticRect.bottom, dBufferHandle, 0, 0, BLACKNESS
Invoke ReleaseDC, NULL, dBufferHandle
Invoke EndPaint, hWnd, Addr ps
Invoke SendMessage, App.Main, WM_PAINT, NULL, NULL
.EndIf
@@:
Return FALSE
Window1Static1 EndP


That code worked fine when things were drawn directly to the Static control, but now that I attempt to use a buffer, all I get is black.  Any ideas?

raymond

After you create a compatible DC, you also have to create a bitmap of the required size and select it in that DC in order to draw on it.

Raymond
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

Bieb

Okay, so I've replaced the previous procedure with this one, which does that.  I still get nothing but black, though.


Window1Static1 Proc Private hWnd:HWND, uMsg:ULONG, wParam:WPARAM, lParam:LPARAM

Local ps:PAINTSTRUCT
Local StaticRect:RECT
Local TempLoop:DWord
Local S1:DWord
Local S2:DWord
Local S3:DWord
Local S4:DWord
Local S5:DWord
Local S6:DWord
Local S7:DWord
Local S8:DWord
Local MathTemp1:DWord
Local MathTemp2:DWord
Local bBufferHandle:DWord
Local BitMapHandle:DWord

.If uMsg == WM_PAINT
Cmp TurnOff, 1
Je @F
Push Seed1
Pop S1
Push Seed2
Pop S2
Push Seed3
Pop S3
Push Seed4
Pop S4
Push Seed5
Pop S5
Push Seed6
Pop S6
Push Seed7
Pop S7
Push Seed8
Pop S8
Invoke BeginPaint, hWnd, Addr ps
Mov StaticHDC, Eax
Invoke GetClientRect, hWnd, Addr StaticRect
Invoke CreateCompatibleDC, StaticHDC
Mov bBufferHandle, Eax
Invoke CreateCompatibleBitmap, bBufferHandle, StaticRect.right, StaticRect.bottom
Mov BitMapHandle, Eax
Invoke SelectObject, bBufferHandle, BitMapHandle
Invoke FillRect, bBufferHandle, Addr StaticRect, BlackPen
Mov Eax, Loops
Mov TempLoop, Eax
.While TempLoop > 0
;BEGIN CUSTOMIZABLE DRAWING LOOP--------------------------------------
Invoke DeleteObject, BlackPen
Invoke CreateSolidBrush, 00000000H
Mov BlackPen, Eax
Invoke SelectObject, bBufferHandle, BlackPen
Invoke DeleteObject, RandomPen
Mov Eax, S7
Add Eax, S1
Invoke CreatePen, PS_SOLID, 1, Eax
Mov RandomPen, Eax
Invoke SelectObject, bBufferHandle, RandomPen
Mov Ecx, S3
Invoke MoveToEx, bBufferHandle, StaticRect.right, StaticRect.top, NULL
Invoke LineTo, bBufferHandle, S1, S5
Invoke DeleteObject, RandomPen
Mov Ecx, S3
Xor Ecx, 00325265H
Invoke CreatePen, PS_SOLID, 1, Ecx
Mov RandomPen, Eax
Invoke SelectObject, bBufferHandle, RandomPen
Invoke MoveToEx, bBufferHandle, StaticRect.left, StaticRect.bottom, NULL
Invoke LineTo, bBufferHandle, S3, S4
Invoke DeleteObject, RandomPen
Mov Eax, S6
Xor Eax, 00FF22AAH
Mov Ecx, S5
Shr Eax, 2
Invoke CreatePen, PS_SOLID, 1, Eax
Mov RandomPen, Eax
Invoke SelectObject, bBufferHandle, RandomPen
Mov Eax, StaticRect.bottom
Xor Edx, Edx
Mov Ecx, 2
Div Ecx
Mov MathTemp1, Eax
Mov Eax, StaticRect.right
Xor Edx, Edx
Mov Ecx, 2
Div Ecx
Mov MathTemp2, Eax
Invoke MoveToEx, bBufferHandle, MathTemp1, MathTemp2, NULL
Invoke LineTo, bBufferHandle, S6, S2
Mov Eax, S3
And Eax, 0000FF11H
Invoke Ellipse, bBufferHandle, S4, S8, S3, S6
Invoke SelectObject, bBufferHandle, BlackPen
Invoke DeleteObject, RandomPen
Mov Ecx, S4
And Ecx, 0022AAFFH
Invoke CreatePen, PS_SOLID, 1, 000FF2200H
Mov RandomPen, Eax
Invoke SelectObject, bBufferHandle, RandomPen
.If TempLoop > 2950
Mov Eax, Offset PointArray
Mov Ecx, S2
Mov [Eax], Ecx
Mov Eax, Offset PointArray + 4
Mov Ecx, S5
Mov [Eax], Ecx
Mov Eax, Offset PointArray + 8
Mov Ecx, S6
Mov [Eax], Ecx
Mov Eax, Offset PointArray + 12
Mov Ecx, S7
Mov [Eax], Ecx
Invoke PolyBezier, bBufferHandle, Addr PointArray, 4
.EndIf
Add S1, 5
Add S2, 5
Mov Eax, S2
Xor Eax, S3
Mov Eax, S2
Add S3, 5
Shr S4, 2
Mov Eax, S5
Add S4, Eax
Mov Eax, S4
Mov Ecx, S2
Xor Edx, Edx
Mul Ecx
Xor Edx, Edx
Mov Eax, S1
Div Ecx
Add S6, 5
Add S7, 5
Add S8, 5
;END CUSTOMIZABLE DRAWING LOOP----------------------------------------
Dec TempLoop
.EndW
Invoke BitBlt, StaticHDC, 0, 0, StaticRect.right, StaticRect.bottom, bBufferHandle, 0, 0, 42H
Invoke DeleteDC, bBufferHandle
Invoke DeleteObject, BitMapHandle
Invoke EndPaint, hWnd, Addr ps
Invoke SendMessage, App.Main, WM_PAINT, NULL, NULL
.EndIf
@@:
Return FALSE
Window1Static1 EndP

raymond

The main purpose of double buffering is to be able to draw something once and keep it available for display whenever required. You don't redraw everything everytime a WM_PAINT message is received by your app.

The only code you then need when a WM_PAINT message is received is to BitBlt from your buffer to the main screen. You thus keep your compatible DC with your drawing until you process the WM_CLOSE message.

Raymond
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

Bieb

Ah, it would run a lot faster if it didn't have to redraw everything everytime the control needeed to be painted, wouldn't it?  I guess I'll see what I can do to that effect.

Bieb

Okay, I took a shot at doing that, and I ended up with the following code scattered throughout the project.

This is where I initialize the back buffer

Invoke GetDC, StaticHandle
Mov StaticHDC, Eax
Invoke GetClientRect, StaticHandle, Addr StaticRect
Invoke CreateCompatibleDC, StaticHDC
Mov BufferHandle, Eax
Invoke CreateCompatibleBitmap, BufferHandle, StaticRect.right, StaticRect.bottom
Mov BitMapHandle, Eax
Invoke SelectObject, BufferHandle, BitMapHandle
Invoke ReleaseDC, StaticHandle, StaticHDC


This is what gets executed when the "draw" button is pushed

Invoke GetValue, Slide1Handle
Mov Seed1, Eax
Invoke GetValue, Slide2Handle
Mov Seed2, Eax
Invoke GetValue, Slide3Handle
Mov Seed3, Eax
Invoke GetValue, Slide4Handle
Mov Seed4, Eax
Invoke GetValue, Slide5Handle
Mov Seed5, Eax
Invoke GetValue, Slide6Handle
Mov Seed6, Eax
Invoke GetValue, Slide7Handle
Mov Seed7, Eax
Invoke GetValue, Slide8Handle
Mov Seed8, Eax
Mov Loops, 5000
Invoke DrawLoop
Invoke GetClientRect, StaticHandle, Addr StaticRect
Invoke InvalidateRect, StaticHandle, Addr StaticRect, FALSE


This is the drawloop proedure

DrawLoop Proc Private
Local TempLoop:DWord
Local S1:DWord
Local S2:DWord
Local S3:DWord
Local S4:DWord
Local S5:DWord
Local S6:DWord
Local S7:DWord
Local S8:DWord
Local MathTemp1:DWord
Local MathTemp2:DWord

Push Seed1
Pop S1
Push Seed2
Pop S2
Push Seed3
Pop S3
Push Seed4
Pop S4
Push Seed5
Pop S5
Push Seed6
Pop S6
Push Seed7
Pop S7
Push Seed8
Pop S8
Invoke FillRect, BufferHandle, Addr StaticRect, BlackPen
Mov Eax, Loops
Mov TempLoop, Eax
.While TempLoop > 0
;BEGIN CUSTOMIZABLE DRAWING LOOP--------------------------------------
Invoke DeleteObject, BlackPen
Invoke CreateSolidBrush, 00000000H
Mov BlackPen, Eax
Invoke SelectObject, BufferHandle, BlackPen
Invoke DeleteObject, RandomPen
Mov Eax, S7
Add Eax, S1
Invoke CreatePen, PS_SOLID, 1, Eax
Mov RandomPen, Eax
Invoke SelectObject, BufferHandle, RandomPen
Mov Ecx, S3
Invoke MoveToEx, BufferHandle, StaticRect.right, StaticRect.top, NULL
Invoke LineTo, BufferHandle, S1, S5
Invoke DeleteObject, RandomPen
Mov Ecx, S3
Xor Ecx, 00325265H
Invoke CreatePen, PS_SOLID, 1, Ecx
Mov RandomPen, Eax
Invoke SelectObject, BufferHandle, RandomPen
Invoke MoveToEx, BufferHandle, StaticRect.left, StaticRect.bottom, NULL
Invoke LineTo, BufferHandle, S3, S4
Invoke DeleteObject, RandomPen
Mov Eax, S6
Xor Eax, 00FF22AAH
Mov Ecx, S5
Shr Eax, 2
Invoke CreatePen, PS_SOLID, 1, Eax
Mov RandomPen, Eax
Invoke SelectObject, BufferHandle, RandomPen
Mov Eax, StaticRect.bottom
Xor Edx, Edx
Mov Ecx, 2
Div Ecx
Mov MathTemp1, Eax
Mov Eax, StaticRect.right
Xor Edx, Edx
Mov Ecx, 2
Div Ecx
Mov MathTemp2, Eax
Invoke MoveToEx, BufferHandle, MathTemp1, MathTemp2, NULL
Invoke LineTo, BufferHandle, S6, S2
Mov Eax, S3
And Eax, 0000FF11H
Invoke Ellipse, BufferHandle, S4, S8, S3, S6
Invoke SelectObject, BufferHandle, BlackPen
Invoke DeleteObject, RandomPen
Mov Ecx, S4
And Ecx, 0022AAFFH
Invoke CreatePen, PS_SOLID, 1, 000FF2200H
Mov RandomPen, Eax
Invoke SelectObject, BufferHandle, RandomPen
.If TempLoop > 4950
Mov Eax, Offset PointArray
Mov Ecx, S2
Mov [Eax], Ecx
Mov Eax, Offset PointArray + 4
Mov Ecx, S5
Mov [Eax], Ecx
Mov Eax, Offset PointArray + 8
Mov Ecx, S6
Mov [Eax], Ecx
Mov Eax, Offset PointArray + 12
Mov Ecx, S7
Mov [Eax], Ecx
Invoke PolyBezier, BufferHandle, Addr PointArray, 4
.EndIf
Add S1, 5
Add S2, 5
Mov Eax, S2
Xor Eax, S3
Mov Eax, S2
Add S3, 5
Shr S4, 2
Mov Eax, S5
Add S4, Eax
Mov Eax, S4
Mov Ecx, S2
Xor Edx, Edx
Mul Ecx
Xor Edx, Edx
Mov Eax, S1
Div Ecx
Add S6, 5
Add S7, 5
Add S8, 5
;END CUSTOMIZABLE DRAWING LOOP----------------------------------------
Dec TempLoop
.EndW
Ret
DrawLoop EndP


And this is the window procedure for the static control

Window1Static1 Proc Private hWnd:HWND, uMsg:ULONG, wParam:WPARAM, lParam:LPARAM

Local ps:PAINTSTRUCT

.If uMsg == WM_PAINT
Invoke DrawLoop
Invoke BeginPaint, hWnd, Addr ps
Mov StaticHDC, Eax
Invoke BitBlt, StaticHDC, 0, 0, StaticRect.right, StaticRect.bottom, BufferHandle, 0, 0, 42H
Invoke EndPaint, hWnd, Addr ps
Invoke SendMessage, App.Main, WM_PAINT, NULL, NULL
.EndIf
Return FALSE
Window1Static1 EndP


Still, all I get is black in the static control.  At first, I left the drawing code in the static controls window procedure, and then just added the BitBlt to the end of it, and I'd see all the drawing happen, and then it would go to black when the buffer was blitted on.  So, what could be the problem now?  It seems to be that nothing is being drawn to the buffer.

Relvinian

Quote from: Robert Bieber on March 21, 2005, 10:08:24 PM
Okay, I took a shot at doing that, and I ended up with the following code scattered throughout the project.

This is where I initialize the back buffer

Invoke GetDC, StaticHandle
Mov StaticHDC, Eax
Invoke GetClientRect, StaticHandle, Addr StaticRect
Invoke CreateCompatibleDC, StaticHDC
Mov BufferHandle, Eax
Invoke CreateCompatibleBitmap, BufferHandle, StaticRect.right, StaticRect.bottom
Mov BitMapHandle, Eax
Invoke SelectObject, BufferHandle, BitMapHandle
Invoke ReleaseDC, StaticHandle, StaticHDC




This code above has one small problem.  When you create a compatible bitmap, you need to use the screen DC and not a DC you create.  So, on your

Invoke CreateCompatibleBitmap, BufferHandle, StaticRect.right, StaticRect.bottom


change it to the following:

Invoke CreateCompatibleBitmap, StaticHDC, StaticRect.right, StaticRect.bottom


Only other thing I can see right off the top of my head is if the static control (where you are getting your DC from) isn't initialized yet, you'll get back a NULL DC value from your invoke GetDC call.  You may want to just use the screen DC instead since you are releasing it quickly anyway.

Relvinian

Bieb

But if I use the screen to create my bitmap, won't I get one that's too big?

raymond

When you create the bitmap, you have to specify its specific size which has nothing to do with the size of the DC.

BTW, why do you send a WM_PAINT message to your App.Main from your static control procedure?

Raymond
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

Bieb

Alright, I've replaced the buffer initialization code with the following.


Invoke GetDC, StaticHandle
Mov StaticHDC, Eax
Invoke GetClientRect, StaticHandle, Addr StaticRect
Invoke CreateCompatibleDC, StaticHDC
Mov BufferHandle, Eax
Invoke CreateCompatibleBitmap, StaticHDC, 428, 432
Mov BitMapHandle, Eax
Invoke SelectObject, BufferHandle, BitMapHandle
Invoke ReleaseDC, StaticHandle, StaticHDC


I would have used the screen to create the compatible DC, but I don't know how to get the screen HDC.  How do you?  Anyways, I'm still getting nothing but black.

Oh, and I'm not sure exactly why that sendmessage was there.  I don't remember putting it there, but I think it might have served some purpose at some earlier stage of development.

Relvinian

To get the screen DC (also known as the Desktop DC), instead of passing in a window handle, pass in NULL.  Same goes when you you release it.

Example code:

; get the Desktop DC
invoke GetDC, NULL       ; Desktop DC

; use the DC handle here


; release the Desktop DC handle
invoke ReleaseDC, NULL, <dc handle>


Relvinian.

PS - I'll take a closer look at your code when I get home to see if there is anything else you may be missing.

raymond

Based on what you had posted originally when handling the WM_PAINT message in the static proc, I would be curious to know how you handle the WM_PAINT message in your main app.

Also, when you modify a part of the code, it can often affect other parts which may then also require modifications. Posting one modification out of context is difficult to analyze.

Raymond
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

Bieb

Here's the whole thing.  It's an Easy Code project.  If you have Easy Code, you can just open RGG.ecp.  If you don't, open Window1.ecw in a text editor, and you'll see a list of all the controls on the window and their properties, followed by all the code for the window.  There's also an executable in the release folder, that doesn't do a whole lot right now.

[attachment deleted by admin]

raymond

I have to assume that the main window creation code is in another file which was not included in the zipped package. I thus have to also assume:
- that no other part of the code is missing from that zipped package, and
- that the Window1Procedure Proc is the main window procedure.

I have not been able to figure out how the Window1Static1 Proc is being used or called, nor how it may receive any message, specially the WM_PAINT message. As far as I could find, that was the only place where you had any code to copy your buffer content to the main window.

You call the DrawLoop function from the main window procedure but that function lacks any code to transfer the drawing to the main window. Furthermore, the only function called when you process the WM_PAINT message in the main window procedure is the InvalidateRect which will certainly not transfer any drawing from one DC to another. And the WM_PAINT message  would only be received if there is any change in the overall size or visibility of the main window.

Raymond

P.S. and the following instruction in the WM_CREATE code
Invoke SelectObject, StaticHDC, Pen1
is located  before you even get a StaticHDC handle.


When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

Bieb

Okay, I forgot to mention, Easy Code automatically generates all the control and window creation code.  Window1Proc does receive messages for the window, and Window1StaticProc does receive messages for the static control.  I tried adding code to BitBlt the buffer in the WM_PAINT code of the main window and in the DrawLoop, but that caused a problem with Windows, where parts of the screen would go black until refreshed.  I know that the buffer is being copied on to the static control, because if  adjust the destination coordinates of BitBlt, it causes it to only draw on part of the static control, and part remains transparent.  So, the buffer must not be being drawn on.

I've attached a newer version with the code that caused the glitch commented out.

[attachment deleted by admin]