Compare commits
27 commits
main
...
dev/migrie
Author | SHA1 | Date | |
---|---|---|---|
207522a93a | |||
a67478c382 | |||
58f31f1b6b | |||
db3bdcb8aa | |||
f516840730 | |||
3504fbea5d | |||
a4fd18d443 | |||
63f87e9d33 | |||
77ef9debb3 | |||
cb8ada7eeb | |||
fb82b1f0fb | |||
32c536c76f | |||
de28bc435d | |||
8fad38d291 | |||
6eacddb4c3 | |||
fd15381384 | |||
c3de05cbd2 | |||
cd99201c70 | |||
468d58706e | |||
d5b59a09e2 | |||
0c4173b238 | |||
d5d8576d1b | |||
54db2c2c3b | |||
3a8c8830e8 | |||
eea3a9bebe | |||
4695dd486c | |||
1f906aba84 |
|
@ -30,4 +30,4 @@ float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
|
|||
|
||||
// Return the final color
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
|
69
samples/PixelShaders/RainbowCursor.hlsl
Normal file
69
samples/PixelShaders/RainbowCursor.hlsl
Normal file
|
@ -0,0 +1,69 @@
|
|||
// 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 {
|
||||
|
||||
float Time; // The number of seconds since the pixel shader was enabled
|
||||
float Scale; // UI Scale
|
||||
float2 Resolution; // Resolution of the shaderTexture
|
||||
float4 Background; // Background color as rgba
|
||||
|
||||
float2 GlyphSize;
|
||||
float2 CursorPosition;
|
||||
float2 BufferSize;
|
||||
};
|
||||
|
||||
// Helper for converting a hue [0, 1) to an RGB value.
|
||||
// Credit to https://www.chilliant.com/rgb2hsv.html
|
||||
float3 HUEtoRGB(float H)
|
||||
{
|
||||
float R = abs(H * 6 - 3) - 1;
|
||||
float G = 2 - abs(H * 6 - 2);
|
||||
float B = 2 - abs(H * 6 - 4);
|
||||
return saturate(float3(R,G,B));
|
||||
};
|
||||
|
||||
// A pixel shader is a program that given a texture coordinate (tex) produces a color.
|
||||
// tex is an x,y tuple that ranges from 0,0 (top left) to 1,1 (bottom right).
|
||||
// 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);
|
||||
|
||||
// Find the location of the cursor within the viewport, in pixels.
|
||||
float2 pxCursorTopLeft = CursorPosition * GlyphSize;
|
||||
float2 pxCursorBottomRight = (CursorPosition + float2(1, 1)) * GlyphSize;
|
||||
|
||||
// Convert pixel position [{0}, {Resolution}) to texel space [{0}, {1})
|
||||
float2 texelRelativeTopLeft = pxCursorTopLeft / Resolution;
|
||||
float2 texelRelativeBottomRight = pxCursorBottomRight / Resolution;
|
||||
|
||||
// If we're rendering the cells within the bounds of the cursor cell...
|
||||
if ((tex.x >= texelRelativeTopLeft.x && tex.x <= texelRelativeBottomRight.x) &&
|
||||
(tex.y >= texelRelativeTopLeft.y && tex.y <= texelRelativeBottomRight.y)) {
|
||||
|
||||
float2 withinCursor = (tex - texelRelativeTopLeft) * (GlyphSize);
|
||||
|
||||
// Duration of the animation, in seconds
|
||||
float duration = 2.0;
|
||||
|
||||
// fmod(x, y) will cycle linearly between 0 and y. fmod(x, 1) will just
|
||||
// get the fractional component of x. Since HUEtoRGB(0) == HUEtoRBG(1),
|
||||
// the colors naturally cycle.
|
||||
// This lets us scroll through the entire color spectrum smoothly.
|
||||
// multiply by 1.25 to make the animation a little faster - this gets a
|
||||
// bit more hue variation in the cursor at a single time.
|
||||
float hue = lerp(0.00, 1, fmod(1.25*(Time - withinCursor.y) / duration, 1));
|
||||
color.xyz = HUEtoRGB(hue);
|
||||
color.a = 1.0;
|
||||
}
|
||||
|
||||
// Return the final color
|
||||
return color;
|
||||
}
|
|
@ -278,13 +278,12 @@ void Renderer::TriggerRedrawCursor(const COORD* const pcoord)
|
|||
const SMALL_RECT cursorRect = { pcoord->X, pcoord->Y, pcoord->X + cursorWidth - 1, pcoord->Y };
|
||||
Viewport cursorView = Viewport::FromInclusive(BufferToScreenLine(cursorRect, lineRendition));
|
||||
|
||||
// The region is clamped within the viewport boundaries and we only
|
||||
// trigger a redraw if the region is not empty.
|
||||
Viewport view = _pData->GetViewport();
|
||||
cursorView = view.Clamp(cursorView);
|
||||
|
||||
if (cursorView.IsValid())
|
||||
if (view.IsInBounds(cursorView))
|
||||
{
|
||||
// The region is clamped within the viewport boundaries and we only
|
||||
// trigger a redraw if the region is not empty.
|
||||
cursorView = view.Clamp(cursorView);
|
||||
const SMALL_RECT updateRect = view.ConvertToOrigin(cursorView).ToExclusive();
|
||||
for (IRenderEngine* pEngine : _rgpEngines)
|
||||
{
|
||||
|
|
|
@ -49,6 +49,8 @@ D3D11_INPUT_ELEMENT_DESC _shaderInputLayout[] = {
|
|||
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }
|
||||
};
|
||||
|
||||
static constexpr std::string_view NOP_PIXEL_SHADER{ nopPixelShaderString };
|
||||
|
||||
#pragma hdrstop
|
||||
|
||||
using namespace Microsoft::Console::Render;
|
||||
|
@ -246,11 +248,20 @@ bool DxEngine::_HasTerminalEffects() const noexcept
|
|||
void DxEngine::ToggleShaderEffects()
|
||||
{
|
||||
_terminalEffectsEnabled = !_terminalEffectsEnabled;
|
||||
// If we're enabling the shader, try reloading it.
|
||||
// It's not as good as hot reloading the hlsl file itself, but it's good enough.
|
||||
if (_terminalEffectsEnabled)
|
||||
{
|
||||
_SetupTerminalEffects();
|
||||
}
|
||||
LOG_IF_FAILED(InvalidateAll());
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Loads pixel shader source depending on _retroTerminalEffect and _pixelShaderPath
|
||||
// - Loads pixel shader source depending on _retroTerminalEffect and _pixelShaderPath.
|
||||
// - If for any reason, we failed to load the shader file, then always fall
|
||||
// back to the no-op shader. It does nothing - it simply passes
|
||||
// the color through unmodified.
|
||||
// Arguments:
|
||||
// Return Value:
|
||||
// - Pixel shader source code
|
||||
|
@ -304,7 +315,11 @@ std::string DxEngine::_LoadPixelShaderFile() const
|
|||
return std::string{ retroPixelShaderString };
|
||||
}
|
||||
|
||||
return std::string{};
|
||||
// If for any reason, we failed to load the shader file, then always fall
|
||||
// back to the no-op shader. This one's embedded as a string inside this
|
||||
// binary. It'll definitely compile, and it does nothing - it simply passes
|
||||
// the color through unmodified.
|
||||
return std::string{ NOP_PIXEL_SHADER };
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -472,6 +487,11 @@ void DxEngine::_ComputePixelShaderSettings() noexcept
|
|||
background.w = _backgroundColor.a;
|
||||
_pixelShaderSettings.Background = background;
|
||||
|
||||
til::size glyphSize{ _fontRenderData->GlyphCell() };
|
||||
_pixelShaderSettings.GlyphSize = XMFLOAT2{ ::base::saturated_cast<float>(glyphSize.width()), ::base::saturated_cast<float>(glyphSize.height()) };
|
||||
_pixelShaderSettings.CursorPosition = XMFLOAT2{ ::base::saturated_cast<float>(_lastCursor.x()), ::base::saturated_cast<float>(_lastCursor.y()) };
|
||||
_pixelShaderSettings.BufferSize = XMFLOAT2{ ::base::saturated_cast<float>(_lastBufferSize.width()), ::base::saturated_cast<float>(_lastBufferSize.height()) };
|
||||
|
||||
_d3dDeviceContext->UpdateSubresource(_pixelShaderSettingsBuffer.Get(), 0, nullptr, &_pixelShaderSettings, 0, 0);
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
@ -685,15 +705,25 @@ try
|
|||
}
|
||||
}
|
||||
|
||||
if (_HasTerminalEffects())
|
||||
::Microsoft::WRL::ComPtr<ID3D11Texture2D> swapBuffer;
|
||||
RETURN_IF_FAILED(_dxgiSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&swapBuffer));
|
||||
|
||||
// Setup _framebuffer, to where we'll write all console graphics
|
||||
D3D11_TEXTURE2D_DESC framebufferDesc{};
|
||||
swapBuffer->GetDesc(&framebufferDesc);
|
||||
WI_SetFlag(framebufferDesc.BindFlags, D3D11_BIND_SHADER_RESOURCE);
|
||||
RETURN_IF_FAILED(_d3dDevice->CreateTexture2D(&framebufferDesc, nullptr, &_framebuffer));
|
||||
RETURN_IF_FAILED(_d3dDevice->CreateTexture2D(&framebufferDesc, nullptr, &_otherbuffer));
|
||||
|
||||
// if (_HasTerminalEffects())
|
||||
// {
|
||||
const HRESULT hr = _SetupTerminalEffects();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
const HRESULT hr = _SetupTerminalEffects();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
LOG_HR_MSG(hr, "Failed to setup terminal effects. Disabling.");
|
||||
_terminalEffectsEnabled = false;
|
||||
}
|
||||
LOG_HR_MSG(hr, "Failed to setup terminal effects. Disabling.");
|
||||
_terminalEffectsEnabled = false;
|
||||
}
|
||||
// }
|
||||
|
||||
// With a new swap chain, mark the entire thing as invalid.
|
||||
RETURN_IF_FAILED(InvalidateAll());
|
||||
|
@ -752,14 +782,19 @@ static constexpr D2D1_ALPHA_MODE _dxgiAlphaToD2d1Alpha(DXGI_ALPHA_MODE mode) noe
|
|||
{
|
||||
try
|
||||
{
|
||||
// Pull surface out of swap chain.
|
||||
RETURN_IF_FAILED(_dxgiSwapChain->GetBuffer(0, IID_PPV_ARGS(&_dxgiSurface)));
|
||||
|
||||
// Make a bitmap and bind it to the swap chain surface
|
||||
const auto bitmapProperties = D2D1::BitmapProperties1(
|
||||
D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
|
||||
D2D1::PixelFormat(_swapChainDesc.Format, _dxgiAlphaToD2d1Alpha(_swapChainDesc.AlphaMode)));
|
||||
|
||||
// Create the bitmaps for each frame buffer. We'll draw to these
|
||||
// surfaces.
|
||||
// Get each framebuffer as a IDXGISurface, and use
|
||||
// CreateBitmapFromDxgiSurface to instantiate the ID2D1Bitmap
|
||||
RETURN_IF_FAILED(_otherbuffer->QueryInterface(IID_PPV_ARGS(&_dxgiSurface)));
|
||||
RETURN_IF_FAILED(_d2dDeviceContext->CreateBitmapFromDxgiSurface(_dxgiSurface.Get(), bitmapProperties, &_d2dOtherBitmap));
|
||||
|
||||
RETURN_IF_FAILED(_framebuffer->QueryInterface(IID_PPV_ARGS(&_dxgiSurface)));
|
||||
RETURN_IF_FAILED(_d2dDeviceContext->CreateBitmapFromDxgiSurface(_dxgiSurface.Get(), bitmapProperties, &_d2dBitmap));
|
||||
|
||||
// Assign that bitmap as the target of the D2D device context. Draw commands hit the context
|
||||
|
@ -768,7 +803,6 @@ static constexpr D2D1_ALPHA_MODE _dxgiAlphaToD2d1Alpha(DXGI_ALPHA_MODE mode) noe
|
|||
// The leg bone connected to the knee bone,
|
||||
// The knee bone connected to the thigh bone
|
||||
// ... and so on)
|
||||
|
||||
_d2dDeviceContext->SetTarget(_d2dBitmap.Get());
|
||||
|
||||
// We need the AntialiasMode for non-text object to be Aliased to ensure
|
||||
|
@ -861,6 +895,8 @@ void DxEngine::_ReleaseDeviceResources() noexcept
|
|||
_d2dDeviceContext->EndDraw();
|
||||
}
|
||||
|
||||
_framebuffer.Reset();
|
||||
|
||||
_d2dDeviceContext.Reset();
|
||||
|
||||
_dxgiSurface.Reset();
|
||||
|
@ -1086,6 +1122,7 @@ CATCH_RETURN()
|
|||
// - S_OK
|
||||
[[nodiscard]] HRESULT DxEngine::InvalidateCursor(const SMALL_RECT* const psrRegion) noexcept
|
||||
{
|
||||
_lastCursor = til::point{ psrRegion->Left, psrRegion->Top };
|
||||
return Invalidate(psrRegion);
|
||||
}
|
||||
|
||||
|
@ -1152,6 +1189,7 @@ try
|
|||
_invalidMap.translate(deltaCells, true);
|
||||
_invalidScroll += deltaCells;
|
||||
_allInvalid = _IsAllInvalid();
|
||||
_lastCursor += deltaCells;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1322,6 +1360,68 @@ try
|
|||
RETURN_IF_FAILED(InvalidateAll());
|
||||
}
|
||||
|
||||
// GH#7147
|
||||
// Here's a bit of trickiness. Each frame, we're rendering what changed
|
||||
// in the console into a framebuffer. But when the frame scrolls, we
|
||||
// don't want to redraw everything we already have, we just want to
|
||||
// shift it up/down.
|
||||
// To facilitate this, we have two buffers that we render to, like a
|
||||
// swapchain. in between frames, we'll copy the contents from the back
|
||||
// buffer into the front buffer, using CopyFromRenderTarget. This allows
|
||||
// us to move the contents from one buffer into the other, at a given
|
||||
// offset,
|
||||
// To optimize this, we only need to do this copying and swapping when
|
||||
// there's actually a scroll delta. Otherwise, the viewport is still
|
||||
// just in the same location, and we can just leave the contents of the
|
||||
// current framebuffer alone.
|
||||
if (_d2dBitmap && _d2dOtherBitmap && _invalidScroll.y() != 0)
|
||||
{
|
||||
// Swap the framebuffers
|
||||
std::swap(_framebuffer, _otherbuffer);
|
||||
std::swap(_d2dBitmap, _d2dOtherBitmap);
|
||||
|
||||
// Figure out how much of the screen to scroll, and where to scroll it to.
|
||||
til::size glyphSize{ _fontRenderData->GlyphCell() };
|
||||
til::point scrollInPixels = _invalidScroll * glyphSize;
|
||||
til::point sourceOrigin{ 0, 0 };
|
||||
// D2D_POINT_2U is an unsigned point, it won't accept negative
|
||||
// numbers. If we want to scroll the frame contents up (s.t. delta.y
|
||||
// < 0), we need to copy just the bottom portion of the last buffer
|
||||
// to 0,0 in the new buffer.
|
||||
if (scrollInPixels.y() < 0)
|
||||
{
|
||||
sourceOrigin = -scrollInPixels;
|
||||
scrollInPixels = til::point{ 0, 0 };
|
||||
}
|
||||
D2D_POINT_2U tgtPos{ scrollInPixels.x<uint32_t>(),
|
||||
scrollInPixels.y<uint32_t>() };
|
||||
|
||||
// auto heightOffset = std::max(sourceOrigin.y<uint32_t>(), scrollInPixels.y<uint32_t>());
|
||||
auto heightOffset = sourceOrigin.y<uint32_t>();
|
||||
til::size realBufferSize{ _lastBufferSize * glyphSize };
|
||||
til::size srcDimensions{ realBufferSize.width() - sourceOrigin.x(),
|
||||
realBufferSize.height() - heightOffset };
|
||||
til::rectangle source{ sourceOrigin, srcDimensions };
|
||||
D2D1_RECT_U srcRect{
|
||||
source.left<uint32_t>(),
|
||||
source.top<uint32_t>(),
|
||||
source.right<uint32_t>(),
|
||||
source.bottom<uint32_t>(),
|
||||
};
|
||||
|
||||
// Get our _d2dDeviceContext as a ID2D1RenderTarget
|
||||
Microsoft::WRL::ComPtr<ID2D1RenderTarget> otherRenderTarget;
|
||||
RETURN_IF_FAILED(_d2dDeviceContext->QueryInterface(IID_PPV_ARGS(&otherRenderTarget)));
|
||||
// Copy the contents from the old buffer (now in otherBuffer) to the
|
||||
// current bitmap.
|
||||
_d2dBitmap->CopyFromRenderTarget(&tgtPos, // destPoint
|
||||
otherRenderTarget.Get(), // renderTarget
|
||||
&srcRect); // srcRect
|
||||
|
||||
// make sure to tell our device that we're drawing to a different bitmap now
|
||||
_d2dDeviceContext->SetTarget(_d2dBitmap.Get());
|
||||
}
|
||||
|
||||
_d2dDeviceContext->BeginDraw();
|
||||
_isPainting = true;
|
||||
|
||||
|
@ -1368,7 +1468,9 @@ try
|
|||
// If there's still a clip hanging around, remove it. We're all done.
|
||||
LOG_IF_FAILED(_customRenderer->EndClip(_drawingContext.get()));
|
||||
|
||||
hr = _d2dDeviceContext->EndDraw();
|
||||
D2D1_TAG tag1;
|
||||
D2D1_TAG tag2;
|
||||
hr = _d2dDeviceContext->EndDraw(&tag1, &tag2);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
|
@ -1431,31 +1533,6 @@ try
|
|||
}
|
||||
CATCH_RETURN()
|
||||
|
||||
// Routine Description:
|
||||
// - Copies the front surface of the swap chain (the one being displayed)
|
||||
// to the back surface of the swap chain (the one we draw on next)
|
||||
// so we can draw on top of what's already there.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - Any DirectX error, a memory error, etc.
|
||||
[[nodiscard]] HRESULT DxEngine::_CopyFrontToBack() noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
Microsoft::WRL::ComPtr<ID3D11Resource> backBuffer;
|
||||
Microsoft::WRL::ComPtr<ID3D11Resource> frontBuffer;
|
||||
|
||||
RETURN_IF_FAILED(_dxgiSwapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer)));
|
||||
RETURN_IF_FAILED(_dxgiSwapChain->GetBuffer(1, IID_PPV_ARGS(&frontBuffer)));
|
||||
|
||||
_d3dDeviceContext->CopyResource(backBuffer.Get(), frontBuffer.Get());
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - When the shaders are on, say that we need to keep redrawing every
|
||||
// possible frame in case they have some smooth action on every frame tick.
|
||||
|
@ -1498,6 +1575,51 @@ void DxEngine::WaitUntilCanRender() noexcept
|
|||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Draws the contents of our _framebuffer to the swapchain. We always do this,
|
||||
// whether the user has specified a custom pixel shader or not.
|
||||
// - This draws a single quad to the entire swapchain, as rendered by the
|
||||
// configured pixel shader.
|
||||
// - If no pixel shader is configured, we'll have already decided to use the
|
||||
// no-op shader (see `nopPixelShaderString` in `ScreenPixelShader.h`), which
|
||||
// will render the _framebuffer unmodified.
|
||||
[[nodiscard]] HRESULT DxEngine::_RenderToSwapChain() noexcept
|
||||
try
|
||||
{
|
||||
// Should have been initialized.
|
||||
RETURN_HR_IF(E_NOT_VALID_STATE, !_framebuffer);
|
||||
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
_framebuffer->GetDesc(&desc);
|
||||
|
||||
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
||||
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
||||
srvDesc.Texture2D.MostDetailedMip = 0;
|
||||
srvDesc.Texture2D.MipLevels = desc.MipLevels;
|
||||
srvDesc.Format = desc.Format;
|
||||
|
||||
::Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> shaderResource;
|
||||
RETURN_IF_FAILED(_d3dDevice->CreateShaderResourceView(_framebuffer.Get(), &srvDesc, &shaderResource));
|
||||
|
||||
// Render the screen quad with shader effects.
|
||||
const UINT stride = sizeof(ShaderInput);
|
||||
const UINT offset = 0;
|
||||
|
||||
_d3dDeviceContext->OMSetRenderTargets(1, _renderTargetView.GetAddressOf(), nullptr);
|
||||
_d3dDeviceContext->IASetVertexBuffers(0, 1, _screenQuadVertexBuffer.GetAddressOf(), &stride, &offset);
|
||||
_d3dDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
|
||||
_d3dDeviceContext->IASetInputLayout(_vertexLayout.Get());
|
||||
_d3dDeviceContext->VSSetShader(_vertexShader.Get(), nullptr, 0);
|
||||
_d3dDeviceContext->PSSetShader(_pixelShader.Get(), nullptr, 0);
|
||||
_d3dDeviceContext->PSSetShaderResources(0, 1, shaderResource.GetAddressOf());
|
||||
_d3dDeviceContext->PSSetSamplers(0, 1, _samplerState.GetAddressOf());
|
||||
_d3dDeviceContext->PSSetConstantBuffers(0, 1, _pixelShaderSettingsBuffer.GetAddressOf());
|
||||
_d3dDeviceContext->Draw(ARRAYSIZE(_screenQuadVertices), 0);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN()
|
||||
|
||||
// Routine Description:
|
||||
// - Takes queued drawing information and presents it to the screen.
|
||||
// - This is separated out so it can be done outside the lock as it's expensive.
|
||||
|
@ -1509,53 +1631,62 @@ void DxEngine::WaitUntilCanRender() noexcept
|
|||
{
|
||||
if (_presentReady)
|
||||
{
|
||||
if (_HasTerminalEffects() && _pixelShaderLoaded)
|
||||
{
|
||||
const HRESULT hr2 = _PaintTerminalEffects();
|
||||
if (FAILED(hr2))
|
||||
{
|
||||
_pixelShaderLoaded = false;
|
||||
LOG_HR_MSG(hr2, "Failed to paint terminal effects. Disabling.");
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
// Updates the pixel shader resource (including time)
|
||||
_ComputePixelShaderSettings();
|
||||
|
||||
// Renders framebuffer texture + potential pixel shader effects
|
||||
LOG_IF_FAILED(_RenderToSwapChain());
|
||||
|
||||
bool recreate = false;
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
// On anything but the first frame, try partial presentation.
|
||||
// We'll do it first because if it fails, we'll try again with full presentation.
|
||||
if (!_firstFrame)
|
||||
{
|
||||
hr = _dxgiSwapChain->Present1(1, 0, &_presentParams);
|
||||
|
||||
// These two error codes are indicated for destroy-and-recreate
|
||||
// If we were told to destroy-and-recreate, we're going to skip straight into doing that
|
||||
// and not try again with full presentation.
|
||||
recreate = hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET;
|
||||
|
||||
// Log this as we actually don't expect it to happen, we just will try again
|
||||
// below for robustness of our drawing.
|
||||
if (FAILED(hr) && !recreate)
|
||||
{
|
||||
LOG_HR(hr);
|
||||
}
|
||||
}
|
||||
|
||||
// If it's the first frame through, we cannot do partial presentation.
|
||||
// Also if partial presentation failed above and we weren't told to skip straight to
|
||||
// device recreation.
|
||||
// In both of these circumstances, do a full presentation.
|
||||
if (_firstFrame || (FAILED(hr) && !recreate))
|
||||
// If we're actually rendering a real shader effect, then just call
|
||||
// the normal Present() to present the whole frame. We're assuming
|
||||
// that the user who's configured a pixel shader isn't concerned
|
||||
// with the optimization that Present1 provides (esp. for remote
|
||||
// desktop scenarios)
|
||||
if (_HasTerminalEffects())
|
||||
{
|
||||
hr = _dxgiSwapChain->Present(1, 0);
|
||||
_firstFrame = false;
|
||||
|
||||
// These two error codes are indicated for destroy-and-recreate
|
||||
recreate = hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET;
|
||||
}
|
||||
else
|
||||
{
|
||||
// On anything but the first frame, try partial presentation.
|
||||
// We'll do it first because if it fails, we'll try again with full presentation.
|
||||
if (!_firstFrame)
|
||||
{
|
||||
hr = _dxgiSwapChain->Present1(1, 0, &_presentParams);
|
||||
|
||||
// These two error codes are indicated for destroy-and-recreate
|
||||
// If we were told to destroy-and-recreate, we're going to skip straight into doing that
|
||||
// and not try again with full presentation.
|
||||
recreate = hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET;
|
||||
|
||||
// Log this as we actually don't expect it to happen, we just will try again
|
||||
// below for robustness of our drawing.
|
||||
if (FAILED(hr) && !recreate)
|
||||
{
|
||||
LOG_HR(hr);
|
||||
}
|
||||
}
|
||||
|
||||
// If it's the first frame through, we cannot do partial presentation.
|
||||
// Also if partial presentation failed above and we weren't told to skip straight to
|
||||
// device recreation.
|
||||
// In both of these circumstances, do a full presentation.
|
||||
if (_firstFrame || (FAILED(hr) && !recreate))
|
||||
{
|
||||
hr = _dxgiSwapChain->Present(1, 0);
|
||||
_firstFrame = false;
|
||||
|
||||
// These two error codes are indicated for destroy-and-recreate
|
||||
recreate = hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET;
|
||||
}
|
||||
}
|
||||
|
||||
// Now check for failure cases from either presentation mode.
|
||||
if (FAILED(hr))
|
||||
|
@ -1575,15 +1706,6 @@ void DxEngine::WaitUntilCanRender() noexcept
|
|||
}
|
||||
}
|
||||
|
||||
// If we are doing full repaints we don't need to copy front buffer to back buffer
|
||||
if (!_FullRepaintNeeded())
|
||||
{
|
||||
// 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;
|
||||
|
||||
_presentDirty.clear();
|
||||
|
@ -1841,64 +1963,14 @@ CATCH_RETURN()
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Paint terminal effects.
|
||||
// Arguments:
|
||||
// Return Value:
|
||||
// - S_OK or relevant DirectX error.
|
||||
[[nodiscard]] HRESULT DxEngine::_PaintTerminalEffects() noexcept
|
||||
try
|
||||
{
|
||||
// Should have been initialized.
|
||||
RETURN_HR_IF(E_NOT_VALID_STATE, !_framebufferCapture);
|
||||
|
||||
// Capture current frame in swap chain to a texture.
|
||||
::Microsoft::WRL::ComPtr<ID3D11Texture2D> swapBuffer;
|
||||
RETURN_IF_FAILED(_dxgiSwapChain->GetBuffer(0, IID_PPV_ARGS(&swapBuffer)));
|
||||
_d3dDeviceContext->CopyResource(_framebufferCapture.Get(), swapBuffer.Get());
|
||||
|
||||
// Prepare captured texture as input resource to shader program.
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
_framebufferCapture->GetDesc(&desc);
|
||||
|
||||
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
||||
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
||||
srvDesc.Texture2D.MostDetailedMip = 0;
|
||||
srvDesc.Texture2D.MipLevels = desc.MipLevels;
|
||||
srvDesc.Format = desc.Format;
|
||||
|
||||
::Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> shaderResource;
|
||||
RETURN_IF_FAILED(_d3dDevice->CreateShaderResourceView(_framebufferCapture.Get(), &srvDesc, &shaderResource));
|
||||
|
||||
// Render the screen quad with shader effects.
|
||||
const UINT stride = sizeof(ShaderInput);
|
||||
const UINT offset = 0;
|
||||
|
||||
_d3dDeviceContext->OMSetRenderTargets(1, _renderTargetView.GetAddressOf(), nullptr);
|
||||
_d3dDeviceContext->IASetVertexBuffers(0, 1, _screenQuadVertexBuffer.GetAddressOf(), &stride, &offset);
|
||||
_d3dDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
|
||||
_d3dDeviceContext->IASetInputLayout(_vertexLayout.Get());
|
||||
_d3dDeviceContext->VSSetShader(_vertexShader.Get(), nullptr, 0);
|
||||
_d3dDeviceContext->PSSetShader(_pixelShader.Get(), nullptr, 0);
|
||||
_d3dDeviceContext->PSSetShaderResources(0, 1, shaderResource.GetAddressOf());
|
||||
_d3dDeviceContext->PSSetSamplers(0, 1, _samplerState.GetAddressOf());
|
||||
_d3dDeviceContext->PSSetConstantBuffers(0, 1, _pixelShaderSettingsBuffer.GetAddressOf());
|
||||
_d3dDeviceContext->Draw(ARRAYSIZE(_screenQuadVertices), 0);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
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 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();
|
||||
// As of GH#7147, we no longer need to repaint everything when shader
|
||||
// effects are enabled.
|
||||
return _forceFullRepaintRendering;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -1967,7 +2039,7 @@ CATCH_RETURN()
|
|||
}
|
||||
|
||||
// Update pixel shader settings as background color might have changed
|
||||
_ComputePixelShaderSettings();
|
||||
// _ComputePixelShaderSettings();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -2038,7 +2110,7 @@ CATCH_RETURN();
|
|||
RETURN_IF_FAILED(InvalidateAll());
|
||||
|
||||
// Update pixel shader settings as scale might have changed
|
||||
_ComputePixelShaderSettings();
|
||||
// _ComputePixelShaderSettings();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -2062,8 +2134,9 @@ float DxEngine::GetScaling() const noexcept
|
|||
// - srNewViewport - The bounds of the new viewport.
|
||||
// Return Value:
|
||||
// - HRESULT S_OK
|
||||
[[nodiscard]] HRESULT DxEngine::UpdateViewport(const SMALL_RECT /*srNewViewport*/) noexcept
|
||||
[[nodiscard]] HRESULT DxEngine::UpdateViewport(const SMALL_RECT srNewViewport) noexcept
|
||||
{
|
||||
_lastBufferSize = til::size{ srNewViewport.Right - srNewViewport.Left, srNewViewport.Bottom - srNewViewport.Top };
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -208,6 +208,7 @@ namespace Microsoft::Console::Render
|
|||
::Microsoft::WRL::ComPtr<ID2D1Device> _d2dDevice;
|
||||
::Microsoft::WRL::ComPtr<ID2D1DeviceContext> _d2dDeviceContext;
|
||||
::Microsoft::WRL::ComPtr<ID2D1Bitmap1> _d2dBitmap;
|
||||
::Microsoft::WRL::ComPtr<ID2D1Bitmap1> _d2dOtherBitmap;
|
||||
::Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> _d2dBrushForeground;
|
||||
::Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> _d2dBrushBackground;
|
||||
|
||||
|
@ -221,10 +222,14 @@ namespace Microsoft::Console::Render
|
|||
wil::unique_handle _swapChainFrameLatencyWaitableObject;
|
||||
std::unique_ptr<DrawingContext> _drawingContext;
|
||||
|
||||
::Microsoft::WRL::ComPtr<ID3D11Texture2D> _framebuffer{ nullptr };
|
||||
::Microsoft::WRL::ComPtr<ID3D11Texture2D> _otherbuffer{ nullptr };
|
||||
// Terminal effects resources.
|
||||
|
||||
// Controls if configured terminal effects are enabled
|
||||
bool _terminalEffectsEnabled;
|
||||
til::point _lastCursor{ 0, 0 };
|
||||
til::size _lastBufferSize{ 0, 0 };
|
||||
|
||||
// Experimental and deprecated retro terminal effect
|
||||
// Preserved for backwards compatibility
|
||||
|
@ -262,16 +267,23 @@ namespace Microsoft::Console::Render
|
|||
{
|
||||
// Note: This can be seen as API endpoint towards user provided pixel shaders.
|
||||
// Changes here can break existing pixel shaders so be careful with changing datatypes
|
||||
// and order of parameters
|
||||
// and order of parameters.
|
||||
float Time;
|
||||
float Scale;
|
||||
DirectX::XMFLOAT2 Resolution;
|
||||
DirectX::XMFLOAT4 Background;
|
||||
DirectX::XMFLOAT2 GlyphSize;
|
||||
DirectX::XMFLOAT2 CursorPosition;
|
||||
DirectX::XMFLOAT2 BufferSize;
|
||||
// You can always add more values to the end, but they must always be in this order.
|
||||
// Adding values is not a breaking change. Shaders that don't have those values declared will simply ignore them.
|
||||
// However, if you want to use `CursorPostion`, then you'll need to declare all the members that preceed it.
|
||||
#pragma warning(suppress : 4324) // structure was padded due to __declspec(align())
|
||||
} _pixelShaderSettings;
|
||||
|
||||
[[nodiscard]] HRESULT _CreateDeviceResources(const bool createSwapChain) noexcept;
|
||||
[[nodiscard]] HRESULT _CreateSurfaceHandle() noexcept;
|
||||
[[nodiscard]] HRESULT _RenderToSwapChain() noexcept;
|
||||
|
||||
bool _HasTerminalEffects() const noexcept;
|
||||
std::string _LoadPixelShaderFile() const;
|
||||
|
@ -279,6 +291,8 @@ namespace Microsoft::Console::Render
|
|||
void _ComputePixelShaderSettings() noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT _PrepareRenderTarget() noexcept;
|
||||
// [[nodiscard]] HRESULT _PrepareRenderTarget(::Microsoft::WRL::ComPtr<ID3D11Texture2D>& buffer,
|
||||
// ::Microsoft::WRL::ComPtr<ID2D1Bitmap1>& bitmap) noexcept;
|
||||
|
||||
void _ReleaseDeviceResources() noexcept;
|
||||
|
||||
|
@ -289,7 +303,7 @@ namespace Microsoft::Console::Render
|
|||
_In_ size_t StringLength,
|
||||
_Out_ IDWriteTextLayout** ppTextLayout) noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT _CopyFrontToBack() noexcept;
|
||||
// [[nodiscard]] HRESULT _CopyFrontToBack() noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT _EnableDisplayAccess(const bool outputEnabled) noexcept;
|
||||
|
||||
|
|
|
@ -89,3 +89,20 @@ float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
|
|||
}
|
||||
)" };
|
||||
#endif
|
||||
|
||||
constexpr std::string_view nopPixelShaderString{ R"(
|
||||
// No-op shader used for normal operations
|
||||
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;
|
||||
}
|
||||
)" };
|
||||
|
|
Loading…
Reference in a new issue