Skip to content

Commit

Permalink
[Impeller] Cache location in metadata. (flutter#46640)
Browse files Browse the repository at this point in the history
Hacky mutable stuff, but avoids recomputing string keys and hashing them multiple times per draw call. [See also
](https://docs.google.com/document/d/1msDN8D4_Hi2zR5JFaFLWDb0IHD3MekuuiaYLJ3Tt6to/edit?resourcekey=0-bZbENKIpXKtrnzU6kkLXcQ)

While we cache the uniform location, looking up this value requires computing a string key for every single uniform value on every single command. We can speed this up by stashing the uniform location in the shader metadata, which shouldn't change for the app lifetime. This avoids both the string computation and the hashmap usage.
  • Loading branch information
jonahwilliams authored Oct 25, 2023
1 parent b8e7d3c commit 6de8127
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 60 deletions.
138 changes: 87 additions & 51 deletions impeller/renderer/backend/gles/buffer_bindings_gles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@

#include "impeller/renderer/backend/gles/buffer_bindings_gles.h"

#include <algorithm>
#include <cstring>
#include <vector>

#include "impeller/base/config.h"
#include "impeller/base/validation.h"
#include "impeller/renderer/backend/gles/device_buffer_gles.h"
#include "impeller/renderer/backend/gles/formats_gles.h"
Expand Down Expand Up @@ -90,6 +88,9 @@ bool BufferBindingsGLES::ReadUniformsBindings(const ProcTableGLES& gl,

GLint uniform_count = 0;
gl.GetProgramiv(program, GL_ACTIVE_UNIFORMS, &uniform_count);

// Query the Program for all active uniform locations, and
// record this via normalized key.
for (GLint i = 0; i < uniform_count; i++) {
std::vector<GLchar> name;
name.resize(max_name_size);
Expand Down Expand Up @@ -139,11 +140,10 @@ bool BufferBindingsGLES::BindVertexAttributes(const ProcTableGLES& gl,
return true;
}

bool BufferBindingsGLES::BindUniformData(
const ProcTableGLES& gl,
Allocator& transients_allocator,
const Bindings& vertex_bindings,
const Bindings& fragment_bindings) const {
bool BufferBindingsGLES::BindUniformData(const ProcTableGLES& gl,
Allocator& transients_allocator,
const Bindings& vertex_bindings,
const Bindings& fragment_bindings) {
for (const auto& buffer : vertex_bindings.buffers) {
if (!BindUniformBuffer(gl, transients_allocator, buffer.second.view)) {
return false;
Expand Down Expand Up @@ -173,9 +173,58 @@ bool BufferBindingsGLES::UnbindVertexAttributes(const ProcTableGLES& gl) const {
return true;
}

GLint BufferBindingsGLES::ComputeTextureLocation(
const ShaderMetadata* metadata) {
auto location = binding_map_.find(metadata->name);
if (location != binding_map_.end()) {
return location->second[0];
}
auto& locations = binding_map_[metadata->name] = {};
auto computed_location =
uniform_locations_.find(CreateUniformMemberKey(metadata->name));
if (computed_location == uniform_locations_.end()) {
locations.push_back(-1);
} else {
locations.push_back(computed_location->second);
}
return locations[0];
}

const std::vector<GLint>& BufferBindingsGLES::ComputeUniformLocations(
const ShaderMetadata* metadata) {
auto location = binding_map_.find(metadata->name);
if (location != binding_map_.end()) {
return location->second;
}

// For each metadata member, look up the binding location and record
// it in the binding map.
auto& locations = binding_map_[metadata->name] = {};
for (const auto member : metadata->members) {
if (member.type == ShaderType::kVoid) {
// Void types are used for padding. We are obviously not going to find
// mappings for these. Keep going.
locations.push_back(-1);
continue;
}

size_t element_count = member.array_elements.value_or(1);
const auto member_key =
CreateUniformMemberKey(metadata->name, member.name, element_count > 1);
const auto computed_location = uniform_locations_.find(member_key);
if (computed_location == uniform_locations_.end()) {
// Uniform was not active.
locations.push_back(-1);
continue;
}
locations.push_back(computed_location->second);
}
return locations;
}

bool BufferBindingsGLES::BindUniformBuffer(const ProcTableGLES& gl,
Allocator& transients_allocator,
const BufferResource& buffer) const {
const BufferResource& buffer) {
const auto* metadata = buffer.GetMetadata();
auto device_buffer =
buffer.resource.buffer->GetDeviceBuffer(transients_allocator);
Expand All @@ -194,35 +243,25 @@ bool BufferBindingsGLES::BindUniformBuffer(const ProcTableGLES& gl,
return false;
}

for (const auto& member : metadata->members) {
if (member.type == ShaderType::kVoid) {
// Void types are used for padding. We are obviously not going to find
// mappings for these. Keep going.
const auto& locations = ComputeUniformLocations(metadata);
for (auto i = 0u; i < metadata->members.size(); i++) {
const auto& member = metadata->members[i];
auto location = locations[i];
// Void type or inactive uniform.
if (location == -1) {
continue;
}

size_t element_count = member.array_elements.value_or(1);

const auto member_key =
CreateUniformMemberKey(metadata->name, member.name, element_count > 1);
const auto location = uniform_locations_.find(member_key);
if (location == uniform_locations_.end()) {
// The list of uniform locations only contains "active" uniforms that are
// not optimized out. So this situation is expected to happen when unused
// uniforms are present in the shader.
continue;
}

size_t element_stride = member.byte_length / element_count;

auto* buffer_data =
reinterpret_cast<const GLfloat*>(buffer_ptr + member.offset);

std::vector<uint8_t> array_element_buffer;
if (element_count > 1) {
// When binding uniform arrays, the elements must be contiguous. Copy the
// uniforms to a temp buffer to eliminate any padding needed by the other
// backends.
// When binding uniform arrays, the elements must be contiguous. Copy
// the uniforms to a temp buffer to eliminate any padding needed by the
// other backends.
array_element_buffer.resize(member.size * element_count);
for (size_t element_i = 0; element_i < element_count; element_i++) {
std::memcpy(array_element_buffer.data() + element_i * member.size,
Expand All @@ -238,34 +277,34 @@ bool BufferBindingsGLES::BindUniformBuffer(const ProcTableGLES& gl,
case ShaderType::kFloat:
switch (member.size) {
case sizeof(Matrix):
gl.UniformMatrix4fv(location->second, // location
element_count, // count
GL_FALSE, // normalize
buffer_data // data
gl.UniformMatrix4fv(location, // location
element_count, // count
GL_FALSE, // normalize
buffer_data // data
);
continue;
case sizeof(Vector4):
gl.Uniform4fv(location->second, // location
element_count, // count
buffer_data // data
gl.Uniform4fv(location, // location
element_count, // count
buffer_data // data
);
continue;
case sizeof(Vector3):
gl.Uniform3fv(location->second, // location
element_count, // count
buffer_data // data
gl.Uniform3fv(location, // location
element_count, // count
buffer_data // data
);
continue;
case sizeof(Vector2):
gl.Uniform2fv(location->second, // location
element_count, // count
buffer_data // data
gl.Uniform2fv(location, // location
element_count, // count
buffer_data // data
);
continue;
case sizeof(Scalar):
gl.Uniform1fv(location->second, // location
element_count, // count
buffer_data // data
gl.Uniform1fv(location, // location
element_count, // count
buffer_data // data
);
continue;
}
Expand All @@ -288,7 +327,7 @@ bool BufferBindingsGLES::BindUniformBuffer(const ProcTableGLES& gl,
case ShaderType::kSampledImage:
case ShaderType::kSampler:
VALIDATION_LOG << "Could not bind uniform buffer data for key: "
<< member_key;
<< member.name;
return false;
}
}
Expand All @@ -297,7 +336,7 @@ bool BufferBindingsGLES::BindUniformBuffer(const ProcTableGLES& gl,

bool BufferBindingsGLES::BindTextures(const ProcTableGLES& gl,
const Bindings& bindings,
ShaderStage stage) const {
ShaderStage stage) {
size_t active_index = 0;
for (const auto& data : bindings.sampled_images) {
const auto& texture_gles = TextureGLES::Cast(*data.second.texture.resource);
Expand All @@ -306,11 +345,8 @@ bool BufferBindingsGLES::BindTextures(const ProcTableGLES& gl,
return false;
}

const auto uniform_key =
CreateUniformMemberKey(data.second.texture.GetMetadata()->name);
auto uniform = uniform_locations_.find(uniform_key);
if (uniform == uniform_locations_.end()) {
VALIDATION_LOG << "Could not find uniform for key: " << uniform_key;
auto location = ComputeTextureLocation(data.second.texture.GetMetadata());
if (location == -1) {
return false;
}

Expand Down Expand Up @@ -343,7 +379,7 @@ bool BufferBindingsGLES::BindTextures(const ProcTableGLES& gl,
//--------------------------------------------------------------------------
/// Set the texture uniform location.
///
gl.Uniform1i(uniform->second, active_index);
gl.Uniform1i(location, active_index);

//--------------------------------------------------------------------------
/// Bump up the active index at binding.
Expand Down
21 changes: 15 additions & 6 deletions impeller/renderer/backend/gles/buffer_bindings_gles.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@

#pragma once

#include <map>
#include <unordered_map>
#include <vector>

#include "flutter/fml/macros.h"
#include "impeller/core/shader_types.h"
#include "impeller/renderer/backend/gles/gles.h"
#include "impeller/renderer/backend/gles/proc_table_gles.h"
#include "impeller/renderer/command.h"
#include "impeller/renderer/vertex_descriptor.h"

namespace impeller {

Expand All @@ -38,7 +38,7 @@ class BufferBindingsGLES {
bool BindUniformData(const ProcTableGLES& gl,
Allocator& transients_allocator,
const Bindings& vertex_bindings,
const Bindings& fragment_bindings) const;
const Bindings& fragment_bindings);

bool UnbindVertexAttributes(const ProcTableGLES& gl) const;

Expand All @@ -55,15 +55,24 @@ class BufferBindingsGLES {
GLsizei offset = 0u;
};
std::vector<VertexAttribPointer> vertex_attrib_arrays_;
std::map<std::string, GLint> uniform_locations_;

std::unordered_map<std::string, GLint> uniform_locations_;

using BindingMap = std::unordered_map<std::string, std::vector<GLint>>;
BindingMap binding_map_ = {};

const std::vector<GLint>& ComputeUniformLocations(
const ShaderMetadata* metadata);

GLint ComputeTextureLocation(const ShaderMetadata* metadata);

bool BindUniformBuffer(const ProcTableGLES& gl,
Allocator& transients_allocator,
const BufferResource& buffer) const;
const BufferResource& buffer);

bool BindTextures(const ProcTableGLES& gl,
const Bindings& bindings,
ShaderStage stage) const;
ShaderStage stage);

BufferBindingsGLES(const BufferBindingsGLES&) = delete;

Expand Down
2 changes: 1 addition & 1 deletion impeller/renderer/backend/gles/pipeline_gles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const HandleGLES& PipelineGLES::GetProgramHandle() const {
return handle_;
}

const BufferBindingsGLES* PipelineGLES::GetBufferBindings() const {
BufferBindingsGLES* PipelineGLES::GetBufferBindings() const {
return buffer_bindings_.get();
}

Expand Down
2 changes: 1 addition & 1 deletion impeller/renderer/backend/gles/pipeline_gles.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class PipelineGLES final

[[nodiscard]] bool UnbindProgram() const;

const BufferBindingsGLES* GetBufferBindings() const;
BufferBindingsGLES* GetBufferBindings() const;

[[nodiscard]] bool BuildVertexDescriptor(const ProcTableGLES& gl,
GLuint program);
Expand Down
2 changes: 1 addition & 1 deletion impeller/renderer/backend/gles/render_pass_gles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ struct RenderPassData {
return false;
}

const auto& vertex_desc_gles = pipeline.GetBufferBindings();
auto vertex_desc_gles = pipeline.GetBufferBindings();

//--------------------------------------------------------------------------
/// Bind vertex and index buffers.
Expand Down

0 comments on commit 6de8127

Please sign in to comment.