398 lines
15 KiB
C++
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
|