Integrating Rive’s native renderer (built on OpenGL, Metal, or Vulkan) with Solar2D via a custom plugin is technically feasible, but requires careful synchronization between Rive’s renderer and Solar2D’s graphics pipeline. Here’s a detailed breakdown of how this could work:
1. Architecture Overview
Rive Renderer: Rive’s renderer outputs frames using low-level GPU APIs (OpenGL, Metal, Vulkan).
Solar2D: Uses OpenGL/Vulkan for rendering and provides Lua APIs to manage textures, sprites, and shaders.
Goal: Render Rive animations into a GPU texture that Solar2D can display and update dynamically.
2. Key Integration Strategies
A. Texture Sharing via GPU Resources
Rive can render animations into a texture (framebuffer), which Solar2D can then bind and display. This avoids CPU-GPU data copies and is highly efficient.
Steps:
Create a Shared Texture:
In the plugin, generate a GPU texture (e.g., GL_TEXTURE_2D for OpenGL).
Share this texture handle between Rive’s renderer and Solar2D.
Render to Texture with Rive:
Configure Rive to render into the shared texture (e.g., using an offscreen framebuffer).
Example for OpenGL:
// In Rive plugin code
glGenTextures(1, &sharedTexture);
glBindTexture(GL_TEXTURE_2D, sharedTexture);
rive::Renderer::beginFrame(sharedTexture); // Hypothetical API
Bind Texture in Solar2D:
Use Solar2D’s graphics.newTexture() or a modified Graphics Extension plugin to reference the shared texture.
Example Lua usage:
local riveTexture = graphics.newTexture({ type = "external", name = "rive_shared_texture" })
local sprite = display.newImageRect(riveTexture, 300, 300)
B. Copying Pixel Data (Fallback)
If direct texture sharing is unsupported (e.g., due to API/platform constraints), copy rendered pixels from Rive to Solar2D’s texture.
Steps:
Render to Offscreen Buffer:
Use Rive to render into a GPU framebuffer.
Read pixels via glReadPixels (OpenGL) or equivalent.
Update Solar2D Texture:
Use Solar2D’s graphics.newTexture() with a volatile flag to create a dynamically updatable texture.
Update the texture data using graphics.setTexture() (similar to PluginGfxe.cpp).
Example in C++ (PluginGfxe.cpp-inspired):
// In the plugin's native code
void updateTexture(lua_State *L) {
// 1. Render Rive frame to offscreen buffer
riveRenderer->drawFrame();
// 2. Read pixel data into a CPU buffer
std::vector<uint8_t> pixels(width * height * 4);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
// 3. Update Solar2D texture
CoronaExternalTextureCallbacks textureCallbacks;
textureCallbacks.size = sizeof(textureCallbacks);
textureCallbacks.getWidth = getWidthCallback;
textureCallbacks.getHeight = getHeightCallback;
textureCallbacks.onRequestBitmap = [pixels](...) { memcpy(bitmap, pixels.data(), size); };
CoronaExternalPushTexture(L, textureCallbacks);
}
3. Critical Technical Considerations
a. Graphics API Alignment
OpenGL: Works best on Android/iOS (Solar2D’s default).
Metal/Vulkan: Requires Solar2D’s experimental Vulkan backend or platform-specific tweaks (e.g., iOS Metal ↔ OpenGL interop).
b. Synchronization
Ensure Rive’s rendering and Solar2D’s frame loop are synchronized (e.g., use enterFrame listeners to trigger Rive updates).
void onFrame(lua_State *L) {
riveRenderer->draw(); // Render to offscreen buffer
updateTexture(L); // Copy pixels to Solar2D texture
}
Lua API for Control:
local rive = require "plugin.rive"
rive.play("animation.riv", "StateMachine1")
6. Challenges
Platform-Specific Code: Requires separate implementations for OpenGL (Android), Metal (iOS), and Vulkan (Windows/Linux).
Texture Lifetime Management: Ensure shared textures are not destroyed prematurely.
Alpha/Blending: Handle transparency correctly between Rive and Solar2D layers.
7. Recommendations
Prioritize Direct Texture Sharing: Use OpenGL texture handles or Metal’s CVPixelBuffer (iOS) for zero-copy workflows.
Leverage Solar2D’s Native Extensions: Use CoronaExternalTextureCallbacks to integrate Rive’s output.
Benchmark Performance: Test both GPU-GPU and CPU-GPU paths to avoid bottlenecks.
By aligning Rive’s renderer with Solar2D’s graphics pipeline, you can achieve smooth, real-time animations. Let me know if you’d like a sample plugin skeleton!
Another AI answer. The native spine plugin would be a good reference, thank you @ depilz for the great work. Next I will look into setting up building the rive-runtime for Solar2D plugin
Yes, the texture handling in the Spine plugin for Solar2D is conceptually similar to what I described for the Graphics Extensions (Gfxe) plugin and a hypothetical Rive plugin. Let’s break down the parallels and differences:
1. Texture Workflow Similarities
a. Texture Creation & Sharing
Spine Plugin:
Uses OpenGL to render Spine skeletal animations into textures.
Shares GPU textures with Solar2D via native plugin code (e.g., CoronaExternalTextureCallbacks).
void update(lua_State* L) {
renderer->draw(); // Renders to OpenGL/Metal/Vulkan texture
CoronaExternalPushTexture(L, textureCallbacks); // Share with Solar2D
}
4. Critical Code Snippets
Texture Callbacks (Shared Between Plugins)
// Similar in Spine, Gfxe, and Rive plugins
CoronaExternalTextureCallbacks textureCallbacks;
textureCallbacks.size = sizeof(textureCallbacks);
textureCallbacks.getWidth = [](void* context) { return width; };
textureCallbacks.getHeight = [](void* context) { return height; };
textureCallbacks.onRequestBitmap = [](void* context, CoronaBitmap* bitmap) {
// Copy pixel data (Gfxe) or bind GPU texture (Spine/Rive)
};
Solar2D Lua Integration
-- Spine, Gfxe, or Rive would expose similar APIs
local spine = require "plugin.spine"
local rive = require "plugin.rive"
local spineActor = spine.newActor("skeleton.json", "atlas.atlas")
local riveAnimation = rive.newAnimation("animation.riv")
5. Takeaways
Shared Pattern: All plugins (Spine, Gfxe, Rive) use native code to generate/update textures and expose them to Solar2D via CoronaExternalTextureCallbacks.
Rendering Differences:
Spine: Prebaked atlas + skeletal deformation.
Rive: Vector paths + runtime interpolation.
Performance: Direct GPU texture sharing (Spine/Rive) is more efficient than CPU pixel copying (Gfxe).
For a Rive plugin, you’d adapt the Spine plugin’s OpenGL texture workflow but replace Spine’s renderer with Rive’s. The Graphics Extensions Plugin is also a useful reference for handling dynamic pixel data.