-
Notifications
You must be signed in to change notification settings - Fork 4
Robust GPU error recovery [Windows] #99
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: stable
Are you sure you want to change the base?
Changes from all commits
223e2b8
b63b8f5
8a3b5fa
33f73bf
be4ad33
c014061
b4f1412
d2b88a9
1af162e
c7c3d66
3066a96
6473bde
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -73,6 +73,7 @@ static void gfx_set_device(struct window_common *cmn, MTY_Device *device) | |
| GFX_UI_API[cmn->api].destroy(&cmn->gfx_ui, cmn->device); | ||
|
|
||
| cmn->device = device; | ||
| cmn->device_changed = true; | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -96,8 +97,12 @@ static bool gfx_begin_ui(struct window_common *cmn, MTY_Device *device) | |
| if (!cmn->gfx_ui) | ||
| cmn->gfx_ui = GFX_UI_API[cmn->api].create(device); | ||
|
|
||
| if (!cmn->ui_textures) | ||
| if (!cmn->gfx_ui) { | ||
| MTY_HashDestroy(&cmn->ui_textures, NULL); | ||
|
|
||
| } else if (!cmn->ui_textures) { | ||
| cmn->ui_textures = MTY_HashCreate(0); | ||
| } | ||
|
|
||
| return cmn->gfx_ui != NULL; | ||
| } | ||
|
|
@@ -213,18 +218,28 @@ bool MTY_WindowSetUITexture(MTY_App *app, MTY_Window window, uint32_t id, const | |
| return false; | ||
|
|
||
| MTY_Device *device = gfx_ctx_get_device(cmn); | ||
| if (!device) | ||
| return false; | ||
|
Comment on lines
220
to
+222
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid a bunch of error scenarios by not allowing the function to proceed if the graphics context doesn't have a good device handle. This scenario occurs when the GPU resets. |
||
|
|
||
| bool r = gfx_begin_ui(cmn, device); | ||
|
|
||
| if (r) { | ||
| void *texture = MTY_HashPopInt(cmn->ui_textures, id); | ||
| GFX_UI_API[cmn->api].destroy_texture(cmn->gfx_ui, &texture, device); | ||
| void *texture = MTY_HashGetInt(cmn->ui_textures, id); | ||
|
|
||
| if (rgba) { | ||
| void *texture_new = GFX_UI_API[cmn->api].create_texture(cmn->gfx_ui, device, rgba, width, height); | ||
|
|
||
| if (rgba) | ||
| texture = GFX_UI_API[cmn->api].create_texture(cmn->gfx_ui, device, rgba, width, height); | ||
| if (texture_new) { | ||
| GFX_UI_API[cmn->api].destroy_texture(cmn->gfx_ui, &texture, device); | ||
|
Comment on lines
+232
to
+233
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please check if |
||
|
|
||
| if (texture) | ||
| MTY_HashSetInt(cmn->ui_textures, id, texture); | ||
| texture = texture_new; | ||
| MTY_HashSetInt(cmn->ui_textures, id, texture_new); | ||
| } | ||
|
|
||
| } else { | ||
| GFX_UI_API[cmn->api].destroy_texture(cmn->gfx_ui, &texture, device); | ||
| MTY_HashPopInt(cmn->ui_textures, id); | ||
| } | ||
|
|
||
| r = texture != NULL; | ||
| } | ||
|
Comment on lines
225
to
245
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The old logic is to first destroy the existing texture for The problem with this is that when My fix is to replace the old/existing texture with the new one IFF the new texture was successfully created. After this change, even when the GPU resets, textures are rendered properly because the hash still maintains a reference to the original valid handle. |
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2062,7 +2062,15 @@ void MTY_WindowWarpCursor(MTY_App *app, MTY_Window window, uint32_t x, uint32_t | |
|
|
||
| MTY_ContextState MTY_WindowGetContextState(MTY_App *app, MTY_Window window) | ||
| { | ||
| return MTY_CONTEXT_STATE_NORMAL; | ||
| MTY_ContextState state = MTY_CONTEXT_STATE_NORMAL; | ||
|
|
||
| struct window_common *cmn = mty_window_get_common(app, window); | ||
| if (cmn && cmn->api != MTY_GFX_NONE && cmn->device_changed) { | ||
| state = MTY_CONTEXT_STATE_NEW; | ||
| cmn->device_changed = false; | ||
| } | ||
|
|
||
| return state; | ||
| } | ||
|
Comment on lines
2063
to
2074
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When the device has changed or been reset, it is a mistake not to inform clients of matoya that textures need to be resubmitted. |
||
|
|
||
| void *MTY_WindowGetNative(MTY_App *app, MTY_Window window) | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All changes in this file implement a more robust GPU error handling. This portion concerns the main graphics context state. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,13 +14,15 @@ GFX_CTX_PROTOTYPES(_d3d11_) | |
|
|
||
| #define DXGI_FATAL(e) ( \ | ||
| (e) == DXGI_ERROR_DEVICE_REMOVED || \ | ||
| (e) == DXGI_ERROR_DRIVER_INTERNAL_ERROR || \ | ||
| (e) == DXGI_ERROR_DEVICE_HUNG || \ | ||
| (e) == DXGI_ERROR_DEVICE_RESET \ | ||
| ) | ||
|
|
||
| #define D3D11_CTX_WAIT 2000 | ||
|
|
||
| struct d3d11_ctx { | ||
| bool ready; | ||
| HWND hwnd; | ||
| struct sync sync; | ||
| struct dxgi_sync *dxgi_sync; | ||
|
|
@@ -46,6 +48,8 @@ static void d3d11_ctx_get_size(struct d3d11_ctx *ctx, uint32_t *width, uint32_t | |
|
|
||
| static void d3d11_ctx_free(struct d3d11_ctx *ctx) | ||
| { | ||
| ctx->ready = false; | ||
|
|
||
| if (ctx->back_buffer) | ||
| ID3D11RenderTargetView_Release(ctx->back_buffer); | ||
|
|
||
|
|
@@ -55,8 +59,11 @@ static void d3d11_ctx_free(struct d3d11_ctx *ctx) | |
| if (ctx->swap_chain2) | ||
| IDXGISwapChain2_Release(ctx->swap_chain2); | ||
|
|
||
| if (ctx->context) | ||
| if (ctx->context) { | ||
| ID3D11DeviceContext_Flush(ctx->context); | ||
| ID3D11DeviceContext_ClearState(ctx->context); | ||
| ID3D11DeviceContext_Release(ctx->context); | ||
| } | ||
|
|
||
| if (ctx->device) | ||
| ID3D11Device_Release(ctx->device); | ||
|
|
@@ -187,10 +194,32 @@ static bool d3d11_ctx_init(struct d3d11_ctx *ctx) | |
| if (device2) | ||
| IDXGIDevice2_Release(device2); | ||
|
|
||
| if (e != S_OK) | ||
| ctx->ready = e == S_OK; | ||
|
|
||
| if (!ctx->ready) | ||
| d3d11_ctx_free(ctx); | ||
|
|
||
| return e == S_OK; | ||
| return ctx->ready; | ||
| } | ||
|
|
||
| static bool d3d11_ctx_reinit(struct d3d11_ctx *ctx) | ||
| { | ||
| MTY_Log("Reinitializing d3d11_ctx..."); | ||
| d3d11_ctx_free(ctx); | ||
| bool r = d3d11_ctx_init(ctx); | ||
| if (r) | ||
| MTY_Log("d3d11_ctx reinit ok."); | ||
| else | ||
| MTY_Log("d3d11_ctx reinit FAILED!"); | ||
|
Comment on lines
+210
to
+213
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No braces, and I'm not certain we need to log two times every time |
||
|
|
||
| return r; | ||
| } | ||
|
|
||
| int32_t mty_d3d11_error_handler_default(int32_t e1, int32_t e2, void *opaque) | ||
| { | ||
| e1; e2; opaque; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We usually put each of these on a different line |
||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| struct gfx_ctx *mty_d3d11_ctx_create(void *native_window, bool vsync) | ||
|
|
@@ -235,18 +264,27 @@ MTY_Device *mty_d3d11_ctx_get_device(struct gfx_ctx *gfx_ctx) | |
| { | ||
| struct d3d11_ctx *ctx = (struct d3d11_ctx *) gfx_ctx; | ||
|
|
||
| if (!ctx->ready) | ||
| d3d11_ctx_reinit(ctx); | ||
|
|
||
| return (MTY_Device *) ctx->device; | ||
| } | ||
|
|
||
| MTY_Context *mty_d3d11_ctx_get_context(struct gfx_ctx *gfx_ctx) | ||
| { | ||
| struct d3d11_ctx *ctx = (struct d3d11_ctx *) gfx_ctx; | ||
|
|
||
| if (!ctx->ready) | ||
| d3d11_ctx_reinit(ctx); | ||
|
|
||
| return (MTY_Context *) ctx->context; | ||
| } | ||
|
|
||
| static void d3d11_ctx_refresh(struct d3d11_ctx *ctx) | ||
| { | ||
| if (!ctx->ready && !d3d11_ctx_reinit(ctx)) | ||
| return; | ||
|
|
||
| uint32_t width = ctx->width; | ||
| uint32_t height = ctx->height; | ||
|
|
||
|
|
@@ -263,8 +301,7 @@ static void d3d11_ctx_refresh(struct d3d11_ctx *ctx) | |
|
|
||
| if (DXGI_FATAL(e)) { | ||
| MTY_Log("'IDXGISwapChain2_ResizeBuffers' failed with HRESULT 0x%X", e); | ||
| d3d11_ctx_free(ctx); | ||
| d3d11_ctx_init(ctx); | ||
| ctx->ready = false; | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -273,6 +310,10 @@ MTY_Surface *mty_d3d11_ctx_get_surface(struct gfx_ctx *gfx_ctx) | |
| { | ||
| struct d3d11_ctx *ctx = (struct d3d11_ctx *) gfx_ctx; | ||
|
|
||
| if (!ctx->ready && !d3d11_ctx_reinit(ctx)) | ||
| return NULL; | ||
|
|
||
| HRESULT e = S_OK; | ||
| ID3D11Resource *resource = NULL; | ||
|
|
||
| if (!ctx->swap_chain2) | ||
|
|
@@ -281,22 +322,26 @@ MTY_Surface *mty_d3d11_ctx_get_surface(struct gfx_ctx *gfx_ctx) | |
| if (!ctx->back_buffer) { | ||
| d3d11_ctx_refresh(ctx); | ||
|
|
||
| HRESULT e = IDXGISwapChain2_GetBuffer(ctx->swap_chain2, 0, &IID_ID3D11Resource, &resource); | ||
| e = IDXGISwapChain2_GetBuffer(ctx->swap_chain2, 0, &IID_ID3D11Resource, &resource); | ||
| if (e != S_OK) { | ||
| MTY_Log("'IDXGISwapChain2_GetBuffer' failed with HRESULT 0x%X", e); | ||
| goto except; | ||
| } | ||
|
|
||
| e = ID3D11Device_CreateRenderTargetView(ctx->device, resource, NULL, &ctx->back_buffer); | ||
| if (e != S_OK) | ||
| if (e != S_OK) { | ||
| MTY_Log("'ID3D11Device_CreateRenderTargetView' failed with HRESULT 0x%X", e); | ||
| goto except; | ||
| } | ||
| } | ||
|
|
||
| except: | ||
|
|
||
| if (resource) | ||
| ID3D11Resource_Release(resource); | ||
|
|
||
| ctx->ready = e == S_OK; | ||
|
|
||
| return (MTY_Surface *) ctx->back_buffer; | ||
| } | ||
|
|
||
|
|
@@ -314,6 +359,9 @@ void mty_d3d11_ctx_present(struct gfx_ctx *gfx_ctx) | |
| { | ||
| struct d3d11_ctx *ctx = (struct d3d11_ctx *) gfx_ctx; | ||
|
|
||
| if (!ctx->ready && !d3d11_ctx_reinit(ctx)) | ||
| return; | ||
|
|
||
| if (ctx->back_buffer) { | ||
| int64_t interval = sync_next_interval(&ctx->sync); | ||
| UINT flags = interval > 0 ? 0 : DXGI_PRESENT_ALLOW_TEARING; | ||
|
|
@@ -335,13 +383,14 @@ void mty_d3d11_ctx_present(struct gfx_ctx *gfx_ctx) | |
|
|
||
| if (DXGI_FATAL(e)) { | ||
| MTY_Log("'IDXGISwapChain2_Present' failed with HRESULT 0x%X", e); | ||
| d3d11_ctx_free(ctx); | ||
| d3d11_ctx_init(ctx); | ||
| ctx->ready = false; | ||
|
|
||
| } else { | ||
| DWORD we = WaitForSingleObjectEx(ctx->waitable, D3D11_CTX_WAIT, TRUE); | ||
| if (we != WAIT_OBJECT_0) | ||
| if (we != WAIT_OBJECT_0) { | ||
| MTY_Log("'WaitForSingleObjectEx' failed with error 0x%X", we); | ||
| ctx->ready = false; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All changes in this file implement a more robust GPU error handling. This portion concerns state pertaining to rendering UI. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the
gfx_uistate could not be created from the givendevice, then we want to clean out the hash of textures.