From 2b22c16ce7d91c5111c6d68e93585aa8212f930c Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Thu, 12 Mar 2026 21:13:53 +0100 Subject: [PATCH] rendertarget multi frame rewrite --- .../Crafter.Graphics-GridElement.cpp | 63 ------ .../Crafter.Graphics-Transform2D.cpp | 30 --- implementations/Crafter.Graphics-Window.cpp | 20 +- .../Crafter.Graphics-ForwardDeclarations.cppm | 29 +++ interfaces/Crafter.Graphics-GridElement.cppm | 45 +++- interfaces/Crafter.Graphics-MouseElement.cppm | 3 +- .../Crafter.Graphics-RenderingElement2D.cppm | 199 +++++++----------- ...after.Graphics-RenderingElement2DBase.cppm | 13 +- interfaces/Crafter.Graphics-Rendertarget.cppm | 48 +++-- interfaces/Crafter.Graphics-Transform2D.cppm | 42 +++- interfaces/Crafter.Graphics-Window.cppm | 4 +- interfaces/Crafter.Graphics.cppm | 1 + project.json | 4 +- 13 files changed, 225 insertions(+), 276 deletions(-) delete mode 100644 implementations/Crafter.Graphics-GridElement.cpp create mode 100644 interfaces/Crafter.Graphics-ForwardDeclarations.cppm diff --git a/implementations/Crafter.Graphics-GridElement.cpp b/implementations/Crafter.Graphics-GridElement.cpp deleted file mode 100644 index 8b691f8..0000000 --- a/implementations/Crafter.Graphics-GridElement.cpp +++ /dev/null @@ -1,63 +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:GridElement_impl; -import :GridElement; -import :Rendertarget; -import :Types; -import std; - -using namespace Crafter; - -GridElement::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) : Transform2D(anchor), columns(columns), rows(rows), spacingX(spacingX), spacingY(spacingY), paddingX(paddingX), paddingY(paddingY) { - -} - -void GridElement::UpdatePositionScaled(RendertargetBase& window) { - std::int32_t cellWidth = (paddingX * 2) - (spacingX * (columns - 1)) / columns; - std::int32_t cellHeight = (paddingY * 2) - (spacingY * (rows - 1)) / rows; - - std::size_t childIndex = 0; - for (std::uint32_t row = 0; row < rows && childIndex < children.size(); ++row) { - for (std::uint32_t col = 0; col < columns && childIndex < children.size(); ++col) { - Transform2D* child = children[childIndex]; - - // Calculate position for this child - std::int32_t childX = (cellWidth * col) + (spacingX * col) + paddingX; - - std::int32_t childY = (cellHeight * row) + (spacingY * row) + paddingY; - - // Apply relative positioning - child->anchor.x = childX; - child->anchor.y = childY; - child->anchor.width = cellWidth; - child->anchor.height = cellHeight; - - // Update child position - child->UpdatePosition(window, *this); - childIndex++; - } - } -} - -void GridElement::UpdatePosition(RendertargetBase& window, Transform2D& parent) { - ScaleElement(parent); - UpdatePositionScaled(window); -} \ No newline at end of file diff --git a/implementations/Crafter.Graphics-Transform2D.cpp b/implementations/Crafter.Graphics-Transform2D.cpp index f0f8b51..6381950 100644 --- a/implementations/Crafter.Graphics-Transform2D.cpp +++ b/implementations/Crafter.Graphics-Transform2D.cpp @@ -30,34 +30,4 @@ using namespace Crafter; Anchor2D::Anchor2D(float x, float y, float width, float height, float offsetX, float offsetY, std::int32_t z, bool maintainAspectRatio): x(x), y(y), width(width), height(height), offsetX(offsetX), offsetY(offsetY), z(z), maintainAspectRatio(maintainAspectRatio) { -} - -Transform2D::Transform2D(Anchor2D anchor) : anchor(anchor) { - -} - -void Transform2D::UpdatePosition(RendertargetBase& window, Transform2D& parent) { - ScaleElement(parent); - for(Transform2D* child : children) { - child->UpdatePosition(window, *this); - } -} - -void Transform2D::ScaleElement(Transform2D& parent) { - if(anchor.maintainAspectRatio) { - if(parent.scaled.size.x > parent.scaled.size.y) { - scaled.size.x = anchor.width * parent.scaled.size.y; - scaled.size.y = anchor.height * parent.scaled.size.y; - } else { - scaled.size.x = anchor.width * parent.scaled.size.x; - scaled.size.y = anchor.height * parent.scaled.size.x; - } - } else { - - scaled.size.x = anchor.width * parent.scaled.size.x; - scaled.size.y = anchor.height * parent.scaled.size.y; - } - - scaled.position.x = parent.scaled.position.x + (anchor.x * parent.scaled.size.x - anchor.offsetX * scaled.size.x); - scaled.position.y = parent.scaled.position.y + (anchor.y * parent.scaled.size.y - anchor.offsetY * scaled.size.y); } \ No newline at end of file diff --git a/implementations/Crafter.Graphics-Window.cpp b/implementations/Crafter.Graphics-Window.cpp index 3ddf19e..7b399bd 100644 --- a/implementations/Crafter.Graphics-Window.cpp +++ b/implementations/Crafter.Graphics-Window.cpp @@ -420,8 +420,8 @@ Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height } // Map the shared memory file - renderer.buffer = reinterpret_cast*>(mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); - if (renderer.buffer == MAP_FAILED) { + renderer.buffer[0] = reinterpret_cast*>(mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); + if (renderer.buffer[0] == MAP_FAILED) { throw std::runtime_error("mmap failed"); } @@ -562,13 +562,13 @@ void Window::SetTitle(const std::string_view title) { } void Window::SetCusorImage(std::uint16_t sizeX, std::uint16_t sizeY) { - new (&cursorRenderer) Rendertarget(sizeX, sizeY); + new (&cursorRenderer) Rendertarget(sizeX, sizeY); #ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND if(cursorSurface == nullptr) { cursorSurface = wl_compositor_create_surface(Device::compositor); } else { wl_buffer_destroy(cursorWlBuffer); - munmap(cursorRenderer.buffer, cursorBufferOldSize); + munmap(cursorRenderer.buffer[0], cursorBufferOldSize); } int stride = sizeX * 4; @@ -581,8 +581,8 @@ void Window::SetCusorImage(std::uint16_t sizeX, std::uint16_t sizeY) { throw std::runtime_error(std::format("creating a buffer file for {}B failed", size)); } - cursorRenderer.buffer = reinterpret_cast*>(mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); - if (cursorRenderer.buffer == MAP_FAILED) { + cursorRenderer.buffer[0] = reinterpret_cast*>(mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); + if (cursorRenderer.buffer[0] == MAP_FAILED) { throw std::runtime_error("mmap failed"); } @@ -607,9 +607,9 @@ void Window::SetCusorImageDefault() { } void Window::UpdateCursorImage() { - cursorRenderer.Render(); + cursorRenderer.Render(0); for(std::uint32_t i = 0; i < cursorBufferOldSize / 4; i++) { - std::swap(cursorRenderer.buffer[i].b, cursorRenderer.buffer[i].r); + std::swap(cursorRenderer.buffer[0][i].b, cursorRenderer.buffer[0][i].r); } wl_surface_attach(cursorSurface, cursorWlBuffer, 0, 0); wl_surface_damage(cursorSurface, 0, 0, 9999999, 99999999); @@ -686,9 +686,7 @@ void Window::Update() { void Window::Render() { #ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE - // elements.erase(std::remove(elements.begin(), elements.end(), static_cast(nullptr)), elements.end()); - // std::sort(elements.begin(), elements.end(), [](Transform* a, Transform* b){ return a->anchor.z < b->anchor.z; }); - renderer.Render(); + renderer.Render(0); #ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND wl_surface_attach(surface, buffer, 0, 0); wl_surface_commit(surface); diff --git a/interfaces/Crafter.Graphics-ForwardDeclarations.cppm b/interfaces/Crafter.Graphics-ForwardDeclarations.cppm new file mode 100644 index 0000000..9a5adca --- /dev/null +++ b/interfaces/Crafter.Graphics-ForwardDeclarations.cppm @@ -0,0 +1,29 @@ +/* +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:ForwardDeclarations; +import std; + +export namespace Crafter { + template + struct RendertargetBase; + + struct GridElement; + struct Window; +} \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-GridElement.cppm b/interfaces/Crafter.Graphics-GridElement.cppm index c18d125..7465b5e 100644 --- a/interfaces/Crafter.Graphics-GridElement.cppm +++ b/interfaces/Crafter.Graphics-GridElement.cppm @@ -20,19 +20,52 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA export module Crafter.Graphics:GridElement; import std; import :Transform2D; -import :Types; +import :ForwardDeclarations; export namespace Crafter { - class GridElement : public Transform2D { - public: + struct GridElement : Transform2D { std::uint32_t columns; std::uint32_t rows; std::int32_t spacingX; std::int32_t spacingY; std::int32_t paddingX; 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); - void UpdatePositionScaled(RendertargetBase& window); - void UpdatePosition(RendertargetBase& window, Transform2D& parent) override; + 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) : Transform2D(anchor), columns(columns), rows(rows), spacingX(spacingX), spacingY(spacingY), paddingX(paddingX), paddingY(paddingY) { + + } + template + void UpdatePositionImpl(RendertargetBase& window, Transform2D& parent) { + ScaleElement(parent); + std::int32_t cellWidth = (paddingX * 2) - (spacingX * (columns - 1)) / columns; + std::int32_t cellHeight = (paddingY * 2) - (spacingY * (rows - 1)) / rows; + + std::size_t childIndex = 0; + for (std::uint32_t row = 0; row < rows && childIndex < this->children.size(); ++row) { + for (std::uint32_t col = 0; col < columns && childIndex < this->children.size(); ++col) { + Transform2D* child = this->children[childIndex]; + + // Calculate position for this child + std::int32_t childX = (cellWidth * col) + (spacingX * col) + paddingX; + + std::int32_t childY = (cellHeight * row) + (spacingY * row) + paddingY; + + // Apply relative positioning + child->anchor.x = childX; + child->anchor.y = childY; + child->anchor.width = cellWidth; + child->anchor.height = cellHeight; + + // Update child position + child->UpdatePosition(window, *this); + childIndex++; + } + } + } + void UpdatePosition(RendertargetBase<1>& window, Transform2D& parent) override { + UpdatePositionImpl<1>(window, parent); + } + void UpdatePosition(RendertargetBase<3>& window, Transform2D& parent) override { + UpdatePositionImpl<3>(window, parent); + } }; } \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-MouseElement.cppm b/interfaces/Crafter.Graphics-MouseElement.cppm index 07406d0..0297e86 100644 --- a/interfaces/Crafter.Graphics-MouseElement.cppm +++ b/interfaces/Crafter.Graphics-MouseElement.cppm @@ -20,11 +20,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA export module Crafter.Graphics:MouseElement; import std; import Crafter.Event; -import :Types; import :Transform2D; +import :ForwardDeclarations; export namespace Crafter { - class Window; struct MouseElement : Transform2D { Event onMouseMove; Event onMouseEnter; diff --git a/interfaces/Crafter.Graphics-RenderingElement2D.cppm b/interfaces/Crafter.Graphics-RenderingElement2D.cppm index 98a6c77..233242f 100644 --- a/interfaces/Crafter.Graphics-RenderingElement2D.cppm +++ b/interfaces/Crafter.Graphics-RenderingElement2D.cppm @@ -29,31 +29,31 @@ import :Types; import :Window; export namespace Crafter { - template requires ((!Rotating || Scaling) && (!Owning || Scaling)) - struct RenderingElement2D : RenderingElement2DBase, ScalingBase, RotatingBase { + template requires ((!Rotating || Scaling) && (!Owning || Scaling)) + struct RenderingElement2D : RenderingElement2DBase, ScalingBase, RotatingBase { RenderingElement2D() = default; - RenderingElement2D(Anchor2D anchor, OpaqueType opaque) : RenderingElement2DBase(anchor, opaque) { + RenderingElement2D(Anchor2D anchor, OpaqueType opaque) : RenderingElement2DBase(anchor, opaque) { } - RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t rotation) requires(Rotating) : RenderingElement2DBase(anchor, opaque), RotatingBase(rotation) { + RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t rotation) requires(Rotating) : RenderingElement2DBase(anchor, opaque), RotatingBase(rotation) { } - RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector* scalingBuffer) requires(Scaling && !Owning) : RenderingElement2DBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight, scalingBuffer) { + RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector* scalingBuffer) requires(Scaling && !Owning) : RenderingElement2DBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight, scalingBuffer) { } - RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector* scalingBuffer, std::uint32_t rotation) requires(Scaling && !Owning && Rotating) : RenderingElement2DBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight, scalingBuffer), RotatingBase(rotation) { + RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector* scalingBuffer, std::uint32_t rotation) requires(Scaling && !Owning && Rotating) : RenderingElement2DBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight, scalingBuffer), RotatingBase(rotation) { } - RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight) requires(Owning) : RenderingElement2DBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight) { + RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight) requires(Owning) : RenderingElement2DBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight) { } - RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, std::uint32_t rotation) requires(Owning && Rotating) : RenderingElement2DBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight) , RotatingBase(rotation) { + RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, std::uint32_t rotation) requires(Owning && Rotating) : RenderingElement2DBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight) , RotatingBase(rotation) { } - RenderingElement2D(Anchor2D anchor, TextureAsset>& texture) requires(!Owning && Scaling) : RenderingElement2DBase(anchor, texture.opaque), ScalingBase(texture.pixels.data(), texture.sizeX, texture.sizeY) { + RenderingElement2D(Anchor2D anchor, TextureAsset>& texture) requires(!Owning && Scaling) : RenderingElement2DBase(anchor, texture.opaque), ScalingBase(texture.pixels.data(), texture.sizeX, texture.sizeY) { } - RenderingElement2D(Anchor2D anchor, TextureAsset>& texture, std::uint32_t rotation) requires(!Owning && Scaling && Rotating) : RenderingElement2DBase(anchor, texture.opaque), ScalingBase(texture.pixels.data(), texture.sizeX, texture.sizeY), RotatingBase(rotation) { + RenderingElement2D(Anchor2D anchor, TextureAsset>& texture, std::uint32_t rotation) requires(!Owning && Scaling && Rotating) : RenderingElement2DBase(anchor, texture.opaque), ScalingBase(texture.pixels.data(), texture.sizeX, texture.sizeY), RotatingBase(rotation) { } @@ -61,18 +61,18 @@ export namespace Crafter { RenderingElement2D& operator=(RenderingElement2D&) = delete; void ScaleNearestNeighbor() requires(Scaling) { - for (std::uint32_t y = 0; y < scaled.size.y; y++) { - std::uint32_t srcY = y * ScalingBase::bufferHeight / scaled.size.y; - for (std::uint32_t x = 0; x < scaled.size.x; x++) { - std::uint32_t srcX = x * ScalingBase::bufferWidth / scaled.size.x; - buffer[y * scaled.size.x + x] = ScalingBase::scalingBuffer[srcY * ScalingBase::bufferWidth + srcX]; + for (std::uint32_t y = 0; y < this->scaled.size.y; y++) { + std::uint32_t srcY = y * ScalingBase::bufferHeight / this->scaled.size.y; + for (std::uint32_t x = 0; x < this->scaled.size.x; x++) { + std::uint32_t srcX = x * ScalingBase::bufferWidth / this->scaled.size.x; + this->buffer[y * this->scaled.size.x + x] = ScalingBase::scalingBuffer[srcY * ScalingBase::bufferWidth + srcX]; } } } void ScaleRotating() requires(Scaling) { - const std::uint32_t dstWidth = scaled.size.x; - const std::uint32_t dstHeight = scaled.size.y; + const float dstWidth = this->scaled.size.x; + const float dstHeight = this->scaled.size.y; const float c2 = std::abs(std::cos(RotatingBase::rotation)); const float s2 = std::abs(std::sin(RotatingBase::rotation)); @@ -80,25 +80,25 @@ export namespace Crafter { const float rotatedWidth = dstWidth * c2 + dstHeight * s2; const float rotatedHeight = dstWidth * s2 + dstHeight * c2; - const std::uint32_t diffX = static_cast(std::ceil((rotatedWidth - dstWidth) * 0.5)); - const std::uint32_t diffY = static_cast(std::ceil((rotatedHeight - dstHeight) * 0.5)); + const float diffX = std::ceil((rotatedWidth - dstWidth) * 0.5); + const float diffY = std::ceil((rotatedHeight - dstHeight) * 0.5); - scaled.size.x += diffX + diffX; - scaled.size.y += diffY + diffY; + this->scaled.size.x += diffX + diffX; + this->scaled.size.y += diffY + diffY; - scaled.position.x -= diffX; - scaled.position.y -= diffY; + this->scaled.position.x -= diffX; + this->scaled.position.y -= diffY; - buffer.clear(); - buffer.resize(scaled.size.x * scaled.size.y); + this->buffer.clear(); + this->buffer.resize(this->scaled.size.x * this->scaled.size.y); // Destination center - const float dstCx = (static_cast(scaled.size.x) - 1.0) * 0.5; - const float dstCy = (static_cast(scaled.size.y) - 1.0) * 0.5; + const float dstCx = (this->scaled.size.x - 1.0) * 0.5; + const float dstCy = (this->scaled.size.y - 1.0) * 0.5; // Source center - const float srcCx = (static_cast(ScalingBase::bufferWidth) - 1.0) * 0.5; - const float srcCy = (static_cast(ScalingBase::bufferHeight) - 1.0) * 0.5; + const float srcCx = (ScalingBase::bufferWidth - 1.0) * 0.5; + const float srcCy = (ScalingBase::bufferHeight - 1.0) * 0.5; const float c = std::cos(RotatingBase::rotation); const float s = std::sin(RotatingBase::rotation); @@ -107,8 +107,8 @@ export namespace Crafter { const float scaleX = static_cast(ScalingBase::bufferWidth) / dstWidth; const float scaleY = static_cast(ScalingBase::bufferHeight) / dstHeight; - for (std::uint32_t yB = 0; yB < scaled.size.y; ++yB) { - for (std::uint32_t xB = 0; xB < scaled.size.x; ++xB) { + for (std::uint32_t yB = 0; yB < this->scaled.size.y; ++yB) { + for (std::uint32_t xB = 0; xB < this->scaled.size.x; ++xB) { // ---- Destination pixel relative to center ---- const float dx = (static_cast(xB) - dstCx) * scaleX; @@ -123,74 +123,37 @@ export namespace Crafter { const std::int32_t srcY = static_cast(std::round(sy)); if (srcX >= 0 && srcX < ScalingBase::bufferWidth && srcY >= 0 && srcY < ScalingBase::bufferHeight) { - buffer[yB * scaled.size.x + xB] = ScalingBase::scalingBuffer[srcY * ScalingBase::bufferWidth + srcX]; + this->buffer[yB * this->scaled.size.x + xB] = ScalingBase::scalingBuffer[srcY * ScalingBase::bufferWidth + srcX]; } } } } - void UpdatePosition(RendertargetBase& renderer, ScaleData2D oldScale) { + + void UpdatePosition(RendertargetBase& window, Transform2D& parent) override { + ScaleData2D oldScale = this->scaled; + this->ScaleElement(parent); if constexpr(Scaling && !Rotating) { - if(oldScale.size.x != scaled.size.x || oldScale.size.y != scaled.size.y) { - buffer.resize(scaled.size.x * scaled.size.y); + if(oldScale.size.x != this->scaled.size.x || oldScale.size.y != this->scaled.size.y) { + this->buffer.resize(this->scaled.size.x * this->scaled.size.y); ScaleNearestNeighbor(); - renderer.AddDirtyRect(oldScale); - renderer.AddDirtyRect(scaled); - } else if(oldScale.position.x != scaled.position.x || oldScale.position.y != scaled.position.y) { - renderer.AddDirtyRect(oldScale); - renderer.AddDirtyRect(scaled); - if(ScalingBase::bufferUpdated) { - ScaleNearestNeighbor(); - ScalingBase::bufferUpdated = false; - } - } else if(ScalingBase::bufferUpdated) { - ScaleNearestNeighbor(); - ScalingBase::bufferUpdated = false; - renderer.AddDirtyRect(scaled); } } else if constexpr(Rotating) { - if(oldScale.size.x != scaled.size.x || oldScale.size.y != scaled.size.y) { - buffer.resize(scaled.size.x * scaled.size.y); + if(oldScale.size.x != this->scaled.size.x || oldScale.size.y != this->scaled.size.y) { + this->buffer.resize(this->scaled.size.x * this->scaled.size.y); ScaleRotating(); - renderer.AddDirtyRect(oldScale); - renderer.AddDirtyRect(scaled); - } else if(oldScale.position.x != scaled.position.x || oldScale.position.y != scaled.position.y) { - renderer.AddDirtyRect(oldScale); - renderer.AddDirtyRect(scaled); - if(ScalingBase::bufferUpdated || RotatingBase::rotationUpdated) { - ScaleRotating(); - ScalingBase::bufferUpdated = false; - RotatingBase::rotationUpdated = false; - } - } else if(ScalingBase::bufferUpdated || RotatingBase::rotationUpdated) { - ScaleRotating(); - ScalingBase::bufferUpdated = false; - RotatingBase::rotationUpdated = false; - renderer.AddDirtyRect(scaled); } } else { - if(oldScale.size.x != scaled.size.x || oldScale.size.y != scaled.size.y) { - buffer.resize(scaled.size.x * scaled.size.y); - renderer.AddDirtyRect(oldScale); - renderer.AddDirtyRect(scaled); - } - if(oldScale.position.x != scaled.position.x || oldScale.position.y != scaled.position.y) { - renderer.AddDirtyRect(oldScale); - renderer.AddDirtyRect(scaled); + if(oldScale.size.x != this->scaled.size.x || oldScale.size.y != this->scaled.size.y) { + this->buffer.resize(this->scaled.size.x * this->scaled.size.y); } } - } - - void UpdatePosition(RendertargetBase& window, Transform2D& parent) override { - ScaleData2D oldScale = scaled; - ScaleElement(parent); - UpdatePosition(window, oldScale); - for(Transform2D* child : children) { + for(Transform2D* child : this->children) { child->UpdatePosition(window, *this); } } - std::vector ResizeText(RendertargetBase& window, Transform2D& parent, const std::string_view text, float& size, Font& font, TextOverflowMode overflowMode = TextOverflowMode::Clip, TextScaleMode scaleMode = TextScaleMode::None) { + std::vector ResizeText(RendertargetBase& window, Transform2D& parent, const std::string_view text, float& size, Font& font, std::uint8_t frame, TextOverflowMode overflowMode = TextOverflowMode::Clip, TextScaleMode scaleMode = TextScaleMode::None) { float scale = stbtt_ScaleForPixelHeight(&font.font, size); int baseline = (int)(font.ascent * scale); @@ -225,40 +188,29 @@ export namespace Crafter { } } - if(scaleMode == TextScaleMode::Element) { - std::int32_t logicalPerPixelY = anchor.height / scaled.size.y; - std::int32_t oldHeight = anchor.height; - std::int32_t logicalPerPixelX = anchor.width / scaled.size.x; - std::int32_t oldwidth = anchor.width; - anchor.height = lineHeight * logicalPerPixelY; - anchor.width = maxWidth * logicalPerPixelX; - if(oldHeight != anchor.height || oldwidth != anchor.width) { + std::int32_t logicalPerPixelY = this->anchor.height / this->scaled.size.y; + std::int32_t oldHeight = this->anchor.height; + std::int32_t logicalPerPixelX = this->anchor.width / this->scaled.size.x; + std::int32_t oldwidth = this->anchor.width; + this->anchor.height = lineHeight * logicalPerPixelY; + this->anchor.width = maxWidth * logicalPerPixelX; + if(oldHeight != this->anchor.height || oldwidth != this->anchor.width) { UpdatePosition(window, parent); } } else if(scaleMode == TextScaleMode::Font) { float lineHeightPerFont = lineHeight / size; float lineWidthPerFont = maxWidth / size; - float maxFontHeight = scaled.size.y / lineHeightPerFont; - float maxFontWidth = scaled.size.x / lineWidthPerFont; + float maxFontHeight = this->scaled.size.y / lineHeightPerFont; + float maxFontWidth = this->scaled.size.x / lineWidthPerFont; size = std::min(maxFontHeight, maxFontWidth); - } else if(scaleMode == TextScaleMode::Buffer) { - if constexpr(Scaling && Owning) { - std::uint32_t neededHeight = lines.size() * lineHeight; - if(neededHeight != ScalingBase::bufferHeight || maxWidth != ScalingBase::bufferWidth) { - ScalingBase::bufferHeight = neededHeight; - ScalingBase::bufferWidth = maxWidth; - ScalingBase::bufferUpdated = true; - ScalingBase::scalingBuffer.resize(neededHeight*maxWidth); - } - } } else { if constexpr(Scaling) { lines.resize(ScalingBase::bufferHeight / lines.size()); } else { - lines.resize(scaled.size.y / lines.size()); + lines.resize(this->scaled.size.y / lines.size()); } } } else { @@ -291,7 +243,7 @@ export namespace Crafter { } // if line exceeds width, wrap - if (lineWidth > scaled.size.x) { + if (lineWidth > this->scaled.size.x) { std::size_t wrapPos; if (lastWrapPos > startPos) { wrapPos = lastWrapPos; // wrap at last space @@ -321,36 +273,27 @@ export namespace Crafter { } if(scaleMode == TextScaleMode::Element) { - float logicalPerPixelY = anchor.height / scaled.size.y; - float oldHeight = anchor.height; - anchor.height = lineHeight * logicalPerPixelY; - if(oldHeight != anchor.height) { + float logicalPerPixelY = this->anchor.height / this->scaled.size.y; + float oldHeight = this->anchor.height; + this->anchor.height = lineHeight * logicalPerPixelY; + if(oldHeight != this->anchor.height) { UpdatePosition(window, parent); } } else if(scaleMode == TextScaleMode::Font) { float lineHeightPerFont = lineHeight / size; - size = scaled.size.y / lineHeightPerFont; - } else if(scaleMode == TextScaleMode::Buffer) { - if constexpr(Scaling && Owning) { - float neededHeight = lines.size() * lineHeight; - if(neededHeight != ScalingBase::bufferHeight) { - ScalingBase::bufferHeight = neededHeight; - ScalingBase::bufferUpdated = true; - ScalingBase::scalingBuffer.resize(neededHeight*ScalingBase::bufferWidth); - } - } + size = this->scaled.size.y / lineHeightPerFont; } else { if constexpr(Scaling) { lines.resize(ScalingBase::bufferHeight / lines.size()); } else { - lines.resize(scaled.size.y / lines.size()); + lines.resize(this->scaled.size.y / lines.size()); } } } return lines; } - void RenderText(std::span lines, float size, Vector color, Font& font, TextAlignment alignment = TextAlignment::Left, std::uint32_t offsetX = 0, std::uint32_t offsetY = 0, OpaqueType opaque = OpaqueType::FullyOpaque) { + void RenderText(std::span lines, float size, Vector color, Font& font, std::uint8_t frame, 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); int baseline = (int)(font.ascent * scale); std::uint32_t lineHeight = (font.ascent - font.descent) * scale; @@ -370,10 +313,10 @@ export namespace Crafter { x = 0; break; case TextAlignment::Center: - x = (scaled.size.x - lineWidth) / 2; + x = (this->scaled.size.x - lineWidth) / 2; break; case TextAlignment::Right: - x = scaled.size.x - lineWidth; + x = this->scaled.size.x - lineWidth; break; } @@ -407,8 +350,8 @@ export namespace Crafter { ScalingBase::scalingBuffer[bufferY * ScalingBase::bufferWidth + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]}; } } else { - if (bufferX >= 0 && bufferX < (int)scaled.size.x && bufferY >= 0 && bufferY < (int)scaled.size.y) { - buffer[bufferY * scaled.size.x + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]}; + 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]}; } } } @@ -427,10 +370,10 @@ export namespace Crafter { ScalingBase::scalingBuffer[bufferY * ScalingBase::bufferWidth + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]}; } } else { - if (bufferX >= 0 && bufferX < (int)scaled.size.x && bufferY >= 0 && bufferY < (int)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(alpha != 0) { - buffer[bufferY * 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, bitmap[j * w + i]}; } } } @@ -450,20 +393,20 @@ export namespace Crafter { ScalingBase::scalingBuffer[bufferY * ScalingBase::bufferWidth + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]}; } } else { - if (bufferX >= 0 && bufferX < (int)scaled.size.x && bufferY >= 0 && bufferY < (int)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(alpha == 0) { continue; } - Vector dst = buffer[bufferY * scaled.size.x + bufferX]; + Vector dst = this->buffer[bufferY * this->scaled.size.x + bufferX]; float srcA = (alpha / 255.0f) * (color.a / 255.0f); float dstA = dst.a / 255.0f; float outA = srcA + dstA * (1.0f - srcA); - buffer[bufferY * scaled.size.x + bufferX] = Vector( + this->buffer[bufferY * this->scaled.size.x + bufferX] = Vector( static_cast((color.r * srcA + dst.r * dstA * (1.0f - srcA)) / outA), static_cast((color.g * srcA + dst.g * dstA * (1.0f - srcA)) / outA), static_cast((color.b * srcA + dst.b * dstA * (1.0f - srcA)) / outA), diff --git a/interfaces/Crafter.Graphics-RenderingElement2DBase.cppm b/interfaces/Crafter.Graphics-RenderingElement2DBase.cppm index 3bbd2e3..e42af39 100644 --- a/interfaces/Crafter.Graphics-RenderingElement2DBase.cppm +++ b/interfaces/Crafter.Graphics-RenderingElement2DBase.cppm @@ -47,7 +47,6 @@ export namespace Crafter { std::vector> 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) { @@ -59,7 +58,6 @@ export namespace Crafter { Vector* scalingBuffer; std::uint32_t bufferWidth; std::uint32_t bufferHeight; - bool bufferUpdated = true; RenderElement2DScalingNonOwning() = default; RenderElement2DScalingNonOwning(Vector* scalingBuffer, std::uint32_t bufferWidth, std::uint32_t bufferHeight) : scalingBuffer(scalingBuffer), bufferWidth(bufferWidth), bufferHeight(bufferHeight) { @@ -68,7 +66,6 @@ export namespace Crafter { struct RenderElement2DRotating { float rotation; - bool rotationUpdated = true; RenderElement2DRotating() = default; RenderElement2DRotating(float rotation) : rotation(rotation) { @@ -97,14 +94,20 @@ export namespace Crafter { EmptyRotatingBase >; + template struct RenderingElement2DBase : Transform2D { + ScaleData2D oldScale[Frames]; std::vector> buffer; OpaqueType opaque; RenderingElement2DBase(Anchor2D anchor) : Transform2D(anchor) { - scaled.size.x = 0; + for(std::uint8_t i = 0; i < Frames; i++) { + this->scaled[i].size.x = 0; + } } RenderingElement2DBase(Anchor2D anchor, OpaqueType opaque) : Transform2D(anchor), opaque(opaque) { - scaled.size.x = 0; + for(std::uint8_t i = 0; i < Frames; i++) { + this->scaled[i].size.x = 0; + } } }; } \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-Rendertarget.cppm b/interfaces/Crafter.Graphics-Rendertarget.cppm index b6ac98d..a3ec6ae 100644 --- a/interfaces/Crafter.Graphics-Rendertarget.cppm +++ b/interfaces/Crafter.Graphics-Rendertarget.cppm @@ -25,6 +25,7 @@ import :Transform2D; import :RenderingElement2DBase; export namespace Crafter { + template struct RendertargetBase { #ifdef CRAFTER_TIMING std::vector> renderTimings; @@ -33,7 +34,7 @@ export namespace Crafter { std::int32_t sizeX; std::int32_t sizeY; std::vector elements; - std::vector dirtyRects; + std::vector dirtyRects[Frames]; RendertargetBase() = default; RendertargetBase(std::int16_t sizeX, std::int16_t sizeY) : sizeX(sizeX), sizeY(sizeY), transform({0, 0, 1, 1, 0, 0, 0}){ transform.scaled.size.x = sizeX; @@ -42,19 +43,21 @@ export namespace Crafter { transform.scaled.position.y = 0; } void AddDirtyRect(ScaleData2D scale) { - dirtyRects.emplace_back(std::max(scale.position.x, std::int32_t(0)), std::min(scale.position.x + scale.size.x, sizeX), std::max(scale.position.y, std::int32_t(0)), std::min(scale.position.y + scale.size.y, sizeY)); + for(std::uint8_t i = 0; i < Frames; i++) { + dirtyRects[i].emplace_back(std::max(scale.position.x, std::int32_t(0)), std::min(scale.position.x + scale.size.x, sizeX), std::max(scale.position.y, std::int32_t(0)), std::min(scale.position.y + scale.size.y, sizeY)); + } } }; - template - struct Rendertarget : RendertargetBase { - Vector* buffer; + template + struct Rendertarget : RendertargetBase { + Vector* buffer[Frames]; Rendertarget() = default; - Rendertarget(std::int16_t sizeX, std::int16_t sizeY) : RendertargetBase(sizeX, sizeY) { + Rendertarget(std::int16_t sizeX, std::int16_t sizeY) : RendertargetBase(sizeX, sizeY) { } - void RenderElement(Transform2D* elementTransform) { - RenderingElement2DBase* element = dynamic_cast(elementTransform); + void RenderElement(Transform2D* elementTransform, std::uint8_t frame) { + RenderingElement2DBase* element = dynamic_cast*>(elementTransform); if(element) { #ifdef CRAFTER_TIMING auto start = std::chrono::high_resolution_clock::now(); @@ -64,7 +67,7 @@ export namespace Crafter { return; } - for(ClipRect dirty : dirtyRects) { + for(ClipRect dirty : this->dirtyRects[frame]) { 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); @@ -82,7 +85,7 @@ export namespace Crafter { 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]; + this->buffer[frame][y * this->sizeX + x] = src_buffer[src_y * src_width + src_x]; } } break; @@ -99,7 +102,7 @@ export namespace Crafter { if (src_pixel.a == 0) { continue; } - buffer[y * sizeX + x] = src_pixel; + this->buffer[frame][y * this->sizeX + x] = src_pixel; } } break; @@ -111,7 +114,7 @@ export namespace Crafter { for (std::int32_t x = dirty.left; x < dirty.right; x++) { std::int32_t src_x = x - element->scaled.position.x; Vector src = src_buffer[src_y * src_width + src_x]; - Vector dst = buffer[y * sizeX + x]; + Vector dst = buffer[frame][y * this->sizeX + x]; if(src.a == 0) { continue; @@ -121,7 +124,7 @@ export namespace Crafter { float dstA = dst.a / 255.0f; float outA = srcA + dstA * (1.0f - srcA); - buffer[y * sizeX + x] = Vector( + this->buffer[frame][y * this->sizeX + x] = Vector( static_cast((src.r * srcA + dst.r * dstA * (1.0f - srcA)) / outA), static_cast((src.g * srcA + dst.g * dstA * (1.0f - srcA)) / outA), static_cast((src.b * srcA + dst.b * dstA * (1.0f - srcA)) / outA), @@ -139,12 +142,11 @@ export namespace Crafter { } std::sort(elementTransform->children.begin(), elementTransform->children.end(), [](Transform2D* a, Transform2D* b){ return a->anchor.z < b->anchor.z; }); for(Transform2D* child : elementTransform->children) { - this->RenderElement(child); + this->RenderElement(child, frame); } } - void Render() { - elements.erase(std::remove(elements.begin(), elements.end(), static_cast(nullptr)), elements.end()); - std::sort(elements.begin(), elements.end(), [](Transform2D* a, Transform2D* b){ return a->anchor.z < b->anchor.z; }); + void Render(std::uint8_t frame) { + std::sort(this->elements.begin(), this->elements.end(), [](Transform2D* a, Transform2D* b){ return a->anchor.z < b->anchor.z; }); //std::vector newClip; // for (std::uint32_t i = 0; i < dirtyRects.size(); i++) { @@ -245,19 +247,19 @@ export namespace Crafter { // ++rectIndex; // } - if (!dirtyRects.empty()) { - for (ClipRect rect : dirtyRects) { + if (!this->dirtyRects[frame].empty()) { + for (ClipRect rect : this->dirtyRects[frame]) { 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}; + this->buffer[frame][y * this->sizeX + x] = {0, 0, 0, 0}; } } } - for(Transform2D* child : elements) { - RenderElement(child); + for(Transform2D* child : this->elements) { + RenderElement(child, frame); } - dirtyRects.clear(); + this->dirtyRects[frame].clear(); } } }; diff --git a/interfaces/Crafter.Graphics-Transform2D.cppm b/interfaces/Crafter.Graphics-Transform2D.cppm index f92b569..96b4933 100644 --- a/interfaces/Crafter.Graphics-Transform2D.cppm +++ b/interfaces/Crafter.Graphics-Transform2D.cppm @@ -20,9 +20,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA export module Crafter.Graphics:Transform2D; import std; import :Types; +import :ForwardDeclarations; export namespace Crafter { - struct RendertargetBase; struct Anchor2D { float x; float y; @@ -35,17 +35,51 @@ export namespace Crafter { Anchor2D() = default; Anchor2D(float x, float y, float width, float height, float offsetX, float offsetY, std::int32_t z, bool maintainAspectRatio = false); }; + struct Transform2D { Anchor2D anchor; ScaleData2D scaled; std::vector children; Transform2D() = default; - Transform2D(Anchor2D anchor); + Transform2D(Anchor2D anchor) : anchor(anchor) { + + } Transform2D(Transform2D&) = delete; Transform2D(Transform2D&&) = delete; Transform2D& operator=(Transform2D&) = delete; virtual ~Transform2D() = default; - void ScaleElement(Transform2D& parent); - virtual void UpdatePosition(RendertargetBase& window, Transform2D& parent); + + virtual void UpdatePosition(RendertargetBase<1>& window, Transform2D& parent) { + ScaleElement(parent); + for(Transform2D* child : children) { + child->UpdatePosition(window, *this); + } + } + + virtual void UpdatePosition(RendertargetBase<3>& window, Transform2D& parent) { + ScaleElement(parent); + for(Transform2D* child : children) { + child->UpdatePosition(window, *this); + } + } + + void ScaleElement(Transform2D& parent) { + if(anchor.maintainAspectRatio) { + if(parent.scaled.size.x > parent.scaled.size.y) { + scaled.size.x = anchor.width * parent.scaled.size.y; + scaled.size.y = anchor.height * parent.scaled.size.y; + } else { + scaled.size.x = anchor.width * parent.scaled.size.x; + scaled.size.y = anchor.height * parent.scaled.size.x; + } + } else { + + scaled.size.x = anchor.width * parent.scaled.size.x; + scaled.size.y = anchor.height * parent.scaled.size.y; + } + + scaled.position.x = parent.scaled.position.x + (anchor.x * parent.scaled.size.x - anchor.offsetX * scaled.size.x); + scaled.position.y = parent.scaled.position.y + (anchor.y * parent.scaled.size.y - anchor.offsetY * scaled.size.y); + } }; } \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-Window.cppm b/interfaces/Crafter.Graphics-Window.cppm index 32f20dc..76365dd 100644 --- a/interfaces/Crafter.Graphics-Window.cppm +++ b/interfaces/Crafter.Graphics-Window.cppm @@ -96,7 +96,7 @@ export namespace Crafter { bool mouseRightHeld = false; std::vector mouseElements; std::vector pendingMouseElements; - Rendertarget cursorRenderer; + Rendertarget cursorRenderer; Window() = default; Window(std::uint32_t width, std::uint32_t height); @@ -130,7 +130,7 @@ export namespace Crafter { #ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND float scale; #ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE - Rendertarget renderer; + Rendertarget renderer; #endif bool configured = false; xdg_toplevel* xdgToplevel = nullptr; diff --git a/interfaces/Crafter.Graphics.cppm b/interfaces/Crafter.Graphics.cppm index 926422f..6e1d964 100644 --- a/interfaces/Crafter.Graphics.cppm +++ b/interfaces/Crafter.Graphics.cppm @@ -32,6 +32,7 @@ export import :Font; export import :Animation; export import :Mesh; export import :Rendertarget; +export import :ForwardDeclarations; #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN export import :Device; diff --git a/project.json b/project.json index 866e244..6ba2874 100644 --- a/project.json +++ b/project.json @@ -8,7 +8,6 @@ "implementations/Crafter.Graphics-Window", "implementations/Crafter.Graphics-MouseElement", "implementations/Crafter.Graphics-Transform2D", - "implementations/Crafter.Graphics-GridElement", "implementations/Crafter.Graphics-Device", "implementations/Crafter.Graphics-Mesh", "implementations/Crafter.Graphics-RenderingElement3D" @@ -36,7 +35,8 @@ "interfaces/Crafter.Graphics-ImageVulkan", "interfaces/Crafter.Graphics-SamplerVulkan", "interfaces/Crafter.Graphics-DescriptorSetLayoutVulkan", - "interfaces/Crafter.Graphics-Rendertarget" + "interfaces/Crafter.Graphics-Rendertarget", + "interfaces/Crafter.Graphics-ForwardDeclarations" ], "type": "library" },