feat(d3d11): expose IDXGISurface on 2D texture resources#171
Conversation
DXMT's 2D texture resources do not answer QueryInterface(IDXGISurface). Wine's Direct2D queries a backing tex2d for IDXGISurface when creating a hardware HwndRenderTarget, so creation previously failed with E_NOINTERFACE. Add an aggregated IDXGISurface facet on TResourceBase for 2D textures, mirroring the existing MTLDXGIResource aggregation: GetDesc derives a DXGI_SURFACE_DESC from the texture description; GetDevice/GetParent/ QueryInterface/private-data delegate to the owning resource; GetResource returns the parent. Map/Unmap/GetDC/ReleaseDC are left E_NOTIMPL because the current Direct2D GPU path does not exercise them. With this change ID2D1Factory::CreateHwndRenderTarget succeeds against the DXMT backend.
|
Thanks for the PRs. Do you have a program/demo that requires Direct2D functionality? |
|
I ran into this with Windows apps that draw their UI with Direct2D under Wine on macOS. On the DXMT backend Here's a small self-contained Direct2D demo I used to verify both PRs end to end — it opens a window, creates a hardware // Minimal Direct2D demo: opens a window, creates a hardware HwndRenderTarget,
// and runs a BeginDraw -> Clear -> draw -> EndDraw loop for a few seconds.
#include <windows.h>
#include <d2d1.h>
#include <cstdio>
static ID2D1Factory *g_factory = nullptr;
static ID2D1HwndRenderTarget *g_rt = nullptr;
static int g_enddraw_logged = 0;
static void log_hr(const char *name, HRESULT hr) {
std::printf("%s: 0x%08lx %s\n", name, (unsigned long)hr, SUCCEEDED(hr) ? "OK" : "FAIL");
std::fflush(stdout);
}
static void create_target(HWND hwnd) {
RECT rc;
GetClientRect(hwnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
HRESULT hr = g_factory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(hwnd, size), &g_rt);
log_hr("CreateHwndRenderTarget", hr);
}
static void render() {
if (!g_rt)
return;
g_rt->BeginDraw();
g_rt->Clear(D2D1::ColorF(0.05f, 0.10f, 0.30f));
ID2D1SolidColorBrush *brush = nullptr;
if (SUCCEEDED(g_rt->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Red), &brush)) && brush) {
g_rt->FillRectangle(D2D1::RectF(40, 40, 200, 140), brush);
brush->Release(); brush = nullptr;
}
if (SUCCEEDED(g_rt->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::LimeGreen), &brush)) && brush) {
g_rt->DrawRectangle(D2D1::RectF(60, 90, 280, 230), brush, 6.0f);
brush->Release(); brush = nullptr;
}
if (SUCCEEDED(g_rt->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), &brush)) && brush) {
g_rt->DrawLine(D2D1::Point2F(0, 0), D2D1::Point2F(380, 260), brush, 3.0f);
g_rt->DrawLine(D2D1::Point2F(380, 0), D2D1::Point2F(0, 260), brush, 3.0f);
g_rt->FillEllipse(D2D1::Ellipse(D2D1::Point2F(300, 80), 40, 40), brush);
brush->Release(); brush = nullptr;
}
HRESULT hr = g_rt->EndDraw();
if (!g_enddraw_logged) { log_hr("EndDraw(first frame)", hr); g_enddraw_logged = 1; }
}
static LRESULT CALLBACK WndProc(HWND h, UINT m, WPARAM w, LPARAM l) {
switch (m) {
case WM_PAINT: render(); ValidateRect(h, nullptr); return 0;
case WM_DESTROY: PostQuitMessage(0); return 0;
}
return DefWindowProcW(h, m, w, l);
}
int main() {
HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &g_factory);
log_hr("D2D1CreateFactory", hr);
if (FAILED(hr)) return 1;
HINSTANCE inst = GetModuleHandleW(nullptr);
WNDCLASSW wc = {};
wc.lpfnWndProc = WndProc; wc.hInstance = inst; wc.lpszClassName = L"D2DDemo";
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
RegisterClassW(&wc);
HWND hwnd = CreateWindowExW(0, L"D2DDemo", L"Direct2D DXMT demo",
WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
nullptr, nullptr, inst, nullptr);
if (!hwnd) { std::printf("CreateWindowExW failed\n"); return 1; }
create_target(hwnd);
ShowWindow(hwnd, SW_SHOW); UpdateWindow(hwnd);
DWORD start = GetTickCount(); int frames = 0; MSG msg; bool quit = false;
while (!quit) {
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) { quit = true; break; }
TranslateMessage(&msg); DispatchMessageW(&msg);
}
render(); frames++; Sleep(50);
if (GetTickCount() - start > 8000) break;
}
std::printf("frames=%d\ndemo finished\n", frames);
if (g_rt) g_rt->Release();
if (g_factory) g_factory->Release();
return 0;
}Build (mingw-w64): x86_64-w64-mingw32-g++ -std=c++17 -O2 -static-libgcc -static-libstdc++ \
d2d_demo.cpp -o d2d_demo.exe -ld2d1 -lole32 -luuid -luser32 -lgdi32Behavior on the DXMT backend:
Tested on macOS (Apple M-series GPU) with a CrossOver/Wine runtime. |
Summary
DXMT's 2D texture resources don't answer
QueryInterface(IDXGISurface). When Wine's Direct2D creates a hardwareHwndRenderTarget, it creates a backing D3D texture and queries it forIDXGISurface; without it,CreateHwndRenderTargetfails withE_NOINTERFACE.This adds an aggregated
IDXGISurfacefacet onTResourceBasefor 2D textures, mirroring the existingMTLDXGIResourceaggregation. It forwards to the owning resource:GetDescderives aDXGI_SURFACE_DESCfrom the texture descriptionGetDevice/GetParent/QueryInterface/ private-data delegate to the resourceGetResourcereturns the parent resourceMap/Unmap/GetDC/ReleaseDCare leftE_NOTIMPL, since the current Direct2D GPU path doesn't exercise themOnly 2D textures expose the facet (guarded by
if constexpr), matching where Direct2D queries it.Testing
Verified on macOS with a CrossOver/Wine runtime on an Apple M-series GPU. With this change,
ID2D1Factory::CreateHwndRenderTargetsucceeds against the DXMT backend (previouslyE_NOINTERFACE). Full Direct2D rendering additionally needsSwapDeviceContextState, submitted as a separate PR.