Path: blob/master/servers/rendering/renderer_rd/pipeline_hash_map_rd.h
21504 views
/**************************************************************************/1/* pipeline_hash_map_rd.h */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#pragma once3132#include "core/templates/rb_set.h"33#include "servers/rendering/rendering_device.h"34#include "servers/rendering/rendering_server.h"3536#define PRINT_PIPELINE_COMPILATION_KEYS 03738template <typename Key, typename CreationClass, typename CreationFunction>39class PipelineHashMapRD {40private:41CreationClass *creation_object = nullptr;42CreationFunction creation_function = nullptr;43Mutex *compilations_mutex = nullptr;44uint32_t *compilations = nullptr;45RBMap<uint32_t, RID> hash_map;46LocalVector<Pair<uint32_t, RID>> compiled_queue;47Mutex compiled_queue_mutex;48RBSet<uint32_t> compilation_set;49HashMap<uint32_t, WorkerThreadPool::TaskID> compilation_tasks;50Mutex local_mutex;5152bool _add_new_pipelines_to_map() {53thread_local Vector<uint32_t> hashes_added;54hashes_added.clear();5556{57MutexLock lock(compiled_queue_mutex);58for (const Pair<uint32_t, RID> &pair : compiled_queue) {59hash_map[pair.first] = pair.second;60hashes_added.push_back(pair.first);61}6263compiled_queue.clear();64}6566{67MutexLock local_lock(local_mutex);68for (uint32_t hash : hashes_added) {69HashMap<uint32_t, WorkerThreadPool::TaskID>::Iterator task_it = compilation_tasks.find(hash);70if (task_it != compilation_tasks.end()) {71compilation_tasks.remove(task_it);72}73}74}7576return !hashes_added.is_empty();77}7879void _wait_for_all_pipelines() {80thread_local LocalVector<WorkerThreadPool::TaskID> tasks_to_wait;81tasks_to_wait.clear();82{83MutexLock local_lock(local_mutex);84for (KeyValue<uint32_t, WorkerThreadPool::TaskID> key_value : compilation_tasks) {85tasks_to_wait.push_back(key_value.value);86}87}8889for (WorkerThreadPool::TaskID task_id : tasks_to_wait) {90WorkerThreadPool::get_singleton()->wait_for_task_completion(task_id);91}92}9394public:95void add_compiled_pipeline(uint32_t p_hash, RID p_pipeline) {96compiled_queue_mutex.lock();97compiled_queue.push_back({ p_hash, p_pipeline });98compiled_queue_mutex.unlock();99}100101// Start compilation of a pipeline ahead of time in the background. Returns true if the compilation was started, false if it wasn't required. Source is only used for collecting statistics.102void compile_pipeline(const Key &p_key, uint32_t p_key_hash, RS::PipelineSource p_source, bool p_high_priority) {103DEV_ASSERT((creation_object != nullptr) && (creation_function != nullptr) && "Creation object and function was not set before attempting to compile a pipeline.");104105MutexLock local_lock(local_mutex);106if (compilation_set.has(p_key_hash)) {107// Check if the pipeline was already submitted.108return;109}110111// Record the pipeline as submitted, a task can't be started for it again.112compilation_set.insert(p_key_hash);113114if (compilations_mutex != nullptr) {115MutexLock compilations_lock(*compilations_mutex);116compilations[p_source]++;117}118119#if PRINT_PIPELINE_COMPILATION_KEYS120String source_name = "UNKNOWN";121switch (p_source) {122case RS::PIPELINE_SOURCE_CANVAS:123source_name = "CANVAS";124break;125case RS::PIPELINE_SOURCE_MESH:126source_name = "MESH";127break;128case RS::PIPELINE_SOURCE_SURFACE:129source_name = "SURFACE";130break;131case RS::PIPELINE_SOURCE_DRAW:132source_name = "DRAW";133break;134case RS::PIPELINE_SOURCE_SPECIALIZATION:135source_name = "SPECIALIZATION";136break;137}138139print_line("HASH:", p_key_hash, "SOURCE:", source_name);140#endif141142// Queue a background compilation task.143WorkerThreadPool::TaskID task_id = WorkerThreadPool::get_singleton()->add_template_task(creation_object, creation_function, p_key, p_high_priority, "PipelineCompilation");144compilation_tasks.insert(p_key_hash, task_id);145}146147void wait_for_pipeline(uint32_t p_key_hash) {148WorkerThreadPool::TaskID task_id_to_wait = WorkerThreadPool::INVALID_TASK_ID;149150{151MutexLock local_lock(local_mutex);152if (!compilation_set.has(p_key_hash)) {153// The pipeline was never submitted, we can't wait for it.154return;155}156157HashMap<uint32_t, WorkerThreadPool::TaskID>::Iterator task_it = compilation_tasks.find(p_key_hash);158if (task_it != compilation_tasks.end()) {159// Wait for and remove the compilation task if it exists.160task_id_to_wait = task_it->value;161compilation_tasks.remove(task_it);162}163}164165if (task_id_to_wait != WorkerThreadPool::INVALID_TASK_ID) {166WorkerThreadPool::get_singleton()->wait_for_task_completion(task_id_to_wait);167}168}169170// Retrieve a pipeline. It'll return an empty pipeline if it's not available yet, but it'll be guaranteed to succeed if 'wait for compilation' is true and stall as necessary. Source is just an optional number to aid debugging.171RID get_pipeline(const Key &p_key, uint32_t p_key_hash, bool p_wait_for_compilation, RS::PipelineSource p_source) {172RBMap<uint32_t, RID>::Element *e = hash_map.find(p_key_hash);173174if (e == nullptr) {175// Check if there's any new pipelines that need to be added and try again. This method triggers a mutex lock.176if (_add_new_pipelines_to_map()) {177e = hash_map.find(p_key_hash);178}179}180181if (e == nullptr) {182// Request compilation. The method will ignore the request if it's already being compiled.183compile_pipeline(p_key, p_key_hash, p_source, p_wait_for_compilation);184185if (p_wait_for_compilation) {186wait_for_pipeline(p_key_hash);187_add_new_pipelines_to_map();188189e = hash_map.find(p_key_hash);190if (e != nullptr) {191return e->value();192} else {193// Pipeline could not be compiled due to an internal error. Store an empty RID so compilation is not attempted again.194hash_map[p_key_hash] = RID();195return RID();196}197} else {198return RID();199}200} else {201return e->value();202}203}204205// Delete all cached pipelines. Can stall if background compilation is in progress.206void clear_pipelines() {207_wait_for_all_pipelines();208_add_new_pipelines_to_map();209210for (KeyValue<uint32_t, RID> entry : hash_map) {211RD::get_singleton()->free_rid(entry.value);212}213214hash_map.clear();215compilation_set.clear();216}217218// Set the external pipeline compilations array to increase the counters on every time a pipeline is compiled.219void set_compilations(uint32_t *p_compilations, Mutex *p_compilations_mutex) {220compilations = p_compilations;221compilations_mutex = p_compilations_mutex;222}223224void set_creation_object_and_function(CreationClass *p_creation_object, CreationFunction p_creation_function) {225creation_object = p_creation_object;226creation_function = p_creation_function;227}228229PipelineHashMapRD() {}230231~PipelineHashMapRD() {232clear_pipelines();233}234};235236237