Fix: GPU: Avoid GPUMaterial/Pass collisions between engines #115819
|
@ -41,7 +41,8 @@ using namespace nodes::derived_node_tree_types;
|
|||
ShaderOperation::ShaderOperation(Context &context, ShaderCompileUnit &compile_unit)
|
||||
: Operation(context), compile_unit_(compile_unit)
|
||||
{
|
||||
material_ = GPU_material_from_callbacks(&construct_material, &generate_code, this);
|
||||
material_ = GPU_material_from_callbacks(
|
||||
GPU_MAT_COMPOSITOR, &construct_material, &generate_code, this);
|
||||
GPU_material_status_set(material_, GPU_MAT_QUEUED);
|
||||
GPU_material_compile(material_);
|
||||
}
|
||||
|
|
|
@ -1384,11 +1384,13 @@ static GPUMaterial *eevee_material_get_ex(
|
|||
|
||||
if (ma) {
|
||||
bNodeTree *ntree = !is_default ? ma->nodetree : EEVEE_shader_default_surface_nodetree(ma);
|
||||
mat = DRW_shader_from_material(ma, ntree, options, is_volume, deferred, cbfn, nullptr);
|
||||
mat = DRW_shader_from_material(
|
||||
ma, ntree, GPU_MAT_EEVEE_LEGACY, options, is_volume, deferred, cbfn, nullptr);
|
||||
}
|
||||
else {
|
||||
bNodeTree *ntree = !is_default ? wo->nodetree : EEVEE_shader_default_world_nodetree(wo);
|
||||
mat = DRW_shader_from_world(wo, ntree, options, is_volume, deferred, cbfn, nullptr);
|
||||
mat = DRW_shader_from_world(
|
||||
wo, ntree, GPU_MAT_EEVEE_LEGACY, options, is_volume, deferred, cbfn, nullptr);
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
|
|
|
@ -666,8 +666,14 @@ GPUMaterial *ShaderModule::material_shader_get(::Material *blender_mat,
|
|||
uint64_t shader_uuid = shader_uuid_from_material_type(
|
||||
pipeline_type, geometry_type, displacement_type, blender_mat->blend_flag);
|
||||
|
||||
return DRW_shader_from_material(
|
||||
blender_mat, nodetree, shader_uuid, is_volume, deferred_compilation, codegen_callback, this);
|
||||
return DRW_shader_from_material(blender_mat,
|
||||
nodetree,
|
||||
GPU_MAT_EEVEE,
|
||||
shader_uuid,
|
||||
is_volume,
|
||||
deferred_compilation,
|
||||
codegen_callback,
|
||||
this);
|
||||
}
|
||||
|
||||
GPUMaterial *ShaderModule::world_shader_get(::World *blender_world,
|
||||
|
@ -681,8 +687,14 @@ GPUMaterial *ShaderModule::world_shader_get(::World *blender_world,
|
|||
|
||||
uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, geometry_type);
|
||||
|
||||
return DRW_shader_from_world(
|
||||
blender_world, nodetree, shader_uuid, is_volume, defer_compilation, codegen_callback, this);
|
||||
return DRW_shader_from_world(blender_world,
|
||||
nodetree,
|
||||
GPU_MAT_EEVEE,
|
||||
shader_uuid,
|
||||
is_volume,
|
||||
defer_compilation,
|
||||
codegen_callback,
|
||||
this);
|
||||
}
|
||||
|
||||
/* Variation to compile a material only with a nodetree. Caller needs to maintain the list of
|
||||
|
@ -702,6 +714,7 @@ GPUMaterial *ShaderModule::material_shader_get(const char *name,
|
|||
nodetree,
|
||||
&materials,
|
||||
name,
|
||||
GPU_MAT_EEVEE,
|
||||
shader_uuid,
|
||||
is_volume,
|
||||
false,
|
||||
|
|
|
@ -296,6 +296,7 @@ struct GPUShader *DRW_shader_create_fullscreen_with_shaderlib_ex(const char *fra
|
|||
|
||||
struct GPUMaterial *DRW_shader_from_world(struct World *wo,
|
||||
struct bNodeTree *ntree,
|
||||
eGPUMaterialEngine engine,
|
||||
const uint64_t shader_id,
|
||||
const bool is_volume_shader,
|
||||
bool deferred,
|
||||
|
@ -303,6 +304,7 @@ struct GPUMaterial *DRW_shader_from_world(struct World *wo,
|
|||
void *thunk);
|
||||
struct GPUMaterial *DRW_shader_from_material(struct Material *ma,
|
||||
struct bNodeTree *ntree,
|
||||
eGPUMaterialEngine engine,
|
||||
const uint64_t shader_id,
|
||||
const bool is_volume_shader,
|
||||
bool deferred,
|
||||
|
|
|
@ -493,6 +493,7 @@ GPUShader *DRW_shader_create_fullscreen_with_shaderlib_ex(const char *frag,
|
|||
|
||||
GPUMaterial *DRW_shader_from_world(World *wo,
|
||||
bNodeTree *ntree,
|
||||
eGPUMaterialEngine engine,
|
||||
const uint64_t shader_id,
|
||||
const bool is_volume_shader,
|
||||
bool deferred,
|
||||
|
@ -505,6 +506,7 @@ GPUMaterial *DRW_shader_from_world(World *wo,
|
|||
ntree,
|
||||
&wo->gpumaterial,
|
||||
wo->id.name,
|
||||
engine,
|
||||
shader_id,
|
||||
is_volume_shader,
|
||||
false,
|
||||
|
@ -525,6 +527,7 @@ GPUMaterial *DRW_shader_from_world(World *wo,
|
|||
|
||||
GPUMaterial *DRW_shader_from_material(Material *ma,
|
||||
bNodeTree *ntree,
|
||||
eGPUMaterialEngine engine,
|
||||
const uint64_t shader_id,
|
||||
const bool is_volume_shader,
|
||||
bool deferred,
|
||||
|
@ -537,6 +540,7 @@ GPUMaterial *DRW_shader_from_material(Material *ma,
|
|||
ntree,
|
||||
&ma->gpumaterial,
|
||||
ma->id.name,
|
||||
engine,
|
||||
shader_id,
|
||||
is_volume_shader,
|
||||
false,
|
||||
|
|
|
@ -233,19 +233,19 @@ struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material,
|
|||
/**
|
||||
* High level functions to create and use GPU materials.
|
||||
*/
|
||||
GPUMaterial *GPU_material_from_nodetree_find(struct ListBase *gpumaterials,
|
||||
const void *engine_type,
|
||||
int options);
|
||||
/**
|
||||
* \note Caller must use #GPU_material_from_nodetree_find to re-use existing materials,
|
||||
* This is enforced since constructing other arguments to this function may be expensive
|
||||
* so only do this when they are needed.
|
||||
*/
|
||||
|
||||
typedef enum eGPUMaterialEngine {
|
||||
GPU_MAT_EEVEE_LEGACY = 0,
|
||||
GPU_MAT_EEVEE,
|
||||
GPU_MAT_COMPOSITOR,
|
||||
} eGPUMaterialEngine;
|
||||
|
||||
GPUMaterial *GPU_material_from_nodetree(struct Scene *scene,
|
||||
struct Material *ma,
|
||||
struct bNodeTree *ntree,
|
||||
struct ListBase *gpumaterials,
|
||||
const char *name,
|
||||
eGPUMaterialEngine engine,
|
||||
uint64_t shader_uuid,
|
||||
bool is_volume_shader,
|
||||
bool is_lookdev,
|
||||
|
@ -421,7 +421,8 @@ typedef void (*ConstructGPUMaterialFn)(void *thunk, GPUMaterial *material);
|
|||
|
||||
/* Construct a GPU material from a set of callbacks. See the callback types for more information.
|
||||
* The given thunk will be passed as the first parameter of each callback. */
|
||||
GPUMaterial *GPU_material_from_callbacks(ConstructGPUMaterialFn construct_function_cb,
|
||||
GPUMaterial *GPU_material_from_callbacks(eGPUMaterialEngine engine,
|
||||
ConstructGPUMaterialFn construct_function_cb,
|
||||
GPUCodegenCallbackFn generate_code_function_cb,
|
||||
void *thunk);
|
||||
|
||||
|
|
|
@ -97,6 +97,8 @@ struct GPUPass {
|
|||
uint refcount;
|
||||
/** The last time the refcount was greater than 0. */
|
||||
int gc_timestamp;
|
||||
/** The engine type this pass is compiled for. */
|
||||
eGPUMaterialEngine engine;
|
||||
/** Identity hash generated from all GLSL code. */
|
||||
uint32_t hash;
|
||||
/** Did we already tried to compile the attached GPUShader. */
|
||||
|
@ -122,12 +124,12 @@ static SpinLock pass_cache_spin;
|
|||
|
||||
/* Search by hash only. Return first pass with the same hash.
|
||||
* There is hash collision if (pass->next && pass->next->hash == hash) */
|
||||
static GPUPass *gpu_pass_cache_lookup(uint32_t hash)
|
||||
static GPUPass *gpu_pass_cache_lookup(eGPUMaterialEngine engine, uint32_t hash)
|
||||
{
|
||||
BLI_spin_lock(&pass_cache_spin);
|
||||
/* Could be optimized with a Lookup table. */
|
||||
for (GPUPass *pass = pass_cache; pass; pass = pass->next) {
|
||||
if (pass->hash == hash) {
|
||||
if (pass->hash == hash && pass->engine == engine) {
|
||||
BLI_spin_unlock(&pass_cache_spin);
|
||||
return pass;
|
||||
}
|
||||
|
@ -157,10 +159,12 @@ static GPUPass *gpu_pass_cache_resolve_collision(GPUPass *pass,
|
|||
GPUShaderCreateInfo *info,
|
||||
uint32_t hash)
|
||||
{
|
||||
eGPUMaterialEngine engine = pass->engine;
|
||||
BLI_spin_lock(&pass_cache_spin);
|
||||
for (; pass && (pass->hash == hash); pass = pass->next) {
|
||||
if (*reinterpret_cast<ShaderCreateInfo *>(info) ==
|
||||
*reinterpret_cast<ShaderCreateInfo *>(pass->create_info))
|
||||
*reinterpret_cast<ShaderCreateInfo *>(pass->create_info) &&
|
||||
pass->engine == engine)
|
||||
{
|
||||
BLI_spin_unlock(&pass_cache_spin);
|
||||
return pass;
|
||||
|
@ -732,6 +736,7 @@ void GPUCodegen::generate_graphs()
|
|||
|
||||
GPUPass *GPU_generate_pass(GPUMaterial *material,
|
||||
GPUNodeGraph *graph,
|
||||
eGPUMaterialEngine engine,
|
||||
GPUCodegenCallbackFn finalize_source_cb,
|
||||
void *thunk,
|
||||
bool optimize_graph)
|
||||
|
@ -763,7 +768,7 @@ GPUPass *GPU_generate_pass(GPUMaterial *material,
|
|||
* NOTE: We only perform cache look-up for non-optimized shader
|
||||
* graphs, as baked constant data among other optimizations will generate too many
|
||||
* shader source permutations, with minimal re-usability. */
|
||||
pass_hash = gpu_pass_cache_lookup(codegen.hash_get());
|
||||
pass_hash = gpu_pass_cache_lookup(engine, codegen.hash_get());
|
||||
|
||||
/* FIXME(fclem): This is broken. Since we only check for the hash and not the full source
|
||||
* there is no way to have a collision currently. Some advocated to only use a bigger hash. */
|
||||
|
@ -813,6 +818,7 @@ GPUPass *GPU_generate_pass(GPUMaterial *material,
|
|||
pass->shader = nullptr;
|
||||
pass->refcount = 1;
|
||||
pass->create_info = codegen.create_info;
|
||||
pass->engine = engine;
|
||||
pass->hash = codegen.hash_get();
|
||||
pass->compiled = false;
|
||||
pass->cached = false;
|
||||
|
|
|
@ -25,6 +25,7 @@ typedef struct GPUPass GPUPass;
|
|||
|
||||
GPUPass *GPU_generate_pass(GPUMaterial *material,
|
||||
struct GPUNodeGraph *graph,
|
||||
eGPUMaterialEngine engine,
|
||||
GPUCodegenCallbackFn finalize_source_cb,
|
||||
void *thunk,
|
||||
bool optimize_graph);
|
||||
|
|
|
@ -99,8 +99,9 @@ struct GPUMaterial {
|
|||
eGPUMaterialStatus status;
|
||||
/** Some flags about the nodetree & the needed resources. */
|
||||
eGPUMaterialFlag flag;
|
||||
/* Identify shader variations (shadow, probe, world background...).
|
||||
* Should be unique even across render engines. */
|
||||
/** The engine type this material is compiled for. */
|
||||
eGPUMaterialEngine engine;
|
||||
/* Identify shader variations (shadow, probe, world background...) */
|
||||
uint64_t uuid;
|
||||
/* Number of generated function. */
|
||||
int generated_function_len;
|
||||
|
@ -821,6 +822,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene,
|
|||
bNodeTree *ntree,
|
||||
ListBase *gpumaterials,
|
||||
const char *name,
|
||||
eGPUMaterialEngine engine,
|
||||
uint64_t shader_uuid,
|
||||
bool is_volume_shader,
|
||||
bool is_lookdev,
|
||||
|
@ -830,7 +832,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene,
|
|||
/* Search if this material is not already compiled. */
|
||||
LISTBASE_FOREACH (LinkData *, link, gpumaterials) {
|
||||
GPUMaterial *mat = (GPUMaterial *)link->data;
|
||||
if (mat->uuid == shader_uuid) {
|
||||
if (mat->uuid == shader_uuid && mat->engine == engine) {
|
||||
return mat;
|
||||
}
|
||||
}
|
||||
|
@ -838,6 +840,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene,
|
|||
GPUMaterial *mat = static_cast<GPUMaterial *>(MEM_callocN(sizeof(GPUMaterial), "GPUMaterial"));
|
||||
mat->ma = ma;
|
||||
mat->scene = scene;
|
||||
mat->engine = engine;
|
||||
mat->uuid = shader_uuid;
|
||||
mat->flag = GPU_MATFLAG_UPDATED;
|
||||
mat->status = GPU_MAT_CREATED;
|
||||
|
@ -860,7 +863,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene,
|
|||
|
||||
{
|
||||
/* Create source code and search pass cache for an already compiled version. */
|
||||
mat->pass = GPU_generate_pass(mat, &mat->graph, callback, thunk, false);
|
||||
mat->pass = GPU_generate_pass(mat, &mat->graph, engine, callback, thunk, false);
|
||||
|
||||
if (mat->pass == nullptr) {
|
||||
/* We had a cache hit and the shader has already failed to compile. */
|
||||
|
@ -891,7 +894,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene,
|
|||
mat->optimize_pass_info.callback = callback;
|
||||
mat->optimize_pass_info.thunk = thunk;
|
||||
#else
|
||||
mat->optimized_pass = GPU_generate_pass(mat, &mat->graph, callback, thunk, true);
|
||||
mat->optimized_pass = GPU_generate_pass(mat, &mat->graph, engine, callback, thunk, true);
|
||||
if (mat->optimized_pass == nullptr) {
|
||||
/* Failed to create optimized pass. */
|
||||
gpu_node_graph_free_nodes(&mat->graph);
|
||||
|
@ -1024,8 +1027,12 @@ void GPU_material_optimize(GPUMaterial *mat)
|
|||
* optimal, as these do not benefit from caching, due to baked constants. However, this could
|
||||
* possibly be cause for concern for certain cases. */
|
||||
if (!mat->optimized_pass) {
|
||||
mat->optimized_pass = GPU_generate_pass(
|
||||
mat, &mat->graph, mat->optimize_pass_info.callback, mat->optimize_pass_info.thunk, true);
|
||||
mat->optimized_pass = GPU_generate_pass(mat,
|
||||
&mat->graph,
|
||||
mat->engine,
|
||||
mat->optimize_pass_info.callback,
|
||||
mat->optimize_pass_info.thunk,
|
||||
true);
|
||||
BLI_assert(mat->optimized_pass);
|
||||
}
|
||||
#else
|
||||
|
@ -1097,7 +1104,8 @@ void GPU_materials_free(Main *bmain)
|
|||
BKE_material_defaults_free_gpu();
|
||||
}
|
||||
|
||||
GPUMaterial *GPU_material_from_callbacks(ConstructGPUMaterialFn construct_function_cb,
|
||||
GPUMaterial *GPU_material_from_callbacks(eGPUMaterialEngine engine,
|
||||
ConstructGPUMaterialFn construct_function_cb,
|
||||
GPUCodegenCallbackFn generate_code_function_cb,
|
||||
void *thunk)
|
||||
{
|
||||
|
@ -1110,6 +1118,7 @@ GPUMaterial *GPU_material_from_callbacks(ConstructGPUMaterialFn construct_functi
|
|||
material->optimization_status = GPU_MAT_OPTIMIZATION_SKIP;
|
||||
material->optimized_pass = nullptr;
|
||||
material->default_mat = nullptr;
|
||||
material->engine = engine;
|
||||
|
||||
/* Construct the material graph by adding and linking the necessary GPU material nodes. */
|
||||
construct_function_cb(thunk, material);
|
||||
|
@ -1119,7 +1128,7 @@ GPUMaterial *GPU_material_from_callbacks(ConstructGPUMaterialFn construct_functi
|
|||
|
||||
/* Lookup an existing pass in the cache or generate a new one. */
|
||||
material->pass = GPU_generate_pass(
|
||||
material, &material->graph, generate_code_function_cb, thunk, false);
|
||||
material, &material->graph, material->engine, generate_code_function_cb, thunk, false);
|
||||
material->optimized_pass = nullptr;
|
||||
|
||||
/* The pass already exists in the pass cache but its shader already failed to compile. */
|
||||
|
|
Loading…
Reference in New Issue