F16 rendering

This commit is contained in:
Jorijn van der Graaf 2026-04-01 18:43:18 +02:00
commit 477b7dd087
5 changed files with 120 additions and 79 deletions

View file

@ -593,8 +593,14 @@ void Device::Initialize() {
queueCreateInfo.queueCount = 1; queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &priority; queueCreateInfo.pQueuePriorities = &priority;
VkPhysicalDevice16BitStorageFeatures bit16 {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES,
.storageBuffer16BitAccess = VK_TRUE,
};
VkPhysicalDeviceVulkan12Features features12 { VkPhysicalDeviceVulkan12Features features12 {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
.pNext = &bit16,
.runtimeDescriptorArray = VK_TRUE, .runtimeDescriptorArray = VK_TRUE,
.bufferDeviceAddress = VK_TRUE .bufferDeviceAddress = VK_TRUE
}; };

View file

@ -737,16 +737,7 @@ void Window::Render() {
#endif #endif
vkCmdBindPipeline(drawCmdBuffers[currentBuffer], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, rtPipeline); vkCmdBindPipeline(drawCmdBuffers[currentBuffer], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, rtPipeline);
VkBindDescriptorSetsInfo bindDescriptorSetsInfo{ vkCmdBindDescriptorSets(drawCmdBuffers[currentBuffer], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, rtPipelineLayout, 0, static_cast<std::uint32_t>(descriptorsRt.size()), descriptorsRt.data(), 0, nullptr);
.sType = VK_STRUCTURE_TYPE_BIND_DESCRIPTOR_SETS_INFO,
.stageFlags = VK_SHADER_STAGE_ALL,
.layout = rtPipelineLayout,
.firstSet = 0,
.descriptorSetCount = static_cast<std::uint32_t>(descriptorsRt.size()),
.pDescriptorSets = descriptorsRt.data()
};
vkCmdBindDescriptorSets2(drawCmdBuffers[currentBuffer], &bindDescriptorSetsInfo);
Device::vkCmdTraceRaysKHR(drawCmdBuffers[currentBuffer], &raygenRegion, &missRegion, &hitRegion, &callableRegion, width, height, 1); Device::vkCmdTraceRaysKHR(drawCmdBuffers[currentBuffer], &raygenRegion, &missRegion, &hitRegion, &callableRegion, width, height, 1);

View file

@ -30,7 +30,7 @@ import :Window;
export namespace Crafter { export namespace Crafter {
template<typename T, bool Scaling, bool Owning, bool Rotating, std::uint8_t Alignment = 0, std::uint8_t Frames = 1> requires ((!Rotating || Scaling) && (!Owning || Scaling)) template<typename T, bool Scaling, bool Owning, bool Rotating, std::uint8_t Alignment = 0, std::uint8_t Frames = 1> requires ((!Rotating || Scaling) && (!Owning || Scaling))
struct RenderingElement2D : RenderingElement2DBase<T, Frames>, ScalingBase<Scaling, Owning, Alignment>, RotatingBase<Rotating> { struct RenderingElement2D : RenderingElement2DBase<T, Frames>, ScalingBase<T, Scaling, Owning, Alignment>, RotatingBase<Rotating> {
RenderingElement2D() = default; RenderingElement2D() = default;
RenderingElement2D(Anchor2D anchor, OpaqueType opaque) : RenderingElement2DBase<T, Frames>(anchor, opaque) { RenderingElement2D(Anchor2D anchor, OpaqueType opaque) : RenderingElement2DBase<T, Frames>(anchor, opaque) {
@ -38,22 +38,22 @@ export namespace Crafter {
RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t rotation) requires(Rotating) : RenderingElement2DBase<T, Frames>(anchor, opaque), RotatingBase<Rotating>(rotation) { RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t rotation) requires(Rotating) : RenderingElement2DBase<T, Frames>(anchor, opaque), RotatingBase<Rotating>(rotation) {
} }
RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector<std::uint8_t, 4, Alignment>* scalingBuffer) requires(Scaling && !Owning) : RenderingElement2DBase<T, Frames>(anchor, opaque), ScalingBase<Scaling, Owning, Alignment>(bufferWidth, bufferHeight, scalingBuffer) { RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector<T, 4, Alignment>* scalingBuffer) requires(Scaling && !Owning) : RenderingElement2DBase<T, Frames>(anchor, opaque), ScalingBase<T, Scaling, Owning, Alignment>(bufferWidth, bufferHeight, scalingBuffer) {
} }
RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector<std::uint8_t, 4, Alignment>* scalingBuffer, std::uint32_t rotation) requires(Scaling && !Owning && Rotating) : RenderingElement2DBase<T, Frames>(anchor, opaque), ScalingBase<Scaling, Owning, Alignment>(bufferWidth, bufferHeight, scalingBuffer), RotatingBase<Rotating>(rotation) { RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector<T, 4, Alignment>* scalingBuffer, std::uint32_t rotation) requires(Scaling && !Owning && Rotating) : RenderingElement2DBase<T, Frames>(anchor, opaque), ScalingBase<T, Scaling, Owning, Alignment>(bufferWidth, bufferHeight, scalingBuffer), RotatingBase<Rotating>(rotation) {
} }
RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight) requires(Owning) : RenderingElement2DBase<T, Frames>(anchor, opaque), ScalingBase<Scaling, Owning, Alignment>(bufferWidth, bufferHeight) { RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight) requires(Owning) : RenderingElement2DBase<T, Frames>(anchor, opaque), ScalingBase<T, Scaling, Owning, Alignment>(bufferWidth, bufferHeight) {
} }
RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, std::uint32_t rotation) requires(Owning && Rotating) : RenderingElement2DBase<T, Frames>(anchor, opaque), ScalingBase<Scaling, Owning, Alignment>(bufferWidth, bufferHeight) , RotatingBase<Rotating>(rotation) { RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, std::uint32_t rotation) requires(Owning && Rotating) : RenderingElement2DBase<T, Frames>(anchor, opaque), ScalingBase<T, Scaling, Owning, Alignment>(bufferWidth, bufferHeight) , RotatingBase<Rotating>(rotation) {
} }
RenderingElement2D(Anchor2D anchor, TextureAsset<Vector<std::uint8_t, 4, Alignment>>& texture) requires(!Owning && Scaling) : RenderingElement2DBase<T, Frames>(anchor, texture.opaque), ScalingBase<Scaling, Owning, Alignment>(texture.pixels.data(), texture.sizeX, texture.sizeY) { RenderingElement2D(Anchor2D anchor, TextureAsset<Vector<T, 4, Alignment>>& texture) requires(!Owning && Scaling) : RenderingElement2DBase<T, Frames>(anchor, texture.opaque), ScalingBase<T, Scaling, Owning, Alignment>(texture.pixels.data(), texture.sizeX, texture.sizeY) {
} }
RenderingElement2D(Anchor2D anchor, TextureAsset<Vector<std::uint8_t, 4, Alignment>>& texture, std::uint32_t rotation) requires(!Owning && Scaling && Rotating) : RenderingElement2DBase<T, Frames>(anchor, texture.opaque), ScalingBase<Scaling, Owning, Alignment>(texture.pixels.data(), texture.sizeX, texture.sizeY), RotatingBase<Rotating>(rotation) { RenderingElement2D(Anchor2D anchor, TextureAsset<Vector<T, 4, Alignment>>& texture, std::uint32_t rotation) requires(!Owning && Scaling && Rotating) : RenderingElement2DBase<T, Frames>(anchor, texture.opaque), ScalingBase<T, Scaling, Owning, Alignment>(texture.pixels.data(), texture.sizeX, texture.sizeY), RotatingBase<Rotating>(rotation) {
} }
@ -62,10 +62,10 @@ export namespace Crafter {
void ScaleNearestNeighbor() requires(Scaling) { void ScaleNearestNeighbor() requires(Scaling) {
for (std::uint32_t y = 0; y < this->scaled.size.y; y++) { for (std::uint32_t y = 0; y < this->scaled.size.y; y++) {
std::uint32_t srcY = y * ScalingBase<true, Owning, Alignment>::bufferHeight / this->scaled.size.y; std::uint32_t srcY = y * ScalingBase<T, true, Owning, Alignment>::bufferHeight / this->scaled.size.y;
for (std::uint32_t x = 0; x < this->scaled.size.x; x++) { for (std::uint32_t x = 0; x < this->scaled.size.x; x++) {
std::uint32_t srcX = x * ScalingBase<true, Owning, Alignment>::bufferWidth / this->scaled.size.x; std::uint32_t srcX = x * ScalingBase<T, true, Owning, Alignment>::bufferWidth / this->scaled.size.x;
this->buffer[y * this->scaled.size.x + x] = ScalingBase<true, Owning, Alignment>::scalingBuffer[srcY * ScalingBase<true, Owning, Alignment>::bufferWidth + srcX]; this->buffer[y * this->scaled.size.x + x] = ScalingBase<T, true, Owning, Alignment>::scalingBuffer[srcY * ScalingBase<T, true, Owning, Alignment>::bufferWidth + srcX];
} }
} }
} }
@ -97,15 +97,15 @@ export namespace Crafter {
const float dstCy = (this->scaled.size.y - 1.0) * 0.5; const float dstCy = (this->scaled.size.y - 1.0) * 0.5;
// Source center // Source center
const float srcCx = (ScalingBase<true, Owning, Alignment>::bufferWidth - 1.0) * 0.5; const float srcCx = (ScalingBase<T, true, Owning, Alignment>::bufferWidth - 1.0) * 0.5;
const float srcCy = (ScalingBase<true, Owning, Alignment>::bufferHeight - 1.0) * 0.5; const float srcCy = (ScalingBase<T, true, Owning, Alignment>::bufferHeight - 1.0) * 0.5;
const float c = std::cos(RotatingBase<true>::rotation); const float c = std::cos(RotatingBase<true>::rotation);
const float s = std::sin(RotatingBase<true>::rotation); const float s = std::sin(RotatingBase<true>::rotation);
// Scale factors (destination → source) // Scale factors (destination → source)
const float scaleX = static_cast<float>(ScalingBase<true, Owning, Alignment>::bufferWidth) / dstWidth; const float scaleX = static_cast<float>(ScalingBase<T, true, Owning, Alignment>::bufferWidth) / dstWidth;
const float scaleY = static_cast<float>(ScalingBase<true, Owning, Alignment>::bufferHeight) / dstHeight; const float scaleY = static_cast<float>(ScalingBase<T, true, Owning, Alignment>::bufferHeight) / dstHeight;
for (std::uint32_t yB = 0; yB < this->scaled.size.y; ++yB) { for (std::uint32_t yB = 0; yB < this->scaled.size.y; ++yB) {
for (std::uint32_t xB = 0; xB < this->scaled.size.x; ++xB) { for (std::uint32_t xB = 0; xB < this->scaled.size.x; ++xB) {
@ -122,8 +122,8 @@ export namespace Crafter {
const std::int32_t srcX = static_cast<std::int32_t>(std::round(sx)); const std::int32_t srcX = static_cast<std::int32_t>(std::round(sx));
const std::int32_t srcY = static_cast<std::int32_t>(std::round(sy)); const std::int32_t srcY = static_cast<std::int32_t>(std::round(sy));
if (srcX >= 0 && srcX < ScalingBase<true, Owning, Alignment>::bufferWidth && srcY >= 0 && srcY < ScalingBase<true, Owning, Alignment>::bufferHeight) { if (srcX >= 0 && srcX < ScalingBase<T, true, Owning, Alignment>::bufferWidth && srcY >= 0 && srcY < ScalingBase<T, true, Owning, Alignment>::bufferHeight) {
this->buffer[yB * this->scaled.size.x + xB] = ScalingBase<true, Owning, Alignment>::scalingBuffer[srcY * ScalingBase<true, Owning, Alignment>::bufferWidth + srcX]; this->buffer[yB * this->scaled.size.x + xB] = ScalingBase<T, true, Owning, Alignment>::scalingBuffer[srcY * ScalingBase<T, true, Owning, Alignment>::bufferWidth + srcX];
} }
} }
} }
@ -208,7 +208,7 @@ export namespace Crafter {
size = std::min(maxFontHeight, maxFontWidth); size = std::min(maxFontHeight, maxFontWidth);
} else { } else {
if constexpr(Scaling) { if constexpr(Scaling) {
lines.resize(ScalingBase<true, Owning, Alignment>::bufferHeight / lines.size()); lines.resize(ScalingBase<T, true, Owning, Alignment>::bufferHeight / lines.size());
} else { } else {
lines.resize(this->scaled.size.y / lines.size()); lines.resize(this->scaled.size.y / lines.size());
} }
@ -284,7 +284,7 @@ export namespace Crafter {
size = this->scaled.size.y / lineHeightPerFont; size = this->scaled.size.y / lineHeightPerFont;
} else { } else {
if constexpr(Scaling) { if constexpr(Scaling) {
lines.resize(ScalingBase<true, Owning, Alignment>::bufferHeight / lines.size()); lines.resize(ScalingBase<T, true, Owning, Alignment>::bufferHeight / lines.size());
} else { } else {
lines.resize(this->scaled.size.y / lines.size()); lines.resize(this->scaled.size.y / lines.size());
} }
@ -293,7 +293,27 @@ export namespace Crafter {
return lines; return lines;
} }
void RenderText(std::span<const std::string_view> lines, float size, Vector<std::uint8_t, 4> color, Font& font, TextAlignment alignment = TextAlignment::Left, std::uint32_t offsetX = 0, std::uint32_t offsetY = 0, OpaqueType opaque = OpaqueType::FullyOpaque) {
int utf8_decode(const char* s, int* bytes_consumed) {
unsigned char c = s[0];
if (c < 0x80) {
*bytes_consumed = 1;
return c;
} else if ((c & 0xE0) == 0xC0) {
*bytes_consumed = 2;
return ((c & 0x1F) << 6) | (s[1] & 0x3F);
} else if ((c & 0xF0) == 0xE0) {
*bytes_consumed = 3;
return ((c & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
} else if ((c & 0xF8) == 0xF0) {
*bytes_consumed = 4;
return ((c & 0x07) << 18) | ((s[1] & 0x3F) << 12) | ((s[2] & 0x3F) << 6) | (s[3] & 0x3F);
}
*bytes_consumed = 1;
return 0xFFFD; // replacement char
}
void RenderText(std::span<const std::string_view> lines, float size, Vector<T, 4> color, Font& font, TextAlignment alignment = TextAlignment::Left, std::uint32_t offsetX = 0, std::uint32_t offsetY = 0, OpaqueType opaque = OpaqueType::FullyOpaque) {
float scale = stbtt_ScaleForPixelHeight(&font.font, size); float scale = stbtt_ScaleForPixelHeight(&font.font, size);
int baseline = (int)(font.ascent * scale); int baseline = (int)(font.ascent * scale);
std::uint32_t lineHeight = (font.ascent - font.descent) * scale; std::uint32_t lineHeight = (font.ascent - font.descent) * scale;
@ -320,8 +340,13 @@ export namespace Crafter {
break; break;
} }
for (std::size_t i = 0; i < line.size(); ++i) { const char* p = line.data();
int codepoint = line[i]; const char* end = p + line.size();
while (p < end) {
int bytes;
int codepoint = utf8_decode(p, &bytes);
p += bytes;
int ax; int ax;
int lsb; int lsb;
@ -346,12 +371,12 @@ export namespace Crafter {
// Only draw pixels that are within our scaled buffer bounds // Only draw pixels that are within our scaled buffer bounds
if constexpr(Scaling) { if constexpr(Scaling) {
if (bufferX >= 0 && bufferX < ScalingBase<true, Owning, Alignment>::bufferWidth && bufferY >= 0 && bufferY < ScalingBase<true, Owning, Alignment>::bufferHeight) { if (bufferX >= 0 && bufferX < ScalingBase<T, true, Owning, Alignment>::bufferWidth && bufferY >= 0 && bufferY < ScalingBase<T, true, Owning, Alignment>::bufferHeight) {
ScalingBase<true, Owning, Alignment>::scalingBuffer[bufferY * ScalingBase<true, Owning, Alignment>::bufferWidth + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]}; ScalingBase<T, true, Owning, Alignment>::scalingBuffer[bufferY * ScalingBase<T, true, Owning, Alignment>::bufferWidth + bufferX] = {color.r, color.g, color.b, static_cast<T>(bitmap[j * w + i])};
} }
} else { } else {
if (bufferX >= 0 && bufferX < (int)this->scaled.size.x && bufferY >= 0 && bufferY < (int)this->scaled.size.y) { if (bufferX >= 0 && bufferX < (int)this->scaled.size.x && bufferY >= 0 && bufferY < (int)this->scaled.size.y) {
this->buffer[bufferY * this->scaled.size.x + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]}; this->buffer[bufferY * this->scaled.size.x + bufferX] = {color.r, color.g, color.b, static_cast<T>(bitmap[j * w + i])};
} }
} }
} }
@ -366,14 +391,14 @@ export namespace Crafter {
// Only draw pixels that are within our scaled buffer bounds // Only draw pixels that are within our scaled buffer bounds
if constexpr(Scaling) { if constexpr(Scaling) {
if (bufferX >= 0 && bufferX < ScalingBase<true, Owning, Alignment>::bufferWidth && bufferY >= 0 && bufferY < ScalingBase<true, Owning, Alignment>::bufferHeight) { if (bufferX >= 0 && bufferX < ScalingBase<T, true, Owning, Alignment>::bufferWidth && bufferY >= 0 && bufferY < ScalingBase<T, true, Owning, Alignment>::bufferHeight) {
ScalingBase<true, Owning, Alignment>::scalingBuffer[bufferY * ScalingBase<true, Owning, Alignment>::bufferWidth + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]}; ScalingBase<T, true, Owning, Alignment>::scalingBuffer[bufferY * ScalingBase<T, true, Owning, Alignment>::bufferWidth + bufferX] = {color.r, color.g, color.b, static_cast<T>(bitmap[j * w + i])};
} }
} else { } else {
if (bufferX >= 0 && bufferX < (int)this->scaled.size.x && bufferY >= 0 && bufferY < (int)this->scaled.size.y) { if (bufferX >= 0 && bufferX < (int)this->scaled.size.x && bufferY >= 0 && bufferY < (int)this->scaled.size.y) {
std::uint8_t alpha = bitmap[j * w + i]; std::uint8_t alpha = bitmap[j * w + i];
if(alpha != 0) { if(alpha != 0) {
this->buffer[bufferY * this->scaled.size.x + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]}; this->buffer[bufferY * this->scaled.size.x + bufferX] = {color.r, color.g, color.b, static_cast<T>(bitmap[j * w + i])};
} }
} }
} }
@ -389,29 +414,41 @@ export namespace Crafter {
// Only draw pixels that are within our scaled buffer bounds // Only draw pixels that are within our scaled buffer bounds
if constexpr(Scaling) { if constexpr(Scaling) {
if (bufferX >= 0 && bufferX < ScalingBase<true, Owning, Alignment>::bufferWidth && bufferY >= 0 && bufferY < ScalingBase<true, Owning, Alignment>::bufferHeight) { if (bufferX >= 0 && bufferX < ScalingBase<T, true, Owning, Alignment>::bufferWidth && bufferY >= 0 && bufferY < ScalingBase<T, true, Owning, Alignment>::bufferHeight) {
ScalingBase<true, Owning, Alignment>::scalingBuffer[bufferY * ScalingBase<true, Owning, Alignment>::bufferWidth + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]}; ScalingBase<T, true, Owning, Alignment>::scalingBuffer[bufferY * ScalingBase<T, true, Owning, Alignment>::bufferWidth + bufferX] = {color.r, color.g, color.b, static_cast<T>(bitmap[j * w + i])};
} }
} else { } else {
if (bufferX >= 0 && bufferX < (int)this->scaled.size.x && bufferY >= 0 && bufferY < (int)this->scaled.size.y) { if (bufferX >= 0 && bufferX < (int)this->scaled.size.x && bufferY >= 0 && bufferY < (int)this->scaled.size.y) {
std::uint8_t alpha = bitmap[j * w + i]; if constexpr(std::same_as<T, std::uint8_t>) {
std::uint8_t alpha = bitmap[j * w + i];
if(alpha == 0) { Vector<T, 4, Alignment> dst = this->buffer[bufferY * this->scaled.size.x + bufferX];
continue;
}
Vector<std::uint8_t, 4, Alignment> dst = this->buffer[bufferY * this->scaled.size.x + bufferX];
float srcA = (alpha / 255.0f) * (color.a / 255.0f); float srcA = (alpha / 255.0f) * (color.a / 255.0f);
float dstA = dst.a / 255.0f; float dstA = dst.a / 255.0f;
float outA = srcA + dstA * (1.0f - srcA); float oneMinusSrcA = 1.0f - color.a;
this->buffer[bufferY * this->scaled.size.x + bufferX] = Vector<std::uint8_t, 4, Alignment>(
static_cast<std::uint8_t>((color.r * srcA + dst.r * dstA * (1.0f - srcA)) / outA), float outA = srcA + dstA * (1.0f - srcA);
static_cast<std::uint8_t>((color.g * srcA + dst.g * dstA * (1.0f - srcA)) / outA), this->buffer[bufferY * this->scaled.size.x + bufferX] = Vector<std::uint8_t, 4, Alignment>(
static_cast<std::uint8_t>((color.b * srcA + dst.b * dstA * (1.0f - srcA)) / outA), static_cast<std::uint8_t>((color.r * srcA + dst.r * dstA * (1.0f - srcA)) / outA),
static_cast<std::uint8_t>(outA * 255) static_cast<std::uint8_t>((color.g * srcA + dst.g * dstA * (1.0f - srcA)) / outA),
); static_cast<std::uint8_t>((color.b * srcA + dst.b * dstA * (1.0f - srcA)) / outA),
static_cast<std::uint8_t>(outA * 255)
);
} else if constexpr(std::same_as<T, _Float16>) {
std::uint8_t alpha = bitmap[j * w + i];
_Float16 srcA = (_Float16(alpha)/_Float16(255.0f))*color.a;
Vector<_Float16, 4, Alignment> dst = this->buffer[bufferY * this->scaled.size.x + bufferX];
_Float16 outA = srcA + dst.a * (1.0f - srcA);
this->buffer[bufferY * this->scaled.size.x + bufferX] = Vector<_Float16, 4, Alignment>(
(color.r * srcA + dst.r * dst.a * (1.0f - srcA)),
(color.g * srcA + dst.g * dst.a * (1.0f - srcA)),
(color.b * srcA + dst.b * dst.a * (1.0f - srcA)),
outA
);
}
} }
} }
} }
@ -422,8 +459,9 @@ export namespace Crafter {
x += (int)(ax * scale); x += (int)(ax * scale);
if (i + 1 < line.size()) { if (p + 1 < end) {
x += (int)stbtt_GetGlyphKernAdvance(&font.font, codepoint, line[i+1]); int next;
x += (int)stbtt_GetGlyphKernAdvance(&font.font, codepoint, utf8_decode(p+1, &next));
} }
} }
currentY += lineHeight; currentY += lineHeight;

View file

@ -42,9 +42,9 @@ export namespace Crafter {
Buffer Buffer
}; };
template<std::uint8_t Alignment = 0> template<typename T, std::uint8_t Alignment = 0>
struct RenderElement2DScalingOwning { struct RenderElement2DScalingOwning {
std::vector<Vector<std::uint8_t, 4, Alignment>> scalingBuffer; std::vector<Vector<T, 4, Alignment>> scalingBuffer;
std::uint32_t bufferWidth; std::uint32_t bufferWidth;
std::uint32_t bufferHeight; std::uint32_t bufferHeight;
RenderElement2DScalingOwning() = default; RenderElement2DScalingOwning() = default;
@ -53,13 +53,13 @@ export namespace Crafter {
} }
}; };
template<std::uint8_t Alignment = 0> template<typename T, std::uint8_t Alignment = 0>
struct RenderElement2DScalingNonOwning { struct RenderElement2DScalingNonOwning {
Vector<std::uint8_t, 4, Alignment>* scalingBuffer; Vector<T, 4, Alignment>* scalingBuffer;
std::uint32_t bufferWidth; std::uint32_t bufferWidth;
std::uint32_t bufferHeight; std::uint32_t bufferHeight;
RenderElement2DScalingNonOwning() = default; RenderElement2DScalingNonOwning() = default;
RenderElement2DScalingNonOwning(Vector<std::uint8_t, 4, Alignment>* scalingBuffer, std::uint32_t bufferWidth, std::uint32_t bufferHeight) : scalingBuffer(scalingBuffer), bufferWidth(bufferWidth), bufferHeight(bufferHeight) { RenderElement2DScalingNonOwning(Vector<T, 4, Alignment>* scalingBuffer, std::uint32_t bufferWidth, std::uint32_t bufferHeight) : scalingBuffer(scalingBuffer), bufferWidth(bufferWidth), bufferHeight(bufferHeight) {
} }
}; };
@ -76,13 +76,13 @@ export namespace Crafter {
struct EmptyScalingBase {}; struct EmptyScalingBase {};
struct EmptyRotatingBase {}; struct EmptyRotatingBase {};
template<bool Scaling, bool Owning, std::uint8_t Alignment = 0> template<typename T, bool Scaling, bool Owning, std::uint8_t Alignment = 0>
using ScalingBase = using ScalingBase =
std::conditional_t< std::conditional_t<
Scaling, Scaling,
std::conditional_t<Owning, std::conditional_t<Owning,
RenderElement2DScalingOwning<Alignment>, RenderElement2DScalingOwning<T, Alignment>,
RenderElement2DScalingNonOwning<Alignment>>, RenderElement2DScalingNonOwning<T, Alignment>>,
EmptyScalingBase EmptyScalingBase
>; >;
@ -115,7 +115,7 @@ export namespace Crafter {
redraw[i] = true; redraw[i] = true;
} }
} }
void CopyNearestNeighbor(Vector<std::uint8_t, 4>* dst, std::uint16_t dstSizeX, std::uint16_t dstScaledSizeX, std::uint16_t dstScaledSizeY, std::uint16_t offsetX, std::uint16_t offsetY) { void CopyNearestNeighbor(Vector<T, 4>* dst, std::uint16_t dstSizeX, std::uint16_t dstScaledSizeX, std::uint16_t dstScaledSizeY, std::uint16_t offsetX, std::uint16_t offsetY) {
for (std::uint16_t y = 0; y < dstScaledSizeY; y++) { for (std::uint16_t y = 0; y < dstScaledSizeY; y++) {
std::uint16_t srcY = y * scaled.size.y / dstScaledSizeY; std::uint16_t srcY = y * scaled.size.y / dstScaledSizeY;
std::uint16_t dstY = y + offsetY; std::uint16_t dstY = y + offsetY;

View file

@ -66,15 +66,20 @@ export namespace Crafter {
dirty.right = std::min(std::uint16_t(element->scaled.position.x+element->scaled.size.x), dirty.right); dirty.right = std::min(std::uint16_t(element->scaled.position.x+element->scaled.size.x), dirty.right);
dirty.bottom = std::min(std::uint16_t(element->scaled.position.y+element->scaled.size.y), dirty.bottom); dirty.bottom = std::min(std::uint16_t(element->scaled.position.y+element->scaled.size.y), dirty.bottom);
if(dirty.right <= dirty.left || dirty.bottom <= dirty.top) {
continue;
}
const Vector<T, 4, 4>* src_buffer = element->buffer.data(); const Vector<T, 4, 4>* src_buffer = element->buffer.data();
std::int32_t src_width = element->scaled.size.x; std::uint16_t src_width = element->scaled.size.x;
std::int32_t src_height = element->scaled.size.y; std::uint16_t src_height = element->scaled.size.y;
switch (element->opaque) { switch (element->opaque) {
case OpaqueType::FullyOpaque: { case OpaqueType::FullyOpaque: {
for (std::int32_t y = dirty.top; y < dirty.bottom; y++) { for (std::uint16_t y = dirty.top; y < dirty.bottom; y++) {
std::int32_t src_y = y - element->scaled.position.y; std::uint16_t src_y = y - element->scaled.position.y;
std::memcpy(&this->buffer[frame][y * this->sizeX], &src_buffer[src_y * src_width], dirty.right-dirty.left*sizeof(Vector<T, Channels, Alignment>)); std::uint16_t src_x = dirty.left - element->scaled.position.x;
std::memcpy(&this->buffer[frame][y * this->sizeX + dirty.left], &src_buffer[src_y * src_width + src_x], (dirty.right - dirty.left) * sizeof(Vector<T, Channels, Alignment>));
} }
break; break;
} }
@ -83,20 +88,20 @@ export namespace Crafter {
if constexpr(std::same_as<T, _Float16>) { if constexpr(std::same_as<T, _Float16>) {
for (std::uint16_t y = dirty.top; y < dirty.bottom; y++) { for (std::uint16_t y = dirty.top; y < dirty.bottom; y++) {
std::uint16_t src_y = y - element->scaled.position.y; std::uint16_t src_y = y - element->scaled.position.y;
std::uint32_t pixel_width = dirty.right - dirty.left; std::uint16_t pixel_width = dirty.right - dirty.left;
constexpr std::uint32_t simd_width = VectorF16<1, 1>::MaxElement / 4; constexpr std::uint32_t simd_width = VectorF16<1, 1>::MaxElement / 4;
std::uint32_t rows = pixel_width / simd_width; std::uint32_t rows = pixel_width / simd_width;
for (std::uint32_t x = 0; x < rows; x++) { for (std::uint32_t x = 0; x < rows; x++) {
std::uint16_t px = dirty.left + x * simd_width; std::uint16_t px = dirty.left + x * simd_width;
std::uint16_t src_x = px - element->scaled.position.x; std::uint16_t src_x = px - element->scaled.position.x;
std::uint16_t dst_x = px;
VectorF16<4, simd_width> src(&src_buffer[src_y * src_width + src_x].v[0]); VectorF16<4, simd_width> src(&src_buffer[src_y * src_width + src_x].v[0]);
VectorF16<4, simd_width> dst(&buffer[frame][y * this->sizeX + dst_x].v[0]); VectorF16<4, simd_width> dst(&buffer[frame][y * this->sizeX + px].v[0]);
VectorF16<4, simd_width> oneMinusSrcA = VectorF16<4, simd_width>(1) - src.Shuffle<{{3, 3, 3, 3}}>(); VectorF16<4, simd_width> oneMinusSrcA = VectorF16<4, simd_width>(1) - src.Shuffle<{{3, 3, 3, 3}}>();
VectorF16<4, simd_width> result = VectorF16<4, simd_width>::MulitplyAdd(dst, oneMinusSrcA, src); VectorF16<4, simd_width> result = VectorF16<4, simd_width>::MulitplyAdd(dst, oneMinusSrcA, src);
result.Store(buffer[frame][y * this->sizeX + dst_x]); result.Store(&buffer[frame][y * this->sizeX + px].v[0]);
} }
std::uint32_t remainder = pixel_width - (rows * simd_width); std::uint32_t remainder = pixel_width - (rows * simd_width);
@ -108,7 +113,7 @@ export namespace Crafter {
Vector<T, Channels, Alignment> src = src_buffer[src_y * src_width + src_x]; Vector<T, Channels, Alignment> src = src_buffer[src_y * src_width + src_x];
Vector<T, Channels, Alignment> dst = buffer[frame][y * this->sizeX + px]; Vector<T, Channels, Alignment> dst = buffer[frame][y * this->sizeX + px];
_Float16 oneMinusSrcA = (_Float16)1.0f - src[3]; _Float16 oneMinusSrcA = (_Float16)1.0f - src.a;
buffer[frame][y * this->sizeX + px] = Vector<T, Channels, Alignment>( buffer[frame][y * this->sizeX + px] = Vector<T, Channels, Alignment>(
src.r + dst.r * oneMinusSrcA, src.r + dst.r * oneMinusSrcA,
@ -119,9 +124,10 @@ export namespace Crafter {
} }
} }
} else { } else {
for (std::int32_t y = dirty.top; y < dirty.bottom; y++) { for (std::uint16_t y = dirty.top; y < dirty.bottom; y++) {
std::int32_t src_y = y - element->scaled.position.y; std::uint16_t src_y = y - element->scaled.position.y;
std::memcpy(&this->buffer[frame][y * this->sizeX], &src_buffer[src_y * src_width], dirty.right-dirty.left*sizeof(Vector<T, Channels, Alignment>)); std::uint16_t src_x = dirty.left - element->scaled.position.x;
std::memcpy(&this->buffer[frame][y * this->sizeX + dirty.left], &src_buffer[src_y * src_width + src_x], (dirty.right - dirty.left) * sizeof(Vector<T, Channels, Alignment>));
} }
} }
break; break;