From 8b99f147069a1f2ad66e5988348d4237bbad5e34 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Wed, 30 Oct 2024 05:22:10 +0100 Subject: [PATCH] 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. --- gdk/win32/gdkd3d12texture.c | 347 ++++++++++++++++++ gdk/win32/gdkd3d12texture.h | 67 ++++ gdk/win32/gdkd3d12texturebuilder.c | 542 +++++++++++++++++++++++++++++ gdk/win32/gdkd3d12texturebuilder.h | 78 +++++ gdk/win32/gdkd3d12textureprivate.h | 15 + gdk/win32/gdkwin32.h | 2 + gdk/win32/meson.build | 4 + 7 files changed, 1055 insertions(+) create mode 100644 gdk/win32/gdkd3d12texture.c create mode 100644 gdk/win32/gdkd3d12texture.h create mode 100644 gdk/win32/gdkd3d12texturebuilder.c create mode 100644 gdk/win32/gdkd3d12texturebuilder.h create mode 100644 gdk/win32/gdkd3d12textureprivate.h diff --git a/gdk/win32/gdkd3d12texture.c b/gdk/win32/gdkd3d12texture.c new file mode 100644 index 0000000000..1d1fd6476f --- /dev/null +++ b/gdk/win32/gdkd3d12texture.c @@ -0,0 +1,347 @@ +/* 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 . + */ + +#include "config.h" + +#include "gdkd3d12textureprivate.h" + +#include "gdkcolorstateprivate.h" +#include "gdkd3d12texturebuilder.h" +#include "gdkdxgiformatprivate.h" +#include "gdkmemoryformatprivate.h" +#include "gdkprivate-win32.h" +#include "gdktextureprivate.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; + + 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); + + 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)); + 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; + D3D12_RESOURCE_DESC desc; + gboolean premultiplied; + + resource = gdk_d3d12_texture_builder_get_resource (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; + + 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 +} diff --git a/gdk/win32/gdkd3d12texture.h b/gdk/win32/gdkd3d12texture.h new file mode 100644 index 0000000000..abe172ec25 --- /dev/null +++ b/gdk/win32/gdkd3d12texture.h @@ -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 . + */ + +#pragma once + +#if !defined (__GDKWIN32_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +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 diff --git a/gdk/win32/gdkd3d12texturebuilder.c b/gdk/win32/gdkd3d12texturebuilder.c new file mode 100644 index 0000000000..66df70d171 --- /dev/null +++ b/gdk/win32/gdkd3d12texturebuilder.c @@ -0,0 +1,542 @@ +/* + * 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 . + * + * Authors: Benjamin Otte + */ + +#include "config.h" + +#include "gdkd3d12texturebuilder.h" + +#include "gdkdebugprivate.h" +#include "gdkcolorstate.h" +#include "gdkd3d12textureprivate.h" +#include "gdkprivate-win32.h" + +#include + +struct _GdkD3D12TextureBuilder +{ + GObject parent_instance; + + ID3D12Resource *resource; + + 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_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); + + 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_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_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: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_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); +} diff --git a/gdk/win32/gdkd3d12texturebuilder.h b/gdk/win32/gdkd3d12texturebuilder.h new file mode 100644 index 0000000000..f3a4eff7fc --- /dev/null +++ b/gdk/win32/gdkd3d12texturebuilder.h @@ -0,0 +1,78 @@ +/* + * 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 . + * + * Authors: Benjamin Otte + */ + +#pragma once + +#if !defined (__GDKWIN32_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +#include + +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 +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 + diff --git a/gdk/win32/gdkd3d12textureprivate.h b/gdk/win32/gdkd3d12textureprivate.h new file mode 100644 index 0000000000..4ee6e848f3 --- /dev/null +++ b/gdk/win32/gdkd3d12textureprivate.h @@ -0,0 +1,15 @@ +#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); + +G_END_DECLS + diff --git a/gdk/win32/gdkwin32.h b/gdk/win32/gdkwin32.h index ed4a7749cd..93d5c76c18 100644 --- a/gdk/win32/gdkwin32.h +++ b/gdk/win32/gdkwin32.h @@ -26,6 +26,8 @@ #define __GDKWIN32_H_INSIDE__ +#include +#include #include #include #include diff --git a/gdk/win32/meson.build b/gdk/win32/meson.build index 18600542cb..601b7b6b31 100644 --- a/gdk/win32/meson.build +++ b/gdk/win32/meson.build @@ -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', @@ -39,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',