Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion Include/RmlUi/Core/RenderBox.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ class RenderBox {
EdgeSizes border_widths;
CornerSizes border_radius;
};

inline bool operator==(const RenderBox& a, const RenderBox& b)
{
return a.GetFillSize() == b.GetFillSize() && a.GetBorderOffset() == b.GetBorderOffset() && a.GetBorderWidths() == b.GetBorderWidths() && a.GetBorderRadius() == b.GetBorderRadius();
}
inline bool operator!=(const RenderBox& a, const RenderBox& b)
{
return !(a == b);
}
} // namespace Rml
#endif
94 changes: 91 additions & 3 deletions Include/RmlUi/Core/RenderManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,92 @@
#define RMLUI_CORE_RENDERMANAGER_H

#include "CallbackTexture.h"
#include "DecorationTypes.h"
#include "Mesh.h"
#include "RenderBox.h"
#include "RenderInterface.h"
#include "StableVector.h"
#include "Types.h"
#include "Utilities.h"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some remnant includes and unnecessary changes now. I'm just making a note here to remember to revert these files that no longer have meaningful changes.


namespace Rml {
using RenderBoxList = Vector<RenderBox>;
struct BoxShadowGeometryInfo {
ColourbPremultiplied background_color;
Array<ColourbPremultiplied, 4> border_colors;
CornerSizes border_radius;
Vector2i texture_dimensions;
Vector2f element_offset_in_texture;
RenderBoxList padding_render_boxes;
RenderBoxList border_render_boxes;
BoxShadowList shadow_list;
};
inline bool operator==(const BoxShadowGeometryInfo& a, const BoxShadowGeometryInfo& b)
{
return a.background_color == b.background_color && a.border_colors == b.border_colors && a.border_radius == b.border_radius && a.texture_dimensions == b.texture_dimensions &&
a.element_offset_in_texture == b.element_offset_in_texture && a.padding_render_boxes == b.padding_render_boxes && a.border_render_boxes == b.border_render_boxes && a.shadow_list == b.shadow_list;
}
inline bool operator!=(const BoxShadowGeometryInfo& a, const BoxShadowGeometryInfo& b)
{
return !(a == b);
}
}

template<> struct std::hash<Rml::BoxShadowGeometryInfo> {
std::size_t operator()(const Rml::BoxShadowGeometryInfo& in) const noexcept {
using namespace Rml;
using namespace Rml::Utilities;
using namespace std;
size_t result = size_t(849128392);

HashCombine(result, reinterpret_cast<const uint32_t&>(in.background_color));
for (const auto& v : in.border_colors) {
HashCombine(result, reinterpret_cast<const uint32_t&>(v));
}

for (const auto& v : in.border_radius) {
HashCombine(result, v);
}
HashCombine(result, in.texture_dimensions.x);
HashCombine(result, in.texture_dimensions.y);
HashCombine(result, in.element_offset_in_texture.x);
HashCombine(result, in.element_offset_in_texture.y);

static const auto fn_hash_render_box = [](size_t& result, const RenderBox& v) {
HashCombine(result, v.GetFillSize().x);
HashCombine(result, v.GetFillSize().y);
HashCombine(result, v.GetBorderOffset().x);
HashCombine(result, v.GetBorderOffset().y);
for (const auto& w : v.GetBorderRadius()) {
HashCombine(result, w);
}
for (const auto& w : v.GetBorderWidths()) {
HashCombine(result, w);
}
};
for (const auto& v : in.padding_render_boxes) {
fn_hash_render_box(result, v);
}
for (const auto& v : in.border_render_boxes) {
fn_hash_render_box(result, v);
}
for (const auto& v : in.shadow_list) {
HashCombine(result, v.blur_radius.number);
HashCombine(result, v.blur_radius.unit);
HashCombine(result, reinterpret_cast<const uint32_t&>(v.color));
HashCombine(result, v.inset);
HashCombine(result, v.offset_x.number);
HashCombine(result, v.offset_x.unit);
HashCombine(result, v.offset_y.number);
HashCombine(result, v.offset_y.unit);
HashCombine(result, v.spread_distance.number);
HashCombine(result, v.spread_distance.unit);
}
return result;
}
};

namespace Rml {
class Geometry;
class CompiledFilter;
class CompiledShader;
Expand Down Expand Up @@ -67,10 +146,10 @@ struct RenderState {
};

/**
A wrapper over the render interface, which tracks its state and resources.
A wrapper over the render interface, which tracks its state and resources.

All operations to be submitted to the render interface should go through this class.
*/
All operations to be submitted to the render interface should go through this class.
*/
class RMLUICORE_API RenderManager : NonCopyMoveable {
public:
RenderManager(RenderInterface* render_interface);
Expand Down Expand Up @@ -101,6 +180,8 @@ class RMLUICORE_API RenderManager : NonCopyMoveable {
Texture LoadTexture(const String& source, const String& document_path = String());
CallbackTexture MakeCallbackTexture(CallbackTextureFunction callback);

SharedPtr<CallbackTexture> FindOrMakeBoxShadowCallbackTexture(const BoxShadowGeometryInfo& geometry_info, CallbackTextureFunction callback);

CompiledFilter CompileFilter(const String& name, const Dictionary& parameters);
CompiledShader CompileShader(const String& name, const Dictionary& parameters);

Expand Down Expand Up @@ -133,6 +214,11 @@ class RMLUICORE_API RenderManager : NonCopyMoveable {
void ReleaseResource(const CompiledFilter& filter);
void ReleaseResource(const CompiledShader& shader);

// TODO: better way to autonomously release the box shadow cache?
// References are needed to texture, and we need to be able to ref count it.
// Another possibility is making a custom SharedPtr<> class and allow us to manually reduce ref counts.
void CleanupDeadBoxShadowCache();

struct GeometryData {
Mesh mesh;
CompiledGeometryHandle handle = {};
Expand All @@ -143,6 +229,8 @@ class RMLUICORE_API RenderManager : NonCopyMoveable {
StableVector<GeometryData> geometry_list;
UniquePtr<TextureDatabase> texture_database;

UnorderedMap<BoxShadowGeometryInfo, SharedPtr<CallbackTexture>> box_shadow_cache;

int compiled_filter_count = 0;
int compiled_shader_count = 0;

Expand Down
10 changes: 5 additions & 5 deletions Source/Core/ElementBackgroundBorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ void ElementBackgroundBorder::Render(Element* element)

Background* shadow = GetBackground(BackgroundType::BoxShadow);
if (shadow && shadow->geometry)
shadow->geometry.Render(element->GetAbsoluteOffset(BoxArea::Border), shadow->texture);
shadow->geometry.Render(element->GetAbsoluteOffset(BoxArea::Border), *shadow->texture);
else if (Background* background = GetBackground(BackgroundType::BackgroundBorder))
{
auto offset = element->GetAbsoluteOffset(BoxArea::Border);
Expand Down Expand Up @@ -139,7 +139,7 @@ void ElementBackgroundBorder::GenerateGeometry(Element* element)
};

ColourbPremultiplied background_color = ConvertColor(computed.background_color());
ColourbPremultiplied border_colors[4] = {
Array<ColourbPremultiplied, 4> border_colors = {
ConvertColor(computed.border_top_color()),
ConvertColor(computed.border_right_color()),
ConvertColor(computed.border_bottom_color()),
Expand All @@ -151,7 +151,7 @@ void ElementBackgroundBorder::GenerateGeometry(Element* element)
Mesh mesh = geometry.Release(Geometry::ReleaseMode::ClearMesh);

for (int i = 0; i < element->GetNumBoxes(); i++)
MeshUtilities::GenerateBackgroundBorder(mesh, element->GetRenderBox(BoxArea::Padding, i), background_color, border_colors);
MeshUtilities::GenerateBackgroundBorder(mesh, element->GetRenderBox(BoxArea::Padding, i), background_color, border_colors.data());

geometry = render_manager->MakeGeometry(std::move(mesh));

Expand All @@ -166,10 +166,10 @@ void ElementBackgroundBorder::GenerateGeometry(Element* element)
// Generate the geometry for the box-shadow texture.
Background& shadow_background = GetOrCreateBackground(BackgroundType::BoxShadow);
Geometry& shadow_geometry = shadow_background.geometry;
CallbackTexture& shadow_texture = shadow_background.texture;
SharedPtr<CallbackTexture>& shadow_texture = shadow_background.texture;

GeometryBoxShadow::Generate(shadow_geometry, shadow_texture, *render_manager, element, background_border_geometry, std::move(shadow_list),
border_radius, computed.opacity());
border_radius, background_color, border_colors, computed.opacity());
}
}

Expand Down
3 changes: 1 addition & 2 deletions Source/Core/ElementBackgroundBorder.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ namespace Rml {
class ElementBackgroundBorder {
public:
ElementBackgroundBorder();

void Render(Element* element);

void DirtyBackground();
Expand All @@ -50,7 +49,7 @@ class ElementBackgroundBorder {
enum class BackgroundType { BackgroundBorder, BoxShadow, ClipBorder, ClipPadding, ClipContent, Count };
struct Background {
Geometry geometry;
CallbackTexture texture;
SharedPtr<CallbackTexture> texture;
};

Background* GetBackground(BackgroundType type);
Expand Down
57 changes: 44 additions & 13 deletions Source/Core/GeometryBoxShadow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
*/

#include "GeometryBoxShadow.h"
#include "ElementBackgroundBorder.h"
#include "../../Include/RmlUi/Core/Box.h"
#include "../../Include/RmlUi/Core/CompiledFilterShader.h"
#include "../../Include/RmlUi/Core/DecorationTypes.h"
Expand All @@ -38,8 +39,9 @@

namespace Rml {

void GeometryBoxShadow::Generate(Geometry& out_shadow_geometry, CallbackTexture& out_shadow_texture, RenderManager& render_manager, Element* element,
Geometry& background_border_geometry, BoxShadowList shadow_list, const CornerSizes border_radius, const float opacity)
void GeometryBoxShadow::Generate(Geometry& out_shadow_geometry, SharedPtr<CallbackTexture>& out_shadow_texture, RenderManager& render_manager,
Element* element, const Geometry& background_border_geometry, BoxShadowList shadow_list, CornerSizes border_radius,
ColourbPremultiplied background_color, Array<ColourbPremultiplied, 4> border_colors, float opacity)
{
// Find the box-shadow texture dimension and offset required to cover all box-shadows and element boxes combined.
Vector2f element_offset_in_texture;
Expand Down Expand Up @@ -86,12 +88,40 @@ void GeometryBoxShadow::Generate(Geometry& out_shadow_geometry, CallbackTexture&
texture_dimensions = Vector2i(texture_region.Size());
}

// Since we can reuse textures across multiple box shadows with the same properties,
// we need to copy the element's box shadow list and the background and border geometry.
RenderBoxList padding_render_boxes{};
RenderBoxList border_render_boxes{};

for (int i = 0; i < element->GetNumBoxes(); i++)
{
padding_render_boxes.push_back(element->GetRenderBox(BoxArea::Padding, i));
border_render_boxes.push_back(element->GetRenderBox(BoxArea::Border, i));
}

// Finally, create cache information
// We do not need to copy over the Rml::Geometry as the other properties here already implicitly define them.
BoxShadowGeometryInfo geometry_info;
geometry_info.background_color = background_color;
geometry_info.border_colors = border_colors;
geometry_info.border_radius = border_radius;
geometry_info.texture_dimensions = texture_dimensions;
geometry_info.element_offset_in_texture = element_offset_in_texture;
geometry_info.padding_render_boxes = padding_render_boxes;
geometry_info.border_render_boxes = border_render_boxes;
geometry_info.shadow_list = shadow_list;

Rml::Mesh background_border_mesh = background_border_geometry.GetMesh();

// Callback for generating the box-shadow texture. Using a callback ensures that the texture can be regenerated at any time, for example if the
// device loses its GPU context and the client calls Rml::ReleaseTextures().
auto texture_callback = [&background_border_geometry, element, border_radius, texture_dimensions, element_offset_in_texture,
auto texture_callback = [border_radius, texture_dimensions, element_offset_in_texture, num_boxes = element->GetNumBoxes(),
background_border_mesh = std::move(background_border_mesh),
padding_render_boxes = std::move(padding_render_boxes),
border_render_boxes = std::move(border_render_boxes),
shadow_list = std::move(shadow_list)](const CallbackTextureInterface& texture_interface) -> bool {
RenderManager& render_manager = texture_interface.GetRenderManager();

Mesh mesh_padding; // Render geometry for inner box-shadow.
Mesh mesh_padding_border; // Clipping mask for outer box-shadow.

Expand All @@ -106,13 +136,13 @@ void GeometryBoxShadow::Generate(Geometry& out_shadow_geometry, CallbackTexture&
}

// Generate the geometry for all the element's boxes and extend the render-texture further to cover all of them.
for (int i = 0; i < element->GetNumBoxes(); i++)
for (int i = 0; i < num_boxes; i++)
{
ColourbPremultiplied white(255);
if (has_inner_shadow)
MeshUtilities::GenerateBackground(mesh_padding, element->GetRenderBox(BoxArea::Padding, i), white);
MeshUtilities::GenerateBackground(mesh_padding, padding_render_boxes[i], white);
if (has_outer_shadow)
MeshUtilities::GenerateBackground(mesh_padding_border, element->GetRenderBox(BoxArea::Border, i), white);
MeshUtilities::GenerateBackground(mesh_padding_border, border_render_boxes[i], white);
}

const RenderState initial_render_state = render_manager.GetState();
Expand All @@ -132,13 +162,14 @@ void GeometryBoxShadow::Generate(Geometry& out_shadow_geometry, CallbackTexture&
{
Log::Message(Log::LT_INFO,
"The desired box-shadow texture dimensions (%d, %d) are larger than the current window region (%d, %d). "
"Results may be clipped. In element: %s",
texture_dimensions.x, texture_dimensions.y, scissor_region.Width(), scissor_region.Height(), element->GetAddress().c_str());
"Results may be clipped. ", // FIXME: how do we log this? now that elements share box shadows? //In element: %s",
texture_dimensions.x, texture_dimensions.y, scissor_region.Width(), scissor_region.Height()/*, element->GetAddress().c_str()*/);
}

render_manager.PushLayer();

Rml::Geometry background_border_geometry = render_manager.MakeGeometry(Rml::Mesh(background_border_mesh));
background_border_geometry.Render(element_offset_in_texture);
background_border_geometry.Release();

for (int shadow_index = (int)shadow_list.size() - 1; shadow_index >= 0; shadow_index--)
{
Expand All @@ -164,10 +195,10 @@ void GeometryBoxShadow::Generate(Geometry& out_shadow_geometry, CallbackTexture&
Mesh mesh_shadow;

// Generate the shadow geometry. For outer box-shadows it is rendered normally, while for inset box-shadows it is used as a clipping mask.
for (int i = 0; i < element->GetNumBoxes(); i++)
for (int i = 0; i < num_boxes; i++)
{
const float signed_spread_distance = (inset ? -spread_distance : spread_distance);
RenderBox render_box = element->GetRenderBox(inset ? BoxArea::Padding : BoxArea::Border, i);
RenderBox render_box = (inset ? padding_render_boxes : border_render_boxes)[i];
render_box.SetFillSize(Math::Max(render_box.GetFillSize() + Vector2f(2.f * signed_spread_distance), Vector2f{0.001f}));
render_box.SetBorderRadius(spread_radii);
render_box.SetBorderOffset(render_box.GetBorderOffset() - Vector2f(signed_spread_distance));
Expand Down Expand Up @@ -227,7 +258,7 @@ void GeometryBoxShadow::Generate(Geometry& out_shadow_geometry, CallbackTexture&
const byte alpha = byte(opacity * 255.f);
MeshUtilities::GenerateQuad(mesh, -element_offset_in_texture, Vector2f(texture_dimensions), ColourbPremultiplied(alpha, alpha));

out_shadow_texture = render_manager.MakeCallbackTexture(std::move(texture_callback));
out_shadow_texture = render_manager.FindOrMakeBoxShadowCallbackTexture(geometry_info, std::move(texture_callback));
out_shadow_geometry = render_manager.MakeGeometry(std::move(mesh));
}

Expand Down
12 changes: 7 additions & 5 deletions Source/Core/GeometryBoxShadow.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,18 @@ class GeometryBoxShadow {
public:
/// Generate the texture and geometry for a box shadow.
/// @param[out] out_shadow_geometry The target geometry.
/// @param[out] out_shadow_texture The target texture, assumes pointer stability during the lifetime of the shadow geometry.
/// @param[out] out_shadow_texture The target texture, assumes pointer stability during the lifetime of the shadow geometry. This may also be shared between
/// @param[in] render_manager The render manager to generate the shadow for.
/// @param[in] element The element to generate the shadow for.
/// @param[in] background_border_geometry The geometry of the background and border, assumed to already have been generated. Assumes pointer
/// stability during the lifetime of the shadow geometry.
/// @param[in] background_border_geometry The geometry of the background and border. Used to avoid recomputing the mesh from the given render boxes and background/border colours
/// @param[in] shadow_list The list of box-shadows to generate.
/// @param[in] border_radius The border radius of the element.
/// @param[in] background_color The background colour of the element.
/// @param[in] border_colors The border colours of the element.
/// @param[in] opacity The opacity of the element.
static void Generate(Geometry& out_shadow_geometry, CallbackTexture& out_shadow_texture, RenderManager& render_manager, Element* element,
Geometry& background_border_geometry, BoxShadowList shadow_list, CornerSizes border_radius, float opacity);
static void Generate(Geometry& out_shadow_geometry, SharedPtr<CallbackTexture>& out_shadow_texture, RenderManager& render_manager,
Element* element, const Geometry& background_border_geometry, BoxShadowList shadow_list, CornerSizes border_radius,
ColourbPremultiplied background_color, Array<ColourbPremultiplied, 4> border_colors, float opacity);
};

} // namespace Rml
Expand Down
Loading
Loading