From ec3d9f6060ba261a85dd2d19238a63a98f372b1b Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 5 Jul 2021 14:37:11 -0700 Subject: [PATCH] Wire up texture creation from buffers. --- engine/src/flutter/impeller/BUILD.gn | 2 +- .../src/flutter/impeller/compositor/BUILD.gn | 6 ++- .../impeller/compositor/device_buffer.h | 4 ++ .../impeller/compositor/device_buffer.mm | 31 ++++++++++++- .../compositor/device_buffer_unittests.mm | 15 ++++++ .../src/flutter/impeller/compositor/formats.h | 17 +++++++ .../src/flutter/impeller/compositor/texture.h | 28 +++++++++++ engine/src/flutter/impeller/geometry/BUILD.gn | 9 ++++ .../impeller/geometry/geometry_unittests.cc | 23 ++++++++++ engine/src/flutter/impeller/geometry/size.h | 46 +++++++++++++------ .../primitives/primitives_unittests.mm | 2 +- 11 files changed, 164 insertions(+), 19 deletions(-) create mode 100644 engine/src/flutter/impeller/compositor/device_buffer_unittests.mm create mode 100644 engine/src/flutter/impeller/geometry/geometry_unittests.cc diff --git a/engine/src/flutter/impeller/BUILD.gn b/engine/src/flutter/impeller/BUILD.gn index c7aeb4641fd..c87d59175ba 100644 --- a/engine/src/flutter/impeller/BUILD.gn +++ b/engine/src/flutter/impeller/BUILD.gn @@ -27,8 +27,8 @@ executable("impeller_unittests") { "compositor:compositor_unittests", "entity:entity_unittests", "fixtures", + "geometry:geometry_unittests", "playground", "primitives:primitives_unittests", - "//flutter/testing", ] } diff --git a/engine/src/flutter/impeller/compositor/BUILD.gn b/engine/src/flutter/impeller/compositor/BUILD.gn index 31041744905..9e5aa689401 100644 --- a/engine/src/flutter/impeller/compositor/BUILD.gn +++ b/engine/src/flutter/impeller/compositor/BUILD.gn @@ -76,10 +76,14 @@ impeller_component("compositor") { source_set("compositor_unittests") { testonly = true - sources = [ "host_buffer_unittests.mm" ] + sources = [ + "device_buffer_unittests.mm", + "host_buffer_unittests.mm", + ] deps = [ ":compositor", + "../playground", "//flutter/testing:testing_lib", ] } diff --git a/engine/src/flutter/impeller/compositor/device_buffer.h b/engine/src/flutter/impeller/compositor/device_buffer.h index cb0f9491995..f8c93b09c2c 100644 --- a/engine/src/flutter/impeller/compositor/device_buffer.h +++ b/engine/src/flutter/impeller/compositor/device_buffer.h @@ -14,6 +14,7 @@ #include "impeller/compositor/buffer.h" #include "impeller/compositor/buffer_view.h" #include "impeller/compositor/range.h" +#include "impeller/compositor/texture.h" namespace impeller { @@ -26,6 +27,9 @@ class DeviceBuffer final : public Buffer, Range source_range, size_t offset = 0u); + std::shared_ptr MakeTexture(TextureDescriptor desc, + size_t offset = 0u) const; + id GetMTLBuffer() const; bool SetLabel(const std::string& label); diff --git a/engine/src/flutter/impeller/compositor/device_buffer.mm b/engine/src/flutter/impeller/compositor/device_buffer.mm index ff4a1a30888..364e4063cff 100644 --- a/engine/src/flutter/impeller/compositor/device_buffer.mm +++ b/engine/src/flutter/impeller/compositor/device_buffer.mm @@ -4,7 +4,8 @@ #include "impeller/compositor/device_buffer.h" -#include +#include "flutter/fml/logging.h" +#include "impeller/compositor/formats_metal.h" namespace impeller { @@ -17,6 +18,34 @@ id DeviceBuffer::GetMTLBuffer() const { return buffer_; } +std::shared_ptr DeviceBuffer::MakeTexture(TextureDescriptor desc, + size_t offset) const { + if (!desc.IsValid() || !buffer_) { + return nullptr; + } + + // Avoid overruns. + if (offset + desc.GetSizeOfBaseMipLevel() > size_) { + FML_DLOG(ERROR) << "Avoiding buffer overrun when creating texture."; + return nullptr; + } + + auto mtl_desc = [[MTLTextureDescriptor alloc] init]; + mtl_desc.pixelFormat = ToMTLPixelFormat(desc.format); + mtl_desc.width = desc.size.width; + mtl_desc.height = desc.size.height; + mtl_desc.mipmapLevelCount = desc.mip_count; + + auto texture = [buffer_ newTextureWithDescriptor:mtl_desc + offset:offset + bytesPerRow:desc.GetBytesPerRow()]; + if (!texture) { + return nullptr; + } + + return std::make_shared(texture); +} + [[nodiscard]] bool DeviceBuffer::CopyHostBuffer(const uint8_t* source, Range source_range, size_t offset) { diff --git a/engine/src/flutter/impeller/compositor/device_buffer_unittests.mm b/engine/src/flutter/impeller/compositor/device_buffer_unittests.mm new file mode 100644 index 00000000000..a502b36f565 --- /dev/null +++ b/engine/src/flutter/impeller/compositor/device_buffer_unittests.mm @@ -0,0 +1,15 @@ +// 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 "flutter/testing/testing.h" +#include "impeller/compositor/device_buffer.h" +#include "impeller/playground/playground.h" + +namespace impeller { +namespace testing { + +using DeviceBufferTest = Playground; + +} // namespace testing +} // namespace impeller diff --git a/engine/src/flutter/impeller/compositor/formats.h b/engine/src/flutter/impeller/compositor/formats.h index 0037fd87022..56fc305ab48 100644 --- a/engine/src/flutter/impeller/compositor/formats.h +++ b/engine/src/flutter/impeller/compositor/formats.h @@ -93,6 +93,23 @@ enum class ColorWriteMask : uint64_t { kAll = kRed | kGreen | kBlue, }; +constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format) { + switch (format) { + case PixelFormat::kUnknown: + return 0u; + case PixelFormat::kPixelFormat_B8G8R8A8_UNormInt: + return 4u; + case PixelFormat::kPixelFormat_B8G8R8A8_UNormInt_SRGB: + return 4u; + case PixelFormat::kPixelFormat_D32_Float_S8_UNormInt: + // This is an esoteric format and implementations may use 64 bits. + // Impeller doesn't work with these natively and this return is only here + // for completeness. The 40 bits is as documented. + return 5u; + } + return 0u; +} + struct ColorAttachmentDescriptor { PixelFormat format = PixelFormat::kUnknown; bool blending_enabled = false; diff --git a/engine/src/flutter/impeller/compositor/texture.h b/engine/src/flutter/impeller/compositor/texture.h index ee0e8c9ba84..e64d2c7dfcf 100644 --- a/engine/src/flutter/impeller/compositor/texture.h +++ b/engine/src/flutter/impeller/compositor/texture.h @@ -7,10 +7,38 @@ #include #include "flutter/fml/macros.h" +#include "impeller/compositor/formats.h" #include "impeller/geometry/size.h" namespace impeller { +struct TextureDescriptor { + PixelFormat format = PixelFormat::kUnknown; + ISize size; + size_t mip_count = 1u; // Size::MipCount is usually appropriate. + + constexpr size_t GetSizeOfBaseMipLevel() const { + if (!IsValid()) { + return 0u; + } + return size.Area() * BytesPerPixelForPixelFormat(format); + } + + constexpr size_t GetBytesPerRow() const { + if (!IsValid()) { + return 0u; + } + return size.width * BytesPerPixelForPixelFormat(format); + } + + bool IsValid() const { + return format != PixelFormat::kUnknown && // + size.IsPositive() && // + mip_count >= 1u // + ; + } +}; + class Texture { public: Texture(id texture); diff --git a/engine/src/flutter/impeller/geometry/BUILD.gn b/engine/src/flutter/impeller/geometry/BUILD.gn index c18b8ff5d86..2d6d5a22a54 100644 --- a/engine/src/flutter/impeller/geometry/BUILD.gn +++ b/engine/src/flutter/impeller/geometry/BUILD.gn @@ -31,3 +31,12 @@ impeller_component("geometry") { "vector.h", ] } + +impeller_component("geometry_unittests") { + testonly = true + sources = [ "geometry_unittests.cc" ] + deps = [ + ":geometry", + "//flutter/testing", + ] +} diff --git a/engine/src/flutter/impeller/geometry/geometry_unittests.cc b/engine/src/flutter/impeller/geometry/geometry_unittests.cc new file mode 100644 index 00000000000..bed8c9987f3 --- /dev/null +++ b/engine/src/flutter/impeller/geometry/geometry_unittests.cc @@ -0,0 +1,23 @@ +// 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 "flutter/testing/testing.h" +#include "impeller/geometry/size.h" + +namespace impeller { +namespace testing { + +TEST(GeometryTest, CanGenerateMipCounts) { + ASSERT_EQ((Size{128, 128}.MipCount()), 7u); + ASSERT_EQ((Size{128, 256}.MipCount()), 8u); + ASSERT_EQ((Size{128, 130}.MipCount()), 8u); + ASSERT_EQ((Size{128, 257}.MipCount()), 9u); + ASSERT_EQ((Size{257, 128}.MipCount()), 9u); + ASSERT_EQ((Size{128, 0}.MipCount()), 1u); + ASSERT_EQ((Size{128, -25}.MipCount()), 1u); + ASSERT_EQ((Size{-128, 25}.MipCount()), 1u); +} + +} // namespace testing +} // namespace impeller diff --git a/engine/src/flutter/impeller/geometry/size.h b/engine/src/flutter/impeller/geometry/size.h index 40fd2c673b2..ac32ffdd9ff 100644 --- a/engine/src/flutter/impeller/geometry/size.h +++ b/engine/src/flutter/impeller/geometry/size.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include @@ -11,60 +12,75 @@ namespace impeller { -struct Size { - Scalar width = 0.0; - Scalar height = 0.0; +template +struct TSize { + using Type = T; - constexpr Size() {} + Type width = {}; + Type height = {}; - constexpr Size(Scalar width, Scalar height) : width(width), height(height) {} + constexpr TSize() {} - static constexpr Size Infinite() { - return Size{std::numeric_limits::max(), - std::numeric_limits::max()}; + constexpr TSize(Type width, Type height) : width(width), height(height) {} + + static constexpr TSize Infinite() { + return TSize{std::numeric_limits::max(), + std::numeric_limits::max()}; } - constexpr Size operator*(Scalar scale) const { + constexpr TSize operator*(Type scale) const { return {width * scale, height * scale}; } - constexpr bool operator==(const Size& s) const { + constexpr bool operator==(const TSize& s) const { return s.width == width && s.height == height; } - constexpr bool operator!=(const Size& s) const { + constexpr bool operator!=(const TSize& s) const { return s.width != width || s.height != height; } - constexpr Size operator+(const Size& s) const { + constexpr TSize operator+(const TSize& s) const { return {width + s.width, height + s.height}; } - constexpr Size operator-(const Size& s) const { + constexpr TSize operator-(const TSize& s) const { return {width - s.width, height - s.height}; } - constexpr Size Union(const Size& o) const { + constexpr TSize Union(const TSize& o) const { return { std::max(width, o.width), std::max(height, o.height), }; } - constexpr Size Intersection(const Size& o) const { + constexpr TSize Intersection(const TSize& o) const { return { std::min(width, o.width), std::min(height, o.height), }; } + constexpr Type Area() const { return width * height; } + constexpr bool IsZero() const { return width * height == 0.0; } constexpr bool IsPositive() const { return width * height > 0.0; } constexpr bool IsEmpty() { return !IsPositive(); } + + constexpr size_t MipCount() const { + if (!IsPositive()) { + return 1u; + } + return std::max(ceil(log2(width)), ceil(log2(height))); + } }; +using Size = TSize; +using ISize = TSize; + static_assert(sizeof(Size) == 2 * sizeof(Scalar)); } // namespace impeller diff --git a/engine/src/flutter/impeller/primitives/primitives_unittests.mm b/engine/src/flutter/impeller/primitives/primitives_unittests.mm index 16e1c2e0e51..bd65d6f28bc 100644 --- a/engine/src/flutter/impeller/primitives/primitives_unittests.mm +++ b/engine/src/flutter/impeller/primitives/primitives_unittests.mm @@ -65,7 +65,7 @@ TEST_F(PrimitivesTest, CanCreateBoxPrimitive) { } return true; }; - OpenPlaygroundHere(callback); + // OpenPlaygroundHere(callback); } } // namespace testing