render target improvements
This commit is contained in:
parent
1bc6293610
commit
789bb307d5
12 changed files with 368 additions and 428 deletions
|
|
@ -30,7 +30,7 @@ GridElement::GridElement(std::uint32_t columns, std::uint32_t rows, std::int32_t
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridElement::UpdatePositionScaled(Rendertarget& window) {
|
void GridElement::UpdatePositionScaled(RendertargetBase& window) {
|
||||||
std::int32_t cellWidth = (paddingX * 2) - (spacingX * (columns - 1)) / columns;
|
std::int32_t cellWidth = (paddingX * 2) - (spacingX * (columns - 1)) / columns;
|
||||||
std::int32_t cellHeight = (paddingY * 2) - (spacingY * (rows - 1)) / rows;
|
std::int32_t cellHeight = (paddingY * 2) - (spacingY * (rows - 1)) / rows;
|
||||||
|
|
||||||
|
|
@ -57,12 +57,12 @@ void GridElement::UpdatePositionScaled(Rendertarget& window) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridElement::UpdatePosition(Rendertarget& window) {
|
void GridElement::UpdatePosition(RendertargetBase& window) {
|
||||||
ScaleElement(window.transform);
|
ScaleElement(window.transform);
|
||||||
UpdatePositionScaled(window);
|
UpdatePositionScaled(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridElement::UpdatePosition(Rendertarget& window, Transform2D& parent) {
|
void GridElement::UpdatePosition(RendertargetBase& window, Transform2D& parent) {
|
||||||
ScaleElement(parent);
|
ScaleElement(parent);
|
||||||
UpdatePositionScaled(window);
|
UpdatePositionScaled(window);
|
||||||
}
|
}
|
||||||
|
|
@ -1,249 +0,0 @@
|
||||||
/*
|
|
||||||
Crafter®.Graphics
|
|
||||||
Copyright (C) 2026 Catcrafts®
|
|
||||||
Catcrafts.net
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 3.0 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
module Crafter.Graphics:Rendertarget_impl;
|
|
||||||
import :Window;
|
|
||||||
import :RenderingElement2D;
|
|
||||||
import std;
|
|
||||||
|
|
||||||
using namespace Crafter;
|
|
||||||
|
|
||||||
Rendertarget::Rendertarget(std::int32_t sizeX, std::int32_t sizeY) : transform({0, 0, static_cast<float>(sizeX), static_cast<float>(sizeY), 0, 0, 0}) {
|
|
||||||
transform.scaled.size.x = sizeX;
|
|
||||||
transform.scaled.size.y = sizeY;
|
|
||||||
transform.scaled.position.x = 0;
|
|
||||||
transform.scaled.position.y = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Rendertarget::AddDirtyRect(ScaleData2D scale) {
|
|
||||||
ClipRect rect {
|
|
||||||
.left = std::max(scale.position.x, std::int32_t(0)),
|
|
||||||
.right = std::min(scale.position.x + scale.size.x, sizeX),
|
|
||||||
.top = std::max(scale.position.y, std::int32_t(0)),
|
|
||||||
.bottom = std::min(scale.position.y + scale.size.y, sizeY),
|
|
||||||
};
|
|
||||||
dirtyRects.push_back(rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void blend_pixel_optimized(Vector<std::uint8_t, 4>& dst, const Vector<std::uint8_t, 4>& src) {
|
|
||||||
if(src.a == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
float srcA = src.a / 255.0f;
|
|
||||||
float dstA = dst.a / 255.0f;
|
|
||||||
|
|
||||||
float outA = srcA + dstA * (1.0f - srcA);
|
|
||||||
if (outA > 0.0f) {
|
|
||||||
dst = Vector<std::uint8_t, 4>(
|
|
||||||
static_cast<std::uint8_t>((src.b * srcA + dst.b * dstA * (1.0f - srcA)) / outA),
|
|
||||||
static_cast<std::uint8_t>((src.g * srcA + dst.g * dstA * (1.0f - srcA)) / outA),
|
|
||||||
static_cast<std::uint8_t>((src.r * srcA + dst.r * dstA * (1.0f - srcA)) / outA),
|
|
||||||
static_cast<std::uint8_t>(outA * 255)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Rendertarget::RenderElement(RenderingElement2DBase* element) {
|
|
||||||
#ifdef CRAFTER_TIMING
|
|
||||||
auto start = std::chrono::high_resolution_clock::now();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(element->scaled.size.x < 1 || element->scaled.size.y < 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(ClipRect dirty : dirtyRects) {
|
|
||||||
dirty.left = std::max(element->scaled.position.x, dirty.left);
|
|
||||||
dirty.top = std::max(element->scaled.position.y, dirty.top);
|
|
||||||
dirty.right = std::min(element->scaled.position.x+element->scaled.size.x, dirty.right);
|
|
||||||
dirty.bottom = std::min(element->scaled.position.y+element->scaled.size.y, dirty.bottom);
|
|
||||||
|
|
||||||
const Vector<std::uint8_t, 4>* src_buffer = element->buffer.data();
|
|
||||||
std::int32_t src_width = element->scaled.size.x;
|
|
||||||
std::int32_t src_height = element->scaled.size.y;
|
|
||||||
|
|
||||||
switch (element->opaque) {
|
|
||||||
case OpaqueType::FullyOpaque:
|
|
||||||
// For fully opaque, just copy pixels directly
|
|
||||||
for (std::int32_t y = dirty.top; y < dirty.bottom; y++) {
|
|
||||||
std::int32_t src_y = y - element->scaled.position.y;
|
|
||||||
|
|
||||||
for (std::int32_t x = dirty.left; x < dirty.right; x++) {
|
|
||||||
std::int32_t src_x = x - element->scaled.position.x;
|
|
||||||
|
|
||||||
buffer[y * sizeX + x] = src_buffer[src_y * src_width + src_x];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OpaqueType::SemiOpaque:
|
|
||||||
// For semi-opaque, we can avoid blending when alpha is 0 or 255
|
|
||||||
for (std::int32_t y = dirty.top; y < dirty.bottom; y++) {
|
|
||||||
std::int32_t src_y = y - element->scaled.position.y;
|
|
||||||
|
|
||||||
for (std::int32_t x = dirty.left; x < dirty.right; x++) {
|
|
||||||
std::int32_t src_x = x - element->scaled.position.x;
|
|
||||||
Vector<std::uint8_t, 4> src_pixel = src_buffer[src_y * src_width + src_x];
|
|
||||||
|
|
||||||
if (src_pixel.a == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
buffer[y * sizeX + x] = src_pixel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OpaqueType::Transparent:
|
|
||||||
// For transparent, always perform blending
|
|
||||||
for (std::int32_t y = dirty.top; y < dirty.bottom; y++) {
|
|
||||||
std::int32_t src_y = y - element->scaled.position.y;
|
|
||||||
|
|
||||||
for (std::int32_t x = dirty.left; x < dirty.right; x++) {
|
|
||||||
std::int32_t src_x = x - element->scaled.position.x;
|
|
||||||
blend_pixel_optimized(buffer[y * sizeX + x], src_buffer[src_y * src_width + src_x]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CRAFTER_TIMING
|
|
||||||
auto end = std::chrono::high_resolution_clock::now();
|
|
||||||
renderTimings.push_back({element, element->scaled.size.x, element->scaled.size.y, end-start});
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Rendertarget::Render() {
|
|
||||||
//std::vector<ClipRect> newClip;
|
|
||||||
|
|
||||||
// for (std::uint32_t i = 0; i < dirtyRects.size(); i++) {
|
|
||||||
// ClipRect rect = dirtyRects[i];
|
|
||||||
// for (std::uint32_t i2 = i + 1; i2 < dirtyRects.size(); i2++) {
|
|
||||||
// ClipRect existing = dirtyRects[i2];
|
|
||||||
// if(rect.bottom >= existing.top && rect.top <= existing.top) {
|
|
||||||
// newClip.push_back({
|
|
||||||
// .left = rect.left,
|
|
||||||
// .right = rect.right,
|
|
||||||
// .top = rect.top,
|
|
||||||
// .bottom = existing.top,
|
|
||||||
// });
|
|
||||||
// //-| shape
|
|
||||||
// if(rect.right > existing.right) {
|
|
||||||
// newClip.push_back({
|
|
||||||
// .left = existing.right,
|
|
||||||
// .right = rect.right,
|
|
||||||
// .top = existing.top,
|
|
||||||
// .bottom = existing.bottom,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// //|- shape
|
|
||||||
// if(rect.left < existing.left) {
|
|
||||||
// newClip.push_back({
|
|
||||||
// .left = rect.left,
|
|
||||||
// .right = existing.left,
|
|
||||||
// .top = existing.top,
|
|
||||||
// .bottom = existing.bottom,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// //-| or |- shape where rect extends further down
|
|
||||||
// if(rect.bottom > existing.bottom) {
|
|
||||||
// newClip.push_back({
|
|
||||||
// .left = rect.left,
|
|
||||||
// .right = rect.right,
|
|
||||||
// .top = existing.bottom,
|
|
||||||
// .bottom = rect.bottom,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// goto inner;
|
|
||||||
// }
|
|
||||||
// if (rect.left <= existing.right && rect.right >= existing.left) {
|
|
||||||
// newClip.push_back({
|
|
||||||
// .left = rect.left,
|
|
||||||
// .right = existing.left,
|
|
||||||
// .top = rect.top,
|
|
||||||
// .bottom = rect.bottom,
|
|
||||||
// });
|
|
||||||
// if (rect.right > existing.right) {
|
|
||||||
// newClip.push_back({
|
|
||||||
// .left = existing.right,
|
|
||||||
// .right = rect.right,
|
|
||||||
// .top = rect.top,
|
|
||||||
// .bottom = rect.bottom,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// goto inner;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// newClip.push_back(rect);
|
|
||||||
// inner:;
|
|
||||||
// }
|
|
||||||
|
|
||||||
//dirtyRects = std::move(newClip);
|
|
||||||
|
|
||||||
// std::memset(buffer, 0, width*height*4);
|
|
||||||
|
|
||||||
// std::cout << dirtyRects.size() << std::endl;
|
|
||||||
// // Color palette
|
|
||||||
// static const std::vector<Vector<std::uint8_t, 4>> colors = {
|
|
||||||
// {255, 0, 0, 255}, // red
|
|
||||||
// { 0, 255, 0, 255}, // green
|
|
||||||
// { 0, 0, 255, 255}, // blue
|
|
||||||
// {255, 255, 0, 255}, // yellow
|
|
||||||
// {255, 0, 255, 255}, // magenta
|
|
||||||
// { 0, 255, 255, 255}, // cyan
|
|
||||||
// };
|
|
||||||
|
|
||||||
// std::size_t rectIndex = 0;
|
|
||||||
|
|
||||||
// for (const ClipRect& rect : dirtyRects) {
|
|
||||||
// const Vector<std::uint8_t, 4>& color = colors[rectIndex % colors.size()];
|
|
||||||
|
|
||||||
// std::cout << std::format(
|
|
||||||
// "ClipRect {}: [{}, {}, {}, {}] Color = RGBA({}, {}, {}, {})",
|
|
||||||
// rectIndex,
|
|
||||||
// rect.left, rect.top, rect.right, rect.bottom,
|
|
||||||
// color.r, color.g, color.b, color.a
|
|
||||||
// ) << std::endl;
|
|
||||||
|
|
||||||
// for (std::int32_t y = rect.top; y < rect.bottom; ++y) {
|
|
||||||
// for (std::int32_t x = rect.left; x < rect.right; ++x) {
|
|
||||||
// buffer[y * width + x] = color;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ++rectIndex;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (!dirtyRects.empty()) {
|
|
||||||
for (ClipRect rect : dirtyRects) {
|
|
||||||
for (std::int32_t y = rect.top; y < rect.bottom; y++) {
|
|
||||||
for (std::int32_t x = rect.left; x < rect.right; x++) {
|
|
||||||
buffer[y * sizeX + x] = {0, 0, 0, 0};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(RenderingElement2DBase* child : elements) {
|
|
||||||
RenderElement(child);
|
|
||||||
}
|
|
||||||
dirtyRects.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -36,14 +36,14 @@ Transform2D::Transform2D(Anchor2D anchor) : anchor(anchor) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Transform2D::UpdatePosition(Rendertarget& window) {
|
void Transform2D::UpdatePosition(RendertargetBase& window) {
|
||||||
ScaleElement(window.transform);
|
ScaleElement(window.transform);
|
||||||
for(Transform2D* child : children) {
|
for(Transform2D* child : children) {
|
||||||
child->UpdatePosition(window, *this);
|
child->UpdatePosition(window, *this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Transform2D::UpdatePosition(Rendertarget& window, Transform2D& parent) {
|
void Transform2D::UpdatePosition(RendertargetBase& window, Transform2D& parent) {
|
||||||
ScaleElement(parent);
|
ScaleElement(parent);
|
||||||
for(Transform2D* child : children) {
|
for(Transform2D* child : children) {
|
||||||
child->UpdatePosition(window, *this);
|
child->UpdatePosition(window, *this);
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,8 @@ export namespace Crafter {
|
||||||
std::int32_t paddingX;
|
std::int32_t paddingX;
|
||||||
std::int32_t paddingY;
|
std::int32_t paddingY;
|
||||||
GridElement(std::uint32_t columns, std::uint32_t rows, std::int32_t spacingX, std::int32_t spacingY, std::int32_t paddingX, std::int32_t paddingY, Anchor2D anchor);
|
GridElement(std::uint32_t columns, std::uint32_t rows, std::int32_t spacingX, std::int32_t spacingY, std::int32_t paddingX, std::int32_t paddingY, Anchor2D anchor);
|
||||||
void UpdatePositionScaled(Rendertarget& window);
|
void UpdatePositionScaled(RendertargetBase& window);
|
||||||
void UpdatePosition(Rendertarget& window) override;
|
void UpdatePosition(RendertargetBase& window) override;
|
||||||
void UpdatePosition(Rendertarget& window, Transform2D& parent) override;
|
void UpdatePosition(RendertargetBase& window, Transform2D& parent) override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -41,7 +41,7 @@ export namespace Crafter {
|
||||||
VkImageView imageView;
|
VkImageView imageView;
|
||||||
VkDescriptorImageInfo descriptor;
|
VkDescriptorImageInfo descriptor;
|
||||||
|
|
||||||
void Create(std::uint16_t width, std::uint16_t height, std::uint8_t mipLevels, VkCommandBuffer cmd, VkFormat format) {
|
void Create(std::uint16_t width, std::uint16_t height, std::uint8_t mipLevels, VkCommandBuffer cmd, VkFormat format, VkImageCreateFlags flags, VkImageLayout layout) {
|
||||||
this->width = width;
|
this->width = width;
|
||||||
this->height = height;
|
this->height = height;
|
||||||
this->mipLevels = mipLevels;
|
this->mipLevels = mipLevels;
|
||||||
|
|
@ -58,7 +58,7 @@ export namespace Crafter {
|
||||||
imageInfo.format = format;
|
imageInfo.format = format;
|
||||||
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
imageInfo.usage = flags;
|
||||||
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
|
||||||
|
|
@ -89,12 +89,14 @@ export namespace Crafter {
|
||||||
Device::CheckVkResult(vkCreateImageView(Device::device, &viewInfo, nullptr, &imageView));
|
Device::CheckVkResult(vkCreateImageView(Device::device, &viewInfo, nullptr, &imageView));
|
||||||
|
|
||||||
// Final transition to shader read-only layout
|
// Final transition to shader read-only layout
|
||||||
TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 0, mipLevels);
|
TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_UNDEFINED, layout, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, 0, VK_ACCESS_SHADER_READ_BIT, 0, mipLevels);
|
||||||
|
|
||||||
|
descriptor = { .imageView = imageView, .imageLayout = layout };
|
||||||
}
|
}
|
||||||
|
|
||||||
void Update(VkCommandBuffer cmd) {
|
void Update(VkCommandBuffer cmd, VkImageLayout layout) {
|
||||||
buffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
buffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||||
TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, mipLevels);
|
TransitionImageLayout(cmd, image, layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, 0, mipLevels);
|
||||||
|
|
||||||
VkBufferImageCopy region{};
|
VkBufferImageCopy region{};
|
||||||
region.bufferOffset = 0;
|
region.bufferOffset = 0;
|
||||||
|
|
@ -116,7 +118,7 @@ export namespace Crafter {
|
||||||
);
|
);
|
||||||
|
|
||||||
if(mipLevels > 1) {
|
if(mipLevels > 1) {
|
||||||
TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 0, 1);
|
TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, 0, 1);
|
||||||
|
|
||||||
for (std::uint16_t i = 1; i < mipLevels; ++i) {
|
for (std::uint16_t i = 1; i < mipLevels; ++i) {
|
||||||
std::uint16_t mipWidth = width >> i;
|
std::uint16_t mipWidth = width >> i;
|
||||||
|
|
@ -141,11 +143,13 @@ export namespace Crafter {
|
||||||
blit.dstOffsets[1] = { (int32_t)mipWidth, (int32_t)mipHeight, 1 };
|
blit.dstOffsets[1] = { (int32_t)mipWidth, (int32_t)mipHeight, 1 };
|
||||||
|
|
||||||
vkCmdBlitImage(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR);
|
vkCmdBlitImage(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR);
|
||||||
TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, i, 1);
|
TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, i, 1);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 0, mipLevels);
|
TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, layout, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT, 0, mipLevels);
|
||||||
|
} else {
|
||||||
|
TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, layout, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, 0, mipLevels);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Destroy() {
|
void Destroy() {
|
||||||
|
|
@ -155,7 +159,7 @@ export namespace Crafter {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void TransitionImageLayout(VkCommandBuffer cmd, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout, std::uint32_t mipLevel, std::uint32_t count) {
|
void TransitionImageLayout(VkCommandBuffer cmd, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout, VkPipelineStageFlags sourceStage, VkPipelineStageFlags destinationStage, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, std::uint32_t mipLevel, std::uint32_t count) {
|
||||||
VkImageMemoryBarrier barrier = {};
|
VkImageMemoryBarrier barrier = {};
|
||||||
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||||
barrier.oldLayout = oldLayout;
|
barrier.oldLayout = oldLayout;
|
||||||
|
|
@ -168,71 +172,10 @@ export namespace Crafter {
|
||||||
barrier.subresourceRange.levelCount = count;
|
barrier.subresourceRange.levelCount = count;
|
||||||
barrier.subresourceRange.baseArrayLayer = 0;
|
barrier.subresourceRange.baseArrayLayer = 0;
|
||||||
barrier.subresourceRange.layerCount = 1;
|
barrier.subresourceRange.layerCount = 1;
|
||||||
barrier.srcAccessMask = 0;
|
barrier.srcAccessMask = srcAccessMask;
|
||||||
barrier.dstAccessMask = 0;
|
barrier.dstAccessMask = dstAccessMask;
|
||||||
|
|
||||||
VkPipelineStageFlags sourceStage;
|
|
||||||
VkPipelineStageFlags destinationStage;
|
|
||||||
|
|
||||||
// Different cases for layout transition
|
|
||||||
if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
|
|
||||||
barrier.srcAccessMask = 0;
|
|
||||||
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
||||||
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
|
||||||
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
||||||
// barrier.srcQueueFamilyIndex = VK_QUEUE_TRANSFER_BIT;
|
|
||||||
// barrier.dstQueueFamilyIndex = VK_QUEUE_TRANSFER_BIT;
|
|
||||||
} if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
|
|
||||||
barrier.srcAccessMask = 0;
|
|
||||||
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
||||||
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
|
||||||
destinationStage = VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR;
|
|
||||||
// barrier.srcQueueFamilyIndex = VK_QUEUE_TRANSFER_BIT;
|
|
||||||
// barrier.dstQueueFamilyIndex = VK_QUEUE_GRAPHICS_BIT;
|
|
||||||
} else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
|
|
||||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
||||||
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
||||||
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
||||||
destinationStage = VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR;
|
|
||||||
// barrier.srcQueueFamilyIndex = VK_QUEUE_TRANSFER_BIT;
|
|
||||||
// barrier.dstQueueFamilyIndex = VK_QUEUE_GRAPHICS_BIT;
|
|
||||||
} else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
|
|
||||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
|
||||||
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
||||||
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
||||||
destinationStage = VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR;
|
|
||||||
// barrier.srcQueueFamilyIndex = VK_QUEUE_TRANSFER_BIT;
|
|
||||||
// barrier.dstQueueFamilyIndex = VK_QUEUE_GRAPHICS_BIT;
|
|
||||||
} else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) {
|
|
||||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
||||||
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
|
||||||
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
||||||
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
||||||
// barrier.srcQueueFamilyIndex = VK_QUEUE_TRANSFER_BIT;
|
|
||||||
// barrier.dstQueueFamilyIndex = VK_QUEUE_TRANSFER_BIT;
|
|
||||||
} else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
|
|
||||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
|
||||||
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
||||||
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
||||||
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
||||||
// barrier.srcQueueFamilyIndex = VK_QUEUE_TRANSFER_BIT;
|
|
||||||
// barrier.dstQueueFamilyIndex = VK_QUEUE_TRANSFER_BIT;
|
|
||||||
} else if (oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
|
|
||||||
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
||||||
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
||||||
sourceStage = VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR;
|
|
||||||
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
||||||
// barrier.srcQueueFamilyIndex = VK_QUEUE_GRAPHICS_BIT;
|
|
||||||
// barrier.dstQueueFamilyIndex = VK_QUEUE_TRANSFER_BIT;
|
|
||||||
} else {
|
|
||||||
std::cout << oldLayout << std::endl;
|
|
||||||
std::cout << newLayout << std::endl;
|
|
||||||
throw std::invalid_argument("unsupported layout transition!");
|
|
||||||
}
|
|
||||||
|
|
||||||
vkCmdPipelineBarrier(cmd, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
vkCmdPipelineBarrier(cmd, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||||
|
|
||||||
descriptor = { .imageView = imageView, .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -23,93 +23,12 @@ export module Crafter.Graphics:RenderingElement2D;
|
||||||
import Crafter.Asset;
|
import Crafter.Asset;
|
||||||
import std;
|
import std;
|
||||||
import :Transform2D;
|
import :Transform2D;
|
||||||
|
import :RenderingElement2DBase;
|
||||||
import :Font;
|
import :Font;
|
||||||
import :Types;
|
import :Types;
|
||||||
import :Window;
|
import :Window;
|
||||||
|
|
||||||
export namespace Crafter {
|
export namespace Crafter {
|
||||||
enum class TextAlignment {
|
|
||||||
Left,
|
|
||||||
Center,
|
|
||||||
Right
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class TextOverflowMode {
|
|
||||||
Clip,
|
|
||||||
Wrap
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class TextScaleMode {
|
|
||||||
None,
|
|
||||||
Font,
|
|
||||||
Element,
|
|
||||||
Buffer
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RenderElement2DScalingOwning {
|
|
||||||
std::vector<Vector<std::uint8_t, 4>> scalingBuffer;
|
|
||||||
std::uint32_t bufferWidth;
|
|
||||||
std::uint32_t bufferHeight;
|
|
||||||
bool bufferUpdated = true;
|
|
||||||
RenderElement2DScalingOwning() = default;
|
|
||||||
RenderElement2DScalingOwning(std::uint32_t bufferWidth, std::uint32_t bufferHeight) : scalingBuffer(bufferWidth*bufferHeight), bufferWidth(bufferWidth), bufferHeight(bufferHeight) {
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RenderElement2DScalingNonOwning {
|
|
||||||
Vector<std::uint8_t, 4>* scalingBuffer;
|
|
||||||
std::uint32_t bufferWidth;
|
|
||||||
std::uint32_t bufferHeight;
|
|
||||||
bool bufferUpdated = true;
|
|
||||||
RenderElement2DScalingNonOwning() = default;
|
|
||||||
RenderElement2DScalingNonOwning(Vector<std::uint8_t, 4>* scalingBuffer, std::uint32_t bufferWidth, std::uint32_t bufferHeight) : scalingBuffer(scalingBuffer), bufferWidth(bufferWidth), bufferHeight(bufferHeight) {
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RenderElement2DRotating {
|
|
||||||
float rotation;
|
|
||||||
bool rotationUpdated = true;
|
|
||||||
RenderElement2DRotating() = default;
|
|
||||||
RenderElement2DRotating(float rotation) : rotation(rotation) {
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct EmptyScalingBase {};
|
|
||||||
struct EmptyRotatingBase {};
|
|
||||||
|
|
||||||
template<bool Scaling, bool Owning>
|
|
||||||
using ScalingBase =
|
|
||||||
std::conditional_t<
|
|
||||||
Scaling,
|
|
||||||
std::conditional_t<Owning,
|
|
||||||
RenderElement2DScalingOwning,
|
|
||||||
RenderElement2DScalingNonOwning>,
|
|
||||||
EmptyScalingBase
|
|
||||||
>;
|
|
||||||
|
|
||||||
template<bool Rotating>
|
|
||||||
using RotatingBase =
|
|
||||||
std::conditional_t<
|
|
||||||
Rotating,
|
|
||||||
RenderElement2DRotating,
|
|
||||||
EmptyRotatingBase
|
|
||||||
>;
|
|
||||||
|
|
||||||
struct RenderingElement2DBase : Transform2D {
|
|
||||||
std::vector<Vector<std::uint8_t, 4>> buffer;
|
|
||||||
OpaqueType opaque;
|
|
||||||
RenderingElement2DBase(Anchor2D anchor) : Transform2D(anchor) {
|
|
||||||
scaled.size.x = 0;
|
|
||||||
}
|
|
||||||
RenderingElement2DBase(Anchor2D anchor, OpaqueType opaque) : Transform2D(anchor), opaque(opaque) {
|
|
||||||
scaled.size.x = 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<bool Scaling, bool Owning, bool Rotating> requires ((!Rotating || Scaling) && (!Owning || Scaling))
|
template<bool Scaling, bool Owning, bool Rotating> requires ((!Rotating || Scaling) && (!Owning || Scaling))
|
||||||
struct RenderingElement2D : RenderingElement2DBase, ScalingBase<Scaling, Owning>, RotatingBase<Rotating> {
|
struct RenderingElement2D : RenderingElement2DBase, ScalingBase<Scaling, Owning>, RotatingBase<Rotating> {
|
||||||
RenderingElement2D() = default;
|
RenderingElement2D() = default;
|
||||||
|
|
@ -210,7 +129,7 @@ export namespace Crafter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdatePosition(Rendertarget& renderer, ScaleData2D oldScale) {
|
void UpdatePosition(RendertargetBase& renderer, ScaleData2D oldScale) {
|
||||||
if constexpr(Scaling && !Rotating) {
|
if constexpr(Scaling && !Rotating) {
|
||||||
if(oldScale.size.x != scaled.size.x || oldScale.size.y != scaled.size.y) {
|
if(oldScale.size.x != scaled.size.x || oldScale.size.y != scaled.size.y) {
|
||||||
buffer.resize(scaled.size.x * scaled.size.y);
|
buffer.resize(scaled.size.x * scaled.size.y);
|
||||||
|
|
@ -260,7 +179,7 @@ export namespace Crafter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdatePosition(Rendertarget& window) override {
|
void UpdatePosition(RendertargetBase& window) override {
|
||||||
ScaleData2D oldScale = scaled;
|
ScaleData2D oldScale = scaled;
|
||||||
ScaleElement(window.transform);
|
ScaleElement(window.transform);
|
||||||
UpdatePosition(window, oldScale);
|
UpdatePosition(window, oldScale);
|
||||||
|
|
@ -269,7 +188,7 @@ export namespace Crafter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdatePosition(Rendertarget& window, Transform2D& parent) override {
|
void UpdatePosition(RendertargetBase& window, Transform2D& parent) override {
|
||||||
ScaleData2D oldScale = scaled;
|
ScaleData2D oldScale = scaled;
|
||||||
ScaleElement(parent);
|
ScaleElement(parent);
|
||||||
UpdatePosition(window, oldScale);
|
UpdatePosition(window, oldScale);
|
||||||
|
|
|
||||||
108
interfaces/Crafter.Graphics-RenderingElement2DBase.cppm
Normal file
108
interfaces/Crafter.Graphics-RenderingElement2DBase.cppm
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
Crafter®.Graphics
|
||||||
|
Copyright (C) 2026 Catcrafts®
|
||||||
|
catcrafts.net
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License version 3.0 as published by the Free Software Foundation;
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
export module Crafter.Graphics:RenderingElement2DBase;
|
||||||
|
import Crafter.Asset;
|
||||||
|
import Crafter.Math;
|
||||||
|
import std;
|
||||||
|
import :Transform2D;
|
||||||
|
|
||||||
|
export namespace Crafter {
|
||||||
|
enum class TextAlignment {
|
||||||
|
Left,
|
||||||
|
Center,
|
||||||
|
Right
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class TextOverflowMode {
|
||||||
|
Clip,
|
||||||
|
Wrap
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class TextScaleMode {
|
||||||
|
None,
|
||||||
|
Font,
|
||||||
|
Element,
|
||||||
|
Buffer
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RenderElement2DScalingOwning {
|
||||||
|
std::vector<Vector<std::uint8_t, 4>> scalingBuffer;
|
||||||
|
std::uint32_t bufferWidth;
|
||||||
|
std::uint32_t bufferHeight;
|
||||||
|
bool bufferUpdated = true;
|
||||||
|
RenderElement2DScalingOwning() = default;
|
||||||
|
RenderElement2DScalingOwning(std::uint32_t bufferWidth, std::uint32_t bufferHeight) : scalingBuffer(bufferWidth*bufferHeight), bufferWidth(bufferWidth), bufferHeight(bufferHeight) {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RenderElement2DScalingNonOwning {
|
||||||
|
Vector<std::uint8_t, 4>* scalingBuffer;
|
||||||
|
std::uint32_t bufferWidth;
|
||||||
|
std::uint32_t bufferHeight;
|
||||||
|
bool bufferUpdated = true;
|
||||||
|
RenderElement2DScalingNonOwning() = default;
|
||||||
|
RenderElement2DScalingNonOwning(Vector<std::uint8_t, 4>* scalingBuffer, std::uint32_t bufferWidth, std::uint32_t bufferHeight) : scalingBuffer(scalingBuffer), bufferWidth(bufferWidth), bufferHeight(bufferHeight) {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RenderElement2DRotating {
|
||||||
|
float rotation;
|
||||||
|
bool rotationUpdated = true;
|
||||||
|
RenderElement2DRotating() = default;
|
||||||
|
RenderElement2DRotating(float rotation) : rotation(rotation) {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct EmptyScalingBase {};
|
||||||
|
struct EmptyRotatingBase {};
|
||||||
|
|
||||||
|
template<bool Scaling, bool Owning>
|
||||||
|
using ScalingBase =
|
||||||
|
std::conditional_t<
|
||||||
|
Scaling,
|
||||||
|
std::conditional_t<Owning,
|
||||||
|
RenderElement2DScalingOwning,
|
||||||
|
RenderElement2DScalingNonOwning>,
|
||||||
|
EmptyScalingBase
|
||||||
|
>;
|
||||||
|
|
||||||
|
template<bool Rotating>
|
||||||
|
using RotatingBase =
|
||||||
|
std::conditional_t<
|
||||||
|
Rotating,
|
||||||
|
RenderElement2DRotating,
|
||||||
|
EmptyRotatingBase
|
||||||
|
>;
|
||||||
|
|
||||||
|
struct RenderingElement2DBase : Transform2D {
|
||||||
|
std::vector<Vector<std::uint8_t, 4>> buffer;
|
||||||
|
OpaqueType opaque;
|
||||||
|
RenderingElement2DBase(Anchor2D anchor) : Transform2D(anchor) {
|
||||||
|
scaled.size.x = 0;
|
||||||
|
}
|
||||||
|
RenderingElement2DBase(Anchor2D anchor, OpaqueType opaque) : Transform2D(anchor), opaque(opaque) {
|
||||||
|
scaled.size.x = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -18,15 +18,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
export module Crafter.Graphics:Rendertarget;
|
export module Crafter.Graphics:Rendertarget;
|
||||||
import Crafter.Math;
|
import Crafter.Math;
|
||||||
|
import Crafter.Asset;
|
||||||
import std;
|
import std;
|
||||||
import :Types;
|
import :Types;
|
||||||
import :Transform2D;
|
import :Transform2D;
|
||||||
|
import :RenderingElement2DBase;
|
||||||
|
|
||||||
export namespace Crafter {
|
export namespace Crafter {
|
||||||
struct RenderingElement2DBase;
|
struct RendertargetBase {
|
||||||
struct Window;
|
|
||||||
struct Rendertarget {
|
|
||||||
Vector<std::uint8_t, 4>* buffer;
|
|
||||||
#ifdef CRAFTER_TIMING
|
#ifdef CRAFTER_TIMING
|
||||||
std::vector<std::tuple<const Transform*, std::uint32_t, std::uint32_t, std::chrono::nanoseconds>> renderTimings;
|
std::vector<std::tuple<const Transform*, std::uint32_t, std::uint32_t, std::chrono::nanoseconds>> renderTimings;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -35,9 +34,227 @@ export namespace Crafter {
|
||||||
std::int32_t sizeY;
|
std::int32_t sizeY;
|
||||||
std::vector<RenderingElement2DBase*> elements;
|
std::vector<RenderingElement2DBase*> elements;
|
||||||
std::vector<ClipRect> dirtyRects;
|
std::vector<ClipRect> dirtyRects;
|
||||||
Rendertarget(std::int32_t sizeX, std::int32_t sizeY);
|
RendertargetBase() = default;
|
||||||
void RenderElement(RenderingElement2DBase* transform);
|
RendertargetBase(std::int16_t sizeX, std::int16_t sizeY) : sizeX(sizeX), sizeY(sizeY), transform({0, 0, 1, 1, 0, 0, 0}){
|
||||||
void AddDirtyRect(ScaleData2D rect);
|
transform.scaled.size.x = sizeX;
|
||||||
void Render();
|
transform.scaled.size.y = sizeY;
|
||||||
|
transform.scaled.position.x = 0;
|
||||||
|
transform.scaled.position.y = 0;
|
||||||
|
}
|
||||||
|
void AddDirtyRect(ScaleData2D scale) {
|
||||||
|
ClipRect rect {
|
||||||
|
.left = std::max(scale.position.x, std::int32_t(0)),
|
||||||
|
.right = std::min(scale.position.x + scale.size.x, sizeX),
|
||||||
|
.top = std::max(scale.position.y, std::int32_t(0)),
|
||||||
|
.bottom = std::min(scale.position.y + scale.size.y, sizeY),
|
||||||
|
};
|
||||||
|
dirtyRects.push_back(rect);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, std::uint8_t Channels, std::uint8_t Alignment>
|
||||||
|
struct Rendertarget : RendertargetBase {
|
||||||
|
Vector<T, Channels, Alignment>* buffer;
|
||||||
|
Rendertarget() = default;
|
||||||
|
Rendertarget(std::int16_t sizeX, std::int16_t sizeY) : RendertargetBase(sizeX, sizeY) {
|
||||||
|
|
||||||
|
}
|
||||||
|
void RenderElement(RenderingElement2DBase* element) {
|
||||||
|
#ifdef CRAFTER_TIMING
|
||||||
|
auto start = std::chrono::high_resolution_clock::now();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(element->scaled.size.x < 1 || element->scaled.size.y < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(ClipRect dirty : dirtyRects) {
|
||||||
|
dirty.left = std::max(element->scaled.position.x, dirty.left);
|
||||||
|
dirty.top = std::max(element->scaled.position.y, dirty.top);
|
||||||
|
dirty.right = std::min(element->scaled.position.x+element->scaled.size.x, dirty.right);
|
||||||
|
dirty.bottom = std::min(element->scaled.position.y+element->scaled.size.y, dirty.bottom);
|
||||||
|
|
||||||
|
const Vector<std::uint8_t, 4>* src_buffer = element->buffer.data();
|
||||||
|
std::int32_t src_width = element->scaled.size.x;
|
||||||
|
std::int32_t src_height = element->scaled.size.y;
|
||||||
|
|
||||||
|
switch (element->opaque) {
|
||||||
|
case OpaqueType::FullyOpaque:
|
||||||
|
for (std::int32_t y = dirty.top; y < dirty.bottom; y++) {
|
||||||
|
std::int32_t src_y = y - element->scaled.position.y;
|
||||||
|
|
||||||
|
for (std::int32_t x = dirty.left; x < dirty.right; x++) {
|
||||||
|
std::int32_t src_x = x - element->scaled.position.x;
|
||||||
|
|
||||||
|
buffer[y * sizeX + x] = src_buffer[src_y * src_width + src_x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpaqueType::SemiOpaque:
|
||||||
|
// For semi-opaque, we can avoid blending when alpha is 0 or 255
|
||||||
|
for (std::int32_t y = dirty.top; y < dirty.bottom; y++) {
|
||||||
|
std::int32_t src_y = y - element->scaled.position.y;
|
||||||
|
|
||||||
|
for (std::int32_t x = dirty.left; x < dirty.right; x++) {
|
||||||
|
std::int32_t src_x = x - element->scaled.position.x;
|
||||||
|
Vector<std::uint8_t, 4> src_pixel = src_buffer[src_y * src_width + src_x];
|
||||||
|
|
||||||
|
if (src_pixel.a == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
buffer[y * sizeX + x] = src_pixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpaqueType::Transparent:
|
||||||
|
// For transparent, always perform blending
|
||||||
|
for (std::int32_t y = dirty.top; y < dirty.bottom; y++) {
|
||||||
|
std::int32_t src_y = y - element->scaled.position.y;
|
||||||
|
for (std::int32_t x = dirty.left; x < dirty.right; x++) {
|
||||||
|
std::int32_t src_x = x - element->scaled.position.x;
|
||||||
|
Vector<T, Channels, Alignment> src = src_buffer[src_y * src_width + src_x];
|
||||||
|
Vector<T, Channels, Alignment> dst = buffer[y * sizeX + x];
|
||||||
|
|
||||||
|
if(src.a == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float srcA = src.a / 255.0f;
|
||||||
|
float dstA = dst.a / 255.0f;
|
||||||
|
|
||||||
|
float outA = srcA + dstA * (1.0f - srcA);
|
||||||
|
buffer[y * sizeX + x] = Vector<T, Channels, Alignment>(
|
||||||
|
static_cast<T>((src.r * srcA + dst.r * dstA * (1.0f - srcA)) / outA),
|
||||||
|
static_cast<T>((src.g * srcA + dst.g * dstA * (1.0f - srcA)) / outA),
|
||||||
|
static_cast<T>((src.b * srcA + dst.b * dstA * (1.0f - srcA)) / outA),
|
||||||
|
static_cast<T>(outA * 255)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef CRAFTER_TIMING
|
||||||
|
auto end = std::chrono::high_resolution_clock::now();
|
||||||
|
renderTimings.push_back({element, element->scaled.size.x, element->scaled.size.y, end-start});
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
void Render() {
|
||||||
|
//std::vector<ClipRect> newClip;
|
||||||
|
// for (std::uint32_t i = 0; i < dirtyRects.size(); i++) {
|
||||||
|
// ClipRect rect = dirtyRects[i];
|
||||||
|
// for (std::uint32_t i2 = i + 1; i2 < dirtyRects.size(); i2++) {
|
||||||
|
// ClipRect existing = dirtyRects[i2];
|
||||||
|
// if(rect.bottom >= existing.top && rect.top <= existing.top) {
|
||||||
|
// newClip.push_back({
|
||||||
|
// .left = rect.left,
|
||||||
|
// .right = rect.right,
|
||||||
|
// .top = rect.top,
|
||||||
|
// .bottom = existing.top,
|
||||||
|
// });
|
||||||
|
// //-| shape
|
||||||
|
// if(rect.right > existing.right) {
|
||||||
|
// newClip.push_back({
|
||||||
|
// .left = existing.right,
|
||||||
|
// .right = rect.right,
|
||||||
|
// .top = existing.top,
|
||||||
|
// .bottom = existing.bottom,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// //|- shape
|
||||||
|
// if(rect.left < existing.left) {
|
||||||
|
// newClip.push_back({
|
||||||
|
// .left = rect.left,
|
||||||
|
// .right = existing.left,
|
||||||
|
// .top = existing.top,
|
||||||
|
// .bottom = existing.bottom,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// //-| or |- shape where rect extends further down
|
||||||
|
// if(rect.bottom > existing.bottom) {
|
||||||
|
// newClip.push_back({
|
||||||
|
// .left = rect.left,
|
||||||
|
// .right = rect.right,
|
||||||
|
// .top = existing.bottom,
|
||||||
|
// .bottom = rect.bottom,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// goto inner;
|
||||||
|
// }
|
||||||
|
// if (rect.left <= existing.right && rect.right >= existing.left) {
|
||||||
|
// newClip.push_back({
|
||||||
|
// .left = rect.left,
|
||||||
|
// .right = existing.left,
|
||||||
|
// .top = rect.top,
|
||||||
|
// .bottom = rect.bottom,
|
||||||
|
// });
|
||||||
|
// if (rect.right > existing.right) {
|
||||||
|
// newClip.push_back({
|
||||||
|
// .left = existing.right,
|
||||||
|
// .right = rect.right,
|
||||||
|
// .top = rect.top,
|
||||||
|
// .bottom = rect.bottom,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// goto inner;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// newClip.push_back(rect);
|
||||||
|
// inner:;
|
||||||
|
// }
|
||||||
|
|
||||||
|
//dirtyRects = std::move(newClip);
|
||||||
|
|
||||||
|
// std::memset(buffer, 0, width*height*4);
|
||||||
|
|
||||||
|
// std::cout << dirtyRects.size() << std::endl;
|
||||||
|
// // Color palette
|
||||||
|
// static const std::vector<Vector<std::uint8_t, 4>> colors = {
|
||||||
|
// {255, 0, 0, 255}, // red
|
||||||
|
// { 0, 255, 0, 255}, // green
|
||||||
|
// { 0, 0, 255, 255}, // blue
|
||||||
|
// {255, 255, 0, 255}, // yellow
|
||||||
|
// {255, 0, 255, 255}, // magenta
|
||||||
|
// { 0, 255, 255, 255}, // cyan
|
||||||
|
// };
|
||||||
|
|
||||||
|
// std::size_t rectIndex = 0;
|
||||||
|
|
||||||
|
// for (const ClipRect& rect : dirtyRects) {
|
||||||
|
// const Vector<std::uint8_t, 4>& color = colors[rectIndex % colors.size()];
|
||||||
|
|
||||||
|
// std::cout << std::format(
|
||||||
|
// "ClipRect {}: [{}, {}, {}, {}] Color = RGBA({}, {}, {}, {})",
|
||||||
|
// rectIndex,
|
||||||
|
// rect.left, rect.top, rect.right, rect.bottom,
|
||||||
|
// color.r, color.g, color.b, color.a
|
||||||
|
// ) << std::endl;
|
||||||
|
|
||||||
|
// for (std::int32_t y = rect.top; y < rect.bottom; ++y) {
|
||||||
|
// for (std::int32_t x = rect.left; x < rect.right; ++x) {
|
||||||
|
// buffer[y * width + x] = color;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ++rectIndex;
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (!dirtyRects.empty()) {
|
||||||
|
for (ClipRect rect : dirtyRects) {
|
||||||
|
for (std::int32_t y = rect.top; y < rect.bottom; y++) {
|
||||||
|
for (std::int32_t x = rect.left; x < rect.right; x++) {
|
||||||
|
buffer[y * sizeX + x] = {0, 0, 0, 0};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(RenderingElement2DBase* child : elements) {
|
||||||
|
RenderElement(child);
|
||||||
|
}
|
||||||
|
dirtyRects.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -22,7 +22,7 @@ import std;
|
||||||
import :Types;
|
import :Types;
|
||||||
|
|
||||||
export namespace Crafter {
|
export namespace Crafter {
|
||||||
struct Rendertarget;
|
struct RendertargetBase;
|
||||||
struct Anchor2D {
|
struct Anchor2D {
|
||||||
float x;
|
float x;
|
||||||
float y;
|
float y;
|
||||||
|
|
@ -39,13 +39,14 @@ export namespace Crafter {
|
||||||
Anchor2D anchor;
|
Anchor2D anchor;
|
||||||
ScaleData2D scaled;
|
ScaleData2D scaled;
|
||||||
std::vector<Transform2D*> children;
|
std::vector<Transform2D*> children;
|
||||||
|
Transform2D() = default;
|
||||||
Transform2D(Anchor2D anchor);
|
Transform2D(Anchor2D anchor);
|
||||||
Transform2D(Transform2D&) = delete;
|
Transform2D(Transform2D&) = delete;
|
||||||
Transform2D(Transform2D&&) = delete;
|
Transform2D(Transform2D&&) = delete;
|
||||||
Transform2D& operator=(Transform2D&) = delete;
|
Transform2D& operator=(Transform2D&) = delete;
|
||||||
virtual ~Transform2D() = default;
|
virtual ~Transform2D() = default;
|
||||||
void ScaleElement(Transform2D& parent);
|
void ScaleElement(Transform2D& parent);
|
||||||
virtual void UpdatePosition(Rendertarget& window);
|
virtual void UpdatePosition(RendertargetBase& window);
|
||||||
virtual void UpdatePosition(Rendertarget& window, Transform2D& parent);
|
virtual void UpdatePosition(RendertargetBase& window, Transform2D& parent);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -125,7 +125,7 @@ export namespace Crafter {
|
||||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
||||||
float scale;
|
float scale;
|
||||||
#ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE
|
#ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE
|
||||||
Rendertarget renderer;
|
Rendertarget<std::uint8_t, 4, 4> renderer;
|
||||||
#endif
|
#endif
|
||||||
bool configured = false;
|
bool configured = false;
|
||||||
xdg_toplevel* xdgToplevel = nullptr;
|
xdg_toplevel* xdgToplevel = nullptr;
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ export module Crafter.Graphics;
|
||||||
export import :Window;
|
export import :Window;
|
||||||
export import :Transform2D;
|
export import :Transform2D;
|
||||||
export import :RenderingElement2D;
|
export import :RenderingElement2D;
|
||||||
|
export import :RenderingElement2DBase;
|
||||||
export import :MouseElement;
|
export import :MouseElement;
|
||||||
export import :GridElement;
|
export import :GridElement;
|
||||||
export import :Types;
|
export import :Types;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@
|
||||||
"implementations/Crafter.Graphics-MouseElement",
|
"implementations/Crafter.Graphics-MouseElement",
|
||||||
"implementations/Crafter.Graphics-Transform2D",
|
"implementations/Crafter.Graphics-Transform2D",
|
||||||
"implementations/Crafter.Graphics-GridElement",
|
"implementations/Crafter.Graphics-GridElement",
|
||||||
"implementations/Crafter.Graphics-Rendertarget",
|
|
||||||
"implementations/Crafter.Graphics-Device",
|
"implementations/Crafter.Graphics-Device",
|
||||||
"implementations/Crafter.Graphics-Mesh",
|
"implementations/Crafter.Graphics-Mesh",
|
||||||
"implementations/Crafter.Graphics-RenderingElement3D"
|
"implementations/Crafter.Graphics-RenderingElement3D"
|
||||||
|
|
@ -21,6 +20,7 @@
|
||||||
"interfaces/Crafter.Graphics-Font",
|
"interfaces/Crafter.Graphics-Font",
|
||||||
"interfaces/Crafter.Graphics-Animation",
|
"interfaces/Crafter.Graphics-Animation",
|
||||||
"interfaces/Crafter.Graphics-RenderingElement2D",
|
"interfaces/Crafter.Graphics-RenderingElement2D",
|
||||||
|
"interfaces/Crafter.Graphics-RenderingElement2DBase",
|
||||||
"interfaces/Crafter.Graphics-MouseElement",
|
"interfaces/Crafter.Graphics-MouseElement",
|
||||||
"interfaces/Crafter.Graphics-Transform2D",
|
"interfaces/Crafter.Graphics-Transform2D",
|
||||||
"interfaces/Crafter.Graphics-GridElement",
|
"interfaces/Crafter.Graphics-GridElement",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue