Skip to content

Commit 0b63e2c

Browse files
chinmaygardednfield
authored andcommitted
Shader data can now be compiled into the binary.
There is no more need to figure out separate packaging of shader data.
1 parent 3ac38d8 commit 0b63e2c

File tree

6 files changed

+244
-61
lines changed

6 files changed

+244
-61
lines changed

impeller/playground/BUILD.gn

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ impeller_component("playground") {
1414
]
1515

1616
public_deps = [
17+
"../entity:entity_shaders",
18+
"../fixtures:shader_fixtures",
1719
"../renderer",
1820
"//flutter/testing",
1921
"//third_party/glfw",

impeller/playground/playground.mm

+15-21
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include <sstream>
66

77
#include "flutter/fml/paths.h"
8+
#include "flutter/impeller/entity/entity_shaders.h"
9+
#include "flutter/impeller/fixtures/shader_fixtures.h"
810
#include "flutter/testing/testing.h"
911
#include "impeller/base/validation.h"
1012
#include "impeller/image/compressed_image.h"
@@ -28,27 +30,19 @@
2830

2931
namespace impeller {
3032

31-
static std::string ShaderLibraryDirectory() {
32-
auto path_result = fml::paths::GetExecutableDirectoryPath();
33-
if (!path_result.first) {
34-
return {};
35-
}
36-
return fml::paths::JoinPaths({path_result.second, "shaders"});
37-
}
33+
static std::vector<std::shared_ptr<fml::Mapping>>
34+
ShaderLibraryMappingsForPlayground() {
35+
return {
36+
std::make_shared<fml::NonOwnedMapping>(impeller_entity_shaders_data,
37+
impeller_entity_shaders_length),
38+
std::make_shared<fml::NonOwnedMapping>(impeller_shader_fixtures_data,
39+
impeller_shader_fixtures_length),
3840

39-
static std::vector<std::string> ShaderLibraryPathsForPlayground() {
40-
std::vector<std::string> paths;
41-
paths.emplace_back(fml::paths::JoinPaths(
42-
{ShaderLibraryDirectory(), "shader_fixtures.metallib"}));
43-
paths.emplace_back(
44-
fml::paths::JoinPaths({fml::paths::GetExecutableDirectoryPath().second,
45-
"shaders", "entity.metallib"}));
46-
return paths;
41+
};
4742
}
4843

4944
Playground::Playground()
50-
: renderer_(
51-
std::make_shared<ContextMTL>(ShaderLibraryPathsForPlayground())) {}
45+
: renderer_(ContextMTL::Create(ShaderLibraryMappingsForPlayground())) {}
5246

5347
Playground::~Playground() = default;
5448

@@ -170,10 +164,10 @@ static void PlaygroundKeyCallback(GLFWwindow* window,
170164
CompressedImage compressed_image(
171165
flutter::testing::OpenFixtureAsMapping(fixture_name));
172166
// The decoded image is immediately converted into RGBA as that format is
173-
// known to be supported everywhere. For image sources that don't need 32 bit
174-
// pixel strides, this is overkill. Since this is a test fixture we aren't
175-
// necessarily trying to eke out memory savings here and instead favor
176-
// simplicity.
167+
// known to be supported everywhere. For image sources that don't need 32
168+
// bit pixel strides, this is overkill. Since this is a test fixture we
169+
// aren't necessarily trying to eke out memory savings here and instead
170+
// favor simplicity.
177171
auto image = compressed_image.Decode().ConvertToRGBA();
178172
if (!image.IsValid()) {
179173
VALIDATION_LOG << "Could not find fixture named " << fixture_name;

impeller/renderer/backend/metal/context_mtl.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ namespace impeller {
2323
class ContextMTL final : public Context,
2424
public BackendCast<ContextMTL, Context> {
2525
public:
26-
ContextMTL(const std::vector<std::string>& shader_libraries);
26+
static std::shared_ptr<Context> Create(
27+
const std::vector<std::string>& shader_library_paths);
28+
29+
static std::shared_ptr<Context> Create(
30+
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data);
2731

2832
// |Context|
2933
~ContextMTL() override;
@@ -41,6 +45,8 @@ class ContextMTL final : public Context,
4145
std::shared_ptr<AllocatorMTL> transients_allocator_;
4246
bool is_valid_ = false;
4347

48+
ContextMTL(id<MTLDevice> device, NSArray<id<MTLLibrary>>* shader_libraries);
49+
4450
// |Context|
4551
bool IsValid() const override;
4652

impeller/renderer/backend/metal/context_mtl.mm

+112-38
Original file line numberDiff line numberDiff line change
@@ -14,36 +14,32 @@
1414

1515
namespace impeller {
1616

17-
static NSArray<id<MTLLibrary>>* ShaderLibrariesFromFiles(
18-
id<MTLDevice> device,
19-
const std::vector<std::string>& libraries_paths) {
20-
NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
21-
for (const auto& library_path : libraries_paths) {
22-
if (!fml::IsFile(library_path)) {
23-
VALIDATION_LOG << "Shader library does not exist at path '"
24-
<< library_path << "'";
25-
continue;
26-
}
27-
NSError* shader_library_error = nil;
28-
auto library = [device newLibraryWithFile:@(library_path.c_str())
29-
error:&shader_library_error];
30-
if (!library) {
31-
FML_LOG(ERROR) << "Could not create shader library: "
32-
<< shader_library_error.localizedDescription.UTF8String;
33-
continue;
34-
}
35-
[found_libraries addObject:library];
36-
}
37-
return found_libraries;
38-
}
39-
40-
ContextMTL::ContextMTL(const std::vector<std::string>& libraries_paths)
41-
: device_(::MTLCreateSystemDefaultDevice()) {
42-
// Setup device.
17+
ContextMTL::ContextMTL(id<MTLDevice> device,
18+
NSArray<id<MTLLibrary>>* shader_libraries)
19+
: device_(device) {
20+
// Validate device.
4321
if (!device_) {
22+
VALIDATION_LOG << "Could not setup valid Metal device.";
4423
return;
4524
}
4625

26+
// Setup the shader library.
27+
{
28+
if (shader_libraries == nil) {
29+
VALIDATION_LOG << "Shader libraries were null.";
30+
return;
31+
}
32+
33+
// std::make_shared disallowed because of private friend ctor.
34+
auto library = std::shared_ptr<ShaderLibraryMTL>(
35+
new ShaderLibraryMTL(shader_libraries));
36+
if (!library->IsValid()) {
37+
VALIDATION_LOG << "Could not create valid Metal shader library.";
38+
return;
39+
}
40+
shader_library_ = std::move(library);
41+
}
42+
4743
// Setup command queues.
4844
render_queue_ = device_.newCommandQueue;
4945
transfer_queue_ = device_.newCommandQueue;
@@ -55,18 +51,6 @@
5551
render_queue_.label = @"Impeller Render Queue";
5652
transfer_queue_.label = @"Impeller Transfer Queue";
5753

58-
// Setup the shader library.
59-
{
60-
// std::make_shared disallowed because of private friend ctor.
61-
auto library = std::shared_ptr<ShaderLibraryMTL>(new ShaderLibraryMTL(
62-
ShaderLibrariesFromFiles(device_, libraries_paths)));
63-
if (!library->IsValid()) {
64-
VALIDATION_LOG << "Could not create valid Metal shader library.";
65-
return;
66-
}
67-
shader_library_ = std::move(library);
68-
}
69-
7054
// Setup the pipeline library.
7155
{ //
7256
pipeline_library_ =
@@ -96,6 +80,96 @@
9680
is_valid_ = true;
9781
}
9882

83+
static NSArray<id<MTLLibrary>>* MTLShaderLibraryFromFilePaths(
84+
id<MTLDevice> device,
85+
const std::vector<std::string>& libraries_paths) {
86+
NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
87+
for (const auto& library_path : libraries_paths) {
88+
if (!fml::IsFile(library_path)) {
89+
VALIDATION_LOG << "Shader library does not exist at path '"
90+
<< library_path << "'";
91+
return nil;
92+
}
93+
NSError* shader_library_error = nil;
94+
auto library = [device newLibraryWithFile:@(library_path.c_str())
95+
error:&shader_library_error];
96+
if (!library) {
97+
FML_LOG(ERROR) << "Could not create shader library: "
98+
<< shader_library_error.localizedDescription.UTF8String;
99+
return nil;
100+
}
101+
[found_libraries addObject:library];
102+
}
103+
return found_libraries;
104+
}
105+
106+
static NSArray<id<MTLLibrary>>* MTLShaderLibraryFromFileData(
107+
id<MTLDevice> device,
108+
const std::vector<std::shared_ptr<fml::Mapping>>& libraries_data) {
109+
NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
110+
for (const auto& library_data : libraries_data) {
111+
if (library_data == nullptr) {
112+
FML_LOG(ERROR) << "Shader library data was null.";
113+
return nil;
114+
}
115+
116+
__block auto data = library_data;
117+
118+
auto dispatch_data =
119+
::dispatch_data_create(library_data->GetMapping(), // buffer
120+
library_data->GetSize(), // size
121+
dispatch_get_main_queue(), // queue
122+
^() {
123+
// We just need a reference.
124+
data.reset();
125+
} // destructor
126+
);
127+
if (!dispatch_data) {
128+
FML_LOG(ERROR) << "Could not wrap shader data in dispatch data.";
129+
return nil;
130+
}
131+
132+
NSError* shader_library_error = nil;
133+
auto library = [device newLibraryWithData:dispatch_data
134+
error:&shader_library_error];
135+
if (!library) {
136+
FML_LOG(ERROR) << "Could not create shader library: "
137+
<< shader_library_error.localizedDescription.UTF8String;
138+
return nil;
139+
}
140+
[found_libraries addObject:library];
141+
}
142+
return found_libraries;
143+
}
144+
145+
static id<MTLDevice> CreateMetalDevice() {
146+
return ::MTLCreateSystemDefaultDevice();
147+
}
148+
149+
std::shared_ptr<Context> ContextMTL::Create(
150+
const std::vector<std::string>& shader_library_paths) {
151+
auto device = CreateMetalDevice();
152+
auto context = std::shared_ptr<ContextMTL>(new ContextMTL(
153+
device, MTLShaderLibraryFromFilePaths(device, shader_library_paths)));
154+
if (!context->IsValid()) {
155+
FML_LOG(ERROR) << "Could not create Metal context.";
156+
return nullptr;
157+
}
158+
return context;
159+
}
160+
161+
std::shared_ptr<Context> ContextMTL::Create(
162+
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data) {
163+
auto device = CreateMetalDevice();
164+
auto context = std::shared_ptr<ContextMTL>(new ContextMTL(
165+
device, MTLShaderLibraryFromFileData(device, shader_libraries_data)));
166+
if (!context->IsValid()) {
167+
FML_LOG(ERROR) << "Could not create Metal context.";
168+
return nullptr;
169+
}
170+
return context;
171+
}
172+
99173
ContextMTL::~ContextMTL() = default;
100174

101175
bool ContextMTL::IsValid() const {

impeller/tools/impeller.gni

+33-1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ template("impeller_shaders") {
100100
assert(defined(invoker.shaders), "Impeller shaders must be specified.")
101101
assert(defined(invoker.name), "Name of the shader library must be specified.")
102102

103+
base_target_name = target_name
103104
impellerc_target_name = "impellerc_$target_name"
104105
compiled_action_foreach(impellerc_target_name) {
105106
tool = "//flutter/impeller/compiler:impellerc"
@@ -192,9 +193,40 @@ template("impeller_shaders") {
192193
]
193194
}
194195

196+
generate_embedder_data_sources = "embedded_data_gen_sources_$target_name"
197+
action(generate_embedder_data_sources) {
198+
metal_library_files = get_target_outputs(":$metal_library_target_name")
199+
metal_library_file = metal_library_files[0]
200+
inputs = [ metal_library_file ]
201+
output_header = "$target_gen_dir/$base_target_name.h"
202+
output_source = "$target_gen_dir/$base_target_name.c"
203+
outputs = [
204+
output_header,
205+
output_source,
206+
]
207+
args = [
208+
"--symbol-name",
209+
base_target_name,
210+
"--output-header",
211+
rebase_path(output_header),
212+
"--output-source",
213+
rebase_path(output_source),
214+
"--source",
215+
rebase_path(metal_library_file),
216+
]
217+
script = "//flutter/impeller/tools/xxd.py"
218+
deps = [ ":$metal_library_target_name" ]
219+
}
220+
221+
shader_embedded_data_target_name = "embedded_data_$target_name"
222+
source_set(shader_embedded_data_target_name) {
223+
sources = get_target_outputs(":$generate_embedder_data_sources")
224+
deps = [ ":$generate_embedder_data_sources" ]
225+
}
226+
195227
group(target_name) {
196228
public_deps = [
197-
":$metal_library_target_name",
229+
":$shader_embedded_data_target_name",
198230
":$shader_glue_target_name",
199231
]
200232
}

impeller/tools/xxd.py

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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+
import sys
6+
7+
import argparse
8+
import errno
9+
import os
10+
import struct
11+
12+
def MakeDirectories(path):
13+
try:
14+
os.makedirs(path)
15+
except OSError as exc:
16+
if exc.errno == errno.EEXIST and os.path.isdir(path):
17+
pass
18+
else:
19+
raise
20+
21+
# Dump the bytes of file into a C translation unit.
22+
# This can be used to embed the file contents into a binary.
23+
def Main():
24+
parser = argparse.ArgumentParser()
25+
parser.add_argument("--symbol-name",
26+
type=str, required=True,
27+
help="The name of the symbol referencing the data.")
28+
parser.add_argument("--output-header",
29+
type=str, required=True,
30+
help="The header file containing the symbol reference.")
31+
parser.add_argument("--output-source",
32+
type=str, required=True,
33+
help="The source file containing the file bytes.")
34+
parser.add_argument("--source",
35+
type=str, required=True,
36+
help="The source file whose contents to embed in the output source file.")
37+
38+
args = parser.parse_args()
39+
40+
assert(os.path.exists(args.source))
41+
42+
output_header = os.path.abspath(args.output_header)
43+
output_source = os.path.abspath(args.output_source)
44+
45+
MakeDirectories(os.path.dirname(output_header))
46+
MakeDirectories(os.path.dirname(output_source))
47+
48+
with open(args.source, "rb") as source, open(output_source, "w") as output:
49+
data_len = 0
50+
output.write(f"const unsigned char impeller_{args.symbol_name}_data[] =\n")
51+
output.write("{\n")
52+
while True:
53+
byte = source.read(1)
54+
if not byte:
55+
break
56+
data_len += 1
57+
output.write(f"{ord(byte)},")
58+
output.write("};\n")
59+
output.write(f"const unsigned long impeller_{args.symbol_name}_length = {data_len};\n")
60+
61+
with open(output_header, "w") as output:
62+
output.write("#pragma once\n")
63+
output.write("#ifdef __cplusplus\n")
64+
output.write("extern \"C\" {\n")
65+
output.write("#endif\n\n")
66+
67+
output.write(f"extern unsigned char impeller_{args.symbol_name}_data[];\n")
68+
output.write(f"extern unsigned long impeller_{args.symbol_name}_length;\n\n")
69+
70+
output.write("#ifdef __cplusplus\n")
71+
output.write("}\n")
72+
output.write("#endif\n")
73+
74+
if __name__ == '__main__':
75+
Main()

0 commit comments

Comments
 (0)