-
Notifications
You must be signed in to change notification settings - Fork 8.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AtlasEngine: Fix various bugs found in testing #13906
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -326,7 +326,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation | |
|
||
// Tell the DX Engine to notify us when the swap chain changes. | ||
// We do this after we initially set the swapchain so as to avoid unnecessary callbacks (and locking problems) | ||
_renderEngine->SetCallback(std::bind(&ControlCore::_renderEngineSwapChainChanged, this)); | ||
_renderEngine->SetCallback([this](auto handle) { _renderEngineSwapChainChanged(handle); }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. YES |
||
|
||
_renderEngine->SetRetroTerminalEffect(_settings->RetroTerminalEffect()); | ||
_renderEngine->SetPixelShaderPath(_settings->PixelShaderPath()); | ||
|
@@ -596,24 +596,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation | |
|
||
void ControlCore::ToggleShaderEffects() | ||
{ | ||
const auto path = _settings->PixelShaderPath(); | ||
auto lock = _terminal->LockForWriting(); | ||
// 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()) | ||
if (path.empty()) | ||
{ | ||
// 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); | ||
_renderEngine->SetRetroTerminalEffect(!_renderEngine->GetRetroTerminalEffect()); | ||
} | ||
else | ||
{ | ||
_renderEngine->ToggleShaderEffects(); | ||
_renderEngine->SetPixelShaderPath(_renderEngine->GetPixelShaderPath().empty() ? std::wstring_view{ path } : std::wstring_view{}); | ||
Comment on lines
+599
to
+612
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh thank you, this will make #9898 so easy |
||
} | ||
// Always redraw after toggling effects. This way even if the control | ||
// does not have focus it will update immediately. | ||
|
@@ -1558,25 +1554,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation | |
} | ||
} | ||
|
||
uint64_t ControlCore::SwapChainHandle() const | ||
{ | ||
// This is called by: | ||
// * TermControl::RenderEngineSwapChainChanged, who is only registered | ||
// after Core::Initialize() is called. | ||
// * TermControl::_InitializeTerminal, after the call to Initialize, for | ||
// _AttachDxgiSwapChainToXaml. | ||
// In both cases, we'll have a _renderEngine by then. | ||
return reinterpret_cast<uint64_t>(_renderEngine->GetSwapChainHandle()); | ||
} | ||
|
||
void ControlCore::_rendererWarning(const HRESULT hr) | ||
{ | ||
_RendererWarningHandlers(*this, winrt::make<RendererWarningArgs>(hr)); | ||
} | ||
|
||
void ControlCore::_renderEngineSwapChainChanged() | ||
void ControlCore::_renderEngineSwapChainChanged(const HANDLE handle) | ||
{ | ||
_SwapChainChangedHandlers(*this, nullptr); | ||
_SwapChainChangedHandlers(*this, winrt::box_value<uint64_t>(reinterpret_cast<uint64_t>(handle))); | ||
} | ||
|
||
void ControlCore::_rendererBackgroundColorChanged() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,8 +76,6 @@ namespace Microsoft.Terminal.Control | |
IControlAppearance UnfocusedAppearance { get; }; | ||
Boolean HasUnfocusedAppearance(); | ||
|
||
UInt64 SwapChainHandle { get; }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cc @zadjii-msft - this will impact xproc work |
||
|
||
Windows.Foundation.Size FontSize { get; }; | ||
String FontFaceName { get; }; | ||
UInt16 FontWeight { get; }; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -731,19 +731,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation | |
return _core.ConnectionState(); | ||
} | ||
|
||
winrt::fire_and_forget TermControl::RenderEngineSwapChainChanged(IInspectable /*sender*/, IInspectable /*args*/) | ||
winrt::fire_and_forget TermControl::RenderEngineSwapChainChanged(IInspectable /*sender*/, IInspectable args) | ||
{ | ||
// This event is only registered during terminal initialization, | ||
// so we don't need to check _initializedTerminal. | ||
// We also don't lock for things that come back from the renderer. | ||
auto weakThis{ get_weak() }; | ||
const auto weakThis{ get_weak() }; | ||
|
||
// Create a copy of the swap chain HANDLE in args, since we don't own that parameter. | ||
// By the time we return from the co_await below, it might be deleted already. | ||
winrt::handle handle; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. winrt has a handle wrapper type? didn't know! |
||
const auto processHandle = GetCurrentProcess(); | ||
const auto sourceHandle = reinterpret_cast<HANDLE>(winrt::unbox_value<uint64_t>(args)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, if the core is out of proc, how will this work? It can raise the event with it's own handle value, sure, but then the TermControl is gonna have to come back around and ask the Core to dupe the handle to the window process anyways. I think I can merge these together, just gonna ignore There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm... Well it's up to us to define whether HANDLE ownership is transferred for that event or not right? If you define it so that the HANDLE ownership is transferred than you can |
||
THROW_IF_WIN32_BOOL_FALSE(DuplicateHandle(processHandle, sourceHandle, processHandle, handle.put(), 0, FALSE, DUPLICATE_SAME_ACCESS)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no issue doing this in-proc i presume? nice |
||
|
||
co_await wil::resume_foreground(Dispatcher()); | ||
|
||
if (auto control{ weakThis.get() }) | ||
{ | ||
const auto chainHandle = reinterpret_cast<HANDLE>(control->_core.SwapChainHandle()); | ||
_AttachDxgiSwapChainToXaml(chainHandle); | ||
_AttachDxgiSwapChainToXaml(handle.get()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it okay that we let the handle de-scope later and get destroyed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. because we just duplicated it... don't we need to keep the duplicate copy "alive" (if we're out of proc, for example?) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean yeah we can do that, but it'd be pretty foolish if |
||
} | ||
} | ||
|
||
|
@@ -830,21 +835,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation | |
} | ||
_interactivity.Initialize(); | ||
|
||
_AttachDxgiSwapChainToXaml(reinterpret_cast<HANDLE>(_core.SwapChainHandle())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this change doesn't impact launching terminal with a bunch of splits at the same time, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (things that may cause device contention) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
// Tell the DX Engine to notify us when the swap chain changes. We do | ||
// this after we initially set the swapchain so as to avoid unnecessary | ||
// callbacks (and locking problems) | ||
_core.SwapChainChanged({ get_weak(), &TermControl::RenderEngineSwapChainChanged }); | ||
|
||
// !! LOAD BEARING !! | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. don't love the removal of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They cause problems though... |
||
// Make sure you enable painting _AFTER_ calling _AttachDxgiSwapChainToXaml | ||
// | ||
// If you EnablePainting first, then you almost certainly won't have any | ||
// problems when running in Debug. However, in Release, you'll run into | ||
// issues where the Renderer starts trying to paint before we've | ||
// actually attached the swapchain to anything, and the DxEngine is not | ||
// prepared to handle that. | ||
_core.EnablePainting(); | ||
|
||
auto bufferHeight = _core.BufferHeight(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -582,6 +582,9 @@ void AtlasEngine::_createResources() | |
D3D_FEATURE_LEVEL_11_0, | ||
D3D_FEATURE_LEVEL_10_1, | ||
D3D_FEATURE_LEVEL_10_0, | ||
D3D_FEATURE_LEVEL_9_3, | ||
D3D_FEATURE_LEVEL_9_2, | ||
D3D_FEATURE_LEVEL_9_1, | ||
Comment on lines
+585
to
+587
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
}; | ||
|
||
auto hr = E_UNEXPECTED; | ||
|
@@ -624,16 +627,6 @@ void AtlasEngine::_createResources() | |
_r.deviceContext = deviceContext.query<ID3D11DeviceContext1>(); | ||
} | ||
|
||
{ | ||
wil::com_ptr<IDXGIAdapter1> dxgiAdapter; | ||
THROW_IF_FAILED(_r.device.query<IDXGIObject>()->GetParent(__uuidof(dxgiAdapter), dxgiAdapter.put_void())); | ||
THROW_IF_FAILED(dxgiAdapter->GetParent(__uuidof(_r.dxgiFactory), _r.dxgiFactory.put_void())); | ||
|
||
DXGI_ADAPTER_DESC1 desc; | ||
THROW_IF_FAILED(dxgiAdapter->GetDesc1(&desc)); | ||
_r.d2dMode = debugForceD2DMode || WI_IsAnyFlagSet(desc.Flags, DXGI_ADAPTER_FLAG_REMOTE | DXGI_ADAPTER_FLAG_SOFTWARE); | ||
} | ||
|
||
#ifndef NDEBUG | ||
// D3D debug messages | ||
if (deviceFlags & D3D11_CREATE_DEVICE_DEBUG) | ||
|
@@ -646,6 +639,18 @@ void AtlasEngine::_createResources() | |
} | ||
#endif // NDEBUG | ||
|
||
const auto featureLevel = _r.device->GetFeatureLevel(); | ||
|
||
{ | ||
wil::com_ptr<IDXGIAdapter1> dxgiAdapter; | ||
THROW_IF_FAILED(_r.device.query<IDXGIObject>()->GetParent(__uuidof(dxgiAdapter), dxgiAdapter.put_void())); | ||
THROW_IF_FAILED(dxgiAdapter->GetParent(__uuidof(_r.dxgiFactory), _r.dxgiFactory.put_void())); | ||
|
||
DXGI_ADAPTER_DESC1 desc; | ||
THROW_IF_FAILED(dxgiAdapter->GetDesc1(&desc)); | ||
_r.d2dMode = debugForceD2DMode || featureLevel < D3D_FEATURE_LEVEL_10_0 || WI_IsAnyFlagSet(desc.Flags, DXGI_ADAPTER_FLAG_REMOTE | DXGI_ADAPTER_FLAG_SOFTWARE); | ||
} | ||
|
||
if (!_r.d2dMode) | ||
{ | ||
// Our constant buffer will never get resized | ||
|
@@ -663,7 +668,7 @@ void AtlasEngine::_createResources() | |
if (!_api.customPixelShaderPath.empty()) | ||
{ | ||
const char* target = nullptr; | ||
switch (_r.device->GetFeatureLevel()) | ||
switch (featureLevel) | ||
{ | ||
case D3D_FEATURE_LEVEL_10_0: | ||
target = "ps_4_0"; | ||
|
@@ -791,10 +796,19 @@ void AtlasEngine::_createSwapChain() | |
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; | ||
desc.SampleDesc.Count = 1; | ||
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; | ||
desc.BufferCount = 2; | ||
// Sometimes up to 2 buffers are locked, for instance during screen capture or when moving the window. | ||
// 3 buffers seems to guarantee a stable framerate at display frequency at all times. | ||
desc.BufferCount = 3; | ||
Comment on lines
+819
to
+821
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
desc.Scaling = DXGI_SCALING_NONE; | ||
desc.SwapEffect = _sr.isWindows10OrGreater && !_r.d2dMode ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; | ||
// If our background is opaque we can enable "independent" flips by setting DXGI_SWAP_EFFECT_FLIP_DISCARD and DXGI_ALPHA_MODE_IGNORE. | ||
// DXGI_SWAP_EFFECT_FLIP_DISCARD is a mode that was created at a time were display drivers | ||
// lacked support for Multiplane Overlays (MPO) and were copying buffers was expensive. | ||
// This allowed DWM to quickly draw overlays (like gamebars) on top of rendered content. | ||
// With faster GPU memory in general and with support for MPO in particular this isn't | ||
// really an advantage anymore. Instead DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL allows for a | ||
// more "intelligent" composition and display updates to occur like Panel Self Refresh | ||
// (PSR) which requires dirty rectangles (Present1 API) to work correctly. | ||
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; | ||
Comment on lines
+823
to
+830
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
// If our background is opaque we can enable "independent" flips by setting DXGI_ALPHA_MODE_IGNORE. | ||
// As our swap chain won't have to compose with DWM anymore it reduces the display latency dramatically. | ||
desc.AlphaMode = _api.backgroundOpaqueMixin ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED; | ||
desc.Flags = debugGeneralPerformance ? 0 : DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; | ||
|
@@ -837,7 +851,7 @@ void AtlasEngine::_createSwapChain() | |
{ | ||
try | ||
{ | ||
_api.swapChainChangedCallback(); | ||
_api.swapChainChangedCallback(_api.swapChainHandle.get()); | ||
} | ||
CATCH_LOG(); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason
std::bind
wouldn't work and I didn't want to figure it out.