research(standalone): GPU first port -- marching cubes in D3D11 compute, exact vs WARP; field still CPU
- Compute shaders classify cells from CPU sign bits, scan output offsets, emit vertex/index buffers and indirect-draw args. Two 32^3 tiles: 0 topology differences hardware vs WARP; two runs on the RX 7900 XTX identical; max CPU/GPU vertex distance 0.000122 cm (LOD0), 0.000244 cm (LOD1). - GPU kernels 0.049 / 0.071 ms vs CPU tile 2.33 / 1.99 ms, but CPU input preparation dominates (7.25 / 2.84 ms wall): no whole-view gain until the field itself runs on the GPU. - VoxelForgeFieldShared.h: first shared C++/HLSL field math (integer noise hash, ...). CPU path unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
566d1f342a
commit
d4b4377d30
6 files changed
+1809
No files matched your search
@@ -0,0 +1,173 @@
|
||||
#pragma once
|
||||
|
||||
// Restricted scalar field math shared by the standalone C++ GPU adapter and its HLSL.
|
||||
// Define VF_FIELD_HLSL before including this file from a shader. Keep this header free of
|
||||
// containers, engine types, and vector intrinsics so it remains usable by both compilers.
|
||||
#if defined(VF_FIELD_HLSL)
|
||||
#define VF_FIELD_INLINE
|
||||
typedef int VF_FieldI32;
|
||||
typedef uint VF_FieldU32;
|
||||
#define VF_FIELD_CAST_I32(v) int(v)
|
||||
#define VF_FIELD_CAST_U32(v) uint(v)
|
||||
#define VF_FIELD_CAST_F32(v) float(v)
|
||||
#define VF_FIELD_FLOOR(v) floor(v)
|
||||
#else
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#define VF_FIELD_INLINE inline
|
||||
typedef std::int32_t VF_FieldI32;
|
||||
typedef std::uint32_t VF_FieldU32;
|
||||
#define VF_FIELD_CAST_I32(v) static_cast<VF_FieldI32>(v)
|
||||
#define VF_FIELD_CAST_U32(v) static_cast<VF_FieldU32>(v)
|
||||
#define VF_FIELD_CAST_F32(v) static_cast<float>(v)
|
||||
#define VF_FIELD_FLOOR(v) std::floor(v)
|
||||
#endif
|
||||
|
||||
VF_FIELD_INLINE VF_FieldU32 VF_FieldMix(VF_FieldU32 x)
|
||||
{
|
||||
x ^= x >> 16;
|
||||
x *= 0x7FEB352Du;
|
||||
x ^= x >> 15;
|
||||
x *= 0x846CA68Bu;
|
||||
x ^= x >> 16;
|
||||
return x;
|
||||
}
|
||||
|
||||
VF_FIELD_INLINE VF_FieldU32 VF_FieldNoiseHash(VF_FieldI32 x, VF_FieldI32 y,
|
||||
VF_FieldI32 z, VF_FieldU32 seed)
|
||||
{
|
||||
VF_FieldU32 h = VF_FieldMix(seed ^ 0xB5297A4Du);
|
||||
h = VF_FieldMix(h ^ VF_FIELD_CAST_U32(x) * 0x68E31DA4u);
|
||||
h = VF_FieldMix(h ^ VF_FIELD_CAST_U32(y) * 0x1B56C4E9u);
|
||||
return VF_FieldMix(h ^ VF_FIELD_CAST_U32(z) * 0x9E3779B9u);
|
||||
}
|
||||
|
||||
VF_FIELD_INLINE float VF_FieldNoiseValue(VF_FieldI32 x, VF_FieldI32 y,
|
||||
VF_FieldI32 z, VF_FieldU32 seed)
|
||||
{
|
||||
return VF_FIELD_CAST_F32(VF_FieldNoiseHash(x, y, z, seed) >> 8)
|
||||
* (2.0f / 16777215.0f) - 1.0f;
|
||||
}
|
||||
|
||||
VF_FIELD_INLINE float VF_FieldFade(float t)
|
||||
{
|
||||
return t * t * (3.0f - 2.0f * t);
|
||||
}
|
||||
|
||||
VF_FIELD_INLINE float VF_FieldLerp(float a, float b, float t)
|
||||
{
|
||||
return a + (b - a) * t;
|
||||
}
|
||||
|
||||
VF_FIELD_INLINE float VF_FieldValueNoise(float x, float y, float z,
|
||||
VF_FieldI32 wavelength, VF_FieldU32 seed)
|
||||
{
|
||||
const float inv = 1.0f / VF_FIELD_CAST_F32(wavelength);
|
||||
const float fx = x * inv, fy = y * inv, fz = z * inv;
|
||||
const VF_FieldI32 x0 = VF_FIELD_CAST_I32(VF_FIELD_FLOOR(fx));
|
||||
const VF_FieldI32 y0 = VF_FIELD_CAST_I32(VF_FIELD_FLOOR(fy));
|
||||
const VF_FieldI32 z0 = VF_FIELD_CAST_I32(VF_FIELD_FLOOR(fz));
|
||||
const float tx = VF_FieldFade(fx - VF_FIELD_CAST_F32(x0));
|
||||
const float ty = VF_FieldFade(fy - VF_FIELD_CAST_F32(y0));
|
||||
const float tz = VF_FieldFade(fz - VF_FIELD_CAST_F32(z0));
|
||||
const float z00 = VF_FieldLerp(VF_FieldNoiseValue(x0, y0, z0, seed),
|
||||
VF_FieldNoiseValue(x0 + 1, y0, z0, seed), tx);
|
||||
const float z10 = VF_FieldLerp(VF_FieldNoiseValue(x0, y0 + 1, z0, seed),
|
||||
VF_FieldNoiseValue(x0 + 1, y0 + 1, z0, seed), tx);
|
||||
const float z01 = VF_FieldLerp(VF_FieldNoiseValue(x0, y0, z0 + 1, seed),
|
||||
VF_FieldNoiseValue(x0 + 1, y0, z0 + 1, seed), tx);
|
||||
const float z11 = VF_FieldLerp(VF_FieldNoiseValue(x0, y0 + 1, z0 + 1, seed),
|
||||
VF_FieldNoiseValue(x0 + 1, y0 + 1, z0 + 1, seed), tx);
|
||||
return VF_FieldLerp(VF_FieldLerp(z00, z10, ty), VF_FieldLerp(z01, z11, ty), tz);
|
||||
}
|
||||
|
||||
VF_FIELD_INLINE float VF_FieldSurfaceRelief(float x, float y, float z, VF_FieldU32 seed)
|
||||
{
|
||||
const float macro = VF_FieldValueNoise(x, y, z, 32, seed ^ 0xB5297A4Du);
|
||||
const float mid = VF_FieldValueNoise(x, y, z, 16, seed ^ 0x63D83595u);
|
||||
const float fine = VF_FieldValueNoise(x, y, z, 4, seed ^ 0xA511E9B3u);
|
||||
return 1.20f * macro + (0.80f + 0.25f * macro) * (0.40f * mid + 0.60f * fine);
|
||||
}
|
||||
|
||||
VF_FIELD_INLINE float VF_FieldSmoothUnit(float value)
|
||||
{
|
||||
#if defined(VF_FIELD_HLSL)
|
||||
value = clamp(value, 0.0f, 1.0f);
|
||||
#else
|
||||
value = value < 0.0f ? 0.0f : (value > 1.0f ? 1.0f : value);
|
||||
#endif
|
||||
return value * value * (3.0f - 2.0f * value);
|
||||
}
|
||||
|
||||
VF_FIELD_INLINE float VF_FieldSmoothMin(float a, float b, float width)
|
||||
{
|
||||
if (width <= 0.0f)
|
||||
return a < b ? a : b;
|
||||
const float delta = a - b;
|
||||
#if defined(VF_FIELD_HLSL)
|
||||
const float absolute = abs(delta);
|
||||
const float minimum = min(a, b);
|
||||
const float h = max(width - absolute, 0.0f) / width;
|
||||
#else
|
||||
const float absolute = std::abs(delta);
|
||||
const float minimum = a < b ? a : b;
|
||||
const float h = (width - absolute) > 0.0f ? (width - absolute) / width : 0.0f;
|
||||
#endif
|
||||
return minimum - (h * h * h) * (width / 6.0f);
|
||||
}
|
||||
|
||||
// CPU and GPU use the same edge-root rule. The optional midpoint is the exact analytic
|
||||
// field evaluation used by the reference LOD0 mesher for roots close to an edge midpoint.
|
||||
VF_FIELD_INLINE float VF_FieldRefineRoot(float d1, float d2, float midpoint,
|
||||
VF_FieldU32 hasMidpoint)
|
||||
{
|
||||
float t = (d2 - d1 < 1.0e-4f && d1 - d2 < 1.0e-4f)
|
||||
? 0.5f : (-d1) / (d2 - d1);
|
||||
#if defined(VF_FIELD_HLSL)
|
||||
t = clamp(t, 0.0f, 1.0f);
|
||||
#else
|
||||
t = t < 0.0f ? 0.0f : (t > 1.0f ? 1.0f : t);
|
||||
#endif
|
||||
// hasMidpoint is an integer CPU decision made from the reference edge parameter. Do not
|
||||
// repeat its threshold test in floating point on different GPU implementations.
|
||||
if (hasMidpoint != 0u)
|
||||
{
|
||||
if (midpoint < 1.0e-6f && midpoint > -1.0e-6f)
|
||||
return 0.5f;
|
||||
if ((midpoint < 0.0f) == (d1 < 0.0f))
|
||||
{
|
||||
const float denominator = d2 - midpoint;
|
||||
if (denominator < -1.0e-4f || denominator > 1.0e-4f)
|
||||
{
|
||||
float half = (-midpoint) / denominator;
|
||||
#if defined(VF_FIELD_HLSL)
|
||||
half = clamp(half, 0.0f, 1.0f);
|
||||
#else
|
||||
half = half < 0.0f ? 0.0f : (half > 1.0f ? 1.0f : half);
|
||||
#endif
|
||||
return 0.5f + 0.5f * half;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const float denominator = midpoint - d1;
|
||||
if (denominator < -1.0e-4f || denominator > 1.0e-4f)
|
||||
{
|
||||
float half = (-d1) / denominator;
|
||||
#if defined(VF_FIELD_HLSL)
|
||||
half = clamp(half, 0.0f, 1.0f);
|
||||
#else
|
||||
half = half < 0.0f ? 0.0f : (half > 1.0f ? 1.0f : half);
|
||||
#endif
|
||||
return 0.5f * half;
|
||||
}
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
#undef VF_FIELD_INLINE
|
||||
#undef VF_FIELD_CAST_I32
|
||||
#undef VF_FIELD_CAST_U32
|
||||
#undef VF_FIELD_CAST_F32
|
||||
#undef VF_FIELD_FLOOR
|
||||
Reference in new issue
Block a user