优化hlsl,为若干effect添加属性

This commit is contained in:
Xu Liu 2021-02-23 16:25:10 +08:00
commit f1621c7cd2
21 changed files with 517 additions and 232 deletions

View file

@ -1,6 +1,6 @@
#include "common.hlsli"
// 因为 d2d 会将着色器间传递的值限制到 [0, 1]pass1 传递到 pass2 的值需要进行压缩
// 假设所有值在 0~32
// 假设所有值在 0~32 之间
#define Compress(value) (value / 32) // (atan(value) / PI + 0.5)
#define Uncompress(value) (value * 32) // (tan((value - 0.5) * PI))

View file

@ -31,18 +31,18 @@ D2D_PS_ENTRY(main) {
// [ c20, c6, c7, c8, c17 ]
// [ c15, c12, c14 ]
// [ c13 ]
float left1X = GetCheckedLeft(0, 1);
float left2X = GetCheckedLeft(0, 2);
float left3X = GetCheckedLeft(0, 3);
float right1X = GetCheckedRight(0, 1);
float right2X = GetCheckedRight(0, 2);
float right3X = GetCheckedRight(0, 3);
float top1Y = GetCheckedTop(0, 1);
float top2Y = GetCheckedTop(0, 2);
float top3Y = GetCheckedTop(0, 3);
float bottom1Y = GetCheckedBottom(0, 1);
float bottom2Y = GetCheckedBottom(0, 2);
float bottom3Y = GetCheckedBottom(0, 3);
float left1X = GetCheckedLeft(1);
float left2X = GetCheckedLeft(2);
float left3X = GetCheckedLeft(3);
float right1X = GetCheckedRight(1);
float right2X = GetCheckedRight(2);
float right3X = GetCheckedRight(3);
float top1Y = GetCheckedTop(1);
float top2Y = GetCheckedTop(2);
float top3Y = GetCheckedTop(3);
float bottom1Y = GetCheckedBottom(1);
float bottom2Y = GetCheckedBottom(2);
float bottom3Y = GetCheckedBottom(3);
float3 c[25] = {
SampleInputCur(0), // c0

View file

@ -72,18 +72,18 @@ D2D_PS_ENTRY(main) {
// [ c20, c6, c7, c8, c17 ]
// [ c15, c12, c14 ]
// [ c13 ]
float left1X = GetCheckedLeft(0, 1);
float left2X = GetCheckedLeft(0, 2);
float left3X = GetCheckedLeft(0, 3);
float right1X = GetCheckedRight(0, 1);
float right2X = GetCheckedRight(0, 2);
float right3X = GetCheckedRight(0, 3);
float top1Y = GetCheckedTop(0, 1);
float top2Y = GetCheckedTop(0, 2);
float top3Y = GetCheckedTop(0, 3);
float bottom1Y = GetCheckedBottom(0, 1);
float bottom2Y = GetCheckedBottom(0, 2);
float bottom3Y = GetCheckedBottom(0, 3);
float left1X = GetCheckedLeft(1);
float left2X = GetCheckedLeft(2);
float left3X = GetCheckedLeft(3);
float right1X = GetCheckedRight(1);
float right2X = GetCheckedRight(2);
float right3X = GetCheckedRight(3);
float top1Y = GetCheckedTop(1);
float top2Y = GetCheckedTop(2);
float top3Y = GetCheckedTop(3);
float bottom1Y = GetCheckedBottom(1);
float bottom2Y = GetCheckedBottom(2);
float bottom3Y = GetCheckedBottom(3);
float4 c[25] = {
orig0, // c0

View file

@ -0,0 +1,40 @@
/*
* Anime4K-v3.1-Upscale(x2)+Deblur-CNN(M)-Kernel(X/Y)
*/
cbuffer constants : register(b0) {
int2 srcSize : packoffset(c0);
};
#define D2D_INPUT_COUNT 1
#define D2D_INPUT0_COMPLEX
#define MAGPIE_USE_SAMPLE_INPUT
#include "Anime4K.hlsli"
#define max9(a, b, c, d, e, f, g, h, i) max3(max4(a, b, c, d), max4(e, f, g, h), i)
#define min9(a, b, c, d, e, f, g, h, i) min3(min4(a, b, c, d), min4(e, f, g, h), i)
D2D_PS_ENTRY(main) {
InitMagpieSampleInput();
float left1X = GetCheckedLeft(1);
float right1X = GetCheckedRight(1);
float top1Y = GetCheckedTop(1);
float bottom1Y = GetCheckedBottom(1);
// [ a, b, c ]
// [ d, e, f ]
// [ g, h, i ]
float a = GetYOfYUV(SampleInputNoCheck(0, float2(left1X, top1Y)));
float b = GetYOfYUV(SampleInputNoCheck(0, float2(coord.x, top1Y)));
float c = GetYOfYUV(SampleInputNoCheck(0, float2(right1X, top1Y)));
float d = GetYOfYUV(SampleInputNoCheck(0, float2(left1X, coord.y)));
float e = GetYOfYUV(SampleInputCur(0));
float f = GetYOfYUV(SampleInputNoCheck(0, float2(right1X, coord.y)));
float g = GetYOfYUV(SampleInputNoCheck(0, float2(left1X, bottom1Y)));
float h = GetYOfYUV(SampleInputNoCheck(0, float2(coord.x, bottom1Y)));
float i = GetYOfYUV(SampleInputNoCheck(0, float2(right1X, bottom1Y)));
return float4(min9(a, b, c, d, e, f, g, h, i), max9(a, b, c, d, e, f, g, h, i), 0, 0);
}

View file

@ -1,27 +0,0 @@
#define D2D_INPUT_COUNT 1
#define D2D_INPUT0_COMPLEX
#include "Anime4K.hlsli"
/*
* Anime4K-v3.1-Upscale(x2)+Deblur-CNN(M)-Kernel(X)
*/
cbuffer constants : register(b0) {
int2 srcSize : packoffset(c0);
};
D2D_PS_ENTRY(main) {
float4 coord = D2DGetInputCoordinate(0);
float a = GetYOfYUV(
D2DSampleInput(0, float2(max(0, coord.x - coord.z), coord.y)).rgb
);
float b = GetYOfYUV(D2DSampleInput(0, coord.xy).rgb);
float c = GetYOfYUV(
D2DSampleInput(0, float2(min((srcSize.x - 1) * coord.z, coord.x + coord.z), coord.y)).rgb
);
return float4(min3(a, b, c), max3(a, b, c), 0, 0);
}

View file

@ -1,23 +0,0 @@
#define D2D_INPUT_COUNT 1
#define D2D_INPUT0_COMPLEX
#include "Anime4K.hlsli"
/*
* Anime4K-v3.1-Upscale(x2)+Deblur-CNN(M)-Kernel(X)
*/
cbuffer constants : register(b0) {
int2 srcSize : packoffset(c0);
};
D2D_PS_ENTRY(main) {
float4 coord = D2DGetInputCoordinate(0);
float2 a = D2DSampleInput(0, float2(coord.x, max(0, coord.y - coord.w))).xy;
float2 b = D2DSampleInput(0, coord.xy).xy;
float2 c = D2DSampleInput(0, float2(coord.x, min((srcSize.y - 1) * coord.w, coord.y + coord.w))).xy;
return float4(min3(a.x, b.x, c.x), max3(a.y, b.y, c.y), 0, 0);
}

View file

@ -21,12 +21,12 @@ public:
// | | | | | |
// | v-----v-----v---+-v-----v
// | |
// | +---v--+
// +--------------->+<-----+reduce|
// | +------+
// +--v---+
// |output|
// +--+---+
// +----------------+ +---v--+
// | | |reduce|
// | | +--+---+
// +-v----+ +--v---+ |
// |kernel+-------->output<-----+
// +------+ +--+---+
// |
// +-v-+
// |out|
@ -90,18 +90,9 @@ public:
}
hr = SimpleDrawTransform::Create(
pEffectContext,
&_deblurKernelXTransform,
MAGPIE_ANIME4K_DEBLUR_KERNEL_X_SHADER,
GUID_MAGPIE_ANIME4K_DEBLUR_KERNEL_X_SHADER
);
if (FAILED(hr)) {
return hr;
}
hr = SimpleDrawTransform::Create(
pEffectContext,
&_deblurKernelYTransform,
MAGPIE_ANIME4K_DEBLUR_KERNEL_Y_SHADER,
GUID_MAGPIE_ANIME4K_DEBLUR_KERNEL_Y_SHADER
&_deblurKernelTransform,
MAGPIE_ANIME4K_DEBLUR_KERNEL_SHADER,
GUID_MAGPIE_ANIME4K_DEBLUR_KERNEL_SHADER
);
if (FAILED(hr)) {
return hr;
@ -139,11 +130,7 @@ public:
if (FAILED(hr)) {
return hr;
}
hr = pTransformGraph->AddNode(_deblurKernelXTransform.Get());
if (FAILED(hr)) {
return hr;
}
hr = pTransformGraph->AddNode(_deblurKernelYTransform.Get());
hr = pTransformGraph->AddNode(_deblurKernelTransform.Get());
if (FAILED(hr)) {
return hr;
}
@ -198,11 +185,7 @@ public:
return hr;
}
hr = pTransformGraph->ConnectToEffectInput(0, _deblurKernelXTransform.Get(), 0);
if (FAILED(hr)) {
return hr;
}
hr = pTransformGraph->ConnectNode(_deblurKernelXTransform.Get(), _deblurKernelYTransform.Get(), 0);
hr = pTransformGraph->ConnectToEffectInput(0, _deblurKernelTransform.Get(), 0);
if (FAILED(hr)) {
return hr;
}
@ -215,7 +198,7 @@ public:
if (FAILED(hr)) {
return hr;
}
hr = pTransformGraph->ConnectNode(_deblurKernelYTransform.Get(), _outputTransform.Get(), 2);
hr = pTransformGraph->ConnectNode(_deblurKernelTransform.Get(), _outputTransform.Get(), 2);
if (FAILED(hr)) {
return hr;
}
@ -269,7 +252,6 @@ private:
ComPtr<SimpleDrawTransform> _conv4x3x3x8Transform4 = nullptr;
ComPtr<SimpleDrawTransform> _conv4x3x3x8Transform5 = nullptr;
ComPtr<Anime4KUpscaleConvReduceTransform> _convReduceTransform = nullptr;
ComPtr<SimpleDrawTransform> _deblurKernelXTransform = nullptr;
ComPtr<SimpleDrawTransform> _deblurKernelYTransform = nullptr;
ComPtr<SimpleDrawTransform> _deblurKernelTransform = nullptr;
ComPtr<Anime4KUpscaleDeblurOutputTransform> _outputTransform = nullptr;
};

View file

@ -1,3 +1,10 @@
cbuffer constants : register(b0) {
int2 srcSize : packoffset(c0);
};
#define D2D_INPUT_COUNT 3
#define D2D_INPUT0_COMPLEX
#define D2D_INPUT1_COMPLEX
@ -11,11 +18,6 @@
#define NOISE_THRESHOLD 0.001 //Value where curve stops, used to not sharpen noise. Only de-blur values that fall above this threshold.
cbuffer constants : register(b0) {
int2 srcSize : packoffset(c0);
};
D2D_PS_ENTRY(main) {
float4 coord = D2DGetInputCoordinate(0);
float2 srcPos = coord.xy / 2;
@ -23,7 +25,8 @@ D2D_PS_ENTRY(main) {
float2 f = frac(round(coord.xy / coord.zw) / 2);
int2 i = f * 2;
float c0 = Uncompress(D2DSampleInput(1, srcPos + (float2(0.5, 0.5) - f) * coord.zw))[i.y * 2 + i.x];
if (c0 < 0.001) {
if (c0 < 0.0001) {
// Ïû³ýÔëÉù
return D2DSampleInput(0, srcPos);
}

View file

@ -3,8 +3,10 @@
#include "AdaptiveSharpenEffect.h"
#include "Anime4KUpscaleEffect.h"
#include "Anime4KUpscaleDeblurEffect.h"
#include "ScaleEffect.h"
#include "Jinc2ScaleEffect.h"
#include "MitchellNetravaliScaleEffect.h"
#include "json.hpp"
#include <unordered_set>
class EffectManager {
public:
@ -14,13 +16,12 @@ public:
const std::wstring_view& effectsJson,
const SIZE &srcSize,
const SIZE &maxSize
): _destSize(srcSize), _maxSize(maxSize), _d2dFactory(d2dFactory), _d2dDC(d2dDC) {
): _maxSize(maxSize), _d2dFactory(d2dFactory), _d2dDC(d2dDC) {
assert(srcSize.cx > 0 && srcSize.cy > 0);
assert(maxSize.cx > 0 && maxSize.cy > 0);
assert(d2dFactory != nullptr && d2dDC != nullptr);
_RegisterEffects();
_CreateSourceEffect();
_CreateSourceEffect(srcSize);
_ReadEffectsJson(effectsJson);
}
@ -39,7 +40,7 @@ public:
}
private:
void _CreateSourceEffect() {
void _CreateSourceEffect(const SIZE& srcSize) {
// 创建 Source effect
Debug::ThrowIfFailed(
_d2dDC->CreateEffect(CLSID_D2D1BitmapSource, &_d2dSourceEffect),
@ -48,13 +49,8 @@ private:
// 初始时输出为 Source effect
_outputEffect = _d2dSourceEffect;
}
void _RegisterEffects() const {
AdaptiveSharpenEffect::Register(_d2dFactory.Get());
Anime4KUpscaleEffect::Register(_d2dFactory.Get());
Anime4KUpscaleDeblurEffect::Register(_d2dFactory.Get());
ScaleEffect::Register(_d2dFactory.Get());
_SetDestSize(srcSize);
}
void _ReadEffectsJson(const std::wstring_view& effectsJson) {
@ -70,17 +66,21 @@ private:
const auto& subType = effect.value("type", "");
if (subType == "anime4K") {
//_AddAnime4KEffect();
_AddAnime4KEffect();
} else if (subType == "anime4KxDeblur") {
_AddAnime4KxDeblurEffect();
} else if (subType == "jinc2") {
_AddJinc2ScaleEffect(effect);
} else if (subType == "mitchell") {
_AddMitchellNetravaliScaleEffect(effect);
}
} else if (effectType == "sharpen") {
const auto& subType = effect.value("type", "");
if (subType == "adaptive") {
_AddAdaptiveSharpenEffect(effect);
_AddBuiltInSharpenEffect();
} else if (subType == "builtIn") {
_AddBuiltInSharpenEffect(effect);
}
} else {
Debug::ThrowIfFalse(false, L"json 格式错误");
@ -90,6 +90,11 @@ private:
}
void _AddAdaptiveSharpenEffect(const nlohmann::json& props) {
_CheckAndRegisterEffect(
CLSID_MAGPIE_ADAPTIVE_SHARPEN_EFFECT,
&AdaptiveSharpenEffect::Register
);
ComPtr<ID2D1Effect> adaptiveSharpenEffect = nullptr;
Debug::ThrowIfFailed(
_d2dDC->CreateEffect(CLSID_MAGPIE_ADAPTIVE_SHARPEN_EFFECT, &adaptiveSharpenEffect),
@ -97,7 +102,7 @@ private:
);
// strength 属性
const auto& it = props.find("strength");
auto it = props.find("strength");
if (it != props.end()) {
const auto& value = *it;
Debug::ThrowIfFalse(value.is_number(), L"非法的 strength 属性值");
@ -115,99 +120,166 @@ private:
}
// 替换 output effect
adaptiveSharpenEffect->SetInputEffect(0, _outputEffect.Get());
_outputEffect = adaptiveSharpenEffect;
_PushAsOutputEffect(adaptiveSharpenEffect);
}
void _AddBuiltInSharpenEffect() {
void _AddBuiltInSharpenEffect(const nlohmann::json& props) {
ComPtr<ID2D1Effect> d2dSharpenEffect = nullptr;
Debug::ThrowIfFailed(
_d2dDC->CreateEffect(CLSID_D2D1Sharpen, &d2dSharpenEffect),
L"创建 sharpen effect 失败"
);
d2dSharpenEffect->SetValue(D2D1_SHARPEN_PROP_SHARPNESS, 6.0f);
d2dSharpenEffect->SetValue(D2D1_SHARPEN_PROP_THRESHOLD, 0.8f);
// sharpness 属性
auto it = props.find("strength");
if (it != props.end()) {
const auto& value = *it;
Debug::ThrowIfFalse(value.is_number(), L"非法的 sharpness 属性值");
float sharpness = value.get<float>();
Debug::ThrowIfFalse(
sharpness >= 0 && sharpness <= 10,
L"非法的 strength 属性值"
);
Debug::ThrowIfFailed(
d2dSharpenEffect->SetValue(D2D1_SHARPEN_PROP_SHARPNESS, sharpness),
L"设置 strength 属性失败"
);
}
// threshold 属性
it = props.find("threshold");
if (it != props.end()) {
const auto& value = *it;
Debug::ThrowIfFalse(value.is_number(), L"非法的 threshold 属性值");
float threshold = value.get<float>();
Debug::ThrowIfFalse(
threshold >= 0 && threshold <= 1,
L"非法的 threshold 属性值"
);
Debug::ThrowIfFailed(
d2dSharpenEffect->SetValue(D2D1_SHARPEN_PROP_THRESHOLD, threshold),
L"设置 threshold 属性失败"
);
}
d2dSharpenEffect->SetValue(D2D1_SHARPEN_PROP_SHARPNESS, 3.0f);
d2dSharpenEffect->SetValue(D2D1_SHARPEN_PROP_THRESHOLD, 0.5f);
// 替换 output effect
d2dSharpenEffect->SetInputEffect(0, _outputEffect.Get());
_outputEffect = d2dSharpenEffect;
_PushAsOutputEffect(d2dSharpenEffect);
}
void _AddAnime4KEffect() {
_CheckAndRegisterEffect(
CLSID_MAGIPE_ANIME4K_UPSCALE_EFFECT,
&Anime4KUpscaleEffect::Register
);
ComPtr<ID2D1Effect> anime4KEffect = nullptr;
Debug::ThrowIfFailed(
_d2dDC->CreateEffect(CLSID_MAGIPE_ANIME4K_UPSCALE_EFFECT, &anime4KEffect),
L"创建 Anime4K Effect 失败"
);
// 替换 output effect
anime4KEffect->SetInputEffect(0, _outputEffect.Get());
_outputEffect = anime4KEffect;
// 输出图像的长和宽变为 2 倍
_destSize.cx *= 2;
_destSize.cy *= 2;
_SetDestSize(SIZE{ _destSize.cx * 2, _destSize.cy * 2 });
_PushAsOutputEffect(anime4KEffect);
}
void _AddAnime4KxDeblurEffect() {
_CheckAndRegisterEffect(
CLSID_MAGIPE_ANIME4K_UPSCALE_DEBLUR_EFFECT,
&Anime4KUpscaleDeblurEffect::Register
);
ComPtr<ID2D1Effect> anime4KxDeblurEffect = nullptr;
Debug::ThrowIfFailed(
_d2dDC->CreateEffect(CLSID_MAGIPE_ANIME4K_UPSCALE_DEBLUR_EFFECT, &anime4KxDeblurEffect),
L"创建 Anime4K Effect 失败"
);
// 替换 output effect
anime4KxDeblurEffect->SetInputEffect(0, _outputEffect.Get());
_outputEffect = anime4KxDeblurEffect;
// 输出图像的长和宽变为 2 倍
_destSize.cx *= 2;
_destSize.cy *= 2;
_SetDestSize(SIZE{ _destSize.cx * 2, _destSize.cy * 2 });
_PushAsOutputEffect(anime4KxDeblurEffect);
}
void _AddJinc2ScaleEffect(const nlohmann::json& props) {
_CheckAndRegisterEffect(
CLSID_MAGPIE_JINC2_SCALE_EFFECT,
&Jinc2ScaleEffect::Register
);
ComPtr<ID2D1Effect> jinc2Effect = nullptr;
Debug::ThrowIfFailed(
_d2dDC->CreateEffect(CLSID_MAGPIE_SCALE_EFFECT, &jinc2Effect),
_d2dDC->CreateEffect(CLSID_MAGPIE_JINC2_SCALE_EFFECT, &jinc2Effect),
L"创建 Anime4K Effect 失败"
);
// scale 属性
auto it = props.find("scale");
if (it != props.end()) {
const auto& scale = _ReadScaleProp(*it);
Debug::ThrowIfFailed(
jinc2Effect->SetValue(Jinc2ScaleEffect::PROP_SCALE, scale),
L"设置 scale 属性失败"
);
// 存在 scale 则输出图像尺寸改变
_SetDestSize(SIZE{ lroundf(_destSize.cx * scale.x), lroundf(_destSize.cy * scale.y) });
}
// 替换 output effect
_PushAsOutputEffect(jinc2Effect);
}
void _AddMitchellNetravaliScaleEffect(const nlohmann::json& props) {
_CheckAndRegisterEffect(
CLSID_MAGPIE_MITCHELL_NETRAVALI_SCALE_EFFECT,
&MitchellNetravaliScaleEffect::Register
);
ComPtr<ID2D1Effect> effect = nullptr;
Debug::ThrowIfFailed(
_d2dDC->CreateEffect(CLSID_MAGPIE_MITCHELL_NETRAVALI_SCALE_EFFECT, &effect),
L"创建 Mitchell-Netraval Scale Effect 失败"
);
// scale 属性
auto it = props.find("scale");
if (it != props.end()) {
const auto& scaleValues = *it;
Debug::ThrowIfFalse(
scaleValues.is_array() && scaleValues.size() == 2
&& scaleValues[0].is_number() && scaleValues[1].is_number(),
L"读取 scale 属性失败"
);
const auto& scale = _ReadScaleProp(*it);
D2D1_VECTOR_2F scale{ scaleValues[0], scaleValues[1]};
Debug::ThrowIfFalse(
scale.x >= 0 && scale.y >= 0,
L"scale 属性的值非法"
);
if (scale.x == 0 || scale.y == 0) {
// 输出图像充满屏幕
scale.x = min((FLOAT)_maxSize.cx / _destSize.cx, (FLOAT)_maxSize.cy / _destSize.cy);
scale.y = scale.x;
}
Debug::ThrowIfFailed(
jinc2Effect->SetValue(ScaleEffect::PROP_SCALE, scale),
effect->SetValue(MitchellNetravaliScaleEffect::PROP_SCALE, scale),
L"设置 scale 属性失败"
);
// 存在 scale 则输出图像尺寸改变
_destSize.cx = lroundf(_destSize.cx * scale.x);
_destSize.cy = lroundf(_destSize.cy * scale.y);
_SetDestSize(SIZE{ lroundf(_destSize.cx * scale.x), lroundf(_destSize.cy * scale.y) });
}
// useSharperVersion 属性
it = props.find("useSharperVersion");
if (it != props.end()) {
const auto& val = *it;
Debug::ThrowIfFalse(val.is_boolean(), L"非法的 useSharperVersion 属性值");
Debug::ThrowIfFailed(
effect->SetValue(MitchellNetravaliScaleEffect::PROP_USE_SHARPER_VERSION, (BOOL)val.get<bool>()),
L"设置 useSharperVersion 属性失败"
);
}
// 替换 output effect
jinc2Effect->SetInputEffect(0, _outputEffect.Get());
_outputEffect = jinc2Effect;
_PushAsOutputEffect(effect);
}
void _AddCubicScaleEffect() {
@ -223,8 +295,61 @@ private:
float scale = min((FLOAT)_maxSize.cx / _destSize.cx, (FLOAT)_maxSize.cy / _destSize.cy);
cubicEffect->SetValue(D2D1_SCALE_PROP_SCALE, D2D1_VECTOR_2F{ scale, scale });
cubicEffect->SetInputEffect(0, _outputEffect.Get());
_outputEffect = cubicEffect;
_PushAsOutputEffect(cubicEffect);
}
D2D1_VECTOR_2F _ReadScaleProp(const nlohmann::json& prop) {
Debug::ThrowIfFalse(
prop.is_array() && prop.size() == 2
&& prop[0].is_number() && prop[1].is_number(),
L"读取 scale 属性失败"
);
D2D1_VECTOR_2F scale{ prop[0], prop[1] };
Debug::ThrowIfFalse(
scale.x >= 0 && scale.y >= 0,
L"scale 属性的值非法"
);
if (scale.x == 0 || scale.y == 0) {
// 输出图像充满屏幕
scale.x = min((FLOAT)_maxSize.cx / _destSize.cx, (FLOAT)_maxSize.cy / _destSize.cy);
scale.y = scale.x;
}
return scale;
}
// 将 effect 添加到 effect 链作为输出
void _PushAsOutputEffect(ComPtr<ID2D1Effect> effect) {
effect->SetInputEffect(0, _outputEffect.Get());
_outputEffect = effect;
}
// 设置 destSize 的同时增大 tile 的大小以容纳图像
void _SetDestSize(SIZE value) {
if (value.cx > _destSize.cx || value.cy > _destSize.cy) {
// 需要更大的 tile
rc.tileSize.width = max(value.cx, _destSize.cx);
rc.tileSize.height = max(value.cy, _destSize.cy);
_d2dDC->SetRenderingControls(rc);
}
_destSize = value;
}
// 必要时注册 effect
void _CheckAndRegisterEffect(const GUID& effectID, std::function<HRESULT(ID2D1Factory1*)> registerFunc) {
if (_registeredEffects.find(effectID) == _registeredEffects.end()) {
// 未注册
Debug::ThrowIfFailed(
registerFunc(_d2dFactory.Get()),
L"注册 Effect 失败"
);
_registeredEffects.insert(effectID);
}
}
ComPtr<ID2D1Factory1> _d2dFactory;
@ -237,4 +362,16 @@ private:
SIZE _destSize{};
// 全屏窗口尺寸
SIZE _maxSize;
// 存储已注册的 effect 的 GUID
std::unordered_set<GUID> _registeredEffects;
// 用于确定 tile 的大小
SIZE _maxDestSize{};
D2D1_RENDERING_CONTROLS rc{
D2D1_BUFFER_PRECISION_32BPC_FLOAT,
1024,
1024
};
};

View file

@ -31,7 +31,7 @@ public:
// 将输出图像显示在窗口中央
_d2dDC->BeginDraw();
_d2dDC->Clear(D2D1_COLOR_F{ 0,1,0,1 });
_d2dDC->Clear();
_d2dDC->DrawImage(
outputImg.Get(),
D2D1_POINT_2F{
@ -187,13 +187,6 @@ private:
// Now we can set the Direct2D render target.
_d2dDC->SetTarget(d2dTargetBitmap.Get());
_d2dDC->SetUnitMode(D2D1_UNIT_MODE_PIXELS);
D2D1_RENDERING_CONTROLS rc{};
_d2dDC->GetRenderingControls(&rc);
rc.bufferPrecision = D2D1_BUFFER_PRECISION_32BPC_FLOAT;
rc.tileSize.width = _hostWndClientSize.cx * 2;
rc.tileSize.height = _hostWndClientSize.cy * 2;
_d2dDC->SetRenderingControls(rc);
}

View file

@ -50,13 +50,9 @@ DEFINE_GUID(GUID_MAGPIE_ANIME4K_UPSCALE_OUTPUT_SHADER,
0x97dbf32e, 0x65b, 0x417a, 0x9c, 0x2d, 0x87, 0x75, 0x3f, 0xb8, 0xeb, 0xbe);
// {C25507A7-845B-4DD2-BEA7-32E64EFBE5C5}
DEFINE_GUID(GUID_MAGPIE_ANIME4K_DEBLUR_KERNEL_X_SHADER,
DEFINE_GUID(GUID_MAGPIE_ANIME4K_DEBLUR_KERNEL_SHADER,
0xc25507a7, 0x845b, 0x4dd2, 0xbe, 0xa7, 0x32, 0xe6, 0x4e, 0xfb, 0xe5, 0xc5);
// {4ED918AA-1569-40A0-80BF-D7E1A0C51026}
DEFINE_GUID(GUID_MAGPIE_ANIME4K_DEBLUR_KERNEL_Y_SHADER,
0x4ed918aa, 0x1569, 0x40a0, 0x80, 0xbf, 0xd7, 0xe1, 0xa0, 0xc5, 0x10, 0x26);
// {898ACF79-3FEE-471D-8565-EA5E45A0C887}
DEFINE_GUID(GUID_MAGPIE_ANIME4K_UPSCALE_DEBLUR_OUTPUT_SHADER,
0x898acf79, 0x3fee, 0x471d, 0x85, 0x65, 0xea, 0x5e, 0x45, 0xa0, 0xc8, 0x87);
@ -84,5 +80,9 @@ DEFINE_GUID(CLSID_MAGIPE_ANIME4K_UPSCALE_DEBLUR_EFFECT,
// {1F1D758E-5EEA-4FE7-BDC0-FC9E98702108}
DEFINE_GUID(CLSID_MAGPIE_SCALE_EFFECT,
DEFINE_GUID(CLSID_MAGPIE_JINC2_SCALE_EFFECT,
0x1f1d758e, 0x5eea, 0x4fe7, 0xbd, 0xc0, 0xfc, 0x9e, 0x98, 0x70, 0x21, 0x8);
// {DED5C76B-9308-4122-AC81-578C6542B7BD}
DEFINE_GUID(CLSID_MAGPIE_MITCHELL_NETRAVALI_SCALE_EFFECT,
0xded5c76b, 0x9308, 0x4122, 0xac, 0x81, 0x57, 0x8c, 0x65, 0x42, 0xb7, 0xbd);

View file

@ -6,18 +6,21 @@
#include <d2d1effecthelpers.h>
class ScaleEffect : public EffectBase {
class Jinc2ScaleEffect : public EffectBase {
public:
IFACEMETHODIMP Initialize(
_In_ ID2D1EffectContext* pEffectContext,
_In_ ID2D1TransformGraph* pTransformGraph
) {
HRESULT hr = SimpleScaleTransform::Create(pEffectContext, &_jinc2SharpTransform, MAGPIE_MITCHELL_NETRAVALI_SCALE_SHADER, GUID_MAGPIE_MITCHELL_NETRAVALI_SCALE_SHADER);
HRESULT hr = SimpleScaleTransform::Create(
pEffectContext, &_transform,
MAGPIE_JINC2_SCALE_SHADER,
GUID_MAGPIE_JINC2_SCALE_SHADER);
if (FAILED(hr)) {
return hr;
}
hr = pTransformGraph->SetSingleTransformNode(_jinc2SharpTransform.Get());
hr = pTransformGraph->SetSingleTransformNode(_transform.Get());
if (FAILED(hr)) {
return hr;
}
@ -30,12 +33,12 @@ public:
return E_INVALIDARG;
}
_jinc2SharpTransform->SetScale(value);
_transform->SetScale(value);
return S_OK;
}
D2D_VECTOR_2F GetScale() const {
return _jinc2SharpTransform->GetScale();
return _transform->GetScale();
}
enum PROPS {
@ -48,7 +51,7 @@ public:
D2D1_VALUE_TYPE_BINDING(L"Scale", &SetScale, &GetScale),
};
HRESULT hr = pFactory->RegisterEffectFromString(CLSID_MAGPIE_SCALE_EFFECT, XML(
HRESULT hr = pFactory->RegisterEffectFromString(CLSID_MAGPIE_JINC2_SCALE_EFFECT, XML(
<?xml version='1.0'?>
<Effect>
<!--System Properties-->
@ -70,7 +73,7 @@ public:
}
static HRESULT CALLBACK CreateEffect(_Outptr_ IUnknown** ppEffectImpl) {
*ppEffectImpl = static_cast<ID2D1EffectImpl*>(new ScaleEffect());
*ppEffectImpl = static_cast<ID2D1EffectImpl*>(new Jinc2ScaleEffect());
if (*ppEffectImpl == nullptr) {
return E_OUTOFMEMORY;
@ -80,7 +83,7 @@ public:
}
private:
ScaleEffect() {}
Jinc2ScaleEffect() {}
ComPtr<SimpleScaleTransform> _jinc2SharpTransform = nullptr;
ComPtr<SimpleScaleTransform> _transform = nullptr;
};

View file

@ -203,7 +203,9 @@
<ClInclude Include="Jinc2Transform.h" />
<ClInclude Include="json.hpp" />
<ClInclude Include="MagWindow.h" />
<ClInclude Include="ScaleEffect.h" />
<ClInclude Include="MitchellNetravaliScaleEffect.h" />
<ClInclude Include="MitchellNetravaliScaleTransform.h" />
<ClInclude Include="Jinc2ScaleEffect.h" />
<ClInclude Include="KeyBoardHook.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="Shaders.h" />
@ -243,6 +245,7 @@
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Pixel</ShaderType>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="Anime4KDeblurKernelShader.hlsl" />
<FxCompile Include="Anime4KUpscaleConv4x3x3x1Shader.hlsl">
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Pixel</ShaderType>
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Pixel</ShaderType>
@ -308,19 +311,6 @@
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Pixel</ShaderType>
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Pixel</ShaderType>
</FxCompile>
<FxCompile Include="Anime4KDeblurKernelXShader.hlsl">
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Pixel</ShaderType>
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Pixel</ShaderType>
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Pixel</ShaderType>
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Pixel</ShaderType>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
</FxCompile>
<FxCompile Include="Anime4KDeblurKernelYShader.hlsl">
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Pixel</ShaderType>
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Pixel</ShaderType>
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Pixel</ShaderType>
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Pixel</ShaderType>
</FxCompile>
<FxCompile Include="Jinc2ScaleShader.hlsl">
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Pixel</ShaderType>
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Pixel</ShaderType>

View file

@ -7,12 +7,6 @@
<FxCompile Include="AdaptiveSharpenPass2Shader.hlsl">
<Filter>着色器</Filter>
</FxCompile>
<FxCompile Include="Anime4KDeblurKernelXShader.hlsl">
<Filter>着色器</Filter>
</FxCompile>
<FxCompile Include="Anime4KDeblurKernelYShader.hlsl">
<Filter>着色器</Filter>
</FxCompile>
<FxCompile Include="Anime4KUpscaleConv4x3x3x1Shader.hlsl">
<Filter>着色器</Filter>
</FxCompile>
@ -49,6 +43,9 @@
<FxCompile Include="MitchellNetravaliScaleShader.hlsl">
<Filter>着色器</Filter>
</FxCompile>
<FxCompile Include="Anime4KDeblurKernelShader.hlsl">
<Filter>着色器</Filter>
</FxCompile>
</ItemGroup>
<ItemGroup>
<ClCompile Include="GUIDs.cpp">
@ -140,10 +137,16 @@
<ClInclude Include="SimpleScaleTransform.h">
<Filter>头文件\Effect</Filter>
</ClInclude>
<ClInclude Include="ScaleEffect.h">
<ClInclude Include="AdaptiveSharpenPass2Transform.h">
<Filter>头文件\Effect</Filter>
</ClInclude>
<ClInclude Include="AdaptiveSharpenPass2Transform.h">
<ClInclude Include="MitchellNetravaliScaleTransform.h">
<Filter>头文件\Effect</Filter>
</ClInclude>
<ClInclude Include="MitchellNetravaliScaleEffect.h">
<Filter>头文件\Effect</Filter>
</ClInclude>
<ClInclude Include="Jinc2ScaleEffect.h">
<Filter>头文件\Effect</Filter>
</ClInclude>
</ItemGroup>

View file

@ -39,7 +39,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
[
{
"effect": "scale",
"type": "anime4K"
"type": "anime4KxDeblur"
},
{
"effect": "scale",
@ -49,7 +49,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
{
"effect": "sharpen",
"type": "adaptive",
"strength": 0.6
"strength": 0.3
}
])"));
} else {

View file

@ -0,0 +1,101 @@
#pragma once
#include "pch.h"
#include "GUIDs.h"
#include "MitchellNetravaliScaleTransform.h"
#include "EffectBase.h"
#include <d2d1effecthelpers.h>
class MitchellNetravaliScaleEffect : public EffectBase {
public:
IFACEMETHODIMP Initialize(
_In_ ID2D1EffectContext* pEffectContext,
_In_ ID2D1TransformGraph* pTransformGraph
) {
HRESULT hr = MitchellNetravaliScaleTransform::Create(pEffectContext, &_transform);
if (FAILED(hr)) {
return hr;
}
hr = pTransformGraph->SetSingleTransformNode(_transform.Get());
if (FAILED(hr)) {
return hr;
}
return S_OK;
}
HRESULT SetScale(D2D_VECTOR_2F value) {
if (value.x <= 0 || value.y <= 0) {
return E_INVALIDARG;
}
_transform->SetScale(value);
return S_OK;
}
D2D_VECTOR_2F GetScale() const {
return _transform->GetScale();
}
HRESULT SetUseSharperVersion(BOOL value) {
_transform->SetUseSharpenVersion((bool)value);
return S_OK;
}
BOOL IsUseSharperVersion() const {
return (BOOL)_transform->IsUseSharpenVersion();
}
enum PROPS {
PROP_SCALE = 0,
PROP_USE_SHARPER_VERSION = 0
};
static HRESULT Register(_In_ ID2D1Factory1* pFactory) {
const D2D1_PROPERTY_BINDING bindings[] =
{
D2D1_VALUE_TYPE_BINDING(L"Scale", &SetScale, &GetScale),
D2D1_VALUE_TYPE_BINDING(L"UseSharperVersion", &SetUseSharperVersion, &IsUseSharperVersion)
};
HRESULT hr = pFactory->RegisterEffectFromString(CLSID_MAGPIE_MITCHELL_NETRAVALI_SCALE_EFFECT, XML(
<?xml version='1.0'?>
<Effect>
<!--System Properties-->
<Property name='DisplayName' type='string' value='Mitchell-Netravali Scale' />
<Property name='Author' type='string' value='Xu Liu' />
<Property name='Category' type='string' value='Scale' />
<Property name='Description' type='string' value='Mitchell-Netravali scale algorithm' />
<Inputs>
<Input name='Source' />
</Inputs>
<Property name='Scale' type='vector2'>
<Property name='DisplayName' type='string' value='Scale' />
<Property name='Default' type='vector2' value='(1,1)' />
</Property>
<Property name='UseSharperVersion' type='bool'>
<Property name='DisplayName' type='string' value='Use Sharper Version' />
<Property name='Default' type='bool' value='false' />
</Property>
</Effect>
), bindings, ARRAYSIZE(bindings), CreateEffect);
return hr;
}
static HRESULT CALLBACK CreateEffect(_Outptr_ IUnknown** ppEffectImpl) {
*ppEffectImpl = static_cast<ID2D1EffectImpl*>(new MitchellNetravaliScaleEffect());
if (*ppEffectImpl == nullptr) {
return E_OUTOFMEMORY;
}
return S_OK;
}
private:
MitchellNetravaliScaleEffect() {}
ComPtr<MitchellNetravaliScaleTransform> _transform = nullptr;
};

View file

@ -5,19 +5,25 @@
cbuffer constants : register(b0) {
int2 srcSize : packoffset(c0.x);
int2 destSize : packoffset(c0.z);
int useSharperVersion : packoffset(c1.x);
};
float weight(float x) {
float ax = abs(x);
// Mitchel-Netravali coefficients.
// Best psychovisual result.
const float B = 1.0 / 3.0;
const float C = 1.0 / 3.0;
// Sharper version.
// May look better in some cases.
//const float B = 0.0;
//const float C = 0.75;
float B = 0.0;
float C = 0.0;
if (useSharperVersion == 0) {
// Mitchel-Netravali coefficients.
// Best psychovisual result.
B = 1.0 / 3.0;
C = 1.0 / 3.0;
} else {
// Sharper version.
// May look better in some cases.
B = 0.0;
C = 0.75;
}
if (ax < 1.0) {
return

View file

@ -0,0 +1,59 @@
#pragma once
#include "pch.h"
#include "GUIDs.h"
#include "SimpleScaleTransform.h"
// Mitchell-Netravali 插值算法,一种双三次插值,可以获得平滑的边缘
// 可选是否使用更锐利的版本,默认为否
// (经测试两种版本几乎没有区别)
class MitchellNetravaliScaleTransform : public SimpleScaleTransform {
private:
MitchellNetravaliScaleTransform() : SimpleScaleTransform(GUID_MAGPIE_MITCHELL_NETRAVALI_SCALE_SHADER) {}
public:
static HRESULT Create(_In_ ID2D1EffectContext* d2dEC, _Outptr_ MitchellNetravaliScaleTransform** ppOutput) {
if (!ppOutput) {
return E_INVALIDARG;
}
HRESULT hr = DrawTransformBase::LoadShader(
d2dEC,
MAGPIE_MITCHELL_NETRAVALI_SCALE_SHADER,
GUID_MAGPIE_MITCHELL_NETRAVALI_SCALE_SHADER
);
if (FAILED(hr)) {
return hr;
}
*ppOutput = new MitchellNetravaliScaleTransform();
return hr;
}
void SetUseSharpenVersion(bool value) {
_useSharperVersion = value;
}
bool IsUseSharpenVersion() {
return _useSharperVersion;
}
protected:
void _SetShaderContantBuffer(const SIZE& srcSize, const SIZE& destSize) override {
struct {
INT32 srcWidth;
INT32 srcHeight;
INT32 destWidth;
INT32 destHeight;
INT32 useSharperVersion;
} shaderConstants{
srcSize.cx,
srcSize.cy,
destSize.cx,
destSize.cy,
(INT32)_useSharperVersion
};
_drawInfo->SetPixelShaderConstantBuffer((BYTE*)&shaderConstants, sizeof(shaderConstants));
}
private:
bool _useSharperVersion = false;
};

View file

@ -19,8 +19,7 @@ constexpr auto MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER4 = L"shaders/Anime4KUp
constexpr auto MAGPIE_ANIME4K_UPSCALE_CONV_4x3x3x8_SHADER5 = L"shaders/Anime4KUpscaleConv4x3x3x8Shader5.cso";
constexpr auto MAGPIE_ANIME4K_UPSCALE_CONV_REDUCE_SHADER = L"shaders/Anime4KUpscaleConvReduceShader.cso";
constexpr auto MAGPIE_ANIME4K_UPSCALE_OUTPUT_SHADER = L"shaders/Anime4KUpscaleOutputShader.cso";
constexpr auto MAGPIE_ANIME4K_DEBLUR_KERNEL_X_SHADER = L"shaders/Anime4KDeblurKernelXShader.cso";
constexpr auto MAGPIE_ANIME4K_DEBLUR_KERNEL_Y_SHADER = L"shaders/Anime4KDeblurKernelYShader.cso";
constexpr auto MAGPIE_ANIME4K_DEBLUR_KERNEL_SHADER = L"shaders/Anime4KDeblurKernelShader.cso";
constexpr auto MAGPIE_ANIME4K_UPSCALE_DEBLUR_OUTPUT_SHADER = L"shaders/Anime4KUpscaleDeblurOutputShader.cso";
/*

View file

@ -1,6 +1,7 @@
#pragma once
#include "pch.h"
#include "Shlwapi.h"
#include <utility>
class Utils {
public:
@ -141,4 +142,22 @@ public:
Debug::ThrowIfFailed(bmpEncoder->Commit(), L"");
stream->Commit(STGC_DEFAULT);
}
};
};
namespace std {
// std::hash µÄ GUID ÌØ»¯
template<>
struct hash<GUID> {
size_t operator()(const GUID& value) const {
size_t result = hash<unsigned long>()(value.Data1);
result ^= hash<unsigned short>()(value.Data2) << 1;
result ^= hash<unsigned short>()(value.Data3) << 2;
for (int i = 0; i < 8; ++i) {
result ^= hash<unsigned short>()(value.Data4[i]) << i;
}
return result;
}
};
}

View file

@ -59,10 +59,10 @@ static float2 _maxCoord = 0;
// 限制坐标在边界内
// n 为 offset
#define GetCheckedLeft(index, n) _checkLeft(coord.x - n * coord.z)
#define GetCheckedRight(index, n) _checkRight(coord.x + n * coord.w)
#define GetCheckedTop(index, n) _checkTop(coord.y - n * coord.z)
#define GetCheckedBottom(index, n) _checkBottom(coord.y + n * coord.w)
#define GetCheckedLeft(n) _checkLeft(coord.x - n * coord.z)
#define GetCheckedRight(n) _checkRight(coord.x + n * coord.w)
#define GetCheckedTop(n) _checkTop(coord.y - n * coord.z)
#define GetCheckedBottom(n) _checkBottom(coord.y + n * coord.w)
// 需要 main 函数的开头调用