diff --git a/CMakeLists.txt b/CMakeLists.txt index 87574b82..87461645 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,7 +125,6 @@ find_package(Threads REQUIRED) set(GLES_VERSION "GLES3") find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION}) -find_package(glslang CONFIG REQUIRED) set(AQUAMARINE_MINIMUM_VERSION 0.9.3) set(HYPRLANG_MINIMUM_VERSION 0.6.7) @@ -267,8 +266,7 @@ pkg_check_modules( gbm gio-2.0 re2 - muparser - lcms2) + muparser) find_package(hyprwayland-scanner 0.3.10 REQUIRED) @@ -480,9 +478,9 @@ function(protocolWayland) endfunction() if(TARGET OpenGL::GL) - target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GL glslang::glslang glslang::glslang-default-resource-limits glslang::SPIRV Threads::Threads) + target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GL Threads::Threads) else() - target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GLES3 glslang::glslang glslang::glslang-default-resource-limits glslang::SPIRV Threads::Threads) + target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GLES3 Threads::Threads) endif() pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.4) diff --git a/Makefile b/Makefile index 852fcddf..91837c2f 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,6 @@ nopch: clear: rm -rf build rm -f ./protocols/*.h ./protocols/*.c ./protocols/*.cpp ./protocols/*.hpp - rm -f ./hyprctl/hw-protocols/*.cpp ./hyprctl/hw-protocols/*.hpp all: $(MAKE) clear diff --git a/flake.lock b/flake.lock index 4a89c0fc..961a20db 100644 --- a/flake.lock +++ b/flake.lock @@ -16,11 +16,11 @@ ] }, "locked": { - "lastModified": 1772292445, - "narHash": "sha256-4F1Q7U313TKUDDovCC96m/Za4wZcJ3yqtu4eSrj8lk8=", + "lastModified": 1771610171, + "narHash": "sha256-+DeInuhbm6a6PpHDNUS7pozDouq2+8xSDefoNaZLW0E=", "owner": "hyprwm", "repo": "aquamarine", - "rev": "1dbbba659c1cef0b0202ce92cadfe13bae550e8f", + "rev": "7f9eb087703ec4acc6b288d02fa9ea3db803cd3d", "type": "github" }, "original": { @@ -325,11 +325,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1772198003, - "narHash": "sha256-I45esRSssFtJ8p/gLHUZ1OUaaTaVLluNkABkk6arQwE=", + "lastModified": 1771848320, + "narHash": "sha256-0MAd+0mun3K/Ns8JATeHT1sX28faLII5hVLq0L3BdZU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "dd9b079222d43e1943b6ebd802f04fd959dc8e61", + "rev": "2fc6539b481e1d2569f25f8799236694180c0993", "type": "github" }, "original": { @@ -348,11 +348,11 @@ ] }, "locked": { - "lastModified": 1772024342, - "narHash": "sha256-+eXlIc4/7dE6EcPs9a2DaSY3fTA9AE526hGqkNID3Wg=", + "lastModified": 1771858127, + "narHash": "sha256-Gtre9YoYl3n25tJH2AoSdjuwcqij5CPxL3U3xysYD08=", "owner": "cachix", "repo": "git-hooks.nix", - "rev": "6e34e97ed9788b17796ee43ccdbaf871a5c2b476", + "rev": "49bbbfc218bf3856dfa631cead3b052d78248b83", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 6d695bfb..21561cc5 100644 --- a/flake.nix +++ b/flake.nix @@ -88,122 +88,108 @@ }; }; - outputs = - inputs@{ - self, - nixpkgs, - systems, - ... - }: - let - inherit (nixpkgs) lib; - eachSystem = lib.genAttrs (import systems); - pkgsFor = eachSystem ( - system: - import nixpkgs { - localSystem = system; - overlays = with self.overlays; [ - hyprland-packages - hyprland-extras - ]; - } - ); - pkgsCrossFor = eachSystem ( - system: crossSystem: - import nixpkgs { - localSystem = system; - inherit crossSystem; - overlays = with self.overlays; [ - hyprland-packages - hyprland-extras - ]; - } - ); - pkgsDebugFor = eachSystem ( - system: - import nixpkgs { - localSystem = system; - overlays = with self.overlays; [ - hyprland-debug - ]; - } - ); - pkgsDebugCrossFor = eachSystem ( - system: crossSystem: - import nixpkgs { - localSystem = system; - inherit crossSystem; - overlays = with self.overlays; [ - hyprland-debug - ]; - } - ); - in - { - overlays = import ./nix/overlays.nix { inherit self lib inputs; }; + outputs = inputs @ { + self, + nixpkgs, + systems, + ... + }: let + inherit (nixpkgs) lib; + eachSystem = lib.genAttrs (import systems); + pkgsFor = eachSystem (system: + import nixpkgs { + localSystem = system; + overlays = with self.overlays; [ + hyprland-packages + hyprland-extras + ]; + }); + pkgsCrossFor = eachSystem (system: crossSystem: + import nixpkgs { + localSystem = system; + inherit crossSystem; + overlays = with self.overlays; [ + hyprland-packages + hyprland-extras + ]; + }); + pkgsDebugFor = eachSystem (system: + import nixpkgs { + localSystem = system; + overlays = with self.overlays; [ + hyprland-debug + ]; + }); + pkgsDebugCrossFor = eachSystem (system: crossSystem: + import nixpkgs { + localSystem = system; + inherit crossSystem; + overlays = with self.overlays; [ + hyprland-debug + ]; + }); + in { + overlays = import ./nix/overlays.nix {inherit self lib inputs;}; - checks = eachSystem ( - system: - (lib.filterAttrs ( - n: _: (lib.hasPrefix "hyprland" n) && !(lib.hasSuffix "debug" n) - ) self.packages.${system}) - // { - inherit (self.packages.${system}) xdg-desktop-portal-hyprland; - pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run { - src = ./.; - hooks = { - hyprland-treewide-formatter = { - enable = true; - entry = "${self.formatter.${system}}/bin/hyprland-treewide-formatter"; - pass_filenames = false; - excludes = [ "subprojects" ]; - always_run = true; - }; + checks = eachSystem (system: + (lib.filterAttrs + (n: _: (lib.hasPrefix "hyprland" n) && !(lib.hasSuffix "debug" n)) + self.packages.${system}) + // { + inherit (self.packages.${system}) xdg-desktop-portal-hyprland; + pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run { + src = ./.; + hooks = { + hyprland-treewide-formatter = { + enable = true; + entry = "${self.formatter.${system}}/bin/hyprland-treewide-formatter"; + pass_filenames = false; + excludes = ["subprojects"]; + always_run = true; }; }; - } - // (import ./nix/tests inputs pkgsFor.${system}) - ); + }; + } + // (import ./nix/tests inputs pkgsFor.${system})); - packages = eachSystem (system: { - default = self.packages.${system}.hyprland; - inherit (pkgsFor.${system}) - # hyprland-packages - hyprland - hyprland-unwrapped - hyprland-with-tests - # hyprland-extras - xdg-desktop-portal-hyprland - ; - inherit (pkgsDebugFor.${system}) hyprland-debug; - hyprland-cross = (pkgsCrossFor.${system} "aarch64-linux").hyprland; - hyprland-debug-cross = (pkgsDebugCrossFor.${system} "aarch64-linux").hyprland-debug; - }); + packages = eachSystem (system: { + default = self.packages.${system}.hyprland; + inherit + (pkgsFor.${system}) + # hyprland-packages + hyprland + hyprland-unwrapped + hyprland-with-tests + # hyprland-extras + xdg-desktop-portal-hyprland + ; + inherit (pkgsDebugFor.${system}) hyprland-debug; + hyprland-cross = (pkgsCrossFor.${system} "aarch64-linux").hyprland; + hyprland-debug-cross = (pkgsDebugCrossFor.${system} "aarch64-linux").hyprland-debug; + }); - devShells = eachSystem (system: { - default = - pkgsFor.${system}.mkShell.override - { - inherit (self.packages.${system}.default) stdenv; - } - { - name = "hyprland-shell"; - hardeningDisable = [ "fortify" ]; - inputsFrom = [ pkgsFor.${system}.hyprland ]; - packages = [ pkgsFor.${system}.clang-tools ]; - inherit (self.checks.${system}.pre-commit-check) shellHook; - }; - }); + devShells = eachSystem (system: { + default = + pkgsFor.${system}.mkShell.override { + inherit (self.packages.${system}.default) stdenv; + } { + name = "hyprland-shell"; + hardeningDisable = ["fortify"]; + inputsFrom = [pkgsFor.${system}.hyprland]; + packages = [pkgsFor.${system}.clang-tools]; + inherit (self.checks.${system}.pre-commit-check) shellHook; + }; + }); - formatter = eachSystem (system: pkgsFor.${system}.callPackage ./nix/formatter.nix { }); + formatter = eachSystem (system: pkgsFor.${system}.callPackage ./nix/formatter.nix {}); - nixosModules.default = import ./nix/module.nix inputs; - homeManagerModules.default = import ./nix/hm-module.nix self; + nixosModules.default = import ./nix/module.nix inputs; + homeManagerModules.default = import ./nix/hm-module.nix self; - # Hydra build jobs - # Recent versions of Hydra can aggregate jobsets from 'hydraJobs' instead of a release.nix - # or similar. Remember to filter large or incompatible attributes here. More eval jobs can - # be added by merging, e.g., self.packages // self.devShells. - hydraJobs = self.packages; - }; + # Hydra build jobs + # Recent versions of Hydra can aggregate jobsets from 'hydraJobs' instead of a release.nix + # or similar. Remember to filter large or incompatible attributes here. More eval jobs can + # be added by merging, e.g., self.packages // self.devShells. + hydraJobs = self.packages; + }; } diff --git a/hyprctl/src/main.cpp b/hyprctl/src/main.cpp index 0a33f3ed..7146c635 100644 --- a/hyprctl/src/main.cpp +++ b/hyprctl/src/main.cpp @@ -228,23 +228,23 @@ int request(std::string_view arg, int minArgs = 0, bool needRoll = false) { constexpr size_t BUFFER_SIZE = 8192; char buffer[BUFFER_SIZE] = {0}; - // read all data until server closes the connection - // this handles partial writes on the server side under high load - while (true) { - sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE); + sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE); + if (sizeWritten < 0) { + if (errno == EWOULDBLOCK) + log("Hyprland IPC didn't respond in time\n"); + log("Couldn't read (6)"); + return 6; + } + + reply += std::string(buffer, sizeWritten); + + while (sizeWritten == BUFFER_SIZE) { + sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE); if (sizeWritten < 0) { - if (errno == EWOULDBLOCK) - log("Hyprland IPC didn't respond in time\n"); log("Couldn't read (6)"); return 6; } - - if (sizeWritten == 0) { - // server closed connection, we're done - break; - } - reply += std::string(buffer, sizeWritten); } diff --git a/hyprpm/CMakeLists.txt b/hyprpm/CMakeLists.txt index 7d5b8eda..9f1318f4 100644 --- a/hyprpm/CMakeLists.txt +++ b/hyprpm/CMakeLists.txt @@ -11,9 +11,9 @@ set(CMAKE_CXX_STANDARD 23) pkg_check_modules(hyprpm_deps REQUIRED IMPORTED_TARGET tomlplusplus hyprutils>=0.7.0) -find_package(glaze 6.0.1 QUIET) +find_package(glaze 7.0.0 QUIET) if (NOT glaze_FOUND) - set(GLAZE_VERSION v6.0.1) + set(GLAZE_VERSION v7.0.0) message(STATUS "glaze dependency not found, retrieving ${GLAZE_VERSION} with FetchContent") include(FetchContent) FetchContent_Declare( diff --git a/hyprpm/src/core/PluginManager.cpp b/hyprpm/src/core/PluginManager.cpp index 6621a49f..7ffb3120 100644 --- a/hyprpm/src/core/PluginManager.cpp +++ b/hyprpm/src/core/PluginManager.cpp @@ -131,18 +131,9 @@ bool CPluginManager::createSafeDirectory(const std::string& path) { return true; } -bool CPluginManager::validArg(const std::string& s) { - return !s.contains("'") && !s.ends_with("\\") && !s.starts_with("\\"); -} - bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& rev) { const auto HLVER = getHyprlandVersion(); - if (!validArg(url) || !validArg(rev)) { - std::println(stderr, "\n{}", failureString("url or rev invalid")); - return false; - } - if (!hasDeps()) { std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Dependencies not satisfied. Hyprpm requires: cmake, cpio, pkg-config, git, g++, gcc")); return false; @@ -207,7 +198,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& progress.printMessageAbove(infoString("Cloning {}", url)); - std::string ret = execAndGet(std::format("cd {} && git clone --recursive '{}' {}", getTempRoot(), url, USERNAME)); + std::string ret = execAndGet(std::format("cd {} && git clone --recursive {} {}", getTempRoot(), url, USERNAME)); if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) { std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. shell returned:\n{}", ret)); @@ -512,11 +503,11 @@ bool CPluginManager::updateHeaders(bool force) { progress.printMessageAbove(verboseString("will shallow since: {}", SHALLOW_DATE)); std::string ret = - execAndGet(std::format("cd {} && git clone --recursive '{}' hyprland-{}{}", getTempRoot(), HL_URL, USERNAME, (bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : ""))); + execAndGet(std::format("cd {} && git clone --recursive {} hyprland-{}{}", getTempRoot(), HL_URL, USERNAME, (bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : ""))); if (!std::filesystem::exists(WORKINGDIR)) { progress.printMessageAbove(failureString("Clone failed. Retrying without shallow.")); - ret = execAndGet(std::format("cd {} && git clone --recursive '{}' hyprland-{}", getTempRoot(), HL_URL, USERNAME)); + ret = execAndGet(std::format("cd {} && git clone --recursive {} hyprland-{}", getTempRoot(), HL_URL, USERNAME)); } if (!std::filesystem::exists(WORKINGDIR + "/.git")) { @@ -657,7 +648,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) { const auto HLVER = getHyprlandVersion(false); CProgressBar progress; - progress.m_iMaxSteps = (REPOS.size() * 2) + 2; + progress.m_iMaxSteps = REPOS.size() * 2 + 2; progress.m_iSteps = 0; progress.m_szCurrentMessage = "Updating repositories"; progress.print(); @@ -678,7 +669,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) { progress.printMessageAbove(infoString("Cloning {}", repo.url)); - std::string ret = execAndGet(std::format("cd {} && git clone --recursive '{}' {}", getTempRoot(), repo.url, USERNAME)); + std::string ret = execAndGet(std::format("cd {} && git clone --recursive {} {}", getTempRoot(), repo.url, USERNAME)); if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) { std::println("{}", failureString("could not clone repo: shell returned: {}", ret)); @@ -688,7 +679,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) { if (!repo.rev.empty()) { progress.printMessageAbove(infoString("Plugin has revision set, resetting: {}", repo.rev)); - std::string ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules \'" + repo.rev + "\'"); + std::string ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules " + repo.rev); if (ret.compare(0, 6, "fatal:") == 0) { std::println(stderr, "\n{}", failureString("could not check out revision {}: shell returned:\n{}", repo.rev, ret)); diff --git a/hyprpm/src/core/PluginManager.hpp b/hyprpm/src/core/PluginManager.hpp index 25878f54..4bbbc5ca 100644 --- a/hyprpm/src/core/PluginManager.hpp +++ b/hyprpm/src/core/PluginManager.hpp @@ -81,7 +81,6 @@ class CPluginManager { private: std::string headerError(const eHeadersErrors err); std::string headerErrorShort(const eHeadersErrors err); - bool validArg(const std::string& s); std::expected nixDevelopIfNeeded(const std::string& cmd, const SHyprlandVersion& ver); diff --git a/hyprtester/plugin/src/main.cpp b/hyprtester/plugin/src/main.cpp index ce83c5b4..6db352fc 100644 --- a/hyprtester/plugin/src/main.cpp +++ b/hyprtester/plugin/src/main.cpp @@ -10,9 +10,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -272,67 +270,32 @@ static SDispatchResult keybind(std::string in) { return {}; } -static Desktop::Rule::CWindowRuleEffectContainer::storageType windowRuleIDX = 0; +static Desktop::Rule::CWindowRuleEffectContainer::storageType ruleIDX = 0; // -static SDispatchResult addWindowRule(std::string in) { - windowRuleIDX = Desktop::Rule::windowEffects()->registerEffect("plugin_rule"); +static SDispatchResult addRule(std::string in) { + ruleIDX = Desktop::Rule::windowEffects()->registerEffect("plugin_rule"); - if (Desktop::Rule::windowEffects()->registerEffect("plugin_rule") != windowRuleIDX) + if (Desktop::Rule::windowEffects()->registerEffect("plugin_rule") != ruleIDX) return {.success = false, .error = "re-registering returned a different id?"}; return {}; } -static SDispatchResult checkWindowRule(std::string in) { +static SDispatchResult checkRule(std::string in) { const auto PLASTWINDOW = Desktop::focusState()->window(); if (!PLASTWINDOW) return {.success = false, .error = "No window"}; - if (!PLASTWINDOW->m_ruleApplicator->m_otherProps.props.contains(windowRuleIDX)) + if (!PLASTWINDOW->m_ruleApplicator->m_otherProps.props.contains(ruleIDX)) return {.success = false, .error = "No rule"}; - if (PLASTWINDOW->m_ruleApplicator->m_otherProps.props[windowRuleIDX]->effect != "effect") + if (PLASTWINDOW->m_ruleApplicator->m_otherProps.props[ruleIDX]->effect != "effect") return {.success = false, .error = "Effect isn't \"effect\""}; return {}; } -static Desktop::Rule::CLayerRuleEffectContainer::storageType layerRuleIDX = 0; - -static SDispatchResult addLayerRule(std::string in) { - layerRuleIDX = Desktop::Rule::layerEffects()->registerEffect("plugin_rule"); - - if (Desktop::Rule::layerEffects()->registerEffect("plugin_rule") != layerRuleIDX) - return {.success = false, .error = "re-registering returned a different id?"}; - return {}; -} - -static SDispatchResult checkLayerRule(std::string in) { - if (g_pCompositor->m_layers.size() != 3) - return {.success = false, .error = "Layers under test not here"}; - - for (const auto& layer : g_pCompositor->m_layers) { - if (layer->m_namespace == "rule-layer") { - - if (!layer->m_ruleApplicator->m_otherProps.props.contains(layerRuleIDX)) - return {.success = false, .error = "No rule"}; - - if (layer->m_ruleApplicator->m_otherProps.props[layerRuleIDX]->effect != "effect") - return {.success = false, .error = "Effect isn't \"effect\""}; - - } else if (layer->m_namespace == "norule-layer") { - - if (layer->m_ruleApplicator->m_otherProps.props.contains(layerRuleIDX)) - return {.success = false, .error = "Rule even though it shouldn't"}; - - } else - return {.success = false, .error = "Unrecognized layer"}; - } - - return {}; -} - static SDispatchResult floatingFocusOnFullscreen(std::string in) { const auto PLASTWINDOW = Desktop::focusState()->window(); @@ -362,10 +325,8 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:scroll", ::scroll); HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:click", ::click); HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:keybind", ::keybind); - HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:add_window_rule", ::addWindowRule); - HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:check_window_rule", ::checkWindowRule); - HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:add_layer_rule", ::addLayerRule); - HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:check_layer_rule", ::checkLayerRule); + HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:add_rule", ::addRule); + HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:check_rule", ::checkRule); HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:floating_focus_on_fullscreen", ::floatingFocusOnFullscreen); // init mouse diff --git a/hyprtester/src/shared.hpp b/hyprtester/src/shared.hpp index 941788fd..1090aa9a 100644 --- a/hyprtester/src/shared.hpp +++ b/hyprtester/src/shared.hpp @@ -39,16 +39,6 @@ namespace Colors { TESTS_PASSED++; \ } -#define EXPECT_NOT(expr, val) \ - if (const auto RESULT = expr; RESULT == (val)) { \ - NLog::log("{}Failed: {}{}, expected not {}, got {}. Source: {}@{}.", Colors::RED, Colors::RESET, #expr, val, RESULT, __FILE__, __LINE__); \ - ret = 1; \ - TESTS_FAILED++; \ - } else { \ - NLog::log("{}Passed: {}{}. Got {}", Colors::GREEN, Colors::RESET, #expr, val); \ - TESTS_PASSED++; \ - } - #define EXPECT_VECTOR2D(expr, val) \ do { \ const auto& RESULT = expr; \ diff --git a/hyprtester/src/tests/clients/child-window.cpp b/hyprtester/src/tests/clients/child-window.cpp index a5680d4f..1b497c3d 100644 --- a/hyprtester/src/tests/clients/child-window.cpp +++ b/hyprtester/src/tests/clients/child-window.cpp @@ -118,33 +118,6 @@ static bool test() { Tests::killAllWindows(); EXPECT(Tests::windowCount(), 0); - // test that child windows (shouldBeFloated) are not auto-grouped - NLog::log("{}Test child windows are not auto-grouped", Colors::GREEN); - auto kitty = Tests::spawnKitty(); - if (!kitty) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; - } - - // create group and enable auto-grouping - OK(getFromSocket("/dispatch togglegroup")); - OK(getFromSocket("/keyword group:auto_group true")); - - SClient client2; - if (!startClient(client2)) - return false; - - EXPECT(Tests::windowCount(), 2); - createChild(client2); - EXPECT(Tests::windowCount(), 3); - - // child has set_parent so shouldBeFloated returns true, it should not be auto-grouped - EXPECT_COUNT_STRING(getFromSocket("/clients"), "grouped: 0", 1); - - stopClient(client2); - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - return !ret; } diff --git a/hyprtester/src/tests/main/dwindle.cpp b/hyprtester/src/tests/main/dwindle.cpp index 234bfc33..4135f2d6 100644 --- a/hyprtester/src/tests/main/dwindle.cpp +++ b/hyprtester/src/tests/main/dwindle.cpp @@ -64,16 +64,16 @@ static void test13349() { { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 497,22"); - EXPECT_CONTAINS(str, "size: 456,1036"); + EXPECT_CONTAINS(str, "at: 22,547"); + EXPECT_CONTAINS(str, "size: 931,511"); } OK(getFromSocket("/dispatch movewindow r")); { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 967,22"); - EXPECT_CONTAINS(str, "size: 456,1036"); + EXPECT_CONTAINS(str, "at: 967,547"); + EXPECT_CONTAINS(str, "size: 931,511"); } // clean up @@ -81,152 +81,6 @@ static void test13349() { Tests::killAllWindows(); } -static void testSplit() { - // Test various split methods - - Tests::spawnKitty("a"); - - // these must not crash - EXPECT_NOT(getFromSocket("/dispatch layoutmsg swapsplit"), "ok"); - EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio 1 exact"), "ok"); - - Tests::spawnKitty("b"); - - OK(getFromSocket("/dispatch focuswindow class:a")); - OK(getFromSocket("/dispatch layoutmsg splitratio -0.2")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 22,22"); - EXPECT_CONTAINS(str, "size: 743,1036"); - } - - OK(getFromSocket("/dispatch layoutmsg splitratio 1.6 exact")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 22,22"); - EXPECT_CONTAINS(str, "size: 1495,1036"); - } - - EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio fhne exact"), "ok"); - EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio exact"), "ok"); - EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio -....9"), "ok"); - EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio ..9"), "ok"); - EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio"), "ok"); - - OK(getFromSocket("/dispatch layoutmsg togglesplit")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 22,22"); - EXPECT_CONTAINS(str, "size: 1876,823"); - } - - OK(getFromSocket("/dispatch layoutmsg swapsplit")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 22,859"); - EXPECT_CONTAINS(str, "size: 1876,199"); - } - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); -} - -static void testRotatesplit() { - OK(getFromSocket("r/keyword general:gaps_in 0")); - OK(getFromSocket("r/keyword general:gaps_out 0")); - OK(getFromSocket("r/keyword general:border_size 0")); - - for (auto const& win : {"a", "b"}) { - if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; - } - } - - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 0,0"); - EXPECT_CONTAINS(str, "size: 960,1080"); - } - - // test 4 repeated rotations by 90 degrees - OK(getFromSocket("/dispatch layoutmsg rotatesplit")); - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 0,0"); - EXPECT_CONTAINS(str, "size: 1920,540"); - } - - OK(getFromSocket("/dispatch layoutmsg rotatesplit")); - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 960,0"); - EXPECT_CONTAINS(str, "size: 960,1080"); - } - - OK(getFromSocket("/dispatch layoutmsg rotatesplit")); - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 0,540"); - EXPECT_CONTAINS(str, "size: 1920,540"); - } - - OK(getFromSocket("/dispatch layoutmsg rotatesplit")); - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 0,0"); - EXPECT_CONTAINS(str, "size: 960,1080"); - } - - // test different angles - OK(getFromSocket("/dispatch layoutmsg rotatesplit 180")); - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 960,0"); - EXPECT_CONTAINS(str, "size: 960,1080"); - } - - OK(getFromSocket("/dispatch layoutmsg rotatesplit 270")); - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 0,540"); - EXPECT_CONTAINS(str, "size: 1920,540"); - } - - OK(getFromSocket("/dispatch layoutmsg rotatesplit 360")); - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 0,0"); - EXPECT_CONTAINS(str, "size: 1920,540"); - } - - // test negative angles - OK(getFromSocket("/dispatch layoutmsg rotatesplit -90")); - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 0,0"); - EXPECT_CONTAINS(str, "size: 960,1080"); - } - - OK(getFromSocket("/dispatch layoutmsg rotatesplit -180")); - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 960,0"); - EXPECT_CONTAINS(str, "size: 960,1080"); - } - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - OK(getFromSocket("/reload")); -} - static bool test() { NLog::log("{}Testing Dwindle layout", Colors::GREEN); @@ -237,12 +91,6 @@ static bool test() { NLog::log("{}Testing #13349", Colors::GREEN); test13349(); - NLog::log("{}Testing splits", Colors::GREEN); - testSplit(); - - NLog::log("{}Testing rotatesplit", Colors::GREEN); - testRotatesplit(); - // clean up NLog::log("Cleaning up", Colors::YELLOW); getFromSocket("/dispatch workspace 1"); diff --git a/hyprtester/src/tests/main/groups.cpp b/hyprtester/src/tests/main/groups.cpp index 2f9c5062..3cf15851 100644 --- a/hyprtester/src/tests/main/groups.cpp +++ b/hyprtester/src/tests/main/groups.cpp @@ -127,34 +127,6 @@ static bool test() { ret = 1; } - // test movegroupwindow: focus should follow the moved window - NLog::log("{}Test movegroupwindow focus follows window", Colors::YELLOW); - try { - auto str = getFromSocket("/activewindow"); - auto activeBeforeMove = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16); - OK(getFromSocket("/dispatch movegroupwindow f")); - str = getFromSocket("/activewindow"); - auto activeAfterMove = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16); - EXPECT(activeAfterMove, activeBeforeMove); - } catch (...) { - NLog::log("{}Fail at getting prop", Colors::RED); - ret = 1; - } - - // and backwards - NLog::log("{}Test movegroupwindow backwards", Colors::YELLOW); - try { - auto str = getFromSocket("/activewindow"); - auto activeBeforeMove = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16); - OK(getFromSocket("/dispatch movegroupwindow b")); - str = getFromSocket("/activewindow"); - auto activeAfterMove = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16); - EXPECT(activeAfterMove, activeBeforeMove); - } catch (...) { - NLog::log("{}Fail at getting prop", Colors::RED); - ret = 1; - } - NLog::log("{}Disable autogrouping", Colors::YELLOW); OK(getFromSocket("/keyword group:auto_group false")); @@ -201,99 +173,6 @@ static bool test() { NLog::log("{}Expecting 0 windows", Colors::YELLOW); EXPECT(Tests::windowCount(), 0); - // test movewindoworgroup: direction should be respected when extracting from group - NLog::log("{}Test movewindoworgroup respects direction out of group", Colors::YELLOW); - OK(getFromSocket("/keyword group:groupbar:enabled 0")); - { - auto kittyE = Tests::spawnKitty(); - if (!kittyE) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; - } - - // group kitty, and new windows should be auto-grouped - OK(getFromSocket("/dispatch togglegroup")); - - auto kittyF = Tests::spawnKitty(); - if (!kittyF) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; - } - EXPECT(Tests::windowCount(), 2); - - // both windows should be grouped at the same position - { - auto str = getFromSocket("/clients"); - EXPECT_COUNT_STRING(str, "at: 22,22", 2); - } - - // move active window out of group to the right - NLog::log("{}Test movewindoworgroup r", Colors::YELLOW); - OK(getFromSocket("/dispatch movewindoworgroup r")); - - // the group should stay at x=22, the extracted window should be to the right - { - auto str = getFromSocket("/clients"); - EXPECT_COUNT_STRING(str, "at: 22,22", 1); - } - - // move it back into the group - OK(getFromSocket("/dispatch moveintogroup l")); - - // move active window out of group downward - NLog::log("{}Test movewindoworgroup d", Colors::YELLOW); - OK(getFromSocket("/dispatch movewindoworgroup d")); - - // the group should stay at y=22, the extracted window should be below - { - auto str = getFromSocket("/clients"); - EXPECT_COUNT_STRING(str, "at: 22,22", 1); - } - - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - } - - // test that we deny a floated window getting auto-grouped into a tiled group. - NLog::log("{}Test that we deny a floated window getting auto-grouped into a tiled group.", Colors::GREEN); - - OK(getFromSocket("/keyword windowrule[kitty-tiled]:match:class kitty_tiled")); - OK(getFromSocket("/keyword windowrule[kitty-tiled]:tile yes")); - auto kittyProcE = Tests::spawnKitty("kitty_tiled"); - if (!kittyProcE) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; - } - OK(getFromSocket("/dispatch togglegroup")); - - OK(getFromSocket("/keyword windowrule[kitty-floated]:match:class kitty_floated")); - OK(getFromSocket("/keyword windowrule[kitty-floated]:float yes")); - auto kittyProcF = Tests::spawnKitty("kitty_floated"); - if (!kittyProcF) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; - } - - EXPECT(Tests::windowCount(), 2); - - { - auto clients = getFromSocket("/clients"); - auto classPos = clients.find("class: kitty_floated"); - if (classPos == std::string::npos) { - NLog::log("{}Could not find kitty_floated in clients output", Colors::RED); - ret = 1; - } else { - auto entryStart = clients.rfind("Window ", classPos); - auto entryEnd = clients.find("\n\n", classPos); - auto windowEntry = clients.substr(entryStart, entryEnd - entryStart); - EXPECT_CONTAINS(windowEntry, "floating: 1"); - EXPECT_CONTAINS(windowEntry, "grouped: 0"); - } - } - - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - return !ret; } diff --git a/hyprtester/src/tests/main/layer.cpp b/hyprtester/src/tests/main/layer.cpp deleted file mode 100644 index 73e30ba6..00000000 --- a/hyprtester/src/tests/main/layer.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "../../Log.hpp" -#include "../shared.hpp" -#include "tests.hpp" -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include -#include - -static int ret = 0; - -using namespace Hyprutils::OS; -using namespace Hyprutils::Memory; - -static bool spawnLayer(const std::string& namespace_) { - NLog::log("{}Spawning kitty layer {}", Colors::YELLOW, namespace_); - if (!Tests::spawnLayerKitty(namespace_)) { - NLog::log("{}Error: {} layer did not spawn", Colors::RED, namespace_); - return false; - } - return true; -} - -static bool test() { - NLog::log("{}Testing plugin layerrules", Colors::GREEN); - - if (!spawnLayer("rule-layer")) - return false; - - OK(getFromSocket("/dispatch plugin:test:add_layer_rule")); - OK(getFromSocket("/reload")); - - OK(getFromSocket("/keyword layerrule match:namespace rule-layer, plugin_rule effect")); - - if (!spawnLayer("rule-layer")) - return false; - - if (!spawnLayer("norule-layer")) - return false; - - OK(getFromSocket("/dispatch plugin:test:check_layer_rule")); - - OK(getFromSocket("/reload")); - - NLog::log("{}Killing all layers", Colors::YELLOW); - Tests::killAllLayers(); - - NLog::log("{}Expecting 0 layers", Colors::YELLOW); - EXPECT(Tests::layerCount(), 0); - - return !ret; -} - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/layout.cpp b/hyprtester/src/tests/main/layout.cpp deleted file mode 100644 index 186d7034..00000000 --- a/hyprtester/src/tests/main/layout.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include "../shared.hpp" -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include "tests.hpp" - -static int ret = 0; - -static void swar() { - OK(getFromSocket("/keyword layout:single_window_aspect_ratio 1 1")); - - Tests::spawnKitty(); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 442,22"); - EXPECT_CONTAINS(str, "size: 1036,1036"); - } - - Tests::spawnKitty(); - - OK(getFromSocket("/dispatch killwindow activewindow")); - - Tests::waitUntilWindowsN(1); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 442,22"); - EXPECT_CONTAINS(str, "size: 1036,1036"); - } - - // don't use swar on maximized - OK(getFromSocket("/dispatch fullscreen 1")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 22,22"); - EXPECT_CONTAINS(str, "size: 1876,1036"); - } - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); -} - -// Don't crash when focus after global geometry changes -static void testCrashOnGeomUpdate() { - Tests::spawnKitty(); - Tests::spawnKitty(); - Tests::spawnKitty(); - - // move the layout - OK(getFromSocket("/keyword monitor HEADLESS-2,1920x1080@60,1000x0,1")); - - // shouldnt crash - OK(getFromSocket("/dispatch movefocus r")); - - OK(getFromSocket("/reload")); - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); -} - -// Test if size + pos is preserved after fs cycle -static void testPosPreserve() { - Tests::spawnKitty(); - - OK(getFromSocket("/dispatch setfloating class:kitty")); - OK(getFromSocket("/dispatch resizewindowpixel exact 1337 69, class:kitty")); - OK(getFromSocket("/dispatch movewindowpixel exact 420 420, class:kitty")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 420,420"); - EXPECT_CONTAINS(str, "size: 1337,69"); - } - - OK(getFromSocket("/dispatch fullscreen")); - OK(getFromSocket("/dispatch fullscreen")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "size: 1337,69"); - } - - OK(getFromSocket("/dispatch movewindow r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 581,420"); - EXPECT_CONTAINS(str, "size: 1337,69"); - } - - OK(getFromSocket("/dispatch fullscreen")); - OK(getFromSocket("/dispatch fullscreen")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 581,420"); - EXPECT_CONTAINS(str, "size: 1337,69"); - } - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); -} - -static bool test() { - NLog::log("{}Testing layout generic", Colors::GREEN); - - // setup - OK(getFromSocket("/dispatch workspace 10")); - - // test - NLog::log("{}Testing `single_window_aspect_ratio`", Colors::GREEN); - swar(); - - testCrashOnGeomUpdate(); - testPosPreserve(); - - // clean up - NLog::log("Cleaning up", Colors::YELLOW); - OK(getFromSocket("/dispatch workspace 1")); - OK(getFromSocket("/reload")); - - return !ret; -} - -REGISTER_TEST_FN(test); diff --git a/hyprtester/src/tests/main/master.cpp b/hyprtester/src/tests/main/master.cpp index 9aaa4cf0..441143ac 100644 --- a/hyprtester/src/tests/main/master.cpp +++ b/hyprtester/src/tests/main/master.cpp @@ -97,67 +97,6 @@ static void focusMasterPrevious() { Tests::killAllWindows(); } -static void testFsBehavior() { - // Master will re-send data to fullscreen / maximized windows, which can interfere with misc:on_focus_under_fullscreen - // check that it doesn't. - - for (auto const& win : {"master", "slave1", "slave2"}) { - if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; - } - } - - OK(getFromSocket("/dispatch focuswindow class:master")); - OK(getFromSocket("/dispatch fullscreen 1")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 22,22"); - EXPECT_CONTAINS(str, "size: 1876,1036"); - EXPECT_CONTAINS(str, "class: master"); - } - - OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 1")); - - Tests::spawnKitty("new_master"); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 22,22"); - EXPECT_CONTAINS(str, "size: 1876,1036"); - EXPECT_CONTAINS(str, "class: new_master"); - EXPECT_CONTAINS(str, "fullscreen: 1"); - } - - OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 0")); - - Tests::spawnKitty("ignored"); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 22,22"); - EXPECT_CONTAINS(str, "size: 1876,1036"); - EXPECT_CONTAINS(str, "class: new_master"); - EXPECT_CONTAINS(str, "fullscreen: 1"); - } - - OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 2")); - - Tests::spawnKitty("vaxwashere"); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: vaxwashere"); - EXPECT_CONTAINS(str, "fullscreen: 0"); - } - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); -} - static bool test() { NLog::log("{}Testing Master layout", Colors::GREEN); @@ -169,9 +108,6 @@ static bool test() { NLog::log("{}Testing `focusmaster previous` layoutmsg", Colors::GREEN); focusMasterPrevious(); - NLog::log("{}Testing fs behavior", Colors::GREEN); - testFsBehavior(); - // clean up NLog::log("Cleaning up", Colors::YELLOW); OK(getFromSocket("/dispatch workspace 1")); diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp deleted file mode 100644 index 8bb950dd..00000000 --- a/hyprtester/src/tests/main/scroll.cpp +++ /dev/null @@ -1,228 +0,0 @@ -#include "../shared.hpp" -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include "tests.hpp" - -static int ret = 0; - -static void testFocusCycling() { - for (auto const& win : {"a", "b", "c", "d"}) { - if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; - } - } - - OK(getFromSocket("/dispatch focuswindow class:a")); - - OK(getFromSocket("/dispatch movefocus r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); - } - - OK(getFromSocket("/dispatch movefocus r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: c"); - } - - OK(getFromSocket("/dispatch movefocus r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: d"); - } - - OK(getFromSocket("/dispatch movewindow l")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: d"); - } - - OK(getFromSocket("/dispatch movefocus u")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: c"); - } - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); -} - -static void testFocusWrapping() { - for (auto const& win : {"a", "b", "c", "d"}) { - if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; - } - } - - // set wrap_focus to true - OK(getFromSocket("/keyword scrolling:wrap_focus true")); - - OK(getFromSocket("/dispatch focuswindow class:a")); - - OK(getFromSocket("/dispatch layoutmsg focus l")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: d"); - } - - OK(getFromSocket("/dispatch layoutmsg focus r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: a"); - } - - // set wrap_focus to false - OK(getFromSocket("/keyword scrolling:wrap_focus false")); - - OK(getFromSocket("/dispatch focuswindow class:a")); - - OK(getFromSocket("/dispatch layoutmsg focus l")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: a"); - } - - OK(getFromSocket("/dispatch focuswindow class:d")); - - OK(getFromSocket("/dispatch layoutmsg focus r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: d"); - } - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); -} - -static void testSwapcolWrapping() { - for (auto const& win : {"a", "b", "c", "d"}) { - if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; - } - } - - // set wrap_swapcol to true - OK(getFromSocket("/keyword scrolling:wrap_swapcol true")); - - OK(getFromSocket("/dispatch focuswindow class:a")); - - OK(getFromSocket("/dispatch layoutmsg swapcol l")); - OK(getFromSocket("/dispatch layoutmsg focus l")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: c"); - } - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - for (auto const& win : {"a", "b", "c", "d"}) { - if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; - } - } - - OK(getFromSocket("/dispatch focuswindow class:d")); - OK(getFromSocket("/dispatch layoutmsg swapcol r")); - OK(getFromSocket("/dispatch layoutmsg focus r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); - } - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - for (auto const& win : {"a", "b", "c", "d"}) { - if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; - } - } - - // set wrap_swapcol to false - OK(getFromSocket("/keyword scrolling:wrap_swapcol false")); - - OK(getFromSocket("/dispatch focuswindow class:a")); - - OK(getFromSocket("/dispatch layoutmsg swapcol l")); - OK(getFromSocket("/dispatch layoutmsg focus r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); - } - - OK(getFromSocket("/dispatch focuswindow class:d")); - - OK(getFromSocket("/dispatch layoutmsg swapcol r")); - OK(getFromSocket("/dispatch layoutmsg focus l")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: c"); - } - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); -} - -static bool test() { - NLog::log("{}Testing Scroll layout", Colors::GREEN); - - // setup - OK(getFromSocket("/dispatch workspace name:scroll")); - OK(getFromSocket("/keyword general:layout scrolling")); - - // test - NLog::log("{}Testing focus cycling", Colors::GREEN); - testFocusCycling(); - - // test - NLog::log("{}Testing focus wrap", Colors::GREEN); - testFocusWrapping(); - - // test - NLog::log("{}Testing swapcol wrap", Colors::GREEN); - testSwapcolWrapping(); - - // clean up - NLog::log("Cleaning up", Colors::YELLOW); - OK(getFromSocket("/dispatch workspace 1")); - OK(getFromSocket("/reload")); - - return !ret; -} - -REGISTER_TEST_FN(test); diff --git a/hyprtester/src/tests/main/window.cpp b/hyprtester/src/tests/main/window.cpp index beac0298..38f8ec48 100644 --- a/hyprtester/src/tests/main/window.cpp +++ b/hyprtester/src/tests/main/window.cpp @@ -93,9 +93,9 @@ static void testSwapWindow() { { getFromSocket("/dispatch focuswindow class:kitty_A"); auto pos = getWindowAttribute(getFromSocket("/activewindow"), "at:"); - NLog::log("{}Testing kitty_A {}, swapwindow with direction 'r'", Colors::YELLOW, pos); + NLog::log("{}Testing kitty_A {}, swapwindow with direction 'l'", Colors::YELLOW, pos); - OK(getFromSocket("/dispatch swapwindow r")); + OK(getFromSocket("/dispatch swapwindow l")); OK(getFromSocket("/dispatch focuswindow class:kitty_B")); EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("{}", pos)); @@ -566,98 +566,6 @@ static bool testWindowRuleFocusOnActivate() { return true; } -// tests if a pinned window contains the valid workspace after change -static bool testPinnedWorkspacesValid() { - OK(getFromSocket("/reload")); - getFromSocket("/dispatch workspace 1337"); - - if (!spawnKitty("kitty")) { - NLog::log("{}Error: failed to spawn kitty", Colors::RED); - return false; - } - - OK(getFromSocket("/dispatch setfloating class:kitty")); - OK(getFromSocket("/dispatch pin class:kitty")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT(str.contains("workspace: 1337"), true); - EXPECT(str.contains("pinned: 1"), true); - } - - getFromSocket("/dispatch workspace 1338"); - - { - auto str = getFromSocket("/activewindow"); - EXPECT(str.contains("workspace: 1338"), true); - EXPECT(str.contains("pinned: 1"), true); - } - - OK(getFromSocket("/dispatch settiled class:kitty")) - - { - auto str = getFromSocket("/activewindow"); - EXPECT(str.contains("workspace: 1338"), true); - EXPECT(str.contains("pinned: 0"), true); - } - - NLog::log("{}Reloading config", Colors::YELLOW); - OK(getFromSocket("/reload")); - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - return true; -} - -static bool testWindowRuleWorkspaceEmpty() { - NLog::log("{}Testing windowrule workspace empty", Colors::YELLOW); - OK(getFromSocket("/reload")); - - OK(getFromSocket("/keyword windowrule match:class kitty_A, workspace empty")); - OK(getFromSocket("/keyword windowrule match:class kitty_B, workspace emptyn")); - - getFromSocket("/dispatch workspace 3"); - - if (!spawnKitty("kitty")) { - NLog::log("{}Error: failed to spawn kitty", Colors::RED); - return false; - } - - { - auto str = getFromSocket("/activewindow"); - EXPECT(str.contains("workspace: 3"), true); - } - - if (!spawnKitty("kitty_A")) { - NLog::log("{}Error: failed to spawn kitty", Colors::RED); - return false; - } - - { - auto str = getFromSocket("/activewindow"); - EXPECT(str.contains("workspace: 1"), true); - } - - getFromSocket("/dispatch workspace 3"); - if (!spawnKitty("kitty_B")) { - NLog::log("{}Error: failed to spawn kitty", Colors::RED); - return false; - } - - { - auto str = getFromSocket("/activewindow"); - EXPECT(str.contains("workspace: 4"), true); - } - - Tests::killAllWindows(); - - return true; -} - static bool test() { NLog::log("{}Testing windows", Colors::GREEN); @@ -1086,7 +994,7 @@ static bool test() { OK(getFromSocket("/reload")); Tests::killAllWindows(); - OK(getFromSocket("/dispatch plugin:test:add_window_rule")); + OK(getFromSocket("/dispatch plugin:test:add_rule")); OK(getFromSocket("/reload")); OK(getFromSocket("/keyword windowrule match:class plugin_kitty, plugin_rule effect")); @@ -1094,12 +1002,12 @@ static bool test() { if (!spawnKitty("plugin_kitty")) return false; - OK(getFromSocket("/dispatch plugin:test:check_window_rule")); + OK(getFromSocket("/dispatch plugin:test:check_rule")); OK(getFromSocket("/reload")); Tests::killAllWindows(); - OK(getFromSocket("/dispatch plugin:test:add_window_rule")); + OK(getFromSocket("/dispatch plugin:test:add_rule")); OK(getFromSocket("/reload")); OK(getFromSocket("/keyword windowrule[test-plugin-rule]:match:class plugin_kitty")); @@ -1108,7 +1016,7 @@ static bool test() { if (!spawnKitty("plugin_kitty")) return false; - OK(getFromSocket("/dispatch plugin:test:check_window_rule")); + OK(getFromSocket("/dispatch plugin:test:check_rule")); OK(getFromSocket("/reload")); Tests::killAllWindows(); @@ -1120,8 +1028,6 @@ static bool test() { testGroupFallbackFocus(); testInitialFloatSize(); testWindowRuleFocusOnActivate(); - testPinnedWorkspacesValid(); - testWindowRuleWorkspaceEmpty(); NLog::log("{}Reloading config", Colors::YELLOW); OK(getFromSocket("/reload")); diff --git a/hyprtester/src/tests/main/workspaces.cpp b/hyprtester/src/tests/main/workspaces.cpp index 122cd619..a126d1b2 100644 --- a/hyprtester/src/tests/main/workspaces.cpp +++ b/hyprtester/src/tests/main/workspaces.cpp @@ -255,144 +255,6 @@ static void testMultimonBAF() { Tests::killAllWindows(); } -static void testMultimonFocus() { - NLog::log("{}Testing multimon focus and move", Colors::YELLOW); - - OK(getFromSocket("/keyword input:follow_mouse 0")); - OK(getFromSocket("/dispatch focusmonitor HEADLESS-3")); - OK(getFromSocket("/dispatch workspace 8")); - OK(getFromSocket("/dispatch focusmonitor HEADLESS-2")); - OK(getFromSocket("/dispatch workspace 7")); - - for (auto const& win : {"a", "b"}) { - if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; - } - } - - OK(getFromSocket("/dispatch focuswindow class:a")); - OK(getFromSocket("/dispatch movefocus r")); - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 7 "); - } - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); - } - - OK(getFromSocket("/dispatch movefocus r")); - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 8 "); - } - - Tests::spawnKitty("c"); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: c"); - } - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 8 "); - } - - OK(getFromSocket("/dispatch movefocus l")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); - } - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 7 "); - } - - OK(getFromSocket("/dispatch movewindow r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); - } - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 8 "); - } - - OK(getFromSocket("/dispatch movefocus r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: c"); - } - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 8 "); - } - - OK(getFromSocket("/dispatch movefocus l")); - - OK(getFromSocket("/keyword general:no_focus_fallback true")); - OK(getFromSocket("/keyword binds:window_direction_monitor_fallback false")); - - EXPECT_NOT(getFromSocket("/dispatch movefocus l"), "ok"); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); - } - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 8 "); - } - - OK(getFromSocket("/dispatch movewindow l")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); - } - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 8 "); - } - - OK(getFromSocket("/reload")); - - Tests::killAllWindows(); -} - -static void testDynamicWsEffects() { - // test dynamic workspace effects, they shouldn't lag - - OK(getFromSocket("/dispatch workspace 69")); - - Tests::spawnKitty("bitch"); - - OK(getFromSocket("r/keyword workspace 69,bordersize:20")); - OK(getFromSocket("r/keyword workspace 69,rounding:false")); - - EXPECT(getFromSocket("/getprop class:bitch border_size"), "20"); - EXPECT(getFromSocket("/getprop class:bitch rounding"), "0"); - - OK(getFromSocket("/reload")); - - Tests::killAllWindows(); -} - static bool test() { NLog::log("{}Testing workspaces", Colors::GREEN); @@ -732,14 +594,13 @@ static bool test() { Tests::killAllWindows(); testMultimonBAF(); - testMultimonFocus(); // destroy the headless output OK(getFromSocket("/output remove HEADLESS-3")); testSpecialWorkspaceFullscreen(); + testAsymmetricGaps(); - testDynamicWsEffects(); NLog::log("{}Expecting 0 windows", Colors::YELLOW); EXPECT(Tests::windowCount(), 0); diff --git a/hyprtester/src/tests/shared.cpp b/hyprtester/src/tests/shared.cpp index f6e2fce9..8cdd648e 100644 --- a/hyprtester/src/tests/shared.cpp +++ b/hyprtester/src/tests/shared.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include "../shared.hpp" #include "../hyprctlCompat.hpp" @@ -40,38 +39,6 @@ CUniquePointer Tests::spawnKitty(const std::string& class_, const std: return kitty; } -CUniquePointer Tests::spawnLayerKitty(const std::string& namespace_, const std::vector args) { - std::vector programArgs = args; - if (!namespace_.empty()) { - programArgs.insert(programArgs.begin(), "--class"); - programArgs.insert(programArgs.begin() + 1, namespace_); - } - - programArgs.insert(programArgs.begin(), "+kitten"); - programArgs.insert(programArgs.begin() + 1, "panel"); - - CUniquePointer kitty = makeUnique("kitty", programArgs); - kitty->addEnv("WAYLAND_DISPLAY", WLDISPLAY); - kitty->runAsync(); - - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - - // wait while the layer spawns - int counter = 0; - while (processAlive(kitty->pid()) && countOccurrences(getFromSocket("/layers"), std::format("pid: {}", kitty->pid())) == 0) { - counter++; - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - if (counter > 50) - return nullptr; - } - - if (!processAlive(kitty->pid())) - return nullptr; - - return kitty; -} - bool Tests::processAlive(pid_t pid) { errno = 0; int ret = kill(pid, 0); @@ -129,38 +96,6 @@ void Tests::waitUntilWindowsN(int n) { } } -int Tests::layerCount() { - return countOccurrences(getFromSocket("/layers"), "namespace: "); -} - -bool Tests::killAllLayers() { - auto str = getFromSocket("/layers"); - auto pos = str.find("pid: "); - while (pos != std::string::npos) { - auto pid = stoi(str.substr(pos + 5, str.find('\n', pos))); - kill(pid, 15); - - // we need to wait for a bit because for some reason otherwise we'll end up - // with layers with pid -1 if they are all removed at the same time - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - pos = str.find("pid: ", pos + 5); - } - - int counter = 0; - while (Tests::layerCount() != 0) { - counter++; - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - if (counter > 50) { - std::println("{}Timed out waiting for layers to close", Colors::RED); - return false; - } - } - - return true; -} - std::string Tests::execAndGet(const std::string& cmd) { CProcess proc("/bin/sh", {"-c", cmd}); @@ -170,14 +105,3 @@ std::string Tests::execAndGet(const std::string& cmd) { return proc.stdOut(); } - -bool Tests::writeFile(const std::string& name, const std::string& contents) { - std::ofstream of(name, std::ios::trunc); - if (!of.good()) - return false; - - of << contents; - of.close(); - - return true; -} diff --git a/hyprtester/src/tests/shared.hpp b/hyprtester/src/tests/shared.hpp index bf875f8b..fe28a69d 100644 --- a/hyprtester/src/tests/shared.hpp +++ b/hyprtester/src/tests/shared.hpp @@ -9,14 +9,10 @@ //NOLINTNEXTLINE namespace Tests { Hyprutils::Memory::CUniquePointer spawnKitty(const std::string& class_ = "", const std::vector args = {}); - Hyprutils::Memory::CUniquePointer spawnLayerKitty(const std::string& namespace_ = "", const std::vector args = {}); bool processAlive(pid_t pid); int windowCount(); int countOccurrences(const std::string& in, const std::string& what); bool killAllWindows(); void waitUntilWindowsN(int n); - int layerCount(); - bool killAllLayers(); std::string execAndGet(const std::string& cmd); - bool writeFile(const std::string& name, const std::string& contents); }; diff --git a/hyprtester/test.conf b/hyprtester/test.conf index ab4f8ee3..56beb5ea 100644 --- a/hyprtester/test.conf +++ b/hyprtester/test.conf @@ -179,17 +179,6 @@ master { new_status = master } -scrolling { - fullscreen_on_one_column = true - column_width = 0.5 - focus_fit_method = 1 - follow_focus = true - follow_min_visible = 1 - explicit_column_widths = 0.25, 0.333, 0.5, 0.667, 0.75, 1.0 - wrap_focus = true - wrap_swapcol = true -} - # https://wiki.hyprland.org/Configuring/Variables/#misc misc { force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers @@ -250,7 +239,7 @@ bind = $mainMod, E, exec, $fileManager bind = $mainMod, V, togglefloating, bind = $mainMod, R, exec, $menu bind = $mainMod, P, pseudo, # dwindle -bind = $mainMod, J, layoutmsg, togglesplit, # dwindle +bind = $mainMod, J, togglesplit, # dwindle # Move focus with mainMod + arrow keys bind = $mainMod, left, movefocus, l diff --git a/nix/default.nix b/nix/default.nix index bc5ba309..eee38887 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -12,7 +12,6 @@ epoll-shim, git, glaze-hyprland, - glslang, gtest, hyprcursor, hyprgraphics, @@ -22,7 +21,6 @@ hyprutils, hyprwayland-scanner, hyprwire, - lcms2, libGL, libdrm, libexecinfo, @@ -66,20 +64,8 @@ inherit (builtins) foldl' readFile; inherit (lib.asserts) assertMsg; inherit (lib.attrsets) mapAttrsToList; - inherit - (lib.lists) - flatten - concatLists - optional - optionals - ; - inherit - (lib.strings) - makeBinPath - optionalString - cmakeBool - trim - ; + inherit (lib.lists) flatten concatLists optional optionals; + inherit (lib.strings) makeBinPath optionalString cmakeBool trim; fs = lib.fileset; adapters = flatten [ @@ -91,8 +77,7 @@ in assert assertMsg (!nvidiaPatches) "The option `nvidiaPatches` has been removed."; assert assertMsg (!enableNvidiaPatches) "The option `enableNvidiaPatches` has been removed."; - assert assertMsg (!hidpiXWayland) - "The option `hidpiXWayland` has been removed. Please refer https://wiki.hypr.land/Configuring/XWayland"; + assert assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been removed. Please refer https://wiki.hypr.land/Configuring/XWayland"; assert assertMsg (!legacyRenderer) "The option `legacyRenderer` has been removed. Legacy renderer is no longer supported."; assert assertMsg (!withHyprtester) "The option `withHyprtester` has been removed. Hyprtester is always built now."; customStdenv.mkDerivation (finalAttrs: { @@ -105,29 +90,24 @@ in fs.intersection # allows non-flake builds to only include files tracked by git (fs.gitTracked ../.) - ( - fs.unions (flatten [ - ../assets/hyprland-portals.conf - ../assets/install - ../hyprctl - ../hyprland.pc.in - ../hyprpm - ../LICENSE - ../protocols - ../src - ../start - ../systemd - ../VERSION - (fs.fileFilter (file: file.hasExt "1") ../docs) - (fs.fileFilter (file: file.hasExt "conf" || file.hasExt "in") ../example) - (fs.fileFilter (file: file.hasExt "sh") ../scripts) - (fs.fileFilter (file: file.name == "CMakeLists.txt") ../.) - (optional withTests [ - ../tests - ../hyprtester - ]) - ]) - ); + (fs.unions (flatten [ + ../assets/hyprland-portals.conf + ../assets/install + ../hyprctl + ../hyprland.pc.in + ../hyprpm + ../LICENSE + ../protocols + ../src + ../start + ../systemd + ../VERSION + (fs.fileFilter (file: file.hasExt "1") ../docs) + (fs.fileFilter (file: file.hasExt "conf" || file.hasExt "in") ../example) + (fs.fileFilter (file: file.hasExt "sh") ../scripts) + (fs.fileFilter (file: file.name == "CMakeLists.txt") ../.) + (optional withTests [../tests ../hyprtester]) + ])); }; postPatch = '' @@ -143,10 +123,7 @@ in GIT_COMMITS = revCount; GIT_COMMIT_DATE = date; GIT_COMMIT_HASH = commit; - GIT_DIRTY = - if (commit == "") - then "clean" - else "dirty"; + GIT_DIRTY = if (commit == "") then "clean" else "dirty"; GIT_TAG = "v${trim (readFile "${finalAttrs.src}/VERSION")}"; }; @@ -174,7 +151,6 @@ in cairo git glaze-hyprland - glslang gtest hyprcursor hyprgraphics @@ -182,7 +158,6 @@ in hyprlang hyprutils hyprwire - lcms2 libdrm libgbm libGL @@ -243,14 +218,12 @@ in postInstall = '' ${optionalString wrapRuntimeDeps '' wrapProgram $out/bin/Hyprland \ - --suffix PATH : ${ - makeBinPath [ - binutils - hyprland-guiutils - pciutils - pkgconf - ] - } + --suffix PATH : ${makeBinPath [ + binutils + hyprland-guiutils + pciutils + pkgconf + ]} ''} ${optionalString withTests '' diff --git a/nix/formatter.nix b/nix/formatter.nix index ac340ae2..66721c2c 100644 --- a/nix/formatter.nix +++ b/nix/formatter.nix @@ -2,7 +2,7 @@ writeShellApplication, deadnix, statix, - nixfmt, + alejandra, llvmPackages_19, fd, }: @@ -11,7 +11,7 @@ writeShellApplication { runtimeInputs = [ deadnix statix - nixfmt + alejandra llvmPackages_19.clang-tools fd ]; @@ -24,14 +24,14 @@ writeShellApplication { nix_format() { if [ "$*" = 0 ]; then fd '.*\.nix' . -E "$excludes" -x statix fix -- {} \; - fd '.*\.nix' . -E "$excludes" -X deadnix -e -- {} \; -X nixfmt {} \; + fd '.*\.nix' . -E "$excludes" -X deadnix -e -- {} \; -X alejandra {} \; elif [ -d "$1" ]; then fd '.*\.nix' "$1" -E "$excludes" -i -x statix fix -- {} \; - fd '.*\.nix' "$1" -E "$excludes" -i -X deadnix -e -- {} \; -X nixfmt {} \; + fd '.*\.nix' "$1" -E "$excludes" -i -X deadnix -e -- {} \; -X alejandra {} \; else statix fix -- "$1" deadnix -e "$1" - nixfmt "$1" + alejandra "$1" fi } diff --git a/nix/hm-module.nix b/nix/hm-module.nix index 948b8217..e3c788d0 100644 --- a/nix/hm-module.nix +++ b/nix/hm-module.nix @@ -1,15 +1,13 @@ -self: -{ +self: { + config, lib, pkgs, ... -}: -let +}: let inherit (pkgs.stdenv.hostPlatform) system; package = self.packages.${system}.default; -in -{ +in { config = { wayland.windowManager.hyprland.package = lib.mkDefault package; }; diff --git a/nix/lib.nix b/nix/lib.nix index 54d23440..ca3aadee 100644 --- a/nix/lib.nix +++ b/nix/lib.nix @@ -1,5 +1,4 @@ -lib: -let +lib: let inherit (lib) attrNames filterAttrs @@ -18,7 +17,7 @@ let This function takes a nested attribute set and converts it into Hyprland-compatible configuration syntax, supporting top, bottom, and regular command sections. - + Commands are flattened using the `flattenAttrs` function, and attributes are formatted as `key = value` pairs. Lists are expanded as duplicate keys to match Hyprland's expected format. @@ -82,51 +81,44 @@ let ::: */ - toHyprlang = - { - topCommandsPrefixes ? [ - "$" - "bezier" - ], - bottomCommandsPrefixes ? [ ], - }: - attrs: - let - toHyprlang' = - attrs: - let - # Specially configured `toKeyValue` generator with support for duplicate keys - # and a legible key-value separator. - mkCommands = generators.toKeyValue { - mkKeyValue = generators.mkKeyValueDefault { } " = "; - listsAsDuplicateKeys = true; - indent = ""; # No indent, since we don't have nesting - }; + toHyprlang = { + topCommandsPrefixes ? ["$" "bezier"], + bottomCommandsPrefixes ? [], + }: attrs: let + toHyprlang' = attrs: let + # Specially configured `toKeyValue` generator with support for duplicate keys + # and a legible key-value separator. + mkCommands = generators.toKeyValue { + mkKeyValue = generators.mkKeyValueDefault {} " = "; + listsAsDuplicateKeys = true; + indent = ""; # No indent, since we don't have nesting + }; - # Flatten the attrset, combining keys in a "path" like `"a:b:c" = "x"`. - # Uses `flattenAttrs` with a colon separator. - commands = flattenAttrs (p: k: "${p}:${k}") attrs; + # Flatten the attrset, combining keys in a "path" like `"a:b:c" = "x"`. + # Uses `flattenAttrs` with a colon separator. + commands = flattenAttrs (p: k: "${p}:${k}") attrs; - # General filtering function to check if a key starts with any prefix in a given list. - filterCommands = list: n: foldl (acc: prefix: acc || hasPrefix prefix n) false list; + # General filtering function to check if a key starts with any prefix in a given list. + filterCommands = list: n: + foldl (acc: prefix: acc || hasPrefix prefix n) false list; - # Partition keys into top commands and the rest - result = partition (filterCommands topCommandsPrefixes) (attrNames commands); - topCommands = filterAttrs (n: _: builtins.elem n result.right) commands; - remainingCommands = removeAttrs commands result.right; + # Partition keys into top commands and the rest + result = partition (filterCommands topCommandsPrefixes) (attrNames commands); + topCommands = filterAttrs (n: _: builtins.elem n result.right) commands; + remainingCommands = removeAttrs commands result.right; - # Partition remaining commands into bottom commands and regular commands - result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong; - bottomCommands = filterAttrs (n: _: builtins.elem n result2.right) remainingCommands; - regularCommands = removeAttrs remainingCommands result2.right; - in - # Concatenate strings from mapping `mkCommands` over top, regular, and bottom commands. - concatMapStrings mkCommands [ - topCommands - regularCommands - bottomCommands - ]; + # Partition remaining commands into bottom commands and regular commands + result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong; + bottomCommands = filterAttrs (n: _: builtins.elem n result2.right) remainingCommands; + regularCommands = removeAttrs remainingCommands result2.right; in + # Concatenate strings from mapping `mkCommands` over top, regular, and bottom commands. + concatMapStrings mkCommands [ + topCommands + regularCommands + bottomCommands + ]; + in toHyprlang' attrs; /** @@ -139,7 +131,7 @@ let Configuration: * `pred` - A function `(string -> string -> string)` defining how keys should be concatenated. - + # Inputs Structured function argument: @@ -147,7 +139,7 @@ let : pred (required) : A function that determines how parent and child keys should be combined into a single key. It takes a `prefix` (parent key) and `key` (current key) and returns the joined key. - + Value: : The nested attribute set to be flattened. @@ -182,21 +174,26 @@ let ``` ::: + */ - flattenAttrs = - pred: attrs: - let - flattenAttrs' = - prefix: attrs: - builtins.foldl' ( - acc: key: - let - value = attrs.${key}; - newKey = if prefix == "" then key else pred prefix key; - in - acc // (if builtins.isAttrs value then flattenAttrs' newKey value else { "${newKey}" = value; }) - ) { } (builtins.attrNames attrs); - in + flattenAttrs = pred: attrs: let + flattenAttrs' = prefix: attrs: + builtins.foldl' ( + acc: key: let + value = attrs.${key}; + newKey = + if prefix == "" + then key + else pred prefix key; + in + acc + // ( + if builtins.isAttrs value + then flattenAttrs' newKey value + else {"${newKey}" = value;} + ) + ) {} (builtins.attrNames attrs); + in flattenAttrs' "" attrs; in { diff --git a/nix/module.nix b/nix/module.nix index 32263943..91705347 100644 --- a/nix/module.nix +++ b/nix/module.nix @@ -1,21 +1,18 @@ -inputs: -{ +inputs: { config, lib, pkgs, ... -}: -let +}: let inherit (pkgs.stdenv.hostPlatform) system; selflib = import ./lib.nix lib; cfg = config.programs.hyprland; -in -{ +in { options = { programs.hyprland = { plugins = lib.mkOption { type = with lib.types; listOf (either package path); - default = [ ]; + default = []; description = '' List of Hyprland plugins to use. Can either be packages or absolute plugin paths. @@ -23,25 +20,23 @@ in }; settings = lib.mkOption { - type = - with lib.types; - let - valueType = - nullOr (oneOf [ - bool - int - float - str - path - (attrsOf valueType) - (listOf valueType) - ]) - // { - description = "Hyprland configuration value"; - }; - in + type = with lib.types; let + valueType = + nullOr (oneOf [ + bool + int + float + str + path + (attrsOf valueType) + (listOf valueType) + ]) + // { + description = "Hyprland configuration value"; + }; + in valueType; - default = { }; + default = {}; description = '' Hyprland configuration written in Nix. Entries with the same key should be written as lists. Variables' and colors' names should be @@ -97,15 +92,8 @@ in topPrefixes = lib.mkOption { type = with lib.types; listOf str; - default = [ - "$" - "bezier" - ]; - example = [ - "$" - "bezier" - "source" - ]; + default = ["$" "bezier"]; + example = ["$" "bezier" "source"]; description = '' List of prefix of attributes to put at the top of the config. ''; @@ -113,8 +101,8 @@ in bottomPrefixes = lib.mkOption { type = with lib.types; listOf str; - default = [ ]; - example = [ "source" ]; + default = []; + example = ["source"]; description = '' List of prefix of attributes to put at the bottom of the config. ''; @@ -129,36 +117,35 @@ in }; } (lib.mkIf cfg.enable { - environment.etc."xdg/hypr/hyprland.conf" = - let - shouldGenerate = cfg.extraConfig != "" || cfg.settings != { } || cfg.plugins != [ ]; + environment.etc."xdg/hypr/hyprland.conf" = let + shouldGenerate = cfg.extraConfig != "" || cfg.settings != {} || cfg.plugins != []; - pluginsToHyprlang = - _plugins: - selflib.toHyprlang - { + pluginsToHyprlang = plugins: + selflib.toHyprlang { + topCommandsPrefixes = cfg.topPrefixes; + bottomCommandsPrefixes = cfg.bottomPrefixes; + } + { + "exec-once" = let + mkEntry = entry: + if lib.types.package.check entry + then "${entry}/lib/lib${entry.pname}.so" + else entry; + hyprctl = lib.getExe' config.programs.hyprland.package "hyprctl"; + in + map (p: "${hyprctl} plugin load ${mkEntry p}") cfg.plugins; + }; + in + lib.mkIf shouldGenerate { + text = + lib.optionalString (cfg.plugins != []) + (pluginsToHyprlang cfg.plugins) + + lib.optionalString (cfg.settings != {}) + (selflib.toHyprlang { topCommandsPrefixes = cfg.topPrefixes; bottomCommandsPrefixes = cfg.bottomPrefixes; } - { - "exec-once" = - let - mkEntry = - entry: if lib.types.package.check entry then "${entry}/lib/lib${entry.pname}.so" else entry; - hyprctl = lib.getExe' config.programs.hyprland.package "hyprctl"; - in - map (p: "${hyprctl} plugin load ${mkEntry p}") cfg.plugins; - }; - in - lib.mkIf shouldGenerate { - text = - lib.optionalString (cfg.plugins != [ ]) (pluginsToHyprlang cfg.plugins) - + lib.optionalString (cfg.settings != { }) ( - selflib.toHyprlang { - topCommandsPrefixes = cfg.topPrefixes; - bottomCommandsPrefixes = cfg.bottomPrefixes; - } cfg.settings - ) + cfg.settings) + lib.optionalString (cfg.extraConfig != "") cfg.extraConfig; }; }) diff --git a/nix/overlays.nix b/nix/overlays.nix index 0d157701..fdb3e652 100644 --- a/nix/overlays.nix +++ b/nix/overlays.nix @@ -2,27 +2,20 @@ self, lib, inputs, -}: -let - mkDate = - longDate: - (lib.concatStringsSep "-" [ - (builtins.substring 0 4 longDate) - (builtins.substring 4 2 longDate) - (builtins.substring 6 2 longDate) - ]); +}: let + mkDate = longDate: (lib.concatStringsSep "-" [ + (builtins.substring 0 4 longDate) + (builtins.substring 4 2 longDate) + (builtins.substring 6 2 longDate) + ]); ver = lib.removeSuffix "\n" (builtins.readFile ../VERSION); -in -{ +in { # Contains what a user is most likely to care about: # Hyprland itself, XDPH and the Share Picker. - default = lib.composeManyExtensions ( - with self.overlays; - [ - hyprland-packages - hyprland-extras - ] - ); + default = lib.composeManyExtensions (with self.overlays; [ + hyprland-packages + hyprland-extras + ]); # Packages for variations of Hyprland, dependencies included. hyprland-packages = lib.composeManyExtensions [ @@ -40,45 +33,49 @@ in self.overlays.glaze # Hyprland packages themselves - ( - final: _prev: - let - date = mkDate (self.lastModifiedDate or "19700101"); - version = "${ver}+date=${date}_${self.shortRev or "dirty"}"; - in - { - hyprland = final.callPackage ./default.nix { - stdenv = final.gcc15Stdenv; - commit = self.rev or ""; - revCount = self.sourceInfo.revCount or ""; - inherit date version; - }; - hyprland-unwrapped = final.hyprland.override { wrapRuntimeDeps = false; }; + (final: _prev: let + date = mkDate (self.lastModifiedDate or "19700101"); + version = "${ver}+date=${date}_${self.shortRev or "dirty"}"; + in { + hyprland = final.callPackage ./default.nix { + stdenv = final.gcc15Stdenv; + commit = self.rev or ""; + revCount = self.sourceInfo.revCount or ""; + inherit date version; + }; + hyprland-unwrapped = final.hyprland.override {wrapRuntimeDeps = false;}; - hyprland-with-tests = final.hyprland.override { withTests = true; }; + hyprland-with-tests = final.hyprland.override {withTests = true;}; - hyprland-with-hyprtester = builtins.trace '' + hyprland-with-hyprtester = + builtins.trace '' hyprland-with-hyprtester was removed. Please use the hyprland package. Hyprtester is always built now. - '' final.hyprland; + '' + final.hyprland; - # deprecated packages - hyprland-legacy-renderer = builtins.trace '' + # deprecated packages + hyprland-legacy-renderer = + builtins.trace '' hyprland-legacy-renderer was removed. Please use the hyprland package. Legacy renderer is no longer supported. - '' final.hyprland; + '' + final.hyprland; - hyprland-nvidia = builtins.trace '' + hyprland-nvidia = + builtins.trace '' hyprland-nvidia was removed. Please use the hyprland package. Nvidia patches are no longer needed. - '' final.hyprland; + '' + final.hyprland; - hyprland-hidpi = builtins.trace '' + hyprland-hidpi = + builtins.trace '' hyprland-hidpi was removed. Please use the hyprland package. For more information, refer to https://wiki.hypr.land/Configuring/XWayland. - '' final.hyprland; - } - ) + '' + final.hyprland; + }) ]; # Debug @@ -86,10 +83,10 @@ in # Dependencies self.overlays.hyprland-packages - (_final: prev: { - aquamarine = prev.aquamarine.override { debug = true; }; - hyprutils = prev.hyprutils.override { debug = true; }; - hyprland-debug = prev.hyprland.override { debug = true; }; + (final: prev: { + aquamarine = prev.aquamarine.override {debug = true;}; + hyprutils = prev.hyprutils.override {debug = true;}; + hyprland-debug = prev.hyprland.override {debug = true;}; }) ]; @@ -103,23 +100,21 @@ in # this version is the one used in the git submodule, and allows us to # fetch the source without '?submodules=1' udis86 = final: prev: { - udis86-hyprland = prev.udis86.overrideAttrs ( - _self: _super: { - src = final.fetchFromGitHub { - owner = "canihavesomecoffee"; - repo = "udis86"; - rev = "5336633af70f3917760a6d441ff02d93477b0c86"; - hash = "sha256-HifdUQPGsKQKQprByeIznvRLONdOXeolOsU5nkwIv3g="; - }; + udis86-hyprland = prev.udis86.overrideAttrs (_self: _super: { + src = final.fetchFromGitHub { + owner = "canihavesomecoffee"; + repo = "udis86"; + rev = "5336633af70f3917760a6d441ff02d93477b0c86"; + hash = "sha256-HifdUQPGsKQKQprByeIznvRLONdOXeolOsU5nkwIv3g="; + }; - patches = [ ]; - } - ); + patches = []; + }); }; # Even though glaze itself disables it by default, nixpkgs sets ENABLE_SSL set to true. # Since we don't include openssl, the build failes without the `enableSSL = false;` override - glaze = _final: prev: { + glaze = final: prev: { glaze-hyprland = prev.glaze.override { enableSSL = false; enableInterop = false; diff --git a/nix/tests/default.nix b/nix/tests/default.nix index 25c4077b..6052ee16 100644 --- a/nix/tests/default.nix +++ b/nix/tests/default.nix @@ -1,76 +1,72 @@ -inputs: pkgs: -let +inputs: pkgs: let flake = inputs.self.packages.${pkgs.stdenv.hostPlatform.system}; hyprland = flake.hyprland-with-tests; -in -{ +in { tests = pkgs.testers.runNixOSTest { name = "hyprland-tests"; - nodes.machine = - { pkgs, ... }: - { - environment.systemPackages = with pkgs; [ - # Programs needed for tests - jq - kitty - wl-clipboard - xeyes - ]; + nodes.machine = {pkgs, ...}: { + environment.systemPackages = with pkgs; [ + # Programs needed for tests + jq + kitty + wl-clipboard + xeyes + ]; - # Enabled by default for some reason - services.speechd.enable = false; + # Enabled by default for some reason + services.speechd.enable = false; - environment.variables = { - "AQ_TRACE" = "1"; - "HYPRLAND_TRACE" = "1"; - "XDG_RUNTIME_DIR" = "/tmp"; - "XDG_CACHE_HOME" = "/tmp"; - "KITTY_CONFIG_DIRECTORY" = "/etc/kitty"; - }; - - environment.etc."kitty/kitty.conf".text = '' - confirm_os_window_close 0 - remember_window_size no - initial_window_width 640 - initial_window_height 400 - ''; - - programs.hyprland = { - enable = true; - package = hyprland; - # We don't need portals in this test, so we don't set portalPackage - }; - - # Test configuration - environment.etc."test.conf".source = "${hyprland}/share/hypr/test.conf"; - - # Disable portals - xdg.portal.enable = pkgs.lib.mkForce false; - - # Autologin root into tty - services.getty.autologinUser = "alice"; - - system.stateVersion = "24.11"; - - users.users.alice = { - isNormalUser = true; - }; - - virtualisation = { - cores = 4; - # Might crash with less - memorySize = 8192; - resolution = { - x = 1920; - y = 1080; - }; - - # Doesn't seem to do much, thought it would fix XWayland crashing - qemu.options = [ "-vga none -device virtio-gpu-pci" ]; - }; + environment.variables = { + "AQ_TRACE" = "1"; + "HYPRLAND_TRACE" = "1"; + "XDG_RUNTIME_DIR" = "/tmp"; + "XDG_CACHE_HOME" = "/tmp"; + "KITTY_CONFIG_DIRECTORY" = "/etc/kitty"; }; + environment.etc."kitty/kitty.conf".text = '' + confirm_os_window_close 0 + remember_window_size no + initial_window_width 640 + initial_window_height 400 + ''; + + programs.hyprland = { + enable = true; + package = hyprland; + # We don't need portals in this test, so we don't set portalPackage + }; + + # Test configuration + environment.etc."test.conf".source = "${hyprland}/share/hypr/test.conf"; + + # Disable portals + xdg.portal.enable = pkgs.lib.mkForce false; + + # Autologin root into tty + services.getty.autologinUser = "alice"; + + system.stateVersion = "24.11"; + + users.users.alice = { + isNormalUser = true; + }; + + virtualisation = { + cores = 4; + # Might crash with less + memorySize = 8192; + resolution = { + x = 1920; + y = 1080; + }; + + # Doesn't seem to do much, thought it would fix XWayland crashing + qemu.options = ["-vga none -device virtio-gpu-pci"]; + }; + }; + testScript = '' # Wait for tty to be up machine.wait_for_unit("multi-user.target") diff --git a/scripts/generateShaderIncludes.sh b/scripts/generateShaderIncludes.sh index c9419031..20c78e9d 100755 --- a/scripts/generateShaderIncludes.sh +++ b/scripts/generateShaderIncludes.sh @@ -15,7 +15,7 @@ echo 'static const std::map SHADERS = {' >> ./src/rend for filename in `ls ${SHADERS_SRC}`; do echo "-- ${filename}" - { echo -n 'R"#('; cat ${SHADERS_SRC}/${filename}; echo ')#"'; } > ./src/render/shaders/${filename}.inc + { echo 'R"#('; cat ${SHADERS_SRC}/${filename}; echo ')#"'; } > ./src/render/shaders/${filename}.inc echo "{\"${filename}\"," >> ./src/render/shaders/Shaders.hpp echo "#include \"./${filename}.inc\"" >> ./src/render/shaders/Shaders.hpp echo "}," >> ./src/render/shaders/Shaders.hpp diff --git a/src/Compositor.cpp b/src/Compositor.cpp index b0fc1545..e027b563 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -47,7 +47,6 @@ #include "protocols/core/Compositor.hpp" #include "protocols/core/Subcompositor.hpp" #include "desktop/view/LayerSurface.hpp" -#include "layout/space/Space.hpp" #include "render/Renderer.hpp" #include "xwayland/XWayland.hpp" #include "helpers/ByteOperations.hpp" @@ -1405,35 +1404,6 @@ PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorks PHLWINDOW leaderWindow = nullptr; if (!useVectorAngles) { - // helper to check if two rectangles are adjacent along an axis, considering slight overlaps. - // returns true if: STICKS (delta <= 2) OR rectangles overlap but no more than 50% of the smaller dimension. - static auto isAdjacent = [](const double aMin, const double aMax, const double bMin, const double bMax) -> bool { - constexpr double STICK_THRESHOLD = 2.0; - constexpr double MAX_OVERLAP_RATIO = 0.5; - - const double aEdge = aMin; - const double bEdge = bMax; - const double delta = aEdge - bEdge; - - // old STICKS check for 2px - if (std::abs(delta) < STICK_THRESHOLD) - return true; - - if (delta >= 0) - return false; - - const double overlap = -delta; - const double sizeA = aMax - aMin; - const double sizeB = bMax - bMin; - - // reject if one rectangle fully contains the other - if ((bMin <= aMin && bMax >= aMax) || (aMin <= bMin && aMax >= bMax)) - return false; - - // accept if overlap is at most 50% of the smaller dimension - return overlap <= std::min(sizeA, sizeB) * MAX_OVERLAP_RATIO; - }; - for (auto const& w : m_windows) { if (w == ignoreWindow || !w->m_workspace || !w->m_isMapped || w->isHidden() || (!w->isFullscreen() && w->m_isFloating) || !w->m_workspace->isVisible()) continue; @@ -1456,20 +1426,24 @@ PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorks switch (dir) { case Math::DIRECTION_LEFT: - if (isAdjacent(POSA.x, POSA.x + SIZEA.x, POSB.x, POSB.x + SIZEB.x)) + if (STICKS(POSA.x, POSB.x + SIZEB.x)) { intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y)); + } break; case Math::DIRECTION_RIGHT: - if (isAdjacent(POSB.x, POSB.x + SIZEB.x, POSA.x, POSA.x + SIZEA.x)) + if (STICKS(POSA.x + SIZEA.x, POSB.x)) { intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y)); + } break; case Math::DIRECTION_UP: - if (isAdjacent(POSA.y, POSA.y + SIZEA.y, POSB.y, POSB.y + SIZEB.y)) + if (STICKS(POSA.y, POSB.y + SIZEB.y)) { intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x)); + } break; case Math::DIRECTION_DOWN: - if (isAdjacent(POSB.y, POSB.y + SIZEB.y, POSA.y, POSA.y + SIZEA.y)) + if (STICKS(POSA.y + SIZEA.y, POSB.y)) { intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x)); + } break; default: break; } @@ -1825,9 +1799,6 @@ void CCompositor::swapActiveWorkspaces(PHLMONITOR pMonitorA, PHLMONITOR pMonitor g_layoutManager->recalculateMonitor(pMonitorA); g_layoutManager->recalculateMonitor(pMonitorB); - g_pHyprRenderer->damageMonitor(pMonitorB); - g_pHyprRenderer->damageMonitor(pMonitorA); - g_pDesktopAnimationManager->setFullscreenFadeAnimation( PWORKSPACEB, PWORKSPACEB->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT); g_pDesktopAnimationManager->setFullscreenFadeAnimation( @@ -1982,7 +1953,6 @@ void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, PHLMONITOR pMo // move the workspace pWorkspace->m_monitor = pMonitor; - pWorkspace->m_space->recheckWorkArea(); pWorkspace->m_events.monitorChanged.emit(); for (auto const& w : m_windows) { @@ -2038,7 +2008,6 @@ void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, PHLMONITOR pMo pWorkspace->m_events.activeChanged.emit(); g_layoutManager->recalculateMonitor(pMonitor); - g_pHyprRenderer->damageMonitor(pMonitor); g_pDesktopAnimationManager->startAnimation(pWorkspace, CDesktopAnimationManager::ANIMATION_TYPE_IN, true, true); pWorkspace->m_visible = true; @@ -2778,7 +2747,6 @@ void CCompositor::arrangeMonitors() { } PROTO::xdgOutput->updateAllOutputs(); - Event::bus()->m_events.monitor.layoutChanged.emit(); #ifndef NO_XWAYLAND const auto box = g_pCompositor->calculateX11WorkArea(); @@ -3074,27 +3042,6 @@ void CCompositor::ensurePersistentWorkspacesPresent(const std::vectorm_isSpecialWorkspace) - continue; - - const auto RULE = g_pConfigManager->getWorkspaceRuleFor(ws); - if (RULE.monitor.empty()) - continue; - - const auto PMONITOR = getMonitorFromString(RULE.monitor); - if (!PMONITOR) - continue; - - if (ws->m_monitor == PMONITOR) - continue; - - Log::logger->log(Log::DEBUG, "ensureWorkspacesOnAssignedMonitors: moving workspace {} to {}", ws->m_name, PMONITOR->m_name); - moveWorkspaceToMonitor(ws, PMONITOR, true); - } -} - std::optional CCompositor::getVTNr() { if (!m_aqBackend->hasSession()) return std::nullopt; diff --git a/src/Compositor.hpp b/src/Compositor.hpp index 6d2044fe..d5317e9b 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -9,7 +9,7 @@ #include "managers/KeybindManager.hpp" #include "managers/SessionLockManager.hpp" #include "desktop/view/Window.hpp" -#include "helpers/cm/ColorManagement.hpp" +#include "protocols/types/ColorManagement.hpp" #include #include @@ -161,7 +161,6 @@ class CCompositor { void updateSuspendedStates(); void onNewMonitor(SP output); void ensurePersistentWorkspacesPresent(const std::vector& rules, PHLWORKSPACE pWorkspace = nullptr); - void ensureWorkspacesOnAssignedMonitors(); std::optional getVTNr(); bool isVRRActiveOnAnyMonitor() const; diff --git a/src/config/ConfigDescriptions.hpp b/src/config/ConfigDescriptions.hpp index f6f777d1..ef6ca535 100644 --- a/src/config/ConfigDescriptions.hpp +++ b/src/config/ConfigDescriptions.hpp @@ -1579,18 +1579,6 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_BOOL, .data = SConfigOptionDescription::SBoolData{true}, }, - SConfigOptionDescription{ - .value = "render:icc_vcgt_enabled", - .description = "Enable sending VCGT ramps to KMS with ICC profiles", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{true}, - }, - { - .value = "render:use_shader_blur_blend", - .description = "Use experimental blurred bg blending", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{false}, - }, /* * cursor: @@ -2105,18 +2093,6 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_CHOICE, .data = SConfigOptionDescription::SChoiceData{.firstIndex = 0, .choices = "right,left,down,up"}, }, - SConfigOptionDescription{ - .value = "scrolling:wrap_focus", - .description = "Determines if column focus wraps around when going before the first column or past the last column", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{.value = true}, - }, - SConfigOptionDescription{ - .value = "scrolling:wrap_swapcol", - .description = "Determines if column movement wraps around when moving to before the first column or past the last column", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{.value = true}, - }, /* * Quirks diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 29967e58..cb38154e 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -655,8 +655,6 @@ CConfigManager::CConfigManager() { registerConfigVar("scrolling:follow_min_visible", Hyprlang::FLOAT{0.4}); registerConfigVar("scrolling:explicit_column_widths", Hyprlang::STRING{"0.333, 0.5, 0.667, 1.0"}); registerConfigVar("scrolling:direction", Hyprlang::STRING{"right"}); - registerConfigVar("scrolling:wrap_focus", Hyprlang::INT{1}); - registerConfigVar("scrolling:wrap_swapcol", Hyprlang::INT{1}); registerConfigVar("animations:enabled", Hyprlang::INT{1}); registerConfigVar("animations:workspace_wraparound", Hyprlang::INT{0}); @@ -799,8 +797,6 @@ CConfigManager::CConfigManager() { registerConfigVar("render:non_shader_cm", Hyprlang::INT{3}); registerConfigVar("render:cm_sdr_eotf", {"default"}); registerConfigVar("render:commit_timing_enabled", Hyprlang::INT{1}); - registerConfigVar("render:icc_vcgt_enabled", Hyprlang::INT{1}); - registerConfigVar("render:use_shader_blur_blend", Hyprlang::INT{0}); registerConfigVar("ecosystem:no_update_news", Hyprlang::INT{0}); registerConfigVar("ecosystem:no_donation_nag", Hyprlang::INT{0}); @@ -875,7 +871,6 @@ CConfigManager::CConfigManager() { m_config->addSpecialConfigValue("monitorv2", "min_luminance", Hyprlang::FLOAT{-1.0}); m_config->addSpecialConfigValue("monitorv2", "max_luminance", Hyprlang::INT{-1}); m_config->addSpecialConfigValue("monitorv2", "max_avg_luminance", Hyprlang::INT{-1}); - m_config->addSpecialConfigValue("monitorv2", "icc", Hyprlang::STRING{""}); // windowrule v3 m_config->addSpecialCategory("windowrule", {.key = "name"}); @@ -1262,10 +1257,6 @@ std::optional CConfigManager::handleMonitorv2(const std::string& ou if (VAL && VAL->m_bSetByUser) parser.rule().maxAvgLuminance = std::any_cast(VAL->getValue()); - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "icc", output.c_str()); - if (VAL && VAL->m_bSetByUser) - parser.rule().iccFile = std::any_cast(VAL->getValue()); - auto newrule = parser.rule(); std::erase_if(m_monitorRules, [&](const auto& other) { return other.name == newrule.name; }); @@ -2284,15 +2275,6 @@ bool CMonitorRuleParser::parseVRR(const std::string& value) { return true; } -bool CMonitorRuleParser::parseICC(const std::string& val) { - if (val.empty()) { - m_error += "invalid icc "; - return false; - } - m_rule.iccFile = val; - return true; -} - void CMonitorRuleParser::setDisabled() { m_rule.disabled = true; } @@ -2387,9 +2369,6 @@ std::optional CConfigManager::handleMonitor(const std::string& comm } else if (ARGS[argno] == "vrr") { parser.parseVRR(std::string(ARGS[argno + 1])); argno++; - } else if (ARGS[argno] == "icc") { - parser.parseICC(std::string(ARGS[argno + 1])); - argno++; } else if (ARGS[argno] == "workspace") { const auto& [id, name, isAutoID] = getWorkspaceIDNameFromString(std::string(ARGS[argno + 1])); diff --git a/src/config/ConfigManager.hpp b/src/config/ConfigManager.hpp index 36e25f6b..21a3c58c 100644 --- a/src/config/ConfigManager.hpp +++ b/src/config/ConfigManager.hpp @@ -177,7 +177,6 @@ class CMonitorRuleParser { bool parseSDRBrightness(const std::string& value); bool parseSDRSaturation(const std::string& value); bool parseVRR(const std::string& value); - bool parseICC(const std::string& value); void setDisabled(); void setMirror(const std::string& value); diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index 3f51e8df..f3a9402d 100644 --- a/src/debug/HyprCtl.cpp +++ b/src/debug/HyprCtl.cpp @@ -2049,12 +2049,7 @@ static std::string submapRequest(eHyprCtlOutputFormat format, std::string reques } static std::string reloadShaders(eHyprCtlOutputFormat format, std::string request) { - CVarList vars(request, 0, ' '); - - if (vars.size() > 2) - return "too many args"; - - if (g_pHyprOpenGL && g_pHyprRenderer->reloadShaders(vars.size() == 2 ? vars[1] : "")) + if (g_pHyprOpenGL->initShaders()) return format == FORMAT_JSON ? "{\"ok\": true}" : "ok"; else return format == FORMAT_JSON ? "{\"ok\": false}" : "error"; @@ -2081,8 +2076,8 @@ CHyprCtl::CHyprCtl() { registerCommand(SHyprCtlCommand{"locked", true, getIsLocked}); registerCommand(SHyprCtlCommand{"descriptions", true, getDescriptions}); registerCommand(SHyprCtlCommand{"submap", true, submapRequest}); + registerCommand(SHyprCtlCommand{.name = "reloadshaders", .exact = true, .fn = reloadShaders}); - registerCommand(SHyprCtlCommand{.name = "reloadshaders", .exact = false, .fn = reloadShaders}); registerCommand(SHyprCtlCommand{"monitors", false, monitorsRequest}); registerCommand(SHyprCtlCommand{"reload", false, reloadRequest}); registerCommand(SHyprCtlCommand{"plugin", false, dispatchPlugin}); @@ -2202,15 +2197,6 @@ std::string CHyprCtl::getReply(std::string request) { Desktop::Rule::ruleEngine()->updateAllRules(); } - for (const auto& ws : g_pCompositor->getWorkspaces()) { - if (!ws) - continue; - - ws->updateWindows(); - ws->updateWindowData(); - ws->updateWindowDecos(); - } - for (auto const& m : g_pCompositor->m_monitors) { g_pHyprRenderer->damageMonitor(m); } @@ -2224,38 +2210,16 @@ std::string CHyprCtl::makeDynamicCall(const std::string& input) { } static bool successWrite(int fd, const std::string& data, bool needLog = true) { - size_t totalWritten = 0; - size_t remaining = data.length(); - size_t waitsDone = 0; - constexpr const size_t MAX_WAITS = 20; // 2000µs = 2ms + if (write(fd, data.c_str(), data.length()) > 0) + return true; - while (totalWritten < data.length()) { - ssize_t written = write(fd, data.c_str() + totalWritten, remaining); + if (errno == EAGAIN) + return true; - if (waitsDone > MAX_WAITS) { - Log::logger->log(Log::ERR, "Couldn't write to socket. Buffer was full and the client couldn't read in time."); - return false; - } + if (needLog) + Log::logger->log(Log::ERR, "Couldn't write to socket. Error: " + std::string(strerror(errno))); - if (written < 0) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - // socket buffer full, wait a bit and retry - std::this_thread::sleep_for(std::chrono::microseconds(100)); - waitsDone++; - continue; - } - if (needLog) - Log::logger->log(Log::ERR, "Couldn't write to socket. Error: {}", strerror(errno)); - return false; - } - - waitsDone = 0; - - totalWritten += written; - remaining -= written; - } - - return true; + return false; } static void runWritingDebugLogThread(const int conn) { diff --git a/src/debug/HyprDebugOverlay.cpp b/src/debug/HyprDebugOverlay.cpp index 17ce12fa..8f4189b0 100644 --- a/src/debug/HyprDebugOverlay.cpp +++ b/src/debug/HyprDebugOverlay.cpp @@ -8,7 +8,7 @@ #include "../desktop/state/FocusState.hpp" CHyprDebugOverlay::CHyprDebugOverlay() { - m_texture = g_pHyprRenderer->createTexture(); + m_texture = makeShared(); } void CHyprMonitorDebugOverlay::renderData(PHLMONITOR pMonitor, float durationUs) { @@ -259,7 +259,15 @@ void CHyprDebugOverlay::draw() { cairo_surface_flush(m_cairoSurface); // copy the data to an OpenGL texture we have - m_texture = g_pHyprRenderer->createTexture(m_cairoSurface); + const auto DATA = cairo_image_surface_get_data(m_cairoSurface); + m_texture->allocate(); + m_texture->bind(); + m_texture->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST); + m_texture->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST); + m_texture->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); + m_texture->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); CTexPassElement::SRenderData data; data.tex = m_texture; diff --git a/src/debug/HyprDebugOverlay.hpp b/src/debug/HyprDebugOverlay.hpp index bf188359..72987d94 100644 --- a/src/debug/HyprDebugOverlay.hpp +++ b/src/debug/HyprDebugOverlay.hpp @@ -42,7 +42,7 @@ class CHyprDebugOverlay { cairo_surface_t* m_cairoSurface = nullptr; cairo_t* m_cairo = nullptr; - SP m_texture; + SP m_texture; friend class CHyprMonitorDebugOverlay; friend class CHyprRenderer; diff --git a/src/debug/HyprNotificationOverlay.cpp b/src/debug/HyprNotificationOverlay.cpp index e67b0434..6b3c3ea8 100644 --- a/src/debug/HyprNotificationOverlay.cpp +++ b/src/debug/HyprNotificationOverlay.cpp @@ -28,6 +28,8 @@ CHyprNotificationOverlay::CHyprNotificationOverlay() { g_pHyprRenderer->damageBox(m_lastDamage); }); + + m_texture = makeShared(); } CHyprNotificationOverlay::~CHyprNotificationOverlay() { @@ -230,7 +232,16 @@ void CHyprNotificationOverlay::draw(PHLMONITOR pMonitor) { m_lastDamage = damage; - m_texture = g_pHyprRenderer->createTexture(m_cairoSurface); + // copy the data to an OpenGL texture we have + const auto DATA = cairo_image_surface_get_data(m_cairoSurface); + m_texture->allocate(); + m_texture->bind(); + m_texture->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST); + m_texture->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST); + m_texture->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); + m_texture->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, MONSIZE.x, MONSIZE.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); CTexPassElement::SRenderData data; data.tex = m_texture; diff --git a/src/debug/HyprNotificationOverlay.hpp b/src/debug/HyprNotificationOverlay.hpp index ec7aed72..868eb05b 100644 --- a/src/debug/HyprNotificationOverlay.hpp +++ b/src/debug/HyprNotificationOverlay.hpp @@ -18,7 +18,7 @@ enum eIconBackend : uint8_t { static const std::array, 3 /* backends */> ICONS_ARRAY = { std::array{"[!]", "[i]", "[Hint]", "[Err]", "[?]", "[ok]", ""}, std::array{"", "", "", "", "", "󰸞", ""}, std::array{"", "", "", "", "", ""}}; -static const std::array ICONS_COLORS = {CHyprColor{1.0, 204 / 255.0, 102 / 255.0, 1.0}, +static const std::array ICONS_COLORS = {CHyprColor{255.0 / 255.0, 204 / 255.0, 102 / 255.0, 1.0}, CHyprColor{128 / 255.0, 255 / 255.0, 255 / 255.0, 1.0}, CHyprColor{179 / 255.0, 255 / 255.0, 204 / 255.0, 1.0}, CHyprColor{255 / 255.0, 77 / 255.0, 77 / 255.0, 1.0}, @@ -57,7 +57,7 @@ class CHyprNotificationOverlay { PHLMONITORREF m_lastMonitor; Vector2D m_lastSize = Vector2D(-1, -1); - SP m_texture; + SP m_texture; }; inline UP g_pHyprNotificationOverlay; diff --git a/src/desktop/rule/layerRule/LayerRuleApplicator.cpp b/src/desktop/rule/layerRule/LayerRuleApplicator.cpp index fec3a5b2..4237e4f7 100644 --- a/src/desktop/rule/layerRule/LayerRuleApplicator.cpp +++ b/src/desktop/rule/layerRule/LayerRuleApplicator.cpp @@ -4,7 +4,6 @@ #include "../../view/LayerSurface.hpp" #include "../../types/OverridableVar.hpp" #include "../../../helpers/MiscFunctions.hpp" -#include "../../../event/EventBus.hpp" using namespace Desktop; using namespace Desktop::Rule; @@ -33,38 +32,11 @@ void CLayerRuleApplicator::resetProps(std::underlying_type_t prop UNSET(aboveLock) UNSET(ignoreAlpha) UNSET(animationStyle) - -#undef UNSET - - if (prio == Types::PRIORITY_WINDOW_RULE) - std::erase_if(m_otherProps.props, [props](const auto& el) { return !el.second || el.second->propMask & props; }); } void CLayerRuleApplicator::applyDynamicRule(const SP& rule) { for (const auto& [key, effect] : rule->effects()) { switch (key) { - default: { - if (key <= LAYER_RULE_EFFECT_LAST_STATIC) { - Log::logger->log(Log::TRACE, "CLayerRuleApplicator::applyDynamicRule: Skipping effect {}, not dynamic", sc>(key)); - break; - } - - // custom type, add to our vec - if (!m_otherProps.props.contains(key)) { - m_otherProps.props.emplace(key, - makeUnique(SCustomPropContainer{ - .idx = key, - .propMask = rule->getPropertiesMask(), - .effect = effect, - })); - } else { - auto& e = m_otherProps.props[key]; - e->propMask |= rule->getPropertiesMask(); - e->effect = effect; - } - - break; - } case LAYER_RULE_EFFECT_NONE: { Log::logger->log(Log::ERR, "CLayerRuleApplicator::applyDynamicRule: BUG THIS: LAYER_RULE_EFFECT_NONE??"); break; @@ -153,7 +125,4 @@ void CLayerRuleApplicator::propertiesChanged(std::underlying_type_tm_events.layer.updateRules.emit(m_ls.lock()); } diff --git a/src/desktop/rule/layerRule/LayerRuleApplicator.hpp b/src/desktop/rule/layerRule/LayerRuleApplicator.hpp index 35aa18c5..97f15b04 100644 --- a/src/desktop/rule/layerRule/LayerRuleApplicator.hpp +++ b/src/desktop/rule/layerRule/LayerRuleApplicator.hpp @@ -1,6 +1,5 @@ #pragma once -#include "LayerRuleEffectContainer.hpp" #include "../../DesktopTypes.hpp" #include "../Rule.hpp" #include "../../types/OverridableVar.hpp" @@ -22,17 +21,6 @@ namespace Desktop::Rule { void propertiesChanged(std::underlying_type_t props); void resetProps(std::underlying_type_t props, Types::eOverridePriority prio = Types::PRIORITY_WINDOW_RULE); - struct SCustomPropContainer { - CLayerRuleEffectContainer::storageType idx = LAYER_RULE_EFFECT_NONE; - std::underlying_type_t propMask = RULE_PROP_NONE; - std::string effect; - }; - - // This struct holds props that were dynamically registered. Plugins may read this. - struct { - std::unordered_map> props; - } m_otherProps; - #define COMMA , #define DEFINE_PROP(type, name, def) \ private: \ diff --git a/src/desktop/rule/windowRule/WindowRule.cpp b/src/desktop/rule/windowRule/WindowRule.cpp index 8028e482..b93dddec 100644 --- a/src/desktop/rule/windowRule/WindowRule.cpp +++ b/src/desktop/rule/windowRule/WindowRule.cpp @@ -97,7 +97,7 @@ bool CWindowRule::matches(PHLWINDOW w, bool allowEnvLookup) { return false; break; case RULE_PROP_CONTENT: - if (!engine->match(w->getContentType()) && !engine->match(NContentType::toString(w->getContentType()))) + if (!engine->match(w->getContentType())) return false; break; case RULE_PROP_XDG_TAG: diff --git a/src/desktop/rule/windowRule/WindowRuleApplicator.cpp b/src/desktop/rule/windowRule/WindowRuleApplicator.cpp index 07cb5f64..45a52471 100644 --- a/src/desktop/rule/windowRule/WindowRuleApplicator.cpp +++ b/src/desktop/rule/windowRule/WindowRuleApplicator.cpp @@ -630,8 +630,6 @@ void CWindowRuleApplicator::propertiesChanged(std::underlying_type_tupdateWindowData(); - m_window->updateWindowDecos(); m_window->updateDecorationValues(); if (needsRelayout) diff --git a/src/desktop/view/Group.cpp b/src/desktop/view/Group.cpp index 06884cff..67a89986 100644 --- a/src/desktop/view/Group.cpp +++ b/src/desktop/view/Group.cpp @@ -120,7 +120,7 @@ void CGroup::add(PHLWINDOW w) { m_target->recalc(); } -void CGroup::remove(PHLWINDOW w, Math::eDirection dir) { +void CGroup::remove(PHLWINDOW w) { std::optional idx; for (size_t i = 0; i < m_windows.size(); ++i) { if (m_windows.at(i) == w) { @@ -156,20 +156,8 @@ void CGroup::remove(PHLWINDOW w, Math::eDirection dir) { updateWindowVisibility(); // do this here: otherwise the new current is hidden and workspace rules get wrong data - if (!REMOVING_GROUP) { - std::optional focalPoint; - if (dir != Math::DIRECTION_DEFAULT) { - const auto box = m_target->position(); - switch (dir) { - case Math::DIRECTION_RIGHT: focalPoint = Vector2D(box.x + box.w, box.y + box.h / 2.0); break; - case Math::DIRECTION_LEFT: focalPoint = Vector2D(box.x, box.y + box.h / 2.0); break; - case Math::DIRECTION_DOWN: focalPoint = Vector2D(box.x + box.w / 2.0, box.y + box.h); break; - case Math::DIRECTION_UP: focalPoint = Vector2D(box.x + box.w / 2.0, box.y); break; - default: break; - } - } - w->m_target->assignToSpace(m_target->space(), focalPoint); - } + if (!REMOVING_GROUP) + w->m_target->assignToSpace(m_target->space()); } void CGroup::moveCurrent(bool next) { @@ -329,7 +317,6 @@ void CGroup::swapWithNext() { size_t idx = m_current + 1 >= m_windows.size() ? 0 : m_current + 1; std::iter_swap(m_windows.begin() + m_current, m_windows.begin() + idx); - m_current = idx; updateWindowVisibility(); @@ -342,7 +329,6 @@ void CGroup::swapWithLast() { size_t idx = m_current == 0 ? m_windows.size() - 1 : m_current - 1; std::iter_swap(m_windows.begin() + m_current, m_windows.begin() + idx); - m_current = idx; updateWindowVisibility(); diff --git a/src/desktop/view/Group.hpp b/src/desktop/view/Group.hpp index 36c4baae..8a7bb840 100644 --- a/src/desktop/view/Group.hpp +++ b/src/desktop/view/Group.hpp @@ -1,7 +1,6 @@ #pragma once #include "../DesktopTypes.hpp" -#include "../../helpers/math/Direction.hpp" #include @@ -18,7 +17,7 @@ namespace Desktop::View { bool has(PHLWINDOW w) const; void add(PHLWINDOW w); - void remove(PHLWINDOW w, Math::eDirection dir = Math::DIRECTION_DEFAULT); + void remove(PHLWINDOW w); void moveCurrent(bool next); void setCurrent(size_t idx); void setCurrent(PHLWINDOW w); diff --git a/src/desktop/view/Window.cpp b/src/desktop/view/Window.cpp index abf4da95..137d79bd 100644 --- a/src/desktop/view/Window.cpp +++ b/src/desktop/view/Window.cpp @@ -277,7 +277,7 @@ CBox CWindow::getWindowIdealBoundingBoxIgnoreReserved() { // fucker fucking fuck const auto WORKAREA = m_workspace->m_space->workArea(); - const auto& RESERVED = CReservedArea(PMONITOR->logicalBox(), WORKAREA); + const auto& RESERVED = PMONITOR->m_reservedArea; if (DELTALESSTHAN(POS.x, WORKAREA.x, 1)) { POS.x -= RESERVED.left(); @@ -510,6 +510,12 @@ void CWindow::moveToWorkspace(PHLWORKSPACE pWorkspace) { setAnimationsToMove(); + OLDWORKSPACE->updateWindows(); + OLDWORKSPACE->updateWindowData(); + + pWorkspace->updateWindows(); + pWorkspace->updateWindowData(); + g_pCompositor->updateAllWindowsAnimatedDecorationValues(); if (valid(pWorkspace)) { @@ -801,13 +807,9 @@ void CWindow::updateWindowData() { } void CWindow::updateWindowData(const SWorkspaceRule& workspaceRule) { - if (workspaceRule.noBorder.value_or(false)) - m_ruleApplicator->borderSize().matchOptional(std::optional(0), Desktop::Types::PRIORITY_WORKSPACE_RULE); - else if (workspaceRule.borderSize) - m_ruleApplicator->borderSize().matchOptional(workspaceRule.borderSize, Desktop::Types::PRIORITY_WORKSPACE_RULE); - else - m_ruleApplicator->borderSize().matchOptional(std::nullopt, Desktop::Types::PRIORITY_WORKSPACE_RULE); + m_ruleApplicator->borderSize().matchOptional(workspaceRule.borderSize, Desktop::Types::PRIORITY_WORKSPACE_RULE); m_ruleApplicator->decorate().matchOptional(workspaceRule.decorate, Desktop::Types::PRIORITY_WORKSPACE_RULE); + m_ruleApplicator->borderSize().matchOptional(workspaceRule.noBorder ? std::optional(0) : std::nullopt, Desktop::Types::PRIORITY_WORKSPACE_RULE); m_ruleApplicator->rounding().matchOptional(workspaceRule.noRounding.value_or(false) ? std::optional(0) : std::nullopt, Desktop::Types::PRIORITY_WORKSPACE_RULE); m_ruleApplicator->noShadow().matchOptional(workspaceRule.noShadow, Desktop::Types::PRIORITY_WORKSPACE_RULE); } @@ -1871,12 +1873,11 @@ void CWindow::mapWindow() { if (WORKSPACEARGS.contains("silent")) workspaceSilent = true; - auto joined = WORKSPACEARGS.join(" ", 0, workspaceSilent ? WORKSPACEARGS.size() - 1 : 0); - if (joined.starts_with("empty") && PWORKSPACE->getWindows() == 0) { + if (WORKSPACEARGS.contains("empty") && PWORKSPACE->getWindows() <= 1) { requestedWorkspaceID = PWORKSPACE->m_id; requestedWorkspaceName = PWORKSPACE->m_name; } else { - auto result = getWorkspaceIDNameFromString(joined); + auto result = getWorkspaceIDNameFromString(WORKSPACEARGS.join(" ", 0, workspaceSilent ? WORKSPACEARGS.size() - 1 : 0)); requestedWorkspaceID = result.id; requestedWorkspaceName = result.name; } @@ -1948,13 +1949,11 @@ void CWindow::mapWindow() { g_pEventManager->postEvent(SHyprIPCEvent{"openwindow", std::format("{:x},{},{},{}", m_self.lock(), PWORKSPACE->m_name, m_class, m_title)}); Event::bus()->m_events.window.openEarly.emit(m_self.lock()); - if (*PAUTOGROUP // auto_group enabled - && Desktop::focusState()->window() // focused window exists - && canBeGroupedInto(Desktop::focusState()->window()->m_group) // we can group - && Desktop::focusState()->window()->m_workspace == m_workspace // workspaces match, we're not opening on another ws - && !g_pXWaylandManager->shouldBeFloated(m_self.lock()) && !isX11OverrideRedirect() // not a window that should float or X11 - && !(m_isFloating && !Desktop::focusState()->window()->m_isFloating) // do not auto-group a floated window into a tiled group - && !isModal() // no modal grouping + if (*PAUTOGROUP // auto_group enabled + && Desktop::focusState()->window() // focused window exists + && canBeGroupedInto(Desktop::focusState()->window()->m_group) // we can group + && Desktop::focusState()->window()->m_workspace == m_workspace // workspaces match, we're not opening on another ws + && !isModal() && !(parent() && m_isFloating) && !isX11OverrideRedirect() // not a modal, floating child or X11 OR ) { // add to group if we are focused on one Desktop::focusState()->window()->m_group->add(m_self.lock()); diff --git a/src/event/EventBus.hpp b/src/event/EventBus.hpp index a30288f0..8f59acbd 100644 --- a/src/event/EventBus.hpp +++ b/src/event/EventBus.hpp @@ -41,7 +41,6 @@ namespace Event { Event openEarly; Event destroy; Event close; - Event kill; Event active; Event urgent; Event title; @@ -55,7 +54,6 @@ namespace Event { struct { Event opened; Event closed; - Event updateRules; } layer; struct { @@ -141,4 +139,4 @@ namespace Event { }; UP& bus(); -}; +}; \ No newline at end of file diff --git a/src/helpers/Format.cpp b/src/helpers/Format.cpp index 7660934e..a4efb948 100644 --- a/src/helpers/Format.cpp +++ b/src/helpers/Format.cpp @@ -297,23 +297,31 @@ int NFormatUtils::minStride(const SPixelFormat* const fmt, int32_t width) { return std::ceil((width * fmt->bytesPerBlock) / pixelsPerBlock(fmt)); } +uint32_t NFormatUtils::drmFormatToGL(DRMFormat drm) { + switch (drm) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_XBGR8888: return GL_RGBA; // doesn't matter, opengl is gucci in this case. + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_XBGR2101010: return GL_RGB10_A2; + default: return GL_RGBA; + } + UNREACHABLE(); + return GL_RGBA; +} + +uint32_t NFormatUtils::glFormatToType(uint32_t gl) { + return gl != GL_RGBA ? GL_UNSIGNED_INT_2_10_10_10_REV : GL_UNSIGNED_BYTE; +} + std::string NFormatUtils::drmFormatName(DRMFormat drm) { - auto n = drmGetFormatName(drm); - - if (!n) - return "unknown"; - + auto n = drmGetFormatName(drm); std::string name = n; free(n); // NOLINT(cppcoreguidelines-no-malloc,-warnings-as-errors) return name; } std::string NFormatUtils::drmModifierName(uint64_t mod) { - auto n = drmGetFormatModifierName(mod); - - if (!n) - return "unknown"; - + auto n = drmGetFormatModifierName(mod); std::string name = n; free(n); // NOLINT(cppcoreguidelines-no-malloc,-warnings-as-errors) return name; diff --git a/src/helpers/Format.hpp b/src/helpers/Format.hpp index 02925e22..ce5d8b40 100644 --- a/src/helpers/Format.hpp +++ b/src/helpers/Format.hpp @@ -53,6 +53,8 @@ namespace NFormatUtils { bool isFormatOpaque(DRMFormat drm); int pixelsPerBlock(const SPixelFormat* const fmt); int minStride(const SPixelFormat* const fmt, int32_t width); + uint32_t drmFormatToGL(DRMFormat drm); + uint32_t glFormatToType(uint32_t gl); std::string drmFormatName(DRMFormat drm); std::string drmModifierName(uint64_t mod); DRMFormat alphaFormat(DRMFormat prevFormat); diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index 07156ff1..f287bff3 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -30,7 +30,7 @@ #include "../hyprerror/HyprError.hpp" #include "../layout/LayoutManager.hpp" #include "../i18n/Engine.hpp" -#include "../helpers/cm/ColorManagement.hpp" +#include "../protocols/types/ColorManagement.hpp" #include "sync/SyncTimeline.hpp" #include "time/Time.hpp" #include "../desktop/view/LayerSurface.hpp" @@ -82,10 +82,7 @@ void CMonitor::onConnect(bool noRule) { m_zoomAnimProgress->setValueAndWarp(0.F); m_zoomAnimFrameCounter = 0; - g_pEventLoopManager->doLater([] { - g_pConfigManager->ensurePersistentWorkspacesPresent(); - g_pCompositor->ensureWorkspacesOnAssignedMonitors(); - }); + g_pEventLoopManager->doLater([] { g_pConfigManager->ensurePersistentWorkspacesPresent(); }); m_listeners.frame = m_output->events.frame.listen([this] { if (m_frameScheduler) @@ -293,16 +290,10 @@ void CMonitor::onConnect(bool noRule) { if (!valid(ws)) continue; - const auto CURRENTMON = ws->m_monitor.lock(); - const bool ORPHANED = !CURRENTMON || std::ranges::none_of(g_pCompositor->m_monitors, [&](const auto& mon) { return mon == CURRENTMON; }); - const bool RETURNING = ws->m_lastMonitor == m_name; - const bool RECOVERY = g_pCompositor->m_monitors.size() == 1 && ORPHANED; // temporarily recover orphaned workspaces - - if (RETURNING || RECOVERY) { + if (ws->m_lastMonitor == m_name || g_pCompositor->m_monitors.size() == 1 /* avoid lost workspaces on recover */) { g_pCompositor->moveWorkspaceToMonitor(ws, m_self.lock()); g_pDesktopAnimationManager->startAnimation(ws, CDesktopAnimationManager::ANIMATION_TYPE_IN, true, true); - if (RETURNING) - ws->m_lastMonitor = ""; + ws->m_lastMonitor = ""; } } @@ -438,24 +429,19 @@ void CMonitor::onDisconnect(bool destroy) { m_enabled = false; m_renderingInitPassed = false; - std::vector wspToMove; - for (auto const& w : g_pCompositor->getWorkspaces()) { - if (w->m_monitor == m_self || !w->m_monitor) - wspToMove.emplace_back(w.lock()); - } - - // Preserve ownership across cascaded monitor disconnects. - // The first disconnected monitor "owns" where a workspace should return. - for (auto const& w : wspToMove) { - if (w && w->m_lastMonitor.empty()) - w->m_lastMonitor = m_name; - } - if (BACKUPMON) { // snap cursor g_pCompositor->warpCursorTo(BACKUPMON->m_position + BACKUPMON->m_transformedSize / 2.F, true); + // move workspaces + std::vector wspToMove; + for (auto const& w : g_pCompositor->getWorkspaces()) { + if (w->m_monitor == m_self || !w->m_monitor) + wspToMove.emplace_back(w.lock()); + } + for (auto const& w : wspToMove) { + w->m_lastMonitor = m_name; g_pCompositor->moveWorkspaceToMonitor(w, BACKUPMON); g_pDesktopAnimationManager->startAnimation(w, CDesktopAnimationManager::ANIMATION_TYPE_IN, true, true); } @@ -933,46 +919,29 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { m_supportsWideColor = RULE->supportsHDR; m_supportsHDR = RULE->supportsHDR; - if (RULE->iccFile.empty()) { - // only apply explicit cm settings if we have no icc file - - m_cmType = RULE->cmType; - switch (m_cmType) { - case NCMType::CM_AUTO: m_cmType = m_enabled10bit && supportsWideColor() ? NCMType::CM_WIDE : NCMType::CM_SRGB; break; - case NCMType::CM_EDID: m_cmType = m_output->parsedEDID.chromaticityCoords.has_value() ? NCMType::CM_EDID : NCMType::CM_SRGB; break; - case NCMType::CM_HDR: - case NCMType::CM_HDR_EDID: m_cmType = supportsHDR() ? m_cmType : NCMType::CM_SRGB; break; - default: break; - } - - m_sdrEotf = RULE->sdrEotf; - - m_sdrMinLuminance = RULE->sdrMinLuminance; - m_sdrMaxLuminance = RULE->sdrMaxLuminance; - - m_minLuminance = RULE->minLuminance; - m_maxLuminance = RULE->maxLuminance; - m_maxAvgLuminance = RULE->maxAvgLuminance; - - applyCMType(m_cmType, m_sdrEotf); - - m_sdrSaturation = RULE->sdrSaturation; - m_sdrBrightness = RULE->sdrBrightness; - } else { - auto image = NColorManagement::SImageDescription::fromICC(RULE->iccFile); - if (!image) { - Log::logger->log(Log::ERR, "icc for {} ({}) failed: {}", m_name, RULE->iccFile, image.error()); - g_pConfigManager->addParseError(std::format("failed to apply icc {} to {}: {}", RULE->iccFile, m_name, image.error())); - } else { - m_imageDescription = CImageDescription::from(*image); - if (!m_imageDescription) { - Log::logger->log(Log::ERR, "icc for {} ({}) failed 2: {}", m_name, RULE->iccFile, image.error()); - g_pConfigManager->addParseError(std::format("failed to apply icc {} to {}: {}", RULE->iccFile, m_name, image.error())); - m_imageDescription = CImageDescription::from(SImageDescription{}); - } - } + m_cmType = RULE->cmType; + switch (m_cmType) { + case NCMType::CM_AUTO: m_cmType = m_enabled10bit && supportsWideColor() ? NCMType::CM_WIDE : NCMType::CM_SRGB; break; + case NCMType::CM_EDID: m_cmType = m_output->parsedEDID.chromaticityCoords.has_value() ? NCMType::CM_EDID : NCMType::CM_SRGB; break; + case NCMType::CM_HDR: + case NCMType::CM_HDR_EDID: m_cmType = supportsHDR() ? m_cmType : NCMType::CM_SRGB; break; + default: break; } + m_sdrEotf = RULE->sdrEotf; + + m_sdrMinLuminance = RULE->sdrMinLuminance; + m_sdrMaxLuminance = RULE->sdrMaxLuminance; + + m_minLuminance = RULE->minLuminance; + m_maxLuminance = RULE->maxLuminance; + m_maxAvgLuminance = RULE->maxAvgLuminance; + + applyCMType(m_cmType, m_sdrEotf); + + m_sdrSaturation = RULE->sdrSaturation; + m_sdrBrightness = RULE->sdrBrightness; + Vector2D logicalSize = m_pixelSize / m_scale; if (!*PDISABLESCALECHECKS && (logicalSize.x != std::round(logicalSize.x) || logicalSize.y != std::round(logicalSize.y))) { // invalid scale, will produce fractional pixels. @@ -1054,8 +1023,6 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { m_damage.setSize(m_transformedSize); - updateVCGTRamps(); - // Set scale for all surfaces on this monitor, needed for some clients // but not on unsafe state to avoid crashes if (!g_pCompositor->m_unsafeState) { @@ -1362,7 +1329,7 @@ void CMonitor::changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal, bo // move pinned windows for (auto const& w : g_pCompositor->m_windows) { if (w->m_workspace == POLDWORKSPACE && w->m_pinned) - w->layoutTarget()->assignToSpace(pWorkspace->m_space); + w->moveToWorkspace(pWorkspace); } if (!noFocus && !Desktop::focusState()->monitor()->m_activeSpecialWorkspace && @@ -1481,7 +1448,6 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) { if (const auto PMWSOWNER = pWorkspace->m_monitor.lock(); PMWSOWNER && PMWSOWNER->m_activeSpecialWorkspace == pWorkspace) { PMWSOWNER->m_activeSpecialWorkspace.reset(); g_layoutManager->recalculateMonitor(PMWSOWNER); - g_pHyprRenderer->damageMonitor(PMWSOWNER); g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", "," + PMWSOWNER->m_name}); g_pEventManager->postEvent(SHyprIPCEvent{"activespecialv2", ",," + PMWSOWNER->m_name}); @@ -1579,20 +1545,10 @@ Vector2D CMonitor::middle() { return m_position + m_size / 2.f; } -const Mat3x3& CMonitor::getTransformMatrix() { - return m_projMatrix; -} - -const Mat3x3& CMonitor::getScaleMatrix() { - return m_projOutputMatrix; -} - void CMonitor::updateMatrix() { m_projMatrix = Mat3x3::identity(); if (m_transform != WL_OUTPUT_TRANSFORM_NORMAL) m_projMatrix.translate(m_pixelSize / 2.0).transform(Math::wlTransformToHyprutils(m_transform)).translate(-m_transformedSize / 2.0); - - m_projOutputMatrix = Mat3x3::outputProjection(m_pixelSize, HYPRUTILS_TRANSFORM_NORMAL); } WORKSPACEID CMonitor::activeWorkspaceID() { @@ -1879,7 +1835,7 @@ uint16_t CMonitor::isDSBlocked(bool full) { // we can't scanout shm buffers. const auto params = PSURFACE->m_current.buffer->dmabuf(); - if (!params.success || !PSURFACE->m_current.texture->isDMA() /* dmabuf */) { + if (!params.success || !PSURFACE->m_current.texture->m_eglImage /* dmabuf */) { reasons |= DS_BLOCK_DMA; if (!full) return reasons; @@ -2216,8 +2172,8 @@ bool CMonitor::canNoShaderCM() { const auto SRC_DESC_VALUE = SRC_DESC.value()->value(); - if (m_imageDescription->value().icc.present) - return false; + if (SRC_DESC_VALUE.icc.fd >= 0 || m_imageDescription->value().icc.fd >= 0) + return false; // no ICC support const auto sdrEOTF = NTransferFunction::fromConfig(); // only primaries differ @@ -2236,71 +2192,6 @@ bool CMonitor::doesNoShaderCM() { return m_noShaderCTM; } -static std::vector resampleInterleavedToKms(const SVCGTTable16& t, size_t gammaSize) { - std::vector out; - out.resize(gammaSize * 3); - - // - auto sample = [&](int c, float x) -> uint16_t { - const float maxX = t.entries - 1; - x = std::clamp(x, 0.F, maxX); - - const size_t i0 = (size_t)std::floor(x); - const size_t i1 = std::min(i0 + 1, (size_t)t.entries - 1); - const float f = x - sc(i0); - - const float v0 = sc(t.ch[c][i0]); - const float v1 = sc(t.ch[c][i1]); - const float v = v0 + ((v1 - v0) * f); - - int64_t vi = std::round(v); - vi = std::clamp(vi, sc(0), sc(65535)); - return sc(vi); - }; - - for (size_t i = 0; i < gammaSize; ++i) { - float x = sc(i) * sc(t.entries - 1) / sc(gammaSize - 1); - - const uint16_t r = sample(0, x); - const uint16_t g = sample(1, x); - const uint16_t b = sample(2, x); - - out[i * 3 + 0] = r; - out[i * 3 + 1] = g; - out[i * 3 + 2] = b; - } - - return out; -} - -void CMonitor::updateVCGTRamps() { - auto gammaSize = m_output->getGammaSize(); - - if (gammaSize <= 10) { - Log::logger->log(Log::DEBUG, "CMonitor::updateVCGTRamps: skipping, no gamma ramp for output"); - return; - } - - if (!m_imageDescription->value().icc.vcgt) { - if (m_vcgtRampsSet) - m_output->state->setGammaLut({}); - - m_vcgtRampsSet = false; - return; - } - - // build table - auto table = resampleInterleavedToKms(*m_imageDescription->value().icc.vcgt, gammaSize); - - m_output->state->setGammaLut(table); - - m_vcgtRampsSet = true; -} - -bool CMonitor::gammaRampsInUse() { - return m_vcgtRampsSet; -} - CMonitorState::CMonitorState(CMonitor* owner) : m_owner(owner) { ; } diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index 7467467a..72e0cf66 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -16,7 +16,7 @@ #include "math/Math.hpp" #include "../desktop/reserved/ReservedArea.hpp" #include -#include "cm/ColorManagement.hpp" +#include "../protocols/types/ColorManagement.hpp" #include "signal/Signal.hpp" #include "DamageRing.hpp" #include @@ -56,7 +56,6 @@ struct SMonitorRule { float sdrSaturation = 1.0f; // SDR -> HDR float sdrBrightness = 1.0f; // SDR -> HDR Desktop::CReservedArea reservedArea; - std::string iccFile; int supportsWideColor = 0; // 0 - auto, 1 - force enable, -1 - force disable int supportsHDR = 0; // 0 - auto, 1 - force enable, -1 - force disable @@ -127,7 +126,7 @@ class CMonitor { bool m_scheduledRecalc = false; wl_output_transform m_transform = WL_OUTPUT_TRANSFORM_NORMAL; float m_xwaylandScale = 1.f; - + Mat3x3 m_projMatrix; std::optional m_forceSize; SP m_currentMode; SP m_cursorSwapchain; @@ -303,6 +302,7 @@ class CMonitor { void setSpecialWorkspace(const WORKSPACEID& id); void moveTo(const Vector2D& pos); Vector2D middle(); + void updateMatrix(); WORKSPACEID activeWorkspaceID(); WORKSPACEID activeSpecialWorkspaceID(); CBox logicalBox(); @@ -332,11 +332,6 @@ class CMonitor { bool wantsHDR(); bool inHDR(); - bool gammaRampsInUse(); - - // - const Mat3x3& getTransformMatrix(); - const Mat3x3& getScaleMatrix(); /// Has an active workspace with a real fullscreen window (includes special workspace) bool inFullscreenMode(); @@ -358,8 +353,8 @@ class CMonitor { PHLWINDOWREF m_previousFSWindow; bool m_needsHDRupdate = false; - NColorManagement::PImageDescription m_imageDescription = NColorManagement::CImageDescription::from(NColorManagement::SImageDescription{}); - bool m_noShaderCTM = false; // sets drm CTM, restore needed + NColorManagement::PImageDescription m_imageDescription; + bool m_noShaderCTM = false; // sets drm CTM, restore needed // For the list lookup @@ -367,19 +362,12 @@ class CMonitor { return m_position == rhs.m_position && m_size == rhs.m_size && m_name == rhs.m_name; } - Mat3x3 m_projMatrix; - private: - void updateMatrix(); - Mat3x3 m_projOutputMatrix; - void setupDefaultWS(const SMonitorRule&); WORKSPACEID findAvailableDefaultWS(); void commitDPMSState(bool state); - void updateVCGTRamps(); bool m_doneScheduled = false; - bool m_vcgtRampsSet = false; std::stack m_prevWorkSpaces; struct { diff --git a/src/helpers/TransferFunction.cpp b/src/helpers/TransferFunction.cpp index 074f4b19..935f77fe 100644 --- a/src/helpers/TransferFunction.cpp +++ b/src/helpers/TransferFunction.cpp @@ -26,10 +26,7 @@ std::string NTransferFunction::toString(eTF tf) { return ""; } -eTF NTransferFunction::fromConfig(bool useICC) { - if (useICC) - return TF_SRGB; - +eTF NTransferFunction::fromConfig() { static auto PSDREOTF = CConfigValue("render:cm_sdr_eotf"); static auto sdrEOTF = NTransferFunction::fromString(*PSDREOTF); static auto P = Event::bus()->m_events.config.reloaded.listen([]() { sdrEOTF = NTransferFunction::fromString(*PSDREOTF); }); diff --git a/src/helpers/TransferFunction.hpp b/src/helpers/TransferFunction.hpp index ae575158..cbf35f37 100644 --- a/src/helpers/TransferFunction.hpp +++ b/src/helpers/TransferFunction.hpp @@ -15,5 +15,5 @@ namespace NTransferFunction { eTF fromString(const std::string tfName); std::string toString(eTF tf); - eTF fromConfig(bool useICC = false); + eTF fromConfig(); } diff --git a/src/helpers/cm/ColorManagement.cpp b/src/helpers/cm/ColorManagement.cpp deleted file mode 100644 index bac9f25a..00000000 --- a/src/helpers/cm/ColorManagement.cpp +++ /dev/null @@ -1,193 +0,0 @@ -#include "ColorManagement.hpp" -#include "../../macros.hpp" -#include -#include -#include - -using namespace NColorManagement; - -namespace NColorManagement { - // expected to be small - static std::vector> knownPrimaries; - static std::vector> knownDescriptions; - static std::map, Hyprgraphics::CMatrix3> primariesConversion; -} - -const SPCPRimaries& NColorManagement::getPrimaries(ePrimaries name) { - switch (name) { - case CM_PRIMARIES_SRGB: return NColorPrimaries::BT709; - case CM_PRIMARIES_BT2020: return NColorPrimaries::BT2020; - case CM_PRIMARIES_PAL_M: return NColorPrimaries::PAL_M; - case CM_PRIMARIES_PAL: return NColorPrimaries::PAL; - case CM_PRIMARIES_NTSC: return NColorPrimaries::NTSC; - case CM_PRIMARIES_GENERIC_FILM: return NColorPrimaries::GENERIC_FILM; - case CM_PRIMARIES_CIE1931_XYZ: return NColorPrimaries::CIE1931_XYZ; - case CM_PRIMARIES_DCI_P3: return NColorPrimaries::DCI_P3; - case CM_PRIMARIES_DISPLAY_P3: return NColorPrimaries::DISPLAY_P3; - case CM_PRIMARIES_ADOBE_RGB: return NColorPrimaries::ADOBE_RGB; - default: return NColorPrimaries::DEFAULT_PRIMARIES; - } -} - -CPrimaries::CPrimaries(const SPCPRimaries& primaries, const uint32_t primariesId) : m_id(primariesId), m_primaries(primaries) { - m_primaries2XYZ = m_primaries.toXYZ(); -} - -WP CPrimaries::from(const SPCPRimaries& primaries) { - for (const auto& known : knownPrimaries) { - if (known->value() == primaries) - return known; - } - - knownPrimaries.emplace_back(CUniquePointer(new CPrimaries(primaries, knownPrimaries.size() + 1))); - return knownPrimaries.back(); -} - -WP CPrimaries::from(const ePrimaries name) { - return from(getPrimaries(name)); -} - -WP CPrimaries::from(const uint32_t primariesId) { - ASSERT(primariesId <= knownPrimaries.size()); - return knownPrimaries[primariesId - 1]; -} - -const SPCPRimaries& CPrimaries::value() const { - return m_primaries; -} - -uint CPrimaries::id() const { - return m_id; -} - -const Hyprgraphics::CMatrix3& CPrimaries::toXYZ() const { - return m_primaries2XYZ; -} - -const Hyprgraphics::CMatrix3& CPrimaries::convertMatrix(const WP dst) const { - const auto cacheKey = std::make_pair(m_id, dst->m_id); - if (!primariesConversion.contains(cacheKey)) - primariesConversion.insert(std::make_pair(cacheKey, m_primaries.convertMatrix(dst->m_primaries))); - - return primariesConversion[cacheKey]; -} - -CImageDescription::CImageDescription(const SImageDescription& imageDescription, const uint32_t imageDescriptionId) : - m_id(imageDescriptionId), m_imageDescription(imageDescription) { - m_primariesId = CPrimaries::from(m_imageDescription.getPrimaries())->id(); -} - -PImageDescription CImageDescription::from(const SImageDescription& imageDescription) { - for (const auto& known : knownDescriptions) { - if (known->value() == imageDescription) - return known; - } - - knownDescriptions.emplace_back(UP(new CImageDescription(imageDescription, knownDescriptions.size() + 1))); - return knownDescriptions.back(); -} - -PImageDescription CImageDescription::from(const uint32_t imageDescriptionId) { - ASSERT(imageDescriptionId <= knownDescriptions.size()); - return knownDescriptions[imageDescriptionId - 1]; -} - -PImageDescription CImageDescription::with(const SImageDescription::SPCLuminances& luminances) const { - auto desc = m_imageDescription; - desc.luminances = luminances; - return CImageDescription::from(desc); -} - -const SImageDescription& CImageDescription::value() const { - return m_imageDescription; -} - -uint CImageDescription::id() const { - return m_id; -} - -WP CImageDescription::getPrimaries() const { - return CPrimaries::from(m_primariesId); -} - -static Mat3x3 diag3(const std::array& s) { - return Mat3x3{std::array{s[0], 0, 0, 0, s[1], 0, 0, 0, s[2]}}; -} - -static std::optional invertMat3(const Mat3x3& m) { - const auto ARR = m.getMatrix(); - const double a = ARR[0], b = ARR[1], c = ARR[2]; - const double d = ARR[3], e = ARR[4], f = ARR[5]; - const double g = ARR[6], h = ARR[7], i = ARR[8]; - - const double A = (e * i - f * h); - const double B = -(d * i - f * g); - const double C = (d * h - e * g); - const double D = -(b * i - c * h); - const double E = (a * i - c * g); - const double F = -(a * h - b * g); - const double G = (b * f - c * e); - const double H = -(a * f - c * d); - const double I = (a * e - b * d); - - const double det = a * A + b * B + c * C; - if (std::abs(det) < 1e-18) - return std::nullopt; - - const double invDet = 1.0 / det; - Mat3x3 inv{std::array{ - A * invDet, - D * invDet, - G * invDet, // - B * invDet, - E * invDet, - H * invDet, // - C * invDet, - F * invDet, - I * invDet, // - }}; - return inv; -} - -static std::array matByVec(const Mat3x3& M, const std::array& v) { - const auto ARR = M.getMatrix(); - return {ARR[0] * v[0] + ARR[1] * v[1] + ARR[2] * v[2], ARR[3] * v[0] + ARR[4] * v[1] + ARR[5] * v[2], ARR[6] * v[0] + ARR[7] * v[1] + ARR[8] * v[2]}; -} - -std::optional NColorManagement::rgbToXYZFromPrimaries(SPCPRimaries pr) { - const auto R = Hyprgraphics::xy2xyz(pr.red); - const auto G = Hyprgraphics::xy2xyz(pr.green); - const auto B = Hyprgraphics::xy2xyz(pr.blue); - const auto W = Hyprgraphics::xy2xyz(pr.white); - - // P has columns R,G,B - Mat3x3 P{std::array{R.x, G.x, B.x, R.y, G.y, B.y, R.z, G.z, B.z}}; - - auto invP = invertMat3(P); - if (!invP) - return std::nullopt; - - const auto S = matByVec(*invP, {W.x, W.y, W.z}); - - P.multiply(diag3(S)); // RGB->XYZ - - return P; -} - -Mat3x3 NColorManagement::adaptBradford(Hyprgraphics::CColor::xy srcW, Hyprgraphics::CColor::xy dstW) { - static const Mat3x3 Bradford{std::array{0.8951f, 0.2664f, -0.1614f, -0.7502f, 1.7135f, 0.0367f, 0.0389f, -0.0685f, 1.0296f}}; - static const Mat3x3 BradfordInv = invertMat3(Bradford).value(); - - const auto srcXYZ = Hyprgraphics::xy2xyz(srcW); - const auto dstXYZ = Hyprgraphics::xy2xyz(dstW); - - const auto srcLMS = matByVec(Bradford, {srcXYZ.x, srcXYZ.y, srcXYZ.z}); - const auto dstLMS = matByVec(Bradford, {dstXYZ.x, dstXYZ.y, dstXYZ.z}); - - const std::array scale{dstLMS[0] / srcLMS[0], dstLMS[1] / srcLMS[1], dstLMS[2] / srcLMS[2]}; - - Mat3x3 result = BradfordInv; - result.multiply(diag3(scale)).multiply(Bradford); - - return result; -} \ No newline at end of file diff --git a/src/helpers/cm/ICC.cpp b/src/helpers/cm/ICC.cpp deleted file mode 100644 index 00140c62..00000000 --- a/src/helpers/cm/ICC.cpp +++ /dev/null @@ -1,278 +0,0 @@ -#include "ColorManagement.hpp" -#include "../math/Math.hpp" -#include -#include - -#include "../../debug/log/Logger.hpp" -#include "../../render/Texture.hpp" -#include "../../render/Renderer.hpp" - -#include -using namespace Hyprutils::Utils; - -#include - -using namespace NColorManagement; - -static std::vector readBinary(const std::filesystem::path& file) { - std::ifstream ifs(file, std::ios::binary); - if (!ifs.good()) - return {}; - - ifs.seekg(0, std::ios::end); - size_t len = ifs.tellg(); - ifs.seekg(0, std::ios::beg); - - if (len <= 0) - return {}; - - std::vector buf; - buf.resize(len); - ifs.read(reinterpret_cast(buf.data()), len); - - return buf; -} - -static uint16_t bigEndianU16(const uint8_t* p) { - return (uint16_t)((uint16_t)p[0] << 8 | (uint16_t)p[1]); -} - -static uint32_t bigEndianU32(const uint8_t* p) { - return (uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 | (uint32_t)p[2] << 8 | (uint32_t)p[3]; -} - -static constexpr cmsTagSignature makeSig(char a, char b, char c, char d) { - return sc(sc(a) << 24 | sc(b) << 16 | sc(c) << 8 | sc(d)); -} - -static constexpr cmsTagSignature VCGT_SIG = makeSig('v', 'c', 'g', 't'); - -// - -static std::expected, std::string> readVCGT16(cmsHPROFILE prof) { - if (!cmsIsTag(prof, VCGT_SIG)) - return std::nullopt; - - cmsUInt32Number n = cmsReadRawTag(prof, VCGT_SIG, nullptr, 0); - if (n < 8 + 4 + 2 + 2 + 2 + 2) // header + type + table header - return std::unexpected("Malformed vcgt tag"); - - std::vector raw(n); - if (cmsReadRawTag(prof, VCGT_SIG, raw.data(), n) != n) - return std::unexpected("Malformed vcgt tag"); - - // raw layout: - // 0 ... 3: 'vcgt' - // 4 ... 7: reserved - // 8 ... 11: gammaType (0 = table) - uint32_t gammaType = bigEndianU32(raw.data() + 8); - if (gammaType != 0) - return std::unexpected("VCGT formula type is not supported by Hyprland"); - - SVCGTTable16 table; - table.channels = bigEndianU16(raw.data() + 12); - table.entries = bigEndianU16(raw.data() + 14); - table.entrySize = bigEndianU16(raw.data() + 16); - // raw+18: reserved u16 - - Log::logger->log(Log::DEBUG, "readVCGT16: table has {} channels, {} entries, and entry size of {}", table.channels, table.entries, table.entrySize); - - if (table.channels != 3 || table.entrySize != 2 || table.entries == 0) - return std::unexpected("invalid vcgt table size"); - - size_t tableBytes = (size_t)table.channels * (size_t)table.entries * (size_t)table.entrySize; - - // VCGT is a piece of shit and some absolute fucking mongoloid idiots - // decided it'd be great to have both 18 and 20 - // FUCK YOU - size_t tableOff = 20; - - auto readTable = [&] -> void { - for (int c = 0; c < 3; ++c) { - table.ch[c].resize(table.entries); - for (uint16_t i = 0; i < table.entries; ++i) { - const uint8_t* p = raw.data() + tableOff + static_cast((c * table.entries + i) * 2); - table.ch[c][i] = bigEndianU16(p); // 0 ... 65535 - } - } - }; - - if (raw.size() < tableOff + tableBytes) { - tableOff = 18; - - if (raw.size() < tableOff + tableBytes) { - Log::logger->log(Log::ERR, "readVCGT16: table is too short, tag is invalid"); - return std::unexpected("table is too short"); - } - - Log::logger->log(Log::DEBUG, "readVCGT16: table is too short, but off = 18 fits. Attempting offset = 18"); - - readTable(); - } else { - readTable(); - - // if the table's last entry is suspiciously low, we more than likely read an 18 as a 20. - if (table.ch[0][table.entries - 1] < 30000) { - Log::logger->log(Log::DEBUG, "readVCGT16: table is likely offset 18 not 20, re-reading"); - - tableOff = 18; - - readTable(); - } - } - - if (table.ch[0][table.entries - 1] < 30000) { - Log::logger->log(Log::ERR, "readVCGT16: table is malformed, last value of a gamma ramp can't be {}", table.ch[0][table.entries - 1]); - return std::unexpected("invalid table values"); - } - - Log::logger->log(Log::DEBUG, "readVCGT16: red channel: [{}, {}, ... {}, {}]", table.ch[0][0], table.ch[0][1], table.ch[0][table.entries - 2], table.ch[0][table.entries - 1]); - Log::logger->log(Log::DEBUG, "readVCGT16: green channel: [{}, {}, ... {}, {}]", table.ch[1][0], table.ch[1][1], table.ch[1][table.entries - 2], table.ch[1][table.entries - 1]); - Log::logger->log(Log::DEBUG, "readVCGT16: blue channel: [{}, {}, ... {}, {}]", table.ch[2][0], table.ch[2][1], table.ch[2][table.entries - 2], table.ch[2][table.entries - 1]); - - return table; -} - -struct CmsProfileDeleter { - void operator()(cmsHPROFILE p) const { - if (p) - cmsCloseProfile(p); - } -}; -struct CmsTransformDeleter { - void operator()(cmsHTRANSFORM t) const { - if (t) - cmsDeleteTransform(t); - } -}; - -using UniqueProfile = std::unique_ptr, CmsProfileDeleter>; -using UniqueTransform = std::unique_ptr, CmsTransformDeleter>; - -static UniqueProfile createLinearSRGBProfile() { - cmsCIExyYTRIPLE prim{}; - // sRGB / Rec.709 primaries - prim.Red.x = 0.6400; - prim.Red.y = 0.3300; - prim.Red.Y = 1.0; - prim.Green.x = 0.3000; - prim.Green.y = 0.6000; - prim.Green.Y = 1.0; - prim.Blue.x = 0.1500; - prim.Blue.y = 0.0600; - prim.Blue.Y = 1.0; - - cmsCIExyY wp{}; - wp.x = 0.3127; - wp.y = 0.3290; - wp.Y = 1.0; // D65 - - cmsToneCurve* lin = cmsBuildGamma(nullptr, 1.0); - cmsToneCurve* curves[3] = {lin, lin, lin}; - - cmsHPROFILE p = cmsCreateRGBProfile(&wp, &prim, curves); - - cmsFreeToneCurve(lin); - return UniqueProfile{p}; -} - -static std::expected buildIcc3DLut(cmsHPROFILE profile, SImageDescription& image) { - UniqueProfile src = createLinearSRGBProfile(); - if (!src) - return std::unexpected("Failed to create linear sRGB profile"); - - // Rendering intent: RELATIVE_COLORIMETRIC is common for displays; add BPC to be safe. - const int intent = INTENT_RELATIVE_COLORIMETRIC; - const cmsUInt32Number flags = cmsFLAGS_BLACKPOINTCOMPENSATION | cmsFLAGS_HIGHRESPRECALC; // good quality precalc in LCMS - - // float->float transform (linear input, encoded output in dst device space) - UniqueTransform xform{cmsCreateTransform(src.get(), TYPE_RGB_FLT, profile, TYPE_RGB_FLT, intent, flags)}; - if (!xform) - return std::unexpected("Failed to create ICC transform"); - - Log::logger->log(Log::DEBUG, "Building a {}³ 3D LUT", image.icc.lutSize); - - image.icc.present = true; - image.icc.lutDataPacked.resize(image.icc.lutSize * image.icc.lutSize * image.icc.lutSize * 3); - - auto idx = [&image](int r, int g, int b) -> size_t { - // - return ((size_t)b * image.icc.lutSize * image.icc.lutSize + (size_t)g * image.icc.lutSize + (size_t)r) * 3; - }; - - for (size_t bz = 0; bz < image.icc.lutSize; ++bz) { - for (size_t gy = 0; gy < image.icc.lutSize; ++gy) { - for (size_t rx = 0; rx < image.icc.lutSize; ++rx) { - float in[3] = { - rx / float(image.icc.lutSize - 1), - gy / float(image.icc.lutSize - 1), - bz / float(image.icc.lutSize - 1), - }; - float outRGB[3]; - cmsDoTransform(xform.get(), in, outRGB, 1); - - outRGB[0] = std::clamp(outRGB[0], 0.F, 1.F); - outRGB[1] = std::clamp(outRGB[1], 0.F, 1.F); - outRGB[2] = std::clamp(outRGB[2], 0.F, 1.F); - - const size_t o = idx(rx, gy, bz); - image.icc.lutDataPacked[o + 0] = outRGB[0]; - image.icc.lutDataPacked[o + 1] = outRGB[1]; - image.icc.lutDataPacked[o + 2] = outRGB[2]; - } - } - } - - Log::logger->log(Log::DEBUG, "3D LUT constructed, size {}", image.icc.lutDataPacked.size()); - - // upload - image.icc.lutTexture = g_pHyprRenderer->createTexture(image.icc.lutDataPacked, image.icc.lutSize); - - return {}; -} - -std::expected SImageDescription::fromICC(const std::filesystem::path& file) { - static auto PVCGTENABLED = CConfigValue("render:icc_vcgt_enabled"); - - std::error_code ec; - if (!std::filesystem::exists(file, ec) || ec) - return std::unexpected("Invalid file"); - - SImageDescription image; - image.rawICC = readBinary(file); - - if (image.rawICC.empty()) - return std::unexpected("Failed to read file"); - - cmsHPROFILE prof = cmsOpenProfileFromFile(file.string().c_str(), "r"); - if (!prof) - return std::unexpected("CMS failed to open icc file"); - - CScopeGuard x([&prof] { cmsCloseProfile(prof); }); - - // only handle RGB (typical display profiles) - if (cmsGetColorSpace(prof) != cmsSigRgbData) - return std::unexpected("Only RGB display profiles are supported"); - - Log::logger->log(Log::DEBUG, "============= Begin ICC load ============="); - Log::logger->log(Log::DEBUG, "ICC size: {} bytes", image.rawICC.size()); - - if (const auto RET = buildIcc3DLut(prof, image); !RET) - return std::unexpected(RET.error()); - - if (*PVCGTENABLED) { - auto vcgtRes = readVCGT16(prof); - if (!vcgtRes) - return std::unexpected(vcgtRes.error()); - - image.icc.vcgt = *vcgtRes; - - if (!*vcgtRes) - Log::logger->log(Log::DEBUG, "ICC profile has no VCGT data"); - } else - Log::logger->log(Log::DEBUG, "Skipping VCGT load, disabled by config"); - - Log::logger->log(Log::DEBUG, "============= End ICC load ============="); - - return image; -} \ No newline at end of file diff --git a/src/hyprerror/HyprError.cpp b/src/hyprerror/HyprError.cpp index 60bf0a78..360bdfdc 100644 --- a/src/hyprerror/HyprError.cpp +++ b/src/hyprerror/HyprError.cpp @@ -30,6 +30,8 @@ CHyprError::CHyprError() { if (m_fadeOpacity->isBeingAnimated() || m_monitorChanged) g_pHyprRenderer->damageBox(m_damageBox); }); + + m_texture = makeShared(); } void CHyprError::queueCreate(std::string message, const CHyprColor& color) { @@ -38,8 +40,8 @@ void CHyprError::queueCreate(std::string message, const CHyprColor& color) { } void CHyprError::createQueued() { - if (m_isCreated && m_texture) - m_texture.reset(); + if (m_isCreated) + m_texture->destroyTexture(); m_fadeOpacity->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeIn")); @@ -143,13 +145,12 @@ void CHyprError::createQueued() { // copy the data to an OpenGL texture we have const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); - auto tex = texture(); - tex->allocate(PMONITOR->m_pixelSize); - tex->bind(); - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); + m_texture->allocate(); + m_texture->bind(); + m_texture->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST); + m_texture->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST); + m_texture->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); + m_texture->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); @@ -186,8 +187,7 @@ void CHyprError::draw() { if (!m_fadeOpacity->isBeingAnimated()) { if (m_fadeOpacity->value() == 0.f) { m_queuedDestroy = false; - if (m_texture) - m_texture.reset(); + m_texture->destroyTexture(); m_isCreated = false; m_queued = ""; @@ -218,7 +218,7 @@ void CHyprError::draw() { m_monitorChanged = false; CTexPassElement::SRenderData data; - data.tex = texture(); + data.tex = m_texture; data.box = monbox; data.a = m_fadeOpacity->value(); @@ -239,9 +239,3 @@ bool CHyprError::active() { float CHyprError::height() { return m_lastHeight; } - -SP CHyprError::texture() { - if (!m_texture) - m_texture = g_pHyprRenderer->createTexture(); - return m_texture; -} \ No newline at end of file diff --git a/src/hyprerror/HyprError.hpp b/src/hyprerror/HyprError.hpp index 48b9e805..f4bc43d8 100644 --- a/src/hyprerror/HyprError.hpp +++ b/src/hyprerror/HyprError.hpp @@ -18,16 +18,13 @@ class CHyprError { bool active(); float height(); // logical - // - SP texture(); - private: void createQueued(); std::string m_queued = ""; CHyprColor m_queuedColor; bool m_queuedDestroy = false; bool m_isCreated = false; - SP m_texture; + SP m_texture; PHLANIMVAR m_fadeOpacity; CBox m_damageBox = {0, 0, 0, 0}; float m_lastHeight = 0.F; diff --git a/src/i18n/Engine.cpp b/src/i18n/Engine.cpp index c68400eb..7b77b856 100644 --- a/src/i18n/Engine.cpp +++ b/src/i18n/Engine.cpp @@ -1605,7 +1605,6 @@ I18n::CI18nEngine::CI18nEngine() { huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Ứng dụng {app} đang yêu cầu một quyền không xác định."); huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Ứng dụng {app} đang cố gắng ghi hình màn hình của bạn.\n\nBạn muốn cho phép không?"); - huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_REQUEST_CURSOR_POS, "Ứng dụng {app} đang cố gắng đọc vị trí chuột của bạn.\n\nBạn muốn cho phép không?"); huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Ứng dụng {app} đang cố gắng tải plugin: {plugin}.\n\nBạn muốn cho phép không?"); huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Phát hiện bàn phím mới: {keyboard}.\n\nBạn muốn cho phép bàn phím này hoạt động không?"); huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(không xác định)"); diff --git a/src/layout/LayoutManager.cpp b/src/layout/LayoutManager.cpp index 4a93809c..bcbf8438 100644 --- a/src/layout/LayoutManager.cpp +++ b/src/layout/LayoutManager.cpp @@ -11,7 +11,13 @@ using namespace Layout; -CLayoutManager::CLayoutManager() = default; +CLayoutManager::CLayoutManager() { + static auto P = Event::bus()->m_events.monitor.layoutChanged.listen([] { + for (const auto& ws : g_pCompositor->getWorkspaces()) { + ws->m_space->recheckWorkArea(); + } + }); +} void CLayoutManager::newTarget(SP target, SP space) { // on a new target: remember desired pos for float, if available @@ -61,13 +67,6 @@ void CLayoutManager::resizeTarget(const Vector2D& Δ, SP target, eRectC target->space()->resizeTarget(Δ, target, corner); } -void CLayoutManager::setTargetGeom(const CBox& box, SP target) { - if (!target->floating()) - return; - - target->space()->setTargetGeom(box, target); -} - std::expected CLayoutManager::layoutMsg(const std::string_view& sv) { const auto MONITOR = Desktop::focusState()->monitor(); diff --git a/src/layout/LayoutManager.hpp b/src/layout/LayoutManager.hpp index e99911d5..638c9f4c 100644 --- a/src/layout/LayoutManager.hpp +++ b/src/layout/LayoutManager.hpp @@ -53,7 +53,6 @@ namespace Layout { void moveMouse(const Vector2D& mousePos); void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); void moveTarget(const Vector2D& Δ, SP target); - void setTargetGeom(const CBox& box, SP target); // floats only void endDragTarget(); std::expected layoutMsg(const std::string_view& sv); diff --git a/src/layout/algorithm/Algorithm.cpp b/src/layout/algorithm/Algorithm.cpp index cfb5b7e3..cd8cfac4 100644 --- a/src/layout/algorithm/Algorithm.cpp +++ b/src/layout/algorithm/Algorithm.cpp @@ -42,16 +42,16 @@ void CAlgorithm::removeTarget(SP target) { const bool IS_FLOATING = std::ranges::contains(m_floatingTargets, target); if (IS_FLOATING) { - std::erase(m_floatingTargets, target); m_floating->removeTarget(target); + std::erase(m_floatingTargets, target); return; } const bool IS_TILED = std::ranges::contains(m_tiledTargets, target); if (IS_TILED) { - std::erase(m_tiledTargets, target); m_tiled->removeTarget(target); + std::erase(m_tiledTargets, target); return; } @@ -262,10 +262,3 @@ SP CAlgorithm::getNextCandidate(SP old) { // god damn it, maybe empty? return nullptr; } - -void CAlgorithm::setTargetGeom(const CBox& box, SP target) { - if (!target->floating() || !std::ranges::contains(m_floatingTargets, target)) - return; - - m_floating->setTargetGeom(box, target); -} diff --git a/src/layout/algorithm/Algorithm.hpp b/src/layout/algorithm/Algorithm.hpp index 7df6c5c1..3ee26a3c 100644 --- a/src/layout/algorithm/Algorithm.hpp +++ b/src/layout/algorithm/Algorithm.hpp @@ -40,8 +40,6 @@ namespace Layout { void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); void moveTarget(const Vector2D& Δ, SP target); - void setTargetGeom(const CBox& box, SP target); // only for float - void updateFloatingAlgo(UP&& algo); void updateTiledAlgo(UP&& algo); diff --git a/src/layout/algorithm/FloatingAlgorithm.hpp b/src/layout/algorithm/FloatingAlgorithm.hpp index 40e53034..2c9ff14b 100644 --- a/src/layout/algorithm/FloatingAlgorithm.hpp +++ b/src/layout/algorithm/FloatingAlgorithm.hpp @@ -17,9 +17,6 @@ namespace Layout { // a target is being moved by a delta virtual void moveTarget(const Vector2D& Δ, SP target) = 0; - // a target is moved to a pos x size - virtual void setTargetGeom(const CBox& geom, SP target) = 0; - virtual void recenter(SP t); virtual void recalculate(); diff --git a/src/layout/algorithm/ModeAlgorithm.cpp b/src/layout/algorithm/ModeAlgorithm.cpp index dea5bb17..261c54da 100644 --- a/src/layout/algorithm/ModeAlgorithm.cpp +++ b/src/layout/algorithm/ModeAlgorithm.cpp @@ -1,10 +1,5 @@ #include "ModeAlgorithm.hpp" -#include "../space/Space.hpp" -#include "Algorithm.hpp" -#include "../../helpers/Monitor.hpp" -#include "../../desktop/view/Window.hpp" - using namespace Layout; std::expected IModeAlgorithm::layoutMsg(const std::string_view& sv) { @@ -14,20 +9,3 @@ std::expected IModeAlgorithm::layoutMsg(const std::string_vie std::optional IModeAlgorithm::predictSizeForNewTarget() { return std::nullopt; } - -std::optional IModeAlgorithm::focalPointForDir(SP t, Math::eDirection dir) { - Vector2D focalPoint; - - const auto WINDOWIDEALBB = - t->fullscreenMode() != FSMODE_NONE ? m_parent->space()->workspace()->m_monitor->logicalBox() : t->window()->getWindowIdealBoundingBoxIgnoreReserved(); - - switch (dir) { - case Math::DIRECTION_UP: focalPoint = WINDOWIDEALBB.pos() + Vector2D{WINDOWIDEALBB.size().x / 2.0, -1.0}; break; - case Math::DIRECTION_DOWN: focalPoint = WINDOWIDEALBB.pos() + Vector2D{WINDOWIDEALBB.size().x / 2.0, WINDOWIDEALBB.size().y + 1.0}; break; - case Math::DIRECTION_LEFT: focalPoint = WINDOWIDEALBB.pos() + Vector2D{-1.0, WINDOWIDEALBB.size().y / 2.0}; break; - case Math::DIRECTION_RIGHT: focalPoint = WINDOWIDEALBB.pos() + Vector2D{WINDOWIDEALBB.size().x + 1.0, WINDOWIDEALBB.size().y / 2.0}; break; - default: return std::nullopt; - } - - return focalPoint; -} diff --git a/src/layout/algorithm/ModeAlgorithm.hpp b/src/layout/algorithm/ModeAlgorithm.hpp index 0fedc3da..90d7ce58 100644 --- a/src/layout/algorithm/ModeAlgorithm.hpp +++ b/src/layout/algorithm/ModeAlgorithm.hpp @@ -44,9 +44,6 @@ namespace Layout { // optional: predict new window's size virtual std::optional predictSizeForNewTarget(); - // Impl'd here: focal point for dir - virtual std::optional focalPointForDir(SP t, Math::eDirection dir); - protected: IModeAlgorithm() = default; diff --git a/src/layout/algorithm/floating/default/DefaultFloatingAlgorithm.cpp b/src/layout/algorithm/floating/default/DefaultFloatingAlgorithm.cpp index 0d069e4f..1fe3b068 100644 --- a/src/layout/algorithm/floating/default/DefaultFloatingAlgorithm.cpp +++ b/src/layout/algorithm/floating/default/DefaultFloatingAlgorithm.cpp @@ -116,8 +116,6 @@ void CDefaultFloatingAlgorithm::newTarget(SP target) { PWINDOW->m_reportedSize = PWINDOW->m_pendingReportedSize; } } - - updateTarget(target); } void CDefaultFloatingAlgorithm::movedTarget(SP target, std::optional focalPoint) { @@ -154,8 +152,6 @@ void CDefaultFloatingAlgorithm::movedTarget(SP target, std::optionalsetPositionGlobal(fitBoxInWorkArea(CBox{NEW_POS, LAST_SIZE}, target)); } - - updateTarget(target); } CBox CDefaultFloatingAlgorithm::fitBoxInWorkArea(const CBox& box, SP t) { @@ -177,7 +173,6 @@ CBox CDefaultFloatingAlgorithm::fitBoxInWorkArea(const CBox& box, SP t) void CDefaultFloatingAlgorithm::removeTarget(SP target) { target->rememberFloatingSize(target->position().size()); - m_datas.erase(target); } void CDefaultFloatingAlgorithm::resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner) { @@ -189,8 +184,6 @@ void CDefaultFloatingAlgorithm::resizeTarget(const Vector2D& Δ, SP tar if (g_layoutManager->dragController()->target() == target) target->warpPositionSize(); - - updateTarget(target); } void CDefaultFloatingAlgorithm::moveTarget(const Vector2D& Δ, SP target) { @@ -200,17 +193,12 @@ void CDefaultFloatingAlgorithm::moveTarget(const Vector2D& Δ, SP targe if (g_layoutManager->dragController()->target() == target) target->warpPositionSize(); - - updateTarget(target); } void CDefaultFloatingAlgorithm::swapTargets(SP a, SP b) { auto posABackup = a->position(); a->setPositionGlobal(b->position()); b->setPositionGlobal(posABackup); - - updateTarget(a); - updateTarget(b); } void CDefaultFloatingAlgorithm::moveTargetInDirection(SP t, Math::eDirection dir, bool silent) { @@ -228,25 +216,4 @@ void CDefaultFloatingAlgorithm::moveTargetInDirection(SP t, Math::eDire } t->setPositionGlobal(pos); - - updateTarget(t); -} - -void CDefaultFloatingAlgorithm::recenter(SP t) { - if (!m_datas.contains(t)) { - IFloatingAlgorithm::recenter(t); - return; - } - - t->setPositionGlobal(m_datas.at(t).lastBox); -} - -void CDefaultFloatingAlgorithm::setTargetGeom(const CBox& geom, SP target) { - target->setPositionGlobal(geom); - - updateTarget(target); -} - -void CDefaultFloatingAlgorithm::updateTarget(SP t) { - m_datas[t] = {.lastBox = t->position()}; } diff --git a/src/layout/algorithm/floating/default/DefaultFloatingAlgorithm.hpp b/src/layout/algorithm/floating/default/DefaultFloatingAlgorithm.hpp index 1e87fac1..ef94e371 100644 --- a/src/layout/algorithm/floating/default/DefaultFloatingAlgorithm.hpp +++ b/src/layout/algorithm/floating/default/DefaultFloatingAlgorithm.hpp @@ -1,7 +1,5 @@ #include "../../FloatingAlgorithm.hpp" -#include - namespace Layout { class CAlgorithm; } @@ -19,22 +17,10 @@ namespace Layout::Floating { virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); virtual void moveTarget(const Vector2D& Δ, SP target); - virtual void setTargetGeom(const CBox& geom, SP target); - virtual void swapTargets(SP a, SP b); virtual void moveTargetInDirection(SP t, Math::eDirection dir, bool silent); - virtual void recenter(SP t); - private: CBox fitBoxInWorkArea(const CBox& box, SP t); - - void updateTarget(SP); - - struct SWindowData { - CBox lastBox; - }; - - std::map, SWindowData> m_datas; }; }; \ No newline at end of file diff --git a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp index 7ef36753..f5e230f8 100644 --- a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp +++ b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp @@ -99,13 +99,11 @@ void CDwindleAlgorithm::addTarget(SP target, bool newTarget) { if (!OPENINGON && g_pCompositor->isPointOnReservedArea(MOUSECOORDS, ACTIVE_MON)) OPENINGON = getClosestNode(MOUSECOORDS); - } else if (*PUSEACTIVE || m_overrideFocalPoint) { + } else if (*PUSEACTIVE) { const auto ACTIVE_WINDOW = Desktop::focusState()->window(); - if (m_overrideFocalPoint) - OPENINGON = getClosestNode(*m_overrideFocalPoint); - else if (!m_overrideFocalPoint && ACTIVE_WINDOW && !ACTIVE_WINDOW->m_isFloating && ACTIVE_WINDOW != target->window() && ACTIVE_WINDOW->m_workspace == PWORKSPACE && - ACTIVE_WINDOW->m_isMapped) + if (!m_overrideFocalPoint && ACTIVE_WINDOW && !ACTIVE_WINDOW->m_isFloating && ACTIVE_WINDOW != target->window() && ACTIVE_WINDOW->m_workspace == PWORKSPACE && + ACTIVE_WINDOW->m_isMapped) OPENINGON = getNodeFromWindow(ACTIVE_WINDOW); if (!OPENINGON) @@ -184,7 +182,7 @@ void CDwindleAlgorithm::addTarget(SP target, bool newTarget) { // whether or not the override persists after opening one window if (*PERMANENTDIRECTIONOVERRIDE == 0) m_overrideDirection = Math::DIRECTION_DEFAULT; - } else if (*PSMARTSPLIT == 1 || m_overrideFocalPoint) { + } else if (*PSMARTSPLIT == 1) { const auto PARENT_CENTER = NEWPARENT->box.pos() + NEWPARENT->box.size() / 2; const auto PARENT_PROPORTIONS = NEWPARENT->box.h / NEWPARENT->box.w; const auto DELTA = MOUSECOORDS - PARENT_CENTER; @@ -216,7 +214,10 @@ void CDwindleAlgorithm::addTarget(SP target, bool newTarget) { } } } else if (*PFORCESPLIT == 0 || !newTarget) { - if ((SIDEBYSIDE && MOUSECOORDS.x < NEWPARENT->box.x + (NEWPARENT->box.w / 2.F)) || (!SIDEBYSIDE && MOUSECOORDS.y < NEWPARENT->box.y + (NEWPARENT->box.h / 2.F))) { + if ((SIDEBYSIDE && + VECINRECT(MOUSECOORDS, NEWPARENT->box.x, NEWPARENT->box.y / *PWIDTHMULTIPLIER, NEWPARENT->box.x + NEWPARENT->box.w / 2.f, NEWPARENT->box.y + NEWPARENT->box.h)) || + (!SIDEBYSIDE && + VECINRECT(MOUSECOORDS, NEWPARENT->box.x, NEWPARENT->box.y / *PWIDTHMULTIPLIER, NEWPARENT->box.x + NEWPARENT->box.w, NEWPARENT->box.y + NEWPARENT->box.h / 2.f))) { // we are hovering over the first node, make PNODE first. NEWPARENT->children[1] = OPENINGON; NEWPARENT->children[0] = PNODE; @@ -241,10 +242,11 @@ void CDwindleAlgorithm::addTarget(SP target, bool newTarget) { // and update the previous parent if it exists if (OPENINGON->pParent) { - if (OPENINGON->pParent->children[0] == OPENINGON) + if (OPENINGON->pParent->children[0] == OPENINGON) { OPENINGON->pParent->children[0] = NEWPARENT; - else + } else { OPENINGON->pParent->children[1] = NEWPARENT; + } } // Update the children @@ -549,35 +551,41 @@ std::optional CDwindleAlgorithm::predictSizeForNewTarget() { } void CDwindleAlgorithm::moveTargetInDirection(SP t, Math::eDirection dir, bool silent) { - static auto PMONITORFALLBACK = CConfigValue("binds:window_direction_monitor_fallback"); - const auto PNODE = getNodeFromTarget(t); const Vector2D originalPos = t->position().middle(); if (!PNODE || !t->window()) return; - const auto FOCAL_POINT = focalPointForDir(t, dir); + Vector2D focalPoint; - const auto PMONITORFOCAL = g_pCompositor->getMonitorFromVector(FOCAL_POINT.value_or(t->position().middle())); + const auto WINDOWIDEALBB = + t->fullscreenMode() != FSMODE_NONE ? m_parent->space()->workspace()->m_monitor->logicalBox() : t->window()->getWindowIdealBoundingBoxIgnoreReserved(); - if (PMONITORFOCAL != m_parent->space()->workspace()->m_monitor && !*PMONITORFALLBACK) - return; // noop + switch (dir) { + case Math::DIRECTION_UP: focalPoint = WINDOWIDEALBB.pos() + Vector2D{WINDOWIDEALBB.size().x / 2.0, -1.0}; break; + case Math::DIRECTION_DOWN: focalPoint = WINDOWIDEALBB.pos() + Vector2D{WINDOWIDEALBB.size().x / 2.0, WINDOWIDEALBB.size().y + 1.0}; break; + case Math::DIRECTION_LEFT: focalPoint = WINDOWIDEALBB.pos() + Vector2D{-1.0, WINDOWIDEALBB.size().y / 2.0}; break; + case Math::DIRECTION_RIGHT: focalPoint = WINDOWIDEALBB.pos() + Vector2D{WINDOWIDEALBB.size().x + 1.0, WINDOWIDEALBB.size().y / 2.0}; break; + default: return; + } t->window()->setAnimationsToMove(); removeTarget(t); + const auto PMONITORFOCAL = g_pCompositor->getMonitorFromVector(focalPoint); + if (PMONITORFOCAL != m_parent->space()->workspace()->m_monitor) { // move with a focal point if (PMONITORFOCAL->m_activeWorkspace) - t->assignToSpace(PMONITORFOCAL->m_activeWorkspace->m_space, FOCAL_POINT); + t->assignToSpace(PMONITORFOCAL->m_activeWorkspace->m_space); return; } - movedTarget(t, FOCAL_POINT); + movedTarget(t, focalPoint); // restore focus to the previous position if (silent) { @@ -657,28 +665,11 @@ std::expected CDwindleAlgorithm::layoutMsg(const std::string_ const auto CURRENT_NODE = getNodeFromWindow(Desktop::focusState()->window()); if (ARGS[0] == "togglesplit") { - if (CURRENT_NODE) { - if (!toggleSplit(CURRENT_NODE)) - return std::unexpected("can't togglesplit in the current workspace"); - } + if (CURRENT_NODE) + toggleSplit(CURRENT_NODE); } else if (ARGS[0] == "swapsplit") { - if (CURRENT_NODE) { - if (!swapSplit(CURRENT_NODE)) - return std::unexpected("can't swapsplit in the current workspace"); - } - } else if (ARGS[0] == "rotatesplit") { - if (CURRENT_NODE) { - int angle = 90; - if (!ARGS[1].empty()) { - try { - angle = std::stoi(std::string{ARGS[1]}); - } catch (const std::exception& e) { - Log::logger->log(Log::WARN, "Invalid angle argument for rotatesplit: {}", ARGS[1]); - return std::unexpected("Invalid angle argument"); - } - } - rotateSplit(CURRENT_NODE, angle); - } + if (CURRENT_NODE) + swapSplit(CURRENT_NODE); } else if (ARGS[0] == "movetoroot") { auto node = CURRENT_NODE; if (!ARGS[1].empty()) { @@ -688,8 +679,7 @@ std::expected CDwindleAlgorithm::layoutMsg(const std::string_ } const auto STABLE = ARGS[2].empty() || ARGS[2] != "unstable"; - if (!moveToRoot(node, STABLE)) - return std::unexpected("can't movetoroot in the current workspace"); + moveToRoot(node, STABLE); } else if (ARGS[0] == "preselect") { auto direction = ARGS[1]; @@ -724,102 +714,42 @@ std::expected CDwindleAlgorithm::layoutMsg(const std::string_ break; } } - } else if (ARGS[0] == "splitratio") { - auto ratio = ARGS[1]; - bool exact = ARGS[2].starts_with("exact"); - - if (ratio.empty()) - return std::unexpected("splitratio requires an arg"); - - auto delta = getPlusMinusKeywordResult(std::string{ratio}, 0.F); - - if (!CURRENT_NODE || !CURRENT_NODE->pParent) - return std::unexpected("cannot alter split ratio on no / single node"); - - if (!delta) - return std::unexpected(std::format("failed to parse \"{}\" as a delta", ratio)); - - const float newRatio = exact ? *delta : CURRENT_NODE->pParent->splitRatio + *delta; - CURRENT_NODE->pParent->splitRatio = std::clamp(newRatio, 0.1F, 1.9F); - - CURRENT_NODE->pParent->recalcSizePosRecursive(); } return {}; } -bool CDwindleAlgorithm::toggleSplit(SP x) { +void CDwindleAlgorithm::toggleSplit(SP x) { if (!x || !x->pParent) - return false; + return; if (x->pTarget->fullscreenMode() != FSMODE_NONE) - return false; + return; x->pParent->splitTop = !x->pParent->splitTop; x->pParent->recalcSizePosRecursive(); - - return true; } -bool CDwindleAlgorithm::swapSplit(SP x) { - if (x->pTarget->fullscreenMode() != FSMODE_NONE || !x->pParent) - return false; +void CDwindleAlgorithm::swapSplit(SP x) { + if (x->pTarget->fullscreenMode() != FSMODE_NONE) + return; std::swap(x->pParent->children[0], x->pParent->children[1]); x->pParent->recalcSizePosRecursive(); - - return true; } -void CDwindleAlgorithm::rotateSplit(SP x, int angle) { +void CDwindleAlgorithm::moveToRoot(SP x, bool stable) { if (!x || !x->pParent) return; if (x->pTarget->fullscreenMode() != FSMODE_NONE) return; - // normalize the angle to multiples of 90 degrees - int normalizedAngle = ((sc(angle / 90) % 4) + 4) % 4; // ensures positive modulo - - auto pParent = x->pParent; - - bool shouldSwap = false; - - switch (normalizedAngle) { - case 0: // 0 degrees - no change - break; - case 1: - if (pParent->splitTop) - shouldSwap = true; - pParent->splitTop = !pParent->splitTop; - break; - case 2: shouldSwap = true; break; - case 3: - if (!pParent->splitTop) - shouldSwap = true; - pParent->splitTop = !pParent->splitTop; - break; - default: break; // should never happen - } - - if (shouldSwap) - std::swap(pParent->children[0], pParent->children[1]); - - pParent->recalcSizePosRecursive(); -} - -bool CDwindleAlgorithm::moveToRoot(SP x, bool stable) { - if (!x || !x->pParent) - return false; - - if (x->pTarget->fullscreenMode() != FSMODE_NONE) - return false; - // already at root if (!x->pParent->pParent) - return false; + return; auto& pNode = x->pParent->children[0] == x ? x->pParent->children[0] : x->pParent->children[1]; @@ -840,6 +770,4 @@ bool CDwindleAlgorithm::moveToRoot(SP x, bool stable) { std::swap(pRoot->children[0], pRoot->children[1]); pRoot->recalcSizePosRecursive(); - - return true; } diff --git a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp index 41cbf8bb..594b033b 100644 --- a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp +++ b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp @@ -48,10 +48,9 @@ namespace Layout::Tiled { SP getClosestNode(const Vector2D&, SP skip = nullptr); SP getMasterNode(); - bool toggleSplit(SP); - bool swapSplit(SP); - void rotateSplit(SP, int angle = 90); - bool moveToRoot(SP, bool stable = true); + void toggleSplit(SP); + void swapSplit(SP); + void moveToRoot(SP, bool stable = true); Math::eDirection m_overrideDirection = Math::DIRECTION_DEFAULT; }; diff --git a/src/layout/algorithm/tiled/master/MasterAlgorithm.cpp b/src/layout/algorithm/tiled/master/MasterAlgorithm.cpp index 7c436b31..7f421e49 100644 --- a/src/layout/algorithm/tiled/master/MasterAlgorithm.cpp +++ b/src/layout/algorithm/tiled/master/MasterAlgorithm.cpp @@ -403,9 +403,7 @@ void CMasterAlgorithm::swapTargets(SP a, SP b) { } void CMasterAlgorithm::moveTargetInDirection(SP t, Math::eDirection dir, bool silent) { - static auto PMONITORFALLBACK = CConfigValue("binds:window_direction_monitor_fallback"); - - const auto PWINDOW2 = g_pCompositor->getWindowInDirection(t->window(), dir); + const auto PWINDOW2 = g_pCompositor->getWindowInDirection(t->window(), dir); if (!t->window()) return; @@ -426,10 +424,7 @@ void CMasterAlgorithm::moveTargetInDirection(SP t, Math::eDirection dir t->window()->setAnimationsToMove(); if (t->window()->m_workspace != targetWs) { - if (!*PMONITORFALLBACK) - return; // noop - - t->assignToSpace(targetWs->m_space, focalPointForDir(t, dir)); + t->assignToSpace(targetWs->m_space); } else if (PWINDOW2) { // if same monitor, switch windows g_layoutManager->switchTargets(t, PWINDOW2->layoutTarget()); @@ -725,8 +720,8 @@ std::expected CMasterAlgorithm::layoutMsg(const std::string_v for (auto& nd : m_masterNodesData) { if (!nd->isMaster) { - const auto& newMaster = nd; - newMaster->isMaster = true; + const auto newMaster = nd; + newMaster->isMaster = true; auto newMasterIt = std::ranges::find(m_masterNodesData, newMaster); @@ -761,8 +756,8 @@ std::expected CMasterAlgorithm::layoutMsg(const std::string_v for (auto& nd : m_masterNodesData | std::views::reverse) { if (!nd->isMaster) { - const auto& newMaster = nd; - newMaster->isMaster = true; + const auto newMaster = nd; + newMaster->isMaster = true; auto newMasterIt = std::ranges::find(m_masterNodesData, newMaster); @@ -961,9 +956,7 @@ void CMasterAlgorithm::calculateWorkspace() { const auto STACKWINDOWS = WINDOWS - MASTERS; const auto WORKAREA = m_parent->space()->workArea(); const auto PMONITOR = m_parent->space()->workspace()->m_monitor; - const auto reservedLeft = PMONITOR ? PMONITOR->m_reservedArea.left() : 0; - const auto reservedRight = PMONITOR ? PMONITOR->m_reservedArea.right() : 0; - const auto UNRESERVED_WIDTH = WORKAREA.width + reservedLeft + reservedRight; + const auto UNRESERVED_WIDTH = WORKAREA.width + PMONITOR->m_reservedArea.left() + PMONITOR->m_reservedArea.right(); if (orientation == ORIENTATION_CENTER) { if (STACKWINDOWS >= *SLAVECOUNTFORCENTER) @@ -1081,7 +1074,7 @@ void CMasterAlgorithm::calculateWorkspace() { } nd->size = Vector2D(WIDTH, HEIGHT); - nd->position = (*PIGNORERESERVED && centerMasterWindow ? WORKAREA.pos() - Vector2D(reservedLeft, 0.0) : WORKAREA.pos()) + Vector2D(nextX, nextY); + nd->position = (*PIGNORERESERVED && centerMasterWindow ? WORKAREA.pos() - Vector2D(PMONITOR->m_reservedArea.left(), 0.0) : WORKAREA.pos()) + Vector2D(nextX, nextY); nd->pTarget->setPositionGlobal({nd->position, nd->size}); mastersLeft--; @@ -1199,7 +1192,7 @@ void CMasterAlgorithm::calculateWorkspace() { continue; if (onRight) { - nextX = WIDTH + PMASTERNODE->size.x - (*PIGNORERESERVED ? reservedLeft : 0); + nextX = WIDTH + PMASTERNODE->size.x - (*PIGNORERESERVED ? PMONITOR->m_reservedArea.left() : 0); nextY = nextYR; heightLeft = heightLeftR; slavesLeft = slavesLeftR; @@ -1224,7 +1217,7 @@ void CMasterAlgorithm::calculateWorkspace() { } } - nd->size = Vector2D(*PIGNORERESERVED ? (WIDTH - (onRight ? reservedRight : reservedLeft)) : WIDTH, HEIGHT); + nd->size = Vector2D(*PIGNORERESERVED ? (WIDTH - (onRight ? PMONITOR->m_reservedArea.right() : PMONITOR->m_reservedArea.left())) : WIDTH, HEIGHT); nd->position = WORKAREA.pos() + Vector2D(nextX, nextY); nd->pTarget->setPositionGlobal({nd->position, nd->size}); diff --git a/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.cpp b/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.cpp index fe92f27c..6e9e822c 100644 --- a/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.cpp +++ b/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.cpp @@ -202,11 +202,6 @@ void CMonocleAlgorithm::swapTargets(SP a, SP b) { } void CMonocleAlgorithm::moveTargetInDirection(SP t, Math::eDirection dir, bool silent) { - static auto PMONITORFALLBACK = CConfigValue("binds:window_direction_monitor_fallback"); - - if (!*PMONITORFALLBACK) - return; // noop - // try to find a monitor in the specified direction, thats the logical thing if (!t || !t->space() || !t->space()->workspace()) return; @@ -220,7 +215,7 @@ void CMonocleAlgorithm::moveTargetInDirection(SP t, Math::eDirection di if (t->window()) t->window()->setAnimationsToMove(); - t->assignToSpace(TARGETWS->m_space, focalPointForDir(t, dir)); + t->assignToSpace(TARGETWS->m_space); } } diff --git a/src/layout/algorithm/tiled/scrolling/ScrollTapeController.cpp b/src/layout/algorithm/tiled/scrolling/ScrollTapeController.cpp index 93a7dac1..c6cda4b5 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollTapeController.cpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollTapeController.cpp @@ -55,14 +55,12 @@ size_t CScrollTapeController::addStrip(float size) { return m_strips.size() - 1; } -void CScrollTapeController::insertStrip(ssize_t afterIndex, float size) { - if (afterIndex >= sc(m_strips.size())) { +void CScrollTapeController::insertStrip(size_t afterIndex, float size) { + if (afterIndex >= m_strips.size()) { addStrip(size); return; } - afterIndex = std::clamp(afterIndex, sc(-1L), sc(INT32_MAX)); - SStripData newStrip; newStrip.size = size; m_strips.insert(m_strips.begin() + afterIndex + 1, newStrip); diff --git a/src/layout/algorithm/tiled/scrolling/ScrollTapeController.hpp b/src/layout/algorithm/tiled/scrolling/ScrollTapeController.hpp index da2efbba..4e0fef7f 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollTapeController.hpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollTapeController.hpp @@ -40,7 +40,7 @@ namespace Layout::Tiled { bool isReversed() const; size_t addStrip(float size = 1.0F); - void insertStrip(ssize_t afterIndex, float size = 1.0F); + void insertStrip(size_t afterIndex, float size = 1.0F); void removeStrip(size_t index); size_t stripCount() const; SStripData& getStrip(size_t index); diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp index ae7c6ecc..8206a796 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp @@ -190,12 +190,10 @@ size_t SColumnData::idx(SP t) { } size_t SColumnData::idxForHeight(float y) { - if (targetDatas.empty()) - return 0; for (size_t i = 0; i < targetDatas.size(); ++i) { if (targetDatas[i]->target->position().y < y) continue; - return i == 0 ? 0 : i - 1; + return i - 1; } return targetDatas.size() - 1; } @@ -247,28 +245,24 @@ void SColumnData::remove(SP t) { scrollingData->remove(self.lock()); } -bool SColumnData::up(SP w) { +void SColumnData::up(SP w) { for (size_t i = 1; i < targetDatas.size(); ++i) { if (targetDatas[i] != w) continue; std::swap(targetDatas[i], targetDatas[i - 1]); - return true; + break; } - - return false; } -bool SColumnData::down(SP w) { +void SColumnData::down(SP w) { for (size_t i = 0; i < targetDatas.size() - 1; ++i) { if (targetDatas[i] != w) continue; std::swap(targetDatas[i], targetDatas[i + 1]); - return true; + break; } - - return false; } SP SColumnData::next(SP w) { @@ -302,21 +296,23 @@ SScrollingData::SScrollingData(CScrollingAlgorithm* algo) : algorithm(algo) { } SP SScrollingData::add() { - auto col = columns.emplace_back(makeShared(self.lock())); - col->self = col; + static const auto PCOLWIDTH = CConfigValue("scrolling:column_width"); + auto col = columns.emplace_back(makeShared(self.lock())); + col->self = col; - size_t stripIdx = controller->addStrip(algorithm->defaultColumnWidth()); + size_t stripIdx = controller->addStrip(*PCOLWIDTH); controller->getStrip(stripIdx).userData = col; return col; } SP SScrollingData::add(int after) { - auto col = makeShared(self.lock()); - col->self = col; + static const auto PCOLWIDTH = CConfigValue("scrolling:column_width"); + auto col = makeShared(self.lock()); + col->self = col; columns.insert(columns.begin() + after + 1, col); - controller->insertStrip(after, algorithm->defaultColumnWidth()); + controller->insertStrip(after, *PCOLWIDTH); controller->getStrip(after + 1).userData = col; return col; @@ -470,21 +466,6 @@ CScrollingAlgorithm::CScrollingAlgorithm() { m_scrollingData = makeShared(this); m_scrollingData->self = m_scrollingData; - // Helper to parse explicit_column_widths string - auto parseColumnWidths = [](const std::string& dir) -> std::vector { - auto widthVec = std::vector(); - - CConstVarList widths(dir, 0, ','); - for (auto& w : widths) { - try { - widthVec.emplace_back(std::clamp(std::stof(std::string{w}), MIN_COLUMN_WIDTH, MAX_COLUMN_WIDTH)); - } catch (...) { Log::logger->log(Log::ERR, "scrolling: Failed to parse width {} as float", w); } - } - if (widthVec.empty()) - widthVec = {0.333, 0.5, 0.667, 1.0}; // default - return widthVec; - }; - // Helper to parse direction string auto parseDirection = [](const std::string& dir) -> eScrollDirection { if (dir == "left") @@ -497,11 +478,19 @@ CScrollingAlgorithm::CScrollingAlgorithm() { return SCROLL_DIR_RIGHT; // default }; - m_configCallback = Event::bus()->m_events.config.reloaded.listen([this, parseColumnWidths, parseDirection] { + m_configCallback = Event::bus()->m_events.config.reloaded.listen([this, parseDirection] { static const auto PCONFDIRECTION = CConfigValue("scrolling:direction"); m_config.configuredWidths.clear(); - m_config.configuredWidths = parseColumnWidths(*PCONFWIDTHS); + + CConstVarList widths(*PCONFWIDTHS, 0, ','); + for (auto& w : widths) { + try { + m_config.configuredWidths.emplace_back(std::stof(std::string{w})); + } catch (...) { Log::logger->log(Log::ERR, "scrolling: Failed to parse width {} as float", w); } + } + if (m_config.configuredWidths.empty()) + m_config.configuredWidths = {0.333, 0.5, 0.667, 1.0}; // Update scroll direction m_scrollingData->controller->setDirection(parseDirection(*PCONFDIRECTION)); @@ -534,7 +523,7 @@ CScrollingAlgorithm::CScrollingAlgorithm() { }); // Initialize default widths and direction - m_config.configuredWidths = parseColumnWidths(*PCONFWIDTHS); + m_config.configuredWidths = {0.333, 0.5, 0.667, 1.0}; m_scrollingData->controller->setDirection(parseDirection(*PCONFDIRECTION)); } @@ -627,20 +616,16 @@ void CScrollingAlgorithm::removeTarget(SP target) { if (!m_scrollingData->next(DATA->column.lock()) && DATA->column->targetDatas.size() <= 1) { // move the view if this is the last column - const auto USABLE = usableArea(); - const bool isPrimaryHoriz = m_scrollingData->controller->isPrimaryHorizontal(); - const double usablePrimary = isPrimaryHoriz ? USABLE.w : USABLE.h; - m_scrollingData->controller->adjustOffset(-(usablePrimary * DATA->column->getColumnWidth())); + const auto USABLE = usableArea(); + m_scrollingData->controller->adjustOffset(-(USABLE.w * DATA->column->getColumnWidth())); } DATA->column->remove(target); if (!DATA->column) { // column got removed, let's ensure we don't leave any cringe extra space - const auto USABLE = usableArea(); - const bool isPrimaryHoriz = m_scrollingData->controller->isPrimaryHorizontal(); - const double usablePrimary = isPrimaryHoriz ? USABLE.w : USABLE.h; - const double newOffset = std::clamp(m_scrollingData->controller->getOffset(), 0.0, std::max(m_scrollingData->maxWidth() - usablePrimary, 1.0)); + const auto USABLE = usableArea(); + double newOffset = std::clamp(m_scrollingData->controller->getOffset(), 0.0, std::max(m_scrollingData->maxWidth() - USABLE.w, 1.0)); m_scrollingData->controller->setOffset(newOffset); } @@ -848,129 +833,66 @@ void CScrollingAlgorithm::moveTargetInDirection(SP t, Math::eDirection } void CScrollingAlgorithm::moveTargetTo(SP t, Math::eDirection dir, bool silent) { - static auto PMONITORFALLBACK = CConfigValue("binds:window_direction_monitor_fallback"); - - const auto DATA = dataFor(t); + const auto DATA = dataFor(t); if (!DATA) return; + const auto TAPE_DIR = getDynamicDirection(); const auto CURRENT_COL = DATA->column.lock(); const auto current_idx = m_scrollingData->idx(CURRENT_COL); - auto rotateDir = [this](Math::eDirection dir) -> Math::eDirection { - switch (m_scrollingData->controller->getDirection()) { - case SCROLL_DIR_RIGHT: return dir; - case SCROLL_DIR_LEFT: { - if (dir == Math::DIRECTION_LEFT) - return Math::DIRECTION_RIGHT; - if (dir == Math::DIRECTION_RIGHT) - return Math::DIRECTION_LEFT; - return dir; - } - case SCROLL_DIR_UP: { - switch (dir) { - case Math::DIRECTION_UP: return Math::DIRECTION_RIGHT; - case Math::DIRECTION_DOWN: return Math::DIRECTION_LEFT; - case Math::DIRECTION_LEFT: return Math::DIRECTION_DOWN; - case Math::DIRECTION_RIGHT: return Math::DIRECTION_UP; - default: break; - } - - return dir; - } - case SCROLL_DIR_DOWN: { - switch (dir) { - case Math::DIRECTION_UP: return Math::DIRECTION_LEFT; - case Math::DIRECTION_DOWN: return Math::DIRECTION_RIGHT; - case Math::DIRECTION_LEFT: return Math::DIRECTION_DOWN; - case Math::DIRECTION_RIGHT: return Math::DIRECTION_UP; - default: break; - } - - return dir; - } - default: break; - } - - return dir; - }; - - const auto ROTATED_DIR = rotateDir(dir); - - auto commenceDir = [&]() -> bool { - if (ROTATED_DIR == Math::DIRECTION_LEFT) { - const auto COL = m_scrollingData->prev(DATA->column.lock()); - - // ignore moves to the origin if we are alone - if (!COL && current_idx == 0 && DATA->column->targetDatas.size() == 1) - return false; - - DATA->column->remove(t); - - if (!COL) { - const auto NEWCOL = m_scrollingData->add(-1); - NEWCOL->add(DATA); - m_scrollingData->centerOrFitCol(NEWCOL); - } else { - if (COL->targetDatas.size() > 0) - COL->add(DATA, COL->idxForHeight(g_pInputManager->getMouseCoordsInternal().y)); - else - COL->add(DATA); - m_scrollingData->centerOrFitCol(COL); - } - - return true; - } else if (ROTATED_DIR == Math::DIRECTION_RIGHT) { - const auto COL = m_scrollingData->next(DATA->column.lock()); - - // ignore move to the right when there is no next column and we're alone - if (!COL && current_idx == (int64_t)m_scrollingData->columns.size() - 1 && DATA->column->targetDatas.size() == 1) - return false; - - DATA->column->remove(t); - - if (!COL) { - // make a new one - const auto NEWCOL = m_scrollingData->add(); - NEWCOL->add(DATA); - m_scrollingData->centerOrFitCol(NEWCOL); - } else { - if (COL->targetDatas.size() > 0) - COL->add(DATA, COL->idxForHeight(g_pInputManager->getMouseCoordsInternal().y)); - else - COL->add(DATA); - m_scrollingData->centerOrFitCol(COL); - } - - return true; - } else if (ROTATED_DIR == Math::DIRECTION_UP) - return DATA->column->up(DATA); - else if (ROTATED_DIR == Math::DIRECTION_DOWN) - return DATA->column->down(DATA); - - return false; - }; - - if (!commenceDir()) { - // dir wasn't commenced, move to a workspace if possible - // with the original dir - - if (!*PMONITORFALLBACK) - return; // noop - - const auto MONINDIR = g_pCompositor->getMonitorInDirection(m_parent->space()->workspace()->m_monitor.lock(), dir); - if (MONINDIR && MONINDIR != m_parent->space()->workspace()->m_monitor && MONINDIR->m_activeWorkspace) { - t->assignToSpace(MONINDIR->m_activeWorkspace->m_space, focalPointForDir(t, dir)); - - m_scrollingData->recalculate(); + if (dir == Math::DIRECTION_LEFT) { + const auto COL = m_scrollingData->prev(DATA->column.lock()); + // ignore moves to the "origin" when on first column and moving opposite to tape direction + if (!COL && current_idx == 0 && (TAPE_DIR == SCROLL_DIR_RIGHT || TAPE_DIR == SCROLL_DIR_DOWN)) return; + + DATA->column->remove(t); + + if (!COL) { + const auto NEWCOL = m_scrollingData->add(-1); + NEWCOL->add(DATA); + m_scrollingData->centerOrFitCol(NEWCOL); + } else { + if (COL->targetDatas.size() > 0) + COL->add(DATA, COL->idxForHeight(g_pInputManager->getMouseCoordsInternal().y)); + else + COL->add(DATA); + m_scrollingData->centerOrFitCol(COL); } - } + } else if (dir == Math::DIRECTION_RIGHT) { + const auto COL = m_scrollingData->next(DATA->column.lock()); + + // ignore moves to the "origin" when on last column and moving opposite to tape direction + if (!COL && current_idx == (int64_t)m_scrollingData->columns.size() - 1 && (TAPE_DIR == SCROLL_DIR_LEFT || TAPE_DIR == SCROLL_DIR_UP)) + return; + + DATA->column->remove(t); + + if (!COL) { + // make a new one + const auto NEWCOL = m_scrollingData->add(); + NEWCOL->add(DATA); + m_scrollingData->centerOrFitCol(NEWCOL); + } else { + if (COL->targetDatas.size() > 0) + COL->add(DATA, COL->idxForHeight(g_pInputManager->getMouseCoordsInternal().y)); + else + COL->add(DATA); + m_scrollingData->centerOrFitCol(COL); + } + + } else if (dir == Math::DIRECTION_UP) + DATA->column->up(DATA); + else if (dir == Math::DIRECTION_DOWN) + DATA->column->down(DATA); m_scrollingData->recalculate(); focusTargetUpdate(t); + if (t->window()) + g_pCompositor->warpCursorTo(t->window()->middle()); } std::expected CScrollingAlgorithm::layoutMsg(const std::string_view& sv) { @@ -1255,9 +1177,8 @@ std::expected CScrollingAlgorithm::layoutMsg(const std::strin m_scrollingData->recalculate(); } } else if (ARGS[0] == "focus") { - const auto TDATA = dataFor(Desktop::focusState()->window() ? Desktop::focusState()->window()->layoutTarget() : nullptr); - static const auto PNOFALLBACK = CConfigValue("general:no_focus_fallback"); - static const auto PCONFWRAPFOCUS = CConfigValue("scrolling:wrap_focus"); + const auto TDATA = dataFor(Desktop::focusState()->window() ? Desktop::focusState()->window()->layoutTarget() : nullptr); + static const auto PNOFALLBACK = CConfigValue("general:no_focus_fallback"); if (!TDATA || ARGS[1].empty()) return std::unexpected("no window to focus"); @@ -1313,7 +1234,7 @@ std::expected CScrollingAlgorithm::layoutMsg(const std::strin g_pCompositor->warpCursorTo(TDATA->target->window()->middle()); return {}; } else - PREV = (*PCONFWRAPFOCUS == 1) ? m_scrollingData->columns.back() : m_scrollingData->columns.front(); + PREV = m_scrollingData->columns.back(); } auto pTargetData = findBestNeighbor(TDATA, PREV); @@ -1335,7 +1256,7 @@ std::expected CScrollingAlgorithm::layoutMsg(const std::strin g_pCompositor->warpCursorTo(TDATA->target->window()->middle()); return {}; } else - NEXT = (*PCONFWRAPFOCUS == 1) ? m_scrollingData->columns.front() : m_scrollingData->columns.back(); + NEXT = m_scrollingData->columns.front(); } auto pTargetData = findBestNeighbor(TDATA, NEXT); @@ -1362,8 +1283,6 @@ std::expected CScrollingAlgorithm::layoutMsg(const std::strin m_scrollingData->recalculate(); } else if (ARGS[0] == "swapcol") { - static const auto PCONFWRAPSWAPCOL = CConfigValue("scrolling:wrap_swapcol"); - if (ARGS.size() < 2) return std::unexpected("not enough args"); @@ -1389,15 +1308,9 @@ std::expected CScrollingAlgorithm::layoutMsg(const std::strin // wrap around swaps if (direction == "l") - if (*PCONFWRAPSWAPCOL == 1) - targetIdx = (currentIdx == 0) ? (colCount - 1) : (currentIdx - 1); - else - targetIdx = (currentIdx == 0) ? 0 : (currentIdx - 1); + targetIdx = (currentIdx == 0) ? (colCount - 1) : (currentIdx - 1); else if (direction == "r") - if (*PCONFWRAPSWAPCOL == 1) - targetIdx = (currentIdx == (int64_t)colCount - 1) ? 0 : (currentIdx + 1); - else - targetIdx = (currentIdx == (int64_t)colCount - 1) ? (colCount - 1) : (currentIdx + 1); + targetIdx = (currentIdx == (int64_t)colCount - 1) ? 0 : (currentIdx + 1); else return std::unexpected("no target (invalid direction?)"); ; @@ -1505,14 +1418,9 @@ CBox CScrollingAlgorithm::usableArea() { CBox box = m_parent->space()->workArea(); // doesn't matter, this happens when this algo is about to be destroyed - if (!m_parent->space()->workspace() || !m_parent->space()->workspace()->m_monitor) + if (!m_parent->space()->workspace()) return box; box.translate(-m_parent->space()->workspace()->m_monitor->m_position); return box; } - -float CScrollingAlgorithm::defaultColumnWidth() { - static const auto PCOLWIDTH = CConfigValue("scrolling:column_width"); - return std::clamp(*PCOLWIDTH, MIN_COLUMN_WIDTH, MAX_COLUMN_WIDTH); -} diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp index d95b3197..20db6efe 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp @@ -40,8 +40,8 @@ namespace Layout::Tiled { // index of lowest target that is above y. size_t idxForHeight(float y); - bool up(SP w); - bool down(SP w); + void up(SP w); + void down(SP w); SP next(SP w); SP prev(SP w); @@ -138,8 +138,6 @@ namespace Layout::Tiled { void moveTargetTo(SP t, Math::eDirection dir, bool silent); void focusOnInput(SP target, eInputMode input); - float defaultColumnWidth(); - friend struct SScrollingData; }; }; diff --git a/src/layout/space/Space.cpp b/src/layout/space/Space.cpp index db3925f6..742c398a 100644 --- a/src/layout/space/Space.cpp +++ b/src/layout/space/Space.cpp @@ -6,7 +6,6 @@ #include "../../debug/log/Logger.hpp" #include "../../desktop/Workspace.hpp" #include "../../config/ConfigManager.hpp" -#include "../../event/EventBus.hpp" using namespace Layout; @@ -18,12 +17,6 @@ SP CSpace::create(PHLWORKSPACE w) { CSpace::CSpace(PHLWORKSPACE parent) : m_parent(parent) { recheckWorkArea(); - - // NOLINTNEXTLINE - m_geomUpdateCallback = Event::bus()->m_events.monitor.layoutChanged.listen([this] { - recheckWorkArea(); - m_algorithm->recalculate(); - }); } void CSpace::add(SP t) { @@ -183,11 +176,6 @@ void CSpace::moveTargetInDirection(SP t, Math::eDirection dir, bool sil m_algorithm->moveTargetInDirection(t, dir, silent); } -void CSpace::setTargetGeom(const CBox& box, SP target) { - if (m_algorithm) - m_algorithm->setTargetGeom(box, target); -} - SP CSpace::getNextCandidate(SP old) { return !m_algorithm ? nullptr : m_algorithm->getNextCandidate(old); } diff --git a/src/layout/space/Space.hpp b/src/layout/space/Space.hpp index e29a6d8f..4229e99d 100644 --- a/src/layout/space/Space.hpp +++ b/src/layout/space/Space.hpp @@ -47,7 +47,6 @@ namespace Layout { void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); void moveTarget(const Vector2D& Δ, SP target); - void setTargetGeom(const CBox& box, SP target); // only for float SP algorithm() const; @@ -64,8 +63,5 @@ namespace Layout { // work area is in global coords CBox m_workArea, m_floatingWorkArea; - - // for recalc - CHyprSignalListener m_geomUpdateCallback; }; }; \ No newline at end of file diff --git a/src/layout/supplementary/DragController.cpp b/src/layout/supplementary/DragController.cpp index be70f4ac..a28aef07 100644 --- a/src/layout/supplementary/DragController.cpp +++ b/src/layout/supplementary/DragController.cpp @@ -239,8 +239,6 @@ void CDragStateController::dragEnd() { draggingTarget->damageEntire(); - g_layoutManager->setTargetGeom(draggingTarget->position(), draggingTarget); - Desktop::focusState()->fullWindowFocus(draggingTarget->window(), Desktop::FOCUS_REASON_DESKTOP_STATE_CHANGE); m_wasDraggingWindow = false; diff --git a/src/layout/target/WindowTarget.cpp b/src/layout/target/WindowTarget.cpp index db03a385..05c328af 100644 --- a/src/layout/target/WindowTarget.cpp +++ b/src/layout/target/WindowTarget.cpp @@ -10,9 +10,6 @@ #include "../../Compositor.hpp" #include "../../render/Renderer.hpp" -#include - -using namespace Hyprutils::Utils; using namespace Layout; SP CWindowTarget::create(PHLWINDOW w) { @@ -37,9 +34,6 @@ void CWindowTarget::setPositionGlobal(const CBox& box) { void CWindowTarget::updatePos() { - g_pHyprRenderer->damageWindow(m_window.lock()); - CScopeGuard x([this] { g_pHyprRenderer->damageWindow(m_window.lock()); }); - if (!m_space) return; @@ -61,11 +55,6 @@ void CWindowTarget::updatePos() { // Tiled is more complicated. - // if we are in maximized, force the box to be max work area. - // TODO: this shouldn't be here. - if (fullscreenMode() == FSMODE_MAXIMIZED) - ITarget::setPositionGlobal(m_space->workArea(floating())); - const auto PMONITOR = m_space->workspace()->m_monitor; const auto PWORKSPACE = m_space->workspace(); @@ -115,7 +104,7 @@ void CWindowTarget::updatePos() { Vector2D ratioPadding; - if ((*REQUESTEDRATIO).y != 0 && m_space->algorithm()->tiledTargets() <= 1 && fullscreenMode() == FSMODE_NONE) { + if ((*REQUESTEDRATIO).y != 0 && m_space->algorithm()->tiledTargets() <= 1) { const Vector2D originalSize = MONITOR_WORKAREA.size(); const double requestedRatio = (*REQUESTEDRATIO).x / (*REQUESTEDRATIO).y; @@ -142,7 +131,7 @@ void CWindowTarget::updatePos() { calcPos = calcPos + GAPOFFSETTOPLEFT + ratioPadding / 2; calcSize = calcSize - GAPOFFSETTOPLEFT - GAPOFFSETBOTTOMRIGHT - ratioPadding; - if (isPseudo() && fullscreenMode() == FSMODE_NONE) { + if (isPseudo()) { // Calculate pseudo float scale = 1; @@ -173,14 +162,18 @@ void CWindowTarget::updatePos() { static auto PCLAMP_TILED = CConfigValue("misc:size_limits_tiled"); if (*PCLAMP_TILED) { - Vector2D minSize = m_window->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}); - Vector2D maxSize = m_window->isFullscreen() ? Vector2D{INFINITY, INFINITY} : m_window->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY}); - calcSize = calcSize.clamp(minSize, maxSize); + const auto borderSize = m_window->getRealBorderSize(); + Vector2D monitorAvailable = MONITOR_WORKAREA.size() - Vector2D{2.0 * borderSize, 2.0 * borderSize}; + + Vector2D minSize = m_window->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}).clamp(Vector2D{0, 0}, monitorAvailable); + Vector2D maxSize = m_window->isFullscreen() ? Vector2D{INFINITY, INFINITY} : + m_window->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY}).clamp(Vector2D{0, 0}, monitorAvailable); + calcSize = calcSize.clamp(minSize, maxSize); calcPos += (availableSpace - calcSize) / 2.0; - calcPos.x = std::clamp(calcPos.x, MONITOR_WORKAREA.x, MONITOR_WORKAREA.x + MONITOR_WORKAREA.w - calcSize.x); - calcPos.y = std::clamp(calcPos.y, MONITOR_WORKAREA.y, MONITOR_WORKAREA.y + MONITOR_WORKAREA.h - calcSize.y); + calcPos.x = std::clamp(calcPos.x, MONITOR_WORKAREA.x + borderSize, MONITOR_WORKAREA.x + MONITOR_WORKAREA.w - calcSize.x - borderSize); + calcPos.y = std::clamp(calcPos.y, MONITOR_WORKAREA.y + borderSize, MONITOR_WORKAREA.y + MONITOR_WORKAREA.h - calcSize.y - borderSize); } if (m_window->onSpecialWorkspace() && !m_window->isFullscreen()) { @@ -377,6 +370,5 @@ void CWindowTarget::onUpdateSpace() { m_window->m_monitor = space()->workspace()->m_monitor; m_window->moveToWorkspace(space()->workspace()); m_window->updateToplevel(); - m_window->updateWindowData(); m_window->updateWindowDecos(); } diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index fd7c4e72..387baaea 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -109,6 +109,9 @@ CKeybindManager::CKeybindManager() { m_dispatchers["togglegroup"] = toggleGroup; m_dispatchers["changegroupactive"] = changeGroupActive; m_dispatchers["movegroupwindow"] = moveGroupWindow; + m_dispatchers["togglesplit"] = toggleSplit; + m_dispatchers["swapsplit"] = swapSplit; + m_dispatchers["splitratio"] = alterSplitRatio; m_dispatchers["focusmonitor"] = focusMonitor; m_dispatchers["movecursortocorner"] = moveCursorToCorner; m_dispatchers["movecursor"] = moveCursor; @@ -1465,10 +1468,9 @@ SDispatchResult CKeybindManager::moveActiveToWorkspaceSilent(std::string args) { } SDispatchResult CKeybindManager::moveFocusTo(std::string args) { - static auto PFULLCYCLE = CConfigValue("binds:movefocus_cycles_fullscreen"); - static auto PGROUPCYCLE = CConfigValue("binds:movefocus_cycles_groupfirst"); - static auto PMONITORFALLBACK = CConfigValue("binds:window_direction_monitor_fallback"); - Math::eDirection dir = Math::fromChar(args[0]); + static auto PFULLCYCLE = CConfigValue("binds:movefocus_cycles_fullscreen"); + static auto PGROUPCYCLE = CConfigValue("binds:movefocus_cycles_groupfirst"); + Math::eDirection dir = Math::fromChar(args[0]); if (dir == Math::DIRECTION_DEFAULT) { Log::logger->log(Log::ERR, "Cannot move focus in direction {}, unsupported direction. Supported: l,r,u/t,d/b", args[0]); @@ -1477,8 +1479,7 @@ SDispatchResult CKeybindManager::moveFocusTo(std::string args) { const auto PLASTWINDOW = Desktop::focusState()->window(); if (!PLASTWINDOW || !PLASTWINDOW->aliveAndVisible()) { - if (*PMONITORFALLBACK) - tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(dir)); + tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(dir)); return {}; } @@ -1508,7 +1509,7 @@ SDispatchResult CKeybindManager::moveFocusTo(std::string args) { Log::logger->log(Log::DEBUG, "No window found in direction {}, looking for a monitor", Math::toString(dir)); - if (*PMONITORFALLBACK && tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(dir))) + if (tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(dir))) return {}; static auto PNOFALLBACK = CConfigValue("general:no_focus_fallback"); @@ -1689,10 +1690,7 @@ SDispatchResult CKeybindManager::changeGroupActive(std::string args) { // index starts from '1'; '0' means last window try { const int INDEX = std::stoi(args); - if (INDEX <= 0) - PWINDOW->m_group->setCurrent(PWINDOW->m_group->size() - 1); - else - PWINDOW->m_group->setCurrent(INDEX - 1); + PWINDOW->m_group->setCurrent(INDEX); } catch (...) { return {.success = false, .error = "invalid idx"}; } return {}; @@ -1706,6 +1704,18 @@ SDispatchResult CKeybindManager::changeGroupActive(std::string args) { return {}; } +SDispatchResult CKeybindManager::toggleSplit(std::string args) { + return {.success = false, .error = "removed - use layoutmsg"}; +} + +SDispatchResult CKeybindManager::swapSplit(std::string args) { + return {.success = false, .error = "removed - use layoutmsg"}; +} + +SDispatchResult CKeybindManager::alterSplitRatio(std::string args) { + return {.success = false, .error = "removed - use layoutmsg"}; +} + SDispatchResult CKeybindManager::focusMonitor(std::string arg) { const auto PMONITOR = g_pCompositor->getMonitorFromString(arg); tryMoveFocusToMonitor(PMONITOR); @@ -2737,9 +2747,7 @@ void CKeybindManager::moveWindowOutOfGroup(PHLWINDOW pWindow, const std::string& WP group = pWindow->m_group; - const auto direction = !dir.empty() ? Math::fromChar(dir[0]) : Math::DIRECTION_DEFAULT; - - pWindow->m_group->remove(pWindow, direction); + pWindow->m_group->remove(pWindow); if (*BFOCUSREMOVEDWINDOW || !group) { Desktop::focusState()->fullWindowFocus(pWindow, Desktop::FOCUS_REASON_KEYBIND); diff --git a/src/managers/KeybindManager.hpp b/src/managers/KeybindManager.hpp index 1f013606..db570c8d 100644 --- a/src/managers/KeybindManager.hpp +++ b/src/managers/KeybindManager.hpp @@ -194,7 +194,10 @@ class CKeybindManager { static SDispatchResult swapActive(std::string); static SDispatchResult toggleGroup(std::string); static SDispatchResult changeGroupActive(std::string); + static SDispatchResult alterSplitRatio(std::string); static SDispatchResult focusMonitor(std::string); + static SDispatchResult toggleSplit(std::string); + static SDispatchResult swapSplit(std::string); static SDispatchResult moveCursorToCorner(std::string); static SDispatchResult moveCursor(std::string); static SDispatchResult workspaceOpt(std::string); diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp index 7256e176..bdc22f43 100644 --- a/src/managers/PointerManager.cpp +++ b/src/managers/PointerManager.cpp @@ -8,7 +8,6 @@ #include "../protocols/IdleNotify.hpp" #include "../protocols/core/Compositor.hpp" #include "../protocols/core/Seat.hpp" -#include "debug/log/Logger.hpp" #include "eventLoop/EventLoopManager.hpp" #include "../render/pass/TexPassElement.hpp" #include "../managers/input/InputManager.hpp" @@ -19,12 +18,9 @@ #include "../helpers/time/Time.hpp" #include "../helpers/Drm.hpp" #include "../event/EventBus.hpp" -#include #include #include #include -#include -#include #include using namespace Hyprutils::Utils; @@ -410,7 +406,7 @@ bool CPointerManager::setHWCursorBuffer(SP state, SP CPointerManager::renderHWCursorBuffer(SP state, SP texture) { +SP CPointerManager::renderHWCursorBuffer(SP state, SP texture) { auto maxSize = state->monitor->m_output->cursorPlaneSize(); auto const& cursorSize = m_currentCursorImage.size; @@ -543,23 +539,24 @@ SP CPointerManager::renderHWCursorBuffer(SPm_size / (m_currentCursorImage.size / m_currentCursorImage.scale * state->monitor->m_scale); - const auto SX = SCALE.x, SY = SCALE.y; - const auto BW = sc(DMABUF.size.x), BH = sc(DMABUF.size.y); + cairo_matrix_scale(&matrixPre, SCALE.x, SCALE.y); - // Cairo pattern matrix maps destination coords to source coords (inverse of visual transform). - // x_src = xx * x_dst + xy * y_dst + x0 - // y_src = yx * x_dst + yy * y_dst + y0 - // cairo_matrix_init(&m, xx, yx, xy, yy, x0, y0) - switch (TR) { - case WL_OUTPUT_TRANSFORM_NORMAL: - default: cairo_matrix_init(&matrixPre, SX, 0, 0, SY, 0, 0); break; - case WL_OUTPUT_TRANSFORM_90: cairo_matrix_init(&matrixPre, 0, SY, -SX, 0, SX * BW, 0); break; - case WL_OUTPUT_TRANSFORM_180: cairo_matrix_init(&matrixPre, -SX, 0, 0, -SY, SX * BW, SY * BH); break; - case WL_OUTPUT_TRANSFORM_270: cairo_matrix_init(&matrixPre, 0, -SY, SX, 0, 0, SY * BH); break; - case WL_OUTPUT_TRANSFORM_FLIPPED: cairo_matrix_init(&matrixPre, -SX, 0, 0, SY, SX * BW, 0); break; - case WL_OUTPUT_TRANSFORM_FLIPPED_90: cairo_matrix_init(&matrixPre, 0, SY, SX, 0, 0, 0); break; - case WL_OUTPUT_TRANSFORM_FLIPPED_180: cairo_matrix_init(&matrixPre, SX, 0, 0, -SY, 0, SY * BH); break; - case WL_OUTPUT_TRANSFORM_FLIPPED_270: cairo_matrix_init(&matrixPre, 0, -SY, -SX, 0, SX * BW, SY * BH); break; + if (TR) { + cairo_matrix_rotate(&matrixPre, M_PI_2 * sc(TR)); + + // FIXME: this is wrong, and doesn't work for 5, 6 and 7. (flipped + rot) + // cba to do it rn, does anyone fucking use that?? + if (TR >= WL_OUTPUT_TRANSFORM_FLIPPED) { + cairo_matrix_scale(&matrixPre, -1, 1); + cairo_matrix_translate(&matrixPre, -DMABUF.size.x, 0); + } + + if (TR == 3 || TR == 7) + cairo_matrix_translate(&matrixPre, -DMABUF.size.x, 0); + else if (TR == 2 || TR == 6) + cairo_matrix_translate(&matrixPre, -DMABUF.size.x, -DMABUF.size.y); + else if (TR == 1 || TR == 5) + cairo_matrix_translate(&matrixPre, 0, -DMABUF.size.y); } cairo_pattern_set_matrix(PATTERNPRE, &matrixPre); @@ -592,14 +589,15 @@ SP CPointerManager::renderHWCursorBuffer(SPbind(); - g_pHyprOpenGL->beginSimple(state->monitor.lock(), {0, 0, INT_MAX, INT_MAX}, RBO); + const auto& damageSize = state->monitor->m_output->cursorPlaneSize(); + g_pHyprOpenGL->beginSimple(state->monitor.lock(), {0, 0, damageSize.x, damageSize.y}, RBO); g_pHyprOpenGL->clear(CHyprColor{0.F, 0.F, 0.F, 0.F}); // ensure the RBO is zero initialized. CBox xbox = {{}, Vector2D{m_currentCursorImage.size / m_currentCursorImage.scale * state->monitor->m_scale}.round()}; Log::logger->log(Log::TRACE, "[pointer] monitor: {}, size: {}, hw buf: {}, scale: {:.2f}, monscale: {:.2f}, xbox: {}", state->monitor->m_name, m_currentCursorImage.size, cursorSize, m_currentCursorImage.scale, state->monitor->m_scale, xbox.size()); - g_pHyprOpenGL->renderTexture(texture, xbox, {.noCM = true}); + g_pHyprOpenGL->renderTexture(texture, xbox, {}); g_pHyprOpenGL->end(); g_pHyprOpenGL->m_renderData.pMonitor.reset(); @@ -903,13 +901,13 @@ const CPointerManager::SCursorImage& CPointerManager::currentCursorImage() { return m_currentCursorImage; } -SP CPointerManager::getCurrentCursorTexture() { +SP CPointerManager::getCurrentCursorTexture() { if (!m_currentCursorImage.pBuffer && (!m_currentCursorImage.surface || !m_currentCursorImage.surface->resource()->m_current.texture)) return nullptr; if (m_currentCursorImage.pBuffer) { if (!m_currentCursorImage.bufferTex) - m_currentCursorImage.bufferTex = g_pHyprRenderer->createTexture(m_currentCursorImage.pBuffer, true); + m_currentCursorImage.bufferTex = makeShared(m_currentCursorImage.pBuffer, true); return m_currentCursorImage.bufferTex; } diff --git a/src/managers/PointerManager.hpp b/src/managers/PointerManager.hpp index a4fe1971..218541a4 100644 --- a/src/managers/PointerManager.hpp +++ b/src/managers/PointerManager.hpp @@ -12,7 +12,7 @@ class CMonitor; class IHID; -class ITexture; +class CTexture; AQUAMARINE_FORWARD(IBuffer); @@ -71,7 +71,7 @@ class CPointerManager { struct SCursorImage { SP pBuffer; - SP bufferTex; + SP bufferTex; WP surface; Vector2D hotspot; @@ -83,7 +83,7 @@ class CPointerManager { }; const SCursorImage& currentCursorImage(); - SP getCurrentCursorTexture(); + SP getCurrentCursorTexture(); struct { CSignalT<> cursorChanged; @@ -181,7 +181,7 @@ class CPointerManager { std::vector> m_monitorStates; SP stateFor(PHLMONITOR mon); bool attemptHardwareCursor(SP state); - SP renderHWCursorBuffer(SP state, SP texture); + SP renderHWCursorBuffer(SP state, SP texture); bool setHWCursorBuffer(SP state, SP buf); struct { diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index 9195536f..64825633 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -847,9 +847,6 @@ void CInputManager::processMouseDownKill(const IPointer::SButtonEvent& e) { break; } - g_pEventManager->postEvent(SHyprIPCEvent({.event = "kill", .data = std::format("{:x}", rc(PWINDOW.m_data))})); - Event::bus()->m_events.window.kill.emit(PWINDOW); - // kill the mf kill(PWINDOW->getPID(), SIGKILL); break; diff --git a/src/managers/screenshare/CursorshareSession.cpp b/src/managers/screenshare/CursorshareSession.cpp index 703832ab..2322625f 100644 --- a/src/managers/screenshare/CursorshareSession.cpp +++ b/src/managers/screenshare/CursorshareSession.cpp @@ -169,10 +169,10 @@ bool CCursorshareSession::copy() { return false; } - auto outFB = g_pHyprRenderer->createFB(); - outFB->alloc(m_bufferSize.x, m_bufferSize.y, m_format); + CFramebuffer outFB; + outFB.alloc(m_bufferSize.x, m_bufferSize.y, m_format); - if (!g_pHyprRenderer->beginRender(m_pendingFrame.monitor, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, outFB, true)) { + if (!g_pHyprRenderer->beginRender(m_pendingFrame.monitor, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, &outFB, true)) { LOGM(Log::ERR, "Can't copy: failed to begin rendering to shm"); return false; } @@ -182,8 +182,8 @@ bool CCursorshareSession::copy() { g_pHyprRenderer->endRender(); g_pHyprOpenGL->m_renderData.pMonitor = m_pendingFrame.monitor; - outFB->bind(); - glBindFramebuffer(GL_READ_FRAMEBUFFER, GLFB(outFB)->getFBID()); + outFB.bind(); + glBindFramebuffer(GL_READ_FRAMEBUFFER, outFB.getFBID()); glPixelStorei(GL_PACK_ALIGNMENT, 1); @@ -212,7 +212,7 @@ bool CCursorshareSession::copy() { g_pHyprOpenGL->m_renderData.pMonitor.reset(); m_pendingFrame.buffer->endDataPtr(); - GLFB(outFB)->unbind(); + outFB.unbind(); glPixelStorei(GL_PACK_ALIGNMENT, 4); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); diff --git a/src/managers/screenshare/ScreenshareFrame.cpp b/src/managers/screenshare/ScreenshareFrame.cpp index d747ecee..73ccf958 100644 --- a/src/managers/screenshare/ScreenshareFrame.cpp +++ b/src/managers/screenshare/ScreenshareFrame.cpp @@ -10,7 +10,6 @@ #include "../../helpers/Monitor.hpp" #include "../../desktop/view/Window.hpp" #include "../../desktop/state/FocusState.hpp" -#include using namespace Screenshare; @@ -134,7 +133,7 @@ void CScreenshareFrame::copy() { // store a snapshot before the permission popup so we don't break screenshots const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(m_session->m_client, PERMISSION_TYPE_SCREENCOPY); if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) { - if (!m_session->m_tempFB || !m_session->m_tempFB->isAllocated()) + if (!m_session->m_tempFB.isAllocated()) storeTempFB(); // don't copy a frame while allow is pending because screenshot tools will only take the first frame we give, which is empty @@ -160,7 +159,7 @@ void CScreenshareFrame::renderMonitor() { const auto PMONITOR = m_session->monitor(); - auto TEXTURE = g_pHyprRenderer->createTexture(PMONITOR->m_output->state->state().buffer); + auto TEXTURE = makeShared(PMONITOR->m_output->state->state().buffer); const bool IS_CM_AWARE = PROTO::colorManagement && PROTO::colorManagement->isClientCMAware(m_session->m_client); g_pHyprOpenGL->m_renderData.transformDamage = false; @@ -296,11 +295,8 @@ void CScreenshareFrame::renderWindow() { return; auto pointerSurface = Desktop::View::CWLSurface::fromResource(pointerSurfaceResource); - if (!pointerSurface) - return; - auto box = pointerSurface->getSurfaceBoxGlobal(); - if (!box.has_value() || box->intersection(m_session->m_window->getFullWindowBoundingBox()).empty()) + if (!pointerSurface || pointerSurface->getSurfaceBoxGlobal()->intersection(m_session->m_window->getFullWindowBoundingBox()).empty()) return; if (Desktop::focusState()->window() != m_session->m_window) @@ -326,10 +322,10 @@ void CScreenshareFrame::render() { return; } - if (m_session->m_tempFB && m_session->m_tempFB->isAllocated()) { + if (m_session->m_tempFB.isAllocated()) { CBox texbox = {{}, m_bufferSize}; - g_pHyprOpenGL->renderTexture(m_session->m_tempFB->getTexture(), texbox, {}); - m_session->m_tempFB->release(); + g_pHyprOpenGL->renderTexture(m_session->m_tempFB.getTexture(), texbox, {}); + m_session->m_tempFB.release(); return; } @@ -382,12 +378,12 @@ bool CScreenshareFrame::copyShm() { return false; } - const auto PMONITOR = m_session->monitor(); + const auto PMONITOR = m_session->monitor(); - auto outFB = g_pHyprRenderer->createFB(); - outFB->alloc(m_bufferSize.x, m_bufferSize.y, shm.format); + CFramebuffer outFB; + outFB.alloc(m_bufferSize.x, m_bufferSize.y, shm.format); - if (!g_pHyprRenderer->beginRender(PMONITOR, m_damage, RENDER_MODE_FULL_FAKE, nullptr, outFB, true)) { + if (!g_pHyprRenderer->beginRender(PMONITOR, m_damage, RENDER_MODE_FULL_FAKE, nullptr, &outFB, true)) { LOGM(Log::ERR, "Can't copy: failed to begin rendering"); return false; } @@ -399,8 +395,8 @@ bool CScreenshareFrame::copyShm() { g_pHyprRenderer->endRender(); g_pHyprOpenGL->m_renderData.pMonitor = PMONITOR; - outFB->bind(); - glBindFramebuffer(GL_READ_FRAMEBUFFER, GLFB(outFB)->getFBID()); + outFB.bind(); + glBindFramebuffer(GL_READ_FRAMEBUFFER, outFB.getFBID()); glPixelStorei(GL_PACK_ALIGNMENT, 1); @@ -442,7 +438,7 @@ bool CScreenshareFrame::copyShm() { }); } - GLFB(outFB)->unbind(); + outFB.unbind(); glPixelStorei(GL_PACK_ALIGNMENT, 4); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); @@ -457,13 +453,13 @@ bool CScreenshareFrame::copyShm() { } void CScreenshareFrame::storeTempFB() { - if (!m_session->m_tempFB) - m_session->m_tempFB = g_pHyprRenderer->createFB(); - m_session->m_tempFB->alloc(m_bufferSize.x, m_bufferSize.y); + g_pHyprRenderer->makeEGLCurrent(); + + m_session->m_tempFB.alloc(m_bufferSize.x, m_bufferSize.y); CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX}; - if (!g_pHyprRenderer->beginRender(m_session->monitor(), fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, m_session->m_tempFB, true)) { + if (!g_pHyprRenderer->beginRender(m_session->monitor(), fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, &m_session->m_tempFB, true)) { LOGM(Log::ERR, "Can't copy: failed to begin rendering to temp fb"); return; } diff --git a/src/managers/screenshare/ScreenshareManager.cpp b/src/managers/screenshare/ScreenshareManager.cpp index 63f2bbbc..823e99b3 100644 --- a/src/managers/screenshare/ScreenshareManager.cpp +++ b/src/managers/screenshare/ScreenshareManager.cpp @@ -145,19 +145,15 @@ WP CScreenshareManager::getManagedSession(eScreenshareType auto& session = *it; session->stoppedListener = session->m_session->m_events.stopped.listen([session = WP(session)]() { - if (!session.expired()) - std::erase_if(Screenshare::mgr()->m_managedSessions, [&](const auto& s) { return s && s->m_session.get() == session->m_session.get(); }); + std::erase_if(Screenshare::mgr()->m_managedSessions, [&](const auto& s) { return !s || session.expired() || s->m_session == session->m_session; }); }); return session->m_session; } -bool CScreenshareManager::isOutputBeingSSd(PHLMONITOR monitor) { - return std::ranges::any_of(m_sessions, [monitor](const auto& s) { - if (!s) - return false; - return (s->m_type == SHARE_MONITOR || s->m_type == SHARE_REGION) && s->m_monitor == monitor; - }); +void CScreenshareManager::destroyClientSessions(wl_client* client) { + LOGM(Log::TRACE, "Destroy client sessions for {:x}", (uintptr_t)client); + std::erase_if(m_managedSessions, [&](const auto& session) { return !session || session->m_session->m_client == client; }); } CScreenshareManager::SManagedSession::SManagedSession(UP&& session) : m_session(std::move(session)) { diff --git a/src/managers/screenshare/ScreenshareManager.hpp b/src/managers/screenshare/ScreenshareManager.hpp index 5a4ada5e..4c61a7b0 100644 --- a/src/managers/screenshare/ScreenshareManager.hpp +++ b/src/managers/screenshare/ScreenshareManager.hpp @@ -75,7 +75,7 @@ namespace Screenshare { std::vector m_formats; Vector2D m_bufferSize = Vector2D(0, 0); - SP m_tempFB; + CFramebuffer m_tempFB; SP m_shareStopTimer; bool m_sharing = false; @@ -206,8 +206,9 @@ namespace Screenshare { UP newCursorSession(wl_client* client, WP pointer); + void destroyClientSessions(wl_client* client); + void onOutputCommit(PHLMONITOR monitor); - bool isOutputBeingSSd(PHLMONITOR monitor); private: std::vector> m_sessions; diff --git a/src/managers/screenshare/ScreenshareSession.cpp b/src/managers/screenshare/ScreenshareSession.cpp index 2fddc431..8e81454e 100644 --- a/src/managers/screenshare/ScreenshareSession.cpp +++ b/src/managers/screenshare/ScreenshareSession.cpp @@ -39,8 +39,7 @@ CScreenshareSession::CScreenshareSession(PHLMONITOR monitor, CBox captureRegion, CScreenshareSession::~CScreenshareSession() { stop(); - uintptr_t ptr = m_type == SHARE_WINDOW && !m_window.expired() ? (uintptr_t)m_window.get() : (m_monitor.expired() ? (uintptr_t)nullptr : (uintptr_t)m_monitor.get()); - LOGM(Log::TRACE, "Destroyed screenshare session for ({}): {}, {:x}", m_type, m_name, ptr); + LOGM(Log::TRACE, "Destroyed screenshare session for ({}): {}", m_type, m_name); } void CScreenshareSession::stop() { @@ -53,9 +52,6 @@ void CScreenshareSession::stop() { } void CScreenshareSession::init() { - uintptr_t ptr = m_type == SHARE_WINDOW && !m_window.expired() ? (uintptr_t)m_window.get() : (m_monitor.expired() ? (uintptr_t)nullptr : (uintptr_t)m_monitor.get()); - LOGM(Log::TRACE, "Created screenshare session for ({}): {}, {:x}", m_type, m_name, ptr); - m_shareStopTimer = makeShared( std::chrono::milliseconds(500), [this](SP self, void* data) { @@ -103,7 +99,7 @@ void CScreenshareSession::calculateConstraints() { m_name = PMONITOR->m_name; break; case SHARE_WINDOW: - m_bufferSize = (m_window->m_realSize->value() * PMONITOR->m_scale).round(); + m_bufferSize = m_window->m_realSize->value().round(); m_name = m_window->m_title; break; case SHARE_REGION: @@ -125,7 +121,7 @@ void CScreenshareSession::screenshareEvents(bool startSharing) { m_sharing = true; g_pEventManager->postEvent(SHyprIPCEvent{.event = "screencast", .data = std::format("1,{}", m_type)}); g_pEventManager->postEvent(SHyprIPCEvent{.event = "screencastv2", .data = std::format("1,{},{}", m_type, m_name)}); - LOGM(Log::INFO, "Started screenshare session for ({}): {}", m_type, m_name); + LOGM(Log::INFO, "New screenshare session for ({}): {}", m_type, m_name); Event::bus()->m_events.screenshare.state.emit(true, m_type, m_name); } else if (!startSharing && m_sharing) { diff --git a/src/protocols/ColorManagement.cpp b/src/protocols/ColorManagement.cpp index b9c3143b..90840217 100644 --- a/src/protocols/ColorManagement.cpp +++ b/src/protocols/ColorManagement.cpp @@ -3,7 +3,7 @@ #include "color-management-v1.hpp" #include "../helpers/Monitor.hpp" #include "core/Output.hpp" -#include "../helpers/cm/ColorManagement.hpp" +#include "types/ColorManagement.hpp" #include using namespace NColorManagement; @@ -388,6 +388,12 @@ CColorManagementFeedbackSurface::CColorManagementFeedbackSurface(SPm_settings = m_surface->getPreferredImageDescription(); m_currentPreferredId = RESOURCE->m_settings->id(); + if (!PROTO::colorManagement->m_debug && RESOURCE->m_settings->value().icc.fd >= 0) { + LOGM(Log::ERR, "FIXME: parse icc profile"); + r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "ICC profiles are not supported"); + return; + } + RESOURCE->resource()->sendReady(m_currentPreferredId); }); @@ -423,7 +429,7 @@ CColorManagementIccCreator::CColorManagementIccCreator(SPerror(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INCOMPLETE_SET, "Missing required settings"); return; } @@ -437,10 +443,10 @@ CColorManagementIccCreator::CColorManagementIccCreator(SPresource()->sendFailed(WP_IMAGE_DESCRIPTION_V1_CAUSE_UNSUPPORTED, "unsupported"); return; } @@ -453,9 +459,9 @@ CColorManagementIccCreator::CColorManagementIccCreator(SPsetSetIccFile([this](CWpImageDescriptionCreatorIccV1* r, int fd, uint32_t offset, uint32_t length) { - m_icc.fd = fd; - m_icc.offset = offset; - m_icc.length = length; + m_settings.icc.fd = fd; + m_settings.icc.offset = offset; + m_settings.icc.length = length; }); } @@ -725,9 +731,8 @@ CColorManagementImageDescriptionInfo::CColorManagementImageDescriptionInfo(SP(std::round(value * PRIMARIES_SCALE)); }; - // FIXME: - // if (m_icc.fd >= 0) - // m_resource->sendIccFile(m_icc.fd, m_icc.length); + if (m_settings.icc.fd >= 0) + m_resource->sendIccFile(m_settings.icc.fd, m_settings.icc.length); // send preferred client paramateres m_resource->sendPrimaries(toProto(m_settings.primaries.red.x), toProto(m_settings.primaries.red.y), toProto(m_settings.primaries.green.x), diff --git a/src/protocols/ColorManagement.hpp b/src/protocols/ColorManagement.hpp index 7cdab37d..d43d5c12 100644 --- a/src/protocols/ColorManagement.hpp +++ b/src/protocols/ColorManagement.hpp @@ -7,7 +7,7 @@ #include "../helpers/Monitor.hpp" #include "core/Compositor.hpp" #include "color-management-v1.hpp" -#include "../helpers/cm/ColorManagement.hpp" +#include "types/ColorManagement.hpp" class CColorManager; class CColorManagementOutput; @@ -109,14 +109,6 @@ class CColorManagementIccCreator { WP m_self; NColorManagement::SImageDescription m_settings; - struct SIccFile { - int fd = -1; - uint32_t length = 0; - uint32_t offset = 0; - bool operator==(const SIccFile& i2) const { - return fd == i2.fd; - } - } m_icc; private: SP m_resource; diff --git a/src/protocols/GammaControl.cpp b/src/protocols/GammaControl.cpp index c28b881f..2517c754 100644 --- a/src/protocols/GammaControl.cpp +++ b/src/protocols/GammaControl.cpp @@ -56,12 +56,6 @@ CGammaControl::CGammaControl(SP resource_, wl_resource* out LOGM(Log::DEBUG, "setGamma for {}", m_monitor->m_name); - if UNLIKELY (m_monitor->gammaRampsInUse()) { - LOGM(Log::ERR, "Monitor has gamma ramps in use (ICC?)"); - m_resource->sendFailed(); - return; - } - // TODO: make CFileDescriptor getflags use F_GETFL int fdFlags = fcntl(gammaFd.get(), F_GETFL, 0); if UNLIKELY (fdFlags < 0) { diff --git a/src/protocols/Screencopy.cpp b/src/protocols/Screencopy.cpp index 825939ef..74b3b608 100644 --- a/src/protocols/Screencopy.cpp +++ b/src/protocols/Screencopy.cpp @@ -13,7 +13,10 @@ CScreencopyClient::CScreencopyClient(SP resource_) : m return; m_resource->setDestroy([this](CZwlrScreencopyManagerV1* pMgr) { PROTO::screencopy->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwlrScreencopyManagerV1* pMgr) { PROTO::screencopy->destroyResource(this); }); + m_resource->setOnDestroy([this](CZwlrScreencopyManagerV1* pMgr) { + Screenshare::mgr()->destroyClientSessions(m_savedClient); + PROTO::screencopy->destroyResource(this); + }); m_resource->setCaptureOutput( [this](CZwlrScreencopyManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, wl_resource* output) { captureOutput(frame, overlayCursor, output, {}); }); m_resource->setCaptureOutputRegion([this](CZwlrScreencopyManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, wl_resource* output, int32_t x, int32_t y, int32_t w, @@ -22,6 +25,10 @@ CScreencopyClient::CScreencopyClient(SP resource_) : m m_savedClient = m_resource->client(); } +CScreencopyClient::~CScreencopyClient() { + Screenshare::mgr()->destroyClientSessions(m_savedClient); +} + void CScreencopyClient::captureOutput(uint32_t frame, int32_t overlayCursor_, wl_resource* output, CBox box) { const auto PMONITORRES = CWLOutputResource::fromResource(output); if (!PMONITORRES || !PMONITORRES->m_monitor) { @@ -62,6 +69,11 @@ CScreencopyFrame::CScreencopyFrame(SP resource_, WPsetCopy([this](CZwlrScreencopyFrameV1* pFrame, wl_resource* res) { shareFrame(pFrame, res, false); }); m_resource->setCopyWithDamage([this](CZwlrScreencopyFrameV1* pFrame, wl_resource* res) { shareFrame(pFrame, res, true); }); + m_listeners.stopped = m_session->m_events.stopped.listen([this]() { + if (good()) + m_resource->sendFailed(); + }); + m_frame = m_session->nextFrame(overlayCursor); auto formats = m_session->allowedFormats(); @@ -75,14 +87,7 @@ CScreencopyFrame::CScreencopyFrame(SP resource_, WPbufferSize(); const auto PSHMINFO = NFormatUtils::getPixelFormatFromDRM(format); - - if (!PSHMINFO) { - LOGM(Log::ERR, "No pixel format for drm format"); - m_resource->sendFailed(); - return; - } - - const auto stride = NFormatUtils::minStride(PSHMINFO, bufSize.x); + const auto stride = NFormatUtils::minStride(PSHMINFO, bufSize.x); m_resource->sendBuffer(NFormatUtils::drmToShm(format), bufSize.x, bufSize.y, stride); if (m_resource->version() >= 3) { @@ -99,12 +104,6 @@ void CScreencopyFrame::shareFrame(CZwlrScreencopyFrameV1* pFrame, wl_resource* b return; } - if UNLIKELY (m_session.expired() || !m_session->monitor()) { - LOGM(Log::ERR, "Session stopped for frame {:x}", (uintptr_t)this); - m_resource->sendFailed(); - return; - } - if UNLIKELY (m_buffer) { LOGM(Log::ERR, "Buffer used in {:x}", (uintptr_t)this); m_resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_ALREADY_USED, "frame already used"); diff --git a/src/protocols/Screencopy.hpp b/src/protocols/Screencopy.hpp index b73c090d..3659c753 100644 --- a/src/protocols/Screencopy.hpp +++ b/src/protocols/Screencopy.hpp @@ -19,6 +19,7 @@ namespace Screenshare { class CScreencopyClient { public: CScreencopyClient(SP resource_); + ~CScreencopyClient(); bool good(); @@ -51,7 +52,10 @@ class CScreencopyFrame { Time::steady_tp m_timestamp; bool m_overlayCursor = true; - // + struct { + CHyprSignalListener stopped; + } m_listeners; + void shareFrame(CZwlrScreencopyFrameV1* pFrame, wl_resource* buffer, bool withDamage); friend class CScreencopyProtocol; diff --git a/src/protocols/SinglePixel.cpp b/src/protocols/SinglePixel.cpp index c32379a3..51c3551c 100644 --- a/src/protocols/SinglePixel.cpp +++ b/src/protocols/SinglePixel.cpp @@ -12,11 +12,11 @@ CSinglePixelBuffer::CSinglePixelBuffer(uint32_t id, wl_client* client, CHyprColo m_opaque = col_.a >= 1.F; - m_texture = g_pHyprRenderer->createTexture(DRM_FORMAT_ARGB8888, rc(&m_color), 4, Vector2D{1, 1}); + m_texture = makeShared(DRM_FORMAT_ARGB8888, rc(&m_color), 4, Vector2D{1, 1}); m_resource = CWLBufferResource::create(makeShared(client, 1, id)); - m_success = m_texture->ok(); + m_success = m_texture->m_texID; size = {1, 1}; diff --git a/src/protocols/ToplevelExport.cpp b/src/protocols/ToplevelExport.cpp index d7ba7519..bf553a92 100644 --- a/src/protocols/ToplevelExport.cpp +++ b/src/protocols/ToplevelExport.cpp @@ -13,7 +13,10 @@ CToplevelExportClient::CToplevelExportClient(SPsetOnDestroy([this](CHyprlandToplevelExportManagerV1* pMgr) { PROTO::toplevelExport->destroyResource(this); }); + m_resource->setOnDestroy([this](CHyprlandToplevelExportManagerV1* pMgr) { + Screenshare::mgr()->destroyClientSessions(m_savedClient); + PROTO::toplevelExport->destroyResource(this); + }); m_resource->setDestroy([this](CHyprlandToplevelExportManagerV1* pMgr) { PROTO::toplevelExport->destroyResource(this); }); m_resource->setCaptureToplevel([this](CHyprlandToplevelExportManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, uint32_t handle) { captureToplevel(frame, overlayCursor, g_pCompositor->getWindowFromHandle(handle)); @@ -25,6 +28,10 @@ CToplevelExportClient::CToplevelExportClient(SPclient(); } +CToplevelExportClient::~CToplevelExportClient() { + Screenshare::mgr()->destroyClientSessions(m_savedClient); +} + void CToplevelExportClient::captureToplevel(uint32_t frame, int32_t overlayCursor_, PHLWINDOW handle) { auto session = Screenshare::mgr()->getManagedSession(m_resource->client(), handle); @@ -56,6 +63,11 @@ CToplevelExportFrame::CToplevelExportFrame(SP re m_resource->setDestroy([this](CHyprlandToplevelExportFrameV1* pFrame) { PROTO::toplevelExport->destroyResource(this); }); m_resource->setCopy([this](CHyprlandToplevelExportFrameV1* pFrame, wl_resource* res, int32_t ignoreDamage) { shareFrame(res, !!ignoreDamage); }); + m_listeners.stopped = m_session->m_events.stopped.listen([this]() { + if (good()) + m_resource->sendFailed(); + }); + m_frame = m_session->nextFrame(overlayCursor); auto formats = m_session->allowedFormats(); @@ -88,12 +100,6 @@ void CToplevelExportFrame::shareFrame(wl_resource* buffer, bool ignoreDamage) { return; } - if UNLIKELY (m_session.expired() || !m_session->monitor()) { - LOGM(Log::ERR, "Session stopped for frame {:x}", (uintptr_t)this); - m_resource->sendFailed(); - return; - } - if UNLIKELY (m_buffer) { LOGM(Log::ERR, "Buffer used in {:x}", (uintptr_t)this); m_resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_ALREADY_USED, "frame already used"); diff --git a/src/protocols/ToplevelExport.hpp b/src/protocols/ToplevelExport.hpp index 5d30f09b..38dec784 100644 --- a/src/protocols/ToplevelExport.hpp +++ b/src/protocols/ToplevelExport.hpp @@ -18,6 +18,7 @@ namespace Screenshare { class CToplevelExportClient { public: CToplevelExportClient(SP resource_); + ~CToplevelExportClient(); bool good(); @@ -49,7 +50,10 @@ class CToplevelExportFrame { CHLBufferReference m_buffer; Time::steady_tp m_timestamp; - // + struct { + CHyprSignalListener stopped; + } m_listeners; + void shareFrame(wl_resource* buffer, bool ignoreDamage); friend class CToplevelExportProtocol; diff --git a/src/protocols/core/Compositor.hpp b/src/protocols/core/Compositor.hpp index 37ca51b7..b5357520 100644 --- a/src/protocols/core/Compositor.hpp +++ b/src/protocols/core/Compositor.hpp @@ -20,7 +20,7 @@ #include "../../helpers/math/Math.hpp" #include "../../helpers/time/Time.hpp" #include "../types/Buffer.hpp" -#include "../../helpers/cm/ColorManagement.hpp" +#include "../types/ColorManagement.hpp" #include "../types/SurfaceRole.hpp" #include "../types/SurfaceState.hpp" diff --git a/src/protocols/types/Buffer.hpp b/src/protocols/types/Buffer.hpp index afff11a5..bda44ebc 100644 --- a/src/protocols/types/Buffer.hpp +++ b/src/protocols/types/Buffer.hpp @@ -26,7 +26,7 @@ class IHLBuffer : public Aquamarine::IBuffer { void onBackendRelease(const std::function& fn); void addReleasePoint(CDRMSyncPointState& point); - SP m_texture; + SP m_texture; bool m_opaque = false; SP m_resource; std::vector> m_syncReleasers; diff --git a/src/protocols/types/ColorManagement.cpp b/src/protocols/types/ColorManagement.cpp new file mode 100644 index 00000000..5d23d1c9 --- /dev/null +++ b/src/protocols/types/ColorManagement.cpp @@ -0,0 +1,110 @@ +#include "ColorManagement.hpp" +#include "../../macros.hpp" +#include +#include +#include + +namespace NColorManagement { + // expected to be small + static std::vector> knownPrimaries; + static std::vector> knownDescriptions; + static std::map, Hyprgraphics::CMatrix3> primariesConversion; + + const SPCPRimaries& getPrimaries(ePrimaries name) { + switch (name) { + case CM_PRIMARIES_SRGB: return NColorPrimaries::BT709; + case CM_PRIMARIES_BT2020: return NColorPrimaries::BT2020; + case CM_PRIMARIES_PAL_M: return NColorPrimaries::PAL_M; + case CM_PRIMARIES_PAL: return NColorPrimaries::PAL; + case CM_PRIMARIES_NTSC: return NColorPrimaries::NTSC; + case CM_PRIMARIES_GENERIC_FILM: return NColorPrimaries::GENERIC_FILM; + case CM_PRIMARIES_CIE1931_XYZ: return NColorPrimaries::CIE1931_XYZ; + case CM_PRIMARIES_DCI_P3: return NColorPrimaries::DCI_P3; + case CM_PRIMARIES_DISPLAY_P3: return NColorPrimaries::DISPLAY_P3; + case CM_PRIMARIES_ADOBE_RGB: return NColorPrimaries::ADOBE_RGB; + default: return NColorPrimaries::DEFAULT_PRIMARIES; + } + } + + CPrimaries::CPrimaries(const SPCPRimaries& primaries, const uint primariesId) : m_id(primariesId), m_primaries(primaries) { + m_primaries2XYZ = m_primaries.toXYZ(); + } + + WP CPrimaries::from(const SPCPRimaries& primaries) { + for (const auto& known : knownPrimaries) { + if (known->value() == primaries) + return known; + } + + knownPrimaries.emplace_back(CUniquePointer(new CPrimaries(primaries, knownPrimaries.size() + 1))); + return knownPrimaries.back(); + } + + WP CPrimaries::from(const ePrimaries name) { + return from(getPrimaries(name)); + } + + WP CPrimaries::from(const uint primariesId) { + ASSERT(primariesId <= knownPrimaries.size()); + return knownPrimaries[primariesId - 1]; + } + + const SPCPRimaries& CPrimaries::value() const { + return m_primaries; + } + + uint CPrimaries::id() const { + return m_id; + } + + const Hyprgraphics::CMatrix3& CPrimaries::toXYZ() const { + return m_primaries2XYZ; + } + + const Hyprgraphics::CMatrix3& CPrimaries::convertMatrix(const WP dst) const { + const auto cacheKey = std::make_pair(m_id, dst->m_id); + if (!primariesConversion.contains(cacheKey)) + primariesConversion.insert(std::make_pair(cacheKey, m_primaries.convertMatrix(dst->m_primaries))); + + return primariesConversion[cacheKey]; + } + + CImageDescription::CImageDescription(const SImageDescription& imageDescription, const uint imageDescriptionId) : + m_id(imageDescriptionId), m_imageDescription(imageDescription) { + m_primariesId = CPrimaries::from(m_imageDescription.getPrimaries())->id(); + } + + PImageDescription CImageDescription::from(const SImageDescription& imageDescription) { + for (const auto& known : knownDescriptions) { + if (known->value() == imageDescription) + return known; + } + + knownDescriptions.emplace_back(CUniquePointer(new CImageDescription(imageDescription, knownDescriptions.size() + 1))); + return knownDescriptions.back(); + } + + PImageDescription CImageDescription::from(const uint imageDescriptionId) { + ASSERT(imageDescriptionId <= knownDescriptions.size()); + return knownDescriptions[imageDescriptionId - 1]; + } + + PImageDescription CImageDescription::with(const SImageDescription::SPCLuminances& luminances) const { + auto desc = m_imageDescription; + desc.luminances = luminances; + return CImageDescription::from(desc); + } + + const SImageDescription& CImageDescription::value() const { + return m_imageDescription; + } + + uint CImageDescription::id() const { + return m_id; + } + + WP CImageDescription::getPrimaries() const { + return CPrimaries::from(m_primariesId); + } + +} \ No newline at end of file diff --git a/src/helpers/cm/ColorManagement.hpp b/src/protocols/types/ColorManagement.hpp similarity index 80% rename from src/helpers/cm/ColorManagement.hpp rename to src/protocols/types/ColorManagement.hpp index 0103e2a4..c1f58316 100644 --- a/src/helpers/cm/ColorManagement.hpp +++ b/src/protocols/types/ColorManagement.hpp @@ -3,11 +3,6 @@ #include "color-management-v1.hpp" #include #include "../../helpers/memory/Memory.hpp" -#include "../../helpers/math/Math.hpp" - -#include -#include -#include #define SDR_MIN_LUMINANCE 0.2 #define SDR_MAX_LUMINANCE 80.0 @@ -17,8 +12,6 @@ #define HDR_REF_LUMINANCE 203.0 #define HLG_MAX_LUMINANCE 1000.0 -class ITexture; - namespace NColorManagement { enum eNoShader : uint8_t { CM_NS_DISABLE = 0, @@ -74,6 +67,7 @@ namespace NColorManagement { using SPCPRimaries = Hyprgraphics::SPCPRimaries; namespace NColorPrimaries { + static const auto DEFAULT_PRIMARIES = SPCPRimaries{}; static const auto BT709 = SPCPRimaries{ .red = {.x = 0.64, .y = 0.33}, @@ -82,8 +76,6 @@ namespace NColorManagement { .white = {.x = 0.3127, .y = 0.3290}, }; - static const auto DEFAULT_PRIMARIES = BT709; - static const auto PAL_M = SPCPRimaries{ .red = {.x = 0.67, .y = 0.33}, .green = {.x = 0.21, .y = 0.71}, @@ -148,16 +140,7 @@ namespace NColorManagement { }; } - struct SVCGTTable16 { - uint16_t channels = 0; - uint16_t entries = 0; - uint16_t entrySize = 0; - std::array, 3> ch; - }; - - const SPCPRimaries& getPrimaries(ePrimaries name); - std::optional rgbToXYZFromPrimaries(SPCPRimaries pr); - Mat3x3 adaptBradford(Hyprgraphics::CColor::xy srcW, Hyprgraphics::CColor::xy dstW); + const SPCPRimaries& getPrimaries(ePrimaries name); class CPrimaries { public: @@ -180,17 +163,22 @@ namespace NColorManagement { }; struct SImageDescription { - static std::expected fromICC(const std::filesystem::path& file); + struct SIccFile { + int fd = -1; + uint32_t length = 0; + uint32_t offset = 0; + bool operator==(const SIccFile& i2) const { + return fd == i2.fd; + } + } icc; - // - std::vector rawICC; + bool windowsScRGB = false; - eTransferFunction transferFunction = CM_TRANSFER_FUNCTION_GAMMA22; - float transferFunctionPower = 1.0f; - bool windowsScRGB = false; + eTransferFunction transferFunction = CM_TRANSFER_FUNCTION_GAMMA22; + float transferFunctionPower = 1.0f; - bool primariesNameSet = false; - ePrimaries primariesNamed = CM_PRIMARIES_SRGB; + bool primariesNameSet = false; + ePrimaries primariesNamed = CM_PRIMARIES_SRGB; // primaries are stored as FP values with the same scale as standard defines (0.0 - 1.0) // wayland protocol expects int32_t values multiplied by 1000000 // drm expects uint16_t values multiplied by 50000 @@ -214,23 +202,11 @@ namespace NColorManagement { } } masteringLuminances; - // Matrix data from ICC - struct SICCData { - bool present = false; - size_t lutSize = 33; - std::vector lutDataPacked; - SP lutTexture; - std::optional vcgt; - } icc; - uint32_t maxCLL = 0; uint32_t maxFALL = 0; bool operator==(const SImageDescription& d2) const { - if (icc.present || d2.icc.present) - return false; - - return windowsScRGB == d2.windowsScRGB && transferFunction == d2.transferFunction && transferFunctionPower == d2.transferFunctionPower && + return icc == d2.icc && windowsScRGB == d2.windowsScRGB && transferFunction == d2.transferFunction && transferFunctionPower == d2.transferFunctionPower && (primariesNameSet == d2.primariesNameSet && (primariesNameSet ? primariesNamed == d2.primariesNamed : primaries == d2.primaries)) && masteringPrimaries == d2.masteringPrimaries && luminances == d2.luminances && masteringLuminances == d2.masteringLuminances && maxCLL == d2.maxCLL && maxFALL == d2.maxFALL; @@ -304,48 +280,44 @@ namespace NColorManagement { class CImageDescription { public: static WP from(const SImageDescription& imageDescription); - static WP from(const uint32_t imageDescriptionId); + static WP from(const uint imageDescriptionId); WP with(const SImageDescription::SPCLuminances& luminances) const; const SImageDescription& value() const; - uint32_t id() const; + uint id() const; WP getPrimaries() const; private: CImageDescription(const SImageDescription& imageDescription, const uint imageDescriptionId); - uint32_t m_id = 0; - uint32_t m_primariesId = 0; + uint m_id; + uint m_primariesId; SImageDescription m_imageDescription; }; using PImageDescription = WP; - static const auto DEFAULT_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{ - .transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22, - .primariesNameSet = true, - .primariesNamed = NColorManagement::CM_PRIMARIES_SRGB, - .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_SRGB), - .luminances = {.min = SDR_MIN_LUMINANCE, .max = 80, .reference = 80}, - }); - - static const auto DEFAULT_HDR_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{ - .transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ, - .primariesNameSet = true, - .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, - .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020), - .luminances = {.min = HDR_MIN_LUMINANCE, .max = 10000, .reference = 203}, - }); + static const auto DEFAULT_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22, + .primariesNameSet = true, + .primariesNamed = NColorManagement::CM_PRIMARIES_SRGB, + .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_SRGB), + .luminances = {.min = SDR_MIN_LUMINANCE, .max = 80, .reference = 80}}); + static const auto DEFAULT_HDR_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ, + .primariesNameSet = true, + .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, + .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020), + .luminances = {.min = HDR_MIN_LUMINANCE, .max = 10000, .reference = 203}}); + ; static const auto SCRGB_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{ - .transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_EXT_LINEAR, .windowsScRGB = true, + .transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_EXT_LINEAR, .primariesNameSet = true, .primariesNamed = NColorManagement::CM_PRIMARIES_SRGB, .primaries = NColorPrimaries::BT709, .luminances = {.reference = 203}, }); + ; - static const auto LINEAR_IMAGE_DESCRIPTION = SCRGB_IMAGE_DESCRIPTION; // TODO any reason to use something different? -} +} \ No newline at end of file diff --git a/src/protocols/types/DMABuffer.cpp b/src/protocols/types/DMABuffer.cpp index 86db8ca6..f3c3e067 100644 --- a/src/protocols/types/DMABuffer.cpp +++ b/src/protocols/types/DMABuffer.cpp @@ -13,28 +13,31 @@ using namespace Hyprutils::OS; CDMABuffer::CDMABuffer(uint32_t id, wl_client* client, Aquamarine::SDMABUFAttrs const& attrs_) : m_attrs(attrs_) { + g_pHyprRenderer->makeEGLCurrent(); + m_listeners.resourceDestroy = events.destroy.listen([this] { closeFDs(); m_listeners.resourceDestroy.reset(); }); - size = m_attrs.size; - m_resource = CWLBufferResource::create(makeShared(client, 1, id)); - m_opaque = NFormatUtils::isFormatOpaque(m_attrs.format); - m_texture = g_pHyprRenderer->createTexture(m_attrs, m_opaque); // texture takes ownership of the eglImage + size = m_attrs.size; + m_resource = CWLBufferResource::create(makeShared(client, 1, id)); + auto eglImage = g_pHyprOpenGL->createEGLImage(m_attrs); - if UNLIKELY (!m_texture) { + if UNLIKELY (!eglImage) { Log::logger->log(Log::ERR, "CDMABuffer: failed to import EGLImage, retrying as implicit"); m_attrs.modifier = DRM_FORMAT_MOD_INVALID; - m_texture = g_pHyprRenderer->createTexture(m_attrs, m_opaque); + eglImage = g_pHyprOpenGL->createEGLImage(m_attrs); - if UNLIKELY (!m_texture) { + if UNLIKELY (!eglImage) { Log::logger->log(Log::ERR, "CDMABuffer: failed to import EGLImage"); return; } } - m_success = m_texture->ok(); + m_texture = makeShared(m_attrs, eglImage); // texture takes ownership of the eglImage + m_opaque = NFormatUtils::isFormatOpaque(m_attrs.format); + m_success = m_texture->m_texID; if UNLIKELY (!m_success) Log::logger->log(Log::ERR, "Failed to create a dmabuf: texture is null"); diff --git a/src/protocols/types/SurfaceState.cpp b/src/protocols/types/SurfaceState.cpp index da98d3fb..46f2a563 100644 --- a/src/protocols/types/SurfaceState.cpp +++ b/src/protocols/types/SurfaceState.cpp @@ -1,7 +1,6 @@ #include "SurfaceState.hpp" #include "helpers/Format.hpp" #include "protocols/types/Buffer.hpp" -#include "render/Renderer.hpp" #include "render/Texture.hpp" Vector2D SSurfaceState::sourceSize() { @@ -35,7 +34,7 @@ CRegion SSurfaceState::accumulateBufferDamage() { return bufferDamage; } -void SSurfaceState::updateSynchronousTexture(SP lastTexture) { +void SSurfaceState::updateSynchronousTexture(SP lastTexture) { auto [dataPtr, fmt, size] = buffer->beginDataPtr(0); if (dataPtr) { auto drmFmt = NFormatUtils::shmToDRM(fmt); @@ -44,7 +43,7 @@ void SSurfaceState::updateSynchronousTexture(SP lastTexture) { texture = lastTexture; texture->update(drmFmt, dataPtr, stride, accumulateBufferDamage()); } else - texture = g_pHyprRenderer->createTexture(drmFmt, dataPtr, stride, bufferSize); + texture = makeShared(drmFmt, dataPtr, stride, bufferSize); } buffer->endDataPtr(); } diff --git a/src/protocols/types/SurfaceState.hpp b/src/protocols/types/SurfaceState.hpp index d5b7e4b9..f6caa83c 100644 --- a/src/protocols/types/SurfaceState.hpp +++ b/src/protocols/types/SurfaceState.hpp @@ -6,7 +6,7 @@ #include "../WaylandProtocol.hpp" #include "./Buffer.hpp" -class ITexture; +class CTexture; class CDRMSyncPointState; class CWLCallbackResource; @@ -88,8 +88,8 @@ struct SSurfaceState { eLockReason lockMask = LOCK_REASON_NONE; // texture of surface content, used for rendering - SP texture; - void updateSynchronousTexture(SP lastTexture); + SP texture; + void updateSynchronousTexture(SP lastTexture); // fifo bool barrierSet = false; diff --git a/src/render/Framebuffer.cpp b/src/render/Framebuffer.cpp index b2ff7e68..23bbd643 100644 --- a/src/render/Framebuffer.cpp +++ b/src/render/Framebuffer.cpp @@ -1,30 +1,140 @@ #include "Framebuffer.hpp" +#include "OpenGL.hpp" -IFramebuffer::IFramebuffer(const std::string& name) : m_name(name) {} +CFramebuffer::CFramebuffer() { + ; +} -bool IFramebuffer::alloc(int w, int h, uint32_t format) { +bool CFramebuffer::alloc(int w, int h, uint32_t drmFormat) { + bool firstAlloc = false; RASSERT((w > 0 && h > 0), "cannot alloc a FB with negative / zero size! (attempted {}x{})", w, h); const bool sizeChanged = (m_size != Vector2D(w, h)); - const bool formatChanged = (format != m_drmFormat); + const bool formatChanged = (drmFormat != m_drmFormat); - if (m_fbAllocated && !sizeChanged && !formatChanged) - return true; + if (!m_tex) { + m_tex = makeShared(); + m_tex->allocate(); + m_tex->bind(); + m_tex->setTexParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + m_tex->setTexParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + m_tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); + m_tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); + firstAlloc = true; + } - m_size = {w, h}; - m_drmFormat = format; - m_fbAllocated = internalAlloc(w, h, format); - return m_fbAllocated; + if (!m_fbAllocated) { + glGenFramebuffers(1, &m_fb); + m_fbAllocated = true; + firstAlloc = true; + } + + if (firstAlloc || sizeChanged || formatChanged) { + const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat); + m_tex->bind(); + glTexImage2D(GL_TEXTURE_2D, 0, format->glInternalFormat ? format->glInternalFormat : format->glFormat, w, h, 0, format->glFormat, format->glType, nullptr); + glBindFramebuffer(GL_FRAMEBUFFER, m_fb); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_tex->m_texID, 0); + + if (m_stencilTex) { + m_stencilTex->bind(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, w, h, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_stencilTex->m_texID, 0); + + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + } + + auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + RASSERT((status == GL_FRAMEBUFFER_COMPLETE), "Framebuffer incomplete, couldn't create! (FB status: {}, GL Error: 0x{:x})", status, sc(glGetError())); + + Log::logger->log(Log::DEBUG, "Framebuffer created, status {}", status); + } + + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + m_drmFormat = drmFormat; + m_size = Vector2D(w, h); + + return true; } -bool IFramebuffer::isAllocated() { +void CFramebuffer::addStencil(SP tex) { + if (m_stencilTex == tex) + return; + + m_stencilTex = tex; + m_stencilTex->bind(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, m_size.x, m_size.y, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr); + + glBindFramebuffer(GL_FRAMEBUFFER, m_fb); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_stencilTex->m_texID, 0); + + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + + auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + RASSERT((status == GL_FRAMEBUFFER_COMPLETE), "Failed adding a stencil to fbo!", status); + + m_stencilTex->unbind(); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void CFramebuffer::bind() { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fb); + + if (g_pHyprOpenGL) + g_pHyprOpenGL->setViewport(0, 0, g_pHyprOpenGL->m_renderData.pMonitor->m_pixelSize.x, g_pHyprOpenGL->m_renderData.pMonitor->m_pixelSize.y); + else + glViewport(0, 0, m_size.x, m_size.y); +} + +void CFramebuffer::unbind() { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); +} + +void CFramebuffer::release() { + if (m_fbAllocated) { + glBindFramebuffer(GL_FRAMEBUFFER, m_fb); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glDeleteFramebuffers(1, &m_fb); + m_fbAllocated = false; + m_fb = 0; + } + + if (m_tex) + m_tex.reset(); + + m_size = Vector2D(); +} + +CFramebuffer::~CFramebuffer() { + release(); +} + +bool CFramebuffer::isAllocated() { return m_fbAllocated && m_tex; } -SP IFramebuffer::getTexture() { +SP CFramebuffer::getTexture() { return m_tex; } -SP IFramebuffer::getStencilTex() { +GLuint CFramebuffer::getFBID() { + return m_fbAllocated ? m_fb : 0; +} + +SP CFramebuffer::getStencilTex() { return m_stencilTex; } + +void CFramebuffer::invalidate(const std::vector& attachments) { + if (!isAllocated()) + return; + + glInvalidateFramebuffer(GL_FRAMEBUFFER, attachments.size(), attachments.data()); +} diff --git a/src/render/Framebuffer.hpp b/src/render/Framebuffer.hpp index 7e33f227..e6c93876 100644 --- a/src/render/Framebuffer.hpp +++ b/src/render/Framebuffer.hpp @@ -3,38 +3,34 @@ #include "../defines.hpp" #include "../helpers/Format.hpp" #include "Texture.hpp" -#include #include -class CHLBufferReference; - -class IFramebuffer { +class CFramebuffer { public: - IFramebuffer() = default; - IFramebuffer(const std::string& name); - virtual ~IFramebuffer() = default; - - virtual bool alloc(int w, int h, uint32_t format = DRM_FORMAT_ARGB8888); - virtual void release() = 0; - virtual bool readPixels(CHLBufferReference buffer, uint32_t offsetX = 0, uint32_t offsetY = 0, uint32_t width = 0, uint32_t height = 0) = 0; - - virtual void bind() = 0; + CFramebuffer(); + ~CFramebuffer(); + bool alloc(int w, int h, uint32_t format = DRM_FORMAT_ARGB8888); + void addStencil(SP tex); + void bind(); + void unbind(); + void release(); + void reset(); bool isAllocated(); - SP getTexture(); - SP getStencilTex(); - - virtual void addStencil(SP tex) = 0; + SP getTexture(); + SP getStencilTex(); + GLuint getFBID(); + void invalidate(const std::vector& attachments); Vector2D m_size; - DRMFormat m_drmFormat = DRM_FORMAT_INVALID; + DRMFormat m_drmFormat = 0 /* DRM_FORMAT_INVALID */; - protected: - virtual bool internalAlloc(int w, int h, uint32_t format = DRM_FORMAT_ARGB8888) = 0; - - SP m_tex; + private: + SP m_tex; + GLuint m_fb = -1; bool m_fbAllocated = false; - SP m_stencilTex; - std::string m_name; // name for logging + SP m_stencilTex; + + friend class CRenderbuffer; }; diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 83ad05ca..916091db 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -20,7 +19,7 @@ #include "../protocols/LayerShell.hpp" #include "../protocols/core/Compositor.hpp" #include "../protocols/ColorManagement.hpp" -#include "../helpers/cm/ColorManagement.hpp" +#include "../protocols/types/ColorManagement.hpp" #include "../managers/input/InputManager.hpp" #include "../managers/eventLoop/EventLoopManager.hpp" #include "../managers/CursorManager.hpp" @@ -29,10 +28,8 @@ #include "../helpers/MainLoopExecutor.hpp" #include "../i18n/Engine.hpp" #include "../event/EventBus.hpp" -#include "../managers/screenshare/ScreenshareManager.hpp" #include "debug/HyprNotificationOverlay.hpp" #include "hyprerror/HyprError.hpp" -#include "macros.hpp" #include "pass/TexPassElement.hpp" #include "pass/RectPassElement.hpp" #include "pass/PreBlurElement.hpp" @@ -46,16 +43,20 @@ #include #include #include -#include -#include "ShaderLoader.hpp" -#include "Texture.hpp" #include -#include "gl/GLFramebuffer.hpp" -#include "gl/GLTexture.hpp" +#include +#include "./shaders/Shaders.hpp" using namespace Hyprutils::OS; using namespace NColorManagement; -using namespace Render; + +const std::vector ASSET_PATHS = { +#ifdef DATAROOTDIR + DATAROOTDIR, +#endif + "/usr/share", + "/usr/local/share", +}; static inline void loadGLProc(void* pProc, const char* name) { void* proc = rc(eglGetProcAddress(name)); @@ -646,7 +647,7 @@ EGLImageKHR CHyprOpenGLImpl::createEGLImage(const Aquamarine::SDMABUFAttrs& attr return image; } -void CHyprOpenGLImpl::beginSimple(PHLMONITOR pMonitor, const CRegion& damage, SP rb, SP fb) { +void CHyprOpenGLImpl::beginSimple(PHLMONITOR pMonitor, const CRegion& damage, SP rb, CFramebuffer* fb) { m_renderData.pMonitor = pMonitor; const GLenum RESETSTATUS = glGetGraphicsResetStatus(); @@ -699,7 +700,7 @@ void CHyprOpenGLImpl::beginSimple(PHLMONITOR pMonitor, const CRegion& damage, SP pushMonitorTransformEnabled(false); } -void CHyprOpenGLImpl::begin(PHLMONITOR pMonitor, const CRegion& damage_, SP fb, std::optional finalDamage) { +void CHyprOpenGLImpl::begin(PHLMONITOR pMonitor, const CRegion& damage_, CFramebuffer* fb, std::optional finalDamage) { m_renderData.pMonitor = pMonitor; const GLenum RESETSTATUS = glGetGraphicsResetStatus(); @@ -723,8 +724,7 @@ void CHyprOpenGLImpl::begin(PHLMONITOR pMonitor, const CRegion& damage_, SPm_projMatrix; - if (m_monitorRenderResources.contains(pMonitor) && - (!m_monitorRenderResources.at(pMonitor).offloadFB || m_monitorRenderResources.at(pMonitor).offloadFB->m_size != pMonitor->m_pixelSize)) + if (m_monitorRenderResources.contains(pMonitor) && m_monitorRenderResources.at(pMonitor).offloadFB.m_size != pMonitor->m_pixelSize) destroyMonitorResources(pMonitor); m_renderData.pCurrentMonData = &m_monitorRenderResources[pMonitor]; @@ -735,40 +735,24 @@ void CHyprOpenGLImpl::begin(PHLMONITOR pMonitor, const CRegion& damage_, SPm_drmFormat : pMonitor->m_output->state->state().drmFormat; // ensure a framebuffer for the monitor exists - if (!m_renderData.pCurrentMonData->offloadFB || m_renderData.pCurrentMonData->offloadFB->m_size != pMonitor->m_pixelSize || - DRM_FORMAT != m_renderData.pCurrentMonData->offloadFB->m_drmFormat) { - m_renderData.pCurrentMonData->stencilTex = g_pHyprRenderer->createStencilTexture(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y); - m_renderData.pCurrentMonData->offloadFB = g_pHyprRenderer->createFB(); - m_renderData.pCurrentMonData->mirrorFB = g_pHyprRenderer->createFB(); - m_renderData.pCurrentMonData->mirrorSwapFB = g_pHyprRenderer->createFB(); + if (m_renderData.pCurrentMonData->offloadFB.m_size != pMonitor->m_pixelSize || DRM_FORMAT != m_renderData.pCurrentMonData->offloadFB.m_drmFormat) { + m_renderData.pCurrentMonData->stencilTex->allocate(); - m_renderData.pCurrentMonData->offloadFB->addStencil(m_renderData.pCurrentMonData->stencilTex); - m_renderData.pCurrentMonData->mirrorFB->addStencil(m_renderData.pCurrentMonData->stencilTex); - m_renderData.pCurrentMonData->mirrorSwapFB->addStencil(m_renderData.pCurrentMonData->stencilTex); + m_renderData.pCurrentMonData->offloadFB.alloc(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y, DRM_FORMAT); + m_renderData.pCurrentMonData->mirrorFB.alloc(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y, DRM_FORMAT); + m_renderData.pCurrentMonData->mirrorSwapFB.alloc(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y, DRM_FORMAT); - m_renderData.pCurrentMonData->offloadFB->alloc(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y, DRM_FORMAT); - m_renderData.pCurrentMonData->mirrorFB->alloc(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y, DRM_FORMAT); - m_renderData.pCurrentMonData->mirrorSwapFB->alloc(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y, DRM_FORMAT); + m_renderData.pCurrentMonData->offloadFB.addStencil(m_renderData.pCurrentMonData->stencilTex); + m_renderData.pCurrentMonData->mirrorFB.addStencil(m_renderData.pCurrentMonData->stencilTex); + m_renderData.pCurrentMonData->mirrorSwapFB.addStencil(m_renderData.pCurrentMonData->stencilTex); } - const bool HAS_MIRROR_FB = m_renderData.pCurrentMonData->monitorMirrorFB && m_renderData.pCurrentMonData->monitorMirrorFB->isAllocated(); - const bool NEEDS_COPY_FB = needsACopyFB(m_renderData.pMonitor.lock()); - - if (HAS_MIRROR_FB && !NEEDS_COPY_FB) - m_renderData.pCurrentMonData->monitorMirrorFB->release(); - else if (!HAS_MIRROR_FB && NEEDS_COPY_FB && m_renderData.pCurrentMonData->monitorMirrorFB) - m_renderData.pCurrentMonData->monitorMirrorFB->alloc(m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y, - m_renderData.pMonitor->m_output->state->state().drmFormat); + if (m_renderData.pCurrentMonData->monitorMirrorFB.isAllocated() && m_renderData.pMonitor->m_mirrors.empty()) + m_renderData.pCurrentMonData->monitorMirrorFB.release(); m_renderData.transformDamage = true; - if (HAS_MIRROR_FB != NEEDS_COPY_FB) { - // force full damage because the mirror fb will be empty - m_renderData.damage.set({0, 0, INT32_MAX, INT32_MAX}); - m_renderData.finalDamage.set(m_renderData.damage); - } else { - m_renderData.damage.set(damage_); - m_renderData.finalDamage.set(finalDamage.value_or(damage_)); - } + m_renderData.damage.set(damage_); + m_renderData.finalDamage.set(finalDamage.value_or(damage_)); m_fakeFrame = fb; @@ -778,8 +762,8 @@ void CHyprOpenGLImpl::begin(PHLMONITOR pMonitor, const CRegion& damage_, SPoffloadFB->bind(); - m_renderData.currentFB = m_renderData.pCurrentMonData->offloadFB; + m_renderData.pCurrentMonData->offloadFB.bind(); + m_renderData.currentFB = &m_renderData.pCurrentMonData->offloadFB; m_offloadedFramebuffer = true; m_renderData.mainFB = m_renderData.currentFB; @@ -793,12 +777,6 @@ void CHyprOpenGLImpl::end() { TRACY_GPU_ZONE("RenderEnd"); - m_renderData.currentWindow.reset(); - m_renderData.surface.reset(); - m_renderData.currentLS.reset(); - m_renderData.clipBox = {}; - m_renderData.clipRegion.clear(); - // end the render, copy the data to the main framebuffer if LIKELY (m_offloadedFramebuffer) { m_renderData.damage = m_renderData.finalDamage; @@ -815,20 +793,17 @@ void CHyprOpenGLImpl::end() { m_renderData.useNearestNeighbor = true; // copy the damaged areas into the mirror buffer - // we can't use the offloadFB for mirroring / ss, as it contains artifacts from blurring - if UNLIKELY (needsACopyFB(m_renderData.pMonitor.lock()) && !m_fakeFrame) + // we can't use the offloadFB for mirroring, as it contains artifacts from blurring + if UNLIKELY (!m_renderData.pMonitor->m_mirrors.empty() && !m_fakeFrame) saveBufferForMirror(monbox); m_renderData.outFB->bind(); blend(false); - const auto PRIMITIVE_BLOCKED = - m_finalScreenShader->program() >= 1 || g_pHyprRenderer->m_crashingInProgress || m_renderData.pMonitor->m_imageDescription->value() != SImageDescription{}; - - if LIKELY (!PRIMITIVE_BLOCKED || g_pHyprRenderer->m_renderMode != RENDER_MODE_NORMAL) - renderTexturePrimitive(m_renderData.pCurrentMonData->offloadFB->getTexture(), monbox); - else // we need to use renderTexture if we do any CM whatsoever. - renderTexture(m_renderData.pCurrentMonData->offloadFB->getTexture(), monbox, {.finalMonitorCM = true}); + if LIKELY (m_finalScreenShader->program() < 1 && !g_pHyprRenderer->m_crashingInProgress) + renderTexturePrimitive(m_renderData.pCurrentMonData->offloadFB.getTexture(), monbox); + else + renderTexture(m_renderData.pCurrentMonData->offloadFB.getTexture(), monbox, {}); blend(true); @@ -838,22 +813,14 @@ void CHyprOpenGLImpl::end() { } // invalidate our render FBs to signal to the driver we don't need them anymore - if (m_renderData.pCurrentMonData->mirrorFB) { - m_renderData.pCurrentMonData->mirrorFB->bind(); - GLFB(m_renderData.pCurrentMonData->mirrorFB)->invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); - } - if (m_renderData.pCurrentMonData->mirrorSwapFB) { - m_renderData.pCurrentMonData->mirrorSwapFB->bind(); - GLFB(m_renderData.pCurrentMonData->mirrorSwapFB)->invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); - } - if (m_renderData.pCurrentMonData->offloadFB) { - m_renderData.pCurrentMonData->offloadFB->bind(); - GLFB(m_renderData.pCurrentMonData->offloadFB)->invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); - } - if (m_renderData.pCurrentMonData->offMainFB) { - m_renderData.pCurrentMonData->offMainFB->bind(); - GLFB(m_renderData.pCurrentMonData->offMainFB)->invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); - } + m_renderData.pCurrentMonData->mirrorFB.bind(); + m_renderData.pCurrentMonData->mirrorFB.invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); + m_renderData.pCurrentMonData->mirrorSwapFB.bind(); + m_renderData.pCurrentMonData->mirrorSwapFB.invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); + m_renderData.pCurrentMonData->offloadFB.bind(); + m_renderData.pCurrentMonData->offloadFB.invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); + m_renderData.pCurrentMonData->offMainFB.bind(); + m_renderData.pCurrentMonData->offMainFB.invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); // reset our data m_renderData.pMonitor.reset(); @@ -868,8 +835,8 @@ void CHyprOpenGLImpl::end() { // if we dropped to offMain, release it now. // if there is a plugin constantly using it, this might be a bit slow, // but I haven't seen a single plugin yet use these, so it's better to drop a bit of vram. - if UNLIKELY (m_renderData.pCurrentMonData->offMainFB && m_renderData.pCurrentMonData->offMainFB->isAllocated()) - m_renderData.pCurrentMonData->offMainFB->release(); + if UNLIKELY (m_renderData.pCurrentMonData->offMainFB.isAllocated()) + m_renderData.pCurrentMonData->offMainFB.release(); static const auto GLDEBUG = CConfigValue("debug:gl_debugging"); @@ -882,39 +849,129 @@ void CHyprOpenGLImpl::end() { } } -bool CHyprOpenGLImpl::needsACopyFB(PHLMONITOR mon) { - return !mon->m_mirrors.empty() || Screenshare::mgr()->isOutputBeingSSd(mon); -} - void CHyprOpenGLImpl::setDamage(const CRegion& damage_, std::optional finalDamage) { m_renderData.damage.set(damage_); m_renderData.finalDamage.set(finalDamage.value_or(damage_)); } -static const std::vector SHADER_INCLUDES = { - "defines.h", "constants.h", "cm_helpers.glsl", "rounding.glsl", "CM.glsl", "tonemap.glsl", "gain.glsl", - "border.glsl", "shadow.glsl", "blurprepare.glsl", "blur1.glsl", "blur2.glsl", "blurFinish.glsl", -}; +// TODO notify user if bundled shader is newer than ~/.config override +static std::string loadShader(const std::string& filename) { + const auto home = Hyprutils::Path::getHome(); + if (home.has_value()) { + const auto src = NFsUtils::readFileAsString(home.value() + "/hypr/shaders/" + filename); + if (src.has_value()) + return src.value(); + } + for (auto& e : ASSET_PATHS) { + const auto src = NFsUtils::readFileAsString(std::string{e} + "/hypr/shaders/" + filename); + if (src.has_value()) + return src.value(); + } + if (SHADERS.contains(filename)) + return SHADERS.at(filename); + throw std::runtime_error(std::format("Couldn't load shader {}", filename)); +} -// order matters, see ePreparedFragmentShader -const std::array FRAG_SHADERS = { - "quad.frag", "passthru.frag", "rgbamatte.frag", "ext.frag", "blur1.frag", "blur2.frag", - "blurprepare.frag", "blurfinish.frag", "shadow.frag", "surface.frag", "border.frag", "glitch.frag", -}; +static void loadShaderInclude(const std::string& filename, std::map& includes) { + includes.insert({filename, loadShader(filename)}); +} -bool CHyprOpenGLImpl::initShaders(const std::string& path) { - auto shaders = makeShared(); - static const auto PCM = CConfigValue("render:cm_enabled"); +static void processShaderIncludes(std::string& source, const std::map& includes) { + for (auto it = includes.begin(); it != includes.end(); ++it) { + Hyprutils::String::replaceInString(source, "#include \"" + it->first + "\"", it->second); + } +} + +static const uint8_t MAX_INCLUDE_DEPTH = 3; + +static std::string processShader(const std::string& filename, const std::map& includes, const uint8_t includeDepth = 1) { + auto source = loadShader(filename); + for (auto i = 0; i < includeDepth; i++) { + processShaderIncludes(source, includes); + } + return source; +} + +bool CHyprOpenGLImpl::initShaders() { + auto shaders = makeShared(); + std::map includes; + const bool isDynamic = m_shadersInitialized; + static const auto PCM = CConfigValue("render:cm_enabled"); try { - auto shaderLoader = makeUnique(SHADER_INCLUDES, FRAG_SHADERS, path); + loadShaderInclude("get_rgb_pixel.glsl", includes); + loadShaderInclude("get_rgba_pixel.glsl", includes); + loadShaderInclude("get_rgbx_pixel.glsl", includes); + loadShaderInclude("discard.glsl", includes); + loadShaderInclude("do_discard.glsl", includes); + loadShaderInclude("tint.glsl", includes); + loadShaderInclude("do_tint.glsl", includes); + loadShaderInclude("rounding.glsl", includes); + loadShaderInclude("do_rounding.glsl", includes); + loadShaderInclude("surface_CM.glsl", includes); + loadShaderInclude("CM.glsl", includes); + loadShaderInclude("do_CM.glsl", includes); + loadShaderInclude("tonemap.glsl", includes); + loadShaderInclude("do_tonemap.glsl", includes); + loadShaderInclude("sdr_mod.glsl", includes); + loadShaderInclude("do_sdr_mod.glsl", includes); + loadShaderInclude("primaries_xyz.glsl", includes); + loadShaderInclude("primaries_xyz_uniform.glsl", includes); + loadShaderInclude("primaries_xyz_const.glsl", includes); + loadShaderInclude("gain.glsl", includes); + loadShaderInclude("border.glsl", includes); - shaders->TEXVERTSRC = shaderLoader->process("tex300.vert"); - shaders->TEXVERTSRC320 = shaderLoader->process("tex320.vert"); + shaders->TEXVERTSRC = processShader("tex300.vert", includes); + shaders->TEXVERTSRC320 = processShader("tex320.vert", includes); - m_cmSupported = *PCM; + if (!*PCM) + m_cmSupported = false; + else { + std::vector CM_SHADERS = {{ + {SH_FRAG_CM_BLURPREPARE, "CMblurprepare.frag"}, + {SH_FRAG_CM_BORDER1, "CMborder.frag"}, + }}; - g_pShaderLoader = std::move(shaderLoader); + bool success = false; + for (const auto& desc : CM_SHADERS) { + const auto fragSrc = processShader(desc.file, includes, MAX_INCLUDE_DEPTH); + + if (!(success = shaders->frag[desc.id]->createProgram(shaders->TEXVERTSRC, fragSrc, true, true))) + break; + } + + if (m_shadersInitialized && m_cmSupported && !success) + g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_CM_RELOAD_FAILED), CHyprColor{}, 15000, ICON_WARNING); + + m_cmSupported = success; + + if (!m_cmSupported) + Log::logger->log( + Log::ERR, + "WARNING: CM Shader failed compiling, color management will not work. It's likely because your GPU is an old piece of garbage, don't file bug reports " + "about this!"); + } + + std::vector FRAG_SHADERS = {{ + {SH_FRAG_QUAD, "quad.frag"}, + {SH_FRAG_PASSTHRURGBA, "passthru.frag"}, + {SH_FRAG_MATTE, "rgbamatte.frag"}, + {SH_FRAG_GLITCH, "glitch.frag"}, + {SH_FRAG_EXT, "ext.frag"}, + {SH_FRAG_BLUR1, "blur1.frag"}, + {SH_FRAG_BLUR2, "blur2.frag"}, + {SH_FRAG_BLURPREPARE, "blurprepare.frag"}, + {SH_FRAG_BLURFINISH, "blurfinish.frag"}, + {SH_FRAG_SHADOW, "shadow.frag"}, + {SH_FRAG_BORDER1, "border.frag"}, + }}; + + for (const auto& desc : FRAG_SHADERS) { + const auto fragSrc = processShader(desc.file, includes, MAX_INCLUDE_DEPTH); + + if (!shaders->frag[desc.id]->createProgram(shaders->TEXVERTSRC, fragSrc, isDynamic)) + return false; + } } catch (const std::exception& e) { if (!m_shadersInitialized) @@ -925,6 +982,7 @@ bool CHyprOpenGLImpl::initShaders(const std::string& path) { } m_shaders = shaders; + m_includes = includes; m_shadersInitialized = true; Log::logger->log(Log::DEBUG, "Shaders initialized successfully."); @@ -996,7 +1054,7 @@ void CHyprOpenGLImpl::clear(const CHyprColor& color) { TRACY_GPU_ZONE("RenderClear"); - GLCALL(glClearColor(color.r, color.g, color.b, color.a)); + glClearColor(color.r, color.g, color.b, color.a); if (!m_renderData.damage.empty()) { m_renderData.damage.forEachRect([this](const auto& RECT) { @@ -1009,7 +1067,7 @@ void CHyprOpenGLImpl::clear(const CHyprColor& color) { void CHyprOpenGLImpl::blend(bool enabled) { if (enabled) { setCapStatus(GL_BLEND, true); - GLCALL(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); // everything is premultiplied + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // everything is premultiplied } else setCapStatus(GL_BLEND, false); @@ -1028,7 +1086,7 @@ void CHyprOpenGLImpl::scissor(const CBox& originalBox, bool transform) { box.transform(TR, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y); if (box != m_lastScissorBox) { - GLCALL(glScissor(box.x, box.y, box.width, box.height)); + glScissor(box.x, box.y, box.width, box.height); m_lastScissorBox = box; } @@ -1037,7 +1095,7 @@ void CHyprOpenGLImpl::scissor(const CBox& originalBox, bool transform) { } if (originalBox != m_lastScissorBox) { - GLCALL(glScissor(originalBox.x, originalBox.y, originalBox.width, originalBox.height)); + glScissor(originalBox.x, originalBox.y, originalBox.width, originalBox.height); m_lastScissorBox = originalBox; } @@ -1079,7 +1137,7 @@ void CHyprOpenGLImpl::renderRectWithBlurInternal(const CBox& box, const CHyprCol CRegion damage{m_renderData.damage}; damage.intersect(box); - auto POUTFB = data.xray ? m_renderData.pCurrentMonData->blurFB : blurMainFramebufferWithDamage(data.blurA, &damage); + CFramebuffer* POUTFB = data.xray ? &m_renderData.pCurrentMonData->blurFB : blurMainFramebufferWithDamage(data.blurA, &damage); m_renderData.currentFB->bind(); @@ -1108,7 +1166,7 @@ void CHyprOpenGLImpl::renderRectWithDamageInternal(const CBox& box, const CHyprC newBox, Math::wlTransformToHyprutils(Math::invertTransform(!m_monitorTransformEnabled ? WL_OUTPUT_TRANSFORM_NORMAL : m_renderData.pMonitor->m_transform)), newBox.rot); Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); - auto shader = useShader(getShaderVariant(SH_FRAG_QUAD, data.round > 0 ? SH_FEAT_ROUNDING : 0)); + auto shader = useShader(m_shaders->frag[SH_FRAG_QUAD]); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); // premultiply the color as well as we don't work with straight alpha @@ -1150,7 +1208,7 @@ void CHyprOpenGLImpl::renderRectWithDamageInternal(const CBox& box, const CHyprC scissor(nullptr); } -void CHyprOpenGLImpl::renderTexture(SP tex, const CBox& box, STextureRenderData data) { +void CHyprOpenGLImpl::renderTexture(SP tex, const CBox& box, STextureRenderData data) { RASSERT(m_renderData.pMonitor, "Tried to render texture without begin()!"); if (!data.damage) { @@ -1188,81 +1246,228 @@ static bool isHDR2SDR(const NColorManagement::SImageDescription& imageDescriptio void CHyprOpenGLImpl::passCMUniforms(WP shader, const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, bool modifySDR, float sdrMinLuminance, int sdrMaxLuminance) { - const auto settings = g_pHyprRenderer->getCMSettings(imageDescription, targetImageDescription, m_renderData.surface.valid() ? m_renderData.surface.lock() : nullptr, modifySDR, - sdrMinLuminance, sdrMaxLuminance); + const auto sdrEOTF = NTransferFunction::fromConfig(); - shader->setUniformInt(SHADER_SOURCE_TF, settings.sourceTF); - shader->setUniformInt(SHADER_TARGET_TF, settings.targetTF); - shader->setUniformFloat2(SHADER_SRC_TF_RANGE, settings.srcTFRange.min, settings.srcTFRange.max); - shader->setUniformFloat2(SHADER_DST_TF_RANGE, settings.dstTFRange.min, settings.dstTFRange.max); - shader->setUniformFloat(SHADER_SRC_REF_LUMINANCE, settings.srcRefLuminance); - shader->setUniformFloat(SHADER_DST_REF_LUMINANCE, settings.dstRefLuminance); - shader->setUniformFloat(SHADER_MAX_LUMINANCE, settings.maxLuminance); - shader->setUniformFloat(SHADER_DST_MAX_LUMINANCE, settings.dstMaxLuminance); - shader->setUniformFloat(SHADER_SDR_SATURATION, settings.sdrSaturation); - shader->setUniformFloat(SHADER_SDR_BRIGHTNESS, settings.sdrBrightnessMultiplier); + if (m_renderData.surface.valid()) { + if (m_renderData.surface->m_colorManagement.valid()) { + if (sdrEOTF == NTransferFunction::TF_FORCED_GAMMA22 && imageDescription->value().transferFunction == NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_SRGB) + shader->setUniformInt(SHADER_SOURCE_TF, NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_GAMMA22); + else + shader->setUniformInt(SHADER_SOURCE_TF, imageDescription->value().transferFunction); + } else if (sdrEOTF == NTransferFunction::TF_SRGB) + shader->setUniformInt(SHADER_SOURCE_TF, NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_SRGB); + else if (sdrEOTF == NTransferFunction::TF_GAMMA22 || sdrEOTF == NTransferFunction::TF_FORCED_GAMMA22) + shader->setUniformInt(SHADER_SOURCE_TF, NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_GAMMA22); + else + shader->setUniformInt(SHADER_SOURCE_TF, imageDescription->value().transferFunction); + } else + shader->setUniformInt(SHADER_SOURCE_TF, imageDescription->value().transferFunction); - if (!targetImageDescription->value().icc.present) { - const auto cacheKey = std::make_pair(imageDescription->id(), targetImageDescription->id()); - if (!primariesConversionCache.contains(cacheKey)) { - const auto& mat = settings.convertMatrix; - const std::array glConvertMatrix = { - mat[0][0], mat[1][0], mat[2][0], // - mat[0][1], mat[1][1], mat[2][1], // - mat[0][2], mat[1][2], mat[2][2], // - }; - primariesConversionCache.insert(std::make_pair(cacheKey, glConvertMatrix)); - } - shader->setUniformMatrix3fv(SHADER_CONVERT_MATRIX, 1, false, primariesConversionCache[cacheKey]); + shader->setUniformInt(SHADER_TARGET_TF, targetImageDescription->value().transferFunction); - const auto mat = settings.dstPrimaries2XYZ; - const std::array glTargetPrimariesXYZ = { + const auto targetPrimaries = targetImageDescription->getPrimaries(); + const auto mat = targetPrimaries->value().toXYZ().mat(); + const std::array glTargetPrimariesXYZ = { + mat[0][0], mat[1][0], mat[2][0], // + mat[0][1], mat[1][1], mat[2][1], // + mat[0][2], mat[1][2], mat[2][2], // + }; + shader->setUniformMatrix3fv(SHADER_TARGET_PRIMARIES_XYZ, 1, false, glTargetPrimariesXYZ); + + const bool needsSDRmod = modifySDR && isSDR2HDR(imageDescription->value(), targetImageDescription->value()); + const bool needsHDRmod = !needsSDRmod && isHDR2SDR(imageDescription->value(), targetImageDescription->value()); + + shader->setUniformFloat2(SHADER_SRC_TF_RANGE, imageDescription->value().getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1), + imageDescription->value().getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1)); + shader->setUniformFloat2(SHADER_DST_TF_RANGE, targetImageDescription->value().getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1), + targetImageDescription->value().getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1)); + + shader->setUniformFloat(SHADER_SRC_REF_LUMINANCE, imageDescription->value().luminances.reference); + shader->setUniformFloat(SHADER_DST_REF_LUMINANCE, targetImageDescription->value().luminances.reference); + + const float maxLuminance = needsHDRmod ? + imageDescription->value().getTFMaxLuminance(-1) : + (imageDescription->value().luminances.max > 0 ? imageDescription->value().luminances.max : imageDescription->value().luminances.reference); + shader->setUniformFloat(SHADER_MAX_LUMINANCE, maxLuminance * targetImageDescription->value().luminances.reference / imageDescription->value().luminances.reference); + shader->setUniformFloat(SHADER_DST_MAX_LUMINANCE, targetImageDescription->value().luminances.max > 0 ? targetImageDescription->value().luminances.max : 10000); + shader->setUniformFloat(SHADER_SDR_SATURATION, needsSDRmod && m_renderData.pMonitor->m_sdrSaturation > 0 ? m_renderData.pMonitor->m_sdrSaturation : 1.0f); + shader->setUniformFloat(SHADER_SDR_BRIGHTNESS, needsSDRmod && m_renderData.pMonitor->m_sdrBrightness > 0 ? m_renderData.pMonitor->m_sdrBrightness : 1.0f); + const auto cacheKey = std::make_pair(imageDescription->id(), targetImageDescription->id()); + if (!primariesConversionCache.contains(cacheKey)) { + auto conversion = imageDescription->getPrimaries()->convertMatrix(targetImageDescription->getPrimaries()); + const auto mat = conversion.mat(); + const std::array glConvertMatrix = { mat[0][0], mat[1][0], mat[2][0], // mat[0][1], mat[1][1], mat[2][1], // mat[0][2], mat[1][2], mat[2][2], // }; - shader->setUniformMatrix3fv(SHADER_TARGET_PRIMARIES_XYZ, 1, false, glTargetPrimariesXYZ); - } else { - // TODO: this sucks - GLCALL(glActiveTexture(GL_TEXTURE8)); - targetImageDescription->value().icc.lutTexture->bind(); - - shader->setUniformInt(SHADER_LUT_3D, 8); - shader->setUniformFloat(SHADER_LUT_SIZE, targetImageDescription->value().icc.lutSize); - - GLCALL(glActiveTexture(GL_TEXTURE0)); + primariesConversionCache.insert(std::make_pair(cacheKey, glConvertMatrix)); } + shader->setUniformMatrix3fv(SHADER_CONVERT_MATRIX, 1, false, primariesConversionCache[cacheKey]); } void CHyprOpenGLImpl::passCMUniforms(WP shader, const PImageDescription imageDescription) { - passCMUniforms(shader, imageDescription, g_pHyprRenderer->workBufferImageDescription(), true, m_renderData.pMonitor->m_sdrMinLuminance, - m_renderData.pMonitor->m_sdrMaxLuminance); + passCMUniforms(shader, imageDescription, m_renderData.pMonitor->m_imageDescription, true, m_renderData.pMonitor->m_sdrMinLuminance, m_renderData.pMonitor->m_sdrMaxLuminance); } -WP CHyprOpenGLImpl::renderToOutputInternal() { +void CHyprOpenGLImpl::renderTextureInternal(SP tex, const CBox& box, const STextureRenderData& data) { + RASSERT(m_renderData.pMonitor, "Tried to render texture without begin()!"); + RASSERT((tex->m_texID > 0), "Attempted to draw nullptr texture!"); + + TRACY_GPU_ZONE("RenderTextureInternalWithDamage"); + + float alpha = std::clamp(data.a, 0.f, 1.f); + + if (data.damage->empty()) + return; + + CBox newBox = box; + m_renderData.renderModif.applyToBox(newBox); + static const auto PDT = CConfigValue("debug:damage_tracking"); + static const auto PPASS = CConfigValue("render:cm_fs_passthrough"); + static const auto PENABLECM = CConfigValue("render:cm_enabled"); static const auto PCURSORTIMEOUT = CConfigValue("cursor:inactive_timeout"); - WP shader = - g_pHyprRenderer->m_crashingInProgress ? getShaderVariant(SH_FRAG_GLITCH) : (m_finalScreenShader->program() ? m_finalScreenShader : getShaderVariant(SH_FRAG_PASSTHRURGBA)); + // get the needed transform for this texture + const auto MONITOR_INVERTED = Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)); + Hyprutils::Math::eTransform TRANSFORM = tex->m_transform; + + if (m_monitorTransformEnabled) + TRANSFORM = Math::composeTransform(MONITOR_INVERTED, TRANSFORM); + + Mat3x3 matrix = m_renderData.monitorProjection.projectBox(newBox, TRANSFORM, newBox.rot); + Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); + + WP shader; + + bool usingFinalShader = false; + + const bool CRASHING = m_applyFinalShader && g_pHyprRenderer->m_crashingInProgress; + + uint8_t shaderFeatures = 0; + + if (CRASHING) { + shader = m_shaders->frag[SH_FRAG_GLITCH]; + usingFinalShader = true; + } else if (m_applyFinalShader && m_finalScreenShader->program()) { + shader = m_finalScreenShader; + usingFinalShader = true; + } else { + if (m_applyFinalShader) { + shader = m_shaders->frag[SH_FRAG_PASSTHRURGBA]; + usingFinalShader = true; + } else { + switch (tex->m_type) { + case TEXTURE_RGBA: shaderFeatures |= SH_FEAT_RGBA; break; + case TEXTURE_RGBX: shaderFeatures &= ~SH_FEAT_RGBA; break; + + case TEXTURE_EXTERNAL: shader = m_shaders->frag[SH_FRAG_EXT]; break; // might be unused + default: RASSERT(false, "tex->m_iTarget unsupported!"); + } + } + } + + if (m_renderData.currentWindow && m_renderData.currentWindow->m_ruleApplicator->RGBX().valueOrDefault()) + shaderFeatures &= ~SH_FEAT_RGBA; + + glActiveTexture(GL_TEXTURE0); + tex->bind(); + + tex->setTexParameter(GL_TEXTURE_WRAP_S, data.wrapX); + tex->setTexParameter(GL_TEXTURE_WRAP_T, data.wrapY); + + if (m_renderData.useNearestNeighbor) { + tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST); + tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } else { + tex->setTexParameter(GL_TEXTURE_MAG_FILTER, tex->magFilter); + tex->setTexParameter(GL_TEXTURE_MIN_FILTER, tex->minFilter); + } + + const bool isHDRSurface = m_renderData.surface.valid() && m_renderData.surface->m_colorManagement.valid() ? m_renderData.surface->m_colorManagement->isHDR() : false; + const bool canPassHDRSurface = isHDRSurface && !m_renderData.surface->m_colorManagement->isWindowsScRGB(); // windows scRGB requires CM shader + + const auto imageDescription = m_renderData.surface.valid() && m_renderData.surface->m_colorManagement.valid() ? + CImageDescription::from(m_renderData.surface->m_colorManagement->imageDescription()) : + (data.cmBackToSRGB ? data.cmBackToSRGBSource->m_imageDescription : DEFAULT_IMAGE_DESCRIPTION); + + const auto sdrEOTF = NTransferFunction::fromConfig(); + auto chosenSdrEotf = sdrEOTF != NTransferFunction::TF_SRGB ? NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22 : NColorManagement::CM_TRANSFER_FUNCTION_SRGB; + const auto targetImageDescription = + data.cmBackToSRGB ? CImageDescription::from(NColorManagement::SImageDescription{.transferFunction = chosenSdrEotf}) : m_renderData.pMonitor->m_imageDescription; + + const bool skipCM = !*PENABLECM || !m_cmSupported /* CM unsupported or disabled */ + || m_renderData.pMonitor->doesNoShaderCM() /* no shader needed */ + || (imageDescription->id() == targetImageDescription->id() && !data.cmBackToSRGB) /* Source and target have the same image description */ + || (((*PPASS && canPassHDRSurface) || + (*PPASS == 1 && !isHDRSurface && m_renderData.pMonitor->m_cmType != NCMType::CM_HDR && m_renderData.pMonitor->m_cmType != NCMType::CM_HDR_EDID)) && + m_renderData.pMonitor->inFullscreenMode()) /* Fullscreen window with pass cm enabled */; + + if (data.discardActive) + shaderFeatures |= SH_FEAT_DISCARD; + + if (!usingFinalShader) { + if (data.allowDim && m_renderData.currentWindow && (m_renderData.currentWindow->m_notRespondingTint->value() > 0 || m_renderData.currentWindow->m_dimPercent->value() > 0)) + shaderFeatures |= SH_FEAT_TINT; + + if (data.round > 0) + shaderFeatures |= SH_FEAT_ROUNDING; + + if (!skipCM) { + shaderFeatures |= SH_FEAT_CM; + + const bool needsSDRmod = isSDR2HDR(imageDescription->value(), targetImageDescription->value()); + const bool needsHDRmod = !needsSDRmod && isHDR2SDR(imageDescription->value(), targetImageDescription->value()); + const float maxLuminance = needsHDRmod ? + imageDescription->value().getTFMaxLuminance(-1) : + (imageDescription->value().luminances.max > 0 ? imageDescription->value().luminances.max : imageDescription->value().luminances.reference); + const auto dstMaxLuminance = targetImageDescription->value().luminances.max > 0 ? targetImageDescription->value().luminances.max : 10000; + + if (maxLuminance >= dstMaxLuminance * 1.01) + shaderFeatures |= SH_FEAT_TONEMAP; + + if (!data.cmBackToSRGB && + (imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_SRGB || imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_GAMMA22) && + targetImageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ && + ((m_renderData.pMonitor->m_sdrSaturation > 0 && m_renderData.pMonitor->m_sdrSaturation != 1.0f) || + (m_renderData.pMonitor->m_sdrBrightness > 0 && m_renderData.pMonitor->m_sdrBrightness != 1.0f))) + shaderFeatures |= SH_FEAT_SDR_MOD; + } + } + + if (!shader) + shader = getSurfaceShader(shaderFeatures); shader = useShader(shader); - if (*PDT == 0 || g_pHyprRenderer->m_crashingInProgress) + if (!skipCM && !usingFinalShader) { + if (data.cmBackToSRGB) + passCMUniforms(shader, imageDescription, targetImageDescription, true, -1, -1); + else + passCMUniforms(shader, imageDescription); + } + + shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); + shader->setUniformInt(SHADER_TEX, 0); + + if ((usingFinalShader && *PDT == 0) || CRASHING) shader->setUniformFloat(SHADER_TIME, m_globalTimer.getSeconds() - shader->getInitialTime()); - else + else if (usingFinalShader) shader->setUniformFloat(SHADER_TIME, 0.f); - shader->setUniformInt(SHADER_WL_OUTPUT, m_renderData.pMonitor->m_id); - shader->setUniformFloat2(SHADER_FULL_SIZE, m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y); - shader->setUniformFloat(SHADER_POINTER_INACTIVE_TIMEOUT, *PCURSORTIMEOUT); - shader->setUniformInt(SHADER_POINTER_HIDDEN, g_pHyprRenderer->m_cursorHiddenByCondition); - shader->setUniformInt(SHADER_POINTER_KILLING, g_pInputManager->getClickMode() == CLICKMODE_KILL); - shader->setUniformInt(SHADER_POINTER_SHAPE, g_pHyprRenderer->m_lastCursorData.shape); - shader->setUniformInt(SHADER_POINTER_SHAPE_PREVIOUS, g_pHyprRenderer->m_lastCursorData.shapePrevious); - shader->setUniformFloat(SHADER_POINTER_SIZE, g_pCursorManager->getScaledSize()); + if (usingFinalShader) { + shader->setUniformInt(SHADER_WL_OUTPUT, m_renderData.pMonitor->m_id); + shader->setUniformFloat2(SHADER_FULL_SIZE, m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y); + shader->setUniformFloat(SHADER_POINTER_INACTIVE_TIMEOUT, *PCURSORTIMEOUT); + shader->setUniformInt(SHADER_POINTER_HIDDEN, g_pHyprRenderer->m_cursorHiddenByCondition); + shader->setUniformInt(SHADER_POINTER_KILLING, g_pInputManager->getClickMode() == CLICKMODE_KILL); + shader->setUniformInt(SHADER_POINTER_SHAPE, g_pHyprRenderer->m_lastCursorData.shape); + shader->setUniformInt(SHADER_POINTER_SHAPE_PREVIOUS, g_pHyprRenderer->m_lastCursorData.shapePrevious); + shader->setUniformFloat(SHADER_POINTER_SIZE, g_pCursorManager->getScaledSize()); + } - if (*PDT == 0) { + if (usingFinalShader && *PDT == 0) { PHLMONITORREF pMonitor = m_renderData.pMonitor; Vector2D p = ((g_pInputManager->getMouseCoordsInternal() - pMonitor->m_position) * pMonitor->m_scale); p = p.transform(Math::wlTransformToHyprutils(pMonitor->m_transform), pMonitor->m_pixelSize); @@ -1288,7 +1493,7 @@ WP CHyprOpenGLImpl::renderToOutputInternal() { shader->setUniformFloat(SHADER_POINTER_LAST_ACTIVE, g_pInputManager->m_lastCursorMovement.getSeconds()); shader->setUniformFloat(SHADER_POINTER_SWITCH_TIME, g_pHyprRenderer->m_lastCursorData.switchedTimer.getSeconds()); - } else { + } else if (usingFinalShader) { shader->setUniformFloat2(SHADER_POINTER, 0.f, 0.f); static const std::vector pressedPosDefault(POINTER_PRESSED_HISTORY_LENGTH * 2uz, 0.f); @@ -1302,223 +1507,55 @@ WP CHyprOpenGLImpl::renderToOutputInternal() { shader->setUniformFloat(SHADER_POINTER_SWITCH_TIME, 0.f); } - if (g_pHyprRenderer->m_crashingInProgress) { + if (CRASHING) { shader->setUniformFloat(SHADER_DISTORT, g_pHyprRenderer->m_crashingDistort); shader->setUniformFloat2(SHADER_FULL_SIZE, m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y); } - return shader; -} + if (!usingFinalShader) { + shader->setUniformFloat(SHADER_ALPHA, alpha); -WP CHyprOpenGLImpl::renderToFBInternal(const STextureRenderData& data, eTextureType texType, const CBox& newBox) { - static const auto PPASS = CConfigValue("render:cm_fs_passthrough"); - static const auto PENABLECM = CConfigValue("render:cm_enabled"); - static auto PBLEND = CConfigValue("render:use_shader_blur_blend"); - - float alpha = std::clamp(data.a, 0.f, 1.f); - - WP shader; - ShaderFeatureFlags shaderFeatures = 0; - - switch (texType) { - case TEXTURE_RGBA: shaderFeatures |= SH_FEAT_RGBA; break; - case TEXTURE_RGBX: shaderFeatures &= ~SH_FEAT_RGBA; break; - - // TODO set correct features - case TEXTURE_EXTERNAL: shader = getShaderVariant(SH_FRAG_EXT, SH_FEAT_ROUNDING | SH_FEAT_DISCARD | SH_FEAT_TINT); break; // might be unused - default: RASSERT(false, "tex->m_iTarget unsupported!"); - } - - if (data.finalMonitorCM || (m_renderData.currentWindow && m_renderData.currentWindow->m_ruleApplicator->RGBX().valueOrDefault())) - shaderFeatures &= ~SH_FEAT_RGBA; - - const auto surface = m_renderData.surface; - const bool isHDRSurface = surface.valid() && surface->m_colorManagement.valid() ? surface->m_colorManagement->isHDR() : false; - const bool canPassHDRSurface = isHDRSurface && !surface->m_colorManagement->isWindowsScRGB(); // windows scRGB requires CM shader - - const auto WORK_BUFFER_IMAGE_DESCRIPTION = g_pHyprRenderer->workBufferImageDescription(); - - // chosenSdrEotf contains the valid eotf for this display - - const auto SOURCE_IMAGE_DESCRIPTION = [&] { - // if valid CM surface, use that as a source - if (m_renderData.surface.valid() && m_renderData.surface->m_colorManagement.valid()) - return CImageDescription::from(m_renderData.surface->m_colorManagement->imageDescription()); - - // otherwise, if we are CM'ing back into source, use chosen, because that's what our work buffer is in - // the same applies to the final monitor CM - if (data.cmBackToSRGB || data.finalMonitorCM) // NOLINTNEXTLINE - return WORK_BUFFER_IMAGE_DESCRIPTION; - - // otherwise, default - return DEFAULT_IMAGE_DESCRIPTION; - }(); - - const auto TARGET_IMAGE_DESCRIPTION = [&] { - // if we are CM'ing back, use default sRGB - if (data.cmBackToSRGB) - return DEFAULT_IMAGE_DESCRIPTION; - - // for final CM, use the target description - if (data.finalMonitorCM) - return m_renderData.pMonitor->m_imageDescription; - // otherwise, use chosen, we're drawing into the work buffer - // NOLINTNEXTLINE - return WORK_BUFFER_IMAGE_DESCRIPTION; - }(); - - if (data.blur && *PBLEND && data.blurredBG) - shaderFeatures |= SH_FEAT_BLUR; - - if (data.discardActive) - shaderFeatures |= SH_FEAT_DISCARD; - - const bool CANT_CHECK_CM_EQUALITY = data.cmBackToSRGB || data.finalMonitorCM || (!m_renderData.surface || !m_renderData.surface->m_colorManagement); - - const bool skipCM = !*PENABLECM || !m_cmSupported /* CM unsupported or disabled */ - || m_renderData.pMonitor->doesNoShaderCM() /* no shader needed */ - || (SOURCE_IMAGE_DESCRIPTION->id() == TARGET_IMAGE_DESCRIPTION->id() && !CANT_CHECK_CM_EQUALITY) /* Source and target have the same image description */ - || (((*PPASS && canPassHDRSurface) || - (*PPASS == 1 && !isHDRSurface && m_renderData.pMonitor->m_cmType != NCMType::CM_HDR && m_renderData.pMonitor->m_cmType != NCMType::CM_HDR_EDID)) && - m_renderData.pMonitor->inFullscreenMode()) /* Fullscreen window with pass cm enabled */; - - if (data.allowDim && m_renderData.currentWindow && (m_renderData.currentWindow->m_notRespondingTint->value() > 0 || m_renderData.currentWindow->m_dimPercent->value() > 0)) - shaderFeatures |= SH_FEAT_TINT; - - if (data.round > 0) - shaderFeatures |= SH_FEAT_ROUNDING; - - if (!skipCM) { - shaderFeatures |= SH_FEAT_CM; - - if (TARGET_IMAGE_DESCRIPTION->value().icc.present) - shaderFeatures |= SH_FEAT_ICC; - else { - const bool needsSDRmod = isSDR2HDR(SOURCE_IMAGE_DESCRIPTION->value(), TARGET_IMAGE_DESCRIPTION->value()); - const bool needsHDRmod = !needsSDRmod && isHDR2SDR(SOURCE_IMAGE_DESCRIPTION->value(), TARGET_IMAGE_DESCRIPTION->value()); - const float maxLuminance = needsHDRmod ? - SOURCE_IMAGE_DESCRIPTION->value().getTFMaxLuminance(-1) : - (SOURCE_IMAGE_DESCRIPTION->value().luminances.max > 0 ? SOURCE_IMAGE_DESCRIPTION->value().luminances.max : SOURCE_IMAGE_DESCRIPTION->value().luminances.reference); - const auto dstMaxLuminance = TARGET_IMAGE_DESCRIPTION->value().luminances.max > 0 ? TARGET_IMAGE_DESCRIPTION->value().luminances.max : 10000; - - if (maxLuminance >= dstMaxLuminance * 1.01) - shaderFeatures |= SH_FEAT_TONEMAP; - - if (data.finalMonitorCM && - (SOURCE_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_SRGB || - SOURCE_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_GAMMA22) && - TARGET_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ && - ((m_renderData.pMonitor->m_sdrSaturation > 0 && m_renderData.pMonitor->m_sdrSaturation != 1.0f) || - (m_renderData.pMonitor->m_sdrBrightness > 0 && m_renderData.pMonitor->m_sdrBrightness != 1.0f))) - shaderFeatures |= SH_FEAT_SDR_MOD; + if (data.discardActive) { + shader->setUniformInt(SHADER_DISCARD_OPAQUE, !!(m_renderData.discardMode & DISCARD_OPAQUE)); + shader->setUniformInt(SHADER_DISCARD_ALPHA, !!(m_renderData.discardMode & DISCARD_ALPHA)); + shader->setUniformFloat(SHADER_DISCARD_ALPHA_VALUE, m_renderData.discardOpacity); + } else { + shader->setUniformInt(SHADER_DISCARD_OPAQUE, 0); + shader->setUniformInt(SHADER_DISCARD_ALPHA, 0); } } - if (!shader) - shader = getShaderVariant(SH_FRAG_SURFACE, shaderFeatures); - shader = useShader(shader); - - if (!skipCM) { - if (data.finalMonitorCM || data.cmBackToSRGB) - passCMUniforms(shader, SOURCE_IMAGE_DESCRIPTION, TARGET_IMAGE_DESCRIPTION, true, m_renderData.pMonitor->m_sdrMinLuminance, m_renderData.pMonitor->m_sdrMaxLuminance); - else - passCMUniforms(shader, SOURCE_IMAGE_DESCRIPTION); - } - - shader->setUniformFloat(SHADER_ALPHA, alpha); - - if (shaderFeatures & SH_FEAT_BLUR) { - shader->setUniformInt(SHADER_BLURRED_BG, 1); - // shader->setUniformFloat2(SHADER_UV_OFFSET, 0, 0); - shader->setUniformFloat2(SHADER_UV_OFFSET, newBox.x / m_renderData.pMonitor->m_transformedSize.x, newBox.y / m_renderData.pMonitor->m_transformedSize.y); - shader->setUniformFloat2(SHADER_UV_SIZE, newBox.width / m_renderData.pMonitor->m_transformedSize.x, newBox.height / m_renderData.pMonitor->m_transformedSize.y); - - glActiveTexture(GL_TEXTURE0 + 1); - data.blurredBG->bind(); - } - - if (data.discardActive) { - shader->setUniformInt(SHADER_DISCARD_OPAQUE, !!(m_renderData.discardMode & DISCARD_OPAQUE)); - shader->setUniformInt(SHADER_DISCARD_ALPHA, !!(m_renderData.discardMode & DISCARD_ALPHA)); - shader->setUniformFloat(SHADER_DISCARD_ALPHA_VALUE, m_renderData.discardOpacity); - } else { - shader->setUniformInt(SHADER_DISCARD_OPAQUE, 0); - shader->setUniformInt(SHADER_DISCARD_ALPHA, 0); - } - CBox transformedBox = newBox; transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y); const auto TOPLEFT = Vector2D(transformedBox.x, transformedBox.y); const auto FULLSIZE = Vector2D(transformedBox.width, transformedBox.height); - // Rounded corners - shader->setUniformFloat2(SHADER_TOP_LEFT, TOPLEFT.x, TOPLEFT.y); - shader->setUniformFloat2(SHADER_FULL_SIZE, FULLSIZE.x, FULLSIZE.y); - shader->setUniformFloat(SHADER_RADIUS, data.round); - shader->setUniformFloat(SHADER_ROUNDING_POWER, data.roundingPower); - if (data.allowDim && m_renderData.currentWindow) { - if (m_renderData.currentWindow->m_notRespondingTint->value() > 0) { - const auto DIM = m_renderData.currentWindow->m_notRespondingTint->value(); - shader->setUniformInt(SHADER_APPLY_TINT, 1); - shader->setUniformFloat3(SHADER_TINT, 1.f - DIM, 1.f - DIM, 1.f - DIM); - } else if (m_renderData.currentWindow->m_dimPercent->value() > 0) { - shader->setUniformInt(SHADER_APPLY_TINT, 1); - const auto DIM = m_renderData.currentWindow->m_dimPercent->value(); - shader->setUniformFloat3(SHADER_TINT, 1.f - DIM, 1.f - DIM, 1.f - DIM); + if (!usingFinalShader) { + // Rounded corners + shader->setUniformFloat2(SHADER_TOP_LEFT, TOPLEFT.x, TOPLEFT.y); + shader->setUniformFloat2(SHADER_FULL_SIZE, FULLSIZE.x, FULLSIZE.y); + shader->setUniformFloat(SHADER_RADIUS, data.round); + shader->setUniformFloat(SHADER_ROUNDING_POWER, data.roundingPower); + + if (data.allowDim && m_renderData.currentWindow) { + if (m_renderData.currentWindow->m_notRespondingTint->value() > 0) { + const auto DIM = m_renderData.currentWindow->m_notRespondingTint->value(); + shader->setUniformInt(SHADER_APPLY_TINT, 1); + shader->setUniformFloat3(SHADER_TINT, 1.f - DIM, 1.f - DIM, 1.f - DIM); + } else if (m_renderData.currentWindow->m_dimPercent->value() > 0) { + shader->setUniformInt(SHADER_APPLY_TINT, 1); + const auto DIM = m_renderData.currentWindow->m_dimPercent->value(); + shader->setUniformFloat3(SHADER_TINT, 1.f - DIM, 1.f - DIM, 1.f - DIM); + } else + shader->setUniformInt(SHADER_APPLY_TINT, 0); } else shader->setUniformInt(SHADER_APPLY_TINT, 0); - } else - shader->setUniformInt(SHADER_APPLY_TINT, 0); - - return shader; -} - -void CHyprOpenGLImpl::renderTextureInternal(SP tex, const CBox& box, const STextureRenderData& data) { - RASSERT(m_renderData.pMonitor, "Tried to render texture without begin()!"); - RASSERT((tex->ok()), "Attempted to draw nullptr texture!"); - - TRACY_GPU_ZONE("RenderTextureInternalWithDamage"); - - if (data.damage->empty()) - return; - - CBox newBox = box; - m_renderData.renderModif.applyToBox(newBox); - - // get the needed transform for this texture - const auto MONITOR_INVERTED = Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)); - Hyprutils::Math::eTransform TRANSFORM = tex->m_transform; - - if (m_monitorTransformEnabled) - TRANSFORM = Math::composeTransform(MONITOR_INVERTED, TRANSFORM); - - Mat3x3 matrix = m_renderData.monitorProjection.projectBox(newBox, TRANSFORM, newBox.rot); - Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); - - const bool renderToOutput = m_applyFinalShader && g_pHyprRenderer->workBufferImageDescription()->id() == m_renderData.pMonitor->m_imageDescription->id(); - - glActiveTexture(GL_TEXTURE0); - tex->bind(); - - tex->setTexParameter(GL_TEXTURE_WRAP_S, data.wrapX); - tex->setTexParameter(GL_TEXTURE_WRAP_T, data.wrapY); - - if (m_renderData.useNearestNeighbor) { - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } else { - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, tex->magFilter); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, tex->minFilter); } - auto shader = renderToOutput ? renderToOutputInternal() : renderToFBInternal(data, tex->m_type, newBox); - - shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); - shader->setUniformInt(SHADER_TEX, 0); - GLCALL(glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO))); - GLCALL(glBindBuffer(GL_ARRAY_BUFFER, shader->getUniformLocation(SHADER_SHADER_VBO))); + glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO)); + glBindBuffer(GL_ARRAY_BUFFER, shader->getUniformLocation(SHADER_SHADER_VBO)); // this tells GPU can keep reading the old block for previous draws while the CPU writes to a new one. // to avoid stalls if renderTextureInternal is called multiple times on same renderpass @@ -1568,14 +1605,14 @@ void CHyprOpenGLImpl::renderTextureInternal(SP tex, const CBox& box, c }); } - GLCALL(glBindVertexArray(0)); - GLCALL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); tex->unbind(); } -void CHyprOpenGLImpl::renderTexturePrimitive(SP tex, const CBox& box) { +void CHyprOpenGLImpl::renderTexturePrimitive(SP tex, const CBox& box) { RASSERT(m_renderData.pMonitor, "Tried to render texture without begin()!"); - RASSERT((tex->ok()), "Attempted to draw nullptr texture!"); + RASSERT((tex->m_texID > 0), "Attempted to draw nullptr texture!"); TRACY_GPU_ZONE("RenderTexturePrimitive"); @@ -1603,7 +1640,7 @@ void CHyprOpenGLImpl::renderTexturePrimitive(SP tex, const CBox& box) tex->setTexParameter(GL_TEXTURE_MIN_FILTER, tex->minFilter); } - auto shader = useShader(getShaderVariant(SH_FRAG_PASSTHRURGBA)); + auto shader = useShader(m_shaders->frag[SH_FRAG_PASSTHRURGBA]); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniformInt(SHADER_TEX, 0); glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO)); @@ -1618,9 +1655,9 @@ void CHyprOpenGLImpl::renderTexturePrimitive(SP tex, const CBox& box) tex->unbind(); } -void CHyprOpenGLImpl::renderTextureMatte(SP tex, const CBox& box, SP matte) { +void CHyprOpenGLImpl::renderTextureMatte(SP tex, const CBox& box, CFramebuffer& matte) { RASSERT(m_renderData.pMonitor, "Tried to render texture without begin()!"); - RASSERT((tex->ok()), "Attempted to draw nullptr texture!"); + RASSERT((tex->m_texID > 0), "Attempted to draw nullptr texture!"); TRACY_GPU_ZONE("RenderTextureMatte"); @@ -1635,7 +1672,7 @@ void CHyprOpenGLImpl::renderTextureMatte(SP tex, const CBox& box, SPfrag[SH_FRAG_MATTE]); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniformInt(SHADER_TEX, 0); shader->setUniformInt(SHADER_ALPHA_MATTE, 1); @@ -1644,7 +1681,7 @@ void CHyprOpenGLImpl::renderTextureMatte(SP tex, const CBox& box, SPbind(); glActiveTexture(GL_TEXTURE0 + 1); - auto matteTex = matte->getTexture(); + auto matteTex = matte.getTexture(); matteTex->bind(); glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO)); @@ -1663,16 +1700,16 @@ void CHyprOpenGLImpl::renderTextureMatte(SP tex, const CBox& box, SP CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* originalDamage) { +CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* originalDamage) { if (!m_renderData.currentFB->getTexture()) { Log::logger->log(Log::ERR, "BUG THIS: null fb texture while attempting to blur main fb?! (introspection off?!)"); - return m_renderData.pCurrentMonData->mirrorFB; // return something to sample from at least + return &m_renderData.pCurrentMonData->mirrorFB; // return something to sample from at least } - return blurFramebufferWithDamage(a, originalDamage, *GLFB(m_renderData.currentFB)); + return blurFramebufferWithDamage(a, originalDamage, *m_renderData.currentFB); } -SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* originalDamage, CGLFramebuffer& source) { +CFramebuffer* CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* originalDamage, CFramebuffer& source) { TRACY_GPU_ZONE("RenderBlurFramebufferWithDamage"); const auto BLENDBEFORE = m_blend; @@ -1700,17 +1737,16 @@ SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or damage.expand(std::clamp(*PBLURSIZE, sc(1), sc(40)) * pow(2, BLUR_PASSES)); // helper - const auto PMIRRORFB = m_renderData.pCurrentMonData->mirrorFB; - const auto PMIRRORSWAPFB = m_renderData.pCurrentMonData->mirrorSwapFB; + const auto PMIRRORFB = &m_renderData.pCurrentMonData->mirrorFB; + const auto PMIRRORSWAPFB = &m_renderData.pCurrentMonData->mirrorSwapFB; - auto currentRenderToFB = PMIRRORFB; + CFramebuffer* currentRenderToFB = PMIRRORFB; // Begin with base color adjustments - global brightness and contrast // TODO: make this a part of the first pass maybe to save on a drawcall? { static auto PBLURCONTRAST = CConfigValue("decoration:blur:contrast"); static auto PBLURBRIGHTNESS = CConfigValue("decoration:blur:brightness"); - static auto PBLEND = CConfigValue("render:use_shader_blur_blend"); PMIRRORSWAPFB->bind(); @@ -1724,9 +1760,9 @@ SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or WP shader; // From FB to sRGB - const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); + const bool skipCM = !m_cmSupported || m_renderData.pMonitor->m_imageDescription->id() == DEFAULT_IMAGE_DESCRIPTION->id(); if (!skipCM) { - shader = useShader(getShaderVariant(SH_FRAG_BLURPREPARE, SH_FEAT_CM)); + shader = useShader(m_shaders->frag[SH_FRAG_CM_BLURPREPARE]); passCMUniforms(shader, m_renderData.pMonitor->m_imageDescription, DEFAULT_IMAGE_DESCRIPTION); shader->setUniformFloat(SHADER_SDR_SATURATION, m_renderData.pMonitor->m_sdrSaturation > 0 && @@ -1739,10 +1775,8 @@ SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or m_renderData.pMonitor->m_sdrBrightness : 1.0f); } else - shader = useShader(getShaderVariant(SH_FRAG_BLURPREPARE)); + shader = useShader(m_shaders->frag[SH_FRAG_BLURPREPARE]); - Mat3x3 matrix = m_renderData.monitorProjection.projectBox(MONITORBOX, *PBLEND ? HYPRUTILS_TRANSFORM_NORMAL : TRANSFORM); - Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniformFloat(SHADER_CONTRAST, *PBLURCONTRAST); shader->setUniformFloat(SHADER_BRIGHTNESS, *PBLURBRIGHTNESS); @@ -1814,13 +1848,13 @@ SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or CRegion tempDamage{damage}; // and draw - auto shader = useShader(getShaderVariant(SH_FRAG_BLUR1)); + auto shader = useShader(m_shaders->frag[SH_FRAG_BLUR1]); for (auto i = 1; i <= BLUR_PASSES; ++i) { tempDamage = damage.copy().scale(1.f / (1 << i)); drawPass(shader, SH_FRAG_BLUR1, &tempDamage); // down } - shader = useShader(getShaderVariant(SH_FRAG_BLUR2)); + shader = useShader(m_shaders->frag[SH_FRAG_BLUR2]); for (auto i = BLUR_PASSES - 1; i >= 0; --i) { tempDamage = damage.copy().scale(1.f / (1 << i)); // when upsampling we make the region twice as big drawPass(shader, SH_FRAG_BLUR2, &tempDamage); // up @@ -1844,7 +1878,7 @@ SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or currentTex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - auto shader = useShader(getShaderVariant(SH_FRAG_BLURFINISH)); + auto shader = useShader(m_shaders->frag[SH_FRAG_BLURFINISH]); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniformFloat(SHADER_NOISE, *PBLURNOISE); shader->setUniformFloat(SHADER_BRIGHTNESS, *PBLURBRIGHTNESS); @@ -1975,12 +2009,9 @@ void CHyprOpenGLImpl::preBlurForCurrentMonitor() { const auto POUTFB = blurMainFramebufferWithDamage(1, &fakeDamage); // render onto blurFB - if (!m_renderData.pCurrentMonData->blurFB) - m_renderData.pCurrentMonData->blurFB = g_pHyprRenderer->createFB(); - - m_renderData.pCurrentMonData->blurFB->alloc(m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y, - m_renderData.pMonitor->m_output->state->state().drmFormat); - m_renderData.pCurrentMonData->blurFB->bind(); + m_renderData.pCurrentMonData->blurFB.alloc(m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y, + m_renderData.pMonitor->m_output->state->state().drmFormat); + m_renderData.pCurrentMonData->blurFB.bind(); clear(CHyprColor(0, 0, 0, 0)); @@ -2016,7 +2047,7 @@ bool CHyprOpenGLImpl::shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWin static auto PBLURNEWOPTIMIZE = CConfigValue("decoration:blur:new_optimizations"); static auto PBLURXRAY = CConfigValue("decoration:blur:xray"); - if (!m_renderData.pCurrentMonData->blurFB || !m_renderData.pCurrentMonData->blurFB->getTexture()) + if (!m_renderData.pCurrentMonData->blurFB.getTexture()) return false; if (pWindow && pWindow->m_ruleApplicator->xray().hasValue() && !pWindow->m_ruleApplicator->xray().valueOrDefault()) @@ -2034,13 +2065,11 @@ bool CHyprOpenGLImpl::shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWin return false; } -void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP tex, const CBox& box, const STextureRenderData& data) { +void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP tex, const CBox& box, const STextureRenderData& data) { RASSERT(m_renderData.pMonitor, "Tried to render texture with blur without begin()!"); TRACY_GPU_ZONE("RenderTextureWithBlur"); - static auto PBLEND = CConfigValue("render:use_shader_blur_blend"); - // make a damage region for this window CRegion texDamage{m_renderData.damage}; texDamage.intersect(box.x, box.y, box.width, box.height); @@ -2074,35 +2103,35 @@ void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP tex, const CBox inverseOpaque.scale(m_renderData.pMonitor->m_scale); // vvv TODO: layered blur fbs? - const bool USENEWOPTIMIZE = shouldUseNewBlurOptimizations(m_renderData.currentLS.lock(), m_renderData.currentWindow.lock()) && !data.blockBlurOptimization; + const bool USENEWOPTIMIZE = shouldUseNewBlurOptimizations(m_renderData.currentLS.lock(), m_renderData.currentWindow.lock()) && !data.blockBlurOptimization; - SP POUTFB = nullptr; + CFramebuffer* POUTFB = nullptr; if (!USENEWOPTIMIZE) { inverseOpaque.translate(box.pos()); m_renderData.renderModif.applyToRegion(inverseOpaque); inverseOpaque.intersect(texDamage); POUTFB = blurMainFramebufferWithDamage(data.a, &inverseOpaque); } else - POUTFB = m_renderData.pCurrentMonData->blurFB; + POUTFB = &m_renderData.pCurrentMonData->blurFB; m_renderData.currentFB->bind(); - auto blurredBG = POUTFB->getTexture(); + const auto NEEDS_STENCIL = m_renderData.discardMode != 0; - const auto NEEDS_STENCIL = m_renderData.discardMode != 0 && (!data.blockBlurOptimization || (m_renderData.discardMode & DISCARD_ALPHA)); - if (!*PBLEND) { + if (NEEDS_STENCIL) { + scissor(nullptr); // allow the entire window and stencil to render + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); - if (NEEDS_STENCIL) { - scissor(nullptr); // allow the entire window and stencil to render - glClearStencil(0); - glClear(GL_STENCIL_BUFFER_BIT); + setCapStatus(GL_STENCIL_TEST, true); - setCapStatus(GL_STENCIL_TEST, true); + glStencilFunc(GL_ALWAYS, 1, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - glStencilFunc(GL_ALWAYS, 1, 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + if (USENEWOPTIMIZE && !(m_renderData.discardMode & DISCARD_ALPHA)) + renderRect(box, CHyprColor(0, 0, 0, 0), SRectRenderData{.round = data.round, .roundingPower = data.roundingPower}); + else renderTexture(tex, box, STextureRenderData{.a = data.a, .round = data.round, @@ -2110,76 +2139,69 @@ void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP tex, const CBox .discardActive = true, .allowCustomUV = true, .wrapX = data.wrapX, - .wrapY = data.wrapY}); // discard opaque and alpha < discardOpacity + .wrapY = data.wrapY}); // discard opaque + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - glStencilFunc(GL_EQUAL, 1, 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - } - - // stencil done. Render everything. - const auto LASTTL = m_renderData.primarySurfaceUVTopLeft; - const auto LASTBR = m_renderData.primarySurfaceUVBottomRight; - - CBox transformedBox = box; - transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x, - m_renderData.pMonitor->m_transformedSize.y); - - CBox monitorSpaceBox = {transformedBox.pos().x / m_renderData.pMonitor->m_pixelSize.x * m_renderData.pMonitor->m_transformedSize.x, - transformedBox.pos().y / m_renderData.pMonitor->m_pixelSize.y * m_renderData.pMonitor->m_transformedSize.y, - transformedBox.width / m_renderData.pMonitor->m_pixelSize.x * m_renderData.pMonitor->m_transformedSize.x, - transformedBox.height / m_renderData.pMonitor->m_pixelSize.y * m_renderData.pMonitor->m_transformedSize.y}; - - m_renderData.primarySurfaceUVTopLeft = monitorSpaceBox.pos() / m_renderData.pMonitor->m_transformedSize; - m_renderData.primarySurfaceUVBottomRight = (monitorSpaceBox.pos() + monitorSpaceBox.size()) / m_renderData.pMonitor->m_transformedSize; - - static auto PBLURIGNOREOPACITY = CConfigValue("decoration:blur:ignore_opacity"); - pushMonitorTransformEnabled(true); - bool renderModif = m_renderData.renderModif.enabled; - if (!USENEWOPTIMIZE) - setRenderModifEnabled(false); - renderTextureInternal(blurredBG, box, - STextureRenderData{ - .damage = &texDamage, - .a = (*PBLURIGNOREOPACITY ? data.blurA : data.a * data.blurA) * data.overallA, - .round = data.round, - .roundingPower = data.roundingPower, - .discardActive = false, - .allowCustomUV = true, - .noAA = false, - .wrapX = data.wrapX, - .wrapY = data.wrapY, - }); - if (!USENEWOPTIMIZE) - setRenderModifEnabled(renderModif); - popMonitorTransformEnabled(); - - if (NEEDS_STENCIL) - setCapStatus(GL_STENCIL_TEST, false); - - m_renderData.primarySurfaceUVTopLeft = LASTTL; - m_renderData.primarySurfaceUVBottomRight = LASTBR; + glStencilFunc(GL_EQUAL, 1, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); } + // stencil done. Render everything. + const auto LASTTL = m_renderData.primarySurfaceUVTopLeft; + const auto LASTBR = m_renderData.primarySurfaceUVBottomRight; + + CBox transformedBox = box; + transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x, + m_renderData.pMonitor->m_transformedSize.y); + + CBox monitorSpaceBox = {transformedBox.pos().x / m_renderData.pMonitor->m_pixelSize.x * m_renderData.pMonitor->m_transformedSize.x, + transformedBox.pos().y / m_renderData.pMonitor->m_pixelSize.y * m_renderData.pMonitor->m_transformedSize.y, + transformedBox.width / m_renderData.pMonitor->m_pixelSize.x * m_renderData.pMonitor->m_transformedSize.x, + transformedBox.height / m_renderData.pMonitor->m_pixelSize.y * m_renderData.pMonitor->m_transformedSize.y}; + + m_renderData.primarySurfaceUVTopLeft = monitorSpaceBox.pos() / m_renderData.pMonitor->m_transformedSize; + m_renderData.primarySurfaceUVBottomRight = (monitorSpaceBox.pos() + monitorSpaceBox.size()) / m_renderData.pMonitor->m_transformedSize; + + static auto PBLURIGNOREOPACITY = CConfigValue("decoration:blur:ignore_opacity"); + pushMonitorTransformEnabled(true); + if (!USENEWOPTIMIZE) + setRenderModifEnabled(false); + renderTextureInternal(POUTFB->getTexture(), box, + STextureRenderData{ + .damage = &texDamage, + .a = (*PBLURIGNOREOPACITY ? data.blurA : data.a * data.blurA) * data.overallA, + .round = data.round, + .roundingPower = data.roundingPower, + .discardActive = false, + .allowCustomUV = true, + .noAA = false, + .wrapX = data.wrapX, + .wrapY = data.wrapY, + }); + if (!USENEWOPTIMIZE) + setRenderModifEnabled(true); + popMonitorTransformEnabled(); + + m_renderData.primarySurfaceUVTopLeft = LASTTL; + m_renderData.primarySurfaceUVBottomRight = LASTBR; + // draw window + setCapStatus(GL_STENCIL_TEST, false); renderTextureInternal(tex, box, STextureRenderData{ .damage = &texDamage, .a = data.a * data.overallA, - .blur = *PBLEND, .round = data.round, .roundingPower = data.roundingPower, - .discardActive = *PBLEND && NEEDS_STENCIL, + .discardActive = false, .allowCustomUV = true, .allowDim = true, .noAA = false, .wrapX = data.wrapX, .wrapY = data.wrapY, - .blurredBG = blurredBG, }); - GLFB(m_renderData.currentFB)->invalidate({GL_STENCIL_ATTACHMENT}); + m_renderData.currentFB->invalidate({GL_STENCIL_ATTACHMENT}); scissor(nullptr); } @@ -2218,13 +2240,12 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& gr WP shader; - const bool IS_ICC = g_pHyprRenderer->workBufferImageDescription()->value().icc.present; - const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); + const bool skipCM = !m_cmSupported || m_renderData.pMonitor->m_imageDescription->id() == DEFAULT_IMAGE_DESCRIPTION->id(); if (!skipCM) { - shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING | SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD))); + shader = useShader(m_shaders->frag[SH_FRAG_CM_BORDER1]); passCMUniforms(shader, DEFAULT_IMAGE_DESCRIPTION); } else - shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING)); + shader = useShader(m_shaders->frag[SH_FRAG_BORDER1]); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniform4fv(SHADER_GRADIENT, grad.m_colorsOkLabA.size() / 4, grad.m_colorsOkLabA); @@ -2304,13 +2325,12 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& gr blend(true); WP shader; - const bool IS_ICC = g_pHyprRenderer->workBufferImageDescription()->value().icc.present; - const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); + const bool skipCM = !m_cmSupported || m_renderData.pMonitor->m_imageDescription->id() == DEFAULT_IMAGE_DESCRIPTION->id(); if (!skipCM) { - shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING | SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD))); + shader = useShader(m_shaders->frag[SH_FRAG_CM_BORDER1]); passCMUniforms(shader, DEFAULT_IMAGE_DESCRIPTION); } else - shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING)); + shader = useShader(m_shaders->frag[SH_FRAG_BORDER1]); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniform4fv(SHADER_GRADIENT, grad1.m_colorsOkLabA.size() / 4, grad1.m_colorsOkLabA); @@ -2383,9 +2403,9 @@ void CHyprOpenGLImpl::renderRoundedShadow(const CBox& box, int round, float roun blend(true); - const bool IS_ICC = g_pHyprRenderer->workBufferImageDescription()->value().icc.present; - const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); - auto shader = useShader(getShaderVariant(SH_FRAG_SHADOW, skipCM ? 0 : SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD))); + auto shader = useShader(m_shaders->frag[SH_FRAG_SHADOW]); + const bool skipCM = !m_cmSupported || m_renderData.pMonitor->m_imageDescription->id() == DEFAULT_IMAGE_DESCRIPTION->id(); + shader->setUniformInt(SHADER_SKIP_CM, skipCM); if (!skipCM) passCMUniforms(shader, DEFAULT_IMAGE_DESCRIPTION); @@ -2428,25 +2448,21 @@ void CHyprOpenGLImpl::renderRoundedShadow(const CBox& box, int round, float roun } void CHyprOpenGLImpl::saveBufferForMirror(const CBox& box) { - if (!m_renderData.pCurrentMonData->monitorMirrorFB) - m_renderData.pCurrentMonData->monitorMirrorFB = g_pHyprRenderer->createFB(); - if (!m_renderData.pCurrentMonData->monitorMirrorFB->isAllocated()) - m_renderData.pCurrentMonData->monitorMirrorFB->alloc(m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y, - m_renderData.pMonitor->m_output->state->state().drmFormat); + if (!m_renderData.pCurrentMonData->monitorMirrorFB.isAllocated()) + m_renderData.pCurrentMonData->monitorMirrorFB.alloc(m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y, + m_renderData.pMonitor->m_output->state->state().drmFormat); - m_renderData.pCurrentMonData->monitorMirrorFB->bind(); + m_renderData.pCurrentMonData->monitorMirrorFB.bind(); blend(false); renderTexture(m_renderData.currentFB->getTexture(), box, STextureRenderData{ - .damage = &m_renderData.finalDamage, - .a = 1.F, + .a = 1.f, .round = 0, .discardActive = false, .allowCustomUV = false, - .cmBackToSRGB = true, }); blend(true); @@ -2468,7 +2484,7 @@ void CHyprOpenGLImpl::renderMirrored() { monbox.x = (monitor->m_transformedSize.x - monbox.w) / 2; monbox.y = (monitor->m_transformedSize.y - monbox.h) / 2; - auto PFB = m_monitorRenderResources[mirrored].monitorMirrorFB; + const auto PFB = &m_monitorRenderResources[mirrored].monitorMirrorFB; if (!PFB->isAllocated() || !PFB->getTexture()) return; @@ -2542,7 +2558,7 @@ std::string CHyprOpenGLImpl::resolveAssetPath(const std::string& filename) { return fullPath; } -SP CHyprOpenGLImpl::loadAsset(const std::string& filename) { +SP CHyprOpenGLImpl::loadAsset(const std::string& filename) { const std::string fullPath = resolveAssetPath(filename); @@ -2564,11 +2580,12 @@ SP CHyprOpenGLImpl::loadAsset(const std::string& filename) { return tex; } -SP CHyprOpenGLImpl::texFromCairo(cairo_surface_t* cairo) { +SP CHyprOpenGLImpl::texFromCairo(cairo_surface_t* cairo) { const auto CAIROFORMAT = cairo_image_surface_get_format(cairo); - auto tex = makeShared(); + auto tex = makeShared(); - tex->allocate({cairo_image_surface_get_width(cairo), cairo_image_surface_get_height(cairo)}); + tex->allocate(); + tex->m_size = {cairo_image_surface_get_width(cairo), cairo_image_surface_get_height(cairo)}; const GLint glIFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB32F : GL_RGBA; const GLint glFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB : GL_RGBA; @@ -2589,8 +2606,8 @@ SP CHyprOpenGLImpl::texFromCairo(cairo_surface_t* cairo) { return tex; } -SP CHyprOpenGLImpl::renderText(const std::string& text, CHyprColor col, int pt, bool italic, const std::string& fontFamily, int maxWidth, int weight) { - SP tex = makeShared(); +SP CHyprOpenGLImpl::renderText(const std::string& text, CHyprColor col, int pt, bool italic, const std::string& fontFamily, int maxWidth, int weight) { + SP tex = makeShared(); static auto FONT = CConfigValue("misc:font_family"); @@ -2652,7 +2669,8 @@ SP CHyprOpenGLImpl::renderText(const std::string& text, CHyprColor col cairo_surface_flush(CAIROSURFACE); - tex->allocate({cairo_image_surface_get_width(CAIROSURFACE), cairo_image_surface_get_height(CAIROSURFACE)}); + tex->allocate(); + tex->m_size = {cairo_image_surface_get_width(CAIROSURFACE), cairo_image_surface_get_height(CAIROSURFACE)}; const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); tex->bind(); @@ -2669,8 +2687,8 @@ SP CHyprOpenGLImpl::renderText(const std::string& text, CHyprColor col } void CHyprOpenGLImpl::initMissingAssetTexture() { - SP tex = makeShared(); - tex->allocate({512, 512}); + SP tex = makeShared(); + tex->allocate(); const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 512, 512); const auto CAIRO = cairo_create(CAIROSURFACE); @@ -2822,19 +2840,16 @@ void CHyprOpenGLImpl::createBGTextureForMonitor(PHLMONITOR pMonitor) { if (!m_backgroundResource->m_ready) return; - if (!m_monitorBGFBs.contains(pMonitor)) - m_monitorBGFBs[pMonitor] = g_pHyprRenderer->createFB(); - // release the last tex if exists - auto PFB = m_monitorBGFBs[pMonitor]; + const auto PFB = &m_monitorBGFBs[pMonitor]; PFB->release(); PFB->alloc(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y, pMonitor->m_output->state->state().drmFormat); // create a new one with cairo - SP tex = makeShared(); + SP tex = makeShared(); - tex->allocate(pMonitor->m_pixelSize); + tex->allocate(); const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y); const auto CAIRO = cairo_create(CAIROSURFACE); @@ -2876,7 +2891,7 @@ void CHyprOpenGLImpl::createBGTextureForMonitor(PHLMONITOR pMonitor) { blend(true); clear(CHyprColor{0, 0, 0, 1}); - SP backgroundTexture = texFromCairo(m_backgroundResource->m_asset.cairoSurface->cairo()); + SP backgroundTexture = texFromCairo(m_backgroundResource->m_asset.cairoSurface->cairo()); // first render the background if (backgroundTexture) { @@ -2931,7 +2946,7 @@ void CHyprOpenGLImpl::clearWithTex() { data.box = {0, 0, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y}; data.a = m_renderData.pMonitor->m_backgroundOpacity->value(); data.flipEndFrame = true; - data.tex = TEXIT->second->getTexture(); + data.tex = TEXIT->second.getTexture(); g_pHyprRenderer->m_renderPass.add(makeUnique(std::move(data))); } } @@ -2944,19 +2959,19 @@ void CHyprOpenGLImpl::destroyMonitorResources(PHLMONITORREF pMonitor) { auto RESIT = g_pHyprOpenGL->m_monitorRenderResources.find(pMonitor); if (RESIT != g_pHyprOpenGL->m_monitorRenderResources.end()) { - RESIT->second.mirrorFB.reset(); - RESIT->second.offloadFB.reset(); - RESIT->second.mirrorSwapFB.reset(); - RESIT->second.monitorMirrorFB.reset(); - RESIT->second.blurFB.reset(); - RESIT->second.offMainFB.reset(); - RESIT->second.stencilTex.reset(); + RESIT->second.mirrorFB.release(); + RESIT->second.offloadFB.release(); + RESIT->second.mirrorSwapFB.release(); + RESIT->second.monitorMirrorFB.release(); + RESIT->second.blurFB.release(); + RESIT->second.offMainFB.release(); + RESIT->second.stencilTex->destroyTexture(); g_pHyprOpenGL->m_monitorRenderResources.erase(RESIT); } auto TEXIT = g_pHyprOpenGL->m_monitorBGFBs.find(pMonitor); if (TEXIT != g_pHyprOpenGL->m_monitorBGFBs.end()) { - TEXIT->second.reset(); + TEXIT->second.release(); g_pHyprOpenGL->m_monitorBGFBs.erase(TEXIT); } @@ -2977,21 +2992,19 @@ void CHyprOpenGLImpl::restoreMatrix() { } void CHyprOpenGLImpl::bindOffMain() { - if (!m_renderData.pCurrentMonData->offMainFB) - m_renderData.pCurrentMonData->offMainFB = g_pHyprRenderer->createFB(); + if (!m_renderData.pCurrentMonData->offMainFB.isAllocated()) { + m_renderData.pCurrentMonData->offMainFB.alloc(m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y, + m_renderData.pMonitor->m_output->state->state().drmFormat); - if (!m_renderData.pCurrentMonData->offMainFB->isAllocated()) { - m_renderData.pCurrentMonData->offMainFB->addStencil(m_renderData.pCurrentMonData->stencilTex); - m_renderData.pCurrentMonData->offMainFB->alloc(m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y, - m_renderData.pMonitor->m_output->state->state().drmFormat); + m_renderData.pCurrentMonData->offMainFB.addStencil(m_renderData.pCurrentMonData->stencilTex); } - m_renderData.pCurrentMonData->offMainFB->bind(); + m_renderData.pCurrentMonData->offMainFB.bind(); clear(CHyprColor(0, 0, 0, 0)); - m_renderData.currentFB = m_renderData.pCurrentMonData->offMainFB; + m_renderData.currentFB = &m_renderData.pCurrentMonData->offMainFB; } -void CHyprOpenGLImpl::renderOffToMain(CGLFramebuffer* off) { +void CHyprOpenGLImpl::renderOffToMain(CFramebuffer* off) { CBox monbox = {0, 0, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y}; renderTexturePrimitive(off->getTexture(), monbox); } @@ -3037,9 +3050,9 @@ void CHyprOpenGLImpl::setCapStatus(int cap, bool status) { if (idx == CAP_STATUS_END) { if (status) - GLCALL(glEnable(cap)) + glEnable(cap); else - GLCALL(glDisable(cap)); + glDisable(cap); return; } @@ -3049,10 +3062,10 @@ void CHyprOpenGLImpl::setCapStatus(int cap, bool status) { if (status) { m_capStatus[idx] = status; - GLCALL(glEnable(cap)); + glEnable(cap); } else { m_capStatus[idx] = status; - GLCALL(glDisable(cap)); + glDisable(cap); } } @@ -3086,23 +3099,53 @@ bool CHyprOpenGLImpl::explicitSyncSupported() { return m_exts.EGL_ANDROID_native_fence_sync_ext; } -WP CHyprOpenGLImpl::getShaderVariant(ePreparedFragmentShader frag, ShaderFeatureFlags features) { - if (!m_shaders->fragVariants[frag].contains(features)) { - auto shader = makeShared(); +WP CHyprOpenGLImpl::getSurfaceShader(uint8_t features) { + if (!m_shaders->fragVariants.contains(features)) { - Log::logger->log(Log::INFO, "compiling feature set {} for {}", features, FRAG_SHADERS[frag]); + auto shader = makeShared(); + auto includes = m_includes; + includes["get_rgb_pixel.glsl"] = includes[features & SH_FEAT_RGBA ? "get_rgba_pixel.glsl" : "get_rgbx_pixel.glsl"]; + if (!(features & SH_FEAT_DISCARD)) { + includes["discard.glsl"] = ""; + includes["do_discard.glsl"] = ""; + } + if (!(features & SH_FEAT_TINT)) { + includes["tint.glsl"] = ""; + includes["do_tint.glsl"] = ""; + } + if (!(features & SH_FEAT_ROUNDING)) { + includes["rounding.glsl"] = ""; + includes["do_rounding.glsl"] = ""; + } + if (!(features & SH_FEAT_CM)) { + includes["surface_CM.glsl"] = ""; + includes["CM.glsl"] = ""; + includes["do_CM.glsl"] = ""; + } + if (!(features & SH_FEAT_TONEMAP)) { + includes["tonemap.glsl"] = ""; + includes["do_tonemap.glsl"] = ""; + } + if (!(features & SH_FEAT_SDR_MOD)) { + includes["sdr_mod.glsl"] = ""; + includes["do_sdr_mod.glsl"] = ""; + } + if (!(features & SH_FEAT_TONEMAP || features & SH_FEAT_SDR_MOD)) + includes["primaries_xyz.glsl"] = includes["primaries_xyz_const.glsl"]; - const auto fragSrc = g_pShaderLoader->getVariantSource(frag, features); - - if (!shader->createProgram(m_shaders->TEXVERTSRC, fragSrc, true, true)) - Log::logger->log(Log::ERR, "shader features {} failed for {}", features, FRAG_SHADERS[frag]); - - m_shaders->fragVariants[frag][features] = shader; - return shader; + Log::logger->log(Log::INFO, "getSurfaceShader: compiling feature set {}", features); + const auto fragSrc = processShader("surface.frag", includes, MAX_INCLUDE_DEPTH); + if (shader->createProgram(m_shaders->TEXVERTSRC, fragSrc, true, true)) { + m_shaders->fragVariants[features] = shader; + return shader; + } else { + Log::logger->log(Log::ERR, "getSurfaceShader failed for {}. Falling back to old branching", features); + m_shaders->fragVariants[features] = nullptr; + } } - ASSERT(m_shaders->fragVariants[frag][features]); - return m_shaders->fragVariants[frag][features]; + ASSERT(m_shaders->fragVariants[features]); + return m_shaders->fragVariants[features]; } std::vector CHyprOpenGLImpl::getDRMFormats() { diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index c9008447..bc1f5f4d 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -20,7 +20,6 @@ #include "Texture.hpp" #include "Framebuffer.hpp" #include "Renderbuffer.hpp" -#include "desktop/DesktopTypes.hpp" #include "pass/Pass.hpp" #include @@ -32,15 +31,9 @@ #include "../debug/TracyDefines.hpp" #include "../protocols/core/Compositor.hpp" -#include "render/ShaderLoader.hpp" -#include "render/gl/GLFramebuffer.hpp" -#include "render/gl/GLRenderbuffer.hpp" -#include "render/gl/GLTexture.hpp" - -#define GLFB(ifb) dc(ifb.get()) struct gbm_device; -class IHyprRenderer; +class CHyprRenderer; struct SVertex { float x, y; // position @@ -94,37 +87,68 @@ enum eMonitorExtraRenderFBs : uint8_t { FB_MONITOR_RENDER_EXTRA_BLUR, }; +enum ePreparedFragmentShader : uint8_t { + SH_FRAG_QUAD = 0, + SH_FRAG_PASSTHRURGBA, + SH_FRAG_MATTE, + SH_FRAG_EXT, + SH_FRAG_BLUR1, + SH_FRAG_BLUR2, + SH_FRAG_CM_BLURPREPARE, + SH_FRAG_BLURPREPARE, + SH_FRAG_BLURFINISH, + SH_FRAG_SHADOW, + SH_FRAG_CM_BORDER1, + SH_FRAG_BORDER1, + SH_FRAG_GLITCH, + + SH_FRAG_LAST, +}; + +enum ePreparedFragmentShaderFeature : uint8_t { + SH_FEAT_UNKNOWN = 0, // all features just in case + + SH_FEAT_RGBA = (1 << 0), // RGBA/RGBX texture sampling + SH_FEAT_DISCARD = (1 << 1), // RGBA/RGBX texture sampling + SH_FEAT_TINT = (1 << 2), // uniforms: tint; condition: applyTint + SH_FEAT_ROUNDING = (1 << 3), // uniforms: radius, roundingPower, topLeft, fullSize; condition: radius > 0 + SH_FEAT_CM = (1 << 4), // uniforms: srcTFRange, dstTFRange, srcRefLuminance, convertMatrix; condition: !skipCM + SH_FEAT_TONEMAP = (1 << 5), // uniforms: maxLuminance, dstMaxLuminance, dstRefLuminance; condition: maxLuminance < dstMaxLuminance * 1.01 + SH_FEAT_SDR_MOD = (1 << 6), // uniforms: sdrSaturation, sdrBrightnessMultiplier; condition: SDR <-> HDR && (sdrSaturation != 1 || sdrBrightnessMultiplier != 1) + + // uniforms: targetPrimariesXYZ; condition: SH_FEAT_TONEMAP || SH_FEAT_SDR_MOD +}; + struct SFragShaderDesc { - Render::ePreparedFragmentShader id; - const char* file; + ePreparedFragmentShader id; + const char* file; }; struct SPreparedShaders { - // SPreparedShaders() { - // for (auto& f : frag) { - // f = makeShared(); - // } - // } + SPreparedShaders() { + for (auto& f : frag) { + f = makeShared(); + } + } - std::string TEXVERTSRC; - std::string TEXVERTSRC320; - // std::array, SH_FRAG_LAST> frag; - // std::map> fragVariants; - std::array>, Render::SH_FRAG_LAST> fragVariants; + std::string TEXVERTSRC; + std::string TEXVERTSRC320; + std::array, SH_FRAG_LAST> frag; + std::map> fragVariants; }; struct SMonitorRenderData { - SP offloadFB; - SP mirrorFB; // these are used for some effects, - SP mirrorSwapFB; // etc - SP offMainFB; - SP monitorMirrorFB; // used for mirroring outputs, does not contain artifacts like offloadFB - SP blurFB; + CFramebuffer offloadFB; + CFramebuffer mirrorFB; // these are used for some effects, + CFramebuffer mirrorSwapFB; // etc + CFramebuffer offMainFB; + CFramebuffer monitorMirrorFB; // used for mirroring outputs, does not contain artifacts like offloadFB + CFramebuffer blurFB; - SP stencilTex = makeShared(); + SP stencilTex = makeShared(); - bool blurFBDirty = true; - bool blurFBShouldRender = false; + bool blurFBDirty = true; + bool blurFBShouldRender = false; }; struct SCurrentRenderData { @@ -135,9 +159,9 @@ struct SCurrentRenderData { // FIXME: raw pointer galore! SMonitorRenderData* pCurrentMonData = nullptr; - SP currentFB = nullptr; // current rendering to - SP mainFB = nullptr; // main to render to - SP outFB = nullptr; // out to render to (if offloaded, etc) + CFramebuffer* currentFB = nullptr; // current rendering to + CFramebuffer* mainFB = nullptr; // main to render to + CFramebuffer* outFB = nullptr; // out to render to (if offloaded, etc) CRegion damage; CRegion finalDamage; // damage used for funal off -> main @@ -215,11 +239,8 @@ class CHyprOpenGLImpl { bool noAA = false; bool blockBlurOptimization = false; GLenum wrapX = GL_CLAMP_TO_EDGE, wrapY = GL_CLAMP_TO_EDGE; - bool cmBackToSRGB = false; - bool noCM = false; - bool finalMonitorCM = false; + bool cmBackToSRGB = false; SP cmBackToSRGBSource; - SP blurredBG; }; struct SBorderRenderData { @@ -230,17 +251,16 @@ class CHyprOpenGLImpl { int outerRound = -1; /* use round */ }; - void begin(PHLMONITOR, const CRegion& damage, SP fb = nullptr, std::optional finalDamage = {}); - void beginSimple(PHLMONITOR, const CRegion& damage, SP rb = nullptr, SP fb = nullptr); + void begin(PHLMONITOR, const CRegion& damage, CFramebuffer* fb = nullptr, std::optional finalDamage = {}); + void beginSimple(PHLMONITOR, const CRegion& damage, SP rb = nullptr, CFramebuffer* fb = nullptr); void end(); void renderRect(const CBox&, const CHyprColor&, SRectRenderData data); - void renderTexture(SP, const CBox&, STextureRenderData data); + void renderTexture(SP, const CBox&, STextureRenderData data); void renderRoundedShadow(const CBox&, int round, float roundingPower, int range, const CHyprColor& color, float a = 1.0); void renderBorder(const CBox&, const CGradientValueData&, SBorderRenderData data); void renderBorder(const CBox&, const CGradientValueData&, const CGradientValueData&, float lerp, SBorderRenderData data); - void renderTextureMatte(SP tex, const CBox& pBox, SP matte); - void renderTexturePrimitive(SP tex, const CBox& box); + void renderTextureMatte(SP tex, const CBox& pBox, CFramebuffer& matte); void pushMonitorTransformEnabled(bool enabled); void popMonitorTransformEnabled(); @@ -277,49 +297,50 @@ class CHyprOpenGLImpl { void applyScreenShader(const std::string& path); void bindOffMain(); - void renderOffToMain(CGLFramebuffer* off); + void renderOffToMain(CFramebuffer* off); void bindBackOnMain(); - bool needsACopyFB(PHLMONITOR mon); - std::string resolveAssetPath(const std::string& file); - SP loadAsset(const std::string& file); - SP texFromCairo(cairo_surface_t* cairo); - SP renderText(const std::string& text, CHyprColor col, int pt, bool italic = false, const std::string& fontFamily = "", int maxWidth = 0, int weight = 400); + SP loadAsset(const std::string& file); + SP texFromCairo(cairo_surface_t* cairo); + SP renderText(const std::string& text, CHyprColor col, int pt, bool italic = false, const std::string& fontFamily = "", int maxWidth = 0, int weight = 400); void setDamage(const CRegion& damage, std::optional finalDamage = {}); DRMFormat getPreferredReadFormat(PHLMONITOR pMonitor); - std::vector getDRMFormats(); - std::vector getDRMFormatModifiers(DRMFormat format); - EGLImageKHR createEGLImage(const Aquamarine::SDMABUFAttrs& attrs); + std::vector getDRMFormats(); + std::vector getDRMFormatModifiers(DRMFormat format); + EGLImageKHR createEGLImage(const Aquamarine::SDMABUFAttrs& attrs); - bool initShaders(const std::string& path = ""); + bool initShaders(); - WP useShader(WP prog); + WP useShader(WP prog); - bool explicitSyncSupported(); - WP getShaderVariant(Render::ePreparedFragmentShader frag, Render::ShaderFeatureFlags features = 0); + void ensureLockTexturesRendered(bool load); - bool m_shadersInitialized = false; - SP m_shaders; + bool explicitSyncSupported(); + WP getSurfaceShader(uint8_t features); - SCurrentRenderData m_renderData; + bool m_shadersInitialized = false; + SP m_shaders; + std::map m_includes; - Hyprutils::OS::CFileDescriptor m_gbmFD; - gbm_device* m_gbmDevice = nullptr; - EGLContext m_eglContext = nullptr; - EGLDisplay m_eglDisplay = nullptr; - EGLDeviceEXT m_eglDevice = nullptr; - uint m_failedAssetsNo = 0; + SCurrentRenderData m_renderData; - bool m_reloadScreenShader = true; // at launch it can be set + Hyprutils::OS::CFileDescriptor m_gbmFD; + gbm_device* m_gbmDevice = nullptr; + EGLContext m_eglContext = nullptr; + EGLDisplay m_eglDisplay = nullptr; + EGLDeviceEXT m_eglDevice = nullptr; + uint m_failedAssetsNo = 0; - std::map> m_windowFramebuffers; - std::map> m_layerFramebuffers; - std::map, SP> m_popupFramebuffers; - std::map m_monitorRenderResources; - std::map> m_monitorBGFBs; + bool m_reloadScreenShader = true; // at launch it can be set + + std::map m_windowFramebuffers; + std::map m_layerFramebuffers; + std::map, CFramebuffer> m_popupFramebuffers; + std::map m_monitorRenderResources; + std::map m_monitorBGFBs; struct { PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES = nullptr; @@ -350,7 +371,7 @@ class CHyprOpenGLImpl { bool EGL_ANDROID_native_fence_sync_ext = false; } m_exts; - SP m_screencopyDeniedTexture; + SP m_screencopyDeniedTexture; enum eEGLContextVersion : uint8_t { EGL_CONTEXT_GLES_2_0 = 0, @@ -391,10 +412,10 @@ class CHyprOpenGLImpl { bool m_monitorTransformEnabled = false; // do not modify directly std::stack m_monitorTransformStack; - SP m_missingAssetTexture; - SP m_lockDeadTexture; - SP m_lockDead2Texture; - SP m_lockTtyTextTexture; + SP m_missingAssetTexture; + SP m_lockDeadTexture; + SP m_lockDead2Texture; + SP m_lockTtyTextTexture; SP m_finalScreenShader; CTimer m_globalTimer; GLuint m_currentProgram; @@ -406,7 +427,6 @@ class CHyprOpenGLImpl { void initEGL(bool gbm); EGLDeviceEXT eglDeviceFromDRMFD(int drmFD); void initAssets(); - void ensureLockTexturesRendered(bool load); void initMissingAssetTexture(); void requestBackgroundResource(); @@ -420,22 +440,21 @@ class CHyprOpenGLImpl { std::optional> getModsForFormat(EGLint format); // returns the out FB, can be either Mirror or MirrorSwap - SP blurMainFramebufferWithDamage(float a, CRegion* damage); - SP blurFramebufferWithDamage(float a, CRegion* damage, CGLFramebuffer& source); + CFramebuffer* blurMainFramebufferWithDamage(float a, CRegion* damage); + CFramebuffer* blurFramebufferWithDamage(float a, CRegion* damage, CFramebuffer& source); - void passCMUniforms(WP, const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, - bool modifySDR = false, float sdrMinLuminance = -1.0f, int sdrMaxLuminance = -1); - void passCMUniforms(WP, const NColorManagement::PImageDescription imageDescription); - void renderSplash(cairo_t* const, cairo_surface_t* const, double offset, const Vector2D& size); - void renderRectInternal(const CBox&, const CHyprColor&, const SRectRenderData& data); - void renderRectWithBlurInternal(const CBox&, const CHyprColor&, const SRectRenderData& data); - void renderRectWithDamageInternal(const CBox&, const CHyprColor&, const SRectRenderData& data); - WP renderToOutputInternal(); - WP renderToFBInternal(const STextureRenderData& data, eTextureType texType, const CBox& newBox); - void renderTextureInternal(SP, const CBox&, const STextureRenderData& data); - void renderTextureWithBlurInternal(SP, const CBox&, const STextureRenderData& data); + void passCMUniforms(WP, const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, + bool modifySDR = false, float sdrMinLuminance = -1.0f, int sdrMaxLuminance = -1); + void passCMUniforms(WP, const NColorManagement::PImageDescription imageDescription); + void renderTexturePrimitive(SP tex, const CBox& box); + void renderSplash(cairo_t* const, cairo_surface_t* const, double offset, const Vector2D& size); + void renderRectInternal(const CBox&, const CHyprColor&, const SRectRenderData& data); + void renderRectWithBlurInternal(const CBox&, const CHyprColor&, const SRectRenderData& data); + void renderRectWithDamageInternal(const CBox&, const CHyprColor&, const SRectRenderData& data); + void renderTextureInternal(SP, const CBox&, const STextureRenderData& data); + void renderTextureWithBlurInternal(SP, const CBox&, const STextureRenderData& data); - void preBlurForCurrentMonitor(); + void preBlurForCurrentMonitor(); friend class CHyprRenderer; friend class CTexPassElement; diff --git a/src/render/Renderbuffer.cpp b/src/render/Renderbuffer.cpp index bab4f73e..bb638e20 100644 --- a/src/render/Renderbuffer.cpp +++ b/src/render/Renderbuffer.cpp @@ -1,21 +1,73 @@ #include "Renderbuffer.hpp" -#include "Framebuffer.hpp" -#include "render/Renderer.hpp" -#include "render/gl/GLRenderbuffer.hpp" -#include +#include "Renderer.hpp" +#include "OpenGL.hpp" +#include "../Compositor.hpp" +#include "../protocols/types/Buffer.hpp" #include #include #include -IRenderbuffer::IRenderbuffer(SP buffer, uint32_t format) : m_hlBuffer(buffer) { - m_listeners.destroyBuffer = buffer->events.destroy.listen([this] { g_pHyprRenderer->onRenderbufferDestroy(dc(this)); }); +CRenderbuffer::~CRenderbuffer() { + if (!g_pCompositor || g_pCompositor->m_isShuttingDown || !g_pHyprRenderer) + return; + + g_pHyprRenderer->makeEGLCurrent(); + + unbind(); + m_framebuffer.release(); + + if (m_rbo) + glDeleteRenderbuffers(1, &m_rbo); + + if (m_image != EGL_NO_IMAGE_KHR) + g_pHyprOpenGL->m_proc.eglDestroyImageKHR(g_pHyprOpenGL->m_eglDisplay, m_image); } -bool IRenderbuffer::good() { +CRenderbuffer::CRenderbuffer(SP buffer, uint32_t format) : m_hlBuffer(buffer), m_drmFormat(format) { + auto dma = buffer->dmabuf(); + + m_image = g_pHyprOpenGL->createEGLImage(dma); + if (m_image == EGL_NO_IMAGE_KHR) { + Log::logger->log(Log::ERR, "rb: createEGLImage failed"); + return; + } + + glGenRenderbuffers(1, &m_rbo); + glBindRenderbuffer(GL_RENDERBUFFER, m_rbo); + g_pHyprOpenGL->m_proc.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, m_image); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + glGenFramebuffers(1, &m_framebuffer.m_fb); + m_framebuffer.m_fbAllocated = true; + m_framebuffer.m_size = buffer->size; + m_framebuffer.bind(); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rbo); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + Log::logger->log(Log::ERR, "rbo: glCheckFramebufferStatus failed"); + return; + } + + m_framebuffer.unbind(); + + m_listeners.destroyBuffer = buffer->events.destroy.listen([this] { g_pHyprRenderer->onRenderbufferDestroy(this); }); + + m_good = true; +} + +bool CRenderbuffer::good() { return m_good; } -SP IRenderbuffer::getFB() { - return m_framebuffer; +void CRenderbuffer::bind() { + m_framebuffer.bind(); +} + +void CRenderbuffer::unbind() { + m_framebuffer.unbind(); +} + +CFramebuffer* CRenderbuffer::getFB() { + return &m_framebuffer; } diff --git a/src/render/Renderbuffer.hpp b/src/render/Renderbuffer.hpp index c33144d3..90c539b1 100644 --- a/src/render/Renderbuffer.hpp +++ b/src/render/Renderbuffer.hpp @@ -5,22 +5,27 @@ #include "Framebuffer.hpp" #include -class IRenderbuffer { +class CMonitor; + +class CRenderbuffer { public: - IRenderbuffer(SP buffer, uint32_t format); - virtual ~IRenderbuffer() = default; + CRenderbuffer(SP buffer, uint32_t format); + ~CRenderbuffer(); bool good(); - SP getFB(); - - virtual void bind() = 0; - virtual void unbind() = 0; + void bind(); + void unbind(); + CFramebuffer* getFB(); + uint32_t getFormat(); WP m_hlBuffer; - protected: - SP m_framebuffer; - bool m_good = false; + private: + void* m_image = nullptr; + GLuint m_rbo = 0; + CFramebuffer m_framebuffer; + uint32_t m_drmFormat = 0; + bool m_good = false; struct { CHyprSignalListener destroyBuffer; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 165f580a..fbc34910 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -3,7 +3,6 @@ #include "../helpers/math/Math.hpp" #include #include -#include #include #include "../config/ConfigValue.hpp" #include "../config/ConfigManager.hpp" @@ -30,12 +29,9 @@ #include "../layout/LayoutManager.hpp" #include "../layout/space/Space.hpp" #include "../i18n/Engine.hpp" -#include "desktop/DesktopTypes.hpp" #include "../event/EventBus.hpp" #include "helpers/CursorShapes.hpp" -#include "helpers/MainLoopExecutor.hpp" #include "helpers/Monitor.hpp" -#include "macros.hpp" #include "pass/TexPassElement.hpp" #include "pass/ClearPassElement.hpp" #include "pass/RectPassElement.hpp" @@ -45,18 +41,8 @@ #include "../protocols/ColorManagement.hpp" #include "../protocols/types/ContentType.hpp" #include "../helpers/MiscFunctions.hpp" -#include "render/AsyncResourceGatherer.hpp" -#include "render/Framebuffer.hpp" +#include "../event/EventBus.hpp" #include "render/OpenGL.hpp" -#include "render/Texture.hpp" -#include "render/gl/GLFramebuffer.hpp" -#include "render/gl/GLTexture.hpp" -#include -#include -#include -#include -#include -#include #include using namespace Hyprutils::Utils; @@ -657,13 +643,13 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T } if (TRANSFORMERSPRESENT) { - IFramebuffer* last = g_pHyprOpenGL->m_renderData.currentFB.get(); + CFramebuffer* last = g_pHyprOpenGL->m_renderData.currentFB; for (auto const& t : pWindow->m_transformers) { last = t->transform(last); } g_pHyprOpenGL->bindBackOnMain(); - g_pHyprOpenGL->renderOffToMain(dc(last)); + g_pHyprOpenGL->renderOffToMain(last); } } @@ -747,36 +733,6 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T g_pHyprOpenGL->m_renderData.currentWindow.reset(); } -SP CHyprRenderer::createTexture(const SP buffer, bool keepDataCopy) { - if (!buffer) - return createTexture(); - - auto attrs = buffer->dmabuf(); - - if (!attrs.success) { - // attempt shm - auto shm = buffer->shm(); - - if (!shm.success) { - Log::logger->log(Log::ERR, "Cannot create a texture: buffer has no dmabuf or shm"); - return createTexture(buffer->opaque); - } - - auto [pixelData, fmt, bufLen] = buffer->beginDataPtr(0); - - return createTexture(fmt, pixelData, bufLen, shm.size, keepDataCopy, buffer->opaque); - } - - auto tex = createTexture(attrs, buffer->opaque); - - if (!tex) { - Log::logger->log(Log::ERR, "Cannot create a texture: failed to create an Image"); - return createTexture(buffer->opaque); - } - - return tex; -} - void CHyprRenderer::renderLayer(PHLLS pLayer, PHLMONITOR pMonitor, const Time::steady_tp& time, bool popups, bool lockscreen) { if (!pLayer) return; @@ -1304,79 +1260,6 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP surface, bool modifySDR, float sdrMinLuminance, int sdrMaxLuminance) { - const auto sdrEOTF = NTransferFunction::fromConfig(); - NColorManagement::eTransferFunction srcTF; - - auto& m_renderData = g_pHyprOpenGL->m_renderData; - if (m_renderData.surface.valid()) { - if (m_renderData.surface->m_colorManagement.valid()) { - if (sdrEOTF == NTransferFunction::TF_FORCED_GAMMA22 && imageDescription->value().transferFunction == NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_SRGB) - srcTF = NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_GAMMA22; - else - srcTF = imageDescription->value().transferFunction; - } else if (sdrEOTF == NTransferFunction::TF_SRGB) - srcTF = NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_SRGB; - else if (sdrEOTF == NTransferFunction::TF_GAMMA22 || sdrEOTF == NTransferFunction::TF_FORCED_GAMMA22) - srcTF = NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_GAMMA22; - else - srcTF = imageDescription->value().transferFunction; - } else - srcTF = imageDescription->value().transferFunction; - - const bool needsSDRmod = modifySDR && isSDR2HDR(imageDescription->value(), targetImageDescription->value()); - const bool needsHDRmod = !needsSDRmod && isHDR2SDR(imageDescription->value(), targetImageDescription->value()); - const float maxLuminance = needsHDRmod ? - imageDescription->value().getTFMaxLuminance(-1) : - (imageDescription->value().luminances.max > 0 ? imageDescription->value().luminances.max : imageDescription->value().luminances.reference); - const auto dstMaxLuminance = targetImageDescription->value().luminances.max > 0 ? targetImageDescription->value().luminances.max : 10000; - - auto matrix = imageDescription->getPrimaries()->convertMatrix(targetImageDescription->getPrimaries()); - auto toXYZ = targetImageDescription->getPrimaries()->value().toXYZ(); - - const bool needsMod = (imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_SRGB || imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_GAMMA22) && - targetImageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ && - ((m_renderData.pMonitor->m_sdrSaturation > 0 && m_renderData.pMonitor->m_sdrSaturation != 1.0f) || - (m_renderData.pMonitor->m_sdrBrightness > 0 && m_renderData.pMonitor->m_sdrBrightness != 1.0f)); - - return { - .sourceTF = srcTF, - .targetTF = targetImageDescription->value().transferFunction, - .srcTFRange = {.min = imageDescription->value().getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1), - .max = imageDescription->value().getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1)}, - .dstTFRange = {.min = targetImageDescription->value().getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1), - .max = targetImageDescription->value().getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1)}, - .srcRefLuminance = imageDescription->value().luminances.reference, - .dstRefLuminance = targetImageDescription->value().luminances.reference, - .convertMatrix = matrix.mat(), - - .needsTonemap = maxLuminance >= dstMaxLuminance * 1.01, - .maxLuminance = maxLuminance * targetImageDescription->value().luminances.reference / imageDescription->value().luminances.reference, - .dstMaxLuminance = dstMaxLuminance, - .dstPrimaries2XYZ = toXYZ.mat(), - .needsSDRmod = needsMod, - .sdrSaturation = needsSDRmod && m_renderData.pMonitor->m_sdrSaturation > 0 ? m_renderData.pMonitor->m_sdrSaturation : 1.0f, - .sdrBrightnessMultiplier = needsSDRmod && m_renderData.pMonitor->m_sdrBrightness > 0 ? m_renderData.pMonitor->m_sdrBrightness : 1.0f, - }; -} - void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) { static std::chrono::high_resolution_clock::time_point renderStart = std::chrono::high_resolution_clock::now(); static std::chrono::high_resolution_clock::time_point renderStartOverlay = std::chrono::high_resolution_clock::now(); @@ -1422,8 +1305,7 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) { if (pMonitor->m_scheduledRecalc) { pMonitor->m_scheduledRecalc = false; - if (pMonitor->m_activeWorkspace) // might be missing (mirror) - pMonitor->m_activeWorkspace->m_space->recalculate(); + pMonitor->m_activeWorkspace->m_space->recalculate(); } if (!pMonitor->m_output->needsFrame && pMonitor->m_forceFullFrames == 0) @@ -2317,8 +2199,10 @@ std::tuple CHyprRenderer::getRenderTimes(PHLMONITOR pMonito float maxRenderTime = 0; float minRenderTime = 9999; for (auto const& rt : POVERLAY->m_lastRenderTimes) { - maxRenderTime = std::max(rt, maxRenderTime); - minRenderTime = std::min(rt, minRenderTime); + if (rt > maxRenderTime) + maxRenderTime = rt; + if (rt < minRenderTime) + minRenderTime = rt; avgRenderTime += rt; } avgRenderTime /= POVERLAY->m_lastRenderTimes.empty() ? 1 : POVERLAY->m_lastRenderTimes.size(); @@ -2357,13 +2241,13 @@ void CHyprRenderer::initiateManualCrash() { **PDT = 0; } -SP CHyprRenderer::getOrCreateRenderbuffer(SP buffer, uint32_t fmt) { +SP CHyprRenderer::getOrCreateRenderbuffer(SP buffer, uint32_t fmt) { auto it = std::ranges::find_if(m_renderbuffers, [&](const auto& other) { return other->m_hlBuffer == buffer; }); if (it != m_renderbuffers.end()) return *it; - auto buf = makeShared(buffer, fmt); + auto buf = makeShared(buffer, fmt); if (!buf->good()) return nullptr; @@ -2387,7 +2271,7 @@ void CHyprRenderer::unsetEGL() { eglMakeCurrent(g_pHyprOpenGL->m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } -bool CHyprRenderer::beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMode mode, SP buffer, SP fb, bool simple) { +bool CHyprRenderer::beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMode mode, SP buffer, CFramebuffer* fb, bool simple) { makeEGLCurrent(); @@ -2519,11 +2403,11 @@ void CHyprRenderer::endRender(const std::function& renderingDoneCallback } } -void CHyprRenderer::onRenderbufferDestroy(CGLRenderbuffer* rb) { +void CHyprRenderer::onRenderbufferDestroy(CRenderbuffer* rb) { std::erase_if(m_renderbuffers, [&](const auto& rbo) { return rbo.get() == rb; }); } -SP CHyprRenderer::getCurrentRBO() { +SP CHyprRenderer::getCurrentRBO() { return m_currentRenderbuffer; } @@ -2576,10 +2460,7 @@ void CHyprRenderer::makeSnapshot(PHLWINDOW pWindow) { makeEGLCurrent(); - if (!g_pHyprOpenGL->m_windowFramebuffers.contains(ref)) - g_pHyprOpenGL->m_windowFramebuffers[ref] = g_pHyprRenderer->createFB(); - - const auto PFRAMEBUFFER = g_pHyprOpenGL->m_windowFramebuffers[ref]; + const auto PFRAMEBUFFER = &g_pHyprOpenGL->m_windowFramebuffers[ref]; PFRAMEBUFFER->alloc(PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y, DRM_FORMAT_ABGR8888); @@ -2612,10 +2493,7 @@ void CHyprRenderer::makeSnapshot(PHLLS pLayer) { makeEGLCurrent(); - if (!g_pHyprOpenGL->m_layerFramebuffers.contains(pLayer)) - g_pHyprOpenGL->m_layerFramebuffers[pLayer] = g_pHyprRenderer->createFB(); - - const auto PFRAMEBUFFER = g_pHyprOpenGL->m_layerFramebuffers[pLayer]; + const auto PFRAMEBUFFER = &g_pHyprOpenGL->m_layerFramebuffers[pLayer]; PFRAMEBUFFER->alloc(PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y, DRM_FORMAT_ABGR8888); @@ -2649,10 +2527,7 @@ void CHyprRenderer::makeSnapshot(WP popup) { makeEGLCurrent(); - if (!g_pHyprOpenGL->m_popupFramebuffers.contains(popup)) - g_pHyprOpenGL->m_popupFramebuffers[popup] = g_pHyprRenderer->createFB(); - - const auto PFRAMEBUFFER = g_pHyprOpenGL->m_popupFramebuffers[popup]; + const auto PFRAMEBUFFER = &g_pHyprOpenGL->m_popupFramebuffers[popup]; PFRAMEBUFFER->alloc(PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y, DRM_FORMAT_ABGR8888); @@ -2701,7 +2576,7 @@ void CHyprRenderer::renderSnapshot(PHLWINDOW pWindow) { if (!g_pHyprOpenGL->m_windowFramebuffers.contains(ref)) return; - const auto FBDATA = g_pHyprOpenGL->m_windowFramebuffers.at(ref); + const auto FBDATA = &g_pHyprOpenGL->m_windowFramebuffers.at(ref); if (!FBDATA->getTexture()) return; @@ -2757,7 +2632,7 @@ void CHyprRenderer::renderSnapshot(PHLLS pLayer) { if (!g_pHyprOpenGL->m_layerFramebuffers.contains(pLayer)) return; - const auto FBDATA = g_pHyprOpenGL->m_layerFramebuffers.at(pLayer); + const auto FBDATA = &g_pHyprOpenGL->m_layerFramebuffers.at(pLayer); if (!FBDATA->getTexture()) return; @@ -2801,7 +2676,7 @@ void CHyprRenderer::renderSnapshot(WP popup) { static CConfigValue PBLURIGNOREA = CConfigValue("decoration:blur:popups_ignorealpha"); - const auto FBDATA = g_pHyprOpenGL->m_popupFramebuffers.at(popup); + const auto FBDATA = &g_pHyprOpenGL->m_popupFramebuffers.at(popup); if (!FBDATA->getTexture()) return; @@ -2830,16 +2705,6 @@ void CHyprRenderer::renderSnapshot(WP popup) { m_renderPass.add(makeUnique(std::move(data))); } -NColorManagement::PImageDescription CHyprRenderer::workBufferImageDescription() { - const auto& m_renderData = g_pHyprOpenGL->m_renderData; - // TODO - // const bool IS_MONITOR_ICC = m_renderData.pMonitor->m_imageDescription.valid() && m_renderData.pMonitor->m_imageDescription->value().icc.present; - // const auto sdrEOTF = NTransferFunction::fromConfig(IS_MONITOR_ICC); - // const auto CHOSEN_SDR_EOTF = sdrEOTF != NTransferFunction::TF_SRGB ? NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22 : NColorManagement::CM_TRANSFER_FUNCTION_SRGB; - - return m_renderData.pMonitor->m_imageDescription; //CImageDescription::from(NColorManagement::SImageDescription{.transferFunction = CHOSEN_SDR_EOTF}); -} - bool CHyprRenderer::shouldBlur(PHLLS ls) { if (m_bRenderingSnapshot) return false; @@ -2863,92 +2728,3 @@ bool CHyprRenderer::shouldBlur(WP p) { return *PBLURPOPUPS && *PBLUR; } - -bool CHyprRenderer::reloadShaders(const std::string& path) { - return g_pHyprOpenGL->initShaders(path); -} - -SP CHyprRenderer::createStencilTexture(const int width, const int height) { - makeEGLCurrent(); - auto tex = makeShared(); - tex->allocate({width, height}); - - return tex; -} - -SP CHyprRenderer::createTexture(bool opaque) { - makeEGLCurrent(); - return makeShared(opaque); -} - -SP CHyprRenderer::createTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy, bool opaque) { - makeEGLCurrent(); - return makeShared(drmFormat, pixels, stride, size, keepDataCopy, opaque); -} - -SP CHyprRenderer::createTexture(const Aquamarine::SDMABUFAttrs& attrs, bool opaque) { - makeEGLCurrent(); - const auto image = g_pHyprOpenGL->createEGLImage(attrs); - if (!image) - return nullptr; - return makeShared(attrs, image, opaque); -} - -SP CHyprRenderer::createTexture(const int width, const int height, unsigned char* const data) { - makeEGLCurrent(); - SP tex = makeShared(); - - tex->allocate({width, height}); - - tex->m_size = {width, height}; - // copy the data to an OpenGL texture we have - const GLint glFormat = GL_RGBA; - const GLint glType = GL_UNSIGNED_BYTE; - - tex->bind(); - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); - - glTexImage2D(GL_TEXTURE_2D, 0, glFormat, tex->m_size.x, tex->m_size.y, 0, glFormat, glType, data); - tex->unbind(); - - return tex; -} - -SP CHyprRenderer::createTexture(cairo_surface_t* cairo) { - makeEGLCurrent(); - const auto CAIROFORMAT = cairo_image_surface_get_format(cairo); - auto tex = makeShared(); - - tex->allocate({cairo_image_surface_get_width(cairo), cairo_image_surface_get_height(cairo)}); - - const GLint glIFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB32F : GL_RGBA; - const GLint glFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB : GL_RGBA; - const GLint glType = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_FLOAT : GL_UNSIGNED_BYTE; - - const auto DATA = cairo_image_surface_get_data(cairo); - tex->bind(); - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - if (CAIROFORMAT != CAIRO_FORMAT_RGB96F) { - tex->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); - } - - glTexImage2D(GL_TEXTURE_2D, 0, glIFormat, tex->m_size.x, tex->m_size.y, 0, glFormat, glType, DATA); - - return tex; -} - -SP CHyprRenderer::createTexture(std::span lut3D, size_t N) { - makeEGLCurrent(); - return makeShared(lut3D, N); -} - -SP CHyprRenderer::createFB(const std::string& name) { - makeEGLCurrent(); - return makeShared(name); -} \ No newline at end of file diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index bd14c219..24e0fb66 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -1,10 +1,7 @@ #pragma once #include "../defines.hpp" -#include -#include #include -#include #include "../helpers/Monitor.hpp" #include "../desktop/view/LayerSurface.hpp" #include "OpenGL.hpp" @@ -13,22 +10,12 @@ #include "../helpers/math/Math.hpp" #include "../helpers/time/Time.hpp" #include "../../protocols/cursor-shape-v1.hpp" -#include "render/Framebuffer.hpp" -#include "render/Texture.hpp" struct SMonitorRule; class CWorkspace; class CInputPopup; class IHLBuffer; class CEventLoopTimer; - -const std::vector ASSET_PATHS = { -#ifdef DATAROOTDIR - DATAROOTDIR, -#endif - "/usr/share", - "/usr/local/share", -}; class CToplevelExportProtocolManager; class CInputManager; struct SSessionLockSurface; @@ -61,29 +48,6 @@ struct SRenderWorkspaceUntilData { PHLWINDOW w; }; -struct STFRange { - float min = 0; - float max = 80; -}; - -struct SCMSettings { - NColorManagement::eTransferFunction sourceTF = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22; - NColorManagement::eTransferFunction targetTF = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22; - STFRange srcTFRange; - STFRange dstTFRange; - float srcRefLuminance = 80; - float dstRefLuminance = 80; - std::array, 3> convertMatrix; - - bool needsTonemap = false; - float maxLuminance = 80; - float dstMaxLuminance = 80; - std::array, 3> dstPrimaries2XYZ; - bool needsSDRmod = false; - float sdrSaturation = 1.0; - float sdrBrightnessMultiplier = 1.0; -}; - class CHyprRenderer { public: CHyprRenderer(); @@ -109,8 +73,8 @@ class CHyprRenderer { void renderLockscreen(PHLMONITOR pMonitor, const Time::steady_tp& now, const CBox& geometry); void setCursorSurface(SP surf, int hotspotX, int hotspotY, bool force = false); void setCursorFromName(const std::string& name, bool force = false); - void onRenderbufferDestroy(CGLRenderbuffer* rb); - SP getCurrentRBO(); + void onRenderbufferDestroy(CRenderbuffer* rb); + SP getCurrentRBO(); bool isNvidia(); bool isIntel(); bool isSoftware(); @@ -125,12 +89,9 @@ class CHyprRenderer { void renderSnapshot(PHLLS); void renderSnapshot(WP); - // - NColorManagement::PImageDescription workBufferImageDescription(); - // if RENDER_MODE_NORMAL, provided damage will be written to. // otherwise, it will be the one used. - bool beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMode mode = RENDER_MODE_NORMAL, SP buffer = {}, SP fb = nullptr, bool simple = false); + bool beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMode mode = RENDER_MODE_NORMAL, SP buffer = {}, CFramebuffer* fb = nullptr, bool simple = false); void endRender(const std::function& renderingDoneCallback = {}); bool m_bBlockSurfaceFeedback = false; @@ -158,21 +119,7 @@ class CHyprRenderer { std::string name; } m_lastCursorData; - CRenderPass m_renderPass = {}; - - SP createStencilTexture(const int width, const int height); - SP createTexture(bool opaque = false); - SP createTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy = false, bool opaque = false); - SP createTexture(const Aquamarine::SDMABUFAttrs&, bool opaque = false); - SP createTexture(const int width, const int height, unsigned char* const); - SP createTexture(cairo_surface_t* cairo); - SP createTexture(const SP buffer, bool keepDataCopy = false); - SP createTexture(std::span lut3D, size_t N); - SP createFB(const std::string& name = ""); - - SCMSettings getCMSettings(const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, - SP surface = nullptr, bool modifySDR = false, float sdrMinLuminance = -1.0f, int sdrMaxLuminance = -1); - bool reloadShaders(const std::string& path = ""); + CRenderPass m_renderPass = {}; private: void arrangeLayerArray(PHLMONITOR, const std::vector&, bool, CBox*); @@ -199,7 +146,7 @@ class CHyprRenderer { bool m_cursorHidden = false; bool m_cursorHiddenByCondition = false; bool m_cursorHasSurface = false; - SP m_currentRenderbuffer = nullptr; + SP m_currentRenderbuffer = nullptr; SP m_currentBuffer = nullptr; eRenderMode m_renderMode = RENDER_MODE_NORMAL; bool m_nvidia = false; @@ -214,8 +161,8 @@ class CHyprRenderer { bool hiddenOnKeyboard = false; } m_cursorHiddenConditions; - SP getOrCreateRenderbuffer(SP buffer, uint32_t fmt); - std::vector> m_renderbuffers; + SP getOrCreateRenderbuffer(SP buffer, uint32_t fmt); + std::vector> m_renderbuffers; std::vector m_renderUnfocused; SP m_renderUnfocusedTimer; diff --git a/src/render/Shader.cpp b/src/render/Shader.cpp index ead841a5..5f62232c 100644 --- a/src/render/Shader.cpp +++ b/src/render/Shader.cpp @@ -127,6 +127,7 @@ void CShader::getUniformLocations() { m_uniformLocations[SHADER_TEX_TYPE] = getUniform("texType"); // shader has #include "CM.glsl" + m_uniformLocations[SHADER_SKIP_CM] = getUniform("skipCM"); m_uniformLocations[SHADER_SOURCE_TF] = getUniform("sourceTF"); m_uniformLocations[SHADER_TARGET_TF] = getUniform("targetTF"); m_uniformLocations[SHADER_SRC_TF_RANGE] = getUniform("srcTFRange"); @@ -139,13 +140,8 @@ void CShader::getUniformLocations() { m_uniformLocations[SHADER_SDR_SATURATION] = getUniform("sdrSaturation"); m_uniformLocations[SHADER_SDR_BRIGHTNESS] = getUniform("sdrBrightnessMultiplier"); m_uniformLocations[SHADER_CONVERT_MATRIX] = getUniform("convertMatrix"); - m_uniformLocations[SHADER_LUT_3D] = getUniform("iccLut3D"); - m_uniformLocations[SHADER_LUT_SIZE] = getUniform("iccLutSize"); // m_uniformLocations[SHADER_TEX] = getUniform("tex"); - m_uniformLocations[SHADER_BLURRED_BG] = getUniform("blurredBG"); - m_uniformLocations[SHADER_UV_SIZE] = getUniform("uvSize"); - m_uniformLocations[SHADER_UV_OFFSET] = getUniform("uvOffset"); m_uniformLocations[SHADER_ALPHA] = getUniform("alpha"); m_uniformLocations[SHADER_POS_ATTRIB] = getAttrib("pos"); m_uniformLocations[SHADER_TEX_ATTRIB] = getAttrib("texcoord"); @@ -252,8 +248,7 @@ void CShader::setUniformInt(eShaderUniform location, GLint v0) { return; cached = v0; - - GLCALL(glUniform1i(m_uniformLocations[location], v0)); + glUniform1i(m_uniformLocations[location], v0); } void CShader::setUniformFloat(eShaderUniform location, GLfloat v0) { @@ -269,7 +264,7 @@ void CShader::setUniformFloat(eShaderUniform location, GLfloat v0) { } cached = v0; - GLCALL(glUniform1f(m_uniformLocations[location], v0)); + glUniform1f(m_uniformLocations[location], v0); } void CShader::setUniformFloat2(eShaderUniform location, GLfloat v0, GLfloat v1) { @@ -285,7 +280,7 @@ void CShader::setUniformFloat2(eShaderUniform location, GLfloat v0, GLfloat v1) } cached = std::array{v0, v1}; - GLCALL(glUniform2f(m_uniformLocations[location], v0, v1)); + glUniform2f(m_uniformLocations[location], v0, v1); } void CShader::setUniformFloat3(eShaderUniform location, GLfloat v0, GLfloat v1, GLfloat v2) { @@ -301,7 +296,7 @@ void CShader::setUniformFloat3(eShaderUniform location, GLfloat v0, GLfloat v1, } cached = std::array{v0, v1, v2}; - GLCALL(glUniform3f(m_uniformLocations[location], v0, v1, v2)); + glUniform3f(m_uniformLocations[location], v0, v1, v2); } void CShader::setUniformFloat4(eShaderUniform location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) { @@ -317,7 +312,7 @@ void CShader::setUniformFloat4(eShaderUniform location, GLfloat v0, GLfloat v1, } cached = std::array{v0, v1, v2, v3}; - GLCALL(glUniform4f(m_uniformLocations[location], v0, v1, v2, v3)); + glUniform4f(m_uniformLocations[location], v0, v1, v2, v3); } void CShader::setUniformMatrix3fv(eShaderUniform location, GLsizei count, GLboolean transpose, std::array value) { @@ -333,7 +328,7 @@ void CShader::setUniformMatrix3fv(eShaderUniform location, GLsizei count, GLbool } cached = SUniformMatrix3Data{.count = count, .transpose = transpose, .value = value}; - GLCALL(glUniformMatrix3fv(m_uniformLocations[location], count, transpose, value.data())); + glUniformMatrix3fv(m_uniformLocations[location], count, transpose, value.data()); } void CShader::setUniformMatrix4x2fv(eShaderUniform location, GLsizei count, GLboolean transpose, std::array value) { @@ -349,7 +344,7 @@ void CShader::setUniformMatrix4x2fv(eShaderUniform location, GLsizei count, GLbo } cached = SUniformMatrix4Data{.count = count, .transpose = transpose, .value = value}; - GLCALL(glUniformMatrix4x2fv(m_uniformLocations[location], count, transpose, value.data())); + glUniformMatrix4x2fv(m_uniformLocations[location], count, transpose, value.data()); } void CShader::setUniformfv(eShaderUniform location, GLsizei count, const std::vector& value, GLsizei vec_size) { @@ -366,9 +361,9 @@ void CShader::setUniformfv(eShaderUniform location, GLsizei count, const std::ve cached = SUniformVData{.count = count, .value = value}; switch (vec_size) { - case 1: GLCALL(glUniform1fv(m_uniformLocations[location], count, value.data())); break; - case 2: GLCALL(glUniform2fv(m_uniformLocations[location], count, value.data())); break; - case 4: GLCALL(glUniform4fv(m_uniformLocations[location], count, value.data())); break; + case 1: glUniform1fv(m_uniformLocations[location], count, value.data()); break; + case 2: glUniform2fv(m_uniformLocations[location], count, value.data()); break; + case 4: glUniform4fv(m_uniformLocations[location], count, value.data()); break; default: UNREACHABLE(); } } diff --git a/src/render/Shader.hpp b/src/render/Shader.hpp index 9b097c44..9f871c0e 100644 --- a/src/render/Shader.hpp +++ b/src/render/Shader.hpp @@ -9,6 +9,7 @@ enum eShaderUniform : uint8_t { SHADER_COLOR, SHADER_ALPHA_MATTE, SHADER_TEX_TYPE, + SHADER_SKIP_CM, SHADER_SOURCE_TF, SHADER_TARGET_TF, SHADER_SRC_TF_RANGE, @@ -74,11 +75,6 @@ enum eShaderUniform : uint8_t { SHADER_POINTER_INACTIVE_TIMEOUT, SHADER_POINTER_LAST_ACTIVE, SHADER_POINTER_SIZE, - SHADER_LUT_3D, - SHADER_LUT_SIZE, - SHADER_BLURRED_BG, - SHADER_UV_SIZE, - SHADER_UV_OFFSET, SHADER_LAST, }; diff --git a/src/render/ShaderLoader.cpp b/src/render/ShaderLoader.cpp deleted file mode 100644 index 0d2d0ee4..00000000 --- a/src/render/ShaderLoader.cpp +++ /dev/null @@ -1,176 +0,0 @@ -#include "ShaderLoader.hpp" -#include -#include -#include -#include -#include -#include "../debug/log/Logger.hpp" -#include "shaders/Shaders.hpp" -#include "../helpers/fs/FsUtils.hpp" -#include "Renderer.hpp" -#include -#include -#include - -using namespace Render; - -CShaderLoader::CShaderLoader(const std::vector includes, const std::array& frags, const std::string shaderPath) : m_shaderPath(shaderPath) { - m_callbacks = glsl_include_callbacks_t{ - .include_local = - [](void* ctx, const char* header_name, const char* includer_name, size_t include_depth) { - auto shaderLoader = sc(ctx); - auto res = new glsl_include_result_t; - if (shaderLoader->m_overrideDefines.length() && std::string{header_name} == "defines.h") { - res->header_name = header_name; - res->header_data = shaderLoader->m_overrideDefines.c_str(); - res->header_length = shaderLoader->m_overrideDefines.length(); - } else if (shaderLoader->includes().contains(header_name)) { - res->header_name = header_name; - res->header_data = shaderLoader->includes().at(header_name).c_str(); - res->header_length = shaderLoader->includes().at(header_name).length(); - } else { - res->header_name = nullptr; - res->header_data = nullptr; - res->header_length = 0; - } - - shaderLoader->m_includeResults.push_back(res); - return res; - }, - .free_include_result = - [](void* ctx, glsl_include_result_t* result) { - auto shaderLoader = sc(ctx); - std::erase(shaderLoader->m_includeResults, result); - delete result; - return 0; - }, - }; - - for (const auto& inc : includes) { - include(inc); - } - - std::ranges::transform(frags, m_fragFiles.begin(), [&](const auto& filename) { return loadShader(filename); }); -} - -CShaderLoader::~CShaderLoader() { - // glslFreeIncludeResult should leave it empty by this point - for (const auto& res : m_includeResults) { - delete res; - } -} - -void CShaderLoader::include(const std::string& filename) { - m_includes.insert({filename, loadShader(filename)}); -} - -std::string CShaderLoader::getDefines(ShaderFeatureFlags features) { - std::string res = ""; - std::map defines = { - {"USE_RGBA", features & SH_FEAT_RGBA ? "1" : "0"}, {"USE_DISCARD", features & SH_FEAT_DISCARD ? "1" : "0"}, {"USE_TINT", features & SH_FEAT_TINT ? "1" : "0"}, - {"USE_ROUNDING", features & SH_FEAT_ROUNDING ? "1" : "0"}, {"USE_CM", features & SH_FEAT_CM ? "1" : "0"}, {"USE_TONEMAP", features & SH_FEAT_TONEMAP ? "1" : "0"}, - {"USE_SDR_MOD", features & SH_FEAT_SDR_MOD ? "1" : "0"}, {"USE_BLUR", features & SH_FEAT_BLUR ? "1" : "0"}, {"USE_ICC", features & SH_FEAT_ICC ? "1" : "0"}, - }; - for (const auto& [name, value] : defines) { - res += std::format("#define {} {}\n", name, value); - } - return res; -} - -std::string CShaderLoader::processSource(const std::string& source, glslang_stage_t stage) { - const glslang_input_t input = { - .language = GLSLANG_SOURCE_GLSL, - .stage = stage, - .client = GLSLANG_CLIENT_NONE, - .target_language = GLSLANG_TARGET_NONE, - .code = source.c_str(), - .default_version = 100, - .default_profile = GLSLANG_NO_PROFILE, - .force_default_version_and_profile = false, - .forward_compatible = false, - .messages = GLSLANG_MSG_DEFAULT_BIT, - .resource = glslang_default_resource(), - .callbacks = m_callbacks, - .callbacks_ctx = this, - }; - - glslang_shader_t* shader = glslang_shader_create(&input); - - if (!glslang_shader_preprocess(shader, &input)) { - Log::logger->log(Log::ERR, "GLSL preprocessing failed"); - Log::logger->log(Log::ERR, "{}", glslang_shader_get_info_log(shader)); - Log::logger->log(Log::ERR, "{}", glslang_shader_get_info_debug_log(shader)); - Log::logger->log(Log::ERR, "{}", input.code); - glslang_shader_delete(shader); - return source; - } - - std::stringstream stream(glslang_shader_get_preprocessed_code(shader)); - std::string code = ""; - std::string line; - - while (std::getline(stream, line)) { - if (!line.starts_with("#line ")) - code += line + "\n"; - } - - return code; -} - -std::string CShaderLoader::process(const std::string& filename) { - auto source = loadShader(filename); - return processSource(source, filename.ends_with(".vert") ? GLSLANG_STAGE_VERTEX : GLSLANG_STAGE_FRAGMENT); -} - -std::string CShaderLoader::process(const std::string& filename, const std::map& defines) { - m_overrideDefines = ""; - for (const auto& [name, value] : defines) { - m_overrideDefines += std::format("#define {} {}\n", name, value); - } - const auto& res = process(filename); - m_overrideDefines = ""; - return res; -} - -std::string CShaderLoader::getVariantSource(ePreparedFragmentShader frag, ShaderFeatureFlags features) { - static const auto PCM = CConfigValue("render:cm_enabled"); - if (!*PCM) - features &= ~(SH_FEAT_CM | SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD); - - if (!m_fragVariants[frag].contains(features)) { - ASSERT(m_fragFiles[frag].length()); - m_overrideDefines = getDefines(features); - m_fragVariants[frag][features] = processSource(m_fragFiles[frag]); - m_overrideDefines = ""; - } - - return m_fragVariants[frag][features]; -} - -const std::map& CShaderLoader::includes() { - return m_includes; -} - -// TODO notify user if bundled shader is newer than ~/.config override -std::string CShaderLoader::loadShader(const std::string& filename) { - if (m_shaderPath.length()) { - std::filesystem::path path = m_shaderPath; - const auto src = NFsUtils::readFileAsString(path / filename); - if (src.has_value()) - return src.value(); - } - const auto home = Hyprutils::Path::getHome(); - if (home.has_value()) { - const auto src = NFsUtils::readFileAsString(home.value() + "/hypr/shaders/" + filename); - if (src.has_value()) - return src.value(); - } - for (auto& e : ASSET_PATHS) { - const auto src = NFsUtils::readFileAsString(std::string{e} + "/hypr/shaders/" + filename); - if (src.has_value()) - return src.value(); - } - if (SHADERS.contains(filename)) - return SHADERS.at(filename); - throw std::runtime_error(std::format("Couldn't load shader {}", filename)); -} diff --git a/src/render/ShaderLoader.hpp b/src/render/ShaderLoader.hpp deleted file mode 100644 index e522e9fa..00000000 --- a/src/render/ShaderLoader.hpp +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include "../helpers/memory/Memory.hpp" - -namespace Render { - enum ePreparedFragmentShaderFeature : uint16_t { - SH_FEAT_UNKNOWN = 0, // all features just in case - - SH_FEAT_RGBA = (1 << 0), // RGBA/RGBX texture sampling - SH_FEAT_DISCARD = (1 << 1), // RGBA/RGBX texture sampling - SH_FEAT_TINT = (1 << 2), // uniforms: tint; condition: applyTint - SH_FEAT_ROUNDING = (1 << 3), // uniforms: radius, roundingPower, topLeft, fullSize; condition: radius > 0 - SH_FEAT_CM = (1 << 4), // uniforms: srcTFRange, dstTFRange, srcRefLuminance, convertMatrix; condition: !skipCM - SH_FEAT_TONEMAP = (1 << 5), // uniforms: maxLuminance, dstMaxLuminance, dstRefLuminance; condition: maxLuminance < dstMaxLuminance * 1.01 - SH_FEAT_SDR_MOD = (1 << 6), // uniforms: sdrSaturation, sdrBrightnessMultiplier; condition: SDR <-> HDR && (sdrSaturation != 1 || sdrBrightnessMultiplier != 1) - SH_FEAT_BLUR = (1 << 7), // condition: render:use_shader_blur_blend - SH_FEAT_ICC = (1 << 8), // - - // uniforms: targetPrimariesXYZ; condition: SH_FEAT_TONEMAP || SH_FEAT_SDR_MOD - }; - - using ShaderFeatureFlags = uint16_t; - - enum ePreparedFragmentShader : uint8_t { - SH_FRAG_QUAD = 0, - SH_FRAG_PASSTHRURGBA, - SH_FRAG_MATTE, - SH_FRAG_EXT, - SH_FRAG_BLUR1, - SH_FRAG_BLUR2, - SH_FRAG_BLURPREPARE, - SH_FRAG_BLURFINISH, - SH_FRAG_SHADOW, - SH_FRAG_SURFACE, - SH_FRAG_BORDER1, - SH_FRAG_GLITCH, - - SH_FRAG_LAST, - }; - - class CShaderLoader { - public: - CShaderLoader(const std::vector includes, const std::array& frags, const std::string shaderPath = ""); - ~CShaderLoader(); - - void include(const std::string& filename); - std::string process(const std::string& filename); - std::string process(const std::string& filename, const std::map& defines); - - std::string getVariantSource(ePreparedFragmentShader frag, ShaderFeatureFlags features); - - const std::map& includes(); - - std::vector m_includeResults; - - private: - std::string loadShader(const std::string& filename); - std::string getDefines(ShaderFeatureFlags features); - std::string processSource(const std::string& source, glslang_stage_t stage = GLSLANG_STAGE_FRAGMENT); - - // - std::string m_shaderPath; - std::array m_fragFiles; - std::array, SH_FRAG_LAST> m_fragVariants; - std::map m_includes; - - std::string m_overrideDefines; - glsl_include_callbacks_t m_callbacks; - }; - - inline UP g_pShaderLoader; -} diff --git a/src/render/Texture.cpp b/src/render/Texture.cpp index 28ae4b41..0e807485 100644 --- a/src/render/Texture.cpp +++ b/src/render/Texture.cpp @@ -1,24 +1,239 @@ #include "Texture.hpp" +#include "Renderer.hpp" +#include "../Compositor.hpp" +#include "../protocols/types/Buffer.hpp" +#include "../helpers/Format.hpp" #include -ITexture::ITexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy, bool opaque) : - m_size(size), m_opaque(opaque), m_drmFormat(drmFormat), m_keepDataCopy(keepDataCopy) { - if (m_keepDataCopy && stride && pixels) { - m_dataCopy.resize(stride * size.y); - memcpy(m_dataCopy.data(), pixels, stride * size.y); +CTexture::CTexture() = default; + +CTexture::~CTexture() { + if (!g_pCompositor || g_pCompositor->m_isShuttingDown || !g_pHyprRenderer) + return; + + g_pHyprRenderer->makeEGLCurrent(); + destroyTexture(); +} + +CTexture::CTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size_, bool keepDataCopy) : m_drmFormat(drmFormat), m_keepDataCopy(keepDataCopy) { + createFromShm(drmFormat, pixels, stride, size_); +} + +CTexture::CTexture(const Aquamarine::SDMABUFAttrs& attrs, void* image) { + createFromDma(attrs, image); +} + +CTexture::CTexture(const SP buffer, bool keepDataCopy) : m_keepDataCopy(keepDataCopy) { + if (!buffer) + return; + + m_opaque = buffer->opaque; + + auto attrs = buffer->dmabuf(); + + if (!attrs.success) { + // attempt shm + auto shm = buffer->shm(); + + if (!shm.success) { + Log::logger->log(Log::ERR, "Cannot create a texture: buffer has no dmabuf or shm"); + return; + } + + auto [pixelData, fmt, bufLen] = buffer->beginDataPtr(0); + + m_drmFormat = fmt; + + createFromShm(fmt, pixelData, bufLen, shm.size); + return; + } + + auto image = g_pHyprOpenGL->createEGLImage(buffer->dmabuf()); + + if (!image) { + Log::logger->log(Log::ERR, "Cannot create a texture: failed to create an EGLImage"); + return; + } + + createFromDma(attrs, image); +} + +void CTexture::createFromShm(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size_) { + g_pHyprRenderer->makeEGLCurrent(); + + const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat); + ASSERT(format); + + m_type = format->withAlpha ? TEXTURE_RGBA : TEXTURE_RGBX; + m_size = size_; + m_isSynchronous = true; + m_target = GL_TEXTURE_2D; + allocate(); + bind(); + setTexParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + setTexParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + if (format->swizzle.has_value()) + swizzle(format->swizzle.value()); + + bool alignmentChanged = false; + if (format->bytesPerBlock != 4) { + const GLint alignment = (stride % 4 == 0) ? 4 : 1; + GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, alignment)); + alignmentChanged = true; + } + + GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / format->bytesPerBlock)); + GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, format->glInternalFormat ? format->glInternalFormat : format->glFormat, size_.x, size_.y, 0, format->glFormat, format->glType, pixels)); + GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0)); + if (alignmentChanged) + GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 4)); + + unbind(); + + if (m_keepDataCopy) { + m_dataCopy.resize(stride * size_.y); + memcpy(m_dataCopy.data(), pixels, stride * size_.y); } } -ITexture::ITexture(std::span lut3D, size_t N) : m_type(TEXTURE_3D_LUT), m_size(lut3D.size() / 3, 1), m_isSynchronous(true) {} +void CTexture::createFromDma(const Aquamarine::SDMABUFAttrs& attrs, void* image) { + if (!g_pHyprOpenGL->m_proc.glEGLImageTargetTexture2DOES) { + Log::logger->log(Log::ERR, "Cannot create a dmabuf texture: no glEGLImageTargetTexture2DOES"); + return; + } -bool ITexture::ok() { - return false; + m_opaque = NFormatUtils::isFormatOpaque(attrs.format); + + // #TODO external only formats should be external aswell. + // also needs a seperate color shader. + /*if (NFormatUtils::isFormatYUV(attrs.format)) { + m_target = GL_TEXTURE_EXTERNAL_OES; + m_type = TEXTURE_EXTERNAL; + } else {*/ + m_target = GL_TEXTURE_2D; + m_type = NFormatUtils::isFormatOpaque(attrs.format) ? TEXTURE_RGBX : TEXTURE_RGBA; + //} + + m_size = attrs.size; + allocate(); + m_eglImage = image; + + bind(); + setTexParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + setTexParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + GLCALL(g_pHyprOpenGL->m_proc.glEGLImageTargetTexture2DOES(m_target, image)); + unbind(); } -bool ITexture::isDMA() { - return false; +void CTexture::update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage) { + if (damage.empty()) + return; + + g_pHyprRenderer->makeEGLCurrent(); + + const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat); + ASSERT(format); + + bind(); + + if (format->swizzle.has_value()) + swizzle(format->swizzle.value()); + + bool alignmentChanged = false; + if (format->bytesPerBlock != 4) { + const GLint alignment = (stride % 4 == 0) ? 4 : 1; + GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, alignment)); + alignmentChanged = true; + } + + GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / format->bytesPerBlock)); + + damage.copy().intersect(CBox{{}, m_size}).forEachRect([&format, &pixels](const auto& rect) { + GLCALL(glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, rect.x1)); + GLCALL(glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, rect.y1)); + + int width = rect.x2 - rect.x1; + int height = rect.y2 - rect.y1; + GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x1, rect.y1, width, height, format->glFormat, format->glType, pixels)); + }); + + if (alignmentChanged) + GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 4)); + + GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0)); + GLCALL(glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0)); + GLCALL(glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0)); + + unbind(); + + if (m_keepDataCopy) { + m_dataCopy.resize(stride * m_size.y); + memcpy(m_dataCopy.data(), pixels, stride * m_size.y); + } } -const std::vector& ITexture::dataCopy() { +void CTexture::destroyTexture() { + if (m_texID) { + GLCALL(glDeleteTextures(1, &m_texID)); + m_texID = 0; + } + + if (m_eglImage) + g_pHyprOpenGL->m_proc.eglDestroyImageKHR(g_pHyprOpenGL->m_eglDisplay, m_eglImage); + m_eglImage = nullptr; + m_cachedStates.fill(std::nullopt); +} + +void CTexture::allocate() { + if (!m_texID) + GLCALL(glGenTextures(1, &m_texID)); +} + +const std::vector& CTexture::dataCopy() { return m_dataCopy; } + +void CTexture::bind() { + GLCALL(glBindTexture(m_target, m_texID)); +} + +void CTexture::unbind() { + GLCALL(glBindTexture(m_target, 0)); +} + +constexpr std::optional CTexture::getCacheStateIndex(GLenum pname) { + switch (pname) { + case GL_TEXTURE_WRAP_S: return TEXTURE_PAR_WRAP_S; + case GL_TEXTURE_WRAP_T: return TEXTURE_PAR_WRAP_T; + case GL_TEXTURE_MAG_FILTER: return TEXTURE_PAR_MAG_FILTER; + case GL_TEXTURE_MIN_FILTER: return TEXTURE_PAR_MIN_FILTER; + case GL_TEXTURE_SWIZZLE_R: return TEXTURE_PAR_SWIZZLE_R; + case GL_TEXTURE_SWIZZLE_B: return TEXTURE_PAR_SWIZZLE_B; + default: return std::nullopt; + } +} + +void CTexture::setTexParameter(GLenum pname, GLint param) { + const auto cacheIndex = getCacheStateIndex(pname); + + if (!cacheIndex) { + GLCALL(glTexParameteri(m_target, pname, param)); + return; + } + + const auto idx = cacheIndex.value(); + + if (m_cachedStates[idx] == param) + return; + + m_cachedStates[idx] = param; + GLCALL(glTexParameteri(m_target, pname, param)); +} + +void CTexture::swizzle(const std::array& colors) { + setTexParameter(GL_TEXTURE_SWIZZLE_R, colors.at(0)); + setTexParameter(GL_TEXTURE_SWIZZLE_G, colors.at(1)); + setTexParameter(GL_TEXTURE_SWIZZLE_B, colors.at(2)); + setTexParameter(GL_TEXTURE_SWIZZLE_A, colors.at(3)); +} diff --git a/src/render/Texture.hpp b/src/render/Texture.hpp index 38c3ff01..c2e9b2c3 100644 --- a/src/render/Texture.hpp +++ b/src/render/Texture.hpp @@ -3,7 +3,6 @@ #include "../defines.hpp" #include #include -#include class IHLBuffer; HYPRUTILS_FORWARD(Math, CRegion); @@ -12,47 +11,63 @@ enum eTextureType : int8_t { TEXTURE_INVALID = -1, // Invalid TEXTURE_RGBA = 0, // 4 channels TEXTURE_RGBX, // discard A - TEXTURE_3D_LUT, // 3D LUT TEXTURE_EXTERNAL, // EGLImage }; -class ITexture { +class CTexture { public: - ITexture(ITexture&) = delete; - ITexture(ITexture&&) = delete; - ITexture(const ITexture&&) = delete; - ITexture(const ITexture&) = delete; + CTexture(); - virtual ~ITexture() = default; + CTexture(CTexture&) = delete; + CTexture(CTexture&&) = delete; + CTexture(const CTexture&&) = delete; + CTexture(const CTexture&) = delete; - virtual void setTexParameter(GLenum pname, GLint param) = 0; - virtual void allocate(const Vector2D& size, uint32_t drmFormat = 0) = 0; - virtual void update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage) = 0; - virtual void bind() {}; - virtual void unbind() {}; - virtual bool ok(); - virtual bool isDMA(); + CTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy = false); + CTexture(const SP buffer, bool keepDataCopy = false); + // this ctor takes ownership of the eglImage. + CTexture(const Aquamarine::SDMABUFAttrs&, void* image); + ~CTexture(); + + void destroyTexture(); + void allocate(); + void update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage); const std::vector& dataCopy(); + void bind(); + void unbind(); + void setTexParameter(GLenum pname, GLint param); + void swizzle(const std::array& colors); - eTextureType m_type = TEXTURE_RGBA; - Vector2D m_size = {}; - eTransform m_transform = HYPRUTILS_TRANSFORM_NORMAL; - bool m_opaque = false; - + eTextureType m_type = TEXTURE_RGBA; + GLenum m_target = GL_TEXTURE_2D; + GLuint m_texID = 0; + Vector2D m_size = {}; + void* m_eglImage = nullptr; + eTransform m_transform = HYPRUTILS_TRANSFORM_NORMAL; + bool m_opaque = false; uint32_t m_drmFormat = 0; // for shm bool m_isSynchronous = false; - // TODO move to GLTexture - GLuint m_texID = 0; - GLenum magFilter = GL_LINEAR; // useNearestNeighbor overwrites these - GLenum minFilter = GL_LINEAR; + GLenum magFilter = GL_LINEAR; // useNearestNeighbor overwrites these + GLenum minFilter = GL_LINEAR; - protected: - ITexture() = default; - ITexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy = false, bool opaque = false); - ITexture(std::span lut3D, size_t N); + private: + enum eTextureParam : uint8_t { + TEXTURE_PAR_WRAP_S = 0, + TEXTURE_PAR_WRAP_T, + TEXTURE_PAR_MAG_FILTER, + TEXTURE_PAR_MIN_FILTER, + TEXTURE_PAR_SWIZZLE_R, + TEXTURE_PAR_SWIZZLE_B, + TEXTURE_PAR_LAST, + }; - bool m_keepDataCopy = false; - std::vector m_dataCopy; + void createFromShm(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size); + void createFromDma(const Aquamarine::SDMABUFAttrs&, void* image); + inline constexpr std::optional getCacheStateIndex(GLenum pname); + + bool m_keepDataCopy = false; + std::vector m_dataCopy; + std::array, TEXTURE_PAR_LAST> m_cachedStates; }; diff --git a/src/render/Transformer.hpp b/src/render/Transformer.hpp index 8f401859..048b1898 100644 --- a/src/render/Transformer.hpp +++ b/src/render/Transformer.hpp @@ -14,7 +14,7 @@ class IWindowTransformer { // called by Hyprland. For more data about what is being rendered, inspect render data. // returns the out fb. - virtual IFramebuffer* transform(IFramebuffer* in) = 0; + virtual CFramebuffer* transform(CFramebuffer* in) = 0; // called by Hyprland before a window main pass is started. virtual void preWindowRender(CSurfacePassElement::SRenderData* pRenderData); diff --git a/src/render/decorations/CHyprBorderDecoration.cpp b/src/render/decorations/CHyprBorderDecoration.cpp index 3e4f04a9..66a15fc8 100644 --- a/src/render/decorations/CHyprBorderDecoration.cpp +++ b/src/render/decorations/CHyprBorderDecoration.cpp @@ -117,12 +117,23 @@ void CHyprBorderDecoration::damageEntire() { if (!validMapped(m_window) || m_window->m_fullscreenState.internal == FSMODE_FULLSCREEN) return; - const auto GLOBAL_BOX = assignedBoxGlobal(); - const auto ROUNDING = m_window->rounding(); - const auto BORDERSIZE = m_window->getRealBorderSize() + 1; + auto surfaceBox = m_window->getWindowMainSurfaceBox(); + const auto ROUNDING = m_window->rounding(); + const auto ROUNDINGSIZE = ROUNDING - M_SQRT1_2 * ROUNDING + 2; + const auto BORDERSIZE = m_window->getRealBorderSize() + 1; - CRegion borderRegion(GLOBAL_BOX); - borderRegion.subtract(GLOBAL_BOX.copy().expand(-(BORDERSIZE + ROUNDING))); + const auto PWINDOWWORKSPACE = m_window->m_workspace; + if (PWINDOWWORKSPACE && PWINDOWWORKSPACE->m_renderOffset->isBeingAnimated() && !m_window->m_pinned) + surfaceBox.translate(PWINDOWWORKSPACE->m_renderOffset->value()); + surfaceBox.translate(m_window->m_floatingOffset); + + CBox surfaceBoxExpandedBorder = surfaceBox; + surfaceBoxExpandedBorder.expand(BORDERSIZE); + CBox surfaceBoxShrunkRounding = surfaceBox; + surfaceBoxShrunkRounding.expand(-ROUNDINGSIZE); + + CRegion borderRegion(surfaceBoxExpandedBorder); + borderRegion.subtract(surfaceBoxShrunkRounding); for (auto const& m : g_pCompositor->m_monitors) { if (!g_pHyprRenderer->shouldRenderWindow(m_window.lock(), m)) { diff --git a/src/render/decorations/CHyprDropShadowDecoration.cpp b/src/render/decorations/CHyprDropShadowDecoration.cpp index 5e1b6e8a..dd82abc5 100644 --- a/src/render/decorations/CHyprDropShadowDecoration.cpp +++ b/src/render/decorations/CHyprDropShadowDecoration.cpp @@ -155,9 +155,9 @@ void CHyprDropShadowDecoration::render(PHLMONITOR pMonitor, float const& a) { g_pHyprOpenGL->m_renderData.currentWindow = m_window; // we'll take the liberty of using this as it should not be used rn - auto alphaFB = g_pHyprOpenGL->m_renderData.pCurrentMonData->mirrorFB; - auto alphaSwapFB = g_pHyprOpenGL->m_renderData.pCurrentMonData->mirrorSwapFB; - auto LASTFB = g_pHyprOpenGL->m_renderData.currentFB; + CFramebuffer& alphaFB = g_pHyprOpenGL->m_renderData.pCurrentMonData->mirrorFB; + CFramebuffer& alphaSwapFB = g_pHyprOpenGL->m_renderData.pCurrentMonData->mirrorSwapFB; + auto* LASTFB = g_pHyprOpenGL->m_renderData.currentFB; fullBox.scale(pMonitor->m_scale).round(); @@ -188,7 +188,7 @@ void CHyprDropShadowDecoration::render(PHLMONITOR pMonitor, float const& a) { g_pHyprOpenGL->m_renderData.damage.subtract(windowBox.copy().expand(-ROUNDING * pMonitor->m_scale)).intersect(saveDamage); g_pHyprOpenGL->m_renderData.renderModif.applyToRegion(g_pHyprOpenGL->m_renderData.damage); - alphaFB->bind(); + alphaFB.bind(); // build the matte // 10-bit formats have dogshit alpha channels, so we have to use the matte to its fullest. @@ -202,7 +202,7 @@ void CHyprDropShadowDecoration::render(PHLMONITOR pMonitor, float const& a) { g_pHyprOpenGL->renderRect(windowBox, CHyprColor(0, 0, 0, 1.0), {.round = (ROUNDING + 1 /* This fixes small pixel gaps. */) * pMonitor->m_scale, .roundingPower = ROUNDINGPOWER}); - alphaSwapFB->bind(); + alphaSwapFB.bind(); // alpha swap just has the shadow color. It will be the "texture" to render. g_pHyprOpenGL->renderRect(fullBox, PWINDOW->m_realShadowColor->value().stripA(), {.round = 0}); @@ -213,7 +213,7 @@ void CHyprDropShadowDecoration::render(PHLMONITOR pMonitor, float const& a) { g_pHyprOpenGL->pushMonitorTransformEnabled(true); g_pHyprOpenGL->setRenderModifEnabled(false); - g_pHyprOpenGL->renderTextureMatte(alphaSwapFB->getTexture(), monbox, alphaFB); + g_pHyprOpenGL->renderTextureMatte(alphaSwapFB.getTexture(), monbox, alphaFB); g_pHyprOpenGL->setRenderModifEnabled(true); g_pHyprOpenGL->popMonitorTransformEnabled(); diff --git a/src/render/decorations/CHyprGroupBarDecoration.cpp b/src/render/decorations/CHyprGroupBarDecoration.cpp index 6ce69261..beb5efcd 100644 --- a/src/render/decorations/CHyprGroupBarDecoration.cpp +++ b/src/render/decorations/CHyprGroupBarDecoration.cpp @@ -13,10 +13,10 @@ #include "../../layout/supplementary/DragController.hpp" // shared things to conserve VRAM -static SP m_tGradientActive; -static SP m_tGradientInactive; -static SP m_tGradientLockedActive; -static SP m_tGradientLockedInactive; +static SP m_tGradientActive = makeShared(); +static SP m_tGradientInactive = makeShared(); +static SP m_tGradientLockedActive = makeShared(); +static SP m_tGradientLockedInactive = makeShared(); constexpr int BAR_TEXT_PAD = 2; @@ -24,16 +24,7 @@ CHyprGroupBarDecoration::CHyprGroupBarDecoration(PHLWINDOW pWindow) : IHyprWindo static auto PGRADIENTS = CConfigValue("group:groupbar:enabled"); static auto PENABLED = CConfigValue("group:groupbar:gradients"); - if (!m_tGradientActive) - m_tGradientActive = g_pHyprRenderer->createTexture(); - if (!m_tGradientInactive) - m_tGradientInactive = g_pHyprRenderer->createTexture(); - if (!m_tGradientLockedActive) - m_tGradientLockedActive = g_pHyprRenderer->createTexture(); - if (!m_tGradientLockedInactive) - m_tGradientLockedInactive = g_pHyprRenderer->createTexture(); - - if (!m_tGradientActive->ok() && *PENABLED && *PGRADIENTS) + if (m_tGradientActive->m_texID == 0 && *PENABLED && *PGRADIENTS) refreshGroupBarGradients(); } @@ -205,7 +196,7 @@ void CHyprGroupBarDecoration::draw(PHLMONITOR pMonitor, float const& a) { if (*PGRADIENTS) { const auto GRADIENTTEX = (m_dwGroupMembers[WINDOWINDEX] == Desktop::focusState()->window() ? (GROUPLOCKED ? m_tGradientLockedActive : m_tGradientActive) : (GROUPLOCKED ? m_tGradientLockedInactive : m_tGradientInactive)); - if (GRADIENTTEX->ok()) { + if (GRADIENTTEX->m_texID) { CTexPassElement::SRenderData data; data.tex = GRADIENTTEX; data.blur = blur; @@ -243,7 +234,7 @@ void CHyprGroupBarDecoration::draw(PHLMONITOR pMonitor, float const& a) { Vector2D{(m_barWidth - (*PTEXTPADDING * 2)) * pMonitor->m_scale, (*PTITLEFONTSIZE + 2L * BAR_TEXT_PAD) * pMonitor->m_scale}, pMonitor->m_scale)) .get(); - SP titleTex; + SP titleTex; if (m_dwGroupMembers[WINDOWINDEX] == Desktop::focusState()->window()) titleTex = GROUPLOCKED ? pTitleTex->m_texLockedActive : pTitleTex->m_texActive; else @@ -316,7 +307,7 @@ CTitleTex::CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float #undef RENDER_TEXT } -static void renderGradientTo(SP tex, CGradientValueData* grad) { +static void renderGradientTo(SP tex, CGradientValueData* grad) { if (!Desktop::focusState()->monitor()) return; @@ -348,7 +339,15 @@ static void renderGradientTo(SP tex, CGradientValueData* grad) { cairo_surface_flush(CAIROSURFACE); // copy the data to an OpenGL texture we have - tex = g_pHyprRenderer->createTexture(CAIROSURFACE); + const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); + tex->allocate(); + tex->bind(); + tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST); + tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST); + tex->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); + tex->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bufferSize.x, bufferSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); // delete cairo cairo_destroy(CAIRO); @@ -368,11 +367,13 @@ void refreshGroupBarGradients() { auto* const GROUPCOLACTIVELOCKED = sc((PGROUPCOLACTIVELOCKED.ptr())->getData()); auto* const GROUPCOLINACTIVELOCKED = sc((PGROUPCOLINACTIVELOCKED.ptr())->getData()); - if (m_tGradientActive && m_tGradientActive->ok()) { - m_tGradientActive.reset(); - m_tGradientInactive.reset(); - m_tGradientLockedActive.reset(); - m_tGradientLockedInactive.reset(); + g_pHyprRenderer->makeEGLCurrent(); + + if (m_tGradientActive->m_texID != 0) { + m_tGradientActive->destroyTexture(); + m_tGradientInactive->destroyTexture(); + m_tGradientLockedActive->destroyTexture(); + m_tGradientLockedInactive->destroyTexture(); } if (!*PENABLED || !*PGRADIENTS) diff --git a/src/render/decorations/CHyprGroupBarDecoration.hpp b/src/render/decorations/CHyprGroupBarDecoration.hpp index 5c3f4ae5..3e5d3c2d 100644 --- a/src/render/decorations/CHyprGroupBarDecoration.hpp +++ b/src/render/decorations/CHyprGroupBarDecoration.hpp @@ -12,10 +12,10 @@ class CTitleTex { CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float monitorScale); ~CTitleTex() = default; - SP m_texActive; - SP m_texInactive; - SP m_texLockedActive; - SP m_texLockedInactive; + SP m_texActive; + SP m_texInactive; + SP m_texLockedActive; + SP m_texLockedInactive; std::string m_content; PHLWINDOWREF m_windowOwner; diff --git a/src/render/gl/GLFramebuffer.cpp b/src/render/gl/GLFramebuffer.cpp deleted file mode 100644 index d821f766..00000000 --- a/src/render/gl/GLFramebuffer.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include "GLFramebuffer.hpp" -#include "../OpenGL.hpp" -#include "../Renderer.hpp" -#include "macros.hpp" -#include "render/Framebuffer.hpp" - -CGLFramebuffer::CGLFramebuffer() : IFramebuffer() {} -CGLFramebuffer::CGLFramebuffer(const std::string& name) : IFramebuffer(name) {} - -bool CGLFramebuffer::internalAlloc(int w, int h, uint32_t drmFormat) { - g_pHyprRenderer->makeEGLCurrent(); - - bool firstAlloc = false; - - if (!m_tex) { - m_tex = g_pHyprRenderer->createTexture(); - m_tex->allocate({w, h}); - m_tex->bind(); - m_tex->setTexParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - m_tex->setTexParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - m_tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); - m_tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - firstAlloc = true; - } - - if (!m_fbAllocated) { - glGenFramebuffers(1, &m_fb); - m_fbAllocated = true; - firstAlloc = true; - } - - if (firstAlloc) { - const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat); - m_tex->bind(); - glTexImage2D(GL_TEXTURE_2D, 0, format->glInternalFormat ? format->glInternalFormat : format->glFormat, w, h, 0, format->glFormat, format->glType, nullptr); - glBindFramebuffer(GL_FRAMEBUFFER, m_fb); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_tex->m_texID, 0); - - if (m_stencilTex && m_stencilTex->ok()) { - m_stencilTex->bind(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, w, h, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_stencilTex->m_texID, 0); - - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - } - - auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - RASSERT((status == GL_FRAMEBUFFER_COMPLETE), "Framebuffer incomplete, couldn't create! (FB status: {}, GL Error: 0x{:x})", status, sc(glGetError())); - - if (m_stencilTex && m_stencilTex->ok()) - m_stencilTex->unbind(); - - Log::logger->log(Log::DEBUG, "Framebuffer created, status {}", status); - } - - glBindTexture(GL_TEXTURE_2D, 0); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - return true; -} - -void CGLFramebuffer::addStencil(SP tex) { - if (m_stencilTex == tex) - return; - - RASSERT(!m_fbAllocated, "Should add stencil tex prior to FB allocation") - m_stencilTex = tex; -} - -void CGLFramebuffer::bind() { - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fb); - - if (g_pHyprOpenGL) - g_pHyprOpenGL->setViewport(0, 0, g_pHyprOpenGL->m_renderData.pMonitor->m_pixelSize.x, g_pHyprOpenGL->m_renderData.pMonitor->m_pixelSize.y); - else - glViewport(0, 0, m_size.x, m_size.y); -} - -void CGLFramebuffer::unbind() { - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); -} - -void CGLFramebuffer::release() { - if (m_fbAllocated) { - glBindFramebuffer(GL_FRAMEBUFFER, m_fb); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - glDeleteFramebuffers(1, &m_fb); - m_fbAllocated = false; - m_fb = 0; - } - - if (m_tex) - m_tex.reset(); - - m_size = Vector2D(); -} - -bool CGLFramebuffer::readPixels(CHLBufferReference buffer, uint32_t offsetX, uint32_t offsetY, uint32_t width, uint32_t height) { - auto shm = buffer->shm(); - auto [pixelData, fmt, bufLen] = buffer->beginDataPtr(0); // no need for end, cuz it's shm - - const auto PFORMAT = NFormatUtils::getPixelFormatFromDRM(shm.format); - if (!PFORMAT) { - LOGM(Log::ERR, "Can't copy: failed to find a pixel format"); - return false; - } - - g_pHyprRenderer->makeEGLCurrent(); - glBindFramebuffer(GL_READ_FRAMEBUFFER, getFBID()); - bind(); - - glPixelStorei(GL_PACK_ALIGNMENT, 1); - - uint32_t packStride = NFormatUtils::minStride(PFORMAT, m_size.x); - int glFormat = PFORMAT->glFormat; - - if (glFormat == GL_RGBA) - glFormat = GL_BGRA_EXT; - - if (glFormat != GL_BGRA_EXT && glFormat != GL_RGB) { - if (PFORMAT->swizzle.has_value()) { - std::array RGBA = SWIZZLE_RGBA; - std::array BGRA = SWIZZLE_BGRA; - if (PFORMAT->swizzle == RGBA) - glFormat = GL_RGBA; - else if (PFORMAT->swizzle == BGRA) - glFormat = GL_BGRA_EXT; - else { - LOGM(Log::ERR, "Copied frame via shm might be broken or color flipped"); - glFormat = GL_RGBA; - } - } - } - - // This could be optimized by using a pixel buffer object to make this async, - // but really clients should just use a dma buffer anyways. - if (packStride == sc(shm.stride)) { - glReadPixels(offsetX, offsetY, width > 0 ? width : m_size.x, height > 0 ? height : m_size.y, glFormat, PFORMAT->glType, pixelData); - } else { - const auto h = height > 0 ? height : m_size.y; - for (size_t i = 0; i < h; ++i) { - uint32_t y = i; - glReadPixels(offsetX, offsetY + y, width > 0 ? width : m_size.x, 1, glFormat, PFORMAT->glType, pixelData + i * shm.stride); - } - } - - unbind(); - glPixelStorei(GL_PACK_ALIGNMENT, 4); - - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - return true; -} - -CGLFramebuffer::~CGLFramebuffer() { - release(); -} - -GLuint CGLFramebuffer::getFBID() { - return m_fbAllocated ? m_fb : 0; -} - -void CGLFramebuffer::invalidate(const std::vector& attachments) { - if (!isAllocated()) - return; - - glInvalidateFramebuffer(GL_FRAMEBUFFER, attachments.size(), attachments.data()); -} diff --git a/src/render/gl/GLFramebuffer.hpp b/src/render/gl/GLFramebuffer.hpp deleted file mode 100644 index c171444e..00000000 --- a/src/render/gl/GLFramebuffer.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "../../defines.hpp" -#include "../Texture.hpp" -#include "../Framebuffer.hpp" -#include - -class CGLFramebuffer : public IFramebuffer { - public: - CGLFramebuffer(); - CGLFramebuffer(const std::string& name); - ~CGLFramebuffer(); - - void addStencil(SP tex) override; - void release() override; - bool readPixels(CHLBufferReference buffer, uint32_t offsetX = 0, uint32_t offsetY = 0, uint32_t width = 0, uint32_t height = 0) override; - - void bind() override; - void unbind(); - GLuint getFBID(); - void invalidate(const std::vector& attachments); - - protected: - bool internalAlloc(int w, int h, uint32_t format = DRM_FORMAT_ARGB8888) override; - - private: - GLuint m_fb = -1; - - friend class CGLRenderbuffer; -}; diff --git a/src/render/gl/GLRenderbuffer.cpp b/src/render/gl/GLRenderbuffer.cpp deleted file mode 100644 index 8299d0e4..00000000 --- a/src/render/gl/GLRenderbuffer.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "GLRenderbuffer.hpp" -#include "../Renderer.hpp" -#include "../OpenGL.hpp" -#include "../../Compositor.hpp" -#include "../Framebuffer.hpp" -#include "GLFramebuffer.hpp" -#include "render/Renderbuffer.hpp" -#include -#include -#include - -#include - -CGLRenderbuffer::~CGLRenderbuffer() { - if (!g_pCompositor || g_pCompositor->m_isShuttingDown || !g_pHyprRenderer) - return; - - g_pHyprRenderer->makeEGLCurrent(); - - unbind(); - m_framebuffer->release(); - - if (m_rbo) - glDeleteRenderbuffers(1, &m_rbo); - - if (m_image != EGL_NO_IMAGE_KHR) - g_pHyprOpenGL->m_proc.eglDestroyImageKHR(g_pHyprOpenGL->m_eglDisplay, m_image); -} - -CGLRenderbuffer::CGLRenderbuffer(SP buffer, uint32_t format) : IRenderbuffer(buffer, format) { - auto dma = buffer->dmabuf(); - - m_image = g_pHyprOpenGL->createEGLImage(dma); - if (m_image == EGL_NO_IMAGE_KHR) { - Log::logger->log(Log::ERR, "rb: createEGLImage failed"); - return; - } - - glGenRenderbuffers(1, &m_rbo); - glBindRenderbuffer(GL_RENDERBUFFER, m_rbo); - g_pHyprOpenGL->m_proc.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, m_image); - glBindRenderbuffer(GL_RENDERBUFFER, 0); - - m_framebuffer = makeShared(); - glGenFramebuffers(1, &GLFB(m_framebuffer)->m_fb); - GLFB(m_framebuffer)->m_fbAllocated = true; - m_framebuffer->m_size = buffer->size; - m_framebuffer->m_drmFormat = dma.format; - m_framebuffer->bind(); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rbo); - - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - Log::logger->log(Log::ERR, "rbo: glCheckFramebufferStatus failed"); - return; - } - - GLFB(m_framebuffer)->unbind(); - - m_listeners.destroyBuffer = buffer->events.destroy.listen([this] { g_pHyprRenderer->onRenderbufferDestroy(this); }); - - m_good = true; -} - -void CGLRenderbuffer::bind() { - g_pHyprRenderer->makeEGLCurrent(); - m_framebuffer->bind(); -} - -void CGLRenderbuffer::unbind() { - GLFB(m_framebuffer)->unbind(); -} diff --git a/src/render/gl/GLRenderbuffer.hpp b/src/render/gl/GLRenderbuffer.hpp deleted file mode 100644 index 8367f702..00000000 --- a/src/render/gl/GLRenderbuffer.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "../../helpers/memory/Memory.hpp" -#include "../Renderbuffer.hpp" -#include - -class CMonitor; - -class CGLRenderbuffer : public IRenderbuffer { - public: - CGLRenderbuffer(SP buffer, uint32_t format); - ~CGLRenderbuffer(); - - void bind() override; - void unbind() override; - - private: - void* m_image = nullptr; - GLuint m_rbo = 0; -}; diff --git a/src/render/gl/GLTexture.cpp b/src/render/gl/GLTexture.cpp deleted file mode 100644 index 6a1fb172..00000000 --- a/src/render/gl/GLTexture.cpp +++ /dev/null @@ -1,223 +0,0 @@ -#include "GLTexture.hpp" -#include "../Renderer.hpp" -#include "../../Compositor.hpp" -#include "../../helpers/Format.hpp" -#include "render/Texture.hpp" -#include - -CGLTexture::CGLTexture(bool opaque) { - m_opaque = opaque; -} - -CGLTexture::~CGLTexture() { - if (!g_pCompositor || g_pCompositor->m_isShuttingDown || !g_pHyprRenderer) - return; - - g_pHyprRenderer->makeEGLCurrent(); - if (m_texID) { - GLCALL(glDeleteTextures(1, &m_texID)); - m_texID = 0; - } - - if (m_eglImage) - g_pHyprOpenGL->m_proc.eglDestroyImageKHR(g_pHyprOpenGL->m_eglDisplay, m_eglImage); - m_eglImage = nullptr; - m_cachedStates.fill(std::nullopt); -} - -CGLTexture::CGLTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size_, bool keepDataCopy, bool opaque) : - ITexture(drmFormat, pixels, stride, size_, keepDataCopy, opaque) { - - g_pHyprRenderer->makeEGLCurrent(); - - const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat); - ASSERT(format); - - m_type = format->withAlpha ? TEXTURE_RGBA : TEXTURE_RGBX; - m_size = size_; - m_isSynchronous = true; - m_target = GL_TEXTURE_2D; - allocate(size_); - bind(); - setTexParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - setTexParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - if (format->swizzle.has_value()) - swizzle(format->swizzle.value()); - - bool alignmentChanged = false; - if (format->bytesPerBlock != 4) { - const GLint alignment = (stride % 4 == 0) ? 4 : 1; - GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, alignment)); - alignmentChanged = true; - } - - GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / format->bytesPerBlock)); - GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, format->glInternalFormat ? format->glInternalFormat : format->glFormat, size_.x, size_.y, 0, format->glFormat, format->glType, pixels)); - GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0)); - if (alignmentChanged) - GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 4)); - - unbind(); -} - -CGLTexture::CGLTexture(const Aquamarine::SDMABUFAttrs& attrs, void* image, bool opaque) { - m_opaque = opaque; - if (!g_pHyprOpenGL->m_proc.glEGLImageTargetTexture2DOES) { - Log::logger->log(Log::ERR, "Cannot create a dmabuf texture: no glEGLImageTargetTexture2DOES"); - return; - } - - m_opaque = NFormatUtils::isFormatOpaque(attrs.format); - - // #TODO external only formats should be external aswell. - // also needs a seperate color shader. - /*if (NFormatUtils::isFormatYUV(attrs.format)) { - m_target = GL_TEXTURE_EXTERNAL_OES; - m_type = TEXTURE_EXTERNAL; - } else {*/ - m_target = GL_TEXTURE_2D; - m_type = NFormatUtils::isFormatOpaque(attrs.format) ? TEXTURE_RGBX : TEXTURE_RGBA; - //} - - allocate(attrs.size); - m_eglImage = image; - - bind(); - setTexParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - setTexParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - GLCALL(g_pHyprOpenGL->m_proc.glEGLImageTargetTexture2DOES(m_target, image)); - unbind(); -} - -CGLTexture::CGLTexture(std::span lut3D, size_t N) : ITexture(lut3D, N), m_target(GL_TEXTURE_3D) { - allocate({}); - bind(); - - GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); - setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); - setTexParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - setTexParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - setTexParameter(GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - - // Expand RGB->RGBA on upload (alpha=1) - std::vector rgba; - rgba.resize(N * N * N * 4); - for (size_t i = 0, j = 0; i < N * N * N; ++i, j += 3) { - rgba[i * 4 + 0] = lut3D[j + 0]; - rgba[i * 4 + 1] = lut3D[j + 1]; - rgba[i * 4 + 2] = lut3D[j + 2]; - rgba[i * 4 + 3] = 1.F; - } - - GLCALL(glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA16F, N, N, N, 0, GL_RGBA, GL_FLOAT, rgba.data())); - - unbind(); -} - -void CGLTexture::update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage) { - if (damage.empty()) - return; - - g_pHyprRenderer->makeEGLCurrent(); - - const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat); - ASSERT(format); - - bind(); - - if (format->swizzle.has_value()) - swizzle(format->swizzle.value()); - - bool alignmentChanged = false; - if (format->bytesPerBlock != 4) { - const GLint alignment = (stride % 4 == 0) ? 4 : 1; - GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, alignment)); - alignmentChanged = true; - } - - GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / format->bytesPerBlock)); - - damage.copy().intersect(CBox{{}, m_size}).forEachRect([&format, &pixels](const auto& rect) { - GLCALL(glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, rect.x1)); - GLCALL(glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, rect.y1)); - - int width = rect.x2 - rect.x1; - int height = rect.y2 - rect.y1; - GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x1, rect.y1, width, height, format->glFormat, format->glType, pixels)); - }); - - if (alignmentChanged) - GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 4)); - - GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0)); - GLCALL(glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0)); - GLCALL(glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0)); - - unbind(); - - if (m_keepDataCopy) { - m_dataCopy.resize(stride * m_size.y); - memcpy(m_dataCopy.data(), pixels, stride * m_size.y); - } -} - -void CGLTexture::allocate(const Vector2D& size, uint32_t drmFormat) { - if (!m_texID) - GLCALL(glGenTextures(1, &m_texID)); - m_size = size; - m_drmFormat = drmFormat; -} - -void CGLTexture::bind() { - GLCALL(glBindTexture(m_target, m_texID)); -} - -void CGLTexture::unbind() { - GLCALL(glBindTexture(m_target, 0)); -} - -bool CGLTexture::ok() { - return m_texID > 0; -} - -bool CGLTexture::isDMA() { - return m_eglImage; -} - -constexpr std::optional CGLTexture::getCacheStateIndex(GLenum pname) { - switch (pname) { - case GL_TEXTURE_WRAP_S: return TEXTURE_PAR_WRAP_S; - case GL_TEXTURE_WRAP_T: return TEXTURE_PAR_WRAP_T; - case GL_TEXTURE_MAG_FILTER: return TEXTURE_PAR_MAG_FILTER; - case GL_TEXTURE_MIN_FILTER: return TEXTURE_PAR_MIN_FILTER; - case GL_TEXTURE_SWIZZLE_R: return TEXTURE_PAR_SWIZZLE_R; - case GL_TEXTURE_SWIZZLE_B: return TEXTURE_PAR_SWIZZLE_B; - default: return std::nullopt; - } -} - -void CGLTexture::setTexParameter(GLenum pname, GLint param) { - const auto cacheIndex = getCacheStateIndex(pname); - - if (!cacheIndex) { - GLCALL(glTexParameteri(m_target, pname, param)); - return; - } - - const auto idx = cacheIndex.value(); - - if (m_cachedStates[idx] == param) - return; - - m_cachedStates[idx] = param; - GLCALL(glTexParameteri(m_target, pname, param)); -} - -void CGLTexture::swizzle(const std::array& colors) { - setTexParameter(GL_TEXTURE_SWIZZLE_R, colors.at(0)); - setTexParameter(GL_TEXTURE_SWIZZLE_G, colors.at(1)); - setTexParameter(GL_TEXTURE_SWIZZLE_B, colors.at(2)); - setTexParameter(GL_TEXTURE_SWIZZLE_A, colors.at(3)); -} diff --git a/src/render/gl/GLTexture.hpp b/src/render/gl/GLTexture.hpp deleted file mode 100644 index 34510e90..00000000 --- a/src/render/gl/GLTexture.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include "../Texture.hpp" -#include -#include - -class CGLTexture : public ITexture { - public: - using ITexture::ITexture; - - CGLTexture(CGLTexture&) = delete; - CGLTexture(CGLTexture&&) = delete; - CGLTexture(const CGLTexture&&) = delete; - CGLTexture(const CGLTexture&) = delete; - - CGLTexture(bool opaque = false); - CGLTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy = false, bool opaque = false); - CGLTexture(const Aquamarine::SDMABUFAttrs&, void* image, bool opaque = false); - CGLTexture(std::span lut3D, size_t N); - ~CGLTexture(); - - void allocate(const Vector2D& size, uint32_t drmFormat = 0) override; - void update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage) override; - void bind() override; - void unbind() override; - void setTexParameter(GLenum pname, GLint param) override; - bool ok() override; - bool isDMA() override; - - private: - void* m_eglImage = nullptr; - - enum eTextureParam : uint8_t { - TEXTURE_PAR_WRAP_S = 0, - TEXTURE_PAR_WRAP_T, - TEXTURE_PAR_MAG_FILTER, - TEXTURE_PAR_MIN_FILTER, - TEXTURE_PAR_SWIZZLE_R, - TEXTURE_PAR_SWIZZLE_B, - TEXTURE_PAR_LAST, - }; - - GLenum m_target = GL_TEXTURE_2D; - - void swizzle(const std::array& colors); - constexpr std::optional getCacheStateIndex(GLenum pname); - - std::array, TEXTURE_PAR_LAST> m_cachedStates; -}; diff --git a/src/render/pass/FramebufferElement.cpp b/src/render/pass/FramebufferElement.cpp index bc7c686a..77a29fba 100644 --- a/src/render/pass/FramebufferElement.cpp +++ b/src/render/pass/FramebufferElement.cpp @@ -6,7 +6,7 @@ CFramebufferElement::CFramebufferElement(const CFramebufferElement::SFramebuffer } void CFramebufferElement::draw(const CRegion& damage) { - SP fb = nullptr; + CFramebuffer* fb = nullptr; if (m_data.main) { switch (m_data.framebufferID) { @@ -22,12 +22,12 @@ void CFramebufferElement::draw(const CRegion& damage) { } else { switch (m_data.framebufferID) { - case FB_MONITOR_RENDER_EXTRA_OFFLOAD: fb = g_pHyprOpenGL->m_renderData.pCurrentMonData->offloadFB; break; - case FB_MONITOR_RENDER_EXTRA_MIRROR: fb = g_pHyprOpenGL->m_renderData.pCurrentMonData->mirrorFB; break; - case FB_MONITOR_RENDER_EXTRA_MIRROR_SWAP: fb = g_pHyprOpenGL->m_renderData.pCurrentMonData->mirrorSwapFB; break; - case FB_MONITOR_RENDER_EXTRA_OFF_MAIN: fb = g_pHyprOpenGL->m_renderData.pCurrentMonData->offMainFB; break; - case FB_MONITOR_RENDER_EXTRA_MONITOR_MIRROR: fb = g_pHyprOpenGL->m_renderData.pCurrentMonData->monitorMirrorFB; break; - case FB_MONITOR_RENDER_EXTRA_BLUR: fb = g_pHyprOpenGL->m_renderData.pCurrentMonData->blurFB; break; + case FB_MONITOR_RENDER_EXTRA_OFFLOAD: fb = &g_pHyprOpenGL->m_renderData.pCurrentMonData->offloadFB; break; + case FB_MONITOR_RENDER_EXTRA_MIRROR: fb = &g_pHyprOpenGL->m_renderData.pCurrentMonData->mirrorFB; break; + case FB_MONITOR_RENDER_EXTRA_MIRROR_SWAP: fb = &g_pHyprOpenGL->m_renderData.pCurrentMonData->mirrorSwapFB; break; + case FB_MONITOR_RENDER_EXTRA_OFF_MAIN: fb = &g_pHyprOpenGL->m_renderData.pCurrentMonData->offMainFB; break; + case FB_MONITOR_RENDER_EXTRA_MONITOR_MIRROR: fb = &g_pHyprOpenGL->m_renderData.pCurrentMonData->monitorMirrorFB; break; + case FB_MONITOR_RENDER_EXTRA_BLUR: fb = &g_pHyprOpenGL->m_renderData.pCurrentMonData->blurFB; break; } if (!fb) { diff --git a/src/render/pass/Pass.cpp b/src/render/pass/Pass.cpp index a4436516..3c82c84c 100644 --- a/src/render/pass/Pass.cpp +++ b/src/render/pass/Pass.cpp @@ -216,7 +216,7 @@ void CRenderPass::renderDebugData() { std::unordered_map offsets; // render focus stuff - auto renderHLSurface = [&offsets](SP texture, SP surface, const CHyprColor& color) { + auto renderHLSurface = [&offsets](SP texture, SP surface, const CHyprColor& color) { if (!surface || !texture) return; diff --git a/src/render/pass/Pass.hpp b/src/render/pass/Pass.hpp index b45af88b..435b5301 100644 --- a/src/render/pass/Pass.hpp +++ b/src/render/pass/Pass.hpp @@ -4,7 +4,7 @@ #include "PassElement.hpp" class CGradientValueData; -class ITexture; +class CTexture; class CRenderPass { public: @@ -36,7 +36,7 @@ class CRenderPass { struct { bool present = false; - SP keyboardFocusText, pointerFocusText, lastWindowText; + SP keyboardFocusText, pointerFocusText, lastWindowText; } m_debugData; friend class CHyprOpenGLImpl; diff --git a/src/render/pass/SurfacePassElement.hpp b/src/render/pass/SurfacePassElement.hpp index 058744de..f4dbb45a 100644 --- a/src/render/pass/SurfacePassElement.hpp +++ b/src/render/pass/SurfacePassElement.hpp @@ -4,7 +4,7 @@ #include "../../helpers/time/Time.hpp" class CWLSurfaceResource; -class ITexture; +class CTexture; class CSyncTimeline; class CSurfacePassElement : public IPassElement { @@ -16,7 +16,7 @@ class CSurfacePassElement : public IPassElement { void* data = nullptr; SP surface = nullptr; - SP texture = nullptr; + SP texture = nullptr; bool mainSurface = true; double w = 0, h = 0; int rounding = 0; diff --git a/src/render/pass/TexPassElement.hpp b/src/render/pass/TexPassElement.hpp index 770e8b05..a922843d 100644 --- a/src/render/pass/TexPassElement.hpp +++ b/src/render/pass/TexPassElement.hpp @@ -3,13 +3,13 @@ #include class CWLSurfaceResource; -class ITexture; +class CTexture; class CSyncTimeline; class CTexPassElement : public IPassElement { public: struct SRenderData { - SP tex; + SP tex; CBox box; float a = 1.F; float blurA = 1.F; diff --git a/src/render/pass/TextureMatteElement.cpp b/src/render/pass/TextureMatteElement.cpp index 8023df8b..aeeeabc6 100644 --- a/src/render/pass/TextureMatteElement.cpp +++ b/src/render/pass/TextureMatteElement.cpp @@ -9,11 +9,11 @@ void CTextureMatteElement::draw(const CRegion& damage) { if (m_data.disableTransformAndModify) { g_pHyprOpenGL->pushMonitorTransformEnabled(true); g_pHyprOpenGL->setRenderModifEnabled(false); - g_pHyprOpenGL->renderTextureMatte(m_data.tex, m_data.box, m_data.fb); + g_pHyprOpenGL->renderTextureMatte(m_data.tex, m_data.box, *m_data.fb); g_pHyprOpenGL->setRenderModifEnabled(true); g_pHyprOpenGL->popMonitorTransformEnabled(); } else - g_pHyprOpenGL->renderTextureMatte(m_data.tex, m_data.box, m_data.fb); + g_pHyprOpenGL->renderTextureMatte(m_data.tex, m_data.box, *m_data.fb); } bool CTextureMatteElement::needsLiveBlur() { diff --git a/src/render/pass/TextureMatteElement.hpp b/src/render/pass/TextureMatteElement.hpp index 273c6474..57d0e1e3 100644 --- a/src/render/pass/TextureMatteElement.hpp +++ b/src/render/pass/TextureMatteElement.hpp @@ -2,14 +2,14 @@ #include "PassElement.hpp" #include "../Framebuffer.hpp" -class ITexture; +class CTexture; class CTextureMatteElement : public IPassElement { public: struct STextureMatteData { CBox box; - SP tex; - SP fb; + SP tex; + SP fb; bool disableTransformAndModify = false; }; diff --git a/src/render/shaders/glsl/CM.glsl b/src/render/shaders/glsl/CM.glsl index 323a3008..36c95a90 100644 --- a/src/render/shaders/glsl/CM.glsl +++ b/src/render/shaders/glsl/CM.glsl @@ -1,27 +1,278 @@ -#ifndef ALLOW_INCLUDES -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable -#endif -#include "cm_helpers.glsl" - -uniform vec2 srcTFRange; -uniform vec2 dstTFRange; +uniform vec2 srcTFRange; +uniform vec2 dstTFRange; uniform float srcRefLuminance; -uniform mat3 convertMatrix; +uniform mat3 convertMatrix; -#if USE_ICC -uniform highp sampler3D iccLut3D; -uniform float iccLutSize; -#endif +#include "sdr_mod.glsl" -#if USE_SDR_MOD -uniform float sdrSaturation; -uniform float sdrBrightnessMultiplier; -#endif +//enum eTransferFunction +#define CM_TRANSFER_FUNCTION_BT1886 1 +#define CM_TRANSFER_FUNCTION_GAMMA22 2 +#define CM_TRANSFER_FUNCTION_GAMMA28 3 +#define CM_TRANSFER_FUNCTION_ST240 4 +#define CM_TRANSFER_FUNCTION_EXT_LINEAR 5 +#define CM_TRANSFER_FUNCTION_LOG_100 6 +#define CM_TRANSFER_FUNCTION_LOG_316 7 +#define CM_TRANSFER_FUNCTION_XVYCC 8 +#define CM_TRANSFER_FUNCTION_SRGB 9 +#define CM_TRANSFER_FUNCTION_EXT_SRGB 10 +#define CM_TRANSFER_FUNCTION_ST2084_PQ 11 +#define CM_TRANSFER_FUNCTION_ST428 12 +#define CM_TRANSFER_FUNCTION_HLG 13 -#if USE_TONEMAP -uniform float maxLuminance; -uniform float dstMaxLuminance; -uniform float dstRefLuminance; -#endif +// sRGB constants +#define SRGB_POW 2.4 +#define SRGB_CUT 0.0031308 +#define SRGB_SCALE 12.92 +#define SRGB_ALPHA 1.055 + +#define BT1886_POW (1.0 / 0.45) +#define BT1886_CUT 0.018053968510807 +#define BT1886_SCALE 4.5 +#define BT1886_ALPHA (1.0 + 5.5 * BT1886_CUT) + +// See http://car.france3.mars.free.fr/HD/INA-%2026%20jan%2006/SMPTE%20normes%20et%20confs/s240m.pdf +#define ST240_POW (1.0 / 0.45) +#define ST240_CUT 0.0228 +#define ST240_SCALE 4.0 +#define ST240_ALPHA 1.1115 + +#define ST428_POW 2.6 +#define ST428_SCALE (52.37 / 48.0) + +// PQ constants +#define PQ_M1 0.1593017578125 +#define PQ_M2 78.84375 +#define PQ_INV_M1 (1.0 / PQ_M1) +#define PQ_INV_M2 (1.0 / PQ_M2) +#define PQ_C1 0.8359375 +#define PQ_C2 18.8515625 +#define PQ_C3 18.6875 + +// HLG constants +#define HLG_D_CUT (1.0 / 12.0) +#define HLG_E_CUT 0.5 +#define HLG_A 0.17883277 +#define HLG_B 0.28466892 +#define HLG_C 0.55991073 + +#define SDR_MIN_LUMINANCE 0.2 +#define SDR_MAX_LUMINANCE 80.0 +#define HDR_MIN_LUMINANCE 0.005 +#define HDR_MAX_LUMINANCE 10000.0 +#define HLG_MAX_LUMINANCE 1000.0 + +#define M_E 2.718281828459045 + +// The primary source for these transfer functions is https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.1361-0-199802-W!!PDF-E.pdf +vec3 tfInvPQ(vec3 color) { + vec3 E = pow(clamp(color.rgb, vec3(0.0), vec3(1.0)), vec3(PQ_INV_M2)); + return pow( + (max(E - PQ_C1, vec3(0.0))) / (PQ_C2 - PQ_C3 * E), + vec3(PQ_INV_M1) + ); +} + +vec3 tfInvHLG(vec3 color) { + bvec3 isLow = lessThanEqual(color.rgb, vec3(HLG_E_CUT)); + vec3 lo = color.rgb * color.rgb / 3.0; + vec3 hi = (exp((color.rgb - HLG_C) / HLG_A) + HLG_B) / 12.0; + return mix(hi, lo, isLow); +} + +// Many transfer functions (including sRGB) follow the same pattern: a linear +// segment for small values and a power function for larger values. The +// following function implements this pattern from which sRGB, BT.1886, and +// others can be derived by plugging in the right constants. +vec3 tfInvLinPow(vec3 color, float gamma, float thres, float scale, float alpha) { + bvec3 isLow = lessThanEqual(color.rgb, vec3(thres * scale)); + vec3 lo = color.rgb / scale; + vec3 hi = pow((color.rgb + alpha - 1.0) / alpha, vec3(gamma)); + return mix(hi, lo, isLow); +} + +vec3 tfInvSRGB(vec3 color) { + return tfInvLinPow(color, SRGB_POW, SRGB_CUT, SRGB_SCALE, SRGB_ALPHA); +} + +vec3 tfInvExtSRGB(vec3 color) { + // EXT sRGB is the sRGB transfer function mirrored around 0. + return sign(color) * tfInvSRGB(abs(color)); +} + +vec3 tfInvBT1886(vec3 color) { + return tfInvLinPow(color, BT1886_POW, BT1886_CUT, BT1886_SCALE, BT1886_ALPHA); +} + +vec3 tfInvXVYCC(vec3 color) { + // The inverse transfer function for XVYCC is the BT1886 transfer function mirrored around 0, + // same as what EXT sRGB is to sRGB. + return sign(color) * tfInvBT1886(abs(color)); +} + +vec3 tfInvST240(vec3 color) { + return tfInvLinPow(color, ST240_POW, ST240_CUT, ST240_SCALE, ST240_ALPHA); +} + +// Forward transfer functions corresponding to the inverse functions above. +vec3 tfPQ(vec3 color) { + vec3 E = pow(clamp(color.rgb, vec3(0.0), vec3(1.0)), vec3(PQ_M1)); + return pow( + (vec3(PQ_C1) + PQ_C2 * E) / (vec3(1.0) + PQ_C3 * E), + vec3(PQ_M2) + ); +} + +vec3 tfHLG(vec3 color) { + bvec3 isLow = lessThanEqual(color.rgb, vec3(HLG_D_CUT)); + vec3 lo = sqrt(max(color.rgb, vec3(0.0)) * 3.0); + vec3 hi = HLG_A * log(max(12.0 * color.rgb - HLG_B, vec3(0.0001))) + HLG_C; + return mix(hi, lo, isLow); +} + +vec3 tfLinPow(vec3 color, float gamma, float thres, float scale, float alpha) { + bvec3 isLow = lessThanEqual(color.rgb, vec3(thres)); + vec3 lo = color.rgb * scale; + vec3 hi = pow(color.rgb, vec3(1.0 / gamma)) * alpha - (alpha - 1.0); + return mix(hi, lo, isLow); +} + +vec3 tfSRGB(vec3 color) { + return tfLinPow(color, SRGB_POW, SRGB_CUT, SRGB_SCALE, SRGB_ALPHA); +} + +vec3 tfExtSRGB(vec3 color) { + // EXT sRGB is the sRGB transfer function mirrored around 0. + return sign(color) * tfSRGB(abs(color)); +} + +vec3 tfBT1886(vec3 color) { + return tfLinPow(color, BT1886_POW, BT1886_CUT, BT1886_SCALE, BT1886_ALPHA); +} + +vec3 tfXVYCC(vec3 color) { + // The transfer function for XVYCC is the BT1886 transfer function mirrored around 0, + // same as what EXT sRGB is to sRGB. + return sign(color) * tfBT1886(abs(color)); +} + +vec3 tfST240(vec3 color) { + return tfLinPow(color, ST240_POW, ST240_CUT, ST240_SCALE, ST240_ALPHA); +} + +vec3 toLinearRGB(vec3 color, int tf) { + switch (tf) { + case CM_TRANSFER_FUNCTION_EXT_LINEAR: + return color; + case CM_TRANSFER_FUNCTION_ST2084_PQ: + return tfInvPQ(color); + case CM_TRANSFER_FUNCTION_GAMMA22: + return pow(max(color, vec3(0.0)), vec3(2.2)); + case CM_TRANSFER_FUNCTION_GAMMA28: + return pow(max(color, vec3(0.0)), vec3(2.8)); + case CM_TRANSFER_FUNCTION_HLG: + return tfInvHLG(color); + case CM_TRANSFER_FUNCTION_EXT_SRGB: + return tfInvExtSRGB(color); + case CM_TRANSFER_FUNCTION_BT1886: + return tfInvBT1886(color); + case CM_TRANSFER_FUNCTION_ST240: + return tfInvST240(color); + case CM_TRANSFER_FUNCTION_LOG_100: + return mix(exp((color - 1.0) * 2.0 * log(10.0)), vec3(0.0), lessThanEqual(color, vec3(0.0))); + case CM_TRANSFER_FUNCTION_LOG_316: + return mix(exp((color - 1.0) * 2.5 * log(10.0)), vec3(0.0), lessThanEqual(color, vec3(0.0))); + case CM_TRANSFER_FUNCTION_XVYCC: + return tfInvXVYCC(color); + case CM_TRANSFER_FUNCTION_ST428: + return pow(max(color, vec3(0.0)), vec3(ST428_POW)) * ST428_SCALE; + case CM_TRANSFER_FUNCTION_SRGB: + default: + return tfInvSRGB(color); + } +} + +vec4 toLinear(vec4 color, int tf) { + if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR) + return color; + + color.rgb /= max(color.a, 0.001); + color.rgb = toLinearRGB(color.rgb, tf); + color.rgb *= color.a; + return color; +} + +vec4 toNit(vec4 color, vec2 range) { + color.rgb = color.rgb * (range[1] - range[0]) + range[0]; + return color; +} + +vec3 fromLinearRGB(vec3 color, int tf) { + switch (tf) { + case CM_TRANSFER_FUNCTION_EXT_LINEAR: + return color; + case CM_TRANSFER_FUNCTION_ST2084_PQ: + return tfPQ(color); + case CM_TRANSFER_FUNCTION_GAMMA22: + return pow(max(color, vec3(0.0)), vec3(1.0 / 2.2)); + case CM_TRANSFER_FUNCTION_GAMMA28: + return pow(max(color, vec3(0.0)), vec3(1.0 / 2.8)); + case CM_TRANSFER_FUNCTION_HLG: + return tfHLG(color); + case CM_TRANSFER_FUNCTION_EXT_SRGB: + return tfExtSRGB(color); + case CM_TRANSFER_FUNCTION_BT1886: + return tfBT1886(color); + case CM_TRANSFER_FUNCTION_ST240: + return tfST240(color); + case CM_TRANSFER_FUNCTION_LOG_100: + return mix(1.0 + log(color) / log(10.0) / 2.0, vec3(0.0), lessThanEqual(color, vec3(0.01))); + case CM_TRANSFER_FUNCTION_LOG_316: + return mix(1.0 + log(color) / log(10.0) / 2.5, vec3(0.0), lessThanEqual(color, vec3(sqrt(10.0) / 1000.0))); + case CM_TRANSFER_FUNCTION_XVYCC: + return tfXVYCC(color); + case CM_TRANSFER_FUNCTION_ST428: + return pow(max(color, vec3(0.0)) / ST428_SCALE, vec3(1.0 / ST428_POW)); + case CM_TRANSFER_FUNCTION_SRGB: + default: + return tfSRGB(color); + } +} + +vec4 fromLinear(vec4 color, int tf) { + if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR) + return color; + + color.rgb /= max(color.a, 0.001); + color.rgb = fromLinearRGB(color.rgb, tf); + color.rgb *= color.a; + return color; +} + +vec4 fromLinearNit(vec4 color, int tf, vec2 range) { + if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR) + color.rgb = color.rgb / SDR_MAX_LUMINANCE; + else { + color.rgb /= max(color.a, 0.001); + color.rgb = (color.rgb - range[0]) / (range[1] - range[0]); + color.rgb = fromLinearRGB(color.rgb, tf); + color.rgb *= color.a; + } + return color; +} + +#include "tonemap.glsl" + +vec4 doColorManagement(vec4 pixColor, int srcTF, int dstTF, mat3 dstxyz) { + pixColor.rgb /= max(pixColor.a, 0.001); + pixColor.rgb = toLinearRGB(pixColor.rgb, srcTF); + pixColor.rgb = convertMatrix * pixColor.rgb; + pixColor = toNit(pixColor, srcTFRange); + pixColor.rgb *= pixColor.a; + #include "do_tonemap.glsl" + pixColor = fromLinearNit(pixColor, dstTF, dstTFRange); + #include "do_sdr_mod.glsl" + + return pixColor; +} diff --git a/src/render/shaders/glsl/CMblurprepare.frag b/src/render/shaders/glsl/CMblurprepare.frag new file mode 100644 index 00000000..8ba2d3f8 --- /dev/null +++ b/src/render/shaders/glsl/CMblurprepare.frag @@ -0,0 +1,36 @@ +#version 300 es +#extension GL_ARB_shading_language_include : enable + +precision highp float; +in vec2 v_texcoord; // is in 0-1 +uniform sampler2D tex; + +uniform float contrast; +uniform float brightness; + +uniform int sourceTF; // eTransferFunction +uniform int targetTF; // eTransferFunction + +#include "CM.glsl" +#include "gain.glsl" + +layout(location = 0) out vec4 fragColor; +void main() { + vec4 pixColor = texture(tex, v_texcoord); + + if (sourceTF == CM_TRANSFER_FUNCTION_ST2084_PQ) { + pixColor.rgb /= sdrBrightnessMultiplier; + } + pixColor.rgb = convertMatrix * toLinearRGB(pixColor.rgb, sourceTF); + pixColor = toNit(pixColor, vec2(srcTFRange[0], srcRefLuminance)); + pixColor = fromLinearNit(pixColor, targetTF, dstTFRange); + + // contrast + if (contrast != 1.0) + pixColor.rgb = gain(pixColor.rgb, contrast); + + // brightness + pixColor.rgb *= max(1.0, brightness); + + fragColor = pixColor; +} diff --git a/src/render/shaders/glsl/CMborder.frag b/src/render/shaders/glsl/CMborder.frag new file mode 100644 index 00000000..079f940d --- /dev/null +++ b/src/render/shaders/glsl/CMborder.frag @@ -0,0 +1,98 @@ +#version 300 es +#extension GL_ARB_shading_language_include : enable + +precision highp float; +in vec2 v_texcoord; + +uniform int sourceTF; // eTransferFunction +uniform int targetTF; // eTransferFunction +uniform mat3 targetPrimariesXYZ; + +uniform vec2 fullSizeUntransformed; +uniform float radiusOuter; +uniform float thick; + +// Gradients are in OkLabA!!!! {l, a, b, alpha} +uniform vec4 gradient[10]; +uniform vec4 gradient2[10]; +uniform int gradientLength; +uniform int gradient2Length; +uniform float angle; +uniform float angle2; +uniform float gradientLerp; +uniform float alpha; + +#include "rounding.glsl" +#include "CM.glsl" +#include "border.glsl" + +layout(location = 0) out vec4 fragColor; +void main() { + highp vec2 pixCoord = vec2(gl_FragCoord); + highp vec2 pixCoordOuter = pixCoord; + highp vec2 originalPixCoord = v_texcoord; + originalPixCoord *= fullSizeUntransformed; + float additionalAlpha = 1.0; + + vec4 pixColor = vec4(1.0, 1.0, 1.0, 1.0); + + bool done = false; + + pixCoord -= topLeft + fullSize * 0.5; + pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0; + pixCoordOuter = pixCoord; + pixCoord -= fullSize * 0.5 - radius; + pixCoordOuter -= fullSize * 0.5 - radiusOuter; + + // center the pixes don't make it top-left + pixCoord += vec2(1.0, 1.0) / fullSize; + pixCoordOuter += vec2(1.0, 1.0) / fullSize; + + if (min(pixCoord.x, pixCoord.y) > 0.0 && radius > 0.0) { + float dist = pow(pow(pixCoord.x,roundingPower)+pow(pixCoord.y,roundingPower),1.0/roundingPower); + float distOuter = pow(pow(pixCoordOuter.x,roundingPower)+pow(pixCoordOuter.y,roundingPower),1.0/roundingPower); + float h = (thick / 2.0); + + if (dist < radius - h) { + // lower + float normalized = smoothstep(0.0, 1.0, (dist - radius + thick + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0)); + additionalAlpha *= normalized; + done = true; + } else if (min(pixCoordOuter.x, pixCoordOuter.y) > 0.0) { + // higher + float normalized = 1.0 - smoothstep(0.0, 1.0, (distOuter - radiusOuter + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0)); + additionalAlpha *= normalized; + done = true; + } else if (distOuter < radiusOuter - h) { + additionalAlpha = 1.0; + done = true; + } + } + + // now check for other shit + if (!done) { + // distance to all straight bb borders + float distanceT = originalPixCoord[1]; + float distanceB = fullSizeUntransformed[1] - originalPixCoord[1]; + float distanceL = originalPixCoord[0]; + float distanceR = fullSizeUntransformed[0] - originalPixCoord[0]; + + // get the smallest + float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR)); + + if (smallest > thick) + discard; + } + + if (additionalAlpha == 0.0) + discard; + + pixColor = getColorForCoord(v_texcoord); + pixColor.rgb *= pixColor[3]; + + pixColor = doColorManagement(pixColor, sourceTF, targetTF, targetPrimariesXYZ); + + pixColor *= alpha * additionalAlpha; + + fragColor = pixColor; +} diff --git a/src/render/shaders/glsl/blur1.frag b/src/render/shaders/glsl/blur1.frag index 044df3cc..796fb42d 100644 --- a/src/render/shaders/glsl/blur1.frag +++ b/src/render/shaders/glsl/blur1.frag @@ -1,21 +1,143 @@ #version 300 es -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable +precision highp float; +uniform sampler2D tex; -precision highp float; -uniform sampler2D tex; +uniform float radius; +uniform vec2 halfpixel; +uniform int passes; +uniform float vibrancy; +uniform float vibrancy_darkness; -uniform float radius; -uniform vec2 halfpixel; -uniform int passes; -uniform float vibrancy; -uniform float vibrancy_darkness; +in vec2 v_texcoord; -in vec2 v_texcoord; -layout(location = 0) out vec4 fragColor; +// see http://alienryderflex.com/hsp.html +const float Pr = 0.299; +const float Pg = 0.587; +const float Pb = 0.114; -#include "blur1.glsl" +// Y is "v" ( brightness ). X is "s" ( saturation ) +// see https://www.desmos.com/3d/a88652b9a4 +// Determines if high brightness or high saturation is more important +const float a = 0.93; +const float b = 0.11; +const float c = 0.66; // Determines the smoothness of the transition of unboosted to boosted colors +// -void main() { - fragColor = blur1(v_texcoord, tex, radius, halfpixel, passes, vibrancy, vibrancy_darkness); +// http://www.flong.com/archive/texts/code/shapers_circ/ +float doubleCircleSigmoid(float x, float a) { + a = clamp(a, 0.0, 1.0); + + float y = .0; + if (x <= a) { + y = a - sqrt(a * a - x * x); + } else { + y = a + sqrt(pow(1. - a, 2.) - pow(x - 1., 2.)); + } + return y; +} + +vec3 rgb2hsl(vec3 col) { + float red = col.r; + float green = col.g; + float blue = col.b; + + float minc = min(col.r, min(col.g, col.b)); + float maxc = max(col.r, max(col.g, col.b)); + float delta = maxc - minc; + + float lum = (minc + maxc) * 0.5; + float sat = 0.0; + float hue = 0.0; + + if (lum > 0.0 && lum < 1.0) { + float mul = (lum < 0.5) ? (lum) : (1.0 - lum); + sat = delta / (mul * 2.0); + } + + if (delta > 0.0) { + vec3 maxcVec = vec3(maxc); + vec3 masks = vec3(equal(maxcVec, col)) * vec3(notEqual(maxcVec, vec3(green, blue, red))); + vec3 adds = vec3(0.0, 2.0, 4.0) + vec3(green - blue, blue - red, red - green) / delta; + + hue += dot(adds, masks); + hue /= 6.0; + + if (hue < 0.0) + hue += 1.0; + } + + return vec3(hue, sat, lum); +} + +vec3 hsl2rgb(vec3 col) { + const float onethird = 1.0 / 3.0; + const float twothird = 2.0 / 3.0; + const float rcpsixth = 6.0; + + float hue = col.x; + float sat = col.y; + float lum = col.z; + + vec3 xt = vec3(0.0); + + if (hue < onethird) { + xt.r = rcpsixth * (onethird - hue); + xt.g = rcpsixth * hue; + xt.b = 0.0; + } else if (hue < twothird) { + xt.r = 0.0; + xt.g = rcpsixth * (twothird - hue); + xt.b = rcpsixth * (hue - onethird); + } else + xt = vec3(rcpsixth * (hue - twothird), 0.0, rcpsixth * (1.0 - hue)); + + xt = min(xt, 1.0); + + float sat2 = 2.0 * sat; + float satinv = 1.0 - sat; + float luminv = 1.0 - lum; + float lum2m1 = (2.0 * lum) - 1.0; + vec3 ct = (sat2 * xt) + satinv; + + vec3 rgb; + if (lum >= 0.5) + rgb = (luminv * ct) + lum2m1; + else + rgb = lum * ct; + + return rgb; +} + +layout(location = 0) out vec4 fragColor; +void main() { + vec2 uv = v_texcoord * 2.0; + + vec4 sum = texture(tex, uv) * 4.0; + sum += texture(tex, uv - halfpixel.xy * radius); + sum += texture(tex, uv + halfpixel.xy * radius); + sum += texture(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius); + sum += texture(tex, uv - vec2(halfpixel.x, -halfpixel.y) * radius); + + vec4 color = sum / 8.0; + + if (vibrancy == 0.0) { + fragColor = color; + } else { + // Invert it so that it correctly maps to the config setting + float vibrancy_darkness1 = 1.0 - vibrancy_darkness; + + // Decrease the RGB components based on their perceived brightness, to prevent visually dark colors from overblowing the rest. + vec3 hsl = rgb2hsl(color.rgb); + // Calculate perceived brightness, as not boost visually dark colors like deep blue as much as equally saturated yellow + float perceivedBrightness = doubleCircleSigmoid(sqrt(color.r * color.r * Pr + color.g * color.g * Pg + color.b * color.b * Pb), 0.8 * vibrancy_darkness1); + + float b1 = b * vibrancy_darkness1; + float boostBase = hsl[1] > 0.0 ? smoothstep(b1 - c * 0.5, b1 + c * 0.5, 1.0 - (pow(1.0 - hsl[1] * cos(a), 2.0) + pow(1.0 - perceivedBrightness * sin(a), 2.0))) : 0.0; + + float saturation = clamp(hsl[1] + (boostBase * vibrancy) / float(passes), 0.0, 1.0); + + vec3 newColor = hsl2rgb(vec3(hsl[0], saturation, hsl[2])); + + fragColor = vec4(newColor, color[3]); + } } diff --git a/src/render/shaders/glsl/blur1.glsl b/src/render/shaders/glsl/blur1.glsl deleted file mode 100644 index 36e7d660..00000000 --- a/src/render/shaders/glsl/blur1.glsl +++ /dev/null @@ -1,130 +0,0 @@ -// see http://alienryderflex.com/hsp.html -const float Pr = 0.299; -const float Pg = 0.587; -const float Pb = 0.114; - -// Y is "v" ( brightness ). X is "s" ( saturation ) -// see https://www.desmos.com/3d/a88652b9a4 -// Determines if high brightness or high saturation is more important -const float a = 0.93; -const float b = 0.11; -const float c = 0.66; // Determines the smoothness of the transition of unboosted to boosted colors -// - -// http://www.flong.com/archive/texts/code/shapers_circ/ -float doubleCircleSigmoid(float x, float a) { - a = clamp(a, 0.0, 1.0); - - float y = .0; - if (x <= a) { - y = a - sqrt(a * a - x * x); - } else { - y = a + sqrt(pow(1. - a, 2.) - pow(x - 1., 2.)); - } - return y; -} - -vec3 rgb2hsl(vec3 col) { - float red = col.r; - float green = col.g; - float blue = col.b; - - float minc = min(col.r, min(col.g, col.b)); - float maxc = max(col.r, max(col.g, col.b)); - float delta = maxc - minc; - - float lum = (minc + maxc) * 0.5; - float sat = 0.0; - float hue = 0.0; - - if (lum > 0.0 && lum < 1.0) { - float mul = (lum < 0.5) ? (lum) : (1.0 - lum); - sat = delta / (mul * 2.0); - } - - if (delta > 0.0) { - vec3 maxcVec = vec3(maxc); - vec3 masks = vec3(equal(maxcVec, col)) * vec3(notEqual(maxcVec, vec3(green, blue, red))); - vec3 adds = vec3(0.0, 2.0, 4.0) + vec3(green - blue, blue - red, red - green) / delta; - - hue += dot(adds, masks); - hue /= 6.0; - - if (hue < 0.0) - hue += 1.0; - } - - return vec3(hue, sat, lum); -} - -vec3 hsl2rgb(vec3 col) { - const float onethird = 1.0 / 3.0; - const float twothird = 2.0 / 3.0; - const float rcpsixth = 6.0; - - float hue = col.x; - float sat = col.y; - float lum = col.z; - - vec3 xt = vec3(0.0); - - if (hue < onethird) { - xt.r = rcpsixth * (onethird - hue); - xt.g = rcpsixth * hue; - xt.b = 0.0; - } else if (hue < twothird) { - xt.r = 0.0; - xt.g = rcpsixth * (twothird - hue); - xt.b = rcpsixth * (hue - onethird); - } else - xt = vec3(rcpsixth * (hue - twothird), 0.0, rcpsixth * (1.0 - hue)); - - xt = min(xt, 1.0); - - float sat2 = 2.0 * sat; - float satinv = 1.0 - sat; - float luminv = 1.0 - lum; - float lum2m1 = (2.0 * lum) - 1.0; - vec3 ct = (sat2 * xt) + satinv; - - vec3 rgb; - if (lum >= 0.5) - rgb = (luminv * ct) + lum2m1; - else - rgb = lum * ct; - - return rgb; -} - -vec4 blur1(vec2 v_texcoord, sampler2D tex, float radius, vec2 halfpixel, int passes, float vibrancy, float vibrancy_darkness) { - vec2 uv = v_texcoord * 2.0; - - vec4 sum = texture(tex, uv) * 4.0; - sum += texture(tex, uv - halfpixel.xy * radius); - sum += texture(tex, uv + halfpixel.xy * radius); - sum += texture(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius); - sum += texture(tex, uv - vec2(halfpixel.x, -halfpixel.y) * radius); - - vec4 color = sum / 8.0; - - if (vibrancy == 0.0) { - return color; - } else { - // Invert it so that it correctly maps to the config setting - float vibrancy_darkness1 = 1.0 - vibrancy_darkness; - - // Decrease the RGB components based on their perceived brightness, to prevent visually dark colors from overblowing the rest. - vec3 hsl = rgb2hsl(color.rgb); - // Calculate perceived brightness, as not boost visually dark colors like deep blue as much as equally saturated yellow - float perceivedBrightness = doubleCircleSigmoid(sqrt(color.r * color.r * Pr + color.g * color.g * Pg + color.b * color.b * Pb), 0.8 * vibrancy_darkness1); - - float b1 = b * vibrancy_darkness1; - float boostBase = hsl[1] > 0.0 ? smoothstep(b1 - c * 0.5, b1 + c * 0.5, 1.0 - (pow(1.0 - hsl[1] * cos(a), 2.0) + pow(1.0 - perceivedBrightness * sin(a), 2.0))) : 0.0; - - float saturation = clamp(hsl[1] + (boostBase * vibrancy) / float(passes), 0.0, 1.0); - - vec3 newColor = hsl2rgb(vec3(hsl[0], saturation, hsl[2])); - - return vec4(newColor, color[3]); - } -} diff --git a/src/render/shaders/glsl/blur2.frag b/src/render/shaders/glsl/blur2.frag index 62caae56..bfe448d5 100644 --- a/src/render/shaders/glsl/blur2.frag +++ b/src/render/shaders/glsl/blur2.frag @@ -1,18 +1,25 @@ #version 300 es -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable - -precision highp float; +precision highp float; uniform sampler2D tex; -uniform float radius; -uniform vec2 halfpixel; +uniform float radius; +uniform vec2 halfpixel; -in vec2 v_texcoord; +in vec2 v_texcoord; layout(location = 0) out vec4 fragColor; -#include "blur2.glsl" - void main() { - fragColor = blur2(v_texcoord, tex, radius, halfpixel); + vec2 uv = v_texcoord / 2.0; + + vec4 sum = texture(tex, uv + vec2(-halfpixel.x * 2.0, 0.0) * radius); + + sum += texture(tex, uv + vec2(-halfpixel.x, halfpixel.y) * radius) * 2.0; + sum += texture(tex, uv + vec2(0.0, halfpixel.y * 2.0) * radius); + sum += texture(tex, uv + vec2(halfpixel.x, halfpixel.y) * radius) * 2.0; + sum += texture(tex, uv + vec2(halfpixel.x * 2.0, 0.0) * radius); + sum += texture(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius) * 2.0; + sum += texture(tex, uv + vec2(0.0, -halfpixel.y * 2.0) * radius); + sum += texture(tex, uv + vec2(-halfpixel.x, -halfpixel.y) * radius) * 2.0; + + fragColor = sum / 12.0; } diff --git a/src/render/shaders/glsl/blur2.glsl b/src/render/shaders/glsl/blur2.glsl deleted file mode 100644 index e73e90e3..00000000 --- a/src/render/shaders/glsl/blur2.glsl +++ /dev/null @@ -1,15 +0,0 @@ -vec4 blur2(vec2 v_texcoord, sampler2D tex, float radius, vec2 halfpixel) { - vec2 uv = v_texcoord / 2.0; - - vec4 sum = texture(tex, uv + vec2(-halfpixel.x * 2.0, 0.0) * radius); - - sum += texture(tex, uv + vec2(-halfpixel.x, halfpixel.y) * radius) * 2.0; - sum += texture(tex, uv + vec2(0.0, halfpixel.y * 2.0) * radius); - sum += texture(tex, uv + vec2(halfpixel.x, halfpixel.y) * radius) * 2.0; - sum += texture(tex, uv + vec2(halfpixel.x * 2.0, 0.0) * radius); - sum += texture(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius) * 2.0; - sum += texture(tex, uv + vec2(0.0, -halfpixel.y * 2.0) * radius); - sum += texture(tex, uv + vec2(-halfpixel.x, -halfpixel.y) * radius) * 2.0; - - return sum / 12.0; -} diff --git a/src/render/shaders/glsl/blurFinish.glsl b/src/render/shaders/glsl/blurFinish.glsl deleted file mode 100644 index f3d225c3..00000000 --- a/src/render/shaders/glsl/blurFinish.glsl +++ /dev/null @@ -1,17 +0,0 @@ -float hash(vec2 p) { - vec3 p3 = fract(vec3(p.xyx) * 1689.1984); - p3 += dot(p3, p3.yzx + 33.33); - return fract((p3.x + p3.y) * p3.z); -} - -vec4 blurFinish(vec4 pixColor, vec2 v_texcoord, float noise, float brightness) { - // noise - float noiseHash = hash(v_texcoord); - float noiseAmount = noiseHash - 0.5; - pixColor.rgb += noiseAmount * noise; - - // brightness - pixColor.rgb *= min(1.0, brightness); - - return pixColor; -} diff --git a/src/render/shaders/glsl/blurfinish.frag b/src/render/shaders/glsl/blurfinish.frag index 0342646b..e3c560e8 100644 --- a/src/render/shaders/glsl/blurfinish.frag +++ b/src/render/shaders/glsl/blurfinish.frag @@ -1,19 +1,30 @@ #version 300 es -#define ALLOW_INCLUDES #extension GL_ARB_shading_language_include : enable -precision highp float; -in vec2 v_texcoord; // is in 0-1 +precision highp float; +in vec2 v_texcoord; // is in 0-1 uniform sampler2D tex; -uniform float noise; -uniform float brightness; +uniform float noise; +uniform float brightness; -#include "blurFinish.glsl" +float hash(vec2 p) { + vec3 p3 = fract(vec3(p.xyx) * 1689.1984); + p3 += dot(p3, p3.yzx + 33.33); + return fract((p3.x + p3.y) * p3.z); +} layout(location = 0) out vec4 fragColor; void main() { vec4 pixColor = texture(tex, v_texcoord); - fragColor = blurFinish(pixColor, v_texcoord, noise, brightness); + // noise + float noiseHash = hash(v_texcoord); + float noiseAmount = noiseHash - 0.5; + pixColor.rgb += noiseAmount * noise; + + // brightness + pixColor.rgb *= min(1.0, brightness); + + fragColor = pixColor; } diff --git a/src/render/shaders/glsl/blurprepare.frag b/src/render/shaders/glsl/blurprepare.frag index e96c54bb..67cd9966 100644 --- a/src/render/shaders/glsl/blurprepare.frag +++ b/src/render/shaders/glsl/blurprepare.frag @@ -1,38 +1,26 @@ #version 300 es -#define ALLOW_INCLUDES #extension GL_ARB_shading_language_include : enable -#include "defines.h" - -precision highp float; -in vec2 v_texcoord; // is in 0-1 +precision highp float; +in vec2 v_texcoord; // is in 0-1 uniform sampler2D tex; -uniform float contrast; -uniform float brightness; +uniform float contrast; +uniform float brightness; -uniform int sourceTF; // eTransferFunction -uniform int targetTF; // eTransferFunction - -#if USE_CM -uniform vec2 srcTFRange; -uniform vec2 dstTFRange; - -uniform float srcRefLuminance; -uniform mat3 convertMatrix; - -uniform float sdrBrightnessMultiplier; -#include "cm_helpers.glsl" -#endif - -#include "blurprepare.glsl" +#include "CM.glsl" +#include "gain.glsl" layout(location = 0) out vec4 fragColor; void main() { - fragColor = fragColor = blurPrepare(texture(tex, v_texcoord), contrast, brightness -#if USE_CM - , - sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange, srcRefLuminance, sdrBrightnessMultiplier -#endif - ); + vec4 pixColor = texture(tex, v_texcoord); + + // contrast + if (contrast != 1.0) + pixColor.rgb = gain(pixColor.rgb, contrast); + + // brightness + pixColor.rgb *= max(1.0, brightness); + + fragColor = pixColor; } diff --git a/src/render/shaders/glsl/blurprepare.glsl b/src/render/shaders/glsl/blurprepare.glsl deleted file mode 100644 index e4a0daad..00000000 --- a/src/render/shaders/glsl/blurprepare.glsl +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef ALLOW_INCLUDES -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable -#endif - -#include "defines.h" - -#if USE_CM -#include "cm_helpers.glsl" -#endif - -#include "gain.glsl" - -vec4 blurPrepare(vec4 pixColor, float contrast, float brightness -#if USE_CM - , - int sourceTF, int targetTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange, float srcRefLuminance, float sdrBrightnessMultiplier -#endif -) { -#if USE_CM - if (sourceTF == CM_TRANSFER_FUNCTION_ST2084_PQ) { - pixColor.rgb /= sdrBrightnessMultiplier; - } - pixColor.rgb = convertMatrix * toLinearRGB(pixColor.rgb, sourceTF); - pixColor = toNit(pixColor, vec2(srcTFRange[0], srcRefLuminance)); - pixColor = fromLinearNit(pixColor, targetTF, dstTFRange); -#endif - - // contrast - if (contrast != 1.0) - pixColor.rgb = gain(pixColor.rgb, contrast); - - // brightness - pixColor.rgb *= max(1.0, brightness); - - return pixColor; -} diff --git a/src/render/shaders/glsl/border.frag b/src/render/shaders/glsl/border.frag index 151593c1..a672452b 100644 --- a/src/render/shaders/glsl/border.frag +++ b/src/render/shaders/glsl/border.frag @@ -1,60 +1,92 @@ #version 300 es -#define ALLOW_INCLUDES #extension GL_ARB_shading_language_include : enable -precision highp float; -in vec2 v_texcoord; +precision highp float; +in vec2 v_texcoord; -uniform int sourceTF; // eTransferFunction -uniform int targetTF; // eTransferFunction -uniform mat3 targetPrimariesXYZ; - -uniform vec2 fullSizeUntransformed; +uniform vec2 fullSizeUntransformed; uniform float radiusOuter; uniform float thick; // Gradients are in OkLabA!!!! {l, a, b, alpha} -uniform vec4 gradient[10]; -uniform vec4 gradient2[10]; -uniform int gradientLength; -uniform int gradient2Length; +uniform vec4 gradient[10]; +uniform vec4 gradient2[10]; +uniform int gradientLength; +uniform int gradient2Length; uniform float angle; uniform float angle2; uniform float gradientLerp; uniform float alpha; -uniform float radius; -uniform float roundingPower; -uniform vec2 topLeft; -uniform vec2 fullSize; #include "rounding.glsl" #include "CM.glsl" #include "border.glsl" layout(location = 0) out vec4 fragColor; void main() { - fragColor = getBorder(v_texcoord, alpha, fullSizeUntransformed, radiusOuter, thick, radius, roundingPower, topLeft, fullSize, gradientLength, gradient, angle, gradient2Length, - gradient2, angle2, gradientLerp -#if USE_CM - , - sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange -#if USE_ICC - , - iccLut3D, iccLutSize -#else -#if USE_TONEMAP || USE_SDR_MOD - , - targetPrimariesXYZ -#endif -#if USE_TONEMAP - , - maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance -#endif -#if USE_SDR_MOD - , - sdrSaturation, sdrBrightnessMultiplier -#endif -#endif -#endif - ); + highp vec2 pixCoord = vec2(gl_FragCoord); + highp vec2 pixCoordOuter = pixCoord; + highp vec2 originalPixCoord = v_texcoord; + originalPixCoord *= fullSizeUntransformed; + float additionalAlpha = 1.0; + + vec4 pixColor = vec4(1.0, 1.0, 1.0, 1.0); + + bool done = false; + + pixCoord -= topLeft + fullSize * 0.5; + pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0; + pixCoordOuter = pixCoord; + pixCoord -= fullSize * 0.5 - radius; + pixCoordOuter -= fullSize * 0.5 - radiusOuter; + + // center the pixes don't make it top-left + pixCoord += vec2(1.0, 1.0) / fullSize; + pixCoordOuter += vec2(1.0, 1.0) / fullSize; + + if (min(pixCoord.x, pixCoord.y) > 0.0 && radius > 0.0) { + float dist = pow(pow(pixCoord.x,roundingPower)+pow(pixCoord.y,roundingPower),1.0/roundingPower); + float distOuter = pow(pow(pixCoordOuter.x,roundingPower)+pow(pixCoordOuter.y,roundingPower),1.0/roundingPower); + float h = (thick / 2.0); + + if (dist < radius - h) { + // lower + float normalized = smoothstep(0.0, 1.0, (dist - radius + thick + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0)); + additionalAlpha *= normalized; + done = true; + } else if (min(pixCoordOuter.x, pixCoordOuter.y) > 0.0) { + // higher + float normalized = 1.0 - smoothstep(0.0, 1.0, (distOuter - radiusOuter + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0)); + additionalAlpha *= normalized; + done = true; + } else if (distOuter < radiusOuter - h) { + additionalAlpha = 1.0; + done = true; + } + } + + // now check for other shit + if (!done) { + // distance to all straight bb borders + float distanceT = originalPixCoord[1]; + float distanceB = fullSizeUntransformed[1] - originalPixCoord[1]; + float distanceL = originalPixCoord[0]; + float distanceR = fullSizeUntransformed[0] - originalPixCoord[0]; + + // get the smallest + float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR)); + + if (smallest > thick) + discard; + } + + if (additionalAlpha == 0.0) + discard; + + pixColor = getColorForCoord(v_texcoord); + pixColor.rgb *= pixColor[3]; + + pixColor *= alpha * additionalAlpha; + + fragColor = pixColor; } diff --git a/src/render/shaders/glsl/border.glsl b/src/render/shaders/glsl/border.glsl index fa2a6980..c5ad7f3d 100644 --- a/src/render/shaders/glsl/border.glsl +++ b/src/render/shaders/glsl/border.glsl @@ -1,24 +1,18 @@ -#ifndef ALLOW_INCLUDES -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable -#endif -#include "cm_helpers.glsl" -#if USE_ROUNDING -#include "rounding.glsl" -#endif - vec4 okLabAToSrgb(vec4 lab) { float l = pow(lab[0] + lab[1] * 0.3963377774 + lab[2] * 0.2158037573, 3.0); float m = pow(lab[0] + lab[1] * (-0.1055613458) + lab[2] * (-0.0638541728), 3.0); float s = pow(lab[0] + lab[1] * (-0.0894841775) + lab[2] * (-1.2914855480), 3.0); - return vec4(fromLinearRGB(vec3(l * 4.0767416621 + m * -3.3077115913 + s * 0.2309699292, l * (-1.2684380046) + m * 2.6097574011 + s * (-0.3413193965), - l * (-0.0041960863) + m * (-0.7034186147) + s * 1.7076147010), - CM_TRANSFER_FUNCTION_GAMMA22), - lab[3]); + return vec4(fromLinearRGB( + vec3( + l * 4.0767416621 + m * -3.3077115913 + s * 0.2309699292, + l * (-1.2684380046) + m * 2.6097574011 + s * (-0.3413193965), + l * (-0.0041960863) + m * (-0.7034186147) + s * 1.7076147010 + ), CM_TRANSFER_FUNCTION_GAMMA22 + ), lab[3]); } -vec4 getOkColorForCoordArray1(vec2 normalizedCoord, int gradientLength, vec4 gradient[10], float angle) { +vec4 getOkColorForCoordArray1(vec2 normalizedCoord) { if (gradientLength < 2) return gradient[0]; @@ -26,14 +20,14 @@ vec4 getOkColorForCoordArray1(vec2 normalizedCoord, int gradientLength, vec4 gra if (angle > 4.71 /* 270 deg */) { normalizedCoord[1] = 1.0 - normalizedCoord[1]; - finalAng = 6.28 - angle; + finalAng = 6.28 - angle; } else if (angle > 3.14 /* 180 deg */) { normalizedCoord[0] = 1.0 - normalizedCoord[0]; normalizedCoord[1] = 1.0 - normalizedCoord[1]; - finalAng = angle - 3.14; + finalAng = angle - 3.14; } else if (angle > 1.57 /* 90 deg */) { normalizedCoord[0] = 1.0 - normalizedCoord[0]; - finalAng = 3.14 - angle; + finalAng = 3.14 - angle; } else { finalAng = angle; } @@ -41,13 +35,13 @@ vec4 getOkColorForCoordArray1(vec2 normalizedCoord, int gradientLength, vec4 gra float sine = sin(finalAng); float progress = (normalizedCoord[1] * sine + normalizedCoord[0] * (1.0 - sine)) * float(gradientLength - 1); - int bottom = int(floor(progress)); - int top = bottom + 1; + int bottom = int(floor(progress)); + int top = bottom + 1; return gradient[top] * (progress - float(bottom)) + gradient[bottom] * (float(top) - progress); } -vec4 getOkColorForCoordArray2(vec2 normalizedCoord, float angle, int gradient2Length, vec4 gradient2[10], float angle2) { +vec4 getOkColorForCoordArray2(vec2 normalizedCoord) { if (gradient2Length < 2) return gradient2[0]; @@ -55,14 +49,14 @@ vec4 getOkColorForCoordArray2(vec2 normalizedCoord, float angle, int gradient2Le if (angle2 > 4.71 /* 270 deg */) { normalizedCoord[1] = 1.0 - normalizedCoord[1]; - finalAng = 6.28 - angle; + finalAng = 6.28 - angle; } else if (angle2 > 3.14 /* 180 deg */) { normalizedCoord[0] = 1.0 - normalizedCoord[0]; normalizedCoord[1] = 1.0 - normalizedCoord[1]; - finalAng = angle - 3.14; + finalAng = angle - 3.14; } else if (angle2 > 1.57 /* 90 deg */) { normalizedCoord[0] = 1.0 - normalizedCoord[0]; - finalAng = 3.14 - angle2; + finalAng = 3.14 - angle2; } else { finalAng = angle2; } @@ -70,134 +64,19 @@ vec4 getOkColorForCoordArray2(vec2 normalizedCoord, float angle, int gradient2Le float sine = sin(finalAng); float progress = (normalizedCoord[1] * sine + normalizedCoord[0] * (1.0 - sine)) * float(gradient2Length - 1); - int bottom = int(floor(progress)); - int top = bottom + 1; + int bottom = int(floor(progress)); + int top = bottom + 1; return gradient2[top] * (progress - float(bottom)) + gradient2[bottom] * (float(top) - progress); } -vec4 getColorForCoord(vec2 normalizedCoord, int gradientLength, vec4 gradient[10], float angle, int gradient2Length, vec4 gradient2[10], float angle2, float gradientLerp) { - vec4 result1 = getOkColorForCoordArray1(normalizedCoord, gradientLength, gradient, angle); +vec4 getColorForCoord(vec2 normalizedCoord) { + vec4 result1 = getOkColorForCoordArray1(normalizedCoord); if (gradient2Length <= 0) return okLabAToSrgb(result1); - vec4 result2 = getOkColorForCoordArray2(normalizedCoord, angle, gradient2Length, gradient2, angle2); + vec4 result2 = getOkColorForCoordArray2(normalizedCoord); return okLabAToSrgb(mix(result1, result2, gradientLerp)); } - -vec4 getBorder(vec2 v_texcoord, float alpha, vec2 fullSizeUntransformed, float radiusOuter, float thick, float radius, float roundingPower, vec2 topLeft, vec2 fullSize, - int gradientLength, vec4 gradient[10], float angle, int gradient2Length, vec4 gradient2[10], float angle2, float gradientLerp -#if USE_CM - , - int sourceTF, int targetTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange -#if USE_ICC - , - highp sampler3D iccLut3D, float iccLutSize -#else -#if USE_TONEMAP || USE_SDR_MOD - , - mat3 targetPrimariesXYZ -#endif -#if USE_TONEMAP - , - float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance -#endif -#if USE_SDR_MOD - , - float sdrSaturation, float sdrBrightnessMultiplier -#endif -#endif -#endif -) { - vec2 pixCoord = vec2(gl_FragCoord); - vec2 pixCoordOuter = pixCoord; - vec2 originalPixCoord = v_texcoord; - originalPixCoord *= fullSizeUntransformed; - float additionalAlpha = 1.0; - - vec4 pixColor = vec4(1.0, 1.0, 1.0, 1.0); - - bool done = false; - - pixCoord -= topLeft + fullSize * 0.5; - pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0; - pixCoordOuter = pixCoord; - pixCoord -= fullSize * 0.5 - radius; - pixCoordOuter -= fullSize * 0.5 - radiusOuter; - - // center the pixes don't make it top-left - pixCoord += vec2(1.0, 1.0) / fullSize; - pixCoordOuter += vec2(1.0, 1.0) / fullSize; - -#if USE_ROUNDING - if (min(pixCoord.x, pixCoord.y) > 0.0 && radius > 0.0) { - float dist = pow(pow(pixCoord.x, roundingPower) + pow(pixCoord.y, roundingPower), 1.0 / roundingPower); - float distOuter = pow(pow(pixCoordOuter.x, roundingPower) + pow(pixCoordOuter.y, roundingPower), 1.0 / roundingPower); - float h = (thick / 2.0); - - if (dist < radius - h) { - // lower - float normalized = smoothstep(0.0, 1.0, (dist - radius + thick + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0)); - additionalAlpha *= normalized; - done = true; - } else if (min(pixCoordOuter.x, pixCoordOuter.y) > 0.0) { - // higher - float normalized = 1.0 - smoothstep(0.0, 1.0, (distOuter - radiusOuter + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0)); - additionalAlpha *= normalized; - done = true; - } else if (distOuter < radiusOuter - h) { - additionalAlpha = 1.0; - done = true; - } - } -#endif - - // now check for other shit - if (!done) { - // distance to all straight bb borders - float distanceT = originalPixCoord[1]; - float distanceB = fullSizeUntransformed[1] - originalPixCoord[1]; - float distanceL = originalPixCoord[0]; - float distanceR = fullSizeUntransformed[0] - originalPixCoord[0]; - - // get the smallest - float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR)); - - if (smallest > thick) - discard; - } - - if (additionalAlpha == 0.0) - discard; - - pixColor = getColorForCoord(v_texcoord, gradientLength, gradient, angle, gradient2Length, gradient2, angle2, gradientLerp); - pixColor.rgb *= pixColor[3]; - -#if USE_CM - pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange -#if USE_ICC - , - iccLut3D, iccLutSize -#else -#if USE_TONEMAP || USE_SDR_MOD - , - targetPrimariesXYZ -#endif -#if USE_TONEMAP - , - maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance -#endif -#if USE_SDR_MOD - , - sdrSaturation, sdrBrightnessMultiplier -#endif -#endif - ); -#endif - - pixColor *= alpha * additionalAlpha; - - return pixColor; -} diff --git a/src/render/shaders/glsl/cm_helpers.glsl b/src/render/shaders/glsl/cm_helpers.glsl deleted file mode 100644 index 5e0d14f6..00000000 --- a/src/render/shaders/glsl/cm_helpers.glsl +++ /dev/null @@ -1,248 +0,0 @@ -#ifndef ALLOW_INCLUDES -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable -#endif -#ifndef CM_HELPERS_GLSL -#define CM_HELPERS_GLSL - -#include "defines.h" -#include "constants.h" - -#if USE_SDR_MOD -vec4 saturate(vec4 color, mat3 primaries, float saturation) { - if (saturation == 1.0) - return color; - vec3 brightness = vec3(primaries[1][0], primaries[1][1], primaries[1][2]); - float Y = dot(color.rgb, brightness); - return vec4(mix(vec3(Y), color.rgb, saturation), color[3]); -} -#endif - -vec3 applyIcc3DLut(vec3 linearRgb01, highp sampler3D iccLut3D, float iccLutSize) { - vec3 x = clamp(linearRgb01, 0.0, 1.0); - - // Map [0..1] to texel centers to avoid edge issues - float N = iccLutSize; - vec3 coord = (x * (N - 1.0) + 0.5) / N; - - return texture(iccLut3D, coord).rgb; -} - -vec3 xy2xyz(vec2 xy) { - if (xy.y == 0.0) - return vec3(0.0, 0.0, 0.0); - - return vec3(xy.x / xy.y, 1.0, (1.0 - xy.x - xy.y) / xy.y); -} - -// The primary source for these transfer functions is https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.1361-0-199802-W!!PDF-E.pdf -vec3 tfInvPQ(vec3 color) { - vec3 E = pow(clamp(color.rgb, vec3(0.0), vec3(1.0)), vec3(PQ_INV_M2)); - return pow((max(E - PQ_C1, vec3(0.0))) / (PQ_C2 - PQ_C3 * E), vec3(PQ_INV_M1)); -} - -vec3 tfInvHLG(vec3 color) { - bvec3 isLow = lessThanEqual(color.rgb, vec3(HLG_E_CUT)); - vec3 lo = color.rgb * color.rgb / 3.0; - vec3 hi = (exp((color.rgb - HLG_C) / HLG_A) + HLG_B) / 12.0; - return mix(hi, lo, isLow); -} - -// Many transfer functions (including sRGB) follow the same pattern: a linear -// segment for small values and a power function for larger values. The -// following function implements this pattern from which sRGB, BT.1886, and -// others can be derived by plugging in the right constants. -vec3 tfInvLinPow(vec3 color, float gamma, float thres, float scale, float alpha) { - bvec3 isLow = lessThanEqual(color.rgb, vec3(thres * scale)); - vec3 lo = color.rgb / scale; - vec3 hi = pow((color.rgb + alpha - 1.0) / alpha, vec3(gamma)); - return mix(hi, lo, isLow); -} - -vec3 tfInvSRGB(vec3 color) { - return tfInvLinPow(color, SRGB_POW, SRGB_CUT, SRGB_SCALE, SRGB_ALPHA); -} - -vec3 tfInvExtSRGB(vec3 color) { - // EXT sRGB is the sRGB transfer function mirrored around 0. - return sign(color) * tfInvSRGB(abs(color)); -} - -vec3 tfInvBT1886(vec3 color) { - return tfInvLinPow(color, BT1886_POW, BT1886_CUT, BT1886_SCALE, BT1886_ALPHA); -} - -vec3 tfInvXVYCC(vec3 color) { - // The inverse transfer function for XVYCC is the BT1886 transfer function mirrored around 0, - // same as what EXT sRGB is to sRGB. - return sign(color) * tfInvBT1886(abs(color)); -} - -vec3 tfInvST240(vec3 color) { - return tfInvLinPow(color, ST240_POW, ST240_CUT, ST240_SCALE, ST240_ALPHA); -} - -// Forward transfer functions corresponding to the inverse functions above. -vec3 tfPQ(vec3 color) { - vec3 E = pow(clamp(color.rgb, vec3(0.0), vec3(1.0)), vec3(PQ_M1)); - return pow((vec3(PQ_C1) + PQ_C2 * E) / (vec3(1.0) + PQ_C3 * E), vec3(PQ_M2)); -} - -vec3 tfHLG(vec3 color) { - bvec3 isLow = lessThanEqual(color.rgb, vec3(HLG_D_CUT)); - vec3 lo = sqrt(max(color.rgb, vec3(0.0)) * 3.0); - vec3 hi = HLG_A * log(max(12.0 * color.rgb - HLG_B, vec3(0.0001))) + HLG_C; - return mix(hi, lo, isLow); -} - -vec3 tfLinPow(vec3 color, float gamma, float thres, float scale, float alpha) { - bvec3 isLow = lessThanEqual(color.rgb, vec3(thres)); - vec3 lo = color.rgb * scale; - vec3 hi = pow(color.rgb, vec3(1.0 / gamma)) * alpha - (alpha - 1.0); - return mix(hi, lo, isLow); -} - -vec3 tfSRGB(vec3 color) { - return tfLinPow(color, SRGB_POW, SRGB_CUT, SRGB_SCALE, SRGB_ALPHA); -} - -vec3 tfExtSRGB(vec3 color) { - // EXT sRGB is the sRGB transfer function mirrored around 0. - return sign(color) * tfSRGB(abs(color)); -} - -vec3 tfBT1886(vec3 color) { - return tfLinPow(color, BT1886_POW, BT1886_CUT, BT1886_SCALE, BT1886_ALPHA); -} - -vec3 tfXVYCC(vec3 color) { - // The transfer function for XVYCC is the BT1886 transfer function mirrored around 0, - // same as what EXT sRGB is to sRGB. - return sign(color) * tfBT1886(abs(color)); -} - -vec3 tfST240(vec3 color) { - return tfLinPow(color, ST240_POW, ST240_CUT, ST240_SCALE, ST240_ALPHA); -} - -vec3 toLinearRGB(vec3 color, int tf) { - switch (tf) { - case CM_TRANSFER_FUNCTION_EXT_LINEAR: return color; - case CM_TRANSFER_FUNCTION_ST2084_PQ: return tfInvPQ(color); - case CM_TRANSFER_FUNCTION_GAMMA22: return pow(max(color, vec3(0.0)), vec3(2.2)); - case CM_TRANSFER_FUNCTION_GAMMA28: return pow(max(color, vec3(0.0)), vec3(2.8)); - case CM_TRANSFER_FUNCTION_HLG: return tfInvHLG(color); - case CM_TRANSFER_FUNCTION_EXT_SRGB: return tfInvExtSRGB(color); - case CM_TRANSFER_FUNCTION_BT1886: return tfInvBT1886(color); - case CM_TRANSFER_FUNCTION_ST240: return tfInvST240(color); - case CM_TRANSFER_FUNCTION_LOG_100: return mix(exp((color - 1.0) * 2.0 * log(10.0)), vec3(0.0), lessThanEqual(color, vec3(0.0))); - case CM_TRANSFER_FUNCTION_LOG_316: return mix(exp((color - 1.0) * 2.5 * log(10.0)), vec3(0.0), lessThanEqual(color, vec3(0.0))); - case CM_TRANSFER_FUNCTION_XVYCC: return tfInvXVYCC(color); - case CM_TRANSFER_FUNCTION_ST428: return pow(max(color, vec3(0.0)), vec3(ST428_POW)) * ST428_SCALE; - case CM_TRANSFER_FUNCTION_SRGB: - default: return tfInvSRGB(color); - } -} - -vec4 toLinear(vec4 color, int tf) { - if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR) - return color; - - color.rgb /= max(color.a, 0.001); - color.rgb = toLinearRGB(color.rgb, tf); - color.rgb *= color.a; - return color; -} - -vec4 toNit(vec4 color, vec2 range) { - color.rgb = color.rgb * (range[1] - range[0]) + range[0]; - return color; -} - -vec3 fromLinearRGB(vec3 color, int tf) { - switch (tf) { - case CM_TRANSFER_FUNCTION_EXT_LINEAR: return color; - case CM_TRANSFER_FUNCTION_ST2084_PQ: return tfPQ(color); - case CM_TRANSFER_FUNCTION_GAMMA22: return pow(max(color, vec3(0.0)), vec3(1.0 / 2.2)); - case CM_TRANSFER_FUNCTION_GAMMA28: return pow(max(color, vec3(0.0)), vec3(1.0 / 2.8)); - case CM_TRANSFER_FUNCTION_HLG: return tfHLG(color); - case CM_TRANSFER_FUNCTION_EXT_SRGB: return tfExtSRGB(color); - case CM_TRANSFER_FUNCTION_BT1886: return tfBT1886(color); - case CM_TRANSFER_FUNCTION_ST240: return tfST240(color); - case CM_TRANSFER_FUNCTION_LOG_100: return mix(1.0 + log(color) / log(10.0) / 2.0, vec3(0.0), lessThanEqual(color, vec3(0.01))); - case CM_TRANSFER_FUNCTION_LOG_316: return mix(1.0 + log(color) / log(10.0) / 2.5, vec3(0.0), lessThanEqual(color, vec3(sqrt(10.0) / 1000.0))); - case CM_TRANSFER_FUNCTION_XVYCC: return tfXVYCC(color); - case CM_TRANSFER_FUNCTION_ST428: return pow(max(color, vec3(0.0)) / ST428_SCALE, vec3(1.0 / ST428_POW)); - case CM_TRANSFER_FUNCTION_SRGB: - default: return tfSRGB(color); - } -} - -vec4 fromLinear(vec4 color, int tf) { - if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR) - return color; - - color.rgb /= max(color.a, 0.001); - color.rgb = fromLinearRGB(color.rgb, tf); - color.rgb *= color.a; - return color; -} - -vec4 fromLinearNit(vec4 color, int tf, vec2 range) { - if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR) - color.rgb = color.rgb / SDR_MAX_LUMINANCE; - else { - color.rgb /= max(color.a, 0.001); - color.rgb = (color.rgb - range[0]) / (range[1] - range[0]); - color.rgb = fromLinearRGB(color.rgb, tf); - color.rgb *= color.a; - } - return color; -} - -#if USE_TONEMAP -#include "tonemap.glsl" -#endif - -vec4 doColorManagement(vec4 pixColor, int srcTF, int dstTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange -#if USE_ICC - , - highp sampler3D iccLut3D, float iccLutSize -#else -#if USE_TONEMAP || USE_SDR_MOD - , - mat3 dstxyz -#endif -#if USE_TONEMAP - , - float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance -#endif -#if USE_SDR_MOD - , - float sdrSaturation, float sdrBrightnessMultiplier -#endif -#endif -) { - pixColor.rgb /= max(pixColor.a, 0.001); - pixColor.rgb = toLinearRGB(pixColor.rgb, srcTF); -#if USE_ICC - pixColor.rgb = applyIcc3DLut(pixColor.rgb, iccLut3D, iccLutSize); - pixColor.rgb *= pixColor.a; -#else - pixColor.rgb = convertMatrix * pixColor.rgb; - pixColor = toNit(pixColor, srcTFRange); - pixColor.rgb *= pixColor.a; -#if USE_TONEMAP - pixColor = tonemap(pixColor, dstxyz, maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance); -#endif - pixColor = fromLinearNit(pixColor, dstTF, dstTFRange); -#if USE_SDR_MOD - pixColor = saturate(pixColor, dstxyz, sdrSaturation); - pixColor.rgb *= sdrBrightnessMultiplier; -#endif -#endif - - return pixColor; -} - -#endif \ No newline at end of file diff --git a/src/render/shaders/glsl/constants.h b/src/render/shaders/glsl/constants.h deleted file mode 100644 index bbab5284..00000000 --- a/src/render/shaders/glsl/constants.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef CONSTANTS_H -#define CONSTANTS_H -//enum eTransferFunction -#define CM_TRANSFER_FUNCTION_BT1886 1 -#define CM_TRANSFER_FUNCTION_GAMMA22 2 -#define CM_TRANSFER_FUNCTION_GAMMA28 3 -#define CM_TRANSFER_FUNCTION_ST240 4 -#define CM_TRANSFER_FUNCTION_EXT_LINEAR 5 -#define CM_TRANSFER_FUNCTION_LOG_100 6 -#define CM_TRANSFER_FUNCTION_LOG_316 7 -#define CM_TRANSFER_FUNCTION_XVYCC 8 -#define CM_TRANSFER_FUNCTION_SRGB 9 -#define CM_TRANSFER_FUNCTION_EXT_SRGB 10 -#define CM_TRANSFER_FUNCTION_ST2084_PQ 11 -#define CM_TRANSFER_FUNCTION_ST428 12 -#define CM_TRANSFER_FUNCTION_HLG 13 - -// sRGB constants -#define SRGB_POW 2.4 -#define SRGB_CUT 0.0031308 -#define SRGB_SCALE 12.92 -#define SRGB_ALPHA 1.055 - -#define BT1886_POW (1.0 / 0.45) -#define BT1886_CUT 0.018053968510807 -#define BT1886_SCALE 4.5 -#define BT1886_ALPHA (1.0 + 5.5 * BT1886_CUT) - -// See http://car.france3.mars.free.fr/HD/INA-%2026%20jan%2006/SMPTE%20normes%20et%20confs/s240m.pdf -#define ST240_POW (1.0 / 0.45) -#define ST240_CUT 0.0228 -#define ST240_SCALE 4.0 -#define ST240_ALPHA 1.1115 - -#define ST428_POW 2.6 -#define ST428_SCALE (52.37 / 48.0) - -// PQ constants -#define PQ_M1 0.1593017578125 -#define PQ_M2 78.84375 -#define PQ_INV_M1 (1.0 / PQ_M1) -#define PQ_INV_M2 (1.0 / PQ_M2) -#define PQ_C1 0.8359375 -#define PQ_C2 18.8515625 -#define PQ_C3 18.6875 - -// HLG constants -#define HLG_D_CUT (1.0 / 12.0) -#define HLG_E_CUT 0.5 -#define HLG_A 0.17883277 -#define HLG_B 0.28466892 -#define HLG_C 0.55991073 - -#define SDR_MIN_LUMINANCE 0.2 -#define SDR_MAX_LUMINANCE 80.0 -#define HDR_MIN_LUMINANCE 0.005 -#define HDR_MAX_LUMINANCE 10000.0 -#define HLG_MAX_LUMINANCE 1000.0 - -#define M_E 2.718281828459045 - -#endif diff --git a/src/render/shaders/glsl/defines.h b/src/render/shaders/glsl/defines.h deleted file mode 100644 index 31b120a4..00000000 --- a/src/render/shaders/glsl/defines.h +++ /dev/null @@ -1,10 +0,0 @@ -// DO NOT EDIT. Will be overwritten in runtime -#define USE_RGBA 1 -#define USE_DISCARD 1 -#define USE_TINT 1 -#define USE_ROUNDING 1 -#define USE_CM 1 -#define USE_TONEMAP 1 -#define USE_SDR_MOD 1 -#define USE_BLUR 1 -#define USE_ICC 1 diff --git a/src/render/shaders/glsl/discard.glsl b/src/render/shaders/glsl/discard.glsl new file mode 100644 index 00000000..311776de --- /dev/null +++ b/src/render/shaders/glsl/discard.glsl @@ -0,0 +1,3 @@ +uniform bool discardOpaque; +uniform bool discardAlpha; +uniform float discardAlphaValue; diff --git a/src/render/shaders/glsl/do_CM.glsl b/src/render/shaders/glsl/do_CM.glsl new file mode 100644 index 00000000..b63d03e5 --- /dev/null +++ b/src/render/shaders/glsl/do_CM.glsl @@ -0,0 +1 @@ +pixColor = doColorManagement(pixColor, sourceTF, targetTF, targetPrimariesXYZ); \ No newline at end of file diff --git a/src/render/shaders/glsl/do_discard.glsl b/src/render/shaders/glsl/do_discard.glsl new file mode 100644 index 00000000..df6e57e3 --- /dev/null +++ b/src/render/shaders/glsl/do_discard.glsl @@ -0,0 +1,5 @@ +if (discardOpaque && pixColor.a * alpha == 1.0) + discard; + +if (discardAlpha && pixColor.a <= discardAlphaValue) + discard; \ No newline at end of file diff --git a/src/render/shaders/glsl/do_rounding.glsl b/src/render/shaders/glsl/do_rounding.glsl new file mode 100644 index 00000000..60368fb1 --- /dev/null +++ b/src/render/shaders/glsl/do_rounding.glsl @@ -0,0 +1 @@ +pixColor = rounding(pixColor); \ No newline at end of file diff --git a/src/render/shaders/glsl/do_sdr_mod.glsl b/src/render/shaders/glsl/do_sdr_mod.glsl new file mode 100644 index 00000000..05dbe180 --- /dev/null +++ b/src/render/shaders/glsl/do_sdr_mod.glsl @@ -0,0 +1,2 @@ +pixColor = saturate(pixColor, dstxyz, sdrSaturation); +pixColor.rgb *= sdrBrightnessMultiplier; diff --git a/src/render/shaders/glsl/do_tint.glsl b/src/render/shaders/glsl/do_tint.glsl new file mode 100644 index 00000000..b761b704 --- /dev/null +++ b/src/render/shaders/glsl/do_tint.glsl @@ -0,0 +1 @@ +pixColor.rgb = pixColor.rgb * tint; diff --git a/src/render/shaders/glsl/do_tonemap.glsl b/src/render/shaders/glsl/do_tonemap.glsl new file mode 100644 index 00000000..db23b0f8 --- /dev/null +++ b/src/render/shaders/glsl/do_tonemap.glsl @@ -0,0 +1 @@ +pixColor = tonemap(pixColor, dstxyz); \ No newline at end of file diff --git a/src/render/shaders/glsl/ext.frag b/src/render/shaders/glsl/ext.frag index 1c614bd3..e855a832 100644 --- a/src/render/shaders/glsl/ext.frag +++ b/src/render/shaders/glsl/ext.frag @@ -1,25 +1,20 @@ #version 300 es -#define ALLOW_INCLUDES #extension GL_ARB_shading_language_include : enable #extension GL_OES_EGL_image_external_essl3 : require -precision highp float; -in vec2 v_texcoord; +precision highp float; +in vec2 v_texcoord; uniform samplerExternalOES tex; -uniform float alpha; +uniform float alpha; -uniform float radius; -uniform float roundingPower; -uniform vec2 topLeft; -uniform vec2 fullSize; #include "rounding.glsl" -uniform int discardOpaque; -uniform int discardAlpha; -uniform int discardAlphaValue; +uniform int discardOpaque; +uniform int discardAlpha; +uniform int discardAlphaValue; -uniform int applyTint; +uniform int applyTint; uniform vec3 tint; layout(location = 0) out vec4 fragColor; @@ -28,16 +23,16 @@ void main() { vec4 pixColor = texture(tex, v_texcoord); if (discardOpaque == 1 && pixColor[3] * alpha == 1.0) - discard; + discard; if (applyTint == 1) { - pixColor[0] = pixColor[0] * tint[0]; - pixColor[1] = pixColor[1] * tint[1]; - pixColor[2] = pixColor[2] * tint[2]; + pixColor[0] = pixColor[0] * tint[0]; + pixColor[1] = pixColor[1] * tint[1]; + pixColor[2] = pixColor[2] * tint[2]; } if (radius > 0.0) - pixColor = rounding(pixColor, radius, roundingPower, topLeft, fullSize); + pixColor = rounding(pixColor); fragColor = pixColor * alpha; } diff --git a/src/render/shaders/glsl/get_rgb_pixel.glsl b/src/render/shaders/glsl/get_rgb_pixel.glsl new file mode 100644 index 00000000..31097c58 --- /dev/null +++ b/src/render/shaders/glsl/get_rgb_pixel.glsl @@ -0,0 +1 @@ +#include "get_rgbx_pixel.glsl" \ No newline at end of file diff --git a/src/render/shaders/glsl/get_rgba_pixel.glsl b/src/render/shaders/glsl/get_rgba_pixel.glsl new file mode 100644 index 00000000..23ad0cf2 --- /dev/null +++ b/src/render/shaders/glsl/get_rgba_pixel.glsl @@ -0,0 +1 @@ +vec4 pixColor = texture(tex, v_texcoord); diff --git a/src/render/shaders/glsl/get_rgbx_pixel.glsl b/src/render/shaders/glsl/get_rgbx_pixel.glsl new file mode 100644 index 00000000..fa4eb74b --- /dev/null +++ b/src/render/shaders/glsl/get_rgbx_pixel.glsl @@ -0,0 +1 @@ +vec4 pixColor = vec4(texture(tex, v_texcoord).rgb, 1.0); diff --git a/src/render/shaders/glsl/primaries_xyz.glsl b/src/render/shaders/glsl/primaries_xyz.glsl new file mode 100644 index 00000000..ddcb5c70 --- /dev/null +++ b/src/render/shaders/glsl/primaries_xyz.glsl @@ -0,0 +1 @@ +#include "primaries_xyz_uniform.glsl" \ No newline at end of file diff --git a/src/render/shaders/glsl/primaries_xyz_const.glsl b/src/render/shaders/glsl/primaries_xyz_const.glsl new file mode 100644 index 00000000..5499d1cd --- /dev/null +++ b/src/render/shaders/glsl/primaries_xyz_const.glsl @@ -0,0 +1 @@ +const mat3 targetPrimariesXYZ = mat3(0.0); diff --git a/src/render/shaders/glsl/primaries_xyz_uniform.glsl b/src/render/shaders/glsl/primaries_xyz_uniform.glsl new file mode 100644 index 00000000..6c0558f0 --- /dev/null +++ b/src/render/shaders/glsl/primaries_xyz_uniform.glsl @@ -0,0 +1 @@ +uniform mat3 targetPrimariesXYZ; \ No newline at end of file diff --git a/src/render/shaders/glsl/quad.frag b/src/render/shaders/glsl/quad.frag index 61895a60..5dae493e 100644 --- a/src/render/shaders/glsl/quad.frag +++ b/src/render/shaders/glsl/quad.frag @@ -1,27 +1,17 @@ #version 300 es -#define ALLOW_INCLUDES #extension GL_ARB_shading_language_include : enable -#include "defines.h" - precision highp float; -in vec4 v_color; +in vec4 v_color; -#if USE_ROUNDING -uniform float radius; -uniform float roundingPower; -uniform vec2 topLeft; -uniform vec2 fullSize; #include "rounding.glsl" -#endif layout(location = 0) out vec4 fragColor; void main() { vec4 pixColor = v_color; -#if USE_ROUNDING - pixColor = rounding(pixColor, radius, roundingPower, topLeft, fullSize); -#endif + if (radius > 0.0) + pixColor = rounding(pixColor); fragColor = pixColor; } diff --git a/src/render/shaders/glsl/rounding.glsl b/src/render/shaders/glsl/rounding.glsl index 61a0bb9c..472415fd 100644 --- a/src/render/shaders/glsl/rounding.glsl +++ b/src/render/shaders/glsl/rounding.glsl @@ -1,10 +1,13 @@ -#ifndef ROUNDING_GLSL -#define ROUNDING_GLSL // smoothing constant for the edge: more = blurrier, but smoother -#define M_PI 3.1415926535897932384626433832795 +#define M_PI 3.1415926535897932384626433832795 #define SMOOTHING_CONSTANT (M_PI / 5.34665792551) -vec4 rounding(vec4 color, float radius, float roundingPower, vec2 topLeft, vec2 fullSize) { +uniform float radius; +uniform float roundingPower; +uniform vec2 topLeft; +uniform vec2 fullSize; + +vec4 rounding(vec4 color) { vec2 pixCoord = vec2(gl_FragCoord); pixCoord -= topLeft + fullSize * 0.5; pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0; @@ -12,7 +15,7 @@ vec4 rounding(vec4 color, float radius, float roundingPower, vec2 topLeft, vec2 pixCoord += vec2(1.0, 1.0) / fullSize; // center the pix don't make it top-left if (pixCoord.x + pixCoord.y > radius) { - float dist = pow(pow(pixCoord.x, roundingPower) + pow(pixCoord.y, roundingPower), 1.0 / roundingPower); + float dist = pow(pow(pixCoord.x, roundingPower) + pow(pixCoord.y, roundingPower), 1.0/roundingPower); if (dist > radius + SMOOTHING_CONSTANT) discard; @@ -24,4 +27,3 @@ vec4 rounding(vec4 color, float radius, float roundingPower, vec2 topLeft, vec2 return color; } -#endif \ No newline at end of file diff --git a/src/render/shaders/glsl/sdr_mod.glsl b/src/render/shaders/glsl/sdr_mod.glsl new file mode 100644 index 00000000..7803d804 --- /dev/null +++ b/src/render/shaders/glsl/sdr_mod.glsl @@ -0,0 +1,10 @@ +uniform float sdrSaturation; +uniform float sdrBrightnessMultiplier; + +vec4 saturate(vec4 color, mat3 primaries, float saturation) { + if (saturation == 1.0) + return color; + vec3 brightness = vec3(primaries[1][0], primaries[1][1], primaries[1][2]); + float Y = dot(color.rgb, brightness); + return vec4(mix(vec3(Y), color.rgb, saturation), color[3]); +} diff --git a/src/render/shaders/glsl/shadow.frag b/src/render/shaders/glsl/shadow.frag index c23ebd5d..71e96ddb 100644 --- a/src/render/shaders/glsl/shadow.frag +++ b/src/render/shaders/glsl/shadow.frag @@ -1,57 +1,99 @@ #version 300 es -#define ALLOW_INCLUDES #extension GL_ARB_shading_language_include : enable -#include "defines.h" +precision highp float; +in vec4 v_color; +in vec2 v_texcoord; -precision highp float; -in vec4 v_color; -in vec2 v_texcoord; +uniform int skipCM; +uniform int sourceTF; // eTransferFunction +uniform int targetTF; // eTransferFunction +uniform mat3 targetPrimariesXYZ; -uniform int sourceTF; // eTransferFunction -uniform int targetTF; // eTransferFunction -uniform mat3 targetPrimariesXYZ; - -uniform vec2 topLeft; -uniform vec2 bottomRight; -uniform vec2 fullSize; +uniform vec2 topLeft; +uniform vec2 bottomRight; +uniform vec2 fullSize; uniform float radius; uniform float roundingPower; uniform float range; uniform float shadowPower; -#if USE_CM -#include "cm_helpers.glsl" #include "CM.glsl" -#endif -#include "shadow.glsl" +float pixAlphaRoundedDistance(float distanceToCorner) { + if (distanceToCorner > radius) { + return 0.0; + } + + if (distanceToCorner > radius - range) { + return pow((range - (distanceToCorner - radius + range)) / range, shadowPower); // i think? + } + + return 1.0; +} + +float modifiedLength(vec2 a) { + return pow(pow(abs(a.x),roundingPower)+pow(abs(a.y),roundingPower),1.0/roundingPower); +} layout(location = 0) out vec4 fragColor; void main() { - vec4 pixColor = v_color; - fragColor = getShadow(pixColor, v_texcoord, radius, roundingPower, topLeft, fullSize, range, shadowPower, bottomRight -#if USE_CM - , - sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange -#if USE_ICC - , - iccLut3D, iccLutSize -#else -#if USE_TONEMAP || USE_SDR_MOD - , - targetPrimariesXYZ -#endif -#if USE_TONEMAP - , - maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance -#endif -#if USE_SDR_MOD - , - sdrSaturation, sdrBrightnessMultiplier -#endif -#endif -#endif - ); + vec4 pixColor = v_color; + float originalAlpha = pixColor[3]; + + bool done = false; + + vec2 pixCoord = fullSize * v_texcoord; + + // ok, now we check the distance to a border. + + if (pixCoord[0] < topLeft[0]) { + if (pixCoord[1] < topLeft[1]) { + // top left + pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - topLeft)); + done = true; + } else if (pixCoord[1] > bottomRight[1]) { + // bottom left + pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - vec2(topLeft[0], bottomRight[1]))); + done = true; + } + } else if (pixCoord[0] > bottomRight[0]) { + if (pixCoord[1] < topLeft[1]) { + // top right + pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - vec2(bottomRight[0], topLeft[1]))); + done = true; + } else if (pixCoord[1] > bottomRight[1]) { + // bottom right + pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - bottomRight)); + done = true; + } + } + + if (!done) { + // distance to all straight bb borders + float distanceT = pixCoord[1]; + float distanceB = fullSize[1] - pixCoord[1]; + float distanceL = pixCoord[0]; + float distanceR = fullSize[0] - pixCoord[0]; + + // get the smallest + float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR)); + + if (smallest < range) { + pixColor[3] = pixColor[3] * pow((smallest / range), shadowPower); + } + } + + if (pixColor[3] == 0.0) { + discard; return; + } + + // premultiply + pixColor.rgb *= pixColor[3]; + + if (skipCM == 0) + pixColor = doColorManagement(pixColor, sourceTF, targetTF, targetPrimariesXYZ); + + fragColor = pixColor; } \ No newline at end of file diff --git a/src/render/shaders/glsl/shadow.glsl b/src/render/shaders/glsl/shadow.glsl deleted file mode 100644 index 48cde562..00000000 --- a/src/render/shaders/glsl/shadow.glsl +++ /dev/null @@ -1,126 +0,0 @@ -#ifndef ALLOW_INCLUDES -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable -#endif -#ifndef SHADOW_GLSL -#define SHADOW_GLSL - -#include "cm_helpers.glsl" - -float pixAlphaRoundedDistance(float distanceToCorner, float radius, float range, float shadowPower) { - if (distanceToCorner > radius) { - return 0.0; - } - - if (distanceToCorner > radius - range) { - return pow((range - (distanceToCorner - radius + range)) / range, shadowPower); // i think? - } - - return 1.0; -} - -float modifiedLength(vec2 a, float roundingPower) { - return pow(pow(abs(a.x), roundingPower) + pow(abs(a.y), roundingPower), 1.0 / roundingPower); -} - -vec4 getShadow(vec4 pixColor, vec2 v_texcoord, float radius, float roundingPower, vec2 topLeft, vec2 fullSize, float range, float shadowPower, vec2 bottomRight -#if USE_CM - , - int sourceTF, int targetTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange -#if USE_ICC - , - highp sampler3D iccLut3D, float iccLutSize -#else -#if USE_TONEMAP || USE_SDR_MOD - , - mat3 targetPrimariesXYZ -#endif -#if USE_TONEMAP - , - float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance -#endif -#if USE_SDR_MOD - , - float sdrSaturation, float sdrBrightnessMultiplier -#endif -#endif -#endif -) { - float originalAlpha = pixColor[3]; - - bool done = false; - - vec2 pixCoord = fullSize * v_texcoord; - - // ok, now we check the distance to a border. - - if (pixCoord[0] < topLeft[0]) { - if (pixCoord[1] < topLeft[1]) { - // top left - pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - topLeft, roundingPower), radius, range, shadowPower); - done = true; - } else if (pixCoord[1] > bottomRight[1]) { - // bottom left - pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - vec2(topLeft[0], bottomRight[1]), roundingPower), radius, range, shadowPower); - done = true; - } - } else if (pixCoord[0] > bottomRight[0]) { - if (pixCoord[1] < topLeft[1]) { - // top right - pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - vec2(bottomRight[0], topLeft[1]), roundingPower), radius, range, shadowPower); - done = true; - } else if (pixCoord[1] > bottomRight[1]) { - // bottom right - pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - bottomRight, roundingPower), radius, range, shadowPower); - done = true; - } - } - - if (!done) { - // distance to all straight bb borders - float distanceT = pixCoord[1]; - float distanceB = fullSize[1] - pixCoord[1]; - float distanceL = pixCoord[0]; - float distanceR = fullSize[0] - pixCoord[0]; - - // get the smallest - float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR)); - - if (smallest < range) { - pixColor[3] = pixColor[3] * pow((smallest / range), shadowPower); - } - } - - if (pixColor[3] == 0.0) { - discard; - return pixColor; - } - - // premultiply - pixColor.rgb *= pixColor[3]; - -#if USE_CM - pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange -#if USE_ICC - , - iccLut3D, iccLutSize -#else -#if USE_TONEMAP || USE_SDR_MOD - , - targetPrimariesXYZ -#endif -#if USE_TONEMAP - , - maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance -#endif -#if USE_SDR_MOD - , - sdrSaturation, sdrBrightnessMultiplier -#endif -#endif - ); -#endif - - return pixColor; -} -#endif \ No newline at end of file diff --git a/src/render/shaders/glsl/surface.frag b/src/render/shaders/glsl/surface.frag index 30023bc8..1d3e80b8 100644 --- a/src/render/shaders/glsl/surface.frag +++ b/src/render/shaders/glsl/surface.frag @@ -1,104 +1,25 @@ #version 300 es -#define ALLOW_INCLUDES #extension GL_ARB_shading_language_include : enable -#include "defines.h" - -precision highp float; -in vec2 v_texcoord; +precision highp float; +in vec2 v_texcoord; uniform sampler2D tex; -#if USE_BLUR -uniform vec2 uvSize; -uniform vec2 uvOffset; -uniform sampler2D blurredBG; -#endif uniform float alpha; -#if USE_DISCARD -uniform bool discardOpaque; -uniform bool discardAlpha; -uniform float discardAlphaValue; -#endif - -#if USE_TINT -uniform vec3 tint; -#endif - -#if USE_ROUNDING -uniform float radius; -uniform float roundingPower; -uniform vec2 topLeft; -uniform vec2 fullSize; +#include "discard.glsl" +#include "tint.glsl" #include "rounding.glsl" -#endif - -#if USE_CM -uniform int sourceTF; // eTransferFunction -uniform int targetTF; // eTransferFunction - -#if USE_TONEMAP || USE_SDR_MOD -uniform mat3 targetPrimariesXYZ; -#else -const mat3 targetPrimariesXYZ = mat3(0.0); -#endif - -#include "CM.glsl" -#endif +#include "surface_CM.glsl" layout(location = 0) out vec4 fragColor; void main() { -#if USE_RGBA - vec4 pixColor = texture(tex, v_texcoord); -#else - vec4 pixColor = vec4(texture(tex, v_texcoord).rgb, 1.0); -#endif + #include "get_rgb_pixel.glsl" -#if USE_DISCARD && !USE_BLUR - if (discardOpaque && pixColor.a * alpha == 1.0) - discard; - - if (discardAlpha && pixColor.a <= discardAlphaValue) - discard; -#endif - -#if USE_CM - pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange -#if USE_ICC - , - iccLut3D, iccLutSize -#else -#if USE_TONEMAP || USE_SDR_MOD - , - targetPrimariesXYZ -#endif -#if USE_TONEMAP - , - maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance -#endif -#if USE_SDR_MOD - , - sdrSaturation, sdrBrightnessMultiplier -#endif -#endif - ); -#endif - -#if USE_TINT - pixColor.rgb = pixColor.rgb * tint; -#endif - -#if USE_ROUNDING - pixColor = rounding(pixColor, radius, roundingPower, topLeft, fullSize); -#endif -#if USE_BLUR -#if USE_DISCARD - pixColor = mix(pixColor, vec4(mix(texture(blurredBG, v_texcoord * uvSize + uvOffset).rgb, pixColor.rgb, pixColor.a), 1.0), - discardAlpha && (pixColor.a <= discardAlphaValue) ? 0.0 : 1.0); -#else - pixColor = vec4(mix(texture(blurredBG, v_texcoord * uvSize + uvOffset).rgb, pixColor.rgb, pixColor.a), 1.0); -#endif -#endif + #include "do_discard.glsl" + #include "do_CM.glsl" + #include "do_tint.glsl" + #include "do_rounding.glsl" fragColor = pixColor * alpha; } diff --git a/src/render/shaders/glsl/surface_CM.glsl b/src/render/shaders/glsl/surface_CM.glsl new file mode 100644 index 00000000..f90b23c2 --- /dev/null +++ b/src/render/shaders/glsl/surface_CM.glsl @@ -0,0 +1,4 @@ +uniform int sourceTF; // eTransferFunction +uniform int targetTF; // eTransferFunction +#include "primaries_xyz.glsl" +#include "CM.glsl" diff --git a/src/render/shaders/glsl/tint.glsl b/src/render/shaders/glsl/tint.glsl new file mode 100644 index 00000000..1523100e --- /dev/null +++ b/src/render/shaders/glsl/tint.glsl @@ -0,0 +1 @@ +uniform vec3 tint; diff --git a/src/render/shaders/glsl/tonemap.glsl b/src/render/shaders/glsl/tonemap.glsl index a0ba24ef..f6ac01f0 100644 --- a/src/render/shaders/glsl/tonemap.glsl +++ b/src/render/shaders/glsl/tonemap.glsl @@ -1,15 +1,17 @@ -#ifndef ALLOW_INCLUDES -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable -#endif -#include "constants.h" +uniform float maxLuminance; +uniform float dstMaxLuminance; +uniform float dstRefLuminance; -const mat3 BT2020toLMS = mat3(0.3592, 0.6976, -0.0358, -0.1922, 1.1004, 0.0755, 0.0070, 0.0749, 0.8434); +const mat3 BT2020toLMS = mat3( + 0.3592, 0.6976, -0.0358, + -0.1922, 1.1004, 0.0755, + 0.0070, 0.0749, 0.8434 +); //const mat3 LMStoBT2020 = inverse(BT2020toLMS); -const mat3 LMStoBT2020 = mat3( // - 2.0701800566956135096, -1.3264568761030210255, 0.20661600684785517081, // - 0.36498825003265747974, 0.68046736285223514102, -0.045421753075853231409, // - -0.049595542238932107896, -0.049421161186757487412, 1.1879959417328034394 // +const mat3 LMStoBT2020 = mat3( + 2.0701800566956135096, -1.3264568761030210255, 0.20661600684785517081, + 0.36498825003265747974, 0.68046736285223514102, -0.045421753075853231409, + -0.049595542238932107896, -0.049421161186757487412, 1.1879959417328034394 ); // const mat3 ICtCpPQ = transpose(mat3( @@ -17,16 +19,16 @@ const mat3 LMStoBT2020 = mat3( // // 6610.0, -13613.0, 7003.0, // 17933.0, -17390.0, -543.0 // ) / 4096.0); -const mat3 ICtCpPQ = mat3( // - 0.5, 1.61376953125, 4.378173828125, // - 0.5, -3.323486328125, -4.24560546875, // - 0.0, 1.709716796875, -0.132568359375 // +const mat3 ICtCpPQ = mat3( + 0.5, 1.61376953125, 4.378173828125, + 0.5, -3.323486328125, -4.24560546875, + 0.0, 1.709716796875, -0.132568359375 ); //const mat3 ICtCpPQInv = inverse(ICtCpPQ); -const mat3 ICtCpPQInv = mat3( // - 1.0, 1.0, 1.0, // - 0.0086090370379327566, -0.0086090370379327566, 0.560031335710679118, // - 0.11102962500302595656, -0.11102962500302595656, -0.32062717498731885185 // +const mat3 ICtCpPQInv = mat3( + 1.0, 1.0, 1.0, + 0.0086090370379327566, -0.0086090370379327566, 0.560031335710679118, + 0.11102962500302595656, -0.11102962500302595656, -0.32062717498731885185 ); // unused for now @@ -37,28 +39,31 @@ const mat3 ICtCpPQInv = mat3( // // ) / 4096.0); // const mat3 ICtCpHLGInv = inverse(ICtCpHLG); -vec4 tonemap(vec4 color, mat3 dstXYZ, float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance) { +vec4 tonemap(vec4 color, mat3 dstXYZ) { if (maxLuminance < dstMaxLuminance * 1.01) return vec4(clamp(color.rgb, vec3(0.0), vec3(dstMaxLuminance)), color[3]); - mat3 toLMS = BT2020toLMS * dstXYZ; - mat3 fromLMS = inverse(dstXYZ) * LMStoBT2020; + mat3 toLMS = BT2020toLMS * dstXYZ; + mat3 fromLMS = inverse(dstXYZ) * LMStoBT2020; - vec3 lms = fromLinear(vec4((toLMS * color.rgb) / HDR_MAX_LUMINANCE, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb; - vec3 ICtCp = ICtCpPQ * lms; + vec3 lms = fromLinear(vec4((toLMS * color.rgb) / HDR_MAX_LUMINANCE, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb; + vec3 ICtCp = ICtCpPQ * lms; - float E = pow(clamp(ICtCp[0], 0.0, 1.0), PQ_INV_M2); - float luminance = pow((max(E - PQ_C1, 0.0)) / (PQ_C2 - PQ_C3 * E), PQ_INV_M1) * HDR_MAX_LUMINANCE; + float E = pow(clamp(ICtCp[0], 0.0, 1.0), PQ_INV_M2); + float luminance = pow( + (max(E - PQ_C1, 0.0)) / (PQ_C2 - PQ_C3 * E), + PQ_INV_M1 + ) * HDR_MAX_LUMINANCE; - float linearPart = min(luminance, dstRefLuminance); - float luminanceAboveRef = max(luminance - dstRefLuminance, 0.0); + float linearPart = min(luminance, dstRefLuminance); + float luminanceAboveRef = max(luminance - dstRefLuminance, 0.0); float maxExcessLuminance = max(maxLuminance - dstRefLuminance, 1.0); - float shoulder = log((luminanceAboveRef / maxExcessLuminance + 1.0) * (M_E - 1.0)); - float mappedHigh = shoulder * (dstMaxLuminance - dstRefLuminance); - float newLum = clamp(linearPart + mappedHigh, 0.0, dstMaxLuminance); + float shoulder = log((luminanceAboveRef / maxExcessLuminance + 1.0) * (M_E - 1.0)); + float mappedHigh = shoulder * (dstMaxLuminance - dstRefLuminance); + float newLum = clamp(linearPart + mappedHigh, 0.0, dstMaxLuminance); // scale src to dst reference float refScale = dstRefLuminance / srcRefLuminance; - return vec4(fromLMS * toLinear(vec4(ICtCpPQInv * ICtCp, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb * HDR_MAX_LUMINANCE * refScale, color[3]); + return vec4(fromLMS * toLinear(vec4(ICtCpPQInv * ICtCp, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb * HDR_MAX_LUMINANCE * refScale, color[3]); }