如何基于DirectX12设备配置Skia与Angle,实现画面叠加绘制需求
实现方案说明
方案一:基于已有ID3D11设备配置Skia + Angle
步骤1:基于现有D3D11设备初始化EGL(Angle后端)
首先确认你使用的Angle编译版本已开启D3D11后端支持,初始化流程如下:
// 1. 加载Angle扩展函数 PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(eglGetProcAddress("eglGetPlatformDisplayEXT")); // 2. 传入现有ID3D11设备创建EGLDisplay const EGLint displayAttribs[] = { EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, EGL_PLATFORM_ANGLE_D3D11_DEVICE_ANGLE, reinterpret_cast<EGLAttrib>(yourExistingD3D11Device), EGL_NONE }; EGLDisplay display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, displayAttribs); // 3. 初始化EGL EGLint major, minor; eglInitialize(display, &major, &minor); // 4. 选择适配的EGL配置 const EGLint configAttribs[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; EGLConfig config; EGLint numConfigs; eglChooseConfig(display, configAttribs, &config, 1, &numConfigs); // 5. 创建EGL上下文与surface const EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE }; EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs); // 若需要渲染到窗口,传入窗口句柄创建window surface,离屏则使用pbuffer surface EGLSurface surface = eglCreateWindowSurface(display, config, yourWindowHandle, nullptr); eglMakeCurrent(display, surface, surface, context);
步骤2:基于EGL上下文初始化Skia
// 1. 关联EGL上下文创建GrContext GrGLInterface* glInterface = GrGLMakeNativeInterface(); sk_sp<GrContext> grContext = GrContext::MakeGL(glInterface); // 2. 创建对应SkSurface SkImageInfo surfaceInfo = SkImageInfo::MakeN32Premul(surfaceWidth, surfaceHeight); sk_sp<SkSurface> skSurface = SkSurface::MakeRenderTarget(grContext.get(), SkBudgeted::kNo, surfaceInfo);
方案二:基于已创建的D3D12 GrContext创建SkSurface
你已经完成D3D12后端GrContext的初始化,可按以下流程直接创建用于绘制的SkSurface,适配你Hook DX12程序叠加绘制的需求:
步骤1:从交换链获取当前后备缓冲纹理
// 从你Hook拿到的IDXGISwapChain3实例获取当前后备缓冲索引 UINT backBufferIndex = swapChain->GetCurrentBackBufferIndex(); // 获取后备缓冲纹理 Microsoft::WRL::ComPtr<ID3D12Resource> backBuffer; swapChain->GetBuffer(backBufferIndex, IID_PPV_ARGS(&backBuffer));
步骤2:构造GrD3DTextureResourceInfo与后端渲染目标
GrD3DTextureResourceInfo texInfo; texInfo.fResource = backBuffer.Get(); texInfo.fResourceState = D3D12_RESOURCE_STATE_RENDER_TARGET; // 确保纹理处于渲染目标状态 texInfo.fFormat = swapChainDesc.Format; // 与交换链格式一致 texInfo.fSampleCount = 1; // 交换链默认无多重采样则填1 texInfo.fLevelCount = 1; // 构造后端渲染目标 GrBackendRenderTarget backendRT(swapChainDesc.BufferDesc.Width, swapChainDesc.BufferDesc.Height, texInfo);
步骤3:创建SkSurface
SkSurfaceProps surfaceProps{}; // 若需要Alpha通道叠加,颜色类型选择kRGBA_8888_SkColorType,匹配交换链格式即可 sk_sp<SkSurface> skSurface = SkSurface::MakeFromBackendRenderTarget( grContext.get(), backendRT, kTopLeft_GrSurfaceOrigin, // D3D纹理默认原点在左上角 kRGBA_8888_SkColorType, SkColorSpace::MakeSRGB(), &surfaceProps );
每帧绘制流程参考
- 原程序渲染完成后,在Present调用前执行你的逻辑
- 调用
grContext->resetContext()重置上下文状态,避免和原程序的D3D状态冲突 - 从
skSurface->getCanvas()拿到SkCanvas实例执行自定义绘制 - 调用
grContext->flush()提交所有绘制指令到D3D12队列 - 调用原Present函数完成画面输出
注意事项
- 多线程渲染的原程序需要加锁保护D3D12设备/命令队列的调用,避免并发冲突
- 交换链重建时需要同步销毁旧的SkSurface,按上述流程重新创建新的表面实例
内容的提问来源于stack exchange,提问作者XvXLuka222
相关产品推荐
相关产品推荐

