不依赖GLFW或Glad创建Skia OpenGL后端(Windows)

首先先做一些预定义工作:

namespace {
    #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
    #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
    #define WGL_CONTEXT_PROFILE_MASK_ARB  0x9126
    #define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 
    typedef HGLRC(WINAPI* PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC, HGLRC hShareContext, const int* attribList);
    PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = nullptr;
    extern "C" {
        __declspec(dllexport) int NvOptimusEnablement = 1;
        __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
    }
}

上述代码最后两句 dllexport 目的是在Windows系统上强制你的OpenGL程序使用高性能独立显卡(NVIDIA或AMD),而不是默认的集成显卡。这很重要,关系到多屏幕用户环境的一个重要BUG。

再定义几个类变量:

HGLRC hglrc;
sk_sp<GrDirectContext> grContext;
sk_sp<const GrGLInterface> backendContext;
SkSurfaceProps surfaceProps(SkSurfaceProps::kUseDeviceIndependentFonts_Flag, kRGB_H_SkPixelGeometry);
GrGLFramebufferInfo fbInfo;

现在初始化hglrc、grContext、backendContext和fbInfo

void OpenGL::init()
{
    HDC hdc = GetDC(win->hwnd);
    PIXELFORMATDESCRIPTOR pfd = {};
    pfd.nSize = sizeof(pfd);
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 32;
    pfd.cDepthBits = 24;
    pfd.cAlphaBits = 8;
    pfd.iLayerType = PFD_MAIN_PLANE;
    int pixelFormat = ChoosePixelFormat(hdc, &pfd);
    auto flag = SetPixelFormat(hdc, pixelFormat, &pfd);
    HGLRC oldContext = wglCreateContext(hdc);
    wglMakeCurrent(hdc, oldContext);
    wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
    int attribs[] = {
        WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
        WGL_CONTEXT_MINOR_VERSION_ARB, 3,
        WGL_CONTEXT_PROFILE_MASK_ARB,
        WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
        0
    };
    HGLRC hglrc;
    HGLRC newContext = wglCreateContextAttribsARB(hdc, 0, attribs);
    if (newContext) {
        wglMakeCurrent(nullptr, nullptr);
        wglDeleteContext(oldContext);
        hglrc = newContext;
    }
    else {
        hglrc = oldContext;
    }
    wglMakeCurrent(hdc, hglrc);
    ReleaseDC(win->hwnd, hdc);
    backendContext = GrGLMakeNativeInterface();
    GrGLint buffer;
    backendContext.get()->fFunctions.fGetIntegerv(GR_GL_FRAMEBUFFER_BINDING, &buffer);
    fbInfo.fFBOID = buffer;
    fbInfo.fFormat = GR_GL_RGBA8;
    fbInfo.fProtected = skgpu::Protected(false);
    grContext = GrDirectContexts::MakeGL(backendContext);
}

获取SkSurface的代码如下所示:

sk_sp<SkSurface> OpenGL::getSurface()
{
    if (nullptr == surface) {
        auto backendRT = GrBackendRenderTargets::MakeGL(win->w, win->h,
            16, //sampleCount
            8, //stencilBits
            fbInfo);
        surface = SkSurfaces::WrapBackendRenderTarget(grContext.get(),
            backendRT,
            kBottomLeft_GrSurfaceOrigin,
            kRGBA_8888_SkColorType,
            nullptr,
            &surfaceProps);
    }
    return surface;
}

这段代码中:sampleCount 表示多重采样抗锯齿的采样数。stencilBits 表示模板缓冲区的位深度。

窗口对应的SkSurface对象是缓存,窗口大小不改变SkSurface对象也不会改变。

当窗口大小改变时:

void OpenGL::resize()
{
    surface.reset(nullptr);
    if (grContext) {
        grContext->flushAndSubmit();
    }
}

窗口大小改变时,我们释放了SkSurface对象,迫使程序去重新获取SkSurface。

值得注意的是,这段代码与Skia的示例Demo不一样的,Skia的示例Demo有内存泄漏的问题。

在处理WM_PAINT消息时,你可以通过如下代码把SkSurface中的内容同步到窗口中:

case WM_PAINT: {
  PAINTSTRUCT ps;
  HDC hdc = BeginPaint(hwnd, &ps);
  onPaint(backend->getCanvas()); //你自己的绘图逻辑
  grContext->flushAndSubmit(surface.get());
  SwapBuffers(dc);
  ReleaseDC(hwnd, hdc);
  EndPaint(hwnd, &ps);
  return 0;
}

当窗口销毁时,我们也要释放相关的资源和对象,如下代码所示:

OpenGL::~OpenGL()
{
    surface.reset(nullptr);
    wglMakeCurrent(nullptr, nullptr);
    wglDeleteContext(hglrc);
    grContext.reset(nullptr);
    backendContext.reset(nullptr);
    hglrc = NULL;
}

如下是我使用上述代码创建的一个半透明窗口,如你所见,资源消耗并不高

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liulun

如果文章真帮到了你,谢谢您打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值