Added experimental.pixelShaderImagePath (#14073)

I realize I might be one of the few developers that care about custom
shader support in terminal but I thought it's worth proposing it and see
what you think.

This is to support custom shaders with custom textures.

I was thinking of exposing the background image to the shader but that
felt complicated after looking into it.

I have tested exploratively. I think the texture loader is possible to
unit test so that is a possible improvement.

The error reporting (as with other custom pixel shader code) is not very
good. That is also an area that I could improve upon.

I do think the risk of adding this is rather low as the new code is only
executed when experimental.pixelShaderImagePath is set.

### Details

Only added to the Atlas engine.

Instead I load the texture using WIC into a shader resource view. When
binding shader resources I test for presence of custom texture and bind
it to register t1.

The image loading code was found in [the D3D Texture documentation].
It's a mouthful but seems rather robust.


Tested setting: "experimental.pixelShaderImagePath"

1. Tested not specifying it.
2. Tested setting it.
3. Tested changing it (the changes are picked up)
4. Tested invalid path
5. Tested a custom shader that made use of the custom texture.

[the D3D Texture documentation]: https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-textures-how-to

Co-authored-by: Mike Griese <migrie@microsoft.com>
Co-authored-by: Leonard Hecker <lhecker@microsoft.com>
This commit is contained in:
Mårten Rånge 2024-03-08 19:01:53 +01:00 коммит произвёл GitHub
Родитель 8a1e8ace97
Коммит 0ba680ad53
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
21 изменённых файлов: 250 добавлений и 61 удалений

Просмотреть файл

@ -0,0 +1,23 @@
// Demo shader to show passing in an image using
// experimental.pixelShaderImagePath. This shader simply displays the Terminal
// contents on top of the given image.
//
// The image loaded by the terminal will be placed into the `image` texture.
SamplerState samplerState;
Texture2D shaderTexture : register(t0);
Texture2D image : register(t1);
cbuffer PixelShaderSettings {
float Time;
float Scale;
float2 Resolution;
float4 Background;
};
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
{
float4 terminalColor = shaderTexture.Sample(samplerState, tex);
float4 imageColor = image.Sample(samplerState, tex);
return lerp(imageColor, terminalColor, terminalColor.a);
}

Просмотреть файл

@ -387,6 +387,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_renderEngine->SetRetroTerminalEffect(_settings->RetroTerminalEffect());
_renderEngine->SetPixelShaderPath(_settings->PixelShaderPath());
_renderEngine->SetPixelShaderImagePath(_settings->PixelShaderImagePath());
_renderEngine->SetForceFullRepaintRendering(_settings->ForceFullRepaintRendering());
_renderEngine->SetSoftwareRendering(_settings->SoftwareRendering());
@ -914,6 +915,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_renderEngine->SetSelectionBackground(til::color{ newAppearance->SelectionBackground() });
_renderEngine->SetRetroTerminalEffect(newAppearance->RetroTerminalEffect());
_renderEngine->SetPixelShaderPath(newAppearance->PixelShaderPath());
_renderEngine->SetPixelShaderImagePath(newAppearance->PixelShaderImagePath());
// Incase EnableUnfocusedAcrylic is disabled and Focused Acrylic is set to true,
// the terminal should ignore the unfocused opacity from settings.

Просмотреть файл

@ -18,5 +18,6 @@ namespace Microsoft.Terminal.Control
// Experimental settings
Boolean RetroTerminalEffect { get; };
String PixelShaderPath { get; };
String PixelShaderImagePath { get; };
};
}

Просмотреть файл

@ -1602,7 +1602,7 @@ til::point Terminal::GetViewportRelativeCursorPosition() const noexcept
// These functions are used by TerminalInput, which must build in conhost
// against OneCore compatible signatures. See the definitions in
// VtApiRedirection.hpp (which we cannot include cross-project.)
// Since we do nto run on OneCore, we can dispense with the compatibility
// Since we don't run on OneCore, we can dispense with the compatibility
// shims.
extern "C" UINT OneCoreSafeMapVirtualKeyW(_In_ UINT uCode, _In_ UINT uMapType)
{

Просмотреть файл

@ -51,6 +51,7 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_APPEARANCE_SETTING(Boolean, RetroTerminalEffect);
INHERITABLE_APPEARANCE_SETTING(String, PixelShaderPath);
INHERITABLE_APPEARANCE_SETTING(String, PixelShaderImagePath);
INHERITABLE_APPEARANCE_SETTING(IntenseStyle, IntenseTextStyle);
INHERITABLE_APPEARANCE_SETTING(Microsoft.Terminal.Core.AdjustTextMode, AdjustIndistinguishableColors);
INHERITABLE_APPEARANCE_SETTING(Double, Opacity);

Просмотреть файл

@ -124,6 +124,7 @@ Author(s):
X(winrt::Windows::UI::Xaml::Media::Stretch, BackgroundImageStretchMode, "backgroundImageStretchMode", winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill) \
X(bool, RetroTerminalEffect, "experimental.retroTerminalEffect", false) \
X(hstring, PixelShaderPath, "experimental.pixelShaderPath") \
X(hstring, PixelShaderImagePath, "experimental.pixelShaderImagePath") \
X(ConvergedAlignment, BackgroundImageAlignment, "backgroundImageAlignment", ConvergedAlignment::Horizontal_Center | ConvergedAlignment::Vertical_Center) \
X(hstring, BackgroundImagePath, "backgroundImage") \
X(Model::IntenseStyle, IntenseTextStyle, "intenseTextStyle", Model::IntenseStyle::Bright) \

Просмотреть файл

@ -256,6 +256,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
_RetroTerminalEffect = appearance.RetroTerminalEffect();
_PixelShaderPath = winrt::hstring{ wil::ExpandEnvironmentStringsW<std::wstring>(appearance.PixelShaderPath().c_str()) };
_PixelShaderImagePath = winrt::hstring{ wil::ExpandEnvironmentStringsW<std::wstring>(appearance.PixelShaderImagePath().c_str()) };
_IntenseIsBold = WI_IsFlagSet(appearance.IntenseTextStyle(), Microsoft::Terminal::Settings::Model::IntenseStyle::Bold);
_IntenseIsBright = WI_IsFlagSet(appearance.IntenseTextStyle(), Microsoft::Terminal::Settings::Model::IntenseStyle::Bright);

Просмотреть файл

@ -160,6 +160,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::TerminalSettings, bool, ForceVTInput, false);
INHERITABLE_SETTING(Model::TerminalSettings, hstring, PixelShaderPath);
INHERITABLE_SETTING(Model::TerminalSettings, hstring, PixelShaderImagePath);
INHERITABLE_SETTING(Model::TerminalSettings, bool, Elevate, false);

Просмотреть файл

@ -27,7 +27,8 @@
X(winrt::Windows::UI::Xaml::HorizontalAlignment, BackgroundImageHorizontalAlignment, winrt::Windows::UI::Xaml::HorizontalAlignment::Center) \
X(winrt::Windows::UI::Xaml::VerticalAlignment, BackgroundImageVerticalAlignment, winrt::Windows::UI::Xaml::VerticalAlignment::Center) \
X(bool, RetroTerminalEffect, false) \
X(winrt::hstring, PixelShaderPath)
X(winrt::hstring, PixelShaderPath) \
X(winrt::hstring, PixelShaderImagePath)
// --------------------------- Core Settings ---------------------------
// All of these settings are defined in ICoreSettings.

Просмотреть файл

@ -328,6 +328,11 @@ HRESULT AtlasEngine::Enable() noexcept
return _api.s->misc->customPixelShaderPath;
}
[[nodiscard]] std::wstring_view AtlasEngine::GetPixelShaderImagePath() noexcept
{
return _api.s->misc->customPixelShaderImagePath;
}
[[nodiscard]] bool AtlasEngine::GetRetroTerminalEffect() const noexcept
{
return _api.s->misc->useRetroTerminalEffect;
@ -400,6 +405,17 @@ try
}
CATCH_LOG()
void AtlasEngine::SetPixelShaderImagePath(std::wstring_view value) noexcept
try
{
if (_api.s->misc->customPixelShaderImagePath != value)
{
_api.s.write()->misc.write()->customPixelShaderImagePath = value;
_resolveTransparencySettings();
}
}
CATCH_LOG()
void AtlasEngine::SetRetroTerminalEffect(bool enable) noexcept
{
if (_api.s->misc->useRetroTerminalEffect != enable)

Просмотреть файл

@ -643,7 +643,7 @@ void AtlasEngine::_recreateCellCountDependentResources()
// and 40x (AMD) faster for allocations with an alignment of 32 or greater.
// backgroundBitmapStride is a "count" of u32 and not in bytes,
// so we round up to multiple of 8 because 8 * sizeof(u32) == 32.
_p.colorBitmapRowStride = (static_cast<size_t>(_p.s->viewportCellCount.x) + 7) & ~7;
_p.colorBitmapRowStride = alignForward<size_t>(_p.s->viewportCellCount.x, 8);
_p.colorBitmapDepthStride = _p.colorBitmapRowStride * _p.s->viewportCellCount.y;
_p.colorBitmap = Buffer<u32, 32>(_p.colorBitmapDepthStride * 2);
_p.backgroundBitmap = { _p.colorBitmap.data(), _p.colorBitmapDepthStride };

Просмотреть файл

@ -61,6 +61,7 @@ namespace Microsoft::Console::Render::Atlas
// DxRenderer - getter
HRESULT Enable() noexcept override;
[[nodiscard]] std::wstring_view GetPixelShaderPath() noexcept override;
[[nodiscard]] std::wstring_view GetPixelShaderImagePath() noexcept override;
[[nodiscard]] bool GetRetroTerminalEffect() const noexcept override;
[[nodiscard]] float GetScaling() const noexcept override;
[[nodiscard]] Types::Viewport GetViewportInCharacters(const Types::Viewport& viewInPixels) const noexcept override;
@ -72,6 +73,7 @@ namespace Microsoft::Console::Render::Atlas
void SetForceFullRepaintRendering(bool enable) noexcept override;
[[nodiscard]] HRESULT SetHwnd(HWND hwnd) noexcept override;
void SetPixelShaderPath(std::wstring_view value) noexcept override;
void SetPixelShaderImagePath(std::wstring_view value) noexcept override;
void SetRetroTerminalEffect(bool enable) noexcept override;
void SetSelectionBackground(COLORREF color, float alpha = 0.5f) noexcept override;
void SetSoftwareRendering(bool enable) noexcept override;

Просмотреть файл

@ -86,6 +86,13 @@ namespace Microsoft::Console::Render::Atlas
return val < min ? min : (max < val ? max : val);
}
template<typename T>
constexpr T alignForward(T val, T alignment) noexcept
{
assert((alignment & (alignment - 1)) == 0); // alignment should be a power of 2
return (val + alignment - 1) & ~(alignment - 1);
}
inline constexpr D2D1_RECT_F GlyphRunEmptyBounds{ 1e38f, 1e38f, -1e38f, -1e38f };
void GlyphRunAccumulateBounds(const ID2D1DeviceContext* d2dRenderTarget, D2D1_POINT_2F baselineOrigin, const DWRITE_GLYPH_RUN* glyphRun, D2D1_RECT_F& bounds);

Просмотреть файл

@ -13,16 +13,13 @@
#include "BuiltinGlyphs.h"
#include "dwrite.h"
#include "wic.h"
#include "../../types/inc/ColorFix.hpp"
#if ATLAS_DEBUG_SHOW_DIRTY || ATLAS_DEBUG_COLORIZE_GLYPH_ATLAS
#include "colorbrewer.h"
#endif
#if ATLAS_DEBUG_DUMP_RENDER_TARGET
#include "wic.h"
#endif
TIL_FAST_MATH_BEGIN
#pragma warning(disable : 4100) // '...': unreferenced formal parameter
@ -345,6 +342,8 @@ void BackendD3D::_recreateCustomShader(const RenderingPayload& p)
_customPixelShader.reset();
_customShaderConstantBuffer.reset();
_customShaderSamplerState.reset();
_customShaderTexture.reset();
_customShaderTextureView.reset();
_requiresContinuousRedraw = false;
if (!p.s->misc->customPixelShaderPath.empty())
@ -436,6 +435,23 @@ void BackendD3D::_recreateCustomShader(const RenderingPayload& p)
p.warningCallback(D2DERR_SHADER_COMPILE_FAILED);
}
}
if (!p.s->misc->customPixelShaderImagePath.empty())
{
try
{
WIC::LoadTextureFromFile(p.device.get(), p.s->misc->customPixelShaderImagePath.c_str(), _customShaderTexture.addressof(), _customShaderTextureView.addressof());
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
_customPixelShader.reset();
if (p.warningCallback)
{
p.warningCallback(D2DERR_SHADER_COMPILE_FAILED);
}
}
}
}
else if (p.s->misc->useRetroTerminalEffect)
{
@ -899,7 +915,7 @@ void BackendD3D::_recreateInstanceBuffers(const RenderingPayload& p)
auto newSize = newCapacity * sizeof(QuadInstance);
// Round up to multiples of 64kB to avoid reallocating too often.
// 64kB is the minimum alignment for committed resources in D3D12.
newSize = (newSize + 0xffff) & ~size_t{ 0xffff };
newSize = alignForward<size_t>(newSize, 64 * 1024);
newCapacity = newSize / sizeof(QuadInstance);
_instanceBuffer.reset();
@ -2121,7 +2137,11 @@ void BackendD3D::_executeCustomShader(RenderingPayload& p)
// PS: Pixel Shader
p.deviceContext->PSSetShader(_customPixelShader.get(), nullptr, 0);
p.deviceContext->PSSetConstantBuffers(0, 1, _customShaderConstantBuffer.addressof());
p.deviceContext->PSSetShaderResources(0, 1, _customOffscreenTextureView.addressof());
ID3D11ShaderResourceView* const resourceViews[]{
_customOffscreenTextureView.get(), // The terminal contents
_customShaderTextureView.get(), // the experimental.pixelShaderImagePath, if there is one
};
p.deviceContext->PSSetShaderResources(0, resourceViews[1] ? 2 : 1, &resourceViews[0]);
p.deviceContext->PSSetSamplers(0, 1, _customShaderSamplerState.addressof());
// OM: Output Merger

Просмотреть файл

@ -247,6 +247,8 @@ namespace Microsoft::Console::Render::Atlas
wil::com_ptr<ID3D11PixelShader> _customPixelShader;
wil::com_ptr<ID3D11Buffer> _customShaderConstantBuffer;
wil::com_ptr<ID3D11SamplerState> _customShaderSamplerState;
wil::com_ptr<ID3D11Texture2D> _customShaderTexture;
wil::com_ptr<ID3D11ShaderResourceView> _customShaderTextureView;
std::chrono::steady_clock::time_point _customShaderStartTime;
wil::com_ptr<ID3D11Texture2D> _backgroundBitmap;

Просмотреть файл

@ -11,6 +11,7 @@
<Import Project="$(SolutionDir)src\common.build.pre.props" />
<Import Project="$(SolutionDir)src\common.nugetversions.props" />
<ItemGroup>
<ClCompile Include="AtlasEngine.cpp" />
<ClCompile Include="AtlasEngine.api.cpp" />
<ClCompile Include="AtlasEngine.r.cpp" />
<ClCompile Include="Backend.cpp" />
@ -22,10 +23,11 @@
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="AtlasEngine.cpp" />
<ClCompile Include="stb_rect_pack.cpp" />
<ClCompile Include="wic.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="AtlasEngine.h" />
<ClInclude Include="Backend.h" />
<ClInclude Include="BackendD2D.h" />
<ClInclude Include="BackendD3D.h" />
@ -35,7 +37,6 @@
<ClInclude Include="dwrite.h" />
<ClInclude Include="DWriteTextAnalysis.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="AtlasEngine.h" />
<ClInclude Include="wic.h" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -380,6 +380,7 @@ namespace Microsoft::Console::Render::Atlas
u32 backgroundColor = 0;
u32 selectionColor = 0x7fffffff;
std::wstring customPixelShaderPath;
std::wstring customPixelShaderImagePath;
bool useRetroTerminalEffect = false;
};

Просмотреть файл

@ -26,6 +26,7 @@
#include <dxgi1_3.h>
#include <dxgidebug.h>
#include <VersionHelpers.h>
#include <wincodec.h>
#include <gsl/gsl_util>
#include <gsl/pointers>

153
src/renderer/atlas/wic.cpp Normal file
Просмотреть файл

@ -0,0 +1,153 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "wic.h"
#include "Backend.h"
using namespace Microsoft::Console::Render::Atlas;
using namespace Microsoft::Console::Render::Atlas::WIC;
static wil::com_ptr<IWICImagingFactory2> getWicFactory()
{
static const auto coUninitialize = wil::CoInitializeEx();
static const auto wicFactory = wil::CoCreateInstance<IWICImagingFactory2>(CLSID_WICImagingFactory2);
return wicFactory;
}
void WIC::SaveTextureToPNG(ID3D11DeviceContext* deviceContext, ID3D11Resource* source, double dpi, const wchar_t* fileName)
{
__assume(deviceContext != nullptr);
__assume(source != nullptr);
wil::com_ptr<ID3D11Texture2D> texture;
THROW_IF_FAILED(source->QueryInterface(IID_PPV_ARGS(texture.addressof())));
wil::com_ptr<ID3D11Device> d3dDevice;
deviceContext->GetDevice(d3dDevice.addressof());
D3D11_TEXTURE2D_DESC desc{};
texture->GetDesc(&desc);
desc.BindFlags = 0;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.Usage = D3D11_USAGE_STAGING;
wil::com_ptr<ID3D11Texture2D> staging;
THROW_IF_FAILED(d3dDevice->CreateTexture2D(&desc, nullptr, staging.put()));
deviceContext->CopyResource(staging.get(), source);
const auto wicFactory = getWicFactory();
wil::com_ptr<IWICStream> stream;
THROW_IF_FAILED(wicFactory->CreateStream(stream.addressof()));
THROW_IF_FAILED(stream->InitializeFromFilename(fileName, GENERIC_WRITE));
wil::com_ptr<IWICBitmapEncoder> encoder;
THROW_IF_FAILED(wicFactory->CreateEncoder(GUID_ContainerFormatPng, nullptr, encoder.addressof()));
THROW_IF_FAILED(encoder->Initialize(stream.get(), WICBitmapEncoderNoCache));
wil::com_ptr<IWICBitmapFrameEncode> frame;
wil::com_ptr<IPropertyBag2> props;
THROW_IF_FAILED(encoder->CreateNewFrame(frame.addressof(), props.addressof()));
THROW_IF_FAILED(frame->Initialize(props.get()));
THROW_IF_FAILED(frame->SetSize(desc.Width, desc.Height));
THROW_IF_FAILED(frame->SetResolution(dpi, dpi));
auto pixelFormat = GUID_WICPixelFormat32bppBGRA;
THROW_IF_FAILED(frame->SetPixelFormat(&pixelFormat));
D3D11_MAPPED_SUBRESOURCE mapped;
THROW_IF_FAILED(deviceContext->Map(staging.get(), 0, D3D11_MAP_READ, 0, &mapped));
THROW_IF_FAILED(frame->WritePixels(desc.Height, mapped.RowPitch, mapped.RowPitch * desc.Height, static_cast<BYTE*>(mapped.pData)));
deviceContext->Unmap(staging.get(), 0);
THROW_IF_FAILED(frame->Commit());
THROW_IF_FAILED(encoder->Commit());
}
void WIC::LoadTextureFromFile(ID3D11Device* device, const wchar_t* fileName, ID3D11Texture2D** out_texture, ID3D11ShaderResourceView** out_textureView)
{
__assume(device != nullptr);
__assume(fileName != nullptr);
__assume(out_texture != nullptr);
__assume(out_textureView != nullptr);
const auto wicFactory = getWicFactory();
wil::com_ptr<IWICBitmapDecoder> decoder;
THROW_IF_FAILED(wicFactory->CreateDecoderFromFilename(fileName, nullptr, GENERIC_READ, WICDecodeMetadataCacheOnDemand, decoder.addressof()));
wil::com_ptr<IWICBitmapFrameDecode> frame;
THROW_IF_FAILED(decoder->GetFrame(0, frame.addressof()));
WICPixelFormatGUID srcFormat;
THROW_IF_FAILED(frame->GetPixelFormat(&srcFormat));
UINT srcWidth, srcHeight;
THROW_IF_FAILED(frame->GetSize(&srcWidth, &srcHeight));
static constexpr u32 maxSizeU32 = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
static constexpr f32 maxSizeF32 = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
UINT tgtWidth = srcWidth;
UINT tgtHeight = srcHeight;
wil::com_ptr<IWICBitmapScaler> scaler;
IWICBitmapSource* currentSource = frame.get();
if (srcWidth > maxSizeU32 || srcHeight > maxSizeU32)
{
const auto ar = static_cast<float>(srcHeight) / static_cast<float>(srcWidth);
if (srcWidth > srcHeight)
{
tgtWidth = maxSizeU32;
tgtHeight = std::max<UINT>(1, lroundf(maxSizeF32 * ar));
}
else
{
tgtHeight = maxSizeU32;
tgtWidth = std::max<UINT>(1, lroundf(maxSizeF32 / ar));
}
THROW_IF_FAILED(wicFactory->CreateBitmapScaler(scaler.addressof()));
THROW_IF_FAILED(scaler->Initialize(currentSource, tgtWidth, tgtHeight, WICBitmapInterpolationModeFant));
currentSource = scaler.get();
}
wil::com_ptr<IWICFormatConverter> converter;
THROW_IF_FAILED(wicFactory->CreateFormatConverter(converter.addressof()));
BOOL canConvert = FALSE;
THROW_IF_FAILED(converter->CanConvert(srcFormat, GUID_WICPixelFormat32bppPBGRA, &canConvert));
THROW_HR_IF(E_UNEXPECTED, !canConvert);
THROW_IF_FAILED(converter->Initialize(currentSource, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeErrorDiffusion, nullptr, 0, WICBitmapPaletteTypeMedianCut));
// Aligning the width by 8 pixels, results in a 32 byte stride, which is better for memcpy on contemporary hardware.
const uint64_t stride = alignForward<uint64_t>(tgtWidth, 8) * sizeof(u32);
const uint64_t bytes = stride * static_cast<uint64_t>(tgtHeight);
THROW_HR_IF(ERROR_ARITHMETIC_OVERFLOW, bytes > UINT32_MAX);
Buffer<BYTE, 32> buffer{ gsl::narrow_cast<size_t>(bytes) };
THROW_IF_FAILED(converter->CopyPixels(nullptr, gsl::narrow_cast<UINT>(stride), gsl::narrow_cast<UINT>(bytes), buffer.data()));
const D3D11_TEXTURE2D_DESC desc = {
.Width = tgtWidth,
.Height = tgtHeight,
.MipLevels = 1,
.ArraySize = 1,
.Format = DXGI_FORMAT_B8G8R8A8_UNORM,
.SampleDesc = { 1, 0 },
.Usage = D3D11_USAGE_IMMUTABLE,
.BindFlags = D3D11_BIND_SHADER_RESOURCE,
};
const D3D11_SUBRESOURCE_DATA initData = {
.pSysMem = buffer.data(),
.SysMemPitch = gsl::narrow_cast<UINT>(stride),
.SysMemSlicePitch = gsl::narrow_cast<UINT>(bytes),
};
wil::com_ptr<ID3D11Texture2D> texture;
THROW_IF_FAILED(device->CreateTexture2D(&desc, &initData, texture.addressof()));
wil::com_ptr<ID3D11ShaderResourceView> textureView;
THROW_IF_FAILED(device->CreateShaderResourceView(texture.get(), nullptr, textureView.addressof()));
*out_texture = texture.detach();
*out_textureView = textureView.detach();
}

Просмотреть файл

@ -3,55 +3,8 @@
#pragma once
#include <wincodec.h>
inline void SaveTextureToPNG(ID3D11DeviceContext* deviceContext, ID3D11Resource* source, double dpi, const wchar_t* fileName)
namespace Microsoft::Console::Render::Atlas::WIC
{
__assume(deviceContext != nullptr);
__assume(source != nullptr);
wil::com_ptr<ID3D11Texture2D> texture;
THROW_IF_FAILED(source->QueryInterface(IID_PPV_ARGS(texture.addressof())));
wil::com_ptr<ID3D11Device> d3dDevice;
deviceContext->GetDevice(d3dDevice.addressof());
D3D11_TEXTURE2D_DESC desc{};
texture->GetDesc(&desc);
desc.BindFlags = 0;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.Usage = D3D11_USAGE_STAGING;
wil::com_ptr<ID3D11Texture2D> staging;
THROW_IF_FAILED(d3dDevice->CreateTexture2D(&desc, nullptr, staging.put()));
deviceContext->CopyResource(staging.get(), source);
static const auto coUninitialize = wil::CoInitializeEx();
static const auto wicFactory = wil::CoCreateInstance<IWICImagingFactory2>(CLSID_WICImagingFactory2);
wil::com_ptr<IWICStream> stream;
THROW_IF_FAILED(wicFactory->CreateStream(stream.addressof()));
THROW_IF_FAILED(stream->InitializeFromFilename(fileName, GENERIC_WRITE));
wil::com_ptr<IWICBitmapEncoder> encoder;
THROW_IF_FAILED(wicFactory->CreateEncoder(GUID_ContainerFormatPng, nullptr, encoder.addressof()));
THROW_IF_FAILED(encoder->Initialize(stream.get(), WICBitmapEncoderNoCache));
wil::com_ptr<IWICBitmapFrameEncode> frame;
wil::com_ptr<IPropertyBag2> props;
THROW_IF_FAILED(encoder->CreateNewFrame(frame.addressof(), props.addressof()));
THROW_IF_FAILED(frame->Initialize(props.get()));
THROW_IF_FAILED(frame->SetSize(desc.Width, desc.Height));
THROW_IF_FAILED(frame->SetResolution(dpi, dpi));
auto pixelFormat = GUID_WICPixelFormat32bppBGRA;
THROW_IF_FAILED(frame->SetPixelFormat(&pixelFormat));
D3D11_MAPPED_SUBRESOURCE mapped;
THROW_IF_FAILED(deviceContext->Map(staging.get(), 0, D3D11_MAP_READ, 0, &mapped));
THROW_IF_FAILED(frame->WritePixels(desc.Height, mapped.RowPitch, mapped.RowPitch * desc.Height, static_cast<BYTE*>(mapped.pData)));
deviceContext->Unmap(staging.get(), 0);
THROW_IF_FAILED(frame->Commit());
THROW_IF_FAILED(encoder->Commit());
void SaveTextureToPNG(ID3D11DeviceContext* deviceContext, ID3D11Resource* source, double dpi, const wchar_t* fileName);
void LoadTextureFromFile(ID3D11Device* device, const wchar_t* fileName, ID3D11Texture2D** out_texture, ID3D11ShaderResourceView** out_textureView);
}

Просмотреть файл

@ -97,6 +97,7 @@ namespace Microsoft::Console::Render
// DxRenderer - getter
virtual HRESULT Enable() noexcept { return S_OK; }
[[nodiscard]] virtual std::wstring_view GetPixelShaderPath() noexcept { return {}; }
[[nodiscard]] virtual std::wstring_view GetPixelShaderImagePath() noexcept { return {}; }
[[nodiscard]] virtual bool GetRetroTerminalEffect() const noexcept { return false; }
[[nodiscard]] virtual float GetScaling() const noexcept { return 1; }
[[nodiscard]] virtual Types::Viewport GetViewportInCharacters(const Types::Viewport& viewInPixels) const noexcept { return Types::Viewport::Empty(); }
@ -108,6 +109,7 @@ namespace Microsoft::Console::Render
virtual void SetForceFullRepaintRendering(bool enable) noexcept {}
[[nodiscard]] virtual HRESULT SetHwnd(const HWND hwnd) noexcept { return E_NOTIMPL; }
virtual void SetPixelShaderPath(std::wstring_view value) noexcept {}
virtual void SetPixelShaderImagePath(std::wstring_view value) noexcept {}
virtual void SetRetroTerminalEffect(bool enable) noexcept {}
virtual void SetSelectionBackground(const COLORREF color, const float alpha = 0.5f) noexcept {}
virtual void SetSoftwareRendering(bool enable) noexcept {}