Thread: OpenGL DC Buffer Renders slow

  1. #1
    Registered User
    Join Date
    Nov 2010
    Posts
    13

    Unhappy OpenGL DC Buffer Renders slow

    Hello, my name's Lane Shaw, and this is my first post on a forum. I'm 16 and I use the Bloodshed Dev-C++ compiler to build my programs. The problem I'm having is an app I made to create lightning with OpenGL by plotting a line and shifting the points on it to make it look jagged. I also implemented an image buffer so I can manually edit the image made by OpenGL. It worked, but the image is rendered rather slowly, and I don't kno if it's the timer in the rendering thread or opengl. Here's my code:

    Code:
    typedef HANDLE THREAD;
    typedef bool (*RENDERPROC)(void);
    typedef void (*INITSCENE)(void);
    typedef struct _rend_dat {
          HWND parHwnd;
          THREAD Thr;
          PIMGBUFFER Image;
          HDC MemDC;
          HGLRC MemRC;
          HANDLE Timer;
          INITSCENE Init;
          RENDERPROC Render;
          INITSCENE UnInit;
          UINT16 Width;
          UINT16 Height;
          UINT32 Flags;
          ULONG ID;
    } THR_REND_DATA, *PTHR_REND_DATA, *PPTHR_REND_DATA;
    #define WM_RENDERR  (WM_USER+1)
    #define WM_RENDDONE (WM_USER+2)
    #define WM_RENDVIEW (WM_USER+3)
    #define ERROR_NO_RENDER_FUNC  0x00
    #define ERROR_IMAGE_BUFFER    0x01
    #define ERROR_WINDOW_DC       0x02
    #define ERROR_MEMORY_DC       0x03
    #define ERROR_MEMDC_SETUP     0x04
    #define ERROR_GL_CONTEXT      0x05
    #define ERROR_GLCONTEXT_SETUP 0x06
    #define ERROR_TIMER 0x07
    #define FLAG_THREAD 0x01
    #define FLAG_RESIZE 0x02
    #define FLAG_RENDER 0x04
    #define FLAG_ERROR  0x08
    bool SetUpDC(HDC hDC){
          PIXELFORMATDESCRIPTOR pix;
          MClr(&pix,sizeof(pix));
          pix.nSize = sizeof(PIXELFORMATDESCRIPTOR);
          pix.nVersion = 1;
          pix.dwFlags = PFD_DRAW_TO_BITMAP|PFD_SUPPORT_OPENGL|PFD_SUPPORT_GDI;
          pix.iPixelType = PFD_TYPE_RGBA;
          pix.cColorBits = 32;
          pix.cDepthBits = 16;
          pix.iLayerType = PFD_MAIN_PLANE;
          int iFor = ChoosePixelFormat(hDC,&pix);
          if(!iFor) return false;
          DescribePixelFormat(hDC,iFor,sizeof(pix),&pix);
          return SetPixelFormat(hDC,iFor,&pix);
    }
    ULONG WINAPI RenderThr(PTHR_REND_DATA ThrData){
          if(!ThrData){
                ExitThread(ERROR_INVALID_PARAMETER);
                return 1;
          }
          if(!ThrData->parHwnd){
                ExitThread(ERROR_INVALID_PARAMETER);
                return 1;
          }
          if(!ThrData->Render){
                PostMessage(ThrData->parHwnd,WM_RENDERR,ERROR_NO_RENDER_FUNC,ERROR_INVALID_PARAMETER);
                ExitThread(ERROR_INVALID_PARAMETER);
                return 1;
          }
          ThrData->Image = NewImageBuffer(ThrData->Width,ThrData->Height);
          if(!ThrData->Image){
                PostMessage(ThrData->parHwnd,WM_RENDERR,ERROR_IMAGE_BUFFER,GetLastError());
                ExitThread(ERROR_SUCCESS);
                return 0;
          }
          register HDC hDC = GetDC(ThrData->parHwnd);
          if(!hDC){
                PostMessage(ThrData->parHwnd,WM_RENDERR,ERROR_WINDOW_DC,GetLastError());
                ExitThread(ERROR_SUCCESS);
                return 0;
          }                             
          ThrData->MemDC = CreateCompatibleDC(hDC);
          if(!ThrData->MemDC){
                ReleaseDC(ThrData->parHwnd,hDC);
                PostMessage(ThrData->parHwnd,WM_RENDERR,ERROR_MEMORY_DC,GetLastError());
                ExitThread(ERROR_SUCCESS);
                return 0;
          }
          ReleaseDC(ThrData->parHwnd,hDC);
          HBITMAP OldBit = SelectObject(ThrData->MemDC,ThrData->Image->Bitmap);
          if(!SetUpDC(ThrData->MemDC)){
                PostMessage(ThrData->parHwnd,WM_RENDERR,ERROR_MEMDC_SETUP,GetLastError());
                ExitThread(ERROR_SUCCESS);
                return 0;
          }
          ThrData->MemRC = wglCreateContext(ThrData->MemDC);
          if(!ThrData->MemRC){
                PostMessage(ThrData->parHwnd,WM_RENDERR,ERROR_GL_CONTEXT,GetLastError());
                ExitThread(ERROR_SUCCESS);
                return 0;
          }
          if(!wglMakeCurrent(ThrData->MemDC,ThrData->MemRC)){
                PostMessage(ThrData->parHwnd,WM_RENDERR,ERROR_GLCONTEXT_SETUP,GetLastError());
                ExitThread(ERROR_SUCCESS);
                return 0;
          }
          LARGE_INTEGER Due;
          MClr(&Due,sizeof(Due));
          Due.QuadPart = -166666;
          ThrData->Timer = CreateWaitableTimer(NULL,TRUE,NULL);
          if(!ThrData->Timer){
                PostMessage(ThrData->parHwnd,WM_RENDERR,ERROR_TIMER,GetLastError());
                ExitThread(ERROR_SUCCESS);
                return 0;
          }
          if(ThrData->Init!=NULL) ThrData->Init();
          while(ThrData->Flags & FLAG_THREAD){
                if(ThrData->Flags & FLAG_RESIZE){
                      SelectObject(ThrData->MemDC,OldBit);
                      ResizeImageBuffer(&ThrData->Image,ThrData->Width,ThrData->Height);
                      OldBit = SelectObject(ThrData->MemDC,ThrData->Image->Bitmap);
                      wglMakeCurrent(ThrData->MemDC,ThrData->MemRC);
                      glViewport(0,0,ThrData->Width,ThrData->Height);
                      glMatrixMode(GL_PROJECTION);
                      glLoadIdentity();
                      gluPerspective(60,(float)ThrData->Width/(float)ThrData->Height,0.1,100.0);
                      glTranslated(0.0,0.0,-2.75);
                      glMatrixMode(GL_MODELVIEW);
                      glLoadIdentity();
                      ThrData->Flags &= ~FLAG_RESIZE;
                }
                if(ThrData->Flags & FLAG_RENDER){
                      SetWaitableTimer(ThrData->Timer,&Due,0,NULL,NULL,0);
                      bool update = ThrData->Render();
                      glFlush();
                      if(update) PostMessage(ThrData->parHwnd,WM_RENDVIEW,0,0);
                      WaitForSingleObject(ThrData->Timer,INFINITE);
                }else{
                      SetWaitableTimer(ThrData->Timer,&Due,0,NULL,NULL,0);
                      WaitForSingleObject(ThrData->Timer,INFINITE);
                }
          }
          if(ThrData->UnInit!=NULL) ThrData->UnInit();
          CloseHandle(ThrData->Timer);
          ThrData->Timer = NULL;
          wglMakeCurrent(NULL,NULL);
          wglDeleteContext(ThrData->MemRC);
          ThrData->MemRC = NULL;
          DeleteDC(ThrData->MemDC);
          ThrData->MemDC = NULL;
          DeleteImageBuffer(&ThrData->Image);
          PostMessage(ThrData->parHwnd,WM_RENDDONE,0,0);
          ExitThread(ERROR_SUCCESS);
          return 0;
    }
    bool CreateRendThread(PTHR_REND_DATA Data){
          if(!Data){
                SetLastError(ERROR_INVALID_PARAMETER);
                return false;
          }
          Data->Thr = CreateThread(NULL,2048,(LPTHREAD_START_ROUTINE)RenderThr,(PVOID)Data,0,&Data->ID);
          if(!Data->Thr) return false;
          return true;
    }
    
    LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
    UCHAR8 err[512];
    UINT64 GetScreenRes(){
          register UINT64 Res = 0;
          Res = GetSystemMetrics(SM_CXSCREEN);
          Res <<= 32;
          Res |= GetSystemMetrics(SM_CYSCREEN);
          return Res;
    }
    #define HIDWORD(Q) ((Q & 0xFFFFFFFF00000000uLL)>>32)
    #define LODWORD(Q)  (Q & 0xFFFFFFFF)   
    void RenderLightning(UINT8 Seg,double zapFact,double x1,double y1,double z1,double x2,double y2,double z2){
          double subX = (x2-x1)/(double)Seg,
                 subY = (y2-y1)/(double)Seg,
                 subZ = (z2-z1)/(double)Seg,
                 x,y,z,xVar,yVar,zVar,xPrev,yPrev,zPrev;
          UINT8 i;
          srand(time(0));
          xPrev = x1;
          yPrev = y1;
          zPrev = z1;
          for(i=1;i<Seg;i++){
                xVar = (double)rand()/((double)RAND_MAX+1)/(double)Seg*zapFact;
                yVar = (double)rand()/((double)RAND_MAX+1)/(double)Seg*zapFact;
                zVar = (double)rand()/((double)RAND_MAX+1)/(double)Seg*zapFact;
                if(rand()%2) xVar = -xVar;
                if(rand()%2) yVar = -yVar;
                if(rand()%2) zVar = -zVar;
                x = x1+(double)i*subX+xVar;
                y = y1+(double)i*subY+yVar;
                z = z1+(double)i*subZ+zVar;
                glBegin(GL_LINES);
                glVertex3d(xPrev,yPrev,zPrev);
                glVertex3d(x,y,z);
                glEnd();
                xPrev = x;
                yPrev = y;
                zPrev = z;
          }
          glBegin(GL_LINES);
          glVertex3d(xPrev,yPrev,zPrev);
          glVertex3d(x2,y2,z2);
          glEnd();
          return;
    }
    
    char szClassName[ ] = "WhiteLightning";
    int WINAPI WinMain (HINSTANCE hThisInstance,
                        HINSTANCE hPrevInstance,
                        LPSTR lpszArgument,
                        int nFunsterStil)
    
    {
        HWND hwnd;
        MSG messages;
        WNDCLASSEX wincl;
        MClr(err,sizeof(err));
    
        wincl.hInstance = hThisInstance;
        wincl.lpszClassName = szClassName;
        wincl.lpfnWndProc = WindowProcedure;
        wincl.style = CS_DBLCLKS;
        wincl.cbSize = sizeof (WNDCLASSEX);
    
        wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
        wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
        wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
        wincl.lpszMenuName = NULL;
        wincl.cbClsExtra = 0;
        wincl.cbWndExtra = 0;
        wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
    
        if (!RegisterClassEx (&wincl)) return 0;
        register UINT64 Resolution = GetScreenRes();
        hwnd = CreateWindowEx (
               0,
               szClassName,
               "Lightning",
               WS_OVERLAPPEDWINDOW,
               HIDWORD(Resolution)/8,
               LODWORD(Resolution)/8,
               HIDWORD(Resolution)*0.75,
               LODWORD(Resolution)*0.75,
               HWND_DESKTOP,
               NULL,
               hThisInstance,
               NULL);
    
        ShowWindow (hwnd, nFunsterStil);
    
        while (GetMessage (&messages, NULL, 0, 0))
        {
            TranslateMessage(&messages);
            DispatchMessage(&messages);
        }
    
        return messages.wParam;
    }
    void InitScene(void){
         glEnable(GL_DEPTH_TEST);
         glLineWidth(1.0);
         return;
    }
    bool RenderScene(void){
         glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
         RenderLightning(25,5.0,-2.0,2.0,-2.0,0.0,-1.0,-0.5);
         return true;
    }
    void UnInitScene(void){
         glDisable(GL_DEPTH_TEST);
         return;
    }
    HGLRC glDisp = NULL;
    HDC Display = NULL;
    PIMGBUFFER Image = NULL;
    UINT Timer = 0;
    THR_REND_DATA ThrDat = {NULL,NULL,NULL,NULL,NULL,NULL,0,0,0,0};
    
    LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        switch (message)                  /* handle the messages */
        {
            case WM_GETMINMAXINFO: ;
                 ((PMINMAXINFO)lParam)->ptMinTrackSize.x = 200;
                 ((PMINMAXINFO)lParam)->ptMinTrackSize.y = 200;
                 break;
            case WM_RENDVIEW: ;
                 InvalidateRect(hwnd,NULL,FALSE);
                 break;
            case WM_PAINT: ;
                 ThrDat.Flags &= ~FLAG_RENDER;
                 PAINTSTRUCT ps;
                 HDC hDC = BeginPaint(hwnd,&ps);
                 BitBlt(hDC,0,0,ThrDat.Width,ThrDat.Height,ThrDat.MemDC,0,0,SRCCOPY);
                 EndPaint(hwnd,&ps);
                 ThrDat.Flags |= FLAG_RENDER;
                 break;
            case WM_SIZE: ;
                 ThrDat.Width  = LOWORD(lParam);
                 ThrDat.Height = HIWORD(lParam);
                 ThrDat.Flags |= FLAG_RESIZE;
                 break;
            case WM_CREATE: ;
                 ThrDat.parHwnd = hwnd;
                 ThrDat.Width = ((LPCREATESTRUCT)lParam)->cx;
                 ThrDat.Height = ((LPCREATESTRUCT)lParam)->cy;
                 ThrDat.Init = InitScene;
                 ThrDat.Render = RenderScene;
                 ThrDat.UnInit = UnInitScene;
                 ThrDat.Flags |= FLAG_THREAD|FLAG_RENDER;
                 CreateRendThread(&ThrDat);
                 break;
            case WM_RENDERR: ;
                 register PUCHAR8 Error = NULL;
                 switch(wParam){
                       case ERROR_IMAGE_BUFFER: ;
                            Error = "Image Buffer Error: %u";
                            break;
                       case ERROR_WINDOW_DC: ;
                            Error = "Window Device Context Error: %u";
                            break;
                       case ERROR_MEMORY_DC: ;
                            Error = "Memory Context Creation Error: %u";
                            break;
                       case ERROR_MEMDC_SETUP: ;
                            Error = "Memory Context Setup Error: %u";
                            break;
                       case ERROR_GL_CONTEXT: ;
                            Error = "OpenGL Context Creation Error: %u";
                            break;
                       case ERROR_GLCONTEXT_SETUP: ;
                            Error = "OpenGL Context Setup Error: %u";
                            break;
                 }
                 if(wParam>ERROR_IMAGE_BUFFER){
                       DeleteImageBuffer(&ThrDat.Image);
                       ThrDat.Image = NULL;
                 }
                 if(wParam>ERROR_MEMORY_DC){
                       DeleteDC(ThrDat.MemDC);
                       ThrDat.MemDC = NULL;
                 }
                 if(wParam>ERROR_GL_CONTEXT){
                       wglDeleteContext(ThrDat.MemRC);
                       ThrDat.MemRC = NULL;
                 }
                 ThrDat.Flags |= FLAG_ERROR;
                 sprintf(err,Error,lParam);
                 ErrorBox(hwnd,err);
                 break;
            case WM_CLOSE: ;
                 if(ThrDat.Flags & FLAG_ERROR) PostMessage(hwnd,WM_RENDDONE,0,0);
                 ThrDat.Flags &= ~FLAG_THREAD;
                 break;
            case WM_RENDDONE: ;
                 CloseHandle(ThrDat.Thr);
                 PostQuitMessage(0);
                 break;
            default: ;
                 return DefWindowProc (hwnd, message, wParam, lParam);
        }
        return ERROR_SUCCESS;
    }

  2. #2
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    So you're wondering how you can speed up your code?

    I noticed that you call glBegin(GL_LINES) repeatedly inside an inner loop in RenderLightning(). It might be more efficient if you put the glBegin()/glEnd() outside the loop. However, I'd guess that RenderLightning() itself isn't the cause of a bottleneck. The inner loop isn't very complicated and only executes 25 times as far as I can see.

    I'm not on Windows so I can't test your code, but I suggest you run a profiler. That will tell you exactly where your code is slowing down. I think Dev-C++ even comes with one: gprof (see this Wiki page). If running "gprof" from the command line doesn't work, check to see if the executable actually exists in C:\Dev-C++\Bin or wherever your install directory is. If it's there you can update your path to include that directory.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  3. #3
    Registered User
    Join Date
    Nov 2010
    Posts
    13
    I found the profiler and compiled the program with profiling and this is what gprof reported:

    Flat profile:

    Each sample counts as 0.01 seconds.
    no time accumulated

    % cumulative self self total
    time seconds seconds calls Ts/call Ts/call name
    0.00 0.00 0.00 367 0.00 0.00 RenderLightning
    0.00 0.00 0.00 367 0.00 0.00 RenderScene
    0.00 0.00 0.00 1 0.00 0.00 CreateRendThread
    0.00 0.00 0.00 1 0.00 0.00 GetScreenRes
    0.00 0.00 0.00 1 0.00 0.00 InitScene
    0.00 0.00 0.00 1 0.00 0.00 SetUpDC
    0.00 0.00 0.00 1 0.00 0.00 UnInitScene
    0.00 0.00 0.00 1 0.00 0.00 WinMain@16

    % the percentage of the total running time of the
    time program used by this function.

    cumulative a running sum of the number of seconds accounted
    seconds for by this function and those listed above it.

    self the number of seconds accounted for by this
    seconds function alone. This is the major sort for this
    listing.

    calls the number of times this function was invoked, if
    this function is profiled, else blank.

    self the average number of milliseconds spent in this
    ms/call function per call, if this function is profiled,
    else blank.

    total the average number of milliseconds spent in this
    ms/call function and its descendents per call, if this
    function is profiled, else blank.

    name the name of the function. This is the minor sort
    for this listing. The index shows the location of
    the function in the gprof listing. If the index is
    in parenthesis it shows where it would appear in
    the gprof listing if it were to be printed.

    Call graph (explanation follows)


    granularity: each sample hit covers 4 byte(s) no time propagated

    index % time self children called name
    0.00 0.00 367/367 RenderScene [4]
    [3] 0.0 0.00 0.00 367 RenderLightning [3]
    -----------------------------------------------
    0.00 0.00 367/367 RenderThr@4 [62]
    [4] 0.0 0.00 0.00 367 RenderScene [4]
    0.00 0.00 367/367 RenderLightning [3]
    -----------------------------------------------
    0.00 0.00 1/1 WindowProcedure@16 [89]
    [5] 0.0 0.00 0.00 1 CreateRendThread [5]
    -----------------------------------------------
    0.00 0.00 1/1 WinMain@16 [10]
    [6] 0.0 0.00 0.00 1 GetScreenRes [6]
    -----------------------------------------------
    0.00 0.00 1/1 RenderThr@4 [62]
    [7] 0.0 0.00 0.00 1 InitScene [7]
    -----------------------------------------------
    0.00 0.00 1/1 RenderThr@4 [62]
    [8] 0.0 0.00 0.00 1 SetUpDC [8]
    -----------------------------------------------
    0.00 0.00 1/1 RenderThr@4 [62]
    [9] 0.0 0.00 0.00 1 UnInitScene [9]
    -----------------------------------------------
    0.00 0.00 1/1 main [136]
    [10] 0.0 0.00 0.00 1 WinMain@16 [10]
    0.00 0.00 1/1 GetScreenRes [6]
    -----------------------------------------------

    This table describes the call tree of the program, and was sorted by
    the total amount of time spent in each function and its children.

    Each entry in this table consists of several lines. The line with the
    index number at the left hand margin lists the current function.
    The lines above it list the functions that called this function,
    and the lines below it list the functions this one called.
    This line lists:
    index A unique number given to each element of the table.
    Index numbers are sorted numerically.
    The index number is printed next to every function name so
    it is easier to look up where the function in the table.

    % time This is the percentage of the `total' time that was spent
    in this function and its children. Note that due to
    different viewpoints, functions excluded by options, etc,
    these numbers will NOT add up to 100%.

    self This is the total amount of time spent in this function.

    children This is the total amount of time propagated into this
    function by its children.

    called This is the number of times the function was called.
    If the function called itself recursively, the number
    only includes non-recursive calls, and is followed by
    a `+' and the number of recursive calls.

    name The name of the current function. The index number is
    printed after it. If the function is a member of a
    cycle, the cycle number is printed between the
    function's name and the index number.


    For the function's parents, the fields have the following meanings:

    self This is the amount of time that was propagated directly
    from the function into this parent.

    children This is the amount of time that was propagated from
    the function's children into this parent.

    called This is the number of times this parent called the
    function `/' the total number of times the function
    was called. Recursive calls to the function are not
    included in the number after the `/'.

    name This is the name of the parent. The parent's index
    number is printed after it. If the parent is a
    member of a cycle, the cycle number is printed between
    the name and the index number.

    If the parents of the function cannot be determined, the word
    `<spontaneous>' is printed in the `name' field, and all the other
    fields are blank.

    For the function's children, the fields have the following meanings:

    self This is the amount of time that was propagated directly
    from the child into the function.

    children This is the amount of time that was propagated from the
    child's children to the function.

    called This is the number of times the function called
    this child `/' the total number of times the child
    was called. Recursive calls by the child are not
    listed in the number after the `/'.

    name This is the name of the child. The child's index
    number is printed after it. If the child is a
    member of a cycle, the cycle number is printed
    between the name and the index number.

    If there are any cycles (circles) in the call graph, there is an
    entry for the cycle-as-a-whole. This entry shows who called the
    cycle (as parents) and the members of the cycle (as children.)
    The `+' recursive calls entry shows the number of function calls that
    were internal to the cycle, and the calls entry for each member shows,
    for that member, how many times it was called from other members of
    the cycle.


    Index by function name

    [5] CreateRendThread [3] RenderLightning [9] UnInitScene
    [6] GetScreenRes [4] RenderScene [10] WinMain@16
    [7] InitScene [8] SetUpDC
    ++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++

    I'm not sure if this was helpful, but this could be useful in my future apps, so thank you. Since none of these functions seem slow I guessed it might be the WM_PAINT message since it is a low priority message. I took out the WM_PAINT message and updated the dc in the rendering thread but still got the same results. I also moved the glBegin/glEnd functions before and after the for loop, which helped a little but is still slow. The image changes about once every second, so I put a timer in the Window Procedure to record how many times it updates per second and averages about 30-32 times per second. Here's the new procedure:
    Code:
    LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        switch (message)                  /* handle the messages */
        {
            case WM_TIMER: ;
                 sprintf(err,"FPS: %u",Frame);
                 SetWindowText(hwnd,err);
                 Frame = 0;
                 break;
            case WM_GETMINMAXINFO: ;
                 ((PMINMAXINFO)lParam)->ptMinTrackSize.x = 200;
                 ((PMINMAXINFO)lParam)->ptMinTrackSize.y = 200;
                 break;
            case WM_RENDVIEW: ;
                 InvalidateRect(hwnd,NULL,FALSE);
                 break;
            case WM_PAINT: ;
                 ThrDat.Flags &= ~FLAG_RENDER;
                 PAINTSTRUCT ps;
                 HDC hDC = BeginPaint(hwnd,&ps);
                 BitBlt(hDC,0,0,ThrDat.Width,ThrDat.Height,ThrDat.MemDC,0,0,SRCCOPY);
                 EndPaint(hwnd,&ps);
                 Frame++;
                 ThrDat.Flags |= FLAG_RENDER;
                 break;
            case WM_SIZE: ;
                 ThrDat.Width  = LOWORD(lParam);
                 ThrDat.Height = HIWORD(lParam);
                 ThrDat.Flags |= FLAG_RESIZE;
                 break;
            case WM_CREATE: ;
                 ThrDat.parHwnd = hwnd;
                 ThrDat.Width = ((LPCREATESTRUCT)lParam)->cx;
                 ThrDat.Height = ((LPCREATESTRUCT)lParam)->cy;
                 ThrDat.Init = InitScene;
                 ThrDat.Render = RenderScene;
                 ThrDat.UnInit = UnInitScene;
                 ThrDat.Flags |= FLAG_THREAD|FLAG_RENDER;
                 CreateRendThread(&ThrDat);
                 Timer = SetTimer(hwnd,1,1000,NULL);
                 break;
            case WM_RENDERR: ;
                 register PUCHAR8 Error = NULL;
                 switch(wParam){
                       case ERROR_IMAGE_BUFFER: ;
                            Error = "Image Buffer Error: %u";
                            break;
                       case ERROR_WINDOW_DC: ;
                            Error = "Window Device Context Error: %u";
                            break;
                       case ERROR_MEMORY_DC: ;
                            Error = "Memory Context Creation Error: %u";
                            break;
                       case ERROR_MEMDC_SETUP: ;
                            Error = "Memory Context Setup Error: %u";
                            break;
                       case ERROR_GL_CONTEXT: ;
                            Error = "OpenGL Context Creation Error: %u";
                            break;
                       case ERROR_GLCONTEXT_SETUP: ;
                            Error = "OpenGL Context Setup Error: %u";
                            break;
                 }
                 if(wParam>ERROR_IMAGE_BUFFER){
                       DeleteImageBuffer(&ThrDat.Image);
                       ThrDat.Image = NULL;
                 }
                 if(wParam>ERROR_MEMORY_DC){
                       DeleteDC(ThrDat.MemDC);
                       ThrDat.MemDC = NULL;
                 }
                 if(wParam>ERROR_GL_CONTEXT){
                       wglDeleteContext(ThrDat.MemRC);
                       ThrDat.MemRC = NULL;
                 }
                 ThrDat.Flags |= FLAG_ERROR;
                 sprintf(err,Error,lParam);
                 ErrorBox(hwnd,err);
                 break;
            case WM_CLOSE: ;
                 KillTimer(hwnd,Timer);
                 if(ThrDat.Flags & FLAG_ERROR) PostMessage(hwnd,WM_RENDDONE,0,0);
                 ThrDat.Flags &= ~FLAG_THREAD;
                 break;
            case WM_RENDDONE: ;
                 CloseHandle(ThrDat.Thr);
                 PostQuitMessage(0);
                 break;
            default: ;                    /* for messages that we don't deal with */
                 return DefWindowProc (hwnd, message, wParam, lParam);
        }
        return ERROR_SUCCESS;
    }
    If you want to test the code for this you'll probably need the Dynamic Library or source code for the Image buffer functions I made. Ex. NewImageBuffer, ResizeImageBuffer, DeleteImageBuffer. Can a DLL, .a, C Header, or .c file be attached to these posts?

  4. #4
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Technically you can only attach mundane files like .txt files; what we usually do is attach .zip files renamed to .txt. But you misunderstand me: I'm not using Windows at all so your code wouldn't work for me at all. PostMessage, CreateThread, HWND and all the other things you are using are Windows-specific.

    The profiling says a great deal actually: it says that the total time spent in RenderLightning was 0 ms! This means your bottleneck must be elsewhere in the code. At a guess, perhaps your Windows framework code is entering a forever loop at some point and eating up most of the CPU time? Perhaps you could try grabbing NeHe's framework (NeHe Productions: Main Page) and putting your Lightning code into it, seeing if there's a speed increase.

    If I have time later today I'll try porting your RenderLightning code to Linux to see if it's any faster there.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  5. #5
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Here is your code ported (somewhat messily) to glut.
    Code:
    #include <cstdlib>
    #include <ctime>
    
    #include "GL/glut.h"
    
    #define UINT8 unsigned char
    
    using namespace std;
    
    void RenderLightning(UINT8 Seg,double zapFact,double x1,double y1,double z1,double x2,double y2,double z2){
          double subX = (x2-x1)/(double)Seg,
                 subY = (y2-y1)/(double)Seg,
                 subZ = (z2-z1)/(double)Seg,
                 x,y,z,xVar,yVar,zVar,xPrev,yPrev,zPrev;
          UINT8 i;
          srand(time(0));
          xPrev = x1;
          yPrev = y1;
          zPrev = z1;
          for(i=1;i<Seg;i++){
                xVar = (double)rand()/((double)RAND_MAX+1)/(double)Seg*zapFact;
                yVar = (double)rand()/((double)RAND_MAX+1)/(double)Seg*zapFact;
                zVar = (double)rand()/((double)RAND_MAX+1)/(double)Seg*zapFact;
                if(rand()%2) xVar = -xVar;
                if(rand()%2) yVar = -yVar;
                if(rand()%2) zVar = -zVar;
                x = x1+(double)i*subX+xVar;
                y = y1+(double)i*subY+yVar;
                z = z1+(double)i*subZ+zVar;
                glBegin(GL_LINES);
                glVertex3d(xPrev,yPrev,zPrev);
                glVertex3d(x,y,z);
                glEnd();
                xPrev = x;
                yPrev = y;
                zPrev = z;
          }
          glBegin(GL_LINES);
          glVertex3d(xPrev,yPrev,zPrev);
          glVertex3d(x2,y2,z2);
          glEnd();
          return;
    }
    
    void InitScene(void){
         glEnable(GL_DEPTH_TEST);
         glLineWidth(1.0);
    }
    void RenderScene(void){
         glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
         RenderLightning(25,5.0,-2.0,2.0,-2.0,0.0,-1.0,-0.5);
         glFlush();
    }
    
    void TimerFunc(int unused) {
        RenderScene();
    }
    
    int main(int argc, char *argv[]) {
        glutInit(&argc, argv);
        InitScene();
        glutCreateWindow("WhiteLightning");
        
        glutDisplayFunc(RenderScene);
        glutTimerFunc(1000, TimerFunc, 0);
        
        glutMainLoop();
        
        return 0;
    }
    [Coloured with codeform.] Important note: I had to add glFlush() to make anything show up.

    There are some rendering artifacts; the pieces of the lightning aren't always connected. But in general it seems to work fairly well. It's certainly not using more than say 5% of the CPU, and this computer has a terrible graphics card to boot.

    As you can see the glut code is way simpler than what you have . . . I'd suggest looking into it. You can compile glut programs under Windows as well. Just grab freeglut from somewhere and link with it. I'm pretty sure Dev-C++ even has a glut devpak for automatic installation.

    So in conclusion, I'm not sure what's wrong with your code but it must be some Windows-specific bit, because it works quite well for me. My suggestion would be to read NeHe's tutorial and use that code, or better yet switch to glut. (If you want more features you might try Qt with OpenGL.) Regardless, have fun with your coding.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  6. #6
    Registered User
    Join Date
    Nov 2010
    Posts
    13
    I used to glut example you gave me and it rendered but it only updated the display when the window needed redrawing by the system. Ex. if the window was covered and uncovered by another window. It didn't update itself. I guess I'll use the tutorials from NeHe I think the problem lies somewhere with the window's message queue and the WM_PAINT message. My code renders fine and uses less than 5% cpu but the image on the screen looks like it changes once or twice per second but should be changing 20-30 times. I'll go through the tutorials and hope they help.

  7. #7
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    Not used OpenGL for many years but a few comments....

    You are using the standard app loop (using a single GetMessage()) as opposed to a 'game' msg loop (using PeekMessage()).

    The difference is that GetMessage() 'blocks'.
    It waits at the OS msg queue until there is a msg for your app (so when the timer created more msgs the app ran faster).

    PeekMessage (PM_REMOVE) will check for a msg and return without waiting (with the msg if there is one).

    Code:
    while(!Done)
    {
         if(PeekMessage(&msg,0,0,0,PM_REMOVE)) //check for a msg
         {
               //we have a msg, 
               //check for WM_QUIT
               //else Translate and Dispatch msg
          }
          else //update game  
          {
                 //check frame rate
                 //check user input and process
                 //redraw game screen
                 //call for a paint 
            }
    }
    //clean up and exit
    To generate a WM_PAINT and post it to the windows callback directly use InvalidateRect() [generates paing msg] followed by an UpdateWindow() [posts directly to callback, bypassing any msg queues].

    Try using the RECT in the PAINTSTRUCT to reduce the area you BitBlt()

    WM_TIMER msgs are as low a priority as WM_PAINT (+/- 50ms error). There are higher perfomance timers available if you need [QueryPrefomanceTimer() runs on CPU cycles].
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  8. #8
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Quote Originally Posted by Lane the Great View Post
    I used to glut example you gave me and it rendered but it only updated the display when the window needed redrawing by the system. Ex. if the window was covered and uncovered by another window. It didn't update itself. I guess I'll use the tutorials from NeHe I think the problem lies somewhere with the window's message queue and the WM_PAINT message. My code renders fine and uses less than 5% cpu but the image on the screen looks like it changes once or twice per second but should be changing 20-30 times. I'll go through the tutorials and hope they help.
    Well actually it also updates once per second (the 1000 number makes it update every 1000 milliseconds). Or it was intended to, but I forgot to call TimerFunc again. Like I said it's hacked together, I apologize.

    As for CPU time, I hadn't realized what your actual problem was. I guess I should read more closely. Anyway with my experimentation it seems like the call to srand(time(0)) is actually slowing it down! If I use a less hacked-together glut port and move the srand() call to main, I get very nice lightning.
    Code:
    #include <cstdlib>
    #include <ctime>
    
    #include "GL/glut.h"
    
    #define UINT8 unsigned char
    
    void RenderLightning(UINT8 Seg,double zapFact,double x1,double y1,double z1,double x2,double y2,double z2){
          double subX = (x2-x1)/(double)Seg,
                 subY = (y2-y1)/(double)Seg,
                 subZ = (z2-z1)/(double)Seg,
                 x,y,z,xVar,yVar,zVar,xPrev,yPrev,zPrev;
          UINT8 i;
          
          xPrev = x1;
          yPrev = y1;
          zPrev = z1;
          for(i=1;i<Seg;i++){
                xVar = (double)rand()/((double)RAND_MAX+1)/(double)Seg*zapFact;
                yVar = (double)rand()/((double)RAND_MAX+1)/(double)Seg*zapFact;
                zVar = (double)rand()/((double)RAND_MAX+1)/(double)Seg*zapFact;
                if(rand()%2) xVar = -xVar;
                if(rand()%2) yVar = -yVar;
                if(rand()%2) zVar = -zVar;
                x = x1+(double)i*subX+xVar;
                y = y1+(double)i*subY+yVar;
                z = z1+(double)i*subZ+zVar;
                glBegin(GL_LINES);
                glVertex3d(xPrev,yPrev,zPrev);
                glVertex3d(x,y,z);
                glEnd();
                xPrev = x;
                yPrev = y;
                zPrev = z;
          }
          glBegin(GL_LINES);
          glVertex3d(xPrev,yPrev,zPrev);
          glVertex3d(x2,y2,z2);
          glEnd();
    }
    
    void InitScene(void){
         glEnable(GL_DEPTH_TEST);
         glLineWidth(1.0);
    }
    void RenderScene(void){
         glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
         RenderLightning(25,5.0,-2.0,2.0,-2.0,0.0,-1.0,-0.5);
         glFlush();
    }
    
    void TimerFunc(int unused) {
        RenderScene();
        glutTimerFunc(10, TimerFunc, 0);
    }
    
    int main(int argc, char *argv[]) {
        srand(time(0));
    
        glutInit(&argc, argv);
        InitScene();
        glutCreateWindow("WhiteLightning");
        
        glutDisplayFunc(RenderScene);
        glutTimerFunc(10, TimerFunc, 0);
        
        glutMainLoop();
        
        return 0;
    }
    By the way, you're only supposed to call srand() once during a program's execution anyway. If you keep re-calling it it keeps re-initializing the random number generator which is, apparently, an expensive operation.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  9. #9
    Registered User
    Join Date
    Nov 2010
    Posts
    13

    Talking

    You were right about srand! All I did was move srand to the WM_CREATE message so it was called only once and the lightning is rendering at the right speed! I didn't expect srand would slow it down. Apparently it was the whole problem. Thanx!!!

  10. #10
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    if(PeekMessage(&msg,0,0,0,PM_REMOVE)) //check for a msg
    Using while here is actually a bit better. I used to use this exact construct but found that many recent books and articles recommend while. Apparently removing 1 message from the queue is fine when the message queue is not under a heavy load. However under a heavy load the messages will begin to pile up. The while pretty much empties the queue each frame which sounds slower but is actually faster b/c the messages never get a chance to pile up and as such while is only removing a small number of messages each time.

  11. #11
    'Allo, 'Allo, Allo
    Join Date
    Apr 2008
    Posts
    639
    Erm, that loop also removes all messages from the queue before doing the update game bit. Replacing the if with a while doesn't change anything. It's kinda the whole basis of if and else that only one of them is taken per loop iteration.
    Last edited by adeyblue; 01-07-2011 at 07:55 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Console Screen Buffer
    By GaPe in forum Windows Programming
    Replies: 0
    Last Post: 02-06-2003, 05:15 AM
  2. MISC questions about OpenGL
    By Silvercord in forum Game Programming
    Replies: 12
    Last Post: 01-25-2003, 04:20 PM
  3. OpenGL and Windows
    By sean345 in forum Game Programming
    Replies: 5
    Last Post: 06-24-2002, 10:14 PM
  4. opengl code not working
    By Unregistered in forum Windows Programming
    Replies: 4
    Last Post: 02-14-2002, 10:01 PM
  5. getline problem
    By Unregistered in forum C++ Programming
    Replies: 4
    Last Post: 10-06-2001, 09:28 AM

Tags for this Thread