Skip to content

Commit

Permalink
sokol_gfx.h wgpu: fix up bindgroups cache when destroying associated …
Browse files Browse the repository at this point in the history
…resource objects (#1097)
  • Loading branch information
floooh authored Aug 31, 2024
1 parent 9b2bf20 commit eacb447
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 30 deletions.
26 changes: 18 additions & 8 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
108 changes: 86 additions & 22 deletions sokol_gfx.h
Original file line number Diff line number Diff line change
Expand Up @@ -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`,
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}

Expand Down Expand Up @@ -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));
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand All @@ -14343,15 +14403,15 @@ _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 {
return false;
}
}
} else {
_sg_wgpu_set_bindings_bindgroup(0);
_sg_wgpu_set_bindgroup(0);
}
return true;
}
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions util/sokol_gfx_imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit eacb447

Please sign in to comment.