Skip to content

XComponent 中的 Surface 详解

在 OpenHarmony 中,Surface 是一个非常重要的图形概念,它代表了图形缓冲区生产者-消费者模型中的生产者端

📚 Surface 的本质

1. 基本定义

Surface 是一个图形绘制表面,可以理解为:

  • 一块内存缓冲区的封装
  • 图形数据的生产场所
  • 连接 UI 框架和底层图形系统的桥梁

2. 在 XComponent 中的角色

typescript
// XComponent 创建 Surface 的流程
XComponent("cube-surface")
onLoad()           // Component 加载
onSurfaceCreated() // Surface 创建完成
  → 获得 Surface 对象   // 可用于原生绘制

🔧 Surface 的底层原理

1. 图形缓冲区管理

text
应用层 (Your App)

XComponent (cube-surface)

Surface (生产者) → 图形缓冲区队列 → Surface (消费者)
    ↓                        ↓
原生绘制 (Native Code)   合成显示 (Display Server)

2. 双缓冲/三缓冲机制

cpp
// Surface 通常维护2-3个图形缓冲区
BufferQueue {
    Buffer [0] ← 正在绘制 (dequeueBuffer)
    Buffer [1] ← 等待显示 (queueBuffer)  
    Buffer [2] ← 正在显示 (acquire by compositor)
}

🎯 Surface 在 NPU 项目中的作用

1. 显示推理结果

cpp
// 在 onSurfaceCreated 回调中
void OnSurfaceCreated(OH_NativeXComponent* component, void* window) {
    // window 实际上就是 Surface 的句柄
    // 可以用于:
    
    // 1. EGL 初始化,创建 OpenGL ES 上下文
    eglCreateWindowSurface(display, config, window, nullptr);
    
    // 2. 直接渲染 NPU 推理结果
    RenderNPUResultToSurface(window);
    
    // 3. 显示处理后的图像/视频
    DisplayProcessedFrame(window);
}

2. 实时预览 NPU 处理效果

cpp
// 典型的渲染循环
void RenderLoop(OH_NativeXComponent* component) {
    // 从 NPU 获取推理结果
    NPUResult result = RunInference();
    
    // 锁定 Surface 缓冲区
    SurfaceBuffer* buffer = LockSurfaceBuffer();
    
    // 将结果渲染到 Surface
    RenderToSurface(buffer, result);
    
    // 提交缓冲区,显示到屏幕
    UnlockAndPostSurfaceBuffer(buffer);
}

📖 Surface 的关键特性

1. 缓冲区特性

cpp
struct SurfaceBuffer {
    int width;      // 缓冲区宽度
    int height;     // 缓冲区高度
    int format;     // 像素格式 (RGBA_8888, YUV420等)
    void* virAddr;  // 虚拟地址 (CPU可访问)
    int fd;         // 文件描述符 (GPU/DMA可访问)
    int stride;     // 行跨度
};

2. 生命周期管理

cpp
// Surface 生命周期回调
struct SurfaceListener {
    void (*OnSurfaceCreated)(OH_NativeXComponent*, void* window);
    void (*OnSurfaceChanged)(OH_NativeXComponent*, void* window);
    void (*OnSurfaceDestroyed)(OH_NativeXComponent*, void* window);
};

🎨 在 NPU 推理场景的具体应用

1. 相机预览 + NPU 处理

text
Camera → Surface [预览] → NPU推理 → Surface [显示结果]

2. 视频处理流水线

cpp
void ProcessVideoFrame(Surface* input, Surface* output) {
    // 1. 从输入Surface获取帧
    SurfaceBuffer* inputBuffer = AcquireInputBuffer(input);
    
    // 2. NPU推理处理
    NPUResult result = HIAI_ProcessFrame(inputBuffer);
    
    // 3. 渲染到输出Surface
    SurfaceBuffer* outputBuffer = AcquireOutputBuffer(output);
    RenderResult(outputBuffer, result);
    
    // 4. 提交显示
    ReleaseOutputBuffer(output);
}

3. 实时 AR 效果

cpp
void ARRenderLoop(Surface* cameraSurface, Surface* displaySurface) {
    while (true) {
        // 获取相机帧
        auto cameraFrame = LockSurface(cameraSurface);
        
        // NPU进行目标检测/姿态估计
        auto detection = RunNPUDetection(cameraFrame);
        
        // 在显示Surface上绘制AR效果
        auto displayBuffer = LockSurface(displaySurface);
        DrawAROverlay(displayBuffer, detection);
        UnlockSurface(displaySurface);
    }
}

⚡ 技术优势

1. 低延迟

  • 避免内存拷贝,直接操作图形缓冲区
  • 支持DMA传输,CPU/GPU/NPU高效协作

2. 高性能

  • 硬件合成器直接处理Surface
  • 支持离屏渲染和多重Surface合成

3. 灵活性

cpp
// 可以创建不同类型的Surface
enum SurfaceType {
    SURFACE_TYPE_NORMAL,    // 普通Surface
    SURFACE_TYPE_VIDEO,     // 视频专用
    SURFACE_TYPE_PROTECTED, // DRM保护内容
};

💻 完整示例:NPU 推理 + Surface 显示

ArkTS 端代码

typescript
@Entry
@Component
struct NPUDisplayPage {
  @State result: string = '';

  build() {
    Column() {
      Text('NPU 推理结果')
        .fontSize(20)
        .margin({ bottom: 10 })

      XComponent({
        id: 'npu-surface',
        type: 'surface',
        controller: this.xComponentController
      })
        .onLoad(() => {
          console.log('XComponent loaded');
        })
        .onDestroy(() => {
          console.log('XComponent destroyed');
        })
        .width('100%')
        .height(400)

      Text(`推理结果: ${this.result}`)
        .fontSize(16)
        .margin({ top: 10 })
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }

  private xComponentController: XComponentController = new XComponentController();

  aboutToAppear() {
    // 设置 Surface 回调
    this.xComponentController.setXComponentSurfaceInitCallback({
      onSurfaceCreated: (component, window) => {
        console.log('Surface created, window:', window);
        // 调用原生代码初始化 NPU 和渲染
        this.initNPURenderer(window);
      },
      onSurfaceChanged: (component, window) => {
        console.log('Surface changed');
      },
      onSurfaceDestroyed: (component, window) => {
        console.log('Surface destroyed');
      }
    });
  }

  private initNPURenderer(window: any) {
    // 调用原生模块初始化 NPU 渲染器
    try {
      const result = native_module.initNPURenderer(window);
      this.result = result;
    } catch (error) {
      console.error('Failed to initialize NPU renderer:', error);
    }
  }
}

C++ 原生代码

cpp
#include "napi/native_api.h"
#include "XComponent/native_xcomponent.h"

// NPU 渲染器类
class NPURenderer {
private:
    OH_NativeXComponent* xComponent_;
    void* window_;
    bool initialized_;

public:
    NPURenderer() : xComponent_(nullptr), window_(nullptr), initialized_(false) {}
    
    bool Initialize(OH_NativeXComponent* component, void* window) {
        xComponent_ = component;
        window_ = window;
        
        // 初始化 EGL
        if (!InitEGL()) {
            return false;
        }
        
        // 初始化 NPU
        if (!InitNPU()) {
            return false;
        }
        
        initialized_ = true;
        return true;
    }
    
    void RenderFrame() {
        if (!initialized_) return;
        
        // 1. 获取 Surface 缓冲区
        SurfaceBuffer* buffer = AcquireSurfaceBuffer();
        if (!buffer) return;
        
        // 2. 运行 NPU 推理
        NPUResult result = RunNPUInference();
        
        // 3. 渲染结果到 Surface
        RenderToSurface(buffer, result);
        
        // 4. 提交缓冲区
        ReleaseSurfaceBuffer(buffer);
    }
    
private:
    bool InitEGL() {
        // EGL 初始化代码
        return true;
    }
    
    bool InitNPU() {
        // NPU 初始化代码
        return true;
    }
    
    SurfaceBuffer* AcquireSurfaceBuffer() {
        // 获取 Surface 缓冲区
        return nullptr;
    }
    
    NPUResult RunNPUInference() {
        // NPU 推理代码
        return NPUResult{};
    }
    
    void RenderToSurface(SurfaceBuffer* buffer, const NPUResult& result) {
        // 渲染到 Surface
    }
    
    void ReleaseSurfaceBuffer(SurfaceBuffer* buffer) {
        // 释放 Surface 缓冲区
    }
};

// 全局渲染器实例
static NPURenderer g_renderer;

// NAPI 导出函数
static napi_value InitNPURenderer(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    
    // 获取 window 参数
    void* window;
    napi_get_value_external(env, args[0], &window);
    
    // 初始化渲染器
    bool success = g_renderer.Initialize(nullptr, window);
    
    napi_value result;
    napi_create_string_utf8(env, success ? "NPU Renderer initialized" : "Failed to initialize", 
                           NAPI_AUTO_LENGTH, &result);
    return result;
}

// 模块导出
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {
        {"initNPURenderer", nullptr, InitNPURenderer, nullptr, nullptr, nullptr, napi_default, nullptr}
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

static napi_module npuModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "npu_renderer",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

extern "C" __attribute__((constructor)) void RegisterNPUModule(void) {
    napi_module_register(&npuModule);
}

🔍 Surface 调试技巧

1. 检查 Surface 状态

cpp
// 检查 Surface 是否有效
bool IsSurfaceValid(void* window) {
    if (window == nullptr) {
        return false;
    }
    
    // 尝试获取 Surface 信息
    SurfaceInfo info;
    int result = GetSurfaceInfo(window, &info);
    return result == 0 && info.width > 0 && info.height > 0;
}

2. 性能监控

cpp
// Surface 渲染性能监控
class SurfaceProfiler {
private:
    std::chrono::high_resolution_clock::time_point start_time_;
    
public:
    void StartFrame() {
        start_time_ = std::chrono::high_resolution_clock::now();
    }
    
    void EndFrame() {
        auto end_time = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
            end_time - start_time_).count();
        
        printf("Frame render time: %ld μs\n", duration);
    }
};

3. 内存泄漏检测

cpp
// Surface 缓冲区泄漏检测
class SurfaceBufferTracker {
private:
    std::set<SurfaceBuffer*> active_buffers_;
    
public:
    void TrackBuffer(SurfaceBuffer* buffer) {
        active_buffers_.insert(buffer);
    }
    
    void UntrackBuffer(SurfaceBuffer* buffer) {
        active_buffers_.erase(buffer);
    }
    
    void CheckLeaks() {
        if (!active_buffers_.empty()) {
            printf("Warning: %zu Surface buffers not released!\n", active_buffers_.size());
        }
    }
};

⚠️ 注意事项

1. 线程安全

cpp
// Surface 操作必须在主线程进行
void SafeSurfaceOperation(void* window) {
    // ❌ 错误:在子线程操作 Surface
    // std::thread([]() { RenderToSurface(window); }).detach();
    
    // ✅ 正确:在主线程或使用线程安全的方式
    OH_NativeXComponent_Callback callback = {
        .OnSurfaceCreated = OnSurfaceCreated,
        .OnSurfaceChanged = OnSurfaceChanged,
        .OnSurfaceDestroyed = OnSurfaceDestroyed
    };
}

2. 资源管理

cpp
// 正确的 Surface 资源管理
class SurfaceManager {
private:
    void* window_;
    bool valid_;
    
public:
    SurfaceManager() : window_(nullptr), valid_(false) {}
    
    ~SurfaceManager() {
        if (valid_) {
            CleanupSurface();
        }
    }
    
    void SetSurface(void* window) {
        if (valid_) {
            CleanupSurface();
        }
        window_ = window;
        valid_ = true;
    }
    
private:
    void CleanupSurface() {
        // 清理 Surface 相关资源
        valid_ = false;
    }
};

3. 错误处理

cpp
// Surface 操作错误处理
napi_value SafeSurfaceOperation(napi_env env, void* window) {
    if (window == nullptr) {
        napi_throw_error(env, nullptr, "Surface window is null");
        return nullptr;
    }
    
    try {
        // Surface 操作
        return PerformSurfaceOperation(window);
    } catch (const std::exception& e) {
        napi_throw_error(env, nullptr, e.what());
        return nullptr;
    }
}

📚 相关资源

官方文档

示例代码

推荐阅读

💡 最佳实践

  1. 生命周期管理:确保 Surface 在正确的时机创建和销毁
  2. 线程安全:Surface 操作必须在主线程进行
  3. 资源清理:及时释放 Surface 相关资源,避免内存泄漏
  4. 错误处理:完善的错误处理机制,提高应用稳定性
  5. 性能优化:合理使用缓冲区,避免不必要的内存拷贝

🎓 学习路径

初学者

  1. 理解 Surface 基本概念
  2. 学习 XComponent 基础用法
  3. 掌握 Surface 生命周期管理
  4. 实现简单的图形渲染

进阶开发者

  1. 深入理解图形缓冲区机制
  2. 掌握 EGL/OpenGL ES 编程
  3. 学习 NPU 与图形渲染结合
  4. 实现复杂的 AR/VR 应用

专家级

  1. 图形系统底层原理
  2. 硬件加速优化
  3. 多 Surface 合成技术
  4. 性能调优与调试

📝 总结

在您的 cube-surface 中,Surface 就是:

  1. 绘制画布 - 提供了一块可以绘制图形内容的内存区域
  2. 显示接口 - 连接您的NPU推理结果和屏幕显示的桥梁
  3. 性能保障 - 通过缓冲区队列机制确保流畅的渲染体验
  4. 多硬件协同 - 支持CPU、GPU、NPU共同操作同一块图形数据

这就是为什么在 onSurfaceCreated 回调触发后,您才能开始进行图形渲染或显示NPU推理结果 - 因为此时图形绘制的基础设施已经准备就绪。


让我们一起探索 HarmonyOS 图形渲染的无限可能! 🚀

本站总访问