Skip to content

Texture and sampler pairing

A Direct3D 11 program reads a texture register t<n> with an explicit sampler register s<m> in every sample, gather4 and lod instruction; the texture’s contents come from the resource bound at the draw, and the filter and addressing from the sampler state bound at s<m>. The program fixes two things: which sampler register goes with which texture register, and whether the read compares against a reference (a comparison sampler). A load (ld, bufferLoad, …) uses no sampler. Sekiro’s programs are Shader Model 5 DXBC and Elden Ring’s Shader Model 6 DXIL; the two pair their registers alike.

In a program, a texture register is read through exactly one sampler register, in all but 26 of the 192,998 (program, texture register) pairs that are sampled.

Sekiro Elden Ring
distinct programs (by file bytes) 9,371, of which 9,204 read a texture: 6,700 pixel, 2,381 vertex, 70 compute, 38 domain, 15 geometry 13,423, of which 12,754 read a texture: 7,856 pixel, 4,694 vertex, 143 compute, 36 domain, 25 geometry
programs that sample a texture 6,876 8,136
texture registers read (sampled or loaded) 106,399 123,339
sampled 89,699 103,299
sampled through one sampler register 89,688 103,284
sampled through two 11 (in 10 programs) 15 (in 13 programs)
only loaded, no sampler 16,700 20,040
sampled and loaded 1 1

Observed · the programs extracted from each game’s shader bundles, each compared with Microsoft’s own disassembly of it The register spaces of all these registers are 0. Observed Every texture and sampler handle of a Shader Model 6 sampling call is made by a createHandle call in the same function; the 3 calls in Elden Ring whose texture register is computed (a texture array indexed in two compute programs) are loads, and no sampler register is computed. Observed · Elden Ring

An instruction compares exactly when its sampler register is declared a comparison sampler: sample_c_lz (Sekiro, 5,585 calls) and sampleCmpLevelZero (Elden Ring, 11,116) on a comparison sampler, every other sample and gather (101,668 and 132,245 calls) on a default sampler. Observed · every sampling call The comparison sampler is always register s12, and no other register is a comparison sampler. Observed sample_b, sampleBias, sampleCmp and textureGatherCmp do not occur; gather4 and textureGather are rare (23 and 27 calls).

The programs name their samplers, and the name of a register is the same in every program that declares it:

Register Name Sekiro programs Elden Ring programs
s6 SS_WrapPoint 3,777 4,551
s7 SS_ClampPoint 394 554
s8 SS_WrapLinear 5,051 5,945
s9 SS_ClampLinear 3,829 4,482
s10 SS_WrapAniso 68 0
s11 SS_ClampAniso 0 106
s12 SS_Shadow (comparison) 1,414 1,463

Some programs spell the name differently (g_ClampPointSampler, g_samLinearWrap, s_WrapPoint, …) and none declares another kind of sampler at these registers. Observed · the Resource Bindings table of every program The registers s0 to s4 are the material samplers of the surface programs: SS_DiffuseMap s0 (5,582 Sekiro and 6,856 Elden Ring programs), SS_SpecularMap s1, SS_BumpMap s2, SS_MaskMap s3 and SS_DisplacementMap s4. Other programs reuse them under their own names: the screen-space ambient occlusion, depth-of-field and bilateral programs declare SS_ClampPoint as s2 and SS_ClampLinear as s3, and SS_WhiteBorderPoint is s4. For these the name carries the meaning and the register does not. Observed The texture register a given sampler register pairs with differs from program to program: the two numbers are equal in 8,955 of Sekiro’s 89,688 single-sampler pairs and 10,003 of Elden Ring’s 103,284. Observed The filter and addressing behind each name are set by the engine when it binds the state and are not stored in the programs. In frame recordings the engine’s registers hold one fixed state each, the same in both games: Observed[every screen-space and compute step of 9 Sekiro and 7 Elden Ring recorded frames]

Register State
s6 point filter, wrap
s7 point filter, clamp
s8 linear filter (0x15), wrap
s9 linear filter, clamp
s12 comparison filter 0x94 (linear minification and magnification, point mip), comparison GREATER (the depth is reversed), border addressing with border colour 0, 0, 0, 0, LOD range 0 to 0

No step binds two different states at one of these registers, over all its runs in the recorded frames. Observed The material registers s0 to s4 vary from program to program. Observed

The 26 exceptions are the same texture read through a point sampler and a linear sampler (22) or through the point sampler and the comparison sampler (4). All are pixel programs. Observed

Program Texture (register) Samplers and calls
GXScreenSpaceCascadedShadow_PCF4_Fade, _PCF9_Fade, _Soft_Fade g_PrevScreenTexture (t2) SS_ClampPoint 1, SS_ClampLinear 1
GXScreenSpaceCascadedShadow_Soft, _Soft_Fade g_ShadowTexture (t32) SS_Shadow 16 (comparison), SS_ClampPoint 4
GXSssBlurX, GXSssBlurY g_LightAccTexture (t36) SS_ClampPoint 1, SS_ClampLinear 16
LensSimu2_BlurCoC g_CoCTex (t2) SS_ClampPoint 1, SS_ClampLinear 8 (Sekiro) or 1 (Elden Ring)
ScreenSpaceAO2_ReProj, _SeparatedReProj, _WithNormalReProj; only in Elden Ring CopyExternalAOTextureReProj g_PrevAOTex (t3) SS_ClampPoint 1, SS_ClampLinear 1
only in Elden Ring SampleBilateralDepthBlurTwo1, Two2 g_SourceTex (t0), and in Two2 g_SourceTex2 (t3) SS_ClampLinear 1, SS_ClampPoint 4

Observed · all 26 slots In Sekiro’s g_PrevScreenTexture and g_PrevAOTex programs the two reads use one coordinate register and take different components of the result (one component through the linear sampler, three through the point sampler); in Elden Ring’s ScreenSpaceAO2, CopyExternalAOTextureReProj and _Soft_Fade programs they use identical coordinate values. In GXSssBlurX/Y and LensSimu2_BlurCoC the point-sampled read is at the interpolated texture coordinate and the linear reads are at computed coordinates. In the two GXScreenSpaceCascadedShadow_Soft programs the shadow map is read four times as plain depth and sixteen times by comparison. Observed

only in Sekiro Sekiro binds the anisotropic registers with the others, as one table: every draw and dispatch of five frames at the Ashina Castle gate that binds samplers (12,914 bindings of each register, vertex, pixel, domain and compute programs) has s10 and s11 set, though no Sekiro program declares s11. Observed · Sekiro · five captured frames

Register Outside the shadow maps In the shadow-map passes Confidence
s5 anisotropic filter (0x55), anisotropy 16, wrap linear filter (0x15), wrap Observed · Sekiro
s10 anisotropic filter (0x55), anisotropy 16, wrap linear filter (0x15), wrap Observed · Sekiro
s11 anisotropic filter (0x55), anisotropy 16, clamp linear filter (0x15), clamp Observed · Sekiro

The states of s6 to s9 and s12 are the same in these frames as in the table above, with no other state at those registers. Observed · Sekiro · five captured frames SS_ClampAniso at s11 in Elden Ring’s programs is therefore the second register of the same pair. Inferred The anisotropy of 16 may follow the game’s graphics settings, which these frames do not vary. Inferred · Sekiro

Elden Ring binds the same three registers with the others: every draw and dispatch that binds samplers in ten Stormveil gate frames has s5, s10 and s11 set, in vertex, pixel and compute stages. Observed · Elden Ring · ten captured frames Outside the shadow and depth passes (render targets of 2048², 4096² and 64² texels) s5 and s10 hold the anisotropic wrap state and s11 the anisotropic clamp state, with the same filter (0x55), anisotropy 16 and border colour (0.5, 0.5, 0.5, 0) as Sekiro’s; inside those passes all three hold the linear filter (0x15). Observed · Elden Ring · ten captured frames No drawn Elden Ring program declares s5 or s10, and one declares s11: M[AMSN_V][Mb4][3Mask][Ov_N2]_Gbuf, one to four draws a frame, which reads Texture2D_0 (Mask3Map, t0) and Texture2D_7 (NormalMap_0, t10) through SS_ClampAniso. Observed · Elden Ring · ten captured frames

What do the anisotropic registers bind in Elden Ring, and who reads them?

The state of the three registers is read from the frames, and the one drawn reader of s11 is named. No drawn program reads s5 or s10, so their readers, if any, are not in these frames, and no frame varies the graphics settings that may set the anisotropy of 16.