Skip to content

Commit a63bce6

Browse files
committed
Implement a DXGI interop swapchain.
1 parent b1d742a commit a63bce6

File tree

6 files changed

+836
-14
lines changed

6 files changed

+836
-14
lines changed

application/platforms/application_sdl3.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,19 @@ struct WSIPlatformSDL : GraniteWSIPlatform
259259
});
260260
}
261261

262+
uintptr_t get_native_window() override
263+
{
264+
#ifdef _WIN32
265+
SDL_PropertiesID props = SDL_GetWindowProperties(window);
266+
SDL_LockProperties(props);
267+
auto hwnd = static_cast<HWND>(SDL_GetProperty(props, "SDL.window.win32.hwnd", nullptr));
268+
SDL_UnlockProperties(props);
269+
return reinterpret_cast<uintptr_t>(hwnd);
270+
#else
271+
return 0;
272+
#endif
273+
}
274+
262275
void toggle_fullscreen()
263276
{
264277
bool is_fullscreen = (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) != 0;

vulkan/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ add_granite_internal_lib(granite-vulkan
2929
query_pool.cpp query_pool.hpp
3030
texture/texture_format.cpp texture/texture_format.hpp)
3131

32+
if (WIN32)
33+
target_sources(granite-vulkan PRIVATE wsi_dxgi.cpp wsi_dxgi.hpp)
34+
endif()
35+
3236
target_include_directories(granite-vulkan PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
3337

3438
if (GRANITE_RENDERDOC_CAPTURE)

vulkan/wsi.cpp

+209-14
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ uintptr_t WSIPlatform::get_fullscreen_monitor()
8383
return 0;
8484
}
8585

86+
uintptr_t WSIPlatform::get_native_window()
87+
{
88+
return 0;
89+
}
90+
8691
const VkApplicationInfo *WSIPlatform::get_application_info()
8792
{
8893
return nullptr;
@@ -158,6 +163,12 @@ bool WSI::init_device()
158163
device = Util::make_handle<Device>();
159164
device->set_context(*context);
160165
platform->event_device_created(device.get());
166+
167+
#ifdef _WIN32
168+
dxgi.reset(new DXGIInteropSwapchain);
169+
if (!dxgi->init_interop_device(*device))
170+
dxgi.reset();
171+
#endif
161172
return true;
162173
}
163174

@@ -166,24 +177,106 @@ bool WSI::init_device(DeviceHandle device_handle)
166177
VK_ASSERT(context);
167178
device = std::move(device_handle);
168179
platform->event_device_created(device.get());
180+
181+
#ifdef _WIN32
182+
dxgi.reset(new DXGIInteropSwapchain);
183+
if (!dxgi->init_interop_device(*device))
184+
dxgi.reset();
185+
#endif
169186
return true;
170187
}
171188

189+
#ifdef _WIN32
190+
bool WSI::init_surface_swapchain_dxgi(unsigned width, unsigned height)
191+
{
192+
if (!dxgi)
193+
return false;
194+
195+
// Anything fancy like compute present cannot use DXGI.
196+
if (current_extra_usage)
197+
return false;
198+
199+
HWND hwnd = reinterpret_cast<HWND>(platform->get_native_window());
200+
if (!hwnd)
201+
return false;
202+
203+
VkSurfaceFormatKHR format = {};
204+
switch (current_backbuffer_format)
205+
{
206+
case BackbufferFormat::UNORM:
207+
format = { VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
208+
break;
209+
210+
case BackbufferFormat::sRGB:
211+
format = { VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
212+
break;
213+
214+
case BackbufferFormat::HDR10:
215+
format = { VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_HDR10_ST2084_EXT };
216+
break;
217+
}
218+
219+
constexpr unsigned num_images = 3;
220+
221+
if (!dxgi->init_swapchain(hwnd, format, width, height, num_images))
222+
return false;
223+
224+
LOGI("Initialized DXGI interop swapchain!\n");
225+
226+
swapchain_width = width;
227+
swapchain_height = height;
228+
swapchain_aspect_ratio = platform->get_aspect_ratio();
229+
swapchain_current_prerotate = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
230+
swapchain_surface_format = dxgi->get_current_surface_format();
231+
has_acquired_swapchain_index = false;
232+
233+
const uint32_t queue_present_support = 1u << context->get_queue_info().family_indices[QUEUE_INDEX_GRAPHICS];
234+
device->set_swapchain_queue_family_support(queue_present_support);
235+
236+
swapchain_images.clear();
237+
for (unsigned i = 0; i < num_images; i++)
238+
swapchain_images.push_back(dxgi->get_vulkan_image(i));
239+
240+
device->init_swapchain(swapchain_images, swapchain_width, swapchain_height,
241+
swapchain_surface_format.format,
242+
swapchain_current_prerotate,
243+
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
244+
platform->get_frame_timer().reset();
245+
246+
platform->event_swapchain_destroyed();
247+
platform->event_swapchain_created(device.get(), swapchain, swapchain_width, swapchain_height,
248+
swapchain_aspect_ratio, num_images,
249+
swapchain_surface_format.format,
250+
swapchain_surface_format.colorSpace,
251+
swapchain_current_prerotate);
252+
253+
return true;
254+
}
255+
#endif
256+
172257
bool WSI::init_surface_swapchain()
173258
{
174259
VK_ASSERT(surface == VK_NULL_HANDLE);
175260
VK_ASSERT(context);
176261
VK_ASSERT(device);
177262

263+
unsigned width = platform->get_surface_width();
264+
unsigned height = platform->get_surface_height();
265+
266+
#ifdef _WIN32
267+
if (init_surface_swapchain_dxgi(width, height))
268+
return true;
269+
else
270+
dxgi.reset();
271+
#endif
272+
178273
surface = platform->create_surface(context->get_instance(), context->get_gpu());
179274
if (surface == VK_NULL_HANDLE)
180275
{
181276
LOGE("Failed to create VkSurfaceKHR.\n");
182277
return false;
183278
}
184279

185-
unsigned width = platform->get_surface_width();
186-
unsigned height = platform->get_surface_height();
187280
swapchain_aspect_ratio = platform->get_aspect_ratio();
188281

189282
VkBool32 supported = VK_FALSE;
@@ -368,6 +461,11 @@ void WSI::drain_swapchain(bool in_tear_down)
368461

369462
void WSI::tear_down_swapchain()
370463
{
464+
#ifdef _WIN32
465+
// We only do explicit teardown on exit.
466+
dxgi.reset();
467+
#endif
468+
371469
drain_swapchain(true);
372470
platform->event_swapchain_destroyed();
373471
table->vkDestroySwapchainKHR(context->get_device(), swapchain, nullptr);
@@ -468,6 +566,55 @@ void WSI::set_low_latency_mode(bool enable)
468566
low_latency_mode_enable = enable;
469567
}
470568

569+
#ifdef _WIN32
570+
bool WSI::begin_frame_dxgi()
571+
{
572+
Semaphore acquire;
573+
574+
while (!acquire)
575+
{
576+
if (!dxgi->acquire(acquire, swapchain_index))
577+
return false;
578+
579+
acquire->signal_external();
580+
has_acquired_swapchain_index = true;
581+
582+
// Poll after acquire as well for optimal latency.
583+
platform->poll_input();
584+
585+
// Polling input may trigger a resize event. Trying to present in that situation without ResizeBuffers
586+
// cause wonky issues on DXGI.
587+
if (platform->should_resize())
588+
update_framebuffer(platform->get_surface_width(), platform->get_surface_height());
589+
590+
// If update_framebuffer caused a resize, we won't have an acquire index anymore, reacquire.
591+
if (!has_acquired_swapchain_index)
592+
acquire.reset();
593+
}
594+
595+
auto wait_ts = device->write_calibrated_timestamp();
596+
if (!dxgi->wait_latency(present_frame_latency))
597+
{
598+
LOGE("Failed to wait on latency handle.\n");
599+
return false;
600+
}
601+
device->register_time_interval("WSI", std::move(wait_ts), device->write_calibrated_timestamp(),
602+
"DXGI wait latency");
603+
604+
auto frame_time = platform->get_frame_timer().frame();
605+
auto elapsed_time = platform->get_frame_timer().get_elapsed();
606+
607+
smooth_frame_time = frame_time;
608+
smooth_elapsed_time = elapsed_time;
609+
610+
platform->event_frame_tick(frame_time, elapsed_time);
611+
platform->event_swapchain_index(device.get(), swapchain_index);
612+
device->set_acquire_semaphore(swapchain_index, std::move(acquire));
613+
614+
return true;
615+
}
616+
#endif
617+
471618
bool WSI::begin_frame()
472619
{
473620
if (frame_is_external)
@@ -478,26 +625,38 @@ bool WSI::begin_frame()
478625
#endif
479626

480627
device->next_frame_context();
628+
external_release.reset();
481629

482630
#ifdef VULKAN_WSI_TIMING_DEBUG
483631
auto next_frame_end = Util::get_current_time_nsecs();
484632
LOGI("Waited for vacant frame context for %.3f ms.\n", (next_frame_end - next_frame_start) * 1e-6);
485633
#endif
486634

487-
if (swapchain == VK_NULL_HANDLE || platform->should_resize() || swapchain_is_suboptimal)
488-
update_framebuffer(platform->get_surface_width(), platform->get_surface_height());
635+
#ifdef _WIN32
636+
if (dxgi)
637+
{
638+
if (platform->should_resize())
639+
update_framebuffer(platform->get_surface_width(), platform->get_surface_height());
640+
641+
if (has_acquired_swapchain_index)
642+
return true;
643+
return begin_frame_dxgi();
644+
}
645+
else
646+
#endif
647+
{
648+
if (swapchain == VK_NULL_HANDLE || platform->should_resize() || swapchain_is_suboptimal)
649+
update_framebuffer(platform->get_surface_width(), platform->get_surface_height());
650+
if (has_acquired_swapchain_index)
651+
return true;
652+
}
489653

490654
if (swapchain == VK_NULL_HANDLE)
491655
{
492656
LOGE("Completely lost swapchain. Cannot continue.\n");
493657
return false;
494658
}
495659

496-
if (has_acquired_swapchain_index)
497-
return true;
498-
499-
external_release.reset();
500-
501660
VkResult result;
502661
do
503662
{
@@ -597,6 +756,17 @@ bool WSI::begin_frame()
597756
return true;
598757
}
599758

759+
#ifdef _WIN32
760+
bool WSI::end_frame_dxgi()
761+
{
762+
auto release = device->consume_release_semaphore();
763+
VK_ASSERT(release);
764+
VK_ASSERT(release->is_signalled());
765+
VK_ASSERT(!release->is_pending_wait());
766+
return dxgi->present(std::move(release), current_present_mode == PresentMode::SyncToVBlank);
767+
}
768+
#endif
769+
600770
bool WSI::end_frame()
601771
{
602772
device->end_frame_context();
@@ -616,10 +786,16 @@ bool WSI::end_frame()
616786

617787
has_acquired_swapchain_index = false;
618788

789+
#ifdef _WIN32
790+
if (dxgi)
791+
return end_frame_dxgi();
792+
#endif
793+
619794
auto release = device->consume_release_semaphore();
620795
VK_ASSERT(release);
621796
VK_ASSERT(release->is_signalled());
622797
VK_ASSERT(!release->is_pending_wait());
798+
623799
auto release_semaphore = release->get_semaphore();
624800
VK_ASSERT(release_semaphore != VK_NULL_HANDLE);
625801

@@ -752,12 +928,22 @@ void WSI::update_framebuffer(unsigned width, unsigned height)
752928
{
753929
if (context && device)
754930
{
755-
drain_swapchain(false);
756-
if (blocking_init_swapchain(width, height))
931+
#ifdef _WIN32
932+
if (dxgi)
757933
{
758-
device->init_swapchain(swapchain_images, swapchain_width, swapchain_height, swapchain_surface_format.format,
759-
swapchain_current_prerotate,
760-
current_extra_usage | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
934+
if (!init_surface_swapchain_dxgi(width, height))
935+
LOGE("Failed to resize DXGI swapchain.\n");
936+
}
937+
else
938+
#endif
939+
{
940+
drain_swapchain(false);
941+
if (blocking_init_swapchain(width, height))
942+
{
943+
device->init_swapchain(swapchain_images, swapchain_width, swapchain_height,
944+
swapchain_surface_format.format, swapchain_current_prerotate,
945+
current_extra_usage | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
946+
}
761947
}
762948
}
763949

@@ -770,6 +956,15 @@ bool WSI::update_active_presentation_mode(PresentMode mode)
770956
if (current_present_mode == mode)
771957
return true;
772958

959+
#ifdef _WIN32
960+
// We set this on Present time.
961+
if (dxgi)
962+
{
963+
current_present_mode = mode;
964+
return true;
965+
}
966+
#endif
967+
773968
for (auto m : present_mode_compat_group)
774969
{
775970
bool match = false;

vulkan/wsi.hpp

+13
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@
2929
#include <vector>
3030
#include <thread>
3131
#include <chrono>
32+
#include <memory>
33+
34+
#ifdef _WIN32
35+
#include "wsi_dxgi.hpp"
36+
#endif
3237

3338
namespace Granite
3439
{
@@ -120,6 +125,7 @@ class WSIPlatform
120125
virtual void set_window_title(const std::string &title);
121126

122127
virtual uintptr_t get_fullscreen_monitor();
128+
virtual uintptr_t get_native_window();
123129

124130
virtual const VkApplicationInfo *get_application_info();
125131

@@ -359,5 +365,12 @@ class WSI
359365
void nonblock_delete_swapchains();
360366

361367
VkSurfaceFormatKHR find_suitable_present_format(const std::vector<VkSurfaceFormatKHR> &formats, BackbufferFormat desired_format) const;
368+
369+
#ifdef _WIN32
370+
std::unique_ptr<DXGIInteropSwapchain> dxgi;
371+
bool init_surface_swapchain_dxgi(unsigned width, unsigned height);
372+
bool begin_frame_dxgi();
373+
bool end_frame_dxgi();
374+
#endif
362375
};
363376
}

0 commit comments

Comments
 (0)