diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 8ef95507..62a9531d 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -14,16 +14,16 @@ These instructions define how GitHub Copilot should assist with this project. Th - See the tutorial at [Getting Started](https://github.com/microsoft/DirectXMesh/wiki/Getting-Started). - The recommended way to integrate *DirectXMesh* into your project is by using the *vcpkg* Package Manager. -- You can make use of the nuget.org packages **directxmesh_desktop_2019**, **directxmesh_desktop_win10**, or **directxmesh_uwp**. +- You can make use of the nuget.org packages **directxmesh_desktop_win10** or **directxmesh_uwp**. - You can also use the library source code directly in your project or as a git submodule. ## General Guidelines -- **Code Style**: The project uses an .editorconfig file to enforce coding standards. Follow the rules defined in `.editorconfig` for indentation, line endings, and other formatting. Additional information can be found on the wiki at [Implementation](https://github.com/microsoft/DirectXTK/wiki/Implementation). The library implementation is written to be compatible with C++14 features, but C++17 is required to build the project for the command-line tools which utilize C++17 filesystem for long file path support. -> Notable `.editorconfig` rules: C/C++ files use 4-space indentation, `crlf` line endings, and `latin1` charset — avoid non-ASCII characters in source files. HLSL files have separate indent/spacing rules defined in `.editorconfig`. +- **Code Style**: The project uses an .editorconfig file to enforce coding standards. Follow the rules defined in `.editorconfig` for indentation, line endings, and other formatting. Additional information can be found on the wiki at [Implementation](https://github.com/microsoft/DirectXTK/wiki/Implementation). The library's public API requires C++11, and the project builds with C++17 (`CMAKE_CXX_STANDARD 17`). The command-line tools also use C++17, including `` for long file path support. This code is designed to build with Visual Studio 2022, Visual Studio 2026, clang for Windows v12 or later, or MinGW 12.2. +> Notable `.editorconfig` rules: C/C++ files use 4-space indentation, `crlf` line endings, and `latin1` charset — avoid non-ASCII characters in source files. - **Documentation**: The project provides documentation in the form of wiki pages available at [Documentation](https://github.com/microsoft/DirectXMesh/wiki/). - **Error Handling**: Use C++ exceptions for error handling and uses RAII smart pointers to ensure resources are properly managed. For some functions that return HRESULT error codes, they are marked `noexcept`, use `std::nothrow` for memory allocation, and should not throw exceptions. -- **Testing**: Unit tests for this project are implemented in this repository [Test Suite](https://github.com/walbourn/directxmeshtest/) and can be run using CTest per the instructions at [Test Documentation](https://github.com/walbourn/directxmeshtest/wiki). +- **Testing**: Unit tests for this project are implemented in this repository [Test Suite](https://github.com/walbourn/directxmeshtest/) and can be run using CTest per the instructions at [Test Documentation](https://github.com/walbourn/directxmeshtest/wiki). See [test copilot instructions](https://github.com/walbourn/directxmeshtest/blob/main/.github/copilot-instructions.md) for additional information on the tests. - **Security**: This project uses secure coding practices from the Microsoft Secure Coding Guidelines, and is subject to the `SECURITY.md` file in the root of the repository. Functions that read input from geometry files are subject to OneFuzz fuzz testing to ensure they are secure against malformed files. - **Dependencies**: The project uses CMake and VCPKG for managing dependencies, making optional use of DirectXMath and DirectX-Headers. The project can be built without these dependencies, relying on the Windows SDK for core functionality. - **Continuous Integration**: This project implements GitHub Actions for continuous integration, ensuring that all code changes are tested and validated before merging. This includes building the project for a number of configurations and toolsets, running a subset of unit tests, and static code analysis including GitHub super-linter, CodeQL, and MSVC Code Analysis. @@ -49,14 +49,14 @@ wiki/ # Local clone of the GitHub wiki documentation repository. - Use RAII for all resource ownership (memory, file handles, etc.). - Many classes utilize the pImpl idiom to hide implementation details, and to enable optimized memory alignment in the implementation. -- Use `std::unique_ptr` for exclusive ownership and `std::shared_ptr` for shared ownership. -- Use `Microsoft::WRL::ComPtr` for COM object management. +- Use `std::unique_ptr` for exclusive ownership. - Make use of anonymous namespaces to limit scope of functions and variables. - Make use of `assert` for debugging checks, but be sure to validate input parameters in release builds. - Explicitly `= delete` copy constructors and copy-assignment operators on all classes that use the pImpl idiom. - Explicitly utilize `= default` or `=delete` for copy constructors, assignment operators, move constructors and move-assignment operators where appropriate. - Use 16-byte alignment (`_aligned_malloc` / `_aligned_free`) to support SIMD operations in the implementation, but do not expose this requirement in public APIs. > For non-Windows support, the implementation uses C++17 `aligned_alloc` instead of `_aligned_malloc`. +- All implementation `.cpp` files include `DirectXMeshP.h` as their first include (precompiled header). MinGW builds skip precompiled headers. #### SAL Annotations @@ -110,17 +110,18 @@ HRESULT DirectX::ComputeNormals( #### `noexcept` Rules - All query and utility functions that cannot fail (e.g., `IsValidVB`, `IsValidIB`) are marked `noexcept`. -- All HRESULT-returning I/O and processing functions are also `noexcept` — errors are communicated via return code, never via exceptions. +- HRESULT-returning functions that do not perform heap allocation or use Standard C++ containers are `noexcept` — errors are communicated via return code, never via exceptions (e.g., `ComputeNormals`, `ReorderIB`, `FinalizeIB`, `FinalizeVB`, `CompactVB`, `OptimizeVertices`, `ComputeCullData`). +- HRESULT-returning functions that use `std::vector`, `std::function`, or other potentially throwing types are *not* marked `noexcept` — they may throw on allocation failure (e.g., `GenerateAdjacencyAndPointReps`, `Validate`, `Clean`, `WeldVertices`, `ComputeMeshlets`, `OptimizeFaces`). - Constructors and functions that perform heap allocation or utilize Standard C++ containers that may throw are marked `noexcept(false)`. #### Enum Flags Pattern -Flags enums follow this pattern — a `uint32_t`-based unscoped enum with a `_NONE = 0x0` base case, followed by a call to `DEFINE_ENUM_FLAG_OPERATORS` (defined in `DirectXMesh.inl`) to enable `|`, `&`, and `~` operators: +Flags enums follow this pattern — a `uint32_t`-based unscoped enum with a `_DEFAULT = 0` base case, followed by a call to `DEFINE_ENUM_FLAG_OPERATORS` (invoked in `DirectXMesh.inl`) to enable `|`, `&`, and `~` operators: ```cpp enum CNORM_FLAGS : uint32_t { - CNORM_DEFAULT = 0x0, + CNORM_DEFAULT = 0, // Default is to compute normals using weight-by-angle CNORM_WEIGHT_BY_AREA = 0x1, @@ -142,7 +143,7 @@ See [this blog post](https://walbourn.github.io/modern-c++-bitmask-types/) for m - Don’t use raw pointers for ownership. - Avoid macros for constants—prefer `constexpr` or `inline` `const`. -- Don’t put implementation logic in header files unless using templates, although the SimpleMath library does use an .inl file for performance. +- Don't put implementation logic in header files unless using templates, although the DirectXMesh library does use an .inl file for performance for a few specific utility functions that are called in tight loops (e.g., `IsValidIB`, `IsValidVB`). - Avoid using `using namespace` in header files to prevent polluting the global namespace. ## Naming Conventions @@ -174,8 +175,8 @@ Every source file (`.cpp`, `.h`, etc.) must begin with this block: ``` Section separators within files use: -- Major sections: `//-------------------------------------------------------------------------------------` -- Subsections: `//---------------------------------------------------------------------------------` +- Major sections: `//=====================================================================================` +- Subsections: `//-------------------------------------------------------------------------------------` The project does **not** use Doxygen. API documentation is maintained exclusively on the GitHub wiki. @@ -183,7 +184,7 @@ The project does **not** use Doxygen. API documentation is maintained exclusivel - [Source git repository on GitHub](https://github.com/microsoft/DirectXMesh.git) - [DirectXMesh documentation git repository on GitHub](https://github.com/microsoft/DirectXMesh.wiki.git) -- [DirectXMesh test suite git repository on GitHub](https://github.com/walbourn/directxmeshtest.wiki.git). +- [DirectXMesh test suite git repository on GitHub](https://github.com/walbourn/directxmeshtest.git). - [C++ Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) - [Microsoft Secure Coding Guidelines](https://learn.microsoft.com/en-us/security/develop/secure-coding-guidelines) - [CMake Documentation](https://cmake.org/documentation/) @@ -221,8 +222,9 @@ When creating documentation: ## Cross-platform Support Notes -- The code supports building for Windows and Linux. +- The code targets Win32 desktop applications for Windows 8.1 or later, Xbox One, Xbox Series X\|S, Universal Windows Platform (UWP) apps for Windows 10 and Windows 11, and Linux. - Portability and conformance of the code is validated by building with Visual C++, clang/LLVM for Windows, MinGW, and GCC for Linux compilers. +- The project ships MSBuild projects for Visual Studio 2022 (`.sln` / `.vcxproj`) and Visual Studio 2026 (`.slnx` / `.vcxproj`). VS 2019 projects have been retired. ### Platform and Compiler `#ifdef` Guards @@ -231,10 +233,12 @@ Use these established guards — do not invent new ones: | Guard | Purpose | | --- | --- | | `_WIN32` | Windows platform (desktop, UWP, Xbox) | -| `_GAMING_XBOX` | Xbox One or Xbox Series X\|S | -| `_GAMING_XBOX_SCARLETT` | Xbox Series X\|S | +| `_GAMING_XBOX` | Xbox platform (GDK - covers both Xbox One and Xbox Series X\|S) | +| `_GAMING_XBOX_SCARLETT` | Xbox Series X\|S (GDK with Xbox Extensions) | +| `_GAMING_XBOX_XBOXONE` | Xbox One (GDK with Xbox Extensions) | | `_XBOX_ONE && _TITLE` | Legacy Xbox One XDK — **no longer supported**; triggers a `#error` at compile time | | `_MSC_VER` | MSVC-specific (and MSVC-like clang-cl) pragmas and warning suppression | +| `__INTEL_COMPILER` | Intel C++ Compiler classic warning suppression | | `__clang__` | Clang/LLVM diagnostic suppressions | | `__MINGW32__` | MinGW compatibility headers | | `__GNUC__` | MinGW/GCC DLL attribute equivalents | @@ -245,17 +249,18 @@ Use these established guards — do not invent new ones: > `_M_ARM`/ `__arm__` is legacy 32-bit ARM which is deprecated. -Non-Windows builds (Linux/WSL) omit WIC entirely and use `` and `` from the DirectX-Headers package instead of the Windows SDK. +Non-Windows builds (Linux/WSL) use `` and `` from the DirectX-Headers package instead of the Windows SDK. ### Error Codes -The following symbols are not custom error codes, but aliases for `HRESULT_FROM_WIN32` error values. +The following symbols are not custom error codes, but aliases for `HRESULT_FROM_WIN32` error codes. | Symbol | Standard Win32 HRESULT | | -------- | ------------- | | `HRESULT_E_ARITHMETIC_OVERFLOW` | `HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW)` | | `HRESULT_E_NOT_SUPPORTED` | `HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)` | | `HRESULT_E_INVALID_NAME` | `HRESULT_FROM_WIN32(ERROR_INVALID_NAME)` | +| `E_BOUNDS` | `0x8000000BL` (conditionally defined if not already present) | ## Code Review Instructions diff --git a/.github/workflows/arm64.yml b/.github/workflows/arm64.yml index 12376e88..2e2dc7e0 100644 --- a/.github/workflows/arm64.yml +++ b/.github/workflows/arm64.yml @@ -12,6 +12,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/*.cmd - build/*.props @@ -24,6 +25,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/*.cmd - build/*.props @@ -45,7 +47,7 @@ jobs: build_type: [arm64-Debug, arm64-Release, arm64-Debug-UWP, arm64-Release-UWP] steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: 'Install Ninja' run: choco install ninja diff --git a/.github/workflows/arm64bvt.yml b/.github/workflows/arm64bvt.yml index 86a67b38..730e7308 100644 --- a/.github/workflows/arm64bvt.yml +++ b/.github/workflows/arm64bvt.yml @@ -12,6 +12,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/*.cmd - build/*.props @@ -24,6 +25,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/*.cmd - build/*.props @@ -46,10 +48,10 @@ jobs: build_type: [arm64-Release] steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Clone test repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: repository: walbourn/directxmeshtest path: Tests diff --git a/.github/workflows/bvt.yml b/.github/workflows/bvt.yml index ca1a137a..448f58ca 100644 --- a/.github/workflows/bvt.yml +++ b/.github/workflows/bvt.yml @@ -12,6 +12,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/*.cmd - build/*.props @@ -24,6 +25,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/*.cmd - build/*.props @@ -48,10 +50,10 @@ jobs: arch: [amd64] steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Clone test repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: repository: walbourn/directxmeshtest path: Tests diff --git a/.github/workflows/clangcl.yml b/.github/workflows/clangcl.yml index 4d7c3db9..0c95b971 100644 --- a/.github/workflows/clangcl.yml +++ b/.github/workflows/clangcl.yml @@ -12,6 +12,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/*.cmd - build/*.props @@ -24,6 +25,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/*.cmd - build/*.props diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 7881c4c9..731d26e4 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -12,6 +12,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/*.cmd - build/*.props @@ -24,6 +25,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/*.cmd - build/*.props @@ -49,7 +51,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: 'Install Ninja' run: choco install ninja @@ -57,7 +59,7 @@ jobs: - uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0 - name: Initialize CodeQL - uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5 + uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5 with: languages: c-cpp build-mode: manual @@ -71,6 +73,6 @@ jobs: run: cmake --build out\build\x64-Debug - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5 + uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5 with: category: "/language:c-cpp" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 69dda264..cffa7b07 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,6 +12,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/*.cmd - build/*.props @@ -24,6 +25,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/*.cmd - build/*.props diff --git a/.github/workflows/msbuild.yml b/.github/workflows/msbuild.yml index 43af5293..ef593adf 100644 --- a/.github/workflows/msbuild.yml +++ b/.github/workflows/msbuild.yml @@ -12,6 +12,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/* pull_request: @@ -20,6 +21,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/* diff --git a/.github/workflows/msvc.yml b/.github/workflows/msvc.yml index 3c978ec8..770fc315 100644 --- a/.github/workflows/msvc.yml +++ b/.github/workflows/msvc.yml @@ -12,6 +12,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/*.cmd - build/*.props @@ -24,6 +25,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/*.cmd - build/*.props @@ -47,7 +49,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Configure CMake working-directory: ${{ github.workspace }} @@ -63,6 +65,6 @@ jobs: # Upload SARIF file to GitHub Code Scanning Alerts - name: Upload SARIF to GitHub - uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5 + uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5 with: sarif_file: ${{ steps.run-analysis.outputs.sarif }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 60d071e5..f30765cf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,6 +12,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/*.cmd - build/*.props @@ -24,6 +25,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/*.cmd - build/*.props diff --git a/.github/workflows/uwp.yml b/.github/workflows/uwp.yml index b7ac33d8..af240eed 100644 --- a/.github/workflows/uwp.yml +++ b/.github/workflows/uwp.yml @@ -12,6 +12,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/*.cmd - build/*.props @@ -24,6 +25,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/*.cmd - build/*.props @@ -63,7 +65,7 @@ jobs: arch: amd64_arm64 steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: 'Install Ninja' run: choco install ninja diff --git a/.github/workflows/vcpkg.yml b/.github/workflows/vcpkg.yml index b2192dbc..1fd865eb 100644 --- a/.github/workflows/vcpkg.yml +++ b/.github/workflows/vcpkg.yml @@ -12,6 +12,8 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' + - '.nuget/*' - build/*.cmd - build/*.props - build/*.ps1 @@ -23,6 +25,8 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' + - '.nuget/*' - build/*.cmd - build/*.props - build/*.ps1 @@ -80,7 +84,7 @@ jobs: shared: 'ON' steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: 'Install Ninja' run: choco install ninja diff --git a/.github/workflows/wsl.yml b/.github/workflows/wsl.yml index 99fc81e7..b8718959 100644 --- a/.github/workflows/wsl.yml +++ b/.github/workflows/wsl.yml @@ -12,6 +12,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/*.cmd - build/*.props @@ -24,6 +25,7 @@ on: - '*.md' - LICENSE - '.azuredevops/**' + - '.github/*.md' - '.nuget/*' - build/*.cmd - build/*.props @@ -47,7 +49,7 @@ jobs: gcc: [12, 13, 14] steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: seanmiddleditch/gha-setup-ninja@3b1f8f94a2f8254bd26914c4ab9474d4f0015f67 # v6 diff --git a/DirectXMesh/DirectXMesh.h b/DirectXMesh/DirectXMesh.h index 3eef83fb..5eb759e5 100644 --- a/DirectXMesh/DirectXMesh.h +++ b/DirectXMesh/DirectXMesh.h @@ -268,7 +268,7 @@ namespace DirectX enum CNORM_FLAGS : uint32_t { - CNORM_DEFAULT = 0x0, + CNORM_DEFAULT = 0, // Default is to compute normals using weight-by-angle CNORM_WEIGHT_BY_AREA = 0x1, @@ -340,7 +340,7 @@ namespace DirectX enum VALIDATE_FLAGS : uint32_t { - VALIDATE_DEFAULT = 0x0, + VALIDATE_DEFAULT = 0, VALIDATE_BACKFACING = 0x1, // Check for duplicate neighbor from triangle (requires adjacency) @@ -426,48 +426,48 @@ namespace DirectX }; DIRECTX_MESH_API HRESULT __cdecl OptimizeFaces( - _In_reads_(nFaces * 3) const uint16_t* indices, _In_ size_t nFaces, + _In_reads_(nFaces * 3) const uint16_t* indices, _In_ size_t nFaces, _In_ size_t nVerts, _In_reads_(nFaces * 3) const uint32_t* adjacency, _Out_writes_(nFaces) uint32_t* faceRemap, _In_ uint32_t vertexCache = OPTFACES_V_DEFAULT, _In_ uint32_t restart = OPTFACES_R_DEFAULT); DIRECTX_MESH_API HRESULT __cdecl OptimizeFaces( - _In_reads_(nFaces * 3) const uint32_t* indices, _In_ size_t nFaces, + _In_reads_(nFaces * 3) const uint32_t* indices, _In_ size_t nFaces, _In_ size_t nVerts, _In_reads_(nFaces * 3) const uint32_t* adjacency, _Out_writes_(nFaces) uint32_t* faceRemap, _In_ uint32_t vertexCache = OPTFACES_V_DEFAULT, _In_ uint32_t restart = OPTFACES_R_DEFAULT); DIRECTX_MESH_API HRESULT __cdecl OptimizeFacesLRU( - _In_reads_(nFaces * 3) const uint16_t* indices, _In_ size_t nFaces, + _In_reads_(nFaces * 3) const uint16_t* indices, _In_ size_t nFaces, _In_ size_t nVerts, _Out_writes_(nFaces) uint32_t* faceRemap, _In_ uint32_t lruCacheSize = OPTFACES_LRU_DEFAULT); DIRECTX_MESH_API HRESULT __cdecl OptimizeFacesLRU( - _In_reads_(nFaces * 3) const uint32_t* indices, _In_ size_t nFaces, + _In_reads_(nFaces * 3) const uint32_t* indices, _In_ size_t nFaces, _In_ size_t nVerts, _Out_writes_(nFaces) uint32_t* faceRemap, _In_ uint32_t lruCacheSize = OPTFACES_LRU_DEFAULT); // Reorders faces to increase hit rate of vertex caches DIRECTX_MESH_API HRESULT __cdecl OptimizeFacesEx( - _In_reads_(nFaces * 3) const uint16_t* indices, _In_ size_t nFaces, + _In_reads_(nFaces * 3) const uint16_t* indices, _In_ size_t nFaces, _In_ size_t nVerts, _In_reads_(nFaces * 3) const uint32_t* adjacency, _In_reads_(nFaces) const uint32_t* attributes, _Out_writes_(nFaces) uint32_t* faceRemap, _In_ uint32_t vertexCache = OPTFACES_V_DEFAULT, _In_ uint32_t restart = OPTFACES_R_DEFAULT); DIRECTX_MESH_API HRESULT __cdecl OptimizeFacesEx( - _In_reads_(nFaces * 3) const uint32_t* indices, _In_ size_t nFaces, + _In_reads_(nFaces * 3) const uint32_t* indices, _In_ size_t nFaces, _In_ size_t nVerts, _In_reads_(nFaces * 3) const uint32_t* adjacency, _In_reads_(nFaces) const uint32_t* attributes, _Out_writes_(nFaces) uint32_t* faceRemap, _In_ uint32_t vertexCache = OPTFACES_V_DEFAULT, _In_ uint32_t restart = OPTFACES_R_DEFAULT); DIRECTX_MESH_API HRESULT __cdecl OptimizeFacesLRUEx( - _In_reads_(nFaces * 3) const uint16_t* indices, _In_ size_t nFaces, + _In_reads_(nFaces * 3) const uint16_t* indices, _In_ size_t nFaces, _In_ size_t nVerts, _In_reads_(nFaces) const uint32_t* attributes, _Out_writes_(nFaces) uint32_t* faceRemap, _In_ uint32_t lruCacheSize = OPTFACES_LRU_DEFAULT); DIRECTX_MESH_API HRESULT __cdecl OptimizeFacesLRUEx( - _In_reads_(nFaces * 3) const uint32_t* indices, _In_ size_t nFaces, + _In_reads_(nFaces * 3) const uint32_t* indices, _In_ size_t nFaces, _In_ size_t nVerts, _In_reads_(nFaces) const uint32_t* attributes, _Out_writes_(nFaces) uint32_t* faceRemap, _In_ uint32_t lruCacheSize = OPTFACES_LRU_DEFAULT); @@ -573,7 +573,7 @@ namespace DirectX enum MESHLET_FLAGS : uint32_t { - MESHLET_DEFAULT = 0x0, + MESHLET_DEFAULT = 0, MESHLET_WIND_CW = 0x1, // Vertices are clock-wise (defaults to CCW) diff --git a/DirectXMesh/DirectXMeshOptimizeLRU.cpp b/DirectXMesh/DirectXMeshOptimizeLRU.cpp index 7277b6fa..f32cca35 100644 --- a/DirectXMesh/DirectXMeshOptimizeLRU.cpp +++ b/DirectXMesh/DirectXMeshOptimizeLRU.cpp @@ -194,7 +194,7 @@ namespace template HRESULT OptimizeFacesImpl( - _In_reads_(indexCount) const IndexType* indexList, uint32_t indexCount, + _In_reads_(indexCount) const IndexType* indexList, uint32_t indexCount, size_t nVerts, _Out_writes_(indexCount / 3) uint32_t* faceRemap, uint32_t lruCacheSize, uint32_t offset) { std::unique_ptr[]> vertexDataList(new (std::nothrow) OptimizeVertexData[indexCount]); @@ -245,6 +245,9 @@ namespace continue; } + if (indexList[idx] >= nVerts) + return E_UNEXPECTED; + if (!i || first || sortFunc(indexSorted[i - 1], idx)) { // it's not a duplicate @@ -510,10 +513,11 @@ _Use_decl_annotations_ HRESULT DirectX::OptimizeFacesLRU( const uint16_t* indices, size_t nFaces, + size_t nVerts, uint32_t* faceRemap, uint32_t lruCacheSize) { - if (!indices || !nFaces || !faceRemap) + if (!indices || !nFaces || !nVerts || !faceRemap) return E_INVALIDARG; if (!lruCacheSize || lruCacheSize > kMaxVertexCacheSize) @@ -528,17 +532,18 @@ HRESULT DirectX::OptimizeFacesLRU( std::call_once(s_initOnce, ComputeVertexScores); #endif - return OptimizeFacesImpl(indices, static_cast(nFaces * 3), faceRemap, lruCacheSize, 0); + return OptimizeFacesImpl(indices, static_cast(nFaces * 3), nVerts, faceRemap, lruCacheSize, 0); } _Use_decl_annotations_ HRESULT DirectX::OptimizeFacesLRU( const uint32_t* indices, size_t nFaces, + size_t nVerts, uint32_t* faceRemap, uint32_t lruCacheSize) { - if (!indices || !nFaces || !faceRemap) + if (!indices || !nFaces || !nVerts || !faceRemap) return E_INVALIDARG; if (!lruCacheSize || lruCacheSize > kMaxVertexCacheSize) @@ -553,7 +558,7 @@ HRESULT DirectX::OptimizeFacesLRU( std::call_once(s_initOnce, ComputeVertexScores); #endif - return OptimizeFacesImpl(indices, static_cast(nFaces * 3), faceRemap, lruCacheSize, 0); + return OptimizeFacesImpl(indices, static_cast(nFaces * 3), nVerts, faceRemap, lruCacheSize, 0); } @@ -562,11 +567,12 @@ _Use_decl_annotations_ HRESULT DirectX::OptimizeFacesLRUEx( const uint16_t* indices, size_t nFaces, + size_t nVerts, const uint32_t* attributes, uint32_t* faceRemap, uint32_t lruCacheSize) { - if (!indices || !nFaces || !attributes || !faceRemap) + if (!indices || !nFaces || !nVerts || !attributes || !faceRemap) return E_INVALIDARG; if (!lruCacheSize || lruCacheSize > kMaxVertexCacheSize) @@ -602,7 +608,7 @@ HRESULT DirectX::OptimizeFacesLRUEx( return E_UNEXPECTED; HRESULT hr = OptimizeFacesImpl( - &indices[it.first * 3], static_cast(it.second * 3), + &indices[it.first * 3], static_cast(it.second * 3), nVerts, &faceRemap[it.first], lruCacheSize, uint32_t(it.first)); if (FAILED(hr)) return hr; @@ -615,11 +621,12 @@ _Use_decl_annotations_ HRESULT DirectX::OptimizeFacesLRUEx( const uint32_t* indices, size_t nFaces, + size_t nVerts, const uint32_t* attributes, uint32_t* faceRemap, uint32_t lruCacheSize) { - if (!indices || !nFaces || !attributes || !faceRemap) + if (!indices || !nFaces || !nVerts || !attributes || !faceRemap) return E_INVALIDARG; if (!lruCacheSize || lruCacheSize > kMaxVertexCacheSize) @@ -655,7 +662,7 @@ HRESULT DirectX::OptimizeFacesLRUEx( return E_UNEXPECTED; HRESULT hr = OptimizeFacesImpl( - &indices[it.first * 3], static_cast(it.second * 3), + &indices[it.first * 3], static_cast(it.second * 3), nVerts, &faceRemap[it.first], lruCacheSize, uint32_t(it.first)); if (FAILED(hr)) return hr; diff --git a/DirectXMesh/DirectXMeshOptimizeTVC.cpp b/DirectXMesh/DirectXMeshOptimizeTVC.cpp index ca79e145..8c87e09c 100644 --- a/DirectXMesh/DirectXMeshOptimizeTVC.cpp +++ b/DirectXMesh/DirectXMeshOptimizeTVC.cpp @@ -31,7 +31,9 @@ namespace {} HRESULT initialize( - _In_reads_(nFaces * 3) const index_t* indices, size_t nFaces, + _In_reads_(nFaces * 3) const index_t* indices, + size_t nFaces, + size_t nVerts, _In_reads_(nFaces * 3) const uint32_t* adjacency, const std::vector>& subsets) { @@ -74,7 +76,13 @@ namespace index_t i1 = indices[face * 3 + 1]; index_t i2 = indices[face * 3 + 2]; - if (i0 == index_t(-1) + if ((i0 != index_t(-1) && i0 >= nVerts) + || (i1 != index_t(-1) && i1 >= nVerts) + || (i2 != index_t(-1) && i2 >= nVerts)) + { + return E_UNEXPECTED; + } + else if (i0 == index_t(-1) || i1 == index_t(-1) || i2 == index_t(-1) || i0 == i1 @@ -163,7 +171,7 @@ namespace } HRESULT setSubset( - _In_reads_(nFaces * 3) const index_t* indices, size_t nFaces, + _In_reads_(nFaces * 3) const index_t* indices, size_t nFaces, size_t nVerts, size_t faceOffset, size_t faceCount) noexcept { if (!indices || !mListElements) @@ -202,6 +210,11 @@ namespace continue; } + if (i0 >= nVerts + || i1 >= nVerts + || i2 >= nVerts) + return E_UNEXPECTED; + uint32_t unprocessed = 0; for (uint32_t n = 0; n < 3; ++n) @@ -508,7 +521,7 @@ namespace //--------------------------------------------------------------------------------- template HRESULT StripReorderImpl( - _In_reads_(nFaces * 3) const index_t* indices, _In_ size_t nFaces, + _In_reads_(nFaces * 3) const index_t* indices, _In_ size_t nFaces, _In_ size_t nVerts, _In_reads_(nFaces * 3) const uint32_t* adjacency, _In_reads_opt_(nFaces) const uint32_t* attributes, _Out_writes_(nFaces) uint32_t* faceRemap) @@ -518,7 +531,7 @@ namespace return E_UNEXPECTED; mesh_status status; - HRESULT hr = status.initialize(indices, nFaces, adjacency, subsets); + HRESULT hr = status.initialize(indices, nFaces, nVerts, adjacency, subsets); if (FAILED(hr)) return hr; @@ -530,7 +543,7 @@ namespace for (const auto& it : subsets) { - hr = status.setSubset(indices, nFaces, it.first, it.second); + hr = status.setSubset(indices, nFaces, nVerts, it.first, it.second); if (FAILED(hr)) return hr; @@ -585,7 +598,7 @@ namespace //--------------------------------------------------------------------------------- template HRESULT VertexCacheStripReorderImpl( - _In_reads_(nFaces * 3) const index_t* indices, _In_ size_t nFaces, + _In_reads_(nFaces * 3) const index_t* indices, _In_ size_t nFaces, _In_ size_t nVerts, _In_reads_(nFaces * 3) const uint32_t* adjacency, _In_reads_opt_(nFaces) const uint32_t* attributes, _Out_writes_(nFaces) uint32_t* faceRemap, @@ -596,7 +609,7 @@ namespace return E_UNEXPECTED; mesh_status status; - HRESULT hr = status.initialize(indices, nFaces, adjacency, subsets); + HRESULT hr = status.initialize(indices, nFaces, nVerts, adjacency, subsets); if (FAILED(hr)) return hr; @@ -616,7 +629,7 @@ namespace for (const auto& it : subsets) { - hr = status.setSubset(indices, nFaces, it.first, it.second); + hr = status.setSubset(indices, nFaces, nVerts, it.first, it.second); if (FAILED(hr)) return hr; @@ -771,12 +784,13 @@ _Use_decl_annotations_ HRESULT DirectX::OptimizeFaces( const uint16_t* indices, size_t nFaces, + size_t nVerts, const uint32_t* adjacency, uint32_t* faceRemap, uint32_t vertexCache, uint32_t restart) { - if (!indices || !nFaces || !adjacency || !faceRemap) + if (!indices || !nFaces || !nVerts || !adjacency || !faceRemap) return E_INVALIDARG; if ((uint64_t(nFaces) * 3) >= UINT32_MAX) @@ -784,14 +798,14 @@ HRESULT DirectX::OptimizeFaces( if (vertexCache == OPTFACES_V_STRIPORDER) { - return StripReorderImpl(indices, nFaces, adjacency, nullptr, faceRemap); + return StripReorderImpl(indices, nFaces, nVerts, adjacency, nullptr, faceRemap); } else { if (restart > vertexCache) return E_INVALIDARG; - return VertexCacheStripReorderImpl(indices, nFaces, adjacency, nullptr, faceRemap, vertexCache, restart); + return VertexCacheStripReorderImpl(indices, nFaces, nVerts, adjacency, nullptr, faceRemap, vertexCache, restart); } } @@ -799,12 +813,13 @@ _Use_decl_annotations_ HRESULT DirectX::OptimizeFaces( const uint32_t* indices, size_t nFaces, + size_t nVerts, const uint32_t* adjacency, uint32_t* faceRemap, uint32_t vertexCache, uint32_t restart) { - if (!indices || !nFaces || !adjacency || !faceRemap) + if (!indices || !nFaces || !nVerts || !adjacency || !faceRemap) return E_INVALIDARG; if ((uint64_t(nFaces) * 3) >= UINT32_MAX) @@ -812,14 +827,14 @@ HRESULT DirectX::OptimizeFaces( if (vertexCache == OPTFACES_V_STRIPORDER) { - return StripReorderImpl(indices, nFaces, adjacency, nullptr, faceRemap); + return StripReorderImpl(indices, nFaces, nVerts, adjacency, nullptr, faceRemap); } else { if (restart > vertexCache) return E_INVALIDARG; - return VertexCacheStripReorderImpl(indices, nFaces, adjacency, nullptr, faceRemap, vertexCache, restart); + return VertexCacheStripReorderImpl(indices, nFaces, nVerts, adjacency, nullptr, faceRemap, vertexCache, restart); } } @@ -829,13 +844,14 @@ _Use_decl_annotations_ HRESULT DirectX::OptimizeFacesEx( const uint16_t* indices, size_t nFaces, + size_t nVerts, const uint32_t* adjacency, const uint32_t* attributes, uint32_t* faceRemap, uint32_t vertexCache, uint32_t restart) { - if (!indices || !nFaces || !adjacency || !attributes || !faceRemap) + if (!indices || !nFaces || !nVerts || !adjacency || !attributes || !faceRemap) return E_INVALIDARG; if ((uint64_t(nFaces) * 3) >= UINT32_MAX) @@ -843,14 +859,14 @@ HRESULT DirectX::OptimizeFacesEx( if (vertexCache == OPTFACES_V_STRIPORDER) { - return StripReorderImpl(indices, nFaces, adjacency, attributes, faceRemap); + return StripReorderImpl(indices, nFaces, nVerts, adjacency, attributes, faceRemap); } else { if (restart > vertexCache) return E_INVALIDARG; - return VertexCacheStripReorderImpl(indices, nFaces, adjacency, attributes, faceRemap, vertexCache, restart); + return VertexCacheStripReorderImpl(indices, nFaces, nVerts, adjacency, attributes, faceRemap, vertexCache, restart); } } @@ -858,13 +874,14 @@ _Use_decl_annotations_ HRESULT DirectX::OptimizeFacesEx( const uint32_t* indices, size_t nFaces, + size_t nVerts, const uint32_t* adjacency, const uint32_t* attributes, uint32_t* faceRemap, uint32_t vertexCache, uint32_t restart) { - if (!indices || !nFaces || !adjacency || !attributes || !faceRemap) + if (!indices || !nFaces || !nVerts || !adjacency || !attributes || !faceRemap) return E_INVALIDARG; if ((uint64_t(nFaces) * 3) >= UINT32_MAX) @@ -872,13 +889,13 @@ HRESULT DirectX::OptimizeFacesEx( if (vertexCache == OPTFACES_V_STRIPORDER) { - return StripReorderImpl(indices, nFaces, adjacency, attributes, faceRemap); + return StripReorderImpl(indices, nFaces, nVerts, adjacency, attributes, faceRemap); } else { if (restart > vertexCache) return E_INVALIDARG; - return VertexCacheStripReorderImpl(indices, nFaces, adjacency, attributes, faceRemap, vertexCache, restart); + return VertexCacheStripReorderImpl(indices, nFaces, nVerts, adjacency, attributes, faceRemap, vertexCache, restart); } } diff --git a/DirectXMesh/scoped.h b/DirectXMesh/scoped.h index 363c393f..ee38a70c 100644 --- a/DirectXMesh/scoped.h +++ b/DirectXMesh/scoped.h @@ -5,6 +5,9 @@ // // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +// +// https://go.microsoft.com/fwlink/?LinkId=248926 +// https://go.microsoft.com/fwlink/?LinkID=324981 //------------------------------------------------------------------------------------- #pragma once diff --git a/Meshconvert/Mesh.cpp b/Meshconvert/Mesh.cpp index f5c391d7..59574885 100644 --- a/Meshconvert/Mesh.cpp +++ b/Meshconvert/Mesh.cpp @@ -654,20 +654,20 @@ HRESULT Mesh::Optimize(bool lru) noexcept // Optimize faces for pre-transform vertex cache if (lru) { - hr = OptimizeFacesLRUEx(mIndices.get(), mnFaces, mAttributes.get(), remap.get()); + hr = OptimizeFacesLRUEx(mIndices.get(), mnFaces, mnVerts, mAttributes.get(), remap.get()); } else { - hr = OptimizeFacesEx(mIndices.get(), mnFaces, mAdjacency.get(), mAttributes.get(), remap.get()); + hr = OptimizeFacesEx(mIndices.get(), mnFaces, mnVerts, mAdjacency.get(), mAttributes.get(), remap.get()); } } else if (lru) { - hr = OptimizeFacesLRU(mIndices.get(), mnFaces, remap.get()); + hr = OptimizeFacesLRU(mIndices.get(), mnFaces, mnVerts, remap.get()); } else { - hr = OptimizeFaces(mIndices.get(), mnFaces, mAdjacency.get(), remap.get()); + hr = OptimizeFaces(mIndices.get(), mnFaces, mnVerts, mAdjacency.get(), remap.get()); } if (FAILED(hr)) return hr; diff --git a/README.md b/README.md index f6ea88bb..fc416d8c 100644 --- a/README.md +++ b/README.md @@ -56,9 +56,21 @@ For a full change history, see [CHANGELOG.md](https://github.com/microsoft/Direc * The CMake projects require 3.21 or later. +* Starting with the April 2026 release, the OptimizeFaces(LRU)(Ex) functions now take an `nVerts` parameter in order to validate the input indices. This is a *breaking change*. + +```cpp +hr = OptimizeFaces(indices, nFaces, adjacency, faceRemap, vertexCache, restart); +``` + +Must be updated to: + +```cpp +hr = OptimizeFaces(indices, nFaces, nVerts, adjacency, faceRemap, vertexCache, restart); +``` + * Starting with the March 2025 release, Windows 7 and Windows 8.0 support has been retired. -* Starting with the June 2020 release, this library makes use of typed enum bitmask flags per the recommendation of the _C++ Standard_ section _17.5.2.1.3 Bitmask types_. This is consistent with Direct3D 12's use of the ``DEFINE_ENUM_FLAG_OPERATORS`` macro. This may have _breaking change_ impacts to client code: +* Starting with the June 2020 release, this library makes use of typed enum bitmask flags per the recommendation of the *C++ Standard* section *17.5.2.1.3 Bitmask types*. This is consistent with Direct3D 12's use of the ``DEFINE_ENUM_FLAG_OPERATORS`` macro. This may have *breaking change* impacts to client code: * You cannot pass the ``0`` literal as your flags value. Instead you must make use of the appropriate default enum value: ``CNORM_DEFAULT``, ``VALIDATE_DEFAULT``, or ``MESHLET_DEFAULT``. @@ -83,7 +95,7 @@ For a full change history, see [CHANGELOG.md](https://github.com/microsoft/Direc ## Support -For questions, consider using [Stack Overflow](https://stackoverflow.com/questions/tagged/directxtk) with the _directxtk_ tag, or the [DirectX Discord Server](https://discord.gg/directx) in the _dx12-developers_ or _dx9-dx11-developers_ channel. +For questions, consider using [Stack Overflow](https://stackoverflow.com/questions/tagged/directxtk) with the *directxtk* tag, or the [DirectX Discord Server](https://discord.gg/directx) in the *dx12-developers* or *dx9-dx11-developers* channel. For bug reports and feature requests, please use GitHub [issues](https://github.com/microsoft/DirectXMesh/issues) for this project.