WebAssembly入门课
于航
PayPal 高级软件工程师
新⼈⾸单¥55
1902 人已学习
课程目录
已完结 23 讲
0/4登录后,你可以任选4讲全文学习。
课前必读 (3讲)
开篇词 | 我们为什么要了解 WebAssembly?
免费
01 | 基础篇:学习此课程你需要了解哪些基础知识?
02 | 历史篇:为什么会有 WebAssembly 这样一门技术?
核心原理篇 (6讲)
03 | WebAssembly 是一门新的编程语言吗?
04 | WebAssembly 模块的基本组成结构到底有多简单?
05 | 二进制编码:WebAssembly 微观世界的基本数据规则是什么?
06 | WAT:如何让一个 WebAssembly 二进制模块的内容易于解读?
07 | WASI:你听说过 WebAssembly 操作系统接口吗?
08 | API:在 WebAssembly MVP 标准下你能做到哪些事?
应用篇 (6讲)
09 | WebAssembly 能够为 Web 前端框架赋能吗?
10 | 有哪些已经投入生产的 WebAssembly 真实案例?
11 | WebAssembly 在物联网、多媒体与云技术方面有哪些创新实践?
12 | 有哪些优秀的 WebAssembly 编译器与运行时?
13 | LLVM:如何将自定义的语言编译到 WebAssembly?
14 | 有哪些正在行进中的 WebAssembly Post-MVP 提案?
实战篇 (6讲)
15 | 如何实现一个 WebAssembly 在线多媒体处理应用(一)?
16 | 如何实现一个 WebAssembly 在线多媒体处理应用(二)?
17 | 如何实现一个 WebAssembly 在线多媒体处理应用(三)?
18 | 如何进行 Wasm 应用的调试与分析?
19 | 如何应用 WASI 及其相关生态?
20 | 总结与答疑
结束语 (1讲)
结束语 | WebAssembly,未来已来
测试题 (1讲)
期末测试|这些 WebAssembly 的问题,你都掌握了吗?
WebAssembly入门课
15
15
1.0x
00:00/00:00
登录|注册

17 | 如何实现一个 WebAssembly 在线多媒体处理应用(三)?

于航 2020-10-12
你好,我是于航。
在上一节课中,我们已经完成了本次实践项目的其中一个核心部分,也就是由 JavaScript 实现的滤镜函数。并且还同时完成了整个 Web 应用与用户的 UI 交互控制部分、视频图像的渲染和绘制逻辑,以及帧率计算逻辑及显示逻辑。
在这节课里,我们将一起来完成整个应用的另外一个核心部分,同时也是整个实践的主角。让我们来看看,相较于 JavaScript 滤镜函数,由 Wasm 实现的同版本滤镜函数会带来怎样的性能提升呢?

编写 C/C++ 函数源码

首先,为了能够得到对应 Wasm 字节码格式的函数实现,我们需要首先准备由 C/C++ 等高级语言编写的源代码,然后再通过 Emscripten 将其编译到 Wasm 格式。这部分代码的主要逻辑,与上一篇中的 JavaScript 版本滤镜函数其实现逻辑基本相同。代码如下所示:
// dip.cc
// 引入必要的头文件;
#include <emscripten.h>
#include <cmath>
// 宏常量定义,表示卷积核矩阵的高和宽;
#define KH 3
#define KW 3
// 声明两个数组,分别用于存放卷积核数据与每一帧对应的像素点数据;
char kernel[KH][KW];
unsigned char data[921600];
// 将被导出的函数,放置在 extern "C" 中防止 Name Mangling;
extern "C" {
// 获取卷积核数组的首地址;
EMSCRIPTEN_KEEPALIVE auto* cppGetkernelPtr() { return kernel; }
// 获取帧像素数组的首地址;
EMSCRIPTEN_KEEPALIVE auto* cppGetDataPtr() { return data; }
// 滤镜函数;
EMSCRIPTEN_KEEPALIVE void cppConvFilter(
int width,
int height,
int divisor) {
const int half = std::floor(KH / 2);
for (int y = half; y < height - half; ++y) {
for (int x = half; x < width - half; ++x) {
int px = (y * width + x) * 4;
int r = 0, g = 0, b = 0;
for (int cy = 0; cy < KH; ++cy) {
for (int cx = 0; cx < KW; ++cx) {
const int cpx = ((y + (cy - half)) * width + (x + (cx - half))) * 4;
r += data[cpx + 0] * kernel[cy][cx];
g += data[cpx + 1] * kernel[cy][cx];
b += data[cpx + 2] * kernel[cy][cx];
}
}
data[px + 0] = ((r / divisor) > 255) ? 255 : ((r / divisor) < 0) ? 0 : r / divisor;
data[px + 1] = ((g / divisor) > 255) ? 255 : ((g / divisor) < 0) ? 0 : g / divisor;
data[px + 2] = ((b / divisor) > 255) ? 255 : ((b / divisor) < 0) ? 0 : b / divisor;
}
}
}
}
在这段代码中,我们将定义的所有函数均以 “cpp” 作为其前缀来命名,表明这个函数的实际定义来自于对应的 C/C++ 代码实现。其中,“cppConvFilter” 函数为主要的滤镜计算函数。在该函数中,我们保持着几乎与上一节课中,JavaScript 版滤镜函数同样的实现逻辑。
在代码的开始,我们首先以 “#include” 的方式,包含了很多需要使用到的 C/C++ 头文件。其中 “emscripten.h” 头文件便由 Emscripten 工具链提供,其中包含着众多与 Wasm 编译相关的宏和函数定义。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《WebAssembly入门课》,如需阅读全部文章,
请订阅文章所属专栏新⼈⾸单¥55
立即订阅
登录 后留言

精选留言(6)

  • 张凯
    大致思路表述明白了,WebAssembly.instantiate这个函数是我们写在JS中?

    作者回复: 是的。

    2020-11-05
    1
  • Geek_79ab1c
    我照着例子,试着编译一段求md5的代码,
    使用#include <openssl/md5.h>

    报错:fatal error: 'openssl/md5.h' file not found

    这种依赖第三方库的情况,怎么处理呢

    作者回复: 你可以自己写一个 CMakeLists.txt 来让编译器自动查找头文件的所在位置,比如用 include_directories 指令来指定头文件的查找位置。具体可以参考 CMake 的官方文档,或者找找相关的文章。或者如果使用 Clang 也可以看下 Clang 的 -I 参数。

    2020-10-26
    1
  • Jason Yu 于航
    源码可以参考这里:https://github.com/Becavalier/geektime-wasm-tutorial。
    2021-04-29
  • 军秋
    编译提示:clang-6.0: error: unsupported option '--no-entry' 是我版本太低了吗? clang的官方文档也没找到--no-entry 选项。
    emcc -v
    emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 1.39.4
    clang version 6.0.1 (https://github.com/emscripten-core/emscripten-fastcomp-clang 98df4be387dde3e3918fa5bbb5fc43e1a0e1daac) (https://github.com/emscripten-core/emscripten-fastcomp 6c7e775325067e33fa60611e619a8b987b6d0c35) (emscripten 1.38.44 : 1.38.31)
    Target: x86_64-unknown-linux-gnu
    Thread model: posix
    InstalledDir: /emsdk_portable/clang/tag-e1.39.4/build_tag-e1.39.4_64/bin
    Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6
    Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6.3.0
    Selected GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6.3.0
    Candidate multilib: .;@m64
    Selected multilib: .;@m64
    shared:INFO: (Emscripten: Running sanity checks)

    作者回复: --no-entry 应该是传给 Linker 的参数,参考:https://lld.llvm.org/WebAssembly.html,可以尝试升级一下 Emscripten 的版本。

    2021-03-18
  • Zion
    2019年的时候,了解了几个月webassembly,由于自身原因当时的感觉就是:webassembly只能做些数据转换加密.希望作者再出一些实用webassembly的课程
    2021-01-28
  • champ
    我是这样计算帧率的:

    const records = []
    let lastDrawTime = 0

    function draw() {
        if (lastDrawTime !== 0) {
            const duration = Date.now() - lastDrawTime
            records.push(duration)
        }
        lastDrawTime = Date.now()

        //...

        setTimeout(draw, 0)
    }
    这样算出来的帧率,在不开启渲染的情况下为170FPS左右,js渲染为80FPS左右,wasm渲染为95FPS左右,好像性能提升并不是很明显。

    另外,我发现同样的算法,wasm算出来的图像显示效果要比js算出来的好很多,不知道是什么原因?
    老师能解答下吗?

    作者回复: 这里提到的显示效果是指?

    2021-01-18
    1
收起评论
6
返回
顶部