Merge branch 'blender-v4.1-release'

This commit is contained in:
Omar Emara
2024-03-06 08:28:04 +02:00
6 changed files with 88 additions and 15 deletions
@@ -162,6 +162,11 @@ class CompileState {
* compiled. */
bool should_compile_shader_compile_unit(DNode node);
/* Computes the number of shader operation outputs that will be added for this node in the
* current shader compile unit. This is essentially the number of outputs that will be added for
* the node in ShaderOperation::populate_results_for_node. */
int compute_shader_node_operation_outputs_count(DNode node);
private:
/* Compute the node domain of the given shader node. This is analogous to the
* Operation::compute_domain method, except it is computed from the node itself as opposed to a
@@ -73,6 +73,9 @@ void compute_dispatch_threads_at_least(GPUShader *shader,
/* Returns true if a node preview needs to be computed for the give node. */
bool is_node_preview_needed(const DNode &node);
/* Returns the node output that will be used to generate previews. */
DOutputSocket find_preview_output_socket(const DNode &node);
/* Computes a lower resolution version of the given result and sets it as a preview for the given
* node after applying the appropriate color management specified in the given context. */
void compute_preview_from_result(Context &context, const DNode &node, Result &input_result);
@@ -106,6 +106,32 @@ bool CompileState::should_compile_shader_compile_unit(DNode node)
return false;
}
int CompileState::compute_shader_node_operation_outputs_count(DNode node)
{
const DOutputSocket preview_output = find_preview_output_socket(node);
int outputs_count = 0;
for (const bNodeSocket *output : node->output_sockets()) {
const DOutputSocket doutput{node.context(), output};
/* If the output is used as the node preview, then an operation output will exist for it. */
const bool is_preview_output = doutput == preview_output;
/* If any of the nodes linked to the output are not part of the shader compile unit but are
* part of the execution schedule, then an operation output will exist for it. */
const bool is_operation_output = is_output_linked_to_node_conditioned(
doutput, [&](DNode node) {
return schedule_.contains(node) && !shader_compile_unit_.contains(node);
});
if (is_operation_output || is_preview_output) {
outputs_count += 1;
}
}
return outputs_count;
}
Domain CompileState::compute_shader_node_domain(DNode node)
{
/* Default to an identity domain in case no domain input was found, most likely because all
@@ -142,6 +142,45 @@ void Evaluator::map_node_operation_inputs_to_their_results(DNode node,
void Evaluator::compile_and_evaluate_shader_compile_unit(CompileState &compile_state)
{
ShaderCompileUnit &compile_unit = compile_state.get_shader_compile_unit();
/* GPUs have hardware limitations on the number of output images shaders can have, so we might
* have to split the compile unit into smaller units to workaround this limitation. In practice,
* splitting will almost always never happen due to the scheduling strategy we use, so the base
* case remains fast. */
int number_of_outputs = 0;
for (int i : compile_unit.index_range()) {
const DNode node = compile_unit[i];
number_of_outputs += compile_state.compute_shader_node_operation_outputs_count(node);
/* The GPU module currently only supports up to 8 output images in shaders, but once this
* limitation is lifted, we can replace that with GPU_max_images(). */
if (number_of_outputs <= 8) {
continue;
}
/* The number of outputs surpassed the limit, so we split the compile unit into two equal parts
* and recursively call this method on each of them. It might seem unexpected that we split in
* half as opposed to split at the node that surpassed the limit, but that is because the act
* of splitting might actually introduce new outputs, since links that were previously internal
* to the compile unit might now be external. So we can't precisely split and guarantee correct
* units, and we just rely or recursive splitting until units are small enough. Further, half
* splitting helps balancing the shaders, where we don't want to have one gigantic shader and
* a tiny one. */
const int split_index = compile_unit.size() / 2;
const ShaderCompileUnit start_compile_unit(compile_unit.as_span().take_front(split_index));
const ShaderCompileUnit end_compile_unit(compile_unit.as_span().drop_front(split_index));
compile_state.get_shader_compile_unit() = start_compile_unit;
this->compile_and_evaluate_shader_compile_unit(compile_state);
compile_state.get_shader_compile_unit() = end_compile_unit;
this->compile_and_evaluate_shader_compile_unit(compile_state);
/* No need to continue, the above recursive calls will eventually exist the loop and do the
* actual compilation. */
return;
}
const Schedule &schedule = compile_state.get_schedule();
ShaderOperation *operation = new ShaderOperation(context_, compile_unit, schedule);
@@ -270,21 +270,6 @@ void ShaderOperation::declare_operation_input(DInputSocket input_socket,
inputs_to_linked_outputs_map_.add_new(input_identifier, output_socket);
}
static DOutputSocket find_preview_output_socket(const DNode &node)
{
if (!is_node_preview_needed(node)) {
return DOutputSocket();
}
for (const bNodeSocket *output : node->output_sockets()) {
if (output->is_logically_linked()) {
return DOutputSocket(node.context(), output);
}
}
return DOutputSocket();
}
void ShaderOperation::populate_results_for_node(DNode node, GPUMaterial *material)
{
const DOutputSocket preview_output = find_preview_output_socket(node);
@@ -163,6 +163,21 @@ bool is_node_preview_needed(const DNode &node)
return true;
}
DOutputSocket find_preview_output_socket(const DNode &node)
{
if (!is_node_preview_needed(node)) {
return DOutputSocket();
}
for (const bNodeSocket *output : node->output_sockets()) {
if (output->is_logically_linked()) {
return DOutputSocket(node.context(), output);
}
}
return DOutputSocket();
}
/* Given the size of a result, compute a lower resolution size for a preview. The greater dimension
* will be assigned an arbitrarily chosen size of 128, while the other dimension will get the size
* that maintains the same aspect ratio. */