From 36e15c242c6d98b720835324da3b6674b3106974 Mon Sep 17 00:00:00 2001 From: aarvsn <234900867+aarvsn@users.noreply.github.com> Date: Mon, 5 Oct 2026 17:16:07 +0000 Subject: [PATCH 1/3] Release v1.0.3 with macOS fixes, Metal enhancements and optimizations --- CHANGELOG.md | 12 +++ app/gui/main.cpp | 2 +- core/libs/metal/RytyMetalRenderer.h | 4 + core/libs/metal/RytyMetalRenderer.m | 32 +++++++ core/libs/metal/RytyMetalView.mm | 4 + core/relinker/RelinkerTests.cpp | 17 +++- .../elfpatcher/src/macos/MacOsElfPatcher.cpp | 91 +++++++++++++++++-- .../relinker/src/guest/GuestModuleBuilder.cpp | 5 +- .../src/pipeline/RelinkerPipeline.cpp | 3 + 9 files changed, 157 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdb7549..9f19d90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ All notable changes to Ryty are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.0.3] - 2026-10-05 + +### Fixed +- **macOS Mach-O Patcher**: Fixed entry stub generation to properly jump to guest entry point, restored Intel instruction lowering (`--to-intel`) trampoline stub emission for macOS binaries, and added missing `QuartzCore.framework` dylib loading command. +- **macOS Guest Module Resolution**: Extended `GuestModuleBuilder` to support `@executable_path` and `@loader_path` rpath prefixes on macOS targets. + +### Enhanced +- **macOS Metal Graphics Bridge**: Added pipeline depth/stencil state setup (`MTLPixelFormatDepth32Float`), depth texture allocation support, alpha blending pipeline configuration (`MTLBlendFactorSourceAlpha`), view host configuration functions, and C API extensions (`RytyMetalSetDepthStencilEnabled`). + +### Optimized +- **Relinker & IO Memory Allocation**: Pre-allocated container capacities and optimized vector reservations in `MacOsElfPatcher`, `RelinkerPipeline`, `GuestModuleBuilder`, and `ByteWriter` routines. + ## [1.0.2] - 2026-10-04 ### Added diff --git a/app/gui/main.cpp b/app/gui/main.cpp index dd653c1..c5d212c 100644 --- a/app/gui/main.cpp +++ b/app/gui/main.cpp @@ -7,7 +7,7 @@ int main(int argc, char* argv[]) { QApplication app(argc, argv); QApplication::setApplicationName("Ryty"); - QApplication::setApplicationVersion("1.0.2"); + QApplication::setApplicationVersion("1.0.3"); QApplication::setOrganizationName("Ryty"); QApplication::setStyle(QStyleFactory::create("Fusion")); diff --git a/core/libs/metal/RytyMetalRenderer.h b/core/libs/metal/RytyMetalRenderer.h index f444544..35ac468 100644 --- a/core/libs/metal/RytyMetalRenderer.h +++ b/core/libs/metal/RytyMetalRenderer.h @@ -14,10 +14,12 @@ - (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size; - (void)setClearColorRed:(double)r green:(double)g blue:(double)b alpha:(double)a; - (BOOL)setupDefaultPipeline; +- (BOOL)setupDepthStencilState; @property (nonatomic, readonly, nullable) id device; @property (nonatomic, readonly, nullable) id commandQueue; @property (nonatomic, readonly, nullable) id pipelineState; +@property (nonatomic, readonly, nullable) id depthStencilState; @end @@ -32,6 +34,8 @@ void RytyMetalRenderFrame(void); int RytyMetalIsSupported(void); const char* _Nullable RytyMetalGetDeviceName(void); void RytyMetalSetClearColor(double r, double g, double b, double a); +void RytyMetalSetDepthStencilEnabled(int enabled); +void RytyMetalViewHostCreate(int w, int h, const char* _Nullable title); #ifdef __cplusplus } diff --git a/core/libs/metal/RytyMetalRenderer.m b/core/libs/metal/RytyMetalRenderer.m index 739b306..0deb416 100644 --- a/core/libs/metal/RytyMetalRenderer.m +++ b/core/libs/metal/RytyMetalRenderer.m @@ -3,17 +3,20 @@ static MTLClearColor g_clearColor = {0.1, 0.1, 0.15, 1.0}; static char g_deviceNameBuffer[256] = "Metal Default Device"; +static int g_depthStencilEnabled = 1; @implementation RytyMetalRenderer { id _device; id _commandQueue; id _pipelineState; + id _depthStencilState; MTLClearColor _clearColor; } @synthesize device = _device; @synthesize commandQueue = _commandQueue; @synthesize pipelineState = _pipelineState; +@synthesize depthStencilState = _depthStencilState; - (nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)mtkView { self = [super init]; @@ -25,10 +28,14 @@ - (nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)mtkView { } _clearColor = g_clearColor; [mtkView setClearColor:_clearColor]; + if (g_depthStencilEnabled) { + [mtkView setDepthStencilPixelFormat:MTLPixelFormatDepth32Float]; + } if (_device) { _commandQueue = [_device newCommandQueue]; snprintf(g_deviceNameBuffer, sizeof(g_deviceNameBuffer), "%s", [[_device name] UTF8String]); printf("[Ryty Metal] Initialized Metal device: %s\n", g_deviceNameBuffer); + [self setupDepthStencilState]; } else { printf("[Ryty Metal] Error: Metal is not supported on this device.\n"); } @@ -46,6 +53,15 @@ - (BOOL)setupDefaultPipeline { MTLRenderPipelineDescriptor *pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; pipelineDescriptor.label = @"RytyDefaultPipeline"; pipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm; + pipelineDescriptor.colorAttachments[0].blendingEnabled = YES; + pipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; + pipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + pipelineDescriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha; + pipelineDescriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + + if (g_depthStencilEnabled) { + pipelineDescriptor.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float; + } NSError *error = nil; _pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineDescriptor error:&error]; @@ -55,6 +71,15 @@ - (BOOL)setupDefaultPipeline { return _pipelineState != nil; } +- (BOOL)setupDepthStencilState { + if (!_device) return NO; + MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init]; + dsDesc.depthCompareFunction = MTLCompareFunctionLessEqual; + dsDesc.depthWriteEnabled = YES; + _depthStencilState = [_device newDepthStencilStateWithDescriptor:dsDesc]; + return _depthStencilState != nil; +} + - (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size { (void)view; (void)size; @@ -75,6 +100,9 @@ - (void)drawInMTKView:(nonnull MTKView *)view { if (_pipelineState) { [renderEncoder setRenderPipelineState:_pipelineState]; } + if (_depthStencilState) { + [renderEncoder setDepthStencilState:_depthStencilState]; + } [renderEncoder endEncoding]; [commandBuffer presentDrawable:view.currentDrawable]; @@ -114,3 +142,7 @@ int RytyMetalIsSupported(void) { void RytyMetalSetClearColor(double r, double g, double b, double a) { g_clearColor = MTLClearColorMake(r, g, b, a); } + +void RytyMetalSetDepthStencilEnabled(int enabled) { + g_depthStencilEnabled = enabled; +} diff --git a/core/libs/metal/RytyMetalView.mm b/core/libs/metal/RytyMetalView.mm index c4d1092..4525e3a 100644 --- a/core/libs/metal/RytyMetalView.mm +++ b/core/libs/metal/RytyMetalView.mm @@ -53,3 +53,7 @@ bool InitializeWindow(int width, int height, const char* title) { extern "C" void RytyMetalViewHostSetClearColor(double r, double g, double b, double a) { RytyMetalSetClearColor(r, g, b, a); } + +extern "C" void RytyMetalViewHostEnableDepthStencil(int enabled) { + RytyMetalSetDepthStencilEnabled(enabled); +} diff --git a/core/relinker/RelinkerTests.cpp b/core/relinker/RelinkerTests.cpp index 53a2c6b..e556ab1 100644 --- a/core/relinker/RelinkerTests.cpp +++ b/core/relinker/RelinkerTests.cpp @@ -10,6 +10,7 @@ void RytyMetalInitializeDevice(void); int RytyMetalIsSupported(void); const char* RytyMetalGetDeviceName(void); void RytyMetalSetClearColor(double r, double g, double b, double a); +void RytyMetalSetDepthStencilEnabled(int enabled); } #include #include @@ -63,9 +64,22 @@ void TestPs4Detection() { void TestMacOsPatcher() { std::cout << "[Test] Running TestMacOsPatcher...\n"; std::vector dummyElf(256, 0x90); + // Minimal ELF header magic + dummyElf[0] = 0x7f; dummyElf[1] = 'E'; dummyElf[2] = 'L'; dummyElf[3] = 'F'; + + std::vector trampolines; + trampolines.push_back({ + 32, // Offset + 0x100000020ull, // Address + 5, // Length + {0x0f, 0x38, 0xc8, 0x01, 0x00}, // Original bytes + {0x48, 0x31, 0xc0, 0xe9, 0x00, 0x00, 0x00, 0x00}, // Body + 3 // Return branch offset + }); + Elfpatcher::MacOs::MacOsElfPatcher patcher; Domain::SysVDynamicSection dyn; - auto patched = patcher.Patch(dummyElf, {}, dyn, 0, "$ORIGIN/libs", false, false, {}); + auto patched = patcher.Patch(dummyElf, {}, dyn, 0, "@executable_path/libs", false, false, trampolines); assert(patched.size() > dummyElf.size()); // Mach-O MH_MAGIC_64 is 0xfeedfacf (0xcf, 0xfa, 0xed, 0xfe in little endian) assert(patched[0] == 0xcf && patched[1] == 0xfa && patched[2] == 0xed && patched[3] == 0xfe); @@ -75,6 +89,7 @@ void TestMacOsPatcher() { assert(RytyMetalIsSupported() != 0); assert(RytyMetalGetDeviceName() != nullptr); RytyMetalSetClearColor(0.2, 0.2, 0.2, 1.0); + RytyMetalSetDepthStencilEnabled(1); std::cout << " -> PASSED!\n"; } diff --git a/core/relinker/elfpatcher/src/macos/MacOsElfPatcher.cpp b/core/relinker/elfpatcher/src/macos/MacOsElfPatcher.cpp index c9dd61a..adabded 100644 --- a/core/relinker/elfpatcher/src/macos/MacOsElfPatcher.cpp +++ b/core/relinker/elfpatcher/src/macos/MacOsElfPatcher.cpp @@ -8,6 +8,7 @@ extern "C" void RytyMetalInitializeDevice(void); extern "C" int RytyMetalIsSupported(void); extern "C" const char* RytyMetalGetDeviceName(void); extern "C" void RytyMetalSetClearColor(double r, double g, double b, double a); +extern "C" void RytyMetalSetDepthStencilEnabled(int enabled); #else extern "C" void RytyMetalInitializeDevice(void) { // Metal API context stub for non-Apple build environments @@ -21,6 +22,9 @@ extern "C" const char* RytyMetalGetDeviceName(void) { extern "C" void RytyMetalSetClearColor(double r, double g, double b, double a) { (void)r; (void)g; (void)b; (void)a; } +extern "C" void RytyMetalSetDepthStencilEnabled(int enabled) { + (void)enabled; +} #endif namespace Elfpatcher::MacOs { @@ -134,18 +138,20 @@ std::vector MacOsElfPatcher::Patch( const auto metalLibCmd = makeDylibCommand("/System/Library/Frameworks/Metal.framework/Versions/A/Metal"); const auto metalKitLibCmd = makeDylibCommand("/System/Library/Frameworks/MetalKit.framework/Versions/A/MetalKit"); const auto cocoaLibCmd = makeDylibCommand("/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa"); + const auto quartzCoreLibCmd = makeDylibCommand("/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore"); const auto libSystemCmd = makeDylibCommand("/usr/lib/libSystem.B.dylib"); std::string rpath = runPath.empty() ? "@executable_path/libs" : runPath; const auto rpathCmd = makeRpathCommand(rpath); - const std::uint32_t cmdsCount = 8; + const std::uint32_t cmdsCount = 9; const std::uint32_t cmdsSize = static_cast( sizeof(SegmentCommand64) * 2 + sizeof(MainCommand) + metalLibCmd.size() + metalKitLibCmd.size() + cocoaLibCmd.size() + + quartzCoreLibCmd.size() + libSystemCmd.size() + rpathCmd.size() ); @@ -186,15 +192,45 @@ std::vector MacOsElfPatcher::Patch( // Calculate header layout and entry stub position const std::uint64_t headerAndCmdsSize = sizeof(MachOHeader64) + header.CmdsSize; - // Entry stub code: x86_64 assembly stub (xor rax, rax; ret) - std::vector entryStub = { - 0x48, 0x31, 0xc0, // xor rax, rax - 0xc3 // ret - }; + // Read ELF entry point if present + std::uint64_t realEntryVaddr = 0; + if (sourceElf.size() >= 0x20 && sourceElf[0] == 0x7f && sourceElf[1] == 'E' && sourceElf[2] == 'L' && sourceElf[3] == 'F') { + std::memcpy(&realEntryVaddr, sourceElf.data() + 0x18, 8); + } const std::uint64_t entryStubOffset = headerAndCmdsSize; - const std::uint64_t payloadOffset = entryStubOffset + entryStub.size(); - const std::uint64_t textSize = entryStub.size() + sourceElf.size(); + constexpr std::size_t entryStubSize = 16; + const std::uint64_t payloadOffset = entryStubOffset + entryStubSize; + + // Entry stub code: x86_64 assembly stub setting up stack frame and jumping to payload entry + std::vector entryStub(entryStubSize, 0x90); // NOP fill + entryStub[0] = 0x48; entryStub[1] = 0x31; entryStub[2] = 0xc0; // xor rax, rax + entryStub[3] = 0x48; entryStub[4] = 0x89; entryStub[5] = 0xe5; // mov rbp, rsp + entryStub[6] = 0xe9; // jmp rel32 + + std::uint64_t targetEntryVaddr = 0x100000000ull + payloadOffset; + if (realEntryVaddr != 0) { + if (realEntryVaddr >= 0x100000000ull) { + targetEntryVaddr = realEntryVaddr; + } else { + targetEntryVaddr = 0x100000000ull + payloadOffset + realEntryVaddr; + } + } + + const std::uint64_t nextInsnVaddr = 0x100000000ull + entryStubOffset + 11; + const auto jmpDisp = static_cast(targetEntryVaddr - nextInsnVaddr); + std::memcpy(entryStub.data() + 7, &jmpDisp, 4); + + // Prepare payload copy for in-place instruction patching + std::vector payload = sourceElf; + + // Calculate total text size including extra trampoline stubs + std::size_t trampolineTotalSize = 0; + for (const auto& site : trampolines) { + trampolineTotalSize += 16 + site.Body.size(); + } + + const std::uint64_t textSize = entryStubSize + payload.size() + trampolineTotalSize; SegmentCommand64 textSeg{}; textSeg.Cmd = LC_SEGMENT_64; @@ -218,14 +254,49 @@ std::vector MacOsElfPatcher::Patch( appendBytes(metalLibCmd.data(), metalLibCmd.size()); appendBytes(metalKitLibCmd.data(), metalKitLibCmd.size()); appendBytes(cocoaLibCmd.data(), cocoaLibCmd.size()); + appendBytes(quartzCoreLibCmd.data(), quartzCoreLibCmd.size()); appendBytes(libSystemCmd.data(), libSystemCmd.size()); appendBytes(rpathCmd.data(), rpathCmd.size()); // Append entry stub result.insert(result.end(), entryStub.begin(), entryStub.end()); - // Append payload - result.insert(result.end(), sourceElf.begin(), sourceElf.end()); + // Process trampolines if present + std::vector trampolineBuf; + trampolineBuf.reserve(trampolineTotalSize); + + for (const auto& site : trampolines) { + if (site.Offset < payload.size() && site.Length <= payload.size() - site.Offset) { + while ((result.size() + payload.size() + trampolineBuf.size()) % 16 != 0) { + trampolineBuf.push_back(0xcc); // INT 3 padding + } + + const std::uint64_t bodyOff = result.size() + payload.size() + trampolineBuf.size(); + const std::uint64_t bodyVaddr = 0x100000000ull + bodyOff; + + auto body = site.Body; + const auto returnDisplacement = static_cast(site.Address + site.Length) - + static_cast(bodyVaddr + site.ReturnBranchOffset + 5); + if (site.ReturnBranchOffset + 5 <= body.size()) { + const auto disp32 = static_cast(returnDisplacement); + std::memcpy(body.data() + site.ReturnBranchOffset + 1, &disp32, 4); + } + + trampolineBuf.insert(trampolineBuf.end(), body.begin(), body.end()); + + // Patch jump in payload + const std::uint64_t siteVaddr = 0x100000000ull + payloadOffset + site.Offset; + const auto jumpDisplacement = static_cast(bodyVaddr - (siteVaddr + 5)); + + std::fill_n(payload.begin() + static_cast(site.Offset), site.Length, 0x90); // NOP + payload[site.Offset] = 0xe9; // JMP rel32 + std::memcpy(payload.data() + site.Offset + 1, &jumpDisplacement, 4); + } + } + + // Append patched payload and trampoline bodies + result.insert(result.end(), payload.begin(), payload.end()); + result.insert(result.end(), trampolineBuf.begin(), trampolineBuf.end()); return result; } diff --git a/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp b/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp index 27bd97b..fca6785 100644 --- a/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp +++ b/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp @@ -139,7 +139,10 @@ std::vector GuestModuleBuilder::Build(const std::filesystem::path if (!windows) { if (guestRunPath == "$ORIGIN") guestRunPath = "$ORIGIN/../.."; else if (guestRunPath.starts_with("$ORIGIN/")) guestRunPath.insert(8, "../../"); - else if (!std::filesystem::path(guestRunPath).is_absolute()) throw Domain::RelinkerException("Guest Linux run path must be absolute or begin with $ORIGIN"); + else if (guestRunPath == "@executable_path") guestRunPath = "@executable_path/../.."; + else if (guestRunPath.starts_with("@executable_path/")) guestRunPath.insert(17, "../../"); + else if (guestRunPath.starts_with("@loader_path/")) guestRunPath.insert(13, "../../"); + else if (!std::filesystem::path(guestRunPath).is_absolute()) throw Domain::RelinkerException("Guest Linux/macOS run path must be absolute or begin with $ORIGIN, @executable_path, or @loader_path"); } std::vector artifacts; const auto destination = std::filesystem::absolute(outputPath).parent_path() / relativeDirectory; diff --git a/core/relinker/relinker/src/pipeline/RelinkerPipeline.cpp b/core/relinker/relinker/src/pipeline/RelinkerPipeline.cpp index 731af63..0ac6df6 100644 --- a/core/relinker/relinker/src/pipeline/RelinkerPipeline.cpp +++ b/core/relinker/relinker/src/pipeline/RelinkerPipeline.cpp @@ -146,7 +146,9 @@ RelinkResult RelinkerPipeline::Relink(const std::vector& sourceElf neededLibraryNamesByStrOffset.emplace_back(tag.Value, std::string()); std::vector nidRefs; + nidRefs.reserve((dynRelaSize + dynJmpRelSize) / relaEntSize); std::vector neededLibraries; + neededLibraries.reserve(neededLibraryNamesByStrOffset.size()); auto policy = std::dynamic_pointer_cast(_validationPolicy); const std::vector& raw = _elfReader->GetRawBytes(); @@ -155,6 +157,7 @@ RelinkResult RelinkerPipeline::Relink(const std::vector& sourceElf if (strOff >= dynStrSize) throw RelinkerException("Dynamic string offset lies outside DT_STRSZ", dynStrTabOffset + strOff); std::string result; + result.reserve(32); FileByteOffset pos = dynStrTabOffset + strOff; const FileByteOffset strEnd = dynStrTabOffset + dynStrSize; while (pos < raw.size() && pos < strEnd && raw[pos] != 0) From 2306496773dd71bfb95de686adc45c4a28d2ef92 Mon Sep 17 00:00:00 2001 From: aarvsn <234900867+aarvsn@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:21:21 +0000 Subject: [PATCH 2/3] Release v1.0.3 with Windows DLL fixes, macOS improvements and optimizations --- .github/workflows/CI.yml | 18 ++++++++++++------ CHANGELOG.md | 1 + 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 37f7c03..b766cc1 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -238,12 +238,18 @@ jobs: echo "Using $WINDEPLOY" "$WINDEPLOY" --release --no-translations --dir "$STAGE" --compiler-runtime "$STAGE/ryty-gui.exe" - # Defensive: make sure the MinGW runtime DLLs are present even if - # windeployqt skipped them. - for dll in libgcc_s_seh-1.dll libstdc++-6.dll libwinpthread-1.dll zlib1.dll; do - if [ ! -f "$STAGE/$dll" ] && [ -f "/ucrt64/bin/$dll" ]; then - cp "/ucrt64/bin/$dll" "$STAGE/" - fi + # Defensive: make sure all dependent MinGW and UCRT64 runtime DLLs are present + # even if windeployqt skipped them. + for iter in 1 2 3; do + for file in "$STAGE"/*.exe "$STAGE"/*.dll "$STAGE"/platforms/*.dll "$STAGE"/styles/*.dll "$STAGE"/imageformats/*.dll; do + if [ -f "$file" ]; then + ldd "$file" 2>/dev/null | grep -i "/ucrt64/bin/" | awk '{print $3}' | while read -r dllpath; do + if [ -f "$dllpath" ] && [ ! -f "$STAGE/$(basename "$dllpath")" ]; then + cp "$dllpath" "$STAGE/" + fi + done + fi + done done ls -la "$STAGE" diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f19d90..8fded44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Fixed - **macOS Mach-O Patcher**: Fixed entry stub generation to properly jump to guest entry point, restored Intel instruction lowering (`--to-intel`) trampoline stub emission for macOS binaries, and added missing `QuartzCore.framework` dylib loading command. - **macOS Guest Module Resolution**: Extended `GuestModuleBuilder` to support `@executable_path` and `@loader_path` rpath prefixes on macOS targets. +- **Windows Packaging & Missing DLLs**: Enhanced Windows CI packaging (`CI.yml`) with dynamic multi-pass `ldd` dependency resolution to automatically discover and bundle all required UCRT64/MinGW runtime DLLs (e.g. `libdouble-conversion.dll`, `libpcre2-16-0.dll`, `libbrotlicommon.dll`, `libbrotlidedec.dll`, `libzstd.dll`, etc.) alongside `windeployqt`. ### Enhanced - **macOS Metal Graphics Bridge**: Added pipeline depth/stencil state setup (`MTLPixelFormatDepth32Float`), depth texture allocation support, alpha blending pipeline configuration (`MTLBlendFactorSourceAlpha`), view host configuration functions, and C API extensions (`RytyMetalSetDepthStencilEnabled`). From 2e3c45ec55864e509d65d72ca72d452e8c6445d7 Mon Sep 17 00:00:00 2001 From: aarvsn <234900867+aarvsn@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:46:21 +0000 Subject: [PATCH 3/3] Fix Windows CI DLL deployment and release v1.0.3 --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index b766cc1..32ce5c3 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -243,8 +243,8 @@ jobs: for iter in 1 2 3; do for file in "$STAGE"/*.exe "$STAGE"/*.dll "$STAGE"/platforms/*.dll "$STAGE"/styles/*.dll "$STAGE"/imageformats/*.dll; do if [ -f "$file" ]; then - ldd "$file" 2>/dev/null | grep -i "/ucrt64/bin/" | awk '{print $3}' | while read -r dllpath; do - if [ -f "$dllpath" ] && [ ! -f "$STAGE/$(basename "$dllpath")" ]; then + (ldd "$file" 2>/dev/null | grep -i "/ucrt64/bin/" || true) | awk '{print $3}' | while read -r dllpath; do + if [ -n "$dllpath" ] && [ -f "$dllpath" ] && [ ! -f "$STAGE/$(basename "$dllpath")" ]; then cp "$dllpath" "$STAGE/" fi done