Vulkan 踩坑的随想随写,随意更新。

1

glsl 中我要预定义超多个用不完的贴图,然后随意绑定贴图数量,例如我预定义了65535个贴图,但我只用了 vkUpdateDescriptorSets 就填了其中6个,所以校验层老是发出烦人警告,提示你有好多desc没有被Update,怎么办。

[ VUID-vkCmdDrawIndexed-None-02699 ] Object 0: handle = 0xf412a10000000123, type = VK_OBJECT_TYPE_DESCRIPTOR_SET; | MessageID = 0xa44449d4 | VkDescriptorSet 0xf412a10000000123[] encountered the following validation error at vkCmdDrawIndexed() time: Descriptor in binding #0 index 0 is being used in draw but has never been updated via vkUpdateDescriptorSets() or a similar call. The Vulkan spec states: Descriptors in each bound descriptor set, specified via vkCmdBindDescriptorSets, must be valid if they are statically used by the VkPipeline bound to the pipeline bind point used by this command (https://vulkan.lunarg.com/doc/view/1.2.162.0/windows/1.2-extensions/vkspec.html#VUID-vkCmdDrawIndexed-None-02699)
layout(set=0, binding=0) uniform sampler2D tex_list[65535];

解决方法:首先你使用一个扩展 VK_EXT_descriptor_indexing 或者选择开启 vulkan v1.2 的核心功能。

然后在创建这个包含这个tex_list 的描述集布局 VkDescriptorSetLayoutCreateInfo 时,用 pNext 外链一个
VkDescriptorSetLayoutBindingFlagsCreateInfo 结构,然后设定 pBindingFlags 为 VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT。

大概如下,我用的是Vulkan C++ API

	auto _tmps = array<vk::DescriptorBindingFlags, 1>();
	_tmps.fill(vk::DescriptorBindingFlagBits::ePartiallyBound);
	auto set_layout_bind_flags_ci = vk::DescriptorSetLayoutBindingFlagsCreateInfo()
		.setBindingFlags(_tmps);
		
	desc_layout_ci = vk::DescriptorSetLayoutCreateInfo()
		.setBindings(layout_bind_set2)
		.setFlags({})
		.setPNext(&set_layout_bind_flags_ci);

引用的官方链接

https://github.com/KhronosGroup/Vulkan-Guide/blob/master/chapters/extensions/VK_EXT_descriptor_indexing.md#partially-bound

2

在 glsl 里面,我要用的 tex_id 是动态算出来的,不是传进去的,直接用它又给我发一堆错误警告。

#version 450

layout(location = 2) in vec2	in_uv;

layout(set=0, binding=0) uniform sampler2D tex_list[65535];
layout(set=0, binding=1) uniform _abc
{
	int b1;
};
void main()
{
	int t1 = 0;
	if (b1 > 5)
		t1 = t1 * 10;
	else if (b1 > 10)
		t1 = t1 - 3;
	gl_Color = texture(tex_list[t1], in_uv);
}

怎么解决。加入扩展 GL_EXT_nonuniform_qualifier 和使用 nonuniformEXT 函数。改成一下

#version 450
#extension GL_EXT_nonuniform_qualifier : enable

layout(location = 2) in vec2	in_uv;

layout(set=0, binding=0) uniform sampler2D tex_list[65535];
layout(set=0, binding=1) uniform _abc
{
	int b1;
};
void main()
{
	int t1 = 0;
	if (b1 > 5)
		t1 = t1 * 10;
	else if (b1 > 10)
		t1 = t1 - 3;
	gl_Color = texture(tex_list[nonuniformEXT(t1)], in_uv);
}

引用的官方链接

https://github.com/KhronosGroup/Vulkan-Guide/blob/master/chapters/extensions/VK_EXT_descriptor_indexing.md#dynamic-non-uniform-indexing
https://developer.nvidia.com/blog/improved-glsl-syntax-vulkans-descriptorset-indexing/

3

我用 Vulkan v1.2 来生成这个实例,居然核心扩展不是自动给我全部打开的,还要另外写个结构体来启动扩展,好麻烦。
我这里开了俩个功能,一个 vulkan v1.2 全部核心功能,一个是非核心的像素锁功能

	vk::PhysicalDevice gpu = init_gpu(0);
	vector<vk::DeviceQueueCreateInfo> queue_infos = {...};
	vector<const char*>enabled_gpu_ext_names = {
		VK_KHR_SWAPCHAIN_EXTENSION_NAME,
		VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME
		}
	
	// 擦,设定使用扩展后,还要设定启用功能才能真正启动目标扩展,坑爹啊
	vk::PhysicalDeviceVulkan12Features features12 = {};	// 指示全v1.2核心功能
	vk::PhysicalDeviceFragmentShaderInterlockFeaturesEXT _ext2;	// 指示像素锁功能
	vk::PhysicalDeviceFeatures2 physical_features2 ={};
	// 要先设定pNext,这样就会在 getFeatures2 时自动设定 features12
	physical_features2.setPNext(&features12);
	features12.setPNext(&_ext2);
	gpu.getFeatures2(&physical_features2);

	VkDeviceCreateInfo info ={};
	info.pNext = &physical_features2;

	auto dev_info = vk::DeviceCreateInfo()
		.setQueueCreateInfos(queue_infos)
		.setPEnabledLayerNames({})
		.setPEnabledExtensionNames(enabled_gpu_ext_names)
		.setPEnabledFeatures(nullptr)
		.setPNext(&physical_features2);

引用官方链接:

https://github.com/KhronosGroup/Vulkan-Guide/blob/master/chapters/enabling_extensions.md
https://github.com/KhronosGroup/Vulkan-Guide/blob/master/chapters/enabling_features.md

4

逐像素自旋锁
在vulkan中,没有真正的 atomic 操作,都是虚假的。
从这里可以看到 opengl 和 vulkan 的内存模型,

https://www.khronos.org/opengl/wiki/Memory_Model#Incoherent_memory_access

不一致内存模型!!!包括原子操作!

举个例子,你在1号Frag线程对1号内存位进行原子修改变量 把3变成了4,而2号Frag线程读取1号内存位时,仍然可能读出为3!而不是4。我花了许久才发现,这原来是个坑货!
如何解决?使用另外一个扩展,逐像素自旋锁。
VK_EXT_fragment_shader_interlock
举例

#version 450
#extension GL_ARB_fragment_shader_interlock : require
#extension GL_ARB_shader_image_load_store : require

layout(pixel_interlock_ordered) in;

layout(location = 0) in vec4	in_color;

layout(set=0, binding=0, std430) uniform _scn_size
{
	uint scn_width;
	uint scn_height;
};

// 简单的 ssbo,作为直接统计每个像素点绘制了多少次。
layout(set=0, binding=1, std430) coherent buffer _pixel_sum
{
	uint pixel_sum[2147483647];
};

void main()
{
	// 开始互斥区
	beginInvocationInterlockARB();
	// 该区域内,同一个位置的像素线程都是串行执行的,不同位置的不互相影响。
	uvec2 xy = gl_FragCoord.xy;
	uint pos = scn_width * xy[1] + xy[0];
	pixel_sum[pos] += 1;
	// 结束互斥区
	endInvocationInterlockARB();
	gl_Color = in_color;
}

官方链接

https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_EXT_fragment_shader_interlock.html

无序透明

https://github.com/chrismile/PixelSyncOIT/tree/master/Data/Shaders/MLAB

5

我希望在同一个set,同一个bind里面访问多个ssbo。然后尝试使用一个WriteDescriptorSet把他们都写进去,结果不行,shader只能看到第一个desc绑定的ssbo,后面desc都被忽略了,没有绑上。

vector<vk::DescriptorBufferInfo>	node_descs;
node_descs = {node_desc1, node_desc2, ...};
writer = vk::WriteDescriptorSet()
			.setDstBinding(1)
			.setDescriptorType(vk::DescriptorType::eStorageBuffer)
			.setBufferInfo(node_descs);
layout(set=0, binding=1, std430) buffer _node_param
{
	// 原本希望
	// node_desc1 映射到node_param[0]
	// node_desc2 映射到node_param[1]
	// 结果
	// 只有 node_desc1 映射到node_param[0]
	// node_param[1] 不存在,RenderDoc也看不到node_desc2绑定
	UniformNodeParamter		node_param[];
};

正确的方法应该是创建一个新的稀疏buffer,然后稀疏绑定到ssbo1,ssbo2的内存上面。然后单个绑定这个稀疏buffer。shader里面就可以访问ssbo1和ssbo2了。
然而发现sparse buffer bind。对每块内存,大小要为64KB的倍数,并且还要64KB对齐,并且类型还只能是GPU_ONLY,不能是CPU_TO_GPU和其他。另外,如果不符合要求,它就直接抛个异常出来,校验层也捕获不到问题。呃,好难用,算了,放弃掉这个功能算了。

6

生成阴影贴图时,使用点光源和聚光灯,透视投影时,生成深度贴图正常,但后面平行光源,使用正交投影时,却发现深度贴图全都是黑色。
后面调试发现,只要在深度贴图中增加一个条件,把 z 深度小于0的片段丢弃掉,正交后的深度贴图就正常了。。。

#version 460
#extension GL_ARB_separate_shader_objects : enable
#extension GL_EXT_scalar_block_layout : enable

layout(location = 0) in vec4	in_pos;

void main()
{
	float z = in_pos.z / in_pos.w;
	if (z < 0)
		discard;
	gl_FragDepth = z;
}

神奇,它居然没有主动把深度超出 [0, 1] 范围的片段丢掉,还写入了深度缓存?!
后面检查发现,把阴影贴图的管线的光栅化信息中的 DepthClampEnable 置为了 TRUE,同时因为正交投影后得到的点坐标w始终为1,导致它不会主动丢弃超过 [0, 1] 范围的深度片段,然后就把深度缓冲写上了。。。

	//设定光栅化相关信息
	auto rasterization_info = vk::PipelineRasterizationStateCreateInfo()
		.setDepthClampEnable(VK_TRUE)

后面把 DepthClampEnable 置为了False,也去掉了片段着色器中的对z小于0的判断,正交投影下的深度贴图也正常了,而不是变成全黑。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值