Context
I have 2 big projects I'm working on: a game engine, and a file explorer replacement. My focus is Windows only. I know they're 2 huge things I'll likely never complete, I just enjoy coding during free time.
Across my many projects I started building up a collection of utilities and essentially made my own library. I'm actually enjoying writing generalized stuff.
That's the tale of how I started trying to make a generic-ish windowing framework for Windows.
A big requirement that libs like glfw and SFML are missing is modularity; my windows are made up of multiple modules, which avoids having a ginormous procedure function that handles everything, and lets me extend the window with future specific modules (I was working on a vulkan module, now i'm working on a direct2d module). Hence all the code you'll be seeing in my snippets related to HWND
s and window procecdures only handles drawing-related messages, which I hope will make navigation easier. Don't worry about the absence of all the WM_CREATE
and other kind of messages unrelated to drawing in these snippets.
For the game engine all I need is "updated" direct2d drawing, so with the newer DeviceContext
that goes through DXGISwapChain
etcc instead of the older RenderTarget
.
For the file explorer I "need" (well, ok, want) window transparency, which with the older RenderTarget
"just works" (you pass it the ALPHA_PREMULTIPLIED
flag and everything is nice).
I can also get a DeviceContext
from the older RenderTarget
as answered by Paint.NET's creator here; that gives me access to newer functions even though the context was cast from the older RenderTarget
. Important: this approach gives me no control over which device that context is associated with.
For the file explorer I also wish to share resources across multiple windows, and the older RenderTarget
doesn't seem to allow for that. Resources will be bound to the individual RenderTarget
, while with DeviceContext
s the resources should be bound to a Device
and can be accessed by all the contexts associated with the same Device
(at least for my understanding so far).
Q: correct me if I'm wrong about last sentence^
Where the pain begun
Here comes the dreaded issue: creating a swapchain with CreateSwapChainForHWND
does not support any ALPHA_*
option besides ALPHA_NONE
. So if I try to draw on a window the DeviceContext
and retain control of which Device
creates them I need to find another way.
In short:
- One window + DeviceContext newer functions + transparency -> use
RenderTarget
and cast toDeviceContext
- Resources shared across multiple windows + DeviceContext newer functions -> use
DXGISwapChain
+DeviceContext
- Resources shared across multiple windows + DeviceContext newer functions + transparency ->
¯\_(ツ)_/¯
+???
The debug layer told me CreateSwapChainForComposition
does support ALPHA_PREMULTIPLIED
, but at that point a whole separate topic of Windows APIs seems to open up, and I've already a huge chunk of DirectX to learn, so I'd rather avoid further months-long-derailings.
But it seems there's no avoiding this issue.
Anyway, I created my CreateSwapChainForComposition
swapchain and... That function takes no HWND
parameter! So I assume I did create a swapchain somewhere, and direct2d calls are drawing on that swapchain, but the content of that swapchain never reaches the actual window, unless I do some magic with the compoisition APIs perhaps?
Then I started giving a look at those composition APIs and... pretty much everything assumes you're working on an higher level framework, mostly XAML, which I'm not.
The closest thing I found in the docs is this
However that example retreives a direct2d device context from the composition APIs through BeginDraw
... there's no swapchain in sight.
And even worse this brings me back to the same issue I had with RenderTarget
: If I've no guarantee the DeviceContext
s are created from the same Device, so I cannot share resources across multiple windows.
Plus having to learn a new whole topic just to have 2 things working together that I already managed to make work individually really discouraged me.
My code, notes
Notes
d2d::*
, dxgi::*
, d3d::*
classes are just wrappers around MS's ComPtr
where I gave various classes a throwing constructor that wraps the "return HRESULT, reference actual return object" functions from MS.
the procedure_result
return value of my window modules procedures exist because multiple modules can work on the same message, the behaviour is as follows:
procedure_result.next()
this module didn't return any value, let next module evaluate the message.procedure_result.stop(value)
this module returnedvalue
, no other module will evaluate the message.procedure_result.next(value)
this module returnedvalue
, let next module evaluate the message.
After all modules have processed the message, or after one module called stop(), return the value returned by the last module that evaluated the current message, or pass to the default window procedure if no module returned any value.
I use whitesmiths indentation. I know it's not really widespread but please don't kill me for it :)
If you want to try my code, the project expects you have my CPP_Utils repository in the same directory as follows:
root
> CPP_Utilities_MS //the project we're discussing
> .git
> ...
> CPP_Utilities
> .git
> ...
The CPP_Utilities repository is entirely header-only because screw compile times, so no need to link .libs or add .dlls
For record-keeping reasons I'll leave links to the last commit to the date this question was asked:
...now to the actual directly meaningful code:
How I make my window transparent
inline bool make_glass_CompositionAttribute(HWND hwnd)
{
if (HMODULE hUser = GetModuleHandleA("user32.dll"))
{
//Windows >= 10
pfnSetWindowCompositionAttribute SetWindowCompositionAttribute = (pfnSetWindowCompositionAttribute)GetProcAddress(hUser, "SetWindowCompositionAttribute");
if (SetWindowCompositionAttribute)
{
ACCENT_POLICY accent = {ACCENT_ENABLE_BLURBEHIND, 0, 0, 0};
WINDOWCOMPOSITIONATTRIBDATA data;
data.Attrib = WCA_ACCENT_POLICY;
data.pvData = &accent;
data.cbData = sizeof(accent);
SetWindowCompositionAttribute(hwnd, &data);
return true;
}
}
return false;
}
How I create my RenderTarget
and get a DeviceContext
out of it
used for (1): One window + DeviceContext newer functions + transparency
Create d2d factory
namespace d2d
{
struct factory : details::com_ptr<ID2D1Factory6>
{
using com_ptr::com_ptr;
factory() : com_ptr{[]
{
D2D1_FACTORY_OPTIONS options
{
.debugLevel{details::enable_debug_layer ? D2D1_DEBUG_LEVEL_INFORMATION : D2D1_DEBUG_LEVEL_NONE}
};
self_t ret{nullptr};
details::throw_if_failed(D2D1CreateFactory
(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
options,
ret.address_of()
));
return ret;
}()} {}
};
}
Create RenderTarget from d2d factory and HWND
namespace d2d
{
struct hwnd_render_target : details::com_ptr<ID2D1HwndRenderTarget>
{
using com_ptr::com_ptr;
hwnd_render_target(const factory& factory, const HWND& hwnd) : com_ptr{[&factory, &hwnd]
{
self_t ret{nullptr};
D2D1_RENDER_TARGET_PROPERTIES properties
{
.type{D2D1_RENDER_TARGET_TYPE_DEFAULT},
.pixelFormat
{
.format{DXGI_FORMAT_UNKNOWN},
.alphaMode{D2D1_ALPHA_MODE_PREMULTIPLIED}
}
};
details::throw_if_failed(factory->CreateHwndRenderTarget(properties, D2D1::HwndRenderTargetProperties(hwnd), ret.address_of()));
return ret;
}()} {}
};
}
Create DeviceContext from RenderTarget
namespace d2d
{
class device_context : public details::com_ptr<ID2D1DeviceContext5>
{
public:
using com_ptr::com_ptr;
device_context(const d2d::hwnd_render_target& hwnd_rt) : com_ptr{create(hwnd_rt )} {}
private:
inline static self_t create(const d2d::hwnd_render_target& hwnd_rt)
{
return hwnd_rt.as<interface_type>();
}
};
}
Window module that makes use of all that
//window modules file/namespace
class render_target : public utils::MS::window::module
{
public:
using on_draw_signature = void(utils::MS::window::base&, const d2d::device_context&);
struct create_info
{
using module_type = render_target;
const d2d::factory& d2d_factory;
std::function<on_draw_signature> on_render;
//adds the WS_EX_NOREDIRECTIONBITMAP flag to the flags used to create the base window
inline void adjust_base_create_info(utils::MS::window::base::create_info& base_create_info) const noexcept
{
base_create_info.style_ex |= WS_EX_NOREDIRECTIONBITMAP;
}
};
render_target(utils::MS::window::base& base, create_info create_info) :
module{base},
on_render{create_info.on_render},
d2d_hwnd_rt{create_info.d2d_factory, get_base().get_handle()},
d2d_device_context{d2d_hwnd_rt}
{
}
std::function<on_draw_signature> on_render;
void present() noexcept
{
}
private:
d2d::hwnd_render_target d2d_hwnd_rt;
d2d::device_context d2d_device_context;
virtual utils::MS::window::procedure_result procedure(UINT msg, WPARAM wparam, LPARAM lparam) override
{
switch (msg)
{
case WM_SIZE:
on_resize({LOWORD(lparam), HIWORD(lparam)});
return utils::MS::window::procedure_result::next(0);
case WM_DISPLAYCHANGE:
//InvalidateRect(get_handle(), NULL, FALSE);
break;
case WM_ERASEBKGND: return utils::MS::window::procedure_result::stop(1);
case WM_PAINT:
if (on_render)
{
on_render(get_base(), d2d_device_context);
ValidateRect(get_base().get_handle(), NULL);
return utils::MS::window::procedure_result::next(0);
}
break;
}
return utils::MS::window::procedure_result::next();
}
void on_resize(utils::math::vec2u size)
{
d2d_hwnd_rt->Resize({size.x, size.y});
}
};
How I create my DeviceContext
from a DXGISwapChain
used for (2): Resources shared across multiple windows + DeviceContext newer functions, does NOT support transparency
Create d3d device
namespace d3d
{
class device : public details::com_ptr<ID3D11Device2>
{
public:
using com_ptr::com_ptr;
device() : com_ptr{create()} {}
private:
inline static self_t create()
{
details::com_ptr<ID3D11Device> base_device;
UINT creation_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
if constexpr (details::enable_debug_layer)
{
// If the project is in a debug build, enable debugging via SDK Layers with this flag.
creation_flags |= D3D11_CREATE_DEVICE_DEBUG;
}
D3D_FEATURE_LEVEL feature_level_created;
std::array<D3D_DRIVER_TYPE, 3> attempts{D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP, D3D_DRIVER_TYPE_SOFTWARE};
HRESULT last{S_FALSE};
for (const auto& attempt : attempts)
{
last = D3D11CreateDevice
(
nullptr, // specify null to use the default adapter
attempt,
0,
creation_flags, // optionally set debug and Direct2D compatibility flags
nullptr, // use the lastest feature level
0, // use the lastest feature level
D3D11_SDK_VERSION,
base_device.address_of(), // returns the Direct3D device created
&feature_level_created, // returns feature level of device created
nullptr
);
if (details::succeeded(last)) { break; }
}
details::throw_if_failed(last);
return base_device.as<self_t>();
}
};
}
Get dxgi device from d3d device
namespace dxgi
{
struct device : details::com_ptr<IDXGIDevice3>
{
using com_ptr::com_ptr;
device(const d3d::device& d3d_device) : com_ptr{[&d3d_device]
{
return d3d_device.as<interface_type>();
}()} {}
};
}
Create dxgi swapchain from dxgi device for HWND
namespace dxgi
{
class swap_chain : public details::com_ptr<IDXGISwapChain1>
{
public:
swap_chain(const dxgi::device& dxgi_device, HWND hwnd) : com_ptr{create(dxgi_device, hwnd)} {}
void resize(utils::math::vec2u size)
{
HRESULT hresult{get()->ResizeBuffers(2, size.x, size.y, DXGI_FORMAT_B8G8R8A8_UNORM, 0)};
if (hresult == DXGI_ERROR_DEVICE_REMOVED || hresult == DXGI_ERROR_DEVICE_RESET)
{
throw std::runtime_error("Device removed or reset");
}
else { details::throw_if_failed(hresult); }
}
void present()
{
HRESULT hresult{get()->Present(1, 0)};
if (hresult == DXGI_ERROR_DEVICE_REMOVED || hresult == DXGI_ERROR_DEVICE_RESET)
{
throw std::runtime_error("Device removed or reset");
}
else { details::throw_if_failed(hresult); }
}
private:
inline static self_t create(const dxgi::device& dxgi_device, HWND hwnd)
{
RECT client_rect{0, 0, 0, 0};
GetClientRect(hwnd, &client_rect);
utils::math::rect<long> rectl{.ll{client_rect.left}, .up{client_rect.top}, .rr{client_rect.right}, .dw{client_rect.bottom}};
details::com_ptr<IDXGIAdapter> dxgi_adapter;
details::throw_if_failed(dxgi_device->GetAdapter(dxgi_adapter.address_of()));
details::com_ptr<IDXGIFactory2> dxgi_factory;
details::throw_if_failed(dxgi_adapter->GetParent(IID_PPV_ARGS(dxgi_factory.address_of())));
DXGI_SWAP_CHAIN_DESC1 desc
{
.Width {static_cast<UINT>(rectl.w())},
.Height {static_cast<UINT>(rectl.h())},
.Format {DXGI_FORMAT_B8G8R8A8_UNORM},
.Stereo {false},
.SampleDesc
{
.Count {1},
.Quality{0}
},
.BufferUsage {DXGI_USAGE_RENDER_TARGET_OUTPUT},
.BufferCount {2},
.Scaling {DXGI_SCALING_NONE},
.SwapEffect {DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL},
.AlphaMode {DXGI_ALPHA_MODE_IGNORE},
//.AlphaMode {DXGI_ALPHA_MODE_PREMULTIPLIED}, // I wish it was this easy!
.Flags {0},
};
DXGI_SWAP_CHAIN_FULLSCREEN_DESC desc_fullscreen
{
.RefreshRate{.Numerator{1}, .Denominator{0}},
.Scaling {DXGI_MODE_SCALING_CENTERED},
};
self_t ret{nullptr};
details::throw_if_failed(dxgi_factory->CreateSwapChainForHwnd(dxgi_device.get(), hwnd, &desc, &desc_fullscreen, nullptr, ret.address_of()));
dxgi_device->SetMaximumFrameLatency(1);
return ret;
}
}
}
Create d2d device from dxgi device, and create d2d device context from d2d device
namespace d2d
{
class device : public details::com_ptr<ID2D1Device5>
{
public:
using com_ptr::com_ptr;
device(const d2d::factory& d2d_factory, const dxgi::device& dxgi_device) : com_ptr{create(d2d_factory, dxgi_device)} {}
dxgi::device get_dxgi_device() const noexcept
{
details::com_ptr<IDXGIDevice> ret{nullptr};
details::throw_if_failed(get()->GetDxgiDevice(ret.address_of()));
return ret.as<dxgi::device>();
}
private:
inline static self_t create(const d2d::factory& d2d_factory, const dxgi::device& dxgi_device)
{
self_t ret{nullptr};
details::throw_if_failed(d2d_factory->CreateDevice(dxgi_device.get(), ret.address_of()));
return ret;
}
};
class device_context : public details::com_ptr<ID2D1DeviceContext5>
{
public:
using com_ptr::com_ptr;
device_context(const d2d::device& d2d_device) : com_ptr{create(d2d_device)} {}
private:
inline static self_t create(const d2d::device& d2d_device)
{
self_t ret{nullptr};
details::throw_if_failed(d2d_device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, ret.address_of()));
return ret;
}
};
}
Create d2d bitmap from d2d device context and dxgi swapchain
namespace d2d
{
class bitmap : public details::com_ptr<ID2D1Bitmap1>
{
public:
using com_ptr::com_ptr;
bitmap(const d2d::device_context& d2d_device_context, const dxgi::swap_chain& dxgi_swapchain) : com_ptr{create(d2d_device_context, dxgi_swapchain)} {}
private:
inline static self_t create(const d2d::device_context& d2d_device_context, const dxgi::swap_chain& dxgi_swapchain)
{
//details::com_ptr<ID3D11Texture2D> d3d_texture_back_buffer;
//details::throw_if_failed(dxgi_swapchain->GetBuffer(0, IID_PPV_ARGS(d3d_texture_back_buffer.address_of())));
details::com_ptr<IDXGISurface2> dxgi_back_buffer;
details::throw_if_failed(dxgi_swapchain->GetBuffer(0, IID_PPV_ARGS(dxgi_back_buffer.address_of())));
D2D1_BITMAP_PROPERTIES1 properties
{
.pixelFormat{DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED},
.dpiX{1},//TODO dpi stuff
.dpiY{1},
.bitmapOptions{D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW},
};
self_t ret{nullptr};
details::throw_if_failed(d2d_device_context->CreateBitmapFromDxgiSurface(dxgi_back_buffer.get(), &properties, ret.address_of()));
return ret;
}
};
}
Window module that makes use of all that
//window modules file/namespace
class swap_chain: public utils::MS::window::module
{
public:
using on_draw_signature = void(utils::MS::window::base&, const d2d::device_context&);
struct create_info
{
using module_type = swap_chain;
const d2d ::device& d2d_device;
std ::function<on_draw_signature> on_render;
inline void adjust_base_create_info(utils::MS::window::base::create_info& base_create_info) const noexcept
{
base_create_info.style_ex |= WS_EX_NOREDIRECTIONBITMAP;
}
};
swap_chain(utils::MS::window::base& base, create_info create_info) :
module{base},
on_render{create_info.on_render},
d2d_device_context{create_info.d2d_device},
dxgi_swapchain{create_info.d2d_device.get_dxgi_device(), get_base().get_handle()},
d2d_bitmap_target{d2d_device_context, dxgi_swapchain}
{
d2d_device_context->SetTarget(d2d_bitmap_target.get());
}
std::function<on_draw_signature> on_render;
private:
d2d::device_context d2d_device_context;
dxgi::swap_chain dxgi_swapchain;
d2d::bitmap d2d_bitmap_target;
virtual utils::MS::window::procedure_result procedure(UINT msg, WPARAM wparam, LPARAM lparam) override
{
switch (msg)
{
case WM_SIZE:
on_resize({LOWORD(lparam), HIWORD(lparam)});
return utils::MS::window::procedure_result::next(0);
case WM_DISPLAYCHANGE:
//InvalidateRect(get_handle(), NULL, FALSE);
break;
case WM_ERASEBKGND: return utils::MS::window::procedure_result::stop(1);
case WM_PAINT:
if (on_render)
{
on_render(get_base(), d2d_device_context);
dxgi_swapchain.present();
ValidateRect(get_base().get_handle(), NULL);
return utils::MS::window::procedure_result::next(0);
}
break;
}
return utils::MS::window::procedure_result::next();
}
void on_resize(utils::math::vec2u size)
{
//Release things that reference the swapchain before resizing
d2d_device_context->SetTarget(nullptr);
d2d_bitmap_target.reset();
dxgi_swapchain.resize(size);
//re-get back buffer
d2d_bitmap_target = d2d::bitmap{d2d_device_context, dxgi_swapchain};
d2d_device_context->SetTarget(d2d_bitmap_target.get());
}
};
Attempt at creating a DeviceContext
from a DXGISwapChain
WITH transparency and somhow composition (questions here)
would be used for (3): Resources shared across multiple windows + DeviceContext newer functions + transparency
This just doesn't work. I followed the guide linked before, but as mentioned it bypasses/does not consider the manual creation of a swapchain. I didn't even get it working since what the example is doing doesn't seem to lead me in the right direction. The following code is relevant more for my commented questions than for actual code
Create dxgi swapchain from dxgi device using the CreateSwapChainForComposition method
namespace dxgi
{
class swap_chain : public details::com_ptr<IDXGISwapChain1>
{
public:
//that nullptr_t is just a temporary flag for testing purposes, to distinguish from the other constructor used in the previous snippets
swap_chain(const dxgi::device& dxgi_device, HWND hwnd, nullptr_t) : com_ptr{create_composition(dxgi_device, hwnd)} {}
void resize(utils::math::vec2u size)
{
HRESULT hresult{get()->ResizeBuffers(2, size.x, size.y, DXGI_FORMAT_B8G8R8A8_UNORM, 0)};
if (hresult == DXGI_ERROR_DEVICE_REMOVED || hresult == DXGI_ERROR_DEVICE_RESET)
{
throw std::runtime_error("Device removed or reset");
}
else { details::throw_if_failed(hresult); }
}
void present()
{
HRESULT hresult{get()->Present(1, 0)};
if (hresult == DXGI_ERROR_DEVICE_REMOVED || hresult == DXGI_ERROR_DEVICE_RESET)
{
throw std::runtime_error("Device removed or reset");
}
else { details::throw_if_failed(hresult); }
}
private:
inline static self_t create_composition(const dxgi::device& dxgi_device, HWND hwnd)
{
RECT client_rect{0, 0, 0, 0};
GetClientRect(hwnd, &client_rect);
utils::math::rect<long> rectl{.ll{client_rect.left}, .up{client_rect.top}, .rr{client_rect.right}, .dw{client_rect.bottom}};
details::com_ptr<IDXGIAdapter> dxgi_adapter;
details::throw_if_failed(dxgi_device->GetAdapter(dxgi_adapter.address_of()));
details::com_ptr<IDXGIFactory2> dxgi_factory;
details::throw_if_failed(dxgi_adapter->GetParent(IID_PPV_ARGS(dxgi_factory.address_of())));
DXGI_SWAP_CHAIN_DESC1 desc
{
.Width {static_cast<UINT>(rectl.w())},
.Height {static_cast<UINT>(rectl.h())},
.Format {DXGI_FORMAT_B8G8R8A8_UNORM},
.Stereo {false},
.SampleDesc
{
.Count {1},
.Quality{0}
},
.BufferUsage {DXGI_USAGE_RENDER_TARGET_OUTPUT},
.BufferCount {2},
.Scaling {DXGI_SCALING_STRETCH},
.SwapEffect {DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL},
.AlphaMode {DXGI_ALPHA_MODE_PREMULTIPLIED},
.Flags {DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER},
};
self_t ret{nullptr};
details::throw_if_failed(dxgi_factory->CreateSwapChainForComposition(dxgi_device.get(), &desc, nullptr, ret.address_of()));
dxgi_device->SetMaximumFrameLatency(1);
return ret;
}
};
}
Composition wrappers, pretty straightforward
namespace composition
{
struct device : details::com_ptr<IDCompositionDevice>
{
using com_ptr::com_ptr;
device(const dxgi::device& dxgi_device) : com_ptr{[&dxgi_device]
{
self_t ret{nullptr};
details::throw_if_failed(DCompositionCreateDevice(dxgi_device.get(), __uuidof(interface_type), reinterpret_cast<void**>(ret.address_of())));
return ret;
}()} {}
};
struct target : details::com_ptr<IDCompositionTarget>
{
using com_ptr::com_ptr;
target(const composition::device& composition_device, HWND hwnd) : com_ptr{[&composition_device, &hwnd]
{
self_t ret{nullptr};
details::throw_if_failed(composition_device->CreateTargetForHwnd(hwnd, TRUE, ret.address_of()));
return ret;
}()} {}
};
struct visual : details::com_ptr<IDCompositionVisual>
{
using com_ptr::com_ptr;
visual(const composition::device& composition_device) : com_ptr{[&composition_device]
{
self_t ret{nullptr};
details::throw_if_failed(composition_device->CreateVisual(ret.address_of()));
return ret;
}()} {}
};
struct surface : details::com_ptr<IDCompositionSurface>
{
using com_ptr::com_ptr;
surface(const composition::device& composition_device, HWND hwnd) : com_ptr{[&composition_device, &hwnd]
{
self_t ret{nullptr};
details::throw_if_failed(composition_device->CreateSurface(128, 128, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_ALPHA_MODE_PREMULTIPLIED, ret.address_of()));
return ret;
}()} {}
};
}
Final window for the composition swapchain, questions and doubts are in the comments of this snippet
//window modules file/namespace
class composition_swap_chain : public utils::MS::window::module
{
public:
using on_draw_signature = void(utils::MS::window::base&, const d2d::device_context&);
struct create_info
{
using module_type = composition_swap_chain;
const d2d ::device& d2d_device;
std ::function<on_draw_signature> on_render;
inline void adjust_base_create_info(utils::MS::window::base::create_info& base_create_info) const noexcept
{
base_create_info.style_ex |= WS_EX_NOREDIRECTIONBITMAP;
}
};
composition_swap_chain(utils::MS::window::base& base, create_info create_info) :
module{base},
on_render{create_info.on_render},
// from here...
d2d_device_context{create_info.d2d_device},
dxgi_swapchain{create_info.d2d_device.get_dxgi_device(), get_base().get_handle(), nullptr/*flag to create swapchain with the "ForComposition" function*/},
d2d_bitmap_target{d2d_device_context, dxgi_swapchain}
// ...to here, I get the swapchain I wish to use
// from here...
composition_device{create_info.d2d_device.get_dxgi_device()},
composition_surface{composition_device, get_base().get_handle()},
// ...to here I started initializing some composition stuff
// that I can't seem to be able to "connect" to the previously
// created swapchain in any way
{
// I wish to target the bitmap from the swapchain backbuffer,
// but again this isn't connected in any way to the composition stuff
d2d_device_context->SetTarget(d2d_bitmap_target.get());
}
std::function<on_draw_signature> on_render;
private:
d2d::device_context d2d_device_context;
composition::device composition_device;
composition::surface composition_surface;
dxgi::swap_chain dxgi_swapchain;
d2d::bitmap d2d_bitmap_target;
virtual utils::MS::window::procedure_result procedure(UINT msg, WPARAM wparam, LPARAM lparam) override
{
switch (msg)
{
case WM_SIZE:
on_resize({LOWORD(lparam), HIWORD(lparam)});
return utils::MS::window::procedure_result::next(0);
case WM_DISPLAYCHANGE:
//InvalidateRect(get_handle(), NULL, FALSE);
break;
case WM_ERASEBKGND: return utils::MS::window::procedure_result::stop(1);
case WM_PAINT:
if (on_render)
{
// If BeginDraw gives me a d2dDevice context...
POINT offset{}; //?
auto comp_surf_interop{composition_surface.as<ABI::Windows::UI::Composition::ICompositionDrawingSurfaceInterop>()};
auto HR{comp_surf_interop->BeginDraw(nullptr, __uuidof(ID2D1DeviceContext5), reinterpret_cast<void**>(d2d_device_context.address_of()), &offset)};
if (SUCCEEDED(HR))
{
on_render(get_base(), d2d_device_context);
// ...what am I supposed to do with the swapchain?
//dxgi_swapchain.present();
details::throw_if_failed(composition_surface->EndDraw());
ValidateRect(get_base().get_handle(), NULL);
}
else
{
std::cout << details::hr_to_string(HR) << std::endl;
}
return utils::MS::window::procedure_result::next(0);
}
break;
}
return utils::MS::window::procedure_result::next();
}
void on_resize(utils::math::vec2u size)
{
// So do I resize a swapchain or not?
}
};