Only the queue mutex owned by ghost is still a std::mutex. Pull Request: https://projects.blender.org/blender/blender/pulls/139344
369 lines
13 KiB
C++
369 lines
13 KiB
C++
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup gpu
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "xxhash.h"
|
|
|
|
#include "BLI_map.hh"
|
|
#include "BLI_mutex.hh"
|
|
#include "BLI_utility_mixins.hh"
|
|
|
|
#include "gpu_state_private.hh"
|
|
|
|
#include "vk_common.hh"
|
|
|
|
namespace blender::gpu {
|
|
class VKDevice;
|
|
class VKDiscardPool;
|
|
|
|
/**
|
|
* Struct containing key information to identify a compute pipeline.
|
|
*/
|
|
struct VKComputeInfo {
|
|
VkShaderModule vk_shader_module;
|
|
VkPipelineLayout vk_pipeline_layout;
|
|
Vector<shader::SpecializationConstant::Value> specialization_constants;
|
|
|
|
bool operator==(const VKComputeInfo &other) const
|
|
{
|
|
return vk_shader_module == other.vk_shader_module &&
|
|
vk_pipeline_layout == other.vk_pipeline_layout &&
|
|
specialization_constants == other.specialization_constants;
|
|
};
|
|
|
|
uint64_t hash() const
|
|
{
|
|
uint64_t hash = uint64_t(vk_shader_module);
|
|
hash = hash * 33 ^ uint64_t(vk_pipeline_layout);
|
|
hash = hash * 33 ^ specialization_constants.hash();
|
|
return hash;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Struct containing key information to identify graphics pipelines.
|
|
*
|
|
* The struct has already been separated into 4 stages to be compatible with
|
|
* `VK_EXT_graphics_pipeline_library` being vertex_in, pre_rasterization, fragment_shader and
|
|
* fragment_out.
|
|
*/
|
|
struct VKGraphicsInfo {
|
|
struct VertexIn {
|
|
VkPrimitiveTopology vk_topology;
|
|
Vector<VkVertexInputAttributeDescription> attributes;
|
|
Vector<VkVertexInputBindingDescription> bindings;
|
|
|
|
bool operator==(const VertexIn &other) const
|
|
{
|
|
/* TODO: use an exact implementation and remove the hash compare. */
|
|
#if 0
|
|
return vk_topology == other.vk_topology && attributes.hash() == other.attributes.hash() &&
|
|
bindings.hash() == other.bindings.hash();
|
|
#endif
|
|
return hash() == other.hash();
|
|
}
|
|
|
|
uint64_t hash() const
|
|
{
|
|
uint64_t hash = uint64_t(vk_topology);
|
|
hash = hash * 33 ^
|
|
XXH3_64bits(attributes.data(),
|
|
attributes.size() * sizeof(VkVertexInputAttributeDescription));
|
|
hash = hash * 33 ^ XXH3_64bits(bindings.data(),
|
|
bindings.size() * sizeof(VkVertexInputBindingDescription));
|
|
return hash;
|
|
}
|
|
};
|
|
struct PreRasterization {
|
|
VkShaderModule vk_vertex_module;
|
|
VkShaderModule vk_geometry_module;
|
|
bool operator==(const PreRasterization &other) const
|
|
{
|
|
return vk_vertex_module == other.vk_vertex_module &&
|
|
vk_geometry_module == other.vk_geometry_module;
|
|
}
|
|
uint64_t hash() const
|
|
{
|
|
uint64_t hash = 0;
|
|
hash = hash * 33 ^ uint64_t(vk_vertex_module);
|
|
hash = hash * 33 ^ uint64_t(vk_geometry_module);
|
|
return hash;
|
|
}
|
|
};
|
|
struct FragmentShader {
|
|
VkShaderModule vk_fragment_module;
|
|
Vector<VkViewport> viewports;
|
|
Vector<VkRect2D> scissors;
|
|
std::optional<uint64_t> cached_hash;
|
|
|
|
bool operator==(const FragmentShader &other) const
|
|
{
|
|
if (vk_fragment_module != other.vk_fragment_module ||
|
|
viewports.size() != other.viewports.size() || scissors.size() != other.scissors.size() ||
|
|
hash() != other.hash())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
uint64_t hash() const
|
|
{
|
|
if (cached_hash.has_value()) {
|
|
return *cached_hash;
|
|
}
|
|
return calc_hash();
|
|
}
|
|
|
|
void update_hash()
|
|
{
|
|
cached_hash = calc_hash();
|
|
}
|
|
|
|
private:
|
|
uint64_t calc_hash() const
|
|
{
|
|
uint64_t hash = uint64_t(vk_fragment_module);
|
|
hash = hash * 33 ^ uint64_t(viewports.size());
|
|
hash = hash * 33 ^ uint64_t(scissors.size());
|
|
|
|
return hash;
|
|
}
|
|
};
|
|
struct FragmentOut {
|
|
uint32_t color_attachment_size;
|
|
|
|
/* Dynamic rendering */
|
|
VkFormat depth_attachment_format;
|
|
VkFormat stencil_attachment_format;
|
|
Vector<VkFormat> color_attachment_formats;
|
|
/* Render pass rendering */
|
|
VkRenderPass vk_render_pass;
|
|
|
|
bool operator==(const FragmentOut &other) const
|
|
{
|
|
#if 1
|
|
return hash() == other.hash();
|
|
#else
|
|
if (depth_attachment_format != other.depth_attachment_format ||
|
|
stencil_attachment_format != other.stencil_attachment_format ||
|
|
vk_render_pass != other.vk_render_pass ||
|
|
color_attachment_formats.size() != other.color_attachment_formats.size())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (memcmp(color_attachment_formats.data(),
|
|
other.color_attachment_formats.data(),
|
|
color_attachment_formats.size() * sizeof(VkFormat)) == 0)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
uint64_t hash() const
|
|
{
|
|
uint64_t hash = uint64_t(vk_render_pass);
|
|
hash = hash * 33 ^ uint64_t(depth_attachment_format);
|
|
hash = hash * 33 ^ uint64_t(stencil_attachment_format);
|
|
hash = hash * 33 ^ XXH3_64bits(color_attachment_formats.data(),
|
|
color_attachment_formats.size() * sizeof(VkFormat));
|
|
return hash;
|
|
}
|
|
};
|
|
|
|
VertexIn vertex_in;
|
|
PreRasterization pre_rasterization;
|
|
FragmentShader fragment_shader;
|
|
FragmentOut fragment_out;
|
|
|
|
GPUState state;
|
|
GPUStateMutable mutable_state;
|
|
VkPipelineLayout vk_pipeline_layout;
|
|
Vector<shader::SpecializationConstant::Value> specialization_constants;
|
|
|
|
bool operator==(const VKGraphicsInfo &other) const
|
|
{
|
|
return vertex_in == other.vertex_in && pre_rasterization == other.pre_rasterization &&
|
|
fragment_shader == other.fragment_shader && fragment_out == other.fragment_out &&
|
|
vk_pipeline_layout == other.vk_pipeline_layout &&
|
|
specialization_constants == other.specialization_constants && state == other.state &&
|
|
mutable_state == other.mutable_state;
|
|
};
|
|
uint64_t hash() const
|
|
{
|
|
uint64_t hash = 0;
|
|
hash = hash * 33 ^ vertex_in.hash();
|
|
hash = hash * 33 ^ pre_rasterization.hash();
|
|
hash = hash * 33 ^ fragment_shader.hash();
|
|
hash = hash * 33 ^ fragment_out.hash();
|
|
hash = hash * 33 ^ uint64_t(vk_pipeline_layout);
|
|
hash = hash * 33 ^ specialization_constants.hash();
|
|
hash = hash * 33 ^ state.data;
|
|
hash = hash * 33 ^ mutable_state.data[0];
|
|
hash = hash * 33 ^ mutable_state.data[1];
|
|
hash = hash * 33 ^ mutable_state.data[2];
|
|
return hash;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Pipelines are lazy initialized and same pipelines should share their handle.
|
|
*
|
|
* A requirement of our render graph implementation is that changes of the pipeline can be detected
|
|
* based on the VkPipeline handle. We only want to rebind the pipeline handle when the handle
|
|
* actually changes. This improves performance (especially on NVIDIA) devices where pipeline binds
|
|
* are known to be costly.
|
|
*
|
|
* Especially for graphics pipelines many parameters are needed to compile a graphics pipeline.
|
|
* Some of the information would be boiler plating; or at least from Blender point of view. To
|
|
* improve lookup performance we use a slimmed down version of the pipeline create info structs.
|
|
* The idea is that we can limit the required data because we control which data we actually use,
|
|
* removing the boiler plating and improve hashing performance better than the VkPipelineCache can
|
|
* give us.
|
|
*
|
|
* TODO: Extensions like `VK_EXT_graphics_pipeline_library` should fit in this class and ease the
|
|
* development for graphics pipelines. Geometry in and frame-buffer out could be cached separately
|
|
* to reduce pipeline creation times. Most likely we will add support when we work on graphic
|
|
* pipelines. Recent drivers all support this extension, but the full coverage is still <20%. A
|
|
* fallback should made available for older drivers is required.
|
|
*
|
|
* TODO: Creation of shader modules needs to be revisited.
|
|
* VK_EXT_graphics_pipeline_library deprecates the use of shader modules and use the `spriv` bin
|
|
* directly. In this extension the pipeline and shader module are the same. The current approach
|
|
* should also be revisited as the latest drivers all implement pipeline libraries, but there are
|
|
* some platforms where the driver isn't been updated and doesn't implement this extension. In
|
|
* that case shader modules should still be used.
|
|
*
|
|
* TODO: GPUMaterials (or any other large shader) should be unloaded when the GPUShader is
|
|
* destroyed. Exact details what the best approach is unclear as support for EEVEE is still
|
|
* lacking.
|
|
*/
|
|
class VKPipelinePool : public NonCopyable {
|
|
friend class VKDevice;
|
|
|
|
public:
|
|
private:
|
|
Map<VKComputeInfo, VkPipeline> compute_pipelines_;
|
|
Map<VKGraphicsInfo, VkPipeline> graphic_pipelines_;
|
|
/* Partially initialized structures to reuse. */
|
|
VkComputePipelineCreateInfo vk_compute_pipeline_create_info_;
|
|
|
|
VkGraphicsPipelineCreateInfo vk_graphics_pipeline_create_info_;
|
|
VkPipelineRenderingCreateInfo vk_pipeline_rendering_create_info_;
|
|
VkPipelineShaderStageCreateInfo vk_pipeline_shader_stage_create_info_[3];
|
|
VkPipelineInputAssemblyStateCreateInfo vk_pipeline_input_assembly_state_create_info_;
|
|
VkPipelineVertexInputStateCreateInfo vk_pipeline_vertex_input_state_create_info_;
|
|
|
|
VkPipelineRasterizationStateCreateInfo vk_pipeline_rasterization_state_create_info_;
|
|
VkPipelineRasterizationProvokingVertexStateCreateInfoEXT
|
|
vk_pipeline_rasterization_provoking_vertex_state_info_;
|
|
|
|
Vector<VkDynamicState> vk_dynamic_states_;
|
|
VkPipelineDynamicStateCreateInfo vk_pipeline_dynamic_state_create_info_;
|
|
|
|
VkPipelineViewportStateCreateInfo vk_pipeline_viewport_state_create_info_;
|
|
VkPipelineDepthStencilStateCreateInfo vk_pipeline_depth_stencil_state_create_info_;
|
|
|
|
VkPipelineMultisampleStateCreateInfo vk_pipeline_multisample_state_create_info_;
|
|
|
|
Vector<VkPipelineColorBlendAttachmentState> vk_pipeline_color_blend_attachment_states_;
|
|
VkPipelineColorBlendStateCreateInfo vk_pipeline_color_blend_state_create_info_;
|
|
VkPipelineColorBlendAttachmentState vk_pipeline_color_blend_attachment_state_template_;
|
|
|
|
VkSpecializationInfo vk_specialization_info_;
|
|
Vector<VkSpecializationMapEntry> vk_specialization_map_entries_;
|
|
VkPushConstantRange vk_push_constant_range_;
|
|
|
|
VkPipelineCache vk_pipeline_cache_static_;
|
|
VkPipelineCache vk_pipeline_cache_non_static_;
|
|
|
|
Mutex mutex_;
|
|
|
|
public:
|
|
VKPipelinePool();
|
|
|
|
void init();
|
|
|
|
/**
|
|
* Get an existing or create a new compute pipeline based on the provided ComputeInfo.
|
|
*
|
|
* When vk_pipeline_base is a valid pipeline handle, the pipeline base will be used to speed up
|
|
* pipeline creation process.
|
|
*/
|
|
VkPipeline get_or_create_compute_pipeline(VKComputeInfo &compute_info,
|
|
bool is_static_shader,
|
|
VkPipeline vk_pipeline_base);
|
|
|
|
/**
|
|
* Get an existing or create a new compute pipeline based on the provided ComputeInfo.
|
|
*
|
|
* When vk_pipeline_base is a valid pipeline handle, the pipeline base will be used to speed up
|
|
* pipeline creation process.
|
|
*/
|
|
VkPipeline get_or_create_graphics_pipeline(VKGraphicsInfo &graphics_info,
|
|
bool is_static_shader,
|
|
VkPipeline vk_pipeline_base);
|
|
|
|
/**
|
|
* Discard all pipelines that uses the given pipeline_layout.
|
|
*/
|
|
void discard(VKDiscardPool &discard_pool, VkPipelineLayout vk_pipeline_layout);
|
|
|
|
/**
|
|
* Destroy all created pipelines.
|
|
*
|
|
* Function is called just before the device is removed. This cannot be done in the destructor as
|
|
* that would be called after the device is removed.
|
|
*/
|
|
void free_data();
|
|
|
|
/**
|
|
* Read the static pipeline cache from cache file.
|
|
*
|
|
* Pipeline caches requires blender to be build with `WITH_BUILDINFO` enabled . Between commits
|
|
* shader modules can change and shader module identifiers cannot be used. We use the build info
|
|
* to check if the identifiers can be reused.
|
|
*
|
|
* Previous stored pipeline cache will not be read when G_DEBUG_GPU is enabled. In this case the
|
|
* shader modules will be compiled with other settings and any cached pipeline will not be used
|
|
* during this session.
|
|
*
|
|
* NOTE: When developing shaders we assume that `WITH_BUILDINFO` is turned off or `G_DEBUG_GPU`
|
|
* flag is set.
|
|
*/
|
|
void read_from_disk();
|
|
|
|
/**
|
|
* Store the static pipeline cache to disk.
|
|
*
|
|
* Pipeline caches requires blender to be build with `WITH_BUILDINFO` enabled . Between commits
|
|
* shader modules can change and shader module identifiers cannot be used. We use the build info
|
|
* to check if the identifiers can be reused.
|
|
*
|
|
* The cache will not be written when G_DEBUG_GPU is active. In this case the shader modules have
|
|
* been generated with debug information and other compiler settings are used. This will clutter
|
|
* the pipeline cache.
|
|
*
|
|
* NOTE: When developing shaders we assume that `WITH_BUILDINFO` is turned off or `G_DEBUG_GPU`
|
|
* flag is set.
|
|
*/
|
|
void write_to_disk();
|
|
|
|
private:
|
|
VkSpecializationInfo *specialization_info_update(
|
|
Span<shader::SpecializationConstant::Value> specialization_constants);
|
|
void specialization_info_reset();
|
|
};
|
|
|
|
} // namespace blender::gpu
|