我正在使用以下预处理器黑客
// shader_descriptor.hpp
#ifdef __cplusplus
#include "./shader_source.hpp"
#include "engine/utils/vec_t.hpp"
#include <tuple>
#include <span>
#define BEGIN_SHADER_DESCRIPTOR(namespace_name, name) \
namespace namespace_name \
{ \
struct name \
{ \
using port_types = std::tuple <
#define SHADER_INPUT(type, name) ::type##f_t,
#define END_SHADER_DESCRIPTOR() \
nullptr_t > ; \
static ::shaders::vertex_shader_source<std::span<uint32_t const>> vertex_shader(); \
static ::shaders::fragment_shader_source<std::span<uint32_t const>> \
fragment_shader(); \
\
static constexpr auto num_inputs = std::tuple_size_v<port_types> - 1; \
} \
; \
}
#else
const int port_counter_base = __COUNTER__;
#define BEGIN_SHADER_DESCRIPTOR(namespace_name, name)
#define SHADER_INPUT(type, name) \
layout(location = __COUNTER__ - port_counter_base - 1) in type name;
#define IDIS_END_SHADER_DESCRIPTOR()
#endif
所以我可以从源代码中提取正确的输入。输入定义如下:
// tsetprog.hpp
#include "./shader_descriptor.hpp"
BEGIN_SHADER_DESCRIPTOR(idis::shaders, testprog)
SHADER_INPUT(vec2, loc)
SHADER_INPUT(vec4, vert_color)
END_SHADER_DESCRIPTOR()
还有一个着色器模块(这里是顶点着色器):
#include "./testprog_def.hpp"
layout(location = 0) out vec4 frag_color;
void main()
{
gl_Position = vec4(loc, 0.0, 1.0);
frag_color = vert_color;
}
然后分两步编译着色器:
targets = args['targets']
source_file = args['source_file']
with tempfile.TemporaryDirectory(suffix = None, prefix = 'maike_' + args['build_info']['build_id']) as tmpdir:
new_source_file = tmpdir + '/' + os.path.basename(source_file)
with open(new_source_file, 'wb') as tmpfile:
tmpfile.write('#version 450\n'.encode())
cpp = subprocess.run(['cpp', '-P', source_file], stdout=subprocess.PIPE)
tmpfile.write(cpp.stdout)
result = subprocess.run(['glslangValidator', '-V', '-Os', '-o', targets[0], new_source_file]);
虽然这种方法有效,但它有一些局限性:
不可能对所有输入使用单个顶点缓冲区(无法生成正确的结构以从 c++ 端使用)
由于与 (1) 类似的原因,可能不适用于统一缓冲区。
是否有任何其他选项可以保证在编译时正确绑定(即:绑定应该具有正确的类型,并且必须连接所有输入)。目前,我使用以下函数来绑定缓冲区:
template<class ShaderDescriptor, class... Buffers>
render_pass_section& bind(VkCommandBuffer cmdbuff,
std::reference_wrapper<pipeline<ShaderDescriptor> const> pipeline,
std::reference_wrapper<Buffers const>... buffers)
{
static_assert(((Buffers::buffer_usage & VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) && ...));
static_assert(sizeof...(Buffers) == ShaderDescriptor::num_inputs);
static_assert(std::is_same_v<std::tuple<typename Buffers::value_type..., nullptr_t>,
typename ShaderDescriptor::port_types>);
std::array<VkBuffer, sizeof...(Buffers)> handles{buffers.get().handle()...};
std::array<VkDeviceSize, sizeof...(Buffers)> offsets{};
vkCmdBindVertexBuffers(
cmdbuff, 0, std::size(handles), std::data(handles), std::data(offsets));
vkCmdBindPipeline(cmdbuff, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.get().handle());
return *this;
}
ShaderDescriptor
由预处理器生成。