Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
Fix window decoration negotiation mode and transparent borders clipping, and complete migration to wlroots-0.20 and scenefx-0.5
build.rs | 9 +-
scenefx/.github/workflows/flake-update.yml | 24 +
scenefx/.gitignore | 6 +-
scenefx/README.md | 64 +
scenefx/examples/scene-graph.c | 3 +-
scenefx/flake.lock | 45 +-
scenefx/flake.nix | 83 +-
scenefx/include/render/color.h | 111 ++
scenefx/include/render/fx_renderer/fx_renderer.h | 28 +-
scenefx/include/render/fx_renderer/shaders.h | 82 +-
scenefx/include/render/pass.h | 4 +
scenefx/include/render/pixel_format.h | 5 +
scenefx/include/render/tracy.h | 178 +++
.../render/fx_renderer/fx_effect_framebuffers.h | 30 -
.../render/fx_renderer/fx_offscreen_buffers.h | 32 +
scenefx/include/scenefx/render/pass.h | 58 +-
scenefx/include/scenefx/types/fx/blur_data.h | 4 +-
scenefx/include/scenefx/types/fx/clipped_region.h | 94 +-
scenefx/include/scenefx/types/fx/corner_location.h | 26 -
scenefx/include/scenefx/types/linked_node.h | 33 +
scenefx/include/scenefx/types/wlr_scene.h | 211 ++-
scenefx/include/types/fx/clipped_region.h | 27 +
scenefx/include/types/wlr_output.h | 3 +
scenefx/include/types/wlr_scene.h | 2 +
scenefx/meson.build | 60 +-
scenefx/meson_options.txt | 2 +
scenefx/render/color.c | 433 ++++++
scenefx/render/color_fallback.c | 24 +
scenefx/render/color_lcms2.c | 113 ++
scenefx/render/egl.c | 43 +-
.../render/fx_renderer/fx_effect_framebuffers.c | 57 -
scenefx/render/fx_renderer/fx_framebuffer.c | 75 +-
scenefx/render/fx_renderer/fx_offscreen_buffers.c | 92 ++
scenefx/render/fx_renderer/fx_pass.c | 830 +++++++-----
scenefx/render/fx_renderer/fx_renderer.c | 144 +-
scenefx/render/fx_renderer/gles2/meson.build | 2 -
.../fx_renderer/gles2/shaders/corner_alpha.frag | 35 -
.../render/fx_renderer/gles2/shaders/meson.build | 30 -
scenefx/render/fx_renderer/gles2/shaders/quad.frag | 31 -
.../fx_renderer/gles2/shaders/quad_round.frag | 64 -
scenefx/render/fx_renderer/gles3/meson.build | 1 -
.../render/fx_renderer/gles3/shaders/blur1.frag | 23 -
.../render/fx_renderer/gles3/shaders/blur2.frag | 27 -
.../fx_renderer/gles3/shaders/blur_effects.frag | 58 -
.../fx_renderer/gles3/shaders/box_shadow.frag | 96 --
.../render/fx_renderer/gles3/shaders/common.vert | 15 -
.../fx_renderer/gles3/shaders/corner_alpha.frag | 35 -
scenefx/render/fx_renderer/gles3/shaders/embed.sh | 11 -
.../render/fx_renderer/gles3/shaders/gradient.frag | 41 -
.../render/fx_renderer/gles3/shaders/meson.build | 30 -
scenefx/render/fx_renderer/gles3/shaders/quad.frag | 31 -
.../fx_renderer/gles3/shaders/quad_grad.frag | 25 -
.../fx_renderer/gles3/shaders/quad_grad_round.frag | 46 -
.../fx_renderer/gles3/shaders/quad_round.frag | 64 -
scenefx/render/fx_renderer/gles3/shaders/tex.frag | 65 -
scenefx/render/fx_renderer/meson.build | 13 +-
scenefx/render/fx_renderer/pixel_format.c | 11 +
scenefx/render/fx_renderer/shaders.c | 244 ++--
.../fx_renderer/{gles2 => }/shaders/blur1.frag | 0
.../fx_renderer/{gles2 => }/shaders/blur2.frag | 0
.../{gles2 => }/shaders/blur_effects.frag | 2 +-
.../{gles2 => }/shaders/box_shadow.frag | 11 +-
.../fx_renderer/{gles2 => }/shaders/common.vert | 0
.../render/fx_renderer/shaders/corner_alpha.frag | 74 +
.../fx_renderer/{gles2 => }/shaders/embed.sh | 0
.../fx_renderer/{gles2 => }/shaders/gradient.frag | 0
scenefx/render/fx_renderer/shaders/meson.build | 31 +
scenefx/render/fx_renderer/shaders/quad.frag | 45 +
.../fx_renderer/{gles2 => }/shaders/quad_grad.frag | 0
.../{gles2 => }/shaders/quad_grad_round.frag | 26 +-
scenefx/render/fx_renderer/shaders/quad_round.frag | 72 +
.../fx_renderer/{gles2 => }/shaders/tex.frag | 53 +-
scenefx/render/fx_renderer/tracy.c | 212 +++
scenefx/render/meson.build | 12 +-
scenefx/render/pixel_format.c | 97 ++
.../packagefiles/tracy_v0_13_1_build_fix.patch | 22 +
scenefx/subprojects/tracy.wrap | 8 +
scenefx/tinywl/Makefile | 17 +-
scenefx/tinywl/meson.build | 2 +-
scenefx/tinywl/tinywl.c | 53 +-
scenefx/types/fx/blur_data.c | 36 +
scenefx/types/fx/clipped_region.c | 67 +-
scenefx/types/fx/corner_location.c | 76 --
scenefx/types/fx/meson.build | 1 -
scenefx/types/meson.build | 3 +-
scenefx/types/output/{wlr_output.c => output.c} | 8 +
scenefx/types/scene/linked_node.c | 78 ++
scenefx/types/scene/wlr_scene.c | 1418 +++++++++++++-------
scenefx/util/array.c | 2 +-
src/server/cursor.rs | 6 +-
src/server/input_manager.rs | 4 +-
src/server/wlroots_log_wrapper.c | 101 +-
src/server/xdg_toplevel.rs | 13 +-
93 files changed, 4293 insertions(+), 2290 deletions(-)
diff --git a/build.rs b/build.rs
index 6089ae5..09eadd4 100644
--- a/build.rs
+++ b/build.rs
@@ -31,16 +31,17 @@ fn main() {
let scenefx_include_paths = vec![scenefx_inc1, scenefx_inc2, scenefx_inc3];
println!("cargo:rustc-link-search=native={}/scenefx/build", manifest_dir);
- println!("cargo:rustc-link-lib=static=scenefx-0.4");
+ println!("cargo:rustc-link-lib=static=scenefx-0.5");
println!("cargo:rustc-link-lib=dylib=GLESv2");
println!("cargo:rustc-link-lib=dylib=EGL");
println!("cargo:rustc-link-lib=dylib=drm");
println!("cargo:rustc-link-lib=dylib=gbm");
+ println!("cargo:rustc-link-lib=dylib=lcms2");
let wlroots = pkg_config::Config::new()
- .atleast_version("0.19.0")
- .probe("wlroots-0.19")
- .expect("wlroots-0.19 is required");
+ .atleast_version("0.20.0")
+ .probe("wlroots-0.20")
+ .expect("wlroots-0.20 is required");
let wl_server = pkg_config::probe_library("wayland-server")
.expect("wayland-server is required");
diff --git a/scenefx/.github/workflows/flake-update.yml b/scenefx/.github/workflows/flake-update.yml
new file mode 100644
index 0000000..d94e1dc
--- /dev/null
+++ b/scenefx/.github/workflows/flake-update.yml
@@ -0,0 +1,24 @@
+name: "Flake.lock: update Nix dependencies"
+
+on:
+ workflow_dispatch: # allows manual triggering
+ schedule:
+ - cron: '0 0 * * 0' # runs weekly on Sunday at 00:00
+
+jobs:
+ nix-flake-update:
+ permissions:
+ contents: write
+ id-token: write
+ issues: write
+ pull-requests: write
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: DeterminateSystems/determinate-nix-action@v3
+ - uses: DeterminateSystems/update-flake-lock@main
+ with:
+ pr-title: "chore: update Nix flake inputs" # Title of PR to be created
+ pr-labels: | # Labels to be set on the PR
+ dependencies
+ automated
diff --git a/scenefx/.gitignore b/scenefx/.gitignore
index cb9554a..93805e6 100644
--- a/scenefx/.gitignore
+++ b/scenefx/.gitignore
@@ -1,3 +1,7 @@
-/subprojects/
+subprojects/*
build/
.cache/
+result
+
+!subprojects/tracy.wrap
+!subprojects/packagefiles
diff --git a/scenefx/README.md b/scenefx/README.md
index 8bdca76..bb32a23 100644
--- a/scenefx/README.md
+++ b/scenefx/README.md
@@ -4,6 +4,13 @@ wlroots is the de-facto library for building wayland compositors, and its scene
**Please note: while SceneFX is in use by SwayFX version 0.4, it is not yet ready for usage by other compositors. Please refer to the [1.0 milestone](https://github.com/wlrfx/scenefx/milestone/2) to track the remaining tasks for our stable 1.0 release**
+## Compositors Using SceneFX
+Plenty of popular wayland compositors are using SceneFX to render eyecandy, including:
+- [SwayFX](https://github.com/WillPower3309/swayfx)
+- [MangoWC](https://github.com/DreamMaoMao/mangowc)
+- [mwc](https://github.com/nikoloc/mwc)
+- dwl [with a patch](https://codeberg.org/dwl/dwl-patches/src/branch/main/stale-patches/scenefx)
+
## Installation
<a href="https://repology.org/project/scenefx/versions"><img src="https://repology.org/badge/vertical-allrepos/scenefx.svg"/></a>
@@ -31,6 +38,63 @@ Install like so:
sudo ninja -C build/ install
```
+## Troubleshooting
+
+### Using scenefx features breaks the compositor
+
+This issue might be caused by compiling scenefx and wlroots with
+Clang compiler and thin LTO(`-flto=thin`) option enabled. Try to
+compile the libraries without LTO optimizations or with GCC compiler instead.
+
+## Debugging
+
+SceneFX includes the same debugging tools and environment variables as upstream wlroots does, but with some extra goodies.
+
+### Environment variables:
+
+- `WLR_RENDERER_ALLOW_SOFTWARE=1`: Use software rendering
+- `WLR_SCENE_DEBUG_DAMAGE=rerender`: Re-render the whole display on each commit (don't use damage)
+- `WLR_SCENE_DEBUG_DAMAGE=highlight`: Highlights where damage has occurred (where SwayFX get's re-rendered)
+- `WLR_SCENE_DISABLE_DIRECT_SCANOUT=1`: Disable direct scanout (always composites, even fullscreen windows)
+- `WLR_SCENE_DISABLE_VISIBILITY=1`: Disables culling of non-visible regions of a window/buffer (an example would be a small window fully covered by an opaque window)
+- `WLR_SCENE_HIGHLIGHT_TRANSPARENT_REGION=1`: Highlights the transparent areas of a window/buffer
+- `WLR_EGL_NO_MODIFIERS=1`: Disables modifiers for EGL
+
+### Tracy profiling
+
+Optional [Tracy](https://github.com/wolfpld/tracy) profiling can be enabled for an extra good view of when and what is happening.
+
+#### Enabling:
+
+Note: These instructions will enable basic profiling
+
+1. Add SceneFX as a subproject to your compositor
+2. Import it as a subproject dependency
+3. Run `meson subprojects download` to download the tracy project into the subproject directory
+4. Compile SceneFX with `-Dtracy_enable=true` (and `--buildtype=debugoptimized` if using meson).
+5. Start your compositor
+6. Start the `tracy-profiler` and connect to the running compositor (The version of the profiler should not matter, but if any issues are encountered, try using a version that matches the subproject).
+
+To enable more advanced profiling, the compositor in question needs to be run by the root user which comes with its own drawbacks, like DBus not working out of the box. A recommended way of doing this is by running your compositor with the `-E` sudo flag, such as `sudo -E sway`.
+
+To enable DBus, you need to give the root user access to the DBus session bus by adding the following lines to the `/etc/dbus-1/session-local.conf` file (you might have to create said file), and reboot your system.
+
+```xml
+<!-- /etc/dbus-1/session-local.conf -->
+<!-- Allow root to access session bus -->
+<busconfig>
+ <policy context="mandatory">
+ <allow user="root"/>
+ </policy>
+</busconfig>
+```
+
+#### Additional tracy documentation
+
+The links below are helpful when learning how to use tracy:
+
+- https://github.com/wolfpld/tracy/releases/latest/download/tracy.pdf
+- https://www.youtube.com/watch?v=ghXk3Bk5F2U
---
[Join our Discord](https://discord.gg/qsSx397rkh)
diff --git a/scenefx/examples/scene-graph.c b/scenefx/examples/scene-graph.c
index 910d60b..7e5162c 100644
--- a/scenefx/examples/scene-graph.c
+++ b/scenefx/examples/scene-graph.c
@@ -1,7 +1,6 @@
#include <assert.h>
#include <getopt.h>
#include <scenefx/render/fx_renderer/fx_renderer.h>
-#include <scenefx/types/fx/corner_location.h>
#include <scenefx/types/wlr_scene.h>
#include <stdbool.h>
#include <stdio.h>
@@ -145,7 +144,7 @@ static void server_handle_new_surface(struct wl_listener *listener,
surface->scene_surface =
wlr_scene_surface_create(&server->scene->tree, wlr_surface);
wlr_scene_buffer_set_corner_radius(
- surface->scene_surface->buffer, corner_radius, CORNER_LOCATION_ALL);
+ surface->scene_surface->buffer, corner_radius);
wlr_scene_node_set_position(&surface->scene_surface->buffer->node,
pos + border_width, pos + border_width);
diff --git a/scenefx/flake.lock b/scenefx/flake.lock
index 2e0310d..0bc8a1a 100644
--- a/scenefx/flake.lock
+++ b/scenefx/flake.lock
@@ -1,12 +1,32 @@
{
"nodes": {
+ "flake-utils": {
+ "inputs": {
+ "systems": [
+ "systems"
+ ]
+ },
+ "locked": {
+ "lastModified": 1731533236,
+ "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
"nixpkgs": {
"locked": {
- "lastModified": 1750605355,
- "narHash": "sha256-xT8cPLTxlktxI9vSdoBlAVK7dXgd8IK59j7ZwzkkhnI=",
+ "lastModified": 1781607440,
+ "narHash": "sha256-rxO+uc/KFbSJp+pgyXRuAX6QlG9hJdnt0BXpEQRXY+U=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "3078b9a9e75f1790e6d6ef9955fdc6a2d1740cc6",
+ "rev": "3e41b24abd260e8f71dbe2f5737d24122f972158",
"type": "github"
},
"original": {
@@ -18,7 +38,24 @@
},
"root": {
"inputs": {
- "nixpkgs": "nixpkgs"
+ "flake-utils": "flake-utils",
+ "nixpkgs": "nixpkgs",
+ "systems": "systems"
+ }
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1689347949,
+ "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
+ "owner": "nix-systems",
+ "repo": "default-linux",
+ "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default-linux",
+ "type": "github"
}
}
},
diff --git a/scenefx/flake.nix b/scenefx/flake.nix
index 34b9257..95d81cb 100644
--- a/scenefx/flake.nix
+++ b/scenefx/flake.nix
@@ -1,15 +1,27 @@
{
description = "Scenefx development environment";
- inputs.nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
- outputs =
- { self, nixpkgs, ... }:
- let
- mkPackages = pkgs: {
- scenefx = pkgs.callPackage (
- { wlroots_0_19, ... }:
- pkgs.stdenv.mkDerivation {
+
+ inputs = {
+ nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
+
+ systems.url = "github:nix-systems/default-linux";
+
+ flake-utils = {
+ url = "github:numtide/flake-utils";
+ inputs.systems.follows = "systems";
+ };
+ };
+
+ outputs = { self, nixpkgs, flake-utils, ... }:
+ flake-utils.lib.eachDefaultSystem (system:
+ let
+ pkgs = import nixpkgs { inherit system; };
+
+ in {
+ packages = rec {
+ scenefx-git = pkgs.stdenv.mkDerivation {
pname = "scenefx";
- version = "0.4.0-git";
+ version = "git";
src = ./.;
outputs = [
"out"
@@ -33,7 +45,11 @@
mesa # gbm
wayland # wayland-server
wayland-protocols
- wlroots_0_19
+ wlroots_0_20
+ libgbm
+ libxcb
+ libxcb-wm
+ lcms2
];
meta = with pkgs.lib; {
@@ -42,44 +58,19 @@
license = licenses.mit;
platforms = platforms.linux;
};
- }
- ) { };
- };
-
- targetSystems = [
- "aarch64-linux"
- "x86_64-linux"
- ];
- pkgsFor = system: import nixpkgs { inherit system; };
- forEachSystem = f: nixpkgs.lib.genAttrs targetSystems (system: f (pkgsFor system));
- in
- {
- overlays = rec {
- default = insert;
- override = _: prev: mkPackages prev;
- insert = _: prev: mkPackages (pkgsFor prev.system);
- };
+ };
- packages = forEachSystem (
- pkgs: (mkPackages pkgs) // { default = self.packages.${pkgs.system}.scenefx; }
- );
+ default = scenefx-git;
+ };
- devShells = forEachSystem (pkgs: {
- default = pkgs.mkShell {
+ devShells.default = pkgs.mkShell {
name = "scenefx-shell";
- inputsFrom = [
- self.packages.${pkgs.system}.scenefx
- pkgs.wlroots_0_19
- ];
- shellHook = ''
- (
- # Copy the nix version of wlroots into the project
- mkdir -p "$PWD/subprojects" && cd "$PWD/subprojects"
- cp -R --no-preserve=mode,ownership ${pkgs.wlroots_0_19.src} wlroots
- )'';
- };
- });
+ inputsFrom = [ self.packages.${system}.scenefx-git ];
+ hardeningDisable = [ "fortify" ];
+ };
- formatter = forEachSystem (pkgs: pkgs.nixfmt-rfc-style);
- };
+ formatter = pkgs.nixfmt;
+ }
+ );
}
+
diff --git a/scenefx/include/render/color.h b/scenefx/include/render/color.h
new file mode 100644
index 0000000..60c841d
--- /dev/null
+++ b/scenefx/include/render/color.h
@@ -0,0 +1,111 @@
+#ifndef RENDER_COLOR_H
+#define RENDER_COLOR_H
+
+#include <stdint.h>
+#include <wlr/render/color.h>
+#include <wlr/util/addon.h>
+
+enum wlr_color_transform_type {
+ COLOR_TRANSFORM_INVERSE_EOTF,
+ COLOR_TRANSFORM_LCMS2,
+ COLOR_TRANSFORM_LUT_3X1D,
+ COLOR_TRANSFORM_MATRIX,
+ COLOR_TRANSFORM_PIPELINE,
+};
+
+struct wlr_color_transform {
+ int ref_count;
+ struct wlr_addon_set addons; // per-renderer helper state
+
+ enum wlr_color_transform_type type;
+};
+
+struct wlr_color_transform_inverse_eotf {
+ struct wlr_color_transform base;
+
+ enum wlr_color_transfer_function tf;
+};
+
+/**
+ * The formula is approximated via three 1D look-up tables. The flat lut_3x1d
+ * array has a length of 3 * dim.
+ *
+ * The offset of a color value for a given channel and color index is:
+ *
+ * offset = channel_index * dim + color_index
+ */
+struct wlr_color_transform_lut_3x1d {
+ struct wlr_color_transform base;
+
+ uint16_t *lut_3x1d;
+ size_t dim;
+};
+
+struct wlr_color_transform_matrix {
+ struct wlr_color_transform base;
+
+ float matrix[9];
+};
+
+struct wlr_color_transform_pipeline {
+ struct wlr_color_transform base;
+
+ struct wlr_color_transform **transforms;
+ size_t len;
+};
+
+void wlr_color_transform_init(struct wlr_color_transform *tr,
+ enum wlr_color_transform_type type);
+
+/**
+ * Get a struct wlr_color_transform_lcms2 from a generic struct wlr_color_transform.
+ * Asserts that the base type is COLOR_TRANSFORM_LCMS2.
+ */
+struct wlr_color_transform_lcms2 *color_transform_lcms2_from_base(
+ struct wlr_color_transform *tr);
+
+void color_transform_lcms2_finish(struct wlr_color_transform_lcms2 *tr);
+
+/**
+ * Evaluate a LCMS2 color transform for a given RGB triplet.
+ */
+void color_transform_lcms2_eval(struct wlr_color_transform_lcms2 *tr,
+ float out[static 3], const float in[static 3]);
+
+/**
+ * Gets a wlr_color_transform_inverse_eotf from a generic wlr_color_transform.
+ * Asserts that the base type is COLOR_TRANSFORM_INVERSE_EOTF
+ */
+struct wlr_color_transform_inverse_eotf *wlr_color_transform_inverse_eotf_from_base(
+ struct wlr_color_transform *tr);
+
+/**
+ * Get a struct wlr_color_transform_lut_3x1d from a generic
+ * struct wlr_color_transform. Asserts that the base type is
+ * COLOR_TRANSFORM_LUT_3X1D.
+ */
+struct wlr_color_transform_lut_3x1d *color_transform_lut_3x1d_from_base(
+ struct wlr_color_transform *tr);
+
+/**
+ * Create a simplified / normalized wlr_color_transform pipeline.
+ * `transforms` may contain NULL transforms, they will be interpreted as the
+ * identity transform, and removed.
+ * `*result` may be set to a transform of a type different from
+ * `wlr_color_transform_pipeline`, or to NULL if all input transforms are NULL
+ */
+bool color_transform_compose(struct wlr_color_transform **result,
+ struct wlr_color_transform **transforms, size_t len);
+
+/**
+ * Compute the matrix to convert RGB color values to CIE 1931 XYZ.
+ */
+void wlr_color_primaries_to_xyz(const struct wlr_color_primaries *primaries, float matrix[static 9]);
+
+/**
+ * Get default luminances for a transfer function.
+ */
+void wlr_color_transfer_function_get_default_luminance(enum wlr_color_transfer_function tf,
+ struct wlr_color_luminances *lum);
+
+#endif
diff --git a/scenefx/include/render/fx_renderer/fx_renderer.h b/scenefx/include/render/fx_renderer/fx_renderer.h
index ea8e7fb..ba6cc4b 100644
--- a/scenefx/include/render/fx_renderer/fx_renderer.h
+++ b/scenefx/include/render/fx_renderer/fx_renderer.h
@@ -13,6 +13,7 @@
#include <wlr/util/box.h>
#include "render/fx_renderer/shaders.h"
+#include "render/tracy.h"
struct fx_framebuffer;
@@ -49,9 +50,12 @@ struct fx_framebuffer {
struct wlr_addon addon;
};
-/** Should only be used with custom fbs */
+/**
+ * Should only be used with custom fbs.
+ * Note: Does not bind back to the default Framebuffer!
+ */
void fx_framebuffer_get_or_create_custom(struct fx_renderer *fx_renderer,
- struct wlr_output *output, struct wlr_swapchain *swapchain,
+ struct wlr_allocator *allocator, int width, int height, bool has_alpha,
struct fx_framebuffer **fx_buffer, bool *failed);
struct fx_framebuffer *fx_framebuffer_get_or_create(struct fx_renderer *renderer,
@@ -59,6 +63,10 @@ struct fx_framebuffer *fx_framebuffer_get_or_create(struct fx_renderer *renderer
void fx_framebuffer_bind(struct fx_framebuffer *buffer);
+/**
+ * Destroy the fx_framebuffer.
+ * Note: Doesn't drop the wlr_buffer, so should only be used internally.
+ */
void fx_framebuffer_destroy(struct fx_framebuffer *buffer);
///
@@ -172,16 +180,25 @@ struct fx_renderer {
PFNGLGETQUERYOBJECTIVEXTPROC glGetQueryObjectivEXT;
PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64vEXT;
PFNGLGETINTEGER64VEXTPROC glGetInteger64vEXT;
+ TRACY_FN(
+ PFNGLGETQUERYIVEXTPROC glGetQueryivEXT;
+ )
} procs;
struct {
struct quad_shader quad;
+ struct quad_shader quad_clip;
struct quad_grad_shader quad_grad;
struct quad_round_shader quad_round;
struct quad_grad_round_shader quad_grad_round;
+
struct tex_shader tex_rgba;
struct tex_shader tex_rgbx;
struct tex_shader tex_ext;
+ struct tex_shader tex_effects_rgba;
+ struct tex_shader tex_effects_rgbx;
+ struct tex_shader tex_effects_ext;
+
struct box_shadow_shader box_shadow;
struct blur_shader blur1;
struct blur_shader blur2;
@@ -190,10 +207,11 @@ struct fx_renderer {
struct wl_list buffers; // fx_framebuffer.link
struct wl_list textures; // fx_texture.link
+ struct wl_list offscreen_buffers; // fx_offscreen_buffers.link
- // Set to true when 'wlr_renderer_begin_buffer_pass' is called instead of
- // our custom 'fx_renderer_begin_buffer_pass' function
- bool basic_renderer;
+ TRACY_FN(
+ struct tracy_data *tracy_data;
+ )
};
#endif
diff --git a/scenefx/include/render/fx_renderer/shaders.h b/scenefx/include/render/fx_renderer/shaders.h
index 650b6e4..bd05f70 100644
--- a/scenefx/include/render/fx_renderer/shaders.h
+++ b/scenefx/include/render/fx_renderer/shaders.h
@@ -3,12 +3,14 @@
#include <GLES2/gl2.h>
#include <stdbool.h>
+#include <scenefx/types/fx/clipped_region.h>
+#include "types/fx/clipped_region.h"
struct fx_renderer;
GLuint compile_shader(GLuint type, const GLchar *src);
-GLuint link_program(const GLchar *frag_src, GLint client_version);
+GLuint link_program(const GLchar *frag_src);
bool check_gl_ext(const char *exts, const char *ext);
@@ -20,21 +22,31 @@ enum fx_tex_shader_source {
SHADER_SOURCE_TEXTURE_EXTERNAL = 3,
};
+struct shader_corner_radii {
+ GLint top_left;
+ GLint top_right;
+ GLint bottom_left;
+ GLint bottom_right;
+};
+
+void uniform_corner_radii_set(const struct shader_corner_radii *uniform,
+ const struct fx_corner_fradii *corners);
+
struct quad_shader {
GLuint program;
GLint proj;
GLint color;
GLint pos_attrib;
- GLint clip_size;
- GLint clip_position;
- GLint clip_radius_top_left;
- GLint clip_radius_top_right;
- GLint clip_radius_bottom_left;
- GLint clip_radius_bottom_right;
+ // Only used for the effects shader
+ struct {
+ GLint clip_size;
+ GLint clip_position;
+ struct shader_corner_radii clip_radius;
+ } effects;
};
-bool link_quad_program(struct quad_shader *shader, GLint client_version);
+bool link_quad_program(struct quad_shader *shader, bool clip);
struct quad_grad_shader {
int max_len;
@@ -52,7 +64,7 @@ struct quad_grad_shader {
GLint blend;
};
-bool link_quad_grad_program(struct quad_grad_shader *shader, GLint client_version, int max_len);
+bool link_quad_grad_program(struct quad_grad_shader *shader, int max_len);
struct quad_round_shader {
GLuint program;
@@ -62,21 +74,15 @@ struct quad_round_shader {
GLint size;
GLint position;
- GLint radius_top_left;
- GLint radius_top_right;
- GLint radius_bottom_left;
- GLint radius_bottom_right;
+ struct shader_corner_radii radius;
GLint clip_size;
GLint clip_position;
- GLint clip_radius_top_left;
- GLint clip_radius_top_right;
- GLint clip_radius_bottom_left;
- GLint clip_radius_bottom_right;
+ struct shader_corner_radii clip_radius;
GLint fade_inset;
};
-bool link_quad_round_program(struct quad_round_shader *shader, GLint client_version);
+bool link_quad_round_program(struct quad_round_shader *shader);
struct quad_grad_round_shader {
GLuint program;
@@ -95,15 +101,12 @@ struct quad_grad_round_shader {
GLint count;
GLint blend;
- GLint radius_top_left;
- GLint radius_top_right;
- GLint radius_bottom_left;
- GLint radius_bottom_right;
+ struct shader_corner_radii radius;
int max_len;
};
-bool link_quad_grad_round_program(struct quad_grad_round_shader *shader, GLint client_version, int max_len);
+bool link_quad_grad_round_program(struct quad_grad_round_shader *shader, int max_len);
struct tex_shader {
GLuint program;
@@ -112,17 +115,23 @@ struct tex_shader {
GLint tex;
GLint alpha;
GLint pos_attrib;
- GLint size;
- GLint position;
- GLint radius_top_left;
- GLint radius_top_right;
- GLint radius_bottom_left;
- GLint radius_bottom_right;
GLint discard_transparent;
+
+ // Only used for the effects shader
+ struct {
+ GLint size;
+ GLint position;
+ struct shader_corner_radii radius;
+
+ GLint clip_size;
+ GLint clip_position;
+ struct shader_corner_radii clip_radius;
+ } effects;
};
-bool link_tex_program(struct tex_shader *shader, GLint client_version, enum fx_tex_shader_source source);
+bool link_tex_program(struct tex_shader *shader, enum fx_tex_shader_source source,
+ bool effects);
struct box_shadow_shader {
GLuint program;
@@ -136,13 +145,10 @@ struct box_shadow_shader {
GLint clip_position;
GLint clip_size;
- GLint clip_radius_top_left;
- GLint clip_radius_top_right;
- GLint clip_radius_bottom_left;
- GLint clip_radius_bottom_right;
+ struct shader_corner_radii clip_radius;
};
-bool link_box_shadow_program(struct box_shadow_shader *shader, GLint client_version);
+bool link_box_shadow_program(struct box_shadow_shader *shader);
struct blur_shader {
GLuint program;
@@ -154,8 +160,8 @@ struct blur_shader {
GLint halfpixel;
};
-bool link_blur1_program(struct blur_shader *shader, GLint client_version);
-bool link_blur2_program(struct blur_shader *shader, GLint client_version);
+bool link_blur1_program(struct blur_shader *shader);
+bool link_blur2_program(struct blur_shader *shader);
struct blur_effects_shader {
GLuint program;
@@ -169,6 +175,6 @@ struct blur_effects_shader {
GLfloat saturation;
};
-bool link_blur_effects_program(struct blur_effects_shader *shader, GLint client_version);
+bool link_blur_effects_program(struct blur_effects_shader *shader);
#endif
diff --git a/scenefx/include/render/pass.h b/scenefx/include/render/pass.h
index 0ee9550..33b8624 100644
--- a/scenefx/include/render/pass.h
+++ b/scenefx/include/render/pass.h
@@ -13,4 +13,8 @@ struct fx_render_texture_options fx_render_texture_options_default(
struct fx_render_rect_options fx_render_rect_options_default(
const struct wlr_render_rect_options *base);
+struct fx_gles_render_pass *fx_begin_buffer_pass(struct fx_framebuffer *buffer,
+ struct wlr_egl_context *prev_ctx, struct fx_render_timer *timer,
+ struct wlr_drm_syncobj_timeline *signal_timeline, uint64_t signal_point);
+
#endif
diff --git a/scenefx/include/render/pixel_format.h b/scenefx/include/render/pixel_format.h
index 0b20aa5..788dc74 100644
--- a/scenefx/include/render/pixel_format.h
+++ b/scenefx/include/render/pixel_format.h
@@ -63,4 +63,9 @@ enum wl_shm_format convert_drm_format_to_wl_shm(uint32_t fmt);
*/
bool pixel_format_has_alpha(uint32_t fmt);
+/**
+ * Return true if the DRM FourCC fmt belongs to a YCbCr colorspace family, false otherwise.
+ */
+bool pixel_format_is_ycbcr(uint32_t fmt);
+
#endif
diff --git a/scenefx/include/render/tracy.h b/scenefx/include/render/tracy.h
new file mode 100644
index 0000000..9d42371
--- /dev/null
+++ b/scenefx/include/render/tracy.h
@@ -0,0 +1,178 @@
+#ifndef TRACY_H
+#define TRACY_H
+
+#ifdef TRACY_ENABLE
+#define TRACY_FN(...) __VA_ARGS__
+#else
+#define TRACY_FN(...) // noop
+#endif // TRACY_ENABLE
+
+#ifdef TRACY_ENABLE
+#include <tracy/TracyC.h>
+
+struct fx_renderer;
+
+struct tracy_data;
+struct tracy_gpu_zone_context {
+ struct tracy_data *tracy_data;
+ bool is_active;
+};
+
+// Private functions: Don't use these outside of this file!
+
+void tracy_gpu_zone_begin(struct tracy_data *tracy_data, struct tracy_gpu_zone_context *out_ctx,
+ const int line, const char *source, const char *func, const char *name);
+void tracy_gpu_zone_end(struct tracy_gpu_zone_context *ctx);
+
+void tracy_gpu_context_collect(struct tracy_data *tracy_data);
+void tracy_gpu_context_destroy(struct tracy_data *tracy_data);
+struct tracy_data *tracy_gpu_context_new(struct fx_renderer *renderer);
+#endif // TRACY_ENABLE
+
+#define TRACY_WHEN_CONNECTED(fn) \
+ TRACY_FN(if (TracyCIsConnected) { fn })
+
+/**
+ * Frame
+ */
+
+#define TRACY_MARK_FRAME TRACY_FN(TracyCFrameMark)
+
+#define TRACY_RESCHEDULE TRACY_FN(TracyCFrameMark)
+
+/**
+ * Messaging
+ */
+
+#define TRACY_MESSAGE(...) \
+ TRACY_FN({ \
+ int len = snprintf(NULL, 0, __VA_ARGS__) + 1; \
+ char str[len]; \
+ snprintf(str, len, __VA_ARGS__); \
+ TracyCMessage(str, len); \
+ })
+#define TRACY_MESSAGE_COLOR(color, ...) \
+ TRACY_FN({ \
+ int len = snprintf(NULL, 0, __VA_ARGS__) + 1; \
+ char str[len]; \
+ snprintf(str, len, __VA_ARGS__); \
+ TracyCMessageC(str, len, color); \
+ })
+#define TRACY_MESSAGE_ERROR(...) \
+ TRACY_FN( \
+ TRACY_MESSAGE_COLOR(0xFF0000, __VA_ARGS__) \
+ )
+
+/**
+ * Zone
+ */
+
+#define TRACY_ZONE_START \
+ TRACY_FN( \
+ TracyCZone(ctx, true) \
+ )
+#define TRACY_ZONE_START_N(name) \
+ TRACY_FN( \
+ TracyCZoneN(ctx, name, true) \
+ )
+#define TRACY_ZONE_END \
+ TRACY_FN( \
+ TRACY_ZONE_TEXT_f("Success On Line: %d", __LINE__) \
+ TracyCZoneEnd(ctx); \
+ )
+#define TRACY_ZONE_END_FAIL \
+ TRACY_FN( \
+ TRACY_ZONE_TEXT_f("Fail On Line: %d", __LINE__) \
+ TracyCZoneEnd(ctx); \
+ )
+
+/**
+ * Zone Helpers
+ */
+
+#define TRACY_ZONE_TEXT(txt, size) \
+ TRACY_FN(TracyCZoneText(ctx, txt, size);)
+#define TRACY_ZONE_TEXT_f(...) \
+ TRACY_FN({ \
+ int len = snprintf(NULL, 0, __VA_ARGS__) + 1; \
+ char str[len]; \
+ snprintf(str, len, __VA_ARGS__); \
+ TracyCZoneText(ctx, str, len); \
+ })
+#define TRACY_ZONE_NAME(txt, size) \
+ TRACY_FN( \
+ TracyCZoneName(ctx, txt, size); \
+ )
+#define TRACY_ZONE_NAME_f(...) \
+ TRACY_FN({ \
+ int len = snprintf(NULL, 0, __VA_ARGS__) + 1; \
+ char str[len]; \
+ snprintf(str, len, __VA_ARGS__); \
+ TracyCZoneName(ctx, str, len); \
+ })
+#define TRACY_ZONE_COLOR(color) \
+ TRACY_FN( \
+ TracyCZoneColor(ctx, color); \
+ )
+#define TRACY_ZONE_VALUE(value) \
+ TRACY_FN( \
+ TracyCZoneValue(ctx, value); \
+ )
+
+/**
+ * GPU Zone
+ */
+
+#define TRACY_GPU_ZONE_START_NAME(renderer, name) \
+ TRACY_FN( \
+ struct tracy_gpu_zone_context gpu_ctx; \
+ tracy_gpu_zone_begin(renderer->tracy_data, &gpu_ctx, __LINE__, __FILE__, __func__, name) \
+ )
+#define TRACY_GPU_ZONE_START(renderer) \
+ TRACY_FN( \
+ struct tracy_gpu_zone_context gpu_ctx; \
+ tracy_gpu_zone_begin(renderer->tracy_data, &gpu_ctx, __LINE__, __FILE__, __func__, __func__) \
+ )
+#define TRACY_GPU_ZONE_END \
+ TRACY_FN( \
+ tracy_gpu_zone_end(&gpu_ctx) \
+ )
+#define TRACY_GPU_ZONE_COLLECT(renderer) \
+ TRACY_FN( \
+ tracy_gpu_context_collect(renderer->tracy_data) \
+ )
+
+/**
+ * GPU Context
+ */
+
+#define TRACY_GPU_CONTEXT_NEW(renderer) \
+ TRACY_FN( \
+ tracy_gpu_context_new(renderer) \
+ )
+#define TRACY_GPU_CONTEXT_DESTROY(tracy_data) \
+ TRACY_FN( \
+ tracy_gpu_context_destroy(tracy_data); \
+ )
+
+/**
+ * GPU/CPU Zones Helpers
+ */
+
+#define TRACY_BOTH_ZONES_START(renderer) \
+ TRACY_FN( \
+ TRACY_ZONE_START \
+ TRACY_GPU_ZONE_START(renderer) \
+ )
+#define TRACY_BOTH_ZONES_END \
+ TRACY_FN( \
+ TRACY_ZONE_END \
+ TRACY_GPU_ZONE_END \
+ )
+#define TRACY_BOTH_ZONES_END_FAIL \
+ TRACY_FN( \
+ TRACY_ZONE_END_FAIL \
+ TRACY_GPU_ZONE_END \
+ )
+
+#endif // !TRACY_H
diff --git a/scenefx/include/scenefx/render/fx_renderer/fx_effect_framebuffers.h b/scenefx/include/scenefx/render/fx_renderer/fx_effect_framebuffers.h
deleted file mode 100644
index 0938db4..0000000
--- a/scenefx/include/scenefx/render/fx_renderer/fx_effect_framebuffers.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef _FX_EFFECT_FRAMEBUFFERS_H
-#define _FX_EFFECT_FRAMEBUFFERS_H
-
-#include <wlr/types/wlr_output.h>
-#include <wlr/util/addon.h>
-
-/**
- * Used to add effect framebuffers per output instead of every output sharing
- * them.
- */
-struct fx_effect_framebuffers {
- struct wlr_addon addon;
-
- // Contains the blurred background for tiled windows
- struct fx_framebuffer *optimized_blur_buffer;
- // Contains the original pixels to draw over the areas where artifact are visible
- struct fx_framebuffer *blur_saved_pixels_buffer;
- // Blur swaps between the two effects buffers everytime it scales the image
- // Buffer used for effects
- struct fx_framebuffer *effects_buffer;
- // Swap buffer used for effects
- struct fx_framebuffer *effects_buffer_swapped;
-
- // The region where there's blur
- pixman_region32_t blur_padding_region;
-};
-
-struct fx_effect_framebuffers *fx_effect_framebuffers_try_get(struct wlr_output *output);
-
-#endif
diff --git a/scenefx/include/scenefx/render/fx_renderer/fx_offscreen_buffers.h b/scenefx/include/scenefx/render/fx_renderer/fx_offscreen_buffers.h
new file mode 100644
index 0000000..c3e2e02
--- /dev/null
+++ b/scenefx/include/scenefx/render/fx_renderer/fx_offscreen_buffers.h
@@ -0,0 +1,32 @@
+#ifndef _FX_OFFSCREEN_BUFFERS_H
+#define _FX_OFFSCREEN_BUFFERS_H
+
+#include <wlr/types/wlr_output.h>
+#include <wlr/util/addon.h>
+
+/**
+ * Used to add effect framebuffers per output instead of every output sharing
+ * them.
+ */
+struct fx_offscreen_buffers {
+ struct wl_list link; // fx_renderer.offscreen_buffers
+ struct wlr_addon addon;
+
+ // Contains the blurred background for tiled windows
+ struct fx_framebuffer *optimized_blur_buffer;
+ // Contains the non-blurred background for tiled windows. Used for blurring
+ // optimized surfaces with an alpha. Just as inefficient as the regular blur.
+ struct fx_framebuffer *optimized_no_blur_buffer;
+ // Contains the original pixels to draw over the areas where artifact are visible
+ struct fx_framebuffer *blur_saved_pixels_buffer;
+ // Blur swaps between the two effects buffers every time it scales the image
+ // Buffer used for effects
+ struct fx_framebuffer *effects_buffer;
+ // Swap buffer used for effects
+ struct fx_framebuffer *effects_buffer_swapped;
+};
+
+void fx_offscreen_buffers_destroy(struct fx_offscreen_buffers *fbos);
+struct fx_offscreen_buffers *fx_offscreen_buffers_try_get(struct wlr_output *output);
+
+#endif
diff --git a/scenefx/include/scenefx/render/pass.h b/scenefx/include/scenefx/render/pass.h
index e8adb68..75f0fd5 100644
--- a/scenefx/include/scenefx/render/pass.h
+++ b/scenefx/include/scenefx/render/pass.h
@@ -7,37 +7,26 @@
#include <wlr/render/swapchain.h>
#include "render/egl.h"
-#include "scenefx/types/fx/clipped_region.h"
-#include "scenefx/types/fx/corner_location.h"
+#include "types/fx/clipped_region.h"
struct fx_gles_render_pass {
struct wlr_render_pass base;
struct fx_framebuffer *buffer;
- struct fx_effect_framebuffers *fx_effect_framebuffers;
- struct wlr_output *output;
float projection_matrix[9];
struct wlr_egl_context prev_ctx;
struct fx_render_timer *timer;
struct wlr_drm_syncobj_timeline *signal_timeline;
uint64_t signal_point;
-};
-
-struct fx_buffer_pass_options {
- const struct wlr_buffer_pass_options *base;
- struct wlr_swapchain *swapchain;
+ // The region where there's blur
+ pixman_region32_t blur_padding_region;
+ bool has_blur;
+ // Contains output-specific framebuffers.
+ // NULL when no advanced effects like blur is being used in the current pass.
+ // Call `fx_render_pass_init_offscreen_buffers` to use advanced effects.
+ struct fx_offscreen_buffers *fx_offscreen_buffers;
};
-/**
- * Begin a new render pass with the supplied destination buffer.
- *
- * Callers must call wlr_render_pass_submit() once they are done with the
- * render pass.
- */
-struct fx_gles_render_pass *fx_renderer_begin_buffer_pass(struct wlr_renderer *wlr_renderer,
- struct wlr_buffer *wlr_buffer, struct wlr_output *output,
- const struct fx_buffer_pass_options *options);
-
struct fx_gradient {
float degree;
/* The full area the gradient fit too, for borders use the window size */
@@ -55,14 +44,14 @@ struct fx_gradient {
struct fx_render_texture_options {
struct wlr_render_texture_options base;
const struct wlr_box *clip_box; // Used to clip csd. Ignored if NULL
- enum corner_location corners;
- int corner_radius;
+ struct fx_corner_fradii corners;
bool discard_transparent;
+ struct clipped_fregion clipped_region;
};
struct fx_render_rect_options {
struct wlr_render_rect_options base;
- struct clipped_region clipped_region;
+ struct clipped_fregion clipped_region;
};
struct fx_render_rect_grad_options {
@@ -72,22 +61,20 @@ struct fx_render_rect_grad_options {
struct fx_render_rounded_rect_options {
struct wlr_render_rect_options base;
- int corner_radius;
- enum corner_location corners;
- struct clipped_region clipped_region;
+ struct fx_corner_fradii corners;
+ struct clipped_fregion clipped_region;
int fade_inset;
};
struct fx_render_rounded_rect_grad_options {
struct wlr_render_rect_options base;
struct fx_gradient gradient;
- int corner_radius;
- enum corner_location corners;
+ struct fx_corner_fradii corners;
};
struct fx_render_box_shadow_options {
struct wlr_box box;
- struct clipped_region clipped_region;
+ struct clipped_fregion clipped_region;
/* Clip region, leave NULL to disable clipping */
const pixman_region32_t *clip;
@@ -98,13 +85,24 @@ struct fx_render_box_shadow_options {
struct fx_render_blur_pass_options {
struct fx_render_texture_options tex_options;
- pixman_region32_t *opaque_region;
struct fx_framebuffer *current_buffer;
struct blur_data *blur_data;
bool use_optimized_blur;
bool ignore_transparent;
+ float blur_strength;
+ struct fx_corner_fradii corners;
+ struct clipped_fregion clipped_region;
};
+struct fx_gles_render_pass *fx_get_render_pass(struct wlr_render_pass *render_pass);
+
+/**
+ * Initializes the render pass offscreen buffers required for advanced effects
+ * like blur.
+ */
+bool fx_render_pass_init_offscreen_buffers(struct wlr_render_pass *render_pass,
+ struct wlr_output *output);
+
/**
* Render a fx texture.
*/
@@ -156,7 +154,7 @@ bool fx_render_pass_add_optimized_blur(struct fx_gles_render_pass *pass,
/**
* Render from one buffer to another
*/
-void fx_renderer_read_to_buffer(struct fx_gles_render_pass *pass,
+void fx_render_pass_read_to_buffer(struct fx_gles_render_pass *pass,
pixman_region32_t *region, struct fx_framebuffer *dst_buffer,
struct fx_framebuffer *src_buffer);
diff --git a/scenefx/include/scenefx/types/fx/blur_data.h b/scenefx/include/scenefx/types/fx/blur_data.h
index 82b3c98..967c2a0 100644
--- a/scenefx/include/scenefx/types/fx/blur_data.h
+++ b/scenefx/include/scenefx/types/fx/blur_data.h
@@ -6,7 +6,7 @@
struct blur_data {
int num_passes;
- int radius;
+ float radius;
float noise;
float brightness;
float contrast;
@@ -21,4 +21,6 @@ bool blur_data_should_parameters_blur_effects(struct blur_data *blur_data);
int blur_data_calc_size(struct blur_data *blur_data);
+struct blur_data blur_data_apply_strength(struct blur_data *blur_data, float strength);
+
#endif
diff --git a/scenefx/include/scenefx/types/fx/clipped_region.h b/scenefx/include/scenefx/types/fx/clipped_region.h
index 8449004..02bac5d 100644
--- a/scenefx/include/scenefx/types/fx/clipped_region.h
+++ b/scenefx/include/scenefx/types/fx/clipped_region.h
@@ -1,16 +1,98 @@
-#ifndef TYPES_FX_CLIPPED_REGION_H
-#define TYPES_FX_CLIPPED_REGION_H
+#ifndef SCENEFX_TYPES_FX_CLIPPED_REGION_H
+#define SCENEFX_TYPES_FX_CLIPPED_REGION_H
#include <wlr/util/box.h>
-#include "scenefx/types/fx/corner_location.h"
+// uint16_t is a reasonable enough range for corner radius
+// and makes it 8 bytes wide, making it cheap with optimisations
+// Note: layout is on purpose to be clockwise
+struct fx_corner_radii {
+ uint16_t top_left;
+ uint16_t top_right;
+ uint16_t bottom_right;
+ uint16_t bottom_left;
+};
+
+#define CORNER_RADIUS_MAX (UINT16_MAX)
+
+static __always_inline uint16_t corner_radius_clamp(int radius) {
+ if (radius <= 0) {
+ return 0;
+ }
+
+ if (radius > CORNER_RADIUS_MAX) {
+ return CORNER_RADIUS_MAX;
+ }
+
+ return radius;
+}
+
+static __always_inline struct fx_corner_radii corner_radii_new(int top_left, int top_right, int bottom_right, int bottom_left) {
+ return (struct fx_corner_radii) {
+ corner_radius_clamp(top_left),
+ corner_radius_clamp(top_right),
+ corner_radius_clamp(bottom_right),
+ corner_radius_clamp(bottom_left),
+ };
+}
+
+static __always_inline struct fx_corner_radii corner_radii_all(int radius) {
+ return corner_radii_new(radius, radius, radius, radius);
+}
+
+static __always_inline struct fx_corner_radii corner_radii_none(void) {
+ return corner_radii_all(0);
+}
+
+#define corner_radii_func(name, tl, tr, br, bl) static __always_inline struct fx_corner_radii corner_radii_##name(int radius) { return corner_radii_new(tl, tr, br, bl); }
+
+corner_radii_func(top, radius, radius, 0, 0);
+corner_radii_func(bottom, 0, 0, radius, radius);
+corner_radii_func(left, radius, 0, 0, radius);
+corner_radii_func(right, 0, radius, radius, 0);
+
+struct fx_corner_radii fx_corner_radii_extend(struct fx_corner_radii corners, int extend);
+
+void fx_corner_radii_transform(enum wl_output_transform transform,
+ struct fx_corner_radii *corners);
+
+bool fx_corner_radii_eq(struct fx_corner_radii lhs, struct fx_corner_radii rhs);
+
+/**
+ * Retains the corner values from `input` based on if the corresponding corner value in `filter` is non-zero
+ *
+ * This can be compared to an `AND` operation
+ */
+static __always_inline struct fx_corner_radii fx_corner_radii_filter(struct fx_corner_radii input, struct fx_corner_radii filter) {
+ return corner_radii_new(
+ input.top_left && filter.top_left ? input.top_left : 0,
+ input.top_right && filter.top_right ? input.top_right : 0,
+ input.bottom_right && filter.bottom_right ? input.bottom_right : 0,
+ input.bottom_left && filter.bottom_left ? input.bottom_left : 0
+ );
+};
+
+/**
+ * Picks the value of the corner from `lhs` if non-zero, otherwise picks the corner value from `rhs`
+ *
+ * This can be compared to an `OR` operation
+ */
+static __always_inline struct fx_corner_radii fx_corner_radii_pick(struct fx_corner_radii lhs, struct fx_corner_radii rhs) {
+ return corner_radii_new(
+ lhs.top_left ? lhs.top_left : rhs.top_left,
+ lhs.top_right ? lhs.top_right : rhs.top_right,
+ lhs.bottom_right ? lhs.bottom_right : rhs.bottom_right,
+ lhs.bottom_left ? lhs.bottom_left : rhs.bottom_left
+ );
+};
+
+bool fx_corner_radii_is_empty(const struct fx_corner_radii* corners);
struct clipped_region {
struct wlr_box area;
- int corner_radius;
- enum corner_location corners;
+ struct fx_corner_radii corners;
};
struct clipped_region clipped_region_get_default(void);
-#endif // !TYPES_FX_CLIPPED_REGION_H
+#endif // !SCENEFX_TYPES_FX_CLIPPED_REGION_H
diff --git a/scenefx/include/scenefx/types/fx/corner_location.h b/scenefx/include/scenefx/types/fx/corner_location.h
deleted file mode 100644
index cd94046..0000000
--- a/scenefx/include/scenefx/types/fx/corner_location.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef TYPES_FX_CORNER_LOCATION_H
-#define TYPES_FX_CORNER_LOCATION_H
-
-#include <wayland-server-protocol.h>
-
-#define CORNER_LOCATION_LAST (1 << 3) + 1
-#define CORNER_LOCATION_COUNT 5
-
-enum corner_location {
- CORNER_LOCATION_NONE = 0,
- CORNER_LOCATION_TOP_LEFT = 1 << 0,
- CORNER_LOCATION_TOP_RIGHT = 1 << 1,
- CORNER_LOCATION_BOTTOM_RIGHT = 1 << 2,
- CORNER_LOCATION_BOTTOM_LEFT = 1 << 3,
- CORNER_LOCATION_TOP = CORNER_LOCATION_TOP_LEFT | CORNER_LOCATION_TOP_RIGHT,
- CORNER_LOCATION_BOTTOM = CORNER_LOCATION_BOTTOM_LEFT | CORNER_LOCATION_BOTTOM_RIGHT,
- CORNER_LOCATION_LEFT = CORNER_LOCATION_TOP_LEFT | CORNER_LOCATION_BOTTOM_LEFT,
- CORNER_LOCATION_RIGHT = CORNER_LOCATION_TOP_RIGHT | CORNER_LOCATION_BOTTOM_RIGHT,
- CORNER_LOCATION_ALL = CORNER_LOCATION_TOP_LEFT | CORNER_LOCATION_TOP_RIGHT
- | CORNER_LOCATION_BOTTOM_LEFT | CORNER_LOCATION_BOTTOM_RIGHT,
-};
-
-void corner_location_transform(enum wl_output_transform transform,
- enum corner_location *corners);
-
-#endif // !TYPES_FX_CORNER_LOCATION_H
diff --git a/scenefx/include/scenefx/types/linked_node.h b/scenefx/include/scenefx/types/linked_node.h
new file mode 100644
index 0000000..4d74241
--- /dev/null
+++ b/scenefx/include/scenefx/types/linked_node.h
@@ -0,0 +1,33 @@
+#ifndef TYPES_LINKED_NODES_H
+#include <stdbool.h>
+#define TYPES_LINKED_NODES_H
+
+/**
+ * A node in a link between two objects. Can be used to safely couple two
+ * objects together.
+ */
+struct linked_node {
+ struct link *link;
+};
+
+#define linked_node_init() \
+((struct linked_node) { \
+.link = NULL \
+})
+
+bool linked_nodes_are_linked(struct linked_node *node_1,
+ struct linked_node *node_2);
+
+void linked_node_init_link(struct linked_node *main_node,
+ struct linked_node *reference_node);
+
+struct linked_node *linked_nodes_get_sibling(struct linked_node *node);
+
+void linked_node_orphan(struct linked_node *node);
+
+void linked_node_unlink(struct linked_node *main_node,
+ struct linked_node *reference_node);
+
+void linked_node_destroy(struct linked_node *node);
+
+#endif
diff --git a/scenefx/include/scenefx/types/wlr_scene.h b/scenefx/include/scenefx/types/wlr_scene.h
index d1011c7..7e59617 100644
--- a/scenefx/include/scenefx/types/wlr_scene.h
+++ b/scenefx/include/scenefx/types/wlr_scene.h
@@ -30,7 +30,7 @@
#include "scenefx/types/fx/blur_data.h"
#include "scenefx/types/fx/clipped_region.h"
-#include "scenefx/types/fx/corner_location.h"
+#include "scenefx/types/linked_node.h"
struct wlr_output;
struct wlr_output_layout;
@@ -46,6 +46,8 @@ struct wlr_scene_output_layout;
struct wlr_presentation;
struct wlr_linux_dmabuf_v1;
+struct wlr_gamma_control_manager_v1;
+struct wlr_color_manager_v1;
struct wlr_output_state;
typedef bool (*wlr_scene_buffer_point_accepts_input_func_t)(
@@ -57,9 +59,10 @@ typedef void (*wlr_scene_buffer_iterator_func_t)(
enum wlr_scene_node_type {
WLR_SCENE_NODE_TREE,
WLR_SCENE_NODE_RECT,
- WLR_SCENE_NODE_SHADOW,
WLR_SCENE_NODE_BUFFER,
+ WLR_SCENE_NODE_SHADOW,
WLR_SCENE_NODE_OPTIMIZED_BLUR,
+ WLR_SCENE_NODE_BLUR,
};
/** A node is an object in the scene. */
@@ -107,11 +110,15 @@ struct wlr_scene {
// May be NULL
struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1;
struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1;
+ struct wlr_color_manager_v1 *color_manager_v1;
+
+ bool restack_xwayland_surfaces;
struct {
struct wl_listener linux_dmabuf_v1_destroy;
struct wl_listener gamma_control_manager_v1_destroy;
struct wl_listener gamma_control_manager_v1_set_gamma;
+ struct wl_listener color_manager_v1_destroy;
enum wlr_scene_debug_damage_option debug_damage_option;
bool direct_scanout;
@@ -133,8 +140,6 @@ struct wlr_scene_surface {
struct wlr_addon addon;
struct wl_listener outputs_update;
- struct wl_listener output_enter;
- struct wl_listener output_leave;
struct wl_listener output_sample;
struct wl_listener frame_done;
struct wl_listener surface_destroy;
@@ -147,11 +152,8 @@ struct wlr_scene_rect {
struct wlr_scene_node node;
int width, height;
float color[4];
- int corner_radius;
- enum corner_location corners;
- bool backdrop_blur;
- bool backdrop_blur_optimized;
+ struct fx_corner_radii corners;
bool accepts_input;
struct clipped_region clipped_region;
int fade_inset;
@@ -168,6 +170,21 @@ struct wlr_scene_shadow {
struct clipped_region clipped_region;
};
+struct wlr_scene_blur {
+ struct wlr_scene_node node;
+ int width, height;
+
+ struct fx_corner_radii corners;
+ struct clipped_region clipped_region;
+
+ float strength;
+ float alpha;
+
+ bool should_only_blur_bottom_layer;
+
+ struct linked_node transparency_mask_source;
+};
+
/** A scene-graph node telling SceneFX to render the optimized blur */
struct wlr_scene_optimized_blur {
struct wlr_scene_node node;
@@ -184,6 +201,13 @@ struct wlr_scene_outputs_update_event {
struct wlr_scene_output_sample_event {
struct wlr_scene_output *output;
bool direct_scanout;
+ struct wlr_drm_syncobj_timeline *release_timeline;
+ uint64_t release_point;
+};
+
+struct wlr_scene_frame_done_event {
+ struct wlr_scene_output *output;
+ struct timespec when;
};
/** A scene-graph node displaying a buffer */
@@ -198,7 +222,7 @@ struct wlr_scene_buffer {
struct wl_signal output_enter; // struct wlr_scene_output
struct wl_signal output_leave; // struct wlr_scene_output
struct wl_signal output_sample; // struct wlr_scene_output_sample_event
- struct wl_signal frame_done; // struct timespec
+ struct wl_signal frame_done; // struct wlr_scene_frame_done_event
} events;
// May be NULL
@@ -207,17 +231,10 @@ struct wlr_scene_buffer {
/**
* The output that the largest area of this buffer is displayed on.
* This may be NULL if the buffer is not currently displayed on any
- * outputs. This is the output that should be used for frame callbacks,
- * presentation feedback, etc.
+ * outputs.
*/
struct wlr_scene_output *primary_output;
- int corner_radius;
- bool backdrop_blur;
- bool backdrop_blur_optimized;
- bool backdrop_blur_ignore_transparent;
- enum corner_location corners;
-
float opacity;
enum wlr_scale_filter_mode filter_mode;
struct wlr_fbox src_box;
@@ -225,6 +242,11 @@ struct wlr_scene_buffer {
enum wl_output_transform transform;
pixman_region32_t opaque_region;
+ enum wlr_color_transfer_function transfer_function;
+ enum wlr_color_named_primaries primaries;
+ enum wlr_color_encoding color_encoding;
+ enum wlr_color_range color_range;
+
struct {
uint64_t active_outputs;
struct wlr_texture *texture;
@@ -246,6 +268,9 @@ struct wlr_scene_buffer {
// as {R, G, B, A} where the max value of each component is UINT32_MAX
uint32_t single_pixel_buffer_color[4];
} WLR_PRIVATE;
+
+ struct fx_corner_radii corners;
+ struct linked_node blur;
};
/** A viewport for an output in the scene-graph */
@@ -279,6 +304,11 @@ struct wlr_scene_output {
bool gamma_lut_changed;
struct wlr_gamma_control_v1 *gamma_lut;
+ struct wlr_color_transform *gamma_lut_color_transform;
+
+ struct wlr_color_transform *prev_gamma_lut_color_transform;
+ struct wlr_color_transform *prev_supplied_color_transform;
+ struct wlr_color_transform *combined_color_transform;
struct wl_listener output_commit;
struct wl_listener output_damage;
@@ -290,6 +320,8 @@ struct wlr_scene_output {
struct wlr_drm_syncobj_timeline *in_timeline;
uint64_t in_point;
+ struct wlr_drm_syncobj_timeline *out_timeline;
+ uint64_t out_point;
} WLR_PRIVATE;
};
@@ -419,6 +451,12 @@ void wlr_scene_set_linux_dmabuf_v1(struct wlr_scene *scene,
void wlr_scene_set_gamma_control_manager_v1(struct wlr_scene *scene,
struct wlr_gamma_control_manager_v1 *gamma_control);
+/**
+ * Handles color_management_v1 feedback for all surfaces in the scene.
+ *
+ * Asserts that a struct wlr_color_manager_v1 hasn't already been set for the scene.
+ */
+void wlr_scene_set_color_manager_v1(struct wlr_scene *scene, struct wlr_color_manager_v1 *manager);
/**
* Add a node displaying nothing but its children.
@@ -479,6 +517,8 @@ struct wlr_scene_rect *wlr_scene_rect_from_node(struct wlr_scene_node *node);
*/
struct wlr_scene_shadow *wlr_scene_shadow_from_node(struct wlr_scene_node *node);
+struct wlr_scene_blur *wlr_scene_blur_from_node(struct wlr_scene_node *node);
+
/**
* If this buffer is backed by a surface, then the struct wlr_scene_surface is
* returned. If not, NULL will be returned.
@@ -486,6 +526,12 @@ struct wlr_scene_shadow *wlr_scene_shadow_from_node(struct wlr_scene_node *node)
struct wlr_scene_surface *wlr_scene_surface_try_from_buffer(
struct wlr_scene_buffer *scene_buffer);
+/**
+ * Call wlr_surface_send_frame_done() if the surface is visible.
+ */
+void wlr_scene_surface_send_frame_done(struct wlr_scene_surface *scene_surface,
+ const struct timespec *when);
+
/**
* Add a node displaying a solid-colored rectangle to the scene-graph.
*
@@ -500,10 +546,14 @@ struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent,
void wlr_scene_rect_set_size(struct wlr_scene_rect *rect, int width, int height);
/**
- * Change the corner radius of an existing rectangle node.
+ * Change the corner radius of all corners of an existing rectangle node.
*/
-void wlr_scene_rect_set_corner_radius(struct wlr_scene_rect *rect, int corner_radius,
- enum corner_location corners);
+void wlr_scene_rect_set_corner_radius(struct wlr_scene_rect *rect, int corner_radius);
+
+/**
+ * Change the rounded corners of an existing rectangle node.
+ */
+void wlr_scene_rect_set_corner_radii(struct wlr_scene_rect *rect, struct fx_corner_radii);
/**
* Sets the region where to clip the rect.
@@ -523,18 +573,6 @@ void wlr_scene_rect_set_clipped_region(struct wlr_scene_rect *rect,
*/
void wlr_scene_rect_set_color(struct wlr_scene_rect *rect, const float color[static 4]);
-/**
-* Sets whether or not the buffer should render backdrop blur
-*/
-void wlr_scene_rect_set_backdrop_blur(struct wlr_scene_rect *rect,
- bool enabled);
-
-/**
-* Sets whether the backdrop blur should use optimized blur or not
-*/
-void wlr_scene_rect_set_backdrop_blur_optimized(struct wlr_scene_rect *rect,
- bool enabled);
-
/**
* Sets the fade inset size for the rectangle's borders.
*/
@@ -578,6 +616,77 @@ void wlr_scene_shadow_set_color(struct wlr_scene_shadow *shadow, const float col
void wlr_scene_shadow_set_clipped_region(struct wlr_scene_shadow *shadow,
struct clipped_region clipped_region);
+/**
+ * Add a node displaying a blur to the scene-graph.
+ */
+struct wlr_scene_blur *wlr_scene_blur_create(struct wlr_scene_tree *parent,
+ int width, int height);
+
+/**
+ * Change the width and height of an existing blur node.
+ */
+void wlr_scene_blur_set_size(struct wlr_scene_blur *blur, int width, int height);
+
+/**
+ * Change the corner radius of all corners of an existing blur node.
+ */
+void wlr_scene_blur_set_corner_radius(struct wlr_scene_blur *blur, int corner_radius);
+
+/**
+ * Change the corners of an existing blur node.
+ */
+void wlr_scene_blur_set_corner_radii(struct wlr_scene_blur *blur, struct fx_corner_radii);
+
+/**
+ * Make the blur node only blur the bottom layer of the scene
+ */
+void wlr_scene_blur_set_should_only_blur_bottom_layer(struct wlr_scene_blur *blur,
+ bool should_only_blur_bottom_layer);
+
+/**
+ * Set the transparency mask source for the blur, only rendering blur where the
+ * Mask source is actually rendering (e.g. skip transparent spaces)
+ */
+void wlr_scene_blur_set_transparency_mask_source(struct wlr_scene_blur *blur,
+ struct wlr_scene_buffer *source);
+
+/**
+ * get the transparency mask source for the blur
+ */
+struct wlr_scene_buffer *wlr_scene_blur_get_transparency_mask_source(
+ struct wlr_scene_blur *blur);
+
+/**
+ * Sets the blur alpha from 1.0f -> 0.0f. This adjusts the actual alpha of the blur.
+ * Default is 1.0f.
+ *
+ * Lower values without also adjusting the strength will look off.
+ *
+ * Can be used combined with strength to create a good-looking
+ * fade-out effect.
+ */
+void wlr_scene_blur_set_alpha(struct wlr_scene_blur *blur, float alpha);
+
+/**
+ * Sets the blur strength from 1.0f -> 0.0f. This adjusts how strong the blur is
+ * relative to the base 1.0 value.
+ *
+ * Can be used combined with alpha to create a good-looking
+ * fade-out effect.
+ */
+void wlr_scene_blur_set_strength(struct wlr_scene_blur *blur, float strength);
+
+/**
+ * Sets the region where to clip the blur.
+ *
+ * For there to be corner rounding of the clipped region, the corner radius and
+ * corners must be non-zero.
+ *
+ * NOTE: The positioning is node-relative.
+ */
+void wlr_scene_blur_set_clipped_region(struct wlr_scene_blur *blur,
+ struct clipped_region clipped_region);
+
/**
* If this node represents a wlr_scene_optimized_blur node, that node will
* be returned. It is not legal to feed a node that does not represent
@@ -702,35 +811,34 @@ void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer,
enum wlr_scale_filter_mode filter_mode);
/**
-* Sets the corner radius and which corners to round of this buffer
+* Sets the corner radius of all corners to round of this buffer
*/
void wlr_scene_buffer_set_corner_radius(struct wlr_scene_buffer *scene_buffer,
- int radii, enum corner_location corners);
+ int radii);
/**
-* Sets whether or not the buffer should render backdrop blur
+* Sets the corner radii of this buffer
*/
-void wlr_scene_buffer_set_backdrop_blur(struct wlr_scene_buffer *scene_buffer,
- bool enabled);
+void wlr_scene_buffer_set_corner_radii(struct wlr_scene_buffer *scene_buffer,
+ struct fx_corner_radii corner_radii);
-/**
-* Sets whether the backdrop blur should use optimized blur or not
-*/
-void wlr_scene_buffer_set_backdrop_blur_optimized(struct wlr_scene_buffer *scene_buffer,
- bool enabled);
+void wlr_scene_buffer_set_transfer_function(struct wlr_scene_buffer *scene_buffer,
+ enum wlr_color_transfer_function transfer_function);
-/**
-* Sets whether the backdrop blur should not render in fully transparent
-* segments.
-*/
-void wlr_scene_buffer_set_backdrop_blur_ignore_transparent(
- struct wlr_scene_buffer *scene_buffer, bool enabled);
+void wlr_scene_buffer_set_primaries(struct wlr_scene_buffer *scene_buffer,
+ enum wlr_color_named_primaries primaries);
+
+void wlr_scene_buffer_set_color_encoding(struct wlr_scene_buffer *scene_buffer,
+ enum wlr_color_encoding encoding);
+
+void wlr_scene_buffer_set_color_range(struct wlr_scene_buffer *scene_buffer,
+ enum wlr_color_range range);
/**
* Calls the buffer's frame_done signal.
*/
void wlr_scene_buffer_send_frame_done(struct wlr_scene_buffer *scene_buffer,
- struct timespec *now);
+ struct wlr_scene_frame_done_event *event);
/**
* Add a viewport for the specified output to the scene-graph.
@@ -751,6 +859,11 @@ void wlr_scene_output_set_position(struct wlr_scene_output *scene_output,
struct wlr_scene_output_state_options {
struct wlr_scene_timer *timer;
+
+ /**
+ * Color transform to apply before the output's color transform. Cannot be
+ * used when the output has a non-NULL image description set.
+ */
struct wlr_color_transform *color_transform;
/**
diff --git a/scenefx/include/types/fx/clipped_region.h b/scenefx/include/types/fx/clipped_region.h
new file mode 100644
index 0000000..44e8414
--- /dev/null
+++ b/scenefx/include/types/fx/clipped_region.h
@@ -0,0 +1,27 @@
+#ifndef TYPES_FX_CLIPPED_REGION_H
+#define TYPES_FX_CLIPPED_REGION_H
+#include <wlr/util/box.h>
+#include <scenefx/types/fx/clipped_region.h>
+
+struct fx_corner_fradii {
+ float top_left;
+ float top_right;
+ float bottom_right;
+ float bottom_left;
+};
+
+
+bool fx_corner_fradii_is_empty(const struct fx_corner_fradii* corners);
+
+struct fx_corner_fradii fx_corner_radii_scale(struct fx_corner_radii, float scale);
+
+struct clipped_fregion {
+ struct wlr_box area;
+ struct fx_corner_fradii corners;
+};
+
+static __always_inline bool clipped_fregion_is_valid(const struct clipped_fregion *fregion) {
+ return !fx_corner_fradii_is_empty(&fregion->corners) && !wlr_box_empty(&fregion->area);
+};
+
+#endif
diff --git a/scenefx/include/types/wlr_output.h b/scenefx/include/types/wlr_output.h
index b239b6a..3981bd2 100644
--- a/scenefx/include/types/wlr_output.h
+++ b/scenefx/include/types/wlr_output.h
@@ -7,4 +7,7 @@
void output_pending_resolution(struct wlr_output *output,
const struct wlr_output_state *state, int *width, int *height);
+const struct wlr_output_image_description *output_pending_image_description(
+ struct wlr_output *output, const struct wlr_output_state *state);
+
#endif
diff --git a/scenefx/include/types/wlr_scene.h b/scenefx/include/types/wlr_scene.h
index 80dcfd1..c4b40cd 100644
--- a/scenefx/include/types/wlr_scene.h
+++ b/scenefx/include/types/wlr_scene.h
@@ -5,6 +5,8 @@
struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node);
+void scene_node_get_size(struct wlr_scene_node *node, int *width, int *height);
+
void scene_surface_set_clip(struct wlr_scene_surface *surface, struct wlr_box *clip);
#endif
diff --git a/scenefx/meson.build b/scenefx/meson.build
index 4198ce0..5422a96 100644
--- a/scenefx/meson.build
+++ b/scenefx/meson.build
@@ -1,7 +1,7 @@
project(
'scenefx',
'c',
- version: '0.4.1',
+ version: '0.5.0',
license: 'MIT',
meson_version: '>=1.3',
default_options: [
@@ -66,8 +66,17 @@ else
)
endif
+features = {
+ 'gles2-renderer': true,
+ 'color-management': false,
+}
+internal_features = {
+ 'egl': false,
+ 'tracy': false,
+}
+
wayland_kwargs = {
- 'version': '>=1.23.1',
+ 'version': '>=1.24.0',
'fallback': 'wayland',
'default_options': [
'tests=false',
@@ -79,8 +88,8 @@ wayland_server = dependency('wayland-server',
)
wlroots_options = [ 'examples=false' ]
-wlroots_version = ['>=0.19.0', '<0.20.0']
-wlroots = dependency('wlroots-0.19',
+wlroots_version = ['>=0.20.0', '<0.21.0']
+wlroots = dependency('wlroots-0.20',
version: wlroots_version,
default_options: wlroots_options,
required: false,
@@ -96,14 +105,23 @@ endif
drm = dependency('libdrm',
- version: '>=2.4.122',
+ version: '>=2.4.129',
fallback: 'libdrm',
default_options: [
'auto_features=disabled',
'tests=false',
],
)
-xkbcommon = dependency('xkbcommon')
+xkbcommon = dependency('xkbcommon',
+ version: '>=1.8.0',
+ fallback: 'libxkbcommon',
+ default_options: [
+ 'enable-tools=false',
+ 'enable-x11=false',
+ 'enable-docs=false',
+ 'enable-xkbregistry=false',
+ ],
+)
pixman = dependency('pixman-1',
version: '>=0.43.0',
fallback: 'pixman',
@@ -123,6 +141,34 @@ scenefx_deps = [
rt,
]
+# Tracy
+if get_option('tracy_enable')
+ internal_features += { 'tracy': true }
+
+ if get_option('buildtype') != 'debugoptimized'
+ warning('Profiling builds should set --buildtype=debugoptimized')
+ endif
+
+ tracy_options = [
+ 'default_library=static',
+ 'werror=false',
+ 'warning_level=0',
+ ]
+ tracy_proj = subproject(
+ 'tracy',
+ required: true,
+ default_options: tracy_options
+ )
+ scenefx_deps += tracy_proj.get_variable('tracy_dep')
+ add_project_arguments(
+ [
+ '-DTRACY_ENABLE',
+ '-DTRACY_ON_DEMAND',
+ ],
+ language: 'c',
+ )
+endif
+
subdir('protocol')
subdir('render')
@@ -149,6 +195,8 @@ scenefx = declare_dependency(
meson.override_dependency(versioned_name, scenefx)
+summary(features + internal_features, bool_yn: true)
+
if get_option('examples')
subdir('examples')
subdir('tinywl')
diff --git a/scenefx/meson_options.txt b/scenefx/meson_options.txt
index 8b527ea..810ec9f 100644
--- a/scenefx/meson_options.txt
+++ b/scenefx/meson_options.txt
@@ -1,2 +1,4 @@
option('examples', type: 'boolean', value: true, description: 'Build example applications')
option('renderers', type: 'array', choices: ['auto', 'gles2'], value: ['auto'], description: 'Select built-in renderers')
+option('color-management', type: 'feature', value: 'auto', description: 'Enable support for color management')
+option('tracy_enable', type: 'boolean', value: false, description: 'Enable profiling')
diff --git a/scenefx/render/color.c b/scenefx/render/color.c
new file mode 100644
index 0000000..e89fab2
--- /dev/null
+++ b/scenefx/render/color.c
@@ -0,0 +1,433 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wlr/render/color.h>
+#include "render/color.h"
+#include "util/matrix.h"
+
+// See H.273 ColourPrimaries
+
+static const struct wlr_color_primaries COLOR_PRIMARIES_SRGB = { // code point 1
+ .red = { 0.640, 0.330 },
+ .green = { 0.300, 0.600 },
+ .blue = { 0.150, 0.060 },
+ .white = { 0.3127, 0.3290 },
+};
+
+static const struct wlr_color_primaries COLOR_PRIMARIES_BT2020 = { // code point 9
+ .red = { 0.708, 0.292 },
+ .green = { 0.170, 0.797 },
+ .blue = { 0.131, 0.046 },
+ .white = { 0.3127, 0.3290 },
+};
+
+void wlr_color_transform_init(struct wlr_color_transform *tr, enum wlr_color_transform_type type) {
+ *tr = (struct wlr_color_transform){
+ .type = type,
+ .ref_count = 1,
+ };
+ wlr_addon_set_init(&tr->addons);
+}
+
+struct wlr_color_transform *wlr_color_transform_init_linear_to_inverse_eotf(
+ enum wlr_color_transfer_function tf) {
+ struct wlr_color_transform_inverse_eotf *tx = calloc(1, sizeof(*tx));
+ if (!tx) {
+ return NULL;
+ }
+ wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_INVERSE_EOTF);
+ tx->tf = tf;
+ return &tx->base;
+}
+
+struct wlr_color_transform *wlr_color_transform_init_lut_3x1d(size_t dim,
+ const uint16_t *r, const uint16_t *g, const uint16_t *b) {
+ uint16_t *lut_3x1d = malloc(3 * dim * sizeof(lut_3x1d[0]));
+ if (lut_3x1d == NULL) {
+ return NULL;
+ }
+
+ memcpy(&lut_3x1d[0 * dim], r, dim * sizeof(lut_3x1d[0]));
+ memcpy(&lut_3x1d[1 * dim], g, dim * sizeof(lut_3x1d[0]));
+ memcpy(&lut_3x1d[2 * dim], b, dim * sizeof(lut_3x1d[0]));
+
+ struct wlr_color_transform_lut_3x1d *tx = calloc(1, sizeof(*tx));
+ if (!tx) {
+ free(lut_3x1d);
+ return NULL;
+ }
+ wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_LUT_3X1D);
+ tx->lut_3x1d = lut_3x1d;
+ tx->dim = dim;
+ return &tx->base;
+}
+
+struct wlr_color_transform *wlr_color_transform_init_matrix(const float matrix[static 9]) {
+ struct wlr_color_transform_matrix *tx = calloc(1, sizeof(*tx));
+ if (!tx) {
+ return NULL;
+ }
+ wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_MATRIX);
+ memcpy(tx->matrix, matrix, sizeof(tx->matrix));
+ return &tx->base;
+}
+
+struct wlr_color_transform *wlr_color_transform_init_pipeline(
+ struct wlr_color_transform **transforms, size_t len) {
+ assert(len > 0);
+
+ struct wlr_color_transform **copy = calloc(len, sizeof(copy[0]));
+ if (copy == NULL) {
+ return NULL;
+ }
+
+ struct wlr_color_transform_pipeline *tx = calloc(1, sizeof(*tx));
+ if (!tx) {
+ free(copy);
+ return NULL;
+ }
+ wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_PIPELINE);
+
+ for (size_t i = 0; i < len; i++) {
+ assert(transforms[i] != NULL);
+ copy[i] = wlr_color_transform_ref(transforms[i]);
+ }
+
+ tx->transforms = copy;
+ tx->len = len;
+
+ return &tx->base;
+}
+
+static void color_transform_destroy(struct wlr_color_transform *tr) {
+ switch (tr->type) {
+ case COLOR_TRANSFORM_INVERSE_EOTF:
+ case COLOR_TRANSFORM_MATRIX:
+ break;
+ case COLOR_TRANSFORM_LCMS2:
+ color_transform_lcms2_finish(color_transform_lcms2_from_base(tr));
+ break;
+ case COLOR_TRANSFORM_LUT_3X1D:;
+ struct wlr_color_transform_lut_3x1d *lut_3x1d = color_transform_lut_3x1d_from_base(tr);
+ free(lut_3x1d->lut_3x1d);
+ break;
+ case COLOR_TRANSFORM_PIPELINE:;
+ struct wlr_color_transform_pipeline *pipeline =
+ wl_container_of(tr, pipeline, base);
+ for (size_t i = 0; i < pipeline->len; i++) {
+ wlr_color_transform_unref(pipeline->transforms[i]);
+ }
+ free(pipeline->transforms);
+ break;
+ }
+ wlr_addon_set_finish(&tr->addons);
+ free(tr);
+}
+
+struct wlr_color_transform *wlr_color_transform_ref(struct wlr_color_transform *tr) {
+ tr->ref_count += 1;
+ return tr;
+}
+
+void wlr_color_transform_unref(struct wlr_color_transform *tr) {
+ if (!tr) {
+ return;
+ }
+ assert(tr->ref_count > 0);
+ tr->ref_count -= 1;
+ if (tr->ref_count == 0) {
+ color_transform_destroy(tr);
+ }
+}
+
+struct wlr_color_transform_inverse_eotf *wlr_color_transform_inverse_eotf_from_base(
+ struct wlr_color_transform *tr) {
+ assert(tr->type == COLOR_TRANSFORM_INVERSE_EOTF);
+ struct wlr_color_transform_inverse_eotf *inverse_eotf = wl_container_of(tr, inverse_eotf, base);
+ return inverse_eotf;
+}
+
+struct wlr_color_transform_lut_3x1d *color_transform_lut_3x1d_from_base(
+ struct wlr_color_transform *tr) {
+ assert(tr->type == COLOR_TRANSFORM_LUT_3X1D);
+ struct wlr_color_transform_lut_3x1d *lut_3x1d = wl_container_of(tr, lut_3x1d, base);
+ return lut_3x1d;
+}
+
+static float srgb_eval_inverse_eotf(float x) {
+ // See https://www.w3.org/Graphics/Color/srgb
+ if (x <= 0.0031308) {
+ return 12.92 * x;
+ } else {
+ return 1.055 * powf(x, 1.0 / 2.4) - 0.055;
+ }
+}
+
+static float st2084_pq_eval_inverse_eotf(float x) {
+ // H.273 TransferCharacteristics code point 16
+ float c1 = 0.8359375;
+ float c2 = 18.8515625;
+ float c3 = 18.6875;
+ float m = 78.84375;
+ float n = 0.1593017578125;
+ if (x < 0) {
+ x = 0;
+ }
+ if (x > 1) {
+ x = 1;
+ }
+ float pow_n = powf(x, n);
+ return powf((c1 + c2 * pow_n) / (1 + c3 * pow_n), m);
+}
+
+static float bt1886_eval_inverse_eotf(float x) {
+ float lb = powf(0.0001, 1.0 / 2.4);
+ float lw = powf(1.0, 1.0 / 2.4);
+ float a = powf(lw - lb, 2.4);
+ float b = lb / (lw - lb);
+ return powf(x / a, 1.0 / 2.4) - b;
+}
+
+static float transfer_function_eval_inverse_eotf(
+ enum wlr_color_transfer_function tf, float x) {
+ switch (tf) {
+ case WLR_COLOR_TRANSFER_FUNCTION_SRGB:
+ return srgb_eval_inverse_eotf(x);
+ case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ:
+ return st2084_pq_eval_inverse_eotf(x);
+ case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR:
+ return x;
+ case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22:
+ return powf(x, 1.0 / 2.2);
+ case WLR_COLOR_TRANSFER_FUNCTION_BT1886:
+ return bt1886_eval_inverse_eotf(x);
+ }
+ abort(); // unreachable
+}
+
+static void color_transform_inverse_eotf_eval(
+ struct wlr_color_transform_inverse_eotf *tr,
+ float out[static 3], const float in[static 3]) {
+ for (size_t i = 0; i < 3; i++) {
+ out[i] = transfer_function_eval_inverse_eotf(tr->tf, in[i]);
+ }
+}
+
+static float lut_1d_get(const uint16_t *lut, size_t len, size_t i) {
+ if (i >= len) {
+ i = len - 1;
+ }
+ return (float) lut[i] / UINT16_MAX;
+}
+
+static float lut_1d_eval(const uint16_t *lut, size_t len, float x) {
+ double pos = x * (len - 1);
+ double int_part;
+ double frac_part = modf(pos, &int_part);
+ size_t i = (size_t) int_part;
+ double a = lut_1d_get(lut, len, i);
+ double b = lut_1d_get(lut, len, i + 1);
+ return a * (1 - frac_part) + b * frac_part;
+}
+
+static void color_transform_lut_3x1d_eval(struct wlr_color_transform_lut_3x1d *tr,
+ float out[static 3], const float in[static 3]) {
+ for (size_t i = 0; i < 3; i++) {
+ out[i] = lut_1d_eval(&tr->lut_3x1d[tr->dim * i], tr->dim, in[i]);
+ }
+}
+
+static void multiply_matrix_vector(float out[static 3], float m[static 9], const float v[static 3]);
+
+void wlr_color_transform_eval(struct wlr_color_transform *tr,
+ float out[static 3], const float in[static 3]) {
+ switch (tr->type) {
+ case COLOR_TRANSFORM_INVERSE_EOTF:
+ color_transform_inverse_eotf_eval(wlr_color_transform_inverse_eotf_from_base(tr), out, in);
+ break;
+ case COLOR_TRANSFORM_LCMS2:
+ color_transform_lcms2_eval(color_transform_lcms2_from_base(tr), out, in);
+ break;
+ case COLOR_TRANSFORM_LUT_3X1D:
+ color_transform_lut_3x1d_eval(color_transform_lut_3x1d_from_base(tr), out, in);
+ break;
+ case COLOR_TRANSFORM_MATRIX:;
+ struct wlr_color_transform_matrix *matrix = wl_container_of(tr, matrix, base);
+ multiply_matrix_vector(out, matrix->matrix, in);
+ break;
+ case COLOR_TRANSFORM_PIPELINE:;
+ struct wlr_color_transform_pipeline *pipeline =
+ wl_container_of(tr, pipeline, base);
+ float color[3];
+ memcpy(color, in, sizeof(color));
+ for (size_t i = 0; i < pipeline->len; i++) {
+ wlr_color_transform_eval(pipeline->transforms[i], color, color);
+ }
+ memcpy(out, color, sizeof(color));
+ break;
+ }
+}
+
+static size_t color_transform_compose_collect(struct wlr_color_transform **out,
+ size_t out_capacity, struct wlr_color_transform **transforms, size_t len) {
+ size_t count = 0;
+ for (size_t i = 0; i < len; i++) {
+ struct wlr_color_transform *transform = transforms[i];
+ if (transform == NULL) {
+ continue;
+ }
+
+ if (transform->type == COLOR_TRANSFORM_PIPELINE) {
+ struct wlr_color_transform_pipeline *pipeline = wl_container_of(transform,
+ pipeline, base);
+ count += color_transform_compose_collect(out, out_capacity,
+ pipeline->transforms, pipeline->len);
+ } else {
+ if (out_capacity > 0) {
+ *out = wlr_color_transform_ref(transform);
+ out++;
+ out_capacity--;
+ }
+ count++;
+ }
+ }
+ return count;
+}
+
+bool color_transform_compose(struct wlr_color_transform **result,
+ struct wlr_color_transform **transforms, size_t len) {
+ // The normalized form has the following properties :
+ // - No NULL transform in a pipeline
+ // - No pipeline of length 1
+ // - No nested pipelines
+ bool status = false;
+
+ size_t result_len = color_transform_compose_collect(NULL, 0, transforms, len);
+ if (result_len == 0) {
+ *result = NULL;
+ return true;
+ }
+
+ struct wlr_color_transform **result_transforms = calloc(result_len,
+ sizeof(result_transforms[0]));
+ if (result_transforms == NULL) {
+ return false;
+ }
+ color_transform_compose_collect(result_transforms, result_len, transforms, len);
+
+ if (result_len == 1) {
+ *result = wlr_color_transform_ref(result_transforms[0]);
+ } else {
+ *result = wlr_color_transform_init_pipeline(result_transforms, result_len);
+ if (*result == NULL) {
+ goto cleanup_transforms;
+ }
+ }
+ status = true;
+
+cleanup_transforms:
+ for (size_t i = 0; i < result_len; i++) {
+ wlr_color_transform_unref(result_transforms[i]);
+ }
+ free(result_transforms);
+ return status;
+}
+
+void wlr_color_primaries_from_named(struct wlr_color_primaries *out,
+ enum wlr_color_named_primaries named) {
+ switch (named) {
+ case WLR_COLOR_NAMED_PRIMARIES_SRGB:
+ *out = COLOR_PRIMARIES_SRGB;
+ return;
+ case WLR_COLOR_NAMED_PRIMARIES_BT2020:
+ *out = COLOR_PRIMARIES_BT2020;
+ return;
+ }
+ abort();
+}
+
+static void multiply_matrix_vector(float out[static 3], float m[static 9], const float v[static 3]) {
+ float result[3] = {
+ m[0] * v[0] + m[1] * v[1] + m[2] * v[2],
+ m[3] * v[0] + m[4] * v[1] + m[5] * v[2],
+ m[6] * v[0] + m[7] * v[1] + m[8] * v[2],
+ };
+ memcpy(out, result, sizeof(result));
+}
+
+static void xy_to_xyz(float out[static 3], struct wlr_color_cie1931_xy src) {
+ if (src.y == 0) {
+ out[0] = out[1] = out[2] = 0;
+ return;
+ }
+
+ out[0] = src.x / src.y;
+ out[1] = 1;
+ out[2] = (1 - src.x - src.y) / src.y;
+}
+
+void wlr_color_primaries_to_xyz(const struct wlr_color_primaries *primaries, float matrix[static 9]) {
+ // See: http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
+
+ float r[3], g[3], b[3], w[3];
+ xy_to_xyz(r, primaries->red);
+ xy_to_xyz(g, primaries->green);
+ xy_to_xyz(b, primaries->blue);
+ xy_to_xyz(w, primaries->white);
+
+ float xyz_matrix[9] = {
+ r[0], g[0], b[0],
+ r[1], g[1], b[1],
+ r[2], g[2], b[2],
+ };
+ matrix_invert(xyz_matrix, xyz_matrix);
+
+ float S[3];
+ multiply_matrix_vector(S, xyz_matrix, w);
+
+ float result[] = {
+ S[0] * r[0], S[1] * g[0], S[2] * b[0],
+ S[0] * r[1], S[1] * g[1], S[2] * b[1],
+ S[0] * r[2], S[1] * g[2], S[2] * b[2],
+ };
+ memcpy(matrix, result, sizeof(result));
+}
+
+void wlr_color_primaries_transform_absolute_colorimetric(
+ const struct wlr_color_primaries *source,
+ const struct wlr_color_primaries *destination, float matrix[static 9]) {
+ float source_to_xyz[9];
+ wlr_color_primaries_to_xyz(source, source_to_xyz);
+ float destination_to_xyz[9];
+ wlr_color_primaries_to_xyz(destination, destination_to_xyz);
+ float xyz_to_destination[9];
+ matrix_invert(xyz_to_destination, destination_to_xyz);
+ wlr_matrix_multiply(matrix, xyz_to_destination, source_to_xyz);
+}
+
+void wlr_color_transfer_function_get_default_luminance(enum wlr_color_transfer_function tf,
+ struct wlr_color_luminances *lum) {
+ switch (tf) {
+ case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ:
+ *lum = (struct wlr_color_luminances){
+ .min = 0.005,
+ .max = 10000,
+ .reference = 203,
+ };
+ break;
+ case WLR_COLOR_TRANSFER_FUNCTION_BT1886:
+ *lum = (struct wlr_color_luminances){
+ .min = 0.01,
+ .max = 100,
+ .reference = 100,
+ };
+ break;
+ default:
+ *lum = (struct wlr_color_luminances){
+ .min = 0.2,
+ .max = 80,
+ .reference = 80,
+ };
+ break;
+ }
+}
diff --git a/scenefx/render/color_fallback.c b/scenefx/render/color_fallback.c
new file mode 100644
index 0000000..1b25881
--- /dev/null
+++ b/scenefx/render/color_fallback.c
@@ -0,0 +1,24 @@
+#include <stdlib.h>
+#include <wlr/util/log.h>
+#include "render/color.h"
+
+struct wlr_color_transform *wlr_color_transform_init_linear_to_icc(
+ const void *data, size_t size) {
+ wlr_log(WLR_ERROR, "Cannot create color transform from ICC profile: "
+ "LCMS2 is compile-time disabled");
+ return NULL;
+}
+
+struct wlr_color_transform_lcms2 *color_transform_lcms2_from_base(
+ struct wlr_color_transform *tr) {
+ abort(); // unreachable
+}
+
+void color_transform_lcms2_finish(struct wlr_color_transform_lcms2 *tr) {
+ abort(); // unreachable
+}
+
+void color_transform_lcms2_eval(struct wlr_color_transform_lcms2 *tr,
+ float out[static 3], const float in[static 3]) {
+ abort(); // unreachable
+}
diff --git a/scenefx/render/color_lcms2.c b/scenefx/render/color_lcms2.c
new file mode 100644
index 0000000..39e16cd
--- /dev/null
+++ b/scenefx/render/color_lcms2.c
@@ -0,0 +1,113 @@
+#include <assert.h>
+#include <lcms2.h>
+#include <stdlib.h>
+#include <wlr/util/log.h>
+#include <wlr/render/color.h>
+#include "render/color.h"
+
+struct wlr_color_transform_lcms2 {
+ struct wlr_color_transform base;
+
+ cmsContext ctx;
+ cmsHTRANSFORM lcms;
+};
+
+static const cmsCIExyY srgb_whitepoint = { 0.3127, 0.3291, 1 };
+
+static const cmsCIExyYTRIPLE srgb_primaries = {
+ .Red = { 0.64, 0.33, 1 },
+ .Green = { 0.3, 0.6, 1 },
+ .Blue = { 0.15, 0.06, 1},
+};
+
+static void handle_lcms_error(cmsContext ctx, cmsUInt32Number code, const char *text) {
+ wlr_log(WLR_ERROR, "[lcms] %s", text);
+}
+
+struct wlr_color_transform *wlr_color_transform_init_linear_to_icc(
+ const void *data, size_t size) {
+ struct wlr_color_transform_lcms2 *tx = NULL;
+
+ cmsContext ctx = cmsCreateContext(NULL, NULL);
+ if (ctx == NULL) {
+ wlr_log(WLR_ERROR, "cmsCreateContext failed");
+ return NULL;
+ }
+
+ cmsSetLogErrorHandlerTHR(ctx, handle_lcms_error);
+
+ cmsHPROFILE icc_profile = cmsOpenProfileFromMemTHR(ctx, data, size);
+ if (icc_profile == NULL) {
+ wlr_log(WLR_ERROR, "cmsOpenProfileFromMemTHR failed");
+ goto error_ctx;
+ }
+
+ if (cmsGetDeviceClass(icc_profile) != cmsSigDisplayClass) {
+ wlr_log(WLR_ERROR, "ICC profile must have the Display device class");
+ goto error_icc_profile;
+ }
+
+ cmsToneCurve *linear_tone_curve = cmsBuildGamma(ctx, 1);
+ if (linear_tone_curve == NULL) {
+ wlr_log(WLR_ERROR, "cmsBuildGamma failed");
+ goto error_icc_profile;
+ }
+
+ cmsToneCurve *linear_tf[] = {
+ linear_tone_curve,
+ linear_tone_curve,
+ linear_tone_curve,
+ };
+ cmsHPROFILE srgb_profile = cmsCreateRGBProfileTHR(ctx, &srgb_whitepoint,
+ &srgb_primaries, linear_tf);
+ cmsFreeToneCurve(linear_tone_curve);
+ if (srgb_profile == NULL) {
+ wlr_log(WLR_ERROR, "cmsCreateRGBProfileTHR failed");
+ goto error_icc_profile;
+ }
+
+ cmsHTRANSFORM lcms_tr = cmsCreateTransformTHR(ctx,
+ srgb_profile, TYPE_RGB_FLT, icc_profile, TYPE_RGB_FLT,
+ INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsCloseProfile(srgb_profile);
+ cmsCloseProfile(icc_profile);
+ if (lcms_tr == NULL) {
+ wlr_log(WLR_ERROR, "cmsCreateTransformTHR failed");
+ goto error_ctx;
+ }
+
+ tx = calloc(1, sizeof(*tx));
+ if (!tx) {
+ cmsDeleteTransform(lcms_tr);
+ goto error_ctx;
+ }
+ wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_LCMS2);
+
+ tx->ctx = ctx;
+ tx->lcms = lcms_tr;
+
+ return &tx->base;
+
+error_icc_profile:
+ cmsCloseProfile(icc_profile);
+error_ctx:
+ cmsDeleteContext(ctx);
+ return NULL;
+}
+
+void color_transform_lcms2_finish(struct wlr_color_transform_lcms2 *tr) {
+ cmsDeleteTransform(tr->lcms);
+ cmsDeleteContext(tr->ctx);
+}
+
+struct wlr_color_transform_lcms2 *color_transform_lcms2_from_base(
+ struct wlr_color_transform *tr) {
+ assert(tr->type == COLOR_TRANSFORM_LCMS2);
+ struct wlr_color_transform_lcms2 *lcms2 = wl_container_of(tr, lcms2, base);
+ return lcms2;
+}
+
+void color_transform_lcms2_eval(struct wlr_color_transform_lcms2 *tr,
+ float out[static 3], const float in[static 3]) {
+ cmsDoTransform(tr->lcms, in, out, 1);
+}
diff --git a/scenefx/render/egl.c b/scenefx/render/egl.c
index 75cef15..85d5f6f 100644
--- a/scenefx/render/egl.c
+++ b/scenefx/render/egl.c
@@ -156,7 +156,7 @@ static void init_dmabuf_formats(struct wlr_egl *egl) {
}
if (modifiers_len == 0) {
- // Asume the linear layout is supported if the driver doesn't
+ // Assume the linear layout is supported if the driver doesn't
// explicitly say otherwise
wlr_drm_format_set_add(&egl->dmabuf_texture_formats, fmt,
DRM_FORMAT_MOD_LINEAR);
@@ -260,7 +260,8 @@ static struct wlr_egl *egl_create(void) {
return egl;
}
-static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) {
+static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display,
+ bool allow_software) {
egl->display = display;
EGLint major, minor;
@@ -326,9 +327,8 @@ static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) {
// The only way a non-DRM device is selected is when the user
// explicitly picks software rendering
- if (check_egl_ext(device_exts_str, "EGL_MESA_device_software") &&
- egl->exts.EXT_device_drm) {
- if (env_parse_bool("WLR_RENDERER_ALLOW_SOFTWARE")) {
+ if (check_egl_ext(device_exts_str, "EGL_MESA_device_software")) {
+ if (allow_software || env_parse_bool("WLR_RENDERER_ALLOW_SOFTWARE")) {
wlr_log(WLR_INFO, "Using software rendering");
} else {
wlr_log(WLR_ERROR, "Software rendering detected, please use "
@@ -382,7 +382,7 @@ static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) {
}
static bool egl_init(struct wlr_egl *egl, EGLenum platform,
- void *remote_display) {
+ void *remote_display, bool allow_software) {
EGLint display_attribs[3] = {0};
size_t display_attribs_len = 0;
@@ -401,7 +401,7 @@ static bool egl_init(struct wlr_egl *egl, EGLenum platform,
return false;
}
- if (!egl_init_display(egl, display)) {
+ if (!egl_init_display(egl, display, allow_software)) {
if (egl->exts.KHR_display_reference) {
eglTerminate(display);
}
@@ -411,10 +411,8 @@ static bool egl_init(struct wlr_egl *egl, EGLenum platform,
size_t atti = 0;
EGLint attribs[7];
- // Try with gles3 first to check if the graphics drivers support it.
- // If it fails, it will fallback to gles2
attribs[atti++] = EGL_CONTEXT_CLIENT_VERSION;
- attribs[atti++] = 3;
+ attribs[atti++] = 2;
// Request a high priority context if possible
// TODO: only do this if we're running as the DRM master
@@ -435,24 +433,13 @@ static bool egl_init(struct wlr_egl *egl, EGLenum platform,
attribs[atti++] = EGL_NONE;
assert(atti <= sizeof(attribs)/sizeof(attribs[0]));
- // Attempt to create a context with gles3
egl->context = eglCreateContext(egl->display, EGL_NO_CONFIG_KHR,
EGL_NO_CONTEXT, attribs);
if (egl->context != EGL_NO_CONTEXT) {
- wlr_log(WLR_DEBUG, "Created EGL context using OpenGL ES 3.0");
+ wlr_log(WLR_DEBUG, "Created EGL context using OpenGL ES 2.0");
} else {
- wlr_log(WLR_INFO, "Failed to create EGL context using OpenGL ES 3.0");
-
- // Retry with gles2
- attribs[1] = 2;
- egl->context = eglCreateContext(egl->display, EGL_NO_CONFIG_KHR,
- EGL_NO_CONTEXT, attribs);
- if (egl->context != EGL_NO_CONTEXT) {
- wlr_log(WLR_DEBUG, "Created EGL context using OpenGL ES 2.0");
- } else {
- wlr_log(WLR_ERROR, "Failed to create EGL context using OpenGL ES 2.0");
- return false;
- }
+ wlr_log(WLR_ERROR, "Failed to create EGL context using OpenGL ES 2.0");
+ return false;
}
if (request_high_priority) {
@@ -572,6 +559,8 @@ static int open_render_node(int drm_fd) {
}
struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) {
+ bool allow_software = drm_fd < 0;
+
struct wlr_egl *egl = egl_create();
if (egl == NULL) {
wlr_log(WLR_ERROR, "Failed to create EGL context");
@@ -585,7 +574,7 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) {
*/
EGLDeviceEXT egl_device = get_egl_device_from_drm_fd(egl, drm_fd);
if (egl_device != EGL_NO_DEVICE_EXT) {
- if (egl_init(egl, EGL_PLATFORM_DEVICE_EXT, egl_device)) {
+ if (egl_init(egl, EGL_PLATFORM_DEVICE_EXT, egl_device, allow_software)) {
wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_DEVICE_EXT");
return egl;
}
@@ -610,7 +599,7 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) {
goto error;
}
- if (egl_init(egl, EGL_PLATFORM_GBM_KHR, egl->gbm_device)) {
+ if (egl_init(egl, EGL_PLATFORM_GBM_KHR, egl->gbm_device, allow_software)) {
wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_GBM_KHR");
return egl;
}
@@ -649,7 +638,7 @@ struct wlr_egl *wlr_egl_create_with_context(EGLDisplay display,
return NULL;
}
- if (!egl_init_display(egl, display)) {
+ if (!egl_init_display(egl, display, true)) {
free(egl);
return NULL;
}
diff --git a/scenefx/render/fx_renderer/fx_effect_framebuffers.c b/scenefx/render/fx_renderer/fx_effect_framebuffers.c
deleted file mode 100644
index b0caa7c..0000000
--- a/scenefx/render/fx_renderer/fx_effect_framebuffers.c
+++ /dev/null
@@ -1,57 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <wlr/types/wlr_output.h>
-#include <wlr/util/addon.h>
-#include <wlr/util/log.h>
-
-#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h"
-
-static void addon_handle_destroy(struct wlr_addon *addon) {
- struct fx_effect_framebuffers *fbos = wl_container_of(addon, fbos, addon);
- wlr_addon_finish(&fbos->addon);
- free(fbos);
-}
-
-static const struct wlr_addon_interface fbos_addon_impl = {
- .name = "fx_effect_framebuffers",
- .destroy = addon_handle_destroy,
-};
-
-static bool fx_effect_framebuffers_assign(struct wlr_output *output,
- struct fx_effect_framebuffers *fbos) {
- wlr_addon_init(&fbos->addon, &output->addons, output, &fbos_addon_impl);
- return true;
-}
-
-struct fx_effect_framebuffers *fx_effect_framebuffers_try_get(struct wlr_output *output) {
- struct fx_effect_framebuffers *fbos = NULL;
- if (!output) {
- return NULL;
- }
-
- struct wlr_addon *addon = wlr_addon_find(&output->addons, output,
- &fbos_addon_impl);
- if (!addon) {
- goto create_new;
- }
-
- if (!(fbos = wl_container_of(addon, fbos, addon))) {
- goto create_new;
- }
- return fbos;
-
-create_new:;
- fbos = calloc(1, sizeof(*fbos));
- if (!fbos) {
- wlr_log(WLR_ERROR, "Could not allocate a fx_effect_framebuffers");
- return NULL;
- }
-
- if (!fx_effect_framebuffers_assign(output, fbos)) {
- wlr_log(WLR_ERROR, "Could not assign fx_effect_framebuffers to output: '%s'",
- output->name);
- free(fbos);
- return NULL;
- }
- return fbos;
-}
diff --git a/scenefx/render/fx_renderer/fx_framebuffer.c b/scenefx/render/fx_renderer/fx_framebuffer.c
index c8835f4..d514b34 100644
--- a/scenefx/render/fx_renderer/fx_framebuffer.c
+++ b/scenefx/render/fx_renderer/fx_framebuffer.c
@@ -1,9 +1,7 @@
-#include <stdio.h>
#include <stdlib.h>
#include <wlr/interfaces/wlr_buffer.h>
#include <wlr/render/allocator.h>
#include <wlr/render/interface.h>
-#include <wlr/render/swapchain.h>
#include <wlr/util/log.h>
#include "render/egl.h"
@@ -69,47 +67,53 @@ GLuint fx_framebuffer_get_fbo(struct fx_framebuffer *buffer) {
}
void fx_framebuffer_get_or_create_custom(struct fx_renderer *renderer,
- struct wlr_output *output, struct wlr_swapchain *swapchain,
+ struct wlr_allocator *allocator, int width, int height, bool has_alpha,
struct fx_framebuffer **fx_framebuffer, bool *failed) {
if (*failed) {
return;
}
- struct wlr_allocator *allocator = output->allocator;
- if (!swapchain) {
- if (!output->swapchain) {
- wlr_log(WLR_ERROR, "Failed to allocate buffer, no swapchain");
- *failed = true;
- return;
+ if (*fx_framebuffer != NULL) {
+ struct wlr_buffer *wlr_buffer = (*fx_framebuffer)->buffer;
+ if (wlr_buffer != NULL) {
+ if (wlr_buffer->width == width && wlr_buffer->height == height) {
+ return;
+ }
+ // Create a new wlr_buffer if it's null or if the output size has
+ // changed
+ wlr_buffer_drop(wlr_buffer);
+ } else {
+ fx_framebuffer_destroy(*fx_framebuffer);
}
- swapchain = output->swapchain;
+ *fx_framebuffer = NULL;
}
- int width = output->width;
- int height = output->height;
- struct wlr_buffer *wlr_buffer = NULL;
+ // Get the best supported DRM format (DMABUF if supported)
+ const struct wlr_drm_format_set *texture_formats = wlr_renderer_get_texture_formats(
+ &renderer->wlr_renderer, renderer->wlr_renderer.render_buffer_caps);
+ const struct fx_pixel_format *pix_format =
+ get_fx_format_from_gl(GL_RGBA, GL_UNSIGNED_BYTE, has_alpha);
+ const struct wlr_drm_format *format = wlr_drm_format_set_get(texture_formats, pix_format->drm_format);
+ if (format == NULL) {
+ wlr_log(WLR_ERROR, "Failed to get a supported texture format while allocating buffer");
+ *failed = true;
+ return;
+ }
+
+ struct wlr_buffer *wlr_buffer = wlr_allocator_create_buffer(allocator, width, height, format);
+ if (wlr_buffer == NULL) {
+ wlr_log(WLR_ERROR, "Failed to allocate wlr_buffer");
+ *failed = true;
+ return;
+ }
+
+ *fx_framebuffer = fx_framebuffer_get_or_create(renderer, wlr_buffer);
if (*fx_framebuffer == NULL) {
- wlr_buffer = wlr_allocator_create_buffer(allocator, width, height,
- &swapchain->format);
- if (wlr_buffer == NULL) {
- wlr_log(WLR_ERROR, "Failed to allocate buffer");
- *failed = true;
- return;
- }
- } else {
- if ((wlr_buffer = (*fx_framebuffer)->buffer) &&
- wlr_buffer->width == width &&
- wlr_buffer->height == height) {
- return;
- }
- // Create a new wlr_buffer if it's null or if the output size has
- // changed
- fx_framebuffer_destroy(*fx_framebuffer);
+ wlr_log(WLR_ERROR, "Failed to allocate fx_buffer");
wlr_buffer_drop(wlr_buffer);
- wlr_buffer = wlr_allocator_create_buffer(allocator,
- width, height, &swapchain->format);
+ *failed = true;
+ return;
}
- *fx_framebuffer = fx_framebuffer_get_or_create(renderer, wlr_buffer);
fx_framebuffer_get_fbo(*fx_framebuffer);
}
@@ -157,10 +161,15 @@ error_buffer:
}
void fx_framebuffer_bind(struct fx_framebuffer *fx_buffer) {
- glBindFramebuffer(GL_FRAMEBUFFER, fx_buffer->fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, fx_framebuffer_get_fbo(fx_buffer));
}
void fx_framebuffer_destroy(struct fx_framebuffer *fx_buffer) {
+ if (!fx_buffer) {
+ wlr_log(WLR_ERROR, "Trying to destroy an already destroyed fx_framebuffer");
+ return;
+ }
+
// Release the framebuffer
wl_list_remove(&fx_buffer->link);
wlr_addon_finish(&fx_buffer->addon);
diff --git a/scenefx/render/fx_renderer/fx_offscreen_buffers.c b/scenefx/render/fx_renderer/fx_offscreen_buffers.c
new file mode 100644
index 0000000..8574a05
--- /dev/null
+++ b/scenefx/render/fx_renderer/fx_offscreen_buffers.c
@@ -0,0 +1,92 @@
+#include <stdlib.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/util/addon.h>
+#include <wlr/util/log.h>
+
+#include "render/fx_renderer/fx_renderer.h"
+#include "scenefx/render/fx_renderer/fx_renderer.h"
+#include "scenefx/render/fx_renderer/fx_offscreen_buffers.h"
+
+static void addon_handle_destroy(struct wlr_addon *addon) {
+ struct fx_offscreen_buffers *fbos = wl_container_of(addon, fbos, addon);
+
+ // Make sure to free the buffers
+ if (fbos->optimized_blur_buffer != NULL) {
+ wlr_buffer_drop(fbos->optimized_blur_buffer->buffer);
+ fbos->optimized_blur_buffer = NULL;
+ }
+ if (fbos->optimized_no_blur_buffer != NULL) {
+ wlr_buffer_drop(fbos->optimized_no_blur_buffer->buffer);
+ fbos->optimized_no_blur_buffer = NULL;
+ }
+ if (fbos->blur_saved_pixels_buffer != NULL) {
+ wlr_buffer_drop(fbos->blur_saved_pixels_buffer->buffer);
+ fbos->blur_saved_pixels_buffer = NULL;
+ }
+ if (fbos->effects_buffer != NULL) {
+ wlr_buffer_drop(fbos->effects_buffer->buffer);
+ fbos->effects_buffer = NULL;
+ }
+ if (fbos->effects_buffer_swapped != NULL) {
+ wlr_buffer_drop(fbos->effects_buffer_swapped->buffer);
+ fbos->effects_buffer_swapped = NULL;
+ }
+
+ wl_list_remove(&fbos->link);
+ wlr_addon_finish(&fbos->addon);
+ free(fbos);
+}
+
+static const struct wlr_addon_interface fbos_addon_impl = {
+ .name = "fx_offscreen_buffers",
+ .destroy = addon_handle_destroy,
+};
+
+static bool fx_offscreen_buffers_assign(struct wlr_output *output,
+ struct fx_offscreen_buffers *fbos) {
+ wlr_addon_init(&fbos->addon, &output->addons, output, &fbos_addon_impl);
+ return true;
+}
+
+void fx_offscreen_buffers_destroy(struct fx_offscreen_buffers *fbos) {
+ addon_handle_destroy(&fbos->addon);
+}
+
+struct fx_offscreen_buffers *fx_offscreen_buffers_try_get(struct wlr_output *output) {
+ struct fx_offscreen_buffers *fbos = NULL;
+ if (!output) {
+ return NULL;
+ }
+
+ struct wlr_addon *addon = wlr_addon_find(&output->addons, output,
+ &fbos_addon_impl);
+ if (!addon) {
+ goto create_new;
+ }
+
+ if (!(fbos = wl_container_of(addon, fbos, addon))) {
+ goto create_new;
+ }
+ return fbos;
+
+create_new:;
+ struct fx_renderer *renderer = fx_get_renderer(output->renderer);
+ if (!renderer) {
+ return NULL;
+ }
+
+ fbos = calloc(1, sizeof(*fbos));
+ if (!fbos) {
+ wlr_log(WLR_ERROR, "Could not allocate a fx_offscreen_buffers");
+ return NULL;
+ }
+
+ if (!fx_offscreen_buffers_assign(output, fbos)) {
+ wlr_log(WLR_ERROR, "Could not assign fx_offscreen_buffers to output: '%s'",
+ output->name);
+ free(fbos);
+ return NULL;
+ }
+ wl_list_insert(&renderer->offscreen_buffers, &fbos->link);
+ return fbos;
+}
diff --git a/scenefx/render/fx_renderer/fx_pass.c b/scenefx/render/fx_renderer/fx_pass.c
index 92df07d..e0a7774 100644
--- a/scenefx/render/fx_renderer/fx_pass.c
+++ b/scenefx/render/fx_renderer/fx_pass.c
@@ -1,3 +1,4 @@
+#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
@@ -14,9 +15,9 @@
#include "render/fx_renderer/fx_renderer.h"
#include "render/fx_renderer/shaders.h"
#include "render/pass.h"
+#include "render/tracy.h"
+#include "scenefx/render/fx_renderer/fx_offscreen_buffers.h"
#include "scenefx/render/fx_renderer/fx_renderer.h"
-#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h"
-#include "scenefx/types/fx/corner_location.h"
#include "scenefx/types/fx/blur_data.h"
#include "util/matrix.h"
@@ -25,10 +26,10 @@
struct fx_render_texture_options fx_render_texture_options_default(
const struct wlr_render_texture_options *base) {
struct fx_render_texture_options options = {
- .corner_radius = 0,
- .corners = CORNER_LOCATION_NONE,
+ .corners = {0},
.discard_transparent = false,
.clip_box = NULL,
+ .clipped_region = {0},
};
memcpy(&options.base, base, sizeof(*base));
return options;
@@ -40,16 +41,58 @@ struct fx_render_rect_options fx_render_rect_options_default(
.base = *base,
.clipped_region = {
.area = { .0, .0, .0, .0 },
- .corner_radius = 0,
- .corners = CORNER_LOCATION_NONE,
+ .corners = {0},
},
};
return options;
}
-// Gets a non-transformed wlr_box
-static struct wlr_box get_monitor_box(struct wlr_output *output) {
- return (struct wlr_box) { 0, 0, output->width, output->height };
+bool fx_render_pass_init_offscreen_buffers(struct wlr_render_pass *render_pass,
+ struct wlr_output *output) {
+ struct fx_gles_render_pass *pass = fx_get_render_pass(render_pass);
+ if (output == NULL) {
+ pass->fx_offscreen_buffers = NULL;
+ return false;
+ }
+ if (pass->fx_offscreen_buffers != NULL) {
+ wlr_log(WLR_ERROR, "Extra buffers called twice. Ignoring...");
+ return true;
+ }
+
+ // For per output framebuffers
+ pass->fx_offscreen_buffers = fx_offscreen_buffers_try_get(output);
+ if (pass->fx_offscreen_buffers == NULL) {
+ wlr_log(WLR_ERROR, "Failed to get/create effect framebuffers for output: %s",
+ output->name);
+ return false;
+ }
+
+ // Update the buffers if needed
+ struct fx_renderer *renderer = pass->buffer->renderer;
+ const int width = pass->buffer->buffer->width;
+ const int height = pass->buffer->buffer->height;
+ bool failed = false;
+ fx_framebuffer_get_or_create_custom(renderer, output->allocator, width, height, false,
+ &pass->fx_offscreen_buffers->blur_saved_pixels_buffer, &failed);
+ fx_framebuffer_get_or_create_custom(renderer, output->allocator, width, height, true,
+ &pass->fx_offscreen_buffers->effects_buffer, &failed);
+ fx_framebuffer_get_or_create_custom(renderer, output->allocator, width, height, true,
+ &pass->fx_offscreen_buffers->effects_buffer_swapped, &failed);
+ fx_framebuffer_get_or_create_custom(renderer, output->allocator, width, height, false,
+ &pass->fx_offscreen_buffers->optimized_blur_buffer, &failed);
+ fx_framebuffer_get_or_create_custom(renderer, output->allocator, width, height, false,
+ &pass->fx_offscreen_buffers->optimized_no_blur_buffer, &failed);
+
+ // Bind back to the default buffer
+ fx_framebuffer_bind(pass->buffer);
+
+ if (failed) {
+ fx_offscreen_buffers_destroy(pass->fx_offscreen_buffers);
+ pass->fx_offscreen_buffers = NULL;
+ wlr_log(WLR_ERROR, "Failed to create effect framebuffers");
+ return false;
+ }
+ return true;
}
///
@@ -58,18 +101,19 @@ static struct wlr_box get_monitor_box(struct wlr_output *output) {
static const struct wlr_render_pass_impl render_pass_impl;
-static struct fx_gles_render_pass *get_render_pass(struct wlr_render_pass *wlr_pass) {
- assert(wlr_pass->impl == &render_pass_impl);
- struct fx_gles_render_pass *pass = wl_container_of(wlr_pass, pass, base);
+struct fx_gles_render_pass *fx_get_render_pass(struct wlr_render_pass *render_pass) {
+ assert(render_pass->impl == &render_pass_impl);
+ struct fx_gles_render_pass *pass = wl_container_of(render_pass, pass, base);
return pass;
}
static bool render_pass_submit(struct wlr_render_pass *wlr_pass) {
- struct fx_gles_render_pass *pass = get_render_pass(wlr_pass);
+ struct fx_gles_render_pass *pass = fx_get_render_pass(wlr_pass);
struct fx_renderer *renderer = pass->buffer->renderer;
struct fx_render_timer *timer = pass->timer;
bool ok = false;
+ TRACY_BOTH_ZONES_START(pass->buffer->renderer);
push_fx_debug(renderer);
if (timer) {
@@ -111,14 +155,17 @@ out:
glBindFramebuffer(GL_FRAMEBUFFER, 0);
pop_fx_debug(renderer);
+ TRACY_BOTH_ZONES_END;
+ TRACY_GPU_ZONE_COLLECT(renderer);
+
wlr_egl_restore_context(&pass->prev_ctx);
wlr_drm_syncobj_timeline_unref(pass->signal_timeline);
wlr_buffer_unlock(pass->buffer->buffer);
- struct fx_effect_framebuffers *fbos = fx_effect_framebuffers_try_get(pass->output);
- if (fbos) {
- pixman_region32_fini(&fbos->blur_padding_region);
- }
+
+ pass->fx_offscreen_buffers = NULL;
+ pixman_region32_fini(&pass->blur_padding_region);
+
free(pass);
return ok;
@@ -126,19 +173,21 @@ out:
static void render_pass_add_texture(struct wlr_render_pass *wlr_pass,
const struct wlr_render_texture_options *options) {
- struct fx_gles_render_pass *pass = get_render_pass(wlr_pass);
+ struct fx_gles_render_pass *pass = fx_get_render_pass(wlr_pass);
const struct fx_render_texture_options fx_options =
fx_render_texture_options_default(options);
// Re-use fx function but with default options
+ // TODO: Simplified version?
fx_render_pass_add_texture(pass, &fx_options);
}
static void render_pass_add_rect(struct wlr_render_pass *wlr_pass,
const struct wlr_render_rect_options *options) {
- struct fx_gles_render_pass *pass = get_render_pass(wlr_pass);
+ struct fx_gles_render_pass *pass = fx_get_render_pass(wlr_pass);
const struct fx_render_rect_options fx_options =
fx_render_rect_options_default(options);
// Re-use fx function but with default options
+ // TODO: Simplified version?
fx_render_pass_add_rect(pass, &fx_options);
}
@@ -275,6 +324,30 @@ static void setup_blending(enum wlr_render_blend_mode mode) {
}
}
+static bool apply_clip_region(pixman_region32_t *clip_region,
+ const struct wlr_box *clipped_region_box, const struct fx_corner_fradii *corners) {
+ if (!wlr_box_empty(clipped_region_box)) {
+ float top = fmax(corners->top_left, corners->top_right);
+ float bottom = fmax(corners->bottom_left, corners->bottom_right);
+ float left = fmax(corners->top_left, corners->bottom_left);
+ float right = fmax(corners->top_right, corners->bottom_right);
+
+ pixman_region32_t user_clip_region;
+ pixman_region32_init_rect(
+ &user_clip_region,
+ clipped_region_box->x + (left * 0.3),
+ clipped_region_box->y + (top * 0.3),
+ fmax(clipped_region_box->width - (left + right) * 0.3, 0),
+ fmax(clipped_region_box->height - (top + bottom) * 0.3, 0)
+ );
+ pixman_region32_subtract(clip_region, clip_region, &user_clip_region);
+ pixman_region32_fini(&user_clip_region);
+ return true;
+ }
+
+ return false;
+}
+
void fx_render_pass_add_texture(struct fx_gles_render_pass *pass,
const struct fx_render_texture_options *fx_options) {
const struct wlr_render_texture_options *options = &fx_options->base;
@@ -283,19 +356,27 @@ void fx_render_pass_add_texture(struct fx_gles_render_pass *pass,
struct tex_shader *shader = NULL;
+ bool use_effects = !fx_corner_fradii_is_empty(&fx_options->corners)
+ || clipped_fregion_is_valid(&fx_options->clipped_region);
switch (texture->target) {
case GL_TEXTURE_2D:
if (texture->has_alpha) {
- shader = &renderer->shaders.tex_rgba;
+ shader = use_effects
+ ? &renderer->shaders.tex_effects_rgba
+ : &renderer->shaders.tex_rgba;
} else {
- shader = &renderer->shaders.tex_rgbx;
+ shader = use_effects
+ ? &renderer->shaders.tex_effects_rgbx
+ : &renderer->shaders.tex_rgbx;
}
break;
case GL_TEXTURE_EXTERNAL_OES:
// EGL_EXT_image_dma_buf_import_modifiers requires
// GL_OES_EGL_image_external
assert(renderer->exts.OES_egl_image_external);
- shader = &renderer->shaders.tex_ext;
+ shader = use_effects
+ ? &renderer->shaders.tex_effects_ext
+ : &renderer->shaders.tex_ext;
break;
default:
abort();
@@ -317,34 +398,64 @@ void fx_render_pass_add_texture(struct fx_gles_render_pass *pass,
src_fbox.width /= options->texture->width;
src_fbox.height /= options->texture->height;
+ TRACY_BOTH_ZONES_START(renderer);
+ TRACY_ZONE_TEXT_f("dst_box (WxH, X, Y): %dx%d, %d, %d",
+ dst_box.width, dst_box.height, dst_box.x, dst_box.y);
+ TRACY_ZONE_TEXT_f("clip_box (WxH, X, Y): %dx%d, %d, %d",
+ clip_box->width, clip_box->height, clip_box->x, clip_box->y);
+ TRACY_ZONE_TEXT_f("src_box (WxH, X, Y): %lfx%lf, %lf, %lf",
+ src_fbox.width, src_fbox.height, src_fbox.x, src_fbox.y);
+ TRACY_ZONE_TEXT_f("Shader Type: %s",
+ use_effects ? (
+ shader == &renderer->shaders.tex_effects_rgba ? "Effects RGBA"
+ : shader == &renderer->shaders.tex_effects_rgbx ? "Effects RGBX"
+ : "Effects EXT"
+ ) : (
+ shader == &renderer->shaders.tex_rgba ? "RGBA"
+ : shader == &renderer->shaders.tex_rgbx ? "RGBX"
+ : "EXT"
+ )
+ );
push_fx_debug(renderer);
if (options->wait_timeline != NULL) {
int sync_file_fd =
wlr_drm_syncobj_timeline_export_sync_file(options->wait_timeline, options->wait_point);
if (sync_file_fd < 0) {
+ TRACY_BOTH_ZONES_END_FAIL;
return;
}
EGLSyncKHR sync = wlr_egl_create_sync(renderer->egl, sync_file_fd);
close(sync_file_fd);
if (sync == EGL_NO_SYNC_KHR) {
+ TRACY_BOTH_ZONES_END_FAIL;
return;
}
bool ok = wlr_egl_wait_sync(renderer->egl, sync);
wlr_egl_destroy_sync(renderer->egl, sync);
if (!ok) {
+ TRACY_BOTH_ZONES_END_FAIL;
return;
}
}
- bool has_alpha = texture->has_alpha
- || alpha < 1.0
- || fx_options->corner_radius > 0
- || fx_options->discard_transparent;
+ bool has_alpha = texture->has_alpha || alpha < 1.0 || use_effects;
+ TRACY_ZONE_TEXT_f("Has Alpha: %d", has_alpha);
setup_blending(!has_alpha ? WLR_RENDER_BLEND_MODE_NONE : options->blend_mode);
+ pixman_region32_t clip_region;
+ if (options->clip) {
+ pixman_region32_init(&clip_region);
+ pixman_region32_copy(&clip_region, options->clip);
+ } else {
+ pixman_region32_init_rect(&clip_region, dst_box.x, dst_box.y, dst_box.width, dst_box.height);
+ }
+ const struct wlr_box clipped_region_box = fx_options->clipped_region.area;
+ struct fx_corner_fradii clipped_region_corners = fx_options->clipped_region.corners;
+ apply_clip_region(&clip_region, &clipped_region_box, &clipped_region_corners);
+
glUseProgram(shader->program);
glActiveTexture(GL_TEXTURE0);
@@ -361,29 +472,33 @@ void fx_render_pass_add_texture(struct fx_gles_render_pass *pass,
break;
}
- enum corner_location corners = fx_options->corners;
-
glUniform1i(shader->tex, 0);
glUniform1f(shader->alpha, alpha);
- glUniform2f(shader->size, clip_box->width, clip_box->height);
- glUniform2f(shader->position, clip_box->x, clip_box->y);
+
glUniform1f(shader->discard_transparent, fx_options->discard_transparent);
- glUniform1f(shader->radius_top_left, (CORNER_LOCATION_TOP_LEFT & corners) == CORNER_LOCATION_TOP_LEFT ?
- fx_options->corner_radius : 0);
- glUniform1f(shader->radius_top_right, (CORNER_LOCATION_TOP_RIGHT & corners) == CORNER_LOCATION_TOP_RIGHT ?
- fx_options->corner_radius : 0);
- glUniform1f(shader->radius_bottom_left, (CORNER_LOCATION_BOTTOM_LEFT & corners) == CORNER_LOCATION_BOTTOM_LEFT ?
- fx_options->corner_radius : 0);
- glUniform1f(shader->radius_bottom_right, (CORNER_LOCATION_BOTTOM_RIGHT & corners) == CORNER_LOCATION_BOTTOM_RIGHT ?
- fx_options->corner_radius : 0);
+
+ if (use_effects) {
+ struct fx_corner_fradii corners = fx_options->corners;
+
+ glUniform2f(shader->effects.size, clip_box->width, clip_box->height);
+ glUniform2f(shader->effects.position, clip_box->x, clip_box->y);
+ uniform_corner_radii_set(&shader->effects.radius, &corners);
+
+ glUniform2f(shader->effects.clip_size, clipped_region_box.width, clipped_region_box.height);
+ glUniform2f(shader->effects.clip_position, clipped_region_box.x, clipped_region_box.y);
+ uniform_corner_radii_set(&shader->effects.clip_radius, &clipped_region_corners);
+ }
set_proj_matrix(shader->proj, pass->projection_matrix, &dst_box);
set_tex_matrix(shader->tex_proj, options->transform, &src_fbox);
- render(&dst_box, options->clip, shader->pos_attrib);
+ render(&dst_box, &clip_region, shader->pos_attrib);
+ pixman_region32_fini(&clip_region);
glBindTexture(texture->target, 0);
+
pop_fx_debug(renderer);
+ TRACY_BOTH_ZONES_END;
}
void fx_render_pass_add_rect(struct fx_gles_render_pass *pass,
@@ -394,57 +509,71 @@ void fx_render_pass_add_rect(struct fx_gles_render_pass *pass,
const struct wlr_render_color *color = &options->color;
struct wlr_box box;
- wlr_render_rect_options_get_box(options, pass->buffer->buffer, &box);
+ struct wlr_buffer *wlr_buffer = pass->buffer->buffer;
+ wlr_render_rect_options_get_box(options, wlr_buffer, &box);
- pixman_region32_t clip_region;
- if (options->clip) {
- pixman_region32_init(&clip_region);
- pixman_region32_copy(&clip_region, options->clip);
- } else {
- pixman_region32_init_rect(&clip_region, box.x, box.y, box.width, box.height);
- }
- const struct wlr_box clipped_region_box = fx_options->clipped_region.area;
- enum corner_location clipped_region_corners = fx_options->clipped_region.corners;
- int clipped_region_corner_radius = clipped_region_corners != CORNER_LOCATION_NONE ?
- fx_options->clipped_region.corner_radius : 0;
- if (!wlr_box_empty(&clipped_region_box)) {
- pixman_region32_t user_clip_region;
- pixman_region32_init_rect(
- &user_clip_region,
- clipped_region_box.x + clipped_region_corner_radius * 0.3,
- clipped_region_box.y + clipped_region_corner_radius * 0.3,
- fmax(clipped_region_box.width - clipped_region_corner_radius * 0.6, 0),
- fmax(clipped_region_box.height - clipped_region_corner_radius * 0.6, 0)
- );
- pixman_region32_subtract(&clip_region, &clip_region, &user_clip_region);
- pixman_region32_fini(&user_clip_region);
+ const bool should_clip = clipped_fregion_is_valid(&fx_options->clipped_region);
+
+ enum wlr_render_blend_mode blend_mode =
+ (color->a == 1.0 && !should_clip) ? WLR_RENDER_BLEND_MODE_NONE : options->blend_mode;
+ const bool use_fast_clear =
+ blend_mode == WLR_RENDER_BLEND_MODE_NONE && // includes check for `should_clip`
+ options->clip == NULL &&
+ box.x == 0 && box.y == 0 &&
+ box.width == wlr_buffer->width &&
+ box.height == wlr_buffer->height;
- push_fx_debug(renderer);
- setup_blending(options->blend_mode);
+ TRACY_BOTH_ZONES_START(renderer);
+ TRACY_ZONE_TEXT_f("Box (WxH, X, Y): %dx%d, %d, %d", box.width, box.height, box.x, box.y);
+ TRACY_ZONE_TEXT_f("Color RGBA: %f, %f, %f, %f", color->r, color->g, color->b, color->a);
+ TRACY_ZONE_TEXT_f("Use fast clear optimization: %d", use_fast_clear);
+
+ push_fx_debug(renderer);
+ if (use_fast_clear) {
+ glClearColor(color->r, color->g, color->b, color->a);
+ glClear(GL_COLOR_BUFFER_BIT);
} else {
- push_fx_debug(renderer);
- setup_blending(color->a == 1.0 ? WLR_RENDER_BLEND_MODE_NONE : options->blend_mode);
- }
+ const struct wlr_box *clipped_region_box = &fx_options->clipped_region.area;
+ const struct fx_corner_fradii *clipped_region_corners = &fx_options->clipped_region.corners;
+
+ TRACY_ZONE_TEXT_f("Clip Box (WxH, X, Y): %dx%d, %d, %d",
+ clipped_region_box->width, clipped_region_box->height,
+ clipped_region_box->x, clipped_region_box->y);
+ TRACY_ZONE_TEXT_f("Clip Box Corners (TL, TR, BL, BR): %f, %f, %f, %f",
+ clipped_region_corners->top_left,
+ clipped_region_corners->top_right,
+ clipped_region_corners->bottom_left,
+ clipped_region_corners->bottom_right);
+
+ pixman_region32_t clip_region;
+ if (options->clip) {
+ pixman_region32_init(&clip_region);
+ pixman_region32_copy(&clip_region, options->clip);
+ } else {
+ pixman_region32_init_rect(&clip_region, box.x, box.y, box.width, box.height);
+ }
- struct quad_shader shader = renderer->shaders.quad;
- glUseProgram(shader.program);
- set_proj_matrix(shader.proj, pass->projection_matrix, &box);
- glUniform4f(shader.color, color->r, color->g, color->b, color->a);
- glUniform2f(shader.clip_size, clipped_region_box.width, clipped_region_box.height);
- glUniform2f(shader.clip_position, clipped_region_box.x, clipped_region_box.y);
- glUniform1f(shader.clip_radius_top_left, (CORNER_LOCATION_TOP_LEFT & clipped_region_corners) == CORNER_LOCATION_TOP_LEFT ?
- clipped_region_corner_radius : 0);
- glUniform1f(shader.clip_radius_top_right, (CORNER_LOCATION_TOP_RIGHT & clipped_region_corners) == CORNER_LOCATION_TOP_RIGHT ?
- clipped_region_corner_radius : 0);
- glUniform1f(shader.clip_radius_bottom_left, (CORNER_LOCATION_BOTTOM_LEFT & clipped_region_corners) == CORNER_LOCATION_BOTTOM_LEFT ?
- clipped_region_corner_radius : 0);
- glUniform1f(shader.clip_radius_bottom_right, (CORNER_LOCATION_BOTTOM_RIGHT & clipped_region_corners) == CORNER_LOCATION_BOTTOM_RIGHT ?
- clipped_region_corner_radius : 0);
-
- render(&box, &clip_region, renderer->shaders.quad.pos_attrib);
- pixman_region32_fini(&clip_region);
+ apply_clip_region(&clip_region, clipped_region_box, clipped_region_corners);
+
+ setup_blending(blend_mode);
+ struct quad_shader *shader = should_clip
+ ? &renderer->shaders.quad_clip
+ : &renderer->shaders.quad;
+ glUseProgram(shader->program);
+ set_proj_matrix(shader->proj, pass->projection_matrix, &box);
+ glUniform4f(shader->color, color->r, color->g, color->b, color->a);
+ if (should_clip) {
+ glUniform2f(shader->effects.clip_size, clipped_region_box->width, clipped_region_box->height);
+ glUniform2f(shader->effects.clip_position, clipped_region_box->x, clipped_region_box->y);
+ uniform_corner_radii_set(&shader->effects.clip_radius, clipped_region_corners);
+ }
+ render(&box, &clip_region, shader->pos_attrib);
+
+ pixman_region32_fini(&clip_region);
+ }
pop_fx_debug(renderer);
+ TRACY_BOTH_ZONES_END;
}
void fx_render_pass_add_rect_grad(struct fx_gles_render_pass *pass,
@@ -454,51 +583,66 @@ void fx_render_pass_add_rect_grad(struct fx_gles_render_pass *pass,
struct fx_renderer *renderer = pass->buffer->renderer;
if (renderer->shaders.quad_grad.max_len <= fx_options->gradient.count) {
- EGLint client_version;
- eglQueryContext(renderer->egl->display, renderer->egl->context,
- EGL_CONTEXT_CLIENT_VERSION, &client_version);
-
glDeleteProgram(renderer->shaders.quad_grad.program);
- if (!link_quad_grad_program(&renderer->shaders.quad_grad, (GLint) client_version, fx_options->gradient.count + 1)) {
+ if (!link_quad_grad_program(&renderer->shaders.quad_grad, fx_options->gradient.count + 1)) {
wlr_log(WLR_ERROR, "Could not link quad shader after updating max_len to %d. Aborting renderer", fx_options->gradient.count + 1);
abort();
}
}
struct wlr_box box;
- wlr_render_rect_options_get_box(options, pass->buffer->buffer, &box);
-
+ struct wlr_buffer *wlr_buffer = pass->buffer->buffer;
+ wlr_render_rect_options_get_box(options, wlr_buffer, &box);
+
+ TRACY_BOTH_ZONES_START(renderer);
+ TRACY_ZONE_TEXT_f("Box (WxH, X, Y): %dx%d, %d, %d", box.width, box.height, box.x, box.y);
+ TRACY_ZONE_TEXT_f("Gradient:");
+ TRACY_ZONE_TEXT_f("\tNum Colors: %d", fx_options->gradient.count);
+ TRACY_ZONE_TEXT_f("\tBlend: %d", fx_options->gradient.blend);
+ TRACY_ZONE_TEXT_f("\tDegree: %f", fx_options->gradient.degree);
+ TRACY_ZONE_TEXT_f("\tType: %s",
+ fx_options->gradient.linear == 1 ? "Linear"
+ : fx_options->gradient.linear == 2 ? "Conic"
+ : "Unknown");
+ TRACY_ZONE_TEXT_f("\tOrigin: %fx%f",
+ fx_options->gradient.origin[0], fx_options->gradient.origin[1]);
+ TRACY_ZONE_TEXT_f("\tRange (WxH, X, Y): %dx%d, %d, %d",
+ fx_options->gradient.range.width, fx_options->gradient.range.height,
+ fx_options->gradient.range.x, fx_options->gradient.range.y);
+ // TODO: Display Colors (not really sure how it works without a scene example...)
push_fx_debug(renderer);
+
setup_blending(options->blend_mode);
- glUseProgram(renderer->shaders.quad_grad.program);
+ struct quad_grad_shader shader = renderer->shaders.quad_grad;
+ glUseProgram(shader.program);
- set_proj_matrix(renderer->shaders.quad_grad.proj, pass->projection_matrix, &box);
- glUniform4fv(renderer->shaders.quad_grad.colors, fx_options->gradient.count, (GLfloat*)fx_options->gradient.colors);
- glUniform1i(renderer->shaders.quad_grad.count, fx_options->gradient.count);
- glUniform2f(renderer->shaders.quad_grad.size, fx_options->gradient.range.width, fx_options->gradient.range.height);
- glUniform1f(renderer->shaders.quad_grad.degree, fx_options->gradient.degree);
- glUniform1f(renderer->shaders.quad_grad.linear, fx_options->gradient.linear);
- glUniform1f(renderer->shaders.quad_grad.blend, fx_options->gradient.blend);
- glUniform2f(renderer->shaders.quad_grad.grad_box, fx_options->gradient.range.x, fx_options->gradient.range.y);
- glUniform2f(renderer->shaders.quad_grad.origin, fx_options->gradient.origin[0], fx_options->gradient.origin[1]);
+ set_proj_matrix(shader.proj, pass->projection_matrix, &box);
+ glUniform4fv(shader.colors, fx_options->gradient.count, (GLfloat*)fx_options->gradient.colors);
+ glUniform1i(shader.count, fx_options->gradient.count);
+ glUniform2f(shader.size, fx_options->gradient.range.width, fx_options->gradient.range.height);
+ glUniform1f(shader.degree, fx_options->gradient.degree);
+ glUniform1f(shader.linear, fx_options->gradient.linear);
+ glUniform1f(shader.blend, fx_options->gradient.blend);
+ glUniform2f(shader.grad_box, fx_options->gradient.range.x, fx_options->gradient.range.y);
+ glUniform2f(shader.origin, fx_options->gradient.origin[0], fx_options->gradient.origin[1]);
- render(&box, options->clip, renderer->shaders.quad_grad.pos_attrib);
+ render(&box, options->clip, shader.pos_attrib);
pop_fx_debug(renderer);
+ TRACY_BOTH_ZONES_END;
}
void fx_render_pass_add_rounded_rect(struct fx_gles_render_pass *pass,
const struct fx_render_rounded_rect_options *fx_options) {
const struct wlr_render_rect_options *options = &fx_options->base;
- struct wlr_box box;
- wlr_render_rect_options_get_box(options, pass->buffer->buffer, &box);
- assert(box.width > 0 && box.height > 0);
-
struct fx_renderer *renderer = pass->buffer->renderer;
const struct wlr_render_color *color = &options->color;
+ struct wlr_box box;
+ struct wlr_buffer *wlr_buffer = pass->buffer->buffer;
+ wlr_render_rect_options_get_box(options, wlr_buffer, &box);
pixman_region32_t clip_region;
if (options->clip) {
@@ -507,24 +651,28 @@ void fx_render_pass_add_rounded_rect(struct fx_gles_render_pass *pass,
} else {
pixman_region32_init_rect(&clip_region, box.x, box.y, box.width, box.height);
}
- const struct wlr_box clipped_region_box = fx_options->clipped_region.area;
- enum corner_location clipped_region_corners = fx_options->clipped_region.corners;
- int clipped_region_corner_radius = clipped_region_corners != CORNER_LOCATION_NONE ?
- fx_options->clipped_region.corner_radius : 0;
- if (!wlr_box_empty(&clipped_region_box)) {
- pixman_region32_t user_clip_region;
- pixman_region32_init_rect(
- &user_clip_region,
- clipped_region_box.x + clipped_region_corner_radius * 0.3,
- clipped_region_box.y + clipped_region_corner_radius * 0.3,
- fmax(clipped_region_box.width - clipped_region_corner_radius * 0.6, 0),
- fmax(clipped_region_box.height - clipped_region_corner_radius * 0.6, 0)
- );
- pixman_region32_subtract(&clip_region, &clip_region, &user_clip_region);
- pixman_region32_fini(&user_clip_region);
- }
-
+ const struct wlr_box *clipped_region_box = &fx_options->clipped_region.area;
+ const struct fx_corner_fradii *clipped_region_corners = &fx_options->clipped_region.corners;
+ apply_clip_region(&clip_region, clipped_region_box, clipped_region_corners);
+
+ TRACY_BOTH_ZONES_START(renderer);
+ TRACY_ZONE_TEXT_f("Box (WxH, X, Y): %dx%d, %d, %d", box.width, box.height, box.x, box.y);
+ TRACY_ZONE_TEXT_f("Clip Box (WxH, X, Y): %dx%d, %d, %d",
+ clipped_region_box->width, clipped_region_box->height,
+ clipped_region_box->x, clipped_region_box->y);
+ TRACY_ZONE_TEXT_f("Clip Box Corners (TL, TR, BL, BR): %f, %f, %f, %f",
+ clipped_region_corners->top_left,
+ clipped_region_corners->top_right,
+ clipped_region_corners->bottom_left,
+ clipped_region_corners->bottom_right);
+ TRACY_ZONE_TEXT_f("Color RGBA: %f, %f, %f, %f", color->r, color->g, color->b, color->a);
+ TRACY_ZONE_TEXT_f("Corners (TL, TR, BL, BR): %f, %f, %f, %f",
+ clipped_region_corners->top_left,
+ clipped_region_corners->top_right,
+ clipped_region_corners->bottom_left,
+ clipped_region_corners->bottom_right);
push_fx_debug(renderer);
+
setup_blending(WLR_RENDER_BLEND_MODE_PREMULTIPLIED);
struct quad_round_shader shader = renderer->shaders.quad_round;
@@ -536,32 +684,19 @@ void fx_render_pass_add_rounded_rect(struct fx_gles_render_pass *pass,
glUniform2f(shader.size, box.width, box.height);
glUniform2f(shader.position, box.x, box.y);
- glUniform2f(shader.clip_size, clipped_region_box.width, clipped_region_box.height);
- glUniform2f(shader.clip_position, clipped_region_box.x, clipped_region_box.y);
- glUniform1f(shader.clip_radius_top_left, (CORNER_LOCATION_TOP_LEFT & clipped_region_corners) == CORNER_LOCATION_TOP_LEFT ?
- clipped_region_corner_radius : 0);
- glUniform1f(shader.clip_radius_top_right, (CORNER_LOCATION_TOP_RIGHT & clipped_region_corners) == CORNER_LOCATION_TOP_RIGHT ?
- clipped_region_corner_radius : 0);
- glUniform1f(shader.clip_radius_bottom_left, (CORNER_LOCATION_BOTTOM_LEFT & clipped_region_corners) == CORNER_LOCATION_BOTTOM_LEFT ?
- clipped_region_corner_radius : 0);
- glUniform1f(shader.clip_radius_bottom_right, (CORNER_LOCATION_BOTTOM_RIGHT & clipped_region_corners) == CORNER_LOCATION_BOTTOM_RIGHT ?
- clipped_region_corner_radius : 0);
-
- enum corner_location corners = fx_options->corners;
- glUniform1f(shader.radius_top_left, (CORNER_LOCATION_TOP_LEFT & corners) == CORNER_LOCATION_TOP_LEFT ?
- fx_options->corner_radius : 0);
- glUniform1f(shader.radius_top_right, (CORNER_LOCATION_TOP_RIGHT & corners) == CORNER_LOCATION_TOP_RIGHT ?
- fx_options->corner_radius : 0);
- glUniform1f(shader.radius_bottom_left, (CORNER_LOCATION_BOTTOM_LEFT & corners) == CORNER_LOCATION_BOTTOM_LEFT ?
- fx_options->corner_radius : 0);
- glUniform1f(shader.radius_bottom_right, (CORNER_LOCATION_BOTTOM_RIGHT & corners) == CORNER_LOCATION_BOTTOM_RIGHT ?
- fx_options->corner_radius : 0);
+ glUniform2f(shader.clip_size, clipped_region_box->width, clipped_region_box->height);
+ glUniform2f(shader.clip_position, clipped_region_box->x, clipped_region_box->y);
+ uniform_corner_radii_set(&shader.clip_radius, clipped_region_corners);
+
+ struct fx_corner_fradii corners = fx_options->corners;
+ uniform_corner_radii_set(&shader.radius, &corners);
glUniform1f(shader.fade_inset, fx_options->fade_inset);
render(&box, &clip_region, renderer->shaders.quad_round.pos_attrib);
pixman_region32_fini(&clip_region);
pop_fx_debug(renderer);
+ TRACY_BOTH_ZONES_END;
}
void fx_render_pass_add_rounded_rect_grad(struct fx_gles_render_pass *pass,
@@ -571,21 +706,40 @@ void fx_render_pass_add_rounded_rect_grad(struct fx_gles_render_pass *pass,
struct fx_renderer *renderer = pass->buffer->renderer;
if (renderer->shaders.quad_grad_round.max_len <= fx_options->gradient.count) {
- EGLint client_version;
- eglQueryContext(renderer->egl->display, renderer->egl->context,
- EGL_CONTEXT_CLIENT_VERSION, &client_version);
-
glDeleteProgram(renderer->shaders.quad_grad_round.program);
- if (!link_quad_grad_round_program(&renderer->shaders.quad_grad_round, (GLint) client_version, fx_options->gradient.count + 1)) {
+ if (!link_quad_grad_round_program(&renderer->shaders.quad_grad_round, fx_options->gradient.count + 1)) {
wlr_log(WLR_ERROR, "Could not link quad shader after updating max_len to %d. Aborting renderer", fx_options->gradient.count + 1);
abort();
}
}
struct wlr_box box;
- wlr_render_rect_options_get_box(options, pass->buffer->buffer, &box);
-
+ struct wlr_buffer *wlr_buffer = pass->buffer->buffer;
+ wlr_render_rect_options_get_box(options, wlr_buffer, &box);
+
+ TRACY_BOTH_ZONES_START(renderer);
+ TRACY_ZONE_TEXT_f("Box (WxH, X, Y): %dx%d, %d, %d", box.width, box.height, box.x, box.y);
+ TRACY_ZONE_TEXT_f("Corners (TL, TR, BL, BR): %f, %f, %f, %f",
+ fx_options->corners.top_left,
+ fx_options->corners.top_right,
+ fx_options->corners.bottom_left,
+ fx_options->corners.bottom_right);
+ TRACY_ZONE_TEXT_f("Gradient:");
+ TRACY_ZONE_TEXT_f("\tNum Colors: %d", fx_options->gradient.count);
+ TRACY_ZONE_TEXT_f("\tBlend: %d", fx_options->gradient.blend);
+ TRACY_ZONE_TEXT_f("\tDegree: %f", fx_options->gradient.degree);
+ TRACY_ZONE_TEXT_f("\tType: %s",
+ fx_options->gradient.linear == 1 ? "Linear"
+ : fx_options->gradient.linear == 2 ? "Conic"
+ : "Unknown");
+ TRACY_ZONE_TEXT_f("\tOrigin: %fx%f",
+ fx_options->gradient.origin[0], fx_options->gradient.origin[1]);
+ TRACY_ZONE_TEXT_f("\tRange (WxH, X, Y): %dx%d, %d, %d",
+ fx_options->gradient.range.width, fx_options->gradient.range.height,
+ fx_options->gradient.range.x, fx_options->gradient.range.y);
+ // TODO: Display Colors (not really sure how it works without a scene example...)
push_fx_debug(renderer);
+
setup_blending(WLR_RENDER_BLEND_MODE_PREMULTIPLIED);
struct quad_grad_round_shader shader = renderer->shaders.quad_grad_round;
@@ -605,19 +759,13 @@ void fx_render_pass_add_rounded_rect_grad(struct fx_gles_render_pass *pass,
glUniform2f(shader.grad_box, fx_options->gradient.range.x, fx_options->gradient.range.y);
glUniform2f(shader.origin, fx_options->gradient.origin[0], fx_options->gradient.origin[1]);
- enum corner_location corners = fx_options->corners;
- glUniform1f(shader.radius_top_left, (CORNER_LOCATION_TOP_LEFT & corners) == CORNER_LOCATION_TOP_LEFT ?
- fx_options->corner_radius : 0);
- glUniform1f(shader.radius_top_right, (CORNER_LOCATION_TOP_RIGHT & corners) == CORNER_LOCATION_TOP_RIGHT ?
- fx_options->corner_radius : 0);
- glUniform1f(shader.radius_bottom_left, (CORNER_LOCATION_BOTTOM_LEFT & corners) == CORNER_LOCATION_BOTTOM_LEFT ?
- fx_options->corner_radius : 0);
- glUniform1f(shader.radius_bottom_right, (CORNER_LOCATION_BOTTOM_RIGHT & corners) == CORNER_LOCATION_BOTTOM_RIGHT ?
- fx_options->corner_radius : 0);
+ struct fx_corner_fradii corners = fx_options->corners;
+ uniform_corner_radii_set(&shader.radius, &corners);
render(&box, options->clip, shader.pos_attrib);
pop_fx_debug(renderer);
+ TRACY_BOTH_ZONES_END;
}
void fx_render_pass_add_box_shadow(struct fx_gles_render_pass *pass,
@@ -635,23 +783,25 @@ void fx_render_pass_add_box_shadow(struct fx_gles_render_pass *pass,
pixman_region32_init_rect(&clip_region, box.x, box.y, box.width, box.height);
}
const struct wlr_box clipped_region_box = options->clipped_region.area;
- enum corner_location clipped_region_corners = options->clipped_region.corners;
- int clipped_region_corner_radius = clipped_region_corners != CORNER_LOCATION_NONE ?
- options->clipped_region.corner_radius : 0;
- if (!wlr_box_empty(&clipped_region_box)) {
- pixman_region32_t user_clip_region;
- pixman_region32_init_rect(
- &user_clip_region,
- clipped_region_box.x + clipped_region_corner_radius * 0.3,
- clipped_region_box.y + clipped_region_corner_radius * 0.3,
- fmax(clipped_region_box.width - clipped_region_corner_radius * 0.6, 0),
- fmax(clipped_region_box.height - clipped_region_corner_radius * 0.6, 0)
- );
- pixman_region32_subtract(&clip_region, &clip_region, &user_clip_region);
- pixman_region32_fini(&user_clip_region);
- }
-
+ struct fx_corner_fradii clipped_region_corners = options->clipped_region.corners;
+ apply_clip_region(&clip_region, &clipped_region_box, &clipped_region_corners);
+
+ TRACY_BOTH_ZONES_START(renderer);
+ TRACY_ZONE_TEXT_f("Box (WxH, X, Y): %dx%d, %d, %d", box.width, box.height, box.x, box.y);
+ TRACY_ZONE_TEXT_f("Clip Box (WxH, X, Y): %dx%d, %d, %d",
+ clipped_region_box.width, clipped_region_box.height,
+ clipped_region_box.x, clipped_region_box.y);
+ TRACY_ZONE_TEXT_f("Clip Box Corners (TL, TR, BL, BR): %f, %f, %f, %f",
+ clipped_region_corners.top_left,
+ clipped_region_corners.top_right,
+ clipped_region_corners.bottom_left,
+ clipped_region_corners.bottom_right);
+ TRACY_ZONE_TEXT_f("Shadow Options:");
+ TRACY_ZONE_TEXT_f("\tColor RGBA: %f, %f, %f, %f",
+ options->color.r, options->color.g, options->color.b, options->color.a);
+ TRACY_ZONE_TEXT_f("\tBlur Sigma: %f", options->blur_sigma);
push_fx_debug(renderer);
+
// blending will practically always be needed (unless we have a madman
// who uses opaque shadows with zero sigma), so just enable it
setup_blending(WLR_RENDER_BLEND_MODE_PREMULTIPLIED);
@@ -667,18 +817,7 @@ void fx_render_pass_add_box_shadow(struct fx_gles_render_pass *pass,
glUniform2f(renderer->shaders.box_shadow.position, box.x, box.y);
glUniform1f(renderer->shaders.box_shadow.corner_radius, options->corner_radius);
- glUniform1f(renderer->shaders.box_shadow.clip_radius_top_left,
- (CORNER_LOCATION_TOP_LEFT & clipped_region_corners) == CORNER_LOCATION_TOP_LEFT ?
- clipped_region_corner_radius : 0);
- glUniform1f(renderer->shaders.box_shadow.clip_radius_top_right,
- (CORNER_LOCATION_TOP_RIGHT & clipped_region_corners) == CORNER_LOCATION_TOP_RIGHT ?
- clipped_region_corner_radius : 0);
- glUniform1f(renderer->shaders.box_shadow.clip_radius_bottom_left,
- (CORNER_LOCATION_BOTTOM_LEFT & clipped_region_corners) == CORNER_LOCATION_BOTTOM_LEFT ?
- clipped_region_corner_radius : 0);
- glUniform1f(renderer->shaders.box_shadow.clip_radius_bottom_right,
- (CORNER_LOCATION_BOTTOM_RIGHT & clipped_region_corners) == CORNER_LOCATION_BOTTOM_RIGHT ?
- clipped_region_corner_radius : 0);
+ uniform_corner_radii_set(&renderer->shaders.box_shadow.clip_radius, &clipped_region_corners);
glUniform2f(renderer->shaders.box_shadow.clip_position, clipped_region_box.x, clipped_region_box.y);
glUniform2f(renderer->shaders.box_shadow.clip_size, clipped_region_box.width, clipped_region_box.height);
@@ -689,6 +828,7 @@ void fx_render_pass_add_box_shadow(struct fx_gles_render_pass *pass,
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
pop_fx_debug(renderer);
+ TRACY_BOTH_ZONES_END;
}
// Renders the blur for each damaged rect and swaps the buffer
@@ -699,11 +839,14 @@ static void render_blur_segments(struct fx_gles_render_pass *pass,
struct fx_renderer *renderer = pass->buffer->renderer;
struct blur_data *blur_data = fx_options->blur_data;
+ TRACY_BOTH_ZONES_START(renderer);
+ push_fx_debug(renderer);
+
// Swap fbo
- if (fx_options->current_buffer == pass->fx_effect_framebuffers->effects_buffer) {
- fx_framebuffer_bind(pass->fx_effect_framebuffers->effects_buffer_swapped);
+ if (fx_options->current_buffer == pass->fx_offscreen_buffers->effects_buffer) {
+ fx_framebuffer_bind(pass->fx_offscreen_buffers->effects_buffer_swapped);
} else {
- fx_framebuffer_bind(pass->fx_effect_framebuffers->effects_buffer);
+ fx_framebuffer_bind(pass->fx_offscreen_buffers->effects_buffer);
}
options->texture = fx_texture_from_buffer(&renderer->wlr_renderer,
@@ -726,8 +869,6 @@ static void render_blur_segments(struct fx_gles_render_pass *pass,
glDisable(GL_BLEND);
glDisable(GL_STENCIL_TEST);
- push_fx_debug(renderer);
-
glUseProgram(shader->program);
glActiveTexture(GL_TEXTURE0);
@@ -762,14 +903,15 @@ static void render_blur_segments(struct fx_gles_render_pass *pass,
glBindTexture(texture->target, 0);
pop_fx_debug(renderer);
+ TRACY_BOTH_ZONES_END;
wlr_texture_destroy(options->texture);
// Swap buffer. We don't want to draw to the same buffer
- if (fx_options->current_buffer != pass->fx_effect_framebuffers->effects_buffer) {
- fx_options->current_buffer = pass->fx_effect_framebuffers->effects_buffer;
+ if (fx_options->current_buffer != pass->fx_offscreen_buffers->effects_buffer) {
+ fx_options->current_buffer = pass->fx_offscreen_buffers->effects_buffer;
} else {
- fx_options->current_buffer = pass->fx_effect_framebuffers->effects_buffer_swapped;
+ fx_options->current_buffer = pass->fx_offscreen_buffers->effects_buffer_swapped;
}
}
@@ -796,6 +938,7 @@ static void render_blur_effects(struct fx_gles_render_pass *pass,
glDisable(GL_BLEND);
glDisable(GL_STENCIL_TEST);
+ TRACY_BOTH_ZONES_START(renderer);
push_fx_debug(renderer);
glUseProgram(shader.program);
@@ -824,50 +967,101 @@ static void render_blur_effects(struct fx_gles_render_pass *pass,
render(&dst_box, options->clip, shader.pos_attrib);
glBindTexture(texture->target, 0);
+
pop_fx_debug(renderer);
+ TRACY_BOTH_ZONES_END;
wlr_texture_destroy(options->texture);
}
-// Blurs the main_buffer content and returns the blurred framebuffer
+// Blurs the fx_options current_buffer content and returns the blurred framebuffer.
+// Returns NULL when the blur parameters reach 0.
static struct fx_framebuffer *get_main_buffer_blur(struct fx_gles_render_pass *pass,
struct fx_render_blur_pass_options *fx_options) {
+ if (pass->fx_offscreen_buffers == NULL) {
+ wlr_log(WLR_ERROR, "FX Pass offscreen buffers not initialized. Skipping getting blur...");
+ return NULL;
+ }
+
struct fx_renderer *renderer = pass->buffer->renderer;
- struct blur_data *blur_data = fx_options->blur_data;
- struct wlr_box monitor_box = get_monitor_box(pass->output);
+ struct wlr_box buffer_bounds = {
+ 0, 0,
+ fx_options->current_buffer->buffer->width, fx_options->current_buffer->buffer->height
+ };
+
+ // We don't want to affect the reference blur_data
+ struct blur_data blur_data = blur_data_apply_strength(fx_options->blur_data, fx_options->blur_strength);
+ if (fx_options->blur_strength <= 0 || !is_scene_blur_enabled(&blur_data)) {
+ return NULL;
+ }
+ fx_options->blur_data = &blur_data;
pixman_region32_t damage;
pixman_region32_init(&damage);
pixman_region32_copy(&damage, fx_options->tex_options.base.clip);
wlr_region_transform(&damage, &damage, fx_options->tex_options.base.transform,
- monitor_box.width, monitor_box.height);
+ buffer_bounds.width, buffer_bounds.height);
- wlr_region_expand(&damage, &damage, blur_data_calc_size(fx_options->blur_data));
+ wlr_region_expand(&damage, &damage, blur_data_calc_size(&blur_data));
+ // Make sure that the region doesn't expand past the buffer bounds
+ pixman_region32_intersect_rect(&damage, &damage,
+ 0, 0, buffer_bounds.width, buffer_bounds.height);
// damage region will be scaled, make a temp
pixman_region32_t scaled_damage;
pixman_region32_init(&scaled_damage);
fx_options->tex_options.base.src_box = (struct wlr_fbox) {
- monitor_box.x,
- monitor_box.y,
- monitor_box.width,
- monitor_box.height,
+ 0, 0,
+ buffer_bounds.width, buffer_bounds.height,
};
- fx_options->tex_options.base.dst_box = monitor_box;
+ fx_options->tex_options.base.dst_box = buffer_bounds;
// Clip the blur to the damage
fx_options->tex_options.base.clip = &scaled_damage;
// Artifacts with NEAREST filter
fx_options->tex_options.base.filter_mode = WLR_SCALE_FILTER_BILINEAR;
+ TRACY_BOTH_ZONES_START(renderer);
+ TRACY_ZONE_TEXT_f("dst_box (WxH, X, Y): %dx%d, %d, %d",
+ fx_options->tex_options.base.dst_box.width,
+ fx_options->tex_options.base.dst_box.height,
+ fx_options->tex_options.base.dst_box.x,
+ fx_options->tex_options.base.dst_box.y);
+ TRACY_ZONE_TEXT_f("clip_box (WxH, X, Y): %dx%d, %d, %d",
+ fx_options->tex_options.clip_box->width,
+ fx_options->tex_options.clip_box->height,
+ fx_options->tex_options.clip_box->x,
+ fx_options->tex_options.clip_box->y);
+ TRACY_ZONE_TEXT_f("Corners (TL, TR, BL, BR): %f, %f, %f, %f",
+ fx_options->corners.top_left,
+ fx_options->corners.top_right,
+ fx_options->corners.bottom_left,
+ fx_options->corners.bottom_right);
+ TRACY_ZONE_TEXT_f("src_box (WxH, X, Y): %lfx%lf, %lf, %lf",
+ fx_options->tex_options.base.src_box.width,
+ fx_options->tex_options.base.src_box.height,
+ fx_options->tex_options.base.src_box.x,
+ fx_options->tex_options.base.src_box.y);
+ TRACY_ZONE_TEXT_f("Ignore Transparent: %d", fx_options->ignore_transparent);
+ TRACY_ZONE_TEXT_f("Discard Transparent: %d", fx_options->tex_options.discard_transparent);
+ TRACY_ZONE_TEXT_f("Use Optimized Blur: %d", fx_options->use_optimized_blur);
+ TRACY_ZONE_TEXT_f("Blur Options:");
+ TRACY_ZONE_TEXT_f("\tNum Blur Passes: %d", fx_options->blur_data->num_passes);
+ TRACY_ZONE_TEXT_f("\tBlur Radius: %f", fx_options->blur_data->radius);
+ TRACY_ZONE_TEXT_f("\tBrightness: %f", fx_options->blur_data->brightness);
+ TRACY_ZONE_TEXT_f("\tContrast: %f", fx_options->blur_data->contrast);
+ TRACY_ZONE_TEXT_f("\tNoise: %f", fx_options->blur_data->noise);
+ TRACY_ZONE_TEXT_f("\tSaturation: %f", fx_options->blur_data->saturation);
+ push_fx_debug(renderer);
+
// Downscale
- for (int i = 0; i < blur_data->num_passes; ++i) {
+ for (int i = 0; i < blur_data.num_passes; ++i) {
wlr_region_scale(&scaled_damage, &damage, 1.0f / (1 << (i + 1)));
render_blur_segments(pass, fx_options, &renderer->shaders.blur1);
}
// Upscale
- for (int i = blur_data->num_passes - 1; i >= 0; --i) {
+ for (int i = blur_data.num_passes - 1; i >= 0; --i) {
// when upsampling we make the region twice as big
wlr_region_scale(&scaled_damage, &damage, 1.0f / (1 << i));
render_blur_segments(pass, fx_options, &renderer->shaders.blur2);
@@ -876,21 +1070,21 @@ static struct fx_framebuffer *get_main_buffer_blur(struct fx_gles_render_pass *p
pixman_region32_fini(&scaled_damage);
// Render additional blur effects like saturation, noise, contrast, etc...
- if (blur_data_should_parameters_blur_effects(blur_data)
+ if (blur_data_should_parameters_blur_effects(&blur_data)
&& pixman_region32_not_empty(&damage)) {
- if (fx_options->current_buffer == pass->fx_effect_framebuffers->effects_buffer) {
- fx_framebuffer_bind(pass->fx_effect_framebuffers->effects_buffer_swapped);
+ if (fx_options->current_buffer == pass->fx_offscreen_buffers->effects_buffer) {
+ fx_framebuffer_bind(pass->fx_offscreen_buffers->effects_buffer_swapped);
} else {
- fx_framebuffer_bind(pass->fx_effect_framebuffers->effects_buffer);
+ fx_framebuffer_bind(pass->fx_offscreen_buffers->effects_buffer);
}
fx_options->tex_options.base.clip = &damage;
fx_options->tex_options.base.texture = fx_texture_from_buffer(
&renderer->wlr_renderer, fx_options->current_buffer->buffer);
render_blur_effects(pass, fx_options);
- if (fx_options->current_buffer != pass->fx_effect_framebuffers->effects_buffer) {
- fx_options->current_buffer = pass->fx_effect_framebuffers->effects_buffer;
+ if (fx_options->current_buffer != pass->fx_offscreen_buffers->effects_buffer) {
+ fx_options->current_buffer = pass->fx_offscreen_buffers->effects_buffer;
} else {
- fx_options->current_buffer = pass->fx_effect_framebuffers->effects_buffer_swapped;
+ fx_options->current_buffer = pass->fx_offscreen_buffers->effects_buffer_swapped;
}
}
@@ -899,52 +1093,48 @@ static struct fx_framebuffer *get_main_buffer_blur(struct fx_gles_render_pass *p
// Bind back to the default buffer
fx_framebuffer_bind(pass->buffer);
+ pop_fx_debug(renderer);
+ TRACY_BOTH_ZONES_END;
+
return fx_options->current_buffer;
}
void fx_render_pass_add_blur(struct fx_gles_render_pass *pass,
struct fx_render_blur_pass_options *fx_options) {
- if (pass->buffer->renderer->basic_renderer) {
- wlr_log(WLR_ERROR, "Please use 'fx_renderer_begin_buffer_pass' instead of "
- "'wlr_renderer_begin_buffer_pass' to use advanced effects");
+ if (pass->fx_offscreen_buffers == NULL) {
+ wlr_log(WLR_ERROR, "FX Pass offscreen buffers not initialized. Skipping blur...");
return;
}
+
struct fx_renderer *renderer = pass->buffer->renderer;
struct fx_render_texture_options *tex_options = &fx_options->tex_options;
- const struct wlr_render_texture_options *options = &tex_options->base;
-
- pixman_region32_t translucent_region;
- pixman_region32_init(&translucent_region);
-
- struct wlr_box dst_box;
- wlr_render_texture_options_get_dst_box(options, &dst_box);
-
- // Gets the translucent region
- pixman_box32_t surface_box = { 0, 0, dst_box.width, dst_box.height };
- pixman_region32_copy(&translucent_region, fx_options->opaque_region);
- pixman_region32_inverse(&translucent_region, &translucent_region, &surface_box);
- if (!pixman_region32_not_empty(&translucent_region)) {
- goto damage_finish;
- }
- struct fx_framebuffer *buffer = pass->fx_effect_framebuffers->optimized_blur_buffer;
- if (!buffer || !fx_options->use_optimized_blur) {
- if (!buffer) {
- wlr_log(WLR_ERROR, "Warning: Failed to use optimized blur");
- }
- pixman_region32_translate(&translucent_region, dst_box.x, dst_box.y);
- pixman_region32_intersect(&translucent_region, &translucent_region, options->clip);
+ TRACY_BOTH_ZONES_START(renderer);
+ push_fx_debug(renderer);
+ const bool has_strength = fx_options->blur_strength < 1.0;
+ struct fx_framebuffer *buffer = pass->fx_offscreen_buffers->optimized_blur_buffer;
+ TRACY_ZONE_TEXT_f("Use Optimized Blur: %d", fx_options->use_optimized_blur);
+ TRACY_ZONE_TEXT_f("Optimized Blur Successfully Used: %d",
+ buffer && fx_options->use_optimized_blur);
+ if (!fx_options->use_optimized_blur || has_strength) {
// Render the blur into its own buffer
struct fx_render_blur_pass_options blur_options = *fx_options;
- blur_options.tex_options.base.clip = &translucent_region;
- blur_options.current_buffer = pass->buffer;
+ if (fx_options->use_optimized_blur && has_strength) {
+ // Re-blur the saved non-blurred version of the optimized blur.
+ // Isn't as efficient as just using the optimized blur buffer
+ blur_options.current_buffer = pass->fx_offscreen_buffers->optimized_no_blur_buffer;
+ } else {
+ blur_options.current_buffer = pass->buffer;
+ }
buffer = get_main_buffer_blur(pass, &blur_options);
}
+ if (!buffer) {
+ goto finish;
+ }
struct wlr_texture *wlr_texture =
fx_texture_from_buffer(&renderer->wlr_renderer, buffer->buffer);
struct fx_texture *blur_texture = fx_get_texture(wlr_texture);
- blur_texture->has_alpha = true;
// Get a stencil of the window ignoring transparent regions
if (fx_options->ignore_transparent && fx_options->tex_options.base.texture) {
@@ -952,13 +1142,19 @@ void fx_render_pass_add_blur(struct fx_gles_render_pass *pass,
struct fx_render_texture_options tex_options = fx_options->tex_options;
tex_options.discard_transparent = true;
+ tex_options.clipped_region = fx_options->clipped_region;
fx_render_pass_add_texture(pass, &tex_options);
stencil_mask_close(true);
}
// Draw the blurred texture
- tex_options->base.dst_box = get_monitor_box(pass->output);
+ tex_options->base.dst_box = (struct wlr_box) {
+ .x = 0,
+ .y = 0,
+ .width = buffer->buffer->width,
+ .height = buffer->buffer->height,
+ };
tex_options->base.src_box = (struct wlr_fbox) {
.x = 0,
.y = 0,
@@ -966,6 +1162,9 @@ void fx_render_pass_add_blur(struct fx_gles_render_pass *pass,
.height = buffer->buffer->height,
};
tex_options->base.texture = &blur_texture->wlr_texture;
+ // since we're capturing from the fbo, transform will always be normal
+ tex_options->base.transform = WL_OUTPUT_TRANSFORM_NORMAL;
+ tex_options->clipped_region = fx_options->clipped_region;
fx_render_pass_add_texture(pass, tex_options);
wlr_texture_destroy(&blur_texture->wlr_texture);
@@ -975,20 +1174,48 @@ void fx_render_pass_add_blur(struct fx_gles_render_pass *pass,
stencil_mask_fini();
}
-damage_finish:
- pixman_region32_fini(&translucent_region);
+finish:
+ pop_fx_debug(renderer);
+ TRACY_BOTH_ZONES_END;
}
bool fx_render_pass_add_optimized_blur(struct fx_gles_render_pass *pass,
struct fx_render_blur_pass_options *fx_options) {
- if (pass->buffer->renderer->basic_renderer) {
- wlr_log(WLR_ERROR, "Please use 'fx_renderer_begin_buffer_pass' instead of "
- "'wlr_renderer_begin_buffer_pass' to use advanced effects");
+ if (pass->fx_offscreen_buffers == NULL) {
+ wlr_log(WLR_ERROR, "FX Pass offscreen buffers not initialized. Skipping optimized blur...");
return false;
}
struct fx_renderer *renderer = pass->buffer->renderer;
struct wlr_box dst_box = fx_options->tex_options.base.dst_box;
+ TRACY_BOTH_ZONES_START(renderer);
+ TRACY_ZONE_TEXT_f("dst_box (WxH, X, Y): %dx%d, %d, %d",
+ fx_options->tex_options.base.dst_box.width,
+ fx_options->tex_options.base.dst_box.height,
+ fx_options->tex_options.base.dst_box.x,
+ fx_options->tex_options.base.dst_box.y);
+ TRACY_ZONE_TEXT_f("clip_box (WxH, X, Y): %dx%d, %d, %d",
+ fx_options->tex_options.clip_box->width,
+ fx_options->tex_options.clip_box->height,
+ fx_options->tex_options.clip_box->x,
+ fx_options->tex_options.clip_box->y);
+ TRACY_ZONE_TEXT_f("src_box (WxH, X, Y): %lfx%lf, %lf, %lf",
+ fx_options->tex_options.base.src_box.width,
+ fx_options->tex_options.base.src_box.height,
+ fx_options->tex_options.base.src_box.x,
+ fx_options->tex_options.base.src_box.y);
+ TRACY_ZONE_TEXT_f("Ignore Transparent: %d", fx_options->ignore_transparent);
+ TRACY_ZONE_TEXT_f("Discard Transparent: %d", fx_options->tex_options.discard_transparent);
+ TRACY_ZONE_TEXT_f("Use Optimized Blur: %d", fx_options->use_optimized_blur);
+ TRACY_ZONE_TEXT_f("Blur Options:");
+ TRACY_ZONE_TEXT_f("\tNum Blur Passes: %d", fx_options->blur_data->num_passes);
+ TRACY_ZONE_TEXT_f("\tBlur Radius: %f", fx_options->blur_data->radius);
+ TRACY_ZONE_TEXT_f("\tBrightness: %f", fx_options->blur_data->brightness);
+ TRACY_ZONE_TEXT_f("\tContrast: %f", fx_options->blur_data->contrast);
+ TRACY_ZONE_TEXT_f("\tNoise: %f", fx_options->blur_data->noise);
+ TRACY_ZONE_TEXT_f("\tSaturation: %f", fx_options->blur_data->saturation);
+ push_fx_debug(renderer);
+
pixman_region32_t clip;
pixman_region32_init_rect(&clip,
dst_box.x, dst_box.y, dst_box.width, dst_box.height);
@@ -997,32 +1224,31 @@ bool fx_render_pass_add_optimized_blur(struct fx_gles_render_pass *pass,
struct fx_render_blur_pass_options blur_options = *fx_options;
blur_options.current_buffer = pass->buffer;
blur_options.tex_options.base.clip = &clip;
- struct fx_framebuffer *buffer = get_main_buffer_blur(pass, &blur_options);
-
- // Update the optimized blur buffer if invalid
- bool failed = false;
- fx_framebuffer_get_or_create_custom(renderer, pass->output, NULL,
- &pass->fx_effect_framebuffers->optimized_blur_buffer, &failed);
- if (failed) {
- goto finish;
+ struct fx_framebuffer *fx_buffer = get_main_buffer_blur(pass, &blur_options);
+ if (fx_buffer != NULL) {
+ // Render the newly blurred content into the blur_buffer
+ fx_render_pass_read_to_buffer(pass, &clip,
+ pass->fx_offscreen_buffers->optimized_blur_buffer, fx_buffer);
+
+ // Save the current scene pass state
+ fx_render_pass_read_to_buffer(pass, &clip,
+ pass->fx_offscreen_buffers->optimized_no_blur_buffer, pass->buffer);
}
- // Render the newly blurred content into the blur_buffer
- fx_renderer_read_to_buffer(pass, &clip,
- pass->fx_effect_framebuffers->optimized_blur_buffer, buffer);
-
-finish:
pixman_region32_fini(&clip);
- return !failed;
+
+ pop_fx_debug(renderer);
+ TRACY_BOTH_ZONES_END;
+ return fx_buffer != NULL;
}
-// TODO: Use blitting for glesv3
-void fx_renderer_read_to_buffer(struct fx_gles_render_pass *pass,
+void fx_render_pass_read_to_buffer(struct fx_gles_render_pass *pass,
pixman_region32_t *_region, struct fx_framebuffer *dst_buffer,
struct fx_framebuffer *src_buffer) {
if (!_region || !pixman_region32_not_empty(_region)) {
return;
}
+ TRACY_BOTH_ZONES_START(pass->buffer->renderer);
pixman_region32_t region;
pixman_region32_init(®ion);
@@ -1031,8 +1257,7 @@ void fx_renderer_read_to_buffer(struct fx_gles_render_pass *pass,
struct wlr_texture *src_tex =
fx_texture_from_buffer(&pass->buffer->renderer->wlr_renderer, src_buffer->buffer);
if (src_tex == NULL) {
- pixman_region32_fini(®ion);
- return;
+ goto done;
}
// Draw onto the dst_buffer
@@ -1043,6 +1268,8 @@ void fx_renderer_read_to_buffer(struct fx_gles_render_pass *pass,
.transform = WL_OUTPUT_TRANSFORM_NORMAL,
.blend_mode = WLR_RENDER_BLEND_MODE_NONE,
.dst_box = (struct wlr_box){
+ .x = 0,
+ .y = 0,
.width = dst_buffer->buffer->width,
.height = dst_buffer->buffer->height,
},
@@ -1058,10 +1285,12 @@ void fx_renderer_read_to_buffer(struct fx_gles_render_pass *pass,
// Bind back to the main WLR buffer
fx_framebuffer_bind(pass->buffer);
+done:
+ TRACY_BOTH_ZONES_END;
+
pixman_region32_fini(®ion);
}
-
static const char *reset_status_str(GLenum status) {
switch (status) {
case GL_GUILTY_CONTEXT_RESET_KHR:
@@ -1075,7 +1304,7 @@ static const char *reset_status_str(GLenum status) {
}
}
-static struct fx_gles_render_pass *begin_buffer_pass(struct fx_framebuffer *buffer,
+struct fx_gles_render_pass *fx_begin_buffer_pass(struct fx_framebuffer *buffer,
struct wlr_egl_context *prev_ctx, struct fx_render_timer *timer,
struct wlr_drm_syncobj_timeline *signal_timeline, uint64_t signal_point) {
struct fx_renderer *renderer = buffer->renderer;
@@ -1110,6 +1339,10 @@ static struct fx_gles_render_pass *begin_buffer_pass(struct fx_framebuffer *buff
pass->signal_point = signal_point;
}
+ pass->fx_offscreen_buffers = NULL;
+ pixman_region32_init(&pass->blur_padding_region);
+ pass->has_blur = false;
+
matrix_projection(pass->projection_matrix, wlr_buffer->width, wlr_buffer->height,
WL_OUTPUT_TRANSFORM_FLIPPED_180);
@@ -1119,66 +1352,7 @@ static struct fx_gles_render_pass *begin_buffer_pass(struct fx_framebuffer *buff
glViewport(0, 0, wlr_buffer->width, wlr_buffer->height);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_SCISSOR_TEST);
- pop_fx_debug(renderer);
- return pass;
-}
-
-struct fx_gles_render_pass *fx_renderer_begin_buffer_pass(
- struct wlr_renderer *wlr_renderer, struct wlr_buffer *wlr_buffer,
- struct wlr_output *output, const struct fx_buffer_pass_options *fx_options) {
- const struct wlr_buffer_pass_options *options = fx_options->base;
- struct fx_renderer *renderer = fx_get_renderer(wlr_renderer);
-
- renderer->basic_renderer = (output == NULL);
-
- struct wlr_egl_context prev_ctx = {0};
- if (!wlr_egl_make_current(renderer->egl, &prev_ctx)) {
- return NULL;
- }
-
- struct fx_render_timer *timer = NULL;
- if (options->timer) {
- timer = fx_get_render_timer(options->timer);
- clock_gettime(CLOCK_MONOTONIC, &timer->cpu_start);
- }
-
- struct fx_framebuffer *buffer = fx_framebuffer_get_or_create(renderer, wlr_buffer);
- if (!buffer) {
- return NULL;
- }
-
- // For per output framebuffers
- struct fx_effect_framebuffers *fbos = NULL;
- // Update the buffers if needed
- if (!renderer->basic_renderer) {
- bool failed = false;
-
- fbos = fx_effect_framebuffers_try_get(output);
- failed |= fbos == NULL;
- if (fbos) {
- pixman_region32_init(&fbos->blur_padding_region);
-
- fx_framebuffer_get_or_create_custom(renderer, output, fx_options->swapchain,
- &fbos->blur_saved_pixels_buffer, &failed);
- fx_framebuffer_get_or_create_custom(renderer, output, fx_options->swapchain,
- &fbos->effects_buffer, &failed);
- fx_framebuffer_get_or_create_custom(renderer, output, fx_options->swapchain,
- &fbos->effects_buffer_swapped, &failed);
- }
-
- if (failed) {
- // Revert back to not running advanced effects
- renderer->basic_renderer = true;
- }
- }
-
- struct fx_gles_render_pass *pass = begin_buffer_pass(buffer,
- &prev_ctx, timer, options->signal_timeline, options->signal_point);
- if (!pass) {
- return NULL;
- }
- pass->fx_effect_framebuffers = fbos;
- pass->output = output;
+ pop_fx_debug(renderer);
return pass;
}
diff --git a/scenefx/render/fx_renderer/fx_renderer.c b/scenefx/render/fx_renderer/fx_renderer.c
index ff42020..8d0a7b2 100644
--- a/scenefx/render/fx_renderer/fx_renderer.c
+++ b/scenefx/render/fx_renderer/fx_renderer.c
@@ -21,6 +21,9 @@
#include "render/fx_renderer/shaders.h"
#include "render/fx_renderer/fx_renderer.h"
#include "render/fx_renderer/util.h"
+#include "render/pass.h"
+#include "render/tracy.h"
+#include "scenefx/render/fx_renderer/fx_offscreen_buffers.h"
#include "scenefx/render/fx_renderer/fx_renderer.h"
#include "scenefx/render/pass.h"
#include "util/time.h"
@@ -78,9 +81,31 @@ static int fx_get_drm_fd(struct wlr_renderer *wlr_renderer) {
return renderer->drm_fd;
}
+static inline void free_shaders(struct fx_renderer *renderer) {
+ push_fx_debug(renderer);
+ glDeleteProgram(renderer->shaders.quad.program);
+ glDeleteProgram(renderer->shaders.quad_clip.program);
+ glDeleteProgram(renderer->shaders.quad_round.program);
+ glDeleteProgram(renderer->shaders.quad_grad.program);
+ glDeleteProgram(renderer->shaders.quad_grad_round.program);
+ glDeleteProgram(renderer->shaders.tex_rgba.program);
+ glDeleteProgram(renderer->shaders.tex_rgbx.program);
+ glDeleteProgram(renderer->shaders.tex_ext.program);
+ glDeleteProgram(renderer->shaders.tex_effects_rgba.program);
+ glDeleteProgram(renderer->shaders.tex_effects_rgbx.program);
+ glDeleteProgram(renderer->shaders.tex_effects_ext.program);
+ glDeleteProgram(renderer->shaders.box_shadow.program);
+ glDeleteProgram(renderer->shaders.blur1.program);
+ glDeleteProgram(renderer->shaders.blur2.program);
+ glDeleteProgram(renderer->shaders.blur_effects.program);
+ pop_fx_debug(renderer);
+}
+
static void fx_renderer_destroy(struct wlr_renderer *wlr_renderer) {
struct fx_renderer *renderer = fx_get_renderer(wlr_renderer);
+ TRACY_GPU_CONTEXT_DESTROY(renderer->tracy_data);
+
wlr_egl_make_current(renderer->egl, NULL);
struct fx_texture *tex, *tex_tmp;
@@ -88,17 +113,17 @@ static void fx_renderer_destroy(struct wlr_renderer *wlr_renderer) {
fx_texture_destroy(tex);
}
+ struct fx_offscreen_buffers *fbos, *fbos_tmp;
+ wl_list_for_each_safe(fbos, fbos_tmp, &renderer->offscreen_buffers, link) {
+ fx_offscreen_buffers_destroy(fbos);
+ }
+
struct fx_framebuffer *buffer, *buffer_tmp;
wl_list_for_each_safe(buffer, buffer_tmp, &renderer->buffers, link) {
fx_framebuffer_destroy(buffer);
}
- push_fx_debug(renderer);
- glDeleteProgram(renderer->shaders.quad.program);
- glDeleteProgram(renderer->shaders.tex_rgba.program);
- glDeleteProgram(renderer->shaders.tex_rgbx.program);
- glDeleteProgram(renderer->shaders.tex_ext.program);
- pop_fx_debug(renderer);
+ free_shaders(renderer);
if (renderer->exts.KHR_debug) {
glDisable(GL_DEBUG_OUTPUT_KHR);
@@ -119,14 +144,36 @@ static void fx_renderer_destroy(struct wlr_renderer *wlr_renderer) {
static struct wlr_render_pass *begin_buffer_pass(struct wlr_renderer *wlr_renderer,
struct wlr_buffer *wlr_buffer, const struct wlr_buffer_pass_options *options) {
- struct fx_gles_render_pass *pass =
- fx_renderer_begin_buffer_pass(wlr_renderer, wlr_buffer, NULL, &(struct fx_buffer_pass_options) {
- .base = options,
- .swapchain = NULL,
- });
+ struct fx_renderer *renderer = fx_get_renderer(wlr_renderer);
+
+ struct wlr_egl_context prev_ctx = {0};
+ if (!wlr_egl_make_current(renderer->egl, &prev_ctx)) {
+ return NULL;
+ }
+
+ TRACY_BOTH_ZONES_START(renderer);
+
+ struct fx_render_timer *timer = NULL;
+ if (options->timer) {
+ timer = fx_get_render_timer(options->timer);
+ clock_gettime(CLOCK_MONOTONIC, &timer->cpu_start);
+ }
+
+ struct fx_framebuffer *buffer = fx_framebuffer_get_or_create(renderer, wlr_buffer);
+ if (!buffer) {
+ TRACY_BOTH_ZONES_END_FAIL;
+ return NULL;
+ }
+
+ struct fx_gles_render_pass *pass = fx_begin_buffer_pass(buffer,
+ &prev_ctx, timer, options->signal_timeline, options->signal_point);
if (!pass) {
+ TRACY_BOTH_ZONES_END_FAIL;
return NULL;
}
+
+ TRACY_BOTH_ZONES_END;
+
return &pass->base;
}
@@ -308,62 +355,84 @@ struct wlr_renderer *fx_renderer_create(struct wlr_backend *backend) {
}
static bool link_shaders(struct fx_renderer *renderer) {
- EGLint client_version;
- eglQueryContext(renderer->egl->display, renderer->egl->context,
- EGL_CONTEXT_CLIENT_VERSION, &client_version);
-
// quad fragment shader
- if (!link_quad_program(&renderer->shaders.quad, (GLint) client_version)) {
+ if (!link_quad_program(&renderer->shaders.quad, false)) {
wlr_log(WLR_ERROR, "Could not link quad shader");
goto error;
}
+ // quad clip fragment shader
+ if (!link_quad_program(&renderer->shaders.quad_clip, true)) {
+ wlr_log(WLR_ERROR, "Could not link quad clip shader");
+ goto error;
+ }
+
// quad fragment shader with gradients
- if (!link_quad_grad_program(&renderer->shaders.quad_grad, (GLint) client_version, 16)) {
+ if (!link_quad_grad_program(&renderer->shaders.quad_grad, 16)) {
wlr_log(WLR_ERROR, "Could not link quad grad shader");
goto error;
}
- if (!link_quad_grad_round_program(&renderer->shaders.quad_grad_round, (GLint) client_version, 16)) {
+ if (!link_quad_grad_round_program(&renderer->shaders.quad_grad_round, 16)) {
wlr_log(WLR_ERROR, "Could not link quad grad round shader");
goto error;
}
- if (!link_quad_round_program(&renderer->shaders.quad_round, (GLint) client_version)) {
+ if (!link_quad_round_program(&renderer->shaders.quad_round)) {
wlr_log(WLR_ERROR, "Could not link quad round shader");
goto error;
}
- // fragment shaders
- if (!link_tex_program(&renderer->shaders.tex_rgba, (GLint) client_version, SHADER_SOURCE_TEXTURE_RGBA)) {
+ // Basic fragment shaders
+ if (!link_tex_program(&renderer->shaders.tex_rgba,
+ SHADER_SOURCE_TEXTURE_RGBA, false)) {
wlr_log(WLR_ERROR, "Could not link tex_RGBA shader");
goto error;
}
- if (!link_tex_program(&renderer->shaders.tex_rgbx, (GLint) client_version, SHADER_SOURCE_TEXTURE_RGBX)) {
+ if (!link_tex_program(&renderer->shaders.tex_rgbx,
+ SHADER_SOURCE_TEXTURE_RGBX, false)) {
wlr_log(WLR_ERROR, "Could not link tex_RGBX shader");
goto error;
}
- if (!link_tex_program(&renderer->shaders.tex_ext, (GLint) client_version, SHADER_SOURCE_TEXTURE_EXTERNAL)) {
+ if (!link_tex_program(&renderer->shaders.tex_ext,
+ SHADER_SOURCE_TEXTURE_EXTERNAL, false)) {
wlr_log(WLR_ERROR, "Could not link tex_EXTERNAL shader");
goto error;
}
+ // Effects fragment shaders
+ if (!link_tex_program(&renderer->shaders.tex_effects_rgba,
+ SHADER_SOURCE_TEXTURE_RGBA, true)) {
+ wlr_log(WLR_ERROR, "Could not link tex_effects_RGBA shader");
+ goto error;
+ }
+ if (!link_tex_program(&renderer->shaders.tex_effects_rgbx,
+ SHADER_SOURCE_TEXTURE_RGBX, true)) {
+ wlr_log(WLR_ERROR, "Could not link tex_effects_RGBX shader");
+ goto error;
+ }
+ if (!link_tex_program(&renderer->shaders.tex_effects_ext,
+ SHADER_SOURCE_TEXTURE_EXTERNAL, true)) {
+ wlr_log(WLR_ERROR, "Could not link tex_effects_EXTERNAL shader");
+ goto error;
+ }
+
// box shadow shader
- if (!link_box_shadow_program(&renderer->shaders.box_shadow, (GLint) client_version)) {
+ if (!link_box_shadow_program(&renderer->shaders.box_shadow)) {
wlr_log(WLR_ERROR, "Could not link box shadow shader");
goto error;
}
// Blur shaders
- if (!link_blur1_program(&renderer->shaders.blur1, (GLint) client_version)) {
+ if (!link_blur1_program(&renderer->shaders.blur1)) {
wlr_log(WLR_ERROR, "Could not link blur1 shader");
goto error;
}
- if (!link_blur2_program(&renderer->shaders.blur2, (GLint) client_version)) {
+ if (!link_blur2_program(&renderer->shaders.blur2)) {
wlr_log(WLR_ERROR, "Could not link blur2 shader");
goto error;
}
- if (!link_blur_effects_program(&renderer->shaders.blur_effects, (GLint) client_version)) {
+ if (!link_blur_effects_program(&renderer->shaders.blur_effects)) {
wlr_log(WLR_ERROR, "Could not link blur_effects shader");
goto error;
}
@@ -371,18 +440,7 @@ static bool link_shaders(struct fx_renderer *renderer) {
return true;
error:
- glDeleteProgram(renderer->shaders.quad.program);
- glDeleteProgram(renderer->shaders.quad_round.program);
- glDeleteProgram(renderer->shaders.quad_grad.program);
- glDeleteProgram(renderer->shaders.quad_grad_round.program);
- glDeleteProgram(renderer->shaders.tex_rgba.program);
- glDeleteProgram(renderer->shaders.tex_rgbx.program);
- glDeleteProgram(renderer->shaders.tex_ext.program);
- glDeleteProgram(renderer->shaders.box_shadow.program);
- glDeleteProgram(renderer->shaders.blur1.program);
- glDeleteProgram(renderer->shaders.blur2.program);
- glDeleteProgram(renderer->shaders.blur_effects.program);
-
+ free_shaders(renderer);
return false;
}
@@ -406,6 +464,7 @@ struct wlr_renderer *fx_renderer_create_egl(struct wlr_egl *egl) {
wl_list_init(&renderer->buffers);
wl_list_init(&renderer->textures);
+ wl_list_init(&renderer->offscreen_buffers);
renderer->egl = egl;
renderer->exts_str = exts_str;
@@ -493,6 +552,9 @@ struct wlr_renderer *fx_renderer_create_egl(struct wlr_egl *egl) {
} else {
load_gl_proc(&renderer->procs.glGetInteger64vEXT, "glGetInteger64v");
}
+ TRACY_FN(
+ load_gl_proc(&renderer->procs.glGetQueryivEXT, "glGetQueryivEXT");
+ )
}
if (renderer->exts.KHR_debug) {
@@ -509,6 +571,10 @@ struct wlr_renderer *fx_renderer_create_egl(struct wlr_egl *egl) {
push_fx_debug(renderer);
+ TRACY_FN(
+ renderer->tracy_data = TRACY_GPU_CONTEXT_NEW(renderer);
+ )
+
// Link all shaders
if (!link_shaders(renderer)) {
goto error;
diff --git a/scenefx/render/fx_renderer/gles2/meson.build b/scenefx/render/fx_renderer/gles2/meson.build
deleted file mode 100644
index 70cde2c..0000000
--- a/scenefx/render/fx_renderer/gles2/meson.build
+++ /dev/null
@@ -1,2 +0,0 @@
-subdir('shaders')
-
diff --git a/scenefx/render/fx_renderer/gles2/shaders/corner_alpha.frag b/scenefx/render/fx_renderer/gles2/shaders/corner_alpha.frag
deleted file mode 100644
index e560f6c..0000000
--- a/scenefx/render/fx_renderer/gles2/shaders/corner_alpha.frag
+++ /dev/null
@@ -1,35 +0,0 @@
-float get_dist(vec2 q, float radius) {
- return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - radius;
-}
-
-float corner_alpha(vec2 size, vec2 position, float radius_tl, float radius_tr, float radius_bl, float radius_br) {
- vec2 relative_pos = (gl_FragCoord.xy - position);
-
- vec2 top_left = abs(relative_pos - size) - size + radius_tl;
- vec2 top_right = abs(relative_pos - vec2(0, size.y)) - size + radius_tr;
- vec2 bottom_left = abs(relative_pos - vec2(size.x, 0)) - size + radius_bl;
- vec2 bottom_right = abs(relative_pos) - size + radius_br;
-
- float dist = max(
- max(get_dist(top_left, radius_tl), get_dist(top_right, radius_tr)),
- max(get_dist(bottom_left, radius_bl), get_dist(bottom_right, radius_br))
- );
-
- return smoothstep(0.0, 1.0, dist);
-}
-
-float corner_dist(vec2 size, vec2 position, float radius_tl, float radius_tr, float radius_bl, float radius_br) {
- vec2 relative_pos = (gl_FragCoord.xy - position);
-
- vec2 top_left = abs(relative_pos - size) - size + radius_tl;
- vec2 top_right = abs(relative_pos - vec2(0, size.y)) - size + radius_tr;
- vec2 bottom_left = abs(relative_pos - vec2(size.x, 0)) - size + radius_bl;
- vec2 bottom_right = abs(relative_pos) - size + radius_br;
-
- float dist = max(
- max(get_dist(top_left, radius_tl), get_dist(top_right, radius_tr)),
- max(get_dist(bottom_left, radius_bl), get_dist(bottom_right, radius_br))
- );
-
- return dist;
-}
diff --git a/scenefx/render/fx_renderer/gles2/shaders/meson.build b/scenefx/render/fx_renderer/gles2/shaders/meson.build
deleted file mode 100644
index 260e07d..0000000
--- a/scenefx/render/fx_renderer/gles2/shaders/meson.build
+++ /dev/null
@@ -1,30 +0,0 @@
-embed = find_program('./embed.sh', native: true)
-
-shaders = [
- 'common.vert',
- 'gradient.frag',
- 'corner_alpha.frag',
- 'quad.frag',
- 'quad_grad.frag',
- 'quad_round.frag',
- 'quad_grad_round.frag',
- 'tex.frag',
- 'box_shadow.frag',
- 'blur1.frag',
- 'blur2.frag',
- 'blur_effects.frag',
-]
-
-foreach name : shaders
- output = name.underscorify() + '_gles2_src.h'
- var = name.underscorify() + '_gles2_src'
- scenefx_files += custom_target(
- output,
- command: [embed, var],
- input: name,
- output: output,
- feed: true,
- capture: true,
- )
-endforeach
-
diff --git a/scenefx/render/fx_renderer/gles2/shaders/quad.frag b/scenefx/render/fx_renderer/gles2/shaders/quad.frag
deleted file mode 100644
index 3144f93..0000000
--- a/scenefx/render/fx_renderer/gles2/shaders/quad.frag
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifdef GL_FRAGMENT_PRECISION_HIGH
-precision highp float;
-#else
-precision mediump float;
-#endif
-
-varying vec4 v_color;
-varying vec2 v_texcoord;
-
-uniform vec2 clip_size;
-uniform vec2 clip_position;
-uniform float clip_radius_top_left;
-uniform float clip_radius_top_right;
-uniform float clip_radius_bottom_left;
-uniform float clip_radius_bottom_right;
-
-float corner_alpha(vec2 size, vec2 position, float radius_tl, float radius_tr, float radius_bl, float radius_br);
-
-void main() {
- // Clipping
- float clip_corner_alpha = corner_alpha(
- clip_size - 1.0,
- clip_position + 0.5,
- clip_radius_top_left,
- clip_radius_top_right,
- clip_radius_bottom_left,
- clip_radius_bottom_right
- );
-
- gl_FragColor = v_color * clip_corner_alpha;
-}
diff --git a/scenefx/render/fx_renderer/gles2/shaders/quad_round.frag b/scenefx/render/fx_renderer/gles2/shaders/quad_round.frag
deleted file mode 100644
index 7efced1..0000000
--- a/scenefx/render/fx_renderer/gles2/shaders/quad_round.frag
+++ /dev/null
@@ -1,64 +0,0 @@
-#ifdef GL_FRAGMENT_PRECISION_HIGH
-precision highp float;
-#else
-precision mediump float;
-#endif
-
-varying vec4 v_color;
-varying vec2 v_texcoord;
-
-uniform vec2 size;
-uniform vec2 position;
-uniform float radius_top_left;
-uniform float radius_top_right;
-uniform float radius_bottom_left;
-uniform float radius_bottom_right;
-uniform float fade_inset;
-
-uniform vec2 clip_size;
-uniform vec2 clip_position;
-uniform float clip_radius_top_left;
-uniform float clip_radius_top_right;
-uniform float clip_radius_bottom_left;
-uniform float clip_radius_bottom_right;
-
-float corner_alpha(vec2 size, vec2 position, float round_tl, float round_tr, float round_bl, float round_br);
-float corner_dist(vec2 size, vec2 position, float round_tl, float round_tr, float round_bl, float round_br);
-
-void main() {
- float quad_corner_alpha = corner_alpha(
- size - 1.0,
- position + 0.5,
- radius_top_left,
- radius_top_right,
- radius_bottom_left,
- radius_bottom_right
- );
-
- // Clipping
- float clip_corner_alpha = corner_alpha(
- clip_size - 1.0,
- clip_position + 0.5,
- clip_radius_top_left,
- clip_radius_top_right,
- clip_radius_bottom_left,
- clip_radius_bottom_right
- );
-
- vec4 final_color = v_color;
- if (fade_inset > 0.0) {
- float d = corner_dist(
- size - 1.0,
- position + 0.5,
- radius_top_left,
- radius_top_right,
- radius_bottom_left,
- radius_bottom_right
- );
- float inside_dist = -d;
- float fade_factor = clamp(inside_dist / fade_inset, 0.0, 1.0);
- final_color *= fade_factor;
- }
-
- gl_FragColor = mix(final_color, vec4(0.0), quad_corner_alpha) * clip_corner_alpha;
-}
diff --git a/scenefx/render/fx_renderer/gles3/meson.build b/scenefx/render/fx_renderer/gles3/meson.build
deleted file mode 100644
index 40da7c3..0000000
--- a/scenefx/render/fx_renderer/gles3/meson.build
+++ /dev/null
@@ -1 +0,0 @@
-subdir('shaders')
\ No newline at end of file
diff --git a/scenefx/render/fx_renderer/gles3/shaders/blur1.frag b/scenefx/render/fx_renderer/gles3/shaders/blur1.frag
deleted file mode 100644
index 55504a2..0000000
--- a/scenefx/render/fx_renderer/gles3/shaders/blur1.frag
+++ /dev/null
@@ -1,23 +0,0 @@
-#version 300 es
-
-precision mediump float;
-
-in mediump vec2 v_texcoord;
-uniform sampler2D tex;
-
-uniform float radius;
-uniform vec2 halfpixel;
-
-out vec4 fragColor;
-
-void main() {
- vec2 uv = v_texcoord * 2.0;
-
- vec4 sum = texture2D(tex, uv) * 4.0;
- sum += texture2D(tex, uv - halfpixel.xy * radius);
- sum += texture2D(tex, uv + halfpixel.xy * radius);
- sum += texture2D(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius);
- sum += texture2D(tex, uv - vec2(halfpixel.x, -halfpixel.y) * radius);
-
- fragColor = sum / 8.0;
-}
diff --git a/scenefx/render/fx_renderer/gles3/shaders/blur2.frag b/scenefx/render/fx_renderer/gles3/shaders/blur2.frag
deleted file mode 100644
index 6fc90b6..0000000
--- a/scenefx/render/fx_renderer/gles3/shaders/blur2.frag
+++ /dev/null
@@ -1,27 +0,0 @@
-#version 300 es
-
-precision mediump float;
-
-in mediump vec2 v_texcoord;
-uniform sampler2D tex;
-
-uniform float radius;
-uniform vec2 halfpixel;
-
-out vec4 fragColor;
-
-void main() {
- vec2 uv = v_texcoord / 2.0;
-
- vec4 sum = texture2D(tex, uv + vec2(-halfpixel.x * 2.0, 0.0) * radius);
-
- sum += texture2D(tex, uv + vec2(-halfpixel.x, halfpixel.y) * radius) * 2.0;
- sum += texture2D(tex, uv + vec2(0.0, halfpixel.y * 2.0) * radius);
- sum += texture2D(tex, uv + vec2(halfpixel.x, halfpixel.y) * radius) * 2.0;
- sum += texture2D(tex, uv + vec2(halfpixel.x * 2.0, 0.0) * radius);
- sum += texture2D(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius) * 2.0;
- sum += texture2D(tex, uv + vec2(0.0, -halfpixel.y * 2.0) * radius);
- sum += texture2D(tex, uv + vec2(-halfpixel.x, -halfpixel.y) * radius) * 2.0;
-
- fragColor = sum / 12.0;
-}
diff --git a/scenefx/render/fx_renderer/gles3/shaders/blur_effects.frag b/scenefx/render/fx_renderer/gles3/shaders/blur_effects.frag
deleted file mode 100644
index 901f2a8..0000000
--- a/scenefx/render/fx_renderer/gles3/shaders/blur_effects.frag
+++ /dev/null
@@ -1,58 +0,0 @@
-#version 300 es
-
-precision highp float;
-
-in vec2 v_texcoord;
-uniform sampler2D tex;
-
-uniform float brightness;
-uniform float contrast;
-uniform float saturation;
-uniform float noise;
-
-out vec4 fragColor;
-
-mat4 brightnessMatrix() {
- float b = brightness - 1.0;
- return mat4(1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- b, b, b, 1);
-}
-
-mat4 contrastMatrix() {
- float t = (1.0 - contrast) / 2.0;
- return mat4(contrast, 0, 0, 0,
- 0, contrast, 0, 0,
- 0, 0, contrast, 0,
- t, t, t, 1);
-}
-
-mat4 saturationMatrix() {
- vec3 luminance = vec3(0.3086, 0.6094, 0.0820) * (1.0 - saturation);
- vec3 red = vec3(luminance.x);
- red.x += saturation;
- vec3 green = vec3(luminance.y);
- green.y += saturation;
- vec3 blue = vec3(luminance.z);
- blue.z += saturation;
- return mat4(red, 0,
- green, 0,
- blue, 0,
- 0, 0, 0, 1);
-}
-
-float noiseAmount(vec2 p) {
- vec3 p3 = fract(vec3(p.xyx) * 1689.1984);
- p3 += dot(p3, p3.yzx + 33.33);
- float hash = fract((p3.x + p3.y) * p3.z);
- return (mod(hash, 1.0) - 0.5) * noise;
-};
-
-void main() {
- vec4 color = texture2D(tex, v_texcoord);
- // Do *not* transpose the combined matrix when multiplying
- color = brightnessMatrix() * contrastMatrix() * saturationMatrix() * color;
- color.xyz += noiseAmount(v_texcoord);
- fragColor = color;
-}
diff --git a/scenefx/render/fx_renderer/gles3/shaders/box_shadow.frag b/scenefx/render/fx_renderer/gles3/shaders/box_shadow.frag
deleted file mode 100644
index 2f6e961..0000000
--- a/scenefx/render/fx_renderer/gles3/shaders/box_shadow.frag
+++ /dev/null
@@ -1,96 +0,0 @@
-// Writeup: https://madebyevan.com/shaders/fast-rounded-rectangle-shadows/
-
-#version 300 es
-
-precision highp float;
-
-in vec4 v_color;
-in vec2 v_texcoord;
-
-uniform vec2 position;
-uniform vec2 size;
-uniform float blur_sigma;
-uniform float corner_radius;
-uniform vec2 clip_position;
-uniform vec2 clip_size;
-uniform float clip_radius_top_left;
-uniform float clip_radius_top_right;
-uniform float clip_radius_bottom_left;
-uniform float clip_radius_bottom_right;
-
-out vec4 fragColor;
-
-float gaussian(float x, float sigma) {
- const float pi = 3.141592653589793;
- return exp(-(x * x) / (2.0 * sigma * sigma)) / (sqrt(2.0 * pi) * sigma);
-}
-
-// approximates the error function, needed for the gaussian integral
-vec2 erf(vec2 x) {
- vec2 s = sign(x), a = abs(x);
- x = 1.0 + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a;
- x *= x;
- return s - s / (x * x);
-}
-
-// return the blurred mask along the x dimension
-float roundedBoxShadowX(float x, float y, float sigma, float corner, vec2 halfSize) {
- float delta = min(halfSize.y - corner - abs(y), 0.0);
- float curved = halfSize.x - corner + sqrt(max(0.0, corner * corner - delta * delta));
- vec2 integral = 0.5 + 0.5 * erf((x + vec2(-curved, curved)) * (sqrt(0.5) / sigma));
- return integral.y - integral.x;
-}
-
-// return the mask for the shadow of a box from lower to upper
-float roundedBoxShadow(vec2 lower, vec2 upper, vec2 point, float sigma, float corner_radius) {
- // Center everything to make the math easier
- vec2 center = (lower + upper) * 0.5;
- vec2 halfSize = (upper - lower) * 0.5;
- point -= center;
-
- // The signal is only non-zero in a limited range, so don't waste samples
- float low = point.y - halfSize.y;
- float high = point.y + halfSize.y;
- float start = clamp(-3.0 * sigma, low, high);
- float end = clamp(3.0 * sigma, low, high);
-
- // Accumulate samples (we can get away with surprisingly few samples)
- float step = (end - start) / 4.0;
- float y = start + step * 0.5;
- float value = 0.0;
- for (int i = 0; i < 4; i++) {
- value += roundedBoxShadowX(point.x, point.y - y, sigma, corner_radius, halfSize) * gaussian(y, sigma) * step;
- y += step;
- }
-
- return value;
-}
-
-// per-pixel "random" number between 0 and 1
-float random() {
- return fract(sin(dot(vec2(12.9898, 78.233), gl_FragCoord.xy)) * 43758.5453);
-}
-
-float corner_alpha(vec2 size, vec2 position, float round_tl, float round_tr, float round_bl, float round_br);
-
-void main() {
- float shadow_alpha = v_color.a * roundedBoxShadow(
- position + blur_sigma,
- position + size - blur_sigma,
- gl_FragCoord.xy, blur_sigma * 0.5,
- corner_radius);
- // dither the alpha to break up color bands
- shadow_alpha += (random() - 0.5) / 128.0;
-
- // Clipping
- float clip_corner_alpha = corner_alpha(
- clip_size - 1.5,
- clip_position + 0.75,
- clip_radius_top_left,
- clip_radius_top_right,
- clip_radius_bottom_left,
- clip_radius_bottom_right
- );
-
- fragColor = vec4(v_color.rgb, shadow_alpha) * clip_corner_alpha;
-}
diff --git a/scenefx/render/fx_renderer/gles3/shaders/common.vert b/scenefx/render/fx_renderer/gles3/shaders/common.vert
deleted file mode 100644
index b420686..0000000
--- a/scenefx/render/fx_renderer/gles3/shaders/common.vert
+++ /dev/null
@@ -1,15 +0,0 @@
-#version 300 es
-
-uniform mat3 proj;
-uniform vec4 color;
-uniform mat3 tex_proj;
-in vec2 pos;
-out vec4 v_color;
-out vec2 v_texcoord;
-
-void main() {
- vec3 pos3 = vec3(pos, 1.0);
- gl_Position = vec4(pos3 * proj, 1.0);
- v_color = color;
- v_texcoord = (pos3 * tex_proj).xy;
-}
diff --git a/scenefx/render/fx_renderer/gles3/shaders/corner_alpha.frag b/scenefx/render/fx_renderer/gles3/shaders/corner_alpha.frag
deleted file mode 100644
index e560f6c..0000000
--- a/scenefx/render/fx_renderer/gles3/shaders/corner_alpha.frag
+++ /dev/null
@@ -1,35 +0,0 @@
-float get_dist(vec2 q, float radius) {
- return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - radius;
-}
-
-float corner_alpha(vec2 size, vec2 position, float radius_tl, float radius_tr, float radius_bl, float radius_br) {
- vec2 relative_pos = (gl_FragCoord.xy - position);
-
- vec2 top_left = abs(relative_pos - size) - size + radius_tl;
- vec2 top_right = abs(relative_pos - vec2(0, size.y)) - size + radius_tr;
- vec2 bottom_left = abs(relative_pos - vec2(size.x, 0)) - size + radius_bl;
- vec2 bottom_right = abs(relative_pos) - size + radius_br;
-
- float dist = max(
- max(get_dist(top_left, radius_tl), get_dist(top_right, radius_tr)),
- max(get_dist(bottom_left, radius_bl), get_dist(bottom_right, radius_br))
- );
-
- return smoothstep(0.0, 1.0, dist);
-}
-
-float corner_dist(vec2 size, vec2 position, float radius_tl, float radius_tr, float radius_bl, float radius_br) {
- vec2 relative_pos = (gl_FragCoord.xy - position);
-
- vec2 top_left = abs(relative_pos - size) - size + radius_tl;
- vec2 top_right = abs(relative_pos - vec2(0, size.y)) - size + radius_tr;
- vec2 bottom_left = abs(relative_pos - vec2(size.x, 0)) - size + radius_bl;
- vec2 bottom_right = abs(relative_pos) - size + radius_br;
-
- float dist = max(
- max(get_dist(top_left, radius_tl), get_dist(top_right, radius_tr)),
- max(get_dist(bottom_left, radius_bl), get_dist(bottom_right, radius_br))
- );
-
- return dist;
-}
diff --git a/scenefx/render/fx_renderer/gles3/shaders/embed.sh b/scenefx/render/fx_renderer/gles3/shaders/embed.sh
deleted file mode 100644
index 47f0789..0000000
--- a/scenefx/render/fx_renderer/gles3/shaders/embed.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/sh -eu
-
-var=${1:-data}
-hex="$(od -A n -t x1 -v)"
-
-echo "static const char $var[] = {"
-for byte in $hex; do
- echo " 0x$byte,"
-done
-echo " 0x00,"
-echo "};"
diff --git a/scenefx/render/fx_renderer/gles3/shaders/gradient.frag b/scenefx/render/fx_renderer/gles3/shaders/gradient.frag
deleted file mode 100644
index f5522ac..0000000
--- a/scenefx/render/fx_renderer/gles3/shaders/gradient.frag
+++ /dev/null
@@ -1,41 +0,0 @@
-vec4 gradient(vec4 colors[LEN], int count, vec2 size, vec2 grad_box, vec2 origin, float degree, bool linear, bool blend) {
- float step;
-
- vec2 normal = (gl_FragCoord.xy - grad_box)/size;
- vec2 uv = normal - origin;
-
- float rad = radians(degree);
-
- if (linear) {
- uv *= vec2(1.0)/vec2(abs(cos(rad)) + abs(sin(rad)));
-
- vec2 rotated = vec2(uv.x * cos(rad) - uv.y * sin(rad) + origin.x,
- uv.x * sin(rad) + uv.y * cos(rad) + origin.y);
-
- step = rotated.x;
- } else {
- vec2 uv = normal - origin;
- uv = vec2(uv.x * cos(rad) - uv.y * sin(rad),
- uv.x * sin(rad) + uv.y * cos(rad));
-
- uv = vec2(-atan(uv.y, uv.x)/3.14159265 * 0.5 + 0.5, 0.0);
- step = uv.x;
- }
-
- if (!blend) {
- float smooth_fac = 1.0/float(count);
- int ind = int(step/smooth_fac);
-
- return colors[ind];
- }
-
- float smooth_fac = 1.0/float(count - 1);
- int ind = int(step/smooth_fac);
- float at = float(ind)*smooth_fac;
-
- vec4 color = colors[ind];
- if(ind > 0) color = mix(colors[ind - 1], color, smoothstep(at - smooth_fac, at, step));
- if(ind <= count - 1) color = mix(color, colors[ind + 1], smoothstep(at, at + smooth_fac, step));
-
- return color;
-}
diff --git a/scenefx/render/fx_renderer/gles3/shaders/meson.build b/scenefx/render/fx_renderer/gles3/shaders/meson.build
deleted file mode 100644
index e7cd6fd..0000000
--- a/scenefx/render/fx_renderer/gles3/shaders/meson.build
+++ /dev/null
@@ -1,30 +0,0 @@
-embed = find_program('./embed.sh', native: true)
-
-shaders = [
- 'common.vert',
- 'gradient.frag',
- 'corner_alpha.frag',
- 'quad.frag',
- 'quad_grad.frag',
- 'quad_round.frag',
- 'quad_grad_round.frag',
- 'tex.frag',
- 'box_shadow.frag',
- 'blur1.frag',
- 'blur2.frag',
- 'blur_effects.frag',
-]
-
-foreach name : shaders
- output = name.underscorify() + '_gles3_src.h'
- var = name.underscorify() + '_gles3_src'
- scenefx_files += custom_target(
- output,
- command: [embed, var],
- input: name,
- output: output,
- feed: true,
- capture: true,
- )
-endforeach
-
diff --git a/scenefx/render/fx_renderer/gles3/shaders/quad.frag b/scenefx/render/fx_renderer/gles3/shaders/quad.frag
deleted file mode 100644
index 63eeac4..0000000
--- a/scenefx/render/fx_renderer/gles3/shaders/quad.frag
+++ /dev/null
@@ -1,31 +0,0 @@
-#version 300 es
-
-precision highp float;
-
-in vec4 v_color;
-in vec2 v_texcoord;
-
-uniform vec2 clip_size;
-uniform vec2 clip_position;
-uniform float clip_radius_top_left;
-uniform float clip_radius_top_right;
-uniform float clip_radius_bottom_left;
-uniform float clip_radius_bottom_right;
-
-out vec4 fragColor;
-
-float corner_alpha(vec2 size, vec2 position, float radius_tl, float radius_tr, float radius_bl, float radius_br);
-
-void main() {
- // Clipping
- float clip_corner_alpha = corner_alpha(
- clip_size - 1.0,
- clip_position + 0.5,
- clip_radius_top_left,
- clip_radius_top_right,
- clip_radius_bottom_left,
- clip_radius_bottom_right
- );
-
- fragColor = v_color * clip_corner_alpha;
-}
diff --git a/scenefx/render/fx_renderer/gles3/shaders/quad_grad.frag b/scenefx/render/fx_renderer/gles3/shaders/quad_grad.frag
deleted file mode 100644
index 25bd978..0000000
--- a/scenefx/render/fx_renderer/gles3/shaders/quad_grad.frag
+++ /dev/null
@@ -1,25 +0,0 @@
-#version 300 es
-
-#define LEN %d
-
-precision highp float;
-
-in vec4 v_color;
-in vec2 v_texcoord;
-
-uniform vec4 colors[LEN];
-uniform vec2 size;
-uniform float degree;
-uniform vec2 grad_box;
-uniform vec2 origin;
-uniform bool linear;
-uniform bool blend;
-uniform int count;
-
-out vec4 fragColor;
-
-vec4 gradient(vec4 colors[LEN], int count, vec2 size, vec2 grad_box, vec2 origin, float degree, bool linear, bool blend);
-
-void main(){
- fragColor = gradient(colors, count, size, grad_box, origin, degree, linear, blend);
-}
diff --git a/scenefx/render/fx_renderer/gles3/shaders/quad_grad_round.frag b/scenefx/render/fx_renderer/gles3/shaders/quad_grad_round.frag
deleted file mode 100644
index 0882cc8..0000000
--- a/scenefx/render/fx_renderer/gles3/shaders/quad_grad_round.frag
+++ /dev/null
@@ -1,46 +0,0 @@
-#version 300 es
-
-#define LEN %d
-
-precision highp float;
-
-in vec4 v_color;
-in vec2 v_texcoord;
-
-uniform vec2 size;
-uniform vec2 position;
-
-uniform float radius_top_left;
-uniform float radius_top_right;
-uniform float radius_bottom_left;
-uniform float radius_bottom_right;
-
-uniform vec4 colors[LEN];
-uniform vec2 grad_size;
-uniform float degree;
-uniform vec2 grad_box;
-uniform vec2 origin;
-uniform bool linear;
-uniform bool blend;
-uniform int count;
-
-out vec4 fragColor;
-
-vec4 gradient(vec4 colors[LEN], int count, vec2 size, vec2 grad_box, vec2 origin, float degree, bool linear, bool blend);
-
-float corner_alpha(vec2 size, vec2 position, float round_tl, float round_tr, float round_bl, float round_br);
-
-// TODO:
-void main() {
- float quad_corner_alpha = corner_alpha(
- size - 1.0,
- position + 0.5,
- radius_top_left,
- radius_top_right,
- radius_bottom_left,
- radius_bottom_right
- );
- float rect_alpha = v_color.a * quad_corner_alpha;
-
- fragColor = mix(vec4(0), gradient(colors, count, size, grad_box, origin, degree, linear, blend), rect_alpha);
-}
diff --git a/scenefx/render/fx_renderer/gles3/shaders/quad_round.frag b/scenefx/render/fx_renderer/gles3/shaders/quad_round.frag
deleted file mode 100644
index aabf7d3..0000000
--- a/scenefx/render/fx_renderer/gles3/shaders/quad_round.frag
+++ /dev/null
@@ -1,64 +0,0 @@
-#version 300 es
-
-precision highp float;
-
-in vec4 v_color;
-in vec2 v_texcoord;
-
-uniform vec2 size;
-uniform vec2 position;
-uniform float radius_top_left;
-uniform float radius_top_right;
-uniform float radius_bottom_left;
-uniform float radius_bottom_right;
-uniform float fade_inset;
-
-uniform vec2 clip_size;
-uniform vec2 clip_position;
-uniform float clip_radius_top_left;
-uniform float clip_radius_top_right;
-uniform float clip_radius_bottom_left;
-uniform float clip_radius_bottom_right;
-
-out vec4 fragColor;
-
-float corner_alpha(vec2 size, vec2 position, float round_tl, float round_tr, float round_bl, float round_br);
-float corner_dist(vec2 size, vec2 position, float round_tl, float round_tr, float round_bl, float round_br);
-
-void main() {
- float quad_corner_alpha = corner_alpha(
- size - 1.0,
- position + 0.5,
- radius_top_left,
- radius_top_right,
- radius_bottom_left,
- radius_bottom_right
- );
-
- // Clipping
- float clip_corner_alpha = corner_alpha(
- clip_size - 1.0,
- clip_position + 0.5,
- clip_radius_top_left,
- clip_radius_top_right,
- clip_radius_bottom_left,
- clip_radius_bottom_right
- );
-
- vec4 final_color = v_color;
- if (fade_inset > 0.0) {
- float d = corner_dist(
- size - 1.0,
- position + 0.5,
- radius_top_left,
- radius_top_right,
- radius_bottom_left,
- radius_bottom_right
- );
- float inside_dist = -d;
- float fade_factor = clamp(inside_dist / fade_inset, 0.0, 1.0);
- final_color *= fade_factor;
- }
-
- fragColor = mix(final_color, vec4(0.0), quad_corner_alpha) * clip_corner_alpha;
-}
diff --git a/scenefx/render/fx_renderer/gles3/shaders/tex.frag b/scenefx/render/fx_renderer/gles3/shaders/tex.frag
deleted file mode 100644
index f8f7edf..0000000
--- a/scenefx/render/fx_renderer/gles3/shaders/tex.frag
+++ /dev/null
@@ -1,65 +0,0 @@
-#version 300 es
-
-#define SOURCE %d
-
-#define SOURCE_TEXTURE_RGBA 1
-#define SOURCE_TEXTURE_RGBX 2
-#define SOURCE_TEXTURE_EXTERNAL 3
-
-#if !defined(SOURCE)
-#error "Missing shader preamble"
-#endif
-
-#if SOURCE == SOURCE_TEXTURE_EXTERNAL
-#extension GL_OES_EGL_image_external : require
-#endif
-
-precision highp float;
-
-in vec2 v_texcoord;
-
-#if SOURCE == SOURCE_TEXTURE_EXTERNAL
-uniform samplerExternalOES tex;
-#elif SOURCE == SOURCE_TEXTURE_RGBA || SOURCE == SOURCE_TEXTURE_RGBX
-uniform sampler2D tex;
-#endif
-
-uniform float alpha;
-
-uniform vec2 size;
-uniform vec2 position;
-uniform float radius_top_left;
-uniform float radius_top_right;
-uniform float radius_bottom_left;
-uniform float radius_bottom_right;
-
-uniform bool discard_transparent;
-
-out vec4 fragColor;
-
-vec4 sample_texture() {
-#if SOURCE == SOURCE_TEXTURE_RGBA || SOURCE == SOURCE_TEXTURE_EXTERNAL
- return texture2D(tex, v_texcoord);
-#elif SOURCE == SOURCE_TEXTURE_RGBX
- return vec4(texture2D(tex, v_texcoord).rgb, 1.0);
-#endif
-}
-
-float corner_alpha(vec2 size, vec2 position, float round_tl, float round_tr, float round_bl, float round_br);
-
-void main() {
- float corner_alpha = corner_alpha(
- size - 0.5,
- position + 0.25,
- radius_top_left,
- radius_top_right,
- radius_bottom_left,
- radius_bottom_right
- );
- fragColor = mix(sample_texture() * alpha, vec4(0.0), corner_alpha);
-
- if (discard_transparent && fragColor.a == 0.0) {
- discard;
- return;
- }
-}
diff --git a/scenefx/render/fx_renderer/meson.build b/scenefx/render/fx_renderer/meson.build
index 1d584bd..cb42b24 100644
--- a/scenefx/render/fx_renderer/meson.build
+++ b/scenefx/render/fx_renderer/meson.build
@@ -7,11 +7,12 @@ endif
scenefx_files += files(
'util.c',
+ 'tracy.c',
'shaders.c',
'pixel_format.c',
'fx_pass.c',
'fx_framebuffer.c',
- 'fx_effect_framebuffers.c',
+ 'fx_offscreen_buffers.c',
'fx_texture.c',
'fx_renderer.c',
)
@@ -35,12 +36,12 @@ if 'gles2' in renderers or 'auto' in renderers
gbm = dependency('gbm', required: 'gles2' in renderers)
glesv2 = dependency('glesv2', required: 'gles2' in renderers)
- glslang = find_program('glslang', 'glslangValidator', native: true, required: false, disabler: true)
-
if egl.found() and gbm.found() and glesv2.found()
scenefx_deps += [egl, gbm, glesv2]
+ features += {'gles2-renderer': true}
+ internal_features += {'egl': true}
endif
- subdir('gles3')
- subdir('gles2')
-endif
+ subdir('shaders')
+
+endif
diff --git a/scenefx/render/fx_renderer/pixel_format.c b/scenefx/render/fx_renderer/pixel_format.c
index 9fff2ea..4403ddc 100644
--- a/scenefx/render/fx_renderer/pixel_format.c
+++ b/scenefx/render/fx_renderer/pixel_format.c
@@ -71,6 +71,11 @@ static const struct fx_pixel_format formats[] = {
.gl_format = GL_RGBA,
.gl_type = GL_UNSIGNED_INT_2_10_10_10_REV_EXT,
},
+ {
+ .drm_format = DRM_FORMAT_BGR161616F,
+ .gl_format = GL_RGB,
+ .gl_type = GL_HALF_FLOAT_OES,
+ },
{
.drm_format = DRM_FORMAT_XBGR16161616F,
.gl_format = GL_RGBA,
@@ -81,6 +86,12 @@ static const struct fx_pixel_format formats[] = {
.gl_format = GL_RGBA,
.gl_type = GL_HALF_FLOAT_OES,
},
+ {
+ .drm_format = DRM_FORMAT_BGR161616,
+ .gl_internalformat = GL_RGB16_EXT,
+ .gl_format = GL_RGB,
+ .gl_type = GL_UNSIGNED_SHORT,
+ },
{
.drm_format = DRM_FORMAT_XBGR16161616,
.gl_internalformat = GL_RGBA16_EXT,
diff --git a/scenefx/render/fx_renderer/shaders.c b/scenefx/render/fx_renderer/shaders.c
index b740c5f..7727376 100644
--- a/scenefx/render/fx_renderer/shaders.c
+++ b/scenefx/render/fx_renderer/shaders.c
@@ -2,37 +2,24 @@
#include <stdio.h>
#include <stdlib.h>
#include <wlr/util/log.h>
+#include <scenefx/types/fx/clipped_region.h>
#include "render/fx_renderer/shaders.h"
// shaders
#include "GLES2/gl2.h"
-// gles3
-#include "common_vert_gles3_src.h"
-#include "gradient_frag_gles3_src.h"
-#include "corner_alpha_frag_gles3_src.h"
-#include "quad_frag_gles3_src.h"
-#include "quad_grad_frag_gles3_src.h"
-#include "quad_round_frag_gles3_src.h"
-#include "quad_grad_round_frag_gles3_src.h"
-#include "tex_frag_gles3_src.h"
-#include "box_shadow_frag_gles3_src.h"
-#include "blur1_frag_gles3_src.h"
-#include "blur2_frag_gles3_src.h"
-#include "blur_effects_frag_gles3_src.h"
-// gles2
-#include "common_vert_gles2_src.h"
-#include "gradient_frag_gles2_src.h"
-#include "corner_alpha_frag_gles2_src.h"
-#include "quad_frag_gles2_src.h"
-#include "quad_grad_frag_gles2_src.h"
-#include "quad_round_frag_gles2_src.h"
-#include "quad_grad_round_frag_gles2_src.h"
-#include "tex_frag_gles2_src.h"
-#include "box_shadow_frag_gles2_src.h"
-#include "blur1_frag_gles2_src.h"
-#include "blur2_frag_gles2_src.h"
-#include "blur_effects_frag_gles2_src.h"
+#include "common_vert_src.h"
+#include "gradient_frag_src.h"
+#include "corner_alpha_frag_src.h"
+#include "quad_frag_src.h"
+#include "quad_grad_frag_src.h"
+#include "quad_round_frag_src.h"
+#include "quad_grad_round_frag_src.h"
+#include "tex_frag_src.h"
+#include "box_shadow_frag_src.h"
+#include "blur1_frag_src.h"
+#include "blur2_frag_src.h"
+#include "blur_effects_frag_src.h"
GLuint compile_shader(GLuint type, const GLchar *src) {
GLuint shader = glCreateShader(type);
@@ -50,9 +37,8 @@ GLuint compile_shader(GLuint type, const GLchar *src) {
return shader;
}
-GLuint link_program(const GLchar *frag_src, GLint client_version) {
- const GLchar *vert_src = client_version > 2 ? common_vert_gles3_src : common_vert_gles2_src;
- GLuint vert = compile_shader(GL_VERTEX_SHADER, vert_src);
+GLuint link_program(const GLchar *frag_src) {
+ GLuint vert = compile_shader(GL_VERTEX_SHADER, common_vert_src);
if (!vert) {
goto error;
}
@@ -115,20 +101,25 @@ void load_gl_proc(void *proc_ptr, const char *name) {
*(void **)proc_ptr = proc;
}
+void uniform_corner_radii_set(const struct shader_corner_radii *uniform,
+ const struct fx_corner_fradii *corners) {
+ glUniform1f(uniform->top_left, corners->top_left);
+ glUniform1f(uniform->top_right, corners->top_right);
+ glUniform1f(uniform->bottom_left, corners->bottom_left);
+ glUniform1f(uniform->bottom_right, corners->bottom_right);
+}
// Shaders
-bool link_quad_program(struct quad_shader *shader, GLint client_version) {
- GLchar quad_src[4096];
- if (client_version > 2) {
- snprintf(quad_src, sizeof(quad_src), "%s\n%s", quad_frag_gles3_src,
- corner_alpha_frag_gles3_src);
- } else {
- snprintf(quad_src, sizeof(quad_src), "%s\n%s", quad_frag_gles2_src,
- corner_alpha_frag_gles2_src);
- }
+bool link_quad_program(struct quad_shader *shader, bool clip) {
+ GLchar quad_src_part[2048];
+ GLchar quad_src[8192];
+ snprintf(quad_src_part, sizeof(quad_src_part),
+ quad_frag_src, clip);
+ snprintf(quad_src, sizeof(quad_src),
+ "%s\n%s\n", quad_src_part, clip ? corner_alpha_frag_src : "");
GLuint prog;
- shader->program = prog = link_program(quad_src, client_version);
+ shader->program = prog = link_program(quad_src);
if (!shader->program) {
return false;
}
@@ -136,33 +127,30 @@ bool link_quad_program(struct quad_shader *shader, GLint client_version) {
shader->proj = glGetUniformLocation(prog, "proj");
shader->color = glGetUniformLocation(prog, "color");
shader->pos_attrib = glGetAttribLocation(prog, "pos");
- shader->clip_size = glGetUniformLocation(prog, "clip_size");
- shader->clip_position = glGetUniformLocation(prog, "clip_position");
- shader->clip_radius_top_left = glGetUniformLocation(prog, "clip_radius_top_left");
- shader->clip_radius_top_right = glGetUniformLocation(prog, "clip_radius_top_right");
- shader->clip_radius_bottom_left = glGetUniformLocation(prog, "clip_radius_bottom_left");
- shader->clip_radius_bottom_right = glGetUniformLocation(prog, "clip_radius_bottom_right");
+
+ if (!clip) {
+ return true;
+ }
+ shader->effects.clip_size = glGetUniformLocation(prog, "clip_size");
+ shader->effects.clip_position = glGetUniformLocation(prog, "clip_position");
+ shader->effects.clip_radius.top_left = glGetUniformLocation(prog, "clip_radius_top_left");
+ shader->effects.clip_radius.top_right = glGetUniformLocation(prog, "clip_radius_top_right");
+ shader->effects.clip_radius.bottom_left = glGetUniformLocation(prog, "clip_radius_bottom_left");
+ shader->effects.clip_radius.bottom_right = glGetUniformLocation(prog, "clip_radius_bottom_right");
return true;
}
-bool link_quad_grad_program(struct quad_grad_shader *shader, GLint client_version, int max_len) {
+bool link_quad_grad_program(struct quad_grad_shader *shader, int max_len) {
GLchar quad_src_part[2048];
GLchar quad_src[4096];
- if (client_version > 2) {
- snprintf(quad_src_part, sizeof(quad_src_part),
- quad_grad_frag_gles3_src, max_len);
- snprintf(quad_src, sizeof(quad_src),
- "%s\n%s", quad_src_part, gradient_frag_gles3_src);
- } else {
- snprintf(quad_src_part, sizeof(quad_src_part),
- quad_grad_frag_gles2_src, max_len);
- snprintf(quad_src, sizeof(quad_src),
- "%s\n%s", quad_src_part, gradient_frag_gles2_src);
- }
+ snprintf(quad_src_part, sizeof(quad_src_part),
+ quad_grad_frag_src, max_len);
+ snprintf(quad_src, sizeof(quad_src),
+ "%s\n%s", quad_src_part, gradient_frag_src);
GLuint prog;
- shader->program = prog = link_program(quad_src, client_version);
+ shader->program = prog = link_program(quad_src);
if (!shader->program) {
return false;
}
@@ -183,18 +171,13 @@ bool link_quad_grad_program(struct quad_grad_shader *shader, GLint client_versio
return true;
}
-bool link_quad_round_program(struct quad_round_shader *shader, GLint client_version) {
+bool link_quad_round_program(struct quad_round_shader *shader) {
GLchar quad_src[8192];
- if (client_version > 2) {
- snprintf(quad_src, sizeof(quad_src), "%s\n%s", quad_round_frag_gles3_src,
- corner_alpha_frag_gles3_src);
- } else {
- snprintf(quad_src, sizeof(quad_src), "%s\n%s", quad_round_frag_gles2_src,
- corner_alpha_frag_gles2_src);
- }
+ snprintf(quad_src, sizeof(quad_src), "%s\n%s", quad_round_frag_src,
+ corner_alpha_frag_src);
GLuint prog;
- shader->program = prog = link_program(quad_src, client_version);
+ shader->program = prog = link_program(quad_src);
if (!shader->program) {
return false;
}
@@ -204,39 +187,32 @@ bool link_quad_round_program(struct quad_round_shader *shader, GLint client_vers
shader->pos_attrib = glGetAttribLocation(prog, "pos");
shader->size = glGetUniformLocation(prog, "size");
shader->position = glGetUniformLocation(prog, "position");
- shader->radius_top_left = glGetUniformLocation(prog, "radius_top_left");
- shader->radius_top_right = glGetUniformLocation(prog, "radius_top_right");
- shader->radius_bottom_left = glGetUniformLocation(prog, "radius_bottom_left");
- shader->radius_bottom_right = glGetUniformLocation(prog, "radius_bottom_right");
+ shader->radius.top_left = glGetUniformLocation(prog, "radius_top_left");
+ shader->radius.top_right = glGetUniformLocation(prog, "radius_top_right");
+ shader->radius.bottom_left = glGetUniformLocation(prog, "radius_bottom_left");
+ shader->radius.bottom_right = glGetUniformLocation(prog, "radius_bottom_right");
shader->clip_size = glGetUniformLocation(prog, "clip_size");
shader->clip_position = glGetUniformLocation(prog, "clip_position");
- shader->clip_radius_top_left = glGetUniformLocation(prog, "clip_radius_top_left");
- shader->clip_radius_top_right = glGetUniformLocation(prog, "clip_radius_top_right");
- shader->clip_radius_bottom_left = glGetUniformLocation(prog, "clip_radius_bottom_left");
- shader->clip_radius_bottom_right = glGetUniformLocation(prog, "clip_radius_bottom_right");
+ shader->clip_radius.top_left = glGetUniformLocation(prog, "clip_radius_top_left");
+ shader->clip_radius.top_right = glGetUniformLocation(prog, "clip_radius_top_right");
+ shader->clip_radius.bottom_left = glGetUniformLocation(prog, "clip_radius_bottom_left");
+ shader->clip_radius.bottom_right = glGetUniformLocation(prog, "clip_radius_bottom_right");
shader->fade_inset = glGetUniformLocation(prog, "fade_inset");
return true;
}
-bool link_quad_grad_round_program(struct quad_grad_round_shader *shader, GLint client_version, int max_len) {
+bool link_quad_grad_round_program(struct quad_grad_round_shader *shader, int max_len) {
GLchar quad_src_part[2048];
GLchar quad_src[8192];
- if (client_version > 2) {
- snprintf(quad_src_part, sizeof(quad_src_part),
- quad_grad_round_frag_gles3_src, max_len);
- snprintf(quad_src, sizeof(quad_src),
- "%s\n%s\n%s", quad_src_part, gradient_frag_gles3_src, corner_alpha_frag_gles3_src);
- } else {
- snprintf(quad_src_part, sizeof(quad_src_part),
- quad_grad_round_frag_gles2_src, max_len);
- snprintf(quad_src, sizeof(quad_src),
- "%s\n%s\n%s", quad_src_part, gradient_frag_gles2_src, corner_alpha_frag_gles2_src);
- }
+ snprintf(quad_src_part, sizeof(quad_src_part),
+ quad_grad_round_frag_src, max_len);
+ snprintf(quad_src, sizeof(quad_src),
+ "%s\n%s\n%s", quad_src_part, gradient_frag_src, corner_alpha_frag_src);
GLuint prog;
- shader->program = prog = link_program(quad_src, client_version);
+ shader->program = prog = link_program(quad_src);
if (!shader->program) {
return false;
}
@@ -246,10 +222,10 @@ bool link_quad_grad_round_program(struct quad_grad_round_shader *shader, GLint c
shader->pos_attrib = glGetAttribLocation(prog, "pos");
shader->size = glGetUniformLocation(prog, "size");
shader->position = glGetUniformLocation(prog, "position");
- shader->radius_top_left = glGetUniformLocation(prog, "radius_top_left");
- shader->radius_top_right = glGetUniformLocation(prog, "radius_top_right");
- shader->radius_bottom_left = glGetUniformLocation(prog, "radius_bottom_left");
- shader->radius_bottom_right = glGetUniformLocation(prog, "radius_bottom_right");
+ shader->radius.top_left = glGetUniformLocation(prog, "radius_top_left");
+ shader->radius.top_right = glGetUniformLocation(prog, "radius_top_right");
+ shader->radius.bottom_left = glGetUniformLocation(prog, "radius_bottom_left");
+ shader->radius.bottom_right = glGetUniformLocation(prog, "radius_bottom_right");
shader->grad_size = glGetUniformLocation(prog, "grad_size");
shader->colors = glGetUniformLocation(prog, "colors");
@@ -265,23 +241,17 @@ bool link_quad_grad_round_program(struct quad_grad_round_shader *shader, GLint c
return true;
}
-bool link_tex_program(struct tex_shader *shader, GLint client_version, enum fx_tex_shader_source source) {
- GLchar frag_src_part[2048];
- GLchar frag_src[4096];
- if (client_version > 2) {
- snprintf(frag_src_part, sizeof(frag_src_part),
- tex_frag_gles3_src, source);
- snprintf(frag_src, sizeof(frag_src),
- "%s\n%s\n", frag_src_part, corner_alpha_frag_gles3_src);
- } else {
- snprintf(frag_src_part, sizeof(frag_src_part),
- tex_frag_gles2_src, source);
- snprintf(frag_src, sizeof(frag_src),
- "%s\n%s\n", frag_src_part, corner_alpha_frag_gles2_src);
- }
+bool link_tex_program(struct tex_shader *shader, enum fx_tex_shader_source source,
+ bool effects) {
+ GLchar frag_src_part[4096];
+ GLchar frag_src[8192];
+ snprintf(frag_src_part, sizeof(frag_src_part),
+ tex_frag_src, source, effects);
+ snprintf(frag_src, sizeof(frag_src),
+ "%s\n%s\n", frag_src_part, effects ? corner_alpha_frag_src : "");
GLuint prog;
- shader->program = prog = link_program(frag_src, client_version);
+ shader->program = prog = link_program(frag_src);
if (!shader->program) {
return false;
}
@@ -291,29 +261,36 @@ bool link_tex_program(struct tex_shader *shader, GLint client_version, enum fx_t
shader->alpha = glGetUniformLocation(prog, "alpha");
shader->pos_attrib = glGetAttribLocation(prog, "pos");
shader->tex_proj = glGetUniformLocation(prog, "tex_proj");
- shader->size = glGetUniformLocation(prog, "size");
- shader->position = glGetUniformLocation(prog, "position");
- shader->radius_top_left = glGetUniformLocation(prog, "radius_top_left");
- shader->radius_top_right = glGetUniformLocation(prog, "radius_top_right");
- shader->radius_bottom_left = glGetUniformLocation(prog, "radius_bottom_left");
- shader->radius_bottom_right = glGetUniformLocation(prog, "radius_bottom_right");
+
shader->discard_transparent = glGetUniformLocation(prog, "discard_transparent");
+ if (!effects) {
+ return true;
+ }
+ shader->effects.size = glGetUniformLocation(prog, "size");
+ shader->effects.position = glGetUniformLocation(prog, "position");
+ shader->effects.radius.top_left = glGetUniformLocation(prog, "radius_top_left");
+ shader->effects.radius.top_right = glGetUniformLocation(prog, "radius_top_right");
+ shader->effects.radius.bottom_left = glGetUniformLocation(prog, "radius_bottom_left");
+ shader->effects.radius.bottom_right = glGetUniformLocation(prog, "radius_bottom_right");
+
+ shader->effects.clip_size = glGetUniformLocation(prog, "clip_size");
+ shader->effects.clip_position = glGetUniformLocation(prog, "clip_position");
+ shader->effects.clip_radius.top_left = glGetUniformLocation(prog, "clip_radius_top_left");
+ shader->effects.clip_radius.top_right = glGetUniformLocation(prog, "clip_radius_top_right");
+ shader->effects.clip_radius.bottom_left = glGetUniformLocation(prog, "clip_radius_bottom_left");
+ shader->effects.clip_radius.bottom_right = glGetUniformLocation(prog, "clip_radius_bottom_right");
+
return true;
}
-bool link_box_shadow_program(struct box_shadow_shader *shader, GLint client_version) {
+bool link_box_shadow_program(struct box_shadow_shader *shader) {
GLchar shadow_src[8192];
- if (client_version > 2) {
- snprintf(shadow_src, sizeof(shadow_src), "%s\n%s", box_shadow_frag_gles3_src,
- corner_alpha_frag_gles3_src);
- } else {
- snprintf(shadow_src, sizeof(shadow_src), "%s\n%s", box_shadow_frag_gles2_src,
- corner_alpha_frag_gles2_src);
- }
+ snprintf(shadow_src, sizeof(shadow_src), "%s\n%s", box_shadow_frag_src,
+ corner_alpha_frag_src);
GLuint prog;
- shader->program = prog = link_program(shadow_src, client_version);
+ shader->program = prog = link_program(shadow_src);
if (!shader->program) {
return false;
}
@@ -326,18 +303,17 @@ bool link_box_shadow_program(struct box_shadow_shader *shader, GLint client_vers
shader->corner_radius = glGetUniformLocation(prog, "corner_radius");
shader->clip_position = glGetUniformLocation(prog, "clip_position");
shader->clip_size = glGetUniformLocation(prog, "clip_size");
- shader->clip_radius_top_left = glGetUniformLocation(prog, "clip_radius_top_left");
- shader->clip_radius_top_right = glGetUniformLocation(prog, "clip_radius_top_right");
- shader->clip_radius_bottom_left = glGetUniformLocation(prog, "clip_radius_bottom_left");
- shader->clip_radius_bottom_right = glGetUniformLocation(prog, "clip_radius_bottom_right");
+ shader->clip_radius.top_left = glGetUniformLocation(prog, "clip_radius_top_left");
+ shader->clip_radius.top_right = glGetUniformLocation(prog, "clip_radius_top_right");
+ shader->clip_radius.bottom_left = glGetUniformLocation(prog, "clip_radius_bottom_left");
+ shader->clip_radius.bottom_right = glGetUniformLocation(prog, "clip_radius_bottom_right");
return true;
}
-bool link_blur1_program(struct blur_shader *shader, GLint client_version) {
+bool link_blur1_program(struct blur_shader *shader) {
GLuint prog;
- shader->program = prog = client_version > 2 ? link_program(blur1_frag_gles3_src, client_version)
- : link_program(blur1_frag_gles2_src, client_version);
+ shader->program = prog = link_program(blur1_frag_src);
if (!shader->program) {
return false;
}
@@ -351,10 +327,9 @@ bool link_blur1_program(struct blur_shader *shader, GLint client_version) {
return true;
}
-bool link_blur2_program(struct blur_shader *shader, GLint client_version) {
+bool link_blur2_program(struct blur_shader *shader) {
GLuint prog;
- shader->program = prog = client_version > 2 ? link_program(blur2_frag_gles3_src, client_version)
- : link_program(blur2_frag_gles2_src, client_version);
+ shader->program = prog = link_program(blur2_frag_src);
if (!shader->program) {
return false;
}
@@ -368,10 +343,9 @@ bool link_blur2_program(struct blur_shader *shader, GLint client_version) {
return true;
}
-bool link_blur_effects_program(struct blur_effects_shader *shader, GLint client_version) {
+bool link_blur_effects_program(struct blur_effects_shader *shader) {
GLuint prog;
- shader->program = prog = client_version > 2 ? link_program(blur_effects_frag_gles3_src, client_version)
- : link_program(blur_effects_frag_gles2_src, client_version);
+ shader->program = prog = link_program(blur_effects_frag_src);
if (!shader->program) {
return false;
}
diff --git a/scenefx/render/fx_renderer/gles2/shaders/blur1.frag b/scenefx/render/fx_renderer/shaders/blur1.frag
similarity index 100%
rename from scenefx/render/fx_renderer/gles2/shaders/blur1.frag
rename to scenefx/render/fx_renderer/shaders/blur1.frag
diff --git a/scenefx/render/fx_renderer/gles2/shaders/blur2.frag b/scenefx/render/fx_renderer/shaders/blur2.frag
similarity index 100%
rename from scenefx/render/fx_renderer/gles2/shaders/blur2.frag
rename to scenefx/render/fx_renderer/shaders/blur2.frag
diff --git a/scenefx/render/fx_renderer/gles2/shaders/blur_effects.frag b/scenefx/render/fx_renderer/shaders/blur_effects.frag
similarity index 99%
rename from scenefx/render/fx_renderer/gles2/shaders/blur_effects.frag
rename to scenefx/render/fx_renderer/shaders/blur_effects.frag
index 6f7411f..c3d3246 100644
--- a/scenefx/render/fx_renderer/gles2/shaders/blur_effects.frag
+++ b/scenefx/render/fx_renderer/shaders/blur_effects.frag
@@ -47,7 +47,7 @@ float noiseAmount(vec2 p) {
p3 += dot(p3, p3.yzx + 33.33);
float hash = fract((p3.x + p3.y) * p3.z);
return (mod(hash, 1.0) - 0.5) * noise;
-};
+}
void main() {
vec4 color = texture2D(tex, v_texcoord);
diff --git a/scenefx/render/fx_renderer/gles2/shaders/box_shadow.frag b/scenefx/render/fx_renderer/shaders/box_shadow.frag
similarity index 88%
rename from scenefx/render/fx_renderer/gles2/shaders/box_shadow.frag
rename to scenefx/render/fx_renderer/shaders/box_shadow.frag
index 6a40109..1cab7b5 100644
--- a/scenefx/render/fx_renderer/gles2/shaders/box_shadow.frag
+++ b/scenefx/render/fx_renderer/shaders/box_shadow.frag
@@ -66,12 +66,8 @@ float roundedBoxShadow(vec2 lower, vec2 upper, vec2 point, float sigma, float co
return value;
}
-// per-pixel "random" number between 0 and 1
-float random() {
- return fract(sin(dot(vec2(12.9898, 78.233), gl_FragCoord.xy)) * 43758.5453);
-}
-
-float corner_alpha(vec2 size, vec2 position, float round_tl, float round_tr, float round_bl, float round_br);
+float corner_alpha(vec2 size, vec2 position, bool is_cutout,
+ float radius_tl, float radius_tr, float radius_bl, float radius_br);
void main() {
float shadow_alpha = v_color.a * roundedBoxShadow(
@@ -79,13 +75,12 @@ void main() {
position + size - blur_sigma,
gl_FragCoord.xy, blur_sigma * 0.5,
corner_radius);
- // dither the alpha to break up color bands
- shadow_alpha += (random() - 0.5) / 128.0;
// Clipping
float clip_corner_alpha = corner_alpha(
clip_size - 1.5,
clip_position + 0.75,
+ true,
clip_radius_top_left,
clip_radius_top_right,
clip_radius_bottom_left,
diff --git a/scenefx/render/fx_renderer/gles2/shaders/common.vert b/scenefx/render/fx_renderer/shaders/common.vert
similarity index 100%
rename from scenefx/render/fx_renderer/gles2/shaders/common.vert
rename to scenefx/render/fx_renderer/shaders/common.vert
diff --git a/scenefx/render/fx_renderer/shaders/corner_alpha.frag b/scenefx/render/fx_renderer/shaders/corner_alpha.frag
new file mode 100644
index 0000000..42c1413
--- /dev/null
+++ b/scenefx/render/fx_renderer/shaders/corner_alpha.frag
@@ -0,0 +1,74 @@
+float get_dist(vec2 q, float radius) {
+ return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - radius;
+}
+
+// Note: Returns 0.0 if outside, 1.0 if inside the bounds. The is_cutout parameter
+// reverses this, 0.0 inside cutout and 1.0 for outside cutout.
+float corner_alpha(vec2 size, vec2 position, bool is_cutout,
+ float radius_tl, float radius_tr, float radius_bl, float radius_br) {
+ if (radius_tl <= 0.0
+ && radius_tr <= 0.0
+ && radius_bl <= 0.0
+ && radius_br <= 0.0) {
+ return 1.0;
+ }
+
+ vec2 relative_pos = (gl_FragCoord.xy - position);
+
+ if (relative_pos.x < 0.0 || relative_pos.y < 0.0
+ || relative_pos.x > size.x || relative_pos.y > size.y) {
+ if (is_cutout) {
+ return 1.0;
+ }
+ discard;
+ }
+
+ bool is_top_left = radius_tl > 0.0
+ && relative_pos.x <= radius_tl
+ && relative_pos.y <= radius_tl;
+ bool is_top_right = radius_tr > 0.0
+ && relative_pos.x >= size.x - radius_tr
+ && relative_pos.y <= radius_tr;
+ bool is_bottom_left = radius_bl > 0.0
+ && relative_pos.x <= radius_bl
+ && relative_pos.y >= size.y - radius_bl;
+ bool is_bottom_right = radius_br > 0.0
+ && relative_pos.x >= size.x - radius_br
+ && relative_pos.y >= size.y - radius_br;
+ if (!is_top_left && !is_top_right && !is_bottom_left && !is_bottom_right) {
+ if (is_cutout) {
+ discard;
+ }
+ return 1.0;
+ }
+
+ vec2 top_left = abs(relative_pos - size) - size + radius_tl;
+ vec2 top_right = abs(relative_pos - vec2(0, size.y)) - size + radius_tr;
+ vec2 bottom_left = abs(relative_pos - vec2(size.x, 0)) - size + radius_bl;
+ vec2 bottom_right = abs(relative_pos) - size + radius_br;
+
+ float dist = max(
+ max(get_dist(top_left, radius_tl), get_dist(top_right, radius_tr)),
+ max(get_dist(bottom_left, radius_bl), get_dist(bottom_right, radius_br))
+ );
+
+ float result = smoothstep(0.0, 1.0, dist);
+ return is_cutout ? result : 1.0 - result;
+}
+
+float corner_dist(vec2 size, vec2 position,
+ float radius_tl, float radius_tr, float radius_bl, float radius_br) {
+ vec2 relative_pos = (gl_FragCoord.xy - position);
+
+ vec2 top_left = abs(relative_pos - size) - size + radius_tl;
+ vec2 top_right = abs(relative_pos - vec2(0, size.y)) - size + radius_tr;
+ vec2 bottom_left = abs(relative_pos - vec2(size.x, 0)) - size + radius_bl;
+ vec2 bottom_right = abs(relative_pos) - size + radius_br;
+
+ float dist = max(
+ max(get_dist(top_left, radius_tl), get_dist(top_right, radius_tr)),
+ max(get_dist(bottom_left, radius_bl), get_dist(bottom_right, radius_br))
+ );
+
+ return dist;
+}
diff --git a/scenefx/render/fx_renderer/gles2/shaders/embed.sh b/scenefx/render/fx_renderer/shaders/embed.sh
similarity index 100%
rename from scenefx/render/fx_renderer/gles2/shaders/embed.sh
rename to scenefx/render/fx_renderer/shaders/embed.sh
diff --git a/scenefx/render/fx_renderer/gles2/shaders/gradient.frag b/scenefx/render/fx_renderer/shaders/gradient.frag
similarity index 100%
rename from scenefx/render/fx_renderer/gles2/shaders/gradient.frag
rename to scenefx/render/fx_renderer/shaders/gradient.frag
diff --git a/scenefx/render/fx_renderer/shaders/meson.build b/scenefx/render/fx_renderer/shaders/meson.build
new file mode 100644
index 0000000..1bafd4f
--- /dev/null
+++ b/scenefx/render/fx_renderer/shaders/meson.build
@@ -0,0 +1,31 @@
+embed = find_program('./embed.sh', native: true)
+
+# TODO: Validate each fragment shader just like upstream
+shaders = [
+ 'common.vert',
+ 'gradient.frag',
+ 'corner_alpha.frag',
+ 'quad.frag',
+ 'quad_grad.frag',
+ 'quad_round.frag',
+ 'quad_grad_round.frag',
+ 'tex.frag',
+ 'box_shadow.frag',
+ 'blur1.frag',
+ 'blur2.frag',
+ 'blur_effects.frag',
+]
+
+foreach name : shaders
+ output = name.underscorify() + '_src.h'
+ var = name.underscorify() + '_src'
+ scenefx_files += custom_target(
+ output,
+ command: [embed, var],
+ input: name,
+ output: output,
+ feed: true,
+ capture: true,
+ )
+endforeach
+
diff --git a/scenefx/render/fx_renderer/shaders/quad.frag b/scenefx/render/fx_renderer/shaders/quad.frag
new file mode 100644
index 0000000..93c9fe6
--- /dev/null
+++ b/scenefx/render/fx_renderer/shaders/quad.frag
@@ -0,0 +1,45 @@
+#define EFFECTS %d
+
+#if !defined(EFFECTS)
+#error "Missing shader preamble"
+#endif
+
+#ifdef GL_FRAGMENT_PRECISION_HIGH
+precision highp float;
+#else
+precision mediump float;
+#endif
+
+varying vec4 v_color;
+varying vec2 v_texcoord;
+
+#if EFFECTS
+uniform vec2 clip_size;
+uniform vec2 clip_position;
+uniform float clip_radius_top_left;
+uniform float clip_radius_top_right;
+uniform float clip_radius_bottom_left;
+uniform float clip_radius_bottom_right;
+#endif
+
+float corner_alpha(vec2 size, vec2 position, bool is_cutout,
+ float radius_tl, float radius_tr, float radius_bl, float radius_br);
+
+void main() {
+#if EFFECTS
+ // Clipping
+ float clip_corner_alpha = corner_alpha(
+ clip_size - 1.0,
+ clip_position + 0.5,
+ true,
+ clip_radius_top_left,
+ clip_radius_top_right,
+ clip_radius_bottom_left,
+ clip_radius_bottom_right
+ );
+
+ gl_FragColor = v_color * clip_corner_alpha;
+#else
+ gl_FragColor = v_color;
+#endif
+}
diff --git a/scenefx/render/fx_renderer/gles2/shaders/quad_grad.frag b/scenefx/render/fx_renderer/shaders/quad_grad.frag
similarity index 100%
rename from scenefx/render/fx_renderer/gles2/shaders/quad_grad.frag
rename to scenefx/render/fx_renderer/shaders/quad_grad.frag
diff --git a/scenefx/render/fx_renderer/gles2/shaders/quad_grad_round.frag b/scenefx/render/fx_renderer/shaders/quad_grad_round.frag
similarity index 57%
rename from scenefx/render/fx_renderer/gles2/shaders/quad_grad_round.frag
rename to scenefx/render/fx_renderer/shaders/quad_grad_round.frag
index bbf7400..87d48d7 100644
--- a/scenefx/render/fx_renderer/gles2/shaders/quad_grad_round.frag
+++ b/scenefx/render/fx_renderer/shaders/quad_grad_round.frag
@@ -28,19 +28,21 @@ uniform int count;
vec4 gradient(vec4 colors[LEN], int count, vec2 size, vec2 grad_box, vec2 origin, float degree, bool linear, bool blend);
-float corner_alpha(vec2 size, vec2 position, float round_tl, float round_tr, float round_bl, float round_br);
+float corner_alpha(vec2 size, vec2 position, bool is_cutout,
+ float radius_tl, float radius_tr, float radius_bl, float radius_br);
// TODO:
void main() {
- float quad_corner_alpha = corner_alpha(
- size - 1.0,
- position + 0.5,
- radius_top_left,
- radius_top_right,
- radius_bottom_left,
- radius_bottom_right
- );
- float rect_alpha = v_color.a * quad_corner_alpha;
-
- gl_FragColor = mix(vec4(0), gradient(colors, count, size, grad_box, origin, degree, linear, blend), rect_alpha);
+ float quad_corner_alpha = corner_alpha(
+ size - 1.0,
+ position + 0.5,
+ false,
+ radius_top_left,
+ radius_top_right,
+ radius_bottom_left,
+ radius_bottom_right
+ );
+ float rect_alpha = v_color.a * quad_corner_alpha;
+
+ gl_FragColor = mix(vec4(0.0), gradient(colors, count, size, grad_box, origin, degree, linear, blend), rect_alpha);
}
diff --git a/scenefx/render/fx_renderer/shaders/quad_round.frag b/scenefx/render/fx_renderer/shaders/quad_round.frag
new file mode 100644
index 0000000..3e80e26
--- /dev/null
+++ b/scenefx/render/fx_renderer/shaders/quad_round.frag
@@ -0,0 +1,72 @@
+#ifdef GL_FRAGMENT_PRECISION_HIGH
+precision highp float;
+#else
+precision mediump float;
+#endif
+
+varying vec4 v_color;
+varying vec2 v_texcoord;
+
+uniform vec2 size;
+uniform vec2 position;
+uniform float radius_top_left;
+uniform float radius_top_right;
+uniform float radius_bottom_left;
+uniform float radius_bottom_right;
+uniform float fade_inset;
+
+uniform vec2 clip_size;
+uniform vec2 clip_position;
+uniform float clip_radius_top_left;
+uniform float clip_radius_top_right;
+uniform float clip_radius_bottom_left;
+uniform float clip_radius_bottom_right;
+
+float corner_alpha(vec2 size, vec2 position, bool is_cutout,
+ float radius_tl, float radius_tr, float radius_bl, float radius_br);
+float corner_dist(vec2 size, vec2 position,
+ float radius_tl, float radius_tr, float radius_bl, float radius_br);
+
+void main() {
+ float quad_corner_alpha = corner_alpha(
+ size - 1.0,
+ position + 0.5,
+ false,
+ radius_top_left,
+ radius_top_right,
+ radius_bottom_left,
+ radius_bottom_right
+ );
+
+ // Clipping
+ float clip_corner_alpha = corner_alpha(
+ clip_size - 1.0,
+ clip_position + 0.5,
+ true,
+ clip_radius_top_left,
+ clip_radius_top_right,
+ clip_radius_bottom_left,
+ clip_radius_bottom_right
+ );
+
+ vec4 final_color = v_color;
+ if (fade_inset > 0.0) {
+ float r_tl = radius_top_left > 0.0 ? radius_top_left + fade_inset : 0.0;
+ float r_tr = radius_top_right > 0.0 ? radius_top_right + fade_inset : 0.0;
+ float r_bl = radius_bottom_left > 0.0 ? radius_bottom_left + fade_inset : 0.0;
+ float r_br = radius_bottom_right > 0.0 ? radius_bottom_right + fade_inset : 0.0;
+ float d = corner_dist(
+ size - 1.0,
+ position + 0.5,
+ r_tl,
+ r_tr,
+ r_bl,
+ r_br
+ );
+ float inside_dist = -d;
+ float fade_factor = clamp(inside_dist / fade_inset, 0.0, 1.0);
+ final_color *= fade_factor;
+ }
+
+ gl_FragColor = final_color * quad_corner_alpha * clip_corner_alpha;
+}
diff --git a/scenefx/render/fx_renderer/gles2/shaders/tex.frag b/scenefx/render/fx_renderer/shaders/tex.frag
similarity index 52%
rename from scenefx/render/fx_renderer/gles2/shaders/tex.frag
rename to scenefx/render/fx_renderer/shaders/tex.frag
index 08ad3a1..11e3102 100644
--- a/scenefx/render/fx_renderer/gles2/shaders/tex.frag
+++ b/scenefx/render/fx_renderer/shaders/tex.frag
@@ -1,10 +1,11 @@
#define SOURCE %d
+#define EFFECTS %d
#define SOURCE_TEXTURE_RGBA 1
#define SOURCE_TEXTURE_RGBX 2
#define SOURCE_TEXTURE_EXTERNAL 3
-#if !defined(SOURCE)
+#if !defined(SOURCE) || !defined(EFFECTS)
#error "Missing shader preamble"
#endif
@@ -28,6 +29,7 @@ uniform sampler2D tex;
uniform float alpha;
+#if EFFECTS
uniform vec2 size;
uniform vec2 position;
uniform float radius_top_left;
@@ -35,6 +37,14 @@ uniform float radius_top_right;
uniform float radius_bottom_left;
uniform float radius_bottom_right;
+uniform vec2 clip_size;
+uniform vec2 clip_position;
+uniform float clip_radius_top_left;
+uniform float clip_radius_top_right;
+uniform float clip_radius_bottom_left;
+uniform float clip_radius_bottom_right;
+#endif
+
uniform bool discard_transparent;
vec4 sample_texture() {
@@ -45,21 +55,40 @@ vec4 sample_texture() {
#endif
}
-float corner_alpha(vec2 size, vec2 position, float round_tl, float round_tr, float round_bl, float round_br);
+#if EFFECTS
+float corner_alpha(vec2 size, vec2 position, bool is_cutout,
+ float radius_tl, float radius_tr, float radius_bl, float radius_br);
+#endif
void main() {
- float corner_alpha = corner_alpha(
- size - 0.5,
- position + 0.25,
- radius_top_left,
- radius_top_right,
- radius_bottom_left,
- radius_bottom_right
- );
- gl_FragColor = mix(sample_texture() * alpha, vec4(0.0), corner_alpha);
+#if EFFECTS
+ float quad_corner_alpha = corner_alpha(
+ size - 0.5,
+ position + 0.25,
+ false,
+ radius_top_left,
+ radius_top_right,
+ radius_bottom_left,
+ radius_bottom_right
+ );
+
+ // Clipping
+ float clip_corner_alpha = corner_alpha(
+ clip_size - 1.0,
+ clip_position + 0.5,
+ true,
+ clip_radius_top_left,
+ clip_radius_top_right,
+ clip_radius_bottom_left,
+ clip_radius_bottom_right
+ );
+
+ gl_FragColor = sample_texture() * alpha * quad_corner_alpha * clip_corner_alpha;
+#else
+ gl_FragColor = sample_texture() * alpha;
+#endif
if (discard_transparent && gl_FragColor.a == 0.0) {
discard;
- return;
}
}
diff --git a/scenefx/render/fx_renderer/tracy.c b/scenefx/render/fx_renderer/tracy.c
new file mode 100644
index 0000000..e8c773e
--- /dev/null
+++ b/scenefx/render/fx_renderer/tracy.c
@@ -0,0 +1,212 @@
+#ifdef TRACY_ENABLE
+#include <GLES2/gl2.h>
+#include <assert.h>
+#include <stdatomic.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tracy/TracyC.h>
+#include <wayland-util.h>
+#include <wlr/util/log.h>
+
+#include "render/fx_renderer/fx_renderer.h"
+#include "render/tracy.h"
+
+// Docs used in the implementation:
+// - https://github.com/wolfpld/tracy/releases/latest/download/tracy.pdf
+// - https://registry.khronos.org/OpenGL/extensions/EXT/EXT_disjoint_timer_query.txt
+// - https://github.com/wolfpld/tracy/blob/7388a476587c93f4e8e3e50d64c1400fc8c5e47e/public/tracy/TracyOpenGL.hpp
+
+#define QUERY_QUEUE_LEN (64 * 1024)
+
+// Colors:
+// From https://github.com/wolfpld/tracy/blob/7388a476587c93f4e8e3e50d64c1400fc8c5e47e/public/common/TracyColor.hpp
+#define RED4 0x8b0000
+
+static atomic_int id_counter = 0;
+
+struct tracy_data {
+ struct fx_renderer *renderer;
+
+ uint8_t context_id;
+
+ struct {
+ uint32_t queries[QUERY_QUEUE_LEN];
+ uint32_t head;
+ uint32_t tail;
+ } queue;
+};
+
+/*
+ * GPU Zone
+ */
+
+static inline unsigned int get_next_query_index(struct tracy_data *tracy_data) {
+ const unsigned int id = tracy_data->queue.head;
+ tracy_data->queue.head = (tracy_data->queue.head + 1) % QUERY_QUEUE_LEN;
+ assert(tracy_data->queue.head != tracy_data->queue.tail);
+ return id;
+}
+
+void tracy_gpu_zone_begin(struct tracy_data *tracy_data, struct tracy_gpu_zone_context *out_ctx,
+ const int line, const char *source, const char *func, const char *name) {
+ if (out_ctx == NULL || tracy_data == NULL) {
+ return;
+ }
+
+ out_ctx->tracy_data = tracy_data;
+#ifdef TRACY_ON_DEMAND
+ out_ctx->is_active = TracyCIsConnected;
+#else
+ out_ctx->is_active = true;
+#endif
+
+ if (!out_ctx->is_active) {
+ return;
+ }
+
+ // Create the query object
+ const unsigned int query = get_next_query_index(tracy_data);
+ tracy_data->renderer->procs.glQueryCounterEXT(tracy_data->queue.queries[query], GL_TIMESTAMP_EXT);
+
+ // Label the zone
+ uint64_t srcloc = ___tracy_alloc_srcloc_name(line,
+ source, strlen(source),
+ func, strlen(func),
+ name, strlen(name),
+ 0);
+
+ // Begin the Tracy GPU zone
+ const struct ___tracy_gpu_zone_begin_data data = {
+ .context = tracy_data->context_id,
+ .queryId = (uint16_t) query,
+ .srcloc = srcloc,
+ };
+ ___tracy_emit_gpu_zone_begin_alloc(data);
+}
+
+void tracy_gpu_zone_end(struct tracy_gpu_zone_context *ctx) {
+ if (ctx == NULL || ctx->tracy_data == NULL || !ctx->is_active) {
+ return;
+ }
+ struct tracy_data *tracy_data = ctx->tracy_data;
+
+ // Create the query object
+ const unsigned int query = get_next_query_index(tracy_data);
+ tracy_data->renderer->procs.glQueryCounterEXT(tracy_data->queue.queries[query], GL_TIMESTAMP_EXT);
+
+ // End the Tracy GPU zone
+ const struct ___tracy_gpu_zone_end_data data = {
+ .context = tracy_data->context_id,
+ .queryId = (uint16_t) query,
+ };
+ ___tracy_emit_gpu_zone_end(data);
+}
+
+void tracy_gpu_context_collect(struct tracy_data *tracy_data) {
+ if (tracy_data == NULL) {
+ TracyCMessageL("tracy_data == NULL");
+ return;
+ }
+
+ TracyCZoneC(ctx, RED4, true);
+
+ if (tracy_data->queue.tail == tracy_data->queue.head) {
+ goto done;
+ }
+
+#ifdef TRACY_ON_DEMAND
+ if (!TracyCIsConnected) {
+ tracy_data->queue.head = 0;
+ tracy_data->queue.tail = 0;
+ goto done;
+ }
+#endif
+
+ while (tracy_data->queue.tail != tracy_data->queue.head) {
+ GLint available;
+ tracy_data->renderer->procs.glGetQueryObjectivEXT(
+ tracy_data->queue.queries[tracy_data->queue.tail],
+ GL_QUERY_RESULT_AVAILABLE_EXT, &available);
+ if (!available) {
+ goto done;
+ }
+
+ GLuint64 time;
+ tracy_data->renderer->procs.glGetQueryObjectui64vEXT(
+ tracy_data->queue.queries[tracy_data->queue.tail],
+ GL_QUERY_RESULT_EXT, &time);
+
+ const struct ___tracy_gpu_time_data data = {
+ .context = tracy_data->context_id,
+ .gpuTime = time,
+ .queryId = (uint16_t) tracy_data->queue.tail,
+ };
+ ___tracy_emit_gpu_time(data);
+
+ tracy_data->queue.tail = (tracy_data->queue.tail + 1) % QUERY_QUEUE_LEN;
+ }
+
+done:
+ TracyCZoneEnd(ctx);
+}
+
+void tracy_gpu_context_destroy(struct tracy_data *tracy_data) {
+ id_counter--;
+ if (tracy_data == NULL) {
+ return;
+ }
+
+ tracy_data->renderer->procs.glDeleteQueriesEXT(QUERY_QUEUE_LEN, tracy_data->queue.queries);
+ free(tracy_data);
+}
+
+struct tracy_data *tracy_gpu_context_new(struct fx_renderer *renderer) {
+ assert(renderer);
+
+ struct tracy_data *tracy_data = calloc(1, sizeof(*tracy_data));
+ if (tracy_data == NULL) {
+ wlr_log_errno(WLR_ERROR, "Allocation failed");
+ return NULL;
+ }
+
+ tracy_data->renderer = renderer;
+ tracy_data->context_id = ++id_counter;
+ tracy_data->queue.head = 0;
+ tracy_data->queue.tail = 0;
+
+ // Create the query objects
+ renderer->procs.glGenQueriesEXT(QUERY_QUEUE_LEN, tracy_data->queue.queries);
+
+ // Get the current GL time
+ GLint64 gl_gpu_time;
+ renderer->procs.glGetInteger64vEXT(GL_TIMESTAMP_EXT, &gl_gpu_time);
+
+ // Create the query objects
+ GLint bits;
+ renderer->procs.glGetQueryivEXT(GL_TIMESTAMP_EXT, GL_QUERY_COUNTER_BITS_EXT, &bits);
+
+ const struct ___tracy_gpu_new_context_data data = {
+ .context = tracy_data->context_id,
+ .gpuTime = gl_gpu_time,
+ .period = 1.0f,
+ .flags = 0,
+ .type = 1, // `enum class GpuContextType` in TracyQueue.hpp
+ };
+ ___tracy_emit_gpu_new_context(data);
+
+ // Set the custom name
+ int len = snprintf(NULL, 0, "FX Renderer (GLESV2): %s",
+ glGetString(GL_RENDERER)) + 1; \
+ char ctx_name[len]; \
+ snprintf(ctx_name, len, "FX Renderer (GLESV2): %s",
+ glGetString(GL_RENDERER)); \
+ const struct ___tracy_gpu_context_name_data name_data = {
+ .context = tracy_data->context_id,
+ .name = ctx_name,
+ .len = strlen(ctx_name),
+ };
+ ___tracy_emit_gpu_context_name(name_data);
+ return tracy_data;
+}
+#endif /* ifdef TRACY_ENABLE */
diff --git a/scenefx/render/meson.build b/scenefx/render/meson.build
index c5dd423..97b97d6 100644
--- a/scenefx/render/meson.build
+++ b/scenefx/render/meson.build
@@ -1,6 +1,16 @@
scenefx_files += files(
- 'pixel_format.c',
+ 'color.c',
'egl.c',
+ 'pixel_format.c',
)
subdir('fx_renderer')
+
+lcms2 = dependency('lcms2', required: get_option('color-management'))
+if lcms2.found()
+ scenefx_deps += lcms2
+ scenefx_files += files('color_lcms2.c')
+ features += {'color-management': true}
+else
+ scenefx_files += files('color_fallback.c')
+endif
diff --git a/scenefx/render/pixel_format.c b/scenefx/render/pixel_format.c
index 7befbf0..c60dd9d 100644
--- a/scenefx/render/pixel_format.c
+++ b/scenefx/render/pixel_format.c
@@ -44,10 +44,26 @@ static const struct wlr_pixel_format_info pixel_format_info[] = {
.drm_format = DRM_FORMAT_R8,
.bytes_per_block = 1,
},
+ {
+ .drm_format = DRM_FORMAT_R16F,
+ .bytes_per_block = 2,
+ },
+ {
+ .drm_format = DRM_FORMAT_R32F,
+ .bytes_per_block = 4,
+ },
{
.drm_format = DRM_FORMAT_GR88,
.bytes_per_block = 2,
},
+ {
+ .drm_format = DRM_FORMAT_GR1616F,
+ .bytes_per_block = 4,
+ },
+ {
+ .drm_format = DRM_FORMAT_GR3232F,
+ .bytes_per_block = 8,
+ },
{
.drm_format = DRM_FORMAT_RGB888,
.bytes_per_block = 3,
@@ -56,6 +72,18 @@ static const struct wlr_pixel_format_info pixel_format_info[] = {
.drm_format = DRM_FORMAT_BGR888,
.bytes_per_block = 3,
},
+ {
+ .drm_format = DRM_FORMAT_BGR161616,
+ .bytes_per_block = 6,
+ },
+ {
+ .drm_format = DRM_FORMAT_BGR161616F,
+ .bytes_per_block = 6,
+ },
+ {
+ .drm_format = DRM_FORMAT_BGR323232F,
+ .bytes_per_block = 12,
+ },
{
.drm_format = DRM_FORMAT_RGBX4444,
.bytes_per_block = 2,
@@ -136,6 +164,10 @@ static const struct wlr_pixel_format_info pixel_format_info[] = {
.opaque_substitute = DRM_FORMAT_XBGR16161616F,
.bytes_per_block = 8,
},
+ {
+ .drm_format = DRM_FORMAT_ABGR32323232F,
+ .bytes_per_block = 16,
+ },
{
.drm_format = DRM_FORMAT_XBGR16161616,
.bytes_per_block = 8,
@@ -275,3 +307,68 @@ bool pixel_format_has_alpha(uint32_t fmt) {
}
return true;
}
+
+bool pixel_format_is_ycbcr(uint32_t format) {
+ switch (format) {
+ case DRM_FORMAT_AYUV:
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV15:
+ case DRM_FORMAT_NV16:
+ case DRM_FORMAT_NV20:
+ case DRM_FORMAT_NV21:
+ case DRM_FORMAT_NV24:
+ case DRM_FORMAT_NV30:
+ case DRM_FORMAT_NV42:
+ case DRM_FORMAT_NV61:
+ case DRM_FORMAT_P010:
+ case DRM_FORMAT_P012:
+ case DRM_FORMAT_P016:
+ case DRM_FORMAT_P030:
+ case DRM_FORMAT_P210:
+ case DRM_FORMAT_Q401:
+ case DRM_FORMAT_Q410:
+ case DRM_FORMAT_S010:
+ case DRM_FORMAT_S012:
+ case DRM_FORMAT_S016:
+ case DRM_FORMAT_S210:
+ case DRM_FORMAT_S212:
+ case DRM_FORMAT_S216:
+ case DRM_FORMAT_S410:
+ case DRM_FORMAT_S412:
+ case DRM_FORMAT_S416:
+ case DRM_FORMAT_UYVY:
+ case DRM_FORMAT_VUY101010:
+ case DRM_FORMAT_VUY888:
+ case DRM_FORMAT_VYUY:
+ case DRM_FORMAT_X0L0:
+ case DRM_FORMAT_X0L2:
+ case DRM_FORMAT_XVYU12_16161616:
+ case DRM_FORMAT_XVYU16161616:
+ case DRM_FORMAT_XVYU2101010:
+ case DRM_FORMAT_XYUV8888:
+ case DRM_FORMAT_Y0L0:
+ case DRM_FORMAT_Y0L2:
+ case DRM_FORMAT_Y210:
+ case DRM_FORMAT_Y212:
+ case DRM_FORMAT_Y216:
+ case DRM_FORMAT_Y410:
+ case DRM_FORMAT_Y412:
+ case DRM_FORMAT_Y416:
+ case DRM_FORMAT_YUV410:
+ case DRM_FORMAT_YUV411:
+ case DRM_FORMAT_YUV420:
+ case DRM_FORMAT_YUV420_10BIT:
+ case DRM_FORMAT_YUV420_8BIT:
+ case DRM_FORMAT_YUV422:
+ case DRM_FORMAT_YUV444:
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_YVU410:
+ case DRM_FORMAT_YVU411:
+ case DRM_FORMAT_YVU420:
+ case DRM_FORMAT_YVU422:
+ case DRM_FORMAT_YVU444:
+ case DRM_FORMAT_YVYU:
+ return true;
+ }
+ return false;
+}
diff --git a/scenefx/subprojects/packagefiles/tracy_v0_13_1_build_fix.patch b/scenefx/subprojects/packagefiles/tracy_v0_13_1_build_fix.patch
new file mode 100644
index 0000000..d85b014
--- /dev/null
+++ b/scenefx/subprojects/packagefiles/tracy_v0_13_1_build_fix.patch
@@ -0,0 +1,22 @@
+diff --git a/public/client/TracyProfiler.cpp b/public/client/TracyProfiler.cpp
+index 0691f78..20a3f02 100644
+--- a/public/client/TracyProfiler.cpp
++++ b/public/client/TracyProfiler.cpp
+@@ -933,7 +933,7 @@ static Thread* s_symbolThread;
+ std::atomic<bool> s_symbolThreadGone { false };
+ #endif
+ #ifdef TRACY_HAS_SYSTEM_TRACING
+-static std::atomic<Thread*> s_sysTraceThread = nullptr;
++static std::atomic<Thread*> s_sysTraceThread(nullptr);
+ #endif
+
+ #if defined __linux__ && !defined TRACY_NO_CRASH_HANDLER
+@@ -2154,7 +2154,7 @@ void Profiler::Worker()
+ // takes care of calling StopSystemTracing(). However, a client may decide to
+ // manually RequestShutdown(), in which case ~Profile() may not execute before
+ // this Worker() thread goes through its teardown stages and reaches this point.
+- // To ensure that system tracing does not keep pushing data to the worker queue
++ // To ensure that system tracing does not keep pushing data to the worker queue
+ // indefinitely (thus preventing this worker from terminating), we have to call
+ // StopSystemTracing() here as well to be safe:
+ StopSystemTracing();
diff --git a/scenefx/subprojects/tracy.wrap b/scenefx/subprojects/tracy.wrap
new file mode 100644
index 0000000..728ed1a
--- /dev/null
+++ b/scenefx/subprojects/tracy.wrap
@@ -0,0 +1,8 @@
+[wrap-git]
+url=https://github.com/wolfpld/tracy.git
+revision=v0.13.1
+depth=1
+
+# Fixes build errors for v0.13.1.
+# Should be removed when the next version gets released!
+diff_files=tracy_v0_13_1_build_fix.patch
diff --git a/scenefx/tinywl/Makefile b/scenefx/tinywl/Makefile
index fb5f315..a99934c 100644
--- a/scenefx/tinywl/Makefile
+++ b/scenefx/tinywl/Makefile
@@ -1,27 +1,18 @@
PKG_CONFIG?=pkg-config
-WAYLAND_PROTOCOLS!=$(PKG_CONFIG) --variable=pkgdatadir wayland-protocols
-WAYLAND_SCANNER!=$(PKG_CONFIG) --variable=wayland_scanner wayland-scanner
-PKGS="scenefx-0.2" "wlroots-0.19" wayland-server xkbcommon
+PKGS="scenefx-0.5" "wlroots-0.20" wayland-server xkbcommon
CFLAGS_PKG_CONFIG!=$(PKG_CONFIG) --cflags $(PKGS)
CFLAGS+=$(CFLAGS_PKG_CONFIG)
LIBS!=$(PKG_CONFIG) --libs $(PKGS)
all: tinywl
-# wayland-scanner is a tool which generates C headers and rigging for Wayland
-# protocols, which are specified in XML. wlroots requires you to rig these up
-# to your build system yourself and provide them in the include path.
-xdg-shell-protocol.h:
- $(WAYLAND_SCANNER) server-header \
- $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
-
-tinywl.o: tinywl.c xdg-shell-protocol.h
+tinywl.o: tinywl.c
$(CC) -c $< -g -Werror $(CFLAGS) -I. -DWLR_USE_UNSTABLE -o $@
tinywl: tinywl.o
- $(CC) $^ $> -g -Werror $(CFLAGS) $(LDFLAGS) $(LIBS) -o $@
+ $(CC) $^ -g -Werror $(CFLAGS) $(LDFLAGS) $(LIBS) -o $@
clean:
- rm -f tinywl tinywl.o xdg-shell-protocol.h
+ rm -f tinywl tinywl.o
.PHONY: all clean
diff --git a/scenefx/tinywl/meson.build b/scenefx/tinywl/meson.build
index 4c3a1d0..295390f 100644
--- a/scenefx/tinywl/meson.build
+++ b/scenefx/tinywl/meson.build
@@ -1,5 +1,5 @@
executable(
'tinywl',
- ['tinywl.c', protocols_client_header['xdg-shell']],
+ 'tinywl.c',
dependencies: scenefx,
)
diff --git a/scenefx/tinywl/tinywl.c b/scenefx/tinywl/tinywl.c
index c6e2a5d..2f29028 100644
--- a/scenefx/tinywl/tinywl.c
+++ b/scenefx/tinywl/tinywl.c
@@ -6,7 +6,6 @@
#include <time.h>
#include <scenefx/render/fx_renderer/fx_renderer.h>
#include <scenefx/types/fx/clipped_region.h>
-#include <scenefx/types/fx/corner_location.h>
#include <scenefx/types/wlr_scene.h>
#include <unistd.h>
#include <wayland-server-core.h>
@@ -68,6 +67,7 @@ struct tinywl_server {
struct wlr_seat *seat;
struct wl_listener new_input;
struct wl_listener request_cursor;
+ struct wl_listener pointer_focus_change;
struct wl_listener request_set_selection;
struct wl_list keyboards;
enum tinywl_cursor_mode cursor_mode;
@@ -107,6 +107,7 @@ struct tinywl_toplevel {
float opacity;
int corner_radius;
+ struct wlr_scene_blur *blur;
struct wlr_scene_shadow *shadow;
struct wlr_scene_rect *border;
};
@@ -351,6 +352,18 @@ static void seat_request_cursor(struct wl_listener *listener, void *data) {
}
}
+static void seat_pointer_focus_change(struct wl_listener *listener, void *data) {
+ struct tinywl_server *server = wl_container_of(
+ listener, server, pointer_focus_change);
+ /* This event is raised when the pointer focus is changed, including when the
+ * client is closed. We set the cursor image to its default if target surface
+ * is NULL */
+ struct wlr_seat_pointer_focus_change_event *event = data;
+ if (event->new_surface == NULL) {
+ wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr, "default");
+ }
+}
+
static void seat_request_set_selection(struct wl_listener *listener, void *data) {
/* This event is raised by the seat when a client wants to set the selection,
* usually when the user copies something. wlroots allows compositors to
@@ -599,6 +612,7 @@ static void xdg_toplevel_commit(struct wl_listener *listener, void *data) {
struct wlr_box *geometry = &toplevel->xdg_toplevel->base->geometry;
wlr_scene_subsurface_tree_set_clip(&toplevel->xdg_scene_tree->node, geometry);
+ wlr_scene_blur_set_size(toplevel->blur, geometry->width, geometry->height);
int border_width = geometry->width + (BORDER_THICKNESS * 2);
int border_height = geometry->height + (BORDER_THICKNESS * 2);
@@ -608,8 +622,7 @@ static void xdg_toplevel_commit(struct wl_listener *listener, void *data) {
wlr_scene_rect_set_size(toplevel->border, border_width, border_height);
wlr_scene_rect_set_clipped_region(toplevel->border, (struct clipped_region) {
- .corner_radius = toplevel->corner_radius,
- .corners = CORNER_LOCATION_ALL,
+ .corners = corner_radii_all(toplevel->corner_radius),
.area = { BORDER_THICKNESS, BORDER_THICKNESS, geometry->width, geometry->height }
});
@@ -618,8 +631,7 @@ static void xdg_toplevel_commit(struct wl_listener *listener, void *data) {
border_width + (blur_sigma * 2),
border_height + (blur_sigma * 2));
wlr_scene_shadow_set_clipped_region(toplevel->shadow, (struct clipped_region) {
- .corner_radius = toplevel->corner_radius + BORDER_THICKNESS,
- .corners = CORNER_LOCATION_ALL,
+ .corners = corner_radii_all(toplevel->corner_radius + BORDER_THICKNESS),
.area = { blur_sigma, blur_sigma, border_width, border_height }
});
}
@@ -653,8 +665,8 @@ static void output_configure_scene(struct wlr_scene_node *node,
wlr_scene_buffer_set_opacity(buffer, toplevel->opacity);
if (!wlr_subsurface_try_from_wlr_surface(xdg_surface->surface)) {
- wlr_scene_buffer_set_corner_radius(
- buffer, toplevel->corner_radius, CORNER_LOCATION_BOTTOM);
+ wlr_scene_buffer_set_corner_radii(
+ buffer, corner_radii_bottom(toplevel->corner_radius));
}
}
} else if (node->type == WLR_SCENE_NODE_TREE) {
@@ -720,7 +732,7 @@ static void server_new_output(struct wl_listener *listener, void *data) {
struct wlr_output *wlr_output = data;
/* Configures the output created by the backend to use our allocator
- * and our renderer. Must be done once, before commiting the output */
+ * and our renderer. Must be done once, before committing the output */
wlr_output_init_render(wlr_output, server->allocator, server->renderer);
/* The output may be disabled, switch it on. */
@@ -803,12 +815,9 @@ static void iter_xdg_scene_buffers(struct wlr_scene_buffer *buffer, int sx,
wlr_scene_buffer_set_opacity(buffer, toplevel->opacity);
if (!wlr_subsurface_try_from_wlr_surface(xdg_surface->surface)) {
- wlr_scene_buffer_set_corner_radius(buffer, toplevel->corner_radius,
- CORNER_LOCATION_BOTTOM);
+ wlr_scene_buffer_set_corner_radii(buffer, corner_radii_bottom(toplevel->corner_radius));
- wlr_scene_buffer_set_backdrop_blur(buffer, true);
- wlr_scene_buffer_set_backdrop_blur_optimized(buffer, true);
- wlr_scene_buffer_set_backdrop_blur_ignore_transparent(buffer, true);
+ wlr_scene_blur_set_transparency_mask_source(toplevel->blur, buffer);
}
}
}
@@ -958,10 +967,13 @@ static void server_new_xdg_toplevel(struct wl_listener *listener, void *data) {
toplevel->opacity = 1;
toplevel->corner_radius = 20;
+ toplevel->blur = wlr_scene_blur_create(toplevel->scene_tree, 0, 0);
+ wlr_scene_blur_set_should_only_blur_bottom_layer(toplevel->blur, true);
+
toplevel->border = wlr_scene_rect_create(toplevel->scene_tree, 0, 0,
(float[4]){ 1.0f, 0.f, 0.f, 1.0f });
- wlr_scene_rect_set_corner_radius(toplevel->border,
- toplevel->corner_radius + BORDER_THICKNESS, CORNER_LOCATION_BOTTOM);
+ wlr_scene_rect_set_corner_radii(toplevel->border,
+ corner_radii_bottom(toplevel->corner_radius + BORDER_THICKNESS));
wlr_scene_node_set_position(&toplevel->border->node, -BORDER_THICKNESS, -BORDER_THICKNESS);
float blur_sigma = 20.0f;
@@ -974,6 +986,8 @@ static void server_new_xdg_toplevel(struct wl_listener *listener, void *data) {
wlr_scene_node_lower_to_bottom(&toplevel->border->node);
// Lower the shadow below the border
wlr_scene_node_lower_to_bottom(&toplevel->shadow->node);
+ // Lower the blur below the shadow
+ wlr_scene_node_lower_to_bottom(&toplevel->blur->node);
/* Listen to the various events it can emit */
toplevel->map.notify = xdg_toplevel_map;
@@ -1066,7 +1080,7 @@ int main(int argc, char *argv[]) {
struct tinywl_server server = {0};
/* The Wayland display is managed by libwayland. It handles accepting
- * clients from the Unix socket, manging Wayland globals, and so on. */
+ * clients from the Unix socket, managing Wayland globals, and so on. */
server.wl_display = wl_display_create();
/* The backend is a wlroots feature which abstracts the underlying input and
* output hardware. The autocreate option will choose the most suitable
@@ -1144,8 +1158,7 @@ int main(int argc, char *argv[]) {
struct wlr_scene_rect *rect = wlr_scene_rect_create(server.layers.toplevel_layer,
200, 200, top_rect_color);
wlr_scene_rect_set_clipped_region(rect, (struct clipped_region) {
- .corner_radius = 12,
- .corners = CORNER_LOCATION_TOP,
+ .corners = {12, 12, 0, 0},
.area = {
.x = 50,
.y = 50,
@@ -1215,6 +1228,9 @@ int main(int argc, char *argv[]) {
server.request_cursor.notify = seat_request_cursor;
wl_signal_add(&server.seat->events.request_set_cursor,
&server.request_cursor);
+ server.pointer_focus_change.notify = seat_pointer_focus_change;
+ wl_signal_add(&server.seat->pointer_state.events.focus_change,
+ &server.pointer_focus_change);
server.request_set_selection.notify = seat_request_set_selection;
wl_signal_add(&server.seat->events.request_set_selection,
&server.request_set_selection);
@@ -1266,6 +1282,7 @@ int main(int argc, char *argv[]) {
wl_list_remove(&server.new_input.link);
wl_list_remove(&server.request_cursor.link);
+ wl_list_remove(&server.pointer_focus_change.link);
wl_list_remove(&server.request_set_selection.link);
wl_list_remove(&server.new_output.link);
diff --git a/scenefx/types/fx/blur_data.c b/scenefx/types/fx/blur_data.c
index 423c02a..fcc8c74 100644
--- a/scenefx/types/fx/blur_data.c
+++ b/scenefx/types/fx/blur_data.c
@@ -25,3 +25,39 @@ bool blur_data_should_parameters_blur_effects(struct blur_data *blur_data) {
int blur_data_calc_size(struct blur_data *blur_data) {
return pow(2, blur_data->num_passes + 1) * blur_data->radius;
}
+
+static float lerp(float a, float b, float f) {
+ return (a * (1.0 - f)) + (b * f);
+}
+
+static void adjust_post_effects(struct blur_data *blur_data, float strength) {
+ blur_data->brightness = lerp(1.0, blur_data->brightness, strength);
+ blur_data->contrast = lerp(1.0, blur_data->contrast, strength);
+ blur_data->noise = lerp(0.0, blur_data->noise, strength);
+ blur_data->saturation = lerp(1.0, blur_data->saturation, strength);
+}
+
+struct blur_data blur_data_apply_strength(struct blur_data *ref_blur_data, float strength) {
+ struct blur_data blur_data = *ref_blur_data;
+
+ if (strength >= 1.0f) {
+ adjust_post_effects(&blur_data, 1.0f);
+ return blur_data;
+ } else if (strength <= 0.0f) {
+ adjust_post_effects(&blur_data, 0.0f);
+ blur_data.num_passes = 0;
+ blur_data.radius = 0.0f;
+ return blur_data;
+ }
+
+ // Calculate the new blur strength from the new alpha multiplied blur size
+ // Also make sure that the values are never larger than the initial values
+ int new_size = blur_data_calc_size(ref_blur_data) * strength;
+ blur_data.num_passes = fmax(0, fmin(ref_blur_data->num_passes, ceilf(log2(new_size / ref_blur_data->radius))));
+ blur_data.radius = fmax(0, fmin(ref_blur_data->radius, new_size / pow(2, blur_data.num_passes + 1)));
+
+ adjust_post_effects(&blur_data, strength);
+
+ return blur_data;
+}
+
diff --git a/scenefx/types/fx/clipped_region.c b/scenefx/types/fx/clipped_region.c
index b12b0d1..bc46a84 100644
--- a/scenefx/types/fx/clipped_region.c
+++ b/scenefx/types/fx/clipped_region.c
@@ -1,10 +1,71 @@
#include "scenefx/types/fx/clipped_region.h"
-#include "scenefx/types/fx/corner_location.h"
+#include "types/fx/clipped_region.h"
+
+struct fx_corner_radii fx_corner_radii_extend(struct fx_corner_radii corners, int extend) {
+ return (struct fx_corner_radii) {
+ .top_left = corners.top_left == 0 ? 0 : (corners.top_left + extend),
+ .top_right = corners.top_right == 0 ? 0 : (corners.top_right + extend),
+ .bottom_left = corners.bottom_left == 0 ? 0 : (corners.bottom_left + extend),
+ .bottom_right = corners.bottom_right == 0 ? 0 : (corners.bottom_right + extend),
+ };
+}
+
+void fx_corner_radii_transform(enum wl_output_transform transform,
+ struct fx_corner_radii *corners) {
+ if (transform & WL_OUTPUT_TRANSFORM_FLIPPED) {
+ *corners = (struct fx_corner_radii){
+ corners->top_right,
+ corners->top_left,
+ corners->bottom_left,
+ corners->bottom_right
+ };
+ }
+
+ unsigned int turns = transform & (WL_OUTPUT_TRANSFORM_90 | WL_OUTPUT_TRANSFORM_180 | WL_OUTPUT_TRANSFORM_270);
+ if (turns > 0) {
+ uint16_t points[4] = {corners->top_left, corners->top_right, corners->bottom_right, corners->bottom_left};
+ *corners = (struct fx_corner_radii){
+ points[turns % 4],
+ points[(turns + 1) % 4],
+ points[(turns + 2) % 4],
+ points[(turns + 3) % 4],
+ };
+ }
+}
+
+struct fx_corner_fradii fx_corner_radii_scale(struct fx_corner_radii corners, float scale) {
+ return (struct fx_corner_fradii) {
+ (float)corners.top_left * scale,
+ (float)corners.top_right * scale,
+ (float)corners.bottom_right * scale,
+ (float)corners.bottom_left * scale,
+ };
+}
+
+bool fx_corner_radii_eq(const struct fx_corner_radii lhs, const struct fx_corner_radii rhs) {
+ return lhs.top_left == rhs.top_left
+ && lhs.top_right == rhs.top_right
+ && lhs.bottom_right == rhs.bottom_right
+ && lhs.bottom_left == rhs.bottom_left;
+}
+
+bool fx_corner_radii_is_empty(const struct fx_corner_radii* corners) {
+ return corners->top_left == 0
+ && corners->top_right == 0
+ && corners->bottom_right == 0
+ && corners->bottom_left == 0;
+}
+
+bool fx_corner_fradii_is_empty(const struct fx_corner_fradii* corners) {
+ return corners->top_left == 0.0
+ && corners->top_right == 0.0
+ && corners->bottom_right == 0.0
+ && corners->bottom_left == 0.0;
+}
struct clipped_region clipped_region_get_default(void) {
return (struct clipped_region) {
- .corner_radius = 0,
- .corners = CORNER_LOCATION_NONE,
+ .corners = {0},
.area = (struct wlr_box) {0},
};
}
diff --git a/scenefx/types/fx/corner_location.c b/scenefx/types/fx/corner_location.c
deleted file mode 100644
index e7069f2..0000000
--- a/scenefx/types/fx/corner_location.c
+++ /dev/null
@@ -1,76 +0,0 @@
-#include "scenefx/types/fx/corner_location.h"
-
-// Maps the corners to its correct transformed corner
-static const enum corner_location corner_transform[8][CORNER_LOCATION_LAST] = {
- [WL_OUTPUT_TRANSFORM_NORMAL] = {
- [CORNER_LOCATION_TOP_LEFT] = CORNER_LOCATION_TOP_LEFT,
- [CORNER_LOCATION_TOP_RIGHT] = CORNER_LOCATION_TOP_RIGHT,
- [CORNER_LOCATION_BOTTOM_RIGHT] = CORNER_LOCATION_BOTTOM_RIGHT,
- [CORNER_LOCATION_BOTTOM_LEFT] = CORNER_LOCATION_BOTTOM_LEFT,
- },
- [WL_OUTPUT_TRANSFORM_90] = {
- [CORNER_LOCATION_TOP_LEFT] = CORNER_LOCATION_BOTTOM_LEFT,
- [CORNER_LOCATION_TOP_RIGHT] = CORNER_LOCATION_TOP_LEFT,
- [CORNER_LOCATION_BOTTOM_RIGHT] = CORNER_LOCATION_TOP_RIGHT,
- [CORNER_LOCATION_BOTTOM_LEFT] = CORNER_LOCATION_BOTTOM_RIGHT,
- },
- [WL_OUTPUT_TRANSFORM_180] = {
- [CORNER_LOCATION_TOP_LEFT] = CORNER_LOCATION_BOTTOM_RIGHT,
- [CORNER_LOCATION_TOP_RIGHT] = CORNER_LOCATION_BOTTOM_LEFT,
- [CORNER_LOCATION_BOTTOM_RIGHT] = CORNER_LOCATION_TOP_LEFT,
- [CORNER_LOCATION_BOTTOM_LEFT] = CORNER_LOCATION_TOP_RIGHT,
- },
- [WL_OUTPUT_TRANSFORM_270] = {
- [CORNER_LOCATION_TOP_LEFT] = CORNER_LOCATION_TOP_RIGHT,
- [CORNER_LOCATION_TOP_RIGHT] = CORNER_LOCATION_BOTTOM_RIGHT,
- [CORNER_LOCATION_BOTTOM_RIGHT] = CORNER_LOCATION_BOTTOM_LEFT,
- [CORNER_LOCATION_BOTTOM_LEFT] = CORNER_LOCATION_TOP_LEFT,
- },
- [WL_OUTPUT_TRANSFORM_FLIPPED] = {
- [CORNER_LOCATION_TOP_LEFT] = CORNER_LOCATION_TOP_RIGHT,
- [CORNER_LOCATION_TOP_RIGHT] = CORNER_LOCATION_TOP_LEFT,
- [CORNER_LOCATION_BOTTOM_RIGHT] = CORNER_LOCATION_BOTTOM_LEFT,
- [CORNER_LOCATION_BOTTOM_LEFT] = CORNER_LOCATION_BOTTOM_RIGHT,
- },
- [WL_OUTPUT_TRANSFORM_FLIPPED_90] = {
- [CORNER_LOCATION_TOP_LEFT] = CORNER_LOCATION_TOP_LEFT,
- [CORNER_LOCATION_TOP_RIGHT] = CORNER_LOCATION_BOTTOM_LEFT,
- [CORNER_LOCATION_BOTTOM_RIGHT] = CORNER_LOCATION_BOTTOM_RIGHT,
- [CORNER_LOCATION_BOTTOM_LEFT] = CORNER_LOCATION_TOP_RIGHT,
- },
- [WL_OUTPUT_TRANSFORM_FLIPPED_180] = {
- [CORNER_LOCATION_TOP_LEFT] = CORNER_LOCATION_BOTTOM_LEFT,
- [CORNER_LOCATION_TOP_RIGHT] = CORNER_LOCATION_BOTTOM_RIGHT,
- [CORNER_LOCATION_BOTTOM_RIGHT] = CORNER_LOCATION_TOP_RIGHT,
- [CORNER_LOCATION_BOTTOM_LEFT] = CORNER_LOCATION_TOP_LEFT,
- },
- [WL_OUTPUT_TRANSFORM_FLIPPED_270] = {
- [CORNER_LOCATION_TOP_LEFT] = CORNER_LOCATION_BOTTOM_RIGHT,
- [CORNER_LOCATION_TOP_RIGHT] = CORNER_LOCATION_TOP_RIGHT,
- [CORNER_LOCATION_BOTTOM_RIGHT] = CORNER_LOCATION_TOP_LEFT,
- [CORNER_LOCATION_BOTTOM_LEFT] = CORNER_LOCATION_BOTTOM_LEFT,
- },
-};
-
-void corner_location_transform(enum wl_output_transform transform,
- enum corner_location *corners) {
- enum corner_location result = CORNER_LOCATION_NONE;
-
- int bitmask = 1 << (CORNER_LOCATION_COUNT - 1);
- int mask = 1;
- while (bitmask) {
- switch (*corners & mask) {
- case CORNER_LOCATION_NONE:
- case CORNER_LOCATION_ALL:
- break;
- default:
- result |= corner_transform[transform][*corners & mask];
- break;
- }
-
- bitmask &= ~mask;
- mask <<= 1;
- }
-
- *corners = result;
-}
diff --git a/scenefx/types/fx/meson.build b/scenefx/types/fx/meson.build
index 749d01d..e22e4a8 100644
--- a/scenefx/types/fx/meson.build
+++ b/scenefx/types/fx/meson.build
@@ -1,5 +1,4 @@
scenefx_files += files(
'blur_data.c',
- 'corner_location.c',
'clipped_region.c',
)
diff --git a/scenefx/types/meson.build b/scenefx/types/meson.build
index a7e855b..96aa83d 100644
--- a/scenefx/types/meson.build
+++ b/scenefx/types/meson.build
@@ -1,6 +1,7 @@
scenefx_files += files(
'scene/wlr_scene.c',
- 'output/wlr_output.c',
+ 'scene/linked_node.c',
+ 'output/output.c',
)
subdir('fx')
diff --git a/scenefx/types/output/wlr_output.c b/scenefx/types/output/output.c
similarity index 67%
rename from scenefx/types/output/wlr_output.c
rename to scenefx/types/output/output.c
index 59c9b31..17a7e3b 100644
--- a/scenefx/types/output/wlr_output.c
+++ b/scenefx/types/output/output.c
@@ -21,3 +21,11 @@ void output_pending_resolution(struct wlr_output *output,
*height = output->height;
}
}
+
+const struct wlr_output_image_description *output_pending_image_description(
+ struct wlr_output *output, const struct wlr_output_state *state) {
+ if (state->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) {
+ return state->image_description;
+ }
+ return output->image_description;
+}
diff --git a/scenefx/types/scene/linked_node.c b/scenefx/types/scene/linked_node.c
new file mode 100644
index 0000000..225d549
--- /dev/null
+++ b/scenefx/types/scene/linked_node.c
@@ -0,0 +1,78 @@
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wlr/util/log.h>
+
+#include "scenefx/types/linked_node.h"
+
+struct link {
+ struct linked_node *node_1;
+ struct linked_node *node_2;
+};
+
+static struct link *link_init(struct linked_node *node_1,
+ struct linked_node *node_2) {
+ struct link *link = calloc(1, sizeof(*link));
+ if (!link) {
+ wlr_log(WLR_ERROR, "Failed to allocate link!");
+ return NULL;
+ }
+
+ link->node_1 = node_1;
+ link->node_2 = node_2;
+
+ return link;
+}
+
+//
+// Linked Node
+//
+
+bool linked_nodes_are_linked(struct linked_node *node_1,
+ struct linked_node *node_2) {
+ return node_1->link != NULL && node_2->link != NULL
+ && node_1->link == node_2->link;
+}
+
+void linked_node_init_link(struct linked_node *node_1,
+ struct linked_node *node_2) {
+ if (node_1->link || node_2->link) {
+ assert(linked_nodes_are_linked(node_1, node_2));
+ return;
+ }
+
+ struct link *link = link_init(node_1, node_2);
+ node_1->link = link;
+ node_2->link = link;
+}
+
+struct linked_node *linked_nodes_get_sibling(struct linked_node *node) {
+ if (node->link == NULL) {
+ return NULL;
+ }
+ assert(node == node->link->node_1 || node == node->link->node_2);
+
+ struct link *link = node->link;
+ if (link->node_1 == node) {
+ return link->node_2;
+ }
+ return link->node_1;
+}
+
+void linked_node_unlink(struct linked_node *node_1,
+ struct linked_node *node_2) {
+ assert(linked_nodes_are_linked(node_1, node_2));
+
+ struct link *link = node_1->link;
+ node_1->link = NULL;
+ node_2->link = NULL;
+ free(link);
+}
+
+void linked_node_destroy(struct linked_node *node) {
+ if (node->link == NULL) {
+ return;
+ }
+ assert(node == node->link->node_1 || node == node->link->node_2);
+ linked_node_unlink(node->link->node_1, node->link->node_2);
+}
diff --git a/scenefx/types/scene/wlr_scene.c b/scenefx/types/scene/wlr_scene.c
index b4c1b63..51c90a5 100644
--- a/scenefx/types/scene/wlr_scene.c
+++ b/scenefx/types/scene/wlr_scene.c
@@ -2,33 +2,34 @@
#include <pixman.h>
#include <stdio.h>
#include <stdlib.h>
-#include <scenefx/types/wlr_scene.h>
#include <string.h>
#include <wlr/backend.h>
-#include <wlr/render/gles2.h>
-#include <wlr/render/drm_syncobj.h>
#include <wlr/render/swapchain.h>
+#include <wlr/render/drm_syncobj.h>
+#include <wlr/render/wlr_renderer.h>
+#include <wlr/types/wlr_color_management_v1.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_damage_ring.h>
#include <wlr/types/wlr_gamma_control_v1.h>
+#include <wlr/types/wlr_linux_dmabuf_v1.h>
#include <wlr/types/wlr_presentation_time.h>
-#include <wlr/types/wlr_subcompositor.h>
-#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
#include <wlr/util/region.h>
#include <wlr/util/transform.h>
-#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h"
-#include "scenefx/render/fx_renderer/fx_renderer.h"
+#include "render/color.h"
+#include "render/tracy.h"
+#include "scenefx/render/fx_renderer/fx_offscreen_buffers.h"
#include "scenefx/render/pass.h"
+#include "scenefx/types/fx/blur_data.h"
#include "scenefx/types/fx/clipped_region.h"
-#include "scenefx/types/fx/corner_location.h"
+#include "scenefx/types/wlr_scene.h"
+#include "types/fx/clipped_region.h"
#include "types/wlr_output.h"
#include "types/wlr_scene.h"
#include "util/array.h"
#include "util/env.h"
#include "util/time.h"
-#include "wlr/util/box.h"
#include <wlr/config.h>
@@ -72,6 +73,12 @@ struct wlr_scene_shadow *wlr_scene_shadow_from_node(struct wlr_scene_node *node)
return shadow;
}
+struct wlr_scene_blur *wlr_scene_blur_from_node(struct wlr_scene_node *node) {
+ assert(node->type == WLR_SCENE_NODE_BLUR);
+ struct wlr_scene_blur *blur = wl_container_of(node, blur, node);
+ return blur;
+}
+
struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) {
struct wlr_scene_tree *tree;
if (node->type == WLR_SCENE_NODE_TREE) {
@@ -150,6 +157,7 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) {
scene_buffer_set_texture(scene_buffer, NULL);
pixman_region32_fini(&scene_buffer->opaque_region);
wlr_drm_syncobj_timeline_unref(scene_buffer->wait_timeline);
+ linked_node_destroy(&scene_buffer->blur);
assert(wl_list_empty(&scene_buffer->events.output_leave.listener_list));
assert(wl_list_empty(&scene_buffer->events.output_enter.listener_list));
@@ -178,6 +186,9 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) {
&scene_tree->children, link) {
wlr_scene_node_destroy(child);
}
+ } else if (node->type == WLR_SCENE_NODE_BLUR) {
+ struct wlr_scene_blur *blur = wlr_scene_blur_from_node(node);
+ linked_node_destroy(&blur->transparency_mask_source);
}
assert(wl_list_empty(&node->events.destroy.listener_list));
@@ -207,6 +218,8 @@ struct wlr_scene *wlr_scene_create(void) {
wl_list_init(&scene->gamma_control_manager_v1_destroy.link);
wl_list_init(&scene->gamma_control_manager_v1_set_gamma.link);
+ scene->restack_xwayland_surfaces = true;
+
const char *debug_damage_options[] = {
"none",
"rerender",
@@ -236,8 +249,6 @@ struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent) {
return tree;
}
-static void scene_node_get_size(struct wlr_scene_node *node, int *lx, int *ly);
-
typedef bool (*scene_node_box_iterator_func_t)(struct wlr_scene_node *node,
int sx, int sy, void *data);
@@ -257,10 +268,11 @@ static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box
}
}
break;
- case WLR_SCENE_NODE_OPTIMIZED_BLUR:;
case WLR_SCENE_NODE_RECT:
+ case WLR_SCENE_NODE_BUFFER:
case WLR_SCENE_NODE_SHADOW:
- case WLR_SCENE_NODE_BUFFER:;
+ case WLR_SCENE_NODE_OPTIMIZED_BLUR:
+ case WLR_SCENE_NODE_BLUR:;
struct wlr_box node_box = { .x = lx, .y = ly };
scene_node_get_size(node, &node_box.width, &node_box.height);
@@ -282,6 +294,32 @@ static bool scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box,
return _scene_nodes_in_box(node, box, iterator, user_data, x, y);
}
+static pixman_region32_t create_corner_location_region(struct fx_corner_radii corners, int x, int y, int width, int height) {
+ pixman_region32_t corner_region;
+ pixman_region32_init(&corner_region);
+ if (corners.top_left) {
+ pixman_region32_union_rect(&corner_region, &corner_region, x, y, corners.top_left, corners.top_left);
+ }
+
+ if (corners.top_right) {
+ pixman_region32_union_rect(&corner_region, &corner_region, x + (width - corners.top_right), y,
+ corners.top_right, corners.top_right);
+ }
+
+ if (corners.bottom_left) {
+ pixman_region32_union_rect(&corner_region, &corner_region, x, y + (height - corners.bottom_left),
+ corners.bottom_left, corners.bottom_left);
+ }
+
+ if (corners.bottom_right) {
+ pixman_region32_union_rect(&corner_region, &corner_region,
+ x + (width - corners.bottom_right), y + (height - corners.bottom_right),
+ corners.bottom_right, corners.bottom_right);
+ }
+
+ return corner_region;
+}
+
static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y,
pixman_region32_t *opaque) {
int width, height;
@@ -289,28 +327,29 @@ static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y,
if (node->type == WLR_SCENE_NODE_RECT) {
struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node);
- if (scene_rect->corner_radius > 0) {
- // TODO: this is incorrect
- return;
- }
if (scene_rect->color[3] != 1) {
return;
}
- if (!wlr_box_empty(&scene_rect->clipped_region.area)) {
- pixman_region32_fini(opaque);
- pixman_region32_init_rect(opaque, x, y, width, height);
- // Subtract the clipped region from a otherwise fully opaque rect
+ pixman_region32_fini(opaque);
+ pixman_region32_init_rect(opaque, x, y, width, height);
+
+ // subtract corners from opaque region
+ if (!fx_corner_radii_is_empty(&scene_rect->corners)) {
+ pixman_region32_t corners = create_corner_location_region(scene_rect->corners, x, y, width, height);
+ pixman_region32_subtract(opaque, opaque, &corners);
+ pixman_region32_fini(&corners);
+ }
+
+ // subtract clipped area from opaque region
+ if (!wlr_box_empty(&scene_rect->clipped_region.area)) {
struct wlr_box *clipped = &scene_rect->clipped_region.area;
pixman_region32_t clipped_region;
pixman_region32_init_rect(&clipped_region, clipped->x + x, clipped->y + y,
clipped->width, clipped->height);
pixman_region32_subtract(opaque, opaque, &clipped_region);
pixman_region32_fini(&clipped_region);
- return;
}
- } else if (node->type == WLR_SCENE_NODE_SHADOW) {
- // TODO: test & handle case of blur sigma = 0 and color[3] = 1?
return;
} else if (node->type == WLR_SCENE_NODE_BUFFER) {
struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
@@ -323,18 +362,27 @@ static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y,
return;
}
- if (scene_buffer->corner_radius > 0) {
- // TODO: this is incorrect
- return;
- }
-
if (!scene_buffer->buffer_is_opaque) {
pixman_region32_copy(opaque, &scene_buffer->opaque_region);
pixman_region32_intersect_rect(opaque, opaque, 0, 0, width, height);
pixman_region32_translate(opaque, x, y);
- return;
+ } else {
+ pixman_region32_fini(opaque);
+ pixman_region32_init_rect(opaque, x, y, width, height);
+ }
+
+ // subtract the corners from the opaque region
+ if (!fx_corner_radii_is_empty(&scene_buffer->corners)) {
+ pixman_region32_t corners = create_corner_location_region(scene_buffer->corners, x, y, width, height);
+ pixman_region32_subtract(opaque, opaque, &corners);
+ pixman_region32_fini(&corners);
}
- } else if (node->type == WLR_SCENE_NODE_OPTIMIZED_BLUR) {
+
+ return;
+ } else if (node->type == WLR_SCENE_NODE_SHADOW) {
+ // TODO: test & handle case of blur sigma = 0 and color[3] = 1?
+ return;
+ } else if (node->type == WLR_SCENE_NODE_OPTIMIZED_BLUR || node->type == WLR_SCENE_NODE_BLUR) {
// Always transparent
return;
}
@@ -345,19 +393,18 @@ static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y,
struct scene_update_data {
pixman_region32_t *visible;
- pixman_region32_t *update_region;
+ const pixman_region32_t *update_region;
struct wlr_box update_box;
struct wl_list *outputs;
bool calculate_visibility;
+ bool restack_xwayland_surfaces;
#if WLR_HAS_XWAYLAND
struct wlr_xwayland_surface *restack_above;
#endif
-
- bool optimized_blur_dirty;
};
-static uint32_t region_area(pixman_region32_t *region) {
+static uint32_t region_area(const pixman_region32_t *region) {
uint32_t area = 0;
int nrects;
@@ -385,10 +432,8 @@ struct render_data {
struct wlr_scene_output *output;
- struct fx_gles_render_pass *render_pass;
+ struct wlr_render_pass *render_pass;
pixman_region32_t damage;
-
- bool has_blur;
};
static void logical_to_buffer_coords(pixman_region32_t *region, const struct render_data *data,
@@ -451,7 +496,7 @@ static void scene_output_damage_whole(struct wlr_scene_output *scene_output) {
pixman_region32_fini(&damage);
}
-static void scene_damage_outputs(struct wlr_scene *scene, pixman_region32_t *damage) {
+static void scene_damage_outputs(struct wlr_scene *scene, const pixman_region32_t *damage) {
if (pixman_region32_empty(damage)) {
return;
}
@@ -486,46 +531,51 @@ static void update_node_update_outputs(struct wlr_scene_node *node,
size_t count = 0;
uint64_t active_outputs = 0;
- // let's update the outputs in two steps:
- // - the primary outputs
- // - the enter/leave signals
- // This ensures that the enter/leave signals can rely on the primary output
- // to have a reasonable value. Otherwise, they may get a value that's in
- // the middle of a calculation.
- struct wlr_scene_output *scene_output;
- wl_list_for_each(scene_output, outputs, link) {
- if (scene_output == ignore) {
- continue;
- }
-
- if (!scene_output->output->enabled) {
- continue;
- }
+ if (!pixman_region32_empty(&node->visible)) {
+ uint32_t visible_area = region_area(&node->visible);
+
+ // let's update the outputs in two steps:
+ // - the primary outputs
+ // - the enter/leave signals
+ // This ensures that the enter/leave signals can rely on the primary output
+ // to have a reasonable value. Otherwise, they may get a value that's in
+ // the middle of a calculation.
+ struct wlr_scene_output *scene_output;
+ wl_list_for_each(scene_output, outputs, link) {
+ if (scene_output == ignore) {
+ continue;
+ }
- struct wlr_box output_box = {
- .x = scene_output->x,
- .y = scene_output->y,
- };
- wlr_output_effective_resolution(scene_output->output,
- &output_box.width, &output_box.height);
+ if (!scene_output->output->enabled) {
+ continue;
+ }
- pixman_region32_t intersection;
- pixman_region32_init(&intersection);
- pixman_region32_intersect_rect(&intersection, &node->visible,
- output_box.x, output_box.y, output_box.width, output_box.height);
+ struct wlr_box output_box = {
+ .x = scene_output->x,
+ .y = scene_output->y,
+ };
+ wlr_output_effective_resolution(scene_output->output,
+ &output_box.width, &output_box.height);
- if (!pixman_region32_empty(&intersection)) {
+ pixman_region32_t intersection;
+ pixman_region32_init(&intersection);
+ pixman_region32_intersect_rect(&intersection, &node->visible,
+ output_box.x, output_box.y, output_box.width, output_box.height);
uint32_t overlap = region_area(&intersection);
- if (overlap >= largest_overlap) {
- largest_overlap = overlap;
- scene_buffer->primary_output = scene_output;
- }
+ pixman_region32_fini(&intersection);
+
+ // If the overlap accounts for less than 10% of the visible node area,
+ // ignore this output
+ if (overlap >= 0.1 * visible_area) {
+ if (overlap >= largest_overlap) {
+ largest_overlap = overlap;
+ scene_buffer->primary_output = scene_output;
+ }
- active_outputs |= 1ull << scene_output->index;
- count++;
+ active_outputs |= 1ull << scene_output->index;
+ count++;
+ }
}
-
- pixman_region32_fini(&intersection);
}
if (old_primary_output != scene_buffer->primary_output) {
@@ -536,6 +586,7 @@ static void update_node_update_outputs(struct wlr_scene_node *node,
uint64_t old_active = scene_buffer->active_outputs;
scene_buffer->active_outputs = active_outputs;
+ struct wlr_scene_output *scene_output;
wl_list_for_each(scene_output, outputs, link) {
uint64_t mask = 1ull << scene_output->index;
bool intersects = active_outputs & mask;
@@ -620,25 +671,6 @@ static void restack_xwayland_surface(struct wlr_scene_node *node,
data->restack_above = xwayland_surface;
}
-
-static void restack_xwayland_surface_below(struct wlr_scene_node *node) {
- if (node->type == WLR_SCENE_NODE_TREE) {
- struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
- struct wlr_scene_node *child;
- wl_list_for_each(child, &scene_tree->children, link) {
- restack_xwayland_surface_below(child);
- }
- return;
- }
-
- struct wlr_xwayland_surface *xwayland_surface =
- scene_node_try_get_managed_xwayland_surface(node);
- if (!xwayland_surface) {
- return;
- }
-
- wlr_xwayland_surface_restack(xwayland_surface, NULL, XCB_STACK_MODE_BELOW);
-}
#endif
static bool scene_node_update_iterator(struct wlr_scene_node *node,
@@ -648,9 +680,9 @@ static bool scene_node_update_iterator(struct wlr_scene_node *node,
if (node->type == WLR_SCENE_NODE_OPTIMIZED_BLUR) {
struct wlr_scene_optimized_blur *scene_blur = wlr_scene_optimized_blur_from_node(node);
if (scene_blur->dirty) {
- data->optimized_blur_dirty = true;
- // Restore the visible region back to default for the nodes below
- // the optimized blur node
+ // Restore the visible region back to default, without any opaque
+ // regions. This ensures that all nodes below are fully re-rendered
+ // and not culled by above nodes.
pixman_region32_clear(data->visible);
pixman_region32_copy(data->visible, data->update_region);
}
@@ -664,7 +696,7 @@ static bool scene_node_update_iterator(struct wlr_scene_node *node,
pixman_region32_intersect_rect(&node->visible, &node->visible,
lx, ly, box.width, box.height);
- if (data->calculate_visibility && !data->optimized_blur_dirty) {
+ if (data->calculate_visibility) {
pixman_region32_t opaque;
pixman_region32_init(&opaque);
scene_node_opaque_region(node, lx, ly, &opaque);
@@ -674,7 +706,9 @@ static bool scene_node_update_iterator(struct wlr_scene_node *node,
update_node_update_outputs(node, data->outputs, NULL, NULL);
#if WLR_HAS_XWAYLAND
- restack_xwayland_surface(node, &box, data);
+ if (data->restack_xwayland_surfaces) {
+ restack_xwayland_surface(node, &box, data);
+ }
#endif
return false;
@@ -719,7 +753,7 @@ static void scene_node_bounds(struct wlr_scene_node *node,
}
static void scene_update_region(struct wlr_scene *scene,
- pixman_region32_t *update_region) {
+ const pixman_region32_t *update_region) {
pixman_region32_t visible;
pixman_region32_init(&visible);
pixman_region32_copy(&visible, update_region);
@@ -736,7 +770,7 @@ static void scene_update_region(struct wlr_scene *scene,
},
.outputs = &scene->outputs,
.calculate_visibility = scene->calculate_visibility,
- .optimized_blur_dirty = false,
+ .restack_xwayland_surfaces = scene->restack_xwayland_surfaces,
};
// update node visibility and output enter/leave events
@@ -745,16 +779,62 @@ static void scene_update_region(struct wlr_scene *scene,
pixman_region32_fini(&visible);
}
+static void scene_node_cleanup_when_disabled(struct wlr_scene_node *node,
+ bool xwayland_restack, struct wl_list *outputs) {
+ if (node->type == WLR_SCENE_NODE_TREE) {
+ struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
+ struct wlr_scene_node *child;
+ wl_list_for_each(child, &scene_tree->children, link) {
+ if (!child->enabled) {
+ continue;
+ }
+
+ scene_node_cleanup_when_disabled(child, xwayland_restack, outputs);
+ }
+ return;
+ }
+
+ pixman_region32_clear(&node->visible);
+ update_node_update_outputs(node, outputs, NULL, NULL);
+
+#if WLR_HAS_XWAYLAND
+ if (xwayland_restack) {
+ struct wlr_xwayland_surface *xwayland_surface =
+ scene_node_try_get_managed_xwayland_surface(node);
+ if (!xwayland_surface) {
+ return;
+ }
+
+ wlr_xwayland_surface_restack(xwayland_surface, NULL, XCB_STACK_MODE_BELOW);
+ }
+#endif
+}
+
+/**
+ * Updates the nodes visibility, xwayland restacking, send leave/enter events
+ * and damages the screen. The damage region is used to not only damage the
+ * screen, but to direct the update logic to only update certain parts of the
+ * screen and not the whole thing. If a NULL damage is given, the damage is
+ * assumed to be the previous nodes visibility.
+ *
+ * Currently, the only usage for an explicit damage is for update scenarios where
+ * the scene node might be enabled/disabled. If the scene node is disabled, the
+ * update logic will ignore the node. This is normally desirable as most update
+ * scenarios like updating the color or whatever. However, it's not what we want
+ * when disabling the node. Note that reparenting the node could lead to the node
+ * being reparented to a disabled super tree.
+ */
static void scene_node_update(struct wlr_scene_node *node,
pixman_region32_t *damage) {
struct wlr_scene *scene = scene_node_get_root(node);
int x, y;
if (!wlr_scene_node_coords(node, &x, &y)) {
-#if WLR_HAS_XWAYLAND
- restack_xwayland_surface_below(node);
-#endif
+ // We assume explicit damage on a disabled tree means the node was just
+ // disabled.
if (damage) {
+ scene_node_cleanup_when_disabled(node, scene->restack_xwayland_surfaces, &scene->outputs);
+
scene_update_region(scene, damage);
scene_damage_outputs(scene, damage);
pixman_region32_fini(damage);
@@ -797,13 +877,9 @@ struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent,
scene_rect->width = width;
scene_rect->height = height;
memcpy(scene_rect->color, color, sizeof(scene_rect->color));
- scene_rect->corner_radius = 0;
- scene_rect->corners = CORNER_LOCATION_NONE;
+ scene_rect->corners = (struct fx_corner_radii){0};
scene_rect->accepts_input = true;
scene_rect->clipped_region = clipped_region_get_default();
- scene_rect->backdrop_blur = false;
- scene_rect->backdrop_blur_optimized = false;
- scene_rect->fade_inset = 0;
scene_node_update(&scene_rect->node, NULL);
@@ -831,28 +907,11 @@ void wlr_scene_rect_set_color(struct wlr_scene_rect *rect, const float color[sta
scene_node_update(&rect->node, NULL);
}
-void wlr_scene_rect_set_backdrop_blur(struct wlr_scene_rect *rect,
- bool enabled) {
- if (rect->backdrop_blur == enabled) {
- return;
- }
- rect->backdrop_blur = enabled;
- scene_node_update(&rect->node, NULL);
-}
-
-void wlr_scene_rect_set_backdrop_blur_optimized(struct wlr_scene_rect *rect,
- bool enabled) {
- if (rect->backdrop_blur_optimized == enabled) {
- return;
- }
- rect->backdrop_blur_optimized = enabled;
- scene_node_update(&rect->node, NULL);
-}
-
void wlr_scene_rect_set_fade_inset(struct wlr_scene_rect *rect, int fade_inset) {
if (rect->fade_inset == fade_inset) {
return;
}
+
rect->fade_inset = fade_inset;
scene_node_update(&rect->node, NULL);
}
@@ -925,29 +984,26 @@ static void scene_buffer_set_wait_timeline(struct wlr_scene_buffer *scene_buffer
}
}
-void wlr_scene_rect_set_corner_radius(struct wlr_scene_rect *rect, int corner_radius,
- enum corner_location corners) {
- if (rect->corner_radius == corner_radius && rect->corners == corners) {
+inline void wlr_scene_rect_set_corner_radius(struct wlr_scene_rect *rect, int corner_radius) {
+ wlr_scene_rect_set_corner_radii(rect, corner_radii_all(corner_radius));
+}
+
+void wlr_scene_rect_set_corner_radii(struct wlr_scene_rect *rect, struct fx_corner_radii corners) {
+ if (fx_corner_radii_eq(rect->corners, corners)) {
return;
}
- rect->corner_radius = corner_radius;
rect->corners = corners;
scene_node_update(&rect->node, NULL);
}
void wlr_scene_rect_set_clipped_region(struct wlr_scene_rect *rect,
struct clipped_region clipped_region) {
- if (rect->clipped_region.corner_radius == clipped_region.corner_radius &&
- rect->clipped_region.corners == clipped_region.corners &&
+ if (fx_corner_radii_eq(rect->clipped_region.corners, clipped_region.corners) &&
wlr_box_equal(&rect->clipped_region.area, &clipped_region.area)) {
return;
}
- if (clipped_region.corner_radius && clipped_region.corners == CORNER_LOCATION_NONE) {
- wlr_log(WLR_ERROR, "Applying corner radius without specifying which"
- " corners to round for rect: %p", rect);
- }
rect->clipped_region = clipped_region;
scene_node_update(&rect->node, NULL);
}
@@ -1013,16 +1069,11 @@ void wlr_scene_shadow_set_color(struct wlr_scene_shadow *shadow, const float col
void wlr_scene_shadow_set_clipped_region(struct wlr_scene_shadow *shadow,
struct clipped_region clipped_region) {
- if (shadow->clipped_region.corner_radius == clipped_region.corner_radius &&
- shadow->clipped_region.corners == clipped_region.corners &&
+ if (fx_corner_radii_eq(shadow->clipped_region.corners, clipped_region.corners) &&
wlr_box_equal(&shadow->clipped_region.area, &clipped_region.area)) {
return;
}
- if (clipped_region.corner_radius && clipped_region.corners == CORNER_LOCATION_NONE) {
- wlr_log(WLR_ERROR, "Applying corner radius without specifying which"
- " corners to round for shadow: %p", shadow);
- }
shadow->clipped_region = clipped_region;
scene_node_update(&shadow->node, NULL);
}
@@ -1040,6 +1091,123 @@ static void mark_all_optimized_blur_nodes_dirty(struct wlr_scene_node *node) {
}
}
+struct wlr_scene_blur *wlr_scene_blur_create(struct wlr_scene_tree *parent,
+ int width, int height) {
+ struct wlr_scene_blur *blur = calloc(1, sizeof(*blur));
+ if (blur == NULL) {
+ return NULL;
+ }
+ assert(parent);
+ scene_node_init(&blur->node, WLR_SCENE_NODE_BLUR, parent);
+
+ blur->alpha = 1.0f;
+ blur->strength = 1.0f;
+ blur->clipped_region = (struct clipped_region){0};
+ blur->corners = corner_radii_all(0);
+ blur->should_only_blur_bottom_layer = false;
+ blur->transparency_mask_source = linked_node_init();
+ blur->width = width;
+ blur->height = height;
+
+ scene_node_update(&blur->node, NULL);
+
+ return blur;
+}
+
+void wlr_scene_blur_set_size(struct wlr_scene_blur *blur, int width, int height) {
+ if (blur->width == width && blur->height == height) {
+ return;
+ }
+
+ blur->width = width;
+ blur->height = height;
+
+ scene_node_update(&blur->node, NULL);
+}
+
+inline void wlr_scene_blur_set_corner_radius(struct wlr_scene_blur *blur, int corner_radius) {
+ wlr_scene_blur_set_corner_radii(blur, corner_radii_all(corner_radius));
+}
+
+void wlr_scene_blur_set_corner_radii(struct wlr_scene_blur *blur, struct fx_corner_radii corners) {
+ if (fx_corner_radii_eq(blur->corners, corners)) {
+ return;
+ }
+
+ blur->corners = corners;
+ scene_node_update(&blur->node, NULL);
+}
+
+void wlr_scene_blur_set_should_only_blur_bottom_layer(struct wlr_scene_blur *blur,
+ bool should_only_blur_bottom_layer) {
+ if (blur->should_only_blur_bottom_layer == should_only_blur_bottom_layer) {
+ return;
+ }
+
+ blur->should_only_blur_bottom_layer = should_only_blur_bottom_layer;
+ scene_node_update(&blur->node, NULL);
+}
+
+void wlr_scene_blur_set_transparency_mask_source(struct wlr_scene_blur *blur,
+ struct wlr_scene_buffer *source) {
+ if (source == NULL && blur->transparency_mask_source.link == NULL) {
+ return;
+ }
+
+ if (source != NULL && linked_nodes_are_linked(&blur->transparency_mask_source, &source->blur)) {
+ return;
+ }
+
+ linked_node_destroy(&blur->transparency_mask_source);
+ linked_node_destroy(&source->blur);
+
+ if (source != NULL) {
+ linked_node_init_link(&blur->transparency_mask_source, &source->blur);
+ }
+
+ scene_node_update(&blur->node, NULL);
+}
+
+struct wlr_scene_buffer *wlr_scene_blur_get_transparency_mask_source(
+ struct wlr_scene_blur *blur) {
+ struct linked_node *node = linked_nodes_get_sibling(&blur->transparency_mask_source);
+ if (node == NULL) {
+ return NULL;
+ }
+
+ struct wlr_scene_buffer *output = wl_container_of(node, output, blur);
+ return output;
+}
+
+void wlr_scene_blur_set_alpha(struct wlr_scene_blur *blur, float alpha) {
+ if (blur->alpha == alpha) {
+ return;
+ }
+
+ blur->alpha = alpha;
+ scene_node_update(&blur->node, NULL);
+}
+
+void wlr_scene_blur_set_strength(struct wlr_scene_blur *blur, float strength) {
+ if (blur->strength == strength) {
+ return;
+ }
+
+ blur->strength = strength;
+ scene_node_update(&blur->node, NULL);
+}
+
+void wlr_scene_blur_set_clipped_region(struct wlr_scene_blur *blur,
+ struct clipped_region clipped_region) {
+ if (fx_corner_radii_eq(blur->clipped_region.corners, clipped_region.corners) &&
+ wlr_box_equal(&blur->clipped_region.area, &clipped_region.area)) {
+ return;
+ }
+
+ blur->clipped_region = clipped_region;
+ scene_node_update(&blur->node, NULL);
+}
+
void wlr_scene_set_blur_data(struct wlr_scene *scene, int num_passes,
int radius, float noise, float brightness, float contrast, float saturation) {
struct blur_data *buff_data = &scene->blur_data;
@@ -1185,11 +1353,9 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent,
wl_list_init(&scene_buffer->renderer_destroy.link);
scene_buffer->opacity = 1;
- scene_buffer->corner_radius = 0;
- scene_buffer->backdrop_blur = false;
- scene_buffer->backdrop_blur_optimized = false;
- scene_buffer->backdrop_blur_ignore_transparent = true;
- scene_buffer->corners = CORNER_LOCATION_NONE;
+ scene_buffer->corners = corner_radii_none();
+
+ scene_buffer->blur = linked_node_init();
scene_buffer_set_buffer(scene_buffer, buffer);
scene_node_update(&scene_buffer->node, NULL);
@@ -1422,9 +1588,9 @@ void wlr_scene_buffer_set_transform(struct wlr_scene_buffer *scene_buffer,
}
void wlr_scene_buffer_send_frame_done(struct wlr_scene_buffer *scene_buffer,
- struct timespec *now) {
+ struct wlr_scene_frame_done_event *event) {
if (!pixman_region32_empty(&scene_buffer->node.visible)) {
- wl_signal_emit_mutable(&scene_buffer->events.frame_done, now);
+ wl_signal_emit_mutable(&scene_buffer->events.frame_done, event);
}
}
@@ -1449,43 +1615,58 @@ void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer,
scene_node_update(&scene_buffer->node, NULL);
}
-void wlr_scene_buffer_set_corner_radius(struct wlr_scene_buffer *scene_buffer,
- int radii, enum corner_location corners) {
- if (scene_buffer->corner_radius == radii
- && scene_buffer->corners == corners) {
+void wlr_scene_buffer_set_transfer_function(struct wlr_scene_buffer *scene_buffer,
+ enum wlr_color_transfer_function transfer_function) {
+ if (scene_buffer->transfer_function == transfer_function) {
+ return;
+ }
+
+ scene_buffer->transfer_function = transfer_function;
+ scene_node_update(&scene_buffer->node, NULL);
+}
+
+void wlr_scene_buffer_set_primaries(struct wlr_scene_buffer *scene_buffer,
+ enum wlr_color_named_primaries primaries) {
+ if (scene_buffer->primaries == primaries) {
return;
}
- scene_buffer->corner_radius = radii;
- scene_buffer->corners = corners;
+ scene_buffer->primaries = primaries;
scene_node_update(&scene_buffer->node, NULL);
}
-void wlr_scene_buffer_set_backdrop_blur(struct wlr_scene_buffer *scene_buffer,
- bool enabled) {
- if (scene_buffer->backdrop_blur == enabled) {
+void wlr_scene_buffer_set_color_encoding(struct wlr_scene_buffer *scene_buffer,
+ enum wlr_color_encoding color_encoding) {
+ if (scene_buffer->color_encoding == color_encoding) {
return;
}
- scene_buffer->backdrop_blur = enabled;
+
+ scene_buffer->color_encoding = color_encoding;
scene_node_update(&scene_buffer->node, NULL);
}
-void wlr_scene_buffer_set_backdrop_blur_optimized(struct wlr_scene_buffer *scene_buffer,
- bool enabled) {
- if (scene_buffer->backdrop_blur_optimized == enabled) {
+void wlr_scene_buffer_set_color_range(struct wlr_scene_buffer *scene_buffer,
+ enum wlr_color_range color_range) {
+ if (scene_buffer->color_range == color_range) {
return;
}
- scene_buffer->backdrop_blur_optimized = enabled;
+
+ scene_buffer->color_range = color_range;
scene_node_update(&scene_buffer->node, NULL);
}
-void wlr_scene_buffer_set_backdrop_blur_ignore_transparent(
- struct wlr_scene_buffer *scene_buffer, bool enabled) {
- if (scene_buffer->backdrop_blur_ignore_transparent == enabled) {
+inline void wlr_scene_buffer_set_corner_radius(struct wlr_scene_buffer *scene_buffer,
+ int radii) {
+ wlr_scene_buffer_set_corner_radii(scene_buffer, corner_radii_all(radii));
+}
+
+void wlr_scene_buffer_set_corner_radii(struct wlr_scene_buffer *scene_buffer,
+ struct fx_corner_radii corner_radii) {
+ if (fx_corner_radii_eq(scene_buffer->corners, corner_radii)) {
return;
}
- scene_buffer->backdrop_blur_ignore_transparent = enabled;
+ scene_buffer->corners = corner_radii;
scene_node_update(&scene_buffer->node, NULL);
}
@@ -1511,30 +1692,18 @@ static struct wlr_texture *scene_buffer_get_texture(
return texture;
}
-static void scene_node_get_size(struct wlr_scene_node *node,
- int *width, int *height) {
+void scene_node_get_size(struct wlr_scene_node *node, int *width, int *height) {
*width = 0;
*height = 0;
switch (node->type) {
case WLR_SCENE_NODE_TREE:
return;
- case WLR_SCENE_NODE_OPTIMIZED_BLUR:;
- struct wlr_scene_optimized_blur *scene_blur =
- wlr_scene_optimized_blur_from_node(node);
- *width = scene_blur->width;
- *height = scene_blur->height;
- break;
case WLR_SCENE_NODE_RECT:;
struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node);
*width = scene_rect->width;
*height = scene_rect->height;
break;
- case WLR_SCENE_NODE_SHADOW:;
- struct wlr_scene_shadow *scene_shadow = wlr_scene_shadow_from_node(node);
- *width = scene_shadow->width;
- *height = scene_shadow->height;
- break;
case WLR_SCENE_NODE_BUFFER:;
struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
if (scene_buffer->dst_width > 0 && scene_buffer->dst_height > 0) {
@@ -1546,6 +1715,22 @@ static void scene_node_get_size(struct wlr_scene_node *node,
wlr_output_transform_coords(scene_buffer->transform, width, height);
}
break;
+ case WLR_SCENE_NODE_SHADOW:;
+ struct wlr_scene_shadow *scene_shadow = wlr_scene_shadow_from_node(node);
+ *width = scene_shadow->width;
+ *height = scene_shadow->height;
+ break;
+ case WLR_SCENE_NODE_OPTIMIZED_BLUR:;
+ struct wlr_scene_optimized_blur *scene_blur =
+ wlr_scene_optimized_blur_from_node(node);
+ *width = scene_blur->width;
+ *height = scene_blur->height;
+ break;
+ case WLR_SCENE_NODE_BLUR:;
+ struct wlr_scene_blur *blur = wlr_scene_blur_from_node(node);
+ *width = blur->width;
+ *height = blur->height;
+ break;
}
}
@@ -1728,7 +1913,8 @@ static bool scene_node_at_iterator(struct wlr_scene_node *node,
return false;
}
} else if (node->type == WLR_SCENE_NODE_SHADOW
- || node->type == WLR_SCENE_NODE_OPTIMIZED_BLUR) {
+ || node->type == WLR_SCENE_NODE_OPTIMIZED_BLUR
+ || node->type == WLR_SCENE_NODE_BLUR) {
// Disable interaction
return false;
}
@@ -1772,8 +1958,14 @@ struct render_list_entry {
int x, y;
};
+static float get_luminance_multiplier(const struct wlr_color_luminances *src_lum,
+ const struct wlr_color_luminances *dst_lum) {
+ return (dst_lum->reference / src_lum->reference) * (src_lum->max / dst_lum->max);
+}
+
static void scene_entry_render(struct render_list_entry *entry, const struct render_data *data) {
struct wlr_scene_node *node = entry->node;
+ struct fx_gles_render_pass *fx_pass = fx_get_render_pass(data->render_pass);
pixman_region32_t render_region;
pixman_region32_init(&render_region);
@@ -1812,62 +2004,22 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren
break;
case WLR_SCENE_NODE_RECT:;
struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node);
- enum corner_location rect_corners = scene_rect->corners;
-
- corner_location_transform(node_transform, &rect_corners);
-
- // blur
- bool has_alpha = !pixman_region32_empty(&opaque);
- if (has_alpha && scene_rect->backdrop_blur &&
- is_scene_blur_enabled(&scene->blur_data)) {
- pixman_region32_t opaque_region;
- pixman_region32_init(&opaque_region);
- scene_node_opaque_region(node, x, y, &opaque_region);
- logical_to_buffer_coords(&opaque_region, data, false);
-
- /* TODO: should this be configurable? Borked when not 1.0, probably due to
- lack of premultiplication in the frag shader
- */
- float blur_alpha = 1.0;
-
- struct fx_render_blur_pass_options blur_options = {
- .tex_options = {
- .base = (struct wlr_render_texture_options) {
- .texture = NULL,
- .src_box = (struct wlr_fbox){0},
- .dst_box = dst_box,
- .transform = WL_OUTPUT_TRANSFORM_NORMAL,
- .clip = &render_region,
- .alpha = &blur_alpha,
- .filter_mode = WLR_SCALE_FILTER_BILINEAR,
- .blend_mode = WLR_RENDER_BLEND_MODE_PREMULTIPLIED,
- },
- .clip_box = &dst_box,
- .corner_radius = scene_rect->corner_radius * data->scale,
- .corners = rect_corners,
- .discard_transparent = false,
- },
- .opaque_region = &opaque_region,
- .use_optimized_blur = scene_rect->backdrop_blur_optimized,
- .blur_data = &scene->blur_data,
- .ignore_transparent = false,
- };
- // Render the actual blur behind the surface
- fx_render_pass_add_blur(data->render_pass, &blur_options);
-
- pixman_region32_fini(&opaque_region);
+ if (scene_rect->color[3] == 0.0) {
+ break;
}
+ struct fx_corner_radii rect_corners = scene_rect->corners;
+
+ fx_corner_radii_transform(node_transform, &rect_corners);
struct wlr_box rect_clipped_region_box = scene_rect->clipped_region.area;
- int rect_clipped_region_corner_radius = scene_rect->clipped_region.corner_radius;
- enum corner_location rect_clipped_corners = scene_rect->clipped_region.corners;
+ struct fx_corner_radii rect_clipped_corners = scene_rect->clipped_region.corners;
// Node relative -> Root relative
rect_clipped_region_box.x += x;
rect_clipped_region_box.y += y;
transform_output_box(&rect_clipped_region_box, data);
- corner_location_transform(node_transform, &rect_clipped_corners);
+ fx_corner_radii_transform(node_transform, &rect_clipped_corners);
struct fx_render_rect_options rect_options = {
.base = {
@@ -1882,93 +2034,32 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren
},
.clipped_region = {
.area = rect_clipped_region_box,
- .corner_radius = rect_clipped_region_corner_radius * data->scale,
- .corners = rect_clipped_corners,
+ .corners = fx_corner_radii_scale(rect_clipped_corners, data->scale),
},
};
- if ((scene_rect->corner_radius && rect_corners != CORNER_LOCATION_NONE) || scene_rect->fade_inset > 0) {
+ // TODO: Use the base wlr_render_pass_add_rect as a fast-path in the future
+ if (!fx_corner_radii_is_empty(&rect_corners) || scene_rect->fade_inset > 0) {
struct fx_render_rounded_rect_options rounded_rect_options = {
.base = rect_options.base,
- .corner_radius = scene_rect->corner_radius * data->scale,
- .corners = rect_corners,
+ .corners = fx_corner_radii_scale(rect_corners, data->scale),
.clipped_region = rect_options.clipped_region,
.fade_inset = scene_rect->fade_inset * data->scale,
};
- fx_render_pass_add_rounded_rect(data->render_pass, &rounded_rect_options);
+ fx_render_pass_add_rounded_rect(fx_pass, &rounded_rect_options);
} else {
- fx_render_pass_add_rect(data->render_pass, &rect_options);
- }
- break;
- case WLR_SCENE_NODE_OPTIMIZED_BLUR:;
- struct wlr_scene_optimized_blur *scene_blur = wlr_scene_optimized_blur_from_node(node);
- // Re-render the optimized blur buffer when needed
- if (data->has_blur && is_scene_blur_enabled(&scene->blur_data)
- && scene_blur->dirty) {
- const float opacity = 1.0f;
- enum wl_output_transform transform =
- wlr_output_transform_invert(data->transform);
- transform = wlr_output_transform_compose(transform, data->transform);
- struct fx_render_blur_pass_options blur_options = {
- .tex_options = {
- .base = {
- .transform = transform,
- .alpha = &opacity,
- .blend_mode = WLR_RENDER_BLEND_MODE_NONE,
- .dst_box = dst_box,
- },
- .clip_box = &dst_box,
- .corner_radius = 0,
- .discard_transparent = false,
- },
- .blur_data = &scene->blur_data,
- };
- bool result = fx_render_pass_add_optimized_blur(data->render_pass, &blur_options);
- if (result) {
- scene_blur->dirty = false;
- }
+ fx_render_pass_add_rect(fx_pass, &rect_options);
}
break;
- case WLR_SCENE_NODE_SHADOW:;
- struct wlr_scene_shadow *scene_shadow = wlr_scene_shadow_from_node(node);
-
- struct wlr_box shadow_clipped_region_box = scene_shadow->clipped_region.area;
- int shadow_clipped_region_corner_radius = scene_shadow->clipped_region.corner_radius;
- enum corner_location shadow_clipped_corners = scene_shadow->clipped_region.corners;
-
- // Node relative -> Root relative
- shadow_clipped_region_box.x += x;
- shadow_clipped_region_box.y += y;
-
- transform_output_box(&shadow_clipped_region_box, data);
- corner_location_transform(node_transform, &shadow_clipped_corners);
-
- struct fx_render_box_shadow_options shadow_options = {
- .box = dst_box,
- .clipped_region = {
- .area = shadow_clipped_region_box,
- .corner_radius = shadow_clipped_region_corner_radius * data->scale,
- .corners = shadow_clipped_corners,
- },
- .blur_sigma = scene_shadow->blur_sigma,
- .corner_radius = scene_shadow->corner_radius * data->scale,
- .color = {
- .r = scene_shadow->color[0],
- .g = scene_shadow->color[1],
- .b = scene_shadow->color[2],
- .a = scene_shadow->color[3],
- },
- .clip = &render_region,
- };
- fx_render_pass_add_box_shadow(data->render_pass, &shadow_options);
- break;
case WLR_SCENE_NODE_BUFFER:;
struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
- enum corner_location buffer_corners = scene_buffer->corners;
+ struct fx_corner_radii buffer_corners = scene_buffer->corners;
if (scene_buffer->is_single_pixel_buffer) {
+ // TODO: Render blur/rounded corners/etc here:
+
// Render the buffer as a rect, this is likely to be more efficient
- wlr_render_pass_add_rect(&data->render_pass->base, &(struct wlr_render_rect_options){
+ wlr_render_pass_add_rect(data->render_pass, &(struct wlr_render_rect_options){
.box = dst_box,
.color = {
.r = (float)scene_buffer->single_pixel_buffer_color[0] / (float)UINT32_MAX,
@@ -1992,53 +2083,20 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren
enum wl_output_transform transform =
wlr_output_transform_invert(scene_buffer->transform);
transform = wlr_output_transform_compose(transform, data->transform);
- corner_location_transform(transform, &buffer_corners);
-
- // Blur
- if (scene_buffer->backdrop_blur && is_scene_blur_enabled(&scene->blur_data)) {
- pixman_region32_t opaque_region;
- pixman_region32_init(&opaque_region);
-
- bool has_alpha = !pixman_region32_empty(&opaque);
- scene_node_opaque_region(node, x, y, &opaque_region);
- logical_to_buffer_coords(&opaque_region, data, false);
-
- if (has_alpha) {
- // Translate the opaque_region by the potential clipping offset.
- // Fixes GTK CSD offsetting the opaque_region
- pixman_region32_translate(&opaque_region,
- -scene_buffer->src_box.x, -scene_buffer->src_box.y);
-
- // TODO: should I be configurable? We should probably move blur to a node
- float blur_alpha = 1.0;
- struct fx_render_blur_pass_options blur_options = {
- .tex_options = {
- .base = (struct wlr_render_texture_options) {
- .texture = texture,
- .src_box = scene_buffer->src_box,
- .dst_box = dst_box,
- .transform = WL_OUTPUT_TRANSFORM_NORMAL,
- .clip = &render_region, // Render with the smaller region, clipping CSD
- .alpha = &blur_alpha,
- .filter_mode = WLR_SCALE_FILTER_BILINEAR,
- },
- .clip_box = &dst_box,
- .corner_radius = scene_buffer->corner_radius * data->scale,
- .corners = buffer_corners,
- .discard_transparent = false,
- },
- .opaque_region = &opaque_region,
- .use_optimized_blur = scene_buffer->backdrop_blur_optimized,
- .blur_data = &scene->blur_data,
- .ignore_transparent = scene_buffer->backdrop_blur_ignore_transparent,
- };
- // Render the actual blur behind the surface
- fx_render_pass_add_blur(data->render_pass, &blur_options);
+ fx_corner_radii_transform(transform, &buffer_corners);
- }
- pixman_region32_fini(&opaque_region);
+ struct wlr_color_primaries primaries = {0};
+ if (scene_buffer->primaries != 0) {
+ wlr_color_primaries_from_named(&primaries, scene_buffer->primaries);
}
+ struct wlr_color_luminances src_lum, srgb_lum;
+ wlr_color_transfer_function_get_default_luminance(
+ scene_buffer->transfer_function, &src_lum);
+ wlr_color_transfer_function_get_default_luminance(
+ WLR_COLOR_TRANSFER_FUNCTION_SRGB, &srgb_lum);
+ float luminance_multiplier = get_luminance_multiplier(&src_lum, &srgb_lum);
+
struct fx_render_texture_options tex_options = {
.base = (struct wlr_render_texture_options){
.texture = texture,
@@ -2051,29 +2109,142 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren
.blend_mode = !data->output->scene->calculate_visibility ||
!pixman_region32_empty(&opaque) ?
WLR_RENDER_BLEND_MODE_PREMULTIPLIED : WLR_RENDER_BLEND_MODE_NONE,
+ .transfer_function = scene_buffer->transfer_function,
+ .primaries = scene_buffer->primaries != 0 ? &primaries : NULL,
+ .color_encoding = scene_buffer->color_encoding,
+ .color_range = scene_buffer->color_range,
+ .luminance_multiplier = &luminance_multiplier,
.wait_timeline = scene_buffer->wait_timeline,
.wait_point = scene_buffer->wait_point,
},
.clip_box = &dst_box,
- .corners = buffer_corners,
- .corner_radius = scene_buffer->corner_radius * data->scale,
+ .corners = fx_corner_radii_scale(buffer_corners, data->scale),
+ .clipped_region = {0},
};
- fx_render_pass_add_texture(data->render_pass, &tex_options);
+ // TODO: Use the base wlr_render_pass_add_texture as a fast-path in the future
+ fx_render_pass_add_texture(fx_pass, &tex_options);
struct wlr_scene_output_sample_event sample_event = {
.output = data->output,
.direct_scanout = false,
+ .release_timeline = data->output->in_timeline,
+ .release_point = data->output->in_point,
};
wl_signal_emit_mutable(&scene_buffer->events.output_sample, &sample_event);
+
if (entry->highlight_transparent_region) {
- wlr_render_pass_add_rect(&data->render_pass->base, &(struct wlr_render_rect_options){
+ wlr_render_pass_add_rect(data->render_pass, &(struct wlr_render_rect_options){
.box = dst_box,
.color = { .r = 0, .g = 0.3, .b = 0, .a = 0.3 },
.clip = &opaque,
});
}
break;
+ case WLR_SCENE_NODE_SHADOW:;
+ struct wlr_scene_shadow *scene_shadow = wlr_scene_shadow_from_node(node);
+
+ struct wlr_box shadow_clipped_region_box = scene_shadow->clipped_region.area;
+ struct fx_corner_radii shadow_clipped_corners = scene_shadow->clipped_region.corners;
+
+ // Node relative -> Root relative
+ shadow_clipped_region_box.x += x;
+ shadow_clipped_region_box.y += y;
+
+ transform_output_box(&shadow_clipped_region_box, data);
+ fx_corner_radii_transform(node_transform, &shadow_clipped_corners);
+
+ struct fx_render_box_shadow_options shadow_options = {
+ .box = dst_box,
+ .clipped_region = {
+ .area = shadow_clipped_region_box,
+ .corners = fx_corner_radii_scale(shadow_clipped_corners, data->scale),
+ },
+ .blur_sigma = scene_shadow->blur_sigma,
+ .corner_radius = scene_shadow->corner_radius * data->scale,
+ .color = {
+ .r = scene_shadow->color[0],
+ .g = scene_shadow->color[1],
+ .b = scene_shadow->color[2],
+ .a = scene_shadow->color[3],
+ },
+ .clip = &render_region,
+ };
+ fx_render_pass_add_box_shadow(fx_pass, &shadow_options);
+ break;
+ case WLR_SCENE_NODE_OPTIMIZED_BLUR:;
+ struct wlr_scene_optimized_blur *scene_blur = wlr_scene_optimized_blur_from_node(node);
+ // Re-render the optimized blur buffer when needed. Retry rendering
+ // until there's a visible blur_node.
+ if (fx_pass->has_blur && is_scene_blur_enabled(&scene->blur_data)
+ && scene_blur->dirty) {
+ const float opacity = 1.0f;
+ enum wl_output_transform transform =
+ wlr_output_transform_invert(data->transform);
+ transform = wlr_output_transform_compose(transform, data->transform);
+ struct fx_render_blur_pass_options blur_options = {
+ .tex_options = {
+ .base = {
+ .transform = transform,
+ .alpha = &opacity,
+ .blend_mode = WLR_RENDER_BLEND_MODE_NONE,
+ .dst_box = dst_box,
+ },
+ .clip_box = &dst_box,
+ .discard_transparent = false,
+ .clipped_region = {0}
+ },
+ .blur_data = &scene->blur_data,
+ .blur_strength = 1.0f,
+ };
+ bool result = fx_render_pass_add_optimized_blur(fx_pass, &blur_options);
+ if (result) {
+ scene_blur->dirty = false;
+ }
+ }
+ break;
+ case WLR_SCENE_NODE_BLUR:;
+ struct wlr_scene_blur *blur = wlr_scene_blur_from_node(node);
+
+ struct wlr_texture *tex = NULL;
+ struct wlr_scene_buffer *mask = wlr_scene_blur_get_transparency_mask_source(blur);
+
+ enum wl_output_transform mask_transform = WL_OUTPUT_TRANSFORM_NORMAL;
+
+ struct wlr_fbox mask_src_box = {0};
+ if (mask != NULL) {
+ tex = scene_buffer_get_texture(mask, data->output->output->renderer);
+ mask_transform = wlr_output_transform_invert(mask->transform);
+ mask_transform = wlr_output_transform_compose(mask_transform, data->transform);
+ mask_src_box = mask->src_box;
+ }
+
+ struct fx_corner_radii blur_corners = blur->corners;
+ fx_corner_radii_transform(node_transform, &blur_corners);
+
+ struct fx_render_blur_pass_options blur_options = {
+ .tex_options = {
+ .base = (struct wlr_render_texture_options) {
+ .texture = tex,
+ .src_box = mask_src_box,
+ .dst_box = dst_box,
+ .transform = mask_transform,
+ .clip = &render_region,
+ .alpha = &blur->alpha,
+ .filter_mode = WLR_SCALE_FILTER_BILINEAR,
+ .blend_mode = WLR_RENDER_BLEND_MODE_PREMULTIPLIED,
+ },
+ .clip_box = &dst_box,
+ .corners = fx_corner_radii_scale(blur_corners, data->scale),
+ .discard_transparent = false,
+ },
+ .use_optimized_blur = blur->should_only_blur_bottom_layer,
+ .blur_data = &scene->blur_data,
+ .ignore_transparent = mask != NULL,
+ .blur_strength = blur->strength,
+ };
+ fx_render_pass_add_blur(fx_pass, &blur_options);
+ break;
}
pixman_region32_fini(&opaque);
@@ -2110,6 +2281,8 @@ static void scene_handle_gamma_control_manager_v1_set_gamma(struct wl_listener *
output->gamma_lut_changed = true;
output->gamma_lut = event->control;
+ wlr_color_transform_unref(output->gamma_lut_color_transform);
+ output->gamma_lut_color_transform = wlr_gamma_control_v1_get_color_transform(event->control);
wlr_output_schedule_frame(output->output);
}
@@ -2127,11 +2300,13 @@ static void scene_handle_gamma_control_manager_v1_destroy(struct wl_listener *li
wl_list_for_each(output, &scene->outputs, link) {
output->gamma_lut_changed = false;
output->gamma_lut = NULL;
+ wlr_color_transform_unref(output->gamma_lut_color_transform);
+ output->gamma_lut_color_transform = NULL;
}
}
void wlr_scene_set_gamma_control_manager_v1(struct wlr_scene *scene,
- struct wlr_gamma_control_manager_v1 *gamma_control) {
+ struct wlr_gamma_control_manager_v1 *gamma_control) {
assert(scene->gamma_control_manager_v1 == NULL);
scene->gamma_control_manager_v1 = gamma_control;
@@ -2143,6 +2318,21 @@ void wlr_scene_set_gamma_control_manager_v1(struct wlr_scene *scene,
wl_signal_add(&gamma_control->events.set_gamma, &scene->gamma_control_manager_v1_set_gamma);
}
+static void scene_handle_color_manager_v1_destroy(struct wl_listener *listener, void *data) {
+ struct wlr_scene *scene = wl_container_of(listener, scene, color_manager_v1_destroy);
+ wl_list_remove(&scene->color_manager_v1_destroy.link);
+ wl_list_init(&scene->color_manager_v1_destroy.link);
+ scene->color_manager_v1 = NULL;
+}
+
+void wlr_scene_set_color_manager_v1(struct wlr_scene *scene, struct wlr_color_manager_v1 *manager) {
+ assert(scene->color_manager_v1 == NULL);
+ scene->color_manager_v1 = manager;
+
+ scene->color_manager_v1_destroy.notify = scene_handle_color_manager_v1_destroy;
+ wl_signal_add(&manager->events.destroy, &scene->color_manager_v1_destroy);
+}
+
static void scene_output_handle_destroy(struct wlr_addon *addon) {
struct wlr_scene_output *scene_output =
wl_container_of(addon, scene_output, addon);
@@ -2189,7 +2379,7 @@ static void scene_output_handle_commit(struct wl_listener *listener, void *data)
if (state->committed & WLR_OUTPUT_STATE_BUFFER) {
if (state->committed & WLR_OUTPUT_STATE_DAMAGE) {
pixman_region32_subtract(&scene_output->pending_commit_damage,
- &scene_output->pending_commit_damage, &state->damage);
+ &scene_output->pending_commit_damage, &state->damage);
} else {
pixman_region32_fini(&scene_output->pending_commit_damage);
pixman_region32_init(&scene_output->pending_commit_damage);
@@ -2211,6 +2401,11 @@ static void scene_output_handle_commit(struct wl_listener *listener, void *data)
wlr_output_schedule_frame(scene_output->output);
}
+ TRACY_WHEN_CONNECTED({
+ // Queue a new frame as soon as possible when profiling
+ wlr_output_schedule_frame(scene_output->output);
+ })
+
// Next time the output is enabled, try to re-apply the gamma LUT
if (scene_output->scene->gamma_control_manager_v1 &&
(state->committed & WLR_OUTPUT_STATE_ENABLED) &&
@@ -2275,7 +2470,10 @@ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene,
if (drm_fd >= 0 && output->backend->features.timeline &&
output->renderer != NULL && output->renderer->features.timeline) {
scene_output->in_timeline = wlr_drm_syncobj_timeline_create(drm_fd);
- if (scene_output->in_timeline == NULL) {
+ scene_output->out_timeline = wlr_drm_syncobj_timeline_create(drm_fd);
+ if (scene_output->in_timeline == NULL || scene_output->out_timeline == NULL) {
+ wlr_drm_syncobj_timeline_unref(scene_output->in_timeline);
+ wlr_drm_syncobj_timeline_unref(scene_output->out_timeline);
return NULL;
}
}
@@ -2330,7 +2528,18 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) {
wl_list_remove(&scene_output->output_commit.link);
wl_list_remove(&scene_output->output_damage.link);
wl_list_remove(&scene_output->output_needs_frame.link);
- wlr_drm_syncobj_timeline_unref(scene_output->in_timeline);
+ if (scene_output->in_timeline != NULL) {
+ wlr_drm_syncobj_timeline_signal(scene_output->in_timeline, UINT64_MAX);
+ wlr_drm_syncobj_timeline_unref(scene_output->in_timeline);
+ }
+ if (scene_output->out_timeline != NULL) {
+ wlr_drm_syncobj_timeline_signal(scene_output->out_timeline, UINT64_MAX);
+ wlr_drm_syncobj_timeline_unref(scene_output->out_timeline);
+ }
+ wlr_color_transform_unref(scene_output->gamma_lut_color_transform);
+ wlr_color_transform_unref(scene_output->prev_gamma_lut_color_transform);
+ wlr_color_transform_unref(scene_output->prev_supplied_color_transform);
+ wlr_color_transform_unref(scene_output->combined_color_transform);
wl_array_release(&scene_output->render_list);
free(scene_output);
}
@@ -2364,18 +2573,16 @@ static bool scene_node_invisible(struct wlr_scene_node *node) {
return true;
} else if (node->type == WLR_SCENE_NODE_RECT) {
struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node);
-
+ // TODO: Check if clipped region covers whole rect?
return rect->color[3] == 0.f;
- } else if (node->type == WLR_SCENE_NODE_SHADOW) {
- struct wlr_scene_shadow *shadow = wlr_scene_shadow_from_node(node);
-
- return shadow->color[3] == 0.f;
- } else if (node->type == WLR_SCENE_NODE_OPTIMIZED_BLUR) {
- return false;
} else if (node->type == WLR_SCENE_NODE_BUFFER) {
struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
return buffer->buffer == NULL && buffer->texture == NULL;
+ } else if (node->type == WLR_SCENE_NODE_SHADOW) {
+ struct wlr_scene_shadow *shadow = wlr_scene_shadow_from_node(node);
+
+ return shadow->color[3] == 0.f;
}
return false;
@@ -2395,7 +2602,18 @@ static bool scene_buffer_is_black_opaque(struct wlr_scene_buffer *scene_buffer)
scene_buffer->single_pixel_buffer_color[1] == 0 &&
scene_buffer->single_pixel_buffer_color[2] == 0 &&
scene_buffer->single_pixel_buffer_color[3] == UINT32_MAX &&
- scene_buffer->opacity == 1.0;
+ scene_buffer->opacity == 1.0 &&
+ fx_corner_radii_is_empty(&scene_buffer->corners);
+}
+
+static bool scene_rect_is_black_opaque(struct wlr_scene_rect *scene_rect) {
+ return scene_rect->color[0] == 0.f &&
+ scene_rect->color[1] == 0.f &&
+ scene_rect->color[2] == 0.f &&
+ scene_rect->color[3] == 1.f &&
+ fx_corner_radii_is_empty(&scene_rect->corners) &&
+ fx_corner_radii_is_empty(&scene_rect->clipped_region.corners) &&
+ wlr_box_empty(&scene_rect->clipped_region.area);
}
static bool construct_render_list_iterator(struct wlr_scene_node *node,
@@ -2413,9 +2631,8 @@ static bool construct_render_list_iterator(struct wlr_scene_node *node,
if (node->type == WLR_SCENE_NODE_RECT && data->calculate_visibility &&
(!data->fractional_scale || data->render_list->size == 0)) {
struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node);
- float *black = (float[4]){ 0.f, 0.f, 0.f, 1.f };
- if (memcmp(rect->color, black, sizeof(float) * 4) == 0) {
+ if (scene_rect_is_black_opaque(rect)) {
return false;
}
}
@@ -2482,12 +2699,41 @@ static void scene_buffer_send_dmabuf_feedback(const struct wlr_scene *scene,
return;
}
+ enum wl_output_transform preferred_buffer_transform = WL_OUTPUT_TRANSFORM_NORMAL;
+ if (options->scanout_primary_output != NULL) {
+ preferred_buffer_transform = options->scanout_primary_output->transform;
+ }
+
+ // TODO: also send wl_surface.preferred_buffer_transform when running with
+ // pure software rendering
+ wlr_surface_set_preferred_buffer_transform(surface->surface, preferred_buffer_transform);
wlr_linux_dmabuf_v1_set_surface_feedback(scene->linux_dmabuf_v1,
surface->surface, &feedback);
wlr_linux_dmabuf_feedback_v1_finish(&feedback);
}
+static bool color_management_is_scanout_allowed(const struct wlr_output_image_description *img_desc,
+ const struct wlr_scene_buffer *buffer) {
+ // Disallow scanout if the output has colorimetry information but buffer
+ // doesn't; allow it only if the output also lacks it.
+ if (buffer->transfer_function == 0 && buffer->primaries == 0) {
+ return img_desc == NULL;
+ }
+
+ // If the output has colorimetry information, the buffer must match it for
+ // direct scanout to be allowed.
+ if (img_desc != NULL) {
+ return img_desc->transfer_function == buffer->transfer_function &&
+ img_desc->primaries == buffer->primaries;
+ }
+ // If the output doesn't have colorimetry image description set, we can only
+ // scan out buffers with default colorimetry (gamma2.2 transfer and sRGB
+ // primaries) used in wlroots.
+ return buffer->transfer_function == WLR_COLOR_TRANSFER_FUNCTION_GAMMA22 &&
+ buffer->primaries == WLR_COLOR_NAMED_PRIMARIES_SRGB;
+}
+
enum scene_direct_scanout_result {
// This scene node is not a candidate for scanout
SCANOUT_INELIGIBLE,
@@ -2544,6 +2790,20 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout(
return SCANOUT_INELIGIBLE;
}
+ const struct wlr_output_image_description *img_desc = output_pending_image_description(scene_output->output, state);
+ if (!color_management_is_scanout_allowed(img_desc, buffer)) {
+ return SCANOUT_INELIGIBLE;
+ }
+
+ bool is_color_repr_none = buffer->color_encoding == WLR_COLOR_ENCODING_NONE &&
+ buffer->color_range == WLR_COLOR_RANGE_NONE;
+ bool is_color_repr_identity_full = buffer->color_encoding == WLR_COLOR_ENCODING_IDENTITY &&
+ buffer->color_range == WLR_COLOR_RANGE_FULL;
+
+ if (!(is_color_repr_none || is_color_repr_identity_full)) {
+ return SCANOUT_INELIGIBLE;
+ }
+
// We want to ensure optimal buffer selection, but as direct-scanout can be enabled and disabled
// on a frame-by-frame basis, we wait for a few frames to send the new format recommendations.
// Maybe we should only send feedback in this case if tests fail.
@@ -2585,6 +2845,12 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout(
if (buffer->wait_timeline != NULL) {
wlr_output_state_set_wait_timeline(&pending, buffer->wait_timeline, buffer->wait_point);
}
+
+ if (scene_output->out_timeline) {
+ scene_output->out_point++;
+ wlr_output_state_set_signal_timeline(&pending, scene_output->out_timeline, scene_output->out_point);
+ }
+
if (!wlr_output_test_state(scene_output->output, &pending)) {
wlr_output_state_finish(&pending);
return SCANOUT_CANDIDATE;
@@ -2596,6 +2862,8 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout(
struct wlr_scene_output_sample_event sample_event = {
.output = scene_output,
.direct_scanout = true,
+ .release_timeline = data->output->out_timeline,
+ .release_point = data->output->out_point,
};
wl_signal_emit_mutable(&buffer->events.output_sample, &sample_event);
return SCANOUT_SUCCESS;
@@ -2607,76 +2875,77 @@ bool wlr_scene_output_needs_frame(struct wlr_scene_output *scene_output) {
scene_output->gamma_lut_changed;
}
-static void apply_blur_region(struct wlr_scene_node *node,
- struct wlr_scene_output *scene_output, pixman_region32_t *blur_region) {
- int x, y;
- wlr_scene_node_coords(node, &x, &y);
+static bool should_blur_node_extend_damage(struct wlr_scene_node *node,
+ struct fx_gles_render_pass *fx_pass, struct blur_data *blur_data) {
+ switch (node->type) {
+ case WLR_SCENE_NODE_BLUR:;
+ struct wlr_scene_blur *blur_node = wlr_scene_blur_from_node(node);
+ // No artifact prevention needed when the whole blur is already
+ // rendered
+ if (blur_node->should_only_blur_bottom_layer && blur_node->strength == 1.0) {
+ fx_pass->has_blur = true;
+ return false;
+ }
+ // Apply the blur strength to avoid rendering more blur than
+ // what's needed
+ if (blur_node->strength < 1.0f) {
+ *blur_data = blur_data_apply_strength(blur_data, blur_node->strength);
+ }
+ break;
+ case WLR_SCENE_NODE_OPTIMIZED_BLUR:;
+ struct wlr_scene_optimized_blur *optimized_node =
+ wlr_scene_optimized_blur_from_node(node);
+ if (!optimized_node->dirty) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
- pixman_region32_t opaque_region;
- pixman_region32_init(&opaque_region);
- scene_node_opaque_region(node, x, y, &opaque_region);
- // Add the buffer to the blur_region if it's not fully opaque
- if (pixman_region32_empty(&opaque_region)) {
- struct wlr_box node_box = {
- .x = x - scene_output->x,
- .y = y - scene_output->y,
- };
- scene_node_get_size(node, &node_box.width, &node_box.height);
+ return is_scene_blur_enabled(blur_data);
+}
- struct wlr_output *output = scene_output->output;
+static bool apply_blur_region(struct wlr_scene_node *node, struct blur_data *blur_data,
+ struct render_data *render_data, struct wlr_output_state *output_state,
+ const pixman_region32_t *original_damage, pixman_region32_t *blur_padding_region) {
+ bool should_compensate_blur = false;
+ const int sample_size = blur_data_calc_size(blur_data);
- int output_width, output_height;
- wlr_output_transformed_resolution(output, &output_width, &output_height);
+ pixman_region32_t node_visible_region;
+ pixman_region32_init(&node_visible_region);
+ pixman_region32_copy(&node_visible_region, &node->visible);
+ pixman_region32_translate(&node_visible_region, -render_data->output->x, -render_data->output->y);
+ logical_to_buffer_coords(&node_visible_region, render_data, false);
- // Transform the box back to regular un-transformed units
- scale_box(&node_box, output->scale);
- wlr_box_transform(&node_box, &node_box,
- wlr_output_transform_invert(output->transform),
- output_width, output_height);
+ pixman_region32_t expanded_damage;
+ pixman_region32_init(&expanded_damage);
+ wlr_region_expand(&expanded_damage, original_damage, sample_size);
- pixman_region32_union_rect(blur_region, blur_region,
- node_box.x,
- node_box.y,
- node_box.width, node_box.height);
- }
- pixman_region32_fini(&opaque_region);
-}
+ pixman_region32_t intersection;
+ pixman_region32_init(&intersection);
+ if (pixman_region32_intersect(&intersection, &expanded_damage, &node_visible_region)) {
+ should_compensate_blur = true;
-static bool scene_output_has_blur(int list_len,
- struct render_list_entry *list_data, struct wlr_scene_output *scene_output,
- pixman_region32_t *blur_region) {
- if (scene_output->scene->blur_data.radius <= 0 ||
- scene_output->scene->blur_data.num_passes <= 0) {
- return false;
- }
+ // Expand the render damage to re-render surrounding blur nodes
+ pixman_region32_union(&render_data->damage, &render_data->damage, &intersection);
+ // Also make sure that the backend also knows about the new
+ // damage. Very important
+ output_state->committed |= WLR_OUTPUT_STATE_DAMAGE;
+ pixman_region32_union(&output_state->damage, &output_state->damage, &intersection);
- for (int i = list_len - 1; i >= 0; i--) {
- struct wlr_scene_node *node = list_data[i].node;
- switch (node->type) {
- case WLR_SCENE_NODE_BUFFER:;
- struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
- if (scene_buffer->backdrop_blur) {
- apply_blur_region(node, scene_output, blur_region);
- }
- break;
- case WLR_SCENE_NODE_RECT:;
- struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node);
- if (scene_rect->backdrop_blur) {
- apply_blur_region(node, scene_output, blur_region);
- }
- break;
- case WLR_SCENE_NODE_OPTIMIZED_BLUR:;
- struct wlr_scene_optimized_blur *scene_blur = wlr_scene_optimized_blur_from_node(node);
- if (scene_blur->dirty) {
- apply_blur_region(node, scene_output, blur_region);
- }
- break;
- default:
- // TODO: Add support for other node types
- break;
- }
+ // Expand it once more to get the blur padding region
+ // which is key for artifact removal :)
+ wlr_region_expand(&intersection, &intersection, sample_size);
+ // Don't re-add already added rectangles
+ pixman_region32_subtract(&intersection, &intersection, blur_padding_region);
+ pixman_region32_union(blur_padding_region, blur_padding_region, &intersection);
}
- return !pixman_region32_empty(blur_region);
+
+ pixman_region32_fini(&intersection);
+ pixman_region32_fini(&expanded_damage);
+ pixman_region32_fini(&node_visible_region);
+ return should_compensate_blur;
}
bool wlr_scene_output_commit(struct wlr_scene_output *scene_output,
@@ -2713,16 +2982,15 @@ static void scene_output_state_attempt_gamma(struct wlr_scene_output *scene_outp
return;
}
- if (!wlr_gamma_control_v1_apply(scene_output->gamma_lut, &gamma_pending)) {
- wlr_output_state_finish(&gamma_pending);
- return;
- }
-
+ wlr_output_state_set_color_transform(&gamma_pending, scene_output->gamma_lut_color_transform);
scene_output->gamma_lut_changed = false;
+
if (!wlr_output_test_state(scene_output->output, &gamma_pending)) {
wlr_gamma_control_v1_send_failed_and_destroy(scene_output->gamma_lut);
scene_output->gamma_lut = NULL;
+ wlr_color_transform_unref(scene_output->gamma_lut_color_transform);
+ scene_output->gamma_lut_color_transform = NULL;
wlr_output_state_finish(&gamma_pending);
return;
}
@@ -2731,6 +2999,79 @@ static void scene_output_state_attempt_gamma(struct wlr_scene_output *scene_outp
wlr_output_state_finish(&gamma_pending);
}
+static bool scene_output_combine_color_transforms(
+ struct wlr_scene_output *scene_output, struct wlr_color_transform *supplied,
+ const struct wlr_output_image_description *img_desc, bool render_gamma_lut) {
+ bool result = false;
+ struct wlr_color_transform *color_matrix = NULL;
+ struct wlr_color_transform *inv_eotf = NULL;
+ struct wlr_color_transform *user_gamma = NULL;
+
+ if (img_desc != NULL) {
+ assert(supplied == NULL);
+ struct wlr_color_primaries primaries_srgb;
+ wlr_color_primaries_from_named(&primaries_srgb, WLR_COLOR_NAMED_PRIMARIES_SRGB);
+ struct wlr_color_primaries primaries;
+ wlr_color_primaries_from_named(&primaries, img_desc->primaries);
+ float matrix[9];
+ wlr_color_primaries_transform_absolute_colorimetric(&primaries_srgb, &primaries, matrix);
+
+ struct wlr_color_luminances srgb_lum, dst_lum;
+ wlr_color_transfer_function_get_default_luminance(
+ WLR_COLOR_TRANSFER_FUNCTION_SRGB, &srgb_lum);
+ wlr_color_transfer_function_get_default_luminance(img_desc->transfer_function, &dst_lum);
+ float luminance_multiplier = get_luminance_multiplier(&srgb_lum, &dst_lum);
+ for (int i = 0; i < 9; ++i) {
+ matrix[i] *= luminance_multiplier;
+ }
+
+ color_matrix = wlr_color_transform_init_matrix(matrix);
+ inv_eotf = wlr_color_transform_init_linear_to_inverse_eotf(img_desc->transfer_function);
+ if (color_matrix == NULL || inv_eotf == NULL) {
+ goto cleanup_transforms;
+ }
+ } else if (supplied != NULL) {
+ inv_eotf = wlr_color_transform_ref(supplied);
+ } else {
+ inv_eotf = wlr_color_transform_init_linear_to_inverse_eotf(
+ WLR_COLOR_TRANSFER_FUNCTION_GAMMA22);
+ if (inv_eotf == NULL) {
+ goto cleanup_transforms;
+ }
+ }
+
+ struct wlr_color_transform *gamma_lut = scene_output->gamma_lut_color_transform;
+ if (gamma_lut != NULL && render_gamma_lut) {
+ user_gamma = wlr_color_transform_ref(gamma_lut);
+ }
+
+ struct wlr_color_transform *combined;
+ struct wlr_color_transform *transforms[] = {
+ color_matrix,
+ inv_eotf,
+ user_gamma,
+ };
+ const size_t transforms_len = sizeof(transforms) / sizeof(transforms[0]);
+ if (!color_transform_compose(&combined, transforms, transforms_len)) {
+ goto cleanup_transforms;
+ }
+
+ wlr_color_transform_unref(scene_output->prev_gamma_lut_color_transform);
+ scene_output->prev_gamma_lut_color_transform = gamma_lut ? wlr_color_transform_ref(gamma_lut) : NULL;
+ wlr_color_transform_unref(scene_output->prev_supplied_color_transform);
+ scene_output->prev_supplied_color_transform = supplied ? wlr_color_transform_ref(supplied) : NULL;
+ wlr_color_transform_unref(scene_output->combined_color_transform);
+ scene_output->combined_color_transform = combined;
+
+ result = true;
+
+cleanup_transforms:
+ wlr_color_transform_unref(color_matrix);
+ wlr_color_transform_unref(inv_eotf);
+ wlr_color_transform_unref(user_gamma);
+ return result;
+}
+
bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
struct wlr_output_state *state, const struct wlr_scene_output_state_options *options) {
struct wlr_scene_output_state_options default_options = {0};
@@ -2754,12 +3095,21 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
enum wlr_scene_debug_damage_option debug_damage =
scene_output->scene->debug_damage_option;
+ bool render_gamma_lut = false;
+ if (wlr_output_get_gamma_size(output) == 0 && output->renderer->features.output_color_transform) {
+ if (scene_output->gamma_lut_color_transform != scene_output->prev_gamma_lut_color_transform) {
+ scene_output_damage_whole(scene_output);
+ }
+ if (scene_output->gamma_lut_color_transform != NULL) {
+ render_gamma_lut = true;
+ }
+ }
+
struct render_data render_data = {
.transform = output->transform,
.scale = output->scale,
.logical = { .x = scene_output->x, .y = scene_output->y },
.output = scene_output,
- .has_blur = false,
};
int resolution_width, resolution_height;
@@ -2831,7 +3181,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
pixman_region32_init(&acc_damage);
struct highlight_region *damage, *tmp_damage;
wl_list_for_each_safe(damage, tmp_damage, regions, link) {
- // remove overlaping damage regions
+ // remove overlapping damage regions
pixman_region32_subtract(&damage->region, &damage->region, &acc_damage);
pixman_region32_union(&acc_damage, &acc_damage, &damage->region);
@@ -2855,7 +3205,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
// - There are no color transforms that need to be applied
// - Damage highlight debugging is not enabled
enum scene_direct_scanout_result scanout_result = SCANOUT_INELIGIBLE;
- if (options->color_transform == NULL && list_len == 1
+ if (options->color_transform == NULL && !render_gamma_lut && list_len == 1
&& debug_damage != WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) {
scanout_result = scene_entry_try_direct_scanout(&list_data[0], state, &render_data);
}
@@ -2887,12 +3237,15 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
timespec_sub(&duration, &end_time, &start_time);
timer->pre_render_duration = timespec_to_nsec(&duration);
}
+
+ TRACY_MARK_FRAME;
return true;
}
struct wlr_swapchain *swapchain = options->swapchain;
if (!swapchain) {
if (!wlr_output_configure_primary_swapchain(output, state, &output->swapchain)) {
+ TRACY_MARK_FRAME;
return false;
}
@@ -2901,6 +3254,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
struct wlr_buffer *buffer = wlr_swapchain_acquire(swapchain);
if (buffer == NULL) {
+ TRACY_MARK_FRAME;
return false;
}
@@ -2915,78 +3269,120 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
timer->pre_render_duration = timespec_to_nsec(&duration);
}
+ if ((render_gamma_lut
+ && scene_output->gamma_lut_color_transform != scene_output->prev_gamma_lut_color_transform)
+ || scene_output->prev_supplied_color_transform != options->color_transform
+ || (state->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION)) {
+ const struct wlr_output_image_description *output_description =
+ output_pending_image_description(output, state);
+ if (!scene_output_combine_color_transforms(scene_output, options->color_transform,
+ output_description, render_gamma_lut)) {
+ wlr_buffer_unlock(buffer);
+ return false;
+ }
+ }
+
scene_output->in_point++;
- struct fx_gles_render_pass *render_pass =
- fx_renderer_begin_buffer_pass(output->renderer, buffer, output,
- &(struct fx_buffer_pass_options) {
- .base = &(struct wlr_buffer_pass_options){
- .timer = timer ? timer->render_timer : NULL,
- .color_transform = options->color_transform,
- .signal_timeline = scene_output->in_timeline,
- .signal_point = scene_output->in_point,
- },
- .swapchain = swapchain,
- }
- );
+ struct wlr_render_pass *render_pass = wlr_renderer_begin_buffer_pass(output->renderer, buffer,
+ &(struct wlr_buffer_pass_options){
+ .timer = timer ? timer->render_timer : NULL,
+ .color_transform = scene_output->combined_color_transform,
+ .signal_timeline = scene_output->in_timeline,
+ .signal_point = scene_output->in_point,
+ });
if (render_pass == NULL) {
wlr_buffer_unlock(buffer);
+
+ TRACY_MARK_FRAME;
return false;
}
- struct fx_effect_framebuffers *effect_fbos = render_pass->fx_effect_framebuffers;
render_data.render_pass = render_pass;
+
pixman_region32_init(&render_data.damage);
wlr_damage_ring_rotate_buffer(&scene_output->damage_ring, buffer,
&render_data.damage);
- // Blur artifact prevention
- pixman_region32_t blur_region;
- pixman_region32_init(&blur_region);
- render_data.has_blur =
- scene_output_has_blur(list_len, list_data, scene_output, &blur_region);
- bool whole_output_blur_damaged = false;
- // Expand the damage to compensate for blur
- if (render_data.has_blur) {
- int output_width = output->width;
- int output_height = output->height;
- struct blur_data *blur_data = &scene_output->scene->blur_data;
- pixman_region32_t *damage = &render_data.damage;
-
- // ensure that the damage isn't expanding past the output's size
- int32_t damage_width = damage->extents.x2 - damage->extents.x1;
- int32_t damage_height = damage->extents.y2 - damage->extents.y1;
- if (damage_width > output_width || damage_height > output_height) {
- pixman_region32_intersect_rect(damage, damage,
- 0, 0, output_width, output_height);
- // No need to compensate for blur artifacts when the damage spans
- // the whole output
- whole_output_blur_damaged = true;
- }else {
- // copy the surrounding content where the blur would display artifacts
- // and draw it above the artifacts
- pixman_region32_t extended_damage;
- pixman_region32_init(&extended_damage);
- pixman_region32_intersect(&extended_damage, damage, &blur_region);
- // Expand the region to compensate for blur artifacts
- wlr_region_expand(&extended_damage, &extended_damage, blur_data_calc_size(blur_data));
- // Limit to the monitors viewport
- pixman_region32_intersect_rect(&extended_damage, &extended_damage,
- 0, 0, output_width, output_height);
-
- // capture the padding pixels around the blur where artifacts will be drawn
- pixman_region32_subtract(&effect_fbos->blur_padding_region,
- &extended_damage, damage);
- // Combine into the surface damage (we need to redraw the padding area as well)
- pixman_region32_union(damage, damage, &extended_damage);
- pixman_region32_fini(&extended_damage);
-
- // Capture the padding pixels before blur for later use
- fx_renderer_read_to_buffer(render_pass, &effect_fbos->blur_padding_region,
- effect_fbos->blur_saved_pixels_buffer,
- render_pass->buffer);
- }
- }
- pixman_region32_fini(&blur_region);
+ struct fx_gles_render_pass *fx_pass = fx_get_render_pass(render_pass);
+ bool should_compensate_blur = false;
+ if (fx_render_pass_init_offscreen_buffers(render_pass, output)
+ && pixman_region32_not_empty(&render_data.damage)) {
+ // Blur artifact prevention
+ // Note: Supports individual blur node blur_data
+ pixman_region32_t original_damage;
+ pixman_region32_init(&original_damage);
+ pixman_region32_copy(&original_damage, &render_data.damage);
+
+ // Only compensate for blur artifacts when the damage doesn't span
+ // the whole output
+ const bool full_damage =
+ original_damage.extents.x2 - original_damage.extents.x1 >= output->width
+ && original_damage.extents.y2 - original_damage.extents.y1 >= output->height;
+
+ // The extra region we copy and paste onto the framebuffer after render
+ // for artifact removal
+ pixman_region32_t blur_padding_region;
+ pixman_region32_init(&blur_padding_region);
+
+ // Check if the original damage expanded by the to-be-rendered blur
+ // nodes sampling size intersects with said nodes visible region. The
+ // intersection gets expanded once more which becomes the region to be
+ // copied before render and pasted onto the framebuffer after render
+ // (the region where artifacts will be visible). This method fixes blur
+ // nodes not updating properly when nearby, non-blurred nodes get
+ // damaged while avoiding to re-render the whole nodes blur region. It
+ // also allows for node-individual blur_data if needed in the future.
+ for (int i = list_len - 1; i >= 0; i--) {
+ struct render_list_entry *entry = &list_data[i];
+ struct wlr_scene_node *node = entry->node;
+ struct blur_data blur_data = scene_output->scene->blur_data;
+ if (!should_blur_node_extend_damage(node, fx_pass, &blur_data)) {
+ continue;
+ }
+
+ // End early. No need to compensate for blur if the whole output
+ // is damaged anyway, but still make sure to let the renderer know that
+ // there's blur nodes to render.
+ if (full_damage) {
+ should_compensate_blur = false;
+ fx_pass->has_blur = true;
+ break;
+ }
+
+ if (apply_blur_region(node, &blur_data, &render_data, state,
+ &original_damage, &blur_padding_region)) {
+ should_compensate_blur = true;
+ fx_pass->has_blur = true;
+ }
+ }
+
+ // Expand the damage to compensate for blur
+ if (should_compensate_blur) {
+ // Capture the padding pixels around the blur where artifacts will be drawn
+ pixman_region32_subtract(&fx_pass->blur_padding_region,
+ &blur_padding_region, &render_data.damage);
+ // Make sure that the padding and damage doesn't exceed the output bounds
+ pixman_region32_intersect_rect(&fx_pass->blur_padding_region, &fx_pass->blur_padding_region,
+ 0, 0, output->width, output->height);
+
+ // Combine with the render damage (we need to redraw the padding area as well)
+ pixman_region32_union(&render_data.damage,
+ &render_data.damage, &fx_pass->blur_padding_region);
+ pixman_region32_intersect_rect(&render_data.damage, &render_data.damage,
+ 0, 0, output->width, output->height);
+
+ // Copy the surrounding content where the blur would display artifacts
+ // and draw it above the artifacts. Otherwise The old rendered
+ // content would be included into the new blur. This means that
+ // content like a high z-index toplevel would be included into the
+ // blur of a toplevel with a low z-index.
+ fx_render_pass_read_to_buffer(fx_pass, &fx_pass->blur_padding_region,
+ fx_pass->fx_offscreen_buffers->blur_saved_pixels_buffer, fx_pass->buffer);
+ }
+
+ pixman_region32_fini(&blur_padding_region);
+ pixman_region32_fini(&original_damage);
+ }
pixman_region32_t background;
pixman_region32_init(&background);
@@ -3024,7 +3420,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
}
}
- wlr_render_pass_add_rect(&render_pass->base, &(struct wlr_render_rect_options){
+ wlr_render_pass_add_rect(render_pass, &(struct wlr_render_rect_options){
.box = { .width = buffer->width, .height = buffer->height },
.color = { .r = 0, .g = 0, .b = 0, .a = 1 },
.clip = &background,
@@ -3053,12 +3449,6 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
}
}
- if (render_data.has_blur && !whole_output_blur_damaged) {
- // Render the saved pixels over the blur artifacts
- fx_renderer_read_to_buffer(render_pass, &effect_fbos->blur_padding_region,
- render_pass->buffer, effect_fbos->blur_saved_pixels_buffer);
- }
-
if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) {
struct highlight_region *damage;
wl_list_for_each(damage, &scene_output->damage_highlight_regions, link) {
@@ -3067,7 +3457,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
int64_t time_diff_ms = timespec_to_msec(&time_diff);
float alpha = 1.0 - (double)time_diff_ms / HIGHLIGHT_DAMAGE_FADEOUT_TIME;
- wlr_render_pass_add_rect(&render_pass->base, &(struct wlr_render_rect_options){
+ wlr_render_pass_add_rect(render_pass, &(struct wlr_render_rect_options){
.box = { .width = buffer->width, .height = buffer->height },
.color = { .r = alpha * 0.5, .g = 0, .b = 0, .a = alpha * 0.5 },
.clip = &damage->region,
@@ -3075,15 +3465,24 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
}
}
- wlr_output_add_software_cursors_to_render_pass(output, &render_pass->base, &render_data.damage);
+ wlr_output_add_software_cursors_to_render_pass(output, render_pass, &render_data.damage);
+
+ if (should_compensate_blur) {
+ // Render the saved pixels over the blur artifacts
+ fx_render_pass_read_to_buffer(fx_pass, &fx_pass->blur_padding_region,
+ fx_pass->buffer, fx_pass->fx_offscreen_buffers->blur_saved_pixels_buffer);
+ }
+
pixman_region32_fini(&render_data.damage);
- if (!wlr_render_pass_submit(&render_pass->base)) {
+ if (!wlr_render_pass_submit(render_pass)) {
wlr_buffer_unlock(buffer);
// if we failed to render the buffer, it will have undefined contents
// Trash the damage ring
wlr_damage_ring_add_whole(&scene_output->damage_ring);
+
+ TRACY_MARK_FRAME;
return false;
}
@@ -3093,10 +3492,16 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
if (scene_output->in_timeline != NULL) {
wlr_output_state_set_wait_timeline(state, scene_output->in_timeline,
scene_output->in_point);
+ scene_output->out_point++;
+ wlr_output_state_set_signal_timeline(state, scene_output->out_timeline,
+ scene_output->out_point);
}
- scene_output_state_attempt_gamma(scene_output, state);
+ if (!render_gamma_lut) {
+ scene_output_state_attempt_gamma(scene_output, state);
+ }
+ TRACY_MARK_FRAME;
return true;
}
@@ -3124,10 +3529,11 @@ static void scene_node_send_frame_done(struct wlr_scene_node *node,
if (node->type == WLR_SCENE_NODE_BUFFER) {
struct wlr_scene_buffer *scene_buffer =
wlr_scene_buffer_from_node(node);
-
- if (scene_buffer->primary_output == scene_output) {
- wlr_scene_buffer_send_frame_done(scene_buffer, now);
- }
+ struct wlr_scene_frame_done_event event = {
+ .output = scene_output,
+ .when = *now,
+ };
+ wlr_scene_buffer_send_frame_done(scene_buffer, &event);
} else if (node->type == WLR_SCENE_NODE_TREE) {
struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
struct wlr_scene_node *child;
diff --git a/scenefx/util/array.c b/scenefx/util/array.c
index ec16a7b..d4771e8 100644
--- a/scenefx/util/array.c
+++ b/scenefx/util/array.c
@@ -14,7 +14,7 @@ bool array_realloc(struct wl_array *arr, size_t size) {
// If the size is less than 1/4th of the allocation size, we shrink it.
// 1/4th is picked to provide hysteresis, without which an array with size
// arr->alloc would constantly reallocate if an element is added and then
- // removed continously.
+ // removed continuously.
size_t alloc;
if (arr->alloc > 0 && size > arr->alloc / 4) {
alloc = arr->alloc;
diff --git a/src/server/cursor.rs b/src/server/cursor.rs
index 95896b1..933c980 100644
--- a/src/server/cursor.rs
+++ b/src/server/cursor.rs
@@ -391,12 +391,10 @@ impl Cursor {
if !wlr_xcursor.is_null() && (*wlr_xcursor).image_count > 0 {
let image = *(*wlr_xcursor).images;
if !image.is_null() {
+ let buffer = ffi::wlr_xcursor_image_get_buffer(image);
ffi::wlr_xwayland_set_cursor(
(*(*self.seat).server).xwayland,
- (*image).buffer,
- (*image).width * 4,
- (*image).width,
- (*image).height,
+ buffer,
(*image).hotspot_x as i32,
(*image).hotspot_y as i32,
);
diff --git a/src/server/input_manager.rs b/src/server/input_manager.rs
index 4a22e27..fcc1f3d 100644
--- a/src/server/input_manager.rs
+++ b/src/server/input_manager.rs
@@ -87,12 +87,12 @@ impl InputManager {
// Connect new_text_input listener
let new_text_input_ptr = &mut self.new_text_input as *mut ffi::wl_listener as *mut WlListener;
(*new_text_input_ptr).notify = Some(handle_new_text_input);
- wl_signal_add(&mut (*self.text_input_manager).events.text_input, &mut self.new_text_input);
+ wl_signal_add(&mut (*self.text_input_manager).events.new_text_input, &mut self.new_text_input);
// Connect new_input_method listener
let new_input_method_ptr = &mut self.new_input_method as *mut ffi::wl_listener as *mut WlListener;
(*new_input_method_ptr).notify = Some(handle_new_input_method);
- wl_signal_add(&mut (*self.input_method_manager).events.input_method, &mut self.new_input_method);
+ wl_signal_add(&mut (*self.input_method_manager).events.new_input_method, &mut self.new_input_method);
// Connect new_virtual_pointer listener
let new_virtual_pointer_ptr = &mut self.new_virtual_pointer_listener as *mut ffi::wl_listener as *mut WlListener;
diff --git a/src/server/wlroots_log_wrapper.c b/src/server/wlroots_log_wrapper.c
index f0626b7..15e605e 100644
--- a/src/server/wlroots_log_wrapper.c
+++ b/src/server/wlroots_log_wrapper.c
@@ -652,35 +652,79 @@ void river_wlr_keyboard_init(struct wlr_keyboard *keyboard,
wlr_keyboard_init(keyboard, &impl, name);
}
-struct river_blur_data {
- bool enabled;
- bool optimized;
- bool ignore_transparent;
-};
+static struct wlr_scene_node *find_blur_node(struct wlr_scene_tree *tree) {
+ struct wlr_scene_node *child;
+ wl_list_for_each(child, &tree->children, link) {
+ if (child->type == WLR_SCENE_NODE_BLUR || child->type == WLR_SCENE_NODE_OPTIMIZED_BLUR) {
+ return child;
+ }
+ }
+ return NULL;
+}
-static void enable_blur_iterator(struct wlr_scene_buffer *buffer, int sx, int sy, void *user_data) {
+static void get_size_iterator(struct wlr_scene_buffer *buffer, int sx, int sy, void *user_data) {
(void)sx;
(void)sy;
- struct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(buffer);
- if (!scene_surface) {
+ int *size = (int *)user_data;
+ if (buffer->dst_width > size[0]) size[0] = buffer->dst_width;
+ if (buffer->dst_height > size[1]) size[1] = buffer->dst_height;
+}
+
+void river_scene_node_enable_blur(struct wlr_scene_node *node, bool enabled, bool optimized, bool ignore_transparent) {
+ (void)ignore_transparent;
+ if (node->type != WLR_SCENE_NODE_TREE) {
return;
}
- struct river_blur_data *data = (struct river_blur_data *)user_data;
- bool enabled = data->enabled;
- bool optimized = data->optimized && enabled;
- bool ignore_transparent = data->ignore_transparent && enabled;
- if (buffer->backdrop_blur != enabled ||
- buffer->backdrop_blur_optimized != optimized ||
- buffer->backdrop_blur_ignore_transparent != ignore_transparent) {
- wlr_scene_buffer_set_backdrop_blur(buffer, enabled);
- wlr_scene_buffer_set_backdrop_blur_optimized(buffer, optimized);
- wlr_scene_buffer_set_backdrop_blur_ignore_transparent(buffer, ignore_transparent);
+ struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node);
+ struct wlr_scene_node *blur_node = find_blur_node(tree);
+
+ if (!enabled) {
+ if (blur_node) {
+ wlr_scene_node_destroy(blur_node);
+ }
+ return;
}
-}
-void river_scene_node_enable_blur(struct wlr_scene_node *node, bool enabled, bool optimized, bool ignore_transparent) {
- struct river_blur_data data = { .enabled = enabled, .optimized = optimized, .ignore_transparent = ignore_transparent };
- wlr_scene_node_for_each_buffer(node, enable_blur_iterator, &data);
+ int width = 0, height = 0;
+ int size[2] = {0, 0};
+ wlr_scene_node_for_each_buffer(node, get_size_iterator, size);
+ width = size[0];
+ height = size[1];
+
+ if (width <= 0 || height <= 0) {
+ width = 1920; // Fallback defaults
+ height = 1080;
+ }
+
+ enum wlr_scene_node_type expected_type = optimized ? WLR_SCENE_NODE_OPTIMIZED_BLUR : WLR_SCENE_NODE_BLUR;
+ if (blur_node && blur_node->type != expected_type) {
+ wlr_scene_node_destroy(blur_node);
+ blur_node = NULL;
+ }
+
+ if (!blur_node) {
+ if (optimized) {
+ struct wlr_scene_optimized_blur *opt_blur = wlr_scene_optimized_blur_create(tree, width, height);
+ if (opt_blur) {
+ blur_node = &opt_blur->node;
+ }
+ } else {
+ struct wlr_scene_blur *std_blur = wlr_scene_blur_create(tree, width, height);
+ if (std_blur) {
+ wlr_scene_blur_set_should_only_blur_bottom_layer(std_blur, true);
+ blur_node = &std_blur->node;
+ }
+ }
+ if (blur_node) {
+ wlr_scene_node_lower_to_bottom(blur_node);
+ }
+ } else {
+ if (optimized) {
+ wlr_scene_optimized_blur_set_size((struct wlr_scene_optimized_blur *)blur_node, width, height);
+ } else {
+ wlr_scene_blur_set_size((struct wlr_scene_blur *)blur_node, width, height);
+ }
+ }
}
static void set_opacity_iterator(struct wlr_scene_buffer *buffer, int sx, int sy, void *user_data) {
@@ -700,13 +744,18 @@ static void set_corner_radius_iterator(struct wlr_scene_buffer *buffer, int sx,
(void)sx;
(void)sy;
int radius = *(int *)user_data;
- if (buffer->corner_radius != radius || buffer->corners != CORNER_LOCATION_ALL) {
- wlr_scene_buffer_set_corner_radius(buffer, radius, CORNER_LOCATION_ALL);
- }
+ wlr_scene_buffer_set_corner_radius(buffer, radius);
}
void river_scene_node_set_corner_radius(struct wlr_scene_node *node, int radius) {
wlr_scene_node_for_each_buffer(node, set_corner_radius_iterator, &radius);
+ if (node->type == WLR_SCENE_NODE_TREE) {
+ struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node);
+ struct wlr_scene_node *blur_node = find_blur_node(tree);
+ if (blur_node && blur_node->type == WLR_SCENE_NODE_BLUR) {
+ wlr_scene_blur_set_corner_radius((struct wlr_scene_blur *)blur_node, radius);
+ }
+ }
}
void river_scene_buffer_set_dest_size_if_changed(struct wlr_scene_buffer *scene_buffer, int width, int height) {
@@ -728,7 +777,7 @@ void river_scene_rect_set_size_if_changed(struct wlr_scene_rect *rect, int width
}
void river_scene_rect_set_corner_radius(struct wlr_scene_rect *rect, int radius) {
- wlr_scene_rect_set_corner_radius(rect, radius, CORNER_LOCATION_ALL);
+ wlr_scene_rect_set_corner_radius(rect, radius);
}
int river_scene_buffer_get_width(struct wlr_scene_buffer *scene_buffer) {
diff --git a/src/server/xdg_toplevel.rs b/src/server/xdg_toplevel.rs
index 709f578..b8b7101 100644
--- a/src/server/xdg_toplevel.rs
+++ b/src/server/xdg_toplevel.rs
@@ -793,8 +793,19 @@ unsafe extern "C" fn handle_decoration_request_mode(listener: *mut ffi::wl_liste
(*window).set_decoration_hint(hint);
if ffi::river_wlr_xdg_surface_get_initialized(base) {
- let mode = ffi::wlr_xdg_toplevel_decoration_v1_mode_WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
+ let mut mode = (*(*decoration).wlr_decoration).requested_mode;
+ if mode == ffi::wlr_xdg_toplevel_decoration_v1_mode_WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_NONE {
+ let server = (*window).server;
+ let rule_ssd = (*server).wm.get_rule_for_window(window).and_then(|r| r.ssd);
+ if let Some(true) = rule_ssd {
+ mode = ffi::wlr_xdg_toplevel_decoration_v1_mode_WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
+ } else {
+ mode = ffi::wlr_xdg_toplevel_decoration_v1_mode_WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
+ }
+ }
ffi::wlr_xdg_toplevel_decoration_v1_set_mode((*decoration).wlr_decoration, mode);
+ (*window).wm_requested.ssd = mode == ffi::wlr_xdg_toplevel_decoration_v1_mode_WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
+ (*(*window).server).wm.dirty_windowing();
}
}