mirror of
https://github.com/Blinue/Magpie.git
synced 2026-06-24 02:04:10 +00:00
feat: 着色器效果渲染 (p2)
This commit is contained in:
parent
355dfbb7e2
commit
2b02fdfc0d
11 changed files with 449 additions and 79 deletions
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
//!MAGPIE EFFECT
|
||||
//!VERSION 5
|
||||
//!CAPABILITY AdvancedColor
|
||||
|
||||
//!PARAMETER
|
||||
//!LABEL Anti-ringing Strength
|
||||
|
|
|
|||
|
|
@ -74,6 +74,21 @@ public:
|
|||
CD3DX12_RESOURCE_BARRIER::Transition(resource, stateBefore, stateAfter, 0));
|
||||
}
|
||||
|
||||
void CopyBufferRegion(
|
||||
ID3D12Resource* destBuffer,
|
||||
uint32_t destOffset,
|
||||
ID3D12Resource* srcBuffer,
|
||||
uint32_t srcOffset,
|
||||
uint32_t numBytes,
|
||||
bool shouldFlushBarriers
|
||||
) noexcept {
|
||||
if (shouldFlushBarriers) {
|
||||
_FlushBarriers();
|
||||
}
|
||||
|
||||
_commandList->CopyBufferRegion(destBuffer, destOffset, srcBuffer, srcOffset, numBytes);
|
||||
}
|
||||
|
||||
void CopyTextureRegion(
|
||||
ID3D12Resource* destResource,
|
||||
uint32_t dstX,
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ bool DescriptorHeap::Initialize(
|
|||
}
|
||||
|
||||
HRESULT DescriptorHeap::Alloc(uint32_t count, uint32_t& offset) noexcept {
|
||||
assert(count != 0);
|
||||
auto lk = _freeBlocksLock.lock_exclusive();
|
||||
|
||||
for (auto it = _freeBlocks.begin(); it != _freeBlocks.end(); ++it) {
|
||||
|
|
@ -74,7 +75,7 @@ static uint32_t GetBlockOffset(const std::pair<const uint32_t, uint32_t>& freeBl
|
|||
}
|
||||
|
||||
void DescriptorHeap::Free(uint32_t offset, uint32_t count) noexcept {
|
||||
assert(offset != std::numeric_limits<uint32_t>::max() && offset + count <= _capacity);
|
||||
assert(count != 0 && offset != std::numeric_limits<uint32_t>::max() && offset + count <= _capacity);
|
||||
|
||||
auto lk = _freeBlocksLock.lock_exclusive();
|
||||
|
||||
|
|
|
|||
|
|
@ -7,10 +7,17 @@
|
|||
#include "ScalingWindow.h"
|
||||
#include "ShaderEffectDrawer.h"
|
||||
#include "EffectInfo.h"
|
||||
#include "DescriptorHeap.h"
|
||||
|
||||
namespace Magpie {
|
||||
|
||||
EffectsDrawer::~EffectsDrawer() noexcept {}
|
||||
EffectsDrawer::~EffectsDrawer() noexcept {
|
||||
#ifdef _DEBUG
|
||||
if (_descriptorBaseOffset != std::numeric_limits<uint32_t>::max()) {
|
||||
_d3d12Context->GetDescriptorHeap().Free(_descriptorBaseOffset, _CalcDescriptorCount());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static SizeU CalcOutputSize(
|
||||
uint32_t scaleFactor,
|
||||
|
|
@ -85,6 +92,7 @@ bool EffectsDrawer::Initialize(
|
|||
_effectDatas.resize(effectCount);
|
||||
outputSize = inputSize;
|
||||
|
||||
// 效果的初始化可能是异步的,因此尽早进行
|
||||
for (uint32_t i = 0; i < effectCount; ++i) {
|
||||
auto& effectData = _effectDatas[i];
|
||||
const auto& effectOption = options.effects[i];
|
||||
|
|
@ -120,6 +128,61 @@ bool EffectsDrawer::Initialize(
|
|||
|
||||
_outputSize = outputSize;
|
||||
|
||||
// 创建效果的输入/输出纹理
|
||||
if (uint32_t descriptorCount = _CalcDescriptorCount()) {
|
||||
auto& descriptorHeap = _d3d12Context->GetDescriptorHeap();
|
||||
HRESULT hr = descriptorHeap.Alloc(descriptorCount, _descriptorBaseOffset);
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("DescriptorHeap::Alloc 失败", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
CD3DX12_CPU_DESCRIPTOR_HANDLE cpuHandle(descriptorHeap.GetCpuHandle(_descriptorBaseOffset));
|
||||
const uint32_t descriptorSize = descriptorHeap.GetDescriptorSize();
|
||||
|
||||
CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_DEFAULT);
|
||||
|
||||
D3D12_HEAP_FLAGS heapFlags = _d3d12Context->IsHeapFlagCreateNotZeroedSupported() ?
|
||||
D3D12_HEAP_FLAG_CREATE_NOT_ZEROED : D3D12_HEAP_FLAG_NONE;
|
||||
|
||||
CD3DX12_RESOURCE_DESC texDesc = CD3DX12_RESOURCE_DESC::Tex2D(
|
||||
_isScRGB ? DXGI_FORMAT_R16G16B16A16_FLOAT : DXGI_FORMAT_R10G10B10A2_UNORM,
|
||||
0, 0, 1, 1, 1, 0,
|
||||
D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS
|
||||
);
|
||||
|
||||
CD3DX12_SHADER_RESOURCE_VIEW_DESC srvDesc =
|
||||
CD3DX12_SHADER_RESOURCE_VIEW_DESC::Tex2D(texDesc.Format, 1);
|
||||
CD3DX12_UNORDERED_ACCESS_VIEW_DESC uavDesc =
|
||||
CD3DX12_UNORDERED_ACCESS_VIEW_DESC::Tex2D(texDesc.Format);
|
||||
|
||||
for (uint32_t i = 0; i < effectCount; ++i) {
|
||||
auto& effectData = _effectDatas[i];
|
||||
|
||||
// 如果不需要缩小,最后一个效果直接写入环形缓冲,不需要创建输出纹理
|
||||
if (i == effectCount - 1 && effectData.outputSize == _outputSize) {
|
||||
break;
|
||||
}
|
||||
|
||||
texDesc.Width = effectData.outputSize.width;
|
||||
texDesc.Height = effectData.outputSize.height;
|
||||
|
||||
hr = device->CreateCommittedResource(&heapProps, heapFlags, &texDesc,
|
||||
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, nullptr, IID_PPV_ARGS(&effectData.outputTexture));
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("CreateCommittedResource 失败", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
device->CreateShaderResourceView(effectData.outputTexture.get(), &srvDesc, cpuHandle);
|
||||
cpuHandle.Offset(descriptorSize);
|
||||
|
||||
device->CreateUnorderedAccessView(
|
||||
effectData.outputTexture.get(), nullptr, &uavDesc, cpuHandle);
|
||||
cpuHandle.Offset(descriptorSize);
|
||||
}
|
||||
}
|
||||
|
||||
// CatmullRomDrawer 将在渲染时按需创建 PSO,初始化无代价
|
||||
_catmullRomDrawer.Initialize(d3d12Context);
|
||||
|
||||
|
|
@ -220,4 +283,26 @@ void EffectsDrawer::OnColorInfoChanged(const ColorInfo& colorInfo) noexcept {
|
|||
_isScRGB = colorInfo.kind != winrt::AdvancedColorKind::StandardDynamicRange;
|
||||
}
|
||||
|
||||
uint32_t EffectsDrawer::_CalcDescriptorCount() const noexcept {
|
||||
// 如果最后一个效果的缩放类型是 Fit 或 Fill 且缩放比例不大于 1,那么始终可以直接写入环形缓冲区,
|
||||
// 需要的描述符数量可以减少两个。
|
||||
// 还有更复杂的情况,如倒数第二个效果是 Fit(0.5,0.5),最后一个效果放大一倍,也可以认为输出尺寸
|
||||
// 永远不会大于 rendererSize,不过这较为复杂,还有舍入的问题,安全起见不进行优化。
|
||||
uint32_t count = (uint32_t)_effectDatas.size() * 2;
|
||||
|
||||
if (_effectDatas.back().effectInfo->scaleFactor != 0) {
|
||||
return count;
|
||||
}
|
||||
|
||||
const EffectOption& effectOption = ScalingWindow::Get().Options().effects.back();
|
||||
if ((effectOption.scalingType == ScalingType::Fit || effectOption.scalingType == ScalingType::Fill) &&
|
||||
effectOption.scale.first < 1 + FLOAT_EPSILON<float> &&
|
||||
effectOption.scale.second < 1 + FLOAT_EPSILON<float>)
|
||||
{
|
||||
return count - 2;
|
||||
} else {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ public:
|
|||
void OnColorInfoChanged(const ColorInfo& colorInfo) noexcept;
|
||||
|
||||
private:
|
||||
uint32_t _CalcDescriptorCount() const noexcept;
|
||||
|
||||
D3D12Context* _d3d12Context = nullptr;
|
||||
|
||||
SizeU _inputSize{};
|
||||
|
|
@ -49,13 +51,17 @@ private:
|
|||
|
||||
struct _EffectData {
|
||||
std::unique_ptr<EffectDrawerBase> drawer;
|
||||
const EffectInfo* effectInfo;
|
||||
SizeU outputSize;
|
||||
const EffectInfo* effectInfo = nullptr;
|
||||
SizeU outputSize{};
|
||||
winrt::com_ptr<ID3D12Resource> outputTexture;
|
||||
};
|
||||
|
||||
SmallVector<_EffectData> _effectDatas;
|
||||
CatmullRomDrawer _catmullRomDrawer;
|
||||
|
||||
// 描述符的布局是 SRV|UAV|SRV|UAV|...
|
||||
uint32_t _descriptorBaseOffset = std::numeric_limits<uint32_t>::max();
|
||||
|
||||
winrt::com_ptr<ID3D12QueryHeap> _queryHeap;
|
||||
winrt::com_ptr<ID3D12Resource> _queryResultBuffer;
|
||||
UINT64 _timestampFrequency = 0;
|
||||
|
|
|
|||
|
|
@ -156,16 +156,21 @@ std::string EffectsService::SubmitCompileShaderEffectTask(
|
|||
// shaderModel 和 flags 不参与哈希,它们决定缓存键(也是缓存文件名)
|
||||
uint64_t hash = rapidhash(source.data(), source.size());
|
||||
if (inlineParams) {
|
||||
for (const auto& pair : *inlineParams) {
|
||||
for (const EffectParameterDesc& param : effectInfo.params) {
|
||||
if (param.name == pair.first) {
|
||||
// 将参数值归一化然后保留 4 位精度
|
||||
long normValue = std::lround((pair.second - param.minValue) /
|
||||
(param.maxValue - param.minValue) * 10000);
|
||||
hash = phmap::HashState().combine(hash, pair.first, normValue);
|
||||
break;
|
||||
}
|
||||
// 即使 inlineParams 中不包含的参数也参与哈希,否则无法区分未启用内联变量和
|
||||
// 启用但 inlineParams 中没有成员。
|
||||
for (const EffectParameterDesc& param : effectInfo.params) {
|
||||
float value;
|
||||
auto it1 = inlineParams->find(param.name);
|
||||
if (it1 != inlineParams->end()) {
|
||||
value = it1->second;
|
||||
} else {
|
||||
value = param.defaultValue;
|
||||
}
|
||||
|
||||
// 将参数值归一化然后保留 4 位精度
|
||||
long normValue = std::lround((value - param.minValue) /
|
||||
(param.maxValue - param.minValue) * 10000);
|
||||
hash = phmap::HashState().combine(hash, normValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -260,7 +265,7 @@ public:
|
|||
return E_FAIL;
|
||||
}
|
||||
|
||||
char* result = new char[file.size()];
|
||||
char* result = std::make_unique<char[]>(file.size()).release();
|
||||
std::memcpy(result, file.data(), file.size());
|
||||
|
||||
*ppData = result;
|
||||
|
|
@ -270,7 +275,7 @@ public:
|
|||
}
|
||||
|
||||
HRESULT CALLBACK Close(LPCVOID pData) noexcept override {
|
||||
delete[](char*)pData;
|
||||
std::unique_ptr<char[]>((char*)pData);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include "pch.h"
|
||||
#include "CommandContext.h"
|
||||
#include "D3D12Context.h"
|
||||
#include "DescriptorHeap.h"
|
||||
#include "DirectXHelper.h"
|
||||
#include "EffectsService.h"
|
||||
#include "Logger.h"
|
||||
#include "ScalingOptions.h"
|
||||
|
|
@ -15,12 +17,12 @@
|
|||
namespace Magpie {
|
||||
|
||||
ShaderEffectDrawer::~ShaderEffectDrawer() noexcept {
|
||||
if (!_passDatas.empty()) {
|
||||
uint32_t descriptorBaseOffset = _passDatas[0].descriptorBaseOffset;
|
||||
if (descriptorBaseOffset != std::numeric_limits<uint32_t>::max()) {
|
||||
_d3d12Context->GetDescriptorHeap().Free(descriptorBaseOffset, _descriptorCount);
|
||||
}
|
||||
#ifdef _DEBUG
|
||||
if (_descriptorBaseOffset != std::numeric_limits<uint32_t>::max()) {
|
||||
_d3d12Context->GetDescriptorHeap().Free(
|
||||
_descriptorBaseOffset, (uint32_t)_textureDescriptorMap.size() + 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!_compilationTaskId.empty()) {
|
||||
EffectsService::Get().ReleaseTask(_compilationTaskId);
|
||||
|
|
@ -34,13 +36,13 @@ const EffectInfo* ShaderEffectDrawer::Initialize(
|
|||
_d3d12Context = &d3d12Context;
|
||||
_effectOption = &effectOption;
|
||||
|
||||
const EffectInfo* effectInfo = EffectsService::Get().GetEffect(effectOption.name);
|
||||
if (!effectInfo) {
|
||||
_effectInfo = EffectsService::Get().GetEffect(effectOption.name);
|
||||
if (!_effectInfo) {
|
||||
Logger::Get().Error("EffectsService::GetEffect 失败");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return effectInfo;
|
||||
return _effectInfo;
|
||||
}
|
||||
|
||||
void ShaderEffectDrawer::Bind(SizeU inputSize, SizeU outputSize, const ColorInfo& colorInfo) noexcept {
|
||||
|
|
@ -119,17 +121,31 @@ HRESULT ShaderEffectDrawer::Update(EffectDrawerState& state, std::string& messag
|
|||
}
|
||||
|
||||
HRESULT ShaderEffectDrawer::Draw(
|
||||
ComputeContext& /*computeContext*/,
|
||||
ComputeContext& computeContext,
|
||||
uint32_t /*inputSrvOffset*/,
|
||||
uint32_t /*outputUavOffset*/
|
||||
) noexcept {
|
||||
assert(_drawInfo);
|
||||
|
||||
if (_isConstantBufferOutdated) {
|
||||
_isConstantBufferOutdated = false;
|
||||
|
||||
computeContext.CopyBufferRegion(_constantBuffer.get(), 0,
|
||||
_constantUploadBuffer.get(), 0, _constantsDataSize, false);
|
||||
|
||||
computeContext.InsertTransitionBarrier(
|
||||
_constantBuffer.get(),
|
||||
D3D12_RESOURCE_STATE_COPY_DEST,
|
||||
D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER
|
||||
);
|
||||
}
|
||||
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT ShaderEffectDrawer::_CreateDeviceResources() {
|
||||
ID3D12Device5* device = _d3d12Context->GetDevice();
|
||||
const ScalingOptions& options = ScalingWindow::Get().Options();
|
||||
|
||||
const uint32_t passCount = (uint32_t)_drawInfo->passes.size();
|
||||
_passDatas.resize(passCount);
|
||||
|
|
@ -284,40 +300,255 @@ HRESULT ShaderEffectDrawer::_CreateDeviceResources() {
|
|||
}
|
||||
}
|
||||
|
||||
// 需要的描述符数量不会变化
|
||||
if (_descriptorCount == 0) {
|
||||
// TODO: 合并输入和输出相同的通道
|
||||
// 描述符布局不会变化
|
||||
if (_descriptorBaseOffset == std::numeric_limits<uint32_t>::max()) {
|
||||
// [(通道序号, 需要的描述符数量)]
|
||||
SmallVector<std::pair<uint32_t, uint32_t>> descriptorCounts;
|
||||
|
||||
for (uint32_t passIdx = 0; passIdx < passCount; ++passIdx) {
|
||||
_PassData& curPassData = _passDatas[passIdx];
|
||||
const ShaderEffectPassDesc& curPassDesc = _drawInfo->passes[passIdx];
|
||||
|
||||
// 暂时保存偏移
|
||||
curPassData.descriptorBaseOffset = _descriptorCount;
|
||||
|
||||
_descriptorCount += (uint32_t)curPassDesc.inputs.size();
|
||||
_descriptorCount += (uint32_t)curPassDesc.outputs.size();
|
||||
uint32_t curPassDescriptorCount =
|
||||
(uint32_t)curPassDesc.inputs.size() + (uint32_t)curPassDesc.outputs.size();
|
||||
|
||||
if (!curPassDesc.inputs.empty() && curPassDesc.inputs[0] == 0) {
|
||||
--_descriptorCount;
|
||||
--curPassDescriptorCount;
|
||||
}
|
||||
if (curPassDesc.outputs[0] == 1) {
|
||||
--_descriptorCount;
|
||||
--curPassDescriptorCount;
|
||||
}
|
||||
|
||||
if (curPassDescriptorCount != 0) {
|
||||
descriptorCounts.emplace_back(passIdx, curPassDescriptorCount);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t descriptorBaseOffset;
|
||||
|
||||
if (!descriptorCounts.empty()) {
|
||||
// 按需要的描述符数量从多到少排序,这可以提高复用描述符的概率
|
||||
std::sort(
|
||||
descriptorCounts.begin(),
|
||||
descriptorCounts.end(),
|
||||
[](const auto& pair1, const auto& pair2) {
|
||||
return pair1.second > pair2.second;
|
||||
}
|
||||
);
|
||||
|
||||
for (const auto& pair : descriptorCounts) {
|
||||
const ShaderEffectPassDesc& curPassDesc = _drawInfo->passes[pair.first];
|
||||
SmallVector<uint32_t> curPassDescriptors;
|
||||
|
||||
if (!curPassDesc.inputs.empty()) {
|
||||
auto begin = curPassDesc.inputs.begin() + uint32_t(curPassDesc.inputs[0] == 0);
|
||||
auto end = curPassDesc.inputs.end();
|
||||
curPassDescriptors.reserve(curPassDescriptors.size() + (end - begin));
|
||||
for (auto it = begin; it != end; ++it) {
|
||||
curPassDescriptors.push_back(*it - 2);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto begin = curPassDesc.outputs.begin() + uint32_t(curPassDesc.outputs[0] == 1);
|
||||
auto end = curPassDesc.outputs.end();
|
||||
curPassDescriptors.reserve(curPassDescriptors.size() + (end - begin));
|
||||
uint32_t textureCount = (uint32_t)_drawInfo->textures.size();
|
||||
for (auto it = begin; it != end; ++it) {
|
||||
// 为了和 SRV 做区分,UAV 的索引加上纹理总数
|
||||
curPassDescriptors.push_back(*it - 2 + textureCount);
|
||||
}
|
||||
}
|
||||
|
||||
assert(curPassDescriptors.size() == pair.second);
|
||||
|
||||
// 寻找可以复用的区域
|
||||
auto it = std::search(
|
||||
_textureDescriptorMap.begin(),
|
||||
_textureDescriptorMap.end(),
|
||||
curPassDescriptors.begin(),
|
||||
curPassDescriptors.end()
|
||||
);
|
||||
if (it == _textureDescriptorMap.end()) {
|
||||
_passDatas[pair.first].descriptorBaseOffset = (uint32_t)_textureDescriptorMap.size();
|
||||
|
||||
_textureDescriptorMap.insert(
|
||||
_textureDescriptorMap.end(),
|
||||
curPassDescriptors.begin(),
|
||||
curPassDescriptors.end()
|
||||
);
|
||||
} else {
|
||||
_passDatas[pair.first].descriptorBaseOffset =
|
||||
uint32_t(it - _textureDescriptorMap.begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT hr = _d3d12Context->GetDescriptorHeap()
|
||||
.Alloc(_descriptorCount, descriptorBaseOffset);
|
||||
.Alloc((uint32_t)_textureDescriptorMap.size() + 1, _descriptorBaseOffset);
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("DescriptorHeap::Alloc 失败", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
// 索引 0 是 CBV
|
||||
for (_PassData& passData : _passDatas) {
|
||||
passData.descriptorBaseOffset += descriptorBaseOffset;
|
||||
passData.descriptorBaseOffset += _descriptorBaseOffset + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 常量缓冲区可以复用。常量缓冲区布局如下:
|
||||
// uint2 __inputSize;
|
||||
// uint2 __outputSize;
|
||||
// float2 __inputPt;
|
||||
// float2 __outputPt;
|
||||
// float2 __scale;
|
||||
// ↓ PS 样式参数 ↓
|
||||
// [float2 __pass1OutputPt;]
|
||||
// [float2 __pass2OutputPt;]
|
||||
// [...]
|
||||
// ↓ WCG/HDR 参数 ↓
|
||||
// [float __maxLuminance;]
|
||||
// [float __sdrWhiteLevel;]
|
||||
// ↓ 效果参数 ↓
|
||||
// [float param1;]
|
||||
// [uint param2;]
|
||||
// [...]
|
||||
if (!_constantBuffer) {
|
||||
// 10 个内置常量
|
||||
uint32_t paramCount = 10;
|
||||
// PS 样式需要额外常量,除了最后一个通道
|
||||
for (uint32_t i = 0, end = (uint32_t)_drawInfo->passes.size() - 1; i < end; ++i) {
|
||||
if (bool(_drawInfo->passes[i].flags & ShaderEffectPassFlags::PSStyle)) {
|
||||
paramCount += 2;
|
||||
}
|
||||
}
|
||||
// WCG/HDR 需要额外常量,始终预留位置使常量缓冲区可以复用
|
||||
paramCount += 2;
|
||||
// 未启用内联参数时每个参数占用一个常量
|
||||
if (!options.IsInlineParams()) {
|
||||
paramCount += (uint32_t)_effectInfo->params.size();
|
||||
}
|
||||
|
||||
D3D12_HEAP_FLAGS heapFlag = _d3d12Context->IsHeapFlagCreateNotZeroedSupported() ?
|
||||
D3D12_HEAP_FLAG_CREATE_NOT_ZEROED : D3D12_HEAP_FLAG_NONE;
|
||||
|
||||
CD3DX12_HEAP_PROPERTIES heapProperties(D3D12_HEAP_TYPE_UPLOAD);
|
||||
|
||||
CD3DX12_RESOURCE_DESC bufferDesc = CD3DX12_RESOURCE_DESC::Buffer(UINT64(paramCount * 4));
|
||||
|
||||
HRESULT hr = device->CreateCommittedResource(
|
||||
&heapProperties,
|
||||
heapFlag,
|
||||
&bufferDesc,
|
||||
D3D12_RESOURCE_STATE_GENERIC_READ,
|
||||
nullptr,
|
||||
IID_PPV_ARGS(&_constantUploadBuffer)
|
||||
);
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("CreateCommittedResource 失败", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
// 常量缓冲区必须对齐到 256 字节
|
||||
bufferDesc.Width = (bufferDesc.Width + 255) & ~255;
|
||||
hr = device->CreateCommittedResource(
|
||||
&heapProperties,
|
||||
heapFlag,
|
||||
&bufferDesc,
|
||||
D3D12_RESOURCE_STATE_COMMON,
|
||||
nullptr,
|
||||
IID_PPV_ARGS(&_constantBuffer)
|
||||
);
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("CreateCommittedResource 失败", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
auto& descriptorHeap = _d3d12Context->GetDescriptorHeap();
|
||||
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {
|
||||
.BufferLocation = _constantBuffer->GetGPUVirtualAddress(),
|
||||
.SizeInBytes = (UINT)bufferDesc.Width
|
||||
};
|
||||
device->CreateConstantBufferView(
|
||||
&cbvDesc, descriptorHeap.GetCpuHandle(_descriptorBaseOffset));
|
||||
|
||||
// 无需解除映射
|
||||
D3D12_RANGE readRange{};
|
||||
hr = _constantUploadBuffer->Map(0, &readRange, &_constantUploadBufferData);
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("ID3D12Resource::Map 失败", hr);
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新 _constantUploadBuffer,_constantBuffer 将在 Draw 中更新
|
||||
{
|
||||
using Constant32 = DirectXHelper::Constant32;
|
||||
// SmallVector 中数据地址偏移量是 16 字节,alignas(16) 使 SmallVector
|
||||
// 和其中数据都对齐到 16 字节边界。
|
||||
alignas(16) SmallVector<Constant32, 32> constants;
|
||||
assert((uint8_t*)constants.data() - (uint8_t*)&constants == 16);
|
||||
|
||||
constants.emplace_back(Constant32::UInt(_inputSize.width));
|
||||
constants.emplace_back(Constant32::UInt(_inputSize.height));
|
||||
constants.emplace_back(Constant32::UInt(_outputSize.width));
|
||||
constants.emplace_back(Constant32::UInt(_outputSize.height));
|
||||
constants.emplace_back(Constant32::Float(1.0f / _inputSize.width));
|
||||
constants.emplace_back(Constant32::Float(1.0f / _inputSize.height));
|
||||
constants.emplace_back(Constant32::Float(1.0f / _outputSize.width));
|
||||
constants.emplace_back(Constant32::Float(1.0f / _outputSize.height));
|
||||
constants.emplace_back(Constant32::Float((float)_outputSize.width / _inputSize.width));
|
||||
constants.emplace_back(Constant32::Float((float)_outputSize.height / _inputSize.height));
|
||||
|
||||
// PS 样式参数
|
||||
for (uint32_t i = 0, end = (uint32_t)_drawInfo->passes.size() - 1; i < end; ++i) {
|
||||
if (bool(_drawInfo->passes[i].flags & ShaderEffectPassFlags::PSStyle)) {
|
||||
uint32_t output = _drawInfo->passes[i].outputs[0];
|
||||
if (output == 1) {
|
||||
constants.emplace_back(Constant32::Float(1.0f / _outputSize.width));
|
||||
constants.emplace_back(Constant32::Float(1.0f / _outputSize.height));
|
||||
} else {
|
||||
D3D12_RESOURCE_DESC texDesc = _textures[size_t(output - 2)]->GetDesc();
|
||||
constants.emplace_back(Constant32::Float(1.0f / texDesc.Width));
|
||||
constants.emplace_back(Constant32::Float(1.0f / texDesc.Height));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WCG/HDR 参数
|
||||
if (bool(_effectInfo->flags & EffectFlags::SupportAdvancedColor) &&
|
||||
_colorInfo.kind != winrt::AdvancedColorKind::StandardDynamicRange)
|
||||
{
|
||||
constants.emplace_back(Constant32::Float(_colorInfo.maxLuminance));
|
||||
constants.emplace_back(Constant32::Float(_colorInfo.sdrWhiteLevel));
|
||||
}
|
||||
|
||||
// 效果参数
|
||||
if (!options.IsInlineParams()) {
|
||||
for (const EffectParameterDesc& paramDesc : _effectInfo->params) {
|
||||
auto it = _effectOption->parameters.find(paramDesc.name);
|
||||
float value = it == _effectOption->parameters.end() ? paramDesc.defaultValue : it->second;
|
||||
switch (paramDesc.type) {
|
||||
case EffectParameterType::Float:
|
||||
constants.emplace_back(Constant32::Float(value));
|
||||
break;
|
||||
case EffectParameterType::Int:
|
||||
constants.emplace_back(Constant32::Int(std::lround(value)));
|
||||
break;
|
||||
default:
|
||||
assert(paramDesc.type == EffectParameterType::UInt);
|
||||
constants.emplace_back(Constant32::UInt((uint32_t)std::lround(value)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_constantsDataSize = (uint32_t)constants.size() * 4;
|
||||
assert(_constantsDataSize <= _constantUploadBuffer->GetDesc().Width);
|
||||
std::memcpy(_constantUploadBufferData, constants.data(), _constantsDataSize);
|
||||
|
||||
_isConstantBufferOutdated = true;
|
||||
}
|
||||
|
||||
{
|
||||
_textures.resize(_drawInfo->textures.size());
|
||||
|
||||
|
|
@ -327,8 +558,8 @@ HRESULT ShaderEffectDrawer::_CreateDeviceResources() {
|
|||
exprParser.DefineConst("OUTPUT_WIDTH", _outputSize.width);
|
||||
exprParser.DefineConst("OUTPUT_HEIGHT", _outputSize.height);
|
||||
|
||||
for (size_t i = 0; i < _textures.size(); ++i) {
|
||||
const ShaderEffectTextureDesc& effectTexDesc = _drawInfo->textures[i];
|
||||
for (size_t texIdx = 0; texIdx < _textures.size(); ++texIdx) {
|
||||
const ShaderEffectTextureDesc& effectTexDesc = _drawInfo->textures[texIdx];
|
||||
|
||||
DXGI_FORMAT dxgiFormat;
|
||||
|
||||
|
|
@ -364,8 +595,8 @@ HRESULT ShaderEffectDrawer::_CreateDeviceResources() {
|
|||
dxgiFormat = SHADER_TEXTURE_FORMAT_PROPS[(uint32_t)effectTexDesc.format].dxgiFormat;
|
||||
}
|
||||
|
||||
if (_textures[i]) {
|
||||
D3D12_RESOURCE_DESC texDesc = _textures[i]->GetDesc();
|
||||
if (_textures[texIdx]) {
|
||||
D3D12_RESOURCE_DESC texDesc = _textures[texIdx]->GetDesc();
|
||||
if (texDesc.Width == texSize.width && texDesc.Height == texSize.height &&
|
||||
texDesc.Format == dxgiFormat) {
|
||||
continue;
|
||||
|
|
@ -383,59 +614,56 @@ HRESULT ShaderEffectDrawer::_CreateDeviceResources() {
|
|||
);
|
||||
|
||||
HRESULT hr = device->CreateCommittedResource(&heapProps, heapFlags, &texDesc,
|
||||
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, nullptr, IID_PPV_ARGS(&_textures[i]));
|
||||
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, nullptr, IID_PPV_ARGS(&_textures[texIdx]));
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("CreateCommittedResource 失败", hr);
|
||||
return hr;
|
||||
}
|
||||
} else {
|
||||
if (_textures[i]) {
|
||||
if (_textures[texIdx]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& descriptorHeap = _d3d12Context->GetDescriptorHeap();
|
||||
const uint32_t descriptorSize = descriptorHeap.GetDescriptorSize();
|
||||
if (!_textureDescriptorMap.empty()) {
|
||||
auto& descriptorHeap = _d3d12Context->GetDescriptorHeap();
|
||||
const uint32_t descriptorSize = descriptorHeap.GetDescriptorSize();
|
||||
|
||||
for (uint32_t passIdx = 0; passIdx < passCount; ++passIdx) {
|
||||
const ShaderEffectPassDesc& curPassDesc = _drawInfo->passes[passIdx];
|
||||
|
||||
CD3DX12_CPU_DESCRIPTOR_HANDLE descriptorCpuHandle(
|
||||
descriptorHeap.GetCpuHandle(_passDatas[passIdx].descriptorBaseOffset));
|
||||
// 索引 0 是 CBV
|
||||
CD3DX12_CPU_DESCRIPTOR_HANDLE baseHandle(
|
||||
descriptorHeap.GetCpuHandle(_descriptorBaseOffset), descriptorSize);
|
||||
|
||||
CD3DX12_SHADER_RESOURCE_VIEW_DESC srvDesc =
|
||||
CD3DX12_SHADER_RESOURCE_VIEW_DESC::Tex2D(dxgiFormat, 1);
|
||||
|
||||
if (!curPassDesc.inputs.empty()) {
|
||||
auto it = std::find(curPassDesc.inputs.begin(), curPassDesc.inputs.end(), i + 2);
|
||||
if (it != curPassDesc.inputs.end()) {
|
||||
size_t offset = it - curPassDesc.inputs.begin();
|
||||
if (curPassDesc.inputs[0] == 0) {
|
||||
--offset;
|
||||
}
|
||||
device->CreateShaderResourceView(_textures[i].get(), &srvDesc,
|
||||
CD3DX12_CPU_DESCRIPTOR_HANDLE(descriptorCpuHandle, (INT)offset, descriptorSize));
|
||||
for (auto it = _textureDescriptorMap.begin();; ++it) {
|
||||
it = std::find(it, _textureDescriptorMap.end(), (uint32_t)texIdx);
|
||||
if (it == _textureDescriptorMap.end()) {
|
||||
break;
|
||||
}
|
||||
|
||||
descriptorCpuHandle.Offset(
|
||||
curPassDesc.inputs[0] == 0 ? curPassDesc.inputs.size() - 1 : curPassDesc.inputs.size(),
|
||||
descriptorSize
|
||||
);
|
||||
size_t offset = it - _textureDescriptorMap.begin();
|
||||
device->CreateShaderResourceView(_textures[texIdx].get(), &srvDesc,
|
||||
CD3DX12_CPU_DESCRIPTOR_HANDLE(baseHandle, (INT)offset, descriptorSize));
|
||||
}
|
||||
|
||||
|
||||
// 为了和 SRV 做区分,UAV 的索引加上纹理总数
|
||||
uint32_t uavIdx = uint32_t(texIdx + _textures.size());
|
||||
CD3DX12_UNORDERED_ACCESS_VIEW_DESC uavDesc =
|
||||
CD3DX12_UNORDERED_ACCESS_VIEW_DESC::Tex2D(dxgiFormat);
|
||||
|
||||
auto it = std::find(curPassDesc.outputs.begin(), curPassDesc.outputs.end(), i + 2);
|
||||
if (it != curPassDesc.outputs.end()) {
|
||||
size_t offset = it - curPassDesc.outputs.begin();
|
||||
if (curPassDesc.outputs[0] == 1) {
|
||||
--offset;
|
||||
for (auto it = _textureDescriptorMap.begin();; ++it) {
|
||||
it = std::find(it, _textureDescriptorMap.end(), uavIdx);
|
||||
if (it == _textureDescriptorMap.end()) {
|
||||
break;
|
||||
}
|
||||
device->CreateUnorderedAccessView(_textures[i].get(), nullptr, &uavDesc,
|
||||
CD3DX12_CPU_DESCRIPTOR_HANDLE(descriptorCpuHandle, (INT)offset, descriptorSize));
|
||||
|
||||
size_t offset = it - _textureDescriptorMap.begin();
|
||||
device->CreateUnorderedAccessView(_textures[texIdx].get(), nullptr, &uavDesc,
|
||||
CD3DX12_CPU_DESCRIPTOR_HANDLE(baseHandle, (INT)offset, descriptorSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ private:
|
|||
|
||||
D3D12Context* _d3d12Context = nullptr;
|
||||
const EffectOption* _effectOption = nullptr;
|
||||
const EffectInfo* _effectInfo = nullptr;
|
||||
|
||||
SizeU _inputSize{};
|
||||
SizeU _outputSize{};
|
||||
|
|
@ -41,12 +42,28 @@ private:
|
|||
struct _PassData {
|
||||
winrt::com_ptr<ID3D12RootSignature> rootSignature;
|
||||
winrt::com_ptr<ID3D12PipelineState> pso;
|
||||
uint32_t descriptorBaseOffset = std::numeric_limits<uint32_t>::max();
|
||||
// 这里是 _descriptorBaseOffset 中成员的引用,无需释放
|
||||
uint32_t descriptorBaseOffset;
|
||||
SizeU dispatchCount;
|
||||
};
|
||||
SmallVector<_PassData> _passDatas;
|
||||
|
||||
winrt::com_ptr<ID3D12Resource> _constantBuffer;
|
||||
winrt::com_ptr<ID3D12Resource> _constantUploadBuffer;
|
||||
void* _constantUploadBufferData = nullptr;
|
||||
uint32_t _constantsDataSize = 0;
|
||||
|
||||
SmallVector<winrt::com_ptr<ID3D12Resource>> _textures;
|
||||
uint32_t _descriptorCount = 0;
|
||||
// 描述符布局: CBV | [SRV] | [UAV] | [SRV] | ...
|
||||
// 多个通道的 SRV 和 UAV 可能合并,_textureDescriptorMap 保存了布局。描述符总数是
|
||||
// _textureDescriptorMap.size() + 1。
|
||||
uint32_t _descriptorBaseOffset = std::numeric_limits<uint32_t>::max();
|
||||
// 和 ShaderEffectPassDesc 的 inputs/outputs 不同,这里存储 _textures 中元素索引,
|
||||
// 从 0 开始。虽然可以用额外字段区分 SRV 和 UAV,但这里想尽可能避免堆分配,因此 UAV
|
||||
// 的索引都加上 _textures.size() 作为区分。
|
||||
SmallVector<uint32_t> _textureDescriptorMap;
|
||||
|
||||
bool _isConstantBufferOutdated = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1575,7 +1575,7 @@ static void GenerateShaderSources(
|
|||
|
||||
// WCG/HDR 需要额外常量
|
||||
if (isAdvancedColorEnabled) {
|
||||
headerCode.append("\tfloat __maxLuminance;\n\tfloat __sdrWhiteLevel\n");
|
||||
headerCode.append("\tfloat __maxLuminance;\n\tfloat __sdrWhiteLevel;\n");
|
||||
}
|
||||
|
||||
constexpr const char* PARAM_TYPE_STRS[] = { "float ","int ","uint " };
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#pragma once
|
||||
#include <d3d11_4.h>
|
||||
#include <d3d12.h>
|
||||
|
||||
namespace Magpie {
|
||||
|
|
@ -7,8 +6,20 @@ namespace Magpie {
|
|||
struct DirectXHelper {
|
||||
union Constant32 {
|
||||
float floatVal;
|
||||
uint32_t uintVal;
|
||||
int intVal;
|
||||
uint32_t uintVal;
|
||||
|
||||
static Constant32 Float(float value) noexcept {
|
||||
return Constant32{ .floatVal = value };
|
||||
}
|
||||
|
||||
static Constant32 Int(int value) noexcept {
|
||||
return Constant32{ .intVal = value };
|
||||
}
|
||||
|
||||
static Constant32 UInt(uint32_t value) noexcept {
|
||||
return Constant32{ .uintVal = value };
|
||||
}
|
||||
};
|
||||
|
||||
static bool IsWARP(const DXGI_ADAPTER_DESC1& desc) noexcept {
|
||||
|
|
|
|||
|
|
@ -62,7 +62,8 @@ private:
|
|||
// UINT_MAX 表示正在使用中
|
||||
uint32_t lastAccess = std::numeric_limits<uint32_t>::max();
|
||||
};
|
||||
phmap::flat_hash_map<std::string, _ShaderEffectMemCacheItem> _shaderEffectCache;
|
||||
// 需确保 drawInfo 地址稳定
|
||||
phmap::node_hash_map<std::string, _ShaderEffectMemCacheItem> _shaderEffectCache;
|
||||
wil::srwlock _shaderEffectCacheLock;
|
||||
uint32_t _nextLastAccess = 0;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue