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

japheth

Quote from: Jimg on February 27, 2009, 07:35:11 PM
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).

dxguid.lib is part of the platform SDK, but you'll find in in Open Watcom as well. However, what's used from this lib is just the DirectDraw2 IID - you can define it on your own in the source and so skip any need for dxguid.lib:

IID_IDirectDraw2 GUID <0B3A6F3E0h,2B43h,11CFh,<0A2h,0DEh,00h,0AAh,00h,0B9h,33h,56h>>

Quote
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.

Don't worry, this DD sample is Public Domain  :toothy

Jimg

Michael-
QuoteRegarding 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.
I am using a 1680 x 1050 screen for my test.  With the blit synced to scanline 750, I just start to see a second break at the top of the picture.  These two move down the screen as I increase the scanline I sync on, until the bottom one rolls off the bottom (line 1050) at which point there is only one break.  Of course, I can't determine the appropriate number in the program since every screen/video card is different.
Ok, re-reading, I see what you are saying about starting from the bottom, but I don't immediately know how to do that with bitblt, and my brain tells me that would cut the available time down to 1/2 frame as me drawing from the bottom collided with the screen refreshing from the top and we met in the middle.  I'll have to think a little more about it.

JJ-
QuoteLet 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?
You are correct. In the initial case, it will be two 1024 x 1024 technical bitmaps, not of stars however, but will be used for many other things as well.  Did you try the code I posted above?  Do you not get the tearing effect?  Is it only my computer that has this problem?  How do you load two bitmaps into one DC and switch between them?  I missed that API somehow.

Japeth-
Quotedxguid.lib is part of the platform SDK, but you'll find in in Open Watcom as well. However, what's used from this lib is just the DirectDraw2 IID - you can define it on your own in the source and so skip any need for dxguid.lib:

IID_IDirectDraw2 GUID <0B3A6F3E0h,2B43h,11CFh,<0A2h,0DEh,00h,0AAh,00h,0B9h,33h,56h>>
Thank you.  I feel much better having that rather than an unknown library from an unknown source.  And it actually works!  I'll look at it some more when I have more time.  Vielen Dank.

NightWare-
Quotewhen you use gdi functions do you look/examine the algos used ?
Actally I did try to trace them.  But Microsoft is the only one I have to accept.  Everything else I scrutinize mercilessly until I understand it and know exactly what is happening.  Which is probably why it takes me so long to do anything.
I'm going to try again in a full implementation and let you know the results.  I certainly hope you are right.  Basically I only care that it is not obvious visually.  And Thank You also for your undeserved patience.


jj2007

Quote from: Jimg on March 03, 2009, 03:00:17 PM
JJ-
...
Do you not get the tearing effect?  Is it only my computer that has this problem?

I see an orange and a grey screen alternating about 4 times a second. Not sure what you mean with "tearing" - the screens look ok, there is a scan line that may be visible for some milliseconds at about 2/3 of the screen, but it might as well be my eyes that produce this effect.

I am a bit surprised why this is so slow: 4 times per second is very slow compared to what you browser has to perform when updating a page.

Quote
  How do you load two bitmaps into one DC and switch between them?  I missed that API somehow.

Not simultaneously, but you switch your selection: inv SelectObject, mdc2, hbm2
I doubt, though, that it's any faster than using two separate DCs.

Jimg

That's about right, it's set in the timer-
inv SetTimer,hWin,999,150,addr TimerProc
to 150 ms.  Just for this test, it's adjustable in the real program.   Set it to 15 ms and the problem should be more visible when not synced to the retrace.
To me, it's really obvious that I'm seeing the top 1/4 in orange and the bottom 3/4's in grey, and on the next paint, it's the top 1/4 in gray and bottom 3/4's in orange.
Windows is doing the screen refresh when the blit has transfered only part of the pixels.  By selecting the scan line to sync on, I can get about the top 1/8th in orange, the middle 6/8's in gray and the bottom 1/8th in orange, and just the opposite on the next paint.  Very disturbing when your'e trying to detect or compare fine details.
I'll put in the time if necessary to learn directdraw or opengl if I can get assurances one of them is necessary and sufficient.  But I'd much prefer to use gdi if I can.  I'm going to try NightWare's method next and hope for the best.

jj2007

OK, I got it. On my screen, the tear is at about 8% from the top, and pretty constant.

Here is a post saying Tearing-free drawing with GDI can be done by exact timing; on the other hand, MSDN says

QuoteIn a preemptive multithreaded environment, it is unlikely that the IDirectDraw::WaitForVerticalBlank method can synchronize with the vertical-blank interval. Instead, use appropriate wait flags to time blits and flips

GetVerticalBlankStatus is sometimes mentioned, too. The general idea seems to do the blitting some microseconds before the vertical blank occurs, i.e. to use QPC etc to finetune the start of the BitBlt...

Good luck :thumbu

Jimg

Thanks.  Like I mentioned above, I've tried syncing everywhere throughout the frame, and some places I see two breaks, so there doesn't exist a place where the whole frame can be blitted and displayed using what I doing now.

MichaelW

If the update takes more than two vertical frames, I can't see any way to eliminate the tearing.
eschew obfuscation

jj2007

Jim, good news: I have tested it on my other puter now, and there is no tear at all. Tell your clients they need a recent model :bg

NightWare

15 ms it's too low for me (and my puter is only 1 year old...), with 17 or + it's ok...

1000/17=58,82 fps... not enough for your need ?

Jimg

Mine can't handle it at any speed, it always tears.

Okay, I created a couple of  dibsections and drew on them, in this case, I just filled them with color ---

.data?
hWin dd ?   ; window handle

hdc    dd ? ; temp dc of dialog
dib1   dd ? ; dibs
dib2   dd ?
mbits1 dd 0 ; address of bits
mbits2 dd 0 

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

.data

bih BITMAPINFOHEADER <sizeof BITMAPINFOHEADER, \
  0,        \;biWidth
  0,        \;biHeight
  1,        \;biPlanes WORD
  32,       \;biBitCount WORD
  BI_RGB,   \;biCompression
  0,        \;biSizeImage
  0,        \;biXPelsPerMeter
  0,        \;biYPelsPerMeter
  0,        \;biClrUsed
  0 >       ;biClrImportant

.code
SetupDibs proc
    pusha

    inv GetClientRect,hWin,addr crect    ; get the size needed for the bitmap
    inv GetDC,hWin  ; get dc's
    mov hdc,eax
    m2m bih.biWidth,mwidth  ; set width and height in bitmapinfoheader
    mov eax,mheight
    neg eax
    mov bih.biHeight,eax
    inv CreateDIBSection,hdc,addr bih,DIB_RGB_COLORS,addr mbits1,0,0
    mov dib1,eax
    inv CreateDIBSection,hdc,addr bih,DIB_RGB_COLORS,addr mbits2,0,0
    mov dib2,eax
    inv ReleaseDC,hWin,hdc

    mov eax,mwidth
    imul eax,mheight    ; get total number of bits

    push eax            ; save
    mov ecx,eax
    mov edi,mbits1
    mov eax,002492ffh   ; fill with color
    rep stosd

    pop ecx             ; get back total number of bits
    mov edi,mbits2
    mov eax,00818181h   ; fill with color
    rep stosd

    inv GdiFlush
sdret:
    popa       
ret
SetupDibs endp


Now to do the SetDIBitsToDevice, I need a BitmapInfo structure.
            inv SetDIBitsToDevice,dib1,0,0,mwidth,mheight,0,0,0,mheight,
                    mbits2,addr bi ,DIB_RGB_COLORS


I can't find how to create a BitmapInfo structure from the DibSection I already have, or alternately, tell SetDIBitsToDevice to just use the section and header.
I'm really struggling over this array of RGBQUAD structure that the system should already know about.   What's the best way to get over this next step?

NightWare

the rgbquad structures are not always exist... it depends if the bitmap use a palette of colors or directly use the colors as pixels.

if you have loaded a bmp file, in BITMAPINFOHEADER.biSize you have SIZEOF BITMAPCOREHEADER if the bmp use a BITMAPINFOHEADER structure, otherwise it use a BITMAPCOREHEADER structure. you just need to get width and height and put them in your BITMAPINFOHEADER to create the appropriate dib.

once you have the width/heidht, you allocate the corresponding mem area (aligned 16, if possible). here you have your pixels area.

once you've made all your manipulation, just send the area to the DC of the display SetDIBitsToDevice,HDC,...
(GetDC from the handler obtained by CreateWindowEx), and use your bih for the bitmapinfo structure, coz here you have defined 32 bits, there is no palette here, so the BITMAPINFO structure it's just a BITMAPINFOHEADER structure, no RGBQUADs

note : keep the HDC you have defined, you define it at the beginnig of the app, and delete it at the end, just before ExitProcess

note2 : why have you created dib sections ? just allocate the memory area for your 2 pixels area, all the infos needed after that are in bih.

note3: no need of GetClientRect, GdiFlush,etc ... coz SetDIBitsToDevice do the FINAL transformation to the DC of the display (including format conversion, if needed...) so you don't need ANY other gdi functions...

Jimg

#26
Excellent, thanks.

So I got it working, the tearing seems about the same or possibly slightly better, it's hard to tell.


After further testing, I'd say it's about twice as fast.  Maybe even fast enough, if I can just figure out which line to sync on for all cases programatically.

edit: removed test, better one below.


NightWare

haven't tested it yet (i need to make few change to make it work with masm32 v8), but you don't need vertical blanks

Jimg

I'm still getting significant tearing until I get up to about scan line 300, so I'll need to check the vertical blanking.  I have an idea for optimizing it, I'll have to check it out.

Jimg

Here's an update to the test.  It prints out in the title bar of the window, the starting scan line, the ending scanline, and the number of scan lines that it takes to do the SetDIBitsToDevice for each paint.
It increments the starting scan line by 5 for each paint so I can see where the vertical retrace shows up on the screen based on starting scan line.  On my computer, it takes about 550 scan lines to do the update, on a 1680 x 1050 screen.
I lowered the delay between paints to 50 ms. to make the progression happen faster.


NightWare-

You have may sincerest apologies for doubting you, and my thanks for your patience.  Every test I ran said SetDIBitsToDevice was slower except the one that actually mattered, how it worked on the screen.

[attachment deleted by admin]