blob: af3ec0de7640bde36ff4ea07981377f78de2aa9d [file] [log] [blame]
// 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.
#include "impeller/renderer/backend/metal/context_mtl.h"
#include <Foundation/Foundation.h>
#include "flutter/fml/file.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/paths.h"
#include "impeller/renderer/backend/metal/sampler_library_mtl.h"
#include "impeller/renderer/sampler_descriptor.h"
namespace impeller {
ContextMTL::ContextMTL(id<MTLDevice> device,
NSArray<id<MTLLibrary>>* shader_libraries)
: device_(device) {
// Validate device.
if (!device_) {
VALIDATION_LOG << "Could not setup valid Metal device.";
return;
}
// Setup the shader library.
{
if (shader_libraries == nil) {
VALIDATION_LOG << "Shader libraries were null.";
return;
}
// std::make_shared disallowed because of private friend ctor.
auto library = std::shared_ptr<ShaderLibraryMTL>(
new ShaderLibraryMTL(shader_libraries));
if (!library->IsValid()) {
VALIDATION_LOG << "Could not create valid Metal shader library.";
return;
}
shader_library_ = std::move(library);
}
// Setup command queues.
render_queue_ = device_.newCommandQueue;
transfer_queue_ = device_.newCommandQueue;
if (!render_queue_ || !transfer_queue_) {
return;
}
render_queue_.label = @"Impeller Render Queue";
transfer_queue_.label = @"Impeller Transfer Queue";
// Setup the pipeline library.
{ //
pipeline_library_ =
std::shared_ptr<PipelineLibraryMTL>(new PipelineLibraryMTL(device_));
}
// Setup the sampler library.
{ //
sampler_library_ =
std::shared_ptr<SamplerLibraryMTL>(new SamplerLibraryMTL(device_));
}
{
transients_allocator_ = std::shared_ptr<AllocatorMTL>(
new AllocatorMTL(device_, "Impeller Transients Allocator"));
if (!transients_allocator_) {
return;
}
permanents_allocator_ = std::shared_ptr<AllocatorMTL>(
new AllocatorMTL(device_, "Impeller Permanents Allocator"));
if (!permanents_allocator_) {
return;
}
}
is_valid_ = true;
}
static NSArray<id<MTLLibrary>>* MTLShaderLibraryFromFilePaths(
id<MTLDevice> device,
const std::vector<std::string>& libraries_paths) {
NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
for (const auto& library_path : libraries_paths) {
if (!fml::IsFile(library_path)) {
VALIDATION_LOG << "Shader library does not exist at path '"
<< library_path << "'";
return nil;
}
NSError* shader_library_error = nil;
auto library = [device newLibraryWithFile:@(library_path.c_str())
error:&shader_library_error];
if (!library) {
FML_LOG(ERROR) << "Could not create shader library: "
<< shader_library_error.localizedDescription.UTF8String;
return nil;
}
[found_libraries addObject:library];
}
return found_libraries;
}
static NSArray<id<MTLLibrary>>* MTLShaderLibraryFromFileData(
id<MTLDevice> device,
const std::vector<std::shared_ptr<fml::Mapping>>& libraries_data,
const std::string& label) {
NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
for (const auto& library_data : libraries_data) {
if (library_data == nullptr) {
FML_LOG(ERROR) << "Shader library data was null.";
return nil;
}
__block auto data = library_data;
auto dispatch_data =
::dispatch_data_create(library_data->GetMapping(), // buffer
library_data->GetSize(), // size
dispatch_get_main_queue(), // queue
^() {
// We just need a reference.
data.reset();
} // destructor
);
if (!dispatch_data) {
FML_LOG(ERROR) << "Could not wrap shader data in dispatch data.";
return nil;
}
NSError* shader_library_error = nil;
auto library = [device newLibraryWithData:dispatch_data
error:&shader_library_error];
if (!library) {
FML_LOG(ERROR) << "Could not create shader library: "
<< shader_library_error.localizedDescription.UTF8String;
return nil;
}
[found_libraries addObject:library];
}
return found_libraries;
}
static id<MTLDevice> CreateMetalDevice() {
return ::MTLCreateSystemDefaultDevice();
}
std::shared_ptr<Context> ContextMTL::Create(
const std::vector<std::string>& shader_library_paths) {
auto device = CreateMetalDevice();
auto context = std::shared_ptr<ContextMTL>(new ContextMTL(
device, MTLShaderLibraryFromFilePaths(device, shader_library_paths)));
if (!context->IsValid()) {
FML_LOG(ERROR) << "Could not create Metal context.";
return nullptr;
}
return context;
}
std::shared_ptr<Context> ContextMTL::Create(
const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
const std::string& label) {
auto device = CreateMetalDevice();
auto context = std::shared_ptr<ContextMTL>(new ContextMTL(
device,
MTLShaderLibraryFromFileData(device, shader_libraries_data, label)));
if (!context->IsValid()) {
FML_LOG(ERROR) << "Could not create Metal context.";
return nullptr;
}
return context;
}
ContextMTL::~ContextMTL() = default;
bool ContextMTL::IsValid() const {
return is_valid_;
}
std::shared_ptr<ShaderLibrary> ContextMTL::GetShaderLibrary() const {
return shader_library_;
}
std::shared_ptr<PipelineLibrary> ContextMTL::GetPipelineLibrary() const {
return pipeline_library_;
}
std::shared_ptr<SamplerLibrary> ContextMTL::GetSamplerLibrary() const {
return sampler_library_;
}
std::shared_ptr<CommandBuffer> ContextMTL::CreateRenderCommandBuffer() const {
return CreateCommandBufferInQueue(render_queue_);
}
std::shared_ptr<CommandBuffer> ContextMTL::CreateTransferCommandBuffer() const {
return CreateCommandBufferInQueue(transfer_queue_);
}
std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBufferInQueue(
id<MTLCommandQueue> queue) const {
if (!IsValid()) {
return nullptr;
}
auto buffer = std::shared_ptr<CommandBufferMTL>(new CommandBufferMTL(queue));
if (!buffer->IsValid()) {
return nullptr;
}
return buffer;
}
std::shared_ptr<Allocator> ContextMTL::GetPermanentsAllocator() const {
return permanents_allocator_;
}
std::shared_ptr<Allocator> ContextMTL::GetTransientsAllocator() const {
return transients_allocator_;
}
id<MTLDevice> ContextMTL::GetMTLDevice() const {
return device_;
}
} // namespace impeller