Skip to content

Commit

Permalink
Properly caching sphere pipelines.
Browse files Browse the repository at this point in the history
- Don't resource leak pipelines when enable/disable depth/alpha.
- Sphere pipelines are now created on demand.
- Pipeline are cleaned up during shutdown.
  • Loading branch information
hysw committed Nov 15, 2023
1 parent 76f221d commit ca44522
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 41 deletions.
116 changes: 77 additions & 39 deletions benchmarks/graphics_pipeline/GraphicsBenchmarkApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,6 @@ void GraphicsBenchmarkApp::Setup()
mMeshesIndexer.AddDimension(kAvailableVbFormats.size());
mMeshesIndexer.AddDimension(kAvailableVertexAttrLayouts.size());
}
// Graphics pipelines indexer
{
mGraphicsPipelinesIndexer.AddDimension(kAvailableVsShaders.size());
mGraphicsPipelinesIndexer.AddDimension(kAvailablePsShaders.size());
mGraphicsPipelinesIndexer.AddDimension(kAvailableVbFormats.size());
mGraphicsPipelinesIndexer.AddDimension(kAvailableVertexAttrLayouts.size());
}
// Sampler
{
grfx::SamplerCreateInfo samplerCreateInfo = {};
Expand Down Expand Up @@ -240,6 +233,22 @@ void GraphicsBenchmarkApp::Setup()
}
}

void GraphicsBenchmarkApp::Shutdown()
{
for (const auto& kv : mPipelines) {
GetDevice()->DestroyGraphicsPipeline(kv.second);
}
mPipelines.clear();
GetDevice()->DestroyGraphicsPipeline(mSkyBox.pipeline);
mSkyBox.pipeline.Reset();
for (auto& pipeline : mQuadsPipelines) {
GetDevice()->DestroyGraphicsPipeline(pipeline);
pipeline.Reset();
}

// TODO: cleanup non-pipeline resources
}

void GraphicsBenchmarkApp::SetupSkyBoxResources()
{
// Textures
Expand Down Expand Up @@ -526,37 +535,67 @@ void GraphicsBenchmarkApp::SetupSpheresPipelines()
piCreateInfo.sets[0].pLayout = mSphere.descriptorSetLayout;
PPX_CHECKED_CALL(GetDevice()->CreatePipelineInterface(&piCreateInfo, &mSphere.pipelineInterface));

uint32_t pipelineIndex = 0;
for (size_t i = 0; i < kAvailableVsShaders.size(); i++) {
for (size_t j = 0; j < kAvailablePsShaders.size(); j++) {
for (size_t k = 0; k < kAvailableVbFormats.size(); k++) {
// Interleaved pipeline
grfx::GraphicsPipelineCreateInfo2 gpCreateInfo = {};
gpCreateInfo.VS = {mVsShaders[i].Get(), "vsmain"};
gpCreateInfo.PS = {mPsShaders[j].Get(), "psmain"};
gpCreateInfo.vertexInputState.bindingCount = 1;
gpCreateInfo.vertexInputState.bindings[0] = mSphereMeshes[2 * k + 0]->GetDerivedVertexBindings()[0];
gpCreateInfo.topology = grfx::PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
gpCreateInfo.polygonMode = grfx::POLYGON_MODE_FILL;
gpCreateInfo.cullMode = grfx::CULL_MODE_BACK;
gpCreateInfo.frontFace = grfx::FRONT_FACE_CCW;
gpCreateInfo.depthReadEnable = pDepthTestWrite->GetValue();
gpCreateInfo.depthWriteEnable = pDepthTestWrite->GetValue();
gpCreateInfo.blendModes[0] = pAlphaBlend->GetValue() ? grfx::BLEND_MODE_ALPHA : grfx::BLEND_MODE_NONE;
gpCreateInfo.outputState.renderTargetCount = 1;
gpCreateInfo.outputState.renderTargetFormats[0] = GetSwapchain()->GetColorFormat();
gpCreateInfo.outputState.depthStencilFormat = GetSwapchain()->GetDepthFormat();
gpCreateInfo.pPipelineInterface = mSphere.pipelineInterface;
PPX_CHECKED_CALL(GetDevice()->CreateGraphicsPipeline(&gpCreateInfo, &mPipelines[pipelineIndex++]));

// Position Planar Pipeline
gpCreateInfo.vertexInputState.bindingCount = 2;
gpCreateInfo.vertexInputState.bindings[0] = mSphereMeshes[2 * k + 1]->GetDerivedVertexBindings()[0];
gpCreateInfo.vertexInputState.bindings[1] = mSphereMeshes[2 * k + 1]->GetDerivedVertexBindings()[1];
PPX_CHECKED_CALL(GetDevice()->CreateGraphicsPipeline(&gpCreateInfo, &mPipelines[pipelineIndex++]));
}
}
// Pre-load the current pipeline variant.
GetSpherePipeline();
}

Result GraphicsBenchmarkApp::CompileSpherePipeline(const SpherePipelineKey& key)
{
if (mPipelines.find(key) != mPipelines.end()) {
return SUCCESS;
}
grfx::GraphicsPipelinePtr pipeline = nullptr;
bool interleaved = key.vertexAttributeLayout == 0;
size_t meshIndex = 2 * key.vertexFormat + (interleaved ? 0 : 1);
grfx::BlendMode blendMode = (key.enableAlphaBlend ? grfx::BLEND_MODE_ALPHA : grfx::BLEND_MODE_NONE);
// Interleaved pipeline
grfx::GraphicsPipelineCreateInfo2 gpCreateInfo = {};
gpCreateInfo.VS = {mVsShaders[key.vs].Get(), "vsmain"};
gpCreateInfo.PS = {mPsShaders[key.ps].Get(), "psmain"};
if (interleaved) {
gpCreateInfo.vertexInputState.bindingCount = 1;
gpCreateInfo.vertexInputState.bindings[0] = mSphereMeshes[meshIndex]->GetDerivedVertexBindings()[0];
}
else {
// Position Planar Pipeline
gpCreateInfo.vertexInputState.bindingCount = 2;
gpCreateInfo.vertexInputState.bindings[0] = mSphereMeshes[meshIndex]->GetDerivedVertexBindings()[0];
gpCreateInfo.vertexInputState.bindings[1] = mSphereMeshes[meshIndex]->GetDerivedVertexBindings()[1];
}
gpCreateInfo.topology = grfx::PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
gpCreateInfo.polygonMode = grfx::POLYGON_MODE_FILL;
gpCreateInfo.cullMode = grfx::CULL_MODE_BACK;
gpCreateInfo.frontFace = grfx::FRONT_FACE_CCW;
gpCreateInfo.depthReadEnable = key.enableDepth;
gpCreateInfo.depthWriteEnable = key.enableDepth;
gpCreateInfo.blendModes[0] = blendMode;
gpCreateInfo.outputState.renderTargetCount = 1;
gpCreateInfo.outputState.renderTargetFormats[0] = GetSwapchain()->GetColorFormat();
gpCreateInfo.outputState.depthStencilFormat = GetSwapchain()->GetDepthFormat();
gpCreateInfo.pPipelineInterface = mSphere.pipelineInterface;

Result ppxres = GetDevice()->CreateGraphicsPipeline(&gpCreateInfo, &pipeline);
if (ppxres == SUCCESS) {
// Insert a new pipeline to the cache.
// We don't delete old pipeline while we run benchmark
// which require this function to be synchronized to end of frame.
mPipelines[key] = pipeline;
}
return ppxres;
}

// Compile or load from cache currently required pipeline.
grfx::GraphicsPipelinePtr GraphicsBenchmarkApp::GetSpherePipeline()
{
SpherePipelineKey key = {};
key.ps = static_cast<uint8_t>(pKnobPs->GetIndex());
key.vs = static_cast<uint8_t>(pKnobVs->GetIndex());
key.vertexFormat = static_cast<uint8_t>(pKnobVbFormat->GetIndex());
key.vertexAttributeLayout = static_cast<uint8_t>(pKnobVertexAttrLayout->GetIndex());
key.enableDepth = pDepthTestWrite->GetValue();
key.enableAlphaBlend = pAlphaBlend->GetValue();
PPX_CHECKED_CALL(CompileSpherePipeline(key));
return mPipelines[key];
}

void GraphicsBenchmarkApp::SetupFullscreenQuadsPipelines()
Expand Down Expand Up @@ -1068,8 +1107,7 @@ void GraphicsBenchmarkApp::RecordCommandBufferSkyBox(PerFrame& frame)
void GraphicsBenchmarkApp::RecordCommandBufferSpheres(PerFrame& frame)
{
// Bind resources
const size_t pipelineIndex = mGraphicsPipelinesIndexer.GetIndex({pKnobVs->GetIndex(), pKnobPs->GetIndex(), pKnobVbFormat->GetIndex(), pKnobVertexAttrLayout->GetIndex()});
frame.cmd->BindGraphicsPipeline(mPipelines[pipelineIndex]);
frame.cmd->BindGraphicsPipeline(GetSpherePipeline());
const size_t meshIndex = mMeshesIndexer.GetIndex({pKnobLOD->GetIndex(), pKnobVbFormat->GetIndex(), pKnobVertexAttrLayout->GetIndex()});
frame.cmd->BindIndexBuffer(mSphereMeshes[meshIndex]);
frame.cmd->BindVertexBuffers(mSphereMeshes[meshIndex]);
Expand Down
54 changes: 52 additions & 2 deletions benchmarks/graphics_pipeline/GraphicsBenchmarkApp.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

#include <array>
#include <vector>
#include <unordered_map>

static constexpr uint32_t kMaxSphereInstanceCount = 3000;
static constexpr uint32_t kSeed = 89977;
Expand Down Expand Up @@ -98,6 +99,7 @@ class GraphicsBenchmarkApp
virtual void InitKnobs() override;
virtual void Config(ppx::ApplicationSettings& settings) override;
virtual void Setup() override;
virtual void Shutdown() override;
virtual void MouseMove(int32_t x, int32_t y, int32_t dx, int32_t dy, uint32_t buttons) override;
virtual void KeyDown(ppx::KeyCode key) override;
virtual void KeyUp(ppx::KeyCode key) override;
Expand Down Expand Up @@ -164,7 +166,51 @@ class GraphicsBenchmarkApp
std::string name;
};

struct SpherePipelineKey
{
uint8_t ps;
uint8_t vs;
uint8_t vertexFormat;
uint8_t vertexAttributeLayout;
bool enableDepth;
bool enableAlphaBlend;

static_assert(kAvailablePsShaders.size() < (1 << (8 * sizeof(ps))));
static_assert(kAvailableVsShaders.size() < (1 << (8 * sizeof(vs))));
static_assert(kAvailableVbFormats.size() < (1 << (8 * sizeof(vertexFormat))));
static_assert(kAvailableVertexAttrLayouts.size() < (1 << (8 * sizeof(vertexAttributeLayout))));

bool operator==(const SpherePipelineKey& rhs) const
{
return ps == rhs.ps &&
vs == rhs.vs &&
vertexFormat == rhs.vertexFormat &&
vertexAttributeLayout == rhs.vertexAttributeLayout &&
enableDepth == rhs.enableDepth &&
enableAlphaBlend == rhs.enableAlphaBlend;
}

struct Hash
{
// Not a good hash function, but good enough.
size_t operator()(const SpherePipelineKey& key) const
{
size_t res = 0;

res = (res << 2) | key.ps;
res = (res << 1) | key.vs;
res = (res << 1) | key.vertexFormat;
res = (res << 1) | key.vertexAttributeLayout;
res = (res << 1) | (key.enableDepth ? 1 : 0);
res = (res << 1) | (key.enableAlphaBlend ? 1 : 0);
return res;
}
};
};

private:
using SpherePipelineMap = std::unordered_map<SpherePipelineKey, grfx::GraphicsPipelinePtr, SpherePipelineKey::Hash>;

std::vector<PerFrame> mPerFrame;
FreeCamera mCamera;
float3 mLightPosition = float3(10, 250, 10);
Expand All @@ -188,10 +234,9 @@ class GraphicsBenchmarkApp
grfx::TexturePtr mNormalMapTexture;
grfx::TexturePtr mMetalRoughnessTexture;
grfx::TexturePtr mWhitePixelTexture;
std::array<grfx::GraphicsPipelinePtr, kPipelineCount> mPipelines;
SpherePipelineMap mPipelines;
std::array<grfx::MeshPtr, kMeshCount> mSphereMeshes;
std::vector<LOD> mSphereLODs;
MultiDimensionalIndexer mGraphicsPipelinesIndexer;
MultiDimensionalIndexer mMeshesIndexer;

// Fullscreen quads resources
Expand Down Expand Up @@ -246,6 +291,8 @@ class GraphicsBenchmarkApp
void SetupSpheresPipelines();
void SetupFullscreenQuadsPipelines();

Result CompileSpherePipeline(const SpherePipelineKey& key);

// Update descriptors
// Note: Descriptors can be updated within rendering loop
void UpdateSkyBoxDescriptors();
Expand Down Expand Up @@ -284,6 +331,9 @@ class GraphicsBenchmarkApp

// Loads shader at shaderBaseDir/fileName and creates it at ppShaderModule
void SetupShader(const std::filesystem::path& fileName, grfx::ShaderModule** ppShaderModule);

// Compile or load from cache currently required pipeline.
grfx::GraphicsPipelinePtr GetSpherePipeline();
};

#endif // BENCHMARKS_GRAPHICS_PIPELINE_GRAPHICS_BENCHMARK_APP_H

0 comments on commit ca44522

Please sign in to comment.