diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b2438057..746e425e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,17 +2,27 @@ ### 31-Aug-2024 -A fix in the sokol-zig bindings generator for a breaking naming convention -change in the Zig stdlib. The fix supports both the old and new naming -convention so that sokol-zig continues to be compatible with zig 0.13.0. +- Some cleanup work the WebGPU backend bindgroups cache which fixes + a number of issues: Destroying an image, sampler, storage buffer + or pipeline object now properly evicts any associated item in + the bindgroups cache and releases the associated WebGPU BindGroup + object. Doing this while the BindGroup is in flight also no longer + causes WebGPU errors. -To update the sokol-zig depenency in your project, just run: + For details see issue https://github.com/floooh/sokol/issues/1066 + and PR https://github.com/floooh/sokol/pull/1097 -``` -zig fetch --save=sokol git+https://github.com/floooh/sokol-zig.git -``` +- A fix in the sokol-zig bindings generator for a breaking naming convention + change in the Zig stdlib. The fix supports both the old and new naming + convention so that sokol-zig continues to be compatible with zig 0.13.0. + + To update the sokol-zig depenency in your project, just run: + + ``` + zig fetch --save=sokol git+https://github.com/floooh/sokol-zig.git + ``` -Details in PR https://github.com/floooh/sokol/pull/1100 + More Details in PR https://github.com/floooh/sokol/pull/1100 ### 26-Aug-2024 diff --git a/sokol_gfx.h b/sokol_gfx.h index 9e1321f84..9e5739264 100644 --- a/sokol_gfx.h +++ b/sokol_gfx.h @@ -1496,6 +1496,7 @@ .wgpu.num_bindgroup_cache_hits .wgpu.num_bindgroup_cache_misses .wgpu.num_bindgroup_cache_collisions + .wgpu_num_bindgroup_cache_invalidates .wgpu.num_bindgroup_cache_vs_hash_key_mismatch The value to pay attention to is `.wgpu.num_bindgroup_cache_collisions`, @@ -3482,6 +3483,7 @@ typedef struct sg_frame_stats_wgpu_bindings { uint32_t num_bindgroup_cache_hits; uint32_t num_bindgroup_cache_misses; uint32_t num_bindgroup_cache_collisions; + uint32_t num_bindgroup_cache_invalidates; uint32_t num_bindgroup_cache_hash_vs_key_mismatch; } sg_frame_stats_wgpu_bindings; @@ -5783,10 +5785,20 @@ typedef struct { uint32_t id; } _sg_wgpu_bindgroup_handle_t; -#define _SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS (1 + _SG_WGPU_MAX_BINDGROUP_ENTRIES) +typedef enum { + _SG_WGPU_BINDGROUPSCACHEITEMTYPE_NONE = 0, + _SG_WGPU_BINDGROUPSCACHEITEMTYPE_IMAGE = 0x1111111111111111, + _SG_WGPU_BINDGROUPSCACHEITEMTYPE_SAMPLER = 0x2222222222222222, + _SG_WGPU_BINDGROUPSCACHEITEMTYPE_STORAGEBUFFER = 0x3333333333333333, + _SG_WGPU_BINDGROUPSCACHEITEMTYPE_PIPELINE = 0x4444444444444444, +} _sg_wgpu_bindgroups_cache_item_type_t; + +#define _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS (1 + _SG_WGPU_MAX_BINDGROUP_ENTRIES) typedef struct { uint64_t hash; - uint32_t items[_SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS]; + // the format of cache key items is (_sg_wgpu_bindgroups_cache_item_type_t << 32) | handle.id, + // where the item type is a per-resource-type bit pattern + uint64_t items[_SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS]; } _sg_wgpu_bindgroups_cache_key_t; typedef struct { @@ -14034,6 +14046,26 @@ _SOKOL_PRIVATE uint64_t _sg_wgpu_hash(const void* key, int len, uint64_t seed) { return h; } +_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_item(_sg_wgpu_bindgroups_cache_item_type_t type, uint32_t id) { + return (((uint64_t)type) << 32) | id; +} + +_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_pip_item(uint32_t id) { + return _sg_wgpu_bindgroups_cache_item(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_PIPELINE, id); +} + +_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_image_item(uint32_t id) { + return _sg_wgpu_bindgroups_cache_item(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_IMAGE, id); +} + +_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_sampler_item(uint32_t id) { + return _sg_wgpu_bindgroups_cache_item(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_SAMPLER, id); +} + +_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_sbuf_item(uint32_t id) { + return _sg_wgpu_bindgroups_cache_item(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_STORAGEBUFFER, id); +} + _SOKOL_PRIVATE void _sg_wgpu_init_bindgroups_cache_key(_sg_wgpu_bindgroups_cache_key_t* key, const _sg_bindings_t* bnd) { SOKOL_ASSERT(bnd); SOKOL_ASSERT(bnd->pip); @@ -14045,38 +14077,39 @@ _SOKOL_PRIVATE void _sg_wgpu_init_bindgroups_cache_key(_sg_wgpu_bindgroups_cache SOKOL_ASSERT(bnd->num_fs_sbufs <= SG_MAX_SHADERSTAGE_STORAGEBUFFERS); _sg_clear(key->items, sizeof(key->items)); - key->items[0] = bnd->pip->slot.id; - const int vs_imgs_offset = 1; - const int vs_smps_offset = vs_imgs_offset + SG_MAX_SHADERSTAGE_IMAGES; - const int vs_sbufs_offset = vs_smps_offset + SG_MAX_SHADERSTAGE_SAMPLERS; - const int fs_imgs_offset = vs_sbufs_offset + SG_MAX_SHADERSTAGE_STORAGEBUFFERS; - const int fs_smps_offset = fs_imgs_offset + SG_MAX_SHADERSTAGE_IMAGES; - const int fs_sbufs_offset = fs_smps_offset + SG_MAX_SHADERSTAGE_SAMPLERS; - SOKOL_ASSERT((fs_sbufs_offset + SG_MAX_SHADERSTAGE_STORAGEBUFFERS) == _SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS); + int item_idx = 0; + key->items[item_idx++] = _sg_wgpu_bindgroups_cache_pip_item(bnd->pip->slot.id); for (int i = 0; i < bnd->num_vs_imgs; i++) { SOKOL_ASSERT(bnd->vs_imgs[i]); - key->items[vs_imgs_offset + i] = bnd->vs_imgs[i]->slot.id; + SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS); + key->items[item_idx++] = _sg_wgpu_bindgroups_cache_image_item(bnd->vs_imgs[i]->slot.id); } for (int i = 0; i < bnd->num_vs_smps; i++) { SOKOL_ASSERT(bnd->vs_smps[i]); - key->items[vs_smps_offset + i] = bnd->vs_smps[i]->slot.id; + SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS); + key->items[item_idx++] = _sg_wgpu_bindgroups_cache_sampler_item(bnd->vs_smps[i]->slot.id); } for (int i = 0; i < bnd->num_vs_sbufs; i++) { SOKOL_ASSERT(bnd->vs_sbufs[i]); - key->items[vs_sbufs_offset + i] = bnd->vs_sbufs[i]->slot.id; + SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS); + key->items[item_idx++] = _sg_wgpu_bindgroups_cache_sbuf_item(bnd->vs_sbufs[i]->slot.id); } for (int i = 0; i < bnd->num_fs_imgs; i++) { SOKOL_ASSERT(bnd->fs_imgs[i]); - key->items[fs_imgs_offset + i] = bnd->fs_imgs[i]->slot.id; + SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS); + key->items[item_idx++] = _sg_wgpu_bindgroups_cache_image_item(bnd->fs_imgs[i]->slot.id); } for (int i = 0; i < bnd->num_fs_smps; i++) { SOKOL_ASSERT(bnd->fs_smps[i]); - key->items[fs_smps_offset + i] = bnd->fs_smps[i]->slot.id; + SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS); + key->items[item_idx++] = _sg_wgpu_bindgroups_cache_sampler_item(bnd->fs_smps[i]->slot.id); } for (int i = 0; i < bnd->num_fs_sbufs; i++) { SOKOL_ASSERT(bnd->fs_sbufs[i]); - key->items[fs_sbufs_offset + i] = bnd->fs_sbufs[i]->slot.id; + SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS); + key->items[item_idx++] = _sg_wgpu_bindgroups_cache_sbuf_item(bnd->fs_sbufs[i]->slot.id); } + SOKOL_ASSERT(item_idx == (1 + bnd->num_vs_imgs + bnd->num_vs_smps + bnd->num_vs_sbufs + bnd->num_fs_imgs + bnd->num_fs_smps + bnd->num_fs_sbufs)); key->hash = _sg_wgpu_hash(&key->items, (int)sizeof(key->items), 0x1234567887654321); } @@ -14228,6 +14261,33 @@ _SOKOL_PRIVATE uint32_t _sg_wgpu_bindgroups_cache_get(uint64_t hash) { return _sg.wgpu.bindgroups_cache.items[index].id; } +// called from wgpu resource destroy functions to also invalidate any +// bindgroups cache slot and bindgroup referencing that resource +_SOKOL_PRIVATE void _sg_wgpu_bindgroups_cache_invalidate(_sg_wgpu_bindgroups_cache_item_type_t type, uint32_t id) { + const uint64_t key_item = _sg_wgpu_bindgroups_cache_item(type, id); + SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.items); + for (uint32_t cache_item_idx = 0; cache_item_idx < _sg.wgpu.bindgroups_cache.num; cache_item_idx++) { + const uint32_t bg_id = _sg.wgpu.bindgroups_cache.items[cache_item_idx].id; + if (bg_id != SG_INVALID_ID) { + _sg_wgpu_bindgroup_t* bg = _sg_wgpu_lookup_bindgroup(bg_id); + SOKOL_ASSERT(bg && (bg->slot.state == SG_RESOURCESTATE_VALID)); + // check if resource is in bindgroup, if yes discard bindgroup and invalidate cache slot + bool invalidate_cache_item = false; + for (int key_item_idx = 0; key_item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS; key_item_idx++) { + if (bg->key.items[key_item_idx] == key_item) { + invalidate_cache_item = true; + break; + } + } + if (invalidate_cache_item) { + _sg_wgpu_discard_bindgroup(bg); bg = 0; + _sg_wgpu_bindgroups_cache_set(cache_item_idx, SG_INVALID_ID); + _sg_stats_add(wgpu.bindings.num_bindgroup_cache_invalidates, 1); + } + } + } +} + _SOKOL_PRIVATE void _sg_wgpu_bindings_cache_clear(void) { memset(&_sg.wgpu.bindings_cache, 0, sizeof(_sg.wgpu.bindings_cache)); } @@ -14288,7 +14348,7 @@ _SOKOL_PRIVATE void _sg_wgpu_bindings_cache_bg_update(const _sg_wgpu_bindgroup_t } } -_SOKOL_PRIVATE void _sg_wgpu_set_bindings_bindgroup(_sg_wgpu_bindgroup_t* bg) { +_SOKOL_PRIVATE void _sg_wgpu_set_bindgroup(_sg_wgpu_bindgroup_t* bg) { if (_sg_wgpu_bindings_cache_bg_dirty(bg)) { _sg_wgpu_bindings_cache_bg_update(bg); _sg_stats_add(wgpu.bindings.num_set_bindgroup, 1); @@ -14334,7 +14394,7 @@ _SOKOL_PRIVATE bool _sg_wgpu_apply_bindgroup(_sg_bindings_t* bnd) { _sg_wgpu_bindgroups_cache_set(key.hash, bg->slot.id); } if (bg && bg->slot.state == SG_RESOURCESTATE_VALID) { - _sg_wgpu_set_bindings_bindgroup(bg); + _sg_wgpu_set_bindgroup(bg); } else { return false; } @@ -14343,7 +14403,7 @@ _SOKOL_PRIVATE bool _sg_wgpu_apply_bindgroup(_sg_bindings_t* bnd) { _sg_wgpu_bindgroup_t* bg = _sg_wgpu_create_bindgroup(bnd); if (bg) { if (bg->slot.state == SG_RESOURCESTATE_VALID) { - _sg_wgpu_set_bindings_bindgroup(bg); + _sg_wgpu_set_bindgroup(bg); } _sg_wgpu_discard_bindgroup(bg); } else { @@ -14351,7 +14411,7 @@ _SOKOL_PRIVATE bool _sg_wgpu_apply_bindgroup(_sg_bindings_t* bnd) { } } } else { - _sg_wgpu_set_bindings_bindgroup(0); + _sg_wgpu_set_bindgroup(0); } return true; } @@ -14484,8 +14544,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_buffer(_sg_buffer_t* buf, const _SOKOL_PRIVATE void _sg_wgpu_discard_buffer(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); + if (buf->cmn.type == SG_BUFFERTYPE_STORAGEBUFFER) { + _sg_wgpu_bindgroups_cache_invalidate(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_STORAGEBUFFER, buf->slot.id); + } if (buf->wgpu.buf) { - wgpuBufferDestroy(buf->wgpu.buf); wgpuBufferRelease(buf->wgpu.buf); } } @@ -14621,12 +14683,12 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const s _SOKOL_PRIVATE void _sg_wgpu_discard_image(_sg_image_t* img) { SOKOL_ASSERT(img); + _sg_wgpu_bindgroups_cache_invalidate(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_IMAGE, img->slot.id); if (img->wgpu.view) { wgpuTextureViewRelease(img->wgpu.view); img->wgpu.view = 0; } if (img->wgpu.tex) { - wgpuTextureDestroy(img->wgpu.tex); wgpuTextureRelease(img->wgpu.tex); img->wgpu.tex = 0; } @@ -14667,6 +14729,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_sampler(_sg_sampler_t* smp, con _SOKOL_PRIVATE void _sg_wgpu_discard_sampler(_sg_sampler_t* smp) { SOKOL_ASSERT(smp); + _sg_wgpu_bindgroups_cache_invalidate(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_SAMPLER, smp->slot.id); if (smp->wgpu.smp) { wgpuSamplerRelease(smp->wgpu.smp); smp->wgpu.smp = 0; @@ -14904,6 +14967,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _ _SOKOL_PRIVATE void _sg_wgpu_discard_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); + _sg_wgpu_bindgroups_cache_invalidate(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_PIPELINE, pip->slot.id); if (pip == _sg.wgpu.cur_pipeline) { _sg.wgpu.cur_pipeline = 0; _sg.wgpu.cur_pipeline_id.id = SG_INVALID_ID; diff --git a/util/sokol_gfx_imgui.h b/util/sokol_gfx_imgui.h index 800c898ce..ba9d25fc9 100644 --- a/util/sokol_gfx_imgui.h +++ b/util/sokol_gfx_imgui.h @@ -4335,6 +4335,7 @@ _SOKOL_PRIVATE void _sgimgui_draw_frame_stats_panel(sgimgui_t* ctx) { _sgimgui_frame_stats(wgpu.bindings.num_bindgroup_cache_hits); _sgimgui_frame_stats(wgpu.bindings.num_bindgroup_cache_misses); _sgimgui_frame_stats(wgpu.bindings.num_bindgroup_cache_collisions); + _sgimgui_frame_stats(wgpu.bindings.num_bindgroup_cache_invalidates); _sgimgui_frame_stats(wgpu.bindings.num_bindgroup_cache_hash_vs_key_mismatch); break; case SG_BACKEND_METAL_MACOS: