mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-12 11:31:08 +00:00
Support older Qualcomm drivers without DMA-BUF or maintenance5
(cherry picked from commit 10939e6bc31e50dcf18ce0fcad4b48d5874b4af6)
(cherry picked from commit 7873adcb23)
This commit is contained in:
1 parent
3dfe06b05a
commit
cf0ba191da
23 files changed
+1110
-12
No files matched your search
+8
-1
@@ -72,7 +72,7 @@ if (!(treeState in ['clean', 'dirty'])) {
|
||||
def status = gitValue(['status', '--porcelain'])
|
||||
treeState = status == null ? 'unknown' : (status.isEmpty() ? 'clean' : 'dirty')
|
||||
}
|
||||
buildLabel = "${buildOrigin} ${treeState} · ${buildLabel}"
|
||||
buildLabel = "${buildOrigin} ${treeState} · ${buildLabel}"
|
||||
def escapedBuildLabel = buildLabel.replace('\\', '\\\\').replace('"', '\\"')
|
||||
|
||||
def sourceCommit = providers.environmentVariable('DROIDDECK_SOURCE_COMMIT').getOrNull()
|
||||
@@ -289,6 +289,13 @@ android.sourceSets.main.assets.setSrcDirs([runtimeAssetsDir])
|
||||
def stageQualcommRuntime = tasks.register('stageQualcommRuntime', Exec) {
|
||||
workingDir rootProject.projectDir
|
||||
inputs.files(qualcommSources)
|
||||
if (qualcommRuntime.gamescope.build) {
|
||||
inputs.file(rootProject.file(qualcommRuntime.gamescope.build.binary))
|
||||
inputs.file(rootProject.file(qualcommRuntime.gamescope.build.source))
|
||||
}
|
||||
if (qualcommRuntime.mesa) {
|
||||
inputs.dir(rootProject.file(qualcommRuntime.mesa.directory))
|
||||
}
|
||||
inputs.property('ndkVersion', android.ndkVersion)
|
||||
outputs.dir(layout.buildDirectory.dir('qualcommAssets'))
|
||||
commandLine pythonExecutable, 'tools/qualcomm/fetch_runtime.py',
|
||||
|
||||
@@ -13,7 +13,10 @@ object QualcommSystemDriver {
|
||||
const val ID = "system-qualcomm"
|
||||
const val HAL = "/vendor/lib64/hw/vulkan.adreno.so"
|
||||
private val required = listOf("lib/libhybris-vulkan-icd.so.0", "lib/libhybris-common.so.1", "lib/libhybris/linker/q.so",
|
||||
"bin/gamescope", "shaders/qualcomm-shaders.so", "wsi/libVkLayer_window_system_integration.so")
|
||||
"bin/gamescope", "shaders/qualcomm-shaders.so", "wsi/libVkLayer_window_system_integration.so",
|
||||
"mesa/libgallium-26.2.3.so", "mesa/dri/zink_dri.so", "mesa/libGLX_droiddeck_qualcomm.so.0",
|
||||
"mesa/libEGL_droiddeck_qualcomm.so.0", "mesa/libgbm.so.1", "mesa/gbm/dri_gbm.so",
|
||||
"mesa/egl-vendor.json", "mesa/qualcomm-gl.sh")
|
||||
|
||||
@JvmStatic fun detected(): Boolean {
|
||||
if (Build.VERSION.SDK_INT < 29 || !File(HAL).canRead()) return false
|
||||
@@ -36,7 +39,7 @@ object QualcommSystemDriver {
|
||||
val vk = VulkanInfo.query()
|
||||
val version = vk["driver_version_raw"]?.toULongOrNull()?.let { "${(it shr 22) and 0x7fu}.${(it shr 12) and 0x3ffu}.${it and 0xfffu}" }
|
||||
?: vk["driver_version"] ?: "unknown version"
|
||||
return "System Qualcomm · ${vk["device"] ?: "Adreno"} · $version (experimental)"
|
||||
return "System Qualcomm · ${vk["device"] ?: "Adreno"} · $version (experimental)"
|
||||
}
|
||||
|
||||
@JvmStatic @Synchronized fun prepare(context: Context): File {
|
||||
@@ -55,6 +58,7 @@ object QualcommSystemDriver {
|
||||
copyAssets(context, "qualcomm/wsi", File(staging, "wsi"))
|
||||
copyAssets(context, "qualcomm/shaders", File(staging, "shaders"))
|
||||
copyAssets(context, "qualcomm/licenses", File(staging, "licenses"))
|
||||
copyAssets(context, "qualcomm/mesa", File(staging, "mesa"))
|
||||
check(required.all { File(staging, it).isFile }) { "The Qualcomm Vulkan bridge is incomplete" }
|
||||
File(staging, ".complete").writeText(sha)
|
||||
FileUtils.delete(dir)
|
||||
|
||||
@@ -27,7 +27,12 @@ if [ "${DROIDDECK_SYSTEM_QUALCOMM:-0}" = 1 ]; then
|
||||
export VK_LAYER_PATH="$DROIDDECK_QUALCOMM_ROOT/shaders"
|
||||
export VK_INSTANCE_LAYERS=VK_LAYER_DROIDDECK_qualcomm_shaders
|
||||
child_env+=("VK_LAYER_PATH=$DROIDDECK_QUALCOMM_ROOT/wsi:$DROIDDECK_QUALCOMM_ROOT/lib"
|
||||
VK_INSTANCE_LAYERS=VK_LAYER_window_system_integration:VK_LAYER_HYBRIS_compat WSI_X11_FORCE_SHM=1 DISABLE_GAMESCOPE_WSI=1)
|
||||
VK_INSTANCE_LAYERS=VK_LAYER_window_system_integration:VK_LAYER_HYBRIS_compat WSI_X11_FORCE_SHM=1 DISABLE_GAMESCOPE_WSI=1
|
||||
"LD_LIBRARY_PATH=$DROIDDECK_QUALCOMM_ROOT/mesa${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
"LIBGL_DRIVERS_PATH=$DROIDDECK_QUALCOMM_ROOT/mesa/dri"
|
||||
"GBM_BACKENDS_PATH=$DROIDDECK_QUALCOMM_ROOT/mesa/gbm"
|
||||
__GLX_VENDOR_LIBRARY_NAME=droiddeck_qualcomm
|
||||
"__EGL_VENDOR_LIBRARY_FILENAMES=$DROIDDECK_QUALCOMM_ROOT/mesa/egl-vendor.json")
|
||||
fi
|
||||
"${BL_GAMESCOPE_BINARY:-gamescope}" "${args[@]}" -- /usr/bin/env "${child_env[@]}" /usr/local/bin/droiddeck-gpu-window "$@"
|
||||
rc=$?
|
||||
|
||||
@@ -4,6 +4,7 @@ if [ "${DROIDDECK_SYSTEM_QUALCOMM:-0}" = 1 ]; then
|
||||
export VK_LAYER_PATH="$DROIDDECK_QUALCOMM_ROOT/wsi:$DROIDDECK_QUALCOMM_ROOT/lib"
|
||||
export VK_INSTANCE_LAYERS=VK_LAYER_window_system_integration:VK_LAYER_HYBRIS_compat
|
||||
export WSI_X11_FORCE_SHM=1 DISABLE_GAMESCOPE_WSI=1
|
||||
. "$DROIDDECK_QUALCOMM_ROOT/mesa/qualcomm-gl.sh"
|
||||
fi
|
||||
starts=/tmp/droiddeck-mangoapp-starts
|
||||
now=$(date +%s)
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
"""Build the private Qualcomm Gamescope on ARM64 Linux and retain its source."""
|
||||
import argparse
|
||||
import gzip
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tarfile
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
HERE = Path(__file__).resolve().parent
|
||||
REVISION = '8f212644c46460549035971ab914421180f61a7c'
|
||||
OPTIONS = ['-Dbuildtype=release', '-Denable_openvr_support=false',
|
||||
'-Denable_gamescope_wsi_layer=false', '-Denable_tests=false',
|
||||
'-Ddrm_backend=enabled', '-Dsdl2_backend=enabled', '-Dpipewire=disabled',
|
||||
'-Davif_screenshots=disabled', '-Dinput_emulation=disabled', '-Dbenchmark=disabled']
|
||||
|
||||
def run(args, cwd=None):
|
||||
subprocess.run([str(arg) for arg in args], cwd=cwd, check=True)
|
||||
|
||||
def patches():
|
||||
base = ROOT / 'tools/gamescope/patches'
|
||||
extra = HERE / 'gamescope-patches'
|
||||
return sorted([*base.glob('*.patch'), *extra.glob('*.patch')], key=lambda p: p.name) + [
|
||||
HERE / 'constant-yuv-samplers.patch', HERE / 'gamescope-no-dmabuf.patch']
|
||||
|
||||
def prepare(source):
|
||||
if source.exists():
|
||||
raise ValueError('Source directory already exists; use a new directory or --prepared-source')
|
||||
source.mkdir(parents=True)
|
||||
run(['git', 'init', source])
|
||||
run(['git', 'remote', 'add', 'origin', 'https://github.com/ValveSoftware/gamescope.git'], source)
|
||||
run(['git', 'fetch', '--depth', '1', 'origin', REVISION], source)
|
||||
run(['git', 'checkout', '--detach', 'FETCH_HEAD'], source)
|
||||
run(['git', 'submodule', 'update', '--init', '--depth', '1', 'src/reshade',
|
||||
'subprojects/libliftoff', 'subprojects/vkroots', 'subprojects/wlroots',
|
||||
'thirdparty/SPIRV-Headers'], source)
|
||||
for patch in patches():
|
||||
run(['git', 'apply', '--ignore-whitespace', patch], source)
|
||||
|
||||
def archive_source(source, destination):
|
||||
with destination.open('wb') as raw, gzip.GzipFile(filename='', mode='wb', fileobj=raw, mtime=0) as compressed:
|
||||
with tarfile.open(fileobj=compressed, mode='w', format=tarfile.PAX_FORMAT) as package:
|
||||
for path in sorted(source.rglob('*')):
|
||||
relative = path.relative_to(source)
|
||||
if any(part in {'.git', 'build', '__pycache__'} for part in relative.parts) or not path.is_file():
|
||||
continue
|
||||
info = package.gettarinfo(str(path), arcname='gamescope/' + relative.as_posix())
|
||||
info.uid = info.gid = info.mtime = 0
|
||||
info.uname = info.gname = ''
|
||||
info.mode = 0o755 if path.suffix == '.sh' else 0o644
|
||||
info.pax_headers = {}
|
||||
with path.open('rb') as data:
|
||||
package.addfile(info, data)
|
||||
|
||||
def package(source, output, strip_tool='strip'):
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copyfile(source / 'build/src/gamescope', output / 'gamescope')
|
||||
run([strip_tool, '--strip-unneeded', output / 'gamescope'])
|
||||
archive_source(source, output / 'source.tar.gz')
|
||||
manifest = {'source_commit': REVISION, 'options': OPTIONS,
|
||||
'patches': {str(p.relative_to(ROOT)).replace('\\', '/'): hashlib.sha256(p.read_bytes()).hexdigest() for p in patches()},
|
||||
'files': {name: {'sha256': hashlib.sha256((output / name).read_bytes()).hexdigest(),
|
||||
'size': (output / name).stat().st_size} for name in ['gamescope', 'source.tar.gz']}}
|
||||
(output / 'build.json').write_text(json.dumps(manifest, indent=2) + '\n')
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--source', type=Path, default=ROOT / 'build/qualcomm-gamescope/source')
|
||||
parser.add_argument('--output', type=Path, default=ROOT / 'build/qualcomm-gamescope')
|
||||
parser.add_argument('--prepared-source', action='store_true')
|
||||
parser.add_argument('--prepare-only', action='store_true')
|
||||
parser.add_argument('--package-only', action='store_true')
|
||||
parser.add_argument('--strip', default='strip')
|
||||
parser.add_argument('--jobs', type=int, default=4)
|
||||
args = parser.parse_args()
|
||||
if not args.prepared_source and not args.package_only:
|
||||
prepare(args.source)
|
||||
if not args.prepare_only:
|
||||
if not args.package_only:
|
||||
run(['meson', 'setup', 'build', *OPTIONS], args.source)
|
||||
run(['ninja', '-C', 'build', '-j' + str(args.jobs), 'src/gamescope'], args.source)
|
||||
package(args.source, args.output, args.strip)
|
||||
@@ -0,0 +1,76 @@
|
||||
"""Build private ARM64 Zink libraries with the pre-maintenance5 fallback."""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
HERE = Path(__file__).resolve().parent
|
||||
REVISION = '31e9a6b2e95e30d84bf3177d1f497d063e59b6b2'
|
||||
OPTIONS = ['-Dbuildtype=release', '-Dgallium-drivers=zink', '-Dvulkan-drivers=[]',
|
||||
'-Dllvm=disabled', '-Dplatforms=x11,wayland', '-Dglvnd=enabled',
|
||||
'-Dglvnd-vendor-name=droiddeck_qualcomm', '-Dgallium-va=disabled',
|
||||
'-Dvideo-codecs=[]', '-Dbuild-tests=false', '-Dtools=[]',
|
||||
'-Dglx=dri', '-Degl=enabled', '-Dgbm=enabled']
|
||||
LIBRARIES = {
|
||||
'libgallium-26.2.3.so': 'src/gallium/targets/dri/libgallium-26.2.3.so',
|
||||
'dri/zink_dri.so': 'src/gallium/targets/dril/libdril_dri.so',
|
||||
'libGLX_droiddeck_qualcomm.so.0': 'src/glx/libGLX_droiddeck_qualcomm.so.0.0.0',
|
||||
'libEGL_droiddeck_qualcomm.so.0': 'src/egl/libEGL_droiddeck_qualcomm.so.0.0.0',
|
||||
'libgbm.so.1': 'src/gbm/libgbm.so.1.0.0',
|
||||
'gbm/dri_gbm.so': 'src/gbm/backends/dri/dri_gbm.so',
|
||||
}
|
||||
|
||||
def run(args, cwd=None):
|
||||
subprocess.run([str(arg) for arg in args], cwd=cwd, check=True)
|
||||
|
||||
def prepare(source):
|
||||
if source.exists():
|
||||
raise ValueError('Source directory already exists; use a new directory or --prepared-source')
|
||||
source.mkdir(parents=True)
|
||||
run(['git', 'init', source])
|
||||
run(['git', 'remote', 'add', 'origin', 'https://gitlab.freedesktop.org/mesa/mesa.git'], source)
|
||||
run(['git', 'fetch', '--depth', '1', 'origin', REVISION], source)
|
||||
run(['git', 'checkout', '--detach', 'FETCH_HEAD'], source)
|
||||
run(['git', 'apply', HERE / 'zink-optional-maintenance5.patch'], source)
|
||||
|
||||
def package(source, output, strip_tool):
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
for name, relative in LIBRARIES.items():
|
||||
destination = output / name
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copyfile(source / 'build' / relative, destination)
|
||||
run([strip_tool, '--strip-unneeded', destination])
|
||||
(output / 'egl-vendor.json').write_text(json.dumps({
|
||||
'file_format_version': '1.0.0', 'ICD': {'library_path': 'libEGL_droiddeck_qualcomm.so.0'}}) + '\n')
|
||||
shutil.copyfile(HERE / 'qualcomm-gl.sh', output / 'qualcomm-gl.sh')
|
||||
shutil.copytree(source / 'licenses', output / 'licenses', dirs_exist_ok=True)
|
||||
shutil.copyfile(source / 'docs/license.rst', output / 'licenses/Mesa-license.rst')
|
||||
shutil.copyfile(HERE / 'zink-optional-maintenance5.patch', output / 'licenses/zink-optional-maintenance5.patch')
|
||||
(output / 'licenses/source.json').write_text(json.dumps({
|
||||
'url': 'https://gitlab.freedesktop.org/mesa/mesa/-/tree/' + REVISION,
|
||||
'source_commit': REVISION, 'options': OPTIONS,
|
||||
'patch_sha256': hashlib.sha256((HERE / 'zink-optional-maintenance5.patch').read_bytes()).hexdigest()}, indent=2) + '\n')
|
||||
files = [{'path': p.relative_to(output).as_posix(), 'sha256': hashlib.sha256(p.read_bytes()).hexdigest(),
|
||||
'size': p.stat().st_size} for p in sorted(output.rglob('*')) if p.is_file() and p.name != 'build.json']
|
||||
(output / 'build.json').write_text(json.dumps({'source_commit': REVISION, 'files': files}, indent=2) + '\n')
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--source', type=Path, default=ROOT / 'build/qualcomm-zink/source')
|
||||
parser.add_argument('--output', type=Path, default=ROOT / 'build/qualcomm-zink/runtime')
|
||||
parser.add_argument('--prepared-source', action='store_true')
|
||||
parser.add_argument('--prepare-only', action='store_true')
|
||||
parser.add_argument('--package-only', action='store_true')
|
||||
parser.add_argument('--strip', default='strip')
|
||||
parser.add_argument('--jobs', type=int, default=4)
|
||||
args = parser.parse_args()
|
||||
if not args.prepared_source and not args.package_only:
|
||||
prepare(args.source)
|
||||
if not args.prepare_only:
|
||||
if not args.package_only:
|
||||
run(['meson', 'setup', 'build', *OPTIONS], args.source)
|
||||
run(['ninja', '-C', 'build', '-j' + str(args.jobs)], args.source)
|
||||
package(args.source, args.output, args.strip)
|
||||
@@ -0,0 +1,56 @@
|
||||
#include <EGL/egl.h>
|
||||
#include <GL/gl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
EGLint major, minor, count;
|
||||
const EGLint attributes[] = { EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8,
|
||||
EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_NONE };
|
||||
const EGLint size[] = { EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE };
|
||||
EGLConfig config;
|
||||
if (!eglInitialize(display, &major, &minor) || !eglBindAPI(EGL_OPENGL_API) ||
|
||||
!eglChooseConfig(display, attributes, &config, 1, &count) || count != 1) {
|
||||
fprintf(stderr, "EGL setup failed: 0x%x\n", eglGetError());
|
||||
return 1;
|
||||
}
|
||||
EGLSurface surface = eglCreatePbufferSurface(display, config, size);
|
||||
EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, NULL);
|
||||
if (surface == EGL_NO_SURFACE || context == EGL_NO_CONTEXT ||
|
||||
!eglMakeCurrent(display, surface, surface, context)) {
|
||||
fprintf(stderr, "EGL context failed: 0x%x\n", eglGetError());
|
||||
return 1;
|
||||
}
|
||||
const char *renderer = (const char *)glGetString(GL_RENDERER);
|
||||
printf("Renderer: %s\nOpenGL: %s\n", renderer, glGetString(GL_VERSION));
|
||||
if (!renderer || !strstr(renderer, "zink") || !strstr(renderer, "Adreno") ||
|
||||
strstr(renderer, "llvmpipe")) return 1;
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glViewport(0, 0, 16, 16);
|
||||
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
glBegin(GL_TRIANGLES);
|
||||
glVertex2f(-1.0f, -1.0f);
|
||||
glVertex2f(3.0f, -1.0f);
|
||||
glVertex2f(-1.0f, 3.0f);
|
||||
glEnd();
|
||||
unsigned char pixels[16 * 16 * 4];
|
||||
memset(pixels, 0, sizeof(pixels));
|
||||
glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
if (glGetError() != GL_NO_ERROR) return 1;
|
||||
for (unsigned i = 0; i < sizeof(pixels); i += 4) {
|
||||
if (pixels[i] != 255 || pixels[i + 1] || pixels[i + 2] || pixels[i + 3] != 255) {
|
||||
fprintf(stderr, "Readback mismatch at pixel %u\n", i / 4);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
puts("GPU triangle/readback: all 256 pixels passed");
|
||||
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
eglDestroyContext(display, context);
|
||||
eglDestroySurface(display, surface);
|
||||
eglTerminate(display);
|
||||
return 0;
|
||||
}
|
||||
@@ -32,11 +32,48 @@ def stage(output, cache, lock, ndk=None):
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copyfile(archive, output / 'runtime.tzst')
|
||||
gamescope = lock['gamescope']
|
||||
with zipfile.ZipFile(fetch(cache, gamescope)) as package:
|
||||
binary = package.read(gamescope['member'])
|
||||
built = gamescope.get('build')
|
||||
if built:
|
||||
root = Path(__file__).resolve().parents[2]
|
||||
def build_input(name):
|
||||
target = (root / built[name]).resolve()
|
||||
if not target.is_relative_to(root / 'build'):
|
||||
raise ValueError('Invalid Qualcomm Gamescope build path')
|
||||
if not target.is_file():
|
||||
raise FileNotFoundError('Build private Gamescope first with tools/qualcomm/build_gamescope.py: ' + str(target))
|
||||
return target.read_bytes()
|
||||
binary = build_input('binary')
|
||||
source = build_input('source')
|
||||
if len(source) != built['source_size'] or hashlib.sha256(source).hexdigest() != built['source_sha256']:
|
||||
raise ValueError('Qualcomm Gamescope source failed SHA-256 verification')
|
||||
licenses = output / 'licenses'
|
||||
licenses.mkdir(exist_ok=True)
|
||||
(licenses / 'gamescope-patched-source.tar.gz').unlink(missing_ok=True)
|
||||
(licenses / 'gamescope-patched-source.tar.gzip').write_bytes(source)
|
||||
else:
|
||||
with zipfile.ZipFile(fetch(cache, gamescope)) as package:
|
||||
binary = package.read(gamescope['member'])
|
||||
if hashlib.sha256(binary).hexdigest() != gamescope['member_sha256']:
|
||||
raise ValueError('Qualcomm Gamescope binary failed SHA-256 verification')
|
||||
(output / 'gamescope').write_bytes(binary)
|
||||
if 'mesa' in lock:
|
||||
root = Path(__file__).resolve().parents[2]
|
||||
directory = (root / lock['mesa']['directory']).resolve()
|
||||
if not directory.is_relative_to(root / 'build'):
|
||||
raise ValueError('Invalid Qualcomm Mesa build path')
|
||||
for entry in lock['mesa']['files']:
|
||||
relative = Path(entry['path'])
|
||||
if relative.is_absolute() or '..' in relative.parts:
|
||||
raise ValueError('Invalid Qualcomm Mesa destination')
|
||||
origin = directory / relative
|
||||
if not origin.is_file():
|
||||
raise FileNotFoundError('Build private Zink first with tools/qualcomm/build_zink.py: ' + str(origin))
|
||||
data = origin.read_bytes()
|
||||
if len(data) != entry['size'] or hashlib.sha256(data).hexdigest() != entry['sha256']:
|
||||
raise ValueError('Qualcomm Mesa member failed SHA-256 verification: ' + entry['path'])
|
||||
target = output / 'mesa' / relative
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
target.write_bytes(data)
|
||||
if 'wsi' in lock:
|
||||
with zipfile.ZipFile(fetch(cache, lock['wsi'])) as package:
|
||||
for entry in lock['wsi']['files']:
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
--- a/src/rendervulkan.hpp
|
||||
+++ b/src/rendervulkan.hpp
|
||||
@@ -866,6 +866,7 @@
|
||||
inline VkPipelineLayout pipelineLayout() {return m_pipelineLayout;}
|
||||
inline int drmRenderFd() {return m_drmRendererFd;}
|
||||
inline bool supportsModifiers() {return m_bSupportsModifiers;}
|
||||
+ inline bool supportsDmaBuf() {return m_bSupportsDmaBuf;}
|
||||
inline uint32_t vendorID() const {return m_uVendorID;}
|
||||
inline const VkPhysicalDeviceMemoryProperties &memoryProperties() const {return m_memoryProperties;}
|
||||
inline bool supportsMutableSwapchain() {return m_bSupportsMutableSwapchain;}
|
||||
@@ -938,6 +939,7 @@
|
||||
uint32_t m_uVendorID = 0;
|
||||
bool m_bHasDrmPrimaryDevId = false;
|
||||
bool m_bSupportsModifiers = false;
|
||||
+ bool m_bSupportsDmaBuf = false;
|
||||
bool m_bSupportsMutableSwapchain = false;
|
||||
bool m_bSupportsPresentWait = false;
|
||||
bool m_bInitialized = false;
|
||||
--- a/src/rendervulkan.cpp
|
||||
+++ b/src/rendervulkan.cpp
|
||||
@@ -452,10 +452,16 @@
|
||||
|
||||
bool hasDrmProps = vulkan_has_drm_props();
|
||||
bool supportsForeignQueue = false;
|
||||
+ bool supportsMemoryFd = false;
|
||||
+ bool supportsDmaBuf = false;
|
||||
bool supportsHDRMetadata = false;
|
||||
bool presentIdExtension = false;
|
||||
bool presentWaitExtension = false;
|
||||
for (const auto& ext : m_supportedExts) {
|
||||
+ if ( strcmp(ext.extensionName, VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME) == 0 )
|
||||
+ supportsMemoryFd = true;
|
||||
+ if ( strcmp(ext.extensionName, VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME) == 0 )
|
||||
+ supportsDmaBuf = true;
|
||||
if ( strcmp(ext.extensionName, VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME) == 0 )
|
||||
m_bSupportsModifiers = true;
|
||||
|
||||
@@ -471,6 +477,18 @@
|
||||
|
||||
if ( strcmp(ext.extensionName, VK_EXT_HDR_METADATA_EXTENSION_NAME) == 0 )
|
||||
supportsHDRMetadata = true;
|
||||
+ }
|
||||
+
|
||||
+ m_bSupportsDmaBuf = supportsMemoryFd && supportsDmaBuf;
|
||||
+ if ( !m_bSupportsDmaBuf )
|
||||
+ {
|
||||
+ if ( !GetBackend()->UsesVulkanSwapchain() )
|
||||
+ {
|
||||
+ vk_log.errorf( "This backend requires DMA-BUF import/export support" );
|
||||
+ return false;
|
||||
+ }
|
||||
+ m_bSupportsModifiers = false;
|
||||
+ vk_log.infof( "DMA-BUF unavailable; using SHM inputs with Vulkan swapchain rendering" );
|
||||
}
|
||||
|
||||
vk_log.infof( "physical device %s DRM format modifiers", m_bSupportsModifiers ? "supports" : "does not support" );
|
||||
@@ -617,8 +635,11 @@
|
||||
enabledExtensions.push_back( VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME );
|
||||
}
|
||||
|
||||
- enabledExtensions.push_back( VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME );
|
||||
- enabledExtensions.push_back( VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME );
|
||||
+ if ( m_bSupportsDmaBuf )
|
||||
+ {
|
||||
+ enabledExtensions.push_back( VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME );
|
||||
+ enabledExtensions.push_back( VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME );
|
||||
+ }
|
||||
|
||||
enabledExtensions.push_back( VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME );
|
||||
|
||||
@@ -2144,6 +2165,11 @@
|
||||
{
|
||||
flags.bExportable = true;
|
||||
}
|
||||
+ if ( ( pDMA || flags.bExportable ) && !g_device.supportsDmaBuf() )
|
||||
+ {
|
||||
+ vk_log.errorf( "DMA-BUF texture requested without import/export support" );
|
||||
+ return false;
|
||||
+ }
|
||||
|
||||
if ( flags.bTransferSrc == true )
|
||||
{
|
||||
@@ -2983,6 +3009,8 @@
|
||||
}
|
||||
|
||||
wlr_drm_format_set_add( &sampledShmFormats, drmFormat, DRM_FORMAT_MOD_LINEAR );
|
||||
+ if ( !g_device.supportsDmaBuf() )
|
||||
+ return true;
|
||||
|
||||
if ( g_device.supportsModifiers() )
|
||||
{
|
||||
@@ -4836,7 +4864,7 @@
|
||||
{
|
||||
if (buffer_caps & WLR_BUFFER_CAP_DMABUF)
|
||||
{
|
||||
- return &sampledDRMFormats;
|
||||
+ return g_device.supportsDmaBuf() ? &sampledDRMFormats : nullptr;
|
||||
}
|
||||
else if (buffer_caps & WLR_BUFFER_CAP_DATA_PTR)
|
||||
{
|
||||
@@ -4878,7 +4906,10 @@
|
||||
struct wlr_renderer *vulkan_renderer_create( void )
|
||||
{
|
||||
VulkanRenderer_t *renderer = new VulkanRenderer_t();
|
||||
- wlr_renderer_init(&renderer->base, &renderer_impl, WLR_BUFFER_CAP_DMABUF | WLR_BUFFER_CAP_DATA_PTR);
|
||||
+ uint32_t caps = WLR_BUFFER_CAP_DATA_PTR;
|
||||
+ if ( g_device.supportsDmaBuf() )
|
||||
+ caps |= WLR_BUFFER_CAP_DMABUF;
|
||||
+ wlr_renderer_init(&renderer->base, &renderer_impl, caps);
|
||||
return &renderer->base;
|
||||
}
|
||||
|
||||
--- a/src/wlserver.cpp
|
||||
+++ b/src/wlserver.cpp
|
||||
@@ -2243,7 +2243,8 @@
|
||||
|
||||
// Have to make this old ancient thing for compat with older XWayland.
|
||||
// Someday, he will be purged.
|
||||
- wlr_drm_create(wlserver.display, wlserver.wlr.renderer);
|
||||
+ if ( wlr_renderer_get_texture_formats( wlserver.wlr.renderer, WLR_BUFFER_CAP_DMABUF ) != nullptr )
|
||||
+ wlr_drm_create(wlserver.display, wlserver.wlr.renderer);
|
||||
|
||||
if ( GetBackend()->SupportsExplicitSync() )
|
||||
{
|
||||
@@ -0,0 +1,15 @@
|
||||
--- a/src/wlserver.cpp
|
||||
+++ b/src/wlserver.cpp
|
||||
@@ -203,6 +203,12 @@
|
||||
}
|
||||
|
||||
void xwayland_surface_commit(struct wlr_surface *wlr_surface) {
|
||||
+ // Cursor surfaces never acquire an X11/xdg window association. Keeping their
|
||||
+ // commits in g_PendingCommits pins every old SHM buffer until the cursor
|
||||
+ // surface is destroyed. X11 cursor images are handled by MouseCursor.
|
||||
+ if ( wlr_surface->role && strcmp( wlr_surface->role->name, "wl_pointer-cursor" ) == 0 )
|
||||
+ return;
|
||||
+
|
||||
wlr_surface->current.committed = 0;
|
||||
|
||||
wlserver_x11_surface_info *wlserver_x11_surface_info = get_wl_surface_info(wlr_surface)->x11_surface;
|
||||
@@ -0,0 +1,15 @@
|
||||
diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp
|
||||
--- a/src/rendervulkan.cpp
|
||||
+++ b/src/rendervulkan.cpp
|
||||
@@ -996,8 +996,9 @@
|
||||
SHADER(BLUR, cs_composite_blur);
|
||||
SHADER(BLUR_COND, cs_composite_blur_cond);
|
||||
SHADER(BLUR_FIRST_PASS, cs_gaussian_blur_horizontal);
|
||||
- // The one pixel layout is only measured on Adreno, every other vendor keeps the quad swizzle.
|
||||
- if (m_uVendorID == 0x5143) /* Qualcomm */
|
||||
+ // PowerVR rejects the quad-swizzled RCAS for 3+ layers with YCbCr mask 2.
|
||||
+ // The existing one-pixel shader compiles those variants and covers the same 16x16 tile.
|
||||
+ if (m_uVendorID == 0x5143 || m_uVendorID == 0x1010) /* Qualcomm or Imagination */
|
||||
{
|
||||
SHADER(RCAS, cs_composite_rcas_1px);
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp
|
||||
--- a/src/rendervulkan.cpp
|
||||
+++ b/src/rendervulkan.cpp
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <dlfcn.h>
|
||||
#include "vulkan_include.h"
|
||||
#include "nested_swapchain.hpp"
|
||||
+#include "dmabuf_import.hpp"
|
||||
#include "Utils/Algorithm.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
@@ -2229,8 +2230,27 @@
|
||||
return false;
|
||||
}
|
||||
|
||||
- if ( res == VK_SUCCESS &&
|
||||
- ( externalImageProperties.externalMemoryProperties.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT ) )
|
||||
+ bool importable = res == VK_SUCCESS &&
|
||||
+ ( externalImageProperties.externalMemoryProperties.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT );
|
||||
+ if ( gamescope::RetryPowerVRLinearQuery( g_device.vendorID(), imageInfo.format,
|
||||
+ pDMA->modifier, pDMA->n_planes, imageInfo.flags, res ) )
|
||||
+ {
|
||||
+ // PowerVR's mutable-format query rejects this linear import even though
|
||||
+ // explicit-layout imports and UNORM/sRGB GPU sampling work. Query the base
|
||||
+ // format without changing the image we actually create or its supplied stride.
|
||||
+ VkImageCreateInfo baseFormat = imageInfo;
|
||||
+ baseFormat.flags &= ~VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
||||
+ baseFormat.pNext = nullptr;
|
||||
+ res = getModifierProps( &baseFormat, pDMA->modifier, &externalImageProperties );
|
||||
+ importable = res == VK_SUCCESS &&
|
||||
+ ( externalImageProperties.externalMemoryProperties.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT );
|
||||
+ }
|
||||
+ if ( !importable && g_device.vendorID() == 0x1010 )
|
||||
+ {
|
||||
+ vk_log.errorf( "PowerVR DMA-BUF modifier is not importable; refusing a different tiling" );
|
||||
+ return false;
|
||||
+ }
|
||||
+ if ( importable )
|
||||
{
|
||||
modifierInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT,
|
||||
@@ -2348,6 +2368,22 @@
|
||||
|
||||
m_format = imageInfo.format;
|
||||
|
||||
+ VkMemoryFdPropertiesKHR fdProperties = {
|
||||
+ .sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR,
|
||||
+ };
|
||||
+ if ( pDMA )
|
||||
+ {
|
||||
+ if ( !g_device.vk.GetMemoryFdPropertiesKHR )
|
||||
+ return false;
|
||||
+ res = g_device.vk.GetMemoryFdPropertiesKHR( g_device.device(),
|
||||
+ VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, pDMA->fd[0], &fdProperties );
|
||||
+ if ( res != VK_SUCCESS )
|
||||
+ {
|
||||
+ vk_errorf( res, "vkGetMemoryFdPropertiesKHR failed" );
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
res = g_device.vk.CreateImage(g_device.device(), &imageInfo, nullptr, &m_vkImage);
|
||||
if (res != VK_SUCCESS) {
|
||||
vk_errorf( res, "vkCreateImage failed" );
|
||||
@@ -2357,10 +2393,22 @@
|
||||
VkMemoryRequirements memRequirements;
|
||||
g_device.vk.GetImageMemoryRequirements(g_device.device(), m_vkImage, &memRequirements);
|
||||
|
||||
+ uint32_t memoryType = pDMA
|
||||
+ ? gamescope::DmabufMemoryType( g_device.memoryProperties(), memRequirements.memoryTypeBits,
|
||||
+ fdProperties.memoryTypeBits, properties )
|
||||
+ : uint32_t( g_device.findMemoryType( properties, memRequirements.memoryTypeBits ) );
|
||||
+ if ( memoryType == UINT32_MAX )
|
||||
+ {
|
||||
+ vk_log.errorf( "No compatible image memory type (image=0x%x, fd=0x%x)",
|
||||
+ memRequirements.memoryTypeBits, fdProperties.memoryTypeBits );
|
||||
+ g_device.vk.DestroyImage( g_device.device(), m_vkImage, nullptr );
|
||||
+ m_vkImage = VK_NULL_HANDLE;
|
||||
+ return false;
|
||||
+ }
|
||||
VkMemoryAllocateInfo allocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = memRequirements.size,
|
||||
- .memoryTypeIndex = uint32_t(g_device.findMemoryType(properties, memRequirements.memoryTypeBits)),
|
||||
+ .memoryTypeIndex = memoryType,
|
||||
};
|
||||
|
||||
m_size = allocInfo.allocationSize;
|
||||
@@ -2453,6 +2501,13 @@
|
||||
if ( res != VK_SUCCESS )
|
||||
{
|
||||
vk_errorf( res, "vkBindImageMemory failed" );
|
||||
+ if ( pDMA )
|
||||
+ vk_log.errorf( "DMA-BUF: %ux%u format=%u modifier=0x%" PRIx64
|
||||
+ " planes=%d stride=%u offset=%u tiling=%u allocation=%" PRIu64
|
||||
+ " memoryType=%u imageBits=0x%x fdBits=0x%x", width, height, imageInfo.format,
|
||||
+ pDMA->modifier, pDMA->n_planes, pDMA->stride[0], pDMA->offset[0], imageInfo.tiling,
|
||||
+ uint64_t( allocInfo.allocationSize ), allocInfo.memoryTypeIndex,
|
||||
+ memRequirements.memoryTypeBits, fdProperties.memoryTypeBits );
|
||||
return false;
|
||||
}
|
||||
|
||||
diff --git a/src/rendervulkan.hpp b/src/rendervulkan.hpp
|
||||
--- a/src/rendervulkan.hpp
|
||||
+++ b/src/rendervulkan.hpp
|
||||
@@ -791,6 +791,7 @@
|
||||
VK_FUNC(GetImageMemoryRequirements) \
|
||||
VK_FUNC(GetImageSubresourceLayout) \
|
||||
VK_FUNC(GetMemoryFdKHR) \
|
||||
+ VK_FUNC(GetMemoryFdPropertiesKHR) \
|
||||
VK_FUNC(GetQueryPoolResults) \
|
||||
VK_FUNC(GetSemaphoreCounterValue) \
|
||||
VK_FUNC(GetSwapchainImagesKHR) \
|
||||
@@ -865,6 +866,8 @@
|
||||
inline VkPipelineLayout pipelineLayout() {return m_pipelineLayout;}
|
||||
inline int drmRenderFd() {return m_drmRendererFd;}
|
||||
inline bool supportsModifiers() {return m_bSupportsModifiers;}
|
||||
+ inline uint32_t vendorID() const {return m_uVendorID;}
|
||||
+ inline const VkPhysicalDeviceMemoryProperties &memoryProperties() const {return m_memoryProperties;}
|
||||
inline bool supportsMutableSwapchain() {return m_bSupportsMutableSwapchain;}
|
||||
inline bool supportsPresentWait() {return m_bSupportsPresentWait;}
|
||||
inline bool hasDrmPrimaryDevId() {return m_bHasDrmPrimaryDevId;}
|
||||
diff --git a/src/dmabuf_import.hpp b/src/dmabuf_import.hpp
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/src/dmabuf_import.hpp
|
||||
@@ -0,0 +1,29 @@
|
||||
+#pragma once
|
||||
+#include <vulkan/vulkan_core.h>
|
||||
+
|
||||
+namespace gamescope
|
||||
+{
|
||||
+inline bool RetryPowerVRLinearQuery( uint32_t vendor, VkFormat format, uint64_t modifier,
|
||||
+ uint32_t planes, VkImageCreateFlags flags, VkResult result )
|
||||
+{
|
||||
+ return vendor == 0x1010 && modifier == 0 && planes == 1 &&
|
||||
+ flags == VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT && result == VK_ERROR_FORMAT_NOT_SUPPORTED &&
|
||||
+ ( format == VK_FORMAT_R8G8B8A8_UNORM || format == VK_FORMAT_B8G8R8A8_UNORM );
|
||||
+}
|
||||
+
|
||||
+inline uint32_t DmabufMemoryType( const VkPhysicalDeviceMemoryProperties &memory,
|
||||
+ uint32_t imageBits, uint32_t fdBits, VkMemoryPropertyFlags preferred )
|
||||
+{
|
||||
+ uint32_t fallback = UINT32_MAX;
|
||||
+ const uint32_t compatible = imageBits & fdBits;
|
||||
+ for ( uint32_t i = 0; i < memory.memoryTypeCount; ++i )
|
||||
+ {
|
||||
+ if ( !( compatible & ( 1u << i ) ) ) continue;
|
||||
+ if ( fallback == UINT32_MAX ) fallback = i;
|
||||
+ if ( ( memory.memoryTypes[i].propertyFlags & preferred ) == preferred ) return i;
|
||||
+ }
|
||||
+ // Device-local is a preference for an existing imported buffer. CPU mapping
|
||||
+ // properties are requirements and must never be silently relaxed.
|
||||
+ return preferred == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ? fallback : UINT32_MAX;
|
||||
+}
|
||||
+}
|
||||
@@ -0,0 +1,61 @@
|
||||
diff --git a/src/nested_surface_format.hpp b/src/nested_surface_format.hpp
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/src/nested_surface_format.hpp
|
||||
@@ -0,0 +1,31 @@
|
||||
+#pragma once
|
||||
+
|
||||
+#include <vulkan/vulkan_core.h>
|
||||
+
|
||||
+namespace gamescope
|
||||
+{
|
||||
+// The SDR composition shader declares rgba8, which requires an R8G8B8A8_UNORM
|
||||
+// storage view. Binding a BGRA view is a format mismatch; a same-format copy
|
||||
+// cannot correct it. Keep the workaround scoped to the PowerVR SDR copy path
|
||||
+// where the channel reversal was reproduced, not game-facing texture formats.
|
||||
+// On success, formatCount means the existing selector should choose the format.
|
||||
+// On failure, no matching RGBA SDR surface was advertised for this scoped path.
|
||||
+inline bool ChooseNestedSDRCopySurfaceFormat( uint32_t vendorId, bool copy, bool hdr,
|
||||
+ const VkSurfaceFormatKHR *formats, uint32_t formatCount, uint32_t &surfaceFormat )
|
||||
+{
|
||||
+ surfaceFormat = formatCount;
|
||||
+ if ( vendorId != 0x1010 || !copy || hdr )
|
||||
+ return true;
|
||||
+
|
||||
+ for ( uint32_t i = 0; i < formatCount; ++i )
|
||||
+ {
|
||||
+ if ( formats[i].format == VK_FORMAT_R8G8B8A8_UNORM &&
|
||||
+ formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR )
|
||||
+ {
|
||||
+ surfaceFormat = i;
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+ return false;
|
||||
+}
|
||||
+}
|
||||
diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp
|
||||
--- a/src/rendervulkan.cpp
|
||||
+++ b/src/rendervulkan.cpp
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "vulkan_include.h"
|
||||
#include "nested_swapchain.hpp"
|
||||
#include "dmabuf_import.hpp"
|
||||
+#include "nested_surface_format.hpp"
|
||||
#include "Utils/Algorithm.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
@@ -3330,6 +3331,13 @@
|
||||
uint32_t surfaceFormat = formatCount;
|
||||
VkColorSpaceKHR preferredColorSpace = g_bOutputHDREnabled ? VK_COLOR_SPACE_HDR10_ST2084_EXT : VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||
|
||||
+ if ( !gamescope::ChooseNestedSDRCopySurfaceFormat( g_device.vendorID(), config.copy,
|
||||
+ g_bOutputHDREnabled, pOutput->surfaceFormats.data(), formatCount, surfaceFormat ) )
|
||||
+ {
|
||||
+ vk_log.errorf( "PowerVR copy composition requires an RGBA SDR surface" );
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
if ( surfaceFormat == formatCount )
|
||||
{
|
||||
for ( surfaceFormat = 0; surfaceFormat < formatCount; surfaceFormat++ )
|
||||
@@ -0,0 +1,23 @@
|
||||
diff --git a/src/Backends/SDLBackend.cpp b/src/Backends/SDLBackend.cpp
|
||||
--- a/src/Backends/SDLBackend.cpp
|
||||
+++ b/src/Backends/SDLBackend.cpp
|
||||
@@ -351,14 +351,15 @@ namespace gamescope
|
||||
std::optional oCompositeResult = vulkan_composite( (FrameInfo_t *)pFrameInfo, nullptr, false );
|
||||
if ( !oCompositeResult )
|
||||
return -EINVAL;
|
||||
|
||||
+ // Present has no render-finished semaphore, so wait before the native consumer can read
|
||||
+ // the old contents. This serializes composition and present; a future pipelined path must
|
||||
+ // signal a binary render-finished semaphore and make vkQueuePresentKHR wait on it.
|
||||
+ vulkan_wait( *oCompositeResult, true );
|
||||
+
|
||||
vulkan_present_to_window();
|
||||
|
||||
// TODO: Hook up PresentationFeedback.
|
||||
-
|
||||
- // Wait for the composite result on our side *after* we
|
||||
- // commit the buffer to the compositor to avoid a bubble.
|
||||
- vulkan_wait( *oCompositeResult, true );
|
||||
|
||||
GetVBlankTimer().UpdateWasCompositing( true );
|
||||
GetVBlankTimer().UpdateLastDrawTime( get_time_in_nanos() - g_SteamCompMgrVBlankTime.ulWakeupTime );
|
||||
@@ -0,0 +1,176 @@
|
||||
diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp
|
||||
--- a/src/steamcompmgr.cpp
|
||||
+++ b/src/steamcompmgr.cpp
|
||||
@@ -95,6 +95,7 @@
|
||||
#include "Script/Script.h"
|
||||
#include "refresh_rate.h"
|
||||
#include "commit.h"
|
||||
+#include "buffer_commit_sync.hpp"
|
||||
#include "reshade_effect_manager.hpp"
|
||||
#include "BufferMemo.h"
|
||||
#include "vrclient_detect.h"
|
||||
@@ -8451,7 +8452,6 @@ void update_wayland_res(CommitDoneList_t *doneCommits, steamcompmgr_win_t *w, Re
|
||||
return;
|
||||
}
|
||||
|
||||
- int fence = -1;
|
||||
global_focus_t *pCurrentFocus = GetCurrentFocus();
|
||||
|
||||
static bool bMangoappSocketDisable = env_to_bool( getenv( "GAMESCOPE_MANGOAPP_SOCKET_DISABLE" ));
|
||||
@@ -8492,8 +8492,6 @@ void update_wayland_res(CommitDoneList_t *doneCommits, steamcompmgr_win_t *w, Re
|
||||
bool bValidPreemptiveScale = reslistentry.pAcquirePoint && pUpscaleFocus && cv_upscale_preemptive;
|
||||
bool bPreemptiveUpscale = bValidPreemptiveScale && newCommit->ShouldPreemptivelyUpscale( pUpscaleFocus->eUpscaleFilter, pUpscaleFocus->eUpscaleScaler );
|
||||
|
||||
- bool bKnownReady = false;
|
||||
-
|
||||
std::pair<int32_t, bool> eventFd = gamescope::CAcquireTimelinePoint::k_InvalidEvent;
|
||||
|
||||
if ( bPreemptiveUpscale )
|
||||
@@ -8589,31 +8587,44 @@ void update_wayland_res(CommitDoneList_t *doneCommits, steamcompmgr_win_t *w, Re
|
||||
pBackendFb->SetBuffer( buf );
|
||||
}
|
||||
|
||||
- if ( eventFd != gamescope::CAcquireTimelinePoint::k_InvalidEvent )
|
||||
- {
|
||||
- fence = eventFd.first;
|
||||
- bKnownReady = eventFd.second;
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- struct wlr_dmabuf_attributes dmabuf = {0};
|
||||
- if ( wlr_buffer_get_dmabuf( buf, &dmabuf ) )
|
||||
- {
|
||||
- fence = dup( dmabuf.fd[0] );
|
||||
- }
|
||||
- else
|
||||
+ gamescope::BufferCommitSync commitSync = gamescope::SelectBufferCommitSync(
|
||||
+ eventFd != gamescope::CAcquireTimelinePoint::k_InvalidEvent,
|
||||
+ eventFd.first,
|
||||
+ eventFd.second,
|
||||
+ [&]() -> std::optional<int> {
|
||||
+ struct wlr_dmabuf_attributes dmabuf = {0};
|
||||
+ if ( wlr_buffer_get_dmabuf( buf, &dmabuf ) )
|
||||
+ {
|
||||
+ int fd = dup( dmabuf.fd[0] );
|
||||
+ if ( fd < 0 )
|
||||
+ xwm_log.errorf( "Failed to duplicate DMA-BUF fence: %d", errno );
|
||||
+ return fd;
|
||||
+ }
|
||||
+ return std::nullopt;
|
||||
+ } );
|
||||
+ if ( commitSync.source == gamescope::BufferCommitSyncSource::SynchronousUpload )
|
||||
+ {
|
||||
+ // vulkan_create_texture_from_wlr_buffer waited for this SHM upload to finish.
|
||||
+ // Exporting its image memory as a DMA-BUF does not produce a sync fence and can
|
||||
+ // return an invalid fd on drivers that do not export the allocation.
|
||||
+ static bool loggedSynchronousUpload = false;
|
||||
+ if ( !loggedSynchronousUpload )
|
||||
{
|
||||
- fence = newCommit->vulkanTex->memoryFence();
|
||||
+ xwm_log.infof( "Treating synchronously uploaded SHM commit %lu as ready without an external fence",
|
||||
+ newCommit->commitID );
|
||||
+ loggedSynchronousUpload = true;
|
||||
}
|
||||
}
|
||||
|
||||
gpuvis_trace_printf( "pushing wait for commit %lu win %lx", newCommit->commitID, w->type == steamcompmgr_win_type_t::XWAYLAND ? w->xwayland().id : 0 );
|
||||
+ if ( !gamescope::DispatchBufferCommitSync( newCommit.get(), g_ImageWaiter, commitSync,
|
||||
+ mango_nudge, uMangoMsgType, doneCommits ) )
|
||||
{
|
||||
- newCommit->SetFence( fence, mango_nudge, uMangoMsgType, doneCommits );
|
||||
- if ( bKnownReady )
|
||||
- newCommit->Signal();
|
||||
- else
|
||||
- g_ImageWaiter.AddWaitable( newCommit.get() );
|
||||
+ xwm_log.errorf( "Failed to queue %s commit fence fd %d for commit %lu",
|
||||
+ gamescope::BufferCommitSyncSourceName( commitSync.source ), commitSync.fence, newCommit->commitID );
|
||||
+ // Dropping the commit releases its buffer and discards presentation feedback.
|
||||
+ w->receivedDoneCommit = true;
|
||||
+ return;
|
||||
}
|
||||
|
||||
w->commit_queue.push_back( std::move(newCommit) );
|
||||
diff --git a/src/buffer_commit_sync.hpp b/src/buffer_commit_sync.hpp
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/src/buffer_commit_sync.hpp
|
||||
@@ -0,0 +1,79 @@
|
||||
+#pragma once
|
||||
+
|
||||
+#include <optional>
|
||||
+
|
||||
+namespace gamescope
|
||||
+{
|
||||
+enum class BufferCommitSyncSource
|
||||
+{
|
||||
+ ExplicitAcquire,
|
||||
+ DmaBuf,
|
||||
+ SynchronousUpload,
|
||||
+};
|
||||
+
|
||||
+struct BufferCommitSync
|
||||
+{
|
||||
+ int fence = -1;
|
||||
+ bool knownReady = false;
|
||||
+ BufferCommitSyncSource source = BufferCommitSyncSource::SynchronousUpload;
|
||||
+
|
||||
+ static constexpr BufferCommitSync ExplicitAcquire( int fence, bool knownReady )
|
||||
+ {
|
||||
+ return { fence, knownReady, BufferCommitSyncSource::ExplicitAcquire };
|
||||
+ }
|
||||
+
|
||||
+ static constexpr BufferCommitSync DmaBuf( int fence )
|
||||
+ {
|
||||
+ return { fence, false, BufferCommitSyncSource::DmaBuf };
|
||||
+ }
|
||||
+
|
||||
+ static constexpr BufferCommitSync SynchronousUpload()
|
||||
+ {
|
||||
+ return { -1, true, BufferCommitSyncSource::SynchronousUpload };
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+template <typename GetDmaBufFence>
|
||||
+BufferCommitSync SelectBufferCommitSync( bool hasExplicitAcquire, int explicitFence,
|
||||
+ bool explicitKnownReady, GetDmaBufFence &&getDmaBufFence )
|
||||
+{
|
||||
+ if ( hasExplicitAcquire )
|
||||
+ return BufferCommitSync::ExplicitAcquire( explicitFence, explicitKnownReady );
|
||||
+
|
||||
+ if ( std::optional<int> dmaBufFence = getDmaBufFence() )
|
||||
+ return BufferCommitSync::DmaBuf( *dmaBufFence );
|
||||
+
|
||||
+ return BufferCommitSync::SynchronousUpload();
|
||||
+}
|
||||
+
|
||||
+constexpr const char *BufferCommitSyncSourceName( BufferCommitSyncSource source )
|
||||
+{
|
||||
+ switch ( source )
|
||||
+ {
|
||||
+ case BufferCommitSyncSource::ExplicitAcquire:
|
||||
+ return "explicit acquire";
|
||||
+ case BufferCommitSyncSource::DmaBuf:
|
||||
+ return "DMA-BUF";
|
||||
+ case BufferCommitSyncSource::SynchronousUpload:
|
||||
+ return "synchronous upload";
|
||||
+ }
|
||||
+
|
||||
+ return "unknown";
|
||||
+}
|
||||
+
|
||||
+template <typename Commit, typename Waiter, typename DoneCommits>
|
||||
+bool DispatchBufferCommitSync( Commit *commit, Waiter &waiter, const BufferCommitSync &sync,
|
||||
+ bool mangoNudge, unsigned int mangoMsgType, DoneCommits *doneCommits )
|
||||
+{
|
||||
+ commit->SetFence( sync.fence, mangoNudge, mangoMsgType, doneCommits );
|
||||
+ if ( !sync.knownReady && sync.fence < 0 )
|
||||
+ return false;
|
||||
+ if ( sync.knownReady )
|
||||
+ {
|
||||
+ commit->Signal();
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ return waiter.AddWaitable( commit );
|
||||
+}
|
||||
+}
|
||||
@@ -0,0 +1,54 @@
|
||||
diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp
|
||||
--- a/src/steamcompmgr.cpp
|
||||
+++ b/src/steamcompmgr.cpp
|
||||
@@ -1851,16 +1851,22 @@
|
||||
commit->fifo = false;
|
||||
}
|
||||
|
||||
- if ( gamescope::OwningRc<CVulkanTexture> pTexture = s_BufferMemos.LookupVulkanTexture( buf ) )
|
||||
- {
|
||||
- // Going from OwningRc -> Rc now.
|
||||
- commit->vulkanTex = pTexture;
|
||||
- return commit;
|
||||
- }
|
||||
-
|
||||
struct wlr_dmabuf_attributes dmabuf = {0};
|
||||
+ const bool bIsDmabuf = wlr_buffer_get_dmabuf( buf, &dmabuf );
|
||||
+ // DMA-BUF textures reference the producer's live allocation. SHM textures are
|
||||
+ // snapshots uploaded into separate memory and must be refreshed every commit.
|
||||
+ if ( bIsDmabuf )
|
||||
+ {
|
||||
+ if ( gamescope::OwningRc<CVulkanTexture> pTexture = s_BufferMemos.LookupVulkanTexture( buf ) )
|
||||
+ {
|
||||
+ // Going from OwningRc -> Rc now.
|
||||
+ commit->vulkanTex = pTexture;
|
||||
+ return commit;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
gamescope::OwningRc<gamescope::IBackendFb> pBackendFb;
|
||||
- if ( wlr_buffer_get_dmabuf( buf, &dmabuf ) )
|
||||
+ if ( bIsDmabuf )
|
||||
{
|
||||
pBackendFb = GetBackend()->ImportDmabufToBackend( &dmabuf );
|
||||
}
|
||||
@@ -1874,7 +1880,8 @@
|
||||
|
||||
commit->vulkanTex = pOwnedTexture;
|
||||
|
||||
- s_BufferMemos.MemoizeBuffer( buf, std::move( pOwnedTexture ) );
|
||||
+ if ( bIsDmabuf )
|
||||
+ s_BufferMemos.MemoizeBuffer( buf, std::move( pOwnedTexture ) );
|
||||
|
||||
return commit;
|
||||
}
|
||||
@@ -8599,7 +8606,8 @@
|
||||
} );
|
||||
if ( commitSync.source == gamescope::BufferCommitSyncSource::SynchronousUpload )
|
||||
{
|
||||
- // vulkan_create_texture_from_wlr_buffer waited for this SHM upload to finish.
|
||||
+ // Every SHM commit is uploaded synchronously; only DMA-BUF textures are memoized.
|
||||
+ // vulkan_create_texture_from_wlr_buffer waited for this upload to finish.
|
||||
// Exporting its image memory as a DMA-BUF does not produce a sync fence and can
|
||||
// return an invalid fd on drivers that do not export the allocation.
|
||||
static bool loggedSynchronousUpload = false;
|
||||
@@ -0,0 +1,15 @@
|
||||
diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp
|
||||
--- a/src/rendervulkan.cpp
|
||||
+++ b/src/rendervulkan.cpp
|
||||
@@ -4966,7 +4966,10 @@
|
||||
CVulkanTexture::createFlags texCreateFlags;
|
||||
texCreateFlags.bSampled = true;
|
||||
texCreateFlags.bTransferDst = true;
|
||||
- texCreateFlags.bFlippable = true;
|
||||
+ // Swapchain backends sample this synchronous SHM upload locally. They never
|
||||
+ // scan it out or forward its DMA-BUF, so external allocation and ownership
|
||||
+ // transfers are unnecessary. Keep exportable inputs for the other backends.
|
||||
+ texCreateFlags.bFlippable = !GetBackend()->UsesVulkanSwapchain();
|
||||
if ( pTex->BInit( width, height, 1u, drmFormat, texCreateFlags, nullptr, 0, 0, nullptr, pBackendFb ) == false )
|
||||
return nullptr;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp
|
||||
--- a/src/steamcompmgr.cpp
|
||||
+++ b/src/steamcompmgr.cpp
|
||||
@@ -2350,6 +2350,9 @@ bool MouseCursor::getTexture()
|
||||
|
||||
CVulkanTexture::createFlags texCreateFlags;
|
||||
- texCreateFlags.bFlippable = true;
|
||||
+ // The Vulkan swapchain backend samples cursor pixels locally. It does not
|
||||
+ // scan out or forward a cursor DMA-BUF, so keep this allocation internal.
|
||||
+ // Hardware and forwarding backends retain their exportable cursor texture.
|
||||
+ texCreateFlags.bFlippable = !GetBackend()->UsesVulkanSwapchain();
|
||||
if ( GetBackend()->SupportsPlaneHardwareCursor() )
|
||||
{
|
||||
texCreateFlags.bLinear = true; // cursor buffer needs to be linear
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/src/wlserver.cpp
|
||||
+++ b/src/wlserver.cpp
|
||||
@@ -2,6 +2,7 @@
|
||||
#define _GNU_SOURCE 1
|
||||
|
||||
#include <assert.h>
|
||||
+#include <cfloat>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
@@ -0,0 +1,5 @@
|
||||
export LD_LIBRARY_PATH="$DROIDDECK_QUALCOMM_ROOT/mesa${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
export LIBGL_DRIVERS_PATH="$DROIDDECK_QUALCOMM_ROOT/mesa/dri"
|
||||
export GBM_BACKENDS_PATH="$DROIDDECK_QUALCOMM_ROOT/mesa/gbm"
|
||||
export __GLX_VENDOR_LIBRARY_NAME=droiddeck_qualcomm
|
||||
export __EGL_VENDOR_LIBRARY_FILENAMES="$DROIDDECK_QUALCOMM_ROOT/mesa/egl-vendor.json"
|
||||
+104
-6
@@ -9,12 +9,14 @@
|
||||
"upstream_source_commit": "bc8cb3586b79d208fd21b925fa0209b3e432e008"
|
||||
},
|
||||
"gamescope": {
|
||||
"url": "https://github.com/wallentx/DroidDeck/releases/download/main-af320dd86c7de5738ff16cfeeefbcf42581c199e/DroidDeck-0.5.0-wallentx.4.apk",
|
||||
"sha256": "f73b229bf5d29c53c4ac618815e77c6e5a3eddd5c99e7c93eab276c831fcef6e",
|
||||
"size": 124331295,
|
||||
"source_commit": "af320dd86c7de5738ff16cfeeefbcf42581c199e",
|
||||
"member": "assets/linuxfs/usr/local/bin/gamescope",
|
||||
"member_sha256": "2250f26cdf0b6c73af80324c2a99b60465bae46ae6719083b9f9f8d1cf680165"
|
||||
"source_commit": "8f212644c46460549035971ab914421180f61a7c",
|
||||
"member_sha256": "1efe37d2bee38b86a051f84279802054d770aed6d9eff7d9379b4648705d0881",
|
||||
"build": {
|
||||
"binary": "build/qualcomm-gamescope/gamescope",
|
||||
"source": "build/qualcomm-gamescope/source.tar.gz",
|
||||
"source_sha256": "11e9134ef9fe0616bc1c1ef8bdf61c52b48f473ce07e562108cf7476a017e531",
|
||||
"source_size": 10104613
|
||||
}
|
||||
},
|
||||
"wsi": {
|
||||
"files": [
|
||||
@@ -113,5 +115,101 @@
|
||||
"73368f4579fe0d27": "cs_composite_blur",
|
||||
"80346d82c5a6d850": "cs_composite_blit"
|
||||
}
|
||||
},
|
||||
"mesa": {
|
||||
"source_commit": "31e9a6b2e95e30d84bf3177d1f497d063e59b6b2",
|
||||
"directory": "build/qualcomm-zink/runtime",
|
||||
"files": [
|
||||
{
|
||||
"path": "dri/zink_dri.so",
|
||||
"sha256": "a65a4b895ebf49ec2841023fa550f50344574e1b68d574468365ea7857f577fa",
|
||||
"size": 133368
|
||||
},
|
||||
{
|
||||
"path": "egl-vendor.json",
|
||||
"sha256": "aafac15b0a72b9b782d88729f3481c217b27f6e8664ffc9d6493df566ebb466a",
|
||||
"size": 93
|
||||
},
|
||||
{
|
||||
"path": "gbm/dri_gbm.so",
|
||||
"sha256": "57e5aa8c3903c13db6a38e28cdc9160f2bd09cec04c88c90d6603d15b761b524",
|
||||
"size": 134024
|
||||
},
|
||||
{
|
||||
"path": "libEGL_droiddeck_qualcomm.so.0",
|
||||
"sha256": "234d2041db7e87717c507ee90697058f646d1d9b61adc94f4a8cc52576fe733d",
|
||||
"size": 398632
|
||||
},
|
||||
{
|
||||
"path": "libgallium-26.2.3.so",
|
||||
"sha256": "bcda320860b051725ca49fd546937cc6512373bc21ce0d1e7f9c097341f3e9bc",
|
||||
"size": 21578336
|
||||
},
|
||||
{
|
||||
"path": "libgbm.so.1",
|
||||
"sha256": "e9f131c50d423832eb947292b2a4f6405e51c272ce76906e3e31d5e5988334a9",
|
||||
"size": 67840
|
||||
},
|
||||
{
|
||||
"path": "libGLX_droiddeck_qualcomm.so.0",
|
||||
"sha256": "0c508865568327c98230c7ae5c241716715e70cc5b9dfc942cdb7bb79fc346a5",
|
||||
"size": 397704
|
||||
},
|
||||
{
|
||||
"path": "licenses/Apache-2.0",
|
||||
"sha256": "aeae52465380b8b7959c940355e869cbbc277d4559a7ababa49cbc4818c051f5",
|
||||
"size": 10232
|
||||
},
|
||||
{
|
||||
"path": "licenses/BSL-1.0",
|
||||
"sha256": "cf7105f24feaf70f28b8de10074e4d37bef89b8f3ae0a5f1280237f027d77ec0",
|
||||
"size": 1666
|
||||
},
|
||||
{
|
||||
"path": "licenses/exceptions/Linux-Syscall-Note",
|
||||
"sha256": "d3a16686cf8797fd8a7b35564b9de8586e6ed8e19c9447e16ab6dda0d411215d",
|
||||
"size": 1281
|
||||
},
|
||||
{
|
||||
"path": "licenses/GPL-1.0-or-later",
|
||||
"sha256": "13ff2a162a56796d863de9373ea46860a0fc28da81c192e76da9704dc117c27b",
|
||||
"size": 13307
|
||||
},
|
||||
{
|
||||
"path": "licenses/GPL-2.0-only",
|
||||
"sha256": "535fe49430f3098603b190f07040d5b0c65eb8ed5fe173108e26eb668e758c00",
|
||||
"size": 18774
|
||||
},
|
||||
{
|
||||
"path": "licenses/Mesa-license.rst",
|
||||
"sha256": "e272a136f6ce1b855afeef2fbe44f94f17d3b4d91cef29f8364af5800c5838a8",
|
||||
"size": 1866
|
||||
},
|
||||
{
|
||||
"path": "licenses/MIT",
|
||||
"sha256": "a1179eae4e8b0ef06cbad6b6a2e324bb1c1cd9281cf40b42c0b15b0e34df2683",
|
||||
"size": 1402
|
||||
},
|
||||
{
|
||||
"path": "licenses/SGI-B-2.0",
|
||||
"sha256": "8299bbd83c4d77eb5ae767cf250e9b3b470218e863714cba2e5e7eaed51285f2",
|
||||
"size": 1844
|
||||
},
|
||||
{
|
||||
"path": "licenses/source.json",
|
||||
"sha256": "6bb1d1549c264228466853656258f1e3c6822c2bf314260e4900cd0eed3a393c",
|
||||
"size": 658
|
||||
},
|
||||
{
|
||||
"path": "licenses/zink-optional-maintenance5.patch",
|
||||
"sha256": "25f0733bc42199344f205a916f6674d578a9a78250fd6b4cecbfb1cbbc8f8cf5",
|
||||
"size": 1682
|
||||
},
|
||||
{
|
||||
"path": "qualcomm-gl.sh",
|
||||
"sha256": "2ed84bb2a5fa20cde52f18733fe3e8d1fda22f3f0d0bdad47363ff71a04522f5",
|
||||
"size": 353
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
--- a/src/gallium/drivers/zink/zink_device_info.py
|
||||
+++ b/src/gallium/drivers/zink/zink_device_info.py
|
||||
@@ -68,8 +68,7 @@
|
||||
features=True),
|
||||
Extension("VK_KHR_maintenance5",
|
||||
alias="maint5",
|
||||
- features=True, properties=True,
|
||||
- required=True),
|
||||
+ features=True, properties=True),
|
||||
Extension("VK_KHR_maintenance6",
|
||||
alias="maint6",
|
||||
features=True, properties=True),
|
||||
--- a/src/gallium/drivers/zink/zink_compiler.c
|
||||
+++ b/src/gallium/drivers/zink/zink_compiler.c
|
||||
@@ -3056,7 +3056,7 @@
|
||||
bool can_remove = false;
|
||||
if (!nir_find_variable_with_location(consumer, nir_var_shader_in, VARYING_SLOT_PSIZ)) {
|
||||
/* maintenance5 guarantees "A default size of 1.0 is used if PointSize is not written" */
|
||||
- if (!var->data.explicit_xfb_buffer && delete_psiz_store(producer, true))
|
||||
+ if (screen->info.have_KHR_maintenance5 && !var->data.explicit_xfb_buffer && delete_psiz_store(producer, true))
|
||||
can_remove = !(producer->info.outputs_written & VARYING_BIT_PSIZ);
|
||||
else if (consumer->info.stage != MESA_SHADER_FRAGMENT)
|
||||
can_remove = !var->data.explicit_location;
|
||||
--- a/src/gallium/drivers/zink/zink_context.c
|
||||
+++ b/src/gallium/drivers/zink/zink_context.c
|
||||
@@ -1007,7 +1007,7 @@
|
||||
NULL,
|
||||
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT
|
||||
};
|
||||
- bvci.pNext = &usage;
|
||||
+ bvci.pNext = screen->info.have_KHR_maintenance5 ? &usage : NULL;
|
||||
if (zink_get_format_props(screen, format)->bufferFeatures & VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT)
|
||||
usage.usage |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
|
||||
|
||||
@@ -86,6 +86,34 @@ class QualcommRuntimeTest(unittest.TestCase):
|
||||
runtime.stage(root / 'bad-path', cache, lock)
|
||||
self.assertFalse((root / 'outside.so').exists())
|
||||
|
||||
def test_local_builds_are_pinned_and_never_fall_back_to_a_download(self):
|
||||
bridge, binary, source, mesa = b'bridge', b'built gamescope', b'corresponding source', b'private mesa'
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
built, cache = root / 'build/private', root / 'cache'
|
||||
built.mkdir(parents=True)
|
||||
cache.mkdir()
|
||||
lock = self.lock(bridge)
|
||||
(cache / (lock['sha256'] + '.tzst')).write_bytes(bridge)
|
||||
for name, data in [('gamescope', binary), ('source.tar.gz', source), ('mesa.so', mesa)]:
|
||||
(built / name).write_bytes(data)
|
||||
lock['gamescope'] = dict(member_sha256=hashlib.sha256(binary).hexdigest(), build=dict(
|
||||
binary='build/private/gamescope', source='build/private/source.tar.gz',
|
||||
source_sha256=hashlib.sha256(source).hexdigest(), source_size=len(source)))
|
||||
lock['mesa'] = dict(directory='build/private', files=[dict(
|
||||
path='mesa.so', sha256=hashlib.sha256(mesa).hexdigest(), size=len(mesa))])
|
||||
with patch.object(runtime, '__file__', str(root / 'tools/qualcomm/fetch_runtime.py')):
|
||||
output = root / 'output'
|
||||
runtime.stage(output, cache, lock)
|
||||
self.assertEqual((output / 'gamescope').read_bytes(), binary)
|
||||
self.assertEqual((output / 'licenses/gamescope-patched-source.tar.gzip').read_bytes(), source)
|
||||
self.assertEqual((output / 'mesa/mesa.so').read_bytes(), mesa)
|
||||
(built / 'mesa.so').write_bytes(b'wrong mesa')
|
||||
with self.assertRaisesRegex(ValueError, 'Mesa member'):
|
||||
runtime.stage(output, cache, lock)
|
||||
(built / 'gamescope').unlink()
|
||||
with self.assertRaisesRegex(FileNotFoundError, 'Build private Gamescope'):
|
||||
runtime.stage(output, cache, lock)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Reference in new issue
Block a user