From 90ab765ec59d64f14f231a026bf15a0e2b506725 Mon Sep 17 00:00:00 2001 From: Joshua Tam <297250+joshuatam@users.noreply.github.com> Date: Tue, 24 Dec 2024 09:14:05 +0800 Subject: [PATCH] feature: add rotation shader for rotating output --- src/Backends/DRMBackend.cpp | 29 +++++++- src/main.cpp | 6 ++ src/main.hpp | 1 + src/meson.build | 1 + src/rendervulkan.cpp | 126 ++++++++++++++++++++++++++++++----- src/rendervulkan.hpp | 6 +- src/shaders/cs_rotation.comp | 53 +++++++++++++++ src/wlserver.cpp | 5 ++ 8 files changed, 208 insertions(+), 19 deletions(-) create mode 100644 src/shaders/cs_rotation.comp diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 0b121e8416..e40cf67130 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -1752,7 +1752,7 @@ LiftoffStateCacheEntry FrameInfoToLiftoffStateCacheEntry( struct drm_t *drm, con uint64_t crtcW = srcWidth / frameInfo->layers[ i ].scale.x; uint64_t crtcH = srcHeight / frameInfo->layers[ i ].scale.y; - if (g_bRotated) + if (g_bRotated && !g_bUseRotationShader) { int64_t imageH = frameInfo->layers[ i ].tex->contentHeight() / frameInfo->layers[ i ].scale.y; @@ -2045,6 +2045,17 @@ namespace gamescope void CDRMConnector::UpdateEffectiveOrientation( const drmModeModeInfo *pMode ) { + if (g_bUseRotationShader) + { + drm_log.infof("Using rotation shader"); + if (g_DesiredInternalOrientation == GAMESCOPE_PANEL_ORIENTATION_270) { + m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_180; + } else { + m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_0; + } + return; + } + if ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO ) { m_ChosenOrientation = g_DesiredInternalOrientation; @@ -3019,6 +3030,15 @@ bool drm_set_mode( struct drm_t *drm, const drmModeModeInfo *mode ) g_bRotated = false; g_nOutputWidth = mode->hdisplay; g_nOutputHeight = mode->vdisplay; + + if (g_bUseRotationShader && drm->pConnector->GetScreenType() == gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL) { + g_bRotated = true; + g_nOutputWidth = mode->vdisplay; + g_nOutputHeight = mode->hdisplay; + } else { + g_bUseRotationShader = false; + } + break; case GAMESCOPE_PANEL_ORIENTATION_90: case GAMESCOPE_PANEL_ORIENTATION_270: @@ -3278,6 +3298,11 @@ namespace gamescope bNeedsFullComposite |= !!(g_uCompositeDebug & CompositeDebugFlag::Heatmap); + if (g_bUseRotationShader) + { + bNeedsFullComposite = true; + } + bool bDoComposite = true; if ( !bNeedsFullComposite && !bWantsPartialComposite ) { @@ -3368,7 +3393,7 @@ namespace gamescope if ( bDefer && !!( g_uCompositeDebug & CompositeDebugFlag::Markers ) ) g_uCompositeDebug |= CompositeDebugFlag::Markers_Partial; - std::optional oCompositeResult = vulkan_composite( &compositeFrameInfo, nullptr, !bNeedsFullComposite ); + std::optional oCompositeResult = vulkan_composite( &compositeFrameInfo, nullptr, !bNeedsFullComposite, nullptr, true, nullptr, g_bUseRotationShader ); m_bWasCompositing = true; diff --git a/src/main.cpp b/src/main.cpp index 9dff5c4d85..c716b81b73 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -127,6 +127,7 @@ const struct option *gamescope_options = (struct option[]){ { "composite-debug", no_argument, nullptr, 0 }, { "disable-xres", no_argument, nullptr, 'x' }, { "fade-out-duration", required_argument, nullptr, 0 }, + { "use-rotation-shader", required_argument, nullptr, 0 }, { "force-orientation", required_argument, nullptr, 0 }, { "force-windows-fullscreen", no_argument, nullptr, 0 }, @@ -189,6 +190,7 @@ const char usage[] = " -e, --steam enable Steam integration\n" " --xwayland-count create N xwayland servers\n" " --prefer-vk-device prefer Vulkan device for compositing (ex: 1002:7300)\n" + " --use-rotation-shader use rotation shader for rotating the screen\n" " --force-orientation rotate the internal display (left, right, normal, upsidedown)\n" " --force-windows-fullscreen force windows inside of gamescope to be the size of the nested display (fullscreen)\n" " --cursor-scale-height if specified, sets a base output height to linearly scale the cursor against.\n" @@ -346,6 +348,8 @@ static gamescope::GamescopeModeGeneration parse_gamescope_mode_generation( const } } +bool g_bUseRotationShader = false; + GamescopePanelOrientation g_DesiredInternalOrientation = GAMESCOPE_PANEL_ORIENTATION_AUTO; static GamescopePanelOrientation force_orientation(const char *str) { @@ -744,6 +748,8 @@ int main(int argc, char **argv) gamescope::cv_touch_click_mode = (gamescope::TouchClickMode) atoi( optarg ); } else if (strcmp(opt_name, "generate-drm-mode") == 0) { g_eGamescopeModeGeneration = parse_gamescope_mode_generation( optarg ); + } else if (strcmp(opt_name, "use-rotation-shader") == 0) { + g_bUseRotationShader = true; } else if (strcmp(opt_name, "force-orientation") == 0) { g_DesiredInternalOrientation = force_orientation( optarg ); } else if (strcmp(opt_name, "sharpness") == 0 || diff --git a/src/main.hpp b/src/main.hpp index 2e6fb833af..e33cb275f5 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -21,6 +21,7 @@ extern bool g_bForceRelativeMouse; extern int g_nOutputRefresh; // mHz extern bool g_bOutputHDREnabled; extern bool g_bForceInternal; +extern bool g_bUseRotationShader; extern bool g_bFullscreen; diff --git a/src/meson.build b/src/meson.build index 74fc0334d4..d4ff3ea142 100644 --- a/src/meson.build +++ b/src/meson.build @@ -70,6 +70,7 @@ shader_src = [ 'shaders/cs_nis.comp', 'shaders/cs_nis_fp16.comp', 'shaders/cs_rgb_to_nv12.comp', + 'shaders/cs_rotation.comp', ] spirv_shaders = glsl_generator.process(shader_src) diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp index 54d7608dd2..10d6c787f6 100644 --- a/src/rendervulkan.cpp +++ b/src/rendervulkan.cpp @@ -48,6 +48,7 @@ #include "cs_nis.h" #include "cs_nis_fp16.h" #include "cs_rgb_to_nv12.h" +#include "cs_rotation.h" #define A_CPU #include "shaders/ffx_a.h" @@ -898,6 +899,7 @@ bool CVulkanDevice::createShaders() SHADER(NIS, cs_nis); } SHADER(RGB_TO_NV12, cs_rgb_to_nv12); + SHADER(ROTATION, cs_rotation); #undef SHADER for (uint32_t i = 0; i < shaderInfos.size(); i++) @@ -1128,6 +1130,7 @@ void CVulkanDevice::compileAllPipelines() SHADER(EASU, 1, 1, 1); SHADER(NIS, 1, 1, 1); SHADER(RGB_TO_NV12, 1, 1, 1); + SHADER(ROTATION, k_nMaxLayers, k_nMaxYcbcrMask_ToPreCompile, k_nMaxBlurLayers); #undef SHADER for (auto& info : pipelineInfos) { @@ -3214,8 +3217,16 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput ) uint32_t uDRMFormat = pOutput->uOutputFormat; + uint32_t l_nOutputWidth = g_nOutputWidth; + uint32_t l_nOutputHeight = g_nOutputHeight; + + if (g_bUseRotationShader) { + l_nOutputWidth = g_nOutputHeight; + l_nOutputHeight = g_nOutputWidth; + } + pOutput->outputImages[0] = new CVulkanTexture(); - bool bSuccess = pOutput->outputImages[0]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, uDRMFormat, outputImageflags ); + bool bSuccess = pOutput->outputImages[0]->BInit( l_nOutputWidth, l_nOutputHeight, 1u, uDRMFormat, outputImageflags ); if ( bSuccess != true ) { vk_log.errorf( "failed to allocate buffer for KMS" ); @@ -3223,7 +3234,7 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput ) } pOutput->outputImages[1] = new CVulkanTexture(); - bSuccess = pOutput->outputImages[1]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, uDRMFormat, outputImageflags ); + bSuccess = pOutput->outputImages[1]->BInit( l_nOutputWidth, l_nOutputHeight, 1u, uDRMFormat, outputImageflags ); if ( bSuccess != true ) { vk_log.errorf( "failed to allocate buffer for KMS" ); @@ -3231,7 +3242,7 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput ) } pOutput->outputImages[2] = new CVulkanTexture(); - bSuccess = pOutput->outputImages[2]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, uDRMFormat, outputImageflags ); + bSuccess = pOutput->outputImages[2]->BInit( l_nOutputWidth, l_nOutputHeight, 1u, uDRMFormat, outputImageflags ); if ( bSuccess != true ) { vk_log.errorf( "failed to allocate buffer for KMS" ); @@ -3246,7 +3257,7 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput ) uint32_t uPartialDRMFormat = pOutput->uOutputFormatOverlay; pOutput->outputImagesPartialOverlay[0] = new CVulkanTexture(); - bool bSuccess = pOutput->outputImagesPartialOverlay[0]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, uPartialDRMFormat, outputImageflags, nullptr, 0, 0, pOutput->outputImages[0].get() ); + bool bSuccess = pOutput->outputImagesPartialOverlay[0]->BInit( l_nOutputWidth, l_nOutputHeight, 1u, uPartialDRMFormat, outputImageflags, nullptr, 0, 0, pOutput->outputImages[0].get() ); if ( bSuccess != true ) { vk_log.errorf( "failed to allocate buffer for KMS" ); @@ -3254,7 +3265,7 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput ) } pOutput->outputImagesPartialOverlay[1] = new CVulkanTexture(); - bSuccess = pOutput->outputImagesPartialOverlay[1]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, uPartialDRMFormat, outputImageflags, nullptr, 0, 0, pOutput->outputImages[1].get() ); + bSuccess = pOutput->outputImagesPartialOverlay[1]->BInit( l_nOutputWidth, l_nOutputHeight, 1u, uPartialDRMFormat, outputImageflags, nullptr, 0, 0, pOutput->outputImages[1].get() ); if ( bSuccess != true ) { vk_log.errorf( "failed to allocate buffer for KMS" ); @@ -3262,7 +3273,7 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput ) } pOutput->outputImagesPartialOverlay[2] = new CVulkanTexture(); - bSuccess = pOutput->outputImagesPartialOverlay[2]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, uPartialDRMFormat, outputImageflags, nullptr, 0, 0, pOutput->outputImages[2].get() ); + bSuccess = pOutput->outputImagesPartialOverlay[2]->BInit( l_nOutputWidth, l_nOutputHeight, 1u, uPartialDRMFormat, outputImageflags, nullptr, 0, 0, pOutput->outputImages[2].get() ); if ( bSuccess != true ) { vk_log.errorf( "failed to allocate buffer for KMS" ); @@ -3392,6 +3403,28 @@ static void update_tmp_images( uint32_t width, uint32_t height ) } } +static void update_rotated_images( uint32_t width, uint32_t height ) +{ + if ( g_output.rotatedOutput != nullptr + && width == g_output.rotatedOutput->width() + && height == g_output.rotatedOutput->height() ) + { + return; + } + + CVulkanTexture::createFlags createFlags; + createFlags.bSampled = true; + createFlags.bStorage = true; + + g_output.rotatedOutput = new CVulkanTexture(); + bool bSuccess = g_output.rotatedOutput->BInit( width, height, 1u, DRM_FORMAT_ARGB8888, createFlags, nullptr ); + + if ( !bSuccess ) + { + vk_log.errorf( "failed to create rotated output" ); + return; + } +} static bool init_nis_data() { @@ -3856,7 +3889,7 @@ std::optional vulkan_screenshot( const struct FrameInfo_t *frameInfo, extern std::string g_reshade_effect; extern uint32_t g_reshade_technique_idx; -std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamescope::Rc pPipewireTexture, bool partial, gamescope::Rc pOutputOverride, bool increment, std::unique_ptr pInCommandBuffer ) +std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamescope::Rc pPipewireTexture, bool partial, gamescope::Rc pOutputOverride, bool increment, std::unique_ptr pInCommandBuffer, bool applyRotation ) { EOTF outputTF = frameInfo->outputEncodingEOTF; if (!frameInfo->applyOutputColorMgmt) @@ -3928,7 +3961,15 @@ std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamesco cmdBuffer->setTextureSrgb(0, true); cmdBuffer->setSamplerUnnormalized(0, false); cmdBuffer->setSamplerNearest(0, false); - cmdBuffer->bindTarget(compositeImage); + + if (applyRotation) { + // Make a rotatedOutput with normal dimensions + update_rotated_images(currentOutputWidth, currentOutputHeight); // 2560x1600 + cmdBuffer->bindTarget(g_output.rotatedOutput); + } else { + cmdBuffer->bindTarget(compositeImage); + } + cmdBuffer->uploadConstants(frameInfo, g_upscaleFilterSharpness / 10.0f); cmdBuffer->dispatch(div_roundup(currentOutputWidth, pixelsPerGroup), div_roundup(currentOutputHeight, pixelsPerGroup)); @@ -3971,7 +4012,15 @@ std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamesco cmdBuffer->bindPipeline( g_device.pipeline(SHADER_TYPE_BLIT, nisFrameInfo.layerCount, nisFrameInfo.ycbcrMask(), 0u, nisFrameInfo.colorspaceMask(), outputTF )); bind_all_layers(cmdBuffer.get(), &nisFrameInfo); - cmdBuffer->bindTarget(compositeImage); + + if (applyRotation) { + // Make a rotatedOutput with normal dimensions + update_rotated_images(currentOutputWidth, currentOutputHeight); // 2560x1600 + cmdBuffer->bindTarget(g_output.rotatedOutput); + } else { + cmdBuffer->bindTarget(compositeImage); + } + cmdBuffer->uploadConstants(&nisFrameInfo); int pixelsPerGroup = 8; @@ -4009,7 +4058,15 @@ std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamesco type = frameInfo->blurLayer0 == BLUR_MODE_COND ? SHADER_TYPE_BLUR_COND : SHADER_TYPE_BLUR; cmdBuffer->bindPipeline(g_device.pipeline(type, frameInfo->layerCount, frameInfo->ycbcrMask(), blur_layer_count, frameInfo->colorspaceMask(), outputTF )); bind_all_layers(cmdBuffer.get(), frameInfo); - cmdBuffer->bindTarget(compositeImage); + + if (applyRotation) { + // Make a rotatedOutput with normal dimensions + update_rotated_images(currentOutputWidth, currentOutputHeight); // 2560x1600 + cmdBuffer->bindTarget(g_output.rotatedOutput); + } else { + cmdBuffer->bindTarget(compositeImage); + } + cmdBuffer->bindTexture(VKR_BLUR_EXTRA_SLOT, g_output.tmpOutput); cmdBuffer->setTextureSrgb(VKR_BLUR_EXTRA_SLOT, !useSrgbView); // Inverted because it chooses whether to view as linear (sRGB view) or sRGB (raw view). It's horrible. I need to change it. cmdBuffer->setSamplerUnnormalized(VKR_BLUR_EXTRA_SLOT, true); @@ -4019,14 +4076,51 @@ std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamesco } else { - cmdBuffer->bindPipeline( g_device.pipeline(SHADER_TYPE_BLIT, frameInfo->layerCount, frameInfo->ycbcrMask(), 0u, frameInfo->colorspaceMask(), outputTF )); - bind_all_layers(cmdBuffer.get(), frameInfo); - cmdBuffer->bindTarget(compositeImage); - cmdBuffer->uploadConstants(frameInfo); + if (applyRotation) { + cmdBuffer->bindPipeline( g_device.pipeline(SHADER_TYPE_ROTATION, frameInfo->layerCount, frameInfo->ycbcrMask(), 0u, frameInfo->colorspaceMask(), outputTF )); + bind_all_layers(cmdBuffer.get(), frameInfo); + cmdBuffer->bindTarget(compositeImage); + cmdBuffer->uploadConstants(frameInfo); - const int pixelsPerGroup = 8; + const int pixelsPerGroup = 8; - cmdBuffer->dispatch(div_roundup(currentOutputWidth, pixelsPerGroup), div_roundup(currentOutputHeight, pixelsPerGroup)); + cmdBuffer->dispatch(div_roundup(currentOutputWidth, pixelsPerGroup), div_roundup(currentOutputHeight, pixelsPerGroup)); + } else { + cmdBuffer->bindPipeline( g_device.pipeline(SHADER_TYPE_BLIT, frameInfo->layerCount, frameInfo->ycbcrMask(), 0u, frameInfo->colorspaceMask(), outputTF )); + bind_all_layers(cmdBuffer.get(), frameInfo); + cmdBuffer->bindTarget(compositeImage); + cmdBuffer->uploadConstants(frameInfo); + + const int pixelsPerGroup = 8; + + cmdBuffer->dispatch(div_roundup(currentOutputWidth, pixelsPerGroup), div_roundup(currentOutputHeight, pixelsPerGroup)); + } + } + + if (applyRotation) + { + if (g_output.rotatedOutput != nullptr) { + // Rotate the final output + // TODO: may need rework with another rotation shader for blur, fsr and nis + cmdBuffer->bindPipeline( g_device.pipeline(SHADER_TYPE_ROTATION, frameInfo->layerCount, frameInfo->ycbcrMask(), 0u, frameInfo->colorspaceMask(), outputTF)); + bind_all_layers(cmdBuffer.get(), frameInfo); + + // if (frameInfo->blurLayer0) { + // bool useSrgbView = frameInfo->layers[0].colorspace == GAMESCOPE_APP_TEXTURE_COLORSPACE_LINEAR; + // + // cmdBuffer->bindTexture(VKR_BLUR_EXTRA_SLOT, g_output.rotatedOutput); + // cmdBuffer->setTextureSrgb(VKR_BLUR_EXTRA_SLOT, !useSrgbView); + // cmdBuffer->setSamplerUnnormalized(VKR_BLUR_EXTRA_SLOT, true); + // cmdBuffer->setSamplerNearest(VKR_BLUR_EXTRA_SLOT, false); + // } + + cmdBuffer->bindTarget(compositeImage); + cmdBuffer->uploadConstants(frameInfo); + + const int pixelsPerGroup = 8; + + cmdBuffer->dispatch(div_roundup(currentOutputWidth, pixelsPerGroup), div_roundup(currentOutputHeight, pixelsPerGroup)); + } } if ( pPipewireTexture != nullptr ) diff --git a/src/rendervulkan.hpp b/src/rendervulkan.hpp index b5371703c0..78e61305cd 100644 --- a/src/rendervulkan.hpp +++ b/src/rendervulkan.hpp @@ -391,7 +391,7 @@ gamescope::OwningRc vulkan_create_texture_from_dmabuf( struct wl gamescope::OwningRc vulkan_create_texture_from_bits( uint32_t width, uint32_t height, uint32_t contentWidth, uint32_t contentHeight, uint32_t drmFormat, CVulkanTexture::createFlags texCreateFlags, void *bits ); gamescope::OwningRc vulkan_create_texture_from_wlr_buffer( struct wlr_buffer *buf, gamescope::OwningRc pBackendFb ); -std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamescope::Rc pScreenshotTexture, bool partial, gamescope::Rc pOutputOverride = nullptr, bool increment = true, std::unique_ptr pInCommandBuffer = nullptr ); +std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamescope::Rc pScreenshotTexture, bool partial, gamescope::Rc pOutputOverride = nullptr, bool increment = true, std::unique_ptr pInCommandBuffer = nullptr, bool applyRotation = false ); void vulkan_wait( uint64_t ulSeqNo, bool bReset ); gamescope::Rc vulkan_get_last_output_image( bool partial, bool defer ); gamescope::Rc vulkan_acquire_screenshot_texture(uint32_t width, uint32_t height, bool exportable, uint32_t drmFormat, EStreamColorspace colorspace = k_EStreamColorspace_Unknown); @@ -520,6 +520,9 @@ struct VulkanOutput_t // NIS gamescope::OwningRc nisScalerImage; gamescope::OwningRc nisUsmImage; + + // Rotated + gamescope::OwningRc rotatedOutput; }; @@ -532,6 +535,7 @@ enum ShaderType { SHADER_TYPE_RCAS, SHADER_TYPE_NIS, SHADER_TYPE_RGB_TO_NV12, + SHADER_TYPE_ROTATION, SHADER_TYPE_COUNT }; diff --git a/src/shaders/cs_rotation.comp b/src/shaders/cs_rotation.comp new file mode 100644 index 0000000000..1a47fd5057 --- /dev/null +++ b/src/shaders/cs_rotation.comp @@ -0,0 +1,53 @@ +#version 450 + +#extension GL_GOOGLE_include_directive : require +#extension GL_EXT_scalar_block_layout : require + +#include "descriptor_set.h" + +layout( + local_size_x = 8, + local_size_y = 8, + local_size_z = 1) in; + +#include "blit_push_data.h" +#include "composite.h" + +vec4 sampleLayer(uint layerIdx, vec2 uv) { + if ((c_ycbcrMask & (1 << layerIdx)) != 0) + return sampleLayer(s_ycbcr_samplers[layerIdx], layerIdx, uv, false); + return sampleLayer(s_samplers[layerIdx], layerIdx, uv, true); +} + +void main() { + uvec2 coord = uvec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y); + uvec2 outSize = imageSize(dst); + float outWidth = outSize.y; + float outHeight = outSize.x; + + vec2 uv = vec2(coord); + vec4 outputValue = vec4(255.0f); + + if (c_layerCount > 0) { + outputValue = sampleLayer(0, uv) * u_opacity[0]; + } + + for (int i = 1; i < c_layerCount; i++) { + vec4 layerColor = sampleLayer(i, uv); + // wl_surfaces come with premultiplied alpha, so that's them being + // premultiplied by layerColor.a. + // We need to then multiply that by the layer's opacity to get to our + // final premultiplied state. + // For the other side of things, we need to multiply by (1.0f - (layerColor.a * opacity)) + float opacity = u_opacity[i]; + float layerAlpha = opacity * layerColor.a; + outputValue = layerColor * opacity + outputValue * (1.0f - layerAlpha); + } + + outputValue.rgb = encodeOutputColor(outputValue.rgb); + + // Rotate the pixel coordinates counter-clockwise by 90 degrees + ivec2 rotatedCoord = ivec2(coord.y, outWidth - coord.x - 1); + + imageStore(dst, rotatedCoord, outputValue); +} diff --git a/src/wlserver.cpp b/src/wlserver.cpp index 78a86ee0e2..a5a230d71f 100644 --- a/src/wlserver.cpp +++ b/src/wlserver.cpp @@ -2498,6 +2498,11 @@ static void apply_touchscreen_orientation(double *x, double *y ) break; } + if (g_bUseRotationShader) { + tx = 1.0 - *y; + ty = *x; + } + *x = tx; *y = ty; }