kwaegel's picture

OpenCL interop AccessViolationException

I am trying to setup a shared OpenGL/OpenCL system and I am getting several odd errors. My setup code is primarily based off this post and this article. The rest of the program is based off the standard game.cs sample file included with OpenTK.

The most annoying error at this point is an AccessViolationException that occurs when the program is closed. The specific error text is "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." The error occurs on line 171 of ComputeContext.cs in Cloo. Specificaly, the dispose method:

protected override void Dispose(bool manual)
{
	if (manual)
	{
		//free managed resources
	}
 
	// free native resources
	if (Handle != IntPtr.Zero)
	{
		CL10.ReleaseContext(Handle);		//--> Exception is thrown from this line
		Handle = IntPtr.Zero;
	}
}

Building the OpenCL context with the default properties list (commented out in the code below) seems to have no issues. Only using properties list constructed with the sharing properties causes the problem. I am assuming it is a pointer issue, but I do not know enough about Cloo (or OpenGL for that matter) to know where to look.

I should probably note that I am not actually using any OpenCL kernels at this point. All that code is commented out while I try to resolve this issue. The kernels have their own set of bugs that I will need to deal with later...

My current shared context setup code is as follows:

private void openCLSharedInit()
{
	// select OpenCL device and platform
	ComputePlatform platform = ComputePlatform.Platforms[0];
	ComputeDevice device = platform.Devices[0];
 
	IntPtr curDC = wglGetCurrentDC();
 
	OpenTK.Graphics.IGraphicsContextInternal ctx = (OpenTK.Graphics.IGraphicsContextInternal)OpenTK.Graphics.GraphicsContext.CurrentContext;
	IntPtr raw_context_handle = ctx.Context.Handle;
	ComputeContextProperty p1 = new ComputeContextProperty(ComputeContextPropertyName.CL_GL_CONTEXT_KHR, raw_context_handle);
	ComputeContextProperty p2 = new ComputeContextProperty(ComputeContextPropertyName.CL_WGL_HDC_KHR, curDC);
	ComputeContextProperty p3 = new ComputeContextProperty(ComputeContextPropertyName.Platform, platform.Handle);
	List<ComputeContextProperty> props = new List<ComputeContextProperty>() { p1, p2, p3 };
	ComputeContextPropertyList Properties = new ComputeContextPropertyList(props);
 
	// using this property list does not cause an error
	//ComputeContextPropertyList Properties = new ComputeContextPropertyList(platform);
 
	_computeContext = new ComputeContext(ComputeDeviceTypes.Gpu, Properties, null, IntPtr.Zero);
 
	//Create the command queue from the context and device
	_commandQueue = new ComputeCommandQueue(_computeContext, device, ComputeCommandQueueFlags.None);
}

System setup:
nVidia 8600GT with driver version 258.96
AMD Phenom II 955 BE 3.2Ghz
Windows 7 x64 (running program in 64-bit mode)
.Net Framework 4 installed, targeting 3.5
OpenTK 1.0 rc1
Cloo latest trunk revision


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
nythrix's picture

This may be related to the GraphicsContext being freed before the ComputeContext. Put this into your application exit procedure and see what happens:

clContext.Dispose();
GC.KeepAlive(glContext);
kwaegel's picture

I tried putting that code in the Dispose method of the game (see below), but nothing seems to have changed. Oddly enough, I tried adding try...catch blocks around all the lines in the dispose method and nothing appears to have been thrown there. I did the same with the main method and I did not get any exceptions from there either. So either AccessViolationExceptions are not being caught by the catch block or it is being thrown when main() terminates.

Would it help if I posted the entire program code? It is only a relatively simple test program (~300 lines) contained in one class.

Updated dispose method:

public override void Dispose()
{
	_computeContext.Dispose();
	GC.KeepAlive(_glContext);
 
	base.Dispose();
 
}
nythrix's picture

Sure, I'll take a look. If this is one of those nasty GC-messing-with-my-stuff bugs other users will hit it too. Sooner or later.

kwaegel's picture

Thanks. Here is the code. I should note that most of the kernel program code was commented out to simplify debugging. The original intent was to fill the screen with a textured quad after drawing to the texture with OpenCL.

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
 
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;
 
using Cloo;
 
namespace ClooTest
{
    class Game : OpenTK.GameWindow
	{
		#region OpenCL source code
		static string _screenColorsSource = @"
 
				kernel void
				cycleColors(	__global		float *		timeData
								__write_only	image2d_t	outputImage)
 
				{
					// Image element index
					//int2 coord = (int2)(get_global_id(0), get_global_id(1));
					int index = get_global_id(0);
 
					float time = timeData[0];
 
					float4 color;
					color.x = 5.0f;
					color.y = 5.0f;
					color.z = 5.0f;
					color.w = 0.0f;
 
					//write_imagef(outputImage, coord, color);
				}
				";
 
		#endregion
 
		// required for OpenCL-OpenGL interop
		[DllImport("opengl32.dll")]
		extern static IntPtr wglGetCurrentDC();
 
		double _totalTime = 0;
 
		OpenTK.Graphics.IGraphicsContextInternal _glContext;
 
		ComputeContext _computeContext;
		ComputeCommandQueue _commandQueue;
 
		ComputeProgram _clProgram;
		ComputeKernel _colorCycleKernel;
 
		// OpenCL data buffers
		ComputeImage2D _imageReadBuffer;
		ComputeImage2D _drawTarget;
 
		// buffer to store time data
		float[] _timeData;
		ComputeBuffer<float> _timeBuffer;
 
		// list of shared objects
		List<ComputeMemory> _sharedObjects = new System.Collections.Generic.List<ComputeMemory>();
 
        /// <summary>Creates a window with the specified title.</summary>
        public Game()
            : base(400, 400, GraphicsMode.Default, "Cloo tester")
        {
            VSync = VSyncMode.On;
        }
 
        /// <summary>Load resources here.</summary>
        /// <param name="e">Not used.</param>
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
 
			//printOpenCLInfo();
			//openCLInit();
			openCLSharedInit();
 
			try
			{
				// build and compile an OpenCL program to change screen colors
				//_clProgram = new ComputeProgram(_computeContext, _screenColorsSource);
				//_clProgram.Build(null, null, null, IntPtr.Zero);
 
				// create a kernel for the color cycling method.
				//_colorCycleKernel = _clProgram.CreateKernel("cycleColors");
 
				// create OpenCL draw target
				//createSharedObjects();
			}
			catch (Exception exc)
			{
				System.Diagnostics.Trace.WriteLine(exc.Message);
 
				this.Exit();
			}
        }
 
		// create objects to share between OpenGL and OpenCL
		private void createSharedObjects()
		{
			int FboWidth = ClientRectangle.Width;
			int FboHeight = ClientRectangle.Height;
 
			int FboHandle;
			int renderbufferHandle;
			int renderTextureHandle;
 
			// create a frameBuffer object (FBO)
			GL.Ext.GenFramebuffers(1, out FboHandle);
			GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, FboHandle);
 
			// create a renderBuffer
			GL.Ext.GenRenderbuffers(1, out renderbufferHandle);
			GL.Ext.BindRenderbuffer(RenderbufferTarget.RenderbufferExt, renderbufferHandle);
			GL.Ext.RenderbufferStorage(RenderbufferTarget.RenderbufferExt, RenderbufferStorage.Rgba8, FboWidth, FboHeight);
 
			// attatch the renderBuffer to the FBO as a color texture
			GL.FramebufferRenderbuffer(FramebufferTarget.FramebufferExt, FramebufferAttachment.ColorAttachment0Ext,
				RenderbufferTarget.RenderbufferExt, renderbufferHandle);
 
			// create openCL image from renderBuffer
			_imageReadBuffer = ComputeImage2D.CreateFromGLRenderbuffer(_computeContext, ComputeMemoryFlags.ReadOnly, renderbufferHandle);
			_sharedObjects.Add(_imageReadBuffer);
 
			// create OpenGL texture for rendering
			GL.GenTextures(1, out renderTextureHandle);
			GL.BindTexture(TextureTarget.Texture2D, renderTextureHandle);
 
			// setup texture data storage
			GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba32f, FboWidth, FboHeight, 0, PixelFormat.Rgba, PixelType.Float, IntPtr.Zero);
 
			// Create OpenCL image for kernel to write to
			_drawTarget = ComputeImage2D.CreateFromGLTexture2D(_computeContext, ComputeMemoryFlags.WriteOnly, (int)TextureTarget.Texture2D, 0, renderTextureHandle);
			_sharedObjects.Add(_drawTarget);
 
			// create time buffer
			_timeData = new float[1];
			_timeBuffer = new ComputeBuffer<float>(_computeContext, ComputeMemoryFlags.ReadWrite | ComputeMemoryFlags.UseHostPointer, _timeData);
		}
 
		private void openCLInit()
		{
			// Identify the platform and device to use.
			ComputePlatform platform = ComputePlatform.Platforms[0];
			ComputeDevice device = platform.Devices[0];
 
			ComputeContextPropertyList Properties = new ComputeContextPropertyList(platform);
			_computeContext = new ComputeContext(ComputeDeviceTypes.Gpu, Properties, null, IntPtr.Zero);
 
			//Create the command queue
			_commandQueue = new ComputeCommandQueue(_computeContext, device, ComputeCommandQueueFlags.None);
		}
 
		private void openCLSharedInit()
		{
			// select OpenCL device and platform
			ComputePlatform platform = ComputePlatform.Platforms[0];
			ComputeDevice device = platform.Devices[0];
 
 
			IntPtr curDC = wglGetCurrentDC();
 
			_glContext = (OpenTK.Graphics.IGraphicsContextInternal)OpenTK.Graphics.GraphicsContext.CurrentContext;
			IntPtr raw_context_handle = _glContext.Context.Handle;
			ComputeContextProperty p1 = new ComputeContextProperty(ComputeContextPropertyName.CL_GL_CONTEXT_KHR, raw_context_handle);
			ComputeContextProperty p2 = new ComputeContextProperty(ComputeContextPropertyName.CL_WGL_HDC_KHR, curDC);
			ComputeContextProperty p3 = new ComputeContextProperty(ComputeContextPropertyName.Platform, platform.Handle);
			List<ComputeContextProperty> props = new List<ComputeContextProperty>() { p1, p2, p3 };
			ComputeContextPropertyList Properties = new ComputeContextPropertyList(props);
 
			// using this property list does not cause an error
			//ComputeContextPropertyList Properties = new ComputeContextPropertyList(platform);
 
			_computeContext = new ComputeContext(ComputeDeviceTypes.Gpu, Properties, null, IntPtr.Zero);
 
			//Create the command queue from the context and device
			_commandQueue = new ComputeCommandQueue(_computeContext, device, ComputeCommandQueueFlags.None);
		}
 
		/// <summary>
        /// Called when your window is resized. Set your viewport here. It is also
        /// a good place to set up your projection matrix (which probably changes
        /// along when the aspect ratio of your window).
        /// </summary>
        /// <param name="e">Not used.</param>
        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
 
			GL.Viewport(ClientRectangle.X, ClientRectangle.Y, ClientRectangle.Width, ClientRectangle.Height);
			Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView(OpenTK.MathHelper.Pi / 4, Width / (float)Height, 1.0f, 64.0f);
			GL.MatrixMode(MatrixMode.Projection);
			GL.LoadMatrix(ref projection);
        }
 
        /// <summary>
        /// Called when it is time to setup the next frame. Add you game logic here.
        /// </summary>
        /// <param name="e">Contains timing information for framerate independent logic.</param>
        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);
 
			_totalTime += e.Time;
 
			//_colorCycleKernel.SetValueArgument<float>(0, (float)_totalTime);
 
            if (Keyboard[Key.Escape])
                Exit();
        }
 
        /// <summary>
        /// Called when it is time to render the next frame. Add your rendering code here.
        /// </summary>
        /// <param name="e">Contains timing information.</param>
        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);
 
			updateTitle(e.Time);
 
			// aquire openGL objects
			GL.Finish();
			//_commandQueue.AcquireGLObjects(_sharedObjects, null);
 
			//_colorCycleKernel.SetValueArgument<float>(0, (float)_totalTime);
			//_colorCycleKernel.SetMemoryArgument(0, _timeBuffer);
			//_colorCycleKernel.SetMemoryArgument(1, _drawTarget);
 
			//_commandQueue.Execute(_colorCycleKernel, null, new long[] {ClientRectangle.Width, ClientRectangle.Height}, null, null);
 
			// release openGL objects
			//_commandQueue.ReleaseGLObjects(_sharedObjects, null);
			_commandQueue.Finish();
 
			// draw the texture as a full screen quad
 
 
			// display the new frame
            SwapBuffers();
        }
 
		private void updateTitle(double frameDelta)
		{
			int pixels = ClientSize.Height * ClientSize.Width;
			String pixelString = String.Format("{0:n0}", pixels);
 
			float fps = (float)(1.0 / frameDelta);
			String fpsString = String.Format("{0:##.#}", (int)fps);
			this.Title = "Raytracing tester (" + fpsString + " FPS, " + pixelString + " pixels)";
 
		}
 
		public static void printOpenCLInfo()
		{
			// system diagnostics
			System.Diagnostics.Trace.WriteLine("All discovered platforms: ");
			int platformID = 0;
			foreach (ComputePlatform cp in ComputePlatform.Platforms)
			{
				//System.Diagnostics.Debug.WriteLine();
				System.Diagnostics.Trace.WriteLine("Platform " + platformID + ": " + cp.Name);
 
				int deviceId = 0;
				foreach (ComputeDevice cd in cp.Devices)
				{
					System.Diagnostics.Trace.WriteLine("\tDevice " + deviceId + ": " + cd.Name);
					System.Diagnostics.Trace.WriteLine("\t\t\tType=" + cd.Type);
					System.Diagnostics.Trace.WriteLine("\t\t\tVendor=" + cd.Vendor);
					System.Diagnostics.Trace.WriteLine("\t\t\tCache size=" + cd.GlobalMemoryCacheSize);
					System.Diagnostics.Trace.WriteLine("\t\t\tCache type=" + cd.GlobalMemoryCacheType);
					System.Diagnostics.Trace.WriteLine("\t\t\tCompute units=" + cd.MaxComputeUnits);
					System.Diagnostics.Trace.WriteLine("\t\t\tLocal memory=" + cd.LocalMemorySize);
					System.Diagnostics.Trace.WriteLine("\t\t\tImage support=" + cd.ImageSupport);
					deviceId++;
				}
				platformID++;
			}
		}
 
		public override void Dispose()
		{
			_computeContext.Dispose();
			GC.KeepAlive(_glContext);
 
			base.Dispose();
 
		}
 
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main()
        {
			System.Diagnostics.Trace.WriteLine("");
			System.Diagnostics.Trace.WriteLine("Run at " + System.DateTime.Now.ToString());
 
			// The 'using' idiom guarantees proper resource cleanup.
			// We request 30 UpdateFrame events per second, and unlimited
			// RenderFrame events (as fast as the computer can handle).
			using (Game game = new Game())
			{
				game.Run(30.0);
				//game.Run();
			}
        }
    }
}
nythrix's picture

This might take a bit longer since I don't have an OpenCL enabled GPU at the moment. Will return to this as soon as possible.

kwaegel's picture

I found a workaround that stopped the error.

Everything seems to work if I manually dispose of all the ComputeMemory objects (ComputeBuffers, ComputeImages, etc) before the program is allowed to exit. I did this by placing dispose commands (or calls to objects that call dispose) in the Dispose(bool manual) method that can be overridden from GameWindow.

nythrix, any idea why manually disposing of everything works correctly, but the automatic cleanup does not?

nythrix's picture

This one got a bit lost in the woods.

It looks like the app has to manually dispose of shared CL/GL contexts. The GC cannot make a qualified decision. Also, does the GL context have to be current when releasing such ComputeContexts? I really don't know and I welcome any ideas on how to detect (and hopefully prevent) this situation through Cloo.

Note: The Dispose() you posted here will probably never get called.