GPU: Batch Compilation: Show compiled GPUMaterials as soon as possible
Request one separate compilation batch for each GPUPass so users can get a better sense of the compilation progress, and to better distribute texture loading over time. Pull Request: https://projects.blender.org/blender/blender/pulls/125012
This commit is contained in:
@@ -84,8 +84,7 @@ static void drw_deferred_shader_compilation_exec(void *custom_data,
|
||||
WM_system_gpu_context_activate(system_gpu_context);
|
||||
GPU_context_active_set(blender_gpu_context);
|
||||
|
||||
Vector<GPUMaterial *> next_batch;
|
||||
Map<BatchHandle, Vector<GPUMaterial *>> batches;
|
||||
Vector<GPUMaterial *> async_mats;
|
||||
|
||||
while (true) {
|
||||
if (worker_status->stop) {
|
||||
@@ -106,38 +105,26 @@ static void drw_deferred_shader_compilation_exec(void *custom_data,
|
||||
|
||||
if (mat) {
|
||||
/* We have a new material that must be compiled,
|
||||
* we either compile it directly or add it to a parallel compilation batch. */
|
||||
* we either compile it directly or add it to the async compilation list. */
|
||||
if (use_parallel_compilation) {
|
||||
next_batch.append(mat);
|
||||
GPU_material_async_compile(mat);
|
||||
async_mats.append(mat);
|
||||
}
|
||||
else {
|
||||
GPU_material_compile(mat);
|
||||
GPU_material_release(mat);
|
||||
}
|
||||
}
|
||||
else if (!next_batch.is_empty()) {
|
||||
else if (!async_mats.is_empty()) {
|
||||
/* (only if use_parallel_compilation == true)
|
||||
* We ran out of pending materials. Request the compilation of the current batch. */
|
||||
BatchHandle batch_handle = GPU_material_batch_compile(next_batch);
|
||||
batches.add(batch_handle, next_batch);
|
||||
next_batch.clear();
|
||||
}
|
||||
else if (!batches.is_empty()) {
|
||||
/* (only if use_parallel_compilation == true)
|
||||
* Keep querying the requested batches until all of them are ready. */
|
||||
Vector<BatchHandle> ready_handles;
|
||||
for (BatchHandle handle : batches.keys()) {
|
||||
if (GPU_material_batch_is_ready(handle)) {
|
||||
ready_handles.append(handle);
|
||||
}
|
||||
}
|
||||
for (BatchHandle handle : ready_handles) {
|
||||
Vector<GPUMaterial *> batch = batches.pop(handle);
|
||||
GPU_material_batch_finalize(handle, batch);
|
||||
for (GPUMaterial *mat : batch) {
|
||||
* Keep querying the requested materials until all of them are ready. */
|
||||
async_mats.remove_if([](GPUMaterial *mat) {
|
||||
if (GPU_material_async_try_finalize(mat)) {
|
||||
GPU_material_release(mat);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
else {
|
||||
/* Check for Material Optimization job once there are no more
|
||||
@@ -172,12 +159,14 @@ static void drw_deferred_shader_compilation_exec(void *custom_data,
|
||||
|
||||
/* We have to wait until all the requested batches are ready,
|
||||
* even if worker_status->stop is true. */
|
||||
for (BatchHandle handle : batches.keys()) {
|
||||
Vector<GPUMaterial *> &batch = batches.lookup(handle);
|
||||
GPU_material_batch_finalize(handle, batch);
|
||||
for (GPUMaterial *mat : batch) {
|
||||
GPU_material_release(mat);
|
||||
}
|
||||
while (!async_mats.is_empty()) {
|
||||
async_mats.remove_if([](GPUMaterial *mat) {
|
||||
if (GPU_material_async_try_finalize(mat)) {
|
||||
GPU_material_release(mat);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
GPU_context_active_set(nullptr);
|
||||
|
||||
@@ -253,29 +253,9 @@ void GPU_material_compile(GPUMaterial *mat);
|
||||
void GPU_material_free_single(GPUMaterial *material);
|
||||
void GPU_material_free(ListBase *gpumaterial);
|
||||
|
||||
/**
|
||||
* Request the creation of multiple `GPUMaterial`s at once, allowing the backend to use
|
||||
* multithreaded compilation.
|
||||
* Returns a handle that can be used to poll if all materials have been
|
||||
* compiled, and to retrieve the compiled result.
|
||||
* NOTE: This function is asynchronous on OpenGL, but it's blocking on Vulkan and Metal.
|
||||
* WARNING: The material pointers and their pass->create_info should be valid until
|
||||
* `GPU_material_batch_finalize` has returned.
|
||||
*/
|
||||
BatchHandle GPU_material_batch_compile(blender::Span<GPUMaterial *> mats);
|
||||
/**
|
||||
* Returns true if all the materials from the batch have finished their compilation.
|
||||
*/
|
||||
bool GPU_material_batch_is_ready(BatchHandle handle);
|
||||
/**
|
||||
* Assign the compiled shaders to their respective materials and flag their status.
|
||||
* The materials list should have the same length and order as in the `GPU_material_batch_compile`
|
||||
* call.
|
||||
* If the compilation has not finished yet, this call will block the thread until all the
|
||||
* shaders are ready.
|
||||
* WARNING: The handle will be invalidated by this call, you can't process the same batch twice.
|
||||
*/
|
||||
void GPU_material_batch_finalize(BatchHandle &handle, blender::Span<GPUMaterial *> mats);
|
||||
void GPU_material_async_compile(GPUMaterial *mat);
|
||||
/** Returns true if the material have finished its compilation. */
|
||||
bool GPU_material_async_try_finalize(GPUMaterial *mat);
|
||||
|
||||
void GPU_material_acquire(GPUMaterial *mat);
|
||||
void GPU_material_release(GPUMaterial *mat);
|
||||
|
||||
@@ -109,6 +109,8 @@ struct GPUPass {
|
||||
bool should_optimize;
|
||||
/** Whether pass is in the GPUPass cache. */
|
||||
bool cached;
|
||||
|
||||
BatchHandle async_compilation_handle;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
@@ -791,6 +793,7 @@ GPUPass *GPU_generate_pass(GPUMaterial *material,
|
||||
* Optimized passes cannot be optimized further, even if the heuristic is still not
|
||||
* favorable. */
|
||||
pass->should_optimize = (!optimize_graph) && codegen.should_optimize_heuristic();
|
||||
pass->async_compilation_handle = -1;
|
||||
|
||||
codegen.create_info = nullptr;
|
||||
|
||||
@@ -894,6 +897,28 @@ bool GPU_pass_finalize_compilation(GPUPass *pass, GPUShader *shader)
|
||||
return success;
|
||||
}
|
||||
|
||||
void GPU_pass_begin_async_compilation(GPUPass *pass, const char *shname)
|
||||
{
|
||||
if (pass->async_compilation_handle == -1) {
|
||||
GPUShaderCreateInfo *info = GPU_pass_begin_compilation(pass, shname);
|
||||
BLI_assert(info);
|
||||
pass->async_compilation_handle = GPU_shader_batch_create_from_infos({info});
|
||||
}
|
||||
}
|
||||
|
||||
bool GPU_pass_async_compilation_try_finalize(GPUPass *pass)
|
||||
{
|
||||
BLI_assert(pass->async_compilation_handle != -1);
|
||||
if (pass->async_compilation_handle) {
|
||||
if (GPU_shader_batch_is_ready(pass->async_compilation_handle)) {
|
||||
GPU_pass_finalize_compilation(
|
||||
pass, GPU_shader_batch_finalize(pass->async_compilation_handle).first());
|
||||
}
|
||||
}
|
||||
|
||||
return pass->async_compilation_handle == 0;
|
||||
}
|
||||
|
||||
bool GPU_pass_compile(GPUPass *pass, const char *shname)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
@@ -36,6 +36,12 @@ bool GPU_pass_should_optimize(GPUPass *pass);
|
||||
GPUShaderCreateInfo *GPU_pass_begin_compilation(GPUPass *pass, const char *shname);
|
||||
bool GPU_pass_finalize_compilation(GPUPass *pass, GPUShader *shader);
|
||||
|
||||
void GPU_pass_begin_async_compilation(GPUPass *pass, const char *shname);
|
||||
/** NOTE: Unlike the non-async version, this one returns true when compilation has finalized,
|
||||
* regardless if it succeeded or not.
|
||||
* To check for success, see if `GPU_pass_shader_get() != nullptr`. */
|
||||
bool GPU_pass_async_compilation_try_finalize(GPUPass *pass);
|
||||
|
||||
/* Module */
|
||||
|
||||
void gpu_codegen_init();
|
||||
|
||||
@@ -1029,45 +1029,26 @@ void GPU_material_compile(GPUMaterial *mat)
|
||||
gpu_material_finalize(mat, success);
|
||||
}
|
||||
|
||||
BatchHandle GPU_material_batch_compile(blender::Span<GPUMaterial *> mats)
|
||||
void GPU_material_async_compile(GPUMaterial *mat)
|
||||
{
|
||||
blender::Vector<GPUShaderCreateInfo *> infos;
|
||||
infos.reserve(mats.size());
|
||||
|
||||
for (GPUMaterial *mat : mats) {
|
||||
BLI_assert(ELEM(mat->status, GPU_MAT_QUEUED, GPU_MAT_CREATED));
|
||||
BLI_assert(mat->pass);
|
||||
BLI_assert(ELEM(mat->status, GPU_MAT_QUEUED, GPU_MAT_CREATED));
|
||||
BLI_assert(mat->pass);
|
||||
#ifndef NDEBUG
|
||||
const char *name = mat->name;
|
||||
const char *name = mat->name;
|
||||
#else
|
||||
const char *name = __func__;
|
||||
const char *name = __func__;
|
||||
#endif
|
||||
mat->do_batch_compilation = false;
|
||||
if (GPUShaderCreateInfo *info = GPU_pass_begin_compilation(mat->pass, name)) {
|
||||
infos.append(info);
|
||||
mat->do_batch_compilation = true;
|
||||
}
|
||||
}
|
||||
|
||||
return GPU_shader_batch_create_from_infos(infos);
|
||||
GPU_pass_begin_async_compilation(mat->pass, name);
|
||||
}
|
||||
|
||||
bool GPU_material_batch_is_ready(BatchHandle handle)
|
||||
bool GPU_material_async_try_finalize(GPUMaterial *mat)
|
||||
{
|
||||
return GPU_shader_batch_is_ready(handle);
|
||||
}
|
||||
|
||||
void GPU_material_batch_finalize(BatchHandle &handle, blender::Span<GPUMaterial *> mats)
|
||||
{
|
||||
blender::Vector<GPUShader *> shaders = GPU_shader_batch_finalize(handle);
|
||||
int i = 0;
|
||||
for (GPUMaterial *mat : mats) {
|
||||
bool success = true;
|
||||
if (mat->do_batch_compilation) {
|
||||
success = GPU_pass_finalize_compilation(mat->pass, shaders[i++]);
|
||||
}
|
||||
gpu_material_finalize(mat, success);
|
||||
BLI_assert(ELEM(mat->status, GPU_MAT_QUEUED, GPU_MAT_CREATED));
|
||||
if (GPU_pass_async_compilation_try_finalize(mat->pass)) {
|
||||
gpu_material_finalize(mat, GPU_pass_shader_get(mat->pass) != nullptr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GPU_material_optimize(GPUMaterial *mat)
|
||||
|
||||
Reference in New Issue
Block a user