我一直在使用Vulkan,但我刚刚学会了专业化常数.规范说:
例如,专用常量对于允许计算着色器在运行时由其更改其本地工作组大小非常有用.
整齐!我想做的几乎就是这样,我也希望将这些变化的常量用于其他目的.但Vulkan规范(目前版本1.0.34)中给出的特化常量的例子似乎都在SPIR-V中,而不是GLSL,我的着色器都是用GLSL编写的.所以我想我可能无法使用这个不错的功能.:(
我对吗?或者有没有办法通过GLSL使用特化常量,作为工作组大小常量,或作为任意常量变量值,还是以其他方式?
我的项目有时会受益于ccache,所以我一直在使用ccache.我现在正在添加预编译头文件.一些消息来源表明,这两者是不相容的,必须在它们之间做出选择.但我在ccache的文档中发现它在某种程度上支持PCH:https: //ccache.samba.org/manual.html#_precompiled_headers
实际上,当我尝试使用ccache在使用Clang -include-pch
选项的同时构建.o文件时,我发现ccache正在成功缓存.o.第一次编译尝试需要1.5秒,第二次只需0.05秒(因为ccache完成了它的工作).
麻烦的是,如果我用clang++
而不是with 运行相同的编译命令/usr/lib/ccache/clang++
,则需要0.5秒......除非我离开-include-pch
部分,在这种情况下需要大约1.5秒.似乎ccache可能会导致我的PCH被忽略,或者其他什么.
我按照说明(从上面的链接).正如那里指出的那样,我的ccache.conf看起来像这样:
sloppiness=pch_defines,time_macros
Run Code Online (Sandbox Code Playgroud)
我已经试过的一切合理的组合#include
,-include
,-include-pch
和-fpch-preprocess
我能想到的.编译总是花费1.5秒然后0.05秒,当它应该花费0.5秒,然后0.05秒.
有可能做到这一点,或者我必须在ccache和PCH之间做出选择吗?
假设我使用新的(从OpenGL 4.3开始)glBindVertexBuffer机制设置了两个VAO:
glGenVertexArrays(1, &vaoIndex0);
glGenVertexArrays(1, &vaoIndex1);
Run Code Online (Sandbox Code Playgroud)
...
glBindVertexArray(vaoIndex0)
glBindVertexBuffer(bindingIndex0, ...)
glEnableVertexAttribArray(0)
glVertexAttribFormat(0, ...)
glVertexAttribBinding(0, bindingIndex0)
...
glBindVertexArray(0)
Run Code Online (Sandbox Code Playgroud)
...
glBindVertexArray(vaoIndex1)
glBindVertexBuffer(bindingIndex1, ...)
glEnableVertexAttribArray(0)
glVertexAttribFormat(0, ...)
glVertexAttribBinding(0, bindingIndex1)
...
glBindVertexArray(0)
Run Code Online (Sandbox Code Playgroud)
并且假设两者是独立的,除非它们存在于相同的OpenGL上下文中; 它们绑定不同的缓冲区,用于绘制不同的东西.
bindingIndex0需要与bindingIndex1不同吗?两个指数的平等(或不平等)是否有任何意义?
...
编辑:
在收到答案后,我开始明白,对于一个真正知道"顶点缓冲区绑定点"是什么的人,特别是它的范围是什么,我的问题似乎是在问一些与我的意图不同的东西.也许一个更好的措辞就是" 为了防止冲突,是否需要尽力避免重复使用OpenGL顶点缓冲区绑定点索引,甚至跨多个VAO?" 但无论如何,似乎现在已经回答了这两个问题:不,你不能重复使用"绑定点",并且不需要以这种方式避免索引冲突.
我想将一个场景绘制到默认的帧缓冲区,同时还将一些与我绘制的模型相关的辅助数据绘制到具有与默认帧缓冲区相同的图像尺寸的屏幕外缓冲区.
我知道我可以分别做两个这样的事情,两个glDraw*调用(一个渲染到一个FBO,一个不渲染),它将使用两个合适的着色器程序.
我认为(仍在学习这一点),我可以做他们大多同时,由两个renderbuffers /纹理连接到一个FBO,做一个glDraw*与它的片段着色器将写入对应于多个FBO适当的值到多个输出一个着色器程序中调用附件,最后将FBO中两个图像中的一个复制到默认帧缓冲区,使用它来构建场景填充四边形或调用glBlitFramebuffer.
但是我可以使用一个着色器程序进行一次glDraw*调用,并让我的片段着色器同时写入可见帧缓冲区和一些屏幕外FBO吗?从我所看到的相关文档中我不怀疑,但我不确定.
我对在一组Vulkan计算着色器中实现特定算法感兴趣.该算法在一个点使用clz()函数.我希望我的NVIDIA GPU可能为这个功能提供硬件支持; CUDA显然使用clz指令,而clz()也在OpenCL 1.2中.所以我不想写自己的clz().有没有办法让我以CUDA或OpenCL的方式调用函数?
我想我可以尝试将一个OpenCL内核编译成SPIR-V并在Vulkan中使用它,但我不认为Vulkan会对此感到高兴......?
我的另一个想法是,也许我可以翻译一个包含对SPIR-V程序集的clz()调用的非常简单的OpenCL内核,对我的GLSL着色器执行相同操作,然后手动破解clz()调用,因为它出现在内核汇编代码中,进入着色器的汇编代码.但我对SPIR-V的细节一无所知,或者说Vulkan可能对计算着色器可能使用的各种SPIR-V指令有什么限制,所以我几乎不知道这是否真的有用.
假设我正在制作一个 VkSampler,或者可能是一组 VkSampler,我打算将它与多个纹理一起使用。每个纹理都有多个 mip 级别,并且 mip 级别的数量因纹理而异。我希望所有纹理的所有采样器选项都相同,但maxLod
.
我实际上不想maxLod
阻止使用任何特定的 mip 级别;我只希望以一种普通的、正确的方式使用每个纹理的所有级别。那么我是否可以只抛出一个高得离谱的上限并对所有纹理使用相同的采样器:
VkSamplerCreateInfo samplerCreateInfo;
Run Code Online (Sandbox Code Playgroud)
...
samplerCreateInfo.minLod = 0;
samplerCreateInfo.maxLod = 1048576;
Run Code Online (Sandbox Code Playgroud)
...或者我是否需要为每个纹理创建不同的采样器maxLod
并将采样器设置为该纹理的唯一最大 mip 索引?
我认为答案可能是,即使我的采样器maxLod
非常高,我也可以指望发生合理的钳位——即使我textureLod
在着色器中明确地传递了一个无用的高值——但我不完全确定我是否正确解释了Vulkan 和 GLSL 规范中的相关部分。
我不明白这个命令结构是如何工作的.所有这些似乎都有意义(在文档中;我还没有实际调用过函数),除了firstIndex.它看起来好像文档中存在拼写错误.
以下是我在每个查找相关文档的地方看到的文字:
间接寻址的参数被打包成一个采用形式的结构(在C中):
Run Code Online (Sandbox Code Playgroud)typedef struct { uint count; uint instanceCount; uint firstIndex; uint baseVertex; uint baseInstance; } DrawElementsIndirectCommand;
假设没有生成错误,对glMultiDrawElementsIndirect的单次调用是等效的:
Run Code Online (Sandbox Code Playgroud)GLsizei n; for (n = 0; n < drawcount; n++) { const DrawElementsIndirectCommand *cmd; if (stride != 0) { cmd = (const DrawElementsIndirectCommand *)((uintptr)indirect + n * stride); } else { cmd = (const DrawElementsIndirectCommand *)indirect + n; } glDrawElementsInstancedBaseVertexBaseInstance(mode, cmd->count, type, cmd->firstIndex + size-of-type, cmd->instanceCount, cmd->baseVertex, cmd->baseInstance); }
但是这些页面没有说明"类型大小"是什么意思,或者为什么它被添加到firstIndex,而不是说,乘以它.似乎glDrawElementsInstancedBaseVertexBaseInstance在那里采用了一个字节偏移量,所以对于我来说,将firstIndex作为GL_ELEMENT_ARRAY_BUFFER顶点索引数组的索引是有意义的 - 因此,索引的索引 - 以及类型的大小是顶点索引(例如4)的字节大小,您需要将"index into indices arrays"转换为该数组的字节偏移量.
但是......转换将表示为乘法,他们说+不*.:(
我对吗?firstIndex是顶点索引数组中条目的索引,而size-of-type是顶点索引的大小(以字节为单位),还是+ a拼写错误?如果没有,我错过了什么?
Vulkan 规范 (1.0.27) 表示(在第6.5 节“管道障碍”中):
pMemoryBarriers、pBufferMemoryBarriers 和 pImageMemoryBarriers 数组的每个元素指定内存依赖性的两半,如上面所定义。[...]
如果在渲染通道实例之外调用 vkCmdPipelineBarrier,则第一组命令是提交到队列并记录在命令缓冲区中的所有先前命令,第二组命令是记录在命令缓冲区中并提交到队列的所有后续命令。
(这个措辞很有趣;如果从字面上解释,它似乎是说屏障只在一个命令缓冲区内对命令进行排序,而“提交到队列”部分可能是多余的;但如果解释得更模糊一点,它似乎旨在说屏障在其命令缓冲区和队列中对命令进行排序。其他 Stack Overflow 页面向我指出了以下内容,这似乎证实了后一种解释: https: //github.com/KhronosGroup/Vulkan-Docs/issues/300)
那么我的问题是。假设您有四个命令缓冲区,分两批提交,每批提交两个,全部在一个vkQueueSubmit
命令中:
VkSubmitInfo nextSubmitInfo;
nextSubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
nextSubmitInfo.pNext = nullptr;
nextSubmitInfo.waitSemaphoreCount = 0;
nextSubmitInfo.pWaitDstStageMask = nullptr;
nextSubmitInfo.pWaitSemaphores = nullptr;
nextSubmitInfo.signalSemaphoreCount = 0;
nextSubmitInfo.pSignalSemaphores = nullptr;
std::vector<VkCommandBuffer> commandBuffersAB{commandBufferA, commandBufferB};
std::vector<VkCommandBuffer> commandBuffersCD{commandBufferC, commandBufferD};
std::vector<VkSubmitInfo> submitInfo;
nextSubmitInfo.commandBufferCount = commandBuffersAB.size();
nextSubmitInfo.pCommandBuffers = commandBuffersAB.data();
submitInfo.emplace_back(nextSubmitInfo);
nextSubmitInfo.commandBufferCount = commandBuffersCD.size();
nextSubmitInfo.pCommandBuffers = commandBuffersCD.data();
submitInfo.emplace_back(nextSubmitInfo);
df.vkQueueSubmit(queue, submitInfo.size(), submitInfo.data(), VK_NULL_HANDLE);
Run Code Online (Sandbox Code Playgroud)
假设四个命令缓冲区中的每一个都包括屏障和一些动作命令(根据规范,这些命令是“执行动作(例如绘制/调度)的命令”)。那么,我倾向于天真地期望,障碍会将命令缓冲区视为按字母顺序提交,以便它们的第一和第二“一半”将包括(可能除其他外)以下内容:
| 屏障| 上半场| 下半场| |--------------------------------|---------------------------------------- -|----------------------------| | …
我有兴趣编写一个lambda函数,除其他外,它将调用std::make_unique
.要调用std::make_unique
我需要一个typename,但似乎为了直接将typename传递给lambda函数,我必须使lambda变量成为一个模板:
struct SpecialThing
{
SpecialThing(/* some arguments */) {}
}
void f()
{
template <typename Thing>
auto createThing = [](auto&&... parameters)
{
return std::make_unique<Thing>(std::forward<decltype(parameters)>(parameters)...);
};
auto thing = createThing<SpecialThing>(/* some construction parameters */);
}
Run Code Online (Sandbox Code Playgroud)
但我的编译器(GCC 4.9.2)似乎并不喜欢这样.(我并没有真正期待它,虽然我对模板变量知之甚少,但我不能确定它不会.)
假设我真的决定保留createThing
lambda函数类型的局部变量.我能以多么干净的方式std::make_unique
以这种方式包装?这是迄今为止我得到的最好的:
void f()
{
auto createThing = [](auto dummyThingPointer, auto&&... parameters)
{
typedef typename std::remove_pointer<decltype(dummyThingPointer)>::type Thing;
return std::make_unique<Thing>(std::forward<decltype(parameters)>(parameters)...);
};
auto thing = createThing(static_cast<SpecialThing*>(nullptr), /* some construction parameters */);
}
Run Code Online (Sandbox Code Playgroud)
它是罗嗦的,但它并不是很难理解,它编译.
我想也许我可以做类似的事情std::declval
,std::remove_reference
但我无法编译.无论如何,它不会比上面更清洁.
C++ …