Wine-devel
Threads by month
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
May 2021
- 86 participants
- 705 discussions
Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com>
---
dlls/user32/tests/monitor.c | 94 ++++++++++++++++++++++++++++++++++++-
1 file changed, 92 insertions(+), 2 deletions(-)
diff --git a/dlls/user32/tests/monitor.c b/dlls/user32/tests/monitor.c
index 4940847409d..a232241a1fc 100644
--- a/dlls/user32/tests/monitor.c
+++ b/dlls/user32/tests/monitor.c
@@ -68,6 +68,11 @@ static void flush_events(void)
}
}
+static int get_bitmap_stride(int width, int bpp)
+{
+ return ((width * bpp + 15) >> 3) & ~1;
+}
+
static BOOL CALLBACK monitor_enum_proc(HMONITOR hmon, HDC hdc, LPRECT lprc,
LPARAM lparam)
{
@@ -1999,7 +2004,10 @@ static void test_handles(void)
#define check_display_dc(a, b, c) _check_display_dc(__LINE__, a, b, c)
static void _check_display_dc(INT line, HDC hdc, const DEVMODEA *dm, BOOL allow_todo)
{
+ BITMAP bitmap;
+ HBITMAP hbmp;
INT value;
+ BOOL ret;
value = GetDeviceCaps(hdc, HORZRES);
todo_wine_if(allow_todo && dm->dmPelsWidth != GetSystemMetrics(SM_CXSCREEN))
@@ -2027,6 +2035,35 @@ static void _check_display_dc(INT line, HDC hdc, const DEVMODEA *dm, BOOL allow_
todo_wine_if(allow_todo)
ok_(__FILE__, line)(value == dm->dmDisplayFrequency, "Expected VREFRESH %d, got %d.\n",
dm->dmDisplayFrequency, value);
+
+ value = GetDeviceCaps(hdc, BITSPIXEL);
+ todo_wine_if(dm->dmBitsPerPel != 32)
+ ok_(__FILE__, line)(value == dm->dmBitsPerPel, "Expected BITSPIXEL %d, got %d.\n",
+ dm->dmBitsPerPel, value);
+
+ hbmp = GetCurrentObject(hdc, OBJ_BITMAP);
+ ok_(__FILE__, line)(!!hbmp, "GetCurrentObject failed, error %#x.\n", GetLastError());
+ ret = GetObjectA(hbmp, sizeof(bitmap), &bitmap);
+ /* GetObjectA fails on Win7 and older */
+ ok_(__FILE__, line)(ret || broken(!ret), "GetObjectA failed, error %d.\n", GetLastError());
+ if (ret)
+ {
+ ok_(__FILE__, line)(bitmap.bmType == 0, "Expected bmType %d, got %d.\n", 0, bitmap.bmType);
+ todo_wine
+ ok_(__FILE__, line)(bitmap.bmWidth == GetSystemMetrics(SM_CXVIRTUALSCREEN),
+ "Expected bmWidth %d, got %d.\n", GetSystemMetrics(SM_CXVIRTUALSCREEN), bitmap.bmWidth);
+ todo_wine
+ ok_(__FILE__, line)(bitmap.bmHeight == GetSystemMetrics(SM_CYVIRTUALSCREEN),
+ "Expected bmHeight %d, got %d.\n", GetSystemMetrics(SM_CYVIRTUALSCREEN), bitmap.bmHeight);
+ todo_wine
+ ok_(__FILE__, line)(bitmap.bmBitsPixel == 32, "Expected bmBitsPixel %d, got %d.\n", 32,
+ bitmap.bmBitsPixel);
+ ok_(__FILE__, line)(bitmap.bmWidthBytes == get_bitmap_stride(bitmap.bmWidth, bitmap.bmBitsPixel),
+ "Expected bmWidthBytes %d, got %d.\n", get_bitmap_stride(bitmap.bmWidth, bitmap.bmBitsPixel),
+ bitmap.bmWidthBytes);
+ ok_(__FILE__, line)(bitmap.bmPlanes == 1, "Expected bmPlanes %d, got %d.\n", 1, bitmap.bmPlanes);
+ ok_(__FILE__, line)(bitmap.bmBits == NULL, "Expected bmBits %p, got %p.\n", NULL, bitmap.bmBits);
+ }
}
static void test_display_dc(void)
@@ -2050,7 +2087,7 @@ static void test_display_dc(void)
check_display_dc(hdc, &dm, FALSE);
- /* Tests after mode changes */
+ /* Tests after mode changes to a different resolution */
memset(&dm2, 0, sizeof(dm2));
dm2.dmSize = sizeof(dm2);
for (mode_idx = 0; EnumDisplaySettingsA(NULL, mode_idx, &dm2); ++mode_idx)
@@ -2072,6 +2109,33 @@ static void test_display_dc(void)
ok(res == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExA returned unexpected %d.\n", res);
}
+ /* Tests after mode changes to a different color depth */
+ memset(&dm2, 0, sizeof(dm2));
+ dm2.dmSize = sizeof(dm2);
+ for (mode_idx = 0; EnumDisplaySettingsA(NULL, mode_idx, &dm2); ++mode_idx)
+ {
+ if (dm2.dmBitsPerPel != dm.dmBitsPerPel)
+ break;
+ }
+ if (dm2.dmBitsPerPel && dm2.dmBitsPerPel != dm.dmBitsPerPel)
+ {
+ res = ChangeDisplaySettingsExA(NULL, &dm2, NULL, CDS_RESET, NULL);
+ /* Win8 TestBots */
+ ok(res == DISP_CHANGE_SUCCESSFUL || broken(res == DISP_CHANGE_FAILED),
+ "ChangeDisplaySettingsExA returned unexpected %d.\n", res);
+ if (res == DISP_CHANGE_SUCCESSFUL)
+ {
+ check_display_dc(hdc, &dm2, FALSE);
+
+ res = ChangeDisplaySettingsExA(NULL, NULL, NULL, 0, NULL);
+ ok(res == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExA returned unexpected %d.\n", res);
+ }
+ }
+ else
+ {
+ win_skip("Failed to find a different color depth other than %u.\n", dm.dmBitsPerPel);
+ }
+
DeleteDC(hdc);
/* Test DCs covering a specific monitor */
@@ -2091,7 +2155,7 @@ static void test_display_dc(void)
check_display_dc(hdc, &dm, FALSE);
- /* Tests after mode changes */
+ /* Tests after mode changes to a different resolution */
memset(&dm2, 0, sizeof(dm2));
dm2.dmSize = sizeof(dm2);
for (mode_idx = 0; EnumDisplaySettingsA(dd.DeviceName, mode_idx, &dm2); ++mode_idx)
@@ -2114,7 +2178,33 @@ static void test_display_dc(void)
check_display_dc(hdc, &dm2, FALSE);
+ /* Tests after mode changes to a different color depth */
+ memset(&dm2, 0, sizeof(dm2));
+ dm2.dmSize = sizeof(dm2);
+ for (mode_idx = 0; EnumDisplaySettingsA(dd.DeviceName, mode_idx, &dm2); ++mode_idx)
+ {
+ if (dm2.dmBitsPerPel != dm.dmBitsPerPel)
+ break;
+ }
+ if (dm2.dmBitsPerPel && dm2.dmBitsPerPel != dm.dmBitsPerPel)
+ {
+ res = ChangeDisplaySettingsExA(dd.DeviceName, &dm2, NULL, CDS_RESET, NULL);
+ ok(res == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExA returned unexpected %d.\n", res);
+
+ check_display_dc(hdc, &dm2, FALSE);
+
+ res = ChangeDisplaySettingsExA(NULL, NULL, NULL, 0, NULL);
+ ok(res == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExA returned unexpected %d.\n", res);
+ }
+ else
+ {
+ win_skip("Failed to find a different color depth other than %u.\n", dm.dmBitsPerPel);
+ }
+
/* Tests after monitor detach */
+ ret = EnumDisplaySettingsA(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm2);
+ ok(ret, "EnumDisplaySettingsA %s failed.\n", dd.DeviceName);
+
if (!(dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE))
{
old_count = GetSystemMetrics(SM_CMONITORS);
--
2.30.2
1
0
13 May '21
Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com>
---
dlls/d3drm/tests/d3drm.c | 6 ++--
dlls/user32/tests/monitor.c | 3 --
dlls/winex11.drv/settings.c | 64 +++++++++++++++++++++++++++++++++++++
3 files changed, 67 insertions(+), 6 deletions(-)
diff --git a/dlls/d3drm/tests/d3drm.c b/dlls/d3drm/tests/d3drm.c
index d854ebcc56d..f1d48639c14 100644
--- a/dlls/d3drm/tests/d3drm.c
+++ b/dlls/d3drm/tests/d3drm.c
@@ -4061,7 +4061,7 @@ static void test_create_device_from_clipper1(void)
surface_desc.dwSize = sizeof(surface_desc);
hr = IDirectDrawSurface_GetSurfaceDesc(surface, &surface_desc);
ok(hr == DD_OK, "Cannot get surface desc structure (hr = %x).\n", hr);
- todo_wine ok(surface_desc.ddpfPixelFormat.dwRGBBitCount == 16, "Expected 16bpp, got %ubpp.\n",
+ ok(surface_desc.ddpfPixelFormat.dwRGBBitCount == 16, "Expected 16bpp, got %ubpp.\n",
surface_desc.ddpfPixelFormat.dwRGBBitCount);
hr = IDirectDraw2_RestoreDisplayMode(ddraw);
@@ -4240,7 +4240,7 @@ static void test_create_device_from_clipper2(void)
surface_desc.dwSize = sizeof(surface_desc);
hr = IDirectDrawSurface_GetSurfaceDesc(surface, &surface_desc);
ok(hr == DD_OK, "Cannot get surface desc structure (hr = %x).\n", hr);
- todo_wine ok(surface_desc.ddpfPixelFormat.dwRGBBitCount == 16, "Expected 16bpp, got %ubpp.\n",
+ ok(surface_desc.ddpfPixelFormat.dwRGBBitCount == 16, "Expected 16bpp, got %ubpp.\n",
surface_desc.ddpfPixelFormat.dwRGBBitCount);
hr = IDirectDraw2_RestoreDisplayMode(ddraw);
@@ -4418,7 +4418,7 @@ static void test_create_device_from_clipper3(void)
surface_desc.dwSize = sizeof(surface_desc);
hr = IDirectDrawSurface_GetSurfaceDesc(surface, &surface_desc);
ok(hr == DD_OK, "Cannot get surface desc structure (hr = %x).\n", hr);
- todo_wine ok(surface_desc.ddpfPixelFormat.dwRGBBitCount == 16, "Expected 16bpp, got %ubpp.\n",
+ ok(surface_desc.ddpfPixelFormat.dwRGBBitCount == 16, "Expected 16bpp, got %ubpp.\n",
surface_desc.ddpfPixelFormat.dwRGBBitCount);
hr = IDirectDraw2_RestoreDisplayMode(ddraw);
diff --git a/dlls/user32/tests/monitor.c b/dlls/user32/tests/monitor.c
index c4416a47259..4940847409d 100644
--- a/dlls/user32/tests/monitor.c
+++ b/dlls/user32/tests/monitor.c
@@ -297,8 +297,6 @@ static void _expect_dm(INT line, const DEVMODEA *expected, const CHAR *device, D
ok_(__FILE__, line)((dm.dmFields & expected->dmFields) == expected->dmFields,
"Device %s test %d expect dmFields to contain %#x, got %#x\n", device, test, expected->dmFields, dm.dmFields);
- /* Wine doesn't support changing color depth yet */
- todo_wine_if(expected->dmFields & DM_BITSPERPEL && expected->dmBitsPerPel != 32 && expected->dmBitsPerPel != 24)
ok_(__FILE__, line)(!(expected->dmFields & DM_BITSPERPEL) || dm.dmBitsPerPel == expected->dmBitsPerPel,
"Device %s test %d expect dmBitsPerPel %u, got %u\n", device, test, expected->dmBitsPerPel, dm.dmBitsPerPel);
ok_(__FILE__, line)(!(expected->dmFields & DM_PELSWIDTH) || dm.dmPelsWidth == expected->dmPelsWidth,
@@ -695,7 +693,6 @@ static void test_ChangeDisplaySettingsEx(void)
dm.dmSize = sizeof(dm);
res = EnumDisplaySettingsA(devices[device].name, ENUM_CURRENT_SETTINGS, &dm);
ok(res, "Device %s EnumDisplaySettingsA failed, error %#x.\n", devices[device].name, GetLastError());
- todo_wine_if(depths[test] != 32)
ok(dm.dmBitsPerPel == depths[test], "Device %s expect dmBitsPerPel %u, got %u.\n",
devices[device].name, depths[test], dm.dmBitsPerPel);
/* 2008 resets to the resolution in the registry. Newer versions of Windows doesn't
diff --git a/dlls/winex11.drv/settings.c b/dlls/winex11.drv/settings.c
index 9a43231f34d..cea7ebbc6b7 100644
--- a/dlls/winex11.drv/settings.c
+++ b/dlls/winex11.drv/settings.c
@@ -45,6 +45,16 @@ struct x11drv_display_setting
DEVMODEW desired_mode;
};
+struct x11drv_display_depth
+{
+ struct list entry;
+ ULONG_PTR display_id;
+ DWORD depth;
+};
+
+/* Display device emulated depth list, protected by modes_section */
+static struct list x11drv_display_depth_list = LIST_INIT(x11drv_display_depth_list);
+
/* All Windows drivers seen so far either support 32 bit depths, or 24 bit depths, but never both. So if we have
* a 32 bit framebuffer, report 32 bit bpps, otherwise 24 bit ones.
*/
@@ -405,6 +415,54 @@ static int mode_compare(const void *p1, const void *p2)
return a->u1.s2.dmDisplayOrientation - b->u1.s2.dmDisplayOrientation;
}
+static void set_display_depth(ULONG_PTR display_id, DWORD depth)
+{
+ struct x11drv_display_depth *display_depth;
+
+ EnterCriticalSection(&modes_section);
+ LIST_FOR_EACH_ENTRY(display_depth, &x11drv_display_depth_list, struct x11drv_display_depth, entry)
+ {
+ if (display_depth->display_id == display_id)
+ {
+ display_depth->depth = depth;
+ LeaveCriticalSection(&modes_section);
+ return;
+ }
+ }
+
+ display_depth = heap_alloc(sizeof(*display_depth));
+ if (!display_depth)
+ {
+ ERR("Failed to allocate memory.\n");
+ LeaveCriticalSection(&modes_section);
+ return;
+ }
+
+ display_depth->display_id = display_id;
+ display_depth->depth = depth;
+ list_add_head(&x11drv_display_depth_list, &display_depth->entry);
+ LeaveCriticalSection(&modes_section);
+}
+
+static DWORD get_display_depth(ULONG_PTR display_id)
+{
+ struct x11drv_display_depth *display_depth;
+ DWORD depth;
+
+ EnterCriticalSection(&modes_section);
+ LIST_FOR_EACH_ENTRY(display_depth, &x11drv_display_depth_list, struct x11drv_display_depth, entry)
+ {
+ if (display_depth->display_id == display_id)
+ {
+ depth = display_depth->depth;
+ LeaveCriticalSection(&modes_section);
+ return depth;
+ }
+ }
+ LeaveCriticalSection(&modes_section);
+ return screen_bpp;
+}
+
/***********************************************************************
* EnumDisplaySettingsEx (X11DRV.@)
*
@@ -434,6 +492,10 @@ BOOL CDECL X11DRV_EnumDisplaySettingsEx( LPCWSTR name, DWORD n, LPDEVMODEW devmo
ERR("Failed to get %s current display settings.\n", wine_dbgstr_w(name));
return FALSE;
}
+
+ if (!is_detached_mode(devmode))
+ devmode->dmBitsPerPel = get_display_depth(id);
+
goto done;
}
@@ -842,6 +904,8 @@ static LONG apply_display_settings(struct x11drv_display_setting *displays, INT
full_mode->u1.s2.dmDisplayOrientation);
ret = handler.set_current_mode(displays[display_idx].id, full_mode);
+ if (attached_mode && ret == DISP_CHANGE_SUCCESSFUL)
+ set_display_depth(displays[display_idx].id, full_mode->dmBitsPerPel);
free_full_mode(full_mode);
if (ret != DISP_CHANGE_SUCCESSFUL)
return ret;
--
2.30.2
1
0
13 May '21
This pattern shows the number of failed test units for each test
configuration. This allows detecting when results get worse after
a VM update, or when a test configuration gets worse results than
its peers.
Signed-off-by: Francois Gouget <fgouget(a)codeweavers.com>
---
winetest/build-patterns | 121 +++++++++++++++++++++++++++-------------
1 file changed, 83 insertions(+), 38 deletions(-)
diff --git a/winetest/build-patterns b/winetest/build-patterns
index f6704bd00..c9fcb6638 100755
--- a/winetest/build-patterns
+++ b/winetest/build-patterns
@@ -31,12 +31,19 @@ sub BEGIN
}
unshift @INC, $1 if ($0 =~ m=^(/.*)/[^/]+$=);
}
-use vars qw/$workdir $gitdir $gitweb $patternbuilds $fixed_threshold/;
+use vars qw/$workdir $gitdir $gitweb @groups $patternbuilds $fixed_threshold/;
require "winetest.conf";
my $name0=$0;
$name0 =~ s+^.*/++;
+# Map dissect's IDs to the platform groups
+my %idmap;
+foreach my $group (@groups)
+{
+ map { $idmap{$_} = $group } @{$group->{ids}};
+}
+
#
# Common helpers
@@ -249,6 +256,16 @@ my %reports;
# - name
# The uniquely identifying test name in the form 'module:unit'.
#
+# - id
+# The HTML id for linking to this test.
+#
+# - title
+# The test's HTML title.
+#
+# - desc
+# An optional chunk of HTML shown above the test pattern which can be used
+# to provide additional information about the test.
+#
# - source
# The source file for that test unit.
#
@@ -310,7 +327,7 @@ my %reports;
# - commits
# A hashtable of the commit objects, indexed first buy build name and then
# by commit id.
-my %tests;
+my %tests = ( 0 => { name => "0", id => "summary", newmodes => {} } );
# A set containing the test units source files.
my %testsources;
@@ -360,7 +377,9 @@ foreach my $build (@sortedbuilds)
if (!$test)
{
$tests{$testname} = $test = {
+ id => $testname,
name => $testname,
+ title => "<a href='tests/$testname.html'>$testname</a>",
source => $source,
};
$testsources{$source} = 1;
@@ -377,6 +396,10 @@ foreach my $build (@sortedbuilds)
next;
}
$test->{testreports}->{$reportdir}->{status}->{$build->{name}} = $status;
+
+ # Count failed test units
+ next if ($status =~ /^(?:missing.|skipped|stub)/);
+ $tests{0}->{testreports}->{$reportdir}->{status}->{$build->{name}}++;
}
}
}
@@ -882,7 +905,15 @@ sub write_pattern_line($$$)
else
{
($symbol, $class, $title, my $link, $attrs) = get_status_html($status, $test->{colors});
- if ($link eq "t")
+ if ($test->{name} eq "0")
+ {
+ $tag = "a";
+ my $platform = $reports{$reportdir}->{platform};
+ my $group = $idmap{$platform} || $idmap{unknown};
+ $attrs .= sprintf " href='%s/index_%s.html'",
+ $build->{name}, $group->{name};
+ }
+ elsif ($link eq "t")
{
$tag = "a";
$attrs .= sprintf " href='%s/%s/%s.html'",
@@ -1042,8 +1073,11 @@ sub write_pattern($$$)
my ($html, $test, $pagereports) = @_;
compute_set_colors($test->{colors});
- write_newmodes_line($html, $test) if (%{$test->{newmodes}});
+ print $html $test->{desc} if (defined $test->{desc});
+
+ print $html "<div class='test'>\n";
+ write_newmodes_line($html, $test) if (%{$test->{newmodes}});
foreach my $reportdir (@sortedreports)
{
next if (!$pagereports->{$reportdir});
@@ -1051,6 +1085,7 @@ sub write_pattern($$$)
write_pattern_line($html, $test, $reportdir);
}
write_commits($html, $test) if (%{$test->{commits}});
+ print $html "</div>\n";
}
sub write_patterns_list($$$$)
@@ -1063,8 +1098,8 @@ sub write_patterns_list($$$$)
my $testname = $testnames->[$i];
my $test = $tests{$testname};
- print $html "<div class='testfile' id='$testname'>\n";
- print $html "<div class='updownbar'><a href='tests/$testname.html'>$testname</a>";
+ print $html "<div class='testfile' id='$test->{id}'>\n";
+ print $html "<div class='updownbar'>$test->{title}";
print $html "<div class='ralign'>";
if ($mainpage)
{
@@ -1078,9 +1113,8 @@ sub write_patterns_list($$$$)
print $html "</div></div>\n";
- print $html "<div class='test'>\n";
write_pattern($html, $test, $pagereports);
- print $html "</div></div>\n";
+ print $html "</div>\n";
}
}
@@ -1114,30 +1148,12 @@ sub write_patterns_page($$$)
</head>
<body>
<div class="navbar"><a href="..">index</a> | Failure patterns: <a href="patterns.html">all</a> | <a href="patterns-tb-win.html">testbot windows</a> | <a href="patterns-tb-wine.html">testbot wine</a></div>
-
-<div class='testfile' id='summary'>
-<div class='updownbar'>$title</div>
-<p>The purpose of this page is to:</p>
-<ul>
- <li>Help detect new test failures, that is test / Wine regressions.</li>
- <li>Help diagnose failures by showing when they are specific to a set of Windows versions, a locale, a day of the week, etc.</li>
-</ul>
-<p>The patterns on this page read as follows:</p>
-<ul>
- <li>Each line corresponds to a test configuration sorted by platform and tag name.</li>
- <li>Test configurations with no failure for a given test are omitted from that test's pattern.</li>
- <li>Each column corresponds to a WineTest build from oldest to newest.</li>
- <li>Lines with a _N suffix are when WineTest was run more than once for a test configuration + build combination. They are rare and thus hang at the bottom of the pattern to not disrupt it.</li>
- <li>Each cell is a single letter describing the result for that test. See the tooltips for a description. Click on the letter to go to that result page.</li>
- <li>Each result is also color coded to make the patterns pop out. There is one color per type of result (crash , timeout, etc.), and one per number of failures (to help detect changes).</li>
- <li>Failure modes that have not been seen before (test that newly crashes, new failure count, etc.) are highlighted, as well as the configuration names in which it appears.</li>
- <li>Below the patterns is a line showing which builds have potentially related commits if any.
-</ul>
EOF
# Show the test units with recent failures first.
# Also use these lists to link the test units to each other.
my %lists = (
+ summary => {testnames => [ 0 ]},
recent => {title => "Recent failures",
desc => "recently started failing (the $patternbuilds oldest builds had no failure).",
testnames => []},
@@ -1194,6 +1210,7 @@ EOF
}
}
next if ($last == -1); # no report had a pattern of interest
+ next if ($testname eq "0");
$test->{newmodes} = {};
foreach my $status (keys %$newfailures)
@@ -1221,23 +1238,51 @@ EOF
}
# Generate the lists index (and up test unit links)
- print $html "<p>The patterns are grouped according to the likelyhood that they contain new failures:</p>\n";
- print $html "<ul>\n";
- my @listids = ("recent", "newmode", "regular", "old");
+ $tests{0}->{title} = $title;
+ $tests{0}->{desc} = "
+<p>The purpose of this page is to:</p>
+<ul>
+ <li>Help detect new test failures, that is test / Wine regressions.</li>
+ <li>Help diagnose failures by showing when they are specific to a set of Windows versions, a locale, a day of the week, etc.</li>
+ <li>Help detect test configuration changes that increase the number of failed test units.</li>
+ <li>Help identify test configurations that get worse results than their peers.</li>
+</ul>
+
+<p>The patterns on this page read as follows:</p>
+<ul>
+ <li>Each line corresponds to a test configuration sorted by platform and tag name.</li>
+ <li>Test configurations with no failure for a given test are omitted from that test's pattern.</li>
+ <li>Each column corresponds to a WineTest build from oldest to newest.</li>
+ <li>Lines with a _N suffix are when WineTest was run more than once for a test configuration + build combination. They are rare and thus hang at the bottom of the pattern to not disrupt it.</li>
+ <li>Each cell is a single letter describing the result for that test. See the tooltips for a description. Click on the letter to go to that result page.</li>
+ <li>Each result is also color coded to make the patterns pop out. There is one color per type of result (crash , timeout, etc.), and one per number of failures (to help detect changes).</li>
+ <li>Failure modes that have not been seen before (test that newly crashes, new failure count, etc.) are highlighted, as well as the configuration names in which it appears.</li>
+ <li>Below the patterns is a line showing which builds have potentially related commits if any.
+</ul>
+
+<p>The patterns are grouped according to the likelyhood that they contain new failures:</p>
+<ul>";
+
+ my @listids = ("summary", "recent", "newmode", "regular", "old");
my ($prevunit, $nolist) = ("", 1);
foreach my $listid (@listids)
{
my $list = $lists{$listid};
my $count = @{$list->{testnames}};
next if (!$count);
- print $html "<li><a href='#$listid'>$count tests</a> $list->{desc}</li>\n";
- $nolist = 0;
$list->{prevunit} = $prevunit;
- $prevunit = $list->{testnames}->[-1];
+ $prevunit = $tests{$list->{testnames}->[-1]}->{id};
+
+ next if (!$list->{desc});
+ $tests{0}->{desc} .= "<li><a href='#$listid'>$count tests</a> $list->{desc}</li>\n";
+ $nolist = 0;
+ }
+ $tests{0}->{desc} .= "<p>No failures. Don't change anything!</p>\n" if ($nolist);
+ $tests{0}->{desc} .= "</ul>";
+ if (!$nolist)
+ {
+ $tests{0}->{desc} .= "<p>Unlike the other patterns on this page, the one below shows the number of failed test units for each configuration.</p>";
}
- print $html "<p>No failures. Don't change anything!</p>\n" if ($nolist);
- print $html "</ul>\n";
- print $html "</div>\n";
# Link the last test unit of each list to the first one of the next list
my $nextunit;
@@ -1247,7 +1292,7 @@ EOF
my $count = @{$list->{testnames}};
next if (!$count);
$list->{nextunit} = $nextunit;
- $nextunit = $list->{testnames}->[0];
+ $nextunit = $tests{$list->{testnames}->[0]}->{id};
}
# Generate the test unit lists themselves
@@ -1257,7 +1302,7 @@ EOF
my $list = $lists{$listid};
my $count = @{$list->{testnames}};
next if (!$count);
- print $html "<h2 id='$listid'>$list->{title}</h2>\n";
+ print $html "<h2 id='$listid'>$list->{title}</h2>\n" if ($list->{title});
write_patterns_list($html, $pagereports, $mainpage, $list);
}
--
2.20.1
1
0
13 May '21
---
v2:
- Addressed comments
- Explicitely named conv parameters for `VkMember.copy` and `VkMember.free` methods
---
dlls/winevulkan/make_vulkan | 568 +++++++++++++++++++++++++++---------
dlls/winevulkan/vulkan.c | 142 +--------
2 files changed, 433 insertions(+), 277 deletions(-)
diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan
index d5cc62d2097..02a0e4bfac7 100755
--- a/dlls/winevulkan/make_vulkan
+++ b/dlls/winevulkan/make_vulkan
@@ -185,7 +185,6 @@ FUNCTION_OVERRIDES = {
# Device functions
"vkAllocateCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
- "vkCmdExecuteCommands" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
"vkCreateCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
"vkDestroyCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
"vkDestroyDevice" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
@@ -193,7 +192,6 @@ FUNCTION_OVERRIDES = {
"vkGetDeviceProcAddr" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE},
"vkGetDeviceQueue" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
"vkGetDeviceQueue2" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
- "vkQueueSubmit" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
# VK_KHR_surface
"vkDestroySurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.NONE},
@@ -204,14 +202,14 @@ FUNCTION_OVERRIDES = {
# VK_KHR_get_surface_capabilities2
"vkGetPhysicalDeviceSurfaceCapabilities2KHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PRIVATE},
- "vkGetPhysicalDeviceSurfaceFormats2KHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PRIVATE},
+ "vkGetPhysicalDeviceSurfaceFormats2KHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
# VK_KHR_win32_surface
"vkCreateWin32SurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.NONE},
"vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
# VK_KHR_swapchain
- "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PRIVATE},
+ "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
"vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
"vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
"vkQueuePresentKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
@@ -645,6 +643,18 @@ class VkFunction(object):
return False
+ def needs_unwrapping(self):
+ """ Check if the function needs any input/output type unwrapping.
+ Functions need input/output unwrapping if struct parameters have
+ wrapped dispatchable handles.
+ """
+
+ for p in self.params:
+ if p.needs_unwrapping():
+ return True
+
+ return False
+
def needs_dispatch(self):
return self.dispatch
@@ -739,7 +749,7 @@ class VkFunction(object):
return body
- def body_conversion(self):
+ def body_conversion(self, conv):
body = ""
# Declare a variable to hold the result for non-void functions.
@@ -748,27 +758,28 @@ class VkFunction(object):
# Declare any tmp parameters for conversion.
for p in self.params:
- if not p.needs_conversion():
- continue
-
- if p.is_dynamic_array():
- body += " {0}_host *{1}_host;\n".format(p.type, p.name)
- else:
- body += " {0}_host {1}_host;\n".format(p.type, p.name)
+ if p.needs_conversion() and conv:
+ if p.is_dynamic_array():
+ body += " {0}_host *{1}_host;\n".format(p.type, p.name)
+ else:
+ body += " {0}_host {1}_host;\n".format(p.type, p.name)
+ elif p.needs_unwrapping():
+ if p.is_dynamic_array():
+ body += " {0} *{1}_host;\n".format(p.type, p.name)
+ else:
+ body += " {0} {1}_host;\n".format(p.type, p.name)
if not self.needs_private_thunk():
body += " {0}\n".format(self.trace())
# Call any win_to_host conversion calls.
for p in self.params:
- if not p.needs_input_conversion():
- continue
-
- body += p.copy(Direction.INPUT)
+ if p.needs_input_conversion() and (p.needs_unwrapping() or conv):
+ body += p.copy(Direction.INPUT)
# Build list of parameters containing converted and non-converted parameters.
# The param itself knows if conversion is needed and applies it when we set conv=True.
- params = ", ".join([p.variable(conv=True) for p in self.params])
+ params = ", ".join([p.variable(conv=conv) for p in self.params])
# Call the native Vulkan function.
if self.type == "void":
@@ -787,10 +798,8 @@ class VkFunction(object):
# Perform any required cleanups. Most of these are for array functions.
for p in self.params:
- if not p.needs_free():
- continue
-
- body += p.free()
+ if p.needs_free() and (p.needs_unwrapping() or conv):
+ body += p.free()
# Finally return the result.
if self.type != "void":
@@ -840,10 +849,15 @@ class VkFunction(object):
if self.needs_conversion():
thunk += "#if defined(USE_STRUCT_CONVERSION)\n"
- thunk += self.body_conversion()
+ thunk += self.body_conversion(conv=True)
thunk += "#else\n"
- thunk += self.body()
+ if self.needs_unwrapping():
+ thunk += self.body_conversion(conv=False)
+ else:
+ thunk += self.body()
thunk += "#endif\n"
+ elif self.needs_unwrapping():
+ thunk += self.body_conversion(conv=False)
else:
thunk += self.body()
@@ -1063,6 +1077,12 @@ class VkHandle(object):
def is_wrapped(self):
return self.native_handle("test") is not None
+ def needs_conversion(self):
+ return False
+
+ def needs_unwrapping(self):
+ return self.is_wrapped()
+
class VkMember(object):
def __init__(self, const=False, struct_fwd_decl=False,_type=None, pointer=None, name=None, array_len=None,
dyn_array_len=None, optional=False, values=None):
@@ -1148,10 +1168,11 @@ class VkMember(object):
return VkMember(const=const, struct_fwd_decl=struct_fwd_decl, _type=member_type, pointer=pointer, name=name_elem.text,
array_len=array_len, dyn_array_len=dyn_array_len, optional=optional, values=values)
- def copy(self, input, output, direction):
- """ Helper method for use by conversion logic to generate a C-code statement to copy this member. """
+ def copy(self, input, output, direction, conv):
+ """ Helper method for use by conversion logic to generate a C-code statement to copy this member.
+ - `conv` indicates whether the statement is in a struct alignment conversion path. """
- if self.needs_conversion():
+ if (conv and self.needs_conversion()) or self.needs_unwrapping():
if self.is_dynamic_array():
if direction == Direction.OUTPUT:
LOGGER.warn("TODO: implement copying of returnedonly dynamic array for {0}.{1}".format(self.type, self.name))
@@ -1167,6 +1188,12 @@ class VkMember(object):
else:
# Nothing needed this yet.
LOGGER.warn("TODO: implement copying of static array for {0}.{1}".format(self.type, self.name))
+ elif self.is_handle() and self.needs_unwrapping():
+ if direction == Direction.OUTPUT:
+ LOGGER.err("Only INPUT parameters can be unwrapped")
+ else:
+ handle = self.type_info["data"]
+ return "{0}{1} = {2};\n".format(output, self.name, handle.driver_handle("{0}{1}".format(input, self.name)))
else:
if direction == Direction.OUTPUT:
return "convert_{0}_host_to_win(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
@@ -1178,6 +1205,27 @@ class VkMember(object):
else:
return "{0}{1} = {2}{1};\n".format(output, self.name, input)
+ def free(self, location, conv):
+ """ Helper method for use by conversion logic to generate a C-code statement to free this member. """
+
+ if not self.needs_unwrapping() and not conv:
+ return ""
+
+ cast = ("(" + self.type + ("_host" if conv and self.needs_conversion() else "") + " *)") if self.is_const() else ""
+ if self.is_dynamic_array():
+ count = self.dyn_array_len if isinstance(self.dyn_array_len, int) else "{0}{1}".format(location, self.dyn_array_len)
+ if self.is_struct() and self.type_info["data"].returnedonly:
+ # For returnedonly, counts is stored in a pointer.
+ return "free_{0}_array({1}{2}{3}, *{4});\n".format(self.type, cast, location, self.name, count)
+ else:
+ return "free_{0}_array({1}{2}{3}, {4});\n".format(self.type, cast, location, self.name, count)
+ else:
+ # We are operating on a single structure. Some structs (very rare) contain dynamic members,
+ # which would need freeing.
+ if self.needs_free():
+ return "free_{0}({1}&{2}{3});\n".format(self.type, cast, location, self.name)
+ return ""
+
def definition(self, align=False, conv=False):
""" Generate prototype for given function.
@@ -1216,31 +1264,34 @@ class VkMember(object):
# Check if we need conversion either for this member itself or for any child members
# in case member represents a struct.
- if not self.needs_conversion():
+ if not self.needs_conversion() and not self.needs_unwrapping():
return None
conversions = []
# Collect any conversion for any member structs.
- struct = self.type_info["data"]
- for m in struct:
- m.needs_struct_extensions_conversion()
- if m.needs_conversion():
- conversions.extend(m.get_conversions())
+ if self.is_struct():
+ struct = self.type_info["data"]
+ for m in struct:
+ m.needs_struct_extensions_conversion()
+ if m.needs_conversion() or m.needs_unwrapping():
+ conversions.extend(m.get_conversions())
- struct.needs_struct_extensions_conversion()
+ struct.needs_struct_extensions_conversion()
+ direction = Direction.OUTPUT if struct.returnedonly else Direction.INPUT
+ elif self.is_handle():
+ direction = Direction.INPUT
- struct = self.type_info["data"]
- direction = Direction.OUTPUT if struct.returnedonly else Direction.INPUT
+ operand = self.type_info["data"]
if self.is_dynamic_array():
- conversions.append(ConversionFunction(False, True, direction, struct))
+ conversions.append(ConversionFunction(False, True, direction, operand))
elif self.is_static_array():
- conversions.append(ConversionFunction(True, False, direction, struct))
+ conversions.append(ConversionFunction(True, False, direction, operand))
else:
- conversions.append(ConversionFunction(False, False, direction, struct))
+ conversions.append(ConversionFunction(False, False, direction, operand))
if self.needs_free():
- conversions.append(FreeFunction(self.is_dynamic_array(), struct))
+ conversions.append(FreeFunction(self.is_dynamic_array(), operand))
return conversions
@@ -1307,8 +1358,21 @@ class VkMember(object):
struct = self.type_info["data"]
return struct.needs_conversion()
+ def needs_unwrapping(self):
+ """ Structures with wrapped dispatchable handles, need unwrapping. """
+
+ if self.is_struct():
+ struct = self.type_info["data"]
+ return struct.needs_unwrapping()
+
+ if self.is_handle():
+ handle = self.type_info["data"]
+ return handle.is_wrapped()
+
+ return False
+
def needs_free(self):
- if not self.needs_conversion():
+ if not self.needs_conversion() and not self.needs_unwrapping():
return False
if self.is_dynamic_array():
@@ -1392,21 +1456,23 @@ class VkParam(object):
self.free_func = None
self.input_conv = None
self.output_conv = None
- if not self.needs_conversion():
+ if not self.needs_conversion() and not self.needs_unwrapping():
return
+ operand = self.struct if self.is_struct() else self.handle
+
# Input functions require win to host conversion.
if self._direction in [Direction.INPUT, Direction.INPUT_OUTPUT]:
- self.input_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.INPUT, self.struct)
+ self.input_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.INPUT, operand)
# Output functions require host to win conversion.
if self._direction in [Direction.INPUT_OUTPUT, Direction.OUTPUT]:
- self.output_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.OUTPUT, self.struct)
+ self.output_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.OUTPUT, operand)
# Dynamic arrays, but also some normal structs (e.g. VkCommandBufferBeginInfo) need memory
# allocation and thus some cleanup.
if self.is_dynamic_array() or self.struct.needs_free():
- self.free_func = FreeFunction(self.is_dynamic_array(), self.struct)
+ self.free_func = FreeFunction(self.is_dynamic_array(), operand)
def _set_direction(self):
""" Internal helper function to set parameter direction (input/output/input_output). """
@@ -1544,7 +1610,7 @@ class VkParam(object):
def free(self):
if self.is_dynamic_array():
- if self.struct.returnedonly:
+ if self.is_struct() and self.struct.returnedonly:
# For returnedonly, counts is stored in a pointer.
return " free_{0}_array({1}_host, *{2});\n".format(self.type, self.name, self.dyn_array_len)
else:
@@ -1552,7 +1618,7 @@ class VkParam(object):
else:
# We are operating on a single structure. Some structs (very rare) contain dynamic members,
# which would need freeing.
- if self.struct.needs_free():
+ if self.is_struct() and self.struct.needs_free():
return " free_{0}(&{1}_host);\n".format(self.type, self.name)
return ""
@@ -1563,14 +1629,14 @@ class VkParam(object):
required.
"""
- if not self.is_struct():
+ if self.is_struct():
+ self.struct.needs_struct_extensions_conversion()
+ for m in self.struct:
+ m.needs_struct_extensions_conversion()
+ elif not self.is_handle():
return None
- self.struct.needs_struct_extensions_conversion()
- for m in self.struct:
- m.needs_struct_extensions_conversion()
-
- if not self.needs_conversion():
+ if not self.needs_conversion() and not self.needs_unwrapping():
return None
conversions = []
@@ -1578,14 +1644,15 @@ class VkParam(object):
# Collect any member conversions first, so we can guarantee
# those functions will be defined prior to usage by the
# 'parent' param requiring conversion.
- for m in self.struct:
- if not m.is_struct():
- continue
+ if self.is_struct():
+ for m in self.struct:
+ if not m.is_struct():
+ continue
- if not m.needs_conversion():
- continue
+ if not m.needs_conversion() and not m.needs_unwrapping():
+ continue
- conversions.extend(m.get_conversions())
+ conversions.extend(m.get_conversions())
# Conversion requirements for the 'parent' parameter.
if self.input_conv is not None:
@@ -1641,6 +1708,18 @@ class VkParam(object):
return False
+ def needs_unwrapping(self):
+ """ Returns if parameter needs unwrapping of dispatchable handle. """
+
+ # Wrapped handle parameters are handled seperately, only look for wrapped handles in structs
+ if self.is_struct():
+ return self.struct.needs_unwrapping()
+
+ if self.is_handle() and self.is_dynamic_array():
+ return self.handle.needs_unwrapping()
+
+ return False
+
def needs_free(self):
return self.free_func is not None
@@ -1691,7 +1770,7 @@ class VkParam(object):
LOGGER.debug("TODO: setting NULL VkAllocationCallbacks for {0}".format(self.name))
return "NULL"
- if conv and self.needs_conversion():
+ if self.needs_unwrapping() or (conv and self.needs_conversion()):
if self.is_dynamic_array():
return "{0}_host".format(self.name)
else:
@@ -1880,6 +1959,14 @@ class VkStruct(Sequence):
return True
return False
+ def needs_unwrapping(self):
+ """ Returns if struct members need unwrapping of a dispatchable handle. """
+
+ for m in self.members:
+ if m.needs_unwrapping():
+ return True
+ return False
+
def needs_free(self):
""" Check if any struct member needs some memory freeing."""
@@ -1897,7 +1984,10 @@ class VkStruct(Sequence):
for e in self.struct_extensions:
if e.required and e.needs_conversion():
- LOGGER.error("Unhandled pNext chain conversion for {0}".format(e.name))
+ LOGGER.error("Unhandled pNext chain alignment conversion for {0}".format(e.name))
+ ret = True
+ if e.required and e.needs_unwrapping():
+ LOGGER.error("Unhandled pNext chain unwrapping conversion for {0}".format(e.name))
ret = True
return ret
@@ -1913,12 +2003,12 @@ class VkStruct(Sequence):
class ConversionFunction(object):
- def __init__(self, array, dyn_array, direction, struct):
+ def __init__(self, array, dyn_array, direction, operand):
self.array = array
self.direction = direction
self.dyn_array = dyn_array
- self.struct = struct
- self.type = struct.name
+ self.operand = operand
+ self.type = operand.name
self._set_name()
@@ -1926,21 +2016,44 @@ class ConversionFunction(object):
return self.name == other.name
def _generate_array_conversion_func(self):
- """ Helper function for generating a conversion function for array structs. """
+ """ Helper function for generating a conversion function for array operands. """
+
+ body = ""
+
+ if self.operand.needs_conversion():
+ body += "#if defined(USE_STRUCT_CONVERSION)\n"
+
+ if self.direction == Direction.OUTPUT:
+ params = ["const {0}_host *in".format(self.type), "uint32_t count"]
+ return_type = self.type
+ else:
+ params = ["const {0} *in".format(self.type), "uint32_t count"]
+ return_type = "{0}_host".format(self.type)
+
+ # Generate function prototype.
+ body += "static inline {0} *{1}(".format(return_type, self.name)
+ body += ", ".join(p for p in params)
+ body += ")\n{\n"
+
+ body += " {0} *out;\n".format(return_type)
+
+ if self.operand.needs_unwrapping():
+ if self.operand.needs_conversion():
+ body += "#else\n"
- if self.direction == Direction.OUTPUT:
- params = ["const {0}_host *in".format(self.type), "uint32_t count"]
- return_type = self.type
- else:
params = ["const {0} *in".format(self.type), "uint32_t count"]
- return_type = "{0}_host".format(self.type)
+ return_type = "{0}".format(self.type)
- # Generate function prototype.
- body = "static inline {0} *{1}(".format(return_type, self.name)
- body += ", ".join(p for p in params)
- body += ")\n{\n"
+ # Generate function prototype.
+ body += "static inline {0} *{1}(".format(return_type, self.name)
+ body += ", ".join(p for p in params)
+ body += ")\n{\n"
+
+ body += " {0} *out;\n".format(return_type)
+
+ if self.operand.needs_conversion():
+ body += "#endif /* USE_STRUCT_CONVERSION */\n"
- body += " {0} *out;\n".format(return_type)
body += " unsigned int i;\n\n"
body += " if (!in) return NULL;\n\n"
@@ -1949,33 +2062,74 @@ class ConversionFunction(object):
body += " for (i = 0; i < count; i++)\n"
body += " {\n"
- for m in self.struct:
- # TODO: support copying of pNext extension structures!
- # Luckily though no extension struct at this point needs conversion.
- body += " " + m.copy("in[i].", "out[i].", self.direction)
+ if isinstance(self.operand, VkStruct):
+ for m in self.operand:
+ # TODO: support copying of pNext extension structures!
+ # Luckily though no extension struct at this point needs conversion.
+ convert = m.copy("in[i].", "out[i].", self.direction, conv=True)
+ if self.operand.needs_conversion() and not self.operand.needs_unwrapping():
+ body += " " + convert
+ else:
+ unwrap = m.copy("in[i].", "out[i].", self.direction, conv=False)
+ if unwrap == convert:
+ body += " " + unwrap
+ else:
+ body += "#if defined(USE_STRUCT_CONVERSION)\n {0}#else\n {1}#endif /* USE_STRUCT_CONVERSION */\n".format(convert, unwrap)
+
+ elif isinstance(self.operand, VkHandle) and self.direction == Direction.INPUT:
+ body += " out[i] = " + self.operand.driver_handle("in[i]") + ";\n"
+ else:
+ LOGGER.warn("Unhandled conversion operand type")
+ body += " out[i] = in[i];\n"
body += " }\n\n"
body += " return out;\n"
body += "}\n\n"
+ # endif is appended by FreeFunction
+
return body
def _generate_conversion_func(self):
- """ Helper function for generating a conversion function for non-array structs. """
+ """ Helper function for generating a conversion function for non-array operands. """
- if self.direction == Direction.OUTPUT:
- params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type)]
- else:
- params = ["const {0} *in".format(self.type), "{0}_host *out".format(self.type)]
+ # It doesn't make sense to generate conversion functions for non-struct variables
+ # which aren't in arrays, as this should be handled by the copy() function
+ if not isinstance(self.operand, VkStruct):
+ return ""
- body = "static inline void {0}(".format(self.name)
+ body = ""
- # Generate parameter list
- body += ", ".join(p for p in params)
- body += ")\n{\n"
+ if self.operand.needs_conversion():
+ body += "#if defined (USE_STRUCT_CONVERSION)\n"
+ body += "static inline void {0}(".format(self.name)
- body += " if (!in) return;\n\n"
+ if self.direction == Direction.OUTPUT:
+ params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type)]
+ else:
+ params = ["const {0} *in".format(self.type), "{0}_host *out".format(self.type)]
+
+ # Generate parameter list
+ body += ", ".join(p for p in params)
+ body += ")\n"
+
+ if self.operand.needs_unwrapping():
+ if self.operand.needs_conversion():
+ body += "#else\n"
+
+ body += "static inline void {0}(".format(self.name)
- if self.direction == Direction.INPUT and "pNext" in self.struct and self.struct.returnedonly:
+ params = ["const {0} *in".format(self.type), "{0} *out".format(self.type)]
+
+ # Generate parameter list
+ body += ", ".join(p for p in params)
+ body += ")\n"
+
+ if self.operand.needs_conversion():
+ body += "#endif /* USE_STRUCT_CONVERSION */\n"
+
+ body += "{\n if (!in) return;\n\n"
+
+ if self.direction == Direction.INPUT and "pNext" in self.operand and self.operand.returnedonly:
# We are dealing with an input_output parameter. For these we only need to copy
# pNext and sType as the other fields are filled in by the host. We do potentially
# have to iterate over pNext and perform conversions based on switch(sType)!
@@ -1984,36 +2138,88 @@ class ConversionFunction(object):
body += " out->pNext = in->pNext;\n"
body += " out->sType = in->sType;\n"
else:
- for m in self.struct:
+ for m in self.operand:
# TODO: support copying of pNext extension structures!
- body += " " + m.copy("in->", "out->", self.direction)
+ convert = m.copy("in->", "out->", self.direction, conv=True)
+ if self.operand.needs_conversion() and not self.operand.needs_unwrapping():
+ body += " " + convert
+ else:
+ unwrap = m.copy("in->", "out->", self.direction, conv=False)
+ if unwrap == convert:
+ body += " " + unwrap
+ else:
+ body += "#if defined(USE_STRUCT_CONVERSION)\n {0}#else\n {1}#endif /* USE_STRUCT_CONVERSION */\n".format(convert, unwrap)
+
+ body += "}\n"
+
+ if not self.operand.needs_unwrapping() and not self.operand.needs_free():
+ body += "#endif /* USE_STRUCT_CONVERSION */\n"
+
+ body += "\n"
- body += "}\n\n"
return body
def _generate_static_array_conversion_func(self):
- """ Helper function for generating a conversion function for array structs. """
+ """ Helper function for generating a conversion function for array operands. """
- if self.direction == Direction.OUTPUT:
- params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type), "uint32_t count"]
- else:
- params = ["const {0} *in".format(self.type), "{0} *out_host".format(self.type), "uint32_t count"]
+ body = ""
- # Generate function prototype.
- body = "static inline void {0}(".format(self.name)
- body += ", ".join(p for p in params)
- body += ")\n{\n"
+ if self.operand.needs_conversion():
+ body += "#if defined (USE_STRUCT_CONVERSION)\n"
+
+ if self.direction == Direction.OUTPUT:
+ params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type), "uint32_t count"]
+ else:
+ params = ["const {0} *in".format(self.type), "{0} *out_host".format(self.type), "uint32_t count"]
+
+ # Generate function prototype.
+ body += "static inline void {0}(".format(self.name)
+ body += ", ".join(p for p in params)
+ body += ")\n"
+
+ if self.operand.needs_unwrapping():
+ if self.operand.needs_conversion():
+ body += "#else\n"
+
+ params = ["const {0} *in".format(self.type), "{0} *out".format(self.type), "uint32_t count"]
+
+ # Generate function prototype.
+ body += "static inline void {0}(".format(self.name)
+ body += ", ".join(p for p in params)
+ body += ")\n"
+
+ body += "{\n"
body += " unsigned int i;\n\n"
body += " if (!in) return;\n\n"
body += " for (i = 0; i < count; i++)\n"
body += " {\n"
- for m in self.struct:
- # TODO: support copying of pNext extension structures!
- body += " " + m.copy("in[i].", "out[i].", self.direction)
+ if isinstance(self.operand, VkStruct):
+ for m in self.operand:
+ # TODO: support copying of pNext extension structures!
+ convert = m.copy("in[i].", "out[i].", self.direction, conv=True)
+ if self.operand.needs_conversion() and not self.operand.needs_unwrapping():
+ body += " " + convert
+ else:
+ unwrap = m.copy("in[i].", "out[i].", self.direction, conv=False)
+ if unwrap == convert:
+ body += " " + unwrap
+ else:
+ body += "#if defined(USE_STRUCT_CONVERSION)\n {0}#else\n {1}#endif /* USE_STRUCT_CONVERSION */\n".format(convert, unwrap)
+ elif isinstance(self.operand, VkHandle) and self.direction == Direction.INPUT:
+ body += " out[i] = " + self.operand.driver_handle("in[i]") + ";\n"
+ else:
+ LOGGER.warn("Unhandled conversion operand type")
+ body += " out[i] = in[i];\n"
body += " }\n"
- body += "}\n\n"
+ body += "}\n"
+
+ if not self.operand.needs_unwrapping():
+ body += "#endif /* USE_STRUCT_CONVERSION) */\n"
+
+ body += "\n"
+
return body
def _set_name(self):
@@ -2044,10 +2250,10 @@ class ConversionFunction(object):
class FreeFunction(object):
- def __init__(self, dyn_array, struct):
+ def __init__(self, dyn_array, operand):
self.dyn_array = dyn_array
- self.struct = struct
- self.type = struct.name
+ self.operand = operand
+ self.type = operand.name
if dyn_array:
self.name = "free_{0}_array".format(self.type)
@@ -2060,52 +2266,94 @@ class FreeFunction(object):
def _generate_array_free_func(self):
""" Helper function for cleaning up temporary buffers required for array conversions. """
- # Generate function prototype.
- body = "static inline void {0}({1}_host *in, uint32_t count)\n{{\n".format(self.name, self.type)
+ body = ""
+
+ if self.operand.needs_conversion():
+ if self.operand.needs_unwrapping():
+ body += "#if defined(USE_STRUCT_CONVERSION)\n"
+
+ # Generate function prototype.
+ body += "static inline void {0}({1}_host *in, uint32_t count)\n".format(self.name, self.type)
+
+ if self.operand.needs_unwrapping():
+ if self.operand.needs_conversion():
+ body += "#else\n"
+
+ # Generate function prototype.
+ body += "static inline void {0}({1} *in, uint32_t count)\n".format(self.name, self.type)
+
+ if self.operand.needs_conversion():
+ body += "#endif /* USE_STRUCT_CONVERSION */\n"
+
+ body += "{\n"
# E.g. VkGraphicsPipelineCreateInfo_host needs freeing for pStages.
- if self.struct.needs_free():
+ if isinstance(self.operand, VkStruct) and self.operand.needs_free():
body += " unsigned int i;\n\n"
body += " if (!in) return;\n\n"
body += " for (i = 0; i < count; i++)\n"
body += " {\n"
- for m in self.struct:
- if m.needs_conversion() and m.is_dynamic_array():
- if m.is_const():
- # Add a cast to ignore const on conversion structs we allocated ourselves.
- body += " free_{0}_array(({0}_host *)in[i].{1}, in[i].{2});\n".format(m.type, m.name, m.dyn_array_len)
+ for m in self.operand:
+ if m.needs_free():
+ convert = m.free("in[i].", conv=True)
+ if self.operand.needs_conversion() and not self.operand.needs_unwrapping():
+ body += " " + convert
else:
- body += " free_{0}_array(in[i].{1}, in[i].{2});\n".format(m.type, m.name, m.dyn_array_len)
- elif m.needs_conversion():
- LOGGER.error("Unhandled conversion for {0}".format(m.name))
+ unwrap = m.free("in[i].", conv=False)
+ if convert == unwrap:
+ body += " " + unwrap
+ elif unwrap == "":
+ body += "#if defined(USE_STRUCT_CONVERSION)\n {0}#endif /* USE_STRUCT_CONVERSION */\n".format(convert)
+ else:
+ body += "#if defined(USE_STRUCT_CONVERSION)\n {0}#else\n {1}#endif /* USE_STRUCT_CONVERSION */\n".format(convert, unwrap)
body += " }\n"
else:
body += " if (!in) return;\n\n"
body += " free(in);\n"
- body += "}\n\n"
+ body += "}\n"
+
+ if not self.operand.needs_unwrapping():
+ body += "#endif /* USE_STRUCT_CONVERSION */\n"
+
+ body += "\n"
+
return body
def _generate_free_func(self):
# E.g. VkCommandBufferBeginInfo.pInheritanceInfo needs freeing.
- if not self.struct.needs_free():
+ if not self.operand.needs_free():
+ return ""
+
+ if not isinstance(self.operand, VkStruct):
return ""
# Generate function prototype.
body = "static inline void {0}({1}_host *in)\n{{\n".format(self.name, self.type)
- for m in self.struct:
- if m.needs_conversion() and m.is_dynamic_array():
- count = m.dyn_array_len if isinstance(m.dyn_array_len, int) else "in->{0}".format(m.dyn_array_len)
- if m.is_const():
- # Add a cast to ignore const on conversion structs we allocated ourselves.
- body += " free_{0}_array(({0}_host *)in->{1}, {2});\n".format(m.type, m.name, count)
+ for m in self.operand:
+ if m.needs_free():
+ convert = m.free("in->", conv=True)
+ if self.operand.needs_conversion() and not self.operand.needs_unwrapping():
+ body += " " + convert
else:
- body += " free_{0}_array(in->{1}, {2});\n".format(m.type, m.name, count)
+ unwrap = m.free("in->", conv=False)
+ if convert == unwrap:
+ body += " " + unwrap
+ elif unwrap == "":
+ body += "#if defined(USE_STRUCT_CONVERSION)\n {0}#endif /* USE_STRUCT_CONVERSION */\n".format(convert)
+ else:
+ body += "#if defined(USE_STRUCT_CONVERSION)\n {0}#else\n {1}#endif /* USE_STRUCT_CONVERSION */\n".format(convert, unwrap)
+
+ body += "}\n"
+
+ if not self.operand.needs_unwrapping():
+ body += "#endif /* USE_STRUCT_CONVERSION */\n"
+
+ body += "\n"
- body += "}\n\n"
return body
def definition(self):
@@ -2168,7 +2416,12 @@ class StructChainConversionFunction(object):
if m.name == "pNext":
body += " out->pNext = NULL;\n"
else:
- body += " " + m.copy("in->", "out->", self.direction)
+ convert = m.copy("in->", "out->", self.direction, conv=True)
+ unwrap = m.copy("in->", "out->", self.direction, conv=False)
+ if unwrap == convert:
+ body += " " + unwrap
+ else:
+ body += "#if defined(USE_STRUCT_CONVERSION)\n {0}#else\n {1}#endif /* USE_STRUCT_CONVERSION */\n".format(convert, unwrap)
body += "\n out_header->pNext = (VkBaseOutStructure *)out;\n"
body += " out_header = out_header->pNext;\n"
@@ -2212,7 +2465,41 @@ class FreeStructChainFunction(object):
body += " while (header)\n"
body += " {\n"
- body += " void *prev = header;\n"
+ body += " void *prev = header;\n\n"
+ body += " switch (header->sType)\n"
+ body += " {\n"
+
+ for e in self.struct.struct_extensions:
+ if not e.required:
+ continue
+
+ if not any(m.needs_free() for m in e):
+ continue
+
+ stype = next(x for x in e.members if x.name == "sType")
+
+ body += " case {0}:\n".format(stype.values)
+ body += " {\n"
+ body += " {0} *structure = ({0} *) header;\n".format(e.name)
+
+ for m in e:
+ if m.needs_free():
+ convert = m.free("structure->", conv=True)
+ unwrap = m.free("structure->", conv=False)
+ if convert == unwrap:
+ body += " " + unwrap
+ elif unwrap == "":
+ body += "#if defined(USE_STRUCT_CONVERSION)\n {0}#endif /* USE_STRUCT_CONVERSION */\n".format(convert)
+ else:
+ body += "#if defined(USE_STRUCT_CONVERSION)\n {0}#else\n {1}#endif /* USE_STRUCT_CONVERSION */\n".format(convert, unwrap)
+
+ body += " break;\n"
+ body += " }\n"
+
+ body += " default:\n"
+ body += " break;\n"
+ body += " }\n"
+
body += " header = header->pNext;\n"
body += " free(prev);\n"
body += " }\n\n"
@@ -2235,7 +2522,7 @@ class VkGenerator(object):
if not func.is_required():
continue
- if not func.needs_conversion():
+ if not func.needs_conversion() and not func.needs_unwrapping():
continue
conversions = func.get_conversions()
@@ -2246,15 +2533,26 @@ class VkGenerator(object):
if not any(c == conv for c in self.conversions):
self.conversions.append(conv)
+ if not isinstance(conv.operand, VkStruct):
+ continue
+
# Structs can be used in different ways by different conversions
# e.g. array vs non-array. Just make sure we pull in each struct once.
- if not any(s.name == conv.struct.name for s in self.host_structs):
- self.host_structs.append(conv.struct)
+ if not any(s.name == conv.operand.name for s in self.host_structs):
+ self.host_structs.append(conv.operand)
for struct in self.registry.structs:
if struct.name in STRUCT_CHAIN_CONVERSIONS:
self.struct_chain_conversions.append(StructChainConversionFunction(Direction.INPUT, struct))
self.struct_chain_conversions.append(FreeStructChainFunction(struct))
+ # Once we decide to support pNext chains conversion everywhere, move this under get_conversions
+ for e in struct.struct_extensions:
+ for m in e:
+ if m.needs_conversion() or m.needs_unwrapping():
+ conversions = m.get_conversions()
+ for conv in conversions:
+ if not any(c == conv for c in self.conversions):
+ self.conversions.append(conv)
def _generate_copyright(self, f, spec_file=False):
f.write("# " if spec_file else "/* ")
@@ -2281,10 +2579,8 @@ class VkGenerator(object):
f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
# Generate any conversion helper functions.
- f.write("#if defined(USE_STRUCT_CONVERSION)\n")
for conv in self.conversions:
f.write(conv.definition())
- f.write("#endif /* USE_STRUCT_CONVERSION */\n\n")
for conv in self.struct_chain_conversions:
f.write(conv.definition())
diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c
index 45eda78e997..9f181d92bc5 100644
--- a/dlls/winevulkan/vulkan.c
+++ b/dlls/winevulkan/vulkan.c
@@ -360,20 +360,12 @@ static void wine_vk_device_get_queues(struct VkDevice_T *device,
static void wine_vk_device_free_create_info(VkDeviceCreateInfo *create_info)
{
- VkDeviceGroupDeviceCreateInfo *group_info;
-
- if ((group_info = wine_vk_find_struct(create_info, DEVICE_GROUP_DEVICE_CREATE_INFO)))
- {
- free((void *)group_info->pPhysicalDevices);
- }
-
free_VkDeviceCreateInfo_struct_chain(create_info);
}
static VkResult wine_vk_device_convert_create_info(const VkDeviceCreateInfo *src,
VkDeviceCreateInfo *dst)
{
- VkDeviceGroupDeviceCreateInfo *group_info;
unsigned int i;
VkResult res;
@@ -385,23 +377,6 @@ static VkResult wine_vk_device_convert_create_info(const VkDeviceCreateInfo *src
return res;
}
- /* FIXME: convert_VkDeviceCreateInfo_struct_chain() should unwrap handles for us. */
- if ((group_info = wine_vk_find_struct(dst, DEVICE_GROUP_DEVICE_CREATE_INFO)))
- {
- VkPhysicalDevice *physical_devices;
-
- if (!(physical_devices = calloc(group_info->physicalDeviceCount, sizeof(*physical_devices))))
- {
- free_VkDeviceCreateInfo_struct_chain(dst);
- return VK_ERROR_OUT_OF_HOST_MEMORY;
- }
- for (i = 0; i < group_info->physicalDeviceCount; ++i)
- {
- physical_devices[i] = group_info->pPhysicalDevices[i]->phys_dev;
- }
- group_info->pPhysicalDevices = physical_devices;
- }
-
/* Should be filtered out by loader as ICDs don't support layers. */
dst->enabledLayerCount = 0;
dst->ppEnabledLayerNames = NULL;
@@ -709,36 +684,6 @@ VkResult WINAPI wine_vkAllocateCommandBuffers(VkDevice device,
return res;
}
-void WINAPI wine_vkCmdExecuteCommands(VkCommandBuffer buffer, uint32_t count,
- const VkCommandBuffer *buffers)
-{
- VkCommandBuffer *tmp_buffers;
- unsigned int i;
-
- TRACE("%p %u %p\n", buffer, count, buffers);
-
- if (!buffers || !count)
- return;
-
- /* Unfortunately we need a temporary buffer as our command buffers are wrapped.
- * This call is called often and if a performance concern, we may want to use
- * alloca as we shouldn't need much memory and it needs to be cleaned up after
- * the call anyway.
- */
- if (!(tmp_buffers = malloc(count * sizeof(*tmp_buffers))))
- {
- ERR("Failed to allocate memory for temporary command buffers\n");
- return;
- }
-
- for (i = 0; i < count; i++)
- tmp_buffers[i] = buffers[i]->command_buffer;
-
- buffer->device->funcs.p_vkCmdExecuteCommands(buffer->command_buffer, count, tmp_buffers);
-
- free(tmp_buffers);
-}
-
VkResult WINAPI wine_vkCreateDevice(VkPhysicalDevice phys_dev,
const VkDeviceCreateInfo *create_info,
const VkAllocationCallbacks *allocator, VkDevice *device)
@@ -1122,61 +1067,6 @@ void WINAPI wine_vkGetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2 *in
*queue = wine_vk_device_find_queue(device, info);
}
-VkResult WINAPI wine_vkQueueSubmit(VkQueue queue, uint32_t count,
- const VkSubmitInfo *submits, VkFence fence)
-{
- VkSubmitInfo *submits_host;
- VkResult res;
- VkCommandBuffer *command_buffers;
- unsigned int i, j, num_command_buffers;
-
- TRACE("%p %u %p 0x%s\n", queue, count, submits, wine_dbgstr_longlong(fence));
-
- if (count == 0)
- {
- return queue->device->funcs.p_vkQueueSubmit(queue->queue, 0, NULL, fence);
- }
-
- submits_host = calloc(count, sizeof(*submits_host));
- if (!submits_host)
- {
- ERR("Unable to allocate memory for submit buffers!\n");
- return VK_ERROR_OUT_OF_HOST_MEMORY;
- }
-
- for (i = 0; i < count; i++)
- {
- memcpy(&submits_host[i], &submits[i], sizeof(*submits_host));
-
- num_command_buffers = submits[i].commandBufferCount;
- command_buffers = calloc(num_command_buffers, sizeof(*command_buffers));
- if (!command_buffers)
- {
- ERR("Unable to allocate memory for command buffers!\n");
- res = VK_ERROR_OUT_OF_HOST_MEMORY;
- goto done;
- }
-
- for (j = 0; j < num_command_buffers; j++)
- {
- command_buffers[j] = submits[i].pCommandBuffers[j]->command_buffer;
- }
- submits_host[i].pCommandBuffers = command_buffers;
- }
-
- res = queue->device->funcs.p_vkQueueSubmit(queue->queue, count, submits_host, fence);
-
-done:
- for (i = 0; i < count; i++)
- {
- free((void *)submits_host[i].pCommandBuffers);
- }
- free(submits_host);
-
- TRACE("Returning %d\n", res);
- return res;
-}
-
VkResult WINAPI wine_vkCreateCommandPool(VkDevice device, const VkCommandPoolCreateInfo *info,
const VkAllocationCallbacks *allocator, VkCommandPool *command_pool)
{
@@ -1540,19 +1430,6 @@ void WINAPI wine_vkGetPrivateDataEXT(VkDevice device, VkObjectType object_type,
device->funcs.p_vkGetPrivateDataEXT(device->device, object_type, object_handle, private_data_slot, data);
}
-VkResult WINAPI wine_vkCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR *create_info,
- const VkAllocationCallbacks *allocator, VkSwapchainKHR *swapchain)
-{
- VkSwapchainCreateInfoKHR native_info;
-
- TRACE("%p, %p, %p, %p\n", device, create_info, allocator, swapchain);
-
- native_info = *create_info;
- native_info.surface = wine_surface_from_handle(create_info->surface)->driver_surface;
-
- return thunk_vkCreateSwapchainKHR(device, &native_info, allocator, swapchain);
-}
-
VkResult WINAPI wine_vkCreateWin32SurfaceKHR(VkInstance instance,
const VkWin32SurfaceCreateInfoKHR *createInfo, const VkAllocationCallbacks *allocator, VkSurfaceKHR *surface)
{
@@ -1601,19 +1478,6 @@ void WINAPI wine_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface,
free(object);
}
-VkResult WINAPI wine_vkGetPhysicalDeviceSurfaceFormats2KHR(VkPhysicalDevice phys_dev,
- const VkPhysicalDeviceSurfaceInfo2KHR *surface_info, uint32_t *formats_count, VkSurfaceFormat2KHR *formats)
-{
- VkPhysicalDeviceSurfaceInfo2KHR native_info;
-
- TRACE("%p, %p, %p, %p\n", phys_dev, surface_info, formats_count, formats);
-
- native_info = *surface_info;
- native_info.surface = wine_surface_from_handle(surface_info->surface)->driver_surface;
-
- return thunk_vkGetPhysicalDeviceSurfaceFormats2KHR(phys_dev, &native_info, formats_count, formats);
-}
-
static inline void adjust_max_image_count(VkPhysicalDevice phys_dev, VkSurfaceCapabilitiesKHR* capabilities)
{
/* Many Windows games, for example Strange Brigade, No Man's Sky, Path of Exile
@@ -1647,15 +1511,11 @@ VkResult WINAPI wine_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice
VkResult WINAPI wine_vkGetPhysicalDeviceSurfaceCapabilities2KHR(VkPhysicalDevice phys_dev,
const VkPhysicalDeviceSurfaceInfo2KHR *surface_info, VkSurfaceCapabilities2KHR *capabilities)
{
- VkPhysicalDeviceSurfaceInfo2KHR native_info;
VkResult res;
TRACE("%p, %p, %p\n", phys_dev, surface_info, capabilities);
- native_info = *surface_info;
- native_info.surface = wine_surface_from_handle(surface_info->surface)->driver_surface;
-
- res = thunk_vkGetPhysicalDeviceSurfaceCapabilities2KHR(phys_dev, &native_info, capabilities);
+ res = thunk_vkGetPhysicalDeviceSurfaceCapabilities2KHR(phys_dev, surface_info, capabilities);
if (res == VK_SUCCESS)
adjust_max_image_count(phys_dev, &capabilities->surfaceCapabilities);
--
2.31.1
2
2
13 May '21
Instead of just updating the info.
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com>
---
dlls/user32/rawinput.c | 38 +++++++++++++++++++-------------------
1 file changed, 19 insertions(+), 19 deletions(-)
diff --git a/dlls/user32/rawinput.c b/dlls/user32/rawinput.c
index d95d29d7656..e4e7bad508f 100644
--- a/dlls/user32/rawinput.c
+++ b/dlls/user32/rawinput.c
@@ -98,7 +98,7 @@ static struct device *add_device(HDEVINFO set, SP_DEVICE_INTERFACE_DATA *iface)
{
SP_DEVINFO_DATA device_data = {sizeof(device_data)};
SP_DEVICE_INTERFACE_DETAIL_DATA_W *detail;
- struct device *device;
+ struct device *device = NULL;
UINT32 handle;
HANDLE file;
DWORD i, size, type;
@@ -117,15 +117,6 @@ static struct device *add_device(HDEVINFO set, SP_DEVICE_INTERFACE_DATA *iface)
return NULL;
}
- for (i = 0; i < rawinput_devices_count; ++i)
- {
- if (rawinput_devices[i].handle == UlongToHandle(handle))
- {
- TRACE("Updating device %x / %s\n", handle, debugstr_w(rawinput_devices[i].detail->DevicePath));
- return rawinput_devices + i;
- }
- }
-
if (!(detail = malloc(size)))
{
ERR("Failed to allocate memory.\n");
@@ -134,8 +125,6 @@ static struct device *add_device(HDEVINFO set, SP_DEVICE_INTERFACE_DATA *iface)
detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
SetupDiGetDeviceInterfaceDetailW(set, iface, detail, size, NULL, NULL);
- TRACE("Found device %x / %s.\n", handle, debugstr_w(detail->DevicePath));
-
file = CreateFileW(detail->DevicePath, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
if (file == INVALID_HANDLE_VALUE)
@@ -145,8 +134,24 @@ static struct device *add_device(HDEVINFO set, SP_DEVICE_INTERFACE_DATA *iface)
return NULL;
}
- if (!array_reserve((void **)&rawinput_devices, &rawinput_devices_max,
- rawinput_devices_count + 1, sizeof(*rawinput_devices)))
+ for (i = 0; i < rawinput_devices_count && !device; ++i)
+ if (rawinput_devices[i].handle == UlongToHandle(handle))
+ device = rawinput_devices + i;
+
+ if (device)
+ {
+ TRACE("Updating device %x / %s.\n", handle, debugstr_w(detail->DevicePath));
+ HidD_FreePreparsedData(device->data);
+ CloseHandle(device->file);
+ free(device->detail);
+ }
+ else if (array_reserve((void **)&rawinput_devices, &rawinput_devices_max,
+ rawinput_devices_count + 1, sizeof(*rawinput_devices)))
+ {
+ device = &rawinput_devices[rawinput_devices_count++];
+ TRACE("Adding device %x / %s.\n", handle, debugstr_w(detail->DevicePath));
+ }
+ else
{
ERR("Failed to allocate memory.\n");
CloseHandle(file);
@@ -154,7 +159,6 @@ static struct device *add_device(HDEVINFO set, SP_DEVICE_INTERFACE_DATA *iface)
return NULL;
}
- device = &rawinput_devices[rawinput_devices_count++];
device->detail = detail;
device->file = file;
device->handle = ULongToHandle(handle);
@@ -226,8 +230,6 @@ static void find_devices(void)
device->info.dwType = RIM_TYPEMOUSE;
device->info.u.mouse = mouse_info;
- HidD_FreePreparsedData(device->data);
- device->data = NULL;
}
SetupDiDestroyDeviceInfoList(set);
@@ -243,8 +245,6 @@ static void find_devices(void)
device->info.dwType = RIM_TYPEKEYBOARD;
device->info.u.keyboard = keyboard_info;
- HidD_FreePreparsedData(device->data);
- device->data = NULL;
}
SetupDiDestroyDeviceInfoList(set);
--
2.31.0
3
9
[PATCH v2] winegstreamer: Ensure that the source reports both PCM and floating point audio formats.
by Giovanni Mascellani 13 May '21
by Giovanni Mascellani 13 May '21
13 May '21
From: Derek Lesho <dlesho(a)codeweavers.com>
The patch is originally by Derek Lesho, with some changes by
Giovanni Mascellani and Nikolay Sivov.
Signed-off-by: Giovanni Mascellani <gmascellani(a)codeweavers.com>
---
dlls/mfreadwrite/tests/mfplat.c | 3 ---
dlls/winegstreamer/media_source.c | 25 +++++++++++++++++++++++++
2 files changed, 25 insertions(+), 3 deletions(-)
diff --git a/dlls/mfreadwrite/tests/mfplat.c b/dlls/mfreadwrite/tests/mfplat.c
index ded45afe771..e3ff7f6e7aa 100644
--- a/dlls/mfreadwrite/tests/mfplat.c
+++ b/dlls/mfreadwrite/tests/mfplat.c
@@ -752,9 +752,6 @@ static void test_source_reader(void)
if (SUCCEEDED(hr))
IMFMediaType_Release(mediatype);
- hr = IMFSourceReader_GetNativeMediaType(reader, MF_SOURCE_READER_FIRST_AUDIO_STREAM, 1, &mediatype);
- ok(hr == MF_E_NO_MORE_TYPES, "Unexpected hr %#x.\n", hr);
-
/* Current media type. */
hr = IMFSourceReader_GetCurrentMediaType(reader, MF_SOURCE_READER_FIRST_VIDEO_STREAM, &mediatype);
ok(hr == MF_E_INVALIDSTREAMNUMBER, "Unexpected hr %#x.\n", hr);
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index eb5b9e366ec..5249bc9c41d 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -806,6 +806,31 @@ static HRESULT media_stream_init_desc(struct media_stream *stream)
goto done;
}
}
+ else if (format.major_type == WG_MAJOR_TYPE_AUDIO)
+ {
+ /* Expose at least one PCM and one floating point type for the
+ consumer to pick from. */
+ static const enum wg_audio_format audio_types[] =
+ {
+ WG_AUDIO_FORMAT_S16LE,
+ WG_AUDIO_FORMAT_F64LE,
+ };
+
+ stream_types = malloc( sizeof(IMFMediaType *) * (ARRAY_SIZE(audio_types) + 1) );
+
+ stream_types[0] = mf_media_type_from_wg_format(&format);
+ type_count = 1;
+
+ for (i = 0; i < ARRAY_SIZE(audio_types); i++)
+ {
+ struct wg_format new_format;
+ if (format.u.audio.format == audio_types[i])
+ continue;
+ new_format = format;
+ new_format.u.audio.format = audio_types[i];
+ stream_types[type_count++] = mf_media_type_from_wg_format(&new_format);
+ }
+ }
else
{
stream_type = mf_media_type_from_wg_format(&format);
--
2.31.1
2
1
Signed-off-by: Haoyang Chen <chenhaoyang(a)uniontech.com>
---
dlls/gdi32/freetype.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/dlls/gdi32/freetype.c b/dlls/gdi32/freetype.c
index 456eb245700..6af76d6f81b 100644
--- a/dlls/gdi32/freetype.c
+++ b/dlls/gdi32/freetype.c
@@ -1540,9 +1540,10 @@ static UINT parse_aa_pattern( FcPattern *pattern )
static FcPattern *create_family_pattern( const char *name, FcPattern **cached )
{
- FcPattern *ret = NULL, *tmp, *pattern = pFcPatternCreate();
+ FcPattern *ret = NULL, *tmp, *pattern;
FcResult result;
if (*cached) return *cached;
+ pattern = pFcPatternCreate();
pFcPatternAddString( pattern, FC_FAMILY, (const FcChar8 *)name );
pFcPatternAddString( pattern, FC_NAMELANG, (const FcChar8 *)"en-us" );
pFcPatternAddString( pattern, FC_PRGNAME, (const FcChar8 *)"wine" );
--
2.20.1
1
0
[PATCH 1/5] wined3d: Pass a wined3d_device_context to wined3d_cs_emit_set_feature_level().
by Zebediah Figura 13 May '21
by Zebediah Figura 13 May '21
13 May '21
Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com>
---
dlls/wined3d/cs.c | 7 ++++---
dlls/wined3d/device.c | 2 +-
dlls/wined3d/wined3d_private.h | 3 ++-
3 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c
index 5d9d19a85af..36bf39cf789 100644
--- a/dlls/wined3d/cs.c
+++ b/dlls/wined3d/cs.c
@@ -2055,15 +2055,16 @@ static void wined3d_cs_exec_set_feature_level(struct wined3d_cs *cs, const void
cs->state.feature_level = op->level;
}
-void wined3d_cs_emit_set_feature_level(struct wined3d_cs *cs, enum wined3d_feature_level level)
+void wined3d_device_context_emit_set_feature_level(struct wined3d_device_context *context,
+ enum wined3d_feature_level level)
{
struct wined3d_cs_set_feature_level *op;
- op = wined3d_device_context_require_space(&cs->c, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT);
+ op = wined3d_device_context_require_space(context, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT);
op->opcode = WINED3D_CS_OP_SET_FEATURE_LEVEL;
op->level = level;
- wined3d_device_context_submit(&cs->c, WINED3D_CS_QUEUE_DEFAULT);
+ wined3d_device_context_submit(context, WINED3D_CS_QUEUE_DEFAULT);
}
static const struct
diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c
index 38ab2aed900..ff1d03de0f8 100644
--- a/dlls/wined3d/device.c
+++ b/dlls/wined3d/device.c
@@ -1748,7 +1748,7 @@ void CDECL wined3d_device_set_state(struct wined3d_device *device, struct wined3
TRACE("device %p, state %p.\n", device, state);
device->cs->c.state = state;
- wined3d_cs_emit_set_feature_level(device->cs, state->feature_level);
+ wined3d_device_context_emit_set_feature_level(context, state->feature_level);
for (i = 0; i < WINED3D_MAX_RENDER_TARGETS; ++i)
{
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 6565b2044df..3babb23f75a 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -4750,7 +4750,6 @@ void wined3d_cs_emit_set_clip_plane(struct wined3d_cs *cs, UINT plane_idx,
const struct wined3d_vec4 *plane) DECLSPEC_HIDDEN;
void wined3d_cs_emit_set_color_key(struct wined3d_cs *cs, struct wined3d_texture *texture,
WORD flags, const struct wined3d_color_key *color_key) DECLSPEC_HIDDEN;
-void wined3d_cs_emit_set_feature_level(struct wined3d_cs *cs, enum wined3d_feature_level level) DECLSPEC_HIDDEN;
void wined3d_cs_emit_set_light(struct wined3d_cs *cs, const struct wined3d_light_info *light) DECLSPEC_HIDDEN;
void wined3d_cs_emit_set_light_enable(struct wined3d_cs *cs, unsigned int idx, BOOL enable) DECLSPEC_HIDDEN;
void wined3d_cs_emit_set_material(struct wined3d_cs *cs, const struct wined3d_material *material) DECLSPEC_HIDDEN;
@@ -4805,6 +4804,8 @@ void wined3d_device_context_emit_set_depth_stencil_state(struct wined3d_device_c
struct wined3d_depth_stencil_state *state, unsigned int stencil_ref) DECLSPEC_HIDDEN;
void wined3d_device_context_emit_set_depth_stencil_view(struct wined3d_device_context *context,
struct wined3d_rendertarget_view *view) DECLSPEC_HIDDEN;
+void wined3d_device_context_emit_set_feature_level(struct wined3d_device_context *context,
+ enum wined3d_feature_level level) DECLSPEC_HIDDEN;
void wined3d_device_context_emit_set_index_buffer(struct wined3d_device_context *context, struct wined3d_buffer *buffer,
enum wined3d_format_id format_id, unsigned int offset) DECLSPEC_HIDDEN;
void wined3d_device_context_emit_set_predication(struct wined3d_device_context *context,
--
2.30.2
3
12
[PATCH 1/5] server: Reset the reported event mask in set_socket_event only when using window messages.
by Zebediah Figura 13 May '21
by Zebediah Figura 13 May '21
13 May '21
Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com>
---
dlls/ws2_32/tests/sock.c | 21 ++++++++++-----------
server/sock.c | 3 ++-
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c
index ec78e62969e..7611b934c3a 100644
--- a/dlls/ws2_32/tests/sock.c
+++ b/dlls/ws2_32/tests/sock.c
@@ -2641,9 +2641,8 @@ static void test_WSAEnumNetworkEvents(void)
}
else
{
- todo_wine_if (i != 0) /* Remove when fixed */
- ok (net_events.lNetworkEvents == 0, "Test[%d]: expected 0, got %d\n",
- i, net_events.lNetworkEvents);
+ ok (net_events.lNetworkEvents == 0, "Test[%d]: expected 0, got %d\n",
+ i, net_events.lNetworkEvents);
}
for (k = 0; k < FD_MAX_EVENTS; k++)
{
@@ -3990,12 +3989,12 @@ static void test_accept_events(struct event_test_ctx *ctx)
select_events(ctx, listener, FD_CONNECT | FD_READ | FD_OOB | FD_ACCEPT);
if (ctx->is_message)
check_events(ctx, FD_ACCEPT, 0, 200);
- check_events_todo_event(ctx, 0, 0, 0);
+ check_events(ctx, 0, 0, 0);
select_events(ctx, listener, 0);
select_events(ctx, listener, FD_CONNECT | FD_READ | FD_OOB | FD_ACCEPT);
if (ctx->is_message)
check_events(ctx, FD_ACCEPT, 0, 200);
- check_events_todo_event(ctx, 0, 0, 0);
+ check_events(ctx, 0, 0, 0);
client2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
ok(client2 != -1, "failed to create socket, error %u\n", WSAGetLastError());
@@ -4134,7 +4133,7 @@ static void test_connect_events(struct event_test_ctx *ctx)
select_events(ctx, client, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ | FD_WRITE);
if (ctx->is_message)
check_events(ctx, FD_WRITE, 0, 200);
- check_events_todo_event(ctx, 0, 0, 0);
+ check_events(ctx, 0, 0, 0);
server = accept(listener, NULL, NULL);
ok(server != -1, "failed to accept, error %u\n", WSAGetLastError());
@@ -4235,12 +4234,12 @@ static void test_write_events(struct event_test_ctx *ctx)
select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ | FD_WRITE);
if (ctx->is_message)
check_events(ctx, FD_WRITE, 0, 200);
- check_events_todo_event(ctx, 0, 0, 0);
+ check_events(ctx, 0, 0, 0);
select_events(ctx, server, 0);
select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ | FD_WRITE);
if (ctx->is_message)
check_events(ctx, FD_WRITE, 0, 200);
- check_events_todo_event(ctx, 0, 0, 0);
+ check_events(ctx, 0, 0, 0);
ret = send(server, "data", 5, 0);
ok(ret == 5, "got %d\n", ret);
@@ -4268,7 +4267,7 @@ static void test_write_events(struct event_test_ctx *ctx)
select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ | FD_WRITE);
if (ctx->is_message)
check_events(ctx, FD_WRITE, 0, 200);
- check_events_todo_event(ctx, 0, 0, 0);
+ check_events(ctx, 0, 0, 0);
}
closesocket(server);
@@ -4313,12 +4312,12 @@ static void test_read_events(struct event_test_ctx *ctx)
select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ);
if (ctx->is_message)
check_events(ctx, FD_READ, 0, 200);
- check_events_todo_event(ctx, 0, 0, 0);
+ check_events(ctx, 0, 0, 0);
select_events(ctx, server, 0);
select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ);
if (ctx->is_message)
check_events(ctx, FD_READ, 0, 200);
- check_events_todo_event(ctx, 0, 0, 0);
+ check_events(ctx, 0, 0, 0);
ret = send(client, "data", 5, 0);
ok(ret == 5, "got %d\n", ret);
diff --git a/server/sock.c b/server/sock.c
index bd65cfc68c3..ad80b897365 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -1780,7 +1780,8 @@ DECL_HANDLER(set_socket_event)
if (get_unix_fd( sock->fd ) == -1) return;
old_event = sock->event;
sock->mask = req->mask;
- sock->reported_events &= ~req->mask; /* re-enable held events */
+ if (req->window)
+ sock->reported_events &= ~req->mask; /* re-enable held events */
sock->event = NULL;
sock->window = req->window;
sock->message = req->msg;
--
2.30.2
1
4
Signed-off-by: Paul Gofman <pgofman(a)codeweavers.com>
---
dlls/kernel32/tests/process.c | 182 +++++++++++++++++++++++++++++++++-
server/process.c | 148 ++++++++++++++++++++-------
2 files changed, 290 insertions(+), 40 deletions(-)
diff --git a/dlls/kernel32/tests/process.c b/dlls/kernel32/tests/process.c
index f960efb05a8..18f0415cb04 100644
--- a/dlls/kernel32/tests/process.c
+++ b/dlls/kernel32/tests/process.c
@@ -71,6 +71,7 @@ static BOOL (WINAPI *pQueryFullProcessImageNameA)(HANDLE hProcess, DWORD dwFla
static BOOL (WINAPI *pQueryFullProcessImageNameW)(HANDLE hProcess, DWORD dwFlags, LPWSTR lpExeName, PDWORD lpdwSize);
static DWORD (WINAPI *pK32GetProcessImageFileNameA)(HANDLE,LPSTR,DWORD);
static HANDLE (WINAPI *pCreateJobObjectW)(LPSECURITY_ATTRIBUTES sa, LPCWSTR name);
+static HANDLE (WINAPI *pOpenJobObjectA)(DWORD access, BOOL inherit, LPCSTR name);
static BOOL (WINAPI *pAssignProcessToJobObject)(HANDLE job, HANDLE process);
static BOOL (WINAPI *pIsProcessInJob)(HANDLE process, HANDLE job, PBOOL result);
static BOOL (WINAPI *pTerminateJobObject)(HANDLE job, UINT exit_code);
@@ -257,6 +258,8 @@ static BOOL init(void)
pQueryFullProcessImageNameW = (void *) GetProcAddress(hkernel32, "QueryFullProcessImageNameW");
pK32GetProcessImageFileNameA = (void *) GetProcAddress(hkernel32, "K32GetProcessImageFileNameA");
pCreateJobObjectW = (void *)GetProcAddress(hkernel32, "CreateJobObjectW");
+ pOpenJobObjectA = (void *)GetProcAddress(hkernel32, "OpenJobObjectA");
+
pAssignProcessToJobObject = (void *)GetProcAddress(hkernel32, "AssignProcessToJobObject");
pIsProcessInJob = (void *)GetProcAddress(hkernel32, "IsProcessInJob");
pTerminateJobObject = (void *)GetProcAddress(hkernel32, "TerminateJobObject");
@@ -2810,6 +2813,7 @@ static void test_CompletionPort(void)
wait_child_process(pi.hProcess);
test_completion(port, JOB_OBJECT_MSG_EXIT_PROCESS, (DWORD_PTR)job, pi.dwProcessId, 0);
+
test_completion(port, JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO, (DWORD_PTR)job, 0, 100);
CloseHandle(pi.hProcess);
@@ -2976,13 +2980,14 @@ static void test_jobInheritance(HANDLE job)
wait_and_close_child_process(&pi);
}
-static void test_BreakawayOk(HANDLE job)
+static void test_BreakawayOk(HANDLE parent_job)
{
JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info;
PROCESS_INFORMATION pi;
STARTUPINFOA si = {0};
char buffer[MAX_PATH + 23];
- BOOL ret, out;
+ BOOL ret, out, nested_jobs;
+ HANDLE job;
if (!pIsProcessInJob)
{
@@ -2990,6 +2995,16 @@ static void test_BreakawayOk(HANDLE job)
return;
}
+ job = pCreateJobObjectW(NULL, NULL);
+ ok(!!job, "CreateJobObjectW error %u\n", GetLastError());
+
+ ret = pAssignProcessToJobObject(job, GetCurrentProcess());
+ ok(ret || broken(!ret && GetLastError() == ERROR_ACCESS_DENIED) /* before Win 8. */,
+ "AssignProcessToJobObject error %u\n", GetLastError());
+ nested_jobs = ret;
+ if (!ret)
+ win_skip("Nested jobs are not supported.\n");
+
sprintf(buffer, "\"%s\" process exit", selfname);
ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &pi);
ok(!ret, "CreateProcessA expected failure\n");
@@ -3001,8 +3016,30 @@ static void test_BreakawayOk(HANDLE job)
wait_and_close_child_process(&pi);
}
+ if (nested_jobs)
+ {
+ limit_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_BREAKAWAY_OK;
+ ret = pSetInformationJobObject(job, JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info));
+ ok(ret, "SetInformationJobObject error %u\n", GetLastError());
+
+ sprintf(buffer, "\"%s\" process exit", selfname);
+ ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &pi);
+ ok(ret, "CreateProcessA error %u\n", GetLastError());
+
+ ret = pIsProcessInJob(pi.hProcess, job, &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(!out, "IsProcessInJob returned out=%u\n", out);
+
+ ret = pIsProcessInJob(pi.hProcess, parent_job, &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(out, "IsProcessInJob returned out=%u\n", out);
+
+ TerminateProcess(pi.hProcess, 0);
+ wait_and_close_child_process(&pi);
+ }
+
limit_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_BREAKAWAY_OK;
- ret = pSetInformationJobObject(job, JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info));
+ ret = pSetInformationJobObject(parent_job, JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info));
ok(ret, "SetInformationJobObject error %u\n", GetLastError());
ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &pi);
@@ -3012,6 +3049,10 @@ static void test_BreakawayOk(HANDLE job)
ok(ret, "IsProcessInJob error %u\n", GetLastError());
ok(!out, "IsProcessInJob returned out=%u\n", out);
+ ret = pIsProcessInJob(pi.hProcess, parent_job, &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(!out, "IsProcessInJob returned out=%u\n", out);
+
wait_and_close_child_process(&pi);
limit_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;
@@ -4309,6 +4350,135 @@ static void test_dead_process(void)
CloseHandle(pi.hThread);
}
+static void test_nested_jobs_child(unsigned int index)
+{
+ HANDLE job, job_parent, job_other;
+ PROCESS_INFORMATION pi;
+ char job_name[32];
+ BOOL ret, out;
+
+ sprintf(job_name, "test_nested_jobs_%u", index);
+ job = pOpenJobObjectA(JOB_OBJECT_ASSIGN_PROCESS | JOB_OBJECT_SET_ATTRIBUTES | JOB_OBJECT_QUERY
+ | JOB_OBJECT_TERMINATE, FALSE, job_name);
+ ok(!!job, "OpenJobObjectA error %u\n", GetLastError());
+
+ sprintf(job_name, "test_nested_jobs_%u", !index);
+ job_other = pOpenJobObjectA(JOB_OBJECT_ASSIGN_PROCESS | JOB_OBJECT_SET_ATTRIBUTES | JOB_OBJECT_QUERY
+ | JOB_OBJECT_TERMINATE, FALSE, job_name);
+ ok(!!job_other, "OpenJobObjectA error %u\n", GetLastError());
+
+ job_parent = pCreateJobObjectW(NULL, NULL);
+ ok(!!job_parent, "CreateJobObjectA error %u\n", GetLastError());
+
+ ret = pAssignProcessToJobObject(job_parent, GetCurrentProcess());
+ ok(ret, "AssignProcessToJobObject error %u\n", GetLastError());
+
+ create_process("wait", &pi);
+
+ ret = pAssignProcessToJobObject(job_parent, pi.hProcess);
+ ok(ret || broken(!ret && GetLastError() == ERROR_ACCESS_DENIED) /* Supported since Windows 8. */,
+ "AssignProcessToJobObject error %u\n", GetLastError());
+ if (!ret)
+ {
+ win_skip("Nested jobs are not supported.\n");
+ goto done;
+ }
+ ret = pAssignProcessToJobObject(job, pi.hProcess);
+ ok(ret, "AssignProcessToJobObject error %u\n", GetLastError());
+
+ out = FALSE;
+ ret = pIsProcessInJob(pi.hProcess, NULL, &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(out, "IsProcessInJob returned out=%u\n", out);
+
+ out = FALSE;
+ ret = pIsProcessInJob(pi.hProcess, job, &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(out, "IsProcessInJob returned out=%u\n", out);
+
+ out = TRUE;
+ ret = pIsProcessInJob(GetCurrentProcess(), job, &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(!out, "IsProcessInJob returned out=%u\n", out);
+
+ out = FALSE;
+ ret = pIsProcessInJob(pi.hProcess, job, &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(out, "IsProcessInJob returned out=%u\n", out);
+
+ ret = pAssignProcessToJobObject(job, GetCurrentProcess());
+ ok(ret, "AssignProcessToJobObject error %u\n", GetLastError());
+
+ TerminateProcess(pi.hProcess, 0);
+ wait_child_process(pi.hProcess);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+
+ create_process("wait", &pi);
+ out = FALSE;
+ ret = pIsProcessInJob(pi.hProcess, job, &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(out, "IsProcessInJob returned out=%u\n", out);
+
+ out = FALSE;
+ ret = pIsProcessInJob(pi.hProcess, job_parent, &out);
+ ok(ret, "IsProcessInJob error %u\n", GetLastError());
+ ok(out, "IsProcessInJob returned out=%u\n", out);
+
+ if (index)
+ {
+ ret = pAssignProcessToJobObject(job_other, GetCurrentProcess());
+ ok(!ret, "AssignProcessToJobObject succeded\n", GetLastError());
+ ok(GetLastError() == ERROR_ACCESS_DENIED, "Got unexpected error %u.\n", GetLastError());
+ }
+done:
+ TerminateProcess(pi.hProcess, 0);
+ wait_child_process(pi.hProcess);
+
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ CloseHandle(job_parent);
+ CloseHandle(job);
+ CloseHandle(job_other);
+}
+
+static void test_nested_jobs(void)
+{
+ PROCESS_INFORMATION info[2];
+ char buffer[MAX_PATH + 26];
+ STARTUPINFOA si = {0};
+ HANDLE job1, job2;
+ unsigned int i;
+
+ if (!pIsProcessInJob)
+ {
+ win_skip("IsProcessInJob not available.\n");
+ return;
+ }
+
+ job1 = pCreateJobObjectW(NULL, L"test_nested_jobs_0");
+ ok(!!job1, "CreateJobObjectW failed, error %u.\n", GetLastError());
+ job2 = pCreateJobObjectW(NULL, L"test_nested_jobs_1");
+ ok(!!job2, "CreateJobObjectW failed, error %u.\n", GetLastError());
+
+ sprintf(buffer, "\"%s\" process nested_jobs 0", selfname);
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info[0]),
+ "CreateProcess failed\n");
+ wait_child_process(info[0].hProcess);
+ sprintf(buffer, "\"%s\" process nested_jobs 1", selfname);
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info[1]),
+ "CreateProcess failed\n");
+ wait_child_process(info[1].hProcess);
+ for (i = 0; i < 2; ++i)
+ {
+ CloseHandle(info[i].hProcess);
+ CloseHandle(info[i].hThread);
+ }
+
+ CloseHandle(job1);
+ CloseHandle(job2);
+}
+
START_TEST(process)
{
HANDLE job, hproc, h, h2;
@@ -4384,6 +4554,11 @@ START_TEST(process)
test_handle_list_attribute(TRUE, h, h2);
return;
}
+ else if (!strcmp(myARGV[2], "nested_jobs") && myARGC >= 4)
+ {
+ test_nested_jobs_child(atoi(myARGV[3]));
+ return;
+ }
ok(0, "Unexpected command %s\n", myARGV[2]);
return;
@@ -4452,6 +4627,7 @@ START_TEST(process)
test_CompletionPort();
test_KillOnJobClose();
test_WaitForJobObject();
+ test_nested_jobs();
job = test_AddSelfToJob();
test_jobInheritance(job);
test_BreakawayOk(job);
diff --git a/server/process.c b/server/process.c
index 5ef7596733f..8b6d6fd3e9b 100644
--- a/server/process.c
+++ b/server/process.c
@@ -30,6 +30,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
+#include <stdint.h>
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
@@ -183,7 +184,7 @@ static void job_destroy( struct object *obj );
struct job
{
struct object obj; /* object header */
- struct list process_list; /* list of all processes */
+ struct list process_list; /* list of processes */
int num_processes; /* count of running processes */
int total_processes; /* count of processes which have been assigned */
unsigned int limit_flags; /* limit flags */
@@ -191,6 +192,9 @@ struct job
int signaled; /* job is signaled */
struct completion *completion_port; /* associated completion port */
apc_param_t completion_key; /* key to send with completion messages */
+ struct job *parent;
+ struct list parent_job_entry; /* list entry for parent job */
+ struct list child_job_list; /* list of child jobs */
};
static const struct object_ops job_ops =
@@ -228,6 +232,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_
{
/* initialize it if it didn't already exist */
list_init( &job->process_list );
+ list_init( &job->child_job_list );
job->num_processes = 0;
job->total_processes = 0;
job->limit_flags = 0;
@@ -235,6 +240,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_
job->signaled = 0;
job->completion_port = NULL;
job->completion_key = 0;
+ job->parent = NULL;
}
}
return job;
@@ -251,14 +257,68 @@ static void add_job_completion( struct job *job, apc_param_t msg, apc_param_t pi
add_completion( job->completion_port, job->completion_key, pid, STATUS_SUCCESS, msg );
}
+static int walk_job( struct job *job, void *param, int (*callback)( struct job *, void * ))
+{
+ struct job *j, *next;
+
+ LIST_FOR_EACH_ENTRY_SAFE( j, next, &job->child_job_list, struct job, parent_job_entry )
+ {
+ assert( j->parent == job );
+ if (walk_job( j, param, callback )) return 1;
+ }
+ return callback( job, param );
+}
+
+static int process_in_job( struct job *job, void *param )
+{
+ struct process *process = param;
+
+ assert( process->obj.ops == &process_ops );
+ return process->job == job;
+}
+
static void add_job_process( struct job *job, struct process *process )
{
+ process_id_t pid;
+ struct job *j;
+
+ if (job == process->job) return;
+
+ if (process->job)
+ {
+ list_remove( &process->job_entry );
+ if (job->parent)
+ {
+ j = job->parent;
+ while (j && j != process->job)
+ j = j->parent;
+
+ if (!j)
+ {
+ set_error( STATUS_ACCESS_DENIED );
+ return;
+ }
+ release_object( process->job );
+ }
+ else
+ {
+ job->parent = process->job;
+ list_add_tail( &job->parent->child_job_list, &job->parent_job_entry );
+ }
+ }
+
+ pid = get_process_id( process );
+ j = job;
+ while (j != process->job)
+ {
+ j->num_processes++;
+ j->total_processes++;
+ add_job_completion( j, JOB_OBJECT_MSG_NEW_PROCESS, pid );
+ j = j->parent;
+ }
+
process->job = (struct job *)grab_object( job );
list_add_tail( &job->process_list, &process->job_entry );
- job->num_processes++;
- job->total_processes++;
-
- add_job_completion( job, JOB_OBJECT_MSG_NEW_PROCESS, get_process_id(process) );
}
/* called when a process has terminated, allow one additional process */
@@ -266,40 +326,42 @@ static void release_job_process( struct process *process )
{
struct job *job = process->job;
- if (!job) return;
+ while (job)
+ {
+ assert( job->num_processes );
+ job->num_processes--;
- assert( job->num_processes );
- job->num_processes--;
+ if (!job->terminating)
+ add_job_completion( job, JOB_OBJECT_MSG_EXIT_PROCESS, get_process_id(process) );
- if (!job->terminating)
- add_job_completion( job, JOB_OBJECT_MSG_EXIT_PROCESS, get_process_id(process) );
+ if (!job->num_processes)
+ add_job_completion( job, JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO, 0 );
- if (!job->num_processes)
- add_job_completion( job, JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO, 0 );
+ job = job->parent;
+ }
}
-static void terminate_job( struct job *job, int exit_code )
+static int terminate_job_processes( struct job *job, void *exit_code )
{
+ struct process *process, *next;
+
/* don't report completion events for terminated processes */
job->terminating = 1;
- for (;;) /* restart from the beginning of the list every time */
+ LIST_FOR_EACH_ENTRY_SAFE( process, next, &job->process_list, struct process, job_entry )
{
- struct process *process;
-
- /* find the first process associated with this job and still running */
- LIST_FOR_EACH_ENTRY( process, &job->process_list, struct process, job_entry )
- {
- if (process->running_threads) break;
- }
- if (&process->job_entry == &job->process_list) break; /* no process found */
assert( process->job == job );
- terminate_process( process, NULL, exit_code );
+ if (process->running_threads) terminate_process( process, NULL, (uintptr_t)exit_code );
}
-
job->terminating = 0;
job->signaled = 1;
wake_up( &job->obj, 0 );
+ return 0;
+}
+
+static void terminate_job( struct job *job, int exit_code )
+{
+ walk_job( job, (void *)(uintptr_t)exit_code, terminate_job_processes );
}
static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle )
@@ -321,16 +383,23 @@ static void job_destroy( struct object *obj )
assert( obj->ops == &job_ops );
assert( !job->num_processes );
- assert( list_empty(&job->process_list) );
+ assert( list_empty( &job->process_list ));
+ assert( list_empty( &job->child_job_list ));
if (job->completion_port) release_object( job->completion_port );
+ if (job->parent)
+ {
+ list_remove( &job->parent_job_entry );
+ release_object( job->parent );
+ }
}
static void job_dump( struct object *obj, int verbose )
{
struct job *job = (struct job *)obj;
assert( obj->ops == &job_ops );
- fprintf( stderr, "Job processes=%d\n", list_count(&job->process_list) );
+ fprintf( stderr, "Job processes=%d child_jobs=%d parent=%p\n",
+ list_count(&job->process_list), list_count(&job->child_job_list), job->parent );
}
static int job_signaled( struct object *obj, struct wait_queue_entry *entry )
@@ -1025,6 +1094,7 @@ DECL_HANDLER(new_process)
struct thread *parent_thread = current;
int socket_fd = thread_get_inflight_fd( current, req->socket_fd );
const obj_handle_t *handles = NULL;
+ struct job *job;
if (socket_fd == -1)
{
@@ -1061,6 +1131,8 @@ DECL_HANDLER(new_process)
}
else parent = (struct process *)grab_object( current->process );
+ /* If a job further in the job chain does not permit breakaway process creation
+ * succeeds and the process which is trying to breakaway is assigned to that job. */
if (parent->job && (req->flags & PROCESS_CREATE_FLAGS_BREAKAWAY) &&
!(parent->job->limit_flags & (JOB_OBJECT_LIMIT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)))
{
@@ -1151,11 +1223,17 @@ DECL_HANDLER(new_process)
process->startup_info = (struct startup_info *)grab_object( info );
- if (parent->job
- && !(req->flags & PROCESS_CREATE_FLAGS_BREAKAWAY)
- && !(parent->job->limit_flags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK))
+ job = parent->job;
+ while (job)
{
- add_job_process( parent->job, process );
+ if (!(job->limit_flags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)
+ && !(req->flags & PROCESS_CREATE_FLAGS_BREAKAWAY
+ && job->limit_flags & JOB_OBJECT_LIMIT_BREAKAWAY_OK))
+ {
+ add_job_process( job, process );
+ break;
+ }
+ job = job->parent;
}
/* connect to the window station */
@@ -1570,12 +1648,8 @@ DECL_HANDLER(assign_job)
if ((process = get_process_from_handle( req->process, PROCESS_SET_QUOTA | PROCESS_TERMINATE )))
{
- if (!process->running_threads)
- set_error( STATUS_PROCESS_IS_TERMINATING );
- else if (process->job)
- set_error( STATUS_ACCESS_DENIED );
- else
- add_job_process( job, process );
+ if (!process->running_threads) set_error( STATUS_PROCESS_IS_TERMINATING );
+ else add_job_process( job, process );
release_object( process );
}
release_object( job );
@@ -1597,7 +1671,7 @@ DECL_HANDLER(process_in_job)
}
else if ((job = get_job_obj( current->process, req->job, JOB_OBJECT_QUERY )))
{
- set_error( process->job == job ? STATUS_PROCESS_IN_JOB : STATUS_PROCESS_NOT_IN_JOB );
+ set_error( walk_job( job, process, process_in_job ) ? STATUS_PROCESS_IN_JOB : STATUS_PROCESS_NOT_IN_JOB );
release_object( job );
}
release_object( process );
--
2.31.1
2
3