载入中...
搜索中...
未找到
OffscreenPreview.cpp
浏览该文件的文档.
2
3#include "graphics/Canvas.h"
6#include "image/ImageData.h"
7
8#include <cmath>
9#include <map>
10#include <unordered_map>
11
13
14namespace {
15struct CanvasTargetRestore {
16 graphics::ICanvasTarget* target;
17 graphics::Canvas* previous;
18 ~CanvasTargetRestore() { target->setCanvas(previous); }
19};
20} // namespace
21
23 struct Artifact {
24 OffscreenPreviewArtifact metadata;
25 std::unique_ptr<image::ImageData> pixels;
26 };
27 graphics::Graphics* graphics = nullptr;
28 graphics::ICanvasFactory* canvases = nullptr;
29 graphics::ICanvasTarget* targets = nullptr;
30 std::map<std::pair<int, int>, graphics::Canvas*> canvasCache;
31 std::unordered_map<std::string, Artifact> artifacts;
32 std::uint64_t sequence = 0;
33};
34
38 : impl_(std::make_unique<Impl>()) {
39 impl_->graphics = graphics;
40 impl_->canvases = canvases;
41 impl_->targets = targets;
42}
44
46 const DrawCallback& draw,
47 int maximumDimension,
48 std::uint64_t maximumPixels) {
49 if (!impl_->graphics || !impl_->canvases || !impl_->targets || request.previewId.empty() || !draw ||
50 request.sourceRevision == 0 || request.width <= 0 || request.height <= 0 || maximumDimension <= 0 ||
51 request.width > maximumDimension || request.height > maximumDimension ||
52 static_cast<std::uint64_t>(request.width) * static_cast<std::uint64_t>(request.height) > maximumPixels)
53 return eve::editing::failed<OffscreenPreviewArtifact>(
54 EditorStatus::Rejected, RuleId("editor.preview.invalid-offscreen-request"),
55 "Offscreen preview requires graphics, identity, revision and bounded dimensions");
56 for (double component : request.clearColor)
57 if (!std::isfinite(component) || component < 0.0 || component > 1.0)
58 return eve::editing::failed<OffscreenPreviewArtifact>(EditorStatus::Rejected,
59 RuleId("editor.preview.invalid-clear-color"),
60 "Offscreen clear color must be normalized");
61 const auto key = std::make_pair(request.width, request.height);
62 graphics::Canvas*& canvas = impl_->canvasCache[key];
63 if (!canvas) canvas = impl_->canvases->newCanvas(request.width, request.height);
64 if (!canvas)
65 return eve::editing::failed<OffscreenPreviewArtifact>(
66 EditorStatus::Failed, RuleId("editor.preview.canvas-allocation"),
67 "Graphics backend could not allocate an offscreen Canvas");
68 graphics::Canvas* previous = impl_->targets->getCanvas();
69 impl_->targets->setCanvas(canvas);
70 CanvasTargetRestore restore{impl_->targets, previous};
71 canvas->clear(graphics::Color(static_cast<float>(request.clearColor[0]), static_cast<float>(request.clearColor[1]),
72 static_cast<float>(request.clearColor[2]), static_cast<float>(request.clearColor[3])),
73 0, 1.0);
74 Result<void> drawn = [&]() -> Result<void> {
75 try {
76 return draw(impl_->graphics, canvas);
77 } catch (const std::exception& exception) {
78 return eve::editing::failed<void>(EditorStatus::Failed, RuleId("editor.preview.draw-exception"),
79 exception.what());
80 } catch (...) {
81 return eve::editing::failed<void>(EditorStatus::Failed, RuleId("editor.preview.draw-exception"),
82 "Offscreen preview draw callback threw an exception");
83 }
84 }();
85 if (!drawn.ok()) {
87 }
88 std::unique_ptr<image::ImageData> pixels(canvas->newImageData());
89 if (!pixels)
90 return eve::editing::failed<OffscreenPreviewArtifact>(
91 EditorStatus::Failed, RuleId("editor.preview.readback-failed"),
92 "Graphics backend could not read back the preview Canvas");
94 metadata.previewId = request.previewId;
95 metadata.sourceRevision = request.sourceRevision;
96 metadata.width = request.width;
97 metadata.height = request.height;
98 metadata.handle = "preview://" + request.previewId.value() + "/" + std::to_string(request.sourceRevision) + "/" +
99 std::to_string(++impl_->sequence);
100 impl_->artifacts.insert_or_assign(metadata.handle, Impl::Artifact{metadata, std::move(pixels)});
101 return eve::editing::applied<OffscreenPreviewArtifact>(std::move(metadata));
102}
103
105 Revision currentRevision) const {
106 const auto found = impl_->artifacts.find(handle);
107 if (found == impl_->artifacts.end())
108 return eve::editing::failed<const image::ImageData*>(EditorStatus::NotFound,
109 RuleId("editor.preview.artifact-not-found"),
110 "Offscreen preview artifact was not found");
111 if (found->second.metadata.sourceRevision != currentRevision)
112 return eve::editing::failed<const image::ImageData*>(
113 EditorStatus::Conflict, RuleId("editor.preview.stale-artifact"),
114 "Offscreen preview artifact belongs to an older source revision");
115 return eve::editing::applied<const image::ImageData*>(found->second.pixels.get());
116}
117
119 if (!impl_->artifacts.erase(handle))
120 return eve::editing::failed<void>(EditorStatus::NotFound, RuleId("editor.preview.artifact-not-found"),
121 "Offscreen preview artifact was not found");
122 return eve::editing::applied<void>();
123}
124
125} // namespace eve::graphics_editing
LogicalId target
const GltfImportRequest & request
std::uint32_t key
MeshVfxBatchedDraw draw
graphics::Canvas * previous
std::unique_ptr< gpgpu::Sequence > sequence
Definition OnnxGpgpu.cpp:43
PrimitiveHandle handle
uint8_t * pixels
bool found
Move-only operation result carrying either a value or Status.
Definition Result.h:155
bool ok() const noexcept
Whether this result represents a non-failure outcome.
Definition Result.h:255
const Status & status() const noexcept
Inspect the structured operation status.
Definition Result.h:269
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
Canvas public API.
Definition Canvas.h:17
virtual image::ImageData * newImageData()=0
Creates a image data. @ownership Caller deletes unless documented otherwise.
virtual void clear(std::optional< Color > color, std::optional< int > stencil, std::optional< double > depth)=0
Clears .
Narrow offscreen-Canvas allocation boundary for tools and optional modules.
Narrow render-target binding boundary for optional tooling modules.
Result< void > release(const std::string &handle)
Remove an in-memory readback artifact.
Result< const image::ImageData * > image(const std::string &handle, Revision currentRevision) const
Borrow pixels only when the requested source revision still matches.
~GraphicsOffscreenPreviewService()
Graphics offscreen preview service.
std::function< Result< void >(graphics::Graphics *, graphics::Canvas *)> DrawCallback
Result< OffscreenPreviewArtifact > render(const OffscreenPreviewRequest &request, const DrawCallback &draw, int maximumDimension=4096, std::uint64_t maximumPixels=16777216)
Clear, draw and read back one isolated offscreen Canvas.
GraphicsOffscreenPreviewService(graphics::Graphics *graphics, graphics::ICanvasFactory *canvases, graphics::ICanvasTarget *targets)
Bind borrowed draw, allocation and target-control interfaces.
editing::Revision Revision
eve::Color Color
RGBA color used by every graphics draw call. Lives inside eve::graphics so including a graphics heade...
Definition Color.h:13
std::map< std::pair< int, int >, graphics::Canvas * > canvasCache
std::unordered_map< std::string, Artifact > artifacts
In-memory artifact metadata returned after synchronous readback.
Immutable request for one isolated Canvas render and CPU readback.