Files
test/source/blender/blenlib/intern/rand.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

412 lines
8.2 KiB
C++
Raw Normal View History

/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bli
2011-02-27 20:37:56 +00:00
*/
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <random>
#include "MEM_guardedalloc.h"
#include "BLI_bitmap.h"
#include "BLI_compiler_compat.h"
#include "BLI_math_vector.h"
#include "BLI_noise.h"
#include "BLI_rand.h"
#include "BLI_rand.hh"
#include "BLI_sys_types.h"
#include "BLI_threads.h"
2017-11-14 16:10:48 +11:00
#include "BLI_strict_flags.h" /* IWYU pragma: keep. Keep last. */
#define hash BLI_noise_hash_uchar_512
2011-05-10 14:48:06 +00:00
/**
* Random Number Generator.
*/
2005-07-25 20:56:48 +00:00
struct RNG {
blender::RandomNumberGenerator rng;
MEM_CXX_CLASS_ALLOC_FUNCS("RNG")
2005-07-25 20:56:48 +00:00
};
2002-10-12 11:37:38 +00:00
RNG *BLI_rng_new(uint seed)
{
RNG *rng = new RNG();
rng->rng.seed(seed);
return rng;
2002-10-12 11:37:38 +00:00
}
RNG *BLI_rng_new_srandom(uint seed)
{
RNG *rng = new RNG();
rng->rng.seed_random(seed);
return rng;
}
void BLI_rng_free(RNG *rng)
{
delete rng;
2002-10-12 11:37:38 +00:00
}
void BLI_rng_seed(RNG *rng, uint seed)
{
rng->rng.seed(seed);
2002-10-12 11:37:38 +00:00
}
void BLI_rng_srandom(RNG *rng, uint seed)
{
rng->rng.seed_random(seed);
2014-07-20 00:38:52 +10:00
}
void BLI_rng_get_char_n(RNG *rng, char *bytes, size_t bytes_len)
{
rng->rng.get_bytes(blender::MutableSpan(bytes, int64_t(bytes_len)));
}
2014-07-20 00:38:52 +10:00
int BLI_rng_get_int(RNG *rng)
{
return rng->rng.get_int32();
2002-10-12 11:37:38 +00:00
}
uint BLI_rng_get_uint(RNG *rng)
{
return rng->rng.get_uint32();
}
double BLI_rng_get_double(RNG *rng)
{
return rng->rng.get_double();
}
2002-10-12 11:37:38 +00:00
float BLI_rng_get_float(RNG *rng)
{
return rng->rng.get_float();
2002-10-12 11:37:38 +00:00
}
void BLI_rng_get_tri_sample_float_v2(
RNG *rng, const float v1[2], const float v2[2], const float v3[2], float r_pt[2])
{
copy_v2_v2(r_pt, rng->rng.get_triangle_sample(v1, v2, v3));
}
void BLI_rng_shuffle_array(RNG *rng, void *data, uint elem_size_i, uint elem_num)
{
if (elem_num <= 1) {
return;
}
const uint elem_size = elem_size_i;
uint i = elem_num;
void *temp = malloc(elem_size);
while (i--) {
const uint j = BLI_rng_get_uint(rng) % elem_num;
if (i != j) {
void *iElem = (uchar *)data + i * elem_size_i;
void *jElem = (uchar *)data + j * elem_size_i;
memcpy(temp, iElem, elem_size);
memcpy(iElem, jElem, elem_size);
memcpy(jElem, temp, elem_size);
}
}
free(temp);
}
void BLI_rng_shuffle_bitmap(RNG *rng, BLI_bitmap *bitmap, uint bits_num)
{
if (bits_num <= 1) {
return;
}
uint i = bits_num;
while (i--) {
const uint j = BLI_rng_get_uint(rng) % bits_num;
if (i != j) {
const bool i_bit = BLI_BITMAP_TEST(bitmap, i);
const bool j_bit = BLI_BITMAP_TEST(bitmap, j);
BLI_BITMAP_SET(bitmap, i, j_bit);
BLI_BITMAP_SET(bitmap, j, i_bit);
}
}
}
void BLI_rng_skip(RNG *rng, int n)
{
rng->rng.skip(uint(n));
}
/***/
float BLI_hash_frand(uint seed)
{
RNG rng;
BLI_rng_srandom(&rng, seed);
return BLI_rng_get_float(&rng);
}
void BLI_array_randomize(void *data, uint elem_size, uint elem_num, uint seed)
{
RNG rng;
BLI_rng_seed(&rng, seed);
BLI_rng_shuffle_array(&rng, data, elem_size, elem_num);
}
void BLI_bitmap_randomize(BLI_bitmap *bitmap, uint bits_num, uint seed)
{
RNG rng;
BLI_rng_seed(&rng, seed);
BLI_rng_shuffle_bitmap(&rng, bitmap, bits_num);
}
/* ********* for threaded random ************** */
struct RNG_THREAD_ARRAY {
MEM_guardedalloc: Refactor to add more type-safety. The main goal of these changes are to improve static (i.e. build-time) checks on whether a given data can be allocated and freed with `malloc` and `free` (C-style), or requires proper C++-style construction and destruction (`new` and `delete`). * Add new `MEM_malloc_arrayN_aligned` API. * Make `MEM_freeN` a template function in C++, which does static assert on type triviality. * Add `MEM_SAFE_DELETE`, similar to `MEM_SAFE_FREE` but calling `MEM_delete`. The changes to `MEM_freeN` was painful and useful, as it allowed to fix a bunch of invalid calls in existing codebase already. It also highlighted a fair amount of places where it is called to free incomplete type pointers, which is likely a sign of badly designed code (there should rather be an API to destroy and free these data then, if the data type is not fully publicly exposed). For now, these are 'worked around' by explicitly casting the freed pointers to `void *` in these cases - which also makes them easy to search for. Some of these will be addressed separately (see blender/blender!134765). Finally, MSVC seems to consider structs defining new/delete operators (e.g. by using the `MEM_CXX_CLASS_ALLOC_FUNCS` macro) as non-trivial. This does not seem to follow the definition of type triviality, so for now static type checking in `MEM_freeN` has been disabled for Windows. We'll likely have to do the same with type-safe `MEM_[cm]allocN` API being worked on in blender/blender!134771 Based on ideas from Brecht in blender/blender!134452 Pull Request: https://projects.blender.org/blender/blender/pulls/134463
2025-02-20 10:37:10 +01:00
std::array<RNG, BLENDER_MAX_THREADS> rng_tab;
};
RNG_THREAD_ARRAY *BLI_rng_threaded_new()
{
uint i;
MEM_guardedalloc: Refactor to add more type-safety. The main goal of these changes are to improve static (i.e. build-time) checks on whether a given data can be allocated and freed with `malloc` and `free` (C-style), or requires proper C++-style construction and destruction (`new` and `delete`). * Add new `MEM_malloc_arrayN_aligned` API. * Make `MEM_freeN` a template function in C++, which does static assert on type triviality. * Add `MEM_SAFE_DELETE`, similar to `MEM_SAFE_FREE` but calling `MEM_delete`. The changes to `MEM_freeN` was painful and useful, as it allowed to fix a bunch of invalid calls in existing codebase already. It also highlighted a fair amount of places where it is called to free incomplete type pointers, which is likely a sign of badly designed code (there should rather be an API to destroy and free these data then, if the data type is not fully publicly exposed). For now, these are 'worked around' by explicitly casting the freed pointers to `void *` in these cases - which also makes them easy to search for. Some of these will be addressed separately (see blender/blender!134765). Finally, MSVC seems to consider structs defining new/delete operators (e.g. by using the `MEM_CXX_CLASS_ALLOC_FUNCS` macro) as non-trivial. This does not seem to follow the definition of type triviality, so for now static type checking in `MEM_freeN` has been disabled for Windows. We'll likely have to do the same with type-safe `MEM_[cm]allocN` API being worked on in blender/blender!134771 Based on ideas from Brecht in blender/blender!134452 Pull Request: https://projects.blender.org/blender/blender/pulls/134463
2025-02-20 10:37:10 +01:00
RNG_THREAD_ARRAY *rngarr = MEM_new<RNG_THREAD_ARRAY>("random_array");
2018-06-17 16:32:54 +02:00
for (i = 0; i < BLENDER_MAX_THREADS; i++) {
BLI_rng_srandom(&rngarr->rng_tab[i], uint(clock()));
}
2018-06-17 16:32:54 +02:00
return rngarr;
}
void BLI_rng_threaded_free(RNG_THREAD_ARRAY *rngarr)
{
MEM_guardedalloc: Refactor to add more type-safety. The main goal of these changes are to improve static (i.e. build-time) checks on whether a given data can be allocated and freed with `malloc` and `free` (C-style), or requires proper C++-style construction and destruction (`new` and `delete`). * Add new `MEM_malloc_arrayN_aligned` API. * Make `MEM_freeN` a template function in C++, which does static assert on type triviality. * Add `MEM_SAFE_DELETE`, similar to `MEM_SAFE_FREE` but calling `MEM_delete`. The changes to `MEM_freeN` was painful and useful, as it allowed to fix a bunch of invalid calls in existing codebase already. It also highlighted a fair amount of places where it is called to free incomplete type pointers, which is likely a sign of badly designed code (there should rather be an API to destroy and free these data then, if the data type is not fully publicly exposed). For now, these are 'worked around' by explicitly casting the freed pointers to `void *` in these cases - which also makes them easy to search for. Some of these will be addressed separately (see blender/blender!134765). Finally, MSVC seems to consider structs defining new/delete operators (e.g. by using the `MEM_CXX_CLASS_ALLOC_FUNCS` macro) as non-trivial. This does not seem to follow the definition of type triviality, so for now static type checking in `MEM_freeN` has been disabled for Windows. We'll likely have to do the same with type-safe `MEM_[cm]allocN` API being worked on in blender/blender!134771 Based on ideas from Brecht in blender/blender!134452 Pull Request: https://projects.blender.org/blender/blender/pulls/134463
2025-02-20 10:37:10 +01:00
MEM_delete(rngarr);
}
int BLI_rng_thread_rand(RNG_THREAD_ARRAY *rngarr, int thread)
{
MEM_guardedalloc: Refactor to add more type-safety. The main goal of these changes are to improve static (i.e. build-time) checks on whether a given data can be allocated and freed with `malloc` and `free` (C-style), or requires proper C++-style construction and destruction (`new` and `delete`). * Add new `MEM_malloc_arrayN_aligned` API. * Make `MEM_freeN` a template function in C++, which does static assert on type triviality. * Add `MEM_SAFE_DELETE`, similar to `MEM_SAFE_FREE` but calling `MEM_delete`. The changes to `MEM_freeN` was painful and useful, as it allowed to fix a bunch of invalid calls in existing codebase already. It also highlighted a fair amount of places where it is called to free incomplete type pointers, which is likely a sign of badly designed code (there should rather be an API to destroy and free these data then, if the data type is not fully publicly exposed). For now, these are 'worked around' by explicitly casting the freed pointers to `void *` in these cases - which also makes them easy to search for. Some of these will be addressed separately (see blender/blender!134765). Finally, MSVC seems to consider structs defining new/delete operators (e.g. by using the `MEM_CXX_CLASS_ALLOC_FUNCS` macro) as non-trivial. This does not seem to follow the definition of type triviality, so for now static type checking in `MEM_freeN` has been disabled for Windows. We'll likely have to do the same with type-safe `MEM_[cm]allocN` API being worked on in blender/blender!134771 Based on ideas from Brecht in blender/blender!134452 Pull Request: https://projects.blender.org/blender/blender/pulls/134463
2025-02-20 10:37:10 +01:00
return BLI_rng_get_int(&rngarr->rng_tab[size_t(thread)]);
}
2017-11-14 16:10:48 +11:00
/* ********* Low-discrepancy sequences ************** */
/* incremental halton sequence generator, from:
* "Instant Radiosity", Keller A. */
BLI_INLINE double halton_ex(double invprimes, double *offset)
{
double e = fabs((1.0 - *offset) - 1e-10);
if (invprimes >= e) {
double lasth;
double h = invprimes;
do {
lasth = h;
h *= invprimes;
} while (h >= e);
*offset += ((lasth + h) - 1.0);
}
else {
*offset += invprimes;
}
return *offset;
}
void BLI_halton_1d(uint prime, double offset, int n, double *r)
2017-11-14 16:10:48 +11:00
{
const double invprime = 1.0 / double(prime);
2017-11-14 16:10:48 +11:00
*r = 0.0;
2017-11-14 16:10:48 +11:00
for (int s = 0; s < n; s++) {
*r = halton_ex(invprime, &offset);
}
}
void BLI_halton_2d(const uint prime[2], double offset[2], int n, double *r)
2017-11-14 16:10:48 +11:00
{
const double invprimes[2] = {1.0 / double(prime[0]), 1.0 / double(prime[1])};
2017-11-14 16:10:48 +11:00
r[0] = r[1] = 0.0;
2017-11-14 16:10:48 +11:00
for (int s = 0; s < n; s++) {
for (int i = 0; i < 2; i++) {
r[i] = halton_ex(invprimes[i], &offset[i]);
}
}
}
void BLI_halton_3d(const uint prime[3], double offset[3], int n, double *r)
2017-11-14 16:10:48 +11:00
{
const double invprimes[3] = {
1.0 / double(prime[0]), 1.0 / double(prime[1]), 1.0 / double(prime[2])};
2017-11-14 16:10:48 +11:00
r[0] = r[1] = r[2] = 0.0;
2017-11-14 16:10:48 +11:00
for (int s = 0; s < n; s++) {
for (int i = 0; i < 3; i++) {
r[i] = halton_ex(invprimes[i], &offset[i]);
}
}
}
/* From "Sampling with Hammersley and Halton Points" TT Wong
* Appendix: Source Code 1 */
BLI_INLINE double radical_inverse(uint n)
2017-11-14 16:10:48 +11:00
{
double u = 0;
2020-07-10 11:41:14 +10:00
/* This reverse the bit-wise representation
2017-11-14 16:10:48 +11:00
* around the decimal point. */
for (double p = 0.5; n; p *= 0.5, n >>= 1) {
if (n & 1) {
u += p;
}
}
return u;
}
void BLI_hammersley_1d(uint n, double *r)
2017-11-14 16:10:48 +11:00
{
*r = radical_inverse(n);
}
namespace blender {
RandomNumberGenerator RandomNumberGenerator::from_random_seed()
{
std::random_device rd;
std::mt19937 e{rd()};
std::uniform_int_distribution<uint32_t> dist;
const uint32_t seed = dist(e);
return RandomNumberGenerator(seed);
}
void RandomNumberGenerator::seed_random(uint32_t seed)
{
this->seed(seed + hash[seed & 255]);
seed = this->get_uint32();
this->seed(seed + hash[seed & 255]);
seed = this->get_uint32();
this->seed(seed + hash[seed & 255]);
}
int RandomNumberGenerator::round_probabilistic(float x)
{
/* Support for negative values can be added when necessary. */
BLI_assert(x >= 0.0f);
const float round_up_probability = fractf(x);
const bool round_up = round_up_probability > this->get_float();
return int(x) + int(round_up);
}
float2 RandomNumberGenerator::get_unit_float2()
{
float a = float(M_PI * 2.0) * this->get_float();
return {cosf(a), sinf(a)};
}
float3 RandomNumberGenerator::get_unit_float3()
{
float z = (2.0f * this->get_float()) - 1.0f;
float r = 1.0f - z * z;
if (r > 0.0f) {
float a = float(M_PI * 2.0) * this->get_float();
r = sqrtf(r);
float x = r * cosf(a);
float y = r * sinf(a);
return {x, y, z};
}
return {0.0f, 0.0f, 1.0f};
}
float2 RandomNumberGenerator::get_triangle_sample(float2 v1, float2 v2, float2 v3)
{
float u = this->get_float();
float v = this->get_float();
if (u + v > 1.0f) {
u = 1.0f - u;
v = 1.0f - v;
}
float2 side_u = v2 - v1;
float2 side_v = v3 - v1;
float2 sample = v1;
sample += side_u * u;
sample += side_v * v;
return sample;
}
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
float3 RandomNumberGenerator::get_triangle_sample_3d(float3 v1, float3 v2, float3 v3)
{
float u = this->get_float();
float v = this->get_float();
if (u + v > 1.0f) {
u = 1.0f - u;
v = 1.0f - v;
}
float3 side_u = v2 - v1;
float3 side_v = v3 - v1;
float3 sample = v1;
sample += side_u * u;
sample += side_v * v;
return sample;
}
void RandomNumberGenerator::get_bytes(MutableSpan<char> r_bytes)
{
constexpr int64_t mask_bytes = 2;
constexpr int64_t rand_stride = int64_t(sizeof(x_)) - mask_bytes;
int64_t last_len = 0;
int64_t trim_len = r_bytes.size();
if (trim_len > rand_stride) {
last_len = trim_len % rand_stride;
trim_len = trim_len - last_len;
}
else {
trim_len = 0;
last_len = r_bytes.size();
}
const char *data_src = (const char *)&x_;
int64_t i = 0;
while (i != trim_len) {
BLI_assert(i < trim_len);
#ifdef __BIG_ENDIAN__
for (int64_t j = (rand_stride + mask_bytes) - 1; j != mask_bytes - 1; j--)
#else
for (int64_t j = 0; j != rand_stride; j++)
#endif
{
r_bytes[i++] = data_src[j];
}
this->step();
}
if (last_len) {
for (int64_t j = 0; j != last_len; j++) {
r_bytes[i++] = data_src[j];
}
}
}
} // namespace blender