Skia使用Dawn后端在Windows窗口中绘图

首先创建一个Windows窗口,如下代码所示:

cpp 复制代码
void WindowMain::createWindow()
{
    static bool isWcexReg = false;
    static const TCHAR clsName[] = L"SkiaApp";
    static WNDCLASSEX wcex;
    auto hinstance = GetModuleHandle(NULL);
    if (!isWcexReg) {
        wcex.cbSize = sizeof(WNDCLASSEX);
        wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
        wcex.lpfnWndProc = &WindowMain::wndProc;
        wcex.cbClsExtra = 0;
        wcex.cbWndExtra = 0;
        wcex.hInstance = hinstance;
        wcex.hIcon = LoadIcon(hinstance, IDI_APPLICATION);
        wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
        wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
        wcex.lpszMenuName = nullptr;
        wcex.lpszClassName = clsName;
        wcex.hIconSm = LoadIcon(hinstance, IDI_APPLICATION);
        if (!RegisterClassEx(&wcex)) {
            return;
        }
        isWcexReg = true;
    }
    hwnd = CreateWindowEx(NULL, clsName, clsName, WS_OVERLAPPEDWINDOW,
        x, y, w, h, nullptr, nullptr, hinstance, nullptr);
    SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)this);
}

接着创建Dawn实例:

cpp 复制代码
void WindowMain::initDawnInstance()
{
    WGPUInstanceDescriptor desc{};
    desc.features.timedWaitAnyEnable = true;
    dawnInstance = std::make_unique<dawn::native::Instance>(&desc);
}

然后创建Dawn设备:

cpp 复制代码
void WindowMain::initDawnDevice()
{
    DawnProcTable backendProcs = dawn::native::GetProcs();
    dawnProcSetProcs(&backendProcs);
    static constexpr const char* kToggles[] = {
        "allow_unsafe_apis",
        "use_user_defined_labels_in_backend",
        "disable_robustness", //禁用这玩意儿,提升性能
        "use_tint_ir",
    };
    wgpu::DawnTogglesDescriptor togglesDesc;
    togglesDesc.enabledToggleCount = std::size(kToggles) - 1;
    togglesDesc.enabledToggles = kToggles;
    wgpu::RequestAdapterOptions adapterOptions;
    adapterOptions.backendType = wgpu::BackendType::D3D11;
    adapterOptions.featureLevel = wgpu::FeatureLevel::Core;
    adapterOptions.nextInChain = &togglesDesc;
    std::vector<dawn::native::Adapter> adapters = dawnInstance->EnumerateAdapters(&adapterOptions);
    if (adapters.empty()) {
        return;
    }
    wgpu::Adapter adapter = adapters[0].Get();
    std::vector<wgpu::FeatureName> features;
    if (adapter.HasFeature(wgpu::FeatureName::MSAARenderToSingleSampled)) {
        features.push_back(wgpu::FeatureName::MSAARenderToSingleSampled);
    }
    if (adapter.HasFeature(wgpu::FeatureName::TransientAttachments)) {
        features.push_back(wgpu::FeatureName::TransientAttachments);
    }
    if (adapter.HasFeature(wgpu::FeatureName::Unorm16TextureFormats)) {
        features.push_back(wgpu::FeatureName::Unorm16TextureFormats);
    }
    if (adapter.HasFeature(wgpu::FeatureName::DualSourceBlending)) {
        features.push_back(wgpu::FeatureName::DualSourceBlending);
    }
    if (adapter.HasFeature(wgpu::FeatureName::FramebufferFetch)) {
        features.push_back(wgpu::FeatureName::FramebufferFetch);
    }
    if (adapter.HasFeature(wgpu::FeatureName::BufferMapExtendedUsages)) {
        features.push_back(wgpu::FeatureName::BufferMapExtendedUsages);
    }
    if (adapter.HasFeature(wgpu::FeatureName::TextureCompressionETC2)) {
        features.push_back(wgpu::FeatureName::TextureCompressionETC2);
    }
    if (adapter.HasFeature(wgpu::FeatureName::TextureCompressionBC)) {
        features.push_back(wgpu::FeatureName::TextureCompressionBC);
    }
    if (adapter.HasFeature(wgpu::FeatureName::R8UnormStorage)) {
        features.push_back(wgpu::FeatureName::R8UnormStorage);
    }
    if (adapter.HasFeature(wgpu::FeatureName::DawnLoadResolveTexture)) {
        features.push_back(wgpu::FeatureName::DawnLoadResolveTexture);
    }
    if (adapter.HasFeature(wgpu::FeatureName::DawnPartialLoadResolveTexture)) {
        features.push_back(wgpu::FeatureName::DawnPartialLoadResolveTexture);
    }
    wgpu::DeviceDescriptor deviceDescriptor;
    deviceDescriptor.requiredFeatures = features.data();
    deviceDescriptor.requiredFeatureCount = features.size();
    deviceDescriptor.nextInChain = &togglesDesc;
    deviceDescriptor.SetDeviceLostCallback(
        wgpu::CallbackMode::AllowSpontaneous,
        [](const wgpu::Device&, wgpu::DeviceLostReason reason, const char* message) {
            if (reason != wgpu::DeviceLostReason::Destroyed &&
                reason != wgpu::DeviceLostReason::InstanceDropped) {
                SK_ABORT("Device lost: %s\n", message);
            }
        });
    deviceDescriptor.SetUncapturedErrorCallback(
        [](const wgpu::Device&, wgpu::ErrorType, const char* message) {
            SkDebugf("Device error: %s\n", message);
            SkASSERT(false);
        });
    dawnDevice = adapter.CreateDevice(&deviceDescriptor);
}

然后创建Dawn表面(注意,dawnSurface不是Skia的SkSurface)

cpp 复制代码
void WindowMain::initDawnSurface()
{
    wgpu::SurfaceDescriptorFromWindowsHWND surfaceChainedDesc;
    surfaceChainedDesc.hwnd = hwnd;
    surfaceChainedDesc.hinstance = GetModuleHandle(nullptr);
    wgpu::SurfaceDescriptor surfaceDesc;
    surfaceDesc.nextInChain = &surfaceChainedDesc;
    dawnSurface = wgpu::Instance(dawnInstance->Get()).CreateSurface(&surfaceDesc);
}

然后创建绘图上下文和Recorder

cpp 复制代码
void WindowMain::initGraphite()
{
    skgpu::graphite::DawnBackendContext backendContext;
    backendContext.fInstance = wgpu::Instance(dawnInstance->Get());
    backendContext.fDevice = dawnDevice;
    backendContext.fQueue = dawnDevice.GetQueue();
    skgpu::graphite::ContextOptions fContextOptions;
    graphiteContext = skgpu::graphite::ContextFactory::MakeDawn(backendContext, fContextOptions);
    if (!graphiteContext) {
        SkASSERT(false);
        return;
    }
    graphiteRecorder = graphiteContext->makeRecorder();
}

然后配置Dawn表面(改变窗口大小时,也得调用这个方法)

cpp 复制代码
void WindowMain::configSurface()
{    
    wgpu::SurfaceConfiguration config;
    config.device = dawnDevice;
    config.format = surfaceFormat;
    config.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding |
        wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
    config.width = w;
    config.height = h;
    //wgpu::PresentMode::Immediate 立即渲染会撕裂
    //wgpu::PresentMode::Fifo 渲染的帧内容会进入一个 FIFO(先进先出)队列,等待显示器的垂直同步信号(VSync)后再显示到屏幕上。
    config.presentMode = wgpu::PresentMode::Fifo;
    dawnSurface.Configure(&config);
}

在窗口中绘图:

cpp 复制代码
case WM_PAINT:{
    PAINTSTRUCT ps;
    BeginPaint(hWnd, &ps);
    auto surface = win->getSurface();
    win->paint(surface->getCanvas());
    win->flush();
    EndPaint(hWnd, &ps);
    return 0;
}

每次绘图都要重新获取Skia的Surface

cpp 复制代码
sk_sp<SkSurface> WindowMain::getSurface()
{
    wgpu::SurfaceTexture surfaceTexture;
    dawnSurface.GetCurrentTexture(&surfaceTexture);
    auto texture = surfaceTexture.texture;
    skgpu::graphite::DawnTextureInfo info(1,skgpu::Mipmapped::kNo, 
        surfaceFormat, wgpu::TextureUsage::None, wgpu::TextureAspect::All);
    auto backendTex = skgpu::graphite::BackendTextures::MakeDawn(texture.Get());
    auto surface = SkSurfaces::WrapBackendTexture(graphiteRecorder.get(),backendTex, 
        kBGRA_8888_SkColorType, displayParams.fColorSpace, &displayParams.fSurfaceProps);
    return surface;
}

下面是绘图逻辑:

cpp 复制代码
void WindowMain::paint(SkCanvas* canvas)
{
    canvas->clear(0xFFFFFFFF);
    SkPaint paint;
    paint.setColor(SK_ColorRED);
    SkRect rect = SkRect::MakeXYWH(w - 150, h - 150, 140, 140);
    canvas->drawRect(rect, paint);
}

最后把绘图内容同步到窗口中

cpp 复制代码
void WindowMain::flush()
{
    std::unique_ptr<skgpu::graphite::Recording> recording = graphiteRecorder->snap();
    if (recording) {
        skgpu::graphite::InsertRecordingInfo info;
        info.fRecording = recording.get();
        graphiteContext->insertRecording(info);
        graphiteContext->submit(skgpu::graphite::SyncToCpu::kNo);
    }
    dawnSurface.Present();
}

这个示例代码有一个小问题,就是改变窗口大小时,窗口画面更新不流畅,如下图所示:

目前我还没找到好得解决办法,希望懂的老师不吝赐教,必有重谢。

相关推荐
君义_noip18 小时前
信息学奥赛一本通 2134:【25CSPS提高组】道路修复 | 洛谷 P14362 [CSP-S 2025] 道路修复
c++·算法·图论·信息学奥赛·csp-s
liulilittle18 小时前
OPENPPP2 Code Analysis One
网络·c++·网络协议·信息与通信·通信
Morwit18 小时前
*【力扣hot100】 647. 回文子串
c++·算法·leetcode
天赐学c语言19 小时前
1.7 - 删除排序链表中的重要元素II && 哈希冲突常用解决冲突方法
数据结构·c++·链表·哈希算法·leecode
w陆压19 小时前
12.STL容器基础
c++·c++基础知识
coding消烦员19 小时前
在 Windows 内网搭建 Git 仓库:共享普通仓库 vs 中心 bare 仓库
windows·git
龚礼鹏20 小时前
Android应用程序 c/c++ 崩溃排查流程二——AddressSanitizer工具使用
android·c语言·c++
qq_4017004120 小时前
QT C++ 好看的连击动画组件
开发语言·c++·qt
额呃呃21 小时前
STL内存分配器
开发语言·c++
七点半77021 小时前
c++基本内容
开发语言·c++·算法