Add pass bindings cache. Frame insights are empty.

This commit is contained in:
Chinmay Garde 2021-07-16 14:42:30 -07:00 committed by Dan Field
parent 66f9afe038
commit d9f645ba50

View File

@ -4,6 +4,9 @@
#include "impeller/compositor/render_pass.h"
#include <map>
#include <set>
#include "flutter/fml/closure.h"
#include "flutter/fml/logging.h"
#include "impeller/base/base.h"
@ -176,7 +179,147 @@ bool RenderPass::FinishEncoding(Allocator& transients_allocator) const {
return EncodeCommands(transients_allocator, pass);
}
static bool Bind(id<MTLRenderCommandEncoder> pass,
//-----------------------------------------------------------------------------
/// @brief Ensures that bindings on the pass are not redundantly set or
/// updated. Avoids making the driver do additional checks and makes
/// the frame insights during profiling and instrumentation not
/// complain about the same.
///
/// There should be no change to rendering if this caching was
/// absent.
///
struct PassBindingsCache {
PassBindingsCache(id<MTLRenderCommandEncoder> pass) : pass_(pass) {}
PassBindingsCache(const PassBindingsCache&) = delete;
PassBindingsCache(PassBindingsCache&&) = delete;
void SetRenderPipelineState(id<MTLRenderPipelineState> pipeline) {
if (pipeline == pipeline_) {
return;
}
pipeline_ = pipeline;
[pass_ setRenderPipelineState:pipeline_];
}
void SetDepthStencilState(id<MTLDepthStencilState> depth_stencil) {
if (depth_stencil_ == depth_stencil) {
return;
}
depth_stencil_ = depth_stencil;
[pass_ setDepthStencilState:depth_stencil_];
}
bool SetBuffer(ShaderStage stage,
uint64_t index,
uint64_t offset,
id<MTLBuffer> buffer) {
auto& buffers_map = buffers_[stage];
auto found = buffers_map.find(index);
if (found != buffers_map.end() && found->second.buffer == buffer) {
// The right buffer is bound. Check if its offset needs to be updated.
if (found->second.offset == offset) {
// Buffer and its offset is identical. Nothing to do.
return true;
}
// Only the offset needs to be updated.
found->second.offset = offset;
switch (stage) {
case ShaderStage::kVertex:
[pass_ setVertexBufferOffset:offset atIndex:index];
return true;
case ShaderStage::kFragment:
[pass_ setFragmentBufferOffset:offset atIndex:index];
return true;
default:
FML_DCHECK(false)
<< "Cannot update buffer offset of an unknown stage.";
return false;
}
return true;
}
buffers_map[index] = {buffer, offset};
switch (stage) {
case ShaderStage::kVertex:
[pass_ setVertexBuffer:buffer offset:offset atIndex:index];
return true;
case ShaderStage::kFragment:
[pass_ setFragmentBuffer:buffer offset:offset atIndex:index];
return true;
default:
FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage.";
return false;
}
return false;
}
bool SetTexture(ShaderStage stage, uint64_t index, id<MTLTexture> texture) {
auto& texture_map = textures_[stage];
auto found = texture_map.find(index);
if (found != texture_map.end() && found->second == texture) {
// Already bound.
return true;
}
texture_map[index] = texture;
switch (stage) {
case ShaderStage::kVertex:
[pass_ setVertexTexture:texture atIndex:index];
return true;
case ShaderStage::kFragment:
[pass_ setFragmentTexture:texture atIndex:index];
return true;
default:
FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage.";
return false;
}
return false;
}
bool SetSampler(ShaderStage stage,
uint64_t index,
id<MTLSamplerState> sampler) {
auto& sampler_map = samplers_[stage];
auto found = sampler_map.find(index);
if (found != sampler_map.end() && found->second == sampler) {
// Already bound.
return true;
}
sampler_map[index] = sampler;
switch (stage) {
case ShaderStage::kVertex:
[pass_ setVertexSamplerState:sampler atIndex:index];
return true;
case ShaderStage::kFragment:
[pass_ setFragmentSamplerState:sampler atIndex:index];
return true;
default:
FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage.";
return false;
}
return false;
}
private:
struct BufferOffsetPair {
id<MTLBuffer> buffer = nullptr;
size_t offset = 0u;
};
using BufferMap = std::map<uint64_t, BufferOffsetPair>;
using TextureMap = std::map<uint64_t, id<MTLTexture>>;
using SamplerMap = std::map<uint64_t, id<MTLSamplerState>>;
const id<MTLRenderCommandEncoder> pass_;
id<MTLRenderPipelineState> pipeline_ = nullptr;
id<MTLDepthStencilState> depth_stencil_ = nullptr;
std::map<ShaderStage, BufferMap> buffers_;
std::map<ShaderStage, TextureMap> textures_;
std::map<ShaderStage, SamplerMap> samplers_;
};
static bool Bind(PassBindingsCache& pass,
Allocator& allocator,
ShaderStage stage,
size_t bind_index,
@ -196,25 +339,10 @@ static bool Bind(id<MTLRenderCommandEncoder> pass,
return false;
}
switch (stage) {
case ShaderStage::kVertex:
[pass setVertexBuffer:buffer offset:view.range.offset atIndex:bind_index];
return true;
case ShaderStage::kFragment:
[pass setFragmentBuffer:buffer
offset:view.range.offset
atIndex:bind_index];
return true;
default:
FML_DLOG(ERROR) << "Cannot bind buffer to unknown shader stage.";
return false;
}
return false;
return pass.SetBuffer(stage, bind_index, view.range.offset, buffer);
}
static bool Bind(id<MTLRenderCommandEncoder> pass,
Allocator& allocator,
static bool Bind(PassBindingsCache& pass,
ShaderStage stage,
size_t bind_index,
const Texture& texture) {
@ -222,23 +350,10 @@ static bool Bind(id<MTLRenderCommandEncoder> pass,
return false;
}
switch (stage) {
case ShaderStage::kVertex:
[pass setVertexTexture:texture.GetMTLTexture() atIndex:bind_index];
return true;
case ShaderStage::kFragment:
[pass setFragmentTexture:texture.GetMTLTexture() atIndex:bind_index];
return true;
default:
FML_DLOG(ERROR) << "Cannot bind buffer to unknown shader stage.";
return false;
}
return false;
return pass.SetTexture(stage, bind_index, texture.GetMTLTexture());
}
static bool Bind(id<MTLRenderCommandEncoder> pass,
Allocator& allocator,
static bool Bind(PassBindingsCache& pass,
ShaderStage stage,
size_t bind_index,
const Sampler& sampler) {
@ -246,48 +361,33 @@ static bool Bind(id<MTLRenderCommandEncoder> pass,
return false;
}
switch (stage) {
case ShaderStage::kVertex:
[pass setVertexSamplerState:sampler.GetMTLSamplerState()
atIndex:bind_index];
return true;
case ShaderStage::kFragment:
[pass setFragmentSamplerState:sampler.GetMTLSamplerState()
atIndex:bind_index];
return true;
default:
FML_DLOG(ERROR) << "Cannot bind buffer to unknown shader stage.";
return false;
}
return false;
return pass.SetSampler(stage, bind_index, sampler.GetMTLSamplerState());
}
bool RenderPass::EncodeCommands(Allocator& allocator,
id<MTLRenderCommandEncoder> pass) const {
// There a numerous opportunities here to ensure bindings are not repeated.
// Stuff like setting the vertex buffer bindings over and over when just the
// offsets could be updated (as recommended in best practices).
auto bind_stage_resources = [&allocator, pass](const Bindings& bindings,
ShaderStage stage) -> bool {
PassBindingsCache pass_bindings(pass);
auto bind_stage_resources = [&allocator, &pass_bindings](
const Bindings& bindings,
ShaderStage stage) -> bool {
for (const auto buffer : bindings.buffers) {
if (!Bind(pass, allocator, stage, buffer.first, buffer.second)) {
if (!Bind(pass_bindings, allocator, stage, buffer.first, buffer.second)) {
return false;
}
}
for (const auto texture : bindings.textures) {
if (!Bind(pass, allocator, stage, texture.first, *texture.second)) {
if (!Bind(pass_bindings, stage, texture.first, *texture.second)) {
return false;
}
}
for (const auto sampler : bindings.samplers) {
if (!Bind(pass, allocator, stage, sampler.first, *sampler.second)) {
if (!Bind(pass_bindings, stage, sampler.first, *sampler.second)) {
return false;
}
}
return true;
};
fml::closure pop_debug_marker = [pass]() { [pass popDebugGroup]; };
for (const auto& command : commands_) {
if (command.index_count == 0u) {
@ -300,8 +400,10 @@ bool RenderPass::EncodeCommands(Allocator& allocator,
} else {
auto_pop_debug_marker.Release();
}
[pass setRenderPipelineState:command.pipeline->GetMTLRenderPipelineState()];
[pass setDepthStencilState:command.pipeline->GetMTLDepthStencilState()];
pass_bindings.SetRenderPipelineState(
command.pipeline->GetMTLRenderPipelineState());
pass_bindings.SetDepthStencilState(
command.pipeline->GetMTLDepthStencilState());
[pass setFrontFacingWinding:MTLWindingClockwise];
[pass setCullMode:MTLCullModeBack];
if (!bind_stage_resources(command.vertex_bindings, ShaderStage::kVertex)) {