/* SPDX-FileCopyrightText: 2016 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup draw */ /** * DRW Instance Data Manager * This is a special memory manager that keeps memory blocks ready to send as VBO data in one * continuous allocation. This way we avoid feeding #gpu::Batch each instance data one by one and * unnecessary `memcpy`. Since we lose which memory block was used each #DRWShadingGroup we need to * redistribute them in the same order/size to avoid to realloc each frame. This is why * #DRWInstanceDatas are sorted in a list for each different data size. */ #include "draw_instance_data.hh" #include "DRW_engine.hh" #include "DRW_render.hh" /* For DRW_shgroup_get_instance_count() */ #include "BLI_listbase.h" #include "BLI_mempool.h" #include "MEM_guardedalloc.h" struct DRWInstanceData { DRWInstanceData *next; bool used; /* If this data is used or not. */ size_t data_size; /* Size of one instance data. */ BLI_mempool *mempool; }; struct DRWInstanceDataList { DRWInstanceDataList *next, *prev; /* Linked lists for all possible data pool size */ DRWInstanceData *idata_head[MAX_INSTANCE_DATA_SIZE]; DRWInstanceData *idata_tail[MAX_INSTANCE_DATA_SIZE]; }; struct DRWTempBufferHandle { blender::gpu::VertBuf *buf; /** Format pointer for reuse. */ GPUVertFormat *format; /** Touched vertex length for resize. */ int *vert_len; }; struct DRWTempInstancingHandle { /** Copy of geom but with the per-instance attributes. */ blender::gpu::Batch *batch; /** Batch containing instancing attributes. */ blender::gpu::Batch *instancer; /** Call-buffer to be used instead of instancer. */ blender::gpu::VertBuf *buf; /** Original non-instanced batch pointer. */ blender::gpu::Batch *geom; }; static ListBase g_idatalists = {nullptr, nullptr}; /* -------------------------------------------------------------------- */ /** \name Instance Data (DRWInstanceData) * \{ */ static DRWInstanceData *drw_instance_data_create(DRWInstanceDataList *idatalist, uint attr_size) { DRWInstanceData *idata = static_cast( MEM_callocN(sizeof(DRWInstanceData), "DRWInstanceData")); idata->next = nullptr; idata->used = true; idata->data_size = attr_size; idata->mempool = BLI_mempool_create(sizeof(float) * idata->data_size, 0, 16, 0); BLI_assert(attr_size > 0); /* Push to linked list. */ if (idatalist->idata_head[attr_size - 1] == nullptr) { idatalist->idata_head[attr_size - 1] = idata; } else { idatalist->idata_tail[attr_size - 1]->next = idata; } idatalist->idata_tail[attr_size - 1] = idata; return idata; } static void DRW_instance_data_free(DRWInstanceData *idata) { BLI_mempool_destroy(idata->mempool); } void *DRW_instance_data_next(DRWInstanceData *idata) { return BLI_mempool_alloc(idata->mempool); } DRWInstanceData *DRW_instance_data_request(DRWInstanceDataList *idatalist, uint attr_size) { BLI_assert(attr_size > 0 && attr_size <= MAX_INSTANCE_DATA_SIZE); DRWInstanceData *idata = idatalist->idata_head[attr_size - 1]; /* Search for an unused data chunk. */ for (; idata; idata = idata->next) { if (idata->used == false) { idata->used = true; return idata; } } return drw_instance_data_create(idatalist, attr_size); } /** \} */ /* -------------------------------------------------------------------- */ /** \name Instance Data List (DRWInstanceDataList) * \{ */ DRWInstanceDataList *DRW_instance_data_list_create() { DRWInstanceDataList *idatalist = static_cast( MEM_callocN(sizeof(DRWInstanceDataList), "DRWInstanceDataList")); BLI_addtail(&g_idatalists, idatalist); return idatalist; } void DRW_instance_data_list_free(DRWInstanceDataList *idatalist) { DRWInstanceData *idata, *next_idata; for (int i = 0; i < MAX_INSTANCE_DATA_SIZE; i++) { for (idata = idatalist->idata_head[i]; idata; idata = next_idata) { next_idata = idata->next; DRW_instance_data_free(idata); MEM_freeN(idata); } idatalist->idata_head[i] = nullptr; idatalist->idata_tail[i] = nullptr; } BLI_remlink(&g_idatalists, idatalist); MEM_freeN(idatalist); } void DRW_instance_data_list_reset(DRWInstanceDataList *idatalist) { DRWInstanceData *idata; for (int i = 0; i < MAX_INSTANCE_DATA_SIZE; i++) { for (idata = idatalist->idata_head[i]; idata; idata = idata->next) { idata->used = false; } } } void DRW_instance_data_list_free_unused(DRWInstanceDataList *idatalist) { DRWInstanceData *idata, *next_idata; /* Remove unused data blocks and sanitize each list. */ for (int i = 0; i < MAX_INSTANCE_DATA_SIZE; i++) { idatalist->idata_tail[i] = nullptr; for (idata = idatalist->idata_head[i]; idata; idata = next_idata) { next_idata = idata->next; if (idata->used == false) { if (idatalist->idata_head[i] == idata) { idatalist->idata_head[i] = next_idata; } else { /* idatalist->idata_tail[i] is guaranteed not to be null in this case. */ idatalist->idata_tail[i]->next = next_idata; } DRW_instance_data_free(idata); MEM_freeN(idata); } else { if (idatalist->idata_tail[i] != nullptr) { idatalist->idata_tail[i]->next = idata; } idatalist->idata_tail[i] = idata; } } } } void DRW_instance_data_list_resize(DRWInstanceDataList *idatalist) { DRWInstanceData *idata; for (int i = 0; i < MAX_INSTANCE_DATA_SIZE; i++) { for (idata = idatalist->idata_head[i]; idata; idata = idata->next) { BLI_mempool_clear_ex(idata->mempool, BLI_mempool_len(idata->mempool)); } } } /** \} */