Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: add rotation shader for rotating output #1655

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions src/Backends/DRMBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -3278,6 +3298,11 @@ namespace gamescope

bNeedsFullComposite |= !!(g_uCompositeDebug & CompositeDebugFlag::Heatmap);

if (g_bUseRotationShader)
{
bNeedsFullComposite = true;
}

bool bDoComposite = true;
if ( !bNeedsFullComposite && !bWantsPartialComposite )
{
Expand Down Expand Up @@ -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;

Expand Down
6 changes: 6 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 },

Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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 ||
Expand Down
1 change: 1 addition & 0 deletions src/main.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
1 change: 1 addition & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
126 changes: 110 additions & 16 deletions src/rendervulkan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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++)
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -3214,24 +3217,32 @@ 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" );
return false;
}

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" );
return false;
}

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" );
Expand All @@ -3246,23 +3257,23 @@ 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" );
return false;
}

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" );
return false;
}

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" );
Expand Down Expand Up @@ -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()
{
Expand Down Expand Up @@ -3856,7 +3889,7 @@ std::optional<uint64_t> vulkan_screenshot( const struct FrameInfo_t *frameInfo,
extern std::string g_reshade_effect;
extern uint32_t g_reshade_technique_idx;

std::optional<uint64_t> vulkan_composite( struct FrameInfo_t *frameInfo, gamescope::Rc<CVulkanTexture> pPipewireTexture, bool partial, gamescope::Rc<CVulkanTexture> pOutputOverride, bool increment, std::unique_ptr<CVulkanCmdBuffer> pInCommandBuffer )
std::optional<uint64_t> vulkan_composite( struct FrameInfo_t *frameInfo, gamescope::Rc<CVulkanTexture> pPipewireTexture, bool partial, gamescope::Rc<CVulkanTexture> pOutputOverride, bool increment, std::unique_ptr<CVulkanCmdBuffer> pInCommandBuffer, bool applyRotation )
{
EOTF outputTF = frameInfo->outputEncodingEOTF;
if (!frameInfo->applyOutputColorMgmt)
Expand Down Expand Up @@ -3928,7 +3961,15 @@ std::optional<uint64_t> 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<RcasPushData_t>(frameInfo, g_upscaleFilterSharpness / 10.0f);

cmdBuffer->dispatch(div_roundup(currentOutputWidth, pixelsPerGroup), div_roundup(currentOutputHeight, pixelsPerGroup));
Expand Down Expand Up @@ -3971,7 +4012,15 @@ std::optional<uint64_t> 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<BlitPushData_t>(&nisFrameInfo);

int pixelsPerGroup = 8;
Expand Down Expand Up @@ -4009,7 +4058,15 @@ std::optional<uint64_t> 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);
Expand All @@ -4019,14 +4076,51 @@ std::optional<uint64_t> 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<BlitPushData_t>(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<BlitPushData_t>(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<BlitPushData_t>(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<BlitPushData_t>(frameInfo);

const int pixelsPerGroup = 8;

cmdBuffer->dispatch(div_roundup(currentOutputWidth, pixelsPerGroup), div_roundup(currentOutputHeight, pixelsPerGroup));
}
}

if ( pPipewireTexture != nullptr )
Expand Down
6 changes: 5 additions & 1 deletion src/rendervulkan.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ gamescope::OwningRc<CVulkanTexture> vulkan_create_texture_from_dmabuf( struct wl
gamescope::OwningRc<CVulkanTexture> 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<CVulkanTexture> vulkan_create_texture_from_wlr_buffer( struct wlr_buffer *buf, gamescope::OwningRc<gamescope::IBackendFb> pBackendFb );

std::optional<uint64_t> vulkan_composite( struct FrameInfo_t *frameInfo, gamescope::Rc<CVulkanTexture> pScreenshotTexture, bool partial, gamescope::Rc<CVulkanTexture> pOutputOverride = nullptr, bool increment = true, std::unique_ptr<CVulkanCmdBuffer> pInCommandBuffer = nullptr );
std::optional<uint64_t> vulkan_composite( struct FrameInfo_t *frameInfo, gamescope::Rc<CVulkanTexture> pScreenshotTexture, bool partial, gamescope::Rc<CVulkanTexture> pOutputOverride = nullptr, bool increment = true, std::unique_ptr<CVulkanCmdBuffer> pInCommandBuffer = nullptr, bool applyRotation = false );
void vulkan_wait( uint64_t ulSeqNo, bool bReset );
gamescope::Rc<CVulkanTexture> vulkan_get_last_output_image( bool partial, bool defer );
gamescope::Rc<CVulkanTexture> vulkan_acquire_screenshot_texture(uint32_t width, uint32_t height, bool exportable, uint32_t drmFormat, EStreamColorspace colorspace = k_EStreamColorspace_Unknown);
Expand Down Expand Up @@ -520,6 +520,9 @@ struct VulkanOutput_t
// NIS
gamescope::OwningRc<CVulkanTexture> nisScalerImage;
gamescope::OwningRc<CVulkanTexture> nisUsmImage;

// Rotated
gamescope::OwningRc<CVulkanTexture> rotatedOutput;
};


Expand All @@ -532,6 +535,7 @@ enum ShaderType {
SHADER_TYPE_RCAS,
SHADER_TYPE_NIS,
SHADER_TYPE_RGB_TO_NV12,
SHADER_TYPE_ROTATION,

SHADER_TYPE_COUNT
};
Expand Down
Loading