Skip to content

Commit

Permalink
Refactor render interface: Require compiled geometry, use modern and …
Browse files Browse the repository at this point in the history
…safer types [breaking change]

All geometry now requires compiling, the immediate geometry rendering call is removed for the following reasons:

    1. Compiled geometry is required to use any of the new rendering features, including clip mask and shaders.
    2. It simplifies the render interface, by removing a function that essentially provided a duplicate feature set.
    3. This change enables us to make a clear distinction between required and optional functions. All required functions are now abstract, while optional functions are not.

With recent changes, migrating from the immediate render function to using the compiled geometry functions, should be much less effort. Now the pointers to the geometry data (vertices and indices) are guaranteed to be available and immutable until the same geometry is released. Thus, users can simply store the views to this data, and reuse that during rendering.

The signatures of several functions are changed:

    - The Span type is now used to represent contiguous data for improved usability and safety.
    - Texture load and generate now return the texture handle directly, to be consistent with the way geometry is compiled. The value zero is used to represent an empty texture, like before.
    - The scissor region now takes a Rectangle type for improved ergonomics.

This is a breaking change for all users, and requires some minor adaptation in existing render interfaces. The changes to the backend renderers in this commit can be used as a reference for the change.
  • Loading branch information
mikke89 committed Dec 26, 2023
1 parent 3176c85 commit 5faedc4
Show file tree
Hide file tree
Showing 34 changed files with 351 additions and 423 deletions.
31 changes: 13 additions & 18 deletions Backends/RmlUi_Backend_SDL_GL2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class RenderInterface_GL2_SDL : public RenderInterface_GL2 {
public:
RenderInterface_GL2_SDL(SDL_Renderer* renderer) : renderer(renderer) {}

void RenderCompiledGeometry(Rml::CompiledGeometryHandle handle, const Rml::Vector2f& translation, Rml::TextureHandle texture) override
void RenderGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture) override
{
SDL_Texture* sdl_texture = (SDL_Texture*)texture;
if (sdl_texture)
Expand All @@ -61,18 +61,18 @@ class RenderInterface_GL2_SDL : public RenderInterface_GL2 {
texture = RenderInterface_GL2::TextureEnableWithoutBinding;
}

RenderInterface_GL2::RenderCompiledGeometry(handle, translation, texture);
RenderInterface_GL2::RenderGeometry(handle, translation, texture);

if (sdl_texture)
SDL_GL_UnbindTexture(sdl_texture);
}

bool LoadTexture(Rml::TextureHandle& texture_handle, Rml::Vector2i& texture_dimensions, const Rml::String& source) override
Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) override
{
Rml::FileInterface* file_interface = Rml::GetFileInterface();
Rml::FileHandle file_handle = file_interface->Open(source);
if (!file_handle)
return false;
return {};

file_interface->Seek(file_handle, 0, SEEK_END);
const size_t buffer_size = file_interface->Tell(file_handle);
Expand All @@ -88,7 +88,9 @@ class RenderInterface_GL2_SDL : public RenderInterface_GL2 {

SDL_Surface* surface = IMG_LoadTyped_RW(SDL_RWFromMem(buffer.get(), int(buffer_size)), 1, extension.c_str());
if (!surface)
return false;
return {};

texture_dimensions = {surface->w, surface->h};

if (surface->format->format != SDL_PIXELFORMAT_RGBA32 && surface->format->format != SDL_PIXELFORMAT_BGRA32)
{
Expand All @@ -98,7 +100,7 @@ class RenderInterface_GL2_SDL : public RenderInterface_GL2 {
SDL_FreeSurface(surface);

if (!converted_surface)
return false;
return {};

surface = converted_surface;
}
Expand All @@ -113,18 +115,12 @@ class RenderInterface_GL2_SDL : public RenderInterface_GL2 {
}

SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);

texture_dimensions = Rml::Vector2i(surface->w, surface->h);
texture_handle = (Rml::TextureHandle)texture;
SDL_FreeSurface(surface);

if (!texture)
return false;

return true;
return (Rml::TextureHandle)texture;
}

bool GenerateTexture(Rml::TextureHandle& texture_handle, const Rml::byte* source, const Rml::Vector2i& source_dimensions) override
Rml::TextureHandle GenerateTexture(Rml::Span<const Rml::byte> source, Rml::Vector2i source_dimensions) override
{
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
Uint32 rmask = 0xff000000;
Expand All @@ -138,13 +134,12 @@ class RenderInterface_GL2_SDL : public RenderInterface_GL2 {
Uint32 amask = 0xff000000;
#endif

SDL_Surface* surface = SDL_CreateRGBSurfaceFrom((void*)source, source_dimensions.x, source_dimensions.y, 32, source_dimensions.x * 4, rmask,
gmask, bmask, amask);
SDL_Surface* surface = SDL_CreateRGBSurfaceFrom((void*)source.data(), source_dimensions.x, source_dimensions.y, 32, source_dimensions.x * 4,
rmask, gmask, bmask, amask);
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_FreeSurface(surface);
texture_handle = (Rml::TextureHandle)texture;
return true;
return (Rml::TextureHandle)texture;
}

void ReleaseTexture(Rml::TextureHandle texture_handle) override { SDL_DestroyTexture((SDL_Texture*)texture_handle); }
Expand Down
21 changes: 11 additions & 10 deletions Backends/RmlUi_Backend_SDL_GL3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ class RenderInterface_GL3_SDL : public RenderInterface_GL3 {
public:
RenderInterface_GL3_SDL() {}

bool LoadTexture(Rml::TextureHandle& texture_handle, Rml::Vector2i& texture_dimensions, const Rml::String& source) override
Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) override
{
Rml::FileInterface* file_interface = Rml::GetFileInterface();
Rml::FileHandle file_handle = file_interface->Open(source);
if (!file_handle)
return false;
return {};

file_interface->Seek(file_handle, 0, SEEK_END);
const size_t buffer_size = file_interface->Tell(file_handle);
Expand All @@ -73,10 +73,9 @@ class RenderInterface_GL3_SDL : public RenderInterface_GL3 {

SDL_Surface* surface = IMG_LoadTyped_RW(SDL_RWFromMem(buffer.get(), int(buffer_size)), 1, extension.c_str());
if (!surface)
return false;
return {};

texture_dimensions.x = surface->w;
texture_dimensions.y = surface->h;
texture_dimensions = {surface->w, surface->h};

if (surface->format->format != SDL_PIXELFORMAT_RGBA32)
{
Expand All @@ -85,24 +84,26 @@ class RenderInterface_GL3_SDL : public RenderInterface_GL3 {
SDL_FreeSurface(surface);

if (!converted_surface)
return false;
return {};

surface = converted_surface;
}

// Convert colors to premultiplied alpha, which is necessary for correct alpha compositing.
const size_t pixels_byte_size = surface->w * surface->h * 4;
byte* pixels = static_cast<byte*>(surface->pixels);
for (int i = 0; i < surface->w * surface->h * 4; i += 4)
for (size_t i = 0; i < pixels_byte_size; i += 4)
{
const byte alpha = pixels[i + 3];
for (int j = 0; j < 3; ++j)
for (size_t j = 0; j < 3; ++j)
pixels[i + j] = byte(int(pixels[i + j]) * int(alpha) / 255);
}

bool success = RenderInterface_GL3::GenerateTexture(texture_handle, (const Rml::byte*)surface->pixels, texture_dimensions);
Rml::TextureHandle texture_handle = RenderInterface_GL3::GenerateTexture({pixels, pixels_byte_size}, texture_dimensions);

SDL_FreeSurface(surface);

return success;
return texture_handle;
}
};

Expand Down
18 changes: 7 additions & 11 deletions Backends/RmlUi_Backend_SFML_GL2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,18 @@ class RenderInterface_GL2_SFML : public RenderInterface_GL2 {
public:
// -- Inherited from Rml::RenderInterface --

void RenderCompiledGeometry(Rml::CompiledGeometryHandle handle, const Rml::Vector2f& translation, Rml::TextureHandle texture) override
void RenderGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture) override
{
if (texture)
{
sf::Texture::bind((sf::Texture*)texture);
texture = RenderInterface_GL2::TextureEnableWithoutBinding;
}

RenderInterface_GL2::RenderCompiledGeometry(handle, translation, texture);
RenderInterface_GL2::RenderGeometry(handle, translation, texture);
}

bool LoadTexture(Rml::TextureHandle& texture_handle, Rml::Vector2i& texture_dimensions, const Rml::String& source) override
Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) override
{
Rml::FileInterface* file_interface = Rml::GetFileInterface();
Rml::FileHandle file_handle = file_interface->Open(source);
Expand Down Expand Up @@ -99,13 +99,11 @@ class RenderInterface_GL2_SFML : public RenderInterface_GL2 {
return false;
}

texture_handle = (Rml::TextureHandle)texture;
texture_dimensions = Rml::Vector2i(texture->getSize().x, texture->getSize().y);

return true;
return (Rml::TextureHandle)texture;
}

bool GenerateTexture(Rml::TextureHandle& texture_handle, const Rml::byte* source, const Rml::Vector2i& source_dimensions) override
Rml::TextureHandle GenerateTexture(Rml::Span<const Rml::byte> source, Rml::Vector2i source_dimensions) override
{
sf::Texture* texture = new sf::Texture();
texture->setSmooth(true);
Expand All @@ -116,11 +114,9 @@ class RenderInterface_GL2_SFML : public RenderInterface_GL2 {
return false;
}

texture->update(source, source_dimensions.x, source_dimensions.y, 0, 0);

texture_handle = (Rml::TextureHandle)texture;
texture->update(source.data(), source_dimensions.x, source_dimensions.y, 0, 0);

return true;
return (Rml::TextureHandle)texture;
}

void ReleaseTexture(Rml::TextureHandle texture_handle) override { delete (sf::Texture*)texture_handle; }
Expand Down
50 changes: 17 additions & 33 deletions Backends/RmlUi_Renderer_GL2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,35 +97,23 @@ void RenderInterface_GL2::Clear()
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}

void RenderInterface_GL2::RenderGeometry(Rml::Vertex* vertices, int num_vertices, int* indices, int num_indices, const Rml::TextureHandle texture,
const Rml::Vector2f& translation)
Rml::CompiledGeometryHandle RenderInterface_GL2::CompileGeometry(Rml::Span<const Rml::Vertex> vertices, Rml::Span<const int> indices)
{
Rml::CompiledGeometryHandle geometry = CompileGeometry(vertices, num_vertices, indices, num_indices);

if (geometry)
{
RenderCompiledGeometry(geometry, translation, texture);
ReleaseCompiledGeometry(geometry);
}
}

Rml::CompiledGeometryHandle RenderInterface_GL2::CompileGeometry(Rml::Vertex* vertices, int num_vertices, int* indices, int num_indices)
{
GeometryView* data = new GeometryView{vertices, indices, num_vertices, num_indices};
GeometryView* data = new GeometryView{vertices, indices};
return reinterpret_cast<Rml::CompiledGeometryHandle>(data);
}

void RenderInterface_GL2::ReleaseCompiledGeometry(Rml::CompiledGeometryHandle geometry)
void RenderInterface_GL2::ReleaseGeometry(Rml::CompiledGeometryHandle geometry)
{
delete reinterpret_cast<GeometryView*>(geometry);
}

void RenderInterface_GL2::RenderCompiledGeometry(Rml::CompiledGeometryHandle handle, const Rml::Vector2f& translation, Rml::TextureHandle texture)
void RenderInterface_GL2::RenderGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture)
{
const GeometryView* geometry = reinterpret_cast<GeometryView*>(handle);
const Rml::Vertex* vertices = geometry->vertices;
const int* indices = geometry->indices;
const int num_indices = geometry->num_indices;
const Rml::Vertex* vertices = geometry->vertices.data();
const int* indices = geometry->indices.data();
const int num_indices = (int)geometry->indices.size();

glPushMatrix();
glTranslatef(translation.x, translation.y, 0);
Expand Down Expand Up @@ -162,9 +150,9 @@ void RenderInterface_GL2::EnableScissorRegion(bool enable)
glDisable(GL_SCISSOR_TEST);
}

void RenderInterface_GL2::SetScissorRegion(int x, int y, int width, int height)
void RenderInterface_GL2::SetScissorRegion(Rml::Rectanglei region)
{
glScissor(x, viewport_height - (y + height), width, height);
glScissor(region.Left(), viewport_height - region.Bottom(), region.Width(), region.Height());
}

void RenderInterface_GL2::EnableClipMask(bool enable)
Expand Down Expand Up @@ -215,7 +203,7 @@ void RenderInterface_GL2::RenderToClipMask(Rml::ClipMaskOperation operation, Rml
break;
}

RenderCompiledGeometry(geometry, translation, {});
RenderGeometry(geometry, translation, {});

// Restore state
// @performance Cache state so we don't toggle it unnecessarily.
Expand Down Expand Up @@ -243,7 +231,7 @@ struct TGAHeader {
// Restore packing
#pragma pack()

bool RenderInterface_GL2::LoadTexture(Rml::TextureHandle& texture_handle, Rml::Vector2i& texture_dimensions, const Rml::String& source)
Rml::TextureHandle RenderInterface_GL2::LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source)
{
Rml::FileInterface* file_interface = Rml::GetFileInterface();
Rml::FileHandle file_handle = file_interface->Open(source);
Expand Down Expand Up @@ -272,7 +260,7 @@ bool RenderInterface_GL2::LoadTexture(Rml::TextureHandle& texture_handle, Rml::V
memcpy(&header, buffer.get(), sizeof(TGAHeader));

int color_mode = header.bitsPerPixel / 8;
int image_size = header.width * header.height * 4; // We always make 32bit textures
const size_t image_size = header.width * header.height * 4; // We always make 32bit textures

if (header.dataType != 2)
{
Expand Down Expand Up @@ -319,33 +307,29 @@ bool RenderInterface_GL2::LoadTexture(Rml::TextureHandle& texture_handle, Rml::V
texture_dimensions.x = header.width;
texture_dimensions.y = header.height;

bool success = GenerateTexture(texture_handle, image_dest, texture_dimensions);

return success;
return GenerateTexture({image_dest, image_size}, texture_dimensions);
}

bool RenderInterface_GL2::GenerateTexture(Rml::TextureHandle& texture_handle, const Rml::byte* source, const Rml::Vector2i& source_dimensions)
Rml::TextureHandle RenderInterface_GL2::GenerateTexture(Rml::Span<const Rml::byte> source, Rml::Vector2i source_dimensions)
{
GLuint texture_id = 0;
glGenTextures(1, &texture_id);
if (texture_id == 0)
{
Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to generate texture.");
return false;
return {};
}

glBindTexture(GL_TEXTURE_2D, texture_id);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, source_dimensions.x, source_dimensions.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, source);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, source_dimensions.x, source_dimensions.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, source.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

texture_handle = (Rml::TextureHandle)texture_id;

return true;
return (Rml::TextureHandle)texture_id;
}

void RenderInterface_GL2::ReleaseTexture(Rml::TextureHandle texture_handle)
Expand Down
24 changes: 10 additions & 14 deletions Backends/RmlUi_Renderer_GL2.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,33 +47,29 @@ class RenderInterface_GL2 : public Rml::RenderInterface {

// -- Inherited from Rml::RenderInterface --

void RenderGeometry(Rml::Vertex* vertices, int num_vertices, int* indices, int num_indices, Rml::TextureHandle texture,
const Rml::Vector2f& translation) override;
Rml::CompiledGeometryHandle CompileGeometry(Rml::Vertex* vertices, int num_vertices, int* indices, int num_indices) override;
void ReleaseCompiledGeometry(Rml::CompiledGeometryHandle geometry) override;
void RenderCompiledGeometry(Rml::CompiledGeometryHandle handle, const Rml::Vector2f& translation, Rml::TextureHandle texture) override;
Rml::CompiledGeometryHandle CompileGeometry(Rml::Span<const Rml::Vertex> vertices, Rml::Span<const int> indices) override;
void ReleaseGeometry(Rml::CompiledGeometryHandle geometry) override;
void RenderGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture) override;

Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) override;
Rml::TextureHandle GenerateTexture(Rml::Span<const Rml::byte> source, Rml::Vector2i source_dimensions) override;
void ReleaseTexture(Rml::TextureHandle texture_handle) override;

void EnableScissorRegion(bool enable) override;
void SetScissorRegion(int x, int y, int width, int height) override;
void SetScissorRegion(Rml::Rectanglei region) override;

void EnableClipMask(bool enable) override;
void RenderToClipMask(Rml::ClipMaskOperation operation, Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation) override;

bool LoadTexture(Rml::TextureHandle& texture_handle, Rml::Vector2i& texture_dimensions, const Rml::String& source) override;
bool GenerateTexture(Rml::TextureHandle& texture_handle, const Rml::byte* source, const Rml::Vector2i& source_dimensions) override;
void ReleaseTexture(Rml::TextureHandle texture_handle) override;

void SetTransform(const Rml::Matrix4f* transform) override;

// Can be passed to RenderGeometry() to enable texture rendering without changing the bound texture.
static const Rml::TextureHandle TextureEnableWithoutBinding = Rml::TextureHandle(-1);

private:
struct GeometryView {
Rml::Vertex* vertices = nullptr;
int* indices = nullptr;
int num_vertices = 0;
int num_indices = 0;
Rml::Span<const Rml::Vertex> vertices;
Rml::Span<const int> indices;
};

int viewport_width = 0;
Expand Down
Loading

0 comments on commit 5faedc4

Please sign in to comment.