简体   繁体   English

用C ++(Vulkan)处理C代码

[英]Handing of C code in C++ (Vulkan)

I am trying to write a rendering engine in C++ based on Vulkan. 我试图用基于Vulkan的C ++编写渲染引擎。 Vulkan is written in C, as a result it has some interesting conventions. Vulkan是用C语言编写的,因此它有一些有趣的约定。

A recurring pattern I see in tutorials/code snippets from Vulkan apps is that most code is in 1 very big class. 我在Vulkan应用程序的教程/代码片段中看到的反复出现的模式是,大多数代码都在一个非常大的类中。 (right now my vulkan class is already about 2000 lines too). (现在我的vulkan课程已经大约2000行)。 But to make a proper rendering engine, I will need to compartmentalize my code till some degree. 但是为了制作一个合适的渲染引擎,我需要将代码划分到某种程度。

One of the aforementioned interesting bits is that it has something called a Logical Device, which is an abstract reference to the graphics card. 上述有趣的一点是它有一个叫做逻辑设备的东西,它是对显卡的抽象参考。

It is used everywhere, to create and allocate things in the following way: 它随处可用,以下列方式创建和分配东西:

  1. Create structs with creation info 使用创建信息创建结构
  2. Create variable that the code will output into 创建代码将输出的变量
  3. Call the actual vkCreateSomething or vkAllocateSomething function, pass in the logical device, the creation info and the reference to the variable to output to and check if it was a success. 调用实际的vkCreateSomething或vkAllocateSomething函数,传入逻辑设备,创建信息和对变量的引用以输出并检查它是否成功。

on its own there is nothing wrong with this style I'd say. 就我自己而言,这种风格没有任何问题。 It's just that it's not really handy at all in OOP because it relies on the logical device being available everywhere. 只是它在OOP中根本不是很方便,因为它依赖于随处可用的逻辑设备。

How would I deal with this problem? 我该如何处理这个问题? Service locators and singletons are considered to be horrible solutions by many (which I can understand), so that seems like something I'd rather avoid. 服务定位器和单身人员被许多人认为是可怕的解决方案(我可以理解),所以这似乎是我宁愿避免的。

Are there design patterns that deal with this? 有设计模式可以解决这个问题吗?

The logical device is an actual dependency. 逻辑设备是实际依赖项。

It has state, and its state needs to be available to work with the hardware. 它具有状态,其状态需要可用于硬件。

You can use it as an argument to your operations, a value stored in pretty much every class, a global, or a monadic-esque "final" argument where every operation just returns something still needing the device to run on. 您可以将它用作操作的参数,存储在几乎每个类中的值,全局或monadic-esque“final”参数,其中每个操作只返回仍需要设备运行的内容。 You can replace a (pointer/reference to) it with a function returning a (pointer/reference to) it. 您可以使用返回(指针/引用)函数的函数替换它(指针/引用)。

Consider if pure OOP is what you want to do; 考虑一下纯粹的OOP是否是您想要做的; vulkan and rendering is more about operations than things being operated on. vulkan和渲染更多的是关于操作而不是操作的东西。 I would want to mix some functional programming patterns in, which makes the monad-like choice more reasonable. 我想混合一些函数式编程模式,这使得类似monad的选择更加合理。

Compose operations on buffers/data. 撰写缓冲区/数据的操作。 These return operations, which also take buffers and data. 这些返回操作也包含缓冲区和数据。 The composition operation specifies which arguments are new inputs, and which are consumed by the next step. 组合操作指定哪些参数是新输入,哪些参数是下一步消耗的参数。 Doing this you can (at compile time) set up a type-safe graph of work to do, all without running anything. 这样做你可以(在编译时)设置一个类型安全的工作图表,所有这些都没有运行任何东西。

The resulting composed operation would then have a setup (where you bind the logical device and anything you can do "early" before you need to have the expensive buffers ready), and an execute phase (where you feed it the expensive buffers and it generates output). 然后,由此产生的组合操作将具有一个设置(在您需要准备好昂贵的缓冲区之前,您可以“早期”绑定逻辑设备和任何操作),以及执行阶段(在此处为您提供昂贵的缓冲区并生成它输出)。

Or as another approach, find a compiler with coroutine support from and write it async yet procedurally. 或者作为另一种方法,从找到具有协程支持的编译器,并在程序上将其编写为异步。

Vulkan is a OOP API. Vulkan是一个OOP API。 It is not class-based, because it is C99 not C++. 它不是基于类的,因为它是C99而不是C ++。 That can easily be fixed by using the official Vulkan-Hpp . 使用官方的Vulkan-Hpp可以很容易地解决这个问题 You can consume it as vulkan.hpp which is part of the semi-official LunarG Vulkan SDK . 您可以将它作为vulkan.hpp ,它是半官方LunarG Vulkan SDK的一部分

The usage would not be that different from vulkan.h though: you would probably have a member pointer/reference to a Device instance, or would have a VkDevice handle member in each object that needs it. 用法与vulkan.h没有什么不同:你可能有一个成员指针/对Device实例的引用,或者在每个需要它的对象中都有一个VkDevice句柄成员。 Some higher level object would handle the lifetime of the Logical Device (eg your RenderingEngine class or such). 某些更高级别的对象将处理逻辑设备的生命周期(例如您的RenderingEngine类等)。 The difference would be almost only esthetical: you would use device->command(...) instead of vkCommand(device, ...) . 差异几乎只是美学:你会使用device->command(...)而不是vkCommand(device, ...) vulkan.hpp does not seem to use proper RAII through constructors/destructors which is a shame. vulkan.hpp似乎没有通过构造函数/析构函数使用正确的RAII,这是一种耻辱。

Alternatively the user of your engine can manage the device. 或者,引擎的用户可以管理设备。 Though unlike OpenGL there is not much use for this. 虽然与OpenGL不同,但这并没有多大用处。 The user can make its own VkInstance and VkDevice if it also wishes to use Vulkan for something. 如果用户也希望使用Vulkan,那么用户可以创建自己的VkInstanceVkDevice

A recurring pattern I see in tutorials/code snippets from Vulkan apps is that most code is in 1 very big class. 我在Vulkan应用程序的教程/代码片段中看到的反复出现的模式是,大多数代码都在一个非常大的类中。

That's not really specific to Vulkan. 这并不是特别针对Vulkan的。 If you think about it, pretty much all C++ applications are one big class doing everything (only differences being how much the programmer bothers to delegate from it to some other class instances). 如果你考虑一下,几乎所有的C ++应用程序都是一个大的类来做所有事情(唯一的区别在于程序员难以从它委托给其他类实例)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM