You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

D2D调试错误:设备资源关联错误ID2D1Device问题求助

问题描述

尝试从位图提取二进制数据时,调用DrawBitmap将ID2D1Bitmap绘制到ID2D1RenderTarget时触发D2D调试错误:

An operation failed because a device-dependent resource is associated with the wrong ID2D1Device (resource domain)

已确认_D2dDeviceContext5由_D2dFactory6间接创建,但对DirectX资源管理不熟悉,不敢修改资源创建顺序,怕引发其他初始化失败。

错误原因

你创建的ID2D1Bitmap是设备依赖资源,绑定在通过_D3dDevice5创建的_D2dDevice5设备上;但调用_D2dFactory6->CreateWicBitmapRenderTarget时,没有指定关联的D2D设备,系统会默认创建一个全新的独立D2D设备,导致两个资源属于不同的设备域,跨设备绘制就会触发这个错误。

解决方案

有两种可行的修复方式:

方式1:让WIC渲染目标和原Bitmap使用同一D2D设备

创建WIC位图渲染目标时,不要直接用Factory的方法,而是通过已有的_D2dDevice5创建关联的设备上下文,再绑定到WIC位图上,确保资源在同一设备域内。

方式2:直接从D2D Bitmap映射内存(更高效)

既然目标是提取像素数据,不需要通过绘制到WIC位图的方式,直接利用ID2D1Bitmap的Map方法获取内存指针,步骤更简洁高效。

修改后的代码示例

方式1:修复原绘制逻辑

#include <Windows.h>
#include <d3dcompiler.h>
#include <dxgi1_5.h>
#include <d3d11.h>
#include <d3d11_4.h>
#include <d2d1.h>
#include <d2d1_3.h>
#include <d2d1_3helper.h>
#include <wincodec.h>
#include <tchar.h>
#include <atlbase.h>
#pragma comment (lib, "d3dcompiler")
#pragma comment (lib, "D2d1")
#pragma comment (lib, "dxguid")
#pragma comment (lib, "d3d11")
#pragma comment (lib, "dxgi")


int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR pszCmdLine, _In_ int iCmdShow)
{
    CComPtr<ID2D1Device5> _D2dDevice5;
    CComPtr<ID2D1Factory> _D2dFactory;
    CComPtr<ID2D1Factory6> _D2dFactory6;
    CComPtr<ID2D1DeviceContext5> _D2dDeviceContext5;

    CComPtr<ID3D11Device> _D3dDevice;
    CComPtr<ID3D11Device5> _D3dDevice5;
    CComPtr<ID3D11DeviceContext> _D3dDeviceContext;
    CComPtr<IDXGIDevice4> _D3d_DXGI_D2d_;

    CComPtr<IWICImagingFactory2> _IWICFactory2;
    D3D_FEATURE_LEVEL featureLevelSupported;

    CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY);

    D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_DEBUG | D3D11_CREATE_DEVICE_BGRA_SUPPORT, NULL, 0, D3D11_SDK_VERSION, &_D3dDevice, &featureLevelSupported, &_D3dDeviceContext);
    _D3dDevice->QueryInterface(__uuidof(ID3D11Device5), reinterpret_cast<void**>(&_D3dDevice5));

    //D2dFactory
    D2D1_FACTORY_OPTIONS const d2dDeviceoptions = { D2D1_DEBUG_LEVEL_INFORMATION };
    D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, d2dDeviceoptions, &_D2dFactory);
    _D2dFactory->QueryInterface(__uuidof(ID2D1Factory6), reinterpret_cast<void**>(&_D2dFactory6));

    //D2dDevice
    _D3dDevice5->QueryInterface(&_D3d_DXGI_D2d_);
    _D2dFactory6->CreateDevice(_D3d_DXGI_D2d_, &_D2dDevice5);

    //D2dDeviceContext
    _D2dDevice5->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, &_D2dDeviceContext5);

    //IWICFactory
    CoCreateInstance(CLSID_WICImagingFactory2, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&_IWICFactory2));


    //
    LPCWSTR pngPath = _T("C:\\Img1.png");
    //

    CComPtr<IWICStream> stream;
    _IWICFactory2->CreateStream(&stream);
    stream->InitializeFromFilename(pngPath, GENERIC_READ);

    CComPtr<IWICBitmapDecoder> decoder;
    _IWICFactory2->CreateDecoderFromStream(stream, NULL, WICDecodeMetadataCacheOnLoad, &decoder);

    CComPtr<IWICBitmapFrameDecode> frameDecode;
    decoder->GetFrame(0, &frameDecode);

    CComPtr<IWICFormatConverter> converter;
    _IWICFactory2->CreateFormatConverter(&converter);
    converter->Initialize(
        frameDecode, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, NULL, 0, WICBitmapPaletteTypeCustom
    );

    CComPtr<ID2D1Bitmap> bitmap;
    _D2dDeviceContext5->CreateBitmapFromWicBitmap(converter, NULL, &bitmap);


    // CopyBitmapToCPU
    char* data = nullptr; // 修正原指针错误,避免非法内存访问
    size_t dataSize = 0;
    D2D1_SIZE_U bitmapSize = bitmap->GetPixelSize();

    CComPtr<IWICBitmap> wicBitmap;
    _IWICFactory2->CreateBitmap(bitmapSize.width, bitmapSize.height, GUID_WICPixelFormat32bppPBGRA, WICBitmapCacheOnLoad, &wicBitmap);
    
    // 关键修改:通过已有_D2dDevice5创建绑定到WIC位图的设备上下文
    CComPtr<ID2D1DeviceContext> targetContext;
    _D2dDevice5->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &targetContext);
    
    // 将WIC位图转换为D2D位图并设置为渲染目标
    CComPtr<ID2D1Bitmap1> wicD2dBitmap;
    targetContext->CreateBitmapFromWicBitmap(wicBitmap, nullptr, &wicD2dBitmap);
    targetContext->SetTarget(wicD2dBitmap);

    targetContext->BeginDraw();
    targetContext->DrawBitmap(bitmap); // 设备域匹配,不再报错
    targetContext->EndDraw();

    WICRect rcLock = { 0,0,static_cast<INT>(bitmapSize.width), static_cast<INT>(bitmapSize.height) };
    CComPtr<IWICBitmapLock> lock;
    wicBitmap->Lock(&rcLock, WICBitmapLockRead, &lock);

    UINT bufferSize = 0;
    unsigned char* tempTargetData = nullptr;
    lock->GetDataPointer(&bufferSize, &tempTargetData);
    if (tempTargetData != NULL) {
        data = new char[bufferSize];
        memcpy(data, tempTargetData, bufferSize);
        dataSize = static_cast<size_t>(bufferSize);
    }

    // 释放内存
    if (data) delete[] data;

    return 0;
}

方式2:直接映射D2D Bitmap内存(更高效)

// 前面初始化部分和原代码一致,省略重复代码

    CComPtr<ID2D1Bitmap> bitmap;
    _D2dDeviceContext5->CreateBitmapFromWicBitmap(converter, NULL, &bitmap);

    // CopyBitmapToCPU - 直接映射D2D Bitmap获取像素数据
    char* data = nullptr;
    size_t dataSize = 0;
    D2D1_SIZE_U bitmapSize = bitmap->GetPixelSize();

    // 将Bitmap映射到CPU内存
    D2D1_MAPPED_RECT mappedRect;
    HRESULT hr = bitmap->Map(D2D1_MAP_OPTIONS_READ, &mappedRect);
    if (SUCCEEDED(hr)) {
        UINT pixelBytes = 4; // 32bpp格式,每个像素4字节
        UINT bufferSize = bitmapSize.width * bitmapSize.height * pixelBytes;
        data = new char[bufferSize];
        
        // 按行复制,处理内存对齐问题
        for (UINT y = 0; y < bitmapSize.height; y++) {
            memcpy(
                data + y * bitmapSize.width * pixelBytes,
                reinterpret_cast<char*>(mappedRect.bits) + y * mappedRect.pitch,
                bitmapSize.width * pixelBytes
            );
        }
        dataSize = bufferSize;
        bitmap->Unmap();
    }

    // 释放内存
    if (data) delete[] data;

    return 0;
}
额外说明
  • 原代码中char** data = nullptr;存在错误,会导致memcpy写入非法地址,已修正为char* data = nullptr;
  • 方式2无需依赖WIC渲染目标,直接从D2D Bitmap获取像素数据,性能更高、步骤更简洁,推荐优先使用

内容的提问来源于stack exchange,提问作者feng lei

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 15:19:58