/* * Copyright (c) 2009 Olav Kalgraf(olav.kalgraf@gmail.com) * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Windows.Forms; using System.IO; using System.Runtime.InteropServices; using System.Threading; using OpenCLNet; namespace UnitTests { public partial class Form1 : Form { Platform[] Platforms; Regex ParseOpenCLVersion = new Regex(@"OpenCL (?\d+)\.(?\d+).*"); public Form1() { InitializeComponent(); } private void startToolStripMenuItem_Click(object sender, EventArgs e) { try { listBoxErrors.Items.Clear(); listBoxWarnings.Items.Clear(); listBoxOutput.Items.Clear(); RunTests(); } catch (Exception ex) { MessageBox.Show(ex.ToString(), "Test terminated with a fatal exception."); } } private void RunTests() { TestOpenCLClass(); } private void TestOpenCLClass() { if (OpenCL.NumberOfPlatforms <= 0) { listBoxOutput.Items.Add("OpenCL.NumberOfPlatforms=" + OpenCL.NumberOfPlatforms); throw new Exception("TestOpenCLClass: NumberOfPlatforms<0. Is the API available at all?"); } Platforms = OpenCL.GetPlatforms(); if (Platforms.Length != OpenCL.NumberOfPlatforms) Error("OpenCL.NumberOfPlatforms!=Length of openCL.GetPlatforms()" + OpenCL.NumberOfPlatforms); for (int platformIndex = 0; platformIndex < Platforms.Length; platformIndex++) { if (OpenCL.GetPlatform(platformIndex) != Platforms[platformIndex]) Error("openCL.GetPlatform(platformIndex)!=Platforms[platformIndex]"); Output("Testing Platform " + platformIndex); TestPlatform(Platforms[platformIndex]); } } private void TestPlatform(Platform p) { Device[] allDevices; Device[] cpuDevices; Device[] gpuDevices; Device[] acceleratorDevices; Output("Name: " + p.Name); Output("Vendor:" + p.Vendor); Output("Version:" + p.Version); // Check format of version string Match m = ParseOpenCLVersion.Match(p.Version); if (!m.Success) Warning("Platform " + p.Name + " has an invalid version string"); else { if (m.Groups["MajorVersion"].Value != "1" && m.Groups["MinorVersion"].Value != "0") Warning("Platform " + p.Name + " has a version number!=1.0(Not really a problem, but this test is written for 1.0)"); } // Check format of profile Output("Profile:" + p.Profile); if (p.Profile == "FULL_PROFILE" || p.Profile == "EMBEDDED_PROFILE") Output("Profile:" + p.Profile); else Warning("Platform " + p.Name + " has unknown profile "+p.Profile); Output("Extensions: " + p.Extensions); // Test whether number of devices is consistent allDevices = p.QueryDevices(DeviceType.ALL); if( allDevices.Length<=0 ) Warning( "Platform "+p.Name+" has no devices" ); cpuDevices = p.QueryDevices(DeviceType.CPU); gpuDevices = p.QueryDevices(DeviceType.GPU); acceleratorDevices = p.QueryDevices(DeviceType.ACCELERATOR); if( allDevices.Length!=cpuDevices.Length+gpuDevices.Length+acceleratorDevices.Length ) Warning( "QueryDevices( DeviceType.ALL ) return length inconsistent with sum of special purpose queries" ); // Create a few contexts and test them Output( "Testing Platform.CreateDefaultContext()" ); using (Context c = p.CreateDefaultContext()) { Output("Testing context"+c); TestContext(c); } Output(""); Output(""); if (cpuDevices.Length > 0) { Output("Testing Platform.CreateContext()"); using (Context c = p.CreateContext(null, cpuDevices, new ContextNotify(ContextNotifyFunc), (IntPtr)0x01234567)) { Output("Testing context" + c); TestContext(c); } Output(""); Output(""); } if (gpuDevices.Length > 0) { Output("Testing Platform.CreateContext()"); using (Context c = p.CreateContext(null, gpuDevices, new ContextNotify(ContextNotifyFunc), (IntPtr)0x01234567)) { Output("Testing context" + c); TestContext(c); } Output(""); Output(""); } if (cpuDevices.Length > 0) { Output("Testing Platform.CreateContextFromType()"); IntPtr[] contextProperties = new IntPtr[] { (IntPtr)ContextProperties.PLATFORM, p.PlatformID, IntPtr.Zero }; using (Context c = p.CreateContextFromType(contextProperties, DeviceType.CPU, new ContextNotify(ContextNotifyFunc), (IntPtr)0x01234567)) { Output("Testing context" + c); TestContext(c); } } if (gpuDevices.Length > 0) { Output("Testing Platform.CreateContextFromType()"); IntPtr[] contextProperties = new IntPtr[] { (IntPtr)ContextProperties.PLATFORM, p.PlatformID, IntPtr.Zero }; using (Context c = p.CreateContextFromType(contextProperties, DeviceType.GPU, new ContextNotify(ContextNotifyFunc), (IntPtr)0x01234567)) { Output("Testing context" + c); TestContext(c); } } } private void ContextNotifyFunc( string errInfo, byte[] privateInfo, IntPtr cb, IntPtr userData ) { Error(errInfo); } int NativeKernelCalled = 0; NativeKernel NativeKernelCallRef; private void TestContext(Context c) { Device[] devices = c.Devices; OpenCLNet.Program p = c.CreateProgramFromFile("MemoryTests.cl"); Dictionary kernelDictionary; try { p.Build(); } catch (OpenCLException ocle) { throw ocle; } kernelDictionary = p.CreateKernelDictionary(); NativeKernelCallRef = new NativeKernel(NativeKernelTest); for (int deviceIndex = 0; deviceIndex < devices.Length; deviceIndex++) { Device d; d = devices[deviceIndex]; using (CommandQueue cq = c.CreateCommandQueue(d)) { if ( (d.ExecutionCapabilities & (ulong)DeviceExecCapabilities.NATIVE_KERNEL)!=0 ) { Output("Testing native kernel execution"); cq.EnqueueNativeKernel(NativeKernelCallRef); cq.EnqueueBarrier(); if (NativeKernelCalled != 1) Error("EnqueueNativeKernel failed"); Interlocked.Decrement(ref NativeKernelCalled); } else { Output("Testing native kernel execution: Not supported"); } TestMem(c, cq, kernelDictionary); TestDevice(d); TestCommandQueue(c, cq); TestKernel(c, cq, kernelDictionary["ArgIO"]); } } } public unsafe void NativeKernelTest(IntPtr args) { Interlocked.Increment(ref NativeKernelCalled); } [StructLayout(LayoutKind.Sequential, Pack=1)] internal struct IOKernelArgs { internal long outLong; internal int outInt; internal float outSingle; internal IntPtr outIntPtr; } private unsafe void TestKernel(Context c, CommandQueue cq, Kernel argIOKernel) { Mem outArgBuffer = c.CreateBuffer(MemFlags.WRITE_ONLY, sizeof(IOKernelArgs), IntPtr.Zero); Output("Testing kernel - Argument return"); argIOKernel.SetIntArg(0, 1); argIOKernel.SetLongArg(1, 65); argIOKernel.SetSingleArg(2, 38.4f); argIOKernel.SetIntPtrArg(3, new IntPtr(0x01234567)); argIOKernel.SetIntPtrArg(4, outArgBuffer.MemID); cq.EnqueueTask(argIOKernel); IntPtr outArgPtr = cq.EnqueueMapBuffer( outArgBuffer, true, MapFlags.READ, IntPtr.Zero, (IntPtr)sizeof(IOKernelArgs) ); IOKernelArgs args = (IOKernelArgs)Marshal.PtrToStructure(outArgPtr, typeof(IOKernelArgs)); cq.EnqueueUnmapMemObject(outArgBuffer, outArgPtr); if (args.outInt != 1) Error("argIOKernel failed to return correct arguments"); if (args.outLong != 65) Error("argIOKernel failed to return correct arguments"); if (args.outSingle != 38.4f) Error("argIOKernel failed to return correct arguments"); if (args.outIntPtr != new IntPtr(0x01234567)) Error("argIOKernel failed to return correct arguments"); } private void TestMem(Context c, CommandQueue cq, Dictionary kernelDictionary ) { long size = 8192; Output( "Testing Mem class" ); Output( "Allocating "+size+" bytes of READ_WRITE memory" ); using (Mem buffer = c.CreateBuffer(MemFlags.READ_WRITE, size, IntPtr.Zero)) { Output("Mem.MemSize=" + size); if (buffer.MemSize.ToInt64() != size) Error("Mem.Size!=input size"); Output("Mem.MemType=" + buffer.MemType); if (buffer.MemType != MemObjectType.BUFFER) Error("Mem.MemType!=MemObjectType.BUFFER"); Output("Mem.MapCount=" + buffer.MapCount); if (buffer.MapCount != 0) Error("Mem.MapCount!=0"); Kernel k = kernelDictionary["TestReadWriteMemory"]; k.SetArg(0, buffer); k.SetArg(1, (IntPtr)size); cq.EnqueueTask(k); cq.EnqueueBarrier(); } Output("Allocating " + size + " bytes of READ memory"); using (Mem buffer = c.CreateBuffer(MemFlags.READ_ONLY, size, IntPtr.Zero)) { Output("Mem.MemSize=" + size); if (buffer.MemSize.ToInt64() != size) Error("Mem.Size!=input size"); Output("Mem.MemType=" + buffer.MemType); if (buffer.MemType != MemObjectType.BUFFER) Error("Mem.MemType!=MemObjectType.BUFFER"); Output("Mem.MapCount=" + buffer.MapCount); if (buffer.MapCount != 0) Error("Mem.MapCount!=0"); Kernel k = kernelDictionary["TestReadMemory"]; k.SetArg(0, buffer); k.SetArg(1, (IntPtr)size); cq.EnqueueTask(k); cq.EnqueueBarrier(); } Output("Allocating " + size + " bytes of WRITE memory"); using (Mem buffer = c.CreateBuffer(MemFlags.WRITE_ONLY, size, IntPtr.Zero)) { Output("Mem.MemSize=" + size); if (buffer.MemSize.ToInt64() != size) Error("Mem.Size!=input size"); Output("Mem.MemType=" + buffer.MemType); if (buffer.MemType != MemObjectType.BUFFER) Error("Mem.MemType!=MemObjectType.BUFFER"); Output("Mem.MapCount=" + buffer.MapCount); if (buffer.MapCount != 0) Error("Mem.MapCount!=0"); Kernel k = kernelDictionary["TestWriteMemory"]; k.SetArg(0, buffer); k.SetArg(1, (IntPtr)size); cq.EnqueueTask(k); cq.EnqueueBarrier(); } } private void TestDevice( Device d ) { Output("Testing device: " + d.Name); // d.ToString() is overloaded to output all properties as a string, so every property will be used that way Output(d.ToString()); } private void TestCommandQueue(Context c, CommandQueue cq ) { string programName = @"MemoryTests.cl"; Output("Testing compilation of: " + programName); OpenCLNet.Program p0 = c.CreateProgramWithSource(File.ReadAllLines(programName)); OpenCLNet.Program p = c.CreateProgramWithSource(File.ReadAllText(programName)); p0.Build(); p.Build(); Kernel k = p.CreateKernel(@"LoopAndDoNothing"); TestCommandQueueMemCopy(c, cq); TestCommandQueueAsync(c, cq, k ); } #region TestCommandQueue helper functions private void TestCommandQueueAsync(Context c, CommandQueue cq, Kernel kernel ) { List events = new List(); Event clEvent; Output("Testing asynchronous task issuing (clEnqueueTask) and waiting for events"); // Issue a bunch of slow operations kernel.SetArg(0, 5000000); for (int i = 0; i < 10; i++) { cq.EnqueueTask(kernel, 0, null, out clEvent); events.Add(clEvent); } // Issue a bunch of fast operations kernel.SetArg(0, 500); for (int i = 0; i < 1000; i++) { cq.EnqueueTask(kernel, 0, null, out clEvent); events.Add(clEvent); } Event[] eventList = events.ToArray(); cq.EnqueueWaitForEvents(eventList.Length, eventList); while (events.Count > 0) { events[0].Dispose(); events.RemoveAt(0); } } private void TestCommandQueueMemCopy(Context c, CommandQueue cq) { AlignedArrayFloat aafSrc = new AlignedArrayFloat(1024 * 1024, 64); AlignedArrayFloat aafDst = new AlignedArrayFloat(1024 * 1024, 64); SetAAF(aafSrc, 0.0f); SetAAF(aafDst, 1.0f); /// Test HOST_PTR -> HOST_PTR copy /// The call to EnqueueMapBuffer synchronizes caches before testing the result Output("Testing synchronous host memory->memory copy(clEnqueueCopyBuffer)"); using (Mem memSrc = c.CreateBuffer(MemFlags.USE_HOST_PTR, aafSrc.ByteLength, aafSrc)) { using (Mem memDst = c.CreateBuffer(MemFlags.USE_HOST_PTR, aafDst.ByteLength, aafDst)) { cq.EnqueueCopyBuffer(memSrc, memDst, IntPtr.Zero, IntPtr.Zero, (IntPtr)aafSrc.ByteLength); cq.EnqueueBarrier(); IntPtr mappedPtr = cq.EnqueueMapBuffer(memDst, true, MapFlags.READ, (IntPtr)0, (IntPtr)aafDst.ByteLength); if (!TestAAF(aafDst, 0.0f)) Error("EnqueueCopyBuffer failed, destination is invalid"); cq.EnqueueUnmapMemObject(memDst, mappedPtr); cq.EnqueueBarrier(); } } /// Test COPY_HOST_PTR -> COPY_HOST_PTR copy /// Verify that original source buffers are intact and that the copy was successful Output("Testing synchronous host memory->memory copy(clEnqueueCopyBuffer)"); SetAAF(aafSrc, 0.0f); SetAAF(aafDst, 1.0f); using (Mem memSrc = c.CreateBuffer(MemFlags.COPY_HOST_PTR, aafSrc.ByteLength, aafSrc)) { using (Mem memDst = c.CreateBuffer(MemFlags.COPY_HOST_PTR, aafSrc.ByteLength, aafDst)) { SetAAF(aafSrc, 2.0f); SetAAF(aafDst, 3.0f); cq.EnqueueCopyBuffer(memSrc, memDst, IntPtr.Zero, IntPtr.Zero, (IntPtr)aafSrc.ByteLength); cq.Finish(); if (!TestAAF(aafSrc, 2.0f)) Error("Memory copy destroyed src buffer"); if (!TestAAF(aafDst, 3.0f)) Error("Memory copy destroyed dst buffer"); cq.EnqueueReadBuffer(memDst, true, IntPtr.Zero, (IntPtr)aafDst.ByteLength, aafDst); cq.Finish(); if (!TestAAF(aafDst, 0.0f)) Error("Memory copy failed"); } } /// Test ALLOC_HOST_PTR -> ALLOC_HOST_PTR copy Output("Testing synchronous host memory->memory copy(clEnqueueCopyBuffer)"); SetAAF(aafSrc, 0.0f); SetAAF(aafDst, 1.0f); using (Mem memSrc = c.CreateBuffer((MemFlags)((ulong)MemFlags.ALLOC_HOST_PTR + (ulong)MemFlags.READ_WRITE), aafSrc.ByteLength, IntPtr.Zero)) { using (Mem memDst = c.CreateBuffer((MemFlags)((ulong)MemFlags.ALLOC_HOST_PTR + (ulong)MemFlags.WRITE_ONLY), aafSrc.ByteLength, IntPtr.Zero)) { cq.EnqueueWriteBuffer(memSrc, false, (IntPtr)0, (IntPtr)aafSrc.ByteLength, aafSrc); cq.EnqueueWriteBuffer(memDst, false, (IntPtr)0, (IntPtr)aafSrc.ByteLength, aafSrc); cq.EnqueueBarrier(); cq.EnqueueCopyBuffer(memSrc, memDst, IntPtr.Zero, IntPtr.Zero, (IntPtr)aafSrc.ByteLength); cq.EnqueueBarrier(); cq.EnqueueReadBuffer(memDst, true, IntPtr.Zero, (IntPtr)aafDst.ByteLength, aafDst); if (!TestAAF(aafDst, 0.0f)) Error("Memory copy failed"); } } /// Test DEFAULT -> DEFAULT copy Output("Testing synchronous host memory->memory copy(clEnqueueCopyBuffer)"); SetAAF(aafSrc, 0.0f); SetAAF(aafDst, 1.0f); using (Mem memSrc = c.CreateBuffer((MemFlags)((ulong)MemFlags.ALLOC_HOST_PTR + (ulong)MemFlags.READ_ONLY), aafSrc.ByteLength, IntPtr.Zero)) { using (Mem memDst = c.CreateBuffer((MemFlags)((ulong)MemFlags.ALLOC_HOST_PTR + (ulong)MemFlags.WRITE_ONLY), aafSrc.ByteLength, IntPtr.Zero)) { cq.EnqueueWriteBuffer(memSrc, false, (IntPtr)0, (IntPtr)aafSrc.ByteLength, aafSrc); cq.EnqueueWriteBuffer(memDst, false, (IntPtr)0, (IntPtr)aafSrc.ByteLength, aafSrc); cq.EnqueueBarrier(); cq.EnqueueCopyBuffer(memSrc, memDst, IntPtr.Zero, IntPtr.Zero, (IntPtr)aafSrc.ByteLength); cq.EnqueueBarrier(); cq.EnqueueReadBuffer(memDst, true, IntPtr.Zero, (IntPtr)aafDst.ByteLength, aafDst); if (!TestAAF(aafDst, 0.0f)) Error("Memory copy failed"); } } } private bool TestAAF(AlignedArrayFloat aaf, float c) { for (int i = 0; i < aaf.Length; i++) if (aaf[i] != c) return false; return true; } private void SetAAF(AlignedArrayFloat aaf, float c) { for (int i = 0; i < aaf.Length; i++) aaf[i] = c; } private void SetAAFLinear(AlignedArrayFloat aaf) { for (int i = 0; i < aaf.Length; i++) aaf[i] = (float)i; } #endregion private void Output(string s) { listBoxOutput.Items.Add(s); Application.DoEvents(); } private void Warning(string s) { listBoxWarnings.Items.Add(s); Application.DoEvents(); } private void Error(string s) { listBoxErrors.Items.Add(s); Application.DoEvents(); } } }