Wine-Devel
By thread
wine-devel@list.winehq.org
By month
Messages by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 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
January 2022
- 86 participants
- 2418 messages
[PATCH v3 02/13] loader: Remove GCC <=4.x EBX register spilling workaround for i386.
by Jinoh Kang
Arbitrarily manipulating the stack pointer in inline assembly interferes
with stack unwinding and debugging experience. Note that it's
impossible to reliably specify unwinding information in inline assembly,
other than adjusting CFA offset.
The workaround appears to be due to "buggy" register allocation that
manifests in GCC <= 4.x when emitting position-independent code.
This is not an issue, since the preloader isn't a position-independent
executable or a shared library.
Fix this by getting rid of extra spilling and value transfer of the EBX
register, and telling the compiler to allocate EBX directly.
Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
---
loader/preloader.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/loader/preloader.c b/loader/preloader.c
index 937843eb9c5..9675dc3f8eb 100644
--- a/loader/preloader.c
+++ b/loader/preloader.c
@@ -241,32 +241,32 @@ __ASM_GLOBAL_FUNC(_start,
static inline __attribute__((noreturn)) void wld_exit( int code )
{
for (;;) /* avoid warning */
- __asm__ __volatile__( "pushl %%ebx; movl %1,%%ebx; int $0x80; popl %%ebx"
- : : "a" (1 /* SYS_exit */), "r" (code) );
+ __asm__ __volatile__( "int $0x80"
+ : : "a" (1 /* SYS_exit */), "b" (code) );
}
static inline int wld_open( const char *name, int flags )
{
long ret;
- __asm__ __volatile__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
- : "=a" (ret) : "0" (5 /* SYS_open */), "r" (name), "c" (flags) );
+ __asm__ __volatile__( "int $0x80"
+ : "=a" (ret) : "0" (5 /* SYS_open */), "b" (name), "c" (flags) );
return SYSCALL_RET(ret);
}
static inline int wld_close( int fd )
{
long ret;
- __asm__ __volatile__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
- : "=a" (ret) : "0" (6 /* SYS_close */), "r" (fd) );
+ __asm__ __volatile__( "int $0x80"
+ : "=a" (ret) : "0" (6 /* SYS_close */), "b" (fd) );
return SYSCALL_RET(ret);
}
static inline ssize_t wld_read( int fd, void *buffer, size_t len )
{
long ret;
- __asm__ __volatile__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
+ __asm__ __volatile__( "int $0x80"
: "=a" (ret)
- : "0" (3 /* SYS_read */), "r" (fd), "c" (buffer), "d" (len)
+ : "0" (3 /* SYS_read */), "b" (fd), "c" (buffer), "d" (len)
: "memory" );
return SYSCALL_RET(ret);
}
@@ -274,16 +274,16 @@ static inline ssize_t wld_read( int fd, void *buffer, size_t len )
static inline ssize_t wld_write( int fd, const void *buffer, size_t len )
{
long ret;
- __asm__ __volatile__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
- : "=a" (ret) : "0" (4 /* SYS_write */), "r" (fd), "c" (buffer), "d" (len) );
+ __asm__ __volatile__( "int $0x80"
+ : "=a" (ret) : "0" (4 /* SYS_write */), "b" (fd), "c" (buffer), "d" (len) );
return SYSCALL_RET(ret);
}
static inline int wld_mprotect( const void *addr, size_t len, int prot )
{
long ret;
- __asm__ __volatile__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
- : "=a" (ret) : "0" (125 /* SYS_mprotect */), "r" (addr), "c" (len), "d" (prot) );
+ __asm__ __volatile__( "int $0x80"
+ : "=a" (ret) : "0" (125 /* SYS_mprotect */), "b" (addr), "c" (len), "d" (prot) );
return SYSCALL_RET(ret);
}
@@ -329,8 +329,8 @@ __ASM_GLOBAL_FUNC(wld_mmap,
static inline int wld_prctl( int code, long arg )
{
long ret;
- __asm__ __volatile__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
- : "=a" (ret) : "0" (172 /* SYS_prctl */), "r" (code), "c" (arg) );
+ __asm__ __volatile__( "int $0x80"
+ : "=a" (ret) : "0" (172 /* SYS_prctl */), "b" (code), "c" (arg) );
return SYSCALL_RET(ret);
}
--
2.31.1
Jan. 25, 2022
[PATCH v3 01/13] loader: Use long instead of int for syscall return type in i386 code.
by Jinoh Kang
Although int = long effectively in i386, this makes it clear that a
machine word-width value is being returned.
Also, future patches adding syscalls returning pointers can use the same
type (long) for return value consistently.
Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
---
Notes:
v1 -> v2: don't change SYSCALL_RET().
loader/preloader.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/loader/preloader.c b/loader/preloader.c
index 585be50624f..937843eb9c5 100644
--- a/loader/preloader.c
+++ b/loader/preloader.c
@@ -247,7 +247,7 @@ static inline __attribute__((noreturn)) void wld_exit( int code )
static inline int wld_open( const char *name, int flags )
{
- int ret;
+ long ret;
__asm__ __volatile__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
: "=a" (ret) : "0" (5 /* SYS_open */), "r" (name), "c" (flags) );
return SYSCALL_RET(ret);
@@ -255,7 +255,7 @@ static inline int wld_open( const char *name, int flags )
static inline int wld_close( int fd )
{
- int ret;
+ long ret;
__asm__ __volatile__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
: "=a" (ret) : "0" (6 /* SYS_close */), "r" (fd) );
return SYSCALL_RET(ret);
@@ -263,7 +263,7 @@ static inline int wld_close( int fd )
static inline ssize_t wld_read( int fd, void *buffer, size_t len )
{
- int ret;
+ long ret;
__asm__ __volatile__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
: "=a" (ret)
: "0" (3 /* SYS_read */), "r" (fd), "c" (buffer), "d" (len)
@@ -273,7 +273,7 @@ static inline ssize_t wld_read( int fd, void *buffer, size_t len )
static inline ssize_t wld_write( int fd, const void *buffer, size_t len )
{
- int ret;
+ long ret;
__asm__ __volatile__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
: "=a" (ret) : "0" (4 /* SYS_write */), "r" (fd), "c" (buffer), "d" (len) );
return SYSCALL_RET(ret);
@@ -281,7 +281,7 @@ static inline ssize_t wld_write( int fd, const void *buffer, size_t len )
static inline int wld_mprotect( const void *addr, size_t len, int prot )
{
- int ret;
+ long ret;
__asm__ __volatile__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
: "=a" (ret) : "0" (125 /* SYS_mprotect */), "r" (addr), "c" (len), "d" (prot) );
return SYSCALL_RET(ret);
@@ -328,7 +328,7 @@ __ASM_GLOBAL_FUNC(wld_mmap,
static inline int wld_prctl( int code, long arg )
{
- int ret;
+ long ret;
__asm__ __volatile__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
: "=a" (ret) : "0" (172 /* SYS_prctl */), "r" (code), "c" (arg) );
return SYSCALL_RET(ret);
--
2.31.1
Jan. 25, 2022
Re: [PATCH] WBEMProxy: Implemented Complete SoftwareLicensingProduct row and data
by Hans Leidekker
On Tue, 2022-01-25 at 08:18 -0600, Ben Shefte wrote:
> Signed-off-by: Ben Shefte<shefben(a)gmail.com>
> ---
> dlls/wbemprox/builtin.c | 98 ++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 92 insertions(+), 6 deletions(-)
>
> diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c
> index 5a09d64b8fb..4122afee59d 100644
> --- a/dlls/wbemprox/builtin.c
> +++ b/dlls/wbemprox/builtin.c
> @@ -388,9 +388,51 @@ static const struct column col_sid[] =
> { L"SidLength", CIM_UINT32 },
> };
> static const struct column col_softwarelicensingproduct[] =
> -{
> - { L"LicenseIsAddon", CIM_BOOLEAN },
> - { L"LicenseStatus", CIM_UINT32 },
> + {
> + { L"ID", CIM_STRING | COL_FLAG_KEY },
> + { L"Name", CIM_STRING },
> + { L"Description", CIM_STRING }
Usually we don't add members until we find an application that needs them.
Jan. 25, 2022
Re: [PATCH v3 resend 2/2] server: Allow creating named pipes using \Device\NamedPipe\ as RootDirectory.
by Jinoh Kang
On 1/5/22 06:58, Jacek Caban wrote:
>> diff --git a/server/named_pipe.c b/server/named_pipe.c
>> index 3e6cf09d4f2..37520789722 100644
>> --- a/server/named_pipe.c
>> +++ b/server/named_pipe.c
>> @@ -103,6 +103,13 @@ struct named_pipe_device_file
>> struct named_pipe_device *device; /* named pipe device */
>> };
>> +struct named_pipe_dir
>> +{
>> + struct object obj; /* object header */
>> + struct fd *fd; /* pseudo-fd for ioctls */
>> + struct named_pipe_device *device; /* named pipe device */
>> +};
>
>
> This is identical to named_pipe_device_file, maybe we could reuse that (we'd still use different ops).
It's certainly possible to reuse the struct, but I suppose making the two structs separate would help check for potential object type confusion.
>> +static WCHAR *named_pipe_dir_get_full_name( struct object *obj, data_size_t *ret_len )
>> +{
>> + static const WCHAR backslash = '\\';
>> + struct named_pipe_dir *dir = (struct named_pipe_dir *)obj;
>> + data_size_t len;
>> + char *device_name, *ret;
>> +
>> + device_name = (char *)dir->device->obj.ops->get_full_name( &dir->device->obj, &len );
>> + if (!device_name) return NULL;
>> +
>> + len += sizeof(WCHAR);
>> + ret = realloc(device_name, len);
>> + if (!ret)
>> + {
>> + free(device_name);
>> + return NULL;
>> + }
>> +
>> + *ret_len = len;
>> + memcpy( ret + len - sizeof(WCHAR), &backslash, sizeof(WCHAR) );
>> + return (WCHAR *)ret;
>> +}
>
>
> My first impression was that there should be a better way, but it seems that we indeed something like that. I think that it could be made a bit nicer by using WCHAR type for device_name and ret. memcpy() can be replaced with a simple assignment:
>
> ret[len / sizeof(WCHAR) - 1] = '\\';
I actually borrowed that part from server/object.c. Puzzled I was at first, but I guessed there
must be some legit reason as to why things were done that way, presumably for compatibility with
older compilers. (Apparently GCC 4.x compilation problems on Bugzilla were recently acknowledged as legitimate and fixed).
If this is not (or no longer) the case, perhaps we can modify the old code too?
>
>
>> +
>> +static struct object *named_pipe_dir_lookup_name( struct object *obj, struct unicode_str *name,
>> + unsigned int attr, struct object *root )
>> +{
>> + struct named_pipe_dir *dir = (struct named_pipe_dir *)obj;
>> + if (!name || !name->len) return NULL; /* open the directory itself */
>> + return dir->device->obj.ops->lookup_name( &dir->device->obj, name, attr, root );
>> +}
>> +
>> +static struct object *named_pipe_dir_open_file( struct object *obj, unsigned int access,
>> + unsigned int sharing, unsigned int options )
>> +{
>> + struct named_pipe_dir *dir = (struct named_pipe_dir *)obj;
>> + if (!dir->fd && !(dir->fd = alloc_pseudo_fd( &named_pipe_dir_fd_ops, obj, options ))) return NULL;
>
>
> Why do you call alloc_pseudo_fd() here instead of named_pipe_device_lookup_name, where you alloc dir object?
named_pipe_device_lookup_name lacks the options parameter.
> Also, it could use allow_fd_caching().
Thanks!
>
>
> Thanks,
>
> Jacek
>
--
Sincerely,
Jinoh Kang
Jan. 25, 2022
[PATCH vkd3d] vkd3d: Use Vulkan timeline semaphores for D3D12 fences.
by Conor McCarthy
If KHR_timeline_semaphore is not available, the old implementation
will be used.
Based on a vkd3d-proton patch by Hans-Kristian Arntzen.
Signed-off-by: Conor McCarthy <cmccarthy(a)codeweavers.com>
---
libs/vkd3d/command.c | 479 +++++++++++++++++++++++++++++++++----
libs/vkd3d/device.c | 14 ++
libs/vkd3d/vkd3d_private.h | 11 +
libs/vkd3d/vulkan_procs.h | 5 +
tests/d3d12.c | 11 +-
5 files changed, 463 insertions(+), 57 deletions(-)
diff --git a/libs/vkd3d/command.c b/libs/vkd3d/command.c
index 2cf1eba2..3203cb51 100644
--- a/libs/vkd3d/command.c
+++ b/libs/vkd3d/command.c
@@ -268,6 +268,7 @@ static HRESULT vkd3d_enqueue_gpu_fence(struct vkd3d_fence_worker *worker,
}
worker->enqueued_fences[worker->enqueued_fence_count].vk_fence = vk_fence;
+ worker->enqueued_fences[worker->enqueued_fence_count].vk_semaphore = VK_NULL_HANDLE;
waiting_fence = &worker->enqueued_fences[worker->enqueued_fence_count].waiting_fence;
waiting_fence->fence = fence;
waiting_fence->value = value;
@@ -317,6 +318,7 @@ static void vkd3d_fence_worker_remove_fence(struct vkd3d_fence_worker *worker, s
static void vkd3d_fence_worker_move_enqueued_fences_locked(struct vkd3d_fence_worker *worker)
{
unsigned int i;
+ bool timeline;
size_t count;
bool ret;
@@ -325,8 +327,18 @@ static void vkd3d_fence_worker_move_enqueued_fences_locked(struct vkd3d_fence_wo
count = worker->fence_count + worker->enqueued_fence_count;
- ret = vkd3d_array_reserve((void **)&worker->vk_fences, &worker->vk_fences_size,
- count, sizeof(*worker->vk_fences));
+ if ((timeline = worker->device->vk_info.KHR_timeline_semaphore))
+ {
+ ret = vkd3d_array_reserve((void **) &worker->vk_semaphores, &worker->vk_semaphores_size,
+ count, sizeof(*worker->vk_semaphores));
+ ret &= vkd3d_array_reserve((void **) &worker->semaphore_wait_values, &worker->semaphore_wait_values_size,
+ count, sizeof(*worker->semaphore_wait_values));
+ }
+ else
+ {
+ ret = vkd3d_array_reserve((void **)&worker->vk_fences, &worker->vk_fences_size,
+ count, sizeof(*worker->vk_fences));
+ }
ret &= vkd3d_array_reserve((void **)&worker->fences, &worker->fences_size,
count, sizeof(*worker->fences));
if (!ret)
@@ -339,7 +351,16 @@ static void vkd3d_fence_worker_move_enqueued_fences_locked(struct vkd3d_fence_wo
{
struct vkd3d_enqueued_fence *current = &worker->enqueued_fences[i];
- worker->vk_fences[worker->fence_count] = current->vk_fence;
+ if (timeline)
+ {
+ worker->vk_semaphores[worker->fence_count] = current->vk_semaphore;
+ worker->semaphore_wait_values[worker->fence_count] = current->waiting_fence.value;
+ }
+ else
+ {
+ worker->vk_fences[worker->fence_count] = current->vk_fence;
+ }
+
worker->fences[worker->fence_count] = current->waiting_fence;
++worker->fence_count;
}
@@ -347,6 +368,66 @@ static void vkd3d_fence_worker_move_enqueued_fences_locked(struct vkd3d_fence_wo
worker->enqueued_fence_count = 0;
}
+static void vkd3d_wait_for_gpu_timeline_semaphores(struct vkd3d_fence_worker *worker)
+{
+ struct d3d12_device *device = worker->device;
+ const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+ VkSemaphoreWaitInfoKHR wait_info;
+ VkSemaphore vk_semaphore;
+ uint64_t counter_value;
+ unsigned int i, j;
+ HRESULT hr;
+ int vr;
+
+ if (!worker->fence_count)
+ return;
+
+ wait_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO_KHR;
+ wait_info.pNext = NULL;
+ wait_info.flags = VK_SEMAPHORE_WAIT_ANY_BIT_KHR;
+ wait_info.pSemaphores = worker->vk_semaphores;
+ wait_info.semaphoreCount = worker->fence_count;
+ wait_info.pValues = worker->semaphore_wait_values;
+
+ vr = VK_CALL(vkWaitSemaphoresKHR(device->vk_device, &wait_info, ~(uint64_t)0));
+ if (vr == VK_TIMEOUT)
+ return;
+ if (vr != VK_SUCCESS)
+ {
+ ERR("Failed to wait for Vulkan timeline semaphores, vr %d.\n", vr);
+ return;
+ }
+
+ for (i = 0, j = 0; i < worker->fence_count; ++i)
+ {
+ struct vkd3d_waiting_fence *current = &worker->fences[i];
+
+ vk_semaphore = worker->vk_semaphores[i];
+ if ((vr = VK_CALL(vkGetSemaphoreCounterValueKHR(device->vk_device, vk_semaphore, &counter_value))) < 0)
+ {
+ ERR("Failed to get Vulkan semaphore status, vr %d.\n", vr);
+ }
+ else if (counter_value >= current->value)
+ {
+ TRACE("Signaling fence %p value %#"PRIx64".\n", current->fence, current->value);
+ if (FAILED(hr = d3d12_fence_signal(current->fence, counter_value, VK_NULL_HANDLE)))
+ ERR("Failed to signal D3D12 fence, hr %#x.\n", hr);
+
+ InterlockedDecrement(¤t->fence->pending_worker_operation_count);
+ continue;
+ }
+
+ if (i != j)
+ {
+ worker->vk_semaphores[j] = worker->vk_semaphores[i];
+ worker->semaphore_wait_values[j] = worker->semaphore_wait_values[i];
+ worker->fences[j] = worker->fences[i];
+ }
+ ++j;
+ }
+ worker->fence_count = j;
+}
+
static void vkd3d_wait_for_gpu_fences(struct vkd3d_fence_worker *worker)
{
struct d3d12_device *device = worker->device;
@@ -402,13 +483,19 @@ static void vkd3d_wait_for_gpu_fences(struct vkd3d_fence_worker *worker)
static void *vkd3d_fence_worker_main(void *arg)
{
struct vkd3d_fence_worker *worker = arg;
+ bool timeline;
int rc;
vkd3d_set_thread_name("vkd3d_fence");
+ timeline = worker->device->vk_info.KHR_timeline_semaphore;
+
for (;;)
{
- vkd3d_wait_for_gpu_fences(worker);
+ if (timeline)
+ vkd3d_wait_for_gpu_timeline_semaphores(worker);
+ else
+ vkd3d_wait_for_gpu_fences(worker);
if (!worker->fence_count || atomic_add_fetch(&worker->enqueued_fence_count, 0))
{
@@ -473,6 +560,10 @@ HRESULT vkd3d_fence_worker_start(struct vkd3d_fence_worker *worker,
worker->vk_fences_size = 0;
worker->fences = NULL;
worker->fences_size = 0;
+ worker->vk_semaphores = NULL;
+ worker->vk_semaphores_size = 0;
+ worker->semaphore_wait_values = NULL;
+ worker->semaphore_wait_values_size = 0;
if ((rc = pthread_mutex_init(&worker->mutex, NULL)))
{
@@ -535,6 +626,8 @@ HRESULT vkd3d_fence_worker_stop(struct vkd3d_fence_worker *worker,
vkd3d_free(worker->enqueued_fences);
vkd3d_free(worker->vk_fences);
vkd3d_free(worker->fences);
+ vkd3d_free(worker->vk_semaphores);
+ vkd3d_free(worker->semaphore_wait_values);
return S_OK;
}
@@ -684,6 +777,7 @@ static void d3d12_fence_destroy_vk_objects(struct d3d12_fence *fence)
}
d3d12_fence_garbage_collect_vk_semaphores_locked(fence, true);
+ VK_CALL(vkDestroySemaphore(device->vk_device, fence->timeline_semaphore, NULL));
pthread_mutex_unlock(&fence->mutex);
}
@@ -802,31 +896,21 @@ static HRESULT d3d12_fence_add_vk_semaphore(struct d3d12_fence *fence,
return hr;
}
-static HRESULT d3d12_fence_signal(struct d3d12_fence *fence, uint64_t value, VkFence vk_fence)
+static bool d3d12_fence_signal_external_events_locked(struct d3d12_fence *fence)
{
struct d3d12_device *device = fence->device;
- struct vkd3d_signaled_semaphore *current;
bool signal_null_event_cond = false;
unsigned int i, j;
- int rc;
-
- if ((rc = pthread_mutex_lock(&fence->mutex)))
- {
- ERR("Failed to lock mutex, error %d.\n", rc);
- return hresult_from_errno(rc);
- }
-
- fence->value = value;
for (i = 0, j = 0; i < fence->event_count; ++i)
{
struct vkd3d_waiting_event *current = &fence->events[i];
- if (current->value <= value)
+ if (current->value <= fence->value)
{
if (current->event)
{
- fence->device->signal_event(current->event);
+ device->signal_event(current->event);
}
else
{
@@ -841,9 +925,36 @@ static HRESULT d3d12_fence_signal(struct d3d12_fence *fence, uint64_t value, VkF
++j;
}
}
+
fence->event_count = j;
- if (signal_null_event_cond)
+ return signal_null_event_cond;
+}
+
+static HRESULT d3d12_fence_signal(struct d3d12_fence *fence, uint64_t value, VkFence vk_fence)
+{
+ struct d3d12_device *device = fence->device;
+ struct vkd3d_signaled_semaphore *current;
+ unsigned int i;
+ int rc;
+
+ if ((rc = pthread_mutex_lock(&fence->mutex)))
+ {
+ ERR("Failed to lock mutex, error %d.\n", rc);
+ return hresult_from_errno(rc);
+ }
+
+ if (value < fence->value && device->vk_info.KHR_timeline_semaphore)
+ {
+ FIXME("Fence values must be monotonically increasing. Fence %p, was %"PRIx64", now %"PRIx64".\n",
+ fence, fence->value, value);
+ }
+ else
+ {
+ fence->value = value;
+ }
+
+ if (d3d12_fence_signal_external_events_locked(fence))
pthread_cond_broadcast(&fence->null_event_cond);
if (vk_fence)
@@ -1069,12 +1180,71 @@ static HRESULT STDMETHODCALLTYPE d3d12_fence_SetEventOnCompletion(ID3D12Fence *i
return S_OK;
}
+static HRESULT d3d12_fence_signal_cpu_timeline_semaphore(struct d3d12_fence *fence, uint64_t value)
+{
+ struct d3d12_device *device = fence->device;
+ VkSemaphoreSignalInfoKHR info;
+ VkResult vr = VK_SUCCESS;
+ int rc;
+
+ if ((rc = pthread_mutex_lock(&fence->mutex)))
+ {
+ ERR("Failed to lock mutex, error %d.\n", rc);
+ return hresult_from_errno(rc);
+ }
+
+ /* We must only signal a value which is greater than the current value.
+ * That value can be in the range of current known value (fence->value), or as large as pending_timeline_value.
+ * Pending timeline value signal might be blocked by another synchronization primitive, and thus statically
+ * cannot be that value, so the safest thing to do is to check the current value which is updated by the fence
+ * wait thread continuously. This check is technically racy since the value might be immediately out of date,
+ * but there is no way to avoid this. */
+ if (value > fence->value)
+ {
+ struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+
+ /* Sanity check against the delta limit. */
+ if (value - fence->value > device->vk_info.timeline_semaphore_properties.maxTimelineSemaphoreValueDifference)
+ {
+ FIXME("Timeline semaphore delta is %"PRIu64", but implementation only supports a delta of %"PRIu64".\n",
+ value - fence->value, device->vk_info.timeline_semaphore_properties.maxTimelineSemaphoreValueDifference);
+ }
+
+ info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO_KHR;
+ info.pNext = NULL;
+ info.semaphore = fence->timeline_semaphore;
+ info.value = value;
+ if ((vr = VK_CALL(vkSignalSemaphoreKHR(device->vk_device, &info))) >= 0)
+ {
+ fence->value = value;
+ if (value > fence->pending_timeline_value)
+ fence->pending_timeline_value = value;
+ }
+ else
+ {
+ ERR("Failed to signal timeline semaphore, vr %d.\n", vr);
+ }
+ }
+ else if (value != fence->value)
+ {
+ FIXME("Attempting to signal fence %p with %"PRIu64", but value is currently %"PRIu64".\n",
+ fence, value, fence->value);
+ }
+
+ d3d12_fence_signal_external_events_locked(fence);
+
+ pthread_mutex_unlock(&fence->mutex);
+ return hresult_from_vk_result(vr);
+}
+
static HRESULT STDMETHODCALLTYPE d3d12_fence_Signal(ID3D12Fence *iface, UINT64 value)
{
struct d3d12_fence *fence = impl_from_ID3D12Fence(iface);
TRACE("iface %p, value %#"PRIx64".\n", iface, value);
+ if (fence->timeline_semaphore)
+ return d3d12_fence_signal_cpu_timeline_semaphore(fence, value);
return d3d12_fence_signal(fence, value, VK_NULL_HANDLE);
}
@@ -1105,6 +1275,34 @@ static struct d3d12_fence *unsafe_impl_from_ID3D12Fence(ID3D12Fence *iface)
return impl_from_ID3D12Fence(iface);
}
+static HRESULT d3d12_fence_init_timeline_semaphore(struct d3d12_fence *fence, struct d3d12_device *device,
+ uint64_t initial_value)
+{
+ const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+ VkSemaphoreTypeCreateInfoKHR type_info;
+ VkSemaphoreCreateInfo info;
+ VkResult vr;
+
+ info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+ info.pNext = &type_info;
+ info.flags = 0;
+
+ type_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR;
+ type_info.pNext = NULL;
+ type_info.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE_KHR;
+ type_info.initialValue = initial_value;
+
+ if ((vr = VK_CALL(vkCreateSemaphore(device->vk_device, &info, NULL, &fence->timeline_semaphore))) < 0)
+ {
+ WARN("Failed to create timeline semaphore, vr %d.\n", vr);
+ return hresult_from_vk_result(vr);
+ }
+
+ fence->pending_timeline_value = initial_value;
+
+ return S_OK;
+}
+
static HRESULT d3d12_fence_init(struct d3d12_fence *fence, struct d3d12_device *device,
UINT64 initial_value, D3D12_FENCE_FLAGS flags)
{
@@ -1136,6 +1334,11 @@ static HRESULT d3d12_fence_init(struct d3d12_fence *fence, struct d3d12_device *
fence->events_size = 0;
fence->event_count = 0;
+ fence->timeline_semaphore = NULL;
+ if (device->vk_info.KHR_timeline_semaphore && FAILED(hr = d3d12_fence_init_timeline_semaphore(fence,
+ device, initial_value)))
+ return hr;
+
list_init(&fence->semaphores);
fence->semaphore_count = 0;
@@ -6000,18 +6203,97 @@ static void STDMETHODCALLTYPE d3d12_command_queue_EndEvent(ID3D12CommandQueue *i
FIXME("iface %p stub!\n", iface);
}
+static bool d3d12_fence_update_gpu_signal_timeline_semaphore(struct d3d12_fence *fence, uint64_t value)
+{
+ struct d3d12_device *device = fence->device;
+ bool need_signal = false;
+ int rc;
+
+ if ((rc = pthread_mutex_lock(&fence->mutex)))
+ {
+ ERR("Failed to lock mutex, error %d.\n", rc);
+ return false;
+ }
+
+ /* If we're attempting to async signal a fence with a value which is not monotonically increasing the payload value,
+ * warn about this case. Do not treat this as an error since it might work. */
+ if (value > fence->pending_timeline_value)
+ {
+ /* Sanity check against the delta limit. Use the current fence value. */
+ if (value - fence->value > device->vk_info.timeline_semaphore_properties.maxTimelineSemaphoreValueDifference)
+ {
+ FIXME("Timeline semaphore delta is %"PRIu64", but implementation only supports a delta of %"PRIu64".\n",
+ value - fence->value, device->vk_info.timeline_semaphore_properties.maxTimelineSemaphoreValueDifference);
+ }
+
+ fence->pending_timeline_value = value;
+ need_signal = true;
+ }
+ else
+ {
+ FIXME("Fence %p is being signalled non-monotonically. Old pending value %"PRIu64", new pending value %"PRIu64".\n",
+ fence, fence->pending_timeline_value, value);
+
+ /* Mostly to be safe against weird, unknown use cases. The pending signal might be blocked
+ * by another fence, so we'll base this on the actual, currently visible count value. */
+ need_signal = value > fence->value;
+ }
+
+ pthread_mutex_unlock(&fence->mutex);
+
+ return need_signal;
+}
+
+static HRESULT vkd3d_enqueue_timeline_semaphore(struct vkd3d_fence_worker *worker, VkSemaphore vk_semaphore,
+ struct d3d12_fence *fence, uint64_t value, struct vkd3d_queue *queue)
+{
+ struct vkd3d_waiting_fence *waiting_fence;
+ int rc;
+
+ TRACE("worker %p, fence %p, value %#"PRIx64".\n", worker, fence, value);
+
+ if ((rc = pthread_mutex_lock(&worker->mutex)))
+ {
+ ERR("Failed to lock mutex, error %d.\n", rc);
+ return hresult_from_errno(rc);
+ }
+
+ if (!vkd3d_array_reserve((void **)&worker->enqueued_fences, &worker->enqueued_fences_size,
+ worker->enqueued_fence_count + 1, sizeof(*worker->enqueued_fences)))
+ {
+ ERR("Failed to add GPU timeline semaphore.\n");
+ pthread_mutex_unlock(&worker->mutex);
+ return E_OUTOFMEMORY;
+ }
+
+ worker->enqueued_fences[worker->enqueued_fence_count].vk_semaphore = vk_semaphore;
+ waiting_fence = &worker->enqueued_fences[worker->enqueued_fence_count].waiting_fence;
+ waiting_fence->fence = fence;
+ waiting_fence->value = value;
+ waiting_fence->queue = queue;
+ ++worker->enqueued_fence_count;
+
+ InterlockedIncrement(&fence->pending_worker_operation_count);
+
+ pthread_cond_signal(&worker->cond);
+ pthread_mutex_unlock(&worker->mutex);
+
+ return S_OK;
+}
+
static HRESULT STDMETHODCALLTYPE d3d12_command_queue_Signal(ID3D12CommandQueue *iface,
ID3D12Fence *fence_iface, UINT64 value)
{
struct d3d12_command_queue *command_queue = impl_from_ID3D12CommandQueue(iface);
+ VkTimelineSemaphoreSubmitInfoKHR timeline_submit_info;
const struct vkd3d_vk_device_procs *vk_procs;
VkSemaphore vk_semaphore = VK_NULL_HANDLE;
VkFence vk_fence = VK_NULL_HANDLE;
struct vkd3d_queue *vkd3d_queue;
+ uint64_t sequence_number = 0;
struct d3d12_device *device;
struct d3d12_fence *fence;
VkSubmitInfo submit_info;
- uint64_t sequence_number;
VkQueue vk_queue;
VkResult vr;
HRESULT hr;
@@ -6024,10 +6306,27 @@ static HRESULT STDMETHODCALLTYPE d3d12_command_queue_Signal(ID3D12CommandQueue *
fence = unsafe_impl_from_ID3D12Fence(fence_iface);
- if ((vr = d3d12_fence_create_vk_fence(fence, &vk_fence)) < 0)
+ if (device->vk_info.KHR_timeline_semaphore)
{
- WARN("Failed to create Vulkan fence, vr %d.\n", vr);
- goto fail_vkresult;
+ if (d3d12_fence_update_gpu_signal_timeline_semaphore(fence, value))
+ {
+ vk_semaphore = fence->timeline_semaphore;
+ assert(vk_semaphore);
+ }
+ else
+ {
+ /* If we are not incrementing the counter, this is a noop since we cannot
+ * signal a timeline semaphore non-monotonically in Vulkan. */
+ return S_OK;
+ }
+ }
+ else
+ {
+ if ((vr = d3d12_fence_create_vk_fence(fence, &vk_fence)) < 0)
+ {
+ WARN("Failed to create Vulkan fence, vr %d.\n", vr);
+ goto fail_vkresult;
+ }
}
if (!(vk_queue = vkd3d_queue_acquire(vkd3d_queue)))
@@ -6037,7 +6336,8 @@ static HRESULT STDMETHODCALLTYPE d3d12_command_queue_Signal(ID3D12CommandQueue *
goto fail;
}
- if ((vr = vkd3d_queue_create_vk_semaphore_locked(vkd3d_queue, device, &vk_semaphore)) < 0)
+ if (!device->vk_info.KHR_timeline_semaphore && (vr = vkd3d_queue_create_vk_semaphore_locked(vkd3d_queue,
+ device, &vk_semaphore)) < 0)
{
ERR("Failed to create Vulkan semaphore, vr %d.\n", vr);
vk_semaphore = VK_NULL_HANDLE;
@@ -6053,7 +6353,19 @@ static HRESULT STDMETHODCALLTYPE d3d12_command_queue_Signal(ID3D12CommandQueue *
submit_info.signalSemaphoreCount = vk_semaphore ? 1 : 0;
submit_info.pSignalSemaphores = &vk_semaphore;
- if ((vr = VK_CALL(vkQueueSubmit(vk_queue, 1, &submit_info, vk_fence))) >= 0)
+ if (device->vk_info.KHR_timeline_semaphore)
+ {
+ timeline_submit_info.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR;
+ timeline_submit_info.pNext = NULL;
+ timeline_submit_info.pSignalSemaphoreValues = &value;
+ timeline_submit_info.signalSemaphoreValueCount = 1;
+ timeline_submit_info.waitSemaphoreValueCount = 0;
+ timeline_submit_info.pWaitSemaphoreValues = NULL;
+ submit_info.pNext = &timeline_submit_info;
+ }
+
+ vr = VK_CALL(vkQueueSubmit(vk_queue, 1, &submit_info, vk_fence));
+ if (!device->vk_info.KHR_timeline_semaphore && vr >= 0)
{
sequence_number = ++vkd3d_queue->submitted_sequence_number;
@@ -6070,26 +6382,34 @@ static HRESULT STDMETHODCALLTYPE d3d12_command_queue_Signal(ID3D12CommandQueue *
goto fail_vkresult;
}
- if (vk_semaphore && SUCCEEDED(hr = d3d12_fence_add_vk_semaphore(fence, vk_semaphore, vk_fence, value)))
- vk_semaphore = VK_NULL_HANDLE;
-
- vr = VK_CALL(vkGetFenceStatus(device->vk_device, vk_fence));
- if (vr == VK_NOT_READY)
- {
- if (SUCCEEDED(hr = vkd3d_enqueue_gpu_fence(&device->fence_worker, vk_fence, fence, value, vkd3d_queue, sequence_number)))
- vk_fence = VK_NULL_HANDLE;
- }
- else if (vr == VK_SUCCESS)
+ if (device->vk_info.KHR_timeline_semaphore)
{
- TRACE("Already signaled %p, value %#"PRIx64".\n", fence, value);
- hr = d3d12_fence_signal(fence, value, vk_fence);
- vk_fence = VK_NULL_HANDLE;
- vkd3d_queue_update_sequence_number(vkd3d_queue, sequence_number, device);
+ if (SUCCEEDED(hr = vkd3d_enqueue_timeline_semaphore(&device->fence_worker, vk_semaphore, fence, value, vkd3d_queue)))
+ vk_semaphore = VK_NULL_HANDLE;
}
else
{
- FIXME("Failed to get fence status, vr %d.\n", vr);
- hr = hresult_from_vk_result(vr);
+ if (vk_semaphore && SUCCEEDED(hr = d3d12_fence_add_vk_semaphore(fence, vk_semaphore, vk_fence, value)))
+ vk_semaphore = VK_NULL_HANDLE;
+
+ vr = VK_CALL(vkGetFenceStatus(device->vk_device, vk_fence));
+ if (vr == VK_NOT_READY)
+ {
+ if (SUCCEEDED(hr = vkd3d_enqueue_gpu_fence(&device->fence_worker, vk_fence, fence, value, vkd3d_queue, sequence_number)))
+ vk_fence = VK_NULL_HANDLE;
+ }
+ else if (vr == VK_SUCCESS)
+ {
+ TRACE("Already signaled %p, value %#"PRIx64".\n", fence, value);
+ hr = d3d12_fence_signal(fence, value, vk_fence);
+ vk_fence = VK_NULL_HANDLE;
+ vkd3d_queue_update_sequence_number(vkd3d_queue, sequence_number, device);
+ }
+ else
+ {
+ FIXME("Failed to get fence status, vr %d.\n", vr);
+ hr = hresult_from_vk_result(vr);
+ }
}
if (vk_fence || vk_semaphore)
@@ -6105,32 +6425,27 @@ fail_vkresult:
hr = hresult_from_vk_result(vr);
fail:
VK_CALL(vkDestroyFence(device->vk_device, vk_fence, NULL));
- VK_CALL(vkDestroySemaphore(device->vk_device, vk_semaphore, NULL));
+ if (!device->vk_info.KHR_timeline_semaphore)
+ VK_CALL(vkDestroySemaphore(device->vk_device, vk_semaphore, NULL));
return hr;
}
-static HRESULT STDMETHODCALLTYPE d3d12_command_queue_Wait(ID3D12CommandQueue *iface,
- ID3D12Fence *fence_iface, UINT64 value)
+static HRESULT d3d12_command_queue_wait_binary_semaphore(struct d3d12_command_queue *command_queue,
+ struct d3d12_fence *fence, uint64_t value)
{
static const VkPipelineStageFlagBits wait_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
- struct d3d12_command_queue *command_queue = impl_from_ID3D12CommandQueue(iface);
const struct vkd3d_vk_device_procs *vk_procs;
struct vkd3d_signaled_semaphore *semaphore;
uint64_t completed_value = 0;
struct vkd3d_queue *queue;
- struct d3d12_fence *fence;
VkSubmitInfo submit_info;
VkQueue vk_queue;
VkResult vr;
HRESULT hr;
- TRACE("iface %p, fence %p, value %#"PRIx64".\n", iface, fence_iface, value);
-
vk_procs = &command_queue->device->vk_procs;
queue = command_queue->vkd3d_queue;
- fence = unsafe_impl_from_ID3D12Fence(fence_iface);
-
semaphore = d3d12_fence_acquire_vk_semaphore(fence, value, &completed_value);
if (!semaphore && completed_value >= value)
{
@@ -6208,6 +6523,72 @@ fail:
return hr;
}
+static HRESULT d3d12_command_queue_wait_timeline_semaphore(struct d3d12_command_queue *command_queue,
+ struct d3d12_fence *fence, uint64_t value)
+{
+ static const VkPipelineStageFlagBits wait_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
+ VkTimelineSemaphoreSubmitInfoKHR timeline_submit_info;
+ const struct vkd3d_vk_device_procs *vk_procs;
+ struct vkd3d_queue *queue;
+ VkSubmitInfo submit_info;
+ VkQueue vk_queue;
+ VkResult vr;
+
+ vk_procs = &command_queue->device->vk_procs;
+ queue = command_queue->vkd3d_queue;
+
+ assert(fence->timeline_semaphore);
+ timeline_submit_info.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR;
+ timeline_submit_info.pNext = NULL;
+ timeline_submit_info.signalSemaphoreValueCount = 0;
+ timeline_submit_info.pSignalSemaphoreValues = NULL;
+ timeline_submit_info.waitSemaphoreValueCount = 1;
+ timeline_submit_info.pWaitSemaphoreValues = &value;
+
+ if (!(vk_queue = vkd3d_queue_acquire(queue)))
+ {
+ ERR("Failed to acquire queue %p.\n", queue);
+ return E_FAIL;
+ }
+
+ submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submit_info.pNext = &timeline_submit_info;
+ submit_info.waitSemaphoreCount = 1;
+ submit_info.pWaitSemaphores = &fence->timeline_semaphore;
+ submit_info.pWaitDstStageMask = &wait_stage_mask;
+ submit_info.commandBufferCount = 0;
+ submit_info.pCommandBuffers = NULL;
+ submit_info.signalSemaphoreCount = 0;
+ submit_info.pSignalSemaphores = NULL;
+
+ vr = VK_CALL(vkQueueSubmit(vk_queue, 1, &submit_info, VK_NULL_HANDLE));
+
+ vkd3d_queue_release(queue);
+
+ if (vr < 0)
+ {
+ WARN("Failed to submit wait operation, vr %d.\n", vr);
+ return hresult_from_vk_result(vr);
+ }
+
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_queue_Wait(ID3D12CommandQueue *iface,
+ ID3D12Fence *fence_iface, UINT64 value)
+{
+ struct d3d12_command_queue *command_queue = impl_from_ID3D12CommandQueue(iface);
+ struct d3d12_fence *fence = unsafe_impl_from_ID3D12Fence(fence_iface);
+
+ TRACE("iface %p, fence %p, value %#"PRIx64".\n", iface, fence_iface, value);
+
+ if (command_queue->device->vk_info.KHR_timeline_semaphore)
+ return d3d12_command_queue_wait_timeline_semaphore(command_queue, fence, value);
+
+ FIXME_ONCE("KHR_timeline_semaphore is not available. Some wait commands may be unsupported.\n");
+ return d3d12_command_queue_wait_binary_semaphore(command_queue, fence, value);
+}
+
static HRESULT STDMETHODCALLTYPE d3d12_command_queue_GetTimestampFrequency(ID3D12CommandQueue *iface,
UINT64 *frequency)
{
diff --git a/libs/vkd3d/device.c b/libs/vkd3d/device.c
index 4bcb5efc..bc5cad76 100644
--- a/libs/vkd3d/device.c
+++ b/libs/vkd3d/device.c
@@ -129,6 +129,7 @@ static const struct vkd3d_optional_extension_info optional_device_extensions[] =
VK_EXTENSION(KHR_MAINTENANCE3, KHR_maintenance3),
VK_EXTENSION(KHR_PUSH_DESCRIPTOR, KHR_push_descriptor),
VK_EXTENSION(KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE, KHR_sampler_mirror_clamp_to_edge),
+ VK_EXTENSION(KHR_TIMELINE_SEMAPHORE, KHR_timeline_semaphore),
/* EXT extensions */
VK_EXTENSION(EXT_CALIBRATED_TIMESTAMPS, EXT_calibrated_timestamps),
VK_EXTENSION(EXT_CONDITIONAL_RENDERING, EXT_conditional_rendering),
@@ -683,6 +684,7 @@ struct vkd3d_physical_device_info
VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT texel_buffer_alignment_properties;
VkPhysicalDeviceTransformFeedbackPropertiesEXT xfb_properties;
VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT vertex_divisor_properties;
+ VkPhysicalDeviceTimelineSemaphorePropertiesKHR timeline_semaphore_properties;
VkPhysicalDeviceProperties2KHR properties2;
@@ -694,6 +696,7 @@ struct vkd3d_physical_device_info
VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT texel_buffer_alignment_features;
VkPhysicalDeviceTransformFeedbackFeaturesEXT xfb_features;
VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT vertex_divisor_features;
+ VkPhysicalDeviceTimelineSemaphoreFeaturesKHR timeline_semaphore_features;
VkPhysicalDeviceFeatures2 features2;
};
@@ -705,10 +708,12 @@ static void vkd3d_physical_device_info_init(struct vkd3d_physical_device_info *i
VkPhysicalDeviceDescriptorIndexingPropertiesEXT *descriptor_indexing_properties;
VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT *vertex_divisor_properties;
VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT *buffer_alignment_properties;
+ VkPhysicalDeviceTimelineSemaphorePropertiesKHR *timeline_semaphore_properties;
VkPhysicalDeviceDescriptorIndexingFeaturesEXT *descriptor_indexing_features;
VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT *vertex_divisor_features;
VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT *buffer_alignment_features;
VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT *demote_features;
+ VkPhysicalDeviceTimelineSemaphoreFeaturesKHR *timeline_semaphore_features;
VkPhysicalDeviceDepthClipEnableFeaturesEXT *depth_clip_features;
VkPhysicalDeviceMaintenance3Properties *maintenance3_properties;
VkPhysicalDeviceTransformFeedbackPropertiesEXT *xfb_properties;
@@ -727,6 +732,8 @@ static void vkd3d_physical_device_info_init(struct vkd3d_physical_device_info *i
buffer_alignment_properties = &info->texel_buffer_alignment_properties;
vertex_divisor_features = &info->vertex_divisor_features;
vertex_divisor_properties = &info->vertex_divisor_properties;
+ timeline_semaphore_features = &info->timeline_semaphore_features;
+ timeline_semaphore_properties = &info->timeline_semaphore_properties;
xfb_features = &info->xfb_features;
xfb_properties = &info->xfb_properties;
@@ -746,6 +753,8 @@ static void vkd3d_physical_device_info_init(struct vkd3d_physical_device_info *i
vk_prepend_struct(&info->features2, xfb_features);
vertex_divisor_features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT;
vk_prepend_struct(&info->features2, vertex_divisor_features);
+ timeline_semaphore_features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR;
+ vk_prepend_struct(&info->features2, timeline_semaphore_features);
if (vulkan_info->KHR_get_physical_device_properties2)
VK_CALL(vkGetPhysicalDeviceFeatures2KHR(physical_device, &info->features2));
@@ -764,6 +773,8 @@ static void vkd3d_physical_device_info_init(struct vkd3d_physical_device_info *i
vk_prepend_struct(&info->properties2, xfb_properties);
vertex_divisor_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, vertex_divisor_properties);
+ timeline_semaphore_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES_KHR;
+ vk_prepend_struct(&info->properties2, timeline_semaphore_properties);
if (vulkan_info->KHR_get_physical_device_properties2)
VK_CALL(vkGetPhysicalDeviceProperties2KHR(physical_device, &info->properties2));
@@ -1312,6 +1323,7 @@ static HRESULT vkd3d_init_device_caps(struct d3d12_device *device,
vulkan_info->rasterization_stream = physical_device_info->xfb_properties.transformFeedbackRasterizationStreamSelect;
vulkan_info->transform_feedback_queries = physical_device_info->xfb_properties.transformFeedbackQueries;
vulkan_info->max_vertex_attrib_divisor = max(physical_device_info->vertex_divisor_properties.maxVertexAttribDivisor, 1);
+ vulkan_info->timeline_semaphore_properties = physical_device_info->timeline_semaphore_properties;
device->feature_options.DoublePrecisionFloatShaderOps = features->shaderFloat64;
device->feature_options.OutputMergerLogicOp = features->logicOp;
@@ -1434,6 +1446,8 @@ static HRESULT vkd3d_init_device_caps(struct d3d12_device *device,
vulkan_info->EXT_shader_demote_to_helper_invocation = false;
if (!physical_device_info->texel_buffer_alignment_features.texelBufferAlignment)
vulkan_info->EXT_texel_buffer_alignment = false;
+ if (!physical_device_info->timeline_semaphore_features.timelineSemaphore)
+ vulkan_info->KHR_timeline_semaphore = false;
vulkan_info->texel_buffer_alignment_properties = physical_device_info->texel_buffer_alignment_properties;
diff --git a/libs/vkd3d/vkd3d_private.h b/libs/vkd3d/vkd3d_private.h
index 136b0203..088d4312 100644
--- a/libs/vkd3d/vkd3d_private.h
+++ b/libs/vkd3d/vkd3d_private.h
@@ -107,6 +107,7 @@ struct vkd3d_vulkan_info
bool KHR_maintenance3;
bool KHR_push_descriptor;
bool KHR_sampler_mirror_clamp_to_edge;
+ bool KHR_timeline_semaphore;
/* EXT device extensions */
bool EXT_calibrated_timestamps;
bool EXT_conditional_rendering;
@@ -130,6 +131,8 @@ struct vkd3d_vulkan_info
VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT texel_buffer_alignment_properties;
+ VkPhysicalDeviceTimelineSemaphorePropertiesKHR timeline_semaphore_properties;
+
unsigned int shader_extension_count;
enum vkd3d_shader_spirv_extension shader_extensions[VKD3D_MAX_SHADER_EXTENSIONS];
@@ -196,6 +199,7 @@ struct vkd3d_fence_worker
struct vkd3d_enqueued_fence
{
VkFence vk_fence;
+ VkSemaphore vk_semaphore;
struct vkd3d_waiting_fence waiting_fence;
} *enqueued_fences;
size_t enqueued_fences_size;
@@ -205,6 +209,10 @@ struct vkd3d_fence_worker
size_t vk_fences_size;
struct vkd3d_waiting_fence *fences;
size_t fences_size;
+ VkSemaphore *vk_semaphores;
+ size_t vk_semaphores_size;
+ uint64_t *semaphore_wait_values;
+ size_t semaphore_wait_values_size;
struct d3d12_device *device;
};
@@ -378,6 +386,9 @@ struct d3d12_fence
size_t events_size;
size_t event_count;
+ VkSemaphore timeline_semaphore;
+ uint64_t pending_timeline_value;
+
struct list semaphores;
unsigned int semaphore_count;
diff --git a/libs/vkd3d/vulkan_procs.h b/libs/vkd3d/vulkan_procs.h
index 60556735..34e0ab4b 100644
--- a/libs/vkd3d/vulkan_procs.h
+++ b/libs/vkd3d/vulkan_procs.h
@@ -195,6 +195,11 @@ VK_DEVICE_EXT_PFN(vkGetDescriptorSetLayoutSupportKHR)
/* VK_KHR_push_descriptor */
VK_DEVICE_EXT_PFN(vkCmdPushDescriptorSetKHR)
+/* VK_KHR_timeline_semaphore */
+VK_DEVICE_EXT_PFN(vkGetSemaphoreCounterValueKHR)
+VK_DEVICE_EXT_PFN(vkWaitSemaphoresKHR)
+VK_DEVICE_EXT_PFN(vkSignalSemaphoreKHR)
+
/* VK_EXT_calibrated_timestamps */
VK_DEVICE_EXT_PFN(vkGetCalibratedTimestampsEXT)
diff --git a/tests/d3d12.c b/tests/d3d12.c
index 82f353a7..b5a9b89f 100644
--- a/tests/d3d12.c
+++ b/tests/d3d12.c
@@ -33238,7 +33238,9 @@ static void test_queue_wait(void)
command_list = context.list;
queue = context.queue;
- queue2 = create_command_queue(device, D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);
+ /* 'queue2' must not map to the same command queue as 'queue', or Wait() before GPU signal will fail.
+ * Using a compute queue fixes this on most hardware, but it may still fail on low spec hardware. */
+ queue2 = create_command_queue(device, D3D12_COMMAND_LIST_TYPE_COMPUTE, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);
event = create_event();
ok(event, "Failed to create event.\n");
@@ -33303,12 +33305,6 @@ static void test_queue_wait(void)
check_readback_data_uint(&rb, NULL, 0xff00ff00, 0);
release_resource_readback(&rb);
- if (!vkd3d_test_platform_is_windows())
- {
- skip("Wait() is not implemented yet.\n"); /* FIXME */
- goto skip_tests;
- }
-
/* Wait() before CPU signal */
update_buffer_data(cb, 0, sizeof(blue), &blue);
queue_wait(queue, fence, 2);
@@ -33384,7 +33380,6 @@ static void test_queue_wait(void)
check_readback_data_uint(&rb, NULL, 0xff00ff00, 0);
release_resource_readback(&rb);
-skip_tests:
/* Signal() and Wait() in the same command queue */
update_buffer_data(cb, 0, sizeof(blue), &blue);
queue_signal(queue, fence, 7);
--
2.34.1
Jan. 25, 2022
[PATCH] Increased winsock2 maximum sockets
by Ben Shefte
Maximised socket amount substantially to allow games and streaming applications that fail after using 128 sockets.
reference:
https://stackoverflow.com/questions/14471964/fd-size-and-maximum-number-of-…
Signed-off-by: Ben Shefte<shefben(a)gmail.com>
---
dlls/ws2_32/socket.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c
index fe7e4fb3464..a44c38285cc 100644
--- a/dlls/ws2_32/socket.c
+++ b/dlls/ws2_32/socket.c
@@ -419,7 +419,7 @@ static BOOL socket_list_remove( SOCKET socket )
return FALSE;
}
-#define MAX_SOCKETS_PER_PROCESS 128 /* reasonable guess */
+#define MAX_SOCKETS_PER_PROCESS 32767 /* reasonable guess */
#define MAX_UDP_DATAGRAM 1024
static INT WINAPI WSA_DefaultBlockingHook( FARPROC x );
--
2.27.0
Jan. 25, 2022
[PATCH] WBEMProxy: Implemented Complete SoftwareLicensingProduct row and data
by Ben Shefte
Signed-off-by: Ben Shefte<shefben(a)gmail.com>
---
dlls/wbemprox/builtin.c | 98 ++++++++++++++++++++++++++++++++++++++---
1 file changed, 92 insertions(+), 6 deletions(-)
diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c
index 5a09d64b8fb..4122afee59d 100644
--- a/dlls/wbemprox/builtin.c
+++ b/dlls/wbemprox/builtin.c
@@ -388,9 +388,51 @@ static const struct column col_sid[] =
{ L"SidLength", CIM_UINT32 },
};
static const struct column col_softwarelicensingproduct[] =
-{
- { L"LicenseIsAddon", CIM_BOOLEAN },
- { L"LicenseStatus", CIM_UINT32 },
+ {
+ { L"ID", CIM_STRING | COL_FLAG_KEY },
+ { L"Name", CIM_STRING },
+ { L"Description", CIM_STRING },
+ { L"ApplicationID", CIM_STRING },
+ { L"ProcessorURL", CIM_STRING },
+ { L"MachineURL", CIM_STRING },
+ { L"ProductKeyURL", CIM_STRING },
+ { L"UseLicenseURL", CIM_STRING },
+ { L"LicenseStatus", CIM_UINT32 },
+ { L"LicenseStatusReason", CIM_UINT32 },
+ { L"GracePeriodRemaining", CIM_UINT32 },
+ { L"EvaluationEndDate", CIM_DATETIME },
+ { L"OfflineInstallationId", CIM_STRING },
+ { L"PartialProductKey", CIM_STRING },
+ { L"ProductKeyID", CIM_STRING },
+ { L"LicenseFamily", CIM_STRING },
+ { L"LicenseDependsOn", CIM_STRING },
+ { L"LicenseIsAddon", CIM_BOOLEAN},
+ { L"VLActivationInterval", CIM_UINT32 },
+ { L"VLRenewalInterval", CIM_UINT32 },
+ { L"KeyManagementServiceProductKeyID", CIM_STRING },
+ { L"KeyManagementServiceMachine", CIM_STRING },
+ { L"KeyManagementServicePort", CIM_UINT32 },
+ { L"DiscoveredKeyManagementServiceMachineName", CIM_STRING },
+ { L"DiscoveredKeyManagementServiceMachinePort", CIM_UINT32 },
+ { L"IsKeyManagementServiceMachine", CIM_UINT32 },
+ { L"KeyManagementServiceCurrentCount", CIM_UINT32 },
+ { L"RequiredClientCount", CIM_UINT32 },
+ { L"KeyManagementServiceUnlicensedRequests", CIM_UINT32 },
+ { L"KeyManagementServiceLicensedRequests", CIM_UINT32 },
+ { L"KeyManagementServiceOOBGraceRequests", CIM_UINT32 },
+ { L"KeyManagementServiceOOTGraceRequests", CIM_UINT32 },
+ { L"KeyManagementServiceNonGenuineGraceRequests", CIM_UINT32 },
+ { L"KeyManagementServiceTotalRequests", CIM_UINT32 },
+ { L"KeyManagementServiceFailedRequests", CIM_UINT32 },
+ { L"KeyManagementServiceNotificationRequests", CIM_UINT32 },
+ { L"GenuineStatus", CIM_UINT32 },
+ { L"ExtendedGrace", CIM_UINT32 },
+ { L"TokenActivationILID", CIM_STRING },
+ { L"TokenActivationILVID", CIM_UINT32 },
+ { L"TokenActivationGrantNumber", CIM_UINT32 },
+ { L"TokenActivationCertificateThumbprint", CIM_STRING },
+ { L"TokenActivationAdditionalInfo", CIM_STRING },
+ { L"TrustedTime", CIM_DATETIME },
};
static const struct column col_sounddevice[] =
{
@@ -816,8 +858,50 @@ struct record_sid
};
struct record_softwarelicensingproduct
{
- int license_is_addon;
- UINT32 license_status;
+ const WCHAR *id;
+ const WCHAR *name;
+ const WCHAR *description;
+ const WCHAR *appid;
+ const WCHAR *processorurl;
+ const WCHAR *machineurl;
+ const WCHAR *productkeyurl;
+ const WCHAR *useLicenseurl;
+ UINT32 licensestatus;
+ UINT32 licensestatusreason;
+ UINT32 graceperiodremaining;
+ const WCHAR *evaluationenddate;
+ const WCHAR *offlineinstallationid;
+ const WCHAR *partialproductkey;
+ const WCHAR *productkeyid;
+ const WCHAR *licensefamily;
+ const WCHAR *licensedependson;
+ BOOLEAN licenseisaddon;
+ UINT32 vlactivationinterval;
+ UINT32 vlrenewalinterval;
+ const WCHAR *keymanagementserviceproductkeyid;
+ const WCHAR *keymanagementservicemachine;
+ UINT32 keymanagementserviceport;
+ const WCHAR *discoveredkeymanagementservicemachinename;
+ UINT32 discoveredkeymanagementservicemachineport;
+ UINT32 isKeymanagementservicemachine;
+ UINT32 keymanagementservicecurrentcount;
+ UINT32 requiredclientcount;
+ UINT32 keymanagementserviceunlicensedrequests;
+ UINT32 keymanagementservicelicensedrequests;
+ UINT32 keymanagementserviceoobgracerequests;
+ UINT32 keymanagementserviceootgracerequests;
+ UINT32 keymanagementservicenongenuinegracerequests;
+ UINT32 keymanagementservicetotalrequests;
+ UINT32 keymanagementservicefailedrequests;
+ UINT32 keymanagementservicenotificationrequests;
+ UINT32 genuinestatus;
+ UINT32 extendedgrace;
+ const WCHAR *tokenactivationilid;
+ UINT32 tokenactivationilvid;
+ UINT32 tokenactivationgrantnumber;
+ const WCHAR *tokenactivationcertificatethumbprint;
+ const WCHAR *tokenactivationadditionalinfo;
+ const WCHAR *trustedtime;
};
struct record_sounddevice
{
@@ -990,7 +1074,9 @@ static const struct record_quickfixengineering data_quickfixengineering[] =
static const struct record_softwarelicensingproduct data_softwarelicensingproduct[] =
{
- { 0, 1 },
+ { L"0", L"Nonexistent", L"first entry into table", L"0", L"localhost", L"localhost", L"localhost",
+ L"localhost", 1, 1, 0, L"1-1-1991", L"0", L"inval", L"0", L"0", NULL, FALSE, 0, 0, L"0", L"localhost",
+ 80, L"localhost", 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"noid", 0, 0, L"none", L"none", L"1-1-1991"},
};
static const struct record_stdregprov data_stdregprov[] =
--
2.27.0
Jan. 25, 2022
Re: [PATCH 3/3] dinput/tests: Add some RegisterDeviceNotificationA and hotplug tests.
by Marvin
Hi,
While running your changed tests, I think I found new failures.
Being a bot and all I'm not very good at pattern recognition, so I might be
wrong, but could you please double-check?
Full results can be found at:
https://testbot.winehq.org/JobDetails.pl?Key=105926
Your paranoid android.
=== w8 (32 bit report) ===
dinput:
joystick8.c:1588: Test failed: 0x500: got lX 0
joystick8.c:1589: Test failed: 0x500: got lY 0
Jan. 25, 2022
[PATCH v10 2/2] ntdll: Properly track refcount with forwarded exports.
by Jinoh Kang
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52094
Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
---
dlls/kernel32/tests/loader.c | 3 --
dlls/ntdll/loader.c | 69 ++++++++++++++++++++----------------
2 files changed, 39 insertions(+), 33 deletions(-)
diff --git a/dlls/kernel32/tests/loader.c b/dlls/kernel32/tests/loader.c
index e9486fe820d..69779becfa0 100644
--- a/dlls/kernel32/tests/loader.c
+++ b/dlls/kernel32/tests/loader.c
@@ -1698,7 +1698,6 @@ static void test_static_forwarded_import_refs(void)
FreeLibrary( iphlpapi );
FreeLibrary( icmp );
- todo_wine
ok( !!GetModuleHandleA( "iphlpapi.dll" ), "iphlpapi.dll unexpectedly unloaded\n" );
ok( !!GetModuleHandleA( "icmp.dll" ), "icmp.dll unexpectedly unloaded\n" );
@@ -1761,9 +1760,7 @@ static void test_dynamic_forwarded_import_refs(void)
FreeLibrary( forward1 );
FreeLibrary( forward2 );
- todo_wine
ok( !!GetModuleHandleA( "forward1.dll" ), "forward1.dll unexpectedly unloaded\n" );
- todo_wine
ok( !!GetModuleHandleA( "forward2.dll" ), "forward2.dll unexpectedly unloaded\n" );
FreeLibrary( forward3 );
diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c
index 362e1c66be5..6852f0280af 100644
--- a/dlls/ntdll/loader.c
+++ b/dlls/ntdll/loader.c
@@ -749,7 +749,7 @@ static FARPROC find_forwarded_export( HMODULE module, const char *forward, LPCWS
{
const IMAGE_EXPORT_DIRECTORY *exports;
DWORD exp_size;
- WINE_MODREF *wm;
+ WINE_MODREF *wm = NULL, *imp;
WCHAR mod_name[256];
const char *end = strrchr(forward, '.');
FARPROC proc = NULL;
@@ -757,30 +757,24 @@ static FARPROC find_forwarded_export( HMODULE module, const char *forward, LPCWS
if (!end) return NULL;
if (build_import_name( mod_name, forward, end - forward )) return NULL;
- if (!(wm = find_basename_module( mod_name )))
+ imp = get_modref( module );
+ TRACE( "delay loading %s for '%s'\n", debugstr_w(mod_name), forward );
+ if (load_dll( load_path, mod_name, 0, &wm, imp->system ) == STATUS_SUCCESS &&
+ !(wm->ldr.Flags & LDR_DONT_RESOLVE_REFS))
{
- WINE_MODREF *imp = get_modref( module );
- TRACE( "delay loading %s for '%s'\n", debugstr_w(mod_name), forward );
- if (load_dll( load_path, mod_name, 0, &wm, imp->system ) == STATUS_SUCCESS &&
- !(wm->ldr.Flags & LDR_DONT_RESOLVE_REFS))
+ if ((imports_fixup_done || !current_modref) &&
+ process_attach( wm->ldr.DdagNode, NULL ) != STATUS_SUCCESS)
{
- if (!imports_fixup_done && current_modref)
- {
- add_module_dependency( current_modref->ldr.DdagNode, wm->ldr.DdagNode );
- }
- else if (process_attach( wm->ldr.DdagNode, NULL ) != STATUS_SUCCESS)
- {
- LdrUnloadDll( wm->ldr.DllBase );
- wm = NULL;
- }
+ LdrUnloadDll( wm->ldr.DllBase );
+ wm = NULL;
}
+ }
- if (!wm)
- {
- ERR( "module not found for forward '%s' used by %s\n",
- forward, debugstr_w(imp->ldr.FullDllName.Buffer) );
- return NULL;
- }
+ if (!wm)
+ {
+ ERR( "module not found for forward '%s' used by %s\n",
+ forward, debugstr_w(imp->ldr.FullDllName.Buffer) );
+ return NULL;
}
if ((exports = RtlImageDirectoryEntryToData( wm->ldr.DllBase, TRUE,
IMAGE_DIRECTORY_ENTRY_EXPORT, &exp_size )))
@@ -800,6 +794,11 @@ static FARPROC find_forwarded_export( HMODULE module, const char *forward, LPCWS
" If you are using builtin %s, try using the native one instead.\n",
forward, debugstr_w(get_modref(module)->ldr.FullDllName.Buffer),
debugstr_w(get_modref(module)->ldr.BaseDllName.Buffer) );
+ if (wm) LdrUnloadDll( wm->ldr.DllBase );
+ }
+ else if (current_modref)
+ {
+ add_module_dependency( current_modref->ldr.DdagNode, wm->ldr.DdagNode );
}
return proc;
}
@@ -1844,21 +1843,31 @@ NTSTATUS WINAPI LdrGetProcedureAddress(HMODULE module, const ANSI_STRING *name,
IMAGE_EXPORT_DIRECTORY *exports;
DWORD exp_size;
NTSTATUS ret = STATUS_PROCEDURE_NOT_FOUND;
+ WINE_MODREF *prev, *wm;
RtlEnterCriticalSection( &loader_section );
- /* check if the module itself is invalid to return the proper error */
- if (!get_modref( module )) ret = STATUS_DLL_NOT_FOUND;
- else if ((exports = RtlImageDirectoryEntryToData( module, TRUE,
- IMAGE_DIRECTORY_ENTRY_EXPORT, &exp_size )))
+ wm = get_modref( module );
+ if (!wm) ret = STATUS_DLL_NOT_FOUND;
+ else
{
- void *proc = name ? find_named_export( module, exports, exp_size, name->Buffer, -1, NULL )
- : find_ordinal_export( module, exports, exp_size, ord - exports->Base, NULL );
- if (proc)
+ prev = current_modref;
+ current_modref = wm;
+
+ /* check if the module itself is invalid to return the proper error */
+ if ((exports = RtlImageDirectoryEntryToData( module, TRUE,
+ IMAGE_DIRECTORY_ENTRY_EXPORT, &exp_size )))
{
- *address = proc;
- ret = STATUS_SUCCESS;
+ void *proc = name ? find_named_export( module, exports, exp_size, name->Buffer, -1, NULL )
+ : find_ordinal_export( module, exports, exp_size, ord - exports->Base, NULL );
+ if (proc)
+ {
+ *address = proc;
+ ret = STATUS_SUCCESS;
+ }
}
+
+ current_modref = prev;
}
RtlLeaveCriticalSection( &loader_section );
--
2.31.1
Jan. 25, 2022
[PATCH v10 1/2] kernel32/tests: Test module refcounting with forwarded exports.
by Jinoh Kang
Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
---
Notes:
v3 -> v4:
- iatgas.h
- LLVM(Clang), ARM, ARM64 support
- Use __ASM_NAME macro
- Don't fail test on MSVC
- Don't end asm macros with "\n\t"
v4 -> v5:
- iatgas.h
- mark idata sections as RO
- loader.c
- test for forward export itself
v5 -> v6:
- loader.c
- Fix compilation warning in format string
v6 -> v7:
- forward4.c
- fix building without MinGW
v7 -> v8:
- Also do GetProcAddress for forwarded ordinal exports
- forward[1-3].c
- Call DisableThreadLibraryCalls in DllMain
- forward4.c: removed
- iatgas.h: removed
- sforward.c: new file
- loader.c
- test static forwarded import using shlwapi -> userenv forward
v8 -> v9:
- Test with iprop (documented as an ole32 forwarder in MSDN [1])
v9 -> v10:
- Test with icmp instead (documented as an iphlpapi forwarder in MSDN [2])
[1] https://docs.microsoft.com/en-us/windows/win32/api/coml2api/nf-coml2api-stg…
[2] https://docs.microsoft.com/en-us/windows/win32/api/icmpapi/nf-icmpapi-icmpc…
dlls/icmp/Makefile.in | 1 +
dlls/kernel32/tests/Makefile.in | 14 ++-
dlls/kernel32/tests/forward1.c | 19 ++++
dlls/kernel32/tests/forward1.spec | 2 +
dlls/kernel32/tests/forward2.c | 9 ++
dlls/kernel32/tests/forward2.spec | 2 +
dlls/kernel32/tests/forward3.c | 9 ++
dlls/kernel32/tests/forward3.spec | 2 +
dlls/kernel32/tests/loader.c | 182 ++++++++++++++++++++++++++++++
dlls/kernel32/tests/sforward.c | 18 +++
dlls/kernel32/tests/sforward.spec | 1 +
11 files changed, 257 insertions(+), 2 deletions(-)
create mode 100644 dlls/kernel32/tests/forward1.c
create mode 100644 dlls/kernel32/tests/forward1.spec
create mode 100644 dlls/kernel32/tests/forward2.c
create mode 100644 dlls/kernel32/tests/forward2.spec
create mode 100644 dlls/kernel32/tests/forward3.c
create mode 100644 dlls/kernel32/tests/forward3.spec
create mode 100644 dlls/kernel32/tests/sforward.c
create mode 100644 dlls/kernel32/tests/sforward.spec
diff --git a/dlls/icmp/Makefile.in b/dlls/icmp/Makefile.in
index 1ecf6459c0f..b1d3c11680c 100644
--- a/dlls/icmp/Makefile.in
+++ b/dlls/icmp/Makefile.in
@@ -1 +1,2 @@
MODULE = icmp.dll
+IMPORTLIB = icmp
diff --git a/dlls/kernel32/tests/Makefile.in b/dlls/kernel32/tests/Makefile.in
index e9516603ce9..67775e78a83 100644
--- a/dlls/kernel32/tests/Makefile.in
+++ b/dlls/kernel32/tests/Makefile.in
@@ -1,5 +1,7 @@
TESTDLL = kernel32.dll
-IMPORTS = user32 advapi32
+
+# icmp is for testing export forwarding (to iphlpapi)
+IMPORTS = user32 advapi32 icmp
SOURCES = \
actctx.c \
@@ -37,4 +39,12 @@ SOURCES = \
toolhelp.c \
version.c \
virtual.c \
- volume.c
+ volume.c \
+ forward1.c \
+ forward1.spec \
+ forward2.c \
+ forward2.spec \
+ forward3.c \
+ forward3.spec \
+ sforward.c \
+ sforward.spec
diff --git a/dlls/kernel32/tests/forward1.c b/dlls/kernel32/tests/forward1.c
new file mode 100644
index 00000000000..6419d95eaea
--- /dev/null
+++ b/dlls/kernel32/tests/forward1.c
@@ -0,0 +1,19 @@
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+BOOL WINAPI DllMain(HINSTANCE instance_new, DWORD reason, LPVOID reserved)
+{
+ if (reason == DLL_PROCESS_ATTACH)
+ DisableThreadLibraryCalls( instance_new );
+ return TRUE;
+}
+
+unsigned long forward_test_func(void)
+{
+ return 0x00005678UL;
+}
+
+unsigned long forward_test_func2(void)
+{
+ return 0x12340000UL;
+}
diff --git a/dlls/kernel32/tests/forward1.spec b/dlls/kernel32/tests/forward1.spec
new file mode 100644
index 00000000000..bf19fa7e011
--- /dev/null
+++ b/dlls/kernel32/tests/forward1.spec
@@ -0,0 +1,2 @@
+1 cdecl forward_test_func()
+2 cdecl -noname forward_test_func2()
diff --git a/dlls/kernel32/tests/forward2.c b/dlls/kernel32/tests/forward2.c
new file mode 100644
index 00000000000..d1e77f45f3c
--- /dev/null
+++ b/dlls/kernel32/tests/forward2.c
@@ -0,0 +1,9 @@
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+BOOL WINAPI DllMain(HINSTANCE instance_new, DWORD reason, LPVOID reserved)
+{
+ if (reason == DLL_PROCESS_ATTACH)
+ DisableThreadLibraryCalls( instance_new );
+ return TRUE;
+}
diff --git a/dlls/kernel32/tests/forward2.spec b/dlls/kernel32/tests/forward2.spec
new file mode 100644
index 00000000000..374156d8d06
--- /dev/null
+++ b/dlls/kernel32/tests/forward2.spec
@@ -0,0 +1,2 @@
+1 cdecl forward_test_func() forward1.forward_test_func
+2 cdecl -noname forward_test_func2() forward1.#2
diff --git a/dlls/kernel32/tests/forward3.c b/dlls/kernel32/tests/forward3.c
new file mode 100644
index 00000000000..d1e77f45f3c
--- /dev/null
+++ b/dlls/kernel32/tests/forward3.c
@@ -0,0 +1,9 @@
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+BOOL WINAPI DllMain(HINSTANCE instance_new, DWORD reason, LPVOID reserved)
+{
+ if (reason == DLL_PROCESS_ATTACH)
+ DisableThreadLibraryCalls( instance_new );
+ return TRUE;
+}
diff --git a/dlls/kernel32/tests/forward3.spec b/dlls/kernel32/tests/forward3.spec
new file mode 100644
index 00000000000..31d019aa071
--- /dev/null
+++ b/dlls/kernel32/tests/forward3.spec
@@ -0,0 +1,2 @@
+1 cdecl forward_test_func() forward2.forward_test_func
+2 cdecl -noname forward_test_func2() forward2.#2
diff --git a/dlls/kernel32/tests/loader.c b/dlls/kernel32/tests/loader.c
index f990d632f73..e9486fe820d 100644
--- a/dlls/kernel32/tests/loader.c
+++ b/dlls/kernel32/tests/loader.c
@@ -1642,6 +1642,185 @@ static void test_ImportDescriptors(void)
}
}
+static void extract_resource(const char *name, const char *type, const char *path)
+{
+ DWORD written;
+ HANDLE file;
+ HRSRC res;
+ void *ptr;
+
+ file = CreateFileA(path, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
+ ok(file != INVALID_HANDLE_VALUE, "file creation failed, at %s, error %d\n", path, GetLastError());
+
+ res = FindResourceA(NULL, name, type);
+ ok( res != 0, "couldn't find resource\n" );
+ ptr = LockResource( LoadResource( GetModuleHandleA(NULL), res ));
+ WriteFile( file, ptr, SizeofResource( GetModuleHandleA(NULL), res ), &written, NULL );
+ ok( written == SizeofResource( GetModuleHandleA(NULL), res ), "couldn't write resource\n" );
+ CloseHandle( file );
+}
+
+static void test_static_forwarded_import_refs(void)
+{
+ CHAR temp_path[MAX_PATH], dir_path[MAX_PATH], sforward_path[MAX_PATH];
+ HMODULE iphlpapi, icmp, sforward;
+ FARPROC test_func_stub;
+
+ if (GetModuleHandleA( "iphlpapi.dll" ))
+ {
+ skip("cannot test since iphlpapi.dll is already loaded\n");
+ return;
+ }
+ if (GetModuleHandleA( "icmp.dll" ))
+ {
+ skip("cannot test since icmp.dll is already loaded\n");
+ return;
+ }
+
+ GetTempPathA( ARRAY_SIZE(temp_path), temp_path );
+ GetTempFileNameA( temp_path, "ldr", GetTickCount() | 1UL, dir_path );
+ ok( CreateDirectoryA( dir_path, NULL ), "failed to create dir %s, error %u\n",
+ dir_path, GetLastError() );
+
+ snprintf( sforward_path, MAX_PATH, "%s\\sforward.dll", dir_path );
+ extract_resource( "sforward.dll", "TESTDLL", sforward_path );
+
+ iphlpapi = LoadLibraryA( "iphlpapi.dll" );
+ ok( !!iphlpapi, "couldn't find iphlpapi.dll: %u\n", GetLastError() );
+ icmp = LoadLibraryA( "icmp.dll" );
+ ok( !!icmp, "couldn't find icmp.dll: %u\n", GetLastError() );
+ sforward = LoadLibraryA( sforward_path );
+ ok( !!sforward, "couldn't find %s: %u\n", sforward_path, GetLastError() );
+
+ test_func_stub = GetProcAddress( sforward, "test_func_stub" );
+ ok( !!test_func_stub, "sforward!test_func_stub not found\n" );
+
+ FreeLibrary( iphlpapi );
+ FreeLibrary( icmp );
+
+ todo_wine
+ ok( !!GetModuleHandleA( "iphlpapi.dll" ), "iphlpapi.dll unexpectedly unloaded\n" );
+ ok( !!GetModuleHandleA( "icmp.dll" ), "icmp.dll unexpectedly unloaded\n" );
+
+ FreeLibrary( sforward );
+
+ ok( !GetModuleHandleA( "iphlpapi.dll" ), "iphlpapi.dll unexpectedly kept open\n" );
+ ok( !GetModuleHandleA( "icmp.dll" ), "icmp.dll unexpectedly kept open\n" );
+ ok( !GetModuleHandleA( "sforward.dll" ), "sforward.dll unexpectedly kept open\n" );
+
+ DeleteFileA( sforward_path );
+ RemoveDirectoryA( dir_path );
+}
+
+static void test_dynamic_forwarded_import_refs(void)
+{
+ CHAR temp_path[MAX_PATH], dir_path[MAX_PATH];
+ CHAR forward1_path[MAX_PATH];
+ CHAR forward2_path[MAX_PATH];
+ CHAR forward3_path[MAX_PATH];
+ HMODULE forward1, forward2, forward3;
+ FARPROC proc1, proc2, proc3, oproc1, oproc2, oproc3;
+
+ GetTempPathA( ARRAY_SIZE(temp_path), temp_path );
+ GetTempFileNameA( temp_path, "ldr", GetTickCount() | 1UL, dir_path );
+ ok( CreateDirectoryA( dir_path, NULL ), "failed to create dir %s, error %u\n",
+ dir_path, GetLastError() );
+
+ snprintf( forward1_path, MAX_PATH, "%s\\forward1.dll", dir_path );
+ snprintf( forward2_path, MAX_PATH, "%s\\forward2.dll", dir_path );
+ snprintf( forward3_path, MAX_PATH, "%s\\forward3.dll", dir_path );
+ extract_resource( "forward1.dll", "TESTDLL", forward1_path );
+ extract_resource( "forward2.dll", "TESTDLL", forward2_path );
+ extract_resource( "forward3.dll", "TESTDLL", forward3_path );
+
+ forward1 = LoadLibraryA( forward1_path );
+ ok( !!forward1, "couldn't find %s: %u\n", forward1_path, GetLastError() );
+ forward2 = LoadLibraryA( forward2_path );
+ ok( !!forward2, "couldn't find %s: %u\n", forward2_path, GetLastError() );
+ forward3 = LoadLibraryA( forward3_path );
+ ok( !!forward3, "couldn't find %s: %u\n", forward3_path, GetLastError() );
+
+ proc1 = GetProcAddress(forward1, "forward_test_func");
+ ok( !!proc1, "cannot resolve forward1!forward_test_func\n");
+ proc2 = GetProcAddress(forward2, "forward_test_func");
+ ok( !!proc2, "cannot resolve forward2!forward_test_func\n");
+ proc3 = GetProcAddress(forward3, "forward_test_func");
+ ok( !!proc3, "cannot resolve forward3!forward_test_func\n");
+ ok( proc1 == proc3, "forward1!forward_test_func is not equal to forward3!forward_test_func\n");
+ ok( proc2 == proc3, "forward2!forward_test_func is not equal to forward3!forward_test_func\n");
+
+ oproc1 = GetProcAddress(forward1, (LPSTR)2);
+ ok( !!oproc1, "cannot resolve forward1!#2 (forward_test_func2)\n");
+ oproc2 = GetProcAddress(forward2, (LPSTR)2);
+ ok( !!oproc2, "cannot resolve forward2!#2 (forward_test_func2)\n");
+ oproc3 = GetProcAddress(forward3, (LPSTR)2);
+ ok( !!oproc3, "cannot resolve forward3!#2 (forward_test_func2)\n");
+ ok( oproc1 == oproc3, "forward1!forward_test_func2 is not equal to forward3!forward_test_func2\n");
+ ok( oproc2 == oproc3, "forward2!forward_test_func2 is not equal to forward3!forward_test_func2\n");
+
+ FreeLibrary( forward1 );
+ FreeLibrary( forward2 );
+
+ todo_wine
+ ok( !!GetModuleHandleA( "forward1.dll" ), "forward1.dll unexpectedly unloaded\n" );
+ todo_wine
+ ok( !!GetModuleHandleA( "forward2.dll" ), "forward2.dll unexpectedly unloaded\n" );
+
+ FreeLibrary( forward3 );
+
+ ok( !GetModuleHandleA( "forward1.dll" ), "forward1.dll unexpectedly kept open\n" );
+ ok( !GetModuleHandleA( "forward2.dll" ), "forward2.dll unexpectedly kept open\n" );
+ ok( !GetModuleHandleA( "forward3.dll" ), "forward3.dll unexpectedly kept open\n" );
+
+ DeleteFileA( forward1_path );
+ DeleteFileA( forward2_path );
+ DeleteFileA( forward3_path );
+ RemoveDirectoryA( dir_path );
+}
+
+static void test_dynamic_forward_export_norefs(void)
+{
+ CHAR temp_path[MAX_PATH], dir_path[MAX_PATH];
+ CHAR forward1_path[MAX_PATH];
+ CHAR forward2_path[MAX_PATH];
+ CHAR forward3_path[MAX_PATH];
+ HMODULE forward1, forward2, forward3;
+
+ GetTempPathA( ARRAY_SIZE(temp_path), temp_path );
+ GetTempFileNameA( temp_path, "ldr", GetTickCount() | 1UL, dir_path );
+ ok( CreateDirectoryA( dir_path, NULL ), "failed to create dir %s, error %u\n",
+ dir_path, GetLastError() );
+
+ snprintf( forward1_path, MAX_PATH, "%s\\forward1.dll", dir_path );
+ snprintf( forward2_path, MAX_PATH, "%s\\forward2.dll", dir_path );
+ snprintf( forward3_path, MAX_PATH, "%s\\forward3.dll", dir_path );
+ extract_resource( "forward1.dll", "TESTDLL", forward1_path );
+ extract_resource( "forward2.dll", "TESTDLL", forward2_path );
+ extract_resource( "forward3.dll", "TESTDLL", forward3_path );
+
+ forward1 = LoadLibraryA( forward1_path );
+ ok( !!forward1, "couldn't find %s: %u\n", forward1_path, GetLastError() );
+ forward2 = LoadLibraryA( forward2_path );
+ ok( !!forward2, "couldn't find %s: %u\n", forward2_path, GetLastError() );
+ forward3 = LoadLibraryA( forward3_path );
+ ok( !!forward3, "couldn't find %s: %u\n", forward3_path, GetLastError() );
+
+ FreeLibrary( forward1 );
+ FreeLibrary( forward3 );
+
+ ok( !GetModuleHandleA( "forward1.dll" ), "forward1.dll unexpectedly kept open\n" );
+ ok( !GetModuleHandleA( "forward3.dll" ), "forward3.dll unexpectedly kept open\n" );
+
+ FreeLibrary( forward2 );
+
+ ok( !GetModuleHandleA( "forward2.dll" ), "forward2.dll unexpectedly kept open\n" );
+
+ DeleteFileA( forward1_path );
+ DeleteFileA( forward2_path );
+ DeleteFileA( forward3_path );
+ RemoveDirectoryA( dir_path );
+}
+
static void test_image_mapping(const char *dll_name, DWORD scn_page_access, BOOL is_dll)
{
HANDLE hfile, hmap;
@@ -4119,9 +4298,12 @@ START_TEST(loader)
return;
}
+ test_static_forwarded_import_refs(); /* Must be first; other tests may load iphlpapi.dll */
test_filenames();
test_ResolveDelayLoadedAPI();
test_ImportDescriptors();
+ test_dynamic_forwarded_import_refs();
+ test_dynamic_forward_export_norefs();
test_section_access();
test_import_resolution();
test_ExitProcess();
diff --git a/dlls/kernel32/tests/sforward.c b/dlls/kernel32/tests/sforward.c
new file mode 100644
index 00000000000..e632f69efb1
--- /dev/null
+++ b/dlls/kernel32/tests/sforward.c
@@ -0,0 +1,18 @@
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <ws2tcpip.h>
+#include <iphlpapi.h>
+#include <icmpapi.h>
+
+void test_func_stub(void)
+{
+ HANDLE file = IcmpCreateFile();
+ if (file != INVALID_HANDLE_VALUE) IcmpCloseHandle( file );
+}
+
+BOOL WINAPI DllMain(HINSTANCE instance_new, DWORD reason, LPVOID reserved)
+{
+ if (reason == DLL_PROCESS_ATTACH)
+ DisableThreadLibraryCalls( instance_new );
+ return TRUE;
+}
diff --git a/dlls/kernel32/tests/sforward.spec b/dlls/kernel32/tests/sforward.spec
new file mode 100644
index 00000000000..cb6d4add796
--- /dev/null
+++ b/dlls/kernel32/tests/sforward.spec
@@ -0,0 +1 @@
+@ cdecl test_func_stub()
--
2.31.1
Jan. 25, 2022