Adding pipeline caching.

This commit is contained in:
Ben Vanik 2016-02-20 15:00:11 -08:00
parent 731ff52773
commit 5759f82276
2 changed files with 88 additions and 48 deletions

View File

@ -78,6 +78,12 @@ PipelineCache::PipelineCache(
}
PipelineCache::~PipelineCache() {
// Destroy all pipelines.
for (auto it : cached_pipelines_) {
vkDestroyPipeline(device_, it.second, nullptr);
}
cached_pipelines_.clear();
vkDestroyPipelineLayout(device_, pipeline_layout_, nullptr);
vkDestroyPipelineCache(device_, pipeline_cache_, nullptr);
@ -164,7 +170,9 @@ bool PipelineCache::ConfigurePipeline(VkCommandBuffer command_buffer,
return false;
}
if (!pipeline) {
pipeline = GetPipeline(render_state);
// Should have a hash key produced by the UpdateState pass.
uint64_t hash_key = XXH64_digest(&hash_state_);
pipeline = GetPipeline(render_state, hash_key);
current_pipeline_ = pipeline;
if (!pipeline) {
// Unable to create pipeline.
@ -192,7 +200,15 @@ void PipelineCache::ClearCache() {
// TODO(benvanik): caching.
}
VkPipeline PipelineCache::GetPipeline(const RenderState* render_state) {
VkPipeline PipelineCache::GetPipeline(const RenderState* render_state,
uint64_t hash_key) {
// Lookup the pipeline in the cache.
auto it = cached_pipelines_.find(hash_key);
if (it != cached_pipelines_.end()) {
// Found existing pipeline.
return it->second;
}
VkPipelineDynamicStateCreateInfo dynamic_state_info;
dynamic_state_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
@ -233,18 +249,19 @@ VkPipeline PipelineCache::GetPipeline(const RenderState* render_state) {
pipeline_info.subpass = 0;
pipeline_info.basePipelineHandle = nullptr;
pipeline_info.basePipelineIndex = 0;
VkPipeline pipeline = nullptr;
auto err = vkCreateGraphicsPipelines(device_, nullptr, 1, &pipeline_info,
nullptr, &pipeline);
CheckResult(err, "vkCreateGraphicsPipelines");
// TODO(benvanik): don't leak.
// Add to cache with the hash key for reuse.
cached_pipelines_.insert({hash_key, pipeline});
return pipeline;
}
VkShaderModule PipelineCache::GetGeometryShader(PrimitiveType primitive_type) {
VkShaderModule PipelineCache::GetGeometryShader(PrimitiveType primitive_type,
bool is_line_mode) {
switch (primitive_type) {
case PrimitiveType::kLineList:
case PrimitiveType::kLineStrip:
@ -267,6 +284,11 @@ VkShaderModule PipelineCache::GetGeometryShader(PrimitiveType primitive_type) {
return nullptr;
case PrimitiveType::kQuadList:
// TODO(benvanik): quad list geometry shader.
if (is_line_mode) {
//
} else {
//
}
return nullptr;
case PrimitiveType::kQuadStrip:
// TODO(benvanik): quad strip geometry shader.
@ -455,6 +477,27 @@ bool PipelineCache::SetDynamicState(VkCommandBuffer command_buffer,
// TODO(benvanik): push constants.
bool push_constants_dirty = full_update;
push_constants_dirty |=
SetShadowRegister(&regs.sq_program_cntl, XE_GPU_REG_SQ_PROGRAM_CNTL);
push_constants_dirty |=
SetShadowRegister(&regs.sq_context_misc, XE_GPU_REG_SQ_CONTEXT_MISC);
xenos::xe_gpu_program_cntl_t program_cntl;
program_cntl.dword_0 = regs.sq_program_cntl;
// Populate a register in the pixel shader with frag coord.
int ps_param_gen = (regs.sq_context_misc >> 8) & 0xFF;
// draw_batcher_.set_ps_param_gen(program_cntl.param_gen ? ps_param_gen : -1);
// Normal vertex shaders only, for now.
// TODO(benvanik): transform feedback/memexport.
// https://github.com/freedreno/freedreno/blob/master/includes/a2xx.xml.h
// 0 = normal
// 2 = point size
assert_true(program_cntl.vs_export_mode == 0 ||
program_cntl.vs_export_mode == 2);
return true;
}
@ -476,35 +519,14 @@ bool PipelineCache::SetShadowRegister(float* dest, uint32_t register_name) {
return true;
}
PipelineCache::UpdateStatus PipelineCache::UpdateRenderTargets() {
auto& regs = update_render_targets_regs_;
bool dirty = false;
dirty |= SetShadowRegister(&regs.rb_modecontrol, XE_GPU_REG_RB_MODECONTROL);
dirty |= SetShadowRegister(&regs.rb_surface_info, XE_GPU_REG_RB_SURFACE_INFO);
dirty |= SetShadowRegister(&regs.rb_color_info, XE_GPU_REG_RB_COLOR_INFO);
dirty |= SetShadowRegister(&regs.rb_color1_info, XE_GPU_REG_RB_COLOR1_INFO);
dirty |= SetShadowRegister(&regs.rb_color2_info, XE_GPU_REG_RB_COLOR2_INFO);
dirty |= SetShadowRegister(&regs.rb_color3_info, XE_GPU_REG_RB_COLOR3_INFO);
dirty |= SetShadowRegister(&regs.rb_color_mask, XE_GPU_REG_RB_COLOR_MASK);
dirty |= SetShadowRegister(&regs.rb_depthcontrol, XE_GPU_REG_RB_DEPTHCONTROL);
dirty |=
SetShadowRegister(&regs.rb_stencilrefmask, XE_GPU_REG_RB_STENCILREFMASK);
dirty |= SetShadowRegister(&regs.rb_depth_info, XE_GPU_REG_RB_DEPTH_INFO);
if (!dirty) {
return UpdateStatus::kCompatible;
}
SCOPE_profile_cpu_f("gpu");
return UpdateStatus::kMismatch;
}
PipelineCache::UpdateStatus PipelineCache::UpdateState(
VulkanShader* vertex_shader, VulkanShader* pixel_shader,
PrimitiveType primitive_type) {
bool mismatch = false;
// Reset hash so we can build it up.
XXH64_reset(&hash_state_, 0);
#define CHECK_UPDATE_STATUS(status, mismatch, error_message) \
{ \
if (status == UpdateStatus::kError) { \
@ -554,17 +576,16 @@ PipelineCache::UpdateStatus PipelineCache::UpdateShaderStages(
bool dirty = false;
dirty |= SetShadowRegister(&regs.pa_su_sc_mode_cntl,
XE_GPU_REG_PA_SU_SC_MODE_CNTL);
dirty |= SetShadowRegister(&regs.sq_program_cntl, XE_GPU_REG_SQ_PROGRAM_CNTL);
dirty |= SetShadowRegister(&regs.sq_context_misc, XE_GPU_REG_SQ_CONTEXT_MISC);
// dirty |= regs.vertex_shader != active_vertex_shader_;
// dirty |= regs.pixel_shader != active_pixel_shader_;
dirty |= regs.prim_type != primitive_type;
dirty |= regs.vertex_shader != vertex_shader;
dirty |= regs.pixel_shader != pixel_shader;
dirty |= regs.primitive_type != primitive_type;
XXH64_update(&hash_state_, &regs, sizeof(regs));
if (!dirty) {
return UpdateStatus::kCompatible;
}
// regs.vertex_shader = static_cast<VulkanShader*>(active_vertex_shader_);
// regs.pixel_shader = static_cast<VulkanShader*>(active_pixel_shader_);
regs.prim_type = primitive_type;
regs.vertex_shader = vertex_shader;
regs.pixel_shader = pixel_shader;
regs.primitive_type = primitive_type;
update_shader_stages_stage_count_ = 0;
@ -579,7 +600,14 @@ PipelineCache::UpdateStatus PipelineCache::UpdateShaderStages(
vertex_pipeline_stage.pName = "main";
vertex_pipeline_stage.pSpecializationInfo = nullptr;
auto geometry_shader = GetGeometryShader(primitive_type);
bool is_line_mode = false;
if (((regs.pa_su_sc_mode_cntl >> 3) & 0x3) != 0) {
uint32_t front_poly_mode = (regs.pa_su_sc_mode_cntl >> 5) & 0x7;
if (front_poly_mode == 1) {
is_line_mode = true;
}
}
auto geometry_shader = GetGeometryShader(primitive_type, is_line_mode);
if (geometry_shader) {
auto& geometry_pipeline_stage =
update_shader_stages_info_[update_shader_stages_stage_count_++];
@ -614,6 +642,7 @@ PipelineCache::UpdateStatus PipelineCache::UpdateVertexInputState(
bool dirty = false;
dirty |= vertex_shader != regs.vertex_shader;
XXH64_update(&hash_state_, &regs, sizeof(regs));
if (!dirty) {
return UpdateStatus::kCompatible;
}
@ -733,6 +762,7 @@ PipelineCache::UpdateStatus PipelineCache::UpdateInputAssemblyState(
XE_GPU_REG_PA_SU_SC_MODE_CNTL);
dirty |= SetShadowRegister(&regs.multi_prim_ib_reset_index,
XE_GPU_REG_VGT_MULTI_PRIM_IB_RESET_INDX);
XXH64_update(&hash_state_, &regs, sizeof(regs));
if (!dirty) {
return UpdateStatus::kCompatible;
}
@ -831,6 +861,7 @@ PipelineCache::UpdateStatus PipelineCache::UpdateRasterizationState(
XE_GPU_REG_PA_SC_SCREEN_SCISSOR_BR);
dirty |= SetShadowRegister(&regs.multi_prim_ib_reset_index,
XE_GPU_REG_VGT_MULTI_PRIM_IB_RESET_INDX);
XXH64_update(&hash_state_, &regs, sizeof(regs));
if (!dirty) {
return UpdateStatus::kCompatible;
}
@ -917,6 +948,7 @@ PipelineCache::UpdateStatus PipelineCache::UpdateDepthStencilState() {
dirty |= SetShadowRegister(&regs.rb_depthcontrol, XE_GPU_REG_RB_DEPTHCONTROL);
dirty |=
SetShadowRegister(&regs.rb_stencilrefmask, XE_GPU_REG_RB_STENCILREFMASK);
XXH64_update(&hash_state_, &regs, sizeof(regs));
if (!dirty) {
return UpdateStatus::kCompatible;
}
@ -976,6 +1008,7 @@ PipelineCache::UpdateStatus PipelineCache::UpdateColorBlendState() {
SetShadowRegister(&regs.rb_blendcontrol[2], XE_GPU_REG_RB_BLENDCONTROL_2);
dirty |=
SetShadowRegister(&regs.rb_blendcontrol[3], XE_GPU_REG_RB_BLENDCONTROL_3);
XXH64_update(&hash_state_, &regs, sizeof(regs));
if (!dirty) {
return UpdateStatus::kCompatible;
}

View File

@ -12,6 +12,8 @@
#include <unordered_map>
#include "third_party/xxhash/xxhash.h"
#include "xenia/gpu/register_file.h"
#include "xenia/gpu/spirv_shader_translator.h"
#include "xenia/gpu/vulkan/render_cache.h"
@ -59,11 +61,12 @@ class PipelineCache {
private:
// Creates or retrieves an existing pipeline for the currently configured
// state.
VkPipeline GetPipeline(const RenderState* render_state);
VkPipeline GetPipeline(const RenderState* render_state, uint64_t hash_key);
// Gets a geometry shader used to emulate the given primitive type.
// Returns nullptr if the primitive doesn't need to be emulated.
VkShaderModule GetGeometryShader(PrimitiveType primitive_type);
VkShaderModule GetGeometryShader(PrimitiveType primitive_type,
bool is_line_mode);
// Sets required dynamic state on the command buffer.
// Only state that has changed since the last call will be set unless
@ -89,6 +92,13 @@ class PipelineCache {
// TODO(benvanik): geometry shader cache.
// Hash state used to incrementally produce pipeline hashes during update.
// By the time the full update pass has run the hash will represent the
// current state in a way that can uniquely identify the produced VkPipeline.
XXH64_state_t hash_state_;
// All previously generated pipelines mapped by hash.
std::unordered_map<uint64_t, VkPipeline> cached_pipelines_;
// Previously used pipeline. This matches our current state settings
// and allows us to quickly(ish) reuse the pipeline if no registers have
// changed.
@ -101,7 +111,6 @@ class PipelineCache {
kError,
};
UpdateStatus UpdateRenderTargets();
UpdateStatus UpdateState(VulkanShader* vertex_shader,
VulkanShader* pixel_shader,
PrimitiveType primitive_type);
@ -137,18 +146,13 @@ class PipelineCache {
} update_render_targets_regs_;
struct UpdateShaderStagesRegisters {
PrimitiveType prim_type;
PrimitiveType primitive_type;
uint32_t pa_su_sc_mode_cntl;
uint32_t sq_program_cntl;
uint32_t sq_context_misc;
VulkanShader* vertex_shader;
VulkanShader* pixel_shader;
UpdateShaderStagesRegisters() { Reset(); }
void Reset() {
sq_program_cntl = 0;
vertex_shader = pixel_shader = nullptr;
}
void Reset() { std::memset(this, 0, sizeof(*this)); }
} update_shader_stages_regs_;
VkPipelineShaderStageCreateInfo update_shader_stages_info_[3];
uint32_t update_shader_stages_stage_count_ = 0;
@ -250,6 +254,9 @@ class PipelineCache {
float rb_blend_rgba[4];
uint32_t sq_program_cntl;
uint32_t sq_context_misc;
SetDynamicStateRegisters() { Reset(); }
void Reset() { std::memset(this, 0, sizeof(*this)); }
} set_dynamic_state_registers_;