From c0593740dcbbea6f3393d94fe5eac556b068e59a Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Mon, 12 Oct 2026 00:08:50 +0100 Subject: [PATCH 1/2] Android: collision and fast_block_compress build and run; denoised_rtao Windows only The full Android build stopped in three ports: - fast_block_compress: fbc_cpu.cpp's SSE2 constants used MSVC's __declspec(align(16)), now alignas(16); its C fallback (arm64) used memcpy_s, which fbc_cpu_compat.h now provides off Windows. The offline BC images' names are .DDS for all four images (opaque_512's own is .dds): Android's file names are case-sensitive, so they have their own extension list. - collision: DirectXMath (the Windows SDK's) is fetched on Android (DIRECTXMATH_FETCH_TAG, jun2026, headers only), with core/sal/sal.h for the annotations it uses. - denoised_rtao: Windows only. It loads textures through WIC and the PBRT parser takes its definitions from windows.h. An APK now also depends on what its executable was made to depend on after it was added (the copies of the samples' media): tools/android.py build builds APKs only, so fast_block_compress_apk failed when the images hadn't been copied. Co-Authored-By: Claude Opus 5.5 --- CMakeLists.txt | 64 +++++++++++++++++++++++---------- core/fbc_cpu.cpp | 48 ++++++++++++------------- core/fbc_cpu_compat.h | 13 ++++++- core/sal/sal.h | 16 +++++++++ examples/fast_block_compress.ts | 4 ++- 5 files changed, 101 insertions(+), 44 deletions(-) create mode 100644 core/sal/sal.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 452f605..d062404 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -549,6 +549,18 @@ function(add_tslang_android_app name media) if (TARGET donut_shaders) add_dependencies(${name}_apk donut_shaders) endif() + # What the executable is made to depend on after this (the copies of the media of the samples' + # checkouts: _media and the like), the APK packages too, and `tools/android.py build` + # builds APKs, not executables: taken at the end of the file, when all of it is known. (A + # deferred call's arguments are evaluated when it runs, hence the EVAL.) + cmake_language(EVAL CODE "cmake_language(DEFER CALL add_tslang_apk_dependencies [[${name}]])") +endfunction() + +function(add_tslang_apk_dependencies name) + get_target_property(dependencies ${name} MANUALLY_ADDED_DEPENDENCIES) + if (dependencies) + add_dependencies(${name}_apk ${dependencies}) + endif() endfunction() # The files the examples' .ts files compile in, which their objects have to be rebuilt after: a @@ -2639,6 +2651,19 @@ donut_compile_shaders_all_platforms( SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/shaders/collision.hlsl OUTPUT_BASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/shaders/collision ) +# DirectXMath is the Windows SDK's; on Android it is fetched at configure time (DIRECTXMATH_FETCH_TAG; +# headers only, not its CMake project), with core/sal/sal.h for the annotations it uses. +if (ANDROID) + set(DIRECTXMATH_FETCH_TAG "jun2026" CACHE STRING "Git tag of DirectXMath, which collision uses on Android") + FetchContent_Declare(directxmath + GIT_REPOSITORY https://github.com/microsoft/DirectXMath.git + GIT_TAG ${DIRECTXMATH_FETCH_TAG} + GIT_SHALLOW TRUE + SOURCE_SUBDIR Inc) + FetchContent_MakeAvailable(directxmath) + set_source_files_properties(core/directx_collision.cpp PROPERTIES + INCLUDE_DIRECTORIES "${directxmath_SOURCE_DIR}/Inc;${CMAKE_CURRENT_SOURCE_DIR}/core/sal") +endif() add_tslang_example(collision core/directx_collision.cpp MEDIA fonts) # Port of NVIDIA's DXR tutorial 14 (Refit, github.com/NVIDIAGameWorks/DxrTutorials): three @@ -2901,24 +2926,27 @@ endfunction() # over a scene of PBRT models (a house, cars, a dragon, a spaceship: the sample's parser, in # core/pbrt) and procedural grass a compute shader regenerates every frame; a specular path tracer # makes the G-buffer. Its assets (~140 MB) are the sample's, from DIRECTX_GRAPHICS_SAMPLES_DIR. -# Ray tracing pipelines: D3D12 or Vulkan. -file(GLOB DENOISED_RTAO_SHADER_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/shaders/denoised_rtao/*) -donut_compile_shaders( - TARGET denoised_rtao_shaders - PROJECT_NAME "Denoised RTAO" - CONFIG ${CMAKE_CURRENT_SOURCE_DIR}/shaders/denoised_rtao.cfg - SOURCES ${DENOISED_RTAO_SHADER_SOURCES} - DXIL ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/shaders/denoised_rtao/dxil - SPIRV_DXC ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/shaders/denoised_rtao/spirv - SHADERMAKE_OPTIONS_SPIRV "--vulkanMemoryLayout dx" -) -add_tslang_example(denoised_rtao core/directx_math.ts core/pbrt_scene.ts core/denoised_rtao.cpp core/pbrt/PBRTParser.cpp core/pbrt/PlyParser.cpp - MEDIA denoised_rtao) -add_dxgs_media_directory(denoised_rtao denoised_rtao - Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingRealTimeDenoisedAmbientOcclusion/Assets) -add_tslang_example_test(denoised_rtao) -add_tslang_example_test(denoised_rtao SUFFIX raw ARGS -raw) -add_tslang_example_test(denoised_rtao SUFFIX shaded ARGS -shaded) +# Ray tracing pipelines: D3D12 or Vulkan. Windows only: the textures are loaded through WIC, and the +# PBRT parser takes its definitions from windows.h. +if (WIN32) + file(GLOB DENOISED_RTAO_SHADER_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/shaders/denoised_rtao/*) + donut_compile_shaders( + TARGET denoised_rtao_shaders + PROJECT_NAME "Denoised RTAO" + CONFIG ${CMAKE_CURRENT_SOURCE_DIR}/shaders/denoised_rtao.cfg + SOURCES ${DENOISED_RTAO_SHADER_SOURCES} + DXIL ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/shaders/denoised_rtao/dxil + SPIRV_DXC ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/shaders/denoised_rtao/spirv + SHADERMAKE_OPTIONS_SPIRV "--vulkanMemoryLayout dx" + ) + add_tslang_example(denoised_rtao core/directx_math.ts core/pbrt_scene.ts core/denoised_rtao.cpp core/pbrt/PBRTParser.cpp core/pbrt/PlyParser.cpp + MEDIA denoised_rtao) + add_dxgs_media_directory(denoised_rtao denoised_rtao + Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingRealTimeDenoisedAmbientOcclusion/Assets) + add_tslang_example_test(denoised_rtao) + add_tslang_example_test(denoised_rtao SUFFIX raw ARGS -raw) + add_tslang_example_test(denoised_rtao SUFFIX shaded ARGS -shaded) +endif() # Port of DirectX-Graphics-Samples' D3D12Multithreading: a room of 1025 draws lit by three moving # spotlights, the first casting shadows, its shadow pass and scene pass recorded by three worker diff --git a/core/fbc_cpu.cpp b/core/fbc_cpu.cpp index 9deea15..9c5fd80 100644 --- a/core/fbc_cpu.cpp +++ b/core/fbc_cpu.cpp @@ -134,10 +134,10 @@ namespace #define INSET_BC5_BBOX() \ static const int INSET_ALPHA_SHIFT = 5; \ - __declspec(align(16)) static const uint16_t s_insetNormal3DcRound[8] = { ((1<<(INSET_ALPHA_SHIFT-1))-1), ((1<<(INSET_ALPHA_SHIFT-1))-1), 0, 0, 0, 0, 0, 0 }; \ - __declspec(align(16)) static const uint16_t s_insetNormal3DcMask[8] = { 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }; \ - __declspec(align(16)) static const uint16_t s_insetNormal3DcShiftUp[8] = { 1 << INSET_ALPHA_SHIFT, 1 << INSET_ALPHA_SHIFT, 1, 1, 1, 1, 1, 1 }; \ - __declspec(align(16)) static const uint16_t s_insetNormal3DcShiftDown[8] = { 1 << (16 - INSET_ALPHA_SHIFT), 1 << (16 - INSET_ALPHA_SHIFT), 0, 0, 0, 0, 0, 0 }; \ + alignas(16) static const uint16_t s_insetNormal3DcRound[8] = { ((1<<(INSET_ALPHA_SHIFT-1))-1), ((1<<(INSET_ALPHA_SHIFT-1))-1), 0, 0, 0, 0, 0, 0 }; \ + alignas(16) static const uint16_t s_insetNormal3DcMask[8] = { 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }; \ + alignas(16) static const uint16_t s_insetNormal3DcShiftUp[8] = { 1 << INSET_ALPHA_SHIFT, 1 << INSET_ALPHA_SHIFT, 1, 1, 1, 1, 1, 1 }; \ + alignas(16) static const uint16_t s_insetNormal3DcShiftDown[8] = { 1 << (16 - INSET_ALPHA_SHIFT), 1 << (16 - INSET_ALPHA_SHIFT), 0, 0, 0, 0, 0, 0 }; \ \ const __m128i zero = _mm_setzero_si128(); \ minColor = _mm_unpacklo_epi8( minColor, zero ); \ @@ -163,10 +163,10 @@ namespace maxColor = _mm_packus_epi16( maxColor, maxColor ); #define EMIT_COLOR_INDICES() \ - __declspec(align(16)) static const uint8_t s_colorMask[16] = { 0xF8, 0xFC, 0xF8, 0, 0, 0, 0, 0, 0xF8, 0xFC, 0xF8, 0, 0, 0, 0, 0 }; \ - __declspec(align(16)) static const uint16_t s_word_div3[8] = { (1<<16)/3+1, (1<<16)/3+1, (1<<16)/3+1, (1<<16)/3+1, (1<<16)/3+1, (1<<16)/3+1, (1<<16)/3+1, (1<<16)/3+1 }; \ - __declspec(align(16)) static const uint16_t s_word_1[8] = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 }; \ - __declspec(align(16)) static const uint16_t s_word_2[8] = { 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2 }; \ + alignas(16) static const uint8_t s_colorMask[16] = { 0xF8, 0xFC, 0xF8, 0, 0, 0, 0, 0, 0xF8, 0xFC, 0xF8, 0, 0, 0, 0, 0 }; \ + alignas(16) static const uint16_t s_word_div3[8] = { (1<<16)/3+1, (1<<16)/3+1, (1<<16)/3+1, (1<<16)/3+1, (1<<16)/3+1, (1<<16)/3+1, (1<<16)/3+1, (1<<16)/3+1 }; \ + alignas(16) static const uint16_t s_word_1[8] = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 }; \ + alignas(16) static const uint16_t s_word_2[8] = { 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2 }; \ \ maxColor = _mm_and_si128( maxColor, reinterpret_cast(s_colorMask)[0] ); \ maxColor = _mm_unpacklo_epi8( maxColor, zero ); \ @@ -365,21 +365,21 @@ namespace result = _mm_or_si128(result, t2); #define EMIT_ALPHA_INDICES_VARS() \ - __declspec(align(16)) static const uint8_t s_byte_1[16] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; \ - __declspec(align(16)) static const uint8_t s_byte_2[16] = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 }; \ - __declspec(align(16)) static const uint8_t s_byte_7[16] = { 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07 }; \ - __declspec(align(16)) static const uint16_t s_word_div7[8] = { (1<<16)/7+1, (1<<16)/7+1, (1<<16)/7+1, (1<<16)/7+1, (1<<16)/7+1, (1<<16)/7+1, (1<<16)/7+1, (1<<16)/7+1 }; \ - __declspec(align(16)) static const uint16_t s_word_div14[8] = { (1<<16)/14+1, (1<<16)/14+1, (1<<16)/14+1, (1<<16)/14+1, (1<<16)/14+1, (1<<16)/14+1, (1<<16)/14+1, (1<<16)/14+1 }; \ - __declspec(align(16)) static const uint16_t s_word_scaleA[8] = { 6, 6, 5, 5, 4, 4, 0, 0 }; \ - __declspec(align(16)) static const uint16_t s_word_scaleB[8] = { 1, 1, 2, 2, 3, 3, 0, 0 }; \ - __declspec(align(16)) static const uint32_t s_alphaMask0[4] = { 7<<0, 0, 7<<0, 0 }; \ - __declspec(align(16)) static const uint32_t s_alphaMask1[4] = { 7<<3, 0, 7<<3, 0 }; \ - __declspec(align(16)) static const uint32_t s_alphaMask2[4] = { 7<<6, 0, 7<<6, 0 }; \ - __declspec(align(16)) static const uint32_t s_alphaMask3[4] = { 7<<9, 0, 7<<9, 0 }; \ - __declspec(align(16)) static const uint32_t s_alphaMask4[4] = { 7<<12, 0, 7<<12, 0 }; \ - __declspec(align(16)) static const uint32_t s_alphaMask5[4] = { 7<<15, 0, 7<<15, 0 }; \ - __declspec(align(16)) static const uint32_t s_alphaMask6[4] = { 7<<18, 0, 7<<18, 0 }; \ - __declspec(align(16)) static const uint32_t s_alphaMask7[4] = { 7<<21, 0, 7<<21, 0 }; \ + alignas(16) static const uint8_t s_byte_1[16] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; \ + alignas(16) static const uint8_t s_byte_2[16] = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 }; \ + alignas(16) static const uint8_t s_byte_7[16] = { 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07 }; \ + alignas(16) static const uint16_t s_word_div7[8] = { (1<<16)/7+1, (1<<16)/7+1, (1<<16)/7+1, (1<<16)/7+1, (1<<16)/7+1, (1<<16)/7+1, (1<<16)/7+1, (1<<16)/7+1 }; \ + alignas(16) static const uint16_t s_word_div14[8] = { (1<<16)/14+1, (1<<16)/14+1, (1<<16)/14+1, (1<<16)/14+1, (1<<16)/14+1, (1<<16)/14+1, (1<<16)/14+1, (1<<16)/14+1 }; \ + alignas(16) static const uint16_t s_word_scaleA[8] = { 6, 6, 5, 5, 4, 4, 0, 0 }; \ + alignas(16) static const uint16_t s_word_scaleB[8] = { 1, 1, 2, 2, 3, 3, 0, 0 }; \ + alignas(16) static const uint32_t s_alphaMask0[4] = { 7<<0, 0, 7<<0, 0 }; \ + alignas(16) static const uint32_t s_alphaMask1[4] = { 7<<3, 0, 7<<3, 0 }; \ + alignas(16) static const uint32_t s_alphaMask2[4] = { 7<<6, 0, 7<<6, 0 }; \ + alignas(16) static const uint32_t s_alphaMask3[4] = { 7<<9, 0, 7<<9, 0 }; \ + alignas(16) static const uint32_t s_alphaMask4[4] = { 7<<12, 0, 7<<12, 0 }; \ + alignas(16) static const uint32_t s_alphaMask5[4] = { 7<<15, 0, 7<<15, 0 }; \ + alignas(16) static const uint32_t s_alphaMask6[4] = { 7<<18, 0, 7<<18, 0 }; \ + alignas(16) static const uint32_t s_alphaMask7[4] = { 7<<21, 0, 7<<21, 0 }; \ __m128i maxa, mina, mid, mid_div_14, ab1, ab2, ab3, ab4, ab5, ab6, ab7, resulta; #define EMIT_ALPHA_INDICES(alpha) \ @@ -592,7 +592,7 @@ namespace void CompressBC5U(const Image& src, Image& dst) { - __declspec(align(16)) static const uint32_t s_mask[4] = { 0xff, 0xff, 0xff, 0xff }; + alignas(16) static const uint32_t s_mask[4] = { 0xff, 0xff, 0xff, 0xff }; const uint8_t* pSource = src.pixels; uint8_t* pDestination = dst.pixels; diff --git a/core/fbc_cpu_compat.h b/core/fbc_cpu_compat.h index e50c241..219cc64 100644 --- a/core/fbc_cpu_compat.h +++ b/core/fbc_cpu_compat.h @@ -1,7 +1,7 @@ // What the Xbox ATG FastBlockCompress sample's CPU compressor (fbc_cpu.cpp, fbc_cpu.h) took from the // sample's precompiled header: the few DXGI / D3D12 / COM definitions it uses, with the values of // the Windows SDK, without Windows headers (the example builds for Android too), plus aligned -// allocation and SSE2. +// allocation, SSE2, and (the compressor without SSE2: ARM) memcpy_s. #pragma once @@ -79,6 +79,17 @@ inline void FbcAlignedFree(void* p) #endif } +#ifndef _WIN32 +// The CRT's bounds-checked copy, which the compressor's C fallback uses. +inline int memcpy_s(void* dest, size_t destSize, const void* src, size_t count) +{ + if (count > destSize) + return 34; // ERANGE + memcpy(dest, src, count); + return 0; +} +#endif + #define _aligned_malloc FbcAlignedMalloc struct aligned_deleter { void operator()(void* p) { FbcAlignedFree(p); } }; diff --git a/core/sal/sal.h b/core/sal/sal.h new file mode 100644 index 0000000..6141572 --- /dev/null +++ b/core/sal/sal.h @@ -0,0 +1,16 @@ +// The Microsoft source-code annotations DirectXMath and DirectXCollision use, as nothing: DirectXMath +// includes "sal.h", which only the Windows SDK has. Its README asks non-Windows builds to provide +// one; this is on core/directx_collision.cpp's include path on Android only (see CMakeLists.txt). + +#pragma once + +#define _Analysis_assume_(expr) +#define _In_ +#define _In_reads_(size) +#define _In_reads_bytes_(size) +#define _Out_ +#define _Out_opt_ +#define _Out_writes_(size) +#define _Out_writes_bytes_(size) +#define _Success_(expr) +#define _Use_decl_annotations_ diff --git a/examples/fast_block_compress.ts b/examples/fast_block_compress.ts index f82350a..27c0599 100644 --- a/examples/fast_block_compress.ts +++ b/examples/fast_block_compress.ts @@ -12,7 +12,9 @@ namespace FastBlockCompress { // interior), alpha (1024x1024 RGBA: the recycle bin and book with alpha), normal (1024x1024 RG: // normal map texture). Each comes with offline BC and BC7 versions, named after it. const IMAGE_NAMES = ["opaque_1024", "opaque_512", "alpha", "normal"]; + // Their files' extensions, as the sample has them (Android's file names are case-sensitive). const IMAGE_EXTENSIONS = [".DDS", ".dds", ".DDS", ".dds"]; + const IMAGE_OFFLINE_EXTENSIONS = [".DDS", ".DDS", ".DDS", ".DDS"]; const IMAGE_BC7_EXTENSIONS = [".dds", ".dds", ".dds", ".dds"]; const IMAGE_FORMATS = [Format.BC1_UNORM, Format.BC1_UNORM, Format.BC3_UNORM, Format.BC5_UNORM]; @@ -512,7 +514,7 @@ namespace FastBlockCompress { } const bcName = formatName(image.format).toLowerCase(); - const offlineBC = this.app.loadTexture(commandList, MEDIA_DIR + name + "_offline_" + bcName + IMAGE_EXTENSIONS[index], 0); + const offlineBC = this.app.loadTexture(commandList, MEDIA_DIR + name + "_offline_" + bcName + IMAGE_OFFLINE_EXTENSIONS[index], 0); const offlineBC7 = this.app.loadTexture(commandList, MEDIA_DIR + name + "_offline_bc7" + IMAGE_BC7_EXTENSIONS[index], 0); if (!offlineBC || !offlineBC7) { return null; From bff077ff595f2b4e8f4e97ae99b72a7354e29b60 Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Mon, 12 Oct 2026 00:08:50 +0100 Subject: [PATCH 2/2] .gitattributes: patches checked out with LF With core.autocrlf, a checkout gives a patch CRLF line endings; git apply then calls Donut-message-callback-all-apis.patch corrupt (its empty context line), and configure skipped it with a warning: a fresh clone on Windows built Donut without it. Co-Authored-By: Claude Opus 5.5 --- .gitattributes | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..241004e --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# git apply needs the patches as they are stored (LF): with core.autocrlf, a checkout would +# give them CRLF, and one with an empty context line is then "corrupt" and skipped. +*.patch text eol=lf