16#include <spdlog/spdlog.h>
22 : m_pipeline_context(std::make_unique<Pipeline::PipelineContext>())
23 , m_worker_context(std::make_unique<Workers::WorkerContext>())
24 , m_working_image_context(std::make_unique<ImageProcessing::WorkingImageContext>())
25 , m_source_manager(std::make_unique<Managers::
SourceManager>())
27 if (!m_source_manager) {
28 spdlog::critical(
"[StateImageManager::StateImageManager]: Null dependency provided during construction.");
29 throw std::invalid_argument(
"[StateImageManager::StateImageManager]: Null dependency provided.");
33 m_worker_thread = std::thread(&StateImageManager::workerLoop,
this);
40 std::lock_guard<std::mutex> lock(m_work_mutex);
41 m_stop_requested.store(
true, std::memory_order_release);
43 m_work_cv.notify_one();
46 if (m_worker_thread.joinable()) {
47 m_worker_thread.join();
50 spdlog::debug(
"[StateImageManager::~StateImageManager]: Destroyed.");
60 waitForPendingProcessing();
63 auto load_result { m_source_manager->loadFile(path) };
66 spdlog::error(
"[StateImageManager::loadImage]: Failed to load file '{}': {}", path,
static_cast<int>(load_result.error()));
72 auto tile { m_source_manager->getTile(0, 0, w, h) };
75 spdlog::error(
"[StateImageManager::loadImage]: Failed to get tile for loaded image '{}': {}", path,
static_cast<int>(tile.error()));
79 if (!m_working_image_context->prepare(std::move(tile.value()))) {
80 spdlog::error(
"[StateImageManager::loadImage]: Failed to prepare working image context for '{}'.", path);
86 std::lock_guard lock(m_state_mutex);
87 m_original_image_path = std::string(path);
92 std::lock_guard lock(m_work_mutex);
93 m_pending_work.reset();
96 spdlog::info(
"[StateImageManager::loadImage]: Image '{}' loaded successfully ({}x{}).",
97 path, m_source_manager->width(), m_source_manager->height());
103void StateImageManager::workerLoop()
105 spdlog::trace(
"[StateImageManager::workerLoop]: Thread started.");
108 while (!m_stop_requested.load(std::memory_order_acquire))
110 std::optional<std::vector<Operations::OperationDescriptor>> work_to_do;
111 std::shared_ptr<std::promise<bool>> work_promise;
117 std::unique_lock<std::mutex> lock(m_work_mutex);
120 m_work_cv.wait(lock, [
this] {
121 return m_stop_requested.load(std::memory_order_acquire)
122 || m_pending_work.has_value();
125 if (m_stop_requested.load(std::memory_order_acquire))
128 if (m_active_promise) {
129 try { m_active_promise->set_value(
false); }
130 catch (
const std::future_error&) {}
134 m_pending_work.reset();
135 m_is_idle.store(
true, std::memory_order_release);
136 m_idle_cv.notify_all();
142 work_to_do = std::move(m_pending_work);
143 m_pending_work.reset();
144 work_promise = m_active_promise;
150 const bool success { executeProcessing(std::move(*work_to_do)) };
155 bool has_more_work {
false };
157 std::lock_guard<std::mutex> lock(m_work_mutex);
158 has_more_work = m_pending_work.has_value();
164 m_is_idle.store(
true, std::memory_order_release);
168 try { work_promise->set_value(success); }
169 catch (
const std::future_error&) {}
176 spdlog::trace(
"[StateImageManager::workerLoop]: Thread exiting.");
179bool StateImageManager::executeProcessing(std::vector<Operations::OperationDescriptor> ops)
183 spdlog::trace(
"[StateImageManager::executeProcessing]: Executing {} operations.", ops.size());
185 auto& halide_manager { m_pipeline_context->getHalideManager() };
186 halide_manager.init(std::move(ops));
188 auto worker { m_worker_context->getHalideOperationWorker() };
190 std::shared_ptr<ImageProcessing::IWorkingImageHardware> working_image;
192 std::lock_guard<std::mutex> lock(m_state_mutex);
193 working_image = m_working_image_context->getWorkingImage();
196 if (!working_image) {
197 spdlog::error(
"[StateImageManager::executeProcessing]: No working image available.");
201 auto future { worker.execute(*m_pipeline_context, *working_image) };
204 catch (
const std::exception& e)
206 spdlog::error(
"[StateImageManager::executeProcessing]: Exception during processing: {}", e.what());
214 waitForPendingProcessing();
216 std::shared_ptr<ImageProcessing::IWorkingImageHardware> working_image_hw;
218 std::lock_guard lock(m_state_mutex);
219 working_image_hw = m_working_image_context->getWorkingImage();
222 if (!working_image_hw) {
223 spdlog::error(
"[StateImageManager::commitWorkingImageToSource]: No working image available.");
227 auto cpu_copy_result { working_image_hw->getFullResImage() };
228 if (!cpu_copy_result) {
229 spdlog::error(
"[StateImageManager::commitWorkingImageToSource]: CPU export failed: {}",
231 return std::unexpected(cpu_copy_result.error());
234 std::unique_ptr<Common::ImageRegion> cpu_copy { std::move(cpu_copy_result.value()) };
236 if (!m_source_manager->setTile(*cpu_copy)) {
237 spdlog::error(
"[StateImageManager::commitWorkingImageToSource]: Write to source failed.");
241 spdlog::info(
"[StateImageManager::commitWorkingImageToSource]: Changes committed to source.");
248 auto future {
applyOperations(std::vector<Operations::OperationDescriptor>{}) };
252 bool success { future.get() };
256 spdlog::error(
"[StateImageManager::resetToOriginal]: Processing failed.");
265 return m_source_manager->getImageSourcePath();
270 spdlog::trace(
"[StateImageManager::applyOperations]: Received {} ops.", ops.size());
272 auto promise { std::make_shared<std::promise<bool>>() };
273 auto future { promise->get_future() };
276 std::lock_guard<std::mutex> lock(m_work_mutex);
278 if (m_active_promise) {
279 try { m_active_promise->set_value(
true); }
280 catch (
const std::future_error&) {}
284 m_active_promise = promise;
285 m_pending_work = std::move(ops);
288 m_is_idle.store(
false, std::memory_order_release);
292 m_work_cv.notify_one();
297void StateImageManager::waitForPendingProcessing()
299 std::unique_lock<std::mutex> lock(m_idle_mutex);
300 m_idle_cv.wait(lock, [
this] {
301 return m_is_idle.load(std::memory_order_acquire);
307 std::lock_guard lock(m_state_mutex);
308 return m_source_manager ? m_source_manager->width() : 0;
313 std::lock_guard lock(m_state_mutex);
314 return m_source_manager ? m_source_manager->height() : 0;
319 std::lock_guard lock(m_state_mutex);
320 return m_source_manager ? m_source_manager->channels() : 0;
325 std::lock_guard lock(m_state_mutex);
327 if (!m_working_image_context) {
331 return m_working_image_context->getWorkingImageAsRegion();
337 std::lock_guard lock(m_state_mutex);
339 if (!m_working_image_context) {
340 spdlog::error(
"[StateImageManager::getDownsampledDisplayImage]: No WorkingImageContext");
344 return m_working_image_context->getDownsampled(target_width, target_height);
Concrete implementation of ISourceManager using OIIO.
bool loadImage(std::string_view path)
Loads an image file into the internal SourceManager.
Common::ImageChan getSourceChannels() const
Gets the number of channels of the original source image.
~StateImageManager()
Destructor.
std::expected< void, ErrorHandling::CoreError > commitWorkingImageToSource()
Commits the current working image (with applied operations) back to the SourceManager.
Common::ImageDim getSourceWidth() const
Gets the width of the original source image.
Common::ImageDim getSourceHeight() const
Gets the height of the original source image.
std::expected< void, ErrorHandling::CoreError > resetToOriginal()
Resets the image to its original state (synchronous).
StateImageManager()
Constructs a StateImageManager.
std::expected< std::unique_ptr< Common::ImageRegion >, ErrorHandling::CoreError > getDownsampledDisplayImage(Common::ImageDim target_width, Common::ImageDim target_height)
Gets a downsampled version of the working image for display. Uses GPU-accelerated downsampling,...
std::string getImageSourcePath() const
Gets the path of the image source.
std::future< bool > applyOperations(std::vector< Operations::OperationDescriptor > &&ops)
Applies a list of operations to the current image using move semantics.
std::expected< std::unique_ptr< Common::ImageRegion >, ErrorHandling::CoreError > getWorkingImageAsRegion() const
Exports the current working image data to CPU memory as an ImageRegion.
std::uint8_t ImageChan
Type for channel count.
std::size_t ImageDim
Type for image dimensions (width, height).
constexpr std::string_view to_string(CoreError code)
Converts a CoreError code to a human-readable string.
CoreError
Enumeration of all error codes for CaptureMoment Core.
@ IOError
Generic File I/O error.
@ InvalidWorkingImage
The working image state is invalid or corrupted.
@ AllocationFailed
Memory allocation failed.
Container for pipeline infrastructure (Builder and Managers).
Image source management class using OpenImageIO (OIIO).
Declaration of StateImageManager class.
Container for worker infrastructure (Builder and Workers).
Manages the lifecycle of a single WorkingImage instance and its associated data.
Factory for creating IWorkingImageHardware instances using a Registry Pattern.