| #ifdef WITH_RIVE_SCRIPTING |
| #ifndef _RIVE_LUA_LIBS_HPP_ |
| #define _RIVE_LUA_LIBS_HPP_ |
| #include "lua.h" |
| #include "lualib.h" |
| #include "rive/animation/linear_animation_instance.hpp" |
| #include "rive/assets/file_asset.hpp" |
| #include "rive/assets/script_asset.hpp" |
| #include "rive/lua/lua_state.hpp" |
| #include "rive/math/raw_path.hpp" |
| #include "rive/factory.hpp" |
| #include "rive/renderer.hpp" |
| #include "rive/math/vec2d.hpp" |
| #include "rive/math/mat4.hpp" |
| #include "rive/math/contour_measure.hpp" |
| #include "rive/math/path_measure.hpp" |
| #include "rive/shapes/paint/image_sampler.hpp" |
| #include "rive/shapes/paint/shape_paint.hpp" |
| #include "rive/viewmodel/data_enum.hpp" |
| #include "rive/viewmodel/viewmodel_instance_asset_image.hpp" |
| #include "rive/viewmodel/viewmodel_instance_boolean.hpp" |
| #include "rive/viewmodel/viewmodel_instance_color.hpp" |
| #include "rive/viewmodel/viewmodel_instance_enum.hpp" |
| #include "rive/viewmodel/viewmodel_instance_value.hpp" |
| #include "rive/viewmodel/viewmodel_instance_viewmodel.hpp" |
| #include "rive/viewmodel/viewmodel_instance_color.hpp" |
| #include "rive/viewmodel/viewmodel_instance_number.hpp" |
| #include "rive/viewmodel/viewmodel_instance_string.hpp" |
| #include "rive/viewmodel/viewmodel_instance_trigger.hpp" |
| #include "rive/viewmodel/viewmodel_instance_list.hpp" |
| #include "rive/data_bind/data_values/data_value.hpp" |
| #include "rive/data_bind/data_values/data_value_boolean.hpp" |
| #include "rive/data_bind/data_values/data_value_color.hpp" |
| #include "rive/data_bind/data_values/data_value_list.hpp" |
| #include "rive/data_bind/data_values/data_value_number.hpp" |
| #include "rive/data_bind/data_values/data_value_string.hpp" |
| #include "rive/viewmodel/viewmodel.hpp" |
| #include "rive/animation/listener_invocation.hpp" |
| #include "rive/hit_result.hpp" |
| #include "rive/lua/scripting_vm.hpp" |
| #include "rive/refcnt.hpp" |
| #ifdef WITH_RIVE_AUDIO |
| #include "rive/audio/audio_engine.hpp" |
| #include "rive/audio/audio_source.hpp" |
| #include "rive/audio/audio_sound.hpp" |
| #endif |
| #ifdef WITH_RIVE_TOOLS |
| #include "rive/core/binary_writer.hpp" |
| #include "rive/core/vector_binary_stream.hpp" |
| #endif |
| |
| #include <chrono> |
| #include <memory> |
| #include <unordered_map> |
| #include <unordered_set> |
| #include <functional> |
| #include <string> |
| #include <vector> |
| |
| static const int maxCStack = 8000; |
| static const int luaGlobalsIndex = -maxCStack - 2002; |
| static const int luaRegistryIndex = -maxCStack - 2000; |
| |
| namespace rive |
| { |
| class Artboard; |
| class ArtboardInstance; |
| class Factory; |
| class File; |
| class ModuleDetails; |
| class ScriptedObject; |
| class StateMachineInstance; |
| class TransformComponent; |
| enum class LuaAtoms : int16_t |
| { |
| // Vector |
| length, |
| lengthSquared, |
| normalized, |
| distance, |
| distanceSquared, |
| dot, |
| lerp, |
| |
| // Path |
| moveTo, |
| lineTo, |
| quadTo, |
| cubicTo, |
| close, |
| reset, |
| add, |
| contours, |
| measure, |
| |
| // Path Command |
| type, |
| points, |
| |
| // Mat2D |
| invert, |
| isIdentity, |
| |
| // Image |
| width, |
| height, |
| |
| // ImageSampler |
| clamp, |
| repeat, |
| mirror, |
| bilinear, |
| nearest, |
| |
| // Paint |
| style, |
| join, |
| cap, |
| thickness, |
| blendMode, |
| feather, |
| gradient, |
| color, |
| |
| stroke, |
| fill, |
| miter, |
| round, |
| bevel, |
| butt, |
| square, |
| srcOver, |
| screen, |
| overlay, |
| darken, |
| lighten, |
| colorDodge, |
| colorBurn, |
| hardLight, |
| softLight, |
| difference, |
| exclusion, |
| multiply, |
| hue, |
| saturation, |
| luminosity, |
| copy, |
| |
| // Renderer |
| drawPath, |
| drawImage, |
| drawImageMesh, |
| clipPath, |
| save, |
| restore, |
| transform, |
| |
| // Scripted Properties |
| value, |
| red, |
| green, |
| blue, |
| alpha, |
| getNumber, |
| getTrigger, |
| getString, |
| getBoolean, |
| getColor, |
| getList, |
| getViewModel, |
| getEnum, |
| getIndex, |
| getImage, |
| values, |
| addListener, |
| removeListener, |
| fire, |
| push, |
| insert, |
| shift, |
| pop, |
| swap, |
| clear, |
| |
| // Artboards |
| draw, |
| advance, |
| frameOrigin, |
| data, |
| instance, |
| animation, |
| newAtom, |
| bounds, |
| pointerDown, |
| pointerMove, |
| pointerUp, |
| pointerExit, |
| addToPath, |
| name, |
| |
| // Scripted DataValues |
| isNumber, |
| isString, |
| isBoolean, |
| isColor, |
| |
| // inputs |
| hit, |
| id, |
| position, |
| |
| // nodes |
| rotation, |
| scale, |
| worldTransform, |
| scaleX, |
| scaleY, |
| decompose, |
| children, |
| parent, |
| node, |
| paint, |
| asPaint, |
| asPath, |
| |
| // PathMeasure/ContourMeasure |
| positionAndTangent, |
| warp, |
| extract, |
| next, |
| isClosed, |
| |
| // Scripted Context |
| markNeedsUpdate, |
| viewModel, |
| rootViewModel, |
| image, |
| blob, |
| size, |
| dataContext, |
| audio, |
| play, |
| playAtTime, |
| playInTime, |
| playAtFrame, |
| playInFrame, |
| stop, |
| pause, |
| resume, |
| seek, |
| seekFrame, |
| volume, |
| completed, |
| time, |
| timeFrame, |
| sampleRate, |
| |
| // Animation |
| duration, |
| setTime, |
| setTimeFrames, |
| setTimePercentage, |
| |
| // PointerEvent (append to avoid shifting existing atom ids) |
| previousPosition, |
| timeStamp, |
| |
| // ScriptedInvocation / listener payloads (append only) |
| isPointerEvent, |
| isKeyboardEvent, |
| isTextInput, |
| isFocus, |
| isReportedEvent, |
| isViewModelChange, |
| isNone, |
| isGamepadConnected, |
| isGamepadEvent, |
| isGamepadDisconnected, |
| asPointerEvent, |
| asKeyboardEvent, |
| asTextInput, |
| asFocus, |
| asReportedEvent, |
| asViewModelChange, |
| asGamepadConnected, |
| asGamepadEvent, |
| asGamepadDisconnected, |
| gamepadEvent, |
| gamepadConnected, |
| gamepadDisconnected, |
| asNone, |
| key, |
| alt, |
| control, |
| meta, |
| text, |
| phase, |
| delaySeconds, |
| deviceId, |
| buttonMask, |
| remove, |
| removeAt, |
| removeAllOf, |
| |
| // GPU bindings |
| write, |
| upload, |
| view, |
| setPipeline, |
| setVertexBuffer, |
| setIndexBuffer, |
| setBindGroup, |
| setViewport, |
| setScissorRect, |
| setStencilReference, |
| setBlendColor, |
| drawIndexed, |
| finish, |
| beginRenderPass, |
| beginFrame, |
| endFrame, |
| colorView, |
| depthView, |
| resize, |
| canvas, |
| gpuCanvas, |
| drawCanvas, |
| features, |
| shader, |
| format, |
| |
| // Promise |
| andThen, |
| catch_, |
| finally_, |
| cancel, |
| onCancel, |
| getStatus, |
| |
| // Image decode |
| decodeImage, |
| |
| // Mat4 |
| transpose, |
| transformPoint, |
| transformVec4, |
| writeToBuffer, |
| invertAffine, |
| |
| // Gamepad |
| axes, |
| gamepadMapping, |
| mapping, |
| isStandardMapping, |
| buttons, |
| buttonPressed, |
| buttonValue, |
| axis, |
| west, |
| south, |
| north, |
| east, |
| leftShoulder, |
| rightShoulder, |
| gamepadBack, |
| gamepadForward, |
| leftStickButton, |
| rightStickButton, |
| dpadUp, |
| dpadDown, |
| dpadLeft, |
| dpadRight, |
| leftStick, |
| rightStick, |
| start, |
| leftTrigger, |
| rightTrigger, |
| leftTriggerPressed, |
| rightTriggerPressed, |
| changeKind, |
| changeIndex, |
| changeValue, |
| hasStandardButtonIntent, |
| hasStandardAxisIntent, |
| intentButton, |
| intentAxis, |
| }; |
| |
| struct ScriptedMat2D |
| { |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 1; |
| static constexpr const char* luaName = "Mat2D"; |
| static constexpr bool hasMetatable = true; |
| |
| ScriptedMat2D(float x1, float y1, float x2, float y2, float tx, float ty) : |
| value(x1, y1, x2, y2, tx, ty) |
| {} |
| |
| ScriptedMat2D(const Mat2D& mat) : value(mat) {} |
| |
| ScriptedMat2D() {} |
| |
| rive::Mat2D value; |
| }; |
| |
| static_assert(std::is_trivially_destructible<ScriptedMat2D>::value, |
| "ScriptedMat2D must be trivially destructible"); |
| |
| struct ScriptedMat4 |
| { |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 62; |
| static constexpr const char* luaName = "Mat4"; |
| static constexpr bool hasMetatable = true; |
| |
| ScriptedMat4() {} |
| ScriptedMat4(const Mat4& mat) : value(mat) {} |
| |
| rive::Mat4 value; |
| }; |
| |
| static_assert(std::is_trivially_destructible<ScriptedMat4>::value, |
| "ScriptedMat4 must be trivially destructible"); |
| |
| class ScriptedPathCommand |
| { |
| public: |
| ScriptedPathCommand(std::string type, std::vector<Vec2D> points = {}) : |
| m_type(type), m_points(points) |
| {} |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 29; |
| static constexpr const char* luaName = "PathCommand"; |
| static constexpr bool hasMetatable = true; |
| std::string type() { return m_type; } |
| std::vector<Vec2D> points() { return m_points; } |
| |
| private: |
| std::string m_type; |
| std::vector<Vec2D> m_points; |
| }; |
| |
| class ScriptedPathData |
| { |
| public: |
| ScriptedPathData() {} |
| ScriptedPathData(const RawPath* path); |
| int totalCommands(); |
| void markDirty() { m_isRenderPathDirty = true; } |
| RawPath rawPath; |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 30; |
| static constexpr const char* luaName = "PathData"; |
| static constexpr bool hasMetatable = true; |
| RenderPath* renderPath(lua_State* L); |
| |
| protected: |
| rcp<RenderPath> m_renderPath; |
| |
| bool m_isRenderPathDirty = true; |
| |
| private: |
| uint64_t m_renderFrameId = 0; |
| }; |
| |
| class ScriptedPath : public ScriptedPathData |
| { |
| public: |
| ScriptedPath() {} |
| ScriptedPath(const RawPath* path) : ScriptedPathData(path) {} |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 2; |
| static constexpr const char* luaName = "Path"; |
| static constexpr bool hasMetatable = true; |
| }; |
| |
| class ScriptedGradient |
| { |
| public: |
| rcp<RenderShader> shader; |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 3; |
| static constexpr const char* luaName = "Gradient"; |
| static constexpr bool hasMetatable = false; |
| }; |
| |
| class ScriptedVertexBuffer |
| { |
| public: |
| std::vector<Vec2D> values; |
| rcp<RenderBuffer> vertexBuffer; |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 4; |
| static constexpr const char* luaName = "VertexBuffer"; |
| static constexpr bool hasMetatable = true; |
| |
| void update(Factory* factory); |
| }; |
| |
| class ScriptedTriangleBuffer |
| { |
| public: |
| std::vector<uint16_t> values; |
| rcp<RenderBuffer> indexBuffer; |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 5; |
| static constexpr const char* luaName = "TriangleBuffer"; |
| static constexpr bool hasMetatable = true; |
| |
| uint16_t max = 0; |
| void update(Factory* factory); |
| }; |
| |
| #if defined(RIVE_CANVAS) && defined(RIVE_ORE) |
| namespace ore |
| { |
| class TextureView; |
| } |
| #endif |
| |
| class ScriptedImage |
| { |
| public: |
| rcp<RenderImage> image; |
| #if defined(RIVE_CANVAS) && defined(RIVE_ORE) |
| rcp<ore::TextureView> cachedOreView; // Cached for Image:view() to avoid |
| // leaking D3D12 CPU descriptors. |
| #if defined(ORE_BACKEND_GL) |
| // GL only: when the source image is a Rive 2D RenderCanvas, the GL |
| // backend's getCanvasImportMirror returns a Y-flipped companion |
| // image. We hold a strong rcp here so the companion stays alive for |
| // the lifetime of this ScriptedImage. The view in cachedOreView wraps |
| // the companion's texture, not the source's. |
| rcp<RenderImage> cachedMirrorImage; |
| #endif |
| #endif |
| // Out-of-line destructor — when ore is enabled, defined in lua_gpu.cpp |
| // where ore::TextureView is complete. Otherwise defined in lua_image.cpp. |
| ~ScriptedImage(); |
| // Out-of-line factory — avoids instantiating rcp<TextureView>::~rcp() in |
| // TUs that don't include the full ore headers (placement new requires a |
| // visible destructor for exception cleanup). |
| static ScriptedImage* luaNew(lua_State* L); |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 6; |
| static constexpr const char* luaName = "Image"; |
| static constexpr bool hasMetatable = true; |
| }; |
| |
| class ScriptedBlob |
| { |
| public: |
| rcp<FileAsset> asset; // Holds ref to keep BlobAsset alive |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 35; |
| static constexpr const char* luaName = "Blob"; |
| static constexpr bool hasMetatable = true; |
| }; |
| |
| #ifdef WITH_RIVE_AUDIO |
| |
| class ScriptedAudio |
| { |
| public: |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 40; |
| static constexpr const char* luaName = "Audio"; |
| static constexpr bool hasMetatable = true; |
| }; |
| |
| class ScriptedAudioSource |
| { |
| public: |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 38; |
| static constexpr const char* luaName = "AudioSource"; |
| static constexpr bool hasMetatable = true; |
| void source(rcp<AudioSource>); |
| rcp<AudioSource> source() { return m_source; } |
| int play(lua_State*, AudioEngine*); |
| int play(lua_State*, AudioEngine*, double, bool); |
| int playFrame(lua_State*, AudioEngine*); |
| int playFrame(lua_State*, AudioEngine*, uint64_t, bool); |
| |
| private: |
| rcp<AudioSource> m_source; |
| int initializeSound(lua_State*, rcp<AudioSound>, Artboard*); |
| }; |
| |
| class ScriptedAudioSound |
| { |
| public: |
| ScriptedAudioSound(Artboard* artboard) : m_artboard(artboard) {} |
| rcp<AudioSound> sound; |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 39; |
| static constexpr const char* luaName = "AudioSound"; |
| static constexpr bool hasMetatable = true; |
| Artboard* artboard() { return m_artboard; } |
| |
| private: |
| Artboard* m_artboard = nullptr; |
| }; |
| #endif |
| |
| #ifdef RIVE_CANVAS |
| namespace gpu |
| { |
| class RenderCanvas; |
| class RenderContext; |
| } // namespace gpu |
| |
| // Forward-declare RiveRenderer so canvas handles can store a raw pointer to the |
| // active frame renderer without pulling in the full rive_renderer.hpp header. |
| class RiveRenderer; |
| |
| #ifdef RIVE_ORE |
| namespace ore |
| { |
| class Buffer; |
| class Texture; |
| class TextureView; |
| class Sampler; |
| class BindGroup; |
| class BindGroupLayout; |
| class ShaderModule; |
| class Pipeline; |
| class RenderPass; |
| class Context; |
| } // namespace ore |
| |
| class ScriptedGPUBuffer |
| { |
| public: |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 41; |
| static constexpr const char* luaName = "GPUBuffer"; |
| static constexpr bool hasMetatable = true; |
| rcp<ore::Buffer> buffer; |
| // Set when constructed with `immutable=true`. The Lua wrapper rejects |
| // `:write` calls on immutable buffers — matching `BufferDesc::immutable`'s |
| // documented contract ("no update() calls allowed after creation"). |
| bool immutable = false; |
| }; |
| |
| class ScriptedGPUTexture |
| { |
| public: |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 42; |
| static constexpr const char* luaName = "GPUTexture"; |
| static constexpr bool hasMetatable = true; |
| rcp<ore::Texture> texture; |
| }; |
| |
| class ScriptedGPUSampler |
| { |
| public: |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 43; |
| static constexpr const char* luaName = "GPUSampler"; |
| static constexpr bool hasMetatable = false; |
| rcp<ore::Sampler> sampler; |
| }; |
| |
| // One @vertex/@fragment entry point resolved from an RSTB v4 container. For |
| // whole-module targets (WGSL/MSL/SPIR-V) every record of a shader shares one |
| // ShaderModule; for per-entry targets (GLSL/HLSL) each record owns its module. |
| struct ScriptedShaderEntry |
| { |
| uint8_t stage = 0; // 0=vertex 1=fragment 2=compute |
| std::string logical; // WGSL entry name a script matches against |
| std::string physical; // name handed to the driver (PipelineDesc entry) |
| rcp<ore::ShaderModule> module; |
| }; |
| |
| class ScriptedShader |
| { |
| public: |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 44; |
| static constexpr const char* luaName = "Shader"; |
| static constexpr bool hasMetatable = false; |
| |
| // Entry points in naga declaration order; the first of each stage is the |
| // WebGPU "no entryPoint" default. |
| std::vector<ScriptedShaderEntry> entries; |
| |
| bool hasModule() const { return !entries.empty(); } |
| |
| // First entry of a stage, or null. |
| const ScriptedShaderEntry* firstOfStage(uint8_t stage) const |
| { |
| for (const auto& e : entries) |
| { |
| if (e.stage == stage) |
| return &e; |
| } |
| return nullptr; |
| } |
| |
| // Resolve a stage's entry by optional WGSL name; null/empty selects the |
| // first entry of that stage (WebGPU default). Returns null if a named |
| // entry is not found. |
| const ScriptedShaderEntry* resolveEntry(uint8_t stage, |
| const char* logical) const |
| { |
| if (logical == nullptr || logical[0] == '\0') |
| return firstOfStage(stage); |
| for (const auto& e : entries) |
| { |
| if (e.stage == stage && e.logical == logical) |
| return &e; |
| } |
| return nullptr; |
| } |
| |
| // Back-compat: first vertex / first fragment module (binding-map reads, |
| // texture-sampler pair propagation). Fragment falls back to the combined |
| // (vertex) module when there is no separate fragment entry. |
| ore::ShaderModule* vertexMod() const |
| { |
| const auto* e = firstOfStage(0); |
| return e ? e->module.get() : nullptr; |
| } |
| ore::ShaderModule* fragmentMod() const |
| { |
| const auto* f = firstOfStage(1); |
| if (f) |
| return f->module.get(); |
| const auto* v = firstOfStage(0); |
| return v ? v->module.get() : nullptr; |
| } |
| }; |
| |
| class ScriptedGPUPipeline |
| { |
| public: |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 45; |
| static constexpr const char* luaName = "GPUPipeline"; |
| static constexpr bool hasMetatable = true; |
| rcp<ore::Pipeline> pipeline; |
| uint32_t sampleCount = 1; |
| |
| // Deep-copied vertex layout data so Pipeline's PipelineDesc pointers |
| // remain valid for the lifetime of this object. Stored as raw bytes to |
| // avoid pulling ore_types.hpp into this header. |
| std::vector<uint8_t> ownedVertexLayoutData; |
| |
| // Auto-derived layouts (one per @group(N) in the shader) when the user |
| // omits `bindGroupLayouts`. Exposed via `pipeline:getBindGroupLayout(N)`. |
| // Empty when explicit layouts were supplied. |
| std::vector<rcp<ore::BindGroupLayout>> autoBindGroupLayouts; |
| }; |
| |
| class ScriptedGPUBindGroup |
| { |
| public: |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 52; |
| static constexpr const char* luaName = "GPUBindGroup"; |
| static constexpr bool hasMetatable = false; |
| ~ScriptedGPUBindGroup(); // Defers destruction via |
| // Context::deferBindGroupDestroy. |
| rcp<ore::BindGroup> bindGroup; |
| }; |
| |
| class ScriptedGPUBindGroupLayout |
| { |
| public: |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 60; |
| static constexpr const char* luaName = "GPUBindGroupLayout"; |
| static constexpr bool hasMetatable = false; |
| rcp<ore::BindGroupLayout> layout; |
| }; |
| |
| class ScriptedGPURenderPass |
| { |
| public: |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 46; |
| static constexpr const char* luaName = "GPURenderPass"; |
| static constexpr bool hasMetatable = true; |
| // Out-of-line: unique_ptr<ore::RenderPass> needs the complete type at |
| // destructor instantiation, and we clear the context's active-pass |
| // slot if the wrapper is GC'd without :finish() so a stale pointer |
| // doesn't survive into the next beginRenderPass. |
| ~ScriptedGPURenderPass(); |
| std::unique_ptr<ore::RenderPass> pass; |
| // Borrowed; Context outlives every wrapper. Used by ~dtor to clear |
| // the active-pass slot if it still points at our pass. |
| ore::Context* m_context = nullptr; |
| bool m_finished = false; |
| bool m_pipelineSet = false; |
| uint32_t sampleCount = 1; // for pipeline sampleCount validation |
| std::string label; |
| uint32_t drawCallCount = 0; |
| }; |
| |
| class ScriptedGPUTextureView |
| { |
| public: |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 51; |
| static constexpr const char* luaName = "GPUTextureView"; |
| static constexpr bool hasMetatable = false; |
| rcp<ore::TextureView> view; |
| // When created from Image:view(), retains the RenderImage so the |
| // underlying gpu::Texture stays alive even if the Image is GC'd. |
| rcp<RenderImage> retainedImage; |
| }; |
| |
| class ScriptedGPUCanvas |
| { |
| public: |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 47; |
| static constexpr const char* luaName = "GPUCanvas"; |
| static constexpr bool hasMetatable = true; |
| ~ScriptedGPUCanvas(); |
| rcp<gpu::RenderCanvas> canvas; |
| // 1× presentation target. Same role as a WebGPU surface texture: |
| // single-sampled, format determined by the platform. MSAA color + |
| // depth are user-allocated and passed in via the RenderPassDesc. |
| rcp<ore::TextureView> oreColorView; |
| lua_State* m_L = nullptr; |
| int m_imageRef = LUA_NOREF; |
| gpu::RenderContext* renderCtx = nullptr; // needed for resize() |
| }; |
| |
| #endif // RIVE_ORE |
| |
| enum class CanvasState |
| { |
| Idle, |
| Rendering |
| }; |
| |
| class ScriptedCanvas |
| { |
| public: |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 50; |
| static constexpr const char* luaName = "Canvas"; |
| static constexpr bool hasMetatable = true; |
| ~ScriptedCanvas(); |
| rcp<gpu::RenderCanvas> canvas; |
| lua_State* m_L = nullptr; |
| int m_imageRef = LUA_NOREF; |
| gpu::RenderContext* renderCtx = nullptr; |
| CanvasState m_state = CanvasState::Idle; |
| // Allocated on beginFrame(), deleted on endFrame(). Wraps renderCtx. |
| RiveRenderer* m_riveRenderer = nullptr; |
| // Lua registry ref to the ScriptedRenderer pushed by beginFrame(), |
| // kept alive until endFrame() so the Lua renderer stays valid. |
| int m_rendererRef = LUA_NOREF; |
| }; |
| #endif // RIVE_CANVAS |
| |
| // ── Promise ──────────────────────────────────────────────────────────────── |
| |
| enum class PromiseState : uint8_t |
| { |
| Pending, |
| Fulfilled, |
| Rejected, |
| Cancelled |
| }; |
| |
| class ScriptedPromise |
| { |
| public: |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 53; |
| static constexpr const char* luaName = "Promise"; |
| static constexpr bool hasMetatable = true; |
| |
| ScriptedPromise(lua_State* mainThread) : m_state(mainThread) {} |
| ~ScriptedPromise(); |
| |
| void resolve(lua_State* L, int valueIdx); |
| // Overload with selfRef: a registry ref to this promise's userdata, |
| // needed for Promise/A+ adoption of pending inner promises. |
| void resolve(lua_State* L, int valueIdx, int selfRef); |
| void reject(lua_State* L, int errorIdx); |
| void cancel(lua_State* L); |
| |
| bool isFulfilled() const |
| { |
| return m_promiseState == PromiseState::Fulfilled; |
| } |
| bool isRejected() const { return m_promiseState == PromiseState::Rejected; } |
| bool isCancelled() const |
| { |
| return m_promiseState == PromiseState::Cancelled; |
| } |
| bool isPending() const { return m_promiseState == PromiseState::Pending; } |
| int resultRef() const { return m_resultRef; } |
| |
| lua_State* m_state; |
| PromiseState m_promiseState = PromiseState::Pending; |
| int m_resultRef = LUA_NOREF; |
| |
| struct ThenCallback |
| { |
| int successRef = LUA_NOREF; |
| int failureRef = LUA_NOREF; |
| int chainedPromiseRef = LUA_NOREF; // registry ref keeps it alive |
| int cancelRef = |
| LUA_NOREF; // cleanup callback fired on cancel (async/await) |
| }; |
| struct FinallyCallback |
| { |
| int callbackRef = LUA_NOREF; |
| int chainedPromiseRef = LUA_NOREF; // registry ref keeps it alive |
| }; |
| |
| std::vector<ThenCallback> m_thenCallbacks; |
| std::vector<FinallyCallback> m_finallyCallbacks; |
| |
| // Parent promise (the promise this was chained FROM via |
| // andThen/catch/finally). |
| int m_parentRef = LUA_NOREF; |
| |
| // Consumer promises (chained FROM this one). Separate refs from |
| // ThenCallback.chainedPromiseRef for independent cancel propagation. |
| std::vector<int> m_consumerRefs; |
| |
| // Cancellation hook: a Lua function called when cancel() fires. |
| // Used by decodeImage to cancel in-flight work, and by Promise.all |
| // to cancel chained promises. |
| int m_onCancelRef = LUA_NOREF; |
| }; |
| |
| int luaopen_rive_promise(lua_State* L); |
| |
| // ── ImageSampler ─────────────────────────────────────────────────────────── |
| |
| class ScriptedImageSampler |
| { |
| public: |
| ImageSampler sampler; |
| |
| ScriptedImageSampler(ImageWrap wx, ImageWrap wy, ImageFilter f) |
| { |
| sampler.wrapX = wx; |
| sampler.wrapY = wy; |
| sampler.filter = f; |
| } |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 7; |
| static constexpr const char* luaName = "ImageSampler"; |
| static constexpr bool hasMetatable = false; |
| }; |
| |
| class ScriptedPaintData |
| { |
| public: |
| ScriptedPaintData(); |
| ScriptedPaintData(const ShapePaint* shapePaint); |
| virtual ~ScriptedPaintData() = default; |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 33; |
| static constexpr const char* luaName = "PaintData"; |
| static constexpr bool hasMetatable = true; |
| |
| virtual void style(RenderPaintStyle style) { m_style = style; } |
| |
| virtual void color(ColorInt value) { m_color = value; } |
| virtual void thickness(float value) { m_thickness = value; } |
| |
| virtual void join(StrokeJoin value) { m_join = value; } |
| |
| virtual void cap(StrokeCap value) { m_cap = value; } |
| |
| virtual void feather(float value) { m_feather = value; } |
| |
| virtual void blendMode(BlendMode value) { m_blendMode = value; } |
| |
| virtual void gradient(rcp<RenderShader> value) { m_gradient = value; } |
| |
| void pushStyle(lua_State* L); |
| void pushJoin(lua_State* L); |
| void pushCap(lua_State* L); |
| void pushThickness(lua_State* L); |
| void pushBlendMode(lua_State* L); |
| void pushFeather(lua_State* L); |
| void pushGradient(lua_State* L); |
| void pushColor(lua_State* L); |
| |
| float thickness() const { return m_thickness; } |
| float feather() const { return m_feather; } |
| ColorInt color() const { return m_color; } |
| |
| protected: |
| RenderPaintStyle m_style = RenderPaintStyle::fill; |
| rcp<RenderShader> m_gradient; |
| float m_thickness = 1; |
| StrokeJoin m_join = StrokeJoin::miter; |
| StrokeCap m_cap = StrokeCap::butt; |
| float m_feather = 0; |
| BlendMode m_blendMode = BlendMode::srcOver; |
| ColorInt m_color = 0xFF000000; |
| }; |
| |
| class ScriptedPaint : public ScriptedPaintData |
| { |
| public: |
| ScriptedPaint(Factory* factory); |
| ScriptedPaint(Factory* factory, const ScriptedPaint& source); |
| virtual ~ScriptedPaint() = default; |
| rcp<RenderPaint> renderPaint; |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 8; |
| static constexpr const char* luaName = "Paint"; |
| static constexpr bool hasMetatable = true; |
| |
| void style(RenderPaintStyle style) override |
| { |
| ScriptedPaintData::style(style); |
| renderPaint->style(style); |
| } |
| |
| void color(ColorInt value) override |
| { |
| ScriptedPaintData::color(value); |
| renderPaint->color(value); |
| } |
| void thickness(float value) override |
| { |
| ScriptedPaintData::thickness(value); |
| renderPaint->thickness(value); |
| } |
| |
| void join(StrokeJoin value) override |
| { |
| ScriptedPaintData::join(value); |
| renderPaint->join(value); |
| } |
| |
| void cap(StrokeCap value) override |
| { |
| ScriptedPaintData::cap(value); |
| renderPaint->cap(value); |
| } |
| |
| void feather(float value) override |
| { |
| ScriptedPaintData::feather(value); |
| renderPaint->feather(value); |
| } |
| |
| void blendMode(BlendMode value) override |
| { |
| ScriptedPaintData::blendMode(value); |
| renderPaint->blendMode(value); |
| } |
| |
| void gradient(rcp<RenderShader> value) override |
| { |
| ScriptedPaintData::gradient(value); |
| renderPaint->shader(value); |
| } |
| }; |
| |
| class ScriptedRenderer |
| { |
| public: |
| ScriptedRenderer(Renderer* renderer) : m_renderer(renderer), m_saveCount(0) |
| {} |
| |
| // End usage of this ScriptedRenderer. |
| bool end(); |
| |
| void save(lua_State* L); |
| void restore(lua_State* L); |
| void transform(lua_State* L, const Mat2D& mat2d); |
| void clipPath(lua_State* L, ScriptedPathData* path); |
| Renderer* validate(lua_State* L); |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 9; |
| static constexpr const char* luaName = "Renderer"; |
| static constexpr bool hasMetatable = true; |
| |
| private: |
| // Not owned by the ScriptedRenderer, only valid when passed in. |
| Renderer* m_renderer = nullptr; |
| uint32_t m_saveCount = 0; |
| }; |
| |
| class ScriptReffedArtboard : public RefCnt<ScriptReffedArtboard> |
| { |
| public: |
| ScriptReffedArtboard(File* file, |
| std::unique_ptr<ArtboardInstance>&& artboardInstance, |
| rcp<ViewModelInstance> viewModelInstance, |
| rcp<DataContext> parentDataContext); |
| |
| ~ScriptReffedArtboard(); |
| rive::File* file(); |
| Artboard* artboard(); |
| StateMachineInstance* stateMachine(); |
| rcp<ViewModelInstance> viewModelInstance() { return m_viewModelInstance; } |
| |
| private: |
| File* m_file; |
| std::unique_ptr<ArtboardInstance> m_artboard; |
| std::unique_ptr<StateMachineInstance> m_stateMachine; |
| rcp<ViewModelInstance> m_viewModelInstance; |
| }; |
| |
| class ScriptedArtboard |
| { |
| public: |
| ScriptedArtboard(lua_State* L, |
| File* file, |
| std::unique_ptr<ArtboardInstance>&& artboardInstance, |
| rcp<ViewModelInstance> viewModelInstance, |
| rcp<DataContext> dataContext); |
| ~ScriptedArtboard(); |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 10; |
| static constexpr const char* luaName = "Artboard"; |
| static constexpr bool hasMetatable = true; |
| |
| Artboard* artboard() { return m_scriptReffedArtboard->artboard(); } |
| StateMachineInstance* stateMachine() |
| { |
| return m_scriptReffedArtboard->stateMachine(); |
| } |
| rcp<ViewModelInstance> viewModelInstance() |
| { |
| return m_scriptReffedArtboard->viewModelInstance(); |
| } |
| |
| rcp<ScriptReffedArtboard> scriptReffedArtboard() |
| { |
| return m_scriptReffedArtboard; |
| } |
| |
| int pushData(lua_State* L); |
| int instance(lua_State* L, rcp<ViewModelInstance> viewModelInstance); |
| int animation(lua_State* L, const char* animationName); |
| |
| bool advance(float seconds); |
| |
| void cleanupDataRef(lua_State* L); |
| |
| private: |
| lua_State* m_state = nullptr; |
| rcp<ScriptReffedArtboard> m_scriptReffedArtboard = nullptr; |
| rcp<DataContext> m_dataContext = nullptr; |
| int m_dataRef = 0; |
| }; |
| |
| class ScriptedAnimation |
| { |
| public: |
| ScriptedAnimation(lua_State* L, |
| std::unique_ptr<LinearAnimationInstance> animation); |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 32; |
| static constexpr const char* luaName = "Animation"; |
| static constexpr bool hasMetatable = true; |
| float duration(); |
| int advance(); |
| int setTime(std::string mode); |
| |
| private: |
| lua_State* m_state; |
| std::unique_ptr<LinearAnimationInstance> m_animation; |
| }; |
| |
| // Each listener keeps a registry ref to the property userdata (self) so Luau |
| // cannot collect ScriptedProperty while callbacks are registered. Released in |
| // clearListeners / removeListener alongside function and optional userdata |
| // refs. |
| struct ScriptedListener |
| { |
| int function; |
| int userdata; |
| int propertySelfRef; |
| }; |
| |
| class ScriptedProperty : public ViewModelInstanceValueDelegate |
| { |
| public: |
| ScriptedProperty(lua_State* L, rcp<ViewModelInstanceValue> value); |
| virtual ~ScriptedProperty(); |
| int addListener(); |
| int removeListener(); |
| void clearListeners(); |
| virtual void dispose(); |
| |
| void valueChanged() override; |
| |
| const lua_State* state() const { return m_state; } |
| |
| ViewModelInstanceValue* instanceValue() { return m_instanceValue.get(); } |
| ScriptedObject* owner() const { return m_owner; } |
| |
| private: |
| std::vector<ScriptedListener> m_listeners; |
| ScriptedObject* m_owner = nullptr; |
| #ifdef WITH_RIVE_TOOLS |
| ScriptingContext* m_orphanContext = nullptr; |
| #endif |
| bool m_disposed = false; |
| |
| protected: |
| lua_State* m_state; |
| rcp<ViewModelInstanceValue> m_instanceValue; |
| }; |
| |
| class ScriptedViewModel |
| { |
| public: |
| ScriptedViewModel(lua_State* L, |
| rcp<ViewModel> viewModel, |
| rcp<ViewModelInstance> viewModelInstance); |
| ~ScriptedViewModel(); |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 11; |
| static constexpr const char* luaName = "ViewModel"; |
| static constexpr bool hasMetatable = true; |
| int pushValue(const char* name, int coreType = 0); |
| int pushIndex(); |
| int instance(lua_State* L); |
| |
| const lua_State* state() const { return m_state; } |
| rcp<ViewModelInstance> viewModelInstance() const |
| { |
| return m_viewModelInstance; |
| } |
| rcp<ViewModelInstance> mutableViewModelInstance() |
| { |
| return m_viewModelInstance; |
| } |
| |
| private: |
| lua_State* m_state; |
| rcp<ViewModel> m_viewModel; |
| rcp<ViewModelInstance> m_viewModelInstance; |
| std::unordered_map<std::string, int> m_propertyRefs; |
| }; |
| |
| class ScriptedPropertyViewModel : public ScriptedProperty, |
| public ViewModelValueDependent |
| { |
| public: |
| ScriptedPropertyViewModel(lua_State* L, |
| rcp<ViewModel> viewModel, |
| rcp<ViewModelInstanceViewModel> value); |
| ~ScriptedPropertyViewModel(); |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 12; |
| static constexpr const char* luaName = "PropertyViewModel"; |
| static constexpr bool hasMetatable = true; |
| int pushValue(); |
| void setValue(ScriptedViewModel*); |
| void dispose() override; |
| void relinkDataBind() override; |
| void addDirt(ComponentDirt value, bool recurse) override {} |
| void clearRef(); |
| |
| private: |
| rcp<ViewModel> m_viewModel; |
| int m_valueRef = 0; |
| }; |
| |
| class ScriptedPropertyNumber : public ScriptedProperty |
| { |
| public: |
| ScriptedPropertyNumber(lua_State* L, rcp<ViewModelInstanceNumber> value); |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 13; |
| static constexpr const char* luaName = "Property<number>"; |
| static constexpr bool hasMetatable = true; |
| |
| int pushValue(); |
| void setValue(float value); |
| }; |
| |
| class ScriptedPropertyTrigger : public ScriptedProperty |
| { |
| public: |
| ScriptedPropertyTrigger(lua_State* L, rcp<ViewModelInstanceTrigger> value); |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 14; |
| static constexpr const char* luaName = "PropertyTrigger"; |
| static constexpr bool hasMetatable = true; |
| }; |
| |
| class ScriptedPropertyList : public ScriptedProperty |
| { |
| public: |
| ScriptedPropertyList(lua_State* L, rcp<ViewModelInstanceList> value); |
| ~ScriptedPropertyList(); |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 15; |
| static constexpr const char* luaName = "PropertyList"; |
| static constexpr bool hasMetatable = true; |
| |
| int pushLength(); |
| int pushValue(int index); |
| void valueChanged() override; |
| void append(ViewModelInstance*); |
| |
| private: |
| bool m_changed = false; |
| std::unordered_map<ViewModelInstance*, int> m_propertyRefs; |
| }; |
| |
| class ScriptedPropertyColor : public ScriptedProperty |
| { |
| public: |
| ScriptedPropertyColor(lua_State* L, rcp<ViewModelInstanceColor> value); |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 16; |
| static constexpr const char* luaName = "PropertyColor"; |
| static constexpr bool hasMetatable = true; |
| |
| int pushValue(); |
| void setValue(unsigned value); |
| }; |
| |
| class ScriptedPropertyString : public ScriptedProperty |
| { |
| public: |
| ScriptedPropertyString(lua_State* L, rcp<ViewModelInstanceString> value); |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 17; |
| static constexpr const char* luaName = "PropertyString"; |
| static constexpr bool hasMetatable = true; |
| |
| int pushValue(); |
| void setValue(const std::string& value); |
| }; |
| |
| class ScriptedPropertyBoolean : public ScriptedProperty |
| { |
| public: |
| ScriptedPropertyBoolean(lua_State* L, rcp<ViewModelInstanceBoolean> value); |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 18; |
| static constexpr const char* luaName = "Property<bool>"; |
| static constexpr bool hasMetatable = true; |
| |
| int pushValue(); |
| void setValue(bool value); |
| }; |
| |
| class ScriptedEnumValues |
| { |
| public: |
| ScriptedEnumValues(lua_State* L, DataEnum* value) : |
| m_state(L), m_dataEnum(value) |
| {} |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 34; |
| static constexpr const char* luaName = "EnumValues"; |
| static constexpr bool hasMetatable = true; |
| void dataEnum(DataEnum* value) { m_dataEnum = value; } |
| int pushValue(int index); |
| int pushLength(); |
| |
| const lua_State* state() const { return m_state; } |
| |
| private: |
| lua_State* m_state = nullptr; |
| DataEnum* m_dataEnum = nullptr; |
| }; |
| |
| class ScriptedPropertyEnum : public ScriptedProperty |
| { |
| public: |
| ScriptedPropertyEnum(lua_State* L, rcp<ViewModelInstanceEnum> value); |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 19; |
| static constexpr const char* luaName = "Property<enum>"; |
| static constexpr bool hasMetatable = true; |
| |
| int pushValue(); |
| void setValue(const std::string& value); |
| }; |
| |
| class ScriptedPropertyImage : public ScriptedProperty |
| { |
| public: |
| ScriptedPropertyImage(lua_State* L, rcp<ViewModelInstanceAssetImage> value); |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 49; |
| static constexpr const char* luaName = "Property<Image>"; |
| static constexpr bool hasMetatable = true; |
| |
| int pushValue(); |
| void setValue(ScriptedImage* scriptedImage); |
| }; |
| |
| // Make |
| // ScriptedPropertyViewModel |
| // - Nullable ViewModelInstanceValue (ViewModelInstanceViewModel) |
| // - Requires ViewModel to know which properties to expect |
| // ScriptedPropertyArtboard |
| // - Nullable ViewModelInstanceValue (ViewModelInstanceArtboard) |
| |
| // Make renderer: return lua_newrive<ScriptedRenderer>(L, renderer); |
| template <class T, class... Args> |
| static T* lua_newrive(lua_State* L, Args&&... args) |
| { |
| if (T::hasMetatable) |
| { |
| return new (lua_newuserdatataggedwithmetatable(L, sizeof(T), T::luaTag)) |
| T(std::forward<Args>(args)...); |
| } |
| else |
| { |
| return new (lua_newuserdatatagged(L, sizeof(T), T::luaTag)) |
| T(std::forward<Args>(args)...); |
| } |
| } |
| |
| BlendMode lua_toblendmode(lua_State* L, int idx); |
| |
| template <typename T> |
| static T* lua_torive(lua_State* L, int idx, bool allowNil = false) |
| { |
| T* riveObject = (T*)lua_touserdatatagged(L, idx, T::luaTag); |
| if (!allowNil && riveObject == nullptr) |
| { |
| luaL_typeerror(L, idx, T::luaName); |
| return nullptr; |
| } |
| return riveObject; |
| } |
| |
| template <typename T> static void lua_register_rive(lua_State* L) |
| { |
| if (T::hasMetatable) |
| { |
| // create metatable for T |
| luaL_newmetatable(L, T::luaName); |
| // lua_createtable(L, 0, 1); |
| |
| // push it again as lua_setuserdatametatable pops it |
| lua_pushvalue(L, -1); |
| lua_setuserdatametatable(L, T::luaTag); |
| } |
| if (!std::is_trivially_destructible<T>::value) |
| { |
| // We only need to call the C++ destructor if the object is not |
| // trivially destructible. |
| lua_setuserdatadtor(L, T::luaTag, [](lua_State* L, void* data) { |
| ((T*)data)->~T(); |
| }); |
| } |
| } |
| |
| inline const Vec2D* lua_checkvec2d(lua_State* L, int stack) |
| { |
| return (const Vec2D*)luaL_checkvector(L, stack); |
| } |
| |
| inline const Vec2D* lua_tovec2d(lua_State* L, int stack) |
| { |
| return (const Vec2D*)lua_tovector(L, stack); |
| } |
| |
| inline void lua_pushvec2d(lua_State* L, Vec2D vec) |
| { |
| return lua_pushvector2(L, vec.x, vec.y); |
| } |
| |
| int luaopen_rive(lua_State* L); |
| int rive_luaErrorHandler(lua_State* L); |
| int rive_lua_pcall(lua_State* state, int nargs, int nresults); |
| int rive_lua_pcall_with_context(lua_State* state, |
| ScriptedObject* scriptedObject, |
| int nargs, |
| int nresults); |
| int rive_lua_pushRef(lua_State* state, int ref); |
| void rive_lua_pop(lua_State* state, int count); |
| |
| #ifdef RIVE_ORE |
| // Finishes any ORE render pass left open at script return and reports it |
| // as a Lua error. Defined in src/lua/renderer/lua_gpu.cpp. |
| void rive_lua_closeOrphanRenderPass(lua_State* state); |
| #endif |
| |
| class ScriptingContext |
| { |
| public: |
| ScriptingContext(Factory* factory) : m_factory(factory) {} |
| virtual ~ScriptingContext() { shutdownAsync(); } |
| Factory* factory() const { return m_factory; } |
| ScriptedObject* currentScriptedObject() const |
| { |
| return m_currentScriptedObject; |
| } |
| void currentScriptedObject(ScriptedObject* value) |
| { |
| m_currentScriptedObject = value; |
| } |
| |
| virtual void printError(lua_State* state) = 0; |
| virtual void printBeginLine(lua_State* state) = 0; |
| virtual void print(Span<const char> data) = 0; |
| virtual void printEndLine() = 0; |
| virtual int pCall(lua_State* state, int nargs, int nresults) = 0; |
| |
| // Add a module to be registered later via performRegistration() |
| void addModule(ModuleDetails* moduleDetails); |
| // Perform registration of all added modules, handling dependencies and |
| // retries |
| void performRegistration(lua_State* state); |
| // Called when a module is required but not found during registration |
| void recordMissingDependency(const std::string& requiringModule, |
| const std::string& missingModule); |
| |
| // Ore GPU context for this VM, derived from the render factory. Null when |
| // there is no render context, or it is not GPU-backed. Returned as void* so |
| // callers that include ore headers cast to ore::Context*. |
| void* oreContext() const |
| { |
| return m_renderContext ? m_renderContext->ore() : nullptr; |
| } |
| |
| // Render factory — attached by the host once a GPU device exists, which may |
| // be after construction (or never, for headless VMs). Distinct from the |
| // construction factory(). A RenderContext is a Factory, so callers needing |
| // gpu APIs cast down to gpu::RenderContext*. |
| void setRenderContext(Factory* ctx) { m_renderContext = ctx; } |
| Factory* renderContext() const { return m_renderContext; } |
| |
| // WorkPool for async operations (image decode, etc.). |
| // Lazily created on first access. Shared across all contexts via a |
| // process-global singleton. |
| class WorkPool* workPool(); |
| uint64_t ownerId() const { return m_ownerId; } |
| |
| // Cancel all pending async tasks for this context. Must be called |
| // BEFORE lua_close() to prevent callbacks on a dead Lua state. |
| void shutdownAsync(); |
| |
| // Like shutdownAsync but also cancels WASM browser-native decodes |
| // that bypass WorkPool. Call from ~ScriptingVM with the main lua_State. |
| void shutdownAsyncForState(lua_State* mainThread); |
| |
| // WebGL/WASM only: whether an ore frame is currently open for this VM. |
| // riveLuaPCall uses this to auto-open a mini-frame when Lua callbacks |
| // fire outside the normal render boundary (e.g. Coop stream events). |
| void setOreFrameOpen(bool open) { m_oreFrameOpen = open; } |
| bool oreFrameOpen() const { return m_oreFrameOpen; } |
| |
| // True while Artboard::drawCanvases() is actively walking scripted |
| // objects to invoke their drawCanvas() Lua callbacks. |
| void setCanvasDrawingPhase(bool value) { m_canvasDrawingPhase = value; } |
| bool canvasDrawingPhase() const { return m_canvasDrawingPhase; } |
| |
| // When set, context:gpuCanvas() always returns a deferred (texture-less) |
| // canvas regardless of requested size, never calling makeRenderCanvas. |
| // Used by the editor's headless method-detection VM, which has no GPU |
| // device / RenderContext. Default false: normal runtimes allocate. |
| void setGpuCanvasDeferOnly(bool value) { m_gpuCanvasDeferOnly = value; } |
| bool gpuCanvasDeferOnly() const { return m_gpuCanvasDeferOnly; } |
| |
| // WebGL/WASM only: GL context handle saved at riveGPUBeginFrame so |
| // riveGPUEndFrame can restore the caller's context afterwards. |
| void setPrevGLContext(intptr_t h) { m_prevGLContext = h; } |
| intptr_t prevGLContext() const { return m_prevGLContext; } |
| |
| #ifdef __EMSCRIPTEN__ |
| void setGLHandle(int h) { m_glHandle = h; } |
| int glHandle() const { return m_glHandle; } |
| #endif |
| |
| private: |
| bool tryRegisterModule(lua_State* state, ModuleDetails* moduleDetails); |
| void sortNextModule(ModuleDetails* module, |
| std::vector<ModuleDetails*>* pendingModules, |
| std::vector<ModuleDetails*>* sortedModules, |
| std::unordered_set<ModuleDetails*>* visitedModules); |
| // Called when a module successfully registers |
| void onModuleRegistered(ModuleDetails* moduleDetails); |
| |
| private: |
| Factory* m_renderContext = nullptr; |
| uint64_t m_ownerId = 0; |
| bool m_oreFrameOpen = false; |
| bool m_canvasDrawingPhase = false; |
| bool m_gpuCanvasDeferOnly = false; |
| intptr_t m_prevGLContext = 0; |
| #ifdef __EMSCRIPTEN__ |
| int m_glHandle = 0; |
| #endif |
| |
| Factory* m_factory; |
| ScriptedObject* m_currentScriptedObject = nullptr; |
| std::vector<ModuleDetails*> m_modulesToRegister; |
| std::unordered_map<std::string, ModuleDetails*> m_moduleLookup; |
| std::unordered_set<ModuleDetails*> m_pendingModules; |
| |
| #ifdef WITH_RIVE_TOOLS |
| // Editor-only: Map from asset ID to generator function ref. |
| // Allows direct ScriptedObject reinitialization without regenerating |
| // the runtime file. |
| std::unordered_map<uint32_t, int> m_assetGeneratorRefs; |
| bool m_isPlaying = false; |
| std::vector<ScriptedProperty*> m_orphanScriptedProperties; |
| |
| // Per-VM RSTB blobs for WGSL shaders compiled during requestVM. Populated |
| // by the scripting workspace response phase; looked up by loadShader(). |
| std::unordered_map<std::string, std::vector<uint8_t>> m_shaderRstbs; |
| |
| public: |
| void setGeneratorRef(uint32_t assetId, int ref); |
| int getGeneratorRef(uint32_t assetId) const; |
| void clearGeneratorRefs(); |
| bool hasGeneratorRef(uint32_t assetId) const; |
| void isPlaying(bool value) { m_isPlaying = value; } |
| bool isPlaying() const { return m_isPlaying; } |
| void trackOrphanScriptedProperty(ScriptedProperty* property); |
| void untrackOrphanScriptedProperty(ScriptedProperty* property); |
| void disposeOrphanScriptedProperties(); |
| void registerShaderRstb(std::string name, std::vector<uint8_t> bytes); |
| const std::vector<uint8_t>* findShaderRstb(const std::string& name) const; |
| // Transfers all RSTB blobs out of this context (used during VM adoption |
| // to preserve blobs across context replacement). |
| std::unordered_map<std::string, std::vector<uint8_t>> takeShaderRstbs() |
| { |
| return std::move(m_shaderRstbs); |
| } |
| #endif |
| }; |
| |
| class ScopedScriptedObjectContext |
| { |
| public: |
| ScopedScriptedObjectContext(ScriptingContext* context, |
| ScriptedObject* scriptedObject) : |
| m_context(context), |
| m_previous(context == nullptr ? nullptr |
| : context->currentScriptedObject()) |
| { |
| if (m_context != nullptr) |
| { |
| m_context->currentScriptedObject(scriptedObject); |
| } |
| } |
| |
| ~ScopedScriptedObjectContext() |
| { |
| if (m_context != nullptr) |
| { |
| m_context->currentScriptedObject(m_previous); |
| } |
| } |
| |
| private: |
| ScriptingContext* m_context; |
| ScriptedObject* m_previous; |
| }; |
| |
| class ScopedCanvasDrawingPhase |
| { |
| public: |
| ScopedCanvasDrawingPhase(ScriptingContext* context) : |
| m_context(context), |
| m_previous(context == nullptr ? false : context->canvasDrawingPhase()) |
| { |
| if (m_context != nullptr) |
| { |
| m_context->setCanvasDrawingPhase(true); |
| } |
| } |
| |
| ~ScopedCanvasDrawingPhase() |
| { |
| if (m_context != nullptr) |
| { |
| m_context->setCanvasDrawingPhase(m_previous); |
| } |
| } |
| |
| private: |
| ScriptingContext* m_context; |
| bool m_previous; |
| }; |
| |
| class ScriptedDataValue |
| { |
| public: |
| ScriptedDataValue(lua_State* L) { m_state = L; } |
| virtual ~ScriptedDataValue(); |
| static constexpr const char* luaName = "DataValue"; |
| DataValue* dataValue() { return m_dataValue; } |
| virtual bool isNumber() { return false; } |
| virtual bool isString() { return false; } |
| virtual bool isBoolean() { return false; } |
| virtual bool isColor() { return false; } |
| |
| const lua_State* state() const { return m_state; } |
| |
| protected: |
| lua_State* m_state; |
| DataValue* m_dataValue = nullptr; |
| }; |
| |
| class ScriptedDataValueNumber : public ScriptedDataValue |
| { |
| public: |
| ScriptedDataValueNumber(lua_State* L, float value) : ScriptedDataValue(L) |
| { |
| m_dataValue = new DataValueNumber(value); |
| } |
| static constexpr bool hasMetatable = true; |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 20; |
| static constexpr const char* luaName = "DataValueNumber"; |
| bool isNumber() override { return true; } |
| }; |
| |
| class ScriptedDataValueString : public ScriptedDataValue |
| { |
| public: |
| ScriptedDataValueString(lua_State* L, std::string value) : |
| ScriptedDataValue(L) |
| { |
| m_dataValue = new DataValueString(value); |
| } |
| static constexpr bool hasMetatable = true; |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 21; |
| static constexpr const char* luaName = "DataValueString"; |
| bool isString() override { return true; } |
| }; |
| |
| class ScriptedDataValueBoolean : public ScriptedDataValue |
| { |
| public: |
| ScriptedDataValueBoolean(lua_State* L, bool value) : ScriptedDataValue(L) |
| { |
| m_dataValue = new DataValueBoolean(value); |
| } |
| static constexpr bool hasMetatable = true; |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 22; |
| static constexpr const char* luaName = "DataValueBoolean"; |
| bool isBoolean() override { return true; } |
| }; |
| |
| class ScriptedDataValueColor : public ScriptedDataValue |
| { |
| public: |
| ScriptedDataValueColor(lua_State* L, int value) : ScriptedDataValue(L) |
| { |
| m_dataValue = new DataValueColor(value); |
| } |
| static constexpr bool hasMetatable = true; |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 23; |
| static constexpr const char* luaName = "DataValueColor"; |
| bool isColor() override { return true; } |
| }; |
| |
| class ScriptedPointerEvent |
| { |
| public: |
| ScriptedPointerEvent(uint8_t id, |
| Vec2D position, |
| Vec2D previousPosition = Vec2D(), |
| int hitListenerType = 0, |
| float timeStamp = 0.f) : |
| m_id(id), |
| m_position(position), |
| m_previousPosition(previousPosition), |
| m_hitListenerType(hitListenerType), |
| m_timeStamp(timeStamp) |
| {} |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 24; |
| static constexpr const char* luaName = "PointerEvent"; |
| static constexpr bool hasMetatable = true; |
| |
| uint8_t m_id = 0; |
| Vec2D m_position; |
| Vec2D m_previousPosition; |
| int m_hitListenerType = 0; |
| float m_timeStamp = 0.f; |
| HitResult m_hitResult = HitResult::none; |
| }; |
| |
| class ScriptedNode |
| { |
| public: |
| ScriptedNode(rcp<ScriptReffedArtboard> artboard, |
| TransformComponent* component); |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 25; |
| static constexpr const char* luaName = "NodeData"; |
| static constexpr bool hasMetatable = true; |
| |
| TransformComponent* component() { return m_component; } |
| rcp<ScriptReffedArtboard> artboard() { return m_artboard; } |
| |
| const ShapePaint* shapePaint(); |
| void shapePaint(const ShapePaint* shapePaint) { m_shapePaint = shapePaint; } |
| |
| private: |
| rcp<ScriptReffedArtboard> m_artboard; |
| TransformComponent* m_component = nullptr; |
| const ShapePaint* m_shapePaint = nullptr; |
| }; |
| |
| class ScriptedContourMeasure |
| { |
| public: |
| ScriptedContourMeasure(rcp<ContourMeasure> measure, |
| rcp<RefCntContourMeasureIter> iter) : |
| m_measure(measure), m_iter(iter) |
| {} |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 26; |
| static constexpr const char* luaName = "ContourMeasure"; |
| static constexpr bool hasMetatable = true; |
| |
| ContourMeasure* measure() { return m_measure.get(); } |
| rcp<RefCntContourMeasureIter> iter() { return m_iter; } |
| |
| private: |
| rcp<ContourMeasure> m_measure; |
| rcp<RefCntContourMeasureIter> m_iter; |
| }; |
| |
| class ScriptedPathMeasure |
| { |
| public: |
| ScriptedPathMeasure(PathMeasure measure) : m_measure(std::move(measure)) {} |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 27; |
| static constexpr const char* luaName = "PathMeasure"; |
| static constexpr bool hasMetatable = true; |
| |
| PathMeasure* measure() { return &m_measure; } |
| |
| private: |
| PathMeasure m_measure; |
| }; |
| |
| class ScriptedContext |
| { |
| public: |
| ScriptedContext(ScriptedObject*); |
| ScriptedObject* scriptedObject() { return m_scriptedObject; } |
| void clearScriptedObject() { m_scriptedObject = nullptr; } |
| int pushViewModel(lua_State*); |
| int pushRootViewModel(lua_State*); |
| int pushDataContext(lua_State*); |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 28; |
| static constexpr const char* luaName = "Context"; |
| static constexpr bool hasMetatable = true; |
| bool missingRequestedData() { return m_missingRequestedData; } |
| |
| private: |
| ScriptedObject* m_scriptedObject = nullptr; |
| bool m_missingRequestedData = false; |
| }; |
| |
| /// Wraps [`ListenerInvocation`] for `performAction` in scripted listener |
| /// actions. |
| class ScriptedInvocation |
| { |
| public: |
| explicit ScriptedInvocation(ListenerInvocation inv) : |
| m_invocation(std::move(inv)) |
| {} |
| |
| ListenerInvocation& invocation() { return m_invocation; } |
| const ListenerInvocation& invocation() const { return m_invocation; } |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 54; |
| static constexpr const char* luaName = "Invocation"; |
| static constexpr bool hasMetatable = true; |
| |
| private: |
| ListenerInvocation m_invocation; |
| }; |
| |
| class ScriptedKeyboardInvocation |
| { |
| public: |
| ScriptedKeyboardInvocation(Key key, |
| KeyModifiers modifiers, |
| bool isPressed, |
| bool isRepeat) : |
| m_key(key), |
| m_modifiers(modifiers), |
| m_isPressed(isPressed), |
| m_isRepeat(isRepeat) |
| {} |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 55; |
| static constexpr const char* luaName = "KeyboardInvocation"; |
| static constexpr bool hasMetatable = true; |
| |
| Key m_key; |
| KeyModifiers m_modifiers; |
| bool m_isPressed; |
| bool m_isRepeat; |
| }; |
| |
| class ScriptedTextInputInvocation |
| { |
| public: |
| explicit ScriptedTextInputInvocation(std::string text) : |
| m_text(std::move(text)) |
| {} |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 56; |
| static constexpr const char* luaName = "TextInputInvocation"; |
| static constexpr bool hasMetatable = true; |
| |
| const std::string& text() const { return m_text; } |
| |
| private: |
| std::string m_text; |
| }; |
| |
| class ScriptedFocusInvocation |
| { |
| public: |
| explicit ScriptedFocusInvocation(bool isFocus) : m_isFocus(isFocus) {} |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 57; |
| static constexpr const char* luaName = "FocusInvocation"; |
| static constexpr bool hasMetatable = true; |
| |
| bool m_isFocus; |
| }; |
| |
| class ScriptedReportedEventInvocation |
| { |
| public: |
| ScriptedReportedEventInvocation(Event* event, float delaySeconds) : |
| m_event(event), m_delaySeconds(delaySeconds) |
| {} |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 58; |
| static constexpr const char* luaName = "ReportedEventInvocation"; |
| static constexpr bool hasMetatable = true; |
| |
| /// Valid only for the duration of the listener callback; do not retain. |
| Event* m_event; |
| float m_delaySeconds; |
| }; |
| |
| class ScriptedViewModelChangeInvocation |
| { |
| public: |
| ScriptedViewModelChangeInvocation() = default; |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 59; |
| static constexpr const char* luaName = "ViewModelChangeInvocation"; |
| static constexpr bool hasMetatable = true; |
| }; |
| |
| class ScriptedGamepadConnected |
| { |
| public: |
| explicit ScriptedGamepadConnected(const GamepadSnapshot& snapshot) : |
| m_snapshot(snapshot) |
| {} |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 48; |
| static constexpr const char* luaName = "GamepadConnected"; |
| static constexpr bool hasMetatable = true; |
| |
| GamepadSnapshot m_snapshot; |
| }; |
| |
| class ScriptedGamepadEvent |
| { |
| public: |
| explicit ScriptedGamepadEvent(const GamepadEventInvocation& v) : m_data(v) |
| {} |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 63; |
| static constexpr const char* luaName = "GamepadEvent"; |
| static constexpr bool hasMetatable = true; |
| |
| GamepadEventInvocation m_data; |
| }; |
| |
| class ScriptedGamepadDisconnected |
| { |
| public: |
| explicit ScriptedGamepadDisconnected(int deviceId) : m_deviceId(deviceId) {} |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 64; |
| static constexpr const char* luaName = "GamepadDisconnected"; |
| static constexpr bool hasMetatable = true; |
| |
| int m_deviceId; |
| }; |
| |
| class ScriptedNoneInvocation |
| { |
| public: |
| ScriptedNoneInvocation() = default; |
| |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 61; |
| static constexpr const char* luaName = "NoneInvocation"; |
| static constexpr bool hasMetatable = true; |
| }; |
| |
| void rive_lua_register_listener_invocation_types(lua_State* L); |
| void rive_lua_push_pointer_arg_for_perform(lua_State* L, |
| const ListenerInvocation& inv); |
| void rive_lua_push_scripted_invocation(lua_State* L, |
| const ListenerInvocation& inv); |
| |
| static void interruptCPP(lua_State* L, int gc); |
| |
| #ifdef WITH_RIVE_TOOLS |
| // Callback type for notifying when console data is available. |
| // If null, console output goes to stdout. |
| using ConsoleCallback = void (*)(); |
| #endif |
| |
| class CPPRuntimeScriptingContext : public ScriptingContext |
| { |
| public: |
| #ifdef WITH_RIVE_TOOLS |
| CPPRuntimeScriptingContext(Factory* factory, |
| int timeoutMs = 200, |
| ConsoleCallback consoleCallback = nullptr) : |
| ScriptingContext(factory), |
| m_timeoutMs(timeoutMs), |
| m_consoleCallback(consoleCallback) |
| {} |
| #else |
| CPPRuntimeScriptingContext(Factory* factory, int timeoutMs = 200) : |
| ScriptingContext(factory), m_timeoutMs(timeoutMs) |
| {} |
| #endif |
| virtual ~CPPRuntimeScriptingContext() = default; |
| |
| std::chrono::time_point<std::chrono::steady_clock> executionTime; |
| |
| int timeoutMs() const { return m_timeoutMs; } |
| void setTimeoutMs(int ms) { m_timeoutMs = ms; } |
| |
| int pCall(lua_State* state, int nargs, int nresults) override; |
| |
| void printBeginLine(lua_State* state) override |
| { |
| #ifdef WITH_RIVE_TOOLS |
| BinaryWriter writer(&m_consoleBuffer); |
| lua_Debug ar; |
| bool hasInfo = lua_getinfo(state, 1, "sl", &ar) != 0; |
| writer.write((uint8_t)0); |
| writer.write(hasInfo && ar.source != nullptr ? ar.source : ""); |
| writer.writeVarUint((uint32_t)(hasInfo ? ar.currentline : 0)); |
| #endif |
| } |
| |
| void print(Span<const char> data) override |
| { |
| #ifdef WITH_RIVE_TOOLS |
| if (data.size() == 0) |
| { |
| return; |
| } |
| BinaryWriter writer(&m_consoleBuffer); |
| writer.writeVarUint((uint64_t)data.size()); |
| writer.write((const uint8_t*)data.data(), (size_t)data.size()); |
| |
| if (m_consoleCallback == nullptr) |
| #endif |
| { |
| auto message = std::string(data.data(), data.size()); |
| printf("%s", message.c_str()); |
| } |
| } |
| |
| void printEndLine() override |
| { |
| #ifdef WITH_RIVE_TOOLS |
| BinaryWriter writer(&m_consoleBuffer); |
| writer.writeVarUint((uint32_t)0); |
| |
| if (m_consoleCallback != nullptr) |
| { |
| if (!m_calledConsoleCallback) |
| { |
| m_calledConsoleCallback = true; |
| m_consoleCallback(); |
| } |
| } |
| else |
| #endif |
| { |
| printf("\n"); |
| } |
| } |
| |
| void printError(lua_State* state) override |
| { |
| const char* error = lua_tostring(state, -1); |
| fprintf(stderr, "%s\n", error); |
| #ifdef WITH_RIVE_TOOLS |
| if (error) |
| { |
| printBeginLine(state); |
| print(Span<const char>(error, strlen(error))); |
| printEndLine(); |
| } |
| #endif |
| } |
| |
| void startTimedExecution(lua_State* state) |
| { |
| if (m_timeoutMs == 0) |
| { |
| return; |
| } |
| lua_Callbacks* cb = lua_callbacks(state); |
| cb->interrupt = interruptCPP; |
| executionTime = std::chrono::steady_clock::now(); |
| } |
| |
| void endTimedExecution(lua_State* state) |
| { |
| if (m_timeoutMs == 0) |
| { |
| return; |
| } |
| lua_Callbacks* cb = lua_callbacks(state); |
| cb->interrupt = nullptr; |
| } |
| |
| #ifdef WITH_RIVE_TOOLS |
| // Console buffer access for editor |
| Span<uint8_t> consoleMemory() { return m_consoleBuffer.memory(); } |
| |
| void clearConsole() |
| { |
| m_consoleBuffer.clear(); |
| m_calledConsoleCallback = false; |
| } |
| |
| bool hasConsoleCallback() const { return m_consoleCallback != nullptr; } |
| #endif |
| |
| private: |
| int m_timeoutMs = 200; |
| #ifdef WITH_RIVE_TOOLS |
| ConsoleCallback m_consoleCallback = nullptr; |
| VectorBinaryStream m_consoleBuffer; |
| bool m_calledConsoleCallback = false; |
| #endif |
| }; |
| |
| class ScriptedDataContext |
| { |
| public: |
| ScriptedDataContext(lua_State* L, rcp<DataContext> dataContext); |
| static constexpr uint8_t luaTag = LUA_T_COUNT + 36; |
| static constexpr const char* luaName = "DataContext"; |
| static constexpr bool hasMetatable = true; |
| int pushViewModel(); |
| int pushParent(); |
| |
| const lua_State* state() const { return m_state; } |
| |
| private: |
| lua_State* m_state = nullptr; |
| rcp<DataContext> m_dataContext = nullptr; |
| }; |
| |
| static void interruptCPP(lua_State* L, int gc) |
| { |
| if (gc >= 0 || !lua_isyieldable(L)) |
| { |
| return; |
| } |
| |
| CPPRuntimeScriptingContext* context = |
| static_cast<CPPRuntimeScriptingContext*>(lua_getthreaddata(L)); |
| |
| const auto now = std::chrono::steady_clock::now(); |
| auto ms = std::chrono::duration_cast<std::chrono::milliseconds>( |
| now - context->executionTime) |
| .count(); |
| if (ms > context->timeoutMs()) |
| { |
| lua_Callbacks* cb = lua_callbacks(L); |
| cb->interrupt = nullptr; |
| // reserve space for error string |
| lua_rawcheckstack(L, 1); |
| |
| // Format human-readable error message |
| char errorMsg[128]; |
| int timeoutMs = context->timeoutMs(); |
| if (timeoutMs >= 1000) |
| { |
| double seconds = timeoutMs / 1000.0; |
| snprintf(errorMsg, |
| sizeof(errorMsg), |
| "execution exceeded %.1f second%s timeout", |
| seconds, |
| seconds == 1.0 ? "" : "s"); |
| } |
| else |
| { |
| snprintf(errorMsg, |
| sizeof(errorMsg), |
| "execution exceeded %d millisecond%s timeout", |
| timeoutMs, |
| timeoutMs == 1 ? "" : "s"); |
| } |
| luaL_error(L, "%s", errorMsg); |
| } |
| } |
| } // namespace rive |
| |
| #ifdef RIVE_CANVAS |
| #ifdef RIVE_ORE |
| namespace rive |
| { |
| class ShaderAsset; |
| } |
| // Load a shader by name into a ScriptedShader (populates both vertex and |
| // fragment modules for GLSL targets with split entry points). |
| // Checks ScriptingContext::m_shaderRstbs first (editor path, compiled |
| // during requestVM), then |fileAsset| if non-null (runtime .riv path). |
| // Returns false on failure. |
| bool lua_gpu_load_shader_by_name(rive::ScriptedShader* out, |
| rive::ScriptingContext* context, |
| const char* name, |
| rive::ShaderAsset* fileAsset); |
| |
| // Compile a shader by name and push the resulting ScriptedShader onto the |
| // Lua stack. Returns 1 on success, 0 on failure. Declared here (implemented |
| // in lua_gpu.cpp) so callers that only have a forward-declaration of |
| // ShaderModule do not need to touch rcp<ShaderModule> directly. |
| int lua_gpu_push_shader_by_name(lua_State* L, const char* name); |
| #endif // RIVE_ORE |
| #endif // RIVE_CANVAS |
| |
| // Push a GPU features table onto the Lua stack. Queries the ORE context when |
| // available, otherwise returns conservative defaults. Always returns 1. |
| // Implemented in lua_scripted_context.cpp. |
| int lua_push_gpu_features(lua_State* L); |
| |
| #endif |
| #endif |