You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SharpGL播放视频卡顿,是否未启用硬件加速?

SharpGL播放视频流性能优化及迁移DirectX建议

问题描述

我用SharpGL播放由OpenCV转换为Bitmap的视频流时,发现两个关键调用耗时过高:

  • texture.Create(gl, bitmap) 耗时约40毫秒
  • gl.Flush() 耗时约100毫秒

设备为Windows 10系统,搭载NVIDIA 3080显卡且驱动为最新版本,怀疑未启用硬件加速。由于SharpGL相关资料匮乏,纠结是否应该转用DirectX。

原始代码

XAML部分

<Border HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Red" Name="BorderControl" SizeChanged="BorderControlSizeChanged">
    <sharpGL:OpenGLControl OpenGLDraw="OpenGLControl_OpenGLDraw" Name="OpenGLControl" Resized="openGLControl1_Resized" Width="500" Height="500" DrawFPS="True">
    </sharpGL:OpenGLControl>
</Border>

C#绘制逻辑

private void OpenGLControl_OpenGLDraw(object sender, OpenGLRoutedEventArgs args)
{
    long time = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;

    long time0 = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
    MutexWaitOne();
    time0 = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond - time0;

    long time1 = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
    OpenGL gl = args.OpenGL;
    gl.Enable(OpenGL.GL_TEXTURE_2D);
    gl.Disable(OpenGL.GL_DEPTH_TEST);
    time1 = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond - time1;

    long time2 = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
    int Width = (int)OpenGLControl.Width;
    int Height = (int)OpenGLControl.Height;
    gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT);
    gl.LoadIdentity();
    time2 = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond - time2;

    long time3 = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
    texture.Destroy(gl);
    time3 = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond - time3;

    long time4 = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
    if (_cameraImagebuffer != null)
    {
        try
        {
            texture.Create(gl, _cameraImagebufferBitmap);
        }catch(Exception ex)
        {
            MutexRelease();
            return;
        }
    }
    gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, OpenGL.GL_LINEAR);
    gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MAG_FILTER, OpenGL.GL_LINEAR);
    time4 = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond - time4;

    long time5 = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
    texture.Bind(gl);
    time5 = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond - time5;

    long time6 = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
    gl.Begin(OpenGL.GL_QUADS);
    gl.TexCoord(0.0f, 0.0f); gl.Vertex(0, 0, 0);
    gl.TexCoord(1.0f, 0.0f); gl.Vertex(Width, 0, 0);
    gl.TexCoord(1.0f, 1.0f); gl.Vertex(Width, Height, 0);
    gl.TexCoord(0.0f, 1.0f); gl.Vertex(0, Height, 0);
    gl.End();
    time6 = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond - time6;

    long time7 = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
    gl.Flush();
    time7 = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond - time7;

    MutexRelease();

    time = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond - time;
    Console.WriteLine(String.Format("OPENGL: Total : {0}   step0:{1}  step1:{2}  step2:{3}  step3:{4}  step4:{5}  step5:{6}  step6:{7}  step7:{8}", time, time0, time1, time2, time3, time4, time5, time6, time7));
}

性能优化方案(优先尝试)

你的代码存在几个核心性能瓶颈,先优化这些再判断是否需要迁移:

1. 避免每帧销毁重建纹理

当前每帧都执行texture.Destroy(gl)和texture.Create(gl, _cameraImagebufferBitmap),会导致GPU反复创建销毁资源,这是texture.Create耗时高的主要原因。正确做法是初始化时创建一次纹理,后续仅更新像素数据:

优化步骤:

  • 在控件加载时初始化纹理:
private void OpenGLControl_Loaded(object sender, RoutedEventArgs e)
{
    OpenGL gl = OpenGLControl.OpenGL;
    texture.Create(gl, _cameraImagebufferBitmap);
    // 纹理参数仅需设置一次
    gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, OpenGL.GL_LINEAR);
    gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MAG_FILTER, OpenGL.GL_LINEAR);
}
  • 在绘制事件中替换销毁重建逻辑为像素更新:
// 移除texture.Destroy(gl);
if (_cameraImagebuffer != null)
{
    try
    {
        gl.Bind(OpenGL.GL_TEXTURE_2D, texture.TextureName);
        BitmapData data = _cameraImagebufferBitmap.LockBits(
            new Rectangle(0, 0, _cameraImagebufferBitmap.Width, _cameraImagebufferBitmap.Height),
            ImageLockMode.ReadOnly,
            System.Drawing.Imaging.PixelFormat.Format32bppArgb);
        // 直接更新纹理像素,无需重建
        gl.TexSubImage2D(OpenGL.GL_TEXTURE_2D, 0, 0, 0, data.Width, data.Height, 
                         OpenGL.GL_BGRA, OpenGL.GL_UNSIGNED_BYTE, data.Scan0);
        _cameraImagebufferBitmap.UnlockBits(data);
    }
    catch(Exception ex)
    {
        MutexRelease();
        return;
    }
}

2. 确认启用硬件加速

检查OpenGLControl的RenderContextType属性,确保使用硬件加速模式:

  • XAML中设置:
<sharpGL:OpenGLControl RenderContextType="FBO" ... />
  • 或代码中设置:
OpenGLControl.RenderContextType = RenderContextType.FBO;

FBO或NativeWindow模式会启用GPU硬件加速,避免软件渲染导致的高耗时。

3. 移除不必要的gl.Flush()

gl.Flush()会强制GPU立即执行所有待处理命令,实时渲染场景中通常无需显式调用——SharpGL控件本身会处理缓冲区交换。直接移除该调用,观察耗时是否下降。

4. 替换老旧的立即模式绘制

当前使用的glBegin/glEnd是老旧的立即模式,CPU-GPU交互开销大。建议改用VAO/VBO存储顶点数据,初始化时仅上传一次,绘制时直接绑定渲染,进一步降低CPU开销。


是否需要转用DirectX?

如果上述优化后性能仍不达标,或需要更深度的Windows平台视频渲染支持,转用DirectX是合理选择:

  • Windows系统对DirectX原生优化更好,NVIDIA显卡对DirectX的驱动优化也更成熟,尤其适合视频流这类性能敏感场景。
  • .NET生态下的DirectX绑定库(如SharpDX、DirectXTK)资料更丰富,社区支持更完善。
  • 可直接处理OpenCV的Mat数据,无需转换为Bitmap,减少数据拷贝开销。

内容的提问来源于stack exchange,提问作者Jonathan Le Cornichone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 01:15:53