tests: Add testd3d12

This is a hacked up testdmabuf, but it's good enough to create a
resource and check that GdkD3D12Texture actually works.
This commit is contained in:
Benjamin Otte
2024-10-30 05:25:29 +01:00
parent 8b99f14706
commit 35904b793a
2 changed files with 439 additions and 1 deletions

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;
}