yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
efda04f3c
master
1// texture.slang 2 3// This class encapsulates a differentiable texture object that uses 4// hardware sampling for the primal version, but substitutes a reference 5// interpolation implementation to generate backward pass. 6// 7// This specific implementation also makes the choice to use fast fixed point 8// atomics to accumulate the derivative (suitable for this example, but maybe 9// not in general) 10// 11struct DifferentiableTexture 12{ 13 RWStructuredBuffer<int> accumulateBuffer; // Per-mip-level accumulate buffer 14 Texture2D texture; // Hardware texture handle. 15 float minLOD; 16 17 [BackwardDerivative(bwd_LoadTexel)] 18 float4 LoadTexel(int3 location, constexpr int2 offset, uint dLayerW, uint dMipOffset) 19 { 20 return texture.Load(location, offset); 21 } 22 23 void bwd_LoadTexel(int3 location, constexpr int2 offset, uint dLayerW, uint dMipOffset, float4 val) 24 { 25 // Ignore alpha dimension for this example.. 26 int4 uval = int4(int3(val.xyz * 65536), 1); 27 28 // We'll use fast fixed point atomics instead of floats. 29 InterlockedAdd(accumulateBuffer[dMipOffset + ((uint)location.y * dLayerW + (uint)location.x) * 4 + 0], uval.x); 30 InterlockedAdd(accumulateBuffer[dMipOffset + ((uint)location.y * dLayerW + (uint)location.x) * 4 + 1], uval.y); 31 InterlockedAdd(accumulateBuffer[dMipOffset + ((uint)location.y * dLayerW + (uint)location.x) * 4 + 2], uval.z); 32 InterlockedAdd(accumulateBuffer[dMipOffset + ((uint)location.y * dLayerW + (uint)location.x) * 4 + 3], uval.w); 33 } 34 35 // Software reference implementation of linear filtering. 36 [BackwardDifferentiable] 37 float4 sampleTexture_linear(uint lod, float2 uv, uint w, uint h) 38 { 39 w >>= lod; 40 h >>= lod; 41 uv = uv - no_diff(floor(uv)); 42 float2 loc = uv * float2(w, h) - float2(0.5); 43 float x0 = no_diff(floor(loc.x)); 44 float y0 = no_diff(floor(loc.y)); 45 float fracX = loc.x - x0; 46 float fracY = loc.y - y0; 47 float x1 = x0 + 1; 48 float y1 = y0 + 1; 49 if (x0 < 0) x0 += w; 50 if (y0 < 0) y0 += h; 51 if (x1 >= w) x1 -= w; 52 if (y1 >= h) y1 -= h; 53 float weight0 = 1.0f - fracY; 54 float weight1 = fracY; 55 float weight00 = weight0 * (1.0f - fracX); 56 float weight01 = weight0 * fracX; 57 float weight10 = weight1 * (1.0f - fracX); 58 float weight11 = weight1 * fracX; 59 60 uint dLayerW = w >>= lod; 61 var offset = mipOffset[lod / 4][lod % 4]; 62 return LoadTexel(int3(int(x0), int(y0), int(lod)), int2(0), dLayerW, offset) * weight00 + 63 LoadTexel(int3(int(x1), int(y0), int(lod)), int2(0), dLayerW, offset) * weight01 + 64 LoadTexel(int3(int(x0), int(y1), int(lod)), int2(0), dLayerW, offset) * weight10 + 65 LoadTexel(int3(int(x1), int(y1), int(lod)), int2(0), dLayerW, offset) * weight11; 66 } 67 68 // Software reference implementation of trilinear filtering. 69 [BackwardDifferentiable] 70 float4 sampleTexture_trilinear(uint w, uint h, uint levels, float2 uv, float2 dX, float2 dY) 71 { 72 dX = dX * float2(w, h); 73 dY = dY * float2(w, h); 74 75 // Isotropic filter. 76 float lengthX = length(dX); 77 float lengthY = length(dY); 78 float LOD = log2(max(lengthX, lengthY)); 79 float maxLOD = levels - 1; 80 float clampedLOD = max(minLOD, (min(maxLOD, LOD))); 81 82 float lodFrac = clampedLOD - no_diff(floor(clampedLOD)); 83 uint lod0 = (uint)floor(clampedLOD); 84 uint lod1 = min(levels - 1, lod0 + 1); 85 float weightLod0 = 1.0 - lodFrac; 86 float weightLod1 = lodFrac; 87 88 let v0 = sampleTexture_linear(lod0, uv, w, h) * weightLod0; 89 let v1 = sampleTexture_linear(lod1, uv, w, h) * weightLod1; 90 return v0 + v1; 91 } 92 93 // Note that there is no need to mark this [BackwardDifferentiable] since it has a substitute 94 // that is marked [BackwardDifferentiable]. The compiler automatically considers a call to 95 // sample() to be differentiable. 96 // 97 static float4 sample(DifferentiableTexture t, SamplerState s, float2 uv, float2 dX, float2 dY) 98 { 99 return t.texture.Sample(s, uv); 100 } 101 102 // Software reference implementation of DifferentiableTexture.sample (trilinear only in this example) 103 [PrimalSubstituteOf(DifferentiableTexture.sample)] 104 [BackwardDifferentiable] 105 static float4 sample_reference_impl(DifferentiableTexture t, SamplerState s, float2 uv, float2 dX, float2 dY) 106 { 107 uint w; 108 uint h; 109 uint levels; 110 t.texture.GetDimensions(0, w, h, levels); 111 return t.sampleTexture_trilinear(w, h, levels, uv, dX, dY); 112 } 113} 114 115cbuffer Uniforms 116{ 117 float4x4 modelViewProjection; 118 uint4 mipOffset[16]; 119 120 Texture2D texRef; 121 SamplerState sampler; 122 DifferentiableTexture bwdTexture; 123} 124 125struct AssembledVertex 126{ 127 float3 position : POSITION; 128}; 129 130struct Fragment 131{ 132 float4 color; 133}; 134 135struct VertexStageOutput 136{ 137 float2 uv : UV; 138 float4 sv_position : SV_Position; 139}; 140 141[BackwardDifferentiable] 142float4 shadeFragment(float2 uv) 143{ 144 uv = uv * 2; 145 146 // Compute fragment differentials using shader intrinsics. 147 float2 dX = no_diff ddx_coarse(uv); 148 float2 dY = no_diff ddy_coarse(uv); 149 150 float3 color = DifferentiableTexture.sample(bwdTexture, sampler, uv, dX, dY).xyz; 151 return float4(color, 1.0); 152} 153 154[BackwardDifferentiable] 155float3 loss(no_diff float2 uv, no_diff float4 screenPos) 156{ 157 float3 refColor = (no_diff texRef.Load(int3(int2(screenPos.xy), 0))).xyz; 158 float3 rs = shadeFragment(uv).xyz - refColor; 159 rs *= rs; 160 return rs; 161} 162 163[shader("vertex")] 164VertexStageOutput vertexMain( 165 AssembledVertex assembledVertex) 166{ 167 VertexStageOutput output; 168 169 float3 position = assembledVertex.position; 170 171 output.uv = position.xy; 172 output.sv_position = mul(modelViewProjection, float4(position, 1.0)); 173 174 return output; 175} 176 177float3 sqr(float3 v) { return v * v; } 178 179[shader("fragment")] 180float4 fragmentMain( 181 float2 uv : UV) : SV_Target 182{ 183 return shadeFragment(uv); 184} 185 186[shader("fragment")] 187float4 diffFragmentMain( 188 float2 uv : UV, 189 float4 screenPos : SV_POSITION) : SV_Target 190{ 191 __bwd_diff(loss)(uv, screenPos, float3(1.0)); 192 return float4(loss(uv, screenPos), 1.0); 193}