refactor: move ProgramFactory to its own file + clang-tidy issues around the renderer (#139)

* refactor: move ProgramFactory to its own file
* refactor: fixing various clang-tidy issues
* refactor: plenty of more cleanup
This commit is contained in:
Matthew Kosarek 2024-05-27 18:58:54 -04:00 committed by GitHub
parent 6e7207717a
commit 06668b0bd2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 415 additions and 321 deletions

View File

@ -63,6 +63,7 @@ add_library(miracle-wm-implementation
src/window_tools_accessor.cpp
src/animator.cpp
src/animation_definition.cpp
src/program_factory.cpp
)
add_executable(miracle-wm

236
src/program_factory.cpp Normal file
View File

@ -0,0 +1,236 @@
/**
Copyright (C) 2024 Matthew Kosarek
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**/
#define MIR_LOG_COMPONENT "program_factory"
#include "program_factory.h"
#include <sstream>
#include <boost/throw_exception.hpp>
#include <mir/graphics/egl_error.h>
#include <mir/log.h>
namespace
{
const GLchar* const vertex_shader_src = R"(
attribute vec3 position;
attribute vec2 texcoord;
uniform mat4 screen_to_gl_coords;
uniform mat4 display_transform;
uniform mat4 workspace_transform;
uniform mat4 transform;
uniform vec2 centre;
varying vec2 v_texcoord;
void main() {
vec4 mid = vec4(centre, 0.0, 0.0);
vec4 transformed = (transform * (vec4(position, 1.0) - mid)) + mid;
gl_Position = display_transform * screen_to_gl_coords * workspace_transform * transformed;
v_texcoord = texcoord;
}
)";
}
miracle::ProgramData::ProgramData(GLuint program_id)
{
id = program_id;
position_attr = glGetAttribLocation(id, "position");
if (position_attr < 0)
mir::log_warning("Program is missing position_attr");
texcoord_attr = glGetAttribLocation(id, "texcoord");
if (position_attr < 0)
mir::log_warning("Program is missing texcoord_attr");
for (auto i = 0u; i < tex_uniforms.size(); ++i)
{
/* You can reference uniform arrays as tex[0], tex[1], tex[2], … until you
* hit the end of the array, which will return -1 as the location.
*/
auto const uniform_name = std::string { "tex[" } + std::to_string(i) + "]";
tex_uniforms[i] = glGetUniformLocation(id, uniform_name.c_str());
}
centre_uniform = glGetUniformLocation(id, "centre");
if (centre_uniform < 0)
mir::log_warning("Program is missing centre_uniform");
display_transform_uniform = glGetUniformLocation(id, "display_transform");
if (display_transform_uniform < 0)
mir::log_warning("Program is missing display_transform_uniform");
workspace_transform_uniform = glGetUniformLocation(id, "workspace_transform");
if (workspace_transform_uniform < 0)
mir::log_warning("Program is missing workspace_transform_uniform");
transform_uniform = glGetUniformLocation(id, "transform");
if (transform_uniform < 0)
mir::log_warning("Program is missing transform_uniform");
screen_to_gl_coords_uniform = glGetUniformLocation(id, "screen_to_gl_coords");
if (screen_to_gl_coords_uniform < 0)
mir::log_warning("Program is missing screen_to_gl_coords_uniform");
alpha_uniform = glGetUniformLocation(id, "alpha");
if (alpha_uniform < 0)
mir::log_warning("Program is missing alpha_uniform");
outline_color_uniform = glGetUniformLocation(id, "outline_color");
if (outline_color_uniform < 0)
mir::log_warning("Program is missing outline_color_uniform");
}
miracle::Program::Program(
ProgramHandle&& opaque_shader, ProgramHandle&& alpha_shader, ProgramHandle&& outline_shader) :
opaque_handle(std::move(opaque_shader)),
alpha_handle(std::move(alpha_shader)),
outline_handle(std::move(outline_shader)),
opaque { opaque_handle },
alpha { alpha_handle },
outline(outline_handle) {}
miracle::ProgramFactory::ProgramFactory() :
vertex_shader { compile_shader(GL_VERTEX_SHADER, vertex_shader_src) } {}
mir::graphics::gl::Program& miracle::ProgramFactory::compile_fragment_shader(
void const* id,
char const* extension_fragment,
char const* fragment_fragment)
{
/* NOTE: This does not lock the programs vector as there is one ProgramFactory instance
* per rendering thread.
*/
for (auto const& pair : programs)
{
if (pair.first == id)
{
return *pair.second;
}
}
std::stringstream opaque_fragment;
opaque_fragment
<< extension_fragment
<< "\n"
<< "#ifdef GL_ES\n"
"precision mediump float;\n"
"#endif\n"
<< "\n"
<< fragment_fragment
<< "\n"
<< "varying vec2 v_texcoord;\n"
"void main() {\n"
" gl_FragColor = sample_to_rgba(v_texcoord);\n"
"}\n";
std::stringstream alpha_fragment;
alpha_fragment
<< extension_fragment
<< "\n"
<< "#ifdef GL_ES\n"
"precision mediump float;\n"
"#endif\n"
<< "\n"
<< fragment_fragment
<< "\n"
<< "varying vec2 v_texcoord;\n"
"uniform float alpha;\n"
"void main() {\n"
" gl_FragColor = alpha * sample_to_rgba(v_texcoord);\n"
"}\n";
const GLchar* const outline_shader_src = R"(
#ifdef GL_ES
precision mediump float;
#endif
uniform float alpha;
uniform vec4 outline_color;
void main() {
gl_FragColor = alpha * outline_color;
}
)";
// GL shader compilation is *not* threadsafe, and requires external synchronisation
std::lock_guard lock { compilation_mutex };
ShaderHandle const opaque_shader {
compile_shader(GL_FRAGMENT_SHADER, opaque_fragment.str().c_str())
};
ShaderHandle const alpha_shader {
compile_shader(GL_FRAGMENT_SHADER, alpha_fragment.str().c_str())
};
ShaderHandle const outline_shader {
compile_shader(GL_FRAGMENT_SHADER, outline_shader_src)
};
programs.emplace_back(id, std::make_unique<miracle::Program>(
link_shader(vertex_shader, opaque_shader),
link_shader(vertex_shader, alpha_shader),
link_shader(vertex_shader, outline_shader)));
return *programs.back().second;
// We delete opaque_shader and alpha_shader here. This is fine; it only marks them
// for deletion. GL will only delete them once the GL Program they're linked in is destroyed.
}
GLuint miracle::ProgramFactory::compile_shader(GLenum type, GLchar const* src)
{
GLuint id = glCreateShader(type);
if (!id)
{
BOOST_THROW_EXCEPTION(mir::graphics::gl_error("Failed to create shader"));
}
glShaderSource(id, 1, &src, NULL);
glCompileShader(id);
GLint ok;
glGetShaderiv(id, GL_COMPILE_STATUS, &ok);
if (!ok)
{
GLchar log[1024] = "(No log info)";
glGetShaderInfoLog(id, sizeof log, NULL, log);
glDeleteShader(id);
BOOST_THROW_EXCEPTION(
std::runtime_error(
std::string("Compile failed: ") + log + " for:\n" + src));
}
return id;
}
miracle::ProgramHandle miracle::ProgramFactory::link_shader(
ShaderHandle const& vertex_shader,
ShaderHandle const& fragment_shader)
{
ProgramHandle program { glCreateProgram() };
glAttachShader(program, fragment_shader);
glAttachShader(program, vertex_shader);
glLinkProgram(program);
GLint ok;
glGetProgramiv(program, GL_LINK_STATUS, &ok);
if (!ok)
{
GLchar log[1024];
glGetProgramInfoLog(program, sizeof log - 1, NULL, log);
log[sizeof log - 1] = '\0';
BOOST_THROW_EXCEPTION(
std::runtime_error(
std::string("Linking GL shader failed: ") + log));
}
return program;
}

121
src/program_factory.h Normal file
View File

@ -0,0 +1,121 @@
/**
Copyright (C) 2024 Matthew Kosarek
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef MIRACLE_WM_PROGRAM_FACTORY_H
#define MIRACLE_WM_PROGRAM_FACTORY_H
#include <mir/graphics/program_factory.h>
#include <mir/graphics/program.h>
#include <GLES2/gl2.h>
#include <vector>
#include <array>
#include <mutex>
namespace miracle
{
template <void (*deleter)(GLuint)>
class GLHandle
{
public:
explicit GLHandle(GLuint id) :
id { id }
{
}
~GLHandle()
{
if (id)
(*deleter)(id);
}
GLHandle(GLHandle const&) = delete;
GLHandle& operator=(GLHandle const&) = delete;
GLHandle(GLHandle&& from) :
id { from.id }
{
from.id = 0;
}
operator GLuint() const
{
return id;
}
private:
GLuint id;
};
using ProgramHandle = GLHandle<&glDeleteProgram>;
using ShaderHandle = GLHandle<&glDeleteShader>;
struct ProgramData
{
GLuint id = 0;
/* 8 is the minimum number of texture units a GL implementation can provide
* and should comfortably provide enough textures for any conceivable buffer
* format
*/
std::array<GLint, 8> tex_uniforms;
GLint position_attr = -1;
GLint texcoord_attr = -1;
GLint centre_uniform = -1;
GLint display_transform_uniform = -1;
GLint workspace_transform_uniform = -1;
GLint transform_uniform = -1;
GLint screen_to_gl_coords_uniform = -1;
GLint alpha_uniform = -1;
GLint outline_color_uniform = -1;
mutable long long last_used_frameno = 0;
ProgramData(GLuint program_id);
};
struct Program : public mir::graphics::gl::Program
{
public:
Program(ProgramHandle&& opaque_shader, ProgramHandle&& alpha_shader, ProgramHandle&& outline_shader);
ProgramHandle opaque_handle, alpha_handle, outline_handle;
ProgramData opaque, alpha, outline;
};
class ProgramFactory : public mir::graphics::gl::ProgramFactory
{
public:
ProgramFactory();
mir::graphics::gl::Program& compile_fragment_shader(
void const* id,
char const* extension_fragment,
char const* fragment_fragment) override;
private:
static GLuint compile_shader(GLenum type, GLchar const* src);
static ProgramHandle link_shader(
ShaderHandle const& vertex_shader,
ShaderHandle const& fragment_shader);
ShaderHandle const vertex_shader;
std::vector<std::pair<void const*, std::unique_ptr<Program>>> programs;
// GL requires us to synchronise multi-threaded access to the shader APIs.
std::mutex compilation_mutex;
};
} // miracle
#endif //MIRACLE_WM_PROGRAM_FACTORY_H

View File

@ -15,35 +15,30 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**/
#include <GLES2/gl2.h>
#define GLM_FORCE_RADIANS
#define MIR_LOG_COMPONENT "GLRenderer"
#include <GLES2/gl2.h>
#include "window_tools_accessor.h"
#include "workspace_content.h"
#include "mir/graphics/buffer.h"
#include "mir/graphics/display_sink.h"
#include "mir/graphics/egl_error.h"
#include "mir/graphics/platform.h"
#include "mir/graphics/program.h"
#include "mir/graphics/program_factory.h"
#include "mir/graphics/renderable.h"
#include "mir/graphics/texture.h"
#include "mir/log.h"
#include "mir/renderer/gl/gl_surface.h"
#include "program_factory.h"
#include "miracle_config.h"
#include "renderer.h"
#include "tessellation_helpers.h"
#include "window_metadata.h"
#define GLM_FORCE_RADIANS
#include <mir/graphics/buffer.h>
#include <mir/graphics/display_sink.h>
#include <mir/graphics/platform.h>
#include <mir/graphics/program_factory.h>
#include <mir/graphics/renderable.h>
#include <mir/graphics/texture.h>
#include <mir/log.h>
#include <mir/renderer/gl/gl_surface.h>
#include <EGL/egl.h>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <boost/throw_exception.hpp>
#include <cmath>
#include <mutex>
#include <sstream>
#include <stdexcept>
namespace mg = mir::graphics;
@ -51,270 +46,6 @@ namespace mgl = mir::gl;
namespace geom = mir::geometry;
using namespace miracle;
namespace
{
template <void (*deleter)(GLuint)>
class GLHandle
{
public:
explicit GLHandle(GLuint id) :
id { id }
{
}
~GLHandle()
{
if (id)
(*deleter)(id);
}
GLHandle(GLHandle const&) = delete;
GLHandle& operator=(GLHandle const&) = delete;
GLHandle(GLHandle&& from) :
id { from.id }
{
from.id = 0;
}
operator GLuint() const
{
return id;
}
private:
GLuint id;
};
using ProgramHandle = GLHandle<&glDeleteProgram>;
using ShaderHandle = GLHandle<&glDeleteShader>;
struct ProgramData
{
GLuint id = 0;
/* 8 is the minimum number of texture units a GL implementation can provide
* and should comfortably provide enough textures for any conceivable buffer
* format
*/
std::array<GLint, 8> tex_uniforms;
GLint position_attr = -1;
GLint texcoord_attr = -1;
GLint centre_uniform = -1;
GLint display_transform_uniform = -1;
GLint workspace_transform_uniform = -1;
GLint transform_uniform = -1;
GLint screen_to_gl_coords_uniform = -1;
GLint alpha_uniform = -1;
GLint outline_color_uniform = -1;
mutable long long last_used_frameno = 0;
ProgramData(GLuint program_id)
{
id = program_id;
position_attr = glGetAttribLocation(id, "position");
texcoord_attr = glGetAttribLocation(id, "texcoord");
for (auto i = 0u; i < tex_uniforms.size(); ++i)
{
/* You can reference uniform arrays as tex[0], tex[1], tex[2], … until you
* hit the end of the array, which will return -1 as the location.
*/
auto const uniform_name = std::string { "tex[" } + std::to_string(i) + "]";
tex_uniforms[i] = glGetUniformLocation(id, uniform_name.c_str());
}
centre_uniform = glGetUniformLocation(id, "centre");
display_transform_uniform = glGetUniformLocation(id, "display_transform");
workspace_transform_uniform = glGetUniformLocation(id, "workspace_transform");
transform_uniform = glGetUniformLocation(id, "transform");
screen_to_gl_coords_uniform = glGetUniformLocation(id, "screen_to_gl_coords");
alpha_uniform = glGetUniformLocation(id, "alpha");
outline_color_uniform = glGetUniformLocation(id, "outline_color");
}
};
struct Program : public mir::graphics::gl::Program
{
public:
Program(ProgramHandle&& opaque_shader, ProgramHandle&& alpha_shader, ProgramHandle&& outline_shader) :
opaque_handle(std::move(opaque_shader)),
alpha_handle(std::move(alpha_shader)),
outline_handle(std::move(outline_shader)),
opaque { opaque_handle },
alpha { alpha_handle },
outline(outline_handle)
{
}
ProgramHandle opaque_handle, alpha_handle, outline_handle;
ProgramData opaque, alpha, outline;
};
const GLchar* const vertex_shader_src = R"(
attribute vec3 position;
attribute vec2 texcoord;
uniform mat4 screen_to_gl_coords;
uniform mat4 display_transform;
uniform mat4 workspace_transform;
uniform mat4 transform;
uniform vec2 centre;
uniform vec4 outline_color;
varying vec2 v_texcoord;
varying vec4 v_outline_color;
void main() {
vec4 mid = vec4(centre, 0.0, 0.0);
vec4 transformed = (transform * (vec4(position, 1.0) - mid)) + mid;
gl_Position = display_transform * screen_to_gl_coords * workspace_transform * transformed;
v_texcoord = texcoord;
v_outline_color = outline_color;
}
)";
}
class Renderer::ProgramFactory : public mir::graphics::gl::ProgramFactory
{
public:
// NOTE: This must be called with a current GL context
ProgramFactory() :
vertex_shader { compile_shader(GL_VERTEX_SHADER, vertex_shader_src) }
{
}
mir::graphics::gl::Program&
compile_fragment_shader(
void const* id,
char const* extension_fragment,
char const* fragment_fragment) override
{
/* NOTE: This does not lock the programs vector as there is one ProgramFactory instance
* per rendering thread.
*/
for (auto const& pair : programs)
{
if (pair.first == id)
{
return *pair.second;
}
}
std::stringstream opaque_fragment;
opaque_fragment
<< extension_fragment
<< "\n"
<< "#ifdef GL_ES\n"
"precision mediump float;\n"
"#endif\n"
<< "\n"
<< fragment_fragment
<< "\n"
<< "varying vec2 v_texcoord;\n"
"void main() {\n"
" gl_FragColor = sample_to_rgba(v_texcoord);\n"
"}\n";
std::stringstream alpha_fragment;
alpha_fragment
<< extension_fragment
<< "\n"
<< "#ifdef GL_ES\n"
"precision mediump float;\n"
"#endif\n"
<< "\n"
<< fragment_fragment
<< "\n"
<< "varying vec2 v_texcoord;\n"
"uniform float alpha;\n"
"void main() {\n"
" gl_FragColor = alpha * sample_to_rgba(v_texcoord);\n"
"}\n";
const GLchar* const outline_shader_src = R"(
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 v_texcoord;
varying vec4 v_outline_color;
void main() {
gl_FragColor = v_outline_color;
}
)";
// GL shader compilation is *not* threadsafe, and requires external synchronisation
std::lock_guard lock { compilation_mutex };
ShaderHandle const opaque_shader {
compile_shader(GL_FRAGMENT_SHADER, opaque_fragment.str().c_str())
};
ShaderHandle const alpha_shader {
compile_shader(GL_FRAGMENT_SHADER, alpha_fragment.str().c_str())
};
ShaderHandle const outline_shader {
compile_shader(GL_FRAGMENT_SHADER, outline_shader_src)
};
programs.emplace_back(id, std::make_unique<::Program>(link_shader(vertex_shader, opaque_shader), link_shader(vertex_shader, alpha_shader), link_shader(vertex_shader, outline_shader)));
return *programs.back().second;
// We delete opaque_shader and alpha_shader here. This is fine; it only marks them
// for deletion. GL will only delete them once the GL Program they're linked in is destroyed.
}
private:
static GLuint compile_shader(GLenum type, GLchar const* src)
{
GLuint id = glCreateShader(type);
if (!id)
{
BOOST_THROW_EXCEPTION(mg::gl_error("Failed to create shader"));
}
glShaderSource(id, 1, &src, NULL);
glCompileShader(id);
GLint ok;
glGetShaderiv(id, GL_COMPILE_STATUS, &ok);
if (!ok)
{
GLchar log[1024] = "(No log info)";
glGetShaderInfoLog(id, sizeof log, NULL, log);
glDeleteShader(id);
BOOST_THROW_EXCEPTION(
std::runtime_error(
std::string("Compile failed: ") + log + " for:\n" + src));
}
return id;
}
static ProgramHandle link_shader(
ShaderHandle const& vertex_shader,
ShaderHandle const& fragment_shader)
{
ProgramHandle program { glCreateProgram() };
glAttachShader(program, fragment_shader);
glAttachShader(program, vertex_shader);
glLinkProgram(program);
GLint ok;
glGetProgramiv(program, GL_LINK_STATUS, &ok);
if (!ok)
{
GLchar log[1024];
glGetProgramInfoLog(program, sizeof log - 1, NULL, log);
log[sizeof log - 1] = '\0';
BOOST_THROW_EXCEPTION(
std::runtime_error(
std::string("Linking GL shader failed: ") + log));
}
return program;
}
ShaderHandle const vertex_shader;
std::vector<std::pair<void const*, std::unique_ptr<::Program>>> programs;
// GL requires us to synchronise multi-threaded access to the shader APIs.
std::mutex compilation_mutex;
};
namespace
{
auto make_output_current(std::unique_ptr<mg::gl::OutputSurface> output) -> std::unique_ptr<mg::gl::OutputSurface>
@ -333,22 +64,22 @@ public:
{
}
ID id() const override
[[nodiscard]] ID id() const override
{
return "";
}
std::shared_ptr<mir::graphics::Buffer> buffer() const override
[[nodiscard]] std::shared_ptr<mir::graphics::Buffer> buffer() const override
{
return renderable.buffer();
}
geom::Rectangle screen_position() const override
[[nodiscard]] geom::Rectangle screen_position() const override
{
return get_rectangle(renderable.screen_position());
}
std::optional<geom::Rectangle> clip_area() const override
[[nodiscard]] std::optional<geom::Rectangle> clip_area() const override
{
auto clip_area_rect = renderable.clip_area();
if (!clip_area_rect)
@ -356,28 +87,28 @@ public:
return get_rectangle(clip_area_rect.value());
}
float alpha() const override
[[nodiscard]] float alpha() const override
{
return _alpha;
}
glm::mat4 transformation() const override
[[nodiscard]] glm::mat4 transformation() const override
{
return renderable.transformation();
}
bool shaped() const override
[[nodiscard]] bool shaped() const override
{
return renderable.shaped();
}
std::optional<mir::scene::Surface const*> surface_if_any() const override
[[nodiscard]] std::optional<mir::scene::Surface const*> surface_if_any() const override
{
return {};
}
private:
geom::Rectangle get_rectangle(geom::Rectangle const& in) const
[[nodiscard]] geom::Rectangle get_rectangle(geom::Rectangle const& in) const
{
auto rectangle = in;
rectangle.top_left = {
@ -405,6 +136,7 @@ Renderer::Renderer(
clear_color { 0.0f, 0.0f, 0.0f, 1.0f },
program_factory { std::make_unique<ProgramFactory>() },
display_transform(1),
screen_to_gl_coords(1),
gl_interface { std::move(gl_interface) },
config { config },
surface_tracker { surface_tracker }
@ -466,13 +198,9 @@ Renderer::Renderer(
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
Renderer::~Renderer()
{
}
void Renderer::tessellate(
std::vector<mgl::Primitive>& primitives,
mg::Renderable const& renderable) const
mg::Renderable const& renderable)
{
primitives.resize(1);
primitives[0] = mgl::tessellate_renderable_into_rectangle(renderable, geom::Displacement { 0, 0 });
@ -492,7 +220,12 @@ auto Renderer::render(mg::RenderableList const& renderables) const -> std::uniqu
++frameno;
for (auto const& r : renderables)
{
draw(*r);
auto context = draw(*r);
if (context.enabled)
{
OutlineRenderable outline(*r, context.size, context.color.a);
draw(outline, &context);
}
}
auto output = output_surface->commit();
@ -504,7 +237,7 @@ auto Renderer::render(mg::RenderableList const& renderables) const -> std::uniqu
return output;
}
void Renderer::draw(mg::Renderable const& renderable, OutlineContext* context) const
miracle::Renderer::OutlineContext Renderer::draw(mg::Renderable const& renderable, OutlineContext* context) const
{
auto surface = renderable.surface_if_any();
std::shared_ptr<WindowMetadata> userdata = nullptr;
@ -568,7 +301,7 @@ void Renderer::draw(mg::Renderable const& renderable, OutlineContext* context) c
auto const* const prog =
[&](bool alpha) -> ProgramData const*
{
auto const& family = static_cast<::Program const&>(texture->shader(*program_factory));
auto const& family = dynamic_cast<Program const&>(texture->shader(*program_factory));
if (context)
return &family.outline;
if (alpha)
@ -585,7 +318,7 @@ void Renderer::draw(mg::Renderable const& renderable, OutlineContext* context) c
{
if (prog->tex_uniforms[i] != -1)
{
glUniform1i(prog->tex_uniforms[i], i);
glUniform1i(prog->tex_uniforms[i], (int)i);
}
}
glUniformMatrix4fv(prog->display_transform_uniform, 1, GL_FALSE,
@ -597,8 +330,8 @@ void Renderer::draw(mg::Renderable const& renderable, OutlineContext* context) c
glActiveTexture(GL_TEXTURE0);
auto const& rect = renderable.screen_position();
GLfloat centrex = rect.top_left.x.as_int() + rect.size.width.as_int() / 2.0f;
GLfloat centrey = rect.top_left.y.as_int() + rect.size.height.as_int() / 2.0f;
GLfloat centrex = (float)rect.top_left.x.as_int() + (float)rect.size.width.as_int() / 2.0f;
GLfloat centrey = (float)rect.top_left.y.as_int() + (float)rect.size.height.as_int() / 2.0f;
glUniform2f(prog->centre_uniform, centrex, centrey);
glm::mat4 transform = renderable.transformation();
@ -633,7 +366,7 @@ void Renderer::draw(mg::Renderable const& renderable, OutlineContext* context) c
glUniformMatrix4fv(prog->workspace_transform_uniform, 1, GL_FALSE,
glm::value_ptr(glm::mat4(1.f)));
if (context)
if (prog->outline_color_uniform >= 0 && context)
{
glUniform4f(prog->outline_color_uniform,
context->color.r, context->color.g,
@ -726,11 +459,11 @@ void Renderer::draw(mg::Renderable const& renderable, OutlineContext* context) c
{
bool is_focused = userdata->is_focused();
auto color = is_focused ? border_config.focus_color : border_config.color;
OutlineContext outline_context = { color };
OutlineRenderable outline(renderable, border_config.size, color.a);
draw(outline, &outline_context);
return OutlineContext{ true, color, border_config.size };
}
}
return OutlineContext{ false };
}
void Renderer::set_viewport(mir::geometry::Rectangle const& rect)
@ -758,12 +491,13 @@ void Renderer::set_viewport(mir::geometry::Rectangle const& rect)
screen_to_gl_coords[2][3] = -1.0f;
float const vertical_fov_degrees = 30.0f;
float const near = (rect.size.height.as_int() / 2.0f) / std::tan((vertical_fov_degrees * M_PI / 180.0f) / 2.0f);
float half_height = (float)rect.size.height.as_int() / 2.f;
float const near = half_height / tanf((float)(vertical_fov_degrees * M_PI / 180.0f) / 2.f);
float const far = -near;
screen_to_gl_coords = glm::scale(screen_to_gl_coords,
glm::vec3 { 2.0f / rect.size.width.as_int(),
-2.0f / rect.size.height.as_int(),
glm::vec3 { 2.0f / (float)rect.size.width.as_int(),
-2.0f / (float)rect.size.height.as_int(),
2.0f / (near - far) });
screen_to_gl_coords = glm::translate(screen_to_gl_coords,
glm::vec3 { -rect.top_left.x.as_int(),
@ -786,17 +520,17 @@ void Renderer::update_gl_viewport()
auto viewport_height = fabs(transformed_viewport[1]);
auto const output_size = output_surface->size();
auto const output_width = output_size.width.as_value();
auto const output_height = output_size.height.as_value();
int const output_width = output_size.width.as_value();
int const output_height = output_size.height.as_value();
if (viewport_width > 0.0f && viewport_height > 0.0f && output_width > 0 && output_height > 0)
{
GLint reduced_width = output_width, reduced_height = output_height;
// if viewport_aspect_ratio >= output_aspect_ratio
if (viewport_width * output_height >= output_width * viewport_height)
reduced_height = output_width * viewport_height / viewport_width;
if (viewport_width * (float)output_height >= (float)output_width * viewport_height)
reduced_height = (int)((float)output_width * viewport_height / viewport_width);
else
reduced_width = output_height * viewport_width / viewport_height;
reduced_width = (int)((float)output_height * viewport_width / viewport_height);
GLint offset_x = (output_width - reduced_width) / 2;
GLint offset_y = (output_height - reduced_height) / 2;

View File

@ -20,12 +20,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "primitive.h"
#include "surface_tracker.h"
#include "program_factory.h"
#include <mir/geometry/rectangle.h>
#include <mir/graphics/buffer_id.h>
#include <mir/graphics/renderable.h>
#include <mir/renderer/renderer.h>
#include <miral/window_manager_tools.h>
#include <GLES2/gl2.h>
#include <unordered_map>
#include <unordered_set>
@ -54,7 +55,7 @@ public:
std::unique_ptr<mir::graphics::gl::OutputSurface> output,
std::shared_ptr<MiracleConfig> const& config,
SurfaceTracker& surface_tracker);
virtual ~Renderer();
~Renderer() override = default;
// These are called with a valid GL context:
void set_viewport(mir::geometry::Rectangle const& rect) override;
@ -81,20 +82,21 @@ private:
* the only OpenGL-specific class in the display server, and
* tessellation is very much OpenGL-specific.
*/
virtual void tessellate(std::vector<mir::gl::Primitive>& primitives,
mir::graphics::Renderable const& renderable) const;
static void tessellate(std::vector<mir::gl::Primitive>& primitives,
mir::graphics::Renderable const& renderable);
struct OutlineContext
{
bool enabled = false;
glm::vec4 color;
int size;
};
virtual void draw(mir::graphics::Renderable const& renderable, OutlineContext* context = nullptr) const;
OutlineContext draw(mir::graphics::Renderable const& renderable, OutlineContext* context = nullptr) const;
void update_gl_viewport();
std::unique_ptr<mir::graphics::gl::OutputSurface> const output_surface;
GLfloat clear_color[4];
mutable long long frameno = 0;
class ProgramFactory;
std::unique_ptr<ProgramFactory> const program_factory;
mir::geometry::Rectangle viewport;
glm::mat4 screen_to_gl_coords;

View File

@ -33,7 +33,7 @@ void SurfaceTracker::remove(miral::Window const& window)
map.erase(it);
}
miral::Window SurfaceTracker::get(mir::scene::Surface const* surface)
miral::Window SurfaceTracker::get(mir::scene::Surface const* surface) const
{
auto it = map.find(surface);
if (it == map.end())

View File

@ -29,7 +29,7 @@ class SurfaceTracker
public:
void add(miral::Window const&);
void remove(miral::Window const&);
miral::Window get(mir::scene::Surface const*);
miral::Window get(mir::scene::Surface const*) const;
private:
std::map<mir::scene::Surface const*, miral::Window> map;