includes = [ "core/stingray_renderer/shader_libraries/common.shader_source"]

hlsl_shaders = {

	decompress_linearize = {
	
		stage_conditions = {
			compute = "true"
		}
		
		
		
		code = """
			  
			#define Depth __depth_input_texture0
			#define HTile __htile_input_texture1
			#define LinearZ	input_texture2 
			
			Texture2D<float> Depth : register(t0);
			ByteAddressBuffer HTile : register(t1);
			RWTexture2D<float> LinearZ : register(u0);
			
			cbuffer global_camera
			{
				float3 camera_near_far;
			}
			
			
			//cbuffer ConstantBuffer : register(b0)
			//{
			//	float ZMagic;				// (zFar - zNear) / zNear
			//	float ZClear;
			//	uint2 TiledDimensions;		// For 1080p, this is 240x136
			//}
			
			#define ZMagic ((camera_near_far.y / camera_near_far.x) / camera_near_far.x)
			#define TiledDimensions uint2(208, 120)
			#define ZClear (1.f)
			
			#define DEPTH_UINT_COUNT (64 * 4)
			groupshared uint sharedDepthData[DEPTH_UINT_COUNT];


			// the coord for the given micro tile index
			uint2 PixelCoordFromLinearIndex(uint element)
			{
				return uint2(
					(element & 1) >> 0 | (element & 4) >> 1 | (element & 16) >> 2,
					(element & 2) >> 1 | (element & 8) >> 2 | (element & 32) >> 3);
			}

			uint2 SwizzleLinearIndex(uint element)
			{
				const uint2 lookup[64] =
				{
					PixelCoordFromLinearIndex(8 * 0 + 0),
					PixelCoordFromLinearIndex(8 * 0 + 1),
					PixelCoordFromLinearIndex(8 * 0 + 2),
					PixelCoordFromLinearIndex(8 * 0 + 3),
					PixelCoordFromLinearIndex(8 * 0 + 4),
					PixelCoordFromLinearIndex(8 * 0 + 5),
					PixelCoordFromLinearIndex(8 * 0 + 6),
					PixelCoordFromLinearIndex(8 * 0 + 7),
					
					PixelCoordFromLinearIndex(8 * 1 + 0),
					PixelCoordFromLinearIndex(8 * 1 + 1),
					PixelCoordFromLinearIndex(8 * 1 + 2),
					PixelCoordFromLinearIndex(8 * 1 + 3),
					PixelCoordFromLinearIndex(8 * 1 + 4),
					PixelCoordFromLinearIndex(8 * 1 + 5),
					PixelCoordFromLinearIndex(8 * 1 + 6),
					PixelCoordFromLinearIndex(8 * 1 + 7),
					
					PixelCoordFromLinearIndex(8 * 2 + 0),
					PixelCoordFromLinearIndex(8 * 2 + 1),
					PixelCoordFromLinearIndex(8 * 2 + 2),
					PixelCoordFromLinearIndex(8 * 2 + 3),
					PixelCoordFromLinearIndex(8 * 2 + 4),
					PixelCoordFromLinearIndex(8 * 2 + 5),
					PixelCoordFromLinearIndex(8 * 2 + 6),
					PixelCoordFromLinearIndex(8 * 2 + 7),
					
					PixelCoordFromLinearIndex(8 * 3 + 0),
					PixelCoordFromLinearIndex(8 * 3 + 1),
					PixelCoordFromLinearIndex(8 * 3 + 2),
					PixelCoordFromLinearIndex(8 * 3 + 3),
					PixelCoordFromLinearIndex(8 * 3 + 4),
					PixelCoordFromLinearIndex(8 * 3 + 5),
					PixelCoordFromLinearIndex(8 * 3 + 6),
					PixelCoordFromLinearIndex(8 * 3 + 7),
					
					PixelCoordFromLinearIndex(8 * 4 + 0),
					PixelCoordFromLinearIndex(8 * 4 + 1),
					PixelCoordFromLinearIndex(8 * 4 + 2),
					PixelCoordFromLinearIndex(8 * 4 + 3),
					PixelCoordFromLinearIndex(8 * 4 + 4),
					PixelCoordFromLinearIndex(8 * 4 + 5),
					PixelCoordFromLinearIndex(8 * 4 + 6),
					PixelCoordFromLinearIndex(8 * 4 + 7),
					
					PixelCoordFromLinearIndex(8 * 5 + 0),
					PixelCoordFromLinearIndex(8 * 5 + 1),
					PixelCoordFromLinearIndex(8 * 5 + 2),
					PixelCoordFromLinearIndex(8 * 5 + 3),
					PixelCoordFromLinearIndex(8 * 5 + 4),
					PixelCoordFromLinearIndex(8 * 5 + 5),
					PixelCoordFromLinearIndex(8 * 5 + 6),
					PixelCoordFromLinearIndex(8 * 5 + 7),
					
					PixelCoordFromLinearIndex(8 * 6 + 0),
					PixelCoordFromLinearIndex(8 * 6 + 1),
					PixelCoordFromLinearIndex(8 * 6 + 2),
					PixelCoordFromLinearIndex(8 * 6 + 3),
					PixelCoordFromLinearIndex(8 * 6 + 4),
					PixelCoordFromLinearIndex(8 * 6 + 5),
					PixelCoordFromLinearIndex(8 * 6 + 6),
					PixelCoordFromLinearIndex(8 * 6 + 7),
					
					PixelCoordFromLinearIndex(8 * 7 + 0),
					PixelCoordFromLinearIndex(8 * 7 + 1),
					PixelCoordFromLinearIndex(8 * 7 + 2),
					PixelCoordFromLinearIndex(8 * 7 + 3),
					PixelCoordFromLinearIndex(8 * 7 + 4),
					PixelCoordFromLinearIndex(8 * 7 + 5),
					PixelCoordFromLinearIndex(8 * 7 + 6),
					PixelCoordFromLinearIndex(8 * 7 + 7),
				};

				return lookup[element];
			}			
						
			// The whole pixel coordinate, ignoring subsampling, ranges from 0 to 7
			// (in two dimensions.)
			// The fixed point grid ranges from -64 to +63 with whole values representing
			// sample locations.  Pixels are 16 samples wide with tiles being 8 pixels wide.
			// Pixel centers are naturally spaced 16 samples apart in the range [-56, +56].
			int2 GetTileRelativeCoord( uint2 pixelCoord )
			{
				return pixelCoord * 16 - 56;
			}
			
			uint ZLayoutForMask( uint zMask, uint sampleCount = 1 )
			{
				uint zPlaneCount = zMask;
				if (zPlaneCount >= 11)
					zPlaneCount += 2;
				else if (zPlaneCount >= 9)
					zPlaneCount += 1;

				uint zPlaneChunkSize = zPlaneCount * 12;

				uint pMaskElementSizeBits = zMask == 0 ? 0 : firstbithigh(zMask - 1) + 1;

				uint pMaskChunkSize = 8 * pMaskElementSizeBits * sampleCount;
				uint paddedZPlaneChunkSize = (zPlaneChunkSize + 0x1f) & ~0x1f;
				uint paddedPMaskChunkSize = (pMaskChunkSize + 0x1f) & ~0x1f;
				uint packedSize = (zPlaneChunkSize + pMaskChunkSize + 0x1f) & ~0x1f;
				uint paddedSize = paddedZPlaneChunkSize + paddedPMaskChunkSize;
				bool pack = packedSize < paddedSize;

				uint pMaskOffset = pack ? zPlaneChunkSize : paddedZPlaneChunkSize;
				if ((pMaskOffset & 0xff) + pMaskChunkSize - 1 > 0xff)
					pMaskOffset = (pMaskOffset + pMaskChunkSize - 1) & ~0xff;
				else
					pMaskOffset = (pMaskOffset + 0x7) & ~0x7;
				
				uint maxLoadIndex_WithoutZDataOffset = (pMaskOffset + pMaskChunkSize) >> 2;

				//uint zPlaneCount : 5;
				//uint pMaskElementSizeBits : 3;
				//uint is32ByteChunk : 1;
				//uint pMaskOffsetElement : 7;
				//uint maxLoadIndex_WithoutZDataOffset : 8;

				uint ld = zPlaneCount | pMaskElementSizeBits << 5;
				ld |= (packedSize <= 32 ? 256 : 0);
				ld |= (pMaskOffset / 4) << 9;
				ld |= maxLoadIndex_WithoutZDataOffset << 16;

				return ld;
			}
			
			
			uint getZDataLayout( uint zMask, uint sampleCount = 1 )
			{
				const uint lookup[16] =
				{
					ZLayoutForMask(0x0, sampleCount),	// Clear
					ZLayoutForMask(0x1, sampleCount),
					ZLayoutForMask(0x2, sampleCount),
					ZLayoutForMask(0x3, sampleCount),
					ZLayoutForMask(0x4, sampleCount),
					ZLayoutForMask(0x5, sampleCount),
					ZLayoutForMask(0x6, sampleCount),
					ZLayoutForMask(0x7, sampleCount),
					ZLayoutForMask(0x8, sampleCount),
					ZLayoutForMask(0x9, sampleCount),
					ZLayoutForMask(0xA, sampleCount),
					ZLayoutForMask(0xB, sampleCount),
					ZLayoutForMask(0xC, sampleCount),
					ZLayoutForMask(0xD, sampleCount),
					ZLayoutForMask(0xE, sampleCount),
					ZLayoutForMask(0xF, sampleCount),	// Uncompressed
				};

				return lookup[zMask];
			}


			// P4_16x16
			uint GetHTileAddress(uint2 tileCoord, uint2 numTiles)
			{
				const uint pipeCount = 4;
				// cache line tiling?
				const bool linearMode = true;

				// in units of tiles
				const uint clWidth = 64;
				const uint clHeight = 32;
				const uint clSize = 512; // per pipe
				
				const uint clCountX = numTiles.x / clWidth; // clPitch
				//const uint clCountY = numTiles.y / clHeight;
				
				const uint slicePitch = numTiles.x * numTiles.y / pipeCount;
				const uint macroPitch = (linearMode ? numTiles.x : clWidth) / 4;
				
				// cache line
				uint clX = tileCoord.x / clWidth;
				uint clY = tileCoord.y / clHeight;
				
				// for 2D array and 3D textures (including cube maps)
				uint tileSlice = 0; // tileZ
				uint sliceOffset = slicePitch * tileSlice;
				
				// cache line tiling (unused for linearMode)
				uint clOffset = linearMode ? 0 : (clX + clCountX * clY) * clSize;
				
				uint tileX0 = tileCoord.x & 1;
				uint tileY0 = tileCoord.y & 1;
				uint tileX1 = (tileCoord.x >> 1) & 1;
				uint tileY1 = (tileCoord.y >> 1) & 1;
				// specific to pipe config
				uint tileOffset = (tileX1 ^ tileY0) | (tileX1 << 1);
				//uint tileOffset = (tileX1 ^ tileY0) | (tileCoord.x & 2);

				// specific to pipe config
				uint pipe = (tileX0 ^ tileY0 ^ tileX1) | ((tileX1 ^ tileY1) << 1);
				//uint pipe = (tileX0 | (tileCoord.y & 2)) ^ tileOffset;
				//uint pipe = (tileX0 | tileY1 << 1) ^ tileOffset;

				// macro (4x4 tile) tiling
				uint macroX = (linearMode ? tileCoord.x : (tileCoord.x % clWidth)) / 4;
				uint macroY = (linearMode ? tileCoord.y : (tileCoord.y % clHeight)) / 4;
				uint macroOffset = (macroX + macroY * macroPitch) << 2 | tileOffset;
				
				uint tileIndex = sliceOffset + clOffset + macroOffset;
				uint tileByteOffset = tileIndex << 2;
				
				// The 2-bit pipe value gets inserted into the tileByteOffset at bits 8 and 9
				return (tileByteOffset & ~0xff) << 2 | (pipe << 8) | (tileByteOffset & 0xff);
			}

			
			uint2 GetZMaskAndStencilState(ByteAddressBuffer HTileBuffer, uint2 NumTiles, uint2 TileCoord)
			{
				uint HTileValue = HTileBuffer.Load(GetHTileAddress(TileCoord, NumTiles));
				uint ZMask = __XB_UBFE(4, 0, HTileValue);
				uint StencilState = __XB_UBFE(2, 8, HTileValue);
				return uint2(ZMask, StencilState);
			}
			
			uint GetZMask(ByteAddressBuffer HTileBuffer, uint2 NumTiles, uint2 TileCoord)
			{
				return GetZMaskAndStencilState(HTileBuffer, NumTiles, TileCoord).x;
			}

			// If extendedPrecision = true, uses the full 28 bits of the x and y slope values
			float GetDepthFromZPlane(uint3 zPlane, int2 pixelPos, bool extendedPrecision = false )
			{
				int depth = __XB_IBFE(31, 0, zPlane.z);
				depth = __XB_MadI24(zPlane.x >> 4, pixelPos.x, depth);
				depth = __XB_MadI24(zPlane.y, pixelPos.y, depth);

				if (extendedPrecision)
				{
					// Use the four remaining bits of slope
					int slopeX = __XB_BFI(0xF, zPlane.x, __XB_IBFE(1, 27, zPlane.x));
					int slopeY = __XB_BFI(0xF, zPlane.x >> 28, __XB_IBFE(1, 23, zPlane.y));
					depth += (int)__XB_MadI24(slopeX, pixelPos.x, (int)__XB_MulI24(slopeY, pixelPos.y)) >> 4;
				}

				float scale = asfloat(((zPlane.y >> 24) - 22) << 23);
				return saturate(scale * (float)depth);
			}

			float GetZValue(uint zDataLayout, uint2 globalPixel, uint threadIndex,
				uint numSamples = 1, uint sampleIdx = 0, int2 sampleOffset = 0 )
			{
				uint zPlaneCount = __XB_UBFE(5, 0, zDataLayout);
				uint pMaskElementSizeBits = __XB_UBFE(3, 5, zDataLayout);
				uint is32ByteChunk = __XB_UBFE(1, 8, zDataLayout);
				uint pMaskOffsetElement = __XB_UBFE(7, 9, zDataLayout);

				uint zDataOffset = (is32ByteChunk << 3) & globalPixel.x;

				GroupMemoryBarrierWithGroupSync();

				uint zPlaneIndex = 0;
				if (zPlaneCount > 1)
				{
					// not sure why this is needed (but it is)
					if (numSamples > 1)
						pMaskElementSizeBits = max(pMaskElementSizeBits, 2);

					uint element = __XB_MadU24(threadIndex, numSamples, sampleIdx);
					uint bitOffset = __XB_MulU24(element, pMaskElementSizeBits);
					uint PlaneIndexOffset = zDataOffset + pMaskOffsetElement + (bitOffset >> 5);

					bitOffset &= 31;

					zPlaneIndex = sharedDepthData[PlaneIndexOffset] >> bitOffset;
					uint bitsRead = 32 - bitOffset;
					if (pMaskElementSizeBits > bitsRead)
						zPlaneIndex |= sharedDepthData[PlaneIndexOffset + 1] << bitsRead;

					zPlaneIndex &= __XB_BFM(pMaskElementSizeBits, 0);
				}

				uint PlaneOffset = __XB_MadU24(zPlaneIndex, 3, zDataOffset);
				uint3 zPlane = uint3(sharedDepthData[PlaneOffset], sharedDepthData[PlaneOffset + 1], sharedDepthData[PlaneOffset + 2]);

				// pixelCoord is [0, 7]
				// pixelPos is [-4.0, 4.0) represented as S3.4 fixed precision
				// Pixel centers are at -3.5, -2.5, ..., 2.5, 3.5
				int2 pixelPos = GetTileRelativeCoord(globalPixel & 0x7);

				// Sub-sample offsets are in fractional terms ranging from -0.5f to +0.5f.
				// They must be converted to fixed precision with 4 fractional bits.
				if (numSamples > 1 && (int)zPlane.z < 0)	// The high bit indicates MSAA is enabled
					pixelPos += sampleOffset;

				return GetDepthFromZPlane(zPlane, pixelPos);
			}
			
			float DecompressDepth(Texture2D<float> depthSrc,
				uint zMask, uint2 st, uint threadIndex, float depthClear = 0.0 )
			{
				float PreFetchDepth = depthSrc[st];

				// Clear
				if (zMask == 0)
					return depthClear;

				// Uncompressed
				if (zMask == 15)
					return PreFetchDepth;

				// Compressed
				sharedDepthData[threadIndex] = asuint(PreFetchDepth);

				return GetZValue(getZDataLayout(zMask), st, threadIndex);
			}
			
			
			inline float linearize_depth(float clip_depth) {
				return (camera_near_far.y*camera_near_far.x)/(camera_near_far.y - clip_depth*(camera_near_far.y-camera_near_far.x));
			}
			
			float DecompressAndLinearize( uint2 tile, uint2 localCoord, uint linearIndex )
			{
				uint zMask = GetZMask(HTile, TiledDimensions, tile);
				uint2 st = tile << 3 | localCoord;
				float depth = DecompressDepth(Depth, zMask, st, linearIndex, ZClear );
				//float dist = 1.0 / (ZMagic * depth + 1.0);
				float dist = linearize_depth(depth);
				LinearZ[st] = dist;
				return dist;
			}
			
			
			
			
			[numthreads( 8, 8, 1 )]
			void cs_main( uint3 Gid : SV_GroupID, uint GI : SV_GroupIndex, uint3 GTid : SV_GroupThreadID, uint3 DTid : SV_DispatchThreadID )
			{
				uint linearIndex = GI & 63;
				uint2 pixelCoord = SwizzleLinearIndex(linearIndex);
				uint destIdx = pixelCoord.x + pixelCoord.y * 16;
				uint2 tile1 = Gid.xy << 1;

				DecompressAndLinearize(tile1 | uint2(0, 0), pixelCoord, linearIndex);
				DecompressAndLinearize(tile1 | uint2(1, 0), pixelCoord, linearIndex);
				DecompressAndLinearize(tile1 | uint2(0, 1), pixelCoord, linearIndex);
				DecompressAndLinearize(tile1 | uint2(1, 1), pixelCoord, linearIndex);
			}
		
		"""
	}
	
	
	cs_downsample = {
		
		includes = [ "common" ]
		
		stage_conditions = {
			compute = "true"
		}
		
		samplers = {
			input_texture0 = { sampler_states = "clamp_linear" }
		}
		
		code = """
			#if defined(THREADS_4)
				#define THREADX 4
				#define THREADY 4
			#endif
			#if defined(THREADS_8)
				#define THREADX 8
				#define THREADY 8
			#endif
			#if defined(THREADS_16)
				#define THREADX 16
				#define THREADY 16
			#endif
			
			#define GROUPTHREADS THREADX * THREADY
			
			#define GATHER_RED(s, uv, offset) s.tex.GatherRed(s.state, uv, offset)
			#define GATHER_GREEN(s, uv, offset) s.tex.GatherGreen(s.state, uv, offset)
			#define GATHER_BLUE(s, uv, offset) s.tex.GatherBlue(s.state, uv, offset)
			#define GATHER_ALPHA(s, uv, offset) s.tex.GatherAlpha(s.state, uv, offset)
			
			#define Input input_texture0
			#define Output input_texture1
			
			#define InTex __tex_input_texture0
			#define Samp __samp_input_texture0
			
			#define DECLARE_SAMPLER_2D_REG(name, tr, sr)	\
					Texture2D TEXTURE_NAME(name) : register(tr); \
					SamplerState SAMPLER_NAME(name) : register(sr); \
					static Sampler2D name = { TEXTURE_NAME(name), SAMPLER_NAME(name) };
			
			
			cbuffer c0
			{
				float2 input_texture0_size_inv;
			};
			
			#if defined(RENDERER_D3D11)
				DECLARE_SAMPLER_2D(input_texture0);
			#else 
				#if defined(POINT)
					DECLARE_SAMPLER_2D_REG(input_texture0, t0, s0);
				#endif
				#if defined(LINEAR)
					DECLARE_SAMPLER_2D_REG(input_texture0, t0, s1);
				#endif
			#endif
			
			RWTexture2D<float4> Output: register(u0);
			
			#if defined(GATHER)
				groupshared float4 shared_mem[GROUPTHREADS];
			#else
				groupshared float3 shared_mem[GROUPTHREADS];
			#endif
			
			static const float inv_div = 1.0 / 16.0;
			
			[numthreads(THREADX, THREADY, 1)]
			void cs_main(uint3 Gid : SV_GroupID, uint3 DTId : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID, uint GI : SV_GroupIndex )
			{
				const float4 one = float4(1,1,1,1);
				
				#if defined(WRITE1)
					// Every thread writes, 4x4 Threads, 100x225 Thread Groups
					#if defined(LOAD)
						float3 rgb = InTex.Load(DTId * 4);
					#endif
					#if defined(SAMPLE)
						float2 uv = float2(DTId.xy * 4) * input_texture0_size_inv;
						float3 rgb = TEX2DLOD(input_texture0, uv, 0);
					#endif
					Output[DTId.xy] = float4(rgb, 0);
				#endif
				
			
				#if defined(WRITE2)
					// Every other thread writes, avarage every other pixel, Load, 8x8 Threads, 100x225 Thread Groups
					#if defined(LOAD)
						float3 rgb = InTex.Load(DTId * 2);
					#endif
					#if defined(SAMPLE)
						float2 uv = float2(DTId.xy * 2) * input_texture0_size_inv;
						float3 rgb = TEX2DLOD(input_texture0, uv, 0);
					#endif
					shared_mem[GI] = rgb;
					GroupMemoryBarrierWithGroupSync();
					bool2 mask = (GTid.xy & uint2(1,1));
					shared_mem[GI] += mask.y ? 0 : shared_mem[GI + THREADX];
					shared_mem[GI] += mask.x ? 0 : shared_mem[GI + 1];
					
					if(!any(mask)) {
						Output[DTId.xy * 0.5] = float4(shared_mem[GI] * (1.0 / 4.0), 0);
					}
				#endif
				
				#if defined(WRITE4)
					// Every forth thread writes, avarage every pixel, 16x16 Threads, 100x225 Thread Groups
					#if defined(LOAD)
						float3 rgb = InTex.Load(DTId);
					#endif
					#if defined(SAMPLE)
						float2 uv = float2(DTId.xy) * input_texture0_size_inv;
						float3 rgb = TEX2DLOD(input_texture0, uv, 0);
					#endif
					shared_mem[GI] = rgb;
					GroupMemoryBarrierWithGroupSync();
					bool4 mask = (GTid.xyxy & uint4(1,1,3,3));
					shared_mem[GI] += mask.y ? 0 : shared_mem[GI + THREADX * 2];
					shared_mem[GI] += mask.w ? 0 : shared_mem[GI + THREADX];
					shared_mem[GI] += mask.x ? 0 : shared_mem[GI + 1];
					shared_mem[GI] += mask.z ? 0 : shared_mem[GI + 2];
					if(!any(mask)) {
						Output[DTId.xy / 4] = float4(shared_mem[GI] * (1.0 / 16.0), 0);
					}
				#endif
				
				#if defined(GATHER)
					// gather 2x2 pixels per thread, 8x8 Thread groups, 100x225 
					float2 uv = float2(DTId.xy * 2) * input_texture0_size_inv;
					float4 reds = GATHER_RED(input_texture0, uv, uint2(1,1));
					float4 greens = GATHER_GREEN(input_texture0, uv, uint2(1,1));
					float4 blues = GATHER_BLUE(input_texture0, uv, uint2(1,1));
					float4 alphas = GATHER_ALPHA(input_texture0, uv, uint2(1,1));
					shared_mem[GI] = float4(dot(reds, one), dot(greens, one), dot(blues, one), dot(alphas, one));
					GroupMemoryBarrierWithGroupSync();
					bool2 mask = (GTid.xy & uint2(1,1));
					shared_mem[GI] += mask.y ? 0 : shared_mem[GI + THREADX];
					shared_mem[GI] += mask.x ? 0 : shared_mem[GI + 1];
					if(!any(mask)) {
						Output[DTId.xy / 2] = shared_mem[GI] * (1.0 / 16.0);
					}
				#endif
			}
		"""
	}
	
	
}

shaders = {
	
	decompress_linearize = {
		contexts = {
			default = {
				passes_sort_mode="immediate"
				passes = [
					{ hlsl_shader="decompress_linearize" }
				]
			}
		}

		compile = {
			default = [
				{ defines="" platforms = "D3D12" }
			]
		}
	}
	
	cs_downsample = {
		contexts = {
			default = {
				passes_sort_mode="immediate"
				passes = [
					{ hlsl_shader="cs_downsample" }
				]
			}
		}
		
		compile = {
			default = [
				{ defines="" }
			]
		}
	}
}

static_compile= [
	{ shader="decompress_linearize" }
	
	{ if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_4" "WRITE1" "LOAD" "POINT"] }
	{ if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_4" "WRITE1" "SAMPLE" "POINT"] }
	{ if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_4" "WRITE1" "SAMPLE" "LINEAR"] }
	
	{ if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_8" "WRITE2" "LOAD" "POINT"] }
	{ if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_8" "WRITE2" "SAMPLE" "POINT"] }
	{ if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_8" "WRITE2" "SAMPLE" "LINEAR"] }
	
	{ if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_16" "WRITE4" "LOAD" "POINT"] }
	{ if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_16" "WRITE4" "SAMPLE" "POINT"] }
	{ if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_16" "WRITE4" "SAMPLE" "LINEAR"] }
	
	{ if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_8" "GATHER" "SAMPLE" "POINT"] }
	{ if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_8" "GATHER" "SAMPLE" "LINEAR"] }
]