1#include "CaptureSystem.h"
4#include "Services/Time.h"
5#include "Components/Core/CameraComponent.h"
6#include "Systems/Core/CameraSystem.h"
7#include "Services/Time.h"
8#include "Resources/TextureManager.h"
9#include "Renderer/RenderResource.h"
11#include "Foundation/Logging/Logger.h"
12#include "Foundation/Platform/IO.h"
17#define STB_IMAGE_WRITE_IMPLEMENTATION
18#include "../../../Libraries/stb/stb_image_write.h"
30 void writeFunc(
void *context,
void *data,
int size)
36 v->resize(size,
false);
37 std::memcpy(v->data(), data, size);
41 struct PNGEncodingTask
43 std::atomic<CaptureItem*>& taskItem;
47 auto * item = taskItem.load();
49 using namespace std::chrono_literals;
53 item->state = CaptureItem::State::Dropped;
56 if (stbi_write_png_to_func(writeFunc, &item->data, item->width, item->height, 4, item->data.data(), 4 * item->width) == 0)
58 LOG_WARNING(logger,
"PNG encoding of frame %d failed", item->frame);
59 item->state = CaptureItem::State::Dropped;
62 item->state = CaptureItem::State::EncodedAsPng;
67 taskItem.store(
nullptr);
73 std::atomic<CaptureItem*>& taskItem;
77 auto * item = taskItem.load();
79 LOG_WARNING(logger,
"Storing of images not implemented.");
83 if (item->recordPath.empty()) {
87 sep[0] = Cogs::IO::pathSeparator();
91 auto seconds = int(std::floor(item->time));
92 auto millis = int(std::floor(1000.f*(item->time - seconds)));
94 const char* suffix =
nullptr;
95 switch (item->format) {
96 case CaptureFormat::PNG:
99 case CaptureFormat::RawARGB:
102 case CaptureFormat::RawFloat:
110 snprintf(buffer,
sizeof(buffer),
"%s%s%s-f%05d-t%05d_%03d-%dx%d.%s", item->recordPath.c_str(), sep, item->name.c_str(), item->frame, seconds, millis, item->width, item->height, suffix);
113 LOG_DEBUG(logger,
"Writing '%s'", buffer);
114 Cogs::IO::writeBinaryFile(buffer, item->data.data(), item->data.size());
116 item->state = CaptureItem::State::Ready;
117 taskItem.store(
nullptr);
123 void readbackCallback(
Context * context,
const CameraData* camData, uint32_t frame,
const char * key,
const void * data,
int size_)
125 const auto size =
static_cast<unsigned>(size_);
126 if (std::string(key) !=
"Capture")
return;
130 LOG_FATAL(logger,
"No capture component attached to camera.");
133 auto & capData = context->captureSystem->getData(capComp);
135 for (
auto & item : capData.items) {
136 if (item->frame == frame) {
138 item->width = int(std::max(0.f, camData->viewportSize[0]));
139 item->height = int(std::max(0.f, camData->viewportSize[1]));
141 if (item->format == CaptureFormat::PNG) {
143 auto expectedSize = 4 * item->width * item->height;
144 if (size != expectedSize) {
145 LOG_WARNING(logger,
"Expected %d bytes of image data, got %d bytes, dropping frame %d", expectedSize, size, frame);
149 if (capData.encodePNGItem.load()) {
150 if (capComp->dropFrames) {
152 item->state = CaptureItem::State::Dropped;
157 assert(capData.encodePNGItem.load() ==
nullptr);
158 capData.encodePngTask = NoTask;
162 item->state = CaptureItem::State::EncodeToPng;
163 item->data.resize(size,
false);
164 std::memcpy(item->data.data(), data, size);
166 capData.encodePNGItem.store(item.get());
168 PNGEncodingTask{ capData.encodePNGItem });
172 item->state = CaptureItem::State::ReceivedReadback;
173 item->data.resize(size,
false);
174 std::memcpy(item->data.data(), data, size);
177 if (0 < capComp->framesToCapture) {
178 capComp->framesToCapture--;
187 void removeDroppedItems(std::vector<std::unique_ptr<CaptureItem>>& itemStore, std::deque<std::unique_ptr<CaptureItem>>& items)
189 for (
auto it = items.begin(); it != items.end(); ) {
190 if ((*it)->state == CaptureItem::State::Dropped) {
192 itemStore.push_back(std::move(*it));
193 it = items.erase(it);
201 void removeScheduledForDiscard(std::vector<std::unique_ptr<CaptureItem>>& itemStore, std::deque<std::unique_ptr<CaptureItem>>& items)
205 bool anyNotJustIssued =
false;
206 for (
auto & item : items) {
207 if (item->state != CaptureItem::State::Issued) {
208 anyNotJustIssued =
true;
213 for (
auto it = items.begin(); it != items.end(); ) {
215 bool discard =
false;
217 if (anyNotJustIssued && item->state == CaptureItem::State::Issued) {
221 anyNotJustIssued =
false;
222 if (item->state == CaptureItem::State::DiscardNextFrame) {
228 itemStore.push_back(std::move(*it));
229 it = items.erase(it);
239 for (
auto & item : items) {
240 if (item->state == CaptureItem::State::Ready) {
241 item->state = CaptureItem::State::DiscardNextFrame;
253 context->callbacks.readbackCallbackInternal.push_back(readbackCallback);
258 for (
auto & capComp : pool) {
259 auto & capData = getData(&capComp);
262 if (!camComp)
continue;
264 bool doCapture = capComp.framesToCapture != 0;
267 snprintf(args,
sizeof(args),
"Capture=%s&Format=%s", doCapture ?
"True" :
"False", capComp.format ==
CaptureFormat::RawFloat ?
"Float" :
"4xU8");
269 switch (capComp.mode) {
272 camComp->renderPipeline =
"";
276 switch (capComp.sceneMode) {
278 camComp->renderPipeline =
"Pipelines/CaptureForward.pipeline?" + std::string(args);
281 camComp->renderPipeline =
"Pipelines/CaptureForwardHdr.pipeline?" + std::string(args);
284 camComp->renderPipeline =
"Pipelines/CaptureDeferredCausticsHdr.pipeline?" + std::string(args);
292 camComp->renderPipeline =
"Pipelines/Capture.pipeline?Mode=ObjectId&" + std::string(args);
296 camComp->renderPipeline =
"Pipelines/Capture.pipeline?Mode=ObjectIdColorize&" + std::string(args);
300 camComp->renderPipeline =
"Pipelines/Capture.pipeline?Mode=Normals&" + std::string(args);
304 camComp->renderPipeline =
"Pipelines/Capture.pipeline?Mode=Depth&" + std::string(args);
309 camComp->setChanged();
311 for (
auto & item : capData.items) {
312 if (item->state == CaptureItem::State::ReceivedReadback || item->state == CaptureItem::State::EncodedAsPng)
314 if (item->recordToDisc) {
315 if (capData.storeImageItem.load() && capComp.dropFrames) {
316 LOG_WARNING(logger,
"Storing of previous frame not finished, skip storing of frame %d.", item->frame);
317 item->state = CaptureItem::State::Ready;
320 if (capData.storeImageTask.isValid()) {
321 context->
taskManager->wait(capData.storeImageTask);
322 capData.storeImageTask = NoTask;
324 assert(capData.storeImageItem.load() ==
nullptr);
325 item->state = CaptureItem::State::WriteToDisc;
326 capData.storeImageItem.store(item.get());
327 assert(capData.storeImageItem.load());
328 capData.storeImageTask = context->
taskManager->enqueue(context->
taskManager->GlobalQueue, StoreImageTask{ capData.storeImageItem });
332 item->state = CaptureItem::State::Ready;
337 removeDroppedItems(capData.itemStore, capData.items);
338 removeScheduledForDiscard(capData.itemStore, capData.items);
341 if (10 <= capData.items.size()) {
342 if (capData.items.front()->state == CaptureItem::State::Issued) {
343 capData.items.pop_front();
346 LOG_WARNING(logger,
"10 or more capture frames in flight for CaptureComponent, skipping.");
350 auto * entity = capComp.getContainer();
351 std::unique_ptr<CaptureItem> item;
352 if (capData.itemStore.empty()) {
353 item = std::make_unique<CaptureItem>();
356 item = std::move(capData.itemStore.back());
357 capData.itemStore.pop_back();
359 item->time = context->
time->getAnimationTime();
360 item->frame = context->
time->getFrame();
361 item->state = CaptureItem::State::Issued;
362 item->mode = capComp.mode;
363 item->sceneMode = capComp.sceneMode;
364 item->format = capComp.format;
365 item->recordToDisc = capComp.recordToDisc;
366 item->recordPath = capComp.recordPath;
367 item->name = entity->getName();
368 if (item->name.empty()) {
369 item->name =
"entity_" + std::to_string(entity->getId());
371 capData.items.push_back(std::move(item));
381 auto & data = getData(comp);
382 if (data.encodePngTask.isValid()) {
385 if (data.storeImageTask.isValid()) {
ComponentType * getComponent() const
void initialize(Context *context) override
Initialize the system.
void destroyComponent(ComponentHandle component) override
Context * context
Pointer to the Context instance the system lives in.
virtual void initialize(Context *context)
Initialize the system.
virtual void destroyComponent(ComponentHandle)
Destroy the component held by the given handle.
void update()
Updates the system state to that of the current frame.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
std::unique_ptr< class TaskManager > taskManager
TaskManager service instance.
std::unique_ptr< class Time > time
Time service instance.
Log implementation class.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
@ RawFloat
Store image as a blob of raw float values (in host byte order).
@ ObjectId
Render object ids.
@ Normals
Render world-space surface normals.
@ ObjectIdColorize
Render object ids, colorized for visualization.
@ Depth
Render world-space distance from eye, scaled by 1/100 (so a value of 1 equals a distance of 100 units...
@ Scene
Render in normal scene mode,.
@ Off
Disable capture and camera, consume no rendering resources.
@ EnableRender
Renderable.
@ Forward
Normal forward rendering.
@ DeferredCausticsHdr
Deferred rendering with support for caustics and HDR post-processing.
@ ForwardHdr
Normal forward rendering with HDR post-proessing (exposure, bloom, tonemapping)
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Handle to a Component instance.
COGSFOUNDATION_API class Component * resolve() const
Resolve the handle, returning a pointer to the held Component instance.
ComponentType * resolveComponent() const
Contains data describing a Camera instance and its derived data structured such as matrix data and vi...