当前位置:首页 > 每日看点 > 正文内容

为什么c#能做大型游戏,而java不适合,c#跟java不是很相似吗?

卡卷网10个月前 (12-29)每日看点141

就说个编写 3D 游戏里最常见的场景吧。

现在我想要渲染一个 3D 模型,需要将组成模型的面传递给游戏引擎,然后引擎会去调用 DirectX/Vulkan/OpenGL 等等的 API 将我们模型数据提供给 GPU 渲染出来。

我们都知道 3D 模型的面由三角形组成,而每个三角形都有 3 个顶点,最后一大堆三角形组成了整个模型的表面。

那这件事情要怎么做呢?最简单的方法:整一个缓冲区把三角形的各顶点的位置存起来,然后调用图形 API 把数据上传到 GPU 就行了。

那这件事情在 C# 里面怎么做呢?

首先定义顶点类型:

[StructLayout(LayoutKind.Sequential)] record struct Vertex(float X, float Y, float Z);

然后我们整个顶点数组:

var vertices = new Vertex[12345];

然后把我们的三角形扔进去之后直接扔给引擎就完事了。

填充的时候简单 for 循环里往 vertices 里塞 Vertex 就行了。

假设引擎用的 OpenGL,那此时引擎接收到我们的数组后需要调用 glBufferData 来给 VAO 提供顶点数据:

const uint GL_ARRAY_BUFFER = 0x8892; const uint GL_STATIC_DRAW = 0x88E4; fixed (Vertex* p = vertices) glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.Length, p, GL_STATIC_DRAW);

glBufferData 这个函数要怎么在 C# 里定义呢?

[DllImport("glew32.dll", EntryPoint = "__glewBufferData"), SuppressGCTransition] static extern void glBufferData(uint target, nint size, void* data, uint usage);

搞定。

至此为止我没有用到任何的(除了 OpenGL 本身之外的)库,也没有编写任何的 C# 以外的代码。

那写法这么简单,性能怎么样呢?我们看看最终调用 glBufferData 的部分 JIT 给我们生成了什么代码:

add rax, 16 ; address of vertices + method table (8) + array length (8) mov r8, rax ; = data of vertices mov edx, 0x242AC ; sizeof(Vertex) * vertices.Length mov ecx, 0x8892 ; GL_ARRAY_BUFFER mov r9d, 0x88E4 ; GL_STATIC_DRAW mov eax, 0x5B589850 ; function pointer of glBufferData call rax ; call glBufferData

可以看到,没有任何额外的内存分配和开销,数据直接从指针传出去,压根不需要拷贝,从头到尾的零成本抽象。

甚至可以说 C# 就是一个可以用 GC 的现代版 C++,这对于需要频繁与 native 进行交互的场景而言最适合不过。更不要提 C# 的 P/Invoke 在 NativeAOT 之下支持静态链接,连额外的 dll 都省了。


以上东西你用 Java 做一个试试,看看能有多复杂,再看看填充 buffer 的时候能否直接利用定义好的数据结构和类型,再看看能不能做到零拷贝。


更新:

鉴于 Java 22 之后有了 FFM API,以上东西虽然做起来很麻烦,但是好歹能写出来了,虽然我不知道他们现在的 FFM API 打算如何支持静态链接,毕竟 Java FFM API 这种运行时定义布局和查找入口点跟静态链接就是天生不兼容的。


那我们现在进一步加大难度。

我们都知道给 GPU 送的数据不一定非得是 float ,还可以是 intdouble 等等。那此时在 C# 里面我们只需要把顶点类型改成:

[StructLayout(LayoutKind.Sequential)] record struct Vertex<T>(T X, T Y, T Z) where T : unmanaged;

然后调用 glBufferData 那句改成:

fixed (Vertex<T>* p = vertices) glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex<T>) * vertices.Length, p, GL_STATIC_DRAW);

就搞定了。

由于 .NET 会针对泛型参数给代码进行特化,因此 T 无论是 intfloat 还是 double,甚至是更复杂的类型例如 record struct Foo<U>(Bar A, float B, U C) 都能获得独一无二的代码生成,并且其内存布局也是编译时就确定的,效率也有保证。

扫描二维码推送至手机访问。

版权声明:本文由卡卷网发布,如需转载请注明出处。

本文链接:https://www.kajuan.net/ttnews/2024/12/5894.html

分享给朋友:

相关文章

200+ 工具,这个网站堪称最强

200+ 工具,这个网站堪称最强

作为一个乱七八糟什么都懂一点的电脑小白,日常经常被别人增加些临时任务,比如PDF转Word,提取视频里的音频,图片加个边框。之前,电脑里安装了不少这类的软件,虽然能满足要求,但装的多了,总感觉电脑卡卡的。最近发现了一个超强的在线工具网站,首...

国内比较好用的快速开发平台有哪些?

国内比较好用的快速开发平台有哪些?

低代码/零代码快速开发平台,一直是近年来很热的一个词,各大巨头的纷纷加入也给这个赛道。题主需要一个能二次开发的低代码快速开发平台,可以试试JVS低代码(私有化)。JVS低代码前端采用Vuex框架和Element-ui组件库,后端则基于Spr...

为什么我们一直在给B站充大会员但B站却一直处于亏损状态?

我讲一下离谱的真相吧,欢迎喷我。很多人都觉得B站在赶走人才,赶走优秀的长视频创作者,也觉得知乎在这么做,是没错的。确实在这么做。而原因很简单。只有影响力很大的KOL才有商业价值。(KOL是指“关键意见领袖”)而你说你是人才?对不起,人才不赚...

马云也搞不明白:为什么现在用户偏爱微信支付,而不是支付宝?

这题我会,我教马云一招。你直接把你那破比支付宝的代码全删了,重新写一个。打开支付宝直接就是一个大大的支付码,然后右上角按一下就是扫一扫。你要是还想保留你的其他那些乱七八糟的功能,麻烦将他们全部做到下拉菜单里。你这么设计我不说你能干死微信,但...

数字人民币为什么又不火了?

我完全不懂行。我一开始以为,数字人民币,是对我银行里的每一分钱,都赋予一个独一无二的数字编码。我要用一块钱买矿泉水,它就从我的存款中随机选取一百个一分钱,组合成一块钱,支付给商家。我花一百块钱吃饭,它就随机选取一万个一分钱,组合成一百块钱,...

是什么原因让你一定要用 iPhone?

是什么原因让你一定要用 iPhone?

1、不用清理内存,iPhone真的可以开机用上一年,你根本不用关注期间打开过什么软件。2、支持的频段足够多,天天在国内走动倒是无所谓,很多Android手机一旦出去了,就会发现很多运营商接入都是问题,更不用说各种本土化的服务了。3、屏幕素质...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。