1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
|
#pragma once
#include <RmlUi/Core/RenderInterface.h>
#include <RmlUi/Core/Types.h>
#include <bitset>
#include <unordered_map>
#include <unordered_set>
enum class ProgramId;
enum class UniformId;
class RenderLayerStack;
namespace Gfx {
struct ProgramData;
struct FramebufferData;
} // namespace Gfx
class RenderInterface_GL3 : public Rml::RenderInterface {
public:
RenderInterface_GL3();
~RenderInterface_GL3();
// Returns true if the renderer was successfully constructed.
explicit operator bool() const { return static_cast<bool>(program_data); }
// The viewport should be updated whenever the window size changes.
void SetViewport(int viewport_width, int viewport_height, int viewport_offset_x = 0, int viewport_offset_y = 0);
// unbox: redirect EndFrame()'s final composite from the default
// framebuffer (0) into the given FBO name. The bridge points this at its
// own offscreen, wlr_buffer-backed FBO so the result is compositable.
// When flip_y is true, the final composite is vertically flipped so the
// output FBO is top-row-first (wlr_buffer convention) despite GL's
// bottom-left framebuffer origin.
void SetOutputFramebuffer(unsigned int framebuffer, bool flip_y = false)
{
output_framebuffer = framebuffer;
output_flip_y = flip_y;
}
// Sets up OpenGL states for taking rendering commands from RmlUi.
void BeginFrame();
// Draws the result to the backbuffer and restores OpenGL state.
void EndFrame();
// Optional, can be used to clear the active framebuffer.
void Clear();
// -- Inherited from Rml::RenderInterface --
Rml::CompiledGeometryHandle CompileGeometry(Rml::Span<const Rml::Vertex> vertices, Rml::Span<const int> indices) override;
void RenderGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture) override;
void ReleaseGeometry(Rml::CompiledGeometryHandle handle) override;
Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) override;
Rml::TextureHandle GenerateTexture(Rml::Span<const Rml::byte> source_data, Rml::Vector2i source_dimensions) override;
void ReleaseTexture(Rml::TextureHandle texture_handle) override;
void EnableScissorRegion(bool enable) override;
void SetScissorRegion(Rml::Rectanglei region) override;
void EnableClipMask(bool enable) override;
void RenderToClipMask(Rml::ClipMaskOperation mask_operation, Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation) override;
void SetTransform(const Rml::Matrix4f* transform) override;
Rml::LayerHandle PushLayer() override;
void CompositeLayers(Rml::LayerHandle source, Rml::LayerHandle destination, Rml::BlendMode blend_mode,
Rml::Span<const Rml::CompiledFilterHandle> filters) override;
void PopLayer() override;
Rml::TextureHandle SaveLayerAsTexture() override;
Rml::CompiledFilterHandle SaveLayerAsMaskImage() override;
Rml::CompiledFilterHandle CompileFilter(const Rml::String& name, const Rml::Dictionary& parameters) override;
void ReleaseFilter(Rml::CompiledFilterHandle filter) override;
Rml::CompiledShaderHandle CompileShader(const Rml::String& name, const Rml::Dictionary& parameters) override;
void RenderShader(Rml::CompiledShaderHandle shader_handle, Rml::CompiledGeometryHandle geometry_handle, Rml::Vector2f translation,
Rml::TextureHandle texture) override;
void ReleaseShader(Rml::CompiledShaderHandle effect_handle) override;
// Can be passed to RenderGeometry() to enable texture rendering without changing the bound texture.
static constexpr Rml::TextureHandle TextureEnableWithoutBinding = Rml::TextureHandle(-1);
// Can be passed to RenderGeometry() to leave the bound texture and used program unchanged.
static constexpr Rml::TextureHandle TexturePostprocess = Rml::TextureHandle(-2);
// -- Utility functions for clients --
const Rml::Matrix4f& GetTransform() const;
void ResetProgram();
// unbox (slice-10 preview spike): register an externally-OWNED GL texture
// under a "source" string (the "unbox-preview://N" URI). LoadTexture()
// resolves that exact source to `texture_id` + `dimensions`, so an
// <img src="unbox-preview://N"> samples it. The texture's GL lifetime is the
// CALLER's (the substrate's Preview): ReleaseTexture() must NOT delete it
// when RmlUi drops its handle, so registered ids are tracked and skipped
// there. unregister_preview_texture() removes the mapping on Preview
// destruction (the caller deletes the GL texture itself, after).
void register_preview_texture(const Rml::String& source, unsigned int texture_id, Rml::Vector2i dimensions);
void unregister_preview_texture(const Rml::String& source);
private:
void UseProgram(ProgramId program_id);
int GetUniformLocation(UniformId uniform_id) const;
void SubmitTransformUniform(Rml::Vector2f translation);
void BlitLayerToPostprocessPrimary(Rml::LayerHandle layer_handle);
void RenderFilters(Rml::Span<const Rml::CompiledFilterHandle> filter_handles);
void SetScissor(Rml::Rectanglei region, bool vertically_flip = false);
void DrawFullscreenQuad();
void DrawFullscreenQuad(Rml::Vector2f uv_offset, Rml::Vector2f uv_scaling = Rml::Vector2f(1.f));
void RenderBlur(float sigma, const Gfx::FramebufferData& source_destination, const Gfx::FramebufferData& temp, Rml::Rectanglei window_flipped);
static constexpr size_t MaxNumPrograms = 32;
std::bitset<MaxNumPrograms> program_transform_dirty;
Rml::Matrix4f transform;
Rml::Matrix4f projection;
ProgramId active_program = {};
Rml::Rectanglei scissor_state;
int viewport_width = 0;
int viewport_height = 0;
int viewport_offset_x = 0;
int viewport_offset_y = 0;
// unbox: EndFrame() composite target (0 = default backbuffer) and whether
// to vertically flip into it (GL bottom-left origin -> top-first buffer).
unsigned int output_framebuffer = 0;
bool output_flip_y = false;
Rml::CompiledGeometryHandle fullscreen_quad_geometry = {};
Rml::UniquePtr<const Gfx::ProgramData> program_data;
/*
Manages render targets, including the layer stack and postprocessing framebuffers.
Layers can be pushed and popped, creating new framebuffers as needed. Typically, geometry is rendered to the top
layer. The layer framebuffers may have MSAA enabled.
Postprocessing framebuffers are separate from the layers, and are commonly used to apply texture-wide effects
such as filters. They are used both as input and output during rendering, and do not use MSAA.
*/
class RenderLayerStack {
public:
RenderLayerStack();
~RenderLayerStack();
// Push a new layer. All references to previously retrieved layers are invalidated.
Rml::LayerHandle PushLayer();
// Pop the top layer. All references to previously retrieved layers are invalidated.
void PopLayer();
const Gfx::FramebufferData& GetLayer(Rml::LayerHandle layer) const;
const Gfx::FramebufferData& GetTopLayer() const;
Rml::LayerHandle GetTopLayerHandle() const;
const Gfx::FramebufferData& GetPostprocessPrimary() { return EnsureFramebufferPostprocess(0); }
const Gfx::FramebufferData& GetPostprocessSecondary() { return EnsureFramebufferPostprocess(1); }
const Gfx::FramebufferData& GetPostprocessTertiary() { return EnsureFramebufferPostprocess(2); }
const Gfx::FramebufferData& GetBlendMask() { return EnsureFramebufferPostprocess(3); }
void SwapPostprocessPrimarySecondary();
void BeginFrame(int new_width, int new_height);
void EndFrame();
private:
void DestroyFramebuffers();
const Gfx::FramebufferData& EnsureFramebufferPostprocess(int index);
int width = 0, height = 0;
// The number of active layers is manually tracked since we re-use the framebuffers stored in the fb_layers stack.
int layers_size = 0;
Rml::Vector<Gfx::FramebufferData> fb_layers;
Rml::Vector<Gfx::FramebufferData> fb_postprocess;
};
RenderLayerStack render_layers;
// unbox (slice-10 preview spike): externally-owned preview textures, keyed
// by their "unbox-preview://N" source string. LoadTexture() resolves a hit
// here; ReleaseTexture() skips any id present in preview_texture_ids so the
// substrate's Preview keeps sole ownership of the GL texture's lifetime.
struct PreviewTexture {
unsigned int texture_id;
Rml::Vector2i dimensions;
};
std::unordered_map<Rml::String, PreviewTexture> preview_textures;
std::unordered_set<unsigned int> preview_texture_ids;
struct GLStateBackup {
bool enable_cull_face;
bool enable_blend;
bool enable_stencil_test;
bool enable_scissor_test;
bool enable_depth_test;
int viewport[4];
int scissor[4];
int active_texture;
int stencil_clear_value;
float color_clear_value[4];
unsigned char color_writemask[4];
int blend_equation_rgb;
int blend_equation_alpha;
int blend_src_rgb;
int blend_dst_rgb;
int blend_src_alpha;
int blend_dst_alpha;
struct Stencil {
int func;
int ref;
int value_mask;
int writemask;
int fail;
int pass_depth_fail;
int pass_depth_pass;
};
Stencil stencil_front;
Stencil stencil_back;
};
GLStateBackup glstate_backup = {};
};
/**
Helper functions for the OpenGL 3 renderer.
*/
namespace RmlGL3 {
// Loads OpenGL functions. Optionally, the out message describes the loaded GL version or an error message on failure.
bool Initialize(Rml::String* out_message = nullptr);
// Unloads OpenGL functions.
void Shutdown();
} // namespace RmlGL3
|