ScopedValue 泛型模板类和 ScopedglDepthMask的 RAII 实现

代码来源:vtkOpenGLState.cxx


🎯 整体设计图

复制代码
┌─────────────────────────────────────────────────────┐
│        ScopedValue<T> (泛型模板基类)                │
│  ─────────────────────────────────────────────────  │
│  • 保存状态值 (Value: T)                            │
│  • 保存恢复方法指针 (Method: void(*)(T))           │
│  • ~ScopedValue() 自动恢复                         │
└──────────────────┬──────────────────────────────────┘
                   │ 继承特化
        ┌──────────┴──────────┐
        ↓                     ↓
┌──────────────────┐  ┌──────────────────┐
│ScopedglDepthMask │  │ScopedglColorMask │ ...
│ (unsigned char)  │  │ (array<uchar,4>) │
└──────────────────┘  └──────────────────┘

1️⃣ 泛型基类 ScopedValue<T> 详解

完整代码分析

cpp 复制代码
template <typename T>
class VTKRENDERINGOPENGL2_EXPORT ScopedValue
{
public:
  ~ScopedValue()  // 析构时自动恢复
  {
    ((*this->State).*(this->Method))(this->Value);
    //  ↑                  ↑              ↑
    // 取引用      调用成员函数指针   传入保存的值
  }

protected:
  vtkOpenGLState* State;      // OpenGL 状态管理器指针
  T Value;                    // 保存的旧状态值(模板参数)
  void (vtkOpenGLState::*Method)(T);  // 指向恢复方法的成员函数指针
};

核心机制分解

成员函数指针语法讲解:

cpp 复制代码
void (vtkOpenGLState::*Method)(T);
//   ↑                      ↑    ↑
//  返回类型          类名+方法指针  参数类型

这表示:

  • 一个指向 vtkOpenGLState 类成员函数的指针
  • 该函数接收类型为 T 的参数
  • 该函数返回 void

析构函数的调用方式:

cpp 复制代码
((*this->State).*(this->Method))(this->Value);
//  ↑              ↑              ↑
//  解指针     解成员函数指针     调用并传入值

// 等价于(更易读的写法):
auto pObj = this->State;           // 获取对象指针
auto pMethod = this->Method;        // 获取方法指针
(*pObj.*pMethod)(this->Value);      // 调用方法并传入值

// 更直观的理解:
this->State->*Method(this->Value);  // 伪代码表示

2️⃣ ScopedglDepthMask 具体实现

头文件声明

cpp 复制代码
class VTKRENDERINGOPENGL2_EXPORT ScopedglDepthMask 
  : public ScopedValue<unsigned char>  // ← 特化为 unsigned char
{
public:
  ScopedglDepthMask(vtkOpenGLState* state);  // 构造函数在.cxx实现
};

实现文件(.cxx)

cpp 复制代码
vtkOpenGLState::ScopedglDepthMask::ScopedglDepthMask(vtkOpenGLState* s)
{
  this->State = s;                           // 保存 state 指针
  this->Value = this->State->Stack.top().DepthMask;  // 保存当前深度掩码值
  this->Method = &vtkOpenGLState::vtkglDepthMask;    // 保存恢复方法指针
}

3️⃣ 完整工作流程

使用示例

cpp 复制代码
void vtkOpenGLActor::Render(vtkRenderer* ren, vtkMapper* mapper)
{
  vtkOpenGLState* ostate = static_cast<vtkOpenGLRenderer*>(ren)->GetState();
  
  // ① 构造:保存当前深度掩码值
  vtkOpenGLState::ScopedglDepthMask dmsaver(ostate);
  //   ↑ 进入作用域
  //   • Value = 旧深度掩码值(可能是 GL_TRUE)
  //   • Method = vtkglDepthMask 方法指针
  //   • State = ostate 指针

  bool opaque = !this->IsRenderingTranslucentPolygonalGeometry();
  
  if (opaque) {
    // ② 临时修改状态(不保存)
    ostate->vtkglDepthMask(GL_TRUE);
  } else {
    ostate->vtkglDepthMask(GL_FALSE);
  }

  mapper->Render(ren, this);  // 执行实际渲染

  if (!opaque) {
    ostate->vtkglDepthMask(GL_TRUE);
  }

} // ③ 析构:自动恢复
  //   执行:ostate->vtkglDepthMask(旧值)

执行时间线

复制代码
时间 ──→

构造 ScopedglDepthMask
  │
  ├─ this->State = ostate
  ├─ this->Value = GL_TRUE        ← 保存旧值
  └─ this->Method = &vtkOpenGLState::vtkglDepthMask
                                      ↓
    临时修改状态
      │
      ├─ ostate->vtkglDepthMask(GL_FALSE)
      └─ (在缓存中: DepthMask = GL_FALSE)
                                      ↓
    执行渲染工作
                                      ↓
    离开作用域
      │
      └─ ~ScopedglDepthMask() 析构
         │
         └─ ostate->vtkglDepthMask(GL_TRUE)  ← 自动恢复!
            (在缓存中: DepthMask = GL_TRUE)

4️⃣ 泛化设计的威力

多种特化实现

cpp 复制代码
// 特化1:单值状态(unsigned char)
class ScopedglDepthMask : public ScopedValue<unsigned char>
{ /* ... */ };

// 特化2:颜色掩码(4个unsigned char)
class ScopedglColorMask : public ScopedValue<std::array<unsigned char, 4>>
{ /* ... */ };

// 特化3:混合函数(4个unsigned int)
class ScopedglBlendFuncSeparate : public ScopedValue<std::array<unsigned int, 4>>
{ /* ... */ };

// 特化4:视口(4个int)
class ScopedglViewport : public ScopedValue<std::array<int, 4>>
{ /* ... */ };

// 特化5:清屏颜色(4个float)
class ScopedglClearColor : public ScopedValue<std::array<float, 4>>
{ /* ... */ };

代码重用对比

没有模板(代码重复):

cpp 复制代码
// ❌ 为每个状态都写一遍
class ScopedglDepthMask {
  ~ScopedglDepthMask() { 
    state->vtkglDepthMask(value); 
  }
  vtkOpenGLState* state;
  unsigned char value;
};

class ScopedglColorMask {
  ~ScopedglColorMask() { 
    state->vtkglColorMask(...); 
  }
  vtkOpenGLState* state;
  std::array<unsigned char, 4> value;
};

// ... 更多重复

使用模板(代码统一):

cpp 复制代码
// ✅ 一个模板,所有特化都自动继承
template <typename T>
class ScopedValue {
  ~ScopedValue() { 
    ((*this->State).*(this->Method))(this->Value); 
  }
  vtkOpenGLState* State;
  T Value;
  void (vtkOpenGLState::*Method)(T);
};

5️⃣ 成员函数指针的核心原理

为什么需要函数指针?

每个 Scoped 类的恢复方法都不同:

cpp 复制代码
// 深度掩码用:vtkglDepthMask(unsigned char)
ScopedglDepthMask → 调用 state->vtkglDepthMask(value)

// 颜色掩码用:vtkglColorMask(4个unsigned char)
ScopedglColorMask → 调用 state->vtkglColorMask(r, g, b, a)
                            // 但数据存在数组里!

// 混合函数用:vtkglBlendFuncSeparate(4个unsigned int)
ScopedglBlendFuncSeparate → 调用 state->vtkglBlendFuncSeparate(...)

解决方案:用模板 + 函数指针

cpp 复制代码
template <typename T>
class ScopedValue {
  // 通过构造函数注入方法指针
  // 不同的特化可以指向不同的恢复方法
  void (vtkOpenGLState::*Method)(T);  // ← 灵活!
};

// 具体特化在构造函数中决定指向哪个方法
ScopedglDepthMask::ScopedglDepthMask(vtkOpenGLState* s) {
  this->Method = &vtkOpenGLState::vtkglDepthMask;  // 指向深度掩码方法
}

ScopedglColorMask::ScopedglColorMask(vtkOpenGLState* s) {
  this->Method = &vtkOpenGLState::vtkglColorMask;  // 指向颜色掩码方法
}

6️⃣ 调用语法详解

成员函数指针的调用方式

cpp 复制代码
// 假设我们有:
vtkOpenGLState* state = ...;
void (vtkOpenGLState::*method)(unsigned char) = &vtkOpenGLState::vtkglDepthMask;
unsigned char value = GL_TRUE;

// 方式1:最复杂(但标准)
((*state).*(method))(value);
//  ↑      ↑        ↑
//  指针   解成员    调用

// 方式2:简化(指针版)
(state->*(method))(value);

// 方式3:等价的宏定义
#define CALL_METHOD(obj, method, value) \
  ((obj).*(method))(value)

// 方式4:在VTK代码中实际使用的
((*this->State).*(this->Method))(this->Value);

为什么这么复杂?

cpp 复制代码
// 对比普通函数指针调用
void (*funcPtr)(int) = &someFunction;
funcPtr(42);  // 简单!

// vs 成员函数指针调用
void (MyClass::*methodPtr)(int) = &MyClass::someMethod;
MyClass obj;
(obj.*(methodPtr))(42);  // 复杂,因为需要对象上下文

7️⃣ RAII 设计优势总结

复制代码
┌────────────────────────────────────────────────────┐
│           RAII(资源获取即初始化)                 │
├────────────────────────────────────────────────────┤
│                                                    │
│  获取资源(保存状态)           in Constructor     │
│         ↓                                          │
│  使用资源(执行临时修改)       in User Code      │
│         ↓                                          │
│  释放资源(恢复状态)           in Destructor     │
│                                                    │
│  ✅ 异常安全(即使抛异常也恢复)                  │
│  ✅ 自动化(不需要手动 restore)                  │
│  ✅ 作用域绑定(清晰的生命周期)                  │
│                                                    │
└────────────────────────────────────────────────────┘

8️⃣ 实战示例对比

❌ 传统做法(容易出错)

cpp 复制代码
void MyRenderFunction() {
  GLboolean oldMask;
  glGetBooleanv(GL_DEPTH_WRITEMASK, &oldMask);  // 查询状态(慢!)
  
  glDepthMask(GL_FALSE);  // 临时修改
  
  DoSomeRendering();
  
  glDepthMask(oldMask);  // 手动恢复(容易忘记!)
  
  if (error) {
    return;  // ❌ 如果这里返回,就没法恢复了!
  }
}

✅ VTK 做法(安全可靠)

cpp 复制代码
void vtkOpenGLActor::Render(vtkRenderer* ren, vtkMapper* mapper) {
  vtkOpenGLState* ostate = static_cast<vtkOpenGLRenderer*>(ren)->GetState();
  
  // 构造时保存,析构时自动恢复
  vtkOpenGLState::ScopedglDepthMask dmsaver(ostate);
  //   ↑ 进入作用域
  
  ostate->vtkglDepthMask(GL_FALSE);  // 临时修改
  
  mapper->Render(ren, this);
  
  if (error) {
    return;  // ✅ 自动调用 dmsaver 析构,恢复状态!
  }
  
} // ✅ 即使正常返回,也会自动恢复

总结

特性 说明
泛型基类 ScopedValue<T> 统一管理所有状态
特化类 ScopedglDepthMaskScopedglColorMask 等继承并特化
成员函数指针 动态指向不同的恢复方法,实现灵活的 RAII
模板参数 T 可以是 unsigned charstd::array<T, 4>
异常安全 无论如何离开作用域都自动恢复
零开销 编译时展开,运行时无额外代价

这就是 VTK OpenGL 状态管理中最优雅的设计模式!🎨

相关推荐
平行云1 天前
虚拟直播混合式2D/3D应用程序实时云渲染推流解决方案
linux·unity·云原生·ue5·图形渲染·实时云渲染·像素流送
RReality3 天前
【Unity Shader URP】平面反射(Planar Reflection)实战教程
ui·平面·unity·游戏引擎·图形渲染·材质
RReality4 天前
【Unity UGUI】Toggle / ToggleGroup 与 Dropdown
ui·unity·游戏引擎·图形渲染·材质
郝学胜-神的一滴4 天前
[简化版 Games 101] 计算机图形学 05:二维变换下
c++·unity·图形渲染·three.js·opengl·unreal
charlie1145141915 天前
通用GUI编程技术——图形渲染实战(三十三)——Direct2D与Win32/GDI互操作:渐进迁移实战
c++·图形渲染·gui·win32
RReality6 天前
【Unity Shader URP】Matcap 材质捕捉实战教程
java·ui·unity·游戏引擎·图形渲染·材质
RReality6 天前
【Unity Shader URP】简易卡通着色(Simple Toon)实战教程
ui·unity·游戏引擎·图形渲染·材质
梵尔纳多8 天前
OpenGL 骨骼动画
c++·图形渲染·opengl
charlie1145141918 天前
通用GUI编程技术——图形渲染实战(三十一)——Direct2D效果与图层:高斯模糊到毛玻璃
c++·图形渲染·gui·win32