Skip to content

Commit 79f7620

Browse files
committed
More rhi fun
1 parent 4516aa9 commit 79f7620

88 files changed

Lines changed: 2902 additions & 2089 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
3030
- New `GpuTarget` class (`rhi/yup_GpuTarget.h`): low-level render-pass-only offscreen GPU surface (`create`, `beginRenderPass`, `asTexture`, `asImage`, `readPixels`). Its backing texture is allocated from the context's main render context, so it does not reserve a dedicated `rive::gpu::RenderContext` — use it for custom `GpuPipeline` work (e.g. post-process passes) that needs no 2D drawing.
3131
- New `GpuCanvas` class (`rhi/yup_GpuCanvas.h`): consolidated backend-agnostic offscreen GPU surface that now composes a `GpuTarget` (over a `RenderableTarget`) and creates a non-owning `Graphics` lazily only when 2D drawing is requested.
3232

33+
#### RHI module extraction & GpuDevice
34+
35+
- **New `yup_rhi` module**: the GPU abstraction layer extracted from `yup_graphics` into its own module (depends on `yup_core`, `yup_shading`, `rive_renderer`). All RHI classes (`GpuFrame`, `GpuPipeline`, `GpuBuffer`, `GpuTexture`, `GpuTarget`, `GpuRenderPass`, `GpuPipelineCache`) now live in `yup_rhi`. `yup_graphics` depends on `yup_rhi` for GPU access.
36+
- **`GpuDevice`**: new reference-counted GPU device abstraction (was `GpuContext`). Owns the native GPU device and command queue without requiring a window — can be used for headless GPU compute (e.g. audio DSP on the GPU). Created via `GpuDevice::create(GpuPlatform, Options)`. All RHI factory methods (`GpuFrame::begin`, `GpuPipeline::compile*`, `GpuBuffer::create`, `GpuTarget::create`) now take `GpuDevice::Ptr` for safe shared ownership.
37+
- **`GpuPlatform`** enum: standalone platform enum (`Headless`, `Metal`, `Direct3D`, `OpenGL`, `OpenGLES`, `WebGPU`) replacing the nested `GpuDevice::Api`. `GraphicsContext::getPlatform()` returns it directly — no typedef alias.
38+
- **`GpuColor`** struct (`rhi/yup_GpuTypes.h`): lightweight 4-component GPU color for render options. Implicitly constructable from any type with `getRedFloat()`/`getGreenFloat()`/`getBlueFloat()`/`getAlphaFloat()` (e.g. `yup::Color`), so `GpuRenderOptions { true, Colors::transparentBlack }` works without code changes.
39+
- **`GraphicsContext` simplified**: wraps a `GpuDevice::Ptr` (obtained via `getGpuDevice()` returning `GpuDevice::Ptr`). Offscreen target management (`createOffscreenTarget`, `beginOffscreen`, `endOffscreen`, `readOffscreenPixels`) delegated to `GpuDevice`. Factory accepts optional `GpuDevice::Ptr` to share an existing GPU device.
40+
- **Backends**: `GpuDevice` has native implementations for all platforms (Metal, OpenGL, Direct3D 11, Dawn, WebGPU/Emscripten, Headless). OpenGL backend probes `GL_VERSION` at runtime to detect compute shader support (GL ≥4.3 / GLES ≥3.1).
41+
- **`::Ptr` safety**: all RHI types that own resources (`GpuPipeline`, `GpuBuffer`, `GpuTexture`, `GpuTarget`, `GpuCanvas`) are reference-counted with `::Ptr`. Factory methods take `GpuDevice::Ptr` to keep the device alive for the resource's lifetime. `GpuFrame` is move-only stack RAII and takes `GpuDevice&` (no ownership).
42+
3343
#### Image Formats
3444

3545
- Added TIFF read/write support (`TiffImageFormat`) via libtiff: RGB, RGBA, Grayscale at 8/16-bit; multi-page reading; DPI and EXIF/ICC/XMP metadata extraction.

docs/graphics/index.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ The graphics stack renders 2D vector content and GPU-accelerated scenes across
44
Metal, Direct3D, OpenGL / OpenGL ES, WebGL and WebGPU (WASM / Emscripten), and Vulkan (in progress).
55
It is built on the open source [Rive](https://rive.app/) renderer.
66

7-
**Modules covered:** `yup_graphics`, `yup_shading`, `yup_animation`.
7+
**Modules covered:** `yup_rhi`, `yup_graphics`, `yup_shading`, `yup_animation`.
88

99
## In this area
1010

@@ -29,12 +29,18 @@ own [Imaging](../imaging/index.md) area.
2929

3030
## Key building blocks
3131

32-
The `yup_graphics` module provides:
32+
The `yup_graphics` and `yup_rhi` modules provide:
3333

34-
- **`GraphicsContext`** - abstracts the active rendering backend (`Api::Metal`,
35-
`Api::Direct3D`, `Api::OpenGL`, `Api::OpenGLES`, `Api::WebGPU`, `Api::Headless`),
36-
exposes DPI scaling, offscreen target creation, and the GPU capability probe
37-
`isGpuAvailable()`.
34+
- **`GpuDevice`** (`yup_rhi`) - a reference-counted GPU device abstraction that
35+
owns the native GPU device and command queue without requiring a window.
36+
Created via `GpuDevice::create(GpuPlatform, Options)`. Supports
37+
`GpuPlatform::Metal`, `Direct3D`, `OpenGL`, `OpenGLES`, `WebGPU`, and `Headless`.
38+
Use `GpuDevice` directly for GPU compute (e.g. audio DSP on the GPU) — no window needed.
39+
- **`GraphicsContext`** (`yup_graphics`) - wraps a `GpuDevice` and adds the
40+
window/swapchain layer plus Rive vector rendering. Created via
41+
`GraphicsContext::createContext(GpuPlatform, Options, GpuDevice::Ptr = {})`.
42+
When an existing `GpuDevice::Ptr` is provided, it shares the GPU device
43+
(useful when an audio processor already owns one).
3844
- **`Graphics`** - the immediate-mode 2D drawing API (fills, strokes, paths,
3945
gradients, text, images, textures).
4046
- **Primitives** - points, rectangles, sizes, affine transforms, and colors.

docs/graphics/rhi/buffers-and-textures.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ buffer set on the pass via `GpuRenderPass::setUniformBuffer()` instead.
1010
```cpp
1111
enum class GpuBufferType : uint8_t { vertex, index, uniform };
1212

13-
static GpuBuffer::Ptr GpuBuffer::create (GraphicsContext& ctx,
13+
static GpuBuffer::Ptr GpuBuffer::create (GpuDevice::Ptr ctx,
1414
GpuBufferType type,
1515
const void* data,
1616
size_t byteSize);

docs/graphics/rhi/concepts.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
## The GPU bridge
44

55
Every RHI type is a thin, portable wrapper over a single backend bridge: Rive's
6-
GPU context. The `GraphicsContext` owns that context and exposes it to the
7-
RHI layer. You never touch GPU types directly - the RHI hides them behind
8-
YUP-native handles (`GpuPipeline`, `GpuFrame`, `GpuRenderPass`, `GpuBuffer`,
9-
`GpuTexture`).
6+
GPU context. The `GpuDevice` owns that context and exposes it to the RHI layer.
7+
For windowed rendering, `GraphicsContext` wraps a `GpuDevice` and adds the
8+
swapchain and Rive vector renderer. For headless GPU compute, use `GpuDevice`
9+
directly — no window or Rive dependency.
1010

1111
Because the RHI targets one common abstraction, the same code path runs on every
1212
backend: Metal, Direct3D, OpenGL / OpenGL ES (including WebGL2), and WebGPU.
@@ -16,7 +16,7 @@ backend: Metal, Direct3D, OpenGL / OpenGL ES (including WebGL2), and WebGPU.
1616
Always check GPU availability before creating RHI resources:
1717

1818
```cpp
19-
if (! ctx.isGpuAvailable())
19+
if (! device.isGpuAvailable())
2020
return; // No GPU context - fall back or bail out.
2121
```
2222

@@ -25,16 +25,16 @@ not reference any GPU type, so user code stays backend-clean.
2525

2626
RHI factory functions honor this contract:
2727

28-
- `GpuPipeline::compile(...)` requires `isGpuAvailable()`.
29-
- `GpuBuffer::create(...)` returns `nullptr` if GPU context is unavailable.
30-
- `GpuTarget::create(...)` / `GpuCanvas::create(...)` return `nullptr` if
28+
- `GpuPipeline::compile(device, ...)` requires `isGpuAvailable()`.
29+
- `GpuBuffer::create(device, ...)` returns `nullptr` if GPU context is unavailable.
30+
- `GpuTarget::create(device, ...)` / `GpuCanvas::create(ctx, ...)` return `nullptr` if
3131
offscreen GPU resources cannot be allocated.
3232

3333
## The frame → pass → draw model
3434

3535
RHI rendering follows a strict hierarchy:
3636

37-
1. **Begin a frame** with `GpuFrame::begin(ctx)`. The frame owns the transient
37+
1. **Begin a frame** with `GpuFrame::begin(device)`. The frame owns the transient
3838
GPU resources (uniform buffers, texture views, samplers) created while
3939
encoding, and keeps them alive until submission completes.
4040
2. **Begin one or more render passes** into a target
@@ -46,9 +46,9 @@ RHI rendering follows a strict hierarchy:
4646
5. **Submit the frame** with `submit()` (or let it submit on destruction).
4747

4848
```cpp
49-
auto frame = GpuFrame::begin (ctx);
49+
auto frame = GpuFrame::begin (device);
5050
auto pass = target->beginRenderPass (frame, { true, Colors::black });
51-
pass.setPipeline (*pipeline);
51+
pass.setPipeline (pipeline);
5252
pass.draw (3);
5353
pass.finish();
5454
frame.submit();

docs/graphics/rhi/frames-and-passes.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ GPU resources (uniform buffers, texture views, samplers) created while encoding
88
its passes.
99

1010
```cpp
11-
static GpuFrame GpuFrame::begin (GraphicsContext& ctx);
11+
static GpuFrame GpuFrame::begin (GpuDevice::Ptr device);
1212
```
1313
1414
Begin a frame, encode one or more render passes into it, then submit:
1515
1616
```cpp
17-
auto frame = GpuFrame::begin (ctx);
17+
auto frame = GpuFrame::begin (device);
1818
if (! frame.isValid())
1919
return; // No GPU context.
2020
@@ -62,7 +62,7 @@ auto pass = canvas->beginRenderPass (frame, { true, background });
6262
if (! pass.isValid())
6363
return;
6464
65-
pass.setPipeline (*pipeline);
65+
pass.setPipeline (pipeline);
6666
pass.setUniformBuffer (0, 0, &uniforms, sizeof uniforms);
6767
pass.setTexture (0, 1, sceneTexture);
6868
pass.setVertexBuffer (0, vertexBuffer);
@@ -89,7 +89,7 @@ For a fullscreen post-process that generates its vertices from the vertex index,
8989
bind **no** vertex buffers and issue a three-vertex draw:
9090

9191
```cpp
92-
pass.setPipeline (*blurPipeline);
92+
pass.setPipeline (blurPipeline);
9393
pass.setTexture (0, 0, sourceTexture);
9494
pass.setUniformBuffer (0, 1, &blurParams, sizeof blurParams);
9595
pass.draw (3); // fullscreen triangle
@@ -103,8 +103,8 @@ Controls attachment load behavior for a pass:
103103
```cpp
104104
struct GpuRenderOptions
105105
{
106-
bool clear = true; // clear vs. load existing contents
107-
Color clearColor = Colors::transparentBlack; // used when clear == true
106+
bool clear = true; // clear vs. load existing contents
107+
GpuColor clearColor = Colors::transparentBlack; // used when clear == true
108108
};
109109
```
110110

@@ -114,7 +114,7 @@ struct GpuRenderOptions
114114

115115
```cpp
116116
// Clear to a solid background:
117-
auto pass = target->beginRenderPass (frame, { true, Colors::cornflowerblue });
117+
auto pass = target->beginRenderPass (frame, { true, Colors::black });
118118

119119
// Draw over existing contents:
120120
auto overlay = target->beginRenderPass (frame, { false, Colors::transparentBlack });

docs/graphics/rhi/index.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
# RHI - GPU Rendering Hardware Interface
22

3-
The **RHI** is YUP's backend-agnostic, low-level GPU layer. It sits below the 2D
4-
`Graphics` API and above Rive's GPU abstraction, giving you direct control
5-
over pipelines, render passes, buffers, and textures while remaining portable
6-
across Metal, Direct3D, OpenGL and OpenGL ES, WebGL2 and WebGPU, and Vulkan (in progress).
3+
The **RHI** is YUP's backend-agnostic, low-level GPU layer, provided by the
4+
`yup_rhi` module. It sits below the 2D `Graphics` API and above Rive's GPU
5+
abstraction, giving you direct control over pipelines, render passes, buffers,
6+
and textures while remaining portable across Metal, Direct3D, OpenGL / OpenGL ES,
7+
WebGL2, WebGPU, and Vulkan (in progress).
78

89
Use the RHI when you need custom GPU work that the 2D `Graphics` API does not
910
express - 3D geometry, post-process effects, compute-style fullscreen passes, or
10-
offscreen render-to-texture pipelines.
11+
offscreen render-to-texture pipelines. For GPU compute without any window or
12+
graphics (e.g. audio DSP on the GPU), use `GpuDevice` directly — no
13+
`GraphicsContext` or `yup_graphics` dependency needed.
1114

1215
## When to use the RHI
1316

@@ -21,6 +24,8 @@ offscreen render-to-texture pipelines.
2124

2225
## Classes at a glance
2326

27+
- **`GpuDevice`** - a reference-counted GPU device abstraction. Owns the native
28+
device and command queue. Factories for all RHI resources start here.
2429
- **`GpuFrame`** - RAII scope for one frame's GPU work. Begin, encode passes,
2530
submit.
2631
- **`GpuRenderPass`** - records draw commands (pipeline, bindings, draws) into a

docs/graphics/rhi/pipelines.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ rendering** (indexed or non-indexed) with vertex buffers, culling, and
1111
depth/stencil state.
1212

1313
```{note}
14-
Compiling a pipeline requires GPU context in `GraphicsContext` to exist.
14+
Compiling a pipeline requires a `GpuDevice` with GPU context available.
1515
Check `ctx.isGpuAvailable()` first.
1616
```
1717

@@ -36,7 +36,7 @@ assignment stays consistent across all targets.
3636

3737
```cpp
3838
ResultValue<GpuPipeline::Ptr> GpuPipeline::compileFromBundle (
39-
GraphicsContext& ctx,
39+
GpuDevice::Ptr ctx,
4040
const ShaderBundle& bundle,
4141
const GpuPipelineOptions& options = {});
4242
```
@@ -70,7 +70,7 @@ pre-compiled RSTB binding-map blob - see [Binding maps](#binding-maps).
7070

7171
```cpp
7272
ResultValue<GpuPipeline::Ptr> GpuPipeline::compile (
73-
GraphicsContext& ctx,
73+
GpuDevice::Ptr ctx,
7474
const GpuShaderSource& vertexShader,
7575
const GpuShaderSource& fragmentShader,
7676
const GpuPipelineOptions& options = {});
@@ -233,5 +233,5 @@ if (result.wasOk())
233233
| `setMaxEntries (n)` / `getMaxEntries()` | LRU eviction limit (0 = unlimited; default 256). |
234234
| `generateCacheKey (bundle, options, api)` | Static SHA1-based key generation. |
235235
236-
The cache references an externally-owned `GraphicsContext` that must outlive it.
236+
The cache references an keeps the `GpuDevice` alive via `Ptr`.
237237
Eviction is LRU by access order.

docs/graphics/rhi/spinning-cube.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ The cube uses a vertex + fragment shader pair. With the transpiler enabled, GLSL
3939

4040
```cpp
4141
GpuPipelineOptions options;
42-
options.vertexBuffers = &cubeLayout; // position/color/normal
43-
options.vertexBufferCount = 1;
44-
options.indexFormat = GpuIndexFormat::uint16;
45-
options.cullMode = GpuCullMode::back;
46-
options.winding = GpuFaceWinding::counterClockwise;
42+
options.vertexBuffers = &cubeLayout; // position/color/normal
43+
options.vertexBufferCount = 1;
44+
options.indexFormat = GpuIndexFormat::uint16;
45+
options.cullMode = GpuCullMode::back;
46+
options.winding = GpuFaceWinding::counterClockwise;
4747
options.depthStencil.enabled = true;
4848

4949
auto result = GpuPipeline::compileFromGlsl (ctx, vertGlsl, fragGlsl, options);
@@ -85,10 +85,10 @@ Each frame, begin a `GpuFrame`, open a render pass on the scene canvas, bind the
8585
pipeline + per-frame uniforms + geometry, and issue an indexed draw:
8686

8787
```cpp
88-
auto frame = GpuFrame::begin (ctx);
88+
auto frame = GpuFrame::begin (device);
8989

9090
auto pass = sceneCanvas->beginRenderPass (frame, { true, Colors::black });
91-
pass.setPipeline (*cubePipeline);
91+
pass.setPipeline (cubePipeline);
9292
pass.setUniformBuffer (0, 0, &mvp, sizeof mvp); // per-frame transform
9393
pass.setTexture (0, 1, animatedTexture);
9494
pass.setVertexBuffer (0, cubeVerts);
@@ -107,14 +107,14 @@ samples the previous result and generates its vertices from the vertex index:
107107
GpuRenderOptions load { false, Colors::transparentBlack };
108108
109109
auto hPass = blurCanvasH->beginRenderPass (frame, { true, Colors::transparentBlack });
110-
hPass.setPipeline (*blurPipeline);
110+
hPass.setPipeline (blurPipeline);
111111
hPass.setTexture (0, 0, sceneCanvas->asTexture());
112112
hPass.setUniformBuffer (0, 1, &horizontalParams, sizeof horizontalParams);
113113
hPass.draw (3);
114114
hPass.finish();
115115
116116
auto vPass = blurCanvasV->beginRenderPass (frame, { true, Colors::transparentBlack });
117-
vPass.setPipeline (*blurPipeline);
117+
vPass.setPipeline (blurPipeline);
118118
vPass.setTexture (0, 0, blurCanvasH->asTexture());
119119
vPass.setUniformBuffer (0, 1, &verticalParams, sizeof verticalParams);
120120
vPass.draw (3);

docs/graphics/rhi/targets.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,16 @@ The minimal offscreen render surface. It allocates a backing texture from the
2121
context's main render context - no dedicated 2D context is created.
2222

2323
```cpp
24-
static GpuTarget::Ptr GpuTarget::create (GraphicsContext& ctx, int width, int height);
24+
static GpuTarget::Ptr GpuTarget::create (GpuDevice::Ptr ctx, int width, int height);
2525
```
2626
2727
```cpp
2828
auto target = GpuTarget::create (ctx, 256, 256);
2929
if (target != nullptr)
3030
{
31-
auto frame = GpuFrame::begin (ctx);
31+
auto frame = GpuFrame::begin (device);
3232
auto pass = target->beginRenderPass (frame, { true, Colors::transparentBlack });
33-
pass.setPipeline (*pipeline);
33+
pass.setPipeline (pipeline);
3434
pass.draw (3);
3535
pass.finish();
3636
frame.submit();
@@ -52,11 +52,11 @@ if (target != nullptr)
5252
Builds on a `GpuTarget` but is backed by a **dedicated render context**, adding
5353
2D `Graphics` drawing via `beginDraw()` / `commit()`. It consolidates creation,
5454
rendering, and readback of an offscreen surface into one object, replacing the
55-
lower-level `GraphicsContext::createOffscreenTarget` / `beginOffscreen` /
55+
lower-level `GpuDevice::createOffscreenTarget` / `beginOffscreen` /
5656
`endOffscreen` API.
5757

5858
```cpp
59-
static GpuCanvas::Ptr GpuCanvas::create (GraphicsContext& ctx, int width, int height);
59+
static GpuCanvas::Ptr GpuCanvas::create (GpuDevice::Ptr ctx, int width, int height);
6060
```
6161
6262
### 2D drawing path
@@ -85,7 +85,7 @@ A canvas can also be the target of a `GpuRenderPass`, exactly like `GpuTarget`:
8585

8686
```cpp
8787
auto pass = canvas->beginRenderPass (frame, { true, background });
88-
pass.setPipeline (*pipeline);
88+
pass.setPipeline (pipeline);
8989
pass.drawIndexed (indexCount);
9090
pass.finish();
9191
```

docs/modules.md

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,17 +120,37 @@ flowchart LR
120120
classDef opt fill:#fff7ed,color:#9a3412,stroke:#fb923c,stroke-dasharray:2 2;
121121
```
122122

123+
### yup_rhi
124+
125+
The low-level GPU abstraction layer: device management, compute and render
126+
pipelines, frames, render passes, buffers, textures, and offscreen targets.
127+
This is the foundation for GPU work that does not require a window or Rive
128+
vector rendering — use it directly for GPU compute (audio DSP, FFT) without
129+
pulling in the 2D graphics stack.
130+
131+
```mermaid
132+
flowchart LR
133+
yup_rhi:::self --> yup_core
134+
yup_rhi --> yup_shading
135+
yup_rhi --> rive_renderer:::ext
136+
classDef self fill:#6366f1,color:#fff,stroke:#4f46e5;
137+
classDef ext fill:#f3f4f6,color:#374151,stroke:#9ca3af,stroke-dasharray:4 3;
138+
```
139+
123140
### yup_graphics
124141

125-
The 2D drawing stack and the low-level GPU RHI, rendered through the Rive
126-
renderer. Covers the graphics context, primitives, paths, fonts, SVG, imaging,
127-
and GPU pipelines. Image-codec support is optional: link `libpng`, `libjpeg`,
128-
`libwebp`, and/or `libgif` to enable the corresponding [image formats](imaging/loading.md#available-formats).
142+
The 2D drawing stack and windowed rendering, layered on `yup_rhi` and the Rive
143+
renderer. Covers the `GraphicsContext` (window + swapchain), `Graphics` (2D
144+
drawing API), primitives, paths, fonts, SVG, imaging, and `GpuCanvas` (offscreen
145+
2D surfaces). Image-codec support is optional: link `libpng`, `libjpeg`,
146+
`libwebp`, `libgif`, and/or `libtiff` to enable the corresponding
147+
[image formats](imaging/loading.md#available-formats).
129148

130149
```mermaid
131150
flowchart LR
132151
yup_graphics:::self --> yup_core
133152
yup_graphics --> yup_simd
153+
yup_graphics --> yup_rhi
134154
yup_graphics --> yup_shading
135155
yup_graphics --> rive:::ext
136156
yup_graphics --> rive_renderer:::ext
@@ -139,6 +159,7 @@ flowchart LR
139159
yup_graphics -. optional .-> libjpeg:::opt
140160
yup_graphics -. optional .-> libwebp:::opt
141161
yup_graphics -. optional .-> libgif:::opt
162+
yup_graphics -. optional .-> libtiff:::opt
142163
classDef self fill:#6366f1,color:#fff,stroke:#4f46e5;
143164
classDef ext fill:#f3f4f6,color:#374151,stroke:#9ca3af,stroke-dasharray:4 3;
144165
classDef opt fill:#fff7ed,color:#9a3412,stroke:#fb923c,stroke-dasharray:2 2;
@@ -368,10 +389,13 @@ flowchart TD
368389
simd[yup_simd] --> core[yup_core]
369390
events[yup_events] --> core
370391
shading[yup_shading] --> core
392+
rhi[yup_rhi] --> core
393+
rhi --> shading
371394
python[yup_python] --> core
372395
373396
graphics[yup_graphics] --> core
374397
graphics --> simd
398+
graphics --> rhi
375399
graphics --> shading
376400
377401
animation[yup_animation] --> core

0 commit comments

Comments
 (0)