News:

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

Graphics again.

Started by Jimg, February 26, 2009, 11:06:12 PM

Previous topic - Next topic

Jimg

I'm working on a blink comparator, it switches between two images at a set interval for comparison.
The problem is - I'm not able blit the full image fast enough.
The attached program doesn't use actual images, it just blinks between two colors by bliting from one memory dc or the other, one gray, one orange.
    .elseif eax==WM_PAINT

        inv BeginPaint,hWnd,ADDR ps
        mov hdc,eax
        neg toggle
        .if sign?
            inv BitBlt,hdc,0,0,mwidth,mheight,mdc,0,0,SRCCOPY
        .else
            inv BitBlt,hdc,0,0,mwidth,mheight,mdc2,0,0,SRCCOPY
        .endif
        inv EndPaint,hWnd,ADDR ps

I've tried all the limited number of things I can find to do with no success.
If you run the program, you will probably be able to see where the vertical retrace occurs.  I added Michaels vertical blanking routine.  To active it, press a key, to deactivate, press a key again.  Activating the retrace code makes the problem stand still on the screen, but it is still there.
What is the proper way to avoid this problem?
.nolist
include \masm32\include\masm32rt.inc
.list
.data?
hInst dd ?
.code
.data
ClassName       DB "StdWindow",0
szAppName       DB "test",0
align 4
wc WNDCLASS <CS_HREDRAW or CS_VREDRAW,WndProc,0,0,0,0,0,0,0,ClassName>

.data?
message MSG <>
hwindow dd ?
hMenu   dd ?
SWidth  dd ?    ; screen width
SHeight dd ?    ; screen height
.code
inv equ invoke
Program:
    inv InitCommonControls
    inv GetModuleHandle,0
    mov hInst,eax
    mov wc.hInstance,eax   ; save for later
    inv LoadIcon, 0, IDI_APPLICATION
    mov wc.hIcon,eax
    inv LoadCursor, 0, IDC_ARROW
    mov wc.hCursor,eax
    inv RegisterClass, addr wc
    ;inv LoadMenu, hInst, MNU
    mov hMenu,0;eax
    call GetScreenSetup   
    inv CreateWindowEx,WS_EX_CLIENTEDGE,addr ClassName,addr szAppName,WS_OVERLAPPEDWINDOW,
           0,0,SWidth,SHeight,0,hMenu,hInst,0
    mov hwindow,eax
    inv ShowWindow, hwindow, SW_SHOWNORMAL
    inv UpdateWindow, hwindow
    .repeat
        inv GetMessage,addr message,0,0,0
        .break .if !eax
        inv TranslateMessage,addr message
        inv DispatchMessage,addr message
    .until 0
    inv ExitProcess,message.wParam

GetScreenSetup proc
    inv GetSystemMetrics,SM_CXSCREEN  ; screen width
    mov SWidth,eax
    inv GetSystemMetrics,SM_CYSCREEN
    mov SHeight,eax
ret
GetScreenSetup endp

.data?
hWin dd ?   ; dialog handle
hdc  dd ?   ; temp dc of dialog
mdc  dd ?   ; memory dc of hidden buffer
mdc2 dd ?   ; scratch for loading images

hbm1 dd ?   ; bitmap for hidden buffer mdc
hbm2 dd ?   ; mdc2

crect RECT <>
mwidth  equ crect.right  ; total width of client rectangle in pixels
mheight equ crect.bottom ; total height in pixels

hbrush1 dd ?
hbrush2 dd ?
usevert dd ?
.data
toggle dd 1
.code
TimerProc proc hwnd,imsg,tid,ctime  ; timer callback routine.
    inv InvalidateRect,hWin,0,0
ret
TimerProc endp

WndProc Proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    local ps:PAINTSTRUCT

    mov eax,uMsg
    .if eax==WM_COMMAND
    .elseif eax==WM_ERASEBKGND
        mov eax,1
        jmp WndRet
    .elseif eax==WM_LBUTTONDOWN
        inv InvalidateRect,hWin,0,0
    .elseif eax==WM_PAINT

        inv BeginPaint,hWnd,ADDR ps
        mov hdc,eax
        .if usevert
        .if VblankOk
                call WaitForVerticalBlank
        .endif
        .endif
        neg toggle
        .if sign?
            inv BitBlt,hdc,0,0,mwidth,mheight,mdc,0,0,SRCCOPY
        .else
            inv BitBlt,hdc,0,0,mwidth,mheight,mdc2,0,0,SRCCOPY
        .endif
        inv EndPaint,hWnd,ADDR ps
       
    .elseif eax==WM_SIZE
        .if mdc==0
            inv GetClientRect,hWin,addr crect    ; get the size needed for the bitmap
            inv GetDC,hWin  ; get dc's
            mov hdc,eax
       
            inv CreateCompatibleDC,hdc      ; create dc for hidden buffer
            mov mdc,eax
            inv SetBkMode,mdc,TRANSPARENT
            inv CreateCompatibleBitmap,hdc, mwidth, mheight ; make a hidden buffer of the appropriate size
            mov hbm1,eax
            inv SelectObject, mdc,hbm1
            inv DeleteObject,eax        ; delete old bitmap
            inv CreateSolidBrush,002492ffh
            mov hbrush1,eax
            inv SelectObject,mdc,eax
       
            inv CreateCompatibleDC,hdc      ; create dc for hidden buffer
            mov mdc2,eax
            inv SetBkMode,mdc2,TRANSPARENT
            inv CreateCompatibleBitmap,hdc, mwidth, mheight ; make a hidden buffer of the appropriate size
            mov hbm2,eax
            inv SelectObject, mdc2, hbm2
            inv DeleteObject,eax        ; delete old bitmap
            inv CreateSolidBrush,00818181h
            mov hbrush2,eax
            inv SelectObject,mdc2,eax
       
            inv FillRect,mdc,addr crect,hbrush1
            inv FillRect,mdc2,addr crect,hbrush2
       
            inv ReleaseDC,hWin,hdc
            inv SetTimer,hWin,999,150,addr TimerProc
        .endif

    .elseif eax==WM_KEYDOWN
        not usevert
    .elseif eax==WM_CREATE

        m2m hWin,hWnd
        call StartDD    ; set up for vertical blanking
       
    .elseif eax==WM_DESTROY

        inv PostQuitMessage,0
        mov eax,wParam
        jmp WndRet

    .endif
LpExit:
    inv DefWindowProc, hWnd, uMsg, wParam, lParam
WndRet:
ret
WndProc endp

;-----code to get vertical blanking to prevent flicker from MichaelW------------
uselib ddraw ; to find vertical blanking to prevent flicker

DDO_GetScanLine EQU 64
DDERR_VERTICALBLANKINPROGRESS EQU ((1 shl 31)or(876h shl 16)or(537))

.data
lpdd_1   dd 0
scanLine dd 0
VblankOk dd 0 ; flag that it is ok to wait for vertical blanking
.code

StartDD proc
    invoke DirectDrawCreate,0,ADDR lpdd_1,0 ; Create a default DirectDraw object.
    or eax,eax
    jnz DDNoGood
    call DDGetScanLine ; Make sure DDGetScanLine doesn't have any obvious problems.
    or eax,eax
    jnz DDNoGood
mov VblankOk,1 ; ok to use
DDNoGood:   
ret
StartDD EndP

DDGetScanLine proc
    push offset scanLine
    push lpdd_1
    mov  eax, lpdd_1
    mov  eax, [eax]
    call DWORD PTR[eax+DDO_GetScanLine]
    ret
DDGetScanLine endp

WaitForVerticalBlank proc
    .repeat
        call DDGetScanLine
    .until eax != DDERR_VERTICALBLANKINPROGRESS
    .repeat
        call DDGetScanLine
    .until eax == DDERR_VERTICALBLANKINPROGRESS   
    ret
WaitForVerticalBlank endp

end Program

[attachment deleted by admin]

Jimg

Usually, after I post something like this, a dozen different things occur to me to try.  This time, the only thing I can think of is to do true video double buffering like the old days.  Perhaps junk gdi entirely and do it in DirectX.  Unfortunately, I haven't found any directx double buffering code I could make enough sense of to convert it to masm.  How do I set up two actual video buffers and then tell the hardware to just switch, when I want, from one to the other rather then copying all those bits each time?

If I stick with GDI, is there some way to tell a window to use this DC or that DC without blitting?

Perhaps I could make two full windows, and make one or the other visible or not?

Any other ideas?

NightWare

old ways, are generally better  :wink

http://www.masm32.com/board/index.php?topic=10624.0, it's easy to understand (maybe you should transform the calls to standard, for an easier understanding). use SetDIBitsToDevice instead of BitBlt, plus here i use double buffer only for the case where you read data of the current screen (fliker effect here), otherwise with SetDIBitsToDevice there is no flicker effect, so no need of double buffer... AND IT'S FAST !

Jimg

#3
It's beyond me how SetDIBitsToDevice could possibly be faster than a simple bitblt, but I'll try it.  I have to say I'm a little overwhelmed by your code however.  Thanks.

gwapo

Hi Jim,

I've made a simple gradient example before:
http://www.masm32.com/board/index.php?topic=5944.0

I think it has a simple double buffering in there. Hopefully, it may help you.

Cheers,

-chris

Jimg

Thanks gwapo, I'll give it a look.

Jimg

Okay, I've look the two over.

I don't see how Chris's program helps at all.  Now if you could switch between the two screens without tearing, that would help.

NightWare's program is just too much for my old brain to follow at the moment.

I'm going to try SetDIBits from scratch, but if anyone else has suggestions, I'd appreciate it.

Jimg

According to Microsoft http://msdn.microsoft.com/en-us/library/dd183562(VS.85).aspx,
QuoteTo repeatedly redraw a bitmap in a window, however, the application should use the BitBlt function. For example, a multimedia application that combines animated graphics with sound would benefit from calling the BitBlt function because it executes faster than SetDIBitsToDevice.

jj2007

Quote from: Jimg on February 27, 2009, 03:52:04 PM
According to Microsoft http://msdn.microsoft.com/en-us/library/dd183562(VS.85).aspx,
QuoteTo repeatedly redraw a bitmap in a window, however, the application should use the BitBlt function. For example, a multimedia application that combines animated graphics with sound would benefit from calling the BitBlt function because it executes faster than SetDIBitsToDevice.

The accent is on repeatedly - for drawing the same unchanged bitmap, BitBlt is probably faster. But if I remember well, you wanted to change the bits, right?

Jimg

Well, yes, I want to change All the bits repeatedly rather than a small band or rectangle.

I found a post http://www.asmcommunity.net/board/index.php?topic=28403.0
I made a little comparing tool which compares some blit operations, results are:

BitBlt            900
SetDIBitsToDevice 6000
StretchBlt        9000
DrawDIB           50000

Jimg

#10
Does someone have a SIMPLE example of showing a graphic image in a standard (resizable, movable, titlebar,etc.) window using DirectDraw?
By Simple I mean something less than say 2000 lines of code counting all non-masm32 include files?
If this isn't going to work with standard GDI, I'd better get started learning DirectDraw, but initially, I'm overwhelmed.
I found something that looked promising on Japeth's site (http://www.japheth.de/Download/ShowBmp.zip), but was totally unable to get it to assemble (I don't seem to have dxguid.lib or be able to find it anywhere).

Jimg

It's been several days and probably 30 hours of searching the web, trying to assemble and run examples, and general frustration.  Just as there is something about C that just won't click in my brain, there's something that just won't click about DirectDraw either.

I looked at OpenGL which makes a small bit more sense to me, but I don't see how to flip back and forth between two buffers. 

Both are just way, way too much code to accomplish anything in my opinion, and they are just rubbing me the wrong way to the point of needing to scream and pull out my hair.

So I need to decide which one can do what I want and just learn one.

Rather than blitting the whole image each time which doesn't seem to be able to happen before the vertical retrace puts an obvious tear in the picture, at least with GDI, which one is better for creating two images, just flipping back and forth between the two by telling the system just display this one or that one?  I wish I could have tested both to see, but I can't get either to work so far.

Just so you know, I'm not trying to hide anything.  I agreed to do this simple app for a set amount of money.  I was totally surprised that GDI couldn't do something so simple.  So I'm going to end up making about $2.00/hour before this is done, and any code or examples or suggestions have to be unencumbered by anti-commercial licensing agreements, a problem with virtually all example OpenGL or Directdraw code I've found.

After I finish this, I'm going to be doing some plotting.  Which one is better suited for this, or should I just drop back to GDI?

I really need to pick one.  Any suggestions will be greatly appreciated.

NightWare

 :bg last attempt, jimg, severals things...

1. for SetDIBitsToDevice you send a memory area (pixels) + infos (not a DC) to the display (a DC), so you can use your own functions.
for BitBlt you copy a src DC to another DC ! but if you have a DC as src, you MUST use microsoft's APIs (and gdi is well known to be very fast  ::) ...) to alterate the content. it's a question a choice.

2. i've no idea of how you DO your speed tests, but here the algos DON'T do the same thing, i even suspect that BitBlt simply swap pointers here, after some test... (so yes it can be fast here, except that you have to use gdi's functions to set/alterate the content, before  :lol), plus without infos on the alignment your results are inconsistant.
and i don't speak of the branch/variables that could affect the results...

3. if you want to test speed, mesure the result in FPS (the reality, and for the same visual result, not a black/background screen). note : the fps is NOT the number of screen seen (your gfx board simply can't), but the number of screen you have calculated/made and sent to the display.

4. direct draw also use DC (originally based on gdi), but it's more a rectangular face texturing than a screen draw... and here again, you will be the slave of the dedicated functions.

Quote from: Jimg on February 27, 2009, 07:35:11 PM
By Simple I mean something less than say 2000 lines of code counting all non-masm32 include files
it's the problem ? then why don't you see that as API (temporary) ? when you use gdi functions do you look/examine the algos used ?

jj2007

Quote from: Jimg on February 26, 2009, 11:06:12 PM
I'm working on a blink comparator
Let me try to understand: A blink comparator is used in astrology for finding moving stars, right? If I remember well, it takes two bitmaps, and they must be drawn on the DC in a given interval. That sounds like a straightforward case for BitBlt... either with two source DCs, or one source DC with two bitmaps selected in alternatively.
So what does make your code so slow? A huge screen resolution?

MichaelW

Regarding the vertical retrace putting a tear in the picture, if you are updating the screen from top to bottom, by starting the update at the correct time, you can have almost two vertical frames to complete the update. As far as I know, that was the purpose of providing GetScanLine. I have never worked this out completely, but I think that by comparing the vertical refresh rate to the update rate you can determine which scan line to start on. If the update moves slower than the vertical scan, then you start the update when the scan reaches the second line from the top. If the update moves faster than the vertical scan, then you start the update somewhere close to the end of vertical blank.
eschew obfuscation