From 202d2fd95361e479e42df5812e61867d9aa0227a Mon Sep 17 00:00:00 2001 From: Zach Date: Sun, 8 Mar 2026 16:29:59 +0800 Subject: [PATCH 01/38] [feat]: Image Resizer && fix test compilation --- .../include/animation/core/AnimationClip.h | 8 +- engine/animation/test/AnimationTest.cpp | 1 + .../core/archive/MemoryStreamArchive.h | 163 +++++++++++++ .../adaptor/src/assets/AnimationAsset.cpp | 2 +- .../include/builder/render/ImageBuilder.h | 2 + .../builder/render/image/ImageResizer.h | 29 +++ .../builder/render/src/ImageBuilder.cpp | 18 +- .../builder/render/src/image/ImageResizer.cpp | 224 ++++++++++++++++++ engine/render/builder/test/BuilderTest.cpp | 185 +++++++++++++-- engine/test/framework/AssetManagerTest.cpp | 6 +- engine/test/render/StaticMeshTest.cpp | 29 ++- 11 files changed, 619 insertions(+), 48 deletions(-) create mode 100644 engine/core/include/core/archive/MemoryStreamArchive.h create mode 100644 engine/render/builder/render/include/builder/render/image/ImageResizer.h create mode 100644 engine/render/builder/render/src/image/ImageResizer.cpp diff --git a/engine/animation/include/animation/core/AnimationClip.h b/engine/animation/include/animation/core/AnimationClip.h index df87235c..12205763 100644 --- a/engine/animation/include/animation/core/AnimationClip.h +++ b/engine/animation/include/animation/core/AnimationClip.h @@ -14,7 +14,7 @@ namespace sky { class AnimationClip : public RefObject { public: - explicit AnimationClip(const Name &inName, const Uuid& inUuid) : name(inName), sourceId(inUuid) {} // NOLINT + explicit AnimationClip(const Name &inName) : name(inName) {} // NOLINT ~AnimationClip() override = default; void AddChannel(const AnimChannelPtr &channel); @@ -28,14 +28,8 @@ namespace sky { FORCEINLINE float GetPlayRate() const { return frameRate; } FORCEINLINE const Name& GetName() const { return name; } -#if SKY_EDITOR - FORCEINLINE const Uuid& GetSourceId() const { return sourceId; } -#endif private: Name name; -#if SKY_EDITOR - Uuid sourceId; -#endif uint32_t frameNum = 0; float frameRate = 0.f; std::unordered_map channels; diff --git a/engine/animation/test/AnimationTest.cpp b/engine/animation/test/AnimationTest.cpp index 96ba1cbd..34ba772f 100644 --- a/engine/animation/test/AnimationTest.cpp +++ b/engine/animation/test/AnimationTest.cpp @@ -143,6 +143,7 @@ TEST(AnimationTest, AnimationClipTest) ASSERT_EQ(data.position.times.size(), 3); AnimationClip clip(Name("Test")); + clip.AddChannel(new AnimationNodeChannel(data)); clip.SetFrameRate(30.f); diff --git a/engine/core/include/core/archive/MemoryStreamArchive.h b/engine/core/include/core/archive/MemoryStreamArchive.h new file mode 100644 index 00000000..e2ecf154 --- /dev/null +++ b/engine/core/include/core/archive/MemoryStreamArchive.h @@ -0,0 +1,163 @@ +// +// Created by blues on 2024/7/1. +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace sky { + + /** + * @brief A read-only streambuf backed by a raw memory region. + * + * Does NOT own the memory; the caller must ensure the buffer + * outlives this object. + */ + class MemoryInputStreamBuf : public std::streambuf { + public: + MemoryInputStreamBuf(const char *data, size_t size) + { + // const_cast is safe because we only read (setg) + auto *p = const_cast(data); + setg(p, p, p + size); + } + + protected: + pos_type seekoff(off_type off, std::ios_base::seekdir dir, + std::ios_base::openmode /*which*/ = std::ios_base::in) override + { + char *base = eback(); + char *end = egptr(); + + switch (dir) { + case std::ios_base::beg: break; + case std::ios_base::cur: off += (gptr() - base); break; + case std::ios_base::end: off += (end - base); break; + default: return pos_type(off_type(-1)); + } + + if (off < 0 || off > (end - base)) { + return pos_type(off_type(-1)); + } + setg(base, base + off, end); + return pos_type(off); + } + + pos_type seekpos(pos_type pos, std::ios_base::openmode which = std::ios_base::in) override + { + return seekoff(off_type(pos), std::ios_base::beg, which); + } + }; + + /** + * @brief A write streambuf backed by a growable std::vector. + */ + class MemoryOutputStreamBuf : public std::streambuf { + public: + MemoryOutputStreamBuf() = default; + + const char *data() const { return buffer.data(); } + size_t size() const { return buffer.size(); } + + protected: + int_type overflow(int_type ch) override + { + if (ch != traits_type::eof()) { + buffer.push_back(static_cast(ch)); + } + return ch; + } + + std::streamsize xsputn(const char *s, std::streamsize count) override + { + buffer.insert(buffer.end(), s, s + count); + return count; + } + + private: + std::vector buffer; + }; + + /** + * @brief A read-only IStreamArchive backed by raw memory. + * + * Wraps a MemoryInputStreamBuf + std::istream so it can be used + * anywhere an IStreamArchive is expected (e.g. BinaryInputArchive). + * + * Accepts: + * - raw pointer + size + * - BinaryDataPtr (ref-counted, kept alive) + * - std::vector (data is copied) + */ + class IMemoryArchive : public IStreamArchive { + public: + /** Construct from raw pointer. Caller must keep the buffer alive. */ + IMemoryArchive(const char *data, size_t size) + : buf(data, size) + , istream(&buf) + , IStreamArchive(istream) + { + } + + IMemoryArchive(const uint8_t *data, size_t size) + : IMemoryArchive(reinterpret_cast(data), size) + { + } + + /** Construct from BinaryData. Ref is held to keep memory alive. */ + explicit IMemoryArchive(const BinaryDataPtr &binaryData) + : IMemoryArchive(reinterpret_cast(binaryData->Data()), binaryData->Size()) + { + dataRef = binaryData; + } + + /** Construct from a vector (data is moved in). */ + explicit IMemoryArchive(std::vector &&vec) + : ownedData(std::move(vec)) + , buf(reinterpret_cast(ownedData.data()), ownedData.size()) + , istream(&buf) + , IStreamArchive(istream) + { + } + + ~IMemoryArchive() override = default; + + private: + BinaryDataPtr dataRef; // optional: keeps BinaryData alive + std::vector ownedData; // optional: owns copied data + MemoryInputStreamBuf buf; + std::istream istream; + }; + + /** + * @brief A write-only OStreamArchive backed by an in-memory buffer. + * + * After writing, call Data() / Size() to retrieve the result. + */ + class OMemoryArchive : public OStreamArchive { + public: + OMemoryArchive() + : buf() + , ostream(&buf) + , OStreamArchive(ostream) + { + } + + ~OMemoryArchive() override = default; + + const char *Data() const { return buf.data(); } + size_t Size() const { return buf.size(); } + + private: + MemoryOutputStreamBuf buf; + std::ostream ostream; + }; + +} // namespace sky diff --git a/engine/render/adaptor/src/assets/AnimationAsset.cpp b/engine/render/adaptor/src/assets/AnimationAsset.cpp index a597212a..f3555c08 100644 --- a/engine/render/adaptor/src/assets/AnimationAsset.cpp +++ b/engine/render/adaptor/src/assets/AnimationAsset.cpp @@ -87,7 +87,7 @@ namespace sky { { const auto &data = asset->Data(); - auto *clip = new AnimationClip(Name(data.name.c_str()), asset->GetUuid()); + auto *clip = new AnimationClip(Name(data.name.c_str())); size_t numFrame = 0; for (const auto &nodeChannel : data.nodeChannels) { clip->AddChannel(new AnimationNodeChannel(nodeChannel)); diff --git a/engine/render/builder/render/include/builder/render/ImageBuilder.h b/engine/render/builder/render/include/builder/render/ImageBuilder.h index e0142265..66700bd5 100644 --- a/engine/render/builder/render/include/builder/render/ImageBuilder.h +++ b/engine/render/builder/render/include/builder/render/ImageBuilder.h @@ -19,6 +19,7 @@ namespace sky::builder { ~ImageBuilder() override = default; void UseCompress(bool en) { compress = en; } + void SetGlobalConfig(const ImageBuildGlobalConfig &cfg) { globalConfig = cfg; } private: void RequestDDS(const AssetBuildRequest &request, AssetBuildResult &result); void RequestSTB(const AssetBuildRequest &request, AssetBuildResult &result); @@ -32,6 +33,7 @@ namespace sky::builder { std::vector extensions = {".jpg", ".dds", ".ktx", ".png", ".hdr", ".image"}; std::unordered_map configs; + ImageBuildGlobalConfig globalConfig; bool compress = true; }; diff --git a/engine/render/builder/render/include/builder/render/image/ImageResizer.h b/engine/render/builder/render/include/builder/render/image/ImageResizer.h new file mode 100644 index 00000000..23c4531a --- /dev/null +++ b/engine/render/builder/render/include/builder/render/image/ImageResizer.h @@ -0,0 +1,29 @@ +// +// Created by blues on 2025/2/1. +// + +#pragma once + +#include + +namespace sky::builder { + + class ImageResizer : public ImageProcess { + public: + struct Payload { + ImageObjectPtr image; + uint32_t maxWidth = 0xFFFFFFFF; + uint32_t maxHeight = 0xFFFFFFFF; + MipGenType filterType = MipGenType::Kaiser; + }; + + explicit ImageResizer(const Payload &pd) : payload(pd) {} + ~ImageResizer() override = default; + + void DoWork() override; + + private: + Payload payload; + }; + +} // namespace sky::builder diff --git a/engine/render/builder/render/src/ImageBuilder.cpp b/engine/render/builder/render/src/ImageBuilder.cpp index bc525774..47d13c67 100644 --- a/engine/render/builder/render/src/ImageBuilder.cpp +++ b/engine/render/builder/render/src/ImageBuilder.cpp @@ -6,10 +6,12 @@ #include #include #include +#include #include #include #include +#include #include #define STB_IMAGE_IMPLEMENTATION @@ -18,6 +20,7 @@ #define STB_IMAGE_WRITE_IMPLEMENTATION #include +static const char *TAG = "ImageBuilder"; namespace sky::builder { @@ -179,6 +182,15 @@ namespace sky::builder { config->generateMip = true; config->compress = false; + globalConfig.maxWidth = 2048; + globalConfig.maxHeight = 2048; + + // Limit resolution to global max + { + ImageResizer resizer(ImageResizer::Payload{image, globalConfig.maxWidth, globalConfig.maxHeight}); + resizer.DoWork(); + } + CompressedImagePtr compressedImage; if (config != nullptr) { @@ -223,8 +235,8 @@ namespace sky::builder { auto *am = AssetManager::Get(); auto asset = am->FindOrCreateAsset(request.assetInfo->uuid); auto &imageData = asset->Data(); - imageData.width = static_cast(x); - imageData.height = static_cast(y); + imageData.width = image->width; + imageData.height = image->height; imageData.type = TextureType::TEXTURE_2D; if (compressedImage) { @@ -244,6 +256,8 @@ namespace sky::builder { } else { RequestSTB(request, result); } + + LOG_I(TAG, "Build Imasge Success. %s", request.assetInfo->path.path.GetStr().c_str()); } void ImageBuilder::LoadConfig(const FileSystemPtr &cfg) diff --git a/engine/render/builder/render/src/image/ImageResizer.cpp b/engine/render/builder/render/src/image/ImageResizer.cpp new file mode 100644 index 00000000..2c5369cf --- /dev/null +++ b/engine/render/builder/render/src/image/ImageResizer.cpp @@ -0,0 +1,224 @@ +// +// Created by blues on 2025/2/1. +// + +#include +#include +#include +#include +#include +#include +#include + +namespace sky::builder { + + // ---------- Polyphase 1-D kernel (same algorithm as ImageMipGen) ---------- + namespace { + + struct PolyphaseKernel1D { + PolyphaseKernel1D(Filter &filter, + uint32_t srcLen, + uint32_t dstLen, + uint32_t samples = 32) + : length(dstLen) + { + scale = static_cast(dstLen) / static_cast(srcLen); + width = filter.GetWidth() / scale; + + windowSize = static_cast(std::ceil(width * 2.f)) + 1; + weights.resize(windowSize, 1.f); + + float sum = 0.f; + for (uint32_t j = 0; j < windowSize; ++j) { + float s = filter.Sample(static_cast(j) - windowSize / 2.f, + scale, static_cast(samples)); + weights[j] = s; + sum += s; + } + for (auto &w : weights) { + w /= sum; + } + } + + // Resample a row (X direction) of the source mip into float output. + void ApplyX(const ImageMipData &src, PixelType type, + uint32_t y, uint32_t z, + float *out, uint32_t components) const + { + uint32_t baseIndex = (z * src.height + y) * src.width; + for (uint32_t i = 0; i < length; ++i) { + float center = (static_cast(i) + 0.5f) / scale; + auto left = static_cast(std::floor(center - width)); + + for (uint32_t c = 0; c < components; ++c) { + float acc = 0.f; + for (uint32_t k = 0; k < windowSize; ++k) { + int32_t tx = left + static_cast(k); + auto sx = static_cast( + std::clamp(tx, 0, static_cast(src.width) - 1)); + uint32_t idx = baseIndex + sx; + + float val = 0.f; + switch (type) { + case PixelType::U8: + val = U8ToF32(src.data.get()[idx * components + c]); + break; + case PixelType::Float: + val = reinterpret_cast( + src.data.get())[idx * components + c]; + break; + default: + break; + } + acc += weights[k] * val; + } + out[i * components + c] = acc; + } + } + } + + // Resample a column (Y direction) of a float-typed tmp buffer. + void ApplyY(const ImageMipData &src, PixelType type, + uint32_t x, uint32_t z, + float *out, uint32_t components) const + { + for (uint32_t i = 0; i < length; ++i) { + float center = (static_cast(i) + 0.5f) / scale; + auto left = static_cast(std::floor(center - width)); + + for (uint32_t c = 0; c < components; ++c) { + float acc = 0.f; + for (uint32_t k = 0; k < windowSize; ++k) { + int32_t ty = left + static_cast(k); + auto sy = static_cast( + std::clamp(ty, 0, static_cast(src.height) - 1)); + uint32_t idx = (z * src.height + sy) * src.width + x; + + float val = 0.f; + switch (type) { + case PixelType::U8: + val = U8ToF32(src.data.get()[idx * components + c]); + break; + case PixelType::Float: + val = reinterpret_cast( + src.data.get())[idx * components + c]; + break; + default: + break; + } + acc += weights[k] * val; + } + out[i * components + c] = acc; + } + } + } + + float scale; + float width; + uint32_t length; + uint32_t windowSize; + std::vector weights; + }; + + void ResampleImage(const ImageObject &image, + const ImageMipData &srcMip, + ImageMipData &dstMip, + MipGenType filterType) + { + uint32_t components = image.components; + + std::unique_ptr> filter; + switch (filterType) { + case MipGenType::Box: + filter = std::make_unique>(); + break; + case MipGenType::Kaiser: + filter = std::make_unique>(4.f, 7.f); + break; + default: + SKY_ASSERT(false && "unsupported filter type"); + return; + } + + PolyphaseKernel1D kernelX(*filter, srcMip.width, dstMip.width); + PolyphaseKernel1D kernelY(*filter, srcMip.height, dstMip.height); + + PixelType type = GetPixelType(image.format); + + // Two-pass separable filter: X first into a float temp, then Y. + // Temp buffer: dstWidth 〜 srcHeight, float components. + ImageMipData tmpData = ImageMipData::Create( + dstMip.width, srcMip.height, srcMip.depth, + components * static_cast(sizeof(float))); + + // Pass 1 ! horizontal + for (uint32_t z = 0; z < srcMip.depth; ++z) { + for (uint32_t y = 0; y < srcMip.height; ++y) { + float *row = reinterpret_cast(tmpData.data.get()) + + (z * srcMip.height + y) * dstMip.width * components; + kernelX.ApplyX(srcMip, type, y, z, row, components); + } + } + + // Pass 2 ! vertical + std::vector tmpCol(dstMip.height * components, 0.f); + for (uint32_t z = 0; z < srcMip.depth; ++z) { + for (uint32_t x = 0; x < dstMip.width; ++x) { + kernelY.ApplyY(tmpData, PixelType::Float, x, z, tmpCol.data(), components); + + for (uint32_t y = 0; y < dstMip.height; ++y) { + uint8_t *dst = dstMip.data.get() + + ((z * dstMip.height + y) * dstMip.width + x) * image.pixelSize; + const float *src = tmpCol.data() + y * components; + + Color color = {}; + for (uint32_t c = 0; c < components; ++c) { + color.v[c] = src[c]; + } + SetImageColor(type, components, dst, color); + } + } + } + } + + } // anonymous namespace + + void ImageResizer::DoWork() + { + auto &image = payload.image; + SKY_ASSERT(image && !image->mips.empty()); + + uint32_t srcW = image->width; + uint32_t srcH = image->height; + + // Nothing to do if already within limits. + if (srcW <= payload.maxWidth && srcH <= payload.maxHeight) { + return; + } + + // Compute target size preserving aspect ratio. + float scaleW = static_cast(payload.maxWidth) / static_cast(srcW); + float scaleH = static_cast(payload.maxHeight) / static_cast(srcH); + float scale = std::min(scaleW, scaleH); + + uint32_t dstW = std::max(1u, static_cast(std::floor(srcW * scale))); + uint32_t dstH = std::max(1u, static_cast(std::floor(srcH * scale))); + + // Clamp to exact limits (floating-point floor may overshoot by 1). + dstW = std::min(dstW, payload.maxWidth); + dstH = std::min(dstH, payload.maxHeight); + + // Only resample mip 0; discard any existing mip chain. + ImageMipData &srcMip = image->mips[0]; + ImageMipData dstMip = ImageMipData::Create(dstW, dstH, srcMip.depth, image->pixelSize); + + ResampleImage(*image, srcMip, dstMip, payload.filterType); + + // Replace the image contents. + image->width = dstW; + image->height = dstH; + image->mips.clear(); + image->mips.emplace_back(std::move(dstMip)); + } + +} // namespace sky::builder diff --git a/engine/render/builder/test/BuilderTest.cpp b/engine/render/builder/test/BuilderTest.cpp index 79cf1f1f..21415a9c 100644 --- a/engine/render/builder/test/BuilderTest.cpp +++ b/engine/render/builder/test/BuilderTest.cpp @@ -7,9 +7,12 @@ #include #include #include +#include #include +using namespace sky; using namespace sky::builder; +using namespace sky::rhi; inline static float bessel0(float x) { @@ -61,36 +64,178 @@ static const uint8_t DFT_COLOR32[] = { TEST(BuilderTest, ConvertTest) { - ImageObjectPtr src = ImageObject::CreateImage2D(2, 2, PixelType::U8, 1); - { - ImageMipData mip0 = {2, 2, 1}; - mip0.data = std::make_unique(sizeof(DFT_COLOR32)); - memcpy(mip0.data.get(), DFT_COLOR32, sizeof(DFT_COLOR32)); - src->mips.emplace_back(std::move(mip0)); - } - + // src: 2x2 RGBA8 + ImageObjectPtr src = ImageObject::CreateImage2D(2, 2, PixelFormat::RGBA8_UNORM); + src->FillMip0(DFT_COLOR32, sizeof(DFT_COLOR32)); - ImageObjectPtr dst = ImageObject::CreateImage2D(2, 2, PixelType::Float, 4); - { - ImageMipData mip0 = {2, 2, 1}; - mip0.data = std::make_unique(sizeof(DFT_COLOR)); - dst->mips.emplace_back(std::move(mip0)); - } + // dst: 2x2 RGBA32F + ImageObjectPtr dst = ImageObject::CreateImage2D(2, 2, PixelFormat::RGBA32_SFLOAT); + dst->FillMip0(); - ImageConverter converter(ImageConverter::Payload{src, dst}); + ImageConverter converter(ImageConverter::Payload{src, dst, 1.f}); // gamma 1.0 = no correction converter.DoWork(); + + // Verify: first pixel should be (1,0,0,1) + const float *outPixels = reinterpret_cast(dst->mips[0].data.get()); + EXPECT_FLOAT_EQ(outPixels[0], 1.0f); // R + EXPECT_FLOAT_EQ(outPixels[1], 0.0f); // G + EXPECT_FLOAT_EQ(outPixels[2], 0.0f); // B + EXPECT_FLOAT_EQ(outPixels[3], 1.0f); // A + + // second pixel (0,1,0,1) + EXPECT_FLOAT_EQ(outPixels[4], 0.0f); + EXPECT_FLOAT_EQ(outPixels[5], 1.0f); + EXPECT_FLOAT_EQ(outPixels[6], 0.0f); + EXPECT_FLOAT_EQ(outPixels[7], 1.0f); } TEST(BuilderTest, MipMapGenTest) { - ImageObjectPtr image = ImageObject::CreateImage2D(2, 2, PixelType::Float, 1); - ImageMipData mip0 = {2, 2, 1}; - mip0.data = std::make_unique(sizeof(DFT_COLOR)); - memcpy(mip0.data.get(), DFT_COLOR, sizeof(DFT_COLOR)); - image->mips.emplace_back(std::move(mip0)); + // 2x2 RGBA32F image + ImageObjectPtr image = ImageObject::CreateImage2D(2, 2, PixelFormat::RGBA32_SFLOAT); + image->FillMip0(reinterpret_cast(DFT_COLOR), sizeof(DFT_COLOR)); + + ImageMipGen mipGen(ImageMipGen::Payload{image}); + mipGen.DoWork(); + + // 2x2 -> mip levels: 2 (2x2, 1x1) + ASSERT_EQ(image->mips.size(), 2u); + ASSERT_EQ(image->mips[1].width, 1u); + ASSERT_EQ(image->mips[1].height, 1u); +} + +// --------------------------------------------------------------------------- +// ImageResizer tests +// --------------------------------------------------------------------------- + +TEST(BuilderTest, ResizerNoOpWhenWithinLimits) +{ + // 4x4 RGBA8 ! max is larger, should be a no-op + ImageObjectPtr image = ImageObject::CreateImage2D(4, 4, PixelFormat::RGBA8_UNORM); + image->FillMip0(); + + ImageResizer resizer(ImageResizer::Payload{image, 1024, 1024}); + resizer.DoWork(); + + ASSERT_EQ(image->width, 4u); + ASSERT_EQ(image->height, 4u); +} + +TEST(BuilderTest, ResizerExactLimit) +{ + // Image exactly at the limit ! no resize + ImageObjectPtr image = ImageObject::CreateImage2D(512, 512, PixelFormat::RGBA8_UNORM); + image->FillMip0(); + + ImageResizer resizer(ImageResizer::Payload{image, 512, 512}); + resizer.DoWork(); + + ASSERT_EQ(image->width, 512u); + ASSERT_EQ(image->height, 512u); +} + +TEST(BuilderTest, ResizerDownscaleSquare) +{ + // 8x8 -> max 4x4 + ImageObjectPtr image = ImageObject::CreateImage2D(8, 8, PixelFormat::RGBA8_UNORM); + image->FillMip0(); + memset(image->mips[0].data.get(), 128, image->mips[0].dataLength); + + ImageResizer resizer(ImageResizer::Payload{image, 4, 4}); + resizer.DoWork(); + + ASSERT_EQ(image->width, 4u); + ASSERT_EQ(image->height, 4u); + ASSERT_EQ(image->mips.size(), 1u); + ASSERT_EQ(image->mips[0].dataLength, 4u * 4u * 4u); // 4x4 RGBA8 = 64 bytes +} + +TEST(BuilderTest, ResizerDownscalePreservesAspectRatio) +{ + // 16x8 -> max 8x8 => should become 8x4 (scale by 0.5 to fit width) + ImageObjectPtr image = ImageObject::CreateImage2D(16, 8, PixelFormat::RGBA8_UNORM); + image->FillMip0(); + memset(image->mips[0].data.get(), 200, image->mips[0].dataLength); + + ImageResizer resizer(ImageResizer::Payload{image, 8, 8}); + resizer.DoWork(); + + ASSERT_EQ(image->width, 8u); + ASSERT_EQ(image->height, 4u); +} + +TEST(BuilderTest, ResizerDownscalePreservesAspectRatioTall) +{ + // 8x16 -> max 8x8 => should become 4x8 (scale by 0.5 to fit height) + ImageObjectPtr image = ImageObject::CreateImage2D(8, 16, PixelFormat::RGBA8_UNORM); + image->FillMip0(); + memset(image->mips[0].data.get(), 100, image->mips[0].dataLength); + + ImageResizer resizer(ImageResizer::Payload{image, 8, 8}); + resizer.DoWork(); + + ASSERT_EQ(image->width, 4u); + ASSERT_EQ(image->height, 8u); +} + +TEST(BuilderTest, ResizerWidthOnlyExceed) +{ + // 1024x64 -> max 256 wide, any height => 256x16 + ImageObjectPtr image = ImageObject::CreateImage2D(1024, 64, PixelFormat::RGBA8_UNORM); + image->FillMip0(); + memset(image->mips[0].data.get(), 50, image->mips[0].dataLength); + + ImageResizer resizer(ImageResizer::Payload{image, 256, 0xFFFFFFFF}); + resizer.DoWork(); + + ASSERT_EQ(image->width, 256u); + ASSERT_EQ(image->height, 16u); +} + +TEST(BuilderTest, ResizerFloat32Image) +{ + // Test with float format: 16x16 RGBA32F -> max 4x4 + ImageObjectPtr image = ImageObject::CreateImage2D(16, 16, PixelFormat::RGBA32_SFLOAT); + image->FillMip0(); + + // Fill with a known value + float *pixels = reinterpret_cast(image->mips[0].data.get()); + uint32_t pixelCount = 16 * 16 * 4; + for (uint32_t i = 0; i < pixelCount; ++i) { + pixels[i] = 0.5f; + } + + ImageResizer resizer(ImageResizer::Payload{image, 4, 4}); + resizer.DoWork(); + + ASSERT_EQ(image->width, 4u); + ASSERT_EQ(image->height, 4u); + ASSERT_EQ(image->mips.size(), 1u); + + // A uniform 0.5 image downscaled should still be ~0.5 + const float *outPixels = reinterpret_cast(image->mips[0].data.get()); + for (uint32_t i = 0; i < 4u * 4u * 4u; ++i) { + EXPECT_NEAR(outPixels[i], 0.5f, 0.05f); + } +} + +TEST(BuilderTest, ResizerDiscardsMipChain) +{ + // Generate mips first, then resize ! should discard mip chain + ImageObjectPtr image = ImageObject::CreateImage2D(8, 8, PixelFormat::RGBA8_UNORM); + image->FillMip0(); + memset(image->mips[0].data.get(), 128, image->mips[0].dataLength); ImageMipGen mipGen(ImageMipGen::Payload{image}); mipGen.DoWork(); + ASSERT_GT(image->mips.size(), 1u); + + ImageResizer resizer(ImageResizer::Payload{image, 4, 4}); + resizer.DoWork(); + + ASSERT_EQ(image->width, 4u); + ASSERT_EQ(image->height, 4u); + ASSERT_EQ(image->mips.size(), 1u); // mip chain discarded } int main(int argc, char *argv[]) diff --git a/engine/test/framework/AssetManagerTest.cpp b/engine/test/framework/AssetManagerTest.cpp index 5ba1904f..bd378675 100644 --- a/engine/test/framework/AssetManagerTest.cpp +++ b/engine/test/framework/AssetManagerTest.cpp @@ -16,11 +16,11 @@ using namespace sky; -struct T1Data { +struct T1Data : public RefObject { int v; }; -struct T2Data { +struct T2Data : public RefObject { float v; int extVal; }; @@ -41,7 +41,7 @@ struct AssetTraits { static constexpr std::string_view ASSET_TYPE = "T2"; }; -struct T3Data { +struct T3Data : public RefObject { Uuid t1; Uuid t2; }; diff --git a/engine/test/render/StaticMeshTest.cpp b/engine/test/render/StaticMeshTest.cpp index 34dd4345..4c272d2f 100644 --- a/engine/test/render/StaticMeshTest.cpp +++ b/engine/test/render/StaticMeshTest.cpp @@ -8,9 +8,9 @@ using namespace sky; TEST(StaticMeshTest, StaticMesthCreate01) { - StaticMesh mesh; + StaticMeshGeometry mesh; - mesh.Init(4, 6, IndexType::U16); + mesh.Init(4, 6, rhi::IndexType::U16); mesh.SetPosition(0, Vector3(1.f, 0.f, 0.f)); mesh.SetPosition(1, Vector3(2.f, 0.f, 0.f)); @@ -26,8 +26,8 @@ TEST(StaticMeshTest, StaticMesthCreate01) auto *positionBuffer = mesh.GetPositionBuffer(); - ASSERT_EQ(positionBuffer != nullptr, true); - ASSERT_EQ(positionBuffer->Num(), 4); + ASSERT_NE(positionBuffer, nullptr); + ASSERT_EQ(positionBuffer->Num(), 4u); const auto& v1 = positionBuffer->GetVertexData(0); ASSERT_FLOAT_EQ(v1.x, 1.f); @@ -39,19 +39,18 @@ TEST(StaticMeshTest, StaticMesthCreate01) ASSERT_FLOAT_EQ(v3.x, 3.f); ASSERT_FLOAT_EQ(v3.y, 3.f); - const auto& v4 = positionBuffer->GetVertexData(3); + const auto& v4 = positionBuffer->GetVertexData(3); ASSERT_FLOAT_EQ(v4.z, 4.f); auto *indexBuffer = mesh.GetIndexBuffer(); - ASSERT_EQ(indexBuffer != nullptr, true); - ASSERT_EQ(indexBuffer->Num(), 6); + ASSERT_NE(indexBuffer, nullptr); + ASSERT_EQ(indexBuffer->Num(), 6u); - ASSERT_EQ(indexBuffer->GetIndexU32(0), 0); - ASSERT_EQ(indexBuffer->GetIndexU32(1), 1); - ASSERT_EQ(indexBuffer->GetIndexU32(2), 2); - ASSERT_EQ(indexBuffer->GetIndexU32(3), 2); - ASSERT_EQ(indexBuffer->GetIndexU32(4), 3); - ASSERT_EQ(indexBuffer->GetIndexU32(5), 0); - -} \ No newline at end of file + ASSERT_EQ(indexBuffer->GetIndexU32(0), 0u); + ASSERT_EQ(indexBuffer->GetIndexU32(1), 1u); + ASSERT_EQ(indexBuffer->GetIndexU32(2), 2u); + ASSERT_EQ(indexBuffer->GetIndexU32(3), 2u); + ASSERT_EQ(indexBuffer->GetIndexU32(4), 3u); + ASSERT_EQ(indexBuffer->GetIndexU32(5), 0u); +}//} \ No newline at end of file From 28bf65a21e779137066193a48ec96fcb7b4bdc37 Mon Sep 17 00:00:00 2001 From: Zach Date: Sun, 8 Mar 2026 20:44:27 +0800 Subject: [PATCH 02/38] [feat]: Fix FileStream. --- .gitignore | 1 + .../core/include/core/archive/FileArchive.h | 2 +- engine/core/include/core/archive/IArchive.h | 2 + .../core/include/core/archive/MemoryArchive.h | 1 + .../core/archive/MemoryStreamArchive.h | 13 +- .../core/include/core/archive/StreamArchive.h | 2 + engine/core/include/core/file/FileSystem.h | 1 + engine/core/src/archive/FileArchive.cpp | 2 +- engine/core/src/archive/MemoryAchive.cpp | 9 ++ engine/core/src/archive/StreamArchive.cpp | 12 ++ engine/core/src/file/FileIO.cpp | 10 +- engine/core/src/file/FileSystem.cpp | 8 +- .../framework/src/AssetBrowserWidget.cpp | 2 + engine/framework/src/asset/AssetDataBase.cpp | 1 + engine/framework/src/asset/AssetManager.cpp | 13 +- .../src/components/PrefabComponent.cpp | 12 +- .../src/components/SkyBoxComponent.cpp | 17 ++- .../builder/render/src/LodGroupBuilder.cpp | 6 +- .../builder/render/src/MaterialBuilder.cpp | 4 +- .../render/builder/render/src/MeshBuilder.cpp | 8 +- .../core/include/render/RenderGeometry.h | 2 - .../include/render/env/SkySphereRenderer.h | 27 +++-- .../render/core/src/env/SkySphereRenderer.cpp | 113 +++++++++++------- 23 files changed, 179 insertions(+), 89 deletions(-) diff --git a/.gitignore b/.gitignore index 9b6fa6fe..cac57f2f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ /test/assets/assets.db /test/products/ /python/.idea +/review/ diff --git a/engine/core/include/core/archive/FileArchive.h b/engine/core/include/core/archive/FileArchive.h index 2748b334..8c336dd6 100644 --- a/engine/core/include/core/archive/FileArchive.h +++ b/engine/core/include/core/archive/FileArchive.h @@ -19,7 +19,7 @@ namespace sky { bool IsOpen() const override { return stream.is_open(); } private: - std::fstream stream; + std::ifstream stream; }; class OFileArchive : public OStreamArchive { diff --git a/engine/core/include/core/archive/IArchive.h b/engine/core/include/core/archive/IArchive.h index abea1189..f2b03c4d 100644 --- a/engine/core/include/core/archive/IArchive.h +++ b/engine/core/include/core/archive/IArchive.h @@ -48,6 +48,8 @@ namespace sky { return *this; } + virtual bool Skip(size_t size) { return false; } + virtual int Peek() { return std::char_traits::eof(); } virtual int Get() { return std::char_traits::eof(); } virtual size_t Tell() const { return 0; } diff --git a/engine/core/include/core/archive/MemoryArchive.h b/engine/core/include/core/archive/MemoryArchive.h index 6fe82d7b..6eacb744 100644 --- a/engine/core/include/core/archive/MemoryArchive.h +++ b/engine/core/include/core/archive/MemoryArchive.h @@ -17,6 +17,7 @@ namespace sky { ~MemoryArchive() override = default; bool LoadRaw(char *data, size_t size) override; + bool Skip(size_t size) override; bool SaveRaw(const char *data, size_t size) override; const char* Data() const; diff --git a/engine/core/include/core/archive/MemoryStreamArchive.h b/engine/core/include/core/archive/MemoryStreamArchive.h index e2ecf154..130db72a 100644 --- a/engine/core/include/core/archive/MemoryStreamArchive.h +++ b/engine/core/include/core/archive/MemoryStreamArchive.h @@ -94,7 +94,6 @@ namespace sky { * Accepts: * - raw pointer + size * - BinaryDataPtr (ref-counted, kept alive) - * - std::vector (data is copied) */ class IMemoryArchive : public IStreamArchive { public: @@ -113,15 +112,8 @@ namespace sky { /** Construct from BinaryData. Ref is held to keep memory alive. */ explicit IMemoryArchive(const BinaryDataPtr &binaryData) - : IMemoryArchive(reinterpret_cast(binaryData->Data()), binaryData->Size()) - { - dataRef = binaryData; - } - - /** Construct from a vector (data is moved in). */ - explicit IMemoryArchive(std::vector &&vec) - : ownedData(std::move(vec)) - , buf(reinterpret_cast(ownedData.data()), ownedData.size()) + : dataRef(binaryData) + , buf(reinterpret_cast(binaryData->Data()), binaryData->Size()) , istream(&buf) , IStreamArchive(istream) { @@ -131,7 +123,6 @@ namespace sky { private: BinaryDataPtr dataRef; // optional: keeps BinaryData alive - std::vector ownedData; // optional: owns copied data MemoryInputStreamBuf buf; std::istream istream; }; diff --git a/engine/core/include/core/archive/StreamArchive.h b/engine/core/include/core/archive/StreamArchive.h index fa360028..7ad6f9d0 100644 --- a/engine/core/include/core/archive/StreamArchive.h +++ b/engine/core/include/core/archive/StreamArchive.h @@ -28,6 +28,7 @@ namespace sky { virtual uint32_t Size() const { return 0; } bool LoadRaw(char *data, size_t size) override; + bool Skip(size_t size) override; template void LoadFromStream(Archive &ar) @@ -53,6 +54,7 @@ namespace sky { ~IStreamArchive() override = default; bool LoadRaw(char *data, size_t size) override; + bool Skip(size_t size) override; std::istream &GetStream() const { return stream; } diff --git a/engine/core/include/core/file/FileSystem.h b/engine/core/include/core/file/FileSystem.h index 7ed77618..2f1cdacd 100644 --- a/engine/core/include/core/file/FileSystem.h +++ b/engine/core/include/core/file/FileSystem.h @@ -50,6 +50,7 @@ namespace sky { void ReplaceExtension(const std::string &name); std::fstream OpenFStream(std::ios_base::openmode) const; + std::ifstream OpenIFStream(std::ios_base::openmode) const; bool operator==(const FilePath &rhs) const { return filePath == rhs.filePath; } private: diff --git a/engine/core/src/archive/FileArchive.cpp b/engine/core/src/archive/FileArchive.cpp index 64cc59dd..325e3f11 100644 --- a/engine/core/src/archive/FileArchive.cpp +++ b/engine/core/src/archive/FileArchive.cpp @@ -7,7 +7,7 @@ namespace sky { IFileArchive::IFileArchive(const FilePath &path, std::ios::openmode mode) - : stream(path.OpenFStream(mode | std::ios::in)) + : stream(path.OpenIFStream(mode)) , IStreamArchive(stream) { } diff --git a/engine/core/src/archive/MemoryAchive.cpp b/engine/core/src/archive/MemoryAchive.cpp index 066488e3..158c9b22 100644 --- a/engine/core/src/archive/MemoryAchive.cpp +++ b/engine/core/src/archive/MemoryAchive.cpp @@ -18,6 +18,15 @@ namespace sky { return true; } + bool MemoryArchive::Skip(size_t size) + { + if (pointer + size > data.size()) { + return false; + } + pointer += size; + return true; + } + bool MemoryArchive::SaveRaw(const char *val, size_t size) { if (pointer + size > data.size()) { diff --git a/engine/core/src/archive/StreamArchive.cpp b/engine/core/src/archive/StreamArchive.cpp index f9c1c0be..eee503b5 100644 --- a/engine/core/src/archive/StreamArchive.cpp +++ b/engine/core/src/archive/StreamArchive.cpp @@ -31,6 +31,12 @@ namespace sky { return stream.rdbuf()->sgetn(data, static_cast(size)) == size; } + bool StreamArchive::Skip(size_t size) + { + stream.ignore(static_cast(size)); + return !stream.fail(); + } + bool StreamArchive::SaveRaw(const char *data, size_t size) { return stream.rdbuf()->sputn(data, static_cast(size)) == size; @@ -41,6 +47,12 @@ namespace sky { return stream.rdbuf()->sgetn(data, static_cast(size)) == size; } + bool IStreamArchive::Skip(size_t size) + { + stream.ignore(static_cast(size)); + return !stream.fail(); + } + int IStreamArchive::Peek() { return stream.peek(); diff --git a/engine/core/src/file/FileIO.cpp b/engine/core/src/file/FileIO.cpp index 26205403..2907a07a 100644 --- a/engine/core/src/file/FileIO.cpp +++ b/engine/core/src/file/FileIO.cpp @@ -28,7 +28,7 @@ namespace sky { bool ReadBin(const FilePath &path, uint8_t *&out, uint32_t &size) { - std::fstream file(path.OpenFStream(std::ios::binary | std::ios::ate | std::ios::in)); + std::ifstream file(path.OpenIFStream(std::ios::binary | std::ios::ate)); if (!file.is_open()) { return false; } @@ -42,7 +42,7 @@ namespace sky { BinaryDataPtr ReadBin(const FilePath &path) { - std::fstream file(path.OpenFStream(std::ios::binary | std::ios::ate | std::ios::in)); + std::ifstream file = path.OpenIFStream(std::ios::binary | std::ios::ate); if (!file.is_open()) { return nullptr; } @@ -57,7 +57,7 @@ namespace sky { bool ReadBin(const FilePath &path, std::vector &out) { - std::fstream file(path.OpenFStream(std::ios::binary | std::ios::ate | std::ios::in)); + std::ifstream file(path.GetStr(), std::ios::binary | std::ios::ate); if (!file.is_open()) { return false; } @@ -71,7 +71,7 @@ namespace sky { bool ReadBin(const FilePath &path, std::vector &out) { - std::fstream file(path.OpenFStream(std::ios::binary | std::ios::ate | std::ios::in)); + std::ifstream file(path.GetStr(), std::ios::binary | std::ios::ate); if (!file.is_open()) { return false; } @@ -85,7 +85,7 @@ namespace sky { bool ReadString(const FilePath &path, std::string &out) { - std::fstream file(path.OpenFStream(std::ios::binary | std::ios::in)); + std::ifstream file(path.OpenIFStream(std::ios::binary)); if (!file.is_open()) { return false; } diff --git a/engine/core/src/file/FileSystem.cpp b/engine/core/src/file/FileSystem.cpp index 8c3ed1c9..56f312aa 100644 --- a/engine/core/src/file/FileSystem.cpp +++ b/engine/core/src/file/FileSystem.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -75,6 +76,11 @@ namespace sky { return std::fstream(filePath, mode); } + std::ifstream FilePath::OpenIFStream(std::ios_base::openmode mode) const + { + return std::ifstream(filePath, mode); + } + FilePath FilePath::Relative(const FilePath& base) const { auto result = std::filesystem::relative(filePath, base.filePath); @@ -129,7 +135,7 @@ namespace sky { void NativeFile::ReadData(uint64_t offset, uint64_t size, uint8_t *out) { - std::fstream stream = filePath.OpenFStream(std::ios::in | std::ios::binary); + std::ifstream stream = filePath.OpenIFStream(std::ios::binary); if (!stream.is_open()) { return; } diff --git a/engine/editor/framework/src/AssetBrowserWidget.cpp b/engine/editor/framework/src/AssetBrowserWidget.cpp index cc398463..7d63c736 100644 --- a/engine/editor/framework/src/AssetBrowserWidget.cpp +++ b/engine/editor/framework/src/AssetBrowserWidget.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -177,6 +178,7 @@ namespace sky::editor { auto indices = assetItemView->selectionModel()->selectedIndexes(); auto *fsModel = static_cast(assetItemView->model()); QDir root = fsModel->rootPath(); + LOG_I("Builder", "Build Assets[%d]", indices.size()); for (auto &index : indices) { auto path = root.relativeFilePath(fsModel->filePath(index)); diff --git a/engine/framework/src/asset/AssetDataBase.cpp b/engine/framework/src/asset/AssetDataBase.cpp index f9ec32ce..ac60004b 100644 --- a/engine/framework/src/asset/AssetDataBase.cpp +++ b/engine/framework/src/asset/AssetDataBase.cpp @@ -141,6 +141,7 @@ namespace sky { AssetBuildRequest request = {}; request.file = fs->OpenFile(path.path); request.assetInfo = info; + LOG_I(TAG, "Request Build Asset %s", info->uuid.ToString().c_str()); AssetBuilderManager::Get()->BuildRequest(request); } diff --git a/engine/framework/src/asset/AssetManager.cpp b/engine/framework/src/asset/AssetManager.cpp index 623cc7fd..533ed95d 100644 --- a/engine/framework/src/asset/AssetManager.cpp +++ b/engine/framework/src/asset/AssetManager.cpp @@ -9,6 +9,7 @@ #include #include #include +#include static const char* TAG = "AssetManager"; @@ -95,12 +96,15 @@ namespace sky { return {}; } - auto archive = file->ReadAsArchive(); + auto bin = file->ReadBin(); + IStreamArchivePtr archive = new IMemoryArchive(bin); + asset = CreateAssetByHeader(uuid, archive); if (!asset) { return {}; } + // avoid release dep asset std::vector holder; std::vector asyncTasks; @@ -130,6 +134,13 @@ namespace sky { } SKY_PROFILE_NAME("LoadAsset") auto asset = FindAsset(uuid); + + if (!asset) + { + auto sourceAsset = AssetDataBase::Get()->FindAsset(uuid); + LOG_E(TAG, "Asset %s : %s not found in database. Maybe deleted?", uuid.ToString().c_str(), sourceAsset->path.path.GetStr().c_str()); + } + SKY_ASSERT(asset) asset->depAssets.swap(deps); auto res = assetHandlers[asset->type]->Load(*archive, asset); diff --git a/engine/render/adaptor/src/components/PrefabComponent.cpp b/engine/render/adaptor/src/components/PrefabComponent.cpp index e9023d8b..e6df8a40 100644 --- a/engine/render/adaptor/src/components/PrefabComponent.cpp +++ b/engine/render/adaptor/src/components/PrefabComponent.cpp @@ -5,6 +5,9 @@ #include #include #include +#include + +static const char *TAG = "PrefabComponent"; namespace sky { @@ -116,6 +119,8 @@ namespace sky { void PrefabComponent::OnAssetLoaded(const Uuid& uuid, const std::string_view& type) { if (type == AssetTraits::ASSET_TYPE) { + std::lock_guard lock(assetMutex); + const auto &prefabData = prefab.Data(); size_t nodeNum = prefabData.nodes.size(); capacity.store(nodeNum); @@ -137,9 +142,14 @@ namespace sky { lodGroupHolders.emplace(meshId, SingleAssetHolder{}); } - std::lock_guard lock(assetMutex); + uint32_t num = 0; + + LOG_I(TAG, "Load Prefab %s, nodeNum%zu", uuid.ToString(), lodGroupHolders.size()); for (auto& [id, holder] : lodGroupHolders) { + holder.SetAsset(id, this); + LOG_I(TAG, "Load LodGroup %s, index%u", id.ToString().c_str(), num); + num++; } } else if (type == AssetTraits::ASSET_TYPE){ diff --git a/engine/render/adaptor/src/components/SkyBoxComponent.cpp b/engine/render/adaptor/src/components/SkyBoxComponent.cpp index 44c293fb..bfa497ad 100644 --- a/engine/render/adaptor/src/components/SkyBoxComponent.cpp +++ b/engine/render/adaptor/src/components/SkyBoxComponent.cpp @@ -16,9 +16,6 @@ namespace sky { techAsset = AssetManager::Get()->LoadAssetFromPath("techniques/skybox.tech"); techAsset->BlockUntilLoaded(); technique = CreateTechniqueFromAsset(techAsset); - - renderer = std::make_unique(); - renderer->SetTechnique(technique); } void SkyBoxComponent::Reflect(SerializationContext *context) @@ -43,6 +40,9 @@ namespace sky { void SkyBoxComponent::OnAttachToWorld() { + auto *scene = GetRenderSceneFromActor(actor); + renderer = std::make_unique(scene, technique); + if (data.skybox && !texture) { SetImage(data.skybox); } @@ -54,15 +54,10 @@ namespace sky { if (data.irradiance && !irradiance) { SetIrradiance(data.irradiance); } - - auto *scene = GetRenderSceneFromActor(actor); - scene->AddPrimitive(renderer->GetPrimitive()); } void SkyBoxComponent::OnDetachFromWorld() { - auto *scene = GetRenderSceneFromActor(actor); - scene->RemovePrimitive(renderer->GetPrimitive()); renderer = nullptr; } @@ -74,7 +69,11 @@ namespace sky { texture = CastPtr(CreateTextureFromAsset(texAsset)); Renderer::Get()->GetStreamingManager()->UploadTexture(texture); - renderer->GetPrimitive()->texture = texture; + + if (renderer != nullptr) + { + renderer->UpdateTexture(texture); + } } void SkyBoxComponent::SetRadiance(const Uuid &image) diff --git a/engine/render/builder/render/src/LodGroupBuilder.cpp b/engine/render/builder/render/src/LodGroupBuilder.cpp index ee3f85f5..a425bfdc 100644 --- a/engine/render/builder/render/src/LodGroupBuilder.cpp +++ b/engine/render/builder/render/src/LodGroupBuilder.cpp @@ -4,6 +4,9 @@ #include #include +#include + +static const char *TAG = "LodGroupBuilder"; namespace sky::builder { @@ -20,12 +23,13 @@ namespace sky::builder { asset->ResetDependencies(); for (const auto& level : data.levels) { - AssetBuilderManager::Get()->BuildRequest(level.resId, request.target); + // AssetBuilderManager::Get()->BuildRequest(level.resId, request.target); asset->AddDependencies(level.resId); } AssetManager::Get()->SaveAsset(asset, request.target); result.retCode = AssetBuildRetCode::SUCCESS; + LOG_I(TAG, "Build LodGroup %s Success", request.assetInfo->uuid.ToString().c_str()); } } // namespace sky::builder \ No newline at end of file diff --git a/engine/render/builder/render/src/MaterialBuilder.cpp b/engine/render/builder/render/src/MaterialBuilder.cpp index 259688fb..a8d3eaad 100644 --- a/engine/render/builder/render/src/MaterialBuilder.cpp +++ b/engine/render/builder/render/src/MaterialBuilder.cpp @@ -116,11 +116,11 @@ namespace sky::builder { if (obj.IsString()) { const auto *imagePath = obj.GetString(); - auto tex = AssetDataBase::Get()->RegisterAsset(imagePath); + auto tex = AssetDataBase::Get()->RegisterAsset(imagePath, false); properties.valueMap[name] = MaterialTexture{tex->uuid}; request.assetInfo->dependencies.emplace_back(tex->uuid); asset.AddDependencies(tex->uuid); - AssetBuilderManager::Get()->BuildRequest(tex->uuid, request.target); + // AssetBuilderManager::Get()->BuildRequest(tex->uuid, request.target); } else if (obj.IsObject()) { properties.valueMap[name] = ProcessSampler(obj); } else if (obj.IsFloat()) { diff --git a/engine/render/builder/render/src/MeshBuilder.cpp b/engine/render/builder/render/src/MeshBuilder.cpp index ae8024f9..4ecdd910 100644 --- a/engine/render/builder/render/src/MeshBuilder.cpp +++ b/engine/render/builder/render/src/MeshBuilder.cpp @@ -5,6 +5,9 @@ #include #include #include +#include +#include +static const char* TAG = "MaterialBuilder"; namespace sky::builder { @@ -31,8 +34,8 @@ namespace sky::builder { asset->ResetDependencies(); const auto& matData = asset->Data(); - for (auto& mat : matData.materials) { - AssetBuilderManager::Get()->BuildRequest(mat, request.target); + for (const auto& mat : matData.materials) { + // AssetBuilderManager::Get()->BuildRequest(mat, request.target); asset->AddDependencies(mat); } @@ -46,6 +49,7 @@ namespace sky::builder { AssetManager::Get()->SaveAsset(asset, request.target); result.retCode = AssetBuildRetCode::SUCCESS; + LOG_I(TAG, "Build Mesh %s Success", request.assetInfo->uuid.ToString().c_str()); } } // namespace sky::builder \ No newline at end of file diff --git a/engine/render/core/include/render/RenderGeometry.h b/engine/render/core/include/render/RenderGeometry.h index 25a759a4..dda9d490 100644 --- a/engine/render/core/include/render/RenderGeometry.h +++ b/engine/render/core/include/render/RenderGeometry.h @@ -4,8 +4,6 @@ #pragma once -#include "RenderGeometry.h" - #include #include #include diff --git a/engine/render/core/include/render/env/SkySphereRenderer.h b/engine/render/core/include/render/env/SkySphereRenderer.h index fa5ca1d5..8d5c07c4 100644 --- a/engine/render/core/include/render/env/SkySphereRenderer.h +++ b/engine/render/core/include/render/env/SkySphereRenderer.h @@ -18,27 +18,40 @@ namespace sky { }; struct SkySpherePrimitive : RenderPrimitive { + explicit SkySpherePrimitive(const RDGfxTechPtr &inTech) : techInst(inTech) + { + shouldUseFrustumCulling = false; + } + + void UpdateTexture(const RDTexture2DPtr &tex); bool IsReady() const noexcept override; + bool PrepareBatch(const RenderBatchPrepareInfo &info) noexcept override; + void GatherRenderItem(IRenderItemGatherContext *context) noexcept override; - RDTexture2DPtr texture; + RenderTechniqueInstance techInst; + rhi::DescriptorSetPoolPtr pool; + rhi::GraphicsPipelinePtr pso; + rhi::CmdDrawIndexed arg; + + RDTexture2DPtr texture; + RDResourceGroupPtr batchGroup; }; class SkySphereRenderer { public: - SkySphereRenderer(); + SkySphereRenderer(RenderScene *scene, const RDGfxTechPtr &tech); ~SkySphereRenderer(); - void SetTechnique(const RDGfxTechPtr &mat); - - SkySpherePrimitive* GetPrimitive() const { return primitive.get(); } + void UpdateTexture(const RDTexture2DPtr &texture); private: void BuildSphere(); + RenderScene *renderScene; RDGfxTechPtr technique; - RDTexturePtr texture; + rhi::DescriptorSetPoolPtr pool; - float radius = 10000.f; + float radius = 500000.f; std::unique_ptr primitive; }; diff --git a/engine/render/core/src/env/SkySphereRenderer.cpp b/engine/render/core/src/env/SkySphereRenderer.cpp index c83634d3..a205cd89 100644 --- a/engine/render/core/src/env/SkySphereRenderer.cpp +++ b/engine/render/core/src/env/SkySphereRenderer.cpp @@ -13,35 +13,75 @@ namespace sky { rhi::DescriptorSetPool::PoolSize{rhi::DescriptorType::SAMPLER, 1}, }; - // void SkySpherePrimitive::UpdateBatch() - // { - // auto &batch = sections[0].batches[0]; - // if (!batch.batchGroup && batch.program) { - // auto layout = batch.program->RequestLayout(BATCH_SET); - // batch.batchGroup = new ResourceGroup(); - // batch.batchGroup->Init(layout, *pool); - // batch.batchGroup->BindTexture(Name("SkyBox"), texture->GetImageView(), 0); - // batch.batchGroup->Update(); - // } - // } + void SkySpherePrimitive::UpdateTexture(const RDTexture2DPtr &tex) + { + texture = tex; + + if (batchGroup) { + batchGroup->BindTexture(Name("SkyBox"), texture->GetImageView(), 0); + batchGroup->Update(); + } + } + + bool SkySpherePrimitive::PrepareBatch(const RenderBatchPrepareInfo& info) noexcept + { + if (info.techId != techInst.technique->GetRasterID()) { + return false; + } + + if (techInst.UpdateProgram(info.pipelineKey)) { + auto pipelineState = techInst.technique->GetPipelineState(); + pso = GraphicsTechnique::BuildPso(techInst.program, pipelineState, geometry->Request(techInst.program), info.pass, info.subPassId); + } + + if (!batchGroup && techInst.program && texture) { + auto layout = techInst.program->RequestLayout(BATCH_SET); + batchGroup = new ResourceGroup(); + batchGroup->Init(layout, *pool); + batchGroup->BindTexture(Name("SkyBox"), texture->GetImageView(), 0); + batchGroup->Update(); + } + + return !!pso; + } + + void SkySpherePrimitive::GatherRenderItem(IRenderItemGatherContext* context) noexcept + { + context->Append(RenderItem { + .geometry = geometry, + .batchGroup = batchGroup, + .pso = pso, + .args = {arg} + }); + } bool SkySpherePrimitive::IsReady() const noexcept { return texture && texture->IsReady(); } - SkySphereRenderer::SkySphereRenderer() + SkySphereRenderer::SkySphereRenderer(RenderScene *scene, const RDGfxTechPtr &tech) + : renderScene(scene) + , primitive(std::make_unique(tech)) { pool = RHI::Get()->GetDevice()->CreateDescriptorSetPool({ 1, sizeof(POOL_DESC) / sizeof(rhi::DescriptorSetPool::PoolSize), POOL_DESC }); - - primitive = std::make_unique(); - primitive->geometry = new RenderGeometry(); - primitive->geometry->AddVertexAttribute(VertexAttribute{VertexSemanticFlagBit::POSITION, 0, OFFSET_OF(SkyBoxVertex, pos), rhi::Format::F_RGBA32}); - primitive->geometry->AddVertexAttribute(VertexAttribute{VertexSemanticFlagBit::UV, 0, OFFSET_OF(SkyBoxVertex, uv), rhi::Format::F_RG32}); + primitive->pool = pool; BuildSphere(); + + scene->AddPrimitive(primitive.get()); + } + + SkySphereRenderer::~SkySphereRenderer() + { + renderScene->RemovePrimitive(primitive.get()); + } + + void SkySphereRenderer::UpdateTexture(const RDTexture2DPtr &texture) + { + primitive->UpdateTexture(texture); } void SkySphereRenderer::BuildSphere() @@ -101,44 +141,27 @@ namespace sky { } } } - rhi::CmdDrawIndexed indexed = {}; - indexed.indexCount = static_cast(indices.size()); - // primitive->sections[0].args.emplace_back(indexed); - - primitive->geometry->vertexBuffers.clear(); + auto *geometry = new RenderGeometry(); + primitive->geometry = geometry; + primitive->arg.indexCount = static_cast(indices.size()); + geometry->AddVertexAttribute(VertexAttribute{VertexSemanticFlagBit::POSITION, 0, OFFSET_OF(SkyBoxVertex, pos), rhi::Format::F_RGBA32}); + geometry->AddVertexAttribute(VertexAttribute{VertexSemanticFlagBit::UV, 0, OFFSET_OF(SkyBoxVertex, uv), rhi::Format::F_RG32}); + geometry->vertexBuffers.clear(); auto* vb = new Buffer(); vb->Init(vertices.size() * sizeof(SkyBoxVertex), rhi::BufferUsageFlagBit::VERTEX | rhi::BufferUsageFlagBit::TRANSFER_DST, rhi::MemoryType::GPU_ONLY); vb->SetUploadData(std::move(vertices)); - primitive->geometry->vertexBuffers.emplace_back(VertexBuffer{ + geometry->vertexBuffers.emplace_back(VertexBuffer{ vb, 0, vb->GetSize(), sizeof(SkyBoxVertex) }); auto* ib = new Buffer(); ib->Init(indices.size() * sizeof(uint32_t), rhi::BufferUsageFlagBit::INDEX | rhi::BufferUsageFlagBit::TRANSFER_DST, rhi::MemoryType::GPU_ONLY); ib->SetUploadData(std::move(indices)); - primitive->geometry->indexBuffer.buffer = ib; - primitive->geometry->indexBuffer.range = ib->GetSize(); - primitive->geometry->indexBuffer.indexType = rhi::IndexType::U32; - - Renderer::Get()->GetStreamingManager()->UploadBuffer(vb); - Renderer::Get()->GetStreamingManager()->UploadBuffer(ib); - primitive->pool = pool; - -// resourceGroup = new ResourceGroup(); -// resourceGroup->Init(technique->RequestProgram({})->RequestLayout(BATCH_SET), *pool); -// primitive->batches[0].batchGroup = resourceGroup; - } - - SkySphereRenderer::~SkySphereRenderer() = default; - - void SkySphereRenderer::SetTechnique(const RDGfxTechPtr &tech) - { - // technique = tech; - // primitive->sections.emplace_back(); - // primitive->sections[0].batches.emplace_back(RenderBatch{ - // technique - // }); + geometry->indexBuffer.buffer = ib; + geometry->indexBuffer.range = ib->GetSize(); + geometry->indexBuffer.indexType = rhi::IndexType::U32; + geometry->Upload(); } } // namespace sky \ No newline at end of file From e57d1bee4525830ed742137769055b601bd19c0c Mon Sep 17 00:00:00 2001 From: Zach Date: Mon, 9 Mar 2026 00:59:41 +0800 Subject: [PATCH 03/38] [feat]: ReverseZ --- assets/shaders/skybox.hlsl | 2 +- engine/core/include/core/math/MathUtil.h | 29 ++++++++++++++ engine/editor/framework/src/EditorCamera.cpp | 1 + .../render/adaptor/src/pipeline/DepthPass.cpp | 5 ++- .../adaptor/src/pipeline/ForwardMSAAPass.cpp | 9 ++++- .../core/include/render/RenderDepthSettings.h | 40 +++++++++++++++++++ .../core/include/render/RenderPrimitive.h | 1 + engine/render/core/include/render/SceneView.h | 3 ++ .../core/include/render/mesh/MeshPrimitive.h | 2 +- engine/render/core/src/SceneView.cpp | 10 ++--- .../render/core/src/env/SkySphereRenderer.cpp | 2 + engine/render/core/src/mesh/MeshPrimitive.cpp | 12 ++++-- .../core/src/rdg/RenderSceneVisitor.cpp | 19 +++++---- plugins/pvs/editor/src/PVSBakePipeline.cpp | 3 +- 14 files changed, 115 insertions(+), 23 deletions(-) create mode 100644 engine/render/core/include/render/RenderDepthSettings.h diff --git a/assets/shaders/skybox.hlsl b/assets/shaders/skybox.hlsl index ec51c48c..8bb70536 100644 --- a/assets/shaders/skybox.hlsl +++ b/assets/shaders/skybox.hlsl @@ -23,7 +23,7 @@ VSOutput VSMain(VSInput input) VSOutput output = (VSOutput)0; output.Pos = mul(VIEW_INFO.ViewProj, float4(input.Pos.xyz, 1.0)); - output.Pos.w = output.Pos.z; + // output.Pos.w = output.Pos.z; output.UV = input.UV; return output; } diff --git a/engine/core/include/core/math/MathUtil.h b/engine/core/include/core/math/MathUtil.h index f0b7177a..6ca74588 100644 --- a/engine/core/include/core/math/MathUtil.h +++ b/engine/core/include/core/math/MathUtil.h @@ -120,6 +120,35 @@ namespace sky { return ret; } + // Reverse-Z infinite far plane perspective projection. + + FORCEINLINE Matrix4 MakePerspectiveReverseZ(float fovy, float aspect, float near) + { + float const inverseHalfTan = cos(0.5f * fovy) / sin(0.5f * fovy); + + Matrix4 ret; + ret[0][0] = inverseHalfTan / aspect; + ret[1][1] = inverseHalfTan; + ret[2][2] = 0.f; + ret[2][3] = -1.f; + ret[3][2] = near; + return ret; + } + + // Reverse-Z perspective projection with finite far plane. + FORCEINLINE Matrix4 MakePerspectiveReverseZFinite(float fovy, float aspect, float near, float far) + { + float const inverseHalfTan = cos(0.5f * fovy) / sin(0.5f * fovy); + + Matrix4 ret; + ret[0][0] = inverseHalfTan / aspect; + ret[1][1] = inverseHalfTan; + ret[2][2] = near / (far - near); + ret[2][3] = -1.f; + ret[3][2] = (far * near) / (far - near); + return ret; + } + FORCEINLINE void Decompose(const Matrix4 &m, Vector3 &trans, Quaternion &quat, Vector3 &scale) { trans.x = m.v[12]; diff --git a/engine/editor/framework/src/EditorCamera.cpp b/engine/editor/framework/src/EditorCamera.cpp index 586bc4e7..8d3576ad 100644 --- a/engine/editor/framework/src/EditorCamera.cpp +++ b/engine/editor/framework/src/EditorCamera.cpp @@ -15,6 +15,7 @@ namespace sky::editor { renderScene->RemoveSceneView(sceneView); } sceneView = renderScene->CreateSceneView(1); + sceneView->SetReverseZ(true); Active(); uint32_t width = window->GetWidth(); diff --git a/engine/render/adaptor/src/pipeline/DepthPass.cpp b/engine/render/adaptor/src/pipeline/DepthPass.cpp index 4bbcc07a..e65fd1bd 100644 --- a/engine/render/adaptor/src/pipeline/DepthPass.cpp +++ b/engine/render/adaptor/src/pipeline/DepthPass.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include namespace sky { @@ -28,8 +29,8 @@ namespace sky { images.emplace_back(fwdDepthResolve, image); depthStencil = Attachment{ - rdg::RasterAttachment{fwdDepthResolve, rhi::LoadOp::CLEAR, rhi::StoreOp::STORE}, - rhi::ClearValue(1.f, 0) + rdg::RasterAttachment{fwdDepthResolve, rhi::LoadOp::CLEAR, rhi::StoreOp::STORE}, + DepthSettings::DepthStencilClear(false) }; auto stageFlags = rhi::ShaderStageFlagBit::VS | rhi::ShaderStageFlagBit::FS | rhi::ShaderStageFlagBit::TAS | rhi::ShaderStageFlagBit::MS; diff --git a/engine/render/adaptor/src/pipeline/ForwardMSAAPass.cpp b/engine/render/adaptor/src/pipeline/ForwardMSAAPass.cpp index 85ba633b..7b1a8f8f 100644 --- a/engine/render/adaptor/src/pipeline/ForwardMSAAPass.cpp +++ b/engine/render/adaptor/src/pipeline/ForwardMSAAPass.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include namespace sky { @@ -17,6 +18,8 @@ namespace sky { , depthStenFormat(ds) , samples(samples_) { + bool reverseZ = true; + rdg::GraphImage image = {}; image.extent.width = width; image.extent.height = height; @@ -48,7 +51,8 @@ namespace sky { Attachment{rdg::RasterAttachment{fwdColor, rhi::LoadOp::DONT_CARE, rhi::StoreOp::STORE}, rhi::ClearValue(0.f, 0.f, 0.f, 0.f)}); depthStencil = - Attachment{rdg::RasterAttachment{fwdMSAADepthStencil, rhi::LoadOp::CLEAR, rhi::StoreOp::DONT_CARE}, rhi::ClearValue(1.f, 0)}; + Attachment{rdg::RasterAttachment{fwdMSAADepthStencil, rhi::LoadOp::CLEAR, rhi::StoreOp::DONT_CARE}, + DepthSettings::DepthStencilClear(reverseZ)}; } else { image.samples = rhi::SampleCount::X1; image.usage = rhi::ImageUsageFlagBit::RENDER_TARGET | rhi::ImageUsageFlagBit::SAMPLED; @@ -62,7 +66,8 @@ namespace sky { Attachment{rdg::RasterAttachment{fwdColor, rhi::LoadOp::CLEAR, rhi::StoreOp::STORE}, rhi::ClearValue(0.2f, 0.2f, 0.2f, 0.f)}); depthStencil = - Attachment{rdg::RasterAttachment{fwdDepthStencil, rhi::LoadOp::CLEAR, rhi::StoreOp::STORE}, rhi::ClearValue(1.f, 0)}; + Attachment{rdg::RasterAttachment{fwdDepthStencil, rhi::LoadOp::CLEAR, rhi::StoreOp::STORE}, + DepthSettings::DepthStencilClear(reverseZ)}; } auto stageFlags = rhi::ShaderStageFlagBit::VS | rhi::ShaderStageFlagBit::FS | rhi::ShaderStageFlagBit::TAS | rhi::ShaderStageFlagBit::MS; diff --git a/engine/render/core/include/render/RenderDepthSettings.h b/engine/render/core/include/render/RenderDepthSettings.h new file mode 100644 index 00000000..add0502d --- /dev/null +++ b/engine/render/core/include/render/RenderDepthSettings.h @@ -0,0 +1,40 @@ +// +// Created by Copilot on 2026/3/8. +// + +#pragma once + +#include + +namespace sky { + + // Centralised depth settings that adapt based on whether reverse-z is active. + // All render passes / pipelines should query these helpers instead of hard-coding + // clear values, compare ops, or depth ranges. + struct DepthSettings { + // ---- query helpers (hot path C inline) ---- + + // Depth clear value: standard = 1.0, reverse-z = 0.0 + static float ClearDepth(bool reverseZ) { return reverseZ ? 0.f : 1.f; } + + // Depth compare op for opaque geometry: + // standard -> LESS_OR_EQUAL + // reverse-z -> GREATER_OR_EQUAL + static rhi::CompareOp DepthCompareOp(bool reverseZ) + { + return reverseZ ? rhi::CompareOp::GREATER_OR_EQUAL : rhi::CompareOp::LESS_OR_EQUAL; + } + + // ClearValue suitable for depth-stencil attachments + static rhi::ClearValue DepthStencilClear(bool reverseZ) + { + return rhi::ClearValue(ClearDepth(reverseZ), 0); + } + + // Min/max depth bounds (viewport depth range). + // Both modes use [0, 1]; the projection matrix itself flips the mapping. + static float MinDepth() { return 0.f; } + static float MaxDepth() { return 1.f; } + }; + +} // namespace sky diff --git a/engine/render/core/include/render/RenderPrimitive.h b/engine/render/core/include/render/RenderPrimitive.h index 4623778c..cc707ad3 100644 --- a/engine/render/core/include/render/RenderPrimitive.h +++ b/engine/render/core/include/render/RenderPrimitive.h @@ -22,6 +22,7 @@ namespace sky { ShaderVariantKey pipelineKey; rhi::RenderPassPtr pass; uint32_t subPassId = 0; + bool reverseZ = false; }; struct RenderTechniqueInstance { diff --git a/engine/render/core/include/render/SceneView.h b/engine/render/core/include/render/SceneView.h index adf17121..f50efc76 100644 --- a/engine/render/core/include/render/SceneView.h +++ b/engine/render/core/include/render/SceneView.h @@ -23,6 +23,7 @@ namespace sky { void SetPerspective(float near, float far, float fov, float aspect, uint32_t index = 0); void SetOrthogonal(float l, float r, float t, float b, float near, float far, uint32_t index = 0); void SetFlipY(bool flip) { flipY = flip; } + void SetReverseZ(bool enable) { reverseZ = enable; dirty = true; } void Update(); const Matrix4 &GetWorld() const { return viewInfo[0].world; } @@ -39,6 +40,7 @@ namespace sky { float GetNearPlane() const { return near; } float GetFarPlane() const { return far; } + bool IsReverseZ() const { return reverseZ; } const RDUniformBufferPtr &GetUBO() const { return viewUbo; } private: @@ -58,6 +60,7 @@ namespace sky { bool dirty; bool flipY = true; + bool reverseZ = false; RDUniformBufferPtr viewUbo; }; diff --git a/engine/render/core/include/render/mesh/MeshPrimitive.h b/engine/render/core/include/render/mesh/MeshPrimitive.h index 2e56e255..23daf19e 100644 --- a/engine/render/core/include/render/mesh/MeshPrimitive.h +++ b/engine/render/core/include/render/mesh/MeshPrimitive.h @@ -21,7 +21,7 @@ namespace sky { bool IsReadyToGather() const noexcept; void BuildPipelineState(const RenderBatchPrepareInfo& info) noexcept; void Prepare(const RenderBatchPrepareInfo& info) noexcept; - uint32_t CalculatePipelineHash(uint32_t passHash) const noexcept; + uint32_t CalculatePipelineHash(const RenderBatchPrepareInfo& info) const noexcept; const Name& GetRasterID() const noexcept; diff --git a/engine/render/core/src/SceneView.cpp b/engine/render/core/src/SceneView.cpp index f702b7de..4226fbb3 100644 --- a/engine/render/core/src/SceneView.cpp +++ b/engine/render/core/src/SceneView.cpp @@ -39,11 +39,11 @@ namespace sky { { near = near_; far = far_; - projects[index] = MakePerspective(fov, aspect, near, far); -// viewInfo[index].zParam.x = 1 - far / near; -// viewInfo[index].zParam.y = far / near; -// viewInfo[index].zParam.z = viewInfo[index].zParam.x / far; -// viewInfo[index].zParam.w = viewInfo[index].zParam.y / far; + if (reverseZ) { + projects[index] = MakePerspectiveReverseZFinite(fov, aspect, near, far); + } else { + projects[index] = MakePerspective(fov, aspect, near, far); + } dirty = true; } diff --git a/engine/render/core/src/env/SkySphereRenderer.cpp b/engine/render/core/src/env/SkySphereRenderer.cpp index a205cd89..29471ee5 100644 --- a/engine/render/core/src/env/SkySphereRenderer.cpp +++ b/engine/render/core/src/env/SkySphereRenderer.cpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace sky { @@ -31,6 +32,7 @@ namespace sky { if (techInst.UpdateProgram(info.pipelineKey)) { auto pipelineState = techInst.technique->GetPipelineState(); + pipelineState.depthStencil.compareOp = DepthSettings::DepthCompareOp(info.reverseZ); pso = GraphicsTechnique::BuildPso(techInst.program, pipelineState, geometry->Request(techInst.program), info.pass, info.subPassId); } diff --git a/engine/render/core/src/mesh/MeshPrimitive.cpp b/engine/render/core/src/mesh/MeshPrimitive.cpp index b4cdc92e..f8f169ad 100644 --- a/engine/render/core/src/mesh/MeshPrimitive.cpp +++ b/engine/render/core/src/mesh/MeshPrimitive.cpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace sky { bool RenderBatch::UpdateBatchGroupLayout() noexcept @@ -134,12 +135,14 @@ namespace sky { } } - uint32_t RenderBatch::CalculatePipelineHash(uint32_t passHash) const noexcept + uint32_t RenderBatch::CalculatePipelineHash(const RenderBatchPrepareInfo& info) const noexcept { uint32_t hash = 0; + uint32_t passHash = info.pass->GetCompatibleHashWithSubPass(info.subPassId); HashCombine32(hash, techInst.program->GetHash()); HashCombine32(hash, passHash); + HashCombine32(hash, info.reverseZ); return hash; } @@ -150,8 +153,11 @@ namespace sky { void RenderBatch::BuildPipelineState(const RenderBatchPrepareInfo& info) noexcept { + auto state = techInst.technique->GetPipelineState(); + state.depthStencil.compareOp = DepthSettings::DepthCompareOp(info.reverseZ); + pso = GraphicsTechnique::BuildPso(techInst.program, - techInst.technique->GetPipelineState(), + state, geometry->Request(techInst.program), info.pass, info.subPassId); } @@ -160,7 +166,7 @@ namespace sky { { Update(info.pipelineKey); - uint32_t currentKey = CalculatePipelineHash(info.pass->GetCompatibleHashWithSubPass(info.subPassId)); + uint32_t currentKey = CalculatePipelineHash(info); if (currentKey != psoKey) { BuildPipelineState(info); psoKey = currentKey; diff --git a/engine/render/core/src/rdg/RenderSceneVisitor.cpp b/engine/render/core/src/rdg/RenderSceneVisitor.cpp index 37c54451..1f64824a 100644 --- a/engine/render/core/src/rdg/RenderSceneVisitor.cpp +++ b/engine/render/core/src/rdg/RenderSceneVisitor.cpp @@ -50,22 +50,25 @@ namespace sky::rdg { const auto &subPass = graph.subPasses[Index(queue.passID, graph)]; const auto &rasterPass = graph.rasterPasses[Index(subPass.parent, graph)]; + RenderItemGatherContext gatherContext(graph, queue, nullptr); + + bool isReverseZ = false; + uint8_t viewId = INVALID_VIEW_INDEX; + if (queue.viewID != INVALID_VERTEX) { + viewId = static_cast(Index(queue.viewID, graph)); + gatherContext.sceneView = graph.sceneViews[viewId].sceneView; + isReverseZ = gatherContext.sceneView->IsReverseZ(); + } RenderBatchPrepareInfo batchInfo = { .techId = queue.rasterID, .pipelineKey = rasterPass.pipelineKey, .pass = rasterPass.renderPass, - .subPassId = subPass.subPassID + .subPassId = subPass.subPassID, + .reverseZ = isReverseZ }; - RenderItemGatherContext gatherContext(graph, queue, nullptr); - - uint8_t viewId = INVALID_VIEW_INDEX; - if (queue.viewID != INVALID_VERTEX) { - viewId = static_cast(Index(queue.viewID, graph)); - gatherContext.sceneView = graph.sceneViews[viewId].sceneView; - } for (uint32_t i = 0; i < visibleInfos.size(); ++i) { if (!visibleInfos[i].IsActive()) { diff --git a/plugins/pvs/editor/src/PVSBakePipeline.cpp b/plugins/pvs/editor/src/PVSBakePipeline.cpp index f4788abf..12d1d789 100644 --- a/plugins/pvs/editor/src/PVSBakePipeline.cpp +++ b/plugins/pvs/editor/src/PVSBakePipeline.cpp @@ -227,12 +227,13 @@ namespace sky::editor { queue->Wait(id); auto buildCellPos = context->sortedCells[cellIndex]; uint8_t* cell = context->GetOrAllocateCellData(buildCellPos); - +#if 0 printf("CellID %u CellVisibleNum %u: ", cellIndex, CountBits(cell, context->cellDataSize)); for (uint32_t i = 0; i < context->cellDataSize; i++) { printf("%u ", static_cast(cell[i])); } printf("\n"); +#endif } From 571767b2c991f4b01c1df9cd0a699a1227522bf1 Mon Sep 17 00:00:00 2001 From: Zach Date: Tue, 10 Mar 2026 00:24:41 +0800 Subject: [PATCH 04/38] [feat]: update 3rdparty --- cmake/options.cmake | 6 ++ cmake/thirdparty.cmake | 30 +-------- cmake/thirdparty.json | 1 + cmake/thirdparty/FindGKlib.cmake | 27 +------- cmake/thirdparty/FindPerlinNoise.cmake | 13 +--- cmake/thirdparty/FindSPIRVCross.cmake | 40 ++---------- cmake/thirdparty/Findbullet3.cmake | 40 ++---------- cmake/thirdparty/Findcereal.cmake | 8 +-- cmake/thirdparty/Findcrc32.cmake | 25 +------ cmake/thirdparty/Findcxxopts.cmake | 8 +-- cmake/thirdparty/Findfreetype.cmake | 33 ++-------- cmake/thirdparty/Findglslang.cmake | 36 +++++----- cmake/thirdparty/Findgoogletest.cmake | 25 +------ cmake/thirdparty/Findimplot.cmake | 25 +------ cmake/thirdparty/Findlz4.cmake | 25 +------ cmake/thirdparty/Findmeshoptimizer.cmake | 25 +------ cmake/thirdparty/Findmetis.cmake | 29 +-------- cmake/thirdparty/Findmimalloc.cmake | 25 +------ cmake/thirdparty/Findnodeeditor.cmake | 27 +------- cmake/thirdparty/Findrapidjson.cmake | 8 +-- cmake/thirdparty/Findrecast.cmake | 35 ++-------- cmake/thirdparty/Findsdl.cmake | 4 +- cmake/thirdparty/Findsfmt.cmake | 25 +------ cmake/thirdparty/Findstb.cmake | 8 +-- cmake/thirdparty/Findtaskflow.cmake | 8 +-- cmake/thirdparty/Findvma.cmake | 13 +--- cmake/thirdparty/Findvolk.cmake | 8 +-- cmake/thirdparty/Findzlib.cmake | 25 +------ cmake/thirdparty_helpers.cmake | 77 ++++++++++++++++++++++ python/third_party.py | 83 ++++++++++++++++++++---- 30 files changed, 214 insertions(+), 528 deletions(-) create mode 100644 cmake/thirdparty_helpers.cmake diff --git a/cmake/options.cmake b/cmake/options.cmake index 836f4e33..359ff9f2 100644 --- a/cmake/options.cmake +++ b/cmake/options.cmake @@ -14,5 +14,11 @@ option(SKY_USE_TRACY "use tracy profiler" OFF) # todo: controlled by project config json. option(SKY_BUILD_XR "xr plugin" OFF) option(SKY_BUILD_PYTHON "python plugin" OFF) +option(SKY_BUILD_CPYTHON "cpython plugin" OFF) +option(SKY_BUILD_COMPRESSION "compression support" OFF) +option(SKY_BUILD_FREETYPE "freetype text rendering" OFF) +option(SKY_BUILD_BULLET "bullet physics" OFF) +option(SKY_BUILD_RECAST "recast navigation" OFF) +option(SKY_BUILD_TOOL "build tools" OFF) option(SKY_MATH_SIMD "enable simd math" OFF) \ No newline at end of file diff --git a/cmake/thirdparty.cmake b/cmake/thirdparty.cmake index cb571dcc..1f25f7bb 100644 --- a/cmake/thirdparty.cmake +++ b/cmake/thirdparty.cmake @@ -1,4 +1,5 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ENGINE_ROOT}/cmake/thirdparty) +include(${ENGINE_ROOT}/cmake/thirdparty_helpers.cmake) function(sky_find_3rd) cmake_parse_arguments(TMP @@ -15,43 +16,22 @@ function(sky_find_3rd) endif() endfunction() -#set(SKY_3RD_REMOTE "115.159.67.235") -#set(SKY_3RD_PACKAGE_NAME Sky3rd-${CMAKE_SYSTEM_NAME}.rar) -#set(SKY_3RD_REMOTE_URL ${SKY_3RD_REMOTE}/${SKY_3RD_PACKAGE_NAME}) -#set(SKY_3RD_SAVE_PATH ${3RD_PATH}/${SKY_3RD_PACKAGE_NAME}) -# -#message("[3rdPath]:" ${SKY_3RD_SAVE_PATH}) -#if (NOT EXISTS ${SKY_3RD_SAVE_PATH}) -# file(DOWNLOAD -# ${SKY_3RD_REMOTE_URL} ${SKY_3RD_SAVE_PATH} -# SHOW_PROGRESS -# ) -# file(ARCHIVE_EXTRACT -# INPUT ${SKY_3RD_SAVE_PATH} -# DESTINATION ${3RD_PATH} -# ) -#endif() - if(EXISTS ${3RD_PATH}) # core sky_find_3rd(TARGET crc32 DIR crc32c) sky_find_3rd(TARGET sfmt DIR sfmt) sky_find_3rd(TARGET boost DIR boost) sky_find_3rd(TARGET taskflow DIR taskflow) -# sky_find_3rd(TARGET mimalloc DIR mimalloc) # framework sky_find_3rd(TARGET rapidjson DIR rapidjson) sky_find_3rd(TARGET sdl DIR sdl) -# sky_find_3rd(TARGET sqlite DIR sqlite) # vulkan -# sky_find_3rd(TARGET volk DIR volk) sky_find_3rd(TARGET vma DIR vma) # imgui sky_find_3rd(TARGET imgui DIR imgui) -# sky_find_3rd(TARGET implot DIR implot) # temp sky_find_3rd(TARGET cxxopts DIR cxxopts) @@ -104,14 +84,6 @@ if(EXISTS ${3RD_PATH}) sky_find_3rd(TARGET ImGuizmo DIR ImGuizmo) sky_find_3rd(TARGET nodeeditor DIR nodeeditor) endif () - -# if (WIN32 OR ANDROID) -# sky_find_3rd(TARGET gles DIR gles) -# endif() -# -# if (SKY_BUILD_XR) -# sky_find_3rd(TARGET OpenXR DIR OpenXR_SDK) -# endif () else() message(FATAL_ERROR "3rdParty folder: ${3RD_PATH} does not exist, call cmake defining a valid 3RD_PATH") endif() diff --git a/cmake/thirdparty.json b/cmake/thirdparty.json index c4fd5f72..9f1ee099 100644 --- a/cmake/thirdparty.json +++ b/cmake/thirdparty.json @@ -302,6 +302,7 @@ { "name": "stb", "url": "https://github.com/nothings/stb.git", + "tag": "f75e8d1cad7d90d72ef7a4661f1b994ef78b4e31", "header_only": true, "platforms": ["Win32", "MacOS-x86", "MacOS-arm"] }, diff --git a/cmake/thirdparty/FindGKlib.cmake b/cmake/thirdparty/FindGKlib.cmake index d0f64061..f659f864 100644 --- a/cmake/thirdparty/FindGKlib.cmake +++ b/cmake/thirdparty/FindGKlib.cmake @@ -1,26 +1 @@ -set(LIB_NAME "GKlib") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -if (TARGET ${TARGET_WITH_NAMESPACE}) - return() -endif() - -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) -set(${LIB_NAME}_LIBS_DIR ${${LIB_NAME}_PATH}/lib) - -set(${LIB_NAME}_LIBRARY_DEBUG - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}GKlib${CMAKE_STATIC_LIBRARY_SUFFIX} -) - -set(${LIB_NAME}_LIBRARY_RELEASE - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}GKlib${CMAKE_STATIC_LIBRARY_SUFFIX} -) - -set(${LIB_NAME}_LIBRARY - "$<$:${${LIB_NAME}_LIBRARY_RELEASE}>" - "$<$:${${LIB_NAME}_LIBRARY_DEBUG}>") - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -target_link_libraries(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_LIBRARY}) - -set(${LIB_NAME}_FOUND True) +sky_3rd_static(GKlib LIBS GKlib) diff --git a/cmake/thirdparty/FindPerlinNoise.cmake b/cmake/thirdparty/FindPerlinNoise.cmake index 83a39bac..d16a6a39 100644 --- a/cmake/thirdparty/FindPerlinNoise.cmake +++ b/cmake/thirdparty/FindPerlinNoise.cmake @@ -1,12 +1 @@ -set(LIB_NAME "PerlinNoise") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -if (TARGET ${TARGET_WITH_NAMESPACE}) - return() -endif() - -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) - -set(${LIB_NAME}_FOUND True) +sky_3rd_header_only(PerlinNoise) diff --git a/cmake/thirdparty/FindSPIRVCross.cmake b/cmake/thirdparty/FindSPIRVCross.cmake index 59e84122..90d6e777 100644 --- a/cmake/thirdparty/FindSPIRVCross.cmake +++ b/cmake/thirdparty/FindSPIRVCross.cmake @@ -1,38 +1,10 @@ set(LIB_NAME "SPIRVCross") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -if (TARGET ${TARGET_WITH_NAMESPACE}) - return() -endif() - -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) -set(${LIB_NAME}_LIBS_DIR ${${LIB_NAME}_PATH}/lib) - if (MSVC) - set(DEBUG_SUFFIX "d") + set(DBG_SUFFIX d) endif() -set(${LIB_NAME}_LIBRARY_DEBUG - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}spirv-cross-glsl${DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}spirv-cross-msl${DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}spirv-cross-cpp${DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}spirv-cross-core${DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}spirv-cross-reflect${DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}spirv-cross-util${DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}) - -set(${LIB_NAME}_LIBRARY_RELEASE - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}spirv-cross-cpp${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}spirv-cross-glsl${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}spirv-cross-msl${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}spirv-cross-core${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}spirv-cross-reflect${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}spirv-cross-util${CMAKE_STATIC_LIBRARY_SUFFIX}) - -set(${LIB_NAME}_LIBRARY - "$<$:${${LIB_NAME}_LIBRARY_RELEASE}>" - "$<$:${${LIB_NAME}_LIBRARY_DEBUG}>") - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -target_link_libraries(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_LIBRARY}) - -set(${LIB_NAME}_FOUND True) +sky_3rd_static(${LIB_NAME} + DEBUG_SUFFIX ${DBG_SUFFIX} + LIBS spirv-cross-glsl spirv-cross-msl spirv-cross-cpp + spirv-cross-core spirv-cross-reflect spirv-cross-util +) diff --git a/cmake/thirdparty/Findbullet3.cmake b/cmake/thirdparty/Findbullet3.cmake index a4706006..a7954093 100644 --- a/cmake/thirdparty/Findbullet3.cmake +++ b/cmake/thirdparty/Findbullet3.cmake @@ -1,36 +1,6 @@ -set(LIB_NAME "bullet3") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include/bullet) -set(${LIB_NAME}_LIBS_DIR ${${LIB_NAME}_PATH}/lib) - -set(LIB_SUFFIX "d") - -set(${LIB_NAME}_LIBRARY_DEBUG - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}Bullet3Collision${LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}Bullet3Common${LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}Bullet3Dynamics${LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}Bullet3Geometry${LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}BulletCollision${LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}BulletDynamics${LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}LinearMath${LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} +sky_3rd_static(bullet3 + INCLUDE_SUBDIR bullet + DEBUG_SUFFIX d + LIBS Bullet3Collision Bullet3Common Bullet3Dynamics Bullet3Geometry + BulletCollision BulletDynamics LinearMath ) - -set(${LIB_NAME}_LIBRARY_RELEASE - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}Bullet3Collision${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}Bullet3Common${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}Bullet3Dynamics${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}Bullet3Geometry${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}BulletCollision${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}BulletDynamics${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}LinearMath${CMAKE_STATIC_LIBRARY_SUFFIX} -) - -set(${LIB_NAME}_LIBRARY - "$<$:${${LIB_NAME}_LIBRARY_RELEASE}>" - "$<$:${${LIB_NAME}_LIBRARY_DEBUG}>") - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -target_link_libraries(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_LIBRARY}) - -set(${LIB_NAME}_FOUND True) diff --git a/cmake/thirdparty/Findcereal.cmake b/cmake/thirdparty/Findcereal.cmake index 0d5bf934..19009da4 100644 --- a/cmake/thirdparty/Findcereal.cmake +++ b/cmake/thirdparty/Findcereal.cmake @@ -1,7 +1 @@ -set(LIB_NAME "cereal") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -set(${LIB_NAME}_FOUND True) \ No newline at end of file +sky_3rd_header_only(cereal) \ No newline at end of file diff --git a/cmake/thirdparty/Findcrc32.cmake b/cmake/thirdparty/Findcrc32.cmake index 46fdb33b..97983f03 100644 --- a/cmake/thirdparty/Findcrc32.cmake +++ b/cmake/thirdparty/Findcrc32.cmake @@ -1,24 +1 @@ -set(LIB_NAME "crc32") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -if (TARGET ${TARGET_WITH_NAMESPACE}) - return() -endif() - -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) -set(${LIB_NAME}_LIBS_DIR ${${LIB_NAME}_PATH}/lib) - -set(${LIB_NAME}_LIBRARY_DEBUG - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}crc32c${CMAKE_STATIC_LIBRARY_SUFFIX}) - -set(${LIB_NAME}_LIBRARY_RELEASE - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}crc32c${CMAKE_STATIC_LIBRARY_SUFFIX}) - -set(${LIB_NAME}_LIBRARY - "$<$:${${LIB_NAME}_LIBRARY_RELEASE}>" - "$<$:${${LIB_NAME}_LIBRARY_DEBUG}>") - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -target_link_libraries(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_LIBRARY}) - -set(${LIB_NAME}_FOUND True) +sky_3rd_static(crc32 LIBS crc32c) diff --git a/cmake/thirdparty/Findcxxopts.cmake b/cmake/thirdparty/Findcxxopts.cmake index 1f1f0b6c..63f462ac 100644 --- a/cmake/thirdparty/Findcxxopts.cmake +++ b/cmake/thirdparty/Findcxxopts.cmake @@ -1,7 +1 @@ -set(LIB_NAME "cxxopts") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -set(${LIB_NAME}_FOUND True) \ No newline at end of file +sky_3rd_header_only(cxxopts) \ No newline at end of file diff --git a/cmake/thirdparty/Findfreetype.cmake b/cmake/thirdparty/Findfreetype.cmake index 6f486955..73a87c8f 100644 --- a/cmake/thirdparty/Findfreetype.cmake +++ b/cmake/thirdparty/Findfreetype.cmake @@ -1,33 +1,12 @@ set(LIB_NAME "freetype") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -if (TARGET ${TARGET_WITH_NAMESPACE}) - return() -endif() - if (APPLE) sky_find_3rd(TARGET zlib DIR zlib) set(FT_EXT_LIBS 3rdParty::zlib) -endif () - -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include/freetype2) -set(${LIB_NAME}_LIBS_DIR ${${LIB_NAME}_PATH}/lib) - -set(${LIB_NAME}_LIBRARY_DEBUG - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}freetyped${CMAKE_STATIC_LIBRARY_SUFFIX} - ${FT_EXT_LIBS} -) +endif() -set(${LIB_NAME}_LIBRARY_RELEASE - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}freetype${CMAKE_STATIC_LIBRARY_SUFFIX} - ${FT_EXT_LIBS} +sky_3rd_static(${LIB_NAME} + INCLUDE_SUBDIR freetype2 + DEBUG_SUFFIX d + LIBS freetype + EXT_LIBS ${FT_EXT_LIBS} ) - -set(${LIB_NAME}_LIBRARY - "$<$:${${LIB_NAME}_LIBRARY_RELEASE}>" - "$<$:${${LIB_NAME}_LIBRARY_DEBUG}>") - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -target_link_libraries(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_LIBRARY}) - -set(${LIB_NAME}_FOUND True) diff --git a/cmake/thirdparty/Findglslang.cmake b/cmake/thirdparty/Findglslang.cmake index e6f6f6d6..1a2b6f98 100644 --- a/cmake/thirdparty/Findglslang.cmake +++ b/cmake/thirdparty/Findglslang.cmake @@ -13,26 +13,26 @@ endif () if (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") set(${LIB_NAME}_LIBRARY_DEBUG - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}GenericCodeGen${LIB_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}glslang${LIB_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}glslang-default-resource-limits${LIB_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}MachineIndependent${LIB_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}OSDependent${LIB_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}SPIRV${LIB_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}SPVRemapper${LIB_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}SPIRV-Tools${LIB_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}SPIRV-Tools-opt${LIB_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}) + ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}GenericCodeGen${CMAKE_STATIC_LIBRARY_SUFFIX} + ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}glslang${CMAKE_STATIC_LIBRARY_SUFFIX} + ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}glslang-default-resource-limits${CMAKE_STATIC_LIBRARY_SUFFIX} + ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}MachineIndependent${CMAKE_STATIC_LIBRARY_SUFFIX} + ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}OSDependent${CMAKE_STATIC_LIBRARY_SUFFIX} + ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}SPIRV${CMAKE_STATIC_LIBRARY_SUFFIX} + ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}SPVRemapper${CMAKE_STATIC_LIBRARY_SUFFIX} + ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}SPIRV-Tools${CMAKE_STATIC_LIBRARY_SUFFIX} + ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}SPIRV-Tools-opt${CMAKE_STATIC_LIBRARY_SUFFIX}) set(${LIB_NAME}_LIBRARY_RELEASE - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}GenericCodeGen${LIB_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}glslang${LIB_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}glslang-default-resource-limits${LIB_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}MachineIndependent${LIB_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}OSDependent${LIB_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}SPIRV${LIB_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}SPVRemapper${LIB_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}SPIRV-Tools${LIB_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}SPIRV-Tools-opt${LIB_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}) + ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}GenericCodeGen${CMAKE_STATIC_LIBRARY_SUFFIX} + ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}glslang${CMAKE_STATIC_LIBRARY_SUFFIX} + ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}glslang-default-resource-limits${CMAKE_STATIC_LIBRARY_SUFFIX} + ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}MachineIndependent${CMAKE_STATIC_LIBRARY_SUFFIX} + ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}OSDependent${CMAKE_STATIC_LIBRARY_SUFFIX} + ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}SPIRV${CMAKE_STATIC_LIBRARY_SUFFIX} + ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}SPVRemapper${CMAKE_STATIC_LIBRARY_SUFFIX} + ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}SPIRV-Tools${CMAKE_STATIC_LIBRARY_SUFFIX} + ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}SPIRV-Tools-opt${CMAKE_STATIC_LIBRARY_SUFFIX}) else () set(${LIB_NAME}_LIBRARY_DEBUG ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}GenericCodeGen${LIB_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} diff --git a/cmake/thirdparty/Findgoogletest.cmake b/cmake/thirdparty/Findgoogletest.cmake index 8a2164c1..a8fc2680 100644 --- a/cmake/thirdparty/Findgoogletest.cmake +++ b/cmake/thirdparty/Findgoogletest.cmake @@ -1,24 +1 @@ -set(LIB_NAME "googletest") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -if (TARGET ${TARGET_WITH_NAMESPACE}) - return() -endif() - -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) -set(${LIB_NAME}_LIBS_DIR ${${LIB_NAME}_PATH}/lib) - -set(${LIB_NAME}_LIBRARY_DEBUG - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}) - -set(${LIB_NAME}_LIBRARY_RELEASE - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}) - -set(${LIB_NAME}_LIBRARY - "$<$:${${LIB_NAME}_LIBRARY_RELEASE}>" - "$<$:${${LIB_NAME}_LIBRARY_DEBUG}>") - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -target_link_libraries(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_LIBRARY}) - -set(${LIB_NAME}_FOUND True) +sky_3rd_static(googletest LIBS gtest) diff --git a/cmake/thirdparty/Findimplot.cmake b/cmake/thirdparty/Findimplot.cmake index 39f4ccb8..5f6b1666 100644 --- a/cmake/thirdparty/Findimplot.cmake +++ b/cmake/thirdparty/Findimplot.cmake @@ -1,24 +1 @@ -set(LIB_NAME "implot") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -if (TARGET ${TARGET_WITH_NAMESPACE}) - return() -endif() - -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) -set(${LIB_NAME}_LIBS_DIR ${${LIB_NAME}_PATH}/lib) - -set(${LIB_NAME}_LIBRARY_DEBUG - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}ImPlot${CMAKE_STATIC_LIBRARY_SUFFIX}) - -set(${LIB_NAME}_LIBRARY_RELEASE - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}ImPlot${CMAKE_STATIC_LIBRARY_SUFFIX}) - -set(${LIB_NAME}_LIBRARY - "$<$:${${LIB_NAME}_LIBRARY_RELEASE}>" - "$<$:${${LIB_NAME}_LIBRARY_DEBUG}>") - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -target_link_libraries(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_LIBRARY}) - -set(${LIB_NAME}_FOUND True) +sky_3rd_static(implot LIBS ImPlot) diff --git a/cmake/thirdparty/Findlz4.cmake b/cmake/thirdparty/Findlz4.cmake index 963e5758..40266a31 100644 --- a/cmake/thirdparty/Findlz4.cmake +++ b/cmake/thirdparty/Findlz4.cmake @@ -1,24 +1 @@ -set(LIB_NAME "lz4") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -if (TARGET ${TARGET_WITH_NAMESPACE}) - return() -endif() - -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) -set(${LIB_NAME}_LIBS_DIR ${${LIB_NAME}_PATH}/lib) -set(${LIB_NAME}_BIN_DIR ${${LIB_NAME}_PATH}/bin) - -set(${LIB_NAME}_LIBRARY_DEBUG - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}lz4${CMAKE_STATIC_LIBRARY_SUFFIX}) - -set(${LIB_NAME}_LIBRARY_RELEASE - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}lz4${CMAKE_STATIC_LIBRARY_SUFFIX}) - -set(${LIB_NAME}_LIBRARY - "$<$:${${LIB_NAME}_LIBRARY_RELEASE}>" - "$<$:${${LIB_NAME}_LIBRARY_DEBUG}>") - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -target_link_libraries(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_LIBRARY}) -set(${LIB_NAME}_FOUND True) +sky_3rd_static(lz4 LIBS lz4) diff --git a/cmake/thirdparty/Findmeshoptimizer.cmake b/cmake/thirdparty/Findmeshoptimizer.cmake index 3639829a..00f58cb5 100644 --- a/cmake/thirdparty/Findmeshoptimizer.cmake +++ b/cmake/thirdparty/Findmeshoptimizer.cmake @@ -1,24 +1 @@ -set(LIB_NAME "meshoptimizer") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -if (TARGET ${TARGET_WITH_NAMESPACE}) - return() -endif() - -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) -set(${LIB_NAME}_LIBS_DIR ${${LIB_NAME}_PATH}/lib) - -set(${LIB_NAME}_LIBRARY_DEBUG - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}meshoptimizer${CMAKE_STATIC_LIBRARY_SUFFIX}) - -set(${LIB_NAME}_LIBRARY_RELEASE - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}meshoptimizer${CMAKE_STATIC_LIBRARY_SUFFIX}) - -set(${LIB_NAME}_LIBRARY - "$<$:${${LIB_NAME}_LIBRARY_RELEASE}>" - "$<$:${${LIB_NAME}_LIBRARY_DEBUG}>") - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -target_link_libraries(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_LIBRARY}) - -set(${LIB_NAME}_FOUND True) +sky_3rd_static(meshoptimizer LIBS meshoptimizer) diff --git a/cmake/thirdparty/Findmetis.cmake b/cmake/thirdparty/Findmetis.cmake index 54f35f4b..272e347d 100644 --- a/cmake/thirdparty/Findmetis.cmake +++ b/cmake/thirdparty/Findmetis.cmake @@ -1,28 +1 @@ -set(LIB_NAME "metis") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -if (TARGET ${TARGET_WITH_NAMESPACE}) - return() -endif() - -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) -set(${LIB_NAME}_LIBS_DIR ${${LIB_NAME}_PATH}/lib) - -set(${LIB_NAME}_LIBRARY_DEBUG - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}metis${CMAKE_STATIC_LIBRARY_SUFFIX} -# ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}GKlib${CMAKE_STATIC_LIBRARY_SUFFIX} -) - -set(${LIB_NAME}_LIBRARY_RELEASE - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}metis${CMAKE_STATIC_LIBRARY_SUFFIX} -# ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}GKlib${CMAKE_STATIC_LIBRARY_SUFFIX} -) - -set(${LIB_NAME}_LIBRARY - "$<$:${${LIB_NAME}_LIBRARY_RELEASE}>" - "$<$:${${LIB_NAME}_LIBRARY_DEBUG}>") - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -target_link_libraries(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_LIBRARY}) - -set(${LIB_NAME}_FOUND True) +sky_3rd_static(metis LIBS metis) diff --git a/cmake/thirdparty/Findmimalloc.cmake b/cmake/thirdparty/Findmimalloc.cmake index a3e88f5f..62d21490 100644 --- a/cmake/thirdparty/Findmimalloc.cmake +++ b/cmake/thirdparty/Findmimalloc.cmake @@ -1,24 +1 @@ -set(LIB_NAME "mimalloc") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -if (TARGET ${TARGET_WITH_NAMESPACE}) - return() -endif() - -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) -set(${LIB_NAME}_LIBS_DIR ${${LIB_NAME}_PATH}/lib) - -set(${LIB_NAME}_LIBRARY_DEBUG - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}mimalloc${CMAKE_STATIC_LIBRARY_SUFFIX}) - -set(${LIB_NAME}_LIBRARY_RELEASE - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}mimalloc${CMAKE_STATIC_LIBRARY_SUFFIX}) - -set(${LIB_NAME}_LIBRARY - "$<$:${${LIB_NAME}_LIBRARY_RELEASE}>" - "$<$:${${LIB_NAME}_LIBRARY_DEBUG}>") - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -target_link_libraries(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_LIBRARY}) - -set(${LIB_NAME}_FOUND True) +sky_3rd_static(mimalloc LIBS mimalloc) diff --git a/cmake/thirdparty/Findnodeeditor.cmake b/cmake/thirdparty/Findnodeeditor.cmake index db878131..5ce850aa 100644 --- a/cmake/thirdparty/Findnodeeditor.cmake +++ b/cmake/thirdparty/Findnodeeditor.cmake @@ -1,26 +1 @@ -set(LIB_NAME "nodeeditor") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -if (TARGET ${TARGET_WITH_NAMESPACE}) - return() -endif() - -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) -set(${LIB_NAME}_LIBS_DIR ${${LIB_NAME}_PATH}/lib) - -set(${LIB_NAME}_LIBRARY_DEBUG - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}QtNodes${CMAKE_STATIC_LIBRARY_SUFFIX} -) - -set(${LIB_NAME}_LIBRARY_RELEASE - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}QtNodes${CMAKE_STATIC_LIBRARY_SUFFIX} -) - -set(${LIB_NAME}_LIBRARY - "$<$:${${LIB_NAME}_LIBRARY_RELEASE}>" - "$<$:${${LIB_NAME}_LIBRARY_DEBUG}>") - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -target_link_libraries(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_LIBRARY}) - -set(${LIB_NAME}_FOUND True) +sky_3rd_static(nodeeditor LIBS QtNodes) diff --git a/cmake/thirdparty/Findrapidjson.cmake b/cmake/thirdparty/Findrapidjson.cmake index 1dd67af9..e670c96f 100644 --- a/cmake/thirdparty/Findrapidjson.cmake +++ b/cmake/thirdparty/Findrapidjson.cmake @@ -1,7 +1 @@ -set(LIB_NAME "rapidjson") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -set(${LIB_NAME}_FOUND True) \ No newline at end of file +sky_3rd_header_only(rapidjson) \ No newline at end of file diff --git a/cmake/thirdparty/Findrecast.cmake b/cmake/thirdparty/Findrecast.cmake index 3c9a5c1b..368a269a 100644 --- a/cmake/thirdparty/Findrecast.cmake +++ b/cmake/thirdparty/Findrecast.cmake @@ -1,32 +1,5 @@ -set(LIB_NAME "recast") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include/recastnavigation) -set(${LIB_NAME}_LIBS_DIR ${${LIB_NAME}_PATH}/lib) - -set(LIB_SUFFIX "-d") - -set(${LIB_NAME}_LIBRARY_DEBUG - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}DebugUtils${LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}DetourCrowd${LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}Detour${LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}DetourTileCache${LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}Recast${LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} +sky_3rd_static(recast + INCLUDE_SUBDIR recastnavigation + DEBUG_SUFFIX -d + LIBS DebugUtils DetourCrowd Detour DetourTileCache Recast ) - -set(${LIB_NAME}_LIBRARY_RELEASE - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}DebugUtils${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}DetourCrowd${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}Detour${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}DetourTileCache${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}Recast${CMAKE_STATIC_LIBRARY_SUFFIX} -) - -set(${LIB_NAME}_LIBRARY - "$<$:${${LIB_NAME}_LIBRARY_RELEASE}>" - "$<$:${${LIB_NAME}_LIBRARY_DEBUG}>") - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -target_link_libraries(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_LIBRARY}) - -set(${LIB_NAME}_FOUND True) diff --git a/cmake/thirdparty/Findsdl.cmake b/cmake/thirdparty/Findsdl.cmake index 551af074..36a0d21a 100644 --- a/cmake/thirdparty/Findsdl.cmake +++ b/cmake/thirdparty/Findsdl.cmake @@ -22,12 +22,10 @@ if (MSVC) elseif (APPLE) set(${LIB_NAME}_LIBRARY_DEBUG ${${LIB_NAME}_LIBS_DIR}/Debug/libSDL2d.a - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}SDL2d${DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}SDL2maind${DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}) + ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}SDL2maind${CMAKE_STATIC_LIBRARY_SUFFIX}) set(${LIB_NAME}_LIBRARY_RELEASE ${${LIB_NAME}_LIBS_DIR}/Release/libSDL2.a - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}SDL2d${CMAKE_STATIC_LIBRARY_SUFFIX} ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}SDL2main${CMAKE_STATIC_LIBRARY_SUFFIX}) endif() diff --git a/cmake/thirdparty/Findsfmt.cmake b/cmake/thirdparty/Findsfmt.cmake index ff14db63..2f338e5c 100644 --- a/cmake/thirdparty/Findsfmt.cmake +++ b/cmake/thirdparty/Findsfmt.cmake @@ -1,24 +1 @@ -set(LIB_NAME "sfmt") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -if (TARGET ${TARGET_WITH_NAMESPACE}) - return() -endif() - -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) -set(${LIB_NAME}_LIBS_DIR ${${LIB_NAME}_PATH}/lib) - -set(${LIB_NAME}_LIBRARY_DEBUG - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}sfmt${CMAKE_STATIC_LIBRARY_SUFFIX}) - -set(${LIB_NAME}_LIBRARY_RELEASE - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}sfmt${CMAKE_STATIC_LIBRARY_SUFFIX}) - -set(${LIB_NAME}_LIBRARY - "$<$:${${LIB_NAME}_LIBRARY_RELEASE}>" - "$<$:${${LIB_NAME}_LIBRARY_DEBUG}>") - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -target_link_libraries(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_LIBRARY}) - -set(${LIB_NAME}_FOUND True) +sky_3rd_static(sfmt LIBS sfmt) diff --git a/cmake/thirdparty/Findstb.cmake b/cmake/thirdparty/Findstb.cmake index 36b19525..9b6aecde 100644 --- a/cmake/thirdparty/Findstb.cmake +++ b/cmake/thirdparty/Findstb.cmake @@ -1,7 +1 @@ -set(LIB_NAME "stb") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -set(${LIB_NAME}_FOUND True) \ No newline at end of file +sky_3rd_header_only(stb) \ No newline at end of file diff --git a/cmake/thirdparty/Findtaskflow.cmake b/cmake/thirdparty/Findtaskflow.cmake index 00d7dea7..29eeee48 100644 --- a/cmake/thirdparty/Findtaskflow.cmake +++ b/cmake/thirdparty/Findtaskflow.cmake @@ -1,7 +1 @@ -set(LIB_NAME "taskflow") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -set(${LIB_NAME}_FOUND True) \ No newline at end of file +sky_3rd_header_only(taskflow) \ No newline at end of file diff --git a/cmake/thirdparty/Findvma.cmake b/cmake/thirdparty/Findvma.cmake index 62684a17..dde5012e 100644 --- a/cmake/thirdparty/Findvma.cmake +++ b/cmake/thirdparty/Findvma.cmake @@ -1,12 +1 @@ -set(LIB_NAME "vma") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -if (TARGET ${TARGET_WITH_NAMESPACE}) - return() -endif() - -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) - -set(${LIB_NAME}_FOUND True) +sky_3rd_header_only(vma) diff --git a/cmake/thirdparty/Findvolk.cmake b/cmake/thirdparty/Findvolk.cmake index 51439f0d..2255efe9 100644 --- a/cmake/thirdparty/Findvolk.cmake +++ b/cmake/thirdparty/Findvolk.cmake @@ -1,7 +1 @@ -set(LIB_NAME "volk") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -set(${LIB_NAME}_FOUND True) +sky_3rd_header_only(volk) diff --git a/cmake/thirdparty/Findzlib.cmake b/cmake/thirdparty/Findzlib.cmake index 000b1bb5..5d08859d 100644 --- a/cmake/thirdparty/Findzlib.cmake +++ b/cmake/thirdparty/Findzlib.cmake @@ -1,24 +1 @@ -set(LIB_NAME "zlib") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -if (TARGET ${TARGET_WITH_NAMESPACE}) - return() -endif() - -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) -set(${LIB_NAME}_LIBS_DIR ${${LIB_NAME}_PATH}/lib) - -set(${LIB_NAME}_LIBRARY_DEBUG - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}z${CMAKE_STATIC_LIBRARY_SUFFIX}) - -set(${LIB_NAME}_LIBRARY_RELEASE - ${${LIB_NAME}_LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}z${CMAKE_STATIC_LIBRARY_SUFFIX}) - -set(${LIB_NAME}_LIBRARY - "$<$:${${LIB_NAME}_LIBRARY_RELEASE}>" - "$<$:${${LIB_NAME}_LIBRARY_DEBUG}>") - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -target_link_libraries(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_LIBRARY}) - -set(${LIB_NAME}_FOUND True) +sky_3rd_static(zlib LIBS z) diff --git a/cmake/thirdparty_helpers.cmake b/cmake/thirdparty_helpers.cmake new file mode 100644 index 00000000..090d8de4 --- /dev/null +++ b/cmake/thirdparty_helpers.cmake @@ -0,0 +1,77 @@ +# Helper functions to reduce boilerplate in Find*.cmake modules. +# Include this file BEFORE any find_package() calls. + +# Creates an INTERFACE IMPORTED target for header-only libraries. +# Usage: +# sky_3rd_header_only( [INCLUDE_SUBDIR ]) +# Example: +# sky_3rd_header_only(rapidjson) +# sky_3rd_header_only(freetype INCLUDE_SUBDIR freetype2) +function(sky_3rd_header_only LIB_NAME) + cmake_parse_arguments(ARGS "" "INCLUDE_SUBDIR" "" ${ARGN}) + set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") + if (TARGET ${TARGET_WITH_NAMESPACE}) + set(${LIB_NAME}_FOUND True PARENT_SCOPE) + return() + endif() + + if (ARGS_INCLUDE_SUBDIR) + set(INC_DIR ${${LIB_NAME}_PATH}/include/${ARGS_INCLUDE_SUBDIR}) + else() + set(INC_DIR ${${LIB_NAME}_PATH}/include) + endif() + + add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) + target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${INC_DIR}) + set(${LIB_NAME}_FOUND True PARENT_SCOPE) +endfunction() + +# Creates an INTERFACE IMPORTED target for prebuilt static libraries. +# Handles Debug/Release paths and optional debug postfix. +# Usage: +# sky_3rd_static( LIBS [lib2 ...] +# [DEBUG_SUFFIX ] +# [INCLUDE_SUBDIR ] +# [EXT_LIBS ...]) +# Examples: +# sky_3rd_static(crc32 LIBS crc32c) +# sky_3rd_static(bullet3 INCLUDE_SUBDIR bullet DEBUG_SUFFIX d +# LIBS Bullet3Collision Bullet3Common Bullet3Dynamics +# Bullet3Geometry BulletCollision BulletDynamics LinearMath) +function(sky_3rd_static LIB_NAME) + cmake_parse_arguments(ARGS "" "DEBUG_SUFFIX;INCLUDE_SUBDIR" "LIBS;EXT_LIBS" ${ARGN}) + set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") + if (TARGET ${TARGET_WITH_NAMESPACE}) + set(${LIB_NAME}_FOUND True PARENT_SCOPE) + return() + endif() + + if (ARGS_INCLUDE_SUBDIR) + set(INC_DIR ${${LIB_NAME}_PATH}/include/${ARGS_INCLUDE_SUBDIR}) + else() + set(INC_DIR ${${LIB_NAME}_PATH}/include) + endif() + + set(LIBS_DIR ${${LIB_NAME}_PATH}/lib) + + foreach(lib ${ARGS_LIBS}) + list(APPEND DEBUG_LIBS + ${LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}${lib}${ARGS_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}) + list(APPEND RELEASE_LIBS + ${LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}${lib}${CMAKE_STATIC_LIBRARY_SUFFIX}) + endforeach() + + if (ARGS_EXT_LIBS) + list(APPEND DEBUG_LIBS ${ARGS_EXT_LIBS}) + list(APPEND RELEASE_LIBS ${ARGS_EXT_LIBS}) + endif() + + set(LIBRARY + "$<$:${DEBUG_LIBS}>" + "$<$:${RELEASE_LIBS}>") + + add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) + target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${INC_DIR}) + target_link_libraries(${TARGET_WITH_NAMESPACE} INTERFACE ${LIBRARY}) + set(${LIB_NAME}_FOUND True PARENT_SCOPE) +endfunction() diff --git a/python/third_party.py b/python/third_party.py index 50654be6..b7b6edb6 100644 --- a/python/third_party.py +++ b/python/third_party.py @@ -3,17 +3,20 @@ import os import shutil import subprocess +import sys from pathlib import Path from git import Repo -parser = argparse.ArgumentParser(description='腓坂') +parser = argparse.ArgumentParser(description='SkyEngine 筝劫膽莚綏ュ') parser.add_argument('-i', '--intermediate', type=str, help='筝贋篁') parser.add_argument('-o', '--output', type=str, help='莨肴君緇') parser.add_argument('-e', '--engine', type=str, help='綣綵') parser.add_argument('-t', '--target', type=str, help='膽莚筝') parser.add_argument('-p', '--platform', type=str, choices=["Win32", "MacOS-x86", "MacOS-arm", "Android", "IOS", "Linux"], help='膽莚綛喝') parser.add_argument('-c', '--clean', action='store_true', default=False, help='羝綏ョ') +parser.add_argument('-j', '--jobs', type=int, default=0, help='綛区膽莚膾睡 (0=)') +parser.add_argument('--list', action='store_true', default=False, help='堺篆≧') args = parser.parse_args() tool_chain = { @@ -44,27 +47,32 @@ def run_cmake(build_dir: str, source_dir: str, build_type, options: dict = None, try: # цCMake臀 + print(f" [configure] {source_dir}") process = subprocess.run(cmake_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - print(process) # ц Build build_cmd = ["cmake", "--build", build_dir, "--config", build_type] + if args.jobs > 0: + build_cmd.extend(["--parallel", str(args.jobs)]) + else: + build_cmd.append("--parallel") + print(f" [build] {build_type}") process = subprocess.run(build_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - print(process) # ц Install - build_cmd = ["cmake", "--install", build_dir, "--config", build_type] + install_cmd = ["cmake", "--install", build_dir, "--config", build_type] if components: for component in components: - build_cmd.extend(["--component", component]) + install_cmd.extend(["--component", component]) - process = subprocess.run(build_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - print(process) + print(f" [install] {build_type}") + process = subprocess.run(install_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - print("CMakeц!") + print(f" [done] {build_type} ") except subprocess.CalledProcessError as e: - print(f"CMakeц紊沿乾: {e.stderr}") + stderr_text = e.stderr.decode('utf-8', errors='replace') if isinstance(e.stderr, bytes) else str(e.stderr) + print(f"CMakeц紊沿乾:\n{stderr_text}") raise def copy_package(name, install_dir, build_type): @@ -150,7 +158,7 @@ def build_package_type(name, source_dir, build_type, options, cache, components) print(f'cmake module find path: {cmake_modules_path}') options['3RD_PATH'] = args.output options['3RD_FIND_PATH'] = cmake_modules_path - options['BUILD_TESTING'] = False + options['BUILD_TESTING'] = 'OFF' options['CMAKE_INSTALL_PREFIX'] = install_dir options['CMAKE_BUILD_TYPE'] = build_type @@ -182,6 +190,7 @@ def process_package(package): source = package.get('source') is_tool = package.get('is_tool') options = package.get('options', {}) + header_only = package.get('header_only', False) if len(name) == 0: return @@ -220,9 +229,9 @@ def process_package(package): # use custom step if custom: if custom_need_platform is True: - subprocess.run(["python", custom, '-p', args.platform], cwd=str(clone_dir)) + subprocess.run([sys.executable, custom, '-p', args.platform], cwd=str(clone_dir), check=True) else: - subprocess.run(["python", custom], cwd=str(clone_dir)) + subprocess.run([sys.executable, custom], cwd=str(clone_dir), check=True) # init submodule if submodule is True: @@ -248,9 +257,53 @@ def process_package(package): if source: source_dir = os.path.join(str(clone_dir), source) - if is_tool is not True: + if is_tool is True: + return + + if header_only is True: + # header-only: only configure+install, skip build + build_header_only(name, source_dir, options, cache) + else: build_package(name, source_dir, options, cache, components) +def build_header_only(name, source_dir, options, cache): + print(f"[header-only] {name}") + for build_type in ['Release']: + build_dir = os.path.join(source_dir, f"build_{args.platform}_{build_type}") + install_dir = os.path.join(build_dir, 'install') + + cmake_modules_path = os.path.join(args.engine, 'cmake', 'thirdparty').replace('\\', '/') + options['3RD_PATH'] = args.output + options['3RD_FIND_PATH'] = cmake_modules_path + options['BUILD_TESTING'] = 'OFF' + options['CMAKE_INSTALL_PREFIX'] = install_dir + options['CMAKE_BUILD_TYPE'] = build_type + + Path(build_dir).mkdir(parents=True, exist_ok=True) + + cmake_cmd = ["cmake", "-S", source_dir, "-B", build_dir, "-G", tool_chain[args.platform]] + if options: + for key, value in options.items(): + cmake_cmd.extend([f"-D{key}={value}"]) + + subprocess.run(cmake_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + install_cmd = ["cmake", "--install", build_dir, "--config", build_type] + subprocess.run(install_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + copy_package(name, install_dir, build_type) + print(f"[header-only] {name} done") + +def list_packages(packages): + print(f"{'Name':<20} {'Tag':<25} {'Type':<12} {'Platforms'}") + print("-" * 80) + for pkg in packages: + name = pkg.get('name', '') + tag = pkg.get('tag', '(no tag)') + ptype = 'header-only' if pkg.get('header_only') else 'tool' if pkg.get('is_tool') else 'static' + platforms = ', '.join(pkg.get('platforms', ['all'])) + print(f"{name:<20} {tag:<25} {ptype:<12} {platforms}") + def app_main(): json_file = os.path.join(args.engine, 'cmake', 'thirdparty.json') with open(json_file, 'r', encoding='utf-8') as file: @@ -258,6 +311,10 @@ def app_main(): packages = data.get('packages', []) + if args.list: + list_packages(packages) + return + filtered = list(filter(lambda pkg: pkg['name'] == args.target, packages)) if len(filtered) > 0: From 716206afdaef93f78eff9b44e4b30cc24f4673d2 Mon Sep 17 00:00:00 2001 From: Zach Date: Tue, 10 Mar 2026 02:12:18 +0800 Subject: [PATCH 05/38] [feat]: fix pipeline key && fix ibl. --- assets/shaders/lighting/pbr.hlslh | 17 +- assets/shaders/pipeline/pass_options.hlslh | 1 + assets/shaders/standard_pbr.hlsl | 4 +- .../adaptor/src/pipeline/ForwardMSAAPass.cpp | 2 + .../core/include/render/light/LightDisplay.h | 50 ++++ .../core/include/render/rdg/RenderGraph.h | 3 + .../core/include/render/renderpass/PassBase.h | 2 + engine/render/core/src/env/EnvFeature.cpp | 39 ++-- engine/render/core/src/light/LightDisplay.cpp | 189 +++++++++++++++ engine/render/core/src/rdg/RenderGraph.cpp | 12 + .../render/core/src/renderpass/RasterPass.cpp | 1 + .../shader/include/shader/ShaderVariant.h | 10 +- engine/render/shader/src/ShaderCompiler.cpp | 2 + engine/render/shader/src/ShaderVariant.cpp | 39 +++- engine/render/shader/test/ShaderTest.cpp | 217 ++++++++++++++++++ 15 files changed, 555 insertions(+), 33 deletions(-) create mode 100644 engine/render/core/include/render/light/LightDisplay.h create mode 100644 engine/render/core/src/light/LightDisplay.cpp diff --git a/assets/shaders/lighting/pbr.hlslh b/assets/shaders/lighting/pbr.hlslh index 801f0f1a..dd896b80 100644 --- a/assets/shaders/lighting/pbr.hlslh +++ b/assets/shaders/lighting/pbr.hlslh @@ -57,16 +57,17 @@ float D_GGX(float dotNH, float roughness) return (alpha2) / (PI * denom * denom); } -float G_SchlickGGX(float dotNV, float roughness) +// Smith GGX for direct lighting: k = (roughness + 1)^2 / 8 +float G_SmithGGX_Direct(float dotNL, float dotNV, float roughness) { float r = (roughness + 1.0); - float k = (r*r) / 8.0; - - float num = dotNV; - float denom = dotNV * (1.0 - k) + k; - return num / denom; + float k = (r * r) / 8.0; + float GL = dotNL / (dotNL * (1.0 - k) + k); + float GV = dotNV / (dotNV * (1.0 - k) + k); + return GL * GV; } +// Smith GGX for IBL: k = roughness^2 / 2 float G_SchlickSmithGGX(float dotNL, float dotNV, float roughness) { float k = (roughness * roughness) / 2.0; @@ -99,7 +100,7 @@ float3 BRDF(float3 V, float3 N, LightInfo light, StandardPBR param) float3 F = F_Schlick(dotVH, F0); float D = D_GGX(dotNH, param.Roughness); - float G = G_SchlickSmithGGX(dotNL, dotNV, param.Roughness); + float G = G_SmithGGX_Direct(dotNL, dotNV, param.Roughness); // Ensure denominator doesn't become zero - add epsilon float specular = (D * G / (4.0 * dotNV * dotNL + 0.001)); @@ -109,6 +110,6 @@ float3 BRDF(float3 V, float3 N, LightInfo light, StandardPBR param) float3 diffuse = kD / PI * param.Albedo; // Combine diffuse and specular with direct light - L0 = (diffuse + specularColor) * light.Color.rgb * light.Color.w * dotNL * param.AO + param.Emissive.rgb; + L0 = (diffuse + specularColor) * light.Color.rgb * light.Color.w * dotNL + param.Emissive.rgb; return L0; } \ No newline at end of file diff --git a/assets/shaders/pipeline/pass_options.hlslh b/assets/shaders/pipeline/pass_options.hlslh index 85426575..20b59a1d 100644 --- a/assets/shaders/pipeline/pass_options.hlslh +++ b/assets/shaders/pipeline/pass_options.hlslh @@ -1,2 +1,3 @@ #pragma option({"key": "ENABLE_SHADOW", "bits": 1, "default": 0, "type": "Pass"}) +#pragma option({"key": "ENABLE_IBL", "bits": 1, "default": 0, "type": "Pass"}) diff --git a/assets/shaders/standard_pbr.hlsl b/assets/shaders/standard_pbr.hlsl index 083ffd56..98bb5d16 100644 --- a/assets/shaders/standard_pbr.hlsl +++ b/assets/shaders/standard_pbr.hlsl @@ -8,8 +8,8 @@ #pragma option({"key": "ENABLE_AO_MAP", "default": 0, "type": "Batch"}) #pragma option({"key": "ENABLE_MR_MAP", "default": 0, "type": "Batch"}) #pragma option({"key": "ENABLE_ALPHA_MASK", "default": 0, "type": "Batch"}) -#pragma option({"key": "ENABLE_IBL", "default": 0, "type": "Batch"}) +#pragma option({"key": "ENABLE_IBL", "default": 0, "type": "Pass"}) #pragma option({"key": "ENABLE_SHADOW", "default": 0, "type": "Pass"}) #include "vertex/standard.hlslh" @@ -417,7 +417,7 @@ float4 FSMain(VSOutput input) : SV_TARGET // Ensure minimum brightness to avoid edge darkening artifacts // This is especially important for grazing angles where N揃V is near zero - float minBrightness = 0.02 * pbrParam.Albedo * pbrParam.AO; // Subtle minimum fill light + float3 minBrightness = 0.02 * pbrParam.Albedo * pbrParam.AO; // Subtle minimum fill light ambient = max(ambient, minBrightness); return float4(e0 + ambient, albedo.a); diff --git a/engine/render/adaptor/src/pipeline/ForwardMSAAPass.cpp b/engine/render/adaptor/src/pipeline/ForwardMSAAPass.cpp index 7b1a8f8f..1c5eca1f 100644 --- a/engine/render/adaptor/src/pipeline/ForwardMSAAPass.cpp +++ b/engine/render/adaptor/src/pipeline/ForwardMSAAPass.cpp @@ -105,6 +105,8 @@ namespace sky { // Name("HizBuffer"), // rdg::ComputeView{Name("HizBuffer"), rdg::ComputeType::SRV, stageFlags} // }); + + pipelineKeys.emplace_back(Name("ENABLE_IBL")); } void ForwardMSAAPass::SetLayout(const RDResourceLayoutPtr &layout_) diff --git a/engine/render/core/include/render/light/LightDisplay.h b/engine/render/core/include/render/light/LightDisplay.h new file mode 100644 index 00000000..04d12b66 --- /dev/null +++ b/engine/render/core/include/render/light/LightDisplay.h @@ -0,0 +1,50 @@ +鏤// +// Created by Zach Lee on 2026/3/8. +// + +#pragma once + +#include +#include +#include +#include +#include + +namespace sky { + + class LightGizmoDisplay { + public: + LightGizmoDisplay(); + ~LightGizmoDisplay() = default; + + void SetTechnique(const RDGfxTechPtr &tech); + + // Draw gizmo for directional light + void DrawDirectLight(const Vector3 &direction, const Vector3 &position, const Color32 &color = Color32(0xFF, 0xFF, 0x00, 0xFF)); + + // Draw gizmo for point light + void DrawPointLight(const Vector3 &position, float range, const Color32 &color = Color32(0xFF, 0xFF, 0x00, 0xFF)); + + // Draw gizmo for spot light + void DrawSpotLight(const Vector3 &position, const Vector3 &direction, float angle, float range, const Color32 &color = Color32(0xFF, 0xFF, 0x00, 0xFF)); + + RenderPrimitive *GetPrimitive() const { return primitive.get(); } + + private: + // Helper: draw a circle in a given plane at center, with given radius, normal, and segment count + void DrawCircle(const Vector3 ¢er, const Vector3 &normal, float radius, uint32_t segments, const Color32 &color); + + // Helper: draw arrow rays from center along direction + void DrawDirectionArrows(const Vector3 ¢er, const Vector3 &direction, float length, float radius, uint32_t rayCount, const Color32 &color); + + // Helper: draw a cone wireframe (for spot light) + void DrawCone(const Vector3 &apex, const Vector3 &direction, float angle, float height, uint32_t segments, const Color32 &color); + + // Helper: build an orthonormal basis from a direction vector + static void BuildBasis(const Vector3 &dir, Vector3 &outRight, Vector3 &outUp); + + std::unique_ptr renderer; + std::unique_ptr primitive; + }; + +} // namespace sky \ No newline at end of file diff --git a/engine/render/core/include/render/rdg/RenderGraph.h b/engine/render/core/include/render/rdg/RenderGraph.h index 0ddb78f8..a9a045b2 100644 --- a/engine/render/core/include/render/rdg/RenderGraph.h +++ b/engine/render/core/include/render/rdg/RenderGraph.h @@ -64,6 +64,7 @@ namespace sky::rdg { struct RasterPassBuilder { RasterPassBuilder &AddAttachment(const RasterAttachment &attachment, const rhi::ClearValue &clear = rhi::ClearValue(0.f, 0.f, 0.f, 0.f)); RasterPassBuilder &AddCoRelationMasks(uint32_t mask); + RasterPassBuilder &ScratchPipelineKey(const std::vector &keys); RasterSubPassBuilder AddRasterSubPass(const Name &name); RenderGraph &rdg; @@ -218,6 +219,8 @@ namespace sky::rdg { ResourceGraph resourceGraph; AccessGraph accessGraph; Graph graph; + + std::unordered_map pipelineKey; }; template diff --git a/engine/render/core/include/render/renderpass/PassBase.h b/engine/render/core/include/render/renderpass/PassBase.h index 5d1f1fab..26918ae3 100644 --- a/engine/render/core/include/render/renderpass/PassBase.h +++ b/engine/render/core/include/render/renderpass/PassBase.h @@ -38,6 +38,8 @@ namespace sky { std::vector> images; std::vector> buffers; + + std::vector pipelineKeys; }; } // namespace sky \ No newline at end of file diff --git a/engine/render/core/src/env/EnvFeature.cpp b/engine/render/core/src/env/EnvFeature.cpp index c53f19c7..6fa627b7 100644 --- a/engine/render/core/src/env/EnvFeature.cpp +++ b/engine/render/core/src/env/EnvFeature.cpp @@ -15,31 +15,44 @@ namespace sky { EnvFeatureProcessor::EnvFeatureProcessor(RenderScene *scn) : IFeatureProcessor(scn) { - const auto& defaultRes = Renderer::Get()->GetDefaultResource(); - - irradiance = defaultRes.textureCubeWhite; - radiance = defaultRes.textureCubeWhite; } void EnvFeatureProcessor::SetRadiance(const RDTextureCubePtr &tex) { - const auto& defaultRes = Renderer::Get()->GetDefaultResource(); - radiance = tex ? tex : defaultRes.textureCubeWhite; + radiance = tex; } void EnvFeatureProcessor::SetIrradiance(const RDTextureCubePtr &tex) { - const auto& defaultRes = Renderer::Get()->GetDefaultResource(); - irradiance = tex ? tex : defaultRes.textureCubeWhite; + irradiance = tex; } void EnvFeatureProcessor::Render(rdg::RenderGraph &rdg) { - radiance->Wait(); - irradiance->Wait(); - - rdg.resourceGraph.ImportImage(Name("PrefilteredMap"), radiance->GetImage(), rhi::ImageViewType::VIEW_CUBE, rhi::AccessFlagBit::FRAGMENT_SRV); - rdg.resourceGraph.ImportImage(Name("IrradianceMap"), irradiance->GetImage(), rhi::ImageViewType::VIEW_CUBE, rhi::AccessFlagBit::FRAGMENT_SRV); + const auto &defaultRes = Renderer::Get()->GetDefaultResource(); + + bool enableIBL = false; + + if (irradiance != nullptr && irradiance->IsReady()) { + rdg.resourceGraph.ImportImage(Name("IrradianceMap"), irradiance->GetImage(), rhi::ImageViewType::VIEW_CUBE, + rhi::AccessFlagBit::FRAGMENT_SRV); + enableIBL = true; + } + else { + rdg.resourceGraph.ImportImage(Name("IrradianceMap"), defaultRes.textureCubeWhite->GetImage(), rhi::ImageViewType::VIEW_CUBE, + rhi::AccessFlagBit::FRAGMENT_SRV); + } + + if (radiance != nullptr && radiance->IsReady()) { + rdg.resourceGraph.ImportImage(Name("PrefilteredMap"), radiance->GetImage(), rhi::ImageViewType::VIEW_CUBE, + rhi::AccessFlagBit::FRAGMENT_SRV); + enableIBL = true; + } else { + rdg.resourceGraph.ImportImage(Name("PrefilteredMap"), defaultRes.textureCubeWhite->GetImage(), rhi::ImageViewType::VIEW_CUBE, + rhi::AccessFlagBit::FRAGMENT_SRV); + } + + rdg.pipelineKey[Name("ENABLE_IBL")] = enableIBL ? 1 : 0; } } // namespace sky \ No newline at end of file diff --git a/engine/render/core/src/light/LightDisplay.cpp b/engine/render/core/src/light/LightDisplay.cpp new file mode 100644 index 00000000..8fb476de --- /dev/null +++ b/engine/render/core/src/light/LightDisplay.cpp @@ -0,0 +1,189 @@ +鏤// +// Created by Zach Lee on 2026/3/8. +// + +#include +#include + +namespace sky { + + static constexpr uint32_t CIRCLE_SEGMENTS = 32; + static constexpr uint32_t ARROW_RAY_COUNT = 8; + static constexpr float ARROW_LENGTH = 3.0f; + static constexpr float SUN_DISK_RADIUS = 1.5f; + static constexpr float ARROW_RAY_OFFSET = 2.0f; + + struct LightGizmoPrimitive : RenderPrimitive {}; + + LightGizmoDisplay::LightGizmoDisplay() + : renderer(std::make_unique()) + , primitive(std::make_unique()) + { + } + + void LightGizmoDisplay::SetTechnique(const RDGfxTechPtr &tech) + { + // RenderBatch batch = {tech}; + // batch.topo = rhi::PrimitiveTopology::LINE_LIST; + // primitive->sections.clear(); + // primitive->sections.emplace_back(); + // primitive->sections.back().batches.clear(); + // primitive->sections.back().batches.emplace_back(batch); + } + + void LightGizmoDisplay::BuildBasis(const Vector3 &dir, Vector3 &outRight, Vector3 &outUp) + { + Vector3 d = dir; + d.Normalize(); + + // Pick a helper vector that is not parallel to dir + Vector3 helper = (std::fabs(d.y) < 0.99f) ? VEC3_Y : VEC3_X; + outRight = d.Cross(helper); + outRight.Normalize(); + outUp = outRight.Cross(d); + outUp.Normalize(); + } + + void LightGizmoDisplay::DrawCircle(const Vector3 ¢er, const Vector3 &normal, float radius, uint32_t segments, const Color32 &color) + { + Vector3 right, up; + BuildBasis(normal, right, up); + + float step = 2.0f * PI / static_cast(segments); + renderer->SetColor(color); + + Vector3 prev = center + right * radius; + for (uint32_t i = 1; i <= segments; ++i) { + float angle = step * static_cast(i); + Vector3 curr = center + right * (radius * std::cos(angle)) + up * (radius * std::sin(angle)); + renderer->DrawLine(prev, curr); + prev = curr; + } + } + + void LightGizmoDisplay::DrawDirectionArrows(const Vector3 ¢er, const Vector3 &direction, float length, float radius, uint32_t rayCount, const Color32 &color) + { + Vector3 dir = direction; + dir.Normalize(); + + Vector3 right, up; + BuildBasis(dir, right, up); + + renderer->SetColor(color); + + float step = 2.0f * PI / static_cast(rayCount); + for (uint32_t i = 0; i < rayCount; ++i) { + float angle = step * static_cast(i); + Vector3 offset = right * (radius * std::cos(angle)) + up * (radius * std::sin(angle)); + Vector3 start = center + offset; + Vector3 end = start + dir * length; + renderer->DrawLine(start, end); + } + } + + void LightGizmoDisplay::DrawCone(const Vector3 &apex, const Vector3 &direction, float angle, float height, uint32_t segments, const Color32 &color) + { + Vector3 dir = direction; + dir.Normalize(); + + Vector3 right, up; + BuildBasis(dir, right, up); + + float baseRadius = height * std::tan(angle); + Vector3 baseCenter = apex + dir * height; + + // Draw the base circle + DrawCircle(baseCenter, dir, baseRadius, segments, color); + + // Draw lines from apex to base circle + renderer->SetColor(color); + float step = 2.0f * PI / static_cast(segments); + for (uint32_t i = 0; i < segments; i += (segments / 4)) { + float a = step * static_cast(i); + Vector3 basePoint = baseCenter + right * (baseRadius * std::cos(a)) + up * (baseRadius * std::sin(a)); + renderer->DrawLine(apex, basePoint); + } + } + + // ----------------------------------------------------------------------- + // DirectLight: sun disk + parallel arrow rays along direction + // ----------------------------------------------------------------------- + void LightGizmoDisplay::DrawDirectLight(const Vector3 &direction, const Vector3 &position, const Color32 &color) + { + renderer->Reset(); + + Vector3 dir = direction; + dir.Normalize(); + + // Sun disk circle perpendicular to direction + DrawCircle(position, dir, SUN_DISK_RADIUS, CIRCLE_SEGMENTS, color); + + // Cross lines through the disk center + { + Vector3 right, up; + BuildBasis(dir, right, up); + renderer->SetColor(color); + renderer->DrawLine(position + right * SUN_DISK_RADIUS, position - right * SUN_DISK_RADIUS); + renderer->DrawLine(position + up * SUN_DISK_RADIUS, position - up * SUN_DISK_RADIUS); + } + + // Arrow rays starting from the edge of the disk going along direction + DrawDirectionArrows(position + dir * ARROW_RAY_OFFSET, dir, ARROW_LENGTH, SUN_DISK_RADIUS, ARROW_RAY_COUNT, color); + + renderer->Render(primitive.get()); + } + + // ----------------------------------------------------------------------- + // PointLight: three mutually-perpendicular wireframe circles + // ----------------------------------------------------------------------- + void LightGizmoDisplay::DrawPointLight(const Vector3 &position, float range, const Color32 &color) + { + renderer->Reset(); + + // XY plane circle + DrawCircle(position, VEC3_Z, range, CIRCLE_SEGMENTS, color); + // XZ plane circle + DrawCircle(position, VEC3_Y, range, CIRCLE_SEGMENTS, color); + // YZ plane circle + DrawCircle(position, VEC3_X, range, CIRCLE_SEGMENTS, color); + + // Small cross at center + float s = range * 0.1f; + renderer->SetColor(color); + renderer->DrawLine(position - VEC3_X * s, position + VEC3_X * s); + renderer->DrawLine(position - VEC3_Y * s, position + VEC3_Y * s); + renderer->DrawLine(position - VEC3_Z * s, position + VEC3_Z * s); + + renderer->Render(primitive.get()); + } + + // ----------------------------------------------------------------------- + // SpotLight: cone showing angle + range, plus a small sphere at apex + // ----------------------------------------------------------------------- + void LightGizmoDisplay::DrawSpotLight(const Vector3 &position, const Vector3 &direction, float angle, float range, const Color32 &color) + { + renderer->Reset(); + + Vector3 dir = direction; + dir.Normalize(); + + // Main cone + DrawCone(position, dir, angle, range, CIRCLE_SEGMENTS, color); + + // Half-range inner circle to show the cone body + float halfRange = range * 0.5f; + float halfRadius = halfRange * std::tan(angle); + Vector3 halfCenter = position + dir * halfRange; + DrawCircle(halfCenter, dir, halfRadius, CIRCLE_SEGMENTS, color); + + // Small source indicator at apex + float s = range * 0.05f; + renderer->SetColor(color); + renderer->DrawLine(position - VEC3_X * s, position + VEC3_X * s); + renderer->DrawLine(position - VEC3_Y * s, position + VEC3_Y * s); + renderer->DrawLine(position - VEC3_Z * s, position + VEC3_Z * s); + + renderer->Render(primitive.get()); + } + +} // namespace sky diff --git a/engine/render/core/src/rdg/RenderGraph.cpp b/engine/render/core/src/rdg/RenderGraph.cpp index 3c455189..e3e7c9f8 100644 --- a/engine/render/core/src/rdg/RenderGraph.cpp +++ b/engine/render/core/src/rdg/RenderGraph.cpp @@ -256,6 +256,18 @@ namespace sky::rdg { return *this; } + RasterPassBuilder &RasterPassBuilder::ScratchPipelineKey(const std::vector &keys) + { + PipelineVariantSetter setter(pass.pipelineKey); + for (const auto& key : keys) { + auto iter = rdg.pipelineKey.find(key); + if (iter != rdg.pipelineKey.end()) { + setter.SetValue(key, iter->second); + } + } + return *this; + } + RasterSubPassBuilder RasterPassBuilder::AddRasterSubPass(const Name &name) { auto dst = AddVertex(name, RasterSubPass{&rdg.context->resources}, rdg); diff --git a/engine/render/core/src/renderpass/RasterPass.cpp b/engine/render/core/src/renderpass/RasterPass.cpp index 1639ac77..36be2cf1 100644 --- a/engine/render/core/src/renderpass/RasterPass.cpp +++ b/engine/render/core/src/renderpass/RasterPass.cpp @@ -21,6 +21,7 @@ namespace sky { void RasterPass::Setup(rdg::RenderGraph &rdg, RenderScene &scene) { auto passBuilder = rdg.AddRasterPass(name, width, height); + passBuilder.ScratchPipelineKey(pipelineKeys); auto colorCount = static_cast(colors.size()); auto resolveCount = static_cast(resolves.size()); diff --git a/engine/render/shader/include/shader/ShaderVariant.h b/engine/render/shader/include/shader/ShaderVariant.h index c959923c..dcfcf328 100644 --- a/engine/render/shader/include/shader/ShaderVariant.h +++ b/engine/render/shader/include/shader/ShaderVariant.h @@ -141,8 +141,8 @@ namespace sky { void FillShaderOption(ShaderOption& option, const ShaderVariantKey& key) const; - inline ShaderVariantKey GetPipelineMask() const { return pipelineMask; } - inline const std::vector& GetOptionEntries() const { return entries; } + FORCEINLINE ShaderVariantKey GetPipelineMask() const { return pipelineMask; } + FORCEINLINE const std::vector& GetOptionEntries() const { return entries; } private: void GeneratePermutationImpl(std::vector& out, const std::vector &list, uint32_t index, ShaderVariantKey key) const; @@ -155,6 +155,12 @@ namespace sky { ShaderVariantKey pipelineMask; }; + struct PipelineVariantSetter { + ShaderVariantKey& key; + + void SetValue(const Name& name, uint8_t value); + }; + } // namespace sky #include diff --git a/engine/render/shader/src/ShaderCompiler.cpp b/engine/render/shader/src/ShaderCompiler.cpp index 208ae11a..59c51cb4 100644 --- a/engine/render/shader/src/ShaderCompiler.cpp +++ b/engine/render/shader/src/ShaderCompiler.cpp @@ -196,6 +196,8 @@ namespace sky { nameMap[entry.key] = static_cast(passEntries.size()); SKY_ASSERT(item.type == ShaderOptionType::PASS); passEntries.emplace_back(entry); + + currentBit = entry.range.second + 1; } } diff --git a/engine/render/shader/src/ShaderVariant.cpp b/engine/render/shader/src/ShaderVariant.cpp index bb4ca2a7..e9ab65e6 100644 --- a/engine/render/shader/src/ShaderVariant.cpp +++ b/engine/render/shader/src/ShaderVariant.cpp @@ -65,13 +65,20 @@ namespace sky { SKY_ASSERT(endBit - beginBit < 8); SKY_ASSERT(endBit < SHADER_VARIANT_KEY_LEN); + auto byteIndex = beginBit >> 3; auto S = beginBit & 0x7; auto E = S + (endBit - beginBit); - auto &num = *(reinterpret_cast(&u8[beginBit >> 3])); - - auto mask = (uint16_t)((~0u << (E + 1)) | ((1u << S) - 1)); - num = (num & mask) | ((uint16_t)(val) << S); + if (E < 8) { + // Bit range fits within a single byte + auto mask = static_cast(((1u << (E + 1)) - 1) ^ ((1u << S) - 1)); + u8[byteIndex] = (u8[byteIndex] & ~mask) | (static_cast(val << S) & mask); + } else { + // Bit range crosses a byte boundary, use two-byte window + auto &num = *(reinterpret_cast(&u8[byteIndex])); + auto mask = static_cast((~0u << (E + 1)) | ((1u << S) - 1)); + num = (num & mask) | (static_cast(val) << S); + } } uint8_t ShaderVariantKey::GetValue(uint8_t beginBit, uint8_t endBit) const @@ -79,13 +86,21 @@ namespace sky { SKY_ASSERT(endBit - beginBit < 8); SKY_ASSERT(endBit < SHADER_VARIANT_KEY_LEN); + auto byteIndex = beginBit >> 3; auto S = beginBit & 0x7; auto E = S + (endBit - beginBit); - const auto &num = *(reinterpret_cast(&u8[beginBit >> 3])); + auto bits = E - S + 1; + auto mask = static_cast((1u << bits) - 1); - auto mask = (uint16_t)((1u << (E - S + 1)) - 1); - return static_cast((num >> S) & mask); + if (E < 8) { + // Bit range fits within a single byte + return static_cast((u8[byteIndex] >> S) & mask); + } else { + // Bit range crosses a byte boundary, use two-byte window + const auto &num = *(reinterpret_cast(&u8[byteIndex])); + return static_cast((num >> S) & mask); + } } std::string ShaderVariantKey::ToString() const @@ -119,7 +134,7 @@ namespace sky { if (item.type == ShaderOptionType::BATCH) { entry.range = {BATCH_OPT_OFFSET + currentBit, BATCH_OPT_OFFSET + currentBit + item.bits - 1}; - currentBit++; + currentBit += item.bits; } else { const auto *passEntry = ShaderCompiler::Get()->FindPassEntry(item.key); @@ -219,4 +234,12 @@ namespace sky { return output; } + void PipelineVariantSetter::SetValue(const Name& name, uint8_t value) + { + const auto *passEntry = ShaderCompiler::Get()->FindPassEntry(name); + if (passEntry != nullptr) { + key.SetValue(passEntry->range.first, passEntry->range.second, value); + } + } + } // namespace sky diff --git a/engine/render/shader/test/ShaderTest.cpp b/engine/render/shader/test/ShaderTest.cpp index df61a1c2..60f366cb 100644 --- a/engine/render/shader/test/ShaderTest.cpp +++ b/engine/render/shader/test/ShaderTest.cpp @@ -18,6 +18,223 @@ TEST(ShaderTest, ShaderVariantKeyTest) ASSERT_EQ(key.GetValue(0, 1), 2); } +// --- SetValue / GetValue: single-bit at every byte boundary --- +TEST(ShaderTest, SetGetSingleBit) +{ + // Test single-bit set/get at bit 0 of each byte + for (uint8_t bit = 0; bit < 64; bit += 8) { + ShaderVariantKey key = {}; + key.SetValue(bit, bit, 1); + ASSERT_EQ(key.GetValue(bit, bit), 1) << "bit=" << (int)bit; + + // Other bits in the same byte should be 0 + if (bit + 1 < 64) { + ASSERT_EQ(key.GetValue(bit + 1, bit + 1), 0) << "bit+1=" << (int)(bit + 1); + } + } + + // Test single-bit set/get at bit 7 of each byte (last bit in byte) + for (uint8_t bit = 7; bit < 64; bit += 8) { + ShaderVariantKey key = {}; + key.SetValue(bit, bit, 1); + ASSERT_EQ(key.GetValue(bit, bit), 1) << "bit=" << (int)bit; + } +} + +// --- SetValue / GetValue: within a single byte (no cross-boundary) --- +TEST(ShaderTest, SetGetWithinSingleByte) +{ + { + // Bits [0,3] ! 4-bit value within byte 0 + ShaderVariantKey key = {}; + key.SetValue(0, 3, 0x0F); + ASSERT_EQ(key.GetValue(0, 3), 0x0F); + } + { + // Bits [0,7] ! full byte 0 + ShaderVariantKey key = {}; + key.SetValue(0, 7, 0xAB); + ASSERT_EQ(key.GetValue(0, 7), 0xAB); + } + { + // Bits [16,19] ! 4-bit value within byte 2 + ShaderVariantKey key = {}; + key.SetValue(16, 19, 9); + ASSERT_EQ(key.GetValue(16, 19), 9); + } +} + +// --- SetValue / GetValue: crossing a byte boundary --- +TEST(ShaderTest, SetGetCrossByteBoundary) +{ + { + // Bits [5,10] ! starts in byte 0, ends in byte 1 (6 bits) + ShaderVariantKey key = {}; + key.SetValue(5, 10, 0x3F); // max 6-bit value + ASSERT_EQ(key.GetValue(5, 10), 0x3F); + } + { + // Bits [6,9] ! 4 bits crossing byte 0 and byte 1 + ShaderVariantKey key = {}; + key.SetValue(6, 9, 0x0A); + ASSERT_EQ(key.GetValue(6, 9), 0x0A); + } + { + // Bits [12,17] ! crossing byte 1 and byte 2 + ShaderVariantKey key = {}; + key.SetValue(12, 17, 42); + ASSERT_EQ(key.GetValue(12, 17), 42); + } + { + // Bits [28,35] ! crossing byte 3 and byte 4 (8 bits, original test value) + ShaderVariantKey key = {}; + key.SetValue(28, 35, 127); + ASSERT_EQ(key.GetValue(28, 35), 127); + } +} + +// --- SetValue / GetValue: high bits near the end of the key (56..63) --- +TEST(ShaderTest, SetGetHighBits) +{ + { + // Single bit at bit 63 (the very last bit) + ShaderVariantKey key = {}; + key.SetValue(63, 63, 1); + ASSERT_EQ(key.GetValue(63, 63), 1); + // Lower bits should be unaffected + ASSERT_EQ(key.GetValue(0, 7), 0); + } + { + // Bits [56,63] ! full last byte + ShaderVariantKey key = {}; + key.SetValue(56, 63, 0xCD); + ASSERT_EQ(key.GetValue(56, 63), 0xCD); + ASSERT_EQ(key.GetValue(0, 7), 0); + } + { + // Bits [60,63] ! upper nibble of last byte + ShaderVariantKey key = {}; + key.SetValue(60, 63, 0x0F); + ASSERT_EQ(key.GetValue(60, 63), 0x0F); + } + { + // Bits [56,59] ! lower nibble of last byte + ShaderVariantKey key = {}; + key.SetValue(56, 59, 0x05); + ASSERT_EQ(key.GetValue(56, 59), 0x05); + } +} + +// --- SetValue / GetValue: crossing into the last byte (byte 6★7) --- +TEST(ShaderTest, SetGetCrossIntoLastByte) +{ + { + // Bits [53,60] ! 8 bits crossing byte 6 and byte 7 + ShaderVariantKey key = {}; + key.SetValue(53, 60, 0xA5); + ASSERT_EQ(key.GetValue(53, 60), 0xA5); + } + { + // Bits [50,55] ! 6 bits crossing byte 6 and byte 6/7 boundary + ShaderVariantKey key = {}; + key.SetValue(50, 55, 0x2B); + ASSERT_EQ(key.GetValue(50, 55), 0x2B); + } +} + +// --- Multiple non-overlapping fields should not corrupt each other --- +TEST(ShaderTest, SetGetMultipleFieldsIsolation) +{ + ShaderVariantKey key = {}; + + key.SetValue(0, 0, 1); // bit 0 + key.SetValue(1, 3, 5); // bits 1-3 + key.SetValue(8, 15, 0xFF); // bits 8-15 (full byte 1) + key.SetValue(32, 39, 0xAB); // bits 32-39 (full byte 4) + key.SetValue(60, 63, 0x0C); // bits 60-63 + + ASSERT_EQ(key.GetValue(0, 0), 1); + ASSERT_EQ(key.GetValue(1, 3), 5); + ASSERT_EQ(key.GetValue(8, 15), 0xFF); + ASSERT_EQ(key.GetValue(32, 39), 0xAB); + ASSERT_EQ(key.GetValue(60, 63), 0x0C); + + // Unset regions should be 0 + ASSERT_EQ(key.GetValue(16, 23), 0); + ASSERT_EQ(key.GetValue(40, 47), 0); +} + +// --- Overwrite: setting a field twice should update correctly --- +TEST(ShaderTest, SetValueOverwrite) +{ + ShaderVariantKey key = {}; + + key.SetValue(4, 7, 0x0F); + ASSERT_EQ(key.GetValue(4, 7), 0x0F); + + key.SetValue(4, 7, 0x03); + ASSERT_EQ(key.GetValue(4, 7), 0x03); + + // Overwrite with 0 + key.SetValue(4, 7, 0x00); + ASSERT_EQ(key.GetValue(4, 7), 0x00); +} + +// --- Overwrite should not affect adjacent bits --- +TEST(ShaderTest, SetValueOverwriteIsolation) +{ + ShaderVariantKey key = {}; + + key.SetValue(0, 3, 0x0F); + key.SetValue(4, 7, 0x0A); + + // Overwrite bits [0,3] + key.SetValue(0, 3, 0x05); + ASSERT_EQ(key.GetValue(0, 3), 0x05); + ASSERT_EQ(key.GetValue(4, 7), 0x0A); // adjacent should be unchanged +} + +// --- Max value for each bit width (1..7 bits) --- +TEST(ShaderTest, SetGetMaxValues) +{ + for (uint8_t bits = 1; bits <= 7; ++bits) { + uint8_t maxVal = static_cast((1u << bits) - 1); + ShaderVariantKey key = {}; + key.SetValue(0, bits - 1, maxVal); + ASSERT_EQ(key.GetValue(0, bits - 1), maxVal) << "bits=" << (int)bits; + } +} + +// --- GenerateDefault should apply default values to all entries --- +TEST(ShaderTest, GenerateDefaultWithNonZeroDefaults) +{ + ShaderVariantList list; + + // entry at bits [0,0] with default=1 + list.AddEntry({Name("flag_a"), {0, 0}, 1}); + // entry at bits [1,3] with default=5 + list.AddEntry({Name("mode_b"), {1, 3}, 5}); + // entry at bits [8,15] with default=0xAB + list.AddEntry({Name("param_c"), {8, 15}, 0xAB}); + + ShaderVariantKey dflt = list.GenerateDefault(); + + ASSERT_EQ(dflt.GetValue(0, 0), 1); + ASSERT_EQ(dflt.GetValue(1, 3), 5); + ASSERT_EQ(dflt.GetValue(8, 15), 0xAB); + // Bits not covered by any entry should remain 0 + ASSERT_EQ(dflt.GetValue(16, 23), 0); +} + +// --- Zero-init: freshly constructed key should read 0 everywhere --- +TEST(ShaderTest, ZeroInitialized) +{ + ShaderVariantKey key = {}; + for (uint8_t byte = 0; byte < 8; ++byte) { + ASSERT_EQ(key.GetValue(byte * 8, byte * 8 + 7), 0) << "byte=" << (int)byte; + } +} + TEST(ShaderTest, ShaderVariantListTest1) { ShaderVariantList list; From c5413fa92484f92a43e85392be56d66c906cd5a8 Mon Sep 17 00:00:00 2001 From: Zach Date: Wed, 11 Mar 2026 22:51:34 +0800 Subject: [PATCH 06/38] [feat]: fix skeleton mesh. --- engine/animation/src/core/AnimationNodeChannel.cpp | 2 +- engine/animation/src/core/AnimationPlayer.cpp | 2 +- engine/core/include/core/math/Quaternion.inl | 10 ++++++++-- .../adaptor/src/animation/CharacterLocomotion.cpp | 7 +++++++ engine/render/core/include/render/light/LightBase.h | 2 ++ engine/render/core/include/render/mesh/MeshLodProxy.h | 2 ++ .../render/core/src/skeleton/SkeletalMeshRenderer.cpp | 4 +++- 7 files changed, 24 insertions(+), 5 deletions(-) diff --git a/engine/animation/src/core/AnimationNodeChannel.cpp b/engine/animation/src/core/AnimationNodeChannel.cpp index 80e017b2..8652c268 100644 --- a/engine/animation/src/core/AnimationNodeChannel.cpp +++ b/engine/animation/src/core/AnimationNodeChannel.cpp @@ -17,7 +17,7 @@ namespace sky { void AnimationNodeChannel::Sample(const SampleParam ¶m, Transform& trans) { trans.translation = AnimSampleChannel(position, param); - trans.scale = AnimSampleChannel(scale, param); + // trans.scale = AnimSampleChannel(scale, param); trans.rotation = AnimSampleChannel(rotation, param); } } // namespace sky diff --git a/engine/animation/src/core/AnimationPlayer.cpp b/engine/animation/src/core/AnimationPlayer.cpp index b6894356..b1b1e2f8 100644 --- a/engine/animation/src/core/AnimationPlayer.cpp +++ b/engine/animation/src/core/AnimationPlayer.cpp @@ -59,7 +59,7 @@ namespace sky { { float time = current + delta; if (time < 0.f || time > length) { - time = loop ? ((length == 0.f) ? std::fmod(time, length) : 0.f) + time = loop ? ((length > 0.f) ? std::fmod(time, length) : 0.f) : std::clamp(time, 0.f, length); } return time; diff --git a/engine/core/include/core/math/Quaternion.inl b/engine/core/include/core/math/Quaternion.inl index 7bb5ee7f..a25c144f 100644 --- a/engine/core/include/core/math/Quaternion.inl +++ b/engine/core/include/core/math/Quaternion.inl @@ -40,9 +40,13 @@ namespace sky { x = 0; y = 0; z = 0; + return; } float inverseSqrt = 1 / sqrt(n); - Quaternion::operator*=(inverseSqrt); + w *= inverseSqrt; + x *= inverseSqrt; + y *= inverseSqrt; + z *= inverseSqrt; } inline Quaternion Quaternion::Conjugate() const @@ -71,11 +75,12 @@ namespace sky { inline Quaternion Quaternion::operator*(float va) const { - return {x * va, y * va, z * va, w * va}; + return {w * va, x * va, y * va, z * va}; } inline Quaternion &Quaternion::operator*=(float m) { + w *= m; x *= m; y *= m; z *= m; @@ -84,6 +89,7 @@ namespace sky { inline Quaternion &Quaternion::operator/=(float d) { + w /= d; x /= d; y /= d; z /= d; diff --git a/engine/render/adaptor/src/animation/CharacterLocomotion.cpp b/engine/render/adaptor/src/animation/CharacterLocomotion.cpp index 83806682..4cc6d902 100644 --- a/engine/render/adaptor/src/animation/CharacterLocomotion.cpp +++ b/engine/render/adaptor/src/animation/CharacterLocomotion.cpp @@ -37,6 +37,11 @@ namespace sky { static constexpr float ROTATE_SPEED = 100.f; static constexpr float MOVE_SPEED = 200.f; + void UpdateTransformComponent(TransformComponent* inTrans) + { + transform = inTrans; + } + void Tick(float deltaTime) override { Transform localTrans = transform->GetLocalTransform(); @@ -224,6 +229,8 @@ namespace sky { } if (controller != nullptr) { + static_cast(controller.get()) + ->UpdateTransformComponent(actor->GetComponent()); controller->Tick(time); } diff --git a/engine/render/core/include/render/light/LightBase.h b/engine/render/core/include/render/light/LightBase.h index e6213073..d5d3b3f7 100644 --- a/engine/render/core/include/render/light/LightBase.h +++ b/engine/render/core/include/render/light/LightBase.h @@ -54,6 +54,8 @@ namespace sky { castShadow = shadow; } + bool GetCastShadow() const { return castShadow; } + const Vector4 &GetColor() const { return color; } const Vector3 &GetDirection() const { return direction; } diff --git a/engine/render/core/include/render/mesh/MeshLodProxy.h b/engine/render/core/include/render/mesh/MeshLodProxy.h index 272e9c2b..a048d160 100644 --- a/engine/render/core/include/render/mesh/MeshLodProxy.h +++ b/engine/render/core/include/render/mesh/MeshLodProxy.h @@ -40,6 +40,8 @@ namespace sky { private: bool IsValid() const noexcept override { return !!mesh; } + RDMeshPtr GetMesh() const override { return mesh; } + void Reset() noexcept override; RDSkeletonMeshPtr mesh; diff --git a/engine/render/core/src/skeleton/SkeletalMeshRenderer.cpp b/engine/render/core/src/skeleton/SkeletalMeshRenderer.cpp index 7faaaff8..af9a2643 100644 --- a/engine/render/core/src/skeleton/SkeletalMeshRenderer.cpp +++ b/engine/render/core/src/skeleton/SkeletalMeshRenderer.cpp @@ -18,7 +18,9 @@ namespace sky { void SkeletalMeshRenderer::UpdateSkinData(const SkinUpdateDataPtr& skinData) { - lodPrimitive->UpdateSkinData(skinData); + if (lodPrimitive) { + lodPrimitive->UpdateSkinData(skinData); + } } From 9f4c9f51d3fb21cbbed653bc893f794a975edc33 Mon Sep 17 00:00:00 2001 From: Zach Date: Thu, 12 Mar 2026 01:52:00 +0800 Subject: [PATCH 07/38] [feat]: Transparent Mat. --- assets/materials/standard_transparent_pbr.mat | 32 ++++ assets/shaders/layout/standard_shading.hlslh | 16 +- assets/shaders/standard_pbr.hlsl | 22 ++- assets/techniques/standard_transparent.tech | 34 ++++ .../src/components/PrefabComponent.cpp | 63 ++++--- .../backend/vulkan/src/DescriptorSet.cpp | 18 +- .../include/builder/render/ImageBuilder.h | 2 +- .../builder/render/src/ImageBuilder.cpp | 4 +- .../builder/render/src/PrefabBuilder.cpp | 160 ++++++++++++++---- .../builder/render/src/image/ImageProcess.cpp | 1 + 10 files changed, 275 insertions(+), 77 deletions(-) create mode 100644 assets/materials/standard_transparent_pbr.mat create mode 100644 assets/techniques/standard_transparent.tech diff --git a/assets/materials/standard_transparent_pbr.mat b/assets/materials/standard_transparent_pbr.mat new file mode 100644 index 00000000..0c0ba2ee --- /dev/null +++ b/assets/materials/standard_transparent_pbr.mat @@ -0,0 +1,32 @@ +{ + "techniques": [ + "techniques/standard_transparent.tech", + "techniques/depth.tech", + "techniques/shadowmap.tech" + ], + "properties": { + "Metallic": 0.5, + "Roughness": 0.5, + "AlphaCutoff": 1.0, + "TransmissionFactor": 0.0, + "Albedo": [1.0, 1.0, 1.0, 1.0], + "AoMap": "textures/black.png", + "EmissiveMap": "textures/black.png", + "AlbedoMap": "textures/white.png", + "MetallicRoughnessMap": "textures/black.png", + "NormalMap": "textures/black.png", + "TransmissionMap": "textures/white.png", + "AoSampler": { "MagFilter": "linear", "MinFilter": "linear", "MipMode": "linear" }, + "EmissiveSampler": { "MagFilter": "linear", "MinFilter": "linear", "MipMode": "linear" }, + "AlbedoSampler": { "MagFilter": "linear", "MinFilter": "linear", "MipMode": "linear" }, + "MetallicRoughnessSampler": { "MagFilter": "linear", "MinFilter": "linear", "MipMode": "linear" }, + "NormalSampler": { "MagFilter": "linear", "MinFilter": "linear", "MipMode": "linear" }, + "TransmissionSampler": { "MagFilter": "linear", "MinFilter": "linear", "MipMode": "linear" }, + "ENABLE_ALPHA_MASK": false, + "ENABLE_NORMAL_MAP": false, + "ENABLE_AO_MAP": false, + "ENABLE_EMISSIVE_MAP": false, + "ENABLE_MR_MAP": false, + "ENABLE_TRANSMISSION": false + } +} \ No newline at end of file diff --git a/assets/shaders/layout/standard_shading.hlslh b/assets/shaders/layout/standard_shading.hlslh index 23b316e0..59c4ff43 100644 --- a/assets/shaders/layout/standard_shading.hlslh +++ b/assets/shaders/layout/standard_shading.hlslh @@ -4,6 +4,7 @@ float Metallic; float Roughness; float AlphaCutoff; + float TransmissionFactor; float4 Albedo; } @@ -12,9 +13,16 @@ [[vk::binding(4, 1)]] Texture2D AoMap : register(t2, space1); [[vk::binding(5, 1)]] Texture2D MetallicRoughnessMap : register(t3, space1); [[vk::binding(6, 1)]] Texture2D EmissiveMap : register(t4, space1); +#if ENABLE_TRANSMISSION +[[vk::binding(7, 1)]] Texture2D TransmissionMap : register(t5, space1); +#endif -[[vk::binding(7, 1)]] SamplerState AlbedoSampler : register(s0, space1); -[[vk::binding(8, 1)]] SamplerState NormalSampler : register(s1, space1); -[[vk::binding(9, 1)]] SamplerState AoSampler : register(s2, space1); +[[vk::binding(7, 1)]] SamplerState AlbedoSampler : register(s0, space1); +[[vk::binding(8, 1)]] SamplerState NormalSampler : register(s1, space1); +[[vk::binding(9, 1)]] SamplerState AoSampler : register(s2, space1); [[vk::binding(10, 1)]] SamplerState MetallicRoughnessSampler : register(s3, space1); -[[vk::binding(11, 1)]] SamplerState EmissiveSampler : register(s4, space1); +[[vk::binding(11, 1)]] SamplerState EmissiveSampler : register(s4, space1); + +#if ENABLE_TRANSMISSION +[[vk::binding(12, 1)]] SamplerState TransmissionSampler : register(s5, space1); +#endif diff --git a/assets/shaders/standard_pbr.hlsl b/assets/shaders/standard_pbr.hlsl index 98bb5d16..3cae8f40 100644 --- a/assets/shaders/standard_pbr.hlsl +++ b/assets/shaders/standard_pbr.hlsl @@ -8,6 +8,7 @@ #pragma option({"key": "ENABLE_AO_MAP", "default": 0, "type": "Batch"}) #pragma option({"key": "ENABLE_MR_MAP", "default": 0, "type": "Batch"}) #pragma option({"key": "ENABLE_ALPHA_MASK", "default": 0, "type": "Batch"}) +#pragma option({"key": "ENABLE_TRANSMISSION", "default": 0, "type": "Batch"}) #pragma option({"key": "ENABLE_IBL", "default": 0, "type": "Pass"}) #pragma option({"key": "ENABLE_SHADOW", "default": 0, "type": "Pass"}) @@ -330,6 +331,12 @@ float4 FSMain(VSOutput input) : SV_TARGET } #endif +#if ENABLE_TRANSMISSION + float transmission = saturate(TransmissionFactor * TransmissionMap.Sample(TransmissionSampler, input.UV.xy).r); +#else + float transmission = 0.0; +#endif + LightInfo light; light.Color = MainLightColor; light.Direction = MainLightDirection; @@ -420,11 +427,20 @@ float4 FSMain(VSOutput input) : SV_TARGET float3 minBrightness = 0.02 * pbrParam.Albedo * pbrParam.AO; // Subtle minimum fill light ambient = max(ambient, minBrightness); - return float4(e0 + ambient, albedo.a); + float3 color = e0 + ambient; #else // Fallback without IBL float3 ambient = float3(0.03, 0.03, 0.03) * pbrParam.Albedo * pbrParam.AO; - return float4(e0 + ambient, albedo.a); + float3 color = e0 + ambient; +#endif + +#if ENABLE_TRANSMISSION + color = lerp(color, color * albedo.rgb, 1.0 - transmission); + float outAlpha = saturate(albedo.a * (1.0 - transmission)); +#else + float outAlpha = albedo.a; #endif + return float4(color, outAlpha); } -//------------------------------------------ Fragment Shader------------------------------------------// \ No newline at end of file +//------------------------------------------ Fragment Shader------------------------------------------// + diff --git a/assets/techniques/standard_transparent.tech b/assets/techniques/standard_transparent.tech new file mode 100644 index 00000000..a2a48560 --- /dev/null +++ b/assets/techniques/standard_transparent.tech @@ -0,0 +1,34 @@ +{ + "type": "graphics", + "shader": { + "path": "standard_pbr.hlsl", + "vertex": "VSMain", + "task": "TASMain", + "mesh": "MSMain", + "fragment": "FSMain" + }, + "pass": { + "tag": "Transparent" + }, + "blend_state": [ + { + "blendEnable": true, + "srcColor": "SRC_ALPHA", + "dstColor": "ONE_MINUS_SRC_ALPHA", + "srcAlpha": "ONE", + "dstAlpha": "ONE_MINUS_SRC_ALPHA" + } + ], + "depth_stencil": { + "depthTestEnable": true, + "depthWriteEnable": false + }, + "raster_state": { + "cullMode": "BACK" + }, + "vertex_options": { + "SKIN": "ENABLE_SKIN", + "INSTANCE": "ENABLE_INSTANCE", + "MESH_SHADER": "MESH_SHADER" + } +} diff --git a/engine/render/adaptor/src/components/PrefabComponent.cpp b/engine/render/adaptor/src/components/PrefabComponent.cpp index e6df8a40..ee2713bd 100644 --- a/engine/render/adaptor/src/components/PrefabComponent.cpp +++ b/engine/render/adaptor/src/components/PrefabComponent.cpp @@ -119,37 +119,46 @@ namespace sky { void PrefabComponent::OnAssetLoaded(const Uuid& uuid, const std::string_view& type) { if (type == AssetTraits::ASSET_TYPE) { - std::lock_guard lock(assetMutex); - - const auto &prefabData = prefab.Data(); - size_t nodeNum = prefabData.nodes.size(); - capacity.store(nodeNum); - proyNodes.resize(nodeNum); - data.visibleIDs.resize(nodeNum, ~(0U)); - linkList.clear(); - std::unordered_set meshesToLoad; - for (uint32_t i = 0; i < nodeNum; ++i) { - const auto& node = prefabData.nodes[i]; - meshesToLoad.emplace(node.mesh); - linkList[node.mesh].emplace_back(i); - - proyNodes[i].nodeId = i; - proyNodes[i].owner = this; - } + // Collect data under assetMutex, but do NOT call SetAsset inside the lock + // because SetAsset may synchronously re-enter OnAssetLoaded for already-loaded + // assets, which would deadlock on the non-recursive assetMutex. + std::vector meshesToLoad; + { + std::lock_guard lock(assetMutex); + + const auto &prefabData = prefab.Data(); + size_t nodeNum = prefabData.nodes.size(); + capacity.store(nodeNum); + proyNodes.resize(nodeNum); + data.visibleIDs.resize(nodeNum, ~(0U)); + linkList.clear(); + std::unordered_set meshSet; + for (uint32_t i = 0; i < nodeNum; ++i) { + const auto& node = prefabData.nodes[i]; + meshSet.emplace(node.mesh); + linkList[node.mesh].emplace_back(i); + + proyNodes[i].nodeId = i; + proyNodes[i].owner = this; + } - lodGroupHolders.clear(); - for (const auto& meshId : meshesToLoad) { - lodGroupHolders.emplace(meshId, SingleAssetHolder{}); + lodGroupHolders.clear(); + for (const auto& meshId : meshSet) { + lodGroupHolders.emplace(meshId, SingleAssetHolder{}); + meshesToLoad.emplace_back(meshId); + } } + // SetAsset calls are outside the lock ! safe for synchronous re-entry uint32_t num = 0; - - LOG_I(TAG, "Load Prefab %s, nodeNum%zu", uuid.ToString(), lodGroupHolders.size()); - for (auto& [id, holder] : lodGroupHolders) { - - holder.SetAsset(id, this); - LOG_I(TAG, "Load LodGroup %s, index%u", id.ToString().c_str(), num); - num++; + LOG_I(TAG, "Load Prefab %s, nodeNum%zu", uuid.ToString(), meshesToLoad.size()); + for (auto& id : meshesToLoad) { + auto iter = lodGroupHolders.find(id); + if (iter != lodGroupHolders.end()) { + iter->second.SetAsset(id, this); + LOG_I(TAG, "Load LodGroup %s, index%u", id.ToString().c_str(), num); + num++; + } } } else if (type == AssetTraits::ASSET_TYPE){ diff --git a/engine/render/backend/vulkan/src/DescriptorSet.cpp b/engine/render/backend/vulkan/src/DescriptorSet.cpp index 10dec54c..aac94e4b 100644 --- a/engine/render/backend/vulkan/src/DescriptorSet.cpp +++ b/engine/render/backend/vulkan/src/DescriptorSet.cpp @@ -106,7 +106,8 @@ namespace sky::vk { if (IsBufferDescriptor(entry.descriptorType)) { entry.pBufferInfo = &writeInfos[index].buffer; - } else if (IsImageDescriptor(entry.descriptorType)) { + } else if (IsImageDescriptor(entry.descriptorType) || + entry.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER) { auto &imageInfo = writeInfos[index].image; if (entry.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || entry.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || @@ -169,18 +170,19 @@ namespace sky::vk { } writeInfo.image.imageView = vkImageView->GetNativeHandle(); - SKY_ASSERT(binding < writeEntries.size()); - const auto &entry = writeEntries[binding]; - auto descriptorType = entry.descriptorType; + // Find the VkWriteDescriptorSet entry for this binding + VkDescriptorType descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + for (const auto &entry : writeEntries) { + if (entry.dstBinding == binding) { + descriptorType = entry.descriptorType; + break; + } + } if (descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || descriptorType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT) { -// if (mask & (rhi::AspectFlagBit::DEPTH_BIT | rhi::AspectFlagBit::STENCIL_BIT)) { -// writeInfo.image.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; -// } else { writeInfo.image.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; -// } } else if (descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) { writeInfo.image.imageLayout = VK_IMAGE_LAYOUT_GENERAL; } diff --git a/engine/render/builder/render/include/builder/render/ImageBuilder.h b/engine/render/builder/render/include/builder/render/ImageBuilder.h index 66700bd5..06438210 100644 --- a/engine/render/builder/render/include/builder/render/ImageBuilder.h +++ b/engine/render/builder/render/include/builder/render/ImageBuilder.h @@ -31,7 +31,7 @@ namespace sky::builder { const std::vector &GetExtensions() const override { return extensions; } std::string_view QueryType(const std::string &ext) const override { return AssetTraits::ASSET_TYPE; } - std::vector extensions = {".jpg", ".dds", ".ktx", ".png", ".hdr", ".image"}; + std::vector extensions = {".jpg", ".jpeg", ".dds", ".ktx", ".png", ".hdr", ".image"}; std::unordered_map configs; ImageBuildGlobalConfig globalConfig; diff --git a/engine/render/builder/render/src/ImageBuilder.cpp b/engine/render/builder/render/src/ImageBuilder.cpp index 47d13c67..8796c15a 100644 --- a/engine/render/builder/render/src/ImageBuilder.cpp +++ b/engine/render/builder/render/src/ImageBuilder.cpp @@ -54,8 +54,8 @@ namespace sky::builder { imageData.dataSize = 0; imageData.mipLevels = static_cast(image->mips.size()) - baseMip; - imageData.width = imageData.width >> baseMip; - imageData.height = imageData.height >> baseMip; + imageData.width = image->width >> baseMip; + imageData.height = image->height >> baseMip; for (uint32_t i = baseMip; i < image->mips.size(); ++i) { const auto &mipData = image->mips[i]; diff --git a/engine/render/builder/render/src/PrefabBuilder.cpp b/engine/render/builder/render/src/PrefabBuilder.cpp index f1ec2374..e50bac30 100644 --- a/engine/render/builder/render/src/PrefabBuilder.cpp +++ b/engine/render/builder/render/src/PrefabBuilder.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -106,7 +107,12 @@ namespace sky::builder { static void SaveEmbeddedTexture(const aiTexture* tex, const AssetSourcePath &sourcePath) { auto file = AssetDataBase::Get()->CreateOrOpenFile(sourcePath); - file->WriteAsArchive()->SaveRaw(reinterpret_cast(tex->pcData), tex->mWidth); + // mHeight == 0: compressed texture, mWidth is the byte size of the buffer. + // mHeight != 0: uncompressed ARGB8888, actual size is mWidth * mHeight * sizeof(aiTexel). + size_t dataSize = (tex->mHeight == 0) + ? tex->mWidth + : static_cast(tex->mWidth) * tex->mHeight * sizeof(aiTexel); + file->WriteAsArchive()->SaveRaw(reinterpret_cast(tex->pcData), dataSize); } static Uuid ProcessTexture(const aiScene *scene, const aiString& str, PrefabBuildContext &context, const AssetImportRequest &request) @@ -146,21 +152,57 @@ namespace sky::builder { uint8_t useNormalMap = false; uint8_t useMetallicRoughnessMap = false; uint8_t useMask = false; + uint8_t useTransmission = false; Vector4 baseColor = Vector4(1.f, 1.f, 1.f, 1.f); float metallic = 0.1f; float roughness = 1.0f; + float transmissionFactor = 0.0f; Uuid normalMap; Uuid emissiveMap; Uuid aoMap; Uuid baseColorMap; Uuid metallicRoughnessMap; + Uuid transmissionMap; + // Detect alpha mode from glTF aiString aiAlphaMode; if (material->Get(AI_MATKEY_GLTF_ALPHAMODE, aiAlphaMode) == 0) { std::string mode = aiAlphaMode.data; if (mode == "MASK") { useMask = true; + } else if (mode == "BLEND") { + useTransmission = true; + transmissionFactor = 1.0f; // Default for BLEND mode when no explicit factor + } + } + + // Detect opacity from non-glTF formats (e.g. FBX) + if (!useTransmission && !useMask) { + float opacity = 1.0f; + if (aiGetMaterialFloat(material, AI_MATKEY_OPACITY, &opacity) == AI_SUCCESS) { + if (opacity < (1.0f - 1e-5f)) { + useTransmission = true; + transmissionFactor = 1.0f - opacity; + } + } + } + + // Read KHR_materials_transmission extension properties + aiGetMaterialFloat(material, AI_MATKEY_TRANSMISSION_FACTOR, &transmissionFactor); + + if (material->GetTexture(AI_MATKEY_TRANSMISSION_TEXTURE, &str) == 0) { + transmissionMap = ProcessTexture(scene, str, context, request); + useTransmission = useTransmission || static_cast(transmissionMap); + } + + useTransmission = useTransmission || transmissionFactor > 0.0f; + if (useTransmission) { + auto transparentMat = AssetDataBase::Get()->RegisterAsset("materials/standard_transparent_pbr.mat"); + if (transparentMat) { + data.material = transparentMat->uuid; + } else { + LOG_W(TAG, "standard_transparent_pbr.mat not found, falling back to standard_pbr.mat"); } } @@ -185,9 +227,10 @@ namespace sky::builder { useAOMap = static_cast(aoMap); } - aiColor4D color = {}; + aiColor4D color = {0.f, 0.f, 0.f, 1.f}; aiGetMaterialColor(material, AI_MATKEY_BASE_COLOR, &color); if (color.IsBlack()) { + color = {0.f, 0.f, 0.f, 1.f}; aiGetMaterialColor(material, AI_MATKEY_COLOR_DIFFUSE, &color); } baseColor = FromAssimp(color); @@ -232,6 +275,13 @@ namespace sky::builder { } data.properties.valueMap.emplace("ENABLE_ALPHA_MASK", useMask); + data.properties.valueMap.emplace("ENABLE_TRANSMISSION", useTransmission); + if (useTransmission) { + data.properties.valueMap.emplace("TransmissionFactor", transmissionFactor); + if (transmissionMap) { + data.properties.valueMap.emplace("TransmissionMap", MaterialTexture{transmissionMap}); + } + } data.properties.valueMap.emplace("Albedo", baseColor); data.properties.valueMap.emplace("Metallic", metallic); @@ -419,6 +469,40 @@ namespace sky::builder { meshData.subMeshes.emplace_back(subMesh); } + static Uuid CreateLodGroupForMesh(const std::string &meshName, const Uuid &meshUuid, const AssetSourcePath &basePath) + { + std::string lodName = meshName; + // replace .mesh extension with .lodgroup + auto dotPos = lodName.rfind('.'); + if (dotPos != std::string::npos) { + lodName = lodName.substr(0, dotPos); + } + lodName += ".lodgroup"; + + LodGroupData lodData; + lodData.version = 0; + lodData.type = "StaticMesh"; + + LodGroupLevelData level; + level.screenSize = 1.f; + level.resId = meshUuid; + lodData.levels.emplace_back(level); + + AssetSourcePath sourcePath = {}; + sourcePath.bundle = SourceAssetBundle::WORKSPACE; + sourcePath.path = basePath.path / FilePath(lodName); + + { + auto file = AssetDataBase::Get()->CreateOrOpenFile(sourcePath); + auto archive = file->WriteAsArchive(); + JsonOutputArchive json(*archive); + lodData.SaveJson(json); + } + + auto source = AssetDataBase::Get()->RegisterAsset(sourcePath); + return source->uuid; + } + static Uuid ProcessMesh(const aiScene *scene, const aiNode *node, PrefabBuildContext& context, const AssetImportRequest &request) { std::string meshName = node->mName.length == 0 ? @@ -514,7 +598,10 @@ namespace sky::builder { auto source = AssetDataBase::Get()->RegisterAsset(sourcePath); context.meshes.emplace_back(source); - return source->uuid; + + // Create a LodGroup wrapper for this mesh 鐃緒申 PrefabComponent expects LodGroup UUIDs + Uuid lodGroupUuid = CreateLodGroupForMesh(meshName, source->uuid, context.path); + return lodGroupUuid; } static void ProcessNode(aiNode *node, const aiScene *scene, uint32_t parent, PrefabBuildContext& context, const AssetImportRequest &request) // NOLINT @@ -549,40 +636,40 @@ namespace sky::builder { channel.position.times.resize(anim->mNumPositionKeys); channel.position.keys.resize(anim->mNumPositionKeys); - // for (uint32_t i = 0; i < anim->mNumPositionKeys; ++i) { - // const auto &src = anim->mPositionKeys[i]; - // auto &dstTime = channel.position.times[i]; - // auto &dstPos = channel.position.keys[i]; - // dstTime = static_cast(src.mTime); - // dstPos.x = src.mValue.x; - // dstPos.y = src.mValue.y; - // dstPos.z = src.mValue.z; - // } + for ( uint32_t i = 0; i < anim->mNumPositionKeys; ++i) { + const auto &src = anim->mPositionKeys[i]; + auto &dstTime = channel.position.times[i]; + auto &dstPos = channel.position.keys[i]; + dstTime = static_cast(src.mTime); + dstPos.x = src.mValue.x; + dstPos.y = src.mValue.y; + dstPos.z = src.mValue.z; + } channel.scale.times.resize(anim->mNumScalingKeys); channel.scale.keys.resize(anim->mNumScalingKeys); - // for (uint32_t i = 0; i < anim->mNumScalingKeys; ++i) { - // const auto &src = anim->mScalingKeys[i]; - // auto &dstTime = channel.scale.times[i]; - // auto &dstScale = channel.scale.keys[i]; - // dstTime = static_cast(src.mTime); - // dstScale.x = src.mValue.x; - // dstScale.y = src.mValue.y; - // dstScale.z = src.mValue.z; - // } + for ( uint32_t i = 0; i < anim->mNumScalingKeys; ++i) { + const auto &src = anim->mScalingKeys[i]; + auto &dstTime = channel.scale.times[i]; + auto &dstScale = channel.scale.keys[i]; + dstTime = static_cast(src.mTime); + dstScale.x = src.mValue.x; + dstScale.y = src.mValue.y; + dstScale.z = src.mValue.z; + } channel.rotation.times.resize(anim->mNumRotationKeys); channel.rotation.keys.resize(anim->mNumRotationKeys); - // for (uint32_t i = 0; i < anim->mNumRotationKeys; ++i) { - // const auto &src = anim->mRotationKeys[i]; - // auto &dstTime = channel.rotation.times[i]; - // auto &dstRot = channel.rotation.keys[i]; - // dstTime = static_cast(src.mTime); - // dstRot.x = src.mValue.x; - // dstRot.y = src.mValue.y; - // dstRot.z = src.mValue.z; - // dstRot.w = src.mValue.w; - // } + for ( uint32_t i = 0; i < anim->mNumRotationKeys; ++i) { + const auto &src = anim->mRotationKeys[i]; + auto &dstTime = channel.rotation.times[i]; + auto &dstRot = channel.rotation.keys[i]; + dstTime = static_cast(src.mTime); + dstRot.x = src.mValue.x; + dstRot.y = src.mValue.y; + dstRot.z = src.mValue.z; + dstRot.w = src.mValue.w; + } } static void ProcessMeshChannel(const aiScene* scene, aiMeshAnim *anim, PrefabBuildContext& context) @@ -627,6 +714,8 @@ namespace sky::builder { AnimationClipAssetData data; data.version = 1; data.name = anim->mName.C_Str(); + data.frameRate = anim->mTicksPerSecond > 0 ? static_cast(anim->mTicksPerSecond) : 30.f; + data.skeleton = context.skeletonSource ? context.skeletonSource->uuid : Uuid{}; data.nodeChannels.resize(anim->mNumChannels); // node animation for (uint32_t j = 0; j < anim->mNumChannels; ++j) {; @@ -700,7 +789,7 @@ namespace sky::builder { auto prefabName = request.filePath.FileName(); context.path.bundle = SourceAssetBundle::WORKSPACE; - context.path.path = FilePath("Prefabs") / prefabName; + context.path.path = FilePath("Prefab") / prefabName; context.name = request.filePath.FileNameWithoutExt(); AssetDataBase::Get()->GetWorkSpaceFs()->CreateSubSystem(context.path.path.GetStr(), true); @@ -746,6 +835,13 @@ namespace sky::builder { auto &data = asset->Data(); data.LoadJson(json); + asset->ResetDependencies(); + for (const auto &node : data.nodes) { + if (static_cast(node.mesh)) { + asset->AddDependencies(node.mesh); + } + } + AssetManager::Get()->SaveAsset(asset, request.target); result.retCode = AssetBuildRetCode::SUCCESS; } diff --git a/engine/render/builder/render/src/image/ImageProcess.cpp b/engine/render/builder/render/src/image/ImageProcess.cpp index 2bb7fcbe..37b899fe 100644 --- a/engine/render/builder/render/src/image/ImageProcess.cpp +++ b/engine/render/builder/render/src/image/ImageProcess.cpp @@ -110,6 +110,7 @@ namespace sky::builder { auto *ptr = reinterpret_cast(dst); ptr[i] = color.v[i]; } + break; default: SKY_ASSERT("not implement") break; From 23767593bba5fd80c123596effd30e7aae6050eb Mon Sep 17 00:00:00 2001 From: Zach Date: Sat, 14 Mar 2026 10:14:17 +0800 Subject: [PATCH 08/38] [feat]: fix camera. --- .../framework/controller/SimpleController.h | 2 + .../src/controller/SimpleController.cpp | 6 ++- .../builder/render/src/PrefabBuilder.cpp | 41 ++++++++++++------- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/engine/framework/include/framework/controller/SimpleController.h b/engine/framework/include/framework/controller/SimpleController.h index c45547e0..a9c344bb 100644 --- a/engine/framework/include/framework/controller/SimpleController.h +++ b/engine/framework/include/framework/controller/SimpleController.h @@ -20,6 +20,7 @@ namespace sky { std::pair Resolve(float time, const Transform &trans); void SetMoveSpeed(float speed); + void SetMouseSensitivity(float sensitivity) { mouseSensitivity = sensitivity; } private: void OnMouseButtonDown(const MouseButtonEvent &event) override; @@ -44,6 +45,7 @@ namespace sky { int32_t currentY = 0; float moveSpeed = 3.f; + float mouseSensitivity = 20.f; const NativeWindow *window = nullptr; }; diff --git a/engine/framework/src/controller/SimpleController.cpp b/engine/framework/src/controller/SimpleController.cpp index c8b212d1..4ba35106 100644 --- a/engine/framework/src/controller/SimpleController.cpp +++ b/engine/framework/src/controller/SimpleController.cpp @@ -29,8 +29,10 @@ namespace sky { startX = currentX; startY = currentY; - euler.y -= diffX * 20000.0f * time; - euler.x -= diffY * 20000.0f * time; + // Mouse rotation is driven by pixel delta (per-frame input), + // not by deltaTime ! otherwise sensitivity varies with frame rate. + euler.y -= diffX * mouseSensitivity; + euler.x -= diffY * mouseSensitivity; res.rotation.FromEulerYZX(euler); isDirty = true; } diff --git a/engine/render/builder/render/src/PrefabBuilder.cpp b/engine/render/builder/render/src/PrefabBuilder.cpp index e50bac30..b248b979 100644 --- a/engine/render/builder/render/src/PrefabBuilder.cpp +++ b/engine/render/builder/render/src/PrefabBuilder.cpp @@ -403,11 +403,11 @@ namespace sky::builder { Vector4 *position = &context.position[subMesh.firstVertex]; StandardVertexData *vtx = &context.ext[subMesh.firstVertex]; + bool hasNormals = mesh->HasNormals(); + bool hasTangents = mesh->HasTangentsAndBitangents(); + for (unsigned int i = 0; i < mesh->mNumVertices; i++) { auto &p = mesh->mVertices[i]; - auto &n = mesh->mNormals[i]; - auto &t = mesh->mTangents[i]; - auto &b = mesh->mBitangents[i]; position[i].x = p.x; position[i].y = p.y; @@ -416,19 +416,31 @@ namespace sky::builder { subMesh.aabb.min = Min(subMesh.aabb.min, Vector3(p.x, p.y, p.z)); subMesh.aabb.max = Max(subMesh.aabb.max, Vector3(p.x, p.y, p.z)); - vtx[i].normal.x = n.x; - vtx[i].normal.y = n.y; - vtx[i].normal.z = n.z; - vtx[i].normal.w = 1.f; + if (hasNormals) { + auto &n = mesh->mNormals[i]; + vtx[i].normal.x = n.x; + vtx[i].normal.y = n.y; + vtx[i].normal.z = n.z; + vtx[i].normal.w = 1.f; + } else { + vtx[i].normal = Vector4(0.f, 1.f, 0.f, 1.f); + } + + if (hasTangents) { + auto &t = mesh->mTangents[i]; + auto &b = mesh->mBitangents[i]; - vtx[i].tangent.x = t.x; - vtx[i].tangent.y = t.y; - vtx[i].tangent.z = t.z; + vtx[i].tangent.x = t.x; + vtx[i].tangent.y = t.y; + vtx[i].tangent.z = t.z; - Vector3 tmpNormal = Vector3(n.x, n.y, n.z); - Vector3 tmpTangent = Vector3(t.x, t.y, t.z); - Vector3 tmpBiTangent = tmpNormal.Cross(tmpTangent); - vtx[i].tangent.w = ((b.x * tmpBiTangent.x < 0.0f) || (b.y * tmpBiTangent.y < 0.0f) || (b.z * tmpBiTangent.z < 0.0f)) ? -1.0f : 1.0f; + Vector3 tmpNormal = Vector3(vtx[i].normal.x, vtx[i].normal.y, vtx[i].normal.z); + Vector3 tmpTangent = Vector3(t.x, t.y, t.z); + Vector3 tmpBiTangent = tmpNormal.Cross(tmpTangent); + vtx[i].tangent.w = ((b.x * tmpBiTangent.x < 0.0f) || (b.y * tmpBiTangent.y < 0.0f) || (b.z * tmpBiTangent.z < 0.0f)) ? -1.0f : 1.0f; + } else { + vtx[i].tangent = Vector4(1.f, 0.f, 0.f, 1.f); + } if (mesh->HasVertexColors(0)) { auto &c = mesh->mColors[0][i]; @@ -467,6 +479,7 @@ namespace sky::builder { indices[3 * i + 2] = face.mIndices[2]; } meshData.subMeshes.emplace_back(subMesh); + meshData.aabb.Merge(subMesh.aabb); } static Uuid CreateLodGroupForMesh(const std::string &meshName, const Uuid &meshUuid, const AssetSourcePath &basePath) From 945dc6355d862f1d320e56cda942d90a2613130f Mon Sep 17 00:00:00 2001 From: Zach Date: Sun, 15 Mar 2026 10:53:44 +0800 Subject: [PATCH 09/38] [3rd]: update 3rd cmake --- .gitignore | 2 + agent/plan/engine_build_plan.md | 69 ++++++++ cmake/thirdparty.json | 87 ++++++++-- cmake/thirdparty/FindGKlib.cmake | 1 + cmake/thirdparty/Findboost.cmake | 29 +--- cmake/thirdparty/Findcereal.cmake | 1 - cmake/thirdparty/Findpmp.cmake | 19 --- cmake/thirdparty/Findsqlite.cmake | 18 --- cmake/thirdparty/Findvolk.cmake | 1 - cmake/thirdparty_helpers.cmake | 18 ++- python/third_party.py | 254 +++++++++++++++++++++--------- 11 files changed, 336 insertions(+), 163 deletions(-) create mode 100644 agent/plan/engine_build_plan.md delete mode 100644 cmake/thirdparty/Findcereal.cmake delete mode 100644 cmake/thirdparty/Findpmp.cmake delete mode 100644 cmake/thirdparty/Findsqlite.cmake delete mode 100644 cmake/thirdparty/Findvolk.cmake diff --git a/.gitignore b/.gitignore index cac57f2f..41b801b8 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ /test/products/ /python/.idea /review/ +/agent/backup +/build_3rd/ diff --git a/agent/plan/engine_build_plan.md b/agent/plan/engine_build_plan.md new file mode 100644 index 00000000..b43be981 --- /dev/null +++ b/agent/plan/engine_build_plan.md @@ -0,0 +1,69 @@ +# SkyEngine 綮肴≦鐚4 罩ワ + +莅≦篁綺綵膸九鐚合墾鐚**筝劫綮阪ュg筝篏睡 `python/third_party.py`** + +## Step1: 筝劫綮削綽蕁糸ц鐚 + +- ュh鐚`python/third_party.py` +- 鐚 `3RD_PATH` 綵鐚膸 CMake 臀箴莎罩よ君緇鐚 +- ィ巡擦鐚Win32鐚鐚 + +```bash +python python/third_party.py \ + -i \ + -o \ + -e \ + -p Win32 \ + -j 8 +``` + +## Step2: 綮 solution鐚CMake Configure/Generate鐚 + +- 莚贋鐚篁綺絽後 CMake 綏ョ鐚Visual Studio 筝篌 `.sln`鐚 +- ィ巡擦鐚 + +```bash +cmake -S -B \ + -G "Visual Studio 17 2022" \ + -D3RD_PATH= \ + -DSKY_BUILD_TEST=OFF +``` + +> ラ荀罨≧腮ц羌莚鐚絨 `-DSKY_BUILD_TEST=ON` + +## Step3: 綮阪 + +- ィ巡擦鐚 + +```bash +cmake --build --config Release --parallel 8 +``` + +- 篋х藥莅よ阪遺綺 `output/bin`鐚掩 CMake 臀ァ駈 + +## Step4: 絋荀菴茵 test + +- 鐚Step2 綣 `SKY_BUILD_TEST=ON`鐚綛九群絎 Step3 +- 菴茵劫鐚 + +```bash +ctest --test-dir -C Release --output-on-failure +``` + +--- + +## 筝ц + +綏臥絅鐚`agent/scripts/build_engine.py` + +腓坂鐚 + +```bash +python agent/scripts/build_engine.py --platform Win32 --config Release --jobs 8 +``` + +羌莚鐚 + +```bash +python agent/scripts/build_engine.py --platform Win32 --config Release --jobs 8 --enable-tests --run-tests +``` diff --git a/cmake/thirdparty.json b/cmake/thirdparty.json index 9f1ee099..9d21f3c2 100644 --- a/cmake/thirdparty.json +++ b/cmake/thirdparty.json @@ -1,12 +1,14 @@ { - "packages":[ + "packages": [ { "name": "ios-cmake", "url": "https://github.com/leetal/ios-cmake.git", "tag": "4.5.0", "header_only": false, "is_tool": true, - "platforms": ["IOS"] + "platforms": [ + "IOS" + ] }, { "name": "crc32c", @@ -24,7 +26,11 @@ "url": "https://github.com/paceholder/nodeeditor.git", "tag": "3.0.12", "header_only": false, - "platforms": ["Win32", "MacOS-x86", "MacOS-arm", "Linux"], + "platforms": [ + "Win32", + "MacOS-x86", + "MacOS-arm" + ], "options": { "BUILD_TESTING": "OFF", "BUILD_SHARED_LIBS": "OFF", @@ -126,7 +132,12 @@ "url": "https://github.com/libsdl-org/SDL.git", "tag": "release-2.32.6", "header_only": false, - "platforms": ["Win32", "MacOS-x86", "MacOS-arm", "Linux"], + "platforms": [ + "Win32", + "MacOS-x86", + "MacOS-arm", + "Linux" + ], "options": { "SDL_SHARED": "OFF", "SDL_STATIC": "ON", @@ -206,7 +217,9 @@ "header_only": false, "cache": "cmake/caches/PredefinedParams.cmake", "submodule": true, - "platforms": ["Win32"], + "platforms": [ + "Win32" + ], "options": { "HLSL_BUILD_DXILCONV": "OFF", "HLSL_INCLUDE_TESTS": "OFF", @@ -284,7 +297,11 @@ "url": "https://github.com/assimp/assimp.git", "tag": "v6.0.2", "header_only": false, - "platforms": ["Win32", "MacOS-x86", "MacOS-arm"], + "platforms": [ + "Win32", + "MacOS-x86", + "MacOS-arm" + ], "options": { "BUILD_SHARED_LIBS": "ON", "CMAKE_DEBUG_POSTFIX": "", @@ -297,49 +314,77 @@ "url": "https://github.com/zeux/meshoptimizer.git", "tag": "v0.23", "header_only": false, - "platforms": ["Win32", "MacOS-x86", "MacOS-arm"] + "platforms": [ + "Win32", + "MacOS-x86", + "MacOS-arm" + ] }, { "name": "stb", "url": "https://github.com/nothings/stb.git", "tag": "f75e8d1cad7d90d72ef7a4661f1b994ef78b4e31", "header_only": true, - "platforms": ["Win32", "MacOS-x86", "MacOS-arm"] + "platforms": [ + "Win32", + "MacOS-x86", + "MacOS-arm" + ] }, { "name": "PerlinNoise", "url": "https://github.com/Reputeless/PerlinNoise.git", "tag": "v3.0.0", "header_only": true, - "platforms": ["Win32", "MacOS-x86", "MacOS-arm"] + "platforms": [ + "Win32", + "MacOS-x86", + "MacOS-arm" + ] }, { "name": "ImGuizmo", "url": "https://github.com/CedricGuillemet/ImGuizmo.git", "tag": "1.83", "header_only": true, - "platforms": ["Win32", "MacOS-x86", "MacOS-arm"] + "platforms": [ + "Win32", + "MacOS-x86", + "MacOS-arm" + ] }, { "name": "GKlib", "url": "https://github.com/KarypisLab/GKlib.git", "tag": "METIS-v5.1.1-DistDGL-0.5", "header_only": false, - "platforms": ["Win32", "MacOS-x86", "MacOS-arm"] + "platforms": [ + "Win32", + "MacOS-x86", + "MacOS-arm" + ] }, { "name": "metis", "url": "https://github.com/KarypisLab/METIS.git", "tag": "v5.2.1", "header_only": false, - "platforms": ["Win32", "MacOS-x86", "MacOS-arm"] + "platforms": [ + "Win32", + "MacOS-x86", + "MacOS-arm" + ] }, { "name": "astc", "url": "https://github.com/ARM-software/astc-encoder.git", "tag": "5.2.0", "header_only": false, - "platforms": ["Win32", "MacOS-x86", "MacOS-arm"], + "platforms": [ + "Win32", + "MacOS-x86", + "MacOS-arm" + ], "options": { "ASTCENC_CLI": "OFF" } @@ -348,9 +393,19 @@ "name": "ispc_texcomp", "url": "https://github.com/bluesky013/ISPCTextureCompressor.git", "header_only": false, - "platforms": ["Win32", "MacOS-x86", "MacOS-arm"], + "platforms": [ + "Win32", + "MacOS-x86", + "MacOS-arm" + ], "custom": "build.py", "custom_need_platform": true } - ] -} + ], + "archives": { + "Win32": { + "file": "thirdparty_Win32_1935718e852e.zip", + "md5": "1935718e852e5743d2d72c550052a305" + } + } +} \ No newline at end of file diff --git a/cmake/thirdparty/FindGKlib.cmake b/cmake/thirdparty/FindGKlib.cmake index f659f864..d45b3845 100644 --- a/cmake/thirdparty/FindGKlib.cmake +++ b/cmake/thirdparty/FindGKlib.cmake @@ -1 +1,2 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../thirdparty_helpers.cmake) sky_3rd_static(GKlib LIBS GKlib) diff --git a/cmake/thirdparty/Findboost.cmake b/cmake/thirdparty/Findboost.cmake index 34a174fd..b58e084e 100644 --- a/cmake/thirdparty/Findboost.cmake +++ b/cmake/thirdparty/Findboost.cmake @@ -1,28 +1 @@ -set(LIB_NAME "boost") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") - -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) -set(${LIB_NAME}_LIBS_DIR ${${LIB_NAME}_PATH}/lib) - -set(BOOST_LIB_NAMES - container - graph -) - -foreach(lib ${BOOST_LIB_NAMES}) - set(${LIB_NAME}_LIBRARY_DEBUG ${${LIB_NAME}_LIBRARY_DEBUG} - ${${LIB_NAME}_LIBS_DIR}/Debug/libboost_${lib}${CMAKE_STATIC_LIBRARY_SUFFIX}) - - set(${LIB_NAME}_LIBRARY_RELEASE ${${LIB_NAME}_LIBRARY_RELEASE} - ${${LIB_NAME}_LIBS_DIR}/Release/libboost_${lib}${CMAKE_STATIC_LIBRARY_SUFFIX}) -endforeach() - -set(${LIB_NAME}_LIBRARY - "$<$:${${LIB_NAME}_LIBRARY_RELEASE}>" - "$<$:${${LIB_NAME}_LIBRARY_DEBUG}>") - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -target_link_libraries(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_LIBRARY}) - -set(${LIB_NAME}_FOUND True) +sky_3rd_static(boost LIB_PREFIX libboost_ LIBS container graph) diff --git a/cmake/thirdparty/Findcereal.cmake b/cmake/thirdparty/Findcereal.cmake deleted file mode 100644 index 19009da4..00000000 --- a/cmake/thirdparty/Findcereal.cmake +++ /dev/null @@ -1 +0,0 @@ -sky_3rd_header_only(cereal) \ No newline at end of file diff --git a/cmake/thirdparty/Findpmp.cmake b/cmake/thirdparty/Findpmp.cmake deleted file mode 100644 index 69359158..00000000 --- a/cmake/thirdparty/Findpmp.cmake +++ /dev/null @@ -1,19 +0,0 @@ -set(LIB_NAME "pmp") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") -if (TARGET ${TARGET_WITH_NAMESPACE}) - return() -endif() - -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) -set(${LIB_NAME}_LIBS_DIR ${${LIB_NAME}_PATH}/lib) - -set(${LIB_NAME}_LIBRARY - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}pmp${CMAKE_STATIC_LIBRARY_SUFFIX} - ${${LIB_NAME}_LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}pmp_vis${CMAKE_STATIC_LIBRARY_SUFFIX} -) - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -target_link_libraries(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_LIBRARY}) - -set(${LIB_NAME}_FOUND True) diff --git a/cmake/thirdparty/Findsqlite.cmake b/cmake/thirdparty/Findsqlite.cmake deleted file mode 100644 index fc5cca95..00000000 --- a/cmake/thirdparty/Findsqlite.cmake +++ /dev/null @@ -1,18 +0,0 @@ -set(LIB_NAME "sqlite") -set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") - -if (MSVC) - set(${LIB_NAME}_DYNAMIC_LIBRARY ${${LIB_NAME}_PATH}/sqlite3.dll) -elseif (APPLE) - set(${LIB_NAME}_DYNAMIC_LIBRARY ${${LIB_NAME}_PATH}/libsqlite3.dylib) -else() - set(${LIB_NAME}_DYNAMIC_LIBRARY ${${LIB_NAME}_PATH}/libsqliteX.so) -endif() - -set(${LIB_NAME}_INCLUDE_DIR ${${LIB_NAME}_PATH}/include) - -add_library(${TARGET_WITH_NAMESPACE} INTERFACE IMPORTED GLOBAL) -target_include_directories(${TARGET_WITH_NAMESPACE} INTERFACE ${${LIB_NAME}_INCLUDE_DIR}) -set_target_properties(${TARGET_WITH_NAMESPACE} PROPERTIES INTERFACE_DYN_LIBS ${${LIB_NAME}_DYNAMIC_LIBRARY}) - -set(${LIB_NAME}_FOUND True) diff --git a/cmake/thirdparty/Findvolk.cmake b/cmake/thirdparty/Findvolk.cmake deleted file mode 100644 index 2255efe9..00000000 --- a/cmake/thirdparty/Findvolk.cmake +++ /dev/null @@ -1 +0,0 @@ -sky_3rd_header_only(volk) diff --git a/cmake/thirdparty_helpers.cmake b/cmake/thirdparty_helpers.cmake index 090d8de4..98fee859 100644 --- a/cmake/thirdparty_helpers.cmake +++ b/cmake/thirdparty_helpers.cmake @@ -1,5 +1,9 @@ # Helper functions to reduce boilerplate in Find*.cmake modules. # Include this file BEFORE any find_package() calls. +# Safe to include multiple times (include guard). +if (COMMAND sky_3rd_static) + return() +endif() # Creates an INTERFACE IMPORTED target for header-only libraries. # Usage: @@ -32,14 +36,16 @@ endfunction() # sky_3rd_static( LIBS [lib2 ...] # [DEBUG_SUFFIX ] # [INCLUDE_SUBDIR ] +# [LIB_PREFIX ] # [EXT_LIBS ...]) # Examples: # sky_3rd_static(crc32 LIBS crc32c) +# sky_3rd_static(boost LIB_PREFIX libboost_ LIBS container graph) # sky_3rd_static(bullet3 INCLUDE_SUBDIR bullet DEBUG_SUFFIX d # LIBS Bullet3Collision Bullet3Common Bullet3Dynamics # Bullet3Geometry BulletCollision BulletDynamics LinearMath) function(sky_3rd_static LIB_NAME) - cmake_parse_arguments(ARGS "" "DEBUG_SUFFIX;INCLUDE_SUBDIR" "LIBS;EXT_LIBS" ${ARGN}) + cmake_parse_arguments(ARGS "" "DEBUG_SUFFIX;INCLUDE_SUBDIR;LIB_PREFIX" "LIBS;EXT_LIBS" ${ARGN}) set(TARGET_WITH_NAMESPACE "3rdParty::${LIB_NAME}") if (TARGET ${TARGET_WITH_NAMESPACE}) set(${LIB_NAME}_FOUND True PARENT_SCOPE) @@ -54,11 +60,17 @@ function(sky_3rd_static LIB_NAME) set(LIBS_DIR ${${LIB_NAME}_PATH}/lib) + if (ARGS_LIB_PREFIX) + set(ACTUAL_PREFIX ${ARGS_LIB_PREFIX}) + else() + set(ACTUAL_PREFIX ${CMAKE_STATIC_LIBRARY_PREFIX}) + endif() + foreach(lib ${ARGS_LIBS}) list(APPEND DEBUG_LIBS - ${LIBS_DIR}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}${lib}${ARGS_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}) + ${LIBS_DIR}/Debug/${ACTUAL_PREFIX}${lib}${ARGS_DEBUG_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}) list(APPEND RELEASE_LIBS - ${LIBS_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}${lib}${CMAKE_STATIC_LIBRARY_SUFFIX}) + ${LIBS_DIR}/Release/${ACTUAL_PREFIX}${lib}${CMAKE_STATIC_LIBRARY_SUFFIX}) endforeach() if (ARGS_EXT_LIBS) diff --git a/python/third_party.py b/python/third_party.py index b7b6edb6..c2a3d7d3 100644 --- a/python/third_party.py +++ b/python/third_party.py @@ -1,9 +1,11 @@ import argparse +import hashlib import json import os import shutil import subprocess import sys +import zipfile from pathlib import Path from git import Repo @@ -16,7 +18,8 @@ parser.add_argument('-p', '--platform', type=str, choices=["Win32", "MacOS-x86", "MacOS-arm", "Android", "IOS", "Linux"], help='膽莚綛喝') parser.add_argument('-c', '--clean', action='store_true', default=False, help='羝綏ョ') parser.add_argument('-j', '--jobs', type=int, default=0, help='綛区膽莚膾睡 (0=)') -parser.add_argument('--list', action='store_true', default=False, help='堺篆≧') +parser.add_argument('-l', '--list', action='store_true', default=False, help='堺篆≧') +parser.add_argument('-f', '--force', action='store_true', default=False, help='綣阪狗井綮削綽順ュ靶絖鐚') args = parser.parse_args() tool_chain = { @@ -29,8 +32,45 @@ } NDK_VERSION = '27.0.12077973' - -def run_cmake(build_dir: str, source_dir: str, build_type, options: dict = None, cache: str = None, components = None): +METADATA_FILE = 'build_metadata.json' + +def load_build_metadata(): + meta_path = os.path.join(args.output, METADATA_FILE) + if os.path.exists(meta_path): + with open(meta_path, 'r', encoding='utf-8') as f: + return json.load(f) + return {} + +def save_build_metadata(metadata): + meta_path = os.path.join(args.output, METADATA_FILE) + Path(args.output).mkdir(parents=True, exist_ok=True) + with open(meta_path, 'w', encoding='utf-8') as f: + json.dump(metadata, f, indent=2, ensure_ascii=False) + +def compute_package_key(package): + """Compute a hash from package config to detect changes.""" + relevant = {k: v for k, v in package.items() if k not in ('name',)} + content = json.dumps(relevant, sort_keys=True) + return hashlib.sha256(content.encode()).hexdigest()[:16] + +def is_package_up_to_date(metadata, name, package_key): + entry = metadata.get(name) + if not entry: + return False + return entry.get('key') == package_key and entry.get('platform') == args.platform + +def mark_package_built(metadata, name, package_key): + metadata[name] = { + 'key': package_key, + 'platform': args.platform + } + +def get_log_dir(): + log_dir = os.path.join(args.intermediate, '_logs') + Path(log_dir).mkdir(parents=True, exist_ok=True) + return log_dir + +def run_cmake(build_dir: str, source_dir: str, build_type, options: dict = None, cache: str = None, components = None, log_name: str = None): # 隋篆綮榊綵絖 Path(build_dir).mkdir(parents=True, exist_ok=True) @@ -45,10 +85,17 @@ def run_cmake(build_dir: str, source_dir: str, build_type, options: dict = None, for key, value in options.items(): cmake_cmd.extend([f"-D{key}={value}"]) + log_file = None + if log_name: + log_path = os.path.join(get_log_dir(), f"{log_name}_{build_type}.log") + log_file = open(log_path, 'w', encoding='utf-8') + try: # цCMake臀 print(f" [configure] {source_dir}") process = subprocess.run(cmake_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if log_file: + log_file.write(f"=== configure ===\n{process.stdout.decode('utf-8', errors='replace')}\n") # ц Build build_cmd = ["cmake", "--build", build_dir, "--config", build_type] @@ -58,6 +105,8 @@ def run_cmake(build_dir: str, source_dir: str, build_type, options: dict = None, build_cmd.append("--parallel") print(f" [build] {build_type}") process = subprocess.run(build_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if log_file: + log_file.write(f"=== build ===\n{process.stdout.decode('utf-8', errors='replace')}\n") # ц Install install_cmd = ["cmake", "--install", build_dir, "--config", build_type] @@ -68,12 +117,19 @@ def run_cmake(build_dir: str, source_dir: str, build_type, options: dict = None, print(f" [install] {build_type}") process = subprocess.run(install_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if log_file: + log_file.write(f"=== install ===\n{process.stdout.decode('utf-8', errors='replace')}\n") print(f" [done] {build_type} ") except subprocess.CalledProcessError as e: stderr_text = e.stderr.decode('utf-8', errors='replace') if isinstance(e.stderr, bytes) else str(e.stderr) + if log_file: + log_file.write(f"=== ERROR ===\n{stderr_text}\n") print(f"CMakeц紊沿乾:\n{stderr_text}") raise + finally: + if log_file: + log_file.close() def copy_package(name, install_dir, build_type): src_inc_path = os.path.join(install_dir, "include") @@ -96,29 +152,11 @@ def copy_package(name, install_dir, build_type): dst_lib = Path(str(dst_lib_path)) dst_bin = Path(str(dst_bin_path)) - if src_inc.exists(): - dst_inc.mkdir(parents=True, exist_ok=True) - if dst_inc.exists(): - shutil.rmtree(dst_inc) - shutil.copytree(src_inc, dst_inc) - - if src_src.exists(): - dst_src.mkdir(parents=True, exist_ok=True) - if dst_src.exists(): - shutil.rmtree(dst_src) - shutil.copytree(src_src, dst_src) - - if src_bin.exists(): - dst_bin.mkdir(parents=True, exist_ok=True) - if dst_bin.exists(): - shutil.rmtree(dst_bin) - shutil.copytree(src_bin, dst_bin) - - if src_lib.exists(): - dst_lib.mkdir(parents=True, exist_ok=True) - if dst_lib.exists(): - shutil.rmtree(dst_lib) - shutil.copytree(src_lib, dst_lib) + for src, dst in [(src_inc, dst_inc), (src_src, dst_src), (src_bin, dst_bin), (src_lib, dst_lib)]: + if src.exists(): + if dst.exists(): + shutil.rmtree(dst) + shutil.copytree(src, dst) def get_android_sdk_path(): @@ -149,13 +187,8 @@ def fill_android_config(options): options['ANDROID_PLATFORM'] = 'android-31' options['CMAKE_TOOLCHAIN_FILE'] = toolchain -def build_package_type(name, source_dir, build_type, options, cache, components): - build_dir = os.path.join(source_dir, f"build_{args.platform}_{build_type}") - install_dir = os.path.join(build_dir, 'install') - - # common options +def fill_common_options(options, build_type, install_dir): cmake_modules_path = os.path.join(args.engine, 'cmake', 'thirdparty').replace('\\', '/') - print(f'cmake module find path: {cmake_modules_path}') options['3RD_PATH'] = args.output options['3RD_FIND_PATH'] = cmake_modules_path options['BUILD_TESTING'] = 'OFF' @@ -167,13 +200,41 @@ def build_package_type(name, source_dir, build_type, options, cache, components) elif args.platform == 'IOS': fill_ios_config(options) - run_cmake(build_dir, source_dir, build_type, options, cache, components) +def build_package_type(name, source_dir, build_type, options, cache, components, header_only=False): + build_dir = os.path.join(source_dir, f"build_{args.platform}_{build_type}") + install_dir = os.path.join(build_dir, 'install') + + fill_common_options(options, build_type, install_dir) + + if header_only: + # header-only: configure + install only, skip build + Path(build_dir).mkdir(parents=True, exist_ok=True) + + cmake_cmd = ["cmake", "-S", source_dir, "-B", build_dir, "-G", tool_chain[args.platform]] + if cache: + cmake_cmd = ["cmake", "-S", source_dir, "-C", cache, "-B", build_dir, "-G", tool_chain[args.platform]] + if options: + for key, value in options.items(): + cmake_cmd.extend([f"-D{key}={value}"]) + + print(f" [configure] {source_dir}") + subprocess.run(cmake_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + install_cmd = ["cmake", "--install", build_dir, "--config", build_type] + print(f" [install] {build_type}") + subprocess.run(install_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + else: + run_cmake(build_dir, source_dir, build_type, options, cache, components, log_name=name) + copy_package(name, install_dir, build_type) -def build_package(name, source_dir, options, cache, components): +def build_package(name, source_dir, options, cache, components, header_only=False): print("build package ...", name) - build_package_type(name, source_dir, 'Debug', options, cache, components) - build_package_type(name, source_dir, 'Release', options, cache, components) + if header_only: + build_package_type(name, source_dir, 'Release', dict(options), cache, components, header_only=True) + else: + build_package_type(name, source_dir, 'Debug', dict(options), cache, components) + build_package_type(name, source_dir, 'Release', dict(options), cache, components) print("build package done") def process_package(package): @@ -200,8 +261,13 @@ def process_package(package): return clone_dir = os.path.join(args.intermediate, name) + need_full_history = submodule or submodules if not os.path.exists(clone_dir): - repo = Repo.clone_from(url, str(clone_dir)) + clone_kwargs = {} + if not need_full_history and tag: + clone_kwargs['depth'] = 1 + clone_kwargs['branch'] = tag + repo = Repo.clone_from(url, str(clone_dir), **clone_kwargs) else: repo = Repo(str(clone_dir)) @@ -214,7 +280,7 @@ def process_package(package): repo.git.clean('-xdf') return - if tag: + if tag and need_full_history: repo.git.fetch('--tags') if tag not in repo.tags: raise ValueError(f"Tag '{tag}' 筝絖篋篁綺筝") @@ -237,8 +303,8 @@ def process_package(package): if submodule is True: print(repo.submodules) - for submodule in repo.submodules: - submodule.update(init=True, recursive=True, force=True) + for sm in repo.submodules: + sm.update(init=True, recursive=True, force=True) if submodules: for subName in submodules: @@ -260,39 +326,52 @@ def process_package(package): if is_tool is True: return - if header_only is True: - # header-only: only configure+install, skip build - build_header_only(name, source_dir, options, cache) - else: - build_package(name, source_dir, options, cache, components) - -def build_header_only(name, source_dir, options, cache): - print(f"[header-only] {name}") - for build_type in ['Release']: - build_dir = os.path.join(source_dir, f"build_{args.platform}_{build_type}") - install_dir = os.path.join(build_dir, 'install') - - cmake_modules_path = os.path.join(args.engine, 'cmake', 'thirdparty').replace('\\', '/') - options['3RD_PATH'] = args.output - options['3RD_FIND_PATH'] = cmake_modules_path - options['BUILD_TESTING'] = 'OFF' - options['CMAKE_INSTALL_PREFIX'] = install_dir - options['CMAKE_BUILD_TYPE'] = build_type - - Path(build_dir).mkdir(parents=True, exist_ok=True) - - cmake_cmd = ["cmake", "-S", source_dir, "-B", build_dir, "-G", tool_chain[args.platform]] - if options: - for key, value in options.items(): - cmake_cmd.extend([f"-D{key}={value}"]) - - subprocess.run(cmake_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - - install_cmd = ["cmake", "--install", build_dir, "--config", build_type] - subprocess.run(install_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + build_package(name, source_dir, options, cache, components, header_only=header_only) + +def archive_output(json_file, data): + """Zip the entire output directory and record MD5 into thirdparty.json.""" + archive_dir = os.path.join(args.engine, 'build_3rd', 'archives') + Path(archive_dir).mkdir(parents=True, exist_ok=True) + + # create zip + zip_name = f"thirdparty_{args.platform}.zip" + zip_path = os.path.join(archive_dir, zip_name) + print(f"[archive] creating {zip_path} ...") + + output_root = Path(args.output) + with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf: + for file in sorted(output_root.rglob('*')): + if file.is_file() and file.name != METADATA_FILE: + arcname = file.relative_to(output_root) + zf.write(file, arcname) + + # compute md5 + md5 = hashlib.md5() + with open(zip_path, 'rb') as f: + for chunk in iter(lambda: f.read(8192), b''): + md5.update(chunk) + md5_hex = md5.hexdigest() + + # rename with short hash + final_name = f"thirdparty_{args.platform}_{md5_hex[:12]}.zip" + final_path = os.path.join(archive_dir, final_name) + if os.path.exists(final_path): + os.remove(final_path) + os.rename(zip_path, final_path) + + # update thirdparty.json + if 'archives' not in data: + data['archives'] = {} + data['archives'][args.platform] = { + 'file': final_name, + 'md5': md5_hex + } + with open(json_file, 'w', encoding='utf-8') as f: + json.dump(data, f, indent='\t', ensure_ascii=False) + + size_mb = os.path.getsize(final_path) / (1024 * 1024) + print(f"[archive] {final_name} ({size_mb:.1f} MB, md5: {md5_hex})") - copy_package(name, install_dir, build_type) - print(f"[header-only] {name} done") def list_packages(packages): print(f"{'Name':<20} {'Tag':<25} {'Type':<12} {'Platforms'}") @@ -315,13 +394,34 @@ def app_main(): list_packages(packages) return - filtered = list(filter(lambda pkg: pkg['name'] == args.target, packages)) + metadata = load_build_metadata() - if len(filtered) > 0: - process_package(filtered[0]) + if args.target: + filtered = list(filter(lambda pkg: pkg['name'] == args.target, packages)) else: - for package in packages: - process_package(package) + filtered = packages + + built_any = False + for package in filtered: + name = package.get('name', '') + if not name: + continue + package_key = compute_package_key(package) + + if not args.force and not args.clean and is_package_up_to_date(metadata, name, package_key): + print(f"[skip] {name} (綏我, 篏睡 -f 綣阪狗綮)") + continue + + process_package(package) + + if not args.clean: + mark_package_built(metadata, name, package_key) + save_build_metadata(metadata) + built_any = True + + # archive after build + if built_any and not args.clean: + archive_output(json_file, data) if __name__ == "__main__": app_main() From 83810cadbc36f04ed9ab848b29742b8997c193df Mon Sep 17 00:00:00 2001 From: Zach Date: Sun, 15 Mar 2026 17:47:48 +0800 Subject: [PATCH 10/38] [doc]: project reference. --- agent/project_reference.md | 288 +++++++++++++++++++++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 agent/project_reference.md diff --git a/agent/project_reference.md b/agent/project_reference.md new file mode 100644 index 00000000..9887d150 --- /dev/null +++ b/agent/project_reference.md @@ -0,0 +1,288 @@ +# SkyEngine 蕁合ユ + +## 网茹 + +SkyEngine 罔≦羝御鏁鐚C++20鐚 Windows / macOS / Android / iOS / Linux +綮榊鎧膸鐚CMake 3.10+鐚莨榊綵 `output/bin/` + +--- + +## 綵膸 + +``` +SkyEngine/ + engine/ # 綣後罔≦ + core/ # 榊綺 (医/絖/篁/ュ/綣罩) + framework/ # 綺罅 (腦/莨/莎篋/筝) + animation/ # 紫鎧膸 + render/ # 羝我膤紫 (紊腴) + physics/ # ・ + navigation/ # 絲取・ + editor/ # 膽莨 (Qt5) + launcher/ # 菴茵九ュ + test/ # 羌莚 + plugins/ # 篁 + tools/ # 綣綏ュ + assets/ # 莎羣篁 (shader/material/texture) + configs/ # 菴茵狗臀 + cmake/ # 綮咲臀 + python/ # Python 綏ュ潔 + agent/ # AI Agent 莨篁 +``` + +--- + +## 綮榊筝茹 + +### 綣後 (Static Libraries) + +| Target | 綵 | 箴莎 | 莚贋 | +|--------|------|------|------| +| `Core` | engine/core | sfmt, crc32, taskflow, boost, rapidjson | 榊綺鐚医/絖/篁/絽/ュ | +| `Framework` | engine/framework | Core, SDL | 綺罅駈腦/莨/莎篋/筝 | +| `Animation` | engine/animation | Core | 薨薨弱/羞桁/ | +| `RHI` | engine/render/backend/rhi | Core | 羝我隋篁倶処院絮 | +| `VulkanRHI` / `VulkanRHI.Static` | engine/render/backend/vulkan | RHI, vma | Vulkan 腴 | +| `DX12RHI` | engine/render/backend/dx12 | RHI | DX12 腴 (Win32) | +| `MetalRHI` | engine/render/backend/metal | RHI | Metal 腴 (Apple) | +| `RenderCore` | engine/render/core | Core, RHI, ShaderCompiler.Static | 羝我後莅丈 | +| `RenderAdaptor` | engine/render/adaptor | RenderCore, Animation, ImGuiRender, Framework | 羝我絮 | +| `ShaderCompiler.Static` | engine/render/shader | Core, RHI, glslang, SPIRVCross, dxcompiler | Shader 膽莚 | +| `RenderBuilder.Static` | engine/render/builder/render | Framework, ShaderCompiler.Static, stb, assimp, ispc_texcomp, meshoptimizer, metis | 莎羣綮 | +| `ImGuiRender` | engine/render/imgui | RenderCore, Framework, imgui | ImGui 羝我絮 | +| `Physics` | engine/physics | Framework | ・ | +| `Navigation` | engine/navigation | Framework | 絲取・ | +| `EditorFramework` | engine/editor/framework | Framework, RenderAdaptor, Physics, Qt5 | 膽莨罅 | + +### 延昆罔≦ (Shared Libraries) + +| Target | 莚贋 | +|--------|------| +| `SkyRender` | 筝紙顕罔≦ | +| `SkyRender.Builder` | 羝我莎羣綮堺─ | +| `SkyRender.Editor` | 膽莨羝我 (Qt5, nodeeditor) | +| `ShaderCompiler` | Shader 膽莚 (綺) | +| `Terrain` / `TerrainEditor` | 医就膤紫 | + +### ц篁 + +| Target | 莚贋 | +|--------|------| +| `Launcher` | 羝御颷茵 | +| `Editor` | 膽莨 (Qt5) | +| `ShaderTool` | 巡擦茵 shader 膽莚綏ュ | +| `AssetBuilder` | 莎篋ф綮阪轡 | +| `SkyImageViewer` | 膾合ョ | + +### 篁 (Shared Libraries) + +| Target | 綣 | 箴莎 | +|--------|------|------| +| `BulletPhysicsModule` | SKY_BUILD_BULLET | bullet3 | +| `RecastNavigation` | SKY_BUILD_RECAST | recast | +| `PythonModule` | SKY_BUILD_PYTHON | cpython | +| `CompressionModule` | SKY_BUILD_COMPRESSION | lz4 | +| `FreeTypeModule` | SKY_BUILD_FREETYPE | freetype | +| `ImGuizmoModule` | SKY_BUILD_EDITOR | ImGuizmo | +| `XRModule` | SKY_BUILD_XR | OpenXR | + +### 羌莚 + +| Target | 莚贋 | +|--------|------| +| `CoreTest` | Core 罔≦羌莚 | +| `FrameworkTest` | Framework 罔≦羌莚 | +| `AnimationTest` | 紙莚 | +| `VulkanTest` | Vulkan 羌莚 | +| `RenderTest` | 羝我羌莚 | +| `RenderBuilderTest` | 綮阪羌莚 | +| `TerrainTest` | 医就羌莚 | + +--- + +## CMake 膽莚蕁 + +| 蕁 | 藥莅 | 莚贋 | +|------|------|------| +| `SKY_BUILD_EDITOR` | OFF | 膽莨 (荀 Qt5) | +| `SKY_BUILD_TEST` | OFF | 羌莚 | +| `SKY_BUILD_TOOL` | OFF | 莎篋у轡 | +| `SKY_BUILD_GLES` | OFF | OpenGL ES 腴 | +| `SKY_BUILD_XR` | OFF | OpenXR | +| `SKY_BUILD_PYTHON` | OFF | Python | +| `SKY_BUILD_CPYTHON` | OFF | CPython 綉 | +| `SKY_BUILD_COMPRESSION` | OFF | LZ4 膽 | +| `SKY_BUILD_FREETYPE` | OFF | 絖篏羝我 | +| `SKY_BUILD_BULLET` | OFF | Bullet | +| `SKY_BUILD_RECAST` | OFF | Recast 絲取 | +| `SKY_USE_TRACY` | OFF | Tracy ц遵 | +| `SKY_MATH_SIMD` | OFF | SIMD 医篌 | + +--- + +## 筝劫 + +### 後箴莎 (紮膸荀) + +| | | 膠糸 | | +|------|------|------|------| +| crc32c | 1.1.2 | static | CRC32C ♂ | +| sfmt | 1.5.4 | static | 堺亥 (SIMD) | +| boost | 1.88.0 | static | container, graph | +| taskflow | v3.7.0 | header-only | 篁糸≦攻茵莪綺 | +| rapidjson | v1.1.0 | header-only | JSON 茹f | +| sdl | 2.32.6 | static | 腦/莨 (Win/Mac/Linux) | +| googletest | v1.17.0 | static | 羌莚 | + +### 羝我箴莎 + +| | | 膠糸 | | +|------|------|------|------| +| vma | v3.2.1 | header-only | Vulkan Memory Allocator | +| imgui | v1.88 | header-only | 恰倶─綣 GUI | +| glslang | 15.3.0 | static | GLSLSPIR-V 膽莚 | +| SPIRV-Cross | sdk-1.4.313.0 | static | SPIR-V 絨/莉 | +| dxcompiler | v1.8.2502 | static | HLSLDXIL/SPIR-V (Win32) | + +### 膽莨箴莎 (SKY_BUILD_EDITOR) + +| | | 膠糸 | | +|------|------|------|------| +| assimp | v6.0.2 | shared | 罔≦絲弱 | +| meshoptimizer | v0.23 | static | 臀寂/膊 | +| stb | (commit) | header-only | 上莉 | +| ispc_texcomp | (custom) | shared (dll) | BC/ASTC 膾合膽 (ISPC) | +| astc | 5.2.0 | static | ASTC 膾合膽 (ARM) | +| GKlib | METIS-v5.1.1 | static | 上肴 | +| metis | v5.2.1 | static | 臀弱 (meshlet) | +| ImGuizmo | 1.83 | header-only | 3D Gizmo | +| nodeeditor | 3.0.12 | static | 合莨 | +| PerlinNoise | v3.0.0 | header-only | 紕亥 | + +### 篁銀莎 + +| | | 綣 | +|------|------|------| +| bullet3 | 3.25 | SKY_BUILD_BULLET | +| recast | v1.6.0 | SKY_BUILD_RECAST | +| lz4 | v1.10.0 | SKY_BUILD_COMPRESSION | +| freetype | 2-13-3 | SKY_BUILD_FREETYPE | +| tracy | v0.11.1 | SKY_USE_TRACY | + +### Patches 茵 + +荀綺 patch 鐚crc32, GKlib, glslang, imgui, ImGuizmo, metis, nodeeditor, PerlinNoise, rapidjson, recast, sfmt, stb + +--- + +## 罔≦莉初臀 + +### 羝御閞≦ (configs/modules_game.json) +``` +FreeTypeModule depends: SkyRender +``` + +### 膽莨罔≦ (configs/modules_editor.json) +``` +ImGuizmoModule depends: SkyRender.Editor +FreeTypeModule depends: SkyRender.Editor +BulletPhysicsModule depends: SkyRender.Editor +CompressionModule depends: (none) +``` + +--- + +## 莎羣膤紫 + +### Asset Build Presets (configs/asset_build_presets.json) +``` +bundles: common, tex_pc, tex_mobile +presets: + windows common + tex_pc + macos common + tex_pc + ios common + tex_mobile +``` + +### 羝我蘂莉 (configs/render_preload_assets.json) +- gui.tech, text.tech, post_processing.tech, depth.tech +- depth_resolve.tech, depth_downsample.tech, debug.tech +- skybox.tech, meshlet_debug.tech, brdf_lut.tech + +### Shader 茵 (assets/shaders/) +``` +蕁九: box, brdf_lut, debug, depth*, draw_id, error, skybox, standard_pbr +絖綵: color/, common/, depth/, layout/, lighting/, mesh/, + pipeline/, post_processing/, terrain/, ui/, vertex/ +``` + +### 茣 (assets/materials/) +- default_terrain.mat, skybox.mat, standard_pbr.mat +- standard_transparent_pbr.mat, unlit.mat, volume_simple.mat + +--- + +## 羝我腴 + +| 腴 | 綛喝 | 倶 | +|------|------|------| +| Vulkan | Win32, macOS, Android | 筝肢腴 | +| DX12 | Win32 | | +| Metal | macOS, iOS | | +| GLES | Android | (SKY_BUILD_GLES) | + +--- + +## Python 綏ュ潔 + +| | 莚贋 | +|------|------| +| `python/third_party.py` | 筝劫膽莚 | +| `python/setup.py` | 綣臀 UI | +| `python/project_manager.py` | 蕁合膊∞ UI | +| `python/build/Presets.py` | CMake Preset | +| `python/build/Project.py` | 蕁合臀/綮 | + +### 筝劫膽莚巡擦 +```bash +# 堺 +python python/third_party.py -e . -p Win32 --list + +# 靶莚 (Win32) +python python/third_party.py -i -o -e . -p Win32 + +# 膽莚筝 +python python/third_party.py -i -o -e . -p Win32 -t + +# 綣阪狗綮 +python python/third_party.py -i -o -e . -p Win32 -f +``` + +--- + +## 膾合膽羌羂雁鎖 + +### 綵号 +- **BC 弱** (PC): ispc_texcomp (BC1/BC3/BC4/BC5/BC6H/BC7) 蘂膽莚 DLL +- **ASTC 弱** (Mobile): astc-encoder (ARM 絎劫) 綺 +- **莎羣**: tex_pc (BC) / tex_mobile (ASTC) +- **綮阪ュ**: RenderBuilder.Static 箴莎 ispc_texcomp + astc + +### 篏睡 +- `RenderBuilder.Static` 丈・ `3rdParty::ispc_texcomp` +- `Findispc_texcomp.cmake` 箴 DLL (Win32) / dylib (macOS) + +--- + +## 箴莎括鎧 (膊) + +``` +Core Framework RenderAdaptor Launcher + Physics BulletPhysicsModule + Navigation RecastNavigation + EditorFramework Editor + +RHI VulkanRHI / DX12RHI / MetalRHI +RHI RenderCore RenderAdaptor + ShaderCompiler.Static RenderBuilder.Static + SkyRender.Builder +``` From 61130ba1c2022ffe7a94c138da101d1e2b5c7d24 Mon Sep 17 00:00:00 2001 From: Zach Date: Sun, 15 Mar 2026 18:51:14 +0800 Subject: [PATCH 11/38] [build]: plugin system driven by JSON config --- CMakeLists.txt | 3 +- cmake/options.cmake | 20 +++------ cmake/plugin_config.cmake | 47 +++++++++++++++++++ cmake/plugin_switches.cmake | 59 ++++++++++++++++++++++++ plugins/CMakeLists.txt | 72 +++++++++++++++++------------- plugins/bullet/CMakeLists.txt | 4 +- plugins/bullet/plugin.json | 8 ++++ plugins/compression/CMakeLists.txt | 4 +- plugins/compression/plugin.json | 8 ++++ plugins/freetype/CMakeLists.txt | 4 +- plugins/freetype/plugin.json | 8 ++++ plugins/guizmo/CMakeLists.txt | 4 +- plugins/guizmo/plugin.json | 8 ++++ plugins/plugins.json | 59 ++++++++++++++++++++++++ plugins/pvs/CMakeLists.txt | 7 +-- plugins/pvs/plugin.json | 12 +++++ plugins/python/CMakeLists.txt | 4 +- plugins/python/plugin.json | 8 ++++ plugins/recast/CMakeLists.txt | 4 +- plugins/recast/plugin.json | 8 ++++ plugins/terrain/CMakeLists.txt | 7 +-- plugins/terrain/plugin.json | 12 +++++ plugins/xr/CMakeLists.txt | 4 +- plugins/xr/plugin.json | 8 ++++ 24 files changed, 321 insertions(+), 61 deletions(-) create mode 100644 cmake/plugin_config.cmake create mode 100644 cmake/plugin_switches.cmake create mode 100644 plugins/bullet/plugin.json create mode 100644 plugins/compression/plugin.json create mode 100644 plugins/freetype/plugin.json create mode 100644 plugins/guizmo/plugin.json create mode 100644 plugins/plugins.json create mode 100644 plugins/pvs/plugin.json create mode 100644 plugins/python/plugin.json create mode 100644 plugins/recast/plugin.json create mode 100644 plugins/terrain/plugin.json create mode 100644 plugins/xr/plugin.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 5475f5b6..658e5965 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.10.0) +cmake_minimum_required(VERSION 3.19) # 3.19+ required for string(JSON ...) PROJECT(SkyEngine) @@ -11,6 +11,7 @@ set(ENGINE_ROOT ${CMAKE_CURRENT_LIST_DIR}) include(${ENGINE_ROOT}/cmake/functions.cmake) include(${ENGINE_ROOT}/cmake/options.cmake) +include(${ENGINE_ROOT}/cmake/plugin_switches.cmake) include(${ENGINE_ROOT}/cmake/configuration.cmake) include(${ENGINE_ROOT}/cmake/thirdparty.cmake) diff --git a/cmake/options.cmake b/cmake/options.cmake index 359ff9f2..c6be650f 100644 --- a/cmake/options.cmake +++ b/cmake/options.cmake @@ -7,18 +7,8 @@ if (SKY_BUILD_EDITOR) set(SKY_EDITOR ON) endif () -option(SKY_BUILD_GLES "build gles" OFF) -option(SKY_BUILD_TEST "build test" OFF) -option(SKY_USE_TRACY "use tracy profiler" OFF) - -# todo: controlled by project config json. -option(SKY_BUILD_XR "xr plugin" OFF) -option(SKY_BUILD_PYTHON "python plugin" OFF) -option(SKY_BUILD_CPYTHON "cpython plugin" OFF) -option(SKY_BUILD_COMPRESSION "compression support" OFF) -option(SKY_BUILD_FREETYPE "freetype text rendering" OFF) -option(SKY_BUILD_BULLET "bullet physics" OFF) -option(SKY_BUILD_RECAST "recast navigation" OFF) -option(SKY_BUILD_TOOL "build tools" OFF) - -option(SKY_MATH_SIMD "enable simd math" OFF) \ No newline at end of file +option(SKY_BUILD_GLES "build gles" OFF) +option(SKY_BUILD_TEST "build test" OFF) +option(SKY_USE_TRACY "use tracy profiler" OFF) +option(SKY_BUILD_TOOL "build tools" OFF) +option(SKY_MATH_SIMD "enable simd math" OFF) \ No newline at end of file diff --git a/cmake/plugin_config.cmake b/cmake/plugin_config.cmake new file mode 100644 index 00000000..dcf1abfd --- /dev/null +++ b/cmake/plugin_config.cmake @@ -0,0 +1,47 @@ +if (COMMAND sky_plugin_apply_target_dependencies) + return() +endif() + +function(sky_plugin_apply_target_dependencies) + cmake_parse_arguments(TMP + "" + "CONFIG" + "" + ${ARGN} + ) + + if (NOT TMP_CONFIG) + message(FATAL_ERROR "sky_plugin_apply_target_dependencies: CONFIG is required") + endif() + + if (NOT EXISTS "${TMP_CONFIG}") + message(FATAL_ERROR "sky_plugin_apply_target_dependencies: config not found: ${TMP_CONFIG}") + endif() + + file(READ "${TMP_CONFIG}" _plugin_json) + string(JSON _target_count LENGTH "${_plugin_json}" "targets") + + if (_target_count EQUAL 0) + return() + endif() + + math(EXPR _target_last "${_target_count} - 1") + foreach(_ti RANGE ${_target_last}) + string(JSON _target_name GET "${_plugin_json}" "targets" "${_ti}" "name") + string(JSON _dep_count ERROR_VARIABLE _dep_count_error + LENGTH "${_plugin_json}" "targets" "${_ti}" "dependencies") + + set(_deps) + if ((NOT _dep_count_error) AND (_dep_count GREATER 0)) + math(EXPR _dep_last "${_dep_count} - 1") + foreach(_di RANGE ${_dep_last}) + string(JSON _dep GET "${_plugin_json}" "targets" "${_ti}" "dependencies" "${_di}") + list(APPEND _deps "${_dep}") + endforeach() + endif() + + if (_deps) + sky_add_dependency(TARGET ${_target_name} DEPENDENCIES ${_deps}) + endif() + endforeach() +endfunction() diff --git a/cmake/plugin_switches.cmake b/cmake/plugin_switches.cmake new file mode 100644 index 00000000..67a7cea5 --- /dev/null +++ b/cmake/plugin_switches.cmake @@ -0,0 +1,59 @@ +set(_SKY_PLUGINS_SWITCHES_CONFIG "${ENGINE_ROOT}/plugins/plugins.json") + +if (NOT EXISTS "${_SKY_PLUGINS_SWITCHES_CONFIG}") + message(FATAL_ERROR "plugins/plugins.json not found: ${_SKY_PLUGINS_SWITCHES_CONFIG}") +endif() + +file(READ "${_SKY_PLUGINS_SWITCHES_CONFIG}" _SKY_PLUGINS_SWITCHES_JSON) +string(MD5 _SKY_PLUGINS_SWITCHES_HASH "${_SKY_PLUGINS_SWITCHES_JSON}") + +set(_SKY_PLUGINS_SWITCHES_HASH_PREV "" CACHE INTERNAL + "MD5 of plugins/plugins.json on last configure") + +if (NOT "${_SKY_PLUGINS_SWITCHES_HASH}" STREQUAL "${_SKY_PLUGINS_SWITCHES_HASH_PREV}") + message(STATUS "[SkyEngine] plugins/plugins.json changed - refreshing plugin switches") + set(_SKY_PLUGINS_SWITCHES_HASH_PREV "${_SKY_PLUGINS_SWITCHES_HASH}" + CACHE INTERNAL "MD5 of plugins/plugins.json on last configure" FORCE) + set(_SKY_PLUGIN_SWITCHES_CHANGED TRUE) +else() + set(_SKY_PLUGIN_SWITCHES_CHANGED FALSE) +endif() + +string(JSON _sky_plugin_count LENGTH "${_SKY_PLUGINS_SWITCHES_JSON}" "plugins") +if (_sky_plugin_count GREATER 0) + math(EXPR _sky_plugin_last "${_sky_plugin_count} - 1") + foreach(_i RANGE ${_sky_plugin_last}) + string(JSON _plugin_name GET "${_SKY_PLUGINS_SWITCHES_JSON}" "plugins" "${_i}" "name") + string(JSON _cmake_var GET "${_SKY_PLUGINS_SWITCHES_JSON}" "plugins" "${_i}" "cmake_var") + string(JSON _enabled GET "${_SKY_PLUGINS_SWITCHES_JSON}" "plugins" "${_i}" "enabled") + if (_enabled) + set(_default ON) + else() + set(_default OFF) + endif() + if (_SKY_PLUGIN_SWITCHES_CHANGED) + set(${_cmake_var} ${_default} CACHE BOOL "Plugin switch: ${_plugin_name}" FORCE) + else() + set(${_cmake_var} ${_default} CACHE BOOL "Plugin switch: ${_plugin_name}") + endif() + endforeach() +endif() + +# cpython is derived from python plugin switch. +if (SKY_BUILD_PYTHON) + set(SKY_BUILD_CPYTHON ON CACHE BOOL "CPython embed (required by python plugin)" FORCE) +else() + set(SKY_BUILD_CPYTHON OFF CACHE BOOL "CPython embed (required by python plugin)" FORCE) +endif() + +unset(_sky_plugin_count) +unset(_sky_plugin_last) +unset(_i) +unset(_plugin_name) +unset(_cmake_var) +unset(_enabled) +unset(_default) +unset(_SKY_PLUGIN_SWITCHES_CHANGED) +unset(_SKY_PLUGINS_SWITCHES_JSON) +unset(_SKY_PLUGINS_SWITCHES_HASH) +unset(_SKY_PLUGINS_SWITCHES_CONFIG) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index cce5aa0f..6a250899 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,37 +1,45 @@ set(CURRENT_FOLDER Plugins) -if (SKY_EDITOR) - add_subdirectory(guizmo) -endif () - -if (SKY_BUILD_PYTHON) - add_subdirectory(python) -endif () - -if (SKY_BUILD_XR) - add_subdirectory(xr) -endif () - -if (SKY_BUILD_BULLET) - add_subdirectory(bullet) -endif () - -if (SKY_BUILD_RECAST) - add_subdirectory(recast) -endif () - -if (SKY_BUILD_FREETYPE) - add_subdirectory(freetype) -endif () - -if (SKY_BUILD_TERRAIN) - add_subdirectory(terrain) -endif () +set(_SKY_PLUGINS_CONFIG "${CMAKE_CURRENT_LIST_DIR}/plugins.json") +if (NOT EXISTS "${_SKY_PLUGINS_CONFIG}") + message(FATAL_ERROR "plugins/plugins.json not found: ${_SKY_PLUGINS_CONFIG}") +endif() -if (SKY_BUILD_COMPRESSION) - add_subdirectory(compression) +file(READ "${_SKY_PLUGINS_CONFIG}" _SKY_PLUGINS_JSON) +string(JSON _sky_plugin_count LENGTH "${_SKY_PLUGINS_JSON}" "plugins") + +if (_sky_plugin_count GREATER 0) + math(EXPR _sky_plugin_last "${_sky_plugin_count} - 1") + foreach(_i RANGE ${_sky_plugin_last}) + string(JSON _plugin_name GET "${_SKY_PLUGINS_JSON}" "plugins" "${_i}" "name") + string(JSON _plugin_dir GET "${_SKY_PLUGINS_JSON}" "plugins" "${_i}" "dir") + string(JSON _cmake_var GET "${_SKY_PLUGINS_JSON}" "plugins" "${_i}" "cmake_var") + string(JSON _requires_editor ERROR_VARIABLE _requires_editor_err + GET "${_SKY_PLUGINS_JSON}" "plugins" "${_i}" "requires_editor") + + set(_can_build TRUE) + if ((NOT _requires_editor_err) AND _requires_editor AND (NOT SKY_EDITOR)) + set(_can_build FALSE) + endif() + + if (_can_build AND ${_cmake_var}) + message(STATUS "[SkyEngine] Enable plugin: ${_plugin_name}") + set(CURRENT_FOLDER "Plugins/${_plugin_dir}") + add_subdirectory(${_plugin_dir}) + endif() + endforeach() endif() -if (SKY_BUILD_PVS) - add_subdirectory(pvs) -endif() \ No newline at end of file +set(CURRENT_FOLDER Plugins) + +unset(_SKY_PLUGINS_CONFIG) +unset(_SKY_PLUGINS_JSON) +unset(_sky_plugin_count) +unset(_sky_plugin_last) +unset(_i) +unset(_plugin_name) +unset(_plugin_dir) +unset(_cmake_var) +unset(_requires_editor) +unset(_requires_editor_err) +unset(_can_build) \ No newline at end of file diff --git a/plugins/bullet/CMakeLists.txt b/plugins/bullet/CMakeLists.txt index eca335d1..398b2c1e 100644 --- a/plugins/bullet/CMakeLists.txt +++ b/plugins/bullet/CMakeLists.txt @@ -1,6 +1,8 @@ file(GLOB_RECURSE SRC_FILES src/*) file(GLOB_RECURSE INC_FILES include/*) +include(${ENGINE_ROOT}/cmake/plugin_config.cmake) + sky_add_library(TARGET BulletPhysicsModule SHARED SOURCES ${SRC_FILES} @@ -16,4 +18,4 @@ sky_add_library(TARGET BulletPhysicsModule SHARED 3rdParty::bullet3 ) -sky_add_dependency(TARGET BulletPhysicsModule DEPENDENCIES Launcher Editor) \ No newline at end of file +sky_plugin_apply_target_dependencies(CONFIG "${CMAKE_CURRENT_LIST_DIR}/plugin.json") \ No newline at end of file diff --git a/plugins/bullet/plugin.json b/plugins/bullet/plugin.json new file mode 100644 index 00000000..46d17d6a --- /dev/null +++ b/plugins/bullet/plugin.json @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "name": "BulletPhysicsModule", + "dependencies": ["Launcher", "Editor"] + } + ] +} diff --git a/plugins/compression/CMakeLists.txt b/plugins/compression/CMakeLists.txt index f7aa458b..cd300fa3 100644 --- a/plugins/compression/CMakeLists.txt +++ b/plugins/compression/CMakeLists.txt @@ -1,6 +1,8 @@ file(GLOB_RECURSE SRC_FILES src/*) file(GLOB_RECURSE INC_FILES include/*) +include(${ENGINE_ROOT}/cmake/plugin_config.cmake) + sky_add_library(TARGET CompressionModule SHARED SOURCES ${SRC_FILES} @@ -15,4 +17,4 @@ sky_add_library(TARGET CompressionModule SHARED 3rdParty::lz4 ) -sky_add_dependency(TARGET CompressionModule DEPENDENCIES Launcher Editor) +sky_plugin_apply_target_dependencies(CONFIG "${CMAKE_CURRENT_LIST_DIR}/plugin.json") diff --git a/plugins/compression/plugin.json b/plugins/compression/plugin.json new file mode 100644 index 00000000..c1da3d14 --- /dev/null +++ b/plugins/compression/plugin.json @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "name": "CompressionModule", + "dependencies": ["Launcher", "Editor"] + } + ] +} diff --git a/plugins/freetype/CMakeLists.txt b/plugins/freetype/CMakeLists.txt index 94cb7e54..04ecbdb9 100644 --- a/plugins/freetype/CMakeLists.txt +++ b/plugins/freetype/CMakeLists.txt @@ -1,6 +1,8 @@ file(GLOB_RECURSE SRC_FILES src/*) file(GLOB_RECURSE INC_FILES include/*) +include(${ENGINE_ROOT}/cmake/plugin_config.cmake) + sky_add_library(TARGET FreeTypeModule SHARED SOURCES ${SRC_FILES} @@ -16,4 +18,4 @@ sky_add_library(TARGET FreeTypeModule SHARED 3rdParty::freetype ) -sky_add_dependency(TARGET FreeTypeModule DEPENDENCIES Launcher Editor) \ No newline at end of file +sky_plugin_apply_target_dependencies(CONFIG "${CMAKE_CURRENT_LIST_DIR}/plugin.json") \ No newline at end of file diff --git a/plugins/freetype/plugin.json b/plugins/freetype/plugin.json new file mode 100644 index 00000000..736d7fa1 --- /dev/null +++ b/plugins/freetype/plugin.json @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "name": "FreeTypeModule", + "dependencies": ["Launcher", "Editor"] + } + ] +} diff --git a/plugins/guizmo/CMakeLists.txt b/plugins/guizmo/CMakeLists.txt index ce555500..7ce9db4a 100644 --- a/plugins/guizmo/CMakeLists.txt +++ b/plugins/guizmo/CMakeLists.txt @@ -1,6 +1,8 @@ file(GLOB_RECURSE SRC_FILES src/*) file(GLOB_RECURSE INC_FILES include/*) +include(${ENGINE_ROOT}/cmake/plugin_config.cmake) + sky_add_library(TARGET ImGuizmoModule SHARED SOURCES ${SRC_FILES} @@ -17,4 +19,4 @@ sky_add_library(TARGET ImGuizmoModule SHARED 3rdParty::ImGuizmo ) -sky_add_dependency(TARGET ImGuizmoModule DEPENDENCIES Launcher Editor) \ No newline at end of file +sky_plugin_apply_target_dependencies(CONFIG "${CMAKE_CURRENT_LIST_DIR}/plugin.json") \ No newline at end of file diff --git a/plugins/guizmo/plugin.json b/plugins/guizmo/plugin.json new file mode 100644 index 00000000..b72658af --- /dev/null +++ b/plugins/guizmo/plugin.json @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "name": "ImGuizmoModule", + "dependencies": ["Launcher", "Editor"] + } + ] +} diff --git a/plugins/plugins.json b/plugins/plugins.json new file mode 100644 index 00000000..40ff6dec --- /dev/null +++ b/plugins/plugins.json @@ -0,0 +1,59 @@ +{ + "plugins": [ + { + "name": "guizmo", + "dir": "guizmo", + "cmake_var": "SKY_BUILD_GUIZMO", + "enabled": true, + "requires_editor": true + }, + { + "name": "python", + "dir": "python", + "cmake_var": "SKY_BUILD_PYTHON", + "enabled": false + }, + { + "name": "xr", + "dir": "xr", + "cmake_var": "SKY_BUILD_XR", + "enabled": false + }, + { + "name": "bullet", + "dir": "bullet", + "cmake_var": "SKY_BUILD_BULLET", + "enabled": true + }, + { + "name": "recast", + "dir": "recast", + "cmake_var": "SKY_BUILD_RECAST", + "enabled": true + }, + { + "name": "freetype", + "dir": "freetype", + "cmake_var": "SKY_BUILD_FREETYPE", + "enabled": true + }, + { + "name": "terrain", + "dir": "terrain", + "cmake_var": "SKY_BUILD_TERRAIN", + "enabled": true + }, + { + "name": "compression", + "dir": "compression", + "cmake_var": "SKY_BUILD_COMPRESSION", + "enabled": true + }, + { + "name": "pvs", + "dir": "pvs", + "cmake_var": "SKY_BUILD_PVS", + "enabled": true + } + ] +} diff --git a/plugins/pvs/CMakeLists.txt b/plugins/pvs/CMakeLists.txt index 99e7ed10..85d87f99 100644 --- a/plugins/pvs/CMakeLists.txt +++ b/plugins/pvs/CMakeLists.txt @@ -6,6 +6,8 @@ file(GLOB_RECURSE EDITOR_INC_FILES editor/include/*) file(GLOB_RECURSE TEST_FILES test/*) +include(${ENGINE_ROOT}/cmake/plugin_config.cmake) + sky_add_library(TARGET PVS.Static STATIC SOURCES ${RUNTIME_SRC_FILES} @@ -28,8 +30,6 @@ sky_add_library(TARGET PVS SHARED PVS.Static ) -sky_add_dependency(TARGET PVS DEPENDENCIES Launcher) - if (SKY_BUILD_EDITOR) sky_add_library(TARGET PVS.Editor SHARED SOURCES @@ -44,9 +44,10 @@ if (SKY_BUILD_EDITOR) PVS.Static EditorFramework ) - sky_add_dependency(TARGET PVS.Editor DEPENDENCIES Editor) endif () +sky_plugin_apply_target_dependencies(CONFIG "${CMAKE_CURRENT_LIST_DIR}/plugin.json") + sky_add_test(TARGET PVSTest SOURCES ${TEST_FILES} diff --git a/plugins/pvs/plugin.json b/plugins/pvs/plugin.json new file mode 100644 index 00000000..89ee1434 --- /dev/null +++ b/plugins/pvs/plugin.json @@ -0,0 +1,12 @@ +{ + "targets": [ + { + "name": "PVS", + "dependencies": ["Launcher"] + }, + { + "name": "PVS.Editor", + "dependencies": ["Editor"] + } + ] +} diff --git a/plugins/python/CMakeLists.txt b/plugins/python/CMakeLists.txt index 00aa94f8..6a3cdcf0 100644 --- a/plugins/python/CMakeLists.txt +++ b/plugins/python/CMakeLists.txt @@ -1,6 +1,8 @@ file(GLOB_RECURSE SRC_FILES src/*) file(GLOB_RECURSE INC_FILES include/*) +include(${ENGINE_ROOT}/cmake/plugin_config.cmake) + sky_add_library(TARGET PythonModule SHARED SOURCES ${SRC_FILES} @@ -15,4 +17,4 @@ sky_add_library(TARGET PythonModule SHARED 3rdParty::cpython ) -sky_add_dependency(TARGET PythonModule DEPENDENCIES Launcher Editor) \ No newline at end of file +sky_plugin_apply_target_dependencies(CONFIG "${CMAKE_CURRENT_LIST_DIR}/plugin.json") \ No newline at end of file diff --git a/plugins/python/plugin.json b/plugins/python/plugin.json new file mode 100644 index 00000000..185ecd65 --- /dev/null +++ b/plugins/python/plugin.json @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "name": "PythonModule", + "dependencies": ["Launcher", "Editor"] + } + ] +} diff --git a/plugins/recast/CMakeLists.txt b/plugins/recast/CMakeLists.txt index e6857b73..7c45dc46 100644 --- a/plugins/recast/CMakeLists.txt +++ b/plugins/recast/CMakeLists.txt @@ -1,6 +1,8 @@ file(GLOB_RECURSE SRC_FILES src/*) file(GLOB_RECURSE INC_FILES include/*) +include(${ENGINE_ROOT}/cmake/plugin_config.cmake) + sky_add_library(TARGET RecastNavigation SHARED SOURCES ${SRC_FILES} @@ -17,4 +19,4 @@ sky_add_library(TARGET RecastNavigation SHARED 3rdParty::recast ) -sky_add_dependency(TARGET RecastNavigation DEPENDENCIES Launcher Editor) \ No newline at end of file +sky_plugin_apply_target_dependencies(CONFIG "${CMAKE_CURRENT_LIST_DIR}/plugin.json") \ No newline at end of file diff --git a/plugins/recast/plugin.json b/plugins/recast/plugin.json new file mode 100644 index 00000000..fceae6e9 --- /dev/null +++ b/plugins/recast/plugin.json @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "name": "RecastNavigation", + "dependencies": ["Launcher", "Editor"] + } + ] +} diff --git a/plugins/terrain/CMakeLists.txt b/plugins/terrain/CMakeLists.txt index 6914f923..529572a1 100644 --- a/plugins/terrain/CMakeLists.txt +++ b/plugins/terrain/CMakeLists.txt @@ -9,6 +9,8 @@ file(GLOB_RECURSE EDITOR_INC_FILES editor/include/*) file(GLOB_RECURSE TEST_FILES test/*) +include(${ENGINE_ROOT}/cmake/plugin_config.cmake) + sky_add_library(TARGET Terrain.Static STATIC SOURCES ${RUNTIME_SRC_FILES} @@ -31,8 +33,6 @@ sky_add_library(TARGET Terrain SHARED Terrain.Static ) -sky_add_dependency(TARGET Terrain DEPENDENCIES Launcher) - if (SKY_BUILD_EDITOR) sky_add_library(TARGET TerrainEditor SHARED SOURCES @@ -48,9 +48,10 @@ if (SKY_BUILD_EDITOR) EditorFramework 3rdParty::PerlinNoise ) - sky_add_dependency(TARGET TerrainEditor DEPENDENCIES Editor) endif () +sky_plugin_apply_target_dependencies(CONFIG "${CMAKE_CURRENT_LIST_DIR}/plugin.json") + sky_add_library(TARGET TerrainTools STATIC SOURCES ${TOOL_SRC_FILES} diff --git a/plugins/terrain/plugin.json b/plugins/terrain/plugin.json new file mode 100644 index 00000000..af21e1b6 --- /dev/null +++ b/plugins/terrain/plugin.json @@ -0,0 +1,12 @@ +{ + "targets": [ + { + "name": "Terrain", + "dependencies": ["Launcher"] + }, + { + "name": "TerrainEditor", + "dependencies": ["Editor"] + } + ] +} diff --git a/plugins/xr/CMakeLists.txt b/plugins/xr/CMakeLists.txt index 4ec6ec80..7491f779 100644 --- a/plugins/xr/CMakeLists.txt +++ b/plugins/xr/CMakeLists.txt @@ -1,6 +1,8 @@ file(GLOB_RECURSE SRC_FILES src/*) file(GLOB_RECURSE INC_FILES include/*) +include(${ENGINE_ROOT}/cmake/plugin_config.cmake) + if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") file(GLOB_RECURSE PLATFORM_SRC platform/windows/*) elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Android") @@ -33,4 +35,4 @@ sky_add_library(TARGET XRModule SHARED XRModule.Static ) -sky_add_dependency(TARGET XRModule DEPENDENCIES Launcher) \ No newline at end of file +sky_plugin_apply_target_dependencies(CONFIG "${CMAKE_CURRENT_LIST_DIR}/plugin.json") \ No newline at end of file diff --git a/plugins/xr/plugin.json b/plugins/xr/plugin.json new file mode 100644 index 00000000..a80c612a --- /dev/null +++ b/plugins/xr/plugin.json @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "name": "XRModule", + "dependencies": ["Launcher"] + } + ] +} From d2af04e17af7a975534ea278c45341a76511cef6 Mon Sep 17 00:00:00 2001 From: Zach Date: Sun, 15 Mar 2026 21:55:03 +0800 Subject: [PATCH 12/38] [feat]: crc32 replace google::crc32c --- .gitmodules | 3 - agent/project_reference.md | 1 - cmake/patches/crc32.patch | 13 - cmake/thirdparty.cmake | 1 - cmake/thirdparty.json | 11 - cmake/thirdparty/Findcrc32.cmake | 1 - engine/core/CMakeLists.txt | 1 - engine/core/include/core/crypto/crc32/Crc32.h | 20 + engine/core/src/crypto/crc32/Crc32.cpp | 414 ++++++++++++++++++ engine/core/src/hash/Crc32.cpp | 8 +- engine/test/core/CryptoTest.cpp | 257 ++++++++++- 11 files changed, 694 insertions(+), 36 deletions(-) delete mode 100644 cmake/patches/crc32.patch delete mode 100644 cmake/thirdparty/Findcrc32.cmake create mode 100644 engine/core/include/core/crypto/crc32/Crc32.h create mode 100644 engine/core/src/crypto/crc32/Crc32.cpp diff --git a/.gitmodules b/.gitmodules index 7a3b7ed1..ec81732c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,9 +13,6 @@ [submodule "thirdparty/rapidjson"] path = thirdparty/rapidjson url = https://github.com/Tencent/rapidjson.git -[submodule "thirdparty/crc32c"] - path = thirdparty/crc32c - url = git@github.com:google/crc32c.git [submodule "thirdparty/assimp"] path = thirdparty/assimp url = git@github.com:assimp/assimp.git diff --git a/agent/project_reference.md b/agent/project_reference.md index 9887d150..6529326f 100644 --- a/agent/project_reference.md +++ b/agent/project_reference.md @@ -126,7 +126,6 @@ SkyEngine/ | | | 膠糸 | | |------|------|------|------| -| crc32c | 1.1.2 | static | CRC32C ♂ | | sfmt | 1.5.4 | static | 堺亥 (SIMD) | | boost | 1.88.0 | static | container, graph | | taskflow | v3.7.0 | header-only | 篁糸≦攻茵莪綺 | diff --git a/cmake/patches/crc32.patch b/cmake/patches/crc32.patch deleted file mode 100644 index a7d468c4..00000000 --- a/cmake/patches/crc32.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 8490728..69e8fb2 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -2,7 +2,7 @@ - # Use of this source code is governed by a BSD-style license that can be - # found in the LICENSE file. See the AUTHORS file for names of contributors. - --cmake_minimum_required(VERSION 3.1) -+cmake_minimum_required(VERSION 3.10) - project(Crc32c VERSION 1.1.0 LANGUAGES C CXX) - - # C standard can be overridden when this is used as a sub-project. diff --git a/cmake/thirdparty.cmake b/cmake/thirdparty.cmake index 1f25f7bb..097472c9 100644 --- a/cmake/thirdparty.cmake +++ b/cmake/thirdparty.cmake @@ -18,7 +18,6 @@ endfunction() if(EXISTS ${3RD_PATH}) # core - sky_find_3rd(TARGET crc32 DIR crc32c) sky_find_3rd(TARGET sfmt DIR sfmt) sky_find_3rd(TARGET boost DIR boost) sky_find_3rd(TARGET taskflow DIR taskflow) diff --git a/cmake/thirdparty.json b/cmake/thirdparty.json index 9d21f3c2..db4a5b90 100644 --- a/cmake/thirdparty.json +++ b/cmake/thirdparty.json @@ -10,17 +10,6 @@ "IOS" ] }, - { - "name": "crc32c", - "url": "https://github.com/google/crc32c.git", - "tag": "1.1.2", - "header_only": false, - "options": { - "CRC32C_BUILD_BENCHMARKS": "OFF", - "CRC32C_BUILD_TESTS": "OFF", - "CRC32C_USE_GLOG": "OFF" - } - }, { "name": "nodeeditor", "url": "https://github.com/paceholder/nodeeditor.git", diff --git a/cmake/thirdparty/Findcrc32.cmake b/cmake/thirdparty/Findcrc32.cmake deleted file mode 100644 index 97983f03..00000000 --- a/cmake/thirdparty/Findcrc32.cmake +++ /dev/null @@ -1 +0,0 @@ -sky_3rd_static(crc32 LIBS crc32c) diff --git a/engine/core/CMakeLists.txt b/engine/core/CMakeLists.txt index 1d1bd98d..853c37ab 100644 --- a/engine/core/CMakeLists.txt +++ b/engine/core/CMakeLists.txt @@ -33,7 +33,6 @@ sky_add_library(TARGET Core STATIC include LINK_LIBS 3rdParty::sfmt - 3rdParty::crc32 3rdParty::taskflow 3rdParty::boost 3rdParty::rapidjson diff --git a/engine/core/include/core/crypto/crc32/Crc32.h b/engine/core/include/core/crypto/crc32/Crc32.h new file mode 100644 index 00000000..4a2122ca --- /dev/null +++ b/engine/core/include/core/crypto/crc32/Crc32.h @@ -0,0 +1,20 @@ +// +// Created by Copilot on 2025/3/15. +// + +#pragma once + +#include +#include + +namespace sky { + + // CRC32C (Castagnoli) - polynomial 0x1EDC6F41 + uint32_t Crc32C(const uint8_t *data, size_t size); + uint32_t Crc32C(const uint8_t *data, size_t size, uint32_t crc); + + // CRC32 (IEEE 802.3) - polynomial 0x04C11DB7 + uint32_t CalcCrc32(const uint8_t *data, size_t size); + uint32_t CalcCrc32(const uint8_t *data, size_t size, uint32_t crc); + +} // namespace sky diff --git a/engine/core/src/crypto/crc32/Crc32.cpp b/engine/core/src/crypto/crc32/Crc32.cpp new file mode 100644 index 00000000..d1f8e9b4 --- /dev/null +++ b/engine/core/src/crypto/crc32/Crc32.cpp @@ -0,0 +1,414 @@ +// +// Created by Copilot on 2025/3/15. +// + +#include +#include + +// --- Platform detection for hardware CRC32C --- +#if defined(_M_X64) || (defined(__x86_64__) && defined(__SSE4_2__)) + #if defined(_MSC_VER) + #include + #else + #include + #endif + #define SKY_CRC32C_X86 1 +#elif defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) + #include + #define SKY_CRC32C_ARM 1 +#endif + +namespace sky { + + namespace { + + // ----------------------------------------------------------------- + // Fixed lookup tables (constexpr evaluated at compile time, + // stored in .rodata; identical to a hand-written literal array) + // ----------------------------------------------------------------- + + static constexpr uint32_t CRC32C_TABLE[256] = { // Castagnoli 0x82F63B78 + 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, + 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, + 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, + 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, + 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, + 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, + 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, + 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, + 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, + 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, + 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, + 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, + 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, + 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, + 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, + 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, + 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, + 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, + 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, + 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, + 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, + 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, + 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, + 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, + 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, + 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, + 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, + 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, + 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, + 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, + 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, + 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351, + }; + + static constexpr uint32_t CRC32_TABLE[256] = { // IEEE 0xEDB88320 + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D, + }; + + // ----------------------------------------------------------------- + // Software fallback + // ----------------------------------------------------------------- + + inline uint32_t SoftwareCrc(const uint32_t *table, const uint8_t *data, size_t size, uint32_t crc) + { + crc = ~crc; + for (size_t i = 0; i < size; ++i) { + crc = table[(crc ^ data[i]) & 0xFF] ^ (crc >> 8); + } + return ~crc; + } + + // ----------------------------------------------------------------- + // Hardware CRC32C (SSE4.2 / ARM CRC) + // + // 3-way parallel processing with 4096-byte blocks. + // The CRC32 instruction has 3-cycle latency / 1-cycle throughput, + // so 3 independent streams fully hide the pipeline latency. + // Skip tables provide O(1) CRC combination. + // ----------------------------------------------------------------- + +#if defined(SKY_CRC32C_X86) || defined(SKY_CRC32C_ARM) + + static constexpr size_t BLOCK = 4096; // Tier 0 segment + static constexpr size_t BLOCK1 = 1360; // Tier 1 segment + static constexpr size_t BLOCK2 = 336; // Tier 2 segment + static constexpr size_t PREFETCH_HORIZON = 256; // bytes ahead + + // Pre-computed skip tables for combining 3 parallel CRC streams. + // Tier 0: SKIP_1X / SKIP_2X for BLOCK (4096 / 8192) + static constexpr uint32_t SKIP_1X[8][16] = { + {0x00000000, 0xC2A5B65E, 0x80A71A4D, 0x4202AC13, 0x04A2426B, 0xC607F435, 0x84055826, 0x46A0EE78, 0x094484D6, 0xCBE13288, 0x89E39E9B, 0x4B4628C5, 0x0DE6C6BD, 0xCF4370E3, 0x8D41DCF0, 0x4FE46AAE}, + {0x00000000, 0x128909AC, 0x25121358, 0x379B1AF4, 0x4A2426B0, 0x58AD2F1C, 0x6F3635E8, 0x7DBF3C44, 0x94484D60, 0x86C144CC, 0xB15A5E38, 0xA3D35794, 0xDE6C6BD0, 0xCCE5627C, 0xFB7E7888, 0xE9F77124}, + {0x00000000, 0x2D7CEC31, 0x5AF9D862, 0x77853453, 0xB5F3B0C4, 0x988F5CF5, 0xEF0A68A6, 0xC2768497, 0x6E0B1779, 0x4377FB48, 0x34F2CF1B, 0x198E232A, 0xDBF8A7BD, 0xF6844B8C, 0x81017FDF, 0xAC7D93EE}, + {0x00000000, 0xDC162EF2, 0xBDC02B15, 0x61D605E7, 0x7E6C20DB, 0xA27A0E29, 0xC3AC0BCE, 0x1FBA253C, 0xFCD841B6, 0x20CE6F44, 0x41186AA3, 0x9D0E4451, 0x82B4616D, 0x5EA24F9F, 0x3F744A78, 0xE362648A}, + {0x00000000, 0xFC5CF59D, 0xFD559DCB, 0x01096856, 0xFF474D67, 0x031BB8FA, 0x0212D0AC, 0xFE4E2531, 0xFB62EC3F, 0x073E19A2, 0x063771F4, 0xFA6B8469, 0x0425A158, 0xF87954C5, 0xF9703C93, 0x052CC90E}, + {0x00000000, 0xF329AE8F, 0xE3BF2BEF, 0x10968560, 0xC292212F, 0x31BB8FA0, 0x212D0AC0, 0xD204A44F, 0x80C834AF, 0x73E19A20, 0x63771F40, 0x905EB1CF, 0x425A1580, 0xB173BB0F, 0xA1E53E6F, 0x52CC90E0}, + {0x00000000, 0x047C1FAF, 0x08F83F5E, 0x0C8420F1, 0x11F07EBC, 0x158C6113, 0x190841E2, 0x1D745E4D, 0x23E0FD78, 0x279CE2D7, 0x2B18C226, 0x2F64DD89, 0x321083C4, 0x366C9C6B, 0x3AE8BC9A, 0x3E94A335}, + {0x00000000, 0x47C1FAF0, 0x8F83F5E0, 0xC8420F10, 0x1AEB9D31, 0x5D2A67C1, 0x956868D1, 0xD2A99221, 0x35D73A62, 0x7216C092, 0xBA54CF82, 0xFD953572, 0x2F3CA753, 0x68FD5DA3, 0xA0BF52B3, 0xE77EA843} + }; + + static constexpr uint32_t SKIP_2X[8][16] = { + {0x00000000, 0xE040E0AC, 0xC56DB7A9, 0x252D5705, 0x8F3719A3, 0x6F77F90F, 0x4A5AAE0A, 0xAA1A4EA6, 0x1B8245B7, 0xFBC2A51B, 0xDEEFF21E, 0x3EAF12B2, 0x94B55C14, 0x74F5BCB8, 0x51D8EBBD, 0xB1980B11}, + {0x00000000, 0x37048B6E, 0x6E0916DC, 0x590D9DB2, 0xDC122DB8, 0xEB16A6D6, 0xB21B3B64, 0x851FB00A, 0xBDC82D81, 0x8ACCA6EF, 0xD3C13B5D, 0xE4C5B033, 0x61DA0039, 0x56DE8B57, 0x0FD316E5, 0x38D79D8B}, + {0x00000000, 0x7E7C2DF3, 0xFCF85BE6, 0x82847615, 0xFC1CC13D, 0x8260ECCE, 0x00E49ADB, 0x7E98B728, 0xFDD5F48B, 0x83A9D978, 0x012DAF6D, 0x7F51829E, 0x01C935B6, 0x7FB51845, 0xFD316E50, 0x834D43A3}, + {0x00000000, 0xFE479FE7, 0xF963493F, 0x0724D6D8, 0xF72AE48F, 0x096D7B68, 0x0E49ADB0, 0xF00E3257, 0xEBB9BFEF, 0x15FE2008, 0x12DAF6D0, 0xEC9D6937, 0x1C935B60, 0xE2D4C487, 0xE5F0125F, 0x1BB78DB8}, + {0x00000000, 0xD29F092F, 0xA0D264AF, 0x724D6D80, 0x4448BFAF, 0x96D7B680, 0xE49ADB00, 0x3605D22F, 0x88917F5E, 0x5A0E7671, 0x28431BF1, 0xFADC12DE, 0xCCD9C0F1, 0x1E46C9DE, 0x6C0BA45E, 0xBE94AD71}, + {0x00000000, 0x14CE884D, 0x299D109A, 0x3D5398D7, 0x533A2134, 0x47F4A979, 0x7AA731AE, 0x6E69B9E3, 0xA6744268, 0xB2BACA25, 0x8FE952F2, 0x9B27DABF, 0xF54E635C, 0xE180EB11, 0xDCD373C6, 0xC81DFB8B}, + {0x00000000, 0x4904F221, 0x9209E442, 0xDB0D1663, 0x21FFBE75, 0x68FB4C54, 0xB3F65A37, 0xFAF2A816, 0x43FF7CEA, 0x0AFB8ECB, 0xD1F698A8, 0x98F26A89, 0x6200C29F, 0x2B0430BE, 0xF00926DD, 0xB90DD4FC}, + {0x00000000, 0x87FEF9D4, 0x0A118559, 0x8DEF7C8D, 0x14230AB2, 0x93DDF366, 0x1E328FEB, 0x99CC763F, 0x28461564, 0xAFB8ECB0, 0x2257903D, 0xA5A969E9, 0x3C651FD6, 0xBB9BE602, 0x36749A8F, 0xB18A635B} + }; + + // Tier 1: SKIP1_1X / SKIP1_2X for BLOCK1 (1360 / 2720) + static constexpr uint32_t SKIP1_1X[8][16] = { + {0x00000000, 0x79113270, 0xF22264E0, 0x8B335690, 0xE1A8BF31, 0x98B98D41, 0x138ADBD1, 0x6A9BE9A1, 0xC6BD0893, 0xBFAC3AE3, 0x349F6C73, 0x4D8E5E03, 0x2715B7A2, 0x5E0485D2, 0xD537D342, 0xAC26E132}, + {0x00000000, 0x889667D7, 0x14C0B95F, 0x9C56DE88, 0x298172BE, 0xA1171569, 0x3D41CBE1, 0xB5D7AC36, 0x5302E57C, 0xDB9482AB, 0x47C25C23, 0xCF543BF4, 0x7A8397C2, 0xF215F015, 0x6E432E9D, 0xE6D5494A}, + {0x00000000, 0xA605CAF8, 0x49E7E301, 0xEFE229F9, 0x93CFC602, 0x35CA0CFA, 0xDA282503, 0x7C2DEFFB, 0x2273FAF5, 0x8476300D, 0x6B9419F4, 0xCD91D30C, 0xB1BC3CF7, 0x17B9F60F, 0xF85BDFF6, 0x5E5E150E}, + {0x00000000, 0x44E7F5EA, 0x89CFEBD4, 0xCD281E3E, 0x1673A159, 0x529454B3, 0x9FBC4A8D, 0xDB5BBF67, 0x2CE742B2, 0x6800B758, 0xA528A966, 0xE1CF5C8C, 0x3A94E3EB, 0x7E731601, 0xB35B083F, 0xF7BCFDD5}, + {0x00000000, 0x59CE8564, 0xB39D0AC8, 0xEA538FAC, 0x62D66361, 0x3B18E605, 0xD14B69A9, 0x8885ECCD, 0xC5ACC6C2, 0x9C6243A6, 0x7631CC0A, 0x2FFF496E, 0xA77AA5A3, 0xFEB420C7, 0x14E7AF6B, 0x4D292A0F}, + {0x00000000, 0x8EB5FB75, 0x1887801B, 0x96327B6E, 0x310F0036, 0xBFBAFB43, 0x2988802D, 0xA73D7B58, 0x621E006C, 0xECABFB19, 0x7A998077, 0xF42C7B02, 0x5311005A, 0xDDA4FB2F, 0x4B968041, 0xC5237B34}, + {0x00000000, 0xC43C00D8, 0x8D947741, 0x49A87799, 0x1EC49873, 0xDAF898AB, 0x9350EF32, 0x576CEFEA, 0x3D8930E6, 0xF9B5303E, 0xB01D47A7, 0x7421477F, 0x234DA895, 0xE771A84D, 0xAED9DFD4, 0x6AE5DF0C}, + {0x00000000, 0x7B1261CC, 0xF624C398, 0x8D36A254, 0xE9A5F1C1, 0x92B7900D, 0x1F813259, 0x64935395, 0xD6A79573, 0xADB5F4BF, 0x208356EB, 0x5B913727, 0x3F0264B2, 0x4410057E, 0xC926A72A, 0xB234C6E6} + }; + + static constexpr uint32_t SKIP1_2X[8][16] = { + {0x00000000, 0x7B454CB3, 0xF68A9966, 0x8DCFD5D5, 0xE8F9443D, 0x93BC088E, 0x1E73DD5B, 0x653691E8, 0xD41EFE8B, 0xAF5BB238, 0x229467ED, 0x59D12B5E, 0x3CE7BAB6, 0x47A2F605, 0xCA6D23D0, 0xB1286F63}, + {0x00000000, 0xADD18BE7, 0x5E4F613F, 0xF39EEAD8, 0xBC9EC27E, 0x114F4999, 0xE2D1A341, 0x4F0028A6, 0x7CD1F20D, 0xD10079EA, 0x229E9332, 0x8F4F18D5, 0xC04F3073, 0x6D9EBB94, 0x9E00514C, 0x33D1DAAB}, + {0x00000000, 0xF9A3E41A, 0xF6ABBEC5, 0x0F085ADF, 0xE8BB0B7B, 0x1118EF61, 0x1E10B5BE, 0xE7B351A4, 0xD49A6007, 0x2D39841D, 0x2231DEC2, 0xDB923AD8, 0x3C216B7C, 0xC5828F66, 0xCA8AD5B9, 0x332931A3}, + {0x00000000, 0xACD8B6FF, 0x5C5D1B0F, 0xF085ADF0, 0xB8BA361E, 0x146280E1, 0xE4E72D11, 0x483F9BEE, 0x74981ACD, 0xD840AC32, 0x28C501C2, 0x841DB73D, 0xCC222CD3, 0x60FA9A2C, 0x907F37DC, 0x3CA78123}, + {0x00000000, 0xE930359A, 0xD78C1DC5, 0x3EBC285F, 0xAAF44D7B, 0x43C478E1, 0x7D7850BE, 0x94486524, 0x5004EC07, 0xB934D99D, 0x8788F1C2, 0x6EB8C458, 0xFAF0A17C, 0x13C094E6, 0x2D7CBCB9, 0xC44C8923}, + {0x00000000, 0xA009D80E, 0x45FFC6ED, 0xE5F61EE3, 0x8BFF8DDA, 0x2BF655D4, 0xCE004B37, 0x6E099339, 0x12136D45, 0xB21AB54B, 0x57ECABA8, 0xF7E573A6, 0x99ECE09F, 0x39E53891, 0xDC132672, 0x7C1AFE7C}, + {0x00000000, 0x2426DA8A, 0x484DB514, 0x6C6B6F9E, 0x909B6A28, 0xB4BDB0A2, 0xD8D6DF3C, 0xFCF005B6, 0x24DAA2A1, 0x00FC782B, 0x6C9717B5, 0x48B1CD3F, 0xB441C889, 0x90671203, 0xFC0C7D9D, 0xD82AA717}, + {0x00000000, 0x49B54542, 0x936A8A84, 0xDADFCFC6, 0x233963F9, 0x6A8C26BB, 0xB053E97D, 0xF9E6AC3F, 0x4672C7F2, 0x0FC782B0, 0xD5184D76, 0x9CAD0834, 0x654BA40B, 0x2CFEE149, 0xF6212E8F, 0xBF946BCD} + }; + + // Tier 2: SKIP2_1X / SKIP2_2X for BLOCK2 (336 / 672) + static constexpr uint32_t SKIP2_1X[8][16] = { + {0x00000000, 0x8F158014, 0x1BC776D9, 0x94D2F6CD, 0x378EEDB2, 0xB89B6DA6, 0x2C499B6B, 0xA35C1B7F, 0x6F1DDB64, 0xE0085B70, 0x74DAADBD, 0xFBCF2DA9, 0x589336D6, 0xD786B6C2, 0x4354400F, 0xCC41C01B}, + {0x00000000, 0xDE3BB6C8, 0xB99B1B61, 0x67A0ADA9, 0x76DA4033, 0xA8E1F6FB, 0xCF415B52, 0x117AED9A, 0xEDB48066, 0x338F36AE, 0x542F9B07, 0x8A142DCF, 0x9B6EC055, 0x4555769D, 0x22F5DB34, 0xFCCE6DFC}, + {0x00000000, 0xDE85763D, 0xB8E69A8B, 0x6663ECB6, 0x742143E7, 0xAAA435DA, 0xCCC7D96C, 0x1242AF51, 0xE84287CE, 0x36C7F1F3, 0x50A41D45, 0x8E216B78, 0x9C63C429, 0x42E6B214, 0x24855EA2, 0xFA00289F}, + {0x00000000, 0xD569796D, 0xAF3E842B, 0x7A57FD46, 0x5B917EA7, 0x8EF807CA, 0xF4AFFA8C, 0x21C683E1, 0xB722FD4E, 0x624B8423, 0x181C7965, 0xCD750008, 0xECB383E9, 0x39DAFA84, 0x438D07C2, 0x96E47EAF}, + {0x00000000, 0x6BA98C6D, 0xD75318DA, 0xBCFA94B7, 0xAB4A4745, 0xC0E3CB28, 0x7C195F9F, 0x17B0D3F2, 0x5378F87B, 0x38D17416, 0x842BE0A1, 0xEF826CCC, 0xF832BF3E, 0x939B3353, 0x2F61A7E4, 0x44C82B89}, + {0x00000000, 0xA6F1F0F6, 0x480F971D, 0xEEFE67EB, 0x901F2E3A, 0x36EEDECC, 0xD810B927, 0x7EE149D1, 0x25D22A85, 0x8323DA73, 0x6DDDBD98, 0xCB2C4D6E, 0xB5CD04BF, 0x133CF449, 0xFDC293A2, 0x5B336354}, + {0x00000000, 0x4BA4550A, 0x9748AA14, 0xDCECFF1E, 0x2B7D22D9, 0x60D977D3, 0xBC3588CD, 0xF791DDC7, 0x56FA45B2, 0x1D5E10B8, 0xC1B2EFA6, 0x8A16BAAC, 0x7D87676B, 0x36233261, 0xEACFCD7F, 0xA16B9875}, + {0x00000000, 0xADF48B64, 0x5E056039, 0xF3F1EB5D, 0xBC0AC072, 0x11FE4B16, 0xE20FA04B, 0x4FFB2B2F, 0x7DF9F615, 0xD00D7D71, 0x23FC962C, 0x8E081D48, 0xC1F33667, 0x6C07BD03, 0x9FF6565E, 0x3202DD3A} + }; + + static constexpr uint32_t SKIP2_2X[8][16] = { + {0x00000000, 0xE417F38A, 0xCDC391E5, 0x29D4626F, 0x9E6B553B, 0x7A7CA6B1, 0x53A8C4DE, 0xB7BF3754, 0x393ADC87, 0xDD2D2F0D, 0xF4F94D62, 0x10EEBEE8, 0xA75189BC, 0x43467A36, 0x6A921859, 0x8E85EBD3}, + {0x00000000, 0x7275B90E, 0xE4EB721C, 0x969ECB12, 0xCC3A92C9, 0xBE4F2BC7, 0x28D1E0D5, 0x5AA459DB, 0x9D995363, 0xEFECEA6D, 0x7972217F, 0x0B079871, 0x51A3C1AA, 0x23D678A4, 0xB548B3B6, 0xC73D0AB8}, + {0x00000000, 0x3EDED037, 0x7DBDA06E, 0x43637059, 0xFB7B40DC, 0xC5A590EB, 0x86C6E0B2, 0xB8183085, 0xF31AF749, 0xCDC4277E, 0x8EA75727, 0xB0798710, 0x0861B795, 0x36BF67A2, 0x75DC17FB, 0x4B02C7CC}, + {0x00000000, 0xE3D99863, 0xC25F4637, 0x2186DE54, 0x8152FA9F, 0x628B62FC, 0x430DBCA8, 0xA0D424CB, 0x074983CF, 0xE4901BAC, 0xC516C5F8, 0x26CF5D9B, 0x861B7950, 0x65C2E133, 0x44443F67, 0xA79DA704}, + {0x00000000, 0x0E93079E, 0x1D260F3C, 0x13B508A2, 0x3A4C1E78, 0x34DF19E6, 0x276A1144, 0x29F916DA, 0x74983CF0, 0x7A0B3B6E, 0x69BE33CC, 0x672D3452, 0x4ED42288, 0x40472516, 0x53F22DB4, 0x5D612A2A}, + {0x00000000, 0xE93079E0, 0xD78C8531, 0x3EBCFCD1, 0xAAF57C93, 0x43C50573, 0x7D79F9A2, 0x94498042, 0x50068FD7, 0xB936F637, 0x878A0AE6, 0x6EBA7306, 0xFAF3F344, 0x13C38AA4, 0x2D7F7675, 0xC44F0F95}, + {0x00000000, 0xA00D1FAE, 0x45F649AD, 0xE5FB5603, 0x8BEC935A, 0x2BE18CF4, 0xCE1ADAF7, 0x6E17C559, 0x12355045, 0xB2384FEB, 0x57C319E8, 0xF7CE0646, 0x99D9C31F, 0x39D4DCB1, 0xDC2F8AB2, 0x7C22951C}, + {0x00000000, 0x246AA08A, 0x48D54114, 0x6CBFE19E, 0x91AA8228, 0xB5C022A2, 0xD97FC33C, 0xFD1563B6, 0x26B972A1, 0x02D3D22B, 0x6E6C33B5, 0x4A06933F, 0xB713F089, 0x93795003, 0xFFC6B19D, 0xDBAC1117} + }; + + // O(1) combine: shift a CRC by N zero bytes via skip table + inline uint32_t ApplySkipTable(const uint32_t table[8][16], uint32_t crc) + { + return table[0][ crc & 0xF] ^ table[1][(crc >> 4) & 0xF] ^ + table[2][(crc >> 8) & 0xF] ^ table[3][(crc >> 12) & 0xF] ^ + table[4][(crc >> 16) & 0xF] ^ table[5][(crc >> 20) & 0xF] ^ + table[6][(crc >> 24) & 0xF] ^ table[7][(crc >> 28) & 0xF]; + } + + // Prefetch helper + inline void CrcPrefetch(const uint8_t *p) + { +#if defined(SKY_CRC32C_X86) + #if defined(_MSC_VER) + _mm_prefetch(reinterpret_cast(p), _MM_HINT_T0); + #else + __builtin_prefetch(p, 0, 3); + #endif +#elif defined(SKY_CRC32C_ARM) + __builtin_prefetch(p, 0, 3); +#endif + } + + // 3-way CRC step: process 8 bytes from each of 3 streams +#if defined(SKY_CRC32C_X86) + #define SKY_CRC_STEP8X3(c0, c1, c2, p0, p1, p2) \ + do { \ + uint64_t v0_, v1_, v2_; \ + memcpy(&v0_, (p0), 8); \ + memcpy(&v1_, (p1), 8); \ + memcpy(&v2_, (p2), 8); \ + (c0) = static_cast(_mm_crc32_u64((c0), v0_)); \ + (c1) = static_cast(_mm_crc32_u64((c1), v1_)); \ + (c2) = static_cast(_mm_crc32_u64((c2), v2_)); \ + (p0) += 8; (p1) += 8; (p2) += 8; \ + } while (0) +#else + #define SKY_CRC_STEP8X3(c0, c1, c2, p0, p1, p2) \ + do { \ + uint64_t v0_, v1_, v2_; \ + memcpy(&v0_, (p0), 8); \ + memcpy(&v1_, (p1), 8); \ + memcpy(&v2_, (p2), 8); \ + (c0) = __crc32cd((c0), v0_); \ + (c1) = __crc32cd((c1), v1_); \ + (c2) = __crc32cd((c2), v2_); \ + (p0) += 8; (p1) += 8; (p2) += 8; \ + } while (0) +#endif + + // Single-stream hardware CRC32C (for tail / small data) + inline uint32_t HardwareCrc32CTail(const uint8_t *data, size_t size, uint32_t crc) + { + while (size >= 8) { + uint64_t v; + memcpy(&v, data, sizeof(v)); +#if defined(SKY_CRC32C_X86) + crc = static_cast(_mm_crc32_u64(crc, v)); +#else + crc = __crc32cd(crc, v); +#endif + data += 8; + size -= 8; + } + + if (size >= 4) { + uint32_t v; + memcpy(&v, data, sizeof(v)); +#if defined(SKY_CRC32C_X86) + crc = _mm_crc32_u32(crc, v); +#else + crc = __crc32cw(crc, v); +#endif + data += 4; + size -= 4; + } + + while (size > 0) { +#if defined(SKY_CRC32C_X86) + crc = _mm_crc32_u8(crc, *data); +#else + crc = __crc32cb(crc, *data); +#endif + ++data; + --size; + } + return crc; + } + + inline uint32_t HardwareCrc32C(const uint8_t *data, size_t size, uint32_t crc) + { + crc = ~crc; + + // 3-way parallel: process 3 BLOCK bytes per iteration + while (size >= 3 * BLOCK) { + uint32_t crc0 = crc; + uint32_t crc1 = 0; + uint32_t crc2 = 0; + + const uint8_t *p0 = data; + const uint8_t *p1 = data + BLOCK; + const uint8_t *p2 = data + BLOCK * 2; + const uint8_t *end = p0 + BLOCK; + + // Inner loop: 8 unrolled (64 bytes per stream per iteration) + while (p0 < end) { + CrcPrefetch(p0 + PREFETCH_HORIZON); + CrcPrefetch(p1 + PREFETCH_HORIZON); + CrcPrefetch(p2 + PREFETCH_HORIZON); + SKY_CRC_STEP8X3(crc0, crc1, crc2, p0, p1, p2); + SKY_CRC_STEP8X3(crc0, crc1, crc2, p0, p1, p2); + SKY_CRC_STEP8X3(crc0, crc1, crc2, p0, p1, p2); + SKY_CRC_STEP8X3(crc0, crc1, crc2, p0, p1, p2); + SKY_CRC_STEP8X3(crc0, crc1, crc2, p0, p1, p2); + SKY_CRC_STEP8X3(crc0, crc1, crc2, p0, p1, p2); + SKY_CRC_STEP8X3(crc0, crc1, crc2, p0, p1, p2); + SKY_CRC_STEP8X3(crc0, crc1, crc2, p0, p1, p2); + } + + crc = ApplySkipTable(SKIP_2X, crc0) ^ ApplySkipTable(SKIP_1X, crc1) ^ crc2; + + data += 3 * BLOCK; + size -= 3 * BLOCK; + } + + // Tier 1: 3-way parallel, BLOCK1=1360, no unroll + while (size >= 3 * BLOCK1) { + uint32_t crc0 = crc; + uint32_t crc1 = 0; + uint32_t crc2 = 0; + + const uint8_t *p0 = data; + const uint8_t *p1 = data + BLOCK1; + const uint8_t *p2 = data + BLOCK1 * 2; + const uint8_t *end = p0 + BLOCK1; + + while (p0 < end) { + SKY_CRC_STEP8X3(crc0, crc1, crc2, p0, p1, p2); + } + + crc = ApplySkipTable(SKIP1_2X, crc0) ^ ApplySkipTable(SKIP1_1X, crc1) ^ crc2; + + data += 3 * BLOCK1; + size -= 3 * BLOCK1; + } + + // Tier 2: 3-way parallel, BLOCK2=336, no unroll + while (size >= 3 * BLOCK2) { + uint32_t crc0 = crc; + uint32_t crc1 = 0; + uint32_t crc2 = 0; + + const uint8_t *p0 = data; + const uint8_t *p1 = data + BLOCK2; + const uint8_t *p2 = data + BLOCK2 * 2; + const uint8_t *end = p0 + BLOCK2; + + while (p0 < end) { + SKY_CRC_STEP8X3(crc0, crc1, crc2, p0, p1, p2); + } + + crc = ApplySkipTable(SKIP2_2X, crc0) ^ ApplySkipTable(SKIP2_1X, crc1) ^ crc2; + + data += 3 * BLOCK2; + size -= 3 * BLOCK2; + } + + // Handle remaining bytes (<1008) with single-stream + crc = HardwareCrc32CTail(data, size, crc); + + return ~crc; + } + +#undef SKY_CRC_STEP8X3 + +#endif + + } // anonymous namespace + + // ----------------------------------------------------------------- + // Public API CRC32C + // ----------------------------------------------------------------- + + uint32_t Crc32C(const uint8_t *data, size_t size) + { +#if defined(SKY_CRC32C_X86) || defined(SKY_CRC32C_ARM) + return HardwareCrc32C(data, size, 0); +#else + return SoftwareCrc(CRC32C_TABLE, data, size, 0); +#endif + } + + uint32_t Crc32C(const uint8_t *data, size_t size, uint32_t crc) + { +#if defined(SKY_CRC32C_X86) || defined(SKY_CRC32C_ARM) + return HardwareCrc32C(data, size, crc); +#else + return SoftwareCrc(CRC32C_TABLE, data, size, crc); +#endif + } + + // ----------------------------------------------------------------- + // Public API CRC32 IEEE (software only, no HW instruction) + // ----------------------------------------------------------------- + + uint32_t CalcCrc32(const uint8_t *data, size_t size) + { + return SoftwareCrc(CRC32_TABLE, data, size, 0); + } + + uint32_t CalcCrc32(const uint8_t *data, size_t size, uint32_t crc) + { + return SoftwareCrc(CRC32_TABLE, data, size, crc); + } + +} // namespace sky diff --git a/engine/core/src/hash/Crc32.cpp b/engine/core/src/hash/Crc32.cpp index e551d516..f20c4346 100644 --- a/engine/core/src/hash/Crc32.cpp +++ b/engine/core/src/hash/Crc32.cpp @@ -3,23 +3,23 @@ // #include -#include +#include namespace sky { uint32_t Crc32::Cal(const uint8_t *buffer, uint32_t size) { - return crc32c::Crc32c(buffer, size); + return Crc32C(buffer, size); } uint32_t Crc32::Cal(const std::string &str) { - return crc32c::Crc32c(str); + return Crc32C(reinterpret_cast(str.data()), str.size()); } uint32_t Crc32::Cal(const std::string_view &str) { - return crc32c::Crc32c(str.data(), str.length()); + return Crc32C(reinterpret_cast(str.data()), str.size()); } } // namespace sky \ No newline at end of file diff --git a/engine/test/core/CryptoTest.cpp b/engine/test/core/CryptoTest.cpp index d3d2a7af..ae01c560 100644 --- a/engine/test/core/CryptoTest.cpp +++ b/engine/test/core/CryptoTest.cpp @@ -3,11 +3,29 @@ // #include +#include +#include #include -#include +#include +#include +#include +#include using namespace sky; +// Helper: build deterministic pattern data (byte = i % 251, prime avoids alignment artifacts) +static std::vector MakePattern(size_t size) +{ + std::vector v(size); + for (size_t i = 0; i < size; ++i) v[i] = static_cast(i % 251); + return v; +} + +// =========================================================================== +// MD5 Tests +// =========================================================================== + +// RFC 1321 - Appendix A.5 test suite TEST(CryptoTest, MD5Test) { ASSERT_EQ(MD5::CalculateMD5("").ToString(), "d41d8cd98f00b204e9800998ecf8427e"); @@ -17,4 +35,241 @@ TEST(CryptoTest, MD5Test) ASSERT_EQ(MD5::CalculateMD5("abcdefghijklmnopqrstuvwxyz").ToString(), "c3fcd3d76192e4007dfb496cca67e13b"); ASSERT_EQ(MD5::CalculateMD5("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789").ToString(), "d174ab98d277d9f5a5611c2c9f419d9f"); ASSERT_EQ(MD5::CalculateMD5("12345678901234567890123456789012345678901234567890123456789012345678901234567890").ToString(), "57edf4a22be3c955ac49da2e2107b67a"); +} + +// =========================================================================== +// CRC32C Tests basic +// =========================================================================== + +TEST(CryptoTest, CRC32CEmpty) +{ + ASSERT_EQ(Crc32C(nullptr, 0), 0x00000000u); +} + +TEST(CryptoTest, CRC32CSingleByte) +{ + uint8_t z = 0x00; + ASSERT_EQ(Crc32C(&z, 1), 0x527D5351u); + + uint8_t f = 0xFF; + ASSERT_EQ(Crc32C(&f, 1), 0xFF000000u); + + uint8_t a = 'A'; + ASSERT_EQ(Crc32C(&a, 1), 0xE16DCDEEu); +} + +TEST(CryptoTest, CRC32CKnownString) +{ + // CRC32C of "123456789" = 0xE3069283 (standard check value) + const std::string digits = "123456789"; + ASSERT_EQ(Crc32C(reinterpret_cast(digits.data()), digits.size()), 0xE3069283u); +} + +TEST(CryptoTest, CRC32CHashWrapperConsistency) +{ + const std::string digits = "123456789"; + ASSERT_EQ(Crc32::Cal(digits), Crc32C(reinterpret_cast(digits.data()), digits.size())); +} + +TEST(CryptoTest, CRC32CAllZeros) +{ + // Verify CRC32C of all-zero buffers at various sizes covering tail paths + struct { size_t size; uint32_t expected; } cases[] = { + { 1, 0x527D5351u}, { 7, 0xBB3E6A6Du}, { 8, 0x8C28B28Au}, + { 15, 0x530ED410u}, { 16, 0x42709AEAu}, { 63, 0x9062E550u}, + { 64, 0x03C8EB67u}, {100, 0x07CB9FF6u}, {256, 0xB872B190u}, + }; + for (const auto &c : cases) { + std::vector buf(c.size, 0); + ASSERT_EQ(Crc32C(buf.data(), buf.size()), c.expected) + << "Failed for zeros(" << c.size << ")"; + } +} + +TEST(CryptoTest, CRC32CAllOnes) +{ + std::vector buf(32, 0xFF); + ASSERT_EQ(Crc32C(buf.data(), buf.size()), 0x62A8AB43u); +} + +// =========================================================================== +// CRC32C Tests incremental (2-arg overload) +// =========================================================================== + +TEST(CryptoTest, CRC32CIncrementalSmall) +{ + const std::string part1 = "12345"; + const std::string part2 = "6789"; + + uint32_t crc = Crc32C(reinterpret_cast(part1.data()), part1.size()); + crc = Crc32C(reinterpret_cast(part2.data()), part2.size(), crc); + + const std::string full = "123456789"; + ASSERT_EQ(crc, Crc32C(reinterpret_cast(full.data()), full.size())); +} + +TEST(CryptoTest, CRC32CIncrementalLarge) +{ + // 20000 bytes split at 12288 (Tier 0 boundary) + auto data = MakePattern(20000); + uint32_t full = Crc32C(data.data(), data.size()); + + uint32_t crc = Crc32C(data.data(), 12288); + crc = Crc32C(data.data() + 12288, data.size() - 12288, crc); + ASSERT_EQ(crc, full); +} + +TEST(CryptoTest, CRC32CIncrementalMultiPart) +{ + // 50000 bytes split into 3 unequal parts spanning all tiers + auto data = MakePattern(50000); + uint32_t full = Crc32C(data.data(), data.size()); + + uint32_t crc = Crc32C(data.data(), 15000); + crc = Crc32C(data.data() + 15000, 20000, crc); + crc = Crc32C(data.data() + 35000, 15000, crc); + ASSERT_EQ(crc, full); +} + +// =========================================================================== +// CRC32C Tests tier boundary coverage +// +// Hardware path tiers: +// Tier 0: size >= 34096 = 12288 (8 unrolled, 3-way parallel) +// Tier 1: size >= 31360 = 4080 (3-way parallel) +// Tier 2: size >= 3336 = 1008 (3-way parallel) +// Tail: size < 1008 (single-stream) +// =========================================================================== + +TEST(CryptoTest, CRC32CTierBoundaries) +{ + // Test at exact thresholds and 賊1 to cover edge cases + struct { size_t size; uint32_t expected; } cases[] = { + { 1007, 0xF71A9321u}, // just below Tier 2 + { 1008, 0x930CB7EBu}, // exact Tier 2 threshold + { 1009, 0xEFCDCBD8u}, // just above Tier 2 + { 4079, 0xF0A85A19u}, // just below Tier 1 + { 4080, 0x5491923Du}, // exact Tier 1 threshold + { 4081, 0x9C99DA1Du}, // just above Tier 1 + {12287, 0x4DC24BEDu}, // just below Tier 0 + {12288, 0xB30BE1EDu}, // exact Tier 0 threshold + {12289, 0xFDB8D30Cu}, // just above Tier 0 + }; + + for (const auto &c : cases) { + auto data = MakePattern(c.size); + ASSERT_EQ(Crc32C(data.data(), data.size()), c.expected) + << "Failed for pattern(" << c.size << ")"; + } +} + +TEST(CryptoTest, CRC32CLargeBlocks) +{ + // 16 KB exercises Tier 0 once + Tier 1 once + tail + auto d16k = MakePattern(16384); + ASSERT_EQ(Crc32C(d16k.data(), d16k.size()), 0xEAFCA51Du); + + // 64 KB exercises Tier 0 multiple times + auto d64k = MakePattern(65536); + ASSERT_EQ(Crc32C(d64k.data(), d64k.size()), 0x0DAAFCDEu); +} + +TEST(CryptoTest, CRC32C1MB) +{ + // 1 MB of 0xAB a known constant for benchmark cross-check + std::vector buf(1024 * 1024, 0xAB); + ASSERT_EQ(Crc32C(buf.data(), buf.size()), 0xF8F79F82u); +} + +// =========================================================================== +// CRC32 IEEE Tests +// =========================================================================== + +TEST(CryptoTest, CRC32IEEEEmpty) +{ + ASSERT_EQ(CalcCrc32(nullptr, 0), 0x00000000u); +} + +TEST(CryptoTest, CRC32IEEEKnownString) +{ + const std::string digits = "123456789"; + ASSERT_EQ(CalcCrc32(reinterpret_cast(digits.data()), digits.size()), 0xCBF43926u); +} + +TEST(CryptoTest, CRC32IEEEIncremental) +{ + const std::string part1 = "12345"; + const std::string part2 = "6789"; + + uint32_t crc = CalcCrc32(reinterpret_cast(part1.data()), part1.size()); + crc = CalcCrc32(reinterpret_cast(part2.data()), part2.size(), crc); + + const std::string full = "123456789"; + ASSERT_EQ(crc, CalcCrc32(reinterpret_cast(full.data()), full.size())); +} + +TEST(CryptoTest, CRC32IEEELargeData) +{ + // Verify at tier boundaries (software-only, so tiers don't apply, but data size still matters) + struct { size_t size; uint32_t expected; } cases[] = { + { 1008, 0x8BC1C062u}, + { 4080, 0x659AC28Cu}, + {12288, 0x7553287Du}, + }; + for (const auto &c : cases) { + auto data = MakePattern(c.size); + ASSERT_EQ(CalcCrc32(data.data(), data.size()), c.expected) + << "Failed for CRC32 IEEE pattern(" << c.size << ")"; + } +} + +// =========================================================================== +// Performance benchmarks (printed, not asserted on speed) +// =========================================================================== + +TEST(CryptoTest, CRC32CPerformance) +{ + constexpr size_t SIZE = 1024 * 1024; + constexpr int ITERATIONS = 100; + std::vector buf(SIZE, 0xAB); + + volatile uint32_t sink = 0; + + auto start = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < ITERATIONS; ++i) { + sink = Crc32C(buf.data(), buf.size()); + } + auto end = std::chrono::high_resolution_clock::now(); + + double ms = std::chrono::duration(end - start).count(); + double totalMB = static_cast(SIZE) * ITERATIONS / (1024.0 * 1024.0); + double throughput = totalMB / (ms / 1000.0); + + printf(" CRC32C: %.1f MB in %.1f ms => %.0f MB/s\n", totalMB, ms, throughput); + + ASSERT_EQ(sink, 0xF8F79F82u); +} + +TEST(CryptoTest, MD5Performance) +{ + constexpr size_t SIZE = 1024 * 1024; + constexpr int ITERATIONS = 100; + std::vector buf(SIZE, 0xAB); + + volatile uint64_t sink = 0; + + auto start = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < ITERATIONS; ++i) { + auto md5 = MD5::CalculateMD5(buf.data(), buf.size()); + sink = md5.u64[0]; + } + auto end = std::chrono::high_resolution_clock::now(); + + double ms = std::chrono::duration(end - start).count(); + double totalMB = static_cast(SIZE) * ITERATIONS / (1024.0 * 1024.0); + double throughput = totalMB / (ms / 1000.0); + + printf(" MD5: %.1f MB in %.1f ms => %.0f MB/s\n", totalMB, ms, throughput); + + ASSERT_NE(sink, 0u); } \ No newline at end of file From 316e3f99b3f3f0780f5a909f3c5b71b633ed881e Mon Sep 17 00:00:00 2001 From: Zach Date: Sun, 15 Mar 2026 21:58:28 +0800 Subject: [PATCH 13/38] [feat]: UTF encode / decode. --- engine/core/include/core/util/String.h | 5 +- engine/core/src/util/String.cpp | 218 ++++++++++++++++--------- engine/test/core/StringTest.cpp | 193 ++++++++++++++++++++++ 3 files changed, 342 insertions(+), 74 deletions(-) create mode 100644 engine/test/core/StringTest.cpp diff --git a/engine/core/include/core/util/String.h b/engine/core/include/core/util/String.h index 60dd5bdf..a044f1c3 100644 --- a/engine/core/include/core/util/String.h +++ b/engine/core/include/core/util/String.h @@ -14,5 +14,8 @@ namespace sky { std::vector Split(const std::string& s, const char *separator); - std::wstring Utf8ToUtf16(const std::string &path); + std::wstring Utf8ToUtf16(const std::string &str); + std::u16string Utf8ToUtf16U(const std::string &str); + std::string Utf16ToUtf8(const std::wstring &str); + std::string Utf16ToUtf8(const std::u16string &str); } \ No newline at end of file diff --git a/engine/core/src/util/String.cpp b/engine/core/src/util/String.cpp index 05b98445..8b672de3 100644 --- a/engine/core/src/util/String.cpp +++ b/engine/core/src/util/String.cpp @@ -3,7 +3,7 @@ // #include -#include +#include namespace sky { @@ -32,80 +32,152 @@ namespace sky { } + // Decode one UTF-8 code point from [it, end). Advances it past consumed bytes. + // Returns 0xFFFD (replacement char) on invalid input. + static uint32_t DecodeUtf8(const char *&it, const char *end) + { + auto byte = static_cast(*it); + uint32_t cp; + int extra; + + if (byte < 0x80) { + cp = byte; extra = 0; + } else if ((byte & 0xE0) == 0xC0) { + cp = byte & 0x1F; extra = 1; + } else if ((byte & 0xF0) == 0xE0) { + cp = byte & 0x0F; extra = 2; + } else if ((byte & 0xF8) == 0xF0) { + cp = byte & 0x07; extra = 3; + } else { + ++it; + return 0xFFFD; + } + + ++it; + for (int i = 0; i < extra; ++i) { + if (it == end || (static_cast(*it) & 0xC0) != 0x80) { + return 0xFFFD; + } + cp = (cp << 6) | (static_cast(*it) & 0x3F); + ++it; + } + + // Overlong / surrogate / out-of-range checks + if (cp > 0x10FFFF || + (cp >= 0xD800 && cp <= 0xDFFF) || + (extra == 1 && cp < 0x80) || + (extra == 2 && cp < 0x800) || + (extra == 3 && cp < 0x10000)) { + return 0xFFFD; + } + return cp; + } + + // Encode a Unicode code point as UTF-8, appending to out. + static void EncodeUtf8(uint32_t cp, std::string &out) + { + if (cp < 0x80) { + out.push_back(static_cast(cp)); + } else if (cp < 0x800) { + out.push_back(static_cast(0xC0 | (cp >> 6))); + out.push_back(static_cast(0x80 | (cp & 0x3F))); + } else if (cp < 0x10000) { + out.push_back(static_cast(0xE0 | (cp >> 12))); + out.push_back(static_cast(0x80 | ((cp >> 6) & 0x3F))); + out.push_back(static_cast(0x80 | (cp & 0x3F))); + } else { + out.push_back(static_cast(0xF0 | (cp >> 18))); + out.push_back(static_cast(0x80 | ((cp >> 12) & 0x3F))); + out.push_back(static_cast(0x80 | ((cp >> 6) & 0x3F))); + out.push_back(static_cast(0x80 | (cp & 0x3F))); + } + } + + // Encode a code point into UTF-16 code units, appending to a container via push_back. + template + static void EncodeUtf16(uint32_t cp, Container &out) + { + using ValueType = typename Container::value_type; + if (cp <= 0xFFFF) { + out.push_back(static_cast(cp)); + } else { + cp -= 0x10000; + out.push_back(static_cast((cp >> 10) + 0xD800)); + out.push_back(static_cast((cp & 0x3FF) + 0xDC00)); + } + } + + // Decode one UTF-16 code point from [it, end). Advances it past consumed units. + template + static uint32_t DecodeUtf16(It &it, It end) + { + auto unit = static_cast(*it); + ++it; + if (unit >= 0xD800 && unit <= 0xDBFF) { // high surrogate + if (it != end) { + auto low = static_cast(*it); + if (low >= 0xDC00 && low <= 0xDFFF) { + ++it; + return ((unit - 0xD800) << 10) + (low - 0xDC00) + 0x10000; + } + } + return 0xFFFD; // unpaired high surrogate + } + if (unit >= 0xDC00 && unit <= 0xDFFF) { + return 0xFFFD; // unpaired low surrogate + } + return unit; + } + std::wstring Utf8ToUtf16(const std::string &str) { -// std::wstring_convert> converter; -// return converter.from_bytes(str); - // TODO - return {}; + std::wstring result; + result.reserve(str.size()); + const char *it = str.data(); + const char *end = it + str.size(); + while (it < end) { + uint32_t cp = DecodeUtf8(it, end); + EncodeUtf16(cp, result); + } + return result; } -// bool ConvertUTF8toUTF16(const UTF8** sourceStart, const UTF8* sourceEnd, UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) -// { -// const UTF8* source = *sourceStart; -// UTF16* target = *targetStart; -// while (source < sourceEnd) { -// UTF32 ch = 0; -// unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; -// if (extraBytesToRead >= sourceEnd - source) { -// result = sourceExhausted; break; -// } -// /* Do this check whether lenient or strict */ -// if (!isLegalUTF8(source, extraBytesToRead+1)) { -// result = sourceIllegal; -// break; -// } -// /* -// * The cases all fall through. See "Note A" below. -// */ -// switch (extraBytesToRead) { -// case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ -// case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ -// case 3: ch += *source++; ch <<= 6; -// case 2: ch += *source++; ch <<= 6; -// case 1: ch += *source++; ch <<= 6; -// case 0: ch += *source++; -// } -// ch -= offsetsFromUTF8[extraBytesToRead]; -// -// if (target >= targetEnd) { -// source -= (extraBytesToRead+1); /* Back up source pointer! */ -// result = targetExhausted; break; -// } -// if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ -// /* UTF-16 surrogate values are illegal in UTF-32 */ -// if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { -// if (flags == strictConversion) { -// source -= (extraBytesToRead+1); /* return to the illegal value itself */ -// result = sourceIllegal; -// break; -// } else { -// *target++ = UNI_REPLACEMENT_CHAR; -// } -// } else { -// *target++ = (UTF16)ch; /* normal case */ -// } -// } else if (ch > UNI_MAX_UTF16) { -// if (flags == strictConversion) { -// result = sourceIllegal; -// source -= (extraBytesToRead+1); /* return to the start */ -// break; /* Bail out; shouldn't continue */ -// } else { -// *target++ = UNI_REPLACEMENT_CHAR; -// } -// } else { -// /* target is a character in range 0xFFFF - 0x10FFFF. */ -// if (target + 1 >= targetEnd) { -// source -= (extraBytesToRead+1); /* Back up source pointer! */ -// result = targetExhausted; break; -// } -// ch -= halfBase; -// *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); -// *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); -// } -// } -// *sourceStart = source; -// *targetStart = target; -// return result; -// } + std::u16string Utf8ToUtf16U(const std::string &str) + { + std::u16string result; + result.reserve(str.size()); + const char *it = str.data(); + const char *end = it + str.size(); + while (it < end) { + uint32_t cp = DecodeUtf8(it, end); + EncodeUtf16(cp, result); + } + return result; + } + + std::string Utf16ToUtf8(const std::wstring &str) + { + std::string result; + result.reserve(str.size() * 2); + auto it = str.begin(); + auto end = str.end(); + while (it != end) { + uint32_t cp = DecodeUtf16(it, end); + EncodeUtf8(cp, result); + } + return result; + } + + std::string Utf16ToUtf8(const std::u16string &str) + { + std::string result; + result.reserve(str.size() * 2); + auto it = str.begin(); + auto end = str.end(); + while (it != end) { + uint32_t cp = DecodeUtf16(it, end); + EncodeUtf8(cp, result); + } + return result; + } } diff --git a/engine/test/core/StringTest.cpp b/engine/test/core/StringTest.cpp new file mode 100644 index 00000000..71891b23 --- /dev/null +++ b/engine/test/core/StringTest.cpp @@ -0,0 +1,193 @@ +// +// Created by Copilot on 2026/3/15. +// + +#include +#include + +using namespace sky; + +// --- Utf8ToUtf16 / Utf16ToUtf8 round-trip --- + +TEST(StringTest, Utf8ToUtf16_Ascii) +{ + std::string src = "Hello, World!"; + std::wstring wide = Utf8ToUtf16(src); + ASSERT_EQ(wide, L"Hello, World!"); +} + +TEST(StringTest, Utf16ToUtf8_Ascii) +{ + std::wstring src = L"Hello, World!"; + std::string utf8 = Utf16ToUtf8(src); + ASSERT_EQ(utf8, "Hello, World!"); +} + +TEST(StringTest, Utf8ToUtf16_Empty) +{ + ASSERT_TRUE(Utf8ToUtf16("").empty()); + ASSERT_TRUE(Utf16ToUtf8(std::wstring{}).empty()); +} + +// --- CJK (3-byte UTF-8) --- + +TEST(StringTest, Utf8ToUtf16_CJK) +{ + // "篏絅" in UTF-8 is 6 bytes (0xE4BDA0 0xE5A5BD) + std::string src = "\xE4\xBD\xA0\xE5\xA5\xBD"; + std::wstring wide = Utf8ToUtf16(src); + ASSERT_EQ(wide.size(), 2u); + ASSERT_EQ(wide[0], L'\u4F60'); // 篏 + ASSERT_EQ(wide[1], L'\u597D'); // 絅 +} + +TEST(StringTest, Utf8Utf16_CJK_RoundTrip) +{ + std::string src = "\xE4\xBD\xA0\xE5\xA5\xBD"; + ASSERT_EQ(Utf16ToUtf8(Utf8ToUtf16(src)), src); +} + +// --- 2-byte UTF-8 (Latin extended, Cyrillic, etc.) --- + +TEST(StringTest, Utf8ToUtf16_TwoByte) +{ + // "caf辿" '辿' is U+00E9, encoded as 0xC3 0xA9 + std::string src = "caf\xC3\xA9"; + std::wstring wide = Utf8ToUtf16(src); + ASSERT_EQ(wide.size(), 4u); + ASSERT_EQ(wide[3], L'\u00E9'); + + ASSERT_EQ(Utf16ToUtf8(wide), src); +} + +// --- 4-byte UTF-8 / surrogate pairs (Supplementary Plane) --- + +TEST(StringTest, Utf8ToUtf16_Surrogate) +{ + // U+1F600 () in UTF-8: F0 9F 98 80 + std::string src = "\xF0\x9F\x98\x80"; + std::wstring wide = Utf8ToUtf16(src); + + // On Windows wchar_t is 16-bit, so supplementary chars become a surrogate pair + ASSERT_EQ(wide.size(), 2u); + ASSERT_EQ(static_cast(wide[0]), 0xD83D); // high surrogate + ASSERT_EQ(static_cast(wide[1]), 0xDE00); // low surrogate + + ASSERT_EQ(Utf16ToUtf8(wide), src); +} + +// --- u16string variants --- + +TEST(StringTest, Utf8ToUtf16U_Ascii) +{ + std::string src = "test"; + std::u16string u16 = Utf8ToUtf16U(src); + ASSERT_EQ(u16.size(), 4u); + ASSERT_EQ(u16[0], u't'); + ASSERT_EQ(u16[3], u't'); +} + +TEST(StringTest, Utf8ToUtf16U_CJK) +{ + std::string src = "\xE4\xBD\xA0\xE5\xA5\xBD"; + std::u16string u16 = Utf8ToUtf16U(src); + ASSERT_EQ(u16.size(), 2u); + ASSERT_EQ(u16[0], u'\u4F60'); + ASSERT_EQ(u16[1], u'\u597D'); +} + +TEST(StringTest, Utf16ToUtf8_u16string_RoundTrip) +{ + std::string src = "\xF0\x9F\x98\x80"; // U+1F600 + std::u16string u16 = Utf8ToUtf16U(src); + ASSERT_EQ(u16.size(), 2u); // surrogate pair + ASSERT_EQ(Utf16ToUtf8(u16), src); +} + +// --- Invalid / edge cases --- + +TEST(StringTest, Utf8ToUtf16_InvalidContinuationByte) +{ + // 0x80 alone is invalid (continuation byte without lead) + std::string src = "\x80"; + std::wstring wide = Utf8ToUtf16(src); + ASSERT_EQ(wide.size(), 1u); + ASSERT_EQ(static_cast(wide[0]), 0xFFFD); +} + +TEST(StringTest, Utf8ToUtf16_TruncatedSequence) +{ + // 0xC3 expects one more continuation byte but string ends + std::string src = "\xC3"; + std::wstring wide = Utf8ToUtf16(src); + ASSERT_EQ(wide.size(), 1u); + ASSERT_EQ(static_cast(wide[0]), 0xFFFD); +} + +TEST(StringTest, Utf8ToUtf16_OverlongTwoByte) +{ + // Overlong encoding of U+0000: C0 80 (should be rejected) + std::string src = "\xC0\x80"; + std::wstring wide = Utf8ToUtf16(src); + ASSERT_EQ(wide.size(), 1u); + ASSERT_EQ(static_cast(wide[0]), 0xFFFD); +} + +TEST(StringTest, Utf16ToUtf8_UnpairedHighSurrogate) +{ + // High surrogate 0xD800 without a following low surrogate + std::u16string src = {char16_t(0xD800), u'A'}; + std::string utf8 = Utf16ToUtf8(src); + // Should produce replacement char for unpaired surrogate, then 'A' + ASSERT_FALSE(utf8.empty()); + // U+FFFD in UTF-8 is 0xEF 0xBF 0xBD + ASSERT_EQ(static_cast(utf8[0]), 0xEF); + ASSERT_EQ(static_cast(utf8[1]), 0xBF); + ASSERT_EQ(static_cast(utf8[2]), 0xBD); + ASSERT_EQ(utf8[3], 'A'); +} + +TEST(StringTest, Utf16ToUtf8_UnpairedLowSurrogate) +{ + // Low surrogate 0xDC00 alone is invalid + std::u16string src = {char16_t(0xDC00)}; + std::string utf8 = Utf16ToUtf8(src); + ASSERT_EQ(static_cast(utf8[0]), 0xEF); + ASSERT_EQ(static_cast(utf8[1]), 0xBF); + ASSERT_EQ(static_cast(utf8[2]), 0xBD); +} + +// --- Split --- + +TEST(StringTest, Split_Basic) +{ + auto result = Split("hello world foo", " "); + ASSERT_EQ(result.size(), 3u); + ASSERT_EQ(result[0], "hello"); + ASSERT_EQ(result[1], "world"); + ASSERT_EQ(result[2], "foo"); +} + +TEST(StringTest, Split_MultipleSeparators) +{ + auto result = Split("a,b;c", ",;"); + ASSERT_EQ(result.size(), 3u); + ASSERT_EQ(result[0], "a"); + ASSERT_EQ(result[1], "b"); + ASSERT_EQ(result[2], "c"); +} + +TEST(StringTest, Split_NoSeparator) +{ + auto result = Split("hello", ","); + ASSERT_EQ(result.size(), 1u); + ASSERT_EQ(result[0], "hello"); +} + +// --- GetEmpty --- + +TEST(StringTest, GetEmpty) +{ + const std::string &empty = String::GetEmpty(); + ASSERT_TRUE(empty.empty()); +} From 4e329b5854bce49151b7d0ba34e00d7e2dbd7611 Mon Sep 17 00:00:00 2001 From: Zach Date: Mon, 16 Mar 2026 04:12:33 +0800 Subject: [PATCH 14/38] [feat]: replace perlin noise. --- .gitmodules | 3 - agent/project_reference.md | 3 +- cmake/patches/PerlinNoise.patch | 18 -- cmake/thirdparty.cmake | 1 - cmake/thirdparty.json | 11 - cmake/thirdparty/FindPerlinNoise.cmake | 1 - engine/core/include/core/math/PerlinNoise.h | 144 ++++++++++++ engine/core/include/core/util/String.h | 4 - engine/test/core/BoundsTest.cpp | 2 +- engine/test/core/CryptoTest.cpp | 2 +- engine/test/core/PerlinNoiseTest.cpp | 230 ++++++++++++++++++++ engine/test/core/StringTest.cpp | 10 +- engine/test/core/TransientAllocatorTest.cpp | 2 +- engine/test/core/TreeTest.cpp | 2 +- plugins/terrain/CMakeLists.txt | 2 - tools/noise/CMakeLists.txt | 1 - tools/noise/NoiseToolModule.cpp | 18 +- 17 files changed, 389 insertions(+), 65 deletions(-) delete mode 100644 cmake/patches/PerlinNoise.patch delete mode 100644 cmake/thirdparty/FindPerlinNoise.cmake create mode 100644 engine/core/include/core/math/PerlinNoise.h create mode 100644 engine/test/core/PerlinNoiseTest.cpp diff --git a/.gitmodules b/.gitmodules index ec81732c..d73f55db 100644 --- a/.gitmodules +++ b/.gitmodules @@ -52,9 +52,6 @@ [submodule "thirdparty/volk"] path = thirdparty/volk url = git@github.com:zeux/volk.git -[submodule "thirdparty/PerlinNoise"] - path = thirdparty/PerlinNoise - url = git@github.com:Reputeless/PerlinNoise.git [submodule "thirdparty/ktx"] path = thirdparty/ktx url = git@github.com:KhronosGroup/KTX-Software.git diff --git a/agent/project_reference.md b/agent/project_reference.md index 6529326f..0f02612d 100644 --- a/agent/project_reference.md +++ b/agent/project_reference.md @@ -156,7 +156,6 @@ SkyEngine/ | metis | v5.2.1 | static | 臀弱 (meshlet) | | ImGuizmo | 1.83 | header-only | 3D Gizmo | | nodeeditor | 3.0.12 | static | 合莨 | -| PerlinNoise | v3.0.0 | header-only | 紕亥 | ### 篁銀莎 @@ -170,7 +169,7 @@ SkyEngine/ ### Patches 茵 -荀綺 patch 鐚crc32, GKlib, glslang, imgui, ImGuizmo, metis, nodeeditor, PerlinNoise, rapidjson, recast, sfmt, stb +荀綺 patch 鐚crc32, GKlib, glslang, imgui, ImGuizmo, metis, nodeeditor, rapidjson, recast, sfmt, stb --- diff --git a/cmake/patches/PerlinNoise.patch b/cmake/patches/PerlinNoise.patch deleted file mode 100644 index 97623a88..00000000 --- a/cmake/patches/PerlinNoise.patch +++ /dev/null @@ -1,18 +0,0 @@ -diff --git a/CMakeLists.txt b/CMakeLists.txt -new file mode 100644 -index 0000000..9f65710 ---- /dev/null -+++ b/CMakeLists.txt -@@ -0,0 +1,11 @@ -+# CMake minimum version -+cmake_minimum_required(VERSION 3.0) -+ -+# project name and languages -+project(PerlinNoise) -+ -+add_library(PerlinNoise INTERFACE) -+ -+install(FILES -+ PerlinNoise.hpp -+ DESTINATION include) -\ No newline at end of file diff --git a/cmake/thirdparty.cmake b/cmake/thirdparty.cmake index 097472c9..ae62ecc3 100644 --- a/cmake/thirdparty.cmake +++ b/cmake/thirdparty.cmake @@ -76,7 +76,6 @@ if(EXISTS ${3RD_PATH}) sky_find_3rd(TARGET assimp DIR assimp) sky_find_3rd(TARGET meshoptimizer DIR meshoptimizer) sky_find_3rd(TARGET stb DIR stb) - sky_find_3rd(TARGET PerlinNoise DIR PerlinNoise) sky_find_3rd(TARGET ispc_texcomp DIR ispc_texcomp) sky_find_3rd(TARGET GKlib DIR GKlib) sky_find_3rd(TARGET metis DIR metis) diff --git a/cmake/thirdparty.json b/cmake/thirdparty.json index db4a5b90..86c3eef2 100644 --- a/cmake/thirdparty.json +++ b/cmake/thirdparty.json @@ -320,17 +320,6 @@ "MacOS-arm" ] }, - { - "name": "PerlinNoise", - "url": "https://github.com/Reputeless/PerlinNoise.git", - "tag": "v3.0.0", - "header_only": true, - "platforms": [ - "Win32", - "MacOS-x86", - "MacOS-arm" - ] - }, { "name": "ImGuizmo", "url": "https://github.com/CedricGuillemet/ImGuizmo.git", diff --git a/cmake/thirdparty/FindPerlinNoise.cmake b/cmake/thirdparty/FindPerlinNoise.cmake deleted file mode 100644 index d16a6a39..00000000 --- a/cmake/thirdparty/FindPerlinNoise.cmake +++ /dev/null @@ -1 +0,0 @@ -sky_3rd_header_only(PerlinNoise) diff --git a/engine/core/include/core/math/PerlinNoise.h b/engine/core/include/core/math/PerlinNoise.h new file mode 100644 index 00000000..89122903 --- /dev/null +++ b/engine/core/include/core/math/PerlinNoise.h @@ -0,0 +1,144 @@ +// +// Created by blues on 2026/3/16. +// + +#pragma once + +#include +#include +#include +#include +#include + +namespace sky { + + class PerlinNoise { + public: + explicit PerlinNoise(uint32_t seed = 0) + { + std::iota(perm.begin(), perm.end(), static_cast(0)); + + uint32_t s = seed; + for (int i = 255; i > 0; --i) { + s = s * 1664525u + 1013904223u; + int j = static_cast((s >> 16) % (i + 1)); + std::swap(perm[i], perm[j]); + } + } + + double Octave2D_01(double x, double y, int octaves) const + { + double result = 0.0; + double amp = 1.0; + double freq = 1.0; + double maxAmp = 0.0; + + for (int i = 0; i < octaves; ++i) { + result += Noise2D(x * freq, y * freq) * amp; + maxAmp += amp; + amp *= 0.5; + freq *= 2.0; + } + return (result / maxAmp) * 0.5 + 0.5; + } + + double Octave3D_01(double x, double y, double z, int octaves) const + { + double result = 0.0; + double amp = 1.0; + double freq = 1.0; + double maxAmp = 0.0; + + for (int i = 0; i < octaves; ++i) { + result += Noise3D(x * freq, y * freq, z * freq) * amp; + maxAmp += amp; + amp *= 0.5; + freq *= 2.0; + } + return (result / maxAmp) * 0.5 + 0.5; + } + + private: + std::array perm{}; + + uint8_t Hash(int x) const { return perm[static_cast(x)]; } + + static double Fade(double t) { return t * t * t * (t * (t * 6.0 - 15.0) + 10.0); } + static double Lerp(double a, double b, double t) { return a + t * (b - a); } + + static double Grad2D(int hash, double x, double y) + { + switch (hash & 3) { + case 0: return x + y; + case 1: return -x + y; + case 2: return x - y; + case 3: return -x - y; + } + return 0.0; + } + + static double Grad3D(int hash, double x, double y, double z) + { + int h = hash & 15; + double u = h < 8 ? x : y; + double v = h < 4 ? y : (h == 12 || h == 14 ? x : z); + return ((h & 1) ? -u : u) + ((h & 2) ? -v : v); + } + + double Noise2D(double x, double y) const + { + int xi = static_cast(std::floor(x)) & 255; + int yi = static_cast(std::floor(y)) & 255; + double xf = x - std::floor(x); + double yf = y - std::floor(y); + + double u = Fade(xf); + double v = Fade(yf); + + int aa = Hash(Hash(xi ) + yi ); + int ab = Hash(Hash(xi ) + yi + 1); + int ba = Hash(Hash(xi + 1) + yi ); + int bb = Hash(Hash(xi + 1) + yi + 1); + + return Lerp( + Lerp(Grad2D(aa, xf, yf ), Grad2D(ba, xf - 1, yf ), u), + Lerp(Grad2D(ab, xf, yf - 1), Grad2D(bb, xf - 1, yf - 1), u), + v); + } + + double Noise3D(double x, double y, double z) const + { + int xi = static_cast(std::floor(x)) & 255; + int yi = static_cast(std::floor(y)) & 255; + int zi = static_cast(std::floor(z)) & 255; + double xf = x - std::floor(x); + double yf = y - std::floor(y); + double zf = z - std::floor(z); + + double u = Fade(xf); + double v = Fade(yf); + double w = Fade(zf); + + int aaa = Hash(Hash(Hash(xi ) + yi ) + zi ); + int aab = Hash(Hash(Hash(xi ) + yi ) + zi + 1); + int aba = Hash(Hash(Hash(xi ) + yi + 1) + zi ); + int abb = Hash(Hash(Hash(xi ) + yi + 1) + zi + 1); + int baa = Hash(Hash(Hash(xi + 1) + yi ) + zi ); + int bab = Hash(Hash(Hash(xi + 1) + yi ) + zi + 1); + int bba = Hash(Hash(Hash(xi + 1) + yi + 1) + zi ); + int bbb = Hash(Hash(Hash(xi + 1) + yi + 1) + zi + 1); + + return Lerp( + Lerp( + Lerp(Grad3D(aaa, xf, yf, zf ), Grad3D(baa, xf - 1, yf, zf ), u), + Lerp(Grad3D(aba, xf, yf - 1, zf ), Grad3D(bba, xf - 1, yf - 1, zf ), u), + v), + Lerp( + Lerp(Grad3D(aab, xf, yf, zf - 1), Grad3D(bab, xf - 1, yf, zf - 1), u), + Lerp(Grad3D(abb, xf, yf - 1, zf - 1), Grad3D(bbb, xf - 1, yf - 1, zf - 1), u), + v), + w); + } + }; + +} // namespace sky diff --git a/engine/core/include/core/util/String.h b/engine/core/include/core/util/String.h index a044f1c3..75a2dded 100644 --- a/engine/core/include/core/util/String.h +++ b/engine/core/include/core/util/String.h @@ -8,10 +8,6 @@ #include namespace sky { - namespace String { - const std::string &GetEmpty(); - } - std::vector Split(const std::string& s, const char *separator); std::wstring Utf8ToUtf16(const std::string &str); diff --git a/engine/test/core/BoundsTest.cpp b/engine/test/core/BoundsTest.cpp index 013103b0..9867aa6b 100644 --- a/engine/test/core/BoundsTest.cpp +++ b/engine/test/core/BoundsTest.cpp @@ -1,4 +1,4 @@ -// +鏤// // Created by blues on 2026/2/19. // diff --git a/engine/test/core/CryptoTest.cpp b/engine/test/core/CryptoTest.cpp index ae01c560..1df1d5ad 100644 --- a/engine/test/core/CryptoTest.cpp +++ b/engine/test/core/CryptoTest.cpp @@ -1,4 +1,4 @@ -// +鏤// // Created by blues on 2024/12/29. // diff --git a/engine/test/core/PerlinNoiseTest.cpp b/engine/test/core/PerlinNoiseTest.cpp new file mode 100644 index 00000000..c63f6c17 --- /dev/null +++ b/engine/test/core/PerlinNoiseTest.cpp @@ -0,0 +1,230 @@ +鏤// +// Created by blues on 2026/3/16. +// + +#include +#include +#include +#include + +using namespace sky; + +// --------------------------------------------------------------------------- +// Correctness: output range [0, 1] +// --------------------------------------------------------------------------- +TEST(PerlinNoiseTest, Octave2DOutputRange) +{ + PerlinNoise perlin{42}; + for (int i = 0; i < 1000; ++i) { + double x = (i % 100) * 0.1; + double y = (i / 100) * 0.1; + double v = perlin.Octave2D_01(x, y, 4); + ASSERT_GE(v, 0.0) << "x=" << x << " y=" << y; + ASSERT_LE(v, 1.0) << "x=" << x << " y=" << y; + } +} + +TEST(PerlinNoiseTest, Octave3DOutputRange) +{ + PerlinNoise perlin{42}; + for (int i = 0; i < 1000; ++i) { + double x = (i % 10) * 0.1; + double y = ((i / 10) % 10) * 0.1; + double z = (i / 100) * 0.1; + double v = perlin.Octave3D_01(x, y, z, 4); + ASSERT_GE(v, 0.0) << "x=" << x << " y=" << y << " z=" << z; + ASSERT_LE(v, 1.0) << "x=" << x << " y=" << y << " z=" << z; + } +} + +// --------------------------------------------------------------------------- +// Determinism: same seed same output +// --------------------------------------------------------------------------- +TEST(PerlinNoiseTest, Deterministic2D) +{ + PerlinNoise a{12345}; + PerlinNoise b{12345}; + for (int i = 0; i < 100; ++i) { + double x = i * 0.37; + double y = i * 0.53; + ASSERT_EQ(a.Octave2D_01(x, y, 3), b.Octave2D_01(x, y, 3)); + } +} + +TEST(PerlinNoiseTest, Deterministic3D) +{ + PerlinNoise a{12345}; + PerlinNoise b{12345}; + for (int i = 0; i < 100; ++i) { + double x = i * 0.37; + double y = i * 0.53; + double z = i * 0.71; + ASSERT_EQ(a.Octave3D_01(x, y, z, 3), b.Octave3D_01(x, y, z, 3)); + } +} + +// --------------------------------------------------------------------------- +// Different seeds different permutations different output +// --------------------------------------------------------------------------- +TEST(PerlinNoiseTest, DifferentSeeds) +{ + PerlinNoise a{1}; + PerlinNoise b{2}; + + int diffCount = 0; + for (int i = 0; i < 100; ++i) { + double x = i * 0.5; + double y = i * 0.7; + if (a.Octave2D_01(x, y, 3) != b.Octave2D_01(x, y, 3)) { + ++diffCount; + } + } + ASSERT_GT(diffCount, 50); +} + +// --------------------------------------------------------------------------- +// Continuity: nearby inputs small difference +// --------------------------------------------------------------------------- +TEST(PerlinNoiseTest, Continuity2D) +{ + PerlinNoise perlin{0}; + double epsilon = 1e-6; + for (int i = 0; i < 100; ++i) { + double x = i * 0.3; + double y = i * 0.7; + double v0 = perlin.Octave2D_01(x, y, 3); + double v1 = perlin.Octave2D_01(x + epsilon, y, 3); + ASSERT_NEAR(v0, v1, 1e-3) << "Continuity broken at x=" << x << " y=" << y; + } +} + +TEST(PerlinNoiseTest, Continuity3D) +{ + PerlinNoise perlin{0}; + double epsilon = 1e-6; + for (int i = 0; i < 100; ++i) { + double x = i * 0.3; + double y = i * 0.5; + double z = i * 0.7; + double v0 = perlin.Octave3D_01(x, y, z, 3); + double v1 = perlin.Octave3D_01(x + epsilon, y, z, 3); + ASSERT_NEAR(v0, v1, 1e-3) << "Continuity broken at x=" << x << " y=" << y << " z=" << z; + } +} + +// --------------------------------------------------------------------------- +// Integer lattice points: noise should be exactly 0.5 +// At integer coords, fractional part = 0, Fade(0) = 0, +// all gradients evaluate to 0 raw noise = 0 octave_01 = 0.5 +// --------------------------------------------------------------------------- +TEST(PerlinNoiseTest, IntegerPointsReturnHalf2D) +{ + PerlinNoise perlin{7}; + for (int x = 0; x < 10; ++x) { + for (int y = 0; y < 10; ++y) { + double v = perlin.Octave2D_01(x, y, 1); + ASSERT_DOUBLE_EQ(v, 0.5) << "x=" << x << " y=" << y; + } + } +} + +TEST(PerlinNoiseTest, IntegerPointsReturnHalf3D) +{ + PerlinNoise perlin{7}; + for (int x = 0; x < 5; ++x) { + for (int y = 0; y < 5; ++y) { + for (int z = 0; z < 5; ++z) { + double v = perlin.Octave3D_01(x, y, z, 1); + ASSERT_DOUBLE_EQ(v, 0.5) << "x=" << x << " y=" << y << " z=" << z; + } + } + } +} + +// --------------------------------------------------------------------------- +// Octave accumulation: more octaves finer detail, output still in [0,1] +// --------------------------------------------------------------------------- +TEST(PerlinNoiseTest, OctaveAccumulation) +{ + PerlinNoise perlin{99}; + double x = 3.7, y = 5.2; + + double v1 = perlin.Octave2D_01(x, y, 1); + double v4 = perlin.Octave2D_01(x, y, 4); + + ASSERT_GE(v1, 0.0); + ASSERT_LE(v1, 1.0); + ASSERT_GE(v4, 0.0); + ASSERT_LE(v4, 1.0); + + // With more octaves, the value should generally differ due to higher-frequency contribution + // (not necessarily larger/smaller, just different) + // We only verify range here; the statistical test below validates distribution. +} + +// --------------------------------------------------------------------------- +// Statistical distribution: mean should be approximately 0.5 +// --------------------------------------------------------------------------- +TEST(PerlinNoiseTest, StatisticalDistribution2D) +{ + PerlinNoise perlin{314}; + double sum = 0.0; + constexpr int N = 10000; + for (int i = 0; i < N; ++i) { + double x = (i % 100) * 0.073; + double y = (i / 100) * 0.073; + sum += perlin.Octave2D_01(x, y, 3); + } + double mean = sum / N; + ASSERT_NEAR(mean, 0.5, 0.1) << "Mean should be close to 0.5, got " << mean; +} + +// --------------------------------------------------------------------------- +// Performance benchmark: 256x256 2D octave noise +// --------------------------------------------------------------------------- +TEST(PerlinNoiseTest, Benchmark2D_256x256) +{ + PerlinNoise perlin{42}; + constexpr uint32_t extent = 256; + constexpr int octaves = 4; + + volatile double sink = 0; + auto start = std::chrono::high_resolution_clock::now(); + + for (uint32_t i = 0; i < extent; ++i) { + for (uint32_t j = 0; j < extent; ++j) { + sink = perlin.Octave2D_01(i * 0.01, j * 0.01, octaves); + } + } + + auto end = std::chrono::high_resolution_clock::now(); + auto ms = std::chrono::duration_cast(end - start).count(); + printf(" [PERF] 2D 256x256 oct=%d: %lld us (%.2f ns/sample)\n", + octaves, static_cast(ms), static_cast(ms) * 1000.0 / (extent * extent)); +} + +// --------------------------------------------------------------------------- +// Performance benchmark: 64x64x64 3D octave noise +// --------------------------------------------------------------------------- +TEST(PerlinNoiseTest, Benchmark3D_64x64x64) +{ + PerlinNoise perlin{42}; + constexpr uint32_t extent = 64; + constexpr int octaves = 4; + + volatile double sink = 0; + auto start = std::chrono::high_resolution_clock::now(); + + for (uint32_t i = 0; i < extent; ++i) { + for (uint32_t j = 0; j < extent; ++j) { + for (uint32_t k = 0; k < extent; ++k) { + sink = perlin.Octave3D_01(i * 0.01, j * 0.01, k * 0.01, octaves); + } + } + } + + auto end = std::chrono::high_resolution_clock::now(); + auto ms = std::chrono::duration_cast(end - start).count(); + printf(" [PERF] 3D 64^3 oct=%d: %lld us (%.2f ns/sample)\n", + octaves, static_cast(ms), static_cast(ms) * 1000.0 / (extent * extent * extent)); +} diff --git a/engine/test/core/StringTest.cpp b/engine/test/core/StringTest.cpp index 71891b23..d071b352 100644 --- a/engine/test/core/StringTest.cpp +++ b/engine/test/core/StringTest.cpp @@ -1,4 +1,4 @@ -// +鏤// // Created by Copilot on 2026/3/15. // @@ -183,11 +183,3 @@ TEST(StringTest, Split_NoSeparator) ASSERT_EQ(result.size(), 1u); ASSERT_EQ(result[0], "hello"); } - -// --- GetEmpty --- - -TEST(StringTest, GetEmpty) -{ - const std::string &empty = String::GetEmpty(); - ASSERT_TRUE(empty.empty()); -} diff --git a/engine/test/core/TransientAllocatorTest.cpp b/engine/test/core/TransientAllocatorTest.cpp index a52fe56f..97c6b0f5 100644 --- a/engine/test/core/TransientAllocatorTest.cpp +++ b/engine/test/core/TransientAllocatorTest.cpp @@ -1,4 +1,4 @@ -// +鏤// // Created by blues on 2026/2/20. // diff --git a/engine/test/core/TreeTest.cpp b/engine/test/core/TreeTest.cpp index 7b4d578e..cd8d53ef 100644 --- a/engine/test/core/TreeTest.cpp +++ b/engine/test/core/TreeTest.cpp @@ -1,4 +1,4 @@ -// +鏤// // Created by blues on 2024/10/18. // diff --git a/plugins/terrain/CMakeLists.txt b/plugins/terrain/CMakeLists.txt index 529572a1..08b62276 100644 --- a/plugins/terrain/CMakeLists.txt +++ b/plugins/terrain/CMakeLists.txt @@ -46,7 +46,6 @@ if (SKY_BUILD_EDITOR) LINK_LIBS Terrain.Static EditorFramework - 3rdParty::PerlinNoise ) endif () @@ -62,7 +61,6 @@ sky_add_library(TARGET TerrainTools STATIC tools/include LINK_LIBS Terrain.Static - 3rdParty::PerlinNoise ) sky_add_test(TARGET TerrainTest diff --git a/tools/noise/CMakeLists.txt b/tools/noise/CMakeLists.txt index 3f8f9cef..e3aa041d 100644 --- a/tools/noise/CMakeLists.txt +++ b/tools/noise/CMakeLists.txt @@ -17,7 +17,6 @@ sky_add_library(TARGET ${TARGET_NAME} SHARED Framework ImGuiRender RenderAdaptor - 3rdParty::PerlinNoise ) add_custom_target(${TARGET_NAME}_RES diff --git a/tools/noise/NoiseToolModule.cpp b/tools/noise/NoiseToolModule.cpp index b621ef7f..c5bf5523 100644 --- a/tools/noise/NoiseToolModule.cpp +++ b/tools/noise/NoiseToolModule.cpp @@ -33,7 +33,7 @@ #include -#include +#include #include @@ -145,11 +145,11 @@ namespace sky { texture->Init(rhi::PixelFormat::R16_UNORM, extent, extent, 1); } - const siv::PerlinNoise perlin{ siv::PerlinNoise::seed_type(seed) }; + const PerlinNoise perlin{seed}; std::vector data(extent * extent, 0); for (uint32_t i = 0; i < extent; i ++) { for (uint32_t j = 0; j < extent; j++) { - const double noise = perlin.octave2D_01(static_cast(i) * factor, static_cast(j) * factor, octave); + const double noise = perlin.Octave2D_01(static_cast(i) * factor, static_cast(j) * factor, octave); data[i * extent + j] = static_cast(noise * 65536); } } @@ -170,12 +170,12 @@ namespace sky { texture3D->Init(rhi::PixelFormat::R32_SFLOAT, extent, extent, extent); } - const siv::PerlinNoise perlin{ siv::PerlinNoise::seed_type(seed) }; + const PerlinNoise perlin{seed}; std::vector data(extent * extent * extent, 0); for (uint32_t i = 0; i < extent; i ++) { for (uint32_t j = 0; j < extent; j++) { for (uint32_t k = 0; k < extent; k++) { - const auto noise = perlin.octave3D_01(static_cast(i) * factor, + const auto noise = perlin.Octave3D_01(static_cast(i) * factor, static_cast(j) * factor, static_cast(k) * factor, octave); @@ -314,14 +314,14 @@ namespace sky { texture->Init(rhi::PixelFormat::R32_SFLOAT, extent, extent, 1); } - const siv::PerlinNoise perlin{ siv::PerlinNoise::seed_type(seed) }; + const PerlinNoise perlin{static_cast(seed)}; std::vector data(extent * extent, 0); auto s = static_cast(extent) / static_cast(frequency); for (uint32_t i = 0; i < extent; i ++) { for (uint32_t j = 0; j < extent; j++) { float w = 1.f - NoiseWorley(Vector2(static_cast(j), static_cast(i)), s); - float p = perlin.octave2D_01(static_cast(j) * factor, static_cast(i) * factor, octave); + float p = perlin.Octave2D_01(static_cast(j) * factor, static_cast(i) * factor, octave); data[i * extent + j] = Remap(p, w, 1.0f, 0.f, 1.0f); } } @@ -342,7 +342,7 @@ namespace sky { texture3D->Init(rhi::PixelFormat::R32_SFLOAT, extent, extent, extent); } - const siv::PerlinNoise perlin{ siv::PerlinNoise::seed_type(seed) }; + const PerlinNoise perlin{static_cast(seed)}; std::vector data(extent * extent * extent, 0); auto s = static_cast(extent) / static_cast(frequency); @@ -352,7 +352,7 @@ namespace sky { float w = 1.f - NoiseWorley3(Vector3(static_cast(k), static_cast(j), static_cast(i)),s); - float p = perlin.octave3D_01(static_cast(k) * factor, + float p = perlin.Octave3D_01(static_cast(k) * factor, static_cast(j) * factor, static_cast(i) * factor, octave); From 8b540d61a71337e88077fa719d829e3cbe76d968 Mon Sep 17 00:00:00 2001 From: Zach Date: Tue, 17 Mar 2026 01:10:42 +0800 Subject: [PATCH 15/38] [feat]: fix perlin noise. --- plugins/terrain/editor/src/TerrainGenerator.cpp | 7 +++---- plugins/terrain/tools/src/TerrainGenerator.cpp | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/plugins/terrain/editor/src/TerrainGenerator.cpp b/plugins/terrain/editor/src/TerrainGenerator.cpp index 6decbd3a..4e59182b 100644 --- a/plugins/terrain/editor/src/TerrainGenerator.cpp +++ b/plugins/terrain/editor/src/TerrainGenerator.cpp @@ -3,7 +3,7 @@ // #include -#include +#include #include #include #include @@ -78,8 +78,7 @@ namespace sky::editor { bool TerrainTileGenerator::DoWork() { - const siv::PerlinNoise::seed_type seed = 113344u; - const siv::PerlinNoise perlin{ seed }; + const PerlinNoise perlin{113344u}; auto xOffset = static_cast(tileCfg.coord.x * tileCfg.sectionSize); auto yOffset = static_cast(tileCfg.coord.y * tileCfg.sectionSize); @@ -104,7 +103,7 @@ namespace sky::editor { float x = yOffset + static_cast(j) * scaleFactor; uint32_t index = i * tileCfg.heightMapSize + j; - auto val = perlin.noise2D_01(x * 0.05, y * 0.05); + auto val = perlin.Octave2D_01(x * 0.05, y * 0.05, 1); heightMapData[index] = static_cast(val); } } diff --git a/plugins/terrain/tools/src/TerrainGenerator.cpp b/plugins/terrain/tools/src/TerrainGenerator.cpp index d7c5c45f..56af4396 100644 --- a/plugins/terrain/tools/src/TerrainGenerator.cpp +++ b/plugins/terrain/tools/src/TerrainGenerator.cpp @@ -3,7 +3,7 @@ // #include -#include +#include namespace sky { @@ -14,8 +14,7 @@ namespace sky { bool TerrainGenerator::DoWork() { - const siv::PerlinNoise::seed_type seed = 113344u; - const siv::PerlinNoise perlin{ seed }; + const PerlinNoise perlin{113344u}; uint32_t blockWidth = 256; uint32_t blockNum = config.maxExt / config.minExt; @@ -29,7 +28,7 @@ namespace sky { for (uint64_t i = 0; i < blockWidth; ++i) { for (uint64_t j = 0; j < blockWidth; ++j) { - const double noise = perlin.noise2D_01((xOffset + i) * 0.002, (yOffset + j) * 0.002); + const double noise = perlin.Octave2D_01((xOffset + i) * 0.002, (yOffset + j) * 0.002, 1); data[j * blockWidth + i] = static_cast(noise * 65535); } } From 2f40fbc5a52f4df451a633d8526ab32b1d169c44 Mon Sep 17 00:00:00 2001 From: Zach Date: Tue, 17 Mar 2026 01:40:45 +0800 Subject: [PATCH 16/38] refactor: replace cxxopts with built-in CmdParser - Add header-only CmdParser (core/cmdline/CmdParser.h) with default value support - Remove cxxopts third-party dependency from CMake and thirdparty.json - Update all 10 source files to use sky::CmdOptions/CmdValue/CmdParseResult --- cmake/thirdparty.cmake | 3 - cmake/thirdparty.json | 10 - engine/core/include/core/cmdline/CmdParser.h | 279 ++++++++++++++++++ .../src/application/EditorApplication.cpp | 10 +- engine/framework/CMakeLists.txt | 1 - .../src/application/GameApplication.cpp | 6 +- .../src/application/ToolApplicationBase.cpp | 8 +- .../src/application/XRApplication.cpp | 6 +- engine/launcher/windows/Win32Launcher.cpp | 6 +- engine/render/adaptor/src/RenderModule.cpp | 10 +- engine/render/shader/CMakeLists.txt | 1 - engine/render/shader/ShaderModule.cpp | 14 +- engine/render/shader/tool/main.cpp | 16 +- plugins/xr/XRModule.cpp | 8 +- tools/asset_builder/CMakeLists.txt | 1 - tools/asset_builder/main.cpp | 12 +- 16 files changed, 327 insertions(+), 64 deletions(-) create mode 100644 engine/core/include/core/cmdline/CmdParser.h diff --git a/cmake/thirdparty.cmake b/cmake/thirdparty.cmake index ae62ecc3..09b55577 100644 --- a/cmake/thirdparty.cmake +++ b/cmake/thirdparty.cmake @@ -32,9 +32,6 @@ if(EXISTS ${3RD_PATH}) # imgui sky_find_3rd(TARGET imgui DIR imgui) - # temp - sky_find_3rd(TARGET cxxopts DIR cxxopts) - # shader sky_find_3rd(TARGET glslang DIR glslang) sky_find_3rd(TARGET SPIRVCross DIR SPIRV-Cross) diff --git a/cmake/thirdparty.json b/cmake/thirdparty.json index 86c3eef2..56378948 100644 --- a/cmake/thirdparty.json +++ b/cmake/thirdparty.json @@ -154,16 +154,6 @@ "TF_BUILD_EXAMPLES": "OFF" } }, - { - "name": "cxxopts", - "url": "https://github.com/jarro2783/cxxopts.git", - "tag": "v3.2.1", - "header_only": true, - "options": { - "CXXOPTS_BUILD_EXAMPLES": "OFF", - "CXXOPTS_BUILD_TESTS": "OFF" - } - }, { "name": "imgui", "url": "https://github.com/ocornut/imgui.git", diff --git a/engine/core/include/core/cmdline/CmdParser.h b/engine/core/include/core/cmdline/CmdParser.h new file mode 100644 index 00000000..5592b2f5 --- /dev/null +++ b/engine/core/include/core/cmdline/CmdParser.h @@ -0,0 +1,279 @@ +// +// Copyright 2024 SkyEngine. All rights reserved. +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace sky { + + template + struct CmdValueTag { + std::string defaultValue; + bool hasDefault = false; + }; + + template + CmdValueTag CmdValue() { return {}; } + + template + CmdValueTag CmdValue(const T &def) + { + CmdValueTag tag; + tag.hasDefault = true; + std::ostringstream os; + os << def; + tag.defaultValue = os.str(); + return tag; + } + + inline CmdValueTag CmdValue(const std::string &def) + { + return CmdValueTag{def, true}; + } + + inline CmdValueTag CmdValue(const char *def) + { + return CmdValueTag{def, true}; + } + + class CmdParseResult { + public: + CmdParseResult() = default; + + size_t count(const std::string &name) const + { + auto it = values_.find(name); + return (it != values_.end() && it->second.set) ? 1 : 0; + } + + struct Proxy { + const std::vector &raw; + + template + T as() const; + }; + + Proxy operator[](const std::string &name) const + { + auto it = values_.find(name); + if (it == values_.end()) { + throw std::runtime_error("Option '" + name + "' not found"); + } + return Proxy{it->second.rawValues}; + } + + private: + friend class CmdOptions; + + struct Entry { + bool set = false; + std::vector rawValues; + }; + std::unordered_map values_; + }; + + template <> + inline std::string CmdParseResult::Proxy::as() const + { + return raw.empty() ? std::string{} : raw.back(); + } + + template <> + inline std::vector CmdParseResult::Proxy::as>() const + { + return raw; + } + + template <> + inline uint32_t CmdParseResult::Proxy::as() const + { + return raw.empty() ? 0u : static_cast(std::stoul(raw.back())); + } + + class CmdOptions { + public: + CmdOptions(const std::string &program, const std::string &description) + : program_(program), description_(description) {} + + class OptionAdder { + public: + explicit OptionAdder(CmdOptions &opts) : opts_(opts) {} + + OptionAdder &operator()(const std::string &flags, const std::string &desc) + { + opts_.AddOpt(flags, desc, false, {}, false); + return *this; + } + + template + OptionAdder &operator()(const std::string &flags, const std::string &desc, CmdValueTag tag) + { + opts_.AddOpt(flags, desc, true, tag.hasDefault ? tag.defaultValue : std::string{}, tag.hasDefault); + return *this; + } + + private: + CmdOptions &opts_; + }; + + OptionAdder add_options() { return OptionAdder{*this}; } + + void allow_unrecognised_options() { allowUnrecognised_ = true; } + + CmdParseResult parse(int argc, const char *const *argv) const + { + CmdParseResult result; + for (auto &[name, info] : optionMap_) { + result.values_[name] = {}; + } + + for (int i = 1; i < argc; ++i) { + std::string arg = argv[i]; + std::string name; + std::string val; + bool hasVal = false; + + if (arg.size() > 2 && arg[0] == '-' && arg[1] == '-') { + auto eq = arg.find('='); + if (eq != std::string::npos) { + name = arg.substr(2, eq - 2); + val = arg.substr(eq + 1); + hasVal = true; + } else { + name = arg.substr(2); + } + } else if (arg.size() >= 2 && arg[0] == '-' && arg[1] != '-') { + std::string shortKey = arg.substr(1, 1); + auto it = shortToLong_.find(shortKey); + if (it != shortToLong_.end()) { + name = it->second; + if (arg.size() > 2) { + val = arg.substr(2); + hasVal = true; + } + } else if (!allowUnrecognised_) { + throw std::runtime_error("Unknown option: " + arg); + } else { + continue; + } + } else { + continue; + } + + auto optIt = optionMap_.find(name); + if (optIt == optionMap_.end()) { + if (!allowUnrecognised_) { + throw std::runtime_error("Unknown option: --" + name); + } + continue; + } + + auto &entry = result.values_[name]; + entry.set = true; + + if (optIt->second.takesValue && !hasVal) { + if (i + 1 < argc) { + val = argv[++i]; + } + hasVal = true; + } + + if (hasVal) { + entry.rawValues.push_back(val); + } + } + + for (auto &[optName, info] : optionMap_) { + auto &entry = result.values_[optName]; + if (!entry.set && info.hasDefault) { + entry.set = true; + entry.rawValues.push_back(info.defaultValue); + } + } + + return result; + } + + CmdParseResult parse(int argc, char **argv) const + { + return parse(argc, const_cast(argv)); + } + + std::string help() const + { + std::ostringstream os; + os << program_ << " - " << description_ << "\n\nOptions:\n"; + for (auto &info : optionList_) { + os << " "; + if (!info.shortName.empty()) { + os << "-" << info.shortName << ", "; + } else { + os << " "; + } + os << "--" << info.longName; + if (info.takesValue) { + os << " "; + } + if (info.hasDefault) { + os << " [=" << info.defaultValue << "]"; + } + os << "\t" << info.description << "\n"; + } + return os.str(); + } + + private: + void AddOpt(const std::string &flags, const std::string &desc, bool takesValue, + const std::string &defaultValue = {}, bool hasDefault = false) + { + std::string shortName, longName; + auto comma = flags.find(','); + if (comma != std::string::npos) { + shortName = Trim(flags.substr(0, comma)); + longName = Trim(flags.substr(comma + 1)); + } else { + longName = Trim(flags); + } + + OptionInfo info{shortName, longName, desc, takesValue, defaultValue, hasDefault}; + optionMap_[longName] = info; + optionList_.push_back(info); + if (!shortName.empty()) { + shortToLong_[shortName] = longName; + } + } + + static std::string Trim(const std::string &s) + { + auto start = s.find_first_not_of(' '); + if (start == std::string::npos) return {}; + auto end = s.find_last_not_of(' '); + return s.substr(start, end - start + 1); + } + + struct OptionInfo { + std::string shortName; + std::string longName; + std::string description; + bool takesValue = false; + std::string defaultValue; + bool hasDefault = false; + }; + + std::string program_; + std::string description_; + std::unordered_map optionMap_; + std::vector optionList_; + std::unordered_map shortToLong_; + bool allowUnrecognised_ = false; + }; + +} // namespace sky diff --git a/engine/editor/src/application/EditorApplication.cpp b/engine/editor/src/application/EditorApplication.cpp index 3444782b..dade41bb 100644 --- a/engine/editor/src/application/EditorApplication.cpp +++ b/engine/editor/src/application/EditorApplication.cpp @@ -14,7 +14,7 @@ #include #include -#include +#include static const char* TAG = "EditorApplication"; @@ -41,12 +41,12 @@ namespace sky::editor { bool EditorApplication::Init(int argc, char **argv) { - cxxopts::Options options("GameApplication Launcher", "SkyEngine Launcher"); + CmdOptions options("GameApplication Launcher", "SkyEngine Launcher"); options.allow_unrecognised_options(); - options.add_options()("p,project", "Project Directory", cxxopts::value()); - options.add_options()("e,engine", "Engine Directory", cxxopts::value()); - options.add_options()("i,intermediate", "Project Intermediate Directory", cxxopts::value()); + options.add_options()("p,project", "Project Directory", CmdValue()); + options.add_options()("e,engine", "Engine Directory", CmdValue()); + options.add_options()("i,intermediate", "Project Intermediate Directory", CmdValue()); auto result = options.parse(argc, argv); if (result.count("project") == 0u || result.count("engine") == 0u) { return false; diff --git a/engine/framework/CMakeLists.txt b/engine/framework/CMakeLists.txt index 60db2b81..28292311 100644 --- a/engine/framework/CMakeLists.txt +++ b/engine/framework/CMakeLists.txt @@ -38,7 +38,6 @@ sky_add_library(TARGET Framework STATIC include LINK_LIBS Core - 3rdParty::cxxopts ${FM_EXT_LIB} ${PLATFORM_EXT_LIBS} INSTALL_DIR diff --git a/engine/framework/src/application/GameApplication.cpp b/engine/framework/src/application/GameApplication.cpp index 0e779733..8307609e 100644 --- a/engine/framework/src/application/GameApplication.cpp +++ b/engine/framework/src/application/GameApplication.cpp @@ -4,7 +4,7 @@ #include -#include +#include #include #include @@ -27,10 +27,10 @@ namespace sky { bool GameApplication::Init(int argc, char **argv) { #ifdef SKY_EDITOR - cxxopts::Options options("GameApplication Launcher", "SkyEngine Launcher"); + CmdOptions options("GameApplication Launcher", "SkyEngine Launcher"); options.allow_unrecognised_options(); - options.add_options()("p,project", "Project Directory", cxxopts::value()); + options.add_options()("p,project", "Project Directory", CmdValue()); auto result = options.parse(argc, argv); if (result.count("project") != 0u) { std::string projectPath = result["project"].as(); diff --git a/engine/framework/src/application/ToolApplicationBase.cpp b/engine/framework/src/application/ToolApplicationBase.cpp index 68b03d64..aa7163fc 100644 --- a/engine/framework/src/application/ToolApplicationBase.cpp +++ b/engine/framework/src/application/ToolApplicationBase.cpp @@ -4,7 +4,7 @@ #include -#include +#include #include #include @@ -17,12 +17,12 @@ namespace sky { #ifdef SKY_EDITOR void ToolApplicationBase::ParseStartArgs() { - cxxopts::Options options("Application Launcher", "SkyEngine Launcher"); + CmdOptions options("Application Launcher", "SkyEngine Launcher"); options.allow_unrecognised_options(); options.add_options() - ("p,project", "Project Directory", cxxopts::value()) - ("m,module", "Addition Module", cxxopts::value>()) + ("p,project", "Project Directory", CmdValue()) + ("m,module", "Addition Module", CmdValue>()) ("h,help", "Print usage"); auto result = options.parse(static_cast(arguments.args.size()), arguments.args.data()); diff --git a/engine/framework/src/application/XRApplication.cpp b/engine/framework/src/application/XRApplication.cpp index b92cf692..ce3dbd7e 100644 --- a/engine/framework/src/application/XRApplication.cpp +++ b/engine/framework/src/application/XRApplication.cpp @@ -3,7 +3,7 @@ // #include -#include +#include #include #include @@ -23,10 +23,10 @@ namespace sky { bool XRApplication::Init(int argc, char **argv) { #ifdef SKY_EDITOR - cxxopts::Options options("GameApplication Launcher", "SkyEngine Launcher"); + CmdOptions options("GameApplication Launcher", "SkyEngine Launcher"); options.allow_unrecognised_options(); - options.add_options()("p,project", "Project Directory", cxxopts::value()); + options.add_options()("p,project", "Project Directory", CmdValue()); auto result = options.parse(argc, argv); if (result.count("project") != 0u) { AssetManager::Get()->SetWorkFileSystem(new NativeFileSystem(result["project"].as())); diff --git a/engine/launcher/windows/Win32Launcher.cpp b/engine/launcher/windows/Win32Launcher.cpp index d67ba72f..45347b1e 100644 --- a/engine/launcher/windows/Win32Launcher.cpp +++ b/engine/launcher/windows/Win32Launcher.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include using namespace sky; @@ -19,9 +19,9 @@ int main(int argc, char **argv) return -1; } - cxxopts::Options options("SkyEngine Launcher", "SkyEngine Launcher"); + CmdOptions options("SkyEngine Launcher", "SkyEngine Launcher"); options.allow_unrecognised_options(); - options.add_options()("a, app", "app mode", cxxopts::value()); + options.add_options()("a, app", "app mode", CmdValue()); auto result = options.parse(argc, argv); bool isXRMode = (result.count("app") != 0u) && result["app"].as() == "xr"; if (isXRMode) { diff --git a/engine/render/adaptor/src/RenderModule.cpp b/engine/render/adaptor/src/RenderModule.cpp index 93c6b45e..5a8e7af6 100644 --- a/engine/render/adaptor/src/RenderModule.cpp +++ b/engine/render/adaptor/src/RenderModule.cpp @@ -37,7 +37,7 @@ #include #include -#include +#include namespace sky { @@ -75,13 +75,13 @@ namespace sky { void RenderModule::ProcessArgs(const StartArguments &args) { - cxxopts::Options options("SkyEngine Render Module", "SkyEngine Render Module"); + CmdOptions options("SkyEngine Render Module", "SkyEngine Render Module"); options.allow_unrecognised_options(); options.add_options() - ("e,engine", "Engine Directory", cxxopts::value()) - ("p,project", "Project Directory", cxxopts::value()) - ("r,rhi", "RHI Type", cxxopts::value()); + ("e,engine", "Engine Directory", CmdValue()) + ("p,project", "Project Directory", CmdValue()) + ("r,rhi", "RHI Type", CmdValue()); if (!args.args.empty()) { auto result = options.parse(static_cast(args.args.size()), args.args.data()); diff --git a/engine/render/shader/CMakeLists.txt b/engine/render/shader/CMakeLists.txt index 28754f77..d76e2550 100644 --- a/engine/render/shader/CMakeLists.txt +++ b/engine/render/shader/CMakeLists.txt @@ -50,7 +50,6 @@ sky_add_exe(TARGET ShaderTool LIBS Framework ShaderCompiler.Static - 3rdParty::cxxopts ) if (SKY_BUILD_TEST) diff --git a/engine/render/shader/ShaderModule.cpp b/engine/render/shader/ShaderModule.cpp index ec54dc3f..dcc394db 100644 --- a/engine/render/shader/ShaderModule.cpp +++ b/engine/render/shader/ShaderModule.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include namespace sky { @@ -65,18 +65,18 @@ namespace sky { bool ShaderModule::Init(const StartArguments &args) { - cxxopts::Options options("SkyEngine ShaderModule", "SkyEngine ShaderModule"); + CmdOptions options("SkyEngine ShaderModule", "SkyEngine ShaderModule"); options.allow_unrecognised_options(); options.add_options()("e,engine", "Engine Directory", - cxxopts::value())("p,project", "Project Directory", - cxxopts::value())("i,intermediate", "Project Intermediate Directory", - cxxopts::value())("r,rhi", "RHI Type", - cxxopts::value()); + CmdValue())("p,project", "Project Directory", + CmdValue())("i,intermediate", "Project Intermediate Directory", + CmdValue())("r,rhi", "RHI Type", + CmdValue()); ShaderCompileTarget target = ShaderCompileTarget::SPIRV; - cxxopts::ParseResult result; + CmdParseResult result; if (!args.args.empty()) { result = options.parse(static_cast(args.args.size()), args.args.data()); if (result.count("rhi") != 0u) { diff --git a/engine/render/shader/tool/main.cpp b/engine/render/shader/tool/main.cpp index 32742b18..85e8e2f5 100644 --- a/engine/render/shader/tool/main.cpp +++ b/engine/render/shader/tool/main.cpp @@ -2,7 +2,7 @@ // Created by blues on 2024/12/25. // #include -#include +#include #include #include #include @@ -22,15 +22,15 @@ class ShaderCompilerApp : public Application { bool Init(int argc, char** argv) override { - cxxopts::Options options("ShaderCompiler Application", "SkyEngine ShaderTool"); + CmdOptions options("ShaderCompiler Application", "SkyEngine ShaderTool"); options.allow_unrecognised_options(); - options.add_options()("s,search", "Shader Search Path", cxxopts::value>()); - options.add_options()("f,file", "Single Shader", cxxopts::value()); - options.add_options()("o,opt", "Shader Opt Level", cxxopts::value()); - options.add_options()("t,target", "Compile target(SpirV, Metal, DX, GLSL)", cxxopts::value>()); - options.add_options()("i,intermediate", "Compile Intermediate Directory", cxxopts::value()); - options.add_options()("p,prefix", "Shader Path Prefix", cxxopts::value()); + options.add_options()("s,search", "Shader Search Path", CmdValue>()); + options.add_options()("f,file", "Single Shader", CmdValue()); + options.add_options()("o,opt", "Shader Opt Level", CmdValue()); + options.add_options()("t,target", "Compile target(SpirV, Metal, DX, GLSL)", CmdValue>()); + options.add_options()("i,intermediate", "Compile Intermediate Directory", CmdValue()); + options.add_options()("p,prefix", "Shader Path Prefix", CmdValue()); options.add_options()("v,spv", "Save SpirV"); options.add_options()("h,help", "Print usage"); diff --git a/plugins/xr/XRModule.cpp b/plugins/xr/XRModule.cpp index 213e6a37..1a466930 100644 --- a/plugins/xr/XRModule.cpp +++ b/plugins/xr/XRModule.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include namespace sky { @@ -33,11 +33,11 @@ namespace sky { bool XRModule::ProcessArgs(const StartArguments &args) { - cxxopts::Options options("SkyEngine XR Plugin", "SkyEngine XR Plugin"); + CmdOptions options("SkyEngine XR Plugin", "SkyEngine XR Plugin"); options.allow_unrecognised_options(); - options.add_options()("r,rhi", "RHI Type", cxxopts::value()); - options.add_options()("a,app", "App Type", cxxopts::value()); + options.add_options()("r,rhi", "RHI Type", CmdValue()); + options.add_options()("a,app", "App Type", CmdValue()); if (args.args.empty()) { return false; diff --git a/tools/asset_builder/CMakeLists.txt b/tools/asset_builder/CMakeLists.txt index 788210ec..019f375e 100644 --- a/tools/asset_builder/CMakeLists.txt +++ b/tools/asset_builder/CMakeLists.txt @@ -4,7 +4,6 @@ sky_add_exe(TARGET AssetBuilder INCS LIBS Framework - 3rdParty::cxxopts ) sky_set_dependency(TARGET AssetBuilder) \ No newline at end of file diff --git a/tools/asset_builder/main.cpp b/tools/asset_builder/main.cpp index f609c8c3..ac7c6f13 100644 --- a/tools/asset_builder/main.cpp +++ b/tools/asset_builder/main.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include @@ -9,11 +9,11 @@ using namespace sky; int main(int argc, char *argv[]) { - cxxopts::Options options("AssetBuilder", "SkyEngine AssetBuilder"); - options.add_options()("e,engine", "Engine Directory", cxxopts::value()) - ("p,project", "Project Directory", cxxopts::value()) - ("i,import", "Import Source Asset", cxxopts::value>()) - ("f,imports", "Import Source Asset by list", cxxopts::value()) + CmdOptions options("AssetBuilder", "SkyEngine AssetBuilder"); + options.add_options()("e,engine", "Engine Directory", CmdValue()) + ("p,project", "Project Directory", CmdValue()) + ("i,import", "Import Source Asset", CmdValue>()) + ("f,imports", "Import Source Asset by list", CmdValue()) ("l,list", "Project Asset List") ("h,help", "Print usage"); options.allow_unrecognised_options(); From 52a3031fe64a6867ccbc008e3f792a553c8a9b56 Mon Sep 17 00:00:00 2001 From: Lizhen Date: Sat, 21 Mar 2026 22:34:13 +0800 Subject: [PATCH 17/38] Remove nodeeditor dependency and related editor code --- agent/project_reference.md | 5 +- cmake/patches/nodeeditor.patch | 16 -- cmake/thirdparty.cmake | 1 - cmake/thirdparty.json | 16 -- cmake/thirdparty/Findnodeeditor.cmake | 1 - engine/render/CMakeLists.txt | 3 - .../render/editor/animation/GraphEditWindow.h | 19 -- .../render/editor/src/RenderEditorModule.cpp | 3 - .../editor/src/animation/GraphEditWindow.cpp | 23 --- .../editor/src/graph/AnimationGraphWidget.cpp | 37 ---- .../editor/src/graph/AnimationGraphWidget.h | 36 ---- .../editor/src/graph/AnimationNodeModel.cpp | 194 ------------------ .../editor/src/graph/AnimationNodeModel.h | 114 ---------- 13 files changed, 2 insertions(+), 466 deletions(-) delete mode 100644 cmake/patches/nodeeditor.patch delete mode 100644 cmake/thirdparty/Findnodeeditor.cmake delete mode 100644 engine/render/editor/include/render/editor/animation/GraphEditWindow.h delete mode 100644 engine/render/editor/src/animation/GraphEditWindow.cpp delete mode 100644 engine/render/editor/src/graph/AnimationGraphWidget.cpp delete mode 100644 engine/render/editor/src/graph/AnimationGraphWidget.h delete mode 100644 engine/render/editor/src/graph/AnimationNodeModel.cpp delete mode 100644 engine/render/editor/src/graph/AnimationNodeModel.h diff --git a/agent/project_reference.md b/agent/project_reference.md index 0f02612d..ea815685 100644 --- a/agent/project_reference.md +++ b/agent/project_reference.md @@ -60,7 +60,7 @@ SkyEngine/ |--------|------| | `SkyRender` | 筝紙顕罔≦ | | `SkyRender.Builder` | 羝我莎羣綮堺─ | -| `SkyRender.Editor` | 膽莨羝我 (Qt5, nodeeditor) | +| `SkyRender.Editor` | 膽莨羝我 (Qt5) | | `ShaderCompiler` | Shader 膽莚 (綺) | | `Terrain` / `TerrainEditor` | 医就膤紫 | @@ -155,7 +155,6 @@ SkyEngine/ | GKlib | METIS-v5.1.1 | static | 上肴 | | metis | v5.2.1 | static | 臀弱 (meshlet) | | ImGuizmo | 1.83 | header-only | 3D Gizmo | -| nodeeditor | 3.0.12 | static | 合莨 | ### 篁銀莎 @@ -169,7 +168,7 @@ SkyEngine/ ### Patches 茵 -荀綺 patch 鐚crc32, GKlib, glslang, imgui, ImGuizmo, metis, nodeeditor, rapidjson, recast, sfmt, stb +荀綺 patch 鐚crc32, GKlib, glslang, imgui, ImGuizmo, metis, rapidjson, recast, sfmt, stb --- diff --git a/cmake/patches/nodeeditor.patch b/cmake/patches/nodeeditor.patch deleted file mode 100644 index 7af3c5a5..00000000 --- a/cmake/patches/nodeeditor.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 51db1f6..a743513 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -6,6 +6,11 @@ cmake_policy(SET CMP0068 NEW) # new in 3.9. The NEW behavior of this policy is t - - project(QtNodesLibrary CXX) - -+set(CMAKE_CXX_STANDARD 17) -+set(CMAKE_CXX_STANDARD_REQUIRED ON) -+set(CMAKE_CXX_EXTENSIONS OFF) -+ -+ - set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) - - set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) diff --git a/cmake/thirdparty.cmake b/cmake/thirdparty.cmake index 09b55577..a47f6dcd 100644 --- a/cmake/thirdparty.cmake +++ b/cmake/thirdparty.cmake @@ -77,7 +77,6 @@ if(EXISTS ${3RD_PATH}) sky_find_3rd(TARGET GKlib DIR GKlib) sky_find_3rd(TARGET metis DIR metis) sky_find_3rd(TARGET ImGuizmo DIR ImGuizmo) - sky_find_3rd(TARGET nodeeditor DIR nodeeditor) endif () else() message(FATAL_ERROR "3rdParty folder: ${3RD_PATH} does not exist, call cmake defining a valid 3RD_PATH") diff --git a/cmake/thirdparty.json b/cmake/thirdparty.json index 56378948..f969724e 100644 --- a/cmake/thirdparty.json +++ b/cmake/thirdparty.json @@ -10,22 +10,6 @@ "IOS" ] }, - { - "name": "nodeeditor", - "url": "https://github.com/paceholder/nodeeditor.git", - "tag": "3.0.12", - "header_only": false, - "platforms": [ - "Win32", - "MacOS-x86", - "MacOS-arm" - ], - "options": { - "BUILD_TESTING": "OFF", - "BUILD_SHARED_LIBS": "OFF", - "USE_QT6": "OFF" - } - }, { "name": "boost", "url": "https://github.com/boostorg/boost.git", diff --git a/cmake/thirdparty/Findnodeeditor.cmake b/cmake/thirdparty/Findnodeeditor.cmake deleted file mode 100644 index 5ce850aa..00000000 --- a/cmake/thirdparty/Findnodeeditor.cmake +++ /dev/null @@ -1 +0,0 @@ -sky_3rd_static(nodeeditor LIBS QtNodes) diff --git a/engine/render/CMakeLists.txt b/engine/render/CMakeLists.txt index eb42759f..46830112 100644 --- a/engine/render/CMakeLists.txt +++ b/engine/render/CMakeLists.txt @@ -45,9 +45,6 @@ if (SKY_BUILD_EDITOR) RenderAdaptor ImGuiRender EditorFramework - 3rdParty::nodeeditor - PUBLIC_DEFS - NODE_EDITOR_STATIC ) endif () diff --git a/engine/render/editor/include/render/editor/animation/GraphEditWindow.h b/engine/render/editor/include/render/editor/animation/GraphEditWindow.h deleted file mode 100644 index 4f4459aa..00000000 --- a/engine/render/editor/include/render/editor/animation/GraphEditWindow.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// Created by Zach Lee on 2026/1/11. -// - -#pragma once - -#include - -namespace sky::editor { - - class GraphEditWindow : public IAssetPreviewWndFactory { - public: - GraphEditWindow() = default; - ~GraphEditWindow() override = default; - - bool SetupWidget(AssetPreviewWidget& widget, const AssetSourcePtr& src) override; - }; - -} // namespace sky::editor \ No newline at end of file diff --git a/engine/render/editor/src/RenderEditorModule.cpp b/engine/render/editor/src/RenderEditorModule.cpp index 59aa96d2..a1c26f8b 100644 --- a/engine/render/editor/src/RenderEditorModule.cpp +++ b/engine/render/editor/src/RenderEditorModule.cpp @@ -6,13 +6,11 @@ #include #include #include -#include #include #include #include -#include namespace sky::editor { @@ -32,7 +30,6 @@ namespace sky::editor { // preview AssetPreviewManager::Get()->Register(AssetTraits::ASSET_TYPE, new SkeletonPreviewWindow()); - AssetPreviewManager::Get()->Register(AssetTraits::ASSET_TYPE, new GraphEditWindow()); return true; } diff --git a/engine/render/editor/src/animation/GraphEditWindow.cpp b/engine/render/editor/src/animation/GraphEditWindow.cpp deleted file mode 100644 index 6bfa82fa..00000000 --- a/engine/render/editor/src/animation/GraphEditWindow.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// -// Created by Zach Lee on 2026/1/11. -// - -#include -#include - -#include "graph/AnimationGraphWidget.h" - -namespace sky::editor { - - bool GraphEditWindow::SetupWidget(AssetPreviewWidget& widget, const AssetSourcePtr& src) - { - auto file = AssetDataBase::Get()->OpenFile(src); - if (file) { - widget.SetWidget(new AnimationGraphWidget(file)); - return true; - } - return false; - } - -} // namespace sky::editor - diff --git a/engine/render/editor/src/graph/AnimationGraphWidget.cpp b/engine/render/editor/src/graph/AnimationGraphWidget.cpp deleted file mode 100644 index f22e8836..00000000 --- a/engine/render/editor/src/graph/AnimationGraphWidget.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// -// Created by Zach Lee on 2026/1/11. -// - -#include "AnimationGraphWidget.h" -#include "AnimationNodeModel.h" -#include -#include - -namespace sky::editor { - - static std::shared_ptr RegisterDataModels() - { - auto ret = std::make_shared(); - ret->registerModel("StateMachine"); - ret->registerModel("StateMachine"); - return ret; - } - - AnimationGraphWidget::AnimationGraphWidget(const FilePtr& source) - : model(new QtNodes::DataFlowGraphModel(RegisterDataModels())) - , scene(new QtNodes::DataFlowGraphicsScene(*model)) - , view(new QtNodes::GraphicsView(scene)) - , asset(source) - , data{} - { - auto *mainLayout = new QVBoxLayout(this); - mainLayout->addWidget(view); - setLayout(mainLayout); - setBaseSize(800, 600); - - connect(scene, &QtNodes::DataFlowGraphicsScene::modified, this, [this]() { - setWindowModified(true); - }); - } - -} // namespace sky::editor \ No newline at end of file diff --git a/engine/render/editor/src/graph/AnimationGraphWidget.h b/engine/render/editor/src/graph/AnimationGraphWidget.h deleted file mode 100644 index 9a04243a..00000000 --- a/engine/render/editor/src/graph/AnimationGraphWidget.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// Created by Zach Lee on 2026/1/11. -// - -#pragma once - -#include -#include - -#include -#include -#include -#include - -namespace sky::editor { - - class AnimationGraphWidget : public AssetPreviewContentWidget { - Q_OBJECT - public: - explicit AnimationGraphWidget(const FilePtr& source); - - private: - void OnClose() override {} - - std::shared_ptr registry; - - QtNodes::DataFlowGraphModel* model; - QtNodes::DataFlowGraphicsScene* scene; - QtNodes::GraphicsView* view; - - FilePtr asset; - AnimationAssetData data; - }; - -} // sky::editor - diff --git a/engine/render/editor/src/graph/AnimationNodeModel.cpp b/engine/render/editor/src/graph/AnimationNodeModel.cpp deleted file mode 100644 index 7864fc67..00000000 --- a/engine/render/editor/src/graph/AnimationNodeModel.cpp +++ /dev/null @@ -1,194 +0,0 @@ -// -// Created by Zach Lee on 2026/1/11. -// - -#include "AnimationNodeModel.h" -#include -#include -#include -#include -#include -#include - -namespace sky::editor { - - AnimationClipEmbeddedWidget::AnimationClipEmbeddedWidget(QWidget* parent) : QWidget(parent) - { - QVBoxLayout *mainLayout = new QVBoxLayout(this); - mainLayout->setContentsMargins(0, 0, 0, 0); - mainLayout->setSpacing(5); - - auto* selectBtn = new QPushButton("Select"); - auto* lineEdit = new QLineEdit(""); - auto* nameEdit = new QLineEdit(""); - - connect(nameEdit, &QLineEdit::textChanged, this, [this](const QString& text) { - emit OnNameChanged(text); - }); - - connect(selectBtn, &QPushButton::clicked, this, [this, lineEdit, nameEdit]() { - AssetSelectWidget dlg("Clip"); - if (dlg.exec()) { - auto path = dlg.GetPath(); - auto asset = AssetDataBase::Get()->FindAsset(path); - if (asset) { - lineEdit->setText(path.path.GetStr().c_str()); - emit OnAnimationAssetChanged(asset->uuid); - - if (nameEdit->text().isEmpty()) { - nameEdit->setText(path.path.FileName().c_str()); - } - } - } - }); - - mainLayout->addWidget(nameEdit); - mainLayout->addWidget(selectBtn); - mainLayout->addWidget(lineEdit); - setLayout(mainLayout); - } - - AnimationCondFloatCompEmbeddedWidget::AnimationCondFloatCompEmbeddedWidget(QWidget* parent) : QWidget(parent) - { - QVBoxLayout *mainLayout = new QVBoxLayout(this); - mainLayout->setContentsMargins(0, 0, 0, 0); - mainLayout->setSpacing(5); - - auto* compFunc = new QComboBox(this); - compFunc->addItem("NEV"); - compFunc->addItem("LT"); - compFunc->addItem("EQ"); - compFunc->addItem("LE"); - compFunc->addItem("GT"); - compFunc->addItem("NE"); - compFunc->addItem("GE"); - compFunc->addItem("AWS"); - - auto* lineEdit = new QLineEdit("", this); - QDoubleValidator* validator = new QDoubleValidator(lineEdit); - lineEdit->setValidator(validator); - - auto* nameEdit = new QLineEdit("", this); - - connect(compFunc, static_cast(&QComboBox::currentIndexChanged), this, [this](int idx) { - emit OnCompFuncChanged(AnimComp(idx)); - }); - - connect(nameEdit, &QLineEdit::textChanged, this, [this](const QString& text) { - emit OnSlotNameChanged(text); - }); - - connect(lineEdit, &QLineEdit::textChanged, this, [this](const QString& text) { - emit OnParamChanged(text.toFloat()); - }); - - mainLayout->addWidget(nameEdit); - mainLayout->addWidget(compFunc); - mainLayout->addWidget(lineEdit); - setLayout(mainLayout); - } - - void StateMachineStateNodeModel::OnSelectAnimationClip(const Uuid& id) - { - data = std::make_shared(); - data->clipId = id; - } - - void StateMachineStateNodeModel::OnNameChanged(const QString& str) - { - if (data) { - data->name = str; - } - } - - - unsigned int StateMachineStateNodeModel::nPorts(QtNodes::PortType portType) const - { - return 4; - } - - QtNodes::NodeDataType StateMachineStateNodeModel::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const - { - return AnimationClipNodeData().type(); - } - - std::shared_ptr StateMachineStateNodeModel::outData(QtNodes::PortIndex port) - { - return data; - } - - void StateMachineStateNodeModel::setInData(std::shared_ptr inData, QtNodes::PortIndex portIndex) - { - auto numberData = std::dynamic_pointer_cast(inData); - - if (!inData) { - Q_EMIT dataInvalidated(0); - } - } - - QWidget *StateMachineStateNodeModel::embeddedWidget() - { - if (embedded == nullptr) { - embedded = new AnimationClipEmbeddedWidget(); - connect(embedded, &AnimationClipEmbeddedWidget::OnAnimationAssetChanged, this, &StateMachineStateNodeModel::OnSelectAnimationClip); - connect(embedded, &AnimationClipEmbeddedWidget::OnNameChanged, this, &StateMachineStateNodeModel::OnNameChanged); - } - return embedded; - } - - void StateMachineStateTransitionModel::OnSlotNameChanged(const QString &str) - { - if (data == nullptr) { - data = std::make_shared(); - } - data->paramSlotName = str; - } - - void StateMachineStateTransitionModel::OnParamChanged(const AnimationParam& val) - { - if (data == nullptr) { - data = std::make_shared(); - } - data->refVal = val; - } - - void StateMachineStateTransitionModel::OnCompFuncChanged(AnimComp comp) - { - if (data == nullptr) { - data = std::make_shared(); - } - data->compFunc = comp; - } - - unsigned int StateMachineStateTransitionModel::nPorts(QtNodes::PortType portType) const - { - return 1; - } - - QtNodes::NodeDataType StateMachineStateTransitionModel::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const - { - return AnimationClipNodeData().type(); - } - - std::shared_ptr StateMachineStateTransitionModel::outData(QtNodes::PortIndex port) - { - return {}; - } - - void StateMachineStateTransitionModel::setInData(std::shared_ptr inData, QtNodes::PortIndex portIndex) - { - } - - QWidget *StateMachineStateTransitionModel::embeddedWidget() - { - if (embedded == nullptr) { - embedded = new AnimationCondFloatCompEmbeddedWidget(); - connect(embedded, &AnimationCondFloatCompEmbeddedWidget::OnSlotNameChanged, this, &StateMachineStateTransitionModel::OnSlotNameChanged); - connect(embedded, &AnimationCondFloatCompEmbeddedWidget::OnParamChanged, this, &StateMachineStateTransitionModel::OnParamChanged); - connect(embedded, &AnimationCondFloatCompEmbeddedWidget::OnCompFuncChanged, this, &StateMachineStateTransitionModel::OnCompFuncChanged); - } - - - return embedded; - } -} // namespace sky::editor \ No newline at end of file diff --git a/engine/render/editor/src/graph/AnimationNodeModel.h b/engine/render/editor/src/graph/AnimationNodeModel.h deleted file mode 100644 index 7455149e..00000000 --- a/engine/render/editor/src/graph/AnimationNodeModel.h +++ /dev/null @@ -1,114 +0,0 @@ -// -// Created by Zach Lee on 2026/1/11. -// - -#pragma once - -#include -#include -#include -#include - -class QLineEdit; - -namespace sky::editor { - - class AnimationClipNodeData : public QtNodes::NodeData { - public: - AnimationClipNodeData() = default; - - QtNodes::NodeDataType type() const override { return QtNodes::NodeDataType{"state", "State"}; } - - QString name; - Uuid clipId; - }; - - class AnimationCompNodeData : public QtNodes::NodeData { - public: - AnimationCompNodeData() = default; - - QtNodes::NodeDataType type() const override { return QtNodes::NodeDataType{"trans", "Transition"}; } - - QString paramSlotName; - AnimationParam refVal; - AnimComp compFunc; - }; - - class AnimationClipEmbeddedWidget : public QWidget { - Q_OBJECT - public: - explicit AnimationClipEmbeddedWidget(QWidget* parent = nullptr); - ~AnimationClipEmbeddedWidget() override = default; - - Q_SIGNALS: - void OnAnimationAssetChanged(const Uuid &id); // NOLINT - void OnNameChanged(const QString& str); // NOLINT - }; - - class AnimationCondFloatCompEmbeddedWidget : public QWidget { - Q_OBJECT - public: - explicit AnimationCondFloatCompEmbeddedWidget(QWidget* parent = nullptr); - ~AnimationCondFloatCompEmbeddedWidget() override = default; - - Q_SIGNALS: - void OnSlotNameChanged(const QString &str); // NOLINT - void OnParamChanged(const AnimationParam& param); // NOLINT - void OnCompFuncChanged(AnimComp comp); // NOLINT - }; - - - class StateMachineStateNodeModel : public QtNodes::NodeDelegateModel { - Q_OBJECT - public: - StateMachineStateNodeModel() = default; - ~StateMachineStateNodeModel() override = default; - - QString caption() const override { return QStringLiteral("Animation State"); } - bool captionVisible() const override { return true; } - QString name() const override { return QStringLiteral("State"); } - - unsigned int nPorts(QtNodes::PortType portType) const override; - QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; - std::shared_ptr outData(QtNodes::PortIndex port) override; - void setInData(std::shared_ptr data, QtNodes::PortIndex portIndex) override; - - QWidget *embeddedWidget() override; - - public Q_SLOTS: - void OnSelectAnimationClip(const Uuid& id); - void OnNameChanged(const QString& str); - - private: - std::shared_ptr data; - AnimationClipEmbeddedWidget *embedded = nullptr; - }; - - class StateMachineStateTransitionModel : public QtNodes::NodeDelegateModel { - Q_OBJECT - public: - StateMachineStateTransitionModel() = default; - ~StateMachineStateTransitionModel() override = default; - - QString caption() const override { return QStringLiteral("Animation Condition"); } - bool captionVisible() const override { return false; } - QString name() const override { return QStringLiteral("FloatCondition"); } - - unsigned int nPorts(QtNodes::PortType portType) const override; - QtNodes::NodeDataType dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const override; - std::shared_ptr outData(QtNodes::PortIndex port) override; - void setInData(std::shared_ptr data, QtNodes::PortIndex portIndex) override; - - QWidget *embeddedWidget() override; - - private: - void OnSlotNameChanged(const QString &str); - void OnParamChanged(const AnimationParam& param); - void OnCompFuncChanged(AnimComp comp); - - std::shared_ptr data; - AnimationCondFloatCompEmbeddedWidget* embedded = nullptr; - }; - - -} // namespace sky::editor \ No newline at end of file From 9130ec98da18ea10020a601c940316595a654c35 Mon Sep 17 00:00:00 2001 From: Lizhen Date: Sun, 22 Mar 2026 01:21:12 +0800 Subject: [PATCH 18/38] fix: mac os build. --- .../framework/application/Application.h | 4 +- .../framework/application/GameApplication.h | 4 +- .../application/ToolApplicationBase.h | 2 +- .../framework/application/XRApplication.h | 2 +- .../framework/src/application/Application.cpp | 10 ++- .../src/application/GameApplication.cpp | 66 ++++++++++++++++--- .../src/application/ToolApplicationBase.cpp | 5 +- .../src/application/XRApplication.cpp | 3 +- engine/launcher/macos/MacosLauncher.mm | 2 - .../src/components/PrefabComponent.cpp | 4 +- .../render/backend/metal/include/mtl/Queue.h | 2 + engine/render/backend/metal/src/Queue.mm | 7 ++ .../include/render/debug/VolumeRenderer.h | 2 +- .../include/render/rdg/RenderSceneVisitor.h | 2 +- engine/render/core/src/resource/Buffer.cpp | 2 - plugins/pvs/runtime/include/pvs/PVSTypes.h | 3 +- 16 files changed, 90 insertions(+), 30 deletions(-) diff --git a/engine/framework/include/framework/application/Application.h b/engine/framework/include/framework/application/Application.h index 92376b5f..0b76199d 100644 --- a/engine/framework/include/framework/application/Application.h +++ b/engine/framework/include/framework/application/Application.h @@ -53,8 +53,8 @@ namespace sky { bool exit = false; virtual void ParseStartArgs() {} - virtual void LoadConfigs() {} - virtual void PreInit() {} + virtual bool LoadConfigs() { return true; } + virtual bool PreInit() { return true; } virtual void PostInit() {} }; diff --git a/engine/framework/include/framework/application/GameApplication.h b/engine/framework/include/framework/application/GameApplication.h index 77d63a32..e712e5d2 100644 --- a/engine/framework/include/framework/application/GameApplication.h +++ b/engine/framework/include/framework/application/GameApplication.h @@ -16,14 +16,14 @@ namespace sky { ~GameApplication() override = default; bool Init(int argc, char **argv) override; - void PreInit() override; + bool PreInit() override; void PostInit() override; void PreTick() override; NativeWindow* GetWindow() const { return nativeWindow.get(); } private: - void LoadConfigs() override; + bool LoadConfigs() override; std::unique_ptr nativeWindow; uint32_t width = 1024; diff --git a/engine/framework/include/framework/application/ToolApplicationBase.h b/engine/framework/include/framework/application/ToolApplicationBase.h index 39efc417..b414e6c2 100644 --- a/engine/framework/include/framework/application/ToolApplicationBase.h +++ b/engine/framework/include/framework/application/ToolApplicationBase.h @@ -14,7 +14,7 @@ namespace sky { ToolApplicationBase() = default; ~ToolApplicationBase() override = default; - void LoadConfigs() override; + bool LoadConfigs() override; void ParseStartArgs() override; void PostInit() override; diff --git a/engine/framework/include/framework/application/XRApplication.h b/engine/framework/include/framework/application/XRApplication.h index 03b7d66b..ef96b0a7 100644 --- a/engine/framework/include/framework/application/XRApplication.h +++ b/engine/framework/include/framework/application/XRApplication.h @@ -16,7 +16,7 @@ namespace sky { bool Init(int argc, char **argv) override; private: - void LoadConfigs() override; + bool LoadConfigs() override; std::unique_ptr nativeWindow; uint32_t width = 1024; diff --git a/engine/framework/src/application/Application.cpp b/engine/framework/src/application/Application.cpp index 0129bbac..d4cf74d0 100644 --- a/engine/framework/src/application/Application.cpp +++ b/engine/framework/src/application/Application.cpp @@ -68,14 +68,20 @@ namespace sky { ParseStartArgs(); // load configs - LoadConfigs(); + if (!LoadConfigs()) { + LOG_E(TAG, "Load Configs Failed"); + return false; + } Interface::Get()->Register(*this); auto *context = SerializationContext::Get(); World::Reflect(context); - PreInit(); + if (!PreInit()) { + LOG_E(TAG, "Application PreInit Failed"); + return false; + } // load dynamic modules if (moduleManager) { diff --git a/engine/framework/src/application/GameApplication.cpp b/engine/framework/src/application/GameApplication.cpp index 8307609e..e7ba0bff 100644 --- a/engine/framework/src/application/GameApplication.cpp +++ b/engine/framework/src/application/GameApplication.cpp @@ -5,11 +5,12 @@ #include #include +#include #include #include -#include #include +#include #include #include @@ -53,10 +54,10 @@ namespace sky { AssetManager::Get()->AddAssetProductBundle(new HashedAssetBundle(bundleFs, bundleKey)); #else - AssetManager::Get()->SetWorkPath(Platform::Get()->GetInternalPath()); - auto fs = std::make_shared(); - fs->AddPath(Platform::Get()->GetInternalPath()); - workFs = fs; + // AssetManager::Get()->SetWorkPath(Platform::Get()->GetInternalPath()); + // auto fs = std::make_shared(); + // fs->AddPath(Platform::Get()->GetInternalPath()); + // workFs = fs; #endif if (!Application::Init(argc, argv)) { return false; @@ -68,7 +69,7 @@ namespace sky { { } - void GameApplication::LoadConfigs() + bool GameApplication::LoadConfigs() { std::unordered_map modules = {}; modules.emplace("SkyRender", ModuleInfo{"SkyRender", {"ShaderCompiler"}}); @@ -79,17 +80,35 @@ namespace sky { std::string json; auto file = workFs->OpenFile(CONFIG_PATH); if (!file || !file->ReadString(json)) { - LOG_W(TAG, "Load Config Failed"); - return; + LOG_E(TAG, "Load Config Failed: %s", CONFIG_PATH); + return false; } rapidjson::Document document; document.Parse(json.c_str()); + if (document.HasParseError()) { + LOG_E(TAG, "Parse Config Failed: %s (%u)", rapidjson::GetParseError_En(document.GetParseError()), static_cast(document.GetErrorOffset())); + return false; + } + + if (!document.IsObject()) { + LOG_E(TAG, "Config Root Is Not An Object: %s", CONFIG_PATH); + return false; + } if (document.HasMember("modules")) { + if (!document["modules"].IsArray()) { + LOG_E(TAG, "Config 'modules' Is Not An Array: %s", CONFIG_PATH); + return false; + } auto array = document["modules"].GetArray(); for (auto &module : array) { - if (!module.HasMember("name")) { + if (!module.IsObject()) { + LOG_E(TAG, "Invalid Module Entry In Config: %s", CONFIG_PATH); + return false; + } + + if (!module.HasMember("name") || !module["name"].IsString()) { continue; } @@ -97,8 +116,16 @@ namespace sky { info.name = module["name"].GetString(); if (module.HasMember("dependencies")) { + if (!module["dependencies"].IsArray()) { + LOG_E(TAG, "Module Dependencies Is Not An Array: %s", info.name.c_str()); + return false; + } auto depArray = module["dependencies"].GetArray(); for (auto &dep : depArray) { + if (!dep.IsString()) { + LOG_E(TAG, "Module Dependency Is Not A String: %s", info.name.c_str()); + return false; + } info.dependencies.emplace_back(dep.GetString()); } } @@ -107,20 +134,39 @@ namespace sky { } if (document.HasMember("game")) { + if (!document["game"].IsObject()) { + LOG_E(TAG, "Config 'game' Is Not An Object: %s", CONFIG_PATH); + return false; + } auto obj = document["game"].GetObject(); if (obj.HasMember("width")) { + if (!obj["width"].IsUint() || obj["width"].GetUint() == 0) { + LOG_E(TAG, "Config 'game.width' Is Invalid: %s", CONFIG_PATH); + return false; + } width = obj["width"].GetUint(); } if (obj.HasMember("height")) { + if (!obj["height"].IsUint() || obj["height"].GetUint() == 0) { + LOG_E(TAG, "Config 'game.height' Is Invalid: %s", CONFIG_PATH); + return false; + } height = obj["height"].GetUint(); } } + + return true; } - void GameApplication::PreInit() + bool GameApplication::PreInit() { auto *handle = Platform::Get()->GetMainWinHandle(); nativeWindow.reset(NativeWindow::Create(NativeWindow::Descriptor{width, height, "SkyGame", "SkyGame", handle})); + if (nativeWindow == nullptr) { + LOG_E(TAG, "Create Native Window Failed"); + return false; + } + return true; } void GameApplication::PostInit() diff --git a/engine/framework/src/application/ToolApplicationBase.cpp b/engine/framework/src/application/ToolApplicationBase.cpp index aa7163fc..02d25fd4 100644 --- a/engine/framework/src/application/ToolApplicationBase.cpp +++ b/engine/framework/src/application/ToolApplicationBase.cpp @@ -44,12 +44,12 @@ namespace sky { AssetManager::Get()->SetWorkFileSystem(new NativeFileSystem(projectPath)); } - void ToolApplicationBase::LoadConfigs() + bool ToolApplicationBase::LoadConfigs() { std::string json; if (!ReadString(projectPath + CONFIG_PATH, json)) { LOG_W(TAG, "Load Config Failed"); - return; + return true; } rapidjson::Document document; @@ -74,6 +74,7 @@ namespace sky { moduleManager->RegisterModule(info); } } + return true; } void ToolApplicationBase::PostInit() diff --git a/engine/framework/src/application/XRApplication.cpp b/engine/framework/src/application/XRApplication.cpp index ce3dbd7e..8bf12fd1 100644 --- a/engine/framework/src/application/XRApplication.cpp +++ b/engine/framework/src/application/XRApplication.cpp @@ -44,7 +44,7 @@ namespace sky { return true; } - void XRApplication::LoadConfigs() + bool XRApplication::LoadConfigs() { //#ifdef SKY_EDITOR // auto configPath = AssetManager::Get()->GetProjectPath() + CONFIG_PATH; @@ -80,6 +80,7 @@ namespace sky { // moduleManager->RegisterModule(info); // } // } + return true; } diff --git a/engine/launcher/macos/MacosLauncher.mm b/engine/launcher/macos/MacosLauncher.mm index 856a58f3..1f0d0e30 100644 --- a/engine/launcher/macos/MacosLauncher.mm +++ b/engine/launcher/macos/MacosLauncher.mm @@ -21,7 +21,5 @@ int main(int argc, char **argv) if (app.Init(argc, argv)) { app.Mainloop(); } - - app.Shutdown(); return 0; } diff --git a/engine/render/adaptor/src/components/PrefabComponent.cpp b/engine/render/adaptor/src/components/PrefabComponent.cpp index ee2713bd..61831727 100644 --- a/engine/render/adaptor/src/components/PrefabComponent.cpp +++ b/engine/render/adaptor/src/components/PrefabComponent.cpp @@ -149,9 +149,9 @@ namespace sky { } } - // SetAsset calls are outside the lock ! safe for synchronous re-entry + // SetAsset calls are outside the lock 鐃緒申 safe for synchronous re-entry uint32_t num = 0; - LOG_I(TAG, "Load Prefab %s, nodeNum%zu", uuid.ToString(), meshesToLoad.size()); + LOG_I(TAG, "Load Prefab %s, nodeNum%zu", uuid.ToString().c_str(), meshesToLoad.size()); for (auto& id : meshesToLoad) { auto iter = lodGroupHolders.find(id); if (iter != lodGroupHolders.end()) { diff --git a/engine/render/backend/metal/include/mtl/Queue.h b/engine/render/backend/metal/include/mtl/Queue.h index 1520e6fe..b15d5009 100644 --- a/engine/render/backend/metal/include/mtl/Queue.h +++ b/engine/render/backend/metal/include/mtl/Queue.h @@ -20,6 +20,8 @@ namespace sky::mtl { rhi::TransferTaskHandle UploadImage(const rhi::ImagePtr &image, const std::vector &requests) override; rhi::TransferTaskHandle UploadBuffer(const rhi::BufferPtr &image, const std::vector &requests) override; + rhi::TransferTaskHandle ReadImage(const rhi::ImagePtr& image, rhi::ReadCallBack&& callback) override; + id GetNativeHandle() const { return queue; } private: friend class Device; diff --git a/engine/render/backend/metal/src/Queue.mm b/engine/render/backend/metal/src/Queue.mm index 96bff8fa..4aff9c02 100644 --- a/engine/render/backend/metal/src/Queue.mm +++ b/engine/render/backend/metal/src/Queue.mm @@ -28,5 +28,12 @@ }); } + rhi::TransferTaskHandle Queue::ReadImage(const rhi::ImagePtr& image, rhi::ReadCallBack&& callback) + { + return CreateTask([]() { + + }); + } + } // namespace sky::mtl diff --git a/engine/render/core/include/render/debug/VolumeRenderer.h b/engine/render/core/include/render/debug/VolumeRenderer.h index 248a912b..f6d8ca6c 100644 --- a/engine/render/core/include/render/debug/VolumeRenderer.h +++ b/engine/render/core/include/render/debug/VolumeRenderer.h @@ -4,7 +4,7 @@ #pragma once -#include +#include namespace sky { class RenderScene; diff --git a/engine/render/core/include/render/rdg/RenderSceneVisitor.h b/engine/render/core/include/render/rdg/RenderSceneVisitor.h index d1cd4590..7c75a7f3 100644 --- a/engine/render/core/include/render/rdg/RenderSceneVisitor.h +++ b/engine/render/core/include/render/rdg/RenderSceneVisitor.h @@ -38,7 +38,7 @@ namespace sky::rdg { FORCEINLINE bool IsActiveInView(uint8_t viewId) const { - const uint32_t viewBit = 1LLU << viewId; + const uint64_t viewBit = 1LLU << viewId; return (visibleMask & viewBit) == viewBit; } diff --git a/engine/render/core/src/resource/Buffer.cpp b/engine/render/core/src/resource/Buffer.cpp index da237c1a..d566a0cf 100644 --- a/engine/render/core/src/resource/Buffer.cpp +++ b/engine/render/core/src/resource/Buffer.cpp @@ -2,8 +2,6 @@ // Created by Zach Lee on 2023/9/1. // -#include "../../../../../../../sky3rd_win32_output/imgui/src/imgui.h" - #include #include #include diff --git a/plugins/pvs/runtime/include/pvs/PVSTypes.h b/plugins/pvs/runtime/include/pvs/PVSTypes.h index 60e9cbbd..c6ad022f 100644 --- a/plugins/pvs/runtime/include/pvs/PVSTypes.h +++ b/plugins/pvs/runtime/include/pvs/PVSTypes.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include namespace sky { @@ -84,4 +85,4 @@ namespace sky { } }; -} // namespace sky \ No newline at end of file +} // namespace sky From 461cd4a3a0136334c72173f57b18b1f2250df819 Mon Sep 17 00:00:00 2001 From: Lizhen Date: Sun, 22 Mar 2026 07:58:26 +0800 Subject: [PATCH 19/38] fix: Add DS_Store --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 41b801b8..e98532a2 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ /review/ /agent/backup /build_3rd/ +.DS_Store From 585a26da2115b25f6d2888258eb47a656022c147 Mon Sep 17 00:00:00 2001 From: Lizhen Date: Sun, 22 Mar 2026 09:27:26 +0800 Subject: [PATCH 20/38] [skill]: update engine skill --- .opencode/skills/README.md | 30 ++ .../skills/skyengine-build-reference/SKILL.md | 352 ++++++++++++++++++ .../skills/skyengine-commit-format/SKILL.md | 126 +++++++ .../skyengine-project-reference/SKILL.md | 56 ++- agent/plan/engine_build_plan.md | 69 ---- 5 files changed, 561 insertions(+), 72 deletions(-) create mode 100644 .opencode/skills/README.md create mode 100644 .opencode/skills/skyengine-build-reference/SKILL.md create mode 100644 .opencode/skills/skyengine-commit-format/SKILL.md rename agent/project_reference.md => .opencode/skills/skyengine-project-reference/SKILL.md (77%) delete mode 100644 agent/plan/engine_build_plan.md diff --git a/.opencode/skills/README.md b/.opencode/skills/README.md new file mode 100644 index 00000000..44aa9a66 --- /dev/null +++ b/.opencode/skills/README.md @@ -0,0 +1,30 @@ +# OpenCode Skills + +This directory contains project-local OpenCode skills for SkyEngine. + +## Layout + +- `skyengine-project-reference/` + - Repository structure, target map, dependency overview, and build flags +- `skyengine-build-reference/` + - Recommended build order and command templates for third-party setup, CMake configure, build, and optional tests +- `skyengine-commit-format/` + - Repository-aligned commit prefix guidance for `feat`, `fix`, `3rd`, `build`, and related commit styles + +Each skill lives in its own folder and must expose `SKILL.md`. + +## Loading + +OpenCode discovers project-local skills from: + +```text +.opencode/skills//SKILL.md +``` + +Agents can load them with the `skill` tool by name. + +## Maintenance + +- Keep project-local skills as the primary maintained agent-facing documentation. +- Update the skill version first when changing agent-facing instructions. +- Preserve exact paths, commands, and option names from the repository when editing skill content. diff --git a/.opencode/skills/skyengine-build-reference/SKILL.md b/.opencode/skills/skyengine-build-reference/SKILL.md new file mode 100644 index 00000000..17645bce --- /dev/null +++ b/.opencode/skills/skyengine-build-reference/SKILL.md @@ -0,0 +1,352 @@ +--- +name: skyengine-build-reference +description: Follow the SkyEngine cross-platform build workflow, including third-party bootstrap, platform-specific CMake generation, engine build, Android packaging, and optional test execution. +compatibility: opencode +metadata: + source: README.md, python/third_party.py, repository inspection + audience: contributors +--- + +# SkyEngine Cross-Platform Build Reference + +Use this skill when the task involves building SkyEngine, proposing build commands, or explaining platform-specific build constraints. + +## Working rules + +- Always keep the documented order: third-party bootstrap first, then CMake configure/generate, then engine build, then optional tests or app packaging. +- Treat `python/third_party.py` as the required entrypoint for third-party setup and `3RD_PATH` generation. +- Preserve placeholders like ``, ``, and `` unless the user provides concrete paths. +- Use only platform names that exist in the repository tooling: `Win32`, `MacOS-x86`, `MacOS-arm`, `IOS`, `Android`, `Linux`. +- Call out platform assumptions explicitly when adapting commands. +- Verify whether `SKY_BUILD_TEST=ON` is enabled before recommending `ctest`. +- Mention required SDK/toolchain constraints when relevant, especially for Android and iOS. + +## Canonical workflow + +SkyEngine uses this order on every platform: + +1. Build third-party dependencies with `python/third_party.py` +2. Configure CMake with `-D3RD_PATH=` +3. Build the engine targets +4. Optionally run tests or package the platform launcher + +If `3RD_PATH` does not exist, CMake-side third-party discovery fails. + +## Step 1: Build third-party dependencies (required) + +Canonical command template: + +```bash +python3 python/third_party.py \ + -i \ + -o \ + -e \ + -p \ + -j +``` + +Common options: + +- `-i, --intermediate`: clone/build workspace for 3rd-party sources +- `-o, --output`: installed third-party output used as `3RD_PATH` +- `-e, --engine`: repository root +- `-p, --platform`: one of `Win32`, `MacOS-x86`, `MacOS-arm`, `IOS`, `Android`, `Linux` +- `-j, --jobs`: build parallelism (`0` means auto in the script) +- `-f, --force`: force rebuild even if cached metadata says up to date +- `-c, --clean`: clean package working trees +- `-t, --target`: build a single package +- `-l, --list`: list packages and supported platforms + +Useful examples: + +```bash +# List available packages +python3 python/third_party.py -e -p Win32 --list + +# Force full rebuild for macOS arm64 +python3 python/third_party.py -i -o -e -p MacOS-arm -j 8 -f + +# Build only one package +python3 python/third_party.py -i -o -e -p Linux -t taskflow +``` + +## Step 2: Configure CMake + +Common configure shape: + +```bash +cmake -S -B \ + -G \ + -D3RD_PATH= \ + -DSKY_BUILD_TEST=OFF +``` + +Frequently useful switches: + +- `-DSKY_BUILD_TEST=ON` enable `ctest` +- `-DSKY_BUILD_EDITOR=ON` build editor targets (Qt5 required) +- `-DSKY_BUILD_TOOL=ON` build tooling targets +- plugin switches from `plugins/plugins.json`, such as `-DSKY_BUILD_BULLET=OFF`, `-DSKY_BUILD_PYTHON=ON`, `-DSKY_BUILD_GUIZMO=ON` +- platform/toolchain flags for Android or iOS when needed + +## Plugin build configuration rules + +Use `plugins/plugins.json` as the authoritative plugin compile-configuration entrypoint. + +Each plugin entry defines: + +- `name` human-readable plugin key +- `dir` subdirectory under `plugins/` +- `cmake_var` cache variable used during configure/build selection +- `enabled` repository default for that plugin switch +- `requires_editor` optional editor-only gate + +Repository flow: + +1. `CMakeLists.txt` includes `cmake/plugin_switches.cmake` before `add_subdirectory(plugins)` +2. `cmake/plugin_switches.cmake` reads `plugins/plugins.json` and creates cache bools such as `SKY_BUILD_BULLET` and `SKY_BUILD_PYTHON` +3. `plugins/CMakeLists.txt` reads the same JSON and only calls `add_subdirectory()` when the plugin's `cmake_var` is enabled +4. `cmake/thirdparty.cmake` consumes these switches to enable required third-party packages +5. Per-plugin `plugin.json` files are separate: they describe target dependency wiring after the plugin has already been included in the build + +Working rules: + +- Treat `plugins/plugins.json` as the source of truth for plugin default states +- Use `-D=ON|OFF` at CMake configure time to override a plugin for the current build directory +- If `plugins/plugins.json` changes, `cmake/plugin_switches.cmake` refreshes the cached defaults on the next configure +- `requires_editor: true` means the plugin still needs its own `cmake_var` enabled, but it is skipped unless editor mode is active +- Do not document plugin enablement as runtime-only; this JSON controls configure/build inclusion +- Distinguish compile selection from runtime loading: compile selection comes from `plugins/plugins.json`, while runtime module lists come from `configs/modules_game.json` and `configs/modules_editor.json` + +Current plugin defaults from `plugins/plugins.json`: + +- `SKY_BUILD_GUIZMO=ON` with `requires_editor=true` +- `SKY_BUILD_PYTHON=OFF` +- `SKY_BUILD_XR=OFF` +- `SKY_BUILD_BULLET=ON` +- `SKY_BUILD_RECAST=ON` +- `SKY_BUILD_FREETYPE=ON` +- `SKY_BUILD_TERRAIN=ON` +- `SKY_BUILD_COMPRESSION=ON` +- `SKY_BUILD_PVS=ON` + +Useful configure examples: + +```bash +# Keep repository defaults from plugins/plugins.json +cmake -S -B \ + -G \ + -D3RD_PATH= + +# Enable editor-only plugins and Python support +cmake -S -B \ + -G \ + -D3RD_PATH= \ + -DSKY_BUILD_EDITOR=ON \ + -DSKY_BUILD_GUIZMO=ON \ + -DSKY_BUILD_PYTHON=ON + +# Disable optional runtime plugins for a leaner build +cmake -S -B \ + -G \ + -D3RD_PATH= \ + -DSKY_BUILD_BULLET=OFF \ + -DSKY_BUILD_RECAST=OFF \ + -DSKY_BUILD_COMPRESSION=OFF +``` + +Runtime note: + +- Building a plugin does not automatically guarantee it is listed in runtime module configs +- `configs/modules_editor.json` controls editor module loading +- `configs/modules_game.json` controls game/runtime module loading +- Keep compile-time plugin switches and runtime module manifests in sync when enabling a new plugin for actual use + +## Step 3: Build the engine + +Canonical build command: + +```bash +cmake --build --config Release --parallel +``` + +For non-Android targets, runtime/library outputs default to `output/bin`. + +## Step 4: Optional tests + +Precondition: configure with `-DSKY_BUILD_TEST=ON` before building. + +```bash +ctest --test-dir -C Release --output-on-failure +``` + +## Platform matrix + +### Win32 + +- Third-party platform: `Win32` +- Third-party generator: `Visual Studio 17 2022` +- Recommended CMake generator: `Visual Studio 17 2022` +- Typical configure: + +```bash +python3 python/third_party.py -i -o -e -p Win32 -j 8 + +cmake -S -B \ + -G "Visual Studio 17 2022" \ + -D3RD_PATH= \ + -DSKY_BUILD_TEST=OFF + +cmake --build --config Release --parallel 8 +``` + +Notes: + +- This is the most directly documented path in the repo README and the original plan doc. +- Windows-only dependencies like `dxcompiler` are resolved here. + +### macOS (Intel) + +- Third-party platform: `MacOS-x86` +- Third-party generator: `Xcode` +- Recommended CMake generator: `Xcode` + +```bash +python3 python/third_party.py -i -o -e -p MacOS-x86 -j 8 + +cmake -S -B \ + -G "Xcode" \ + -D3RD_PATH= \ + -DSKY_BUILD_TEST=OFF + +cmake --build --config Release --parallel 8 +``` + +Notes: + +- Metal backend and Apple frameworks are configured on Darwin builds. +- Use this specifically for x86_64 macOS third-party output. + +### macOS (Apple Silicon) + +- Third-party platform: `MacOS-arm` +- Third-party generator: `Xcode` +- Recommended CMake generator: `Xcode` + +```bash +python3 python/third_party.py -i -o -e -p MacOS-arm -j 8 + +cmake -S -B \ + -G "Xcode" \ + -D3RD_PATH= \ + -DSKY_BUILD_TEST=OFF + +cmake --build --config Release --parallel 8 +``` + +Notes: + +- Use this for arm64-native third-party artifacts. +- Do not reuse `MacOS-x86` third-party output for Apple Silicon builds. + +### iOS + +- Third-party platform: `IOS` +- Third-party generator: `Xcode` +- third-party bootstrap injects: + - `CMAKE_TOOLCHAIN_FILE=/ios-cmake/ios.toolchain.cmake` + - `PLATFORM=OS64` + +```bash +python3 python/third_party.py -i -o -e -p IOS -j 8 + +cmake -S -B \ + -G "Xcode" \ + -D3RD_PATH= \ + -DSKY_BUILD_TEST=OFF + +cmake --build --config Release --parallel 8 +``` + +Notes: + +- The repo's third-party pipeline expects `ios-cmake` to exist under the intermediate workspace. +- iOS is arm64-oriented in the repository tooling. +- Xcode is required. + +### Android + +- Third-party platform: `Android` +- Third-party generator: `Ninja` +- third-party bootstrap injects: + - `ANDROID_ABI=arm64-v8a` + - `ANDROID_STL=c++_static` + - `ANDROID_PLATFORM=android-31` + - `CMAKE_TOOLCHAIN_FILE=/build/cmake/android.toolchain.cmake` +- Required SDK env var: one of `ANDROID_HOME`, `ANDROID_SDK_ROOT`, `ANDROID_SDK` +- Expected NDK version in the repo script: `27.0.12077973` + +```bash +python3 python/third_party.py -i -o -e -p Android -j 8 + +cmake -S -B \ + -G Ninja \ + -D3RD_PATH= \ + -DCMAKE_TOOLCHAIN_FILE=/build/cmake/android.toolchain.cmake \ + -DANDROID_ABI=arm64-v8a \ + -DANDROID_PLATFORM=android-31 + +cmake --build --config Release --parallel 8 +``` + +Notes: + +- If the Android SDK/NDK env vars are missing, `python/third_party.py` fails. +- The repo also contains an Android Gradle project under `engine/launcher/android/` for launcher packaging. +- Native engine/library build and Android app packaging are related but not identical steps. + +### Linux + +- Third-party platform: `Linux` +- Third-party generator: `Ninja` +- Repository tooling also lists `Unix Makefiles` as an available configure generator on Linux + +```bash +python3 python/third_party.py -i -o -e -p Linux -j 8 + +cmake -S -B \ + -G Ninja \ + -D3RD_PATH= \ + -DSKY_BUILD_TEST=OFF + +cmake --build --config Release --parallel 8 +``` + +Notes: + +- Linux is supported by the third-party script even though the top-level README platform table is older and omits it. +- If Ninja is unavailable, `Unix Makefiles` is a repository-supported alternative from the Python preset generator map. + +## Optional build targets + +Use these configure switches when the request needs more than the default launcher/runtime build: + +- `-DSKY_BUILD_EDITOR=ON` editor targets (Qt5 required) +- `-DSKY_BUILD_TOOL=ON` tools such as asset/shader utilities +- `-DSKY_BUILD_TEST=ON` test targets plus `ctest` + +## Missing wrapper note + +No repository wrapper script currently provides a one-command engine build flow. + +When users ask for a one-command build flow, prefer the explicit `python/third_party.py` + `cmake` sequence shown above unless a supported wrapper is added back to the repository. + +## Response style guidance + +When answering with this skill: + +- start by naming the target platform +- show bootstrap first, configure second, build third +- include test or packaging steps only when relevant +- mention platform constraints inline instead of burying them later +- do not suggest skipping `3RD_PATH` generation diff --git a/.opencode/skills/skyengine-commit-format/SKILL.md b/.opencode/skills/skyengine-commit-format/SKILL.md new file mode 100644 index 00000000..c4e44ba8 --- /dev/null +++ b/.opencode/skills/skyengine-commit-format/SKILL.md @@ -0,0 +1,126 @@ +--- +name: skyengine-commit-format +description: Write SkyEngine commit messages using the repository's common prefix style, especially feat, fix, 3rd, build, refactor, doc, tool, and platform. +compatibility: opencode +metadata: + audience: contributors + source: git-history-derived +--- + +# SkyEngine Commit Format + +Use this skill whenever you need to draft, review, or suggest a git commit message for this repository. + +## Goal + +Match the repository's existing commit style instead of defaulting to generic Conventional Commits. + +## Core rules + +- Prefer a short prefix that reflects the change scope. +- Use the repository's existing common prefixes first: `feat`, `fix`, `3rd`, `build`. +- Other observed prefixes that are acceptable when they fit better: `refactor`, `doc`, `tool`, `platform`, `sample`, `assets`, `bug`, `editor`. +- Keep the subject concise and action-oriented. +- English commit subjects are the norm in this repository. +- Do not invent a new prefix if an existing one already fits. + +## Prefix guide + +### `feat` +Use for new user-facing or developer-facing functionality. + +Examples: + +- `[feat]: Update PBR Shader.` +- `[feat]: update 3rdparty` +- `[feat]: ReverseZ` + +### `fix` +Use for bug fixes, regressions, broken builds, or corrections to existing behavior. + +Examples: + +- `fix: mac os build.` +- `[fix]: fix release compile issue.` +- `[fix]: DescriptorSet update.` + +### `3rd` +Use for third-party dependency changes, patches, package updates, submodule/vendor changes, or external library build logic. + +Examples: + +- `[3rd]: update 3rd cmake` +- `[3rd]: rewrite 3rd build script. add readme.` +- `[3rd]: add node editor` + +### `build` +Use for build system logic, build configuration, CI/build pipeline behavior, or generator/config changes that are primarily build-related. + +Examples: + +- `[build]: plugin system driven by JSON config` +- `fix: Add DS_Store` *(if the practical effect is to fix repo/build hygiene rather than feature behavior)* + +## Secondary prefixes + +- `refactor` internal structure cleanup without intended behavior change +- `doc` documentation-only updates +- `tool` developer tooling or scripts +- `platform` platform-specific support or fixes +- `sample` sample/demo changes +- `assets` asset-only updates +- `editor` editor-specific work when that scope is clearer than `feat` +- `bug` older history uses this; prefer `fix` for new commits unless matching nearby history is important + +## Formatting preference + +This repository has mixed historical formatting, but these patterns are all seen: + +- `fix: message` +- `[fix]: message` +- `[feat]: message` +- `Plain sentence without prefix` + +For new commits, prefer one of these two styles for consistency and clarity: + +1. `fix: short message` +2. `[feat]: short message` + +If you are matching nearby history in a touched area, mirror the local style. + +## Selection checklist + +Before finalizing a commit message, ask: + +1. Is this a new capability? `feat` +2. Is this repairing behavior or build breakage? `fix` +3. Is this changing vendored or external dependencies? `3rd` +4. Is this primarily build/config/pipeline logic? `build` +5. Is this only restructuring? `refactor` +6. Is this only documentation? `doc` + +## Good examples + +- `feat: add OpenCode skills for SkyEngine docs` +- `fix: restore macOS build flags` +- `3rd: update glslang patch handling` +- `build: simplify third-party configure flow` +- `doc: add OpenCode skills README` + +## Avoid + +- Vague subjects like `update`, `fix issue`, or `change code` +- Prefixes not already used in the repository when a common one fits +- Overly long explanations in the subject line +- Mixing multiple unrelated concerns into one commit message + +## Default recommendation + +When unsure, choose from this order: + +1. `fix` +2. `feat` +3. `3rd` +4. `build` + +Then write a short, specific subject describing the actual change. diff --git a/agent/project_reference.md b/.opencode/skills/skyengine-project-reference/SKILL.md similarity index 77% rename from agent/project_reference.md rename to .opencode/skills/skyengine-project-reference/SKILL.md index ea815685..5dc68ca7 100644 --- a/agent/project_reference.md +++ b/.opencode/skills/skyengine-project-reference/SKILL.md @@ -1,4 +1,30 @@ -# SkyEngine 蕁合ユ +--- +name: skyengine-project-reference +description: Reference the SkyEngine project layout, build targets, module dependencies, and third-party library map when answering architecture or repository structure questions. +compatibility: opencode +metadata: + source: README.md, repository inspection + audience: contributors +--- + +# SkyEngine Project Reference + +Use this skill when the task needs repository context instead of fresh exploration, especially for: + +- explaining where a subsystem lives +- mapping target names to source directories +- recalling build flags and plugin toggles +- identifying third-party dependencies and their purpose +- summarizing render backends, asset pipeline, or module relationships + +## Working rules + +- Treat this skill as a quick-reference source for repository structure and build knowledge. +- Prefer this reference before doing broad repo searches when the user asks for high-level project understanding. +- If the request depends on current file contents beyond this document, verify with tools instead of assuming. +- Quote paths, targets, and flags exactly as listed here. + +## SkyEngine 蕁合ユ ## 网茹 @@ -27,7 +53,6 @@ SkyEngine/ configs/ # 菴茵狗臀 cmake/ # 綮咲臀 python/ # Python 綏ュ潔 - agent/ # AI Agent 莨篁 ``` --- @@ -83,9 +108,31 @@ SkyEngine/ | `PythonModule` | SKY_BUILD_PYTHON | cpython | | `CompressionModule` | SKY_BUILD_COMPRESSION | lz4 | | `FreeTypeModule` | SKY_BUILD_FREETYPE | freetype | -| `ImGuizmoModule` | SKY_BUILD_EDITOR | ImGuizmo | +| `ImGuizmoModule` | SKY_BUILD_GUIZMO + SKY_BUILD_EDITOR | ImGuizmo | | `XRModule` | SKY_BUILD_XR | OpenXR | +### 篁句莚臀膊∞ + +- `plugins/plugins.json` 篁句莚臀膸筝ュ +- 罸♂臀喝絎箙鐚`name``dir``cmake_var``enabled`鐚篁区絽 `requires_editor` +- `cmake/plugin_switches.cmake` 蕁九臀倶笈糸莚ユ篁駈綛倶 `cmake_var` 筝 CMake cache +- `plugins/CMakeLists.txt` 罨∴糸莚ユ篁駈篁綵絲劫 `cmake_var` 筝 ON 倶 `add_subdirectory()` +- `requires_editor: true` 篁区荀羆膽莨罔≦鐚綵 `guizmo` 莎 `SKY_BUILD_GUIZMO`鐚綛九 `SKY_BUILD_EDITOR` / `SKY_EDITOR` 膾 +- `plugins/plugins.json` 膊∞膽莚/倶究膾喝ュ轡腮鐚筝菴茵倶─莉醇 +- 菴茵倶─羝 `configs/modules_game.json` 筝 `configs/modules_editor.json` 膊∞ + +綵 `plugins/plugins.json` 藥莅ら々鐚 + +- `guizmo` `SKY_BUILD_GUIZMO=ON`鐚`requires_editor=true` +- `python` `SKY_BUILD_PYTHON=OFF` +- `xr` `SKY_BUILD_XR=OFF` +- `bullet` `SKY_BUILD_BULLET=ON` +- `recast` `SKY_BUILD_RECAST=ON` +- `freetype` `SKY_BUILD_FREETYPE=ON` +- `terrain` `SKY_BUILD_TERRAIN=ON` +- `compression` `SKY_BUILD_COMPRESSION=ON` +- `pvs` `SKY_BUILD_PVS=ON` + ### 羌莚 | Target | 莚贋 | @@ -115,6 +162,9 @@ SkyEngine/ | `SKY_BUILD_FREETYPE` | OFF | 絖篏羝我 | | `SKY_BUILD_BULLET` | OFF | Bullet | | `SKY_BUILD_RECAST` | OFF | Recast 絲取 | +| `SKY_BUILD_GUIZMO` | OFF | ImGuizmo 篁句莚綣鰹篁膽莨罔≦鐚 | +| `SKY_BUILD_TERRAIN` | OFF | Terrain 篁句莚綣 | +| `SKY_BUILD_PVS` | OFF | PVS 篁句莚綣 | | `SKY_USE_TRACY` | OFF | Tracy ц遵 | | `SKY_MATH_SIMD` | OFF | SIMD 医篌 | diff --git a/agent/plan/engine_build_plan.md b/agent/plan/engine_build_plan.md deleted file mode 100644 index b43be981..00000000 --- a/agent/plan/engine_build_plan.md +++ /dev/null @@ -1,69 +0,0 @@ -# SkyEngine 綮肴≦鐚4 罩ワ - -莅≦篁綺綵膸九鐚合墾鐚**筝劫綮阪ュg筝篏睡 `python/third_party.py`** - -## Step1: 筝劫綮削綽蕁糸ц鐚 - -- ュh鐚`python/third_party.py` -- 鐚 `3RD_PATH` 綵鐚膸 CMake 臀箴莎罩よ君緇鐚 -- ィ巡擦鐚Win32鐚鐚 - -```bash -python python/third_party.py \ - -i \ - -o \ - -e \ - -p Win32 \ - -j 8 -``` - -## Step2: 綮 solution鐚CMake Configure/Generate鐚 - -- 莚贋鐚篁綺絽後 CMake 綏ョ鐚Visual Studio 筝篌 `.sln`鐚 -- ィ巡擦鐚 - -```bash -cmake -S -B \ - -G "Visual Studio 17 2022" \ - -D3RD_PATH= \ - -DSKY_BUILD_TEST=OFF -``` - -> ラ荀罨≧腮ц羌莚鐚絨 `-DSKY_BUILD_TEST=ON` - -## Step3: 綮阪 - -- ィ巡擦鐚 - -```bash -cmake --build --config Release --parallel 8 -``` - -- 篋х藥莅よ阪遺綺 `output/bin`鐚掩 CMake 臀ァ駈 - -## Step4: 絋荀菴茵 test - -- 鐚Step2 綣 `SKY_BUILD_TEST=ON`鐚綛九群絎 Step3 -- 菴茵劫鐚 - -```bash -ctest --test-dir -C Release --output-on-failure -``` - ---- - -## 筝ц - -綏臥絅鐚`agent/scripts/build_engine.py` - -腓坂鐚 - -```bash -python agent/scripts/build_engine.py --platform Win32 --config Release --jobs 8 -``` - -羌莚鐚 - -```bash -python agent/scripts/build_engine.py --platform Win32 --config Release --jobs 8 --enable-tests --run-tests -``` From 9e4d58ecbfdb04c76d6297ac6a9a38f913589190 Mon Sep 17 00:00:00 2001 From: Zach Date: Sun, 22 Mar 2026 14:40:38 +0800 Subject: [PATCH 21/38] [ops]: open spec init. --- .github/prompts/opsx-apply.prompt.md | 149 +++++++++ .github/prompts/opsx-archive.prompt.md | 154 ++++++++++ .github/prompts/opsx-explore.prompt.md | 170 +++++++++++ .github/prompts/opsx-propose.prompt.md | 103 +++++++ .github/skills/openspec-apply-change/SKILL.md | 156 ++++++++++ .../skills/openspec-archive-change/SKILL.md | 114 +++++++ .github/skills/openspec-explore/SKILL.md | 288 ++++++++++++++++++ .github/skills/openspec-propose/SKILL.md | 110 +++++++ .opencode/command/opsx-apply.md | 149 +++++++++ .opencode/command/opsx-archive.md | 154 ++++++++++ .opencode/command/opsx-explore.md | 170 +++++++++++ .opencode/command/opsx-propose.md | 103 +++++++ .../skills/openspec-apply-change/SKILL.md | 156 ++++++++++ .../skills/openspec-archive-change/SKILL.md | 114 +++++++ .opencode/skills/openspec-explore/SKILL.md | 288 ++++++++++++++++++ .opencode/skills/openspec-propose/SKILL.md | 110 +++++++ openspec/config.yaml | 20 ++ 17 files changed, 2508 insertions(+) create mode 100644 .github/prompts/opsx-apply.prompt.md create mode 100644 .github/prompts/opsx-archive.prompt.md create mode 100644 .github/prompts/opsx-explore.prompt.md create mode 100644 .github/prompts/opsx-propose.prompt.md create mode 100644 .github/skills/openspec-apply-change/SKILL.md create mode 100644 .github/skills/openspec-archive-change/SKILL.md create mode 100644 .github/skills/openspec-explore/SKILL.md create mode 100644 .github/skills/openspec-propose/SKILL.md create mode 100644 .opencode/command/opsx-apply.md create mode 100644 .opencode/command/opsx-archive.md create mode 100644 .opencode/command/opsx-explore.md create mode 100644 .opencode/command/opsx-propose.md create mode 100644 .opencode/skills/openspec-apply-change/SKILL.md create mode 100644 .opencode/skills/openspec-archive-change/SKILL.md create mode 100644 .opencode/skills/openspec-explore/SKILL.md create mode 100644 .opencode/skills/openspec-propose/SKILL.md create mode 100644 openspec/config.yaml diff --git a/.github/prompts/opsx-apply.prompt.md b/.github/prompts/opsx-apply.prompt.md new file mode 100644 index 00000000..494e10e9 --- /dev/null +++ b/.github/prompts/opsx-apply.prompt.md @@ -0,0 +1,149 @@ +--- +description: Implement tasks from an OpenSpec change (Experimental) +--- + +Implement tasks from an OpenSpec change. + +**Input**: Optionally specify a change name (e.g., `/opsx:apply add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes. + +**Steps** + +1. **Select the change** + + If a name is provided, use it. Otherwise: + - Infer from conversation context if the user mentioned a change + - Auto-select if only one active change exists + - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select + + Always announce: "Using change: " and how to override (e.g., `/opsx:apply `). + +2. **Check status to understand the schema** + ```bash + openspec status --change "" --json + ``` + Parse the JSON to understand: + - `schemaName`: The workflow being used (e.g., "spec-driven") + - Which artifact contains the tasks (typically "tasks" for spec-driven, check status for others) + +3. **Get apply instructions** + + ```bash + openspec instructions apply --change "" --json + ``` + + This returns: + - Context file paths (varies by schema) + - Progress (total, complete, remaining) + - Task list with status + - Dynamic instruction based on current state + + **Handle states:** + - If `state: "blocked"` (missing artifacts): show message, suggest using `/opsx:continue` + - If `state: "all_done"`: congratulate, suggest archive + - Otherwise: proceed to implementation + +4. **Read context files** + + Read the files listed in `contextFiles` from the apply instructions output. + The files depend on the schema being used: + - **spec-driven**: proposal, specs, design, tasks + - Other schemas: follow the contextFiles from CLI output + +5. **Show current progress** + + Display: + - Schema being used + - Progress: "N/M tasks complete" + - Remaining tasks overview + - Dynamic instruction from CLI + +6. **Implement tasks (loop until done or blocked)** + + For each pending task: + - Show which task is being worked on + - Make the code changes required + - Keep changes minimal and focused + - Mark task complete in the tasks file: `- [ ]` `- [x]` + - Continue to next task + + **Pause if:** + - Task is unclear ask for clarification + - Implementation reveals a design issue suggest updating artifacts + - Error or blocker encountered report and wait for guidance + - User interrupts + +7. **On completion or pause, show status** + + Display: + - Tasks completed this session + - Overall progress: "N/M tasks complete" + - If all done: suggest archive + - If paused: explain why and wait for guidance + +**Output During Implementation** + +``` +## Implementing: (schema: ) + +Working on task 3/7: +[...implementation happening...] + Task complete + +Working on task 4/7: +[...implementation happening...] + Task complete +``` + +**Output On Completion** + +``` +## Implementation Complete + +**Change:** +**Schema:** +**Progress:** 7/7 tasks complete + +### Completed This Session +- [x] Task 1 +- [x] Task 2 +... + +All tasks complete! You can archive this change with `/opsx:archive`. +``` + +**Output On Pause (Issue Encountered)** + +``` +## Implementation Paused + +**Change:** +**Schema:** +**Progress:** 4/7 tasks complete + +### Issue Encountered + + +**Options:** +1.