This commit is contained in:
2026-08-29 14:37:56 +08:00
parent 4ff2220e38
commit 46ddd5d3d6
4 changed files with 111 additions and 19 deletions
+31 -1
View File
@@ -277,7 +277,10 @@ protected:
const auto input = configuration.input;
for (auto& count : workload->completed)
count->store(0, std::memory_order_relaxed);
for (const auto& plot : plots) plot->reset_diagnostics();
for (const auto& plot : plots) {
plot->reset_diagnostics();
plot->request_taskflow_trace(1);
}
if (input == Input_Workload::drag || input == Input_Workload::mixed) {
if (!submit_input_batch(
input, 0, Event_Type::pointer_press)) {
@@ -478,6 +481,29 @@ protected:
? busy_ns / (wall_ns * worker_count) * 100.0 : 0.0;
state.counters["worker_cpu_pct"] = wall_ns > 0.0 && worker_count > 0.0
? cpu_ns / (wall_ns * worker_count) * 100.0 : 0.0;
double longest_sampled_busy_ms{};
std::string longest_sampled_busy_node;
std::string longest_sampled_busy_plot;
for (std::size_t plot_index = 0; plot_index < plots.size(); ++plot_index) {
const auto trace = plots[plot_index]->taskflow_trace();
if (!trace.value("complete", false)) continue;
for (const auto& frame : trace.at("frames"))
for (const auto& execution : frame.at("executions")) {
const auto busy = std::max(
0.0, execution.value("duration_ms", 0.0) -
execution.value("cooperative_wait_ms", 0.0));
if (busy <= longest_sampled_busy_ms) continue;
longest_sampled_busy_ms = busy;
longest_sampled_busy_node = execution.value(
"node_id", std::string{});
longest_sampled_busy_plot =
std::string{configuration.plot_ids[plot_index]};
}
}
state.counters["sampled_longest_busy_ms"] = longest_sampled_busy_ms;
if (!longest_sampled_busy_node.empty())
state.SetLabel("busy=" + longest_sampled_busy_plot + ":" +
longest_sampled_busy_node);
state.counters["cooperative_yields"] = cooperative_waits;
state.counters["cooperative_wait_ms"] =
cooperative_wait_ns / 1'000'000.0;
@@ -632,9 +658,13 @@ public:
<< " input_requests/s=" << counter("input_request_rate")
<< " worker_busy=" << counter("worker_busy_pct") << "%"
<< " worker_cpu=" << counter("worker_cpu_pct") << "%"
<< " sampled_busy_max="
<< counter("sampled_longest_busy_ms") << " ms"
<< " cooperative_yields=" << counter("cooperative_yields")
<< " cooperative_wait=" << counter("cooperative_wait_ms")
<< " ms\n";
if (!report.report_label.empty())
output << report.report_label << '\n';
}
output.flags(flags);
output.precision(precision);
@@ -137,7 +137,7 @@ private:
owner<DvzDrp2Runtime*> runtime{}; /* 本目标槽独占的 DRP2 Runtime。 */
owner<DvzFramePlanEmitter*> emitter{}; /* 本目标槽独占的帧计划 emitter。 */
};
std::shared_ptr<Datoviz_Render_Context> render_context_{}; /* 本 Scene 独占 GPU 上下文的共享销毁闸门。 */
std::shared_ptr<Datoviz_Render_Context> render_context_{}; /* 按 GPU 索引与验证模式共享的进程级上下文。 */
std::uintptr_t command_pool_{}; /* 本 Scene 独占 Vulkan Command Pool。 */
std::uintptr_t descriptor_pool_{}; /* 本 Scene 独占 Vulkan Descriptor Pool。 */
std::array<Runtime_Slot, 3> runtime_slots_{}; /* 三个固定目标槽的 Runtime。 */
@@ -507,7 +507,7 @@ void Scene_Datoviz_State::quarantine(
void Scene_Datoviz_State::abandon_resources() noexcept {
quarantined_.store(true, std::memory_order_release);
static_cast<void>(targets_.release());
retain_quarantined_context(render_context_);
render_context_.reset();
}
/* 必须在无在途帧时调用;按 Runtime -> Scene -> Vulkan 池 -> GPU 上下文逆序释放。 */
void Scene_Datoviz_State::destroy() {
@@ -23,7 +23,6 @@
#include <iomanip>
#include <memory>
#include <mutex>
#include <new>
#include <numbers>
#include <sstream>
#include <stdexcept>
@@ -126,10 +125,7 @@ struct Datoviz_Render_Context final :
std::enable_shared_from_this<Datoviz_Render_Context> {
public:
[[nodiscard]] static std::shared_ptr<Datoviz_Render_Context> acquire(
std::uint32_t gpu_index, bool validation_enabled) {
return std::shared_ptr<Datoviz_Render_Context>(
new Datoviz_Render_Context(gpu_index, validation_enabled));
}
std::uint32_t gpu_index, bool validation_enabled);
~Datoviz_Render_Context() {
if (gpu_context_ != nullptr) dvz_gpu_ctx_destroy(gpu_context_);
}
@@ -151,6 +147,10 @@ public:
&configuration, &Datoviz_Render_Context::submit_immediate, this);
}
private:
struct Registry_Entry;
struct Registry;
static Registry registry_;
struct Immediate_Submission final {
explicit Immediate_Submission(VkDevice device_value) noexcept :
device(device_value) {}
@@ -263,20 +263,82 @@ private:
if (submission_generation_.load(std::memory_order_acquire) != observed)
arm_submission_drain();
}
owner<DvzGpuCtx*> gpu_context_{}; /* 当前 Scene 独占 GPU Device 与分配器。 */
owner<DvzGpuCtx*> gpu_context_{}; /* 同 GPU 配置的 Scene 共享的进程级 Device 与分配器。 */
moodycamel::ConcurrentQueue<std::function<void()>> submissions_{};
std::atomic_uint64_t submission_generation_{};
std::atomic_bool submission_drain_active_{};
};
void retain_quarantined_context(
std::shared_ptr<Datoviz_Render_Context>& context) noexcept {
if (!context) return;
static moodycamel::ConcurrentQueue<
std::shared_ptr<Datoviz_Render_Context>> quarantined;
if (quarantined.enqueue(context))
context.reset();
else
static_cast<void>(new(std::nothrow)
std::shared_ptr<Datoviz_Render_Context>(std::move(context)));
/*
* Sole process authority for a Datoviz GPU configuration. Entries and their
* contexts are immutable after publication, so readers neither lock nor wait.
*/
struct Datoviz_Render_Context::Registry_Entry final {
Registry_Entry(std::uint32_t gpu_index_value, bool validation_value,
std::shared_ptr<Datoviz_Render_Context> context_value) noexcept :
gpu_index(gpu_index_value), validation_enabled(validation_value),
context(std::move(context_value)) {}
std::uint32_t gpu_index{};
bool validation_enabled{};
std::shared_ptr<Datoviz_Render_Context> context{};
Registry_Entry* next{}; /* Nullable, non-owning link; Registry::head owns every published entry. */
};
struct Datoviz_Render_Context::Registry final {
Registry() = default;
~Registry() {
owner<Registry_Entry*> entry = head.exchange(
nullptr, std::memory_order_acq_rel);
while (entry != nullptr) {
owner<Registry_Entry*> next = entry->next;
delete entry;
entry = next;
}
}
Registry(const Registry&) = delete;
Registry& operator=(const Registry&) = delete;
[[nodiscard]] std::shared_ptr<Datoviz_Render_Context> acquire(
std::uint32_t gpu_index, bool validation_enabled) {
const auto find = [gpu_index, validation_enabled](
Registry_Entry* first) noexcept -> Registry_Entry* {
for (auto* entry = first; entry != nullptr; entry = entry->next) {
if (entry->gpu_index == gpu_index &&
entry->validation_enabled == validation_enabled)
return entry;
}
return nullptr;
};
if (auto* entry = find(head.load(std::memory_order_acquire)))
return entry->context;
auto context = std::shared_ptr<Datoviz_Render_Context>(
new Datoviz_Render_Context(gpu_index, validation_enabled));
auto candidate = std::make_unique<Registry_Entry>(
gpu_index, validation_enabled, context);
for (;;) {
Registry_Entry* first = head.load(std::memory_order_acquire);
if (auto* entry = find(first)) return entry->context;
candidate->next = first;
Registry_Entry* published = candidate.get();
if (head.compare_exchange_weak(
first, published, std::memory_order_release,
std::memory_order_acquire)) {
static_cast<void>(candidate.release());
return context;
}
}
}
std::atomic<owner<Registry_Entry*>> head{};
};
inline Datoviz_Render_Context::Registry Datoviz_Render_Context::registry_{};
std::shared_ptr<Datoviz_Render_Context> Datoviz_Render_Context::acquire(
std::uint32_t gpu_index, bool validation_enabled) {
return registry_.acquire(gpu_index, validation_enabled);
}
} // namespace aethera::render_3d::detail