Fix T81167: Texture Painting with Paint mask enabled, (de)selecting faces causes a mess with texture slots

Issue caused by {9582797d4b50} in b2.90. The surface per material used
an index buffer owned by the batch. These index buffers are created at
the same time the surface tris index buffer was created. When a material
per batch buffer was invalidated it used the surface tris index buffer
rendering all materials on all surfaces making the last draw command
render succeed.

This patch stores the surface tris per material in the cache so they can
be reused. There is also no need to use the `saved_elem_ranges` anymore as they are
now part of the cache.

The ugly bit of the implementation is that in `extract_tris_finish` the
MeshBufferCache is retrieved. But as this part was already documented as
a hack and it is something that is only used for final meshes. Other
solutions would impact performance or made the fix not condensed
(passing parameters that shouldn't be used).

Reviewed By: Clément Foucault

Differential Revision: https://developer.blender.org/D9136
This commit is contained in:
Jeroen Bakker 2020-10-19 08:08:49 +02:00 committed by Jeroen Bakker
parent b2e067d98c
commit 1ceb91d1b3
Notes: blender-bot 2023-02-14 01:52:41 +01:00
Referenced by issue #82298, Color picker samples merged (display output) colors on a certain object regardless of "Sample Merged" setting
Referenced by issue #81775, Crash on Exiting Texture Paint With Multiresolution (Image Editor involved)
Referenced by issue #81167, Texture Painting with Paint mask enabled, (de)selecting faces causes a mess with texture slots.
3 changed files with 19 additions and 24 deletions

View File

@ -142,6 +142,8 @@ typedef struct MeshBufferCache {
GPUIndexBuf *edituv_points;
GPUIndexBuf *edituv_fdots;
} ibo;
/* Index buffer per material. These are subranges of `ibo.tris` */
GPUIndexBuf **tris_per_mat;
} MeshBufferCache;
typedef enum DRWBatchFlag {

View File

@ -901,19 +901,19 @@ static void extract_tris_finish(const MeshRenderData *mr,
{
MeshExtract_Tri_Data *data = _data;
GPU_indexbuf_build_in_place(&data->elb, ibo);
/* HACK: Create ibo sub-ranges and assign them to each #GPUBatch. */
/* The `surface_per_mat` tests are there when object shading type is set to Wire or Bounds. In
* these cases there isn't a surface per material. */
if (mr->use_final_mesh && cache->surface_per_mat && cache->surface_per_mat[0]) {
MeshBufferCache *mbc = &cache->final;
for (int i = 0; i < mr->mat_len; i++) {
/* Multiply by 3 because these are triangle indices. */
const int mat_start = data->tri_mat_start[i];
const int mat_end = data->tri_mat_end[i];
const int start = mat_start * 3;
const int len = (mat_end - mat_start) * 3;
GPUIndexBuf *sub_ibo = GPU_indexbuf_create_subrange(ibo, start, len);
/* WARNING: We modify the #GPUBatch here! */
GPU_batch_elembuf_set(cache->surface_per_mat[i], sub_ibo, true);
GPU_indexbuf_create_subrange_in_place(mbc->tris_per_mat[i], ibo, start, len);
}
}
MEM_freeN(data->tri_mat_start);

View File

@ -496,6 +496,10 @@ static void mesh_batch_cache_init(Mesh *me)
cache->mat_len = mesh_render_mat_len_get(me);
cache->surface_per_mat = MEM_callocN(sizeof(*cache->surface_per_mat) * cache->mat_len, __func__);
FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) {
mbufcache->tris_per_mat = MEM_callocN(sizeof(*mbufcache->tris_per_mat) * cache->mat_len,
__func__);
}
cache->is_dirty = false;
cache->batch_ready = 0;
@ -703,6 +707,15 @@ static void mesh_batch_cache_clear(Mesh *me)
for (int i = 0; i < sizeof(mbufcache->ibo) / sizeof(void *); i++) {
GPU_INDEXBUF_DISCARD_SAFE(ibos[i]);
}
BLI_assert((mbufcache->tris_per_mat != NULL) || (cache->mat_len == 0));
BLI_assert((mbufcache->tris_per_mat != NULL) && (cache->mat_len > 0));
if (mbufcache->tris_per_mat) {
for (int i = 0; i < cache->mat_len; i++) {
GPU_INDEXBUF_DISCARD_SAFE(mbufcache->tris_per_mat[i]);
}
MEM_SAFE_FREE(mbufcache->tris_per_mat);
}
}
for (int i = 0; i < sizeof(cache->batch) / sizeof(void *); i++) {
GPUBatch **batch = (GPUBatch **)&cache->batch;
@ -1168,7 +1181,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
const bool use_hide)
{
BLI_assert(task_graph);
GPUIndexBuf **saved_elem_ranges = NULL;
const ToolSettings *ts = NULL;
if (scene) {
ts = scene->toolsettings;
@ -1254,17 +1266,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.vcol);
}
}
/* XXX save element buffer to avoid recreating them.
* This is only if the cd_needed changes so it is ok to keep them.*/
if (cache->surface_per_mat[0] && cache->surface_per_mat[0]->elem) {
saved_elem_ranges = MEM_callocN(sizeof(saved_elem_ranges) * cache->mat_len, __func__);
for (int i = 0; i < cache->mat_len; i++) {
saved_elem_ranges[i] = cache->surface_per_mat[i]->elem;
/* Avoid deletion as the batch is owner. */
cache->surface_per_mat[i]->elem = NULL;
cache->surface_per_mat[i]->flag &= ~GPU_BATCH_OWNS_INDEX;
}
}
/* We can't discard batches at this point as they have been
* referenced for drawing. Just clear them in place. */
for (int i = 0; i < cache->mat_len; i++) {
@ -1389,13 +1390,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
/* Per Material */
for (int i = 0; i < cache->mat_len; i++) {
if (DRW_batch_requested(cache->surface_per_mat[i], GPU_PRIM_TRIS)) {
if (saved_elem_ranges && saved_elem_ranges[i]) {
/* XXX assign old element buffer range (it did not change).*/
GPU_batch_elembuf_set(cache->surface_per_mat[i], saved_elem_ranges[i], true);
}
else {
DRW_ibo_request(cache->surface_per_mat[i], &mbufcache->ibo.tris);
}
DRW_ibo_request(cache->surface_per_mat[i], &mbufcache->tris_per_mat[i]);
/* Order matters. First ones override latest VBO's attributes. */
DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.lnor);
DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.pos_nor);
@ -1414,8 +1409,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
}
}
MEM_SAFE_FREE(saved_elem_ranges);
mbufcache = (do_cage) ? &cache->cage : &cache->final;
/* Edit Mesh */