working ivshmem setup
This commit is contained in:
parent
7a6ef52144
commit
758df315a1
13 changed files with 387 additions and 279 deletions
34
Cargo.lock
generated
34
Cargo.lock
generated
|
@ -26,12 +26,6 @@ version = "3.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "byteorder"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
|
@ -47,12 +41,6 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg_aliases"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dlib"
|
name = "dlib"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
|
@ -88,10 +76,8 @@ checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
|
||||||
name = "fbcserver"
|
name = "fbcserver"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"cc",
|
||||||
"libc",
|
|
||||||
"minifb",
|
"minifb",
|
||||||
"nix 0.29.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -302,18 +288,6 @@ dependencies = [
|
||||||
"memoffset",
|
"memoffset",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nix"
|
|
||||||
version = "0.29.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.6.0",
|
|
||||||
"cfg-if",
|
|
||||||
"cfg_aliases",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.20.2"
|
version = "1.20.2"
|
||||||
|
@ -578,7 +552,7 @@ dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"downcast-rs",
|
"downcast-rs",
|
||||||
"libc",
|
"libc",
|
||||||
"nix 0.24.3",
|
"nix",
|
||||||
"scoped-tls",
|
"scoped-tls",
|
||||||
"wayland-commons",
|
"wayland-commons",
|
||||||
"wayland-scanner",
|
"wayland-scanner",
|
||||||
|
@ -591,7 +565,7 @@ version = "0.29.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902"
|
checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nix 0.24.3",
|
"nix",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"wayland-sys",
|
"wayland-sys",
|
||||||
|
@ -603,7 +577,7 @@ version = "0.29.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661"
|
checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nix 0.24.3",
|
"nix",
|
||||||
"wayland-client",
|
"wayland-client",
|
||||||
"xcursor",
|
"xcursor",
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.5.0"
|
|
||||||
libc = "0.2.167"
|
|
||||||
minifb = "0.27.0"
|
minifb = "0.27.0"
|
||||||
nix = { version = "0.29.0", features = [ "stat", "mman" ] }
|
|
||||||
|
[build-dependencies]
|
||||||
|
cc = "1.0.99"
|
|
@ -33,7 +33,6 @@ LINK_LIBS := $(VS2022_PATH)/ucrt/lib/$(ARCH)/libucrt$(D).lib \
|
||||||
$(VS2022_PATH)/winsdk/lib/$(ARCH)/user32.lib \
|
$(VS2022_PATH)/winsdk/lib/$(ARCH)/user32.lib \
|
||||||
$(VS2022_PATH)/winsdk/lib/$(ARCH)/comctl32.lib \
|
$(VS2022_PATH)/winsdk/lib/$(ARCH)/comctl32.lib \
|
||||||
$(VS2022_PATH)/winsdk/lib/$(ARCH)/setupapi.lib \
|
$(VS2022_PATH)/winsdk/lib/$(ARCH)/setupapi.lib \
|
||||||
$(VS2022_PATH)/winsdk/lib/$(ARCH)/ws2_32.lib
|
|
||||||
|
|
||||||
.PHONY: all dumpinfo clean matrix
|
.PHONY: all dumpinfo clean matrix
|
||||||
|
|
||||||
|
|
|
@ -133,7 +133,7 @@ namespace hazelnut {
|
||||||
blockSize = NVFBC_TOSYS_DIFFMAP_BLOCKSIZE_32X32;
|
blockSize = NVFBC_TOSYS_DIFFMAP_BLOCKSIZE_32X32;
|
||||||
|
|
||||||
// set up a session
|
// set up a session
|
||||||
NVFBC_TOSYS_SETUP_PARAMS fbcSysSetupParams{};
|
NVFBC_TOSYS_SETUP_PARAMS fbcSysSetupParams {};
|
||||||
fbcSysSetupParams.dwVersion = NVFBC_TOSYS_SETUP_PARAMS_VER;
|
fbcSysSetupParams.dwVersion = NVFBC_TOSYS_SETUP_PARAMS_VER;
|
||||||
fbcSysSetupParams.eMode = NVFBC_TOSYS_ARGB;
|
fbcSysSetupParams.eMode = NVFBC_TOSYS_ARGB;
|
||||||
fbcSysSetupParams.bWithHWCursor = true;
|
fbcSysSetupParams.bWithHWCursor = true;
|
||||||
|
@ -197,8 +197,6 @@ namespace hazelnut {
|
||||||
auto* pSrcData = (u8*)&pRawFramebuffer[0];
|
auto* pSrcData = (u8*)&pRawFramebuffer[0];
|
||||||
|
|
||||||
for(u32 y = 0; y < grabInfo.dwHeight; ++y) {
|
for(u32 y = 0; y < grabInfo.dwHeight; ++y) {
|
||||||
|
|
||||||
|
|
||||||
// Convert to BGRA
|
// Convert to BGRA
|
||||||
// FIXME: Make this SIMD. I can't into this very well
|
// FIXME: Make this SIMD. I can't into this very well
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -224,8 +222,8 @@ namespace hazelnut {
|
||||||
FramebufferInformation GetFramebufferInformation() override { return FramebufferInformation { convertedFramebuffer.data(), width, height }; }
|
FramebufferInformation GetFramebufferInformation() override { return FramebufferInformation { convertedFramebuffer.data(), width, height }; }
|
||||||
|
|
||||||
DiffInformation GetDiffInformation() override {
|
DiffInformation GetDiffInformation() override {
|
||||||
//diffmapWidth = (u32)ceil((f32)width / 32);
|
// diffmapWidth = (u32)ceil((f32)width / 32);
|
||||||
//diffmapHeight = (u32)ceil((f32)height / 32);
|
// diffmapHeight = (u32)ceil((f32)height / 32);
|
||||||
|
|
||||||
diffmapWidth = DiffMapDimension(width, blockSize);
|
diffmapWidth = DiffMapDimension(width, blockSize);
|
||||||
diffmapHeight = DiffMapDimension(height, blockSize);
|
diffmapHeight = DiffMapDimension(height, blockSize);
|
||||||
|
|
|
@ -12,17 +12,17 @@
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
|
||||||
#include "atomic_spinlock.hpp"
|
#include "atomic_spinlock.hpp"
|
||||||
|
#include "ivshmem_protocol.hpp"
|
||||||
|
|
||||||
struct tileRect {
|
struct tileRect {
|
||||||
u32 x, y, width, height;
|
u32 x, y, width, height;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Test {
|
struct Test {
|
||||||
hazelnut::AtomicSpinlock lk{};
|
hazelnut::AtomicSpinlock lk {};
|
||||||
std::atomic<u32> sessionId {};
|
std::atomic<u32> sessionId {};
|
||||||
std::atomic<u32> pingPong{};
|
std::atomic<u32> pingPong {};
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
|
@ -34,43 +34,108 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto size = dev.GetSize();
|
auto size = dev.GetSize();
|
||||||
auto ptr = (u32*)dev.GetPointer();
|
auto ptr = (u8*)dev.GetPointer();
|
||||||
|
|
||||||
printf("opened a %zu MB large ivshmem\n", (size / (1024 * 1024)));
|
printf("opened a %zu MB large ivshmem\n", (size / (1024 * 1024)));
|
||||||
|
|
||||||
// wipe the first 1mb
|
// wipe the first 1mb
|
||||||
memset(&ptr[0], 0, 1*(1024*1024));
|
memset(&ptr[0], 0, 1 * (1024 * 1024));
|
||||||
|
|
||||||
printf("wiped memory\n");
|
printf("wiped memory\n");
|
||||||
|
|
||||||
// Setup a test struct at the start of ivshmem
|
// sex
|
||||||
auto* pTest = new(&ptr[0]) Test;
|
auto* pHeader = new(&ptr[0]) hazelnut::IvshHeader {};
|
||||||
|
auto* pFrameHeader = new(&ptr[0x1000]) hazelnut::FrameHeader {};
|
||||||
|
|
||||||
// reset pingpong counter
|
// reset pingpong counter
|
||||||
pTest->pingPong.store(0);
|
|
||||||
|
|
||||||
u32 tries = 0;
|
pHeader->serverSessionId.store(rand());
|
||||||
u32 curTries = 10;
|
|
||||||
|
|
||||||
pTest->sessionId.store(rand());
|
// Create a capture interface
|
||||||
|
auto capture = hazelnut::CreateDisplayCapture(hazelnut::GuessBestCaptureInterface());
|
||||||
|
if(!capture) {
|
||||||
|
printf("Failed to create a capture interface\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Successfully created a framebuffer capture interface\n");
|
||||||
|
|
||||||
|
bool firstFrame = true;
|
||||||
|
std::vector<tileRect> tiles {};
|
||||||
|
hazelnut::FramebufferInformation framebuffer {};
|
||||||
|
hazelnut::DiffInformation diff {};
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
auto result = capture->CaptureFrame();
|
||||||
|
if(result == hazelnut::DisplayCaptureResult::Ok) {
|
||||||
|
tiles.clear();
|
||||||
|
#if 0
|
||||||
|
if(firstFrame == false) {
|
||||||
|
for(u32 y = 0; y < diff.diffMapHeight; ++y) {
|
||||||
|
for(u32 x = 0; x < diff.diffmapWidth; ++x) {
|
||||||
|
auto& bl = diff.pDiffMap[y * diff.diffmapWidth + x];
|
||||||
|
if(bl != 0) {
|
||||||
|
tiles.push_back(tileRect {
|
||||||
|
x * (framebuffer.width / diff.diffmapWidth), // x
|
||||||
|
y * (framebuffer.height / diff.diffMapHeight), // y
|
||||||
|
framebuffer.width / diff.diffmapWidth, // width
|
||||||
|
framebuffer.height / diff.diffMapHeight // height
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(tiles.empty())
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
{
|
||||||
|
auto guard = pHeader->lock.lock();
|
||||||
|
|
||||||
|
pFrameHeader->serial.fetch_add(1);
|
||||||
|
pFrameHeader->width.store(framebuffer.width);
|
||||||
|
pFrameHeader->height.store(framebuffer.height);
|
||||||
|
|
||||||
|
if(framebuffer.pFramebuffer == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
memcpy(pFrameHeader->bits(), &framebuffer.pFramebuffer[0], (framebuffer.width * framebuffer.height) * 4);
|
||||||
|
|
||||||
|
//printf("FRAME SERIAL %u loaded\n", pFrameHeader->serial.load());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(firstFrame)
|
||||||
|
firstFrame = false;
|
||||||
|
} else if(result == hazelnut::DisplayCaptureResult::OkButResized) {
|
||||||
|
// We resized. Notify of that
|
||||||
|
framebuffer = capture->GetFramebufferInformation();
|
||||||
|
diff = capture->GetDiffInformation();
|
||||||
|
firstFrame = true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
printf("Failed to capture\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
while(true) {
|
while(true) {
|
||||||
// lock
|
// lock
|
||||||
{
|
{
|
||||||
auto guard = pTest->lk.lock();
|
auto guard = pHeader->lk.lock();
|
||||||
Sleep(5);
|
Sleep(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
printf("pingpong %u\n", pTest->pingPong.load());
|
printf("pingpong %u\n", pHeader->pingPong.load());
|
||||||
|
|
||||||
if(tries++ == curTries) {
|
if(tries++ == curTries) {
|
||||||
tries = 0;
|
tries = 0;
|
||||||
pTest->pingPong.fetch_add(1);
|
pHeader->pingPong.fetch_add(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
14
build.rs
Normal file
14
build.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
use cc;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut build = cc::Build::new();
|
||||||
|
|
||||||
|
build
|
||||||
|
.emit_rerun_if_env_changed(true)
|
||||||
|
.cpp(true)
|
||||||
|
.std("c++20")
|
||||||
|
.include("shared/src")
|
||||||
|
.file("shared/src/ivshmem.cpp")
|
||||||
|
.file("src/rust_wrapper.cpp")
|
||||||
|
.compile("rust_ivshmem_bare");
|
||||||
|
}
|
|
@ -4,33 +4,35 @@
|
||||||
|
|
||||||
namespace hazelnut {
|
namespace hazelnut {
|
||||||
|
|
||||||
#pragma pack(push, 4)
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
|
|
||||||
/// Header for Hazelnut ivshmem. At 0x0 in the ivshmem memory space.
|
/// Header for Hazelnut ivshmem. At 0x0 in the ivshmem memory space.
|
||||||
struct alignas(4096) IvshHeader {
|
struct IvshHeader {
|
||||||
std::atomic<u32> magic;
|
std::atomic<u32> magic;
|
||||||
|
|
||||||
|
std::atomic<u32> serverSessionId{};
|
||||||
|
|
||||||
// When this lock is held by the host, the host can read the rest of memory freely.
|
// When this lock is held by the host, the host can read the rest of memory freely.
|
||||||
// We wait for the host to release before updating memory ourselves.
|
// We wait for the host to release before updating memory ourselves.
|
||||||
AtomicSpinlock lock;
|
AtomicSpinlock lock;
|
||||||
|
|
||||||
// See the next page boundary for FrameHeader
|
// Immediately after is FrameHeader
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Stored at a page boundary
|
/// Stored at a page boundary
|
||||||
struct alignas(4096) FrameHeader {
|
struct FrameHeader {
|
||||||
/// The serial of the frame. If this is unchanged between a
|
/// The serial of the frame. If this is unchanged between a
|
||||||
/// lock-grab-unlock cycle, the frame has not changed.
|
/// lock-grab-unlock cycle, the frame has not changed.
|
||||||
std::atomic<u32> serial;
|
std::atomic<u32> serial{};
|
||||||
|
|
||||||
// size
|
// size
|
||||||
std::atomic<u32> width;
|
std::atomic<u32> width{};
|
||||||
std::atomic<u32> height;
|
std::atomic<u32> height{};
|
||||||
|
|
||||||
// obtain bits at the next page!
|
// obtain bits at the next page boundary
|
||||||
u32* bits() {
|
u32* bits() {
|
||||||
return reinterpret_cast<u32*>(this + 1);
|
return reinterpret_cast<u32*>(reinterpret_cast<u8*>(this) + 0x1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,12 +4,10 @@
|
||||||
#include "atomic_spinlock.hpp"
|
#include "atomic_spinlock.hpp"
|
||||||
#include "ivshmem.hpp"
|
#include "ivshmem.hpp"
|
||||||
|
|
||||||
|
#include "ivshmem_protocol.hpp"
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
struct Test {
|
|
||||||
hazelnut::AtomicSpinlock lk {};
|
|
||||||
std::atomic<u32> sessionId {};
|
|
||||||
std::atomic<u32> pingPong {};
|
|
||||||
};
|
|
||||||
|
|
||||||
hazelnut::IvshmemDevice dev;
|
hazelnut::IvshmemDevice dev;
|
||||||
|
|
||||||
|
@ -19,55 +17,43 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto size = dev.GetSize();
|
auto size = dev.GetSize();
|
||||||
auto ptr = (u32*)dev.GetPointer();
|
auto ptr = (u8*)dev.GetPointer();
|
||||||
|
|
||||||
printf("opened a %zu MB large ivshmem\n", (size / (1024 * 1024)));
|
printf("opened a %zu MB large ivshmem\n", (size / (1024 * 1024)));
|
||||||
|
|
||||||
auto* pTest = (Test*)ptr;
|
auto* pHeader = (hazelnut::IvshHeader*)&ptr[0];;
|
||||||
u32 last = 0;
|
auto* pFrameHeader = (hazelnut::FrameHeader*)&ptr[0x1000];
|
||||||
u32 nrSame = 0;
|
|
||||||
|
|
||||||
u64 initalizedSessionId = pTest->sessionId.load();
|
u64 initalizedSessionId = pHeader->serverSessionId.load();
|
||||||
|
u32 lastSerial = 0;
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
//printf("%d\n", pTest->timestamp.load());
|
if(initalizedSessionId != pHeader->serverSessionId.load()) {
|
||||||
|
|
||||||
if(initalizedSessionId != pTest->sessionId.load()) {
|
|
||||||
printf("Agent restarted. Closing\n");
|
printf("Agent restarted. Closing\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// need monotonic way
|
|
||||||
#if 0
|
|
||||||
if(auto curTimestamp = pTest->timestamp.load(); curTimestamp < lastTimestamp) {
|
|
||||||
//printf("Not connected or agent crashed %d, %d\n", pTest->timestamp.load() , time(nullptr));
|
|
||||||
printf("Not connected or agent crashed. %zu\n", curTimestamp);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
lastTimestamp = curTimestamp;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// lock for a bit
|
// lock for a bit
|
||||||
{
|
{
|
||||||
if(pTest->lk.try_lock_manually()) {
|
if(pHeader->lock.try_lock_manually()) {
|
||||||
// failed to lock
|
// failed to lock
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto current = pTest->pingPong.load();
|
auto current = pFrameHeader->serial.load();
|
||||||
|
|
||||||
if(current == last) {
|
if(current == lastSerial) {
|
||||||
pTest->lk.unlock();
|
pHeader->lock.unlock();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
nrSame = 0;
|
|
||||||
|
|
||||||
last = current;
|
lastSerial = current;
|
||||||
printf("pingpong %u\n", current);
|
printf("Frame with serial %u. Width %ux%u\n", lastSerial, pFrameHeader->width.load(), pFrameHeader->height.load());
|
||||||
|
|
||||||
pTest->lk.unlock();
|
pHeader->lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
// allow the vm some time to do whatever it is it wants to do
|
// allow the vm some time to do whatever it is it wants to do
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
use std::sync::atomic::AtomicU32;
|
|
||||||
use std::sync::atomic::Ordering;
|
|
||||||
|
|
||||||
#[repr(C, packed(4))]
|
|
||||||
pub struct AtomicSpinlock {
|
|
||||||
lock: AtomicU32
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AtomicSpinlockGuard<'a> {
|
|
||||||
lock: &'a mut AtomicSpinlock,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for AtomicSpinlockGuard {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.lock.store(0, Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AtomicSpinlock {
|
|
||||||
fn init(&mut self) {
|
|
||||||
self.lock.store(0, Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn lock(&mut self) -> AtomicSpinlockGuard<'_> {
|
|
||||||
loop {
|
|
||||||
match self
|
|
||||||
.sync
|
|
||||||
.compare_exchange(cur, 1, Ordering::SeqCst, Ordering::SeqCst)
|
|
||||||
{
|
|
||||||
Ok(last) => return HzLockGuard { lock: self },
|
|
||||||
Err(_) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
89
src/hzclient.rs
Normal file
89
src/hzclient.rs
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
use std::{ffi, path::Path};
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum ResultCode {
|
||||||
|
Unchanged,
|
||||||
|
Changed,
|
||||||
|
Fail,
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn rust_new_hazelnut_client() -> *mut ffi::c_void;
|
||||||
|
fn rust_destroy_hazelnut_client(client: *mut ffi::c_void);
|
||||||
|
|
||||||
|
fn rust_hazelnut_client_open(client: *mut ffi::c_void, url: *const ffi::c_char) -> bool;
|
||||||
|
|
||||||
|
fn rust_hazelnut_client_tick(client: *mut ffi::c_void) -> ResultCode;
|
||||||
|
|
||||||
|
fn rust_hazelnut_client_lock(client: *mut ffi::c_void);
|
||||||
|
fn rust_hazelnut_client_unlock(client: *mut ffi::c_void);
|
||||||
|
|
||||||
|
fn rust_hazelnut_client_get_framebuffer(client: *mut ffi::c_void) -> *mut u32;
|
||||||
|
fn rust_hazelnut_client_get_width(client: *mut ffi::c_void) -> u32;
|
||||||
|
fn rust_hazelnut_client_get_height(client: *mut ffi::c_void) -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HazelnutClient(*mut std::ffi::c_void);
|
||||||
|
|
||||||
|
impl HazelnutClient {
|
||||||
|
pub fn new() -> HazelnutClient {
|
||||||
|
unsafe { Self(rust_new_hazelnut_client()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open(&mut self, path: String) -> bool {
|
||||||
|
let cstr = ffi::CString::new(path).expect("dumbass");
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
return rust_hazelnut_client_open(self.0, cstr.as_ptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tick_one(&mut self) -> ResultCode {
|
||||||
|
unsafe {
|
||||||
|
return rust_hazelnut_client_tick(self.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lock(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
return rust_hazelnut_client_lock(self.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unlock(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
return rust_hazelnut_client_unlock(self.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// only use while lock is held!
|
||||||
|
pub fn dimensions(&mut self) -> (u32, u32) {
|
||||||
|
let tup = unsafe {
|
||||||
|
let width = rust_hazelnut_client_get_width(self.0);
|
||||||
|
let height = rust_hazelnut_client_get_height(self.0);
|
||||||
|
(width, height)
|
||||||
|
};
|
||||||
|
tup
|
||||||
|
}
|
||||||
|
|
||||||
|
/// only use while lock is held!
|
||||||
|
pub fn framebuffer(&mut self) -> &mut [u32] {
|
||||||
|
let sl = unsafe {
|
||||||
|
let fb_ptr = rust_hazelnut_client_get_framebuffer(self.0);
|
||||||
|
let dim = self.dimensions();
|
||||||
|
|
||||||
|
std::slice::from_raw_parts_mut(fb_ptr, ((dim.0 * dim.1) as usize * 4))
|
||||||
|
};
|
||||||
|
sl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for HazelnutClient {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
rust_destroy_hazelnut_client(self.0);
|
||||||
|
}
|
||||||
|
self.0 = std::ptr::null_mut();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,30 +0,0 @@
|
||||||
//! Utilities for ivshmem
|
|
||||||
|
|
||||||
use std::sync::atomic::AtomicU32;
|
|
||||||
use std::sync::atomic::Ordering;
|
|
||||||
|
|
||||||
use crate::atomic_spinlock::AtomicSpinlock;
|
|
||||||
|
|
||||||
// at 0x0 in ivshmem map
|
|
||||||
#[repr(C, align(4096))]
|
|
||||||
pub struct HzHeader {
|
|
||||||
magic: AtomicU32,
|
|
||||||
sync: AtomicSpinlock,
|
|
||||||
|
|
||||||
// frame serial. if this is unchanged from prev. load it's not the same
|
|
||||||
frame_serial: AtomicU32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HzHeader {
|
|
||||||
fn valid(&self) -> bool {
|
|
||||||
self.magic.load(std::sync::atomic::Ordering::Acquire) == 0xabcd1234
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct HzFrameHeader {
|
|
||||||
width: AtomicU32,
|
|
||||||
height: AtomicU32,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Ivshmem {}
|
|
152
src/main.rs
152
src/main.rs
|
@ -1,139 +1,57 @@
|
||||||
use std::{
|
|
||||||
io::Read,
|
|
||||||
net::{TcpListener, TcpStream},
|
|
||||||
};
|
|
||||||
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt};
|
|
||||||
use minifb::{Window, WindowOptions};
|
use minifb::{Window, WindowOptions};
|
||||||
|
|
||||||
mod atomic_spinlock;
|
mod hzclient;
|
||||||
mod ivshmem;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum Message {
|
|
||||||
Resize { width: u32, height: u32 },
|
|
||||||
|
|
||||||
Data {},
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MESSAGETYPE_RESIZE: u32 = 0;
|
|
||||||
pub const MESSAGETYPE_DATA: u32 = 1;
|
|
||||||
|
|
||||||
fn read_message(
|
|
||||||
stream: &mut TcpStream,
|
|
||||||
argb_buffer: &mut Vec<u32>,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
) -> Option<Message> {
|
|
||||||
let message_type = stream.read_u32::<LittleEndian>().expect("fuck");
|
|
||||||
let message_len = stream.read_u32::<LittleEndian>().expect("fuck");
|
|
||||||
|
|
||||||
match message_type {
|
|
||||||
MESSAGETYPE_RESIZE => {
|
|
||||||
if message_len != 8 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let width = stream.read_u32::<LittleEndian>().expect("fuck");
|
|
||||||
let height = stream.read_u32::<LittleEndian>().expect("fuck");
|
|
||||||
Some(Message::Resize {
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
MESSAGETYPE_DATA => {
|
|
||||||
let tile_count = stream.read_u32::<LittleEndian>().expect("fuck");
|
|
||||||
|
|
||||||
//println!("{tile_count} tiles");
|
|
||||||
|
|
||||||
// tile data, painted directly onto the argb buffer. It's stupid
|
|
||||||
let argb_slice = unsafe {
|
|
||||||
std::slice::from_raw_parts_mut(
|
|
||||||
argb_buffer.as_mut_ptr() as *mut u8,
|
|
||||||
argb_buffer.len() * core::mem::size_of::<u32>(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
for i in 0..tile_count {
|
|
||||||
// tile rect
|
|
||||||
let tile_x = stream.read_u32::<LittleEndian>().expect("fuck");
|
|
||||||
let tile_y = stream.read_u32::<LittleEndian>().expect("fuck");
|
|
||||||
let tile_width = stream.read_u32::<LittleEndian>().expect("fuck");
|
|
||||||
let tile_height = stream.read_u32::<LittleEndian>().expect("fuck");
|
|
||||||
|
|
||||||
//println!("tile{i}: {tile_x} {tile_y} {tile_width}x{tile_height}");
|
|
||||||
}
|
|
||||||
|
|
||||||
for y in 0..height {
|
|
||||||
//println!("{y} {tile_y} {tile_height}");
|
|
||||||
//for x in tile_
|
|
||||||
let dest_slice = &mut argb_slice
|
|
||||||
[((y * width) * 4) as usize..((y * width + (width)) * 4) as usize];
|
|
||||||
|
|
||||||
stream.read_exact(&mut dest_slice[..]).expect("FUCK");
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(Message::Data {})
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let listener = TcpListener::bind("192.168.1.149:9438").expect("fuck");
|
let mut screen_width: u32 = 320;
|
||||||
let (mut socket, client_addr) = listener.accept().expect("FUCK!");
|
let mut screen_height: u32 = 200;
|
||||||
|
|
||||||
// disable nagles garbage bullshit
|
|
||||||
socket.set_nodelay(true).expect("fuck tcp");
|
|
||||||
|
|
||||||
let mut window = Window::new("FbcServer", 320, 200, WindowOptions::default())
|
let mut window = Window::new("FbcServer", 320, 200, WindowOptions::default())
|
||||||
.expect("you banned forever: rules do not");
|
.expect("you banned forever: rules do not");
|
||||||
|
|
||||||
let mut argb_buffer: Vec<u32> = Vec::new();
|
let mut client = hzclient::HazelnutClient::new();
|
||||||
let mut screen_width: u32 = 320;
|
|
||||||
let mut screen_height: u32 = 200;
|
if !client.open("/dev/shm/lg-win7".into()) {
|
||||||
|
println!("FUCK");
|
||||||
|
} else {
|
||||||
|
println!("Opened ivshmem!!!");
|
||||||
|
}
|
||||||
|
|
||||||
|
//let mut argb_buffer: Vec<u32> = Vec::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if let Some(message) =
|
match client.tick_one() {
|
||||||
read_message(&mut socket, &mut argb_buffer, screen_width, screen_height)
|
hzclient::ResultCode::Fail => break,
|
||||||
{
|
hzclient::ResultCode::Changed => {
|
||||||
match message {
|
let dims = client.dimensions();
|
||||||
Message::Resize { width, height } => {
|
|
||||||
println!("read message {:?}", message);
|
if screen_width != dims.0 && screen_height != dims.1 {
|
||||||
screen_width = width;
|
screen_width = dims.0;
|
||||||
screen_height = height;
|
screen_height = dims.1;
|
||||||
|
|
||||||
window = Window::new(
|
window = Window::new(
|
||||||
"FbcServer",
|
"FbcServer",
|
||||||
width as usize,
|
screen_width as usize,
|
||||||
height as usize,
|
screen_height as usize,
|
||||||
WindowOptions::default(),
|
WindowOptions::default(),
|
||||||
)
|
)
|
||||||
.expect("you banned forever: rules do not");
|
.expect("you banned forever: rules do not");
|
||||||
|
}
|
||||||
|
|
||||||
argb_buffer.resize((screen_width * screen_height) as usize, 0);
|
window.update_with_buffer(
|
||||||
}
|
&client.framebuffer(),
|
||||||
Message::Data {} => {
|
screen_width as usize,
|
||||||
//println!("read DATA");
|
screen_height as usize,
|
||||||
window
|
).expect("well its done anyways");
|
||||||
.update_with_buffer(
|
|
||||||
&argb_buffer[..],
|
client.unlock();
|
||||||
screen_width as usize,
|
}
|
||||||
screen_height as usize,
|
hzclient::ResultCode::Unchanged => {
|
||||||
)
|
window.update();
|
||||||
.expect("Failed to update screen");
|
// Not needed, C++ unlocks us
|
||||||
}
|
//client.unlock();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
println!("invalid message, termination.");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//window.upd
|
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Hello, world!");
|
println!("Hello, world!");
|
||||||
|
|
130
src/rust_wrapper.cpp
Normal file
130
src/rust_wrapper.cpp
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
#include "ivshmem.hpp"
|
||||||
|
#include "ivshmem_protocol.hpp"
|
||||||
|
#include "Utils.hpp"
|
||||||
|
|
||||||
|
enum class ResultCode : u32 { Unchanged, Changed, Fail };
|
||||||
|
|
||||||
|
struct HazelnutIvshmemClient {
|
||||||
|
bool Open(const char* path) {
|
||||||
|
if(!ivshmemDevice.Open(path))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto* ptr = (u8*)ivshmemDevice.GetPointer();
|
||||||
|
|
||||||
|
pHeader = (hazelnut::IvshHeader*)&ptr[0];
|
||||||
|
pFrameHeader = (hazelnut::FrameHeader*)&ptr[0x1000];
|
||||||
|
|
||||||
|
serial = pHeader->serverSessionId.load();
|
||||||
|
lastFrameSerial = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS LEAVES THE LOCK HELD SO YOU CAN READ ON CHANGE!!!!
|
||||||
|
ResultCode TickOne() {
|
||||||
|
if(!pHeader)
|
||||||
|
return ResultCode::Fail;
|
||||||
|
|
||||||
|
if(serial != pHeader->serverSessionId.load())
|
||||||
|
return ResultCode::Fail;
|
||||||
|
|
||||||
|
if(pHeader->lock.try_lock_manually()) {
|
||||||
|
// failed to lock
|
||||||
|
return ResultCode::Unchanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto current = pFrameHeader->serial.load();
|
||||||
|
|
||||||
|
if(current == lastFrameSerial) {
|
||||||
|
pHeader->lock.unlock();
|
||||||
|
return ResultCode::Unchanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastFrameSerial = current;
|
||||||
|
// printf("Frame with serial %u. Width %ux%u\n", lastSerial, pFrameHeader->width.load(), pFrameHeader->height.load());
|
||||||
|
// pHeader->lock.unlock();
|
||||||
|
return ResultCode::Changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lock() { pHeader->lock.lock_manually(); }
|
||||||
|
|
||||||
|
void Unlock() { pHeader->lock.unlock(); }
|
||||||
|
|
||||||
|
u32* Framebuffer() {
|
||||||
|
if(pFrameHeader) {
|
||||||
|
return pFrameHeader->bits();
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
hazelnut::IvshmemDevice ivshmemDevice;
|
||||||
|
|
||||||
|
// Protocol/memory stuff
|
||||||
|
hazelnut::IvshHeader* pHeader;
|
||||||
|
hazelnut::FrameHeader* pFrameHeader;
|
||||||
|
|
||||||
|
u32 serial {};
|
||||||
|
|
||||||
|
u32 lastFrameSerial {};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Rust bindings
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
void* rust_new_hazelnut_client() {
|
||||||
|
return (void*)new HazelnutIvshmemClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rust_destroy_hazelnut_client(void* pClient) {
|
||||||
|
if(pClient)
|
||||||
|
delete(HazelnutIvshmemClient*)pClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rust_hazelnut_client_open(void* pClient, const char* pszPath) {
|
||||||
|
if(pClient) {
|
||||||
|
return ((HazelnutIvshmemClient*)pClient)->Open(pszPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rust_hazelnut_client_tick(void* pClient) {
|
||||||
|
if(pClient) {
|
||||||
|
return (int)((HazelnutIvshmemClient*)pClient)->TickOne();
|
||||||
|
}
|
||||||
|
return (int)ResultCode::Fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rust_hazelnut_client_lock(void* pClient) {
|
||||||
|
if(pClient) {
|
||||||
|
return ((HazelnutIvshmemClient*)pClient)->Lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rust_hazelnut_client_unlock(void* pClient) {
|
||||||
|
if(pClient) {
|
||||||
|
return ((HazelnutIvshmemClient*)pClient)->Unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u32* rust_hazelnut_client_get_framebuffer(void* pClient) {
|
||||||
|
if(pClient) {
|
||||||
|
return ((HazelnutIvshmemClient*)pClient)->Framebuffer();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 rust_hazelnut_client_get_width(void* pClient) {
|
||||||
|
if(pClient) {
|
||||||
|
return ((HazelnutIvshmemClient*)pClient)->pFrameHeader->width.load();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 rust_hazelnut_client_get_height(void* pClient) {
|
||||||
|
if(pClient) {
|
||||||
|
return ((HazelnutIvshmemClient*)pClient)->pFrameHeader->height.load();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue