首先先做一些预定义工作:
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;
}
如下是我使用上述代码创建的一个半透明窗口,如你所见,资源消耗并不高