Revert "While we can, restrict SkFixedAlloc/SkFallbackAlloc to POD."
authorMike Klein <mtklein@chromium.org>
Tue, 15 Nov 2016 17:58:33 +0000 (17:58 +0000)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Tue, 15 Nov 2016 18:01:21 +0000 (18:01 +0000)
This reverts commit 1ee70359a199f161ce38451989a706d727954cc1.

Reason for revert: breaking my various rolls.

Original change's description:
> While we can, restrict SkFixedAlloc/SkFallbackAlloc to POD.
>
> This trims the overhead down to a uniform 4 bytes.
>
> BUG=skia:
>
> GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=4786
>
> Change-Id: I92127a0c2d6c23a4a372eca39842e22195d2dc99
> Reviewed-on: https://skia-review.googlesource.com/4786
> Reviewed-by: Herb Derby <herb@google.com>
> Commit-Queue: Mike Klein <mtklein@chromium.org>
>

TBR=mtklein@chromium.org,herb@google.com,reviews@skia.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true

Change-Id: Iae9ef553422352a1abf95b709fccd014d468da86
Reviewed-on: https://skia-review.googlesource.com/4829
Commit-Queue: Mike Klein <mtklein@chromium.org>
Reviewed-by: Mike Klein <mtklein@chromium.org>
src/core/SkColorShader.cpp
src/core/SkFixedAlloc.cpp
src/core/SkFixedAlloc.h
src/core/SkModeColorFilter.cpp
tests/FixedAllocTest.cpp

index 5551f1e..69d9e46 100644 (file)
@@ -317,7 +317,7 @@ bool SkColor4Shader::Color4Context::onChooseBlitProcs(const SkImageInfo& info, B
 bool SkColorShader::onAppendStages(SkRasterPipeline* p,
                                    SkColorSpace* dst,
                                    SkFallbackAlloc* scratch) const {
-    auto color = scratch->copy(SkPM4f_from_SkColor(fColor, dst));
+    auto color = scratch->make<SkPM4f>(SkPM4f_from_SkColor(fColor, dst));
     p->append(SkRasterPipeline::move_src_dst);
     p->append(SkRasterPipeline::constant_color, color);
     if (!append_gamut_transform(p, scratch,
@@ -331,7 +331,7 @@ bool SkColorShader::onAppendStages(SkRasterPipeline* p,
 bool SkColor4Shader::onAppendStages(SkRasterPipeline* p,
                                     SkColorSpace* dst,
                                     SkFallbackAlloc* scratch) const {
-    auto color = scratch->copy(fColor4.premul());
+    auto color = scratch->make<SkPM4f>(fColor4.premul());
     p->append(SkRasterPipeline::move_src_dst);
     p->append(SkRasterPipeline::constant_color, color);
     if (!append_gamut_transform(p, scratch, fColorSpace.get(), dst)) {
index 420cdb2..133245a 100644 (file)
@@ -11,37 +11,23 @@ SkFixedAlloc::SkFixedAlloc(void* ptr, size_t len)
     : fBuffer((char*)ptr), fUsed(0), fLimit(len) {}
 
 void SkFixedAlloc::undo() {
-    uint32_t skip_and_size;
-    memcpy(&skip_and_size, fBuffer + fUsed - 4, 4);
-    fUsed -= skip_and_size + 4;
-}
-
-void SkFixedAlloc::reset() {
-    fUsed = 0;
-}
-
-void* SkFixedAlloc::alloc(size_t size, size_t align) {
-    auto aligned = ((uintptr_t)(fBuffer+fUsed) + align-1) & ~(align-1);
-    size_t skip = aligned - (uintptr_t)(fBuffer+fUsed);
+    // This function is essentially make() in reverse.
 
-    if (!SkTFitsIn<uint32_t>(skip + size) ||
-        fUsed + skip + size + 4 > fLimit) {
-        return nullptr;
-    }
+    // First, read the Footer we stamped at the end.
+    Footer footer;
+    memcpy(&footer, fBuffer + fUsed - sizeof(Footer), sizeof(Footer));
 
-    // Skip ahead until aligned.
-    fUsed += skip;
+    // That tells us where the T starts and how to destroy it.
+    footer.dtor(fBuffer + fUsed - sizeof(Footer) - footer.len);
 
-    // Allocate size bytes.
-    void* ptr = (fBuffer+fUsed);
-    fUsed += size;
-
-    // Stamp a footer that we can use to clean up.
-    uint32_t skip_and_size = SkToU32(skip + size);
-    memcpy(fBuffer+fUsed, &skip_and_size, 4);
-    fUsed += 4;
+    // We can reuse bytes that stored the Footer, the T, and any that we skipped for alignment.
+    fUsed -= sizeof(Footer) + footer.len + footer.skip;
+}
 
-    return ptr;
+void SkFixedAlloc::reset() {
+    while (fUsed) {
+        this->undo();
+    }
 }
 
 
@@ -51,7 +37,8 @@ void SkFallbackAlloc::undo() {
     if (fHeapAllocs.empty()) {
         return fFixedAlloc->undo();
     }
-    sk_free(fHeapAllocs.back());
+    HeapAlloc alloc = fHeapAllocs.back();
+    alloc.deleter(alloc.ptr);
     fHeapAllocs.pop_back();
 }
 
index 0f45057..d4e4d8f 100644 (file)
 #include "SkTFitsIn.h"
 #include "SkTypes.h"
 #include <new>
-#include <type_traits>
 #include <utility>
 #include <vector>
 
-// Before GCC 5, is_trivially_copyable had a pre-standard name.
-#if defined(__GLIBCXX__) && (__GLIBCXX__ < 20150801)
-    namespace std {
-        template <typename T>
-        using is_trivially_copyable = has_trivial_copy_constructor<T>;
-    }
-#endif
-
-// SkFixedAlloc allocates POD objects out of a fixed-size buffer.
+// SkFixedAlloc allocates objects out of a fixed-size buffer and destroys them when destroyed.
 class SkFixedAlloc {
 public:
     SkFixedAlloc(void* ptr, size_t len);
     ~SkFixedAlloc() { this->reset(); }
 
-    // Allocates space suitable for a T.  If we can't, returns nullptr.
-    template <typename T>
-    void* alloc() {
-        static_assert(std::is_standard_layout   <T>::value
-                   && std::is_trivially_copyable<T>::value, "");
-        return this->alloc(sizeof(T), alignof(T));
-    }
-
+    // Allocates a new T in the buffer if possible.  If not, returns nullptr.
     template <typename T, typename... Args>
     T* make(Args&&... args) {
-        if (auto ptr = this->alloc<T>()) {
-            return new (ptr) T(std::forward<Args>(args)...);
+        auto aligned = ((uintptr_t)(fBuffer+fUsed) + alignof(T) - 1) & ~(alignof(T)-1);
+        size_t skip = aligned - (uintptr_t)(fBuffer+fUsed);
+
+        if (!SkTFitsIn<uint32_t>(skip)      ||
+            !SkTFitsIn<uint32_t>(sizeof(T)) ||
+            fUsed + skip + sizeof(T) + sizeof(Footer) > fLimit) {
+            return nullptr;
         }
-        return nullptr;
-    }
 
-    template <typename T>
-    T* copy(const T& src) { return this->make<T>(src); }
+        // Skip ahead until our buffer is aligned for T.
+        fUsed += skip;
+
+        // Create the T.
+        auto ptr = (T*)(fBuffer+fUsed);
+        new (ptr) T(std::forward<Args>(args)...);
+        fUsed += sizeof(T);
 
-    // Frees the space of the last object allocated.
+        // Stamp a footer after the T that we can use to clean it up.
+        Footer footer = { [](void* ptr) { ((T*)ptr)->~T(); }, SkToU32(skip), SkToU32(sizeof(T)) };
+        memcpy(fBuffer+fUsed, &footer, sizeof(Footer));
+        fUsed += sizeof(Footer);
+
+        return ptr;
+    }
+
+    // Destroys the last object allocated and frees its space in the buffer.
     void undo();
 
-    // Frees all space in the buffer.
+    // Destroys all objects and frees all space in the buffer.
     void reset();
 
 private:
-    void* alloc(size_t, size_t);
+    struct Footer {
+        void (*dtor)(void*);
+        uint32_t skip, len;
+    };
 
     char* fBuffer;
     size_t fUsed, fLimit;
@@ -66,34 +69,34 @@ public:
     explicit SkFallbackAlloc(SkFixedAlloc*);
     ~SkFallbackAlloc() { this->reset(); }
 
-    template <typename T>
-    void* alloc() {
-        // Once we go heap we never go back to fixed.  This keeps undo() working.
+    // Allocates a new T with the SkFixedAlloc if possible.  If not, uses the heap.
+    template <typename T, typename... Args>
+    T* make(Args&&... args) {
+        // Once we go heap we never go back to fixed.  This keeps destructor ordering sane.
         if (fHeapAllocs.empty()) {
-            if (void* ptr = fFixedAlloc->alloc<T>()) {
+            if (T* ptr = fFixedAlloc->make<T>(std::forward<Args>(args)...)) {
                 return ptr;
             }
         }
-        void* ptr = sk_malloc_throw(sizeof(T));
-        fHeapAllocs.push_back(ptr);
+        auto ptr = new T(std::forward<Args>(args)...);
+        fHeapAllocs.push_back({[](void* ptr) { delete (T*)ptr; }, ptr});
         return ptr;
     }
 
-    template <typename T, typename... Args>
-    T* make(Args&&... args) { return new (this->alloc<T>()) T(std::forward<Args>(args)...); }
-
-    template <typename T>
-    T* copy(const T& src) { return this->make<T>(src); }
-
-    // Frees the last object allocated, including any space it used in the SkFixedAlloc.
+    // Destroys the last object allocated and frees any space it used in the SkFixedAlloc.
     void undo();
 
-    // Frees all objects and all space in the SkFixedAlloc.
+    // Destroys all objects and frees all space in the SkFixedAlloc.
     void reset();
 
 private:
-    SkFixedAlloc*      fFixedAlloc;
-    std::vector<void*> fHeapAllocs;
+    struct HeapAlloc {
+        void (*deleter)(void*);
+        void* ptr;
+    };
+
+    SkFixedAlloc*          fFixedAlloc;
+    std::vector<HeapAlloc> fHeapAllocs;
 };
 
 #endif//SkFixedAlloc_DEFINED
index 1eb2946..9c76f9e 100644 (file)
@@ -90,7 +90,7 @@ bool SkModeColorFilter::onAppendStages(SkRasterPipeline* p,
                                        SkColorSpace* dst,
                                        SkFallbackAlloc* scratch,
                                        bool shaderIsOpaque) const {
-    auto color = scratch->copy(SkPM4f_from_SkColor(fColor, dst));
+    auto color = scratch->make<SkPM4f>(SkPM4f_from_SkColor(fColor, dst));
 
     p->append(SkRasterPipeline::move_src_dst);
     p->append(SkRasterPipeline::constant_color, color);
index e9bae28..0a00f00 100644 (file)
 
 namespace {
 
-    static int created;
+    static int created, destroyed;
 
     struct Foo {
          Foo(int X, float Y) : x(X), y(Y) { created++; }
+        ~Foo() { destroyed++; }
 
         int x;
         float y;
@@ -37,15 +38,21 @@ DEF_TEST(FixedAlloc, r) {
         REPORTER_ASSERT(r, foo->x == 3);
         REPORTER_ASSERT(r, foo->y == 4.0f);
         REPORTER_ASSERT(r, created == 1);
+        REPORTER_ASSERT(r, destroyed == 0);
 
         Foo* bar = fa.make<Foo>(8, 1.0f);
         REPORTER_ASSERT(r, bar);
         REPORTER_ASSERT(r, bar->x == 8);
         REPORTER_ASSERT(r, bar->y == 1.0f);
         REPORTER_ASSERT(r, created == 2);
+        REPORTER_ASSERT(r, destroyed == 0);
 
         fa.undo();
+        REPORTER_ASSERT(r, created == 2);
+        REPORTER_ASSERT(r, destroyed == 1);
     }
+    REPORTER_ASSERT(r, created == 2);
+    REPORTER_ASSERT(r, destroyed == 2);
 
     {
         // Test alignment gurantees.
@@ -55,10 +62,15 @@ DEF_TEST(FixedAlloc, r) {
         Foo* foo = fa.make<Foo>(3, 4.0f);
         REPORTER_ASSERT(r, SkIsAlign4((uintptr_t)foo));
         REPORTER_ASSERT(r, created == 3);
+        REPORTER_ASSERT(r, destroyed == 2);
 
         // Might as well test reset() while we're at it.
         fa.reset();
+        REPORTER_ASSERT(r, created == 3);
+        REPORTER_ASSERT(r, destroyed == 3);
     }
+    REPORTER_ASSERT(r, created == 3);
+    REPORTER_ASSERT(r, destroyed == 3);
 }
 
 DEF_TEST(FallbackAlloc, r) {
@@ -67,8 +79,8 @@ DEF_TEST(FallbackAlloc, r) {
     SkFixedAlloc fixed(buf, sizeof(buf));
     bool fixed_failed = false;
     for (int i = 0; i < 32; i++) {
-        // (Remember, there is some overhead to each copy() call.)
-        fixed_failed = fixed_failed || (fixed.copy(i) == nullptr);
+        // (Remember, there is some overhead to each make() call.)
+        fixed_failed = fixed_failed || (fixed.make<int>(i) == nullptr);
     }
     REPORTER_ASSERT(r, fixed_failed);
 
@@ -79,7 +91,7 @@ DEF_TEST(FallbackAlloc, r) {
 
     bool fallback_failed = false;
     for (int i = 0; i < 32; i++) {
-        fallback_failed = fallback_failed || (fallback.copy(i) == nullptr);
+        fallback_failed = fallback_failed || (fallback.make<int>(i) == nullptr);
     }
     REPORTER_ASSERT(r, !fallback_failed);