Compare commits

...

1 Commits

Author SHA1 Message Date
Leonard Hecker
69112b228f Implement strided helper functions 2024-07-16 20:06:43 +02:00
3 changed files with 116 additions and 14 deletions

View File

@@ -52,4 +52,103 @@ namespace til
target = { target.data() + targetCount, target.size() - targetCount };
source = { source.data() + sourceCount, source.size() - sourceCount };
}
// memmove(), but you can specify a stride! This can be useful for copying between bitmaps.
// A stride is (usually) the number of bytes between two rows in a bitmap. The stride doesn't necessarily
// equal the actual number of pixels between rows, for instance for memory alignment purposes.
//
// All sizes are in bytes.
//
// Higher abstractions could be built on top of this function.
void bytes_strided_copy(void* target, size_t targetStride, size_t targetSize, const void* source, size_t sourceStride, size_t sourceSize) noexcept
{
// Strides are supposed to be smaller than the whole bitmap size and the remaining code assumes that too.
targetStride = std::min(targetStride, targetSize);
sourceStride = std::min(sourceStride, sourceSize);
auto targetPtr = static_cast<uint8_t*>(target);
auto sourcePtr = static_cast<const uint8_t*>(source);
// If the two bitmaps have the same stride we can just copy them in one go.
if (sourceStride == targetStride)
{
memmove(targetPtr, sourcePtr, std::min(targetSize, sourceSize));
}
else
{
const auto targetEnd = targetPtr + targetSize;
const auto sourceEnd = sourcePtr + sourceSize;
// The max. amount we can copy per row is the min. width (the intersection).
const auto width = std::min(targetStride, sourceStride);
while (targetPtr < targetEnd && sourcePtr < sourceEnd)
{
memmove(targetPtr, sourcePtr, width);
targetPtr += targetStride;
sourcePtr += sourceStride;
}
}
}
// Fills the given rectangle inside target with the given value.
// All values are in units of T, not in bytes.
template<TriviallyCopyable T>
void rect_fill(T* target, size_t targetStride, size_t targetSize, T value, size_t left, size_t top, size_t right, size_t bottom) noexcept
{
// Strides are supposed to be smaller than the whole bitmap size and the remaining code assumes that too.
targetStride = std::min(targetStride, targetSize);
// Ensure that the rectangle is valid (left <= right && top <= bottom)
// and within bounds (right <= width && bottom <= height).
right = std::min(right, targetStride);
left = std::min(left, right);
bottom = std::min(bottom, targetSize / targetStride);
top = std::min(top, bottom);
const auto height = bottom - top;
const auto width = right - left;
const auto offsetBeg = top * targetStride + left;
const auto offsetEnd = bottom * targetStride + right;
const auto targetEnd = target + offsetEnd;
target = target + offsetBeg;
// If we're allowed to fill entire rows at a time, we don't need to loop around the memset().
if (width == targetStride)
{
if constexpr (sizeof(T) == 1)
{
// Memset is generally expected to be the fasted way to clear memory.
memset(target, static_cast<unsigned char>(value), static_cast<size_t>(targetEnd - target));
}
else
{
// This should ideally compile down to a `rep stosb` or similar.
for (; target < targetEnd; ++target)
{
*target = value;
}
}
}
else
{
// Same as the above but with a loop around it.
while (target < targetEnd)
{
if constexpr (sizeof(T) == 1)
{
memset(target, static_cast<unsigned char>(value), width * sizeof(T));
}
else
{
for (auto it = target, end = it + width; it < end; ++it)
{
*it = value;
}
}
target += targetStride;
}
}
}
}

View File

@@ -336,12 +336,20 @@ CATCH_RETURN()
return S_OK;
}
void AtlasEngine::_fillColorBitmap(const size_t y, const size_t x1, const size_t x2, const u32 fgColor, const u32 bgColor) noexcept
void AtlasEngine::_fillColorBitmap(size_t y, size_t x1, size_t x2, const u32 fgColor, const u32 bgColor) noexcept
{
// Never trust the caller. Do bounds checks.
// The caller was written by yesterday's me and that person is a complete fool.
y = clamp<size_t>(y, 0, _p.s->viewportCellCount.y);
const auto shift = _p.rows[y]->lineRendition != LineRendition::SingleWidth ? 1 : 0;
// Ensure that x1 <= x2 and x2 <= width.
x2 = clamp<size_t>(x2 << shift, 0, _p.s->viewportCellCount.x);
x1 = clamp<size_t>(x1 << shift, 0, x2);
const auto bitmap = _p.colorBitmap.begin() + _p.colorBitmapRowStride * y;
const auto shift = gsl::narrow_cast<u8>(_p.rows[y]->lineRendition != LineRendition::SingleWidth);
auto beg = bitmap + (x1 << shift);
auto end = bitmap + (x2 << shift);
auto beg = bitmap + x1;
auto end = bitmap + x2;
const u32 colors[] = {
u32ColorPremultiply(bgColor),

View File

@@ -4,6 +4,7 @@
#include "pch.h"
#include "BackendD3D.h"
#include <til/bytes.h>
#include <til/unicode.h>
#include <custom_shader_ps.h>
@@ -1067,18 +1068,12 @@ void BackendD3D::_uploadBackgroundBitmap(const RenderingPayload& p)
D3D11_MAPPED_SUBRESOURCE mapped{};
THROW_IF_FAILED(p.deviceContext->Map(_backgroundBitmap.get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped));
auto src = std::bit_cast<const char*>(p.backgroundBitmap.data());
const auto srcEnd = std::bit_cast<const char*>(p.backgroundBitmap.data() + p.backgroundBitmap.size());
const auto srcWidth = p.s->viewportCellCount.x * sizeof(u32);
const auto src = std::bit_cast<const char*>(p.backgroundBitmap.data());
const auto srcStride = p.colorBitmapRowStride * sizeof(u32);
auto dst = static_cast<char*>(mapped.pData);
const auto srcSize = p.backgroundBitmap.size();
const auto dst = static_cast<char*>(mapped.pData);
while (src < srcEnd)
{
memcpy(dst, src, srcWidth);
src += srcStride;
dst += mapped.RowPitch;
}
til::bytes_strided_copy(dst, mapped.RowPitch, mapped.DepthPitch, src, srcStride, srcSize);
p.deviceContext->Unmap(_backgroundBitmap.get(), 0);
_backgroundBitmapGeneration = p.colorBitmapGenerations[0];