Commit 89ae2753 authored by Edwin Carlinet's avatar Edwin Carlinet
Browse files

Add tile functionalities.

parent a118f79a
Pipeline #27925 passed with stage
in 25 minutes and 7 seconds
......@@ -2,12 +2,13 @@
#include <cstddef>
#include <algorithm>
#include <mln/bp/tile.hpp>
#include <mln/bp/utils.hpp>
namespace mln::bp
{
/// \brief Swap 2 2D-buffer
/// \brief Copy a 2D-buffer into another
///
/// \rst
/// :param src: Buffer source
......@@ -23,6 +24,17 @@ namespace mln::bp
template <int WIDTH, int HEIGHT, typename T>
void copy(const T* src, T* dst, int width, int height, std::ptrdiff_t src_stride, std::ptrdiff_t dst_stride);
/// \brief Copy a 2D-tile into another
///
/// \rst
/// :param src: Source tile
/// :param dst: Destination tile
///
/// .. note: Source has to equal (or included) in the destination
/// \endrst
template <typename T>
void copy(Tile2DView<T> src, Tile2DView<T> dst);
/******************************************/
/**** Implementation ****/
......@@ -40,4 +52,31 @@ namespace mln::bp
std::copy_n(mln::bp::ptr_offset(src, y * src_stride), w, mln::bp::ptr_offset(dst, y * dst_stride));
}
template <class T>
inline void copy(const T* __restrict src, T* __restrict dst, int w, int h, std::ptrdiff_t src_stride,
std::ptrdiff_t dst_stride)
{
for (int y = 0; y < h; ++y)
std::copy_n(mln::bp::ptr_offset(src, y * src_stride), w, mln::bp::ptr_offset(dst, y * dst_stride));
}
template <int WIDTH, int HEIGHT, typename T>
inline void copy(Tile2DView<T> src, Tile2DView<T> dst)
{
assert(src.width() <= dst.width());
assert(src.height() <= dst.height());
copy<WIDTH, HEIGHT>(src.data(), dst.data(), src.width(), src.height(), src.stride(), dst.stride());
}
template <typename T>
inline void copy(Tile2DView<T> src, Tile2DView<T> dst)
{
assert(src.width() <= dst.width());
assert(src.height() <= dst.height());
copy(src.data(), dst.data(), src.width(), src.height(), src.stride(), dst.stride());
}
}
#pragma once
#include <cstddef>
#include <algorithm>
#include <mln/bp/tile.hpp>
#include <mln/bp/utils.hpp>
namespace mln::bp
{
/// \brief Swap 2 2D-buffer
///
/// \rst
/// :param buffer: Buffer source
/// :param dst: Buffer destination
/// :param width: Width of the 2D-buffers
/// :param height: Height of the 2D-buffers
/// :param src_stride: Stride of the source buffer (in bytes)
/// :param dst_stride: Stride of the destination buffer (in bytes)
/// \endrst
template <typename T>
inline void fill(Tile2DView<T> buffer, T value);
template <int WIDTH, int HEIGHT, typename T>
inline void fill(Tile2DView<T> buffer, T value);
/******************************************/
/**** Implementation ****/
/******************************************/
template <int WIDTH, int HEIGHT, typename T>
inline void fill(Tile2DView<T> buffer, T value)
{
mln_assume(buffer.width() <= WIDTH);
mln_assume(buffer.height() <= HEIGHT);
for (int y = 0; y < buffer.height(); ++y)
std::fill_n(buffer.row(y), buffer.width(), value);
}
template <typename T>
inline void fill(Tile2DView<T> buffer, T value)
{
for (int y = 0; y < buffer.height(); ++y)
std::fill_n(buffer.row(y), buffer.width(), value);
}
}
#pragma once
#include <mln/bp/tile.hpp>
#include <xsimd/xsimd.hpp>
#include <concepts>
namespace mln::bp
{
template <class T, class Func>
void transform(Tile2DView<T> in, Tile2DView<T> out, Func f);
template <class T, class Func>
void apply(Tile2DView<T> in, Func f);
template <class T, class Func>
void apply(T* in, std::size_t n, Func f);
/******************************************/
/**** Implementation ****/
/******************************************/
template <class T, class Func>
void transform(Tile2DView<T> in, Tile2DView<T> out, Func f)
{
using simd_t = xsimd::simd_type<T>;
static_assert(std::is_invocable_r_v<simd_t, Func, simd_t>);
static_assert(std::is_invocable_r_v<T, Func, T>);
assert(in.width() == out.width());
assert(in.height() == out.height());
int w = in.width();
for (int y = 0; y < in.height(); ++y)
{
auto a = in.row(y);
auto b = out.row(y);
int i;
for (i = 0; (i + simd_t::size) < w; i += simd_t::size)
xsimd::store_unaligned(b + i, f(xsimd::load_unaligned(a + i)));
for (; i < w; i++)
b[i] = f(a[i]);
}
}
template <class T, class Func>
void apply(T* in, std::size_t n, Func f)
{
using simd_t = xsimd::simd_type<T>;
static_assert(std::is_invocable_r_v<simd_t, Func, simd_t>);
static_assert(std::is_invocable_r_v<T, Func, T>);
std::size_t i = 0;
for (i = 0; (i + simd_t::size) < n; i += simd_t::size)
xsimd::store_unaligned(in + i, f(xsimd::load_unaligned(in + i)));
for (; i < n; i++)
in[i] = f(in[i]);
}
template <class T, class Func>
void apply(Tile2DView<T> in, Func f)
{
using simd_t = xsimd::simd_type<T>;
static_assert(std::is_invocable_r_v<simd_t, Func, simd_t>);
static_assert(std::is_invocable_r_v<T, Func, T>);
for (int y = 0; y < in.height(); ++y)
apply(in.row(y), in.width(), f);
}
}
#pragma once
#include <mln/bp/alloc.hpp>
#include <utility>
#include <cassert>
namespace mln::bp
{
template <class T>
class Tile2DView;
template <>
class Tile2DView<void>
{
public:
Tile2DView() = default;
Tile2DView(void* ptr, int width, int height, std::ptrdiff_t pitch) noexcept
: m_ptr{ptr}
, m_width{width}
, m_height{height}
, m_stride{pitch}
{
}
/// \brief Return a pointer to the beginning of the row
void* row(int y) const noexcept { return ptr_offset(m_ptr, y * m_stride); }
/// \brief Return a raw to pointer to the buffer
void* data() const noexcept { return m_ptr; }
/// \brief Get the stride (in **bytes**)
std::ptrdiff_t stride() const noexcept { return m_stride; }
/// \Brief Get the number of cols
int width() const noexcept { return m_width; }
/// \brief Get the number of rows
int height() const noexcept { return m_height; }
/// \brief Return true if the buffer aligned with the required size
/// e.g., ``is_aligned(16)`` to check that the buffer is 16-bytes aligned
bool is_aligned(int width = 16) { return ((intptr_t)m_ptr & (intptr_t)(width - 1)) == 0; }
protected:
void* m_ptr = nullptr;
int m_width;
int m_height;
std::ptrdiff_t m_stride;
};
template <class T>
class Tile2DView : public Tile2DView<void>
{
public:
Tile2DView() = default;
Tile2DView(T* ptr, int width, int height, std::ptrdiff_t pitch) noexcept
: base(ptr, width, height, pitch)
{
}
/// \brief Return a raw to pointer to the buffer
T* data() const noexcept { return (T*)m_ptr; }
T* row(int y) const noexcept { return (T*)(base::row(y)); }
T& at(int x, int y) const noexcept { return *((T*)(base::row(y)) + x); }
T& operator()(int x, int y) const noexcept { return *((T*)(base::row(y)) + x); }
/// \brief Return a sub-area of the tile
Tile2DView<T> clip(int x, int y, int width, int height);
private:
using base = Tile2DView<void>;
};
template <class T>
class Tile2D : public Tile2DView<T>
{
using free_fn_t = void (*)(void*);
public:
Tile2D() = default;
Tile2D(int width, int height);
~Tile2D();
Tile2D(const Tile2D&) = delete;
Tile2D& operator=(const Tile2D&) = delete;
Tile2D(Tile2D&&) noexcept;
Tile2D& operator=(Tile2D&&) noexcept;
static Tile2D<T> acquire(T* ptr, int width, int height, std::ptrdiff_t stride, free_fn_t free_fn= nullptr) noexcept;
T* release() noexcept;
private:
using base = Tile2DView<T>;
free_fn_t m_free = nullptr;
};
/******************************************/
/**** Implementation ****/
/******************************************/
template <class T>
Tile2DView<T> Tile2DView<T>::clip(int x, int y, int width, int height)
{
assert(0 <= x && x < m_width);
assert(0 <= y && y < m_height);
assert(0 < width && (x + width) <= m_width);
assert(0 < height && (y + height) <= m_height);
return Tile2DView<T>{this->row(y) + x, width, height, this->m_stride};
}
template <class T>
Tile2D<T>::Tile2D(int width, int height)
{
std::ptrdiff_t pitch;
this->m_ptr = aligned_alloc_2d<T>(width, height, pitch);
this->m_stride = pitch;
this->m_width = width;
this->m_height = height;
}
template <class T>
Tile2D<T>::~Tile2D()
{
if (this->m_ptr)
{
if (this->m_free == nullptr)
aligned_free_2d<T>((T*)this->m_ptr, this->m_width, this->m_height, this->m_stride);
else
this->m_free(this->m_ptr);
}
}
template <class T>
Tile2D<T>::Tile2D(Tile2D&& other) noexcept
: base(other)
{
this->m_ptr = std::exchange(other.m_ptr, nullptr);
this->m_free = std::exchange(other.m_free, nullptr);
}
template <class T>
Tile2D<T>& Tile2D<T>::operator=(Tile2D&& other) noexcept
{
std::swap((base&)(*this), (base&)other);
std::swap(this->m_free, other.m_free);
return *this;
}
template <class T>
T* Tile2D<T>::release() noexcept
{
return (T*)std::exchange(this->m_ptr, nullptr);
}
template <class T>
Tile2D<T> Tile2D<T>::acquire(T* ptr, int width, int height, std::ptrdiff_t pitch, free_fn_t free_fn) noexcept
{
Tile2D t;
t.m_ptr = ptr;
t.m_stride = pitch;
t.m_width = width;
t.m_height = height;
t.m_free = free_fn;
return t;
}
} // namespace mln::bp
......@@ -8,6 +8,7 @@
#include <mln/core/box.hpp>
#include <mln/core/concepts/image.hpp>
#include <mln/core/range/foreach.hpp>
#include <mln/bp/tile.hpp>
namespace mln
{
......@@ -50,6 +51,11 @@ namespace mln
template <class T, std::size_t dim>
void pad(const ranges::mdspan<T, dim>& image, e_padding_mode mode, const int borders[][2], T value = {});
/// \overload
template <class T>
void pad(const bp::Tile2DView<T>& image, e_padding_mode mode, const int borders[][2], T value = {});
/// \brief Copy a region from a ndimage or a mdspan and pad the missing values using one of the padding strategy
......@@ -208,6 +214,18 @@ namespace mln
}
template <class T>
void pad(const bp::Tile2DView<T>& f, e_padding_mode mode, const int borders[][2], T value)
{
padder<T> _padder;
std::ptrdiff_t strides[2] = {sizeof(T), f.stride()};
int sizes[2] = {f.width(), f.height()};
T* vptr = (mode == mln::PAD_ZERO) ? nullptr : &value;
impl::pad(&_padder, 2, (std::byte*)f.data(), sizes, strides, borders, mode, vptr);
}
template <class T, int dim>
void pad(ndimage<T, dim>& f, e_padding_mode mode, const int borders[][2], T value)
{
......
......@@ -7,6 +7,7 @@
#include <mln/core/image/private/ndbuffer_image_pixel.hpp>
#include <mln/core/image/private/ndimage_extension.hpp>
#include <mln/core/range/mdspan.hpp>
#include <mln/bp/tile.hpp>
namespace mln
{
......@@ -135,6 +136,7 @@ namespace mln
/// \{
T* buffer() noexcept;
const T* buffer() const noexcept;
[[deprecated]] T* data() noexcept { return this->buffer(); }
[[deprecated]] const T* data() const noexcept { return this->buffer(); }
......@@ -150,6 +152,13 @@ namespace mln
__ndbuffer_image<T, N> clip(const ndbox<N>& roi) const;
/// \}
/// Conversion to primitive objects
/// \{
bp::Tile2DView<T> as_tile() noexcept requires (N == 2);
bp::Tile2DView<const T> as_tile() const noexcept requires (N == 2);
/// \}
/// Value access
/// \{
const_reference operator()(fast_point_type p) const noexcept;
......@@ -608,5 +617,18 @@ namespace mln
return mln::internal::ndimage_extension<T, N>{(char*)ptr, strides, dims, this->border()};
}
template <class T, int N>
bp::Tile2DView<T> __ndbuffer_image<T, N>::as_tile() noexcept requires (N == 2)
{
return bp::Tile2DView{this->buffer(), this->width(), this->height(), this->byte_stride()};
}
template <class T, int N>
bp::Tile2DView<const T> __ndbuffer_image<T, N>::as_tile() const noexcept requires (N == 2)
{
return bp::Tile2DView{this->buffer(), this->width(), this->height(), this->byte_stride()};
}
} // namespace mln
......@@ -11,7 +11,7 @@ namespace mln::bp::impl
void swap_raw(std::byte* __restrict a, std::byte* __restrict b, std::size_t len, int height, std::ptrdiff_t a_stride, std::ptrdiff_t b_stride) noexcept
{
static constexpr int BLOCK_SIZE = 32;
static constexpr int HALF_BLOCK_SIZE = 32;
static constexpr int HALF_BLOCK_SIZE = 16;
alignas(32) std::byte tmp[BLOCK_SIZE];
int rem0 = len % BLOCK_SIZE;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment