From 4ebc220e1962328eb4de9d3b28808367903a55e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20CHEN?= Date: Thu, 4 Jun 2026 14:13:29 +0800 Subject: [PATCH 1/3] feat: add OptiX SDK detection and WL_OPTIX build flag --- xmake.lua | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/xmake.lua b/xmake.lua index 6b89798..e115387 100644 --- a/xmake.lua +++ b/xmake.lua @@ -163,3 +163,42 @@ if ispc_found then add_includedirs("RayTracing/src") add_ldflags(path.absolute("RayTracing/src/PathTracer.ispc.obj"), {force = true}) end + +-- ── OptiX Denoiser Support ── +local optix_found = false +local optix_include = nil + +-- Check OptiX_ROOT / OPTIX_PATH environment variables +local optix_env = os.getenv("OptiX_ROOT") or os.getenv("OPTIX_PATH") +if optix_env then + local inc = path.join(optix_env, "include") + if os.isdir(inc) then + optix_include = inc + optix_found = true + end +end + +-- Fallback: scan ProgramData for OptiX SDK installations +if not optix_found then + for _, p in ipairs(os.dirs("C:/ProgramData/NVIDIA Corporation/OptiX SDK *") or {}) do + local inc = path.join(p, "include") + if os.isdir(inc) then + optix_include = inc + optix_found = true + break + end + end +end + +if optix_found and cuda_found then + target("RayTracing") + add_defines("WL_OPTIX") + add_files("RayTracing/src/OptiXDenoiser.cpp") + add_includedirs(optix_include) + add_links("cuda") -- CUDA driver API (cuCtxGetCurrent, cuDeviceGet, etc.) + print("[OptiX] Denoiser enabled — SDK: " .. optix_include) +elseif not optix_found then + print("[OptiX] SDK not found — denoiser disabled (set OptiX_ROOT env var)") +else + print("[OptiX] CUDA not found — denoiser requires CUDA") +end From 729d0265c89ec555a5dfde61fa2cfc23345c373f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20CHEN?= Date: Thu, 4 Jun 2026 14:15:15 +0800 Subject: [PATCH 2/3] feat: add OptiXDenoiser RAII wrapper class --- RayTracing/src/OptiXDenoiser.cpp | 176 +++++++++++++++++++++++++++++++ RayTracing/src/OptiXDenoiser.h | 71 +++++++++++++ 2 files changed, 247 insertions(+) create mode 100644 RayTracing/src/OptiXDenoiser.cpp create mode 100644 RayTracing/src/OptiXDenoiser.h diff --git a/RayTracing/src/OptiXDenoiser.cpp b/RayTracing/src/OptiXDenoiser.cpp new file mode 100644 index 0000000..fb2ee25 --- /dev/null +++ b/RayTracing/src/OptiXDenoiser.cpp @@ -0,0 +1,176 @@ +#ifdef WL_OPTIX + +#include "OptiXDenoiser.h" + +#include + +// ── Error-checking helper ── + +#define OPTIX_CHECK(call) \ + do { \ + OptixResult res = (call); \ + if (res != OPTIX_SUCCESS) { \ + std::fprintf(stderr, \ + "[OptiX] Error at %s:%d - %s (code: %d)\n", \ + __FILE__, __LINE__, optixGetErrorString(res), \ + static_cast(res)); \ + } \ + } while (0) + +// ── RAII Lifecycle ── + +OptiXDenoiser::OptiXDenoiser() +{ + OptixResult result = optixInit(); + if (result != OPTIX_SUCCESS) + { + std::fprintf(stderr, "[OptiX] optixInit failed: %s\n", + optixGetErrorString(result)); + return; + } + + // Retrieve current CUDA context (must already exist) + CUcontext cuCtx = nullptr; + CUresult cuRes = cuCtxGetCurrent(&cuCtx); + if (cuRes != CUDA_SUCCESS || cuCtx == nullptr) + { + CUdevice cuDevice; + cuCtxGetDevice(&cuDevice); + cuRes = cuDevicePrimaryCtxRetain(&cuCtx, cuDevice); + if (cuRes != CUDA_SUCCESS) + { + std::fprintf(stderr, "[OptiX] Failed to get CUDA context\n"); + return; + } + } + + OptixDeviceContextOptions ctxOpts = {}; + result = optixDeviceContextCreate(cuCtx, &ctxOpts, &m_optixContext); + if (result != OPTIX_SUCCESS) + { + std::fprintf(stderr, "[OptiX] optixDeviceContextCreate failed: %s\n", + optixGetErrorString(result)); + return; + } + + std::printf("[OptiX] Context created successfully\n"); +} + +OptiXDenoiser::~OptiXDenoiser() +{ + Cleanup(); + if (m_optixContext) + optixDeviceContextDestroy(m_optixContext); +} + +// ── Initialize / Resize ── + +bool OptiXDenoiser::Initialize(uint32_t width, uint32_t height, cudaStream_t stream) +{ + if (!m_optixContext || width == 0 || height == 0) + return false; + + Cleanup(); // Free previous denoiser resources + + m_width = width; + m_height = height; + + OptixDenoiserOptions opts = {}; + opts.inputKind = OPTIX_DENOISER_INPUT_RGB; + + OPTIX_CHECK(optixDenoiserCreate( + m_optixContext, OPTIX_DENOISER_MODEL_KIND_HDR, + &opts, &m_denoiser)); + + if (!m_denoiser) return false; + + OPTIX_CHECK(optixDenoiserComputeMemoryResources( + m_denoiser, width, height, &m_sizes)); + + cudaMalloc(&m_dStateBuffer, m_sizes.stateSizeInBytes); + cudaMalloc(&m_dScratchBuffer, m_sizes.withoutOverlapScratchSizeInBytes); + cudaMalloc(&m_dHdrIntensity, sizeof(float)); + + OPTIX_CHECK(optixDenoiserSetup( + m_denoiser, (CUstream)stream, + width, height, + (CUdeviceptr)m_dStateBuffer, m_sizes.stateSizeInBytes, + (CUdeviceptr)m_dScratchBuffer, m_sizes.withoutOverlapScratchSizeInBytes)); + + m_valid = true; + + std::printf("[OptiX] Denoiser ready (%ux%u, %.1f MB scratch)\n", + width, height, + static_cast(m_sizes.withoutOverlapScratchSizeInBytes + + m_sizes.stateSizeInBytes + + sizeof(float)) / (1024.0f * 1024.0f)); + return true; +} + +void OptiXDenoiser::Resize(uint32_t width, uint32_t height, cudaStream_t stream) +{ + if (width == m_width && height == m_height && m_valid) + return; + Initialize(width, height, stream); +} + +// ── Per-Frame Denoise ── + +void OptiXDenoiser::Denoise(float4* d_input, float4* d_output, + uint32_t width, uint32_t height, + cudaStream_t stream) +{ + if (!m_valid || !m_denoiser || width != m_width || height != m_height) + return; + + OptixImage2D inputLayer = {}; + inputLayer.data = (CUdeviceptr)d_input; + inputLayer.width = width; + inputLayer.height = height; + inputLayer.rowStrideInBytes = width * sizeof(float4); + inputLayer.pixelStrideInBytes = sizeof(float4); + inputLayer.format = OPTIX_PIXEL_FORMAT_FLOAT4; + + OptixImage2D outputLayer = inputLayer; + outputLayer.data = (CUdeviceptr)d_output; + + // Compute HDR intensity for brightness normalization + OPTIX_CHECK(optixDenoiserComputeIntensity( + m_denoiser, (CUstream)stream, + &inputLayer, + (CUdeviceptr)m_dHdrIntensity, + (CUdeviceptr)m_dScratchBuffer, + m_sizes.withoutOverlapScratchSizeInBytes)); + + OptixDenoiserParams params = {}; + params.denoiseAlpha = 0; + params.blendFactor = 0.0f; + params.hdrIntensity = (CUdeviceptr)m_dHdrIntensity; + + OptixDenoiserGuideLayer guideLayer = {}; + OptixDenoiserLayer layer = {}; + layer.input = inputLayer; + layer.output = outputLayer; + + OPTIX_CHECK(optixDenoiserInvoke( + m_denoiser, (CUstream)stream, + ¶ms, + (CUdeviceptr)m_dStateBuffer, m_sizes.stateSizeInBytes, + &guideLayer, &layer, 1, + 0, 0, + (CUdeviceptr)m_dScratchBuffer, + m_sizes.withoutOverlapScratchSizeInBytes)); +} + +// ── Cleanup ── + +void OptiXDenoiser::Cleanup() +{ + if (m_dHdrIntensity) { cudaFree(m_dHdrIntensity); m_dHdrIntensity = nullptr; } + if (m_dScratchBuffer) { cudaFree(m_dScratchBuffer); m_dScratchBuffer = nullptr; } + if (m_dStateBuffer) { cudaFree(m_dStateBuffer); m_dStateBuffer = nullptr; } + if (m_denoiser) { optixDenoiserDestroy(m_denoiser); m_denoiser = nullptr; } + m_valid = false; +} + +#endif // WL_OPTIX diff --git a/RayTracing/src/OptiXDenoiser.h b/RayTracing/src/OptiXDenoiser.h new file mode 100644 index 0000000..0e8f7fa --- /dev/null +++ b/RayTracing/src/OptiXDenoiser.h @@ -0,0 +1,71 @@ +#pragma once + +#include + +// ────────────────────────────────────────────── +// OptiX AI Denoiser — RAII Wrapper +// Requires OptiX SDK 7.0+ and WL_OPTIX define. +// When WL_OPTIX is not defined, stub methods are no-ops. +// ────────────────────────────────────────────── + +#ifdef WL_OPTIX + +#include +#include +#include + +class OptiXDenoiser +{ +public: + OptiXDenoiser(); + ~OptiXDenoiser(); + + OptiXDenoiser(const OptiXDenoiser&) = delete; + OptiXDenoiser& operator=(const OptiXDenoiser&) = delete; + + // Initialize OptiX context + create denoiser for given resolution. + // Returns true on success. + bool Initialize(uint32_t width, uint32_t height, cudaStream_t stream); + + // Reconfigure for new resolution (reallocates scratch buffers). + // No-op if dimensions unchanged. + void Resize(uint32_t width, uint32_t height, cudaStream_t stream); + + // Denoise input → output (in-place allowed). + // d_input / d_output: device-side float4* HDR color buffers. + // Call after path tracing, on the compute stream. + void Denoise(float4* d_input, float4* d_output, + uint32_t width, uint32_t height, + cudaStream_t stream); + + bool IsValid() const { return m_valid; } + +private: + void Cleanup(); + + OptixDeviceContext m_optixContext = nullptr; + OptixDenoiser m_denoiser = nullptr; + + // GPU scratch buffers (allocated via cudaMalloc) + void* m_dStateBuffer = nullptr; + void* m_dScratchBuffer = nullptr; + float* m_dHdrIntensity = nullptr; + + OptixDenoiserSizes m_sizes = {}; + uint32_t m_width = 0; + uint32_t m_height = 0; + bool m_valid = false; +}; + +#else // !WL_OPTIX — Stub (types resolved via void* to avoid CUDA dependency) + +class OptiXDenoiser +{ +public: + bool Initialize(uint32_t, uint32_t, void*) { return false; } + void Resize(uint32_t, uint32_t, void*) {} + void Denoise(void*, void*, uint32_t, uint32_t, void*) {} + bool IsValid() const { return false; } +}; + +#endif // WL_OPTIX From cb05cd2bd9829e1e99199e4dcc76fdc5fb828462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20CHEN?= Date: Thu, 4 Jun 2026 14:39:51 +0800 Subject: [PATCH 3/3] feat: integrate OptiX denoiser into render pipeline with ImGui toggle --- RayTracing/src/CUDARenderer.cu | 31 ++++++++++++++++++++++++++++++ RayTracing/src/CUDARenderer.cuh | 33 +++++++++++++++++++++++++++++++- RayTracing/src/CUDARenderer.h | 12 ++++++++++++ RayTracing/src/OptiXDenoiser.cpp | 9 +++++++-- RayTracing/src/Renderer.cpp | 17 ++++++++++++++++ RayTracing/src/Renderer.h | 14 ++++++++++++++ RayTracing/src/WalnutApp.cpp | 4 ++++ xmake.lua | 28 +++++++++++++-------------- 8 files changed, 131 insertions(+), 17 deletions(-) diff --git a/RayTracing/src/CUDARenderer.cu b/RayTracing/src/CUDARenderer.cu index 526b149..9a656cf 100644 --- a/RayTracing/src/CUDARenderer.cu +++ b/RayTracing/src/CUDARenderer.cu @@ -15,6 +15,7 @@ extern "C" struct CUDARenderState { float4* d_SampleBuffer; // Device per-pixel sample buffer (raw path trace output) + float4* d_DenoiseBuffer; // Device per-pixel denoise buffer (averaged HDR, for OptiX) float4* d_AccumulationBuffer; // Device accumulation buffer uint32_t* d_OutputImage; // Device output RGBA buffer @@ -46,6 +47,7 @@ CUDARenderState* CUDARenderer_Create() state->initialized = false; state->d_SampleBuffer = nullptr; + state->d_DenoiseBuffer = nullptr; state->d_AccumulationBuffer = nullptr; state->d_OutputImage = nullptr; state->d_Spheres = nullptr; @@ -67,6 +69,7 @@ void CUDARenderer_Destroy(CUDARenderState const* state) if (!state) return; if (state->d_SampleBuffer) cudaFree(state->d_SampleBuffer); + if (state->d_DenoiseBuffer) cudaFree(state->d_DenoiseBuffer); if (state->d_AccumulationBuffer) cudaFree(state->d_AccumulationBuffer); if (state->d_OutputImage) cudaFree(state->d_OutputImage); if (state->d_Spheres) cudaFree(state->d_Spheres); @@ -129,12 +132,14 @@ void CUDARenderer_OnResize(CUDARenderState* state, uint32_t width, uint32_t heig // Free old buffers if (state->d_SampleBuffer) cudaFree(state->d_SampleBuffer); + if (state->d_DenoiseBuffer) cudaFree(state->d_DenoiseBuffer); if (state->d_AccumulationBuffer) cudaFree(state->d_AccumulationBuffer); if (state->d_OutputImage) cudaFree(state->d_OutputImage); if (state->d_RayDirections) cudaFree(state->d_RayDirections); // Allocate new buffers cudaMalloc(&state->d_SampleBuffer, state->pixelCount * sizeof(float4)); + cudaMalloc(&state->d_DenoiseBuffer, state->pixelCount * sizeof(float4)); cudaMalloc(&state->d_AccumulationBuffer, state->pixelCount * sizeof(float4)); cudaMalloc(&state->d_OutputImage, state->pixelCount * sizeof(uint32_t)); cudaMalloc(&state->d_RayDirections, state->pixelCount * sizeof(float3)); @@ -272,6 +277,7 @@ void CUDARenderer_Render( PostProcessKernel<<computeStream>>>( state->d_SampleBuffer, state->d_AccumulationBuffer, + state->d_DenoiseBuffer, state->d_OutputImage, frameIndex, state->pixelCount @@ -299,6 +305,31 @@ void CUDARenderer_GetOutput( byteSize, cudaMemcpyDeviceToHost); } +void* CUDARenderer_GetDenoiseBuffer(CUDARenderState* state) +{ + if (!state || !state->initialized) return nullptr; + return state->d_DenoiseBuffer; +} + +void CUDARenderer_ConvertDenoisedToRGBA(CUDARenderState* state, cudaStream_t stream) +{ + if (!state || !state->initialized || state->pixelCount == 0) return; + + int threads = 256; + int blocks = (state->pixelCount + threads - 1) / threads; + ConvertToRGBAKernel<<>>( + (const float4*)state->d_DenoiseBuffer, + state->d_OutputImage, + state->pixelCount + ); +} + +void* CUDARenderer_GetComputeStream(CUDARenderState* state) +{ + if (!state) return nullptr; + return state->computeStream; +} + void CUDARenderer_SetSettings( CUDARenderState* state, int maxBounces) diff --git a/RayTracing/src/CUDARenderer.cuh b/RayTracing/src/CUDARenderer.cuh index 6c86e4e..4d3dd80 100644 --- a/RayTracing/src/CUDARenderer.cuh +++ b/RayTracing/src/CUDARenderer.cuh @@ -276,6 +276,7 @@ __launch_bounds__(256, 4) __global__ void PostProcessKernel( const float4* __restrict__ sampleBuffer, float4* __restrict__ accumulationBuffer, + float4* __restrict__ denoiseBuffer, uint32_t* __restrict__ outputImage, uint32_t frameIndex, uint32_t pixelCount) @@ -291,7 +292,7 @@ __global__ void PostProcessKernel( accumulated.x += sample.x; accumulated.y += sample.y; accumulated.z += sample.z; - accumulated.w += 1.0f; // sample count + accumulated.w += 1.0f; accumulationBuffer[idx] = accumulated; // Average @@ -303,6 +304,9 @@ __global__ void PostProcessKernel( accumulated.w * invFrames ); + // Write averaged HDR for denoising (before clamp/RGBA conversion) + denoiseBuffer[idx] = make_float4(averaged.x, averaged.y, averaged.z, 0.0f); + // Clamp to [0, 1] averaged.x = fminf(fmaxf(averaged.x, 0.0f), 1.0f); averaged.y = fminf(fmaxf(averaged.y, 0.0f), 1.0f); @@ -317,6 +321,33 @@ __global__ void PostProcessKernel( outputImage[idx] = (a << 24) | (b << 16) | (g << 8) | r; } +// ────────────────────────────────────────────── +// Convert denoised float4 → RGBA8 (used after OptiX denoise pass) +// ────────────────────────────────────────────── + +__launch_bounds__(256, 4) +__global__ void ConvertToRGBAKernel( + const float4* __restrict__ inputBuffer, + uint32_t* __restrict__ outputImage, + uint32_t pixelCount) +{ + uint32_t idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx >= pixelCount) + return; + + float4 color = inputBuffer[idx]; + color.x = fminf(fmaxf(color.x, 0.0f), 1.0f); + color.y = fminf(fmaxf(color.y, 0.0f), 1.0f); + color.z = fminf(fmaxf(color.z, 0.0f), 1.0f); + + uint8_t r = static_cast(color.x * 255.0f); + uint8_t g = static_cast(color.y * 255.0f); + uint8_t b = static_cast(color.z * 255.0f); + uint8_t a = 255; + + outputImage[idx] = (a << 24) | (b << 16) | (g << 8) | r; +} + // ────────────────────────────────────────────── // Clear accumulation buffer to zero // ────────────────────────────────────────────── diff --git a/RayTracing/src/CUDARenderer.h b/RayTracing/src/CUDARenderer.h index 671d991..e35d237 100644 --- a/RayTracing/src/CUDARenderer.h +++ b/RayTracing/src/CUDARenderer.h @@ -47,6 +47,15 @@ void CUDARenderer_GetOutput( CUDARenderState* state, void* hostOutput, uint32_t byteSize); +// Get denoise buffer pointer (float4* HDR, for OptiX denoiser) +void* CUDARenderer_GetDenoiseBuffer(CUDARenderState* state); + +// Convert denoised float4 buffer → RGBA8 output (GPU-side) +void CUDARenderer_ConvertDenoisedToRGBA(CUDARenderState* state, void* stream); + +// Get compute stream for OptiX denoiser +void* CUDARenderer_GetComputeStream(CUDARenderState* state); + // Settings void CUDARenderer_SetSettings( CUDARenderState* state, @@ -69,10 +78,13 @@ void CUDARenderer_DebugFill(CUDARenderState* state); #include // C++-compatible float3 (matches CUDA float3 layout: 12 bytes, alignment 4) +// Only define when CUDA's float3 from cuda_runtime.h is not already available +#if !defined(CUDART_VERSION) struct float3 { float x, y, z; }; +#endif // Memory layout must match GPUSphere in CUDATypes.cuh // Compile-time enforcement via static_assert in CUDATypes.cuh diff --git a/RayTracing/src/OptiXDenoiser.cpp b/RayTracing/src/OptiXDenoiser.cpp index fb2ee25..ca7a2e9 100644 --- a/RayTracing/src/OptiXDenoiser.cpp +++ b/RayTracing/src/OptiXDenoiser.cpp @@ -2,6 +2,10 @@ #include "OptiXDenoiser.h" +// Define the global OptiX function table (required by optix_stubs.h) +// Must be defined in exactly one translation unit. +OptixFunctionTable g_optixFunctionTable_118 = {}; + #include // ── Error-checking helper ── @@ -76,7 +80,9 @@ bool OptiXDenoiser::Initialize(uint32_t width, uint32_t height, cudaStream_t str m_height = height; OptixDenoiserOptions opts = {}; - opts.inputKind = OPTIX_DENOISER_INPUT_RGB; + opts.guideAlbedo = 0; + opts.guideNormal = 0; + opts.denoiseAlpha = OPTIX_DENOISER_ALPHA_MODE_COPY; OPTIX_CHECK(optixDenoiserCreate( m_optixContext, OPTIX_DENOISER_MODEL_KIND_HDR, @@ -143,7 +149,6 @@ void OptiXDenoiser::Denoise(float4* d_input, float4* d_output, m_sizes.withoutOverlapScratchSizeInBytes)); OptixDenoiserParams params = {}; - params.denoiseAlpha = 0; params.blendFactor = 0.0f; params.hdrIntensity = (CUdeviceptr)m_dHdrIntensity; diff --git a/RayTracing/src/Renderer.cpp b/RayTracing/src/Renderer.cpp index dc7b4f6..465f984 100644 --- a/RayTracing/src/Renderer.cpp +++ b/RayTracing/src/Renderer.cpp @@ -545,6 +545,23 @@ void Renderer::RenderGPU(const Scene& scene, const Camera& camera) // Launch CUDA render kernel CUDARenderer_Render(m_CUDAState, m_FrameIndex); +#ifdef WL_OPTIX + // Denoise pass: run OptiX on the averaged HDR buffer, then re-convert to RGBA + if (m_Settings.EnableDenoising) + { + cudaStream_t stream = (cudaStream_t)CUDARenderer_GetComputeStream(m_CUDAState); + if (!m_Denoiser.IsValid()) + m_Denoiser.Initialize(width, height, stream); + + float4* d_denoiseBuf = (float4*)CUDARenderer_GetDenoiseBuffer(m_CUDAState); + if (d_denoiseBuf && m_Denoiser.IsValid()) + { + m_Denoiser.Denoise(d_denoiseBuf, d_denoiseBuf, width, height, stream); + CUDARenderer_ConvertDenoisedToRGBA(m_CUDAState, stream); + } + } +#endif + // Download output image from GPU CUDARenderer_GetOutput( m_CUDAState, diff --git a/RayTracing/src/Renderer.h b/RayTracing/src/Renderer.h index 36c29da..e6d075b 100644 --- a/RayTracing/src/Renderer.h +++ b/RayTracing/src/Renderer.h @@ -6,12 +6,20 @@ #include "Ray.h" #include "Scene.h" +#ifdef WL_OPTIX +#include "OptiXDenoiser.h" +#endif + #ifdef WL_CUDA #include "CUDARenderer.h" #endif #include +#ifdef WL_OPTIX +#include "OptiXDenoiser.h" +#endif + #include class Renderer @@ -21,6 +29,9 @@ class Renderer { bool Accumulate = true; bool SlowRandom = true; +#ifdef WL_OPTIX + bool EnableDenoising = true; +#endif }; Renderer(); @@ -90,6 +101,9 @@ class Renderer std::vector m_GPUMaterials; std::vector m_GPURayDirs; uint32_t m_LastSceneVersion = UINT32_MAX; // Force first upload +#ifdef WL_OPTIX + OptiXDenoiser m_Denoiser; +#endif #endif #ifdef WL_ISPC diff --git a/RayTracing/src/WalnutApp.cpp b/RayTracing/src/WalnutApp.cpp index e12065b..95ab374 100644 --- a/RayTracing/src/WalnutApp.cpp +++ b/RayTracing/src/WalnutApp.cpp @@ -100,6 +100,10 @@ class ExampleLayer final : public Walnut::Layer m_NeedsRender = true; if (ImGui::Checkbox("Slow Random", &m_Renderer.GetSettings().SlowRandom)) m_NeedsRender = true; +#ifdef WL_OPTIX + if (ImGui::Checkbox("Denoise", &m_Renderer.GetSettings().EnableDenoising)) + m_NeedsRender = true; +#endif if (ImGui::Button("Reset")) { diff --git a/xmake.lua b/xmake.lua index e115387..b20aee5 100644 --- a/xmake.lua +++ b/xmake.lua @@ -106,7 +106,7 @@ target("RayTracing") if is_plat("windows") then add_cxflags("/utf-8", "/EHsc") - add_defines("WL_PLATFORM_WINDOWS") + add_defines("WL_PLATFORM_WINDOWS", "NOMINMAX") add_links("opengl32", "gdi32") end @@ -180,25 +180,25 @@ end -- Fallback: scan ProgramData for OptiX SDK installations if not optix_found then - for _, p in ipairs(os.dirs("C:/ProgramData/NVIDIA Corporation/OptiX SDK *") or {}) do - local inc = path.join(p, "include") - if os.isdir(inc) then - optix_include = inc - optix_found = true - break + local base = "C:/ProgramData/NVIDIA Corporation" + if os.isdir(base) then + for _, p in ipairs(os.dirs(base .. "/*")) do + if p:find("OptiX SDK") then + local inc = path.join(p, "include") + if os.isdir(inc) then + optix_include = inc + optix_found = true + break + end + end end end end if optix_found and cuda_found then target("RayTracing") - add_defines("WL_OPTIX") + add_defines("WL_OPTIX", "NOMINMAX") add_files("RayTracing/src/OptiXDenoiser.cpp") add_includedirs(optix_include) - add_links("cuda") -- CUDA driver API (cuCtxGetCurrent, cuDeviceGet, etc.) - print("[OptiX] Denoiser enabled — SDK: " .. optix_include) -elseif not optix_found then - print("[OptiX] SDK not found — denoiser disabled (set OptiX_ROOT env var)") -else - print("[OptiX] CUDA not found — denoiser requires CUDA") + add_links("cuda", "Advapi32") end