struct FragmentInput
{
	float4 position : SV_Position;
	float2 texCoord : TEXCOORD0;
};

struct ShaderContext
{
	float2 uv;
	float4 sourceColor;
	float2 inputResolution;
	float2 outputResolution;
	float time;
	float utcTimeSeconds;
	float utcOffsetSeconds;
	float startupRandom;
	float frameCount;
	float mixAmount;
	float bypass;
	int sourceHistoryLength;
	int temporalHistoryLength;
	int feedbackAvailable;
};

cbuffer GlobalParams
{
	float gTime;
	float2 gInputResolution;
	float2 gOutputResolution;
	float gUtcTimeSeconds;
	float gUtcOffsetSeconds;
	float gStartupRandom;
	float gFrameCount;
	float gMixAmount;
	float gBypass;
	int gSourceHistoryLength;
	int gTemporalHistoryLength;
	int gFeedbackAvailable;
{{PARAMETER_UNIFORMS}}};

Sampler2D<float4> gVideoInput;
Sampler2D<float4> gLayerInput;
{{SOURCE_HISTORY_SAMPLERS}}{{TEMPORAL_HISTORY_SAMPLERS}}{{FEEDBACK_SAMPLER}}{{TEXTURE_SAMPLERS}}
{{TEXT_SAMPLERS}}
float4 sampleVideo(float2 tc)
{
	return gVideoInput.Sample(tc);
}

float4 sampleLayerInput(float2 tc)
{
	return gLayerInput.Sample(tc);
}

float4 sampleSourceHistory(int framesAgo, float2 tc)
{
	if (gSourceHistoryLength <= 0)
		return sampleVideo(tc);
	int clampedIndex = framesAgo;
	if (clampedIndex < 0)
		clampedIndex = 0;
	if (clampedIndex >= gSourceHistoryLength)
		clampedIndex = gSourceHistoryLength - 1;
	switch (clampedIndex)
	{
{{SOURCE_HISTORY_SWITCH_CASES}}	default: return sampleVideo(tc);
	}
}

float4 sampleTemporalHistory(int framesAgo, float2 tc)
{
	if (gTemporalHistoryLength <= 0)
		return sampleVideo(tc);
	int clampedIndex = framesAgo;
	if (clampedIndex < 0)
		clampedIndex = 0;
	if (clampedIndex >= gTemporalHistoryLength)
		clampedIndex = gTemporalHistoryLength - 1;
	switch (clampedIndex)
	{
{{TEMPORAL_HISTORY_SWITCH_CASES}}	default: return sampleVideo(tc);
	}
}

{{FEEDBACK_HELPER}}

{{TEXT_HELPERS}}
#include "{{USER_SHADER_INCLUDE}}"

[shader("fragment")]
float4 fragmentMain(FragmentInput input) : SV_Target
{
	ShaderContext context;
	context.uv = input.texCoord;
	context.sourceColor = sampleVideo(context.uv);
	context.inputResolution = gInputResolution;
	context.outputResolution = gOutputResolution;
	context.time = gTime;
	context.utcTimeSeconds = gUtcTimeSeconds;
	context.utcOffsetSeconds = gUtcOffsetSeconds;
	context.startupRandom = gStartupRandom;
	context.frameCount = gFrameCount;
	context.mixAmount = gMixAmount;
	context.bypass = gBypass;
	context.sourceHistoryLength = gSourceHistoryLength;
	context.temporalHistoryLength = gTemporalHistoryLength;
	context.feedbackAvailable = gFeedbackAvailable;
	float4 effectedColor = {{ENTRY_POINT_CALL}};
	float mixValue = clamp(gBypass > 0.5 ? 0.0 : gMixAmount, 0.0, 1.0);
	return lerp(context.sourceColor, effectedColor, mixValue);
}
