Skip to content

Commit

Permalink
More precise present syncing D3D
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisd1100 committed Dec 29, 2023
1 parent 09ccf9d commit a8f86fc
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 7 deletions.
22 changes: 17 additions & 5 deletions src/windows/gfx/d3d11-ctx.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ GFX_CTX_PROTOTYPES(_d3d11_)
#include <dxgi1_5.h>

#include "gfx/sync.h"
#include "dxgi-sync.h"

#define DXGI_FATAL(e) ( \
(e) == DXGI_ERROR_DEVICE_REMOVED || \
Expand All @@ -22,6 +23,8 @@ GFX_CTX_PROTOTYPES(_d3d11_)
struct d3d11_ctx {
HWND hwnd;
struct sync sync;
struct dxgi_sync *dxgi_sync;
int64_t pcount;
uint32_t width;
uint32_t height;
ID3D11Device *device;
Expand Down Expand Up @@ -213,6 +216,7 @@ void mty_d3d11_ctx_destroy(struct gfx_ctx **gfx_ctx)

struct d3d11_ctx *ctx = (struct d3d11_ctx *) *gfx_ctx;

dxgi_sync_destroy(&ctx->dxgi_sync);
d3d11_ctx_free(ctx);

MTY_Free(ctx);
Expand Down Expand Up @@ -301,22 +305,30 @@ void mty_d3d11_ctx_set_sync_interval(struct gfx_ctx *gfx_ctx, uint32_t interval)
struct d3d11_ctx *ctx = (struct d3d11_ctx *) gfx_ctx;

sync_set_interval(&ctx->sync, interval);

if (interval > 100 && !ctx->dxgi_sync)
ctx->dxgi_sync = dxgi_sync_create(ctx->hwnd);
}

void mty_d3d11_ctx_present(struct gfx_ctx *gfx_ctx)
{
struct d3d11_ctx *ctx = (struct d3d11_ctx *) gfx_ctx;

if (ctx->back_buffer) {
UINT interval = sync_next_interval(&ctx->sync);
int64_t interval = sync_next_interval(&ctx->sync);
UINT flags = interval > 0 ? 0 : DXGI_PRESENT_ALLOW_TEARING;

if (interval == 0 && !(ctx->flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING)) {
interval = 1;
if (interval == 0 && !(ctx->flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING))
flags = 0;
}

HRESULT e = IDXGISwapChain2_Present(ctx->swap_chain2, interval, flags);
int64_t elapsed = dxgi_sync_get_count(ctx->dxgi_sync) - ctx->pcount;

for (int64_t x = 0; x < interval - elapsed; x++)
dxgi_sync_wait(ctx->dxgi_sync, D3D11_CTX_WAIT);

HRESULT e = IDXGISwapChain2_Present(ctx->swap_chain2, 0, flags);

ctx->pcount = dxgi_sync_get_count(ctx->dxgi_sync);

ID3D11RenderTargetView_Release(ctx->back_buffer);
ctx->back_buffer = NULL;
Expand Down
18 changes: 16 additions & 2 deletions src/windows/gfx/d3d12-ctx.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ GFX_CTX_PROTOTYPES(_d3d12_)
#include <dxgi1_4.h>

#include "gfx/sync.h"
#include "dxgi-sync.h"

#define DXGI_FATAL(e) ( \
(e) == DXGI_ERROR_DEVICE_REMOVED || \
Expand All @@ -34,6 +35,8 @@ struct d3d12_ctx_buffer {
struct d3d12_ctx {
HWND hwnd;
struct sync sync;
struct dxgi_sync *dxgi_sync;
int64_t pcount;
uint32_t width;
uint32_t height;

Expand Down Expand Up @@ -308,6 +311,7 @@ void mty_d3d12_ctx_destroy(struct gfx_ctx **gfx_ctx)

struct d3d12_ctx *ctx = (struct d3d12_ctx *) *gfx_ctx;

dxgi_sync_destroy(&ctx->dxgi_sync);
d3d12_core_free(&ctx->core);

MTY_Free(ctx);
Expand Down Expand Up @@ -406,6 +410,9 @@ void mty_d3d12_ctx_set_sync_interval(struct gfx_ctx *gfx_ctx, uint32_t interval)
struct d3d12_ctx *ctx = (struct d3d12_ctx *) gfx_ctx;

sync_set_interval(&ctx->sync, interval);

if (interval > 100 && !ctx->dxgi_sync)
ctx->dxgi_sync = dxgi_sync_create(ctx->hwnd);
}

void mty_d3d12_ctx_present(struct gfx_ctx *gfx_ctx)
Expand Down Expand Up @@ -441,10 +448,17 @@ void mty_d3d12_ctx_present(struct gfx_ctx *gfx_ctx)
ID3D12CommandQueue_ExecuteCommandLists(core->cq, 1, &cl);
ID3D12CommandList_Release(cl);

UINT interval = sync_next_interval(&ctx->sync);
int64_t interval = sync_next_interval(&ctx->sync);
UINT flags = interval > 0 ? 0 : DXGI_PRESENT_ALLOW_TEARING;

e = IDXGISwapChain3_Present(core->swap_chain3, interval, flags);
int64_t elapsed = dxgi_sync_get_count(ctx->dxgi_sync) - ctx->pcount;

for (int64_t x = 0; x < interval - elapsed; x++)
dxgi_sync_wait(ctx->dxgi_sync, D3D12_CTX_WAIT);

e = IDXGISwapChain3_Present(core->swap_chain3, 0, flags);

ctx->pcount = dxgi_sync_get_count(ctx->dxgi_sync);

if (DXGI_FATAL(e)) {
MTY_Log("'IDXGISwapChain3_Present' failed with HRESULT 0x%X", e);
Expand Down
119 changes: 119 additions & 0 deletions src/windows/gfx/dxgi-sync.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT License was not distributed with this file,
// You can obtain one at https://spdx.org/licenses/MIT.html.

struct dxgi_sync {
HANDLE event;
MTY_Thread *thread;
MTY_Atomic32 done;
MTY_Atomic64 count;
};

static void dxgi_sync_destroy(struct dxgi_sync **dxgi_sync)
{
if (!dxgi_sync || !*dxgi_sync)
return;

struct dxgi_sync *ctx = *dxgi_sync;

MTY_Atomic32Set(&ctx->done, 1);
MTY_ThreadDestroy(&ctx->thread);

if (ctx->event)
CloseHandle(ctx->event);

MTY_Free(ctx);
*dxgi_sync = NULL;
}

static void *dxgi_sync_thread(void *opaque)
{
struct dxgi_sync *ctx = opaque;

IDXGIFactory2 *factory2 = NULL;
IDXGIAdapter *adapter = NULL;
IDXGIOutput *output = NULL;

HRESULT e = CreateDXGIFactory2(0, &IID_IDXGIFactory2, &factory2);
if (e != S_OK) {
MTY_Log("'CreateDXGIFactory2' failed with HRESULT 0x%X", e);
goto except;
}

e = IDXGIFactory2_EnumAdapters(factory2, 0, &adapter);
if (e != S_OK) {
MTY_Log("'IDXGIFactory2_EnumAdapters' failed with HRESULT 0x%X", e);
goto except;
}

e = IDXGIAdapter_EnumOutputs(adapter, 0, &output);
if (e != S_OK) {
MTY_Log("'IDXGIAdapter_EnumOutputs' failed with HRESULT 0x%X", e);
goto except;
}

while (MTY_Atomic32Get(&ctx->done) == 0) {
e = IDXGIOutput_WaitForVBlank(output);
if (e != S_OK) {
MTY_Log("'IDXGIOutput_WaitForVBlank' failed with HRESULT 0x%X", e);
break;
}

MTY_Atomic64Add(&ctx->count, 1);

SetEvent(ctx->event);
ResetEvent(ctx->event);
}

except:

if (output)
IDXGIOutput_Release(output);

if (adapter)
IDXGIAdapter_Release(adapter);

if (factory2)
IDXGIFactory2_Release(factory2);

return NULL;
}

static struct dxgi_sync *dxgi_sync_create(HWND hwnd)
{
struct dxgi_sync *ctx = MTY_Alloc(1, sizeof(struct dxgi_sync));

ctx->event = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!ctx->event) {
MTY_Log("'CreateEvent' failed with error 0x%X", GetLastError());
goto except;
}

ctx->thread = MTY_ThreadCreate(dxgi_sync_thread, ctx);

except:

if (!ctx->event)
dxgi_sync_destroy(&ctx);

return ctx;
}

static int64_t dxgi_sync_get_count(struct dxgi_sync *ctx)
{
if (!ctx)
return 0;

return MTY_Atomic64Get(&ctx->count);
}

static void dxgi_sync_wait(struct dxgi_sync *ctx, DWORD timeout)
{
if (!ctx)
return;

DWORD e = WaitForSingleObjectEx(ctx->event, timeout, FALSE);

if (e != WAIT_OBJECT_0)
MTY_Log("'WaitForSingleObjectEx' failed with error 0x%X", e);
}

0 comments on commit a8f86fc

Please sign in to comment.