Files
Renderive/YSGraphic_Core/architecture/Renderable.cpp
T
2026-07-28 17:55:29 +08:00

398 lines
15 KiB
C++

#include <algorithm>
#include <utility>
#include "../base/global.h"
#include "Plot.h"
#include "Renderable_Core.h"
namespace YSG {
Renderable::Color_Buffer_Lease::Color_Buffer_Lease(Color_Buffer* buffer, Triple_Role_Buffer_Control* control, Triple_Buffer_Lease lease) : buffer(buffer), control(control), lease(lease) {
}
Renderable::Color_Buffer_Lease::Color_Buffer_Lease(Color_Buffer_Lease&& other) noexcept : buffer(std::exchange(other.buffer, nullptr)), control(std::exchange(other.control, nullptr)), lease(std::exchange(other.lease, Triple_Buffer_Lease{})) {
}
Renderable::Color_Buffer_Lease& Renderable::Color_Buffer_Lease::operator=(Color_Buffer_Lease&& other) noexcept {
if (this == &other)
return *this;
reset();
buffer = std::exchange(other.buffer, nullptr);
control = std::exchange(other.control, nullptr);
lease = std::exchange(other.lease, Triple_Buffer_Lease{});
return *this;
}
Renderable::Color_Buffer_Lease::~Color_Buffer_Lease() {
reset();
}
void Renderable::Color_Buffer_Lease::reset() {
if (!control)
return;
control->unmark_use(lease);
buffer = nullptr;
control = nullptr;
lease = {};
}
Plot_Buffer_Acquire_Mode Renderable::buffer_acquire_mode() const {
return dPtr->buffer_acquire_mode();
}
void Renderable::set_buffer_acquire_mode(Plot_Buffer_Acquire_Mode mode) {
dPtr->set_buffer_acquire_mode(mode);
}
RenderAble_Render_Schedule_Mode Renderable::render_schedule_mode() const {
return dPtr->render_schedule_mode();
}
void Renderable::set_render_schedule_mode(RenderAble_Render_Schedule_Mode mode) {
dPtr->set_render_schedule_mode(mode);
}
double Renderable::render_schedule_max_fps() const {
return dPtr->render_schedule_max_fps();
}
void Renderable::set_render_schedule_max_fps(double fps) {
dPtr->set_render_schedule_max_fps(fps);
}
Renderable_Cache_Mode Renderable::cache_mode() const {
return mCacheMode.load(std::memory_order_acquire);
}
void Renderable::set_cache_mode(Renderable_Cache_Mode mode) {
mCacheMode.store(mode, std::memory_order_release);
mCacheDirty.store(true, std::memory_order_release);
mark_render_dirty();
}
Renderable_Child_Render_Order Renderable::child_render_order() const {
return mChildRenderOrder.load(std::memory_order_acquire);
}
void Renderable::set_child_render_order(Renderable_Child_Render_Order order) {
mChildRenderOrder.store(order, std::memory_order_release);
mark_render_dirty();
}
std::uint64_t Renderable::cache_image_bytes() const {
std::lock_guard<std::mutex> lock(mCacheMutex);
return mCacheImage.isNull() ? 0 : static_cast<std::uint64_t>(mCacheImage.sizeInBytes());
}
std::uint64_t Renderable::cache_image_area() const {
std::lock_guard<std::mutex> lock(mCacheMutex);
return static_cast<std::uint64_t>(mCacheBounds.width()) * static_cast<std::uint64_t>(mCacheBounds.height());
}
void Renderable::init_root(Abs_Plot* plot) {
if (!plot) {
ASSERT(plot, "Renderable root init error! plot == nullptr");
return;
}
bool initialized = mInitialized.exchange(true, std::memory_order_acq_rel);
if (initialized) {
ASSERT(!initialized, "Renderable root init error! renderable already initialized");
return;
}
init_common(plot);
}
void Renderable::init(const std::shared_ptr<Renderable>& parent) {
if (!parent || !parent->mPlot) {
ASSERT(parent && parent->mPlot, "Renderable init error! parent invalid");
return;
}
bool initialized = mInitialized.exchange(true, std::memory_order_acq_rel);
if (initialized) {
ASSERT(!initialized, "Renderable init error! renderable already initialized");
return;
}
init_common(parent->mPlot);
parent->add_child(shared_from_this());
}
void Renderable::init_common(Abs_Plot* plot) {
mPlot = plot;
mPlotContext = plot->render_context();
mRetiring.store(false, std::memory_order_release);
if (!dPtr)
dPtr = create_render_data();
dPtr->set_buffer_acquire_mode(plot->render_able_buffer_acquire_mode());
dPtr->clear_state_buffers();
dPtr->clear_input_buffers();
dPtr->mStateBuffers[State_Edit] = create_render_state();
dPtr->mStateBuffers[State_Ready] = create_render_state();
dPtr->mStateBuffers[State_Render] = create_render_state();
dPtr->mInputBuffers[Input_Edit].data = create_input_data();
dPtr->mInputBuffers[Input_Ready].data = create_input_data();
dPtr->mInputBuffers[Input_Render].data = create_input_data();
for (Render_State* state : dPtr->mStateBuffers) {
state->dPtr = dPtr;
}
dPtr->qPtr = this;
dPtr->mStateBuffers[State_Edit]->version = 1;
plot->mark_render_state_dirty();
}
void Renderable::add_child(const std::shared_ptr<Renderable>& child) {
if (!child || child.get() == this || child->mPlot != mPlot || !child->mParentRenderable.expired()) {
ASSERT(child && child.get() != this && child->mPlot == mPlot && child->mParentRenderable.expired(), "Renderable add_child error! invalid child");
return;
}
std::lock_guard<std::mutex> lock(mChildrenMutex);
if (mChildren.contains(child))
return;
child->mParentRenderable = shared_from_this();
mChildren.append(child);
mark_render_dirty();
}
void Renderable::remove_child(const std::shared_ptr<Renderable>& child) {
if (!child || child->mParentRenderable.lock().get() != this)
return;
{
std::lock_guard<std::mutex> lock(mChildrenMutex);
mChildren.erase(std::remove(mChildren.begin(), mChildren.end(), child), mChildren.end());
}
child->detach_from_plot_tree();
mark_render_dirty();
}
QVector<std::shared_ptr<Renderable>> Renderable::children_snapshot() const {
std::lock_guard<std::mutex> lock(mChildrenMutex);
return mChildren;
}
QVector<std::shared_ptr<Renderable>> Renderable::hit_renderables(const QPointF& pos) {
QVector<std::shared_ptr<Renderable>> hits;
append_hit_renderables(hits, pos);
return hits;
}
void Renderable::append_hit_renderables(QVector<std::shared_ptr<Renderable>>& hits, const QPointF& pos) {
if (retiring() || !mVisable)
return;
if (child_render_order() == Renderable_Child_Render_Order::Children_Before_Self && select_test(pos))
hits.append(shared_from_this());
QVector<std::shared_ptr<Renderable>> children = children_snapshot();
for (int i = children.size() - 1; i >= 0; --i) {
const auto& child = children.at(i);
if (child)
child->append_hit_renderables(hits, pos);
}
if (child_render_order() == Renderable_Child_Render_Order::Self_Before_Children && select_test(pos))
hits.append(shared_from_this());
}
void Renderable::render(QPainter* painter, const Plot_Render_Snapshot& snapshot) {
if (retiring() || !mVisable)
return;
if (cache_mode() == Renderable_Cache_Mode::Local_Pixel)
render_cached(painter, snapshot);
else
render_direct(painter, snapshot);
}
void Renderable::render_direct(QPainter* painter, const Plot_Render_Snapshot& snapshot) {
if (child_render_order() == Renderable_Child_Render_Order::Children_Before_Self) {
render_children(painter, snapshot);
render_self(painter, snapshot);
}
else {
render_self(painter, snapshot);
render_children(painter, snapshot);
}
}
void Renderable::render_children(QPainter* painter, const Plot_Render_Snapshot& snapshot) {
QVector<std::shared_ptr<Renderable>> children = children_snapshot();
for (const auto& child : children) {
if (child)
child->render(painter, snapshot);
}
}
void Renderable::render_cached(QPainter* painter, const Plot_Render_Snapshot& snapshot) {
QSize size = snapshot.size;
QRect bounds = dPtr ? dPtr->pixel_bounds(size).intersected(QRect(QPoint(0, 0), size)) : QRect(QPoint(0, 0), size);
if (bounds.isEmpty())
return;
std::uint64_t editVersion = mCacheEditVersion.load(std::memory_order_acquire);
{
std::lock_guard<std::mutex> lock(mCacheMutex);
bool ready = !mCacheDirty.load(std::memory_order_acquire) && mCacheReadyVersion.load(std::memory_order_acquire) == editVersion && mCacheImage.size() == bounds.size() && mCacheImage.format() == QImage::Format_ARGB32 && mCacheBounds == bounds;
if (ready) {
painter->drawImage(bounds.topLeft(), mCacheImage);
return;
}
if (!mCacheImage.isNull() && mCacheBounds == bounds) {
schedule_cache_render(bounds, snapshot, editVersion);
painter->drawImage(bounds.topLeft(), mCacheImage);
return;
}
}
render_cache_image(bounds, snapshot, editVersion);
std::lock_guard<std::mutex> lock(mCacheMutex);
if (!mCacheImage.isNull())
painter->drawImage(bounds.topLeft(), mCacheImage);
}
void Renderable::render_cache_image(const QRect& bounds, const Plot_Render_Snapshot& snapshot, std::uint64_t version) {
if (retiring())
return;
QImage cacheImage(bounds.size(), QImage::Format_ARGB32);
cacheImage.fill(Qt::transparent);
{
QPainter cachePainter(&cacheImage);
cachePainter.translate(-bounds.topLeft());
render_direct(&cachePainter, snapshot);
cachePainter.end();
}
{
std::lock_guard<std::mutex> lock(mCacheMutex);
mCacheBounds = bounds;
mCacheImage = cacheImage;
mCacheReadyVersion.store(version, std::memory_order_release);
bool dirty = mCacheEditVersion.load(std::memory_order_acquire) != version;
mCacheDirty.store(dirty, std::memory_order_release);
}
}
void Renderable::schedule_cache_render(const QRect& bounds, const Plot_Render_Snapshot& snapshot, std::uint64_t version) {
if (mCacheRenderQueued.exchange(true, std::memory_order_acq_rel))
return;
auto self = shared_from_this();
auto snapshotOwner = std::make_shared<Plot_Render_Snapshot>(snapshot);
Global::instance()->renderScheduler().post_cpu([self, snapshotOwner, bounds, version]() {
if (self->retiring() || !snapshotOwner || snapshotOwner->destroying()) {
self->mCacheRenderQueued.store(false, std::memory_order_release);
return;
}
self->render_cache_image(bounds, *snapshotOwner, version);
self->mCacheRenderQueued.store(false, std::memory_order_release);
if (!self->retiring())
self->notify_parent_render_dirty();
});
}
void Renderable::notify_parent_render_dirty() {
if (retiring())
return;
auto context = mPlotContext.lock();
if (!context || context->destroying.load(std::memory_order_acquire))
return;
if (auto parent = mParentRenderable.lock()) {
parent->mark_render_dirty();
return;
}
std::lock_guard<std::mutex> lock(context->mutex);
if (!context->destroying.load(std::memory_order_acquire) && context->plot)
context->plot->mark_render_state_dirty();
}
void Renderable::render_self(QPainter* painter, const Plot_Render_Snapshot& snapshot) {
if (!dPtr)
return;
auto lease = dPtr->mark_state_role(State_Render);
if (!lease)
return;
draw(painter, snapshot);
dPtr->unmark_state(lease);
}
void Renderable::draw(QPainter* painter, const Plot_Render_Snapshot& snapshot) {
if (retiring())
return;
dPtr->draw(painter, snapshot);
}
void Renderable::prepare_data(const Plot_Render_Snapshot& snapshot) {
if (retiring())
return;
dPtr->prepare_data(snapshot);
}
bool Renderable::select_test(const QPointF& pos) {
if (retiring())
return false;
return dPtr->select_test(pos);
}
void Renderable::plot_event(QEvent* event) {
if (retiring())
return;
dPtr->plot_event(event);
}
void Renderable::mousePressEvent(QMouseEvent* event) {
if (retiring())
return;
dPtr->mousePressEvent(event);
}
void Renderable::mouseMoveEvent(QMouseEvent* event) {
if (retiring())
return;
dPtr->mouseMoveEvent(event);
}
void Renderable::mouseReleaseEvent(QMouseEvent* event) {
if (retiring())
return;
dPtr->mouseReleaseEvent(event);
}
void Renderable::set_hover_state(const QPoint& pos, bool active) {
if (retiring())
return;
dPtr->set_hover_state(pos, active);
}
void Renderable::keyPressEvent(QKeyEvent* event) {
if (retiring())
return;
dPtr->keyPressEvent(event);
}
void Renderable::keyReleaseEvent(QKeyEvent* event) {
if (retiring())
return;
dPtr->keyReleaseEvent(event);
}
void Renderable::first_resize_event(QResizeEvent* event) {
if (retiring())
return;
dPtr->first_resize_event(event);
}
void Renderable::resizeEvent(QResizeEvent* event) {
if (retiring())
return;
dPtr->resizeEvent(event);
}
void Renderable::wheelEvent(QWheelEvent* event) {
if (retiring())
return;
dPtr->wheelEvent(event);
}
bool Renderable::version_newer(std::uint64_t a, std::uint64_t b) {
return buffer_version_newer(a, b);
}
void Renderable::mark_state_dirty() {
if (retiring())
return;
if (dPtr) {
auto lease = dPtr->mStateControl.wait_mark_use_role(State_Edit);
++dPtr->state_by_index(lease.index)->version;
dPtr->mStateControl.unmark_use(lease);
}
mark_render_dirty();
}
void Renderable::mark_render_dirty() {
if (retiring())
return;
auto context = mPlotContext.lock();
if (!context || context->destroying.load(std::memory_order_acquire))
return;
mCacheDirty.store(true, std::memory_order_release);
mCacheEditVersion.fetch_add(1, std::memory_order_acq_rel);
if (auto parent = mParentRenderable.lock()) {
parent->mark_render_dirty();
return;
}
std::lock_guard<std::mutex> lock(context->mutex);
if (!context->destroying.load(std::memory_order_acquire) && context->plot)
context->plot->mark_render_state_dirty();
}
bool Renderable::ok() const {
auto context = mPlotContext.lock();
return dPtr && dPtr->mRenderStart && !retiring() && context && !context->destroying.load(std::memory_order_acquire) && !context->firstResize.load(std::memory_order_acquire) && !context->firstPrepareData.load(std::memory_order_acquire);
}
bool Renderable::retiring() const {
return mRetiring.load(std::memory_order_acquire);
}
void Renderable::detach_from_plot_tree() {
mRetiring.store(true, std::memory_order_release);
QVector<std::shared_ptr<Renderable>> children = children_snapshot();
{
std::lock_guard<std::mutex> lock(mChildrenMutex);
mChildren.clear();
}
for (const auto& child : children) {
if (child)
child->detach_from_plot_tree();
}
mPlot = nullptr;
mCacheRenderQueued.store(false, std::memory_order_release);
{
std::lock_guard<std::mutex> lock(mCacheMutex);
mCacheImage = QImage();
mCacheBounds = {};
}
}
Renderable::~Renderable() {
detach_from_plot_tree();
delete dPtr;
dPtr = nullptr;
}
} // namespace YSG