Compare commits

...

26 Commits

Author SHA1 Message Date
Benjamin Otte
847c7e2315 vulkan: Add support for D3D12 fences 2024-11-08 03:26:48 +01:00
Benjamin Otte
3e9b6abdec win32: Add (private) getters for fences and the HANDLE
We will need those when using them in Vulkan or GL.
2024-11-08 03:26:47 +01:00
Benjamin Otte
e48dc89daa gstreamer: Add support for D3D12 fences 2024-11-08 03:26:47 +01:00
Benjamin Otte
ac066e1807 win32: Add GdkD3D12TextureBuilder::fence
Add ::fence and ::fence-wait to set the fence and the value to wait for.

And wait for it before download()ing.
2024-11-08 03:26:47 +01:00
Benjamin Otte
b5c0dc1ced gstreamer/win32: Implement an allocation pool
We always use the default device, because that's what we do in GTK
proper and there's no API to query it anyway.
2024-11-08 03:26:47 +01:00
Benjamin Otte
ca4915962e win32: Support common DXGI video formats (incomplete) 2024-11-08 03:26:47 +01:00
Benjamin Otte
90d09a1180 win32: Import D3D12 resources into Vulkan renderer 2024-11-08 03:26:47 +01:00
Benjamin Otte
9ae76f6e69 vulkan: Change the allocator querying API
The old API was awkward in that it didn't allow access to the proper
memory type index and was prone to misuse in codepaths when importing
memory instead of allocating it.

Change that.

The old way now requires more code and a local variable, but importing
memory is a lot more straightforward.

And I don't have to spend days tracking down a leak on Windows.
2024-11-08 03:26:47 +01:00
Benjamin Otte
641b8c9cd0 vulkan: Add support for timeline semaphores
Unlike binary semaphores, timeline semaphores represent a (64bit
unsigned) value and the binary decision about whether they have signaled
or not is decided by comparing their value to a predefined value.
So this code adds the ability to pass that value.

In fact, it now requires this value, but that is not a problem because
Vulkan ignores the value for binary semaphores, so we can just pass
0 everywhere a binary sempahore is used.

There is no use of the code in those commit, but timeline semaphores
are used for explicit sync with dmabufs and D3D12 uses them exclusively.
2024-11-08 03:19:27 +01:00
Benjamin Otte
a6d30b1694 vulkan: Add GDK_VULKAN_FEATURE_WIN32
We will use this for import/export of Win32-specific resources.

So far it's set up for d3d12 texture import.
2024-11-08 03:16:39 +01:00
Benjamin Otte
9f9d8f59bd vulkan: Always include vulkan_win32.h on Windows
I am so totally not interested to think about this special header.

Also, I had to hunt it down because everything else includes
just vulkan.h and only Windows does the custom header.
2024-11-08 03:16:39 +01:00
Benjamin Otte
d22cfef06e win32: Replace glsinkbin with d3d12convert on Windows
On Windows, we don't want to use GL and let D3D12 do all the work.

But as long as we don't support all formats and implement all the
interfaces GstPlayer expects, we need to have a way to convert.

So we put a d3d12convert for that.

Related: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3956
2024-11-08 03:13:43 +01:00
Benjamin Otte
577176b0f2 gstreamer/win32: Add initial D3D12 support to GTK sink 2024-11-07 22:16:38 +01:00
Benjamin Otte
595294b8f4 gpu: Add D3D12 texture support to GL renderer
Add a gdk_d3d12_texture_import_gl() function to match the EGL import
behavior.

And then use it in the import code.

What makes this a bit awkward is that we need to not just shuffle a
tex_id but also a mem_id around, but oh well...
2024-11-07 22:16:37 +01:00
Benjamin Otte
80c04eb223 win32: Add gdk_d3d12_texture_get_handle()
Makes shared HANDLE handling part of the texture. This handle will be
needed when importing into GL or Vulkan or using elsewhere.
Its lifetime is bound to the texture and so is it's use,
so it's best to maintain it here instead of requiring renderers to
deal with HANDLEs.
2024-11-07 22:07:53 +01:00
Benjamin Otte
c9d58ee54e gpu: Add an optional memory_id to GL images
This implements initial support for GL_EXT_external_objects.
An external memory object is (unlike EGL images) represented by a
GLuint just like all other GL objects.
It needs to be kept alive for as long as the memory is used, which
means it needs to outlive the texture ID.

That's why the object is handed over to the texture code and the
finalizer will then free the memory object.

This is just plumbing, users will follow in future commits.
2024-11-06 23:44:20 +01:00
Benjamin Otte
768fcfb718 gl: Add features for EXT_external_objects(_win32) 2024-11-06 23:44:20 +01:00
Benjamin Otte
35904b793a tests: Add testd3d12
This is a hacked up testdmabuf, but it's good enough to create a
resource and check that GdkD3D12Texture actually works.
2024-11-06 23:44:20 +01:00
Benjamin Otte
8b99f14706 win32: Add GdkD3d12Texture and builder
This is a simple implementation of a GdkTexture that is backed by
a ID3D12Resource.

The only functionality implemented so far is GdkTexture::download(),
but that is enough to make it work - albeit slowly - in all renderers.

It also doesn't support any fancy formats yet; in particular: no YUV.
2024-11-06 23:44:20 +01:00
Benjamin Otte
b5af96c21f win32: Add gdkdxgiformat.[ch]
These contain utility functions for mapping DXGI formats to the
formats GTK uses in various places.

For now, only the straightforward formats are mapped.
2024-11-06 17:59:21 +01:00
Benjamin Otte
d1e903203b win32: link against Direct3D 12
This prepares the build system for adding D3D12 support.

No actual code changes yet.
2024-11-06 17:59:20 +01:00
Benjamin Otte
d859b8a8e4 meson: Update GStreamer wrap to main
It's needed for D3D12 elements to have ranks and be picked for
autoplugging.

Related: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7045
2024-11-06 17:59:20 +01:00
Benjamin Otte
c3eb4ff025 build: Add wrap file for DirectX headers
Fixes CI for MSVC build
2024-11-06 17:59:20 +01:00
Benjamin Otte
2a411b895a CI: Install DirectX headers 2024-11-06 17:59:20 +01:00
Benjamin Otte
fe6b9d61f5 gdk: Add GDK_DEBUG=d3d12
This is meant to mirror GDK_DEBUG=dmabuf on Linux.
2024-11-06 17:59:20 +01:00
Benjamin Otte
7b6a7671cf memoryformat: Add DXGI format mappings
This mirrors the Vulkan code pretty much.
The 2 APIs are reasonably similar.
2024-11-06 17:59:20 +01:00
37 changed files with 4111 additions and 47 deletions

View File

@@ -17,6 +17,7 @@ pacman --noconfirm -S --needed \
${MINGW_PACKAGE_PREFIX}-adwaita-icon-theme \
${MINGW_PACKAGE_PREFIX}-atk \
${MINGW_PACKAGE_PREFIX}-cairo \
${MINGW_PACKAGE_PREFIX}-directx-headers \
${MINGW_PACKAGE_PREFIX}-gdk-pixbuf2 \
${MINGW_PACKAGE_PREFIX}-glib2 \
${MINGW_PACKAGE_PREFIX}-graphene \

View File

@@ -206,6 +206,9 @@ print out different types of debugging information.
`dmabuf`
: Information about dmabuf handling (Linux-only)
`d3d12`
: Information about Direct3D12 (Windows-only)
`offload`
: Information about subsurfaces and graphics offload (Wayland-only)

View File

@@ -132,6 +132,7 @@ static const GdkDebugKey gdk_debug_keys[] = {
{ "selection", GDK_DEBUG_SELECTION, "Information about selections" },
{ "clipboard", GDK_DEBUG_CLIPBOARD, "Information about clipboards" },
{ "dmabuf", GDK_DEBUG_DMABUF, "Information about dmabuf buffers" },
{ "d3d12", GDK_DEBUG_D3D12, "Information about Direct3D12" },
{ "offload", GDK_DEBUG_OFFLOAD, "Information about subsurfaces and graphics offload" },
{ "linear", GDK_DEBUG_LINEAR, "Enable linear rendering" },

View File

@@ -37,20 +37,21 @@ typedef enum {
GDK_DEBUG_SELECTION = 1 << 9,
GDK_DEBUG_CLIPBOARD = 1 << 10,
GDK_DEBUG_DMABUF = 1 << 11,
GDK_DEBUG_OFFLOAD = 1 << 12,
GDK_DEBUG_D3D12 = 1 << 12,
GDK_DEBUG_OFFLOAD = 1 << 13,
/* flags below are influencing behavior */
GDK_DEBUG_LINEAR = 1 << 13,
GDK_DEBUG_HDR = 1 << 14,
GDK_DEBUG_PORTALS = 1 << 15,
GDK_DEBUG_NO_PORTALS = 1 << 16,
GDK_DEBUG_GL_NO_FRACTIONAL= 1 << 17,
GDK_DEBUG_FORCE_OFFLOAD = 1 << 18,
GDK_DEBUG_GL_PREFER_GL = 1 << 19,
GDK_DEBUG_GL_DEBUG = 1 << 20,
GDK_DEBUG_DEFAULT_SETTINGS= 1 << 21,
GDK_DEBUG_HIGH_DEPTH = 1 << 22,
GDK_DEBUG_NO_VSYNC = 1 << 23,
GDK_DEBUG_LINEAR = 1 << 14,
GDK_DEBUG_HDR = 1 << 15,
GDK_DEBUG_PORTALS = 1 << 16,
GDK_DEBUG_NO_PORTALS = 1 << 17,
GDK_DEBUG_GL_NO_FRACTIONAL= 1 << 18,
GDK_DEBUG_FORCE_OFFLOAD = 1 << 19,
GDK_DEBUG_GL_PREFER_GL = 1 << 20,
GDK_DEBUG_GL_DEBUG = 1 << 21,
GDK_DEBUG_DEFAULT_SETTINGS= 1 << 22,
GDK_DEBUG_HIGH_DEPTH = 1 << 23,
GDK_DEBUG_NO_VSYNC = 1 << 24,
} GdkDebugFlags;
typedef enum {

View File

@@ -31,6 +31,9 @@
#ifdef GDK_RENDERING_VULKAN
#include <vulkan/vulkan.h>
#ifdef GDK_WINDOWING_WIN32
#include <vulkan/vulkan_win32.h>
#endif
#endif
G_BEGIN_DECLS
@@ -44,11 +47,14 @@ typedef struct _GdkDisplayClass GdkDisplayClass;
typedef enum {
GDK_VULKAN_FEATURE_DMABUF = 1 << 0,
GDK_VULKAN_FEATURE_YCBCR = 1 << 1,
GDK_VULKAN_FEATURE_SEMAPHORE_EXPORT = 1 << 2,
GDK_VULKAN_FEATURE_SEMAPHORE_IMPORT = 1 << 3,
GDK_VULKAN_FEATURE_INCREMENTAL_PRESENT = 1 << 4,
GDK_VULKAN_FEATURE_SWAPCHAIN_MAINTENANCE = 1 << 5,
GDK_VULKAN_FEATURE_WIN32 = 1 << 1,
GDK_VULKAN_FEATURE_YCBCR = 1 << 2,
GDK_VULKAN_FEATURE_TIMELINE_SEMAPHORE = 1 << 3,
GDK_VULKAN_FEATURE_SEMAPHORE_EXPORT = 1 << 4,
GDK_VULKAN_FEATURE_SEMAPHORE_IMPORT = 1 << 5,
GDK_VULKAN_FEATURE_WIN32_SEMAPHORE = 1 << 6,
GDK_VULKAN_FEATURE_INCREMENTAL_PRESENT = 1 << 7,
GDK_VULKAN_FEATURE_SWAPCHAIN_MAINTENANCE = 1 << 8,
} GdkVulkanFeatures;
#define GDK_VULKAN_N_FEATURES 6

View File

@@ -107,6 +107,8 @@ const GdkDebugKey gdk_gl_feature_keys[] = {
{ "debug", GDK_GL_FEATURE_DEBUG, "GL_KHR_debug" },
{ "base-instance", GDK_GL_FEATURE_BASE_INSTANCE, "GL_ARB_base_instance" },
{ "buffer-storage", GDK_GL_FEATURE_BUFFER_STORAGE, "GL_EXT_buffer_storage" },
{ "external-objects", GDK_GL_FEATURE_EXTERNAL_OBJECTS, "GL_EXT_memory_object and GL_EXT_semaphore"},
{ "external-objects-win32", GDK_GL_FEATURE_EXTERNAL_OBJECTS_WIN32, "GL_EXT_memory_object_win32 and GL_EXT_semaphore_win32" },
};
typedef struct _GdkGLContextPrivate GdkGLContextPrivate;
@@ -1694,6 +1696,16 @@ gdk_gl_context_check_features (GdkGLContext *context)
epoxy_has_gl_extension ("GL_ARB_buffer_storage"))
features |= GDK_GL_FEATURE_BUFFER_STORAGE;
if (epoxy_has_gl_extension ("GL_EXT_memory_object") &&
epoxy_has_gl_extension ("GL_EXT_semaphore"))
{
features |= GDK_GL_FEATURE_EXTERNAL_OBJECTS;
if (epoxy_has_gl_extension ("GL_EXT_memory_object_win32") &&
epoxy_has_gl_extension ("GL_EXT_semaphore_win32"))
features |= GDK_GL_FEATURE_EXTERNAL_OBJECTS_WIN32;
}
return features;
}
@@ -1736,6 +1748,10 @@ gdk_gl_context_check_extensions (GdkGLContext *context)
gdk_gl_feature_keys,
G_N_ELEMENTS (gdk_gl_feature_keys));
/* handle feature dependencies */
if (disabled_features & GDK_GL_FEATURE_EXTERNAL_OBJECTS)
disabled_features |= GDK_GL_FEATURE_EXTERNAL_OBJECTS_WIN32;
priv->features = supported_features & ~disabled_features;
gdk_gl_context_init_memory_flags (context);

View File

@@ -32,6 +32,8 @@ typedef enum {
GDK_GL_FEATURE_DEBUG = 1 << 0,
GDK_GL_FEATURE_BASE_INSTANCE = 1 << 1,
GDK_GL_FEATURE_BUFFER_STORAGE = 1 << 2,
GDK_GL_FEATURE_EXTERNAL_OBJECTS = 1 << 3,
GDK_GL_FEATURE_EXTERNAL_OBJECTS_WIN32 = 1 << 4,
} GdkGLFeatures;
#define GDK_GL_N_FEATURES 3

View File

@@ -31,6 +31,10 @@
#include <epoxy/gl.h>
#ifdef GDK_WINDOWING_WIN32
#include <directx/d3d12.h>
#endif
/* Don't report quick (< 0.5 msec) runs */
#define MIN_MARK_DURATION 500000
@@ -454,6 +458,10 @@ struct _GdkMemoryFormatDescription
VkFormat vk_format;
VkFormat vk_srgb_format;
#endif
#ifdef GDK_WINDOWING_WIN32
DXGI_FORMAT dxgi_format;
DXGI_FORMAT dxgi_srgb_format;
#endif
#ifdef HAVE_DMABUF
guint32 dmabuf_fourcc;
#endif
@@ -498,6 +506,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_B8G8R8A8_UNORM,
.vk_srgb_format = VK_FORMAT_B8G8R8A8_SRGB,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_B8G8R8A8_UNORM,
.dxgi_srgb_format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = DRM_FORMAT_ARGB8888,
#endif
@@ -531,6 +543,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_UNDEFINED,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_UNKNOWN,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = DRM_FORMAT_BGRA8888,
#endif
@@ -563,6 +579,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R8G8B8A8_UNORM,
.vk_srgb_format = VK_FORMAT_R8G8B8A8_SRGB,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_R8G8B8A8_UNORM,
.dxgi_srgb_format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = DRM_FORMAT_ABGR8888,
#endif
@@ -596,6 +616,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_UNDEFINED,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_UNKNOWN,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = DRM_FORMAT_RGBA8888,
#endif
@@ -629,6 +653,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_B8G8R8A8_UNORM,
.vk_srgb_format = VK_FORMAT_B8G8R8A8_SRGB,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_B8G8R8A8_UNORM,
.dxgi_srgb_format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = DRM_FORMAT_ARGB8888,
#endif
@@ -662,6 +690,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_UNDEFINED,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_UNKNOWN,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = DRM_FORMAT_BGRA8888,
#endif
@@ -694,6 +726,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R8G8B8A8_UNORM,
.vk_srgb_format = VK_FORMAT_R8G8B8A8_SRGB,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_R8G8B8A8_UNORM,
.dxgi_srgb_format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = DRM_FORMAT_ABGR8888,
#endif
@@ -727,6 +763,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_UNDEFINED,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_UNKNOWN,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = DRM_FORMAT_RGBA8888,
#endif
@@ -761,6 +801,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_B8G8R8A8_UNORM,
.vk_srgb_format = VK_FORMAT_B8G8R8A8_SRGB,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_B8G8R8A8_UNORM,
.dxgi_srgb_format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = DRM_FORMAT_XRGB8888,
#endif
@@ -795,6 +839,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_UNDEFINED,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_UNKNOWN,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = DRM_FORMAT_BGRX8888,
#endif
@@ -828,6 +876,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R8G8B8A8_UNORM,
.vk_srgb_format = VK_FORMAT_R8G8B8A8_SRGB,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_R8G8B8A8_UNORM,
.dxgi_srgb_format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = DRM_FORMAT_XBGR8888,
#endif
@@ -862,6 +914,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_UNDEFINED,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_UNKNOWN,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = DRM_FORMAT_RGBX8888,
#endif
@@ -895,6 +951,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R8G8B8_UNORM,
.vk_srgb_format = VK_FORMAT_R8G8B8_SRGB,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_UNKNOWN,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = DRM_FORMAT_BGR888,
#endif
@@ -929,6 +989,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_B8G8R8_UNORM,
.vk_srgb_format = VK_FORMAT_B8G8R8_SRGB,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_UNKNOWN,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = DRM_FORMAT_RGB888,
#endif
@@ -965,6 +1029,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R16G16B16_UNORM,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_UNKNOWN,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = 0,
#endif
@@ -1000,6 +1068,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R16G16B16A16_UNORM,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_R16G16B16A16_UNORM,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = DRM_FORMAT_ABGR16161616,
#endif
@@ -1035,6 +1107,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R16G16B16A16_UNORM,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_R16G16B16A16_UNORM,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = DRM_FORMAT_ABGR16161616,
#endif
@@ -1070,6 +1146,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R16G16B16_SFLOAT,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_UNKNOWN,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = 0,
#endif
@@ -1104,6 +1184,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R16G16B16A16_SFLOAT,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_R16G16B16A16_FLOAT,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = DRM_FORMAT_ABGR16161616F,
#endif
@@ -1138,6 +1222,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R16G16B16A16_SFLOAT,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_R16G16B16A16_FLOAT,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = DRM_FORMAT_ABGR16161616F,
#endif
@@ -1173,6 +1261,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R32G32B32_SFLOAT,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_R32G32B32_FLOAT,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = 0,
#endif
@@ -1207,6 +1299,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R32G32B32A32_SFLOAT,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_R32G32B32A32_FLOAT,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = 0,
#endif
@@ -1241,6 +1337,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R32G32B32A32_SFLOAT,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_R32G32B32A32_FLOAT,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = 0,
#endif
@@ -1274,6 +1374,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R8G8_UNORM,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_R8G8_UNORM,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = 0,
#endif
@@ -1307,6 +1411,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R8G8_UNORM,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_R8G8_UNORM,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = 0,
#endif
@@ -1340,6 +1448,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R8_UNORM,
.vk_srgb_format = VK_FORMAT_R8_SRGB,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_R8_UNORM,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = DRM_FORMAT_R8,
#endif
@@ -1376,6 +1488,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R16G16_UNORM,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_R16G16_UNORM,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = 0,
#endif
@@ -1412,6 +1528,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R16G16_UNORM,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_R16G16_UNORM,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = 0,
#endif
@@ -1448,6 +1568,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R16_UNORM,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_R16_UNORM,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = DRM_FORMAT_R16,
#endif
@@ -1481,6 +1605,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R8_UNORM,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_A8_UNORM,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = 0,
#endif
@@ -1517,6 +1645,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R16_UNORM,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_R16_UNORM,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = 0,
#endif
@@ -1552,6 +1684,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R16_SFLOAT,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_R16_FLOAT,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = 0,
#endif
@@ -1587,6 +1723,10 @@ static const GdkMemoryFormatDescription memory_formats[] = {
.vk_format = VK_FORMAT_R32_SFLOAT,
.vk_srgb_format = VK_FORMAT_UNDEFINED,
#endif
#ifdef GDK_WINDOWING_WIN32
.dxgi_format = DXGI_FORMAT_R32_FLOAT,
.dxgi_srgb_format = DXGI_FORMAT_UNKNOWN,
#endif
#ifdef HAVE_DMABUF
.dmabuf_fourcc = 0,
#endif
@@ -1971,6 +2111,80 @@ gdk_memory_format_vk_rgba_format (GdkMemoryFormat format,
}
#endif
#ifdef GDK_WINDOWING_WIN32
static D3D12_SHADER_COMPONENT_MAPPING
dxgi_swizzle_from_gl_swizzle_one (GLint swizzle)
{
switch (swizzle)
{
case GL_RED:
return D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_0;
case GL_GREEN:
return D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1;
case GL_BLUE:
return D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_2;
case GL_ALPHA:
return D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_3;
case GL_ZERO:
return D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0;
case GL_ONE:
return D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1;
default:
g_assert_not_reached ();
return D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0;
}
}
static UINT
dxgi_swizzle_from_gl_swizzle (const GLint gl_swizzle[4])
{
return D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING (
dxgi_swizzle_from_gl_swizzle_one (gl_swizzle[0]),
dxgi_swizzle_from_gl_swizzle_one (gl_swizzle[1]),
dxgi_swizzle_from_gl_swizzle_one (gl_swizzle[2]),
dxgi_swizzle_from_gl_swizzle_one (gl_swizzle[3]));
}
/* DXGI version of gdk_memory_format_gl_format()
* Returns DXGI_FORMAT_UNKNOWN on failure */
DXGI_FORMAT
gdk_memory_format_dxgi_format (GdkMemoryFormat format,
UINT *out_shader_4_component_mapping)
{
if (out_shader_4_component_mapping)
*out_shader_4_component_mapping = dxgi_swizzle_from_gl_swizzle (memory_formats[format].gl.swizzle);
return memory_formats[format].dxgi_format;
}
/* Gets the matching SRGB version of a DXGI_FORMAT
* Returns DXGI_FORMAT_UNKNOWN if none exists */
DXGI_FORMAT
gdk_memory_format_dxgi_srgb_format (GdkMemoryFormat format)
{
return memory_formats[format].dxgi_srgb_format;
}
/* DXGI version of gdk_memory_format_gl_rgba_format()
* Returns DXGI_FORMAT_UNKNOWN on failure */
DXGI_FORMAT
gdk_memory_format_dxgi_rgba_format (GdkMemoryFormat format,
GdkMemoryFormat *out_rgba_format,
UINT *out_shader_4_component_mapping)
{
GdkMemoryFormat actual = memory_formats[format].gl.rgba_format;
if (actual == -1)
return DXGI_FORMAT_UNKNOWN;
if (out_rgba_format)
*out_rgba_format = actual;
if (out_shader_4_component_mapping)
*out_shader_4_component_mapping = dxgi_swizzle_from_gl_swizzle (memory_formats[format].gl.swizzle);
return memory_formats[actual].dxgi_format;
}
#endif
/*
* gdk_memory_format_get_dmabuf_fourcc:
* @format: The memory format

View File

@@ -25,6 +25,7 @@
/* epoxy needs this, see https://github.com/anholt/libepoxy/issues/299 */
#ifdef GDK_WINDOWING_WIN32
#include <windows.h>
#include <dxgiformat.h>
#endif
#include <epoxy/gl.h>
@@ -95,6 +96,14 @@ VkFormat gdk_memory_format_vk_rgba_format (GdkMemoryFormat
GdkMemoryFormat *out_rgba_format,
VkComponentMapping *out_swizzle);
#endif
#ifdef GDK_WINDOWING_WIN32
DXGI_FORMAT gdk_memory_format_dxgi_format (GdkMemoryFormat format,
UINT *out_shader_4_component_mapping);
DXGI_FORMAT gdk_memory_format_dxgi_srgb_format (GdkMemoryFormat format);
DXGI_FORMAT gdk_memory_format_dxgi_rgba_format (GdkMemoryFormat format,
GdkMemoryFormat *out_rgba_format,
UINT *out_shader_4_component_mapping);
#endif
guint32 gdk_memory_format_get_dmabuf_fourcc (GdkMemoryFormat format);
const char * gdk_memory_format_get_name (GdkMemoryFormat format);

View File

@@ -37,9 +37,12 @@
#ifdef GDK_RENDERING_VULKAN
const GdkDebugKey gdk_vulkan_feature_keys[] = {
{ "dmabuf", GDK_VULKAN_FEATURE_DMABUF, "Never import Dmabufs" },
{ "win32", GDK_VULKAN_FEATURE_WIN32, "Never import Windows resources" },
{ "ycbcr", GDK_VULKAN_FEATURE_YCBCR, "Do not support Ycbcr textures (also disables dmabufs)" },
{ "timeline-semaphore", GDK_VULKAN_FEATURE_TIMELINE_SEMAPHORE, "Disable timeline semaphore support (disables Windows sync)"},
{ "semaphore-export", GDK_VULKAN_FEATURE_SEMAPHORE_EXPORT, "Disable sync of exported dmabufs" },
{ "semaphore-import", GDK_VULKAN_FEATURE_SEMAPHORE_IMPORT, "Disable sync of imported dmabufs" },
{ "win32-semaphore", GDK_VULKAN_FEATURE_WIN32_SEMAPHORE, "Disable Windows sync support" },
{ "incremental-present", GDK_VULKAN_FEATURE_INCREMENTAL_PRESENT, "Do not send damage regions" },
{ "swapchain-maintenance", GDK_VULKAN_FEATURE_SWAPCHAIN_MAINTENANCE, "Do not use advanced swapchain features" },
};
@@ -631,6 +634,18 @@ physical_device_check_features (VkPhysicalDevice device)
physical_device_supports_extension (device, VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME))
features |= GDK_VULKAN_FEATURE_DMABUF;
if (physical_device_supports_extension (device, VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME))
features |= GDK_VULKAN_FEATURE_TIMELINE_SEMAPHORE;
#ifdef GDK_WINDOWING_WIN32
if (physical_device_supports_extension (device, VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME))
features |= GDK_VULKAN_FEATURE_WIN32;
if ((features & GDK_VULKAN_FEATURE_TIMELINE_SEMAPHORE) &&
physical_device_supports_extension (device, VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME))
features |= GDK_VULKAN_FEATURE_WIN32_SEMAPHORE;
#endif
if (physical_device_supports_extension (device, VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME))
{
if (semaphore_props.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT)
@@ -1478,6 +1493,8 @@ gdk_display_create_vulkan_device (GdkDisplay *display,
G_N_ELEMENTS (gdk_vulkan_feature_keys));
if (skip_features & GDK_VULKAN_FEATURE_YCBCR)
skip_features |= GDK_VULKAN_FEATURE_DMABUF;
if (skip_features & GDK_VULKAN_FEATURE_TIMELINE_SEMAPHORE)
skip_features |= GDK_VULKAN_FEATURE_WIN32_SEMAPHORE;
if (GDK_DISPLAY_DEBUG_CHECK (display, VULKAN))
{
@@ -1573,8 +1590,27 @@ gdk_display_create_vulkan_device (GdkDisplay *display,
g_ptr_array_add (device_extensions, (gpointer) VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME);
g_ptr_array_add (device_extensions, (gpointer) VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME);
}
if (features & GDK_VULKAN_FEATURE_TIMELINE_SEMAPHORE)
{
g_ptr_array_add (device_extensions, (gpointer) VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
g_ptr_array_add (device_extensions, (gpointer) VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME);
}
#ifdef GDK_WINDOWING_WIN32
if (features & GDK_VULKAN_FEATURE_WIN32)
{
if (!(features & GDK_VULKAN_FEATURE_DMABUF))
g_ptr_array_add (device_extensions, (gpointer) VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME);
g_ptr_array_add (device_extensions, (gpointer) VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME);
}
if (features & GDK_VULKAN_FEATURE_WIN32_SEMAPHORE)
{
g_ptr_array_add (device_extensions, (gpointer) VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME);
}
#endif
if (features & (GDK_VULKAN_FEATURE_SEMAPHORE_IMPORT | GDK_VULKAN_FEATURE_SEMAPHORE_EXPORT))
{
if (!(features & GDK_VULKAN_FEATURE_TIMELINE_SEMAPHORE))
g_ptr_array_add (device_extensions, (gpointer) VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
g_ptr_array_add (device_extensions, (gpointer) VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME);
}
if (features & GDK_VULKAN_FEATURE_INCREMENTAL_PRESENT)

591
gdk/win32/gdkd3d12texture.c Normal file
View File

@@ -0,0 +1,591 @@
/* gdkd3d12texture.c
*
* Copyright 2024 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gdkd3d12textureprivate.h"
#include "gdkcolorstateprivate.h"
#include "gdkd3d12texturebuilder.h"
#include "gdkdxgiformatprivate.h"
#include "gdkglcontextprivate.h"
#include "gdkmemoryformatprivate.h"
#include "gdkprivate-win32.h"
#include "gdktextureprivate.h"
#include <epoxy/gl.h>
/**
* GdkD3D12Texture:
*
* A `GdkTexture` representing a [ID3D12Resource](https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nn-d3d12-id3d12resource).
*
* To create a `GdkD3D12Texture`, use the auxiliary
* [class@Gdk.D3d12TextureBuilder] object.
*
* D3D12 textures can only be created on Windows.
*
* Since: 4.18
*/
struct _GdkD3D12Texture
{
GdkTexture parent_instance;
ID3D12Resource *resource;
HANDLE resource_handle;
ID3D12Fence *fence;
HANDLE fence_handle;
guint64 fence_wait;
GDestroyNotify destroy;
gpointer data;
};
struct _GdkD3D12TextureClass
{
GdkTextureClass parent_class;
};
/**
* gdk_d3d12_error_quark:
*
* Registers an error quark for [class@Gdk.D3d12Texture] errors.
*
* Returns: the error quark
**/
G_DEFINE_QUARK (gdk-d3d12-error-quark, gdk_d3d12_error)
G_DEFINE_TYPE (GdkD3D12Texture, gdk_d3d12_texture, GDK_TYPE_TEXTURE)
static void
gdk_d3d12_texture_dispose (GObject *object)
{
GdkD3D12Texture *self = GDK_D3D12_TEXTURE (object);
if (self->fence_handle)
{
if (G_UNLIKELY (!CloseHandle (self->fence_handle)))
{
g_warning ("Failed to fence handle: %s", g_win32_error_message (GetLastError ()));
}
self->fence_handle = NULL;
}
if (self->resource_handle)
{
if (G_UNLIKELY (!CloseHandle (self->resource_handle)))
{
g_warning ("Failed to close resource handle: %s", g_win32_error_message (GetLastError ()));
}
self->resource_handle = NULL;
}
gdk_win32_com_clear (&self->fence);
gdk_win32_com_clear (&self->resource);
if (self->destroy)
{
self->destroy (self->data);
self->destroy = NULL;
}
G_OBJECT_CLASS (gdk_d3d12_texture_parent_class)->dispose (object);
}
static gboolean
supports_nonzero (ID3D12Device *device)
{
D3D12_FEATURE_DATA_D3D12_OPTIONS7 options;
HRESULT hr;
hr = ID3D12Device_CheckFeatureSupport (device,
D3D12_FEATURE_D3D12_OPTIONS7,
&options,
sizeof (options));
return SUCCEEDED (hr);
}
#define hr_return_if_fail(expr) G_STMT_START {\
HRESULT _hr = (expr); \
if (!SUCCEEDED (_hr)) \
{ \
g_log (G_LOG_DOMAIN, \
G_LOG_LEVEL_CRITICAL, \
"file %s: line %d (%s): %s returned %ld (%s)", \
__FILE__, \
__LINE__, \
G_STRFUNC, \
#expr, \
_hr, \
g_win32_error_message (_hr)); \
return; \
} \
}G_STMT_END
static void
gdk_d3d12_texture_download (GdkTexture *texture,
GdkMemoryFormat format,
GdkColorState *color_state,
guchar *data,
gsize stride)
{
GdkD3D12Texture *self = GDK_D3D12_TEXTURE (texture);
UINT64 buffer_size;
ID3D12Device *device;
ID3D12CommandAllocator *allocator;
ID3D12GraphicsCommandList *commands;
ID3D12CommandQueue *queue;
ID3D12Fence *fence;
ID3D12Resource *buffer;
D3D12_RESOURCE_DESC resource_desc;
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint;
void *buffer_data;
hr_return_if_fail (ID3D12Resource_GetDevice (self->resource,
&IID_ID3D12Device,
(void **) &device));
ID3D12Resource_GetDesc (self->resource, &resource_desc);
ID3D12Device_GetCopyableFootprints (device,
&resource_desc,
0, 1, 0,
&footprint,
NULL,
NULL,
NULL);
buffer_size = footprint.Footprint.RowPitch * footprint.Footprint.Height;
hr_return_if_fail (ID3D12Device_CreateCommittedResource (device,
(&(D3D12_HEAP_PROPERTIES) {
.Type = D3D12_HEAP_TYPE_READBACK,
.CreationNodeMask = 1,
.VisibleNodeMask = 1,
}),
supports_nonzero (device) ? D3D12_HEAP_FLAG_CREATE_NOT_ZEROED
: D3D12_HEAP_FLAG_NONE,
(&(D3D12_RESOURCE_DESC) {
.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER,
.Width = buffer_size,
.Height = 1,
.DepthOrArraySize = 1,
.MipLevels = 1,
.SampleDesc = {
.Count = 1,
.Quality = 0,
},
.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
}),
D3D12_RESOURCE_STATE_COMMON,
NULL,
&IID_ID3D12Resource,
(void **) &buffer));
hr_return_if_fail (ID3D12Device_CreateFence (device,
0,
D3D12_FENCE_FLAG_NONE,
&IID_ID3D12Fence,
(void **) &fence));
hr_return_if_fail (ID3D12Device_CreateCommandQueue (device,
(&(D3D12_COMMAND_QUEUE_DESC) {
.Type = D3D12_COMMAND_LIST_TYPE_COPY,
.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL,
.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE,
}),
&IID_ID3D12CommandQueue,
(void **) &queue));
hr_return_if_fail (ID3D12Device_CreateCommandAllocator (device,
D3D12_COMMAND_LIST_TYPE_COPY,
&IID_ID3D12CommandAllocator,
(void **) &allocator));
hr_return_if_fail (ID3D12Device_CreateCommandList (device,
0,
D3D12_COMMAND_LIST_TYPE_COPY,
allocator,
NULL,
&IID_ID3D12GraphicsCommandList,
(void **) &commands));
ID3D12GraphicsCommandList_CopyTextureRegion (commands,
(&(D3D12_TEXTURE_COPY_LOCATION) {
.pResource = buffer,
.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
.PlacedFootprint = footprint,
}),
0, 0, 0,
(&(D3D12_TEXTURE_COPY_LOCATION) {
.pResource = self->resource,
.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
.SubresourceIndex = 0,
}),
NULL);
hr_return_if_fail (ID3D12GraphicsCommandList_Close (commands));
if (self->fence)
ID3D12CommandQueue_Wait (queue, self->fence, self->fence_wait);
ID3D12CommandQueue_ExecuteCommandLists (queue, 1, (ID3D12CommandList **) &commands);
#define FENCE_SIGNAL 1
hr_return_if_fail (ID3D12CommandQueue_Signal (queue, fence, FENCE_SIGNAL));
hr_return_if_fail (ID3D12Fence_SetEventOnCompletion (fence, FENCE_SIGNAL, NULL));
hr_return_if_fail (ID3D12Resource_Map (buffer,
0,
(&(D3D12_RANGE) {
.Begin = 0,
.End = buffer_size,
}),
&buffer_data));
gdk_dxgi_format_convert (resource_desc.Format,
gdk_memory_format_alpha (texture->format) != GDK_MEMORY_ALPHA_STRAIGHT,
buffer_data,
footprint.Footprint.RowPitch,
texture->color_state,
format,
data,
stride,
color_state,
texture->width,
texture->height);
ID3D12Resource_Unmap (buffer, 0, (&(D3D12_RANGE) { 0, 0 }));
gdk_win32_com_clear (&buffer);
gdk_win32_com_clear (&commands);
gdk_win32_com_clear (&allocator);
gdk_win32_com_clear (&queue);
gdk_win32_com_clear (&fence);
gdk_win32_com_clear (&device);
}
static void
gdk_d3d12_texture_class_init (GdkD3D12TextureClass *klass)
{
GdkTextureClass *texture_class = GDK_TEXTURE_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
texture_class->download = gdk_d3d12_texture_download;
gobject_class->dispose = gdk_d3d12_texture_dispose;
}
static void
gdk_d3d12_texture_init (GdkD3D12Texture *self)
{
}
GdkTexture *
gdk_d3d12_texture_new_from_builder (GdkD3D12TextureBuilder *builder,
GDestroyNotify destroy,
gpointer data,
GError **error)
{
GdkD3D12Texture *self;
GdkTexture *update_texture;
GdkColorState *color_state;
GdkMemoryFormat format;
ID3D12Resource *resource;
ID3D12Fence *fence;
D3D12_RESOURCE_DESC desc;
gboolean premultiplied;
resource = gdk_d3d12_texture_builder_get_resource (builder);
fence = gdk_d3d12_texture_builder_get_fence (builder);
premultiplied = gdk_d3d12_texture_builder_get_premultiplied (builder);
ID3D12Resource_GetDesc (resource, &desc);
if (desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D)
{
g_set_error (error,
GDK_D3D12_ERROR, GDK_D3D12_ERROR_UNSUPPORTED_FORMAT,
"Resource must be 2D texture");
return NULL;
}
if (!gdk_dxgi_format_is_supported (desc.Format))
{
g_set_error (error,
GDK_D3D12_ERROR, GDK_D3D12_ERROR_UNSUPPORTED_FORMAT,
"Unsupported DXGI format %u", desc.Format);
return NULL;
}
format = gdk_dxgi_format_get_memory_format (desc.Format, premultiplied);
/* FIXME: Do we need to validate the desc.SampleDesc? */
color_state = gdk_d3d12_texture_builder_get_color_state (builder);
if (color_state == NULL)
color_state = gdk_color_state_get_srgb ();
self = (GdkD3D12Texture *) g_object_new (GDK_TYPE_D3D12_TEXTURE,
"width", (int) desc.Width,
"height", (int) desc.Height,
"color-state", color_state,
NULL);
GDK_TEXTURE (self)->format = format;
ID3D12Resource_AddRef (resource);
self->resource = resource;
if (fence)
{
ID3D12Fence_AddRef (fence);
self->fence = fence;
self->fence_wait = gdk_d3d12_texture_builder_get_fence_wait (builder);
}
GDK_DEBUG (D3D12,
"Creating %ux%u D3D12 texture, format %u",
(UINT) desc.Width, desc.Height,
desc.Format);
/* Set this only once we know that the texture will be created.
* Otherwise dispose() will run the callback */
self->destroy = destroy;
self->data = data;
update_texture = gdk_d3d12_texture_builder_get_update_texture (builder);
if (update_texture)
{
cairo_region_t *update_region = gdk_d3d12_texture_builder_get_update_region (builder);
if (update_region)
{
cairo_rectangle_int_t tex_rect = { 0, 0,
update_texture->width, update_texture->height };
update_region = cairo_region_copy (update_region);
cairo_region_intersect_rectangle (update_region, &tex_rect);
gdk_texture_set_diff (GDK_TEXTURE (self), update_texture, update_region);
}
}
return GDK_TEXTURE (self);
#if 0
g_set_error_literal (error, GDK_D3D12_ERROR, GDK_D3D12_ERROR_NOT_AVAILABLE,
"d3d12 support disabled at compile-time.");
return NULL;
#endif
}
ID3D12Resource *
gdk_d3d12_texture_get_resource (GdkD3D12Texture *self)
{
return self->resource;
}
G_LOCK_DEFINE_STATIC(handle_creation);
HANDLE
gdk_d3d12_texture_get_resource_handle (GdkD3D12Texture *self)
{
ID3D12Device *device = NULL;
D3D12_HEAP_FLAGS heap_flags;
HANDLE result;
HRESULT hr;
result = g_atomic_pointer_get (&self->resource_handle);
if (result)
return result;
G_LOCK (handle_creation);
result = g_atomic_pointer_get (&self->resource_handle);
if (result)
goto out;
if (FAILED (ID3D12Resource_GetHeapProperties (self->resource, NULL, &heap_flags)) ||
(heap_flags & D3D12_HEAP_FLAG_SHARED == 0))
{
GDK_DEBUG (D3D12, "Cannot export handle, heap is not shared");
goto out;
}
if (FAILED (ID3D12Resource_GetDevice (self->resource,
&IID_ID3D12Device,
(void **) &device)))
goto out;
hr = ID3D12Device_CreateSharedHandle (device,
(ID3D12DeviceChild *)self->resource,
NULL,
GENERIC_ALL,
NULL,
&result);
if (FAILED (hr))
{
GDK_DEBUG (D3D12, "Failed to create shared handle for texture: %s",
g_win32_error_message (hr));
goto out;
}
g_atomic_pointer_set (&self->resource_handle, result);
out:
gdk_win32_com_clear (&device);
G_UNLOCK (handle_creation);
return result;
}
ID3D12Fence *
gdk_d3d12_texture_get_fence (GdkD3D12Texture *self)
{
return self->fence;
}
HANDLE
gdk_d3d12_texture_get_fence_handle (GdkD3D12Texture *self)
{
ID3D12Device *device = NULL;
D3D12_HEAP_FLAGS heap_flags;
HANDLE result;
HRESULT hr;
if (self->fence == NULL)
return NULL;
result = g_atomic_pointer_get (&self->fence_handle);
if (result)
return result;
G_LOCK (handle_creation);
result = g_atomic_pointer_get (&self->fence_handle);
if (result)
goto out;
if (FAILED (ID3D12Fence_GetDevice (self->fence,
&IID_ID3D12Device,
(void **) &device)))
goto out;
hr = ID3D12Device_CreateSharedHandle (device,
(ID3D12DeviceChild *) self->fence,
NULL,
GENERIC_ALL,
NULL,
&result);
if (FAILED (hr))
{
GDK_DEBUG (D3D12, "Failed to create shared handle for fence: %s",
g_win32_error_message (hr));
goto out;
}
g_atomic_pointer_set (&self->fence_handle, result);
out:
gdk_win32_com_clear (&device);
G_UNLOCK (handle_creation);
return result;
}
guint64
gdk_d3d12_texture_get_fence_wait (GdkD3D12Texture *self)
{
return self->fence_wait;
}
/*
* gdk_d3d12_texture_import_gl:
* @self: texture to import into GL
* @context: GL context to use for the import. The context
* must be the current context.
* @out_mem_id: (out): out result for the memory object
* created during the import. See GL_EXT_memory_object_win32
* for details.
*
* Imports the given D3D12 texture into the given OpenGL
* context.
*
* This function binds `GL_TEXTURE_2D` during the import.
*
* Returns: The newly created texture or 0 on failure.
*/
guint
gdk_d3d12_texture_import_gl (GdkD3D12Texture *self,
GdkGLContext *context,
guint *out_mem_id)
{
GdkTexture *texture = GDK_TEXTURE (self);
GLuint tex_id, mem_id;
D3D12_RESOURCE_DESC desc;
HANDLE handle;
GLenum gl_internal_format;
GLenum gl_error;
/* We assume that the context is current, the caller needs to juggle that */
g_assert (gdk_gl_context_get_current () == context);
if (!gdk_gl_context_has_feature (context, GDK_GL_FEATURE_EXTERNAL_OBJECTS_WIN32))
{
GDK_DEBUG (D3D12, "Not importing %ux%u texture, EXT_external_objects_win32 is not supported",
texture->width, texture->height);
return 0;
}
ID3D12Resource_GetDesc (self->resource, &desc);
gl_internal_format = gdk_dxgi_format_get_gl_format (desc.Format);
if (gl_internal_format == 0)
{
GDK_DEBUG (D3D12, "Not importing %ux%u texture, no matching GL format for DGI format %u",
texture->width, texture->height,
desc.Format);
return 0;
}
handle = gdk_d3d12_texture_get_resource_handle (self);
if (!handle)
return 0;
GDK_DEBUG (D3D12, "Attempting to import %ux%u texture",
texture->width, texture->height);
glCreateMemoryObjectsEXT (1, &mem_id);
glImportMemoryWin32HandleEXT (mem_id,
0,
GL_HANDLE_TYPE_D3D12_RESOURCE_EXT,
handle);
glGenTextures (1, &tex_id);
glBindTexture (GL_TEXTURE_2D, tex_id);
glTexStorageMem2DEXT (GL_TEXTURE_2D,
desc.MipLevels,
gl_internal_format,
desc.Width,
desc.Height,
mem_id,
0);
gl_error = glGetError ();
if (gl_error != GL_NO_ERROR)
{
GDK_DEBUG (D3D12, "Failed to import %ux%u texture, got GL error %u",
texture->width, texture->height,
gl_error);
glDeleteTextures (1, &tex_id);
return 0;
}
*out_mem_id = mem_id;
return tex_id;
}

View File

@@ -0,0 +1,67 @@
/* gdkd3d12texture.h
*
* Copyright 2024 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined (__GDKWIN32_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gdk/gdk.h> can be included directly."
#endif
#include <gdk/gdktypes.h>
#include <gdk/gdktexture.h>
G_BEGIN_DECLS
#define GDK_TYPE_D3D12_TEXTURE (gdk_d3d12_texture_get_type ())
#define GDK_D3D12_TEXTURE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_D3D12_TEXTURE, GdkD3D12Texture))
#define GDK_IS_D3D12_TEXTURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_D3D12_TEXTURE))
#define GDK_D3D12_ERROR (gdk_d3d12_error_quark ())
typedef struct _GdkD3D12Texture GdkD3D12Texture;
typedef struct _GdkD3D12TextureClass GdkD3D12TextureClass;
/**
* GdkD3D12Error:
* @GDK_D3D12_ERROR_NOT_AVAILABLE: D3D12 support is not available, because the OS
* is not Windows, the Windows version is not recent enough, or it was explicitly
* disabled at compile- or runtime
* @GDK_D3D12_ERROR_UNSUPPORTED_FORMAT: The requested format is not supported
* @GDK_D3D12_ERROR_CREATION_FAILED: GTK failed to create the resource for other
* reasons
*
* Error enumeration for `GdkD3D12Texture`.
*
* Since: 4.18
*/
typedef enum {
GDK_D3D12_ERROR_NOT_AVAILABLE,
GDK_D3D12_ERROR_UNSUPPORTED_FORMAT,
GDK_D3D12_ERROR_CREATION_FAILED,
} GdkD3D12Error;
GDK_AVAILABLE_IN_4_18
GType gdk_d3d12_texture_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_4_18
GQuark gdk_d3d12_error_quark (void) G_GNUC_CONST;
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkD3D12Texture, g_object_unref)
G_END_DECLS

View File

@@ -0,0 +1,677 @@
/*
* Copyright © 2024 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gdkd3d12texturebuilder.h"
#include "gdkdebugprivate.h"
#include "gdkcolorstate.h"
#include "gdkd3d12textureprivate.h"
#include "gdkprivate-win32.h"
#include <cairo-gobject.h>
struct _GdkD3D12TextureBuilder
{
GObject parent_instance;
ID3D12Resource *resource;
ID3D12Fence *fence;
guint64 fence_wait;
GdkColorState *color_state;
gboolean premultiplied;
GdkTexture *update_texture;
cairo_region_t *update_region;
};
struct _GdkD3D12TextureBuilderClass
{
GObjectClass parent_class;
};
/**
* GdkD3D12TextureBuilder:
*
* `GdkD3D12TextureBuilder` is a builder used to construct [class@Gdk.Texture]
* objects from [ID3D12Resources](https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nn-d3d12-id3d12resource).
*
* The operation of `GdkD3D12TextureBuilder` is quite simple: Create a texture builder,
* set all the necessary properties, and then call [method@Gdk.D3d12TextureBuilder.build]
* to create the new texture.
*
* Not all `D3D12Resources` can be used. You have to use a texture resource for a `GdkTexture`.
* GDK will attempt to detect invalid resources and fail to create the texture in that case.
* `GdkD3D12TextureBuilder` can be used for quick one-shot construction of
* textures as well as kept around and reused to construct multiple textures.
*
* Since: 4.18
*/
enum
{
PROP_0,
PROP_COLOR_STATE,
PROP_FENCE,
PROP_FENCE_WAIT,
PROP_RESOURCE,
PROP_PREMULTIPLIED,
PROP_UPDATE_REGION,
PROP_UPDATE_TEXTURE,
N_PROPS
};
G_DEFINE_TYPE (GdkD3D12TextureBuilder, gdk_d3d12_texture_builder, G_TYPE_OBJECT)
static GParamSpec *properties[N_PROPS] = { NULL, };
static void
gdk_d3d12_texture_builder_dispose (GObject *object)
{
GdkD3D12TextureBuilder *self = GDK_D3D12_TEXTURE_BUILDER (object);
g_clear_object (&self->update_texture);
g_clear_pointer (&self->update_region, cairo_region_destroy);
g_clear_pointer (&self->color_state, gdk_color_state_unref);
gdk_win32_com_clear (&self->resource);
gdk_win32_com_clear (&self->fence);
G_OBJECT_CLASS (gdk_d3d12_texture_builder_parent_class)->dispose (object);
}
static void
gdk_d3d12_texture_builder_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GdkD3D12TextureBuilder *self = GDK_D3D12_TEXTURE_BUILDER (object);
switch (property_id)
{
case PROP_COLOR_STATE:
g_value_set_boxed (value, self->color_state);
break;
case PROP_FENCE:
g_value_set_pointer (value, self->fence);
break;
case PROP_FENCE_WAIT:
g_value_set_uint64 (value, self->fence_wait);
break;
case PROP_PREMULTIPLIED:
g_value_set_boolean (value, self->premultiplied);
break;
case PROP_RESOURCE:
g_value_set_pointer (value, self->resource);
break;
case PROP_UPDATE_REGION:
g_value_set_boxed (value, self->update_region);
break;
case PROP_UPDATE_TEXTURE:
g_value_set_object (value, self->update_texture);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gdk_d3d12_texture_builder_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GdkD3D12TextureBuilder *self = GDK_D3D12_TEXTURE_BUILDER (object);
switch (property_id)
{
case PROP_COLOR_STATE:
gdk_d3d12_texture_builder_set_color_state (self, g_value_get_boxed (value));
break;
case PROP_FENCE:
gdk_d3d12_texture_builder_set_fence (self, g_value_get_pointer (value));
break;
case PROP_FENCE_WAIT:
gdk_d3d12_texture_builder_set_fence_wait (self, g_value_get_uint64 (value));
break;
case PROP_PREMULTIPLIED:
gdk_d3d12_texture_builder_set_premultiplied (self, g_value_get_boolean (value));
break;
case PROP_RESOURCE:
gdk_d3d12_texture_builder_set_resource (self, g_value_get_pointer (value));
break;
case PROP_UPDATE_REGION:
gdk_d3d12_texture_builder_set_update_region (self, g_value_get_boxed (value));
break;
case PROP_UPDATE_TEXTURE:
gdk_d3d12_texture_builder_set_update_texture (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gdk_d3d12_texture_builder_class_init (GdkD3D12TextureBuilderClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = gdk_d3d12_texture_builder_dispose;
gobject_class->get_property = gdk_d3d12_texture_builder_get_property;
gobject_class->set_property = gdk_d3d12_texture_builder_set_property;
/**
* GdkD3D12TextureBuilder:color-state:
*
* The color state of the texture.
*
* Since: 4.18
*/
properties[PROP_COLOR_STATE] =
g_param_spec_boxed ("color-state", NULL, NULL,
GDK_TYPE_COLOR_STATE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GdkD3D12TextureBuilder:fence:
*
* The optional `ID3D12Fence` to wait on before using the resource.
*
* Since: 4.18
*/
properties[PROP_FENCE] =
g_param_spec_pointer ("fence", NULL, NULL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GdkD3D12TextureBuilder:fence-wait:
*
* The value the fence should wait on
*
* Since: 4.18
*/
properties[PROP_FENCE_WAIT] =
g_param_spec_uint64 ("fence-wait", NULL, NULL,
0, G_MAXUINT64, 0,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GdkD3D12TextureBuilder:premultiplied:
*
* Whether the alpha channel is premultiplied into the others.
*
* Only relevant if the format has alpha.
*
* Since: 4.18
*/
properties[PROP_PREMULTIPLIED] =
g_param_spec_boolean ("premultiplied", NULL, NULL,
TRUE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GdkD3D12TextureBuilder:resource:
*
* The `ID3D12Resource`
*
* Since: 4.18
*/
properties[PROP_RESOURCE] =
g_param_spec_pointer ("resource", NULL, NULL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GdkD3D12TextureBuilder:update-region:
*
* The update region for [property@Gdk.D3d12TextureBuilder:update-texture].
*
* Since: 4.18
*/
properties[PROP_UPDATE_REGION] =
g_param_spec_boxed ("update-region", NULL, NULL,
CAIRO_GOBJECT_TYPE_REGION,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* GdkD3D12TextureBuilder:update-texture:
*
* The texture [property@Gdk.D3d12TextureBuilder:update-region] is an update for.
*
* Since: 4.18
*/
properties[PROP_UPDATE_TEXTURE] =
g_param_spec_object ("update-texture", NULL, NULL,
GDK_TYPE_TEXTURE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
static void
gdk_d3d12_texture_builder_init (GdkD3D12TextureBuilder *self)
{
self->premultiplied = TRUE;
}
/**
* gdk_d3d12_texture_builder_new: (constructor):
*
* Creates a new texture builder.
*
* Returns: the new `GdkTextureBuilder`
*
* Since: 4.18
**/
GdkD3D12TextureBuilder *
gdk_d3d12_texture_builder_new (void)
{
return (GdkD3D12TextureBuilder *) g_object_new (GDK_TYPE_D3D12_TEXTURE_BUILDER, NULL);
}
/**
* gdk_d3d12_texture_builder_get_resource:
* @self: a `GdkD3D12TextureBuilder`
*
* Returns the resource that this texture builder is
* associated with.
*
* Returns: (nullable) (transfer none): the resource
*
* Since: 4.18
*/
ID3D12Resource *
gdk_d3d12_texture_builder_get_resource (GdkD3D12TextureBuilder *self)
{
g_return_val_if_fail (GDK_IS_D3D12_TEXTURE_BUILDER (self), NULL);
return self->resource;
}
/**
* gdk_d3d12_texture_builder_set_resource:
* @self: a `GdkD3D12TextureBuilder`
* @resource: the resource
*
* Sets the resource that this texture builder is going to construct
* a texture for.
*
* Since: 4.18
*/
void
gdk_d3d12_texture_builder_set_resource (GdkD3D12TextureBuilder *self,
ID3D12Resource *resource)
{
g_return_if_fail (GDK_IS_D3D12_TEXTURE_BUILDER (self));
if (self->resource == resource)
return;
if (resource)
ID3D12Resource_AddRef (resource);
if (self->resource)
ID3D12Resource_Release (self->resource);
self->resource = resource;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_RESOURCE]);
}
/**
* gdk_d3d12_texture_builder_get_fence:
* @self: a `GdkD3D12TextureBuilder`
*
* Returns the fence that this texture builder is
* associated with.
*
* Returns: (nullable) (transfer none): the fence
*
* Since: 4.18
*/
ID3D12Fence *
gdk_d3d12_texture_builder_get_fence (GdkD3D12TextureBuilder *self)
{
g_return_val_if_fail (GDK_IS_D3D12_TEXTURE_BUILDER (self), NULL);
return self->fence;
}
/**
* gdk_d3d12_texture_builder_set_fence:
* @self: a `GdkD3D12TextureBuilder`
* @fence: the fence
*
* Sets the fence that this texture builder is going to construct
* a texture for.
*
* Since: 4.18
*/
void
gdk_d3d12_texture_builder_set_fence (GdkD3D12TextureBuilder *self,
ID3D12Fence *fence)
{
g_return_if_fail (GDK_IS_D3D12_TEXTURE_BUILDER (self));
if (self->fence == fence)
return;
if (fence)
ID3D12Fence_AddRef (fence);
gdk_win32_com_clear (&self->fence);
self->fence = fence;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FENCE]);
}
/**
* gdk_d3d12_texture_builder_get_fence_wait:
* @self: a `GdkD3D12TextureBuilder`
*
* Returns the value that GTK should wait for on the fence
* before using the resource.
*
* Returns: the fence wait value
*
* Since: 4.18
*/
guint64
gdk_d3d12_texture_builder_get_fence_wait (GdkD3D12TextureBuilder *self)
{
g_return_val_if_fail (GDK_IS_D3D12_TEXTURE_BUILDER (self), 0);
return self->fence_wait;
}
/**
* gdk_d3d12_texture_builder_set_fence_wait:
* @self: a `GdkD3D12TextureBuilder`
* @fence_wait: the value to wait on
*
* Sets the value that GTK should wait on on the given fence before using the
* resource.
*
* When no fence is set, this value has no effect.
*
* Since: 4.18
*/
void
gdk_d3d12_texture_builder_set_fence_wait (GdkD3D12TextureBuilder *self,
guint64 fence_wait)
{
g_return_if_fail (GDK_IS_D3D12_TEXTURE_BUILDER (self));
if (self->fence_wait == fence_wait)
return;
self->fence_wait = fence_wait;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FENCE_WAIT]);
}
/**
* gdk_d3d12_texture_builder_get_premultiplied:
* @self: a `GdkD3D12TextureBuilder`
*
* Whether the data is premultiplied.
*
* Returns: whether the data is premultiplied
*
* Since: 4.18
*/
gboolean
gdk_d3d12_texture_builder_get_premultiplied (GdkD3D12TextureBuilder *self)
{
g_return_val_if_fail (GDK_IS_D3D12_TEXTURE_BUILDER (self), TRUE);
return self->premultiplied;
}
/**
* gdk_d3d12_texture_builder_set_premultiplied:
* @self: a `GdkD3D12TextureBuilder`
* @premultiplied: whether the data is premultiplied
*
* Sets whether the data is premultiplied.
*
* Unless otherwise specified, all formats including alpha channels are assumed
* to be premultiplied.
*
* Since: 4.18
*/
void
gdk_d3d12_texture_builder_set_premultiplied (GdkD3D12TextureBuilder *self,
gboolean premultiplied)
{
g_return_if_fail (GDK_IS_D3D12_TEXTURE_BUILDER (self));
if (self->premultiplied == premultiplied)
return;
self->premultiplied = premultiplied;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PREMULTIPLIED]);
}
/**
* gdk_d3d12_texture_builder_get_color_state:
* @self: a `GdkD3D12TextureBuilder`
*
* Gets the color state previously set via gdk_d3d12_texture_builder_set_color_state().
*
* Returns: (nullable) (transfer none): the color state
*
* Since: 4.18
*/
GdkColorState *
gdk_d3d12_texture_builder_get_color_state (GdkD3D12TextureBuilder *self)
{
g_return_val_if_fail (GDK_IS_D3D12_TEXTURE_BUILDER (self), NULL);
return self->color_state;
}
/**
* gdk_d3d12_texture_builder_set_color_state:
* @self: a `GdkD3D12TextureBuilder`
* @color_state: (nullable): a `GdkColorState` or `NULL` to unset the colorstate.
*
* Sets the color state for the texture.
*
* By default, the colorstate is `NULL`. In that case, GTK will choose the
* correct colorstate based on the format.
* If you don't know what colorstates are, this is probably the right thing.
*
* Since: 4.18
*/
void
gdk_d3d12_texture_builder_set_color_state (GdkD3D12TextureBuilder *self,
GdkColorState *color_state)
{
g_return_if_fail (GDK_IS_D3D12_TEXTURE_BUILDER (self));
if (self->color_state == color_state ||
(self->color_state != NULL && color_state != NULL && gdk_color_state_equal (self->color_state, color_state)))
return;
g_clear_pointer (&self->color_state, gdk_color_state_unref);
self->color_state = color_state;
if (color_state)
gdk_color_state_ref (color_state);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COLOR_STATE]);
}
/**
* gdk_d3d12_texture_builder_get_update_texture:
* @self: a `GdkD3D12TextureBuilder`
*
* Gets the texture previously set via gdk_d3d12_texture_builder_set_update_texture() or
* %NULL if none was set.
*
* Returns: (transfer none) (nullable): The texture
*
* Since: 4.18
*/
GdkTexture *
gdk_d3d12_texture_builder_get_update_texture (GdkD3D12TextureBuilder *self)
{
g_return_val_if_fail (GDK_IS_D3D12_TEXTURE_BUILDER (self), NULL);
return self->update_texture;
}
/**
* gdk_d3d12_texture_builder_set_update_texture:
* @self: a `GdkD3D12TextureBuilder`
* @texture: (nullable): the texture to update
*
* Sets the texture to be updated by this texture. See
* [method@Gdk.D3d12TextureBuilder.set_update_region] for an explanation.
*
* Since: 4.18
*/
void
gdk_d3d12_texture_builder_set_update_texture (GdkD3D12TextureBuilder *self,
GdkTexture *texture)
{
g_return_if_fail (GDK_IS_D3D12_TEXTURE_BUILDER (self));
g_return_if_fail (texture == NULL || GDK_IS_TEXTURE (texture));
if (!g_set_object (&self->update_texture, texture))
return;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_UPDATE_TEXTURE]);
}
/**
* gdk_d3d12_texture_builder_get_update_region:
* @self: a `GdkD3D12TextureBuilder`
*
* Gets the region previously set via gdk_d3d12_texture_builder_set_update_region() or
* %NULL if none was set.
*
* Returns: (transfer none) (nullable): The region
*
* Since: 4.18
*/
cairo_region_t *
gdk_d3d12_texture_builder_get_update_region (GdkD3D12TextureBuilder *self)
{
g_return_val_if_fail (GDK_IS_D3D12_TEXTURE_BUILDER (self), NULL);
return self->update_region;
}
/**
* gdk_d3d12_texture_builder_set_update_region:
* @self: a `GdkD3D12TextureBuilder`
* @region: (nullable): the region to update
*
* Sets the region to be updated by this texture. Together with
* [property@Gdk.D3d12TextureBuilder:update-texture] this describes an
* update of a previous texture.
*
* When rendering animations of large textures, it is possible that
* consecutive textures are only updating contents in parts of the texture.
* It is then possible to describe this update via these two properties,
* so that GTK can avoid rerendering parts that did not change.
*
* An example would be a screen recording where only the mouse pointer moves.
*
* Since: 4.18
*/
void
gdk_d3d12_texture_builder_set_update_region (GdkD3D12TextureBuilder *self,
cairo_region_t *region)
{
g_return_if_fail (GDK_IS_D3D12_TEXTURE_BUILDER (self));
if (self->update_region == region)
return;
g_clear_pointer (&self->update_region, cairo_region_destroy);
if (region)
self->update_region = cairo_region_reference (region);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_UPDATE_REGION]);
}
/**
* gdk_d3d12_texture_builder_build:
* @self: a `GdkD3D12TextureBuilder`
* @destroy: (nullable): destroy function to be called when the texture is
* released
* @data: user data to pass to the destroy function
* @error: Return location for an error
*
* Builds a new `GdkTexture` with the values set up in the builder.
*
* It is a programming error to call this function if any mandatory property has not been set.
*
* Not all formats defined in the `drm_fourcc.h` header are supported. You can use
* [method@Gdk.Display.get_d3d12_formats] to get a list of supported formats. If the
* format is not supported by GTK, %NULL will be returned and @error will be set.
*
* The `destroy` function gets called when the returned texture gets released.
*
* It is the responsibility of the caller to keep the file descriptors for the planes
* open until the created texture is no longer used, and close them afterwards (possibly
* using the @destroy notify).
*
* It is possible to call this function multiple times to create multiple textures,
* possibly with changing properties in between.
*
* Returns: (transfer full) (nullable): a newly built `GdkTexture` or `NULL`
* if the format is not supported
*
* Since: 4.18
*/
GdkTexture *
gdk_d3d12_texture_builder_build (GdkD3D12TextureBuilder *self,
GDestroyNotify destroy,
gpointer data,
GError **error)
{
g_return_val_if_fail (GDK_IS_D3D12_TEXTURE_BUILDER (self), NULL);
g_return_val_if_fail (destroy == NULL || data != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
g_return_val_if_fail (self->resource, NULL);
return gdk_d3d12_texture_new_from_builder (self, destroy, data, error);
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright © 2024 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#pragma once
#if !defined (__GDKWIN32_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gdk/gdk.h> can be included directly."
#endif
#include <gdk/gdktypes.h>
#include <directx/d3d12.h>
G_BEGIN_DECLS
#define GDK_TYPE_D3D12_TEXTURE_BUILDER (gdk_d3d12_texture_builder_get_type ())
GDK_AVAILABLE_IN_4_18
GDK_DECLARE_INTERNAL_TYPE (GdkD3D12TextureBuilder, gdk_d3d12_texture_builder, GDK, D3D12_TEXTURE_BUILDER, GObject)
typedef struct _GdkD3D12TextureBuilder GdkD3D12TextureBuilder;
GDK_AVAILABLE_IN_4_18
GdkD3D12TextureBuilder * gdk_d3d12_texture_builder_new (void);
GDK_AVAILABLE_IN_4_18
ID3D12Resource * gdk_d3d12_texture_builder_get_resource (GdkD3D12TextureBuilder *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_4_18
void gdk_d3d12_texture_builder_set_resource (GdkD3D12TextureBuilder *self,
ID3D12Resource *resource);
GDK_AVAILABLE_IN_4_18
ID3D12Fence * gdk_d3d12_texture_builder_get_fence (GdkD3D12TextureBuilder *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_4_18
void gdk_d3d12_texture_builder_set_fence (GdkD3D12TextureBuilder *self,
ID3D12Fence *fence);
GDK_AVAILABLE_IN_4_18
guint64 gdk_d3d12_texture_builder_get_fence_wait (GdkD3D12TextureBuilder *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_4_18
void gdk_d3d12_texture_builder_set_fence_wait (GdkD3D12TextureBuilder *self,
guint64 fence_wait);
GDK_AVAILABLE_IN_4_18
gboolean gdk_d3d12_texture_builder_get_premultiplied (GdkD3D12TextureBuilder *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_4_18
void gdk_d3d12_texture_builder_set_premultiplied (GdkD3D12TextureBuilder *self,
gboolean premultiplied);
GDK_AVAILABLE_IN_4_18
GdkColorState * gdk_d3d12_texture_builder_get_color_state (GdkD3D12TextureBuilder *self);
GDK_AVAILABLE_IN_4_18
void gdk_d3d12_texture_builder_set_color_state (GdkD3D12TextureBuilder *self,
GdkColorState *color_state);
GDK_AVAILABLE_IN_4_18
GdkTexture * gdk_d3d12_texture_builder_get_update_texture (GdkD3D12TextureBuilder *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_4_18
void gdk_d3d12_texture_builder_set_update_texture (GdkD3D12TextureBuilder *self,
GdkTexture *texture);
GDK_AVAILABLE_IN_4_18
cairo_region_t * gdk_d3d12_texture_builder_get_update_region (GdkD3D12TextureBuilder *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_4_18
void gdk_d3d12_texture_builder_set_update_region (GdkD3D12TextureBuilder *self,
cairo_region_t *region);
GDK_AVAILABLE_IN_4_18
GdkTexture * gdk_d3d12_texture_builder_build (GdkD3D12TextureBuilder *self,
GDestroyNotify destroy,
gpointer data,
GError **error);
G_END_DECLS

View File

@@ -0,0 +1,25 @@
#pragma once
#include "gdkd3d12texture.h"
#include "gdkd3d12texturebuilder.h"
G_BEGIN_DECLS
GdkTexture * gdk_d3d12_texture_new_from_builder (GdkD3D12TextureBuilder *builder,
GDestroyNotify destroy,
gpointer data,
GError **error);
ID3D12Resource * gdk_d3d12_texture_get_resource (GdkD3D12Texture *self);
HANDLE gdk_d3d12_texture_get_resource_handle (GdkD3D12Texture *self);
ID3D12Fence * gdk_d3d12_texture_get_fence (GdkD3D12Texture *self);
HANDLE gdk_d3d12_texture_get_fence_handle (GdkD3D12Texture *self);
guint64 gdk_d3d12_texture_get_fence_wait (GdkD3D12Texture *self);
guint gdk_d3d12_texture_import_gl (GdkD3D12Texture *self,
GdkGLContext *context,
guint *out_mem_id);
G_END_DECLS

1338
gdk/win32/gdkdxgiformat.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
#pragma once
#include <gdk/gdkenums.h>
#include <gdk/gdktypes.h>
#include <directx/d3d12.h>
#include <epoxy/gl.h>
#ifdef GDK_RENDERING_VULKAN
#include <vulkan/vulkan.h>
#endif
gboolean gdk_dxgi_format_is_supported (DXGI_FORMAT dxgi_format);
GdkMemoryFormat gdk_dxgi_format_get_memory_format (DXGI_FORMAT dxgi_format,
gboolean premultiplied);
gboolean gdk_dxgi_format_is_yuv (DXGI_FORMAT dxgi_format);
GLenum gdk_dxgi_format_get_gl_format (DXGI_FORMAT dxgi_format);
#ifdef GDK_RENDERING_VULKAN
VkFormat gdk_dxgi_format_get_vk_format (DXGI_FORMAT dxgi_format,
VkComponentMapping *out_swizzle);
#endif
void gdk_dxgi_format_convert (DXGI_FORMAT src_format,
gboolean src_premultiplied,
const guchar *src_data,
gsize src_stride,
GdkColorState *src_color_state,
GdkMemoryFormat dest_format,
guchar *dest_data,
gsize dest_stride,
GdkColorState *dest_color_state,
gsize width,
gsize height);

View File

@@ -26,6 +26,8 @@
#define __GDKWIN32_H_INSIDE__
#include <gdk/win32/gdkd3d12texture.h>
#include <gdk/win32/gdkd3d12texturebuilder.h>
#include <gdk/win32/gdkwin32cursor.h>
#include <gdk/win32/gdkwin32display.h>
#include <gdk/win32/gdkwin32displaymanager.h>

View File

@@ -1,5 +1,7 @@
gdk_win32_public_sources = files([
'gdkcursor-win32.c',
'gdkd3d12texture.c',
'gdkd3d12texturebuilder.c',
'gdkdisplay-win32.c',
'gdkdisplaymanager-win32.c',
'gdkdrag-win32.c',
@@ -21,6 +23,7 @@ gdk_win32_sources = gdk_win32_public_sources + files([
'gdkdevice-win32.c',
'gdkdevice-winpointer.c',
'gdkdevice-wintab.c',
'gdkdxgiformat.c',
'gdkdrop-win32.c',
'gdkglcontext-win32-wgl-private.c',
'gdkhdataoutputstream-win32.c',
@@ -38,6 +41,8 @@ gdk_win32_sources = gdk_win32_public_sources + files([
])
gdk_win32_public_headers = files([
'gdkd3d12texture.h',
'gdkd3d12texturebuilder.h',
'gdkwin32cursor.h',
'gdkwin32display.h',
'gdkwin32displaymanager.h',
@@ -55,10 +60,17 @@ if have_egl
gdk_win32_sources += ['gdkglcontext-win32-egl.c']
endif
d3d12_dep = [
cc.find_library('d3d12'),
cc.find_library('dxgi'),
dependency('DirectX-Headers', version: '>= 1.611'),
]
gdk_win32_deps = [
pangowin32_dep, # FIXME
cc.find_library('hid'),
cc.find_library('opengl32'),
d3d12_dep,
]
libgdk_win32 = static_library('gdk-win32',

View File

@@ -13,6 +13,9 @@
#include "gdkglcontextprivate.h"
#include "gdkgltextureprivate.h"
#ifdef GDK_WINDOWING_WIN32
#include "win32/gdkd3d12textureprivate.h"
#endif
struct _GskGLFrame
{
GskGpuFrame parent_instance;
@@ -89,6 +92,7 @@ gsk_gl_frame_upload_texture (GskGpuFrame *frame,
image = gsk_gl_image_new_for_texture (GSK_GL_DEVICE (gsk_gpu_frame_get_device (frame)),
texture,
gdk_gl_texture_get_id (gl_texture),
0,
FALSE,
gdk_gl_texture_has_mipmap (gl_texture) ? (GSK_GPU_IMAGE_CAN_MIPMAP | GSK_GPU_IMAGE_MIPMAP) : 0);
@@ -115,10 +119,30 @@ gsk_gl_frame_upload_texture (GskGpuFrame *frame,
return gsk_gl_image_new_for_texture (GSK_GL_DEVICE (gsk_gpu_frame_get_device (frame)),
texture,
tex_id,
0,
TRUE,
(external ? GSK_GPU_IMAGE_EXTERNAL | GSK_GPU_IMAGE_NO_BLIT : 0));
}
}
#ifdef GDK_WINDOWING_WIN32
else if (GDK_IS_D3D12_TEXTURE (texture))
{
guint tex_id, mem_id;
tex_id = gdk_d3d12_texture_import_gl (GDK_D3D12_TEXTURE (texture),
GDK_GL_CONTEXT (gsk_gpu_frame_get_context (frame)),
&mem_id);
if (tex_id)
{
return gsk_gl_image_new_for_texture (GSK_GL_DEVICE (gsk_gpu_frame_get_device (frame)),
texture,
tex_id,
mem_id,
TRUE,
0);
}
}
#endif
return GSK_GPU_FRAME_CLASS (gsk_gl_frame_parent_class)->upload_texture (frame, with_mipmap, texture);
}

View File

@@ -10,6 +10,7 @@ struct _GskGLImage
GskGpuImage parent_instance;
guint texture_id;
guint memory_id;
guint framebuffer_id;
GLint gl_internal_format;
@@ -49,6 +50,9 @@ gsk_gl_image_finalize (GObject *object)
if (self->owns_texture)
glDeleteTextures (1, &self->texture_id);
if (self->memory_id)
glDeleteMemoryObjectsEXT (1, &self->memory_id);
G_OBJECT_CLASS (gsk_gl_image_parent_class)->finalize (object);
}
@@ -216,6 +220,7 @@ GskGpuImage *
gsk_gl_image_new_for_texture (GskGLDevice *device,
GdkTexture *owner,
GLuint tex_id,
GLuint mem_id,
gboolean take_ownership,
GskGpuImageFlags extra_flags)
{
@@ -258,6 +263,7 @@ gsk_gl_image_new_for_texture (GskGLDevice *device,
gsk_gpu_image_toggle_ref_texture (GSK_GPU_IMAGE (self), owner);
self->texture_id = tex_id;
self->memory_id = mem_id;
self->owns_texture = take_ownership;
return GSK_GPU_IMAGE (self);

View File

@@ -25,6 +25,7 @@ GskGpuImage * gsk_gl_image_new (GskGLDe
GskGpuImage * gsk_gl_image_new_for_texture (GskGLDevice *device,
GdkTexture *owner,
GLuint tex_id,
GLuint mem_id,
gboolean take_ownership,
GskGpuImageFlags extra_flags);

View File

@@ -74,6 +74,7 @@ gsk_vulkan_buffer_new_internal (GskVulkanDevice *device,
{
VkMemoryRequirements requirements;
GskVulkanBuffer *self;
gsize memory_index;
self = g_object_new (GSK_TYPE_VULKAN_BUFFER, NULL);
@@ -94,11 +95,14 @@ gsk_vulkan_buffer_new_internal (GskVulkanDevice *device,
self->vk_buffer,
&requirements);
self->allocator = gsk_vulkan_device_find_allocator (device,
requirements.memoryTypeBits,
GSK_VULKAN_MEMORY_MAPPABLE,
GSK_VULKAN_MEMORY_MAPPABLE |
VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
memory_index = gsk_vulkan_device_find_allocator (device,
requirements.memoryTypeBits,
GSK_VULKAN_MEMORY_MAPPABLE,
GSK_VULKAN_MEMORY_MAPPABLE |
VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
self->allocator = gsk_vulkan_device_get_allocator (device, memory_index);
gsk_vulkan_allocator_ref (self->allocator);
gsk_vulkan_alloc (self->allocator,
requirements.size,
requirements.alignment,

View File

@@ -1033,16 +1033,20 @@ gsk_vulkan_device_get_vk_pipeline (GskVulkanDevice *self,
return vk_pipeline;
}
static GskVulkanAllocator *
GskVulkanAllocator *
gsk_vulkan_device_get_allocator (GskVulkanDevice *self,
gsize index,
const VkMemoryType *type)
gsize index)
{
if (self->allocators[index] == NULL)
{
VkPhysicalDeviceMemoryProperties properties;
vkGetPhysicalDeviceMemoryProperties (gsk_vulkan_device_get_vk_physical_device (self),
&properties);
self->allocators[index] = gsk_vulkan_direct_allocator_new (gsk_vulkan_device_get_vk_device (self),
index,
type);
&properties.memoryTypes[index]);
self->allocators[index] = gsk_vulkan_buddy_allocator_new (self->allocators[index],
1024 * 1024);
//allocators[index] = gsk_vulkan_stats_allocator_new (allocators[index]);
@@ -1053,11 +1057,11 @@ gsk_vulkan_device_get_allocator (GskVulkanDevice *self,
/* following code found in
* https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceMemoryProperties.html */
GskVulkanAllocator *
gsk_vulkan_device_find_allocator (GskVulkanDevice *self,
uint32_t allowed_types,
VkMemoryPropertyFlags required_flags,
VkMemoryPropertyFlags desired_flags)
gsize
gsk_vulkan_device_find_allocator (GskVulkanDevice *self,
uint32_t allowed_types,
VkMemoryPropertyFlags required_flags,
VkMemoryPropertyFlags desired_flags)
{
VkPhysicalDeviceMemoryProperties properties;
uint32_t i, found;
@@ -1085,7 +1089,7 @@ gsk_vulkan_device_find_allocator (GskVulkanDevice *self,
g_assert (found < properties.memoryTypeCount);
return gsk_vulkan_allocator_ref (gsk_vulkan_device_get_allocator (self, found, &properties.memoryTypes[found]));
return found;
}
GskVulkanAllocator *

View File

@@ -67,10 +67,14 @@ VkPipeline gsk_vulkan_device_get_vk_pipeline (GskVulk
VkRenderPass render_pass);
GskVulkanAllocator * gsk_vulkan_device_get_external_allocator (GskVulkanDevice *self);
GskVulkanAllocator * gsk_vulkan_device_find_allocator (GskVulkanDevice *self,
GskVulkanAllocator * gsk_vulkan_device_get_allocator (GskVulkanDevice *self,
gsize index);
gsize gsk_vulkan_device_find_allocator (GskVulkanDevice *self,
uint32_t allowed_types,
VkMemoryPropertyFlags required_flags,
VkMemoryPropertyFlags desired_flags);
static inline VkResult
gsk_vulkan_handle_result (VkResult res,
const char *called_function)

View File

@@ -11,6 +11,10 @@
#include "gdk/gdkglcontextprivate.h"
#include "gdk/gdkgltextureprivate.h"
#ifdef GDK_WINDOWING_WIN32
#include "gdk/win32/gdkd3d12textureprivate.h"
#endif
#define GDK_ARRAY_NAME gsk_semaphores
#define GDK_ARRAY_TYPE_NAME GskSemaphores
#define GDK_ARRAY_ELEMENT_TYPE VkSemaphore
@@ -18,6 +22,13 @@
#define GDK_ARRAY_NO_MEMSET 1
#include "gdk/gdkarrayimpl.c"
#define GDK_ARRAY_NAME gsk_semaphore_values
#define GDK_ARRAY_TYPE_NAME GskSemaphoreValues
#define GDK_ARRAY_ELEMENT_TYPE uint64_t
#define GDK_ARRAY_PREALLOC 16
#define GDK_ARRAY_NO_MEMSET 1
#include "gdk/gdkarrayimpl.c"
#define GDK_ARRAY_NAME gsk_pipeline_stages
#define GDK_ARRAY_TYPE_NAME GskPipelineStages
#define GDK_ARRAY_ELEMENT_TYPE VkPipelineStageFlags
@@ -28,6 +39,7 @@
struct _GskVulkanSemaphores
{
GskSemaphores wait_semaphores;
GskSemaphoreValues wait_semaphore_values;
GskPipelineStages wait_stages;
GskSemaphores signal_semaphores;
};
@@ -218,6 +230,26 @@ gsk_vulkan_frame_upload_texture (GskGpuFrame *frame,
}
}
#endif
#ifdef GDK_WINDOWING_WIN32
if (GDK_IS_D3D12_TEXTURE (texture))
{
GdkD3D12Texture *d3d_texture = GDK_D3D12_TEXTURE (texture);
GskGpuImage *image;
image = gsk_vulkan_image_new_for_d3d12resource (GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (frame)),
gdk_d3d12_texture_get_resource (d3d_texture),
gdk_d3d12_texture_get_resource_handle (d3d_texture),
gdk_d3d12_texture_get_fence (d3d_texture),
gdk_d3d12_texture_get_fence_handle (d3d_texture),
gdk_d3d12_texture_get_fence_wait (d3d_texture),
gdk_memory_format_alpha (gdk_texture_get_format (texture)) != GDK_MEMORY_ALPHA_STRAIGHT);
if (image)
{
gsk_gpu_image_toggle_ref_texture (image, texture);
return image;
}
}
#endif
return GSK_GPU_FRAME_CLASS (gsk_vulkan_frame_parent_class)->upload_texture (frame, with_mipmap, texture);
}
@@ -260,9 +292,12 @@ gsk_vulkan_frame_submit (GskGpuFrame *frame,
GskGpuOp *op)
{
GskVulkanFrame *self = GSK_VULKAN_FRAME (frame);
GskVulkanDevice *device;
GskVulkanSemaphores semaphores;
GskVulkanCommandState state = { 0, };
device = GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (frame));
GSK_VK_CHECK (vkBeginCommandBuffer, self->vk_command_buffer,
&(VkCommandBufferBeginInfo) {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
@@ -279,6 +314,7 @@ gsk_vulkan_frame_submit (GskGpuFrame *frame,
(VkDeviceSize[1]) { 0 });
gsk_semaphores_init (&semaphores.wait_semaphores);
gsk_semaphore_values_init (&semaphores.wait_semaphore_values);
gsk_pipeline_stages_init (&semaphores.wait_stages);
gsk_semaphores_init (&semaphores.signal_semaphores);
@@ -286,6 +322,7 @@ gsk_vulkan_frame_submit (GskGpuFrame *frame,
{
gsk_vulkan_semaphores_add_wait (&semaphores,
self->vk_acquire_semaphore,
0,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
}
@@ -302,7 +339,7 @@ gsk_vulkan_frame_submit (GskGpuFrame *frame,
GSK_VK_CHECK (vkEndCommandBuffer, self->vk_command_buffer);
GSK_VK_CHECK (vkQueueSubmit, gsk_vulkan_device_get_vk_queue (GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (frame))),
GSK_VK_CHECK (vkQueueSubmit, gsk_vulkan_device_get_vk_queue (device),
1,
&(VkSubmitInfo) {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
@@ -313,10 +350,16 @@ gsk_vulkan_frame_submit (GskGpuFrame *frame,
.waitSemaphoreCount = gsk_semaphores_get_size (&semaphores.wait_semaphores),
.pSignalSemaphores = gsk_semaphores_get_data (&semaphores.signal_semaphores),
.signalSemaphoreCount = gsk_semaphores_get_size (&semaphores.signal_semaphores),
.pNext = gsk_vulkan_device_has_feature (device, GDK_VULKAN_FEATURE_TIMELINE_SEMAPHORE) ? &(VkTimelineSemaphoreSubmitInfo) {
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
.waitSemaphoreValueCount = gsk_semaphore_values_get_size (&semaphores.wait_semaphore_values),
.pWaitSemaphoreValues = gsk_semaphore_values_get_data (&semaphores.wait_semaphore_values),
} : NULL,
},
self->vk_fence);
gsk_semaphores_clear (&semaphores.wait_semaphores);
gsk_semaphore_values_clear (&semaphores.wait_semaphore_values);
gsk_pipeline_stages_clear (&semaphores.wait_stages);
gsk_semaphores_clear (&semaphores.signal_semaphores);
}
@@ -384,9 +427,11 @@ gsk_vulkan_frame_get_vk_fence (GskVulkanFrame *self)
void
gsk_vulkan_semaphores_add_wait (GskVulkanSemaphores *self,
VkSemaphore semaphore,
guint64 semaphore_wait,
VkPipelineStageFlags stage)
{
gsk_semaphores_append (&self->wait_semaphores, semaphore);
gsk_semaphore_values_append (&self->wait_semaphore_values, semaphore_wait);
gsk_pipeline_stages_append (&self->wait_stages, stage);
}

View File

@@ -14,6 +14,7 @@ VkFence gsk_vulkan_frame_get_vk_fence (GskVulk
void gsk_vulkan_semaphores_add_wait (GskVulkanSemaphores *self,
VkSemaphore semaphore,
uint64_t semaphore_wait,
VkPipelineStageFlags stage);
void gsk_vulkan_semaphores_add_signal (GskVulkanSemaphores *self,
VkSemaphore semaphore);

View File

@@ -18,6 +18,9 @@
#ifdef HAVE_DMABUF
#include <linux/dma-buf.h>
#endif
#ifdef GDK_WINDOWING_WIN32
#include <gdk/win32/gdkdxgiformatprivate.h>
#endif
struct _GskVulkanImage
{
@@ -34,6 +37,7 @@ struct _GskVulkanImage
VkImageView vk_framebuffer_image_view;
GskVulkanYcbcr *ycbcr;
VkSemaphore vk_semaphore;
uint64_t vk_semaphore_wait;
struct {
VkDescriptorSet vk_descriptor_set;
gsize pool_id;
@@ -265,6 +269,7 @@ gsk_vulkan_image_new (GskVulkanDevice *device,
GskGpuImageFlags flags;
VkFormat vk_format, vk_srgb_format;
VkComponentMapping vk_components;
gsize memory_index;
g_assert (width > 0 && height > 0);
@@ -386,10 +391,13 @@ gsk_vulkan_image_new (GskVulkanDevice *device,
self->vk_image,
&requirements);
self->allocator = gsk_vulkan_device_find_allocator (device,
requirements.memoryTypeBits,
0,
tiling == VK_IMAGE_TILING_LINEAR ? GSK_VULKAN_MEMORY_MAPPABLE : 0);
memory_index = gsk_vulkan_device_find_allocator (device,
requirements.memoryTypeBits,
0,
tiling == VK_IMAGE_TILING_LINEAR ? GSK_VULKAN_MEMORY_MAPPABLE : 0);
self->allocator = gsk_vulkan_device_get_allocator (device, memory_index);
gsk_vulkan_allocator_ref (self->allocator);
gsk_vulkan_alloc (self->allocator,
requirements.size,
requirements.alignment,
@@ -676,6 +684,7 @@ gsk_vulkan_image_new_dmabuf (GskVulkanDevice *device,
VkFormat vk_format, vk_srgb_format;
VkComponentMapping vk_components;
VkMemoryRequirements requirements;
gsize memory_index;
GskVulkanImage *self;
VkResult res;
gsize n_modifiers;
@@ -809,6 +818,10 @@ gsk_vulkan_image_new_dmabuf (GskVulkanDevice *device,
self->vk_image,
&requirements);
memory_index = gsk_vulkan_device_find_allocator (device,
requirements.memoryTypeBits,
0,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
self->allocator = gsk_vulkan_device_get_external_allocator (device);
gsk_vulkan_allocator_ref (self->allocator);
@@ -821,7 +834,7 @@ gsk_vulkan_image_new_dmabuf (GskVulkanDevice *device,
&(VkMemoryAllocateInfo) {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = requirements.size,
.memoryTypeIndex = g_bit_nth_lsf (requirements.memoryTypeBits, -1),
.memoryTypeIndex = memory_index,
.pNext = &(VkExportMemoryAllocateInfo) {
.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO,
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
@@ -994,6 +1007,7 @@ gsk_vulkan_image_new_for_dmabuf (GskVulkanDevice *device,
VkMemoryRequirements2 requirements = {
.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2,
};
gsize memory_index;
GSK_VK_CHECK (func_vkGetMemoryFdPropertiesKHR, vk_device,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
@@ -1037,6 +1051,10 @@ gsk_vulkan_image_new_for_dmabuf (GskVulkanDevice *device,
}
}
memory_index = gsk_vulkan_device_find_allocator (device,
fd_props.memoryTypeBits,
0,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
gsk_vulkan_alloc (self->allocator,
requirements.memoryRequirements.size,
requirements.memoryRequirements.alignment,
@@ -1045,7 +1063,7 @@ gsk_vulkan_image_new_for_dmabuf (GskVulkanDevice *device,
&(VkMemoryAllocateInfo) {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = requirements.memoryRequirements.size,
.memoryTypeIndex = g_bit_nth_lsf (fd_props.memoryTypeBits, -1),
.memoryTypeIndex = memory_index,
.pNext = &(VkImportMemoryFdInfoKHR) {
.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR,
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
@@ -1269,6 +1287,236 @@ gsk_vulkan_image_to_dmabuf_texture (GskVulkanImage *self,
}
#endif
#ifdef GDK_WINDOWING_WIN32
static gboolean
gsk_vulkan_is_same_device (VkDevice vk_device,
ID3D12DeviceChild *child)
{
/* FIXME: implement */
return TRUE;
}
GskGpuImage *
gsk_vulkan_image_new_for_d3d12resource (GskVulkanDevice *device,
ID3D12Resource *resource,
HANDLE resource_handle,
ID3D12Fence *fence,
HANDLE fence_handle,
guint64 fence_wait,
gboolean premultiplied)
{
GskVulkanImage *self;
VkDevice vk_device;
VkFormat vk_format;
VkComponentMapping vk_components;
VkSamplerYcbcrConversion vk_conversion;
PFN_vkGetMemoryWin32HandlePropertiesKHR func_vkGetMemoryWin32HandlePropertiesKHR;
VkResult res;
GdkMemoryFormat format;
GskGpuImageFlags flags;
D3D12_RESOURCE_DESC desc;
gsize memory_index;
gboolean is_yuv;
VkMemoryRequirements2 requirements = {
.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2,
};
VkMemoryWin32HandlePropertiesKHR handle_properties = {
.sType = VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHR,
};
if (!gsk_vulkan_device_has_feature (device, GDK_VULKAN_FEATURE_WIN32))
{
GDK_DEBUG (D3D12, "Vulkan does not support D3D12Resource import");
return NULL;
}
vk_device = gsk_vulkan_device_get_vk_device (device);
func_vkGetMemoryWin32HandlePropertiesKHR = (PFN_vkGetMemoryWin32HandlePropertiesKHR) vkGetDeviceProcAddr (vk_device, "vkGetMemoryWin32HandlePropertiesKHR");
ID3D12Resource_GetDesc (resource, &desc);
if (!gsk_vulkan_is_same_device (vk_device, (ID3D12DeviceChild *) resource))
{
GDK_DEBUG (D3D12, "Resource is from a different device");
return NULL;
}
vk_format = gdk_dxgi_format_get_vk_format (desc.Format, &vk_components);
if (vk_format == VK_FORMAT_UNDEFINED)
{
GDK_DEBUG (D3D12, "GTK's Vulkan doesn't support DXGI format %u", desc.Format);
return NULL;
}
is_yuv = gdk_dxgi_format_is_yuv (desc.Format);
if (is_yuv && !gsk_vulkan_device_has_feature (device, GDK_VULKAN_FEATURE_YCBCR))
{
GDK_DEBUG (D3D12, "Vulkan driver cannot import DXGI format %u because it is YUV", desc.Format);
return NULL;
}
#if 0
if (!gsk_vulkan_device_supports_format (device,
vk_format,
dmabuf->modifier,
dmabuf->n_planes,
VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT,
VK_IMAGE_USAGE_SAMPLED_BIT,
width, height,
&flags))
{
GDK_DEBUG (DMABUF, "Vulkan driver does not support format %.4s::%016llx with %u planes",
(char *) &dmabuf->fourcc, (unsigned long long) dmabuf->modifier, dmabuf->n_planes);
return NULL;
}
#else
flags = 0;
#endif
self = g_object_new (GSK_TYPE_VULKAN_IMAGE, NULL);
self->device = g_object_ref (device);
self->vk_tiling = VK_IMAGE_TILING_OPTIMAL;
self->vk_format = vk_format;
self->vk_pipeline_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
self->vk_image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
self->vk_access = 0;
res = vkCreateImage (vk_device,
&(VkImageCreateInfo) {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.flags = 0,
.imageType = VK_IMAGE_TYPE_2D,
.format = vk_format,
.extent = { desc.Width, desc.Height, 1 },
.mipLevels = desc.MipLevels,
.arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = self->vk_tiling,
.usage = VK_IMAGE_USAGE_SAMPLED_BIT |
(flags & GSK_GPU_IMAGE_NO_BLIT ? 0 : VK_IMAGE_USAGE_TRANSFER_SRC_BIT),
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.initialLayout = self->vk_image_layout,
.pNext = &(VkExternalMemoryImageCreateInfo) {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT,
},
},
NULL,
&self->vk_image);
if (res != VK_SUCCESS)
{
GDK_DEBUG (D3D12, "vkCreateImage() failed: %s", gdk_vulkan_strerror (res));
return NULL;
}
gsk_gpu_image_setup (GSK_GPU_IMAGE (self),
flags |
(!premultiplied ? GSK_GPU_IMAGE_STRAIGHT_ALPHA : 0) |
(is_yuv ? (GSK_GPU_IMAGE_EXTERNAL | GSK_GPU_IMAGE_NO_BLIT) : 0) |
(desc.MipLevels > 1 ? GSK_GPU_IMAGE_CAN_MIPMAP | GSK_GPU_IMAGE_MIPMAP : 0),
gdk_dxgi_format_get_memory_format (desc.Format, premultiplied),
desc.Width, desc.Height);
vkGetImageMemoryRequirements2 (vk_device,
&(VkImageMemoryRequirementsInfo2) {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2,
.image = self->vk_image,
},
&requirements);
// Vulkan memory import
GSK_VK_CHECK (func_vkGetMemoryWin32HandlePropertiesKHR, vk_device,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT,
resource_handle,
&handle_properties);
memory_index = gsk_vulkan_device_find_allocator (device,
handle_properties.memoryTypeBits,
0,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
self->allocator = gsk_vulkan_device_get_external_allocator (device);
gsk_vulkan_allocator_ref (self->allocator);
gsk_vulkan_alloc (self->allocator,
requirements.memoryRequirements.size,
requirements.memoryRequirements.alignment,
&self->allocation);
GSK_VK_CHECK (vkAllocateMemory, vk_device,
&(VkMemoryAllocateInfo) {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = requirements.memoryRequirements.size,
.memoryTypeIndex = memory_index,
.pNext = &(VkImportMemoryWin32HandleInfoKHR) {
.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR,
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT,
.handle = resource_handle,
.pNext = &(VkMemoryDedicatedAllocateInfo) {
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
.image = self->vk_image,
}
}
},
NULL,
&self->allocation.vk_memory);
GSK_VK_CHECK (vkBindImageMemory2, gsk_vulkan_device_get_vk_device (self->device),
1,
&(VkBindImageMemoryInfo) {
.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO,
.image = self->vk_image,
.memory = self->allocation.vk_memory,
.memoryOffset = self->allocation.offset,
});
if (gsk_vulkan_device_has_feature (device, GDK_VULKAN_FEATURE_WIN32_SEMAPHORE) && fence)
{
PFN_vkImportSemaphoreWin32HandleKHR func_vkImportSemaphoreWin32HandleKHR;
func_vkImportSemaphoreWin32HandleKHR = (PFN_vkImportSemaphoreWin32HandleKHR) vkGetDeviceProcAddr (vk_device, "vkImportSemaphoreWin32HandleKHR");
GSK_VK_CHECK (vkCreateSemaphore, vk_device,
&(VkSemaphoreCreateInfo) {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = &(VkSemaphoreTypeCreateInfo) {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE,
},
},
NULL,
&self->vk_semaphore);
GSK_VK_CHECK (func_vkImportSemaphoreWin32HandleKHR, vk_device,
&(VkImportSemaphoreWin32HandleInfoKHR) {
.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR,
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT,
.semaphore = self->vk_semaphore,
.handle = fence_handle,
});
self->vk_semaphore_wait = fence_wait;
}
if (is_yuv)
{
self->ycbcr = gsk_vulkan_device_get_ycbcr (device, vk_format);
gsk_vulkan_ycbcr_ref (self->ycbcr);
vk_conversion = gsk_vulkan_ycbcr_get_vk_conversion (self->ycbcr);
}
else
vk_conversion = VK_NULL_HANDLE;
gsk_vulkan_image_create_view (self,
vk_format,
&vk_components,
vk_conversion);
GDK_DEBUG (D3D12, "Vulkan uploaded %ux%u resource of %sformat %u",
(guint) desc.Width, (guint) desc.Height,
is_yuv ? "YUV " : "",
desc.Format);
return GSK_GPU_IMAGE (self);
}
#endif
static void
gsk_vulkan_image_get_projection_matrix (GskGpuImage *image,
graphene_matrix_t *out_projection)
@@ -1501,7 +1749,10 @@ gsk_vulkan_image_transition (GskVulkanImage *self,
if (self->vk_pipeline_stage == VK_IMAGE_LAYOUT_GENERAL &&
self->vk_semaphore)
{
gsk_vulkan_semaphores_add_wait (semaphores, self->vk_semaphore, stage);
gsk_vulkan_semaphores_add_wait (semaphores,
self->vk_semaphore,
self->vk_semaphore_wait,
stage);
}
vkCmdPipelineBarrier (command_buffer,

View File

@@ -5,6 +5,10 @@
#include "gdk/gdkdmabufprivate.h"
#ifdef GDK_WINDOWING_WIN32
#include <gdk/win32/gdkwin32.h>
#endif
G_BEGIN_DECLS
#define GSK_TYPE_VULKAN_IMAGE (gsk_vulkan_image_get_type ())
@@ -47,6 +51,15 @@ GskGpuImage * gsk_vulkan_image_new_for_dmabuf (GskVulk
GdkTexture * gsk_vulkan_image_to_dmabuf_texture (GskVulkanImage *self,
GdkColorState *color_state);
#endif
#ifdef GDK_WINDOWING_WIN32
GskGpuImage * gsk_vulkan_image_new_for_d3d12resource (GskVulkanDevice *device,
ID3D12Resource *resource,
HANDLE resource_handle,
ID3D12Fence *fence,
HANDLE fence_handle,
guint64 fence_wait,
gboolean premultiplied);
#endif
guchar * gsk_vulkan_image_get_data (GskVulkanImage *self,
gsize *out_stride);

View File

@@ -458,6 +458,13 @@ gstgl_dep = dependency('gstreamer-gl-1.0', version: gstreamer_req,
required: get_option('media-gstreamer'))
gstallocators_dep = dependency('gstreamer-allocators-1.0', version: gstreamer_req,
required: get_option('media-gstreamer'))
if os_win32
gstd3d12_dep = dependency('gstreamer-d3d12-1.0', version: gstreamer_req,
required: get_option('media-gstreamer'))
else
gstd3d12_dep = dependency('', required: false)
endif
fontconfig_dep = [] # only used in x11 backend

View File

@@ -28,6 +28,10 @@
#include <math.h>
#ifdef GDK_WINDOWING_WIN32
#include <gdk/win32/gdkwin32.h>
#endif
struct _GtkGstPaintable
{
GObject parent_instance;
@@ -164,6 +168,30 @@ gtk_gst_paintable_video_renderer_create_video_sink (GstPlayerVideoRenderer *rend
g_object_get (GTK_GST_SINK (sink), "uses-gl", &uses_gl, NULL);
#ifdef GDK_WINDOWING_WIN32
if (GDK_IS_WIN32_DISPLAY (display))
{
GstElement *bin, *convert;
GstPad *pad, *ghostpad;
gboolean res = TRUE;
convert = gst_element_factory_make ("d3d12convert", NULL);
if (convert)
{
bin = gst_bin_new ("d3d12sinkbin");
res &= gst_bin_add (GST_BIN (bin), convert);
res &= gst_bin_add (GST_BIN (bin), sink);
res &= gst_element_link_pads (convert, "src", sink, "sink");
pad = gst_element_get_static_pad (convert, "sink");
ghostpad = gst_ghost_pad_new ("sink", pad);
gst_element_add_pad (bin, ghostpad);
gst_object_unref (pad);
g_assert (res);
sink = bin;
}
}
else
#endif
if (uses_gl)
{
GstElement *glsinkbin;

View File

@@ -38,8 +38,9 @@
#include <gst/gl/wayland/gstgldisplay_wayland.h>
#endif
#if GST_GL_HAVE_WINDOW_WIN32 && (GST_GL_HAVE_PLATFORM_WGL || GST_GL_HAVE_PLATFORM_EGL) && defined (GDK_WINDOWING_WIN32)
#ifdef GDK_WINDOWING_WIN32
#include <gdk/win32/gdkwin32.h>
#include <gst/d3d12/gstd3d12.h>
#endif
#if GST_GL_HAVE_PLATFORM_EGL && (GST_GL_HAVE_WINDOW_WIN32 || GST_GL_HAVE_WINDOW_X11)
@@ -72,6 +73,16 @@ GST_DEBUG_CATEGORY (gtk_debug_gst_sink);
#define MEMORY_TEXTURE_CAPS GST_VIDEO_CAPS_MAKE (FORMATS)
#ifdef GDK_WINDOWING_WIN32
#define D3D12_TEXTURE_CAPS \
"video/x-raw(" GST_CAPS_FEATURE_MEMORY_D3D12_MEMORY "), " \
"width = " GST_VIDEO_SIZE_RANGE ", " \
"height = " GST_VIDEO_SIZE_RANGE ", " \
"framerate = " GST_VIDEO_FPS_RANGE ", "
#else
#define D3D12_TEXTURE_CAPS ""
#endif
#define GL_TEXTURE_CAPS \
"video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY "), " \
"format = (string) RGBA, " \
@@ -87,6 +98,7 @@ GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS(DMABUF_TEXTURE_CAPS "; "
D3D12_TEXTURE_CAPS "; "
GL_TEXTURE_CAPS "; "
MEMORY_TEXTURE_CAPS)
);
@@ -215,6 +227,11 @@ gtk_gst_sink_get_caps (GstBaseSink *bsink,
unfiltered = gst_caps_new_empty ();
#ifdef GDK_WINDOWING_WIN32
tmp = gst_caps_from_string (D3D12_TEXTURE_CAPS);
gst_caps_append (unfiltered, tmp);
#endif
if (self->gdk_display)
{
GdkDmabufFormats *formats = gdk_display_get_dmabuf_formats (self->gdk_display);
@@ -263,6 +280,18 @@ gtk_gst_sink_set_caps (GstBaseSink *bsink,
GST_DEBUG_OBJECT (self, "set caps with %" GST_PTR_FORMAT, caps);
#ifdef GDK_WINDOWING_WIN32
if (gst_caps_features_contains (gst_caps_get_features (caps, 0), GST_CAPS_FEATURE_MEMORY_D3D12_MEMORY))
{
GST_DEBUG_OBJECT (self, "using D3D12");
gst_video_info_dma_drm_init (&self->drm_info);
if (!gst_video_info_from_caps (&self->v_info, caps))
return FALSE;
}
else
#endif
if (gst_video_is_dma_drm_caps (caps))
{
if (!gst_video_info_dma_drm_from_caps (&self->drm_info, caps))
@@ -323,6 +352,50 @@ gtk_gst_sink_propose_allocation (GstBaseSink *bsink,
return FALSE;
}
#ifdef GDK_WINDOWING_WIN32
if (gst_caps_features_contains (gst_caps_get_features (caps, 0), GST_CAPS_FEATURE_MEMORY_D3D12_MEMORY))
{
GstD3D12Device *device;
if (!gst_video_info_from_caps (&info, caps))
{
GST_DEBUG_OBJECT (self, "invalid caps specified");
return FALSE;
}
/* the normal size of a frame */
size = info.size;
if (need_pool &&
(device = gst_d3d12_device_new (0)))
{
pool = gst_d3d12_buffer_pool_new (device);
config = gst_buffer_pool_get_config (pool);
gst_buffer_pool_config_set_params (config, caps, size, 2, 0);
gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
g_clear_object (&device);
if (!gst_buffer_pool_set_config (pool, config))
{
GST_DEBUG_OBJECT (bsink, "failed setting config");
gst_object_unref (pool);
return FALSE;
}
}
/* we need at least 2 buffer because we hold on to the last one */
gst_query_add_allocation_pool (query, pool, size, 2, 0);
g_clear_object (&pool);
/* we also support various metadata */
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0);
return TRUE;
}
#endif
if (gst_caps_features_contains (gst_caps_get_features (caps, 0), GST_CAPS_FEATURE_MEMORY_DMABUF))
{
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0);
@@ -359,8 +432,7 @@ gtk_gst_sink_propose_allocation (GstBaseSink *bsink,
/* we need at least 2 buffer because we hold on to the last one */
gst_query_add_allocation_pool (query, pool, size, 2, 0);
if (pool)
gst_object_unref (pool);
g_clear_object (&pool);
/* we also support various metadata */
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0);
@@ -425,6 +497,44 @@ gtk_gst_sink_texture_from_buffer (GtkGstSink *self,
mem = gst_buffer_peek_memory (buffer, 0);
#ifdef GDK_WINDOWING_WIN32
if (gst_is_d3d12_memory (mem) &&
gst_video_frame_map (frame, &self->v_info, buffer, GST_MAP_READ_D3D12))
{
GstD3D12Memory *dmem = GST_D3D12_MEMORY_CAST (mem);
GdkD3D12TextureBuilder *builder;
ID3D12Fence *fence;
guint64 fence_wait;
GError *error = NULL;
int i;
builder = gdk_d3d12_texture_builder_new ();
gdk_d3d12_texture_builder_set_resource (builder, gst_d3d12_memory_get_resource_handle (dmem));
if (gst_d3d12_memory_get_fence (dmem, &fence, &fence_wait))
{
gdk_d3d12_texture_builder_set_fence (builder, fence);
ID3D12Fence_Release (fence);
gdk_d3d12_texture_builder_set_fence_wait (builder, fence_wait);
}
gdk_d3d12_texture_builder_set_color_state (builder, self->color_state);
texture = gdk_d3d12_texture_builder_build (builder,
(GDestroyNotify) video_frame_free,
frame,
&error);
g_object_unref (builder);
if (!texture)
{
GST_ERROR_OBJECT (self, "Failed to create d3d12 texture: %s", error->message);
g_error_free (error);
}
*pixel_aspect_ratio = ((double) GST_VIDEO_INFO_PAR_N (&self->v_info) /
(double) GST_VIDEO_INFO_PAR_D (&self->v_info));
}
else
#endif
if (gst_is_dmabuf_memory (mem))
{
GdkDmabufTextureBuilder *builder = NULL;

View File

@@ -8,7 +8,7 @@ extra_c_args += common_cflags
if gstplayer_dep.found() and gstgl_dep.found() and gstallocators_dep.found()
media_backends += 'gstreamer'
cdata.set('HAVE_GSTREAMER', 1)
media_gst_deps = [ libm, libgtk_dep, gstplayer_dep, gstgl_dep, gstallocators_dep ]
media_gst_deps = [ libm, libgtk_dep, gstplayer_dep, gstgl_dep, gstallocators_dep, gstd3d12_dep ]
shared_module('media-gstreamer',
sources: [

View File

@@ -0,0 +1,5 @@
[wrap-git]
directory = DirectX-Headers-1.0
url = https://github.com/microsoft/DirectX-Headers.git
revision = v1.611.0

View File

@@ -1,7 +1,7 @@
[wrap-git]
directory=gstreamer
url=https://gitlab.freedesktop.org/gstreamer/gstreamer.git
revision=1.24
revision=main
depth=1
[provide]

View File

@@ -137,6 +137,12 @@ if os_linux
]
endif
if os_win32
gtk_tests += [
['testd3d12', ['testd3d12.c', 'gtkclipper.c']],
]
endif
if x11_enabled
gtk_tests += [['testerrors']]
endif
@@ -153,5 +159,5 @@ foreach t: gtk_tests
include_directories: [confinc, gdkinc],
c_args: test_args + common_cflags,
dependencies: [libgtk_dep, libm],
)
)
endforeach

432
tests/testd3d12.c Normal file
View File

@@ -0,0 +1,432 @@
#include <gtk/gtk.h>
#include <gdk/win32/gdkwin32.h>
#include "gtkclipperprivate.h"
static char *
supported_formats_to_string (void)
{
return g_strdup ("whatever");
}
static GdkTexture *
make_d3d12_texture (ID3D12Device *device,
GdkTexture *texture,
guint32 format,
gboolean disjoint,
gboolean premultiplied,
gboolean flip)
{
GdkTextureDownloader *downloader;
UINT64 buffer_size;
GdkD3D12TextureBuilder *builder;
GError *error = NULL;
HRESULT hr;
ID3D12CommandAllocator *allocator;
ID3D12GraphicsCommandList *commands;
ID3D12CommandQueue *queue;
ID3D12Resource *buffer, *resource;
D3D12_RESOURCE_DESC resource_desc;
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint;
void *buffer_data;
hr = ID3D12Device_CreateCommittedResource (device,
(&(D3D12_HEAP_PROPERTIES) {
.Type = D3D12_HEAP_TYPE_DEFAULT,
.CreationNodeMask = 1,
.VisibleNodeMask = 1,
}),
D3D12_HEAP_FLAG_SHARED,
(&(D3D12_RESOURCE_DESC) {
.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D,
.Width = gdk_texture_get_width (texture),
.Height = gdk_texture_get_height (texture),
.DepthOrArraySize = 1,
.MipLevels = 0,
.Format = DXGI_FORMAT_R8G8B8A8_UNORM,
.SampleDesc = {
.Count = 1,
.Quality = 0,
},
.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN,
.Flags = D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS,
}),
D3D12_RESOURCE_STATE_COPY_DEST,
NULL,
&IID_ID3D12Resource,
(void **) &resource);
g_assert (SUCCEEDED (hr));
ID3D12Resource_GetDesc (resource, &resource_desc);
ID3D12Device_GetCopyableFootprints (device,
&resource_desc,
0, 1, 0,
&footprint,
NULL,
NULL,
NULL);
buffer_size = footprint.Footprint.RowPitch * footprint.Footprint.Height;
hr = ID3D12Device_CreateCommittedResource (device,
(&(D3D12_HEAP_PROPERTIES) {
.Type = D3D12_HEAP_TYPE_UPLOAD,
.CreationNodeMask = 1,
.VisibleNodeMask = 1,
}),
D3D12_HEAP_FLAG_NONE,
(&(D3D12_RESOURCE_DESC) {
.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER,
.Width = buffer_size,
.Height = 1,
.DepthOrArraySize = 1,
.MipLevels = 1,
.SampleDesc = {
.Count = 1,
.Quality = 0,
},
.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
}),
D3D12_RESOURCE_STATE_GENERIC_READ,
NULL,
&IID_ID3D12Resource,
(void **) &buffer);
g_assert (SUCCEEDED (hr));
ID3D12Resource_Map (buffer, 0, (&(D3D12_RANGE) { 0, buffer_size }), &buffer_data );
downloader = gdk_texture_downloader_new (texture);
if (premultiplied)
gdk_texture_downloader_set_format (downloader, GDK_MEMORY_R8G8B8A8_PREMULTIPLIED);
else
gdk_texture_downloader_set_format (downloader, GDK_MEMORY_R8G8B8A8);
gdk_texture_downloader_download_into (downloader, buffer_data, footprint.Footprint.RowPitch);
gdk_texture_downloader_free (downloader);
if (flip)
{
int x, y, width, height, stride;
width = gdk_texture_get_width (texture);
height = gdk_texture_get_height (texture);
stride = footprint.Footprint.RowPitch;
for (y = 0; y < height; y++)
{
guint32 *row = (guint32 *) ((guint8 *) buffer_data + y * stride);
for (x = 0; x < width / 2; x++)
{
guint32 p = row[x];
row[x] = row[width - 1 - x];
row[width - 1 - x] = p;
}
}
}
ID3D12Resource_Unmap (buffer, 0, (&(D3D12_RANGE) { 0, buffer_size }));
hr = ID3D12Device_CreateCommandAllocator (device,
D3D12_COMMAND_LIST_TYPE_DIRECT,
&IID_ID3D12CommandAllocator,
(void **) &allocator);
g_assert (SUCCEEDED (hr));
hr = ID3D12Device_CreateCommandList (device,
0,
D3D12_COMMAND_LIST_TYPE_DIRECT,
allocator,
NULL,
&IID_ID3D12GraphicsCommandList,
(void **) &commands);
g_assert (SUCCEEDED (hr));
ID3D12GraphicsCommandList_CopyTextureRegion (commands,
(&(D3D12_TEXTURE_COPY_LOCATION) {
.pResource = resource,
.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
.SubresourceIndex = 0,
}),
0, 0, 0,
(&(D3D12_TEXTURE_COPY_LOCATION) {
.pResource = buffer,
.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
.PlacedFootprint = footprint,
}),
NULL);
hr = ID3D12GraphicsCommandList_Close (commands);
g_assert (SUCCEEDED (hr));
hr = ID3D12Device_CreateCommandQueue (device,
(&(D3D12_COMMAND_QUEUE_DESC) {
.Type = D3D12_COMMAND_LIST_TYPE_DIRECT,
.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL,
.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE,
}),
&IID_ID3D12CommandQueue,
(void **) &queue);
g_assert (SUCCEEDED (hr));
ID3D12CommandQueue_ExecuteCommandLists (queue, 1, (ID3D12CommandList **) &commands);
builder = gdk_d3d12_texture_builder_new ();
gdk_d3d12_texture_builder_set_resource (builder, resource);
gdk_d3d12_texture_builder_set_premultiplied (builder, premultiplied);
texture = gdk_d3d12_texture_builder_build (builder, NULL, NULL, &error);
if (!texture)
g_error ("Failed to create d3d12 texture: %s", error->message);
g_object_unref (builder);
ID3D12Resource_Release (buffer);
ID3D12Resource_Release (resource);
ID3D12GraphicsCommandList_Release (commands);
ID3D12CommandAllocator_Release (allocator);
ID3D12CommandQueue_Release (queue);
return texture;
}
G_GNUC_NORETURN
static void
usage (void)
{
char *formats = supported_formats_to_string ();
g_print ("Usage: testdmabuf [--undecorated][--disjoint][--download-to FILE][--padding PADDING] FORMAT FILE\n"
"Supported formats: %s\n", formats);
g_free (formats);
exit (1);
}
static gboolean
toggle_fullscreen (GtkWidget *widget,
GVariant *args,
gpointer data)
{
GtkWindow *window = GTK_WINDOW (widget);
if (gtk_window_is_fullscreen (window))
gtk_window_unfullscreen (window);
else
gtk_window_fullscreen (window);
return TRUE;
}
static gboolean
toggle_overlay (GtkWidget *widget,
GVariant *args,
gpointer data)
{
static GtkWidget *child = NULL;
GtkOverlay *overlay = (GtkOverlay *) data;
if (child)
{
gtk_overlay_remove_overlay (overlay, child);
child = NULL;
}
else
{
GtkWidget *spinner;
spinner = gtk_spinner_new ();
gtk_spinner_start (GTK_SPINNER (spinner));
child = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE);
gtk_box_append (GTK_BOX (child), spinner);
gtk_box_append (GTK_BOX (child), gtk_image_new_from_icon_name ("media-playback-start-symbolic"));
gtk_widget_set_halign (child, GTK_ALIGN_CENTER);
gtk_widget_set_valign (child, GTK_ALIGN_CENTER);
gtk_overlay_add_overlay (overlay, child);
}
return TRUE;
}
static GdkTexture *texture;
static GdkTexture *texture_flipped;
static gboolean
toggle_flip (GtkWidget *widget,
GVariant *args,
gpointer data)
{
GtkPicture *picture = (GtkPicture *) data;
if (!texture_flipped)
return FALSE;
if (gtk_picture_get_paintable (picture) == GDK_PAINTABLE (texture))
gtk_picture_set_paintable (picture, GDK_PAINTABLE (texture_flipped));
else
gtk_picture_set_paintable (picture, GDK_PAINTABLE (texture));
return TRUE;
}
static gboolean
toggle_start (GtkWidget *widget,
GVariant *args,
gpointer data)
{
GtkWidget *offload = (GtkWidget *) data;
if (gtk_widget_get_halign (offload) == GTK_ALIGN_CENTER)
gtk_widget_set_halign (offload, GTK_ALIGN_START);
else
gtk_widget_set_halign (offload, GTK_ALIGN_CENTER);
return TRUE;
}
int
main (int argc, char *argv[])
{
GtkWidget *window, *offload, *picture, *overlay;
char *filename;
guint32 format;
gboolean disjoint = FALSE;
gboolean premultiplied = TRUE;
gboolean decorated = TRUE;
gboolean fullscreen = FALSE;
unsigned int i;
const char *save_filename = NULL;
GtkEventController *controller;
GtkShortcutTrigger *trigger;
GtkShortcutAction *action;
GtkShortcut *shortcut;
GdkPaintable *paintable;
GdkTexture *orig;
ID3D12Device *device;
HRESULT hr;
int padding[4] = { 0, }; /* left, right, top, bottom */
int padding_set = 0;
for (i = 1; i < argc; i++)
{
if (g_str_equal (argv[i], "--disjoint"))
disjoint = TRUE;
else if (g_str_equal (argv[i], "--undecorated"))
decorated = FALSE;
else if (g_str_equal (argv[i], "--fullscreen"))
fullscreen = TRUE;
else if (g_str_equal (argv[i], "--unpremultiplied"))
premultiplied = FALSE;
else if (g_str_equal (argv[i], "--download-to"))
{
i++;
if (i == argc)
usage ();
save_filename = argv[i];
}
else if (g_str_equal (argv[i], "--padding"))
{
if (padding_set < 4)
{
char **strv;
i++;
if (i == argc)
usage ();
strv = g_strsplit (argv[i], ",", 0);
if (g_strv_length (strv) > 4)
g_error ("Too much padding");
for (padding_set = 0; padding_set < 4; padding_set++)
{
guint64 num;
GError *error = NULL;
if (!strv[padding_set])
break;
if (!g_ascii_string_to_unsigned (strv[padding_set], 10, 0, 100, &num, &error))
g_error ("%s", error->message);
padding[padding_set] = (int) num;
}
}
else
g_error ("Too much padding");
}
else
break;
}
if (argc - i != 2)
{
usage ();
return 1;
}
filename = argv[argc - 1];
gtk_init ();
hr = D3D12CreateDevice (NULL, D3D_FEATURE_LEVEL_12_0, &IID_ID3D12Device, (void **) &device);
g_assert (SUCCEEDED (hr));
orig = gdk_texture_new_from_filename (filename, NULL);
format = strtoul (argv[argc-2], NULL, 10);
texture = make_d3d12_texture (device, orig, format, disjoint, premultiplied, FALSE);
texture_flipped = make_d3d12_texture (device, orig, format, disjoint, premultiplied, TRUE);
g_object_unref (orig);
if (padding_set > 0)
{
paintable = gtk_clipper_new (GDK_PAINTABLE (texture),
&GRAPHENE_RECT_INIT (padding[0],
padding[2],
gdk_texture_get_width (texture) - padding[0] - padding[1],
gdk_texture_get_height (texture) - padding[2] - padding[3]));
}
else
paintable = GDK_PAINTABLE (texture);
if (save_filename)
gdk_texture_save_to_png (texture, save_filename);
window = gtk_window_new ();
gtk_window_set_decorated (GTK_WINDOW (window), decorated);
if (fullscreen)
gtk_window_fullscreen (GTK_WINDOW (window));
picture = gtk_picture_new_for_paintable (paintable);
offload = gtk_graphics_offload_new (picture);
gtk_widget_set_halign (offload, GTK_ALIGN_CENTER);
gtk_widget_set_valign (offload, GTK_ALIGN_CENTER);
overlay = gtk_overlay_new ();
gtk_overlay_set_child (GTK_OVERLAY (overlay), offload);
gtk_window_set_child (GTK_WINDOW (window), overlay);
controller = gtk_shortcut_controller_new ();
trigger = gtk_keyval_trigger_new (GDK_KEY_F11, GDK_NO_MODIFIER_MASK);
action = gtk_callback_action_new (toggle_fullscreen, NULL, NULL);
shortcut = gtk_shortcut_new (trigger, action);
gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut);
trigger = gtk_keyval_trigger_new (GDK_KEY_O, GDK_CONTROL_MASK);
action = gtk_callback_action_new (toggle_overlay, overlay, NULL);
shortcut = gtk_shortcut_new (trigger, action);
gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut);
trigger = gtk_keyval_trigger_new (GDK_KEY_F, GDK_CONTROL_MASK);
action = gtk_callback_action_new (toggle_flip, picture, NULL);
shortcut = gtk_shortcut_new (trigger, action);
gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut);
trigger = gtk_keyval_trigger_new (GDK_KEY_S, GDK_CONTROL_MASK);
action = gtk_callback_action_new (toggle_start, offload, NULL);
shortcut = gtk_shortcut_new (trigger, action);
gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut);
gtk_widget_add_controller (window, controller);
gtk_window_present (GTK_WINDOW (window));
while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
g_main_context_iteration (NULL, TRUE);
return 0;
}