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>
This commit is contained in:
parent
f530586c41
commit
1ee70359a1
@ -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->make<SkPM4f>(SkPM4f_from_SkColor(fColor, dst));
|
||||
auto color = scratch->copy(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->make<SkPM4f>(fColor4.premul());
|
||||
auto color = scratch->copy(fColor4.premul());
|
||||
p->append(SkRasterPipeline::move_src_dst);
|
||||
p->append(SkRasterPipeline::constant_color, color);
|
||||
if (!append_gamut_transform(p, scratch, fColorSpace.get(), dst)) {
|
||||
|
@ -11,23 +11,37 @@ SkFixedAlloc::SkFixedAlloc(void* ptr, size_t len)
|
||||
: fBuffer((char*)ptr), fUsed(0), fLimit(len) {}
|
||||
|
||||
void SkFixedAlloc::undo() {
|
||||
// This function is essentially make() in reverse.
|
||||
|
||||
// First, read the Footer we stamped at the end.
|
||||
Footer footer;
|
||||
memcpy(&footer, fBuffer + fUsed - sizeof(Footer), sizeof(Footer));
|
||||
|
||||
// That tells us where the T starts and how to destroy it.
|
||||
footer.dtor(fBuffer + fUsed - sizeof(Footer) - footer.len);
|
||||
|
||||
// We can reuse bytes that stored the Footer, the T, and any that we skipped for alignment.
|
||||
fUsed -= sizeof(Footer) + footer.len + footer.skip;
|
||||
uint32_t skip_and_size;
|
||||
memcpy(&skip_and_size, fBuffer + fUsed - 4, 4);
|
||||
fUsed -= skip_and_size + 4;
|
||||
}
|
||||
|
||||
void SkFixedAlloc::reset() {
|
||||
while (fUsed) {
|
||||
this->undo();
|
||||
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);
|
||||
|
||||
if (!SkTFitsIn<uint32_t>(skip + size) ||
|
||||
fUsed + skip + size + 4 > fLimit) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Skip ahead until aligned.
|
||||
fUsed += skip;
|
||||
|
||||
// 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;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
@ -37,8 +51,7 @@ void SkFallbackAlloc::undo() {
|
||||
if (fHeapAllocs.empty()) {
|
||||
return fFixedAlloc->undo();
|
||||
}
|
||||
HeapAlloc alloc = fHeapAllocs.back();
|
||||
alloc.deleter(alloc.ptr);
|
||||
sk_free(fHeapAllocs.back());
|
||||
fHeapAllocs.pop_back();
|
||||
}
|
||||
|
||||
|
@ -11,54 +11,51 @@
|
||||
#include "SkTFitsIn.h"
|
||||
#include "SkTypes.h"
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
// SkFixedAlloc allocates objects out of a fixed-size buffer and destroys them when destroyed.
|
||||
// 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.
|
||||
class SkFixedAlloc {
|
||||
public:
|
||||
SkFixedAlloc(void* ptr, size_t len);
|
||||
~SkFixedAlloc() { this->reset(); }
|
||||
|
||||
// Allocates a new T in the buffer if possible. If not, returns nullptr.
|
||||
// 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));
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T* make(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) {
|
||||
if (auto ptr = this->alloc<T>()) {
|
||||
return new (ptr) T(std::forward<Args>(args)...);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Skip ahead until our buffer is aligned for T.
|
||||
fUsed += skip;
|
||||
template <typename T>
|
||||
T* copy(const T& src) { return this->make<T>(src); }
|
||||
|
||||
// Create the T.
|
||||
auto ptr = (T*)(fBuffer+fUsed);
|
||||
new (ptr) T(std::forward<Args>(args)...);
|
||||
fUsed += sizeof(T);
|
||||
|
||||
// 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.
|
||||
// Frees the space of the last object allocated.
|
||||
void undo();
|
||||
|
||||
// Destroys all objects and frees all space in the buffer.
|
||||
// Frees all space in the buffer.
|
||||
void reset();
|
||||
|
||||
private:
|
||||
struct Footer {
|
||||
void (*dtor)(void*);
|
||||
uint32_t skip, len;
|
||||
};
|
||||
void* alloc(size_t, size_t);
|
||||
|
||||
char* fBuffer;
|
||||
size_t fUsed, fLimit;
|
||||
@ -69,34 +66,34 @@ public:
|
||||
explicit SkFallbackAlloc(SkFixedAlloc*);
|
||||
~SkFallbackAlloc() { this->reset(); }
|
||||
|
||||
// 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.
|
||||
template <typename T>
|
||||
void* alloc() {
|
||||
// Once we go heap we never go back to fixed. This keeps undo() working.
|
||||
if (fHeapAllocs.empty()) {
|
||||
if (T* ptr = fFixedAlloc->make<T>(std::forward<Args>(args)...)) {
|
||||
if (void* ptr = fFixedAlloc->alloc<T>()) {
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
auto ptr = new T(std::forward<Args>(args)...);
|
||||
fHeapAllocs.push_back({[](void* ptr) { delete (T*)ptr; }, ptr});
|
||||
void* ptr = sk_malloc_throw(sizeof(T));
|
||||
fHeapAllocs.push_back(ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Destroys the last object allocated and frees any space it used in the SkFixedAlloc.
|
||||
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.
|
||||
void undo();
|
||||
|
||||
// Destroys all objects and frees all space in the SkFixedAlloc.
|
||||
// Frees all objects and all space in the SkFixedAlloc.
|
||||
void reset();
|
||||
|
||||
private:
|
||||
struct HeapAlloc {
|
||||
void (*deleter)(void*);
|
||||
void* ptr;
|
||||
};
|
||||
|
||||
SkFixedAlloc* fFixedAlloc;
|
||||
std::vector<HeapAlloc> fHeapAllocs;
|
||||
std::vector<void*> fHeapAllocs;
|
||||
};
|
||||
|
||||
#endif//SkFixedAlloc_DEFINED
|
||||
|
@ -90,7 +90,7 @@ bool SkModeColorFilter::onAppendStages(SkRasterPipeline* p,
|
||||
SkColorSpace* dst,
|
||||
SkFallbackAlloc* scratch,
|
||||
bool shaderIsOpaque) const {
|
||||
auto color = scratch->make<SkPM4f>(SkPM4f_from_SkColor(fColor, dst));
|
||||
auto color = scratch->copy(SkPM4f_from_SkColor(fColor, dst));
|
||||
|
||||
p->append(SkRasterPipeline::move_src_dst);
|
||||
p->append(SkRasterPipeline::constant_color, color);
|
||||
|
@ -10,11 +10,10 @@
|
||||
|
||||
namespace {
|
||||
|
||||
static int created, destroyed;
|
||||
static int created;
|
||||
|
||||
struct Foo {
|
||||
Foo(int X, float Y) : x(X), y(Y) { created++; }
|
||||
~Foo() { destroyed++; }
|
||||
|
||||
int x;
|
||||
float y;
|
||||
@ -38,21 +37,15 @@ 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.
|
||||
@ -62,15 +55,10 @@ 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) {
|
||||
@ -79,8 +67,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 make() call.)
|
||||
fixed_failed = fixed_failed || (fixed.make<int>(i) == nullptr);
|
||||
// (Remember, there is some overhead to each copy() call.)
|
||||
fixed_failed = fixed_failed || (fixed.copy(i) == nullptr);
|
||||
}
|
||||
REPORTER_ASSERT(r, fixed_failed);
|
||||
|
||||
@ -91,7 +79,7 @@ DEF_TEST(FallbackAlloc, r) {
|
||||
|
||||
bool fallback_failed = false;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
fallback_failed = fallback_failed || (fallback.make<int>(i) == nullptr);
|
||||
fallback_failed = fallback_failed || (fallback.copy(i) == nullptr);
|
||||
}
|
||||
REPORTER_ASSERT(r, !fallback_failed);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user