Donner SVG 0.8.0
0.8.0 ยท ebf353960.8.0 main
SVG editor and embeddable C⁠+⁠+⁠20 engine.
Loading...
Searching...
No Matches
geode_embed.cc

Minimal native windowed Geode host.

Minimal native windowed Geode host.The host selects the GPU root against its actual GLFW window, creates a runtime surface, renders an SVG into each acquired frame, and retires the surface before the window. Metal attaches a Core Animation layer; Vulkan completes physical-device and queue selection against the exact VkSurfaceKHR it will present to. No WebGPU-C++ handle enters the product.

bazel run –config=geode //examples:geode_embed – donner_splash.svg

/**
* @example geode_embed.cc Minimal native windowed Geode host.
*
* The host selects the GPU root against its actual GLFW window, creates a runtime surface,
* renders an SVG into each acquired frame, and retires the surface before the window. Metal
* attaches a Core Animation layer; Vulkan completes physical-device and queue selection against
* the exact VkSurfaceKHR it will present to. No WebGPU-C++ handle enters the product.
*
* bazel run --config=geode //examples:geode_embed -- donner_splash.svg
*/
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <iostream>
#include <memory>
#include <optional>
#include <sstream>
#include <string>
#include <string_view>
#include <utility>
#include "donner/svg/SVG.h"
extern "C" {
#include "GLFW/glfw3.h"
}
namespace {
constexpr int kWindowWidth = 800;
constexpr int kWindowHeight = 600;
constexpr int kMaxOneFrameAttempts = 32;
void GlfwErrorCallback(int error, const char* description) {
std::fprintf(stderr, "GLFW error %d: %s\n", error, description);
}
std::string LoadFile(const char* path) {
auto result =
if (const auto* contents = std::get_if<std::string>(&result)) {
return *contents;
}
return {};
}
return std::find(caps.formats.begin(), caps.formats.end(), format) != caps.formats.end() &&
}
config.format = format;
config.size = {kWindowWidth, kWindowHeight};
config.presentMode = std::find(caps.presentModes.begin(), caps.presentModes.end(),
: caps.presentModes.front();
config.alphaMode = std::find(caps.alphaModes.begin(), caps.alphaModes.end(),
: caps.alphaModes.front();
return config;
}
std::optional<donner::svg::SVGDocument> ParseDocument(const char* svgPath) {
const std::string svgData = LoadFile(svgPath);
if (svgData.empty()) {
const std::string safePath = donner::EscapeTerminalText(svgPath);
std::fprintf(stderr, "Failed to open or empty SVG: %s\n", safePath.c_str());
return std::nullopt;
}
auto parsed = donner::svg::parser::SVGParser::ParseSVG(svgData, warnings);
if (parsed.hasError()) {
std::ostringstream diagnostic;
diagnostic << parsed.error();
std::cerr << "SVG parse error: " << donner::EscapeTerminalText(diagnostic.str()) << "\n";
return std::nullopt;
}
donner::svg::SVGDocument document = std::move(parsed.result());
document.setCanvasSize(kWindowWidth, kWindowHeight);
return document;
}
GLFWwindow* CreateWindow() {
glfwSetErrorCallback(GlfwErrorCallback);
if (!glfwInit()) {
std::fprintf(stderr, "glfwInit failed\n");
return nullptr;
}
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
GLFWwindow* window =
glfwCreateWindow(kWindowWidth, kWindowHeight, "Donner Geode Embed", nullptr, nullptr);
if (window == nullptr) {
std::fprintf(stderr, "glfwCreateWindow failed\n");
glfwTerminate();
}
return window;
}
struct EmbedSession {
explicit EmbedSession(GLFWwindow* window) : window(window) {}
GLFWwindow* window;
donner::example::NativeEmbedSurface native;
std::shared_ptr<donner::geode::GeodeDevice> context;
donner::gpu::SurfaceConfiguration config;
int finish(int code) {
//! [retire]
if (surface.isValid() && context != nullptr) {
if (donner::gpu::Status released =
context->runtimeDevice().destroySurface(std::move(surface));
released.hasError()) {
std::fprintf(stderr, "Could not retire runtime surface: %s\n",
released.error().toString().c_str());
code = 1;
}
}
context.reset();
native.root.reset();
if (donner::example::RetireNativeEmbedSurface(native, window)) {
glfwDestroyWindow(window);
glfwTerminate();
} else {
std::fprintf(stderr, "Native surface retirement is unproven; retaining its window\n");
code = 1;
}
//! [retire]
return code;
}
};
bool InitializeSession(EmbedSession& session) {
//! [select_root]
session.native = donner::example::PrepareNativeEmbedSurface(session.window);
if (session.native.root == nullptr) {
std::fprintf(stderr, "Could not select a native GPU root for this window\n");
return false;
}
//! [select_root]
//! [create_surface]
session.context = std::shared_ptr<donner::geode::GeodeDevice>(
session.native.format));
if (session.context == nullptr) {
std::fprintf(stderr, "Could not create a Geode context over the selected root\n");
return false;
}
donner::gpu::Device& device = session.context->runtimeDevice();
descriptor.label = "GeodeEmbedSurface";
descriptor.native = session.native.native;
if (created.hasError()) {
std::fprintf(stderr, "Could not create runtime surface: %s\n",
created.error().toString().c_str());
return false;
}
session.surface = std::move(created).result();
//! [create_surface]
//! [configure_surface]
device.surfaceCapabilities(session.surface);
if (capabilities.hasError() || !Supports(capabilities.result(), session.native.format) ||
capabilities.result().presentModes.empty() || capabilities.result().alphaModes.empty()) {
std::fprintf(stderr, "Window surface cannot present Geode's selected format\n");
return false;
}
session.config = Configuration(capabilities.result(), session.native.format);
if (donner::gpu::Status configured = device.configureSurface(session.surface, session.config);
configured.hasError()) {
std::fprintf(stderr, "Could not configure runtime surface: %s\n",
configured.error().toString().c_str());
return false;
}
//! [configure_surface]
return true;
}
//! [frame_status]
enum class FrameOutcome { Continue, Presented, Failed };
FrameOutcome HandleUnavailableFrame(EmbedSession& session, donner::gpu::Device& device,
if (frame.texture.isValid()) {
(void)device.abandonCurrentTexture(session.surface);
}
return device.configureSurface(session.surface, session.config).hasError()
? FrameOutcome::Failed
: FrameOutcome::Continue;
}
return FrameOutcome::Continue;
}
std::fprintf(stderr, "Native presentation surface was lost\n");
return FrameOutcome::Failed;
}
FrameOutcome PresentFrame(EmbedSession& session, donner::gpu::Device& device) {
device.presentSurface(session.surface);
if (presented.hasError() || presented.result() == donner::gpu::SurfaceStatus::DeviceLost ||
std::fprintf(stderr, "Could not present the native frame\n");
return FrameOutcome::Failed;
}
device.configureSurface(session.surface, session.config).hasError()) {
return FrameOutcome::Failed;
}
return presented.result() == donner::gpu::SurfaceStatus::Success ? FrameOutcome::Presented
: FrameOutcome::Continue;
}
//! [frame_status]
FrameOutcome DrawFrame(EmbedSession& session, donner::svg::RendererGeode& renderer,
donner::gpu::Device& device = session.context->runtimeDevice();
//! [draw_frame]
device.acquireCurrentTexture(session.surface);
if (acquired.hasError()) {
std::fprintf(stderr, "Could not acquire a native frame: %s\n",
acquired.error().toString().c_str());
return FrameOutcome::Failed;
}
donner::gpu::SurfaceTexture frame = std::move(acquired).result();
return HandleUnavailableFrame(session, device, frame);
}
renderer.setTargetTexture(frame.texture);
renderer.draw(document);
renderer.clearTargetTexture();
return PresentFrame(session, device);
//! [draw_frame]
}
int RenderFrames(EmbedSession& session, donner::svg::SVGDocument& document, bool oneFrame) {
int oneFrameAttempts = 0;
//! [create_renderer]
donner::svg::RendererGeode renderer(session.context);
//! [create_renderer]
while (!glfwWindowShouldClose(session.window)) {
if (oneFrame && ++oneFrameAttempts > kMaxOneFrameAttempts) {
std::fprintf(stderr, "One-frame smoke could not present within %d attempts\n",
kMaxOneFrameAttempts);
return 1;
}
glfwPollEvents();
switch (DrawFrame(session, renderer, document)) {
case FrameOutcome::Failed: return 1;
case FrameOutcome::Presented:
if (oneFrame) {
return 0;
}
break;
case FrameOutcome::Continue: break;
}
}
return oneFrame ? 1 : 0;
}
} // namespace
int main(int argc, char* argv[]) {
if (const char* bwd = std::getenv("BUILD_WORKING_DIRECTORY")) {
std::filesystem::current_path(bwd);
}
const bool oneFrame = argc == 3 && std::string_view(argv[1]) == "--one-frame";
if ((!oneFrame && argc != 2) || (argc == 2 && std::string_view(argv[1]) == "--one-frame")) {
std::fprintf(stderr, "USAGE: geode_embed [--one-frame] <svg-file>\n");
return 1;
}
const char* svgPath = oneFrame ? argv[2] : argv[1];
std::optional<donner::svg::SVGDocument> document = ParseDocument(svgPath);
if (!document.has_value()) {
return 1;
}
GLFWwindow* window = CreateWindow();
if (window == nullptr) {
return 1;
}
EmbedSession session(window);
if (!InitializeSession(session)) {
return session.finish(1);
}
const int result = session.finish(RenderFrames(session, *document, oneFrame));
if (oneFrame && result == 0) {
std::puts("GEODE_EMBED_PRESENTED=1");
}
return result;
}
@ Failed
The browser rejected the operation.
Definition BrowserBridge.h:50
@ Opaque
Alpha is ignored.
Definition Descriptors.h:185
@ Fifo
Queue frames and present on refresh. Always available.
Definition Descriptors.h:178
@ Success
The operation succeeded.
Definition Descriptors.h:623
@ Outdated
The configuration no longer matches the window. Reconfigure and retry.
Definition Descriptors.h:624
@ Lost
The platform object is gone. A new surface is needed.
Definition Descriptors.h:625
@ Timeout
No texture became available in time. Retry.
Definition Descriptors.h:627
@ DeviceLost
The device was lost.
Definition Descriptors.h:626
TextureFormat
Texture formats used by Donner render targets, masks, and image uploads.
Definition Descriptors.h:83
@ None
No usage; invalid for creation.
Definition Descriptors.h:92
@ RenderAttachment
Color attachment of a render pass.
Definition Descriptors.h:93
donner::gpu::Device - the abstract GPU device with shared fail-closed validation.
Logical rendering context over a selected GPU runtime device.
Result< std::monostate > Status
Status of a fallible GPU operation with no result value: either success (a std::monostate result) or ...
Definition GpuResult.h:134
Handle< SurfaceTag > Surface
Surface handle.
Definition Handles.h:283
@ Continue
Run the innermost loop continuation.
Definition Module.h:308
Geode GPU implementation of donner::svg::RendererInterface.
Collects parse warnings during parsing.
Definition ParseWarningSink.h:29
gpu::Device & runtimeDevice() const
This context's GPU runtime device: the owner of its handle tables, submission serials,...
static std::unique_ptr< GeodeDevice > CreateOverSelectedRoot(std::shared_ptr< GeodeGpuRoot > root, gpu::TextureFormat textureFormat)
Creates the first logical context over a backend root the caller already selected.
Abstract GPU device: resource creation, queue writes, and submission with shared fail-closed validati...
Definition Device.h:271
Result< SurfaceCapabilities > surfaceCapabilities(const Surface &surface) const
What surface supports, for choosing a configuration.
Result< SurfaceTexture > acquireCurrentTexture(const Surface &surface)
Acquires the texture for the next frame of surface.
Status abandonCurrentTexture(const Surface &surface)
Releases the acquired texture of surface without presenting it, for a frame the caller decided not to...
Status configureSurface(const Surface &surface, const SurfaceConfiguration &configuration)
Configures how surface presents, replacing any previous configuration.
Result< SurfaceStatus > presentSurface(const Surface &surface)
Presents the texture acquired from surface and invalidates it.
Result< Surface > createSurface(const SurfaceDescriptor &descriptor)
Creates a surface presenting to a platform object.
bool isValid() const
Returns true if this handle is non-null. The device generation check is authoritative for whether the...
Definition Handles.h:110
Result of a fallible GPU operation: exactly one of a value of type T or a GpuError.
Definition GpuResult.h:26
bool hasError() const noexcept
Returns true if this Result contains an error.
Definition GpuResult.h:123
T & result() &
Returns the contained result.
Definition GpuResult.h:64
GpuError & error() &
Returns the contained error.
Definition GpuResult.h:94
Geode rendering backend - GPU-native via the Slug algorithm.
Definition RendererGeode.h:264
void draw(SVGDocument &document) override
Renders the given SVG document.
void clearTargetTexture()
Clear a previously set target texture, reverting to internal offscreen targets allocated per-frame by...
void setTargetTexture(const gpu::Texture &texture)
Set a texture this renderer's device already names as the render target for subsequent frames.
Represents a parsed SVG document containing a tree of SVGElement nodes.
Definition SVGDocument.h:106
void setCanvasSize(int width, int height)
Set the canvas (output image) size to a fixed width and height, in pixels.
static ParseResult< SVGDocument > ParseSVG(std::string_view source, ParseWarningSink &warningSink, Options options={}, SVGDocument::Settings settings={}) noexcept
Parses an SVG XML document from a string (typically the contents of a .svg file).
static constexpr size_t kDefaultMaximumInputSize
Default maximum number of source or expanded SVG bytes accepted from untrusted input.
Definition SVGParser.h:27
Native window surface prepared before Geode's device and pipelines are created.
NativeEmbedSurface PrepareNativeEmbedSurface(GLFWwindow *window)
Prepare a native window surface and select a root that can present to it. A null root means preparati...
Definition geode_embed_surface_linux.cc:24
std::string EscapeTerminalText(std::string_view text, bool preserveNewlines=false)
Escape untrusted bytes and terminal format controls while preserving printable UTF-8.
Definition TerminalEscape.h:22
FileReadResult ReadFileBounded(const std::filesystem::path &path, size_t maximumSize)
Open and read a regular file without allocating more than maximumSize bytes.
int main()
Run the rendering example and report its result.
Definition render_test.cc:34
gpu::TextureFormat format
Chosen presentation format.
Definition geode_embed_surface.h:41
gpu::NativeSurfaceHandle native
Handle naming the host-owned platform surface for runtime attachment.
Definition geode_embed_surface.h:40
std::shared_ptr< geode::GeodeGpuRoot > root
Selected Geode root used by the rendering context.
Definition geode_embed_surface.h:38
std::string toString() const
Formats this error as <type>: <message>.
What a surface supports, for choosing a SurfaceConfiguration.
Definition Descriptors.h:608
std::vector< TextureFormat > formats
Formats acquired textures may take.
Definition Descriptors.h:609
TextureUsage usages
Usage flags acquired textures may carry.
Definition Descriptors.h:610
std::vector< PresentMode > presentModes
Supported frame pacing.
Definition Descriptors.h:611
std::vector< SurfaceAlphaMode > alphaModes
Supported alpha compositing.
Definition Descriptors.h:612
Configuration a surface presents under.
Definition Descriptors.h:599
Extent2d size
Extent in texels. Must be nonzero.
Definition Descriptors.h:602
TextureUsage usage
Usage acquired textures carry.
Definition Descriptors.h:601
SurfaceAlphaMode alphaMode
Alpha compositing.
Definition Descriptors.h:604
TextureFormat format
Format of acquired textures.
Definition Descriptors.h:600
PresentMode presentMode
Frame pacing.
Definition Descriptors.h:603
Descriptor for Device::createSurface.
Definition Descriptors.h:191
RcString label
Debug label.
Definition Descriptors.h:192
NativeSurfaceHandle native
Platform object to present to.
Definition Descriptors.h:193
A texture acquired from a surface for one frame, with the state the surface reported while handing it...
Definition Device.h:234
SurfaceStatus status
What the surface reported.
Definition Device.h:236
Texture texture
The frame's texture; null when none came.
Definition Device.h:235