WIP on buffer management.

This commit is contained in:
Chinmay Garde 2021-06-07 15:05:42 -07:00 committed by Dan Field
parent d7c12d7638
commit 88cc8570da
16 changed files with 255 additions and 132 deletions

View File

@ -10,6 +10,8 @@ impeller_component("compositor") {
"allocator.mm",
"buffer.h",
"buffer.mm",
"buffer_view.h",
"buffer_view.mm",
"command.h",
"command.mm",
"command_buffer.h",
@ -18,10 +20,14 @@ impeller_component("compositor") {
"comparable.h",
"context.h",
"context.mm",
"device_buffer.h",
"device_buffer.mm",
"formats.cc",
"formats.h",
"formats_metal.h",
"formats_metal.mm",
"host_buffer.h",
"host_buffer.mm",
"pipeline.h",
"pipeline.mm",
"pipeline_descriptor.h",

View File

@ -5,6 +5,7 @@
#include "impeller/compositor/allocator.h"
#include "impeller/compositor/buffer.h"
#include "impeller/compositor/device_buffer.h"
namespace impeller {

View File

@ -28,8 +28,6 @@ class BufferView final : public BufferBase {
// |BufferBase|
~BufferView() = default;
const BufferBase* GetBuffer() const { return parent_.get(); }
// |BufferBase|
uint8_t* GetMapping() const override {
return parent_->GetMapping() + range_in_parent_.offset;
@ -48,55 +46,4 @@ class BufferView final : public BufferBase {
: parent_(std::move(parent)), range_in_parent_(range_in_parent) {}
};
class Buffer {
public:
~Buffer();
private:
friend class Allocator;
const id<MTLBuffer> buffer_;
const size_t size_;
const StorageMode mode_;
const std::string label_;
Buffer(id<MTLBuffer> buffer,
size_t size,
StorageMode mode,
std::string label);
FML_DISALLOW_COPY_AND_ASSIGN(Buffer);
};
class HostBuffer : public std::enable_shared_from_this<HostBuffer>,
public BufferBase {
public:
std::shared_ptr<HostBuffer> Create();
std::shared_ptr<BufferView> Emplace(size_t length);
virtual ~HostBuffer();
// |BufferBase|
uint8_t* GetMapping() const override { return buffer_; }
// |BufferBase|
size_t GetLength() const override { return length_; }
[[nodiscard]] bool Truncate(size_t length);
private:
uint8_t* buffer_ = nullptr;
size_t length_ = 0;
size_t reserved_ = 0;
[[nodiscard]] bool Reserve(size_t reserved);
[[nodiscard]] bool ReserveNPOT(size_t reserved);
HostBuffer();
FML_DISALLOW_COPY_AND_ASSIGN(HostBuffer);
};
} // namespace impeller

View File

@ -4,80 +4,8 @@
#include "impeller/compositor/buffer.h"
#include "flutter/fml/logging.h"
namespace impeller {
Buffer::Buffer(id<MTLBuffer> buffer,
size_t size,
StorageMode mode,
std::string label)
: buffer_(buffer), size_(size), mode_(mode), label_(std::move(label)) {}
Buffer::~Buffer() = default;
std::shared_ptr<HostBuffer> HostBuffer::Create() {
return std::shared_ptr<HostBuffer>(new HostBuffer());
}
HostBuffer::HostBuffer() = default;
HostBuffer::~HostBuffer() {
::free(buffer_);
}
std::shared_ptr<BufferView> HostBuffer::Emplace(size_t length) {
auto old_length = length_;
if (!Truncate(length_ + length)) {
return nullptr;
}
return std::shared_ptr<BufferView>(
new BufferView(shared_from_this(), Range{old_length, length}));
}
bool HostBuffer::Truncate(size_t length) {
if (!ReserveNPOT(length)) {
return false;
}
length_ = length;
return true;
}
static uint32_t NextPowerOfTwoSize(uint32_t x) {
if (x == 0) {
return 1;
}
--x;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
return x + 1;
}
bool HostBuffer::ReserveNPOT(size_t reserved) {
return Reserve(NextPowerOfTwoSize(reserved));
}
bool HostBuffer::Reserve(size_t reserved) {
if (reserved == reserved_) {
return true;
}
auto new_allocation = ::realloc(buffer_, reserved);
if (!new_allocation) {
// If new length is zero, a minimum non-zero sized allocation is returned.
// So this check will not trip and this routine will indicate success as
// expected.
FML_LOG(ERROR) << "Allocation failed. Out of memory.";
return false;
}
buffer_ = static_cast<uint8_t*>(new_allocation);
reserved_ = reserved;
return true;
}
//
} // namespace impeller

View File

@ -19,7 +19,7 @@ class Sampler;
struct Bindings {
std::map<size_t, BufferView> buffers;
std::map<size_t, std::shared_ptr<Texture>> textures;
std::map<size_t, std::shared_ptr<Texture>> samplers;
std::map<size_t, std::shared_ptr<Sampler>> samplers;
};
struct Command {
@ -27,6 +27,7 @@ struct Command {
Bindings vertex_bindings;
Bindings fragment_bindings;
BufferView index_buffer;
size_t index_count = 0u;
constexpr operator bool() const { return pipeline && pipeline->IsValid(); }
};

View File

@ -4,4 +4,10 @@
#include "impeller/compositor/command.h"
namespace impeller {} // namespace impeller
#include <Metal/Metal.h>
namespace impeller {
//
} // namespace impeller

View File

@ -0,0 +1,36 @@
// 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.
#pragma once
#include <Metal/Metal.h>
#include <string>
#include "flutter/fml/macros.h"
#include "impeller/compositor/allocator.h"
namespace impeller {
class Buffer {
public:
~Buffer();
private:
friend class Allocator;
const id<MTLBuffer> buffer_;
const size_t size_;
const StorageMode mode_;
const std::string label_;
Buffer(id<MTLBuffer> buffer,
size_t size,
StorageMode mode,
std::string label);
FML_DISALLOW_COPY_AND_ASSIGN(Buffer);
};
} // namespace impeller

View File

@ -0,0 +1,17 @@
// 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/compositor/device_buffer.h"
namespace impeller {
Buffer::Buffer(id<MTLBuffer> buffer,
size_t size,
StorageMode mode,
std::string label)
: buffer_(buffer), size_(size), mode_(mode), label_(std::move(label)) {}
Buffer::~Buffer() = default;
} // namespace impeller

View File

@ -0,0 +1,46 @@
// 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.
#pragma once
#include <memory>
#include "flutter/fml/macros.h"
#include "impeller/compositor/buffer.h"
#include "impeller/compositor/host_buffer.h"
namespace impeller {
class HostBuffer : public std::enable_shared_from_this<HostBuffer>,
public BufferBase {
public:
std::shared_ptr<HostBuffer> Create();
std::shared_ptr<BufferView> Emplace(size_t length);
virtual ~HostBuffer();
// |BufferBase|
uint8_t* GetMapping() const override { return buffer_; }
// |BufferBase|
size_t GetLength() const override { return length_; }
[[nodiscard]] bool Truncate(size_t length);
private:
uint8_t* buffer_ = nullptr;
size_t length_ = 0;
size_t reserved_ = 0;
[[nodiscard]] bool Reserve(size_t reserved);
[[nodiscard]] bool ReserveNPOT(size_t reserved);
HostBuffer();
FML_DISALLOW_COPY_AND_ASSIGN(HostBuffer);
};
} // namespace impeller

View File

@ -0,0 +1,75 @@
// 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/compositor/host_buffer.h"
#include "flutter/fml/logging.h"
namespace impeller {
std::shared_ptr<HostBuffer> HostBuffer::Create() {
return std::shared_ptr<HostBuffer>(new HostBuffer());
}
HostBuffer::HostBuffer() = default;
HostBuffer::~HostBuffer() {
::free(buffer_);
}
std::shared_ptr<BufferView> HostBuffer::Emplace(size_t length) {
auto old_length = length_;
if (!Truncate(length_ + length)) {
return nullptr;
}
return std::shared_ptr<BufferView>(
new BufferView(shared_from_this(), Range{old_length, length}));
}
bool HostBuffer::Truncate(size_t length) {
if (!ReserveNPOT(length)) {
return false;
}
length_ = length;
return true;
}
static uint32_t NextPowerOfTwoSize(uint32_t x) {
if (x == 0) {
return 1;
}
--x;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
return x + 1;
}
bool HostBuffer::ReserveNPOT(size_t reserved) {
return Reserve(NextPowerOfTwoSize(reserved));
}
bool HostBuffer::Reserve(size_t reserved) {
if (reserved == reserved_) {
return true;
}
auto new_allocation = ::realloc(buffer_, reserved);
if (!new_allocation) {
// If new length is zero, a minimum non-zero sized allocation is returned.
// So this check will not trip and this routine will indicate success as
// expected.
FML_LOG(ERROR) << "Allocation failed. Out of memory.";
return false;
}
buffer_ = static_cast<uint8_t*>(new_allocation);
reserved_ = reserved;
return true;
}
} // namespace impeller

View File

@ -23,11 +23,15 @@ class Pipeline {
bool IsValid() const;
id<MTLRenderPipelineState> GetMTLRenderPipelineState() const;
id<MTLDepthStencilState> GetMTLDepthStencilState() const;
private:
friend class PipelineLibrary;
Type type_ = Type::kUnknown;
id<MTLRenderPipelineState> state_;
id<MTLRenderPipelineState> pipeline_state_;
id<MTLDepthStencilState> depth_stencil_state_;
bool is_valid_ = false;

View File

@ -8,8 +8,8 @@ namespace impeller {
Pipeline::Pipeline(id<MTLRenderPipelineState> state,
id<MTLDepthStencilState> depth_stencil_state)
: state_(state), depth_stencil_state_(depth_stencil_state) {
if (!state_) {
: pipeline_state_(state), depth_stencil_state_(depth_stencil_state) {
if (!pipeline_state_) {
return;
}
type_ = Type::kRender;
@ -22,4 +22,12 @@ bool Pipeline::IsValid() const {
return is_valid_;
}
id<MTLRenderPipelineState> Pipeline::GetMTLRenderPipelineState() const {
return pipeline_state_;
}
id<MTLDepthStencilState> Pipeline::GetMTLDepthStencilState() const {
return depth_stencil_state_;
}
} // namespace impeller

View File

@ -84,6 +84,8 @@ class RenderPass {
RenderPass(id<MTLCommandBuffer> buffer, const RenderPassDescriptor& desc);
bool EncodeCommands(id<MTLRenderCommandEncoder> pass) const;
FML_DISALLOW_COPY_AND_ASSIGN(RenderPass);
};

View File

@ -4,8 +4,10 @@
#include "impeller/compositor/render_pass.h"
#include "flutter/fml/closure.h"
#include "flutter/fml/logging.h"
#include "impeller/compositor/formats_metal.h"
#include "impeller/shader_glue/shader_types.h"
namespace impeller {
@ -136,7 +138,51 @@ bool RenderPass::Encode() const {
if (!pass) {
return false;
}
[pass endEncoding];
// Success or failure, the pass must end. The buffer can only process one pass
// at a time.
fml::ScopedCleanupClosure auto_end([pass]() { [pass endEncoding]; });
return EncodeCommands(pass);
}
void Bind(ShaderStage stage, const BufferView& view) {}
void Bind(ShaderStage stage, const Texture& view) {}
void Bind(ShaderStage stage, const Sampler& view) {}
bool RenderPass::EncodeCommands(id<MTLRenderCommandEncoder> pass) const {
// There a numerous opportunities here to ensure bindings are not repeated.
// Stuff like setting the vertex buffer bindings over and over when just the
// offsets could be updated (as recommended in best practices).
auto bind_stage_resources = [](const Bindings& bindings, ShaderStage stage) {
for (const auto buffer : bindings.buffers) {
Bind(stage, buffer.second);
}
for (const auto texture : bindings.textures) {
Bind(stage, *texture.second);
}
for (const auto sampler : bindings.samplers) {
Bind(stage, *sampler.second);
}
};
for (const auto& command : commands_) {
[pass setRenderPipelineState:command.pipeline->GetMTLRenderPipelineState()];
[pass setDepthStencilState:command.pipeline->GetMTLDepthStencilState()];
[pass setFrontFacingWinding:MTLWindingClockwise];
[pass setCullMode:MTLCullModeBack];
bind_stage_resources(command.vertex_bindings, ShaderStage::kVertex);
bind_stage_resources(command.fragment_bindings, ShaderStage::kFragment);
// [pass drawIndexedPrimitives:MTLPrimitiveTypeTriangleStrip
// indexCount:command.index_count
// indexType:MTLIndexTypeUInt32
// indexBuffer:command.index_buffer
// indexBufferOffset:(NSUInteger)indexBufferOffset
// instanceCount:1u
// baseVertex:0u
// baseInstance:0u];
}
return true;
}