Search code examples
c#crashopentk

OpenTK Graphics Card Crashing During Occlusion Culling


I recently implemented Frustum culling in my game, and in an attempt to squeeze every last drop out of my render cycle I also decided to implement Occlusion Culling. It works brilliantly, however, I was distressed to discover that if I dont look at anything in my game(If I look out into the void and away from my game objects) my graphics card literally crashes. I am running a voxel-type game, which means it is a world filled with cubes. If there is no cubes present in my line of sight the crash occurs.

Here is my render loop which contains the Occlusion code:

protected override void OnRenderFrame( FrameEventArgs e ) {
    base.OnRenderFrame( e );

    GL.MatrixMode( MatrixMode.Modelview );
    GL.Clear( ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit );
    GL.EnableClientState( ArrayCap.VertexArray );
    GL.EnableClientState( ArrayCap.TextureCoordArray );
    GL.DisableClientState( ArrayCap.ColorArray );

    /**
     * Pass 1
     * Do Occlusion Testing
     */
    GameCamera.LookThrough( this , _mousePosition , e );
    foreach( Voxel voxel in World.VisibleVoxels ) {
        if( GameCamera.Frustum.SphereInFrustum( voxel.Location.X , voxel.Location.Y , voxel.Location.Z , 2.0f ) ) {
            try {
                GL.BeginQuery( QueryTarget.SamplesPassed , voxel.OcclusionID );
                voxel.Render( GameCamera );
                GL.EndQuery( QueryTarget.SamplesPassed );
            } catch( Exception ex ) {
                //
                Console.WriteLine( "Setting It" );
                Console.WriteLine( ex.StackTrace );
            }
        }
    }
    GL.Clear( ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit );
    /**
     * Pass 2
     * Normal Rendering
     */
    GameCamera.LookThrough( this , _mousePosition , e );
    foreach( Voxel voxel in World.VisibleVoxels ) {
        if( GameCamera.Frustum.SphereInFrustum( voxel.Location.X , voxel.Location.Y , voxel.Location.Z , 2.0f ) ) {
            try {
                GL.NV.BeginConditionalRender( voxel.OcclusionID , NvConditionalRender.QueryWaitNv );
                voxel.Render( GameCamera );
                GL.NV.EndConditionalRender();
            } catch( Exception ex ) {
                Console.WriteLine( "Testing It" );
                Console.WriteLine( ex.StackTrace );
            }

        }
    }
    GL.DisableClientState( ArrayCap.VertexArray );
    GL.DisableClientState( ArrayCap.TextureCoordArray );
    GL.DisableClientState( ArrayCap.ColorArray );
    RenderDeveloperHud();
    SwapBuffers();
    this.Title = GAME_NAME + " FPS: " + ( int )this.RenderFrequency;
}

I desperatly need to find a solution for this. It seems to me that OpenTK/OpenGL flips its shit when there is nothing visible in my viewport, but I dont know why. The loop itself should pass through if nothing is visible. Am I missing something here?

I can literally reproduce this crash every time I start the game and look away from the level. And by crash I mean my entire monitor goes black, hangs up, and then resumes with a message saying my display driver stopped working


Solution

  • As much as it infuriates me, I have to answer my own question again. I think I pinpointed the error, although I dont particularly understand it...it doesnt crash anymore. Instead of having two passes, i compressed my render cycle down to a single iteration; this solved the graphics card crash. Anyone know why this works:

    protected override void OnRenderFrame( FrameEventArgs e ) {
    
        base.OnRenderFrame( e );
    
        GL.MatrixMode( MatrixMode.Modelview );
        GL.Clear( ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit );
        GL.EnableClientState( ArrayCap.VertexArray );
        GL.EnableClientState( ArrayCap.TextureCoordArray );
        GL.DisableClientState( ArrayCap.ColorArray );
    
        /**
         * Pass 1
         * Normal Rendering && Occlusion Test
         */
        GameCamera.LookThrough( this , _mousePosition , e );
        if(NeedsOcclusionPass) {
            foreach(Voxel voxel in World.VisibleVoxels) {
                if(GameCamera.Frustum.SphereInFrustum(voxel.Location.X, voxel.Location.Y, voxel.Location.Z, 2.0f)) {
                    GL.BeginQuery(QueryTarget.SamplesPassed, voxel.OcclusionID);
                    voxel.Render(GameCamera);
                    GL.EndQuery(QueryTarget.SamplesPassed);
                }
            }
            NeedsOcclusionPass = false;
        } else {
            foreach( Voxel voxel in World.VisibleVoxels ) {
                if( GameCamera.Frustum.SphereInFrustum( voxel.Location.X , voxel.Location.Y , voxel.Location.Z , 2.0f ) ) {
                    GL.NV.BeginConditionalRender( voxel.OcclusionID , NvConditionalRender.QueryNoWaitNv );
                    voxel.Render( GameCamera );
                    GL.NV.EndConditionalRender();
                }
            }
            NeedsOcclusionPass = true;
        }
    
        GL.DisableClientState( ArrayCap.VertexArray );
        GL.DisableClientState( ArrayCap.TextureCoordArray );
        GL.DisableClientState( ArrayCap.ColorArray );
        //RenderDeveloperHud();
        SwapBuffers();
        this.Title = GAME_NAME + " FPS: " + ( int )this.RenderFrequency;
    }
    

    But my original code above causes a crash? Now I am just more confused than anything. (And no the issue was not caused by the double call of GameCamera.LookThrough(); It just seems that having two passes itself was the error.

    Edit: After further testing and various forms of literature I have come to the conclusion that I was using the above code completely wrong. According to other sources, I should be disabling almost EVERYTHING before beginning the occlusion queries. This means: textures, lighting,and even the depth buffer. I still don't know what caused the crashing of my graphics card, however, it most certainly stems from my lack of understanding on the topic.