Workbench: Share shader cache across instances

Improve instance creation performance by sharing compiled shaders
across instances. (See #114990)

Pull Request: https://projects.blender.org/blender/blender/pulls/118062
This commit is contained in:
Miguel Pozo
2024-02-13 20:56:54 +01:00
parent 8fee7c5fc7
commit 2a2effc0c2
10 changed files with 218 additions and 381 deletions
@@ -111,12 +111,6 @@ static void setup_taa_weights(const float2 offset, float r_weights[9], float &r_
AntiAliasingPass::AntiAliasingPass()
{
taa_accumulation_sh_ = GPU_shader_create_from_info_name("workbench_taa");
smaa_edge_detect_sh_ = GPU_shader_create_from_info_name("workbench_smaa_stage_0");
smaa_aa_weight_sh_ = GPU_shader_create_from_info_name("workbench_smaa_stage_1");
smaa_resolve_sh_ = GPU_shader_create_from_info_name("workbench_smaa_stage_2");
overlay_depth_sh_ = GPU_shader_create_from_info_name("workbench_overlay_depth");
smaa_search_tx_.ensure_2d(
GPU_R8, {SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT}, GPU_TEXTURE_USAGE_SHADER_READ);
GPU_texture_update(smaa_search_tx_, GPU_DATA_UBYTE, searchTexBytes);
@@ -127,15 +121,6 @@ AntiAliasingPass::AntiAliasingPass()
GPU_texture_filter_mode(smaa_area_tx_, true);
}
AntiAliasingPass::~AntiAliasingPass()
{
DRW_SHADER_FREE_SAFE(taa_accumulation_sh_);
DRW_SHADER_FREE_SAFE(smaa_edge_detect_sh_);
DRW_SHADER_FREE_SAFE(smaa_aa_weight_sh_);
DRW_SHADER_FREE_SAFE(smaa_resolve_sh_);
DRW_SHADER_FREE_SAFE(overlay_depth_sh_);
}
void AntiAliasingPass::init(const SceneState &scene_state)
{
enabled_ = scene_state.draw_aa;
@@ -147,7 +132,7 @@ void AntiAliasingPass::sync(const SceneState &scene_state, SceneResources &resou
overlay_depth_ps_.state_set(DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS |
DRW_STATE_STENCIL_EQUAL);
overlay_depth_ps_.state_stencil(0x00, 0xFF, uint8_t(StencilBits::OBJECT_IN_FRONT));
overlay_depth_ps_.shader_set(overlay_depth_sh_);
overlay_depth_ps_.shader_set(ShaderCache::get().overlay_depth.get());
overlay_depth_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
if (!enabled_) {
@@ -171,14 +156,14 @@ void AntiAliasingPass::sync(const SceneState &scene_state, SceneResources &resou
taa_accumulation_ps_.state_set(scene_state.sample == 0 ?
DRW_STATE_WRITE_COLOR :
DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL);
taa_accumulation_ps_.shader_set(taa_accumulation_sh_);
taa_accumulation_ps_.shader_set(ShaderCache::get().taa_accumulation.get());
taa_accumulation_ps_.bind_texture("colorBuffer", &resources.color_tx);
taa_accumulation_ps_.push_constant("samplesWeights", weights_, 9);
taa_accumulation_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
smaa_edge_detect_ps_.init();
smaa_edge_detect_ps_.state_set(DRW_STATE_WRITE_COLOR);
smaa_edge_detect_ps_.shader_set(smaa_edge_detect_sh_);
smaa_edge_detect_ps_.shader_set(ShaderCache::get().smaa_edge_detect.get());
smaa_edge_detect_ps_.bind_texture("colorTex", &taa_accumulation_tx_);
smaa_edge_detect_ps_.push_constant("viewportMetrics", &smaa_viewport_metrics_, 1);
smaa_edge_detect_ps_.clear_color(float4(0.0f));
@@ -186,7 +171,7 @@ void AntiAliasingPass::sync(const SceneState &scene_state, SceneResources &resou
smaa_aa_weight_ps_.init();
smaa_aa_weight_ps_.state_set(DRW_STATE_WRITE_COLOR);
smaa_aa_weight_ps_.shader_set(smaa_aa_weight_sh_);
smaa_aa_weight_ps_.shader_set(ShaderCache::get().smaa_aa_weight.get());
smaa_aa_weight_ps_.bind_texture("edgesTex", &smaa_edge_tx_);
smaa_aa_weight_ps_.bind_texture("areaTex", smaa_area_tx_);
smaa_aa_weight_ps_.bind_texture("searchTex", smaa_search_tx_);
@@ -196,7 +181,7 @@ void AntiAliasingPass::sync(const SceneState &scene_state, SceneResources &resou
smaa_resolve_ps_.init();
smaa_resolve_ps_.state_set(DRW_STATE_WRITE_COLOR);
smaa_resolve_ps_.shader_set(smaa_resolve_sh_);
smaa_resolve_ps_.shader_set(ShaderCache::get().smaa_resolve.get());
smaa_resolve_ps_.bind_texture("blendTex", &smaa_weight_tx_);
smaa_resolve_ps_.bind_texture("colorTex", &taa_accumulation_tx_);
smaa_resolve_ps_.push_constant("viewportMetrics", &smaa_viewport_metrics_, 1);
@@ -55,15 +55,6 @@ static void square_to_circle(float x, float y, float &r, float &T)
}
}
DofPass::~DofPass()
{
DRW_SHADER_FREE_SAFE(prepare_sh_);
DRW_SHADER_FREE_SAFE(downsample_sh_);
DRW_SHADER_FREE_SAFE(blur1_sh_);
DRW_SHADER_FREE_SAFE(blur2_sh_);
DRW_SHADER_FREE_SAFE(resolve_sh_);
}
void DofPass::setup_samples()
{
float4 *sample = samples_buf_.begin();
@@ -111,14 +102,6 @@ void DofPass::init(const SceneState &scene_state)
return;
}
if (prepare_sh_ == nullptr) {
prepare_sh_ = GPU_shader_create_from_info_name("workbench_effect_dof_prepare");
downsample_sh_ = GPU_shader_create_from_info_name("workbench_effect_dof_downsample");
blur1_sh_ = GPU_shader_create_from_info_name("workbench_effect_dof_blur1");
blur2_sh_ = GPU_shader_create_from_info_name("workbench_effect_dof_blur2");
resolve_sh_ = GPU_shader_create_from_info_name("workbench_effect_dof_resolve");
}
offset_ = scene_state.sample / float(scene_state.samples_len);
int2 half_res = scene_state.resolution / 2;
@@ -180,7 +163,7 @@ void DofPass::sync(SceneResources &resources)
down_ps_.init();
down_ps_.state_set(DRW_STATE_WRITE_COLOR);
down_ps_.shader_set(prepare_sh_);
down_ps_.shader_set(ShaderCache::get().dof_prepare.get());
down_ps_.bind_texture("sceneColorTex", &resources.color_tx);
down_ps_.bind_texture("sceneDepthTex", &resources.depth_tx);
down_ps_.push_constant("invertedViewportSize", float2(DRW_viewport_invert_size_get()));
@@ -190,14 +173,14 @@ void DofPass::sync(SceneResources &resources)
down2_ps_.init();
down2_ps_.state_set(DRW_STATE_WRITE_COLOR);
down2_ps_.shader_set(downsample_sh_);
down2_ps_.shader_set(ShaderCache::get().dof_downsample.get());
down2_ps_.bind_texture("sceneColorTex", &source_tx_, sampler_state);
down2_ps_.bind_texture("inputCocTex", &coc_halfres_tx_, sampler_state);
down2_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
blur_ps_.init();
blur_ps_.state_set(DRW_STATE_WRITE_COLOR);
blur_ps_.shader_set(blur1_sh_);
blur_ps_.shader_set(ShaderCache::get().dof_blur1.get());
blur_ps_.bind_ubo("samples", samples_buf_);
blur_ps_.bind_texture("noiseTex", resources.jitter_tx);
blur_ps_.bind_texture("inputCocTex", &coc_halfres_tx_, sampler_state);
@@ -208,7 +191,7 @@ void DofPass::sync(SceneResources &resources)
blur2_ps_.init();
blur2_ps_.state_set(DRW_STATE_WRITE_COLOR);
blur2_ps_.shader_set(blur2_sh_);
blur2_ps_.shader_set(ShaderCache::get().dof_blur2.get());
blur2_ps_.bind_texture("inputCocTex", &coc_halfres_tx_, sampler_state);
blur2_ps_.bind_texture("blurTex", &blur_tx_);
blur2_ps_.push_constant("invertedViewportSize", float2(DRW_viewport_invert_size_get()));
@@ -216,7 +199,7 @@ void DofPass::sync(SceneResources &resources)
resolve_ps_.init();
resolve_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM);
resolve_ps_.shader_set(resolve_sh_);
resolve_ps_.shader_set(ShaderCache::get().dof_resolve.get());
resolve_ps_.bind_texture("halfResColorTex", &source_tx_, sampler_state);
resolve_ps_.bind_texture("sceneDepthTex", &resources.depth_tx);
resolve_ps_.push_constant("invertedViewportSize", float2(DRW_viewport_invert_size_get()));
@@ -14,21 +14,12 @@
namespace blender::workbench {
OutlinePass::~OutlinePass()
{
DRW_SHADER_FREE_SAFE(sh_);
}
void OutlinePass::init(const SceneState &scene_state)
{
enabled_ = scene_state.draw_outline;
if (!enabled_) {
return;
}
if (sh_ == nullptr) {
sh_ = GPU_shader_create_from_info_name("workbench_effect_outline");
}
}
void OutlinePass::sync(SceneResources &resources)
@@ -39,7 +30,7 @@ void OutlinePass::sync(SceneResources &resources)
ps_.init();
ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL);
ps_.shader_set(sh_);
ps_.shader_set(ShaderCache::get().outline.get());
ps_.bind_ubo("world_data", resources.world_buf);
ps_.bind_texture("objectIdBuffer", &resources.object_id_tx);
ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
@@ -593,6 +593,11 @@ static void workbench_instance_free(void *instance)
delete reinterpret_cast<workbench::Instance *>(instance);
}
static void workbench_engine_free()
{
workbench::ShaderCache::release();
}
static void workbench_view_update(void *vedata)
{
WORKBENCH_Data *ved = reinterpret_cast<WORKBENCH_Data *>(vedata);
@@ -806,7 +811,7 @@ DrawEngineType draw_engine_workbench = {
/*idname*/ N_("Workbench"),
/*vedata_size*/ &workbench_data_size,
/*engine_init*/ &workbench_engine_init,
/*engine_free*/ nullptr,
/*engine_free*/ &workbench_engine_free,
/*instance_free*/ &workbench_instance_free,
/*cache_init*/ &workbench_cache_init,
/*cache_populate*/ &workbench_cache_populate,
@@ -45,9 +45,8 @@ static inline eGeometryType geometry_type_from_object(Object *ob)
enum class ePipelineType {
OPAQUE = 0,
TRANSPARENT,
SHADOW,
};
static constexpr int pipeline_type_len = static_cast<int>(ePipelineType::SHADOW) + 1;
static constexpr int pipeline_type_len = static_cast<int>(ePipelineType::TRANSPARENT) + 1;
enum class eLightingType {
FLAT = 0,
@@ -32,10 +32,7 @@ void MeshPass::init_pass(SceneResources &resources, DRWState state, int clip_pla
}
}
void MeshPass::init_subpasses(ePipelineType pipeline,
eLightingType lighting,
bool clip,
ShaderCache &shaders)
void MeshPass::init_subpasses(ePipelineType pipeline, eLightingType lighting, bool clip)
{
texture_subpass_map_.clear();
@@ -49,9 +46,9 @@ void MeshPass::init_subpasses(ePipelineType pipeline,
pass_names[geom][shader] = std::string(get_name(geom_type)) +
std::string(get_name(shader_type));
}
GPUShader *sh = shaders.prepass_shader_get(pipeline, geom_type, shader_type, lighting, clip);
PassMain::Sub *pass = &sub(pass_names[geom][shader].c_str());
pass->shader_set(sh);
pass->shader_set(
ShaderCache::get().prepass_get(geom_type, pipeline, lighting, shader_type, clip));
passes_[geom][shader] = pass;
}
}
@@ -115,23 +112,20 @@ void OpaquePass::sync(const SceneState &scene_state, SceneResources &resources)
DRWState in_front_state = state | DRW_STATE_STENCIL_ALWAYS;
gbuffer_in_front_ps_.init_pass(resources, in_front_state, scene_state.clip_planes.size());
gbuffer_in_front_ps_.state_stencil(uint8_t(StencilBits::OBJECT_IN_FRONT), 0xFF, 0x00);
gbuffer_in_front_ps_.init_subpasses(
ePipelineType::OPAQUE, scene_state.lighting_type, clip, resources.shader_cache);
gbuffer_in_front_ps_.init_subpasses(ePipelineType::OPAQUE, scene_state.lighting_type, clip);
state |= DRW_STATE_STENCIL_NEQUAL;
gbuffer_ps_.init_pass(resources, state, scene_state.clip_planes.size());
gbuffer_ps_.state_stencil(
uint8_t(StencilBits::OBJECT), 0xFF, uint8_t(StencilBits::OBJECT_IN_FRONT));
gbuffer_ps_.init_subpasses(
ePipelineType::OPAQUE, scene_state.lighting_type, clip, resources.shader_cache);
gbuffer_ps_.init_subpasses(ePipelineType::OPAQUE, scene_state.lighting_type, clip);
deferred_ps_.init();
deferred_ps_.state_set(DRW_STATE_WRITE_COLOR);
deferred_ps_.shader_set(resources.shader_cache.resolve_shader_get(ePipelineType::OPAQUE,
scene_state.lighting_type,
scene_state.draw_cavity,
scene_state.draw_curvature,
scene_state.draw_shadows));
deferred_ps_.shader_set(ShaderCache::get().resolve_get(scene_state.lighting_type,
scene_state.draw_cavity,
scene_state.draw_curvature,
scene_state.draw_shadows));
deferred_ps_.push_constant("forceShadowing", false);
deferred_ps_.bind_ubo(WB_WORLD_SLOT, resources.world_buf);
deferred_ps_.bind_texture(WB_MATCAP_SLOT, resources.matcap_tx);
@@ -229,11 +223,6 @@ bool OpaquePass::is_empty() const
/** \name TransparentPass
* \{ */
TransparentPass::~TransparentPass()
{
DRW_SHADER_FREE_SAFE(resolve_sh_);
}
void TransparentPass::sync(const SceneState &scene_state, SceneResources &resources)
{
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_OIT |
@@ -246,20 +235,16 @@ void TransparentPass::sync(const SceneState &scene_state, SceneResources &resour
accumulation_ps_.state_stencil(
uint8_t(StencilBits::OBJECT), 0xFF, uint8_t(StencilBits::OBJECT_IN_FRONT));
accumulation_ps_.clear_color(float4(0.0f, 0.0f, 0.0f, 1.0f));
accumulation_ps_.init_subpasses(
ePipelineType::TRANSPARENT, scene_state.lighting_type, clip, resources.shader_cache);
accumulation_ps_.init_subpasses(ePipelineType::TRANSPARENT, scene_state.lighting_type, clip);
accumulation_in_front_ps_.init_pass(resources, state, scene_state.clip_planes.size());
accumulation_in_front_ps_.clear_color(float4(0.0f, 0.0f, 0.0f, 1.0f));
accumulation_in_front_ps_.init_subpasses(
ePipelineType::TRANSPARENT, scene_state.lighting_type, clip, resources.shader_cache);
ePipelineType::TRANSPARENT, scene_state.lighting_type, clip);
if (resolve_sh_ == nullptr) {
resolve_sh_ = GPU_shader_create_from_info_name("workbench_transparent_resolve");
}
resolve_ps_.init();
resolve_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA);
resolve_ps_.shader_set(resolve_sh_);
resolve_ps_.shader_set(ShaderCache::get().transparent_resolve.get());
resolve_ps_.bind_texture("transparentAccum", &accumulation_tx);
resolve_ps_.bind_texture("transparentRevealage", &reveal_tx);
resolve_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
@@ -314,11 +299,6 @@ bool TransparentPass::is_empty() const
/** \name TransparentDepthPass
* \{ */
TransparentDepthPass::~TransparentDepthPass()
{
DRW_SHADER_FREE_SAFE(merge_sh_);
}
void TransparentDepthPass::sync(const SceneState &scene_state, SceneResources &resources)
{
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL |
@@ -329,14 +309,10 @@ void TransparentDepthPass::sync(const SceneState &scene_state, SceneResources &r
DRWState in_front_state = state | DRW_STATE_STENCIL_ALWAYS;
in_front_ps_.init_pass(resources, in_front_state, scene_state.clip_planes.size());
in_front_ps_.state_stencil(uint8_t(StencilBits::OBJECT_IN_FRONT), 0xFF, 0x00);
in_front_ps_.init_subpasses(
ePipelineType::OPAQUE, eLightingType::FLAT, clip, resources.shader_cache);
in_front_ps_.init_subpasses(ePipelineType::OPAQUE, eLightingType::FLAT, clip);
if (merge_sh_ == nullptr) {
merge_sh_ = GPU_shader_create_from_info_name("workbench_merge_depth");
}
merge_ps_.init();
merge_ps_.shader_set(merge_sh_);
merge_ps_.shader_set(ShaderCache::get().merge_depth.get());
merge_ps_.state_set(DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_WRITE_STENCIL |
DRW_STATE_STENCIL_EQUAL);
merge_ps_.state_stencil(
@@ -348,8 +324,7 @@ void TransparentDepthPass::sync(const SceneState &scene_state, SceneResources &r
main_ps_.init_pass(resources, state, scene_state.clip_planes.size());
main_ps_.state_stencil(
uint8_t(StencilBits::OBJECT), 0xFF, uint8_t(StencilBits::OBJECT_IN_FRONT));
main_ps_.init_subpasses(
ePipelineType::OPAQUE, eLightingType::FLAT, clip, resources.shader_cache);
main_ps_.init_subpasses(ePipelineType::OPAQUE, eLightingType::FLAT, clip);
}
void TransparentDepthPass::draw(Manager &manager, View &view, SceneResources &resources)
@@ -19,28 +19,107 @@ namespace blender::workbench {
using namespace draw;
class ShaderCache {
public:
~ShaderCache();
GPUShader *prepass_shader_get(ePipelineType pipeline_type,
eGeometryType geometry_type,
eShaderType shader_type,
eLightingType lighting_type,
bool clip);
GPUShader *resolve_shader_get(ePipelineType pipeline_type,
eLightingType lighting_type,
bool cavity = false,
bool curvature = false,
bool shadow = false);
class StaticShader : NonCopyable {
private:
/* TODO(fclem): We might want to change to a Map since most shader will never be compiled. */
GPUShader *prepass_shader_cache_[pipeline_type_len][geometry_type_len][shader_type_len]
[lighting_type_len][2 /*clip*/] = {{{{{nullptr}}}}};
GPUShader *resolve_shader_cache_[pipeline_type_len][lighting_type_len][2 /*cavity*/]
[2 /*curvature*/][2 /*shadow*/] = {{{{{nullptr}}}}};
std::string info_name_;
GPUShader *shader_ = nullptr;
public:
StaticShader(std::string info_name) : info_name_(info_name) {}
StaticShader() = default;
StaticShader(StaticShader &&other) = default;
StaticShader &operator=(StaticShader &&other) = default;
~StaticShader()
{
DRW_SHADER_FREE_SAFE(shader_);
}
GPUShader *get()
{
if (!shader_) {
BLI_assert(!info_name_.empty());
shader_ = GPU_shader_create_from_info_name(info_name_.c_str());
}
return shader_;
}
};
class ShaderCache {
private:
static ShaderCache *static_cache;
StaticShader prepass_[geometry_type_len][pipeline_type_len][lighting_type_len][shader_type_len]
[2 /*clip*/];
StaticShader resolve_[lighting_type_len][2 /*cavity*/][2 /*curvature*/][2 /*shadow*/];
StaticShader shadow_[2 /*depth_pass*/][2 /*manifold*/][2 /*cap*/];
StaticShader volume_[2 /*smoke*/][3 /*interpolation*/][2 /*slice*/][2 /*coba*/];
public:
static ShaderCache &get();
static void release();
ShaderCache();
GPUShader *prepass_get(eGeometryType geometry_type,
ePipelineType pipeline_type,
eLightingType lighting_type,
eShaderType shader_type,
bool clip)
{
return prepass_[int(geometry_type)][int(pipeline_type)][int(lighting_type)][int(shader_type)]
[clip]
.get();
}
GPUShader *resolve_get(eLightingType lighting_type,
bool cavity = false,
bool curvature = false,
bool shadow = false)
{
return resolve_[int(lighting_type)][cavity][curvature][shadow].get();
}
GPUShader *shadow_get(bool depth_pass, bool manifold, bool cap = false)
{
return shadow_[depth_pass][manifold][cap].get();
}
GPUShader *volume_get(bool smoke, int interpolation, bool coba, bool slice)
{
return volume_[smoke][interpolation][coba][slice].get();
}
StaticShader extract_stencil = {"workbench_extract_stencil"};
/* Transparency */
StaticShader transparent_resolve = {"workbench_transparent_resolve"};
StaticShader merge_depth = {"workbench_merge_depth"};
/* ShadowView */
StaticShader shadow_visibility_dynamic = {
"workbench_shadow_visibility_compute_dynamic_pass_type"};
StaticShader shadow_visibility_static = {"workbench_shadow_visibility_compute_static_pass_type"};
/* Outline */
StaticShader outline = {"workbench_effect_outline"};
/* Dof */
StaticShader dof_prepare = {"workbench_effect_dof_prepare"};
StaticShader dof_downsample = {"workbench_effect_dof_downsample"};
StaticShader dof_blur1 = {"workbench_effect_dof_blur1"};
StaticShader dof_blur2 = {"workbench_effect_dof_blur2"};
StaticShader dof_resolve = {"workbench_effect_dof_resolve"};
/* AA */
StaticShader taa_accumulation = {"workbench_taa"};
StaticShader smaa_edge_detect = {"workbench_smaa_stage_0"};
StaticShader smaa_aa_weight = {"workbench_smaa_stage_1"};
StaticShader smaa_resolve = {"workbench_smaa_stage_2"};
StaticShader overlay_depth = {"workbench_overlay_depth"};
};
struct Material {
@@ -145,18 +224,8 @@ class CavityEffect {
class StencilViewWorkaround {
private:
Texture stencil_copy_tx_ = "stencil_copy_tx";
GPUShader *stencil_copy_sh_ = nullptr;
public:
StencilViewWorkaround()
{
stencil_copy_sh_ = GPU_shader_create_from_info_name("workbench_extract_stencil");
}
~StencilViewWorkaround()
{
DRW_SHADER_FREE_SAFE(stencil_copy_sh_);
}
/** WARNING: Should only be called at render time.
* When the workaround path is active,
* the returned texture won't stay in sync with the stencil_src,
@@ -178,7 +247,7 @@ class StencilViewWorkaround {
ps.clear_color(float4(0));
ps.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL);
ps.state_stencil(0x00, 0x00, 0xFF);
ps.shader_set(stencil_copy_sh_);
ps.shader_set(ShaderCache::get().extract_stencil.get());
ps.draw_procedural(GPU_PRIM_TRIS, 1, 3);
Framebuffer fb;
@@ -194,8 +263,6 @@ class StencilViewWorkaround {
struct SceneResources {
static const int jitter_tx_size = 64;
ShaderCache shader_cache = {};
StringRefNull current_matcap = {};
Texture matcap_tx = "matcap_tx";
@@ -239,10 +306,7 @@ class MeshPass : public PassMain {
bool is_empty() const;
void init_pass(SceneResources &resources, DRWState state, int clip_planes);
void init_subpasses(ePipelineType pipeline,
eLightingType lighting,
bool clip,
ShaderCache &shaders);
void init_subpasses(ePipelineType pipeline, eLightingType lighting, bool clip);
PassMain::Sub &get_subpass(eGeometryType geometry_type,
::Image *image = nullptr,
@@ -283,9 +347,6 @@ class OpaquePass {
};
class TransparentPass {
private:
GPUShader *resolve_sh_ = nullptr;
public:
TextureFromPool accumulation_tx = {"accumulation_accumulation_tx"};
TextureFromPool reveal_tx = {"accumulation_reveal_tx"};
@@ -296,17 +357,12 @@ class TransparentPass {
PassSimple resolve_ps_ = {"Transparent.Resolve"};
Framebuffer resolve_fb = {};
~TransparentPass();
void sync(const SceneState &scene_state, SceneResources &resources);
void draw(Manager &manager, View &view, SceneResources &resources, int2 resolution);
bool is_empty() const;
};
class TransparentDepthPass {
private:
GPUShader *merge_sh_ = nullptr;
public:
MeshPass main_ps_ = {"TransparentDepth.Main"};
Framebuffer main_fb = {"TransparentDepth.Main"};
@@ -315,13 +371,13 @@ class TransparentDepthPass {
PassSimple merge_ps_ = {"TransparentDepth.Merge"};
Framebuffer merge_fb = {"TransparentDepth.Merge"};
~TransparentDepthPass();
void sync(const SceneState &scene_state, SceneResources &resources);
void draw(Manager &manager, View &view, SceneResources &resources);
bool is_empty() const;
};
#define DEBUG_SHADOW_VOLUME 0
class ShadowPass {
private:
enum PassType { PASS = 0, FAIL, FORCED_FAIL, MAX };
@@ -335,12 +391,8 @@ class ShadowPass {
VisibilityBuf pass_visibility_buf_ = {};
VisibilityBuf fail_visibility_buf_ = {};
GPUShader *dynamic_pass_type_shader_;
GPUShader *static_pass_type_shader_;
public:
ShadowView();
~ShadowView();
ShadowView() : View("ShadowPass.View"){};
void setup(View &view, float3 light_direction, bool force_fail_method);
bool debug_object_culling(Object *ob);
@@ -368,16 +420,10 @@ class ShadowPass {
PassMain::Sub *passes_[PassType::MAX][2][2] = {{{nullptr}}};
PassMain::Sub *&get_pass_ptr(PassType type, bool manifold, bool cap = false);
/* [Is Pass Technique][Is Manifold][Is Cap] */
GPUShader *shaders_[2][2][2] = {{{nullptr}}};
GPUShader *get_shader(bool depth_pass, bool manifold, bool cap = false);
TextureFromPool depth_tx_ = {};
Framebuffer fb_ = {};
public:
~ShadowPass();
void init(const SceneState &scene_state, SceneResources &resources);
void update();
void sync();
@@ -407,11 +453,7 @@ class VolumePass {
GPUTexture *stencil_tx_ = nullptr;
GPUShader *shaders_[2 /*slice*/][2 /*coba*/][3 /*interpolation*/][2 /*smoke*/];
public:
~VolumePass();
void sync(SceneResources &resources);
void object_sync_volume(Manager &manager,
@@ -429,8 +471,6 @@ class VolumePass {
void draw(Manager &manager, View &view, SceneResources &resources);
private:
GPUShader *get_shader(bool slice, bool coba, int interpolation, bool smoke);
void draw_slice_ps(Manager &manager,
PassMain::Sub &ps,
ObjectRef &ob_ref,
@@ -450,12 +490,9 @@ class OutlinePass {
bool enabled_ = false;
PassSimple ps_ = PassSimple("Workbench.Outline");
GPUShader *sh_ = nullptr;
Framebuffer fb_ = Framebuffer("Workbench.Outline");
public:
~OutlinePass();
void init(const SceneState &scene_state);
void sync(SceneResources &resources);
void draw(Manager &manager, SceneResources &resources);
@@ -481,12 +518,6 @@ class DofPass {
Framebuffer blur2_fb_ = {};
Framebuffer resolve_fb_ = {};
GPUShader *prepare_sh_ = nullptr;
GPUShader *downsample_sh_ = nullptr;
GPUShader *blur1_sh_ = nullptr;
GPUShader *blur2_sh_ = nullptr;
GPUShader *resolve_sh_ = nullptr;
PassSimple down_ps_ = {"Workbench.DoF.DownSample"};
PassSimple down2_ps_ = {"Workbench.DoF.DownSample2"};
PassSimple blur_ps_ = {"Workbench.DoF.Blur"};
@@ -503,8 +534,6 @@ class DofPass {
float ratio_ = 0;
public:
~DofPass();
void init(const SceneState &scene_state);
void sync(SceneResources &resources);
void draw(Manager &manager, View &view, SceneResources &resources, int2 resolution);
@@ -542,12 +571,6 @@ class AntiAliasingPass {
float4 smaa_viewport_metrics_ = float4(0);
float smaa_mix_factor_ = 0;
GPUShader *taa_accumulation_sh_ = nullptr;
GPUShader *smaa_edge_detect_sh_ = nullptr;
GPUShader *smaa_aa_weight_sh_ = nullptr;
GPUShader *smaa_resolve_sh_ = nullptr;
GPUShader *overlay_depth_sh_ = nullptr;
PassSimple taa_accumulation_ps_ = {"TAA.Accumulation"};
PassSimple smaa_edge_detect_ps_ = {"SMAA.EdgeDetect"};
PassSimple smaa_aa_weight_ps_ = {"SMAA.BlendWeights"};
@@ -556,7 +579,6 @@ class AntiAliasingPass {
public:
AntiAliasingPass();
~AntiAliasingPass();
void init(const SceneState &scene_state);
void sync(const SceneState &scene_state, SceneResources &resources);
@@ -6,132 +6,92 @@
namespace blender::workbench {
ShaderCache::~ShaderCache()
ShaderCache *ShaderCache::static_cache = nullptr;
ShaderCache &ShaderCache::get()
{
for (auto i : IndexRange(pipeline_type_len)) {
for (auto j : IndexRange(geometry_type_len)) {
for (auto k : IndexRange(shader_type_len)) {
for (auto l : IndexRange(lighting_type_len)) {
for (auto m : IndexRange(2) /*clip*/) {
DRW_SHADER_FREE_SAFE(prepass_shader_cache_[i][j][k][l][m]);
if (!ShaderCache::static_cache) {
ShaderCache::static_cache = new ShaderCache();
}
return *ShaderCache::static_cache;
}
void ShaderCache::release()
{
if (ShaderCache::static_cache) {
delete ShaderCache::static_cache;
ShaderCache::static_cache = nullptr;
}
}
ShaderCache::ShaderCache()
{
std::string geometries[] = {"_mesh", "_curves", "_ptcloud"};
std::string pipelines[] = {"_opaque", "_transparent"};
std::string lightings[] = {"_flat", "_studio", "_matcap"};
std::string shaders[] = {"_material", "_texture"};
std::string clip[] = {"_no_clip", "_clip"};
static_assert(std::size(geometries) == geometry_type_len);
static_assert(std::size(pipelines) == pipeline_type_len);
static_assert(std::size(lightings) == lighting_type_len);
static_assert(std::size(shaders) == shader_type_len);
for (auto g : IndexRange(geometry_type_len)) {
for (auto p : IndexRange(pipeline_type_len)) {
for (auto l : IndexRange(lighting_type_len)) {
for (auto s : IndexRange(shader_type_len)) {
for (auto c : IndexRange(2) /*clip*/) {
prepass_[g][p][l][s][c] = {"workbench_prepass" + geometries[g] + pipelines[p] +
lightings[l] + shaders[s] + clip[c]};
}
}
}
}
}
for (auto i : IndexRange(pipeline_type_len)) {
for (auto j : IndexRange(lighting_type_len)) {
for (auto k : IndexRange(2) /*cavity*/) {
for (auto l : IndexRange(2) /*curvature*/) {
for (auto m : IndexRange(2) /*shadow*/) {
DRW_SHADER_FREE_SAFE(resolve_shader_cache_[i][j][k][l][m]);
}
std::string cavity[] = {"_no_cavity", "_cavity"};
std::string curvature[] = {"_no_curvature", "_curvature"};
std::string shadow[] = {"_no_shadow", "_shadow"};
for (auto l : IndexRange(lighting_type_len)) {
for (auto ca : IndexRange(2) /*cavity*/) {
for (auto cu : IndexRange(2) /*curvature*/) {
for (auto s : IndexRange(2) /*shadow*/) {
resolve_[l][ca][cu][s] = {"workbench_resolve_opaque" + lightings[l] + cavity[ca] +
curvature[cu] + shadow[s]};
}
}
}
}
}
GPUShader *ShaderCache::prepass_shader_get(ePipelineType pipeline_type,
eGeometryType geometry_type,
eShaderType shader_type,
eLightingType lighting_type,
bool clip)
{
GPUShader *&shader_ptr = prepass_shader_cache_[int(pipeline_type)][int(geometry_type)][int(
shader_type)][int(lighting_type)][clip ? 1 : 0];
std::string pass[] = {"_fail", "_pass"};
std::string manifold[] = {"_no_manifold", "_manifold"};
std::string caps[] = {"_no_caps", "_caps"};
if (shader_ptr != nullptr) {
return shader_ptr;
for (auto p : IndexRange(2) /*pass*/) {
for (auto m : IndexRange(2) /*manifold*/) {
for (auto c : IndexRange(2) /*caps*/) {
shadow_[p][m][c] = {"workbench_shadow" + pass[p] + manifold[m] + caps[c] +
(DEBUG_SHADOW_VOLUME ? "_debug" : "")};
}
}
}
std::string info_name = "workbench_prepass_";
switch (geometry_type) {
case eGeometryType::MESH:
info_name += "mesh_";
break;
case eGeometryType::CURVES:
info_name += "curves_";
break;
case eGeometryType::POINTCLOUD:
info_name += "ptcloud_";
break;
}
switch (pipeline_type) {
case ePipelineType::OPAQUE:
info_name += "opaque_";
break;
case ePipelineType::TRANSPARENT:
info_name += "transparent_";
break;
case ePipelineType::SHADOW:
info_name += "shadow_";
break;
}
switch (lighting_type) {
case eLightingType::FLAT:
info_name += "flat_";
break;
case eLightingType::STUDIO:
info_name += "studio_";
break;
case eLightingType::MATCAP:
info_name += "matcap_";
break;
}
switch (shader_type) {
case eShaderType::MATERIAL:
info_name += "material";
break;
case eShaderType::TEXTURE:
info_name += "texture";
break;
}
info_name += clip ? "_clip" : "_no_clip";
shader_ptr = GPU_shader_create_from_info_name(info_name.c_str());
return shader_ptr;
}
GPUShader *ShaderCache::resolve_shader_get(ePipelineType pipeline_type,
eLightingType lighting_type,
bool cavity,
bool curvature,
bool shadow)
{
GPUShader *&shader_ptr =
resolve_shader_cache_[int(pipeline_type)][int(lighting_type)][cavity][curvature][shadow];
std::string smoke[] = {"_smoke", "_object"};
std::string interpolation[] = {"_linear", "_cubic", "_closest"};
std::string coba[] = {"_no_coba", "_coba"};
std::string slice[] = {"_no_slice", "_slice"};
if (shader_ptr != nullptr) {
return shader_ptr;
for (auto sm : IndexRange(2) /*smoke*/) {
for (auto i : IndexRange(3) /*interpolation*/) {
for (auto c : IndexRange(2) /*coba*/) {
for (auto sl : IndexRange(2) /*slice*/) {
volume_[sm][i][c][sl] = {"workbench_volume" + smoke[sm] + interpolation[i] + coba[c] +
slice[sl]};
}
}
}
}
std::string info_name = "workbench_resolve_";
switch (pipeline_type) {
case ePipelineType::OPAQUE:
info_name += "opaque_";
break;
case ePipelineType::TRANSPARENT:
info_name += "transparent_";
break;
case ePipelineType::SHADOW:
BLI_assert_unreachable();
break;
}
switch (lighting_type) {
case eLightingType::FLAT:
info_name += "flat";
break;
case eLightingType::STUDIO:
info_name += "studio";
break;
case eLightingType::MATCAP:
info_name += "matcap";
break;
}
info_name += cavity ? "_cavity" : "_no_cavity";
info_name += curvature ? "_curvature" : "_no_curvature";
info_name += shadow ? "_shadow" : "_no_shadow";
shader_ptr = GPU_shader_create_from_info_name(info_name.c_str());
return shader_ptr;
}
} // namespace blender::workbench
@@ -21,17 +21,8 @@
#include "workbench_private.hh"
#define DEBUG_SHADOW_VOLUME 0
namespace blender::workbench {
ShadowPass::ShadowView::ShadowView() : View("ShadowPass.View"){};
ShadowPass::ShadowView::~ShadowView()
{
DRW_SHADER_FREE_SAFE(dynamic_pass_type_shader_);
DRW_SHADER_FREE_SAFE(static_pass_type_shader_);
}
void ShadowPass::ShadowView::setup(View &view, float3 light_direction, bool force_fail_method)
{
force_fail_method_ = force_fail_method;
@@ -244,18 +235,9 @@ void ShadowPass::ShadowView::compute_visibility(ObjectBoundsBuf &bounds,
if (do_visibility_) {
/* TODO(@pragma37): Use regular culling for the caps pass. */
if (dynamic_pass_type_shader_ == nullptr) {
dynamic_pass_type_shader_ = GPU_shader_create_from_info_name(
"workbench_shadow_visibility_compute_dynamic_pass_type");
}
if (static_pass_type_shader_ == nullptr) {
static_pass_type_shader_ = GPU_shader_create_from_info_name(
"workbench_shadow_visibility_compute_static_pass_type");
}
GPUShader *shader = current_pass_type_ == ShadowPass::FORCED_FAIL ? static_pass_type_shader_ :
dynamic_pass_type_shader_;
GPUShader *shader = current_pass_type_ == ShadowPass::FORCED_FAIL ?
ShaderCache::get().shadow_visibility_static.get() :
ShaderCache::get().shadow_visibility_dynamic.get();
GPU_shader_bind(shader);
GPU_shader_uniform_1i(shader, "resource_len", resource_len);
GPU_shader_uniform_1i(shader, "view_len", view_len_);
@@ -296,39 +278,11 @@ VisibilityBuf &ShadowPass::ShadowView::get_visibility_buffer()
return visibility_buf_;
}
ShadowPass::~ShadowPass()
{
for (int depth_pass : IndexRange(2)) {
for (int manifold : IndexRange(2)) {
for (int cap : IndexRange(2)) {
DRW_SHADER_FREE_SAFE(shaders_[depth_pass][manifold][cap]);
}
}
}
}
PassMain::Sub *&ShadowPass::get_pass_ptr(PassType type, bool manifold, bool cap /*=false*/)
{
return passes_[type][manifold][cap];
}
GPUShader *ShadowPass::get_shader(bool depth_pass, bool manifold, bool cap /*=false*/)
{
GPUShader *&shader = shaders_[depth_pass][manifold][cap];
if (shader == nullptr) {
std::string create_info_name = "workbench_shadow";
create_info_name += (depth_pass) ? "_pass" : "_fail";
create_info_name += (manifold) ? "_manifold" : "_no_manifold";
create_info_name += (cap) ? "_caps" : "_no_caps";
#if DEBUG_SHADOW_VOLUME
create_info_name += "_debug";
#endif
shader = GPU_shader_create_from_info_name(create_info_name.c_str());
}
return shader;
}
void ShadowPass::init(const SceneState &scene_state, SceneResources &resources)
{
enabled_ = scene_state.draw_shadows;
@@ -397,7 +351,7 @@ void ShadowPass::sync()
for (bool manifold : {false, true}) {
PassMain::Sub *&ps = get_pass_ptr(PASS, manifold);
ps = &pass_ps_.sub(manifold ? "manifold" : "non_manifold");
ps->shader_set(get_shader(true, manifold));
ps->shader_set(ShaderCache::get().shadow_get(true, manifold));
ps->bind_ubo("pass_data", pass_data_);
for (PassType fail_type : {FAIL, FORCED_FAIL}) {
@@ -405,12 +359,12 @@ void ShadowPass::sync()
PassMain::Sub *&ps = get_pass_ptr(fail_type, manifold, false);
ps = &ps_main.sub(manifold ? "NoCaps.manifold" : "NoCaps.non_manifold");
ps->shader_set(get_shader(false, manifold, false));
ps->shader_set(ShaderCache::get().shadow_get(false, manifold, false));
ps->bind_ubo("pass_data", pass_data_);
PassMain::Sub *&caps_ps = get_pass_ptr(fail_type, manifold, true);
caps_ps = &ps_main.sub(manifold ? "Caps.manifold" : "Caps.non_manifold");
caps_ps->shader_set(get_shader(false, manifold, true));
caps_ps->shader_set(ShaderCache::get().shadow_get(false, manifold, true));
caps_ps->bind_ubo("pass_data", pass_data_);
}
}
@@ -12,18 +12,6 @@
namespace blender::workbench {
VolumePass::~VolumePass()
{
GPUShader **sh_p = &shaders_[0][0][0][0];
const int n = ARRAY_SIZE(shaders_);
for (int i = 0; i < n; i++, sh_p++) {
GPUShader *sh = *sh_p;
if (sh) {
GPU_shader_free(sh);
}
}
}
void VolumePass::sync(SceneResources &resources)
{
active_ = false;
@@ -61,7 +49,8 @@ void VolumePass::object_sync_volume(Manager &manager,
const bool use_slice = (volume->display.axis_slice_method == AXIS_SLICE_SINGLE);
sub_ps.shader_set(get_shader(use_slice, false, volume->display.interpolation_method, false));
sub_ps.shader_set(ShaderCache::get().volume_get(
false, volume->display.interpolation_method, false, use_slice));
sub_ps.push_constant("do_depth_test", scene_state.shading.type >= OB_SOLID);
const float density_scale = volume->display.density *
@@ -128,7 +117,8 @@ void VolumePass::object_sync_modifier(Manager &manager,
const bool use_slice = settings.axis_slice_method == AXIS_SLICE_SINGLE;
sub_ps.shader_set(get_shader(use_slice, settings.use_coba, settings.interp_method, true));
sub_ps.shader_set(
ShaderCache::get().volume_get(true, settings.interp_method, settings.use_coba, use_slice));
sub_ps.push_constant("do_depth_test", scene_state.shading.type >= OB_SOLID);
if (settings.use_coba) {
@@ -202,33 +192,6 @@ void VolumePass::draw(Manager &manager, View &view, SceneResources &resources)
manager.submit(ps_, view);
}
GPUShader *VolumePass::get_shader(bool slice, bool coba, int interpolation, bool smoke)
{
GPUShader *&shader = shaders_[slice][coba][interpolation][smoke];
if (shader == nullptr) {
std::string create_info_name = "workbench_volume";
create_info_name += (smoke) ? "_smoke" : "_object";
switch (interpolation) {
case VOLUME_DISPLAY_INTERP_LINEAR:
create_info_name += "_linear";
break;
case VOLUME_DISPLAY_INTERP_CUBIC:
create_info_name += "_cubic";
break;
case VOLUME_DISPLAY_INTERP_CLOSEST:
create_info_name += "_closest";
break;
default:
BLI_assert_unreachable();
}
create_info_name += (coba) ? "_coba" : "_no_coba";
create_info_name += (slice) ? "_slice" : "_no_slice";
shader = GPU_shader_create_from_info_name(create_info_name.c_str());
}
return shader;
}
void VolumePass::draw_slice_ps(
Manager &manager, PassMain::Sub &ps, ObjectRef &ob_ref, int slice_axis_enum, float slice_depth)
{