File: renderer.cpp

package info (click to toggle)
wayfire-shadows 0.0~git20250720.453c217-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 148 kB
  • sloc: cpp: 598; xml: 118; makefile: 4
file content (220 lines) | stat: -rw-r--r-- 7,108 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#include <random>
#include <wayfire/geometry.hpp>
#include <wayfire/toplevel.hpp>
#include "renderer.hpp"

namespace winshadows {

shadow_renderer_t::shadow_renderer_t() {
        wf::gles::run_in_context([&]
        {
    generate_dither_texture();
    recompile_shaders();
    });

    light_type_option.set_callback([this] () {
        recompile_shaders();
    });
}

void shadow_renderer_t::recompile_shaders() {
        wf::gles::run_in_context([&]
        {
    shadow_program.free_resources();
    shadow_glow_program.free_resources();

    shadow_program.set_simple(
        OpenGL::compile_program(shadow_vert_shader, frag_shader(light_type_option, /*no glow*/ false))
    );
    shadow_glow_program.set_simple(
        OpenGL::compile_program(shadow_vert_shader, frag_shader(light_type_option, /*glow*/ true))
    );

    });
}

void shadow_renderer_t::generate_dither_texture() {
    const int size = 32;
    GLuint data[size*size];

    std::mt19937_64 gen{std::random_device{}()};
    std::uniform_int_distribution<GLuint> distrib;

    for (int i = 0; i < size*size; i++) {
        data[i] = distrib(gen);
    }

    GL_CALL(glGenTextures(1, &dither_texture));
    GL_CALL(glBindTexture(GL_TEXTURE_2D, dither_texture));
    GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, data));
    GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
    GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
    GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT));
    GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT));
}

shadow_renderer_t::~shadow_renderer_t() {
        wf::gles::run_in_context([&]
        {
    shadow_program.free_resources();
    shadow_glow_program.free_resources();

    GL_CALL(glDeleteTextures(1, &dither_texture));

    });
}

void shadow_renderer_t::render(const wf::scene::render_instruction_t& data, wf::point_t window_origin, const wf::geometry_t& scissor, const bool glow) {
    float radius = shadow_radius_option;

    wf::color_t color = shadow_color_option;

    // Premultiply alpha for shader
    glm::vec4 premultiplied = {
        color.r * color.a,
        color.g * color.a,
        color.b * color.a,
        color.a
    };

    // Glow color, alpha=0 => additive blending (exploiting premultiplied alpha)
    wf::color_t glow_color = glow_color_option;
    glm::vec4 glow_premultiplied = {
        glow_color.r * glow_color.a,
        glow_color.g * glow_color.a,
        glow_color.b * glow_color.a,
        glow_color.a * (1.0 - glow_emissivity_option)
    };

    // Enable glow shader only when glow radius > 0 and view is focused
    bool use_glow = (glow && is_glow_enabled());
    OpenGL::program_t &program = 
        use_glow ? shadow_glow_program : shadow_program;

            data.pass->custom_gles_subpass(data.target,[&]
            {

                wf::gles::render_target_logic_scissor(data.target, scissor);
    program.use(wf::TEXTURE_TYPE_RGBA);

    // Compute vertex rectangle geometry
    wf::geometry_t bounds = outer_geometry + window_origin;
    float left = bounds.x;
    float right = bounds.x + bounds.width;
    float top = bounds.y;
    float bottom = bounds.y + bounds.height;

    GLfloat vertexData[] = {
        left, bottom,
        right, bottom,
        right, top,
        left, top
    };

    glm::mat4 matrix = wf::gles::render_target_orthographic_projection(data.target);

    // vertex parameters
    program.attrib_pointer("position", 2, 0, vertexData);
    program.uniformMatrix4f("MVP", matrix);

    // fragment parameters
    program.uniform1f("radius", radius);
    program.uniform4f("color", premultiplied);

    const auto inner = window_geometry + window_origin;
    const auto shadow_inner = shadow_projection_geometry + window_origin;
    program.uniform2f("lower", shadow_inner.x, shadow_inner.y);
    program.uniform2f("upper", shadow_inner.x + shadow_inner.width, shadow_inner.y + shadow_inner.height);

    if (use_glow) {
        program.uniform2f("glow_lower", inner.x, inner.y);
        program.uniform2f("glow_upper", inner.x + inner.width, inner.y + inner.height);

        program.uniform1f("glow_spread", glow_spread_option);
        program.uniform4f("glow_color", glow_premultiplied);
        program.uniform1f("glow_intensity",  glow_intensity_option);
        program.uniform1f("glow_threshold",  glow_threshold_option);
    }

    // dither texture
    program.uniform1i("dither_texture", 0);
    GL_CALL(glActiveTexture(GL_TEXTURE0));
    GL_CALL(glBindTexture(GL_TEXTURE_2D, dither_texture));

    GL_CALL(glEnable(GL_BLEND));
    GL_CALL(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
    GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));

    program.deactivate();
    });
}

wf::region_t shadow_renderer_t::calculate_region() const {
    // TODO: geometry and region depending on whether glow is active or not
    wf::region_t region = wf::region_t(shadow_geometry) | wf::region_t(glow_geometry);

    if (clip_shadow_inside) {
        region ^= window_geometry;
    }

    return region;
}

wf::geometry_t shadow_renderer_t::get_geometry() const {
    return outer_geometry;
}

wf::geometry_t expand_geometry(const wf::geometry_t& geometry, const int marginX, const int marginY) {
    return {
        geometry.x - marginX,
        geometry.y - marginY,
        geometry.width + marginX * 2,
        geometry.height + marginY * 2
    };
}

wf::geometry_t expand_geometry(const wf::geometry_t& geometry, const int margin) {
    return expand_geometry(geometry, margin, margin);
}

wf::geometry_t inflate_geometry(const wf::geometry_t& geometry, const float inflation) {
    int expandX = geometry.width * inflation * 0.5;
    int expandY = geometry.height * inflation * 0.5;
    return expand_geometry(geometry, expandX, expandY);
}

void shadow_renderer_t::resize(const int window_width, const int window_height) {
    window_geometry = {
        0,
        0,
        window_width,
        window_height
    };

    float overscale = overscale_option / 100.0;
    const wf::point_t offset { horizontal_offset, vertical_offset };
    shadow_projection_geometry =
        inflate_geometry(window_geometry, overscale) + offset;

    shadow_geometry = expand_geometry(shadow_projection_geometry, shadow_radius_option);

    int glow_radius = is_glow_enabled() ? glow_radius_limit_option : 0;
    glow_geometry = expand_geometry(shadow_projection_geometry, glow_radius);

    int left = std::min(shadow_geometry.x, glow_geometry.x);
    int top = std::min(shadow_geometry.y, glow_geometry.y);
    int right = std::max(shadow_geometry.x + shadow_geometry.width, glow_geometry.x + glow_geometry.width);
    int bottom = std::max(shadow_geometry.y + shadow_geometry.height, glow_geometry.y + glow_geometry.height);
    outer_geometry = {
        left,
        top,
        right - left,
        bottom - top
    };
}

bool shadow_renderer_t::is_glow_enabled() const {
    return glow_enabled_option && (glow_radius_limit_option > 0) && (glow_intensity_option > 0);
}

}