diff options
| author | Tomasz Kramkowski <tomasz@kramkow.ski> | 2026-11-03 20:01:42 +0000 |
|---|---|---|
| committer | Tomasz Kramkowski <tomasz@kramkow.ski> | 2026-11-03 20:01:42 +0000 |
| commit | 098bd37f355a77abbb5751fc55a91f7ebe74cc6b (patch) | |
| tree | d3bc93cb427276a4936d79903b62e156d5c538e7 /steamvr/image-wait | |
| parent | 64c349eb1dc0fde65fd4e6ddf525faa0a4c64a91 (diff) | |
| download | steamvr-patches-098bd37f355a77abbb5751fc55a91f7ebe74cc6b.tar.gz steamvr-patches-098bd37f355a77abbb5751fc55a91f7ebe74cc6b.tar.xz steamvr-patches-098bd37f355a77abbb5751fc55a91f7ebe74cc6b.zip | |
use patch generator to build patchers
Diffstat (limited to 'steamvr/image-wait')
| -rw-r--r-- | steamvr/image-wait/image_wait.c | 148 | ||||
| -rw-r--r-- | steamvr/image-wait/trampoline.S | 26 |
2 files changed, 174 insertions, 0 deletions
diff --git a/steamvr/image-wait/image_wait.c b/steamvr/image-wait/image_wait.c new file mode 100644 index 0000000..e8112c3 --- /dev/null +++ b/steamvr/image-wait/image_wait.c @@ -0,0 +1,148 @@ +// SPDX-FileCopyrightText: 2026 Tomasz Kramkowski <tomasz@kramkow.ski> +// SPDX-License-Identifier: CC0-1.0 + +#include <assert.h> +#include <stddef.h> +#include <stdint.h> + +#define ASSERT_SIZE(type, expected) \ + static_assert(sizeof (type) == (expected), #type " size must be " #expected) +#define ASSERT_OFFSET(type, field, expected) \ + static_assert( \ + offsetof(type, field) == (expected), \ + #type "." #field " offset must be " #expected \ + ) + +ASSERT_SIZE(void *, 8); + +enum { VK_PIPELINE_STAGE_ALL_COMMANDS_BIT = 0x00010000 }; + +typedef struct { + uintptr_t semaphore; + uint64_t value; +} SemaphoreEntry; +ASSERT_SIZE(SemaphoreEntry, 16); +ASSERT_OFFSET(SemaphoreEntry, value, 8); + +// vr::IVRSharedResource +struct SharedResource { + const struct SharedResourceVtable *vtable; +}; + +typedef struct Semaphore *GetSemaphore(struct SharedResource *); + +struct SharedResourceVtable { + unsigned char unknown_000[0x30]; + GetSemaphore *get_semaphore; +}; +ASSERT_OFFSET(struct SharedResourceVtable, get_semaphore, 0x30); + +typedef uint64_t SemaphoreGetU64(struct Semaphore *); + +struct SemaphoreVtable { + unsigned char unknown_000[0x40]; + SemaphoreGetU64 *get_handle; + unsigned char unknown_048[0x70 - 0x48]; + SemaphoreGetU64 *get_value; +}; +ASSERT_OFFSET(struct SemaphoreVtable, get_handle, 0x40); +ASSERT_OFFSET(struct SemaphoreVtable, get_value, 0x70); + +// vr::IVRSharedSemaphore +struct Semaphore { + const struct SemaphoreVtable *vtable; +}; + +// vr::IVRSharedImage +struct Image { + const void *vtable; +}; + +struct Texture { + unsigned char unknown_000[0x48]; + struct Image *image; +}; +ASSERT_OFFSET(struct Texture, image, 0x48); + +struct Batch { + unsigned char unknown_000[0xf0]; + SemaphoreEntry *waits; + SemaphoreEntry *waits_end; + unsigned char unknown_100[0x140 - 0x100]; + uint32_t *wait_stages; + unsigned char unknown_148[0x398 - 0x148]; + SemaphoreEntry *signals; + SemaphoreEntry *signals_end; +}; +ASSERT_OFFSET(struct Batch, waits, 0xf0); +ASSERT_OFFSET(struct Batch, waits_end, 0xf8); +ASSERT_OFFSET(struct Batch, wait_stages, 0x140); +ASSERT_OFFSET(struct Batch, signals, 0x398); +ASSERT_OFFSET(struct Batch, signals_end, 0x3a0); + +struct RendererContext { + unsigned char unknown_000[8]; + struct Batch *batch; +}; +ASSERT_OFFSET(struct RendererContext, batch, 8); + +// CFacetVRRenderer +struct Renderer { + unsigned char unknown_000[0x12b0]; + struct RendererContext *context; +}; +ASSERT_OFFSET(struct Renderer, context, 0x12b0); + +extern void append_wait( + struct Batch *batch, uintptr_t semaphore, uint32_t stages, uint64_t value +) __attribute__((visibility("hidden"))); + +// The patch calls this function at the beginning of a method in the "Facet" +// `renderer` which is called, among other things, on `texture`s that the current +// submission batch will use as shader inputs (e.g. eye textures). +void register_image_wait(struct Renderer *renderer, struct Texture *texture) +{ + // First we need the completion semaphore for the texture. + struct Image *image = texture->image; + if (!image) return; + + // The vbase offset entry for converting `vr::IVRSharedImage *` to + // `vr::IVRSharedResource *` is located at `image->vtable - 0x50`. + ptrdiff_t resource_offset = + *(const ptrdiff_t *)((const char *)image->vtable - 0x50); + struct SharedResource *resource = (void *)((char *)image + resource_offset); + + struct Semaphore *semaphore = resource->vtable->get_semaphore(resource); + if (!semaphore) return; + + uintptr_t handle = semaphore->vtable->get_handle(semaphore); + if (!handle) return; + + // The current batch that is being prepared, and that may need the image + // semaphore as a dependency. + struct Batch *batch = renderer->context->batch; + + // If the texture we got called on has the same completion semaphore as one + // of the completion signals of this batch, then making the batch depend on + // the semaphore could create a loop, and that would be bad. + for (SemaphoreEntry *p = batch->signals; p != batch->signals_end; p++) { + if (p->semaphore == handle) return; + } + + // If there's already a wait on this texture's semaphore, ensure it will + // block all work in this batch and that the value is at least as high as the + // semaphore value required for this texture to be ready. + uint64_t value = semaphore->vtable->get_value(semaphore); + uint32_t *stages = batch->wait_stages; + for (SemaphoreEntry *p = batch->waits; p != batch->waits_end; + p++, stages++) { + if (p->semaphore != handle) continue; + + if (p->value < value) p->value = value; + *stages |= VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + return; + } + + // Otherwise append a wait on this texture's completion semaphore. + append_wait(batch, handle, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, value); +} diff --git a/steamvr/image-wait/trampoline.S b/steamvr/image-wait/trampoline.S new file mode 100644 index 0000000..6c7dbc4 --- /dev/null +++ b/steamvr/image-wait/trampoline.S @@ -0,0 +1,26 @@ +/* SPDX-FileCopyrightText: 2026 Tomasz Kramkowski <tomasz@kramkow.ski> + * SPDX-License-Identifier: CC0-1.0 + */ + +.intel_syntax noprefix +.section .text.entry,"ax",@progbits +.global input_wait +input_wait: + push rdi + push rsi + push rdx + push rcx + sub rsp, 8 + call register_image_wait + add rsp, 8 + pop rcx + pop rdx + pop rsi + pop rdi + + # Replay the three displaced instructions, then resume the original function. + push rbp + mov rax, rsi + mov r9d, edx + jmp resume_transition +.section .note.GNU-stack,"",@progbits |
