s&box
RustCounter-Strike 2s&boxDeadlock
Wiki
Get s&box on Steam
Browse
OverviewGetting StartedCreating GamesEngine & ToolsDocumentationCommunity GamesResources
s&boxDocumentationRenderingShadersClassesBindless Api
SOURCE 2 · C#
Build and play games with s&box.

Facepunch's game engine and creation platform — make games in C#, or jump straight into community games.

Get s&box on Steam

s&box Wiki

  • Overview
  • Getting Started
  • Creating Games
  • Engine & Tools
  • Documentation
  • Community Games
  • Resources

Tools & Community

  • Getting Started
  • Creating Games
  • Engine & Tools
  • Resources

Platforms

  • RustBattle
  • CSBattle
  • Rust Game Wiki
  • CS2 Skins Wiki
  • Deadlock Wiki

About

  • Official Site
  • Developer Wiki
  • GitHub
  • Get on Steam
NOT AFFILIATED WITH FACEPUNCH STUDIOS · © 2026 s&box Wiki (community)
Docs/Rendering
Rendering

Bindless API

The fancy new way to do things in Vulkan / DX12 is bindless. This removes the limitations of the binding model allowing you to have access to far more textures and other resources within a shader and the ability to sample them from a dynamically provided identifier from buffers, vertex input, etc.

This allows a lot more versatility in your shaders, less CPU time spent binding textures and is the fundamental key to GPU driven rendering.

HLSL

The following methods are accessible from shaders:

Texture2D Bindless::GetTexture2D( int nIndex, bool srgb = false );
Texture3D Bindless::GetTexture3D( int nIndex );
TextureCube Bindless::GetTextureCube( int nIndex );
Texture2DArray Bindless::GetTexture2DArray( int nIndex );
TextureCubeArray Bindless::GetTextureCubeArray( int nIndex );
SamplerState Bindless::GetSampler( int nIndex );

:::tip sRGB variants of Texture2D can be sampled with Bindless::GetTexture2D( nIndex, true ); :::

Additionally these methods are available in compute shaders only:

RWTexture2D<float4> Bindless::GetRWTexture2D( int nIndex );
RWTexture3D<float4> Bindless::GetRWTexture3D( int nIndex );
RWTexture2DArray<float4> Bindless::GetRWTexture2DArray( int nIndex );

Example: Bindless Textures

Here's how you would create a structured buffer in C# containing texture indices and consume them in a shader.

struct TerrainMaterial
{
  public int ColorTextureIndex;
  public int NormalTextureIndex;
}

GpuBuffer<TerrainMaterial> TerrainMaterialsBuffer = new( 1 );
TerrainMaterialsBuffer.SetData( new[] {
  new TerrainMaterial {
    ColorTextureIndex = ColorTexture.Index,
    NormalTextureIndex = NormalTexture.Index
  },
  // ...
} );

SceneModel.Attributes.Set( "TerrainMaterials", TerrainMaterialsBuffer );
struct TerrainMaterial
{
  int ColorTextureIndex;
  int NormalTextureIndex;
};

StructuredBuffer<TerrainMaterial> TerrainMaterials < Attribute( "TerrainMaterials" ); >;

void SampleTerrain( float2 uv, in out float3 color, in out float3 normal )
{
  // Combine 4 dynamic terrain materials together
  for ( int i = 0; i < 4; i++ )
  {
    Texture2D ColorTexture = Bindless::GetTexture2D( TerrainMaterials[i].ColorTextureIndex, true ); // srgb
    Texture2D NormalTexture = Bindless::GetTexture2D( TerrainMaterials[i].NormalTextureIndex );
    
    color += ColorTexture.Sample( Sampler, uv );
    normal += NormalTexture.Sample( Sampler, uv );
  }
}

Example: Bindless Sampler State

You can also create a sampler state from C# and then expose it to shaders via bindless.

// Create a new component sampler property with bilinear filtering set as default
[Property] public SamplerState MyBindlessSampler { get; set; } = new() { Filter = FilterMode.Bilinear };

protected override void OnEnabled()
{
  Scene.RenderAttributes.Set( "MyBindlessSamplerIndex", MyBindlessSampler.Index );
}

And this is how you read this bindless index value from your shader:

// Add an attribute which reads "MyBindlessSamplerIndex" value from C#
int g_nMyBindlessSamplerId < Attribute( "MyBindlessSamplerIndex" ); Default( -1 ); >; 

float4 MainPs( PixelInput i ) : SV_Target0
{
  SamplerState sBindlessSampler = Bindless::GetSampler( g_nMyBindlessSamplerId );
  float3 vColor = g_tColor.Sample( sBindlessSampler, i.vTextureCoords.xy ).rgb;

  return float4( vColor, 1.0f );
}

If your bindless resources index is going to vary across threads within a wave (this could happen if passed from a vertex input) you need to explicitly mark it as so otherwise you'll get undefined behavior.

Texture2D texture = Bindless::GetTexture2D( NonUniformResourceIndex( i.TextureIndex ) );

Undefined behavior from a non uniform resource index

Source: Facepunch/sbox-docs (CC-BY-4.0) · updated 2026-07-30. Read it rendered on the official docs.

More in Rendering

AmbientLight
Animated Effects
Attributes and Variables
BeamEffect
Beams
C# Shader Nodes
Classes
Command Lists
← All documentation

Community wiki — not affiliated with Facepunch Studios. For the latest and most authoritative information, see the official developer wiki.