我正在尝试构建基于3D体素的游戏引擎,以学习如何使用vulkan。我碰到一堵墙,找不到关于如何攀登的文档。现在,我正在绘制2D三角形并将其在屏幕上移动,我的2D三角形在顶点缓冲区中定义为1个顶点,将来将在顶点着色器中转换为屏幕空间。我的几何着色器将单个顶点变成3个顶点,然后传递给我的片段着色器。
triangle_center_position[0] = (cursor_x - (vulkan_window_width_get() / 2.0f)) / vulkan_window_width_get();
triangle_center_position[1] = (cursor_y - (vulkan_window_height_get() / 2.0f)) / vulkan_window_height_get();
// send to gpu via memory mapped region
memcpy(triangle_position_buffer.mapped_memory, &triangle_center_position, sizeof(vec2) * 1);
它通过以下方式绑定在命令缓冲区中:
VkBuffer vertexBuffers[] = {buffer->buffer};
VkDeviceSize offsets[] = {0};
vkCmdBindVertexBuffers(command_buffer[i], 0, 1, vertexBuffers, offsets);
vkCmdDraw(command_buffer[i], (uint32_t) buffer->num_elements, 1, 0, 0);
我的计划是最终修改此代码以包含3D点,并使我的几何体着色器将3D点扩展为体素。
不幸的是,我只希望这种转换发生在体素世界中的块上,而不是其他事物(玩家模型等)上。
在OpenGL中,我只是在针对不同“材质”构建的两个着色器程序上调用glUseProgram()
,在vulkan中似乎极力劝阻。我的直觉就是这样
currentMaterial = null
for (Renderable r : sort(everything, by material type))
if (!r.isWithinViewOfScreen())
continue
if (r.material != currentMaterial)
currentMaterial = r.material
r.material.use()
r.render()
主要问题是某些Renderable
将使用完全不同的着色器要求,但是vulkan中似乎没有提供在着色器程序之间交换的规定。
答案 0 :(得分:2)
vulkan中似乎没有在着色器程序之间交换的规定。
在vulkan中与glUseProgram
最接近的类似物是vkCmdBindPipeline
。
主要区别在于,许多被认为是OpenGL状态机一部分的东西都被烘焙到Vulkan流水线对象中。因此,例如,在OpenGL上,您可以调用glUseProgram
,渲染一些内容,然后调用glDisable(GL_DEPTH_TEST)
并渲染更多内容,在Vulkan上,这将是两个不同的管道,并且需要调用{{ 1}}。
如果您熟悉vkCmdBindPipeline
,并且正在寻找某种方法来修改哪些着色器,那么简短的答案是您不能这样做。您需要为需要使用的每组着色器创建一个完整的VkPipeline对象,即使管道中的其他任何值都不同。
但是,如果您担心最终将创建大量管道,并且这会影响您的性能,则应研究管道缓存的使用以及VK_PIPELINE_CREATE_DERIVATIVE_BIT标志,该标志可让您创建类似于模板管道中的子管道。与从OpenGL获得的性能相同或更好。
编辑:
请记住,试图提高管道创建性能的开发人员应同时使用vkCmdBindPipeline
和VkPipelineCache
的功能。派生管道对您有多少帮助完全取决于驱动程序,并且某些驱动程序(例如ARM MALI驱动程序)被明确记录为忽略此标志。 Vulkan规范并未就何时使用或不使用派生标志提供任何指导。正确的方法可能是直接与ISV合作以找出如何最好地应用它,或者总是使用它,以期希望它会在不影响性能的情况下提高性能,或者根本不使用派生工具。 。
另一方面,管道缓存功能几乎可以肯定会在各种硬件上缩短管道创建时间。