Skip to content

Pipeline program writer

Sylvain Doremus edited this page Jan 15, 2024 · 2 revisions

Table of Contents

  1. Description
  2. Examples

Description

Initially, ShaderWriter allows writing shaders for a specific shader stage, through a specific writer instance (sdw::VertexWriter, sdw::FragmentWriter, sdw::RayMissWriter, ...)

Since version 2.7.0, it is also possible to write a full program with an entry point for each stage, through three classes :

  • sdw::TraditionalGraphicsWriter, for traditional rasterizer stages (vertex, geometry, tessellation)
    This writer can't write mesh or task shaders.
  • sdw::ModernGraphicsWriter, for task/mesh (and fragment) rasterizer stages.
    From this writer, you can't write other rasterizer stages.
  • sdw::RayTraceWriter, for ray trace stages (ray generation, intersection, miss, closest hit, any hit, and callable)

Each one of them exposes the functions to create an entry point for the specific stages, all named implementEntryPoint(T), and the parameters tell what kind of stage is written.

Examples

Common Data

Here is the description of structures used in the next examples

template< sdw::var::Flag FlagT >
using ColourStructT = sdw::IOStructInstanceHelperT< FlagT, "Colour"
	, sdw::IOVec4Field< "colour", 0u > >;
template< sdw::var::Flag FlagT >
using PosColStructT = sdw::IOStructInstanceHelperT< FlagT, "PosCol"
	, sdw::IOVec4Field< "position", 0u >
	, sdw::IOVec4Field< "colour", 1u > >;

template< sdw::var::Flag FlagT >
struct ColourT : public ColourStructT< FlagT >
{
	ColourT( sdw::ShaderWriter & writer
		, sdw::expr::ExprPtr expr
		, bool enabled = true )
		: ColourStructT< FlagT >{ writer, std::move( expr ), enabled }
	{
	}

	auto colour()const { return this->template getMember< "colour" >(); }
};

template< sdw::var::Flag FlagT >
struct PosColT : public PosColStructT< FlagT >
{
	PosColT( sdw::ShaderWriter & writer
		, sdw::expr::ExprPtr expr
		, bool enabled = true )
		: PosColStructT< FlagT >{ writer, std::move( expr ), enabled }
	{
	}

	auto position()const { return this->template getMember< "position" >(); }
	auto colour()const { return this->template getMember< "colour" >(); }
};

Basic Vertex/Fragment

sdw::TraditionalGraphicsWriter writer;

sdw::UniformBuffer myUbo{ writer, "MyUbo", 0u, 0u };
auto mvp = myUbo.declMember< sdw::Mat4 >( "mvp" );
myUbo.end();

// Vertex Shader
writer.implementEntryPointT< PosColT, ColourT >( [&]( sdw::VertexInT< PosColT > in
	, sdw::VertexOutT< ColourT > out )
	{
		out.colour() = in.colour();
		out.vtx.position = mvp * in.position();
	} );

// Fragment Shader
writer.implementEntryPointT< ColourT, ColourT >( [&]( sdw::FragmentInT< ColourT > in
	, sdw::FragmentOutT< ColourT > out )
	{
		out.colour() = in.colour();
	} );

Basic Vertex/Geometry/Fragment

sdw::TraditionalGraphicsWriter writer{ &testCounts.allocator };

sdw::UniformBuffer myUbo{ writer, "MyUbo", 0u, 0u };
auto mvp = myUbo.declMember< sdw::Mat4 >( "mvp" );
myUbo.end();

// Vertex Shader
writer.implementEntryPointT< PosColT, PosColT >( [&]( sdw::VertexInT< PosColT > in
	, sdw::VertexOutT< PosColT > out )
	{
		out.position() = in.position();
		out.colour() = in.colour();
		out.vtx.position = out.position();
	} );

// Geometry Shader
writer.implementEntryPointT< 3u, sdw::TriangleListT< PosColT >, sdw::TriangleStreamT< ColourT > >( [&]( sdw::GeometryIn in
	, sdw::TriangleListT< PosColT > list
	, sdw::TriangleStreamT< ColourT > out )
	{
		out.colour() = list[0].colour();
		out.vtx.position = mvp * list[0].vtx.position;
		out.append();

		out.colour() = list[1].colour();
		out.vtx.position = mvp * list[1].vtx.position;
		out.append();

		out.colour() = list[2].colour();
		out.vtx.position = mvp * list[2].vtx.position;
		out.append();

		out.restartStrip();
	} );

// Fragment Shader
writer.implementEntryPointT< ColourT, ColourT >( [&]( sdw::FragmentInT< ColourT > in
	, sdw::FragmentOutT< ColourT > out )
	{
		out.colour() = in.colour();
	} );

Basic Task/Mesh/Fragment

// Structures
static uint32_t const ThreadsPerWave = 32u;

template< sdw::var::Flag FlagT >
using PayloadStructT = sdw::IOStructInstanceHelperT< FlagT, "Payload"
	, sdw::IOUIntArrayField< "meshletIndices", ast::type::Struct::InvalidLocation, ThreadsPerWave > >;

template< sdw::var::Flag FlagT >
struct PayloadT : public PayloadStructT< FlagT >
{
	PayloadT( sdw::ShaderWriter & writer
		, sdw::expr::ExprPtr expr
		, bool enabled = true )
		: PayloadStructT< FlagT >{ writer, std::move( expr ), enabled }
	{
	}

	auto meshletIndices()const { return this->template getMember< "meshletIndices" >(); }
};

struct CullData : public sdw::StructInstanceHelperT< "CullData"
	, std::type::MemoryLayout::eStd430
	, sdw::Vec4Field< "boundingSphere" > >
{
	CullData( sdw::ShaderWriter & writer
		, sdw::expr::ExprPtr expr
		, bool enabled = true )
		: StructInstanceHelperT{ writer, std::move( expr ), enabled }
	{
	}

	auto boundingSphere()const { return getMember< "boundingSphere" >(); }
};

struct Meshlet : public sdw::StructInstanceHelperT< "Meshlet"
	, sdw::type::MemoryLayout::eStd430
	, sdw::UIntField< "vertCount" >
	, sdw::UIntField< "vertOffset" >
	, sdw::UIntField< "primCount" >
	, sdw::UIntField< "primOffset" > >
{
	Meshlet( sdw::ShaderWriter & writer
		, sdw::expr::ExprPtr expr
		, bool enabled = true )
		: StructInstanceHelperT{ writer, std::move( expr ), enabled }
	{
	}
	
	auto vertCount()const { return getMember< "vertCount" >(); }
	auto vertOffset()const { return getMember< "vertOffset" >(); }
	auto primCount()const { return getMember< "primCount" >(); }
	auto primOffset()const { return getMember< "primOffset" >(); }
};

struct TriIndex : public sdw::StructInstanceHelperT< "TriIndex"
	, sdw::type::MemoryLayout::eStd430
	, sdw::U32Vec3Field< "index" > >
{
	TriIndex( sdw::ShaderWriter & writer
		, sdw::expr::ExprPtr expr
		, bool enabled = true )
		: StructInstanceHelperT{ writer, std::move( expr ), enabled }
	{
	}

	auto index()const { return getMember< "index" >(); }
};

struct VtxIndex : public sdw::StructInstanceHelperT< "VtxIndex"
	, sdw::type::MemoryLayout::eStd430
	, sdw::UIntField< "index" > >
{
	VtxIndex( sdw::ShaderWriter & writer
		, sdw::expr::ExprPtr expr
		, bool enabled = true )
		: StructInstanceHelperT{ writer, std::move( expr ), enabled }
	{
	}

	auto index()const { return getMember< "index" >(); }
};

struct PosCol : public sdw::StructInstanceHelperT< "PosCol"
	, sdw::type::MemoryLayout::eStd430
	, sdw::Vec4Field< "position" >
	, sdw::Vec4Field< "colour" > >
{
	PosCol( sdw::ShaderWriter & writer
		, sdw::expr::ExprPtr expr
		, bool enabled = true )
		: StructInstanceHelperT{ writer, std::move( expr ), enabled }
	{
	}

	auto position()const { return getMember< "position" >(); }
	auto colour()const { return getMember< "colour" >(); }
};

sdw::ModernGraphicsWriter writer{ &testCounts.allocator };

auto ModelUbo = writer.declUniformBuffer( "ModelUbo", 1u, 0u );
auto mvp = ModelUbo.declMember< sdw::Mat4 >( "mvp" );
auto world = ModelUbo.declMember< sdw::Mat4 >( "world" );
auto scale = ModelUbo.declMember< sdw::Float >( "scale" );
auto cullPlanes = ModelUbo.declMember< sdw::Vec4 >( "cullPlanes", 6u );
ModelUbo.end();

auto vertices = writer.declArrayStorageBuffer< PosCol >( "bufferVertices", 0u, 1u );
auto meshlets = writer.declArrayStorageBuffer< Meshlet >( "bufferMeshlets", 1u, 1u );
auto vertexIndices = writer.declArrayStorageBuffer< VtxIndex >( "bufferVertexIndices", 2u, 1u );
auto primitiveIndices = writer.declArrayStorageBuffer< TriIndex >( "bufferPrimitiveIndices", 3u, 1u );
auto meshletCullData = writer.declArrayStorageBuffer< CullData >( "bufferMeshletCullData", 4u, 1u );

auto isVisible = writer.implementFunction< sdw::Boolean >( "isVisible"
	, [&]( CullData cullData )
	{
		auto center = writer.declLocale( "center", vec4( cullData.boundingSphere.xyz(), 1.0_f ) * world );
		auto radius = writer.declLocale( "radius", cullData.boundingSphere.w() * scale );

		for ( int i = 0; i < 6; ++i )
		{
			if ( dot( center, cullPlanes[i] ) < -radius )
			{
				writer.returnStmt( sdw::Boolean{ false } );
			}
		}

		writer.returnStmt( sdw::Boolean{ true } );
	}
	, sdw::InParam< CullData >{ writer, "cullData" } );

// Task Shader
writer.implementEntryPointT< PayloadT >( SDW_MeshLocalSize( ThreadsPerWave, 1u, 1u )
	, sdw::TaskPayloadOutT< PayloadT >{ writer }
	, [&]( sdw::TaskSubgroupIn in
		, sdw::TaskPayloadOutT< PayloadT > payload )
	{
		auto laneId = writer.declLocale( "laneId", in.localInvocationID );
		auto baseId = writer.declLocale( "baseId", in.workGroupID );
		auto meshletId = writer.declLocale( "meshletId", ( baseId * 32u + laneId ) );
		auto visible = writer.declLocale( "visible"
			, isVisible( meshletCullData[meshletId] ) );
		auto vote = writer.declLocale( "vote", subgroupBallot( visible ) );
		auto tasks = writer.declLocale( "tasks", subgroupBallotBitCount( vote ) );
		auto idxOffset = writer.declLocale( "idxOffset", subgroupBallotExclusiveBitCount( vote ) );

		// Compact visible meshlets into the export payload array
		IF( writer, visible )
		{
			payload.meshletIndices()[idxOffset] = meshletId;
		}
		FI;

		payload.dispatchMesh( SDW_MeshLocalSize( tasks, 1_u, 1_u ) );
	} );

// Mesh Shader
writer.implementEntryPointT< PayloadT, ColourT, sdw::VoidT >( SDW_MeshLocalSize( 32u, 1u, 1u )
	, sdw::TaskPayloadInT< PayloadT >{ writer }
	, sdw::MeshVertexListOutT< ColourT >{ writer, 252u }
	, sdw::TrianglesMeshPrimitiveListOut{ writer, 84u }
	, [&]( sdw::MeshSubgroupIn in
		, sdw::TaskPayloadInT< PayloadT > payload
		, sdw::MeshVertexListOutT< ColourT > vtxOut
		, sdw::TrianglesMeshPrimitiveListOut primOut )
	{
		auto laneId = writer.declLocale( "laneId", in.localInvocationID );
		auto meshletId = writer.declLocale( "meshletId", payload.meshletIndices()[laneId] );
		auto meshlet = writer.declLocale( "meshlet", meshlets[meshletId] );

		primOut.setMeshOutputCounts( meshlet.vertCount, meshlet.primCount );

		IF( writer, laneId < meshlet.primCount )
		{
			primOut[laneId].primitiveIndex = primitiveIndices[meshlet.primOffset + laneId].index();
		}
		FI;

		IF( writer, laneId < meshlet.vertCount )
		{
			auto vertexIndex = writer.declLocale( "vertexIndex", vertexIndices[meshlet.vertOffset + laneId].index() );
			auto vertex = writer.declLocale( "vertex", vertices[vertexIndex] );

			vtxOut[laneId].position = mvp * vertex.position();
			vtxOut[laneId].colour() = vertex.colour();
		}
		FI;
	} );

// Fragment Shader
writer.implementEntryPointT< ColourT, ColourT >( [&]( sdw::FragmentInT< ColourT > in
	, sdw::FragmentOutT< ColourT > out )
	{
		out.colour() = in.colour();
	} );