; This custom shader will automatically adjust the convergence to try to keep
; the game comfortable to look at no matter how the camera changes.

[Constants]
; Set to 1 to enable auto-convergence (if you need to momentarily disable
; auto-convergence for a key binding or preset do not change this since it
; could get saved in the d3dx_user.ini - set the $suppress variable instead):
global persist $auto_convergence_enabled = 1
global persist $auto_convergence_disabled = 0

; Minimum convergence for auto-convergence:
global $min_convergence = 0.1

; Soft maximum convergence for auto-convergence (before popout bias):
global $max_convergence_soft = 0.5

; Hard maximum convergence for auto-convergence (after popout bias):
global $max_convergence_hard = 10.0

; Popout bias. Sets how far out of the screen auto-convergence will aim to
; position the closest object, and tries to do so in a way that works pretty
; well regardless of the scene, screen size or viewing distance. 0.0 is screen
; depth, -1 is infinity. A small positive value usually works pretty well. The
; auto-convergence HUD will display this value while adjusting convergence
global persist $popout_bias = 0.001

; Rate for the slow auto-convergence transition (convergence change per second):
global $slow_convergence_rate = 1.0

; Amount the convergence is allowed to get *closer* than the auto-convergence
; target before the slower auto-adjustment kicks in:
global $slow_convergence_threshold_near = 0.2

; Amount the convergence is allowed to get further than the auto-convergence
; target before the slower auto-adjustment kicks in:
global $slow_convergence_threshold_far = 0.5

; If the convergence gets at least this far out in either direction an instant
; correction will be triggered. Set to 0 to lock auto-convergence on:
global $instant_convergence_threshold = 2.5

; This is the anti-judder threshold - if an object near the camera is pushed
; out of frame due to an auto-convergence adjustment, the auto-convergence will
; no longer see it and may start shaking the convergence between two values. The
; auto-convergence shader tries to counter this, by detecting if it is trying
; to set the convergence back to what it just was a moment ago and stopping.
; This value defines how close the convergence can be to the previous value to
; trigger this detection. Set to 0 to disable this countermeasure:
global $anti_judder_threshold = 0.5

; Whether to display the auto convergence HUD while adjusting. Set to 2 to keep
; the HUD visible all the time:
global $auto_convergence_hud_enabled = 1

; Number of seconds the auto-convergence HUD is displayed after adjusting the
; convergence via hotkey:
global $auto_convergence_hud_timeout = 5.0

; Display a notice if the auto-convergence shader did not receive a Z-buffer:
global $warn_no_z_buffer = 1

; Use this to temporarily suppress auto-convergence and its HUD from a key
; binding (e.g. aim down sights) or preset:
global $suppress = 0

; Internal state, do not edit:
global $prev_auto_convergence_enabled
global $prev_suppress
global $prev_convergence
global $prev_stereo_active
global $prev_time
global $notification_timeout
global $user_convergence_delta
global $instant_convergence_timeout

; Set this to the game's depth buffer for auto-convergence to work:
[ResourceDepthBuffer]
[Resource_gGlobalParam]

[Present]
; To make pre/post phases a little clearer use separate command lists for each.
; Keep in mind that between these two is when the driver will process user
; changes the stereo settings (e.g. convergence, stereo active), and when
; 3DMigoto processes user input and presets.
pre run = CommandListBeforePresent
post run = CommandListAfterPresent

[CommandListBeforePresent]
if stereo_active && !$suppress
	local $bak_x1 = x1
	local $bak_y1 = y1
	local $bak_z1 = z1
	local $bak_w1 = w1

	local $bak_x2 = x2
	local $bak_y2 = y2
	local $bak_z2 = z2
	local $bak_w2 = w2

	local $bak_x3 = x3
	local $bak_y3 = y3
	local $bak_z3 = z3
	local $bak_w3 = w3

	local $bak_x4 = x4
	local $bak_y4 = y4
	local $bak_z4 = z4

	x1 = $min_convergence
	y1 = $max_convergence_soft
	z1 = $max_convergence_hard
	w1 = $popout_bias

	x2 = $slow_convergence_rate
	y2 = $slow_convergence_threshold_near
	z2 = $slow_convergence_threshold_far
	if time < $instant_convergence_timeout
		; Instant jump to target convergence
		w2 = 0
		w3 = 0
	else
		w2 = $instant_convergence_threshold
		w3 = $anti_judder_threshold
	endif

	x3 = time
	y3 = $prev_time
	z3 = $user_convergence_delta

	x4 = $prev_auto_convergence_enabled && !$prev_suppress && $prev_stereo_active
	y4 = $auto_convergence_enabled
	z4 = $warn_no_z_buffer && ResourceDepthBuffer === null

	if $auto_convergence_enabled && ResourceDepthBuffer !== null
		run = CustomShaderAutoConvergence
	endif
	if ($auto_convergence_hud_enabled == 1 && time < $notification_timeout) || $auto_convergence_hud_enabled == 2 || z4
		run = CustomShaderAutoConvergenceHUD
	endif

	$prev_time = x3

	x1 = $bak_x1
	y1 = $bak_y1
	z1 = $bak_z1
	w1 = $bak_w1

	x2 = $bak_x2
	y2 = $bak_y2
	z2 = $bak_z2
	w2 = $bak_w2

	x3 = $bak_x3
	y3 = $bak_y3
	z3 = $bak_z3
	w3 = $bak_w3

	x4 = $bak_x4
	y4 = $bak_y4
	z4 = $bak_z4
endif
; Record the convergence set (or not) prior to the present call. We compare
; this to the convergence in the next frame to determine if the user is
; adjusting the convergence, and convert it to an equivalent popout bias:
$prev_convergence = convergence
$prev_stereo_active = stereo_active
$prev_auto_convergence_enabled = $auto_convergence_enabled
$prev_suppress = $suppress

[CommandListAfterPresent]
; Check the convergence value immediately after the present call to see if the
; user adjusted the convergence.
if stereo_active && $prev_stereo_active && !$suppress && !$prev_suppress
	$user_convergence_delta = convergence - $prev_convergence
	if $user_convergence_delta
		run = CommandListShowAutoConvergenceHUD
	endif
	if $auto_convergence_enabled && $prev_auto_convergence_enabled
		if $user_convergence_delta
			; TODO: Go back to calculating the equivelent popout bias
			; adjustment on the GPU once this can be staged back out
			if convergence > $max_convergence_hard
				$user_convergence_delta = $max_convergence_hard - $prev_convergence
			elif convergence < $min_convergence
				$user_convergence_delta = $min_convergence - $prev_convergence
			endif
			; The division by convergence here helps make the adjustment closer to linear:
			$popout_bias = $popout_bias + $user_convergence_delta / $prev_convergence / 4

			; Ignore thresholds for a short time so the user can
			; see exactly what popout this is aiming for:
			$instant_convergence_timeout = time + 0.1

			; Optionally delay the user convergence adjustment
			; until the auto convergence shader has had a chance to
			; account for it - makes the vertices a little less
			; "jumpy", but 3DMigoto doesn't currently update
			; StereoParams as a result of these commands, and since
			; we are at the start of a new frame fixed shaders
			; would be out of sync for the frame - maybe wait until
			; 3DMigoto keeps that in sync:
			;pre convergence = $prev_convergence
		endif
	endif
endif

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; AUTO CONVERGENCE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This implements a depth-buffer based auto-convergence system. The depth
; buffer is sampled in a number of places and downscaled to find the nearest
; point to the camera on screen. A shader (auto_convergence.hlsl) then uses
; this value to calculate an appropriate convergence value to use for the
; current scene, and the result will be used in the next+1 frame.
;
; Remember to use StereoFlagsDX10 = 0x00000008 so that this takes the left eye
; into account in SLI - it will still work without that, but won't be as good.

[Key Toggle Auto Convergence]
key = 0
key = XB_RIGHT_THUMB XB_LEFT_THUMB
$auto_convergence_enabled = 0, 1
$auto_convergence_disabled = 1, 0
type = cycle
run = CommandListShowAutoConvergenceHUD

[CommandListShowAutoConvergenceHUD]
$notification_timeout = time + 2.0

; A bunch of downsampled depth buffers:
[ResourceAutoConvergenceDepthDownscale128]
width = 128
height = 128
format = R32_FLOAT
mode = stereo
[ResourceAutoConvergenceDepthDownscale64]
width = 64
height = 64
mode = stereo
[ResourceAutoConvergenceDepthDownscale32]
width = 32
height = 32
mode = stereo
[ResourceAutoConvergenceDepthDownscale16]
width = 16
height = 16
mode = stereo
[ResourceAutoConvergenceDepthDownscale8]
width = 8
height = 8
mode = stereo
[ResourceAutoConvergenceDepthDownscale4]
width = 4
height = 4
mode = stereo
[ResourceAutoConvergenceDepthDownscale2]
width = 2
height = 2
mode = stereo
[ResourceAutoConvergenceDepthDownscale1]
width = 1
height = 1
mode = stereo

[ResourceAutoConvergence]
; Buffer to hold the new convergence value, which will be transferred from the
; GPU back to the CPU and will be set to the convergence in some future frame.
type = Buffer
format = R32_FLOAT
array = 1

[ResourceAutoConvergenceState]
; This buffer holds some state that the auto-convergence shader uses to try to
; counter unwanted judder in some (literal) edge cases, and other things.
type = RWStructuredBuffer
;
; data= sets the initial state of the buffer - the size of the buffer is
; automatically calculated from this, offering an alternative to manually
; specifying stride and array - for a structured buffer stride will be set to
; the buffer size, and array will be 1.
;
; Specifying the format inline here instead of in a separate format= line makes
; it only apply to parsing this line, leaving the actual buffer format as
; UNKNOWN, which is necessary for structured buffers.
;
; 0x7fc00000 is "nan", signifying that 3DMigoto did not set the convergence
; last frame. When we switch 3DMigoto to the vs2015 branch we will be able to
; just write "nan" here instead.
data = R32_FLOAT  0 0 0 0  0  0x7fc00000  0 0

[CustomShaderAutoConvergenceInitialDownscale]
; Description below where this is called
ps = ShaderFixes/downscale_coarse.hlsl
Draw = 4, 0

[CustomShaderAutoConvergenceIntermediateDownscale]
; Description below where this is called
ps = ShaderFixes/downscale_mono_half_max.hlsl

ResourceAutoConvergenceDepthDownscale64 = copy_desc ResourceAutoConvergenceDepthDownscale128
o0 = set_viewport ResourceAutoConvergenceDepthDownscale64
ps-t110 = ResourceAutoConvergenceDepthDownscale128
Draw = 4, 0
;post Resource\ShaderFixes\debug_2d.ini\Debug2D = ResourceAutoConvergenceDepthDownscale64

ResourceAutoConvergenceDepthDownscale32 = copy_desc ResourceAutoConvergenceDepthDownscale128
o0 = set_viewport ResourceAutoConvergenceDepthDownscale32
ps-t110 = ResourceAutoConvergenceDepthDownscale64
Draw = 4, 0
;post Resource\ShaderFixes\debug_2d.ini\Debug2D = ResourceAutoConvergenceDepthDownscale32

ResourceAutoConvergenceDepthDownscale16 = copy_desc ResourceAutoConvergenceDepthDownscale128
o0 = set_viewport ResourceAutoConvergenceDepthDownscale16
ps-t110 = ResourceAutoConvergenceDepthDownscale32
Draw = 4, 0
;post Resource\ShaderFixes\debug_2d.ini\Debug2D = ResourceAutoConvergenceDepthDownscale16

ResourceAutoConvergenceDepthDownscale8 = copy_desc ResourceAutoConvergenceDepthDownscale128
o0 = set_viewport ResourceAutoConvergenceDepthDownscale8
ps-t110 = ResourceAutoConvergenceDepthDownscale16
Draw = 4, 0
;post Resource\ShaderFixes\debug_2d.ini\Debug2D = ResourceAutoConvergenceDepthDownscale8

ResourceAutoConvergenceDepthDownscale4 = copy_desc ResourceAutoConvergenceDepthDownscale128
o0 = set_viewport ResourceAutoConvergenceDepthDownscale4
ps-t110 = ResourceAutoConvergenceDepthDownscale8
Draw = 4, 0
;post Resource\ShaderFixes\debug_2d.ini\Debug2D = ResourceAutoConvergenceDepthDownscale4

ResourceAutoConvergenceDepthDownscale2 = copy_desc ResourceAutoConvergenceDepthDownscale128
o0 = set_viewport ResourceAutoConvergenceDepthDownscale2
ps-t110 = ResourceAutoConvergenceDepthDownscale4
Draw = 4, 0
;post Resource\ShaderFixes\debug_2d.ini\Debug2D = ResourceAutoConvergenceDepthDownscale2

ResourceAutoConvergenceDepthDownscale1 = copy_desc ResourceAutoConvergenceDepthDownscale128
o0 = set_viewport ResourceAutoConvergenceDepthDownscale1
ps-t110 = ResourceAutoConvergenceDepthDownscale2
Draw = 4, 0
;post Resource\ShaderFixes\debug_2d.ini\Debug2D = ResourceAutoConvergenceDepthDownscale1

[CustomShaderAutoConvergence]
vs = ShaderFixes/full_screen.hlsl
blend = disable
cull = none
topology = triangle_strip
run = BuiltInCommandListUnbindAllRenderTargets

; Initial pass samples points on the depth buffer to downscale it to a fixed
; size, regardless of the original depth buffer size. This will have severe
; aliasing, and is not intended to be displayed graphically. Calling this
; custom shader from here to inherit common state:
ResourceAutoConvergenceDepthDownscale128 = copy_desc ResourceDepthBuffer
o0 = set_viewport ResourceAutoConvergenceDepthDownscale128
ps-t110 = ResourceDepthBuffer
run = CustomShaderAutoConvergenceInitialDownscale
;post Resource\ShaderFixes\debug_2d.ini\Debug2D = ResourceAutoConvergenceDepthDownscale128

; Intermediate passes just combine the samples we have into a progressively
; smaller buffer. Calling this from here inherits common state, and this shader
; section will call Draw() multiple times for each progressive downscale step:
run = CustomShaderAutoConvergenceIntermediateDownscale

; The final pass is run from the context of this custom shader. This takes the
; downscaled buffers from both eyes and implements the actual auto-convergence
; logic to determine the new convergence value:
ps = ShaderFixes/auto_convergence.hlsl
ps-cb13 = Resource_gGlobalParam
post ps-cb13 = null
; FIXME: set_viewport once 3DMigoto supports that with buffers, otherwise we're
; relying on the viewport still being set to something usable from the
; downscaling (which assumes we have a depth buffer to play with)
o0 = ResourceAutoConvergence
ps-u1 = ResourceAutoConvergenceState
ps-t110 = stereo2mono ResourceAutoConvergenceDepthDownscale1
Draw = 4, 0
;Resource\ShaderFixes\debug_cb.ini\DebugBuf = ResourceAutoConvergenceState

post ps-t100 = null

; This will asynchronously transfer the new convergence value from the GPU back
; to the CPU, which will then be used in some future frame (not necessarily the
; next frame). Specifying "pre" or "post" is required for this to preserve the
; new convergence, otherwise the old value would be immediately restored at the
; end of the custom shader. This is due to the way that 3DMigoto historically
; handled per-shader convergence:
post convergence = ResourceAutoConvergence

[ResourceAutoConvergenceFont]
; Note that this font has the size of each character embedded in the unused
; space of the final character, which the geometry shader uses to render each
; character the correct width. This font was generated with gen_bm_font.py,
; then converted to DDS (because our PNG loading seems to be broken o_O):
filename = ShaderFixes\LiberationSans-Bold.dds
[CustomShaderAutoConvergenceHUD]
; The vertex shader passes draw indexes to the geometry shader:
vs = ShaderFixes/auto_convergence_hud.hlsl
; The geometry shader generates the text for a given draw index:
gs = ShaderFixes/auto_convergence_hud.hlsl
; The pixel shader draws the font:
ps = ShaderFixes/auto_convergence_hud.hlsl
; Allows us to use SV_Position.z to pack a texcoord, increasing the character
; limit per geometry shader invocation:
depth_clip_enable = false
cull = none
; Enable alpha blending. To change the text colour, edit the pixel shader:
blend = add one inv_src_alpha
; Use points as the primitive from the vertex shader to the geometry shader:
topology = point_list
run = CommandListUnbindAllRenderTargets
o0 = set_viewport no_view_cache bb
; Pass the auto-convergence state to the geometry shader:
gs-t113 = ResourceAutoConvergenceState
post gs-t113 = null
; Font is passed into the pixel shader (to draw it) *and* the geometry shader
; (as the character sizes are encoded in the final character of the font):
gs-t100 = ResourceAutoConvergenceFont
ps-t100 = ResourceAutoConvergenceFont
post gs-t100 = null
post ps-t100 = null
; Change this number to set how many lines of text may be drawn:
draw = 1, 0
