Skip to content

Conversation

Przemog1
Copy link
Contributor

No description provided.

@devshgraphicsprogramming
Copy link
Member

devshgraphicsprogramming commented Jan 6, 2025

why do you have random things from sorakrit here instead of your own branch against his or master ?

I literally can't see whats yours and what's @keptsecret's.

SBxDFTestResources retval;

retval.rng = nbl::hlsl::Xoroshiro64Star::construct(seed);
retval.u = float32_t3(rngFloat01(retval.rng), rngFloat01(retval.rng), 0.0);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the third random number is sometimes necessary to discern discrete BxDF branches:

  • reflection vs refraction in e.g. glass
  • diffuse vs specular in e.g. plastic
  • choosing a leaf BxDF in a mixture/tree of BxDFs

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

your new code is still testing with u.z=0

while the jacobian test can't work with varying u.z, its important to test varying u.xy for different constant values of u.z

I'm pretty sure the Chi2 and bucket/histogram tests can handle varying u.z (and you should vary it)

Comment on lines 83 to 210
struct STestMeta
{
float32_t4 result;
#ifndef __HLSL_VERSION
std::string bxdfName;
std::string testName;
#endif
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

returning a result as a vec4 is quite a bit meh, I'd actually provide a callback (via static polymorphism) which is only called upon a failure

struct FailureCallbackConceptExample
{
   void __call(NBL_CONST_REF_ARG(SBxDFTestResources) failedFor, NBL_CONST_REF_ARG(sample_t) failedAt);
};

then you can print in C++, mark booleans, append to vector, break & repeat call or whatever

Copy link
Member

@devshgraphicsprogramming devshgraphicsprogramming Jan 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perfect C++ callback

printf(...your error message...);
for (volatile bool repeat=true; gDebuggerAttached&&repeat; )
{
   repeat = false; // I can set to true with debugger and repeat as many times as I want till I find the cause
   __debugbreak();
   failedFor.test(failedAt);
}

Comment on lines 351 to 588
t.meta.result = t.test();
t.meta.testName = "u offset";
return t.meta;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setting those thousands of times will be expensive, function should be void and not return anything, callback instead of meta

Comment on lines 342 to 345
static STestMeta run(uint32_t2 seed)
{
this_t t;
t.init(seed);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd make the function take uint32_t as an argument, then run a hash function (wide not deep)
https://rene.ruhr/gfx/gpuhash/

This will make it easy to invoke multiple run(uint32_t invocationID) in parallel independently, be it on CPU with for loop or GPU with gl_GlobalInvocationID.x

Comment on lines 358 to 376
template<class BxDF, bool aniso = false>
struct TestVOffset : TestBxDF<BxDF>
{
using base_t = TestBase<BxDF>;
using this_t = TestVOffset<BxDF, aniso>;

float32_t4 test()
{
sample_t s, sx, sy, sz;
quotient_pdf_t pdf;
float32_t3 brdf;
aniso_cache cache, dummy;

iso_interaction isointerx = iso_interaction::create(base_t::rc.dV(0), base_t::rc.N);
aniso_interaction anisointerx = aniso_interaction::create(isointerx, base_t::rc.T, base_t::rc.B);
iso_interaction isointery = iso_interaction::create(base_t::rc.dV(1), base_t::rc.N);
aniso_interaction anisointery = aniso_interaction::create(isointery, base_t::rc.T, base_t::rc.B);
iso_interaction isointerz = iso_interaction::create(base_t::rc.dV(2), base_t::rc.N);
aniso_interaction anisointerz = aniso_interaction::create(isointerz, base_t::rc.T, base_t::rc.B);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's no point to this test existing, the test I've outlined ONLY makes sense for varying (differentiating) the 2D random number xi, because thats the uniform distribution that must be stretched and squeezed to form the PDF of the importance sampler


#include "app_resources/tests.hlsl"

#define ASSERT_ZERO(x) (assert(all<bool32_t4>((x.result) < float32_t4(1e-3))));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use a nice callback

Comment on lines 28 to 42
const uint32_t2 state = uint32_t2(12u, 69u); // (12u, 69u)

// test u offset, 2 axis
ASSERT_ZERO((TestUOffset<bxdf::reflection::SLambertianBxDF<sample_t, iso_interaction, aniso_interaction>>::run(state)));
ASSERT_ZERO((TestUOffset<bxdf::reflection::SOrenNayarBxDF<sample_t, iso_interaction, aniso_interaction>>::run(state)));
printResult(TestUOffset<bxdf::reflection::SBeckmannBxDF<sample_t, iso_cache, aniso_cache>,false>::run(state));
ASSERT_ZERO((TestUOffset<bxdf::reflection::SBeckmannBxDF<sample_t, iso_cache, aniso_cache>,true>::run(state)));
printResult(TestUOffset<bxdf::reflection::SGGXBxDF<sample_t, iso_cache, aniso_cache>,false>::run(state));
ASSERT_ZERO((TestUOffset<bxdf::reflection::SGGXBxDF<sample_t, iso_cache, aniso_cache>,true>::run(state)));

ASSERT_ZERO((TestUOffset<bxdf::transmission::SLambertianBxDF<sample_t, iso_interaction, aniso_interaction>>::run(state)));
printResult(TestUOffset<bxdf::transmission::SBeckmannDielectricBxDF<sample_t, iso_cache, aniso_cache>,false>::run(state));
ASSERT_ZERO((TestUOffset<bxdf::transmission::SBeckmannDielectricBxDF<sample_t, iso_cache, aniso_cache>,true>::run(state)));
printResult(TestUOffset<bxdf::transmission::SGGXDielectricBxDF<sample_t, iso_cache, aniso_cache>,false>::run(state));
printResult(TestUOffset<bxdf::transmission::SGGXDielectricBxDF<sample_t, iso_cache, aniso_cache>,true>::run(state));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you need to run the tests thousands of times with different seeds, I'd use for_each over a ranges::iota_view with par_unseq policy to perform these tests on all CPU cores at once.
https://godbolt.org/z/xvxE7jzE3

this connects with https://github.com/Devsh-Graphics-Programming/Nabla-Examples-and-Tests/pull/165/files#r1906914911

Comment on lines 288 to 494
if NBL_CONSTEXPR_FUNC (is_basic_brdf_v<BxDF>)
{
s = base_t::bxdf.generate(base_t::anisointer, base_t::rc.u.xy);
sx = base_t::bxdf.generate(base_t::anisointer, base_t::rc.u.xy + float32_t2(base_t::rc.h,0));
sy = base_t::bxdf.generate(base_t::anisointer, base_t::rc.u.xy + float32_t2(0,base_t::rc.h));
}
if NBL_CONSTEXPR_FUNC (is_microfacet_brdf_v<BxDF>)
{
s = base_t::bxdf.generate(base_t::anisointer, base_t::rc.u.xy, cache);
sx = base_t::bxdf.generate(base_t::anisointer, base_t::rc.u.xy + float32_t2(base_t::rc.h,0), dummy);
sy = base_t::bxdf.generate(base_t::anisointer, base_t::rc.u.xy + float32_t2(0,base_t::rc.h), dummy);
}
if NBL_CONSTEXPR_FUNC (is_basic_bsdf_v<BxDF>)
{
s = base_t::bxdf.generate(base_t::anisointer, base_t::rc.u);
sx = base_t::bxdf.generate(base_t::anisointer, base_t::rc.u + float32_t3(base_t::rc.h,0,0));
sy = base_t::bxdf.generate(base_t::anisointer, base_t::rc.u + float32_t3(0,base_t::rc.h,0));
}
if NBL_CONSTEXPR_FUNC (is_microfacet_bsdf_v<BxDF>)
{
s = base_t::bxdf.generate(base_t::anisointer, base_t::rc.u, cache);
float32_t3 ux = base_t::rc.u + float32_t3(base_t::rc.h,0,0);
sx = base_t::bxdf.generate(base_t::anisointer, ux, dummy);
float32_t3 uy = base_t::rc.u + float32_t3(0,base_t::rc.h,0);
sy = base_t::bxdf.generate(base_t::anisointer, uy, dummy);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

h should be a parameter of each individual specialized BxDF test, because its specific to the differential test and also may need to change depending on the BxDF getting tested

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also maybe compute the perturbed u above for readability

float32_t2x2 m = float32_t2x2(sx.TdotL - s.TdotL, sy.TdotL - s.TdotL, sx.BdotL - s.BdotL, sy.BdotL - s.BdotL);
float det = nbl::hlsl::determinant<float32_t2x2>(m);

return float32_t4(nbl::hlsl::abs<float32_t3>(pdf.value() - brdf), nbl::hlsl::abs<float>(det*pdf.pdf/s.NdotL) * 0.5);
Copy link
Member

@devshgraphicsprogramming devshgraphicsprogramming Jan 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this needs to be done better semantically with a callback, one thing for each individually.

Alos pdf.value() does not equal brdf in general!!!!!

P.s. the *0.5 thing on the PDF to Jacobian ratio was a thing for visualization, you should really have a callback which gets triggered if the value is too far from 1.0

Comment on lines 333 to 526
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you need to skip checking anything that produces an "impossible" s, so if eval or quotient is 0, you can skip the rest of the function (but not before you check pdf and quotient for 0 and INF respectively - the checks also check for NaN)

add more simple tests for:

  1. pdf>0 because something you generated cannot have 0 probability of getting generated
  2. quotient<INF always because our importance sampler's job is to prevent that!
  3. positivity, all pdf, quotient, and eval need to be >=0
  4. recprocity (eval must be equal if we swap L and V around, make a method/function to swap L and V around in s, the interaction, the bxdf itself and the cache)

Leave lots of comments.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also when pdf is close to INF (above some super high threshold) you need to back off the last test you have here

Comment on lines 79 to 80
retval.V.direction = nbl::hlsl::normalize<float32_t3>(projected_hemisphere_generate<float>(rngUniformDist<float32_t2>(retval.rng)));
retval.N = nbl::hlsl::normalize<float32_t3>(projected_hemisphere_generate<float>(rngUniformDist<float32_t2>(retval.rng)));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not projected hemisphere, just hemisphere.

Also not hemisphere but full sphere, because its good to test that BRDFs handle invalid (below hemisphere) correctly

@Przemog1 Przemog1 changed the title New tgmath BxDF tests Jan 20, 2025
@@ -91,18 +154,21 @@ struct SBxDFTestResources
retval.rng = nbl::hlsl::Xoroshiro64Star::construct(seed);
retval.u = float32_t3(rngUniformDist<float32_t2>(retval.rng), 0.0);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 169 to 172
#else
retval.T = tb[0];
retval.B = tb[1];
retval.T = tangent;
retval.B = bitangent;
#endif

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do a quick quaternion.hlsl we can refine later (create from matrix<T,3,3> and createAngleAxis)

have it store vector<T,4> data member

its important that GPU also does the test with rotated tangent frames

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed it to use the rotation_mat in math/linalg/transform.hlsl instead. Do we still want this quaternion.hlsl?

struct STestInitParams
{
bool logInfo;
uint32_t state;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is state ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the loop iteration number. Pass into pcg to get uint32_t2 seed for xoroshiro rng. Same as here as well
#165 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants