JincResize 代码重构(3)
JincResize avs 版迁移工作告一段落了,速度比之前快了 5-6 倍,在这个过程中学到了 C/C++ 结构、C++ 类和对象(特别是通过调用构造函数、公有函数实现抽象)、C++ 内存对齐与回收的知识,对堆栈有了初步的概念,同时对程序整体设计有了大致的概念。
当修改好 readme,发布了 JincResize-r7-RC1,再来回看 commit 历史,有些感动。在这一周的时间中,有些印象很深的改动,都忘记了是早期的几天就完成的;而同时在最初的几天,以现在的视角看,更多的是四处尝试,像在黑暗中一样,但最终是看到了光亮。
如果说早期的 coding 是崎岖但知道方向;那么中期之后,在我得到一份能看到结果、但带有问题的代码时,再往前走,就有些戏剧性了。虽然通过 Debug 找到一些改正的线索,但还不够,真要靠一些运气才走下去的。当时主要是为了在实际代码中应用一下类,其实不用类完全也可以实现当时预想的目的。在这个过程中学到基础知识且不提,在把计算 Lut(Look up table)的过程放到Lut类后,我突然意识到,把整个计算好的 Lut 表传给函数,(占用了过多内存),可能是导致编译好dll调用一次就崩溃的原因。这时我才明白 avs 版代码为什么要封装一个函数,Lut 表中的数据随用随调。虽然是仿写,但我自己能独立想到原因,这也是值得欣喜的。之后又经历了一两天的折腾,才明白怎么去正确地初始化变量。终于不崩溃了。
虽然还有一个 bug 未解决,但这两三天也为此花了不少时间,该放下就放下,先告一段落吧,这一周的工作已经收获很多了。
要学会发现和留住成就感,这是增加自信的一种方式。
内存管理
_aligned_malloc()
、_aligned_free()
是微软提出的动态申请、释放内存对齐函数。
1 |
|
Examples:
1 |
|
Windows 下的 gcc 编译器也可以接受上述函数,但 Linux 下的 gcc 编译器就不行了。
Linux 下的通用替代方法且不提,在 VapourSynth 下有专门的替代函数vs_aligned_malloc()
、vs_aligned_free()
,用法和前面的原始函数一样(虽然就是给 Linux 下的相应函数起个别名,但毕竟方便很多)。
从结构到类
这次迁移了 AviSynth 版 JincResize 中的 EWA(椭圆加权平均)重采样方法,关于算法本身的疑问先放一下(因为我还是觉得,AviSynth 版用的重采样也是通过圆划定范围、进行计算,而非椭圆——虽然直观时上觉得,干脆用椭圆的长轴长为半径画圆不是更精确吗?或许椭圆本身能减少不太必要的计算?),在实现算法时,设计了两个类,一个(辅助的)类用来存放重采样/反向映射用的圆/椭圆半径、指针移动相关的数据,另一个类用来存放传递过来的核心Lut表及其他辅助数据(包括前一个类)。
以上这些都是常规设计,但计算时的函数调用,让我感觉把结构改成类,或许看上去更 C++ 一点。
如果就是单独存一些数据,那似乎是没必要把结构写成类,但类似构造函数的初始化、计算,类似析构函数的内存释放都出来了,而且就单独写一个函数放在头文件里,可能写成类和成员函数看上去更舒服一点。
带着这种想法,我打算先把主 cpp 中计算 Lut 表的过程单独拿出来写成类(至于上面提到的 EWA 重采样过程,既然结构已经完善,先这样放着吧)。具体的过程,写几个私有变量、构造函数、Lut 计算函数、空的析构函数,再写一个销毁数组释放内存的函数,大概就 OK 了。
调用,用 new 方法新建一个 Lut 对象(实际操作为创建一个数组),将Lut计算函数作用于该对象(由于数组用new方法创建,所以用指针运算符->
访问成员函数),将 lut 数组定义为 Lut 类的公有变量,在主cpp里调用 lut 数组,传递给后续函数(commit 8fbcfdc
)。
不要一次都塞进来
在当时,代码存在一个问题,虽然能正常运行,但只能运行一次,每次关闭 vsedit 的预览窗口,再打开,就会崩溃。
我想到,现在把计算 Lut 表的过程封装/抽象成类,但本质上和之前没有区别,还是把整个 lut 数组传递给函数,这可能导致内存泄漏,可能是导致崩溃的原因。
同时,我发现我在写构造函数、创建lut数组时,没有确定数组长度,像下面这样。
1 |
|
于是从两个方向入手解决。
增加获取 Lut 数组的函数,这样改后应该是一次传递一个,而不是把每次都把整个数组传递过去(commit
70100f
)1
2
3
4float Lut::GetFactor(int index)
{
return (float)lut[index];
}给
lut_size
手动设定初始值(commit5fcf58
),虽然代码不好看,但毕竟解决问题了1
2
3
4
5class
{
private:
int lut_size = 1024;
};
Debug
基于 vsedit 自带的信息
最开始写好初版代码,编译好后,vsedit 居然打不开了。于是先打开 vsedit 再把 dll 丢进去,终于看到报错信息,原来是我把 python 接口的float
写成了foat
…orz…在编译时肯定看不出来问题,在调用时肯定出问题…
VS Debug
调试方法
在“调试”-“命令”里把 vsedit 的路径写进去,就可以用 VS 调试了。
一开始我傻傻地把 gcc 编译好 dll 丢进去,让 VS 调试…呃,肯定是加载不了啊。
崩溃问题
前面提到的lut_size
没有初始化,就是借助 VS Debug 发现的。
文字的 Bug
虽然用断点查看具体计算过程有些有趣,但对解决文字 Bug 没有实质性的帮助。这个问题先放一放吧。