Skip to content

Commit

Permalink
feature: add rotation shader for rotating output
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuatam committed Dec 24, 2024
1 parent d317492 commit 90ab765
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 19 deletions.
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

0 comments on commit 90ab765

Please sign in to comment.