mirror of
https://github.com/nestriness/nestri.git
synced 2025-12-11 00:05:36 +02:00
✨ feat: Add a new blog post
This commit is contained in:
@@ -88,6 +88,11 @@ Nestri is an open-source, self-hosted Geforce Now alternative with Stadia's soci
|
||||
|
||||
If you appreciate our work and wish to support the development of Nestri, consider making a donation [here](https://polar.sh/nestri/donate). Your contributions will help us improve the platform and enhance your gaming experience. Thank you for your support!
|
||||
|
||||
## Demo
|
||||
|
||||
Nestri is still in development, but here is some footage from Behind-The-Scenes
|
||||
|
||||
<img src="/apps/www/public/seo/code.avif" alt="Nestri - What will you play next?">
|
||||
|
||||
|
||||
[github-release-link]: https://github.com/nestriness/nestri/releases
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{
|
||||
"extends": "./.nuxt/tsconfig.json"
|
||||
"extends": "./.nuxt/tsconfig.json",
|
||||
"ignoreConfigErrors": true
|
||||
}
|
||||
|
||||
BIN
apps/www/public/seo/code.avif
Normal file
BIN
apps/www/public/seo/code.avif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 546 KiB |
BIN
apps/www/public/seo/image.png
Normal file
BIN
apps/www/public/seo/image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 247 KiB |
@@ -23,11 +23,27 @@
|
||||
}
|
||||
|
||||
.blog ul {
|
||||
@apply list-disc list-inside pl-4;
|
||||
@apply list-inside pl-4;
|
||||
}
|
||||
|
||||
.blog ol {
|
||||
@apply list-decimal list-inside;
|
||||
@apply list-disc list-decimal list-inside;
|
||||
}
|
||||
|
||||
.blog li {
|
||||
@apply list-decimal list-inside pl-4;
|
||||
}
|
||||
|
||||
.blog a {
|
||||
@apply text-primary-500 underline underline-offset-2 cursor-pointer;
|
||||
}
|
||||
|
||||
.blog code {
|
||||
@apply bg-gray-700 p-1 rounded-sm;
|
||||
}
|
||||
|
||||
.blog blockquote {
|
||||
@apply bg-gray-800 p-2 rounded-md border-l-4 border-l-primary-500
|
||||
}
|
||||
|
||||
#toc .active {
|
||||
|
||||
72
apps/www/src/routes/blog/gpu-passthru/index.mdx
Normal file
72
apps/www/src/routes/blog/gpu-passthru/index.mdx
Normal file
@@ -0,0 +1,72 @@
|
||||
---
|
||||
title: "Navigating VMs and GPU Passthrough: Building a Better Foundation for Nestri"
|
||||
blogTitle: "Navigating VMs and GPU Passthrough: Building a Better Foundation for Nestri"
|
||||
summary: "Join us as we navigate the challenges of building Nestri"
|
||||
slug: "gpu-passthru"
|
||||
thumbnail: "/seo/image.png"
|
||||
createdAt: "2024-10-26T23:28:02.584Z"
|
||||
authors:
|
||||
- name: "Wanjohi Ryan"
|
||||
link: "https://github.com/wanjohiryan"
|
||||
---
|
||||
|
||||
As we continue to work on [Nestri](https://github.com/nestriness/nestri), our open-source cloud gaming platform, we've found ourselves at an important crossroads. Currently, in our v0.2 release, we are utilizing Docker extensively with numerous customizations. However, this setup has proven to be somewhat limiting, prompting me to explore alternative solutions.
|
||||
|
||||
> TL;DR: In my search for a more efficient virtual machine (VM) setup, I ventured into the realm of Virtual Machine Monitors (VMMs) for better GPU passthrough capabilities. The journey was... with its fair share of challenges.
|
||||
|
||||
My motivation was - I wanted a VM, but not just a standard one—a VMM, which provides paravirtualized environments with numerous advantages.
|
||||
|
||||
### Key Criteria for Selecting a VMM
|
||||
1. **Fast Boot Times** - Gaming is all about urgency; no one wants to wait ages to start playing.
|
||||
2. **Small Resource Footprint:** - The VM should use minimal host resources outside of the allocated ones. I recognize that this ties closely with the first criterion. ^^
|
||||
3. **GPU Passthrough Support (Venus and Virgl):** - GPU passthrough is essential for gaming, particularly as we aim to facilitate multiple sessions on a single host if possible.
|
||||
4. **Support for Wayland and X11 Passthrough:** - This feature is crucial for debugging, allowing visibility of applications on the host window manager.
|
||||
5. **Security and Isolation:** - While most VMs are designed with security in mind, having this as a bonus feature is essential.
|
||||
|
||||
Think of it this way: if you’re running a home server, you want the best possible experience when using Nestri - you want it to be fast, easy to set up, and secure. I wanted to find a solution that didn’t compromise user and developer experiences compared to our existing Docker setup.
|
||||
|
||||
The aspect of GPU passthrough was vital; utilizing Venus and Virgl would allow me to "pass in" the GPU without mounting it directly within the VM. This could simplify supporting integrated GPUs and enable multiple gaming sessions on a single GPU—very important to Nestri’s functionality.
|
||||
|
||||
> Explainer time ⌚ <br/>
|
||||
> **Virgl:** A modern virtual 3D GPU that enables guest operating systems to leverage the host's GPU for 3D rendering through OpenGL. <br/>
|
||||
> **Venus:** Similar to Virgl, but it utilizes Vulkan for rendering instead of OpenGL.
|
||||
|
||||
## The Quest for the Right VMM
|
||||
|
||||
The search for the right VMM brought forth various experiences and challenges. Here’s a breakdown of the contenders:
|
||||
|
||||
### Firecracker
|
||||
I had worked with Firecracker before - the AWS' "MicroVM" with a marketing budget - so i started there. Unfortunately, I quickly discovered that it does not support GPU passthrough :(
|
||||
|
||||
### Cloud Hypervisor
|
||||
Next, I explored Cloud Hypervisor. While it is a convenient option, it also lacks native GPU passthrough capabilities unless patched with modifications from [Spectrum OS](https://spectrum-os.org/software/cloud-hypervisor/). It’s worth noting that this MicroVM comes prebuilt, which is very appealing,_* has the best DX so far *_.
|
||||
|
||||
### QEMU
|
||||
Then there’s QEMU. While not strictly a MicroVMM, it offers extensive features - this is probably the closest one to CrosVM in terms of features. However, implementing Venus requires significant work in terms of [kernel and QEMU patches](https://github.com/TrippleXC/VenusPatches)—far more effort than I was aiming for.
|
||||
|
||||
### CrosVM
|
||||
CrosVM emerged as a strong candidate. At first glance, it looked perfect _too perfect_ - excellent documentation, backed by Google, support for Wayland, Xorg, and GPU passthrough? Why is no one is using this?. I later found out the hard way that it was quite challenging to set up and work with.
|
||||
|
||||
After weeks of extensive work and research - sometimes even going as far as reading the ChromeOS, Mesa, Virgl or CrosVM codebase - I finally managed to get Virtio-GPU with Venus functioning on the Intel Arc A780 dGPU. In retrospect, I am confident that I could simplify the process and cut down on the time-to-setup significantly if I were to repeat it - but i will reserve the energy for another day.
|
||||
|
||||
To get it to work, I had to install and tinker around with multiple versions of Virgl renderer, Mesa, Minijail and Minigbm on the host. Additionally, there were multiple command-line arguments to experiment with, each contributing to the eventual success of the setup.
|
||||
|
||||
Testing if the GPU is operating correctly usually involves running a simple application like `Vkcube` or `glxgears`, which visually indicates successful GPU rendering. That's where Sommelier enters the picture; it’s a tool that allows the guest OS to pass Wayland (and x11 thru XWayland) compositing to the host OS while maintaining VMM isolation.
|
||||
|
||||
To make Sommelier work, one is required to either modify the upstream Linux kernel or use the ChromeOS kernel - specifically built with the Borealis config.
|
||||
|
||||
I went ahead and built the Chrome OS kernel with an ArchLinux Docker image and to confirm that the kernel was working as intended, I had to check whether the `/dev/wl0` file on the guest os - the installation was succesful.
|
||||
|
||||
But after a lot of trial and error, I still could not get it to work. There were Rutabaga errors everywhere... I have reached out for help from [the Google Group](https://groups.google.com/a/chromium.org/g/crosvm-dev/c/1RQUOI8FlNY) forum for assistance, no response yet.
|
||||
|
||||
Well, i gave up 😅
|
||||
|
||||
> **Food for thought:**
|
||||
> Why do you have to build the MicroVMMs yourself? A prebuilt binary, similar to what Cloud Hypervisor offers, would have a much better DX. But what do i know? 🤷🏾♂️
|
||||
|
||||
This morning, I came across [muvm](https://github.com/AsahiLinux/muvm/) and , which Asahi Linux employs to run Steam games on Mac. It appears promising, but like most of the other options, it also demands building it. Should i try setting it up in another blog? Tune in to find out.
|
||||
|
||||
And there you have it—my exploration of VMMs and GPU passthrough for Nestri. If you have insights or experiences in this domain, please feel free to reach out on the [Nestri Discord channel](https://discord.com/invite/Y6etn3qKZ3). I look forward to hearing from you!
|
||||
|
||||
> You can find source code for the whole mini-project [here](https://github.com/nestriness/nestri/pull/123). <br/>
|
||||
> If you need help setting it up let me know on Discord :)
|
||||
@@ -5,10 +5,10 @@ import { MotionComponent, TitleSection, transition } from "@nestri/ui/react"
|
||||
|
||||
const blogs = [
|
||||
{
|
||||
title: "Nestri's Architecture so far",
|
||||
createdAt: "2024-09-20T12:15:22.974Z",
|
||||
description: "Nestri has been in development for a while now and we are working on",
|
||||
href: "how-nestri-works"
|
||||
title: "Navigating VMs and GPU Passthrough: Building a Better Foundation for Nestri",
|
||||
createdAt: "2024-10-26T23:28:02.584Z",
|
||||
description: "Join us as we navigate the challenges of building Nestri",
|
||||
href: "gpu-passthru"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -56,15 +56,15 @@ export default component$(() => {
|
||||
</div>
|
||||
<hr class="h-[2px] bg-gray-200 dark:bg-gray-800" />
|
||||
<div class="w-full relative sm:text-sm text-base gap-3 flex flex-col">
|
||||
<div class="flex item-center flex-col gap-2 w-full">
|
||||
{/* <div class="flex item-center flex-col gap-2 w-full">
|
||||
<div class="gap-1.5 flex w-full items-center text-neutral-900/70 dark:text-neutral-100/70" >
|
||||
<div class="size-5 relative">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-full h-full" viewBox="0 0 24 24"><path fill="currentColor" d="M8.21 17.32L7 16.8a2.13 2.13 0 1 0 1.17-2.93l1.28.53a1.58 1.58 0 0 1-1.22 2.92z" /><path fill="currentColor" d="M12 2a10 10 0 0 0-10 9.34l5.38 2.21a2.31 2.31 0 0 1 .47-.24A2.62 2.62 0 0 1 9 13.1l2.44-3.56a3.8 3.8 0 1 1 3.8 3.8h-.08l-3.51 2.5a2.77 2.77 0 0 1-5.47.68l-3.77-1.6A10 10 0 1 0 12 2" /><path fill="currentColor" d="M17.79 9.5a2.53 2.53 0 1 0-2.53 2.5a2.54 2.54 0 0 0 2.53-2.5m-4.42 0a1.9 1.9 0 1 1 1.9 1.91a1.9 1.9 0 0 1-1.9-1.92z" /></svg>
|
||||
</div>
|
||||
<p class="group relative">Add upto <span class="">3 games</span> at a time</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gap-2.5 flex relative items-center w-full" >
|
||||
</div> */}
|
||||
{/* <div class="gap-2.5 flex relative items-center w-full" >
|
||||
<div class="gap-1.5 flex w-full items-center text-neutral-900/70 dark:text-neutral-100/70" >
|
||||
<div class="size-5 relative">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-full h-full" viewBox="0 0 24 24">
|
||||
@@ -77,15 +77,15 @@ export default component$(() => {
|
||||
</div>
|
||||
<p>Basic <span class="">datacenter</span> GPU</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gap-2.5 flex relative items-center w-full" >
|
||||
</div> */}
|
||||
{/* <div class="gap-2.5 flex relative items-center w-full" >
|
||||
<div class="gap-1.5 flex w-full items-center text-neutral-900/70 dark:text-neutral-100/70" >
|
||||
<div class="size-5 relative">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-full h-full" viewBox="0 0 24 24"><path fill="currentColor" d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2S2 6.477 2 12s4.477 10 10 10" opacity=".5" /><path fill="currentColor" fill-rule="evenodd" d="M12 7.25a.75.75 0 0 1 .75.75v3.69l2.28 2.28a.75.75 0 1 1-1.06 1.06l-2.5-2.5a.75.75 0 0 1-.22-.53V8a.75.75 0 0 1 .75-.75" clip-rule="evenodd" /></svg>
|
||||
</div>
|
||||
<p><span class="">3 hours</span> of daily playtime</p>
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
<div class="gap-2.5 flex relative items-center w-full" >
|
||||
<div class="gap-1.5 flex w-full items-center text-neutral-900/70 dark:text-neutral-100/70" >
|
||||
<div class="size-5 relative">
|
||||
@@ -199,7 +199,7 @@ export default component$(() => {
|
||||
</div>
|
||||
<hr class="h-[2px] bg-gray-300 dark:bg-gray-700" />
|
||||
<div class="w-full sm:text-sm text-base relative gap-3 flex flex-col">
|
||||
<div class="flex item-center flex-col gap-2 w-full">
|
||||
{/* <div class="flex item-center flex-col gap-2 w-full">
|
||||
<div class="gap-1.5 flex w-full items-center text-neutral-900/70 dark:text-neutral-100/70" >
|
||||
<div class="size-5 relative">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-full h-full" viewBox="0 0 24 24"><path fill="currentColor" d="M8.21 17.32L7 16.8a2.13 2.13 0 1 0 1.17-2.93l1.28.53a1.58 1.58 0 0 1-1.22 2.92z" /><path fill="currentColor" d="M12 2a10 10 0 0 0-10 9.34l5.38 2.21a2.31 2.31 0 0 1 .47-.24A2.62 2.62 0 0 1 9 13.1l2.44-3.56a3.8 3.8 0 1 1 3.8 3.8h-.08l-3.51 2.5a2.77 2.77 0 0 1-5.47.68l-3.77-1.6A10 10 0 1 0 12 2" /><path fill="currentColor" d="M17.79 9.5a2.53 2.53 0 1 0-2.53 2.5a2.54 2.54 0 0 0 2.53-2.5m-4.42 0a1.9 1.9 0 1 1 1.9 1.91a1.9 1.9 0 0 1-1.9-1.92z" /></svg>
|
||||
@@ -209,8 +209,8 @@ export default component$(() => {
|
||||
<p>+$3/game</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gap-2.5 flex relative items-center w-full" >
|
||||
</div> */}
|
||||
{/* <div class="gap-2.5 flex relative items-center w-full" >
|
||||
<div class="gap-1.5 flex w-full items-center text-neutral-900/70 dark:text-neutral-100/70" >
|
||||
<div class="size-5 relative">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-full h-full" viewBox="0 0 24 24">
|
||||
@@ -223,15 +223,15 @@ export default component$(() => {
|
||||
</div>
|
||||
<p>Premium <span class="">consumer</span> GPU</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gap-2.5 flex relative items-center w-full" >
|
||||
</div> */}
|
||||
{/* <div class="gap-2.5 flex relative items-center w-full" >
|
||||
<div class="gap-1.5 flex w-full items-center text-neutral-900/70 dark:text-neutral-100/70" >
|
||||
<div class="size-5 relative">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-full h-full" viewBox="0 0 24 24"><path fill="currentColor" d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2S2 6.477 2 12s4.477 10 10 10" opacity=".5" /><path fill="currentColor" fill-rule="evenodd" d="M12 7.25a.75.75 0 0 1 .75.75v3.69l2.28 2.28a.75.75 0 1 1-1.06 1.06l-2.5-2.5a.75.75 0 0 1-.22-.53V8a.75.75 0 0 1 .75-.75" clip-rule="evenodd" /></svg>
|
||||
</div>
|
||||
<p><span class="">Unlimited</span> daily playtime</p>
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
<div class="gap-2.5 flex relative items-center w-full" >
|
||||
<div class="gap-1.5 flex w-full items-center text-neutral-900/70 dark:text-neutral-100/70" >
|
||||
<div class="size-5 relative">
|
||||
@@ -323,9 +323,9 @@ export default component$(() => {
|
||||
<p class="text-neutral-900/70 dark:text-neutral-100/70 text-base" >
|
||||
Looking for a custom cloud gaming platform? Use Nestri as your own on our servers or yours. Flexible licensing and white-glove onboarding included.
|
||||
</p>
|
||||
<Link class="underline underline-offset-1 font-medium font-title hover:opacity-70" href="mailto:enterprise@nestri.io">
|
||||
Let's Chat
|
||||
</Link>
|
||||
<div class="underline underline-offset-2 font-medium font-title">
|
||||
Nestri Studio is coming soon
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -28,16 +28,16 @@ export default defineConfig((): UserConfig => {
|
||||
tsconfigPaths(),
|
||||
qwikReact(),
|
||||
//For Moq-js (SharedArrayBuffer)
|
||||
{
|
||||
name: "configure-response-headers",
|
||||
configureServer: (server) => {
|
||||
server.middlewares.use((_req, res, next) => {
|
||||
res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
|
||||
res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
|
||||
next();
|
||||
});
|
||||
},
|
||||
},
|
||||
// {
|
||||
// name: "configure-response-headers",
|
||||
// configureServer: (server) => {
|
||||
// server.middlewares.use((_req, res, next) => {
|
||||
// res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
|
||||
// res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
|
||||
// next();
|
||||
// });
|
||||
// },
|
||||
// },
|
||||
],
|
||||
// This tells Vite which dependencies to pre-build in dev mode.
|
||||
optimizeDeps: {
|
||||
|
||||
@@ -24,6 +24,7 @@ export const Card = component$(({ titleWidth, titleHeight, game, size, ...props
|
||||
return Promise.all([modalUrl, modalTitleUrl].map(url => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.crossOrigin = "anonymous"
|
||||
img.onload = () => resolve(img);
|
||||
img.onerror = reject;
|
||||
img.src = url;
|
||||
|
||||
@@ -76,7 +76,7 @@ export const Footer = component$(({ showGH = true }: Props) => {
|
||||
<div class="flex flex-col gap-2">
|
||||
<h2 class="font-title text-sm font-bold" >Company</h2>
|
||||
<div class="text-gray-950/50 dark:text-gray-50/50 flex flex-col gap-2" >
|
||||
<p class="text-base opacity-50 cursor-not-allowed" >Blog</p>
|
||||
<Link href="/blog" class="text-base hover:text-gray-950 dark:hover:text-gray-50 transition-all duration-200 hover:underline hover:underline-offset-4" >Blog</Link>
|
||||
<Link href="/contact" class="text-base hover:text-gray-950 dark:hover:text-gray-50 transition-all duration-200 hover:underline hover:underline-offset-4" >Contact Us</Link>
|
||||
<p class="text-base opacity-50 cursor-not-allowed" >Open Startup</p>
|
||||
</div>
|
||||
|
||||
@@ -50,7 +50,7 @@ export const GithubBanner = component$(() => {
|
||||
</Link>
|
||||
<div class="min-w-max min-h-max w-full relative overflow-hidden rounded-[8px] flex justify-center items-center group">
|
||||
<div class="animate-multicolor before:-z-[1] -z-[2] absolute -right-full left-0 bottom-0 h-full w-[1000px] [background:linear-gradient(90deg,rgb(232,23,98)_1.26%,rgb(30,134,248)_18.6%,rgb(91,108,255)_34.56%,rgb(52,199,89)_49.76%,rgb(245,197,5)_64.87%,rgb(236,62,62)_85.7%)_0%_0%/50%_100%_repeat-x]" />
|
||||
<Link class="select-none m-[3px] relative justify-center items-center min-w-max flex z-[2] px-3 rounded-md h-8 w-full bg-white dark:bg-black group-hover:bg-transparent transition-all duration-200" rel="noopener noreferrer" href="/join" target="_blank">
|
||||
<Link class="select-none m-[3px] relative justify-center items-center min-w-max flex z-[2] px-3 rounded-md h-8 w-full bg-white dark:bg-black group-hover:bg-transparent transition-all duration-200" rel="noopener noreferrer" href="https://nestri.io/join" target="_blank">
|
||||
<span class="text-sm dark:text-white text-black group-hover:text-white w-full transition-all duration-200">
|
||||
<div class="flex justify-around items-center w-full p-1 h-max">
|
||||
Join Waitlist
|
||||
|
||||
@@ -86,6 +86,7 @@ export const BasicImageLoader = component$((props: ImageLoaderProps) => {
|
||||
draggable={false}
|
||||
alt={props.alt}
|
||||
width={props.width}
|
||||
crossOrigin='anonymous'
|
||||
height={props.height}
|
||||
ref={imgRef}
|
||||
class={{
|
||||
|
||||
Reference in New Issue
Block a user