当前位置: 首页 > article >正文

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

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

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实例:

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

然后创建Dawn设备:

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)

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

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表面(改变窗口大小时,也得调用这个方法)

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);
}

在窗口中绘图:

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 

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;
}

下面是绘图逻辑: 

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);
}

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

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();
}

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

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


http://www.kler.cn/a/517681.html

相关文章:

  • xss靶场
  • 自然语言处理——从原理、经典模型到应用
  • CVE-2025-0411 7-zip 漏洞复现
  • Unity3D仿星露谷物语开发25之创建时钟界面
  • 【信息系统项目管理师-选择真题】2018上半年综合知识答案和详解
  • 【JavaEE进阶】Spring留言板实现
  • 反向代理模块1
  • 第五天 Labview数据记录(5.1 INI配置文件读写)
  • python+playwright自动化测试(九):expect断言和expect_xxx()元素及事件捕获
  • 隐马尔科夫模型HMM
  • HDLC,pap,chap网络
  • C语言初阶--折半查找算法
  • Titans 架构下MAC变体的探究
  • polars as pl
  • 消息队列:春招面试的重要知识模块
  • Mono里运行C#脚本34—内部函数调用的过程
  • 【Prometheus】RabbitMQ安装部署,如何通过prometheus监控RabbitMQ
  • 【qt信号槽】
  • YOLOV11改进1-检测头篇
  • QT笔记——debug模式调试
  • [Datawheel]利用Zigent框架编写智能体-2
  • 突破极限!!!20米每秒的端到端无人机自主导航
  • 三元组抽取在实际应用中如何处理语义模糊性?
  • Android GLSurfaceView 覆盖其它控件问题 (RK平台)
  • 51单片机——定时器时钟
  • 微信小程序压缩图片