Thread: Strange memset question

  1. #1
    Registered User
    Join Date
    Mar 2004
    Posts
    180

    Strange memset question

    I have this set of code
    Code:
    LPDIRECTDRAW7 lpdd;  //version 7.0 main
    DDSURFACEDESC2 ddsd; //primary surface descriptor
    LPDIRECTDRAWSURFACE7 DDprimary; //DX primary surface
    LPDIRECTDRAWSURFACE7 DDback; //DX BACK surface...
    
    ///init the the DD stuff here....
    ///
    ///
    ///
    
    
    
    //lock the surface
    DDback->Lock(NULL,&ddsd,DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
    
    //valuse are valid now
    int mempitch        = (int)(ddsd.lPitch >> 2);
    UINT *video_buffer = (UINT *)ddsd.lpSurface;
    
    if(ddsd.lPitch == 800) //linear memory
    {
            memset(video_buffer,0,800*600);
    }
    else //BOOO! non-linear memory
    {
            UINT *dest_ptr = video_buffer;
    
            for(int i=0;i<600;i++)
            {
                    //clear nxt line
                    memset(dest_ptr,0,600);
    
                    //advance ptr
                    dest_ptr += (UNIT)mempitch;
            }
    }
    
    for(int i = 0; i<1000;++i)
    {
      x = rand()%800;
      y = rand()%600;
      red   = rand()%256;
      blue  = rand()%256;
      green = rand()%256;
      Plot_Pixel32(x,y,0,red,green,blue,video_buffer,mempitch);
    }
    
    //unlock the surface
    DDback->Unlock(NULL);
    
    //flip
    while(FAILED(DDprimary->Flip(NULL,DDFLIP_WAIT)));
    but it only clears out the very left most 1/5 of the screen, the rest is a flickery mess, and is never cleared.

    Its 32-bit, 800x600 mode

    Thank you very much

    DW

  2. #2
    Has a Masters in B.S.
    Join Date
    Aug 2001
    Posts
    2,263
    off-hand i see this:

    memset works with bytes.

    void * memset ( void * b, int c, size_t n)

    where
    b - is the memory buffer
    c - is the value to set the buffer to
    n - is the size in BYTES of the buffer to be set

    try this

    memset(video_buffer,0,800*600*sizeof(UINT));
    ADVISORY: This users posts are rated CP-MA, for Mature Audiences only.

  3. #3
    Registered User
    Join Date
    Mar 2004
    Posts
    180
    Seems to work now!!

    The extra *sizeof(UINT) makes it run slower, and every few frames a I get what looks like a flicker at the top of the screen which I can only image is the memset() clearing out the data.

    Also, every random amount of frames, I get my form(window) appearing for a second or two, then disapearing.

    This runs bloody slow though.... Like wow. Its drawing the pixels every..oh 1/2 second or so! Any way to speed that up?

    thanks for the help

    DW

  4. #4
    Registered User
    Join Date
    Apr 2002
    Posts
    1,571
    You're talking about locking a surface and manually plotting pixels. That is slow no matter how you slice it.
    "...the results are undefined, and we all know what "undefined" means: it means it works during development, it works during testing, and it blows up in your most important customers' faces." --Scott Meyers

  5. #5
    Registered User
    Join Date
    Mar 2004
    Posts
    180
    Quote Originally Posted by MrWizard
    You're talking about locking a surface and manually plotting pixels. That is slow no matter how you slice it.

    Aye, assumed this, but everytime I assume with programming, I tend to blow stuff up!

    Also, Is the memory clearing tecnique going to be very slow? Also, what can I do about that small flicker?

    Thanks

  6. #6
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Well there are some instances that require you to lock the surface. I'm not sure where or why you are saying this is slow. If you only do it once per frame then its not that bad.

    Here is why your code is so slow, or at least a couple of reasons I can see right off hand.

    //valuse are valid now

    Code:
    int mempitch = (int)(ddsd.lPitch >> 2);
    UINT *video_buffer = (UINT *)ddsd.lpSurface;
     
    if(ddsd.lPitch == 800) //linear memory
    {
    memset(video_buffer,0,800*600);
    }
    else //BOOO! non-linear memory
    {
    UINT *dest_ptr = video_buffer;
     
    for(int i=0;i<600;i++)
    {
    //clear nxt line
    memset(dest_ptr,0,600);
     
    //advance ptr
    dest_ptr += (UNIT)mempitch;
    }
    }
    Since BufferPitch according to the SDK is guaranteed to work in all cases then you need not check for linearity of the memory. This added if on every frame adds more cycles than you might think. Also you are clearing out one line at a time instead of the whole screen at once.

    There are several options here. In my experience I've found that nearly every buffer has a linear pitch to it. So basically this can be broken down into a simple bitblt. Also, memset only works with bytes at a time. So in 32-bit color it must use stosb 4 times as opposed to one stosd. A stosb writes the byte in AL to ES:EDI. A stosd writes the DWORD or 32-bit value in EAX to ES:EDI. A stosd is just about 4 times faster than using stosb.
    Code:
    UINT *dest_ptr = video_buffer;
     
    for(int i=0;i<600;i++)
    {
    //clear nxt line
    memset(dest_ptr,0,600);
     
    //advance ptr
    dest_ptr += (UNIT)mempitch;
    }
    }

    For linear memory:
    Code:
    UINT *dest_ptr = video_buffer;
    DWORD numDwords=width*height;
     
    	asm {
     
    	 mov	edi,[dest_ptr]
    	 mov	ecx,numDwords
    	 mov	eax,00000h
    	 rep	 stosd
    	}
    For non-linear:
    Code:
    UINT *dest_ptr=video_buffer;
    DWORD numDwords=width*height*bufferpitch;
    asm {
    mov edi,[dest_ptr]
    mov ecx,numDwords
    mov eax,00000h
     
    START: 
    	mov [es:edi],eax		 ;full selector might cause problems
    	add	edi,bufferpitch
    	dec	ecx
    LOOP START

    In pure C and linear memory:
    Code:
    for (DWORD offset=0;offset<numDwords;offset++) 
    {
    video_ptr[offset]=0x0000;
    }
    In pure C and non-linear memory:
    Code:
    for (DWORD offset=0;offset<numDwords;offset+=bufferpitch) 
    {
    video_ptr[offset]=0x0000;
    }

    You can actually get this to go very fast. My voxel engine uses this method and locks/unlocks the buffer once per frame. Unfortunately if you want to directly access the buffer there is no other way. But the performance hit is not that bad. If it is crawling it is your code or your algorithm. I can get scrolling and voxels to run very fast (if my voxel algo were just a bit faster) even whilst locking/unlocking the back buffer.

    I used assembly in this post because I'm afraid to say that when you need speed you need to use assembly. Argue with me all you want about compiler optimizations but it is all bunk. Assembly is faster hands down (if you code it right) and no amount of C code is going to beat finely crafted hand-tuned assembly code. Many many many game programmer positions still want you to know assembly - trust me they use it more than you think. It is extremely useful in Direct3D for texture to texture writes and other operations. Hardware is not always available (including vertex/pixel shaders) and so when it is not....your code should switch to an assembly function.

    Just no way around it in this post. For directly accessing the buffer you will need some assembly to get it fast enough. There also are ways to split the screen into sections and render each section inside the loop. Say for instance split the screen in half and instead of writing one pixel per frame you write 2. One to the left side and one to the right side. For clearing the screen this works very well. There are about a million ways to do this so I leave that as an exercise for you to try.


    Dump memset. It's not suited for this.

    This setup will fix the flicker.
    When you set your back buffer up there are two important things you must do. First you must use the D3DPRESENTFLAG_LOCKABLE_BACKBUFFER flag to be able to create a back buffer that can be locked.

    Second you must use D3DSWAPEFFECT_COPY as the SwapEffect flag to stop the flickering.

    Also you CANNOT render to the primary buffer w/o causing flickering. You should always render to the BACK BUFFER and then when D3D flips it to the front it will not flicker. At no time will your image cease to exist and therefore no flicker. This is the same exact process used in the old DOS days when you rendered to a buffer and then either bitblt'ed it to the screen or page flipped it. Do not write to the primary buffer.

    Code:
    ...
    ...
    _sPresentParams.BackBufferWidth			= width;
    _sPresentParams.BackBufferHeight		 = height;
    _sPresentParams.BackBufferFormat		 = D3DFMT_A8R8G8B8;
    _sPresentParams.BackBufferCount			= 1;
    _sPresentParams.MultiSampleType			= D3DMULTISAMPLE_NONE;
    _sPresentParams.MultiSampleQuality		 = 0;
    _sPresentParams.SwapEffect				 = D3DSWAPEFFECT_COPY; 
    _sPresentParams.hDeviceWindow			 = hwnd;
    _sPresentParams.Windowed				 = windowed;
    _sPresentParams.EnableAutoDepthStencil	 = true; 
    _sPresentParams.AutoDepthStencilFormat	 = D3DFMT_D24S8;
    _sPresentParams.Flags					 = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
    _sPresentParams.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
    _sPresentParams.PresentationInterval	 = D3DPRESENT_INTERVAL_IMMEDIATE;
     
    hr = D3D9->CreateDevice(D3DADAPTER_DEFAULT, 
    						DeviceType,		 
    						hwnd,			 
    						vp,				 
    						 &_sPresentParams,		 
    						 &Device);
    Getting to the back buffer in Direct3D is a bit challenging and you have to get a hold of the swap chain to do it. Here is a class called CBackBuffer that I created that works fine.

    CBackBuffer.h
    Code:
    #ifndef BACKBUFFER
    #define BACKBUFFER
    #include <d3dx9.h>
    #include "D3DError.h"
     
    class CBackBuffer
    {
    protected:
    	IDirect3DDevice9		*Device;
    	IDirect3DSwapChain9	 *SwapChain;
    	IDirect3DSurface9	 *Surface;
    	D3DSURFACE_DESC		 BufferDesc;
    	D3DLOCKED_RECT		 BLRect;
    	void				 *Buffer;
    	int					 MemPitch;
     
    public:
    	CBackBuffer(IDirect3DDevice9 *device);
     
    	int Pitch(void) {return MemPitch;};
    	int Width(void) {return BufferDesc.Width;};
    	int Height(void) {return BufferDesc.Height;};
    	void *Lock(void);
    	HRESULT Unlock();
     
    };
    #endif

    CBackBuffer.cpp
    Code:
    #include "CBackBuffer.h"
    #include <stdio.h>
     
    CBackBuffer::CBackBuffer(IDirect3DDevice9 *device)
    {
    Device=device;
     
    Device->GetSwapChain(0,&SwapChain);
    SwapChain->GetBackBuffer(D3DADAPTER_DEFAULT,D3DBACKBUFFER_TYPE_MONO,&Surface);
    if (!Surface) ::MessageBox(0,"GetBackBuffer Failed",0,0);
     
    if FAILED(Surface->GetDesc(&BufferDesc)) ::MessageBox(0,"GetDesc Failed",0,0);
     
     
     
    }
    void *CBackBuffer::Lock(void)
    {
    D3DLOCKED_RECT lrect;
    HRESULT hr;
    hr=Surface->LockRect(&lrect,0,0);
    if FAILED(hr)
    { 
     
    	char *text=D3DError::GetTextFromD3DError(hr);
    	::MessageBox(0,text,0,0);
    	return NULL;
    }
     
     
    MemPitch=lrect.Pitch>>2;
     
    return lrect.pBits;
    }
    HRESULT CBackBuffer::Unlock(void)
    {
    HRESULT hr=Surface->UnlockRect();
    return hr;
    }

    Don't worry about D3DError.h It was something I wrote to turn D3D error codes into textual error messages.....until wonderful Mr. Wizard pointed out to me that D3D already has this functionality. So I wrote all the code for nothing....but hey it was fun.


    If you have any questions or this does not work I will give you my full Direct3D framework class so that you can at least get an environment up and running that takes care of all the nitty gritty stuff for you.

    Also dump the DirectX 7 interfaces. It is quite possible that your video driver will fail on some of the calls. Yes they are still valid since MS DX is backwards compatible...but the driver may not utilize them in the best way anymore since they are deprecated calls. This code should bump you up to DirectX 9.0. All of the code is my own and it comes from wanting to do exactly what you are talking about - direct video access in DirectX 9.0(a,b and c) just like in the old DOS days.
    Last edited by VirtualAce; 09-21-2004 at 09:16 PM.

  7. #7
    Registered User
    Join Date
    Mar 2004
    Posts
    180
    Wow! Thanks a million! Atm, I'm building up on 2D because thats what the first of LaMoth's books is..but once I hit D3D(2nd book) , definatly gonna use that code, so thanks again! I was going to use Dx 9.0(a/b/c) but since Lamoth's book is DirectX, I figured for my first atempt at DirectX, I might as well keep it the same.

    Is there a way to do what your saying, but in 2D w/DirectX 7?

    Also, am I writing to the front buffer? or the backbuffer? I mean to write to the back... Since I'm new to this, I keep confusing all the DX variables that I have. Should I be assigning video_buffer = DDback.lsurface or ddsd.lsurface?

    Also, tryid out that asm code, and it definatly doesn't work. Just hardlocks the comp until, after madly bash ctrl+alt+del, I can kill it.

    bufferpitch would equal my mempitch variable right?

    Code:
    asm {
    mov edi,[dest_ptr]
    mov ecx,numDwords
    mov eax,00000h
     
    START: 
    	mov [es:edi],eax		//full selector might cause problems
    	add	edi,bufferpitch
    	dec	ecx
    LOOP START
    in this line, is there a '}' after LOOP START ?

    what do you mean by...?

    //full selector might cause problems

    Again thank you so much for helping me out,

    regards

    DW

  8. #8
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    If you load the complete selector under XP or any 32-bit OS it might crash. This is because the operating system gives you memory addresses that are offsets into your current selector - not entirely new selectors. So if XP allocates one selector for your program, code, data, stack, etc. and then you attempt to load in a new selector then you will be accessing the wrong thing.

    It actually gets into fairly advanced assembly and so I defer you to the Intel technical reference manuals for the IA-32 architecture for a complete discussion of it.

  9. #9
    Registered User
    Join Date
    Mar 2004
    Posts
    180
    Oie!

    Well, I tried the plain C code, and that too locks up...any ideas on that? Is that the same issue?

    DW

  10. #10
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Make sure you lock the buffer, write to it, then unlock it. Failure to lock/unlock will cause problems.

  11. #11
    Registered User
    Join Date
    Mar 2004
    Posts
    180
    Ya I've had that happen before! No i'm most definatly locking the buffer, but I'm locking the *back* buffer but video_buffer is assigned to ddsd.lsurface not DDbackbuffer.lsurface....is this bad?

    CHeers

    DW

  12. #12
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Well if you are writing to the back buffer you lock it. If you are locking the front buffer and attempting to write to the back buffer then essentially you are not locking the buffer because you are locking the incorrect one.

    This will cause major issues. You must lock the buffer surface that you wish to write to. Anything else is incorrect.

  13. #13
    Registered User
    Join Date
    Mar 2004
    Posts
    180
    OK.....now this is gonna sound like a stupid qustion, and it is really...but what does my ddsd do? Because thats the only variable I dont really understands it's use for.

    Thanks for the help

    regards

    DW

  14. #14
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    ddsd is a pointer to a surface structure. The structure holds information about the surface and when locked one of the members becomes a valid pointer to the actual memory location of the surface allowing you to directly access it.

    There are definitions of the surface structure all over the DirectX SDK's. I do not use surface desc's that much in Direct3D but I do use them in 2D with Direct3D. Look it up in the SDK.

  15. #15
    Registered User
    Join Date
    Mar 2004
    Posts
    180
    So I should be locking it not DDback?

    I have andré lamoth's book so I'll refresh my mem there....

    DW

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Question about memset
    By Mortissus in forum C Programming
    Replies: 3
    Last Post: 03-24-2009, 08:16 AM
  2. another do while question
    By kbpsu in forum C++ Programming
    Replies: 3
    Last Post: 03-23-2009, 12:14 PM
  3. Newbie Question - fflush(stdin) & fpurge(stdin) on Mac and PC
    By tvsinesperanto in forum C Programming
    Replies: 34
    Last Post: 03-11-2006, 12:13 PM
  4. Strange results using dnsapi and windns
    By Niara in forum Networking/Device Communication
    Replies: 3
    Last Post: 08-13-2005, 10:21 AM
  5. Question type program for beginners
    By Kirdra in forum C++ Programming
    Replies: 7
    Last Post: 09-15-2002, 05:10 AM