mirror of
https://github.com/LizardByte/Sunshine.git
synced 2026-08-07 10:20:46 +00:00
feat(linux): Add Vulkan video encoder (#4603)
This commit is contained in:
@@ -332,6 +332,14 @@
|
||||
"vaapi_strict_rc_buffer": "disabled",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "vulkan",
|
||||
name: "Vulkan Encoder",
|
||||
options: {
|
||||
"vk_tune": 2,
|
||||
"vk_rc_mode": 4,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "sw",
|
||||
name: "Software Encoder",
|
||||
@@ -383,7 +391,7 @@
|
||||
var app = document.getElementById("app");
|
||||
if (this.platform === "windows") {
|
||||
this.tabs = this.tabs.filter((el) => {
|
||||
return el.id !== "vt" && el.id !== "vaapi";
|
||||
return el.id !== "vt" && el.id !== "vaapi" && el.id !== "vulkan";
|
||||
});
|
||||
}
|
||||
if (this.platform === "freebsd" || this.platform === "linux") {
|
||||
@@ -393,7 +401,7 @@
|
||||
}
|
||||
if (this.platform === "macos") {
|
||||
this.tabs = this.tabs.filter((el) => {
|
||||
return el.id !== "amd" && el.id !== "nv" && el.id !== "qsv" && el.id !== "vaapi";
|
||||
return el.id !== "amd" && el.id !== "nv" && el.id !== "qsv" && el.id !== "vaapi" && el.id !== "vulkan";
|
||||
});
|
||||
}
|
||||
|
||||
@@ -437,6 +445,7 @@
|
||||
'qsv': 'Gpu',
|
||||
'vaapi': 'Gpu',
|
||||
'vt': 'Gpu',
|
||||
'vulkan': 'Gpu',
|
||||
'sw': 'Cpu',
|
||||
};
|
||||
return iconMap[tabId] || 'Settings';
|
||||
|
||||
@@ -97,11 +97,13 @@ const config = ref(props.config)
|
||||
<option value="amdvce">AMD AMF/VCE</option>
|
||||
</template>
|
||||
<template #freebsd>
|
||||
<option value="vulkan">Vulkan</option>
|
||||
<option value="vaapi">VA-API</option>
|
||||
</template>
|
||||
<template #linux>
|
||||
<option value="nvenc">NVIDIA NVENC</option>
|
||||
<option value="vaapi">VA-API</option>
|
||||
<option value="vulkan">Vulkan</option>
|
||||
</template>
|
||||
<template #macos>
|
||||
<option value="videotoolbox">VideoToolbox</option>
|
||||
|
||||
@@ -6,6 +6,7 @@ import AmdAmfEncoder from './encoders/AmdAmfEncoder.vue'
|
||||
import VideotoolboxEncoder from './encoders/VideotoolboxEncoder.vue'
|
||||
import SoftwareEncoder from './encoders/SoftwareEncoder.vue'
|
||||
import VAAPIEncoder from './encoders/VAAPIEncoder.vue'
|
||||
import VulkanEncoder from './encoders/VulkanEncoder.vue'
|
||||
|
||||
const props = defineProps([
|
||||
'platform',
|
||||
@@ -53,6 +54,13 @@ const config = ref(props.config)
|
||||
:config="config"
|
||||
/>
|
||||
|
||||
<!-- Vulkan Encoder Tab -->
|
||||
<VulkanEncoder
|
||||
v-if="currentTab === 'vulkan'"
|
||||
:platform="platform"
|
||||
:config="config"
|
||||
/>
|
||||
|
||||
<!-- Software Encoder Tab -->
|
||||
<SoftwareEncoder
|
||||
v-if="currentTab === 'sw'"
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const props = defineProps([
|
||||
'platform',
|
||||
'config',
|
||||
])
|
||||
|
||||
const config = ref(props.config)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="vulkan-encoder" class="config-page">
|
||||
<!-- Tuning -->
|
||||
<div class="mb-3">
|
||||
<label for="vk_tune" class="form-label">{{ $t('config.vk_tune') }}</label>
|
||||
<select id="vk_tune" class="form-select" v-model="config.vk_tune">
|
||||
<option value="0">{{ $t('_common.auto') }}</option>
|
||||
<option value="1">{{ $t('config.vk_tune_hq') }}</option>
|
||||
<option value="2">{{ $t('config.vk_tune_ll') }}</option>
|
||||
<option value="3">{{ $t('config.vk_tune_ull') }}</option>
|
||||
</select>
|
||||
<div class="form-text">{{ $t('config.vk_tune_desc') }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Rate Control -->
|
||||
<div class="mb-3">
|
||||
<label for="vk_rc_mode" class="form-label">{{ $t('config.vk_rc_mode') }}</label>
|
||||
<select id="vk_rc_mode" class="form-select" v-model="config.vk_rc_mode">
|
||||
<option value="0">{{ $t('_common.auto') }}</option>
|
||||
<option value="1">{{ $t('config.vk_rc_cqp') }}</option>
|
||||
<option value="2">{{ $t('config.vk_rc_cbr') }}</option>
|
||||
<option value="4">{{ $t('config.vk_rc_vbr') }}</option>
|
||||
</select>
|
||||
<div class="form-text">{{ $t('config.vk_rc_mode_desc') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -378,6 +378,16 @@
|
||||
"upnp_desc": "Automatically configure port forwarding for streaming over the Internet",
|
||||
"vaapi_strict_rc_buffer": "Strictly enforce frame bitrate limits for H.264/HEVC on AMD GPUs",
|
||||
"vaapi_strict_rc_buffer_desc": "Enabling this option can avoid dropped frames over the network during scene changes, but video quality may be reduced during motion.",
|
||||
"vk_rc_cbr": "CBR (Constant Bitrate)",
|
||||
"vk_rc_cqp": "CQP (Constant QP)",
|
||||
"vk_rc_mode": "Rate Control",
|
||||
"vk_rc_mode_desc": "Rate control mode for encoding. Auto lets the driver decide.",
|
||||
"vk_rc_vbr": "VBR (Variable Bitrate) (default)",
|
||||
"vk_tune": "Tuning",
|
||||
"vk_tune_desc": "Low latency modes reduce encoding delay at the cost of quality.",
|
||||
"vk_tune_hq": "High Quality (hq)",
|
||||
"vk_tune_ll": "Low Latency (ll) (default)",
|
||||
"vk_tune_ull": "Ultra Low Latency (ull)",
|
||||
"virtual_sink": "Virtual Sink",
|
||||
"virtual_sink_desc": "Manually specify a virtual audio device to use. If unset, the device is chosen automatically. We strongly recommend leaving this field blank to use automatic device selection!",
|
||||
"virtual_sink_placeholder": "Steam Streaming Speakers",
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
#version 450
|
||||
|
||||
layout(local_size_x = 16, local_size_y = 16) in;
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D rgb_in;
|
||||
layout(set = 0, binding = 1) uniform writeonly image2D y_out;
|
||||
layout(set = 0, binding = 2) uniform writeonly image2D uv_out;
|
||||
layout(set = 0, binding = 3) uniform sampler2D cursor_in;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
vec4 color_vec_y;
|
||||
vec4 color_vec_u;
|
||||
vec4 color_vec_v;
|
||||
vec2 range_y;
|
||||
vec2 range_uv;
|
||||
ivec2 src_offset;
|
||||
ivec2 src_size;
|
||||
ivec2 dst_size;
|
||||
ivec2 cursor_pos;
|
||||
ivec2 cursor_size; // w=0 means no cursor
|
||||
int y_invert;
|
||||
} pc;
|
||||
|
||||
vec3 blend_cursor(vec3 rgb, ivec2 pos) {
|
||||
ivec2 cp = pos - pc.cursor_pos;
|
||||
if (cp.x >= 0 && cp.y >= 0 && cp.x < pc.cursor_size.x && cp.y < pc.cursor_size.y) {
|
||||
vec4 c = texture(cursor_in, (vec2(cp) + 0.5) / vec2(pc.cursor_size));
|
||||
rgb = mix(rgb, c.bgr, c.a);
|
||||
}
|
||||
return rgb;
|
||||
}
|
||||
|
||||
void main() {
|
||||
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
|
||||
if (pos.x >= pc.dst_size.x || pos.y >= pc.dst_size.y)
|
||||
return;
|
||||
|
||||
vec2 inv_tex = 1.0 / vec2(textureSize(rgb_in, 0));
|
||||
vec2 scale = vec2(pc.src_size) / vec2(pc.dst_size);
|
||||
|
||||
vec2 uv = (vec2(pc.src_offset) + (vec2(pos) + 0.5) * scale) * inv_tex;
|
||||
if (pc.y_invert != 0)
|
||||
uv.y = 1.0 - uv.y;
|
||||
vec3 rgb = texture(rgb_in, uv).rgb;
|
||||
|
||||
if (pc.cursor_size.x > 0)
|
||||
rgb = blend_cursor(rgb, pos);
|
||||
|
||||
// Y plane
|
||||
float y = dot(pc.color_vec_y.xyz, rgb) + pc.color_vec_y.w;
|
||||
imageStore(y_out, pos, vec4(y * pc.range_y.x + pc.range_y.y, 0, 0, 0));
|
||||
|
||||
// UV plane (half resolution, one thread per 2x2 block)
|
||||
if ((pos.x & 1) == 0 && (pos.y & 1) == 0) {
|
||||
vec2 step = scale * inv_tex;
|
||||
if (pc.y_invert != 0)
|
||||
step.y = -step.y;
|
||||
|
||||
vec3 rgb_r = texture(rgb_in, uv + vec2(step.x, 0)).rgb;
|
||||
vec3 rgb_b = texture(rgb_in, uv + vec2(0, step.y)).rgb;
|
||||
vec3 rgb_br = texture(rgb_in, uv + vec2(step.x, step.y)).rgb;
|
||||
if (pc.cursor_size.x > 0) {
|
||||
rgb_r = blend_cursor(rgb_r, pos + ivec2(1, 0));
|
||||
rgb_b = blend_cursor(rgb_b, pos + ivec2(0, 1));
|
||||
rgb_br = blend_cursor(rgb_br, pos + ivec2(1, 1));
|
||||
}
|
||||
|
||||
vec3 avg = (rgb + rgb_r + rgb_b + rgb_br) * 0.25;
|
||||
|
||||
float cb = dot(pc.color_vec_u.xyz, avg) + pc.color_vec_u.w;
|
||||
float cr = dot(pc.color_vec_v.xyz, avg) + pc.color_vec_v.w;
|
||||
|
||||
imageStore(uv_out, pos >> 1, vec4(cb * pc.range_uv.x + pc.range_uv.y,
|
||||
cr * pc.range_uv.x + pc.range_uv.y, 0, 0));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user