vk: Implement optimized pipeline cache

This commit is contained in:
kd-11 2020-04-20 21:17:36 +03:00 committed by Ivan
parent bc5c4c9205
commit fc5b4026e1
4 changed files with 163 additions and 53 deletions

View File

@ -573,6 +573,12 @@ void VKGSRender::bind_texture_env()
void VKGSRender::bind_interpreter_texture_env()
{
if (current_fp_metadata.referenced_textures_mask == 0)
{
// Nothing to do
return;
}
std::array<VkDescriptorImageInfo, 68> texture_env;
VkDescriptorImageInfo fallback = { vk::null_sampler(), vk::null_image_view(*m_current_command_buffer, VK_IMAGE_VIEW_TYPE_1D)->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };

View File

@ -1573,22 +1573,12 @@ bool VKGSRender::load_program()
}
properties.renderpass_key = m_current_renderpass_key;
vk::glsl::program* active_interpreter = nullptr;
if (!m_interpreter_state && m_pipeline_properties == properties) [[likely]]
if (!m_interpreter_state && m_program) [[likely]]
{
// Nothing changed
if (m_shader_interpreter.is_interpreter(m_program))
{
if (g_cfg.video.shader_interpreter_mode == shader_interpreter_mode::forced)
{
return true;
}
active_interpreter = m_program;
}
else
if (!m_shader_interpreter.is_interpreter(m_program) &&
m_pipeline_properties == properties)
{
// Nothing changed
return true;
}
}
@ -1638,14 +1628,7 @@ bool VKGSRender::load_program()
m_interpreter_state = rsx::invalidate_pipeline_bits;
}
if (active_interpreter) [[likely]]
{
m_program = active_interpreter;
}
else
{
m_program = m_shader_interpreter.get(properties);
}
m_program = m_shader_interpreter.get(properties, current_fp_metadata);
}
m_pipeline_properties = properties;

View File

@ -80,7 +80,7 @@ namespace vk
// TODO: Bind textures if needed
}
void shader_interpreter::build_fs()
glsl::shader* shader_interpreter::build_fs(u64 compiler_options)
{
::glsl::shader_properties properties{};
properties.domain = ::glsl::program_domain::glsl_fragment_program;
@ -102,18 +102,63 @@ namespace vk
::glsl::insert_subheader_block(builder);
comp.insertConstants(builder);
const char* type_names[] = { "sampler1D", "sampler2D", "sampler3D", "samplerCube" };
for (int i = 0, bind_location = m_fragment_textures_start; i < 4; ++i)
if (compiler_options & program_common::interpreter::COMPILER_OPT_ENABLE_ALPHA_TEST_GE)
{
builder << "layout(set=0, binding=" << bind_location++ << ") " << "uniform " << type_names[i] << " " << type_names[i] << "_array[16];\n";
builder << "#define ALPHA_TEST_GEQUAL\n";
}
builder << "\n"
"#define IS_TEXTURE_RESIDENT(index) true\n"
"#define SAMPLER1D(index) sampler1D_array[index]\n"
"#define SAMPLER2D(index) sampler2D_array[index]\n"
"#define SAMPLER3D(index) sampler3D_array[index]\n"
"#define SAMPLERCUBE(index) samplerCube_array[index]\n\n";
if (compiler_options & program_common::interpreter::COMPILER_OPT_ENABLE_ALPHA_TEST_G)
{
builder << "#define ALPHA_TEST_GREATER\n";
}
if (compiler_options & program_common::interpreter::COMPILER_OPT_ENABLE_ALPHA_TEST_LE)
{
builder << "#define ALPHA_TEST_LEQUAL\n";
}
if (compiler_options & program_common::interpreter::COMPILER_OPT_ENABLE_ALPHA_TEST_L)
{
builder << "#define ALPHA_TEST_LESS\n";
}
if (compiler_options & program_common::interpreter::COMPILER_OPT_ENABLE_ALPHA_TEST_EQ)
{
builder << "#define ALPHA_TEST_EQUAL\n";
}
if (compiler_options & program_common::interpreter::COMPILER_OPT_ENABLE_ALPHA_TEST_NE)
{
builder << "#define ALPHA_TEST_NEQUAL\n";
}
if (!(compiler_options & program_common::interpreter::COMPILER_OPT_ENABLE_F32_EXPORT))
{
builder << "#define WITH_HALF_OUTPUT_REGISTER\n";
}
if (compiler_options & program_common::interpreter::COMPILER_OPT_ENABLE_DEPTH_EXPORT)
{
builder << "#define WITH_DEPTH_EXPORT\n";
}
const char* type_names[] = { "sampler1D", "sampler2D", "sampler3D", "samplerCube" };
if (compiler_options & program_common::interpreter::COMPILER_OPT_ENABLE_TEXTURES)
{
builder << "#define WITH_TEXTURES\n\n";
for (int i = 0, bind_location = m_fragment_textures_start; i < 4; ++i)
{
builder << "layout(set=0, binding=" << bind_location++ << ") " << "uniform " << type_names[i] << " " << type_names[i] << "_array[16];\n";
}
builder << "\n"
"#define IS_TEXTURE_RESIDENT(index) true\n"
"#define SAMPLER1D(index) sampler1D_array[index]\n"
"#define SAMPLER2D(index) sampler2D_array[index]\n"
"#define SAMPLER3D(index) sampler3D_array[index]\n"
"#define SAMPLERCUBE(index) samplerCube_array[index]\n\n";
}
builder <<
"layout(std430, binding=" << m_fragment_instruction_start << ") readonly restrict buffer FragmentInstructionBlock\n"
@ -130,8 +175,9 @@ namespace vk
builder << program_common::interpreter::get_fragment_interpreter();
const std::string s = builder.str();
m_fs.create(::glsl::program_domain::glsl_fragment_program, s);
m_fs.compile();
auto fs = new glsl::shader();
fs->create(::glsl::program_domain::glsl_fragment_program, s);
fs->compile();
// Prepare input table
const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table();
@ -156,6 +202,9 @@ namespace vk
in.name = std::string(type_names[i]) + "_array[16]";
m_fs_inputs.push_back(in);
}
m_fs_cache[compiler_options].reset(fs);
return fs;
}
std::pair<VkDescriptorSetLayout, VkPipelineLayout> shader_interpreter::create_layout(VkDevice dev)
@ -319,19 +368,22 @@ namespace vk
create_descriptor_pools(dev);
build_vs();
build_fs();
// TODO: Seed the cache
}
void shader_interpreter::destroy()
{
m_vs.destroy();
m_fs.destroy();
m_program_cache.clear();
m_descriptor_pool.destroy();
for (auto &fs : m_fs_cache)
{
fs.second->destroy();
}
m_vs.destroy();
m_fs_cache.clear();
if (m_shared_pipeline_layout)
{
vkDestroyPipelineLayout(m_device, m_shared_pipeline_layout, nullptr);
@ -345,8 +397,18 @@ namespace vk
}
}
glsl::program* shader_interpreter::link(const vk::pipeline_props& properties)
glsl::program* shader_interpreter::link(const vk::pipeline_props& properties, u64 compiler_opt)
{
glsl::shader* fs;
if (auto found = m_fs_cache.find(compiler_opt); found != m_fs_cache.end())
{
fs = found->second.get();
}
else
{
fs = build_fs(compiler_opt);
}
VkPipelineShaderStageCreateInfo shader_stages[2] = {};
shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
@ -355,7 +417,7 @@ namespace vk
shader_stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shader_stages[1].module = m_fs.get_handle();
shader_stages[1].module = fs->get_handle();
shader_stages[1].pName = "main";
VkDynamicState dynamic_state_descriptors[VK_DYNAMIC_STATE_RANGE_SIZE] = {};
@ -462,17 +524,63 @@ namespace vk
return new_descriptor_set;
}
glsl::program* shader_interpreter::get(const vk::pipeline_props& properties)
glsl::program* shader_interpreter::get(const vk::pipeline_props& properties, const program_hash_util::fragment_program_utils::fragment_program_metadata& metadata)
{
auto found = m_program_cache.find(properties);
pipeline_key key;
key.compiler_opt = 0;
key.properties = properties;
if (rsx::method_registers.alpha_test_enabled()) [[unlikely]]
{
switch (rsx::method_registers.alpha_func())
{
case rsx::comparison_function::always:
break;
case rsx::comparison_function::never:
return nullptr;
case rsx::comparison_function::greater_or_equal:
key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_ALPHA_TEST_GE;
break;
case rsx::comparison_function::greater:
key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_ALPHA_TEST_G;
break;
case rsx::comparison_function::less_or_equal:
key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_ALPHA_TEST_LE;
break;
case rsx::comparison_function::less:
key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_ALPHA_TEST_L;
break;
case rsx::comparison_function::equal:
key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_ALPHA_TEST_EQ;
break;
case rsx::comparison_function::not_equal:
key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_ALPHA_TEST_NE;
break;
}
}
if (rsx::method_registers.shader_control() & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_DEPTH_EXPORT;
if (rsx::method_registers.shader_control() & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_F32_EXPORT;
if (metadata.referenced_textures_mask) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_TEXTURES;
if (m_current_key == key) [[likely]]
{
return m_current_interpreter;
}
else
{
m_current_key = key;
}
auto found = m_program_cache.find(key);
if (found != m_program_cache.end()) [[likely]]
{
m_current_interpreter = found->second.get();
return m_current_interpreter;
}
m_current_interpreter = link(properties);
m_program_cache[properties].reset(m_current_interpreter);
m_current_interpreter = link(properties, key.compiler_opt);
m_program_cache[key].reset(m_current_interpreter);
return m_current_interpreter;
}

View File

@ -9,7 +9,6 @@ namespace vk
class shader_interpreter
{
glsl::shader m_vs;
glsl::shader m_fs;
std::vector<glsl::program_input> m_vs_inputs;
std::vector<glsl::program_input> m_fs_inputs;
@ -19,15 +18,27 @@ namespace vk
VkPipelineLayout m_shared_pipeline_layout = VK_NULL_HANDLE;
glsl::program* m_current_interpreter = nullptr;
struct key_hasher
struct pipeline_key
{
size_t operator()(const vk::pipeline_props& props) const
u64 compiler_opt;
vk::pipeline_props properties;
bool operator == (const pipeline_key& other) const
{
return rpcs3::hash_struct(props);
return other.compiler_opt == compiler_opt && other.properties == properties;
}
};
std::unordered_map<vk::pipeline_props, std::unique_ptr<glsl::program>, key_hasher> m_program_cache;
struct key_hasher
{
size_t operator()(const pipeline_key& key) const
{
return rpcs3::hash_struct(key.properties) ^ key.compiler_opt;
}
};
std::unordered_map<pipeline_key, std::unique_ptr<glsl::program>, key_hasher> m_program_cache;
std::unordered_map<u64, std::unique_ptr<glsl::shader>> m_fs_cache;
vk::descriptor_pool m_descriptor_pool;
size_t m_used_descriptors = 0;
@ -35,18 +46,20 @@ namespace vk
uint32_t m_fragment_instruction_start = 0;
uint32_t m_fragment_textures_start = 0;
pipeline_key m_current_key{};
std::pair<VkDescriptorSetLayout, VkPipelineLayout> create_layout(VkDevice dev);
void create_descriptor_pools(const vk::render_device& dev);
void build_vs();
void build_fs();
glsl::program* link(const vk::pipeline_props& properties);
glsl::shader* build_fs(u64 compiler_opt);
glsl::program* link(const vk::pipeline_props& properties, u64 compiler_opt);
public:
void init(const vk::render_device& dev);
void destroy();
glsl::program* get(const vk::pipeline_props& properties);
glsl::program* get(const vk::pipeline_props& properties, const program_hash_util::fragment_program_utils::fragment_program_metadata& metadata);
bool is_interpreter(const glsl::program* prog) const;
uint32_t get_vertex_instruction_location() const;