diff --git a/public/include/remix/remix.h b/public/include/remix/remix.h index afe8af2e8..0c0458f05 100644 --- a/public/include/remix/remix.h +++ b/public/include/remix/remix.h @@ -157,6 +157,7 @@ namespace remix { Result< void > DrawInstance(const InstanceInfo& info); Result< remixapi_LightHandle > CreateLight(const LightInfo& info); Result< void > DestroyLight(remixapi_LightHandle handle); + Result< void > DrawLightInstance(remixapi_LightHandle handle); Result< void > SetConfigVariable(const char* key, const char* value); // DXVK interoperability @@ -676,7 +677,9 @@ namespace remix { return m_CInterface.DestroyLight(handle); } - + inline Result< void > Interface::DrawLightInstance(remixapi_LightHandle handle) { + return m_CInterface.DrawLightInstance(handle); + } namespace detail { struct dxvk_ExternalSwapchain { diff --git a/public/include/remix/remix_c.h b/public/include/remix/remix_c.h index 05ac5e6bb..f5b586203 100644 --- a/public/include/remix/remix_c.h +++ b/public/include/remix/remix_c.h @@ -459,6 +459,9 @@ extern "C" { remixapi_LightHandle handle); + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_DrawLightInstance)( + remixapi_LightHandle lightHandle); + typedef remixapi_ErrorCode(REMIXAPI_PTR* PFN_remixapi_SetConfigVariable)( const char* key, @@ -511,6 +514,7 @@ extern "C" { PFN_remixapi_DrawInstance DrawInstance; PFN_remixapi_CreateLight CreateLight; PFN_remixapi_DestroyLight DestroyLight; + PFN_remixapi_DrawLightInstance DrawLightInstance; PFN_remixapi_SetConfigVariable SetConfigVariable; // DXVK interoperability PFN_remixapi_dxvk_CreateD3D9 dxvk_CreateD3D9; diff --git a/src/dxvk/rtx_render/rtx_context.cpp b/src/dxvk/rtx_render/rtx_context.cpp index 373696387..5f80dd19f 100644 --- a/src/dxvk/rtx_render/rtx_context.cpp +++ b/src/dxvk/rtx_render/rtx_context.cpp @@ -995,16 +995,7 @@ namespace dxvk { constants.isZUp = RtxOptions::Get()->isZUp(); constants.enableCullingSecondaryRays = RtxOptions::Get()->enableCullingInSecondaryRays(); - // Dome light handling - DomeLight domeLight; - uint32_t domeLightTextureIndex; - if (constants.domeLightActive = getSceneManager().getLightManager().getActiveDomeLight(domeLight, domeLightTextureIndex)) { - assert(domeLightTextureIndex != UINT_MAX); - - constants.domeLightTextureIndex = domeLightTextureIndex; - constants.domeLightRadiance = domeLight.radiance; - constants.worldToDomeLightTranform = domeLight.worldToLight; - } + constants.domeLightArgs = getSceneManager().getLightManager().getDomeLightArgs(); // Upload the constants to the GPU { diff --git a/src/dxvk/rtx_render/rtx_light_manager.cpp b/src/dxvk/rtx_render/rtx_light_manager.cpp index c629262b6..809ca5b3d 100644 --- a/src/dxvk/rtx_render/rtx_light_manager.cpp +++ b/src/dxvk/rtx_render/rtx_light_manager.cpp @@ -324,8 +324,11 @@ namespace dxvk { m_linearizedLights.emplace_back(&light); } - for (auto& [handle, light] : m_externalLights) { - m_linearizedLights.emplace_back(&light); + for (auto& handle : m_externalActiveLightList) { + auto& found = m_externalLights.find(handle); + if (found != m_externalLights.end()) { + m_linearizedLights.emplace_back(&found->second); + } } // Count the active light of each type @@ -443,13 +446,23 @@ namespace dxvk { // Generate a GPU dome light if necessary DomeLight activeDomeLight; - if (getActiveDomeLight(activeDomeLight, m_activeDomeLightBindlessTextureIndex)) { + if (getActiveDomeLight(activeDomeLight)) { // Ensures a texture stays in VidMem SceneManager& sceneManager = device()->getCommon()->getSceneManager(); - sceneManager.trackTexture(ctx, activeDomeLight.texture, m_activeDomeLightBindlessTextureIndex, true, false); + sceneManager.trackTexture(ctx, activeDomeLight.texture, m_gpuDomeLightArgs.textureIndex, true, false); + + m_gpuDomeLightArgs.active = true; + m_gpuDomeLightArgs.radiance = activeDomeLight.radiance; + m_gpuDomeLightArgs.worldToLightTransform = activeDomeLight.worldToLight; } else { - m_activeDomeLightBindlessTextureIndex = UINT_MAX; + m_gpuDomeLightArgs.active = false; + m_gpuDomeLightArgs.radiance = Vector3(0.0f); + m_gpuDomeLightArgs.textureIndex = BINDING_INDEX_INVALID; } + + // Reset external active light list. + m_externalActiveDomeLight = nullptr; + m_externalActiveLightList.clear(); } float LightManager::isSimilar(const RtLight& a, const RtLight& b, float distanceThreshold) { @@ -635,14 +648,19 @@ namespace dxvk { m_externalDomeLights.erase(handle); } - bool LightManager::getActiveDomeLight(DomeLight& domeLightOut, uint32_t& bindlessTextureIndexOut) const { - if (m_externalDomeLights.size() == 0) { + bool LightManager::getActiveDomeLight(DomeLight& domeLightOut) { + if (m_externalDomeLights.size() == 0 || m_externalActiveDomeLight == nullptr) { + return false; + } + + auto found = m_externalDomeLights.find(m_externalActiveDomeLight); + if (found == m_externalDomeLights.end()) { + // Invalid active dome light, reset it + m_externalActiveDomeLight = nullptr; return false; } - // We take the first dome light - domeLightOut = m_externalDomeLights.begin()->second; - bindlessTextureIndexOut = m_activeDomeLightBindlessTextureIndex; + domeLightOut = found->second; return true; } @@ -658,6 +676,14 @@ namespace dxvk { } } + void LightManager::addExternalLightInstance(remixapi_LightHandle enabledLight) { + if (m_externalLights.find(enabledLight) != m_externalLights.end()) { + m_externalActiveLightList.insert(enabledLight); + } else if (m_externalDomeLights.find(enabledLight) != m_externalDomeLights.end() && m_externalActiveDomeLight == nullptr) { + m_externalActiveDomeLight = enabledLight; + } + } + void LightManager::setRaytraceArgs(RaytraceArgs& raytraceArgs, uint32_t rtxdiInitialLightSamples, uint32_t volumeRISInitialLightSamples, uint32_t risLightSamples) const { // The algorithm below performs two tasks: diff --git a/src/dxvk/rtx_render/rtx_light_manager.h b/src/dxvk/rtx_render/rtx_light_manager.h index c2b681af7..1bd6ecf22 100644 --- a/src/dxvk/rtx_render/rtx_light_manager.h +++ b/src/dxvk/rtx_render/rtx_light_manager.h @@ -31,6 +31,8 @@ #include "rtx_lights.h" #include "rtx_camera_manager.h" #include "rtx_common_object.h" +#include "rtx/pass/common_binding_indices.h" +#include "rtx/pass/raytrace_args.h" using remixapi_LightHandle = struct remixapi_LightHandle_T*; @@ -73,8 +75,7 @@ struct LightManager : public CommonDeviceObject { const Rc getPreviousLightBuffer() const { return m_previousLightBuffer.ptr() ? m_previousLightBuffer : m_lightBuffer; } const Rc getLightMappingBuffer() const { return m_lightMappingBuffer; } const uint32_t getActiveCount() const { return m_currentActiveLightCount; } - - bool getActiveDomeLight(DomeLight& lightOut, uint32_t& bindlessTextureIndexOut) const; + const DomeLightArgs& getDomeLightArgs() const { return m_gpuDomeLightArgs; } void clear(); @@ -91,6 +92,7 @@ struct LightManager : public CommonDeviceObject { void addExternalLight(remixapi_LightHandle handle, const RtLight& rtlight); void addExternalDomeLight(remixapi_LightHandle handle, const DomeLight& domeLight); void removeExternalLight(remixapi_LightHandle handle); + void addExternalLightInstance(remixapi_LightHandle enabledLight); void setRaytraceArgs(RaytraceArgs& raytraceArgs, uint32_t rtxdiInitialLightSamples, uint32_t volumeRISInitialLightSamples, uint32_t risLightSamples) const; @@ -104,12 +106,14 @@ struct LightManager : public CommonDeviceObject { std::optional m_fallbackLight{}; std::unordered_map m_externalLights; std::unordered_map m_externalDomeLights; + std::unordered_set m_externalActiveLightList; + remixapi_LightHandle m_externalActiveDomeLight = nullptr; + DomeLightArgs m_gpuDomeLightArgs; + Rc m_lightBuffer; Rc m_previousLightBuffer; Rc m_lightMappingBuffer; - uint32_t m_activeDomeLightBindlessTextureIndex = UINT_MAX; - uint32_t m_currentActiveLightCount = 0; std::array m_lightTypeRanges; // Note: The following vectors are included as members rather as local variables in the @@ -120,6 +124,7 @@ struct LightManager : public CommonDeviceObject { std::vector m_lightsGPUData{}; std::vector m_lightMappingData{}; + bool getActiveDomeLight(DomeLight& lightOut); void garbageCollectionInternal(); diff --git a/src/dxvk/rtx_render/rtx_remix_api.cpp b/src/dxvk/rtx_render/rtx_remix_api.cpp index b0c18142f..0ed474bf6 100644 --- a/src/dxvk/rtx_render/rtx_remix_api.cpp +++ b/src/dxvk/rtx_render/rtx_remix_api.cpp @@ -889,6 +889,28 @@ namespace { return REMIXAPI_ERROR_CODE_SUCCESS; } + + remixapi_ErrorCode REMIXAPI_CALL remixapi_DrawLightInstance( + remixapi_LightHandle lightHandle) { + dxvk::D3D9DeviceEx* remixDevice = tryAsDxvk(); + if (!remixDevice) { + return REMIXAPI_ERROR_CODE_REMIX_DEVICE_WAS_NOT_REGISTERED; + } + if (!lightHandle) { + return REMIXAPI_ERROR_CODE_WRONG_ARGUMENTS; + } + + // async load + std::lock_guard lock { s_mutex }; + remixDevice->EmitCs([lightHandle](dxvk::DxvkContext* ctx) { + auto& lightMgr = ctx->getCommonObjects()->getSceneManager().getLightManager(); + lightMgr.addExternalLightInstance(lightHandle); + }); + + return REMIXAPI_ERROR_CODE_SUCCESS; + } + + remixapi_ErrorCode REMIXAPI_CALL remixapi_SetConfigVariable( const char* key, const char* value) { @@ -1090,6 +1112,7 @@ extern "C" interf.DrawInstance = remixapi_DrawInstance; interf.CreateLight = remixapi_CreateLight; interf.DestroyLight = remixapi_DestroyLight; + interf.DrawLightInstance = remixapi_DrawLightInstance; interf.SetConfigVariable = remixapi_SetConfigVariable; interf.dxvk_CreateD3D9 = remixapi_dxvk_CreateD3D9; interf.dxvk_RegisterD3D9Device = remixapi_dxvk_RegisterD3D9Device; diff --git a/src/dxvk/shaders/rtx/algorithm/geometry_resolver.slangh b/src/dxvk/shaders/rtx/algorithm/geometry_resolver.slangh index 51a1f0916..21c63d8c4 100644 --- a/src/dxvk/shaders/rtx/algorithm/geometry_resolver.slangh +++ b/src/dxvk/shaders/rtx/algorithm/geometry_resolver.slangh @@ -1083,18 +1083,16 @@ void geometryResolverVertex( } else { - if(cb.domeLightActive) + if(cb.domeLightArgs.active) { - float3 domeSampleDirection = mul(cb.worldToDomeLightTranform, float4(geometryResolverState.direction, 0.0f)).xyz; - float2 sampleUV = cartesianDirectionToLatLongSphere(domeSampleDirection); - emissiveRadiance += cb.domeLightRadiance * textures[nonuniformEXT(uint(cb.domeLightTextureIndex))].SampleLevel(LinearWrapSampler, sampleUV, 0).xyz; + emissiveRadiance += cb.domeLightArgs.radiance * sampleDomeLightTexture(LinearWrapSampler, geometryResolverState.direction, cb.domeLightArgs.textureIndex, cb.domeLightArgs.worldToLightTransform); } else { - // Apply sky radiance on miss - // TODO: add jitter? - vec2 screenUV = cameraPixelCoordinateToScreenUV(cb.camera, geometryResolverState.pixelCoordinate); - emissiveRadiance += cb.skyBrightness * SkyMatte.Sample(screenUV); + // Apply sky radiance on miss + // TODO: add jitter? + vec2 screenUV = cameraPixelCoordinateToScreenUV(cb.camera, geometryResolverState.pixelCoordinate); + emissiveRadiance += cb.skyBrightness * SkyMatte.Sample(screenUV); } } @@ -1568,16 +1566,14 @@ void geometryPSRResolverVertex( } else { - if(cb.domeLightActive) + if(cb.domeLightArgs.active) { - float3 domeSampleDirection = mul(cb.worldToDomeLightTranform, float4(ray.direction, 0.0f)).xyz; - float2 sampleUV = cartesianDirectionToLatLongSphere(domeSampleDirection); - emissiveRadiance += cb.domeLightRadiance * textures[nonuniformEXT(uint(cb.domeLightTextureIndex))].SampleLevel(LinearWrapSampler, sampleUV, 0).xyz; + emissiveRadiance += cb.domeLightArgs.radiance * sampleDomeLightTexture(LinearWrapSampler, ray.direction, cb.domeLightArgs.textureIndex, cb.domeLightArgs.worldToLightTransform); } else { - // Output radiance from sky probe - emissiveRadiance += cb.skyBrightness * SkyProbe.Sample(ray.direction); + // Output radiance from sky probe + emissiveRadiance += cb.skyBrightness * SkyProbe.Sample(ray.direction); } } diff --git a/src/dxvk/shaders/rtx/algorithm/integrator_indirect.slangh b/src/dxvk/shaders/rtx/algorithm/integrator_indirect.slangh index 056e87500..7527e5ea1 100644 --- a/src/dxvk/shaders/rtx/algorithm/integrator_indirect.slangh +++ b/src/dxvk/shaders/rtx/algorithm/integrator_indirect.slangh @@ -319,15 +319,13 @@ void integratePathVertex( // Note: True misses going out into infinity will have no hit in the ray interaction and the continue resolving flag set to false. This is in contrast to misses // which may require further resolving which may be needed in cases of skipping specific pieces of geometry while still wishing to continue traversal. - if(cb.domeLightActive) + if(cb.domeLightArgs.active) { - float3 domeSampleDirection = mul(cb.worldToDomeLightTranform, float4(ray.direction, 0.0f)).xyz; - float2 sampleUV = cartesianDirectionToLatLongSphere(domeSampleDirection); - emissiveRadiance += cb.domeLightRadiance * textures[nonuniformEXT(uint(cb.domeLightTextureIndex))].SampleLevel(LinearWrapSampler, sampleUV, 0).xyz; + emissiveRadiance += cb.domeLightArgs.radiance * sampleDomeLightTexture(LinearWrapSampler, pathState.direction, cb.domeLightArgs.textureIndex, cb.domeLightArgs.worldToLightTransform); } else { - emissiveRadiance += cb.skyBrightness * SkyProbe.Sample(pathState.direction); + emissiveRadiance += cb.skyBrightness * SkyProbe.Sample(pathState.direction); } } diff --git a/src/dxvk/shaders/rtx/concept/light/light_helper.slangh b/src/dxvk/shaders/rtx/concept/light/light_helper.slangh index 2c6110de0..458cb7cdb 100644 --- a/src/dxvk/shaders/rtx/concept/light/light_helper.slangh +++ b/src/dxvk/shaders/rtx/concept/light/light_helper.slangh @@ -36,3 +36,15 @@ struct LightSample // Note: 32 bit floating point used to avoid precision issues with some kinds of sampling on lights. float solidAnglePdf; }; + +float3 sampleDomeLightTexture(SamplerState sampler, float3 worldDirection, uint32_t domeLightTextureIndex, float4x4 worldToDomeLightTransform) +{ + if(domeLightTextureIndex == BINDING_INDEX_INVALID) + { + return 1..xxx; + } + + float3 domeSampleDirection = mul(worldToDomeLightTransform, float4(worldDirection, 0.0f)).xyz; + float2 sampleUV = cartesianDirectionToLatLongSphere(domeSampleDirection); + return textures[nonuniformEXT(uint(domeLightTextureIndex))].SampleLevel(sampler, sampleUV, 0).xyz; +} \ No newline at end of file diff --git a/src/dxvk/shaders/rtx/pass/raytrace_args.h b/src/dxvk/shaders/rtx/pass/raytrace_args.h index 22eb104ef..73c683810 100644 --- a/src/dxvk/shaders/rtx/pass/raytrace_args.h +++ b/src/dxvk/shaders/rtx/pass/raytrace_args.h @@ -79,6 +79,15 @@ struct NeeCacheArgs { uint clearCache; }; +struct DomeLightArgs { + mat4 worldToLightTransform; + + vec3 radiance; + uint active; + + uint textureIndex; +}; + // Constant buffer struct RaytraceArgs { Camera camera; @@ -296,11 +305,7 @@ struct RaytraceArgs { uint thinOpaqueEnable; float totalMipBias; - mat4 worldToDomeLightTranform; - - vec3 domeLightRadiance; - uint domeLightActive; + DomeLightArgs domeLightArgs; - uint domeLightTextureIndex; float2 upscaleFactor; // Displayed(upscaled) / RT resolution };