Skip to content

Commit a6b0939

Browse files
chinmaygardednfield
authored andcommitted
Add support for instanced rendering and shader storage buffers. (flutter#95)
1 parent f2d2351 commit a6b0939

File tree

9 files changed

+169
-12
lines changed

9 files changed

+169
-12
lines changed

impeller/compiler/code_gen_template.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,16 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader {
4444
}; // struct {{def.name}} (size {{def.byte_length}})
4545
{% endfor %}
4646
{% endif %}
47-
{% if length(uniform_buffers) > 0 %}
47+
{% if length(buffers) > 0 %}
4848
4949
// ===========================================================================
50-
// Stage Uniforms ============================================================
50+
// Stage Uniform & Storage Buffers ===========================================
5151
// ===========================================================================
52-
{% for uniform in uniform_buffers %}
52+
{% for buffer in buffers %}
5353
54-
static constexpr auto kResource{{camel_case(uniform.name)}} = ShaderUniformSlot<{{uniform.name}}> { // {{uniform.name}}
55-
"{{uniform.name}}", // name
56-
{{uniform.msl_res_0}}u, // binding
54+
static constexpr auto kResource{{camel_case(buffer.name)}} = ShaderUniformSlot<{{buffer.name}}> { // {{buffer.name}}
55+
"{{buffer.name}}", // name
56+
{{buffer.msl_res_0}}u, // binding
5757
};
5858
{% endfor %}
5959
{% endif %}
@@ -119,6 +119,10 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader {
119119
};
120120
{% endif %}
121121
122+
// ===========================================================================
123+
// Resource Binding Utilities ================================================
124+
// ===========================================================================
125+
122126
{% for proto in bind_prototypes %}
123127
/// {{proto.docstring}}
124128
static {{proto.return_type}} Bind{{proto.name}}({% for arg in proto.args %}

impeller/compiler/reflector.cc

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,27 @@ std::optional<nlohmann::json> Reflector::GenerateTemplateArguments() const {
166166

167167
const auto shader_resources = compiler_->get_shader_resources();
168168

169-
if (auto uniform_buffers = ReflectResources(shader_resources.uniform_buffers);
170-
uniform_buffers.has_value()) {
171-
root["uniform_buffers"] = std::move(uniform_buffers.value());
172-
} else {
173-
return std::nullopt;
169+
// Uniform and storage buffers.
170+
{
171+
auto& buffers = root["buffers"] = nlohmann::json::array_t{};
172+
if (auto uniform_buffers_json =
173+
ReflectResources(shader_resources.uniform_buffers);
174+
uniform_buffers_json.has_value()) {
175+
for (const auto& uniform_buffer : uniform_buffers_json.value()) {
176+
buffers.emplace_back(std::move(uniform_buffer));
177+
}
178+
} else {
179+
return std::nullopt;
180+
}
181+
if (auto storage_buffers_json =
182+
ReflectResources(shader_resources.storage_buffers);
183+
storage_buffers_json.has_value()) {
184+
for (const auto& uniform_buffer : storage_buffers_json.value()) {
185+
buffers.emplace_back(std::move(uniform_buffer));
186+
}
187+
} else {
188+
return std::nullopt;
189+
}
174190
}
175191

176192
{
@@ -742,6 +758,25 @@ std::vector<Reflector::BindPrototype> Reflector::ReflectBindPrototypes(
742758
.argument_name = "view",
743759
});
744760
}
761+
for (const auto& storage_buffer : resources.storage_buffers) {
762+
auto& proto = prototypes.emplace_back(BindPrototype{});
763+
proto.return_type = "bool";
764+
proto.name = ConvertToCamelCase(storage_buffer.name);
765+
{
766+
std::stringstream stream;
767+
stream << "Bind storage buffer for resource named " << storage_buffer.name
768+
<< ".";
769+
proto.docstring = stream.str();
770+
}
771+
proto.args.push_back(BindPrototypeArgument{
772+
.type_name = "Command&",
773+
.argument_name = "command",
774+
});
775+
proto.args.push_back(BindPrototypeArgument{
776+
.type_name = "BufferView",
777+
.argument_name = "view",
778+
});
779+
}
745780
for (const auto& sampled_image : resources.sampled_images) {
746781
auto& proto = prototypes.emplace_back(BindPrototype{});
747782
proto.return_type = "bool";

impeller/fixtures/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ impeller_shaders("shader_fixtures") {
1212
shaders = [
1313
"box_fade.vert",
1414
"box_fade.frag",
15+
"instanced_draw.vert",
16+
"instanced_draw.frag",
1517
"test_texture.vert",
1618
"test_texture.frag",
1719
]

impeller/fixtures/instanced_draw.frag

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
in vec4 v_color;
6+
7+
out vec4 frag_color;
8+
9+
void main() {
10+
frag_color = v_color;
11+
}

impeller/fixtures/instanced_draw.vert

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
uniform FrameInfo {
6+
mat4 mvp;
7+
} frame_info;
8+
9+
readonly buffer InstanceInfo {
10+
vec4 colors[];
11+
} instance_info;
12+
13+
in vec2 vtx;
14+
15+
out vec4 v_color;
16+
17+
void main () {
18+
gl_Position = frame_info.mvp *
19+
vec4(vtx.x + 105.0 * gl_InstanceIndex,
20+
vtx.y + 105.0 * gl_InstanceIndex,
21+
0.0,
22+
1.0);
23+
v_color = instance_info.colors[gl_InstanceIndex];
24+
}

impeller/renderer/backend/metal/render_pass_mtl.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ static bool Bind(PassBindingsCache& pass,
482482
indexType:ToMTLIndexType(command.index_type)
483483
indexBuffer:mtl_index_buffer
484484
indexBufferOffset:command.index_buffer.range.offset
485-
instanceCount:1u
485+
instanceCount:command.instance_count
486486
baseVertex:command.base_vertex
487487
baseInstance:0u];
488488
}

impeller/renderer/command.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ struct Command {
9090
/// If unset, no scissor is applied.
9191
///
9292
std::optional<IRect> scissor;
93+
size_t instance_count = 1u;
9394

9495
bool BindVertices(const VertexBuffer& buffer);
9596

impeller/renderer/host_buffer.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,29 @@ class HostBuffer final : public std::enable_shared_from_this<HostBuffer>,
4949
);
5050
}
5151

52+
//----------------------------------------------------------------------------
53+
/// @brief Emplace storage buffer data onto the host buffer. Ensure that
54+
/// backend specific uniform alignment requirements are respected.
55+
///
56+
/// @param[in] uniform The storage buffer to emplace onto the buffer.
57+
///
58+
/// @tparam StorageBufferType The type of the shader storage buffer.
59+
///
60+
/// @return The buffer view.
61+
///
62+
template <
63+
class StorageBufferType,
64+
class = std::enable_if_t<std::is_standard_layout_v<StorageBufferType>>>
65+
[[nodiscard]] BufferView EmplaceStorageBuffer(
66+
const std::vector<StorageBufferType>& buffer) {
67+
const auto alignment =
68+
std::max(alignof(StorageBufferType), DefaultUniformAlignment());
69+
return Emplace(buffer.data(), // buffer
70+
buffer.size() * sizeof(StorageBufferType), // size
71+
alignment // alignment
72+
);
73+
}
74+
5275
//----------------------------------------------------------------------------
5376
/// @brief Emplace non-uniform data (like contiguous vertices) onto the
5477
/// host buffer.

impeller/renderer/renderer_unittests.cc

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#include "flutter/testing/testing.h"
77
#include "impeller/fixtures/box_fade.frag.h"
88
#include "impeller/fixtures/box_fade.vert.h"
9+
#include "impeller/fixtures/instanced_draw.frag.h"
10+
#include "impeller/fixtures/instanced_draw.vert.h"
911
#include "impeller/fixtures/test_texture.frag.h"
1012
#include "impeller/fixtures/test_texture.vert.h"
1113
#include "impeller/geometry/path_builder.h"
@@ -270,5 +272,60 @@ TEST_F(RendererTest, CanRenderToTexture) {
270272
ASSERT_TRUE(r2t_pass->EncodeCommands(*context->GetTransientsAllocator()));
271273
}
272274

275+
TEST_F(RendererTest, CanRenderInstanced) {
276+
using VS = InstancedDrawVertexShader;
277+
using FS = InstancedDrawFragmentShader;
278+
279+
VertexBufferBuilder<VS::PerVertexData> builder;
280+
281+
ASSERT_TRUE(
282+
Tessellator{}.Tessellate(FillType::kPositive,
283+
PathBuilder{}
284+
.AddRect(Rect::MakeXYWH(10, 10, 100, 100))
285+
.TakePath()
286+
.CreatePolyline(),
287+
[&builder](Point vtx) {
288+
VS::PerVertexData data;
289+
data.vtx = vtx;
290+
builder.AppendVertex(data);
291+
}));
292+
293+
auto pipeline =
294+
GetContext()
295+
->GetPipelineLibrary()
296+
->GetRenderPipeline(
297+
PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(
298+
*GetContext())
299+
->SetSampleCount(SampleCount::kCount4))
300+
.get();
301+
ASSERT_TRUE(pipeline && pipeline->IsValid());
302+
303+
Command cmd;
304+
cmd.pipeline = pipeline;
305+
cmd.label = "InstancedDraw";
306+
307+
static constexpr size_t kInstancesCount = 5u;
308+
std::vector<VS::InstanceInfo> instances;
309+
for (size_t i = 0; i < kInstancesCount; i++) {
310+
VS::InstanceInfo info;
311+
info.colors = Color::Random();
312+
instances.emplace_back(info);
313+
}
314+
315+
ASSERT_TRUE(OpenPlaygroundHere([&](RenderPass& pass) -> bool {
316+
VS::FrameInfo frame_info;
317+
frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize());
318+
VS::BindFrameInfo(cmd,
319+
pass.GetTransientsBuffer().EmplaceUniform(frame_info));
320+
VS::BindInstanceInfo(
321+
cmd, pass.GetTransientsBuffer().EmplaceStorageBuffer(instances));
322+
cmd.BindVertices(builder.CreateVertexBuffer(pass.GetTransientsBuffer()));
323+
324+
cmd.instance_count = kInstancesCount;
325+
pass.AddCommand(cmd);
326+
return true;
327+
}));
328+
}
329+
273330
} // namespace testing
274331
} // namespace impeller

0 commit comments

Comments
 (0)