Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >谈谈跨平台图形API的抽象

谈谈跨平台图形API的抽象

作者头像
重归混沌
发布于 2022-11-11 01:40:09
发布于 2022-11-11 01:40:09
60100
代码可运行
举报
文章被收录于专栏:重归混沌重归混沌
运行总次数:0
代码可运行

本来按3月份的计划,是先把王者荣耀基本模式抄完 ,并以此为基础来抽象出一套基于Lua的通用客户端框架,然后根据需求再慢慢优化。

但是,3月底GAMES系列又出了一个新课GAMES104,《现代游戏引擎:从入门到实践》。

这门课一下子燃爆了我的兴趣,于是我决定暂停客户端框架的开发计划。学完GAMES104之后再回来继续开发客户端框架。

经过这几年的观察。我发现由于算力的缘故,很多高级的技术总是选应用于端游,然后再过很多年。才被用于手游开发(有时甚至还需要各种Trick才能跑得起来)。所以,要想学习和体验最新的引擎技术,最好还是通过端游引擎。

我打算趁着这次GAMES104的课程,写一个自己的引擎。

这个引擎应该使用最新的技术和最新的硬件特性。

这个引擎的业务逻辑语言为Lua。从表现力上讲,Lua要比C和C++强不少,虽然性能会慢一点,但是因为是实验性质的引擎,开发快反而会更重要。

这个引擎应该是跨平台的。虽然我的主要目标是端游,但是我也希望像在手机算力允许的情况下,可以在手机上玩耍。

我花了一周时间把vulkan教程上的例子抄了一遍(画一一个三角形,我竟然抄了3天半 ^_^!)。

然后就开始根据GAMES104的视频课程实现引擎了。

虽然第一版引擎以Vulkan图形API为基础,但是我还是希望能先抽象的个差不多的RHI(Render Hardware Interface), 为未来支持Direct3D和Metal打下基础。

这对我来讲很难,因为我没有任何Direct3D和Metal的基础,连Vulkan也只有一个星期的经验。

我还是想试一下。


一个最容易想到的方案是,为所有图形API设计相同的接口和相同的导出结构,然后使用宏来切换平台,这也正是RHI的表面含义.

伪码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//-----rhi/texture.h------
namespace rhi {
    gpu_handle texture_create();
    texture_destroy(gpu_handle handle);
}
//-----rhi/texture.cpp-------
#ifdef RHI_VULKAN
#include "vulkan/texture.cpp"
#elseif RHI_DIRECT3D
#include "direct3d/texture.cpp"
#elseif RHI_METAL
#include "metal/texture.cpp"
#endif
//-----render/texture2d.h
namespace render {
    class texture2d {
    public:
        texture2d() {
            gpu_texture = texture_create();
        }
        ~texture2d() {
            texture_destroy(gpu_texture);
        }
    private:
        gpu_handle gpu_texture;
        int width;
        int height;
    }
}COPY

但是这么做会有一些令人纠结的问题。

以texture2d为例,在Vulkan层面去使用texture时,部分情况是需要用到width和height,write_enable,filter_mode等属性,这时需要如何去获取这些属性。

这时有三种方案:

  • 第一种方案:在调用rhi::texture_create()时把所有需要用到的参数都传递过去,然后Vulkan层在内部保存供后面使用。这样做有两个坏处:数据冗余严重、需要额外的代码来将texture2d和gpu_texture之间的属性进行同步。
  • 第二种方案:在调用rhi::texture_create()时,直接把texture2d的this指针传递进去,Vulkan层在内部将gpu_texture和this进行绑定。Vulkan层在内部操作gpu_texture时可以通过这种绑定关系,查询到texture2d的指针,并读取相关设置信息。这么做同样也有坏处,首先是会产生循环引用,在render层textuer_2d引用了gpu_texture, 在vulkan层gpu_texture又引用了texture2d,然后是,因为rhi::texture_create的参数有了类型,那么就需要为每一种texture(textur2d, texture3d, cubemap)等添加一个texture_create/texture_destroy接口。
  • 第三种方案:在第二种方案的基础上,可以通过去掉gpu_handle的存在,来切断循环引用。

伪码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//-----rhi/texture.h------
namespace rhi {
    bool texture_create(texture2d *tex);
    void texture_destroy(texture2d *tex);
}
//-----rhi/texture.cpp-------
#ifdef RHI_VULKAN
#include "vulkan/texture.cpp"
#elseif RHI_DIRECT3D
#include "direct3d/texture.cpp"
#elseif RHI_METAL
#include "metal/texture.cpp"
#endif
//-----render/texture2d.h
namespace render {
    class texture2d {
    public:
        texture2d() {
            rhi::texture_create(this);
        }
        ~texture2d() {
            rhi::texture_destroy(this);
        }
    private:
        int width;
        int height;
    }
}COPY

当调用rhi::texture_create时,Vulkan层会创建一个texture的GPU资源,并这份GPU资源和texture2d指针进行绑定,但是这种绑定并不导出到外部接口使用。

后续操作某个GPU资源时,直接使用texture2d指针即可。

至于绑定方式,可以有多种多样,最简单直接方式就是使用unordered_map(显然性能并不会太高)。

第三种方案和第二种方案有一个通病,就是一个texture2d资源同时需要至少两个对象来表示,render层的texture2d和vulkan层的gpu_texture2d, 这会造成内存碎片问题。


花了2周时间反复重构了多次,都不太满意。

在重构过程中,我想到了一个全新的思路。

伪码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//-----render/texture2d.h
namespace render {
    class texture2d {
    pubilc:
    static texture2d *create(int width, int height);
    static void destroy(texture2d *tex);
    protected:
        texture2d() {}
        ~texture2d() {}
    protected:
        gpu_handle gpu_texture;
        int width;
        int height;
    }
}

//-----vulkan/vk_texture2d.h
namespace vulkan {
    class vk_texture2d : public render::texture2d {
    public:
        vk_texture2d(int width, int height) : texture2d() {
            //todo some gpu create
        }
        ~vk_texture2d() {
            ~texture2d()
            //release some gpu resource
        }
    private:
        //some GPU-related resource
    }
}

//-----vulkan/vk_factory.h
namespace render {
    texture2d *texture2d::create(int width, int height) 
    {
        return new vk_texture2d(width, height);
    }
    void texture2d::destroy(texture2d *tex)
    {
        delete (vk_texture2d *)tex;
    }
}COPY

在这次抽象中,我彻底去掉了RHI相关的所有中间层。而且几乎解决了上述方案中所有的缺点:内存碎片不存在了,循环引用没有了,GPU和CPU端数据的粘合逻辑消失了。

当然,这套抽象也有他自己的缺点:

  • 所有的渲染对象不能再使用new来创建,只能使用create/destroy来创建和销毁。
  • vulkan层中的对象之间不能再继承, 比如vk_texture2d本来可以继承自vk_texture的

但是,相比他能解决的问题,我觉得这两个问题都不算是大问题。

业务逻辑是使用Lua来做,所以本来也不会用到new来创建渲染对象。

少使用乃至不使用继承更是我一惯的坚持原则。

最后, 完整代码附上

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-09-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 重归混沌 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
重新抽象图形API
但是,在QQ群里无意间看到大佬们聊起来bindless, 然后去查了查资料,发现bindless性能又好,抽象又好做,于是果断入bindless的坑。
重归混沌
2022/11/11
4440
SDL2来源分析3:渲染(SDL_Renderer)
=====================================================
全栈程序员站长
2022/07/06
3.5K0
SDL2来源分析3:渲染(SDL_Renderer)
Unity实现Camera和Audio数据的低延迟RTMP推送技术探讨
关于Unity实现RTMP直播推送技术方案,之前零散的写过几篇介绍,得到了好多开发者的关注。以Android平台为例,目前视频这块,我们demo实现的是Camera数据的采集,然后编码投递到底层,如果设备没有性能瓶颈,可达到高帧率(60帧)均匀的RTMP推送效果。
音视频牛哥
2022/09/03
6570
用 Vulkan 渲染写一个 Android GPUImage
说的 GPUImage 相信大家都不陌生,GPUImage 是做滤镜、渲染、特效最主流的框架之一,被广泛应用在短视频应用中。
音视频开发进阶
2021/03/13
8380
Unity3D下如何采集camera场景数据并推送RTMP服务?
Unity3D是非常流行的游戏开发引擎,可以创建各种类型的3D和2D游戏或其他互动应用程序。常见使用场景如下:
音视频牛哥
2023/09/01
6560
Unity3D下如何采集camera场景数据并推送RTMP服务?
Games will be better on weaker hardware on Android 10
ANGLE can let you run cool games on a cheap phone. That's all you really need to know.
用户9732312
2022/05/13
3260
UE4/UE5的RHI(Vulkan为例)
RHI是Render Hardware Interface的缩写,虚幻引擎通过RHI把各个平台的图形API包装成统一接口,供上层渲染来使用,让业务不用过多的关注API细节(实际还得关注RHI细节)。从代码结构上来看,RHI封装的比较贴合于现代的图形API(vulkan, metal, DX12),也支持opengl/opengles。这个接口是广义上的概念,不仅指C++的纯虚基类,也包括一些全局变量,全局函数等,具体形式就像下面RHI.h头文件这样:
quabqi
2021/11/04
6.8K0
UE4/UE5的RHI(Vulkan为例)
播放视频时如何在视频帧上添加水印
之前的一篇文章中我们介绍了播放视频的时候调整音频的音量,我们能否在播放视频的时候在视频画面上加上水印?
马上就说
2020/11/11
3.5K0
【100个 Unity实用技能】| Unity将本地图片文件显示到Image组件中 通用方法整理
本文总结了两种将本地图片文件显示到Image组件中 的两种方法,下面一起来看一下吧!
呆呆敲代码的小Y
2022/11/20
2.8K0
【100个 Unity实用技能】| Unity将本地图片文件显示到Image组件中 通用方法整理
将 Direct3D11 在 GPU 中的纹理(Texture2D)导出到内存(Map)或导出成图片文件
Direct3D11 的使用通常不是应用程序唯一的部分,于是使用 Direct3D11 的代码如何与其他模块正确地组合在一起就是一个需要解决的问题。
walterlv
2023/10/22
1.5K0
[ZZ] cbuffer和tbuffer
http://blog.chinaunix.net/uid-20235103-id-2578297.html
全栈程序员站长
2021/07/13
9260
DAY18:阅读纹理内存之Layered Textures
3.2.11.1.3. 16-Bit Floating-Point Textures The 16-bit floating-point or half format supported by CUDA arrays is the same as the IEEE 754-2008 binary2 format. CUDA C does not support a matching data type, but provides intrinsic functions【内联函数】 to convert
GPUS Lady
2018/06/22
9820
使用 Direct3D11 的 OpenSharedResource 方法渲染来自其他进程/设备的共享资源(SharedHandle)
如果你得到了一个来自于其他进程或者其他模块的 Direct3D11 的共享资源,即 SharedHandle 句柄,那么可以使用本文提到的方法将其转换成 Direct3D11 的设备和纹理,这样你可以进行后续的其他处理。
walterlv
2023/10/22
7310
D3D API – D3DXCreateRenderToSurface渲染到纹理
//创建纹理对象 if( FAILED( D3DXCreateTextureFromFile( g_pd3dDevice, L”shitoub01.jpg”, &g_pTexture ) ) ) { MessageBox(NULL, L”创建纹理失败”, L”Texture.exe”, MB_OK); return E_FAIL; }
全栈程序员站长
2022/07/05
6830
C# 从零开始写 SharpDx 应用 初始化dx修改颜色
本文来告诉大家如何在上一篇博客创建的窗口里面使用 Sharpdx 初始化,然后设置窗口颜色。
林德熙
2018/09/19
1.7K0
C# 从零开始写 SharpDx 应用 初始化dx修改颜色
Direct3D 11 Tutorial 1: Basics_Direct3D 11 教程1:基础
在这第一篇教程中,我们将通过介绍创建最小Direct3D应用程序所必需的元素。每一个Direct3D应用程序必需拥有这些元素才能正常地工作。这些元素包括设置窗口和设备对象,以及在窗口上显示颜色。
Zoctopus
2018/10/10
1.8K0
Android平台实现Unity3D下RTMP推送
像Unity3D下的RTMP或RTSP播放器一样,好多开发者苦于在Unity环境下,如何高效率低延迟的把数据采集并编码实时推送到流媒体服务器,实现Unity场景下的低延迟推拉流方案。
音视频牛哥
2021/06/08
1K0
Android平台实现Unity3D下RTMP推送
C# 从零开始写 SharpDx 应用 绘制基础图形
上面创建的代码大部分参阅了C# 从零开始写 SharpDx 应用 初始化dx修改颜色的代码
林德熙
2022/08/04
2.6K1
原创Paper | DirectX Hook - 优雅的实现游戏辅助窗口
最近看到了一个github的项目,分析过后觉得里面无论是代码还是界面都很好看,然后开始研究其代码。
Seebug漏洞平台
2023/01/08
5.2K2
原创Paper | DirectX Hook - 优雅的实现游戏辅助窗口
多平台游戏模拟器、游戏启动器和游戏引擎 | 开源专题 No.98
HeroicGamesLauncher 是一个原生的 GOG、Amazon 和 Epic Games 的游戏启动器,支持 Linux、Windows 和 Mac。主要功能和优势包括:
小柒
2024/07/12
3280
多平台游戏模拟器、游戏启动器和游戏引擎 | 开源专题 No.98
推荐阅读
相关推荐
重新抽象图形API
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验