解析SPIR-V

一、解析SPIR-V

编译

首先,我们需要将每个着色器阶段从源代码文件编译为SPIR-V 文件。有多种工具可用于执行此操作,比如GLSlangValidator-它支持 Google #include 扩展。

将顶点着色器代码放入名为“shader.vert”的文本文件中,将像素着色器代码放入名为“shader.frag”的文件中。在同一目录中创建一个包含以下内容的 .bat 文件:

glslangValidator.exe“shader.vert”-V -o“vert.spv”
glslangValidator.exe“shader.frag”-V -o“frag.spv”

运行bat文件,将保存两个.spv文件。

链接

现在我们想要将代表不同着色器阶段的两个文件合并到一个文件中。这是通过Khronos 的链接工具完成的。将以下行添加到 .bat 文件中,将两个 .spv 文件编译为一个。它还将删除现有文件以进行一些清理:

spirv-link "vert.spv" "frag.spv" -o "shader.spv"
del "vert.spv"
del "frag.spv"

这将保存一个名为“shader.spv”的文件,您可以将其作为一个着色器模块加载并用于 Vulkan 中的不同阶段。

解析

如果您始终使用顶点和片段阶段,那么没有问题,但如果组合的 .spv 文件包含其他阶段,或者缺少片段阶段怎么办?我们可以使用最小的 SPIR-V 文件解析器轻松解释这一点。我们不会包含任何大型臃肿的库来执行此操作,因为我们只需要有关着色器中包含哪些阶段的一些基本信息。幸运的是,SPIR-V 规范非常简单,不需要太多代码即可提取我们想要的信息:

std::string entrypointname[6];

auto stream = ReadFile(L"Shaders/shader.spv");

// Parse SPIR-V data
Assert(stream->ReadInt() == 0x07230203);
int version = stream->ReadInt();
int genmagnum = stream->ReadInt();
int bound = stream->ReadInt();
int reserved = stream->ReadInt();

bool stages[6] = {false,false,false,false,false,false};		

// Instruction stream
while (stream->Ended() == false)
{
	int pos = stream->GetPos();
	unsigned int bytes = stream->ReadUInt();
	int opcode = LOWORD(bytes);
	int wordcount = HIWORD(bytes);
	if (opcode == 15)
	{
		int executionmodel = stream->ReadInt();
		Assert(executionmodel >= 0);
		if (executionmodel < 6)
		{
			stream->ReadInt(); // entry point
			stages[executionmodel] = true;
			entrypointname[executionmodel] = stream->ReadString();
		}
	}
	stream->Seek(pos + wordcount * 4);
}

这个代码可以检索每个阶段的入口点名称,因此可以确保正确加载着色器。

以下是 SPIR-V 规范中的不同着色器阶段:

  • 0: Vertex
  • 1: TessellationControl
  • 2: TessellationEvaluation
  • 3: Geometry
  • 4: Fragment
  • 5: GLCompute

我们现在为 Vulkan 程序提供了标准的单文件着色器格式。用于创建这些的代码将如下所示:

VkShaderModule shadermodule;

// Create shader module
VkShaderModuleCreateInfo shaderCreateInfo = {};
shaderCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
shaderCreateInfo.codeSize = bank->GetSize();
shaderCreateInfo.pCode = reinterpret_cast<const uint32_t*>(bank->buf);
VkAssert(vkCreateShaderModule(device->device, &shaderCreateInfo, nullptr, &shadermodule));

// Create vertex stage info
VkPipelineShaderStageCreateInfo vertShaderStageInfo = {};
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertShaderStageInfo.module = shadermodule;
vertShaderStageInfo.pName = entrypointname[0].c_str();

VkPipelineShaderStageCreateInfo fragShaderStageInfo = {};
if (stages[4])
{
  // Create fragment stage info			
  fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
  fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
  fragShaderStageInfo.module = shadermodule;
  fragShaderStageInfo.pName = entrypointname[4].c_str();
}

// Create your graphics pipeline...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值