From adcf7612434d573085f97b6bbd002640cf0a3043 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 26 Jan 2025 19:03:33 +0300 Subject: [PATCH 001/141] Support dynamic size in math::vector --- .../math/include/psemek/math/detail/array.hpp | 9 ++ libs/math/include/psemek/math/dynamic.hpp | 27 +++++ libs/math/include/psemek/math/vector.hpp | 110 ++++++++++++------ libs/math/tests/vector.cpp | 38 ++++++ 4 files changed, 149 insertions(+), 35 deletions(-) create mode 100644 libs/math/include/psemek/math/dynamic.hpp diff --git a/libs/math/include/psemek/math/detail/array.hpp b/libs/math/include/psemek/math/detail/array.hpp index db36bbcb..ca6e2e79 100644 --- a/libs/math/include/psemek/math/detail/array.hpp +++ b/libs/math/include/psemek/math/detail/array.hpp @@ -1,7 +1,10 @@ #pragma once +#include #include +#include + namespace psemek::math::detail { @@ -31,6 +34,12 @@ namespace psemek::math::detail }; }; + template + struct array + { + using type = std::vector; + }; + template struct all_convertible_to; diff --git a/libs/math/include/psemek/math/dynamic.hpp b/libs/math/include/psemek/math/dynamic.hpp new file mode 100644 index 00000000..1a18b928 --- /dev/null +++ b/libs/math/include/psemek/math/dynamic.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include +#include + +namespace psemek::math +{ + + constexpr std::size_t dynamic = static_cast(-1); + + struct dynamic_size_mismatch + : util::exception + { + dynamic_size_mismatch(std::size_t size1, std::size_t size2, util::stacktrace stacktrace = {}) + : util::exception(std::format("Dynamic array size mismatch: {} != {}", size1, size2), std::move(stacktrace)) + {} + }; + + inline void check_dynamic_size(std::size_t size1, std::size_t size2) + { + if (size1 != size2) + throw dynamic_size_mismatch(size1, size2); + } + +} diff --git a/libs/math/include/psemek/math/vector.hpp b/libs/math/include/psemek/math/vector.hpp index 5573b748..a3c79def 100644 --- a/libs/math/include/psemek/math/vector.hpp +++ b/libs/math/include/psemek/math/vector.hpp @@ -2,12 +2,12 @@ #include #include +#include #include #include #include #include -#include #include #include @@ -24,15 +24,31 @@ namespace psemek::math vector() = default; + explicit vector(std::size_t size) requires (N != dynamic) + { + assert(size == N); + } + + explicit vector(std::size_t size) requires (N == dynamic) + : coords(size, T{}) + {} + template - requires((sizeof...(Args) == N) && detail::all_convertible_to::value) + requires((sizeof...(Args) == N || N == dynamic) && detail::all_convertible_to::value) vector(Args && ... args) : coords{ static_cast(std::forward(args))... } {} std::size_t dimension() const { - return N; + if constexpr (N == dynamic) + { + return coords.size(); + } + else + { + return N; + } } T & operator[](std::size_t i) @@ -51,7 +67,8 @@ namespace psemek::math vector & operator += (vector const & v); vector & operator -= (vector const & v); - static vector zero(); + static vector zero() requires (N != dynamic); + static vector zero(std::size_t size); }; template @@ -60,8 +77,8 @@ namespace psemek::math template vector cast(vector const & v) { - vector r; - for (std::size_t i = 0; i < N; ++i) + vector r(v.dimension()); + for (std::size_t i = 0; i < v.dimension(); ++i) r[i] = static_cast(v[i]); return r; } @@ -69,7 +86,9 @@ namespace psemek::math template std::strong_ordering operator <=> (vector const & v1, vector const & v2) { - for (std::size_t i = 0; i < N; ++i) + check_dynamic_size(v1.dimension(), v2.dimension()); + + for (std::size_t i = 0; i < v1.dimension(); ++i) { if (v1[i] < v2[i]) return std::strong_ordering::less; @@ -118,8 +137,8 @@ namespace psemek::math template vector operator * (vector const & v, T const & s) { - vector r; - for (std::size_t i = 0; i < N; ++i) + vector r(v.dimension()); + for (std::size_t i = 0; i < v.dimension(); ++i) r[i] = v[i] * s; return r; } @@ -127,8 +146,8 @@ namespace psemek::math template vector operator * (T const & s, vector const & v) { - vector r; - for (std::size_t i = 0; i < N; ++i) + vector r(v.dimension()); + for (std::size_t i = 0; i < v.dimension(); ++i) r[i] = s * v[i]; return r; } @@ -136,8 +155,8 @@ namespace psemek::math template vector operator / (vector const & v, T const & s) { - vector r; - for (std::size_t i = 0; i < N; ++i) + vector r(v.dimension()); + for (std::size_t i = 0; i < v.dimension(); ++i) r[i] = v[i] / s; return r; } @@ -145,8 +164,8 @@ namespace psemek::math template vector operator - (vector const & v) { - vector r; - for (std::size_t i = 0; i < N; ++i) + vector r(v.dimension()); + for (std::size_t i = 0; i < v.dimension(); ++i) r[i] = -v[i]; return r; } @@ -154,8 +173,10 @@ namespace psemek::math template vector operator + (vector const & v1, vector const & v2) { - vector r; - for (std::size_t i = 0; i < N; ++i) + check_dynamic_size(v1.dimension(), v2.dimension()); + + vector r(v1.dimension()); + for (std::size_t i = 0; i < v1.dimension(); ++i) r[i] = v1[i] + v2[i]; return r; } @@ -163,8 +184,10 @@ namespace psemek::math template vector operator - (vector const & v1, vector const & v2) { - vector r; - for (std::size_t i = 0; i < N; ++i) + check_dynamic_size(v1.dimension(), v2.dimension()); + + vector r(v1.dimension()); + for (std::size_t i = 0; i < v1.dimension(); ++i) r[i] = v1[i] - v2[i]; return r; } @@ -194,7 +217,7 @@ namespace psemek::math } template - vector vector::zero() + vector vector::zero() requires (N != dynamic) { vector r; for (std::size_t i = 0; i < N; ++i) @@ -202,11 +225,27 @@ namespace psemek::math return r; } + template + vector vector::zero(std::size_t size) + { + if constexpr (N != dynamic) + { + assert(N == size); + } + + vector r(size); + for (std::size_t i = 0; i < size; ++i) + r[i] = T(0); + return r; + } + template T dot(vector const & v1, vector const & v2) { + check_dynamic_size(v1.dimension(), v2.dimension()); + T r{}; - for (std::size_t i = 0; i < N; ++i) + for (std::size_t i = 0; i < v1.dimension(); ++i) r += v1[i] * v2[i]; return r; } @@ -223,7 +262,7 @@ namespace psemek::math using std::abs; using std::max; T r = abs(v[0]); - for (std::size_t i = 1; i < N; ++i) + for (std::size_t i = 1; i < v.dimension(); ++i) r = max(r, abs(v[i])); return r; } @@ -279,10 +318,10 @@ namespace psemek::math template vector ort(vector const & v) { - vector result; + vector result = vector::zero(v.dimension()); if constexpr ((N % 2) == 0) { - for (std::size_t i = 0; i < N; i += 2) + for (std::size_t i = 0; i < v.dimension(); i += 2) { result[i] = -v[i + 1]; result[i + 1] = v[i]; @@ -294,7 +333,7 @@ namespace psemek::math using std::abs; - for (std::size_t k = 1; k < N; ++k) + for (std::size_t k = 1; k < v.dimension(); ++k) { if (abs(v[k]) > abs(v[i])) { @@ -303,7 +342,6 @@ namespace psemek::math } } - result = vector::zero(); result[i] = -v[j]; result[j] = v[i]; } @@ -357,14 +395,16 @@ namespace psemek::math return {v[0] * c - v[1] * s, v[0] * s + v[1] * c}; } - template - auto pointwise(F && f, vector const & ... vs) + template + auto pointwise(F && f, vector const & v0, vector const & ... vs) { - using R = decltype(f(vs[0]...)); + using R = decltype(f(v0[0], vs[0]...)); - vector result; - for (std::size_t i = 0; i < N; ++i) - result[i] = f(vs[i]...); + (check_dynamic_size(v0.dimension(), vs.dimension()), ...); + + vector result(v0.dimension()); + for (std::size_t i = 0; i < v0.dimension(); ++i) + result[i] = f(v0[i], vs[i]...); return result; } @@ -448,7 +488,7 @@ namespace psemek::math } os << '(' << v[0]; - for (std::size_t i = 1; i < N; ++i) + for (std::size_t i = 1; i < v.dimension(); ++i) os << ", " << v[i]; os << ')'; return os; @@ -458,7 +498,7 @@ namespace psemek::math bool isfinite(vector const & v) { using std::isfinite; - for (std::size_t i = 0; i < N; ++i) + for (std::size_t i = 0; i < v.dimension(); ++i) if (!isfinite(v[i])) return false; return true; @@ -476,7 +516,7 @@ namespace std { std::hash h; std::uint64_t r = 0; - for (std::size_t i = 0; i < N; ++i) + for (std::size_t i = 0; i < v.dimension(); ++i) ::psemek::util::hash_combine(r, h(v[i])); return r; } diff --git a/libs/math/tests/vector.cpp b/libs/math/tests/vector.cpp index a34d8c30..bcbfe53e 100644 --- a/libs/math/tests/vector.cpp +++ b/libs/math/tests/vector.cpp @@ -34,6 +34,24 @@ test_case(math_vector_init) expect_equal(pf[2], 5.f); } +test_case(math_vector_dynamic) +{ + vector pi1(std::size_t(15)); + vector pi2{1, 2, 3, 4, 5}; + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + expect_equal(pi1.dimension(), 15); + expect_equal(pi2.dimension(), 5); + + expect_equal(pi2[0], 1); + expect_equal(pi2[1], 2); + expect_equal(pi2[2], 3); + expect_equal(pi2[3], 4); + expect_equal(pi2[4], 5); +} + test_case(math_vector_deduce) { vector pi{1, 2}; @@ -70,6 +88,16 @@ test_case(math_vector_arithmetic) expect_equal(vf1 + vf2, (vector{13.f, 15.f, 17.f})); expect_equal(vf1 - vf2, (vector{-3.f, -3.f, -3.f})); expect_equal(2.f * vf1 - 3.f * vf2, (vector{-14.f, -15.f, -16.f})); + + vector vd1{-1.f, -2.f, -3.f}; + vector vd2{-4.f, -5.f, -6.f}; + + expect_equal(-vd1, (vector{1.f, 2.f, 3.f})); + expect_equal(5.f * vd1, (vector{-5.f, -10.f, -15.f})); + expect_equal(vd1 * 5.f, (vector{-5.f, -10.f, -15.f})); + expect_equal(vd1 + vd2, (vector{-5.f, -7.f, -9.f})); + expect_equal(vd1 - vd2, (vector{3.f, 3.f, 3.f})); + expect_equal(2.f * vd1 - 3.f * vd2, (vector{10.f, 11.f, 12.f})); } test_case(math_vector_dot) @@ -83,6 +111,11 @@ test_case(math_vector_dot) vector vf2{8.f, 9.f, 10.f}; expect_equal((dot(vf1, vf2)), 164.f); + + vector vd1{-1.f, -2.f, -3.f}; + vector vd2{-4.f, -5.f, -6.f}; + + expect_equal((dot(vd1, vd2)), 32.f); } test_case(math_vector_det) @@ -118,18 +151,22 @@ test_case(math_vector_cast) { vector pi{1, 2}; vector pf{3.f, 4.f, 5.f}; + vector pd{-1.f, -2.f, -3.f}; expect_equal(cast(pi), (vector{1.f, 2.f})); expect_equal(cast(pf), (vector{3, 4, 5})); + expect_equal(cast(pd), (vector{-1, -2, -3})); } test_case(math_vector_ort) { vector v1{1.f, 2.f}; vector v2{3.f, 4.f, 5.f}; + vector v3{-1.f, -2.f, -3.f}; expect_small((dot(v1, ort(v1))), 1e-6); expect_small((dot(v2, ort(v2))), 1e-6); + expect_small((dot(v3, ort(v3))), 1e-6); } test_case(math_vector_cross) @@ -169,6 +206,7 @@ test_case(math_vector_slerp) // 10-degree steps auto s = slerp(v1, v2, i / 9.f); expect_close((length(s)), 1.f, 1e-6); + expect_close((angle(s)), rad(10.f * i), 1e-6); expect_gequal((det(v1, s)), 0.f); expect_gequal((det(s, v2)), 0.f); } From b01c594cba74c8a2ec0d876a4bc64c0ef456e17e Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 30 Jan 2025 12:37:48 +0300 Subject: [PATCH 002/141] Add math::fmod that works for negative inputs --- libs/math/include/psemek/math/math.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/math/include/psemek/math/math.hpp b/libs/math/include/psemek/math/math.hpp index d6c0c633..40d3d3e8 100644 --- a/libs/math/include/psemek/math/math.hpp +++ b/libs/math/include/psemek/math/math.hpp @@ -174,6 +174,12 @@ namespace psemek::math return (x / y) + ((x % y) == 0 ? 0 : 1); } + template + T fmod(T x, T m) + { + return (x >= 0) ? std::fmod(x, m) : (m - std::fmod(-x, m)); + } + template bool make_min(T & target, T const & source) { From cca966b33ec59f754cf24d96317bd2a2db3fd152 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 30 Jan 2025 12:38:10 +0300 Subject: [PATCH 003/141] Fix missing include --- libs/ecs/source/container.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/ecs/source/container.cpp b/libs/ecs/source/container.cpp index 944a858d..ac0ba484 100644 --- a/libs/ecs/source/container.cpp +++ b/libs/ecs/source/container.cpp @@ -1,6 +1,8 @@ #include #include +#include + namespace psemek::ecs { From d4e3cc623a789c5f8e54b58d1ae1cc76b1bd0d4a Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 30 Jan 2025 12:38:44 +0300 Subject: [PATCH 004/141] Replace deprecated std::atomic_load with std::atomic in audio::channel --- libs/audio/include/psemek/audio/channel.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/audio/include/psemek/audio/channel.hpp b/libs/audio/include/psemek/audio/channel.hpp index ad7732bf..75081f07 100644 --- a/libs/audio/include/psemek/audio/channel.hpp +++ b/libs/audio/include/psemek/audio/channel.hpp @@ -18,12 +18,12 @@ namespace psemek::audio stream_ptr stream() const { - return std::atomic_load(&stream_); + return stream_.load(); } stream_ptr stream(stream_ptr new_stream) { - return std::atomic_exchange(&stream_, std::move(new_stream)); + return stream_.exchange(std::move(new_stream)); } stream_ptr stop() @@ -37,7 +37,7 @@ namespace psemek::audio } private: - stream_ptr stream_; + std::atomic stream_; }; using channel_ptr = std::shared_ptr; From 64d85dd1c00b9e42479154ada34f6c263b0bf203 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 30 Jan 2025 12:39:04 +0300 Subject: [PATCH 005/141] Don't require OpenMP in sort creatures 2d example --- examples/soft_creatures_2d.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/soft_creatures_2d.cpp b/examples/soft_creatures_2d.cpp index bc1664b1..fd4dffa8 100644 --- a/examples/soft_creatures_2d.cpp +++ b/examples/soft_creatures_2d.cpp @@ -751,7 +751,9 @@ private: std::vector evaluated_creatures(population_.size()); +#ifdef _OPENMP #pragma omp parallel for +#endif for (int i = 0; i < population_.size(); ++i) { auto & creature = population_[i]; From 4e2aa5f577d982dd65003d29b2172772c71071a9 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 30 Jan 2025 12:39:13 +0300 Subject: [PATCH 006/141] Update stb_image.h --- .../gfx/include/psemek/gfx/detail/stb_image.h | 355 +++++++++--------- 1 file changed, 178 insertions(+), 177 deletions(-) diff --git a/libs/gfx/include/psemek/gfx/detail/stb_image.h b/libs/gfx/include/psemek/gfx/detail/stb_image.h index 5e807a0a..9eedabed 100644 --- a/libs/gfx/include/psemek/gfx/detail/stb_image.h +++ b/libs/gfx/include/psemek/gfx/detail/stb_image.h @@ -1,4 +1,4 @@ -/* stb_image - v2.28 - public domain image loader - http://nothings.org/stb +/* stb_image - v2.30 - public domain image loader - http://nothings.org/stb no warranty implied; use at your own risk Do this: @@ -48,6 +48,8 @@ LICENSE RECENT REVISION HISTORY: + 2.30 (2024-05-31) avoid erroneous gcc warning + 2.29 (2023-05-xx) optimizations 2.28 (2023-01-29) many error fixes, security errors, just tons of stuff 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes 2.26 (2020-07-13) many minor fixes @@ -1072,8 +1074,8 @@ static int stbi__addints_valid(int a, int b) return a <= INT_MAX - b; } -// returns 1 if the product of two signed shorts is valid, 0 on overflow. -static int stbi__mul2shorts_valid(short a, short b) +// returns 1 if the product of two ints fits in a signed short, 0 on overflow. +static int stbi__mul2shorts_valid(int a, int b) { if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid @@ -3384,13 +3386,13 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) return 1; } -static int stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) +static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) { // some JPEGs have junk at end, skip over it but if we find what looks // like a valid marker, resume there while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - while (x == 255) { // might be a marker + stbi_uc x = stbi__get8(j->s); + while (x == 0xff) { // might be a marker if (stbi__at_eof(j->s)) return STBI__MARKER_none; x = stbi__get8(j->s); if (x != 0x00 && x != 0xff) { @@ -4176,6 +4178,7 @@ typedef struct { stbi_uc *zbuffer, *zbuffer_end; int num_bits; + int hit_zeof_once; stbi__uint32 code_buffer; char *zout; @@ -4242,9 +4245,20 @@ stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) int b,s; if (a->num_bits < 16) { if (stbi__zeof(a)) { - return -1; /* report error for unexpected end of data. */ + if (!a->hit_zeof_once) { + // This is the first time we hit eof, insert 16 extra padding btis + // to allow us to keep going; if we actually consume any of them + // though, that is invalid data. This is caught later. + a->hit_zeof_once = 1; + a->num_bits += 16; // add 16 implicit zero bits + } else { + // We already inserted our extra 16 padding bits and are again + // out, this stream is actually prematurely terminated. + return -1; + } + } else { + stbi__fill_bits(a); } - stbi__fill_bits(a); } b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; if (b) { @@ -4309,6 +4323,13 @@ static int stbi__parse_huffman_block(stbi__zbuf *a) int len,dist; if (z == 256) { a->zout = zout; + if (a->hit_zeof_once && a->num_bits < 16) { + // The first time we hit zeof, we inserted 16 extra zero bits into our bit + // buffer so the decoder can just do its speculative decoding. But if we + // actually consumed any of those bits (which is the case when num_bits < 16), + // the stream actually read past the end so it is malformed. + return stbi__err("unexpected end","Corrupt PNG"); + } return 1; } if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data @@ -4320,7 +4341,7 @@ static int stbi__parse_huffman_block(stbi__zbuf *a) dist = stbi__zdist_base[z]; if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (zout + len > a->zout_end) { + if (len > a->zout_end - zout) { if (!stbi__zexpand(a, zout, len)) return 0; zout = a->zout; } @@ -4464,6 +4485,7 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) if (!stbi__parse_zlib_header(a)) return 0; a->num_bits = 0; a->code_buffer = 0; + a->hit_zeof_once = 0; do { final = stbi__zreceive(a,1); type = stbi__zreceive(a,2); @@ -4619,9 +4641,8 @@ enum { STBI__F_up=2, STBI__F_avg=3, STBI__F_paeth=4, - // synthetic filters used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first, - STBI__F_paeth_first + // synthetic filter used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first }; static stbi_uc first_row_filter[5] = @@ -4630,29 +4651,56 @@ static stbi_uc first_row_filter[5] = STBI__F_sub, STBI__F_none, STBI__F_avg_first, - STBI__F_paeth_first + STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub }; static int stbi__paeth(int a, int b, int c) { - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; + // This formulation looks very different from the reference in the PNG spec, but is + // actually equivalent and has favorable data dependencies and admits straightforward + // generation of branch-free code, which helps performance significantly. + int thresh = c*3 - (a + b); + int lo = a < b ? a : b; + int hi = a < b ? b : a; + int t0 = (hi <= thresh) ? lo : c; + int t1 = (thresh <= lo) ? hi : t0; + return t1; } static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; +// adds an extra all-255 alpha channel +// dest == src is legal +// img_n must be 1 or 3 +static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n) +{ + int i; + // must process data backwards since we allow dest==src + if (img_n == 1) { + for (i=x-1; i >= 0; --i) { + dest[i*2+1] = 255; + dest[i*2+0] = src[i]; + } + } else { + STBI_ASSERT(img_n == 3); + for (i=x-1; i >= 0; --i) { + dest[i*4+3] = 255; + dest[i*4+2] = src[i*3+2]; + dest[i*4+1] = src[i*3+1]; + dest[i*4+0] = src[i*3+0]; + } + } +} + // create the png data from post-deflated data static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) { - int bytes = (depth == 16? 2 : 1); + int bytes = (depth == 16 ? 2 : 1); stbi__context *s = a->s; stbi__uint32 i,j,stride = x*out_n*bytes; stbi__uint32 img_len, img_width_bytes; + stbi_uc *filter_buf; + int all_ok = 1; int k; int img_n = s->img_n; // copy it into a local for later @@ -4664,8 +4712,11 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into if (!a->out) return stbi__err("outofmem", "Out of memory"); + // note: error exits here don't need to clean up a->out individually, + // stbi__do_png always does on error. if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); img_width_bytes = (((img_n * x * depth) + 7) >> 3); + if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) return stbi__err("too large", "Corrupt PNG"); img_len = (img_width_bytes + 1) * y; // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, @@ -4673,189 +4724,137 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r // so just check for raw_len < img_len always. if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + // Allocate two scan lines worth of filter workspace buffer. + filter_buf = (stbi_uc *) stbi__malloc_mad2(img_width_bytes, 2, 0); + if (!filter_buf) return stbi__err("outofmem", "Out of memory"); + + // Filtering for low-bit-depth images + if (depth < 8) { + filter_bytes = 1; + width = img_width_bytes; + } + for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *prior; + // cur/prior filter buffers alternate + stbi_uc *cur = filter_buf + (j & 1)*img_width_bytes; + stbi_uc *prior = filter_buf + (~j & 1)*img_width_bytes; + stbi_uc *dest = a->out + stride*j; + int nk = width * filter_bytes; int filter = *raw++; - if (filter > 4) - return stbi__err("invalid filter","Corrupt PNG"); - - if (depth < 8) { - if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); - cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place - filter_bytes = 1; - width = img_width_bytes; + // check filter type + if (filter > 4) { + all_ok = stbi__err("invalid filter","Corrupt PNG"); + break; } - prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above // if first row, use special filter that doesn't sample previous row if (j == 0) filter = first_row_filter[filter]; - // handle first byte explicitly - for (k=0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none : cur[k] = raw[k]; break; - case STBI__F_sub : cur[k] = raw[k]; break; - case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; - case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; - case STBI__F_avg_first : cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } + // perform actual filtering + switch (filter) { + case STBI__F_none: + memcpy(cur, raw, nk); + break; + case STBI__F_sub: + memcpy(cur, raw, filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); + break; + case STBI__F_up: + for (k = 0; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + prior[k]); + break; + case STBI__F_avg: + for (k = 0; k < filter_bytes; ++k) + cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); + break; + case STBI__F_paeth: + for (k = 0; k < filter_bytes; ++k) + cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0) + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes], prior[k], prior[k-filter_bytes])); + break; + case STBI__F_avg_first: + memcpy(cur, raw, filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); + break; } - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; // first pixel - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; // first pixel top byte - cur[filter_bytes+1] = 255; // first pixel bottom byte - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; - } + raw += nk; - // this is a little gross, so that we don't switch per-pixel or per-component - if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*filter_bytes; - #define STBI__CASE(f) \ - case f: \ - for (k=0; k < nk; ++k) - switch (filter) { - // "none" filter turns into a memcpy here; make that explicit. - case STBI__F_none: memcpy(cur, raw, nk); break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; - } - #undef STBI__CASE - raw += nk; - } else { - STBI_ASSERT(img_n+1 == out_n); - #define STBI__CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ - for (k=0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; - } - #undef STBI__CASE - - // the loop above sets the high byte of the pixels' alpha, but for - // 16 bit png files we also need the low byte set. we'll do that here. - if (depth == 16) { - cur = a->out + stride*j; // start at the beginning of the row again - for (i=0; i < x; ++i,cur+=output_bytes) { - cur[filter_bytes+1] = 255; - } - } - } - } - - // we make a separate pass to expand bits to pixels; for performance, - // this could run two scanlines behind the above code, so it won't - // intefere with filtering but will still be in the cache. - if (depth < 8) { - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit - // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + // expand decoded bits in cur to dest, also adding an extra alpha channel if desired + if (depth < 8) { stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + stbi_uc *in = cur; + stbi_uc *out = dest; + stbi_uc inb = 0; + stbi__uint32 nsmp = x*img_n; - // note that the final byte might overshoot and write more data than desired. - // we can allocate enough data that this never writes out of memory, but it - // could also overwrite the next scanline. can it overwrite non-empty data - // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. - // so we need to explicitly clamp the final ones - + // expand bits to bytes first if (depth == 4) { - for (k=x*img_n; k >= 2; k-=2, ++in) { - *cur++ = scale * ((*in >> 4) ); - *cur++ = scale * ((*in ) & 0x0f); + for (i=0; i < nsmp; ++i) { + if ((i & 1) == 0) inb = *in++; + *out++ = scale * (inb >> 4); + inb <<= 4; } - if (k > 0) *cur++ = scale * ((*in >> 4) ); } else if (depth == 2) { - for (k=x*img_n; k >= 4; k-=4, ++in) { - *cur++ = scale * ((*in >> 6) ); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in ) & 0x03); + for (i=0; i < nsmp; ++i) { + if ((i & 3) == 0) inb = *in++; + *out++ = scale * (inb >> 6); + inb <<= 2; } - if (k > 0) *cur++ = scale * ((*in >> 6) ); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k=x*img_n; k >= 8; k-=8, ++in) { - *cur++ = scale * ((*in >> 7) ); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in ) & 0x01); + } else { + STBI_ASSERT(depth == 1); + for (i=0; i < nsmp; ++i) { + if ((i & 7) == 0) inb = *in++; + *out++ = scale * (inb >> 7); + inb <<= 1; } - if (k > 0) *cur++ = scale * ((*in >> 7) ); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); } - if (img_n != out_n) { - int q; - // insert alpha = 255 - cur = a->out + stride*j; + + // insert alpha=255 values if desired + if (img_n != out_n) + stbi__create_png_alpha_expand8(dest, dest, x, img_n); + } else if (depth == 8) { + if (img_n == out_n) + memcpy(dest, cur, x*img_n); + else + stbi__create_png_alpha_expand8(dest, cur, x, img_n); + } else if (depth == 16) { + // convert the image data from big-endian to platform-native + stbi__uint16 *dest16 = (stbi__uint16*)dest; + stbi__uint32 nsmp = x*img_n; + + if (img_n == out_n) { + for (i = 0; i < nsmp; ++i, ++dest16, cur += 2) + *dest16 = (cur[0] << 8) | cur[1]; + } else { + STBI_ASSERT(img_n+1 == out_n); if (img_n == 1) { - for (q=x-1; q >= 0; --q) { - cur[q*2+1] = 255; - cur[q*2+0] = cur[q]; + for (i = 0; i < x; ++i, dest16 += 2, cur += 2) { + dest16[0] = (cur[0] << 8) | cur[1]; + dest16[1] = 0xffff; } } else { STBI_ASSERT(img_n == 3); - for (q=x-1; q >= 0; --q) { - cur[q*4+3] = 255; - cur[q*4+2] = cur[q*3+2]; - cur[q*4+1] = cur[q*3+1]; - cur[q*4+0] = cur[q*3+0]; + for (i = 0; i < x; ++i, dest16 += 4, cur += 6) { + dest16[0] = (cur[0] << 8) | cur[1]; + dest16[1] = (cur[2] << 8) | cur[3]; + dest16[2] = (cur[4] << 8) | cur[5]; + dest16[3] = 0xffff; } } } } - } else if (depth == 16) { - // force the image data from big-endian to platform-native. - // this is done in a separate pass due to the decoding relying - // on the data being untouched, but could probably be done - // per-line during decode if care is taken. - stbi_uc *cur = a->out; - stbi__uint16 *cur16 = (stbi__uint16*)cur; - - for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { - *cur16 = (cur[0] << 8) | cur[1]; - } } + STBI_FREE(filter_buf); + if (!all_ok) return 0; + return 1; } @@ -5161,9 +5160,11 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. if (scan == STBI__SCAN_header) { ++s->img_n; return 1; } if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + for (k = 0; k < s->img_n && k < 3; ++k) // extra loop test to suppress false GCC warning + tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is } else { - for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + for (k = 0; k < s->img_n && k < 3; ++k) + tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger } } break; From f75c04d988b9ef3f5d19da41d92a51475e288f08 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 30 Jan 2025 12:40:19 +0300 Subject: [PATCH 007/141] Make gfx::mesh only bind index buffer as GL_ELEMENT_ARRAY_BUFFER, in accordance with WebGL restrictions --- libs/gfx/include/psemek/gfx/mesh.hpp | 13 +++++-------- libs/gfx/source/mesh.cpp | 7 ++----- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/libs/gfx/include/psemek/gfx/mesh.hpp b/libs/gfx/include/psemek/gfx/mesh.hpp index 89d37fc1..52153bdb 100644 --- a/libs/gfx/include/psemek/gfx/mesh.hpp +++ b/libs/gfx/include/psemek/gfx/mesh.hpp @@ -195,11 +195,11 @@ namespace psemek::gfx GLenum index_type() const { return info_.index_type_; } buffer & vertex_buffer() { return vertex_buffer_; } - buffer & index_buffer() { return index_buffer_; } + auto & index_buffer() { return index_buffer_; } buffer & instance_buffer() { return instance_buffer_; } buffer const & vertex_buffer() const { return vertex_buffer_; } - buffer const & index_buffer() const { return index_buffer_; } + auto const & index_buffer() const { return index_buffer_; } buffer const & instance_buffer() const { return instance_buffer_; } // Drawing commands @@ -211,7 +211,7 @@ namespace psemek::gfx private: array array_; buffer vertex_buffer_ = buffer::null(); - buffer index_buffer_ = buffer::null(); + basic_buffer index_buffer_ = basic_buffer::null(); buffer instance_buffer_ = buffer::null(); struct mesh_info @@ -298,11 +298,8 @@ namespace psemek::gfx assert((index_count % (*n)) == 0); if (!index_buffer_) - { - index_buffer_ = buffer{}; - array_.bind(); - gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, index_buffer_.id()); - } + index_buffer_ = {}; + array_.bind(); index_buffer_.load(indices, index_count, usage); info_.index_count_ = index_count; info_.indexed_ = true; diff --git a/libs/gfx/source/mesh.cpp b/libs/gfx/source/mesh.cpp index 455225b0..6cb2afa7 100644 --- a/libs/gfx/source/mesh.cpp +++ b/libs/gfx/source/mesh.cpp @@ -144,11 +144,8 @@ namespace psemek::gfx assert(vertex_buffer_); if (!index_buffer_) - { - index_buffer_ = buffer{}; - array_.bind(); - gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, index_buffer_.id()); - } + index_buffer_ = {}; + array_.bind(); vertex_buffer_.load(vertices, vertex_size * vertex_count, usage); index_buffer_.load(indices, index_size(index_type) * index_count, usage); info_.vertex_count_ = vertex_count; From fa9c5efaf0a327dea9d5db124a96c374dc11a4cf Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 30 Jan 2025 12:40:32 +0300 Subject: [PATCH 008/141] Add 2D hex water example --- examples/water_2d_hex.cpp | 349 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 349 insertions(+) create mode 100644 examples/water_2d_hex.cpp diff --git a/examples/water_2d_hex.cpp b/examples/water_2d_hex.cpp new file mode 100644 index 00000000..80badaf5 --- /dev/null +++ b/examples/water_2d_hex.cpp @@ -0,0 +1,349 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace psemek; + +static const math::vector x_axis {1.f, 0.f}; +static const math::vector y_axis {0.5f, std::sqrt(0.75f)}; +static const math::vector z_axis = y_axis - x_axis; +static const int N = 64; + +static const float dt = 0.1f; +static const float dx = 1.f; +static float const g = 10.f; +static float const friction = std::pow(0.875f, dt); + +math::point to_world(int x, int y) +{ + return math::point{0.f, 0.f} + x_axis * (x - N / 2.f) + y_axis * (y - N / 2.f); +} + +math::point to_grid(math::point const & p) +{ + static auto matrix = *math::inverse(math::by_columns(x_axis, y_axis)); + static auto zero = math::point{0.f, 0.f}; + + return zero + matrix * (p - zero) + math::vector{N / 2.f, N / 2.f}; +} + +struct water_2d_hex_app + : app::application_base +{ + water_2d_hex_app(options const &, context const &); + + void update() override; + void present() override; + + void on_event(app::resize_event const & event) override; + void on_event(app::key_event const & event) override; + + void stop() override; + +private: + bool vsync_on_ = true; + std::function set_vsync_; + + random::generator rng_{random::device{}}; + + float aspect_ratio_ = 1.f; + math::vector screen_size_; + math::box view_area_; + gfx::painter painter_; + + bool paused_ = false; + float time_ = 0.f; + + bool show_velocity_ = false; + + util::array bed_; + util::array water_; + util::array flow_x_; // flow_x(x, y) is (x-1, y) => (x, y) + util::array flow_y_; // flow_y(x, y) is (x, y-1) => (x, y) + util::array flow_z_; // flow_z(x, y) is (x, y-1) => (x-1, y) +}; + +water_2d_hex_app::water_2d_hex_app(options const &, context const & ctx) + : set_vsync_(ctx.vsync) +{ + set_vsync_(vsync_on_); + + bed_.resize({N + 1, N + 1}, 0.f); + water_.resize({N + 1, N + 1}, 0.f); + flow_x_.resize({N + 2, N + 1}, 0.f); + flow_y_.resize({N + 1, N + 2}, 0.f); + flow_z_.resize({N + 2, N + 2}, 0.f); + + random::uniform_sphere_vector_distribution d_grad; + + util::array, 2> perlin_grad({17, 17}); + for (auto & v : perlin_grad) + v = d_grad(rng_); + + pcg::perlin noise(std::move(perlin_grad)); + + for (int y = 0; y <= N; ++y) + { + for (int x = 0; x <= N; ++x) + { + auto q = (to_world(x, y) - math::point{0.f, 0.f}) / (1.f * N); + auto p = q + math::vector{0.5f, 0.5f}; + + (void)p; + + // Canyon + bed_(x, y) = std::max(0.f, 10.f * std::abs(2.f * noise(p) - 1.f) - 1.5f); + + // Islands + // bed_(x, y) = 5.f * math::smoothstep(math::clamp(math::unlerp({0.45f, 0.55f}, noise(p) - 1.f * math::length(q)), {0.f, 1.f})); + + water_(x, y) = 0.f; + } + } +} + +void water_2d_hex_app::update() +{ + { + float y_extent = (N / 2) * y_axis[1]; + view_area_[1] = {- y_extent, y_extent}; + view_area_[0] = {- y_extent * aspect_ratio_, y_extent * aspect_ratio_}; + } + + if (state().mouse_button_down.contains(app::mouse_button::left)) + { + auto m = math::lerp(view_area_, math::vector{state().mouse[0] * 1.f / screen_size_[0], 1.f - state().mouse[1] * 1.f / screen_size_[1]}); + auto p = to_grid(m); + + int const R = 4; + + for (int dy = -R; dy <= R; ++dy) + { + for (int dx = -R; dx <= R; ++dx) + { + if (dx + dy < -R || dx + dy > R) continue; + + int mx = std::round(p[0]) + dx; + int my = std::round(p[1]) + dy; + + if (mx >= 0 && mx <= N && my >= 0 && my <= N) + { + auto c = to_world(mx, my); + water_(mx, my) += 10.f * std::exp(- 0.25f * math::distance_sqr(c, m)) * dt; + } + } + } + } + + if (paused_) + return; + + prof::profiler prof("update"); + + time_ += dt; + + // Init boundary flows + for (int i = 0; i <= N / 2; ++i) + if (bed_(N / 2 - i, i) < 0.5f) + flow_x_(N / 2 - i, i) = 300.f / N; + // flow_x_(N / 2 - i, i) = 0*std::pow(std::sin(0.5f * time_) * std::sin(math::pi * (i * 4.f / N)), 5.f) * 300.f / N; + + for (int i = 0; i <= N / 2; ++i) + if (bed_(N - i, N / 2 + i) < 0.5f) + flow_x_(N - i + 1, N / 2 + i) = 300.f / N; + + // Update X flows + for (int y = 0; y <= N; ++y) + // for (int x = 1; x <= N; ++x) + for (int x = std::max(1, N / 2 + 1 - y); x <= std::min(N, 3 * N / 2 - y); ++x) + // if (x + y - 1 >= N / 2 && x + y <= 3 * N / 2) + flow_x_(x, y) = friction * flow_x_(x, y) + g * dt * (water_(x - 1, y) + bed_(x - 1, y) - water_(x, y) - bed_(x, y)); + + // Update Y flows + for (int y = 1; y <= N; ++y) + // for (int x = 0; x <= N; ++x) + for (int x = std::max(0, N / 2 + 1 - y); x <= std::min(N, 3 * N / 2 - y); ++x) + // if (x + y - 1 >= N / 2 && x + y <= 3 * N / 2) + flow_y_(x, y) = friction * flow_y_(x, y) + g * dt * (water_(x, y - 1) + bed_(x, y - 1) - water_(x, y) - bed_(x, y)); + + // Update Z flows + for (int y = 1; y <= N; ++y) + // for (int x = 1; x <= N; ++x) + for (int x = std::max(1, N / 2 + 1 - y); x <= std::min(N, 3 * N / 2 + 1 - y); ++x) + // if (x + y - 1 >= N / 2 && x + y - 1 <= 3 * N / 2) + flow_z_(x, y) = friction * flow_z_(x, y) + g * dt * (water_(x, y - 1) + bed_(x, y - 1) - water_(x - 1, y) - bed_(x - 1, y)); + + // Scale flows + for (int y = 0; y <= N; ++y) + { + // for (int x = 0; x <= N; ++x) + for (int x = std::max(0, N / 2 - y); x <= std::min(N, 3 * N / 2 - y); ++x) + { + // if (x + y >= N / 2 && x + y <= 3 * N / 2) + { + float outflow = 0.f; + + float & fin1 = flow_x_(x , y ); + float & fin2 = flow_y_(x , y ); + float & fin3 = flow_z_(x + 1, y ); + + float & fout1 = flow_x_(x + 1, y ); + float & fout2 = flow_y_(x , y + 1); + float & fout3 = flow_z_(x , y + 1); + + outflow += std::max(0.f, -fin1); + outflow += std::max(0.f, -fin2); + outflow += std::max(0.f, -fin3); + outflow += std::max(0.f, fout1); + outflow += std::max(0.f, fout2); + outflow += std::max(0.f, fout3); + + if (outflow > 0.f) + { + float max_outflow = water_(x, y) * dx * dx / dt; + + float scale = std::min(1.f, max_outflow / outflow); + + fin1 *= (fin1 < 0.f ? scale : 1.f); + fin2 *= (fin2 < 0.f ? scale : 1.f); + fin3 *= (fin3 < 0.f ? scale : 1.f); + + fout1 *= (fout1 > 0.f ? scale : 1.f); + fout2 *= (fout2 > 0.f ? scale : 1.f); + fout3 *= (fout3 > 0.f ? scale : 1.f); + } + } + } + } + + // Update water + for (int y = 0; y <= N; ++y) + // for (int x = 0; x <= N; ++x) + for (int x = std::max(0, N / 2 - y); x <= std::min(N, 3 * N / 2 - y); ++x) + // if (x + y >= N / 2 && x + y <= 3 * N / 2) + water_(x, y) += dt / dx / dx * (flow_x_(x, y) + flow_y_(x, y) + flow_z_(x + 1,y) - flow_x_(x + 1, y) - flow_y_(x, y + 1) - flow_z_(x, y + 1)); +} + +void water_2d_hex_app::present() +{ + gl::ClearColor(0.f, 0.f, 0.f, 0.f); + gl::Clear(gl::COLOR_BUFFER_BIT); + + for (int y = 0; y < N; ++y) + { + for (int x = 0; x < N; ++x) + { + auto color = [this](int x, int y) + { + auto bed = gfx::color_4f{0.9f, 0.7f, 0.5f, - std::expm1(- bed_(x, y))}; + auto water = gfx::color_4f{0.0f, 0.25f, 1.f, - std::expm1(- water_(x, y))}; + + auto color = gfx::color_4f{0.f, 0.f, 0.f, 0.f}; + color = gfx::blend(color, bed); + color = gfx::blend(color, water); + return gfx::to_coloru8(color); + }; + + auto p00 = to_world(x, y); + auto p01 = to_world(x + 1, y); + auto p10 = to_world(x, y + 1); + auto p11 = to_world(x + 1, y + 1); + + auto b00 = color(x, y); + auto b01 = color(x + 1, y); + auto b10 = color(x, y + 1); + auto b11 = color(x + 1, y + 1); + + if (x + y >= N / 2 && x + y < (3 * N) / 2) + painter_.triangle(p00, p01, p10, b00, b01, b10); + + if (x + y + 1 >= N / 2 && x + y + 1 < (3 * N) / 2) + painter_.triangle(p10, p01, p11, b10, b01, b11); + } + } + + if (show_velocity_) + for (int y = 0; y <= N; ++y) + { + for (int x = 0; x <= N; ++x) + { + if (x + y >= N / 2 && x + y <= (3 * N) / 2) + { + auto p = to_world(x, y); + + auto vx = (flow_x_(x, y) + flow_x_(x + 1, y)) * 0.5f * x_axis; + auto vy = (flow_y_(x, y) + flow_y_(x, y + 1)) * 0.5f * y_axis; + auto vz = (flow_z_(x + 1, y) + flow_z_(x, y + 1)) * 0.5f * z_axis; + + auto v = (vx + vy + vz); + + auto M = 1.f; + auto l = math::length(v); + float s = std::min(1.f, l / M); + float c = -std::expm1(- 0.1f * l); + + auto color = gfx::to_coloru8(gfx::color_4f{1.f, 1.f - c, 1.f - c, 1.f}); + + painter_.line(p, p + v * (s / l), 0.25f * s, 0.f, color, color, true); + } + } + } + + painter_.render(math::orthographic_camera{view_area_}.transform()); +} + +void water_2d_hex_app::on_event(app::resize_event const & event) +{ + gl::Viewport(0, 0, event.size[0], event.size[1]); + screen_size_ = event.size; + aspect_ratio_ = (event.size[0] * 1.f) / event.size[1]; +} + +void water_2d_hex_app::on_event(app::key_event const & event) +{ + if (event.down) + { + switch (event.key) + { + case app::keycode::V: + vsync_on_ ^= true; + set_vsync_(vsync_on_); + break; + case app::keycode::C: + show_velocity_ ^= true; + break; + case app::keycode::SPACE: + paused_ ^= true; + break; + default: + break; + } + } +} + + +void water_2d_hex_app::stop() +{ + app::application_base::stop(); + prof::dump(); +} + +namespace psemek::app +{ + + std::unique_ptr make_application_factory() + { + return default_application_factory({.name = "Water 2D hex example", .multisampling = 4}); + } + +} From a6268663da30e32edc91660803be80b4195bea04 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 30 Jan 2025 13:07:30 +0300 Subject: [PATCH 009/141] More 2d shallow water tests --- examples/water_2d.cpp | 136 +++++++++++++++++++++++++++++++++++------- 1 file changed, 114 insertions(+), 22 deletions(-) diff --git a/examples/water_2d.cpp b/examples/water_2d.cpp index ce22777b..9ace87fb 100644 --- a/examples/water_2d.cpp +++ b/examples/water_2d.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -31,7 +32,7 @@ struct water_2d_app random::generator rng{random::device{}}; - int const N = 128; + int const N = 256; float time = 0.f; @@ -48,14 +49,23 @@ struct water_2d_app bool paused = true; bool show_water = true; bool show_velocity = false; + bool show_particles = false; bool erosion_on = false; bool rain_on = false; + + struct particle + { + math::point position; + int lifetime; + }; + + std::vector particles; }; water_2d_app::water_2d_app(options const &, context const & ctx) { (void)ctx; - ctx.vsync(true); + // ctx.vsync(true); simulation_area[0] = {-1.f, 1.f}; simulation_area[1] = {-1.f, 1.f}; @@ -86,8 +96,8 @@ water_2d_app::water_2d_app(options const &, context const & ctx) // if (x == N / 2 && (y != N / 4 && y != 3 * N / 4)) // bed(x, y) = 100.f; - float tx = (x + 0.5f) / N; - float ty = (y + 0.5f) / N; + [[maybe_unused]] float tx = (x + 0.5f) / N; + [[maybe_unused]] float ty = (y + 0.5f) / N; float n = 0.f; @@ -106,7 +116,9 @@ water_2d_app::water_2d_app(options const &, context const & ctx) // n = 4.f * std::abs(2.f * noise(tx, ty) - 1.f) * std::pow(4.f * tx * (1.f - tx), 2.f); // River - n = std::min(4.f, math::lerp(10.f, 20.f, noise(tx, ty)) * std::abs(ty - math::lerp({0.4f, 0.6f}, noise(tx, 0.f)))); + // n = std::min(4.f, math::lerp(10.f, 20.f, noise(tx, ty)) * std::abs(ty - math::lerp({0.4f, 0.6f}, noise(tx, 0.f)))); + + // n = 0.f; // Canyon // n = 10.f * math::clamp(10.f * math::lerp(-1.f, 1.f, noise(tx, ty)), {0.f, 1.f}); @@ -128,13 +140,17 @@ water_2d_app::water_2d_app(options const &, context const & ctx) // if (x == 0) // water(x, y) = 100.f - bed(x, y); + + water(x, y) = std::max(0.f, 1.f - bed(x, y)) * (0.5f + 0.5f * noise(tx, ty)); + + flowx(x, y) = (2.f * ty - 1.f) * water(x, y) * 10.f / N; } } // water(N / 2, N / 2) = 10000.f; } -float const dt = 0.002f; +float const dt = 0.001f; void water_2d_app::update() { @@ -144,11 +160,15 @@ void water_2d_app::update() float const dx = simulation_area[0].length() / N; float const dy = simulation_area[1].length() / N; float const g = 10.f; - float const friction = std::pow(0.5f, dt); + float const friction = std::pow(1.f, dt); float const viscosity = 0.f; float const sediment_capacity = 0.1f; float const erosion = 1.f; float const deposition = 10.f; + float const coriolis = 0.01f; + int const max_particles = 16 * 1024; + int const particles_per_frame = 16; + int const max_particle_lifetime = max_particles / particles_per_frame; // Init boundary flows for (int x = 0; x < N; ++x) @@ -160,13 +180,16 @@ void water_2d_app::update() // if (x < N / 4) flowy(x, N) = -3.f / N; // flowy(x, 0) = 5.f * std::sin(10.f * time) / N; + + flowy(x, 0) = 0.f; + flowy(x, N) = 0.f; } for (int y = 0; y < N; ++y) { // flowx(0, y) = 5.f * std::sin(10.f * time) / N; - // flowx(0, y) = 1.f / N; + // flowx(0, y) = 10.f / N; // flowx(N, y) = 1.f / N; // flowx(0, y) = 10.f * ((y + 0.5f) / N) / N; @@ -175,12 +198,12 @@ void water_2d_app::update() // if (y < N / 4) flowx(0, y) = 3.f / N; // if (y >= 3 * N / 4) flowx(N, y) = -3.f / N; - if (bed(0, y) < 0.75f) - flowx(0, y) = 4.f / N; + // if (bed(0, y) < 0.75f) + // flowx(0, y) = 4.f / N; // flowx(0, y) = 5.f * math::sqr(std::max(0.f, std::sin(15.f * time))) / N; - if (bed(N - 1, y) < 0.75f) - flowx(N, y) = 5.f / N; + // if (bed(N - 1, y) < 0.75f) + // flowx(N, y) = 5.f / N; } // Rain :) @@ -195,9 +218,13 @@ void water_2d_app::update() // Update X flows for (int y = 0; y < N; ++y) + { for (int x = 1; x < N; ++x) flowx(x, y) = friction * flowx(x, y) + g * dt * (water(x - 1, y) + bed(x - 1, y) - water(x, y) - bed(x, y)); + flowx(0, y) = friction * flowx(0, y) + g * dt * (water(N - 1, y) + bed(N - 1, y) - water(0, y) - bed(0, y)); + } + // Update Y flows for (int y = 1; y < N; ++y) for (int x = 0; x < N; ++x) @@ -236,7 +263,7 @@ void water_2d_app::update() { float outflow = 0.f; outflow += std::max(0.f, - flowx(x , y )); - outflow += std::max(0.f, flowx(x + 1, y )); + outflow += std::max(0.f, flowx((x + 1) % N, y )); outflow += std::max(0.f, - flowy(x , y )); outflow += std::max(0.f, flowy(x , y + 1)); @@ -247,7 +274,7 @@ void water_2d_app::update() float scale = std::min(1.f, max_outflow / outflow); if (flowx(x, y) < 0.f) flowx(x, y) *= scale; - if (flowx(x + 1, y) > 0.f) flowx(x + 1, y) *= scale; + if (flowx((x + 1) % N, y) > 0.f) flowx((x + 1) % N, y) *= scale; if (flowy(x, y) < 0.f) flowy(x, y) *= scale; if (flowy(x, y + 1) > 0.f) flowy(x, y + 1) *= scale; @@ -262,7 +289,7 @@ void water_2d_app::update() { float w_old = water(x, y); - water(x, y) += dt / dx / dy * (flowx(x, y) + flowy(x, y) - flowx(x + 1, y) - flowy(x, y + 1)); + water(x, y) += dt / dx / dy * (flowx(x, y) + flowy(x, y) - flowx((x + 1) % N, y) - flowy(x, y + 1)); float w_avg = (w_old + water(x, y)) / 2.f; @@ -270,7 +297,7 @@ void water_2d_app::update() if (w_avg > 0.f) { - v[0] = (flowx(x, y) + flowx(x + 1, y)) / 2.f / dx / w_avg; + v[0] = (flowx(x, y) + flowx((x + 1) % N, y)) / 2.f / dx / w_avg; v[1] = (flowy(x, y) + flowy(x, y + 1)) / 2.f / dy / w_avg; } @@ -278,6 +305,20 @@ void water_2d_app::update() } } + // Add coriolis force + for (int y = 0; y < N; ++y) + { + for (int x = 0; x < N; ++x) + { + auto n = math::ort(velocity(x, y)) * coriolis * dt / 2.f * std::sin(float(math::pi) / 2.f * ((y + 0.5f) * 2.f / N - 1.f)); + + flowx(x, y) += n[0]; + flowx((x + 1) % N, y) += n[0]; + flowy(x, y) += n[1]; + flowy(x, y + 1) += n[1]; + } + } + if (erosion_on) { // Erosion-deposition @@ -332,6 +373,36 @@ void water_2d_app::update() std::swap(sediment, new_sediment); } + // Init particles + for (int i = 0; i < particles_per_frame; ++i) + if (particles.size() < max_particles) + { + math::point pos{random::uniform(rng, 0.f, N), random::uniform(rng, 0.f, N)}; + if (water(std::floor(pos[0]), std::floor(pos[1])) > 1e-3f) + particles.push_back({pos, 0}); + } + + // Update particles + for (auto & p : particles) + { + p.position += velocity(std::min(N - 1, std::floor(p.position[0])), std::min(N - 1, std::floor(p.position[1]))) * (N * dt); + p.position[0] = math::fmod(p.position[0], float(N)); + p.position[1] = math::clamp(p.position[1], {0.f, N}); + p.lifetime += 1; + } + + // Destroy particles + for (int i = 0; i < particles.size();) + { + if (particles[i].lifetime >= max_particle_lifetime) + { + std::swap(particles[i], particles.back()); + particles.pop_back(); + } + else + ++i; + } + time += dt; } @@ -371,8 +442,8 @@ void water_2d_app::present() float w = 1.f - std::exp(- 1.f * water(x, y)); float s = 1.f - std::exp(- 1.f * sediment(x, y)); - if (water(x, y) > 1e-3f) - w = 0.5f + 0.5f * w; + // if (water(x, y) > 1e-3f) + // w = 0.5f + 0.5f * w; auto bed_color = gfx::to_coloru8(gfx::color_4f{0.96f, 0.72f, 0.53f, b}); // auto bed_color = gfx::to_coloru8(gfx::color_4f{0.3f, 0.5f, 0.1f, b}); @@ -398,20 +469,33 @@ void water_2d_app::present() { auto center = simulation_area.corner((x + 0.5f) * invN, (y + 0.5f) * invN); - auto v = water(x, y) * velocity(x, y) / 100.f; + auto v = water(x, y) * velocity(x, y) / 40.f; - float max_length = 0.02f; + float max_length = 0.05f; auto l = math::length(v); - auto color = gfx::to_coloru8(gfx::color_4f{1.f, std::exp(- 10.f * l), 0.f, 1.f}); + // auto color = gfx::to_coloru8(gfx::color_4f{1.f, std::exp(- 10.f * l), 0.f, 1.f}); + auto color = gfx::color_rgba{255, 255, 255, 255}; float s = std::min(1.f, l / max_length); + // s = 1.f; - painter.line(center, center + math::normalized(v) * max_length * s, s * 0.004f, s * 0.002f, color, color, false); + auto d = math::normalized(v) * max_length * s / 2.f; + + painter.line(center - d, center + d, s * 0.001f, 0.f, color, color, false); } } } + if (show_particles) + { + for (auto const & p : particles) + { + auto pos = math::lerp(simulation_area, math::vector{p.position[0] / N, p.position[1] / N}); + painter.circle(pos, 0.005f, {255, 255, 255, 127}, 6); + } + } + auto mouse = math::cast(state().mouse); mouse[0] = math::lerp(view_area[0], mouse[0] / state().size[0]); @@ -447,6 +531,11 @@ void water_2d_app::present() } painter.render(transform); + + { + painter.text({20.f, 20.f}, std::format("{} particles", particles.size()), {.scale = {2.f, 2.f}, .x = gfx::painter::x_align::left, .y = gfx::painter::y_align::top, .c = {0, 0, 0, 255}}); + painter.render(math::window_camera{state().size[0], state().size[1]}.transform()); + } } void water_2d_app::on_event(app::resize_event const & event) @@ -467,6 +556,9 @@ void water_2d_app::on_event(app::key_event const & event) if (event.down && event.key == app::keycode::V) show_velocity ^= true; + if (event.down && event.key == app::keycode::P) + show_particles ^= true; + if (event.down && event.key == app::keycode::E) erosion_on ^= true; From cd31187d3f310ccab2fbf0ca3329cab4457ea72f Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 2 Feb 2025 21:28:59 +0300 Subject: [PATCH 010/141] Add missing include --- libs/ecs/source/detail/table.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/ecs/source/detail/table.cpp b/libs/ecs/source/detail/table.cpp index aa4c185f..9fdd4d38 100644 --- a/libs/ecs/source/detail/table.cpp +++ b/libs/ecs/source/detail/table.cpp @@ -1,6 +1,8 @@ #include #include +#include + namespace psemek::ecs::detail { From 3655fc9c6f424559179570c640dbdae39352f089 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Mon, 3 Feb 2025 00:43:39 +0300 Subject: [PATCH 011/141] Add (bad) support for multiline text in gfx::painter: --- libs/gfx/source/painter.cpp | 46 +++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/libs/gfx/source/painter.cpp b/libs/gfx/source/painter.cpp index 051d9db1..038cc6b3 100644 --- a/libs/gfx/source/painter.cpp +++ b/libs/gfx/source/painter.cpp @@ -301,18 +301,37 @@ namespace psemek::gfx math::vector painter::text_size(std::string_view str, font f) { // TODO: multiline text - math::vector s; + math::vector font_size; switch (f) { case font::font_9x12: - s = {9.f, 12.f}; + font_size = {9.f, 12.f}; break; default: throw util::unknown_enum_value_exception(f); } - s[0] *= str.size(); - return s; + int max_line_width = 0; + int line_count = 0; + + int last_line_start = 0; + for (int i = 0; i < str.size(); ++i) + { + if (str[i] == '\n') + { + math::make_max(max_line_width, i - last_line_start); + last_line_start = i; + ++line_count; + } + } + + if (last_line_start + 1 != str.size()) + { + math::make_max(max_line_width, static_cast(str.size()) - last_line_start); + ++line_count; + } + + return {font_size[0] * max_line_width, font_size[1] * line_count}; } void painter::text(math::point const & p, std::string_view str, text_options const & opts) @@ -489,17 +508,17 @@ namespace psemek::gfx { auto const size = math::pointwise_mult(text_size(str, opts.f), opts.scale); - math::vector pen { 0.f, 0.f, 0.f }; + math::vector origin { 0.f, 0.f, 0.f }; switch (opts.x) { case x_align::left: break; case x_align::center: - pen[0] -= size[0] / 2.f; + origin[0] -= size[0] / 2.f; break; case x_align::right: - pen[0] -= size[0]; + origin[0] -= size[0]; break; default: throw util::unknown_enum_value_exception(opts.x); @@ -510,15 +529,17 @@ namespace psemek::gfx case y_align::top: break; case y_align::center: - pen[1] -= size[1] / 2.f; + origin[1] -= size[1] / 2.f; break; case y_align::bottom: - pen[1] -= size[1]; + origin[1] -= size[1]; break; default: throw util::unknown_enum_value_exception(opts.y); } + auto pen = origin; + math::vector const sx = {9.f * opts.scale[0], 0.f, 0.f}; math::vector const sy = {0.f, 12.f * opts.scale[1], 0.f}; @@ -529,6 +550,13 @@ namespace psemek::gfx for (char c : str) { + if (c == '\n') + { + pen[0] = origin[0]; + pen += sy; + continue; + } + // Guard against unsigned char #ifdef __GNUC__ #pragma GCC diagnostic push From 97c9d79a5ae64f2504a07a257533ac8eb7b35054 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 16 Feb 2025 18:43:30 +0300 Subject: [PATCH 012/141] Add default values to wgpu-related fields in application::options --- libs/app/include/psemek/app/application.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/app/include/psemek/app/application.hpp b/libs/app/include/psemek/app/application.hpp index b9cd1f36..96a1f110 100644 --- a/libs/app/include/psemek/app/application.hpp +++ b/libs/app/include/psemek/app/application.hpp @@ -24,9 +24,9 @@ namespace psemek::app int multisampling = 4; bool highdpi = false; #if defined(PSEMEK_GRAPHICS_API_WEBGPU) - std::vector required_features; - std::optional required_limits; - std::optional required_native_limits; + std::vector required_features = {}; + std::optional required_limits = {}; + std::optional required_native_limits = {}; #endif }; From 28a4d9ccb0fa4f2fe05ec499d5d3d1f616a51eb4 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 21 Feb 2025 19:20:54 +0300 Subject: [PATCH 013/141] Make cg::kdtree::closest const & add cg::kdtree::clear & assign --- libs/cg/include/psemek/cg/kdtree.hpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/libs/cg/include/psemek/cg/kdtree.hpp b/libs/cg/include/psemek/cg/kdtree.hpp index 13373606..d728f9f0 100644 --- a/libs/cg/include/psemek/cg/kdtree.hpp +++ b/libs/cg/include/psemek/cg/kdtree.hpp @@ -77,6 +77,9 @@ namespace psemek::cg template kdtree(Iterator begin, Iterator end); + template + void assign(Iterator begin, Iterator end); + bool empty() const { return nodes_.empty(); } bool insert(value_type && value); @@ -84,11 +87,13 @@ namespace psemek::cg // TODO: implement bool remove(point_type const & point) const; + void clear(); + // TODO: implement // TODO: alternative non-const version that allows modifying value.data value_type const * find(point_type const & point) const; - value_type const & closest(point_type const & target); + value_type const & closest(point_type const & target) const; private: @@ -125,6 +130,14 @@ namespace psemek::cg build_node_impl(begin, end, 0); } + template + template + void kdtree::assign(Iterator begin, Iterator end) + { + clear(); + build_node_impl(begin, end, 0); + } + template bool kdtree::insert(value_type && value) { @@ -132,7 +145,13 @@ namespace psemek::cg } template - kdtree::value_type const & kdtree::closest(point_type const & target) + void kdtree::clear() + { + nodes_.clear(); + } + + template + kdtree::value_type const & kdtree::closest(point_type const & target) const { if (nodes_.empty()) throw util::exception("empty kdtree"); From 72694664deb1ea73ae4895c7fbc7f6a195e55c71 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 21 Feb 2025 22:26:24 +0300 Subject: [PATCH 014/141] Add util::moving_average::max_count() --- libs/util/include/psemek/util/moving_average.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libs/util/include/psemek/util/moving_average.hpp b/libs/util/include/psemek/util/moving_average.hpp index a3d99885..33bde40e 100644 --- a/libs/util/include/psemek/util/moving_average.hpp +++ b/libs/util/include/psemek/util/moving_average.hpp @@ -48,6 +48,11 @@ namespace psemek::util return size_; } + std::size_t max_count() const + { + return data_.size(); + } + T average() const { return sum() / count(); From 85451a1d6f85b8923174ee40e0d2b4f5172bb312 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 23 Feb 2025 00:31:13 +0300 Subject: [PATCH 015/141] Add gfx::hue(colorf) --- libs/gfx/include/psemek/gfx/color.hpp | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/libs/gfx/include/psemek/gfx/color.hpp b/libs/gfx/include/psemek/gfx/color.hpp index 2f2e78f6..9fbd4c39 100644 --- a/libs/gfx/include/psemek/gfx/color.hpp +++ b/libs/gfx/include/psemek/gfx/color.hpp @@ -317,6 +317,39 @@ namespace psemek::gfx return generic_color{dark(c.c, darkness)}; } + template requires (std::is_floating_point_v && (N == 3 || N == 4)) + T hue(math::vector const & c) + { + T max = std::max({c[0], c[1], c[2]}); + T min = std::min({c[0], c[1], c[2]}); + T delta = max - min; + + if (delta == T{0}) + return T{0}; + + T result; + + if (c[0] > c[1] && c[0] > c[2]) + { + result = (c[1] - c[2]) / delta; + } + else if (c[1] > c[2]) + { + result = 2.f + (c[2] - c[0]) / delta; + } + else + { + result = 4.f + (c[0] - c[1]) / delta; + } + + result *= math::rad(60); + + if (result < 0) + result += math::rad(360); + + return result; + } + std::optional parse_color(std::string_view const & text); } From 4c16594b3f2bce88eb23f236252966de112e5ded Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 23 Feb 2025 11:46:43 +0300 Subject: [PATCH 016/141] Use pi casted to appropriate type instead of implicit conversion to double in math/math.hpp --- libs/math/include/psemek/math/math.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/math/include/psemek/math/math.hpp b/libs/math/include/psemek/math/math.hpp index 40d3d3e8..d29f14f9 100644 --- a/libs/math/include/psemek/math/math.hpp +++ b/libs/math/include/psemek/math/math.hpp @@ -35,13 +35,13 @@ namespace psemek::math template T deg(T x) { - return static_cast((180 * x) / pi); + return static_cast((180 * x) / static_cast(pi)); } template T rad(T x) { - return static_cast((x * pi) / 180); + return static_cast((x * static_cast(pi)) / 180); } template From 4f4e86ce4f87987fb2376dc7fda5cec8b89bbe31 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 23 Feb 2025 13:56:13 +0300 Subject: [PATCH 017/141] Add math::inverse_smoothstep --- libs/math/include/psemek/math/math.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/math/include/psemek/math/math.hpp b/libs/math/include/psemek/math/math.hpp index d29f14f9..f3e598ea 100644 --- a/libs/math/include/psemek/math/math.hpp +++ b/libs/math/include/psemek/math/math.hpp @@ -225,4 +225,10 @@ namespace psemek::math return x * x * x * (10 - x * (15 - 6 * x)); } + template + T inverse_smoothstep(T x) + { + return T{1} / T{2} - std::sin(std::asin(T{1} - T{2} * x) / T{3}); + } + } From 12eed4dda5a1f69c5cadf11c8554a7a8ffebc85e Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 23 Feb 2025 13:56:33 +0300 Subject: [PATCH 018/141] Add HSV <-> RGB conversions --- libs/gfx/include/psemek/gfx/color.hpp | 76 +++++++++++++++++++-------- 1 file changed, 54 insertions(+), 22 deletions(-) diff --git a/libs/gfx/include/psemek/gfx/color.hpp b/libs/gfx/include/psemek/gfx/color.hpp index 9fbd4c39..7bec7f28 100644 --- a/libs/gfx/include/psemek/gfx/color.hpp +++ b/libs/gfx/include/psemek/gfx/color.hpp @@ -317,35 +317,67 @@ namespace psemek::gfx return generic_color{dark(c.c, darkness)}; } - template requires (std::is_floating_point_v && (N == 3 || N == 4)) - T hue(math::vector const & c) + template + math::vector rgb_to_hsv(math::vector const & rgb) { - T max = std::max({c[0], c[1], c[2]}); - T min = std::min({c[0], c[1], c[2]}); + T max = std::max({rgb[0], rgb[1], rgb[2]}); + T min = std::min({rgb[0], rgb[1], rgb[2]}); T delta = max - min; - if (delta == T{0}) - return T{0}; + T hue = T{0}; - T result; + if (delta > T{0}) + { + if (rgb[0] > rgb[1] && rgb[0] > rgb[2]) + { + hue = (rgb[1] - rgb[2]) / delta; + } + else if (rgb[1] > rgb[2]) + { + hue = 2.f + (rgb[2] - rgb[0]) / delta; + } + else + { + hue = 4.f + (rgb[0] - rgb[1]) / delta; + } + } - if (c[0] > c[1] && c[0] > c[2]) - { - result = (c[1] - c[2]) / delta; - } - else if (c[1] > c[2]) - { - result = 2.f + (c[2] - c[0]) / delta; - } + hue *= math::rad(60); + + if (hue < 0) + hue += math::rad(360); + + math::vector result; + result[0] = hue; + result[1] = (max > T{0}) ? (delta / max) : T{0}; + result[2] = max; + + return result; + } + + template + math::vector hsv_to_rgb(math::vector const & hsv) + { + auto c = hsv[1] * hsv[2]; + auto x = c * (T{1} - std::abs(std::fmod(hsv[0] / math::rad(60), 2.f) - 1.f)); + auto m = hsv[2] - c; + + math::vector result; + + if (hsv[0] < math::rad(60)) + result = {c, x, T{0}}; + else if (hsv[0] < math::rad(120)) + result = {x, c, T{0}}; + else if (hsv[0] < math::rad(180)) + result = {T{0}, c, x}; + else if (hsv[0] < math::rad(240)) + result = {T{0}, x, c}; + else if (hsv[0] < math::rad(300)) + result = {x, T{0}, c}; else - { - result = 4.f + (c[0] - c[1]) / delta; - } + result = {c, T{0}, x}; - result *= math::rad(60); - - if (result < 0) - result += math::rad(360); + result += math::vector{m, m, m}; return result; } From 195a31fa1c507864c41cc0b54d98a7c3fef2c2c7 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Mon, 24 Feb 2025 13:02:48 +0300 Subject: [PATCH 019/141] Add text input events in SDL2 backend --- libs/app/include/psemek/app/application.hpp | 1 + libs/app/include/psemek/app/event_handler.hpp | 1 + libs/app/include/psemek/app/events.hpp | 7 +++++++ libs/sdl2/source/events.cpp | 3 +++ libs/sdl2/source/main.cpp | 1 + 5 files changed, 13 insertions(+) diff --git a/libs/app/include/psemek/app/application.hpp b/libs/app/include/psemek/app/application.hpp index 96a1f110..b6948266 100644 --- a/libs/app/include/psemek/app/application.hpp +++ b/libs/app/include/psemek/app/application.hpp @@ -37,6 +37,7 @@ namespace psemek::app std::function show_cursor; std::function relative_mouse_mode; std::function windowed; + std::function text_input; #if defined(PSEMEK_GRAPHICS_API_OPENGL) std::function vsync; #endif diff --git a/libs/app/include/psemek/app/event_handler.hpp b/libs/app/include/psemek/app/event_handler.hpp index 3094555a..913d708a 100644 --- a/libs/app/include/psemek/app/event_handler.hpp +++ b/libs/app/include/psemek/app/event_handler.hpp @@ -16,6 +16,7 @@ namespace psemek::app virtual void on_event(touch_up_event const &) {} virtual void on_event(touch_move_event const &) {} virtual void on_event(key_event const &) {} + virtual void on_event(text_input_event const &) {} virtual ~event_handler() {} }; diff --git a/libs/app/include/psemek/app/events.hpp b/libs/app/include/psemek/app/events.hpp index e3e21ade..ea0fe495 100644 --- a/libs/app/include/psemek/app/events.hpp +++ b/libs/app/include/psemek/app/events.hpp @@ -2,6 +2,8 @@ #include +#include + namespace psemek::app { @@ -172,4 +174,9 @@ namespace psemek::app bool down; }; + struct text_input_event + { + std::string input; + }; + } diff --git a/libs/sdl2/source/events.cpp b/libs/sdl2/source/events.cpp index 37b486d7..4b5fec15 100644 --- a/libs/sdl2/source/events.cpp +++ b/libs/sdl2/source/events.cpp @@ -192,6 +192,9 @@ namespace psemek::sdl2 if (auto key = keycode(e.key.keysym)) handler.on_event(app::key_event{*key, false}); break; + case SDL_TEXTINPUT: + handler.on_event(app::text_input_event{e.text.text}); + break; } return false; diff --git a/libs/sdl2/source/main.cpp b/libs/sdl2/source/main.cpp index 714399a8..989dfd3e 100644 --- a/libs/sdl2/source/main.cpp +++ b/libs/sdl2/source/main.cpp @@ -48,6 +48,7 @@ int main(int argc, char ** argv) try context.show_cursor = [&](bool show){ window.show_cursor(show); }; context.relative_mouse_mode = [&](bool mode){ window.relative_mouse_mode(mode); }; context.windowed = [&](bool on){ window.windowed(on); }; + context.text_input = [](bool on){ if (on) SDL_StartTextInput(); else SDL_StopTextInput(); }; #if defined(PSEMEK_GRAPHICS_API_OPENGL) context.vsync = [&](bool on){ window.vsync(on); }; From dab7b6327e5c25a0031006896746eded0a7563ce Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 26 Feb 2025 22:13:14 +0300 Subject: [PATCH 020/141] Fix wgpu packaging for windows --- package/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/CMakeLists.txt b/package/CMakeLists.txt index 79c0a35d..b3b510a0 100644 --- a/package/CMakeLists.txt +++ b/package/CMakeLists.txt @@ -81,7 +81,7 @@ function(psemek_add_executable_impl target is_application) set(_COPY_FILES $ ${PSEMEK_PACKAGE_COPY_FILES}) if(PSEMEK_GRAPHICS_API STREQUAL WEBGPU) - list(APPEND _COPY_FILES ${PSEMEK_PACKAGE_COPY_FILES_WEBGPU}) + list(APPEND _COPY_FILES ${PSEMEK_PACKAGE_COPY_FILES_WGPU}) endif() if(PSEMEK_USE_FREETYPE) From ef85eec1781d9fc311905de03cc0a63d9363a550 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Tue, 4 Mar 2025 18:12:21 +0300 Subject: [PATCH 021/141] Use wide strings for io::file_stream on Windows --- libs/io/source/file_stream.cpp | 79 ++++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 22 deletions(-) diff --git a/libs/io/source/file_stream.cpp b/libs/io/source/file_stream.cpp index 7c1cc481..089b9c71 100644 --- a/libs/io/source/file_stream.cpp +++ b/libs/io/source/file_stream.cpp @@ -1,27 +1,72 @@ #include #include +#include #include -#include namespace psemek::io { - static void throw_fopen [[noreturn]] (std::filesystem::path const & path) + namespace { - throw util::system_error(std::error_code{errno, std::system_category()}, "Failed to open " + path.string()); - } - static FILE * safe_fopen(std::filesystem::path const & path, const char * mode) - { - std::string path_str = path.string(); - auto f = std::fopen(path_str.c_str(), mode); - if (!f) throw_fopen(path); - return f; + void throw_fopen [[noreturn]] (std::filesystem::path const & path) + { + throw util::system_error(std::error_code{errno, std::system_category()}, "Failed to open " + path.string()); + } + +#ifdef __WIN32__ + wchar_t const * fopen_read_mode() + { + return L"rb"; + } + + wchar_t const * fopen_write_mode(unsigned flags) + { + switch (flags) + { + case 0: return L"wb"; + case file_ostream::append: return L"ab"; + } + + throw util::exception("Unknown file_ostream open flags"); + } + + FILE * safe_fopen(std::filesystem::path const & path, wchar_t const * mode) + { + auto f = _wfopen(path.c_str(), mode); + if (!f) throw_fopen(path); + return f; + } +#else + char const * fopen_read_mode() + { + return "rb"; + } + + char const * fopen_write_mode(unsigned flags) + { + switch (flags) + { + case 0: return "wb"; + case file_ostream::append: return "ab"; + } + + throw util::exception("Unknown file_ostream open flags"); + } + + FILE * safe_fopen(std::filesystem::path const & path, char const * mode) + { + auto f = std::fopen(path.c_str(), mode); + if (!f) throw_fopen(path); + return f; + } +#endif + } file_istream::file_istream(std::filesystem::path const & path) - : file_{safe_fopen(path.c_str(), "rb")} + : file_{safe_fopen(path, fopen_read_mode())} {} void file_istream::reset() @@ -43,18 +88,8 @@ namespace psemek::io return std::feof(file_) != 0; } - static char const * fopen_write_mode(unsigned flags) - { - switch (flags) - { - case 0: return "wb"; - case file_ostream::append: return "ab"; - default: throw util::exception("Unknown file_ostream open flags"); - } - } - file_ostream::file_ostream(std::filesystem::path const & path, unsigned flags) - : file_{safe_fopen(path.c_str(), fopen_write_mode(flags))} + : file_{safe_fopen(path, fopen_write_mode(flags))} {} void file_ostream::reset() From d41c53193d4e0310a1820a452ef686e92dcf2eb7 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Tue, 4 Mar 2025 19:59:04 +0300 Subject: [PATCH 022/141] Use wide strings in util::executable_path() --- libs/util/source/executable_path.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/util/source/executable_path.cpp b/libs/util/source/executable_path.cpp index 83415df8..b2f3e9f6 100644 --- a/libs/util/source/executable_path.cpp +++ b/libs/util/source/executable_path.cpp @@ -14,10 +14,10 @@ namespace psemek::util std::filesystem::path executable_path() { #if defined WIN32 - std::string result(256, '\0'); + std::wstring result(256, '\0'); while (true) { - GetModuleFileNameA(NULL, result.data(), result.size()); + GetModuleFileNameW(NULL, result.data(), result.size()); auto error = GetLastError(); if (error == ERROR_SUCCESS) break; From 2e28b3ffc6f4e98cb7a5d73d15414dcc7a8c4d0a Mon Sep 17 00:00:00 2001 From: lisyarus Date: Tue, 4 Mar 2025 19:59:38 +0300 Subject: [PATCH 023/141] Use FT_New_Memory_Face for freetype fonts to fix loading non-ascii paths on Windows --- libs/fonts/source/freetype.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libs/fonts/source/freetype.cpp b/libs/fonts/source/freetype.cpp index 615c3cde..f27dead1 100644 --- a/libs/fonts/source/freetype.cpp +++ b/libs/fonts/source/freetype.cpp @@ -71,6 +71,7 @@ namespace psemek::fonts struct face_shared { + util::blob data; wgpu::device device; FT_Face face; @@ -333,11 +334,12 @@ namespace psemek::fonts std::unique_ptr load_freetype_font(wgpu::device device, std::filesystem::path const & path) { - FT_Face face; - auto path_str = path.string(); - ft_check_result(FT_New_Face(ft_library(), path_str.data(), 0, &face), "Failed to load font " + path_str + ": "); + auto font_data = io::read_full(io::file_istream{path}); - auto result = std::make_unique(std::make_shared(device, face)); + FT_Face face; + ft_check_result(FT_New_Memory_Face(ft_library(), reinterpret_cast(font_data.data()), font_data.size(), 0, &face), util::to_string("Failed to load font ", path, ": ")); + + auto result = std::make_unique(std::make_shared(std::move(font_data), device, face)); log::debug() << "Loaded font " << result->name() << " (" << path << ")"; From 4dc8b6a183e2ce3aa40721a5c621ebc5b7d35c45 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Tue, 4 Mar 2025 20:00:03 +0300 Subject: [PATCH 024/141] Use _WIN32 macro to detect Windows (instead of other macro variants) --- libs/io/source/file_stream.cpp | 2 +- libs/sdl2/include/psemek/sdl2/discrete_gpu.hpp | 2 +- libs/util/source/executable_path.cpp | 4 ++-- libs/util/source/open_url.cpp | 3 +-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/libs/io/source/file_stream.cpp b/libs/io/source/file_stream.cpp index 089b9c71..5e0a5b7e 100644 --- a/libs/io/source/file_stream.cpp +++ b/libs/io/source/file_stream.cpp @@ -15,7 +15,7 @@ namespace psemek::io throw util::system_error(std::error_code{errno, std::system_category()}, "Failed to open " + path.string()); } -#ifdef __WIN32__ +#ifdef _WIN32 wchar_t const * fopen_read_mode() { return L"rb"; diff --git a/libs/sdl2/include/psemek/sdl2/discrete_gpu.hpp b/libs/sdl2/include/psemek/sdl2/discrete_gpu.hpp index a9157612..29ac99eb 100644 --- a/libs/sdl2/include/psemek/sdl2/discrete_gpu.hpp +++ b/libs/sdl2/include/psemek/sdl2/discrete_gpu.hpp @@ -1,6 +1,6 @@ #pragma once -#ifdef WIN32 +#ifdef _WIN32 #define PSEMEK_SDL2_DISCRETE_GPU \ extern "C" { __declspec(dllexport) unsigned long NvOptimusEnablement = 1; } \ diff --git a/libs/util/source/executable_path.cpp b/libs/util/source/executable_path.cpp index b2f3e9f6..ba8a9dc8 100644 --- a/libs/util/source/executable_path.cpp +++ b/libs/util/source/executable_path.cpp @@ -2,7 +2,7 @@ #include #include -#ifdef WIN32 +#ifdef _WIN32 #include #include #include @@ -13,7 +13,7 @@ namespace psemek::util std::filesystem::path executable_path() { -#if defined WIN32 +#if defined _WIN32 std::wstring result(256, '\0'); while (true) { diff --git a/libs/util/source/open_url.cpp b/libs/util/source/open_url.cpp index 2af6098f..c5d7c114 100644 --- a/libs/util/source/open_url.cpp +++ b/libs/util/source/open_url.cpp @@ -1,7 +1,6 @@ #include #include -#include namespace psemek::util { @@ -13,7 +12,7 @@ namespace psemek::util if (!url.starts_with("http")) url = "https://" + url; -#ifdef WIN32 +#ifdef _WIN32 url = "start " + url; ignore(std::system(url.data())); #endif From 42260d4d7d9967d712dafa383f6238641ad92ef6 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 6 Mar 2025 18:15:57 +0300 Subject: [PATCH 025/141] Replace template operator() of util::function with a fixed-arg version to put all implicit conversions & list initializations to the actual call site --- libs/util/include/psemek/util/function.hpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libs/util/include/psemek/util/function.hpp b/libs/util/include/psemek/util/function.hpp index f9cbea95..c96a8a62 100644 --- a/libs/util/include/psemek/util/function.hpp +++ b/libs/util/include/psemek/util/function.hpp @@ -74,8 +74,7 @@ namespace psemek::util return static_cast(vtable_); } - template - R operator()(Args1 && ... args) const; + R operator()(Args ... args) const; void reset(); @@ -168,13 +167,12 @@ namespace psemek::util } template - template - R function::operator()(Args1 && ... args) const + R function::operator()(Args ... args) const { if (!vtable_) throw empty_function_error{}; - return vtable_->call(const_cast(static_cast(&storage_)), std::forward(args)...); + return vtable_->call(const_cast(static_cast(&storage_)), std::forward(args)...); } template From e5811b61a0a8d6d4304f7b27a41ab920688b8cd6 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 6 Mar 2025 18:20:08 +0300 Subject: [PATCH 026/141] Fix triggering constructor & destructor callbacks when attaching/detaching ecs components --- libs/ecs/include/psemek/ecs/container.hpp | 32 ++++++++++++------- .../include/psemek/ecs/detail/callback.hpp | 2 +- libs/ecs/include/psemek/ecs/detail/table.hpp | 4 +-- .../ecs/include/psemek/ecs/detail/without.hpp | 12 +++++++ libs/ecs/source/detail/table.cpp | 12 +++---- 5 files changed, 42 insertions(+), 20 deletions(-) diff --git a/libs/ecs/include/psemek/ecs/container.hpp b/libs/ecs/include/psemek/ecs/container.hpp index f33ab6f1..a09f73a8 100644 --- a/libs/ecs/include/psemek/ecs/container.hpp +++ b/libs/ecs/include/psemek/ecs/container.hpp @@ -479,6 +479,9 @@ namespace psemek::ecs for (auto const & uuid : attached_uuid_set) uuids.push_back(uuid); + if (archetype_changed) + data->table->trigger_destructors(*this, data->row, attached_uuid_set, {}); + auto table = table_container_.get(uuids); if (!table) @@ -508,7 +511,7 @@ namespace psemek::ecs ((accessor.get>() = std::forward(components)), ...); if (archetype_changed) - table->trigger_constructors(*this, data->row, attached_uuid_set); + table->trigger_constructors(*this, data->row, attached_uuid_set, {}); attached_uuid_set.clear(); uuids.clear(); @@ -530,10 +533,9 @@ namespace psemek::ecs auto * data = entity_list_.get_entities().begin() + entity.id; for (auto const & column : data->table->columns()) { - if (detached_uuid_set.contains(column->uuid())) - detached_uuid_set.insert(column->uuid()); - else - uuids.push_back(column->uuid()); + auto const column_uuid = column->uuid(); + if (!detached_uuid_set.contains(column_uuid)) + uuids.push_back(column_uuid); } bool const archetype_changed = sizeof...(Components) > 0; @@ -541,7 +543,7 @@ namespace psemek::ecs auto table = table_container_.get(uuids); if (archetype_changed) - data->table->trigger_destructors(*this, data->row, detached_uuid_set); + data->table->trigger_destructors(*this, data->row, {}, detached_uuid_set); // Destructors could lead to reallocation of entity list data = entity_list_.get_entities().begin() + entity.id; @@ -568,6 +570,9 @@ namespace psemek::ecs data->row = new_row; } + if (archetype_changed) + table->trigger_constructors(*this, data->row, {}, detached_uuid_set); + detached_uuid_set.clear(); uuids.clear(); detached_uuid_set.clear(); @@ -664,9 +669,12 @@ namespace psemek::ecs auto id = next_constructor_id_++; auto constructor_factory = [function = std::move(function)](std::vector const & column_indices) -> detail::table_callback { - return [function, column_indices](container & container, detail::table & table, std::uint32_t row, util::hash_set const & attached_components, bool force){ + return [function, column_indices](container & container, detail::table & table, std::uint32_t row, util::hash_set const & attached_components, util::hash_set const & detached_components, bool force){ - bool const invoke = force || (detail::contains_helper::contains(attached_components) || ...); + bool const invoke = force + || (detail::contains_helper::contains(attached_components) || ...) + || (detail::contains_helper::contains_without(detached_components) || ...) + ; if (!invoke) return; @@ -699,9 +707,11 @@ namespace psemek::ecs auto id = next_destructor_id_++; auto destructor_factory = [function = std::move(function)](std::vector const & column_indices) -> detail::table_callback { - return [function, column_indices](container & container, detail::table & table, std::uint32_t row, util::hash_set const & detached_components, bool force){ - - bool const invoke = force || (detail::contains_helper::contains(detached_components) || ...); + return [function, column_indices](container & container, detail::table & table, std::uint32_t row, util::hash_set const & attached_components, util::hash_set const & detached_components, bool force){ + bool const invoke = force + || (detail::contains_helper::contains_without(attached_components) || ...) + || (detail::contains_helper::contains(detached_components) || ...) + ; if (!invoke) return; diff --git a/libs/ecs/include/psemek/ecs/detail/callback.hpp b/libs/ecs/include/psemek/ecs/detail/callback.hpp index 02fa9e61..cb154e11 100644 --- a/libs/ecs/include/psemek/ecs/detail/callback.hpp +++ b/libs/ecs/include/psemek/ecs/detail/callback.hpp @@ -16,7 +16,7 @@ namespace psemek::ecs struct table; - using table_callback = util::function const &, bool)>; + using table_callback = util::function const &, util::hash_set const &, bool)>; struct ordered_table_callback { diff --git a/libs/ecs/include/psemek/ecs/detail/table.hpp b/libs/ecs/include/psemek/ecs/detail/table.hpp index ad2edf8a..5dea109a 100644 --- a/libs/ecs/include/psemek/ecs/detail/table.hpp +++ b/libs/ecs/include/psemek/ecs/detail/table.hpp @@ -74,10 +74,10 @@ namespace psemek::ecs::detail void add_destructor(ordered_table_callback callback); void trigger_constructors(container & container, std::uint32_t row); - void trigger_constructors(container & container, std::uint32_t row, util::hash_set const & attached_components); + void trigger_constructors(container & container, std::uint32_t row, util::hash_set const & attached_components, util::hash_set const & detached_components); void trigger_destructors(container & container, std::uint32_t row); - void trigger_destructors(container & container, std::uint32_t row, util::hash_set const & detached_components); + void trigger_destructors(container & container, std::uint32_t row, util::hash_set const & attached_components, util::hash_set const & detached_components); std::size_t memory_usage() const; diff --git a/libs/ecs/include/psemek/ecs/detail/without.hpp b/libs/ecs/include/psemek/ecs/detail/without.hpp index 9841c3fd..ed4f895e 100644 --- a/libs/ecs/include/psemek/ecs/detail/without.hpp +++ b/libs/ecs/include/psemek/ecs/detail/without.hpp @@ -87,6 +87,12 @@ namespace psemek::ecs::detail { return container.contains(Component::uuid()); } + + template + static bool contains_without(Container const &) + { + return false; + } }; template @@ -97,6 +103,12 @@ namespace psemek::ecs::detail { return false; } + + template + static bool contains_without(Container const & container) + { + return container.contains(Component::uuid()); + } }; } diff --git a/libs/ecs/source/detail/table.cpp b/libs/ecs/source/detail/table.cpp index 9fdd4d38..27f8011f 100644 --- a/libs/ecs/source/detail/table.cpp +++ b/libs/ecs/source/detail/table.cpp @@ -161,25 +161,25 @@ namespace psemek::ecs::detail void table::trigger_constructors(container & container, std::uint32_t row) { for (auto const & callback : *constructors_) - callback.callback(container, *this, row, column_uuid_set_, true); + callback.callback(container, *this, row, column_uuid_set_, {}, true); } - void table::trigger_constructors(container & container, std::uint32_t row, util::hash_set const & attached_components) + void table::trigger_constructors(container & container, std::uint32_t row, util::hash_set const & attached_components, util::hash_set const & detached_components) { for (auto const & callback : *constructors_) - callback.callback(container, *this, row, attached_components, false); + callback.callback(container, *this, row, attached_components, detached_components, false); } void table::trigger_destructors(container & container, std::uint32_t row) { for (auto const & callback : *destructors_) - callback.callback(container, *this, row, column_uuid_set_, true); + callback.callback(container, *this, row, {}, column_uuid_set_, true); } - void table::trigger_destructors(container & container, std::uint32_t row, util::hash_set const & detached_components) + void table::trigger_destructors(container & container, std::uint32_t row, util::hash_set const & attached_components, util::hash_set const & detached_components) { for (auto const & callback : *destructors_) - callback.callback(container, *this, row, detached_components, false); + callback.callback(container, *this, row, attached_components, detached_components, false); } std::size_t table::memory_usage() const From 6a8a896aba1a4eeec25772d1cb773297ad1fcf45 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 4 Apr 2025 15:15:49 +0300 Subject: [PATCH 027/141] Remove bt logging --- CMakeLists.txt | 2 -- libs/bt/CMakeLists.txt | 3 --- libs/bt/include/psemek/bt/log.hpp | 9 --------- 3 files changed, 14 deletions(-) delete mode 100644 libs/bt/include/psemek/bt/log.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d921795..3f68e2e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,8 +45,6 @@ option(PSEMEK_LEGACY_UI "Use legacy UI library" OFF) message(STATUS "Using backend ${PSEMEK_BACKEND}") -option(PSEMEK_BT_LOG "Add logging for behavior tree actions" OFF) - if(PSEMEK_BACKEND STREQUAL "ANDROID") set(PSEMEK_GL_API gles32) set(PSEMEK_GL_LIBRARIES GLESv3 EGL) diff --git a/libs/bt/CMakeLists.txt b/libs/bt/CMakeLists.txt index a03ab8c1..f39a5379 100644 --- a/libs/bt/CMakeLists.txt +++ b/libs/bt/CMakeLists.txt @@ -4,6 +4,3 @@ file(GLOB_RECURSE PSEMEK_BT_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "sour psemek_add_library(psemek-bt ${PSEMEK_BT_HEADERS} ${PSEMEK_BT_SOURCES}) target_include_directories(psemek-bt PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") target_link_libraries(psemek-bt PUBLIC psemek-util psemek-log) -if(PSEMEK_BT_LOG) - target_compile_definitions(psemek-bt PUBLIC -DPSEMEK_BT_LOG=1) -endif() diff --git a/libs/bt/include/psemek/bt/log.hpp b/libs/bt/include/psemek/bt/log.hpp deleted file mode 100644 index 3d785abc..00000000 --- a/libs/bt/include/psemek/bt/log.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#ifdef PSEMEK_BT_LOG -#include -#define bt_log ::psemek::log::debug -#else -#include -#define bt_log ::psemek::util::null_ostream -#endif From 57f9f7331cb73a128752fce070ecbe18dcb9843b Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 4 Apr 2025 21:07:19 +0300 Subject: [PATCH 028/141] Add std::format formatters for basic math types --- libs/math/include/psemek/math/box.hpp | 32 ++++++++++++++++++++ libs/math/include/psemek/math/interval.hpp | 20 ++++++++++++ libs/math/include/psemek/math/point.hpp | 27 +++++++++++++++++ libs/math/include/psemek/math/quaternion.hpp | 21 +++++++++++++ libs/math/include/psemek/math/vector.hpp | 26 ++++++++++++++++ 5 files changed, 126 insertions(+) diff --git a/libs/math/include/psemek/math/box.hpp b/libs/math/include/psemek/math/box.hpp index 18038144..09aa56f9 100644 --- a/libs/math/include/psemek/math/box.hpp +++ b/libs/math/include/psemek/math/box.hpp @@ -5,6 +5,7 @@ #include #include +#include namespace psemek::math { @@ -348,3 +349,34 @@ namespace psemek::math } } + +namespace std +{ + + template + struct formatter<::psemek::math::box, Char> + { + constexpr auto parse(std::format_parse_context & ctx) + { + return ctx.begin(); + } + + auto format(::psemek::math::point const & p, std::format_context & ctx) const + { + if constexpr (N == 0) + { + return std::format_to(ctx.out(), "()"); + } + else + { + auto out = ctx.out(); + out = std::format_to(out, "({}", p[0]); + for (std::size_t i = 1; i < p.dimension(); ++i) + out = std::format_to(out, ", {}", p[i]); + out = std::format_to(out, ")"); + return out; + } + } + }; + +} diff --git a/libs/math/include/psemek/math/interval.hpp b/libs/math/include/psemek/math/interval.hpp index 1cff3ffa..d2a55d4e 100644 --- a/libs/math/include/psemek/math/interval.hpp +++ b/libs/math/include/psemek/math/interval.hpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace psemek::math { @@ -302,3 +303,22 @@ namespace psemek::math } } + +namespace std +{ + + template + struct formatter<::psemek::math::interval, Char> + { + constexpr auto parse(std::format_parse_context & ctx) + { + return ctx.begin(); + } + + auto format(::psemek::math::interval const & i, std::format_context & ctx) const + { + return std::format_to(ctx.out(), "[{} .. {}]", i.min, i.max); + } + }; + +} diff --git a/libs/math/include/psemek/math/point.hpp b/libs/math/include/psemek/math/point.hpp index 461d6cec..7cb5d2bf 100644 --- a/libs/math/include/psemek/math/point.hpp +++ b/libs/math/include/psemek/math/point.hpp @@ -6,6 +6,7 @@ #include #include +#include namespace psemek::math { @@ -232,4 +233,30 @@ namespace std } }; + template + struct formatter<::psemek::math::point, Char> + { + constexpr auto parse(std::format_parse_context & ctx) + { + return ctx.begin(); + } + + auto format(::psemek::math::point const & p, std::format_context & ctx) const + { + if constexpr (N == 0) + { + return std::format_to(ctx.out(), "()"); + } + else + { + auto out = ctx.out(); + out = std::format_to(out, "({}", p[0]); + for (std::size_t i = 1; i < p.dimension(); ++i) + out = std::format_to(out, ", {}", p[i]); + out = std::format_to(out, ")"); + return out; + } + } + }; + } diff --git a/libs/math/include/psemek/math/quaternion.hpp b/libs/math/include/psemek/math/quaternion.hpp index a86b7303..3b644990 100644 --- a/libs/math/include/psemek/math/quaternion.hpp +++ b/libs/math/include/psemek/math/quaternion.hpp @@ -5,6 +5,8 @@ #include #include +#include + namespace psemek::math { @@ -338,3 +340,22 @@ namespace psemek::math } } + +namespace std +{ + + template + struct formatter<::psemek::math::quaternion, Char> + { + constexpr auto parse(std::format_parse_context & ctx) + { + return ctx.begin(); + } + + auto format(::psemek::math::quaternion const & q, std::format_context & ctx) const + { + return std::format_to(ctx.out(), "{}", q.coords); + } + }; + +} diff --git a/libs/math/include/psemek/math/vector.hpp b/libs/math/include/psemek/math/vector.hpp index a3c79def..5c9d9709 100644 --- a/libs/math/include/psemek/math/vector.hpp +++ b/libs/math/include/psemek/math/vector.hpp @@ -522,4 +522,30 @@ namespace std } }; + template + struct formatter<::psemek::math::vector, Char> + { + constexpr auto parse(std::format_parse_context & ctx) + { + return ctx.begin(); + } + + auto format(::psemek::math::vector const & v, std::format_context & ctx) const + { + if constexpr (N == 0) + { + return std::format_to(ctx.out(), "()"); + } + else + { + auto out = ctx.out(); + out = std::format_to(out, "({}", v[0]); + for (std::size_t i = 1; i < v.dimension(); ++i) + out = std::format_to(out, ", {}", v[i]); + out = std::format_to(out, ")"); + return out; + } + } + }; + } From 8099e928dc5b35bacf10f0e1119e675d42df6883 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 4 Apr 2025 21:07:31 +0300 Subject: [PATCH 029/141] Add std::format formatter for ecs::handle --- libs/ecs/include/psemek/ecs/handle.hpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/libs/ecs/include/psemek/ecs/handle.hpp b/libs/ecs/include/psemek/ecs/handle.hpp index ef2c054d..2066ad01 100644 --- a/libs/ecs/include/psemek/ecs/handle.hpp +++ b/libs/ecs/include/psemek/ecs/handle.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace psemek::ecs { @@ -46,4 +47,18 @@ namespace std } }; + template + struct formatter<::psemek::ecs::handle, Char> + { + constexpr auto parse(std::format_parse_context & ctx) + { + return ctx.begin(); + } + + auto format(::psemek::ecs::handle const & handle, std::format_context & ctx) const + { + return std::format_to(ctx.out(), "({},{})", handle.id, handle.epoch); + } + }; + } From c8a58d1de4ea7172ade878fed76cfd749984ecd9 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 4 Apr 2025 21:07:49 +0300 Subject: [PATCH 030/141] Add sqlite-based event journaling library --- CMakeLists.txt | 5 + libs/journal/CMakeLists.txt | 10 + libs/journal/include/psemek/journal/event.hpp | 31 +++ .../include/psemek/journal/journal.hpp | 27 +++ .../include/psemek/journal/log_event.hpp | 25 +++ libs/journal/source/event.cpp | 15 ++ libs/journal/source/journal.cpp | 181 ++++++++++++++++++ 7 files changed, 294 insertions(+) create mode 100644 libs/journal/CMakeLists.txt create mode 100644 libs/journal/include/psemek/journal/event.hpp create mode 100644 libs/journal/include/psemek/journal/journal.hpp create mode 100644 libs/journal/include/psemek/journal/log_event.hpp create mode 100644 libs/journal/source/event.cpp create mode 100644 libs/journal/source/journal.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f68e2e0..43d106e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,11 @@ if(PSEMEK_USE_FREETYPE) list(APPEND PSEMEK_DEFINITIONS "-DPSEMEK_USE_FREETYPE=1") endif() +option(PSEMEK_USE_SQLITE "Include sqlite3 database support for journaling" OFF) +if(PSEMEK_USE_SQLITE) + list(APPEND PSEMEK_DEFINITIONS "-DPSEMEK_USE_SQLITE=1") +endif() + set(PSEMEK_CXX_FLAGS) if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")) list(APPEND PSEMEK_CXX_FLAGS -Wall -Werror -Wextra -pedantic -Wno-narrowing -Wno-sign-compare) diff --git a/libs/journal/CMakeLists.txt b/libs/journal/CMakeLists.txt new file mode 100644 index 00000000..8cca1e28 --- /dev/null +++ b/libs/journal/CMakeLists.txt @@ -0,0 +1,10 @@ +file(GLOB_RECURSE PSEMEK_JOURNAL_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "include/*.hpp") +file(GLOB_RECURSE PSEMEK_JOURNAL_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "source/*.cpp") + +if(PSEMEK_USE_SQLITE) + find_package(SQLite3 REQUIRED) +endif() + +psemek_add_library(psemek-journal ${PSEMEK_JOURNAL_HEADERS} ${PSEMEK_JOURNAL_SOURCES}) +target_include_directories(psemek-journal PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ${SQLite3_INCLUDE_DIRS}) +target_link_libraries(psemek-journal PUBLIC psemek-util psemek-log SQLite::SQLite3) diff --git a/libs/journal/include/psemek/journal/event.hpp b/libs/journal/include/psemek/journal/event.hpp new file mode 100644 index 00000000..7a8a7272 --- /dev/null +++ b/libs/journal/include/psemek/journal/event.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +namespace psemek::journal +{ + + struct event_metadata + { + std::string source_file; + int source_line; + std::string name; + std::vector columns; + }; + + struct event_data + { + std::string time; + std::vector values; + }; + + struct event + { + event_metadata metadata; + event_data data; + }; + + std::string current_time(); + +} diff --git a/libs/journal/include/psemek/journal/journal.hpp b/libs/journal/include/psemek/journal/journal.hpp new file mode 100644 index 00000000..01c3496b --- /dev/null +++ b/libs/journal/include/psemek/journal/journal.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +#include + +namespace psemek::journal +{ + + struct journal + { + journal(std::filesystem::path const & path); + ~journal(); + + bool enabled() const; + void set_enabled(bool enabled); + + void log_event(event const & event); + + std::vector> select(std::string const & query); + + private: + psemek_declare_pimpl + }; + +} diff --git a/libs/journal/include/psemek/journal/log_event.hpp b/libs/journal/include/psemek/journal/log_event.hpp new file mode 100644 index 00000000..7804f5ee --- /dev/null +++ b/libs/journal/include/psemek/journal/log_event.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include + +#include + +#define psemek_journal_log_event_extract_key(key, value) key +#define psemek_journal_log_event_extract_value(key, value) std::format("\"{}\"", value) + +#define psemek_journal_log_event_single_attribute_key(r, data, elem) psemek_journal_log_event_extract_key elem, +#define psemek_journal_log_event_single_attribute_value(r, data, elem) psemek_journal_log_event_extract_value elem, + +#define psemek_journal_log_event(JOURNAL, NAME, ATTRIBUTES) \ + if ((JOURNAL).enabled()) \ + (JOURNAL).log_event({{ \ + .source_file = __FILE__, \ + .source_line = __LINE__, \ + .name = NAME, \ + .columns = { BOOST_PP_SEQ_FOR_EACH(psemek_journal_log_event_single_attribute_key, _, ATTRIBUTES) }, \ + }, { \ + .time = ::psemek::journal::current_time(), \ + .values = { BOOST_PP_SEQ_FOR_EACH(psemek_journal_log_event_single_attribute_value, _, ATTRIBUTES) }, \ + }}) diff --git a/libs/journal/source/event.cpp b/libs/journal/source/event.cpp new file mode 100644 index 00000000..4ebc68c6 --- /dev/null +++ b/libs/journal/source/event.cpp @@ -0,0 +1,15 @@ +#include + +#include +#include + +namespace psemek::journal +{ + + std::string current_time() + { + const auto now = std::chrono::high_resolution_clock::now(); + return std::format("{:%FT%TZ}", now); + } + +} diff --git a/libs/journal/source/journal.cpp b/libs/journal/source/journal.cpp new file mode 100644 index 00000000..7ced403f --- /dev/null +++ b/libs/journal/source/journal.cpp @@ -0,0 +1,181 @@ +#include +#include +#include +#include + +#include +#include + +#ifdef PSEMEK_USE_SQLITE + #include +#endif + +namespace psemek::journal +{ + +#ifdef PSEMEK_USE_SQLITE + + struct journal::impl + { + impl(std::filesystem::path const & path) + { + if (sqlite3_open(path.c_str(), &database) != SQLITE_OK) + { + auto error_message = std::format("Failed to open sqlite database at {}: {}", path.c_str(), sqlite3_errmsg(database)); + sqlite3_close(database); + throw util::exception(std::move(error_message)); + } + } + + ~impl() + { + sqlite3_close(database); + } + + void log_event(event const & event) + { + auto table_it = tables.find(event.metadata.name); + + if (table_it == tables.end()) + { + std::ostringstream command; + + command << "PRAGMA synchronous = OFF;"; + + command << "CREATE TABLE IF NOT EXISTS " << event.metadata.name << "(timestamp TEXT"; + + table_it = tables.insert({event.metadata.name, event.metadata}).first; + table_it->second.source_file = event.metadata.source_file; + table_it->second.source_line = event.metadata.source_line; + + for (auto const & attribute : event.metadata.columns) + command << ", " << attribute << " TEXT"; + + command << ");"; + + auto command_str = command.str(); + + char * error = nullptr; + if (sqlite3_exec(database, command_str.data(), nullptr, nullptr, &error) != SQLITE_OK) + { + auto error_message = std::string("Failed to create sqlite table: ") + error + std::string("\nSQL command: ") + command_str; + sqlite3_free(error); + throw util::exception(std::move(error_message)); + } + } + + { + std::ostringstream command; + + command << "PRAGMA synchronous = OFF; INSERT INTO " << event.metadata.name << "(timestamp"; + + for (auto const & attribute : event.metadata.columns) + command << ", " << attribute; + + command << ") VALUES (\"" << event.data.time << "\""; + + for (auto const & attribute : event.data.values) + command << ", " << attribute; + + command << ");"; + + auto command_str = command.str(); + + char * error = nullptr; + if (sqlite3_exec(database, command_str.data(), nullptr, nullptr, &error) != SQLITE_OK) + { + auto error_message = std::string("Failed to insert into sqlite table: ") + error + std::string("\nSQL command: ") + command_str; + sqlite3_free(error); + throw util::exception(std::move(error_message)); + } + } + } + + std::vector> select(std::string const & query) + { + std::vector> result; + + for (auto const & table : tables) + { + std::string table_query = "SELECT * FROM " + table.first + (query.empty() ? "" : " WHERE ") + query + ";"; + + struct context + { + std::vector> * result; + event_metadata const * metadata; + }; + + context ctx{&result, &table.second}; + + auto callback = [](void * pcontext, int columns, char ** values, char **) -> int + { + auto & ctx = *(context *)pcontext; + + auto & event = ctx.result->emplace_back(std::pair{ctx.metadata, event_data{}}); + + event.second.time = values[0]; + + for (int i = 1; i < columns; ++i) + event.second.values.push_back(values[i]); + + return 0; + }; + + char * error = nullptr; + if (sqlite3_exec(database, table_query.c_str(), callback, &ctx, &error) != SQLITE_OK) + sqlite3_free(error); + } + + std::sort(result.begin(), result.end(), [](auto const & e1, auto const & e2){ return e1.second.time < e2.second.time; }); + + return result; + } + + sqlite3 * database = nullptr; + bool enabled = true; + + util::hash_map tables; + }; + +#else + + struct journal::impl + { + impl(std::filesystem::path const &) + {} + + void log_event(event const &) + {} + + bool enabled = true; + }; + +#endif + + journal::journal(std::filesystem::path const & path) + : pimpl_(make_impl(path)) + {} + + journal::~journal() = default; + + bool journal::enabled() const + { + return impl().enabled; + } + + void journal::set_enabled(bool enabled) + { + impl().enabled = enabled; + } + + void journal::log_event(event const & event) + { + impl().log_event(event); + } + + std::vector> journal::select(std::string const & query) + { + return impl().select(query); + } + +} From 472d095e2d9a8401b7bbabd8616b9aec4d6ede5e Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 6 Apr 2025 21:55:29 +0300 Subject: [PATCH 031/141] Support setting individual pixels in vecr::renderer --- libs/vecr/include/psemek/vecr/renderer.hpp | 2 ++ libs/vecr/source/renderer.cpp | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/libs/vecr/include/psemek/vecr/renderer.hpp b/libs/vecr/include/psemek/vecr/renderer.hpp index 3f5890ec..ae9fceed 100644 --- a/libs/vecr/include/psemek/vecr/renderer.hpp +++ b/libs/vecr/include/psemek/vecr/renderer.hpp @@ -34,6 +34,8 @@ namespace psemek::vecr void clear(gfx::color_rgba const & color = {0, 0, 0, 0}); + void set_pixel(math::vector const & coords, gfx::color_rgba const & color); + void draw(primitive const & primitive); private: diff --git a/libs/vecr/source/renderer.cpp b/libs/vecr/source/renderer.cpp index ee4b294f..e6eb5789 100644 --- a/libs/vecr/source/renderer.cpp +++ b/libs/vecr/source/renderer.cpp @@ -54,6 +54,17 @@ namespace psemek::vecr need_resolve_ = false; } + void renderer::set_pixel(math::vector const & coords, gfx::color_rgba const & color) + { + for (std::size_t ty = 0; ty < samples_; ++ty) + { + for (std::size_t tx = 0; tx < samples_; ++tx) + { + canvas_(coords[0] * samples_ + tx, coords[1] * samples_ + ty) = color; + } + } + } + void renderer::draw(primitive const & primitive) { float const aa = primitive.blur / 2.f; From 5fcd2a171f0eacbd66f24f179ec99c3952bfa82c Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 6 Apr 2025 21:55:40 +0300 Subject: [PATCH 032/141] Support using a lambda in vecr colorizer --- libs/vecr/include/psemek/vecr/colorizer.hpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/libs/vecr/include/psemek/vecr/colorizer.hpp b/libs/vecr/include/psemek/vecr/colorizer.hpp index c3533b00..94d4991e 100644 --- a/libs/vecr/include/psemek/vecr/colorizer.hpp +++ b/libs/vecr/include/psemek/vecr/colorizer.hpp @@ -50,9 +50,16 @@ namespace psemek::vecr template any_colorizer & operator = (Colorizer && colorizer) { - colorizer_ = [colorizer = std::forward(colorizer)](math::point const & p, sdf_sample const & sample){ - return colorize(colorizer, p, sample); - }; + if constexpr (std::is_invocable_v const &, sdf_sample const &>) + { + colorizer_ = std::move(colorizer); + } + else + { + colorizer_ = [colorizer = std::forward(colorizer)](math::point const & p, sdf_sample const & sample){ + return colorize(colorizer, p, sample); + }; + } return *this; } From 944d1374a55b86bb4cb2315e0b787db1c2e2283d Mon Sep 17 00:00:00 2001 From: lisyarus Date: Mon, 7 Apr 2025 11:09:01 +0300 Subject: [PATCH 033/141] Fix handling text input events in app::scene_application --- libs/app/include/psemek/app/application_base.hpp | 1 + libs/app/include/psemek/app/scene_application.hpp | 1 + libs/app/source/application_base.cpp | 3 +++ libs/app/source/scene_application.cpp | 5 +++++ 4 files changed, 10 insertions(+) diff --git a/libs/app/include/psemek/app/application_base.hpp b/libs/app/include/psemek/app/application_base.hpp index 2dfa0448..637e22a1 100644 --- a/libs/app/include/psemek/app/application_base.hpp +++ b/libs/app/include/psemek/app/application_base.hpp @@ -18,6 +18,7 @@ namespace psemek::app void on_event(touch_up_event const &) override; void on_event(touch_move_event const &) override; void on_event(key_event const &) override; + void on_event(text_input_event const &) override; void stop() override; bool running() const override; diff --git a/libs/app/include/psemek/app/scene_application.hpp b/libs/app/include/psemek/app/scene_application.hpp index 129af5a9..84f37eed 100644 --- a/libs/app/include/psemek/app/scene_application.hpp +++ b/libs/app/include/psemek/app/scene_application.hpp @@ -18,6 +18,7 @@ namespace psemek::app void on_event(touch_up_event const &) override; void on_event(touch_move_event const &) override; void on_event(key_event const &) override; + void on_event(text_input_event const &) override; void stop() override; diff --git a/libs/app/source/application_base.cpp b/libs/app/source/application_base.cpp index a6352ac9..53b56213 100644 --- a/libs/app/source/application_base.cpp +++ b/libs/app/source/application_base.cpp @@ -48,6 +48,9 @@ namespace psemek::app apply(state_, event); } + void application_base::on_event(text_input_event const &) + {} + void application_base::stop() { running_ = false; diff --git a/libs/app/source/scene_application.cpp b/libs/app/source/scene_application.cpp index 5f18715d..0936bec2 100644 --- a/libs/app/source/scene_application.cpp +++ b/libs/app/source/scene_application.cpp @@ -48,6 +48,11 @@ namespace psemek::app on_event_impl(event); } + void scene_application::on_event(text_input_event const & event) + { + on_event_impl(event); + } + void scene_application::stop() { if (auto scene = current_scene()) From 0034e6a9f1dfd2f6fcad477f19ea98128117bfa1 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Mon, 7 Apr 2025 11:10:12 +0300 Subject: [PATCH 034/141] Disable text input at application start in SDL2 backend --- libs/sdl2/source/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/sdl2/source/main.cpp b/libs/sdl2/source/main.cpp index 989dfd3e..2849d16d 100644 --- a/libs/sdl2/source/main.cpp +++ b/libs/sdl2/source/main.cpp @@ -72,6 +72,8 @@ int main(int argc, char ** argv) try log::info() << "Started in " << util::pretty(clock.duration(), std::chrono::milliseconds{1}); log::info() << "Running"; + SDL_StopTextInput(); + while (application->running()) { if (sdl2::poll_events(*application)) From 083e5841aace550478bfa1ea4a2aea4c5a896722 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 10 Apr 2025 11:57:50 +0300 Subject: [PATCH 035/141] Add ecs::container::finally mechanism --- libs/ecs/include/psemek/ecs/container.hpp | 77 +++++++++++++++++++++++ libs/ecs/source/container.cpp | 27 ++++++++ 2 files changed, 104 insertions(+) diff --git a/libs/ecs/include/psemek/ecs/container.hpp b/libs/ecs/include/psemek/ecs/container.hpp index a09f73a8..26228e54 100644 --- a/libs/ecs/include/psemek/ecs/container.hpp +++ b/libs/ecs/include/psemek/ecs/container.hpp @@ -385,6 +385,30 @@ namespace psemek::ecs template void watch(Function && function); + /** Call a callback after exiting all currently executing ECS container methods. + * + * If no ECS container method is currently executed, call the callback immediately instead. + * + * The function must have one of the following signatures: + * void() + * void(container) + * + * This method is meant to be called from inside other callbacks (typically + * constructors and destructors) and can be used to add extra modifications + * to entities that don't clash with other callbacks and archetype changes. + * + * Generally, if a constructor, destructor, apply or batch apply function + * changes the archetype of some entities, it should do so using `finally` + * instead of doing that directly. + * + * @param function A callback to be called before the topmost currently executing + * ECS container method returns. + * @warning If a callback adds new callbacks via `finally`, those will also get executed, + * which potentially leads to an infinite loop. + */ + template + void finally(Function && function); + template Index & index(Args && ... args); @@ -412,10 +436,14 @@ namespace psemek::ecs util::object_pool> uuid_list_pool_; util::object_pool> uuid_set_pool_; + std::size_t method_recursion_depth_ = 0; + std::vector> finally_callbacks_; + detail::table * insert_table(std::vector> columns); void do_destroy(handle entity); void remove_row(detail::table & table, std::uint32_t row, util::span entities); void finalize_iteration(detail::table & table); + void finalize_method(); }; template @@ -429,6 +457,8 @@ namespace psemek::ecs { static_assert(detail::all_different_types_v...>, "all component types must be different"); + ++method_recursion_depth_; + (register_component>(), ...); detail::component_uuid_helper...> uuids; @@ -456,6 +486,10 @@ namespace psemek::ecs table->trigger_constructors(*this, row); + finalize_method(); + + --method_recursion_depth_; + return handle; } @@ -464,6 +498,8 @@ namespace psemek::ecs { static_assert(detail::all_different_types_v...>, "all component types must be different"); + ++method_recursion_depth_; + (register_component(), ...); auto uuids = uuid_list_pool_.get(); @@ -518,6 +554,10 @@ namespace psemek::ecs uuid_set_pool_.put(std::move(attached_uuid_set)); uuid_list_pool_.put(std::move(uuids)); + + finalize_method(); + + --method_recursion_depth_; } template @@ -525,6 +565,8 @@ namespace psemek::ecs { static_assert(detail::all_different_types_v...>, "all component types must be different"); + ++method_recursion_depth_; + auto detached_uuid_set = uuid_set_pool_.get(); (detached_uuid_set.insert(std::remove_const_t::uuid()), ...); @@ -579,6 +621,10 @@ namespace psemek::ecs uuid_list_pool_.put(std::move(uuids)); uuid_set_pool_.put(std::move(detached_uuid_set)); + + finalize_method(); + + --method_recursion_depth_; } template @@ -595,6 +641,8 @@ namespace psemek::ecs { static_assert(detail::all_different_types_v...>, "all component types must be different"); + ++method_recursion_depth_; + using invocable_type = typename detail::filter_with, Function>::type; static_assert(invocable_type::value, "function is not invocable with these components"); @@ -622,6 +670,10 @@ namespace psemek::ecs finalize_iteration(*entry.table); } + finalize_method(); + + --method_recursion_depth_; + return cache; } @@ -630,6 +682,8 @@ namespace psemek::ecs { static_assert(detail::all_different_types_v...>, "all component types must be different"); + ++method_recursion_depth_; + using invocable_type = typename detail::filter_with, Function>::type; static_assert(invocable_type::value, "function is not batch-invocable with these components"); @@ -654,6 +708,10 @@ namespace psemek::ecs finalize_iteration(*entry.table); } + finalize_method(); + + --method_recursion_depth_; + return cache; } @@ -732,6 +790,25 @@ namespace psemek::ecs entry.table->add_destructor({id, destructor_factory(entry.columns_indices)}); } + template + void container::finally(Function && function) + { + util::function wrapper; + if constexpr (std::is_invocable_v) + { + wrapper = std::move(function); + } + else + { + wrapper = [function = std::move(function)](ecs::container &){ function(); }; + } + + if (method_recursion_depth_ == 0) + wrapper(*this); + else + finally_callbacks_.push_back(std::move(wrapper)); + } + template Index & container::index(Args && ... args) { diff --git a/libs/ecs/source/container.cpp b/libs/ecs/source/container.cpp index ac0ba484..788ab5f3 100644 --- a/libs/ecs/source/container.cpp +++ b/libs/ecs/source/container.cpp @@ -15,11 +15,17 @@ namespace psemek::ecs { assert(alive(entity)); + ++method_recursion_depth_; + auto const data = entity_list_.get_entities()[entity.id]; data.table->trigger_destructors(*this, data.row); do_destroy(entity); entity_list_.destroy(entity.id); + + finalize_method(); + + --method_recursion_depth_; } accessor container::get(handle entity) @@ -52,6 +58,8 @@ namespace psemek::ecs std::optional container::try_clone(handle entity) { + ++method_recursion_depth_; + auto const data = entity_list_.get_entities()[entity.id]; if (!data.table->non_copyable_components().empty()) return std::nullopt; @@ -68,6 +76,10 @@ namespace psemek::ecs table->trigger_constructors(*this, row); + finalize_method(); + + --method_recursion_depth_; + return handle; } @@ -125,6 +137,21 @@ namespace psemek::ecs } } + void container::finalize_method() + { + if (method_recursion_depth_ > 1) + return; + + while (!finally_callbacks_.empty()) + { + auto callbacks = std::move(finally_callbacks_); + finally_callbacks_.clear(); + + for (auto & callback : callbacks) + callback(*this); + } + } + std::size_t container::entity_count() { return entity_list_.size(); From 9f0f07885d7f14a37858ff0a5c166d1f067da183 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 13 Apr 2025 00:43:42 +0300 Subject: [PATCH 036/141] Show unknown characters as '?' in freetype fonts --- libs/fonts/source/freetype.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/fonts/source/freetype.cpp b/libs/fonts/source/freetype.cpp index f27dead1..373636fe 100644 --- a/libs/fonts/source/freetype.cpp +++ b/libs/fonts/source/freetype.cpp @@ -185,6 +185,9 @@ namespace psemek::fonts else { glyph_id = FT_Get_Char_Index(face, ch); + if (glyph_id == 0) + glyph_id = FT_Get_Char_Index(face, '?'); + glyph_mapping_[ch] = glyph_id; } From 6cf5eb008beb4ee8d413242c3112b86f69763cbd Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 13 Apr 2025 12:42:51 +0300 Subject: [PATCH 037/141] Support operator--() for util::utf8_iterator --- libs/util/include/psemek/util/unicode.hpp | 6 +++++- libs/util/source/unicode.cpp | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/libs/util/include/psemek/util/unicode.hpp b/libs/util/include/psemek/util/unicode.hpp index 31a2dc94..8cbb914c 100644 --- a/libs/util/include/psemek/util/unicode.hpp +++ b/libs/util/include/psemek/util/unicode.hpp @@ -17,12 +17,16 @@ namespace psemek::util using pointer = void; using reference = void; using difference_type = std::ptrdiff_t; - using iterator_category = std::input_iterator_tag; + using iterator_category = std::bidirectional_iterator_tag; char const * ptr; utf8_iterator & operator ++(); utf8_iterator operator ++(int); + + utf8_iterator & operator --(); + utf8_iterator operator --(int); + char32_t operator *() const; }; diff --git a/libs/util/source/unicode.cpp b/libs/util/source/unicode.cpp index 2b44ec43..e7fa9ef0 100644 --- a/libs/util/source/unicode.cpp +++ b/libs/util/source/unicode.cpp @@ -82,6 +82,21 @@ namespace psemek::util return copy; } + utf8_iterator & utf8_iterator::operator --() + { + --ptr; + while (is_middle_byte(*ptr)) + --ptr; + return *this; + } + + utf8_iterator utf8_iterator::operator --(int) + { + utf8_iterator copy(*this); + --(*this); + return copy; + } + char32_t utf8_iterator::operator *() const { if (is_1_byte(ptr[0])) From 7e2c2a4c6d5388338828250baf55a082643657ab Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 13 Apr 2025 12:43:40 +0300 Subject: [PATCH 038/141] Implement utf8 encoding and replace std::codecvt with custom code in utf32 <-> utf8 conversions --- libs/util/include/psemek/util/unicode.hpp | 2 + libs/util/source/unicode.cpp | 46 +++++++++++++++++++++-- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/libs/util/include/psemek/util/unicode.hpp b/libs/util/include/psemek/util/unicode.hpp index 8cbb914c..66cf3f3b 100644 --- a/libs/util/include/psemek/util/unicode.hpp +++ b/libs/util/include/psemek/util/unicode.hpp @@ -11,6 +11,8 @@ namespace psemek::util std::string to_utf8(std::u32string const & str); std::u32string from_utf8(std::string const & str); + char * append_utf8(char32_t c, char * out); + struct utf8_iterator { using value_type = char32_t; diff --git a/libs/util/source/unicode.cpp b/libs/util/source/unicode.cpp index e7fa9ef0..e981d70a 100644 --- a/libs/util/source/unicode.cpp +++ b/libs/util/source/unicode.cpp @@ -11,16 +11,54 @@ namespace psemek::util #pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif - using converter = std::wstring_convert, char32_t>; - std::string to_utf8(std::u32string const & str) { - return converter{}.to_bytes(str); + std::string result; + result.resize(str.size() * 4); + + char * out = result.data(); + for (char32_t c : str) + out = append_utf8(c, out); + result.resize(out - result.data()); + + return result; } std::u32string from_utf8(std::string const & str) { - return converter{}.from_bytes(str); + std::u32string result; + result.reserve(str.size()); + for (char32_t c : utf8_range(str)) + result.push_back(c); + return result; + } + + char * append_utf8(char32_t c, char * out) + { + if (c < 0x80) + { + *out++ = c; + } + else if (c < 0x800) + { + *out++ = 0b11000000 | (c >> 6); + *out++ = 0b10000000 | (c & 0b00111111); + } + else if (c < 0x10000) + { + *out++ = 0b11100000 | (c >> 12); + *out++ = 0b10000000 | ((c >> 6) & 0b00111111); + *out++ = 0b10000000 | (c & 0b00111111); + } + else + { + *out++ = 0b11110000 | (c >> 18); + *out++ = 0b10000000 | ((c >> 12) & 0b00111111); + *out++ = 0b10000000 | ((c >> 6) & 0b00111111); + *out++ = 0b10000000 | (c & 0b00111111); + } + + return out; } #ifdef __clang__ From 73abc03ae204a05dece211543e831e544c285796 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 13 Apr 2025 12:43:56 +0300 Subject: [PATCH 039/141] Add explicit util::utf8_range(std::string) constructor --- libs/util/include/psemek/util/unicode.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/util/include/psemek/util/unicode.hpp b/libs/util/include/psemek/util/unicode.hpp index 66cf3f3b..55036094 100644 --- a/libs/util/include/psemek/util/unicode.hpp +++ b/libs/util/include/psemek/util/unicode.hpp @@ -58,6 +58,10 @@ namespace psemek::util : str(str) {} + utf8_range(std::string const & str) + : str(str) + {} + utf8_range(utf8_range const &) = default; auto begin() const From e4222f35fffc723191367d1a053b29f1270d2844 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 13 Apr 2025 12:44:26 +0300 Subject: [PATCH 040/141] Replace util::blob copy constructor/assignment with .copy() method and mark all appropriate methods noexcept --- libs/util/include/psemek/util/blob.hpp | 87 ++++++++++++-------------- 1 file changed, 40 insertions(+), 47 deletions(-) diff --git a/libs/util/include/psemek/util/blob.hpp b/libs/util/include/psemek/util/blob.hpp index 5b581cb5..08520d1b 100644 --- a/libs/util/include/psemek/util/blob.hpp +++ b/libs/util/include/psemek/util/blob.hpp @@ -11,60 +11,57 @@ namespace psemek::util struct blob { - blob() = default; - blob(blob const & other); - blob(blob && other); + blob() noexcept = default; + blob(blob const & other) = delete; + blob(blob && other) noexcept; blob(std::size_t size); - blob(std::size_t size, std::unique_ptr data); - blob(std::size_t size, char * data); + blob(std::size_t size, std::unique_ptr data) noexcept; + blob(std::size_t size, char * data) noexcept; - blob & operator = (blob const & other); - blob & operator = (blob && other); + blob & operator = (blob const & other) = delete; + blob & operator = (blob && other) noexcept; ~blob() = default; - char * data() { return data_.get(); } - char const * data() const { return data_.get(); } + char * data() noexcept { return data_.get(); } + char const * data() const noexcept { return data_.get(); } - std::size_t size() const { return size_; } + std::size_t size() const noexcept { return size_; } - explicit operator bool() const { return static_cast(data_); } + explicit operator bool() const noexcept { return static_cast(data_); } void reset(); - std::unique_ptr release(); + std::unique_ptr release() noexcept; - void swap(blob & other); + void swap(blob & other) noexcept; using iterator = char *; using const_iterator = char const *; - char * begin() { return data(); } - char const * begin() const { return data(); } + char * begin() noexcept { return data(); } + char const * begin() const noexcept { return data(); } - char * end() { return data() + size(); } - char const * end() const { return data() + size(); } + char * end() noexcept { return data() + size(); } + char const * end() const noexcept { return data() + size(); } - char & operator[](std::size_t i) { return data()[i]; } - char const & operator[](std::size_t i) const { return data()[i]; } + char & operator[](std::size_t i) noexcept { return data()[i]; } + char const & operator[](std::size_t i) const noexcept { return data()[i]; } std::string string() const; - std::string_view string_view() const; + std::string_view string_view() const noexcept; - util::span span(); - util::span span() const; + util::span span() noexcept; + util::span span() const noexcept; + + blob copy() const; private: std::unique_ptr data_; std::size_t size_ = 0; }; - inline blob::blob(blob const & other) - { - *this = other; - } - - inline blob::blob(blob && other) + inline blob::blob(blob && other) noexcept : data_{std::move(other.data_)} , size_{other.size_} { @@ -76,28 +73,17 @@ namespace psemek::util , size_{size} {} - inline blob::blob(std::size_t size, std::unique_ptr data) + inline blob::blob(std::size_t size, std::unique_ptr data) noexcept : data_{std::move(data)} , size_{size} {} - inline blob::blob(std::size_t size, char * data) + inline blob::blob(std::size_t size, char * data) noexcept : data_{data} , size_{size} {} - inline blob & blob::operator=(blob const & other) - { - if (this != &other) - { - data_.reset(new char [other.size()]); - std::copy(other.begin(), other.end(), begin()); - size_ = other.size(); - } - return *this; - } - - inline blob & blob::operator=(blob && other) + inline blob & blob::operator = (blob && other) noexcept { blob(std::move(other)).swap(*this); return *this; @@ -109,13 +95,13 @@ namespace psemek::util size_ = 0; } - inline std::unique_ptr blob::release() + inline std::unique_ptr blob::release() noexcept { size_ = 0; return std::move(data_); } - inline void blob::swap(blob & other) + inline void blob::swap(blob & other) noexcept { std::swap(data_, other.data_); std::swap(size_, other.size_); @@ -126,19 +112,26 @@ namespace psemek::util return std::string(data_.get(), data_.get() + size_); } - inline std::string_view blob::string_view() const + inline std::string_view blob::string_view() const noexcept { return std::string_view(data_.get(), size_); } - inline util::span blob::span() + inline util::span blob::span() noexcept { return {data_.get(), size_}; } - inline util::span blob::span() const + inline util::span blob::span() const noexcept { return {data_.get(), size_}; } + inline blob blob::copy() const + { + std::unique_ptr data(new char [size_]); + std::copy(begin(), end(), data.get()); + return blob(size_, std::move(data)); + } + } From 6a8563cce97bf37bbf6f77a40bc68a19cad95cd4 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 13 Apr 2025 12:44:44 +0300 Subject: [PATCH 041/141] Support freetype font fallbacks by storing a sequence of fonts --- libs/fonts/include/psemek/fonts/font_v2.hpp | 3 + libs/fonts/source/freetype.cpp | 93 +++++++++++++++------ 2 files changed, 69 insertions(+), 27 deletions(-) diff --git a/libs/fonts/include/psemek/fonts/font_v2.hpp b/libs/fonts/include/psemek/fonts/font_v2.hpp index ed2976b8..53639b11 100644 --- a/libs/fonts/include/psemek/fonts/font_v2.hpp +++ b/libs/fonts/include/psemek/fonts/font_v2.hpp @@ -92,4 +92,7 @@ namespace psemek::fonts std::unique_ptr load_freetype_font(wgpu::device device, std::filesystem::path const & path); + // Font paths are specified in order + std::unique_ptr load_combined_freetype_font(wgpu::device device, std::vector const & paths); + } diff --git a/libs/fonts/source/freetype.cpp b/libs/fonts/source/freetype.cpp index 373636fe..30795f96 100644 --- a/libs/fonts/source/freetype.cpp +++ b/libs/fonts/source/freetype.cpp @@ -71,13 +71,20 @@ namespace psemek::fonts struct face_shared { - util::blob data; wgpu::device device; - FT_Face face; + + struct face_data + { + util::blob data; + FT_Face face; + }; + + std::vector faces = {}; ~face_shared() { - FT_Done_Face(face); + for (auto const & face : faces) + FT_Done_Face(face.face); } }; @@ -89,6 +96,9 @@ namespace psemek::fonts , type_(type) , size_(size) { + for (auto const & face : state_->faces) + ft_check_result(FT_Set_Char_Size(face.face, 0, size_ << 6, 0, 0), "Failed to select freetype face size: "); + font::shape("x", {}); xheight_ = -shaped_text_[0].position[1].min; shaped_text_.clear(); @@ -101,7 +111,7 @@ namespace psemek::fonts std::string_view name() const override { - return FT_Get_Postscript_Name(state_->face); + return FT_Get_Postscript_Name(state_->faces[0].face); } math::vector size() const override @@ -148,6 +158,12 @@ namespace psemek::fonts std::vector pages_; + struct glyph_mapping_data + { + std::uint32_t font_index; + std::uint32_t glyph_index; + }; + struct glyph_data { int page; @@ -156,8 +172,8 @@ namespace psemek::fonts math::vector advance; }; - util::hash_map glyph_mapping_; - util::hash_map glyphs_; + util::hash_map glyph_mapping_; + util::hash_map> glyphs_; std::vector shaped_text_; @@ -168,40 +184,53 @@ namespace psemek::fonts shaped_text_.clear(); - auto face = state_->face; - - ft_check_result(FT_Set_Char_Size(face, 0, size_ << 6, 0, 0), "Failed to select freetype face size: "); - bool need_update_pages = false; for (char32_t ch : string) { - std::uint32_t glyph_id; + glyph_mapping_data mapping{0, 0}; if (auto it = glyph_mapping_.find(ch); it != glyph_mapping_.end()) { - glyph_id = it->second; + mapping = it->second; } else { - glyph_id = FT_Get_Char_Index(face, ch); - if (glyph_id == 0) - glyph_id = FT_Get_Char_Index(face, '?'); + int glyph_index = 0; + for (int font_index = 0; font_index < state_->faces.size(); ++font_index) + { + glyph_index = FT_Get_Char_Index(state_->faces[font_index].face, ch); + if (glyph_index != 0) + { + mapping = {font_index, glyph_index}; + break; + } + } - glyph_mapping_[ch] = glyph_id; + if (glyph_index == 0) + { + mapping.font_index = 0; + mapping.glyph_index = FT_Get_Char_Index(state_->faces[0].face, '?'); + } + + glyph_mapping_[ch] = mapping; } + auto face = state_->faces[mapping.font_index].face; + glyph_data * data = nullptr; - if (auto it = glyphs_.find(glyph_id); it != glyphs_.end()) + auto & glyphs = glyphs_[mapping.font_index]; + + if (auto it = glyphs.find(mapping.glyph_index); it != glyphs.end()) { data = &(it->second); } else { - data = &glyphs_[glyph_id]; + data = &glyphs[mapping.glyph_index]; - FT_Load_Glyph(face, glyph_id, type_ == font_type::bitmap ? FT_LOAD_TARGET_LIGHT : FT_LOAD_DEFAULT); + FT_Load_Glyph(face, mapping.glyph_index, type_ == font_type::bitmap ? FT_LOAD_TARGET_LIGHT : FT_LOAD_DEFAULT); FT_Render_Glyph(face->glyph, type_ == font_type::bitmap ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_SDF); if (face->glyph->bitmap.width + 2 * padding > page_size || face->glyph->bitmap.rows + 2 * padding > page_size) @@ -210,7 +239,7 @@ namespace psemek::fonts data->part = {{{0, 0}, {0, 0}}}; data->offset = {0, 0}; data->advance = {size_, 0}; - log::warning() << "Glyph with ID " << glyph_id << " (U+" << std::hex << (int)ch << ") is larger than font atlas page: " << face->glyph->bitmap.width << "x" << face->glyph->bitmap.rows; + log::warning() << "Glyph with ID " << mapping.glyph_index << " (U+" << std::hex << (int)ch << ") is larger than font atlas page: " << face->glyph->bitmap.width << "x" << face->glyph->bitmap.rows; } else { @@ -319,7 +348,7 @@ namespace psemek::fonts std::string_view name() const override { - return FT_Get_Postscript_Name(state_->face); + return FT_Get_Postscript_Name(state_->faces[0].face); } std::unique_ptr create(font_type type, int size) override @@ -337,16 +366,26 @@ namespace psemek::fonts std::unique_ptr load_freetype_font(wgpu::device device, std::filesystem::path const & path) { - auto font_data = io::read_full(io::file_istream{path}); + return load_combined_freetype_font(device, {path}); + } - FT_Face face; - ft_check_result(FT_New_Memory_Face(ft_library(), reinterpret_cast(font_data.data()), font_data.size(), 0, &face), util::to_string("Failed to load font ", path, ": ")); + std::unique_ptr load_combined_freetype_font(wgpu::device device, std::vector const & paths) + { + auto result = std::make_shared(device); - auto result = std::make_unique(std::make_shared(std::move(font_data), device, face)); + for (auto const & path : paths) + { + auto font_data = io::read_full(io::file_istream{path}); - log::debug() << "Loaded font " << result->name() << " (" << path << ")"; + FT_Face face = nullptr; + ft_check_result(FT_New_Memory_Face(ft_library(), reinterpret_cast(font_data.data()), font_data.size(), 0, &face), util::to_string("Failed to load font ", path, ": ")); - return result; + result->faces.push_back({std::move(font_data), face}); + + log::debug() << "Loaded font " << FT_Get_Postscript_Name(face) << " (" << path << ")"; + } + + return std::make_unique(std::move(result)); } } From c75809aa75800ec045462726f98e746ee903a3bd Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 13 Apr 2025 17:36:05 +0300 Subject: [PATCH 042/141] Support move operations in prof::profiler --- libs/prof/include/psemek/prof/profiler.hpp | 12 ++++++-- libs/prof/source/profiler.cpp | 32 +++++++++++++++++++--- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/libs/prof/include/psemek/prof/profiler.hpp b/libs/prof/include/psemek/prof/profiler.hpp index 0d5293e2..bd1796c8 100644 --- a/libs/prof/include/psemek/prof/profiler.hpp +++ b/libs/prof/include/psemek/prof/profiler.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include namespace psemek::prof { @@ -13,8 +13,13 @@ namespace psemek::prof struct profiler { profiler(std::string const & name); + profiler(profiler && other); + profiler(profiler const & other) = delete; - ~ profiler(); + profiler & operator = (profiler && other); + profiler & operator = (profiler const & other) = delete; + + ~profiler(); static void start_frame(std::string const & name); static void push(std::string const & name, std::chrono::duration duration, std::size_t count = 1); @@ -33,6 +38,9 @@ namespace psemek::prof private: util::clock, std::chrono::high_resolution_clock> clock_; + bool moved_out_ = false; + + void report(); }; } diff --git a/libs/prof/source/profiler.cpp b/libs/prof/source/profiler.cpp index e99a6e65..a7f03b03 100644 --- a/libs/prof/source/profiler.cpp +++ b/libs/prof/source/profiler.cpp @@ -183,12 +183,25 @@ namespace psemek::prof clock_.restart(); } + profiler::profiler(profiler && other) + : clock_(other.clock_) + , moved_out_(other.moved_out_) + { + other.moved_out_ = true; + } + + profiler & profiler::operator = (profiler && other) + { + report(); + clock_ = other.clock_; + moved_out_ = other.moved_out_; + other.moved_out_ = true; + return *this; + } + profiler::~profiler() { - auto count = clock_.count(); - std::lock_guard lock{*(current->mutex)}; - current->execution_time.push(count); - current = current->parent; + report(); } void profiler::start_frame(std::string const & name) @@ -214,4 +227,15 @@ namespace psemek::prof current = current->parent; } + void profiler::report() + { + if (moved_out_) + return; + + auto count = clock_.count(); + std::lock_guard lock{*(current->mutex)}; + current->execution_time.push(count); + current = current->parent; + } + } From 93aa697347e47141d6b15bdbeb6fdf3055dfbcc0 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 13 Apr 2025 17:36:43 +0300 Subject: [PATCH 043/141] Support RAII-wrapping the execution of an ECS system into some context (e.g. for profiling) --- libs/ecs/include/psemek/ecs/dispatcher.hpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/libs/ecs/include/psemek/ecs/dispatcher.hpp b/libs/ecs/include/psemek/ecs/dispatcher.hpp index b534413e..a51aaa39 100644 --- a/libs/ecs/include/psemek/ecs/dispatcher.hpp +++ b/libs/ecs/include/psemek/ecs/dispatcher.hpp @@ -7,6 +7,19 @@ namespace psemek::ecs { + namespace detail + { + + struct default_context_factory + { + int operator()() const noexcept + { + return 0; + } + }; + + } + struct dispatcher { dispatcher() = default; @@ -42,12 +55,14 @@ namespace psemek::ecs }); } - template - void system(System system) + template + void system(System system, ContextFactory && context_factory = ContextFactory{}) { - handlers_[Event::uuid()].push_back([system = std::move(system), this, cache = container_->cache()](void const * event_ptr) mutable { + handlers_[Event::uuid()].push_back([system = std::move(system), this, cache = container_->cache(), context_factory = std::move(context_factory)](void const * event_ptr) mutable { auto const & event = *reinterpret_cast(event_ptr); + [[maybe_unused]] auto context = context_factory(); + container_->apply([&](ecs::container & container, ecs::handle entity, FilteredComponents & ... filtered_components){ if constexpr (std::invocable) { From 8e8967921906cb691945906c44f0972f46cd7fbb Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 14 May 2025 22:52:29 +0300 Subject: [PATCH 044/141] Remove tests for util::blob copying since the copy operator was removed --- libs/util/tests/blob.cpp | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/libs/util/tests/blob.cpp b/libs/util/tests/blob.cpp index cf03fc78..3563409e 100644 --- a/libs/util/tests/blob.cpp +++ b/libs/util/tests/blob.cpp @@ -49,29 +49,6 @@ test_case(util_blob_release) expect_equal(p.get(), q); } -test_case(util_blob_copy) -{ - blob b(16); - expect(b); - expect_equal(b.size(), 16); - expect_different_ptr(b.data(), nullptr); - - blob c(b); - expect(c); - expect_equal(c.size(), 16); - expect_different_ptr(c.data(), nullptr); - - blob d; - expect(!d); - expect_equal(d.size(), 0); - expect_equal_ptr(d.data(), nullptr); - - d = c; - expect(d); - expect_equal(d.size(), 16); - expect_different_ptr(d.data(), nullptr); -} - test_case(util_blob_move) { blob b(16); From d5abbb476207b634371d3e10239f95f3ad6fa85c Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 14 May 2025 22:52:47 +0300 Subject: [PATCH 045/141] Support random::uniform_from with initializer_list --- libs/random/include/psemek/random/uniform.hpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libs/random/include/psemek/random/uniform.hpp b/libs/random/include/psemek/random/uniform.hpp index 777acf06..3ae10c3f 100644 --- a/libs/random/include/psemek/random/uniform.hpp +++ b/libs/random/include/psemek/random/uniform.hpp @@ -9,7 +9,7 @@ #include #include -#include +#include namespace psemek::random { @@ -71,4 +71,10 @@ namespace psemek::random return *std::next(begin(container), uniform(rng, 0, size(container) - 1)); } + template + decltype(auto) uniform_from(RNG && rng, std::initializer_list const & list) + { + return *(list.begin() + uniform(rng, 0, list.size() - 1)); + } + } From 1d8ba361fc61f769465ae4f3185d2b6205eae463 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 14 May 2025 22:53:36 +0300 Subject: [PATCH 046/141] Fix compiling journal lib without sqlite --- libs/journal/CMakeLists.txt | 6 +++++- libs/journal/source/journal.cpp | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/libs/journal/CMakeLists.txt b/libs/journal/CMakeLists.txt index 8cca1e28..cb4e3fd9 100644 --- a/libs/journal/CMakeLists.txt +++ b/libs/journal/CMakeLists.txt @@ -7,4 +7,8 @@ endif() psemek_add_library(psemek-journal ${PSEMEK_JOURNAL_HEADERS} ${PSEMEK_JOURNAL_SOURCES}) target_include_directories(psemek-journal PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ${SQLite3_INCLUDE_DIRS}) -target_link_libraries(psemek-journal PUBLIC psemek-util psemek-log SQLite::SQLite3) +target_link_libraries(psemek-journal PUBLIC psemek-util psemek-log) + +if(PSEMEK_USE_SQLITE) + target_link_libraries(psemek-journal PUBLIC SQLite::SQLite3) +endif() diff --git a/libs/journal/source/journal.cpp b/libs/journal/source/journal.cpp index 7ced403f..82f1f4d7 100644 --- a/libs/journal/source/journal.cpp +++ b/libs/journal/source/journal.cpp @@ -147,6 +147,11 @@ namespace psemek::journal void log_event(event const &) {} + std::vector> select(std::string const &) + { + return {}; + } + bool enabled = true; }; From 132a521b6a97f54afec19321a4f6817315b6f1ea Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 14 May 2025 22:54:05 +0300 Subject: [PATCH 047/141] Add gfx::painter::circle() with different center & border colors --- libs/gfx/include/psemek/gfx/painter.hpp | 1 + libs/gfx/source/painter.cpp | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/libs/gfx/include/psemek/gfx/painter.hpp b/libs/gfx/include/psemek/gfx/painter.hpp index f86c44c0..d22cbb24 100644 --- a/libs/gfx/include/psemek/gfx/painter.hpp +++ b/libs/gfx/include/psemek/gfx/painter.hpp @@ -56,6 +56,7 @@ namespace psemek::gfx void triangle(math::point const & p0, math::point const & p1, math::point const & p2, color const & c0, color const & c1, color const & c2); void rect(math::box const & box, color const & c); void circle(math::point const & center, float radius, color const & c, int quality = 24); + void circle(math::point const & center, float radius, color const & c0, color const & c1, int quality = 24); void line(math::point const & p0, math::point const & p1, float width, color const & c, bool smooth = true); void line(math::point const & p0, math::point const & p1, float w0, float w1, color const & c0, color const & c1, bool smooth = true); void besier(math::point const & p0, math::point const & p1, math::point const & p2, float width, color const & c, int quality = 8, bool smooth = true); diff --git a/libs/gfx/source/painter.cpp b/libs/gfx/source/painter.cpp index 038cc6b3..28bbac6d 100644 --- a/libs/gfx/source/painter.cpp +++ b/libs/gfx/source/painter.cpp @@ -216,14 +216,19 @@ namespace psemek::gfx } void painter::circle(math::point const & p, float r, color const & c, int quality) + { + circle(p, r, c, c, quality); + } + + void painter::circle(math::point const & p, float r, color const & c0, color const & c1, int quality) { std::uint32_t const base = impl().vertices.size(); - impl().vertices.push_back({{p[0], p[1], 0.f}, c}); + impl().vertices.push_back({{p[0], p[1], 0.f}, c0}); for (int i = 0; i < quality; ++i) { float const a = (math::pi * 2.f * i) / quality; - impl().vertices.push_back({{p[0] + r * std::cos(a), p[1] + r * std::sin(a), 0.f}, c}); + impl().vertices.push_back({{p[0] + r * std::cos(a), p[1] + r * std::sin(a), 0.f}, c1}); } for (int i = 0; i < quality; ++i) From 2f75df3c91cbe772cf725ae8600af927d25abba7 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 14 May 2025 22:55:17 +0300 Subject: [PATCH 048/141] Track the size of kd-tree nodes --- libs/cg/include/psemek/cg/kdtree.hpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libs/cg/include/psemek/cg/kdtree.hpp b/libs/cg/include/psemek/cg/kdtree.hpp index d728f9f0..9f86ee6f 100644 --- a/libs/cg/include/psemek/cg/kdtree.hpp +++ b/libs/cg/include/psemek/cg/kdtree.hpp @@ -111,6 +111,7 @@ namespace psemek::cg { value_type value; node_id children[2] {null, null}; + std::uint32_t size = 0; }; std::vector nodes_; @@ -179,6 +180,7 @@ namespace psemek::cg auto result = static_cast(nodes_.size()); auto & node = nodes_.emplace_back(); node.value = std::move(*middle); + node.size = (end - begin); nodes_[result].children[0] = build_node_impl(begin, middle, next_axis(split_axis)); nodes_[result].children[1] = build_node_impl(std::next(middle), end, next_axis(split_axis)); @@ -193,6 +195,7 @@ namespace psemek::cg { auto & node = nodes_.emplace_back(); node.value = std::move(value); + node.size = 1; return true; } @@ -212,10 +215,18 @@ namespace psemek::cg auto & child_node = nodes_.emplace_back(); child_node.value = std::move(value); + child_node.size = 1; + nodes_[id].size += 1; return true; } - return insert_impl(std::move(value), node.children[child], next_axis(split_axis)); + if (insert_impl(std::move(value), node.children[child], next_axis(split_axis))) + { + nodes_[id].size += 1; + return true; + } + + return false; } template From 08ca6ab21ba2c27458746eb055f8d8d832185854 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 14 May 2025 22:56:15 +0300 Subject: [PATCH 049/141] Fix bug in cg::kdtree::insert --- libs/cg/include/psemek/cg/kdtree.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/cg/include/psemek/cg/kdtree.hpp b/libs/cg/include/psemek/cg/kdtree.hpp index 9f86ee6f..961531fb 100644 --- a/libs/cg/include/psemek/cg/kdtree.hpp +++ b/libs/cg/include/psemek/cg/kdtree.hpp @@ -206,7 +206,7 @@ namespace psemek::cg if (node_point == point) return false; - int child = (node.point[split_axis] < point[split_axis]) ? 0 : 1; + int child = (point[split_axis] < node_point[split_axis]) ? 0 : 1; if (node.children[child] == null) { From a8e32e2d98d598d53bc8832200379cdff39f7419 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 14 May 2025 22:56:26 +0300 Subject: [PATCH 050/141] Add cg::kdtree::closer_than --- libs/cg/include/psemek/cg/kdtree.hpp | 74 ++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/libs/cg/include/psemek/cg/kdtree.hpp b/libs/cg/include/psemek/cg/kdtree.hpp index 961531fb..93666fa6 100644 --- a/libs/cg/include/psemek/cg/kdtree.hpp +++ b/libs/cg/include/psemek/cg/kdtree.hpp @@ -95,6 +95,12 @@ namespace psemek::cg value_type const & closest(point_type const & target) const; + template + Iterator closer_than(point_type const & target, scalar_type max_distance, Iterator out) const; + + template + void closer_than_map(point_type const & target, scalar_type max_distance, Callback && callback) const; + private: using node_id = std::uint32_t; @@ -122,6 +128,12 @@ namespace psemek::cg bool insert_impl(value_type && value, node_id id, std::uint32_t split_axis); value_type const * closest_impl(point_type const & target, scalar_type & best_distance_sqr, node_id id, std::uint32_t split_axis) const; + + template + Iterator closer_than_impl(point_type const & target, scalar_type max_distance_sqr, Iterator out, node_id id, std::uint32_t split_axis) const; + + template + void closer_than_map_impl(point_type const & target, scalar_type max_distance_sqr, Callback & callback, node_id id, std::uint32_t split_axis) const; }; template @@ -161,6 +173,26 @@ namespace psemek::cg return *closest_impl(target, best_distance_sqr, 0, 0); } + template + template + Iterator kdtree::closer_than(point_type const & target, scalar_type max_distance, Iterator out) const + { + if (nodes_.empty()) + return out; + + return closer_than_impl(target, math::sqr(max_distance), out, 0, 0); + } + + template + template + void kdtree::closer_than_map(point_type const & target, scalar_type max_distance, Callback && callback) const + { + if (nodes_.empty()) + return; + + return closer_than_map_impl(target, math::sqr(max_distance), callback, 0, 0); + } + template template kdtree::node_id kdtree::build_node_impl(Iterator begin, Iterator end, std::uint32_t split_axis) @@ -254,4 +286,46 @@ namespace psemek::cg return result; } + template + template + Iterator kdtree::closer_than_impl(point_type const & target, scalar_type max_distance_sqr, Iterator out, node_id id, std::uint32_t split_axis) const + { + auto const & node = nodes_[id]; + auto const & node_point = detail::get_point(node.value); + + if (math::distance_sqr(node_point, target) < max_distance_sqr) + *out++ = node.value; + + auto delta = target[split_axis] - node_point[split_axis]; + auto delta_sqr = math::sqr(delta); + + if (node.children[0] != null && (delta < 0 || delta_sqr < max_distance_sqr)) + out = closer_than_impl(target, max_distance_sqr, out, node.children[0], next_axis(split_axis)); + + if (node.children[1] != null && (delta >= 0 || delta_sqr < max_distance_sqr)) + out = closer_than_impl(target, max_distance_sqr, out, node.children[1], next_axis(split_axis)); + + return out; + } + + template + template + void kdtree::closer_than_map_impl(point_type const & target, scalar_type max_distance_sqr, Callback & callback, node_id id, std::uint32_t split_axis) const + { + auto const & node = nodes_[id]; + auto const & node_point = detail::get_point(node.value); + + if (math::distance_sqr(node_point, target) < max_distance_sqr) + callback(node.value); + + auto delta = target[split_axis] - node_point[split_axis]; + auto delta_sqr = math::sqr(delta); + + if (node.children[0] != null && (delta < 0 || delta_sqr < max_distance_sqr)) + closer_than_map_impl(target, max_distance_sqr, callback, node.children[0], next_axis(split_axis)); + + if (node.children[1] != null && (delta >= 0 || delta_sqr < max_distance_sqr)) + closer_than_map_impl(target, max_distance_sqr, callback, node.children[1], next_axis(split_axis)); + } + } From cc031470b27153224da17e6c7dbeef475fd63319 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 14 May 2025 23:05:35 +0300 Subject: [PATCH 051/141] Add particle life example --- examples/particle_life_2d.cpp | 441 ++++++++++++++++++++++++++++++++++ 1 file changed, 441 insertions(+) create mode 100644 examples/particle_life_2d.cpp diff --git a/examples/particle_life_2d.cpp b/examples/particle_life_2d.cpp new file mode 100644 index 00000000..2b27fa02 --- /dev/null +++ b/examples/particle_life_2d.cpp @@ -0,0 +1,441 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace psemek; + +struct particle +{ + math::point position; + math::vector velocity; + float mass; + float radius; + int type; +}; + +struct particle_life_2d_app + : app::application_base +{ + particle_life_2d_app(options const &, context const &) + { + float S = 5.f; + float W = 160.f * S; + float H = 90.f * S; + area_ = {{{-W, W}, {-H, H}}}; + + types_ = 10; + + auto seed = random::device{}(); + + // Explosions + // types_ = 6; + // seed = 3940378904; + + // Cells + // types_ = 8; + // seed = 3071915692; + + // Cells v2 + // types_ = 6; + // seed = 3337663839; + + // Cool cells! + types_ = 10; + seed = 388834085; + + // One cell, use central gravity + // types_ = 10; + // seed = 175098945; + + // Mitochondria cell + // types_ = 10; + // seed = 1763331406; + + // Just fun + // types_ = 10; + // seed = 1676103531; + + // Another cell + // types_ = 10; + // seed = 2396049785; + + log::info() << "Seed: " << seed; + + random::generator rng{seed, 0}; + + random::uniform_distribution dcolor(63, 255); + random::uniform_distribution dtype(0, types_ - 1); + random::uniform_box_point_distribution darea(area_); + + for (int i = 0; i < types_; ++i) + colors_.push_back({dcolor(rng), dcolor(rng), dcolor(rng), 255}); + + for (int i = 0; i < 4 * 1024; ++i) + { + particles_.push_back({ + .position = darea(rng), + .velocity = {0.f, 0.f}, + .mass = 1.f, + .radius = 1.f, + .type = dtype(rng), + // .type = random::uniform_from(rng, {4, 6}), + }); + } + + auto dforce = [](auto & rng) + { + return random::uniform(rng, 20.f, 100.f) * (random::uniform(rng) ? 1.f : -1.f); + }; + + random::uniform_distribution ddistance(3.f, 20.f); + + force_constants_.resize({types_, types_}); + force_distance_.resize({types_, types_}); + collision_distance_.resize({types_, types_}); + + for (int i = 0; i < types_; ++i) + for (int j = 0; j < types_; ++j) + force_constants_(i, j) = dforce(rng); + + for (int i = 0; i < types_; ++i) + for (int j = 0; j < types_; ++j) + force_distance_(i, j) = ddistance(rng); + + for (int i = 0; i < types_; ++i) + for (int j = 0; j < types_; ++j) + collision_distance_(i, j) = random::uniform(rng, 0.125f, 0.75f) * force_distance_(i, j); + + // for (int i = 0; i < types_; ++i) + // for (int j = 0; j < types_; ++j) + // force_constants_(i, j) = -100.f * ((i == j) ? 1.f : 2.f); + + max_force_distance_.resize(types_, 2.f); + for (int i = 0; i < types_; ++i) + for (int j = 0; j < types_; ++j) + math::make_max(max_force_distance_[i], force_distance_(i, j)); + } + + void on_event(app::mouse_wheel_event const & event) override + { + app::application_base::on_event(event); + + scale_target_ *= std::pow(1.25f, -event.delta); + } + + void on_event(app::key_event const & event) override + { + app::application_base::on_event(event); + + if (event.down && event.key == app::keycode::SPACE) + paused_ ^= true; + } + + void update() override + { + float const dt = 0.02f; + + scale_ += (scale_target_ - scale_) * (- std::expm1(- 20.f * dt)); + + auto visible_area = area_; + visible_area = math::expand(visible_area, 0.5f * (scale_ - 1.f) * visible_area.dimensions()); + + float window_aspect_ratio = state().size[0] * 1.f / state().size[1]; + float area_aspect_ratio = visible_area[0].length() / visible_area[1].length(); + + if (area_aspect_ratio < window_aspect_ratio) + { + view_area_[1] = visible_area[1]; + view_area_[0] = math::expand(math::interval::singleton(visible_area[0].center()), visible_area[1].length() * window_aspect_ratio * 0.5f); + } + else + { + view_area_[0] = visible_area[0]; + view_area_[1] = math::expand(math::interval::singleton(visible_area[1].center()), visible_area[0].length() / window_aspect_ratio * 0.5f); + } + + if (!paused_) + for (int step = 0; step < 5; ++step) + { + cg::kdtree kdtree; + + for (int i = 0; i < particles_.size(); ++i) + kdtree.insert({particles_[i].position, i}); + + float const collision_strength = 200.f; + + mouseover_particle_ = std::nullopt; + if (state().mouse_button_down.contains(app::mouse_button::left)) + { + math::point m; + m[0] = math::lerp(view_area_[0], state().mouse[0] * 1.f / state().size[0]); + m[1] = math::lerp(view_area_[1], 1.f - state().mouse[1] * 1.f / state().size[1]); + mouseover_particle_ = kdtree.closest(m).data; + } + + #pragma omp parallel for + for (int i = 0; i < particles_.size(); ++i) + { + // ======== kd-tree based algorithm ======== + + auto & pi = particles_[i]; + auto f = math::vector{0.f, 0.f}; + + kdtree.closer_than_map(pi.position, max_force_distance_[pi.type], [&](auto const & value){ + auto j = value.data; + + if (i == j) + return; + + auto const & pj = particles_[j]; + + auto r = pj.position - pi.position; + float l = math::length(r); + auto n = r / l; + + // ==== 1/R forces ==== + // float lg = std::max(0.5f, l); + // auto g = n / (lg * lg); + + // if (l < force_distance_(pi.type, pj.type)) + // f += force_constants_(pi.type, pj.type) * g; + + // float collision_distance = pi.radius + pj.radius; + + // if (l < collision_distance) + // { + // float d = std::max(0.25f, l / collision_distance); + + // f -= collision_strength * n * (1.f / (d * d) - 1.f); + // } + + // ==== Linear forces ==== + if (l < force_distance_(pi.type, pj.type)) + f += force_constants_(pi.type, pj.type) * n * (1.f - l / force_distance_(pi.type, pj.type)); + + if (l < collision_distance_(pi.type, pj.type)) + f -= 10.f * std::abs(force_constants_(pi.type, pj.type)) * n * (1.f - l / collision_distance_(pi.type, pj.type)); + + (void)collision_strength; + }); + + pi.velocity += f * dt / pi.mass; + + + + + + + // ======== Naive algorithm v2 ======== + + // auto & pi = particles_[i]; + // auto f = math::vector{0.f, 0.f}; + + // for (int j = 0; j < particles_.size(); ++j) + // { + // if (i == j) + // continue; + + // auto const & pj = particles_[j]; + + // auto r = pj.position - pi.position; + // float l = math::length(r); + // auto n = r / l; + // auto g = n / (l * l); + + // if (l < force_distance_(pi.type, pj.type)) + // f += force_constants_(pi.type, pj.type) * g; + + // if (l < pi.radius + pj.radius) + // { + // float d = l / (pi.radius + pj.radius); + + // f -= collision_strength * n * (1.f / (d * d) - 1.f); + // } + // } + + // pi.velocity += f * dt / pi.mass; + + + + + + // ======== Naive algorithm ======== + + // for (int j = i + 1; j < particles_.size(); ++j) + // { + // auto & pi = particles_[i]; + // auto & pj = particles_[j]; + + // auto fi = math::vector{0.f, 0.f}; + // auto fj = math::vector{0.f, 0.f}; + + // auto r = pj.position - pi.position; + // float l = math::length(r); + // auto n = r / l; + // auto g = n / (l * l); + + // if (l < force_distance_(pi.type, pj.type)) + // fi += force_constants_(pi.type, pj.type) * g; + + // if (l < force_distance_(pj.type, pi.type)) + // fj -= force_constants_(pj.type, pi.type) * g; + + // if (l < pi.radius + pj.radius) + // { + // float d = l / (pi.radius + pj.radius); + + // auto f = collision_strength * n * (1.f / (d * d) - 1.f); + // fi -= f; + // fj += f; + // } + + // pi.velocity += fi * dt / pi.mass; + // pj.velocity += fj * dt / pj.mass; + // } + } + + float const friction = 10.f; + float const friction_factor = std::exp(- friction * dt); + float const wall_distance = 0.f; + float const wall_force = 10.f; + float const gravity = 0.f; + float const center_gravity = 0.f; + float const line_gravity = 0.f; + float const circle_gravity = 0.f; + float const circle_radius = 100.f; + bool const circle_wall = false; + float const circle_wall_radius = 100.f; + + for (auto & p : particles_) + { + auto r = p.position - p.position.zero(); + + p.velocity[1] -= gravity * dt; + p.velocity[1] -= line_gravity * p.position[1] * dt; + p.velocity -= center_gravity * r * dt; + p.velocity += circle_gravity * r / math::length(r) * (circle_radius - math::length(r)) * dt; + p.velocity *= friction_factor; + p.position += p.velocity * dt; + + for (int d : {0, 1}) + { + // if (p.position[d] < area_[d].min + p.radius) + // { + // p.position[d] = area_[d].min + p.radius; + // p.velocity[d] *= -1.f; + // } + + // if (p.position[d] > area_[d].max - p.radius) + // { + // p.position[d] = area_[d].max - p.radius; + // p.velocity[d] *= -1.f; + // } + + if (circle_wall) + { + auto r = p.position - p.position.zero(); + auto l = math::length(r); + if (l > circle_wall_radius) + { + p.velocity -= wall_force * r / l * (l - circle_wall_radius) * dt; + } + } + else + { + if (p.position[d] < area_[d].min + wall_distance) + { + p.velocity[d] += wall_force * (area_[d].min + wall_distance - p.position[d]) * dt; + } + + if (p.position[d] > area_[d].max - wall_distance) + { + p.velocity[d] -= wall_force * (p.position[d] - (area_[d].max - wall_distance)) * dt; + } + } + } + } + } + } + + void present() override + { + gl::Viewport(0, 0, state().size[0], state().size[1]); + + gl::ClearColor(0.02f, 0.02f, 0.02f, 1.f); + gl::Clear(gl::COLOR_BUFFER_BIT); + + for (auto const & p : particles_) + { + auto c0 = colors_[p.type]; + c0[3] = 15; + + auto c1 = colors_[p.type]; + c1[3] = 0; + + painter_.circle(p.position, p.radius * 8.f, c0, c1); + } + + for (auto const & p : particles_) + painter_.circle(p.position, p.radius, colors_[p.type]); + + painter_.render(math::orthographic_camera{view_area_}.transform()); + + if (mouseover_particle_) + { + int type = particles_[*mouseover_particle_].type; + painter_.text({state().size[0] / 2.f, state().size[1] - 20.f}, std::format("Species #{}", type), { + .scale = {2.f, 2.f}, + .y = gfx::painter::y_align::bottom, + .c = colors_[type], + }); + } + + painter_.render(math::window_camera{state().size[0], state().size[1]}.transform()); + } + +private: + gfx::painter painter_; + + util::clock, std::chrono::high_resolution_clock> clock_; + + math::box area_; + math::box view_area_; + float scale_ = 1.f; + float scale_target_ = 1.f; + + int types_; + std::vector colors_; + util::array force_constants_; + util::array force_distance_; + util::array collision_distance_; + std::vector max_force_distance_; + + std::vector particles_; + + std::optional mouseover_particle_; + + bool paused_ = true; +}; + +namespace psemek::app +{ + + std::unique_ptr make_application_factory() + { + return default_application_factory({.name = "Particle Life 2D"}); + } + +} From fde307e96f5e512c76b08d6c045922da4b164abd Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 15 May 2025 12:57:28 +0300 Subject: [PATCH 052/141] Update license --- LICENSE.txt | 29 +++++++++++++++++++++++++++++ readme.md | 4 +++- 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000..075d3886 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,29 @@ +MIT NON-AI NON-NFT License + +Copyright (c) 2025, Nikita Lisitsa + +Permission is hereby granted, free of charge, to any person obtaining a copy of the software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions. + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +In addition, the following restrictions apply: + +1. The Software, any modifications made to it, any artifacts or media generated by it, and any parts thereof, including but not limited to images, +video, and code snippets, may not be used for the purpose of training or improving machine learning algorithms, or be included in any dataset used +for training or improving machine learning algorithms, including but not limited to artificial intelligence, natural language processing, large +language models, or data mining. This condition applies to any derivatives, modifications, or updates based on the Software code. Any usage of the +Software in an AI-training dataset is considered a breach of this License. + +2. The Software, any modifications made to it, any artifacts or media generated by it, and any parts thereof, including but not limited to images, +video, and code snippets, may not be owned via a blockchain-backed ownership token, or as a means of generating blockchain-backed ownership tokens, +including but not limited to non-fungible tokens. Any usage of the Software to generate non-fungible tokens is considered a breach of this License. + +3. Any person or organization found to be in violation of these restrictions will be subject to legal action and may be held liable for any damages +resulting from such use. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/readme.md b/readme.md index 2e8d0ea9..2ffa4cd6 100644 --- a/readme.md +++ b/readme.md @@ -1 +1,3 @@ -Psemek - a game & visualization engine +Psemek - a game & visualization engine. + +Licensed under the terms of the [NON-AI NON-NFT MIT license](LICENSE.txt). From 24f3df6f35f2505fb898b77cb0efec5523e83c17 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Tue, 20 May 2025 17:22:48 +0300 Subject: [PATCH 053/141] Add app::apply(event_state, text_input_event) --- libs/app/include/psemek/app/event_state.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/app/include/psemek/app/event_state.hpp b/libs/app/include/psemek/app/event_state.hpp index 62526d71..387e9fa2 100644 --- a/libs/app/include/psemek/app/event_state.hpp +++ b/libs/app/include/psemek/app/event_state.hpp @@ -61,4 +61,7 @@ namespace psemek::app state.key_down.erase(event.key); } + inline void apply(event_state &, text_input_event const &) + {} + } From 08510e265b305a3e2a4d702c75f76ffef909c7fa Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 5 Jun 2025 18:47:38 +0300 Subject: [PATCH 054/141] Support comparing wgpu objects by comparing underlying pointers --- libs/wgpu/include/psemek/wgpu/detail/object.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libs/wgpu/include/psemek/wgpu/detail/object.hpp b/libs/wgpu/include/psemek/wgpu/detail/object.hpp index 887acdff..2e3586b6 100644 --- a/libs/wgpu/include/psemek/wgpu/detail/object.hpp +++ b/libs/wgpu/include/psemek/wgpu/detail/object.hpp @@ -55,6 +55,11 @@ namespace psemek::wgpu::detail explicit operator bool() const { return ptr_ != nullptr; } + friend bool operator == (object const & o1, object const & o2) + { + return o1.ptr_ == o2.ptr_; + } + protected: object(void * ptr) : ptr_(ptr) From afd942af140e851234091dc208765450fdeb9045 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Mon, 9 Jun 2025 13:37:20 +0300 Subject: [PATCH 055/141] Support worley noise in noise-generator tool --- tools/noise-generator/generator.cpp | 206 +++++++++++++++++++++++++++- 1 file changed, 201 insertions(+), 5 deletions(-) diff --git a/tools/noise-generator/generator.cpp b/tools/noise-generator/generator.cpp index af22b91a..9845a371 100644 --- a/tools/noise-generator/generator.cpp +++ b/tools/noise-generator/generator.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -10,10 +11,32 @@ using namespace psemek; template -util::array generate(std::array const & size, int octaves, int minoctave, float power, - bool tile, float gamma, bool remap, std::uint64_t seed) +auto moore_neighbourhood() { - std::cout << "Generating " << D << "D noise\n"; + if constexpr (D == 2) + { + std::array, 9> result; + for (int dy = -1; dy <= 1; ++dy) + for (int dx = -1; dx <= 1; ++dx) + result[(dy + 1) * 3 + (dx + 1)] = {dx, dy}; + return result; + } + else if constexpr (D == 3) + { + std::array, 27> result; + for (int dz = -1; dz <= 1; ++dz) + for (int dy = -1; dy <= 1; ++dy) + for (int dx = -1; dx <= 1; ++dx) + result[(dz + 1) * 9 + (dy + 1) * 3 + (dx + 1)] = {dx, dy, dz}; + return result; + } +} + +template +util::array generate_perlin(std::array const & size, int octaves, int minoctave, float power, + bool invert, bool tile, float gamma, bool remap, std::uint64_t seed) +{ + std::cout << "Generating " << D << "D perlin noise\n"; std::cout << "Size "; for (int d = 0; d < D; ++d) { @@ -24,6 +47,7 @@ util::array generate(std::array const & size, i std::cout << "\nOctaves: " << minoctave << ".." << (minoctave + octaves - 1) << "\n"; std::cout << "Power: " << power << "\n"; + std::cout << "Invert: " << (invert ? "true" : "false") << "\n"; std::cout << "Tile: " << (tile ? "true" : "false") << "\n"; std::cout << "Gamma: " << gamma << "\n"; std::cout << "Remap: " << (remap ? "true" : "false") << "\n"; @@ -93,6 +117,147 @@ util::array generate(std::array const & size, i float v = result_float(idx); if (remap) v = math::unlerp(value_range, v); + if (invert) + v = 1.f - v; + v = std::pow(v, gamma); + result(idx) = static_cast(std::clamp(v * 255.f, 0.f, 255.f)); + } + + return result; +} + +template +util::array generate_worley(std::array const & size, int octaves, int minoctave, float power, + bool invert, bool tile, float gamma, bool remap, std::uint64_t seed) +{ + std::cout << "Generating " << D << "D worley noise\n"; + std::cout << "Size "; + for (int d = 0; d < D; ++d) + { + if (d > 0) + std::cout << "x"; + std::cout << size[d]; + } + + std::cout << "\nOctaves: " << minoctave << ".." << (minoctave + octaves - 1) << "\n"; + std::cout << "Power: " << power << "\n"; + std::cout << "Invert: " << (invert ? "true" : "false") << "\n"; + std::cout << "Tile: " << (tile ? "true" : "false") << "\n"; + std::cout << "Gamma: " << gamma << "\n"; + std::cout << "Remap: " << (remap ? "true" : "false") << "\n"; + std::cout << "Seed: " << std::hex << std::setw(16) << std::setfill('0') << seed << "\n"; + std::cout << std::flush; + + random::generator rng{seed, 0}; + + std::vector, D>> octave_points; + std::vector octave_weights; + float sum_octave_weights = 0.f; + + for (int o = minoctave; o < minoctave + octaves; ++o) + { + std::array octave_size; + for (int d = 0; d < D; ++d) + octave_size[d] = (1 << o); + + util::array, D> octave(octave_size); + + for (auto idx : octave.indices()) + { + auto & p = octave(idx); + for (int d = 0; d < D; ++d) + p[d] = idx[d] + random::uniform(rng); + } + + octave_points.push_back(std::move(octave)); + + float weight = std::pow(static_cast(1 << o), - power); + octave_weights.push_back(weight); + sum_octave_weights += weight; + } + + for (auto & w : octave_weights) + w /= sum_octave_weights; + + util::array result_float(size); + math::interval value_range; + + for (auto idx : result_float.indices()) + { + math::point p; + + for (int d = 0; d < D; ++d) + p[d] = (idx[d] + 0.5f) / size[d] * (1 << minoctave); + + float v = 0.f; + + for (int o = 0; o < octaves; ++o) + { + math::point i; + for (int d = 0; d < D; ++d) + i[d] = std::floor(p[d]); + + int octave_size = 1 << (o + minoctave); + + float closest_distance = std::numeric_limits::infinity(); + + for (auto n : moore_neighbourhood()) + { + math::point j = i + n; + + if (tile) + { + for (int d = 0; d < D; ++d) + j[d] = (j[d] + octave_size) % octave_size; + } + else + { + bool good = true; + for (int d = 0; d < D; ++d) + { + good &= (j[d] >= 0); + good &= (j[d] < octave_size); + } + + if (!good) + continue; + } + + math::vector r = octave_points[o](j) - p; + + if (tile) + { + for (int d = 0; d < D; ++d) + { + if (i[d] + n[d] == -1) + r[d] -= octave_size; + else if (i[d] + n[d] == octave_size) + r[d] += octave_size; + } + } + + math::make_min(closest_distance, math::length(r)); + } + + v += closest_distance * octave_weights[o] / std::sqrt(2.f); + + for (int d = 0; d < D; ++d) + p[d] *= 2.f; + } + + result_float(idx) = v; + value_range |= v; + } + + util::array result(size); + + for (auto idx : result.indices()) + { + float v = result_float(idx); + if (remap) + v = math::unlerp(value_range, v); + if (invert) + v = 1.f - v; v = std::pow(v, gamma); result(idx) = static_cast(std::clamp(v * 255.f, 0.f, 255.f)); } @@ -104,6 +269,10 @@ char const options_usage[] = R"( Available options: + type + Noise type (perlin or worley) + Default = worley + dim Noise dimension (2 or 3) Default = 2 @@ -132,6 +301,10 @@ Available options: Octaves weight power (0.0 means all octaves have equal contributions), can be negative Default = 1.0 + invert + Replace noise values with 1-value (makes sense for worley noise only) + Default = false + tile Whether to make the noise tileable (seamless) Default = true @@ -200,6 +373,7 @@ std::optional parse_bool(std::string const & str) int main(int argc, char * argv[]) { util::hash_map values; + values["type"] = "perlin"; values["dim"] = "2"; values["sizex"] = "256"; values["sizey"] = "256"; @@ -207,6 +381,7 @@ int main(int argc, char * argv[]) values["octaves"] = "8"; values["minoctave"] = "0"; values["power"] = "1.0"; + values["invert"] = "false"; values["tile"] = "true"; values["gamma"] = "1.0"; values["remap"] = "true"; @@ -243,6 +418,7 @@ int main(int argc, char * argv[]) values[key] = value; } + auto type = values["type"]; auto dim = parse_int(values["dim"]); auto sizex = parse_int(values["sizex"]); auto sizey = parse_int(values["sizey"]); @@ -250,6 +426,7 @@ int main(int argc, char * argv[]) auto octaves = parse_int(values["octaves"]); auto minoctave = parse_int(values["minoctave"]); auto power = parse_float(values["power"]); + auto invert = parse_bool(values["invert"]); auto tile = parse_bool(values["tile"]); auto gamma = parse_float(values["gamma"]); auto remap = parse_bool(values["remap"]); @@ -262,6 +439,12 @@ int main(int argc, char * argv[]) if (!seed) seed = parse_seed(values["seed"], 10); if (!seed) seed = parse_seed(values["seed"], 16); + if (type != "perlin" && type != "worley") + { + std::cerr << "Unknown noise type: " << values["type"] << "\n"; + return EXIT_FAILURE; + } + if (!dim || (*dim != 2 && *dim != 3)) { std::cerr << "Unknown noise dimension: " << values["dim"] << "\n"; return EXIT_FAILURE; @@ -297,6 +480,11 @@ int main(int argc, char * argv[]) return EXIT_FAILURE; } + if (!invert) { + std::cerr << "Invert must be true or false: " << values["invert"] << "\n"; + return EXIT_FAILURE; + } + if (!tile) { std::cerr << "Tile must be true or false: " << values["tile"] << "\n"; return EXIT_FAILURE; @@ -319,7 +507,11 @@ int main(int argc, char * argv[]) if (*dim == 2) { - auto result = generate<2>({*sizex, *sizey}, *octaves, *minoctave, *power, *tile, *gamma, *remap, *seed); + util::array result; + if (type == "perlin") + result = generate_perlin<2>({*sizex, *sizey}, *octaves, *minoctave, *power, *invert, *tile, *gamma, *remap, *seed); + else if (type == "worley") + result = generate_worley<2>({*sizex, *sizey}, *octaves, *minoctave, *power, *invert, *tile, *gamma, *remap, *seed); io::file_ostream out{std::filesystem::path(output_path)}; @@ -347,7 +539,11 @@ int main(int argc, char * argv[]) if (*dim == 3) { - auto result = generate<3>({*sizex, *sizey, *sizez}, *octaves, *minoctave, *power, *tile, *gamma, *remap, *seed); + util::array result; + if (type == "perlin") + generate_perlin<3>({*sizex, *sizey, *sizez}, *octaves, *minoctave, *power, *invert, *tile, *gamma, *remap, *seed); + else if (type == "worley") + result = generate_worley<3>({*sizex, *sizey, *sizez}, *octaves, *minoctave, *power, *invert, *tile, *gamma, *remap, *seed); io::file_ostream out{std::filesystem::path(output_path)}; std::cout << "Saving raw image to " << output_path << std::endl; From 14b18197bfe688fbce20b2483165da10ace78daa Mon Sep 17 00:00:00 2001 From: lisyarus Date: Mon, 9 Jun 2025 14:24:37 +0300 Subject: [PATCH 056/141] Support resetting vecr::renderer to an existing image --- libs/vecr/include/psemek/vecr/renderer.hpp | 1 + libs/vecr/source/renderer.cpp | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/libs/vecr/include/psemek/vecr/renderer.hpp b/libs/vecr/include/psemek/vecr/renderer.hpp index ae9fceed..d26e7361 100644 --- a/libs/vecr/include/psemek/vecr/renderer.hpp +++ b/libs/vecr/include/psemek/vecr/renderer.hpp @@ -23,6 +23,7 @@ namespace psemek::vecr struct renderer { void reset(math::vector const & size, std::size_t samples = 4, gfx::color_rgba const & color = {0, 0, 0, 0}); + void reset(gfx::pixmap_rgba image, std::size_t samples = 4); math::vector size() const; std::size_t samples() const; diff --git a/libs/vecr/source/renderer.cpp b/libs/vecr/source/renderer.cpp index e6eb5789..330d1c0b 100644 --- a/libs/vecr/source/renderer.cpp +++ b/libs/vecr/source/renderer.cpp @@ -12,6 +12,15 @@ namespace psemek::vecr need_resolve_ = false; } + void renderer::reset(gfx::pixmap_rgba image, std::size_t samples) + { + result_ = std::move(image); + canvas_.resize({result_.width() * samples, result_.height() * samples}); + for (auto idx : canvas_.indices()) + canvas_(idx) = result_({idx[0] / samples, idx[1] / samples}); + need_resolve_ = false; + } + math::vector renderer::size() const { return {result_.width(), result_.height()}; From d826940b4c636f038241b3629af65fc0bd69fa82 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Tue, 8 Jul 2025 20:21:59 +0300 Subject: [PATCH 057/141] Make bt nodes execute subnodes immediately (and not across several updates) whenever possible --- libs/bt/include/psemek/bt/conditional.hpp | 22 +++++++++------------- libs/bt/include/psemek/bt/selector.hpp | 5 +++-- libs/bt/include/psemek/bt/sequence.hpp | 5 +++-- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/libs/bt/include/psemek/bt/conditional.hpp b/libs/bt/include/psemek/bt/conditional.hpp index 3e4c5b23..d8a94f4d 100644 --- a/libs/bt/include/psemek/bt/conditional.hpp +++ b/libs/bt/include/psemek/bt/conditional.hpp @@ -39,10 +39,7 @@ namespace psemek::bt { if (condition_result_) { - if (*condition_result_) - return true_branch_->update(dt, args...); - else - return false_branch_->update(dt, args...); + return current_node()->update(dt, args...); } else { @@ -51,11 +48,8 @@ namespace psemek::bt if (auto finished = std::get_if(&result)) { condition_result_ = finished->result; - if (*condition_result_) - true_branch_->start(args...); - else - false_branch_->start(args...); - return running{}; + current_node()->start(args...); + return current_node()->update(Time{}, args...); } return result; @@ -71,10 +65,7 @@ namespace psemek::bt { if (condition_result_) { - if (*condition_result_) - return true_branch_->event(event, args...); - else - return false_branch_->event(event, args...); + return current_node()->event(event, args...); } else return condition_->event(event, args...); @@ -85,6 +76,11 @@ namespace psemek::bt node_ptr true_branch_; node_ptr false_branch_; std::optional condition_result_; + + node * current_node() + { + return *condition_result_ ? true_branch_.get() : false_branch_.get(); + } }; template diff --git a/libs/bt/include/psemek/bt/selector.hpp b/libs/bt/include/psemek/bt/selector.hpp index 9acdb188..9a46c4eb 100644 --- a/libs/bt/include/psemek/bt/selector.hpp +++ b/libs/bt/include/psemek/bt/selector.hpp @@ -32,7 +32,7 @@ namespace psemek::bt status update(Time dt, Args ... args) override { - if (current_index_ < children_.size()) + while (current_index_ < children_.size()) { if (!current_started_) { @@ -41,13 +41,14 @@ namespace psemek::bt } auto result = children_[current_index_]->update(dt, args...); + dt = Time{}; if (auto f = std::get_if(&result)) { if (f->result) return finished{true}; ++current_index_; current_started_ = false; - return running{}; + continue; } else return result; diff --git a/libs/bt/include/psemek/bt/sequence.hpp b/libs/bt/include/psemek/bt/sequence.hpp index 49395087..a207a441 100644 --- a/libs/bt/include/psemek/bt/sequence.hpp +++ b/libs/bt/include/psemek/bt/sequence.hpp @@ -32,7 +32,7 @@ namespace psemek::bt status update(Time dt, Args ... args) override { - if (current_index_ < children_.size()) + while (current_index_ < children_.size()) { if (!current_started_) { @@ -41,13 +41,14 @@ namespace psemek::bt } auto result = children_[current_index_]->update(dt, args...); + dt = Time{}; if (auto f = std::get_if(&result)) { if (!f->result) return finished{false}; ++current_index_; current_started_ = false; - return running{}; + continue; } else return result; From a7cbe697124f12b24924b3d7f5e73a993e33509b Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 9 Jul 2025 21:02:22 +0300 Subject: [PATCH 058/141] Add math::trs class & use it in gltf animations --- .../gfx/include/psemek/gfx/gltf_animation.hpp | 6 +- libs/gfx/source/gltf_animation.cpp | 6 +- libs/math/include/psemek/math/trs.hpp | 88 +++++++++++++++++++ 3 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 libs/math/include/psemek/math/trs.hpp diff --git a/libs/gfx/include/psemek/gfx/gltf_animation.hpp b/libs/gfx/include/psemek/gfx/gltf_animation.hpp index ac5f95b7..df3e8491 100644 --- a/libs/gfx/include/psemek/gfx/gltf_animation.hpp +++ b/libs/gfx/include/psemek/gfx/gltf_animation.hpp @@ -1,9 +1,7 @@ #pragma once #include -#include -#include -#include +#include #include #include @@ -147,7 +145,7 @@ namespace psemek::gfx math::interval range() const; - math::affine_transform operator()(float time) const; + math::trs operator()(float time) const; gltf_scale_animation const & scale() const { return scale_; } gltf_rotation_animation const & rotation() const { return rotation_; } diff --git a/libs/gfx/source/gltf_animation.cpp b/libs/gfx/source/gltf_animation.cpp index bfd28800..efb2f351 100644 --- a/libs/gfx/source/gltf_animation.cpp +++ b/libs/gfx/source/gltf_animation.cpp @@ -90,11 +90,9 @@ namespace psemek::gfx return scale_.range() | rotation_.range() | translation_.range(); } - math::affine_transform gltf_animation::operator()(float time) const + math::trs gltf_animation::operator()(float time) const { - return math::translation(translation_(time)).transform() - * math::quaternion_rotation(rotation_(time)).transform() - * math::scale(scale_(time)).transform(); + return {translation_(time), rotation_(time), scale_(time)}; } } diff --git a/libs/math/include/psemek/math/trs.hpp b/libs/math/include/psemek/math/trs.hpp new file mode 100644 index 00000000..20593728 --- /dev/null +++ b/libs/math/include/psemek/math/trs.hpp @@ -0,0 +1,88 @@ +#pragma once + +#include +#include +#include + +namespace psemek::math +{ + + // NB: composition of TRS triplets is not defined as the result + // might not be expressible in TRS form + + template + struct trs; + + template + struct trs + { + vector translation; + quaternion rotation; + vector scale; + + static trs identity() + { + return {vector::zero(), quaternion::identity(), vector{T{1}, T{1}, T{1}}}; + } + + static trs from(matrix const & m) + { + trs result; + + result.translation[0] = m[0][3]; + result.translation[1] = m[1][3]; + result.translation[2] = m[2][3]; + + result.scale[0] = length(vector{m[0][0], m[1][0], m[2][0]}); + result.scale[1] = length(vector{m[0][1], m[1][1], m[2][1]}); + result.scale[2] = length(vector{m[0][2], m[1][2], m[2][2]}); + + matrix r; + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + r[i][j] = m[i][j] / result.scale[j]; + + result.rotation = quaternion::rotation(r); + + return result; + } + + matrix affine_matrix() const + { + return transform().affine_matrix(); + } + + matrix linear_matrix() const + { + return transform().linear_matrix(); + } + + vector translation_vector() const + { + return translation; + } + + matrix homogeneous_matrix() const + { + return transform().homogeneous_matrix(); + } + + vector operator()(vector const & v) const + { + return rotate(rotation, pointwise_mult(v, scale)); + } + + point operator()(point const & p) const + { + return p.zero() + rotate(rotation, pointwise_mult(p - p.zero(), scale)) + translation; + } + + affine_transform transform() const + { + return math::translation(translation).transform() + * quaternion_rotation(rotation).transform() + * math::scale(scale).transform(); + } + }; + +} From c3c844643158c732f65ef87a15927932c1a9513b Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 10 Jul 2025 02:13:02 +0300 Subject: [PATCH 059/141] Make default-initialized math::trs the identity --- libs/math/include/psemek/math/trs.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/math/include/psemek/math/trs.hpp b/libs/math/include/psemek/math/trs.hpp index 20593728..2e9eb273 100644 --- a/libs/math/include/psemek/math/trs.hpp +++ b/libs/math/include/psemek/math/trs.hpp @@ -16,13 +16,13 @@ namespace psemek::math template struct trs { - vector translation; - quaternion rotation; - vector scale; + vector translation = {T{0}, T{0}, T{0}}; + quaternion rotation = quaternion::identity(); + vector scale = {T{1}, T{1}, T{1}}; static trs identity() { - return {vector::zero(), quaternion::identity(), vector{T{1}, T{1}, T{1}}}; + return {}; } static trs from(matrix const & m) From 1f57c76036c4748b75b4e9ca07be6f3274c1d1ab Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sat, 12 Jul 2025 01:28:14 +0300 Subject: [PATCH 060/141] Add std::array hash --- libs/util/include/psemek/util/hash.hpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/libs/util/include/psemek/util/hash.hpp b/libs/util/include/psemek/util/hash.hpp index 3e53bf22..3a05a1f9 100644 --- a/libs/util/include/psemek/util/hash.hpp +++ b/libs/util/include/psemek/util/hash.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -64,6 +65,20 @@ namespace std } }; + template + struct hash> + : std::hash + { + using std::hash::operator(); + + std::uint64_t operator()(std::array const & x) const + { + std::uint64_t seed = 0; + ::psemek::util::hash_sequence(seed, x.begin(), x.end(), *this); + return seed; + } + }; + template struct hash> : std::tuple...> From ce829f7356a69d4110e81c2e762bc27d876917a9 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sat, 12 Jul 2025 01:28:40 +0300 Subject: [PATCH 061/141] Add permutation groups & implement converting cyclic & dihedral groups into permutations --- libs/group/include/psemek/group/cyclic.hpp | 9 + libs/group/include/psemek/group/dihedral.hpp | 20 +++ .../include/psemek/group/permutation.hpp | 154 ++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 libs/group/include/psemek/group/permutation.hpp diff --git a/libs/group/include/psemek/group/cyclic.hpp b/libs/group/include/psemek/group/cyclic.hpp index aad2a145..132c66e2 100644 --- a/libs/group/include/psemek/group/cyclic.hpp +++ b/libs/group/include/psemek/group/cyclic.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -55,6 +56,14 @@ namespace psemek::group return repr_; } + group::permutation permutation() const + { + typename group::permutation::repr_type repr; + for (Repr i = 0; i < N; ++i) + repr[i] = (i + repr_) % N; + return group::permutation::from_repr(repr); + } + struct value_iterator { using difference_type = Repr; diff --git a/libs/group/include/psemek/group/dihedral.hpp b/libs/group/include/psemek/group/dihedral.hpp index a1a5e364..cc199a35 100644 --- a/libs/group/include/psemek/group/dihedral.hpp +++ b/libs/group/include/psemek/group/dihedral.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -87,6 +88,25 @@ namespace psemek::group return repr_ >= N; } + group::permutation permutation() const + { + if (repr_ < N) + { + typename group::permutation::repr_type repr; + for (Repr i = 0; i < N; ++i) + repr[i] = (i + repr_) % N; + return group::permutation::from_repr(repr); + } + else + { + typename group::permutation::repr_type repr; + for (Repr i = 0; i < N; ++i) + repr[i] = (N - i) % N; + + return rotation(repr_ % N).permutation() * group::permutation::from_repr(repr); + } + } + struct value_iterator { using difference_type = Repr; diff --git a/libs/group/include/psemek/group/permutation.hpp b/libs/group/include/psemek/group/permutation.hpp new file mode 100644 index 00000000..f8efffa1 --- /dev/null +++ b/libs/group/include/psemek/group/permutation.hpp @@ -0,0 +1,154 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +namespace psemek::group +{ + + template + struct permutation + { + static_assert(std::is_integral_v && std::is_unsigned_v); + static_assert(std::numeric_limits::max() > N); + + using repr_type = std::array; + + static constexpr std::size_t size() + { + return N; + } + + permutation() = default; + + static permutation identity() + { + return permutation{}; + } + + static permutation from_repr(repr_type const & value) + { + return permutation{value}; + } + + repr_type const & value() const + { + return repr_; + } + + repr_type operator() (repr_type const & value) const + { + repr_type result; + for (Repr i = 0; i < N; ++i) + result[repr_[i]] = value[i]; + return result; + } + + struct value_iterator + { + using difference_type = Repr; + using value_type = permutation; + using pointer = value_type *; + using reference = value_type &; + using iterator_category = std::forward_iterator_tag; + + repr_type repr; + bool end; + + permutation operator *() const + { + return permutation{repr}; + } + + value_iterator & operator++() + { + end = std::next_permutation(repr.begin(), repr.end()); + return *this; + } + + friend bool operator == (value_iterator const & it1, value_iterator const & it2) + { + return it1.repr == it2.repr && it1.end == it2.end; + } + }; + + static auto values() + { + repr_type repr; + std::iota(repr.begin(), repr.end(), Repr{0}); + return util::range{value_iterator{repr, false}, value_iterator{repr, true}}; + } + + private: + repr_type repr_{0}; + + explicit permutation(repr_type const & repr) + : repr_(repr) + {} + }; + + template + bool operator == (permutation const & g1, permutation const & g2) + { + return g1.value() == g2.value(); + } + + template + auto operator <=> (permutation const & g1, permutation const & g2) + { + return g1.value() <=> g2.value(); + } + + template + permutation operator * (permutation const & g1, permutation const & g2) + { + typename permutation::repr_type repr; + for (Repr i = 0; i < N; ++i) + repr[i] = g1.value()[g2.value()[i]]; + return permutation::from_repr(repr); + } + + template + permutation inverse(permutation const & g) + { + typename permutation::repr_type repr; + for (Repr i = 0; i < N; ++i) + repr[g.value()[i]] = i; + return permutation::from_repr(repr); + } + + template + OStream & operator << (OStream & os, permutation const & g) + { + os << 'S' << N << '('; + for (Repr i = 0; i < N; ++i) + { + if (i > 0) + os << ", "; + os << g.value()[i]; + } + os << ')'; + return os; + } + + template + void write(OStream & out, permutation const & g) + { + write(out, g.value()); + } + + template + void read(IStream & in, permutation & g) + { + typename permutation::repr_type value; + read(in, value); + g = permutation::from_repr(value); + } + +} From 68ba3698f8fff0102565adc080e53e023c62a3a7 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Tue, 29 Jul 2025 21:05:42 +0300 Subject: [PATCH 062/141] Fix transcendetal functions on util::fixed_point --- libs/util/include/psemek/util/fixed_point.hpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/libs/util/include/psemek/util/fixed_point.hpp b/libs/util/include/psemek/util/fixed_point.hpp index 115590b4..20295b43 100644 --- a/libs/util/include/psemek/util/fixed_point.hpp +++ b/libs/util/include/psemek/util/fixed_point.hpp @@ -113,6 +113,13 @@ namespace psemek::util return static_cast(x); } + template + auto apply_via_floating_point(Function && function, FixedPoint1 const & x1, FixedPoint const & ... xs) + { + static_assert((std::is_same_v && ...)); + return static_cast(std::forward(function)(static_cast(x1), static_cast(xs) ...)); + } + } template @@ -353,29 +360,25 @@ namespace psemek::util template fixed_point sin(fixed_point x) { - using FP = fixed_point; - return static_cast(std::round(std::sin(static_cast(x)))); + return detail::apply_via_floating_point(&std::sin, x); } template fixed_point cos(fixed_point x) { - using FP = fixed_point; - return static_cast(std::round(std::cos(static_cast(x)))); + return detail::apply_via_floating_point(&std::cos, x); } template fixed_point exp(fixed_point x) { - using FP = fixed_point; - return static_cast(std::round(std::exp(static_cast(x)))); + return detail::apply_via_floating_point(&std::exp, x); } template fixed_point log(fixed_point x) { - using FP = fixed_point; - return static_cast(std::round(std::log(static_cast(x)))); + return detail::apply_via_floating_point(&std::log, x); } } From 80daea54fa6b296b8dda3d847b326f9857c11438 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 6 Aug 2025 17:53:00 +0300 Subject: [PATCH 063/141] Move old font classes to an inline fonts::v1 namespace --- libs/fonts/include/psemek/fonts/font.hpp | 2 +- libs/fonts/include/psemek/fonts/kerned_font.hpp | 2 +- libs/fonts/include/psemek/fonts/msdf_font.hpp | 2 +- libs/fonts/source/default_fonts.cpp | 2 +- libs/fonts/source/kerned_font.cpp | 2 +- libs/fonts/source/msdf_font.cpp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libs/fonts/include/psemek/fonts/font.hpp b/libs/fonts/include/psemek/fonts/font.hpp index a70f77fc..1b73bd7c 100644 --- a/libs/fonts/include/psemek/fonts/font.hpp +++ b/libs/fonts/include/psemek/fonts/font.hpp @@ -11,7 +11,7 @@ #include #include -namespace psemek::fonts +namespace psemek::fonts::inline v1 { enum class font_type diff --git a/libs/fonts/include/psemek/fonts/kerned_font.hpp b/libs/fonts/include/psemek/fonts/kerned_font.hpp index 79ce1a47..0eaa75be 100644 --- a/libs/fonts/include/psemek/fonts/kerned_font.hpp +++ b/libs/fonts/include/psemek/fonts/kerned_font.hpp @@ -3,7 +3,7 @@ #include #include -namespace psemek::fonts +namespace psemek::fonts::inline v1 { struct kerned_font diff --git a/libs/fonts/include/psemek/fonts/msdf_font.hpp b/libs/fonts/include/psemek/fonts/msdf_font.hpp index 3963d5be..4d8254c4 100644 --- a/libs/fonts/include/psemek/fonts/msdf_font.hpp +++ b/libs/fonts/include/psemek/fonts/msdf_font.hpp @@ -3,7 +3,7 @@ #include #include -namespace psemek::fonts +namespace psemek::fonts::inline v1 { struct msdf_font diff --git a/libs/fonts/source/default_fonts.cpp b/libs/fonts/source/default_fonts.cpp index d04973a2..0ede52fc 100644 --- a/libs/fonts/source/default_fonts.cpp +++ b/libs/fonts/source/default_fonts.cpp @@ -13,7 +13,7 @@ #include -namespace psemek::fonts +namespace psemek::fonts::inline v1 { std::unique_ptr make_default_monospace_9x12_font() diff --git a/libs/fonts/source/kerned_font.cpp b/libs/fonts/source/kerned_font.cpp index 0e8b2846..8ded8d5d 100644 --- a/libs/fonts/source/kerned_font.cpp +++ b/libs/fonts/source/kerned_font.cpp @@ -3,7 +3,7 @@ #include #include -namespace psemek::fonts +namespace psemek::fonts::inline v1 { kerned_font::kerned_font(bmfont_data data, gfx::texture_2d atlas) diff --git a/libs/fonts/source/msdf_font.cpp b/libs/fonts/source/msdf_font.cpp index 43678560..ee8765b9 100644 --- a/libs/fonts/source/msdf_font.cpp +++ b/libs/fonts/source/msdf_font.cpp @@ -1,7 +1,7 @@ #include #include -namespace psemek::fonts +namespace psemek::fonts::inline v1 { std::unique_ptr make_msdf_font(io::istream && description, io::istream && texture) From e57b284ffcdab60e8f8a5c52ffcb2608867aefe1 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 6 Aug 2025 17:53:24 +0300 Subject: [PATCH 064/141] Add constexpr md5 hash implementation --- libs/util/include/psemek/util/md5.hpp | 12 ++ libs/util/include/psemek/util/md5_inl.hpp | 202 ++++++++++++++++++++++ libs/util/source/md5.cpp | 12 ++ 3 files changed, 226 insertions(+) create mode 100644 libs/util/include/psemek/util/md5.hpp create mode 100644 libs/util/include/psemek/util/md5_inl.hpp create mode 100644 libs/util/source/md5.cpp diff --git a/libs/util/include/psemek/util/md5.hpp b/libs/util/include/psemek/util/md5.hpp new file mode 100644 index 00000000..9d726a7e --- /dev/null +++ b/libs/util/include/psemek/util/md5.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include + +namespace psemek::util +{ + + std::array md5_hash(std::string_view data); + +} diff --git a/libs/util/include/psemek/util/md5_inl.hpp b/libs/util/include/psemek/util/md5_inl.hpp new file mode 100644 index 00000000..80c4917d --- /dev/null +++ b/libs/util/include/psemek/util/md5_inl.hpp @@ -0,0 +1,202 @@ +#pragma once + +#include +#include +#include + +namespace psemek::util +{ + + // Constexpr MD5 hash implementation. + // Partially based on https://github.com/Wodann/constexpr-md5-cpp/blob/master/include/md5.h + + namespace md5_detail + { + + constexpr std::uint32_t f(std::uint32_t b, std::uint32_t c, std::uint32_t d) + { + return (b & c) | (~b & d); + } + + constexpr std::uint32_t g(std::uint32_t b, std::uint32_t c, std::uint32_t d) + { + return (b & d) | (c & ~d); + } + + constexpr std::uint32_t h(std::uint32_t b, std::uint32_t c, std::uint32_t d) + { + return b ^ c ^ d; + } + + constexpr std::uint32_t i(std::uint32_t b, std::uint32_t c, std::uint32_t d) + { + return c ^ (b | ~d); + } + + using Fn = uint32_t(*)(uint32_t, uint32_t, uint32_t); + + constexpr Fn F[4] = { f, g, h, i }; + + constexpr std::uint32_t G[64] = + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, + 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, + 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 + }; + + constexpr std::uint32_t K[64] = + { + 0xd76aa478u, 0xe8c7b756u, 0x242070dbu, 0xc1bdceeeu, + 0xf57c0fafu, 0x4787c62au, 0xa8304613u, 0xfd469501u, + 0x698098d8u, 0x8b44f7afu, 0xffff5bb1u, 0x895cd7beu, + 0x6b901122u, 0xfd987193u, 0xa679438eu, 0x49b40821u, + 0xf61e2562u, 0xc040b340u, 0x265e5a51u, 0xe9b6c7aau, + 0xd62f105du, 0x02441453u, 0xd8a1e681u, 0xe7d3fbc8u, + 0x21e1cde6u, 0xc33707d6u, 0xf4d50d87u, 0x455a14edu, + 0xa9e3e905u, 0xfcefa3f8u, 0x676f02d9u, 0x8d2a4c8au, + 0xfffa3942u, 0x8771f681u, 0x6d9d6122u, 0xfde5380cu, + 0xa4beea44u, 0x4bdecfa9u, 0xf6bb4b60u, 0xbebfbc70u, + 0x289b7ec6u, 0xeaa127fau, 0xd4ef3085u, 0x04881d05u, + 0xd9d4d039u, 0xe6db99e5u, 0x1fa27cf8u, 0xc4ac5665u, + 0xf4292244u, 0x432aff97u, 0xab9423a7u, 0xfc93a039u, + 0x655b59c3u, 0x8f0ccc92u, 0xffeff47du, 0x85845dd1u, + 0x6fa87e4fu, 0xfe2ce6e0u, 0xa3014314u, 0x4e0811a1u, + 0xf7537e82u, 0xbd3af235u, 0x2ad7d2bbu, 0xeb86d391u, + }; + + constexpr std::uint32_t S[16] = + { + 7, 12, 17, 22, + 5, 9, 14, 20, + 4, 11, 16, 23, + 6, 10, 15, 21 + }; + + constexpr char PADDING[64] = + { + -128, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + + constexpr std::uint32_t rotate(std::uint32_t x, std::uint32_t n) + { + return (x << n) | (x >> (32 - n)); + } + + template + constexpr std::uint32_t t(F f, std::uint32_t a, std::uint32_t b, std::uint32_t c, std::uint32_t d, std::uint32_t x, std::uint32_t s, std::uint32_t ac) + { + return b + rotate(a + f(b, c, d) + x + ac, s); + } + + constexpr uint32_t to_uint32(unsigned char const * data) + { + return 0 + | (static_cast(data[0]) << 0) + | (static_cast(data[1]) << 8) + | (static_cast(data[2]) << 16) + | (static_cast(data[3]) << 24) + ; + } + + + struct hasher + { + std::uint8_t buffer[64]; + std::uint32_t state[4] = { 0x67452301u, 0xefcdab89u, 0x98badcfeu, 0x10325476u }; + std::uint32_t nl = 0; + std::uint32_t nh = 0; + + constexpr void append(std::string_view data) + { + std::uint32_t input[16]; + + std::uint32_t k = (nl >> 3) & 0x3f; + + std::uint32_t length = data.size(); + nl += length << 3; + if (nl < length << 3) + nh += 1; + nh += length >> 29; + + for (char c : data) + { + buffer[k++] = static_cast(static_cast(c) + 256); + + if (k == 0x40) + { + for (int i = 0; i < 16; ++i) + input[i] = to_uint32(buffer + i * 4); + transform(input); + k = 0; + } + } + } + + constexpr void transform(std::uint32_t const (&input)[16]) + { + std::uint32_t a = state[0], b = state[1], c = state[2], d = state[3]; + for (uint32_t r = 0; r < 4; ++r) + { + const auto g = G + r * 16; + const auto s = S + r * 4; + const auto k = K + r * 16; + + for (std::uint32_t i = 0; i < 16; ++i) + { + const auto new_b = t(F[r], a, b, c, d, input[g[i]], s[i % 4], k[i]); + a = d; + d = c; + c = b; + b = new_b; + } + } + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + } + + constexpr std::array result() + { + std::uint32_t input[16]; + + const auto k = ((nl >> 3) & 0x3f); + input[14] = nl; + input[15] = nh; + + append(std::string_view(PADDING, k < 56 ? 56 - k : 120 - k)); + + for (int i = 0; i < 14; ++i) { + input[i] = to_uint32(buffer + i * 4); + } + transform(input); + + return {state[0], state[1], state[2], state[3]}; + } + }; + + } + + namespace md5_inl + { + + using hasher = md5_detail::hasher; + + constexpr auto hash(std::string_view data) + { + hasher h; + h.append(data); + return h.result(); + } + + } + +} diff --git a/libs/util/source/md5.cpp b/libs/util/source/md5.cpp new file mode 100644 index 00000000..dbea84cb --- /dev/null +++ b/libs/util/source/md5.cpp @@ -0,0 +1,12 @@ +#include +#include + +namespace psemek::util +{ + + std::array md5_hash(std::string_view data) + { + return md5_inl::hash(data); + } + +} From 7b1ed4bd956a7db7bdc49972e24f3554367c857a Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 6 Aug 2025 17:54:24 +0300 Subject: [PATCH 065/141] Make util::make_uuid use md5 hash --- libs/util/include/psemek/util/uuid.hpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/libs/util/include/psemek/util/uuid.hpp b/libs/util/include/psemek/util/uuid.hpp index 9170f956..3987444f 100644 --- a/libs/util/include/psemek/util/uuid.hpp +++ b/libs/util/include/psemek/util/uuid.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -46,18 +47,14 @@ namespace psemek::util // TODO: use SHA-1 or something like that? constexpr uuid make_uuid(std::string_view str) { - struct char_hash - { - constexpr std::size_t operator()(char c) const noexcept - { - return static_cast(c); - } - }; + auto md5_result = md5_inl::hash(str); + + uuid result; + result.values[0] = static_cast(md5_result[0]) | (static_cast(md5_result[1]) << 32); + result.values[1] = static_cast(md5_result[2]) | (static_cast(md5_result[3]) << 32); - uuid result{0x6eb49abceae6db24ull, 0x476c4c69fd7925a7ull}; - hash_sequence(result[0], str.begin(), str.end(), char_hash{}); - hash_sequence(result[1], str.begin(), str.end(), char_hash{}); make_rfc_4122(result); + return result; } From 0e000b190d0b13028147c8abcdaff7d68e3e0acf Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 8 Aug 2025 23:19:06 +0300 Subject: [PATCH 066/141] Nump minimum cmake version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 43d106e2..5952c592 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.10) project(psemek) cmake_policy(SET CMP0077 NEW) From d3366b56aab7ead6d1a3845f5a12dafc6e0a6389 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 8 Aug 2025 23:19:48 +0300 Subject: [PATCH 067/141] Support version argument in find-wgpu-native script --- cmake/modules/Findwgpu-native.cmake | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/cmake/modules/Findwgpu-native.cmake b/cmake/modules/Findwgpu-native.cmake index 517d3c06..c146786a 100644 --- a/cmake/modules/Findwgpu-native.cmake +++ b/cmake/modules/Findwgpu-native.cmake @@ -3,16 +3,27 @@ if(wgpu-native_FOUND) endif() # Don't search for include files - these are bundled with psemek-wgpu lib -find_library(wgpu-native_LIBRARIES NAMES libwgpu_native.a wgpu_native.dll wgpu_native PATHS "${WGPU_NATIVE_ROOT}") +find_library(wgpu-native_LIBRARY NAMES libwgpu_native.a wgpu_native.dll wgpu_native PATHS "${WGPU_NATIVE_ROOT}" "${WGPU_NATIVE_ROOT}/lib") +find_file(wgpu-native_VERSION_FILE wgpu-native-git-tag PATHS "${WGPU_NATIVE_ROOT}/wgpu-native-meta") + +if(EXISTS "${wgpu-native_VERSION_FILE}") + file(READ "${wgpu-native_VERSION_FILE}" wgpu-native_VERSION_NOT_STRIPPED) + string(STRIP "${wgpu-native_VERSION_NOT_STRIPPED}" wgpu-native_VERSION_STRIPPED) + string(SUBSTRING "${wgpu-native_VERSION_STRIPPED}" 1 -1 wgpu-native_VERSION) +endif() include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(wgpu-native DEFAULT_MSG wgpu-native_LIBRARIES) +find_package_handle_standard_args(wgpu-native + REQUIRED_VARS wgpu-native_LIBRARY wgpu-native_VERSION_FILE + VERSION_VAR wgpu-native_VERSION +) if(wgpu-native_FOUND AND NOT TARGET wgpu-native) + set(wgpu-native_LIBRARIES ${wgpu-native_LIBRARY}) add_library(wgpu-native STATIC IMPORTED) set_target_properties(wgpu-native PROPERTIES IMPORTED_LOCATION "${wgpu-native_LIBRARIES}" ) endif() -mark_as_advanced(wgpu-native_LIBRARIES) +mark_as_advanced(wgpu-native_LIBRARY wgpu-native_VERSION_FILE) From 46f672599d7c4979b185cca524a49fa0b9396d6f Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 8 Aug 2025 23:20:44 +0300 Subject: [PATCH 068/141] Upgrade to wgpu-native version 25.0.2.1 --- CMakeLists.txt | 2 +- libs/sdl2/source/window.cpp | 38 +- libs/wgpu/include/psemek/wgpu/adapter.hpp | 51 +- .../include/psemek/wgpu/bind_group_layout.hpp | 35 +- libs/wgpu/include/psemek/wgpu/buffer.hpp | 53 +- .../include/psemek/wgpu/callback_mode.hpp | 15 + .../include/psemek/wgpu/chained_struct.hpp | 1 + .../include/psemek/wgpu/command_encoder.hpp | 6 +- .../psemek/wgpu/compute_pass_encoder.hpp | 2 + .../include/psemek/wgpu/detail/status.hpp | 18 + .../psemek/wgpu/detail/string_view.hpp | 21 + libs/wgpu/include/psemek/wgpu/device.hpp | 112 +- .../include/psemek/wgpu/external/webgpu.h | 3311 +++++++++++++---- libs/wgpu/include/psemek/wgpu/external/wgpu.h | 175 +- libs/wgpu/include/psemek/wgpu/image_copy.hpp | 19 +- libs/wgpu/include/psemek/wgpu/instance.hpp | 4 +- libs/wgpu/include/psemek/wgpu/query_set.hpp | 4 +- libs/wgpu/include/psemek/wgpu/queue.hpp | 13 +- .../psemek/wgpu/render_bundle_encoder.hpp | 4 +- .../psemek/wgpu/render_pass_encoder.hpp | 14 +- .../include/psemek/wgpu/render_pipeline.hpp | 180 +- libs/wgpu/include/psemek/wgpu/sampler.hpp | 35 +- .../include/psemek/wgpu/shader_module.hpp | 36 +- .../wgpu/include/psemek/wgpu/shader_stage.hpp | 10 +- libs/wgpu/include/psemek/wgpu/surface.hpp | 40 +- libs/wgpu/include/psemek/wgpu/texture.hpp | 246 +- .../wgpu/include/psemek/wgpu/texture_view.hpp | 12 +- libs/wgpu/source/adapter.cpp | 150 +- libs/wgpu/source/bind_group.cpp | 5 +- libs/wgpu/source/bind_group_layout.cpp | 5 +- libs/wgpu/source/buffer.cpp | 18 +- libs/wgpu/source/chained_struct.cpp | 26 + libs/wgpu/source/command_buffer.cpp | 5 +- libs/wgpu/source/command_encoder.cpp | 100 +- libs/wgpu/source/compute_pass_encoder.cpp | 20 +- libs/wgpu/source/compute_pipeline.cpp | 5 +- libs/wgpu/source/device.cpp | 177 +- libs/wgpu/source/instance.cpp | 25 +- libs/wgpu/source/logging.cpp | 4 +- libs/wgpu/source/pipeline_layout.cpp | 5 +- libs/wgpu/source/query_set.cpp | 5 +- libs/wgpu/source/queue.cpp | 40 +- libs/wgpu/source/render_bundle.cpp | 5 +- libs/wgpu/source/render_bundle_encoder.cpp | 15 +- libs/wgpu/source/render_pass_encoder.cpp | 20 +- libs/wgpu/source/render_pipeline.cpp | 5 +- libs/wgpu/source/sampler.cpp | 5 +- libs/wgpu/source/shader_module.cpp | 57 +- libs/wgpu/source/surface.cpp | 52 +- libs/wgpu/source/texture.cpp | 7 +- libs/wgpu/source/texture_view.cpp | 5 +- 51 files changed, 3813 insertions(+), 1405 deletions(-) create mode 100644 libs/wgpu/include/psemek/wgpu/callback_mode.hpp create mode 100644 libs/wgpu/include/psemek/wgpu/detail/status.hpp create mode 100644 libs/wgpu/include/psemek/wgpu/detail/string_view.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5952c592..faf923b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,7 +69,7 @@ if(NOT DEFINED PSEMEK_GRAPHICS_API) endif() if(PSEMEK_GRAPHICS_API STREQUAL WEBGPU) - find_package(wgpu-native REQUIRED) + find_package(wgpu-native 25.0.2.1 REQUIRED) endif() message(STATUS "Using graphics API ${PSEMEK_GRAPHICS_API}") diff --git a/libs/sdl2/source/window.cpp b/libs/sdl2/source/window.cpp index b673046a..34009f34 100644 --- a/libs/sdl2/source/window.cpp +++ b/libs/sdl2/source/window.cpp @@ -92,21 +92,43 @@ namespace psemek::sdl2 wgpu_surface_ = instance.create_surface(surface_descriptor); wgpu::adapter::request_options adapter_request_options; + // adapter_request_options.feature_level = wgpu::feature_level::compatibility; adapter_request_options.compatible_surface = wgpu_surface_; adapter_request_options.backend_type = wgpu::backend_type::undefined; - instance.request_adapter(adapter_request_options, [this](wgpu::adapter::request_status status, wgpu::adapter adapter_in, std::string const & message) + instance.request_adapter(wgpu::callback_mode::allow_process_events, adapter_request_options, [this](wgpu::adapter::request_status status, wgpu::adapter adapter_in, std::string_view message) { if (status != wgpu::adapter::request_status::success) - throw std::runtime_error("Failed to request WebGPU adapter: " + message); + throw std::runtime_error("Failed to request WebGPU adapter: " + std::string(message)); wgpu_adapter_ = std::move(adapter_in); }); + [[maybe_unused]] auto adapter_limits = wgpu_adapter_.get_limits(); + wgpu::device::descriptor device_descriptor { .required_features = options.required_features, .required_limits = std::nullopt, + .lost_callback = [](wgpu::device::lost_reason reason, std::string_view message) + { + std::ostringstream os; + os << "WebGPU device lost, reason: " << static_cast(reason) << ", message: " << message; + auto str = os.str(); + log::error() << str; + // This will most probably panic in wgpu-native internals + throw util::exception(std::move(str)); + }, + .lost_callback_mode = wgpu::callback_mode::allow_process_events, + .uncaptured_error_callback = [](wgpu::error_type type, std::string_view message) + { + std::ostringstream os; + os << "Uncaptured WebGPU error, type: " << static_cast(type) << ", message: " << message; + auto str = os.str(); + log::error() << str; + // This will most probably panic in wgpu-native internals + throw util::exception(std::move(str)); + }, }; if (options.required_limits || options.required_native_limits) @@ -118,16 +140,18 @@ namespace psemek::sdl2 if (options.required_native_limits) device_descriptor.required_limits->chain.push_back(wgpu::native_limits{*options.required_native_limits}); - wgpu_adapter_.request_device(device_descriptor, [this](wgpu::device::request_status status, wgpu::device device_in, std::string const & message) + wgpu_adapter_.request_device(wgpu::callback_mode::allow_process_events, device_descriptor, [this](wgpu::device::request_status status, wgpu::device device_in, std::string_view message) { if (status != wgpu::device::request_status::success) - throw std::runtime_error("Failed to request WebGPU device: " + message); + throw std::runtime_error("Failed to request WebGPU device: " + std::string(message)); wgpu_device_ = std::move(device_in); }); - auto adapter_properties = wgpu_adapter_.get_properties(); + [[maybe_unused]] auto device_limits = wgpu_device_.get_limits(); + + auto adapter_info = wgpu_adapter_.get_info(); std::string adapter_backend_str; - switch (adapter_properties.backend_type) + switch (adapter_info.backend_type) { case wgpu::backend_type::undefined: adapter_backend_str = "undefined"; @@ -158,7 +182,7 @@ namespace psemek::sdl2 break; } - log::info() << "Initialized WebGPU: " << adapter_properties.name << ", " << adapter_backend_str << " backend"; + log::info() << "Initialized WebGPU: " << adapter_info.device << ", " << adapter_backend_str << " backend"; log::info() << "Using wgpu-native version " << wgpu::get_version(); #endif } diff --git a/libs/wgpu/include/psemek/wgpu/adapter.hpp b/libs/wgpu/include/psemek/wgpu/adapter.hpp index 25b27919..fc560ba3 100644 --- a/libs/wgpu/include/psemek/wgpu/adapter.hpp +++ b/libs/wgpu/include/psemek/wgpu/adapter.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -11,6 +12,12 @@ namespace psemek::wgpu { + enum class feature_level : std::uint32_t + { + compatibility = 1, + core = 2, + }; + enum class power_preference : std::uint32_t { undefined = 0x00000000, @@ -39,47 +46,49 @@ namespace psemek::wgpu struct request_options { std::vector chain = {}; - surface compatible_surface = {}; + wgpu::feature_level feature_level = feature_level::core; wgpu::power_preference power_preference = power_preference::high; - wgpu::backend_type backend_type = backend_type::vulkan; bool force_fallback_adapter = false; + wgpu::backend_type backend_type = backend_type::vulkan; + surface compatible_surface = {}; }; enum class request_status : std::uint32_t { - success = 0x00000000, - unavailable = 0x00000001, - error = 0x00000002, - unknown = 0x00000003, + success = 0x00000001, + instance_dropped = 0x00000002, + unavailable = 0x00000003, + error = 0x00000004, + unknown = 0x00000005, }; - using request_callback = std::function; + using request_callback = std::function; enum class type : std::uint32_t { - discrete_gpu = 0x00000000, - integrated_gpu = 0x00000001, - cpu = 0x00000002, - unknown = 0x00000003, + discrete_gpu = 0x00000001, + integrated_gpu = 0x00000002, + cpu = 0x00000003, + unknown = 0x00000004, }; - struct properties + struct info { - std::uint32_t vendor_id; - std::string vendor_name; - std::string architecture; - std::uint32_t device_id; - std::string name; - std::string driver_description; - enum type adapter_type; + std::string_view vendor; + std::string_view architecture; + std::string_view device; + std::string_view description; wgpu::backend_type backend_type; + type adapter_type; + uint32_t vendor_id; + uint32_t device_id; }; std::vector enumerate_features(); limits get_limits(); - properties get_properties(); + info get_info(); bool has_feature(feature feature); - void request_device(device::descriptor const & descriptor, device::request_callback const & callback); + void request_device(callback_mode mode, device::descriptor const & descriptor, device::request_callback const & callback); static void reference(void * ptr); static void release(void * ptr); diff --git a/libs/wgpu/include/psemek/wgpu/bind_group_layout.hpp b/libs/wgpu/include/psemek/wgpu/bind_group_layout.hpp index d9b0726f..270a292c 100644 --- a/libs/wgpu/include/psemek/wgpu/bind_group_layout.hpp +++ b/libs/wgpu/include/psemek/wgpu/bind_group_layout.hpp @@ -14,54 +14,57 @@ namespace psemek::wgpu enum class buffer_binding_type : std::uint32_t { - undefined = 0x00000000, - uniform = 0x00000001, - storage = 0x00000002, - read_only_storage = 0x00000003, + binding_not_used = 0x00000000, + undefined = 0x00000001, + uniform = 0x00000002, + storage = 0x00000003, + read_only_storage = 0x00000004, }; struct buffer_binding_layout { std::vector chain = {}; - buffer_binding_type type = buffer_binding_type::undefined; + buffer_binding_type type = buffer_binding_type::binding_not_used; bool has_dynamic_offset = false; std::uint64_t min_binding_size = 0; }; enum class sampler_binding_type : std::uint32_t { - undefined = 0x00000000, - filtering = 0x00000001, - nonfiltering = 0x00000002, - comparison = 0x00000003, + binding_not_used = 0x00000000, + undefined = 0x00000001, + filtering = 0x00000002, + nonfiltering = 0x00000003, + comparison = 0x00000004, }; struct sampler_binding_layout { std::vector chain = {}; - sampler_binding_type type = sampler_binding_type::undefined; + sampler_binding_type type = sampler_binding_type::binding_not_used; }; struct texture_binding_layout { std::vector chain = {}; - texture::sample_type sample_type = texture::sample_type::undefined; + texture::sample_type sample_type = texture::sample_type::binding_not_used; texture_view::dimension view_dimension = texture_view::dimension::undefined; bool multisampled = false; }; enum class storage_texture_access : std::uint32_t { - undefined = 0x00000000, - write_only = 0x00000001, - read_only = 0x00000002, - read_write = 0x00000003, + binding_not_used = 0x00000000, + undefined = 0x00000001, + write_only = 0x00000002, + read_only = 0x00000003, + read_write = 0x00000004, }; struct storage_texture_binding_layout { std::vector chain = {}; - storage_texture_access access = storage_texture_access::undefined; + storage_texture_access access = storage_texture_access::binding_not_used; texture::format format = texture::format::undefined; texture_view::dimension view_dimension = texture_view::dimension::undefined; }; diff --git a/libs/wgpu/include/psemek/wgpu/buffer.hpp b/libs/wgpu/include/psemek/wgpu/buffer.hpp index 4d05e1f6..ba0ded37 100644 --- a/libs/wgpu/include/psemek/wgpu/buffer.hpp +++ b/libs/wgpu/include/psemek/wgpu/buffer.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -15,46 +16,42 @@ namespace psemek::wgpu { using detail::object::object; - enum class usage : std::uint32_t + enum class usage : std::uint64_t { - none = 0x00000000, - map_read = 0x00000001, - map_write = 0x00000002, - copy_src = 0x00000004, - copy_dst = 0x00000008, - index = 0x00000010, - vertex = 0x00000020, - uniform = 0x00000040, - storage = 0x00000080, - indirect = 0x00000100, + none = 0x00000000, + map_read = 0x00000001, + map_write = 0x00000002, + copy_src = 0x00000004, + copy_dst = 0x00000008, + index = 0x00000010, + vertex = 0x00000020, + uniform = 0x00000040, + storage = 0x00000080, + indirect = 0x00000100, query_resolve = 0x00000200, }; - enum class map_mode : std::uint32_t + enum class map_mode : std::uint64_t { - none = 0x00000000, - read = 0x00000001, + none = 0x00000000, + read = 0x00000001, write = 0x00000002, }; enum class map_state : std::uint32_t { - unmapped = 0x00000000, - pending = 0x00000001, - mapped = 0x00000002, + unmapped = 0x00000001, + pending = 0x00000002, + mapped = 0x00000003, }; enum class map_async_status : std::uint32_t { - success = 0x00000000, - validation_error = 0x00000001, - unknown = 0x00000002, - device_lost = 0x00000003, - destroyed_before_callback = 0x00000004, - unmapped_before_callback = 0x00000005, - mapping_already_pending = 0x00000006, - offset_out_of_range = 0x00000007, - size_out_of_range = 0x00000008, + success = 0x00000001, + instance_dropped = 0x00000002, + error = 0x00000003, + aborted = 0x00000004, + unknown = 0x00000005, }; struct descriptor { @@ -65,7 +62,7 @@ namespace psemek::wgpu bool mapped_at_creation = false; }; - using map_callback = std::function; + using map_callback = std::function; void destroy(); std::uint64_t get_size(); @@ -73,7 +70,7 @@ namespace psemek::wgpu map_state get_map_state(); void const * get_const_mapped_range(std::size_t offset, std::size_t size); void * get_mapped_range(std::size_t offset, std::size_t size); - void map_async(map_mode flags, std::size_t offset, std::size_t size, map_callback const & callback); + void map_async(callback_mode mode, map_mode flags, std::size_t offset, std::size_t size, map_callback const & callback); void unmap(); void set_label(std::string const & label); diff --git a/libs/wgpu/include/psemek/wgpu/callback_mode.hpp b/libs/wgpu/include/psemek/wgpu/callback_mode.hpp new file mode 100644 index 00000000..5b8262f3 --- /dev/null +++ b/libs/wgpu/include/psemek/wgpu/callback_mode.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace psemek::wgpu +{ + + enum class callback_mode : std::uint32_t + { + wait_any_only = 0x00000001, + allow_process_events = 0x00000002, + allow_spontaneous = 0x00000003, + }; + +} diff --git a/libs/wgpu/include/psemek/wgpu/chained_struct.hpp b/libs/wgpu/include/psemek/wgpu/chained_struct.hpp index f13b0472..2e018d99 100644 --- a/libs/wgpu/include/psemek/wgpu/chained_struct.hpp +++ b/libs/wgpu/include/psemek/wgpu/chained_struct.hpp @@ -45,6 +45,7 @@ namespace psemek::wgpu }; void * fill_chain(std::vector const & chain); + void * fill_chain_out(std::vector const & chain); using chained_struct_ptr = std::shared_ptr; diff --git a/libs/wgpu/include/psemek/wgpu/command_encoder.hpp b/libs/wgpu/include/psemek/wgpu/command_encoder.hpp index ed5d7c7d..4c466caf 100644 --- a/libs/wgpu/include/psemek/wgpu/command_encoder.hpp +++ b/libs/wgpu/include/psemek/wgpu/command_encoder.hpp @@ -31,9 +31,9 @@ namespace psemek::wgpu render_pass_encoder begin_render_pass(render_pass_encoder::descriptor const & desc); void clear_buffer(buffer const & buffer, std::uint64_t offset, std::uint64_t size); void copy_buffer_to_buffer(buffer const & source, std::uint64_t source_offset, buffer const & destination, std::uint64_t destination_offset, std::uint64_t size); - void copy_buffer_to_texture(image_copy_buffer const & source, image_copy_texture const & destination, math::vector const & extent); - void copy_texture_to_buffer(image_copy_texture const & source, image_copy_buffer const & destination, math::vector const & extent); - void copy_texture_to_texture(image_copy_texture const & source, image_copy_texture const & destination, math::vector const & extent); + void copy_buffer_to_texture(texel_copy_buffer_info const & source, texel_copy_texture_info const & destination, math::vector const & extent); + void copy_texture_to_buffer(texel_copy_texture_info const & source, texel_copy_buffer_info const & destination, math::vector const & extent); + void copy_texture_to_texture(texel_copy_texture_info const & source, texel_copy_texture_info const & destination, math::vector const & extent); void insert_debug_marker(std::string const & marker_label); void push_debug_group(std::string const & group_label); void pop_debug_group(); diff --git a/libs/wgpu/include/psemek/wgpu/compute_pass_encoder.hpp b/libs/wgpu/include/psemek/wgpu/compute_pass_encoder.hpp index 14b30fc8..b3d1d4d1 100644 --- a/libs/wgpu/include/psemek/wgpu/compute_pass_encoder.hpp +++ b/libs/wgpu/include/psemek/wgpu/compute_pass_encoder.hpp @@ -37,6 +37,8 @@ namespace psemek::wgpu void set_pipeline(compute_pipeline const & pipeline); void dispatch_workgroups(math::vector const & workgroup_count); void dispatch_workgroups_indirect(buffer const & indirect_buffer, std::uint64_t offset); + void set_push_constants(std::uint32_t offset, util::span data); + void write_timestamp(query_set const & query_set, std::uint32_t query_index); void insert_debug_marker(std::string const & marker_label); void push_debug_group(std::string const & group_label); void pop_debug_group(); diff --git a/libs/wgpu/include/psemek/wgpu/detail/status.hpp b/libs/wgpu/include/psemek/wgpu/detail/status.hpp new file mode 100644 index 00000000..f4bde899 --- /dev/null +++ b/libs/wgpu/include/psemek/wgpu/detail/status.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include +#include + +namespace psemek::wgpu::detail +{ + + inline void check_status(std::string_view caption, WGPUStatus status) + { + if (status != WGPUStatus_Success) + throw util::exception("wgpu error in " + std::string(caption)); + } + +} diff --git a/libs/wgpu/include/psemek/wgpu/detail/string_view.hpp b/libs/wgpu/include/psemek/wgpu/detail/string_view.hpp new file mode 100644 index 00000000..646dc6c9 --- /dev/null +++ b/libs/wgpu/include/psemek/wgpu/detail/string_view.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include +#include + +namespace psemek::wgpu::detail +{ + + inline WGPUStringView to_string_view(std::string const & string) + { + return {.data = string.data(), .length = string.size()}; + } + + inline WGPUStringView to_string_view(std::string_view const & string_view) + { + return {.data = string_view.data(), .length = string_view.size()}; + } + +} diff --git a/libs/wgpu/include/psemek/wgpu/device.hpp b/libs/wgpu/include/psemek/wgpu/device.hpp index 27fef999..b0f5f432 100644 --- a/libs/wgpu/include/psemek/wgpu/device.hpp +++ b/libs/wgpu/include/psemek/wgpu/device.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -26,16 +27,22 @@ namespace psemek::wgpu enum class feature : std::uint32_t { undefined = 0x00000000, depth_clip_control = 0x00000001, - depth32float_stencil8 = 0x00000002, + depth32_float_stencil8 = 0x00000002, timestamp_query = 0x00000003, - texture_compression_bc = 0x00000004, - texture_compression_etc2 = 0x00000005, - texture_compression_astc = 0x00000006, - indirect_first_instance = 0x00000007, - shader_f16 = 0x00000008, - rg11b10ufloat_renderable = 0x00000009, - bgra8unorm_storage = 0x0000000a, - float32_filterable = 0x0000000b, + texture_compression_BC = 0x00000004, + texture_compression_BC_sliced_3D = 0x00000005, + texture_compression_ETC2 = 0x00000006, + texture_compression_ASTC = 0x00000007, + texture_compression_ASTC_sliced_3D = 0x00000008, + indirect_first_instance = 0x00000009, + shader_f16 = 0x0000000A, + rg11b10_ufloat_renderable = 0x0000000B, + bgra8_unorm_storage = 0x0000000C, + float32_filterable = 0x0000000D, + float32_blendable = 0x0000000E, + clip_distances = 0x0000000F, + dual_source_blending = 0x00000010, + push_constants = 0x00030001, texture_adapter_specific_format_features = 0x00030002, @@ -45,6 +52,27 @@ namespace psemek::wgpu texture_binding_array = 0x00030006, sampled_texture_and_storage_buffer_array_non_uniform_indexing = 0x00030007, pipeline_statistics_query = 0x00030008, + storage_resource_binding_array = 0x00030009, + partially_bound_binding_array = 0x0003000a, + texture_format16bit_norm = 0x0003000b, + texture_compression_astc_hdr = 0x0003000c, + mappable_primary_buffers = 0x0003000e, + buffer_binding_array = 0x0003000f, + uniform_buffer_and_storage_texture_array_non_uniform_indexing = 0x00030010, + spirv_shader_passthrough = 0x00030017, + vertex_attribute64bit = 0x00030019, + texture_format_nv12 = 0x0003001a, + ray_tracing_acceleration_structure = 0x0003001b, + ray_query = 0x0003001c, + shader_f64 = 0x0003001d, + shader_i16 = 0x0003001e, + shader_primitive_index = 0x0003001f, + shader_early_depth_test = 0x00030020, + subgroup = 0x00030021, + subgroup_vertex = 0x00030022, + subgroup_barrier = 0x00030023, + timestamp_query_inside_encoders = 0x00030024, + timestamp_query_inside_passes = 0x00030025, }; struct limits @@ -71,7 +99,6 @@ namespace psemek::wgpu std::uint64_t max_buffer_size = 256 * 1024 * 1024; std::uint32_t max_vertex_attributes = 16; std::uint32_t max_vertex_buffer_array_stride = 2048; - std::uint32_t max_inter_stage_shader_components = 60; std::uint32_t max_inter_stage_shader_variables = 15; std::uint32_t max_color_attachments = 1; std::uint32_t max_color_attachment_bytes_per_sample = 4; @@ -91,29 +118,34 @@ namespace psemek::wgpu enum class create_pipeline_async_status : std::uint32_t { - success = 0x00000000, - validation_error = 0x00000001, - internal_error = 0x00000002, - device_lost = 0x00000003, - device_destroyed = 0x00000004, - unknown = 0x00000005, + success = 0x00000001, + instance_dropped = 0x00000002, + validation_error = 0x00000003, + internal_error = 0x00000004, + unknown = 0x00000005, }; enum class error_filter : std::uint32_t { - validation = 0x00000000, - out_of_memory = 0x00000001, - internal = 0x00000002, + validation = 0x00000001, + out_of_memory = 0x00000002, + internal = 0x00000003, }; enum class error_type : std::uint32_t { - no_error = 0x00000000, - validation = 0x00000001, - out_of_memory = 0x00000002, - internal = 0x00000003, - unknown = 0x00000004, - device_lost = 0x00000005, + no_error = 0x00000001, + validation = 0x00000002, + out_of_memory = 0x00000003, + internal = 0x00000004, + unknown = 0x00000005, + }; + + enum class pop_error_scope_status : std::uint32_t + { + success = 0x00000001, + instance_dropped = 0x00000002, + empty_stack = 0x00000003, }; struct device @@ -123,11 +155,14 @@ namespace psemek::wgpu enum class lost_reason : std::uint32_t { - undefined = 0x00000000, - destroyed = 0x00000001, + unknown = 0x00000001, + destroyed = 0x00000002, + instance_dropped = 0x00000003, + failed_creation = 0x00000004, }; - using lost_callback = std::function; + using lost_callback = std::function; + using uncaptured_error_callback = std::function; struct required_limits { @@ -143,18 +178,22 @@ namespace psemek::wgpu std::optional required_limits = {}; queue::descriptor default_queue = {}; device::lost_callback lost_callback = {}; + callback_mode lost_callback_mode = {}; + device::uncaptured_error_callback uncaptured_error_callback = {}; }; enum class request_status : std::uint32_t { - success = 0x00000000, - error = 0x00000001, - unknown = 0x00000002, + success = 0x00000001, + instance_dropped = 0x00000002, + error = 0x00000003, + unknown = 0x00000004, }; using request_callback = std::function; - using create_compute_pipeline_async_callback = std::function; - using create_render_pipeline_async_callback = std::function; + using create_compute_pipeline_async_callback = std::function; + using create_render_pipeline_async_callback = std::function; + using pop_error_callback = std::function; using error_callback = std::function; queue get_queue(); @@ -163,11 +202,11 @@ namespace psemek::wgpu buffer create_buffer(buffer::descriptor const & desc); command_encoder create_command_encoder(command_encoder::descriptor const & desc); compute_pipeline create_compute_pipeline(compute_pipeline::descriptor const & desc); - void create_compute_pipeline_async(compute_pipeline::descriptor const & desc, create_compute_pipeline_async_callback const & callback); + void create_compute_pipeline_async(callback_mode mode, compute_pipeline::descriptor const & desc, create_compute_pipeline_async_callback const & callback); pipeline_layout create_pipeline_layout(pipeline_layout::descriptor const & desc); query_set create_query_set(query_set::descriptor const & desc); render_pipeline create_render_pipeline(render_pipeline::descriptor const & desc); - void create_render_pipeline_async(render_pipeline::descriptor const & desc, create_render_pipeline_async_callback const & callback); + void create_render_pipeline_async(callback_mode mode, render_pipeline::descriptor const & desc, create_render_pipeline_async_callback const & callback); render_bundle_encoder create_render_bundle_encoder(render_bundle_encoder::descriptor const & desc); sampler create_sampler(sampler::descriptor const & desc); shader_module create_shader_module(shader_module::descriptor const & desc); @@ -177,8 +216,7 @@ namespace psemek::wgpu limits get_limits(); bool has_feature(feature feature); void push_error_scope(error_filter filter); - void pop_error_scope(error_callback const & callback); - void set_uncaptured_error_callback(error_callback const & callback); + void pop_error_scope(callback_mode mode, pop_error_callback const & callback); void set_label(std::string const & label); static void reference(void * ptr); diff --git a/libs/wgpu/include/psemek/wgpu/external/webgpu.h b/libs/wgpu/include/psemek/wgpu/external/webgpu.h index d2533513..084ac1bb 100644 --- a/libs/wgpu/include/psemek/wgpu/external/webgpu.h +++ b/libs/wgpu/include/psemek/wgpu/external/webgpu.h @@ -1,32 +1,20 @@ -// BSD 3-Clause License -// -// Copyright (c) 2019, "WebGPU native" developers -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +/** + * Copyright 2019-2023 WebGPU-Native developers + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** @file */ + +/** + * \mainpage + * + * **Important:** *This documentation is a Work In Progress.* + * + * This is the home of WebGPU C API specification. We define here the standard + * `webgpu.h` header that all implementations should provide. + */ + #ifndef WEBGPU_H_ #define WEBGPU_H_ @@ -67,18 +55,93 @@ #include #include -#define WGPU_ARRAY_LAYER_COUNT_UNDEFINED (0xffffffffUL) -#define WGPU_COPY_STRIDE_UNDEFINED (0xffffffffUL) -#define WGPU_LIMIT_U32_UNDEFINED (0xffffffffUL) -#define WGPU_LIMIT_U64_UNDEFINED (0xffffffffffffffffULL) -#define WGPU_MIP_LEVEL_COUNT_UNDEFINED (0xffffffffUL) -#define WGPU_QUERY_SET_INDEX_UNDEFINED (0xffffffffUL) -#define WGPU_WHOLE_MAP_SIZE SIZE_MAX -#define WGPU_WHOLE_SIZE (0xffffffffffffffffULL) +#define _wgpu_COMMA , +#if defined(__cplusplus) +# if __cplusplus >= 201103L +# define _wgpu_MAKE_INIT_STRUCT(type, value) (type value) +# else +# define _wgpu_MAKE_INIT_STRUCT(type, value) value +# endif +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +# define _wgpu_MAKE_INIT_STRUCT(type, value) ((type) value) +#else +# define _wgpu_MAKE_INIT_STRUCT(type, value) value +#endif -typedef uint32_t WGPUFlags; + +/** + * \defgroup Constants + * \brief Constants. + * + * @{ + */ +#define WGPU_ARRAY_LAYER_COUNT_UNDEFINED (UINT32_MAX) +#define WGPU_COPY_STRIDE_UNDEFINED (UINT32_MAX) +#define WGPU_DEPTH_SLICE_UNDEFINED (UINT32_MAX) +#define WGPU_LIMIT_U32_UNDEFINED (UINT32_MAX) +#define WGPU_LIMIT_U64_UNDEFINED (UINT64_MAX) +#define WGPU_MIP_LEVEL_COUNT_UNDEFINED (UINT32_MAX) +#define WGPU_QUERY_SET_INDEX_UNDEFINED (UINT32_MAX) +#define WGPU_WHOLE_MAP_SIZE (SIZE_MAX) +#define WGPU_WHOLE_SIZE (UINT64_MAX) + + +/** @} */ + +/** + * \defgroup UtilityTypes Utility Types + * + * @{ + */ +typedef uint64_t WGPUFlags; typedef uint32_t WGPUBool; +/** + * Nullable value defining a pointer+length view into a UTF-8 encoded string. + * + * Values passed into the API may use the special length value @ref WGPU_STRLEN + * to indicate a null-terminated string. + * Non-null values passed out of the API (for example as callback arguments) + * always provide an explicit length and **may or may not be null-terminated**. + * + * Some inputs to the API accept null values. Those which do not accept null + * values "default" to the empty string when null values are passed. + * + * Values are encoded as follows: + * - `{NULL, WGPU_STRLEN}`: the null value. + * - `{non_null_pointer, WGPU_STRLEN}`: a null-terminated string view. + * - `{any, 0}`: the empty string. + * - `{NULL, non_zero_length}`: not allowed (null dereference). + * - `{non_null_pointer, non_zero_length}`: an explictly-sized string view with + * size `non_zero_length` (in bytes). + * + * For info on how this is used in various places, see \ref Strings. + */ +typedef struct WGPUStringView { + char const * WGPU_NULLABLE data; + size_t length; +} WGPUStringView; + +/** + * Sentinel value used in @ref WGPUStringView to indicate that the pointer + * is to a null-terminated string, rather than an explicitly-sized string. + */ +#define WGPU_STRLEN SIZE_MAX + +#define WGPU_STRING_VIEW_INIT _wgpu_MAKE_INIT_STRUCT(WGPUStringView, { \ + /*.data=*/NULL _wgpu_COMMA \ + /*.length=*/WGPU_STRLEN _wgpu_COMMA \ +}) + + +/** @} */ + +/** + * \defgroup Objects + * \brief Opaque, non-dispatchable handles to WebGPU objects. + * + * @{ + */ typedef struct WGPUAdapterImpl* WGPUAdapter WGPU_OBJECT_ATTRIBUTE; typedef struct WGPUBindGroupImpl* WGPUBindGroup WGPU_OBJECT_ATTRIBUTE; typedef struct WGPUBindGroupLayoutImpl* WGPUBindGroupLayout WGPU_OBJECT_ATTRIBUTE; @@ -98,12 +161,17 @@ typedef struct WGPURenderPassEncoderImpl* WGPURenderPassEncoder WGPU_OBJECT_ATTR typedef struct WGPURenderPipelineImpl* WGPURenderPipeline WGPU_OBJECT_ATTRIBUTE; typedef struct WGPUSamplerImpl* WGPUSampler WGPU_OBJECT_ATTRIBUTE; typedef struct WGPUShaderModuleImpl* WGPUShaderModule WGPU_OBJECT_ATTRIBUTE; +/** + * An object used to continuously present image data to the user, see @ref Surfaces for more details. + */ typedef struct WGPUSurfaceImpl* WGPUSurface WGPU_OBJECT_ATTRIBUTE; typedef struct WGPUTextureImpl* WGPUTexture WGPU_OBJECT_ATTRIBUTE; typedef struct WGPUTextureViewImpl* WGPUTextureView WGPU_OBJECT_ATTRIBUTE; + +/** @} */ // Structure forward declarations -struct WGPUAdapterProperties; +struct WGPUAdapterInfo; struct WGPUBindGroupEntry; struct WGPUBlendComponent; struct WGPUBufferBindingLayout; @@ -115,41 +183,42 @@ struct WGPUCompilationMessage; struct WGPUComputePassTimestampWrites; struct WGPUConstantEntry; struct WGPUExtent3D; -struct WGPUInstanceDescriptor; +struct WGPUFuture; +struct WGPUInstanceCapabilities; struct WGPULimits; struct WGPUMultisampleState; struct WGPUOrigin3D; struct WGPUPipelineLayoutDescriptor; -struct WGPUPrimitiveDepthClipControl; struct WGPUPrimitiveState; struct WGPUQuerySetDescriptor; struct WGPUQueueDescriptor; struct WGPURenderBundleDescriptor; struct WGPURenderBundleEncoderDescriptor; struct WGPURenderPassDepthStencilAttachment; -struct WGPURenderPassDescriptorMaxDrawCount; +struct WGPURenderPassMaxDrawCount; struct WGPURenderPassTimestampWrites; struct WGPURequestAdapterOptions; struct WGPUSamplerBindingLayout; struct WGPUSamplerDescriptor; -struct WGPUShaderModuleCompilationHint; -struct WGPUShaderModuleSPIRVDescriptor; -struct WGPUShaderModuleWGSLDescriptor; +struct WGPUShaderModuleDescriptor; +struct WGPUShaderSourceSPIRV; +struct WGPUShaderSourceWGSL; struct WGPUStencilFaceState; struct WGPUStorageTextureBindingLayout; +struct WGPUSupportedFeatures; +struct WGPUSupportedWGSLLanguageFeatures; struct WGPUSurfaceCapabilities; struct WGPUSurfaceConfiguration; struct WGPUSurfaceDescriptor; -struct WGPUSurfaceDescriptorFromAndroidNativeWindow; -struct WGPUSurfaceDescriptorFromCanvasHTMLSelector; -struct WGPUSurfaceDescriptorFromMetalLayer; -struct WGPUSurfaceDescriptorFromWaylandSurface; -struct WGPUSurfaceDescriptorFromWindowsHWND; -struct WGPUSurfaceDescriptorFromXcbWindow; -struct WGPUSurfaceDescriptorFromXlibWindow; +struct WGPUSurfaceSourceAndroidNativeWindow; +struct WGPUSurfaceSourceMetalLayer; +struct WGPUSurfaceSourceWaylandSurface; +struct WGPUSurfaceSourceWindowsHWND; +struct WGPUSurfaceSourceXCBWindow; +struct WGPUSurfaceSourceXlibWindow; struct WGPUSurfaceTexture; +struct WGPUTexelCopyBufferLayout; struct WGPUTextureBindingLayout; -struct WGPUTextureDataLayout; struct WGPUTextureViewDescriptor; struct WGPUVertexAttribute; struct WGPUBindGroupDescriptor; @@ -158,40 +227,67 @@ struct WGPUBlendState; struct WGPUCompilationInfo; struct WGPUComputePassDescriptor; struct WGPUDepthStencilState; -struct WGPUImageCopyBuffer; -struct WGPUImageCopyTexture; +struct WGPUDeviceDescriptor; +struct WGPUFutureWaitInfo; +struct WGPUInstanceDescriptor; struct WGPUProgrammableStageDescriptor; struct WGPURenderPassColorAttachment; -struct WGPURequiredLimits; -struct WGPUShaderModuleDescriptor; -struct WGPUSupportedLimits; +struct WGPUTexelCopyBufferInfo; +struct WGPUTexelCopyTextureInfo; struct WGPUTextureDescriptor; struct WGPUVertexBufferLayout; struct WGPUBindGroupLayoutDescriptor; struct WGPUColorTargetState; struct WGPUComputePipelineDescriptor; -struct WGPUDeviceDescriptor; struct WGPURenderPassDescriptor; struct WGPUVertexState; struct WGPUFragmentState; struct WGPURenderPipelineDescriptor; +// Callback info structure forward declarations +struct WGPUBufferMapCallbackInfo; +struct WGPUCompilationInfoCallbackInfo; +struct WGPUCreateComputePipelineAsyncCallbackInfo; +struct WGPUCreateRenderPipelineAsyncCallbackInfo; +struct WGPUDeviceLostCallbackInfo; +struct WGPUPopErrorScopeCallbackInfo; +struct WGPUQueueWorkDoneCallbackInfo; +struct WGPURequestAdapterCallbackInfo; +struct WGPURequestDeviceCallbackInfo; +struct WGPUUncapturedErrorCallbackInfo; + + +/** + * \defgroup Enumerations + * \brief Enums. + * + * @{ + */ typedef enum WGPUAdapterType { - WGPUAdapterType_DiscreteGPU = 0x00000000, - WGPUAdapterType_IntegratedGPU = 0x00000001, - WGPUAdapterType_CPU = 0x00000002, - WGPUAdapterType_Unknown = 0x00000003, + WGPUAdapterType_DiscreteGPU = 0x00000001, + WGPUAdapterType_IntegratedGPU = 0x00000002, + WGPUAdapterType_CPU = 0x00000003, + WGPUAdapterType_Unknown = 0x00000004, WGPUAdapterType_Force32 = 0x7FFFFFFF } WGPUAdapterType WGPU_ENUM_ATTRIBUTE; typedef enum WGPUAddressMode { - WGPUAddressMode_Repeat = 0x00000000, - WGPUAddressMode_MirrorRepeat = 0x00000001, - WGPUAddressMode_ClampToEdge = 0x00000002, + /** + * `0x00000000`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ + WGPUAddressMode_Undefined = 0x00000000, + WGPUAddressMode_ClampToEdge = 0x00000001, + WGPUAddressMode_Repeat = 0x00000002, + WGPUAddressMode_MirrorRepeat = 0x00000003, WGPUAddressMode_Force32 = 0x7FFFFFFF } WGPUAddressMode WGPU_ENUM_ATTRIBUTE; typedef enum WGPUBackendType { + /** + * `0x00000000`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ WGPUBackendType_Undefined = 0x00000000, WGPUBackendType_Null = 0x00000001, WGPUBackendType_WebGPU = 0x00000002, @@ -205,165 +301,277 @@ typedef enum WGPUBackendType { } WGPUBackendType WGPU_ENUM_ATTRIBUTE; typedef enum WGPUBlendFactor { - WGPUBlendFactor_Zero = 0x00000000, - WGPUBlendFactor_One = 0x00000001, - WGPUBlendFactor_Src = 0x00000002, - WGPUBlendFactor_OneMinusSrc = 0x00000003, - WGPUBlendFactor_SrcAlpha = 0x00000004, - WGPUBlendFactor_OneMinusSrcAlpha = 0x00000005, - WGPUBlendFactor_Dst = 0x00000006, - WGPUBlendFactor_OneMinusDst = 0x00000007, - WGPUBlendFactor_DstAlpha = 0x00000008, - WGPUBlendFactor_OneMinusDstAlpha = 0x00000009, - WGPUBlendFactor_SrcAlphaSaturated = 0x0000000A, - WGPUBlendFactor_Constant = 0x0000000B, - WGPUBlendFactor_OneMinusConstant = 0x0000000C, + /** + * `0x00000000`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ + WGPUBlendFactor_Undefined = 0x00000000, + WGPUBlendFactor_Zero = 0x00000001, + WGPUBlendFactor_One = 0x00000002, + WGPUBlendFactor_Src = 0x00000003, + WGPUBlendFactor_OneMinusSrc = 0x00000004, + WGPUBlendFactor_SrcAlpha = 0x00000005, + WGPUBlendFactor_OneMinusSrcAlpha = 0x00000006, + WGPUBlendFactor_Dst = 0x00000007, + WGPUBlendFactor_OneMinusDst = 0x00000008, + WGPUBlendFactor_DstAlpha = 0x00000009, + WGPUBlendFactor_OneMinusDstAlpha = 0x0000000A, + WGPUBlendFactor_SrcAlphaSaturated = 0x0000000B, + WGPUBlendFactor_Constant = 0x0000000C, + WGPUBlendFactor_OneMinusConstant = 0x0000000D, + WGPUBlendFactor_Src1 = 0x0000000E, + WGPUBlendFactor_OneMinusSrc1 = 0x0000000F, + WGPUBlendFactor_Src1Alpha = 0x00000010, + WGPUBlendFactor_OneMinusSrc1Alpha = 0x00000011, WGPUBlendFactor_Force32 = 0x7FFFFFFF } WGPUBlendFactor WGPU_ENUM_ATTRIBUTE; typedef enum WGPUBlendOperation { - WGPUBlendOperation_Add = 0x00000000, - WGPUBlendOperation_Subtract = 0x00000001, - WGPUBlendOperation_ReverseSubtract = 0x00000002, - WGPUBlendOperation_Min = 0x00000003, - WGPUBlendOperation_Max = 0x00000004, + /** + * `0x00000000`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ + WGPUBlendOperation_Undefined = 0x00000000, + WGPUBlendOperation_Add = 0x00000001, + WGPUBlendOperation_Subtract = 0x00000002, + WGPUBlendOperation_ReverseSubtract = 0x00000003, + WGPUBlendOperation_Min = 0x00000004, + WGPUBlendOperation_Max = 0x00000005, WGPUBlendOperation_Force32 = 0x7FFFFFFF } WGPUBlendOperation WGPU_ENUM_ATTRIBUTE; typedef enum WGPUBufferBindingType { - WGPUBufferBindingType_Undefined = 0x00000000, - WGPUBufferBindingType_Uniform = 0x00000001, - WGPUBufferBindingType_Storage = 0x00000002, - WGPUBufferBindingType_ReadOnlyStorage = 0x00000003, + /** + * `0x00000000`. + * Indicates that this @ref WGPUBufferBindingLayout member of + * its parent @ref WGPUBindGroupLayoutEntry is not used. + * (See also @ref SentinelValues.) + */ + WGPUBufferBindingType_BindingNotUsed = 0x00000000, + /** + * `0x00000001`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ + WGPUBufferBindingType_Undefined = 0x00000001, + WGPUBufferBindingType_Uniform = 0x00000002, + WGPUBufferBindingType_Storage = 0x00000003, + WGPUBufferBindingType_ReadOnlyStorage = 0x00000004, WGPUBufferBindingType_Force32 = 0x7FFFFFFF } WGPUBufferBindingType WGPU_ENUM_ATTRIBUTE; -typedef enum WGPUBufferMapAsyncStatus { - WGPUBufferMapAsyncStatus_Success = 0x00000000, - WGPUBufferMapAsyncStatus_ValidationError = 0x00000001, - WGPUBufferMapAsyncStatus_Unknown = 0x00000002, - WGPUBufferMapAsyncStatus_DeviceLost = 0x00000003, - WGPUBufferMapAsyncStatus_DestroyedBeforeCallback = 0x00000004, - WGPUBufferMapAsyncStatus_UnmappedBeforeCallback = 0x00000005, - WGPUBufferMapAsyncStatus_MappingAlreadyPending = 0x00000006, - WGPUBufferMapAsyncStatus_OffsetOutOfRange = 0x00000007, - WGPUBufferMapAsyncStatus_SizeOutOfRange = 0x00000008, - WGPUBufferMapAsyncStatus_Force32 = 0x7FFFFFFF -} WGPUBufferMapAsyncStatus WGPU_ENUM_ATTRIBUTE; - typedef enum WGPUBufferMapState { - WGPUBufferMapState_Unmapped = 0x00000000, - WGPUBufferMapState_Pending = 0x00000001, - WGPUBufferMapState_Mapped = 0x00000002, + WGPUBufferMapState_Unmapped = 0x00000001, + WGPUBufferMapState_Pending = 0x00000002, + WGPUBufferMapState_Mapped = 0x00000003, WGPUBufferMapState_Force32 = 0x7FFFFFFF } WGPUBufferMapState WGPU_ENUM_ATTRIBUTE; +/** + * The callback mode controls how a callback for an asynchronous operation may be fired. See @ref Asynchronous-Operations for how these are used. + */ +typedef enum WGPUCallbackMode { + /** + * `0x00000001`. + * Callbacks created with `WGPUCallbackMode_WaitAnyOnly`: + * - fire when the asynchronous operation's future is passed to a call to `::wgpuInstanceWaitAny` + * AND the operation has already completed or it completes inside the call to `::wgpuInstanceWaitAny`. + */ + WGPUCallbackMode_WaitAnyOnly = 0x00000001, + /** + * `0x00000002`. + * Callbacks created with `WGPUCallbackMode_AllowProcessEvents`: + * - fire for the same reasons as callbacks created with `WGPUCallbackMode_WaitAnyOnly` + * - fire inside a call to `::wgpuInstanceProcessEvents` if the asynchronous operation is complete. + */ + WGPUCallbackMode_AllowProcessEvents = 0x00000002, + /** + * `0x00000003`. + * Callbacks created with `WGPUCallbackMode_AllowSpontaneous`: + * - fire for the same reasons as callbacks created with `WGPUCallbackMode_AllowProcessEvents` + * - **may** fire spontaneously on an arbitrary or application thread, when the WebGPU implementations discovers that the asynchronous operation is complete. + * + * Implementations _should_ fire spontaneous callbacks as soon as possible. + * + * @note Because spontaneous callbacks may fire at an arbitrary time on an arbitrary thread, applications should take extra care when acquiring locks or mutating state inside the callback. It undefined behavior to re-entrantly call into the webgpu.h API if the callback fires while inside the callstack of another webgpu.h function that is not `wgpuInstanceWaitAny` or `wgpuInstanceProcessEvents`. + */ + WGPUCallbackMode_AllowSpontaneous = 0x00000003, + WGPUCallbackMode_Force32 = 0x7FFFFFFF +} WGPUCallbackMode WGPU_ENUM_ATTRIBUTE; + typedef enum WGPUCompareFunction { + /** + * `0x00000000`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ WGPUCompareFunction_Undefined = 0x00000000, WGPUCompareFunction_Never = 0x00000001, WGPUCompareFunction_Less = 0x00000002, - WGPUCompareFunction_LessEqual = 0x00000003, - WGPUCompareFunction_Greater = 0x00000004, - WGPUCompareFunction_GreaterEqual = 0x00000005, - WGPUCompareFunction_Equal = 0x00000006, - WGPUCompareFunction_NotEqual = 0x00000007, + WGPUCompareFunction_Equal = 0x00000003, + WGPUCompareFunction_LessEqual = 0x00000004, + WGPUCompareFunction_Greater = 0x00000005, + WGPUCompareFunction_NotEqual = 0x00000006, + WGPUCompareFunction_GreaterEqual = 0x00000007, WGPUCompareFunction_Always = 0x00000008, WGPUCompareFunction_Force32 = 0x7FFFFFFF } WGPUCompareFunction WGPU_ENUM_ATTRIBUTE; typedef enum WGPUCompilationInfoRequestStatus { - WGPUCompilationInfoRequestStatus_Success = 0x00000000, - WGPUCompilationInfoRequestStatus_Error = 0x00000001, - WGPUCompilationInfoRequestStatus_DeviceLost = 0x00000002, - WGPUCompilationInfoRequestStatus_Unknown = 0x00000003, + WGPUCompilationInfoRequestStatus_Success = 0x00000001, + WGPUCompilationInfoRequestStatus_InstanceDropped = 0x00000002, + WGPUCompilationInfoRequestStatus_Error = 0x00000003, + WGPUCompilationInfoRequestStatus_Unknown = 0x00000004, WGPUCompilationInfoRequestStatus_Force32 = 0x7FFFFFFF } WGPUCompilationInfoRequestStatus WGPU_ENUM_ATTRIBUTE; typedef enum WGPUCompilationMessageType { - WGPUCompilationMessageType_Error = 0x00000000, - WGPUCompilationMessageType_Warning = 0x00000001, - WGPUCompilationMessageType_Info = 0x00000002, + WGPUCompilationMessageType_Error = 0x00000001, + WGPUCompilationMessageType_Warning = 0x00000002, + WGPUCompilationMessageType_Info = 0x00000003, WGPUCompilationMessageType_Force32 = 0x7FFFFFFF } WGPUCompilationMessageType WGPU_ENUM_ATTRIBUTE; +/** + * Describes how frames are composited with other contents on the screen when `::wgpuSurfacePresent` is called. + */ typedef enum WGPUCompositeAlphaMode { + /** + * `0x00000000`. + * Lets the WebGPU implementation choose the best mode (supported, and with the best performance) between @ref WGPUCompositeAlphaMode_Opaque or @ref WGPUCompositeAlphaMode_Inherit. + */ WGPUCompositeAlphaMode_Auto = 0x00000000, + /** + * `0x00000001`. + * The alpha component of the image is ignored and teated as if it is always 1.0. + */ WGPUCompositeAlphaMode_Opaque = 0x00000001, + /** + * `0x00000002`. + * The alpha component is respected and non-alpha components are assumed to be already multiplied with the alpha component. For example, (0.5, 0, 0, 0.5) is semi-transparent bright red. + */ WGPUCompositeAlphaMode_Premultiplied = 0x00000002, + /** + * `0x00000003`. + * The alpha component is respected and non-alpha components are assumed to NOT be already multiplied with the alpha component. For example, (1.0, 0, 0, 0.5) is semi-transparent bright red. + */ WGPUCompositeAlphaMode_Unpremultiplied = 0x00000003, + /** + * `0x00000004`. + * The handling of the alpha component is unknown to WebGPU and should be handled by the application using system-specific APIs. This mode may be unavailable (for example on Wasm). + */ WGPUCompositeAlphaMode_Inherit = 0x00000004, WGPUCompositeAlphaMode_Force32 = 0x7FFFFFFF } WGPUCompositeAlphaMode WGPU_ENUM_ATTRIBUTE; typedef enum WGPUCreatePipelineAsyncStatus { - WGPUCreatePipelineAsyncStatus_Success = 0x00000000, - WGPUCreatePipelineAsyncStatus_ValidationError = 0x00000001, - WGPUCreatePipelineAsyncStatus_InternalError = 0x00000002, - WGPUCreatePipelineAsyncStatus_DeviceLost = 0x00000003, - WGPUCreatePipelineAsyncStatus_DeviceDestroyed = 0x00000004, + WGPUCreatePipelineAsyncStatus_Success = 0x00000001, + WGPUCreatePipelineAsyncStatus_InstanceDropped = 0x00000002, + WGPUCreatePipelineAsyncStatus_ValidationError = 0x00000003, + WGPUCreatePipelineAsyncStatus_InternalError = 0x00000004, WGPUCreatePipelineAsyncStatus_Unknown = 0x00000005, WGPUCreatePipelineAsyncStatus_Force32 = 0x7FFFFFFF } WGPUCreatePipelineAsyncStatus WGPU_ENUM_ATTRIBUTE; typedef enum WGPUCullMode { - WGPUCullMode_None = 0x00000000, - WGPUCullMode_Front = 0x00000001, - WGPUCullMode_Back = 0x00000002, + /** + * `0x00000000`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ + WGPUCullMode_Undefined = 0x00000000, + WGPUCullMode_None = 0x00000001, + WGPUCullMode_Front = 0x00000002, + WGPUCullMode_Back = 0x00000003, WGPUCullMode_Force32 = 0x7FFFFFFF } WGPUCullMode WGPU_ENUM_ATTRIBUTE; typedef enum WGPUDeviceLostReason { - WGPUDeviceLostReason_Undefined = 0x00000000, - WGPUDeviceLostReason_Destroyed = 0x00000001, + WGPUDeviceLostReason_Unknown = 0x00000001, + WGPUDeviceLostReason_Destroyed = 0x00000002, + WGPUDeviceLostReason_InstanceDropped = 0x00000003, + WGPUDeviceLostReason_FailedCreation = 0x00000004, WGPUDeviceLostReason_Force32 = 0x7FFFFFFF } WGPUDeviceLostReason WGPU_ENUM_ATTRIBUTE; typedef enum WGPUErrorFilter { - WGPUErrorFilter_Validation = 0x00000000, - WGPUErrorFilter_OutOfMemory = 0x00000001, - WGPUErrorFilter_Internal = 0x00000002, + WGPUErrorFilter_Validation = 0x00000001, + WGPUErrorFilter_OutOfMemory = 0x00000002, + WGPUErrorFilter_Internal = 0x00000003, WGPUErrorFilter_Force32 = 0x7FFFFFFF } WGPUErrorFilter WGPU_ENUM_ATTRIBUTE; typedef enum WGPUErrorType { - WGPUErrorType_NoError = 0x00000000, - WGPUErrorType_Validation = 0x00000001, - WGPUErrorType_OutOfMemory = 0x00000002, - WGPUErrorType_Internal = 0x00000003, - WGPUErrorType_Unknown = 0x00000004, - WGPUErrorType_DeviceLost = 0x00000005, + WGPUErrorType_NoError = 0x00000001, + WGPUErrorType_Validation = 0x00000002, + WGPUErrorType_OutOfMemory = 0x00000003, + WGPUErrorType_Internal = 0x00000004, + WGPUErrorType_Unknown = 0x00000005, WGPUErrorType_Force32 = 0x7FFFFFFF } WGPUErrorType WGPU_ENUM_ATTRIBUTE; +/** + * See @ref WGPURequestAdapterOptions::featureLevel. + */ +typedef enum WGPUFeatureLevel { + /** + * `0x00000001`. + * "Compatibility" profile which can be supported on OpenGL ES 3.1. + */ + WGPUFeatureLevel_Compatibility = 0x00000001, + /** + * `0x00000002`. + * "Core" profile which can be supported on Vulkan/Metal/D3D12. + */ + WGPUFeatureLevel_Core = 0x00000002, + WGPUFeatureLevel_Force32 = 0x7FFFFFFF +} WGPUFeatureLevel WGPU_ENUM_ATTRIBUTE; + typedef enum WGPUFeatureName { WGPUFeatureName_Undefined = 0x00000000, WGPUFeatureName_DepthClipControl = 0x00000001, WGPUFeatureName_Depth32FloatStencil8 = 0x00000002, WGPUFeatureName_TimestampQuery = 0x00000003, WGPUFeatureName_TextureCompressionBC = 0x00000004, - WGPUFeatureName_TextureCompressionETC2 = 0x00000005, - WGPUFeatureName_TextureCompressionASTC = 0x00000006, - WGPUFeatureName_IndirectFirstInstance = 0x00000007, - WGPUFeatureName_ShaderF16 = 0x00000008, - WGPUFeatureName_RG11B10UfloatRenderable = 0x00000009, - WGPUFeatureName_BGRA8UnormStorage = 0x0000000A, - WGPUFeatureName_Float32Filterable = 0x0000000B, + WGPUFeatureName_TextureCompressionBCSliced3D = 0x00000005, + WGPUFeatureName_TextureCompressionETC2 = 0x00000006, + WGPUFeatureName_TextureCompressionASTC = 0x00000007, + WGPUFeatureName_TextureCompressionASTCSliced3D = 0x00000008, + WGPUFeatureName_IndirectFirstInstance = 0x00000009, + WGPUFeatureName_ShaderF16 = 0x0000000A, + WGPUFeatureName_RG11B10UfloatRenderable = 0x0000000B, + WGPUFeatureName_BGRA8UnormStorage = 0x0000000C, + WGPUFeatureName_Float32Filterable = 0x0000000D, + WGPUFeatureName_Float32Blendable = 0x0000000E, + WGPUFeatureName_ClipDistances = 0x0000000F, + WGPUFeatureName_DualSourceBlending = 0x00000010, WGPUFeatureName_Force32 = 0x7FFFFFFF } WGPUFeatureName WGPU_ENUM_ATTRIBUTE; typedef enum WGPUFilterMode { - WGPUFilterMode_Nearest = 0x00000000, - WGPUFilterMode_Linear = 0x00000001, + /** + * `0x00000000`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ + WGPUFilterMode_Undefined = 0x00000000, + WGPUFilterMode_Nearest = 0x00000001, + WGPUFilterMode_Linear = 0x00000002, WGPUFilterMode_Force32 = 0x7FFFFFFF } WGPUFilterMode WGPU_ENUM_ATTRIBUTE; typedef enum WGPUFrontFace { - WGPUFrontFace_CCW = 0x00000000, - WGPUFrontFace_CW = 0x00000001, + /** + * `0x00000000`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ + WGPUFrontFace_Undefined = 0x00000000, + WGPUFrontFace_CCW = 0x00000001, + WGPUFrontFace_CW = 0x00000002, WGPUFrontFace_Force32 = 0x7FFFFFFF } WGPUFrontFace WGPU_ENUM_ATTRIBUTE; typedef enum WGPUIndexFormat { + /** + * `0x00000000`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ WGPUIndexFormat_Undefined = 0x00000000, WGPUIndexFormat_Uint16 = 0x00000001, WGPUIndexFormat_Uint32 = 0x00000002, @@ -371,147 +579,318 @@ typedef enum WGPUIndexFormat { } WGPUIndexFormat WGPU_ENUM_ATTRIBUTE; typedef enum WGPULoadOp { + /** + * `0x00000000`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ WGPULoadOp_Undefined = 0x00000000, - WGPULoadOp_Clear = 0x00000001, - WGPULoadOp_Load = 0x00000002, + WGPULoadOp_Load = 0x00000001, + WGPULoadOp_Clear = 0x00000002, WGPULoadOp_Force32 = 0x7FFFFFFF } WGPULoadOp WGPU_ENUM_ATTRIBUTE; +typedef enum WGPUMapAsyncStatus { + WGPUMapAsyncStatus_Success = 0x00000001, + WGPUMapAsyncStatus_InstanceDropped = 0x00000002, + WGPUMapAsyncStatus_Error = 0x00000003, + WGPUMapAsyncStatus_Aborted = 0x00000004, + WGPUMapAsyncStatus_Unknown = 0x00000005, + WGPUMapAsyncStatus_Force32 = 0x7FFFFFFF +} WGPUMapAsyncStatus WGPU_ENUM_ATTRIBUTE; + typedef enum WGPUMipmapFilterMode { - WGPUMipmapFilterMode_Nearest = 0x00000000, - WGPUMipmapFilterMode_Linear = 0x00000001, + /** + * `0x00000000`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ + WGPUMipmapFilterMode_Undefined = 0x00000000, + WGPUMipmapFilterMode_Nearest = 0x00000001, + WGPUMipmapFilterMode_Linear = 0x00000002, WGPUMipmapFilterMode_Force32 = 0x7FFFFFFF } WGPUMipmapFilterMode WGPU_ENUM_ATTRIBUTE; +typedef enum WGPUOptionalBool { + WGPUOptionalBool_False = 0x00000000, + WGPUOptionalBool_True = 0x00000001, + WGPUOptionalBool_Undefined = 0x00000002, + WGPUOptionalBool_Force32 = 0x7FFFFFFF +} WGPUOptionalBool WGPU_ENUM_ATTRIBUTE; + +typedef enum WGPUPopErrorScopeStatus { + /** + * `0x00000001`. + * The error scope stack was successfully popped and a result was reported. + */ + WGPUPopErrorScopeStatus_Success = 0x00000001, + WGPUPopErrorScopeStatus_InstanceDropped = 0x00000002, + /** + * `0x00000003`. + * The error scope stack could not be popped, because it was empty. + */ + WGPUPopErrorScopeStatus_EmptyStack = 0x00000003, + WGPUPopErrorScopeStatus_Force32 = 0x7FFFFFFF +} WGPUPopErrorScopeStatus WGPU_ENUM_ATTRIBUTE; + typedef enum WGPUPowerPreference { + /** + * `0x00000000`. + * No preference. (See also @ref SentinelValues.) + */ WGPUPowerPreference_Undefined = 0x00000000, WGPUPowerPreference_LowPower = 0x00000001, WGPUPowerPreference_HighPerformance = 0x00000002, WGPUPowerPreference_Force32 = 0x7FFFFFFF } WGPUPowerPreference WGPU_ENUM_ATTRIBUTE; +/** + * Describes when and in which order frames are presented on the screen when `::wgpuSurfacePresent` is called. + */ typedef enum WGPUPresentMode { - WGPUPresentMode_Fifo = 0x00000000, - WGPUPresentMode_FifoRelaxed = 0x00000001, - WGPUPresentMode_Immediate = 0x00000002, - WGPUPresentMode_Mailbox = 0x00000003, + /** + * `0x00000000`. + * Present mode is not specified. Use the default. + */ + WGPUPresentMode_Undefined = 0x00000000, + /** + * `0x00000001`. + * The presentation of the image to the user waits for the next vertical blanking period to update in a first-in, first-out manner. + * Tearing cannot be observed and frame-loop will be limited to the display's refresh rate. + * This is the only mode that's always available. + */ + WGPUPresentMode_Fifo = 0x00000001, + /** + * `0x00000002`. + * The presentation of the image to the user tries to wait for the next vertical blanking period but may decide to not wait if a frame is presented late. + * Tearing can sometimes be observed but late-frame don't produce a full-frame stutter in the presentation. + * This is still a first-in, first-out mechanism so a frame-loop will be limited to the display's refresh rate. + */ + WGPUPresentMode_FifoRelaxed = 0x00000002, + /** + * `0x00000003`. + * The presentation of the image to the user is updated immediately without waiting for a vertical blank. + * Tearing can be observed but latency is minimized. + */ + WGPUPresentMode_Immediate = 0x00000003, + /** + * `0x00000004`. + * The presentation of the image to the user waits for the next vertical blanking period to update to the latest provided image. + * Tearing cannot be observed and a frame-loop is not limited to the display's refresh rate. + */ + WGPUPresentMode_Mailbox = 0x00000004, WGPUPresentMode_Force32 = 0x7FFFFFFF } WGPUPresentMode WGPU_ENUM_ATTRIBUTE; typedef enum WGPUPrimitiveTopology { - WGPUPrimitiveTopology_PointList = 0x00000000, - WGPUPrimitiveTopology_LineList = 0x00000001, - WGPUPrimitiveTopology_LineStrip = 0x00000002, - WGPUPrimitiveTopology_TriangleList = 0x00000003, - WGPUPrimitiveTopology_TriangleStrip = 0x00000004, + /** + * `0x00000000`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ + WGPUPrimitiveTopology_Undefined = 0x00000000, + WGPUPrimitiveTopology_PointList = 0x00000001, + WGPUPrimitiveTopology_LineList = 0x00000002, + WGPUPrimitiveTopology_LineStrip = 0x00000003, + WGPUPrimitiveTopology_TriangleList = 0x00000004, + WGPUPrimitiveTopology_TriangleStrip = 0x00000005, WGPUPrimitiveTopology_Force32 = 0x7FFFFFFF } WGPUPrimitiveTopology WGPU_ENUM_ATTRIBUTE; typedef enum WGPUQueryType { - WGPUQueryType_Occlusion = 0x00000000, - WGPUQueryType_Timestamp = 0x00000001, + WGPUQueryType_Occlusion = 0x00000001, + WGPUQueryType_Timestamp = 0x00000002, WGPUQueryType_Force32 = 0x7FFFFFFF } WGPUQueryType WGPU_ENUM_ATTRIBUTE; typedef enum WGPUQueueWorkDoneStatus { - WGPUQueueWorkDoneStatus_Success = 0x00000000, - WGPUQueueWorkDoneStatus_Error = 0x00000001, - WGPUQueueWorkDoneStatus_Unknown = 0x00000002, - WGPUQueueWorkDoneStatus_DeviceLost = 0x00000003, + WGPUQueueWorkDoneStatus_Success = 0x00000001, + WGPUQueueWorkDoneStatus_InstanceDropped = 0x00000002, + WGPUQueueWorkDoneStatus_Error = 0x00000003, + WGPUQueueWorkDoneStatus_Unknown = 0x00000004, WGPUQueueWorkDoneStatus_Force32 = 0x7FFFFFFF } WGPUQueueWorkDoneStatus WGPU_ENUM_ATTRIBUTE; typedef enum WGPURequestAdapterStatus { - WGPURequestAdapterStatus_Success = 0x00000000, - WGPURequestAdapterStatus_Unavailable = 0x00000001, - WGPURequestAdapterStatus_Error = 0x00000002, - WGPURequestAdapterStatus_Unknown = 0x00000003, + WGPURequestAdapterStatus_Success = 0x00000001, + WGPURequestAdapterStatus_InstanceDropped = 0x00000002, + WGPURequestAdapterStatus_Unavailable = 0x00000003, + WGPURequestAdapterStatus_Error = 0x00000004, + WGPURequestAdapterStatus_Unknown = 0x00000005, WGPURequestAdapterStatus_Force32 = 0x7FFFFFFF } WGPURequestAdapterStatus WGPU_ENUM_ATTRIBUTE; typedef enum WGPURequestDeviceStatus { - WGPURequestDeviceStatus_Success = 0x00000000, - WGPURequestDeviceStatus_Error = 0x00000001, - WGPURequestDeviceStatus_Unknown = 0x00000002, + WGPURequestDeviceStatus_Success = 0x00000001, + WGPURequestDeviceStatus_InstanceDropped = 0x00000002, + WGPURequestDeviceStatus_Error = 0x00000003, + WGPURequestDeviceStatus_Unknown = 0x00000004, WGPURequestDeviceStatus_Force32 = 0x7FFFFFFF } WGPURequestDeviceStatus WGPU_ENUM_ATTRIBUTE; typedef enum WGPUSType { - WGPUSType_Invalid = 0x00000000, - WGPUSType_SurfaceDescriptorFromMetalLayer = 0x00000001, - WGPUSType_SurfaceDescriptorFromWindowsHWND = 0x00000002, - WGPUSType_SurfaceDescriptorFromXlibWindow = 0x00000003, - WGPUSType_SurfaceDescriptorFromCanvasHTMLSelector = 0x00000004, - WGPUSType_ShaderModuleSPIRVDescriptor = 0x00000005, - WGPUSType_ShaderModuleWGSLDescriptor = 0x00000006, - WGPUSType_PrimitiveDepthClipControl = 0x00000007, - WGPUSType_SurfaceDescriptorFromWaylandSurface = 0x00000008, - WGPUSType_SurfaceDescriptorFromAndroidNativeWindow = 0x00000009, - WGPUSType_SurfaceDescriptorFromXcbWindow = 0x0000000A, - WGPUSType_RenderPassDescriptorMaxDrawCount = 0x0000000F, + WGPUSType_ShaderSourceSPIRV = 0x00000001, + WGPUSType_ShaderSourceWGSL = 0x00000002, + WGPUSType_RenderPassMaxDrawCount = 0x00000003, + WGPUSType_SurfaceSourceMetalLayer = 0x00000004, + WGPUSType_SurfaceSourceWindowsHWND = 0x00000005, + WGPUSType_SurfaceSourceXlibWindow = 0x00000006, + WGPUSType_SurfaceSourceWaylandSurface = 0x00000007, + WGPUSType_SurfaceSourceAndroidNativeWindow = 0x00000008, + WGPUSType_SurfaceSourceXCBWindow = 0x00000009, WGPUSType_Force32 = 0x7FFFFFFF } WGPUSType WGPU_ENUM_ATTRIBUTE; typedef enum WGPUSamplerBindingType { - WGPUSamplerBindingType_Undefined = 0x00000000, - WGPUSamplerBindingType_Filtering = 0x00000001, - WGPUSamplerBindingType_NonFiltering = 0x00000002, - WGPUSamplerBindingType_Comparison = 0x00000003, + /** + * `0x00000000`. + * Indicates that this @ref WGPUSamplerBindingLayout member of + * its parent @ref WGPUBindGroupLayoutEntry is not used. + * (See also @ref SentinelValues.) + */ + WGPUSamplerBindingType_BindingNotUsed = 0x00000000, + /** + * `0x00000001`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ + WGPUSamplerBindingType_Undefined = 0x00000001, + WGPUSamplerBindingType_Filtering = 0x00000002, + WGPUSamplerBindingType_NonFiltering = 0x00000003, + WGPUSamplerBindingType_Comparison = 0x00000004, WGPUSamplerBindingType_Force32 = 0x7FFFFFFF } WGPUSamplerBindingType WGPU_ENUM_ATTRIBUTE; +/** + * Status code returned (synchronously) from many operations. Generally + * indicates an invalid input like an unknown enum value or @ref OutStructChainError. + * Read the function's documentation for specific error conditions. + */ +typedef enum WGPUStatus { + WGPUStatus_Success = 0x00000001, + WGPUStatus_Error = 0x00000002, + WGPUStatus_Force32 = 0x7FFFFFFF +} WGPUStatus WGPU_ENUM_ATTRIBUTE; + typedef enum WGPUStencilOperation { - WGPUStencilOperation_Keep = 0x00000000, - WGPUStencilOperation_Zero = 0x00000001, - WGPUStencilOperation_Replace = 0x00000002, - WGPUStencilOperation_Invert = 0x00000003, - WGPUStencilOperation_IncrementClamp = 0x00000004, - WGPUStencilOperation_DecrementClamp = 0x00000005, - WGPUStencilOperation_IncrementWrap = 0x00000006, - WGPUStencilOperation_DecrementWrap = 0x00000007, + /** + * `0x00000000`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ + WGPUStencilOperation_Undefined = 0x00000000, + WGPUStencilOperation_Keep = 0x00000001, + WGPUStencilOperation_Zero = 0x00000002, + WGPUStencilOperation_Replace = 0x00000003, + WGPUStencilOperation_Invert = 0x00000004, + WGPUStencilOperation_IncrementClamp = 0x00000005, + WGPUStencilOperation_DecrementClamp = 0x00000006, + WGPUStencilOperation_IncrementWrap = 0x00000007, + WGPUStencilOperation_DecrementWrap = 0x00000008, WGPUStencilOperation_Force32 = 0x7FFFFFFF } WGPUStencilOperation WGPU_ENUM_ATTRIBUTE; typedef enum WGPUStorageTextureAccess { - WGPUStorageTextureAccess_Undefined = 0x00000000, - WGPUStorageTextureAccess_WriteOnly = 0x00000001, - WGPUStorageTextureAccess_ReadOnly = 0x00000002, - WGPUStorageTextureAccess_ReadWrite = 0x00000003, + /** + * `0x00000000`. + * Indicates that this @ref WGPUStorageTextureBindingLayout member of + * its parent @ref WGPUBindGroupLayoutEntry is not used. + * (See also @ref SentinelValues.) + */ + WGPUStorageTextureAccess_BindingNotUsed = 0x00000000, + /** + * `0x00000001`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ + WGPUStorageTextureAccess_Undefined = 0x00000001, + WGPUStorageTextureAccess_WriteOnly = 0x00000002, + WGPUStorageTextureAccess_ReadOnly = 0x00000003, + WGPUStorageTextureAccess_ReadWrite = 0x00000004, WGPUStorageTextureAccess_Force32 = 0x7FFFFFFF } WGPUStorageTextureAccess WGPU_ENUM_ATTRIBUTE; typedef enum WGPUStoreOp { + /** + * `0x00000000`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ WGPUStoreOp_Undefined = 0x00000000, WGPUStoreOp_Store = 0x00000001, WGPUStoreOp_Discard = 0x00000002, WGPUStoreOp_Force32 = 0x7FFFFFFF } WGPUStoreOp WGPU_ENUM_ATTRIBUTE; +/** + * The status enum for `::wgpuSurfaceGetCurrentTexture`. + */ typedef enum WGPUSurfaceGetCurrentTextureStatus { - WGPUSurfaceGetCurrentTextureStatus_Success = 0x00000000, - WGPUSurfaceGetCurrentTextureStatus_Timeout = 0x00000001, - WGPUSurfaceGetCurrentTextureStatus_Outdated = 0x00000002, - WGPUSurfaceGetCurrentTextureStatus_Lost = 0x00000003, - WGPUSurfaceGetCurrentTextureStatus_OutOfMemory = 0x00000004, - WGPUSurfaceGetCurrentTextureStatus_DeviceLost = 0x00000005, + /** + * `0x00000001`. + * Yay! Everything is good and we can render this frame. + */ + WGPUSurfaceGetCurrentTextureStatus_SuccessOptimal = 0x00000001, + /** + * `0x00000002`. + * Still OK - the surface can present the frame, but in a suboptimal way. The surface may need reconfiguration. + */ + WGPUSurfaceGetCurrentTextureStatus_SuccessSuboptimal = 0x00000002, + /** + * `0x00000003`. + * Some operation timed out while trying to acquire the frame. + */ + WGPUSurfaceGetCurrentTextureStatus_Timeout = 0x00000003, + /** + * `0x00000004`. + * The surface is too different to be used, compared to when it was originally created. + */ + WGPUSurfaceGetCurrentTextureStatus_Outdated = 0x00000004, + /** + * `0x00000005`. + * The connection to whatever owns the surface was lost. + */ + WGPUSurfaceGetCurrentTextureStatus_Lost = 0x00000005, + /** + * `0x00000006`. + * The system ran out of memory. + */ + WGPUSurfaceGetCurrentTextureStatus_OutOfMemory = 0x00000006, + /** + * `0x00000007`. + * The @ref WGPUDevice configured on the @ref WGPUSurface was lost. + */ + WGPUSurfaceGetCurrentTextureStatus_DeviceLost = 0x00000007, + /** + * `0x00000008`. + * The surface is not configured, or there was an @ref OutStructChainError. + */ + WGPUSurfaceGetCurrentTextureStatus_Error = 0x00000008, WGPUSurfaceGetCurrentTextureStatus_Force32 = 0x7FFFFFFF } WGPUSurfaceGetCurrentTextureStatus WGPU_ENUM_ATTRIBUTE; typedef enum WGPUTextureAspect { - WGPUTextureAspect_All = 0x00000000, - WGPUTextureAspect_StencilOnly = 0x00000001, - WGPUTextureAspect_DepthOnly = 0x00000002, + /** + * `0x00000000`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ + WGPUTextureAspect_Undefined = 0x00000000, + WGPUTextureAspect_All = 0x00000001, + WGPUTextureAspect_StencilOnly = 0x00000002, + WGPUTextureAspect_DepthOnly = 0x00000003, WGPUTextureAspect_Force32 = 0x7FFFFFFF } WGPUTextureAspect WGPU_ENUM_ATTRIBUTE; typedef enum WGPUTextureDimension { - WGPUTextureDimension_1D = 0x00000000, - WGPUTextureDimension_2D = 0x00000001, - WGPUTextureDimension_3D = 0x00000002, + /** + * `0x00000000`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ + WGPUTextureDimension_Undefined = 0x00000000, + WGPUTextureDimension_1D = 0x00000001, + WGPUTextureDimension_2D = 0x00000002, + WGPUTextureDimension_3D = 0x00000003, WGPUTextureDimension_Force32 = 0x7FFFFFFF } WGPUTextureDimension WGPU_ENUM_ATTRIBUTE; typedef enum WGPUTextureFormat { + /** + * `0x00000000`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ WGPUTextureFormat_Undefined = 0x00000000, WGPUTextureFormat_R8Unorm = 0x00000001, WGPUTextureFormat_R8Snorm = 0x00000002, @@ -612,16 +991,31 @@ typedef enum WGPUTextureFormat { } WGPUTextureFormat WGPU_ENUM_ATTRIBUTE; typedef enum WGPUTextureSampleType { - WGPUTextureSampleType_Undefined = 0x00000000, - WGPUTextureSampleType_Float = 0x00000001, - WGPUTextureSampleType_UnfilterableFloat = 0x00000002, - WGPUTextureSampleType_Depth = 0x00000003, - WGPUTextureSampleType_Sint = 0x00000004, - WGPUTextureSampleType_Uint = 0x00000005, + /** + * `0x00000000`. + * Indicates that this @ref WGPUTextureBindingLayout member of + * its parent @ref WGPUBindGroupLayoutEntry is not used. + * (See also @ref SentinelValues.) + */ + WGPUTextureSampleType_BindingNotUsed = 0x00000000, + /** + * `0x00000001`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ + WGPUTextureSampleType_Undefined = 0x00000001, + WGPUTextureSampleType_Float = 0x00000002, + WGPUTextureSampleType_UnfilterableFloat = 0x00000003, + WGPUTextureSampleType_Depth = 0x00000004, + WGPUTextureSampleType_Sint = 0x00000005, + WGPUTextureSampleType_Uint = 0x00000006, WGPUTextureSampleType_Force32 = 0x7FFFFFFF } WGPUTextureSampleType WGPU_ENUM_ATTRIBUTE; typedef enum WGPUTextureViewDimension { + /** + * `0x00000000`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ WGPUTextureViewDimension_Undefined = 0x00000000, WGPUTextureViewDimension_1D = 0x00000001, WGPUTextureViewDimension_2D = 0x00000002, @@ -633,112 +1027,243 @@ typedef enum WGPUTextureViewDimension { } WGPUTextureViewDimension WGPU_ENUM_ATTRIBUTE; typedef enum WGPUVertexFormat { - WGPUVertexFormat_Undefined = 0x00000000, - WGPUVertexFormat_Uint8x2 = 0x00000001, - WGPUVertexFormat_Uint8x4 = 0x00000002, - WGPUVertexFormat_Sint8x2 = 0x00000003, - WGPUVertexFormat_Sint8x4 = 0x00000004, - WGPUVertexFormat_Unorm8x2 = 0x00000005, - WGPUVertexFormat_Unorm8x4 = 0x00000006, - WGPUVertexFormat_Snorm8x2 = 0x00000007, - WGPUVertexFormat_Snorm8x4 = 0x00000008, - WGPUVertexFormat_Uint16x2 = 0x00000009, - WGPUVertexFormat_Uint16x4 = 0x0000000A, - WGPUVertexFormat_Sint16x2 = 0x0000000B, - WGPUVertexFormat_Sint16x4 = 0x0000000C, - WGPUVertexFormat_Unorm16x2 = 0x0000000D, - WGPUVertexFormat_Unorm16x4 = 0x0000000E, - WGPUVertexFormat_Snorm16x2 = 0x0000000F, - WGPUVertexFormat_Snorm16x4 = 0x00000010, - WGPUVertexFormat_Float16x2 = 0x00000011, - WGPUVertexFormat_Float16x4 = 0x00000012, - WGPUVertexFormat_Float32 = 0x00000013, - WGPUVertexFormat_Float32x2 = 0x00000014, - WGPUVertexFormat_Float32x3 = 0x00000015, - WGPUVertexFormat_Float32x4 = 0x00000016, - WGPUVertexFormat_Uint32 = 0x00000017, - WGPUVertexFormat_Uint32x2 = 0x00000018, - WGPUVertexFormat_Uint32x3 = 0x00000019, - WGPUVertexFormat_Uint32x4 = 0x0000001A, - WGPUVertexFormat_Sint32 = 0x0000001B, - WGPUVertexFormat_Sint32x2 = 0x0000001C, - WGPUVertexFormat_Sint32x3 = 0x0000001D, - WGPUVertexFormat_Sint32x4 = 0x0000001E, + WGPUVertexFormat_Uint8 = 0x00000001, + WGPUVertexFormat_Uint8x2 = 0x00000002, + WGPUVertexFormat_Uint8x4 = 0x00000003, + WGPUVertexFormat_Sint8 = 0x00000004, + WGPUVertexFormat_Sint8x2 = 0x00000005, + WGPUVertexFormat_Sint8x4 = 0x00000006, + WGPUVertexFormat_Unorm8 = 0x00000007, + WGPUVertexFormat_Unorm8x2 = 0x00000008, + WGPUVertexFormat_Unorm8x4 = 0x00000009, + WGPUVertexFormat_Snorm8 = 0x0000000A, + WGPUVertexFormat_Snorm8x2 = 0x0000000B, + WGPUVertexFormat_Snorm8x4 = 0x0000000C, + WGPUVertexFormat_Uint16 = 0x0000000D, + WGPUVertexFormat_Uint16x2 = 0x0000000E, + WGPUVertexFormat_Uint16x4 = 0x0000000F, + WGPUVertexFormat_Sint16 = 0x00000010, + WGPUVertexFormat_Sint16x2 = 0x00000011, + WGPUVertexFormat_Sint16x4 = 0x00000012, + WGPUVertexFormat_Unorm16 = 0x00000013, + WGPUVertexFormat_Unorm16x2 = 0x00000014, + WGPUVertexFormat_Unorm16x4 = 0x00000015, + WGPUVertexFormat_Snorm16 = 0x00000016, + WGPUVertexFormat_Snorm16x2 = 0x00000017, + WGPUVertexFormat_Snorm16x4 = 0x00000018, + WGPUVertexFormat_Float16 = 0x00000019, + WGPUVertexFormat_Float16x2 = 0x0000001A, + WGPUVertexFormat_Float16x4 = 0x0000001B, + WGPUVertexFormat_Float32 = 0x0000001C, + WGPUVertexFormat_Float32x2 = 0x0000001D, + WGPUVertexFormat_Float32x3 = 0x0000001E, + WGPUVertexFormat_Float32x4 = 0x0000001F, + WGPUVertexFormat_Uint32 = 0x00000020, + WGPUVertexFormat_Uint32x2 = 0x00000021, + WGPUVertexFormat_Uint32x3 = 0x00000022, + WGPUVertexFormat_Uint32x4 = 0x00000023, + WGPUVertexFormat_Sint32 = 0x00000024, + WGPUVertexFormat_Sint32x2 = 0x00000025, + WGPUVertexFormat_Sint32x3 = 0x00000026, + WGPUVertexFormat_Sint32x4 = 0x00000027, + WGPUVertexFormat_Unorm10_10_10_2 = 0x00000028, + WGPUVertexFormat_Unorm8x4BGRA = 0x00000029, WGPUVertexFormat_Force32 = 0x7FFFFFFF } WGPUVertexFormat WGPU_ENUM_ATTRIBUTE; typedef enum WGPUVertexStepMode { - WGPUVertexStepMode_Vertex = 0x00000000, - WGPUVertexStepMode_Instance = 0x00000001, - WGPUVertexStepMode_VertexBufferNotUsed = 0x00000002, + /** + * `0x00000000`. + * This @ref WGPUVertexBufferLayout is a "hole" in the @ref WGPUVertexState `buffers` array. + * (See also @ref SentinelValues.) + */ + WGPUVertexStepMode_VertexBufferNotUsed = 0x00000000, + /** + * `0x00000001`. + * Indicates no value is passed for this argument. See @ref SentinelValues. + */ + WGPUVertexStepMode_Undefined = 0x00000001, + WGPUVertexStepMode_Vertex = 0x00000002, + WGPUVertexStepMode_Instance = 0x00000003, WGPUVertexStepMode_Force32 = 0x7FFFFFFF } WGPUVertexStepMode WGPU_ENUM_ATTRIBUTE; -typedef enum WGPUBufferUsage { - WGPUBufferUsage_None = 0x00000000, - WGPUBufferUsage_MapRead = 0x00000001, - WGPUBufferUsage_MapWrite = 0x00000002, - WGPUBufferUsage_CopySrc = 0x00000004, - WGPUBufferUsage_CopyDst = 0x00000008, - WGPUBufferUsage_Index = 0x00000010, - WGPUBufferUsage_Vertex = 0x00000020, - WGPUBufferUsage_Uniform = 0x00000040, - WGPUBufferUsage_Storage = 0x00000080, - WGPUBufferUsage_Indirect = 0x00000100, - WGPUBufferUsage_QueryResolve = 0x00000200, - WGPUBufferUsage_Force32 = 0x7FFFFFFF -} WGPUBufferUsage WGPU_ENUM_ATTRIBUTE; -typedef WGPUFlags WGPUBufferUsageFlags WGPU_ENUM_ATTRIBUTE; +typedef enum WGPUWGSLLanguageFeatureName { + WGPUWGSLLanguageFeatureName_ReadonlyAndReadwriteStorageTextures = 0x00000001, + WGPUWGSLLanguageFeatureName_Packed4x8IntegerDotProduct = 0x00000002, + WGPUWGSLLanguageFeatureName_UnrestrictedPointerParameters = 0x00000003, + WGPUWGSLLanguageFeatureName_PointerCompositeAccess = 0x00000004, + WGPUWGSLLanguageFeatureName_Force32 = 0x7FFFFFFF +} WGPUWGSLLanguageFeatureName WGPU_ENUM_ATTRIBUTE; -typedef enum WGPUColorWriteMask { - WGPUColorWriteMask_None = 0x00000000, - WGPUColorWriteMask_Red = 0x00000001, - WGPUColorWriteMask_Green = 0x00000002, - WGPUColorWriteMask_Blue = 0x00000004, - WGPUColorWriteMask_Alpha = 0x00000008, - WGPUColorWriteMask_All = 0x0000000F, - WGPUColorWriteMask_Force32 = 0x7FFFFFFF -} WGPUColorWriteMask WGPU_ENUM_ATTRIBUTE; -typedef WGPUFlags WGPUColorWriteMaskFlags WGPU_ENUM_ATTRIBUTE; +/** + * Status returned from a call to ::wgpuInstanceWaitAny. + */ +typedef enum WGPUWaitStatus { + /** + * `0x00000001`. + * At least one WGPUFuture completed successfully. + */ + WGPUWaitStatus_Success = 0x00000001, + /** + * `0x00000002`. + * No WGPUFutures completed within the timeout. + */ + WGPUWaitStatus_TimedOut = 0x00000002, + /** + * `0x00000003`. + * A @ref Timed-Wait was performed when WGPUInstanceFeatures::timedWaitAnyEnable is false. + */ + WGPUWaitStatus_UnsupportedTimeout = 0x00000003, + /** + * `0x00000004`. + * The number of futures waited on in a @ref Timed-Wait is greater than the supported WGPUInstanceFeatures::timedWaitAnyMaxCount. + */ + WGPUWaitStatus_UnsupportedCount = 0x00000004, + /** + * `0x00000005`. + * An invalid wait was performed with @ref Mixed-Sources. + */ + WGPUWaitStatus_UnsupportedMixedSources = 0x00000005, + WGPUWaitStatus_Force32 = 0x7FFFFFFF +} WGPUWaitStatus WGPU_ENUM_ATTRIBUTE; -typedef enum WGPUMapMode { - WGPUMapMode_None = 0x00000000, - WGPUMapMode_Read = 0x00000001, - WGPUMapMode_Write = 0x00000002, - WGPUMapMode_Force32 = 0x7FFFFFFF -} WGPUMapMode WGPU_ENUM_ATTRIBUTE; -typedef WGPUFlags WGPUMapModeFlags WGPU_ENUM_ATTRIBUTE; -typedef enum WGPUShaderStage { - WGPUShaderStage_None = 0x00000000, - WGPUShaderStage_Vertex = 0x00000001, - WGPUShaderStage_Fragment = 0x00000002, - WGPUShaderStage_Compute = 0x00000004, - WGPUShaderStage_Force32 = 0x7FFFFFFF -} WGPUShaderStage WGPU_ENUM_ATTRIBUTE; -typedef WGPUFlags WGPUShaderStageFlags WGPU_ENUM_ATTRIBUTE; +/** @} */ -typedef enum WGPUTextureUsage { - WGPUTextureUsage_None = 0x00000000, - WGPUTextureUsage_CopySrc = 0x00000001, - WGPUTextureUsage_CopyDst = 0x00000002, - WGPUTextureUsage_TextureBinding = 0x00000004, - WGPUTextureUsage_StorageBinding = 0x00000008, - WGPUTextureUsage_RenderAttachment = 0x00000010, - WGPUTextureUsage_Force32 = 0x7FFFFFFF -} WGPUTextureUsage WGPU_ENUM_ATTRIBUTE; -typedef WGPUFlags WGPUTextureUsageFlags WGPU_ENUM_ATTRIBUTE; +/** + * \defgroup Bitflags + * \brief Type and constant definitions for bitflag types. + * + * @{ + */ +typedef WGPUFlags WGPUBufferUsage; +static const WGPUBufferUsage WGPUBufferUsage_None = 0x0000000000000000; +static const WGPUBufferUsage WGPUBufferUsage_MapRead = 0x0000000000000001; +static const WGPUBufferUsage WGPUBufferUsage_MapWrite = 0x0000000000000002; +static const WGPUBufferUsage WGPUBufferUsage_CopySrc = 0x0000000000000004; +static const WGPUBufferUsage WGPUBufferUsage_CopyDst = 0x0000000000000008; +static const WGPUBufferUsage WGPUBufferUsage_Index = 0x0000000000000010; +static const WGPUBufferUsage WGPUBufferUsage_Vertex = 0x0000000000000020; +static const WGPUBufferUsage WGPUBufferUsage_Uniform = 0x0000000000000040; +static const WGPUBufferUsage WGPUBufferUsage_Storage = 0x0000000000000080; +static const WGPUBufferUsage WGPUBufferUsage_Indirect = 0x0000000000000100; +static const WGPUBufferUsage WGPUBufferUsage_QueryResolve = 0x0000000000000200; -typedef void (*WGPUBufferMapCallback)(WGPUBufferMapAsyncStatus status, void * userdata) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUCompilationInfoCallback)(WGPUCompilationInfoRequestStatus status, struct WGPUCompilationInfo const * compilationInfo, void * userdata) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUCreateComputePipelineAsyncCallback)(WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline pipeline, char const * message, void * userdata) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUCreateRenderPipelineAsyncCallback)(WGPUCreatePipelineAsyncStatus status, WGPURenderPipeline pipeline, char const * message, void * userdata) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUDeviceLostCallback)(WGPUDeviceLostReason reason, char const * message, void * userdata) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUErrorCallback)(WGPUErrorType type, char const * message, void * userdata) WGPU_FUNCTION_ATTRIBUTE; +typedef WGPUFlags WGPUColorWriteMask; +static const WGPUColorWriteMask WGPUColorWriteMask_None = 0x0000000000000000; +static const WGPUColorWriteMask WGPUColorWriteMask_Red = 0x0000000000000001; +static const WGPUColorWriteMask WGPUColorWriteMask_Green = 0x0000000000000002; +static const WGPUColorWriteMask WGPUColorWriteMask_Blue = 0x0000000000000004; +static const WGPUColorWriteMask WGPUColorWriteMask_Alpha = 0x0000000000000008; +static const WGPUColorWriteMask WGPUColorWriteMask_All = 0x000000000000000F /* Red | Green | Blue | Alpha */; + +typedef WGPUFlags WGPUMapMode; +static const WGPUMapMode WGPUMapMode_None = 0x0000000000000000; +static const WGPUMapMode WGPUMapMode_Read = 0x0000000000000001; +static const WGPUMapMode WGPUMapMode_Write = 0x0000000000000002; + +typedef WGPUFlags WGPUShaderStage; +static const WGPUShaderStage WGPUShaderStage_None = 0x0000000000000000; +static const WGPUShaderStage WGPUShaderStage_Vertex = 0x0000000000000001; +static const WGPUShaderStage WGPUShaderStage_Fragment = 0x0000000000000002; +static const WGPUShaderStage WGPUShaderStage_Compute = 0x0000000000000004; + +typedef WGPUFlags WGPUTextureUsage; +static const WGPUTextureUsage WGPUTextureUsage_None = 0x0000000000000000; +static const WGPUTextureUsage WGPUTextureUsage_CopySrc = 0x0000000000000001; +static const WGPUTextureUsage WGPUTextureUsage_CopyDst = 0x0000000000000002; +static const WGPUTextureUsage WGPUTextureUsage_TextureBinding = 0x0000000000000004; +static const WGPUTextureUsage WGPUTextureUsage_StorageBinding = 0x0000000000000008; +static const WGPUTextureUsage WGPUTextureUsage_RenderAttachment = 0x0000000000000010; + + +/** @} */ typedef void (*WGPUProc)(void) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUQueueWorkDoneCallback)(WGPUQueueWorkDoneStatus status, void * userdata) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPURequestAdapterCallback)(WGPURequestAdapterStatus status, WGPUAdapter adapter, char const * message, void * userdata) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPURequestDeviceCallback)(WGPURequestDeviceStatus status, WGPUDevice device, char const * message, void * userdata) WGPU_FUNCTION_ATTRIBUTE; + + +/** + * \defgroup Callbacks + * \brief Callbacks through which asynchronous functions return. + * + * @{ + */ +/** + * @param message + * This parameter is @ref PassedWithoutOwnership. + */ +typedef void (*WGPUBufferMapCallback)(WGPUMapAsyncStatus status, WGPUStringView message, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2) WGPU_FUNCTION_ATTRIBUTE; +/** + * @param compilationInfo + * This parameter is @ref PassedWithoutOwnership. + */ +typedef void (*WGPUCompilationInfoCallback)(WGPUCompilationInfoRequestStatus status, struct WGPUCompilationInfo const * compilationInfo, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2) WGPU_FUNCTION_ATTRIBUTE; +/** + * @param pipeline + * This parameter is @ref PassedWithOwnership. + */ +typedef void (*WGPUCreateComputePipelineAsyncCallback)(WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline pipeline, WGPUStringView message, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2) WGPU_FUNCTION_ATTRIBUTE; +/** + * @param pipeline + * This parameter is @ref PassedWithOwnership. + */ +typedef void (*WGPUCreateRenderPipelineAsyncCallback)(WGPUCreatePipelineAsyncStatus status, WGPURenderPipeline pipeline, WGPUStringView message, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2) WGPU_FUNCTION_ATTRIBUTE; +/** + * @param device + * Reference to the device which was lost. If, and only if, the `reason` is @ref WGPUDeviceLostReason_FailedCreation, this is a non-null pointer to a null @ref WGPUDevice. + * This parameter is @ref PassedWithoutOwnership. + * + * @param message + * This parameter is @ref PassedWithoutOwnership. + */ +typedef void (*WGPUDeviceLostCallback)(WGPUDevice const * device, WGPUDeviceLostReason reason, WGPUStringView message, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2) WGPU_FUNCTION_ATTRIBUTE; +/** + * @param status + * See @ref WGPUPopErrorScopeStatus. + * + * @param type + * The type of the error caught by the scope, or @ref WGPUErrorType_NoError if there was none. + * If the `status` is not @ref WGPUPopErrorScopeStatus_Success, this is @ref WGPUErrorType_NoError. + * + * @param message + * If the `type` is not @ref WGPUErrorType_NoError, this is a non-empty @ref LocalizableHumanReadableMessageString; + * otherwise, this is an empty string. + * This parameter is @ref PassedWithoutOwnership. + */ +typedef void (*WGPUPopErrorScopeCallback)(WGPUPopErrorScopeStatus status, WGPUErrorType type, WGPUStringView message, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2) WGPU_FUNCTION_ATTRIBUTE; +typedef void (*WGPUQueueWorkDoneCallback)(WGPUQueueWorkDoneStatus status, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2) WGPU_FUNCTION_ATTRIBUTE; +/** + * @param adapter + * This parameter is @ref PassedWithOwnership. + * + * @param message + * This parameter is @ref PassedWithoutOwnership. + */ +typedef void (*WGPURequestAdapterCallback)(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2) WGPU_FUNCTION_ATTRIBUTE; +/** + * @param device + * This parameter is @ref PassedWithOwnership. + * + * @param message + * This parameter is @ref PassedWithoutOwnership. + */ +typedef void (*WGPURequestDeviceCallback)(WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView message, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2) WGPU_FUNCTION_ATTRIBUTE; +/** + * @param device + * This parameter is @ref PassedWithoutOwnership. + * + * @param message + * This parameter is @ref PassedWithoutOwnership. + */ +typedef void (*WGPUUncapturedErrorCallback)(WGPUDevice const * device, WGPUErrorType type, WGPUStringView message, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2) WGPU_FUNCTION_ATTRIBUTE; + +/** @} */ +/** + * \defgroup ChainedStructures Chained Structures + * \brief Structures used to extend descriptors. + * + * @{ + */ typedef struct WGPUChainedStruct { struct WGPUChainedStruct const * next; @@ -750,17 +1275,126 @@ typedef struct WGPUChainedStructOut { WGPUSType sType; } WGPUChainedStructOut WGPU_STRUCTURE_ATTRIBUTE; -typedef struct WGPUAdapterProperties { +/** @} */ + + +/** + * \defgroup Structures + * \brief Descriptors and other transparent structures. + * + * @{ + */ + + /** + * \defgroup WGPUCallbackInfo + * \brief Callback info structures that are used in asynchronous functions. + * + * @{ + */ +typedef struct WGPUBufferMapCallbackInfo { + WGPUChainedStruct const * nextInChain; + WGPUCallbackMode mode; + WGPUBufferMapCallback callback; + WGPU_NULLABLE void* userdata1; + WGPU_NULLABLE void* userdata2; +} WGPUBufferMapCallbackInfo WGPU_STRUCTURE_ATTRIBUTE; + +typedef struct WGPUCompilationInfoCallbackInfo { + WGPUChainedStruct const * nextInChain; + WGPUCallbackMode mode; + WGPUCompilationInfoCallback callback; + WGPU_NULLABLE void* userdata1; + WGPU_NULLABLE void* userdata2; +} WGPUCompilationInfoCallbackInfo WGPU_STRUCTURE_ATTRIBUTE; + +typedef struct WGPUCreateComputePipelineAsyncCallbackInfo { + WGPUChainedStruct const * nextInChain; + WGPUCallbackMode mode; + WGPUCreateComputePipelineAsyncCallback callback; + WGPU_NULLABLE void* userdata1; + WGPU_NULLABLE void* userdata2; +} WGPUCreateComputePipelineAsyncCallbackInfo WGPU_STRUCTURE_ATTRIBUTE; + +typedef struct WGPUCreateRenderPipelineAsyncCallbackInfo { + WGPUChainedStruct const * nextInChain; + WGPUCallbackMode mode; + WGPUCreateRenderPipelineAsyncCallback callback; + WGPU_NULLABLE void* userdata1; + WGPU_NULLABLE void* userdata2; +} WGPUCreateRenderPipelineAsyncCallbackInfo WGPU_STRUCTURE_ATTRIBUTE; + +typedef struct WGPUDeviceLostCallbackInfo { + WGPUChainedStruct const * nextInChain; + WGPUCallbackMode mode; + WGPUDeviceLostCallback callback; + WGPU_NULLABLE void* userdata1; + WGPU_NULLABLE void* userdata2; +} WGPUDeviceLostCallbackInfo WGPU_STRUCTURE_ATTRIBUTE; + +typedef struct WGPUPopErrorScopeCallbackInfo { + WGPUChainedStruct const * nextInChain; + WGPUCallbackMode mode; + WGPUPopErrorScopeCallback callback; + WGPU_NULLABLE void* userdata1; + WGPU_NULLABLE void* userdata2; +} WGPUPopErrorScopeCallbackInfo WGPU_STRUCTURE_ATTRIBUTE; + +typedef struct WGPUQueueWorkDoneCallbackInfo { + WGPUChainedStruct const * nextInChain; + WGPUCallbackMode mode; + WGPUQueueWorkDoneCallback callback; + WGPU_NULLABLE void* userdata1; + WGPU_NULLABLE void* userdata2; +} WGPUQueueWorkDoneCallbackInfo WGPU_STRUCTURE_ATTRIBUTE; + +typedef struct WGPURequestAdapterCallbackInfo { + WGPUChainedStruct const * nextInChain; + WGPUCallbackMode mode; + WGPURequestAdapterCallback callback; + WGPU_NULLABLE void* userdata1; + WGPU_NULLABLE void* userdata2; +} WGPURequestAdapterCallbackInfo WGPU_STRUCTURE_ATTRIBUTE; + +typedef struct WGPURequestDeviceCallbackInfo { + WGPUChainedStruct const * nextInChain; + WGPUCallbackMode mode; + WGPURequestDeviceCallback callback; + WGPU_NULLABLE void* userdata1; + WGPU_NULLABLE void* userdata2; +} WGPURequestDeviceCallbackInfo WGPU_STRUCTURE_ATTRIBUTE; + +typedef struct WGPUUncapturedErrorCallbackInfo { + WGPUChainedStruct const * nextInChain; + WGPUUncapturedErrorCallback callback; + WGPU_NULLABLE void* userdata1; + WGPU_NULLABLE void* userdata2; +} WGPUUncapturedErrorCallbackInfo WGPU_STRUCTURE_ATTRIBUTE; + +/** @} */ + +typedef struct WGPUAdapterInfo { WGPUChainedStructOut * nextInChain; - uint32_t vendorID; - char const * vendorName; - char const * architecture; - uint32_t deviceID; - char const * name; - char const * driverDescription; - WGPUAdapterType adapterType; + /** + * This is an \ref OutputString. + */ + WGPUStringView vendor; + /** + * This is an \ref OutputString. + */ + WGPUStringView architecture; + /** + * This is an \ref OutputString. + */ + WGPUStringView device; + /** + * This is an \ref OutputString. + */ + WGPUStringView description; WGPUBackendType backendType; -} WGPUAdapterProperties WGPU_STRUCTURE_ATTRIBUTE; + WGPUAdapterType adapterType; + uint32_t vendorID; + uint32_t deviceID; +} WGPUAdapterInfo WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUBindGroupEntry { WGPUChainedStruct const * nextInChain; @@ -787,8 +1421,11 @@ typedef struct WGPUBufferBindingLayout { typedef struct WGPUBufferDescriptor { WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; - WGPUBufferUsageFlags usage; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView label; + WGPUBufferUsage usage; uint64_t size; WGPUBool mappedAtCreation; } WGPUBufferDescriptor WGPU_STRUCTURE_ATTRIBUTE; @@ -802,25 +1439,48 @@ typedef struct WGPUColor { typedef struct WGPUCommandBufferDescriptor { WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView label; } WGPUCommandBufferDescriptor WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUCommandEncoderDescriptor { WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView label; } WGPUCommandEncoderDescriptor WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUCompilationMessage { WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * message; + /** + * A @ref LocalizableHumanReadableMessageString. + * + * This is an \ref OutputString. + */ + WGPUStringView message; + /** + * Severity level of the message. + */ WGPUCompilationMessageType type; + /** + * Line number where the message is attached, starting at 1. + */ uint64_t lineNum; + /** + * Offset in UTF-8 code units (bytes) from the beginning of the line, starting at 1. + */ uint64_t linePos; + /** + * Offset in UTF-8 code units (bytes) from the beginning of the shader code, starting at 0. + */ uint64_t offset; + /** + * Length in UTF-8 code units (bytes) of the span the message corresponds to. + */ uint64_t length; - uint64_t utf16LinePos; - uint64_t utf16Offset; - uint64_t utf16Length; } WGPUCompilationMessage WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUComputePassTimestampWrites { @@ -831,7 +1491,10 @@ typedef struct WGPUComputePassTimestampWrites { typedef struct WGPUConstantEntry { WGPUChainedStruct const * nextInChain; - char const * key; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView key; double value; } WGPUConstantEntry WGPU_STRUCTURE_ATTRIBUTE; @@ -841,11 +1504,35 @@ typedef struct WGPUExtent3D { uint32_t depthOrArrayLayers; } WGPUExtent3D WGPU_STRUCTURE_ATTRIBUTE; -typedef struct WGPUInstanceDescriptor { - WGPUChainedStruct const * nextInChain; -} WGPUInstanceDescriptor WGPU_STRUCTURE_ATTRIBUTE; +/** + * Opaque handle to an asynchronous operation. See @ref Asynchronous-Operations for more information. + */ +typedef struct WGPUFuture { + /** + * Opaque id of the @ref WGPUFuture + */ + uint64_t id; +} WGPUFuture WGPU_STRUCTURE_ATTRIBUTE; + +/** + * Features enabled on the WGPUInstance + */ +typedef struct WGPUInstanceCapabilities { + /** This struct chain is used as mutable in some places and immutable in others. */ + WGPUChainedStructOut * nextInChain; + /** + * Enable use of ::wgpuInstanceWaitAny with `timeoutNS > 0`. + */ + WGPUBool timedWaitAnyEnable; + /** + * The maximum number @ref WGPUFutureWaitInfo supported in a call to ::wgpuInstanceWaitAny with `timeoutNS > 0`. + */ + size_t timedWaitAnyMaxCount; +} WGPUInstanceCapabilities WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPULimits { + /** This struct chain is used as mutable in some places and immutable in others. */ + WGPUChainedStructOut * nextInChain; uint32_t maxTextureDimension1D; uint32_t maxTextureDimension2D; uint32_t maxTextureDimension3D; @@ -868,7 +1555,6 @@ typedef struct WGPULimits { uint64_t maxBufferSize; uint32_t maxVertexAttributes; uint32_t maxVertexBufferArrayStride; - uint32_t maxInterStageShaderComponents; uint32_t maxInterStageShaderVariables; uint32_t maxColorAttachments; uint32_t maxColorAttachmentBytesPerSample; @@ -895,45 +1581,55 @@ typedef struct WGPUOrigin3D { typedef struct WGPUPipelineLayoutDescriptor { WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView label; size_t bindGroupLayoutCount; WGPUBindGroupLayout const * bindGroupLayouts; } WGPUPipelineLayoutDescriptor WGPU_STRUCTURE_ATTRIBUTE; -// Can be chained in WGPUPrimitiveState -typedef struct WGPUPrimitiveDepthClipControl { - WGPUChainedStruct chain; - WGPUBool unclippedDepth; -} WGPUPrimitiveDepthClipControl WGPU_STRUCTURE_ATTRIBUTE; - typedef struct WGPUPrimitiveState { WGPUChainedStruct const * nextInChain; WGPUPrimitiveTopology topology; WGPUIndexFormat stripIndexFormat; WGPUFrontFace frontFace; WGPUCullMode cullMode; + WGPUBool unclippedDepth; } WGPUPrimitiveState WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUQuerySetDescriptor { WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView label; WGPUQueryType type; uint32_t count; } WGPUQuerySetDescriptor WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUQueueDescriptor { WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView label; } WGPUQueueDescriptor WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPURenderBundleDescriptor { WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView label; } WGPURenderBundleDescriptor WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPURenderBundleEncoderDescriptor { WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView label; size_t colorFormatCount; WGPUTextureFormat const * colorFormats; WGPUTextureFormat depthStencilFormat; @@ -954,11 +1650,10 @@ typedef struct WGPURenderPassDepthStencilAttachment { WGPUBool stencilReadOnly; } WGPURenderPassDepthStencilAttachment WGPU_STRUCTURE_ATTRIBUTE; -// Can be chained in WGPURenderPassDescriptor -typedef struct WGPURenderPassDescriptorMaxDrawCount { +typedef struct WGPURenderPassMaxDrawCount { WGPUChainedStruct chain; uint64_t maxDrawCount; -} WGPURenderPassDescriptorMaxDrawCount WGPU_STRUCTURE_ATTRIBUTE; +} WGPURenderPassMaxDrawCount WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPURenderPassTimestampWrites { WGPUQuerySet querySet; @@ -968,10 +1663,28 @@ typedef struct WGPURenderPassTimestampWrites { typedef struct WGPURequestAdapterOptions { WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE WGPUSurface compatibleSurface; + /** + * "Feature level" for the adapter request. If an adapter is returned, it must support the features and limits in the requested feature level. + * + * Implementations may ignore @ref WGPUFeatureLevel_Compatibility and provide @ref WGPUFeatureLevel_Core instead. @ref WGPUFeatureLevel_Core is the default in the JS API, but in C, this field is **required** (must not be undefined). + */ + WGPUFeatureLevel featureLevel; WGPUPowerPreference powerPreference; - WGPUBackendType backendType; + /** + * If true, requires the adapter to be a "fallback" adapter as defined by the JS spec. + * If this is not possible, the request returns null. + */ WGPUBool forceFallbackAdapter; + /** + * If set, requires the adapter to have a particular backend type. + * If this is not possible, the request returns null. + */ + WGPUBackendType backendType; + /** + * If set, requires the adapter to be able to output to a particular surface. + * If this is not possible, the request returns null. + */ + WGPU_NULLABLE WGPUSurface compatibleSurface; } WGPURequestAdapterOptions WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUSamplerBindingLayout { @@ -981,7 +1694,10 @@ typedef struct WGPUSamplerBindingLayout { typedef struct WGPUSamplerDescriptor { WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView label; WGPUAddressMode addressModeU; WGPUAddressMode addressModeV; WGPUAddressMode addressModeW; @@ -994,24 +1710,27 @@ typedef struct WGPUSamplerDescriptor { uint16_t maxAnisotropy; } WGPUSamplerDescriptor WGPU_STRUCTURE_ATTRIBUTE; -typedef struct WGPUShaderModuleCompilationHint { +typedef struct WGPUShaderModuleDescriptor { WGPUChainedStruct const * nextInChain; - char const * entryPoint; - WGPUPipelineLayout layout; -} WGPUShaderModuleCompilationHint WGPU_STRUCTURE_ATTRIBUTE; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView label; +} WGPUShaderModuleDescriptor WGPU_STRUCTURE_ATTRIBUTE; -// Can be chained in WGPUShaderModuleDescriptor -typedef struct WGPUShaderModuleSPIRVDescriptor { +typedef struct WGPUShaderSourceSPIRV { WGPUChainedStruct chain; uint32_t codeSize; uint32_t const * code; -} WGPUShaderModuleSPIRVDescriptor WGPU_STRUCTURE_ATTRIBUTE; +} WGPUShaderSourceSPIRV WGPU_STRUCTURE_ATTRIBUTE; -// Can be chained in WGPUShaderModuleDescriptor -typedef struct WGPUShaderModuleWGSLDescriptor { +typedef struct WGPUShaderSourceWGSL { WGPUChainedStruct chain; - char const * code; -} WGPUShaderModuleWGSLDescriptor WGPU_STRUCTURE_ATTRIBUTE; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView code; +} WGPUShaderSourceWGSL WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUStencilFaceState { WGPUCompareFunction compare; @@ -1027,86 +1746,207 @@ typedef struct WGPUStorageTextureBindingLayout { WGPUTextureViewDimension viewDimension; } WGPUStorageTextureBindingLayout WGPU_STRUCTURE_ATTRIBUTE; +typedef struct WGPUSupportedFeatures { + size_t featureCount; + WGPUFeatureName const * features; +} WGPUSupportedFeatures WGPU_STRUCTURE_ATTRIBUTE; + +typedef struct WGPUSupportedWGSLLanguageFeatures { + size_t featureCount; + WGPUWGSLLanguageFeatureName const * features; +} WGPUSupportedWGSLLanguageFeatures WGPU_STRUCTURE_ATTRIBUTE; + +/** + * Filled by `::wgpuSurfaceGetCapabilities` with what's supported for `::wgpuSurfaceConfigure` for a pair of @ref WGPUSurface and @ref WGPUAdapter. + */ typedef struct WGPUSurfaceCapabilities { WGPUChainedStructOut * nextInChain; + /** + * The bit set of supported @ref WGPUTextureUsage bits. + * Guaranteed to contain @ref WGPUTextureUsage_RenderAttachment. + */ + WGPUTextureUsage usages; + /** + * A list of supported @ref WGPUTextureFormat values, in order of preference. + */ size_t formatCount; - WGPUTextureFormat * formats; + WGPUTextureFormat const * formats; + /** + * A list of supported @ref WGPUPresentMode values. + * Guaranteed to contain @ref WGPUPresentMode_Fifo. + */ size_t presentModeCount; - WGPUPresentMode * presentModes; + WGPUPresentMode const * presentModes; + /** + * A list of supported @ref WGPUCompositeAlphaMode values. + * @ref WGPUCompositeAlphaMode_Auto will be an alias for the first element and will never be present in this array. + */ size_t alphaModeCount; - WGPUCompositeAlphaMode * alphaModes; + WGPUCompositeAlphaMode const * alphaModes; } WGPUSurfaceCapabilities WGPU_STRUCTURE_ATTRIBUTE; +/** + * Options to `::wgpuSurfaceConfigure` for defining how a @ref WGPUSurface will be rendered to and presented to the user. + * See @ref Surface-Configuration for more details. + */ typedef struct WGPUSurfaceConfiguration { WGPUChainedStruct const * nextInChain; + /** + * The @ref WGPUDevice to use to render to surface's textures. + */ WGPUDevice device; + /** + * The @ref WGPUTextureFormat of the surface's textures. + */ WGPUTextureFormat format; - WGPUTextureUsageFlags usage; + /** + * The @ref WGPUTextureUsage of the surface's textures. + */ + WGPUTextureUsage usage; + /** + * The width of the surface's textures. + */ + uint32_t width; + /** + * The height of the surface's textures. + */ + uint32_t height; + /** + * The additional @ref WGPUTextureFormat for @ref WGPUTextureView format reinterpretation of the surface's textures. + */ size_t viewFormatCount; WGPUTextureFormat const * viewFormats; + /** + * How the surface's frames will be composited on the screen. + */ WGPUCompositeAlphaMode alphaMode; - uint32_t width; - uint32_t height; + /** + * When and in which order the surface's frames will be shown on the screen. Defaults to @ref WGPUPresentMode_Fifo. + */ WGPUPresentMode presentMode; } WGPUSurfaceConfiguration WGPU_STRUCTURE_ATTRIBUTE; +/** + * The root descriptor for the creation of an @ref WGPUSurface with `::wgpuInstanceCreateSurface`. + * It isn't sufficient by itself and must have one of the `WGPUSurfaceSource*` in its chain. + * See @ref Surface-Creation for more details. + */ typedef struct WGPUSurfaceDescriptor { WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; + /** + * Label used to refer to the object. + * + * This is a \ref NonNullInputString. + */ + WGPUStringView label; } WGPUSurfaceDescriptor WGPU_STRUCTURE_ATTRIBUTE; -// Can be chained in WGPUSurfaceDescriptor -typedef struct WGPUSurfaceDescriptorFromAndroidNativeWindow { +/** + * Chained in @ref WGPUSurfaceDescriptor to make an @ref WGPUSurface wrapping an Android [`ANativeWindow`](https://developer.android.com/ndk/reference/group/a-native-window). + */ +typedef struct WGPUSurfaceSourceAndroidNativeWindow { WGPUChainedStruct chain; + /** + * The pointer to the [`ANativeWindow`](https://developer.android.com/ndk/reference/group/a-native-window) that will be wrapped by the @ref WGPUSurface. + */ void * window; -} WGPUSurfaceDescriptorFromAndroidNativeWindow WGPU_STRUCTURE_ATTRIBUTE; +} WGPUSurfaceSourceAndroidNativeWindow WGPU_STRUCTURE_ATTRIBUTE; -// Can be chained in WGPUSurfaceDescriptor -typedef struct WGPUSurfaceDescriptorFromCanvasHTMLSelector { - WGPUChainedStruct chain; - char const * selector; -} WGPUSurfaceDescriptorFromCanvasHTMLSelector WGPU_STRUCTURE_ATTRIBUTE; - -// Can be chained in WGPUSurfaceDescriptor -typedef struct WGPUSurfaceDescriptorFromMetalLayer { +/** + * Chained in @ref WGPUSurfaceDescriptor to make an @ref WGPUSurface wrapping a [`CAMetalLayer`](https://developer.apple.com/documentation/quartzcore/cametallayer?language=objc). + */ +typedef struct WGPUSurfaceSourceMetalLayer { WGPUChainedStruct chain; + /** + * The pointer to the [`CAMetalLayer`](https://developer.apple.com/documentation/quartzcore/cametallayer?language=objc) that will be wrapped by the @ref WGPUSurface. + */ void * layer; -} WGPUSurfaceDescriptorFromMetalLayer WGPU_STRUCTURE_ATTRIBUTE; +} WGPUSurfaceSourceMetalLayer WGPU_STRUCTURE_ATTRIBUTE; -// Can be chained in WGPUSurfaceDescriptor -typedef struct WGPUSurfaceDescriptorFromWaylandSurface { +/** + * Chained in @ref WGPUSurfaceDescriptor to make an @ref WGPUSurface wrapping a [Wayland](https://wayland.freedesktop.org/) [`wl_surface`](https://wayland.freedesktop.org/docs/html/apa.html#protocol-spec-wl_surface). + */ +typedef struct WGPUSurfaceSourceWaylandSurface { WGPUChainedStruct chain; + /** + * A [`wl_display`](https://wayland.freedesktop.org/docs/html/apa.html#protocol-spec-wl_display) for this Wayland instance. + */ void * display; + /** + * A [`wl_surface`](https://wayland.freedesktop.org/docs/html/apa.html#protocol-spec-wl_surface) that will be wrapped by the @ref WGPUSurface + */ void * surface; -} WGPUSurfaceDescriptorFromWaylandSurface WGPU_STRUCTURE_ATTRIBUTE; +} WGPUSurfaceSourceWaylandSurface WGPU_STRUCTURE_ATTRIBUTE; -// Can be chained in WGPUSurfaceDescriptor -typedef struct WGPUSurfaceDescriptorFromWindowsHWND { +/** + * Chained in @ref WGPUSurfaceDescriptor to make an @ref WGPUSurface wrapping a Windows [`HWND`](https://learn.microsoft.com/en-us/windows/apps/develop/ui-input/retrieve-hwnd). + */ +typedef struct WGPUSurfaceSourceWindowsHWND { WGPUChainedStruct chain; + /** + * The [`HINSTANCE`](https://learn.microsoft.com/en-us/windows/win32/learnwin32/winmain--the-application-entry-point) for this application. + * Most commonly `GetModuleHandle(nullptr)`. + */ void * hinstance; + /** + * The [`HWND`](https://learn.microsoft.com/en-us/windows/apps/develop/ui-input/retrieve-hwnd) that will be wrapped by the @ref WGPUSurface. + */ void * hwnd; -} WGPUSurfaceDescriptorFromWindowsHWND WGPU_STRUCTURE_ATTRIBUTE; +} WGPUSurfaceSourceWindowsHWND WGPU_STRUCTURE_ATTRIBUTE; -// Can be chained in WGPUSurfaceDescriptor -typedef struct WGPUSurfaceDescriptorFromXcbWindow { +/** + * Chained in @ref WGPUSurfaceDescriptor to make an @ref WGPUSurface wrapping an [XCB](https://xcb.freedesktop.org/) `xcb_window_t`. + */ +typedef struct WGPUSurfaceSourceXCBWindow { WGPUChainedStruct chain; + /** + * The `xcb_connection_t` for the connection to the X server. + */ void * connection; + /** + * The `xcb_window_t` for the window that will be wrapped by the @ref WGPUSurface. + */ uint32_t window; -} WGPUSurfaceDescriptorFromXcbWindow WGPU_STRUCTURE_ATTRIBUTE; +} WGPUSurfaceSourceXCBWindow WGPU_STRUCTURE_ATTRIBUTE; -// Can be chained in WGPUSurfaceDescriptor -typedef struct WGPUSurfaceDescriptorFromXlibWindow { +/** + * Chained in @ref WGPUSurfaceDescriptor to make an @ref WGPUSurface wrapping an [Xlib](https://www.x.org/releases/current/doc/libX11/libX11/libX11.html) `Window`. + */ +typedef struct WGPUSurfaceSourceXlibWindow { WGPUChainedStruct chain; + /** + * A pointer to the [`Display`](https://www.x.org/releases/current/doc/libX11/libX11/libX11.html#Opening_the_Display) connected to the X server. + */ void * display; + /** + * The [`Window`](https://www.x.org/releases/current/doc/libX11/libX11/libX11.html#Creating_Windows) that will be wrapped by the @ref WGPUSurface. + */ uint64_t window; -} WGPUSurfaceDescriptorFromXlibWindow WGPU_STRUCTURE_ATTRIBUTE; +} WGPUSurfaceSourceXlibWindow WGPU_STRUCTURE_ATTRIBUTE; +/** + * Queried each frame from a @ref WGPUSurface to get a @ref WGPUTexture to render to along with some metadata. + * See @ref Surface-Presenting for more details. + */ typedef struct WGPUSurfaceTexture { + WGPUChainedStructOut * nextInChain; + /** + * The @ref WGPUTexture representing the frame that will be shown on the surface. + * It is @ref ReturnedWithOwnership from @ref wgpuSurfaceGetCurrentTexture. + */ WGPUTexture texture; - WGPUBool suboptimal; + /** + * Whether the call to `::wgpuSurfaceGetCurrentTexture` succeeded and a hint as to why it might not have. + */ WGPUSurfaceGetCurrentTextureStatus status; } WGPUSurfaceTexture WGPU_STRUCTURE_ATTRIBUTE; +typedef struct WGPUTexelCopyBufferLayout { + uint64_t offset; + uint32_t bytesPerRow; + uint32_t rowsPerImage; +} WGPUTexelCopyBufferLayout WGPU_STRUCTURE_ATTRIBUTE; + typedef struct WGPUTextureBindingLayout { WGPUChainedStruct const * nextInChain; WGPUTextureSampleType sampleType; @@ -1114,16 +1954,12 @@ typedef struct WGPUTextureBindingLayout { WGPUBool multisampled; } WGPUTextureBindingLayout WGPU_STRUCTURE_ATTRIBUTE; -typedef struct WGPUTextureDataLayout { - WGPUChainedStruct const * nextInChain; - uint64_t offset; - uint32_t bytesPerRow; - uint32_t rowsPerImage; -} WGPUTextureDataLayout WGPU_STRUCTURE_ATTRIBUTE; - typedef struct WGPUTextureViewDescriptor { WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView label; WGPUTextureFormat format; WGPUTextureViewDimension dimension; uint32_t baseMipLevel; @@ -1131,6 +1967,7 @@ typedef struct WGPUTextureViewDescriptor { uint32_t baseArrayLayer; uint32_t arrayLayerCount; WGPUTextureAspect aspect; + WGPUTextureUsage usage; } WGPUTextureViewDescriptor WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUVertexAttribute { @@ -1141,7 +1978,10 @@ typedef struct WGPUVertexAttribute { typedef struct WGPUBindGroupDescriptor { WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView label; WGPUBindGroupLayout layout; size_t entryCount; WGPUBindGroupEntry const * entries; @@ -1150,7 +1990,7 @@ typedef struct WGPUBindGroupDescriptor { typedef struct WGPUBindGroupLayoutEntry { WGPUChainedStruct const * nextInChain; uint32_t binding; - WGPUShaderStageFlags visibility; + WGPUShaderStage visibility; WGPUBufferBindingLayout buffer; WGPUSamplerBindingLayout sampler; WGPUTextureBindingLayout texture; @@ -1170,14 +2010,17 @@ typedef struct WGPUCompilationInfo { typedef struct WGPUComputePassDescriptor { WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView label; WGPU_NULLABLE WGPUComputePassTimestampWrites const * timestampWrites; } WGPUComputePassDescriptor WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUDepthStencilState { WGPUChainedStruct const * nextInChain; WGPUTextureFormat format; - WGPUBool depthWriteEnabled; + WGPUOptionalBool depthWriteEnabled; WGPUCompareFunction depthCompare; WGPUStencilFaceState stencilFront; WGPUStencilFaceState stencilBack; @@ -1188,24 +2031,49 @@ typedef struct WGPUDepthStencilState { float depthBiasClamp; } WGPUDepthStencilState WGPU_STRUCTURE_ATTRIBUTE; -typedef struct WGPUImageCopyBuffer { +typedef struct WGPUDeviceDescriptor { WGPUChainedStruct const * nextInChain; - WGPUTextureDataLayout layout; - WGPUBuffer buffer; -} WGPUImageCopyBuffer WGPU_STRUCTURE_ATTRIBUTE; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView label; + size_t requiredFeatureCount; + WGPUFeatureName const * requiredFeatures; + WGPU_NULLABLE WGPULimits const * requiredLimits; + WGPUQueueDescriptor defaultQueue; + WGPUDeviceLostCallbackInfo deviceLostCallbackInfo; + WGPUUncapturedErrorCallbackInfo uncapturedErrorCallbackInfo; +} WGPUDeviceDescriptor WGPU_STRUCTURE_ATTRIBUTE; -typedef struct WGPUImageCopyTexture { +/** + * Struct holding a future to wait on, and a `completed` boolean flag. + */ +typedef struct WGPUFutureWaitInfo { + /** + * The future to wait on. + */ + WGPUFuture future; + /** + * Whether or not the future completed. + */ + WGPUBool completed; +} WGPUFutureWaitInfo WGPU_STRUCTURE_ATTRIBUTE; + +typedef struct WGPUInstanceDescriptor { WGPUChainedStruct const * nextInChain; - WGPUTexture texture; - uint32_t mipLevel; - WGPUOrigin3D origin; - WGPUTextureAspect aspect; -} WGPUImageCopyTexture WGPU_STRUCTURE_ATTRIBUTE; + /** + * Instance features to enable + */ + WGPUInstanceCapabilities features; +} WGPUInstanceDescriptor WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUProgrammableStageDescriptor { WGPUChainedStruct const * nextInChain; WGPUShaderModule module; - WGPU_NULLABLE char const * entryPoint; + /** + * This is a \ref NullableInputString. + */ + WGPUStringView entryPoint; size_t constantCount; WGPUConstantEntry const * constants; } WGPUProgrammableStageDescriptor WGPU_STRUCTURE_ATTRIBUTE; @@ -1213,33 +2081,32 @@ typedef struct WGPUProgrammableStageDescriptor { typedef struct WGPURenderPassColorAttachment { WGPUChainedStruct const * nextInChain; WGPU_NULLABLE WGPUTextureView view; + uint32_t depthSlice; WGPU_NULLABLE WGPUTextureView resolveTarget; WGPULoadOp loadOp; WGPUStoreOp storeOp; WGPUColor clearValue; } WGPURenderPassColorAttachment WGPU_STRUCTURE_ATTRIBUTE; -typedef struct WGPURequiredLimits { - WGPUChainedStruct const * nextInChain; - WGPULimits limits; -} WGPURequiredLimits WGPU_STRUCTURE_ATTRIBUTE; +typedef struct WGPUTexelCopyBufferInfo { + WGPUTexelCopyBufferLayout layout; + WGPUBuffer buffer; +} WGPUTexelCopyBufferInfo WGPU_STRUCTURE_ATTRIBUTE; -typedef struct WGPUShaderModuleDescriptor { - WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; - size_t hintCount; - WGPUShaderModuleCompilationHint const * hints; -} WGPUShaderModuleDescriptor WGPU_STRUCTURE_ATTRIBUTE; - -typedef struct WGPUSupportedLimits { - WGPUChainedStructOut * nextInChain; - WGPULimits limits; -} WGPUSupportedLimits WGPU_STRUCTURE_ATTRIBUTE; +typedef struct WGPUTexelCopyTextureInfo { + WGPUTexture texture; + uint32_t mipLevel; + WGPUOrigin3D origin; + WGPUTextureAspect aspect; +} WGPUTexelCopyTextureInfo WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUTextureDescriptor { WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; - WGPUTextureUsageFlags usage; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView label; + WGPUTextureUsage usage; WGPUTextureDimension dimension; WGPUExtent3D size; WGPUTextureFormat format; @@ -1250,47 +2117,55 @@ typedef struct WGPUTextureDescriptor { } WGPUTextureDescriptor WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUVertexBufferLayout { - uint64_t arrayStride; + /** + * The step mode for the vertex buffer. If @ref WGPUVertexStepMode_VertexBufferNotUsed, + * indicates a "hole" in the parent @ref WGPUVertexState `buffers` array: + * the pipeline does not use a vertex buffer at this `location`. + */ WGPUVertexStepMode stepMode; + uint64_t arrayStride; size_t attributeCount; WGPUVertexAttribute const * attributes; } WGPUVertexBufferLayout WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUBindGroupLayoutDescriptor { WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView label; size_t entryCount; WGPUBindGroupLayoutEntry const * entries; } WGPUBindGroupLayoutDescriptor WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUColorTargetState { WGPUChainedStruct const * nextInChain; + /** + * The texture format of the target. If @ref WGPUTextureFormat_Undefined, + * indicates a "hole" in the parent @ref WGPUFragmentState `targets` array: + * the pipeline does not output a value at this `location`. + */ WGPUTextureFormat format; WGPU_NULLABLE WGPUBlendState const * blend; - WGPUColorWriteMaskFlags writeMask; + WGPUColorWriteMask writeMask; } WGPUColorTargetState WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUComputePipelineDescriptor { WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView label; WGPU_NULLABLE WGPUPipelineLayout layout; WGPUProgrammableStageDescriptor compute; } WGPUComputePipelineDescriptor WGPU_STRUCTURE_ATTRIBUTE; -typedef struct WGPUDeviceDescriptor { - WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; - size_t requiredFeatureCount; - WGPUFeatureName const * requiredFeatures; - WGPU_NULLABLE WGPURequiredLimits const * requiredLimits; - WGPUQueueDescriptor defaultQueue; - WGPUDeviceLostCallback deviceLostCallback; - void * deviceLostUserdata; -} WGPUDeviceDescriptor WGPU_STRUCTURE_ATTRIBUTE; - typedef struct WGPURenderPassDescriptor { WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView label; size_t colorAttachmentCount; WGPURenderPassColorAttachment const * colorAttachments; WGPU_NULLABLE WGPURenderPassDepthStencilAttachment const * depthStencilAttachment; @@ -1301,7 +2176,10 @@ typedef struct WGPURenderPassDescriptor { typedef struct WGPUVertexState { WGPUChainedStruct const * nextInChain; WGPUShaderModule module; - WGPU_NULLABLE char const * entryPoint; + /** + * This is a \ref NullableInputString. + */ + WGPUStringView entryPoint; size_t constantCount; WGPUConstantEntry const * constants; size_t bufferCount; @@ -1311,7 +2189,10 @@ typedef struct WGPUVertexState { typedef struct WGPUFragmentState { WGPUChainedStruct const * nextInChain; WGPUShaderModule module; - WGPU_NULLABLE char const * entryPoint; + /** + * This is a \ref NullableInputString. + */ + WGPUStringView entryPoint; size_t constantCount; WGPUConstantEntry const * constants; size_t targetCount; @@ -1320,7 +2201,10 @@ typedef struct WGPUFragmentState { typedef struct WGPURenderPipelineDescriptor { WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; + /** + * This is a \ref NonNullInputString. + */ + WGPUStringView label; WGPU_NULLABLE WGPUPipelineLayout layout; WGPUVertexState vertex; WGPUPrimitiveState primitive; @@ -1329,401 +2213,1439 @@ typedef struct WGPURenderPipelineDescriptor { WGPU_NULLABLE WGPUFragmentState const * fragment; } WGPURenderPipelineDescriptor WGPU_STRUCTURE_ATTRIBUTE; +/** @} */ + #ifdef __cplusplus extern "C" { #endif #if !defined(WGPU_SKIP_PROCS) +/** + * Proc pointer type for @ref wgpuCreateInstance: + * > @copydoc wgpuCreateInstance + */ typedef WGPUInstance (*WGPUProcCreateInstance)(WGPU_NULLABLE WGPUInstanceDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; -typedef WGPUProc (*WGPUProcGetProcAddress)(WGPUDevice device, char const * procName) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuGetInstanceCapabilities: + * > @copydoc wgpuGetInstanceCapabilities + */ +typedef WGPUStatus (*WGPUProcGetInstanceCapabilities)(WGPUInstanceCapabilities * capabilities) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuGetProcAddress: + * > @copydoc wgpuGetProcAddress + */ +typedef WGPUProc (*WGPUProcGetProcAddress)(WGPUStringView procName) WGPU_FUNCTION_ATTRIBUTE; // Procs of Adapter -typedef size_t (*WGPUProcAdapterEnumerateFeatures)(WGPUAdapter adapter, WGPUFeatureName * features) WGPU_FUNCTION_ATTRIBUTE; -typedef WGPUBool (*WGPUProcAdapterGetLimits)(WGPUAdapter adapter, WGPUSupportedLimits * limits) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcAdapterGetProperties)(WGPUAdapter adapter, WGPUAdapterProperties * properties) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuAdapterGetFeatures: + * > @copydoc wgpuAdapterGetFeatures + */ +typedef void (*WGPUProcAdapterGetFeatures)(WGPUAdapter adapter, WGPUSupportedFeatures * features) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuAdapterGetInfo: + * > @copydoc wgpuAdapterGetInfo + */ +typedef WGPUStatus (*WGPUProcAdapterGetInfo)(WGPUAdapter adapter, WGPUAdapterInfo * info) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuAdapterGetLimits: + * > @copydoc wgpuAdapterGetLimits + */ +typedef WGPUStatus (*WGPUProcAdapterGetLimits)(WGPUAdapter adapter, WGPULimits * limits) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuAdapterHasFeature: + * > @copydoc wgpuAdapterHasFeature + */ typedef WGPUBool (*WGPUProcAdapterHasFeature)(WGPUAdapter adapter, WGPUFeatureName feature) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcAdapterRequestDevice)(WGPUAdapter adapter, WGPU_NULLABLE WGPUDeviceDescriptor const * descriptor, WGPURequestDeviceCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcAdapterReference)(WGPUAdapter adapter) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuAdapterRequestDevice: + * > @copydoc wgpuAdapterRequestDevice + */ +typedef WGPUFuture (*WGPUProcAdapterRequestDevice)(WGPUAdapter adapter, WGPU_NULLABLE WGPUDeviceDescriptor const * descriptor, WGPURequestDeviceCallbackInfo callbackInfo) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuAdapterAddRef. + * > @copydoc wgpuAdapterAddRef + */ +typedef void (*WGPUProcAdapterAddRef)(WGPUAdapter adapter) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuAdapterRelease. + * > @copydoc wgpuAdapterRelease + */ typedef void (*WGPUProcAdapterRelease)(WGPUAdapter adapter) WGPU_FUNCTION_ATTRIBUTE; +// Procs of AdapterInfo +/** + * Proc pointer type for @ref wgpuAdapterInfoFreeMembers: + * > @copydoc wgpuAdapterInfoFreeMembers + */ +typedef void (*WGPUProcAdapterInfoFreeMembers)(WGPUAdapterInfo adapterInfo) WGPU_FUNCTION_ATTRIBUTE; + // Procs of BindGroup -typedef void (*WGPUProcBindGroupSetLabel)(WGPUBindGroup bindGroup, char const * label) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcBindGroupReference)(WGPUBindGroup bindGroup) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuBindGroupSetLabel: + * > @copydoc wgpuBindGroupSetLabel + */ +typedef void (*WGPUProcBindGroupSetLabel)(WGPUBindGroup bindGroup, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuBindGroupAddRef. + * > @copydoc wgpuBindGroupAddRef + */ +typedef void (*WGPUProcBindGroupAddRef)(WGPUBindGroup bindGroup) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuBindGroupRelease. + * > @copydoc wgpuBindGroupRelease + */ typedef void (*WGPUProcBindGroupRelease)(WGPUBindGroup bindGroup) WGPU_FUNCTION_ATTRIBUTE; // Procs of BindGroupLayout -typedef void (*WGPUProcBindGroupLayoutSetLabel)(WGPUBindGroupLayout bindGroupLayout, char const * label) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcBindGroupLayoutReference)(WGPUBindGroupLayout bindGroupLayout) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuBindGroupLayoutSetLabel: + * > @copydoc wgpuBindGroupLayoutSetLabel + */ +typedef void (*WGPUProcBindGroupLayoutSetLabel)(WGPUBindGroupLayout bindGroupLayout, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuBindGroupLayoutAddRef. + * > @copydoc wgpuBindGroupLayoutAddRef + */ +typedef void (*WGPUProcBindGroupLayoutAddRef)(WGPUBindGroupLayout bindGroupLayout) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuBindGroupLayoutRelease. + * > @copydoc wgpuBindGroupLayoutRelease + */ typedef void (*WGPUProcBindGroupLayoutRelease)(WGPUBindGroupLayout bindGroupLayout) WGPU_FUNCTION_ATTRIBUTE; // Procs of Buffer +/** + * Proc pointer type for @ref wgpuBufferDestroy: + * > @copydoc wgpuBufferDestroy + */ typedef void (*WGPUProcBufferDestroy)(WGPUBuffer buffer) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuBufferGetConstMappedRange: + * > @copydoc wgpuBufferGetConstMappedRange + */ typedef void const * (*WGPUProcBufferGetConstMappedRange)(WGPUBuffer buffer, size_t offset, size_t size) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuBufferGetMapState: + * > @copydoc wgpuBufferGetMapState + */ typedef WGPUBufferMapState (*WGPUProcBufferGetMapState)(WGPUBuffer buffer) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuBufferGetMappedRange: + * > @copydoc wgpuBufferGetMappedRange + */ typedef void * (*WGPUProcBufferGetMappedRange)(WGPUBuffer buffer, size_t offset, size_t size) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuBufferGetSize: + * > @copydoc wgpuBufferGetSize + */ typedef uint64_t (*WGPUProcBufferGetSize)(WGPUBuffer buffer) WGPU_FUNCTION_ATTRIBUTE; -typedef WGPUBufferUsageFlags (*WGPUProcBufferGetUsage)(WGPUBuffer buffer) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcBufferMapAsync)(WGPUBuffer buffer, WGPUMapModeFlags mode, size_t offset, size_t size, WGPUBufferMapCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcBufferSetLabel)(WGPUBuffer buffer, char const * label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuBufferGetUsage: + * > @copydoc wgpuBufferGetUsage + */ +typedef WGPUBufferUsage (*WGPUProcBufferGetUsage)(WGPUBuffer buffer) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuBufferMapAsync: + * > @copydoc wgpuBufferMapAsync + */ +typedef WGPUFuture (*WGPUProcBufferMapAsync)(WGPUBuffer buffer, WGPUMapMode mode, size_t offset, size_t size, WGPUBufferMapCallbackInfo callbackInfo) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuBufferSetLabel: + * > @copydoc wgpuBufferSetLabel + */ +typedef void (*WGPUProcBufferSetLabel)(WGPUBuffer buffer, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuBufferUnmap: + * > @copydoc wgpuBufferUnmap + */ typedef void (*WGPUProcBufferUnmap)(WGPUBuffer buffer) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcBufferReference)(WGPUBuffer buffer) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuBufferAddRef. + * > @copydoc wgpuBufferAddRef + */ +typedef void (*WGPUProcBufferAddRef)(WGPUBuffer buffer) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuBufferRelease. + * > @copydoc wgpuBufferRelease + */ typedef void (*WGPUProcBufferRelease)(WGPUBuffer buffer) WGPU_FUNCTION_ATTRIBUTE; // Procs of CommandBuffer -typedef void (*WGPUProcCommandBufferSetLabel)(WGPUCommandBuffer commandBuffer, char const * label) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcCommandBufferReference)(WGPUCommandBuffer commandBuffer) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuCommandBufferSetLabel: + * > @copydoc wgpuCommandBufferSetLabel + */ +typedef void (*WGPUProcCommandBufferSetLabel)(WGPUCommandBuffer commandBuffer, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuCommandBufferAddRef. + * > @copydoc wgpuCommandBufferAddRef + */ +typedef void (*WGPUProcCommandBufferAddRef)(WGPUCommandBuffer commandBuffer) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuCommandBufferRelease. + * > @copydoc wgpuCommandBufferRelease + */ typedef void (*WGPUProcCommandBufferRelease)(WGPUCommandBuffer commandBuffer) WGPU_FUNCTION_ATTRIBUTE; // Procs of CommandEncoder +/** + * Proc pointer type for @ref wgpuCommandEncoderBeginComputePass: + * > @copydoc wgpuCommandEncoderBeginComputePass + */ typedef WGPUComputePassEncoder (*WGPUProcCommandEncoderBeginComputePass)(WGPUCommandEncoder commandEncoder, WGPU_NULLABLE WGPUComputePassDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuCommandEncoderBeginRenderPass: + * > @copydoc wgpuCommandEncoderBeginRenderPass + */ typedef WGPURenderPassEncoder (*WGPUProcCommandEncoderBeginRenderPass)(WGPUCommandEncoder commandEncoder, WGPURenderPassDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuCommandEncoderClearBuffer: + * > @copydoc wgpuCommandEncoderClearBuffer + */ typedef void (*WGPUProcCommandEncoderClearBuffer)(WGPUCommandEncoder commandEncoder, WGPUBuffer buffer, uint64_t offset, uint64_t size) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuCommandEncoderCopyBufferToBuffer: + * > @copydoc wgpuCommandEncoderCopyBufferToBuffer + */ typedef void (*WGPUProcCommandEncoderCopyBufferToBuffer)(WGPUCommandEncoder commandEncoder, WGPUBuffer source, uint64_t sourceOffset, WGPUBuffer destination, uint64_t destinationOffset, uint64_t size) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcCommandEncoderCopyBufferToTexture)(WGPUCommandEncoder commandEncoder, WGPUImageCopyBuffer const * source, WGPUImageCopyTexture const * destination, WGPUExtent3D const * copySize) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcCommandEncoderCopyTextureToBuffer)(WGPUCommandEncoder commandEncoder, WGPUImageCopyTexture const * source, WGPUImageCopyBuffer const * destination, WGPUExtent3D const * copySize) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcCommandEncoderCopyTextureToTexture)(WGPUCommandEncoder commandEncoder, WGPUImageCopyTexture const * source, WGPUImageCopyTexture const * destination, WGPUExtent3D const * copySize) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuCommandEncoderCopyBufferToTexture: + * > @copydoc wgpuCommandEncoderCopyBufferToTexture + */ +typedef void (*WGPUProcCommandEncoderCopyBufferToTexture)(WGPUCommandEncoder commandEncoder, WGPUTexelCopyBufferInfo const * source, WGPUTexelCopyTextureInfo const * destination, WGPUExtent3D const * copySize) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuCommandEncoderCopyTextureToBuffer: + * > @copydoc wgpuCommandEncoderCopyTextureToBuffer + */ +typedef void (*WGPUProcCommandEncoderCopyTextureToBuffer)(WGPUCommandEncoder commandEncoder, WGPUTexelCopyTextureInfo const * source, WGPUTexelCopyBufferInfo const * destination, WGPUExtent3D const * copySize) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuCommandEncoderCopyTextureToTexture: + * > @copydoc wgpuCommandEncoderCopyTextureToTexture + */ +typedef void (*WGPUProcCommandEncoderCopyTextureToTexture)(WGPUCommandEncoder commandEncoder, WGPUTexelCopyTextureInfo const * source, WGPUTexelCopyTextureInfo const * destination, WGPUExtent3D const * copySize) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuCommandEncoderFinish: + * > @copydoc wgpuCommandEncoderFinish + */ typedef WGPUCommandBuffer (*WGPUProcCommandEncoderFinish)(WGPUCommandEncoder commandEncoder, WGPU_NULLABLE WGPUCommandBufferDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcCommandEncoderInsertDebugMarker)(WGPUCommandEncoder commandEncoder, char const * markerLabel) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuCommandEncoderInsertDebugMarker: + * > @copydoc wgpuCommandEncoderInsertDebugMarker + */ +typedef void (*WGPUProcCommandEncoderInsertDebugMarker)(WGPUCommandEncoder commandEncoder, WGPUStringView markerLabel) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuCommandEncoderPopDebugGroup: + * > @copydoc wgpuCommandEncoderPopDebugGroup + */ typedef void (*WGPUProcCommandEncoderPopDebugGroup)(WGPUCommandEncoder commandEncoder) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcCommandEncoderPushDebugGroup)(WGPUCommandEncoder commandEncoder, char const * groupLabel) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuCommandEncoderPushDebugGroup: + * > @copydoc wgpuCommandEncoderPushDebugGroup + */ +typedef void (*WGPUProcCommandEncoderPushDebugGroup)(WGPUCommandEncoder commandEncoder, WGPUStringView groupLabel) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuCommandEncoderResolveQuerySet: + * > @copydoc wgpuCommandEncoderResolveQuerySet + */ typedef void (*WGPUProcCommandEncoderResolveQuerySet)(WGPUCommandEncoder commandEncoder, WGPUQuerySet querySet, uint32_t firstQuery, uint32_t queryCount, WGPUBuffer destination, uint64_t destinationOffset) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcCommandEncoderSetLabel)(WGPUCommandEncoder commandEncoder, char const * label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuCommandEncoderSetLabel: + * > @copydoc wgpuCommandEncoderSetLabel + */ +typedef void (*WGPUProcCommandEncoderSetLabel)(WGPUCommandEncoder commandEncoder, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuCommandEncoderWriteTimestamp: + * > @copydoc wgpuCommandEncoderWriteTimestamp + */ typedef void (*WGPUProcCommandEncoderWriteTimestamp)(WGPUCommandEncoder commandEncoder, WGPUQuerySet querySet, uint32_t queryIndex) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcCommandEncoderReference)(WGPUCommandEncoder commandEncoder) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuCommandEncoderAddRef. + * > @copydoc wgpuCommandEncoderAddRef + */ +typedef void (*WGPUProcCommandEncoderAddRef)(WGPUCommandEncoder commandEncoder) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuCommandEncoderRelease. + * > @copydoc wgpuCommandEncoderRelease + */ typedef void (*WGPUProcCommandEncoderRelease)(WGPUCommandEncoder commandEncoder) WGPU_FUNCTION_ATTRIBUTE; // Procs of ComputePassEncoder +/** + * Proc pointer type for @ref wgpuComputePassEncoderDispatchWorkgroups: + * > @copydoc wgpuComputePassEncoderDispatchWorkgroups + */ typedef void (*WGPUProcComputePassEncoderDispatchWorkgroups)(WGPUComputePassEncoder computePassEncoder, uint32_t workgroupCountX, uint32_t workgroupCountY, uint32_t workgroupCountZ) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuComputePassEncoderDispatchWorkgroupsIndirect: + * > @copydoc wgpuComputePassEncoderDispatchWorkgroupsIndirect + */ typedef void (*WGPUProcComputePassEncoderDispatchWorkgroupsIndirect)(WGPUComputePassEncoder computePassEncoder, WGPUBuffer indirectBuffer, uint64_t indirectOffset) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuComputePassEncoderEnd: + * > @copydoc wgpuComputePassEncoderEnd + */ typedef void (*WGPUProcComputePassEncoderEnd)(WGPUComputePassEncoder computePassEncoder) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcComputePassEncoderInsertDebugMarker)(WGPUComputePassEncoder computePassEncoder, char const * markerLabel) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuComputePassEncoderInsertDebugMarker: + * > @copydoc wgpuComputePassEncoderInsertDebugMarker + */ +typedef void (*WGPUProcComputePassEncoderInsertDebugMarker)(WGPUComputePassEncoder computePassEncoder, WGPUStringView markerLabel) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuComputePassEncoderPopDebugGroup: + * > @copydoc wgpuComputePassEncoderPopDebugGroup + */ typedef void (*WGPUProcComputePassEncoderPopDebugGroup)(WGPUComputePassEncoder computePassEncoder) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcComputePassEncoderPushDebugGroup)(WGPUComputePassEncoder computePassEncoder, char const * groupLabel) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuComputePassEncoderPushDebugGroup: + * > @copydoc wgpuComputePassEncoderPushDebugGroup + */ +typedef void (*WGPUProcComputePassEncoderPushDebugGroup)(WGPUComputePassEncoder computePassEncoder, WGPUStringView groupLabel) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuComputePassEncoderSetBindGroup: + * > @copydoc wgpuComputePassEncoderSetBindGroup + */ typedef void (*WGPUProcComputePassEncoderSetBindGroup)(WGPUComputePassEncoder computePassEncoder, uint32_t groupIndex, WGPU_NULLABLE WGPUBindGroup group, size_t dynamicOffsetCount, uint32_t const * dynamicOffsets) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcComputePassEncoderSetLabel)(WGPUComputePassEncoder computePassEncoder, char const * label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuComputePassEncoderSetLabel: + * > @copydoc wgpuComputePassEncoderSetLabel + */ +typedef void (*WGPUProcComputePassEncoderSetLabel)(WGPUComputePassEncoder computePassEncoder, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuComputePassEncoderSetPipeline: + * > @copydoc wgpuComputePassEncoderSetPipeline + */ typedef void (*WGPUProcComputePassEncoderSetPipeline)(WGPUComputePassEncoder computePassEncoder, WGPUComputePipeline pipeline) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcComputePassEncoderReference)(WGPUComputePassEncoder computePassEncoder) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuComputePassEncoderAddRef. + * > @copydoc wgpuComputePassEncoderAddRef + */ +typedef void (*WGPUProcComputePassEncoderAddRef)(WGPUComputePassEncoder computePassEncoder) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuComputePassEncoderRelease. + * > @copydoc wgpuComputePassEncoderRelease + */ typedef void (*WGPUProcComputePassEncoderRelease)(WGPUComputePassEncoder computePassEncoder) WGPU_FUNCTION_ATTRIBUTE; // Procs of ComputePipeline +/** + * Proc pointer type for @ref wgpuComputePipelineGetBindGroupLayout: + * > @copydoc wgpuComputePipelineGetBindGroupLayout + */ typedef WGPUBindGroupLayout (*WGPUProcComputePipelineGetBindGroupLayout)(WGPUComputePipeline computePipeline, uint32_t groupIndex) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcComputePipelineSetLabel)(WGPUComputePipeline computePipeline, char const * label) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcComputePipelineReference)(WGPUComputePipeline computePipeline) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuComputePipelineSetLabel: + * > @copydoc wgpuComputePipelineSetLabel + */ +typedef void (*WGPUProcComputePipelineSetLabel)(WGPUComputePipeline computePipeline, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuComputePipelineAddRef. + * > @copydoc wgpuComputePipelineAddRef + */ +typedef void (*WGPUProcComputePipelineAddRef)(WGPUComputePipeline computePipeline) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuComputePipelineRelease. + * > @copydoc wgpuComputePipelineRelease + */ typedef void (*WGPUProcComputePipelineRelease)(WGPUComputePipeline computePipeline) WGPU_FUNCTION_ATTRIBUTE; // Procs of Device +/** + * Proc pointer type for @ref wgpuDeviceCreateBindGroup: + * > @copydoc wgpuDeviceCreateBindGroup + */ typedef WGPUBindGroup (*WGPUProcDeviceCreateBindGroup)(WGPUDevice device, WGPUBindGroupDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceCreateBindGroupLayout: + * > @copydoc wgpuDeviceCreateBindGroupLayout + */ typedef WGPUBindGroupLayout (*WGPUProcDeviceCreateBindGroupLayout)(WGPUDevice device, WGPUBindGroupLayoutDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceCreateBuffer: + * > @copydoc wgpuDeviceCreateBuffer + */ typedef WGPUBuffer (*WGPUProcDeviceCreateBuffer)(WGPUDevice device, WGPUBufferDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceCreateCommandEncoder: + * > @copydoc wgpuDeviceCreateCommandEncoder + */ typedef WGPUCommandEncoder (*WGPUProcDeviceCreateCommandEncoder)(WGPUDevice device, WGPU_NULLABLE WGPUCommandEncoderDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceCreateComputePipeline: + * > @copydoc wgpuDeviceCreateComputePipeline + */ typedef WGPUComputePipeline (*WGPUProcDeviceCreateComputePipeline)(WGPUDevice device, WGPUComputePipelineDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcDeviceCreateComputePipelineAsync)(WGPUDevice device, WGPUComputePipelineDescriptor const * descriptor, WGPUCreateComputePipelineAsyncCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceCreateComputePipelineAsync: + * > @copydoc wgpuDeviceCreateComputePipelineAsync + */ +typedef WGPUFuture (*WGPUProcDeviceCreateComputePipelineAsync)(WGPUDevice device, WGPUComputePipelineDescriptor const * descriptor, WGPUCreateComputePipelineAsyncCallbackInfo callbackInfo) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceCreatePipelineLayout: + * > @copydoc wgpuDeviceCreatePipelineLayout + */ typedef WGPUPipelineLayout (*WGPUProcDeviceCreatePipelineLayout)(WGPUDevice device, WGPUPipelineLayoutDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceCreateQuerySet: + * > @copydoc wgpuDeviceCreateQuerySet + */ typedef WGPUQuerySet (*WGPUProcDeviceCreateQuerySet)(WGPUDevice device, WGPUQuerySetDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceCreateRenderBundleEncoder: + * > @copydoc wgpuDeviceCreateRenderBundleEncoder + */ typedef WGPURenderBundleEncoder (*WGPUProcDeviceCreateRenderBundleEncoder)(WGPUDevice device, WGPURenderBundleEncoderDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceCreateRenderPipeline: + * > @copydoc wgpuDeviceCreateRenderPipeline + */ typedef WGPURenderPipeline (*WGPUProcDeviceCreateRenderPipeline)(WGPUDevice device, WGPURenderPipelineDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcDeviceCreateRenderPipelineAsync)(WGPUDevice device, WGPURenderPipelineDescriptor const * descriptor, WGPUCreateRenderPipelineAsyncCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceCreateRenderPipelineAsync: + * > @copydoc wgpuDeviceCreateRenderPipelineAsync + */ +typedef WGPUFuture (*WGPUProcDeviceCreateRenderPipelineAsync)(WGPUDevice device, WGPURenderPipelineDescriptor const * descriptor, WGPUCreateRenderPipelineAsyncCallbackInfo callbackInfo) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceCreateSampler: + * > @copydoc wgpuDeviceCreateSampler + */ typedef WGPUSampler (*WGPUProcDeviceCreateSampler)(WGPUDevice device, WGPU_NULLABLE WGPUSamplerDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceCreateShaderModule: + * > @copydoc wgpuDeviceCreateShaderModule + */ typedef WGPUShaderModule (*WGPUProcDeviceCreateShaderModule)(WGPUDevice device, WGPUShaderModuleDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceCreateTexture: + * > @copydoc wgpuDeviceCreateTexture + */ typedef WGPUTexture (*WGPUProcDeviceCreateTexture)(WGPUDevice device, WGPUTextureDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceDestroy: + * > @copydoc wgpuDeviceDestroy + */ typedef void (*WGPUProcDeviceDestroy)(WGPUDevice device) WGPU_FUNCTION_ATTRIBUTE; -typedef size_t (*WGPUProcDeviceEnumerateFeatures)(WGPUDevice device, WGPUFeatureName * features) WGPU_FUNCTION_ATTRIBUTE; -typedef WGPUBool (*WGPUProcDeviceGetLimits)(WGPUDevice device, WGPUSupportedLimits * limits) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceGetAdapterInfo: + * > @copydoc wgpuDeviceGetAdapterInfo + */ +typedef WGPUAdapterInfo (*WGPUProcDeviceGetAdapterInfo)(WGPUDevice device) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceGetFeatures: + * > @copydoc wgpuDeviceGetFeatures + */ +typedef void (*WGPUProcDeviceGetFeatures)(WGPUDevice device, WGPUSupportedFeatures * features) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceGetLimits: + * > @copydoc wgpuDeviceGetLimits + */ +typedef WGPUStatus (*WGPUProcDeviceGetLimits)(WGPUDevice device, WGPULimits * limits) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceGetLostFuture: + * > @copydoc wgpuDeviceGetLostFuture + */ +typedef WGPUFuture (*WGPUProcDeviceGetLostFuture)(WGPUDevice device) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceGetQueue: + * > @copydoc wgpuDeviceGetQueue + */ typedef WGPUQueue (*WGPUProcDeviceGetQueue)(WGPUDevice device) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceHasFeature: + * > @copydoc wgpuDeviceHasFeature + */ typedef WGPUBool (*WGPUProcDeviceHasFeature)(WGPUDevice device, WGPUFeatureName feature) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcDevicePopErrorScope)(WGPUDevice device, WGPUErrorCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDevicePopErrorScope: + * > @copydoc wgpuDevicePopErrorScope + */ +typedef WGPUFuture (*WGPUProcDevicePopErrorScope)(WGPUDevice device, WGPUPopErrorScopeCallbackInfo callbackInfo) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDevicePushErrorScope: + * > @copydoc wgpuDevicePushErrorScope + */ typedef void (*WGPUProcDevicePushErrorScope)(WGPUDevice device, WGPUErrorFilter filter) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcDeviceSetLabel)(WGPUDevice device, char const * label) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcDeviceSetUncapturedErrorCallback)(WGPUDevice device, WGPUErrorCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcDeviceReference)(WGPUDevice device) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceSetLabel: + * > @copydoc wgpuDeviceSetLabel + */ +typedef void (*WGPUProcDeviceSetLabel)(WGPUDevice device, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceAddRef. + * > @copydoc wgpuDeviceAddRef + */ +typedef void (*WGPUProcDeviceAddRef)(WGPUDevice device) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuDeviceRelease. + * > @copydoc wgpuDeviceRelease + */ typedef void (*WGPUProcDeviceRelease)(WGPUDevice device) WGPU_FUNCTION_ATTRIBUTE; // Procs of Instance +/** + * Proc pointer type for @ref wgpuInstanceCreateSurface: + * > @copydoc wgpuInstanceCreateSurface + */ typedef WGPUSurface (*WGPUProcInstanceCreateSurface)(WGPUInstance instance, WGPUSurfaceDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuInstanceGetWGSLLanguageFeatures: + * > @copydoc wgpuInstanceGetWGSLLanguageFeatures + */ +typedef WGPUStatus (*WGPUProcInstanceGetWGSLLanguageFeatures)(WGPUInstance instance, WGPUSupportedWGSLLanguageFeatures * features) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuInstanceHasWGSLLanguageFeature: + * > @copydoc wgpuInstanceHasWGSLLanguageFeature + */ +typedef WGPUBool (*WGPUProcInstanceHasWGSLLanguageFeature)(WGPUInstance instance, WGPUWGSLLanguageFeatureName feature) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuInstanceProcessEvents: + * > @copydoc wgpuInstanceProcessEvents + */ typedef void (*WGPUProcInstanceProcessEvents)(WGPUInstance instance) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcInstanceRequestAdapter)(WGPUInstance instance, WGPU_NULLABLE WGPURequestAdapterOptions const * options, WGPURequestAdapterCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcInstanceReference)(WGPUInstance instance) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuInstanceRequestAdapter: + * > @copydoc wgpuInstanceRequestAdapter + */ +typedef WGPUFuture (*WGPUProcInstanceRequestAdapter)(WGPUInstance instance, WGPU_NULLABLE WGPURequestAdapterOptions const * options, WGPURequestAdapterCallbackInfo callbackInfo) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuInstanceWaitAny: + * > @copydoc wgpuInstanceWaitAny + */ +typedef WGPUWaitStatus (*WGPUProcInstanceWaitAny)(WGPUInstance instance, size_t futureCount, WGPU_NULLABLE WGPUFutureWaitInfo * futures, uint64_t timeoutNS) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuInstanceAddRef. + * > @copydoc wgpuInstanceAddRef + */ +typedef void (*WGPUProcInstanceAddRef)(WGPUInstance instance) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuInstanceRelease. + * > @copydoc wgpuInstanceRelease + */ typedef void (*WGPUProcInstanceRelease)(WGPUInstance instance) WGPU_FUNCTION_ATTRIBUTE; // Procs of PipelineLayout -typedef void (*WGPUProcPipelineLayoutSetLabel)(WGPUPipelineLayout pipelineLayout, char const * label) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcPipelineLayoutReference)(WGPUPipelineLayout pipelineLayout) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuPipelineLayoutSetLabel: + * > @copydoc wgpuPipelineLayoutSetLabel + */ +typedef void (*WGPUProcPipelineLayoutSetLabel)(WGPUPipelineLayout pipelineLayout, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuPipelineLayoutAddRef. + * > @copydoc wgpuPipelineLayoutAddRef + */ +typedef void (*WGPUProcPipelineLayoutAddRef)(WGPUPipelineLayout pipelineLayout) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuPipelineLayoutRelease. + * > @copydoc wgpuPipelineLayoutRelease + */ typedef void (*WGPUProcPipelineLayoutRelease)(WGPUPipelineLayout pipelineLayout) WGPU_FUNCTION_ATTRIBUTE; // Procs of QuerySet +/** + * Proc pointer type for @ref wgpuQuerySetDestroy: + * > @copydoc wgpuQuerySetDestroy + */ typedef void (*WGPUProcQuerySetDestroy)(WGPUQuerySet querySet) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuQuerySetGetCount: + * > @copydoc wgpuQuerySetGetCount + */ typedef uint32_t (*WGPUProcQuerySetGetCount)(WGPUQuerySet querySet) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuQuerySetGetType: + * > @copydoc wgpuQuerySetGetType + */ typedef WGPUQueryType (*WGPUProcQuerySetGetType)(WGPUQuerySet querySet) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcQuerySetSetLabel)(WGPUQuerySet querySet, char const * label) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcQuerySetReference)(WGPUQuerySet querySet) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuQuerySetSetLabel: + * > @copydoc wgpuQuerySetSetLabel + */ +typedef void (*WGPUProcQuerySetSetLabel)(WGPUQuerySet querySet, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuQuerySetAddRef. + * > @copydoc wgpuQuerySetAddRef + */ +typedef void (*WGPUProcQuerySetAddRef)(WGPUQuerySet querySet) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuQuerySetRelease. + * > @copydoc wgpuQuerySetRelease + */ typedef void (*WGPUProcQuerySetRelease)(WGPUQuerySet querySet) WGPU_FUNCTION_ATTRIBUTE; // Procs of Queue -typedef void (*WGPUProcQueueOnSubmittedWorkDone)(WGPUQueue queue, WGPUQueueWorkDoneCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcQueueSetLabel)(WGPUQueue queue, char const * label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuQueueOnSubmittedWorkDone: + * > @copydoc wgpuQueueOnSubmittedWorkDone + */ +typedef WGPUFuture (*WGPUProcQueueOnSubmittedWorkDone)(WGPUQueue queue, WGPUQueueWorkDoneCallbackInfo callbackInfo) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuQueueSetLabel: + * > @copydoc wgpuQueueSetLabel + */ +typedef void (*WGPUProcQueueSetLabel)(WGPUQueue queue, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuQueueSubmit: + * > @copydoc wgpuQueueSubmit + */ typedef void (*WGPUProcQueueSubmit)(WGPUQueue queue, size_t commandCount, WGPUCommandBuffer const * commands) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuQueueWriteBuffer: + * > @copydoc wgpuQueueWriteBuffer + */ typedef void (*WGPUProcQueueWriteBuffer)(WGPUQueue queue, WGPUBuffer buffer, uint64_t bufferOffset, void const * data, size_t size) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcQueueWriteTexture)(WGPUQueue queue, WGPUImageCopyTexture const * destination, void const * data, size_t dataSize, WGPUTextureDataLayout const * dataLayout, WGPUExtent3D const * writeSize) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcQueueReference)(WGPUQueue queue) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuQueueWriteTexture: + * > @copydoc wgpuQueueWriteTexture + */ +typedef void (*WGPUProcQueueWriteTexture)(WGPUQueue queue, WGPUTexelCopyTextureInfo const * destination, void const * data, size_t dataSize, WGPUTexelCopyBufferLayout const * dataLayout, WGPUExtent3D const * writeSize) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuQueueAddRef. + * > @copydoc wgpuQueueAddRef + */ +typedef void (*WGPUProcQueueAddRef)(WGPUQueue queue) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuQueueRelease. + * > @copydoc wgpuQueueRelease + */ typedef void (*WGPUProcQueueRelease)(WGPUQueue queue) WGPU_FUNCTION_ATTRIBUTE; // Procs of RenderBundle -typedef void (*WGPUProcRenderBundleSetLabel)(WGPURenderBundle renderBundle, char const * label) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcRenderBundleReference)(WGPURenderBundle renderBundle) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderBundleSetLabel: + * > @copydoc wgpuRenderBundleSetLabel + */ +typedef void (*WGPUProcRenderBundleSetLabel)(WGPURenderBundle renderBundle, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderBundleAddRef. + * > @copydoc wgpuRenderBundleAddRef + */ +typedef void (*WGPUProcRenderBundleAddRef)(WGPURenderBundle renderBundle) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderBundleRelease. + * > @copydoc wgpuRenderBundleRelease + */ typedef void (*WGPUProcRenderBundleRelease)(WGPURenderBundle renderBundle) WGPU_FUNCTION_ATTRIBUTE; // Procs of RenderBundleEncoder +/** + * Proc pointer type for @ref wgpuRenderBundleEncoderDraw: + * > @copydoc wgpuRenderBundleEncoderDraw + */ typedef void (*WGPUProcRenderBundleEncoderDraw)(WGPURenderBundleEncoder renderBundleEncoder, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderBundleEncoderDrawIndexed: + * > @copydoc wgpuRenderBundleEncoderDrawIndexed + */ typedef void (*WGPUProcRenderBundleEncoderDrawIndexed)(WGPURenderBundleEncoder renderBundleEncoder, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t baseVertex, uint32_t firstInstance) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderBundleEncoderDrawIndexedIndirect: + * > @copydoc wgpuRenderBundleEncoderDrawIndexedIndirect + */ typedef void (*WGPUProcRenderBundleEncoderDrawIndexedIndirect)(WGPURenderBundleEncoder renderBundleEncoder, WGPUBuffer indirectBuffer, uint64_t indirectOffset) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderBundleEncoderDrawIndirect: + * > @copydoc wgpuRenderBundleEncoderDrawIndirect + */ typedef void (*WGPUProcRenderBundleEncoderDrawIndirect)(WGPURenderBundleEncoder renderBundleEncoder, WGPUBuffer indirectBuffer, uint64_t indirectOffset) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderBundleEncoderFinish: + * > @copydoc wgpuRenderBundleEncoderFinish + */ typedef WGPURenderBundle (*WGPUProcRenderBundleEncoderFinish)(WGPURenderBundleEncoder renderBundleEncoder, WGPU_NULLABLE WGPURenderBundleDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcRenderBundleEncoderInsertDebugMarker)(WGPURenderBundleEncoder renderBundleEncoder, char const * markerLabel) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderBundleEncoderInsertDebugMarker: + * > @copydoc wgpuRenderBundleEncoderInsertDebugMarker + */ +typedef void (*WGPUProcRenderBundleEncoderInsertDebugMarker)(WGPURenderBundleEncoder renderBundleEncoder, WGPUStringView markerLabel) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderBundleEncoderPopDebugGroup: + * > @copydoc wgpuRenderBundleEncoderPopDebugGroup + */ typedef void (*WGPUProcRenderBundleEncoderPopDebugGroup)(WGPURenderBundleEncoder renderBundleEncoder) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcRenderBundleEncoderPushDebugGroup)(WGPURenderBundleEncoder renderBundleEncoder, char const * groupLabel) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderBundleEncoderPushDebugGroup: + * > @copydoc wgpuRenderBundleEncoderPushDebugGroup + */ +typedef void (*WGPUProcRenderBundleEncoderPushDebugGroup)(WGPURenderBundleEncoder renderBundleEncoder, WGPUStringView groupLabel) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderBundleEncoderSetBindGroup: + * > @copydoc wgpuRenderBundleEncoderSetBindGroup + */ typedef void (*WGPUProcRenderBundleEncoderSetBindGroup)(WGPURenderBundleEncoder renderBundleEncoder, uint32_t groupIndex, WGPU_NULLABLE WGPUBindGroup group, size_t dynamicOffsetCount, uint32_t const * dynamicOffsets) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderBundleEncoderSetIndexBuffer: + * > @copydoc wgpuRenderBundleEncoderSetIndexBuffer + */ typedef void (*WGPUProcRenderBundleEncoderSetIndexBuffer)(WGPURenderBundleEncoder renderBundleEncoder, WGPUBuffer buffer, WGPUIndexFormat format, uint64_t offset, uint64_t size) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcRenderBundleEncoderSetLabel)(WGPURenderBundleEncoder renderBundleEncoder, char const * label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderBundleEncoderSetLabel: + * > @copydoc wgpuRenderBundleEncoderSetLabel + */ +typedef void (*WGPUProcRenderBundleEncoderSetLabel)(WGPURenderBundleEncoder renderBundleEncoder, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderBundleEncoderSetPipeline: + * > @copydoc wgpuRenderBundleEncoderSetPipeline + */ typedef void (*WGPUProcRenderBundleEncoderSetPipeline)(WGPURenderBundleEncoder renderBundleEncoder, WGPURenderPipeline pipeline) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderBundleEncoderSetVertexBuffer: + * > @copydoc wgpuRenderBundleEncoderSetVertexBuffer + */ typedef void (*WGPUProcRenderBundleEncoderSetVertexBuffer)(WGPURenderBundleEncoder renderBundleEncoder, uint32_t slot, WGPU_NULLABLE WGPUBuffer buffer, uint64_t offset, uint64_t size) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcRenderBundleEncoderReference)(WGPURenderBundleEncoder renderBundleEncoder) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderBundleEncoderAddRef. + * > @copydoc wgpuRenderBundleEncoderAddRef + */ +typedef void (*WGPUProcRenderBundleEncoderAddRef)(WGPURenderBundleEncoder renderBundleEncoder) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderBundleEncoderRelease. + * > @copydoc wgpuRenderBundleEncoderRelease + */ typedef void (*WGPUProcRenderBundleEncoderRelease)(WGPURenderBundleEncoder renderBundleEncoder) WGPU_FUNCTION_ATTRIBUTE; // Procs of RenderPassEncoder +/** + * Proc pointer type for @ref wgpuRenderPassEncoderBeginOcclusionQuery: + * > @copydoc wgpuRenderPassEncoderBeginOcclusionQuery + */ typedef void (*WGPUProcRenderPassEncoderBeginOcclusionQuery)(WGPURenderPassEncoder renderPassEncoder, uint32_t queryIndex) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderDraw: + * > @copydoc wgpuRenderPassEncoderDraw + */ typedef void (*WGPUProcRenderPassEncoderDraw)(WGPURenderPassEncoder renderPassEncoder, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderDrawIndexed: + * > @copydoc wgpuRenderPassEncoderDrawIndexed + */ typedef void (*WGPUProcRenderPassEncoderDrawIndexed)(WGPURenderPassEncoder renderPassEncoder, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t baseVertex, uint32_t firstInstance) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderDrawIndexedIndirect: + * > @copydoc wgpuRenderPassEncoderDrawIndexedIndirect + */ typedef void (*WGPUProcRenderPassEncoderDrawIndexedIndirect)(WGPURenderPassEncoder renderPassEncoder, WGPUBuffer indirectBuffer, uint64_t indirectOffset) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderDrawIndirect: + * > @copydoc wgpuRenderPassEncoderDrawIndirect + */ typedef void (*WGPUProcRenderPassEncoderDrawIndirect)(WGPURenderPassEncoder renderPassEncoder, WGPUBuffer indirectBuffer, uint64_t indirectOffset) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderEnd: + * > @copydoc wgpuRenderPassEncoderEnd + */ typedef void (*WGPUProcRenderPassEncoderEnd)(WGPURenderPassEncoder renderPassEncoder) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderEndOcclusionQuery: + * > @copydoc wgpuRenderPassEncoderEndOcclusionQuery + */ typedef void (*WGPUProcRenderPassEncoderEndOcclusionQuery)(WGPURenderPassEncoder renderPassEncoder) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderExecuteBundles: + * > @copydoc wgpuRenderPassEncoderExecuteBundles + */ typedef void (*WGPUProcRenderPassEncoderExecuteBundles)(WGPURenderPassEncoder renderPassEncoder, size_t bundleCount, WGPURenderBundle const * bundles) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcRenderPassEncoderInsertDebugMarker)(WGPURenderPassEncoder renderPassEncoder, char const * markerLabel) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderInsertDebugMarker: + * > @copydoc wgpuRenderPassEncoderInsertDebugMarker + */ +typedef void (*WGPUProcRenderPassEncoderInsertDebugMarker)(WGPURenderPassEncoder renderPassEncoder, WGPUStringView markerLabel) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderPopDebugGroup: + * > @copydoc wgpuRenderPassEncoderPopDebugGroup + */ typedef void (*WGPUProcRenderPassEncoderPopDebugGroup)(WGPURenderPassEncoder renderPassEncoder) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcRenderPassEncoderPushDebugGroup)(WGPURenderPassEncoder renderPassEncoder, char const * groupLabel) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderPushDebugGroup: + * > @copydoc wgpuRenderPassEncoderPushDebugGroup + */ +typedef void (*WGPUProcRenderPassEncoderPushDebugGroup)(WGPURenderPassEncoder renderPassEncoder, WGPUStringView groupLabel) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderSetBindGroup: + * > @copydoc wgpuRenderPassEncoderSetBindGroup + */ typedef void (*WGPUProcRenderPassEncoderSetBindGroup)(WGPURenderPassEncoder renderPassEncoder, uint32_t groupIndex, WGPU_NULLABLE WGPUBindGroup group, size_t dynamicOffsetCount, uint32_t const * dynamicOffsets) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderSetBlendConstant: + * > @copydoc wgpuRenderPassEncoderSetBlendConstant + */ typedef void (*WGPUProcRenderPassEncoderSetBlendConstant)(WGPURenderPassEncoder renderPassEncoder, WGPUColor const * color) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderSetIndexBuffer: + * > @copydoc wgpuRenderPassEncoderSetIndexBuffer + */ typedef void (*WGPUProcRenderPassEncoderSetIndexBuffer)(WGPURenderPassEncoder renderPassEncoder, WGPUBuffer buffer, WGPUIndexFormat format, uint64_t offset, uint64_t size) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcRenderPassEncoderSetLabel)(WGPURenderPassEncoder renderPassEncoder, char const * label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderSetLabel: + * > @copydoc wgpuRenderPassEncoderSetLabel + */ +typedef void (*WGPUProcRenderPassEncoderSetLabel)(WGPURenderPassEncoder renderPassEncoder, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderSetPipeline: + * > @copydoc wgpuRenderPassEncoderSetPipeline + */ typedef void (*WGPUProcRenderPassEncoderSetPipeline)(WGPURenderPassEncoder renderPassEncoder, WGPURenderPipeline pipeline) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderSetScissorRect: + * > @copydoc wgpuRenderPassEncoderSetScissorRect + */ typedef void (*WGPUProcRenderPassEncoderSetScissorRect)(WGPURenderPassEncoder renderPassEncoder, uint32_t x, uint32_t y, uint32_t width, uint32_t height) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderSetStencilReference: + * > @copydoc wgpuRenderPassEncoderSetStencilReference + */ typedef void (*WGPUProcRenderPassEncoderSetStencilReference)(WGPURenderPassEncoder renderPassEncoder, uint32_t reference) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderSetVertexBuffer: + * > @copydoc wgpuRenderPassEncoderSetVertexBuffer + */ typedef void (*WGPUProcRenderPassEncoderSetVertexBuffer)(WGPURenderPassEncoder renderPassEncoder, uint32_t slot, WGPU_NULLABLE WGPUBuffer buffer, uint64_t offset, uint64_t size) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderSetViewport: + * > @copydoc wgpuRenderPassEncoderSetViewport + */ typedef void (*WGPUProcRenderPassEncoderSetViewport)(WGPURenderPassEncoder renderPassEncoder, float x, float y, float width, float height, float minDepth, float maxDepth) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcRenderPassEncoderReference)(WGPURenderPassEncoder renderPassEncoder) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderAddRef. + * > @copydoc wgpuRenderPassEncoderAddRef + */ +typedef void (*WGPUProcRenderPassEncoderAddRef)(WGPURenderPassEncoder renderPassEncoder) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPassEncoderRelease. + * > @copydoc wgpuRenderPassEncoderRelease + */ typedef void (*WGPUProcRenderPassEncoderRelease)(WGPURenderPassEncoder renderPassEncoder) WGPU_FUNCTION_ATTRIBUTE; // Procs of RenderPipeline +/** + * Proc pointer type for @ref wgpuRenderPipelineGetBindGroupLayout: + * > @copydoc wgpuRenderPipelineGetBindGroupLayout + */ typedef WGPUBindGroupLayout (*WGPUProcRenderPipelineGetBindGroupLayout)(WGPURenderPipeline renderPipeline, uint32_t groupIndex) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcRenderPipelineSetLabel)(WGPURenderPipeline renderPipeline, char const * label) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcRenderPipelineReference)(WGPURenderPipeline renderPipeline) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPipelineSetLabel: + * > @copydoc wgpuRenderPipelineSetLabel + */ +typedef void (*WGPUProcRenderPipelineSetLabel)(WGPURenderPipeline renderPipeline, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPipelineAddRef. + * > @copydoc wgpuRenderPipelineAddRef + */ +typedef void (*WGPUProcRenderPipelineAddRef)(WGPURenderPipeline renderPipeline) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuRenderPipelineRelease. + * > @copydoc wgpuRenderPipelineRelease + */ typedef void (*WGPUProcRenderPipelineRelease)(WGPURenderPipeline renderPipeline) WGPU_FUNCTION_ATTRIBUTE; // Procs of Sampler -typedef void (*WGPUProcSamplerSetLabel)(WGPUSampler sampler, char const * label) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcSamplerReference)(WGPUSampler sampler) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuSamplerSetLabel: + * > @copydoc wgpuSamplerSetLabel + */ +typedef void (*WGPUProcSamplerSetLabel)(WGPUSampler sampler, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuSamplerAddRef. + * > @copydoc wgpuSamplerAddRef + */ +typedef void (*WGPUProcSamplerAddRef)(WGPUSampler sampler) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuSamplerRelease. + * > @copydoc wgpuSamplerRelease + */ typedef void (*WGPUProcSamplerRelease)(WGPUSampler sampler) WGPU_FUNCTION_ATTRIBUTE; // Procs of ShaderModule -typedef void (*WGPUProcShaderModuleGetCompilationInfo)(WGPUShaderModule shaderModule, WGPUCompilationInfoCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcShaderModuleSetLabel)(WGPUShaderModule shaderModule, char const * label) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcShaderModuleReference)(WGPUShaderModule shaderModule) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuShaderModuleGetCompilationInfo: + * > @copydoc wgpuShaderModuleGetCompilationInfo + */ +typedef WGPUFuture (*WGPUProcShaderModuleGetCompilationInfo)(WGPUShaderModule shaderModule, WGPUCompilationInfoCallbackInfo callbackInfo) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuShaderModuleSetLabel: + * > @copydoc wgpuShaderModuleSetLabel + */ +typedef void (*WGPUProcShaderModuleSetLabel)(WGPUShaderModule shaderModule, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuShaderModuleAddRef. + * > @copydoc wgpuShaderModuleAddRef + */ +typedef void (*WGPUProcShaderModuleAddRef)(WGPUShaderModule shaderModule) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuShaderModuleRelease. + * > @copydoc wgpuShaderModuleRelease + */ typedef void (*WGPUProcShaderModuleRelease)(WGPUShaderModule shaderModule) WGPU_FUNCTION_ATTRIBUTE; +// Procs of SupportedFeatures +/** + * Proc pointer type for @ref wgpuSupportedFeaturesFreeMembers: + * > @copydoc wgpuSupportedFeaturesFreeMembers + */ +typedef void (*WGPUProcSupportedFeaturesFreeMembers)(WGPUSupportedFeatures supportedFeatures) WGPU_FUNCTION_ATTRIBUTE; + +// Procs of SupportedWGSLLanguageFeatures +/** + * Proc pointer type for @ref wgpuSupportedWGSLLanguageFeaturesFreeMembers: + * > @copydoc wgpuSupportedWGSLLanguageFeaturesFreeMembers + */ +typedef void (*WGPUProcSupportedWGSLLanguageFeaturesFreeMembers)(WGPUSupportedWGSLLanguageFeatures supportedWGSLLanguageFeatures) WGPU_FUNCTION_ATTRIBUTE; + // Procs of Surface +/** + * Proc pointer type for @ref wgpuSurfaceConfigure: + * > @copydoc wgpuSurfaceConfigure + */ typedef void (*WGPUProcSurfaceConfigure)(WGPUSurface surface, WGPUSurfaceConfiguration const * config) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcSurfaceGetCapabilities)(WGPUSurface surface, WGPUAdapter adapter, WGPUSurfaceCapabilities * capabilities) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuSurfaceGetCapabilities: + * > @copydoc wgpuSurfaceGetCapabilities + */ +typedef WGPUStatus (*WGPUProcSurfaceGetCapabilities)(WGPUSurface surface, WGPUAdapter adapter, WGPUSurfaceCapabilities * capabilities) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuSurfaceGetCurrentTexture: + * > @copydoc wgpuSurfaceGetCurrentTexture + */ typedef void (*WGPUProcSurfaceGetCurrentTexture)(WGPUSurface surface, WGPUSurfaceTexture * surfaceTexture) WGPU_FUNCTION_ATTRIBUTE; -typedef WGPUTextureFormat (*WGPUProcSurfaceGetPreferredFormat)(WGPUSurface surface, WGPUAdapter adapter) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcSurfacePresent)(WGPUSurface surface) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuSurfacePresent: + * > @copydoc wgpuSurfacePresent + */ +typedef WGPUStatus (*WGPUProcSurfacePresent)(WGPUSurface surface) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuSurfaceSetLabel: + * > @copydoc wgpuSurfaceSetLabel + */ +typedef void (*WGPUProcSurfaceSetLabel)(WGPUSurface surface, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuSurfaceUnconfigure: + * > @copydoc wgpuSurfaceUnconfigure + */ typedef void (*WGPUProcSurfaceUnconfigure)(WGPUSurface surface) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcSurfaceReference)(WGPUSurface surface) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuSurfaceAddRef. + * > @copydoc wgpuSurfaceAddRef + */ +typedef void (*WGPUProcSurfaceAddRef)(WGPUSurface surface) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuSurfaceRelease. + * > @copydoc wgpuSurfaceRelease + */ typedef void (*WGPUProcSurfaceRelease)(WGPUSurface surface) WGPU_FUNCTION_ATTRIBUTE; // Procs of SurfaceCapabilities -typedef void (*WGPUProcSurfaceCapabilitiesFreeMembers)(WGPUSurfaceCapabilities capabilities) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuSurfaceCapabilitiesFreeMembers: + * > @copydoc wgpuSurfaceCapabilitiesFreeMembers + */ +typedef void (*WGPUProcSurfaceCapabilitiesFreeMembers)(WGPUSurfaceCapabilities surfaceCapabilities) WGPU_FUNCTION_ATTRIBUTE; // Procs of Texture +/** + * Proc pointer type for @ref wgpuTextureCreateView: + * > @copydoc wgpuTextureCreateView + */ typedef WGPUTextureView (*WGPUProcTextureCreateView)(WGPUTexture texture, WGPU_NULLABLE WGPUTextureViewDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuTextureDestroy: + * > @copydoc wgpuTextureDestroy + */ typedef void (*WGPUProcTextureDestroy)(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuTextureGetDepthOrArrayLayers: + * > @copydoc wgpuTextureGetDepthOrArrayLayers + */ typedef uint32_t (*WGPUProcTextureGetDepthOrArrayLayers)(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuTextureGetDimension: + * > @copydoc wgpuTextureGetDimension + */ typedef WGPUTextureDimension (*WGPUProcTextureGetDimension)(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuTextureGetFormat: + * > @copydoc wgpuTextureGetFormat + */ typedef WGPUTextureFormat (*WGPUProcTextureGetFormat)(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuTextureGetHeight: + * > @copydoc wgpuTextureGetHeight + */ typedef uint32_t (*WGPUProcTextureGetHeight)(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuTextureGetMipLevelCount: + * > @copydoc wgpuTextureGetMipLevelCount + */ typedef uint32_t (*WGPUProcTextureGetMipLevelCount)(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuTextureGetSampleCount: + * > @copydoc wgpuTextureGetSampleCount + */ typedef uint32_t (*WGPUProcTextureGetSampleCount)(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; -typedef WGPUTextureUsageFlags (*WGPUProcTextureGetUsage)(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuTextureGetUsage: + * > @copydoc wgpuTextureGetUsage + */ +typedef WGPUTextureUsage (*WGPUProcTextureGetUsage)(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuTextureGetWidth: + * > @copydoc wgpuTextureGetWidth + */ typedef uint32_t (*WGPUProcTextureGetWidth)(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcTextureSetLabel)(WGPUTexture texture, char const * label) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcTextureReference)(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuTextureSetLabel: + * > @copydoc wgpuTextureSetLabel + */ +typedef void (*WGPUProcTextureSetLabel)(WGPUTexture texture, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuTextureAddRef. + * > @copydoc wgpuTextureAddRef + */ +typedef void (*WGPUProcTextureAddRef)(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuTextureRelease. + * > @copydoc wgpuTextureRelease + */ typedef void (*WGPUProcTextureRelease)(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; // Procs of TextureView -typedef void (*WGPUProcTextureViewSetLabel)(WGPUTextureView textureView, char const * label) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcTextureViewReference)(WGPUTextureView textureView) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuTextureViewSetLabel: + * > @copydoc wgpuTextureViewSetLabel + */ +typedef void (*WGPUProcTextureViewSetLabel)(WGPUTextureView textureView, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuTextureViewAddRef. + * > @copydoc wgpuTextureViewAddRef + */ +typedef void (*WGPUProcTextureViewAddRef)(WGPUTextureView textureView) WGPU_FUNCTION_ATTRIBUTE; +/** + * Proc pointer type for @ref wgpuTextureViewRelease. + * > @copydoc wgpuTextureViewRelease + */ typedef void (*WGPUProcTextureViewRelease)(WGPUTextureView textureView) WGPU_FUNCTION_ATTRIBUTE; #endif // !defined(WGPU_SKIP_PROCS) #if !defined(WGPU_SKIP_DECLARATIONS) - +/** + * \defgroup GlobalFunctions Global Functions + * \brief Functions that are not specific to an object. + * + * @{ + */ +/** + * Create a WGPUInstance + */ WGPU_EXPORT WGPUInstance wgpuCreateInstance(WGPU_NULLABLE WGPUInstanceDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT WGPUProc wgpuGetProcAddress(WGPUDevice device, char const * procName) WGPU_FUNCTION_ATTRIBUTE; +/** + * Query the supported instance capabilities. + * + * @param capabilities + * The supported instance capabilities + * + * @returns + * Indicates if there was an @ref OutStructChainError. + */ +WGPU_EXPORT WGPUStatus wgpuGetInstanceCapabilities(WGPUInstanceCapabilities * capabilities) WGPU_FUNCTION_ATTRIBUTE; +/** + * Returns the "procedure address" (function pointer) of the named function. + * The result must be cast to the appropriate proc pointer type. + */ +WGPU_EXPORT WGPUProc wgpuGetProcAddress(WGPUStringView procName) WGPU_FUNCTION_ATTRIBUTE; -// Methods of Adapter -WGPU_EXPORT size_t wgpuAdapterEnumerateFeatures(WGPUAdapter adapter, WGPUFeatureName * features) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT WGPUBool wgpuAdapterGetLimits(WGPUAdapter adapter, WGPUSupportedLimits * limits) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuAdapterGetProperties(WGPUAdapter adapter, WGPUAdapterProperties * properties) WGPU_FUNCTION_ATTRIBUTE; + +/** @} */ + +/** + * \defgroup Methods + * \brief Functions that are relative to a specific object. + * + * @{ + */ + +/** + * \defgroup WGPUAdapterMethods WGPUAdapter methods + * \brief Functions whose first argument has type WGPUAdapter. + * + * @{ + */ +/** + * Get the list of @ref WGPUFeatureName values supported by the adapter. + * + * @param features + * This parameter is @ref ReturnedWithOwnership. + */ +WGPU_EXPORT void wgpuAdapterGetFeatures(WGPUAdapter adapter, WGPUSupportedFeatures * features) WGPU_FUNCTION_ATTRIBUTE; +/** + * @param info + * This parameter is @ref ReturnedWithOwnership. + * + * @returns + * Indicates if there was an @ref OutStructChainError. + */ +WGPU_EXPORT WGPUStatus wgpuAdapterGetInfo(WGPUAdapter adapter, WGPUAdapterInfo * info) WGPU_FUNCTION_ATTRIBUTE; +/** + * @returns + * Indicates if there was an @ref OutStructChainError. + */ +WGPU_EXPORT WGPUStatus wgpuAdapterGetLimits(WGPUAdapter adapter, WGPULimits * limits) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUBool wgpuAdapterHasFeature(WGPUAdapter adapter, WGPUFeatureName feature) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuAdapterRequestDevice(WGPUAdapter adapter, WGPU_NULLABLE WGPUDeviceDescriptor const * descriptor, WGPURequestDeviceCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuAdapterReference(WGPUAdapter adapter) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT WGPUFuture wgpuAdapterRequestDevice(WGPUAdapter adapter, WGPU_NULLABLE WGPUDeviceDescriptor const * descriptor, WGPURequestDeviceCallbackInfo callbackInfo) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuAdapterAddRef(WGPUAdapter adapter) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuAdapterRelease(WGPUAdapter adapter) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of BindGroup -WGPU_EXPORT void wgpuBindGroupSetLabel(WGPUBindGroup bindGroup, char const * label) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuBindGroupReference(WGPUBindGroup bindGroup) WGPU_FUNCTION_ATTRIBUTE; + + +/** + * \defgroup WGPUAdapterInfoMethods WGPUAdapterInfo methods + * \brief Functions whose first argument has type WGPUAdapterInfo. + * + * @{ + */ +/** + * Frees array members of WGPUAdapterInfo which were allocated by the API. + */ +WGPU_EXPORT void wgpuAdapterInfoFreeMembers(WGPUAdapterInfo adapterInfo) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ + + + +/** + * \defgroup WGPUBindGroupMethods WGPUBindGroup methods + * \brief Functions whose first argument has type WGPUBindGroup. + * + * @{ + */ +WGPU_EXPORT void wgpuBindGroupSetLabel(WGPUBindGroup bindGroup, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuBindGroupAddRef(WGPUBindGroup bindGroup) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuBindGroupRelease(WGPUBindGroup bindGroup) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of BindGroupLayout -WGPU_EXPORT void wgpuBindGroupLayoutSetLabel(WGPUBindGroupLayout bindGroupLayout, char const * label) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuBindGroupLayoutReference(WGPUBindGroupLayout bindGroupLayout) WGPU_FUNCTION_ATTRIBUTE; + + +/** + * \defgroup WGPUBindGroupLayoutMethods WGPUBindGroupLayout methods + * \brief Functions whose first argument has type WGPUBindGroupLayout. + * + * @{ + */ +WGPU_EXPORT void wgpuBindGroupLayoutSetLabel(WGPUBindGroupLayout bindGroupLayout, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuBindGroupLayoutAddRef(WGPUBindGroupLayout bindGroupLayout) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuBindGroupLayoutRelease(WGPUBindGroupLayout bindGroupLayout) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of Buffer + + +/** + * \defgroup WGPUBufferMethods WGPUBuffer methods + * \brief Functions whose first argument has type WGPUBuffer. + * + * @{ + */ WGPU_EXPORT void wgpuBufferDestroy(WGPUBuffer buffer) WGPU_FUNCTION_ATTRIBUTE; +/** + * @param offset + * Byte offset relative to the beginning of the buffer. + * + * @param size + * Byte size of the range to get. The returned pointer is valid for exactly this many bytes. + * + * @returns + * Returns a const pointer to beginning of the mapped range. + * It must not be written; writing to this range causes undefined behavior. + * Returns `NULL` with @ref ImplementationDefinedLogging if: + * + * - There is any content-timeline error as defined in the WebGPU specification for `getMappedRange()` (alignments, overlaps, etc.) + * **except** for overlaps with other *const* ranges, which are allowed in C. + * (JS does not allow this because const ranges do not exist.) + */ WGPU_EXPORT void const * wgpuBufferGetConstMappedRange(WGPUBuffer buffer, size_t offset, size_t size) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUBufferMapState wgpuBufferGetMapState(WGPUBuffer buffer) WGPU_FUNCTION_ATTRIBUTE; +/** + * @param offset + * Byte offset relative to the beginning of the buffer. + * + * @param size + * Byte size of the range to get. The returned pointer is valid for exactly this many bytes. + * + * @returns + * Returns a mutable pointer to beginning of the mapped range. + * Returns `NULL` with @ref ImplementationDefinedLogging if: + * + * - There is any content-timeline error as defined in the WebGPU specification for `getMappedRange()` (alignments, overlaps, etc.) + * - The buffer is not mapped with @ref WGPUMapMode_Write. + */ WGPU_EXPORT void * wgpuBufferGetMappedRange(WGPUBuffer buffer, size_t offset, size_t size) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT uint64_t wgpuBufferGetSize(WGPUBuffer buffer) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT WGPUBufferUsageFlags wgpuBufferGetUsage(WGPUBuffer buffer) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuBufferMapAsync(WGPUBuffer buffer, WGPUMapModeFlags mode, size_t offset, size_t size, WGPUBufferMapCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuBufferSetLabel(WGPUBuffer buffer, char const * label) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT WGPUBufferUsage wgpuBufferGetUsage(WGPUBuffer buffer) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT WGPUFuture wgpuBufferMapAsync(WGPUBuffer buffer, WGPUMapMode mode, size_t offset, size_t size, WGPUBufferMapCallbackInfo callbackInfo) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuBufferSetLabel(WGPUBuffer buffer, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuBufferUnmap(WGPUBuffer buffer) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuBufferReference(WGPUBuffer buffer) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuBufferAddRef(WGPUBuffer buffer) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuBufferRelease(WGPUBuffer buffer) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of CommandBuffer -WGPU_EXPORT void wgpuCommandBufferSetLabel(WGPUCommandBuffer commandBuffer, char const * label) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuCommandBufferReference(WGPUCommandBuffer commandBuffer) WGPU_FUNCTION_ATTRIBUTE; + + +/** + * \defgroup WGPUCommandBufferMethods WGPUCommandBuffer methods + * \brief Functions whose first argument has type WGPUCommandBuffer. + * + * @{ + */ +WGPU_EXPORT void wgpuCommandBufferSetLabel(WGPUCommandBuffer commandBuffer, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuCommandBufferAddRef(WGPUCommandBuffer commandBuffer) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuCommandBufferRelease(WGPUCommandBuffer commandBuffer) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of CommandEncoder + + +/** + * \defgroup WGPUCommandEncoderMethods WGPUCommandEncoder methods + * \brief Functions whose first argument has type WGPUCommandEncoder. + * + * @{ + */ WGPU_EXPORT WGPUComputePassEncoder wgpuCommandEncoderBeginComputePass(WGPUCommandEncoder commandEncoder, WGPU_NULLABLE WGPUComputePassDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPURenderPassEncoder wgpuCommandEncoderBeginRenderPass(WGPUCommandEncoder commandEncoder, WGPURenderPassDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuCommandEncoderClearBuffer(WGPUCommandEncoder commandEncoder, WGPUBuffer buffer, uint64_t offset, uint64_t size) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuCommandEncoderCopyBufferToBuffer(WGPUCommandEncoder commandEncoder, WGPUBuffer source, uint64_t sourceOffset, WGPUBuffer destination, uint64_t destinationOffset, uint64_t size) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuCommandEncoderCopyBufferToTexture(WGPUCommandEncoder commandEncoder, WGPUImageCopyBuffer const * source, WGPUImageCopyTexture const * destination, WGPUExtent3D const * copySize) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuCommandEncoderCopyTextureToBuffer(WGPUCommandEncoder commandEncoder, WGPUImageCopyTexture const * source, WGPUImageCopyBuffer const * destination, WGPUExtent3D const * copySize) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuCommandEncoderCopyTextureToTexture(WGPUCommandEncoder commandEncoder, WGPUImageCopyTexture const * source, WGPUImageCopyTexture const * destination, WGPUExtent3D const * copySize) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuCommandEncoderCopyBufferToTexture(WGPUCommandEncoder commandEncoder, WGPUTexelCopyBufferInfo const * source, WGPUTexelCopyTextureInfo const * destination, WGPUExtent3D const * copySize) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuCommandEncoderCopyTextureToBuffer(WGPUCommandEncoder commandEncoder, WGPUTexelCopyTextureInfo const * source, WGPUTexelCopyBufferInfo const * destination, WGPUExtent3D const * copySize) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuCommandEncoderCopyTextureToTexture(WGPUCommandEncoder commandEncoder, WGPUTexelCopyTextureInfo const * source, WGPUTexelCopyTextureInfo const * destination, WGPUExtent3D const * copySize) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUCommandBuffer wgpuCommandEncoderFinish(WGPUCommandEncoder commandEncoder, WGPU_NULLABLE WGPUCommandBufferDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuCommandEncoderInsertDebugMarker(WGPUCommandEncoder commandEncoder, char const * markerLabel) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuCommandEncoderInsertDebugMarker(WGPUCommandEncoder commandEncoder, WGPUStringView markerLabel) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuCommandEncoderPopDebugGroup(WGPUCommandEncoder commandEncoder) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuCommandEncoderPushDebugGroup(WGPUCommandEncoder commandEncoder, char const * groupLabel) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuCommandEncoderPushDebugGroup(WGPUCommandEncoder commandEncoder, WGPUStringView groupLabel) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuCommandEncoderResolveQuerySet(WGPUCommandEncoder commandEncoder, WGPUQuerySet querySet, uint32_t firstQuery, uint32_t queryCount, WGPUBuffer destination, uint64_t destinationOffset) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuCommandEncoderSetLabel(WGPUCommandEncoder commandEncoder, char const * label) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuCommandEncoderSetLabel(WGPUCommandEncoder commandEncoder, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuCommandEncoderWriteTimestamp(WGPUCommandEncoder commandEncoder, WGPUQuerySet querySet, uint32_t queryIndex) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuCommandEncoderReference(WGPUCommandEncoder commandEncoder) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuCommandEncoderAddRef(WGPUCommandEncoder commandEncoder) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuCommandEncoderRelease(WGPUCommandEncoder commandEncoder) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of ComputePassEncoder + + +/** + * \defgroup WGPUComputePassEncoderMethods WGPUComputePassEncoder methods + * \brief Functions whose first argument has type WGPUComputePassEncoder. + * + * @{ + */ WGPU_EXPORT void wgpuComputePassEncoderDispatchWorkgroups(WGPUComputePassEncoder computePassEncoder, uint32_t workgroupCountX, uint32_t workgroupCountY, uint32_t workgroupCountZ) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuComputePassEncoderDispatchWorkgroupsIndirect(WGPUComputePassEncoder computePassEncoder, WGPUBuffer indirectBuffer, uint64_t indirectOffset) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuComputePassEncoderEnd(WGPUComputePassEncoder computePassEncoder) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuComputePassEncoderInsertDebugMarker(WGPUComputePassEncoder computePassEncoder, char const * markerLabel) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuComputePassEncoderInsertDebugMarker(WGPUComputePassEncoder computePassEncoder, WGPUStringView markerLabel) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuComputePassEncoderPopDebugGroup(WGPUComputePassEncoder computePassEncoder) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuComputePassEncoderPushDebugGroup(WGPUComputePassEncoder computePassEncoder, char const * groupLabel) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuComputePassEncoderPushDebugGroup(WGPUComputePassEncoder computePassEncoder, WGPUStringView groupLabel) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuComputePassEncoderSetBindGroup(WGPUComputePassEncoder computePassEncoder, uint32_t groupIndex, WGPU_NULLABLE WGPUBindGroup group, size_t dynamicOffsetCount, uint32_t const * dynamicOffsets) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuComputePassEncoderSetLabel(WGPUComputePassEncoder computePassEncoder, char const * label) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuComputePassEncoderSetLabel(WGPUComputePassEncoder computePassEncoder, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuComputePassEncoderSetPipeline(WGPUComputePassEncoder computePassEncoder, WGPUComputePipeline pipeline) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuComputePassEncoderReference(WGPUComputePassEncoder computePassEncoder) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuComputePassEncoderAddRef(WGPUComputePassEncoder computePassEncoder) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuComputePassEncoderRelease(WGPUComputePassEncoder computePassEncoder) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of ComputePipeline + + +/** + * \defgroup WGPUComputePipelineMethods WGPUComputePipeline methods + * \brief Functions whose first argument has type WGPUComputePipeline. + * + * @{ + */ WGPU_EXPORT WGPUBindGroupLayout wgpuComputePipelineGetBindGroupLayout(WGPUComputePipeline computePipeline, uint32_t groupIndex) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuComputePipelineSetLabel(WGPUComputePipeline computePipeline, char const * label) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuComputePipelineReference(WGPUComputePipeline computePipeline) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuComputePipelineSetLabel(WGPUComputePipeline computePipeline, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuComputePipelineAddRef(WGPUComputePipeline computePipeline) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuComputePipelineRelease(WGPUComputePipeline computePipeline) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of Device + + +/** + * \defgroup WGPUDeviceMethods WGPUDevice methods + * \brief Functions whose first argument has type WGPUDevice. + * + * @{ + */ WGPU_EXPORT WGPUBindGroup wgpuDeviceCreateBindGroup(WGPUDevice device, WGPUBindGroupDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUBindGroupLayout wgpuDeviceCreateBindGroupLayout(WGPUDevice device, WGPUBindGroupLayoutDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUBuffer wgpuDeviceCreateBuffer(WGPUDevice device, WGPUBufferDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUCommandEncoder wgpuDeviceCreateCommandEncoder(WGPUDevice device, WGPU_NULLABLE WGPUCommandEncoderDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUComputePipeline wgpuDeviceCreateComputePipeline(WGPUDevice device, WGPUComputePipelineDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuDeviceCreateComputePipelineAsync(WGPUDevice device, WGPUComputePipelineDescriptor const * descriptor, WGPUCreateComputePipelineAsyncCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT WGPUFuture wgpuDeviceCreateComputePipelineAsync(WGPUDevice device, WGPUComputePipelineDescriptor const * descriptor, WGPUCreateComputePipelineAsyncCallbackInfo callbackInfo) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUPipelineLayout wgpuDeviceCreatePipelineLayout(WGPUDevice device, WGPUPipelineLayoutDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUQuerySet wgpuDeviceCreateQuerySet(WGPUDevice device, WGPUQuerySetDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPURenderBundleEncoder wgpuDeviceCreateRenderBundleEncoder(WGPUDevice device, WGPURenderBundleEncoderDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPURenderPipeline wgpuDeviceCreateRenderPipeline(WGPUDevice device, WGPURenderPipelineDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuDeviceCreateRenderPipelineAsync(WGPUDevice device, WGPURenderPipelineDescriptor const * descriptor, WGPUCreateRenderPipelineAsyncCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT WGPUFuture wgpuDeviceCreateRenderPipelineAsync(WGPUDevice device, WGPURenderPipelineDescriptor const * descriptor, WGPUCreateRenderPipelineAsyncCallbackInfo callbackInfo) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUSampler wgpuDeviceCreateSampler(WGPUDevice device, WGPU_NULLABLE WGPUSamplerDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUShaderModule wgpuDeviceCreateShaderModule(WGPUDevice device, WGPUShaderModuleDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUTexture wgpuDeviceCreateTexture(WGPUDevice device, WGPUTextureDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuDeviceDestroy(WGPUDevice device) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT size_t wgpuDeviceEnumerateFeatures(WGPUDevice device, WGPUFeatureName * features) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT WGPUBool wgpuDeviceGetLimits(WGPUDevice device, WGPUSupportedLimits * limits) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT WGPUAdapterInfo wgpuDeviceGetAdapterInfo(WGPUDevice device) WGPU_FUNCTION_ATTRIBUTE; +/** + * Get the list of @ref WGPUFeatureName values supported by the device. + * + * @param features + * This parameter is @ref ReturnedWithOwnership. + */ +WGPU_EXPORT void wgpuDeviceGetFeatures(WGPUDevice device, WGPUSupportedFeatures * features) WGPU_FUNCTION_ATTRIBUTE; +/** + * @returns + * Indicates if there was an @ref OutStructChainError. + */ +WGPU_EXPORT WGPUStatus wgpuDeviceGetLimits(WGPUDevice device, WGPULimits * limits) WGPU_FUNCTION_ATTRIBUTE; +/** + * @returns + * The @ref WGPUFuture for the device-lost event of the device. + */ +WGPU_EXPORT WGPUFuture wgpuDeviceGetLostFuture(WGPUDevice device) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUQueue wgpuDeviceGetQueue(WGPUDevice device) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUBool wgpuDeviceHasFeature(WGPUDevice device, WGPUFeatureName feature) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuDevicePopErrorScope(WGPUDevice device, WGPUErrorCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT WGPUFuture wgpuDevicePopErrorScope(WGPUDevice device, WGPUPopErrorScopeCallbackInfo callbackInfo) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuDevicePushErrorScope(WGPUDevice device, WGPUErrorFilter filter) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuDeviceSetLabel(WGPUDevice device, char const * label) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuDeviceSetUncapturedErrorCallback(WGPUDevice device, WGPUErrorCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuDeviceReference(WGPUDevice device) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuDeviceSetLabel(WGPUDevice device, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuDeviceAddRef(WGPUDevice device) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuDeviceRelease(WGPUDevice device) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of Instance + + +/** + * \defgroup WGPUInstanceMethods WGPUInstance methods + * \brief Functions whose first argument has type WGPUInstance. + * + * @{ + */ +/** + * Creates a @ref WGPUSurface, see @ref Surface-Creation for more details. + * + * @param descriptor + * The description of the @ref WGPUSurface to create. + * + * @returns + * A new @ref WGPUSurface for this descriptor (or an error @ref WGPUSurface). + */ WGPU_EXPORT WGPUSurface wgpuInstanceCreateSurface(WGPUInstance instance, WGPUSurfaceDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; +/** + * Get the list of @ref WGPUWGSLLanguageFeatureName values supported by the instance. + */ +WGPU_EXPORT WGPUStatus wgpuInstanceGetWGSLLanguageFeatures(WGPUInstance instance, WGPUSupportedWGSLLanguageFeatures * features) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT WGPUBool wgpuInstanceHasWGSLLanguageFeature(WGPUInstance instance, WGPUWGSLLanguageFeatureName feature) WGPU_FUNCTION_ATTRIBUTE; +/** + * Processes asynchronous events on this `WGPUInstance`, calling any callbacks for asynchronous operations created with `::WGPUCallbackMode_AllowProcessEvents`. + * + * See @ref Process-Events for more information. + */ WGPU_EXPORT void wgpuInstanceProcessEvents(WGPUInstance instance) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuInstanceRequestAdapter(WGPUInstance instance, WGPU_NULLABLE WGPURequestAdapterOptions const * options, WGPURequestAdapterCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuInstanceReference(WGPUInstance instance) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT WGPUFuture wgpuInstanceRequestAdapter(WGPUInstance instance, WGPU_NULLABLE WGPURequestAdapterOptions const * options, WGPURequestAdapterCallbackInfo callbackInfo) WGPU_FUNCTION_ATTRIBUTE; +/** + * Wait for at least one WGPUFuture in `futures` to complete, and call callbacks of the respective completed asynchronous operations. + * + * See @ref Wait-Any for more information. + */ +WGPU_EXPORT WGPUWaitStatus wgpuInstanceWaitAny(WGPUInstance instance, size_t futureCount, WGPU_NULLABLE WGPUFutureWaitInfo * futures, uint64_t timeoutNS) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuInstanceAddRef(WGPUInstance instance) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuInstanceRelease(WGPUInstance instance) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of PipelineLayout -WGPU_EXPORT void wgpuPipelineLayoutSetLabel(WGPUPipelineLayout pipelineLayout, char const * label) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuPipelineLayoutReference(WGPUPipelineLayout pipelineLayout) WGPU_FUNCTION_ATTRIBUTE; + + +/** + * \defgroup WGPUPipelineLayoutMethods WGPUPipelineLayout methods + * \brief Functions whose first argument has type WGPUPipelineLayout. + * + * @{ + */ +WGPU_EXPORT void wgpuPipelineLayoutSetLabel(WGPUPipelineLayout pipelineLayout, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuPipelineLayoutAddRef(WGPUPipelineLayout pipelineLayout) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuPipelineLayoutRelease(WGPUPipelineLayout pipelineLayout) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of QuerySet + + +/** + * \defgroup WGPUQuerySetMethods WGPUQuerySet methods + * \brief Functions whose first argument has type WGPUQuerySet. + * + * @{ + */ WGPU_EXPORT void wgpuQuerySetDestroy(WGPUQuerySet querySet) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT uint32_t wgpuQuerySetGetCount(WGPUQuerySet querySet) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUQueryType wgpuQuerySetGetType(WGPUQuerySet querySet) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuQuerySetSetLabel(WGPUQuerySet querySet, char const * label) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuQuerySetReference(WGPUQuerySet querySet) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuQuerySetSetLabel(WGPUQuerySet querySet, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuQuerySetAddRef(WGPUQuerySet querySet) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuQuerySetRelease(WGPUQuerySet querySet) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of Queue -WGPU_EXPORT void wgpuQueueOnSubmittedWorkDone(WGPUQueue queue, WGPUQueueWorkDoneCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuQueueSetLabel(WGPUQueue queue, char const * label) WGPU_FUNCTION_ATTRIBUTE; + + +/** + * \defgroup WGPUQueueMethods WGPUQueue methods + * \brief Functions whose first argument has type WGPUQueue. + * + * @{ + */ +WGPU_EXPORT WGPUFuture wgpuQueueOnSubmittedWorkDone(WGPUQueue queue, WGPUQueueWorkDoneCallbackInfo callbackInfo) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuQueueSetLabel(WGPUQueue queue, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuQueueSubmit(WGPUQueue queue, size_t commandCount, WGPUCommandBuffer const * commands) WGPU_FUNCTION_ATTRIBUTE; +/** + * Produces a @ref DeviceError both content-timeline (`size` alignment) and device-timeline + * errors defined by the WebGPU specification. + */ WGPU_EXPORT void wgpuQueueWriteBuffer(WGPUQueue queue, WGPUBuffer buffer, uint64_t bufferOffset, void const * data, size_t size) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuQueueWriteTexture(WGPUQueue queue, WGPUImageCopyTexture const * destination, void const * data, size_t dataSize, WGPUTextureDataLayout const * dataLayout, WGPUExtent3D const * writeSize) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuQueueReference(WGPUQueue queue) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuQueueWriteTexture(WGPUQueue queue, WGPUTexelCopyTextureInfo const * destination, void const * data, size_t dataSize, WGPUTexelCopyBufferLayout const * dataLayout, WGPUExtent3D const * writeSize) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuQueueAddRef(WGPUQueue queue) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuQueueRelease(WGPUQueue queue) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of RenderBundle -WGPU_EXPORT void wgpuRenderBundleSetLabel(WGPURenderBundle renderBundle, char const * label) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuRenderBundleReference(WGPURenderBundle renderBundle) WGPU_FUNCTION_ATTRIBUTE; + + +/** + * \defgroup WGPURenderBundleMethods WGPURenderBundle methods + * \brief Functions whose first argument has type WGPURenderBundle. + * + * @{ + */ +WGPU_EXPORT void wgpuRenderBundleSetLabel(WGPURenderBundle renderBundle, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuRenderBundleAddRef(WGPURenderBundle renderBundle) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderBundleRelease(WGPURenderBundle renderBundle) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of RenderBundleEncoder + + +/** + * \defgroup WGPURenderBundleEncoderMethods WGPURenderBundleEncoder methods + * \brief Functions whose first argument has type WGPURenderBundleEncoder. + * + * @{ + */ WGPU_EXPORT void wgpuRenderBundleEncoderDraw(WGPURenderBundleEncoder renderBundleEncoder, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderBundleEncoderDrawIndexed(WGPURenderBundleEncoder renderBundleEncoder, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t baseVertex, uint32_t firstInstance) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderBundleEncoderDrawIndexedIndirect(WGPURenderBundleEncoder renderBundleEncoder, WGPUBuffer indirectBuffer, uint64_t indirectOffset) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderBundleEncoderDrawIndirect(WGPURenderBundleEncoder renderBundleEncoder, WGPUBuffer indirectBuffer, uint64_t indirectOffset) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPURenderBundle wgpuRenderBundleEncoderFinish(WGPURenderBundleEncoder renderBundleEncoder, WGPU_NULLABLE WGPURenderBundleDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuRenderBundleEncoderInsertDebugMarker(WGPURenderBundleEncoder renderBundleEncoder, char const * markerLabel) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuRenderBundleEncoderInsertDebugMarker(WGPURenderBundleEncoder renderBundleEncoder, WGPUStringView markerLabel) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderBundleEncoderPopDebugGroup(WGPURenderBundleEncoder renderBundleEncoder) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuRenderBundleEncoderPushDebugGroup(WGPURenderBundleEncoder renderBundleEncoder, char const * groupLabel) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuRenderBundleEncoderPushDebugGroup(WGPURenderBundleEncoder renderBundleEncoder, WGPUStringView groupLabel) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderBundleEncoderSetBindGroup(WGPURenderBundleEncoder renderBundleEncoder, uint32_t groupIndex, WGPU_NULLABLE WGPUBindGroup group, size_t dynamicOffsetCount, uint32_t const * dynamicOffsets) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderBundleEncoderSetIndexBuffer(WGPURenderBundleEncoder renderBundleEncoder, WGPUBuffer buffer, WGPUIndexFormat format, uint64_t offset, uint64_t size) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuRenderBundleEncoderSetLabel(WGPURenderBundleEncoder renderBundleEncoder, char const * label) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuRenderBundleEncoderSetLabel(WGPURenderBundleEncoder renderBundleEncoder, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderBundleEncoderSetPipeline(WGPURenderBundleEncoder renderBundleEncoder, WGPURenderPipeline pipeline) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderBundleEncoderSetVertexBuffer(WGPURenderBundleEncoder renderBundleEncoder, uint32_t slot, WGPU_NULLABLE WGPUBuffer buffer, uint64_t offset, uint64_t size) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuRenderBundleEncoderReference(WGPURenderBundleEncoder renderBundleEncoder) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuRenderBundleEncoderAddRef(WGPURenderBundleEncoder renderBundleEncoder) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderBundleEncoderRelease(WGPURenderBundleEncoder renderBundleEncoder) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of RenderPassEncoder + + +/** + * \defgroup WGPURenderPassEncoderMethods WGPURenderPassEncoder methods + * \brief Functions whose first argument has type WGPURenderPassEncoder. + * + * @{ + */ WGPU_EXPORT void wgpuRenderPassEncoderBeginOcclusionQuery(WGPURenderPassEncoder renderPassEncoder, uint32_t queryIndex) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderDraw(WGPURenderPassEncoder renderPassEncoder, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderDrawIndexed(WGPURenderPassEncoder renderPassEncoder, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t baseVertex, uint32_t firstInstance) WGPU_FUNCTION_ATTRIBUTE; @@ -1732,52 +3654,181 @@ WGPU_EXPORT void wgpuRenderPassEncoderDrawIndirect(WGPURenderPassEncoder renderP WGPU_EXPORT void wgpuRenderPassEncoderEnd(WGPURenderPassEncoder renderPassEncoder) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderEndOcclusionQuery(WGPURenderPassEncoder renderPassEncoder) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderExecuteBundles(WGPURenderPassEncoder renderPassEncoder, size_t bundleCount, WGPURenderBundle const * bundles) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuRenderPassEncoderInsertDebugMarker(WGPURenderPassEncoder renderPassEncoder, char const * markerLabel) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuRenderPassEncoderInsertDebugMarker(WGPURenderPassEncoder renderPassEncoder, WGPUStringView markerLabel) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderPopDebugGroup(WGPURenderPassEncoder renderPassEncoder) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuRenderPassEncoderPushDebugGroup(WGPURenderPassEncoder renderPassEncoder, char const * groupLabel) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuRenderPassEncoderPushDebugGroup(WGPURenderPassEncoder renderPassEncoder, WGPUStringView groupLabel) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderSetBindGroup(WGPURenderPassEncoder renderPassEncoder, uint32_t groupIndex, WGPU_NULLABLE WGPUBindGroup group, size_t dynamicOffsetCount, uint32_t const * dynamicOffsets) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderSetBlendConstant(WGPURenderPassEncoder renderPassEncoder, WGPUColor const * color) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderSetIndexBuffer(WGPURenderPassEncoder renderPassEncoder, WGPUBuffer buffer, WGPUIndexFormat format, uint64_t offset, uint64_t size) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuRenderPassEncoderSetLabel(WGPURenderPassEncoder renderPassEncoder, char const * label) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuRenderPassEncoderSetLabel(WGPURenderPassEncoder renderPassEncoder, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderSetPipeline(WGPURenderPassEncoder renderPassEncoder, WGPURenderPipeline pipeline) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderSetScissorRect(WGPURenderPassEncoder renderPassEncoder, uint32_t x, uint32_t y, uint32_t width, uint32_t height) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderSetStencilReference(WGPURenderPassEncoder renderPassEncoder, uint32_t reference) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderSetVertexBuffer(WGPURenderPassEncoder renderPassEncoder, uint32_t slot, WGPU_NULLABLE WGPUBuffer buffer, uint64_t offset, uint64_t size) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderSetViewport(WGPURenderPassEncoder renderPassEncoder, float x, float y, float width, float height, float minDepth, float maxDepth) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuRenderPassEncoderReference(WGPURenderPassEncoder renderPassEncoder) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuRenderPassEncoderAddRef(WGPURenderPassEncoder renderPassEncoder) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderRelease(WGPURenderPassEncoder renderPassEncoder) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of RenderPipeline + + +/** + * \defgroup WGPURenderPipelineMethods WGPURenderPipeline methods + * \brief Functions whose first argument has type WGPURenderPipeline. + * + * @{ + */ WGPU_EXPORT WGPUBindGroupLayout wgpuRenderPipelineGetBindGroupLayout(WGPURenderPipeline renderPipeline, uint32_t groupIndex) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuRenderPipelineSetLabel(WGPURenderPipeline renderPipeline, char const * label) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuRenderPipelineReference(WGPURenderPipeline renderPipeline) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuRenderPipelineSetLabel(WGPURenderPipeline renderPipeline, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuRenderPipelineAddRef(WGPURenderPipeline renderPipeline) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPipelineRelease(WGPURenderPipeline renderPipeline) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of Sampler -WGPU_EXPORT void wgpuSamplerSetLabel(WGPUSampler sampler, char const * label) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuSamplerReference(WGPUSampler sampler) WGPU_FUNCTION_ATTRIBUTE; + + +/** + * \defgroup WGPUSamplerMethods WGPUSampler methods + * \brief Functions whose first argument has type WGPUSampler. + * + * @{ + */ +WGPU_EXPORT void wgpuSamplerSetLabel(WGPUSampler sampler, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuSamplerAddRef(WGPUSampler sampler) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuSamplerRelease(WGPUSampler sampler) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of ShaderModule -WGPU_EXPORT void wgpuShaderModuleGetCompilationInfo(WGPUShaderModule shaderModule, WGPUCompilationInfoCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuShaderModuleSetLabel(WGPUShaderModule shaderModule, char const * label) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuShaderModuleReference(WGPUShaderModule shaderModule) WGPU_FUNCTION_ATTRIBUTE; + + +/** + * \defgroup WGPUShaderModuleMethods WGPUShaderModule methods + * \brief Functions whose first argument has type WGPUShaderModule. + * + * @{ + */ +WGPU_EXPORT WGPUFuture wgpuShaderModuleGetCompilationInfo(WGPUShaderModule shaderModule, WGPUCompilationInfoCallbackInfo callbackInfo) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuShaderModuleSetLabel(WGPUShaderModule shaderModule, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuShaderModuleAddRef(WGPUShaderModule shaderModule) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuShaderModuleRelease(WGPUShaderModule shaderModule) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of Surface + + +/** + * \defgroup WGPUSupportedFeaturesMethods WGPUSupportedFeatures methods + * \brief Functions whose first argument has type WGPUSupportedFeatures. + * + * @{ + */ +/** + * Frees array members of WGPUSupportedFeatures which were allocated by the API. + */ +WGPU_EXPORT void wgpuSupportedFeaturesFreeMembers(WGPUSupportedFeatures supportedFeatures) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ + + + +/** + * \defgroup WGPUSupportedWGSLLanguageFeaturesMethods WGPUSupportedWGSLLanguageFeatures methods + * \brief Functions whose first argument has type WGPUSupportedWGSLLanguageFeatures. + * + * @{ + */ +/** + * Frees array members of WGPUSupportedWGSLLanguageFeatures which were allocated by the API. + */ +WGPU_EXPORT void wgpuSupportedWGSLLanguageFeaturesFreeMembers(WGPUSupportedWGSLLanguageFeatures supportedWGSLLanguageFeatures) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ + + + +/** + * \defgroup WGPUSurfaceMethods WGPUSurface methods + * \brief Functions whose first argument has type WGPUSurface. + * + * @{ + */ +/** + * Configures parameters for rendering to `surface`. + * Produces a @ref DeviceError for all content-timeline errors defined by the WebGPU specification. + * + * See @ref Surface-Configuration for more details. + * + * @param config + * The new configuration to use. + */ WGPU_EXPORT void wgpuSurfaceConfigure(WGPUSurface surface, WGPUSurfaceConfiguration const * config) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuSurfaceGetCapabilities(WGPUSurface surface, WGPUAdapter adapter, WGPUSurfaceCapabilities * capabilities) WGPU_FUNCTION_ATTRIBUTE; +/** + * Provides information on how `adapter` is able to use `surface`. + * See @ref Surface-Capabilities for more details. + * + * @param adapter + * The @ref WGPUAdapter to get capabilities for presenting to this @ref WGPUSurface. + * + * @param capabilities + * The structure to fill capabilities in. + * It may contain memory allocations so `::wgpuSurfaceCapabilitiesFreeMembers` must be called to avoid memory leaks. + * This parameter is @ref ReturnedWithOwnership. + * + * @returns + * Indicates if there was an @ref OutStructChainError. + */ +WGPU_EXPORT WGPUStatus wgpuSurfaceGetCapabilities(WGPUSurface surface, WGPUAdapter adapter, WGPUSurfaceCapabilities * capabilities) WGPU_FUNCTION_ATTRIBUTE; +/** + * Returns the @ref WGPUTexture to render to `surface` this frame along with metadata on the frame. + * Returns `NULL` and @ref WGPUSurfaceGetCurrentTextureStatus_Error if the surface is not configured. + * + * See @ref Surface-Presenting for more details. + * + * @param surfaceTexture + * The structure to fill the @ref WGPUTexture and metadata in. + */ WGPU_EXPORT void wgpuSurfaceGetCurrentTexture(WGPUSurface surface, WGPUSurfaceTexture * surfaceTexture) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT WGPUTextureFormat wgpuSurfaceGetPreferredFormat(WGPUSurface surface, WGPUAdapter adapter) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuSurfacePresent(WGPUSurface surface) WGPU_FUNCTION_ATTRIBUTE; +/** + * Shows `surface`'s current texture to the user. + * See @ref Surface-Presenting for more details. + * + * @returns + * Returns @ref WGPUStatus_Error if the surface doesn't have a current texture. + */ +WGPU_EXPORT WGPUStatus wgpuSurfacePresent(WGPUSurface surface) WGPU_FUNCTION_ATTRIBUTE; +/** + * Modifies the label used to refer to `surface`. + * + * @param label + * The new label. + */ +WGPU_EXPORT void wgpuSurfaceSetLabel(WGPUSurface surface, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +/** + * Removes the configuration for `surface`. + * See @ref Surface-Configuration for more details. + */ WGPU_EXPORT void wgpuSurfaceUnconfigure(WGPUSurface surface) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuSurfaceReference(WGPUSurface surface) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuSurfaceAddRef(WGPUSurface surface) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuSurfaceRelease(WGPUSurface surface) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of SurfaceCapabilities -WGPU_EXPORT void wgpuSurfaceCapabilitiesFreeMembers(WGPUSurfaceCapabilities capabilities) WGPU_FUNCTION_ATTRIBUTE; -// Methods of Texture + +/** + * \defgroup WGPUSurfaceCapabilitiesMethods WGPUSurfaceCapabilities methods + * \brief Functions whose first argument has type WGPUSurfaceCapabilities. + * + * @{ + */ +/** + * Frees array members of WGPUSurfaceCapabilities which were allocated by the API. + */ +WGPU_EXPORT void wgpuSurfaceCapabilitiesFreeMembers(WGPUSurfaceCapabilities surfaceCapabilities) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ + + + +/** + * \defgroup WGPUTextureMethods WGPUTexture methods + * \brief Functions whose first argument has type WGPUTexture. + * + * @{ + */ WGPU_EXPORT WGPUTextureView wgpuTextureCreateView(WGPUTexture texture, WGPU_NULLABLE WGPUTextureViewDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuTextureDestroy(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT uint32_t wgpuTextureGetDepthOrArrayLayers(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; @@ -1786,16 +3837,28 @@ WGPU_EXPORT WGPUTextureFormat wgpuTextureGetFormat(WGPUTexture texture) WGPU_FUN WGPU_EXPORT uint32_t wgpuTextureGetHeight(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT uint32_t wgpuTextureGetMipLevelCount(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT uint32_t wgpuTextureGetSampleCount(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT WGPUTextureUsageFlags wgpuTextureGetUsage(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT WGPUTextureUsage wgpuTextureGetUsage(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT uint32_t wgpuTextureGetWidth(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuTextureSetLabel(WGPUTexture texture, char const * label) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuTextureReference(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuTextureSetLabel(WGPUTexture texture, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuTextureAddRef(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuTextureRelease(WGPUTexture texture) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ -// Methods of TextureView -WGPU_EXPORT void wgpuTextureViewSetLabel(WGPUTextureView textureView, char const * label) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuTextureViewReference(WGPUTextureView textureView) WGPU_FUNCTION_ATTRIBUTE; + + +/** + * \defgroup WGPUTextureViewMethods WGPUTextureView methods + * \brief Functions whose first argument has type WGPUTextureView. + * + * @{ + */ +WGPU_EXPORT void wgpuTextureViewSetLabel(WGPUTextureView textureView, WGPUStringView label) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuTextureViewAddRef(WGPUTextureView textureView) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuTextureViewRelease(WGPUTextureView textureView) WGPU_FUNCTION_ATTRIBUTE; +/** @} */ + + +/** @} */ #endif // !defined(WGPU_SKIP_DECLARATIONS) diff --git a/libs/wgpu/include/psemek/wgpu/external/wgpu.h b/libs/wgpu/include/psemek/wgpu/external/wgpu.h index ce9c7589..ec12da35 100644 --- a/libs/wgpu/include/psemek/wgpu/external/wgpu.h +++ b/libs/wgpu/include/psemek/wgpu/external/wgpu.h @@ -6,10 +6,9 @@ typedef enum WGPUNativeSType { // Start at 0003 since that's allocated range for wgpu-native WGPUSType_DeviceExtras = 0x00030001, - WGPUSType_RequiredLimitsExtras = 0x00030002, + WGPUSType_NativeLimits = 0x00030002, WGPUSType_PipelineLayoutExtras = 0x00030003, - WGPUSType_ShaderModuleGLSLDescriptor = 0x00030004, - WGPUSType_SupportedLimitsExtras = 0x00030005, + WGPUSType_ShaderSourceGLSL = 0x00030004, WGPUSType_InstanceExtras = 0x00030006, WGPUSType_BindGroupEntryExtras = 0x00030007, WGPUSType_BindGroupLayoutEntryExtras = 0x00030008, @@ -29,6 +28,33 @@ typedef enum WGPUNativeFeature { WGPUNativeFeature_PipelineStatisticsQuery = 0x00030008, WGPUNativeFeature_StorageResourceBindingArray = 0x00030009, WGPUNativeFeature_PartiallyBoundBindingArray = 0x0003000A, + WGPUNativeFeature_TextureFormat16bitNorm = 0x0003000B, + WGPUNativeFeature_TextureCompressionAstcHdr = 0x0003000C, + WGPUNativeFeature_MappablePrimaryBuffers = 0x0003000E, + WGPUNativeFeature_BufferBindingArray = 0x0003000F, + WGPUNativeFeature_UniformBufferAndStorageTextureArrayNonUniformIndexing = 0x00030010, + // TODO: requires wgpu.h api change + // WGPUNativeFeature_AddressModeClampToZero = 0x00030011, + // WGPUNativeFeature_AddressModeClampToBorder = 0x00030012, + // WGPUNativeFeature_PolygonModeLine = 0x00030013, + // WGPUNativeFeature_PolygonModePoint = 0x00030014, + // WGPUNativeFeature_ConservativeRasterization = 0x00030015, + // WGPUNativeFeature_ClearTexture = 0x00030016, + WGPUNativeFeature_SpirvShaderPassthrough = 0x00030017, + // WGPUNativeFeature_Multiview = 0x00030018, + WGPUNativeFeature_VertexAttribute64bit = 0x00030019, + WGPUNativeFeature_TextureFormatNv12 = 0x0003001A, + WGPUNativeFeature_RayTracingAccelerationStructure = 0x0003001B, + WGPUNativeFeature_RayQuery = 0x0003001C, + WGPUNativeFeature_ShaderF64 = 0x0003001D, + WGPUNativeFeature_ShaderI16 = 0x0003001E, + WGPUNativeFeature_ShaderPrimitiveIndex = 0x0003001F, + WGPUNativeFeature_ShaderEarlyDepthTest = 0x00030020, + WGPUNativeFeature_Subgroup = 0x00030021, + WGPUNativeFeature_SubgroupVertex = 0x00030022, + WGPUNativeFeature_SubgroupBarrier = 0x00030023, + WGPUNativeFeature_TimestampQueryInsideEncoders = 0x00030024, + WGPUNativeFeature_TimestampQueryInsidePasses = 0x00030025, WGPUNativeFeature_Force32 = 0x7FFFFFFF } WGPUNativeFeature; @@ -42,30 +68,26 @@ typedef enum WGPULogLevel { WGPULogLevel_Force32 = 0x7FFFFFFF } WGPULogLevel; -typedef enum WGPUInstanceBackend { - WGPUInstanceBackend_All = 0x00000000, - WGPUInstanceBackend_Vulkan = 1 << 0, - WGPUInstanceBackend_GL = 1 << 1, - WGPUInstanceBackend_Metal = 1 << 2, - WGPUInstanceBackend_DX12 = 1 << 3, - WGPUInstanceBackend_DX11 = 1 << 4, - WGPUInstanceBackend_BrowserWebGPU = 1 << 5, - WGPUInstanceBackend_Primary = WGPUInstanceBackend_Vulkan | WGPUInstanceBackend_Metal | - WGPUInstanceBackend_DX12 | - WGPUInstanceBackend_BrowserWebGPU, - WGPUInstanceBackend_Secondary = WGPUInstanceBackend_GL | WGPUInstanceBackend_DX11, - WGPUInstanceBackend_Force32 = 0x7FFFFFFF -} WGPUInstanceBackend; -typedef WGPUFlags WGPUInstanceBackendFlags; +typedef WGPUFlags WGPUInstanceBackend; +static const WGPUInstanceBackend WGPUInstanceBackend_All = 0x00000000; +static const WGPUInstanceBackend WGPUInstanceBackend_Vulkan = 1 << 0; +static const WGPUInstanceBackend WGPUInstanceBackend_GL = 1 << 1; +static const WGPUInstanceBackend WGPUInstanceBackend_Metal = 1 << 2; +static const WGPUInstanceBackend WGPUInstanceBackend_DX12 = 1 << 3; +static const WGPUInstanceBackend WGPUInstanceBackend_DX11 = 1 << 4; +static const WGPUInstanceBackend WGPUInstanceBackend_BrowserWebGPU = 1 << 5; +// Vulkan, Metal, DX12 and BrowserWebGPU +static const WGPUInstanceBackend WGPUInstanceBackend_Primary = (1 << 0) | (1 << 2) | (1 << 3) | (1 << 5); +// GL and DX11 +static const WGPUInstanceBackend WGPUInstanceBackend_Secondary = (1 << 1) | (1 << 4); +static const WGPUInstanceBackend WGPUInstanceBackend_Force32 = 0x7FFFFFFF; -typedef enum WGPUInstanceFlag { - WGPUInstanceFlag_Default = 0x00000000, - WGPUInstanceFlag_Debug = 1 << 0, - WGPUInstanceFlag_Validation = 1 << 1, - WGPUInstanceFlag_DiscardHalLabels = 1 << 2, - WGPUInstanceFlag_Force32 = 0x7FFFFFFF -} WGPUInstanceFlag; -typedef WGPUFlags WGPUInstanceFlags; +typedef WGPUFlags WGPUInstanceFlag; +static const WGPUInstanceFlag WGPUInstanceFlag_Default = 0x00000000; +static const WGPUInstanceFlag WGPUInstanceFlag_Debug = 1 << 0; +static const WGPUInstanceFlag WGPUInstanceFlag_Validation = 1 << 1; +static const WGPUInstanceFlag WGPUInstanceFlag_DiscardHalLabels = 1 << 2; +static const WGPUInstanceFlag WGPUInstanceFlag_Force32 = 0x7FFFFFFF; typedef enum WGPUDx12Compiler { WGPUDx12Compiler_Undefined = 0x00000000, @@ -96,38 +118,50 @@ typedef enum WGPUNativeQueryType { WGPUNativeQueryType_Force32 = 0x7FFFFFFF } WGPUNativeQueryType WGPU_ENUM_ATTRIBUTE; +typedef enum WGPUDxcMaxShaderModel { + WGPUDxcMaxShaderModel_V6_0 = 0x00000000, + WGPUDxcMaxShaderModel_V6_1 = 0x00000001, + WGPUDxcMaxShaderModel_V6_2 = 0x00000002, + WGPUDxcMaxShaderModel_V6_3 = 0x00000003, + WGPUDxcMaxShaderModel_V6_4 = 0x00000004, + WGPUDxcMaxShaderModel_V6_5 = 0x00000005, + WGPUDxcMaxShaderModel_V6_6 = 0x00000006, + WGPUDxcMaxShaderModel_V6_7 = 0x00000007, + WGPUDxcMaxShaderModel_Force32 = 0x7FFFFFFF +} WGPUDxcMaxShaderModel; + +typedef enum WGPUGLFenceBehaviour { + WGPUGLFenceBehaviour_Normal = 0x00000000, + WGPUGLFenceBehaviour_AutoFinish = 0x00000001, + WGPUGLFenceBehaviour_Force32 = 0x7FFFFFFF +} WGPUGLFenceBehaviour; + typedef struct WGPUInstanceExtras { WGPUChainedStruct chain; - WGPUInstanceBackendFlags backends; - WGPUInstanceFlags flags; + WGPUInstanceBackend backends; + WGPUInstanceFlag flags; WGPUDx12Compiler dx12ShaderCompiler; WGPUGles3MinorVersion gles3MinorVersion; - const char * dxilPath; - const char * dxcPath; + WGPUGLFenceBehaviour glFenceBehaviour; + WGPUStringView dxilPath; + WGPUStringView dxcPath; + WGPUDxcMaxShaderModel dxcMaxShaderModel; } WGPUInstanceExtras; typedef struct WGPUDeviceExtras { WGPUChainedStruct chain; - const char * tracePath; + WGPUStringView tracePath; } WGPUDeviceExtras; typedef struct WGPUNativeLimits { + /** This struct chain is used as mutable in some places and immutable in others. */ + WGPUChainedStructOut chain; uint32_t maxPushConstantSize; uint32_t maxNonSamplerBindings; } WGPUNativeLimits; -typedef struct WGPURequiredLimitsExtras { - WGPUChainedStruct chain; - WGPUNativeLimits limits; -} WGPURequiredLimitsExtras; - -typedef struct WGPUSupportedLimitsExtras { - WGPUChainedStructOut chain; - WGPUNativeLimits limits; -} WGPUSupportedLimitsExtras; - typedef struct WGPUPushConstantRange { - WGPUShaderStageFlags stages; + WGPUShaderStage stages; uint32_t start; uint32_t end; } WGPUPushConstantRange; @@ -140,29 +174,29 @@ typedef struct WGPUPipelineLayoutExtras { typedef uint64_t WGPUSubmissionIndex; -typedef struct WGPUWrappedSubmissionIndex { - WGPUQueue queue; - WGPUSubmissionIndex submissionIndex; -} WGPUWrappedSubmissionIndex; - typedef struct WGPUShaderDefine { - char const * name; - char const * value; + WGPUStringView name; + WGPUStringView value; } WGPUShaderDefine; -typedef struct WGPUShaderModuleGLSLDescriptor { +typedef struct WGPUShaderSourceGLSL { WGPUChainedStruct chain; WGPUShaderStage stage; - char const * code; + WGPUStringView code; uint32_t defineCount; WGPUShaderDefine * defines; -} WGPUShaderModuleGLSLDescriptor; +} WGPUShaderSourceGLSL; + +typedef struct WGPUShaderModuleDescriptorSpirV { + WGPUStringView label; + uint32_t sourceSize; + uint32_t const * source; +} WGPUShaderModuleDescriptorSpirV; typedef struct WGPURegistryReport { size_t numAllocated; size_t numKeptFromUser; size_t numReleasedFromUser; - size_t numError; size_t elementSize; } WGPURegistryReport; @@ -178,6 +212,7 @@ typedef struct WGPUHubReport { WGPURegistryReport renderBundles; WGPURegistryReport renderPipelines; WGPURegistryReport computePipelines; + WGPURegistryReport pipelineCaches; WGPURegistryReport querySets; WGPURegistryReport buffers; WGPURegistryReport textures; @@ -187,16 +222,12 @@ typedef struct WGPUHubReport { typedef struct WGPUGlobalReport { WGPURegistryReport surfaces; - WGPUBackendType backendType; - WGPUHubReport vulkan; - WGPUHubReport metal; - WGPUHubReport dx12; - WGPUHubReport gl; + WGPUHubReport hub; } WGPUGlobalReport; typedef struct WGPUInstanceEnumerateAdapterOptions { WGPUChainedStruct const * nextInChain; - WGPUInstanceBackendFlags backends; + WGPUInstanceBackend backends; } WGPUInstanceEnumerateAdapterOptions; typedef struct WGPUBindGroupEntryExtras { @@ -222,10 +253,22 @@ typedef struct WGPUQuerySetDescriptorExtras { typedef struct WGPUSurfaceConfigurationExtras { WGPUChainedStruct chain; - WGPUBool desiredMaximumFrameLatency; + uint32_t desiredMaximumFrameLatency; } WGPUSurfaceConfigurationExtras WGPU_STRUCTURE_ATTRIBUTE; -typedef void (*WGPULogCallback)(WGPULogLevel level, char const * message, void * userdata); +typedef void (*WGPULogCallback)(WGPULogLevel level, WGPUStringView message, void * userdata); + +typedef enum WGPUNativeTextureFormat { + // From Features::TEXTURE_FORMAT_16BIT_NORM + WGPUNativeTextureFormat_R16Unorm = 0x00030001, + WGPUNativeTextureFormat_R16Snorm = 0x00030002, + WGPUNativeTextureFormat_Rg16Unorm = 0x00030003, + WGPUNativeTextureFormat_Rg16Snorm = 0x00030004, + WGPUNativeTextureFormat_Rgba16Unorm = 0x00030005, + WGPUNativeTextureFormat_Rgba16Snorm = 0x00030006, + // From Features::TEXTURE_FORMAT_NV12 + WGPUNativeTextureFormat_NV12 = 0x00030007, +} WGPUNativeTextureFormat; #ifdef __cplusplus extern "C" { @@ -237,7 +280,8 @@ size_t wgpuInstanceEnumerateAdapters(WGPUInstance instance, WGPU_NULLABLE WGPUIn WGPUSubmissionIndex wgpuQueueSubmitForIndex(WGPUQueue queue, size_t commandCount, WGPUCommandBuffer const * commands); // Returns true if the queue is empty, or false if there are more queue submissions still in flight. -WGPUBool wgpuDevicePoll(WGPUDevice device, WGPUBool wait, WGPU_NULLABLE WGPUWrappedSubmissionIndex const * wrappedSubmissionIndex); +WGPUBool wgpuDevicePoll(WGPUDevice device, WGPUBool wait, WGPU_NULLABLE WGPUSubmissionIndex const * submissionIndex); +WGPUShaderModule wgpuDeviceCreateShaderModuleSpirV(WGPUDevice device, WGPUShaderModuleDescriptorSpirV const * descriptor); void wgpuSetLogCallback(WGPULogCallback callback, void * userdata); @@ -245,7 +289,9 @@ void wgpuSetLogLevel(WGPULogLevel level); uint32_t wgpuGetVersion(void); -void wgpuRenderPassEncoderSetPushConstants(WGPURenderPassEncoder encoder, WGPUShaderStageFlags stages, uint32_t offset, uint32_t sizeBytes, void const * data); +void wgpuRenderPassEncoderSetPushConstants(WGPURenderPassEncoder encoder, WGPUShaderStage stages, uint32_t offset, uint32_t sizeBytes, void const * data); +void wgpuComputePassEncoderSetPushConstants(WGPUComputePassEncoder encoder, uint32_t offset, uint32_t sizeBytes, void const * data); +void wgpuRenderBundleEncoderSetPushConstants(WGPURenderBundleEncoder encoder, WGPUShaderStage stages, uint32_t offset, uint32_t sizeBytes, void const * data); void wgpuRenderPassEncoderMultiDrawIndirect(WGPURenderPassEncoder encoder, WGPUBuffer buffer, uint64_t offset, uint32_t count); void wgpuRenderPassEncoderMultiDrawIndexedIndirect(WGPURenderPassEncoder encoder, WGPUBuffer buffer, uint64_t offset, uint32_t count); @@ -258,6 +304,9 @@ void wgpuComputePassEncoderEndPipelineStatisticsQuery(WGPUComputePassEncoder com void wgpuRenderPassEncoderBeginPipelineStatisticsQuery(WGPURenderPassEncoder renderPassEncoder, WGPUQuerySet querySet, uint32_t queryIndex); void wgpuRenderPassEncoderEndPipelineStatisticsQuery(WGPURenderPassEncoder renderPassEncoder); +void wgpuComputePassEncoderWriteTimestamp(WGPUComputePassEncoder computePassEncoder, WGPUQuerySet querySet, uint32_t queryIndex); +void wgpuRenderPassEncoderWriteTimestamp(WGPURenderPassEncoder renderPassEncoder, WGPUQuerySet querySet, uint32_t queryIndex); + #ifdef __cplusplus } // extern "C" #endif diff --git a/libs/wgpu/include/psemek/wgpu/image_copy.hpp b/libs/wgpu/include/psemek/wgpu/image_copy.hpp index 58fa0fb1..ae752142 100644 --- a/libs/wgpu/include/psemek/wgpu/image_copy.hpp +++ b/libs/wgpu/include/psemek/wgpu/image_copy.hpp @@ -3,25 +3,28 @@ #include #include #include -#include - -#include namespace psemek::wgpu { - struct image_copy_texture + struct texel_copy_texture_info { - std::vector chain = {}; struct texture texture; std::uint32_t mip_level = 0; math::point origin = {0, 0, 0}; texture::aspect aspect = texture::aspect::all; }; - struct image_copy_buffer { - std::vector chain = {}; - texture::data_layout layout; + struct texel_copy_buffer_layout + { + std::uint64_t offset = 0; + std::uint32_t bytes_per_row; + std::uint32_t rows_per_image; + }; + + struct texel_copy_buffer_info + { + texel_copy_buffer_layout layout; struct buffer buffer; }; diff --git a/libs/wgpu/include/psemek/wgpu/instance.hpp b/libs/wgpu/include/psemek/wgpu/instance.hpp index b5a0bca9..24d13d75 100644 --- a/libs/wgpu/include/psemek/wgpu/instance.hpp +++ b/libs/wgpu/include/psemek/wgpu/instance.hpp @@ -13,6 +13,8 @@ namespace psemek::wgpu { using detail::object::object; + // TODO: add WGPUFuture stuff when it's supported in wgpu-native + struct descriptor { std::vector chain = {}; @@ -22,7 +24,7 @@ namespace psemek::wgpu void process_events(); surface create_surface(surface::descriptor const & desc); - void request_adapter(adapter::request_options const & request_options, adapter::request_callback callback); + void request_adapter(callback_mode mode, adapter::request_options const & request_options, adapter::request_callback callback); static void reference(void * ptr); static void release(void * ptr); diff --git a/libs/wgpu/include/psemek/wgpu/query_set.hpp b/libs/wgpu/include/psemek/wgpu/query_set.hpp index 0b9a3f69..98fb15fe 100644 --- a/libs/wgpu/include/psemek/wgpu/query_set.hpp +++ b/libs/wgpu/include/psemek/wgpu/query_set.hpp @@ -16,8 +16,8 @@ namespace psemek::wgpu enum class type : std::uint32_t { - occlusion = 0x00000000, - timestamp = 0x00000001, + occlusion = 0x00000001, + timestamp = 0x00000002, }; struct descriptor diff --git a/libs/wgpu/include/psemek/wgpu/queue.hpp b/libs/wgpu/include/psemek/wgpu/queue.hpp index e4738ccd..66951165 100644 --- a/libs/wgpu/include/psemek/wgpu/queue.hpp +++ b/libs/wgpu/include/psemek/wgpu/queue.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -29,18 +30,18 @@ namespace psemek::wgpu enum class work_done_status : std::uint32_t { - success = 0x00000000, - error = 0x00000001, - unknown = 0x00000002, - device_lost = 0x00000003, + success = 0x00000001, + instance_dropped = 0x00000002, + error = 0x00000003, + unknown = 0x00000004, }; using work_done_callback = std::function; void submit(std::vector const & commands); void write_buffer(buffer const & buffer, std::uint64_t offset, util::span data); - void write_texture(image_copy_texture const & dest, util::span data, texture::data_layout const & data_layout, math::vector const & write_size); - void on_submitted_work_done(work_done_callback const & callback); + void write_texture(texel_copy_texture_info const & dest, util::span data, texel_copy_buffer_layout const & data_layout, math::vector const & write_size); + void on_submitted_work_done(callback_mode mode, work_done_callback const & callback); void set_label(std::string const & label); static void reference(void * ptr); diff --git a/libs/wgpu/include/psemek/wgpu/render_bundle_encoder.hpp b/libs/wgpu/include/psemek/wgpu/render_bundle_encoder.hpp index 0d931df7..44b1d2d8 100644 --- a/libs/wgpu/include/psemek/wgpu/render_bundle_encoder.hpp +++ b/libs/wgpu/include/psemek/wgpu/render_bundle_encoder.hpp @@ -38,8 +38,8 @@ namespace psemek::wgpu void draw_indexed(std::uint32_t index_count, std::uint32_t instance_count, std::uint32_t first_index, std::uint32_t base_vertex, std::uint32_t first_instance); void draw_indirect(buffer const & indirect_buffer, std::uint64_t offset); void draw_indexed_indirect(buffer const & indirect_buffer, std::uint64_t offset); - void insert_debug_marker(std::string const & marker_label); - void push_debug_group(std::string const & group_label); + void insert_debug_marker(std::string_view marker_label); + void push_debug_group(std::string_view group_label); void pop_debug_group(); render_bundle finish(render_bundle::descriptor const & desc); void set_label(std::string const & label); diff --git a/libs/wgpu/include/psemek/wgpu/render_pass_encoder.hpp b/libs/wgpu/include/psemek/wgpu/render_pass_encoder.hpp index 37368a24..2bb77c2f 100644 --- a/libs/wgpu/include/psemek/wgpu/render_pass_encoder.hpp +++ b/libs/wgpu/include/psemek/wgpu/render_pass_encoder.hpp @@ -21,15 +21,15 @@ namespace psemek::wgpu enum class load_op : std::uint32_t { undefined = 0x00000000, - clear = 0x00000001, - load = 0x00000002, + load = 0x00000001, + clear = 0x00000002, }; enum class store_op : std::uint32_t { undefined = 0x00000000, - store = 0x00000001, - discard = 0x00000002, + store = 0x00000001, + discard = 0x00000002, }; struct render_pass_encoder @@ -41,6 +41,7 @@ namespace psemek::wgpu { std::vector chain = {}; texture_view view; + std::uint32_t depth_slice = 0; texture_view resolve_target = {}; enum load_op load_op; enum store_op store_op; @@ -97,8 +98,9 @@ namespace psemek::wgpu void execute_bundles(std::vector const & bundles); void begin_occlusion_query(std::uint32_t query_index); void end_occlusion_query(); - void insert_debug_marker(std::string const & marker_label); - void push_debug_group(std::string const & group_label); + void write_timestamp(query_set const & query_set, std::uint32_t query_index); + void insert_debug_marker(std::string_view marker_label); + void push_debug_group(std::string_view group_label); void pop_debug_group(); void end(); void set_label(std::string const & label); diff --git a/libs/wgpu/include/psemek/wgpu/render_pipeline.hpp b/libs/wgpu/include/psemek/wgpu/render_pipeline.hpp index 1b1e998d..e5592074 100644 --- a/libs/wgpu/include/psemek/wgpu/render_pipeline.hpp +++ b/libs/wgpu/include/psemek/wgpu/render_pipeline.hpp @@ -19,44 +19,55 @@ namespace psemek::wgpu enum class vertex_step_mode : std::uint32_t { - vertex = 0x00000000, - instance = 0x00000001, - vertex_buffer_not_used = 0x00000002, + vertex_buffer_not_used = 0x00000000, + undefined = 0x00000001, + vertex = 0x00000002, + instance = 0x00000003, }; enum class vertex_format : std::uint32_t { - undefined = 0x00000000, - uint8x2 = 0x00000001, - uint8x4 = 0x00000002, - sint8x2 = 0x00000003, - sint8x4 = 0x00000004, - unorm8x2 = 0x00000005, - unorm8x4 = 0x00000006, - snorm8x2 = 0x00000007, - snorm8x4 = 0x00000008, - uint16x2 = 0x00000009, - uint16x4 = 0x0000000a, - sint16x2 = 0x0000000b, - sint16x4 = 0x0000000c, - unorm16x2 = 0x0000000d, - unorm16x4 = 0x0000000e, - snorm16x2 = 0x0000000f, - snorm16x4 = 0x00000010, - float16x2 = 0x00000011, - float16x4 = 0x00000012, - float32 = 0x00000013, - float32x2 = 0x00000014, - float32x3 = 0x00000015, - float32x4 = 0x00000016, - uint32 = 0x00000017, - uint32x2 = 0x00000018, - uint32x3 = 0x00000019, - uint32x4 = 0x0000001a, - sint32 = 0x0000001b, - sint32x2 = 0x0000001c, - sint32x3 = 0x0000001d, - sint32x4 = 0x0000001e, + uint8 = 0x00000001, + uint8x2 = 0x00000002, + uint8x4 = 0x00000003, + sint8 = 0x00000004, + sint8x2 = 0x00000005, + sint8x4 = 0x00000006, + unorm8 = 0x00000007, + unorm8x2 = 0x00000008, + unorm8x4 = 0x00000009, + snorm8 = 0x0000000a, + snorm8x2 = 0x0000000b, + snorm8x4 = 0x0000000c, + uint16 = 0x0000000d, + uint16x2 = 0x0000000e, + uint16x4 = 0x0000000f, + sint16 = 0x00000010, + sint16x2 = 0x00000011, + sint16x4 = 0x00000012, + unorm16 = 0x00000013, + unorm16x2 = 0x00000014, + unorm16x4 = 0x00000015, + snorm16 = 0x00000016, + snorm16x2 = 0x00000017, + snorm16x4 = 0x00000018, + float16 = 0x00000019, + float16x2 = 0x0000001a, + float16x4 = 0x0000001b, + float32 = 0x0000001c, + float32x2 = 0x0000001d, + float32x3 = 0x0000001e, + float32x4 = 0x0000001f, + uint32 = 0x00000020, + uint32x2 = 0x00000021, + uint32x3 = 0x00000022, + uint32x4 = 0x00000023, + sint32 = 0x00000024, + sint32x2 = 0x00000025, + sint32x3 = 0x00000026, + sint32x4 = 0x00000027, + unorm10_10_10_2 = 0x00000028, + unorm8x4_bgra = 0x00000029, }; struct vertex_attribute @@ -84,31 +95,34 @@ namespace psemek::wgpu enum class primitive_topology : std::uint32_t { - point_list = 0x00000000, - line_list = 0x00000001, - line_strip = 0x00000002, - triangle_list = 0x00000003, - triangle_strip = 0x00000004, + undefined = 0x00000000, + point_list = 0x00000001, + line_list = 0x00000002, + line_strip = 0x00000003, + triangle_list = 0x00000004, + triangle_strip = 0x00000005, }; enum class index_format : std::uint32_t { undefined = 0x00000000, - uint16 = 0x00000001, - uint32 = 0x00000002, + uint16 = 0x00000001, + uint32 = 0x00000002, }; enum class front_face : std::uint32_t { - ccw = 0x00000000, - cw = 0x00000001, + undefined = 0x00000000, + ccw = 0x00000001, + cw = 0x00000002, }; enum class cull_mode : std::uint32_t { - none = 0x00000000, - front = 0x00000001, - back = 0x00000002, + undefined = 0x00000000, + none = 0x00000001, + front = 0x00000002, + back = 0x00000003, }; struct primitive_state @@ -118,18 +132,20 @@ namespace psemek::wgpu index_format strip_index_format = index_format::undefined; enum front_face front_face; enum cull_mode cull_mode; + bool clip_depth = true; }; enum class stencil_operation : std::uint32_t { - keep = 0x00000000, - zero = 0x00000001, - replace = 0x00000002, - invert = 0x00000003, - increment_clamp = 0x00000004, - decrement_clamp = 0x00000005, - increment_wrap = 0x00000006, - decrement_wrap = 0x00000007, + undefined = 0x00000000, + keep = 0x00000001, + zero = 0x00000002, + replace = 0x00000003, + invert = 0x00000004, + increment_clamp = 0x00000005, + decrement_clamp = 0x00000006, + increment_wrap = 0x00000007, + decrement_wrap = 0x00000008, }; struct stencil_face_state @@ -144,8 +160,8 @@ namespace psemek::wgpu { std::vector chain = {}; texture::format format; - bool depth_write = true; - compare_function depth_compare; + std::optional depth_write = true; + compare_function depth_compare = compare_function::always; stencil_face_state stencil_front = {}; stencil_face_state stencil_back = {}; std::uint32_t stencil_read_mask = 0; @@ -165,28 +181,34 @@ namespace psemek::wgpu enum class blend_operation : std::uint32_t { - add = 0x00000000, - subtract = 0x00000001, - reverse_subtract = 0x00000002, - min = 0x00000003, - max = 0x00000004, + undefined = 0x00000000, + add = 0x00000001, + subtract = 0x00000002, + reverse_subtract = 0x00000003, + min = 0x00000004, + max = 0x00000005, }; enum class blend_factor : std::uint32_t { - zero = 0x00000000, - one = 0x00000001, - src = 0x00000002, - one_minus_src = 0x00000003, - src_alpha = 0x00000004, - one_minus_src_alpha = 0x00000005, - dst = 0x00000006, - one_minus_dst = 0x00000007, - dst_alpha = 0x00000008, - one_minus_dst_alpha = 0x00000009, - src_alpha_saturated = 0x0000000a, - constant = 0x0000000b, - one_minus_constant = 0x0000000c, + undefined = 0x00000000, + zero = 0x00000001, + one = 0x00000002, + src = 0x00000003, + one_minus_src = 0x00000004, + src_alpha = 0x00000005, + one_minus_src_alpha = 0x00000006, + dst = 0x00000007, + one_minus_dst = 0x00000008, + dst_alpha = 0x00000009, + one_minus_dst_alpha = 0x0000000a, + src_alpha_saturated = 0x0000000b, + constant = 0x0000000c, + one_minus_constant = 0x0000000d, + src1 = 0x0000000e, + one_minus_src1 = 0x0000000f, + src1_alpha = 0x00000010, + one_minus_src1_alpha = 0x00000011, }; struct blend_component @@ -202,14 +224,14 @@ namespace psemek::wgpu blend_component alpha; }; - enum color_write_mask : std::uint32_t + enum color_write_mask : std::uint64_t { - none = 0x00000000, - red = 0x00000001, + none = 0x00000000, + red = 0x00000001, green = 0x00000002, - blue = 0x00000004, + blue = 0x00000004, alpha = 0x00000008, - all = 0x0000000f, + all = 0x0000000f, }; struct color_target_state diff --git a/libs/wgpu/include/psemek/wgpu/sampler.hpp b/libs/wgpu/include/psemek/wgpu/sampler.hpp index ccc67bd4..f14c361a 100644 --- a/libs/wgpu/include/psemek/wgpu/sampler.hpp +++ b/libs/wgpu/include/psemek/wgpu/sampler.hpp @@ -13,34 +13,37 @@ namespace psemek::wgpu enum class address_mode : std::uint32_t { - repeat = 0x00000000, - mirror_repeat = 0x00000001, - clamp_to_edge = 0x00000002, + undefined = 0x00000000, + clamp_to_edge = 0x00000001, + repeat = 0x00000002, + mirror_repeat = 0x00000003, }; enum class filter_mode : std::uint32_t { - nearest = 0x00000000, - linear = 0x00000001, + undefined = 0x00000000, + nearest = 0x00000001, + linear = 0x00000002, }; enum class mipmap_filter_mode : std::uint32_t { - nearest = 0x00000000, - linear = 0x00000001, + undefined = 0x00000000, + nearest = 0x00000001, + linear = 0x00000002, }; enum class compare_function : std::uint32_t { - undefined = 0x00000000, - never = 0x00000001, - less = 0x00000002, - less_equal = 0x00000003, - greater = 0x00000004, - greater_equal = 0x00000005, - equal = 0x00000006, - not_equal = 0x00000007, - always = 0x00000008, + undefined = 0x00000000, + never = 0x00000001, + less = 0x00000002, + equal = 0x00000003, + less_equal = 0x00000004, + greater = 0x00000005, + not_equal = 0x00000006, + greater_equal = 0x00000007, + always = 0x00000008, }; struct sampler diff --git a/libs/wgpu/include/psemek/wgpu/shader_module.hpp b/libs/wgpu/include/psemek/wgpu/shader_module.hpp index 72c4b7ff..64b01467 100644 --- a/libs/wgpu/include/psemek/wgpu/shader_module.hpp +++ b/libs/wgpu/include/psemek/wgpu/shader_module.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -21,17 +22,17 @@ namespace psemek::wgpu enum class compilation_info_request_status : std::uint32_t { - success = 0x00000000, - error = 0x00000001, - device_lost = 0x00000002, - unknown = 0x00000003, + success = 0x00000001, + instance_dropped = 0x00000002, + error = 0x00000003, + unknown = 0x00000004, }; enum class compilation_message_type : std::uint32_t { - error = 0x00000000, - warning = 0x00000001, - info = 0x00000002, + error = 0x00000001, + warning = 0x00000002, + info = 0x00000003, }; struct compilation_message @@ -43,9 +44,6 @@ namespace psemek::wgpu std::uint64_t line_pos; std::uint64_t offset; std::uint64_t length; - std::uint64_t utf16_line_pos; - std::uint64_t utf16_offset; - std::uint64_t utf16_length; }; struct compilation_info @@ -54,24 +52,16 @@ namespace psemek::wgpu std::vector messages = {}; }; - struct compilation_hint - { - std::vector chain = {}; - std::string entry_point; - pipeline_layout layout; - }; - struct define { - char const * name; - char const * value; + std::string_view name; + std::string_view value; }; struct descriptor { std::vector chain = {}; std::string label = {}; - std::vector hints = {}; }; struct spirv_descriptor @@ -81,19 +71,19 @@ namespace psemek::wgpu struct wgsl_descriptor { - char const * code; + std::string_view code; }; struct glsl_descriptor { shader_stage stage; - char const * code; + std::string_view code; std::vector defines = {}; }; using compilation_info_callback = std::function; - void get_compilation_info(compilation_info_callback const & callback); + void get_compilation_info(callback_mode mode, compilation_info_callback const & callback); void set_label(std::string const & label); static void reference(void * ptr); diff --git a/libs/wgpu/include/psemek/wgpu/shader_stage.hpp b/libs/wgpu/include/psemek/wgpu/shader_stage.hpp index e5a38c1d..d5c789ad 100644 --- a/libs/wgpu/include/psemek/wgpu/shader_stage.hpp +++ b/libs/wgpu/include/psemek/wgpu/shader_stage.hpp @@ -5,17 +5,17 @@ namespace psemek::wgpu { - enum class shader_stage : std::uint32_t + enum class shader_stage : std::uint64_t { - none = 0x00000000, - vertex = 0x00000001, + none = 0x00000000, + vertex = 0x00000001, fragment = 0x00000002, - compute = 0x00000004, + compute = 0x00000004, }; inline shader_stage operator | (shader_stage s1, shader_stage s2) { - return static_cast(static_cast(s1) | static_cast(s2)); + return static_cast(static_cast(s1) | static_cast(s2)); } } diff --git a/libs/wgpu/include/psemek/wgpu/surface.hpp b/libs/wgpu/include/psemek/wgpu/surface.hpp index 70c03729..7c35fba5 100644 --- a/libs/wgpu/include/psemek/wgpu/surface.hpp +++ b/libs/wgpu/include/psemek/wgpu/surface.hpp @@ -27,19 +27,20 @@ namespace psemek::wgpu enum class composite_alpha_mode : std::uint32_t { - _auto = 0x00000000, - opaque = 0x00000001, - premultiplied = 0x00000002, + _auto = 0x00000000, + opaque = 0x00000001, + premultiplied = 0x00000002, unpremultiplied = 0x00000003, - inherit = 0x00000004, + inherit = 0x00000004, }; enum class present_mode : std::uint32_t { - fifo = 0x00000000, - fifo_relaxed = 0x00000001, - immediate = 0x00000002, - mailbox = 0x00000003, + undefined = 0x00000000, + fifo = 0x00000001, + fifo_relaxed = 0x00000002, + immediate = 0x00000003, + mailbox = 0x00000004, }; struct configuration @@ -63,6 +64,7 @@ namespace psemek::wgpu struct capabilities { std::vector chain = {}; + texture::usage usage = texture::usage::none; std::vector formats = {}; std::vector present_modes = {}; std::vector alpha_modes = {}; @@ -72,23 +74,23 @@ namespace psemek::wgpu { enum class status : std::uint32_t { - success = 0x00000000, - timeout = 0x00000001, - outdated = 0x00000002, - lost = 0x00000003, - out_of_memory = 0x00000004, - device_lost = 0x00000005, + success_optimal = 0x00000001, + success_suboptimal = 0x00000002, + timeout = 0x00000003, + outdated = 0x00000004, + lost = 0x00000005, + out_of_memory = 0x00000006, + device_lost = 0x00000007, + error = 0x00000008, }; wgpu::texture texture; - bool suboptimal; enum status status; }; void configure(configuration const & conf); capabilities get_capabilities(adapter const & adapter); current_texture get_current_texture(); - wgpu::texture::format get_preferred_format(adapter const & adapter); void present(); void unconfigure(); @@ -97,11 +99,6 @@ namespace psemek::wgpu void * window; }; - struct from_canvas_html_selected - { - std::string selector; - }; - struct from_metal_layer { void * layer; @@ -144,7 +141,6 @@ namespace psemek::wgpu detail::chained_struct_ptr to_chained_struct(surface::configuration::extras const & value); detail::chained_struct_ptr to_chained_struct(surface::from_android_native_window const & value); - detail::chained_struct_ptr to_chained_struct(surface::from_canvas_html_selected const & value); detail::chained_struct_ptr to_chained_struct(surface::from_metal_layer const & value); detail::chained_struct_ptr to_chained_struct(surface::from_wayland_surface const & value); detail::chained_struct_ptr to_chained_struct(surface::from_windows_hwnd const & value); diff --git a/libs/wgpu/include/psemek/wgpu/texture.hpp b/libs/wgpu/include/psemek/wgpu/texture.hpp index 563162c2..748b9921 100644 --- a/libs/wgpu/include/psemek/wgpu/texture.hpp +++ b/libs/wgpu/include/psemek/wgpu/texture.hpp @@ -19,136 +19,150 @@ namespace psemek::wgpu enum class format : std::uint32_t { - undefined = 0x00000000, - r8unorm = 0x00000001, - r8snorm = 0x00000002, - r8uint = 0x00000003, - r8sint = 0x00000004, - r16uint = 0x00000005, - r16sint = 0x00000006, - r16float = 0x00000007, - rg8unorm = 0x00000008, - rg8snorm = 0x00000009, - rg8uint = 0x0000000a, - rg8sint = 0x0000000b, - r32float = 0x0000000c, - r32uint = 0x0000000d, - r32sint = 0x0000000e, - rg16uint = 0x0000000f, - rg16sint = 0x00000010, - rg16float = 0x00000011, - rgba8unorm = 0x00000012, - rgba8unorm_srgb = 0x00000013, - rgba8snorm = 0x00000014, - rgba8uint = 0x00000015, - rgba8sint = 0x00000016, - bgra8unorm = 0x00000017, - bgra8unorm_srgb = 0x00000018, - rgb10a2uint = 0x00000019, - rgb10a2unorm = 0x0000001a, - rg11b10ufloat = 0x0000001b, - rgb9e5ufloat = 0x0000001c, - rg32float = 0x0000001d, - rg32uint = 0x0000001e, - rg32sint = 0x0000001f, - rgba16uint = 0x00000020, - rgba16sint = 0x00000021, - rgba16float = 0x00000022, - rgba32float = 0x00000023, - rgba32uint = 0x00000024, - rgba32sint = 0x00000025, - stencil8 = 0x00000026, - depth16unorm = 0x00000027, - depth24plus = 0x00000028, - depth24plusstencil8 = 0x00000029, - depth32float = 0x0000002a, + undefined = 0x00000000, + r8unorm = 0x00000001, + r8snorm = 0x00000002, + r8uint = 0x00000003, + r8sint = 0x00000004, + r16uint = 0x00000005, + r16sint = 0x00000006, + r16float = 0x00000007, + rg8unorm = 0x00000008, + rg8snorm = 0x00000009, + rg8uint = 0x0000000a, + rg8sint = 0x0000000b, + r32float = 0x0000000c, + r32uint = 0x0000000d, + r32sint = 0x0000000e, + rg16uint = 0x0000000f, + rg16sint = 0x00000010, + rg16float = 0x00000011, + rgba8unorm = 0x00000012, + rgba8unorm_srgb = 0x00000013, + rgba8snorm = 0x00000014, + rgba8uint = 0x00000015, + rgba8sint = 0x00000016, + bgra8unorm = 0x00000017, + bgra8unorm_srgb = 0x00000018, + rgb10a2uint = 0x00000019, + rgb10a2unorm = 0x0000001a, + rg11b10ufloat = 0x0000001b, + rgb9e5ufloat = 0x0000001c, + rg32float = 0x0000001d, + rg32uint = 0x0000001e, + rg32sint = 0x0000001f, + rgba16uint = 0x00000020, + rgba16sint = 0x00000021, + rgba16float = 0x00000022, + rgba32float = 0x00000023, + rgba32uint = 0x00000024, + rgba32sint = 0x00000025, + stencil8 = 0x00000026, + depth16unorm = 0x00000027, + depth24plus = 0x00000028, + depth24plusstencil8 = 0x00000029, + depth32float = 0x0000002a, depth32floatstencil8 = 0x0000002b, - bc1rgbaunorm = 0x0000002c, - bc1rgbaunorm_srgb = 0x0000002d, - bc2rgbaunorm = 0x0000002e, - bc2rgbaunorm_srgb = 0x0000002f, - bc3rgbaunorm = 0x00000030, - bc3rgbaunorm_srgb = 0x00000031, - bc4runorm = 0x00000032, - bc4rsnorm = 0x00000033, - bc5rgunorm = 0x00000034, - bc5rgsnorm = 0x00000035, - bc6hrgbufloat = 0x00000036, - bc6hrgbfloat = 0x00000037, - bc7rgbaunorm = 0x00000038, - bc7rgbaunorm_srgb = 0x00000039, - etc2rgb8unorm = 0x0000003a, - etc2rgb8unorm_srgb = 0x0000003b, - etc2rgb8a1unorm = 0x0000003c, + bc1rgbaunorm = 0x0000002c, + bc1rgbaunorm_srgb = 0x0000002d, + bc2rgbaunorm = 0x0000002e, + bc2rgbaunorm_srgb = 0x0000002f, + bc3rgbaunorm = 0x00000030, + bc3rgbaunorm_srgb = 0x00000031, + bc4runorm = 0x00000032, + bc4rsnorm = 0x00000033, + bc5rgunorm = 0x00000034, + bc5rgsnorm = 0x00000035, + bc6hrgbufloat = 0x00000036, + bc6hrgbfloat = 0x00000037, + bc7rgbaunorm = 0x00000038, + bc7rgbaunorm_srgb = 0x00000039, + etc2rgb8unorm = 0x0000003a, + etc2rgb8unorm_srgb = 0x0000003b, + etc2rgb8a1unorm = 0x0000003c, etc2rgb8a1unorm_srgb = 0x0000003d, - etc2rgba8unorm = 0x0000003e, - etc2rgba8unorm_srgb = 0x0000003f, - eacr11unorm = 0x00000040, - eacr11snorm = 0x00000041, - eacrg11unorm = 0x00000042, - eacrg11snorm = 0x00000043, - astc4x4unorm = 0x00000044, - astc4x4unorm_srgb = 0x00000045, - astc5x4unorm = 0x00000046, - astc5x4unorm_srgb = 0x00000047, - astc5x5unorm = 0x00000048, - astc5x5unorm_srgb = 0x00000049, - astc6x5unorm = 0x0000004a, - astc6x5unorm_srgb = 0x0000004b, - astc6x6unorm = 0x0000004c, - astc6x6unorm_srgb = 0x0000004d, - astc8x5unorm = 0x0000004e, - astc8x5unorm_srgb = 0x0000004f, - astc8x6unorm = 0x00000050, - astc8x6unorm_srgb = 0x00000051, - astc8x8unorm = 0x00000052, - astc8x8unorm_srgb = 0x00000053, - astc10x5unorm = 0x00000054, - astc10x5unorm_srgb = 0x00000055, - astc10x6unorm = 0x00000056, - astc10x6unorm_srgb = 0x00000057, - astc10x8unorm = 0x00000058, - astc10x8unorm_srgb = 0x00000059, - astc10x10unorm = 0x0000005a, - astc10x10unorm_srgb = 0x0000005b, - astc12x10unorm = 0x0000005c, - astc12x10unorm_srgb = 0x0000005d, - astc12x12unorm = 0x0000005e, - astc12x12unorm_srgb = 0x0000005f, + etc2rgba8unorm = 0x0000003e, + etc2rgba8unorm_srgb = 0x0000003f, + eacr11unorm = 0x00000040, + eacr11snorm = 0x00000041, + eacrg11unorm = 0x00000042, + eacrg11snorm = 0x00000043, + astc4x4unorm = 0x00000044, + astc4x4unorm_srgb = 0x00000045, + astc5x4unorm = 0x00000046, + astc5x4unorm_srgb = 0x00000047, + astc5x5unorm = 0x00000048, + astc5x5unorm_srgb = 0x00000049, + astc6x5unorm = 0x0000004a, + astc6x5unorm_srgb = 0x0000004b, + astc6x6unorm = 0x0000004c, + astc6x6unorm_srgb = 0x0000004d, + astc8x5unorm = 0x0000004e, + astc8x5unorm_srgb = 0x0000004f, + astc8x6unorm = 0x00000050, + astc8x6unorm_srgb = 0x00000051, + astc8x8unorm = 0x00000052, + astc8x8unorm_srgb = 0x00000053, + astc10x5unorm = 0x00000054, + astc10x5unorm_srgb = 0x00000055, + astc10x6unorm = 0x00000056, + astc10x6unorm_srgb = 0x00000057, + astc10x8unorm = 0x00000058, + astc10x8unorm_srgb = 0x00000059, + astc10x10unorm = 0x0000005a, + astc10x10unorm_srgb = 0x0000005b, + astc12x10unorm = 0x0000005c, + astc12x10unorm_srgb = 0x0000005d, + astc12x12unorm = 0x0000005e, + astc12x12unorm_srgb = 0x0000005f, + + // texture_format16bit_norm + r16unorm = 0x00030001, + r16snorm = 0x00030002, + rg16unorm = 0x00030003, + rg16snorm = 0x00030004, + rgba16unorm = 0x00030005, + rgba16snorm = 0x00030006, + + // texture_format_nv12 + nv12 = 0x00030007, }; - enum class usage : std::uint32_t + enum class usage : std::uint64_t { - none = 0x00000000, - copy_src = 0x00000001, - copy_dst = 0x00000002, - texture_binding = 0x00000004, - storage_binding = 0x00000008, + none = 0x00000000, + copy_src = 0x00000001, + copy_dst = 0x00000002, + texture_binding = 0x00000004, + storage_binding = 0x00000008, render_attachment = 0x00000010, }; enum class dimension : std::uint32_t { - _1d = 0x00000000, - _2d = 0x00000001, - _3d = 0x00000002, + undefined = 0x00000000, + _1d = 0x00000001, + _2d = 0x00000002, + _3d = 0x00000003, }; enum class aspect : std::uint32_t { - all = 0x00000000, - stencil_only = 0x00000001, - depth_only = 0x00000002, + undefined = 0x00000000, + all = 0x00000001, + stencil_only = 0x00000002, + depth_only = 0x00000003, }; enum class sample_type : std::uint32_t { - undefined = 0x00000000, - _float = 0x00000001, - unfilterable_float = 0x00000002, - depth = 0x00000003, - sint = 0x00000004, - uint = 0x00000005, + binding_not_used = 0x00000000, + undefined = 0x00000001, + _float = 0x00000002, + unfilterable_float = 0x00000003, + depth = 0x00000004, + sint = 0x00000005, + uint = 0x00000006, }; struct descriptor @@ -164,14 +178,6 @@ namespace psemek::wgpu std::vector view_formats = {}; }; - struct data_layout - { - std::vector chain = {}; - std::uint64_t offset = 0; - std::uint32_t bytes_per_row; - std::uint32_t rows_per_image; - }; - texture_view create_view(texture_view::descriptor const & desc); void destroy(); std::uint32_t get_width(); diff --git a/libs/wgpu/include/psemek/wgpu/texture_view.hpp b/libs/wgpu/include/psemek/wgpu/texture_view.hpp index 23ec5886..48cb3f2c 100644 --- a/libs/wgpu/include/psemek/wgpu/texture_view.hpp +++ b/libs/wgpu/include/psemek/wgpu/texture_view.hpp @@ -15,13 +15,13 @@ namespace psemek::wgpu enum class dimension : std::uint32_t { - undefined = 0x00000000, - _1d = 0x00000001, - _2d = 0x00000002, - _2d_array = 0x00000003, - cube = 0x00000004, + undefined = 0x00000000, + _1d = 0x00000001, + _2d = 0x00000002, + _2d_array = 0x00000003, + cube = 0x00000004, cube_array = 0x00000005, - _3d = 0x00000006, + _3d = 0x00000006, }; // Defined in wgpu/texture.hpp to break circular dependencies diff --git a/libs/wgpu/source/adapter.cpp b/libs/wgpu/source/adapter.cpp index b906b8b5..5476b437 100644 --- a/libs/wgpu/source/adapter.cpp +++ b/libs/wgpu/source/adapter.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #include @@ -8,37 +10,67 @@ namespace psemek::wgpu std::vector adapter::enumerate_features() { - std::size_t count = wgpuAdapterEnumerateFeatures((WGPUAdapter)get(), nullptr); - std::vector result(count); - wgpuAdapterEnumerateFeatures((WGPUAdapter)get(), (WGPUFeatureName *)result.data()); - return result; + WGPUSupportedFeatures supported_features; + wgpuAdapterGetFeatures((WGPUAdapter)get(), &supported_features); + return std::vector((feature const *)supported_features.features, (feature const *)(supported_features.features) + supported_features.featureCount); } limits adapter::get_limits() { - WGPUSupportedLimits limits = {}; - wgpuAdapterGetLimits((WGPUAdapter)get(), &limits); + WGPULimits limits = {}; + detail::check_status("wgpuAdapterGetLimits", wgpuAdapterGetLimits((WGPUAdapter)get(), &limits)); // TODO: support out chain wgpu::limits result; - static_assert(sizeof(result) == sizeof(limits.limits)); - std::memcpy((char *)&result, (char *)&limits.limits, sizeof(result)); + result.max_texture_dimension_1D = limits.maxTextureDimension1D; + result.max_texture_dimension_2D = limits.maxTextureDimension2D; + result.max_texture_dimension_3D = limits.maxTextureDimension3D; + result.max_texture_array_layers = limits.maxTextureArrayLayers; + result.max_bind_groups = limits.maxBindGroups; + result.max_bind_groups_plus_vertex_buffers = limits.maxBindGroupsPlusVertexBuffers; + result.max_bindings_per_bind_group = limits.maxBindingsPerBindGroup; + result.max_dynamic_uniform_buffers_per_pipeline_layout = limits.maxDynamicUniformBuffersPerPipelineLayout; + result.max_dynamic_storage_buffers_per_pipeline_layout = limits.maxDynamicStorageBuffersPerPipelineLayout; + result.max_sampled_textures_per_shader_stage = limits.maxSampledTexturesPerShaderStage; + result.max_samplers_per_shader_stage = limits.maxSamplersPerShaderStage; + result.max_storage_buffers_per_shader_stage = limits.maxStorageBuffersPerShaderStage; + result.max_storage_textures_per_shader_stage = limits.maxStorageTexturesPerShaderStage; + result.max_uniform_buffers_per_shader_stage = limits.maxUniformBuffersPerShaderStage; + result.max_uniform_buffer_binding_size = limits.maxUniformBufferBindingSize; + result.max_storage_buffer_binding_size = limits.maxStorageBufferBindingSize; + result.min_uniform_buffer_offset_alignment = limits.minUniformBufferOffsetAlignment; + result.min_storage_buffer_offset_alignment = limits.minStorageBufferOffsetAlignment; + result.max_vertex_buffers = limits.maxVertexBuffers; + result.max_buffer_size = limits.maxBufferSize; + result.max_vertex_attributes = limits.maxVertexAttributes; + result.max_vertex_buffer_array_stride = limits.maxVertexBufferArrayStride; + result.max_inter_stage_shader_variables = limits.maxInterStageShaderVariables; + result.max_color_attachments = limits.maxColorAttachments; + result.max_color_attachment_bytes_per_sample = limits.maxColorAttachmentBytesPerSample; + result.max_compute_workgroup_storage_size = limits.maxComputeWorkgroupStorageSize; + result.max_compute_invocations_per_workgroup = limits.maxComputeInvocationsPerWorkgroup; + result.max_compute_workgroup_size_x = limits.maxComputeWorkgroupSizeX; + result.max_compute_workgroup_size_y = limits.maxComputeWorkgroupSizeY; + result.max_compute_workgroup_size_z = limits.maxComputeWorkgroupSizeZ; + result.max_compute_workgroups_per_dimension = limits.maxComputeWorkgroupsPerDimension; + return result; } - adapter::properties adapter::get_properties() + adapter::info adapter::get_info() { - WGPUAdapterProperties props; - wgpuAdapterGetProperties((WGPUAdapter)get(), &props); + WGPUAdapterInfo info; + detail::check_status("wgpuAdapterGetInfo", wgpuAdapterGetInfo((WGPUAdapter)get(), &info)); + return { - .vendor_id = props.vendorID, - .vendor_name = props.vendorName, - .architecture = props.architecture, - .device_id = props.deviceID, - .name = props.name, - .driver_description = props.driverDescription, - .adapter_type = (adapter::type)props.adapterType, - .backend_type = (backend_type)props.backendType, + .vendor = std::string_view(info.vendor.data, info.vendor.length), + .architecture = std::string_view(info.architecture.data, info.architecture.length), + .device = std::string_view(info.device.data, info.device.length), + .description = std::string_view(info.description.data, info.description.length), + .backend_type = (backend_type)info.backendType, + .adapter_type = (type)info.adapterType, + .vendor_id = info.vendorID, + .device_id = info.deviceID, }; } @@ -47,49 +79,83 @@ namespace psemek::wgpu return wgpuAdapterHasFeature((WGPUAdapter)get(), (WGPUFeatureName)feature); } - void adapter::request_device(device::descriptor const & descriptor, device::request_callback const & callback) + void adapter::request_device(callback_mode mode, device::descriptor const & descriptor, device::request_callback const & callback) { - WGPURequiredLimits limits = {}; + WGPULimits limits = {}; if (descriptor.required_limits) { - static_assert(sizeof(limits.limits) == sizeof(descriptor.required_limits->limits)); - limits.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(descriptor.required_limits->chain); - std::memcpy(&limits.limits, &descriptor.required_limits->limits, sizeof(limits.limits)); + limits.nextInChain = (WGPUChainedStructOut *)detail::fill_chain_out(descriptor.required_limits->chain); + + limits.maxTextureDimension1D = descriptor.required_limits->limits.max_texture_dimension_1D; + limits.maxTextureDimension2D = descriptor.required_limits->limits.max_texture_dimension_2D; + limits.maxTextureDimension3D = descriptor.required_limits->limits.max_texture_dimension_3D; + limits.maxTextureArrayLayers = descriptor.required_limits->limits.max_texture_array_layers; + limits.maxBindGroups = descriptor.required_limits->limits.max_bind_groups; + limits.maxBindGroupsPlusVertexBuffers = descriptor.required_limits->limits.max_bind_groups_plus_vertex_buffers; + limits.maxBindingsPerBindGroup = descriptor.required_limits->limits.max_bindings_per_bind_group; + limits.maxDynamicUniformBuffersPerPipelineLayout = descriptor.required_limits->limits.max_dynamic_uniform_buffers_per_pipeline_layout; + limits.maxDynamicStorageBuffersPerPipelineLayout = descriptor.required_limits->limits.max_dynamic_storage_buffers_per_pipeline_layout; + limits.maxSampledTexturesPerShaderStage = descriptor.required_limits->limits.max_sampled_textures_per_shader_stage; + limits.maxSamplersPerShaderStage = descriptor.required_limits->limits.max_samplers_per_shader_stage; + limits.maxStorageBuffersPerShaderStage = descriptor.required_limits->limits.max_storage_buffers_per_shader_stage; + limits.maxStorageTexturesPerShaderStage = descriptor.required_limits->limits.max_storage_textures_per_shader_stage; + limits.maxUniformBuffersPerShaderStage = descriptor.required_limits->limits.max_uniform_buffers_per_shader_stage; + limits.maxUniformBufferBindingSize = descriptor.required_limits->limits.max_uniform_buffer_binding_size; + limits.maxStorageBufferBindingSize = descriptor.required_limits->limits.max_storage_buffer_binding_size; + limits.minUniformBufferOffsetAlignment = descriptor.required_limits->limits.min_uniform_buffer_offset_alignment; + limits.minStorageBufferOffsetAlignment = descriptor.required_limits->limits.min_storage_buffer_offset_alignment; + limits.maxVertexBuffers = descriptor.required_limits->limits.max_vertex_buffers; + limits.maxBufferSize = descriptor.required_limits->limits.max_buffer_size; + limits.maxVertexAttributes = descriptor.required_limits->limits.max_vertex_attributes; + limits.maxVertexBufferArrayStride = descriptor.required_limits->limits.max_vertex_buffer_array_stride; + limits.maxInterStageShaderVariables = descriptor.required_limits->limits.max_inter_stage_shader_variables; + limits.maxColorAttachments = descriptor.required_limits->limits.max_color_attachments; + limits.maxColorAttachmentBytesPerSample = descriptor.required_limits->limits.max_color_attachment_bytes_per_sample; + limits.maxComputeWorkgroupStorageSize = descriptor.required_limits->limits.max_compute_workgroup_storage_size; + limits.maxComputeInvocationsPerWorkgroup = descriptor.required_limits->limits.max_compute_invocations_per_workgroup; + limits.maxComputeWorkgroupSizeX = descriptor.required_limits->limits.max_compute_workgroup_size_x; + limits.maxComputeWorkgroupSizeY = descriptor.required_limits->limits.max_compute_workgroup_size_y; + limits.maxComputeWorkgroupSizeZ = descriptor.required_limits->limits.max_compute_workgroup_size_z; + limits.maxComputeWorkgroupsPerDimension = descriptor.required_limits->limits.max_compute_workgroups_per_dimension; } - auto device_lost_userdata = new device::lost_callback(descriptor.lost_callback); - - auto device_lost_real_callback = [](WGPUDeviceLostReason reason, char const * message, void * userdata) - { - std::unique_ptr callback((device::lost_callback *)userdata); - if (*callback) (*callback)((device::lost_reason)reason, message ? message : ""); - }; - WGPUDeviceDescriptor device_desc = {}; device_desc.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(descriptor.chain); - device_desc.label = descriptor.label.data(); + device_desc.label = detail::to_string_view(descriptor.label); device_desc.requiredFeatureCount = descriptor.required_features.size(); device_desc.requiredFeatures = (WGPUFeatureName const *)descriptor.required_features.data(); device_desc.requiredLimits = descriptor.required_limits ? &limits : nullptr; device_desc.defaultQueue.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(descriptor.default_queue.chain); - device_desc.defaultQueue.label = descriptor.default_queue.label.data(); - device_desc.deviceLostCallback = device_lost_real_callback; - device_desc.deviceLostUserdata = device_lost_userdata; + device_desc.defaultQueue.label = detail::to_string_view(descriptor.default_queue.label); + device_desc.deviceLostCallbackInfo.mode = (WGPUCallbackMode)descriptor.lost_callback_mode; + device_desc.deviceLostCallbackInfo.callback = [](WGPUDevice const *, WGPUDeviceLostReason reason, WGPUStringView message, void * userdata, void *) + { + std::unique_ptr callback{(device::lost_callback *)userdata}; + if (*callback) (*callback)((device::lost_reason)reason, std::string(message.data, message.length)); + }; + device_desc.deviceLostCallbackInfo.userdata1 = new device::lost_callback(descriptor.lost_callback); + device_desc.uncapturedErrorCallbackInfo.callback = [](WGPUDevice const *, WGPUErrorType type, WGPUStringView message, void * userdata, void *) + { + auto callback = (device::uncaptured_error_callback *)userdata; + if (*callback) (*callback)((error_type)type, std::string(message.data, message.length)); + }; + device_desc.uncapturedErrorCallbackInfo.userdata1 = new device::uncaptured_error_callback(descriptor.uncaptured_error_callback); - auto userdata = new device::request_callback(callback); - - auto real_callback = [](WGPURequestDeviceStatus status, WGPUDevice device, char const * message, void * userdata) + WGPURequestDeviceCallbackInfo callback_info = {}; + callback_info.mode = (WGPUCallbackMode)mode; + callback_info.callback = [](WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView message, void * userdata, void *) { std::unique_ptr callback{(device::request_callback *)userdata}; - if (*callback) (*callback)((device::request_status)status, wgpu::device(device), message ? message : ""); + if (*callback) (*callback)((device::request_status)status, wgpu::device(device), std::string(message.data, message.length)); }; + callback_info.userdata1 = new device::request_callback(callback); - wgpuAdapterRequestDevice((WGPUAdapter)get(), &device_desc, real_callback, userdata); + wgpuAdapterRequestDevice((WGPUAdapter)get(), &device_desc, callback_info); } void adapter::reference(void * ptr) { - wgpuAdapterReference((WGPUAdapter)ptr); + wgpuAdapterAddRef((WGPUAdapter)ptr); } void adapter::release(void * ptr) diff --git a/libs/wgpu/source/bind_group.cpp b/libs/wgpu/source/bind_group.cpp index 0e51baf5..c4901186 100644 --- a/libs/wgpu/source/bind_group.cpp +++ b/libs/wgpu/source/bind_group.cpp @@ -1,4 +1,5 @@ #include +#include #include namespace psemek::wgpu @@ -6,12 +7,12 @@ namespace psemek::wgpu void bind_group::set_label(std::string const & label) { - wgpuBindGroupSetLabel((WGPUBindGroup)get(), label.data()); + wgpuBindGroupSetLabel((WGPUBindGroup)get(), detail::to_string_view(label)); } void bind_group::reference(void * ptr) { - wgpuBindGroupReference((WGPUBindGroup)ptr); + wgpuBindGroupAddRef((WGPUBindGroup)ptr); } void bind_group::release(void * ptr) diff --git a/libs/wgpu/source/bind_group_layout.cpp b/libs/wgpu/source/bind_group_layout.cpp index a32e18e5..e640837c 100644 --- a/libs/wgpu/source/bind_group_layout.cpp +++ b/libs/wgpu/source/bind_group_layout.cpp @@ -1,4 +1,5 @@ #include +#include #include namespace psemek::wgpu @@ -6,12 +7,12 @@ namespace psemek::wgpu void bind_group_layout::set_label(std::string const & label) { - wgpuBindGroupLayoutSetLabel((WGPUBindGroupLayout)get(), label.data()); + wgpuBindGroupLayoutSetLabel((WGPUBindGroupLayout)get(), detail::to_string_view(label)); } void bind_group_layout::reference(void * ptr) { - wgpuBindGroupLayoutReference((WGPUBindGroupLayout)ptr); + wgpuBindGroupLayoutAddRef((WGPUBindGroupLayout)ptr); } void bind_group_layout::release(void * ptr) diff --git a/libs/wgpu/source/buffer.cpp b/libs/wgpu/source/buffer.cpp index f0bab4e1..ff6ec097 100644 --- a/libs/wgpu/source/buffer.cpp +++ b/libs/wgpu/source/buffer.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -36,17 +37,18 @@ namespace psemek::wgpu return wgpuBufferGetMappedRange((WGPUBuffer)get(), offset, size); } - void buffer::map_async(map_mode flags, std::size_t offset, std::size_t size, map_callback const & callback) + void buffer::map_async(callback_mode mode, map_mode flags, std::size_t offset, std::size_t size, map_callback const & callback) { - auto userdata = new map_callback(callback); - - auto real_callback = [](WGPUBufferMapAsyncStatus status, void * userdata) + WGPUBufferMapCallbackInfo callback_info = {}; + callback_info.mode = (WGPUCallbackMode)mode; + callback_info.callback = [](WGPUMapAsyncStatus status, WGPUStringView message, void * userdata, void *) { std::unique_ptr callback((map_callback *)userdata); - if (*callback) (*callback)((map_async_status)status); + if (*callback) (*callback)((map_async_status)status, std::string_view(message.data, message.length)); }; + callback_info.userdata1 = new map_callback(callback); - wgpuBufferMapAsync((WGPUBuffer)get(), (WGPUMapModeFlags)flags, offset, size, real_callback, userdata); + wgpuBufferMapAsync((WGPUBuffer)get(), (WGPUMapMode)flags, offset, size, callback_info); } void buffer::unmap() @@ -56,12 +58,12 @@ namespace psemek::wgpu void buffer::set_label(std::string const & label) { - wgpuBufferSetLabel((WGPUBuffer)get(), label.data()); + wgpuBufferSetLabel((WGPUBuffer)get(), detail::to_string_view(label)); } void buffer::reference(void * ptr) { - wgpuBufferReference((WGPUBuffer)ptr); + wgpuBufferAddRef((WGPUBuffer)ptr); } void buffer::release(void * ptr) diff --git a/libs/wgpu/source/chained_struct.cpp b/libs/wgpu/source/chained_struct.cpp index 5090add2..8a1c3d02 100644 --- a/libs/wgpu/source/chained_struct.cpp +++ b/libs/wgpu/source/chained_struct.cpp @@ -30,4 +30,30 @@ namespace psemek::wgpu::detail return first; } + void * fill_chain_out(std::vector const & chain) + { + WGPUChainedStructOut * first = nullptr; + WGPUChainedStructOut * current = nullptr; + + for (auto const & s : chain) + { + auto p = (WGPUChainedStructOut *)s.ptr(); + if (!first) + { + first = p; + current = p; + } + else + { + current->next = p; + current = p; + } + } + + if (current) + current->next = nullptr; + + return first; + } + } diff --git a/libs/wgpu/source/command_buffer.cpp b/libs/wgpu/source/command_buffer.cpp index 9a2c43d2..b27f781b 100644 --- a/libs/wgpu/source/command_buffer.cpp +++ b/libs/wgpu/source/command_buffer.cpp @@ -1,4 +1,5 @@ #include +#include #include namespace psemek::wgpu @@ -6,12 +7,12 @@ namespace psemek::wgpu void command_buffer::set_label(std::string const & label) { - wgpuCommandBufferSetLabel((WGPUCommandBuffer)get(), label.data()); + wgpuCommandBufferSetLabel((WGPUCommandBuffer)get(), detail::to_string_view(label)); } void command_buffer::reference(void * ptr) { - wgpuCommandBufferReference((WGPUCommandBuffer)ptr); + wgpuCommandBufferAddRef((WGPUCommandBuffer)ptr); } void command_buffer::release(void * ptr) diff --git a/libs/wgpu/source/command_encoder.cpp b/libs/wgpu/source/command_encoder.cpp index 9a8b63b6..e9dfbe4f 100644 --- a/libs/wgpu/source/command_encoder.cpp +++ b/libs/wgpu/source/command_encoder.cpp @@ -1,4 +1,5 @@ #include +#include #include namespace psemek::wgpu @@ -16,7 +17,7 @@ namespace psemek::wgpu WGPUComputePassDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); - descriptor.label = desc.label.data(); + descriptor.label = detail::to_string_view(desc.label); descriptor.timestampWrites = desc.timestamp_writes ? ×tamp_writes : nullptr; return compute_pass_encoder(wgpuCommandEncoderBeginComputePass((WGPUCommandEncoder)get(), &descriptor)); @@ -30,6 +31,7 @@ namespace psemek::wgpu auto & dst = color_attachments.emplace_back(); dst.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(src.chain); dst.view = (WGPUTextureView)src.view.get(); + dst.depthSlice = src.depth_slice; dst.resolveTarget = (WGPUTextureView)src.resolve_target.get(); dst.loadOp = (WGPULoadOp)src.load_op; dst.storeOp = (WGPUStoreOp)src.store_op; @@ -43,11 +45,11 @@ namespace psemek::wgpu depth_stencil_attachment.depthLoadOp = (WGPULoadOp)desc.depth_stencil_attachment->depth_load_op; depth_stencil_attachment.depthStoreOp = (WGPUStoreOp)desc.depth_stencil_attachment->depth_store_op; depth_stencil_attachment.depthClearValue = desc.depth_stencil_attachment->depth_clear_value; - depth_stencil_attachment.depthReadOnly = desc.depth_stencil_attachment->depth_read_only; + depth_stencil_attachment.depthReadOnly = desc.depth_stencil_attachment->depth_read_only ? 1 : 0; depth_stencil_attachment.stencilLoadOp = (WGPULoadOp)desc.depth_stencil_attachment->stencil_load_op; depth_stencil_attachment.stencilStoreOp = (WGPUStoreOp)desc.depth_stencil_attachment->stencil_store_op; depth_stencil_attachment.stencilClearValue = desc.depth_stencil_attachment->stencil_clear_value; - depth_stencil_attachment.stencilReadOnly = desc.depth_stencil_attachment->stencil_clear_value; + depth_stencil_attachment.stencilReadOnly = desc.depth_stencil_attachment->stencil_read_only ? 1 : 0; } WGPURenderPassTimestampWrites timestamp_writes = {}; @@ -60,7 +62,7 @@ namespace psemek::wgpu WGPURenderPassDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); - descriptor.label = desc.label.data(); + descriptor.label = detail::to_string_view(desc.label); descriptor.colorAttachmentCount = color_attachments.size(); descriptor.colorAttachments = color_attachments.data(); descriptor.depthStencilAttachment = desc.depth_stencil_attachment ? &depth_stencil_attachment : nullptr; @@ -80,73 +82,65 @@ namespace psemek::wgpu wgpuCommandEncoderCopyBufferToBuffer((WGPUCommandEncoder)get(), (WGPUBuffer)source.get(), source_offset, (WGPUBuffer)destination.get(), destination_offset, size); } - void command_encoder::copy_buffer_to_texture(image_copy_buffer const & source, image_copy_texture const & destination, math::vector const & extent) + void command_encoder::copy_buffer_to_texture(texel_copy_buffer_info const & source, texel_copy_texture_info const & destination, math::vector const & extent) { - WGPUImageCopyBuffer image_copy_src = {}; - image_copy_src.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(source.chain); - image_copy_src.layout.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(source.layout.chain); - image_copy_src.layout.offset = source.layout.offset; - image_copy_src.layout.bytesPerRow = source.layout.bytes_per_row; - image_copy_src.layout.rowsPerImage = source.layout.rows_per_image; - image_copy_src.buffer = (WGPUBuffer)source.buffer.get(); + WGPUTexelCopyBufferInfo texel_copy_buffer_info_src = {}; + texel_copy_buffer_info_src.layout.offset = source.layout.offset; + texel_copy_buffer_info_src.layout.bytesPerRow = source.layout.bytes_per_row; + texel_copy_buffer_info_src.layout.rowsPerImage = source.layout.rows_per_image; + texel_copy_buffer_info_src.buffer = (WGPUBuffer)source.buffer.get(); - WGPUImageCopyTexture image_copy_dst = {}; - image_copy_dst.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(destination.chain); - image_copy_dst.texture = (WGPUTexture)destination.texture.get(); - image_copy_dst.mipLevel = destination.mip_level; - image_copy_dst.origin = {destination.origin[0], destination.origin[1], destination.origin[2]}; - image_copy_dst.aspect = (WGPUTextureAspect)destination.aspect; + WGPUTexelCopyTextureInfo texel_copy_texture_info_src_dst = {}; + texel_copy_texture_info_src_dst.texture = (WGPUTexture)destination.texture.get(); + texel_copy_texture_info_src_dst.mipLevel = destination.mip_level; + texel_copy_texture_info_src_dst.origin = {destination.origin[0], destination.origin[1], destination.origin[2]}; + texel_copy_texture_info_src_dst.aspect = (WGPUTextureAspect)destination.aspect; - wgpuCommandEncoderCopyBufferToTexture((WGPUCommandEncoder)get(), &image_copy_src, &image_copy_dst, (WGPUExtent3D const *)(&extent)); + wgpuCommandEncoderCopyBufferToTexture((WGPUCommandEncoder)get(), &texel_copy_buffer_info_src, &texel_copy_texture_info_src_dst, (WGPUExtent3D const *)(&extent)); } - void command_encoder::copy_texture_to_buffer(image_copy_texture const & source, image_copy_buffer const & destination, math::vector const & extent) + void command_encoder::copy_texture_to_buffer(texel_copy_texture_info const & source, texel_copy_buffer_info const & destination, math::vector const & extent) { - WGPUImageCopyTexture image_copy_src = {}; - image_copy_src.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(source.chain); - image_copy_src.texture = (WGPUTexture)source.texture.get(); - image_copy_src.mipLevel = source.mip_level; - image_copy_src.origin = {source.origin[0], source.origin[1], source.origin[2]}; - image_copy_src.aspect = (WGPUTextureAspect)source.aspect; + WGPUTexelCopyTextureInfo texel_copy_texture_info_src = {}; + texel_copy_texture_info_src.texture = (WGPUTexture)source.texture.get(); + texel_copy_texture_info_src.mipLevel = source.mip_level; + texel_copy_texture_info_src.origin = {source.origin[0], source.origin[1], source.origin[2]}; + texel_copy_texture_info_src.aspect = (WGPUTextureAspect)source.aspect; - WGPUImageCopyBuffer image_copy_dst = {}; - image_copy_dst.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(destination.chain); - image_copy_dst.layout.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(destination.layout.chain); - image_copy_dst.layout.offset = destination.layout.offset; - image_copy_dst.layout.bytesPerRow = destination.layout.bytes_per_row; - image_copy_dst.layout.rowsPerImage = destination.layout.rows_per_image; - image_copy_dst.buffer = (WGPUBuffer)destination.buffer.get(); + WGPUTexelCopyBufferInfo texel_copy_buffer_info_dst = {}; + texel_copy_buffer_info_dst.layout.offset = destination.layout.offset; + texel_copy_buffer_info_dst.layout.bytesPerRow = destination.layout.bytes_per_row; + texel_copy_buffer_info_dst.layout.rowsPerImage = destination.layout.rows_per_image; + texel_copy_buffer_info_dst.buffer = (WGPUBuffer)destination.buffer.get(); - wgpuCommandEncoderCopyTextureToBuffer((WGPUCommandEncoder)get(), &image_copy_src, &image_copy_dst, (WGPUExtent3D const *)(&extent)); + wgpuCommandEncoderCopyTextureToBuffer((WGPUCommandEncoder)get(), &texel_copy_texture_info_src, &texel_copy_buffer_info_dst, (WGPUExtent3D const *)(&extent)); } - void command_encoder::copy_texture_to_texture(image_copy_texture const & source, image_copy_texture const & destination, math::vector const & extent) + void command_encoder::copy_texture_to_texture(texel_copy_texture_info const & source, texel_copy_texture_info const & destination, math::vector const & extent) { - WGPUImageCopyTexture image_copy_src = {}; - image_copy_src.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(source.chain); - image_copy_src.texture = (WGPUTexture)source.texture.get(); - image_copy_src.mipLevel = source.mip_level; - image_copy_src.origin = {source.origin[0], source.origin[1], source.origin[2]}; - image_copy_src.aspect = (WGPUTextureAspect)source.aspect; + WGPUTexelCopyTextureInfo texel_copy_texture_info_src = {}; + texel_copy_texture_info_src.texture = (WGPUTexture)source.texture.get(); + texel_copy_texture_info_src.mipLevel = source.mip_level; + texel_copy_texture_info_src.origin = {source.origin[0], source.origin[1], source.origin[2]}; + texel_copy_texture_info_src.aspect = (WGPUTextureAspect)source.aspect; - WGPUImageCopyTexture image_copy_dst = {}; - image_copy_dst.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(destination.chain); - image_copy_dst.texture = (WGPUTexture)destination.texture.get(); - image_copy_dst.mipLevel = destination.mip_level; - image_copy_dst.origin = {destination.origin[0], destination.origin[1], destination.origin[2]}; - image_copy_dst.aspect = (WGPUTextureAspect)destination.aspect; + WGPUTexelCopyTextureInfo texel_copy_texture_info_dst = {}; + texel_copy_texture_info_dst.texture = (WGPUTexture)destination.texture.get(); + texel_copy_texture_info_dst.mipLevel = destination.mip_level; + texel_copy_texture_info_dst.origin = {destination.origin[0], destination.origin[1], destination.origin[2]}; + texel_copy_texture_info_dst.aspect = (WGPUTextureAspect)destination.aspect; - wgpuCommandEncoderCopyTextureToTexture((WGPUCommandEncoder)get(), &image_copy_src, &image_copy_dst, (WGPUExtent3D const *)(&extent)); + wgpuCommandEncoderCopyTextureToTexture((WGPUCommandEncoder)get(), &texel_copy_texture_info_src, &texel_copy_texture_info_dst, (WGPUExtent3D const *)(&extent)); } void command_encoder::insert_debug_marker(std::string const & marker_label) { - wgpuCommandEncoderInsertDebugMarker((WGPUCommandEncoder)get(), marker_label.data()); + wgpuCommandEncoderInsertDebugMarker((WGPUCommandEncoder)get(), detail::to_string_view(marker_label)); } void command_encoder::push_debug_group(std::string const & group_label) { - wgpuCommandEncoderPushDebugGroup((WGPUCommandEncoder)get(), group_label.data()); + wgpuCommandEncoderPushDebugGroup((WGPUCommandEncoder)get(), detail::to_string_view(group_label)); } void command_encoder::pop_debug_group() @@ -168,18 +162,18 @@ namespace psemek::wgpu { WGPUCommandBufferDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); - descriptor.label = desc.label.data(); + descriptor.label = detail::to_string_view(desc.label); return command_buffer(wgpuCommandEncoderFinish((WGPUCommandEncoder)get(), &descriptor)); } void command_encoder::set_label(std::string const & label) { - wgpuCommandEncoderSetLabel((WGPUCommandEncoder)get(), label.data()); + wgpuCommandEncoderSetLabel((WGPUCommandEncoder)get(), detail::to_string_view(label)); } void command_encoder::reference(void * ptr) { - wgpuCommandEncoderReference((WGPUCommandEncoder)ptr); + wgpuCommandEncoderAddRef((WGPUCommandEncoder)ptr); } void command_encoder::release(void * ptr) diff --git a/libs/wgpu/source/compute_pass_encoder.cpp b/libs/wgpu/source/compute_pass_encoder.cpp index 49d1de33..f3ee8b53 100644 --- a/libs/wgpu/source/compute_pass_encoder.cpp +++ b/libs/wgpu/source/compute_pass_encoder.cpp @@ -1,5 +1,7 @@ #include +#include #include +#include namespace psemek::wgpu { @@ -24,14 +26,24 @@ namespace psemek::wgpu wgpuComputePassEncoderDispatchWorkgroupsIndirect((WGPUComputePassEncoder)get(), (WGPUBuffer)indirect_buffer.get(), offset); } + void compute_pass_encoder::set_push_constants(std::uint32_t offset, util::span data) + { + wgpuComputePassEncoderSetPushConstants((WGPUComputePassEncoder)get(), offset, data.size(), data.data()); + } + + void compute_pass_encoder::write_timestamp(query_set const & query_set, std::uint32_t query_index) + { + wgpuComputePassEncoderWriteTimestamp((WGPUComputePassEncoder)get(), (WGPUQuerySet)query_set.get(), query_index); + } + void compute_pass_encoder::insert_debug_marker(std::string const & marker_label) { - wgpuComputePassEncoderInsertDebugMarker((WGPUComputePassEncoder)get(), marker_label.data()); + wgpuComputePassEncoderInsertDebugMarker((WGPUComputePassEncoder)get(), detail::to_string_view(marker_label)); } void compute_pass_encoder::push_debug_group(std::string const & group_label) { - wgpuComputePassEncoderPushDebugGroup((WGPUComputePassEncoder)get(), group_label.data()); + wgpuComputePassEncoderPushDebugGroup((WGPUComputePassEncoder)get(), detail::to_string_view(group_label)); } void compute_pass_encoder::pop_debug_group() @@ -46,12 +58,12 @@ namespace psemek::wgpu void compute_pass_encoder::set_label(std::string const & label) { - wgpuComputePassEncoderSetLabel((WGPUComputePassEncoder)get(), label.data()); + wgpuComputePassEncoderSetLabel((WGPUComputePassEncoder)get(), detail::to_string_view(label)); } void compute_pass_encoder::reference(void * ptr) { - wgpuComputePassEncoderReference((WGPUComputePassEncoder)ptr); + wgpuComputePassEncoderAddRef((WGPUComputePassEncoder)ptr); } void compute_pass_encoder::release(void * ptr) diff --git a/libs/wgpu/source/compute_pipeline.cpp b/libs/wgpu/source/compute_pipeline.cpp index 7058efc3..178723ad 100644 --- a/libs/wgpu/source/compute_pipeline.cpp +++ b/libs/wgpu/source/compute_pipeline.cpp @@ -1,4 +1,5 @@ #include +#include #include namespace psemek::wgpu @@ -11,12 +12,12 @@ namespace psemek::wgpu void compute_pipeline::set_label(std::string const & label) { - wgpuComputePipelineSetLabel((WGPUComputePipeline)get(), label.data()); + wgpuComputePipelineSetLabel((WGPUComputePipeline)get(), detail::to_string_view(label)); } void compute_pipeline::reference(void * ptr) { - wgpuComputePipelineReference((WGPUComputePipeline)ptr); + wgpuComputePipelineAddRef((WGPUComputePipeline)ptr); } void compute_pipeline::release(void * ptr) diff --git a/libs/wgpu/source/device.cpp b/libs/wgpu/source/device.cpp index 6dced1f7..cf581e1f 100644 --- a/libs/wgpu/source/device.cpp +++ b/libs/wgpu/source/device.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #include @@ -29,7 +31,7 @@ namespace psemek::wgpu WGPUBindGroupDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); - descriptor.label = desc.label.data(); + descriptor.label = detail::to_string_view(desc.label); descriptor.layout = (WGPUBindGroupLayout)desc.layout.get(); descriptor.entryCount = entries.size(); descriptor.entries = entries.data(); @@ -45,7 +47,7 @@ namespace psemek::wgpu auto & entry_out = entries.emplace_back(); entry_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(entry_in.chain); entry_out.binding = entry_in.binding; - entry_out.visibility = (WGPUShaderStageFlags)entry_in.visibility; + entry_out.visibility = (WGPUShaderStage)entry_in.visibility; entry_out.buffer.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(entry_in.buffer.chain); entry_out.buffer.type = (WGPUBufferBindingType)entry_in.buffer.type; entry_out.buffer.hasDynamicOffset = entry_in.buffer.has_dynamic_offset; @@ -64,7 +66,7 @@ namespace psemek::wgpu WGPUBindGroupLayoutDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); - descriptor.label = desc.label.data(); + descriptor.label = detail::to_string_view(desc.label); descriptor.entryCount = entries.size(); descriptor.entries = entries.data(); @@ -75,8 +77,8 @@ namespace psemek::wgpu { WGPUBufferDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); - descriptor.label = desc.label.data(); - descriptor.usage = (WGPUBufferUsageFlags)desc.usage; + descriptor.label = detail::to_string_view(desc.label); + descriptor.usage = (WGPUBufferUsage)desc.usage; descriptor.size = desc.size; descriptor.mappedAtCreation = desc.mapped_at_creation; return buffer(wgpuDeviceCreateBuffer((WGPUDevice)get(), &descriptor)); @@ -86,7 +88,7 @@ namespace psemek::wgpu { WGPUCommandEncoderDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); - descriptor.label = desc.label.data(); + descriptor.label = detail::to_string_view(desc.label); return command_encoder(wgpuDeviceCreateCommandEncoder((WGPUDevice)get(), &descriptor)); } @@ -99,16 +101,16 @@ namespace psemek::wgpu { auto & constant_out = constants.emplace_back(); constant_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(constant_in.chain); - constant_out.key = constant_in.key.data(); + constant_out.key = detail::to_string_view(constant_in.key); constant_out.value = constant_in.value; } descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); - descriptor.label = desc.label.data(); + descriptor.label = detail::to_string_view(desc.label); descriptor.layout = (WGPUPipelineLayout)desc.layout.get(); descriptor.compute.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.compute.chain); descriptor.compute.module = (WGPUShaderModule)desc.compute.module.get(); - descriptor.compute.entryPoint = desc.compute.entry_point.data(); + descriptor.compute.entryPoint = detail::to_string_view(desc.compute.entry_point); descriptor.compute.constantCount = constants.size(); descriptor.compute.constants = constants.data(); } @@ -124,28 +126,29 @@ namespace psemek::wgpu return compute_pipeline(wgpuDeviceCreateComputePipeline((WGPUDevice)get(), &descriptor)); } - void device::create_compute_pipeline_async(compute_pipeline::descriptor const & desc, create_compute_pipeline_async_callback const & callback) + void device::create_compute_pipeline_async(callback_mode mode, compute_pipeline::descriptor const & desc, create_compute_pipeline_async_callback const & callback) { std::vector constants; WGPUComputePipelineDescriptor descriptor = {}; fill_compute_pipeline_descriptor(desc, constants, descriptor); - auto userdata = new create_compute_pipeline_async_callback(callback); - - auto real_callback = [](WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline pipeline, char const * message, void * userdata) + WGPUCreateComputePipelineAsyncCallbackInfo callback_info = {}; + callback_info.mode = (WGPUCallbackMode)mode; + callback_info.callback = [](WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline pipeline, WGPUStringView message, void * userdata, void *) { std::unique_ptr callback((create_compute_pipeline_async_callback *)userdata); - if (*callback) (*callback)((create_pipeline_async_status)status, compute_pipeline(pipeline), std::string(message ? message : "")); + if (*callback) (*callback)((create_pipeline_async_status)status, compute_pipeline(pipeline), std::string_view(message.data, message.length)); }; + callback_info.userdata1 = new create_compute_pipeline_async_callback(callback); - wgpuDeviceCreateComputePipelineAsync((WGPUDevice)get(), &descriptor, real_callback, userdata); + wgpuDeviceCreateComputePipelineAsync((WGPUDevice)get(), &descriptor, callback_info); } pipeline_layout device::create_pipeline_layout(pipeline_layout::descriptor const & desc) { WGPUPipelineLayoutDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); - descriptor.label = desc.label.data(); + descriptor.label = detail::to_string_view(desc.label); descriptor.bindGroupLayoutCount = desc.layouts.size(); static_assert(sizeof(WGPUBindGroupLayout) == sizeof(bind_group_layout)); descriptor.bindGroupLayouts = (WGPUBindGroupLayout const *)desc.layouts.data(); @@ -156,7 +159,7 @@ namespace psemek::wgpu { WGPUQuerySetDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); - descriptor.label = desc.label.data(); + descriptor.label = detail::to_string_view(desc.label); descriptor.type = (WGPUQueryType)desc.type; descriptor.count = desc.count; return query_set(wgpuDeviceCreateQuerySet((WGPUDevice)get(), &descriptor)); @@ -165,6 +168,11 @@ namespace psemek::wgpu namespace { + WGPUOptionalBool to_optional_bool(std::optional value) + { + return value ? (*value ? WGPUOptionalBool_True : WGPUOptionalBool_False) : WGPUOptionalBool_Undefined; + } + void fill_render_pipeline_descriptor(render_pipeline::descriptor const & desc, WGPURenderPipelineDescriptor & descriptor, std::vector & vertex_constants, @@ -175,14 +183,14 @@ namespace psemek::wgpu std::vector & color_targets) { descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); - descriptor.label = desc.label.data(); + descriptor.label = detail::to_string_view(desc.label); descriptor.layout = (WGPUPipelineLayout)desc.layout.get(); for (auto const & constant_in : desc.vertex.constants) { auto & constant_out = vertex_constants.emplace_back(); constant_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(constant_in.chain); - constant_out.key = constant_in.key.data(); + constant_out.key = detail::to_string_view(constant_in.key); constant_out.value = constant_in.value; } @@ -198,7 +206,7 @@ namespace psemek::wgpu descriptor.vertex.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.vertex.chain); descriptor.vertex.module = (WGPUShaderModule)desc.vertex.module.get(); - descriptor.vertex.entryPoint = desc.vertex.entry_point.data(); + descriptor.vertex.entryPoint = detail::to_string_view(desc.vertex.entry_point); descriptor.vertex.constantCount = vertex_constants.size(); descriptor.vertex.constants = vertex_constants.data(); descriptor.vertex.bufferCount = vertex_buffers.size(); @@ -209,12 +217,13 @@ namespace psemek::wgpu descriptor.primitive.stripIndexFormat = (WGPUIndexFormat)desc.primitive.strip_index_format; descriptor.primitive.frontFace = (WGPUFrontFace)desc.primitive.front_face; descriptor.primitive.cullMode = (WGPUCullMode)desc.primitive.cull_mode; + descriptor.primitive.unclippedDepth = desc.primitive.clip_depth ? 0 : 1; if (desc.depth_stencil) { depth_stencil_state.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.depth_stencil->chain); depth_stencil_state.format = (WGPUTextureFormat)desc.depth_stencil->format; - depth_stencil_state.depthWriteEnabled = desc.depth_stencil->depth_write; + depth_stencil_state.depthWriteEnabled = to_optional_bool(desc.depth_stencil->depth_write); depth_stencil_state.depthCompare = (WGPUCompareFunction)desc.depth_stencil->depth_compare; depth_stencil_state.stencilFront.compare = (WGPUCompareFunction)desc.depth_stencil->stencil_front.compare; depth_stencil_state.stencilFront.failOp = (WGPUStencilOperation)desc.depth_stencil->stencil_front.fail_op; @@ -244,7 +253,7 @@ namespace psemek::wgpu { auto & constant_out = fragment_constants.emplace_back(); constant_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(constant_in.chain); - constant_out.key = constant_in.key.data(); + constant_out.key = detail::to_string_view(constant_in.key); constant_out.value = constant_in.value; } @@ -255,12 +264,12 @@ namespace psemek::wgpu target_out.format = (WGPUTextureFormat)target_in.format; static_assert(sizeof(WGPUBlendState) == sizeof(blend_state)); target_out.blend = (WGPUBlendState *)(target_in.blend ? &(*target_in.blend) : nullptr); - target_out.writeMask = (WGPUColorWriteMaskFlags)target_in.write_mask; + target_out.writeMask = (WGPUColorWriteMask)target_in.write_mask; } fragment_state.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.fragment->chain); fragment_state.module = (WGPUShaderModule)desc.fragment->module.get(); - fragment_state.entryPoint = desc.fragment->entry_point.data(); + fragment_state.entryPoint = detail::to_string_view(desc.fragment->entry_point); fragment_state.constantCount = fragment_constants.size(); fragment_state.constants = fragment_constants.data(); fragment_state.targetCount = color_targets.size(); @@ -287,7 +296,7 @@ namespace psemek::wgpu return render_pipeline(wgpuDeviceCreateRenderPipeline((WGPUDevice)get(), &descriptor)); } - void device::create_render_pipeline_async(render_pipeline::descriptor const & desc, create_render_pipeline_async_callback const & callback) + void device::create_render_pipeline_async(callback_mode mode, render_pipeline::descriptor const & desc, create_render_pipeline_async_callback const & callback) { WGPURenderPipelineDescriptor descriptor = {}; std::vector vertex_constants; @@ -299,22 +308,23 @@ namespace psemek::wgpu fill_render_pipeline_descriptor(desc, descriptor, vertex_constants, vertex_buffers, depth_stencil_state, fragment_state, fragment_constants, color_targets); - auto userdata = new create_render_pipeline_async_callback(callback); - - auto real_callback = [](WGPUCreatePipelineAsyncStatus status, WGPURenderPipeline pipeline, char const * message, void * userdata) + WGPUCreateRenderPipelineAsyncCallbackInfo callback_info = {}; + callback_info.mode = (WGPUCallbackMode)mode; + callback_info.callback = [](WGPUCreatePipelineAsyncStatus status, WGPURenderPipeline pipeline, WGPUStringView message, void * userdata, void *) { std::unique_ptr callback((create_render_pipeline_async_callback *)userdata); - if (*callback) (*callback)((create_pipeline_async_status)status, render_pipeline(pipeline), std::string(message ? message : "")); + if (*callback) (*callback)((create_pipeline_async_status)status, render_pipeline(pipeline), std::string_view(message.data, message.length)); }; + callback_info.userdata1 = new create_render_pipeline_async_callback(callback); - wgpuDeviceCreateRenderPipelineAsync((WGPUDevice)get(), &descriptor, real_callback, userdata); + wgpuDeviceCreateRenderPipelineAsync((WGPUDevice)get(), &descriptor, callback_info); } render_bundle_encoder device::create_render_bundle_encoder(render_bundle_encoder::descriptor const & desc) { WGPURenderBundleEncoderDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); - descriptor.label = desc.label.data(); + descriptor.label = detail::to_string_view(desc.label); descriptor.colorFormatCount = desc.color_formats.size(); descriptor.colorFormats = (WGPUTextureFormat const *)desc.color_formats.data(); descriptor.depthStencilFormat = (WGPUTextureFormat)desc.depth_stencil_format; @@ -328,7 +338,7 @@ namespace psemek::wgpu { WGPUSamplerDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); - descriptor.label = desc.label.data(); + descriptor.label = detail::to_string_view(desc.label); descriptor.addressModeU = (WGPUAddressMode)desc.address_mode_u; descriptor.addressModeV = (WGPUAddressMode)desc.address_mode_v; descriptor.addressModeW = (WGPUAddressMode)desc.address_mode_w; @@ -344,20 +354,9 @@ namespace psemek::wgpu shader_module device::create_shader_module(shader_module::descriptor const & desc) { - std::vector hints; - for (auto const & hint_in : desc.hints) - { - auto & hint = hints.emplace_back(); - hint.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(hint_in.chain); - hint.entryPoint = hint_in.entry_point.data(); - hint.layout = (WGPUPipelineLayout)hint_in.layout.get(); - } - WGPUShaderModuleDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); - descriptor.label = desc.label.data(); - descriptor.hintCount = hints.size(); - descriptor.hints = hints.data(); + descriptor.label = detail::to_string_view(desc.label); return shader_module(wgpuDeviceCreateShaderModule((WGPUDevice)get(), &descriptor)); } @@ -365,8 +364,8 @@ namespace psemek::wgpu { WGPUTextureDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); - descriptor.label = desc.label.data(); - descriptor.usage = (WGPUTextureUsageFlags)desc.usage; + descriptor.label = detail::to_string_view(desc.label); + descriptor.usage = (WGPUTextureUsage)desc.usage; descriptor.dimension = (WGPUTextureDimension)desc.dimension; descriptor.size = {desc.size[0], desc.size[1], desc.size[2]}; descriptor.format = (WGPUTextureFormat)desc.format; @@ -384,21 +383,53 @@ namespace psemek::wgpu std::vector device::enumerate_features() { - std::size_t count = wgpuDeviceEnumerateFeatures((WGPUDevice)get(), nullptr); - std::vector result(count); - wgpuDeviceEnumerateFeatures((WGPUDevice)get(), (WGPUFeatureName *)result.data()); + WGPUSupportedFeatures supported_features; + wgpuDeviceGetFeatures((WGPUDevice)get(), &supported_features); + + std::vector result((feature const *)supported_features.features, (feature const *)(supported_features.features + supported_features.featureCount)); + wgpuSupportedFeaturesFreeMembers(supported_features); return result; } limits device::get_limits() { - WGPUSupportedLimits limits = {}; - wgpuDeviceGetLimits((WGPUDevice)get(), &limits); + WGPULimits limits = {}; + detail::check_status("wgpuDeviceGetLimits", wgpuDeviceGetLimits((WGPUDevice)get(), &limits)); // TODO: support out chain wgpu::limits result; - static_assert(sizeof(result) == sizeof(limits.limits)); - std::memcpy((char *)&result, (char *)&limits.limits, sizeof(result)); + result.max_texture_dimension_1D = limits.maxTextureDimension1D; + result.max_texture_dimension_2D = limits.maxTextureDimension2D; + result.max_texture_dimension_3D = limits.maxTextureDimension3D; + result.max_texture_array_layers = limits.maxTextureArrayLayers; + result.max_bind_groups = limits.maxBindGroups; + result.max_bind_groups_plus_vertex_buffers = limits.maxBindGroupsPlusVertexBuffers; + result.max_bindings_per_bind_group = limits.maxBindingsPerBindGroup; + result.max_dynamic_uniform_buffers_per_pipeline_layout = limits.maxDynamicUniformBuffersPerPipelineLayout; + result.max_dynamic_storage_buffers_per_pipeline_layout = limits.maxDynamicStorageBuffersPerPipelineLayout; + result.max_sampled_textures_per_shader_stage = limits.maxSampledTexturesPerShaderStage; + result.max_samplers_per_shader_stage = limits.maxSamplersPerShaderStage; + result.max_storage_buffers_per_shader_stage = limits.maxStorageBuffersPerShaderStage; + result.max_storage_textures_per_shader_stage = limits.maxStorageTexturesPerShaderStage; + result.max_uniform_buffers_per_shader_stage = limits.maxUniformBuffersPerShaderStage; + result.max_uniform_buffer_binding_size = limits.maxUniformBufferBindingSize; + result.max_storage_buffer_binding_size = limits.maxStorageBufferBindingSize; + result.min_uniform_buffer_offset_alignment = limits.minUniformBufferOffsetAlignment; + result.min_storage_buffer_offset_alignment = limits.minStorageBufferOffsetAlignment; + result.max_vertex_buffers = limits.maxVertexBuffers; + result.max_buffer_size = limits.maxBufferSize; + result.max_vertex_attributes = limits.maxVertexAttributes; + result.max_vertex_buffer_array_stride = limits.maxVertexBufferArrayStride; + result.max_inter_stage_shader_variables = limits.maxInterStageShaderVariables; + result.max_color_attachments = limits.maxColorAttachments; + result.max_color_attachment_bytes_per_sample = limits.maxColorAttachmentBytesPerSample; + result.max_compute_workgroup_storage_size = limits.maxComputeWorkgroupStorageSize; + result.max_compute_invocations_per_workgroup = limits.maxComputeInvocationsPerWorkgroup; + result.max_compute_workgroup_size_x = limits.maxComputeWorkgroupSizeX; + result.max_compute_workgroup_size_y = limits.maxComputeWorkgroupSizeY; + result.max_compute_workgroup_size_z = limits.maxComputeWorkgroupSizeZ; + result.max_compute_workgroups_per_dimension = limits.maxComputeWorkgroupsPerDimension; + return result; } @@ -412,40 +443,28 @@ namespace psemek::wgpu wgpuDevicePushErrorScope((WGPUDevice)get(), (WGPUErrorFilter)filter); } - void device::pop_error_scope(error_callback const & callback) + void device::pop_error_scope(callback_mode mode, pop_error_callback const & callback) { - auto userdata = new error_callback(callback); - - auto real_callback = [](WGPUErrorType type, char const * message, void * userdata) + WGPUPopErrorScopeCallbackInfo callback_info = {}; + callback_info.mode = (WGPUCallbackMode)mode; + callback_info.callback = [](WGPUPopErrorScopeStatus status, WGPUErrorType type, WGPUStringView message, void * userdata, void *) { - std::unique_ptr callback((error_callback *)userdata); - if (*callback) (*callback)((error_type)type, message ? message : ""); + std::unique_ptr callback((pop_error_callback *)userdata); + if (*callback) (*callback)((pop_error_scope_status)status, (error_type)type, std::string_view(message.data, message.length)); }; + callback_info.userdata1 = new pop_error_callback(callback); - wgpuDevicePopErrorScope((WGPUDevice)get(), real_callback, userdata); - } - - void device::set_uncaptured_error_callback(error_callback const & callback) - { - auto userdata = new error_callback(callback); - - auto real_callback = [](WGPUErrorType type, char const * message, void * userdata) - { - std::unique_ptr callback((error_callback *)userdata); - if (*callback) (*callback)((error_type)type, message ? message : ""); - }; - - wgpuDeviceSetUncapturedErrorCallback((WGPUDevice)get(), real_callback, userdata); + wgpuDevicePopErrorScope((WGPUDevice)get(), callback_info); } void device::set_label(std::string const & label) { - wgpuDeviceSetLabel((WGPUDevice)get(), label.data()); + wgpuDeviceSetLabel((WGPUDevice)get(), detail::to_string_view(label)); } void device::reference(void * ptr) { - wgpuDeviceReference((WGPUDevice)ptr); + wgpuDeviceAddRef((WGPUDevice)ptr); } void device::release(void * ptr) @@ -455,10 +474,10 @@ namespace psemek::wgpu detail::chained_struct_ptr to_chained_struct(native_limits && value) { - WGPURequiredLimitsExtras chained = {}; - chained.chain.sType = (WGPUSType)WGPUSType_RequiredLimitsExtras; - static_assert(sizeof(WGPUNativeLimits) == sizeof(native_limits)); - std::memcpy(&chained.limits, &value, sizeof(value)); + WGPUNativeLimits chained = {}; + chained.chain.sType = (WGPUSType)WGPUSType_NativeLimits; + chained.maxPushConstantSize = value.max_push_constant_size; + chained.maxNonSamplerBindings = value.max_non_sampler_bindings; return detail::make_chained_struct(chained); } diff --git a/libs/wgpu/source/instance.cpp b/libs/wgpu/source/instance.cpp index 6d6233f1..644107a2 100644 --- a/libs/wgpu/source/instance.cpp +++ b/libs/wgpu/source/instance.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -40,33 +41,35 @@ namespace psemek::wgpu { WGPUSurfaceDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); - descriptor.label = desc.label.data(); + descriptor.label = detail::to_string_view(desc.label); return surface{wgpuInstanceCreateSurface((WGPUInstance)get(), &descriptor)}; } - void instance::request_adapter(adapter::request_options const & request_options, adapter::request_callback callback) + void instance::request_adapter(callback_mode mode, adapter::request_options const & request_options, adapter::request_callback callback) { - WGPURequestAdapterOptions options; + WGPURequestAdapterOptions options = {}; options.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(request_options.chain); - options.compatibleSurface = (WGPUSurface)request_options.compatible_surface.get(); + options.featureLevel = (WGPUFeatureLevel)request_options.feature_level; options.powerPreference = (WGPUPowerPreference)request_options.power_preference; - options.backendType = (WGPUBackendType)request_options.backend_type; options.forceFallbackAdapter = request_options.force_fallback_adapter; + options.backendType = (WGPUBackendType)request_options.backend_type; + options.compatibleSurface = (WGPUSurface)request_options.compatible_surface.get(); - auto userdata = new adapter::request_callback(callback); - - auto real_callback = [](WGPURequestAdapterStatus status, WGPUAdapter adapter, char const * message, void * userdata) + WGPURequestAdapterCallbackInfo callback_info = {}; + callback_info.mode = (WGPUCallbackMode)mode; + callback_info.callback = [](WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, void * userdata, void *) { std::unique_ptr callback{(adapter::request_callback *)userdata}; - if (*callback) (*callback)(adapter::request_status{status}, wgpu::adapter(adapter), message ? message : ""); + if (*callback) (*callback)(adapter::request_status{status}, wgpu::adapter(adapter), std::string_view(message.data, message.length)); }; + callback_info.userdata1 = new adapter::request_callback(callback); - wgpuInstanceRequestAdapter((WGPUInstance)get(), &options, real_callback, userdata); + wgpuInstanceRequestAdapter((WGPUInstance)get(), &options, callback_info); } void instance::reference(void * ptr) { - wgpuInstanceReference((WGPUInstance)ptr); + wgpuInstanceAddRef((WGPUInstance)ptr); } void instance::release(void * ptr) diff --git a/libs/wgpu/source/logging.cpp b/libs/wgpu/source/logging.cpp index 265197c2..e8d771d6 100644 --- a/libs/wgpu/source/logging.cpp +++ b/libs/wgpu/source/logging.cpp @@ -40,8 +40,8 @@ namespace psemek::wgpu void setup_logging(log::level level) { wgpuSetLogLevel(to_wgpu_log_level(level)); - wgpuSetLogCallback([](WGPULogLevel level, char const * message, void * /* userdata */){ - log::log(to_psemek_log_level(level)) << "WebGPU: " << message; + wgpuSetLogCallback([](WGPULogLevel level, WGPUStringView message, void * /* userdata */){ + log::log(to_psemek_log_level(level)) << "WebGPU: " << std::string_view(message.data, message.length); }, nullptr); } diff --git a/libs/wgpu/source/pipeline_layout.cpp b/libs/wgpu/source/pipeline_layout.cpp index 0ed0ea5e..7994751d 100644 --- a/libs/wgpu/source/pipeline_layout.cpp +++ b/libs/wgpu/source/pipeline_layout.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -7,12 +8,12 @@ namespace psemek::wgpu void pipeline_layout::set_label(std::string const & label) { - wgpuPipelineLayoutSetLabel((WGPUPipelineLayout)get(), label.data()); + wgpuPipelineLayoutSetLabel((WGPUPipelineLayout)get(), detail::to_string_view(label)); } void pipeline_layout::reference(void * ptr) { - wgpuPipelineLayoutReference((WGPUPipelineLayout)ptr); + wgpuPipelineLayoutAddRef((WGPUPipelineLayout)ptr); } void pipeline_layout::release(void * ptr) diff --git a/libs/wgpu/source/query_set.cpp b/libs/wgpu/source/query_set.cpp index 3dab9410..f778ddc6 100644 --- a/libs/wgpu/source/query_set.cpp +++ b/libs/wgpu/source/query_set.cpp @@ -1,4 +1,5 @@ #include +#include #include namespace psemek::wgpu @@ -21,12 +22,12 @@ namespace psemek::wgpu void query_set::set_label(std::string const & label) { - wgpuQuerySetSetLabel((WGPUQuerySet)get(), label.data()); + wgpuQuerySetSetLabel((WGPUQuerySet)get(), detail::to_string_view(label)); } void query_set::reference(void * ptr) { - wgpuQuerySetReference((WGPUQuerySet)ptr); + wgpuQuerySetAddRef((WGPUQuerySet)ptr); } void query_set::release(void * ptr) diff --git a/libs/wgpu/source/queue.cpp b/libs/wgpu/source/queue.cpp index cd1a665b..2cba63fc 100644 --- a/libs/wgpu/source/queue.cpp +++ b/libs/wgpu/source/queue.cpp @@ -1,4 +1,5 @@ #include +#include #include namespace psemek::wgpu @@ -15,47 +16,46 @@ namespace psemek::wgpu wgpuQueueWriteBuffer((WGPUQueue)get(), (WGPUBuffer)buffer.get(), offset, data.data(), data.size()); } - void queue::write_texture(image_copy_texture const & dest, util::span data, texture::data_layout const & data_layout, math::vector const & write_size) + void queue::write_texture(texel_copy_texture_info const & dest, util::span data, texel_copy_buffer_layout const & data_layout, math::vector const & write_size) { - WGPUImageCopyTexture image_copy_texture = {}; - image_copy_texture.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(dest.chain); - image_copy_texture.texture = (WGPUTexture)dest.texture.get(); - image_copy_texture.mipLevel = dest.mip_level; - image_copy_texture.origin = {dest.origin[0], dest.origin[1], dest.origin[2]}; - image_copy_texture.aspect = (WGPUTextureAspect)dest.aspect; + WGPUTexelCopyTextureInfo texel_copy_texture_info = {}; + texel_copy_texture_info.texture = (WGPUTexture)dest.texture.get(); + texel_copy_texture_info.mipLevel = dest.mip_level; + texel_copy_texture_info.origin = {dest.origin[0], dest.origin[1], dest.origin[2]}; + texel_copy_texture_info.aspect = (WGPUTextureAspect)dest.aspect; - WGPUTextureDataLayout texture_data_layout = {}; - texture_data_layout.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(data_layout.chain); - texture_data_layout.offset = data_layout.offset; - texture_data_layout.bytesPerRow = data_layout.bytes_per_row; - texture_data_layout.rowsPerImage = data_layout.rows_per_image; + WGPUTexelCopyBufferLayout texel_copy_buffer_layout = {}; + texel_copy_buffer_layout.offset = data_layout.offset; + texel_copy_buffer_layout.bytesPerRow = data_layout.bytes_per_row; + texel_copy_buffer_layout.rowsPerImage = data_layout.rows_per_image; WGPUExtent3D extent = {write_size[0], write_size[1], write_size[2]}; - wgpuQueueWriteTexture((WGPUQueue)get(), &image_copy_texture, data.data(), data.size(), &texture_data_layout, &extent); + wgpuQueueWriteTexture((WGPUQueue)get(), &texel_copy_texture_info, data.data(), data.size(), &texel_copy_buffer_layout, &extent); } - void queue::on_submitted_work_done(work_done_callback const & callback) + void queue::on_submitted_work_done(callback_mode mode, work_done_callback const & callback) { - auto userdata = new work_done_callback(callback); - - auto real_callback = [](WGPUQueueWorkDoneStatus status, void * userdata) + WGPUQueueWorkDoneCallbackInfo callback_info = {}; + callback_info.mode = (WGPUCallbackMode)mode; + callback_info.callback = [](WGPUQueueWorkDoneStatus status, void * userdata, void *) { std::unique_ptr callback((work_done_callback*)userdata); if (*callback) (*callback)((work_done_status)status); }; + callback_info.userdata1 = new work_done_callback(callback); - wgpuQueueOnSubmittedWorkDone((WGPUQueue)get(), real_callback, userdata); + wgpuQueueOnSubmittedWorkDone((WGPUQueue)get(), callback_info); } void queue::set_label(std::string const & label) { - wgpuQueueSetLabel((WGPUQueue)get(), label.data()); + wgpuQueueSetLabel((WGPUQueue)get(), detail::to_string_view(label)); } void queue::reference(void * ptr) { - wgpuQueueReference((WGPUQueue)ptr); + wgpuQueueAddRef((WGPUQueue)ptr); } void queue::release(void * ptr) diff --git a/libs/wgpu/source/render_bundle.cpp b/libs/wgpu/source/render_bundle.cpp index 6aeed09b..72309274 100644 --- a/libs/wgpu/source/render_bundle.cpp +++ b/libs/wgpu/source/render_bundle.cpp @@ -1,4 +1,5 @@ #include +#include #include namespace psemek::wgpu @@ -6,12 +7,12 @@ namespace psemek::wgpu void render_bundle::set_label(std::string const & label) { - wgpuRenderBundleSetLabel((WGPURenderBundle)get(), label.data()); + wgpuRenderBundleSetLabel((WGPURenderBundle)get(), detail::to_string_view(label)); } void render_bundle::reference(void * ptr) { - wgpuRenderBundleReference((WGPURenderBundle)ptr); + wgpuRenderBundleAddRef((WGPURenderBundle)ptr); } void render_bundle::release(void * ptr) diff --git a/libs/wgpu/source/render_bundle_encoder.cpp b/libs/wgpu/source/render_bundle_encoder.cpp index b0c2b275..83f4be86 100644 --- a/libs/wgpu/source/render_bundle_encoder.cpp +++ b/libs/wgpu/source/render_bundle_encoder.cpp @@ -1,4 +1,5 @@ #include +#include #include namespace psemek::wgpu @@ -44,14 +45,14 @@ namespace psemek::wgpu wgpuRenderBundleEncoderDrawIndexedIndirect((WGPURenderBundleEncoder)get(), (WGPUBuffer)indirect_buffer.get(), offset); } - void render_bundle_encoder::insert_debug_marker(std::string const & marker_label) + void render_bundle_encoder::insert_debug_marker(std::string_view marker_label) { - wgpuRenderBundleEncoderInsertDebugMarker((WGPURenderBundleEncoder)get(), marker_label.data()); + wgpuRenderBundleEncoderInsertDebugMarker((WGPURenderBundleEncoder)get(), detail::to_string_view(marker_label)); } - void render_bundle_encoder::push_debug_group(std::string const & group_label) + void render_bundle_encoder::push_debug_group(std::string_view group_label) { - wgpuRenderBundleEncoderPushDebugGroup((WGPURenderBundleEncoder)get(), group_label.data()); + wgpuRenderBundleEncoderPushDebugGroup((WGPURenderBundleEncoder)get(), detail::to_string_view(group_label)); } void render_bundle_encoder::pop_debug_group() @@ -63,18 +64,18 @@ namespace psemek::wgpu { WGPURenderBundleDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); - descriptor.label = desc.label.data(); + descriptor.label = detail::to_string_view(desc.label); return render_bundle(wgpuRenderBundleEncoderFinish((WGPURenderBundleEncoder)get(), &descriptor)); } void render_bundle_encoder::set_label(std::string const & label) { - wgpuRenderBundleEncoderSetLabel((WGPURenderBundleEncoder)get(), label.data()); + wgpuRenderBundleEncoderSetLabel((WGPURenderBundleEncoder)get(), detail::to_string_view(label)); } void render_bundle_encoder::reference(void * ptr) { - wgpuRenderBundleEncoderReference((WGPURenderBundleEncoder)ptr); + wgpuRenderBundleEncoderAddRef((WGPURenderBundleEncoder)ptr); } void render_bundle_encoder::release(void * ptr) diff --git a/libs/wgpu/source/render_pass_encoder.cpp b/libs/wgpu/source/render_pass_encoder.cpp index 422f486e..5122bbe9 100644 --- a/libs/wgpu/source/render_pass_encoder.cpp +++ b/libs/wgpu/source/render_pass_encoder.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -48,7 +49,7 @@ namespace psemek::wgpu void render_pass_encoder::set_push_constants(shader_stage stages, std::uint32_t offset, util::span data) { - wgpuRenderPassEncoderSetPushConstants((WGPURenderPassEncoder)get(), (WGPUShaderStageFlags)stages, offset, data.size(), data.data()); + wgpuRenderPassEncoderSetPushConstants((WGPURenderPassEncoder)get(), (WGPUShaderStage)stages, offset, data.size(), data.data()); } void render_pass_encoder::draw(std::uint32_t vertex_count, std::uint32_t instance_count, std::uint32_t first_vertex, std::uint32_t first_instance) @@ -107,14 +108,19 @@ namespace psemek::wgpu wgpuRenderPassEncoderEndOcclusionQuery((WGPURenderPassEncoder)get()); } - void render_pass_encoder::insert_debug_marker(std::string const & marker_label) + void render_pass_encoder::write_timestamp(query_set const & query_set, std::uint32_t query_index) { - wgpuRenderPassEncoderInsertDebugMarker((WGPURenderPassEncoder)get(), marker_label.data()); + wgpuRenderPassEncoderWriteTimestamp((WGPURenderPassEncoder)get(), (WGPUQuerySet)query_set.get(), query_index); } - void render_pass_encoder::push_debug_group(std::string const & group_label) + void render_pass_encoder::insert_debug_marker(std::string_view marker_label) { - wgpuRenderPassEncoderPushDebugGroup((WGPURenderPassEncoder)get(), group_label.data()); + wgpuRenderPassEncoderInsertDebugMarker((WGPURenderPassEncoder)get(), detail::to_string_view(marker_label)); + } + + void render_pass_encoder::push_debug_group(std::string_view group_label) + { + wgpuRenderPassEncoderPushDebugGroup((WGPURenderPassEncoder)get(), detail::to_string_view(group_label)); } void render_pass_encoder::pop_debug_group() @@ -129,12 +135,12 @@ namespace psemek::wgpu void render_pass_encoder::set_label(std::string const & label) { - wgpuRenderPassEncoderSetLabel((WGPURenderPassEncoder)get(), label.data()); + wgpuRenderPassEncoderSetLabel((WGPURenderPassEncoder)get(), detail::to_string_view(label)); } void render_pass_encoder::reference(void * ptr) { - wgpuRenderPassEncoderReference((WGPURenderPassEncoder)ptr); + wgpuRenderPassEncoderAddRef((WGPURenderPassEncoder)ptr); } void render_pass_encoder::release(void * ptr) diff --git a/libs/wgpu/source/render_pipeline.cpp b/libs/wgpu/source/render_pipeline.cpp index d721c396..84749f43 100644 --- a/libs/wgpu/source/render_pipeline.cpp +++ b/libs/wgpu/source/render_pipeline.cpp @@ -1,4 +1,5 @@ #include +#include #include namespace psemek::wgpu @@ -11,12 +12,12 @@ namespace psemek::wgpu void render_pipeline::set_label(std::string const & label) { - wgpuRenderPipelineSetLabel((WGPURenderPipeline)get(), label.data()); + wgpuRenderPipelineSetLabel((WGPURenderPipeline)get(), detail::to_string_view(label)); } void render_pipeline::reference(void * ptr) { - wgpuRenderPipelineReference((WGPURenderPipeline)ptr); + wgpuRenderPipelineAddRef((WGPURenderPipeline)ptr); } void render_pipeline::release(void * ptr) diff --git a/libs/wgpu/source/sampler.cpp b/libs/wgpu/source/sampler.cpp index f8fc0870..bde0d1f8 100644 --- a/libs/wgpu/source/sampler.cpp +++ b/libs/wgpu/source/sampler.cpp @@ -1,4 +1,5 @@ #include +#include #include namespace psemek::wgpu @@ -6,12 +7,12 @@ namespace psemek::wgpu void sampler::set_label(std::string const & label) { - wgpuSamplerSetLabel((WGPUSampler)get(), label.data()); + wgpuSamplerSetLabel((WGPUSampler)get(), detail::to_string_view(label)); } void sampler::reference(void * ptr) { - wgpuSamplerReference((WGPUSampler)ptr); + wgpuSamplerAddRef((WGPUSampler)ptr); } void sampler::release(void * ptr) diff --git a/libs/wgpu/source/shader_module.cpp b/libs/wgpu/source/shader_module.cpp index f1d434aa..8dcceee1 100644 --- a/libs/wgpu/source/shader_module.cpp +++ b/libs/wgpu/source/shader_module.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -7,11 +8,11 @@ namespace psemek::wgpu { - void shader_module::get_compilation_info(compilation_info_callback const & callback) + void shader_module::get_compilation_info(callback_mode mode, compilation_info_callback const & callback) { - auto userdata = new compilation_info_callback(callback); - - auto real_callback = [](WGPUCompilationInfoRequestStatus status, WGPUCompilationInfo const * info, void * userdata) + WGPUCompilationInfoCallbackInfo callback_info = {}; + callback_info.mode = (WGPUCallbackMode)mode; + callback_info.callback = [](WGPUCompilationInfoRequestStatus status, WGPUCompilationInfo const * info, void * userdata, void *) { std::unique_ptr callback((compilation_info_callback *)userdata); @@ -24,32 +25,30 @@ namespace psemek::wgpu auto & message = cinfo.messages.emplace_back(); auto & message_in = info->messages[i]; - message.message = message_in.message; + message.message.assign(message_in.message.data, message_in.message.length); message.type = (compilation_message_type)message_in.type; message.line_num = message_in.lineNum; message.line_pos = message_in.linePos; message.offset = message_in.offset; message.length = message_in.length; - message.utf16_line_pos = message_in.utf16LinePos; - message.utf16_offset = message_in.utf16Offset; - message.utf16_length = message_in.utf16Length; } } if (*callback) (*callback)((compilation_info_request_status)status, cinfo); }; + callback_info.userdata1 = new compilation_info_callback(callback); - wgpuShaderModuleGetCompilationInfo((WGPUShaderModule)get(), real_callback, userdata); + wgpuShaderModuleGetCompilationInfo((WGPUShaderModule)get(), callback_info); } void shader_module::set_label(std::string const & label) { - wgpuShaderModuleSetLabel((WGPUShaderModule)get(), label.data()); + wgpuShaderModuleSetLabel((WGPUShaderModule)get(), detail::to_string_view(label)); } void shader_module::reference(void * ptr) { - wgpuShaderModuleReference((WGPUShaderModule)ptr); + wgpuShaderModuleAddRef((WGPUShaderModule)ptr); } void shader_module::release(void * ptr) @@ -59,8 +58,8 @@ namespace psemek::wgpu detail::chained_struct_ptr to_chained_struct(shader_module::spirv_descriptor const & value) { - WGPUShaderModuleSPIRVDescriptor chained = {}; - chained.chain.sType = WGPUSType_ShaderModuleSPIRVDescriptor; + WGPUShaderSourceSPIRV chained = {}; + chained.chain.sType = WGPUSType_ShaderSourceSPIRV; chained.codeSize = value.code.size(); // TODO: is it in bytes or in words? chained.code = value.code.data(); return detail::make_chained_struct(chained); @@ -68,22 +67,34 @@ namespace psemek::wgpu detail::chained_struct_ptr to_chained_struct(shader_module::wgsl_descriptor const & value) { - WGPUShaderModuleWGSLDescriptor chained = {}; - chained.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor; - chained.code = value.code; + WGPUShaderSourceWGSL chained = {}; + chained.chain.sType = WGPUSType_ShaderSourceWGSL; + chained.code = detail::to_string_view(value.code); return detail::make_chained_struct(chained); } detail::chained_struct_ptr to_chained_struct(shader_module::glsl_descriptor && value) { - WGPUShaderModuleGLSLDescriptor chained = {}; - chained.chain.sType = (WGPUSType)WGPUSType_ShaderModuleGLSLDescriptor; + struct chained_storage + { + std::vector defines; + }; + + chained_storage storage; + storage.defines.resize(value.defines.size()); + for (int i = 0; i < value.defines.size(); ++i) + { + storage.defines[i].name = detail::to_string_view(value.defines[i].name); + storage.defines[i].value = detail::to_string_view(value.defines[i].value); + } + + WGPUShaderSourceGLSL chained = {}; + chained.chain.sType = (WGPUSType)WGPUSType_ShaderSourceGLSL; chained.stage = (WGPUShaderStage)value.stage; - chained.code = value.code; - chained.defineCount = value.defines.size(); - static_assert(sizeof(WGPUShaderDefine) == sizeof(shader_module::define)); - chained.defines = (WGPUShaderDefine *)value.defines.data(); - return detail::make_chained_struct(chained, std::move(value)); + chained.code = detail::to_string_view(value.code); + chained.defineCount = storage.defines.size(); + chained.defines = storage.defines.data(); + return detail::make_chained_struct(chained, std::move(storage)); } } diff --git a/libs/wgpu/source/surface.cpp b/libs/wgpu/source/surface.cpp index 091d7a08..da24425c 100644 --- a/libs/wgpu/source/surface.cpp +++ b/libs/wgpu/source/surface.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include @@ -13,7 +15,7 @@ namespace psemek::wgpu config.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(conf.chain); config.device = (WGPUDevice)conf.device.get(); config.format = (WGPUTextureFormat)conf.format; - config.usage = (WGPUTextureUsageFlags)conf.usage; + config.usage = (WGPUTextureUsage)conf.usage; config.viewFormatCount = conf.view_formats.size(); config.viewFormats = (WGPUTextureFormat const *)conf.view_formats.data(); config.alphaMode = (WGPUCompositeAlphaMode)conf.alpha_mode; @@ -26,13 +28,17 @@ namespace psemek::wgpu surface::capabilities surface::get_capabilities(adapter const & adapter) { WGPUSurfaceCapabilities caps = {}; - wgpuSurfaceGetCapabilities((WGPUSurface)get(), (WGPUAdapter)adapter.get(), &caps); + detail::check_status("wgpuSurfaceGetCapabilities", wgpuSurfaceGetCapabilities((WGPUSurface)get(), (WGPUAdapter)adapter.get(), &caps)); capabilities result; // TODO: support out chain + result.usage = (texture::usage)caps.usages; result.formats.assign((wgpu::texture::format *)(caps.formats), (wgpu::texture::format *)(caps.formats) + caps.formatCount); result.present_modes.assign((present_mode *)(caps.presentModes), (present_mode *)(caps.presentModes) + caps.presentModeCount); result.alpha_modes.assign((composite_alpha_mode *)(caps.alphaModes), (composite_alpha_mode *)(caps.alphaModes) + caps.alphaModeCount); + + wgpuSurfaceCapabilitiesFreeMembers(caps); + return result; } @@ -43,19 +49,13 @@ namespace psemek::wgpu return { .texture = wgpu::texture(tex.texture), - .suboptimal = tex.suboptimal, .status = (enum current_texture::status)tex.status, }; } - wgpu::texture::format surface::get_preferred_format(adapter const & adapter) - { - return (wgpu::texture::format)wgpuSurfaceGetPreferredFormat((WGPUSurface)get(), (WGPUAdapter)adapter.get()); - } - void surface::present() { - wgpuSurfacePresent((WGPUSurface)get()); + detail::check_status("wgpuSurfacePresent", wgpuSurfacePresent((WGPUSurface)get())); } void surface::unconfigure() @@ -65,7 +65,7 @@ namespace psemek::wgpu void surface::reference(void * ptr) { - wgpuSurfaceReference((WGPUSurface)ptr); + wgpuSurfaceAddRef((WGPUSurface)ptr); } void surface::release(void * ptr) @@ -83,32 +83,24 @@ namespace psemek::wgpu detail::chained_struct_ptr to_chained_struct(surface::from_android_native_window const & value) { - WGPUSurfaceDescriptorFromAndroidNativeWindow chained = {}; - chained.chain.sType = WGPUSType_SurfaceDescriptorFromAndroidNativeWindow; + WGPUSurfaceSourceAndroidNativeWindow chained = {}; + chained.chain.sType = WGPUSType_SurfaceSourceAndroidNativeWindow; chained.window = value.window; return detail::make_chained_struct(chained); } - detail::chained_struct_ptr to_chained_struct(surface::from_canvas_html_selected const & value) - { - WGPUSurfaceDescriptorFromCanvasHTMLSelector chained = {}; - chained.chain.sType = WGPUSType_SurfaceDescriptorFromCanvasHTMLSelector; - chained.selector = value.selector.data(); - return detail::make_chained_struct(chained); - } - detail::chained_struct_ptr to_chained_struct(surface::from_metal_layer const & value) { - WGPUSurfaceDescriptorFromMetalLayer chained = {}; - chained.chain.sType = WGPUSType_SurfaceDescriptorFromMetalLayer; + WGPUSurfaceSourceMetalLayer chained = {}; + chained.chain.sType = WGPUSType_SurfaceSourceMetalLayer; chained.layer = value.layer; return detail::make_chained_struct(chained); } detail::chained_struct_ptr to_chained_struct(surface::from_wayland_surface const & value) { - WGPUSurfaceDescriptorFromWaylandSurface chained = {}; - chained.chain.sType = WGPUSType_SurfaceDescriptorFromWaylandSurface; + WGPUSurfaceSourceWaylandSurface chained = {}; + chained.chain.sType = WGPUSType_SurfaceSourceWaylandSurface; chained.display = value.display; chained.surface = value.surface; return detail::make_chained_struct(chained); @@ -116,8 +108,8 @@ namespace psemek::wgpu detail::chained_struct_ptr to_chained_struct(surface::from_windows_hwnd const & value) { - WGPUSurfaceDescriptorFromWindowsHWND chained = {}; - chained.chain.sType = WGPUSType_SurfaceDescriptorFromWindowsHWND; + WGPUSurfaceSourceWindowsHWND chained = {}; + chained.chain.sType = WGPUSType_SurfaceSourceWindowsHWND; chained.hinstance = value.hinstance; chained.hwnd = value.hwnd; return detail::make_chained_struct(chained); @@ -125,8 +117,8 @@ namespace psemek::wgpu detail::chained_struct_ptr to_chained_struct(surface::from_xcb_window const & value) { - WGPUSurfaceDescriptorFromXcbWindow chained = {}; - chained.chain.sType = WGPUSType_SurfaceDescriptorFromXcbWindow; + WGPUSurfaceSourceXCBWindow chained = {}; + chained.chain.sType = WGPUSType_SurfaceSourceXCBWindow; chained.connection = value.connection; chained.window = value.window; return detail::make_chained_struct(chained); @@ -134,8 +126,8 @@ namespace psemek::wgpu detail::chained_struct_ptr to_chained_struct(surface::from_xlib_window const & value) { - WGPUSurfaceDescriptorFromXlibWindow chained = {}; - chained.chain.sType = WGPUSType_SurfaceDescriptorFromXlibWindow; + WGPUSurfaceSourceXlibWindow chained = {}; + chained.chain.sType = WGPUSType_SurfaceSourceXlibWindow; chained.display = value.display; chained.window = value.window; return detail::make_chained_struct(chained); diff --git a/libs/wgpu/source/texture.cpp b/libs/wgpu/source/texture.cpp index ed8b08d1..6186fa56 100644 --- a/libs/wgpu/source/texture.cpp +++ b/libs/wgpu/source/texture.cpp @@ -1,4 +1,5 @@ #include +#include #include namespace psemek::wgpu @@ -8,7 +9,7 @@ namespace psemek::wgpu { WGPUTextureViewDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); - descriptor.label = desc.label.data(); + descriptor.label = detail::to_string_view(desc.label); descriptor.format = (WGPUTextureFormat)desc.format; descriptor.dimension = (WGPUTextureViewDimension)desc.dimension; descriptor.baseMipLevel = desc.base_mip_level; @@ -62,12 +63,12 @@ namespace psemek::wgpu void texture::set_label(std::string const & label) { - wgpuTextureSetLabel((WGPUTexture)get(), label.data()); + wgpuTextureSetLabel((WGPUTexture)get(), detail::to_string_view(label)); } void texture::reference(void * ptr) { - wgpuTextureReference((WGPUTexture)ptr); + wgpuTextureAddRef((WGPUTexture)ptr); } void texture::release(void * ptr) diff --git a/libs/wgpu/source/texture_view.cpp b/libs/wgpu/source/texture_view.cpp index 60ebc094..7d12fc8d 100644 --- a/libs/wgpu/source/texture_view.cpp +++ b/libs/wgpu/source/texture_view.cpp @@ -1,4 +1,5 @@ #include +#include #include namespace psemek::wgpu @@ -6,12 +7,12 @@ namespace psemek::wgpu void texture_view::set_label(std::string const & label) { - wgpuTextureViewSetLabel((WGPUTextureView)get(), label.data()); + wgpuTextureViewSetLabel((WGPUTextureView)get(), detail::to_string_view(label)); } void texture_view::reference(void * ptr) { - wgpuTextureViewReference((WGPUTextureView)ptr); + wgpuTextureViewAddRef((WGPUTextureView)ptr); } void texture_view::release(void * ptr) From b1255f7a14cd377f4274a258c4a6d8985762f921 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sat, 9 Aug 2025 13:31:23 +0300 Subject: [PATCH 069/141] Expose wgpu instance in app context --- libs/app/include/psemek/app/application.hpp | 2 ++ libs/sdl2/include/psemek/sdl2/window.hpp | 3 +++ libs/sdl2/source/main.cpp | 1 + libs/sdl2/source/window.cpp | 6 +++--- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/libs/app/include/psemek/app/application.hpp b/libs/app/include/psemek/app/application.hpp index b6948266..544e0dd0 100644 --- a/libs/app/include/psemek/app/application.hpp +++ b/libs/app/include/psemek/app/application.hpp @@ -3,6 +3,7 @@ #include #if defined(PSEMEK_GRAPHICS_API_WEBGPU) +#include #include #include #include @@ -42,6 +43,7 @@ namespace psemek::app std::function vsync; #endif #if defined(PSEMEK_GRAPHICS_API_WEBGPU) + wgpu::instance instance; wgpu::adapter adapter; wgpu::surface surface; wgpu::device device; diff --git a/libs/sdl2/include/psemek/sdl2/window.hpp b/libs/sdl2/include/psemek/sdl2/window.hpp index dc344505..dd8f2e61 100644 --- a/libs/sdl2/include/psemek/sdl2/window.hpp +++ b/libs/sdl2/include/psemek/sdl2/window.hpp @@ -3,6 +3,7 @@ #include #if defined(PSEMEK_GRAPHICS_API_WEBGPU) +#include #include #include #include @@ -28,6 +29,7 @@ namespace psemek::sdl2 void windowed(bool on); #if defined(PSEMEK_GRAPHICS_API_WEBGPU) + wgpu::instance wgpu_instance() const { return wgpu_instance_; } wgpu::adapter wgpu_adapter() const { return wgpu_adapter_; } wgpu::surface wgpu_surface() const { return wgpu_surface_; } wgpu::device wgpu_device() const { return wgpu_device_; } @@ -40,6 +42,7 @@ namespace psemek::sdl2 #if defined(PSEMEK_GRAPHICS_API_OPENGL) SDL_GLContext gl_context_ = nullptr; #elif defined(PSEMEK_GRAPHICS_API_WEBGPU) + wgpu::instance wgpu_instance_; wgpu::adapter wgpu_adapter_; wgpu::surface wgpu_surface_; wgpu::device wgpu_device_; diff --git a/libs/sdl2/source/main.cpp b/libs/sdl2/source/main.cpp index 2849d16d..20d433b0 100644 --- a/libs/sdl2/source/main.cpp +++ b/libs/sdl2/source/main.cpp @@ -55,6 +55,7 @@ int main(int argc, char ** argv) try #endif #if defined(PSEMEK_GRAPHICS_API_WEBGPU) + context.instance = window.wgpu_instance(); context.adapter = window.wgpu_adapter(); context.surface = window.wgpu_surface(); context.device = window.wgpu_device(); diff --git a/libs/sdl2/source/window.cpp b/libs/sdl2/source/window.cpp index 34009f34..b35cfde5 100644 --- a/libs/sdl2/source/window.cpp +++ b/libs/sdl2/source/window.cpp @@ -77,7 +77,7 @@ namespace psemek::sdl2 #elif defined(PSEMEK_GRAPHICS_API_WEBGPU) wgpu::setup_logging(log::level::error); - auto instance = wgpu::instance::create({}); + wgpu_instance_ = wgpu::instance::create({}); SDL_SysWMinfo wminfo; SDL_VERSION(&wminfo.version); @@ -89,14 +89,14 @@ namespace psemek::sdl2 #else surface_descriptor.chain = {wgpu::surface::from_xlib_window{ .display = wminfo.info.x11.display, .window = wminfo.info.x11.window }}; #endif - wgpu_surface_ = instance.create_surface(surface_descriptor); + wgpu_surface_ = wgpu_instance_.create_surface(surface_descriptor); wgpu::adapter::request_options adapter_request_options; // adapter_request_options.feature_level = wgpu::feature_level::compatibility; adapter_request_options.compatible_surface = wgpu_surface_; adapter_request_options.backend_type = wgpu::backend_type::undefined; - instance.request_adapter(wgpu::callback_mode::allow_process_events, adapter_request_options, [this](wgpu::adapter::request_status status, wgpu::adapter adapter_in, std::string_view message) + wgpu_instance_.request_adapter(wgpu::callback_mode::allow_process_events, adapter_request_options, [this](wgpu::adapter::request_status status, wgpu::adapter adapter_in, std::string_view message) { if (status != wgpu::adapter::request_status::success) throw std::runtime_error("Failed to request WebGPU adapter: " + std::string(message)); From b83a3635fc89bf2c1b7ba409a479f73b9ad889c3 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 10 Aug 2025 14:45:17 +0300 Subject: [PATCH 070/141] Remove modification callbacks API from ecs::container - ain't gonna implement them anyway --- libs/ecs/include/psemek/ecs/container.hpp | 48 ----------------------- 1 file changed, 48 deletions(-) diff --git a/libs/ecs/include/psemek/ecs/container.hpp b/libs/ecs/include/psemek/ecs/container.hpp index 26228e54..c9e31370 100644 --- a/libs/ecs/include/psemek/ecs/container.hpp +++ b/libs/ecs/include/psemek/ecs/container.hpp @@ -22,11 +22,8 @@ namespace psemek::ecs // TODO: // - Fully document which functions can be called from which callbacks - // - Modification callbacks implementation (const-only) // - Tables serialization // - Refactor query caches - // - Index API - // - Index implementation struct container { @@ -296,12 +293,7 @@ namespace psemek::ecs * The constructor function must have the same signature as a function * passed to the `apply()` call. * - * The constuctor is not considered to be a modification of the entity, i.e. it - * doesn't trigger modification callbacks. - * * @param function A function to be applied to created entity's components - * @return An ownerwhip token; destroying this token removes - * the constructor from this container * @warning If any two of the passed component types are equal, the call fails with * a compilation error * @warning If the constructor modifies the entity's archetype (i.e. attaches or @@ -335,11 +327,7 @@ namespace psemek::ecs * * Note that there is no way to cancel the entity's destruction. * - * The destructor is not considered to be a modification of the entity, i.e. it - * doesn't trigger modification callbacks. - * * @param function A function to be applied to a to-be destroyed entity's components - * @return An ownerwhip token; destroying it removes the destructor from the container * @warning If any two of the passed component types are equal, the call fails with * a compilation error * @warning If the destructor modifies the entity's archetype (i.e. attaches or @@ -349,42 +337,6 @@ namespace psemek::ecs template void destructor(Function && function); - /** Register a component modification callback. Each time an entity that has - * the specified set of components is modified, and the modification affects - * one of this callback's component types, the callback is called. - * - * The component types can be const-qualified, in which case the corresponding function must - * also accept the corresponding components by a const reference. - * - * The component types can be equal to ecs::without, indicating that entities having - * this component type will not be watched by this callback. These component types are not - * included in the called function signature. - * - * If the modification occurred via an accessor, the callback is called - * after the accessor is destroyed, allowing for transaction-like modification. - * - * If the modification occurred via an `apply()` or `batch_apply()` call, - * the callback is called immediately after this call. - * - * Note that modifications from within a modification callback are not considered - * as modifications, i.e. they don't recursively invoke modification callbacks. - * - * The callback function must have the same signature as a function - * passed to the `apply()` call. - * - * @param function A function to be applied to modified entity's components - * @return An ownerwhip token; destroying it removes the modification callback from the container - * @warning If any two of the passed component types are equal, the call fails with - * a compilation error - * @warning If the modification callback modifies the entity's archetype (i.e. attaches or - * detaches components), the behavior is undefined - * - * TODO: can we allow the callback to modify the archetype? - */ - // TODO: implement - template - void watch(Function && function); - /** Call a callback after exiting all currently executing ECS container methods. * * If no ECS container method is currently executed, call the callback immediately instead. From c7b1bc0b0a0aa77aad5613fdb5909587b57005dd Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 17 Aug 2025 21:09:31 +0300 Subject: [PATCH 071/141] Fix throwing key_error in util::hash_table for heterogeneous keys --- libs/util/include/psemek/util/hash_table.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/util/include/psemek/util/hash_table.hpp b/libs/util/include/psemek/util/hash_table.hpp index f1f13951..590e903a 100644 --- a/libs/util/include/psemek/util/hash_table.hpp +++ b/libs/util/include/psemek/util/hash_table.hpp @@ -655,7 +655,7 @@ namespace psemek::util { auto it = find(key); if (it == end()) - throw util::key_error(key); + throw util::key_error(Key(key)); return it->second; } @@ -672,7 +672,7 @@ namespace psemek::util { auto it = find(key); if (it == end()) - throw util::key_error(key); + throw util::key_error(Key(key)); return it->second; } From fa214ed956b930e67fa4f65d546904ad511e431d Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 24 Aug 2025 12:09:00 +0300 Subject: [PATCH 072/141] Support more generic index factories in ecs container --- libs/ecs/include/psemek/ecs/container.hpp | 9 ++++++ .../psemek/ecs/detail/index_container.hpp | 31 ++++++++++++++----- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/libs/ecs/include/psemek/ecs/container.hpp b/libs/ecs/include/psemek/ecs/container.hpp index c9e31370..1a8b24fa 100644 --- a/libs/ecs/include/psemek/ecs/container.hpp +++ b/libs/ecs/include/psemek/ecs/container.hpp @@ -364,6 +364,9 @@ namespace psemek::ecs template Index & index(Args && ... args); + template + Index & index_factory(Factory && factory); + template std::size_t memory_usage(); @@ -767,6 +770,12 @@ namespace psemek::ecs return index_container_.get(*this, std::forward(args)...); } + template + Index & container::index_factory(Factory && factory) + { + return index_container_.set(std::move(factory)); + } + template std::size_t container::memory_usage() { diff --git a/libs/ecs/include/psemek/ecs/detail/index_container.hpp b/libs/ecs/include/psemek/ecs/detail/index_container.hpp index a13bdbe5..d0e71160 100644 --- a/libs/ecs/include/psemek/ecs/detail/index_container.hpp +++ b/libs/ecs/include/psemek/ecs/detail/index_container.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -24,19 +25,21 @@ namespace psemek::ecs::detail { auto uuid = Index::uuid(); if (auto it = storage_.find(uuid); it != storage_.end()) - return *reinterpret_cast(it->second.get()); + return *reinterpret_cast(it->second()); if constexpr (std::is_constructible_v) { - auto ptr = std::make_shared(container, std::forward(args)...); - storage_.insert({uuid, ptr}); - return *ptr; + auto ptr = std::make_unique(container, std::forward(args)...); + auto result = ptr.get(); + storage_.insert({uuid, [ptr = std::move(ptr)]{ return ptr.get(); }}); + return *result; } else if constexpr (std::is_constructible_v) { - auto ptr = std::make_shared(std::forward(args)...); - storage_.insert({uuid, ptr}); - return *ptr; + auto ptr = std::make_unique(std::forward(args)...); + auto result = ptr.get(); + storage_.insert({uuid, [ptr = std::move(ptr)]{ return ptr.get(); }}); + return *result; } else { @@ -44,8 +47,20 @@ namespace psemek::ecs::detail } } + template + Index & set(Factory && factory) + { + auto uuid = Index::uuid(); + if (storage_.contains(uuid)) + throw util::exception("Index " + util::type_name() + " is already set"); + + auto result = factory(); + storage_[Index::uuid()] = std::move(factory); + return *result; + } + private: - util::hash_map> storage_; + util::hash_map> storage_; }; } From d2f35276bfec59636390cbebe773ede9280a0fb9 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 29 Aug 2025 16:39:19 +0300 Subject: [PATCH 073/141] Add util::is_pow2 --- libs/util/include/psemek/util/bits.hpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libs/util/include/psemek/util/bits.hpp b/libs/util/include/psemek/util/bits.hpp index 38d7343b..0e5e5d1e 100644 --- a/libs/util/include/psemek/util/bits.hpp +++ b/libs/util/include/psemek/util/bits.hpp @@ -5,8 +5,15 @@ namespace psemek::util { - // log2 rounded down + template + bool is_pow2(T x) + { + static_assert(std::is_integral_v); + return (x > 0) && ((x & (x - 1)) == 0); + } + + // log2 rounded down template T log2(T x) { From 905e98070cdec3818fac44b966c4a83750da5520 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 29 Aug 2025 16:39:28 +0300 Subject: [PATCH 074/141] Fix includes in util/recursive.hpp --- libs/util/include/psemek/util/recursive.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/util/include/psemek/util/recursive.hpp b/libs/util/include/psemek/util/recursive.hpp index 486810ac..ce6395ff 100644 --- a/libs/util/include/psemek/util/recursive.hpp +++ b/libs/util/include/psemek/util/recursive.hpp @@ -1,5 +1,7 @@ #pragma once +#include + namespace psemek::util { From 2cce0082cf45259d9144d5951e1e9bf9b1f840b8 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 31 Aug 2025 14:45:52 +0300 Subject: [PATCH 075/141] Fix math::smooth(er)step in case the type isn't implicitly constructible from integers --- libs/math/include/psemek/math/math.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/math/include/psemek/math/math.hpp b/libs/math/include/psemek/math/math.hpp index f3e598ea..19e11e24 100644 --- a/libs/math/include/psemek/math/math.hpp +++ b/libs/math/include/psemek/math/math.hpp @@ -216,13 +216,13 @@ namespace psemek::math template T smoothstep(T x) { - return x * x * (3 - 2 * x); + return x * x * (T{3} - T{2} * x); } template T smootherstep(T x) { - return x * x * x * (10 - x * (15 - 6 * x)); + return x * x * x * (T{10} - x * (T{15} - T{6} * x)); } template From 78ea54ac2dc72aa5b44041bd7c567461490cec65 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 31 Aug 2025 14:46:20 +0300 Subject: [PATCH 076/141] Add math::dual weak comparison operators & some transcendental functions --- libs/math/include/psemek/math/dual.hpp | 45 ++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/libs/math/include/psemek/math/dual.hpp b/libs/math/include/psemek/math/dual.hpp index cca32a95..dc198927 100644 --- a/libs/math/include/psemek/math/dual.hpp +++ b/libs/math/include/psemek/math/dual.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace psemek::math { @@ -148,6 +149,50 @@ namespace psemek::math return v * inverse(d); } + // Exists mostly to make max/min/clamp/etc work + template + std::partial_ordering operator <=> (dual const & d1, dual const & d2) + { + return d1.scalar <=> d2.scalar; + } + + template + dual abs(dual const & d) + { + return d.scalar > T{0} ? d : -d; + } + + template + dual exp(dual const & d) + { + auto e = std::exp(d.scalar); + return {e, e * d.delta}; + } + + template + dual log(dual const & d) + { + return {std::log(d.scalar), d.delta / d.scalar}; + } + + template + dual sin(dual const & d) + { + return {std::sin(d.scalar), std::cos(d.scalar) * d.delta}; + } + + template + dual cos(dual const & d) + { + return {std::cos(d.scalar), - std::sin(d.scalar) * d.delta}; + } + + template + dual pow(dual const & d1, dual const & d2) + { + return exp(log(d1) * d2); + } + template std::ostream & operator << (std::ostream & os, dual const & d) { From 8784938e9786ad54d3135b3cbb3c8546065915ca Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 3 Sep 2025 20:20:16 +0300 Subject: [PATCH 077/141] Make async::future shared & support multiple .then() calls --- libs/async/include/psemek/async/future.hpp | 39 ++++++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/libs/async/include/psemek/async/future.hpp b/libs/async/include/psemek/async/future.hpp index e6863889..0945aed5 100644 --- a/libs/async/include/psemek/async/future.hpp +++ b/libs/async/include/psemek/async/future.hpp @@ -55,7 +55,8 @@ namespace psemek::async std::weak_ptr weak_cancel; std::mutex then_mutex; - typename then_function::type then_func; + std::vector::type> then_funcs; + std::vector> then_exception_funcs; }; } @@ -85,14 +86,16 @@ namespace psemek::async using result_type = T; future() = default; - future(future&&) = default; + future(future const &) = default; + future(future &&) = default; future(std::shared_ptr> state, std::shared_ptr cancel) : state_(std::move(state)) , cancel_(std::move(cancel)) {} - future & operator = (future&&) = default; + future & operator = (future const &) = default; + future & operator = (future &&) = default; ~future() { @@ -239,8 +242,10 @@ namespace psemek::async state_->value = value; } state_->value_cv.notify_all(); - if (state_->then_func) - state_->then_func(*state_->value); + + std::lock_guard lock{state_->then_mutex}; + for (auto & func : state_->then_funcs) + func(*state_->value); } void set_value(T && value) @@ -252,8 +257,10 @@ namespace psemek::async state_->value = std::move(value); } state_->value_cv.notify_all(); - if (state_->then_func) - state_->then_func(*state_->value); + + std::lock_guard lock{state_->then_mutex}; + for (auto & func : state_->then_funcs) + func(*state_->value); } void set_exception(std::exception_ptr e) @@ -263,6 +270,10 @@ namespace psemek::async std::lock_guard lock{state_->value_mutex}; if (state_->value || state_->exception) throw satisfied_promise_error{}; state_->exception = std::move(e); + + std::lock_guard lock_then{state_->then_mutex}; + for (auto & func : state_->then_exception_funcs) + func(state_->exception); } state_->value_cv.notify_all(); } @@ -324,8 +335,10 @@ namespace psemek::async state_->value = true; } state_->value_cv.notify_all(); - if (state_->then_func) - state_->then_func(); + + std::lock_guard lock{state_->then_mutex}; + for (auto & func : state_->then_funcs) + func(); } void set_exception(std::exception_ptr e) @@ -335,6 +348,10 @@ namespace psemek::async std::lock_guard lock{state_->value_mutex}; if (state_->value || state_->exception) throw satisfied_promise_error{}; state_->exception = std::move(e); + + std::lock_guard lock_then{state_->then_mutex}; + for (auto & func : state_->then_exception_funcs) + func(state_->exception); } state_->value_cv.notify_all(); } @@ -488,7 +505,7 @@ namespace psemek::async auto fut = t.get_future(); std::lock_guard lock{state_->then_mutex}; - state_->then_func = std::move(t); + state_->then_funcs.push_back(std::move(t)); return fut; } else @@ -498,7 +515,7 @@ namespace psemek::async auto fut = t.get_future(); std::lock_guard lock{state_->then_mutex}; - state_->then_func = std::move(t); + state_->then_funcs.push_back(std::move(t)); return fut; } } From 3e4d4f39ea41cac8149253302d81f8aa145f69ac Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 4 Sep 2025 00:54:04 +0300 Subject: [PATCH 078/141] Fix NaN's in profiling statistics --- libs/util/include/psemek/util/statistics.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libs/util/include/psemek/util/statistics.hpp b/libs/util/include/psemek/util/statistics.hpp index e7643125..7a747f7a 100644 --- a/libs/util/include/psemek/util/statistics.hpp +++ b/libs/util/include/psemek/util/statistics.hpp @@ -120,8 +120,11 @@ namespace psemek::util base_statistics result; result.count_ = s1.count_ + s2.count_; - result.mean_ = (s1.count_ * s1.mean_ + s2.count_ * s2.mean_) / result.count_; - result.variance_ = (s1.count_ * (s1.variance_ + sqr(s1.mean_ - result.mean_)) + s2.count_ * (s2.variance_ + sqr(s2.mean_ - result.mean_))) / result.count_; + if (result.count_ > 0) + { + result.mean_ = (s1.count_ * s1.mean_ + s2.count_ * s2.mean_) / result.count_; + result.variance_ = (s1.count_ * (s1.variance_ + sqr(s1.mean_ - result.mean_)) + s2.count_ * (s2.variance_ + sqr(s2.mean_ - result.mean_))) / result.count_; + } return result; } From 5e7a9b7697a4733f5985b2054c3cce314148135c Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 4 Sep 2025 00:54:26 +0300 Subject: [PATCH 079/141] Fix saving thread names in logger --- libs/log/source/log.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/libs/log/source/log.cpp b/libs/log/source/log.cpp index a452eca2..582f8341 100644 --- a/libs/log/source/log.cpp +++ b/libs/log/source/log.cpp @@ -24,7 +24,7 @@ namespace psemek::log std::atomic max_thread_name_length = 3; std::mutex thread_names_mutex; - util::hash_map thread_names; + util::hash_map> thread_names; std::mutex sinks_mutex; std::vector> sinks; @@ -163,10 +163,14 @@ namespace psemek::log if (it != thread_names.end()) throw util::exception("Thread \"" + name + "\" already registered!"); - thread_names[id] = name; + thread_names[id] = std::make_unique(name); } - max_thread_name_length = std::max(max_thread_name_length.load(), name.size()); + { + std::size_t current_max = max_thread_name_length.load(); + while(current_max < name.size() && !max_thread_name_length.compare_exchange_weak(current_max, name.size())) + {} + } put_message(level::info, "Thread \"" + name + "\" registered"); } @@ -185,7 +189,7 @@ namespace psemek::log throw util::exception(os.str()); } - name = std::move(it->second); + name = std::move(*(it->second)); thread_names.erase(it); } @@ -236,7 +240,7 @@ namespace psemek::log if (it == thread_names.end()) thread_name = &unknown_thread_name; else - thread_name = &(it->second); + thread_name = it->second.get(); } put_message(l, message, *thread_name); From 059b2b1539b070712e4c85eb5958f051df4625dd Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 4 Sep 2025 00:55:04 +0300 Subject: [PATCH 080/141] Use null util::function instead of a stop_execution exception when stopping threadpool threads to prevent exceptions from triggering when debugging --- libs/async/include/psemek/async/executor.hpp | 1 - libs/async/include/psemek/async/threadpool.hpp | 1 - libs/async/source/threadpool.cpp | 18 +++++------------- 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/libs/async/include/psemek/async/executor.hpp b/libs/async/include/psemek/async/executor.hpp index 74fab0a5..56e3c004 100644 --- a/libs/async/include/psemek/async/executor.hpp +++ b/libs/async/include/psemek/async/executor.hpp @@ -4,7 +4,6 @@ #include #include -#include namespace psemek::async { diff --git a/libs/async/include/psemek/async/threadpool.hpp b/libs/async/include/psemek/async/threadpool.hpp index 381d3d1a..99b1501b 100644 --- a/libs/async/include/psemek/async/threadpool.hpp +++ b/libs/async/include/psemek/async/threadpool.hpp @@ -4,7 +4,6 @@ #include #include -#include #include #include #include diff --git a/libs/async/source/threadpool.cpp b/libs/async/source/threadpool.cpp index c5b5d417..c4501f7f 100644 --- a/libs/async/source/threadpool.cpp +++ b/libs/async/source/threadpool.cpp @@ -9,13 +9,6 @@ namespace psemek::async { - namespace - { - - struct stop_execution{}; - - } - threadpool::threadpool(std::string const & name, std::size_t thread_count) : working_count_{0} { @@ -25,10 +18,13 @@ namespace psemek::async threads_.emplace_back([this, tname = std::move(tname)]() mutable { log::thread_registrator reg(std::move(tname)); - for (bool running = true; running;) + while (true) { auto task = task_queue_.pop(); + if (!task) + break; + { std::lock_guard lock{working_count_mutex_}; ++working_count_; @@ -38,10 +34,6 @@ namespace psemek::async { task(); } - catch (stop_execution const &) - { - running = false; - } catch (util::exception const & e) { log::error() << "Unhandled exception in threadpool executor: " << e; @@ -86,7 +78,7 @@ namespace psemek::async for (auto const & thread: threads_) { unused(thread); - task_queue_.push([]{ throw stop_execution{}; }); + task_queue_.push(nullptr); } threads_.clear(); } From 56d23480b0dfb0729bd69eb12278292aab7c077f Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 4 Sep 2025 14:18:18 +0300 Subject: [PATCH 081/141] Add async::make_ready_future(T const &) --- libs/async/include/psemek/async/future.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libs/async/include/psemek/async/future.hpp b/libs/async/include/psemek/async/future.hpp index 0945aed5..119bfb6c 100644 --- a/libs/async/include/psemek/async/future.hpp +++ b/libs/async/include/psemek/async/future.hpp @@ -379,6 +379,14 @@ namespace psemek::async return p.get_future(); } + template + future make_ready_future(T const & x) + { + promise p; + p.set_value(x); + return p.get_future(); + } + template struct packaged_task; From 0210b61540bc2f514c68e9d54ab363c13dc6633f Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 5 Sep 2025 19:35:36 +0300 Subject: [PATCH 082/141] Tweak util::fixed_point comments --- libs/util/include/psemek/util/fixed_point.hpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libs/util/include/psemek/util/fixed_point.hpp b/libs/util/include/psemek/util/fixed_point.hpp index 20295b43..8827092a 100644 --- a/libs/util/include/psemek/util/fixed_point.hpp +++ b/libs/util/include/psemek/util/fixed_point.hpp @@ -20,7 +20,7 @@ namespace psemek::util * represented as an (I+F)-bit-wide standard signed integer type, and the * stored value is interpreted as representing the fraction (value / 2^F). * It is capable of representing values in the range from (- 2^(I+F-1)) / 2^F - * to (2^(I+F-1) - 1) / 2^F, with a fixed precision of 2^F. + * to (2^(I+F-1) - 1) / 2^F, with a fixed precision of 1 / 2^F. * * For example, * @@ -40,7 +40,9 @@ namespace psemek::util * separate question...). * * Multiplication and division are implemented by casting to a 2x wider integer type, - * to increase precision and prevent overflows. + * to increase precision and prevent overflows. Note that the result is still truncated + * to the original size, i.e. multiplying two 16.16 fixed-points gives a 16.16 fixed-point + * as well. * * Typical usage of this class would be to make a type alias like * @@ -345,6 +347,8 @@ namespace psemek::util if (p < 0) return fixed_point(1) / pow(x, -p); + // Binary exponentiation algorithm + fixed_point result(1); while (p > 0) { From 5c0d4d8d291ecb94aae4711c7a1bcb0790e22fa4 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sat, 6 Sep 2025 15:35:07 +0300 Subject: [PATCH 083/141] Make future return reference instead of value --- libs/async/include/psemek/async/future.hpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/libs/async/include/psemek/async/future.hpp b/libs/async/include/psemek/async/future.hpp index 119bfb6c..ca1cda75 100644 --- a/libs/async/include/psemek/async/future.hpp +++ b/libs/async/include/psemek/async/future.hpp @@ -40,6 +40,18 @@ namespace psemek::async using type = util::function; }; + template + struct future_result_type + { + using type = T &; + }; + + template <> + struct future_result_type + { + using type = void; + }; + struct cancel_token {}; @@ -139,7 +151,7 @@ namespace psemek::async return has_value_unsafe(); } - T get() + typename detail::future_result_type::type get() { if (!state_) throw empty_future_error{}; @@ -151,7 +163,7 @@ namespace psemek::async if constexpr (std::is_same_v) return; else - return std::move(*(state_->value)); + return *(state_->value); } else std::rethrow_exception(state_->exception); From 1b548f1ba3d18395ac3a741ab136054e2a1e9e43 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 7 Sep 2025 13:59:06 +0300 Subject: [PATCH 084/141] Profile tasks execution in async::threadpool --- libs/async/CMakeLists.txt | 6 +++++- libs/async/source/threadpool.cpp | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libs/async/CMakeLists.txt b/libs/async/CMakeLists.txt index d911e5fc..97977802 100644 --- a/libs/async/CMakeLists.txt +++ b/libs/async/CMakeLists.txt @@ -3,4 +3,8 @@ file(GLOB_RECURSE PSEMEK_ASYNC_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "s psemek_add_library(psemek-async ${PSEMEK_ASYNC_HEADERS} ${PSEMEK_ASYNC_SOURCES}) target_include_directories(psemek-async PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") -target_link_libraries(psemek-async PUBLIC psemek-log psemek-util) +target_link_libraries(psemek-async PUBLIC + psemek-log + psemek-util + psemek-prof +) diff --git a/libs/async/source/threadpool.cpp b/libs/async/source/threadpool.cpp index c4501f7f..81f8d8eb 100644 --- a/libs/async/source/threadpool.cpp +++ b/libs/async/source/threadpool.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -32,6 +33,7 @@ namespace psemek::async try { + prof::profiler prof("task"); task(); } catch (util::exception const & e) From 3bb8bd36f34fccd5c3b8bdcd6f55757a4fc854e3 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 7 Sep 2025 13:59:16 +0300 Subject: [PATCH 085/141] Add math::sqrt(dual) --- libs/math/include/psemek/math/dual.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libs/math/include/psemek/math/dual.hpp b/libs/math/include/psemek/math/dual.hpp index dc198927..6de9473e 100644 --- a/libs/math/include/psemek/math/dual.hpp +++ b/libs/math/include/psemek/math/dual.hpp @@ -175,6 +175,13 @@ namespace psemek::math return {std::log(d.scalar), d.delta / d.scalar}; } + template + dual sqrt(dual const & d) + { + auto s = std::sqrt(d.scalar); + return {s, d.delta / (T{2} * s)}; + } + template dual sin(dual const & d) { From 3b89037c25a807575226d1b6fbae65b68536a794 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 7 Sep 2025 13:59:43 +0300 Subject: [PATCH 086/141] Pass string_view instead of string in profiler scopes --- libs/prof/include/psemek/prof/profiler.hpp | 10 +++++----- libs/prof/source/profiler.cpp | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/libs/prof/include/psemek/prof/profiler.hpp b/libs/prof/include/psemek/prof/profiler.hpp index bd1796c8..daa9d935 100644 --- a/libs/prof/include/psemek/prof/profiler.hpp +++ b/libs/prof/include/psemek/prof/profiler.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include namespace psemek::prof { @@ -12,7 +12,7 @@ namespace psemek::prof struct profiler { - profiler(std::string const & name); + profiler(std::string_view name); profiler(profiler && other); profiler(profiler const & other) = delete; @@ -21,12 +21,12 @@ namespace psemek::prof ~profiler(); - static void start_frame(std::string const & name); - static void push(std::string const & name, std::chrono::duration duration, std::size_t count = 1); + static void start_frame(std::string_view name); + static void push(std::string_view name, std::chrono::duration duration, std::size_t count = 1); static void end_frame(std::chrono::duration duration); template - static void push(std::string const & name, Duration duration, std::size_t count = 1) + static void push(std::string_view name, Duration duration, std::size_t count = 1) { push(name, std::chrono::duration_cast>(duration), count); } diff --git a/libs/prof/source/profiler.cpp b/libs/prof/source/profiler.cpp index a7f03b03..6d1785a3 100644 --- a/libs/prof/source/profiler.cpp +++ b/libs/prof/source/profiler.cpp @@ -23,15 +23,15 @@ namespace psemek::prof util::statistics_log_bucket execution_time{0.05}; - std::map children; + std::map> children; std::vector::iterator> children_list; - profiler_tree * child(std::string const & name) + profiler_tree * child(std::string_view const & name) { auto it = children.find(name); if (it == children.end()) { - it = children.insert({name, profiler_tree{}}).first; + it = children.insert({std::string(name), profiler_tree{}}).first; it->second.parent = this; it->second.mutex = mutex; children_list.push_back(it); @@ -174,7 +174,7 @@ namespace psemek::prof dump_impl(merged_tree(), 0, level); } - profiler::profiler(std::string const & name) + profiler::profiler(std::string_view name) { get_current(); @@ -204,7 +204,7 @@ namespace psemek::prof report(); } - void profiler::start_frame(std::string const & name) + void profiler::start_frame(std::string_view name) { get_current(); @@ -212,7 +212,7 @@ namespace psemek::prof current = current->child(name); } - void profiler::push(std::string const & name, std::chrono::duration duration, std::size_t count) + void profiler::push(std::string_view name, std::chrono::duration duration, std::size_t count) { get_current(); From 3b88908534715e1d3136dd566e6b8326694de6bb Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 7 Sep 2025 13:59:57 +0300 Subject: [PATCH 087/141] Log max execution time in profiler dump --- libs/prof/source/profiler.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/prof/source/profiler.cpp b/libs/prof/source/profiler.cpp index 6d1785a3..6860148e 100644 --- a/libs/prof/source/profiler.cpp +++ b/libs/prof/source/profiler.cpp @@ -115,6 +115,7 @@ namespace psemek::prof << " 25% " << pretty(stat.percentile(0.25)) << " 50% " << pretty(stat.percentile(0.50)) << " 75% " << pretty(stat.percentile(0.75)) + << " max " << pretty(stat.max()) << " (" << stat.count() << " calls, " << std::setprecision(3) << (stat.count() * 1.f / node.execution_time.count() * 1.f) << "x, " << std::setprecision(2) << (100.f * stat.count() * stat.mean() / node.execution_time.mean() / node.execution_time.count()) << "%)"; else @@ -123,6 +124,7 @@ namespace psemek::prof << " 25% " << pretty(stat.percentile(0.25)) << " 50% " << pretty(stat.percentile(0.50)) << " 75% " << pretty(stat.percentile(0.75)) + << " max " << pretty(stat.max()) << " (" << stat.count() << " calls)"; dump_impl(c.second, depth + 1, level); } From 08f62bdaf75ae5e6dbf5e8be7ce07098661cf84b Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 7 Sep 2025 14:00:25 +0300 Subject: [PATCH 088/141] Rewrite math::angle_difference without trigonometry --- libs/math/include/psemek/math/math.hpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/libs/math/include/psemek/math/math.hpp b/libs/math/include/psemek/math/math.hpp index 19e11e24..8438e6c7 100644 --- a/libs/math/include/psemek/math/math.hpp +++ b/libs/math/include/psemek/math/math.hpp @@ -128,19 +128,22 @@ namespace psemek::math return std::pair{x1, x2}; } + // Moves the angle to the range [-pi, pi] + template + T normalize_angle(T a) + { + while (a < -static_cast(pi)) + a += static_cast(2 * pi); + while (a > static_cast(pi)) + a -= static_cast(2 * pi); + return a; + } + // returns (a1 - a0) template T angle_difference(T a0, T a1) { - T const x0 = std::cos(a0); - T const x1 = std::cos(a1); - T const y0 = std::sin(a0); - T const y1 = std::sin(a1); - - T const x = x0 * x1 + y0 * y1; - T const y = x0 * y1 - y0 * x1; - - return std::atan2(y, x); + return normalize_angle(a1 - a0); } template From 754b279b1ae0f51c6fd4dfb1c063103ad70ff9f0 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 7 Sep 2025 14:00:45 +0300 Subject: [PATCH 089/141] Optimize math::smootherstep somewhat --- libs/math/include/psemek/math/math.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/math/include/psemek/math/math.hpp b/libs/math/include/psemek/math/math.hpp index 8438e6c7..a19ef28d 100644 --- a/libs/math/include/psemek/math/math.hpp +++ b/libs/math/include/psemek/math/math.hpp @@ -225,7 +225,8 @@ namespace psemek::math template T smootherstep(T x) { - return x * x * x * (T{10} - x * (T{15} - T{6} * x)); + auto x2 = x * x; + return x * x2 * (T{10} - x * T{15} + x2 * T{6}); } template From be372ee0075edf5b422afac51a1fae624af27566 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 7 Sep 2025 14:01:18 +0300 Subject: [PATCH 090/141] Switch to a simpler & faster uniform real generation algorithm --- .../include/psemek/random/uniform_real.hpp | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/libs/random/include/psemek/random/uniform_real.hpp b/libs/random/include/psemek/random/uniform_real.hpp index 377197dc..531d7524 100644 --- a/libs/random/include/psemek/random/uniform_real.hpp +++ b/libs/random/include/psemek/random/uniform_real.hpp @@ -52,25 +52,12 @@ namespace psemek::random template T uniform_real_distribution::generate(RNG & rng) { - // see https://en.cppreference.com/w/cpp/numeric/random/generate_canonical - - static constexpr std::size_t digits = std::numeric_limits::digits; - static T const limit = std::pow(2, digits); + // Previously used an algorithm like in https://en.cppreference.com/w/cpp/numeric/random/generate_canonical + // But the simple algorithm here is way faster, so... T const R = static_cast(rng.max()) - static_cast(rng.min()) + 1; - T mult = T(1); - - T sum = T(0); - - while (mult < limit) - { - T inc = static_cast(rng() - rng.min()); - sum += inc * mult; - mult *= R; - } - - return sum / mult; + return static_cast(rng() - rng.min()) / R; } } From a42d025ffb8069e42cd090dd13a87b9664465370 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 14 Sep 2025 00:02:19 +0300 Subject: [PATCH 091/141] libs/math fixes in case scalar type isn't implicitly constructible from int --- libs/math/include/psemek/math/interval.hpp | 2 +- libs/math/include/psemek/math/matrix.hpp | 4 ++-- libs/math/include/psemek/math/point.hpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/math/include/psemek/math/interval.hpp b/libs/math/include/psemek/math/interval.hpp index d2a55d4e..80542481 100644 --- a/libs/math/include/psemek/math/interval.hpp +++ b/libs/math/include/psemek/math/interval.hpp @@ -98,7 +98,7 @@ namespace psemek::math T center() const { - return min + (max - min) / 2; + return min + (max - min) / T(2); } using iterator = interval_iterator; diff --git a/libs/math/include/psemek/math/matrix.hpp b/libs/math/include/psemek/math/matrix.hpp index eabfd12f..d3f81f6c 100644 --- a/libs/math/include/psemek/math/matrix.hpp +++ b/libs/math/include/psemek/math/matrix.hpp @@ -57,14 +57,14 @@ namespace psemek::math { matrix m; for (std::size_t i = 0; i < R * C; ++i) - m.coords[i] = 0; + m.coords[i] = T(0); return m; } template matrix matrix::identity() { - return scalar(T{1}); + return scalar(T(1)); } template diff --git a/libs/math/include/psemek/math/point.hpp b/libs/math/include/psemek/math/point.hpp index 7cb5d2bf..8b05a67d 100644 --- a/libs/math/include/psemek/math/point.hpp +++ b/libs/math/include/psemek/math/point.hpp @@ -56,7 +56,7 @@ namespace psemek::math { point p; for (std::size_t i = 0; i < N; ++i) - p[i] = 0; + p[i] = T(0); return p; } From 7beba986aeeb36f9aae10de5ea8dc8730f38132f Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 14 Sep 2025 00:02:28 +0300 Subject: [PATCH 092/141] Add math::cast(ray) --- libs/math/include/psemek/math/ray.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/math/include/psemek/math/ray.hpp b/libs/math/include/psemek/math/ray.hpp index c517d9dd..7611c51d 100644 --- a/libs/math/include/psemek/math/ray.hpp +++ b/libs/math/include/psemek/math/ray.hpp @@ -58,6 +58,12 @@ namespace psemek::math return res; } + template + ray cast(ray const & r) + { + return {cast(r.origin), cast(r.direction)}; + } + template std::ostream & operator << (std::ostream & os, ray const & r) { From 8a739bf637f2a43b738f8fa6ec5deb0102755467 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 14 Sep 2025 00:03:07 +0300 Subject: [PATCH 093/141] Fixed-point improvements: * Mark everything constexpr * Use unsigned type for multiplication to prevent UB on overflow * Add fp -> fp conversions * Add floor & ceil functions --- libs/util/include/psemek/util/fixed_point.hpp | 129 ++++++++++++------ 1 file changed, 90 insertions(+), 39 deletions(-) diff --git a/libs/util/include/psemek/util/fixed_point.hpp b/libs/util/include/psemek/util/fixed_point.hpp index 8827092a..116e480a 100644 --- a/libs/util/include/psemek/util/fixed_point.hpp +++ b/libs/util/include/psemek/util/fixed_point.hpp @@ -106,7 +106,7 @@ namespace psemek::util }; template - H clamp(T x, H min, H max) + constexpr H clamp(T x, H min, H max) { if (x < static_cast(min)) return min; @@ -147,32 +147,37 @@ namespace psemek::util using unsigned_rep_type = typename traits::unsigned_rep_type; using unsigned_wide_type = typename traits::unsigned_wide_type; - fixed_point() = default; - fixed_point(fixed_point const &) = default; + constexpr fixed_point() = default; + constexpr fixed_point(fixed_point const &) = default; + + constexpr fixed_point & operator = (fixed_point const &) = default; template - explicit fixed_point(T value); + constexpr explicit fixed_point(T value); template - explicit fixed_point(T value); + constexpr explicit fixed_point(T value); + + template + constexpr explicit fixed_point(fixed_point value); template - explicit operator T () const; + constexpr explicit operator T () const; template - explicit operator T () const; + constexpr explicit operator T () const; - fixed_point & operator += (fixed_point); - fixed_point & operator -= (fixed_point); - fixed_point & operator *= (fixed_point); - fixed_point & operator /= (fixed_point); + constexpr fixed_point & operator += (fixed_point); + constexpr fixed_point & operator -= (fixed_point); + constexpr fixed_point & operator *= (fixed_point); + constexpr fixed_point & operator /= (fixed_point); - rep_type rep() const { return rep_; } + constexpr rep_type rep() const { return rep_; } - static fixed_point from_rep(rep_type rep); + static constexpr fixed_point from_rep(rep_type rep); - static fixed_point min(); - static fixed_point max(); + static constexpr fixed_point min(); + static constexpr fixed_point max(); private: rep_type rep_; @@ -180,32 +185,47 @@ namespace psemek::util template template - fixed_point::fixed_point(T value) + constexpr fixed_point::fixed_point(T value) : rep_(static_cast(value) << F) {} template template - fixed_point::fixed_point(T value) + constexpr fixed_point::fixed_point(T value) : rep_(static_cast(detail::clamp(std::round(value * (static_cast(1) << F)), min().rep(), max().rep()))) {} + template + template + constexpr fixed_point::fixed_point(fixed_point value) + { + // TODO: what to do with signs? + if constexpr (F >= F2) + { + rep_ = static_cast(value.rep_) << (F - F2); + } + else + { + rep_ = static_cast(value.rep_) >> (F - F2); + } + } + template template - fixed_point::operator T () const + constexpr fixed_point::operator T () const { return static_cast(rep_) >> F; } template template - fixed_point::operator T () const + constexpr fixed_point::operator T () const { return rep_ / static_cast(static_cast(1) << F); } template - fixed_point fixed_point::from_rep(fixed_point::rep_type rep) + constexpr fixed_point fixed_point::from_rep(fixed_point::rep_type rep) { fixed_point result; result.rep_ = rep; @@ -213,7 +233,7 @@ namespace psemek::util } template - fixed_point fixed_point::min() + constexpr fixed_point fixed_point::min() { using FP = fixed_point; using rep_type = typename FP::rep_type; @@ -221,7 +241,7 @@ namespace psemek::util } template - fixed_point fixed_point::max() + constexpr fixed_point fixed_point::max() { using FP = fixed_point; using rep_type = typename FP::rep_type; @@ -229,7 +249,7 @@ namespace psemek::util } template - fixed_point operator - (fixed_point x) + constexpr fixed_point operator - (fixed_point x) { using FP = fixed_point; using rep_type = typename FP::rep_type; @@ -238,7 +258,7 @@ namespace psemek::util } template - fixed_point operator + (fixed_point x1, fixed_point x2) + constexpr fixed_point operator + (fixed_point x1, fixed_point x2) { using FP = fixed_point; using rep_type = typename FP::rep_type; @@ -247,7 +267,7 @@ namespace psemek::util } template - fixed_point operator - (fixed_point x1, fixed_point x2) + constexpr fixed_point operator - (fixed_point x1, fixed_point x2) { using FP = fixed_point; using rep_type = typename FP::rep_type; @@ -256,16 +276,16 @@ namespace psemek::util } template - fixed_point operator * (fixed_point x1, fixed_point x2) + constexpr fixed_point operator * (fixed_point x1, fixed_point x2) { using FP = fixed_point; using rep_type = typename FP::rep_type; - using wide_type = typename FP::wide_type; - return FP::from_rep(static_cast((static_cast(x1.rep()) * static_cast(x2.rep())) >> F)); + using unsigned_wide_type = typename FP::unsigned_wide_type; + return FP::from_rep(static_cast((static_cast(x1.rep()) * static_cast(x2.rep())) >> F)); } template - fixed_point operator / (fixed_point x1, fixed_point x2) + constexpr fixed_point operator / (fixed_point x1, fixed_point x2) { using FP = fixed_point; using rep_type = typename FP::rep_type; @@ -274,47 +294,47 @@ namespace psemek::util } template - fixed_point & fixed_point::operator += (fixed_point x) + constexpr fixed_point & fixed_point::operator += (fixed_point x) { (*this) = (*this) + x; return (*this); } template - fixed_point & fixed_point::operator -= (fixed_point x) + constexpr fixed_point & fixed_point::operator -= (fixed_point x) { (*this) = (*this) - x; return (*this); } template - fixed_point & fixed_point::operator *= (fixed_point x) + constexpr fixed_point & fixed_point::operator *= (fixed_point x) { (*this) = (*this) * x; return (*this); } template - fixed_point & fixed_point::operator /= (fixed_point x) + constexpr fixed_point & fixed_point::operator /= (fixed_point x) { (*this) = (*this) / x; return (*this); } template - bool operator == (fixed_point x1, fixed_point x2) + constexpr bool operator == (fixed_point x1, fixed_point x2) { return x1.rep() == x2.rep(); } template - auto operator <=> (fixed_point x1, fixed_point x2) + constexpr auto operator <=> (fixed_point x1, fixed_point x2) { return x1.rep() <=> x2.rep(); } template - fixed_point abs(fixed_point x) + constexpr fixed_point abs(fixed_point x) { using FP = fixed_point; using rep_type = typename FP::rep_type; @@ -325,11 +345,42 @@ namespace psemek::util } template - fixed_point round(fixed_point x) + constexpr fixed_point floor(fixed_point x) { using FP = fixed_point; using rep_type = typename FP::rep_type; - return FP::from_rep(x.rep() & ~((static_cast(1) << F) - 1)); + constexpr auto fixed_point_one = static_cast(1) << F; + constexpr auto fractional_mask = fixed_point_one - static_cast(1); + return FP::from_rep(x.rep() & ~fractional_mask); + } + + template + constexpr fixed_point ceil(fixed_point x) + { + using FP = fixed_point; + using rep_type = typename FP::rep_type; + constexpr auto fixed_point_one = static_cast(1) << F; + constexpr auto fractional_mask = fixed_point_one - static_cast(1); + return FP::from_rep(((x.rep() - static_cast(1)) & ~fractional_mask) + fixed_point_one); + } + + template + constexpr fixed_point round(fixed_point x) + { + if constexpr (F == 0) + { + return x; + } + else + { + using FP = fixed_point; + using rep_type = typename FP::rep_type; + constexpr auto fixed_point_half = static_cast(1) << (F - 1); + if ((x.rep() & fixed_point_half) == 0) + return floor(x); + else + return ceil(x); + } } template @@ -342,7 +393,7 @@ namespace psemek::util } template - fixed_point pow(fixed_point x, int p) + constexpr fixed_point pow(fixed_point x, int p) { if (p < 0) return fixed_point(1) / pow(x, -p); From 7300679f563611e50b388f9870f98719d3c168a9 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 14 Sep 2025 17:22:10 +0300 Subject: [PATCH 094/141] Properly initialize math::interval --- libs/math/include/psemek/math/interval.hpp | 42 +++++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/libs/math/include/psemek/math/interval.hpp b/libs/math/include/psemek/math/interval.hpp index 80542481..adb8934d 100644 --- a/libs/math/include/psemek/math/interval.hpp +++ b/libs/math/include/psemek/math/interval.hpp @@ -9,36 +9,68 @@ namespace psemek::math { + namespace detail + { + + template + concept has_min = requires () + { + { T::min() } -> std::same_as; + }; + + template + concept has_max = requires () + { + { T::max() } -> std::same_as; + }; + + } + // Can be specialized in client code template struct limits { static constexpr T min() { - if constexpr (std::is_floating_point_v) + if constexpr (detail::has_min) + { + return T::min(); + } + else if constexpr (std::is_floating_point_v) { return -std::numeric_limits::infinity(); } - else + else if constexpr (std::is_integral_v) { return std::numeric_limits::min(); } + else + { + static_assert("unknown type"); + } } static constexpr T max() { - if constexpr (std::is_floating_point_v) + if constexpr (detail::has_max) + { + return T::max(); + } + else if constexpr (std::is_floating_point_v) { return std::numeric_limits::infinity(); } - else + else if constexpr (std::is_integral_v) { return std::numeric_limits::max(); } + else + { + static_assert("unknown type"); + } } }; - template struct interval_iterator { From 7c5062e081797d4de211f55ed403ce8793c16f17 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Tue, 16 Sep 2025 20:44:42 +0300 Subject: [PATCH 095/141] Add hash_table::reserve --- libs/util/include/psemek/util/hash_table.hpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/libs/util/include/psemek/util/hash_table.hpp b/libs/util/include/psemek/util/hash_table.hpp index 590e903a..00463c09 100644 --- a/libs/util/include/psemek/util/hash_table.hpp +++ b/libs/util/include/psemek/util/hash_table.hpp @@ -269,6 +269,11 @@ namespace psemek::util return storage_.capacity; } + void reserve(std::size_t capacity) + { + ensure_capacity_for(capacity); + } + private: hash_table_storage storage_; std::size_t size_ = 0; @@ -516,6 +521,11 @@ namespace psemek::util return impl_.size(); } + void reserve(std::size_t capacity) + { + impl_.reserve(capacity); + } + private: detail::hash_table_impl impl_; }; @@ -691,6 +701,11 @@ namespace psemek::util return impl_.size() == 0; } + void reserve(std::size_t capacity) + { + impl_.reserve(capacity); + } + private: detail::hash_table_impl, KeyHash, KeyEqual, detail::pair_key_projector> impl_; }; From 8d25721d352850399020ea6b62adb49ee00ef824 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Tue, 16 Sep 2025 20:45:12 +0300 Subject: [PATCH 096/141] Don't call most SDL_GL_xxx functions when using WebGPU API --- libs/sdl2/source/window.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libs/sdl2/source/window.cpp b/libs/sdl2/source/window.cpp index b35cfde5..60632ad4 100644 --- a/libs/sdl2/source/window.cpp +++ b/libs/sdl2/source/window.cpp @@ -201,7 +201,9 @@ namespace psemek::sdl2 void window::swap() { +#if defined(PSEMEK_GRAPHICS_API_OPENGL) SDL_GL_SwapWindow(window_); +#endif } void window::show_cursor(bool show) @@ -215,8 +217,9 @@ namespace psemek::sdl2 SDL_SetRelativeMouseMode(mode ? SDL_TRUE: SDL_FALSE); } - void window::vsync(bool on) + void window::vsync([[maybe_unused]] bool on) { +#if defined(PSEMEK_GRAPHICS_API_OPENGL) log::info() << "Turning VSync " << (on ? "on" : "off"); if (on) { @@ -231,6 +234,7 @@ namespace psemek::sdl2 { SDL_GL_SetSwapInterval(0); } +#endif } void window::windowed(bool on) From 4a4f680d0adc369f82c415e5edb463dc50c19ab2 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 10 Oct 2025 15:51:45 +0300 Subject: [PATCH 097/141] Add debug assertions for when an entity's archetype is changed while it is being created/destroyed --- libs/ecs/include/psemek/ecs/container.hpp | 29 +++++++++++++++++++++++ libs/ecs/source/container.cpp | 10 ++++++++ 2 files changed, 39 insertions(+) diff --git a/libs/ecs/include/psemek/ecs/container.hpp b/libs/ecs/include/psemek/ecs/container.hpp index 1a8b24fa..c112bb1d 100644 --- a/libs/ecs/include/psemek/ecs/container.hpp +++ b/libs/ecs/include/psemek/ecs/container.hpp @@ -394,6 +394,10 @@ namespace psemek::ecs std::size_t method_recursion_depth_ = 0; std::vector> finally_callbacks_; + #ifdef PSEMEK_DEBUG + util::hash_set currently_changing_archetype_; + #endif + detail::table * insert_table(std::vector> columns); void do_destroy(handle entity); void remove_row(detail::table & table, std::uint32_t row, util::span entities); @@ -434,6 +438,9 @@ namespace psemek::ecs auto row = table->row_count(); auto id = entity_list_.create(table, row); handle handle{id, entity_list_.get_entities()[id].epoch}; + #ifdef PSEMEK_DEBUG + currently_changing_archetype_.insert(handle); + #endif [[maybe_unused]] accessor accessor = get(handle); table->push_row(handle); @@ -441,6 +448,10 @@ namespace psemek::ecs table->trigger_constructors(*this, row); + #ifdef PSEMEK_DEBUG + currently_changing_archetype_.erase(handle); + #endif + finalize_method(); --method_recursion_depth_; @@ -453,6 +464,11 @@ namespace psemek::ecs { static_assert(detail::all_different_types_v...>, "all component types must be different"); + #ifdef PSEMEK_DEBUG + assert(!currently_changing_archetype_.contains(entity)); + currently_changing_archetype_.insert(entity); + #endif + ++method_recursion_depth_; (register_component(), ...); @@ -510,6 +526,10 @@ namespace psemek::ecs uuid_set_pool_.put(std::move(attached_uuid_set)); uuid_list_pool_.put(std::move(uuids)); + #ifdef PSEMEK_DEBUG + currently_changing_archetype_.erase(entity); + #endif + finalize_method(); --method_recursion_depth_; @@ -520,6 +540,11 @@ namespace psemek::ecs { static_assert(detail::all_different_types_v...>, "all component types must be different"); + #ifdef PSEMEK_DEBUG + assert(!currently_changing_archetype_.contains(entity)); + currently_changing_archetype_.insert(entity); + #endif + ++method_recursion_depth_; auto detached_uuid_set = uuid_set_pool_.get(); @@ -577,6 +602,10 @@ namespace psemek::ecs uuid_list_pool_.put(std::move(uuids)); uuid_set_pool_.put(std::move(detached_uuid_set)); + #ifdef PSEMEK_DEBUG + currently_changing_archetype_.erase(entity); + #endif + finalize_method(); --method_recursion_depth_; diff --git a/libs/ecs/source/container.cpp b/libs/ecs/source/container.cpp index 788ab5f3..b913960e 100644 --- a/libs/ecs/source/container.cpp +++ b/libs/ecs/source/container.cpp @@ -13,6 +13,11 @@ namespace psemek::ecs void container::destroy(handle entity) { + #ifdef PSEMEK_DEBUG + assert(!currently_changing_archetype_.contains(entity)); + currently_changing_archetype_.insert(entity); + #endif + assert(alive(entity)); ++method_recursion_depth_; @@ -23,10 +28,15 @@ namespace psemek::ecs do_destroy(entity); entity_list_.destroy(entity.id); + #ifdef PSEMEK_DEBUG + currently_changing_archetype_.erase(entity); + #endif + finalize_method(); --method_recursion_depth_; } + } accessor container::get(handle entity) { From 424bf5f533e50148a6c194eead7f9e20d2094761 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 10 Oct 2025 16:01:17 +0300 Subject: [PATCH 098/141] Add ecs::container::detach_finally/destroy_finally helpers --- libs/ecs/include/psemek/ecs/container.hpp | 16 ++++++++++++++++ libs/ecs/source/container.cpp | 7 +++++++ 2 files changed, 23 insertions(+) diff --git a/libs/ecs/include/psemek/ecs/container.hpp b/libs/ecs/include/psemek/ecs/container.hpp index c112bb1d..8ecd12e7 100644 --- a/libs/ecs/include/psemek/ecs/container.hpp +++ b/libs/ecs/include/psemek/ecs/container.hpp @@ -79,6 +79,9 @@ namespace psemek::ecs */ void destroy(handle entity); + /** Same as destroy(), but via finally() */ + void destroy_finally(handle entity); + /** Get an accessor for an entity, which provides access to the entity's components. * It is designed to be a single-use object; it cannot be stored as a reference to * the entity. Use handle for that instead. @@ -169,6 +172,10 @@ namespace psemek::ecs template void detach(handle entity); + /** Same as detach(), but via finally() */ + template + void detach_finally(handle entity); + /** Create a query cache that can be used to speed up `apply()` calls. * * The constness of the component types is ignored. @@ -611,6 +618,15 @@ namespace psemek::ecs --method_recursion_depth_; } + template + void container::detach_finally(handle entity) + { + finally([entity](container & world){ + if (world.alive(entity)) + world.detach(entity); + }); + } + template query_cache container::cache() { diff --git a/libs/ecs/source/container.cpp b/libs/ecs/source/container.cpp index b913960e..5396c6e0 100644 --- a/libs/ecs/source/container.cpp +++ b/libs/ecs/source/container.cpp @@ -36,6 +36,13 @@ namespace psemek::ecs --method_recursion_depth_; } + + void container::destroy_finally(handle entity) + { + finally([entity](container & world){ + if (world.alive(entity)) + world.destroy(entity); + }); } accessor container::get(handle entity) From b1ea7bc763f5a79200d615485d87202e47d28b49 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sat, 11 Oct 2025 00:39:57 +0300 Subject: [PATCH 099/141] Add gfx::luminance(color) --- libs/gfx/include/psemek/gfx/color.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/gfx/include/psemek/gfx/color.hpp b/libs/gfx/include/psemek/gfx/color.hpp index 7bec7f28..b4d01a28 100644 --- a/libs/gfx/include/psemek/gfx/color.hpp +++ b/libs/gfx/include/psemek/gfx/color.hpp @@ -382,6 +382,12 @@ namespace psemek::gfx return result; } + template + T luminance(math::vector const & c) + { + return c[0] * T(0.299) + c[1] * T(0.587) + c[2] * T(0.114); + } + std::optional parse_color(std::string_view const & text); } From 78e9731d8d6f8fa168b0c26290f467606978a58e Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sat, 18 Oct 2025 15:27:39 +0300 Subject: [PATCH 100/141] Fix math library std::formatters & make them use format rules derived from base scalar type --- libs/math/include/psemek/math/box.hpp | 24 ++++++------------ libs/math/include/psemek/math/interval.hpp | 15 ++++++----- libs/math/include/psemek/math/point.hpp | 26 ++++++++++---------- libs/math/include/psemek/math/quaternion.hpp | 12 ++++----- libs/math/include/psemek/math/vector.hpp | 22 ++++++++--------- 5 files changed, 47 insertions(+), 52 deletions(-) diff --git a/libs/math/include/psemek/math/box.hpp b/libs/math/include/psemek/math/box.hpp index 09aa56f9..407be6d1 100644 --- a/libs/math/include/psemek/math/box.hpp +++ b/libs/math/include/psemek/math/box.hpp @@ -355,27 +355,19 @@ namespace std template struct formatter<::psemek::math::box, Char> + : formatter<::psemek::math::interval> { - constexpr auto parse(std::format_parse_context & ctx) - { - return ctx.begin(); - } + using formatter<::psemek::math::interval>::parse; - auto format(::psemek::math::point const & p, std::format_context & ctx) const + template + auto format(::psemek::math::box const & b, FormatContext & ctx) const { - if constexpr (N == 0) + for (std::size_t i = 0; i < b.dimension(); ++i) { - return std::format_to(ctx.out(), "()"); - } - else - { - auto out = ctx.out(); - out = std::format_to(out, "({}", p[0]); - for (std::size_t i = 1; i < p.dimension(); ++i) - out = std::format_to(out, ", {}", p[i]); - out = std::format_to(out, ")"); - return out; + if (i > 0) ctx.advance_to(std::format_to(ctx.out(), "x")); + formatter<::psemek::math::interval>::format(b[i], ctx); } + return ctx.out(); } }; diff --git a/libs/math/include/psemek/math/interval.hpp b/libs/math/include/psemek/math/interval.hpp index adb8934d..352c3163 100644 --- a/libs/math/include/psemek/math/interval.hpp +++ b/libs/math/include/psemek/math/interval.hpp @@ -341,15 +341,18 @@ namespace std template struct formatter<::psemek::math::interval, Char> + : formatter { - constexpr auto parse(std::format_parse_context & ctx) - { - return ctx.begin(); - } + using formatter::parse; - auto format(::psemek::math::interval const & i, std::format_context & ctx) const + template + auto format(::psemek::math::interval const & i, FormatContext & ctx) const { - return std::format_to(ctx.out(), "[{} .. {}]", i.min, i.max); + ctx.advance_to(std::format_to(ctx.out(), "[")); + ctx.advance_to(formatter::format(i.min, ctx)); + ctx.advance_to(std::format_to(ctx.out(), " .. ")); + ctx.advance_to(formatter::format(i.max, ctx)); + return std::format_to(ctx.out(), "]"); } }; diff --git a/libs/math/include/psemek/math/point.hpp b/libs/math/include/psemek/math/point.hpp index 8b05a67d..afb4b004 100644 --- a/libs/math/include/psemek/math/point.hpp +++ b/libs/math/include/psemek/math/point.hpp @@ -223,25 +223,24 @@ namespace std template struct hash<::psemek::math::point> { - std::uint64_t operator()(::psemek::math::point const & v) const noexcept + std::uint64_t operator()(::psemek::math::point const & p) const noexcept { hash h; std::uint64_t r = 0; for (std::size_t i = 0; i < N; ++i) - ::psemek::util::hash_combine(r, h(v[i])); + ::psemek::util::hash_combine(r, h(p[i])); return r; } }; template struct formatter<::psemek::math::point, Char> + : formatter { - constexpr auto parse(std::format_parse_context & ctx) - { - return ctx.begin(); - } + using formatter::parse; - auto format(::psemek::math::point const & p, std::format_context & ctx) const + template + auto format(::psemek::math::point const & p, FormatContext & ctx) const { if constexpr (N == 0) { @@ -249,12 +248,13 @@ namespace std } else { - auto out = ctx.out(); - out = std::format_to(out, "({}", p[0]); - for (std::size_t i = 1; i < p.dimension(); ++i) - out = std::format_to(out, ", {}", p[i]); - out = std::format_to(out, ")"); - return out; + ctx.advance_to(std::format_to(ctx.out(), "(")); + for (std::size_t i = 0; i < p.dimension(); ++i) + { + if (i > 0) ctx.advance_to(std::format_to(ctx.out(), ", ")); + formatter::format(p[i], ctx); + } + return std::format_to(ctx.out(), ")"); } } }; diff --git a/libs/math/include/psemek/math/quaternion.hpp b/libs/math/include/psemek/math/quaternion.hpp index 3b644990..df6f6dbb 100644 --- a/libs/math/include/psemek/math/quaternion.hpp +++ b/libs/math/include/psemek/math/quaternion.hpp @@ -346,15 +346,15 @@ namespace std template struct formatter<::psemek::math::quaternion, Char> + : formatter<::psemek::math::vector> { - constexpr auto parse(std::format_parse_context & ctx) - { - return ctx.begin(); - } + using formatter<::psemek::math::vector>::parse; - auto format(::psemek::math::quaternion const & q, std::format_context & ctx) const + template + auto format(::psemek::math::quaternion const & q, FormatContext & ctx) const { - return std::format_to(ctx.out(), "{}", q.coords); + formatter<::psemek::math::vector>::format(q.coords, ctx); + return ctx.out(); } }; diff --git a/libs/math/include/psemek/math/vector.hpp b/libs/math/include/psemek/math/vector.hpp index 5c9d9709..e53a5ffb 100644 --- a/libs/math/include/psemek/math/vector.hpp +++ b/libs/math/include/psemek/math/vector.hpp @@ -524,13 +524,12 @@ namespace std template struct formatter<::psemek::math::vector, Char> + : formatter { - constexpr auto parse(std::format_parse_context & ctx) - { - return ctx.begin(); - } + using formatter::parse; - auto format(::psemek::math::vector const & v, std::format_context & ctx) const + template + auto format(::psemek::math::vector const & v, FormatContext & ctx) const { if constexpr (N == 0) { @@ -538,12 +537,13 @@ namespace std } else { - auto out = ctx.out(); - out = std::format_to(out, "({}", v[0]); - for (std::size_t i = 1; i < v.dimension(); ++i) - out = std::format_to(out, ", {}", v[i]); - out = std::format_to(out, ")"); - return out; + ctx.advance_to(std::format_to(ctx.out(), "(")); + for (std::size_t i = 0; i < v.dimension(); ++i) + { + if (i > 0) ctx.advance_to(std::format_to(ctx.out(), ", ")); + formatter::format(v[i], ctx); + } + return std::format_to(ctx.out(), ")"); } } }; From 615ee51229730f0f8decb8722b258e3d2d7c1c81 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sat, 18 Oct 2025 17:31:34 +0300 Subject: [PATCH 101/141] Support async::executor::clear() --- libs/async/include/psemek/async/event_loop.hpp | 2 ++ libs/async/include/psemek/async/executor.hpp | 3 +++ libs/async/include/psemek/async/synchronous_executor.hpp | 2 ++ libs/async/include/psemek/async/threadpool.hpp | 2 ++ libs/async/source/event_loop.cpp | 7 +++++-- libs/async/source/synchronous_executor.cpp | 5 +++-- libs/async/source/threadpool.cpp | 5 +++++ 7 files changed, 22 insertions(+), 4 deletions(-) diff --git a/libs/async/include/psemek/async/event_loop.hpp b/libs/async/include/psemek/async/event_loop.hpp index 35b7d9db..f19ca05e 100644 --- a/libs/async/include/psemek/async/event_loop.hpp +++ b/libs/async/include/psemek/async/event_loop.hpp @@ -17,6 +17,8 @@ namespace psemek::async void stop() override; + void clear() override; + void wait() override; void wait_for(clock::duration period) override; diff --git a/libs/async/include/psemek/async/executor.hpp b/libs/async/include/psemek/async/executor.hpp index 56e3c004..ae7a1348 100644 --- a/libs/async/include/psemek/async/executor.hpp +++ b/libs/async/include/psemek/async/executor.hpp @@ -29,6 +29,9 @@ namespace psemek::async // NB: the executor must call stop() from destructor. virtual void stop() = 0; + // Clear the pending tasks queue. + virtual void clear() = 0; + // Wait for all the tasks to be executed. // May take forever. virtual void wait() = 0; diff --git a/libs/async/include/psemek/async/synchronous_executor.hpp b/libs/async/include/psemek/async/synchronous_executor.hpp index 45b6fa3f..cc273176 100644 --- a/libs/async/include/psemek/async/synchronous_executor.hpp +++ b/libs/async/include/psemek/async/synchronous_executor.hpp @@ -14,6 +14,8 @@ namespace psemek::async void stop() override; + void clear() override; + void wait() override; void wait_for(clock::duration period) override; diff --git a/libs/async/include/psemek/async/threadpool.hpp b/libs/async/include/psemek/async/threadpool.hpp index 99b1501b..852349e2 100644 --- a/libs/async/include/psemek/async/threadpool.hpp +++ b/libs/async/include/psemek/async/threadpool.hpp @@ -31,6 +31,8 @@ namespace psemek::async void stop() override; + void clear() override; + void wait() override; void wait_for(clock::duration period) override; diff --git a/libs/async/source/event_loop.cpp b/libs/async/source/event_loop.cpp index 546694c8..0fdac888 100644 --- a/libs/async/source/event_loop.cpp +++ b/libs/async/source/event_loop.cpp @@ -1,7 +1,5 @@ #include -#include - namespace psemek::async { @@ -20,6 +18,11 @@ namespace psemek::async queue_.clear(); } + void event_loop::clear() + { + queue_.clear(); + } + void event_loop::wait() { while (!queue_.empty()) diff --git a/libs/async/source/synchronous_executor.cpp b/libs/async/source/synchronous_executor.cpp index 57179ce3..1662b854 100644 --- a/libs/async/source/synchronous_executor.cpp +++ b/libs/async/source/synchronous_executor.cpp @@ -1,8 +1,6 @@ #include #include -#include - namespace psemek::async { @@ -19,6 +17,9 @@ namespace psemek::async void synchronous_executor::stop() {} + void synchronous_executor::clear() + {} + void synchronous_executor::wait() {} diff --git a/libs/async/source/threadpool.cpp b/libs/async/source/threadpool.cpp index 81f8d8eb..f163b935 100644 --- a/libs/async/source/threadpool.cpp +++ b/libs/async/source/threadpool.cpp @@ -85,6 +85,11 @@ namespace psemek::async threads_.clear(); } + void threadpool::clear() + { + task_queue_.clear(); + } + void threadpool::wait() { std::unique_lock lock{working_count_mutex_}; From 64ffe27c2ad3215e4f6e67777b1a1126f88a848b Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 19 Oct 2025 02:40:35 +0300 Subject: [PATCH 102/141] Fix abs(fixed_point), lmao --- libs/util/include/psemek/util/fixed_point.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libs/util/include/psemek/util/fixed_point.hpp b/libs/util/include/psemek/util/fixed_point.hpp index 116e480a..472ec7d7 100644 --- a/libs/util/include/psemek/util/fixed_point.hpp +++ b/libs/util/include/psemek/util/fixed_point.hpp @@ -337,9 +337,8 @@ namespace psemek::util constexpr fixed_point abs(fixed_point x) { using FP = fixed_point; - using rep_type = typename FP::rep_type; if constexpr (S) - return FP::from_rep(x.rep() & ~(static_cast(1) << (I + F - 1))); + return FP::from_rep(std::abs(x.rep())); else return x; } From e23455356dfda74da8ff2b1324d24e1f18bbd01e Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 23 Oct 2025 18:40:30 +0300 Subject: [PATCH 103/141] Support float32 output in SDL2 audio backend (with int16 fallback) --- libs/sdl2/source/audio_engine.cpp | 49 +++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/libs/sdl2/source/audio_engine.cpp b/libs/sdl2/source/audio_engine.cpp index 6cbef475..4cd0e720 100644 --- a/libs/sdl2/source/audio_engine.cpp +++ b/libs/sdl2/source/audio_engine.cpp @@ -8,10 +8,7 @@ #include -#include -#include #include -#include #include #include @@ -39,6 +36,7 @@ namespace psemek::sdl2 audio::channel_ptr output_; + template static void callback(void * userdata, std::uint8_t * stream, int len); }; @@ -48,14 +46,23 @@ namespace psemek::sdl2 SDL_AudioSpec desired, obtained; desired.freq = audio::frequency; desired.channels = 2; - desired.format = AUDIO_S16SYS; + desired.format = AUDIO_F32SYS; desired.samples = 256; - desired.callback = &callback; + desired.callback = &callback; desired.userdata = this; if (device_ = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, 0); device_ == 0) - fail("SDL_OpenAudioDevice failed:"); + { + log::error() << "SDL_OpenAudioDevice failed: " << SDL_GetError(); + log::error() << "Fallback to int16 audio output"; + desired.format = AUDIO_S16SYS; + desired.callback = &callback; + if (device_ = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, 0); device_ == 0) + { + fail("SDL_OpenAudioDevice failed:"); + } + } - log::info() << "Initialized audio: " << static_cast(obtained.channels) << " channels, " << obtained.freq << " Hz, " << obtained.samples << " samples"; + log::info() << "Initialized audio: " << static_cast(obtained.channels) << " channels, " << obtained.freq << " Hz, " << obtained.samples << " samples " << (desired.format == AUDIO_F32SYS ? "f32" : "i16"); buffer_.resize(obtained.samples * obtained.channels); output_ = std::make_shared(); @@ -67,6 +74,7 @@ namespace psemek::sdl2 SDL_CloseAudioDevice(device_); } + template void audio_engine_impl::callback(void * userdata, std::uint8_t * dst_u8, int len) { static std::string const profiler_str = "audio"; @@ -74,7 +82,7 @@ namespace psemek::sdl2 auto self = static_cast(userdata); auto stream = self->output()->stream(); - std::int16_t * dst = reinterpret_cast(dst_u8); + SampleType * dst = reinterpret_cast(dst_u8); if (!self->thread_registered_) { @@ -82,14 +90,25 @@ namespace psemek::sdl2 self->thread_registered_ = true; } - std::size_t const size = len / 2; - std::size_t read = 0; - if (stream) - read = stream->read({self->buffer_.data(), size}); - std::fill(self->buffer_.data() + read, self->buffer_.data() + size, 0.f); + std::size_t const size = len / sizeof(SampleType); - for (auto s : self->buffer_) - *dst++ = static_cast(std::max(std::min((65535.f * s - 1.f) / 2.f, 32767.f), -32768.f)); + if constexpr (std::is_same_v) + { + std::size_t read = 0; + if (stream) + read = stream->read({self->buffer_.data(), size}); + std::fill(self->buffer_.data() + read, self->buffer_.data() + size, 0.f); + + for (auto s : self->buffer_) + *dst++ = static_cast(std::max(std::min((65535.f * s - 1.f) / 2.f, 32767.f), -32768.f)); + } + else + { + std::size_t read = 0; + if (stream) + read = stream->read({dst, size}); + std::fill(dst + read, dst + size, 0.f); + } if (auto duration = prof.duration(); duration > (size * audio::inv_frequency / 2)) log::warning() << "Audio can't keep up, callback took " << std::setprecision(5) << (1000.0 * duration) << " ms"; From b99a4da18fabbe48962e0a5d789a0fddc467e40a Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sat, 25 Oct 2025 13:44:48 +0300 Subject: [PATCH 104/141] Rename util::array -> util::ndarray --- examples/cloud.cpp | 8 +- examples/fibonacci_music_box.cpp | 4 +- examples/fire.cpp | 2 +- examples/particle_life_2d.cpp | 8 +- examples/physics.cpp | 2 +- examples/srtm.cpp | 4 +- examples/water_2d.cpp | 18 ++-- examples/water_2d_hex.cpp | 14 +-- libs/fonts/source/freetype.cpp | 2 +- libs/gfx/include/psemek/gfx/pixmap.hpp | 8 +- libs/gfx/include/psemek/gfx/texture.hpp | 10 +-- libs/gfx/include/psemek/gfx/texture_atlas.hpp | 6 +- libs/gfx/source/renderer/deferred.cpp | 2 +- libs/pcg/include/psemek/pcg/chunked_map.hpp | 6 +- libs/pcg/include/psemek/pcg/lazy_perlin.hpp | 10 +-- libs/pcg/include/psemek/pcg/perlin.hpp | 10 +-- libs/pcg/include/psemek/pcg/sample.hpp | 4 +- .../include/psemek/phys/shallow_water.hpp | 12 +-- libs/sir/include/psemek/sir/array.hpp | 10 +-- .../include/psemek/ui/grid_layout.hpp | 4 +- libs/ui_legacy/source/grid_layout.cpp | 4 +- libs/util/include/psemek/util/atlas.hpp | 8 +- .../psemek/util/{array.hpp => ndarray.hpp} | 86 +++++++++---------- .../include/psemek/util/spatial_array.hpp | 6 +- libs/util/tests/{array.cpp => ndarray.cpp} | 44 +++++----- tools/noise-generator/generator.cpp | 22 ++--- 26 files changed, 157 insertions(+), 157 deletions(-) rename libs/util/include/psemek/util/{array.hpp => ndarray.hpp} (81%) rename libs/util/tests/{array.cpp => ndarray.cpp} (72%) diff --git a/examples/cloud.cpp b/examples/cloud.cpp index 274d5b3c..adec7a4f 100644 --- a/examples/cloud.cpp +++ b/examples/cloud.cpp @@ -223,7 +223,7 @@ struct cloud_app random::generator rng(random::device{}); random::uniform_sphere_vector_distribution d; - std::vector, 3>> grad(4); + std::vector, 3>> grad(4); std::vector weights(grad.size()); float weight_sum = 0.f; @@ -247,7 +247,7 @@ struct cloud_app // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89062 math::gradient g(std::make_pair(0.2f, 0.f), math::easing_type::quadratic_out, std::pair{0.3f, max_density}); - util::array cloud_data({cloud_size[0], cloud_size[1], cloud_size[2]}); + util::ndarray cloud_data({cloud_size[0], cloud_size[1], cloud_size[2]}); for (std::size_t z = 0; z < cloud_data.depth(); ++z) { @@ -328,7 +328,7 @@ struct cloud_app return math::lerp(v0, v1, t[2]) / 255.f * max_density; }; - util::array, 3> cloud_shadow_f(cloud_data.dims()); + util::ndarray, 3> cloud_shadow_f(cloud_data.dims()); dirs.resize(32); @@ -448,7 +448,7 @@ struct cloud_app } } - util::array, 3> cloud_shadow(cloud_data.dims()); + util::ndarray, 3> cloud_shadow(cloud_data.dims()); for (auto const & idx : cloud_shadow.indices()) { diff --git a/examples/fibonacci_music_box.cpp b/examples/fibonacci_music_box.cpp index 176bff5a..bb9f638e 100644 --- a/examples/fibonacci_music_box.cpp +++ b/examples/fibonacci_music_box.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include @@ -32,7 +32,7 @@ using namespace psemek; std::vector>> fibonacci_cycles(int n) { - util::array cycle({n, n}, -1); + util::ndarray cycle({n, n}, -1); std::vector>> result; diff --git a/examples/fire.cpp b/examples/fire.cpp index db1b350b..62a2da16 100644 --- a/examples/fire.cpp +++ b/examples/fire.cpp @@ -172,7 +172,7 @@ candle_renderer::candle_renderer() random::generator rng; random::uniform_sphere_vector_distribution d; - util::array, 2> grad({16, 16}); + util::ndarray, 2> grad({16, 16}); for (auto & v : grad) v = d(rng); pcg::perlin perlinx(grad, pcg::seamless); diff --git a/examples/particle_life_2d.cpp b/examples/particle_life_2d.cpp index 2b27fa02..85f74e4a 100644 --- a/examples/particle_life_2d.cpp +++ b/examples/particle_life_2d.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include @@ -418,9 +418,9 @@ private: int types_; std::vector colors_; - util::array force_constants_; - util::array force_distance_; - util::array collision_distance_; + util::ndarray force_constants_; + util::ndarray force_distance_; + util::ndarray collision_distance_; std::vector max_force_distance_; std::vector particles_; diff --git a/examples/physics.cpp b/examples/physics.cpp index 6a80550d..918e90e0 100644 --- a/examples/physics.cpp +++ b/examples/physics.cpp @@ -507,7 +507,7 @@ void physics_demo_app::update() std::vector, 8>> ball_cells(model.points.size()); - util::array, 2> cells({std::ceil(view_region[0].length() / cell_size), std::ceil(view_region[1].length() / cell_size)}); + util::ndarray, 2> cells({std::ceil(view_region[0].length() / cell_size), std::ceil(view_region[1].length() / cell_size)}); float dx = view_region[0].length() / cells.width(); float dy = view_region[1].length() / cells.height(); diff --git a/examples/srtm.cpp b/examples/srtm.cpp index cec65db4..1adc1bc6 100644 --- a/examples/srtm.cpp +++ b/examples/srtm.cpp @@ -648,7 +648,7 @@ srtm_app::srtm_app(options const &, context const & context) selected_mesh.setup>(); { - util::array colors({16}); + util::ndarray colors({16}); auto * c = colors.data(); @@ -674,7 +674,7 @@ srtm_app::srtm_app(options const &, context const & context) } { - util::array colors({5}); + util::ndarray colors({5}); colors(0) = {0, 63, 127}; colors(1) = {0, 0, 127}; diff --git a/examples/water_2d.cpp b/examples/water_2d.cpp index 9ace87fb..f9081d00 100644 --- a/examples/water_2d.cpp +++ b/examples/water_2d.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include #include @@ -36,13 +36,13 @@ struct water_2d_app float time = 0.f; - util::array bed; - util::array water; - util::array flowx; - util::array flowy; - util::array, 2> velocity; - util::array sediment; - util::array new_sediment; + util::ndarray bed; + util::ndarray water; + util::ndarray flowx; + util::ndarray flowy; + util::ndarray, 2> velocity; + util::ndarray sediment; + util::ndarray new_sediment; gfx::painter painter; @@ -83,7 +83,7 @@ water_2d_app::water_2d_app(options const &, context const & ctx) // water(N / 2, N / 2) = 100.f; random::uniform_sphere_vector_distribution d; - util::array, 2> grads({9, 9}); + util::ndarray, 2> grads({9, 9}); for (auto & v : grads) v = d(rng); diff --git a/examples/water_2d_hex.cpp b/examples/water_2d_hex.cpp index 80badaf5..08f43d40 100644 --- a/examples/water_2d_hex.cpp +++ b/examples/water_2d_hex.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include #include @@ -66,11 +66,11 @@ private: bool show_velocity_ = false; - util::array bed_; - util::array water_; - util::array flow_x_; // flow_x(x, y) is (x-1, y) => (x, y) - util::array flow_y_; // flow_y(x, y) is (x, y-1) => (x, y) - util::array flow_z_; // flow_z(x, y) is (x, y-1) => (x-1, y) + util::ndarray bed_; + util::ndarray water_; + util::ndarray flow_x_; // flow_x(x, y) is (x-1, y) => (x, y) + util::ndarray flow_y_; // flow_y(x, y) is (x, y-1) => (x, y) + util::ndarray flow_z_; // flow_z(x, y) is (x, y-1) => (x-1, y) }; water_2d_hex_app::water_2d_hex_app(options const &, context const & ctx) @@ -86,7 +86,7 @@ water_2d_hex_app::water_2d_hex_app(options const &, context const & ctx) random::uniform_sphere_vector_distribution d_grad; - util::array, 2> perlin_grad({17, 17}); + util::ndarray, 2> perlin_grad({17, 17}); for (auto & v : perlin_grad) v = d_grad(rng_); diff --git a/libs/fonts/source/freetype.cpp b/libs/fonts/source/freetype.cpp index 30795f96..38063bd3 100644 --- a/libs/fonts/source/freetype.cpp +++ b/libs/fonts/source/freetype.cpp @@ -145,7 +145,7 @@ namespace psemek::fonts struct page { - util::array pixmap; + util::ndarray pixmap; int current_row_start = 0; int current_row_height = 0; diff --git a/libs/gfx/include/psemek/gfx/pixmap.hpp b/libs/gfx/include/psemek/gfx/pixmap.hpp index 00566687..524d28bc 100644 --- a/libs/gfx/include/psemek/gfx/pixmap.hpp +++ b/libs/gfx/include/psemek/gfx/pixmap.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -10,7 +10,7 @@ namespace psemek::gfx { template - using basic_pixmap = util::array; + using basic_pixmap = util::ndarray; using pixmap_monochrome = basic_pixmap; using pixmap_rgb = basic_pixmap; @@ -48,7 +48,7 @@ namespace psemek::gfx // Utilities template - auto to_srgb(util::array pm, float g = 1.f / 2.2f) + auto to_srgb(util::ndarray pm, float g = 1.f / 2.2f) { for (auto & c : pm) c = to_srgb(c, g); @@ -56,7 +56,7 @@ namespace psemek::gfx } template - auto to_linear(util::array pm, float g = 1.f / 2.2f) + auto to_linear(util::ndarray pm, float g = 1.f / 2.2f) { return to_srgb(std::move(pm), 1.f / g); } diff --git a/libs/gfx/include/psemek/gfx/texture.hpp b/libs/gfx/include/psemek/gfx/texture.hpp index 88cb4f09..de20fa8f 100644 --- a/libs/gfx/include/psemek/gfx/texture.hpp +++ b/libs/gfx/include/psemek/gfx/texture.hpp @@ -54,10 +54,10 @@ namespace psemek::gfx void load_srgb(math::vector const & size, Pixel const * data = nullptr); template - void load(util::array const & p); + void load(util::ndarray const & p); template - void load_srgb(util::array const & p); + void load_srgb(util::ndarray const & p); #ifndef PSEMEK_GLES void pixels(GLenum format, GLenum type, void * data, int layer = 0) const; @@ -142,7 +142,7 @@ namespace psemek::gfx } template - void load(int f, util::array const & p) + void load(int f, util::ndarray const & p) { math::vector size; for (std::size_t i = 0; i < 2; ++i) size[i] = p.dim(i); @@ -392,7 +392,7 @@ namespace psemek::gfx template template - void basic_texture::load(util::array const & p) + void basic_texture::load(util::ndarray const & p) { math::vector size; for (std::size_t i = 0; i < D; ++i) size[i] = p.dim(i); @@ -401,7 +401,7 @@ namespace psemek::gfx template template - void basic_texture::load_srgb(util::array const & p) + void basic_texture::load_srgb(util::ndarray const & p) { math::vector size; for (std::size_t i = 0; i < D; ++i) size[i] = p.dim(i); diff --git a/libs/gfx/include/psemek/gfx/texture_atlas.hpp b/libs/gfx/include/psemek/gfx/texture_atlas.hpp index b46d81b4..0060ba42 100644 --- a/libs/gfx/include/psemek/gfx/texture_atlas.hpp +++ b/libs/gfx/include/psemek/gfx/texture_atlas.hpp @@ -19,9 +19,9 @@ namespace psemek::gfx texture_2d & texture() { return texture_; } texture_2d const & texture() const { return texture_; } - util::array const & pixmap() const { return atlas_.array(); } + util::ndarray const & pixmap() const { return atlas_.array(); } - std::pair insert(Key const & key, util::array const & data); + std::pair insert(Key const & key, util::ndarray const & data); texture_view find(Key const & key) const; texture_view at(Key const & key) const; @@ -43,7 +43,7 @@ namespace psemek::gfx {} template - std::pair texture_atlas_2d::insert(Key const & key, util::array const & data) + std::pair texture_atlas_2d::insert(Key const & key, util::ndarray const & data) { auto result = atlas_.insert(key, data); if (result.second) diff --git a/libs/gfx/source/renderer/deferred.cpp b/libs/gfx/source/renderer/deferred.cpp index 03d6be49..f96a9810 100644 --- a/libs/gfx/source/renderer/deferred.cpp +++ b/libs/gfx/source/renderer/deferred.cpp @@ -1069,7 +1069,7 @@ void main(){} camera_distance[i] = dist; } - util::array bins({opts.grid_size[0], opts.grid_size[1], opts.grid_size[2]}); + util::ndarray bins({opts.grid_size[0], opts.grid_size[1], opts.grid_size[2]}); math::box lit_bbox; math::box casts_shadow_bbox; diff --git a/libs/pcg/include/psemek/pcg/chunked_map.hpp b/libs/pcg/include/psemek/pcg/chunked_map.hpp index a20a6e16..96ef5c17 100644 --- a/libs/pcg/include/psemek/pcg/chunked_map.hpp +++ b/libs/pcg/include/psemek/pcg/chunked_map.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include @@ -13,7 +13,7 @@ namespace psemek::pcg template struct chunked_map { - using generator_func = util::function(math::box const &)>; + using generator_func = util::function(math::box const &)>; chunked_map(std::size_t chunk_size, generator_func generator) : chunk_size_(chunk_size) @@ -68,7 +68,7 @@ namespace psemek::pcg private: int const chunk_size_; generator_func generator_; - mutable util::hash_map, util::array> chunks_; + mutable util::hash_map, util::ndarray> chunks_; }; diff --git a/libs/pcg/include/psemek/pcg/lazy_perlin.hpp b/libs/pcg/include/psemek/pcg/lazy_perlin.hpp index ec97b83a..9abbcbea 100644 --- a/libs/pcg/include/psemek/pcg/lazy_perlin.hpp +++ b/libs/pcg/include/psemek/pcg/lazy_perlin.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include @@ -68,7 +68,7 @@ namespace psemek::pcg lazy_perlin_view() = default; - lazy_perlin_view(std::size_t grid_size, math::vector const & view_origin, util::array, N> subgrid) + lazy_perlin_view(std::size_t grid_size, math::vector const & view_origin, util::ndarray, N> subgrid) : grid_size_(grid_size) , origin_(view_origin) , subgrid_(std::move(subgrid)) @@ -79,7 +79,7 @@ namespace psemek::pcg private: int grid_size_; math::vector origin_; - util::array, N> subgrid_; + util::ndarray, N> subgrid_; }; template @@ -143,7 +143,7 @@ namespace psemek::pcg int const grid_size_; math::vector origin_; math::vector corner_; - mutable util::array>, N> grid_; + mutable util::ndarray>, N> grid_; generator_func gen_; math::vector grid_at(math::vector const & c) const @@ -191,7 +191,7 @@ namespace psemek::pcg subgrid_dimensions[i] = subgrid_max - subgrid_origin[i] + 1; } - util::array, N> subgrid(subgrid_dimensions); + util::ndarray, N> subgrid(subgrid_dimensions); for (auto const idx : subgrid.indices()) { math::vector id; diff --git a/libs/pcg/include/psemek/pcg/perlin.hpp b/libs/pcg/include/psemek/pcg/perlin.hpp index 374b6bd5..9414b8d5 100644 --- a/libs/pcg/include/psemek/pcg/perlin.hpp +++ b/libs/pcg/include/psemek/pcg/perlin.hpp @@ -16,8 +16,8 @@ namespace psemek::pcg static constexpr std::size_t dimension = N; perlin() = default; - perlin(util::array, N> grad_map); - perlin(util::array, N> const & grad_map, seamless_tag); + perlin(util::ndarray, N> grad_map); + perlin(util::ndarray, N> const & grad_map, seamless_tag); perlin(perlin &&) = default; perlin & operator = (perlin &&) = default; @@ -51,16 +51,16 @@ namespace psemek::pcg auto const & grad() const { return grad_map_; } private: - util::array, N> grad_map_; + util::ndarray, N> grad_map_; }; template - perlin::perlin(util::array, N> grad_map) + perlin::perlin(util::ndarray, N> grad_map) : grad_map_{std::move(grad_map)} {} template - perlin::perlin(util::array, N> const & grad_map, seamless_tag) + perlin::perlin(util::ndarray, N> const & grad_map, seamless_tag) { auto dims = grad_map.dims(); for (std::size_t i = 0; i < N; ++i) diff --git a/libs/pcg/include/psemek/pcg/sample.hpp b/libs/pcg/include/psemek/pcg/sample.hpp index 659db515..95f27750 100644 --- a/libs/pcg/include/psemek/pcg/sample.hpp +++ b/libs/pcg/include/psemek/pcg/sample.hpp @@ -21,7 +21,7 @@ namespace psemek::pcg } template - void sample(Gen && gen, util::array & result) + void sample(Gen && gen, util::ndarray & result) { for (std::size_t k = 0; k < result.height(); ++k) { @@ -55,7 +55,7 @@ namespace psemek::pcg { using R = decltype(gen(0.f, 0.f, 0.f)); - util::array result({width, height, depth}); + util::ndarray result({width, height, depth}); sample(std::forward(gen), result); return result; diff --git a/libs/phys/include/psemek/phys/shallow_water.hpp b/libs/phys/include/psemek/phys/shallow_water.hpp index ff457891..a7b1d6ea 100644 --- a/libs/phys/include/psemek/phys/shallow_water.hpp +++ b/libs/phys/include/psemek/phys/shallow_water.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -8,16 +8,16 @@ namespace psemek::phys { template - std::pair shallow_water_steady_state_single_step(util::array const & depth, util::array & water_height, - util::array & x_velocity, util::array & y_velocity, + std::pair shallow_water_steady_state_single_step(util::ndarray const & depth, util::ndarray & water_height, + util::ndarray & x_velocity, util::ndarray & y_velocity, T gravity, std::size_t max_iterations, T max_error) { std::size_t const n = depth.width() - 2; std::size_t const m = depth.height() - 2; - util::array delta_height({n + 2, m + 2}, 0.f); - util::array delta_x_velocity({n + 1, m + 2}, 0.f); - util::array delta_y_velocity({n + 2, m + 1}, 0.f); + util::ndarray delta_height({n + 2, m + 2}, 0.f); + util::ndarray delta_x_velocity({n + 1, m + 2}, 0.f); + util::ndarray delta_y_velocity({n + 2, m + 1}, 0.f); auto h_dual = [&](auto i, auto j) { return math::dual{water_height(i, j), {delta_height(i, j), 0.f}}; }; auto ux_dual = [&](auto i, auto j) { return math::dual{x_velocity(i, j + 1), {delta_x_velocity(i, j + 1), 0.f}}; }; diff --git a/libs/sir/include/psemek/sir/array.hpp b/libs/sir/include/psemek/sir/array.hpp index 42c7a797..4a49e409 100644 --- a/libs/sir/include/psemek/sir/array.hpp +++ b/libs/sir/include/psemek/sir/array.hpp @@ -2,14 +2,14 @@ #include #include -#include +#include namespace psemek::sir { template - requires (is_ostream_v && !is_custom_v>) - void write(Stream & s, util::array const & x) + requires (is_ostream_v && !is_custom_v>) + void write(Stream & s, util::ndarray const & x) { write(s, x.dims()); if constexpr (is_trivial_v) @@ -23,8 +23,8 @@ namespace psemek::sir } template - requires (is_istream_v && !is_custom_v>) - void read(Stream & s, util::array & x) + requires (is_istream_v && !is_custom_v>) + void read(Stream & s, util::ndarray & x) { std::array dims; read(s, dims); diff --git a/libs/ui_legacy/include/psemek/ui/grid_layout.hpp b/libs/ui_legacy/include/psemek/ui/grid_layout.hpp index f7b7c4b7..fc7233e0 100644 --- a/libs/ui_legacy/include/psemek/ui/grid_layout.hpp +++ b/libs/ui_legacy/include/psemek/ui/grid_layout.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include namespace psemek::ui { @@ -60,7 +60,7 @@ namespace psemek::ui std::vector> column_shape_; private: - util::array, 2> children_; + util::ndarray, 2> children_; std::vector children_range_; std::vector row_weight_; diff --git a/libs/ui_legacy/source/grid_layout.cpp b/libs/ui_legacy/source/grid_layout.cpp index b35959c7..fb325530 100644 --- a/libs/ui_legacy/source/grid_layout.cpp +++ b/libs/ui_legacy/source/grid_layout.cpp @@ -159,7 +159,7 @@ namespace psemek::ui } template - static std::vector> cell_sizes(util::array, 2> const & children) + static std::vector> cell_sizes(util::ndarray, 2> const & children) { std::vector> result(children.dim(Dimension), {0.f, inf}); @@ -175,7 +175,7 @@ namespace psemek::ui } template - static std::vector> cell_sizes_constrained(util::array, 2> const & children, std::vector> const & other_dimension_shapes) + static std::vector> cell_sizes_constrained(util::ndarray, 2> const & children, std::vector> const & other_dimension_shapes) { std::vector> result(children.dim(Dimension), {0.f, inf}); diff --git a/libs/util/include/psemek/util/atlas.hpp b/libs/util/include/psemek/util/atlas.hpp index 5c9c0076..c5fe9765 100644 --- a/libs/util/include/psemek/util/atlas.hpp +++ b/libs/util/include/psemek/util/atlas.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -33,12 +33,12 @@ namespace psemek::util , data_(std::move(compare)) {} - util::array const & array() const + util::ndarray const & array() const { return array_; } - std::pair insert(Key const & key, util::array const & data) + std::pair insert(Key const & key, util::ndarray const & data) { auto it = data_.find(key); if (it != data_.end()) @@ -126,7 +126,7 @@ namespace psemek::util std::size_t const padding_; padding_mode const padding_mode_; std::map data_; - util::array array_; + util::ndarray array_; std::size_t free_start_ = 0; }; diff --git a/libs/util/include/psemek/util/array.hpp b/libs/util/include/psemek/util/ndarray.hpp similarity index 81% rename from libs/util/include/psemek/util/array.hpp rename to libs/util/include/psemek/util/ndarray.hpp index 2a804506..6e629d48 100644 --- a/libs/util/include/psemek/util/array.hpp +++ b/libs/util/include/psemek/util/ndarray.hpp @@ -43,7 +43,7 @@ namespace psemek::util } template - struct array + struct ndarray { static_assert(N >= 1); @@ -52,17 +52,17 @@ namespace psemek::util using dims_type = std::array; - array(); - array(dims_type const & dims); - array(dims_type const & dims, T const & value); - array(dims_type const & dims, std::unique_ptr data); - array(array &&); + ndarray(); + ndarray(dims_type const & dims); + ndarray(dims_type const & dims, T const & value); + ndarray(dims_type const & dims, std::unique_ptr data); + ndarray(ndarray &&); - array(array const &) = delete; + ndarray(ndarray const &) = delete; - array & operator = (array &&); + ndarray & operator = (ndarray &&); - array & operator = (array const &) = delete; + ndarray & operator = (ndarray const &) = delete; dims_type const & dims() const { @@ -117,8 +117,8 @@ namespace psemek::util template T const & operator()(std::initializer_list const & index) const; - array copy() const; - array subarray(dims_type const & start, dims_type const & end) const; + ndarray copy() const; + ndarray subarray(dims_type const & start, dims_type const & end) const; void resize(dims_type const & dims); @@ -255,52 +255,52 @@ namespace psemek::util } template - auto array::index_begin() const + auto ndarray::index_begin() const { return indices().begin(); } template - auto array::index_end() const + auto ndarray::index_end() const { return indices().end(); } template - auto array::indices() const + auto ndarray::indices() const { return detail::array_index_range{dims_}; } template - array::array() + ndarray::ndarray() { dims_.fill(0); } template - array::array(dims_type const & dims) + ndarray::ndarray(dims_type const & dims) : dims_{dims} { data_.reset(new T[size()]); } template - array::array(dims_type const & dims, T const & value) - : array(dims) + ndarray::ndarray(dims_type const & dims, T const & value) + : ndarray(dims) { fill(value); } template - array::array(dims_type const & dims, std::unique_ptr data) + ndarray::ndarray(dims_type const & dims, std::unique_ptr data) : dims_{dims} { data_ = std::move(data); } template - array::array(array && other) + ndarray::ndarray(ndarray && other) : data_{std::move(other.data_)} , dims_{other.dims_} { @@ -308,7 +308,7 @@ namespace psemek::util } template - array & array::operator = (array && other) + ndarray & ndarray::operator = (ndarray && other) { if (this == &other) return *this; @@ -321,7 +321,7 @@ namespace psemek::util } template - std::size_t array::size() const + std::size_t ndarray::size() const { return detail::product(dims_); } @@ -329,7 +329,7 @@ namespace psemek::util template template std::enable_if_t, T &> - array::operator()(Index const & index) + ndarray::operator()(Index const & index) { return data_[detail::index(index, dims_)]; } @@ -337,7 +337,7 @@ namespace psemek::util template template std::enable_if_t, T const &> - array::operator()(Index const & index) const + ndarray::operator()(Index const & index) const { return data_[detail::index(index, dims_)]; } @@ -345,7 +345,7 @@ namespace psemek::util template template std::enable_if_t, T &> - array::operator()(Ixs ... ixs) + ndarray::operator()(Ixs ... ixs) { static_assert(sizeof...(Ixs) == N); dims_type dims{static_cast(ixs)...}; @@ -355,7 +355,7 @@ namespace psemek::util template template std::enable_if_t, T const &> - array::operator()(Ixs ... ixs) const + ndarray::operator()(Ixs ... ixs) const { static_assert(sizeof...(Ixs) == N); dims_type dims{static_cast(ixs)...}; @@ -364,34 +364,34 @@ namespace psemek::util template template - T & array::operator()(std::initializer_list const & index) + T & ndarray::operator()(std::initializer_list const & index) { return data_[detail::index(std::data(index), dims_)]; } template template - T const & array::operator()(std::initializer_list const & index) const + T const & ndarray::operator()(std::initializer_list const & index) const { return data_[detail::index(std::data(index), dims_)]; } template - array array::copy() const + ndarray ndarray::copy() const { std::unique_ptr data(new T[size()]); std::copy(begin(), end(), data.get()); - return array{dims_, std::move(data)}; + return ndarray{dims_, std::move(data)}; } template - array array::subarray(dims_type const & start, dims_type const & end) const + ndarray ndarray::subarray(dims_type const & start, dims_type const & end) const { dims_type size; for (std::size_t i = 0; i < N; ++i) size[i] = end[i] - start[i]; - array result(size); + ndarray result(size); for (auto const & idx : result.indices()) { auto jdx = idx; @@ -404,7 +404,7 @@ namespace psemek::util } template - void array::resize(dims_type const & dims) + void ndarray::resize(dims_type const & dims) { if (dims == dims_) return; @@ -414,7 +414,7 @@ namespace psemek::util } template - void array::resize(dims_type const & dims, T const & value) + void ndarray::resize(dims_type const & dims, T const & value) { if (dims == dims_) return; @@ -426,7 +426,7 @@ namespace psemek::util } template - void array::assign(dims_type const & dims, T const & value) + void ndarray::assign(dims_type const & dims, T const & value) { auto const size = detail::product(dims); std::unique_ptr data(new T[size]); @@ -436,33 +436,33 @@ namespace psemek::util } template - std::unique_ptr array::release() + std::unique_ptr ndarray::release() { dims_.fill(0); return std::move(data_); } template - bool array::empty() const + bool ndarray::empty() const { return detail::empty(dims_); } template - void array::clear() + void ndarray::clear() { data_.reset(); dims_.fill(0); } template - void array::fill(T const & value) + void ndarray::fill(T const & value) { std::fill(data_.get(), data_.get() + size(), value); } template - void array::resize_impl(std::unique_ptr data, dims_type const & dims) + void ndarray::resize_impl(std::unique_ptr data, dims_type const & dims) { std::array min_dim; for (std::size_t d = 0; d < N; ++d) @@ -483,7 +483,7 @@ namespace psemek::util } template - void mirror(array & a, std::size_t i) + void mirror(ndarray & a, std::size_t i) { assert(i < N); @@ -498,11 +498,11 @@ namespace psemek::util } template - auto map(F && f, array const & a) + auto map(F && f, ndarray const & a) { using R = std::decay_t; - array r(a.dims()); + ndarray r(a.dims()); auto begin = a.begin(); auto end = a.end(); diff --git a/libs/util/include/psemek/util/spatial_array.hpp b/libs/util/include/psemek/util/spatial_array.hpp index 671aa19a..d97ac953 100644 --- a/libs/util/include/psemek/util/spatial_array.hpp +++ b/libs/util/include/psemek/util/spatial_array.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -49,7 +49,7 @@ namespace psemek::util private: Index origin_[N]; - array array_; + ndarray array_; }; template @@ -176,7 +176,7 @@ namespace psemek::util } } - array new_array(new_size); + ndarray new_array(new_size); for (auto & v : new_array) v = T(); diff --git a/libs/util/tests/array.cpp b/libs/util/tests/ndarray.cpp similarity index 72% rename from libs/util/tests/array.cpp rename to libs/util/tests/ndarray.cpp index 2f93addb..6fd2313b 100644 --- a/libs/util/tests/array.cpp +++ b/libs/util/tests/ndarray.cpp @@ -1,38 +1,38 @@ #include -#include +#include using namespace psemek::util; -test_case(util_array_empty) +test_case(util_ndarray_empty) { - array a; + ndarray a; expect_equal(a.size(), 0); expect(a.empty()); - array b; + ndarray b; expect_equal(b.size(), 0); expect(b.empty()); - array c; + ndarray c; expect_equal(c.size(), 0); expect(c.empty()); } -test_case(util_array_size) +test_case(util_ndarray_size) { - array a({16}); + ndarray a({16}); expect(!a.empty()); expect_equal(a.size(), 16); expect_equal(a.width(), 16); - array b({8, 16}); + ndarray b({8, 16}); expect(!b.empty()); expect_equal(b.size(), 8 * 16); expect_equal(b.width(), 8); expect_equal(b.height(), 16); - array c({4, 8, 16}); + ndarray c({4, 8, 16}); expect(!c.empty()); expect_equal(c.size(), 4 * 8 * 16); expect_equal(c.width(), 4); @@ -40,20 +40,20 @@ test_case(util_array_size) expect_equal(c.depth(), 16); } -test_case(util_array_init) +test_case(util_ndarray_init) { - array a({16, 16}, 42); + ndarray a({16, 16}, 42); for (std::size_t i = 0; i < a.width(); ++i) for (std::size_t j = 0; j < a.height(); ++j) expect_equal(a(i, j), 42); } -test_case(util_array_release) +test_case(util_ndarray_release) { std::unique_ptr p(new int[256]); auto q = p.get(); - array a({16, 16}, std::move(p)); + ndarray a({16, 16}, std::move(p)); expect_equal(a.size(), 256); expect_equal(a.width(), 16); expect_equal(a.height(), 16); @@ -66,20 +66,20 @@ test_case(util_array_release) expect_equal_ptr(p.get(), q); } -test_case(util_array_move) +test_case(util_ndarray_move) { - array a({16, 16}, 42); + ndarray a({16, 16}, 42); auto p = a.data(); - array b(std::move(a)); + ndarray b(std::move(a)); expect(a.empty()); expect_equal(a.size(), 0); expect(!b.empty()); expect_equal(b.size(), 256); expect_equal_ptr(b.data(), p); - array c; + ndarray c; c = std::move(b); expect(b.empty()); expect_equal(b.size(), 0); @@ -88,11 +88,11 @@ test_case(util_array_move) expect_equal_ptr(c.data(), p); } -test_case(util_array_copy) +test_case(util_ndarray_copy) { - array a({16, 16}, 42); + ndarray a({16, 16}, 42); - array b(a.copy()); + ndarray b(a.copy()); expect(!a.empty()); expect(!b.empty()); expect_equal(a.size(), b.size()); @@ -100,12 +100,12 @@ test_case(util_array_copy) expect_different_ptr(a.data(), b.data()); } -test_case(util_array_lifetime) +test_case(util_ndarray_lifetime) { std::weak_ptr p; { - array, 2> a({16, 16}, std::make_shared(42)); + ndarray, 2> a({16, 16}, std::make_shared(42)); p = a(0, 0); for (std::size_t i = 0; i < a.width(); ++i) { diff --git a/tools/noise-generator/generator.cpp b/tools/noise-generator/generator.cpp index 9845a371..667af1c4 100644 --- a/tools/noise-generator/generator.cpp +++ b/tools/noise-generator/generator.cpp @@ -33,7 +33,7 @@ auto moore_neighbourhood() } template -util::array generate_perlin(std::array const & size, int octaves, int minoctave, float power, +util::ndarray generate_perlin(std::array const & size, int octaves, int minoctave, float power, bool invert, bool tile, float gamma, bool remap, std::uint64_t seed) { std::cout << "Generating " << D << "D perlin noise\n"; @@ -68,7 +68,7 @@ util::array generate_perlin(std::array const & for (int d = 0; d < D; ++d) octave_size[d] = (1 << o) + (tile ? 0 : 1); - util::array, D> gradients(octave_size); + util::ndarray, D> gradients(octave_size); for (auto & g : gradients) g = random_gradient(rng); @@ -91,7 +91,7 @@ util::array generate_perlin(std::array const & for (int d = 0; d < D; ++d) largest_size = std::max(largest_size, size[d]); - util::array result_float(size); + util::ndarray result_float(size); math::interval value_range; for (auto idx : result_float.indices()) @@ -110,7 +110,7 @@ util::array generate_perlin(std::array const & value_range |= v; } - util::array result(size); + util::ndarray result(size); for (auto idx : result.indices()) { @@ -127,7 +127,7 @@ util::array generate_perlin(std::array const & } template -util::array generate_worley(std::array const & size, int octaves, int minoctave, float power, +util::ndarray generate_worley(std::array const & size, int octaves, int minoctave, float power, bool invert, bool tile, float gamma, bool remap, std::uint64_t seed) { std::cout << "Generating " << D << "D worley noise\n"; @@ -150,7 +150,7 @@ util::array generate_worley(std::array const & random::generator rng{seed, 0}; - std::vector, D>> octave_points; + std::vector, D>> octave_points; std::vector octave_weights; float sum_octave_weights = 0.f; @@ -160,7 +160,7 @@ util::array generate_worley(std::array const & for (int d = 0; d < D; ++d) octave_size[d] = (1 << o); - util::array, D> octave(octave_size); + util::ndarray, D> octave(octave_size); for (auto idx : octave.indices()) { @@ -179,7 +179,7 @@ util::array generate_worley(std::array const & for (auto & w : octave_weights) w /= sum_octave_weights; - util::array result_float(size); + util::ndarray result_float(size); math::interval value_range; for (auto idx : result_float.indices()) @@ -249,7 +249,7 @@ util::array generate_worley(std::array const & value_range |= v; } - util::array result(size); + util::ndarray result(size); for (auto idx : result.indices()) { @@ -507,7 +507,7 @@ int main(int argc, char * argv[]) if (*dim == 2) { - util::array result; + util::ndarray result; if (type == "perlin") result = generate_perlin<2>({*sizex, *sizey}, *octaves, *minoctave, *power, *invert, *tile, *gamma, *remap, *seed); else if (type == "worley") @@ -539,7 +539,7 @@ int main(int argc, char * argv[]) if (*dim == 3) { - util::array result; + util::ndarray result; if (type == "perlin") generate_perlin<3>({*sizex, *sizey, *sizez}, *octaves, *minoctave, *power, *invert, *tile, *gamma, *remap, *seed); else if (type == "worley") From e40467a8d19854f6ec1672bb748b721b4f6b50b9 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sat, 25 Oct 2025 15:20:42 +0300 Subject: [PATCH 105/141] Add util::array - a dynamic array without auto-expansion --- libs/util/include/psemek/util/array.hpp | 279 ++++++++++++++++++++++++ libs/util/tests/array.cpp | 165 ++++++++++++++ 2 files changed, 444 insertions(+) create mode 100644 libs/util/include/psemek/util/array.hpp create mode 100644 libs/util/tests/array.cpp diff --git a/libs/util/include/psemek/util/array.hpp b/libs/util/include/psemek/util/array.hpp new file mode 100644 index 00000000..46897985 --- /dev/null +++ b/libs/util/include/psemek/util/array.hpp @@ -0,0 +1,279 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +namespace psemek::util +{ + + struct array_index_out_of_bounds + : util::exception + { + array_index_out_of_bounds(std::size_t index, std::size_t size, util::stacktrace stacktrace = {}) + : util::exception(std::format("Array index ({}) out of bounds ({})", index, size), std::move(stacktrace)) + {} + }; + + // Like std::vector, but without auto-reallocation and implicit copying + template + struct array + { + using value_type = T; + using reference_type = T &; + using pointer_type = T *; + using iterator = T *; + using const_iterator = T const *; + using size_type = std::size_t; + + array() = default; + + explicit array(std::size_t size); + array(std::size_t size, T const & value); + + template + array(Iterator begin, Iterator end); + + array(std::initializer_list const & list); + + array(array const & other) = delete; + array(array && other); + + array & operator = (array const & other) = delete; + array & operator = (array && other); + + std::size_t size() const; + bool empty(); + + T * data(); + T const * data() const; + + void resize(std::size_t size); + void resize(std::size_t size, T const & value); + void clear(); + + void fill(T const & value); + + T & operator[] (std::size_t index); + T const & operator[] (std::size_t index) const; + + T & at(std::size_t index); + T const & at(std::size_t index) const; + + T * begin(); + T * end(); + + T const * begin() const; + T const * end() const; + + T const * cbegin() const; + T const * cend() const; + + array copy() const; + + private: + std::unique_ptr data_ = nullptr; + std::size_t size_ = 0; + + static std::unique_ptr allocate(std::size_t size); + void check_index(std::size_t index); + }; + + template + array::array(std::size_t size) + : data_(allocate(size)) + , size_(size) + {} + + template + array::array(std::size_t size, T const & value) + : data_(allocate(size)) + , size_(size) + { + fill(value); + } + + template + template + array::array(Iterator begin, Iterator end) + { + size_ = std::distance(begin, end); + data_ = allocate(size_); + std::copy(begin, end, data_.get()); + } + + template + array::array(std::initializer_list const & list) + : data_(allocate(list.size())) + , size_(list.size()) + { + std::copy(list.begin(), list.end(), begin()); + } + + template + array::array(array && other) + : data_(std::move(other.data_)) + , size_(other.size_) + { + other.size_ = 0; + } + + template + array & array::operator = (array && other) + { + data_ = std::move(other.data_); + size_ = other.size_; + return *this; + } + + template + std::size_t array::size() const + { + return size_; + } + + template + bool array::empty() + { + return size_ == 0; + } + + template + T * array::data() + { + return data_.get(); + } + + template + T const * array::data() const + { + return data_.get(); + } + + template + void array::resize(std::size_t size) + { + if (size == size_) return; + + auto new_data = allocate(size); + std::copy_n(data_.get(), std::min(size, size_), new_data.get()); + + data_ = std::move(new_data); + size_ = size; + } + + template + void array::resize(std::size_t size, T const & value) + { + if (size == size_) return; + + auto min_size = std::min(size, size_); + + auto new_data = allocate(size); + std::copy_n(data_.get(), min_size, new_data.get()); + std::fill_n(new_data.get() + min_size, size - min_size, value); + + data_ = std::move(new_data); + size_ = size; + } + + template + void array::clear() + { + data_.reset(); + size_ = 0; + } + + template + void array::fill(T const & value) + { + std::fill(begin(), end(), value); + } + + template + T & array::operator[] (std::size_t index) + { + return data_[index]; + } + + template + T const & array::operator[] (std::size_t index) const + { + return data_[index]; + } + + template + T & array::at(std::size_t index) + { + check_index(index); + return data_[index]; + } + + template + T const & array::at(std::size_t index) const + { + check_index(index); + return data_[index]; + } + + template + T * array::begin() + { + return data_.get(); + } + + template + T * array::end() + { + return data_.get() + size_; + } + + template + T const * array::begin() const + { + return data_.get(); + } + + template + T const * array::end() const + { + return data_.get() + size_; + } + + template + T const * array::cbegin() const + { + return begin(); + } + + template + T const * array::cend() const + { + return end(); + } + + template + array array::copy() const + { + return array(begin(), end()); + } + + template + std::unique_ptr array::allocate(std::size_t size) + { + return size == 0 ? nullptr : std::make_unique_for_overwrite(size); + } + + template + void array::check_index(std::size_t index) + { + if (index >= size_) + throw array_index_out_of_bounds(index, size_); + } + +} diff --git a/libs/util/tests/array.cpp b/libs/util/tests/array.cpp new file mode 100644 index 00000000..f4843a23 --- /dev/null +++ b/libs/util/tests/array.cpp @@ -0,0 +1,165 @@ +#include + +#include + +using namespace psemek::util; + +test_case(util_array_empty) +{ + array a; + expect(a.empty()); + expect_equal(a.size(), 0); + expect_equal_ptr(a.data(), nullptr); +} + +test_case(util_array_ctor_size) +{ + array a(16, 42); + expect(!a.empty()); + expect_equal(a.size(), 16); + expect_different_ptr(a.data(), nullptr); + + for (int i = 0; i < a.size(); ++i) + expect_equal(a[i], 42); + + for (int i = 0; i < a.size(); ++i) + a[i] = i; + + for (int i = 0; i < a.size(); ++i) + expect_equal(a[i], i); +} + +test_case(util_array_ctor_range) +{ + int data[8] = {2, 3, 5, 7, 11, 13, 17, 19}; + + array a(std::begin(data), std::end(data)); + expect(!a.empty()); + expect_equal(a.size(), std::size(data)); + expect_different_ptr(a.data(), nullptr); + + for (int i = 0; i < a.size(); ++i) + expect_equal(a[i], data[i]); +} + +test_case(util_array_ctor_initializer__list) +{ + int data[8] = {2, 3, 5, 7, 11, 13, 17, 19}; + + array a{2, 3, 5, 7, 11, 13, 17, 19}; + expect(!a.empty()); + expect_equal(a.size(), std::size(data)); + expect_different_ptr(a.data(), nullptr); + + for (int i = 0; i < a.size(); ++i) + expect_equal(a[i], data[i]); +} + +test_case(util_array_ctor_move) +{ + array a(16); + for (int i = 0; i < a.size(); ++i) + a[i] = i; + + array b = std::move(a); + + expect(a.empty()); + expect_equal(a.size(), 0); + expect_equal_ptr(a.data(), nullptr); + expect(!b.empty()); + expect_equal(b.size(), 16); + expect_different_ptr(b.data(), nullptr); + + for (int i = 0; i < b.size(); ++i) + expect_equal(b[i], i); +} + +test_case(util_array_resize) +{ + array a(16); + for (int i = 0; i < a.size(); ++i) + a[i] = i; + + a.resize(32); + for (int i = 0; i < 16; ++i) + expect_equal(a[i], i); + + a.resize(8); + for (int i = 0; i < 8; ++i) + expect_equal(a[i], i); +} + +test_case(util_array_clear) +{ + array a(16); + a.clear(); + + expect(a.empty()); + expect_equal(a.size(), 0); + expect_equal_ptr(a.data(), nullptr); +} + +test_case(util_array_fill) +{ + array a(16); + a.fill(42); + + for (int i = 0; i < a.size(); ++i) + expect_equal(a[i], 42); +} + +test_case(util_array_index) +{ + array a(16); + + for (int i = 0; i < a.size(); ++i) + expect_equal_ptr(&a[i], a.data() + i); +} + +test_case(util_array_at) +{ + array a(16); + + for (int i = 0; i < a.size(); ++i) + expect_equal_ptr(&a.at(i), a.data() + i); + + for (int i = a.size(); i < a.size() + 16; ++i) + expect_throw(a.at(i), array_index_out_of_bounds); +} + +test_case(util_array_iterator) +{ + array a(16); + for (int i = 0; i < a.size(); ++i) + a[i] = i; + + int i = 0; + for (auto v : a) + expect_equal(a[i++], v); + + i = 0; + for (auto & v : a) + v = 42 * (i++); + + for (int i = 0; i < a.size(); ++i) + expect_equal(a[i], 42 * i); + + array const b(a.begin(), a.end()); + i = 0; + for (auto v : b) + expect_equal(a[i++], v); +} + +test_case(util_array_copy) +{ + array a(16); + for (int i = 0; i < a.size(); ++i) + a[i] = i; + + auto b = a.copy(); + expect_equal(a.size(), b.size()); + expect_different_ptr(a.data(), b.data()); + + for (int i = 0; i < b.size(); ++i) + expect_equal(a[i], b[i]); +} From 17d857ecf25e4a92fc4277736ff778f9d970e95e Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sat, 25 Oct 2025 15:20:59 +0300 Subject: [PATCH 106/141] Use util::array as the storage for math types with dynamic size --- libs/math/include/psemek/math/detail/array.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libs/math/include/psemek/math/detail/array.hpp b/libs/math/include/psemek/math/detail/array.hpp index ca6e2e79..91456e95 100644 --- a/libs/math/include/psemek/math/detail/array.hpp +++ b/libs/math/include/psemek/math/detail/array.hpp @@ -2,8 +2,7 @@ #include #include - -#include +#include namespace psemek::math::detail { @@ -37,7 +36,7 @@ namespace psemek::math::detail template struct array { - using type = std::vector; + using type = util::array; }; template From 774620c673a10a85094346d57761da1b5164b16b Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sat, 25 Oct 2025 16:12:38 +0300 Subject: [PATCH 107/141] Change math::matrix storage type in preparation for dynamic-sized matrices --- .../psemek/gfx/gltf_accessor_iterator.hpp | 6 +- libs/gfx/source/program.cpp | 18 +- .../math/include/psemek/math/detail/array.hpp | 4 +- .../include/psemek/math/detail/array_2d.hpp | 218 ++++++++++++++++++ libs/math/include/psemek/math/matrix.hpp | 123 +++++++--- libs/math/include/psemek/math/vector.hpp | 14 ++ 6 files changed, 331 insertions(+), 52 deletions(-) create mode 100644 libs/math/include/psemek/math/detail/array_2d.hpp diff --git a/libs/gfx/include/psemek/gfx/gltf_accessor_iterator.hpp b/libs/gfx/include/psemek/gfx/gltf_accessor_iterator.hpp index 16af4e84..890b69eb 100644 --- a/libs/gfx/include/psemek/gfx/gltf_accessor_iterator.hpp +++ b/libs/gfx/include/psemek/gfx/gltf_accessor_iterator.hpp @@ -93,14 +93,12 @@ namespace psemek::gfx static auto pointer(math::matrix & value) { - return &value[0][0]; + return value.values().begin(); } static void finalize(math::matrix & value) { - math::matrix temp; - std::copy(value.coords, value.coords + R * C, temp.coords); - value = math::transpose(temp); + value = math::transpose(value); } }; diff --git a/libs/gfx/source/program.cpp b/libs/gfx/source/program.cpp index 05668990..a092048a 100644 --- a/libs/gfx/source/program.cpp +++ b/libs/gfx/source/program.cpp @@ -153,47 +153,47 @@ namespace psemek::gfx void program::uniform_proxy::operator = (math::matrix const & m) { - gl::UniformMatrix2fv(location_, 1, gl::TRUE, m.coords); + gl::UniformMatrix2fv(location_, 1, gl::TRUE, &m[0][0]); } void program::uniform_proxy::operator = (math::matrix const & m) { - gl::UniformMatrix3x2fv(location_, 1, gl::TRUE, m.coords); + gl::UniformMatrix3x2fv(location_, 1, gl::TRUE, &m[0][0]); } void program::uniform_proxy::operator = (math::matrix const & m) { - gl::UniformMatrix4x2fv(location_, 1, gl::TRUE, m.coords); + gl::UniformMatrix4x2fv(location_, 1, gl::TRUE, &m[0][0]); } void program::uniform_proxy::operator = (math::matrix const & m) { - gl::UniformMatrix2x3fv(location_, 1, gl::TRUE, m.coords); + gl::UniformMatrix2x3fv(location_, 1, gl::TRUE, &m[0][0]); } void program::uniform_proxy::operator = (math::matrix const & m) { - gl::UniformMatrix3fv(location_, 1, gl::TRUE, m.coords); + gl::UniformMatrix3fv(location_, 1, gl::TRUE, &m[0][0]); } void program::uniform_proxy::operator = (math::matrix const & m) { - gl::UniformMatrix4x3fv(location_, 1, gl::TRUE, m.coords); + gl::UniformMatrix4x3fv(location_, 1, gl::TRUE, &m[0][0]); } void program::uniform_proxy::operator = (math::matrix const & m) { - gl::UniformMatrix2x4fv(location_, 1, gl::TRUE, m.coords); + gl::UniformMatrix2x4fv(location_, 1, gl::TRUE, &m[0][0]); } void program::uniform_proxy::operator = (math::matrix const & m) { - gl::UniformMatrix3x4fv(location_, 1, gl::TRUE, m.coords); + gl::UniformMatrix3x4fv(location_, 1, gl::TRUE, &m[0][0]); } void program::uniform_proxy::operator = (math::matrix const & m) { - gl::UniformMatrix4fv(location_, 1, gl::TRUE, m.coords); + gl::UniformMatrix4fv(location_, 1, gl::TRUE, &m[0][0]); } void program::uniform_proxy::operator = (math::interval const & i) diff --git a/libs/math/include/psemek/math/detail/array.hpp b/libs/math/include/psemek/math/detail/array.hpp index 91456e95..a86f9f13 100644 --- a/libs/math/include/psemek/math/detail/array.hpp +++ b/libs/math/include/psemek/math/detail/array.hpp @@ -11,7 +11,7 @@ namespace psemek::math::detail : util::exception { empty_array_exception(util::stacktrace stacktrace = {}) - : util::exception("Indexing an empty array", std::move(stacktrace)) + : util::exception("Indexing into a zero-dimensional array", std::move(stacktrace)) {} }; @@ -29,7 +29,7 @@ namespace psemek::math::detail T const & operator[](std::size_t) const { throw empty_array_exception{}; } T & operator[](std::size_t) { throw empty_array_exception{}; } - type operator + (std::size_t) const { return *this; } + // type operator + (std::size_t) const { return *this; } }; }; diff --git a/libs/math/include/psemek/math/detail/array_2d.hpp b/libs/math/include/psemek/math/detail/array_2d.hpp new file mode 100644 index 00000000..bd257e33 --- /dev/null +++ b/libs/math/include/psemek/math/detail/array_2d.hpp @@ -0,0 +1,218 @@ +#pragma once + +#include + +namespace psemek::math::detail +{ + + template + struct array_2d + { + using type = T[R][C]; + }; + + template + struct array_2d + { + struct type + { + static constexpr std::size_t rows = R; + static constexpr std::size_t columns = 0; + + T const * operator[](std::size_t) const { throw empty_array_exception{}; } + T * operator[](std::size_t) { throw empty_array_exception{}; } + }; + }; + + template + struct array_2d + { + struct type + { + std::size_t rows; + static constexpr std::size_t columns = 0; + + type(std::size_t rows) + : rows(rows) + {} + + T const * operator[](std::size_t) const { throw empty_array_exception{}; } + T * operator[](std::size_t) { throw empty_array_exception{}; } + }; + }; + + template + struct array_2d + { + struct type + { + static constexpr std::size_t rows = 0; + static constexpr std::size_t columns = C; + + T const * operator[](std::size_t) const { throw empty_array_exception{}; } + T * operator[](std::size_t) { throw empty_array_exception{}; } + }; + }; + + template + struct array_2d + { + struct type + { + static constexpr std::size_t rows = 0; + std::size_t columns; + + type(std::size_t columns) + : columns(columns) + {} + + T const * operator[](std::size_t) const { throw empty_array_exception{}; } + T * operator[](std::size_t) { throw empty_array_exception{}; } + }; + }; + + template + struct array_2d + { + struct type + { + static constexpr std::size_t rows = 0; + static constexpr std::size_t columns = 0; + + T const * operator[](std::size_t) const { throw empty_array_exception{}; } + T * operator[](std::size_t) { throw empty_array_exception{}; } + }; + }; + + template + struct array_2d + { + struct type + { + static constexpr std::size_t rows = R; + std::size_t columns; + std::unique_ptr data; + + type() + : columns(0) + {} + + type(std::size_t columns) + : columns(columns) + , data(std::make_unique_for_overwrite(rows * columns)) + {} + + type([[maybe_unused]] std::size_t rows, std::size_t columns) + : type(columns) + { + assert(rows == R); + } + + T * operator[] (std::size_t row) + { + return data.get() + row * columns; + } + + T const * operator[] (std::size_t row) const + { + return data.get() + row * columns; + } + + type copy() const + { + type result; + result.columns = columns; + result.data = std::make_unique_for_overwrite(rows * columns); + std::copy(data.get(), data.get() + rows * columns, result.data.get()); + return result; + } + }; + }; + + template + struct array_2d + { + struct type + { + std::size_t rows; + static constexpr std::size_t columns = C; + std::unique_ptr data; + + type() + : rows(0) + {} + + type(std::size_t rows) + : rows(rows) + , data(std::make_unique_for_overwrite(rows * columns)) + {} + + type(std::size_t rows, [[maybe_unused]] std::size_t columns) + : type(rows) + { + assert(columns == C); + } + + T * operator[] (std::size_t row) + { + return data.get() + row * columns; + } + + T const * operator[] (std::size_t row) const + { + return data.get() + row * columns; + } + + type copy() const + { + type result; + result.rows = rows; + result.data = std::make_unique_for_overwrite(rows * columns); + std::copy(data.get(), data.get() + rows * columns, result.data.get()); + return result; + } + }; + }; + + template + struct array_2d + { + struct type + { + std::size_t rows; + std::size_t columns; + std::unique_ptr data; + + type() + : rows(0) + , columns(0) + {} + + type(std::size_t rows, std::size_t columns) + : rows(rows) + , columns(columns) + , data(std::make_unique_for_overwrite(rows * columns)) + {} + + T * operator[] (std::size_t row) + { + return data.get() + row * columns; + } + + T const * operator[] (std::size_t row) const + { + return data.get() + row * columns; + } + + type copy() const + { + type result; + result.rows = rows; + result.data = std::make_unique_for_overwrite(rows * columns); + std::copy(data.get(), data.get() + rows * columns, result.data.get()); + return result; + } + }; + }; + +} diff --git a/libs/math/include/psemek/math/matrix.hpp b/libs/math/include/psemek/math/matrix.hpp index d3f81f6c..ecc5cfa3 100644 --- a/libs/math/include/psemek/math/matrix.hpp +++ b/libs/math/include/psemek/math/matrix.hpp @@ -1,8 +1,9 @@ #pragma once -#include +#include #include #include +#include #include #include @@ -18,26 +19,64 @@ namespace psemek::math static constexpr std::size_t static_rows = R; static constexpr std::size_t static_columns = C; - typename detail::array::type coords; + typename detail::array_2d::type coords; std::size_t rows() const { - return R; + if constexpr (R == dynamic) + { + return coords.rows; + } + else + { + return R; + } } std::size_t columns() const { - return C; + if constexpr (C == dynamic) + { + return coords.columns; + } + else + { + return C; + } } auto operator[](std::size_t i) { - return coords + C * i; + return coords[i]; } auto operator[](std::size_t i) const { - return coords + C * i; + return coords[i]; + } + + util::range values() + { + return {&coords[0][0], &coords[0][0] + rows() * columns()}; + } + + util::range values() const + { + return {&coords[0][0], &coords[0][0] + rows() * columns()}; + } + + matrix copy() const + { + if constexpr (R == dynamic || C == dynamic) + { + matrix r; + r.coords = coords.copy(); + return r; + } + else + { + return *this; + } } matrix & operator *= (T const & s); @@ -56,8 +95,8 @@ namespace psemek::math matrix matrix::zero() { matrix m; - for (std::size_t i = 0; i < R * C; ++i) - m.coords[i] = T(0); + for (auto & v : m.values()) + v = T(0); return m; } @@ -89,20 +128,24 @@ namespace psemek::math matrix cast(matrix const & m) { matrix r; - for (std::size_t i = 0; i < R * C; ++i) - r.coords[i] = static_cast(m.coords[i]); + for (std::size_t i = 0; i < R; ++i) + for (std::size_t j = 0; j < C; ++j) + r[i][j] = static_cast(m[i][j]); return r; } template std::strong_ordering operator <=> (matrix const & m1, matrix const & m2) { - for (std::size_t i = 0; i < R * C; ++i) + for (std::size_t i = 0; i < R; ++i) { - if (m1.coords[i] < m2.coords[i]) - return std::strong_ordering::less; - else if (m1.coords[i] > m2.coords[i]) - return std::strong_ordering::greater; + for (std::size_t j = 0; j < C; ++j) + { + if (m1[i][j] < m2[i][j]) + return std::strong_ordering::less; + else if (m1[i][j] > m2[i][j]) + return std::strong_ordering::greater; + } } return std::strong_ordering::equal; } @@ -147,8 +190,9 @@ namespace psemek::math matrix operator * (matrix const & m, T const & s) { matrix r; - for (std::size_t i = 0; i < R * C; ++i) - r.coords[i] = m.coords[i] * s; + for (std::size_t i = 0; i < R; ++i) + for (std::size_t j = 0; j < C; ++j) + r[i][j] = m[i][j] * s; return r; } @@ -156,8 +200,9 @@ namespace psemek::math matrix operator * (T const & s, matrix const & m) { matrix r; - for (std::size_t i = 0; i < R * C; ++i) - r.coords[i] = s * m.coords[i]; + for (std::size_t i = 0; i < R; ++i) + for (std::size_t j = 0; j < C; ++j) + r[i][j] = m[i][j] * s; return r; } @@ -165,8 +210,9 @@ namespace psemek::math matrix operator / (matrix const & m, T const & s) { matrix r; - for (std::size_t i = 0; i < R * C; ++i) - r.coords[i] = m.coords[i] / s; + for (std::size_t i = 0; i < R; ++i) + for (std::size_t j = 0; j < C; ++j) + r[i][j] = m[i][j] / s; return r; } @@ -188,8 +234,9 @@ namespace psemek::math matrix operator - (matrix const & m) { matrix r; - for (std::size_t i = 0; i < R * C; ++i) - r.coords[i] = -m.coords[i]; + for (std::size_t i = 0; i < R; ++i) + for (std::size_t j = 0; j < C; ++j) + r[i][j] = -m[i][j]; return r; } @@ -197,8 +244,9 @@ namespace psemek::math matrix operator + (matrix const & m1, matrix const & m2) { matrix r; - for (std::size_t i = 0; i < R * C; ++i) - r.coords[i] = m1.coords[i] + m2.coords[i]; + for (std::size_t i = 0; i < R; ++i) + for (std::size_t j = 0; j < C; ++j) + r[i][j] = m1[i][j] + m2[i][j]; return r; } @@ -206,8 +254,9 @@ namespace psemek::math matrix operator - (matrix const & m1, matrix const & m2) { matrix r; - for (std::size_t i = 0; i < R * C; ++i) - r.coords[i] = m1.coords[i] - m2.coords[i]; + for (std::size_t i = 0; i < R; ++i) + for (std::size_t j = 0; j < C; ++j) + r[i][j] = m1[i][j] - m2[i][j]; return r; } @@ -283,11 +332,11 @@ namespace psemek::math { vector m[] = {v, rows...}; - matrix result; - for (std::size_t i = 0; i < result.rows(); ++i) - for (std::size_t j = 0; j < result.columns(); ++j) - result[i][j] = m[i][j]; - return result; + matrix r; + for (std::size_t i = 0; i < r.rows(); ++i) + for (std::size_t j = 0; j < r.columns(); ++j) + r[i][j] = m[i][j]; + return r; } template @@ -295,11 +344,11 @@ namespace psemek::math { vector m[] = {v, columns...}; - matrix result; - for (std::size_t i = 0; i < result.rows(); ++i) - for (std::size_t j = 0; j < result.columns(); ++j) - result[i][j] = m[j][i]; - return result; + matrix r; + for (std::size_t i = 0; i < r.rows(); ++i) + for (std::size_t j = 0; j < r.columns(); ++j) + r[i][j] = m[j][i]; + return r; } template diff --git a/libs/math/include/psemek/math/vector.hpp b/libs/math/include/psemek/math/vector.hpp index e53a5ffb..0be0e1e7 100644 --- a/libs/math/include/psemek/math/vector.hpp +++ b/libs/math/include/psemek/math/vector.hpp @@ -51,6 +51,20 @@ namespace psemek::math } } + vector copy() const + { + if constexpr (N == dynamic) + { + vector result; + result.coords = coords.copy(); + return result; + } + else + { + return *this; + } + } + T & operator[](std::size_t i) { return coords[i]; From 7c398e28ff5832b3c56ade9f2658681d6c4dbdeb Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 13 Nov 2025 12:42:33 +0300 Subject: [PATCH 108/141] Fix hash_table::insert (lmao) --- libs/util/include/psemek/util/hash_table.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libs/util/include/psemek/util/hash_table.hpp b/libs/util/include/psemek/util/hash_table.hpp index 00463c09..f5989f0f 100644 --- a/libs/util/include/psemek/util/hash_table.hpp +++ b/libs/util/include/psemek/util/hash_table.hpp @@ -210,6 +210,11 @@ namespace psemek::util template std::pair, bool> insert(H && value) { + // Need to make sure the key isn't present first, because + // inserting it at a tombstone can lead to duplicate keys + if (auto it = find(this->key_projector()(value)); it != end()) + return {it, false}; + ensure_capacity_for(size_ + 1); std::uint64_t hash = this->hash()(this->key_projector()(value)); return insert_impl(std::forward(value), hash); From 48d756bdd5778ce130ba01b22982ef0c104259bd Mon Sep 17 00:00:00 2001 From: lisyarus Date: Tue, 2 Dec 2025 13:43:30 +0300 Subject: [PATCH 109/141] Reimplement journal lib without sqlite & remove sqlite dependency --- CMakeLists.txt | 5 - libs/journal/CMakeLists.txt | 8 - .../include/psemek/journal/journal.hpp | 6 +- .../include/psemek/journal/log_event.hpp | 2 +- libs/journal/source/journal.cpp | 184 +++++------------- 5 files changed, 52 insertions(+), 153 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index faf923b1..e9aa2924 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,11 +23,6 @@ if(PSEMEK_USE_FREETYPE) list(APPEND PSEMEK_DEFINITIONS "-DPSEMEK_USE_FREETYPE=1") endif() -option(PSEMEK_USE_SQLITE "Include sqlite3 database support for journaling" OFF) -if(PSEMEK_USE_SQLITE) - list(APPEND PSEMEK_DEFINITIONS "-DPSEMEK_USE_SQLITE=1") -endif() - set(PSEMEK_CXX_FLAGS) if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")) list(APPEND PSEMEK_CXX_FLAGS -Wall -Werror -Wextra -pedantic -Wno-narrowing -Wno-sign-compare) diff --git a/libs/journal/CMakeLists.txt b/libs/journal/CMakeLists.txt index cb4e3fd9..33a6feea 100644 --- a/libs/journal/CMakeLists.txt +++ b/libs/journal/CMakeLists.txt @@ -1,14 +1,6 @@ file(GLOB_RECURSE PSEMEK_JOURNAL_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "include/*.hpp") file(GLOB_RECURSE PSEMEK_JOURNAL_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "source/*.cpp") -if(PSEMEK_USE_SQLITE) - find_package(SQLite3 REQUIRED) -endif() - psemek_add_library(psemek-journal ${PSEMEK_JOURNAL_HEADERS} ${PSEMEK_JOURNAL_SOURCES}) target_include_directories(psemek-journal PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ${SQLite3_INCLUDE_DIRS}) target_link_libraries(psemek-journal PUBLIC psemek-util psemek-log) - -if(PSEMEK_USE_SQLITE) - target_link_libraries(psemek-journal PUBLIC SQLite::SQLite3) -endif() diff --git a/libs/journal/include/psemek/journal/journal.hpp b/libs/journal/include/psemek/journal/journal.hpp index 01c3496b..b81a35da 100644 --- a/libs/journal/include/psemek/journal/journal.hpp +++ b/libs/journal/include/psemek/journal/journal.hpp @@ -3,14 +3,12 @@ #include #include -#include - namespace psemek::journal { struct journal { - journal(std::filesystem::path const & path); + journal(); ~journal(); bool enabled() const; @@ -18,7 +16,7 @@ namespace psemek::journal void log_event(event const & event); - std::vector> select(std::string const & query); + std::vector> select(); private: psemek_declare_pimpl diff --git a/libs/journal/include/psemek/journal/log_event.hpp b/libs/journal/include/psemek/journal/log_event.hpp index 7804f5ee..b1dc8f12 100644 --- a/libs/journal/include/psemek/journal/log_event.hpp +++ b/libs/journal/include/psemek/journal/log_event.hpp @@ -7,7 +7,7 @@ #include #define psemek_journal_log_event_extract_key(key, value) key -#define psemek_journal_log_event_extract_value(key, value) std::format("\"{}\"", value) +#define psemek_journal_log_event_extract_value(key, value) std::format("{}", value) #define psemek_journal_log_event_single_attribute_key(r, data, elem) psemek_journal_log_event_extract_key elem, #define psemek_journal_log_event_single_attribute_value(r, data, elem) psemek_journal_log_event_extract_value elem, diff --git a/libs/journal/source/journal.cpp b/libs/journal/source/journal.cpp index 82f1f4d7..4c252344 100644 --- a/libs/journal/source/journal.cpp +++ b/libs/journal/source/journal.cpp @@ -1,164 +1,78 @@ #include -#include -#include #include -#include -#include - -#ifdef PSEMEK_USE_SQLITE - #include -#endif +#include namespace psemek::journal { -#ifdef PSEMEK_USE_SQLITE - struct journal::impl { - impl(std::filesystem::path const & path) + struct table { - if (sqlite3_open(path.c_str(), &database) != SQLITE_OK) - { - auto error_message = std::format("Failed to open sqlite database at {}: {}", path.c_str(), sqlite3_errmsg(database)); - sqlite3_close(database); - throw util::exception(std::move(error_message)); - } - } + event_metadata metadata; + mutable std::vector events; + }; - ~impl() + struct table_hash { - sqlite3_close(database); - } + std::uint64_t operator()(std::string const & name) const noexcept + { + return std::hash{}(name); + } + + std::uint64_t operator()(table const & table) const noexcept + { + return std::hash{}(table.metadata.name); + } + }; + + struct table_equal + { + bool operator()(std::string const & name1, table const & table2) const noexcept + { + return name1 == table2.metadata.name; + } + + bool operator()(table const & table1, table const & table2) const noexcept + { + return table1.metadata.name == table2.metadata.name; + } + }; + + bool enabled = true; + util::hash_set tables; void log_event(event const & event) { + if (!enabled) + return; + auto table_it = tables.find(event.metadata.name); - if (table_it == tables.end()) - { - std::ostringstream command; - - command << "PRAGMA synchronous = OFF;"; - - command << "CREATE TABLE IF NOT EXISTS " << event.metadata.name << "(timestamp TEXT"; - - table_it = tables.insert({event.metadata.name, event.metadata}).first; - table_it->second.source_file = event.metadata.source_file; - table_it->second.source_line = event.metadata.source_line; - - for (auto const & attribute : event.metadata.columns) - command << ", " << attribute << " TEXT"; - - command << ");"; - - auto command_str = command.str(); - - char * error = nullptr; - if (sqlite3_exec(database, command_str.data(), nullptr, nullptr, &error) != SQLITE_OK) - { - auto error_message = std::string("Failed to create sqlite table: ") + error + std::string("\nSQL command: ") + command_str; - sqlite3_free(error); - throw util::exception(std::move(error_message)); - } - } - - { - std::ostringstream command; - - command << "PRAGMA synchronous = OFF; INSERT INTO " << event.metadata.name << "(timestamp"; - - for (auto const & attribute : event.metadata.columns) - command << ", " << attribute; - - command << ") VALUES (\"" << event.data.time << "\""; - - for (auto const & attribute : event.data.values) - command << ", " << attribute; - - command << ");"; - - auto command_str = command.str(); - - char * error = nullptr; - if (sqlite3_exec(database, command_str.data(), nullptr, nullptr, &error) != SQLITE_OK) - { - auto error_message = std::string("Failed to insert into sqlite table: ") + error + std::string("\nSQL command: ") + command_str; - sqlite3_free(error); - throw util::exception(std::move(error_message)); - } - } + table_it = tables.insert(table{event.metadata, {}}).first; + table_it->events.push_back(event.data); } - std::vector> select(std::string const & query) + std::vector> select() { - std::vector> result; + if (!enabled) + return {}; + + std::vector> result; for (auto const & table : tables) - { - std::string table_query = "SELECT * FROM " + table.first + (query.empty() ? "" : " WHERE ") + query + ";"; + for (auto const & event : table.events) + result.push_back({&table.metadata, &event}); - struct context - { - std::vector> * result; - event_metadata const * metadata; - }; - - context ctx{&result, &table.second}; - - auto callback = [](void * pcontext, int columns, char ** values, char **) -> int - { - auto & ctx = *(context *)pcontext; - - auto & event = ctx.result->emplace_back(std::pair{ctx.metadata, event_data{}}); - - event.second.time = values[0]; - - for (int i = 1; i < columns; ++i) - event.second.values.push_back(values[i]); - - return 0; - }; - - char * error = nullptr; - if (sqlite3_exec(database, table_query.c_str(), callback, &ctx, &error) != SQLITE_OK) - sqlite3_free(error); - } - - std::sort(result.begin(), result.end(), [](auto const & e1, auto const & e2){ return e1.second.time < e2.second.time; }); + std::sort(result.begin(), result.end(), [](auto const & e1, auto const & e2){ return e1.second->time < e2.second->time; }); return result; } - - sqlite3 * database = nullptr; - bool enabled = true; - - util::hash_map tables; }; -#else - - struct journal::impl - { - impl(std::filesystem::path const &) - {} - - void log_event(event const &) - {} - - std::vector> select(std::string const &) - { - return {}; - } - - bool enabled = true; - }; - -#endif - - journal::journal(std::filesystem::path const & path) - : pimpl_(make_impl(path)) + journal::journal() + : pimpl_(make_impl()) {} journal::~journal() = default; @@ -178,9 +92,9 @@ namespace psemek::journal impl().log_event(event); } - std::vector> journal::select(std::string const & query) + std::vector> journal::select() { - return impl().select(query); + return impl().select(); } } From 62f2adcb82d3849241b151cc79f6081c7f715fe8 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Tue, 2 Dec 2025 17:40:51 +0300 Subject: [PATCH 110/141] Update to wgpu-native v27.0.2.0 --- CMakeLists.txt | 2 +- libs/wgpu/include/psemek/wgpu/device.hpp | 6 ++- libs/wgpu/include/psemek/wgpu/external/wgpu.h | 41 ++++++++++++++++--- .../psemek/wgpu/render_pass_encoder.hpp | 2 +- .../include/psemek/wgpu/render_pipeline.hpp | 15 +++++++ libs/wgpu/include/psemek/wgpu/texture.hpp | 1 + libs/wgpu/source/command_encoder.cpp | 2 +- libs/wgpu/source/render_pipeline.cpp | 11 ++++- 8 files changed, 68 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e9aa2924..029bf0e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,7 +64,7 @@ if(NOT DEFINED PSEMEK_GRAPHICS_API) endif() if(PSEMEK_GRAPHICS_API STREQUAL WEBGPU) - find_package(wgpu-native 25.0.2.1 REQUIRED) + find_package(wgpu-native 27.0.2.0 REQUIRED) endif() message(STATUS "Using graphics API ${PSEMEK_GRAPHICS_API}") diff --git a/libs/wgpu/include/psemek/wgpu/device.hpp b/libs/wgpu/include/psemek/wgpu/device.hpp index b0f5f432..d9f58549 100644 --- a/libs/wgpu/include/psemek/wgpu/device.hpp +++ b/libs/wgpu/include/psemek/wgpu/device.hpp @@ -46,7 +46,6 @@ namespace psemek::wgpu push_constants = 0x00030001, texture_adapter_specific_format_features = 0x00030002, - multi_draw_indirect = 0x00030003, multi_draw_indirect_count = 0x00030004, vertex_writable_storage = 0x00030005, texture_binding_array = 0x00030006, @@ -59,10 +58,12 @@ namespace psemek::wgpu mappable_primary_buffers = 0x0003000e, buffer_binding_array = 0x0003000f, uniform_buffer_and_storage_texture_array_non_uniform_indexing = 0x00030010, + polygon_mode_line = 0x00030013, + polygon_mode_point = 0x00030014, + conservative_rasterization = 0x00030015, spirv_shader_passthrough = 0x00030017, vertex_attribute64bit = 0x00030019, texture_format_nv12 = 0x0003001a, - ray_tracing_acceleration_structure = 0x0003001b, ray_query = 0x0003001c, shader_f64 = 0x0003001d, shader_i16 = 0x0003001e, @@ -73,6 +74,7 @@ namespace psemek::wgpu subgroup_barrier = 0x00030023, timestamp_query_inside_encoders = 0x00030024, timestamp_query_inside_passes = 0x00030025, + shader_int64 = 0x00030026, }; struct limits diff --git a/libs/wgpu/include/psemek/wgpu/external/wgpu.h b/libs/wgpu/include/psemek/wgpu/external/wgpu.h index ec12da35..272ed23a 100644 --- a/libs/wgpu/include/psemek/wgpu/external/wgpu.h +++ b/libs/wgpu/include/psemek/wgpu/external/wgpu.h @@ -14,13 +14,14 @@ typedef enum WGPUNativeSType { WGPUSType_BindGroupLayoutEntryExtras = 0x00030008, WGPUSType_QuerySetDescriptorExtras = 0x00030009, WGPUSType_SurfaceConfigurationExtras = 0x0003000A, + WGPUSType_SurfaceSourceSwapChainPanel = 0x0003000B, + WGPUSType_PrimitiveStateExtras = 0x0003000C, WGPUNativeSType_Force32 = 0x7FFFFFFF } WGPUNativeSType; typedef enum WGPUNativeFeature { WGPUNativeFeature_PushConstants = 0x00030001, WGPUNativeFeature_TextureAdapterSpecificFormatFeatures = 0x00030002, - WGPUNativeFeature_MultiDrawIndirect = 0x00030003, WGPUNativeFeature_MultiDrawIndirectCount = 0x00030004, WGPUNativeFeature_VertexWritableStorage = 0x00030005, WGPUNativeFeature_TextureBindingArray = 0x00030006, @@ -36,15 +37,14 @@ typedef enum WGPUNativeFeature { // TODO: requires wgpu.h api change // WGPUNativeFeature_AddressModeClampToZero = 0x00030011, // WGPUNativeFeature_AddressModeClampToBorder = 0x00030012, - // WGPUNativeFeature_PolygonModeLine = 0x00030013, - // WGPUNativeFeature_PolygonModePoint = 0x00030014, - // WGPUNativeFeature_ConservativeRasterization = 0x00030015, + WGPUNativeFeature_PolygonModeLine = 0x00030013, + WGPUNativeFeature_PolygonModePoint = 0x00030014, + WGPUNativeFeature_ConservativeRasterization = 0x00030015, // WGPUNativeFeature_ClearTexture = 0x00030016, WGPUNativeFeature_SpirvShaderPassthrough = 0x00030017, // WGPUNativeFeature_Multiview = 0x00030018, WGPUNativeFeature_VertexAttribute64bit = 0x00030019, WGPUNativeFeature_TextureFormatNv12 = 0x0003001A, - WGPUNativeFeature_RayTracingAccelerationStructure = 0x0003001B, WGPUNativeFeature_RayQuery = 0x0003001C, WGPUNativeFeature_ShaderF64 = 0x0003001D, WGPUNativeFeature_ShaderI16 = 0x0003001E, @@ -55,6 +55,7 @@ typedef enum WGPUNativeFeature { WGPUNativeFeature_SubgroupBarrier = 0x00030023, WGPUNativeFeature_TimestampQueryInsideEncoders = 0x00030024, WGPUNativeFeature_TimestampQueryInsidePasses = 0x00030025, + WGPUNativeFeature_ShaderInt64 = 0x00030026, WGPUNativeFeature_Force32 = 0x7FFFFFFF } WGPUNativeFeature; @@ -143,9 +144,11 @@ typedef struct WGPUInstanceExtras { WGPUDx12Compiler dx12ShaderCompiler; WGPUGles3MinorVersion gles3MinorVersion; WGPUGLFenceBehaviour glFenceBehaviour; - WGPUStringView dxilPath; WGPUStringView dxcPath; WGPUDxcMaxShaderModel dxcMaxShaderModel; + + WGPU_NULLABLE const uint8_t* budgetForDeviceCreation; + WGPU_NULLABLE const uint8_t* budgetForDeviceLoss; } WGPUInstanceExtras; typedef struct WGPUDeviceExtras { @@ -256,6 +259,30 @@ typedef struct WGPUSurfaceConfigurationExtras { uint32_t desiredMaximumFrameLatency; } WGPUSurfaceConfigurationExtras WGPU_STRUCTURE_ATTRIBUTE; +/** +* Chained in @ref WGPUSurfaceDescriptor to make a @ref WGPUSurface wrapping a WinUI [`SwapChainPanel`](https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.controls.swapchainpanel). +*/ +typedef struct WGPUSurfaceSourceSwapChainPanel { + WGPUChainedStruct chain; + /** + * A pointer to the [`ISwapChainPanelNative`](https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/win32/microsoft.ui.xaml.media.dxinterop/nn-microsoft-ui-xaml-media-dxinterop-iswapchainpanelnative) + * interface of the SwapChainPanel that will be wrapped by the @ref WGPUSurface. + */ + void * panelNative; +} WGPUSurfaceSourceSwapChainPanel WGPU_STRUCTURE_ATTRIBUTE; + +typedef enum WGPUPolygonMode { + WGPUPolygonMode_Fill = 0, + WGPUPolygonMode_Line = 1, + WGPUPolygonMode_Point = 2, +} WGPUPolygonMode; + +typedef struct WGPUPrimitiveStateExtras { + WGPUChainedStruct chain; + WGPUPolygonMode polygonMode; + WGPUBool conservative; +} WGPUPrimitiveStateExtras WGPU_STRUCTURE_ATTRIBUTE; + typedef void (*WGPULogCallback)(WGPULogLevel level, WGPUStringView message, void * userdata); typedef enum WGPUNativeTextureFormat { @@ -268,8 +295,10 @@ typedef enum WGPUNativeTextureFormat { WGPUNativeTextureFormat_Rgba16Snorm = 0x00030006, // From Features::TEXTURE_FORMAT_NV12 WGPUNativeTextureFormat_NV12 = 0x00030007, + WGPUNativeTextureFormat_P010 = 0x00030008, } WGPUNativeTextureFormat; + #ifdef __cplusplus extern "C" { #endif diff --git a/libs/wgpu/include/psemek/wgpu/render_pass_encoder.hpp b/libs/wgpu/include/psemek/wgpu/render_pass_encoder.hpp index 2bb77c2f..86898227 100644 --- a/libs/wgpu/include/psemek/wgpu/render_pass_encoder.hpp +++ b/libs/wgpu/include/psemek/wgpu/render_pass_encoder.hpp @@ -41,7 +41,7 @@ namespace psemek::wgpu { std::vector chain = {}; texture_view view; - std::uint32_t depth_slice = 0; + std::optional depth_slice = std::nullopt; texture_view resolve_target = {}; enum load_op load_op; enum store_op store_op; diff --git a/libs/wgpu/include/psemek/wgpu/render_pipeline.hpp b/libs/wgpu/include/psemek/wgpu/render_pipeline.hpp index e5592074..5e0fd167 100644 --- a/libs/wgpu/include/psemek/wgpu/render_pipeline.hpp +++ b/libs/wgpu/include/psemek/wgpu/render_pipeline.hpp @@ -125,8 +125,21 @@ namespace psemek::wgpu back = 0x00000003, }; + enum class polygon_mode + { + fill = 0, + line = 1, + point = 2, + }; + struct primitive_state { + struct extras + { + enum polygon_mode polygon_mode; + bool conservative; + }; + std::vector chain = {}; primitive_topology topology; index_format strip_index_format = index_format::undefined; @@ -281,4 +294,6 @@ namespace psemek::wgpu friend struct device; }; + detail::chained_struct_ptr to_chained_struct(primitive_state::extras const &); + } diff --git a/libs/wgpu/include/psemek/wgpu/texture.hpp b/libs/wgpu/include/psemek/wgpu/texture.hpp index 748b9921..e53588da 100644 --- a/libs/wgpu/include/psemek/wgpu/texture.hpp +++ b/libs/wgpu/include/psemek/wgpu/texture.hpp @@ -126,6 +126,7 @@ namespace psemek::wgpu // texture_format_nv12 nv12 = 0x00030007, + p010 = 0x00030008, }; enum class usage : std::uint64_t diff --git a/libs/wgpu/source/command_encoder.cpp b/libs/wgpu/source/command_encoder.cpp index e9dfbe4f..b50b0e00 100644 --- a/libs/wgpu/source/command_encoder.cpp +++ b/libs/wgpu/source/command_encoder.cpp @@ -31,7 +31,7 @@ namespace psemek::wgpu auto & dst = color_attachments.emplace_back(); dst.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(src.chain); dst.view = (WGPUTextureView)src.view.get(); - dst.depthSlice = src.depth_slice; + dst.depthSlice = src.depth_slice.value_or(WGPU_DEPTH_SLICE_UNDEFINED); dst.resolveTarget = (WGPUTextureView)src.resolve_target.get(); dst.loadOp = (WGPULoadOp)src.load_op; dst.storeOp = (WGPUStoreOp)src.store_op; diff --git a/libs/wgpu/source/render_pipeline.cpp b/libs/wgpu/source/render_pipeline.cpp index 84749f43..0d47c148 100644 --- a/libs/wgpu/source/render_pipeline.cpp +++ b/libs/wgpu/source/render_pipeline.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include namespace psemek::wgpu { @@ -25,4 +25,13 @@ namespace psemek::wgpu wgpuRenderPipelineRelease((WGPURenderPipeline)ptr); } + detail::chained_struct_ptr to_chained_struct(primitive_state::extras const & extras) + { + WGPUPrimitiveStateExtras chained = {}; + chained.chain.sType = (WGPUSType)WGPUSType_PrimitiveStateExtras; + chained.polygonMode = (WGPUPolygonMode)extras.polygon_mode; + chained.conservative = extras.conservative; + return detail::make_chained_struct(chained); + } + } From 4b314d78c4ee4613e7d60282a294859385734710 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 3 Dec 2025 15:24:48 +0300 Subject: [PATCH 111/141] Make math::matrix::values() return util::span --- libs/math/include/psemek/math/matrix.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/math/include/psemek/math/matrix.hpp b/libs/math/include/psemek/math/matrix.hpp index ecc5cfa3..15d9d032 100644 --- a/libs/math/include/psemek/math/matrix.hpp +++ b/libs/math/include/psemek/math/matrix.hpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include @@ -55,14 +55,14 @@ namespace psemek::math return coords[i]; } - util::range values() + util::span values() { - return {&coords[0][0], &coords[0][0] + rows() * columns()}; + return {&coords[0][0], rows() * columns()}; } - util::range values() const + util::span values() const { - return {&coords[0][0], &coords[0][0] + rows() * columns()}; + return {&coords[0][0], rows() * columns()}; } matrix copy() const From a8e08bf6d3606f3679175810640bf1647e9db9b4 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 4 Dec 2025 17:21:57 +0300 Subject: [PATCH 112/141] Remove dynamic-sized storage from math::box --- libs/math/include/psemek/math/box.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libs/math/include/psemek/math/box.hpp b/libs/math/include/psemek/math/box.hpp index 407be6d1..24aa6924 100644 --- a/libs/math/include/psemek/math/box.hpp +++ b/libs/math/include/psemek/math/box.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include @@ -13,7 +12,7 @@ namespace psemek::math template struct box { - typename detail::array, N>::type axes; + interval axes[N]; using point_type = point; using vector_type = vector; From a5acb9534b9a7169e44c5acfc97221ebc27e04c7 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 4 Dec 2025 17:22:32 +0300 Subject: [PATCH 113/141] Big math::vector,point,matrix refactor: fully support dynamic (runtime) size in basic operations --- .../math/include/psemek/math/detail/array.hpp | 59 +++- .../include/psemek/math/detail/array_2d.hpp | 94 +++--- libs/math/include/psemek/math/dynamic.hpp | 6 - libs/math/include/psemek/math/matrix.hpp | 271 +++++++++++------- libs/math/include/psemek/math/point.hpp | 109 +++++-- libs/math/include/psemek/math/vector.hpp | 135 +++++---- 6 files changed, 442 insertions(+), 232 deletions(-) diff --git a/libs/math/include/psemek/math/detail/array.hpp b/libs/math/include/psemek/math/detail/array.hpp index a86f9f13..fd835628 100644 --- a/libs/math/include/psemek/math/detail/array.hpp +++ b/libs/math/include/psemek/math/detail/array.hpp @@ -2,7 +2,6 @@ #include #include -#include namespace psemek::math::detail { @@ -15,10 +14,42 @@ namespace psemek::math::detail {} }; + template + struct dynamic_dimensions + {}; + + template <> + struct dynamic_dimensions + { + std::size_t size = 0; + }; + + template + void check_dynamic_size(dynamic_dimensions d1, dynamic_dimensions d2) + { + if constexpr (N == dynamic) + { + if (d1.size != d2.size) + throw dynamic_size_mismatch(d1.size, d2.size); + } + } + template struct array { - using type = T[N]; + struct type + { + static constexpr std::size_t size = N; + + T data[N]; + + type(dynamic_dimensions){} + + dynamic_dimensions dimensions() const { return {}; } + + T const & operator[](std::size_t i) const { return data[i]; } + T & operator[](std::size_t i) { return data[i]; } + }; }; template @@ -26,17 +57,35 @@ namespace psemek::math::detail { struct type { + static constexpr std::size_t size = 0; + + type(dynamic_dimensions<0>){} + + dynamic_dimensions<0> dimensions() const { return {}; } + T const & operator[](std::size_t) const { throw empty_array_exception{}; } T & operator[](std::size_t) { throw empty_array_exception{}; } - - // type operator + (std::size_t) const { return *this; } }; }; template struct array { - using type = util::array; + struct type + { + std::size_t size; + std::unique_ptr data; + + type(dynamic_dimensions dimensions) + : size(dimensions.size) + , data(std::make_unique_for_overwrite(dimensions.size)) + {} + + dynamic_dimensions dimensions() const { return {.size = size}; } + + T const & operator[](std::size_t i) const { return data[i]; } + T & operator[](std::size_t i) { return data[i]; } + }; }; template diff --git a/libs/math/include/psemek/math/detail/array_2d.hpp b/libs/math/include/psemek/math/detail/array_2d.hpp index bd257e33..98f0075f 100644 --- a/libs/math/include/psemek/math/detail/array_2d.hpp +++ b/libs/math/include/psemek/math/detail/array_2d.hpp @@ -5,10 +5,37 @@ namespace psemek::math::detail { + template + struct dynamic_dimensions_2d + { + dynamic_dimensions rows = {}; + dynamic_dimensions columns = {}; + }; + + template + void check_dynamic_size(dynamic_dimensions_2d d1, dynamic_dimensions_2d d2) + { + check_dynamic_size(d1.rows, d2.rows); + check_dynamic_size(d1.columns, d2.columns); + } + template struct array_2d { - using type = T[R][C]; + struct type + { + T data[R * C]; + + static constexpr std::size_t rows = R; + static constexpr std::size_t columns = C; + + type(dynamic_dimensions_2d) {} + + dynamic_dimensions_2d dimensions() const { return {}; } + + T const * operator[](std::size_t row) const { return data + C * row; } + T * operator[](std::size_t row) { return data + C * row; } + }; }; template @@ -19,6 +46,10 @@ namespace psemek::math::detail static constexpr std::size_t rows = R; static constexpr std::size_t columns = 0; + type(dynamic_dimensions_2d) {} + + dynamic_dimensions_2d dimensions() const { return {}; } + T const * operator[](std::size_t) const { throw empty_array_exception{}; } T * operator[](std::size_t) { throw empty_array_exception{}; } }; @@ -32,10 +63,12 @@ namespace psemek::math::detail std::size_t rows; static constexpr std::size_t columns = 0; - type(std::size_t rows) - : rows(rows) + type(dynamic_dimensions_2d dimensions) + : rows(dimensions.rows.size) {} + dynamic_dimensions_2d dimensions() const { return {.rows = rows}; } + T const * operator[](std::size_t) const { throw empty_array_exception{}; } T * operator[](std::size_t) { throw empty_array_exception{}; } }; @@ -49,6 +82,10 @@ namespace psemek::math::detail static constexpr std::size_t rows = 0; static constexpr std::size_t columns = C; + type(dynamic_dimensions_2d<0, C>) {} + + dynamic_dimensions_2d<0, C> dimensions() const { return {}; } + T const * operator[](std::size_t) const { throw empty_array_exception{}; } T * operator[](std::size_t) { throw empty_array_exception{}; } }; @@ -62,10 +99,12 @@ namespace psemek::math::detail static constexpr std::size_t rows = 0; std::size_t columns; - type(std::size_t columns) - : columns(columns) + type(dynamic_dimensions_2d<0, dynamic> dimensions) + : columns(dimensions.columns.size) {} + dynamic_dimensions_2d<0, dynamic> dimensions() const { return {.columns = columns}; } + T const * operator[](std::size_t) const { throw empty_array_exception{}; } T * operator[](std::size_t) { throw empty_array_exception{}; } }; @@ -79,6 +118,10 @@ namespace psemek::math::detail static constexpr std::size_t rows = 0; static constexpr std::size_t columns = 0; + type(dynamic_dimensions_2d<0, 0>) {} + + dynamic_dimensions_2d<0, 0> dimensions() const { return {}; } + T const * operator[](std::size_t) const { throw empty_array_exception{}; } T * operator[](std::size_t) { throw empty_array_exception{}; } }; @@ -93,20 +136,12 @@ namespace psemek::math::detail std::size_t columns; std::unique_ptr data; - type() - : columns(0) - {} - - type(std::size_t columns) - : columns(columns) + type(dynamic_dimensions_2d dimensions) + : columns(dimensions.columns.size) , data(std::make_unique_for_overwrite(rows * columns)) {} - type([[maybe_unused]] std::size_t rows, std::size_t columns) - : type(columns) - { - assert(rows == R); - } + dynamic_dimensions_2d dimensions() const { return {.columns = columns}; } T * operator[] (std::size_t row) { @@ -138,20 +173,12 @@ namespace psemek::math::detail static constexpr std::size_t columns = C; std::unique_ptr data; - type() - : rows(0) - {} - - type(std::size_t rows) - : rows(rows) + type(dynamic_dimensions_2d dimensions) + : rows(dimensions.rows.size) , data(std::make_unique_for_overwrite(rows * columns)) {} - type(std::size_t rows, [[maybe_unused]] std::size_t columns) - : type(rows) - { - assert(columns == C); - } + dynamic_dimensions_2d dimensions() const { return {.rows = rows}; } T * operator[] (std::size_t row) { @@ -183,17 +210,14 @@ namespace psemek::math::detail std::size_t columns; std::unique_ptr data; - type() - : rows(0) - , columns(0) - {} - - type(std::size_t rows, std::size_t columns) - : rows(rows) - , columns(columns) + type(dynamic_dimensions_2d dimensions) + : rows(dimensions.rows.size) + , columns(dimensions.columns.size) , data(std::make_unique_for_overwrite(rows * columns)) {} + dynamic_dimensions_2d dimensions() const { return {.rows = rows, .columns = columns}; } + T * operator[] (std::size_t row) { return data.get() + row * columns; diff --git a/libs/math/include/psemek/math/dynamic.hpp b/libs/math/include/psemek/math/dynamic.hpp index 1a18b928..37f17942 100644 --- a/libs/math/include/psemek/math/dynamic.hpp +++ b/libs/math/include/psemek/math/dynamic.hpp @@ -18,10 +18,4 @@ namespace psemek::math {} }; - inline void check_dynamic_size(std::size_t size1, std::size_t size2) - { - if (size1 != size2) - throw dynamic_size_mismatch(size1, size2); - } - } diff --git a/libs/math/include/psemek/math/matrix.hpp b/libs/math/include/psemek/math/matrix.hpp index 15d9d032..21662f82 100644 --- a/libs/math/include/psemek/math/matrix.hpp +++ b/libs/math/include/psemek/math/matrix.hpp @@ -19,30 +19,30 @@ namespace psemek::math static constexpr std::size_t static_rows = R; static constexpr std::size_t static_columns = C; + using dynamic_dimensions_type = detail::dynamic_dimensions_2d; + typename detail::array_2d::type coords; + // Internal + matrix(dynamic_dimensions_type dimensions); + + matrix(); + + template + matrix(Args && ... args) requires (R != dynamic && C != dynamic && sizeof...(Args) == R * C && detail::all_convertible_to::value); + + explicit matrix(std::size_t rows) requires(R == dynamic && C != dynamic); + explicit matrix(std::size_t columns) requires(R != dynamic && C == dynamic); + matrix(std::size_t rows, std::size_t columns) requires(R == dynamic && C == dynamic); + std::size_t rows() const { - if constexpr (R == dynamic) - { - return coords.rows; - } - else - { - return R; - } + return coords.rows; } std::size_t columns() const { - if constexpr (C == dynamic) - { - return coords.columns; - } - else - { - return C; - } + return coords.columns; } auto operator[](std::size_t i) @@ -85,32 +85,72 @@ namespace psemek::math matrix & operator += (matrix const & v); matrix & operator -= (matrix const & v); - static matrix zero(); - static matrix identity(); - static matrix scalar(T const & s); + static matrix zero(dynamic_dimensions_type dimensions = {}); + static matrix identity(dynamic_dimensions_type dimensions = {}); + static matrix scalar(T const & s, dynamic_dimensions_type dimensions = {}); static matrix diagonal(vector const & d); }; template - matrix matrix::zero() + void check_dynamic_size(matrix const & m1, matrix const & m2) { - matrix m; + check_dynamic_size(m1.coords.dimensions(), m2.coords.dimensions()); + } + + template + matrix::matrix(dynamic_dimensions_type dimensions) + : coords(dimensions) + {} + + template + matrix::matrix() + : matrix(dynamic_dimensions_type{}) + {} + + template + template + matrix::matrix(Args && ... args) requires (R != dynamic && C != dynamic && sizeof...(Args) == R * C && detail::all_convertible_to::value) + : coords({}) + { + auto out = values().begin(); + ((*out++ = args), ...); + } + + template + matrix::matrix(std::size_t rows) requires(R == dynamic && C != dynamic) + : coords({.rows = rows}) + {} + + template + matrix::matrix(std::size_t columns) requires(R != dynamic && C == dynamic) + : coords({.columns = columns}) + {} + + template + matrix::matrix(std::size_t rows, std::size_t columns) requires(R == dynamic && C == dynamic) + : coords({.rows = rows, .columns = columns}) + {} + + template + matrix matrix::zero(dynamic_dimensions_type dimensions) + { + matrix m(dimensions); for (auto & v : m.values()) v = T(0); return m; } template - matrix matrix::identity() + matrix matrix::identity(dynamic_dimensions_type dimensions) { - return scalar(T(1)); + return scalar(T(1), dimensions); } template - matrix matrix::scalar(T const & s) + matrix matrix::scalar(T const & s, dynamic_dimensions_type dimensions) { - matrix m = zero(); - for (std::size_t i = 0; i < std::min(R, C); ++i) + matrix m = zero(dimensions); + for (std::size_t i = 0; i < std::min(m.rows(), m.columns()); ++i) m[i][i] = s; return m; } @@ -118,7 +158,17 @@ namespace psemek::math template matrix matrix::diagonal(vector const & d) { - matrix m = zero(); + dynamic_dimensions_type dimensions; + if constexpr (R == dynamic) + { + dimensions.rows = d.dimension(); + } + if constexpr (C == dynamic) + { + dimensions.columns = d.dimension(); + } + + matrix m = zero(dimensions); for (std::size_t i = 0; i < std::min(R, C); ++i) m[i][i] = d[i]; return m; @@ -127,25 +177,33 @@ namespace psemek::math template matrix cast(matrix const & m) { - matrix r; - for (std::size_t i = 0; i < R; ++i) - for (std::size_t j = 0; j < C; ++j) - r[i][j] = static_cast(m[i][j]); + matrix r(m.coords.dimensions()); + + auto out = r.values().begin(); + for (auto const & value : m.values()) + *out++ = static_cast(value); + return r; } template std::strong_ordering operator <=> (matrix const & m1, matrix const & m2) { - for (std::size_t i = 0; i < R; ++i) + check_dynamic_size(m1, m2); + auto begin1 = m1.values().begin(); + auto begin2 = m2.values().begin(); + auto end1 = m1.values().end(); + + // Intentionally don't use std::lexicographical_compare_three_way to fake strong ordering + // (actual ordering might be partial & weak due to NaN's and signed zeros + while (begin1 != end1) { - for (std::size_t j = 0; j < C; ++j) - { - if (m1[i][j] < m2[i][j]) - return std::strong_ordering::less; - else if (m1[i][j] > m2[i][j]) - return std::strong_ordering::greater; - } + if (*begin1 < *begin2) + return std::strong_ordering::less; + else if (*begin1 > *begin2) + return std::strong_ordering::greater; + ++begin1; + ++begin2; } return std::strong_ordering::equal; } @@ -189,30 +247,30 @@ namespace psemek::math template matrix operator * (matrix const & m, T const & s) { - matrix r; - for (std::size_t i = 0; i < R; ++i) - for (std::size_t j = 0; j < C; ++j) - r[i][j] = m[i][j] * s; + matrix r(m.coords.dimensions()); + auto out = r.values().begin(); + for (auto const & value : m.values()) + *out++ = value * s; return r; } template matrix operator * (T const & s, matrix const & m) { - matrix r; - for (std::size_t i = 0; i < R; ++i) - for (std::size_t j = 0; j < C; ++j) - r[i][j] = m[i][j] * s; + matrix r(m.coords.dimensions()); + auto out = r.values().begin(); + for (auto const & value : m.values()) + *out++ = s * value; return r; } template matrix operator / (matrix const & m, T const & s) { - matrix r; - for (std::size_t i = 0; i < R; ++i) - for (std::size_t j = 0; j < C; ++j) - r[i][j] = m[i][j] / s; + matrix r(m.coords.dimensions()); + auto out = r.values().begin(); + for (auto const & value : m.values()) + *out++ = value / s; return r; } @@ -233,30 +291,34 @@ namespace psemek::math template matrix operator - (matrix const & m) { - matrix r; - for (std::size_t i = 0; i < R; ++i) - for (std::size_t j = 0; j < C; ++j) - r[i][j] = -m[i][j]; + matrix r(m.coords.dimensions()); + auto out = r.values().begin(); + for (auto const & value : m.values()) + *out++ = -value; return r; } template matrix operator + (matrix const & m1, matrix const & m2) { - matrix r; - for (std::size_t i = 0; i < R; ++i) - for (std::size_t j = 0; j < C; ++j) - r[i][j] = m1[i][j] + m2[i][j]; + check_dynamic_size(m1, m2); + matrix r(m1.coords.dimensions()); + auto in1 = m1.values().begin(); + auto in2 = m2.values().begin(); + for (auto & value : r.values()) + value = (*in1++) + (*in2++); return r; } template matrix operator - (matrix const & m1, matrix const & m2) { - matrix r; - for (std::size_t i = 0; i < R; ++i) - for (std::size_t j = 0; j < C; ++j) - r[i][j] = m1[i][j] - m2[i][j]; + check_dynamic_size(m1, m2); + matrix r(m1.coords.dimensions()); + auto in1 = m1.values().begin(); + auto in2 = m2.values().begin(); + for (auto & value : r.values()) + value = (*in1++) - (*in2++); return r; } @@ -277,11 +339,11 @@ namespace psemek::math template vector operator * (matrix const & m, vector const & v) { - vector r; - for (std::size_t i = 0; i < R; ++i) + detail::check_dynamic_size(m.coords.dimensions().columns, v.coords.dimensions()); + auto r = vector::zero(m.coords.dimensions().rows); + for (std::size_t i = 0; i < m.rows(); ++i) { - r[i] = T{}; - for (std::size_t j = 0; j < C; ++j) + for (std::size_t j = 0; j < m.columns(); ++j) r[i] += m[i][j] * v[j]; } return r; @@ -290,12 +352,12 @@ namespace psemek::math template vector operator * (vector const & v, matrix const & m) { - vector r; - for (std::size_t j = 0; j < C; ++j) + detail::check_dynamic_size(v.coords.dimensions(), m.coords.dimensions().rows); + auto r = vector::zero(m.coords.dimensions().columns); + for (std::size_t i = 0; i < m.rows(); ++i) { - r[j] = T{}; - for (std::size_t i = 0; i < R; ++i) - r[i] += v[j] * m[i][j]; + for (std::size_t j = 0; j < m.columns(); ++j) + r[j] += v[i] * m[i][j]; } return r; } @@ -303,14 +365,13 @@ namespace psemek::math template matrix operator * (matrix const & m1, matrix const & m2) { - matrix r; - for (std::size_t i = 0; i < R; ++i) + detail::check_dynamic_size(m1.coords.dimensions().columns, m2.coords.dimensions().rows); + auto r = matrix::zero(); + for (std::size_t i = 0; i < m1.rows(); ++i) { - for (std::size_t j = 0; j < C; ++j) + for (std::size_t j = 0; j < m2.columns(); ++j) { - r[i][j] = T{}; - - for (std::size_t k = 0; k < K; ++k) + for (std::size_t k = 0; k < m1.columns(); ++k) r[i][j] += m1[i][k] * m2[k][j]; } } @@ -320,9 +381,9 @@ namespace psemek::math template matrix transpose(matrix const & m) { - matrix r; - for (std::size_t i = 0; i < R; ++i) - for (std::size_t j = 0; j < C; ++j) + matrix r({.rows = m.coords.dimensions().columns, .columns = m.coords.dimensions().rows}); + for (std::size_t i = 0; i < m.rows(); ++i) + for (std::size_t j = 0; j < m.columns(); ++j) r[j][i] = m[i][j]; return r; } @@ -330,9 +391,11 @@ namespace psemek::math template auto by_rows(vector const & v, Rows const & ... rows) { + (check_dynamic_size(v, rows), ...); + vector m[] = {v, rows...}; - matrix r; + matrix r({.columns = v.coords.dimensions()}); for (std::size_t i = 0; i < r.rows(); ++i) for (std::size_t j = 0; j < r.columns(); ++j) r[i][j] = m[i][j]; @@ -342,9 +405,10 @@ namespace psemek::math template auto by_columns(vector const & v, Columns const & ... columns) { + (check_dynamic_size(v, columns), ...); vector m[] = {v, columns...}; - matrix r; + matrix r({.rows = v.coords.dimensions()}); for (std::size_t i = 0; i < r.rows(); ++i) for (std::size_t j = 0; j < r.columns(); ++j) r[i][j] = m[j][i]; @@ -354,8 +418,8 @@ namespace psemek::math template vector row(matrix const & m, std::size_t i) { - vector r; - for (std::size_t j = 0; j < C; ++j) + vector r(m.coords.dimensions().columns); + for (std::size_t j = 0; j < m.columns(); ++j) r[j] = m[i][j]; return r; } @@ -363,8 +427,8 @@ namespace psemek::math template vector column(matrix const & m, std::size_t j) { - vector r; - for (std::size_t i = 0; i < R; ++i) + vector r(m.coords.dimensions().rows); + for (std::size_t i = 0; i < m.rows(); ++i) r[i] = m[i][j]; return r; } @@ -373,8 +437,8 @@ namespace psemek::math T frobenius_norm_sqr(matrix const & m) { T r{0}; - for (std::size_t i = 0; i < R; ++i) - for (std::size_t j = 0; j < C; ++j) + for (std::size_t i = 0; i < m.rows(); ++i) + for (std::size_t j = 0; j < m.columns(); ++j) r += sqr(m[i][j]); return r; } @@ -389,12 +453,13 @@ namespace psemek::math T trace(matrix const & m) { T r{0}; - for (std::size_t i = 0; i < std::min(R, C); ++i) + for (std::size_t i = 0; i < std::min(m.rows(), m.columns()); ++i) r += m[i][i]; return r; } template + requires (R1 != dynamic && C1 != dynamic && R2 != dynamic && C2 != dynamic) matrix submatrix(matrix const & m) { static_assert(R1 < R2); @@ -403,13 +468,9 @@ namespace psemek::math static_assert(C2 <= C); matrix result; - for (std::size_t i = 0; i < R2 - R1; ++i) - { - for (std::size_t j = 0; j < C2 - C1; ++j) - { + for (std::size_t i = 0; i < result.rows(); ++i) + for (std::size_t j = 0; j < result.columns(); ++j) result[i][j] = m[i + R1][j + C1]; - } - } return result; } @@ -426,12 +487,12 @@ namespace psemek::math return r; } - template - matrix outer_product(vector const & v1, vector const & v2) + template + matrix outer_product(vector const & v1, vector const & v2) { - matrix r; - for (std::size_t i = 0; i < N; ++i) - for (std::size_t j = 0; j < N; ++j) + matrix r({.rows = v1.coords.dimensions(), .columns = v2.coords.dimensions()}); + for (std::size_t i = 0; i < r.rows(); ++i) + for (std::size_t j = 0; j < r.columns(); ++j) r[i][j] = v1[i] * v2[j]; return r; } @@ -439,9 +500,9 @@ namespace psemek::math template std::ostream & operator << (std::ostream & os, matrix const & m) { - for (std::size_t i = 0; i < R; ++i) + for (std::size_t i = 0; i < m.rows(); ++i) { - for (std::size_t j = 0; j < C; ++j) + for (std::size_t j = 0; j < m.columns(); ++j) os << m[i][j] << ' '; os << '\n'; } @@ -458,9 +519,9 @@ namespace psemek::math template std::ostream & operator << (std::ostream & os, setw const & w) { - for (std::size_t i = 0; i < R; ++i) + for (std::size_t i = 0; i < w.m.rows(); ++i) { - for (std::size_t j = 0; j < C; ++j) + for (std::size_t j = 0; j < w.m.columns(); ++j) os << std::fixed << std::right << std::setw(w.w) << w.m[i][j] << ' '; os << '\n'; } diff --git a/libs/math/include/psemek/math/point.hpp b/libs/math/include/psemek/math/point.hpp index afb4b004..7aa26924 100644 --- a/libs/math/include/psemek/math/point.hpp +++ b/libs/math/include/psemek/math/point.hpp @@ -19,17 +19,42 @@ namespace psemek::math typename detail::array::type coords; - point() = default; + using dynamic_dimensions_type = detail::dynamic_dimensions; + + // Internal + point(dynamic_dimensions_type dimensions) + : coords(dimensions) + {} + + point() + : coords({}) + {} + + explicit point(std::size_t size) requires (N == dynamic) + : coords({.size = size}) + {} template - requires((sizeof...(Args) == N) && detail::all_convertible_to::value) + requires(N != dynamic && sizeof...(Args) == N && detail::all_convertible_to::value) point(Args && ... args) - : coords{ static_cast(std::forward(args))... } - {} + : coords({}) + { + auto out = values().begin(); + ((*out++ = args), ...); + } + + template + requires(N == dynamic && detail::all_convertible_to::value) + point(Args && ... args) + : coords({.size = sizeof...(Args)}) + { + auto out = values().begin(); + ((*out++ = args), ...); + } std::size_t dimension() const { - return N; + return coords.size; } T & operator[](std::size_t i) @@ -42,20 +67,56 @@ namespace psemek::math return coords[i]; } + util::span values() + { + return {&coords[0], dimension()}; + } + + util::span values() const + { + return {&coords[0], dimension()}; + } + + point copy() const + { + if constexpr (N == dynamic) + { + point result; + result.coords = coords.copy(); + return result; + } + else + { + return *this; + } + } + point & operator += (vector const & v); point & operator -= (vector const & v); - static point zero(); + static point zero(dynamic_dimensions_type dimensions = {}); }; template point(Args && ...) -> point, sizeof...(Args)>; template - point point::zero() + void check_dynamic_size(point const & p1, point const & p2) { - point p; - for (std::size_t i = 0; i < N; ++i) + check_dynamic_size(p1.coords.dimensions(), p2.coords.dimensions()); + } + + template + void check_dynamic_size(point const & p, vector const & v) + { + check_dynamic_size(p.coords.dimensions(), v.coords.dimensions()); + } + + template + point point::zero(dynamic_dimensions_type dimensions) + { + point p(dimensions); + for (std::size_t i = 0; i < p.dimension(); ++i) p[i] = T(0); return p; } @@ -63,8 +124,8 @@ namespace psemek::math template point cast(point const & p) { - point r; - for (std::size_t i = 0; i < N; ++i) + point r(p.coords.dimensions()); + for (std::size_t i = 0; i < r.dimension(); ++i) r[i] = T1(p[i]); return r; } @@ -108,7 +169,9 @@ namespace psemek::math template std::strong_ordering operator <=> (point const & p1, point const & p2) { - for (std::size_t i = 0; i < N; ++i) + check_dynamic_size(p1, p2); + + for (std::size_t i = 0; i < p1.dimension(); ++i) { if (p1[i] < p2[i]) return std::strong_ordering::less; @@ -121,7 +184,8 @@ namespace psemek::math template point operator + (point const & p, vector const & v) { - point r; + check_dynamic_size(p, v); + point r(p.coords.dimensions()); for (std::size_t i = 0; i < N; ++i) r[i] = p[i] + v[i]; return r; @@ -130,7 +194,8 @@ namespace psemek::math template point operator + (vector const & v, point const & p) { - point r; + check_dynamic_size(p, v); + point r(p.coords.dimensions()); for (std::size_t i = 0; i < N; ++i) r[i] = v[i] + p[i]; return r; @@ -139,7 +204,8 @@ namespace psemek::math template point operator - (point const & p, vector const & v) { - point r; + check_dynamic_size(p, v); + point r(p.coords.dimensions()); for (std::size_t i = 0; i < N; ++i) r[i] = p[i] - v[i]; return r; @@ -148,7 +214,8 @@ namespace psemek::math template vector operator - (point const & p1, point const & p2) { - vector r; + check_dynamic_size(p1, p2); + vector r(p1.coords.dimensions()); for (std::size_t i = 0; i < N; ++i) r[i] = p1[i] - p2[i]; return r; @@ -199,8 +266,14 @@ namespace psemek::math template std::ostream & operator << (std::ostream & os, point const & p) { + if constexpr (N == 0) + { + os << "()"; + return os; + } + os << '(' << p[0]; - for (std::size_t i = 1; i < N; ++i) + for (std::size_t i = 1; i < p.dimension(); ++i) os << ", " << p[i]; os << ')'; return os; @@ -209,7 +282,7 @@ namespace psemek::math template bool isfinite(point const & p) { - for (std::size_t i = 0; i < N; ++i) + for (std::size_t i = 0; i < p.dimension(); ++i) if (!std::isfinite(p[i])) return false; return true; diff --git a/libs/math/include/psemek/math/vector.hpp b/libs/math/include/psemek/math/vector.hpp index 0be0e1e7..dc067e0f 100644 --- a/libs/math/include/psemek/math/vector.hpp +++ b/libs/math/include/psemek/math/vector.hpp @@ -2,11 +2,10 @@ #include #include -#include +#include #include #include -#include #include #include #include @@ -22,33 +21,62 @@ namespace psemek::math typename detail::array::type coords; - vector() = default; + using dynamic_dimensions_type = detail::dynamic_dimensions; - explicit vector(std::size_t size) requires (N != dynamic) - { - assert(size == N); - } + // Internal + vector(dynamic_dimensions_type dimensions) + : coords(dimensions) + {} + + vector() + : coords({}) + {} explicit vector(std::size_t size) requires (N == dynamic) - : coords(size, T{}) + : coords({.size = size}) {} template - requires((sizeof...(Args) == N || N == dynamic) && detail::all_convertible_to::value) + requires(N != dynamic && sizeof...(Args) == N && detail::all_convertible_to::value) vector(Args && ... args) - : coords{ static_cast(std::forward(args))... } - {} + : coords({}) + { + auto out = values().begin(); + ((*out++ = args), ...); + } + + template + requires(N == dynamic && detail::all_convertible_to::value) + vector(Args && ... args) + : coords({.size = sizeof...(Args)}) + { + auto out = values().begin(); + ((*out++ = args), ...); + } std::size_t dimension() const { - if constexpr (N == dynamic) - { - return coords.size(); - } - else - { - return N; - } + return coords.size; + } + + T & operator[](std::size_t i) + { + return coords[i]; + } + + T const & operator[](std::size_t i) const + { + return coords[i]; + } + + util::span values() + { + return {&coords[0], dimension()}; + } + + util::span values() const + { + return {&coords[0], dimension()}; } vector copy() const @@ -65,33 +93,28 @@ namespace psemek::math } } - T & operator[](std::size_t i) - { - return coords[i]; - } - - T const & operator[](std::size_t i) const - { - return coords[i]; - } - vector & operator *= (T const & s); vector & operator /= (T const & s); vector & operator += (vector const & v); vector & operator -= (vector const & v); - static vector zero() requires (N != dynamic); - static vector zero(std::size_t size); + static vector zero(dynamic_dimensions_type dimensions = {}); }; template vector(Args && ...) -> vector, sizeof...(Args)>; + template + void check_dynamic_size(vector const & v1, vector const & v2) + { + check_dynamic_size(v1.coords.dimensions(), v2.coords.dimensions()); + } + template vector cast(vector const & v) { - vector r(v.dimension()); + vector r(v.coords.dimensions()); for (std::size_t i = 0; i < v.dimension(); ++i) r[i] = static_cast(v[i]); return r; @@ -100,7 +123,7 @@ namespace psemek::math template std::strong_ordering operator <=> (vector const & v1, vector const & v2) { - check_dynamic_size(v1.dimension(), v2.dimension()); + check_dynamic_size(v1, v2); for (std::size_t i = 0; i < v1.dimension(); ++i) { @@ -151,7 +174,7 @@ namespace psemek::math template vector operator * (vector const & v, T const & s) { - vector r(v.dimension()); + vector r(v.coords.dimensions()); for (std::size_t i = 0; i < v.dimension(); ++i) r[i] = v[i] * s; return r; @@ -160,7 +183,7 @@ namespace psemek::math template vector operator * (T const & s, vector const & v) { - vector r(v.dimension()); + vector r(v.coords.dimensions()); for (std::size_t i = 0; i < v.dimension(); ++i) r[i] = s * v[i]; return r; @@ -169,7 +192,7 @@ namespace psemek::math template vector operator / (vector const & v, T const & s) { - vector r(v.dimension()); + vector r(v.coords.dimensions()); for (std::size_t i = 0; i < v.dimension(); ++i) r[i] = v[i] / s; return r; @@ -178,7 +201,7 @@ namespace psemek::math template vector operator - (vector const & v) { - vector r(v.dimension()); + vector r(v.coords.dimensions()); for (std::size_t i = 0; i < v.dimension(); ++i) r[i] = -v[i]; return r; @@ -187,9 +210,9 @@ namespace psemek::math template vector operator + (vector const & v1, vector const & v2) { - check_dynamic_size(v1.dimension(), v2.dimension()); + check_dynamic_size(v1, v2); - vector r(v1.dimension()); + vector r(v1.coords.dimensions()); for (std::size_t i = 0; i < v1.dimension(); ++i) r[i] = v1[i] + v2[i]; return r; @@ -198,9 +221,9 @@ namespace psemek::math template vector operator - (vector const & v1, vector const & v2) { - check_dynamic_size(v1.dimension(), v2.dimension()); + check_dynamic_size(v1, v2); - vector r(v1.dimension()); + vector r(v1.coords.dimensions()); for (std::size_t i = 0; i < v1.dimension(); ++i) r[i] = v1[i] - v2[i]; return r; @@ -231,24 +254,10 @@ namespace psemek::math } template - vector vector::zero() requires (N != dynamic) + vector vector::zero(dynamic_dimensions_type dimensions) { - vector r; - for (std::size_t i = 0; i < N; ++i) - r[i] = T(0); - return r; - } - - template - vector vector::zero(std::size_t size) - { - if constexpr (N != dynamic) - { - assert(N == size); - } - - vector r(size); - for (std::size_t i = 0; i < size; ++i) + vector r(dimensions); + for (std::size_t i = 0; i < r.dimension(); ++i) r[i] = T(0); return r; } @@ -256,7 +265,7 @@ namespace psemek::math template T dot(vector const & v1, vector const & v2) { - check_dynamic_size(v1.dimension(), v2.dimension()); + check_dynamic_size(v1, v2); T r{}; for (std::size_t i = 0; i < v1.dimension(); ++i) @@ -332,8 +341,8 @@ namespace psemek::math template vector ort(vector const & v) { - vector result = vector::zero(v.dimension()); - if constexpr ((N % 2) == 0) + vector result = vector::zero(v.coords.dimensions()); + if ((N != dynamic && (N % 2) == 0) || (N == dynamic && (v.dimension() % 2) == 0)) { for (std::size_t i = 0; i < v.dimension(); i += 2) { @@ -414,9 +423,9 @@ namespace psemek::math { using R = decltype(f(v0[0], vs[0]...)); - (check_dynamic_size(v0.dimension(), vs.dimension()), ...); + (check_dynamic_size(v0, vs), ...); - vector result(v0.dimension()); + vector result(v0.coords.dimensions()); for (std::size_t i = 0; i < v0.dimension(); ++i) result[i] = f(v0[i], vs[i]...); return result; From 8832700e25f2ed6507454e22d8f61fd027c8a59a Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 4 Dec 2025 18:11:11 +0300 Subject: [PATCH 114/141] Optimize math::length(vector) to prevent allocations for runtime-sized vectors --- libs/math/include/psemek/math/vector.hpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/libs/math/include/psemek/math/vector.hpp b/libs/math/include/psemek/math/vector.hpp index dc067e0f..fa017bb8 100644 --- a/libs/math/include/psemek/math/vector.hpp +++ b/libs/math/include/psemek/math/vector.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -286,17 +287,22 @@ namespace psemek::math using std::max; T r = abs(v[0]); for (std::size_t i = 1; i < v.dimension(); ++i) - r = max(r, abs(v[i])); + make_max(r, abs(v[i])); return r; } template T length(vector const & v) { - T const m = linf_norm(v); - if (m == T(0)) return m; + T const max = linf_norm(v); + if (max == T(0)) return max; + + T sum{}; + for (auto const & value : v.values()) + sum += sqr(value / max); + using std::sqrt; - return m * sqrt(length_sqr(v / m)); + return max * sqrt(sum); } template From 2549d248a5fe0210e3acf7c6f89d48f7853416f2 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 4 Dec 2025 18:11:32 +0300 Subject: [PATCH 115/141] Improve matrix norms computation & add linf_norm --- libs/math/include/psemek/math/matrix.hpp | 26 ++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/libs/math/include/psemek/math/matrix.hpp b/libs/math/include/psemek/math/matrix.hpp index 21662f82..49bee094 100644 --- a/libs/math/include/psemek/math/matrix.hpp +++ b/libs/math/include/psemek/math/matrix.hpp @@ -437,16 +437,34 @@ namespace psemek::math T frobenius_norm_sqr(matrix const & m) { T r{0}; - for (std::size_t i = 0; i < m.rows(); ++i) - for (std::size_t j = 0; j < m.columns(); ++j) - r += sqr(m[i][j]); + for (auto const & value : m.values()) + r += sqr(value); + return r; + } + + template + T linf_norm(matrix const & m) + { + using std::abs; + using std::max; + T r = abs(m[0][0]); + for (auto const & value : m.values()) + make_max(r, abs(value)); return r; } template T frobenius_norm(matrix const & m) { - return std::sqrt(frobenius_norm_sqr(m)); + T const max = linf_norm(m); + if (max == T(0)) return max; + + T sum{}; + for (auto const & value : m.values()) + sum += sqr(value / max); + + using std::sqrt; + return max * sqrt(sum); } template From e3cbbb5b4703e86ff5e1c714f482c9d4cf44c4e9 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 5 Dec 2025 17:10:52 +0300 Subject: [PATCH 116/141] Weather simulation test (wip) --- examples/weather.cpp | 326 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 326 insertions(+) create mode 100644 examples/weather.cpp diff --git a/examples/weather.cpp b/examples/weather.cpp new file mode 100644 index 00000000..b4c0824e --- /dev/null +++ b/examples/weather.cpp @@ -0,0 +1,326 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace psemek; + +struct weather_app + : app::application_base +{ + static constexpr int N = 128; + + weather_app(options const &, context const &) + { + simulation_box_ = {{{0.f, N}, {0.f, N}}}; + + obstacle_.resize({N, N}, 0.f); + velocity_.resize({N, N}); + new_velocity_.resize({N, N}); + pressure_.resize({N, N}, 0.f); + temperature_.resize({N, N}, -1.f); + new_temperature_.resize({N, N}); + + random::generator rng{random::device{}}; + // random::generator rng{0, 0}; + random::uniform_ball_vector_distribution random_velocity{}; + + for (auto & v : velocity_) + v = random_velocity(rng); + + for (int y = 0; y < N; ++y) + { + for (int x = 0; x < N; ++x) + { + // velocity_(x, y)[0] += (y*1.f/N-0.5f); + temperature_(x, y) = (x < N / 2) ? -1.f : 1.f; + } + } + } + + void on_event(app::key_event const & event) override + { + if (event.down && event.key == app::keycode::SPACE) + paused_ = !paused_; + } + + void update() override + { + if (paused_) + return; + + float const dt = 0.1f; + float const viscosity = 0.01f; + float const temperature_diffusion = 0.01f; + float const coriolis = 0.5f; + float const friction = 0.001f; + float const friction_factor = std::exp(- friction * dt); + + // Temperature source + // temperature_(16, 16) += 10.f * dt; + + // Boundary values + for (int y = 0; y < N; ++y) + { + // velocity_(1, y) = {(y*1.f/N)-0.5f, 0.f}; + // velocity_(N-2, y) = {(y*1.f/N)-0.5f, 0.f}; + + // if (y < N/2) + // { + // velocity_(1, y) = {-1.f, 0.f}; + // velocity_(N-2, y) = {-1.f, 0.f}; + // temperature_(N-1, y) = -1.f; + // } + // else + // { + // velocity_(1, y) = {1.f, 0.f}; + // velocity_(N-2, y) = {1.f, 0.f}; + + // temperature_(0, y) = 1.f; + // } + } + + // Velocity & temperature advection + for (int y = 1; y < N - 1; ++y) + { + for (int x = 1; x < N - 1; ++x) + { + auto v = velocity_(x, y); + auto p = math::point{x + 0.5f, y + 0.5f} - dt * v; + p[0] = math::clamp(p[0] - 0.5f, {0.f, N - 1.f}); + p[1] = math::clamp(p[1] - 0.5f, {0.f, N - 1.f}); + + int ix = std::min(N - 1, std::floor(p[0])); + int iy = std::min(N - 1, std::floor(p[1])); + + float tx = p[0] - ix; + float ty = p[1] - iy; + + new_velocity_(x, y) = math::lerp( + math::lerp(velocity_(ix + 0, iy + 0), velocity_(ix + 1, iy + 0), tx), + math::lerp(velocity_(ix + 0, iy + 1), velocity_(ix + 1, iy + 1), tx), + ty + ); + + new_temperature_(x, y) = math::lerp( + math::lerp(temperature_(ix + 0, iy + 0), temperature_(ix + 1, iy + 0), tx), + math::lerp(temperature_(ix + 0, iy + 1), temperature_(ix + 1, iy + 1), tx), + ty + ); + } + } + std::swap(velocity_, new_velocity_); + std::swap(temperature_, new_temperature_); + + // Apply velocity diffusion & friction + for (int y = 0; y < N; ++y) + for (int x = 0; x < N; ++x) + new_velocity_(x, y) = velocity_(x, y); + for (int y = 1; y < N - 1; ++y) + { + for (int x = 1; x < N - 1; ++x) + { + // Velocity Laplacian + auto laplacian = velocity_(x + 1, y) + velocity_(x - 1, y) + velocity_(x, y + 1) + velocity_(x, y - 1) - 4.f * velocity_(x, y); + new_velocity_(x, y) = velocity_(x, y) + viscosity * dt * laplacian; + new_velocity_(x, y) *= friction_factor; + } + } + std::swap(velocity_, new_velocity_); + + // Apply temperature diffusion + for (int y = 0; y < N; ++y) + for (int x = 0; x < N; ++x) + new_temperature_(x, y) = temperature_(x, y); + for (int y = 1; y < N - 1; ++y) + { + for (int x = 1; x < N - 1; ++x) + { + // Velocity Laplacian + auto laplacian = temperature_(x + 1, y) + temperature_(x - 1, y) + temperature_(x, y + 1) + temperature_(x, y - 1) - 4.f * temperature_(x, y); + new_temperature_(x, y) = temperature_(x, y) + temperature_diffusion * dt * laplacian; + } + } + std::swap(temperature_, new_temperature_); + + // Apply forces + for (int y = 1; y < N - 1; ++y) + { + for (int x = 1; x < N - 1; ++x) + { + float latitude = (N * 0.5f - y) * 2.f / N; + // float latitude = (N - y) * 1.f / N; + velocity_(x, y) += math::ort(velocity_(x, y)) * (coriolis * dt * std::sin(0.5f * float(math::pi) * latitude * 2.f)); + } + } + + // Solve Poisson equation for pressure + for (int iteration = 0; iteration < 16; ++iteration) + { + for (int y = 1; y < N - 1; ++y) + { + for (int x = 1; x < N - 1; ++x) + { + // Velocity divergence + float divergence = (velocity_(x + 1, y)[0] - velocity_(x - 1, y)[0] + velocity_(x, y + 1)[1] - velocity_(x, y - 1)[1]) / 2.f; + + // Gauss-Seidel iteration step + pressure_(x, y) = (pressure_(x - 1, y) + pressure_(x + 1, y) + pressure_(x, y - 1) + pressure_(x, y + 1) - divergence) / 4.f; + } + } + } + + // Apply boundary conditions for pressure + for (int i = 1; i < N - 1; ++i) + { + pressure_(0, i) = pressure_(1, i); + pressure_(N - 1, i) = pressure_(N - 2, i); + pressure_(i, 0) = pressure_(i, 1); + pressure_(i, N - 1) = pressure_(i, N - 2); + } + pressure_(0, 0) = (pressure_(0, 1) + pressure_(1, 0)) / 2.f; + pressure_(N-1, 0) = (pressure_(N-1, 1) + pressure_(N-2, 0)) / 2.f; + pressure_(0, N-1) = (pressure_(0, N-2) + pressure_(1, N-2)) / 2.f; + pressure_(N-1, N-1) = (pressure_(N-1, N-2) + pressure_(N-2, N-1)) / 2.f; + + // Normalize pressure + float average_pressure = 0.f; + for (auto const & value : pressure_) + average_pressure += value; + average_pressure /= (1.f * N * N); + for (auto & value : pressure_) + value -= average_pressure; + + // Project velocity into divergence-free space + // by subtracting pressure gradient + for (int y = 1; y < N - 1; ++y) + { + for (int x = 1; x < N - 1; ++x) + { + // Pressure gradient + math::vector gradient{pressure_(x + 1, y) - pressure_(x - 1, y), pressure_(x, y + 1) - pressure_(x, y - 1)}; + + velocity_(x, y) -= gradient; + } + } + + // Apply boundary conditions for velocity + for (int i = 1; i < N - 1; ++i) + { + velocity_(0, i)[0] = -velocity_(1, i)[0]; + velocity_(N-1, i)[0] = -velocity_(N-2, i)[0]; + velocity_(i, 0)[1] = -velocity_(i, 1)[1]; + velocity_(i, N-2)[1] = -velocity_(i, N-2)[1]; + } + } + + void present() override + { + gl::ClearColor(0.f, 0.f, 0.f, 0.f); + gl::Clear(gl::COLOR_BUFFER_BIT); + + float const aspect_ratio = state().size[0] * 1.f / state().size[1]; + math::box view_box = math::expand(simulation_box_, 1.f); + if (view_box[0].length() / view_box[1].length() > aspect_ratio) + view_box[1] = math::expand(view_box[1], (view_box[0].length() / aspect_ratio - view_box[1].length()) / 2.f); + else + view_box[0] = math::expand(view_box[0], (view_box[1].length() * aspect_ratio - view_box[0].length()) / 2.f); + + std::optional> mouseover_cell; + { + auto mouse = math::lerp(view_box, math::vector{state().mouse[0] * 1.f / state().size[0], 1.f - state().mouse[1] * 1.f / state().size[1]}); + int x = std::floor(mouse[0]); + int y = std::floor(mouse[1]); + + if (x >= 0 && x < N && y >= 0 && y < N) + mouseover_cell = {x, y}; + } + + [[maybe_unused]] float const pixel_size = view_box[0].length() / state().size[0]; + + for (int y = 0; y < N; ++y) + { + for (int x = 0; x < N; ++x) + { + math::point center{x + 0.5f, y + 0.5f}; + + auto v = velocity_(x, y); + if (auto l = math::length(v); l > 0.f) + { + float const magnification = 40.f; + float const max_length = 2.f; + v *= 0.5f * max_length * (1.f - std::exp(- magnification * l)) / l; + } + auto n = math::ort(v) * 0.25f; + + float value = 1000.f * pressure_(x, y); + // float value = 4.f * temperature_(x, y); + + gfx::color_4f color; + if (value > 0.f) + { + value = 1.f - std::exp(- value); + color = {value, value * 0.5f, value * 0.125f, 1.f}; + } + else + { + value = 1.f - std::exp(value); + color = {value * 0.125f, value * 0.5f, value, 1.f}; + } + + painter_.rect({{{x, x + 1.f}, {y, y + 1.f}}}, gfx::to_coloru8(color)); + + painter_.triangle(center - v - n, center - v + n, center + v, {255, 255, 255, 255}); + // painter_.line(center - v, center + v, 1.f * pixel_size, {255, 255, 255, 255}, false); + } + } + + auto push_text = [&, row = 0](std::string const & text) mutable + { + painter_.text(view_box.corner(0.f, 1.f) - math::vector{0.f, row * pixel_size * 2.f * 12.f}, text, {.scale = {2.f * pixel_size, - 2.f * pixel_size}, .x = gfx::painter::x_align::left, .y = gfx::painter::y_align::top, .c = {255, 255, 255, 255}}); + ++row; + }; + + if (mouseover_cell) + { + int x = (*mouseover_cell)[0]; + int y = (*mouseover_cell)[1]; + painter_.rect({{{x, x + 1.f}, {y, y + 1.f}}}, {255, 255, 255, 127}); + + push_text(std::format("{} {}", x, y)); + push_text(std::format("{} {}", velocity_(x, y)[0], velocity_(x, y)[1])); + } + + painter_.render(math::orthographic_camera{view_box}.transform()); + } + +private: + gfx::painter painter_; + + math::box simulation_box_; + + bool paused_ = true; + + util::ndarray obstacle_; + util::ndarray, 2> velocity_; + util::ndarray, 2> new_velocity_; + util::ndarray pressure_; + util::ndarray temperature_; + util::ndarray new_temperature_; +}; + +namespace psemek::app +{ + + std::unique_ptr make_application_factory() + { + return default_application_factory({.name = "Weather simulation test"}); + } + +} From 253eda65cecdc75686538519cab63d4b83eaab7a Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sat, 6 Dec 2025 14:07:19 +0300 Subject: [PATCH 117/141] Weather simulation test: periodic X-boundary --- examples/weather.cpp | 181 +++++++++++++++++++++++-------------------- 1 file changed, 98 insertions(+), 83 deletions(-) diff --git a/examples/weather.cpp b/examples/weather.cpp index b4c0824e..53aa2078 100644 --- a/examples/weather.cpp +++ b/examples/weather.cpp @@ -39,7 +39,7 @@ struct weather_app for (int x = 0; x < N; ++x) { // velocity_(x, y)[0] += (y*1.f/N-0.5f); - temperature_(x, y) = (x < N / 2) ? -1.f : 1.f; + temperature_(x, y) = 0.5f - std::abs(y*2.f/N - 1.f); } } } @@ -47,7 +47,16 @@ struct weather_app void on_event(app::key_event const & event) override { if (event.down && event.key == app::keycode::SPACE) - paused_ = !paused_; + paused_ ^= true; + + if (event.down && event.key == app::keycode::V) + show_velocity_ ^= true; + + if (event.down && event.key == app::keycode::T) + show_temperature_ ^= true; + + if (event.down && event.key == app::keycode::P) + show_pressure_ ^= true; } void update() override @@ -55,62 +64,54 @@ struct weather_app if (paused_) return; - float const dt = 0.1f; - float const viscosity = 0.01f; + float const dt = 0.2f; + float const viscosity = 0.f; float const temperature_diffusion = 0.01f; - float const coriolis = 0.5f; - float const friction = 0.001f; - float const friction_factor = std::exp(- friction * dt); + float const heating = 0.001f; + float const coriolis = 0.1f; + float const coriolis_bands = 5.f; + float const friction = 0.1f; + float const friction_factor = 1.f - std::exp(- friction * dt); // Temperature source - // temperature_(16, 16) += 10.f * dt; - - // Boundary values for (int y = 0; y < N; ++y) { - // velocity_(1, y) = {(y*1.f/N)-0.5f, 0.f}; - // velocity_(N-2, y) = {(y*1.f/N)-0.5f, 0.f}; - - // if (y < N/2) - // { - // velocity_(1, y) = {-1.f, 0.f}; - // velocity_(N-2, y) = {-1.f, 0.f}; - // temperature_(N-1, y) = -1.f; - // } - // else - // { - // velocity_(1, y) = {1.f, 0.f}; - // velocity_(N-2, y) = {1.f, 0.f}; - - // temperature_(0, y) = 1.f; - // } + for (int x = 0; x < N; ++x) + { + temperature_(x, y) += dt * heating * (0.5f - std::abs(y*2.f/N - 1.f)); + } } + auto wrap = [](int i) + { + return (i + N) % N; + }; + // Velocity & temperature advection for (int y = 1; y < N - 1; ++y) { - for (int x = 1; x < N - 1; ++x) + for (int x = 0; x < N; ++x) { auto v = velocity_(x, y); auto p = math::point{x + 0.5f, y + 0.5f} - dt * v; p[0] = math::clamp(p[0] - 0.5f, {0.f, N - 1.f}); p[1] = math::clamp(p[1] - 0.5f, {0.f, N - 1.f}); - int ix = std::min(N - 1, std::floor(p[0])); + int ix = std::floor(p[0]); int iy = std::min(N - 1, std::floor(p[1])); float tx = p[0] - ix; float ty = p[1] - iy; new_velocity_(x, y) = math::lerp( - math::lerp(velocity_(ix + 0, iy + 0), velocity_(ix + 1, iy + 0), tx), - math::lerp(velocity_(ix + 0, iy + 1), velocity_(ix + 1, iy + 1), tx), + math::lerp(velocity_(wrap(ix + 0), iy + 0), velocity_(wrap(ix + 1), iy + 0), tx), + math::lerp(velocity_(wrap(ix + 0), iy + 1), velocity_(wrap(ix + 1), iy + 1), tx), ty ); new_temperature_(x, y) = math::lerp( - math::lerp(temperature_(ix + 0, iy + 0), temperature_(ix + 1, iy + 0), tx), - math::lerp(temperature_(ix + 0, iy + 1), temperature_(ix + 1, iy + 1), tx), + math::lerp(temperature_(wrap(ix + 0), iy + 0), temperature_(wrap(ix + 1), iy + 0), tx), + math::lerp(temperature_(wrap(ix + 0), iy + 1), temperature_(wrap(ix + 1), iy + 1), tx), ty ); } @@ -118,18 +119,17 @@ struct weather_app std::swap(velocity_, new_velocity_); std::swap(temperature_, new_temperature_); - // Apply velocity diffusion & friction + // Apply velocity diffusion for (int y = 0; y < N; ++y) for (int x = 0; x < N; ++x) new_velocity_(x, y) = velocity_(x, y); for (int y = 1; y < N - 1; ++y) { - for (int x = 1; x < N - 1; ++x) + for (int x = 0; x < N; ++x) { // Velocity Laplacian - auto laplacian = velocity_(x + 1, y) + velocity_(x - 1, y) + velocity_(x, y + 1) + velocity_(x, y - 1) - 4.f * velocity_(x, y); + auto laplacian = velocity_(wrap(x + 1), y) + velocity_(wrap(x - 1), y) + velocity_(x, y + 1) + velocity_(x, y - 1) - 4.f * velocity_(x, y); new_velocity_(x, y) = velocity_(x, y) + viscosity * dt * laplacian; - new_velocity_(x, y) *= friction_factor; } } std::swap(velocity_, new_velocity_); @@ -140,23 +140,27 @@ struct weather_app new_temperature_(x, y) = temperature_(x, y); for (int y = 1; y < N - 1; ++y) { - for (int x = 1; x < N - 1; ++x) + for (int x = 0; x < N; ++x) { - // Velocity Laplacian - auto laplacian = temperature_(x + 1, y) + temperature_(x - 1, y) + temperature_(x, y + 1) + temperature_(x, y - 1) - 4.f * temperature_(x, y); + // Temperature Laplacian + auto laplacian = temperature_(wrap(x + 1), y) + temperature_(wrap(x - 1), y) + temperature_(x, y + 1) + temperature_(x, y - 1) - 4.f * temperature_(x, y); new_temperature_(x, y) = temperature_(x, y) + temperature_diffusion * dt * laplacian; } } std::swap(temperature_, new_temperature_); - // Apply forces + // Apply forces & friction for (int y = 1; y < N - 1; ++y) { - for (int x = 1; x < N - 1; ++x) + for (int x = 0; x < N; ++x) { float latitude = (N * 0.5f - y) * 2.f / N; // float latitude = (N - y) * 1.f / N; - velocity_(x, y) += math::ort(velocity_(x, y)) * (coriolis * dt * std::sin(0.5f * float(math::pi) * latitude * 2.f)); + velocity_(x, y) += math::ort(velocity_(x, y)) * (coriolis * dt * std::sin(0.5f * float(math::pi) * latitude * coriolis_bands)); + + bool is_land = math::length(math::vector{x - N / 2.f, y - N / 4.f}) < N / 8.f; + if (is_land && false) + velocity_(x, y) -= friction_factor * velocity_(x, y);// * math::length(velocity_(x, y)); } } @@ -165,29 +169,29 @@ struct weather_app { for (int y = 1; y < N - 1; ++y) { - for (int x = 1; x < N - 1; ++x) + for (int x = 0; x < N; ++x) { // Velocity divergence - float divergence = (velocity_(x + 1, y)[0] - velocity_(x - 1, y)[0] + velocity_(x, y + 1)[1] - velocity_(x, y - 1)[1]) / 2.f; + float divergence = (velocity_(wrap(x + 1), y)[0] - velocity_(wrap(x - 1), y)[0] + velocity_(x, y + 1)[1] - velocity_(x, y - 1)[1]) / 2.f; // Gauss-Seidel iteration step - pressure_(x, y) = (pressure_(x - 1, y) + pressure_(x + 1, y) + pressure_(x, y - 1) + pressure_(x, y + 1) - divergence) / 4.f; + pressure_(x, y) = (pressure_(wrap(x - 1), y) + pressure_(wrap(x + 1), y) + pressure_(x, y - 1) + pressure_(x, y + 1) - divergence) / 4.f; } } } // Apply boundary conditions for pressure - for (int i = 1; i < N - 1; ++i) + for (int i = 0; i < N; ++i) { - pressure_(0, i) = pressure_(1, i); - pressure_(N - 1, i) = pressure_(N - 2, i); + // pressure_(0, i) = pressure_(1, i); + // pressure_(N - 1, i) = pressure_(N - 2, i); pressure_(i, 0) = pressure_(i, 1); pressure_(i, N - 1) = pressure_(i, N - 2); } - pressure_(0, 0) = (pressure_(0, 1) + pressure_(1, 0)) / 2.f; - pressure_(N-1, 0) = (pressure_(N-1, 1) + pressure_(N-2, 0)) / 2.f; - pressure_(0, N-1) = (pressure_(0, N-2) + pressure_(1, N-2)) / 2.f; - pressure_(N-1, N-1) = (pressure_(N-1, N-2) + pressure_(N-2, N-1)) / 2.f; + // pressure_(0, 0) = (pressure_(0, 1) + pressure_(1, 0)) / 2.f; + // pressure_(N-1, 0) = (pressure_(N-1, 1) + pressure_(N-2, 0)) / 2.f; + // pressure_(0, N-1) = (pressure_(0, N-2) + pressure_(1, N-2)) / 2.f; + // pressure_(N-1, N-1) = (pressure_(N-1, N-2) + pressure_(N-2, N-1)) / 2.f; // Normalize pressure float average_pressure = 0.f; @@ -201,20 +205,19 @@ struct weather_app // by subtracting pressure gradient for (int y = 1; y < N - 1; ++y) { - for (int x = 1; x < N - 1; ++x) + for (int x = 0; x < N; ++x) { // Pressure gradient - math::vector gradient{pressure_(x + 1, y) - pressure_(x - 1, y), pressure_(x, y + 1) - pressure_(x, y - 1)}; - + math::vector gradient{pressure_(wrap(x + 1), y) - pressure_(wrap(x - 1), y), pressure_(x, y + 1) - pressure_(x, y - 1)}; velocity_(x, y) -= gradient; } } // Apply boundary conditions for velocity - for (int i = 1; i < N - 1; ++i) + for (int i = 0; i < N; ++i) { - velocity_(0, i)[0] = -velocity_(1, i)[0]; - velocity_(N-1, i)[0] = -velocity_(N-2, i)[0]; + // velocity_(0, i)[0] = -velocity_(1, i)[0]; + // velocity_(N-1, i)[0] = -velocity_(N-2, i)[0]; velocity_(i, 0)[1] = -velocity_(i, 1)[1]; velocity_(i, N-2)[1] = -velocity_(i, N-2)[1]; } @@ -248,36 +251,43 @@ struct weather_app { for (int x = 0; x < N; ++x) { - math::point center{x + 0.5f, y + 0.5f}; - - auto v = velocity_(x, y); - if (auto l = math::length(v); l > 0.f) + if (show_temperature_ || show_pressure_) { - float const magnification = 40.f; - float const max_length = 2.f; - v *= 0.5f * max_length * (1.f - std::exp(- magnification * l)) / l; - } - auto n = math::ort(v) * 0.25f; + float value = 0.f; + if (show_temperature_) + value = 2.f * temperature_(x, y); + else if (show_pressure_) + value = 1000.f * pressure_(x, y); - float value = 1000.f * pressure_(x, y); - // float value = 4.f * temperature_(x, y); + gfx::color_4f color; + if (value > 0.f) + { + value = 1.f - std::exp(- value); + color = {value, value * 0.5f, value * 0.125f, 1.f}; + } + else + { + value = 1.f - std::exp(value); + color = {value * 0.125f, value * 0.5f, value, 1.f}; + } - gfx::color_4f color; - if (value > 0.f) - { - value = 1.f - std::exp(- value); - color = {value, value * 0.5f, value * 0.125f, 1.f}; - } - else - { - value = 1.f - std::exp(value); - color = {value * 0.125f, value * 0.5f, value, 1.f}; + painter_.rect({{{x, x + 1.f}, {y, y + 1.f}}}, gfx::to_coloru8(color)); } - painter_.rect({{{x, x + 1.f}, {y, y + 1.f}}}, gfx::to_coloru8(color)); + if (show_velocity_) + { + math::point center{x + 0.5f, y + 0.5f}; + auto v = velocity_(x, y); + if (auto l = math::length(v); l > 0.f) + { + float const magnification = 40.f; + float const max_length = 1.5f; + v *= 0.5f * max_length * (1.f - std::exp(- magnification * l)) / l; + } + auto n = math::ort(v) * 0.3f; - painter_.triangle(center - v - n, center - v + n, center + v, {255, 255, 255, 255}); - // painter_.line(center - v, center + v, 1.f * pixel_size, {255, 255, 255, 255}, false); + painter_.triangle(center - v - n, center - v + n, center + v, {255, 255, 255, 255}); + } } } @@ -294,7 +304,9 @@ struct weather_app painter_.rect({{{x, x + 1.f}, {y, y + 1.f}}}, {255, 255, 255, 127}); push_text(std::format("{} {}", x, y)); - push_text(std::format("{} {}", velocity_(x, y)[0], velocity_(x, y)[1])); + push_text(std::format("V = {} {}", velocity_(x, y)[0], velocity_(x, y)[1])); + push_text(std::format("P = {}", pressure_(x, y))); + push_text(std::format("T = {}", temperature_(x, y))); } painter_.render(math::orthographic_camera{view_box}.transform()); @@ -306,6 +318,9 @@ private: math::box simulation_box_; bool paused_ = true; + bool show_velocity_ = true; + bool show_temperature_ = true; + bool show_pressure_ = false; util::ndarray obstacle_; util::ndarray, 2> velocity_; From 9e4a96f622d6fa5f624e2ad5492d6fa934f3e847 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 7 Dec 2025 13:31:15 +0300 Subject: [PATCH 118/141] Weather simulation test wip --- examples/weather.cpp | 244 ++++++++++++++++++++++++++++++++----------- 1 file changed, 185 insertions(+), 59 deletions(-) diff --git a/examples/weather.cpp b/examples/weather.cpp index 53aa2078..1ee6cc01 100644 --- a/examples/weather.cpp +++ b/examples/weather.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include @@ -16,30 +18,98 @@ struct weather_app { static constexpr int N = 128; + const float dt = 0.2f; + const float viscosity = 0.f; + const float temperature_diffusion = 0.001f; + const float cooling = 1.f / 300.f; + const float cooling_factor = std::exp(- cooling * dt); + const float heating = 323.f * (std::exp(cooling * dt) - 1.f) / dt; + const float coriolis = 0.f; + const float coriolis_bands = 2.f; + const float friction = 0.f; + // const float friction_factor = 1.f - std::exp(- friction * dt); + + const bool periodic_x = false; + + float expected_temperature_at(int y) + { + // float latitude = (y - N * 0.5f) * 2.f / N; + // return std::cos(latitude * float(math::pi)); + + return temperature_income_at(y) * dt / (std::exp(cooling * dt) - 1.f); + } + + float temperature_income_at(int y) + { + float latitude = (y - N * 0.5f) * 2.f / N; + // return heating * math::lerp(0.75f, 1.f, std::cos(latitude * float(math::pi) / 2.f)); + return heating * math::lerp(0.6f, 1.f, 1.f - std::abs(latitude)); + } + weather_app(options const &, context const &) { simulation_box_ = {{{0.f, N}, {0.f, N}}}; - obstacle_.resize({N, N}, 0.f); + terrain_.resize({N, N}, 0.f); velocity_.resize({N, N}); new_velocity_.resize({N, N}); pressure_.resize({N, N}, 0.f); temperature_.resize({N, N}, -1.f); new_temperature_.resize({N, N}); + average_temperature_.resize({N, N}, 0.f); random::generator rng{random::device{}}; // random::generator rng{0, 0}; random::uniform_ball_vector_distribution random_velocity{}; - for (auto & v : velocity_) - v = random_velocity(rng); + // for (auto & v : velocity_) + // { + // v = random_velocity(rng) * 0.01f; + // v += std::cos(0.5f * float(math::pi) * latitude * coriolis_bands + // } for (int y = 0; y < N; ++y) { for (int x = 0; x < N; ++x) { - // velocity_(x, y)[0] += (y*1.f/N-0.5f); - temperature_(x, y) = 0.5f - std::abs(y*2.f/N - 1.f); + float latitude = (N * 0.5f - y) * 2.f / N; + velocity_(x, y) = random_velocity(rng) * 1.1f + 0.001f * math::vector{-std::cos(0.5f * float(math::pi) * latitude * coriolis_bands), 0.f}; + temperature_(x, y) = expected_temperature_at(y); + } + } + + std::vector> octaves; + std::vector weights; + + random::uniform_sphere_vector_distribution random_vector{}; + + for (int octave = 0; octave < 8; ++octave) + { + int size = 4 << octave; + util::ndarray, 2> gradients({size + 1, size + 1}); + for (auto & g : gradients) + g = random_vector(rng); + octaves.emplace_back(std::move(gradients)); + weights.push_back(std::pow(2.f, - 0.75f * octave)); + } + + float weight_sum = 0.f; + for (auto w : weights) + weight_sum += w; + for (auto & w : weights) + w /= weight_sum; + + pcg::fractal> noise(std::move(octaves), std::move(weights)); + + for (int y = 0; y < N; ++y) + { + for (int x = 0; x < N; ++x) + { + auto d = math::length(math::vector{x - N / 2.f, y - N / 2.f}) / (N / 2.f); + (void)d; + float value = noise((x + 0.5f) / N, (y + 0.5f) / N); + value = pow(value, 4.f) - d / 4.f; + terrain_(x, y) = std::max(0.f, math::lerp(1.f, 16.f, value)); } } } @@ -55,8 +125,17 @@ struct weather_app if (event.down && event.key == app::keycode::T) show_temperature_ ^= true; + if (event.down && event.key == app::keycode::D) + show_temperature_delta_ ^= true; + + if (event.down && event.key == app::keycode::A) + show_average_temperature_delta_ ^= true; + if (event.down && event.key == app::keycode::P) show_pressure_ ^= true; + + if (event.down && event.key == app::keycode::H) + show_land_ ^= true; } void update() override @@ -64,21 +143,14 @@ struct weather_app if (paused_) return; - float const dt = 0.2f; - float const viscosity = 0.f; - float const temperature_diffusion = 0.01f; - float const heating = 0.001f; - float const coriolis = 0.1f; - float const coriolis_bands = 5.f; - float const friction = 0.1f; - float const friction_factor = 1.f - std::exp(- friction * dt); - // Temperature source for (int y = 0; y < N; ++y) { for (int x = 0; x < N; ++x) { - temperature_(x, y) += dt * heating * (0.5f - std::abs(y*2.f/N - 1.f)); + // temperature_(x, y) = math::lerp(temperature_(x, y), expected_temperature_at(y), 1.f - std::exp(- heating * dt)); + temperature_(x, y) += dt * temperature_income_at(y); + temperature_(x, y) *= cooling_factor; } } @@ -87,19 +159,39 @@ struct weather_app return (i + N) % N; }; + int xmin = periodic_x ? 0 : 1; + int xmax = periodic_x ? N : N - 1; // exclusive + // Velocity & temperature advection + for (int i = 0; i < N; ++i) + { + new_temperature_(i, 0) = temperature_(i, 0); + new_temperature_(i, N - 1) = temperature_(i, N - 1); + + if (!periodic_x) + { + new_temperature_(0, i) = temperature_(0, i); + new_temperature_(N - 1, i) = temperature_(N - 1, i); + } + } for (int y = 1; y < N - 1; ++y) { - for (int x = 0; x < N; ++x) + for (int x = xmin; x < xmax; ++x) { auto v = velocity_(x, y); auto p = math::point{x + 0.5f, y + 0.5f} - dt * v; - p[0] = math::clamp(p[0] - 0.5f, {0.f, N - 1.f}); + p[0] = p[0] - 0.5f; p[1] = math::clamp(p[1] - 0.5f, {0.f, N - 1.f}); + if (!periodic_x) + p[0] = math::clamp(p[0], {0.f, N - 1.f}); + int ix = std::floor(p[0]); int iy = std::min(N - 1, std::floor(p[1])); + if (!periodic_x) + ix = std::min(N - 1, ix); + float tx = p[0] - ix; float ty = p[1] - iy; @@ -125,7 +217,7 @@ struct weather_app new_velocity_(x, y) = velocity_(x, y); for (int y = 1; y < N - 1; ++y) { - for (int x = 0; x < N; ++x) + for (int x = xmin; x < xmax; ++x) { // Velocity Laplacian auto laplacian = velocity_(wrap(x + 1), y) + velocity_(wrap(x - 1), y) + velocity_(x, y + 1) + velocity_(x, y - 1) - 4.f * velocity_(x, y); @@ -140,7 +232,7 @@ struct weather_app new_temperature_(x, y) = temperature_(x, y); for (int y = 1; y < N - 1; ++y) { - for (int x = 0; x < N; ++x) + for (int x = xmin; x < xmax; ++x) { // Temperature Laplacian auto laplacian = temperature_(wrap(x + 1), y) + temperature_(wrap(x - 1), y) + temperature_(x, y + 1) + temperature_(x, y - 1) - 4.f * temperature_(x, y); @@ -152,15 +244,17 @@ struct weather_app // Apply forces & friction for (int y = 1; y < N - 1; ++y) { - for (int x = 0; x < N; ++x) + for (int x = xmin; x < xmax; ++x) { float latitude = (N * 0.5f - y) * 2.f / N; // float latitude = (N - y) * 1.f / N; - velocity_(x, y) += math::ort(velocity_(x, y)) * (coriolis * dt * std::sin(0.5f * float(math::pi) * latitude * coriolis_bands)); + // velocity_(x, y) += math::ort(velocity_(x, y)) * (coriolis * dt * std::sin(0.5f * float(math::pi) * latitude * coriolis_bands)); + velocity_(x, y) = math::rotate(velocity_(x, y), coriolis * dt * std::sin(0.5f * float(math::pi) * latitude * coriolis_bands)); - bool is_land = math::length(math::vector{x - N / 2.f, y - N / 4.f}) < N / 8.f; - if (is_land && false) - velocity_(x, y) -= friction_factor * velocity_(x, y);// * math::length(velocity_(x, y)); + float local_friction = friction * terrain_(x, y); + // velocity_(x, y) -= local_friction * velocity_(x, y) * math::length(velocity_(x, y)); + float local_friction_factor = std::exp(- local_friction * dt); + velocity_(x, y) *= local_friction_factor; } } @@ -169,7 +263,7 @@ struct weather_app { for (int y = 1; y < N - 1; ++y) { - for (int x = 0; x < N; ++x) + for (int x = xmin; x < xmax; ++x) { // Velocity divergence float divergence = (velocity_(wrap(x + 1), y)[0] - velocity_(wrap(x - 1), y)[0] + velocity_(x, y + 1)[1] - velocity_(x, y - 1)[1]) / 2.f; @@ -183,15 +277,21 @@ struct weather_app // Apply boundary conditions for pressure for (int i = 0; i < N; ++i) { - // pressure_(0, i) = pressure_(1, i); - // pressure_(N - 1, i) = pressure_(N - 2, i); + if (!periodic_x) + { + pressure_(0, i) = pressure_(1, i); + pressure_(N - 1, i) = pressure_(N - 2, i); + } pressure_(i, 0) = pressure_(i, 1); pressure_(i, N - 1) = pressure_(i, N - 2); } - // pressure_(0, 0) = (pressure_(0, 1) + pressure_(1, 0)) / 2.f; - // pressure_(N-1, 0) = (pressure_(N-1, 1) + pressure_(N-2, 0)) / 2.f; - // pressure_(0, N-1) = (pressure_(0, N-2) + pressure_(1, N-2)) / 2.f; - // pressure_(N-1, N-1) = (pressure_(N-1, N-2) + pressure_(N-2, N-1)) / 2.f; + if (!periodic_x) + { + pressure_(0, 0) = (pressure_(0, 1) + pressure_(1, 0)) / 2.f; + pressure_(N-1, 0) = (pressure_(N-1, 1) + pressure_(N-2, 0)) / 2.f; + pressure_(0, N-1) = (pressure_(0, N-2) + pressure_(1, N-2)) / 2.f; + pressure_(N-1, N-1) = (pressure_(N-1, N-2) + pressure_(N-2, N-1)) / 2.f; + } // Normalize pressure float average_pressure = 0.f; @@ -205,7 +305,7 @@ struct weather_app // by subtracting pressure gradient for (int y = 1; y < N - 1; ++y) { - for (int x = 0; x < N; ++x) + for (int x = xmin; x < xmax; ++x) { // Pressure gradient math::vector gradient{pressure_(wrap(x + 1), y) - pressure_(wrap(x - 1), y), pressure_(x, y + 1) - pressure_(x, y - 1)}; @@ -216,11 +316,25 @@ struct weather_app // Apply boundary conditions for velocity for (int i = 0; i < N; ++i) { - // velocity_(0, i)[0] = -velocity_(1, i)[0]; - // velocity_(N-1, i)[0] = -velocity_(N-2, i)[0]; + if (!periodic_x) + { + velocity_(0, i)[0] = -velocity_(1, i)[0]; + velocity_(N-1, i)[0] = -velocity_(N-2, i)[0]; + } velocity_(i, 0)[1] = -velocity_(i, 1)[1]; velocity_(i, N-2)[1] = -velocity_(i, N-2)[1]; } + + ++frame_; + + // Update all-time average temperature + for (int y = 0; y < N; ++y) + { + for (int x = 0; x < N; ++x) + { + average_temperature_(x, y) = math::lerp(average_temperature_(x, y), temperature_(x, y), 1.f / frame_); + } + } } void present() override @@ -251,28 +365,28 @@ struct weather_app { for (int x = 0; x < N; ++x) { - if (show_temperature_ || show_pressure_) - { - float value = 0.f; - if (show_temperature_) - value = 2.f * temperature_(x, y); - else if (show_pressure_) - value = 1000.f * pressure_(x, y); + gfx::color_4f color = gfx::color_4f::zero(); - gfx::color_4f color; - if (value > 0.f) - { - value = 1.f - std::exp(- value); - color = {value, value * 0.5f, value * 0.125f, 1.f}; - } - else - { - value = 1.f - std::exp(value); - color = {value * 0.125f, value * 0.5f, value, 1.f}; - } + auto map_color = [](float value, gfx::color_4f const & negative, gfx::color_4f const & positive){ + return math::lerp(negative, positive, 1.f/ (1.f + std::exp(- value))); + }; - painter_.rect({{{x, x + 1.f}, {y, y + 1.f}}}, gfx::to_coloru8(color)); - } + if (show_land_) + color = gfx::blend(color, map_color(terrain_(x, y), {-1.f, -1.f, -1.f, 1.f}, {1.f, 1.f, 1.f, 1.f})); + + if (show_temperature_) + color = gfx::blend(color, map_color(0.1f * (temperature_(x, y) - 273.f), {0.125f, 0.5f, 1.f, 0.75f}, {1.f, 0.5f, 0.125f, 0.75f})); + + if (show_temperature_delta_) + color = gfx::blend(color, map_color((temperature_(x, y) - expected_temperature_at(y)), {0.125f, 0.5f, 1.f, 0.75f}, {1.f, 0.5f, 0.125f, 0.75f})); + + if (show_average_temperature_delta_) + color = gfx::blend(color, map_color((average_temperature_(x, y) - expected_temperature_at(y)), {0.125f, 0.5f, 1.f, 0.75f}, {1.f, 0.5f, 0.125f, 0.75f})); + + if (show_pressure_) + color = gfx::blend(color, map_color(1000.f * pressure_(x, y), {0.f, 0.f, 1.f, 0.75f}, {1.f, 0.f, 0.f, 0.75f})); + + painter_.rect({{{x, x + 1.f}, {y, y + 1.f}}}, gfx::to_coloru8(color)); if (show_velocity_) { @@ -297,6 +411,9 @@ struct weather_app ++row; }; + push_text(std::format("Frame {}", frame_)); + push_text(std::format("Day {:.2f}", frame_ / (720.f / dt))); + if (mouseover_cell) { int x = (*mouseover_cell)[0]; @@ -304,9 +421,12 @@ struct weather_app painter_.rect({{{x, x + 1.f}, {y, y + 1.f}}}, {255, 255, 255, 127}); push_text(std::format("{} {}", x, y)); - push_text(std::format("V = {} {}", velocity_(x, y)[0], velocity_(x, y)[1])); - push_text(std::format("P = {}", pressure_(x, y))); - push_text(std::format("T = {}", temperature_(x, y))); + push_text(std::format("V = {:.3f} {:.3f}", velocity_(x, y)[0] * 1000.f, velocity_(x, y)[1] * 1000.f)); + push_text(std::format("P = {:.3f}", pressure_(x, y))); + push_text(std::format("T = {:.3f}", temperature_(x, y) - 273.f)); + push_text(std::format("A = {:.3f}", average_temperature_(x, y) - 273.f)); + push_text(std::format("E = {:.3f}", expected_temperature_at(y) - 273.f)); + push_text(std::format("H = {:.3f}", terrain_(x, y))); } painter_.render(math::orthographic_camera{view_box}.transform()); @@ -319,15 +439,21 @@ private: bool paused_ = true; bool show_velocity_ = true; - bool show_temperature_ = true; + bool show_temperature_ = false; + bool show_temperature_delta_ = false; + bool show_average_temperature_delta_ = false; bool show_pressure_ = false; + bool show_land_ = true; - util::ndarray obstacle_; + util::ndarray terrain_; util::ndarray, 2> velocity_; util::ndarray, 2> new_velocity_; util::ndarray pressure_; util::ndarray temperature_; util::ndarray new_temperature_; + util::ndarray average_temperature_; + + int frame_ = 0; }; namespace psemek::app From 69a2a04811a0d9073dfde26afdd85c3e52fee500 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 10 Dec 2025 17:32:29 +0300 Subject: [PATCH 119/141] Weather simulation force field experiments --- examples/weather.cpp | 212 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 168 insertions(+), 44 deletions(-) diff --git a/examples/weather.cpp b/examples/weather.cpp index 1ee6cc01..b26c487a 100644 --- a/examples/weather.cpp +++ b/examples/weather.cpp @@ -13,23 +13,73 @@ using namespace psemek; +auto make_perlin(random::generator & rng, int min_octave, int max_octave, float power) +{ + std::vector> octaves; + std::vector weights; + + random::uniform_sphere_vector_distribution random_vector{}; + + for (int octave = min_octave; octave < max_octave; ++octave) + { + int size = 1 << octave; + util::ndarray, 2> gradients({size + 1, size + 1}); + for (auto & g : gradients) + g = random_vector(rng); + octaves.emplace_back(std::move(gradients)); + weights.push_back(std::pow(power, - octave)); + } + + float weight_sum = 0.f; + for (auto w : weights) + weight_sum += w; + for (auto & w : weights) + w /= weight_sum; + + return pcg::fractal>(std::move(octaves), std::move(weights)); +} + +void make_force_field(random::generator & rng, util::ndarray, 2> & result, float scale) +{ + auto noise_1 = make_perlin(rng, 2, 6, 2.f); + auto noise_2 = make_perlin(rng, 2, 6, 2.f); + + for (int y = 0; y < result.height(); ++y) + { + for (int x = 0; x < result.width(); ++x) + { + math::point p{(x + 0.5f) / result.height(), (y + 0.5f) / result.width()}; + result(x, y) = scale * (math::vector{noise_1(p), noise_2(p)} * 2.f - math::vector{1.f, 1.f}); + } + } +} + struct weather_app : app::application_base { static constexpr int N = 128; - const float dt = 0.2f; + const float dt = 4.f; const float viscosity = 0.f; + const float advection_magnification = 1.f; const float temperature_diffusion = 0.001f; - const float cooling = 1.f / 300.f; + const float cooling = 0.01f / 300.f; const float cooling_factor = std::exp(- cooling * dt); const float heating = 323.f * (std::exp(cooling * dt) - 1.f) / dt; - const float coriolis = 0.f; + const float coriolis = 0.01f; const float coriolis_bands = 2.f; const float friction = 0.f; + const float slope_force = 0.04f; + const float vorticity_confinement = 0.f; + const float force_field_amplitude = 0.00005f; + const float force_field_switch_duration = 720.f * 7.5f; // 7.5 days + const int force_field_switch_frames = std::round(force_field_switch_duration / dt); // const float friction_factor = 1.f - std::exp(- friction * dt); - const bool periodic_x = false; + const bool periodic_x = true; + + random::generator rng{random::device{}}; + // random::generator rng{0, 0}; float expected_temperature_at(int y) { @@ -43,7 +93,7 @@ struct weather_app { float latitude = (y - N * 0.5f) * 2.f / N; // return heating * math::lerp(0.75f, 1.f, std::cos(latitude * float(math::pi) / 2.f)); - return heating * math::lerp(0.6f, 1.f, 1.f - std::abs(latitude)); + return heating * math::lerp(0.65f, 1.f, 1.f - std::abs(latitude)); } weather_app(options const &, context const &) @@ -57,8 +107,11 @@ struct weather_app temperature_.resize({N, N}, -1.f); new_temperature_.resize({N, N}); average_temperature_.resize({N, N}, 0.f); + force_field_main_.resize({N, N}); + force_field_current_.resize({N, N}); + force_field_next_.resize({N, N}); + vorticity_.resize({N, N}); - random::generator rng{random::device{}}; // random::generator rng{0, 0}; random::uniform_ball_vector_distribution random_velocity{}; @@ -73,33 +126,12 @@ struct weather_app for (int x = 0; x < N; ++x) { float latitude = (N * 0.5f - y) * 2.f / N; - velocity_(x, y) = random_velocity(rng) * 1.1f + 0.001f * math::vector{-std::cos(0.5f * float(math::pi) * latitude * coriolis_bands), 0.f}; + velocity_(x, y) = random_velocity(rng) * 0.f + 0.f * math::vector{-std::cos(0.5f * float(math::pi) * latitude * coriolis_bands), 0.f}; temperature_(x, y) = expected_temperature_at(y); } } - std::vector> octaves; - std::vector weights; - - random::uniform_sphere_vector_distribution random_vector{}; - - for (int octave = 0; octave < 8; ++octave) - { - int size = 4 << octave; - util::ndarray, 2> gradients({size + 1, size + 1}); - for (auto & g : gradients) - g = random_vector(rng); - octaves.emplace_back(std::move(gradients)); - weights.push_back(std::pow(2.f, - 0.75f * octave)); - } - - float weight_sum = 0.f; - for (auto w : weights) - weight_sum += w; - for (auto & w : weights) - w /= weight_sum; - - pcg::fractal> noise(std::move(octaves), std::move(weights)); + auto terrain_noise = make_perlin(rng, 2, 10, 1.6f); for (int y = 0; y < N; ++y) { @@ -107,11 +139,14 @@ struct weather_app { auto d = math::length(math::vector{x - N / 2.f, y - N / 2.f}) / (N / 2.f); (void)d; - float value = noise((x + 0.5f) / N, (y + 0.5f) / N); + float value = terrain_noise((x + 0.5f) / N, (y + 0.5f) / N); value = pow(value, 4.f) - d / 4.f; terrain_(x, y) = std::max(0.f, math::lerp(1.f, 16.f, value)); } } + + make_force_field(rng, force_field_main_, 0.5f); + make_force_field(rng, force_field_next_, 0.5f); } void on_event(app::key_event const & event) override @@ -143,6 +178,14 @@ struct weather_app if (paused_) return; + // Update force field + if ((frame_ % force_field_switch_frames) == 0) + { + std::swap(force_field_current_, force_field_next_); + make_force_field(rng, force_field_next_, 0.5f); + } + float const force_field_t = ((frame_ % force_field_switch_frames) + 0.5f) / force_field_switch_frames; + // Temperature source for (int y = 0; y < N; ++y) { @@ -179,7 +222,7 @@ struct weather_app for (int x = xmin; x < xmax; ++x) { auto v = velocity_(x, y); - auto p = math::point{x + 0.5f, y + 0.5f} - dt * v; + auto p = math::point{x + 0.5f, y + 0.5f} - (advection_magnification * dt) * v; p[0] = p[0] - 0.5f; p[1] = math::clamp(p[1] - 0.5f, {0.f, N - 1.f}); @@ -241,6 +284,11 @@ struct weather_app } std::swap(temperature_, new_temperature_); + // Compute vorticity + for (int y = 1; y < N - 1; ++y) + for (int x = xmin; x < xmax; ++x) + vorticity_(x, y) = (velocity_(x, y + 1)[0] - velocity_(x, y - 1)[0]) / 2.f - (velocity_(wrap(x + 1), y)[1] - velocity_(wrap(x - 1), y)[1]) / 2; + // Apply forces & friction for (int y = 1; y < N - 1; ++y) { @@ -251,6 +299,33 @@ struct weather_app // velocity_(x, y) += math::ort(velocity_(x, y)) * (coriolis * dt * std::sin(0.5f * float(math::pi) * latitude * coriolis_bands)); velocity_(x, y) = math::rotate(velocity_(x, y), coriolis * dt * std::sin(0.5f * float(math::pi) * latitude * coriolis_bands)); + auto force = force_field_main_(x, y) + math::lerp(force_field_current_(x, y), force_field_next_(x, y), force_field_t); + velocity_(x, y) += (dt * force_field_amplitude) * force; + + math::vector terrain_gradient + { + (terrain_(x + 1, y) - terrain_(x - 1, y)) / 2.f, + (terrain_(x, y + 1) - terrain_(x, y - 1)) / 2.f, + }; + + [[maybe_unused]] float slope_factor = std::exp(- dt * slope_force * math::dot(math::normalized(velocity_(x, y)), terrain_gradient)); + velocity_(x, y) *= std::min(1.f, slope_factor); + // velocity_(x, y) -= terrain_gradient * slope_force * dt; + + // Directional external force + // velocity_(x, y)[1] += 0.001f * dt * std::sin(0.5f * float(math::pi) * latitude * coriolis_bands); + // velocity_(x, y) += math::direction(frame_ * dt * 2.f * float(math::pi) / 10080.f) * 0.001f * dt; + + math::vector vorticity_gradient + { + (vorticity_(wrap(x + 1), y) - vorticity_(wrap(x - 1), y)) / 2.f, + (vorticity_(x, y + 1) - vorticity_(x, y - 1)) / 2.f, + }; + if (auto l = math::length(vorticity_gradient); l > 0.f) + vorticity_gradient /= l; + + velocity_(x, y) += vorticity_confinement * dt * vorticity_(x, y) * math::ort(vorticity_gradient); + float local_friction = friction * terrain_(x, y); // velocity_(x, y) -= local_friction * velocity_(x, y) * math::length(velocity_(x, y)); float local_friction_factor = std::exp(- local_friction * dt); @@ -261,7 +336,11 @@ struct weather_app // Solve Poisson equation for pressure for (int iteration = 0; iteration < 16; ++iteration) { - for (int y = 1; y < N - 1; ++y) + int ymin = ((iteration % 2) == 0) ? 1 : N - 2; + int ymax = ((iteration % 2) == 0) ? N - 1 : 0; + int ystep = ((iteration % 2) == 0) ? 1 : -1; + + for (int y = ymin; y != ymax; y += ystep) { for (int x = xmin; x < xmax; ++x) { @@ -308,7 +387,10 @@ struct weather_app for (int x = xmin; x < xmax; ++x) { // Pressure gradient - math::vector gradient{pressure_(wrap(x + 1), y) - pressure_(wrap(x - 1), y), pressure_(x, y + 1) - pressure_(x, y - 1)}; + math::vector gradient{ + (pressure_(wrap(x + 1), y) - pressure_(wrap(x - 1), y)) / 2.f, + (pressure_(x, y + 1) - pressure_(x, y - 1)) / 2.f + }; velocity_(x, y) -= gradient; } } @@ -318,13 +400,39 @@ struct weather_app { if (!periodic_x) { - velocity_(0, i)[0] = -velocity_(1, i)[0]; - velocity_(N-1, i)[0] = -velocity_(N-2, i)[0]; + float left_boundary_flow = 0.f;//0.01f * std::sin((i * 1.f / N) * float(math::pi) * 4.f); + float right_boundary_flow = -left_boundary_flow; + + velocity_(1, i)[0] = left_boundary_flow; + velocity_(N-2, i)[0] = right_boundary_flow; + + velocity_(0, i)[0] = - velocity_(1, i)[0]; + velocity_(N-1, i)[0] = - velocity_(N-2, i)[0]; } velocity_(i, 0)[1] = -velocity_(i, 1)[1]; velocity_(i, N-2)[1] = -velocity_(i, N-2)[1]; } + // Uncomment to visualize the force field + // for (int y = 0; y < N; ++y) + // for (int x = 0; x < N; ++x) + // velocity_(x, y) = 100000.f * force_field_(x, y); + + // Uncomment to visualize the terrain gradient field + // for (int y = 1; y < N - 1; ++y) + // { + // for (int x = 1; x < N - 1; ++x) + // { + // math::vector terrain_gradient + // { + // (terrain_(x + 1, y) - terrain_(x - 1, y)) / 2.f, + // (terrain_(x, y + 1) - terrain_(x, y - 1)) / 2.f, + // }; + + // velocity_(x, y) = terrain_gradient; + // } + // } + ++frame_; // Update all-time average temperature @@ -332,7 +440,9 @@ struct weather_app { for (int x = 0; x < N; ++x) { - average_temperature_(x, y) = math::lerp(average_temperature_(x, y), temperature_(x, y), 1.f / frame_); + float t = 1.f / frame_; + // float t = 1.f / std::min(8192, frame_); + average_temperature_(x, y) = math::lerp(average_temperature_(x, y), temperature_(x, y), t); } } } @@ -361,16 +471,16 @@ struct weather_app [[maybe_unused]] float const pixel_size = view_box[0].length() / state().size[0]; + auto map_color = [](float value, gfx::color_4f const & negative, gfx::color_4f const & positive){ + return math::lerp(negative, positive, 1.f/ (1.f + std::exp(- value))); + }; + for (int y = 0; y < N; ++y) { for (int x = 0; x < N; ++x) { gfx::color_4f color = gfx::color_4f::zero(); - auto map_color = [](float value, gfx::color_4f const & negative, gfx::color_4f const & positive){ - return math::lerp(negative, positive, 1.f/ (1.f + std::exp(- value))); - }; - if (show_land_) color = gfx::blend(color, map_color(terrain_(x, y), {-1.f, -1.f, -1.f, 1.f}, {1.f, 1.f, 1.f, 1.f})); @@ -384,23 +494,33 @@ struct weather_app color = gfx::blend(color, map_color((average_temperature_(x, y) - expected_temperature_at(y)), {0.125f, 0.5f, 1.f, 0.75f}, {1.f, 0.5f, 0.125f, 0.75f})); if (show_pressure_) - color = gfx::blend(color, map_color(1000.f * pressure_(x, y), {0.f, 0.f, 1.f, 0.75f}, {1.f, 0.f, 0.f, 0.75f})); + color = gfx::blend(color, map_color(10000.f * pressure_(x, y), {0.f, 0.f, 1.f, 0.75f}, {1.f, 0.f, 0.f, 0.75f})); painter_.rect({{{x, x + 1.f}, {y, y + 1.f}}}, gfx::to_coloru8(color)); + } + } - if (show_velocity_) + if (show_velocity_) + { + for (int y = 0; y < N; ++y) + { + for (int x = 0; x < N; ++x) { math::point center{x + 0.5f, y + 0.5f}; auto v = velocity_(x, y); + auto color = gfx::color_4f::zero(); if (auto l = math::length(v); l > 0.f) { - float const magnification = 40.f; + float const magnification = 1000.f; float const max_length = 1.5f; v *= 0.5f * max_length * (1.f - std::exp(- magnification * l)) / l; + + // color = gfx::lerp(gfx::color_4f{0.5f, 1.f, 0.f, 1.f}, gfx::color_4f{1.f, 0.f, 0.f, 1.f}, 1.f - std::exp(- 0.25f * magnification * l)); + color = map_color(0.1f * (temperature_(x, y) - 273.f), {0.125f, 0.5f, 1.f, 1.f}, {1.f, 0.5f, 0.125f, 1.f}); } auto n = math::ort(v) * 0.3f; - painter_.triangle(center - v - n, center - v + n, center + v, {255, 255, 255, 255}); + painter_.triangle(center - v - n, center - v + n, center + v, gfx::to_coloru8(color)); } } } @@ -452,6 +572,10 @@ private: util::ndarray temperature_; util::ndarray new_temperature_; util::ndarray average_temperature_; + util::ndarray, 2> force_field_main_; + util::ndarray, 2> force_field_current_; + util::ndarray, 2> force_field_next_; + util::ndarray vorticity_; int frame_ = 0; }; From 02817be114aa6c9a584b10ebbc3e04a5352ecd75 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 11 Dec 2025 16:44:36 +0300 Subject: [PATCH 120/141] Weather simulation: biome distribution --- examples/biomes.png | Bin 0 -> 4367 bytes examples/weather.cpp | 158 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 144 insertions(+), 14 deletions(-) create mode 100644 examples/biomes.png diff --git a/examples/biomes.png b/examples/biomes.png new file mode 100644 index 0000000000000000000000000000000000000000..00437a7e4e552cf3b489c429dff201810656167a GIT binary patch literal 4367 zcmeHKdu&rx7{4vd&CQXCL!B&o>3GQ8efD`TEwHtPRVWUqY`(qE?XtGF_TH`CgeV9q zfr!qxfT)uM38ILBz@ly_8=4@32I8BbF+M@#iy)%kz3sY9NX!!bZ(UB$x!?JHzweyi z`OdfARdw@b7Z+Yph#*LDs5Y*;l==VUQVO$FR zerU>lnYf?8*!UEffBvMz>5_=16uk+Z9SYYWRS?LC)!!RNl*TC%w|jt*q@659vlN9= z4%Y5r9WKO*+}n|ll!4D_XqION)(elh&$*H6er7rWze4f*>q34%nnDc}6M^HQJn^sZ&AzuP(aq~-Bx-Nzor%Q`CZnobwx9s6_5>r?YD`o!vMj;k*& zT|XTu`?j)WRC&3zGx*M}zo%21ckH_B;H#(quzdbO^04;2|6<7Fv=V918^Uv0L5<;@ zsPYn?jwQ@<^v*~pIH6h6QC@0P;y&BSt~YF`BKmCg&M+BH_@ySLwmm5=Y@Zhq+M5MN zw9S}a=uNXg5R-HcO~;~fjZOP(CNB$P!%Wyv6QVc!Yz>fYel;nf4%~s0STL=$(zfY^ zs5dFf?1I3oECoFIY)!hJU@yXUn~(0=jh{FRt3<5NOK8-!bu_)BL;hDdaxBpvI{z| zhZbo|NW=n3Q(KaP6l|5^dc|N0QONlxT9Q$7JEA~HQ7Hyc4Wd#*LK^on=V3_DsKgSc z7l=IssVnk;SVPuk%$VC5yby5D@eV=v@7)9gDja45s?cIAFBI_EjQFgm3W~^@Lylo6 zmtA&ZGAY|Jhd{Y7MwDfYcaS_!akAaxrUp@k;+oFI1<9ZSa$Er(x5)DjT5w_{;}J0j z=O!_R;W*6cak>N#O}d>7Gl=54qykyVMF+3Spb~+~DKmDLz%v-fIDyK|*fHMj^k9tJ zB~c>H^Da^{sYHRDr6yw>9H$cF8YLnTZ!{+i!P)A%kk3ZrSmQy?(yuou=W zqiQmus!^XUSDSOwVYp5rr*i?Q>Oh*S){*{deGO@6$;|Q%y09uLa@)V5jmv|20S`xB zt7y8KY%@)n8?{inBlDDbj4I|;LQ(Tlu$++Dg2uHXA*?e!Fs> zRN!Tgho(6PX6H#a<{%v$#=GH)JM0V(sxltRo(5uaZ znmQDnYLbjo0KqVd#whBaf)Uvb6GqMG-?5iCM-#6J7*u4yFJpt^1+|dK6~nA%M%nov zKiRYRA0q(uNRVOaJ0jPJT*FdeSm2TD8j)*Q3JeQ8l3o9qT!jO#Q&Jp$1*PC+>9+Z8 z0=#Nnz|WZ-KuE-cRBJuGN5OJoqV@(2LGTKrS<;(kw1P2T4~2vI-&|BwG#Ni*|Lz!= z%Jg7^?pI^RuX5y^ckcRSV=e=bKNP5r6db+2XZyIBWGT}9#im2j=ymUXw6`GIdqMQa z+4~Eq>e9;8EzQOAmmXevBG}u0bM5<2bdLV2{Odm_Rr&Ax;f1t?*jiGt;U(cyphH5z Ld4X*;w=e%2tXsi9 literal 0 HcmV?d00001 diff --git a/examples/weather.cpp b/examples/weather.cpp index b26c487a..cb16ac71 100644 --- a/examples/weather.cpp +++ b/examples/weather.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -10,6 +11,7 @@ #include #include #include +#include using namespace psemek; @@ -63,15 +65,19 @@ struct weather_app const float viscosity = 0.f; const float advection_magnification = 1.f; const float temperature_diffusion = 0.001f; - const float cooling = 0.01f / 300.f; + const float cooling = 0.1f / 300.f; const float cooling_factor = std::exp(- cooling * dt); const float heating = 323.f * (std::exp(cooling * dt) - 1.f) / dt; - const float coriolis = 0.01f; + const float coriolis = 0.f; const float coriolis_bands = 2.f; const float friction = 0.f; const float slope_force = 0.04f; const float vorticity_confinement = 0.f; + const float evaporation = 0.1f; + const float max_humidity_factor = 0.001f; + const float precipitation_factor = 0.0003f; const float force_field_amplitude = 0.00005f; + const float random_forces = 0.f; const float force_field_switch_duration = 720.f * 7.5f; // 7.5 days const int force_field_switch_frames = std::round(force_field_switch_duration / dt); // const float friction_factor = 1.f - std::exp(- friction * dt); @@ -81,6 +87,8 @@ struct weather_app random::generator rng{random::device{}}; // random::generator rng{0, 0}; + gfx::pixmap_rgba biomes_map; + float expected_temperature_at(int y) { // float latitude = (y - N * 0.5f) * 2.f / N; @@ -91,9 +99,10 @@ struct weather_app float temperature_income_at(int y) { - float latitude = (y - N * 0.5f) * 2.f / N; + // float latitude = (y - N * 0.5f) * 2.f / N; + float latitude = y * 1.f / N; // return heating * math::lerp(0.75f, 1.f, std::cos(latitude * float(math::pi) / 2.f)); - return heating * math::lerp(0.65f, 1.f, 1.f - std::abs(latitude)); + return heating * math::lerp(0.8f, 1.f, 1.f - std::abs(latitude)); } weather_app(options const &, context const &) @@ -111,6 +120,10 @@ struct weather_app force_field_current_.resize({N, N}); force_field_next_.resize({N, N}); vorticity_.resize({N, N}); + humidity_.resize({N, N}); + new_humidity_.resize({N, N}); + precipitation_.resize({N, N}); + average_precipitation_.resize({N, N}); // random::generator rng{0, 0}; random::uniform_ball_vector_distribution random_velocity{}; @@ -141,12 +154,15 @@ struct weather_app (void)d; float value = terrain_noise((x + 0.5f) / N, (y + 0.5f) / N); value = pow(value, 4.f) - d / 4.f; - terrain_(x, y) = std::max(0.f, math::lerp(1.f, 16.f, value)); + value = math::lerp(1.f, 16.f, value); + terrain_(x, y) = value; } } make_force_field(rng, force_field_main_, 0.5f); make_force_field(rng, force_field_next_, 0.5f); + + biomes_map = gfx::read_image(io::file_istream{std::filesystem::path{PSEMEK_EXAMPLES_DIR} / "biomes.png"}); } void on_event(app::key_event const & event) override @@ -171,6 +187,18 @@ struct weather_app if (event.down && event.key == app::keycode::H) show_land_ ^= true; + + if (event.down && event.key == app::keycode::W) + show_water_vapor_ ^= true; + + if (event.down && event.key == app::keycode::R) + show_precipitation_ ^= true; + + if (event.down && event.key == app::keycode::Q) + show_average_precipitation_ ^= true; + + if (event.down && event.key == app::keycode::B) + show_biomes_ ^= true; } void update() override @@ -197,6 +225,23 @@ struct weather_app } } + // Evaporation + for (int y = 0; y < N; ++y) + { + for (int x = 0; x < N; ++x) + { + if (terrain_(x, y) <= 0.f) + humidity_(x, y) += dt * evaporation * std::max(0.f, temperature_(x, y) - 273.f); + + // float discharge = std::min(humidity_(x, y), precipitation_factor * dt); + float discharge = humidity_(x, y) * precipitation_factor * dt; + // float max_humidity = std::max(0.f, temperature_(x, y) - 223.f) * max_humidity_factor; + // float discharge = std::max(0.f, humidity_(x, y) - max_humidity) * precipitation_factor * dt; + humidity_(x, y) -= discharge; + precipitation_(x, y) = discharge; + } + } + auto wrap = [](int i) { return (i + N) % N; @@ -211,10 +256,16 @@ struct weather_app new_temperature_(i, 0) = temperature_(i, 0); new_temperature_(i, N - 1) = temperature_(i, N - 1); + new_humidity_(i, 0) = humidity_(i, 0); + new_humidity_(i, N - 1) = humidity_(i, N - 1); + if (!periodic_x) { new_temperature_(0, i) = temperature_(0, i); new_temperature_(N - 1, i) = temperature_(N - 1, i); + + new_humidity_(0, i) = humidity_(0, i); + new_humidity_(N - 1, i) = humidity_(N - 1, i); } } for (int y = 1; y < N - 1; ++y) @@ -249,10 +300,17 @@ struct weather_app math::lerp(temperature_(wrap(ix + 0), iy + 1), temperature_(wrap(ix + 1), iy + 1), tx), ty ); + + new_humidity_(x, y) = math::lerp( + math::lerp(humidity_(wrap(ix + 0), iy + 0), humidity_(wrap(ix + 1), iy + 0), tx), + math::lerp(humidity_(wrap(ix + 0), iy + 1), humidity_(wrap(ix + 1), iy + 1), tx), + ty + ); } } std::swap(velocity_, new_velocity_); std::swap(temperature_, new_temperature_); + std::swap(humidity_, new_humidity_); // Apply velocity diffusion for (int y = 0; y < N; ++y) @@ -299,13 +357,13 @@ struct weather_app // velocity_(x, y) += math::ort(velocity_(x, y)) * (coriolis * dt * std::sin(0.5f * float(math::pi) * latitude * coriolis_bands)); velocity_(x, y) = math::rotate(velocity_(x, y), coriolis * dt * std::sin(0.5f * float(math::pi) * latitude * coriolis_bands)); - auto force = force_field_main_(x, y) + math::lerp(force_field_current_(x, y), force_field_next_(x, y), force_field_t); + auto force = force_field_main_(x, y) + random_forces * math::lerp(force_field_current_(x, y), force_field_next_(x, y), force_field_t); velocity_(x, y) += (dt * force_field_amplitude) * force; math::vector terrain_gradient { - (terrain_(x + 1, y) - terrain_(x - 1, y)) / 2.f, - (terrain_(x, y + 1) - terrain_(x, y - 1)) / 2.f, + (std::max(0.f, terrain_(x + 1, y)) - std::max(0.f, terrain_(x - 1, y))) / 2.f, + (std::max(0.f, terrain_(x, y + 1)) - std::max(0.f, terrain_(x, y - 1))) / 2.f, }; [[maybe_unused]] float slope_factor = std::exp(- dt * slope_force * math::dot(math::normalized(velocity_(x, y)), terrain_gradient)); @@ -435,7 +493,7 @@ struct weather_app ++frame_; - // Update all-time average temperature + // Update all-time average temperature & precipitation for (int y = 0; y < N; ++y) { for (int x = 0; x < N; ++x) @@ -443,6 +501,7 @@ struct weather_app float t = 1.f / frame_; // float t = 1.f / std::min(8192, frame_); average_temperature_(x, y) = math::lerp(average_temperature_(x, y), temperature_(x, y), t); + average_precipitation_(x, y) = math::lerp(average_precipitation_(x, y), precipitation_(x, y), t); } } } @@ -475,17 +534,65 @@ struct weather_app return math::lerp(negative, positive, 1.f/ (1.f + std::exp(- value))); }; + auto map_temperature = [&](float value) { + return map_color(2.f * std::round(value / 20.f), {0.125f, 0.5f, 1.f, 0.75f}, {1.f, 0.5f, 0.125f, 0.75f}); + }; + + auto map_biome = [this](float temperature, float precipitation) + { + auto x = math::clamp(math::unlerp({ -3.f, 3.f}, precipitation) * biomes_map.width() , {0, biomes_map.width() - 1}); + auto y = math::clamp(math::unlerp({-10.f, 40.f}, temperature ) * biomes_map.height(), {0, biomes_map.height() - 1}); + + return gfx::to_colorf(biomes_map(x, y)); + }; + for (int y = 0; y < N; ++y) { for (int x = 0; x < N; ++x) { gfx::color_4f color = gfx::color_4f::zero(); - if (show_land_) - color = gfx::blend(color, map_color(terrain_(x, y), {-1.f, -1.f, -1.f, 1.f}, {1.f, 1.f, 1.f, 1.f})); + if (show_land_ || show_biomes_) + { + if (!show_biomes_) + { + if (terrain_(x, y) <= 0.f) + color = {0.5f, 0.5f, 1.f, 1.f}; + else + color = {1.f, 1.f, 1.f, 1.f}; + } + else + { + if (terrain_(x, y) <= 0.f) + color = map_color(4.f * terrain_(x, y), {0.f, 0.f, 0.125f, 1.f}, {0.f, 1.f, 1.5f, 1.f}); + else + { + float temperature = average_temperature_(x, y) - 273.f - std::max(0.f, terrain_(x, y)) * 6.5f; + // float precipitation = (std::log10(std::max(1e-9f, average_precipitation_(x, y))) + 3.f) * 2.f; + // float precipitation = std::pow(average_precipitation_(x, y) * 125.f, 2.f) * 8.f; + float precipitation = std::log2(std::max(1e-9f, average_precipitation_(x, y))); + color = map_biome(temperature, precipitation); + } + } + + if (show_land_ && x > 0 && x + 1 < N && y > 0 && y + 1 < N) + { + math::vector terrain_gradient + { + (terrain_(x + 1, y) - terrain_(x - 1, y)) / 2.f, + (terrain_(x, y + 1) - terrain_(x, y - 1)) / 2.f, + }; + + auto terrain_normal = math::normalized(math::vector{-terrain_gradient[0], -terrain_gradient[1], 0.5f}); + + float lightness = 0.5f + 0.5f * math::dot(terrain_normal, math::normalized(math::vector{1.f, 2.f, 3.f})); + + color = gfx::dark(color, 1.f - lightness); + } + } if (show_temperature_) - color = gfx::blend(color, map_color(0.1f * (temperature_(x, y) - 273.f), {0.125f, 0.5f, 1.f, 0.75f}, {1.f, 0.5f, 0.125f, 0.75f})); + color = map_temperature(temperature_(x, y) - 273.f); if (show_temperature_delta_) color = gfx::blend(color, map_color((temperature_(x, y) - expected_temperature_at(y)), {0.125f, 0.5f, 1.f, 0.75f}, {1.f, 0.5f, 0.125f, 0.75f})); @@ -496,6 +603,15 @@ struct weather_app if (show_pressure_) color = gfx::blend(color, map_color(10000.f * pressure_(x, y), {0.f, 0.f, 1.f, 0.75f}, {1.f, 0.f, 0.f, 0.75f})); + if (show_water_vapor_) + color = gfx::blend(color, map_color(humidity_(x, y), {0.f, 1.f, 1.f, -0.75f}, {0.f, 1.f, 1.f, 0.75f})); + + if (show_precipitation_) + color = gfx::blend(color, map_color(precipitation_(x, y), {0.f, 1.f, 1.f, -0.75f}, {0.f, 1.f, 1.f, 0.75f})); + + if (show_average_precipitation_) + color = gfx::blend(color, map_color(average_precipitation_(x, y), {0.f, 1.f, 1.f, -0.75f}, {0.f, 1.f, 1.f, 0.75f})); + painter_.rect({{{x, x + 1.f}, {y, y + 1.f}}}, gfx::to_coloru8(color)); } } @@ -542,11 +658,14 @@ struct weather_app push_text(std::format("{} {}", x, y)); push_text(std::format("V = {:.3f} {:.3f}", velocity_(x, y)[0] * 1000.f, velocity_(x, y)[1] * 1000.f)); - push_text(std::format("P = {:.3f}", pressure_(x, y))); + push_text(std::format("P = {:.3f}", pressure_(x, y) * 1000.f)); push_text(std::format("T = {:.3f}", temperature_(x, y) - 273.f)); push_text(std::format("A = {:.3f}", average_temperature_(x, y) - 273.f)); push_text(std::format("E = {:.3f}", expected_temperature_at(y) - 273.f)); push_text(std::format("H = {:.3f}", terrain_(x, y))); + push_text(std::format("W = {:.3f}", humidity_(x, y))); + push_text(std::format("R = {:.3f}", precipitation_(x, y))); + push_text(std::format("AR= {:.3f}", average_precipitation_(x, y))); } painter_.render(math::orthographic_camera{view_box}.transform()); @@ -564,18 +683,29 @@ private: bool show_average_temperature_delta_ = false; bool show_pressure_ = false; bool show_land_ = true; + bool show_biomes_ = true; + bool show_water_vapor_ = false; + bool show_precipitation_ = false; + bool show_average_precipitation_ = false; util::ndarray terrain_; util::ndarray, 2> velocity_; util::ndarray, 2> new_velocity_; util::ndarray pressure_; + util::ndarray vorticity_; + util::ndarray temperature_; util::ndarray new_temperature_; util::ndarray average_temperature_; + + util::ndarray humidity_; + util::ndarray new_humidity_; + util::ndarray precipitation_; + util::ndarray average_precipitation_; + util::ndarray, 2> force_field_main_; util::ndarray, 2> force_field_current_; util::ndarray, 2> force_field_next_; - util::ndarray vorticity_; int frame_ = 0; }; From f88d158b35ab006d2d8b3cdb32ccaa218a91e23d Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 12 Dec 2025 15:35:31 +0300 Subject: [PATCH 121/141] Weather simulation wip --- examples/biomes.png | Bin 4367 -> 4595 bytes examples/weather.cpp | 141 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 117 insertions(+), 24 deletions(-) diff --git a/examples/biomes.png b/examples/biomes.png index 00437a7e4e552cf3b489c429dff201810656167a..36b3c9bb5a810424dcca536a930e8be1e05cf8a0 100644 GIT binary patch delta 725 zcmeBI`m8*mpOJau1ZUnqnhXrl3=9lldg22ab|Yge10yTb$;^ya^%M6$OJZPPY|eCc z4)An#RwyXSPs_|nWnie7Q#;Yt~0;9*=5O3eni=w3<(C zdY$9sHkG4C0}FI5er${=EhxNp^SGe_!~WPe4j^ zS!ArC!{uD#xgFj&@2nE3s&|#B6;Mp@NSkIj-AnbM^}ct44_?PkN&h+jck!3imEDYt zofgMFKAIG|TrD{zzDaKNob49bQ>C)+PTp@Y=X&_Goqok4oH|LInjQfG&XS__H##n@ znw+HQ`@2y7f5&V8iQbCgl07oqE1a6&{5tsmLHYKu#H>r+T*n*Vi68$d!R)l#Ve>nS z`^Wn(+b}krKQQ&)w{YnLZ`5CY-6m$q#I~)m`}MYEZy2XrANVj?arP!{)=aKerVn*) z_opzkjC_2JAC;t1*Z+=_2;+z`pxKmzi8@vtC(~K z2EORcSDCt)RLsmxl8uwiQ*=#D4J>p`(oBpQxc8M4b6=W5+^sauH>>X zHa0giFts!?n{38b0*~X3Z0a!f=KE|yjFb7;V^oY&lTA|0ElqS&EDch0O$<_!b(2hu z(sV77%q$EI3=$2DERrVovHQYxK4Q=35bHAA+XzfXY)RhkE9@k83-6wNC;h2zrV8tWRIt-pUXO@geCxZ C!X6<2 delta 671 zcmV;Q0$}~~Bab4Gj{yRakV6eD0RR920000i0RR92k>DH$F*G_eI65?w0|7dJQ1dyX z0004mX+uL$Nkc;*aB^>EX>4Tx0C=2zkv&MmKpe$iTct%S4t5Z6$WWau_=PyuDionY zs1;guFuC*#nlvOSE{=k0!NHHks)LKOt`4q(Aou~|E;uQ=NQwVT3N2zhIPS;0dyl(! zfWJ{;s@W9>RLwHd$%K&2tqQ?^SA@}v5XLbfF;kxt#S}cp*FAiEy^HZI?{j~SJ|%B5 zz$XyLnQmCb8^kl4md<&fIKoPjLVQjC;;dF`taVTR!f;+&S>`&;VI;ALC5R9pqlPknDzFfvRU^ej zn)VYO{vpRNl1nC68H^kYs6vJ0_`(0+ceiF?YSK*#CxGr3+x{2@ctJ^&f&Ds=-K90H>oMao|Hcz39?w|~#H`uhRj)N;~uq(cz^01bGv)B=hEBsXMY zVl*;iH7zhXIAkp}VK*=>IXPirEj2hbHe)z4FgGz z*ON&RAqNX4F8nj=;FEC?C|E;DL_t&t*Hyu>6+kc)0Kns=(Padi(AvTXY{mv`@JGl9 zF+mh6cU@}@QGL>V?nTCWTu9THqXgz0==~)uLx=6DcmXf05F?$hW8DA%002ovPDHLk FV1n$N5$XT{ diff --git a/examples/weather.cpp b/examples/weather.cpp index cb16ac71..8f19d7c7 100644 --- a/examples/weather.cpp +++ b/examples/weather.cpp @@ -43,8 +43,8 @@ auto make_perlin(random::generator & rng, int min_octave, int max_octave, float void make_force_field(random::generator & rng, util::ndarray, 2> & result, float scale) { - auto noise_1 = make_perlin(rng, 2, 6, 2.f); - auto noise_2 = make_perlin(rng, 2, 6, 2.f); + auto noise_1 = make_perlin(rng, 3, 6, 2.f); + auto noise_2 = make_perlin(rng, 3, 6, 2.f); for (int y = 0; y < result.height(); ++y) { @@ -61,7 +61,7 @@ struct weather_app { static constexpr int N = 128; - const float dt = 4.f; + const float dt = 20.f; const float viscosity = 0.f; const float advection_magnification = 1.f; const float temperature_diffusion = 0.001f; @@ -71,13 +71,13 @@ struct weather_app const float coriolis = 0.f; const float coriolis_bands = 2.f; const float friction = 0.f; - const float slope_force = 0.04f; + const float slope_force = 4.f; const float vorticity_confinement = 0.f; - const float evaporation = 0.1f; - const float max_humidity_factor = 0.001f; + const float evaporation = 0.01f; + const float max_humidity_factor = 100.f; const float precipitation_factor = 0.0003f; const float force_field_amplitude = 0.00005f; - const float random_forces = 0.f; + const float random_forces = 1.f; const float force_field_switch_duration = 720.f * 7.5f; // 7.5 days const int force_field_switch_frames = std::round(force_field_switch_duration / dt); // const float friction_factor = 1.f - std::exp(- friction * dt); @@ -89,7 +89,7 @@ struct weather_app gfx::pixmap_rgba biomes_map; - float expected_temperature_at(int y) + float expected_temperature_at(int y) const { // float latitude = (y - N * 0.5f) * 2.f / N; // return std::cos(latitude * float(math::pi)); @@ -97,7 +97,7 @@ struct weather_app return temperature_income_at(y) * dt / (std::exp(cooling * dt) - 1.f); } - float temperature_income_at(int y) + float temperature_income_at(int y) const { // float latitude = (y - N * 0.5f) * 2.f / N; float latitude = y * 1.f / N; @@ -105,6 +105,11 @@ struct weather_app return heating * math::lerp(0.8f, 1.f, 1.f - std::abs(latitude)); } + int wrap(int i) const + { + return (i + N) % N; + } + weather_app(options const &, context const &) { simulation_box_ = {{{0.f, N}, {0.f, N}}}; @@ -141,6 +146,9 @@ struct weather_app float latitude = (N * 0.5f - y) * 2.f / N; velocity_(x, y) = random_velocity(rng) * 0.f + 0.f * math::vector{-std::cos(0.5f * float(math::pi) * latitude * coriolis_bands), 0.f}; temperature_(x, y) = expected_temperature_at(y); + + float max_humidity = std::max(0.f, temperature_(x, y) - 223.f) * max_humidity_factor; + humidity_(x, y) = max_humidity + dt * evaporation * std::max(0.f, temperature_(x, y) - 273.f) * (1.f - precipitation_factor * dt) / precipitation_factor / dt; } } @@ -214,6 +222,9 @@ struct weather_app } float const force_field_t = ((frame_ % force_field_switch_frames) + 0.5f) / force_field_switch_frames; + int xmin = periodic_x ? 0 : 1; + int xmax = periodic_x ? N : N - 1; // exclusive + // Temperature source for (int y = 0; y < N; ++y) { @@ -234,22 +245,14 @@ struct weather_app humidity_(x, y) += dt * evaporation * std::max(0.f, temperature_(x, y) - 273.f); // float discharge = std::min(humidity_(x, y), precipitation_factor * dt); - float discharge = humidity_(x, y) * precipitation_factor * dt; - // float max_humidity = std::max(0.f, temperature_(x, y) - 223.f) * max_humidity_factor; - // float discharge = std::max(0.f, humidity_(x, y) - max_humidity) * precipitation_factor * dt; + // float discharge = humidity_(x, y) * precipitation_factor * dt; + float max_humidity = std::max(0.f, temperature_(x, y) - 223.f) * max_humidity_factor; + float discharge = std::max(0.f, humidity_(x, y) - max_humidity) * precipitation_factor * dt; humidity_(x, y) -= discharge; precipitation_(x, y) = discharge; } } - auto wrap = [](int i) - { - return (i + N) % N; - }; - - int xmin = periodic_x ? 0 : 1; - int xmax = periodic_x ? N : N - 1; // exclusive - // Velocity & temperature advection for (int i = 0; i < N; ++i) { @@ -366,10 +369,14 @@ struct weather_app (std::max(0.f, terrain_(x, y + 1)) - std::max(0.f, terrain_(x, y - 1))) / 2.f, }; - [[maybe_unused]] float slope_factor = std::exp(- dt * slope_force * math::dot(math::normalized(velocity_(x, y)), terrain_gradient)); - velocity_(x, y) *= std::min(1.f, slope_factor); + // [[maybe_unused]] float slope_factor = std::exp(- dt * slope_force * math::dot(math::normalized(velocity_(x, y)), terrain_gradient)); + // velocity_(x, y) *= std::min(1.f, slope_factor); // velocity_(x, y) -= terrain_gradient * slope_force * dt; + [[maybe_unused]] float slope_factor = std::exp(- dt * slope_force * std::pow(math::length(terrain_gradient), 4.f)); + // [[maybe_unused]] float slope_factor = std::exp(- dt * slope_force * std::pow(std::max(0.f, terrain_(x, y)), 1.f)); + velocity_(x, y) *= slope_factor; + // Directional external force // velocity_(x, y)[1] += 0.001f * dt * std::sin(0.5f * float(math::pi) * latitude * coriolis_bands); // velocity_(x, y) += math::direction(frame_ * dt * 2.f * float(math::pi) / 10080.f) * 0.001f * dt; @@ -528,6 +535,68 @@ struct weather_app mouseover_cell = {x, y}; } + if (mouseover_cell && (state().mouse_button_down.contains(app::mouse_button::left) || state().mouse_button_down.contains(app::mouse_button::right))) + { + float delta = state().mouse_button_down.contains(app::mouse_button::left) ? 1.f : -1.f; + + int R = 4; + + for (int iy = -R; iy <= R; ++iy) + { + for (int ix = -R; ix <= R; ++ix) + { + int x = (*mouseover_cell)[0] + ix; + int y = (*mouseover_cell)[1] + iy; + + if (x >= 0 && x < N && y >= 0 && y < N) + { + auto d = math::vector{ix, iy} / 2.f; + terrain_(x, y) += delta * 0.05f * std::exp(- math::dot(d, d)); + } + } + } + } + + if (mouseover_cell && state().mouse_button_down.contains(app::mouse_button::middle)) + { + int R = 4; + + float average = 0.f; + int count = 0; + + for (int iy = -R; iy <= R; ++iy) + { + for (int ix = -R; ix <= R; ++ix) + { + int x = (*mouseover_cell)[0] + ix; + int y = (*mouseover_cell)[1] + iy; + + if (x >= 0 && x < N && y >= 0 && y < N) + { + average += terrain_(x, y); + count += 1; + } + } + } + + average /= count; + + for (int iy = -R; iy <= R; ++iy) + { + for (int ix = -R; ix <= R; ++ix) + { + int x = (*mouseover_cell)[0] + ix; + int y = (*mouseover_cell)[1] + iy; + + if (x >= 0 && x < N && y >= 0 && y < N) + { + auto d = math::vector{ix, iy} / 2.f; + terrain_(x, y) += (average - terrain_(x, y)) * 0.05f * std::exp(- math::dot(d, d)); + } + } + } + } + [[maybe_unused]] float const pixel_size = view_box[0].length() / state().size[0]; auto map_color = [](float value, gfx::color_4f const & negative, gfx::color_4f const & positive){ @@ -604,10 +673,34 @@ struct weather_app color = gfx::blend(color, map_color(10000.f * pressure_(x, y), {0.f, 0.f, 1.f, 0.75f}, {1.f, 0.f, 0.f, 0.75f})); if (show_water_vapor_) - color = gfx::blend(color, map_color(humidity_(x, y), {0.f, 1.f, 1.f, -0.75f}, {0.f, 1.f, 1.f, 0.75f})); + color = gfx::blend(color, map_color(humidity_(x, y) * 0.001f, {0.f, 1.f, 1.f, -0.75f}, {0.f, 1.f, 1.f, 0.75f})); if (show_precipitation_) - color = gfx::blend(color, map_color(precipitation_(x, y), {0.f, 1.f, 1.f, -0.75f}, {0.f, 1.f, 1.f, 0.75f})); + { + // color = gfx::blend(color, map_color(precipitation_(x, y), {1.f, 1.f, 1.f, -1.f}, {1.f, 1.f, 1.f, 1.f})); + + float alpha = 2.f / (1.f + std::exp(- precipitation_(x, y))) - 1.f; + + gfx::color_4f cloud_color{1.f, 1.f, 1.f, alpha * 0.875f}; + + if (y > 0 && y + 1 < N) + { + if (periodic_x || (x > 0 && x + 1 < N)) + { + math::vector gradient + { + (precipitation_(wrap(x + 1), y) - precipitation_(wrap(x - 1), y)) / 2.f, + (precipitation_(x, y + 1) - precipitation_(x, y - 1)) / 2.f, + }; + + auto normal = math::normalized(math::vector{-gradient[0], -gradient[1], 2.f}); + auto lightness = 0.5f + 0.5f * math::dot(normal, math::normalized(math::vector{1.f, 2.f, 3.f})); + cloud_color = gfx::dark(cloud_color, 1.f - lightness); + } + } + + color = gfx::blend(color, cloud_color); + } if (show_average_precipitation_) color = gfx::blend(color, map_color(average_precipitation_(x, y), {0.f, 1.f, 1.f, -0.75f}, {0.f, 1.f, 1.f, 0.75f})); From 23f153c38dcb90834b16566dc81c8c609f7dd5ba Mon Sep 17 00:00:00 2001 From: lisyarus Date: Mon, 15 Dec 2025 13:19:23 +0300 Subject: [PATCH 122/141] Weather simulation experiments wip --- examples/heightmap_seed_0.png | Bin 0 -> 7837 bytes examples/heightmap_seed_1.png | Bin 0 -> 7595 bytes examples/heightmap_seed_2.png | Bin 0 -> 7371 bytes examples/heightmap_seed_3.png | Bin 0 -> 7506 bytes examples/weather.cpp | 78 +++++++++++++++++++++++----------- 5 files changed, 54 insertions(+), 24 deletions(-) create mode 100644 examples/heightmap_seed_0.png create mode 100644 examples/heightmap_seed_1.png create mode 100644 examples/heightmap_seed_2.png create mode 100644 examples/heightmap_seed_3.png diff --git a/examples/heightmap_seed_0.png b/examples/heightmap_seed_0.png new file mode 100644 index 0000000000000000000000000000000000000000..0b246c8182336dd9318fcb28ac35954a5df5c4b8 GIT binary patch literal 7837 zcmeHMc|4Te+rNjglO+`;#)O6pvtdk#jF@Cek`^?}JxrLvjI|OGDrHI83YFz)(I!Ne zN=lgMkwg?KAxn}iz2hg|OYz5RcgRT@Ng`xupl#nG14!|HO0&1||WFeR^APnhZ zpr!!H0vkbF2@_fD;~{y~4><*rQ9o>iAQ=l20%V~6EYxt2tO)g&p~i~+K35ea3oh}+ zmklHdkpJqfDRzzsoE{dVhbKb*7@PqKi-X<>tRaa22}Y1LEaz850;J7gWdekPWLa7| zQY0yTJ5SO3y*uwE>s&e-#UdsuaHIXFF%;rU(?WYx^iyQ zZjV^Ipr{*-`?J~##tu}`ln)N9yxv!~p~*=x^*rxd{ey}>?scqKU!-Gnl{O#Lt#(xH zVNeP)X_`{8G%tx-^s(RxVQ!32%6i;hTQ+c|#NW6_Q8rV4o0f(%FE5?+m^egnlN1`! zmdKO18HVROcgAftE>k%sitNf)=UjO@o`Oq$Om^4mIq96GB|Cnoy3OqApq)28OU%l# zbh&fkn9^&t^Z4juPuEIz-L9GWk{IQJteFq1ssR+vFw^Z=G0kHujl1ejDBCy-}~L_*_;zvA=tv$OjN&*uJM z0pdeHgzBe{)x+rf`s)Ag!R1*6K_EW@`i~x5=TJXT-wEV$0t4uvRS?MLt@#~-LI3IR z7Z~8PxEuyuAM^ozAyY0iEB0?w3eLk%4*><9EMLDxFNo~FS@KxSUu69)Ho?f^a()j4 za{r0@H|xLFzGw_t+1ZiE9D1N2JPO$qDVU$c;LuqN(xONQ4GoOwcqST8#2cUq7_2ec z7)vxnGch!x0UZY#f*|%cC<>d)qq6Cs015)vV?j7rBP^3)WJE_3m_#a?fMYVzM3Bxv z8`6zvG(!U>6}o7r@9&MaSW8;>er6KiB>U_dZ5#v2g`I0C`=myrt?z=bkVfVo)N7DoizLV^|p5la>1 zDFm>nht@)}3;?M-PJlCq<70~aSrdN_+d;>PLFG}&R2~R{{;ZPDe^tqK7(5B{J$wP2 z9f!eUhW>BXg2RI_f$X6zw_$Oi`9l{~-%r#w(Et10_gf#<;!#2%77qo9O8>qDE;R^b zEXE08eIKHGQQ4j#R6TyA>tA}-e@F$q0g=XF((q_w0)d7mV2K83BF=!0Ho)SUL_-|j zm|%?mJ35!cv0NXL%)K8p@*e2+F!Rq4_ZPrTWc~PM2_01 z3f)Qg+3e&3fYee!g8><5l^~@Uk78#fHXto6qa%N)oZkTeOL8b=3+IrI;oaU`l_&m< zR|m7&Ma&~iK5N)|?hYV6_@@1B{@YL-w|`mk$n-sKuxI&|Kh>D`*C&T0-8(WDIWDs5 zWmo#pwZY4cL+;;}EWB*^wCvsvzyo$bWKme?zG5Pt3-q6yk`c`juA5&7`Cw!w3zW^Z z7LI17pL&w`$5fNpga$w)1ys?APAH)pa-v5xEF6f!ftcKlp&wE`ee-fBt+a`O`ZLHRAz&g^^fq*i|8U*+ZX&VJZi>#4C@spA0SL zhX=cBr$@Si_0%|9yB)pOq=xx41FdE$YU0O+cw*0Xon6;hVU!QJ=# zFlOOGhra!3Gq}dtLo&i;u!rOE?Q6T1#py$&+#}0YJjs@P_xO$fnu$+)J7w;DOtiKR ztgIRInjdPdz0~x^>=jNt?P#eYTU{+nIv9qyn!(iEaphv_^{V#y88?+f!34W>%w6B) z(+(xlwf!+hZ*3=0W7pJrtnA-%W?w18P#O8FHeFu3M+EM*r&4Lx>Yx^;nY&EwlEImn zycfI9ZthKLI~QoqtTGF!>Kokg-eG*Q|IP71%@V&g2%`ergp?4_4es$BqXA7Nfu+ea zvahITMPQ5<1K)zegB|O;-sHU>aZ{0PZ5_rKUFfr0mlu^1&n{ovPv|tJ1b+Yu%TFi2 zj|luaQC*Q9UQE)yzTX>(!koV&7SZ0d&Cg*8=5Tyy+osus9q6(!1C@&TQ`@8AH*|`+ zTGgjCZ9OuCsr=xH<^+c-E3^IHo}8z#Z&$9yh@dOY8&BRjcd1$9^-bdFhzMYzW?vN= z#B(+BEIgOxfY;ZX{g$P1g3x6~K3q|R?JSbSIb8+Q^!5xNC|&)i)kjRPK9`WD(>8zk zjr&}RXw9(7a22TJQ#iHFuFAV?Tjrc(#AJhDC$B;Xl4UsuV6#M+d5q~#V5AZR1%>PH!eF&9b4G}oJS0T;}tZ|e=P z!#)GPD1MQZxAqf2#C*48d}OY9a>O$c(G4!QdXuBzobCqM<`@NJz&`T}O7KX`vlMlT z)58Gz#Sdq-Ups%JET5QD2(?eH44x#MNW3Fntsx?zsDWSUrP~hc6T^DA?>K|IU6gRq ze*;j~a!aZ(FK@-L@lAMCt|r38_mb?%=r5F+g0}M~W4W)wqr27d0JjJUUbrK!Yf=Y` za5MKjdRkLUV};Ns*21Mgw>5|83$x0=9Zo+pvOIU!&U^x&VJ}WbI=WuDWEE>JenxE# z3RF?)zEV`GR<&(}w@F9i&NC^EL<>1N6t+mWNny3t#R#~PpYVp{&x#dFB_G7-18R)^ zbLyxi5^ufwbdqvBJ~$`CB_5>xp{0pC!qD;>-7YnCtE+O!NMpmC-HdlhEKE*9;(hnt z?dbbffx{qiz+OH_!d;R3<+X5qp6$+k)n8H-Rb<G#@iYkQq+o(aK^i9oSSwAHcg4=kz$L_vx~e*#}oF6gG$niLWb8YrjIYCeo3kyhL`fvl!LU^3k2JiJ^V7U$3g#8P^s7?A}p%d3iZ=V`(qyOY;xQJ6#Mogpj$IHCS5UAcAaB|qv!v*wc_R0^tbi!`%}WfDO+~FxQ{0+ zJbc{$2`4=gMR7Yk-NYW=e;`=Su<_8{kZ+%7!)J9~uJ6)dIi1S8nbI2B{&ZvdfFCpT=HInzOc_KT#sxY509$Z zj~}~M52i_q^TJklNe+nm6~{bRkH#&_!AeA*Z?TF~N*bu4_-7RCLtP8%O41UmKDY3( zHZ$C%VPS5@;rgMo?9>nBd~!xxZ!u}0w*N>iem^R8P#mu#lEk|RFUN0RBD(a1&7Hoo zQy0ow`T{GyH3X$Ce46)o+LXy273ug`h@e(FVOAlKA7%nFX&qT#C1`r}R0p6wY)IIZ zf7Pv6M=c{W*--T%xc-=0LRRJMTiF%9RYj}4n|z`$VC3D*y!It6*FGS{DP9y|-1%I} zBf@1HRBKeci!HiN1!J;4>uz=rWBe<>R&l&CnxQ?UF5Dfp%8K{yIZJ3NIeYe?L(-E6 zXGCosV*h?ftDiML$`EbaJUTtU2+rt!795T)6wWrn_oy&bmLH`6MHCI2X6E?zJU3_PmF1j=Dmh?cm7z`Uf$& z(Q#gGHa0}}bJg?XY761vWyi%@0yNg{7L8@-`G%{9@AnA5uF|D-U%@d|_fNOFWODm$ za**#=LItsM-}TbFmp*d$;NB;5l=o};I3E^D{ZwOf@tF9x{Am?ml+IciAEn0YMo}mW5m-G!o=J-a>t#&Mixu_?>-MzYb)~IPaj|@8oiR|Q z3`CN%g&q3>`67JOeeq{^Ry;aJmZbI_Zjl~L57TT?zW&CxWz_U!h+u&^-TJ? z`^AVeOvj<%_`Hr|^DyU#nqSQgUL_yUy19=`FF*@T*Ef>I~;op4iw#2}VRib6KLIC5lmh%UzAy z(s7H(2P`k!W&1?D5ksUi6w-GYf|^TQUKS5Ew>ag>HL%qc$kw^#;0n%$R(%J@BAYEhh( zAf-G$kUur4xk+S1<-6ipcK@!E>ll+~kXsopy10w*po1-RedXCbPGfl0&!L80 z``hz;@^aTvGSH_E$HP+&?_^H-c|E%hgm*Yg;qAUU0UKc2NA|z9uCJ8xya-*Xdq zm-k%ZLJGl%@gS0%Q1$YWd(4ERZpnfddR#OO@13T%ycBD$BcE47VIdTQUo7KB*4w@$kKbm z8l2+x>g>ukI&NY4d{?}USMQC3M)SmM_6NDWBX$WQ2+10Rfi!^;onQChMeo--8_udX zGgvO0o{7H zcw2VA^lc{hZh%ZTD#CXxaVe;U6a{8xfQquBmSA$~8E(TfEWrySA%##)t#$TiZ}7 z2OGpxRjNwwZBPO%iW*;RHa1b((HC;8L3Qo`UigHgmr`u`uCk~EF-O&ua70`pWiY(> z>x84rhAqJMM7b}=g)q7$g&Lai3OvWYt+zm_o+^_=8h{JFmL@J)^0_?RP$%}Sn&(RG zn$>HsWy66N;c}yJI6lF}I%-3;jU>>RjVy~B_lkBYT|3>x|?!e>5F!s8je@Y8q&$0m(k4nG~)qu lQ`8nFV(ouDJ<0-eYI33cT0W{u@aHi=v9c#$+qn1ee*u?ZcEbPw literal 0 HcmV?d00001 diff --git a/examples/heightmap_seed_1.png b/examples/heightmap_seed_1.png new file mode 100644 index 0000000000000000000000000000000000000000..5275a52b8a2581772fec4206fbf9f729a6e88784 GIT binary patch literal 7595 zcmeHKdpOiv_x~E>Rz!&CVhn0jb03!(_giL?%aPnlHP=jGWM+&@YK|@>gp}NhkVB-S zBqYgE3b`kVPN|7CkT5|9MP zj)P4Zl*KyYrYRw{*vEqMs&8^KC?me~fjPl>^$#^fk@Cu@5=(Q`#)mJ(g+^0u^G z&#g4FYYgZ2^uaow67+fP_S&9>%`!6ALSDQ-y!z4XLOA;RuNs)M2SB_yr)`oQqZXOr8_cE%Iy>sU>(`f@8G*q0tHUsmOs z5-AMdV$HTL>fec9VRX^e2_8JO-s@eXi z4_UHrA14(Wq~4j=;cdP;F8{7u!E1lb^T(KhPW-~b0d-?rm)N~qYu17p62f-#R*1I2U#rWPTARQi4<4@P6|We1qS$Es@JFgC*s?qT4FaSXI!`m)Jk zLCkGoPHyzD{d6J&ZehN}G=u~K_%nG_Sct!00GAYE24BP_fwFiQ4TmkN@b;U*J;5m3 zaDtdH0*ZjbAniif!8o}25}0WagGF++-TVy#d^3al@_2zHG+H1KpaggnCulDkOC%D} z7#tdhLxLJeZfF3H8iEYq>WCq}V%Rdd^dNR1kIe~yi7}}(4xeWRhlBI5Km79#baea< zAHe;_0>}qCgc^v(qA+NGfAo(QT%KJp2=dLLe`~>Y3k_tVU71`CKZwq>3uXrJbbf?j z(7)RU@`L;qx5J>LnSM-vP?Za=iv7!y;`8v`LQKJ4wtwKF6-f49G$#A1*H zj3FLL#L(zS8k5Gt84`_*Fm&S|peO-c9yNf@6hndFC^m>gz!Hq9OdJzwK&P@m2pS7X zrD2Ikx)IgTh{3`UaYVuo5Y9ntFe|BkKYAsGVu+y(3=LUE#&{%_L8l`LOk*RYG1icd zWKr=(SUiDhK%;&2hCwH7<^=gu!Emzuse75|z<|ArYlsOaS-DWm;5ZcK&k+|tDvt#k zn8CNQ1Nb3-PPnoCncH|&F`HN;LjywtBO@Hq0E@?Cf3S9E264em6k{${wncMsSV-Vz zKw_!lJOu$3$HBdjY=W3n9w*3+!|^kNf3Jz(ryarL#Gvx1wp1Py1pQtm-TtVO8!>ni z=Boo@I7beH%?kZb+Tz0lGX?d*kdxV5aQ)E5p|2-u8}q=|x38~$?8T!5gDoBk5|#dS z3tVb2ld(w{-nuoc(W7fx!erW&j3^PGEwmU_>)U5}BZc!!a=o zV+M;z#QzIAm&4)-s6kAty&#VuS73TB{(is^i-FSrt1kjyrZ@^9VMr_viN*gbVd!rg zMvH64A3HWh|36Ml7ZrXeGN9epF|c@nwGjQi7=GhSTz3A4pKr1FAFcpG|Fg*7;`blA z{-Nt{G4QvP|H-a@==xg>{4M2wvg^M_*OEW4Q_KMHUyuO2EVbS_?*U%4AT$SiTL5Bv z#2UPj0dj$4FD?KqRS`D{AT47hI4HxTINHe!$t$R?#+#mZLOwp?4rq--Ps zVL=j}YZJW{`h|=O203}O^ZO7iw|7WfGO86}zQHJEewPn$^nL#WYhb0v8_6xx2}&rI z6H=Lylxv1te3&0piTd@z;5s?^fS!Wx==oU$@baR-mRTOdC-!ER zM9m&8H}p(K#X!#h=Tv1J8c&5OP%e?B@y+FX(U0CGbQ~f#^yzJZ%UQ}-`l3ue*M_yfA zI@Z`r$;M^($Qq z%O9t7Xx{g3N|l@Dn}{aYc#pmSpt7>8I#1cwM=dQcjaWTTJLfyMU4mYmzGPRbDsV+e zd3A+O-kM(dFzVAOnDW)^1V~f9nP_gq?h8&n+AfXjbv66!JuE2{Ox7d2B@}`7h)MH% zsun`^b={z9gSRLsE$XN0GF7A=rFaUEn9hb!h|qI#Hb*L7_Bn<{s+4+e;rnFBb#B<& z%_VvYrDgRCoVlAk`ZJ*I5aDUc4zJU$&NWh_^x!k4$#=NbFGGA^pG^}k%hzq@=CXhG zda3-oY=ZUng&VJ@JOM76ph|}4Mfh+0xGlF~t4l(dim&&wT}g?Us&ELa@x=hj$>M_= zIe@33u+^$TBU)aCoCL8IwEnJ;3a|rYVR6!O{IL91-#MqUxmshGo-EgroKVn_6sk?y zCUlD=NRwdg^0`U-*Xd^`dkK!|Y8_VSa;9FdZ8+NJlb`&+l0>W6rRVh|8Pe2;b9gXU znlY!{uq*bKO@qBjHs&&IL|Jx=gWVRL(@EhnuMl!6>^9wX64$Apf-3uW_S7CHA&+H5 zv@kzmET!%3`zuz!+T|c}m0r#|cx{BuW_`OQe9xNICFpl?>563! z+c-4H-GgT;Cxj;5Sb0Qv0eE2@p7q`*hq(w$!gB zr6HGXXv1ABm_Bsxie^U#vqIB8;d;%C9=q|(I?uT1WW~FKgc#|&)uEHFZys!XV-}NM zf4Uu;)!?mWMFp%+TDU;1qJie@J=pf7_*Vhb(*rg4-D-ThYEX2GpTg4C<&w+mzr2FC z$0d!*(<(2o@{@Y%T5D+&F;%epleTFvzjV2M{`kDD)vN`4(#(~vxUaT<~i(OdQ{Kg@eQ#i_o&Hb0>)5&6;z zIEbb$?^+Y2O@+(iwIww0r5Sx=l7;t`MivEANMIwYYnC*kG+#Vou`)H zkf4j-^!b9YRs$~IL*4Tc0|{1N&ve~PFw&MEoH#V9xUGKf<5;>zwOgLU$zQeioQ|q* zzX*w3)hT(lbgTDSs0xaT6Xe<5YhJCPIQcVY4ba#8Oe0b^W*K#5rGTuIDr|!4E$8Xt zHbEPpS(a;-cRmw6)>4(5`hvMry;4ugRqD~h>-?d6(3|93lf+!1{vNV2BPlWilCUqo zpe)3in#9(S$1;u`oi*fkSvhFvHNX&39+&v7gFeZrHv<%M*6a`!WL~^KaG9=`Fyw>D zZf$D3ibRs+&grNSRFo)35Q@(z_jx57qe5A~@%C|FZQG!Oz1p;{@YxBU&*|#DAGfD9 zhpJX|YC8yz$3~Kf(X4pSA;Xl1vvNHyvbn$;&#bPQ9`y_UC8!cw#&jFm#cGd! z-D-&A>ZYM%{Gxaut7-Q3Y`x6(U!mv1(8G?OdfqC|O5N)ysZxcFT*20KQVQ>B8wVPU z7COHReZDUHxrv3oR>QH1SmuhKuIrvZuruy9ar0sIGDdi}elf;#D3# zw9Wj9o)1n}Tkz<4M%>MjC>_5xGxE>JQ4q-GRriX^)avTrmZrl8B+NP#&#+)IRSHMu zds2Jigs#$=Q+$g(HL_3*D}4jM^pxiOxtzGi78Nt1u*g?e^0g!^!wT}`k8oN9?}m-; zH{bN;80@R8&U~Y%nU&kg{XFEru71Sko_q6!l_%^rcYofda_`+;OC_eS5c zEp1K9qPjdqw^N@Wl(t0f%*?#NdE9{+RaN&0o(e9(#pjbI#sLEAhLl5J{1JtLkS}j9 zPSa~rgTo@YtSZvT2g3A#PoM7%@9^%P#^9J)8kf|+Q$~c)J2}1nH;Fps{e6AiIf}hq zHy;g;1V*sByVJMpGY#S=tECXQvRw-=Rj!q*Us#yqe;yttrRx*uMW2l=G%X|584n9p zM8CW6{o9;mItLC+y*A9SkzRg658HXIWnrl6&|!W>4Jp~Z&(rrqSK(OPscDJ42cEVq z#_<;S({Jct%c2Jgoze?G>4`>?@9VA}O}$*!)BNIeN|e!nDCF~p5x%?H#K}UV_3n|X4{{oGRxN1l=h8kDW;UD)bu<=M^n&`J_K(+ z-Z60cr%UZk`b(FY2b2utm523ImAD7oJDh1T>(jD3^E2zl3(jZ93m$p5J~}r})tZ|X z9TK>C?MHas@sYlu932_jeef54z#DRCJpJj(E{tWz&~9^m|J4>172_VeW8bBFOETxHP{SQ=t_2REJ=ZK!bh_W9dvpY@k7TzQ?Zf4uqWOfac`?MBg7 z=GYLw^oon_8*{MUlzFB1R_omQ5HX^UsEurF&lUO z&wIAc6C}$&=Z>6g+2Qmd$+=0&p<7{MiqoxrB~(+M-$eH`Sv&n2;<0`pP>oBMxi#am z#$~S3QK2AbgvRXVn9O!Ebb(Zrg_)qS*=C`J2X^)PI`iqrsI+vC#>0oEE-Z_GO`CbT z>x@qSBXfCs5%%eUs^_~MjV2UbY%PzJmps<<>3BBLoV88q;xTuOV1CvjqpW-9sosg(q#6GwU@%*@C;JUzwIl=_`im9(nOk>gVj#n^Rr>fx5Tx0ve^r1?(=;0hm~zkNGMuquCsL_N)mv$(mbikiPf2;oMhJ% zyC7;a^sy1XqV0a}L;KP)wdIOE`JoN3R4tVUGke01roo!|Y31j)>>q5qR3K<-PET+j zCh%KikSoo7PuKx^jwjY8CM6WG;&PHb&LmcsRZMvWK<0|nd=+;D9(wntHT1N)pN*+cfah_AeiARX83cshc}p zwX$6v^8Uah;#{9w(Mo*QDfz1hv%d)NT#Z(lH^1R@SP9frwqhSV^gZc9fA71{v6{5^ zDLHZtQ_=oc>_?LiO%FPZIfmji+HJZ`w5mG87RsfQX3p&(oLZ1ono)t=>K?x}HgT(e fLH_+;9}Am3W}AacV#C1896+&ivb|!xC-VOQUT_51 literal 0 HcmV?d00001 diff --git a/examples/heightmap_seed_2.png b/examples/heightmap_seed_2.png new file mode 100644 index 0000000000000000000000000000000000000000..34a0206d991cecb4d7045d3884099652f1ce3479 GIT binary patch literal 7371 zcmeHKcT`i^x4r~KKsu;2Aw&cVA%&g+=_L$7q(~8jG$2X{B%w+Z6-1<101-rr^o}5d zNKsUz2?~m|K}12iioi&HH|RLd@4dC&nzi2hZ*JDf*>|74zq8MG_SwmawzM$e7YR0D zP-fY%ngrXnt^N`yOMaKHf%5+EHV{xou|WVnuulUU8k9xBz7TAbsBd#MfHLdh+WMG+ z5)1jy-UN%Ygrb#DNF`+r&>x9b#h}m_lrj{pj8Rs_AT>d2wv4|bVj(51NrMnF3LRe zyXJQJ5A1!BoXhw-(|N6mYoAnVCLi@buAMBp>#aF1Dv*j&&Xu$Il9Pfl(U>uF=7G!| z;LhQ$2x4^F3|%nOtd+VD2^-FprQLb9a1EW*Vsk=%G|nbnUSOed#~wzNll)zr~wlsfMR3Mw8xoah%~Aa zo(^VEH(ixod9n<@f4W}C6L`IUfQtvx<(k3LehrWsp610Zv(Oi#WaXcwhpqe zAqJfyYLZ|&hk3LDFdzVx%)~>5V}k{Qdov{FRkxbaw zIA{T91bQ*?0dOzIUKYeR3_~)5NT+x+DKsxA3lmSE`7*U(FmN3DXM9v|9PS6a7vnn% zARmYTyf*@+ghWuOh@U+eOd~%KqX)w#(3^~~A~R^dbRyZvkL<91;;h_9Rn5QwBII>TgrBj>8WR76tAUs`r)`NcP_>nH0Cb$ogAstdXtd{2U1A z{sZ@K)_<;j%NVr6;V_0YqAx2vtf4lHH9v+#BT`71Em1=ik0+yuNH{?itqNBmp$TwJ z4T36M!%f2tg~Fo=1P#JZP*^Vp6YoVNv!Fn5B?^e6u7O7qRW(s?H zhy*n?G+CXXp^DmqA`vmhG&&UzZYPC`cPAsfz1+73ScGHrEV0@!v=Z_!jio1^=>|Gz z!^|mOz5#y?*ifisYbKt>CQ4mRRZUd`h1AqUs;R1ci+Kz07@5uhGm(Y4RoJ%NS=)jE z7XuQDXXPmfu%!prf-#_z@k|=shDP($hW#jsKZbGOaU$WFctbps41)eBk~V)9$wNqG z4Dws}EI1sEL~#rJ->g}O2dV|ygIjJ&VSw`oZmGVVC~LCMx3_Pvo|LVl1ch!L3Jjk3 zZ3zs#ADOfjCy4cJi0Fa$awmh;<9oXPsi*u8sesl1_efn;6;4zmk-+^^N5b(aH5IrD zo~(={tCR6)xBozA(A=2*csg0n9pn+@3QW(fpAYE%t&KYH_gMTr$gEue34^21aFp_Y z5{CG`U<9jX{JCN+#DC*NYs=uLA_Mw;(}Bectc8dl#qc|4tg`d}_Gsr%7o5bep)ye7#_bb`R$EXbIjCcn91%vRflHjM&8Jt_`(jZzD&+wMZJveV%~_S z&DUccjwIWW&P7GJ^KbHP4($tueB^FY0B!+Wf=*y{jE;IHQV@v&9zual3m^-p%;3^< z-}lF9lZ=bknlFvp`1fio3lX>ZK(-`n6FQk60tJyVmysOKk?{vb8$7IvX*>$-#i)=G~?_ze3^v;cDx}SPfOMu=y za?YC~4iTR=v4Yit<@QN$wTZ%GLzjK?Z$*6cyU`#9@SeWm7`)n|V-es^=FW5V1?RIwSiD;J{;9-vHDjdbD*y;f!g)4Tb0!ht@N zB)zk^lDk~kOGz+x;>?^y=YmS6aa`pm^W)tEDnh+e&+QZCX8H(^``K-Y*rxHJvTpgN z(7pu(%^20WYFzljTrARkc_UtmE*#&*Qf*P)7rpx)OBG>tc zH>~19OW~7i2cKPz`aMiKTwR&4+L_n5QL&yvYGPV(o}uSK!~JT@c2zD{Iv|ri4Hru^ z3Sxs9dQ~FC!GF898 zeAHBK=}{pgJ{@HO0D8|BRK#VQ<_2EGs6cK%RFkvhO^z}QKMBuDX9KVWMqF7?2}Lb4 z-Eg-Z?_cjf!`JL!ykv4BAP(+&RX+Ol^L&#?nQb(cokB0NRd;dnY}0i3ydWkTDY4Tk z)mHVQ?r+k^5t#`W0JB}~vg8zp(zka2UE8DR@MG^INtrwe=b@5V5h#Z#Q(qcM)rmKF zJGW=F@E|@nvEz2Ds|tQFSeKJ$6yhx3wn*wiHvECp4sR(vt!*OeEO@}k^3ju?z1uPr z&jGGiq#2RCpHLU(?IM?WtopT!xJyo1McAy_6xdel=@G@lBY?t>jt03?>urK^XmP{$ z5d}dClK6b16r_j7Z16jy+pRG14l~P4&fdvk}*!%LDxPzj={s-(_caX^Cq6Y3^6K+t*XLkNZuJzU62Uyd8G;&X`X&qf0MMb6zXsdAyMaY;(Rx_fgeHwfxSR&hN#;<_qA#h-JLqR`dJ zs_Pe_l)9OYg9bQKti<3Dp=K8(r{FSa@za;lYfCe4fs~UMBiGip2{ilh~ ziqCw$y3Uq;?`BGCcz#ko8J@k1*PNbILxsvlkK3eVRB=ZRIGV&gc+kIS7y%Fg^l(jGBimHj*K?#(@Ryk)n{ z_K#-5Pvu(4lj~ADH!G&|x(0>q9LwuARs{Oe?_i*Cv&oK`j?v3&>k61uf~%jWiWcHLL#1$WBjs9fT|p}(kpd^bWs>;8EZF{@Z>8# z*?&Ri)$T}LiLpkU1X<3-NJod-rzDFq^Ld59p?W7Z4sa`RCXr9dal0O8d0phLv94d zH$puo8M&<*D|EH$Q{s788qqe^Ic9Nmjzd8)($ihGMc!d8)34q^&auvlL)9}O$P<;a zqfxAGa9;YvaEhRqB4rX0d&jvgKd6Yth<2t4J|7>pAOv{|h2whmsMpc*dYadZEGl)Y zUiFM^bX3S^e&S!4$y$Iss~DMgyCT-S%jtM2hj`f9)Qp;~keYGPTdVLS)CqWh)U&kjt40s$JowqYhFnS~_3af*V^u$V_7VTu#)y!n$83)d~XQ zcCjtBA!E%ix${+=MvCl9T8Y|5ynmm7j)Z;tB>OH~cta#j_i z#P2&z=ruCQ+YLZ(AMH`)pAasH%`+Tq2=6E?gH}c9)7@=$yN| z<5XSxiABU`{QClJskkV{;pbVwke#RB<9R~U?d0Cnm-W61wGH-|Ht4G@5r`r-jF2m1 zfbx6%o~yfYXGZVG-Yv2Ya@=f4YcG7ybefIl1R|Xe-zr_4M|}=kI9%K{R#x!aFLqWf z3--S+eBCX5f~EkOSZEvy)trkPS-qy=epA)>$j4x(NS&$k#*ma0>5*9l8H*69BVl!p zp&3(n(Q?Jb3WUmwr!&)2jpnnft5H3Z_rK(D^&wAo#`87#)#feT-$Y2y$B*4FuHWsX z1VqP&6}{5!TbI^C&i7R)X4};_yo?cWn5`5AL_W8tRTh>_*Xle|k_o=E3f?`2GBMQ! zuO1B~x48!_iNK(_N1ryo^ZjiliZpzhOZdv5>`jsC(kFN7Hy&$2E7VYqDbW*7$q-;a z<(J1F8Ia%J=_txo778c)UN#Zx%5Pe%*ki>%*r*;}2osi`5!ux{FMUR)tfcJ7tK(hQ i*kyb}{&wZqWcNsLd$C&%lM3E;0IZRPVX3}r#D4+dUc2}J literal 0 HcmV?d00001 diff --git a/examples/heightmap_seed_3.png b/examples/heightmap_seed_3.png new file mode 100644 index 0000000000000000000000000000000000000000..7f591c9e3e9dd269aef295899aa786fa38d73841 GIT binary patch literal 7506 zcmeHKc{tST+y5G4jSPj6r7>Exj9G1S>?Vw;WNEX_@*T!D!;CdCC{!pa5lK1JK_%ok zSt3P+h!#t`tR+hJ;{A>~ozDCIuIs(db-nL@=R4Ok&;7lh`~E!l^Lg&)o_P-M*k&y& zy-FGY09mRHWhc}NgiR9q0tTE-l%cL2+)ZUssT%>I60#J510s+lfSSlpGE77i5QTK{ zP*Z?pp^dPu5)oVKlOS33yL=LowZGfIAQ>+L17x8-8)^hdUJ3PuP-7>2o2wR*g^%RY z#|Dyw$ba_MRQnw$f({<1Lo|T=aRgm5o@NK!1`#!jQ- z4{K64)W&cw$aR_{A|-Z-c`d4{tF^p2)UX(6Ft|8Y(_VT4hv}U^&T@V|7x%D#=)QY+ zp5&JA}CoefL&gbWXpvj^(h$sYBroG1u3J3^)hu-ZB<4wED>E z+aV`esWa5-75S;OvUeBnZT|9+^!xDb?mK@B7gcZ#2Uf~u;fV!noIdAglC2Hi+qy}^ zK5UT6m%0|gYjt{-XlwX~T4Fr8_zDy*0%;oOzKRvSTcPK%^*iO6oyY21B z4357Jjme>dI${1?A)A1aX&9Hr@CErOI_Sj?Fh-A6-9)3Sgu-!Fhi4l_nC;gTU)sK%mEOD23@WAtullPx(x zAc~|z(!pW2gt0>iXcK9aQ4o_w-bu0g4gtLxqrLfjE*Xmr4Gq-^CF*d3ys&sfLqjZ% zfF%$xkOhVp9>AxCVFGwsLWpk|6p+UVVsrUyP5?@XNuzUu`Nn88G>-Z+K7X#g{SWv6 z-gg!tKCodlE*7tY!}|MUfA-+^OClYs^OK!3=T2hEEA+myoN@WVq$ffw7KyW|Ct{WnWKoAno2e~V2xvb3C^ z1A*Ls;Qr0}&$TZZLss_oWD187EDVoIF-8mLCo?$=Hj}(05{U#BQ5O$lSPVl$42h(# zk1;UNr(+Ba=mrc>kEn+y(tm=Y2JrZ_00t<8g1~jy5RNWGpGGHvEDXtzp^qWy>5?#p zpsoSNP}dN|;h1zLi=q1y#P%RIl$A8UpQ92&F(D{JkVRk_=;ASSf`KlEM8|;`8jeK5 z5OwwR^@vO&NsmEWf?_hrR-7Pz8nm5kf0`GFbd2Oaq|A)9!8JzYILJkC(pK$k!y=>28n0tWG*OcY`+6}F`j;kJ;W z#X!W;gn0@9Ea{=OkS&8i8lMy7#NqfEqkojdAH(+0abnW=GzyImLZCm2q|={8aube7 z#(fK42xre>vRUE(o3-%ppo}1UXv=NbJZS##CDpeR

6Td;9k4$6h*0DAdxSAk!G% zmcXNhfXt;hA*^ph3~yS17YJ34@9Fxdp8X$EfxysXundSa42}fiFeC;JkD;+Z0}O+t zi#OC|fGixr@bBn64vQa33j)o(ARZyEp!8h&`9NteZPbRpTZejs!d-v}!{7-RJn6p^ zhW)-^tgvSMxnd*i|KP-E$>66V1NnW^LB$KIh1eg(@H=P1vh)A=eBX=z#~C2#e<%4z z{QgVVzjXa02L6%pzuEOKUH^!Ie`Ne`cKx5xCH>cR3Jie$1%*PFrH^~6ZJ~=6jK0;H z0>CJ778=l-6xU`C4*-^}5H=CuOg0iyO7N-nTO@{MWEGM6Bijx`*IZc_D#hF>tY>uJ z7>8jUR6LS1ZDU(i(YIQ8b!>y?AMn}5#q%gL?zZ=3HAQZhZE7>G+)uC$e=4!{g3FC6 z>{c^@q{CI;8yBmJeB-}pEnK@=w;nhLM8d|UmV_^H9-~QZYertIUn65FNrKDd;!ubP zB|v`s`Sv4z$)#&c>{ZHNufhWvQA%=x>OtxQJ2_SP&V$e98XNnO8&`{&ssrr!u?U2G zV5)UG!f-9&xMEiLz5~^dcNnVxanwE?cuK{*+d^22b_M)Vy`j7NLdtyT;^pGJ94+{( z=g}F1%Gll@OwsRKC6l)XctlM-4q5C|d}q~k5msVBn_WKNIq+%A?ZL}s%R5b5?<7xm zxz%>LzkMA#q3hcBn?}jIoXr^p%GjQjlZvLl%rqCxMe?3`jK#kE5Yb!xg*|;XM+Rsd ze?EoP8O&Um`}Aqz^0cf+c|&06+dD_Gb5s5OQ}e2m{VG#+&viHYRCm?Xn|ip0jtuJa zSYKa{moZQtN#|NCE}vgt@S0OSJ^Z^&sZWyOKCPyqz*8O%&TZcnXcU0jVmCopol_|> zQ{-du!o}c9SBmL_yss~nv#wSo81Mti=<~M2{o|cg8NYe&+5TK~OnE!*)->*%~e@q+|iDfc-#NyVViA46k7g_~Z!yR|cp9|>u z`~my)z{8-j?uv#=tYWWL%XaS;&ldt9wm$sK;Alm2-+X2F*P_$;4zbG!I!5Zu)(ACZD!D(IBsSgcQyOD!}I-N9@`@+l9H!4Y`m42o};JIL6aYn9UKlT zK6BxEzE9+|+t}7!NiU9xkHwEm0xm@M6^u#lLDo5 zwN#W=kKJPR%Q25+@TQG3V^75C?cl-C(z1{c1z3d`l21RQ+T2i1LpFb$Oiu_+SVi7l zla5zqhtlo~ za(4@i)EA}e9Bl)cS!vx0Wl~{jP8wPW^HVWv6jL!7=?BlZqHk`m4ql96u2M*^ks_f` z;>FU|z8CEUf<(d56^Y65xvd>vbKr?j`Z|waJO^}_rcw;*_<`IrG71^67zOWMTh=;> zf}YKDf_u?2nwn;M$Q6$VtSWF|;M5zN?sai8D>g4nYEeR=V-Bl$$rOZ&?C#rA@#G-Y zC;6E@Wg3NmrwRPC;0ST_lY2&~|(hOZiiPi3Udxoz< zg6qTL-)=lmE+LsE$Unk5nu>2tcF8yI0cbE$&$4!JM4b&IUd*XG3?^`PMJCy<*D6rS zB`Ov~u>;fU1){mKT_B>OsS-OmCdNh2i?WqON*CnZimrX&jMT!3iY@`MxC?V zGSSfeb?X=C>0|}--1}*^70ZU6`=Q?ovV9v&uNB;(j47DtESoWxkT3#%ybhf~MW7kW!SY_YKY#S4>5c-xwu(|6b)D=~}oe zTiSe}I@5evrk=KQa^WYohD{2dc6;NI6>ngkXK#1(T2$~o+7C3knng{O`rQ20pZ*Du zDpx|)(G@D3YpkxXv(pikZ@gh*5%Atu-P#~S*NeqhcYpdO!Dhq9$4?EXtmP9aNV-+Q zS=1|9iEU|N-93y^(i1-m*Ix`?o!Df9_95EWcR8!-YlM00RL&f``r`fR#JabVXyvz$ zw8gf+>%4S6qrif$=gfMLM46>{mP`AHQ@9uepmxGRu1&0y?^XvKmzS*CB7-!)V${qK zs3IHTkD2S@T97D88i?S-8QOVx+tjLt4p+H@bz=)fGgGKj=i&E~k*luU_q02Be&=nq zw8&L|Jnh!r;JY?Xpppo`^($ug-usCm>rE>s>*T7G$6hIc;+f(^L8*U1h2$=k%-=2; zW~?LHHS8`!SeQv}*!Uz|3HL%x+5BF@XORQbK~}z(2z3Bjs>+pqL$|meut7)*60MdI zE}hEhkx|}lUfTNZZBA+b#}X@kwq8=#h!u;1WrUoLUG72JKip{4-GMt<G+m zXtniXuByhA<7u{h?qM`$`$;9Qt0CT! z07m>fBp=;~kx)@`bD2ao-G{}-i`|kbwCgcBRWIOda?tg;8}t(UH7ek<=KA;Ih~tM* zYE0gi&?k0}1nR!$Em64!vN&MVEySi6TYH6qp5`gq%Vwi}h3ar=l6D7OV6ch}zbk(! z7__kGZ+9pbZ z=sxv_jxd%Uz&KxI|B}%ZKV|HiyEW(As`|B2V%%C04YeH)$mO5TrzUq4${m*< ze;Qp`e9n1c^FXtn)fmU;h>xWu0Wh3%L0=ry)zK_ewN-cel_y+aK5*Pkd zs_bQlBI%)1;!m?=9>GTC9O}(fKEzVCly2^BXoJ6cB36retRoK3tc)uA+_#{8S4{oU zd_{g0&!&x=JQ(Mwdi4TH(#%efhwM1^i-uJ&44?kcy2k(EsTNHmIUtS6+DRV13s)iD z$mL5UY?g3p9&*!BI`W&>4XPLlohNNO<6>XnW|@0LYe&s7fwifOs#u+ob894yN52$ZSNEX9`$5fJW-isomssMzJfgU>t^HLO1OF_z!eS=2jrK)dV>4{x4+BTjiZ>_#Q-sHwa=W5^e`K>YBq^oG9 zWn);{SKd2;l=oS9>IrgQdwlH590kFI)Zu%$?xw_Bq{Sx-w;HGVM1qoZW7Wl()DheZ=_6{;Ls%q7NiWj<=+%QKa7Q8mf_-9aDrM2Os4G-Sa!!X|<7f{LPpu9;;qy3FrVbopD-LM^W|3Hrl{vK8nIYerHu^lD zJgZ%GjL, 2> & result, float scale) { - auto noise_1 = make_perlin(rng, 3, 6, 2.f); - auto noise_2 = make_perlin(rng, 3, 6, 2.f); + auto noise_1 = make_perlin(rng, 4, 6, 2.f); + auto noise_2 = make_perlin(rng, 4, 6, 2.f); for (int y = 0; y < result.height(); ++y) { @@ -61,31 +61,34 @@ struct weather_app { static constexpr int N = 128; + const bool static_mode = true; + const float dt = 20.f; - const float viscosity = 0.f; + const float viscosity = static_mode ? 0.005f : 0.f; const float advection_magnification = 1.f; const float temperature_diffusion = 0.001f; - const float cooling = 0.1f / 300.f; + const float cooling = 0.01f / 300.f; const float cooling_factor = std::exp(- cooling * dt); const float heating = 323.f * (std::exp(cooling * dt) - 1.f) / dt; const float coriolis = 0.f; const float coriolis_bands = 2.f; const float friction = 0.f; - const float slope_force = 4.f; + const float slope_force = 0.05f; const float vorticity_confinement = 0.f; - const float evaporation = 0.01f; + const float elevation_temperature_drop = 30.f; + const float evaporation = 1.0f; const float max_humidity_factor = 100.f; - const float precipitation_factor = 0.0003f; + const float precipitation_factor = 0.0001f; const float force_field_amplitude = 0.00005f; - const float random_forces = 1.f; + const float random_forces = 0.25f * (static_mode ? 0.f : 1.f); const float force_field_switch_duration = 720.f * 7.5f; // 7.5 days const int force_field_switch_frames = std::round(force_field_switch_duration / dt); // const float friction_factor = 1.f - std::exp(- friction * dt); const bool periodic_x = true; - random::generator rng{random::device{}}; - // random::generator rng{0, 0}; + // random::generator rng{random::device{}}; + random::generator rng{0, 0}; gfx::pixmap_rgba biomes_map; @@ -146,9 +149,6 @@ struct weather_app float latitude = (N * 0.5f - y) * 2.f / N; velocity_(x, y) = random_velocity(rng) * 0.f + 0.f * math::vector{-std::cos(0.5f * float(math::pi) * latitude * coriolis_bands), 0.f}; temperature_(x, y) = expected_temperature_at(y); - - float max_humidity = std::max(0.f, temperature_(x, y) - 223.f) * max_humidity_factor; - humidity_(x, y) = max_humidity + dt * evaporation * std::max(0.f, temperature_(x, y) - 273.f) * (1.f - precipitation_factor * dt) / precipitation_factor / dt; } } @@ -167,6 +167,28 @@ struct weather_app } } + auto heightmap = gfx::read_image(io::file_istream{std::filesystem::path{PSEMEK_EXAMPLES_DIR} / "heightmap_seed_3.png"}); + + for (int y = 0; y < N; ++y) + { + for (int x = 0; x < N; ++x) + { + terrain_(x, y) = ((heightmap(x, y) / 255.f) * 2048.f - 512.f) / 1024.f; + } + } + + for (int y = 0; y < N; ++y) + { + for (int x = 0; x < N; ++x) + { + if (terrain_(x, y) > 0.f) + continue; + + float max_humidity = std::max(0.f, temperature_(x, y) - 223.f) * max_humidity_factor; + humidity_(x, y) = max_humidity + dt * evaporation * std::max(0.f, temperature_(x, y) - 273.f) * (1.f - precipitation_factor * dt) / precipitation_factor / dt; + } + } + make_force_field(rng, force_field_main_, 0.5f); make_force_field(rng, force_field_next_, 0.5f); @@ -249,7 +271,7 @@ struct weather_app float max_humidity = std::max(0.f, temperature_(x, y) - 223.f) * max_humidity_factor; float discharge = std::max(0.f, humidity_(x, y) - max_humidity) * precipitation_factor * dt; humidity_(x, y) -= discharge; - precipitation_(x, y) = discharge; + precipitation_(x, y) = discharge / dt; } } @@ -373,8 +395,8 @@ struct weather_app // velocity_(x, y) *= std::min(1.f, slope_factor); // velocity_(x, y) -= terrain_gradient * slope_force * dt; - [[maybe_unused]] float slope_factor = std::exp(- dt * slope_force * std::pow(math::length(terrain_gradient), 4.f)); - // [[maybe_unused]] float slope_factor = std::exp(- dt * slope_force * std::pow(std::max(0.f, terrain_(x, y)), 1.f)); + // [[maybe_unused]] float slope_factor = std::exp(- dt * slope_force * std::pow(math::length(terrain_gradient), 4.f)); + [[maybe_unused]] float slope_factor = std::exp(- dt * slope_force * std::pow(std::max(0.f, terrain_(x, y)), 1.f)); velocity_(x, y) *= slope_factor; // Directional external force @@ -465,7 +487,7 @@ struct weather_app { if (!periodic_x) { - float left_boundary_flow = 0.f;//0.01f * std::sin((i * 1.f / N) * float(math::pi) * 4.f); + float left_boundary_flow = 0.01f;//0.01f * std::sin((i * 1.f / N) * float(math::pi) * 4.f); float right_boundary_flow = -left_boundary_flow; velocity_(1, i)[0] = left_boundary_flow; @@ -476,6 +498,9 @@ struct weather_app } velocity_(i, 0)[1] = -velocity_(i, 1)[1]; velocity_(i, N-2)[1] = -velocity_(i, N-2)[1]; + + velocity_(i, 1)[0] = 0.01f; + velocity_(i, N-2)[0] = 0.01f; } // Uncomment to visualize the force field @@ -505,8 +530,13 @@ struct weather_app { for (int x = 0; x < N; ++x) { - float t = 1.f / frame_; + // float t = 1.f / frame_; // float t = 1.f / std::min(8192, frame_); + float t = 1.f / std::min(86400.f / dt, frame_); // year average + + if (static_mode) + t = 1.f; + average_temperature_(x, y) = math::lerp(average_temperature_(x, y), temperature_(x, y), t); average_precipitation_(x, y) = math::lerp(average_precipitation_(x, y), precipitation_(x, y), t); } @@ -610,7 +640,7 @@ struct weather_app auto map_biome = [this](float temperature, float precipitation) { auto x = math::clamp(math::unlerp({ -3.f, 3.f}, precipitation) * biomes_map.width() , {0, biomes_map.width() - 1}); - auto y = math::clamp(math::unlerp({-10.f, 40.f}, temperature ) * biomes_map.height(), {0, biomes_map.height() - 1}); + auto y = math::clamp(math::unlerp({-10.f, 30.f}, temperature ) * biomes_map.height(), {0, biomes_map.height() - 1}); return gfx::to_colorf(biomes_map(x, y)); }; @@ -633,10 +663,10 @@ struct weather_app else { if (terrain_(x, y) <= 0.f) - color = map_color(4.f * terrain_(x, y), {0.f, 0.f, 0.125f, 1.f}, {0.f, 1.f, 1.5f, 1.f}); + color = map_color(8.f * terrain_(x, y), {0.f, 0.f, 0.125f, 1.f}, {0.f, 1.f, 1.5f, 1.f}); else { - float temperature = average_temperature_(x, y) - 273.f - std::max(0.f, terrain_(x, y)) * 6.5f; + float temperature = average_temperature_(x, y) - 273.f - std::max(0.f, terrain_(x, y)) * elevation_temperature_drop; // float precipitation = (std::log10(std::max(1e-9f, average_precipitation_(x, y))) + 3.f) * 2.f; // float precipitation = std::pow(average_precipitation_(x, y) * 125.f, 2.f) * 8.f; float precipitation = std::log2(std::max(1e-9f, average_precipitation_(x, y))); @@ -652,7 +682,7 @@ struct weather_app (terrain_(x, y + 1) - terrain_(x, y - 1)) / 2.f, }; - auto terrain_normal = math::normalized(math::vector{-terrain_gradient[0], -terrain_gradient[1], 0.5f}); + auto terrain_normal = math::normalized(math::vector{-terrain_gradient[0], -terrain_gradient[1], 0.125f}); float lightness = 0.5f + 0.5f * math::dot(terrain_normal, math::normalized(math::vector{1.f, 2.f, 3.f})); @@ -673,13 +703,13 @@ struct weather_app color = gfx::blend(color, map_color(10000.f * pressure_(x, y), {0.f, 0.f, 1.f, 0.75f}, {1.f, 0.f, 0.f, 0.75f})); if (show_water_vapor_) - color = gfx::blend(color, map_color(humidity_(x, y) * 0.001f, {0.f, 1.f, 1.f, -0.75f}, {0.f, 1.f, 1.f, 0.75f})); + color = gfx::blend(color, map_color(humidity_(x, y) * 0.00001f, {0.f, 1.f, 1.f, -0.75f}, {0.f, 1.f, 1.f, 0.75f})); if (show_precipitation_) { // color = gfx::blend(color, map_color(precipitation_(x, y), {1.f, 1.f, 1.f, -1.f}, {1.f, 1.f, 1.f, 1.f})); - float alpha = 2.f / (1.f + std::exp(- precipitation_(x, y))) - 1.f; + float alpha = 2.f / (1.f + std::exp(- 0.1f * precipitation_(x, y))) - 1.f; gfx::color_4f cloud_color{1.f, 1.f, 1.f, alpha * 0.875f}; From 0e813c330729099d34c963f1e9f655e00b9a01ee Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 25 Jan 2026 14:20:36 +0300 Subject: [PATCH 123/141] Fix compilation on gcc-15: int template arguments can't be implicitly casted to size_t --- libs/math/include/psemek/math/swizzle.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/math/include/psemek/math/swizzle.hpp b/libs/math/include/psemek/math/swizzle.hpp index a60e5f0e..d2232c9f 100644 --- a/libs/math/include/psemek/math/swizzle.hpp +++ b/libs/math/include/psemek/math/swizzle.hpp @@ -24,7 +24,7 @@ namespace psemek::math #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Warray-bounds" - template + template constexpr auto swizzle = [](auto const & x){ static_assert(((I == -1 || I < std::decay_t::static_dimension) && ...)); using result = typename detail::swizzle_rebind>::template type; From 339de6b2181f3ea65614abac87eef7ecd4783e0f Mon Sep 17 00:00:00 2001 From: lisyarus Date: Tue, 3 Feb 2026 23:44:46 +0300 Subject: [PATCH 124/141] Handle negative values as zeroes in statistics log buckets --- libs/util/include/psemek/util/statistics.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/util/include/psemek/util/statistics.hpp b/libs/util/include/psemek/util/statistics.hpp index 7a747f7a..701692d2 100644 --- a/libs/util/include/psemek/util/statistics.hpp +++ b/libs/util/include/psemek/util/statistics.hpp @@ -268,7 +268,7 @@ namespace psemek::util void statistics_log_bucket::push(T const & value, std::size_t count) { lite().push(value, count); - if (value == T{0}) [[unlikely]] + if (value <= T{0}) [[unlikely]] ++zero_count_; else { From 2f781525d7b9ac36794d43fbba3f0a0da4fd4e0d Mon Sep 17 00:00:00 2001 From: lisyarus Date: Tue, 3 Feb 2026 23:45:00 +0300 Subject: [PATCH 125/141] Implement math::concat for arbitrary types, not just vectors --- libs/math/include/psemek/math/concat.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/math/include/psemek/math/concat.hpp b/libs/math/include/psemek/math/concat.hpp index 9da409fc..db871c53 100644 --- a/libs/math/include/psemek/math/concat.hpp +++ b/libs/math/include/psemek/math/concat.hpp @@ -1,18 +1,18 @@ #pragma once -#include +#include namespace psemek::math { - template - auto concat(vector const & ... vs) + template