Implement user-specified pixel shaders, redux (#8565)

Co-authored-by: mrange <marten_range@hotmail.com>

I loved the pixel shaders in #7058, but that PR needed a bit of polish
to be ready for ingestion. This PR is almost _exactly_ that PR, with
some small changes.

* It adds a new pre-profile setting `"experimental.pixelShaderPath"`,
  which lets the user set a pixel shader to use with the Terminal.
    - CHANGED FROM #7058: It does _not_ add any built-in shaders.
    - CHANGED FROM #7058: it will _override_
      `experimental.retroTerminalEffect`
* It adds a bunch of sample shaders in `samples/shaders`. Included: 
    - A NOP shader as a base to build from.
    - An "invert" shader that inverts the colors, as a simple example
    - An "grayscale" shader that converts all colors to grayscale, as a
      simple example
    - An "raster bars" shader that draws some colored bars on the screen
      with a drop shadow, as a more involved example
    - The original retro terminal effects, as a more involved example
    - It also includes a broken shader, as an example of what heppens
      when the shader fails to compile
    - CHANGED FROM #7058: It does _not_ add the "retroII" shader we were
      all worried about.
* When a shader fails to be found or fails to compile, we'll display an
  error dialog to the user with a relevant error message.
    - CHANGED FROM #7058: Originally, #7058 would display "error bars"
      on the screen. I've removed that, and had the Terminal disable the
      shader entirely then.
* Renames the `toggleRetroEffect` action to `toggleShaderEffect`.
  (`toggleRetroEffect` is now an alias to `toggleShaderEffect`). This
  action will turn the shader OR the retro effects on/off. 

`toggleShaderEffect` works the way you'd expect it to, but the mental
math on _how_ is a little weird. The logic is basically:

```
useShader = shaderEffectsEnabled ? 
                (pixelShaderProvided ? 
                    pixelShader : 
                    (retroEffectEnabled ? 
                        retroEffect : null
                    )
                ) : 
                null
```

and `toggleShaderEffect` toggles `shaderEffectsEnabled`.

* If you've got both a shader and retro enabled, `toggleShaderEffect`
  will toggle between the shader on/off.
* If you've got a shader and retro disabled, `toggleShaderEffect` will
  toggle between the shader on/off.

References #6191
References #7058

Closes #7013

Closes #3930 "Add setting to retro terminal shader to control blur
radius, color" 
Closes #3929 "Add setting to retro terminal shader to enable drawing
scanlines" 
     - At this point, just roll your own version of the shader.
This commit is contained in:
Mike Griese 2020-12-15 14:40:22 -06:00 committed by GitHub
parent e943785e1a
commit b140299e50
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 843 additions and 116 deletions

View file

@ -8,9 +8,12 @@ cmdletbinding
COLORPROPERTY COLORPROPERTY
CXICON CXICON
CYICON CYICON
D2DERR_SHADER_COMPILE_FAILED
DERR
environstrings environstrings
EXPCMDFLAGS EXPCMDFLAGS
EXPCMDSTATE EXPCMDSTATE
frac
fullkbd fullkbd
futex futex
GETDESKWALLPAPER GETDESKWALLPAPER
@ -34,7 +37,6 @@ IObject
IStorage IStorage
ITab ITab
ITaskbar ITaskbar
llabs
LCID LCID
llabs llabs
localtime localtime
@ -64,6 +66,7 @@ semver
serializer serializer
shobjidl shobjidl
SIZENS SIZENS
smoothstep
GETDESKWALLPAPER GETDESKWALLPAPER
snprintf snprintf
spsc spsc
@ -83,6 +86,7 @@ UPDATEINIFILE
userenv userenv
wcsstr wcsstr
wcstoui wcstoui
wpc
wsregex wsregex
XDocument XDocument
XElement XElement

View file

@ -12,16 +12,20 @@ ekg
ethanschoonover ethanschoonover
Firefox Firefox
Gatta Gatta
glsl
Grie Grie
Griese Griese
Hernan Hernan
Howett Howett
Illhardt Illhardt
iquilezles
jantari jantari
jerrysh jerrysh
Kaiyu Kaiyu
kimwalisch kimwalisch
KMehrain KMehrain
KODELIFE
Kodelife
Kourosh Kourosh
kowalczyk kowalczyk
leonmsft leonmsft
@ -30,6 +34,7 @@ lukesampson
Manandhar Manandhar
mbadolato mbadolato
Mehrain Mehrain
menger
mgravell mgravell
michaelniksa michaelniksa
michkap michkap
@ -43,6 +48,7 @@ nvaccess
nvda nvda
oising oising
oldnewthing oldnewthing
opengl
osgwiki osgwiki
paulcam paulcam
pauldotknopf pauldotknopf
@ -51,6 +57,7 @@ Pham
Rincewind Rincewind
rprichard rprichard
Schoonover Schoonover
shadertoy
Somuah Somuah
sonph sonph
sonpham sonpham
@ -60,8 +67,8 @@ Wirt
Wojciech Wojciech
zadjii zadjii
Zamor Zamor
Zamora
zamora zamora
Zamora
Zoey Zoey
zorio zorio
Zverovich Zverovich

View file

@ -24,8 +24,8 @@ ADDREF
addressof addressof
ADDSTRING ADDSTRING
ADDTOOL ADDTOOL
AEnd
aef aef
AEnd
AFew AFew
AFill AFill
AFX AFX
@ -628,6 +628,7 @@ doskey
dotnet dotnet
doubleclick doubleclick
downlevel downlevel
DOWNSCALE
dpg dpg
dpi dpi
DPIAPI DPIAPI
@ -832,6 +833,7 @@ fuzzwrapper
fwdecl fwdecl
fwe fwe
fwlink fwlink
GAUSSIAN
gb gb
gci gci
gcx gcx
@ -909,6 +911,7 @@ Goldmine
gonce gonce
Google Google
goutput goutput
GPUs
Gravell's Gravell's
grayscale grayscale
GREENSCROLL GREENSCROLL
@ -1109,6 +1112,7 @@ INTERCEPTCOPYPASTE
INTERNALNAME INTERNALNAME
interop interop
interoperability interoperability
intersectors
inthread inthread
intptr intptr
intsafe intsafe
@ -1207,6 +1211,7 @@ KJ
KLF KLF
KLMNOPQRST KLMNOPQRST
KLMNOPQRSTQQQQQ KLMNOPQRSTQQQQQ
Kode
KU KU
KVM KVM
KX KX
@ -1591,6 +1596,7 @@ OACR
oauth oauth
objbase objbase
ocf ocf
ocolor
odl odl
oem oem
oemcp oemcp
@ -1789,6 +1795,7 @@ pragma
prc prc
prealigned prealigned
prebuilt prebuilt
precendence
precomp precomp
prect prect
prefast prefast
@ -1834,6 +1841,7 @@ pshn
PSHNOTIFY PSHNOTIFY
PSHORT PSHORT
pshpack pshpack
psin
PSINGLE PSINGLE
psl psl
psldl psldl
@ -1882,10 +1890,12 @@ QWER
qzmp qzmp
RAII RAII
RALT RALT
rasterbar
rasterfont rasterfont
rasterization rasterization
rawinput rawinput
RAWPATH RAWPATH
raytracers
razzlerc razzlerc
rbar rbar
rbegin rbegin
@ -1956,9 +1966,11 @@ RESETCONTENT
resheader resheader
resizable resizable
resmimetype resmimetype
reso
restrictedcapabilities restrictedcapabilities
resw resw
resx resx
RETROII
retval retval
rfa rfa
rfc rfc
@ -2029,11 +2041,14 @@ SBCSDBCS
sbi sbi
sbiex sbiex
sbold sbold
sbri
scanbri
scancode scancode
scanline scanline
schemename schemename
SCL SCL
scm scm
scol
scprintf scprintf
SCRBUF SCRBUF
SCRBUFSIZE SCRBUFSIZE
@ -2167,6 +2182,9 @@ SOURCESDIRECTORY
SPACEBAR SPACEBAR
spammy spammy
spand spand
spe
sph
spherefunctions
splashscreen splashscreen
sprintf sprintf
sqlproj sqlproj
@ -2296,6 +2314,7 @@ tellp
telnet telnet
telnetd telnetd
templated templated
teraflop
terminalcore terminalcore
TERMINALSCROLLING TERMINALSCROLLING
terminfo terminfo
@ -2544,6 +2563,7 @@ vga
vgaoem vgaoem
viewkind viewkind
viewports viewports
Viginetting
Virt Virt
VIRTTERM VIRTTERM
Virtualizing Virtualizing
@ -2760,6 +2780,7 @@ wwaproj
WWith WWith
wx wx
wxh wxh
wz
xa xa
xact xact
xamarin xamarin
@ -2809,7 +2830,10 @@ xutr
xvalue xvalue
XVIRTUALSCREEN XVIRTUALSCREEN
XWalk XWalk
XWV
xy xy
xyw
Xzn
yact yact
YAML YAML
YCast YCast
@ -2825,6 +2849,7 @@ YVIRTUALSCREEN
Yw Yw
YWalk YWalk
yx yx
yzx
Zc Zc
ZCmd ZCmd
ZCtrl ZCtrl

View file

@ -103,7 +103,7 @@
"toggleFocusMode", "toggleFocusMode",
"toggleFullscreen", "toggleFullscreen",
"togglePaneZoom", "togglePaneZoom",
"toggleRetroEffect", "toggleShaderEffects",
"wt", "wt",
"unbound" "unbound"
], ],
@ -936,6 +936,10 @@
"description": "When set to true, enable retro terminal effects. This is an experimental feature, and its continued existence is not guaranteed.", "description": "When set to true, enable retro terminal effects. This is an experimental feature, and its continued existence is not guaranteed.",
"type": "boolean" "type": "boolean"
}, },
"experimental.pixelShaderPath": {
"description": "Use to set a path to a pixel shader to use with the Terminal. Overrides `experimental.retroTerminalEffect`. This is an experimental feature, and its continued existence is not guaranteed.",
"type": "string"
},
"fontFace": { "fontFace": {
"default": "Cascadia Mono", "default": "Cascadia Mono",
"description": "Name of the font face used in the profile.", "description": "Name of the font face used in the profile.",

View file

@ -0,0 +1,18 @@
// Broken, can be used for explorative testing of pixel shader error handling
Texture2D shaderTexture;
SamplerState samplerState;
cbuffer PixelShaderSettings {
float Time;
float Scale;
float2 Resolution;
float4 Background;
};
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
{
// OOPS; vec4 is not a hlsl but a glsl datatype!
vec4 color = shaderTexture.Sample(samplerState, tex);
return color;
}

View file

@ -0,0 +1,18 @@
// Shader used to indicate something went wrong during shader loading
Texture2D shaderTexture;
SamplerState samplerState;
cbuffer PixelShaderSettings {
float Time;
float Scale;
float2 Resolution;
float4 Background;
};
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
{
float4 color = shaderTexture.Sample(samplerState, tex);
float bars = 0.5+0.5*sin(tex.y*100);
color.x += pow(bars, 20.0);
return color;
}

View file

@ -0,0 +1,32 @@
// A minimal pixel shader that inverts the colors
// The terminal graphics as a texture
Texture2D shaderTexture;
SamplerState samplerState;
// Terminal settings such as the resolution of the texture
cbuffer PixelShaderSettings {
// Time since pixel shader was enabled
float Time;
// UI Scale
float Scale;
// Resolution of the shaderTexture
float2 Resolution;
// Background color as rgba
float4 Background;
};
// A pixel shader is a program that given a texture coordinate (tex) produces a color
// Just ignore the pos parameter
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
{
// Read the color value at the current texture coordinate (tex)
// float4 is tuple of 4 floats, rgba
float4 color = shaderTexture.Sample(samplerState, tex);
float avg = (color.x + color.y + color.z) / 3.0;
// Inverts the rgb values (xyz) but don't touch the alpha (w)
color.xyz = avg;
// Return the final color
return color;
}

View file

@ -0,0 +1,32 @@
// A minimal pixel shader that inverts the colors
// The terminal graphics as a texture
Texture2D shaderTexture;
SamplerState samplerState;
// Terminal settings such as the resolution of the texture
cbuffer PixelShaderSettings {
// Time since pixel shader was enabled
float Time;
// UI Scale
float Scale;
// Resolution of the shaderTexture
float2 Resolution;
// Background color as rgba
float4 Background;
};
// A pixel shader is a program that given a texture coordinate (tex) produces a color
// Just ignore the pos parameter
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
{
// Read the color value at the current texture coordinate (tex)
// float4 is tuple of 4 floats, rgba
float4 color = shaderTexture.Sample(samplerState, tex);
// Inverts the rgb values (xyz) but don't touch the alpha (w)
color.xyz = 1.0 - color.xyz;
// Return the final color
return color;
}

View file

@ -0,0 +1,17 @@
// Does nothing, serves as an example of a minimal pixel shader
Texture2D shaderTexture;
SamplerState samplerState;
cbuffer PixelShaderSettings {
float Time;
float Scale;
float2 Resolution;
float4 Background;
};
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
{
float4 color = shaderTexture.Sample(samplerState, tex);
return color;
}

View file

@ -0,0 +1,136 @@
# Pixel Shaders in Windows Terminal
Due to the sheer amount of computing power in GPUs, one can do awesome things with pixel shaders such as real-time fractal zoom, ray tracers and image processing.
Windows Terminal allows user to provide a pixel shader which will be applied to the terminal. To try it out, add the following setting to one of your profiles:
```
"experimental.pixelShaderPath": "<path to a .hlsl pixel shader>"
```
> **Note**: if you specify a shader with `experimental.pixelShaderPath`, the Terminal will use that instead of the `experimental.retroTerminalEffect`.
To get started using pixel shaders in the Terminal, start with the following sample shader. This is `Invert.hlsl` in this directory:
```hlsl
// A minimal pixel shader that inverts the colors
// The terminal graphics as a texture
Texture2D shaderTexture;
SamplerState samplerState;
// Terminal settings such as the resolution of the texture
cbuffer PixelShaderSettings {
// Time since pixel shader was enabled
float Time;
// UI Scale
float Scale;
// Resolution of the shaderTexture
float2 Resolution;
// Background color as rgba
float4 Background;
};
// A pixel shader is a program that given a texture coordinate (tex) produces a color
// Just ignore the pos parameter
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
{
// Read the color value at the current texture coordinate (tex)
// float4 is tuple of 4 floats, rgba
float4 color = shaderTexture.Sample(samplerState, tex);
// Inverts the rgb values (xyz) but don't touch the alpha (w)
color.xyz = 1.0 - color.xyz;
// Return the final color
return color;
}
```
Save this file as `C:\temp\invert.hlsl`, then update a profile with the setting:
```
"experimental.pixelShaderEffect": "C:\\temp\\invert.hlsl"
```
Once the settings file is saved, open a terminal with the changed profile. It should now invert the colors of the screen!
If your shader fails to compile, the Terminal will display a warning dialog and ignore it temporarily. After fixing your shader, touch the `settings.json` file again, or open a new tab, and the Terminal will try loading the shader again.
## HLSL
The language we use to write pixel shaders is called `HLSL`. It a `C`-like language, with some restrictions.You can't allocate memory, use pointers or recursion.
What you get access to is computing power in the teraflop range on decently recent GPUs. This means writing real-time raytracers or other cool effects are in the realm of possibility.
[shadertoy](https://shadertoy.com/) is a great site that show case what's possible with pixel shaders (albeit in `GLSL`). For example this [menger sponge](https://www.shadertoy.com/view/4scXzn). Converting from `GLSL` to `HLSL` isn't overly hard once you gotten the hang of it.
## Adding some retro raster bars
Let's try a more complicated example. Raster bars was cool in the 80's, so let's add that. Start by modifying shader like so: (This is `Rasterbars.hlsl`)
```hlsl
// A minimal pixel shader that shows some raster bars
// The terminal graphics as a texture
Texture2D shaderTexture;
SamplerState samplerState;
// Terminal settings such as the resolution of the texture
cbuffer PixelShaderSettings {
// Time since pixel shader was enabled
float Time;
// UI Scale
float Scale;
// Resolution of the shaderTexture
float2 Resolution;
// Background color as rgba
float4 Background;
};
// A pixel shader is a program that given a texture coordinate (tex) produces a color
// Just ignore the pos parameter
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
{
// Read the color value at the current texture coordinate (tex)
// float4 is tuple of 4 floats, rgba
float4 color = shaderTexture.Sample(samplerState, tex);
// Read the color value at some offset, will be used as shadow
float4 ocolor = shaderTexture.Sample(samplerState, tex+2.0*Scale*float2(-1.0, -1.0)/Resolution.y);
// Thickness of raster
const float thickness = 0.1;
float ny = floor(tex.y/thickness);
float my = tex.y%thickness;
const float pi = 3.141592654;
// ny is used to compute the rasterbar base color
float cola = ny*2.0*pi;
float3 col = 0.75+0.25*float3(sin(cola*0.111), sin(cola*0.222), sin(cola*0.333));
// my is used to compute the rasterbar brightness
// smoothstep is a great little function: https://en.wikipedia.org/wiki/Smoothstep
float brightness = 1.0-smoothstep(0.0, thickness*0.5, abs(my - 0.5*thickness));
float3 rasterColor = col*brightness;
// lerp(x, y, a) is another very useful function: https://en.wikipedia.org/wiki/Linear_interpolation
float3 final = rasterColor;
// Create the drop shadow of the terminal graphics
// .w is the alpha channel, 0 is fully transparent and 1 is fully opaque
final = lerp(final, float(0.0), ocolor.w);
// Draw the terminal graphics
final = lerp(final, color.xyz, color.w);
// Return the final color, set alpha to 1 (ie opaque)
return float4(final, 1.0);
}
```
Once reloaded, it should show some retro raster bars in the background, with a drop shadow to make the text more readable.
## Retro Terminal Effect
As a more complicated example, the Terminal's built-in `experimental.retroTerminalEffect` is included as the `Retro.hlsl` file in this directory. Feel free to modify and experiment!

View file

@ -0,0 +1,58 @@
// A minimal pixel shader that shows some raster bars
// The terminal graphics as a texture
Texture2D shaderTexture;
SamplerState samplerState;
// Terminal settings such as the resolution of the texture
cbuffer PixelShaderSettings {
// Time since pixel shader was enabled
float Time;
// UI Scale
float Scale;
// Resolution of the shaderTexture
float2 Resolution;
// Background color as rgba
float4 Background;
};
// A pixel shader is a program that given a texture coordinate (tex) produces a color
// Just ignore the pos parameter
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
{
// Read the color value at the current texture coordinate (tex)
// float4 is tuple of 4 floats, rgba
float4 color = shaderTexture.Sample(samplerState, tex);
// Read the color value at some offset, will be used as shadow
float4 ocolor = shaderTexture.Sample(samplerState, tex+2.0*Scale*float2(-1.0, -1.0)/Resolution.y);
// Thickness of raster
const float thickness = 0.1;
float ny = floor(tex.y/thickness);
float my = tex.y%thickness;
const float pi = 3.141592654;
// ny is used to compute the rasterbar base color
float cola = ny*2.0*pi;
float3 col = 0.75+0.25*float3(sin(cola*0.111), sin(cola*0.222), sin(cola*0.333));
// my is used to compute the rasterbar brightness
// smoothstep is a great little function: https://en.wikipedia.org/wiki/Smoothstep
float brightness = 1.0-smoothstep(0.0, thickness*0.5, abs(my - 0.5*thickness));
float3 rasterColor = col*brightness;
// lerp(x, y, a) is another very useful function: https://en.wikipedia.org/wiki/Linear_interpolation
float3 final = rasterColor;
// Create the drop shadow of the terminal graphics
// .w is the alpha channel, 0 is fully transparent and 1 is fully opaque
final = lerp(final, float(0.0), ocolor.w);
// Draw the terminal graphics
final = lerp(final, color.xyz, color.w);
// Return the final color, set alpha to 1 (ie opaque)
return float4(final, 1.0);
}

View file

@ -0,0 +1,83 @@
// The original retro pixel shader
Texture2D shaderTexture;
SamplerState samplerState;
cbuffer PixelShaderSettings {
float Time;
float Scale;
float2 Resolution;
float4 Background;
};
#define SCANLINE_FACTOR 0.5
#define SCALED_SCANLINE_PERIOD Scale
#define SCALED_GAUSSIAN_SIGMA (2.0*Scale)
static const float M_PI = 3.14159265f;
float Gaussian2D(float x, float y, float sigma)
{
return 1/(sigma*sqrt(2*M_PI)) * exp(-0.5*(x*x + y*y)/sigma/sigma);
}
float4 Blur(Texture2D input, float2 tex_coord, float sigma)
{
uint width, height;
shaderTexture.GetDimensions(width, height);
float texelWidth = 1.0f/width;
float texelHeight = 1.0f/height;
float4 color = { 0, 0, 0, 0 };
int sampleCount = 13;
for (int x = 0; x < sampleCount; x++)
{
float2 samplePos = { 0, 0 };
samplePos.x = tex_coord.x + (x - sampleCount/2) * texelWidth;
for (int y = 0; y < sampleCount; y++)
{
samplePos.y = tex_coord.y + (y - sampleCount/2) * texelHeight;
if (samplePos.x <= 0 || samplePos.y <= 0 || samplePos.x >= width || samplePos.y >= height) continue;
color += input.Sample(samplerState, samplePos) * Gaussian2D((x - sampleCount/2), (y - sampleCount/2), sigma);
}
}
return color;
}
float SquareWave(float y)
{
return 1 - (floor(y / SCALED_SCANLINE_PERIOD) % 2) * SCANLINE_FACTOR;
}
float4 Scanline(float4 color, float4 pos)
{
float wave = SquareWave(pos.y);
// TODO:GH#3929 make this configurable.
// Remove the && false to draw scanlines everywhere.
if (length(color.rgb) < 0.2 && false)
{
return color + wave*0.1;
}
else
{
return color * wave;
}
}
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
{
Texture2D input = shaderTexture;
// TODO:GH#3930 Make these configurable in some way.
float4 color = input.Sample(samplerState, tex);
color += Blur(input, tex, SCALED_GAUSSIAN_SIGMA)*0.3;
color = Scanline(color, pos);
return color;
}

View file

@ -295,11 +295,11 @@ namespace winrt::TerminalApp::implementation
args.Handled(true); args.Handled(true);
} }
void TerminalPage::_HandleToggleRetroEffect(const IInspectable& /*sender*/, void TerminalPage::_HandleToggleShaderEffects(const IInspectable& /*sender*/,
const ActionEventArgs& args) const ActionEventArgs& args)
{ {
const auto termControl = _GetActiveControl(); const auto termControl = _GetActiveControl();
termControl.ToggleRetroEffect(); termControl.ToggleShaderEffects();
args.Handled(true); args.Handled(true);
} }

View file

@ -176,9 +176,9 @@ namespace winrt::TerminalApp::implementation
_ResetFontSizeHandlers(*this, eventArgs); _ResetFontSizeHandlers(*this, eventArgs);
break; break;
} }
case ShortcutAction::ToggleRetroEffect: case ShortcutAction::ToggleShaderEffects:
{ {
_ToggleRetroEffectHandlers(*this, eventArgs); _ToggleShaderEffectsHandlers(*this, eventArgs);
break; break;
} }
case ShortcutAction::ToggleFocusMode: case ShortcutAction::ToggleFocusMode:

View file

@ -49,7 +49,7 @@ namespace winrt::TerminalApp::implementation
TYPED_EVENT(ResizePane, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); TYPED_EVENT(ResizePane, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
TYPED_EVENT(Find, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); TYPED_EVENT(Find, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
TYPED_EVENT(MoveFocus, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); TYPED_EVENT(MoveFocus, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
TYPED_EVENT(ToggleRetroEffect, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); TYPED_EVENT(ToggleShaderEffects, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
TYPED_EVENT(ToggleFocusMode, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); TYPED_EVENT(ToggleFocusMode, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
TYPED_EVENT(ToggleFullscreen, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); TYPED_EVENT(ToggleFullscreen, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
TYPED_EVENT(ToggleAlwaysOnTop, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); TYPED_EVENT(ToggleAlwaysOnTop, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);

View file

@ -35,7 +35,7 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> ResizePane; event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> ResizePane;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> Find; event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> Find;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> MoveFocus; event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> MoveFocus;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> ToggleRetroEffect; event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> ToggleShaderEffects;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> ToggleFocusMode; event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> ToggleFocusMode;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> ToggleFullscreen; event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> ToggleFullscreen;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> ToggleAlwaysOnTop; event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> ToggleAlwaysOnTop;

View file

@ -976,7 +976,7 @@ namespace winrt::TerminalApp::implementation
_actionDispatch->AdjustFontSize({ this, &TerminalPage::_HandleAdjustFontSize }); _actionDispatch->AdjustFontSize({ this, &TerminalPage::_HandleAdjustFontSize });
_actionDispatch->Find({ this, &TerminalPage::_HandleFind }); _actionDispatch->Find({ this, &TerminalPage::_HandleFind });
_actionDispatch->ResetFontSize({ this, &TerminalPage::_HandleResetFontSize }); _actionDispatch->ResetFontSize({ this, &TerminalPage::_HandleResetFontSize });
_actionDispatch->ToggleRetroEffect({ this, &TerminalPage::_HandleToggleRetroEffect }); _actionDispatch->ToggleShaderEffects({ this, &TerminalPage::_HandleToggleShaderEffects });
_actionDispatch->ToggleFocusMode({ this, &TerminalPage::_HandleToggleFocusMode }); _actionDispatch->ToggleFocusMode({ this, &TerminalPage::_HandleToggleFocusMode });
_actionDispatch->ToggleFullscreen({ this, &TerminalPage::_HandleToggleFullscreen }); _actionDispatch->ToggleFullscreen({ this, &TerminalPage::_HandleToggleFullscreen });
_actionDispatch->ToggleAlwaysOnTop({ this, &TerminalPage::_HandleToggleAlwaysOnTop }); _actionDispatch->ToggleAlwaysOnTop({ this, &TerminalPage::_HandleToggleAlwaysOnTop });

View file

@ -282,7 +282,7 @@ namespace winrt::TerminalApp::implementation
void _HandleAdjustFontSize(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); void _HandleAdjustFontSize(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
void _HandleFind(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); void _HandleFind(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
void _HandleResetFontSize(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); void _HandleResetFontSize(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
void _HandleToggleRetroEffect(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); void _HandleToggleShaderEffects(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
void _HandleToggleFocusMode(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); void _HandleToggleFocusMode(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
void _HandleToggleFullscreen(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); void _HandleToggleFullscreen(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
void _HandleToggleAlwaysOnTop(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); void _HandleToggleAlwaysOnTop(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);

View file

@ -182,6 +182,7 @@ namespace winrt::TerminalApp::implementation
std::tie(_BackgroundImageHorizontalAlignment, _BackgroundImageVerticalAlignment) = ConvertConvergedAlignment(profile.BackgroundImageAlignment()); std::tie(_BackgroundImageHorizontalAlignment, _BackgroundImageVerticalAlignment) = ConvertConvergedAlignment(profile.BackgroundImageAlignment());
_RetroTerminalEffect = profile.RetroTerminalEffect(); _RetroTerminalEffect = profile.RetroTerminalEffect();
_PixelShaderPath = winrt::hstring{ wil::ExpandEnvironmentStringsW<std::wstring>(profile.PixelShaderPath().c_str()) };
_AntialiasingMode = profile.AntialiasingMode(); _AntialiasingMode = profile.AntialiasingMode();

View file

@ -121,6 +121,7 @@ namespace winrt::TerminalApp::implementation
GETSET_PROPERTY(bool, SoftwareRendering, false); GETSET_PROPERTY(bool, SoftwareRendering, false);
GETSET_PROPERTY(bool, ForceVTInput, false); GETSET_PROPERTY(bool, ForceVTInput, false);
GETSET_PROPERTY(hstring, PixelShaderPath);
#pragma warning(pop) #pragma warning(pop)
private: private:

View file

@ -54,8 +54,11 @@ namespace Microsoft.Terminal.TerminalControl
TextAntialiasingMode AntialiasingMode; TextAntialiasingMode AntialiasingMode;
// Experimental Settings
Boolean RetroTerminalEffect; Boolean RetroTerminalEffect;
Boolean ForceFullRepaintRendering; Boolean ForceFullRepaintRendering;
Boolean SoftwareRendering; Boolean SoftwareRendering;
String PixelShaderPath;
}; };
} }

View file

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
Version 2.0 Version 2.0
The primary goals of this format is to allow a simple XML format The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes various data types are done through the TypeConverter classes
associated with the data types. associated with the data types.
Example: Example:
... ado.net/XML headers & schema ... ... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader> <resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader> <resheader name="version">2.0</resheader>
@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment> <comment>This is a comment</comment>
</data> </data>
There are any number of "resheader" rows that contain simple There are any number of "resheader" rows that contain simple
name/value pairs. name/value pairs.
Each data row contains a name, and value. The row also contains a Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture. text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the Classes that don't support this are serialized and stored with the
mimetype set. mimetype set.
The mimetype is used for serialized objects, and tells the The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly: extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below. read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64 mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64 mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64 mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter : using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
--> -->
@ -179,11 +179,22 @@
<value>Ctrl+Click to follow link</value> <value>Ctrl+Click to follow link</value>
</data> </data>
<data name="NoticeFontNotFound" xml:space="preserve"> <data name="NoticeFontNotFound" xml:space="preserve">
<value>Unable to find the selected font "{0}". <value>Unable to find the selected font "{0}".
"{1}" has been selected instead. "{1}" has been selected instead.
Please either install the missing font or choose another one.</value> Please either install the missing font or choose another one.</value>
<comment>0 and 1 are names of fonts provided by the user and system respectively.</comment> <comment>0 and 1 are names of fonts provided by the user and system respectively.</comment>
</data> </data>
</root> <data name="PixelShaderNotFound" xml:space="preserve">
<value>Unable to find the provided shader "{0}".</value>
<comment>{0} is a file name</comment>
</data>
<data name="PixelShaderCompileFailed" xml:space="preserve">
<value>Unable to compile the specified pixel shader.</value>
</data>
<data name="UnexpectedRendererError" xml:space="preserve">
<value>Renderer encountered an unexpected error: {0}</value>
<comment>{0} is an error code.</comment>
</data>
</root>

View file

@ -308,7 +308,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Update DxEngine settings under the lock // Update DxEngine settings under the lock
_renderEngine->SetSelectionBackground(_settings.SelectionBackground()); _renderEngine->SetSelectionBackground(_settings.SelectionBackground());
_renderEngine->SetRetroTerminalEffects(_settings.RetroTerminalEffect()); _renderEngine->SetRetroTerminalEffect(_settings.RetroTerminalEffect());
_renderEngine->SetPixelShaderPath(_settings.PixelShaderPath());
_renderEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering()); _renderEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering());
_renderEngine->SetSoftwareRendering(_settings.SoftwareRendering()); _renderEngine->SetSoftwareRendering(_settings.SoftwareRendering());
@ -348,10 +349,27 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_SendInputToConnection(wstr); _SendInputToConnection(wstr);
} }
void TermControl::ToggleRetroEffect() void TermControl::ToggleShaderEffects()
{ {
auto lock = _terminal->LockForWriting(); auto lock = _terminal->LockForWriting();
_renderEngine->SetRetroTerminalEffects(!_renderEngine->GetRetroTerminalEffects()); // Originally, this action could be used to enable the retro effects
// even when they're set to `false` in the settings. If the user didn't
// specify a custom pixel shader, manually enable the legacy retro
// effect first. This will ensure that a toggle off->on will still work,
// even if they currently have retro effect off.
if (_settings.PixelShaderPath().empty() && !_renderEngine->GetRetroTerminalEffect())
{
// SetRetroTerminalEffect to true will enable the effect. In this
// case, the shader effect will already be disabled (because neither
// a pixel shader nor the retro effects were originally requested).
// So we _don't_ want to toggle it again below, because that would
// toggle it back off.
_renderEngine->SetRetroTerminalEffect(true);
}
else
{
_renderEngine->ToggleShaderEffects();
}
} }
// Method Description: // Method Description:
@ -616,6 +634,45 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
} }
} }
// Method Description:
// - Called when the renderer triggers a warning. It might do this when it
// fails to find a shader file, or fails to compile a shader. We'll take
// that renderer warning, and display a dialog to the user with and
// appropriate error message. WE'll display the dialog with our
// RaiseNotice event.
// Arguments:
// - hr: an HRESULT describing the warning
// Return Value:
// - <none>
winrt::fire_and_forget TermControl::_RendererWarning(const HRESULT hr)
{
auto weakThis{ get_weak() };
co_await winrt::resume_foreground(Dispatcher());
if (auto control{ weakThis.get() })
{
winrt::hstring message;
if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr ||
HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
{
message = { fmt::format(std::wstring_view{ RS_(L"PixelShaderNotFound") },
_settings.PixelShaderPath()) };
}
else if (D2DERR_SHADER_COMPILE_FAILED == hr)
{
message = { fmt::format(std::wstring_view{ RS_(L"PixelShaderCompileFailed") }) };
}
else
{
message = { fmt::format(std::wstring_view{ RS_(L"UnexpectedRendererError") },
hr) };
}
auto noticeArgs = winrt::make<NoticeEventArgs>(NoticeLevel::Warning, std::move(message));
control->_raiseNoticeHandlers(*control, std::move(noticeArgs));
}
}
void TermControl::_AttachDxgiSwapChainToXaml(IDXGISwapChain1* swapChain) void TermControl::_AttachDxgiSwapChainToXaml(IDXGISwapChain1* swapChain)
{ {
auto nativePanel = SwapChainPanel().as<ISwapChainPanelNative>(); auto nativePanel = SwapChainPanel().as<ISwapChainPanelNative>();
@ -695,7 +752,14 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_terminal->CreateFromSettings(_settings, renderTarget); _terminal->CreateFromSettings(_settings, renderTarget);
dxEngine->SetRetroTerminalEffects(_settings.RetroTerminalEffect()); // IMPORTANT! Set this callback up sooner than later. If we do it
// after Enable, then it'll be possible to paint the frame once
// _before_ the warning handler is set up, and then warnings from
// the first paint will be ignored!
dxEngine->SetWarningCallback(std::bind(&TermControl::_RendererWarning, this, std::placeholders::_1));
dxEngine->SetRetroTerminalEffect(_settings.RetroTerminalEffect());
dxEngine->SetPixelShaderPath(_settings.PixelShaderPath());
dxEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering()); dxEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering());
dxEngine->SetSoftwareRendering(_settings.SoftwareRendering()); dxEngine->SetSoftwareRendering(_settings.SoftwareRendering());

View file

@ -122,12 +122,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void ResetFontSize(); void ResetFontSize();
void SendInput(const winrt::hstring& input); void SendInput(const winrt::hstring& input);
void ToggleRetroEffect(); void ToggleShaderEffects();
winrt::fire_and_forget RenderEngineSwapChainChanged(); winrt::fire_and_forget RenderEngineSwapChainChanged();
void _AttachDxgiSwapChainToXaml(IDXGISwapChain1* swapChain); void _AttachDxgiSwapChainToXaml(IDXGISwapChain1* swapChain);
winrt::fire_and_forget _RendererEnteredErrorState(); winrt::fire_and_forget _RendererEnteredErrorState();
void _RenderRetryButton_Click(IInspectable const& button, IInspectable const& args); void _RenderRetryButton_Click(IInspectable const& button, IInspectable const& args);
winrt::fire_and_forget _RendererWarning(const HRESULT hr);
void CreateSearchBoxControl(); void CreateSearchBoxControl();

View file

@ -17,7 +17,7 @@ namespace Microsoft.Terminal.TerminalControl
// If you press F7 or Alt and get a runtime error, go make sure both copies are the same. // If you press F7 or Alt and get a runtime error, go make sure both copies are the same.
[uuid("0ddf4edc-3fda-4dee-97ca-a417ee3dd510")] interface IDirectKeyListener { [uuid("0ddf4edc-3fda-4dee-97ca-a417ee3dd510")] interface IDirectKeyListener {
Boolean OnDirectKeyEvent(UInt32 vkey, UInt8 scanCode, Boolean down); Boolean OnDirectKeyEvent(UInt32 vkey, UInt8 scanCode, Boolean down);
} };
[flags] [flags]
enum CopyFormat enum CopyFormat
@ -103,8 +103,8 @@ namespace Microsoft.Terminal.TerminalControl
void AdjustFontSize(Int32 fontSizeDelta); void AdjustFontSize(Int32 fontSizeDelta);
void ResetFontSize(); void ResetFontSize();
void ToggleShaderEffects();
void SendInput(String input); void SendInput(String input);
void ToggleRetroEffect();
void TaskbarProgressChanged(); void TaskbarProgressChanged();
UInt64 TaskbarState { get; }; UInt64 TaskbarState { get; };

View file

@ -47,7 +47,8 @@ static constexpr std::string_view ToggleCommandPaletteKey{ "commandPalette" };
static constexpr std::string_view ToggleFocusModeKey{ "toggleFocusMode" }; static constexpr std::string_view ToggleFocusModeKey{ "toggleFocusMode" };
static constexpr std::string_view ToggleFullscreenKey{ "toggleFullscreen" }; static constexpr std::string_view ToggleFullscreenKey{ "toggleFullscreen" };
static constexpr std::string_view TogglePaneZoomKey{ "togglePaneZoom" }; static constexpr std::string_view TogglePaneZoomKey{ "togglePaneZoom" };
static constexpr std::string_view ToggleRetroEffectKey{ "toggleRetroEffect" }; static constexpr std::string_view LegacyToggleRetroEffectKey{ "toggleRetroEffect" };
static constexpr std::string_view ToggleShaderEffectsKey{ "toggleShaderEffects" };
static constexpr std::string_view MoveTabKey{ "moveTab" }; static constexpr std::string_view MoveTabKey{ "moveTab" };
static constexpr std::string_view BreakIntoDebuggerKey{ "breakIntoDebugger" }; static constexpr std::string_view BreakIntoDebuggerKey{ "breakIntoDebugger" };
@ -109,7 +110,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{ ToggleFocusModeKey, ShortcutAction::ToggleFocusMode }, { ToggleFocusModeKey, ShortcutAction::ToggleFocusMode },
{ ToggleFullscreenKey, ShortcutAction::ToggleFullscreen }, { ToggleFullscreenKey, ShortcutAction::ToggleFullscreen },
{ TogglePaneZoomKey, ShortcutAction::TogglePaneZoom }, { TogglePaneZoomKey, ShortcutAction::TogglePaneZoom },
{ ToggleRetroEffectKey, ShortcutAction::ToggleRetroEffect }, { LegacyToggleRetroEffectKey, ShortcutAction::ToggleShaderEffects },
{ ToggleShaderEffectsKey, ShortcutAction::ToggleShaderEffects },
{ MoveTabKey, ShortcutAction::MoveTab }, { MoveTabKey, ShortcutAction::MoveTab },
{ BreakIntoDebuggerKey, ShortcutAction::BreakIntoDebugger }, { BreakIntoDebuggerKey, ShortcutAction::BreakIntoDebugger },
{ UnboundKey, ShortcutAction::Invalid }, { UnboundKey, ShortcutAction::Invalid },
@ -309,7 +311,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{ ShortcutAction::ToggleFocusMode, RS_(L"ToggleFocusModeCommandKey") }, { ShortcutAction::ToggleFocusMode, RS_(L"ToggleFocusModeCommandKey") },
{ ShortcutAction::ToggleFullscreen, RS_(L"ToggleFullscreenCommandKey") }, { ShortcutAction::ToggleFullscreen, RS_(L"ToggleFullscreenCommandKey") },
{ ShortcutAction::TogglePaneZoom, RS_(L"TogglePaneZoomCommandKey") }, { ShortcutAction::TogglePaneZoom, RS_(L"TogglePaneZoomCommandKey") },
{ ShortcutAction::ToggleRetroEffect, RS_(L"ToggleRetroEffectCommandKey") }, { ShortcutAction::ToggleShaderEffects, RS_(L"ToggleShaderEffectsCommandKey") },
{ ShortcutAction::MoveTab, L"" }, // Intentionally omitted, must be generated by GenerateName { ShortcutAction::MoveTab, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::BreakIntoDebugger, RS_(L"BreakIntoDebuggerCommandKey") }, { ShortcutAction::BreakIntoDebugger, RS_(L"BreakIntoDebuggerCommandKey") },
}; };

View file

@ -36,7 +36,7 @@ namespace Microsoft.Terminal.Settings.Model
ResizePane, ResizePane,
MoveFocus, MoveFocus,
Find, Find,
ToggleRetroEffect, ToggleShaderEffects,
ToggleFocusMode, ToggleFocusMode,
ToggleFullscreen, ToggleFullscreen,
ToggleAlwaysOnTop, ToggleAlwaysOnTop,

View file

@ -58,6 +58,7 @@ static constexpr std::string_view RetroTerminalEffectKey{ "experimental.retroTer
static constexpr std::string_view AntialiasingModeKey{ "antialiasingMode" }; static constexpr std::string_view AntialiasingModeKey{ "antialiasingMode" };
static constexpr std::string_view TabColorKey{ "tabColor" }; static constexpr std::string_view TabColorKey{ "tabColor" };
static constexpr std::string_view BellStyleKey{ "bellStyle" }; static constexpr std::string_view BellStyleKey{ "bellStyle" };
static constexpr std::string_view PixelShaderPathKey{ "experimental.pixelShaderPath" };
static constexpr std::wstring_view DesktopWallpaperEnum{ L"desktopWallpaper" }; static constexpr std::wstring_view DesktopWallpaperEnum{ L"desktopWallpaper" };
@ -109,6 +110,7 @@ winrt::com_ptr<Profile> Profile::CopySettings(winrt::com_ptr<Profile> source)
profile->_CursorShape = source->_CursorShape; profile->_CursorShape = source->_CursorShape;
profile->_CursorHeight = source->_CursorHeight; profile->_CursorHeight = source->_CursorHeight;
profile->_BellStyle = source->_BellStyle; profile->_BellStyle = source->_BellStyle;
profile->_PixelShaderPath = source->_PixelShaderPath;
profile->_BackgroundImageAlignment = source->_BackgroundImageAlignment; profile->_BackgroundImageAlignment = source->_BackgroundImageAlignment;
profile->_ConnectionType = source->_ConnectionType; profile->_ConnectionType = source->_ConnectionType;
@ -334,10 +336,9 @@ void Profile::LayerJson(const Json::Value& json)
JsonUtils::GetValueForKey(json, BackgroundImageAlignmentKey, _BackgroundImageAlignment); JsonUtils::GetValueForKey(json, BackgroundImageAlignmentKey, _BackgroundImageAlignment);
JsonUtils::GetValueForKey(json, RetroTerminalEffectKey, _RetroTerminalEffect); JsonUtils::GetValueForKey(json, RetroTerminalEffectKey, _RetroTerminalEffect);
JsonUtils::GetValueForKey(json, AntialiasingModeKey, _AntialiasingMode); JsonUtils::GetValueForKey(json, AntialiasingModeKey, _AntialiasingMode);
JsonUtils::GetValueForKey(json, TabColorKey, _TabColor); JsonUtils::GetValueForKey(json, TabColorKey, _TabColor);
JsonUtils::GetValueForKey(json, BellStyleKey, _BellStyle); JsonUtils::GetValueForKey(json, BellStyleKey, _BellStyle);
JsonUtils::GetValueForKey(json, PixelShaderPathKey, _PixelShaderPath);
} }
// Method Description: // Method Description:
@ -529,6 +530,7 @@ Json::Value Profile::ToJson() const
JsonUtils::SetValueForKey(json, AntialiasingModeKey, _AntialiasingMode); JsonUtils::SetValueForKey(json, AntialiasingModeKey, _AntialiasingMode);
JsonUtils::SetValueForKey(json, TabColorKey, _TabColor); JsonUtils::SetValueForKey(json, TabColorKey, _TabColor);
JsonUtils::SetValueForKey(json, BellStyleKey, _BellStyle); JsonUtils::SetValueForKey(json, BellStyleKey, _BellStyle);
JsonUtils::SetValueForKey(json, PixelShaderPathKey, _PixelShaderPath);
return json; return json;
} }

View file

@ -94,6 +94,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
GETSET_SETTING(Microsoft::Terminal::TerminalControl::TextAntialiasingMode, AntialiasingMode, Microsoft::Terminal::TerminalControl::TextAntialiasingMode::Grayscale); GETSET_SETTING(Microsoft::Terminal::TerminalControl::TextAntialiasingMode, AntialiasingMode, Microsoft::Terminal::TerminalControl::TextAntialiasingMode::Grayscale);
GETSET_SETTING(bool, RetroTerminalEffect, false); GETSET_SETTING(bool, RetroTerminalEffect, false);
GETSET_SETTING(hstring, PixelShaderPath, L"");
GETSET_SETTING(bool, ForceFullRepaintRendering, false); GETSET_SETTING(bool, ForceFullRepaintRendering, false);
GETSET_SETTING(bool, SoftwareRendering, false); GETSET_SETTING(bool, SoftwareRendering, false);

View file

@ -135,6 +135,10 @@ namespace Microsoft.Terminal.Settings.Model
void ClearRetroTerminalEffect(); void ClearRetroTerminalEffect();
Boolean RetroTerminalEffect; Boolean RetroTerminalEffect;
Boolean HasPixelShaderPath();
void ClearPixelShaderPath();
String PixelShaderPath;
Boolean HasForceFullRepaintRendering(); Boolean HasForceFullRepaintRendering();
void ClearForceFullRepaintRendering(); void ClearForceFullRepaintRendering();
Boolean ForceFullRepaintRendering; Boolean ForceFullRepaintRendering;

View file

@ -357,8 +357,8 @@
<data name="TogglePaneZoomCommandKey" xml:space="preserve"> <data name="TogglePaneZoomCommandKey" xml:space="preserve">
<value>Toggle pane zoom</value> <value>Toggle pane zoom</value>
</data> </data>
<data name="ToggleRetroEffectCommandKey" xml:space="preserve"> <data name="ToggleShaderEffectsCommandKey" xml:space="preserve">
<value>Toggle retro terminal effect</value> <value>Toggle terminal visual effects</value>
</data> </data>
<data name="BreakIntoDebuggerCommandKey" xml:space="preserve"> <data name="BreakIntoDebuggerCommandKey" xml:space="preserve">
<value>Break into the debugger</value> <value>Break into the debugger</value>

View file

@ -112,7 +112,6 @@
{ "command": "openSettings", "keys": "ctrl+," }, { "command": "openSettings", "keys": "ctrl+," },
{ "command": { "action": "openSettings", "target": "defaultsFile" }, "keys": "ctrl+alt+," }, { "command": { "action": "openSettings", "target": "defaultsFile" }, "keys": "ctrl+alt+," },
{ "command": "find", "keys": "ctrl+shift+f" }, { "command": "find", "keys": "ctrl+shift+f" },
{ "command": "toggleRetroEffect" },
{ "command": "openTabColorPicker" }, { "command": "openTabColorPicker" },
{ "command": "commandPalette", "keys":"ctrl+shift+p" }, { "command": "commandPalette", "keys":"ctrl+shift+p" },

View file

@ -285,7 +285,7 @@
{ "command": "openSettings", "keys": "ctrl+," }, { "command": "openSettings", "keys": "ctrl+," },
{ "command": { "action": "openSettings", "target": "defaultsFile" }, "keys": "ctrl+alt+," }, { "command": { "action": "openSettings", "target": "defaultsFile" }, "keys": "ctrl+alt+," },
{ "command": "find", "keys": "ctrl+shift+f" }, { "command": "find", "keys": "ctrl+shift+f" },
{ "command": "toggleRetroEffect" }, { "command": "toggleShaderEffects" },
{ "command": "openTabColorPicker" }, { "command": "openTabColorPicker" },
{ "command": "renameTab" }, { "command": "renameTab" },
{ "command": "openTabRenamer" }, { "command": "openTabRenamer" },

View file

@ -87,7 +87,9 @@ DxEngine::DxEngine() :
_swapChainDesc{ 0 }, _swapChainDesc{ 0 },
_swapChainFrameLatencyWaitableObject{ INVALID_HANDLE_VALUE }, _swapChainFrameLatencyWaitableObject{ INVALID_HANDLE_VALUE },
_recreateDeviceRequested{ false }, _recreateDeviceRequested{ false },
_retroTerminalEffects{ false }, _terminalEffectsEnabled{ false },
_retroTerminalEffect{ false },
_pixelShaderPath{},
_forceFullRepaintRendering{ false }, _forceFullRepaintRendering{ false },
_softwareRendering{ false }, _softwareRendering{ false },
_antialiasingMode{ D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE }, _antialiasingMode{ D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE },
@ -229,6 +231,85 @@ _CompileShader(
#endif #endif
} }
// Routine Description:
// - Checks if terminal effects are enabled.
// Arguments:
// Return Value:
// - True if terminal effects are enabled
bool DxEngine::_HasTerminalEffects() const noexcept
{
return _terminalEffectsEnabled && (_retroTerminalEffect || !_pixelShaderPath.empty());
}
// Routine Description:
// - Toggles terminal effects off and on. If no terminal effect is configured has no effect
// Arguments:
// Return Value:
// - Void
void DxEngine::ToggleShaderEffects()
{
_terminalEffectsEnabled = !_terminalEffectsEnabled;
LOG_IF_FAILED(InvalidateAll());
}
// Routine Description:
// - Loads pixel shader source depending on _retroTerminalEffect and _pixelShaderPath
// Arguments:
// Return Value:
// - Pixel shader source code
std::string DxEngine::_LoadPixelShaderFile() const
{
// If the user specified the new pixel shader, it has precedence
if (!_pixelShaderPath.empty())
{
try
{
wil::unique_hfile hFile{ CreateFileW(_pixelShaderPath.c_str(),
GENERIC_READ,
FILE_SHARE_READ,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr) };
THROW_LAST_ERROR_IF(!hFile); // This will be caught below.
// fileSize is in bytes
const auto fileSize = GetFileSize(hFile.get(), nullptr);
THROW_LAST_ERROR_IF(fileSize == INVALID_FILE_SIZE);
std::vector<char> utf8buffer;
utf8buffer.reserve(fileSize);
DWORD bytesRead = 0;
THROW_LAST_ERROR_IF(!ReadFile(hFile.get(), utf8buffer.data(), fileSize, &bytesRead, nullptr));
// convert buffer to UTF-8 string
std::string utf8string(utf8buffer.data(), fileSize);
return utf8string;
}
catch (...)
{
// If we ran into any problems during loading pixel shader, call to
// the warning callback to surface the file not found error
const auto exceptionHr = LOG_CAUGHT_EXCEPTION();
if (_pfnWarningCallback)
{
_pfnWarningCallback(exceptionHr);
}
return std::string{};
}
}
else if (_retroTerminalEffect)
{
return std::string{ retroPixelShaderString };
}
return std::string{};
}
// Routine Description: // Routine Description:
// - Setup D3D objects for doing shader things for terminal effects. // - Setup D3D objects for doing shader things for terminal effects.
// Arguments: // Arguments:
@ -236,6 +317,19 @@ _CompileShader(
// - HRESULT status. // - HRESULT status.
HRESULT DxEngine::_SetupTerminalEffects() HRESULT DxEngine::_SetupTerminalEffects()
{ {
_pixelShaderLoaded = false;
const auto pixelShaderSource = _LoadPixelShaderFile();
if (pixelShaderSource.empty())
{
// There's no shader to compile. This might be due to failing to load,
// or because there's just no shader enabled at all.
// Turn the effects off for now.
_terminalEffectsEnabled = false;
return S_FALSE;
}
::Microsoft::WRL::ComPtr<ID3D11Texture2D> swapBuffer; ::Microsoft::WRL::ComPtr<ID3D11Texture2D> swapBuffer;
RETURN_IF_FAILED(_dxgiSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&swapBuffer)); RETURN_IF_FAILED(_dxgiSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&swapBuffer));
@ -260,12 +354,26 @@ HRESULT DxEngine::_SetupTerminalEffects()
// Prepare shaders. // Prepare shaders.
auto vertexBlob = _CompileShader(screenVertexShaderString, "vs_5_0"); auto vertexBlob = _CompileShader(screenVertexShaderString, "vs_5_0");
auto pixelBlob = _CompileShader(screenPixelShaderString, "ps_5_0"); Microsoft::WRL::ComPtr<ID3DBlob> pixelBlob;
// TODO:GH#3928 move the shader files to to hlsl files and package their // As the pixel shader source is user provided it's possible there's a problem with it
// build output to UWP app and load with these. // so load it inside a try catch, on any error log and fallback on the error pixel shader
// ::Microsoft::WRL::ComPtr<ID3DBlob> vertexBlob, pixelBlob; // If even the error pixel shader fails to load rely on standard exception handling
// RETURN_IF_FAILED(D3DReadFileToBlob(L"ScreenVertexShader.cso", &vertexBlob)); try
// RETURN_IF_FAILED(D3DReadFileToBlob(L"ScreenPixelShader.cso", &pixelBlob)); {
pixelBlob = _CompileShader(pixelShaderSource, "ps_5_0");
}
catch (...)
{
// Call to the warning callback to surface the shader compile error
const auto exceptionHr = LOG_CAUGHT_EXCEPTION();
if (_pfnWarningCallback)
{
// If this fails, it'll return E_FAIL, which is terribly
// uninformative. Instead, raise something more useful.
_pfnWarningCallback(D2DERR_SHADER_COMPILE_FAILED);
}
return exceptionHr;
}
RETURN_IF_FAILED(_d3dDevice->CreateVertexShader( RETURN_IF_FAILED(_d3dDevice->CreateVertexShader(
vertexBlob->GetBufferPointer(), vertexBlob->GetBufferPointer(),
@ -329,23 +437,47 @@ HRESULT DxEngine::_SetupTerminalEffects()
// Create the texture sampler state. // Create the texture sampler state.
RETURN_IF_FAILED(_d3dDevice->CreateSamplerState(&samplerDesc, &_samplerState)); RETURN_IF_FAILED(_d3dDevice->CreateSamplerState(&samplerDesc, &_samplerState));
_pixelShaderLoaded = true;
return S_OK; return S_OK;
} }
// Routine Description: // Routine Description:
// - Puts the correct values in _pixelShaderSettings, so the struct can be // - Puts the correct values in _pixelShaderSettings, so the struct can be
// passed the GPU. // passed the GPU and updates the GPU resource.
// Arguments: // Arguments:
// - <none> // - <none>
// Return Value: // Return Value:
// - <none> // - <none>
void DxEngine::_ComputePixelShaderSettings() noexcept void DxEngine::_ComputePixelShaderSettings() noexcept
{ {
// Retro scan lines alternate every pixel row at 100% scaling. if (_HasTerminalEffects() && _d3dDeviceContext && _pixelShaderSettingsBuffer)
_pixelShaderSettings.ScaledScanLinePeriod = _scale * 1.0f; {
try
{
// Set the time
// TODO:GH#7013 Grab timestamp
_pixelShaderSettings.Time = 0.0f;
// Gaussian distribution sigma used for blurring. // Set the UI Scale
_pixelShaderSettings.ScaledGaussianSigma = _scale * 2.0f; _pixelShaderSettings.Scale = _scale;
// Set the display resolution
const float w = 1.0f * _displaySizePixels.width<UINT>();
const float h = 1.0f * _displaySizePixels.height<UINT>();
_pixelShaderSettings.Resolution = XMFLOAT2{ w, h };
// Set the background
DirectX::XMFLOAT4 background{};
background.x = _backgroundColor.r;
background.y = _backgroundColor.g;
background.z = _backgroundColor.b;
background.w = _backgroundColor.a;
_pixelShaderSettings.Background = background;
_d3dDeviceContext->UpdateSubresource(_pixelShaderSettingsBuffer.Get(), 0, nullptr, &_pixelShaderSettings, 0, 0);
}
CATCH_LOG();
}
} }
// Routine Description; // Routine Description;
@ -521,13 +653,13 @@ try
} }
} }
if (_retroTerminalEffects) if (_HasTerminalEffects())
{ {
const HRESULT hr = _SetupTerminalEffects(); const HRESULT hr = _SetupTerminalEffects();
if (FAILED(hr)) if (FAILED(hr))
{ {
_retroTerminalEffects = false;
LOG_HR_MSG(hr, "Failed to setup terminal effects. Disabling."); LOG_HR_MSG(hr, "Failed to setup terminal effects. Disabling.");
_terminalEffectsEnabled = false;
} }
} }
@ -560,6 +692,8 @@ try
CATCH_LOG(); // A failure in the notification function isn't a failure to prepare, so just log it and go on. CATCH_LOG(); // A failure in the notification function isn't a failure to prepare, so just log it and go on.
} }
_recreateDeviceRequested = false;
return S_OK; return S_OK;
} }
CATCH_RETURN(); CATCH_RETURN();
@ -675,7 +809,15 @@ void DxEngine::_ReleaseDeviceResources() noexcept
{ {
_haveDeviceResources = false; _haveDeviceResources = false;
// Destroy Terminal Effect resources
_renderTargetView.Reset();
_vertexShader.Reset();
_pixelShader.Reset();
_vertexLayout.Reset();
_screenQuadVertexBuffer.Reset();
_pixelShaderSettingsBuffer.Reset(); _pixelShaderSettingsBuffer.Reset();
_samplerState.Reset();
_framebufferCapture.Reset();
_d2dBrushForeground.Reset(); _d2dBrushForeground.Reset();
_d2dBrushBackground.Reset(); _d2dBrushBackground.Reset();
@ -802,17 +944,38 @@ void DxEngine::SetCallback(std::function<void()> pfn)
_pfn = pfn; _pfn = pfn;
} }
bool DxEngine::GetRetroTerminalEffects() const noexcept void DxEngine::SetWarningCallback(std::function<void(const HRESULT)> pfn)
{ {
return _retroTerminalEffects; _pfnWarningCallback = pfn;
} }
void DxEngine::SetRetroTerminalEffects(bool enable) noexcept bool DxEngine::GetRetroTerminalEffect() const noexcept
{
return _retroTerminalEffect;
}
void DxEngine::SetRetroTerminalEffect(bool enable) noexcept
try try
{ {
if (_retroTerminalEffects != enable) if (_retroTerminalEffect != enable)
{ {
_retroTerminalEffects = enable; // Enable shader effects if the path isn't empty. Otherwise leave it untouched.
_terminalEffectsEnabled = enable ? true : _terminalEffectsEnabled;
_retroTerminalEffect = enable;
_recreateDeviceRequested = true;
LOG_IF_FAILED(InvalidateAll());
}
}
CATCH_LOG()
void DxEngine::SetPixelShaderPath(std::wstring_view value) noexcept
try
{
if (_pixelShaderPath != value)
{
// Enable shader effects if the path isn't empty. Otherwise leave it untouched.
_terminalEffectsEnabled = value.empty() ? _terminalEffectsEnabled : true;
_pixelShaderPath = { value };
_recreateDeviceRequested = true; _recreateDeviceRequested = true;
LOG_IF_FAILED(InvalidateAll()); LOG_IF_FAILED(InvalidateAll());
} }
@ -1085,14 +1248,9 @@ try
{ {
RETURN_HR_IF(E_NOT_VALID_STATE, _isPainting); // invalid to start a paint while painting. RETURN_HR_IF(E_NOT_VALID_STATE, _isPainting); // invalid to start a paint while painting.
// If someone explicitly requested differential rendering off, then we need to invalidate everything // If full repaints are needed then we need to invalidate everything
// so the entire frame is repainted. // so the entire frame is repainted.
// if (_FullRepaintNeeded())
// If retro terminal effects are on, we must invalidate everything for them to draw correctly.
// Yes, this will further impact the performance of retro terminal effects.
// But we're talking about running the entire display pipeline through a shader for
// cosmetic effect, so performance isn't likely the top concern with this feature.
if (_forceFullRepaintRendering || _retroTerminalEffects)
{ {
_invalidMap.set_all(); _invalidMap.set_all();
} }
@ -1118,7 +1276,6 @@ try
if (!_haveDeviceResources || _recreateDeviceRequested) if (!_haveDeviceResources || _recreateDeviceRequested)
{ {
RETURN_IF_FAILED(_CreateDeviceResources(true)); RETURN_IF_FAILED(_CreateDeviceResources(true));
_recreateDeviceRequested = false;
} }
else if (_displaySizePixels != clientSize || _prevScale != _scale) else if (_displaySizePixels != clientSize || _prevScale != _scale)
{ {
@ -1312,12 +1469,12 @@ void DxEngine::WaitUntilCanRender() noexcept
{ {
if (_presentReady) if (_presentReady)
{ {
if (_retroTerminalEffects) if (_HasTerminalEffects() && _pixelShaderLoaded)
{ {
const HRESULT hr2 = _PaintTerminalEffects(); const HRESULT hr2 = _PaintTerminalEffects();
if (FAILED(hr2)) if (FAILED(hr2))
{ {
_retroTerminalEffects = false; _pixelShaderLoaded = false;
LOG_HR_MSG(hr2, "Failed to paint terminal effects. Disabling."); LOG_HR_MSG(hr2, "Failed to paint terminal effects. Disabling.");
} }
} }
@ -1378,10 +1535,15 @@ void DxEngine::WaitUntilCanRender() noexcept
} }
} }
// Finally copy the front image (being presented now) onto the backing buffer // If we are doing full repaints we don't need to copy front buffer to back buffer
// (where we are about to draw the next frame) so we can draw only the differences if (!_FullRepaintNeeded())
// next frame. {
RETURN_IF_FAILED(_CopyFrontToBack()); // Finally copy the front image (being presented now) onto the backing buffer
// (where we are about to draw the next frame) so we can draw only the differences
// next frame.
RETURN_IF_FAILED(_CopyFrontToBack());
}
_presentReady = false; _presentReady = false;
_presentDirty.clear(); _presentDirty.clear();
@ -1687,6 +1849,18 @@ try
} }
CATCH_RETURN() CATCH_RETURN()
[[nodiscard]] bool DxEngine::_FullRepaintNeeded() const noexcept
{
// If someone explicitly requested differential rendering off, then we need to invalidate everything
// so the entire frame is repainted.
//
// If terminal effects are on, we must invalidate everything for them to draw correctly.
// Yes, this will further impact the performance of terminal effects.
// But we're talking about running the entire display pipeline through a shader for
// cosmetic effect, so performance isn't likely the top concern with this feature.
return _forceFullRepaintRendering || _HasTerminalEffects();
}
// Routine Description: // Routine Description:
// - Updates the default brush colors used for drawing // - Updates the default brush colors used for drawing
// Arguments: // Arguments:
@ -1749,6 +1923,9 @@ CATCH_RETURN()
_hyperlinkStrokeStyle = (textAttributes.GetHyperlinkId() == _hyperlinkHoveredId) ? _strokeStyle : _dashStrokeStyle; _hyperlinkStrokeStyle = (textAttributes.GetHyperlinkId() == _hyperlinkHoveredId) ? _strokeStyle : _dashStrokeStyle;
} }
// Update pixel shader settings as background color might have changed
_ComputePixelShaderSettings();
return S_OK; return S_OK;
} }
@ -1813,15 +1990,8 @@ CATCH_RETURN();
RETURN_IF_FAILED(InvalidateAll()); RETURN_IF_FAILED(InvalidateAll());
if (_retroTerminalEffects && _d3dDeviceContext && _pixelShaderSettingsBuffer) // Update pixel shader settings as scale might have changed
{ _ComputePixelShaderSettings();
_ComputePixelShaderSettings();
try
{
_d3dDeviceContext->UpdateSubresource(_pixelShaderSettingsBuffer.Get(), 0, nullptr, &_pixelShaderSettings, 0, 0);
}
CATCH_RETURN();
}
return S_OK; return S_OK;
} }

View file

@ -15,6 +15,7 @@
#include <d2d1.h> #include <d2d1.h>
#include <d2d1_1.h> #include <d2d1_1.h>
#include <d2d1helper.h> #include <d2d1helper.h>
#include <DirectXMath.h>
#include <dwrite.h> #include <dwrite.h>
#include <dwrite_1.h> #include <dwrite_1.h>
#include <dwrite_2.h> #include <dwrite_2.h>
@ -55,9 +56,14 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT SetWindowSize(const SIZE pixels) noexcept; [[nodiscard]] HRESULT SetWindowSize(const SIZE pixels) noexcept;
void SetCallback(std::function<void()> pfn); void SetCallback(std::function<void()> pfn);
void SetWarningCallback(std::function<void(const HRESULT)> pfn);
bool GetRetroTerminalEffects() const noexcept; void ToggleShaderEffects();
void SetRetroTerminalEffects(bool enable) noexcept;
bool GetRetroTerminalEffect() const noexcept;
void SetRetroTerminalEffect(bool enable) noexcept;
void SetPixelShaderPath(std::wstring_view value) noexcept;
void SetForceFullRepaintRendering(bool enable) noexcept; void SetForceFullRepaintRendering(bool enable) noexcept;
@ -124,6 +130,7 @@ namespace Microsoft::Console::Render
protected: protected:
[[nodiscard]] HRESULT _DoUpdateTitle(_In_ const std::wstring& newTitle) noexcept override; [[nodiscard]] HRESULT _DoUpdateTitle(_In_ const std::wstring& newTitle) noexcept override;
[[nodiscard]] HRESULT _PaintTerminalEffects() noexcept; [[nodiscard]] HRESULT _PaintTerminalEffects() noexcept;
[[nodiscard]] bool _FullRepaintNeeded() const noexcept;
private: private:
enum class SwapChainMode enum class SwapChainMode
@ -141,6 +148,7 @@ namespace Microsoft::Console::Render
float _prevScale; float _prevScale;
std::function<void()> _pfn; std::function<void()> _pfn;
std::function<void(const HRESULT)> _pfnWarningCallback;
bool _isEnabled; bool _isEnabled;
bool _isPainting; bool _isPainting;
@ -221,7 +229,22 @@ namespace Microsoft::Console::Render
std::unique_ptr<DrawingContext> _drawingContext; std::unique_ptr<DrawingContext> _drawingContext;
// Terminal effects resources. // Terminal effects resources.
bool _retroTerminalEffects;
// Controls if configured terminal effects are enabled
bool _terminalEffectsEnabled;
// Experimental and deprecated retro terminal effect
// Preserved for backwards compatibility
// Implemented in terms of the more generic pixel shader effect
// Has precendence over pixel shader effect
bool _retroTerminalEffect;
// Experimental and pixel shader effect
// Allows user to load a pixel shader from a few presets or from a file path
std::wstring _pixelShaderPath;
bool _pixelShaderLoaded{ false };
// DX resources needed for terminal effects
::Microsoft::WRL::ComPtr<ID3D11RenderTargetView> _renderTargetView; ::Microsoft::WRL::ComPtr<ID3D11RenderTargetView> _renderTargetView;
::Microsoft::WRL::ComPtr<ID3D11VertexShader> _vertexShader; ::Microsoft::WRL::ComPtr<ID3D11VertexShader> _vertexShader;
::Microsoft::WRL::ComPtr<ID3D11PixelShader> _pixelShader; ::Microsoft::WRL::ComPtr<ID3D11PixelShader> _pixelShader;
@ -242,12 +265,19 @@ namespace Microsoft::Console::Render
// DirectX constant buffers need to be a multiple of 16; align to pad the size. // DirectX constant buffers need to be a multiple of 16; align to pad the size.
__declspec(align(16)) struct __declspec(align(16)) struct
{ {
float ScaledScanLinePeriod; // Note: This can be seen as API endpoint towards user provided pixel shaders.
float ScaledGaussianSigma; // Changes here can break existing pixel shaders so be careful with changing datatypes
// and order of parameters
float Time;
float Scale;
DirectX::XMFLOAT2 Resolution;
DirectX::XMFLOAT4 Background;
#pragma warning(suppress : 4324) // structure was padded due to __declspec(align()) #pragma warning(suppress : 4324) // structure was padded due to __declspec(align())
} _pixelShaderSettings; } _pixelShaderSettings;
[[nodiscard]] HRESULT _CreateDeviceResources(const bool createSwapChain) noexcept; [[nodiscard]] HRESULT _CreateDeviceResources(const bool createSwapChain) noexcept;
bool _HasTerminalEffects() const noexcept;
std::string _LoadPixelShaderFile() const;
HRESULT _SetupTerminalEffects(); HRESULT _SetupTerminalEffects();
void _ComputePixelShaderSettings() noexcept; void _ComputePixelShaderSettings() noexcept;

View file

@ -1,19 +1,23 @@
#pragma once #pragma once
#ifdef __INSIDE_WINDOWS #ifdef __INSIDE_WINDOWS
const char screenPixelShaderString[] = ""; constexpr std::string_view retroPixelShaderString{ "" };
#else #else
const char screenPixelShaderString[] = R"( constexpr std::string_view retroPixelShaderString{ R"(
// The original retro pixel shader
Texture2D shaderTexture; Texture2D shaderTexture;
SamplerState samplerState; SamplerState samplerState;
cbuffer PixelShaderSettings cbuffer PixelShaderSettings {
{ float Time;
float ScaledScanLinePeriod; float Scale;
float ScaledGaussianSigma; float2 Resolution;
float4 Background;
}; };
#define SCANLINE_FACTOR 0.5 #define SCANLINE_FACTOR 0.5
#define SCALED_SCANLINE_PERIOD Scale
#define SCALED_GAUSSIAN_SIGMA (2.0*Scale)
static const float M_PI = 3.14159265f; static const float M_PI = 3.14159265f;
@ -53,7 +57,7 @@ float4 Blur(Texture2D input, float2 tex_coord, float sigma)
float SquareWave(float y) float SquareWave(float y)
{ {
return 1 - (floor(y / ScaledScanLinePeriod) % 2) * SCANLINE_FACTOR; return 1 - (floor(y / SCALED_SCANLINE_PERIOD) % 2) * SCANLINE_FACTOR;
} }
float4 Scanline(float4 color, float4 pos) float4 Scanline(float4 color, float4 pos)
@ -78,10 +82,10 @@ float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
// TODO:GH#3930 Make these configurable in some way. // TODO:GH#3930 Make these configurable in some way.
float4 color = input.Sample(samplerState, tex); float4 color = input.Sample(samplerState, tex);
color += Blur(input, tex, ScaledGaussianSigma)*0.3; color += Blur(input, tex, SCALED_GAUSSIAN_SIGMA)*0.3;
color = Scanline(color, pos); color = Scanline(color, pos);
return color; return color;
} }
)"; )" };
#endif #endif