☰
SkiaSharp 1.60.0 SkiaSharp.Views.Android 新增 GPU 纹理视图:GLTextureView 与 SKGLTextureView 完整解析
2026/10/14 18:29:07 网站建设 项目流程
  • 图形学
  • 图像处理
  • 跨平台

【免费下载链接】SkiaSharp

SkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Google's Skia Graphics Library. It provides a comprehensive 2D API that can be used across mobile, server and desktop models to render images.

项目地址:https://gitcode.com/gh_mirrors/sk/SkiaSharp
点击查看免费下载

本文基于 SkiaSharp 仓库中 SkiaSharp.Views.Android 程序集 1.59.0 → 1.60.0 的 API 差异文档(SkiaSharp.Views.Android.md)展开。1.60.0 为该程序集引入了三个全新类型——GLTextureView、SKGLTextureView与SKGLTextureViewRenderer,为 .NET 绑定 Android 应用提供了基于TextureView的 OpenGL ES 硬件加速渲染通道。读完本文,你将掌握这套新视图类型的完整公开 API、内部 GL 线程与 EGL 生命周期机制,以及如何在自己的 Android 布局中接入并绘制 SkiaSharp GPU 内容。

一、变更概览:程序集版本与三个新类型

API 差异文档开篇即给出本次变更的核心信息:

Assembly Version Changed: 1.60.0.0 vs 1.59.0.0

即在 1.60.0 版本中,SkiaSharp.Views.Android.dll的程序集版本由 1.59.0.0 提升至 1.60.0.0,命名空间SkiaSharp.Views.Android下新增了三个公开类型:

新类型职责
GLTextureView继承自Android.Views.TextureView,在专用线程上驱动 OpenGL ES 渲染的通用视图
SKGLTextureView继承自GLTextureView,为硬件加速的SKSurface提供专用渲染表面的 SkiaSharp 封装
SKGLTextureViewRenderer抽象渲染器,实现GLTextureView.IRenderer,把 GL 帧缓冲包装成可绘制的SKSurface

三者形成清晰的层次:GLTextureView负责 EGL/GL 环境与渲染线程,SKGLTextureView负责把 SkiaSharp 绘制接到该环境上,SKGLTextureViewRenderer则负责每一帧的SKSurface创建与事件派发。对应实现位于 source/SkiaSharp.Views/SkiaSharp.Views/Platform/Android/ 下的GLTextureView.cs、SKGLTextureView.cs与SKGLTextureViewRenderer.cs。

二、GLTextureView:基于 TextureView 的 OpenGL ES 渲染视图

2.1 类型声明与构造

差异文档给出的完整声明如下:

public class GLTextureView : Android.Views.TextureView, Android.Graphics.Drawables.Drawable.ICallback, Android.Runtime.IJavaObject, Android.Views.Accessibility.IAccessibilityEventSource, Android.Views.KeyEvent.ICallback, Android.Views.TextureView.ISurfaceTextureListener, Android.Views.View.IOnLayoutChangeListener, Java.Interop.IJavaPeerable, System.IDisposable { // constructors public GLTextureView (Android.Content.Context context); public GLTextureView (Android.Content.Context context, Android.Util.IAttributeSet attrs); ... }

两个构造函数分别对应「代码中程序化创建视图」与「从 Android XML 布局文件 inflate」两种场景。从源码看,构造函数都会调用私有Initialize()方法,完成两件关键初始化(GLTextureView.cs):

  • 把视图自身注册为SurfaceTextureListener,以接收SurfaceTexture的可用、销毁、尺寸变化回调;
  • 通过AddOnLayoutChangeListener监听布局变化,并在OnLayoutChange回调中把新尺寸转发给渲染表面(GLTextureView.cs)。

2.2 核心属性与渲染控制

// properties public Android.Opengl.DebugFlags DebugFlags { get; set; } public bool PreserveEGLContextOnPause { get; set; } public Android.Opengl.Rendermode RenderMode { get; set; }
  • DebugFlags:GL 调试开关。源码中当设置了CheckGlError或LogGlCalls时,会通过GLDebugHelper.Wrap对 GL 接口进行包装以检查错误或记录调用(GLTextureView.cs),主要用于排查渲染问题。
  • PreserveEGLContextOnPause:是否在视图暂停时保留 EGL 上下文。GL 线程在暂停时默认会释放 EGL 表面,并视此属性决定是否连 EGL 上下文一并释放(GLTextureView.cs)。
  • RenderMode:渲染模式,对应 Android 的Rendermode枚举。GL 线程只有在requestRender被置位或模式为Continuously时才真正绘制(GLTextureView.cs)。需要连续动画时使用Rendermode.Continuously,按需绘制时使用Rendermode.WhenDirty并配合RequestRender()。

2.3 生命周期方法:OnPause / OnResume

public void OnPause (); public void OnResume ();

视图宿主必须在 Activity 暂停/恢复时调用这两个方法:OnPause()会暂停渲染线程,OnResume()会重建 OpenGL 显示并恢复渲染线程(GLTextureView.cs)。注意:两者都必须在设置 renderer 之后才能调用。

2.4 线程与任务投递

public void QueueEvent (Java.Lang.IRunnable r); public void QueueEvent (System.Action r); public void RequestRender ();

GLTextureView内部维护一个名为GLThread的专用线程(GLTextureView.cs),所有 EGL 初始化、OnSurfaceCreated/OnSurfaceChanged/OnDrawFrame回调、eglSwapBuffers交换缓冲都在该线程内完成(GLTextureView.cs)。QueueEvent可以把任务(System.Action会被包装为Java.Lang.Runnable)投递到该线程队列中串行执行;RequestRender()则请求立刻渲染一帧。

2.5 EGL 配置方法族

public void SetEGLConfigChooser (GLTextureView.IEGLConfigChooser configChooser); public void SetEGLConfigChooser (bool needDepth); public void SetEGLConfigChooser (int redSize, int greenSize, int blueSize, int alphaSize, int depthSize, int stencilSize); public void SetEGLContextClientVersion (int version); public void SetEGLContextFactory (GLTextureView.IEGLContextFactory factory); public void SetEGLWindowSurfaceFactory (GLTextureView.IEGLWindowSurfaceFactory factory); public void SetGLWrapper (GLTextureView.IGLWrapper glWrapper);

这些方法对应 AndroidGLSurfaceView的经典可定制点:

  • SetEGLConfigChooser三个重载分别接受自定义选择器、仅按「是否需要深度缓冲」选择、或精确指定 RGBA/深度/模板各分量位数。默认行为是选择 RGB 888 且深度至少 16 位的配置(源码中默认SimpleEGLConfigChooser使用(8, 8, 8, 0, 深度16, 模板0),见 GLTextureView.cs)。当 EGL 客户端版本设为 2 时,配置规格还会自动追加EGL_RENDERABLE_TYPE/EGL_OPENGL_ES2_BIT,确保选到 ES 2.0 可渲染的配置(GLTextureView.cs)。
  • SetEGLContextClientVersion(int version):指定 OpenGL ES 上下文版本,传 2 即 ES 2.0。DefaultContextFactory会据此构造EGL_CONTEXT_CLIENT_VERSION属性列表(GLTextureView.cs)。
  • SetEGLContextFactory/SetEGLWindowSurfaceFactory/SetGLWrapper:分别定制 EGL 上下文的创建/销毁、窗口表面的创建/销毁、以及 GL 接口的包装(后者典型用途是调试追踪)。

以上配置方法均须在SetRenderer之前调用,源码中CheckRenderThreadState()会在已启动 GL 线程后再次调用时抛出异常(GLTextureView.cs)。

2.6 内嵌接口

差异文档完整列出了五个内嵌接口,它们定义了渲染与 EGL 的可扩展契约:

public interface IEGLConfigChooser { public virtual Javax.Microedition.Khronos.Egl.EGLConfig ChooseConfig (Javax.Microedition.Khronos.Egl.IEGL10 egl, Javax.Microedition.Khronos.Egl.EGLDisplay display); } public interface IEGLContextFactory { public virtual Javax.Microedition.Khronos.Egl.EGLContext CreateContext (Javax.Microedition.Khronos.Egl.IEGL10 egl, Javax.Microedition.Khronos.Egl.EGLDisplay display, Javax.Microedition.Khronos.Egl.EGLConfig eglConfig); public virtual void DestroyContext (Javax.Microedition.Khronos.Egl.IEGL10 egl, Javax.Microedition.Khronos.Egl.EGLDisplay display, Javax.Microedition.Khronos.Egl.EGLContext context); } public interface IEGLWindowSurfaceFactory { public virtual Javax.Microedition.Khronos.Egl.EGLSurface CreateWindowSurface (Javax.Microedition.Khronos.Egl.IEGL10 egl, Javax.Microedition.Khronos.Egl.EGLDisplay display, Javax.Microedition.Khronos.Egl.EGLConfig config, Java.Lang.Object nativeWindow); public virtual void DestroySurface (Javax.Microedition.Khronos.Egl.IEGL10 egl, Javax.Microedition.Khronos.Egl.EGLDisplay display, Javax.Microedition.Khronos.Egl.EGLSurface surface); } public interface IGLWrapper { public virtual Javax.Microedition.Khronos.Opengles.IGL Wrap (Javax.Microedition.Khronos.Opengles.IGL gl); } public interface IRenderer { public virtual void OnDrawFrame (Javax.Microedition.Khronos.Opengles.IGL10 gl); public virtual void OnSurfaceChanged (Javax.Microedition.Khronos.Opengles.IGL10 gl, int width, int height); public virtual void OnSurfaceCreated (Javax.Microedition.Khronos.Opengles.IGL10 gl, Javax.Microedition.Khronos.Egl.EGLConfig config); public virtual void OnSurfaceDestroyed (); }

其中IRenderer是最核心的契约:OnSurfaceCreated在 EGL 上下文创建后调用(可用于创建配套 pbuffer 等),OnSurfaceChanged在表面创建及尺寸变化时调用(典型做法是设置视口),OnDrawFrame每帧调用,OnSurfaceDestroyed在表面丢失时调用。GL 线程在eglSwapBuffers返回EGL_CONTEXT_LOST或其他错误时,也会走对应的上下文/表面重建路径(GLTextureView.cs)。

三、SKGLTextureView:面向 SKSurface 的硬件加速视图

3.1 类型声明与自动配置

public class SKGLTextureView : SkiaSharp.Views.Android.GLTextureView, Android.Graphics.Drawables.Drawable.ICallback, ... { // constructors public SKGLTextureView (Android.Content.Context context); public SKGLTextureView (Android.Content.Context context, Android.Util.IAttributeSet attrs); // properties public SkiaSharp.SKSize CanvasSize { get; } public SkiaSharp.GRContext GRContext { get; } // methods public virtual void SetRenderer (SKGLTextureView.ISKRenderer renderer); ... }

SKGLTextureView的源码注释将其定位为「使用专用表面显示硬件加速SKSurface的GLTextureView实现」。它继承GLTextureView,构造时自动完成两件 EGL 配置(SKGLTextureView.cs):

private void Initialize() { SetEGLContextClientVersion(2); SetEGLConfigChooser(8, 8, 8, 8, 0, 8); renderer = new InternalRenderer(this); SetRenderer(renderer); }

即OpenGL ES 2.0 上下文 + RGBA 8888 颜色、无深度缓冲、8 位模板缓冲的 EGL 配置,并内置了一个InternalRenderer(继承SKGLTextureViewRenderer),它会回调到外层视图的OnPaintSurface方法(SKGLTextureView.cs)。因此使用者无需手动SetRenderer。

3.2 两个只读属性

  • CanvasSize:当前画布尺寸(SKSize)。源码注释特别提醒:画布尺寸可能因设备像素密度而与视图尺寸不同(SKGLTextureView.cs)。
  • GRContext:当前 GPU 上下文。SkiaSharp 的 GPU 绘制对象(纹理、着色器、渲染目标)都在该上下文上创建。

两者均直接转发自内部 renderer(SKGLTextureView.cs)。

3.3 绘制入口:PaintSurface 事件与 OnPaintSurface 方法

public event EventHandler<SKPaintGLSurfaceEventArgs> PaintSurface; protected virtual void OnPaintSurface(SKPaintGLSurfaceEventArgs e);

源码注释明确给出了两种绘制方式:

  1. 订阅PaintSurface事件——最常用的方式,在事件处理器中通过e.Surface拿到SKSurface,用e.BackendRenderTarget.Width/Height获得表面尺寸,从surface.Canvas开始绘制,最后调用canvas.Flush():
myView.PaintSurface += (sender, e) => { var surface = e.Surface; var surfaceWidth = e.BackendRenderTarget.Width; var surfaceHeight = e.BackendRenderTarget.Height; var canvas = surface.Canvas; // draw on the canvas canvas.Flush(); };
  1. 重写OnPaintSurface——若重写此方法,必须调用base.OnPaintSurface(e),否则事件不会被触发(SKGLTextureView.cs):
protected override void OnPaintSurface(SKPaintGLSurfaceEventArgs e) { base.OnPaintSurface(e); var canvas = e.Surface.Canvas; // draw on the canvas canvas.Flush(); }

3.4 事件数据:SKPaintGLSurfaceEventArgs

PaintSurface事件携带的SKPaintGLSurfaceEventArgs定义在 source/SkiaSharp.Views/SkiaSharp.Views.Shared/SKPaintGLSurfaceEventArgs.cs,主要成员如下:

成员说明
SKSurface Surface当前正在绘制的表面
GRBackendRenderTarget BackendRenderTarget当前绘制所基于的 GL 后端渲染目标(含宽高)
SKColorType ColorType渲染目标的颜色类型
GRSurfaceOrigin Origin渲染目标的表面原点
SKImageInfo Info表面的图像信息(尺寸 + 颜色类型)
SKImageInfo RawInfo底层渲染目标的原始图像信息

它提供四个构造函数重载(SKPaintGLSurfaceEventArgs.cs):最简单的(SKSurface, GRBackendRenderTarget)重载默认采用GRSurfaceOrigin.BottomLeft与SKColorType.Rgba8888。

四、SKGLTextureViewRenderer:把 GL 帧缓冲变成 SKSurface

public abstract class SKGLTextureViewRenderer : Java.Lang.Object, ..., GLTextureView.IRenderer, System.IDisposable { // constructors protected SKGLTextureViewRenderer (); // properties public SkiaSharp.SKSize CanvasSize { get; } public SkiaSharp.GRContext GRContext { get; } // methods protected override void Dispose (bool disposing); public virtual void OnDrawFrame (Javax.Microedition.Khronos.Opengles.IGL10 gl); protected virtual void OnDrawFrame (SkiaSharp.SKSurface surface, SkiaSharp.GRBackendRenderTargetDesc renderTarget); public virtual void OnSurfaceChanged (Javax.Microedition.Khronos.Opengles.IGL10 gl, int width, int height); public virtual void OnSurfaceCreated (Javax.Microedition.Khronos.Opengles.IGL10 gl, Javax.Microedition.Khronos.Egl.EGLConfig config); public virtual void OnSurfaceDestroyed (); }

这是连接 GL 世界与 Skia 世界的桥:它在 GL 线程的OnDrawFrame回调里,把当前 GL 帧缓冲包装为SKSurface,再调用受保护的OnDrawFrame(SKSurface, GRBackendRenderTargetDesc)让子类绘制。源码实现位于 SKGLTextureViewRenderer.cs。

4.1 默认的渲染目标格式

源码定义了两个常量作为默认格式(SKGLTextureViewRenderer.cs):

private const SKColorType colorType = SKColorType.Rgba8888; private const GRSurfaceOrigin surfaceOrigin = GRSurfaceOrigin.BottomLeft;

即RGBA 8888 颜色类型 + 左下原点,与 OpenGL 的传统坐标取向一致。

4.2 OnDrawFrame 的完整流程

每一帧OnDrawFrame(IGL10 gl)的执行序列(SKGLTextureViewRenderer.cs):

  1. 清屏:GLES10.GlClear清除颜色、深度、模板缓冲;
  2. 惰性创建 GRContext:若尚未创建,通过GRGlInterface.Create()拿到 GL 接口,再GRContext.CreateGl(glInterface)创建 Skia GPU 上下文;
  3. 按需重建后端渲染目标:当渲染目标为空、尺寸变化或目标失效时,用GLES20.GlGetIntegerv读取当前GL_FRAMEBUFFER_BINDING、模板位数与采样数(采样数会通过context.GetMaxSurfaceSampleCount(colorType)限制到硬件上限),构造GRGlFramebufferInfo与GRBackendRenderTarget,同时销毁旧的 surface 与 renderTarget;
  4. 创建 SKSurface:SKSurface.Create(context, renderTarget, surfaceOrigin, colorType)并取出canvas;
  5. 派发绘制:在SKAutoCanvasRestore保护下构造SKPaintGLSurfaceEventArgs(surface, renderTarget, surfaceOrigin, colorType),调用受保护的OnPaintSurface(e);
  6. 冲刷:canvas.Flush()与context.Flush(),把 Skia 命令提交给 GL。

4.3 生命周期与资源释放

  • OnSurfaceCreated与OnSurfaceDestroyed都会调用FreeContext(),释放 surface、renderTarget 与 GRContext(SKGLTextureViewRenderer.cs);
  • Dispose(bool)在disposing时同样释放上述资源(SKGLTextureViewRenderer.cs);
  • OnSurfaceChanged设置GLES20.GlViewport并记录新尺寸(SKGLTextureViewRenderer.cs)。

五、实战:在 Android 布局中接入 SKGLTextureView

仓库自带的 samples/Basic/Android 示例工程展示了最标准的接入方式:先在 XML 布局中声明视图,再在 Fragment 中订阅事件。

5.1 布局文件

在 fragment_gpu_texture.xml 中直接以全限定名放置视图:

<SkiaSharp.Views.Android.SKGLTextureView android:id="@+id/skiaView" android:layout_width="match_parent" android:layout_height="match_parent" />

由于它本身就是一个 Android 视图,可以像普通视图一样参与布局、叠加与变换。

5.2 Fragment 中订阅绘制事件

GpuTextureFragment.cs 的核心逻辑:

skiaView = view.FindViewById<SKGLTextureView>(Resource.Id.skiaView); skiaView.PaintSurface += OnPaintSurface; private void OnPaintSurface(object sender, SKPaintGLSurfaceEventArgs e) { var canvas = e.Surface.Canvas; var width = e.BackendRenderTarget.Width; var height = e.BackendRenderTarget.Height; // 通过 SKRuntimeEffect 编译的 SkSL 着色器逐帧绘制 var builder = shaderBuilder.Value; builder.Uniforms["iTime"] = fpsCounter.ElapsedSeconds; builder.Uniforms["iResolution"] = new float[] { (float)width, (float)height }; using var shader = builder.Build(); using var paint = new SKPaint { Shader = shader }; canvas.DrawRect(0, 0, width, height, paint); }

注意两点:尺寸取自e.BackendRenderTarget而非视图本身(确保与 GPU 表面一致);绘制完成后 Skia 会在渲染器内部统一Flush,事件处理器内只需完成绘制命令的录入。示例还展示了Touch事件与绘制线程的配合:触摸坐标归一化后作为 shader uniform 传入,实现「metaball lava lamp」的实时交互动画。Fragment 销毁时记得解绑事件并释放相关资源(GpuTextureFragment.cs)。

5.3 与 SKGLSurfaceView 的取舍

示例工程 README(samples/Basic/Android/README.md)对两种 GPU 视图的定位做了明确区分:

  • SKGLSurfaceView:基于 AndroidSurfaceView,适合全屏 GPU 内容;
  • SKGLTextureView:基于TextureView,支持透明度、变换以及在视图层级内的合成——例如把 GPU 渲染结果与上层 UI 叠加、做缩放/旋转动画时,应优先选择后者。代价是TextureView相对SurfaceView存在一定的合成开销。

从实现上看,SKGLTextureView走的是「SurfaceTexture→ EGL window surface → GL 帧缓冲 →GRBackendRenderTarget→SKSurface」的完整链路,而GLTextureView基类本身不依赖任何 Skia 类型,因此也适用于在 SkiaSharp 之外直接承载自定义 OpenGL 渲染。

六、小结与进一步阅读

1.60.0 中SkiaSharp.Views.Android新增的GLTextureView/SKGLTextureView/SKGLTextureViewRenderer,补齐了该平台「基于 TextureView 的 GPU 渲染」能力:GLTextureView提供与 AndroidGLSurfaceView同构的 EGL 配置、渲染线程与生命周期管理;SKGLTextureView开箱即用地提供PaintSurface事件与CanvasSize/GRContext属性;SKGLTextureViewRenderer则在每帧完成 GL 帧缓冲到SKSurface的桥接与资源重建。

  • API 差异文档原文:SkiaSharp.Views.Android.md
  • 核心实现:GLTextureView.cs、SKGLTextureView.cs、SKGLTextureViewRenderer.cs
  • 事件数据:SKPaintGLSurfaceEventArgs.cs
  • 可运行示例:GpuTextureFragment.cs 与 fragment_gpu_texture.xml

如需在本地复现,可进入samples/Basic/Android目录,使用.NET 8 SDK并安装 Android 工作负载后执行dotnet build -t:Install -f net8.0-android部署到设备或模拟器,并在MainActivity.cs中将DefaultPage设为GpuTexture即可查看该视图的实际渲染效果。

  • 图形学
  • 图像处理
  • 跨平台

【免费下载链接】SkiaSharp

SkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Google's Skia Graphics Library. It provides a comprehensive 2D API that can be used across mobile, server and desktop models to render images.

项目地址:https://gitcode.com/gh_mirrors/sk/SkiaSharp
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询