# BGFX Effect Format

DirectX 11 compute shader effects.

## File Structure

```hlsl
//!BGFX EFFECT
//!VERSION 1
//!NAME My Effect
//!CATEGORY Upscaling

//!PARAMETER
//!DEFAULT 0.5
//!MIN 0.0
//!MAX 1.0
//!STEP 0.01
float strength;

//!TEXTURE
//!WIDTH INPUT_WIDTH * 2
//!HEIGHT INPUT_HEIGHT * 2
Texture2D myBuffer;

//!SAMPLER
//!FILTER LINEAR
SamplerState sam;

//!COMMON
float3 helper(float3 c) { return c * 2.0; }

//!PASS 1
//!STYLE PS
//!IN INPUT
//!OUT OUTPUT
MF4 Pass1(float2 pos) {
    return INPUT.SampleLevel(sam, pos, 0);
}
```

## Header

Required:
```hlsl
//!BGFX EFFECT     // Must be first non-blank line
//!VERSION 1
```

Optional:
```hlsl
//!NAME Display Name       // Shown in UI (default: filename)
//!CATEGORY Upscaling      // Groups effects in UI
//!DESCRIPTION text        // Tooltip
```

Flags:
```hlsl
//!USE_DYNAMIC             // Adds GetTime(), GetFrameCount(), GetMouse()
//!USE MulAdd              // Adds MulAdd() matrix helpers
//!CAPABILITY FP16         // Declares FP16 support
```

## Parameters

```hlsl
//!PARAMETER
//!LABEL Sharpness         // UI label (default: variable name)
//!DESC Tooltip text
//!DEFAULT 0.5
//!MIN 0.0
//!MAX 1.0
//!STEP 0.01
float sharpness;
```

Types: `float`, `int`. An `int` with min=0, max=1, step=1 renders as a checkbox.

## Textures

Built-in:
- `INPUT` - Source (read-only)
- `OUTPUT` - Final output (last pass only)

Intermediate:
```hlsl
//!TEXTURE
//!WIDTH INPUT_WIDTH * 2
//!HEIGHT INPUT_HEIGHT * 2
//!FORMAT R16G16B16A16_FLOAT  // Default: R8G8B8A8_UNORM
Texture2D myBuffer;
```

Size expressions:
- **Variables**: `INPUT_WIDTH`, `INPUT_HEIGHT`, `OUTPUT_WIDTH`, `OUTPUT_HEIGHT`, `RENDERER_WIDTH`, `RENDERER_HEIGHT`
- **Operators**: `+`, `-`, `*`, `/`, `^`
- **Functions**: `ceil()`, `floor()`, `round()`, `abs()`, `min()`, `max()`, `sqrt()`

```hlsl
//!WIDTH INPUT_WIDTH * 2                    // 2x upscale
//!HEIGHT ceil(OUTPUT_HEIGHT / 4) * 4       // Multiple of 4
//!WIDTH max(INPUT_WIDTH, 1920)             // At least 1920
//!WIDTH RENDERER_WIDTH                     // Full screen
```

From file:
```hlsl
//!TEXTURE
//!SOURCE lut.png           // BMP, PNG, JPG, DDS
//!FORMAT R8G8B8A8_UNORM
Texture2D lookupTable;
```

Formats: `R8G8B8A8_UNORM`, `R16G16B16A16_FLOAT`, `R32G32B32A32_FLOAT`, `R11G11B10_FLOAT`, `R16G16_FLOAT`, `R32_FLOAT`, `R16_FLOAT`, `R8_UNORM`, SNORM/UNORM variants.

## Temporal Textures

### Original History

Previous INPUT frames (before effects):

```hlsl
//!TEXTURE
//!HISTORY 1          // 1 frame ago
Texture2D PrevFrame;

//!TEXTURE
//!HISTORY 3          // 3 frames ago
Texture2D OlderFrame;
```

- `//!HISTORY N` where N is 1-7
- Shared ring buffer
- Black until history fills

```hlsl
//!TEXTURE
//!HISTORY 1
Texture2D Prev1;

//!PASS 1
//!STYLE PS
//!IN INPUT, Prev1
//!OUT OUTPUT

float4 Pass1(float2 pos) {
    float4 current = INPUT.SampleLevel(sam, pos, 0);
    float4 prev = Prev1.SampleLevel(sam, pos, 0);
    return lerp(current, prev, 0.5);
}
```

### Pass Feedback

Previous frame's output from a named pass:

```hlsl
//!TEXTURE
//!FEEDBACK MyPass
Texture2D PrevOutput;

//!PASS 1
//!NAME MyPass
//!STYLE PS
//!IN INPUT, PrevOutput
//!OUT OUTPUT

float4 Pass1(float2 pos) {
    float4 current = INPUT.SampleLevel(sam, pos, 0);
    float4 prev = PrevOutput.SampleLevel(sam, pos, 0);
    return lerp(current, prev, 0.8);
}
```

- `//!FEEDBACK PassName` references a pass by `//!NAME`
- Double-buffered
- Defaults to OUTPUT dimensions

| | History | Feedback |
|--|---------|----------|
| Source | INPUT (pre-effects) | Pass OUTPUT |
| Depth | 1-7 frames | 1 frame |
| Buffer | Shared ring buffer | Per-effect |

With `//!USE_DYNAMIC`:
```hlsl
float GetHistoryTimeDelta(int age)  // Seconds since history frame N
```

## Samplers

```hlsl
//!SAMPLER
//!FILTER LINEAR    // LINEAR or POINT
//!ADDRESS CLAMP    // CLAMP, WRAP, MIRROR (default: CLAMP)
SamplerState linearSam;
```

## Passes

### PS Style

Auto-converted to compute shader:

```hlsl
//!PASS 1
//!DESC Sharpen
//!STYLE PS
//!IN INPUT
//!OUT OUTPUT

MF4 Pass1(float2 pos) {
    // pos = normalized UV (0-1)
    return INPUT.SampleLevel(sam, pos, 0);
}
```

Multiple outputs:
```hlsl
//!PASS 1
//!STYLE PS
//!IN INPUT
//!OUT tex1, tex2

void Pass1(float2 pos, out MF4 o1, out MF4 o2) {
    o1 = MF4(1,0,0,1);
    o2 = MF4(0,1,0,1);
}
```

### CS Style

```hlsl
//!PASS 1
//!IN INPUT
//!OUT OUTPUT
//!BLOCK_SIZE 16, 16
//!NUM_THREADS 64, 1, 1

void Pass1(uint2 blockStart, uint3 threadId) {
    uint2 pos = blockStart + TileSwizzle8x8(threadId.x);
    if (pos.x >= GetOutputSize().x || pos.y >= GetOutputSize().y) return;
    OUTPUT[pos] = INPUT[pos];
}
```

## Built-in Functions

```hlsl
uint2 GetInputSize()
float2 GetInputPt()           // 1.0 / InputSize
uint2 GetOutputSize()
float2 GetOutputPt()          // 1.0 / OutputSize
float2 GetScale()             // OutputSize / InputSize
uint2 TileSwizzle8x8(uint id) // 0-63 to 8x8 swizzled coords
```

With `//!USE_DYNAMIC`:
```hlsl
uint GetFrameCount()
float GetTime()               // Seconds since start
float GetTimeDelta()          // Seconds since last frame
float4 GetMouse()             // xy=pos (0-1), zw=click pos (negative when up)
```

## Precision Types

`MF` types switch between FP32/FP16:

| Type | FP32 | FP16 |
|------|------|------|
| `MF` | `float` | `min16float` |
| `MF2` | `float2` | `min16float2` |
| `MF3` | `float3` | `min16float3` |
| `MF4` | `float4` | `min16float4` |

## Macros

```hlsl
BG_BLOCK_WIDTH
BG_BLOCK_HEIGHT
BG_NUM_THREADS_X/Y/Z
BG_FP16             // 1 if FP16 enabled
BG_PS_STYLE         // Defined for PS-style passes
BG_DEBUG            // Defined in debug builds
```

## Scaling

Set output size via OUTPUT dimensions:

```hlsl
//!TEXTURE
//!WIDTH INPUT_WIDTH * 2
//!HEIGHT INPUT_HEIGHT * 2
Texture2D OUTPUT;
```

No size = use profile scaling settings.

## Background Effects

Fill letterbox/pillarbox areas. Output at renderer size:

```hlsl
//!BGFX EFFECT
//!VERSION 1
//!NAME My Background
//!CATEGORY Background

//!TEXTURE
Texture2D INPUT;

//!TEXTURE
//!WIDTH RENDERER_WIDTH
//!HEIGHT RENDERER_HEIGHT
Texture2D OUTPUT;

//!SAMPLER
//!FILTER LINEAR
SamplerState sam;

//!PASS 1
//!STYLE PS
//!IN INPUT
//!OUT OUTPUT

float4 Pass1(float2 pos) {
    float2 inputSize = float2(GetInputSize());
    float2 outputSize = float2(GetOutputSize());

    float inputAspect = inputSize.x / inputSize.y;
    float outputAspect = outputSize.x / outputSize.y;

    float2 contentScale;
    float2 contentOffset;

    if (inputAspect > outputAspect) {
        contentScale = float2(1.0, outputAspect / inputAspect);
        contentOffset = float2(0.0, (1.0 - contentScale.y) * 0.5);
    } else {
        contentScale = float2(inputAspect / outputAspect, 1.0);
        contentOffset = float2((1.0 - contentScale.x) * 0.5, 0.0);
    }

    float2 contentUV = (pos - contentOffset) / contentScale;
    bool insideContent = all(contentUV >= 0.0) && all(contentUV <= 1.0);

    if (insideContent) {
        return INPUT.SampleLevel(sam, contentUV, 0);
    }

    return float4(0.1, 0.1, 0.1, 1.0);
}
```

Place background effects last in the chain.

## Example: CAS

```hlsl
//!BGFX EFFECT
//!VERSION 1
//!NAME CAS
//!CATEGORY Post Processing
//!CAPABILITY FP16

//!PARAMETER
//!LABEL Sharpness
//!DEFAULT 0.4
//!MIN 0.0
//!MAX 1.0
//!STEP 0.01
float sharpness;

//!SAMPLER
//!FILTER POINT
SamplerState sam;

//!PASS 1
//!DESC CAS Sharpen
//!STYLE PS
//!IN INPUT
//!OUT OUTPUT

MF4 Pass1(float2 pos) {
    float2 pt = GetInputPt();

    MF3 a = INPUT.SampleLevel(sam, pos + float2(-pt.x, -pt.y), 0).rgb;
    MF3 b = INPUT.SampleLevel(sam, pos + float2(0, -pt.y), 0).rgb;
    MF3 c = INPUT.SampleLevel(sam, pos + float2(pt.x, -pt.y), 0).rgb;
    MF3 d = INPUT.SampleLevel(sam, pos + float2(-pt.x, 0), 0).rgb;
    MF3 e = INPUT.SampleLevel(sam, pos, 0).rgb;
    MF3 f = INPUT.SampleLevel(sam, pos + float2(pt.x, 0), 0).rgb;
    MF3 g = INPUT.SampleLevel(sam, pos + float2(-pt.x, pt.y), 0).rgb;
    MF3 h = INPUT.SampleLevel(sam, pos + float2(0, pt.y), 0).rgb;
    MF3 i = INPUT.SampleLevel(sam, pos + float2(pt.x, pt.y), 0).rgb;

    MF3 mnRGB = min(min(min(d, e), min(f, b)), h);
    MF3 mxRGB = max(max(max(d, e), max(f, b)), h);
    mnRGB = min(mnRGB, min(min(a, c), min(g, i)));
    mxRGB = max(mxRGB, max(max(a, c), max(g, i)));

    MF3 amp = saturate(min(mnRGB, 2.0 - mxRGB) / mxRGB);
    amp = sqrt(amp);

    MF peak = -1.0 / lerp(8.0, 5.0, sharpness);
    MF3 w = amp * peak;
    MF3 rcpW = rcp(1.0 + 4.0 * w);

    MF3 result = saturate((b * w + d * w + f * w + h * w + e) * rcpW);
    return MF4(result, 1.0);
}
```

## Debugging

Enable source output in profile settings:
- Saves `{effect}_pass{n}.hlsl` next to effect file

Common issues:
- Missing `//!BGFX EFFECT` header
- Missing required directives
- Pass function must be `Pass{n}`
- PS-style returns `MF4`, CS-style returns `void`
