GLM库向量乘矩阵在Shader类中性能缓慢问题求助
问题分析与优化方案
核心问题定位
你看到的计时差异本质是单次操作耗时 vs 批量片元操作累计耗时:main里单独执行一次向量矩阵乘法耗时远低于1ms,所以GetTickCount64显示0;而fragmentShader会被每一个片元调用(一个三角形可能有数千至上万个片元),30ms是所有片元执行该操作的总耗时。
具体优化手段
1. 替换低精度计时工具
GetTickCount64的精度仅为1-16ms,无法准确测量单次片元操作的耗时,会导致累计计时误差。改用高精度计时API:
#include <windows.h> LARGE_INTEGER freq, start, end; QueryPerformanceFrequency(&freq); QueryPerformanceCounter(&start); // 要测量的代码 glm::vec2 uv = bc * uv_mat_f; QueryPerformanceCounter(&end); totaltime += (double)(end.QuadPart - start.QuadPart) / freq.QuadPart * 1000; // 转成毫秒
2. 把矩阵乘法提前到顶点着色器(最有效优化)
当前你在每个片元都执行bc * uv_mat_f,但uv_mat_f和三角形的顶点UV是绑定的,完全可以在vertexShader中计算每个顶点的UV,然后在片元阶段直接用bc插值UV,彻底省去片元阶段的矩阵乘法:
// 修改Shader类,新增顶点UV存储 class TriangleShader :public Shader { public: glm::mat3x3 normal_mat_f; glm::vec2 uv_v0, uv_v1, uv_v2; // 存储三个顶点的UV public: TriangleShader() :normal_mat_f(0) {} void vertexShader(glm::mat4x3& vertex_mat, glm::mat4x4& M, const glm::mat3x3& normal_mat, const glm::mat2x3& uv_mat, const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2) override { normal_mat_f = normal_mat; // 提前计算每个顶点的UV(根据你的顶点原始坐标调整) uv_v0 = glm::vec2(v0) * uv_mat; uv_v1 = glm::vec2(v1) * uv_mat; uv_v2 = glm::vec2(v2) * uv_mat; vertex_mat = vertex_mat * M; } TGAColor fragmentShader(glm::vec3 bc, double& totaltime, Model& model) override { // 直接用bc插值顶点UV,省去矩阵乘法 glm::vec2 uv = bc.x * uv_v0 + bc.y * uv_v1 + bc.z * uv_v2; // 后续光照、颜色计算不变... glm::vec3 n = model.normal(uv); n = glm::normalize(n); float intensity = std::max((float)0, glm::dot(n, light)); TGAColor color = model.diffuse(uv); return color * intensity + 5; } };
这个优化能直接把片元阶段的矩阵乘法开销降为0,是最彻底的解决方案。
3. 手动展开矩阵乘法(如果无法提前计算)
如果必须在片元阶段执行乘法,手动展开计算可以避免GLM模板的额外开销,同时让编译器更容易做SIMD优化:
// 代替 glm::vec2 uv = bc * uv_mat_f; glm::vec2 uv; uv.x = bc.x * uv_mat_f[0][0] + bc.y * uv_mat_f[0][1] + bc.z * uv_mat_f[0][2]; uv.y = bc.x * uv_mat_f[1][0] + bc.y * uv_mat_f[1][1] + bc.z * uv_mat_f[1][2];
4. 优化成员变量访问
uv_mat_f是类的成员变量,编译器可能无法很好地做缓存优化。可以将其作为参数直接传入fragmentShader,让编译器把数据放到寄存器中:
// 修改fragmentShader签名 TGAColor fragmentShader(glm::vec3 bc, double& totaltime, Model& model, const glm::mat2x3& uv_mat) override { glm::vec2 uv = bc * uv_mat; // ...后续代码不变 }
5. 开启编译器优化
确保你在Release模式下编译,并且开启最高优化等级(VS中是/O2,GCC/Clang中是-O3)。Debug模式下GLM会有大量调试断言和未优化的模板代码,耗时会飙升数十倍。
内容的提问来源于stack exchange,提问作者SeeingDragon
相关产品推荐
相关产品推荐

