mirror of
https://github.com/miracle-wm-org/miracle-wm.git
synced 2024-11-23 04:08:27 +03:00
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:
parent
6e7207717a
commit
06668b0bd2
@ -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
236
src/program_factory.cpp
Normal 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
121
src/program_factory.h
Normal 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
|
360
src/renderer.cpp
360
src/renderer.cpp
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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())
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user