Skip to content

Commit

Permalink
Add support for instanced rendering and shader storage buffers. (#95)
Browse files Browse the repository at this point in the history
  • Loading branch information
chinmaygarde authored and dnfield committed Apr 27, 2022
1 parent f2d2351 commit a6b0939
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 12 deletions.
16 changes: 10 additions & 6 deletions impeller/compiler/code_gen_template.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,16 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader {
}; // struct {{def.name}} (size {{def.byte_length}})
{% endfor %}
{% endif %}
{% if length(uniform_buffers) > 0 %}
{% if length(buffers) > 0 %}
// ===========================================================================
// Stage Uniforms ============================================================
// Stage Uniform & Storage Buffers ===========================================
// ===========================================================================
{% for uniform in uniform_buffers %}
{% for buffer in buffers %}
static constexpr auto kResource{{camel_case(uniform.name)}} = ShaderUniformSlot<{{uniform.name}}> { // {{uniform.name}}
"{{uniform.name}}", // name
{{uniform.msl_res_0}}u, // binding
static constexpr auto kResource{{camel_case(buffer.name)}} = ShaderUniformSlot<{{buffer.name}}> { // {{buffer.name}}
"{{buffer.name}}", // name
{{buffer.msl_res_0}}u, // binding
};
{% endfor %}
{% endif %}
Expand Down Expand Up @@ -119,6 +119,10 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader {
};
{% endif %}
// ===========================================================================
// Resource Binding Utilities ================================================
// ===========================================================================
{% for proto in bind_prototypes %}
/// {{proto.docstring}}
static {{proto.return_type}} Bind{{proto.name}}({% for arg in proto.args %}
Expand Down
45 changes: 40 additions & 5 deletions impeller/compiler/reflector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,27 @@ std::optional<nlohmann::json> Reflector::GenerateTemplateArguments() const {

const auto shader_resources = compiler_->get_shader_resources();

if (auto uniform_buffers = ReflectResources(shader_resources.uniform_buffers);
uniform_buffers.has_value()) {
root["uniform_buffers"] = std::move(uniform_buffers.value());
} else {
return std::nullopt;
// Uniform and storage buffers.
{
auto& buffers = root["buffers"] = nlohmann::json::array_t{};
if (auto uniform_buffers_json =
ReflectResources(shader_resources.uniform_buffers);
uniform_buffers_json.has_value()) {
for (const auto& uniform_buffer : uniform_buffers_json.value()) {
buffers.emplace_back(std::move(uniform_buffer));
}
} else {
return std::nullopt;
}
if (auto storage_buffers_json =
ReflectResources(shader_resources.storage_buffers);
storage_buffers_json.has_value()) {
for (const auto& uniform_buffer : storage_buffers_json.value()) {
buffers.emplace_back(std::move(uniform_buffer));
}
} else {
return std::nullopt;
}
}

{
Expand Down Expand Up @@ -742,6 +758,25 @@ std::vector<Reflector::BindPrototype> Reflector::ReflectBindPrototypes(
.argument_name = "view",
});
}
for (const auto& storage_buffer : resources.storage_buffers) {
auto& proto = prototypes.emplace_back(BindPrototype{});
proto.return_type = "bool";
proto.name = ConvertToCamelCase(storage_buffer.name);
{
std::stringstream stream;
stream << "Bind storage buffer for resource named " << storage_buffer.name
<< ".";
proto.docstring = stream.str();
}
proto.args.push_back(BindPrototypeArgument{
.type_name = "Command&",
.argument_name = "command",
});
proto.args.push_back(BindPrototypeArgument{
.type_name = "BufferView",
.argument_name = "view",
});
}
for (const auto& sampled_image : resources.sampled_images) {
auto& proto = prototypes.emplace_back(BindPrototype{});
proto.return_type = "bool";
Expand Down
2 changes: 2 additions & 0 deletions impeller/fixtures/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ impeller_shaders("shader_fixtures") {
shaders = [
"box_fade.vert",
"box_fade.frag",
"instanced_draw.vert",
"instanced_draw.frag",
"test_texture.vert",
"test_texture.frag",
]
Expand Down
11 changes: 11 additions & 0 deletions impeller/fixtures/instanced_draw.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

in vec4 v_color;

out vec4 frag_color;

void main() {
frag_color = v_color;
}
24 changes: 24 additions & 0 deletions impeller/fixtures/instanced_draw.vert
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

uniform FrameInfo {
mat4 mvp;
} frame_info;

readonly buffer InstanceInfo {
vec4 colors[];
} instance_info;

in vec2 vtx;

out vec4 v_color;

void main () {
gl_Position = frame_info.mvp *
vec4(vtx.x + 105.0 * gl_InstanceIndex,
vtx.y + 105.0 * gl_InstanceIndex,
0.0,
1.0);
v_color = instance_info.colors[gl_InstanceIndex];
}
2 changes: 1 addition & 1 deletion impeller/renderer/backend/metal/render_pass_mtl.mm
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ static bool Bind(PassBindingsCache& pass,
indexType:ToMTLIndexType(command.index_type)
indexBuffer:mtl_index_buffer
indexBufferOffset:command.index_buffer.range.offset
instanceCount:1u
instanceCount:command.instance_count
baseVertex:command.base_vertex
baseInstance:0u];
}
Expand Down
1 change: 1 addition & 0 deletions impeller/renderer/command.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ struct Command {
/// If unset, no scissor is applied.
///
std::optional<IRect> scissor;
size_t instance_count = 1u;

bool BindVertices(const VertexBuffer& buffer);

Expand Down
23 changes: 23 additions & 0 deletions impeller/renderer/host_buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,29 @@ class HostBuffer final : public std::enable_shared_from_this<HostBuffer>,
);
}

//----------------------------------------------------------------------------
/// @brief Emplace storage buffer data onto the host buffer. Ensure that
/// backend specific uniform alignment requirements are respected.
///
/// @param[in] uniform The storage buffer to emplace onto the buffer.
///
/// @tparam StorageBufferType The type of the shader storage buffer.
///
/// @return The buffer view.
///
template <
class StorageBufferType,
class = std::enable_if_t<std::is_standard_layout_v<StorageBufferType>>>
[[nodiscard]] BufferView EmplaceStorageBuffer(
const std::vector<StorageBufferType>& buffer) {
const auto alignment =
std::max(alignof(StorageBufferType), DefaultUniformAlignment());
return Emplace(buffer.data(), // buffer
buffer.size() * sizeof(StorageBufferType), // size
alignment // alignment
);
}

//----------------------------------------------------------------------------
/// @brief Emplace non-uniform data (like contiguous vertices) onto the
/// host buffer.
Expand Down
57 changes: 57 additions & 0 deletions impeller/renderer/renderer_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include "flutter/testing/testing.h"
#include "impeller/fixtures/box_fade.frag.h"
#include "impeller/fixtures/box_fade.vert.h"
#include "impeller/fixtures/instanced_draw.frag.h"
#include "impeller/fixtures/instanced_draw.vert.h"
#include "impeller/fixtures/test_texture.frag.h"
#include "impeller/fixtures/test_texture.vert.h"
#include "impeller/geometry/path_builder.h"
Expand Down Expand Up @@ -270,5 +272,60 @@ TEST_F(RendererTest, CanRenderToTexture) {
ASSERT_TRUE(r2t_pass->EncodeCommands(*context->GetTransientsAllocator()));
}

TEST_F(RendererTest, CanRenderInstanced) {
using VS = InstancedDrawVertexShader;
using FS = InstancedDrawFragmentShader;

VertexBufferBuilder<VS::PerVertexData> builder;

ASSERT_TRUE(
Tessellator{}.Tessellate(FillType::kPositive,
PathBuilder{}
.AddRect(Rect::MakeXYWH(10, 10, 100, 100))
.TakePath()
.CreatePolyline(),
[&builder](Point vtx) {
VS::PerVertexData data;
data.vtx = vtx;
builder.AppendVertex(data);
}));

auto pipeline =
GetContext()
->GetPipelineLibrary()
->GetRenderPipeline(
PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(
*GetContext())
->SetSampleCount(SampleCount::kCount4))
.get();
ASSERT_TRUE(pipeline && pipeline->IsValid());

Command cmd;
cmd.pipeline = pipeline;
cmd.label = "InstancedDraw";

static constexpr size_t kInstancesCount = 5u;
std::vector<VS::InstanceInfo> instances;
for (size_t i = 0; i < kInstancesCount; i++) {
VS::InstanceInfo info;
info.colors = Color::Random();
instances.emplace_back(info);
}

ASSERT_TRUE(OpenPlaygroundHere([&](RenderPass& pass) -> bool {
VS::FrameInfo frame_info;
frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize());
VS::BindFrameInfo(cmd,
pass.GetTransientsBuffer().EmplaceUniform(frame_info));
VS::BindInstanceInfo(
cmd, pass.GetTransientsBuffer().EmplaceStorageBuffer(instances));
cmd.BindVertices(builder.CreateVertexBuffer(pass.GetTransientsBuffer()));

cmd.instance_count = kInstancesCount;
pass.AddCommand(cmd);
return true;
}));
}

} // namespace testing
} // namespace impeller

0 comments on commit a6b0939

Please sign in to comment.