CSharpGL(36)通用的非托管数组排序方法
CSharpGL(36)通用的非托管数组排序方法
如果OpenGL要渲染半透明物体,一个方法是根据顶点到窗口的距离排序,按照从远到近的顺序依次渲染。所以本篇介绍对 UnmanagedArray<T> 进行排序的几种方法。
UnmanagedArray<T>
首先重新介绍一下非托管数组这个东西。一个 UnmanagedArray<float> 与一个 float[] 是一样的用处,只不过 UnmanagedArray<float> 是用 Marshal.AllocHGlobal(memSize); 在非托管内存上申请的空间。
namespace CSharpGL
{
/// <summary>
/// unmanaged huge array.
/// <para>Check http://www.cnblogs.com/bitzhuwei/p/huge-unmanged-array-in-csharp.html </para>
/// </summary>
/// <typeparam name="T">sbyte, byte, char, short, ushort, int, uint, long, ulong, float, double, decimal, bool or other struct types. enum not supported.</typeparam>
public sealed unsafe class UnmanagedArray<T> : UnmanagedArrayBase where T : struct
{
public UnmanagedArray(int count)
: base(count, Marshal.SizeOf(typeof(T)))
{
}
} /// <summary>
/// Base type of unmanaged array.
/// <para>Similar to array in <code>int array[Length];</code></para>
/// </summary>
public abstract class UnmanagedArrayBase : IDisposable
{
/// <summary>
/// 此非托管数组中的数据在内存中的起始地址
/// Start position of array; Head of array; first element's position of array.
/// <para>Similar to <code>array</code> in <code>int array[Length];</code></para>
/// </summary>
public IntPtr Header { get; private set; } /// <summary>
/// How many elements?
/// <para>Similar to <code>Length</code> in <code>int array[Length];</code></para>
/// </summary>
public int Length { get; private set; } /// <summary>
/// 单个元素的字节数。
/// <para>How manay bytes for one element of array?</para>
/// </summary>
protected int elementSize; /// <summary>
/// 申请到的字节数。(元素的总数 * 单个元素的字节数)。
/// <para>How many bytes for total array?</para>
/// <para>Length * elementSize</para>
/// </summary>
public int ByteLength
{
get { return (this.Length * this.elementSize); }
} protected UnmanagedArrayBase(int elementCount, int elementSize)
{
this.Length = elementCount;
this.elementSize = elementSize;
int memSize = this.Length * this.elementSize;
this.Header = Marshal.AllocHGlobal(memSize);
}
}
}
UnmanagedArray<T>
UnmanagedArray<float>
下面我们以 UnmanagedArray<float> 为例,实现对非托管数组的排序。(下面是快速排序算法)
public static void Sort(this UnmanagedArray<float> array, bool descending)
{
QuickSort(array, , array.Length - , descending);
} public static void Sort(UnmanagedArray<float> array, int start, int length, bool descending)
{
QuickSort(array, start, start + length - , descending);
} private static void QuickSort(UnmanagedArray<float> array, int start, int end, bool descending)
{
if (start >= end) { return; } Stack<int> stack = new Stack<int>();
stack.Push(end);
stack.Push(start);
QuickSort(array, descending, stack);
} private static void QuickSort(UnmanagedArray<float> array, bool descending, Stack<int> stack)
{
while (stack.Count > )
{
int start = stack.Pop();
int end = stack.Pop();
int index = QuickSortPartion(array, start, end, descending);
if (start < index - )
{
stack.Push(index - ); stack.Push(start);
}
if (index + < end)
{
stack.Push(end); stack.Push(index + );
}
}
} private static unsafe int QuickSortPartion(UnmanagedArray<float> array, int start, int end, bool descending)
{
float* pointer = (float*)array.Header.ToPointer();
float pivot, startValue, endValue;
pivot = pointer[start];
while (start < end)
{
startValue = pointer[start];
while ((start < end)
&& ((descending && (startValue.CompareTo(pivot) > ))
|| (!descending) && (startValue.CompareTo(pivot) < )))
{
start++;
startValue = pointer[start];
} endValue = pointer[end];
while ((start < end)
&& ((descending && (endValue.CompareTo(pivot) < ))
|| (!descending) && (endValue.CompareTo(pivot) > )))
{
end--;
endValue = pointer[end];
} if (start < end)
{
pointer[end] = startValue;
pointer[start] = endValue;
}
} return start;
}
public static void Sort(this UnmanagedArray array, bool descending)
原本我以为把这个方法改写一下,用 T代替具体的 float ,就可以实现对任意 T where T : struct,IComparable<T> 的排序,但是VS报了这样的编译错误:无法获取托管类型(“T”)的地址和大小,或无法声明指向它的指针。
看来C#不允许将泛型T作为unsafe里的数组的类型。那么只能用下面的几种方式变通一下。
Marshal
Marshal 有这样2个方法:
// 从非托管数组中读取一个元素
public static object PtrToStructure(IntPtr ptr, Type structureType);
// 向非托管数组写入一个元素
public static void StructureToPtr(object structure, IntPtr ptr, bool fDeleteOld);
用这2个方法就可以实现对非托管数组的读写操作。于是泛型版本( UnmagedArray<T> )的排序算法就实现了。
public static void Sort<T>(this UnmanagedArray<T> array, bool descending) where T : struct, IComparable<T>
{
QuickSort(array, , array.Length - , descending);
} public static void Sort<T>(this UnmanagedArray<T> array, int start, int length, bool descending) where T : struct, IComparable<T>
{
QuickSort(array, start, start + length - , descending);
} private static void QuickSort<T>(UnmanagedArray<T> array, int start, int end, bool descending) where T : struct, IComparable<T>
{
if (start >= end) { return; } var stack = new Stack<int>();
stack.Push(end);
stack.Push(start);
QuickSort(array, descending, stack);
} private static void QuickSort<T>(UnmanagedArray<T> array, bool descending, Stack<int> stack) where T : struct, IComparable<T>
{
IntPtr pointer = array.Header;
Type type = typeof(T);
int elementSize = Marshal.SizeOf(type); while (stack.Count > )
{
int start = stack.Pop();
int end = stack.Pop();
int index = QuickSortPartion(array, start, end, descending, type, elementSize);
if (start < index - )
{
stack.Push(index - ); stack.Push(start);
}
if (index + < end)
{
stack.Push(end); stack.Push(index + );
}
}
} private static int QuickSortPartion<T>(UnmanagedArray<T> array, int start, int end, bool descending, Type type, int elementSize) where T : struct, IComparable<T>
{
IntPtr pointer = array.Header;
IntPtr pivotIndex, startIndex, endIndex;
T pivot, startValue, endValue;
pivotIndex = new IntPtr((int)pointer + start * elementSize);
pivot = (T)Marshal.PtrToStructure(pivotIndex, type);
while (start < end)
{
startIndex = new IntPtr((int)pointer + start * elementSize);
startValue = (T)Marshal.PtrToStructure(startIndex, type);
while ((start < end)
&& ((descending && (startValue.CompareTo(pivot) > ))
|| ((!descending) && (startValue.CompareTo(pivot) < ))))
{
start++;
startIndex = new IntPtr((int)pointer + start * elementSize);
startValue = (T)Marshal.PtrToStructure(startIndex, type);
} endIndex = new IntPtr((int)pointer + end * elementSize);
endValue = (T)Marshal.PtrToStructure(endIndex, type);
while ((start < end)
&& ((descending && (endValue.CompareTo(pivot) < ))
|| ((!descending) && (endValue.CompareTo(pivot) > ))))
{
end--;
endIndex = new IntPtr((int)pointer + end * elementSize);
endValue = (T)Marshal.PtrToStructure(endIndex, type);
} if (start < end)
{
Marshal.StructureToPtr(endValue, startIndex, true);
Marshal.StructureToPtr(startValue, endIndex, true);
}
} return start;
}
public static void Sort(this UnmanagedArray array, bool descending) where T : struct, IComparable
虽然可用,但是用Marshal读写非托管数组效率比较低(使用了装箱拆箱操作)。于是我有了下面这个思路。
CodeProvider
我们知道C#现有的类库是支持动态生成和调用C#代码的。因此,我可以在调用 Sort<T>(this UnmanagedArray<T> array, bool descending) 时,临时为具体的T生成一个排序方法,然后去调用这个方法。这就不需要使用 Marshal 了。
具体步骤如下。
准备模板
为了便于编码、维护,我们先准备一个排序代码的模板。模板里的TemplateStructType纯属一个占位符,在使用的时候我们先用具体的类型把它替换掉,就成了我们需要的源代码。
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices; namespace CSharpGL
{
/// <summary>
/// Helper class for sorting unmanaged array.
/// </summary>
public static partial class SortingHelper
{
///// <summary>
///// Sort unmanaged array specified with <paramref name="array"/> at specified area.
///// </summary>
///// <param name="array"></param>
///// <param name="descending">true for descending sort; otherwise false.</param>
//public static void Sort(this UnmanagedArray<TemplateStructType> array, bool descending)
//{
// QuickSort(array, 0, array.Length - 1, descending);
//} /// <summary>
/// Sort unmanaged array specified with <paramref name="array"/> at specified area.
/// </summary>
/// <param name="array"></param>
/// <param name="start">index of first value to be sorted.</param>
/// <param name="length">length of <paramref name="array"/> to bo sorted.</param>
/// <param name="descending">true for descending sort; otherwise false.</param>
public static void Sort(UnmanagedArray<TemplateStructType> array, int start, int length, bool descending)
{
QuickSort(array, start, start + length - , descending);
} private static void QuickSort(UnmanagedArray<TemplateStructType> array, int start, int end, bool descending)
{
if (start >= end) { return; } Stack<int> stack = new Stack<int>();
stack.Push(end);
stack.Push(start);
QuickSort(array, descending, stack);
} private static void QuickSort(UnmanagedArray<TemplateStructType> array, bool descending, Stack<int> stack)
{
while (stack.Count > )
{
int start = stack.Pop();
int end = stack.Pop();
int index = QuickSortPartion(array, start, end, descending);
if (start < index - )
{
stack.Push(index - ); stack.Push(start);
}
if (index + < end)
{
stack.Push(end); stack.Push(index + );
}
}
} private static unsafe int QuickSortPartion(UnmanagedArray<TemplateStructType> array, int start, int end, bool descending)
{
TemplateStructType* pointer = (TemplateStructType*)array.Header.ToPointer();
TemplateStructType pivot, startValue, endValue;
pivot = pointer[start];
while (start < end)
{
startValue = pointer[start];
while ((start < end)
&& ((descending && (startValue.CompareTo(pivot) > ))
|| (!descending) && (startValue.CompareTo(pivot) < )))
{
start++;
startValue = pointer[start];
} endValue = pointer[end];
while ((start < end)
&& ((descending && (endValue.CompareTo(pivot) < ))
|| (!descending) && (endValue.CompareTo(pivot) > )))
{
end--;
endValue = pointer[end];
} if (start < end)
{
pointer[end] = startValue;
pointer[start] = endValue;
}
} return start;
}
}
}
Teamplate method
嵌入的资源
然后把这个模板文件设置为嵌入的资源,以便于调用。
动态生成和调用代码
利用CSharpCodeProvider来把源代码编译为Assembly并调用。
public static void Sort<T>(this UnmanagedArray<T> array, bool descending) where T : struct, IComparable<T>
{
Type type = typeof(T);
string order = ManifestResourceLoader.LoadTextFile(@"Resources\SortingHelper.Order`1.cs");
order = order.Replace("TemplateStructType", type.FullName);
var codeProvider = new CSharpCodeProvider();
var option = new CompilerParameters();
option.GenerateInMemory = true;
option.CompilerOptions = "/unsafe";
option.ReferencedAssemblies.Add("System.dll");
option.ReferencedAssemblies.Add("CSharpGL.dll");
CompilerResults result = codeProvider.CompileAssemblyFromSource(option,
order);
Assembly asm = result.CompiledAssembly;
Type sortingHelper = asm.GetType("CSharpGL.SortingHelper");
Type unmanagedArrayGeneric = typeof(UnmanagedArray<>);
Type unmanagedArray = unmanagedArrayGeneric.MakeGenericType(type);
MethodInfo method = sortingHelper.GetMethod("Sort", new Type[] { unmanagedArray, typeof(int), typeof(int), typeof(bool) });
method.Invoke(null, new object[] { array, , array.Length, descending });
}
总结
还有一种利用emit的方法,我就暂时不研究了。
CSharpGL(36)通用的非托管数组排序方法的更多相关文章
- 在C#调用C++的DLL方法(一)生成非托管dll
C#与C/C++相比,前者的优势在于UI,后者的优势在于算法,C++下的指针虽然恶心,若使用得当还是相当方便的,最重要的问题是,市面上很多流行的开发工具库,几乎没有不支持C++的,但全面支持C#只能说 ...
- .Net 程序在自定义位置查找托管/非托管 dll 的几种方法
原文:.Net 程序在自定义位置查找托管/非托管 dll 的几种方法 一.自定义托管 dll 程序集的查找位置 目前(.Net4.7)能用的有2种: #define DEFAULT_IMPLEMENT ...
- 将WinForm程序(含多个非托管Dll)合并成一个exe的方法
原文:将WinForm程序(含多个非托管Dll)合并成一个exe的方法 开发程序的时候经常会引用一些第三方的DLL,然后编译生成的exe文件就不能脱离这些DLL独立运行了. ILMerge能把托管dl ...
- 托管和非托管转换新方法:Marshaling Library(zz) 【转】
托管和非托管转换新方法:Marshaling Library(zz) 托管和非托管转换新方法:Marshaling Library(zz) http://hi.baidu.com/superql/bl ...
- 在C#调用C++的DLL简析(一)——生成非托管dll
经过一晚上的折腾,还是下点决心将些许的心得写下来,以免以后重复劳动. C#与C/C++相 比,前者的优势在于UI,后者的优势在于算法,C++下的指针虽然恶心,若使用得当还是相当方便的,最重要的问题是, ...
- (转)C#调用非托管Win 32 DLL
转载学习收藏,原文地址http://www.cnblogs.com/mywebname/articles/2291876.html 背景 在项目过程中,有时候你需要调用非C#编写的DLL文件,尤其在使 ...
- C#的托管与非托管大难点
托管代码与非托管代码 众所周知,我们正常编程所用的高级语言,是无法被计算机识别的.需要先将高级语言翻译为机器语言,才能被机器理解和运行.在标准C/C++中,编译过程是这样的:源代码首先经过预处理器,对 ...
- ASP.NET应用技巧:非托管COM组件的使用
众所周知,asp.net是基于通用语言运行库创建的,也就是所谓的托管执行环境.生成的代码称为托管代码.编译器能够从源代码的描述中产生元数据信息,而运行库又从元数据中获得托管代码的信息.而我们编写的组件 ...
- .NET对象的创建、垃圾回收、非托管资源的手动处理
本篇用来梳理对象的创建.垃圾的回收,以及非托管资源的手动处理. →首先运行应用程序,创建一个Windows进程. →CLR创建一块连续的虚拟地址空间,这个地址空间就是托管堆.而且,这个地址空间最初并没 ...
随机推荐
- html5 canvas常用api总结(三)--图像变换API
canvas的图像变换api,可以帮助我们更加方便的绘画出一些酷炫的效果,也可以用来制作动画.接下来将总结一下canvas的变换方法,文末有一个例子来更加深刻的了解和利用这几个api. 1.画布旋转a ...
- 多线程的通信和同步(Java并发编程的艺术--笔记)
1. 线程间的通信机制 线程之间通信机制有两种: 共享内存.消息传递. 2. Java并发 Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式执行,通信的过程对于程序员来说是完全透 ...
- 开源免费且稳定实用的.NET PDF打印组件itextSharp(.NET组件介绍之八)
在这个.NET组件的介绍系列中,受到了很多园友的支持,一些园友(如:数据之巅. [秦时明月]等等这些大神 )也给我提出了对应的建议,我正在努力去改正,有不足之处还望大家多多包涵.在传播一些简单的知识的 ...
- 【开源毕设】一款精美的家校互动APP分享——爱吖校推 [你关注的,我们才推](持续开源更新3)附高效动态压缩Bitmap
一.写在前面 爱吖校推如同它的名字一样,是一款校园类信息推送交流平台,这么多的家校互动类软件,你选择了我,这是我的幸运.从第一次在博客园上写博客到现在,我一次一次地提高博文的质量和代码的可读性,都是为 ...
- TFS 测试用例步骤数据统计
TFS系统集成了一套BI系统,基于SQL Server的Analysis Service进行实现的.通过这几年的深入使用,能够感触到这个数据数据仓库模型是多么的优秀,和微软官方提供的数据仓库示例Adv ...
- [EasyUI美化换肤]更换EasyUi图标
前言 本篇文章主要是记录一些换EasyUI皮肤的过程,备忘.也欢迎美工大神各路UI给点好意见,EasyUI我就不介绍了,自行百度吧..(So..所以别问我是不是响应式..本身EasyUI就不是响应式. ...
- Win10 UWP开发系列——开源控件库:UWPCommunityToolkit
在开发应用的过程中,不可避免的会使用第三方类库.之前用过一个WinRTXamlToolkit.UWP,现在微软官方发布了一个新的开源控件库—— UWPCommunityToolkit 项目代码托管在G ...
- 无法访问org.springframework.core.NestedRuntimeException 找不到org.springframework.core.NestedRuntimeException的类文件
在学习springAOP时,出现如下异常: 无法访问org.springframework.core.NestedRuntimeException 找不到org.springframework.cor ...
- 彻底搞懂Javascript的“==”
本文转载自:@manxisuo的<通过一张简单的图,让你彻底地.永久地搞懂JS的==运算>. 大家知道,==是JavaScript中比较复杂的一个运算符.它的运算规则奇怪,容让人犯错,从而 ...
- KOTLIN开发语言文档(官方文档) -- 2.基本概念
网页链接:https://kotlinlang.org/docs/reference/basic-types.html 2. 基本概念 2.1. 基本类型 从可以在任何变量处理调用成员函数和属性 ...