mirror of
https://github.com/Blinue/Magpie.git
synced 2026-06-24 02:04:10 +00:00
736 lines
22 KiB
C++
736 lines
22 KiB
C++
#include "pch.h"
|
||
#include "Renderer2.h"
|
||
#include "DebugInfo.h"
|
||
#include "DirectXHelper.h"
|
||
#include "FrameProducer.h"
|
||
#include "Logger.h"
|
||
#include "ScalingWindow.h"
|
||
#include "SwapChainPresenter.h"
|
||
#include "shaders/CopyFrameVS.h"
|
||
#include "shaders/CopyFrameVS_SM5.h"
|
||
#include "shaders/TextureBlitPS.h"
|
||
#include "shaders/TextureBlitPS_SM5.h"
|
||
#include "GraphicsCaptureFrameSource.h"
|
||
#include <d3dkmthk.h>
|
||
#include <windows.graphics.display.interop.h>
|
||
|
||
namespace Magpie {
|
||
|
||
// 如果描述符大小为 32 字节,描述符堆消耗 2MiB 显存
|
||
static uint32_t CSU_HEAP_CAPACITY = 65536;
|
||
// 目前只有渲染到后缓冲和缩放光标时需要 RTV
|
||
static uint32_t RTV_HEAP_CAPACITY = 1024;
|
||
|
||
Renderer2::Renderer2() noexcept {}
|
||
|
||
Renderer2::~Renderer2() noexcept {
|
||
if (_d3d12Context.GetCommandQueue()) {
|
||
_d3d12Context.WaitForGpu();
|
||
}
|
||
}
|
||
|
||
static void SetGpuPriority() noexcept {
|
||
// 不使用 REALTIME 优先级,它可能造成系统不稳定。
|
||
// 来自 https://github.com/obsproject/obs-studio/blob/16cb051a57bb357fe866252c1360ce2c38e2deec/libobs-d3d11/d3d11-subsystem.cpp#L429
|
||
NTSTATUS status = D3DKMTSetProcessSchedulingPriorityClass(
|
||
GetCurrentProcess(), D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH);
|
||
if (status != STATUS_SUCCESS) {
|
||
Logger::Get().NTError("D3DKMTSetProcessSchedulingPriorityClass 失败", status);
|
||
}
|
||
}
|
||
|
||
ScalingError Renderer2::Initialize(
|
||
HWND hwndAttach,
|
||
HMONITOR hMonitor,
|
||
const RECT& srcRect,
|
||
const RECT& rendererRect,
|
||
OverlayOptions& /*overlayOptions*/,
|
||
RECT& destRect
|
||
) noexcept {
|
||
_hCurMonitor = hMonitor;
|
||
|
||
SetGpuPriority();
|
||
|
||
const ScalingOptions& options = ScalingWindow::Get().Options();
|
||
if (!_d3d12Context.Initialize(
|
||
options.graphicsCardId,
|
||
options.Is3DGameMode() ? 2 : 6,
|
||
D3D12_COMMAND_QUEUE_PRIORITY_HIGH,
|
||
D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||
_csuDescriptorHeap,
|
||
_rtvDescriptorHeap
|
||
)) {
|
||
Logger::Get().Error("初始化 D3D12Context 失败");
|
||
return ScalingError::ScalingFailedGeneral;
|
||
}
|
||
|
||
ID3D12Device5* device = _d3d12Context.GetDevice();
|
||
|
||
#ifdef MP_DEBUG_INFO
|
||
{
|
||
// 禁用动态时钟频率调整
|
||
if (DEBUG_INFO.enableStablePower) {
|
||
HRESULT hr = device->SetStablePowerState(TRUE);
|
||
if (FAILED(hr)) {
|
||
Logger::Get().ComError("SetStablePowerState 失败", hr);
|
||
}
|
||
}
|
||
|
||
#ifdef _DEBUG
|
||
// 模拟低速 GPU
|
||
winrt::com_ptr<ID3D12DebugDevice1> debugDevice;
|
||
HRESULT hr = device->QueryInterface<ID3D12DebugDevice1>(debugDevice.put());
|
||
if (SUCCEEDED(hr)) {
|
||
D3D12_DEBUG_DEVICE_GPU_SLOWDOWN_PERFORMANCE_FACTOR value = {
|
||
.SlowdownFactor = DEBUG_INFO.gpuSlowDownFactor
|
||
};
|
||
hr = debugDevice->SetDebugParameter(
|
||
D3D12_DEBUG_DEVICE_PARAMETER_GPU_SLOWDOWN_PERFORMANCE_FACTOR, &value, sizeof(value));
|
||
if (FAILED(hr)) {
|
||
Logger::Get().ComError("ID3D12DebugDevice1::SetDebugParameter 失败", hr);
|
||
}
|
||
} else {
|
||
Logger::Get().ComError("获取 ID3D12DebugDevice1 失败", hr);
|
||
}
|
||
#endif
|
||
}
|
||
#endif
|
||
|
||
if (!_csuDescriptorHeap.Initialize(
|
||
device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, CSU_HEAP_CAPACITY)
|
||
) {
|
||
Logger::Get().Error("DescriptorHeap::Initialize 失败");
|
||
return ScalingError::ScalingFailedGeneral;
|
||
}
|
||
|
||
if (!_rtvDescriptorHeap.Initialize(
|
||
device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, RTV_HEAP_CAPACITY)
|
||
) {
|
||
Logger::Get().Error("DescriptorHeap::Initialize 失败");
|
||
return ScalingError::ScalingFailedGeneral;
|
||
}
|
||
|
||
_graphicsContext.Initialize(_d3d12Context);
|
||
|
||
// 失败则回落到使用传统方法获取颜色显示能力
|
||
_TryInitDisplayInfo();
|
||
|
||
if (!_UpdateColorInfo()) {
|
||
Logger::Get().Error("_UpdateColorInfo 失败");
|
||
return ScalingError::ScalingFailedGeneral;
|
||
}
|
||
|
||
const Size rendererSize = {
|
||
uint32_t(rendererRect.right - rendererRect.left),
|
||
uint32_t(rendererRect.bottom - rendererRect.top)
|
||
};
|
||
|
||
Size outputSize;
|
||
SimpleTask<bool> task;
|
||
_frameProducer.InitializeAsync(
|
||
_d3d12Context, _colorInfo, hMonitor, srcRect, rendererSize, outputSize, task);
|
||
|
||
_presenter = std::make_unique<SwapChainPresenter>();
|
||
if (!_presenter->Initialize(_d3d12Context, hwndAttach, rendererSize, _colorInfo)) {
|
||
Logger::Get().Error("SwapChainPresenter::Initialize 失败");
|
||
return ScalingError::ScalingFailedGeneral;
|
||
}
|
||
|
||
// 同步点,使生产者线程的修改可见
|
||
if (!task.GetResult(std::memory_order_acquire)) {
|
||
Logger::Get().Error("FrameProducer::InitializeAsync 失败");
|
||
return ScalingError::ScalingFailedGeneral;
|
||
}
|
||
|
||
_UpdateOutputRect(outputSize);
|
||
|
||
destRect.left = rendererRect.left + (LONG)_outputRect.left;
|
||
destRect.top = rendererRect.top + (LONG)_outputRect.top;
|
||
destRect.right = rendererRect.left + (LONG)_outputRect.right;
|
||
destRect.bottom = rendererRect.top + (LONG)_outputRect.bottom;
|
||
|
||
if (!_cursorDrawer.Initialize(_d3d12Context, srcRect, rendererRect, destRect, _colorInfo)) {
|
||
Logger::Get().Error("CursorDrawer::Initialize 失败");
|
||
return ScalingError::ScalingFailedGeneral;
|
||
}
|
||
|
||
return ScalingError::NoError;
|
||
}
|
||
|
||
ComponentState Renderer2::Render(
|
||
HCURSOR hCursor,
|
||
POINT cursorPos,
|
||
bool waitForGpu,
|
||
bool* waitingForFirstFrame
|
||
) noexcept {
|
||
assert(!waitingForFirstFrame || !*waitingForFirstFrame);
|
||
|
||
if (_state != ComponentState::NoError) {
|
||
return _state;
|
||
}
|
||
|
||
_state = _frameProducer.GetState();
|
||
if (_state != ComponentState::NoError) {
|
||
return _state;
|
||
}
|
||
|
||
bool needRedraw = false;
|
||
|
||
{
|
||
const uint64_t latestProducerFrameNumber = _frameProducer.GetLatestFrameNumber();
|
||
if (latestProducerFrameNumber == 0) {
|
||
if (waitingForFirstFrame && latestProducerFrameNumber == 0) {
|
||
*waitingForFirstFrame = true;
|
||
}
|
||
return _state;
|
||
}
|
||
|
||
if (latestProducerFrameNumber != _lastProducerFrameNumber) {
|
||
needRedraw = true;
|
||
_lastProducerFrameNumber = latestProducerFrameNumber;
|
||
}
|
||
}
|
||
|
||
{
|
||
bool cursorNeedRedraw = false;
|
||
_cursorDrawer.PrepareForDraw(hCursor, cursorPos, cursorNeedRedraw);
|
||
needRedraw |= cursorNeedRedraw;
|
||
}
|
||
|
||
if (needRedraw) {
|
||
_CheckResult(_RenderImpl(waitForGpu), "_RenderImpl 失败");
|
||
}
|
||
|
||
return _state;
|
||
}
|
||
|
||
void Renderer2::OnMonitorChanged(HMONITOR hMonitor) noexcept {
|
||
if (_state != ComponentState::NoError) {
|
||
return;
|
||
}
|
||
|
||
_acInfoChangedRevoker.revoke();
|
||
_displayInfo = nullptr;
|
||
_hCurMonitor = hMonitor;
|
||
|
||
_TryInitDisplayInfo();
|
||
_CheckResult(_UpdateColorSpace(), "_UpdateColorSpace 失败");
|
||
}
|
||
|
||
void Renderer2::OnResizeStarted() noexcept {
|
||
if (_state == ComponentState::NoError) {
|
||
_presenter->OnResizeStarted();
|
||
}
|
||
}
|
||
|
||
void Renderer2::OnResizeEnded() noexcept {
|
||
if (_state == ComponentState::NoError) {
|
||
_CheckResult(_presenter->OnResizeEnded(), "SwapChainPresenter::OnResizeEnded 失败");
|
||
}
|
||
}
|
||
|
||
void Renderer2::OnResized(const RECT& rendererRect, RECT& destRect) noexcept {
|
||
if (_state != ComponentState::NoError) {
|
||
return;
|
||
}
|
||
|
||
// 确保消费者不再使用环形缓冲区
|
||
if (!_CheckResult(_d3d12Context.WaitForGpu(), "D3D12Context::WaitForGpu 失败")) {
|
||
return;
|
||
}
|
||
|
||
const Size rendererSize = {
|
||
uint32_t(rendererRect.right - rendererRect.left),
|
||
uint32_t(rendererRect.bottom - rendererRect.top)
|
||
};
|
||
|
||
Size outputSize;
|
||
SimpleTask<HRESULT> task;
|
||
_frameProducer.OnResizedAsync(rendererSize, outputSize, task);
|
||
|
||
if (!_CheckResult(_presenter->OnResized(rendererSize), "SwapChainPresenter::OnResized 失败")) {
|
||
return;
|
||
}
|
||
|
||
if (!_CheckResult(task.GetResult(std::memory_order_acquire),
|
||
"FrameProducer::OnResizedAsync 失败")) {
|
||
return;
|
||
}
|
||
|
||
_UpdateOutputRect(outputSize);
|
||
|
||
destRect.left = rendererRect.left + (LONG)_outputRect.left;
|
||
destRect.top = rendererRect.top + (LONG)_outputRect.top;
|
||
destRect.right = rendererRect.left + (LONG)_outputRect.right;
|
||
destRect.bottom = rendererRect.top + (LONG)_outputRect.bottom;
|
||
|
||
_cursorDrawer.OnResized(rendererRect, destRect);
|
||
}
|
||
|
||
void Renderer2::OnMoveStarted() noexcept {
|
||
_cursorDrawer.OnMoveStarted();
|
||
}
|
||
|
||
void Renderer2::OnMoveEnded() noexcept {
|
||
_cursorDrawer.OnMoveEnded();
|
||
}
|
||
|
||
void Renderer2::OnMoved(const RECT& rendererRect, RECT& destRect) noexcept {
|
||
destRect.left = rendererRect.left + (LONG)_outputRect.left;
|
||
destRect.top = rendererRect.top + (LONG)_outputRect.top;
|
||
destRect.right = rendererRect.left + (LONG)_outputRect.right;
|
||
destRect.bottom = rendererRect.top + (LONG)_outputRect.bottom;
|
||
|
||
_cursorDrawer.OnMoved(rendererRect, destRect);
|
||
}
|
||
|
||
void Renderer2::OnCursorVirtualizationStarted() noexcept {
|
||
_cursorDrawer.OnCursorVirtualizationStarted();
|
||
}
|
||
|
||
void Renderer2::OnCursorVirtualizationEnded() noexcept {
|
||
_cursorDrawer.OnCursorVirtualizationEnded();
|
||
}
|
||
|
||
void Renderer2::OnSrcMoveStarted() noexcept {
|
||
_cursorDrawer.OnSrcMoveStarted();
|
||
}
|
||
|
||
void Renderer2::OnSrcMoveEnded() noexcept {
|
||
_cursorDrawer.OnSrcMoveEnded();
|
||
}
|
||
|
||
void Renderer2::OnMsgDisplayChanged() noexcept {
|
||
// winrt::DisplayInformation 可用时已通过事件监听颜色配置变化
|
||
if (_state != ComponentState::NoError || _displayInfo) {
|
||
return;
|
||
}
|
||
|
||
_CheckResult(_UpdateColorSpace(), "_UpdateColorSpace 失败");
|
||
}
|
||
|
||
void Renderer2::OnCursorVisibilityChanged(bool isVisible, bool onDestory) noexcept {
|
||
_frameProducer.OnCursorVisibilityChanged(isVisible, onDestory);
|
||
}
|
||
|
||
void Renderer2::_TryInitDisplayInfo() noexcept {
|
||
// 从 Win11 22H2 开始支持
|
||
winrt::com_ptr<IDisplayInformationStaticsInterop> interop =
|
||
winrt::try_get_activation_factory<winrt::DisplayInformation, IDisplayInformationStaticsInterop>();
|
||
if (!interop) {
|
||
return;
|
||
}
|
||
|
||
HRESULT hr = interop->GetForMonitor(
|
||
_hCurMonitor, winrt::guid_of<winrt::DisplayInformation>(), winrt::put_abi(_displayInfo));
|
||
if (FAILED(hr)) {
|
||
Logger::Get().ComError("IDisplayInformationStaticsInterop::GetForMonitor 失败", hr);
|
||
return;
|
||
}
|
||
|
||
_acInfoChangedRevoker = _displayInfo.AdvancedColorInfoChanged(
|
||
winrt::auto_revoke,
|
||
[this](winrt::DisplayInformation const&, winrt::IInspectable const&) {
|
||
_CheckResult(_UpdateColorSpace(), "_UpdateColorSpace 失败");
|
||
}
|
||
);
|
||
}
|
||
|
||
static float GetSDRWhiteLevel(std::wstring_view monitorName) noexcept {
|
||
UINT32 pathCount = 0, modeCount = 0;
|
||
if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount) != ERROR_SUCCESS) {
|
||
return 1.0f;
|
||
}
|
||
|
||
std::vector<DISPLAYCONFIG_PATH_INFO> paths(pathCount);
|
||
std::vector<DISPLAYCONFIG_MODE_INFO> modes(modeCount);
|
||
if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathCount, paths.data(), &modeCount, modes.data(), nullptr) != ERROR_SUCCESS) {
|
||
return 1.0f;
|
||
}
|
||
|
||
for (const DISPLAYCONFIG_PATH_INFO& path : paths) {
|
||
DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName = {
|
||
.header = {
|
||
.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME,
|
||
.size = sizeof(sourceName),
|
||
.adapterId = path.sourceInfo.adapterId,
|
||
.id = path.sourceInfo.id
|
||
}
|
||
};
|
||
if (DisplayConfigGetDeviceInfo(&sourceName.header) != ERROR_SUCCESS) {
|
||
continue;
|
||
}
|
||
|
||
if (monitorName == sourceName.viewGdiDeviceName) {
|
||
DISPLAYCONFIG_SDR_WHITE_LEVEL sdr = {
|
||
.header = {
|
||
.type = DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL,
|
||
.size = sizeof(sdr),
|
||
.adapterId = path.targetInfo.adapterId,
|
||
.id = path.targetInfo.id
|
||
}
|
||
};
|
||
if (DisplayConfigGetDeviceInfo(&sdr.header) == ERROR_SUCCESS) {
|
||
return sdr.SDRWhiteLevel / 1000.0f;
|
||
} else {
|
||
return 1.0f;
|
||
}
|
||
}
|
||
}
|
||
|
||
return 1.0f;
|
||
}
|
||
|
||
bool Renderer2::_UpdateColorInfo() noexcept {
|
||
if (_displayInfo) {
|
||
winrt::AdvancedColorInfo acInfo = _displayInfo.GetAdvancedColorInfo();
|
||
|
||
_colorInfo.kind = acInfo.CurrentAdvancedColorKind();
|
||
if (_colorInfo.kind == winrt::AdvancedColorKind::HighDynamicRange) {
|
||
_colorInfo.maxLuminance = acInfo.MaxLuminanceInNits() / SCENE_REFERRED_SDR_WHITE_LEVEL;
|
||
_colorInfo.sdrWhiteLevel = acInfo.SdrWhiteLevelInNits() / SCENE_REFERRED_SDR_WHITE_LEVEL;
|
||
} else {
|
||
_colorInfo.maxLuminance = 1.0f;
|
||
_colorInfo.sdrWhiteLevel = 1.0f;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
IDXGIFactory7* dxgiFactory = _d3d12Context.GetDXGIFactoryForEnumingAdapters();
|
||
if (!dxgiFactory) {
|
||
return false;
|
||
}
|
||
|
||
winrt::com_ptr<IDXGIAdapter1> adapter;
|
||
winrt::com_ptr<IDXGIOutput> output;
|
||
for (UINT adapterIdx = 0;
|
||
SUCCEEDED(dxgiFactory->EnumAdapters1(adapterIdx, adapter.put()));
|
||
++adapterIdx
|
||
) {
|
||
for (UINT outputIdx = 0;
|
||
SUCCEEDED(adapter->EnumOutputs(outputIdx, output.put()));
|
||
++outputIdx
|
||
) {
|
||
DXGI_OUTPUT_DESC1 desc;
|
||
if (SUCCEEDED(output.try_as<IDXGIOutput6>()->GetDesc1(&desc))) {
|
||
if (desc.Monitor == _hCurMonitor) {
|
||
// DXGI 将 WCG 视为 SDR
|
||
if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) {
|
||
_colorInfo.kind = winrt::AdvancedColorKind::HighDynamicRange;
|
||
_colorInfo.maxLuminance = desc.MaxLuminance / SCENE_REFERRED_SDR_WHITE_LEVEL;
|
||
_colorInfo.sdrWhiteLevel = GetSDRWhiteLevel(desc.DeviceName);
|
||
} else {
|
||
_colorInfo.kind = winrt::AdvancedColorKind::StandardDynamicRange;
|
||
_colorInfo.maxLuminance = 1.0f;
|
||
_colorInfo.sdrWhiteLevel = 1.0f;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 未找到视为 SDR
|
||
_colorInfo.kind = winrt::AdvancedColorKind::StandardDynamicRange;
|
||
_colorInfo.maxLuminance = 1.0f;
|
||
_colorInfo.sdrWhiteLevel = 1.0f;
|
||
return true;
|
||
}
|
||
|
||
HRESULT Renderer2::_UpdateColorSpace() noexcept {
|
||
ColorInfo oldColorInfo = _colorInfo;
|
||
if (!_UpdateColorInfo()) {
|
||
return E_FAIL;
|
||
}
|
||
|
||
if (oldColorInfo == _colorInfo) {
|
||
return S_OK;
|
||
}
|
||
|
||
// 确保消费者不再使用环形缓冲区
|
||
HRESULT hr = _d3d12Context.WaitForGpu();
|
||
if (FAILED(hr)) {
|
||
Logger::Get().ComError("D3D12Context::WaitForGpu 失败", hr);
|
||
return hr;
|
||
}
|
||
|
||
SimpleTask<HRESULT> task;
|
||
_frameProducer.OnColorInfoChangedAsync(_colorInfo, task);
|
||
|
||
_cursorDrawer.OnColorInfoChanged(_colorInfo);
|
||
|
||
hr = _presenter->OnColorInfoChanged(_colorInfo);
|
||
if (FAILED(hr)) {
|
||
Logger::Get().ComError("SwapChainPresenter::OnColorInfoChanged 失败", hr);
|
||
return hr;
|
||
}
|
||
|
||
hr = task.GetResult();
|
||
if (FAILED(hr)) {
|
||
Logger::Get().ComError("FrameProducer::OnColorInfoChangedAsync 失败", hr);
|
||
return hr;
|
||
}
|
||
|
||
// 生产者渲染完成会通知消费者渲染新帧
|
||
return S_OK;
|
||
}
|
||
|
||
HRESULT Renderer2::_RenderImpl(bool waitForGpu) noexcept {
|
||
// 处于 COMMON 状态,依赖隐式状态转换
|
||
ID3D12Resource* curFrame;
|
||
uint32_t curFrameSrvOffset;
|
||
uint64_t completedFenceValue;
|
||
uint64_t fenceValueToSignal;
|
||
if (!_frameProducer.ConsumerBeginFrame(
|
||
curFrame, curFrameSrvOffset, completedFenceValue, fenceValueToSignal)) {
|
||
// 不应出现第一帧未完成的情况
|
||
assert(false);
|
||
return S_OK;
|
||
}
|
||
|
||
// SwapChain::BeginFrame 和 D3D12Context::BeginFrame 无顺序要求,不过
|
||
// 前者通常等待时间更久,将它放在前面可以减少等待次数。
|
||
ID3D12Resource* backBuffer;
|
||
uint32_t rtvOffset, rawRtvOffset;
|
||
_presenter->BeginFrame(&backBuffer, rtvOffset, rawRtvOffset);
|
||
|
||
{
|
||
bool isSrgb = _colorInfo.kind == winrt::AdvancedColorKind::StandardDynamicRange;
|
||
winrt::com_ptr<ID3D12PipelineState>& pso = isSrgb ? _copyFrameSrgbPSO : _copyFramePSO;
|
||
if (!pso) {
|
||
HRESULT hr = _CreateCopyFramePSO(isSrgb, pso);
|
||
if (FAILED(hr)) {
|
||
Logger::Get().ComError("_CreateCopyFramePSO 失败", hr);
|
||
return hr;
|
||
}
|
||
}
|
||
|
||
uint32_t frameIndex;
|
||
HRESULT hr = _d3d12Context.BeginFrame(frameIndex, pso.get());
|
||
if (FAILED(hr)) {
|
||
Logger::Get().ComError("D3D12Context::BeginFrame 失败", hr);
|
||
return hr;
|
||
}
|
||
}
|
||
|
||
_graphicsContext.SetDescriptorHeap(_csuDescriptorHeap.GetHeap());
|
||
|
||
_graphicsContext.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
|
||
|
||
_graphicsContext.InsertTransitionBarrier(
|
||
backBuffer, D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||
|
||
_graphicsContext.SetRootSignature(_copyRootSignature.get());
|
||
|
||
const Size rendererSize = _presenter->GetSize();
|
||
|
||
{
|
||
float outputWidth = float(_outputRect.right - _outputRect.left);
|
||
float outputHeight = float(_outputRect.bottom - _outputRect.top);
|
||
// 这些参数将输出区域 uv 变换为 0~1
|
||
float constants[] = {
|
||
rendererSize.width / outputWidth,
|
||
rendererSize.height / outputHeight,
|
||
_outputRect.left / -outputWidth,
|
||
_outputRect.top / -outputHeight
|
||
};
|
||
_graphicsContext.SetRoot32BitConstants(0, (uint32_t)std::size(constants), constants);
|
||
}
|
||
|
||
_graphicsContext.SetRootDescriptorTable(1, curFrameSrvOffset);
|
||
|
||
_graphicsContext.RSSetViewportAndScissorRect(CD3DX12_RECT(
|
||
0, 0, (LONG)rendererSize.width, (LONG)rendererSize.height));
|
||
|
||
_graphicsContext.OMSetRenderTarget(rtvOffset);
|
||
|
||
_graphicsContext.Draw(3);
|
||
|
||
// 为了和 OS 保持一致,SDR 下在 sRGB 空间中混合
|
||
if (_colorInfo.kind == winrt::AdvancedColorKind::StandardDynamicRange) {
|
||
_graphicsContext.OMSetRenderTarget(rawRtvOffset);
|
||
}
|
||
|
||
HRESULT hr = _cursorDrawer.Draw(_graphicsContext, completedFenceValue, fenceValueToSignal,
|
||
curFrameSrvOffset, nullptr);
|
||
if (FAILED(hr)) {
|
||
Logger::Get().ComError("CursorDrawer::Draw 失败", hr);
|
||
return hr;
|
||
}
|
||
|
||
_graphicsContext.InsertTransitionBarrier(
|
||
backBuffer, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
|
||
|
||
ID3D12CommandQueue* commandQueue = _d3d12Context.GetCommandQueue();
|
||
|
||
hr = _graphicsContext.Execute(commandQueue);
|
||
if (FAILED(hr)) {
|
||
Logger::Get().ComError("CommandContext::Execute 失败", hr);
|
||
return hr;
|
||
}
|
||
|
||
hr =_frameProducer.ConsumerEndFrame(commandQueue, fenceValueToSignal);
|
||
if (FAILED(hr)) {
|
||
Logger::Get().ComError("FrameProducer::ConsumerEndFrame 失败", hr);
|
||
return hr;
|
||
}
|
||
|
||
hr = _presenter->EndFrame(waitForGpu);
|
||
if (FAILED(hr)) {
|
||
Logger::Get().ComError("SwapChainPresenter::EndFrame 失败", hr);
|
||
return hr;
|
||
}
|
||
|
||
// D3D12Context::EndFrame 必须在 SwapChain::EndFrame 之后
|
||
hr = _d3d12Context.EndFrame();
|
||
if (FAILED(hr)) {
|
||
Logger::Get().ComError("D3D12Context::EndFrame 失败", hr);
|
||
return hr;
|
||
}
|
||
|
||
return S_OK;
|
||
}
|
||
|
||
void Renderer2::_UpdateOutputRect(Size outputSize) noexcept {
|
||
// TODO: 窗口模式缩放始终填充画面
|
||
const Size rendererSize = _presenter->GetSize();
|
||
OutputAlignment alignment = ScalingWindow::Get().Options().outputAlignment;
|
||
|
||
using enum OutputAlignment;
|
||
|
||
if (alignment == LeftTop || alignment == Left || alignment == LeftBottom) {
|
||
_outputRect.left = 0;
|
||
_outputRect.right = outputSize.width;
|
||
} else if (alignment == Top || alignment == Center || alignment == Bottom) {
|
||
_outputRect.left = (rendererSize.width - outputSize.width) / 2;
|
||
_outputRect.right = _outputRect.left + outputSize.width;
|
||
} else {
|
||
_outputRect.left = rendererSize.width - outputSize.width;
|
||
_outputRect.right = rendererSize.width;
|
||
}
|
||
|
||
if (alignment == LeftTop || alignment == Top || alignment == RightTop) {
|
||
_outputRect.top = 0;
|
||
_outputRect.bottom = outputSize.height;
|
||
} else if (alignment == Left || alignment == Center || alignment == Right) {
|
||
_outputRect.top = (rendererSize.height - outputSize.height) / 2;
|
||
_outputRect.bottom = _outputRect.top + outputSize.height;
|
||
} else {
|
||
_outputRect.top = rendererSize.height - outputSize.height;
|
||
_outputRect.bottom = rendererSize.height;
|
||
}
|
||
|
||
assert(_outputRect.left + outputSize.width == _outputRect.right);
|
||
assert(_outputRect.top + outputSize.height == _outputRect.bottom);
|
||
}
|
||
|
||
bool Renderer2::_CheckResult(bool success, std::string_view errorMsg) noexcept {
|
||
assert(_state == ComponentState::NoError);
|
||
|
||
if (!success) {
|
||
_state = ComponentState::Error;
|
||
Logger::Get().Error(errorMsg);
|
||
}
|
||
return success;
|
||
}
|
||
|
||
bool Renderer2::_CheckResult(HRESULT hr, std::string_view errorMsg) noexcept {
|
||
assert(_state == ComponentState::NoError);
|
||
|
||
if (SUCCEEDED(hr)) {
|
||
return true;
|
||
}
|
||
|
||
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
|
||
_state = ComponentState::DeviceLost;
|
||
} else {
|
||
_state = ComponentState::Error;
|
||
}
|
||
|
||
Logger::Get().ComError(errorMsg, hr);
|
||
return false;
|
||
}
|
||
|
||
HRESULT Renderer2::_CreateCopyFramePSO(bool isSrgb, winrt::com_ptr<ID3D12PipelineState>& result) noexcept {
|
||
ID3D12Device5* device = _d3d12Context.GetDevice();
|
||
|
||
if (!_copyRootSignature) {
|
||
CD3DX12_DESCRIPTOR_RANGE1 srvRange(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0,
|
||
D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC_WHILE_SET_AT_EXECUTE);
|
||
D3D12_ROOT_PARAMETER1 rootParams[] = {
|
||
{
|
||
.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS,
|
||
.Constants = {
|
||
.Num32BitValues = 4
|
||
},
|
||
.ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX
|
||
},
|
||
{
|
||
.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
|
||
.DescriptorTable = {
|
||
.NumDescriptorRanges = 1,
|
||
.pDescriptorRanges = &srvRange
|
||
},
|
||
.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL
|
||
}
|
||
};
|
||
D3D12_STATIC_SAMPLER_DESC samplerDesc = {
|
||
.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT,
|
||
.AddressU = D3D12_TEXTURE_ADDRESS_MODE_BORDER,
|
||
.AddressV = D3D12_TEXTURE_ADDRESS_MODE_BORDER,
|
||
.AddressW = D3D12_TEXTURE_ADDRESS_MODE_BORDER,
|
||
.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER,
|
||
// 边界外使用黑色填充
|
||
.BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK,
|
||
.ShaderRegister = 0
|
||
};
|
||
CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc(
|
||
(UINT)std::size(rootParams), rootParams, 1, &samplerDesc, D3D12_ROOT_SIGNATURE_FLAG_NONE);
|
||
|
||
winrt::com_ptr<ID3DBlob> signature;
|
||
HRESULT hr = D3DX12SerializeVersionedRootSignature(
|
||
&rootSignatureDesc, _d3d12Context.GetRootSignatureVersion(), signature.put(), nullptr);
|
||
if (FAILED(hr)) {
|
||
Logger::Get().ComError("D3DX12SerializeVersionedRootSignature 失败", hr);
|
||
return hr;
|
||
}
|
||
|
||
hr = device->CreateRootSignature(
|
||
0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&_copyRootSignature));
|
||
if (FAILED(hr)) {
|
||
Logger::Get().ComError("CreateRootSignature 失败", hr);
|
||
return hr;
|
||
}
|
||
}
|
||
|
||
bool isSM6Supported = _d3d12Context.GetShaderModel() >= D3D_SHADER_MODEL_6_0;
|
||
|
||
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {
|
||
.pRootSignature = _copyRootSignature.get(),
|
||
.VS = DirectXHelper::SelectShader(isSM6Supported, CopyFrameVS, CopyFrameVS_SM5),
|
||
.PS = DirectXHelper::SelectShader(isSM6Supported, TextureBlitPS, TextureBlitPS_SM5),
|
||
.BlendState = {
|
||
.RenderTarget = {{ .RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL }}
|
||
},
|
||
.SampleMask = UINT_MAX,
|
||
.RasterizerState = {
|
||
.FillMode = D3D12_FILL_MODE_SOLID,
|
||
.CullMode = D3D12_CULL_MODE_NONE
|
||
},
|
||
.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
|
||
.NumRenderTargets = 1,
|
||
.RTVFormats = { isSrgb ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R16G16B16A16_FLOAT },
|
||
.SampleDesc = { .Count = 1 }
|
||
};
|
||
HRESULT hr = device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&result));
|
||
if (FAILED(hr)) {
|
||
Logger::Get().ComError("CreateGraphicsPipelineState 失败", hr);
|
||
return hr;
|
||
}
|
||
|
||
return S_OK;
|
||
}
|
||
|
||
}
|