Files
WickedEngine/WickedEngine/wiRenderPath3D.cpp
T
2025-03-20 08:20:45 +01:00

3010 lines
88 KiB
C++

#include "wiRenderPath3D.h"
#include "wiRenderer.h"
#include "wiImage.h"
#include "wiHelper.h"
#include "wiTextureHelper.h"
#include "wiProfiler.h"
using namespace wi::graphics;
using namespace wi::enums;
namespace wi
{
static constexpr float foreground_depth_range = 0.01f;
void RenderPath3D::DeleteGPUResources()
{
RenderPath2D::DeleteGPUResources();
rtMain = {};
rtMain_render = {};
rtPrimitiveID = {};
rtPrimitiveID_render = {};
rtVelocity = {};
rtReflection = {};
rtRaytracedDiffuse = {};
rtSSR = {};
rtSceneCopy = {};
rtSceneCopy_tmp = {};
rtWaterRipple = {};
rtParticleDistortion_render = {};
rtParticleDistortion = {};
rtVolumetricLights = {};
rtBloom = {};
rtBloom_tmp = {};
rtAO = {};
rtShadow = {};
rtSun[0] = {};
rtSun[1] = {};
rtSun[2] = {};
rtSun_resolved = {};
rtGUIBlurredBackground[0] = {};
rtGUIBlurredBackground[1] = {};
rtGUIBlurredBackground[2] = {};
rtShadingRate = {};
rtFSR[0] = {};
rtFSR[1] = {};
rtOutlineSource = {};
rtPostprocess = {};
depthBuffer_Main = {};
depthBuffer_Copy = {};
depthBuffer_Copy1 = {};
depthBuffer_Reflection = {};
rtLinearDepth = {};
reprojectedDepth = {};
debugUAV = {};
tiledLightResources = {};
tiledLightResources_planarReflection = {};
luminanceResources = {};
ssaoResources = {};
msaoResources = {};
rtaoResources = {};
rtdiffuseResources = {};
rtreflectionResources = {};
ssrResources = {};
rtshadowResources = {};
screenspaceshadowResources = {};
depthoffieldResources = {};
motionblurResources = {};
aerialperspectiveResources = {};
aerialperspectiveResources_reflection = {};
volumetriccloudResources = {};
volumetriccloudResources_reflection = {};
bloomResources = {};
surfelGIResources = {};
temporalAAResources = {};
visibilityResources = {};
fsr2Resources = {};
vxgiResources = {};
}
void RenderPath3D::ResizeBuffers()
{
first_frame = true;
DeleteGPUResources();
GraphicsDevice* device = wi::graphics::GetDevice();
XMUINT2 internalResolution = GetInternalResolution();
camera->width = (float)internalResolution.x;
camera->height = (float)internalResolution.y;
// Render targets:
{
TextureDesc desc;
desc.format = wi::renderer::format_rendertarget_main;
desc.bind_flags = BindFlag::RENDER_TARGET | BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
desc.width = internalResolution.x;
desc.height = internalResolution.y;
desc.sample_count = 1;
device->CreateTexture(&desc, nullptr, &rtMain);
device->SetName(&rtMain, "rtMain");
if (getMSAASampleCount() > 1)
{
desc.sample_count = getMSAASampleCount();
desc.bind_flags = BindFlag::RENDER_TARGET | BindFlag::SHADER_RESOURCE;
device->CreateTexture(&desc, nullptr, &rtMain_render);
device->SetName(&rtMain_render, "rtMain_render");
}
else
{
rtMain_render = rtMain;
}
}
{
TextureDesc desc;
desc.format = wi::renderer::format_idbuffer;
desc.bind_flags = BindFlag::RENDER_TARGET | BindFlag::SHADER_RESOURCE;
if (getMSAASampleCount() > 1)
{
desc.bind_flags |= BindFlag::UNORDERED_ACCESS;
}
desc.width = internalResolution.x;
desc.height = internalResolution.y;
desc.sample_count = 1;
desc.layout = ResourceState::SHADER_RESOURCE_COMPUTE;
desc.misc_flags = ResourceMiscFlag::ALIASING_TEXTURE_RT_DS;
device->CreateTexture(&desc, nullptr, &rtPrimitiveID);
device->SetName(&rtPrimitiveID, "rtPrimitiveID");
if (getMSAASampleCount() > 1)
{
desc.sample_count = getMSAASampleCount();
desc.bind_flags = BindFlag::RENDER_TARGET | BindFlag::SHADER_RESOURCE;
desc.misc_flags = ResourceMiscFlag::NONE;
device->CreateTexture(&desc, nullptr, &rtPrimitiveID_render);
device->SetName(&rtPrimitiveID_render, "rtPrimitiveID_render");
}
else
{
rtPrimitiveID_render = rtPrimitiveID;
}
}
{
TextureDesc desc;
desc.bind_flags = BindFlag::RENDER_TARGET | BindFlag::SHADER_RESOURCE;
desc.format = Format::R16G16_FLOAT;
desc.width = internalResolution.x;
desc.height = internalResolution.y;
desc.sample_count = 1;
desc.misc_flags = ResourceMiscFlag::ALIASING_TEXTURE_RT_DS;
device->CreateTexture(&desc, nullptr, &rtParticleDistortion);
device->SetName(&rtParticleDistortion, "rtParticleDistortion");
if (getMSAASampleCount() > 1)
{
desc.sample_count = getMSAASampleCount();
desc.misc_flags = ResourceMiscFlag::NONE;
device->CreateTexture(&desc, nullptr, &rtParticleDistortion_render);
device->SetName(&rtParticleDistortion_render, "rtParticleDistortion_render");
}
else
{
rtParticleDistortion_render = rtParticleDistortion;
}
}
{
TextureDesc desc;
desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
desc.format = wi::renderer::format_rendertarget_main;
desc.width = internalResolution.x / 4;
desc.height = internalResolution.y / 4;
desc.mip_levels = std::min(8u, (uint32_t)std::log2(std::max(desc.width, desc.height)));
device->CreateTexture(&desc, nullptr, &rtSceneCopy);
device->SetName(&rtSceneCopy, "rtSceneCopy");
desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS | BindFlag::RENDER_TARGET; // render target for aliasing
device->CreateTexture(&desc, nullptr, &rtSceneCopy_tmp, &rtPrimitiveID);
device->SetName(&rtSceneCopy_tmp, "rtSceneCopy_tmp");
for (uint32_t i = 0; i < rtSceneCopy.GetDesc().mip_levels; ++i)
{
int subresource_index;
subresource_index = device->CreateSubresource(&rtSceneCopy, SubresourceType::SRV, 0, 1, i, 1);
assert(subresource_index == i);
subresource_index = device->CreateSubresource(&rtSceneCopy_tmp, SubresourceType::SRV, 0, 1, i, 1);
assert(subresource_index == i);
subresource_index = device->CreateSubresource(&rtSceneCopy, SubresourceType::UAV, 0, 1, i, 1);
assert(subresource_index == i);
subresource_index = device->CreateSubresource(&rtSceneCopy_tmp, SubresourceType::UAV, 0, 1, i, 1);
assert(subresource_index == i);
}
// because this is used by SSR and SSGI before it gets a chance to be normally rendered, it MUST be cleared!
CommandList cmd = device->BeginCommandList();
device->Barrier(GPUBarrier::Image(&rtSceneCopy, rtSceneCopy.desc.layout, ResourceState::UNORDERED_ACCESS), cmd);
device->ClearUAV(&rtSceneCopy, 0, cmd);
device->Barrier(GPUBarrier::Image(&rtSceneCopy, ResourceState::UNORDERED_ACCESS, rtSceneCopy.desc.layout), cmd);
device->SubmitCommandLists();
}
{
TextureDesc desc;
desc.bind_flags = BindFlag::RENDER_TARGET | BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
desc.format = wi::renderer::format_rendertarget_main;
desc.width = internalResolution.x;
desc.height = internalResolution.y;
assert(ComputeTextureMemorySizeInBytes(desc) <= ComputeTextureMemorySizeInBytes(rtPrimitiveID.desc)); // Aliased check
device->CreateTexture(&desc, nullptr, &rtPostprocess, &rtPrimitiveID); // Aliased!
device->SetName(&rtPostprocess, "rtPostprocess");
}
{
TextureDesc desc;
desc.bind_flags = BindFlag::RENDER_TARGET | BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
desc.format = Format::R10G10B10A2_UNORM;
desc.width = internalResolution.x / 4;
desc.height = internalResolution.y / 4;
desc.bind_flags = BindFlag::UNORDERED_ACCESS | BindFlag::SHADER_RESOURCE;
device->CreateTexture(&desc, nullptr, &rtGUIBlurredBackground[0]);
device->SetName(&rtGUIBlurredBackground[0], "rtGUIBlurredBackground[0]");
desc.width /= 4;
desc.height /= 4;
device->CreateTexture(&desc, nullptr, &rtGUIBlurredBackground[1]);
device->SetName(&rtGUIBlurredBackground[1], "rtGUIBlurredBackground[1]");
device->CreateTexture(&desc, nullptr, &rtGUIBlurredBackground[2]);
device->SetName(&rtGUIBlurredBackground[2], "rtGUIBlurredBackground[2]");
}
if (device->CheckCapability(GraphicsDeviceCapability::VARIABLE_RATE_SHADING_TIER2) &&
wi::renderer::GetVariableRateShadingClassification())
{
uint32_t tileSize = device->GetVariableRateShadingTileSize();
TextureDesc desc;
desc.layout = ResourceState::UNORDERED_ACCESS;
desc.bind_flags = BindFlag::UNORDERED_ACCESS | BindFlag::SHADING_RATE;
desc.format = Format::R8_UINT;
desc.width = (internalResolution.x + tileSize - 1) / tileSize;
desc.height = (internalResolution.y + tileSize - 1) / tileSize;
device->CreateTexture(&desc, nullptr, &rtShadingRate);
device->SetName(&rtShadingRate, "rtShadingRate");
}
// Depth buffers:
{
TextureDesc desc;
desc.width = internalResolution.x;
desc.height = internalResolution.y;
desc.sample_count = getMSAASampleCount();
desc.layout = ResourceState::DEPTHSTENCIL;
desc.format = wi::renderer::format_depthbuffer_main;
desc.bind_flags = BindFlag::DEPTH_STENCIL;
device->CreateTexture(&desc, nullptr, &depthBuffer_Main);
device->SetName(&depthBuffer_Main, "depthBuffer_Main");
desc.layout = ResourceState::SHADER_RESOURCE_COMPUTE;
desc.format = Format::R32_FLOAT;
desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
desc.sample_count = 1;
desc.mip_levels = 5;
device->CreateTexture(&desc, nullptr, &depthBuffer_Copy);
device->SetName(&depthBuffer_Copy, "depthBuffer_Copy");
device->CreateTexture(&desc, nullptr, &depthBuffer_Copy1);
device->SetName(&depthBuffer_Copy1, "depthBuffer_Copy1");
for (uint32_t i = 0; i < depthBuffer_Copy.desc.mip_levels; ++i)
{
int subresource = 0;
subresource = device->CreateSubresource(&depthBuffer_Copy, SubresourceType::SRV, 0, 1, i, 1);
assert(subresource == i);
subresource = device->CreateSubresource(&depthBuffer_Copy, SubresourceType::UAV, 0, 1, i, 1);
assert(subresource == i);
subresource = device->CreateSubresource(&depthBuffer_Copy1, SubresourceType::SRV, 0, 1, i, 1);
assert(subresource == i);
subresource = device->CreateSubresource(&depthBuffer_Copy1, SubresourceType::UAV, 0, 1, i, 1);
assert(subresource == i);
}
}
{
TextureDesc desc;
desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
desc.format = Format::R32_FLOAT;
desc.width = internalResolution.x;
desc.height = internalResolution.y;
desc.mip_levels = 5;
desc.layout = ResourceState::SHADER_RESOURCE_COMPUTE;
device->CreateTexture(&desc, nullptr, &rtLinearDepth);
device->SetName(&rtLinearDepth, "rtLinearDepth");
for (uint32_t i = 0; i < desc.mip_levels; ++i)
{
int subresource_index;
subresource_index = device->CreateSubresource(&rtLinearDepth, SubresourceType::SRV, 0, 1, i, 1);
assert(subresource_index == i);
subresource_index = device->CreateSubresource(&rtLinearDepth, SubresourceType::UAV, 0, 1, i, 1);
assert(subresource_index == i);
}
}
// Other resources:
{
TextureDesc desc;
desc.width = internalResolution.x;
desc.height = internalResolution.y;
desc.mip_levels = 1;
desc.array_size = 1;
desc.format = Format::R8G8B8A8_UNORM;
desc.sample_count = 1;
desc.usage = Usage::DEFAULT;
desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
desc.layout = ResourceState::SHADER_RESOURCE;
device->CreateTexture(&desc, nullptr, &debugUAV);
device->SetName(&debugUAV, "debugUAV");
}
wi::renderer::CreateTiledLightResources(tiledLightResources, internalResolution);
wi::renderer::CreateScreenSpaceShadowResources(screenspaceshadowResources, internalResolution);
// These can trigger resource creations if needed:
setAO(ao);
setSSREnabled(ssrEnabled);
setSSGIEnabled(ssgiEnabled);
setRaytracedReflectionsEnabled(raytracedReflectionsEnabled);
setRaytracedDiffuseEnabled(raytracedDiffuseEnabled);
setFSREnabled(fsrEnabled);
setFSR2Enabled(fsr2Enabled);
setEyeAdaptionEnabled(eyeAdaptionEnabled);
setReflectionsEnabled(reflectionsEnabled);
setBloomEnabled(bloomEnabled);
setVolumeLightsEnabled(volumeLightsEnabled);
setLightShaftsEnabled(lightShaftsEnabled);
setOutlineEnabled(outlineEnabled);
RenderPath2D::ResizeBuffers();
}
void RenderPath3D::PreUpdate()
{
camera_previous = *camera;
camera_reflection_previous = camera_reflection;
}
void RenderPath3D::Update(float dt)
{
GraphicsDevice* device = wi::graphics::GetDevice();
if (rtMain_render.desc.sample_count != msaaSampleCount)
{
ResizeBuffers();
}
RenderPath2D::Update(dt);
wi::renderer::SetShadowsEnabled(getShadowsEnabled());
float update_speed = 0;
const bool hw_raytrace = device->CheckCapability(GraphicsDeviceCapability::RAYTRACING);
if (getSceneUpdateEnabled())
{
if (wi::renderer::GetSurfelGIEnabled() ||
wi::renderer::GetDDGIEnabled() ||
(hw_raytrace && wi::renderer::GetRaytracedShadowsEnabled()) ||
(hw_raytrace && getAO() == AO_RTAO) ||
(hw_raytrace && getRaytracedReflectionEnabled()) ||
(hw_raytrace && getRaytracedDiffuseEnabled())
)
{
scene->SetAccelerationStructureUpdateRequested(true);
}
scene->camera = *camera;
update_speed = dt * wi::renderer::GetGameSpeed();
}
scene->Update(update_speed);
}
void RenderPath3D::PreRender()
{
RenderPath2D::PreRender();
GraphicsDevice* device = wi::graphics::GetDevice();
// Frustum culling for main camera:
visibility_main.layerMask = getLayerMask();
visibility_main.scene = scene;
visibility_main.camera = camera;
visibility_main.flags = wi::renderer::Visibility::ALLOW_EVERYTHING;
if (!getOcclusionCullingEnabled())
{
visibility_main.flags &= ~wi::renderer::Visibility::ALLOW_OCCLUSION_CULLING;
}
wi::renderer::UpdateVisibility(visibility_main);
if (visibility_main.planar_reflection_visible)
{
// Frustum culling for planar reflections:
camera_reflection = *camera;
camera_reflection.jitter = XMFLOAT2(0, 0);
camera_reflection.Reflect(visibility_main.reflectionPlane);
visibility_reflection.layerMask = getLayerMask();
visibility_reflection.scene = scene;
visibility_reflection.camera = &camera_reflection;
visibility_reflection.flags =
wi::renderer::Visibility::ALLOW_OBJECTS |
wi::renderer::Visibility::ALLOW_EMITTERS |
wi::renderer::Visibility::ALLOW_HAIRS |
wi::renderer::Visibility::ALLOW_LIGHTS
;
wi::renderer::UpdateVisibility(visibility_reflection);
}
XMUINT2 internalResolution = GetInternalResolution();
wi::renderer::UpdatePerFrameData(
*scene,
visibility_main,
frameCB,
getSceneUpdateEnabled() ? scene->dt : 0
);
if (getFSR2Enabled())
{
camera->jitter = fsr2Resources.GetJitter();
}
else if (wi::renderer::GetTemporalAAEnabled())
{
const XMFLOAT4& halton = wi::math::GetHaltonSequence(wi::graphics::GetDevice()->GetFrameCount() % 256);
camera->jitter.x = (halton.x * 2 - 1) / (float)internalResolution.x;
camera->jitter.y = (halton.y * 2 - 1) / (float)internalResolution.y;
if (!temporalAAResources.IsValid())
{
wi::renderer::CreateTemporalAAResources(temporalAAResources, internalResolution);
}
}
else
{
camera->jitter = XMFLOAT2(0, 0);
temporalAAResources = {};
}
camera_reflection.jitter = XMFLOAT2(0, 0);
camera->UpdateCamera();
if (visibility_main.planar_reflection_visible)
{
camera_reflection.UpdateCamera();
}
if (getAO() != AO_RTAO)
{
rtaoResources.frame = 0;
}
if (!wi::renderer::GetRaytracedShadowsEnabled())
{
rtshadowResources.frame = 0;
}
if (!getSSREnabled() && !getRaytracedReflectionEnabled())
{
rtSSR = {};
}
if (!getSSGIEnabled())
{
rtSSGI = {};
}
if (!getRaytracedDiffuseEnabled())
{
rtRaytracedDiffuse = {};
}
if (getAO() == AO_DISABLED)
{
rtAO = {};
}
if (wi::renderer::GetRaytracedShadowsEnabled() && device->CheckCapability(GraphicsDeviceCapability::RAYTRACING))
{
if (!rtshadowResources.denoised.IsValid())
{
wi::renderer::CreateRTShadowResources(rtshadowResources, internalResolution);
}
}
else
{
rtshadowResources = {};
}
if (scene->weather.IsRealisticSky() && scene->weather.IsRealisticSkyAerialPerspective())
{
if (!aerialperspectiveResources.texture_output.IsValid())
{
wi::renderer::CreateAerialPerspectiveResources(aerialperspectiveResources, internalResolution);
}
if (getReflectionsEnabled() && depthBuffer_Reflection.IsValid())
{
if (!aerialperspectiveResources_reflection.texture_output.IsValid())
{
wi::renderer::CreateAerialPerspectiveResources(aerialperspectiveResources_reflection, XMUINT2(depthBuffer_Reflection.desc.width, depthBuffer_Reflection.desc.height));
}
}
else
{
aerialperspectiveResources_reflection = {};
}
}
else
{
aerialperspectiveResources = {};
}
if (scene->weather.IsVolumetricClouds())
{
if (!volumetriccloudResources.texture_cloudRender.IsValid())
{
wi::renderer::CreateVolumetricCloudResources(volumetriccloudResources, internalResolution);
}
if (getReflectionsEnabled() && depthBuffer_Reflection.IsValid())
{
if (!volumetriccloudResources_reflection.texture_cloudRender.IsValid())
{
wi::renderer::CreateVolumetricCloudResources(volumetriccloudResources_reflection, XMUINT2(depthBuffer_Reflection.desc.width, depthBuffer_Reflection.desc.height));
}
}
else
{
volumetriccloudResources_reflection = {};
}
volumetriccloudResources.AdvanceFrame();
volumetriccloudResources_reflection.AdvanceFrame();
}
else
{
volumetriccloudResources = {};
}
if (!scene->waterRipples.empty())
{
if (!rtWaterRipple.IsValid())
{
TextureDesc desc;
desc.bind_flags = BindFlag::RENDER_TARGET | BindFlag::SHADER_RESOURCE;
desc.format = Format::R16G16_FLOAT;
desc.width = internalResolution.x / 8;
desc.height = internalResolution.y / 8;
assert(ComputeTextureMemorySizeInBytes(desc) <= ComputeTextureMemorySizeInBytes(rtParticleDistortion.desc)); // aliasing check
device->CreateTexture(&desc, nullptr, &rtWaterRipple, &rtParticleDistortion); // aliased!
device->SetName(&rtWaterRipple, "rtWaterRipple");
}
}
else
{
rtWaterRipple = {};
}
if (wi::renderer::GetSurfelGIEnabled())
{
if (!surfelGIResources.result.IsValid())
{
wi::renderer::CreateSurfelGIResources(surfelGIResources, internalResolution);
}
}
if (wi::renderer::GetVXGIEnabled())
{
if (!vxgiResources.IsValid())
{
wi::renderer::CreateVXGIResources(vxgiResources, internalResolution);
}
}
else
{
vxgiResources = {};
}
// Check whether reprojected depth is required:
if (!first_frame && wi::renderer::IsMeshShaderAllowed() && wi::renderer::IsMeshletOcclusionCullingEnabled())
{
TextureDesc desc;
desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
desc.format = Format::R16_UNORM;
desc.width = internalResolution.x;
desc.height = internalResolution.y;
desc.mip_levels = GetMipCount(desc.width, desc.height, 1, 4);
desc.layout = ResourceState::SHADER_RESOURCE_COMPUTE;
device->CreateTexture(&desc, nullptr, &reprojectedDepth);
device->SetName(&reprojectedDepth, "reprojectedDepth");
for (uint32_t i = 0; i < reprojectedDepth.desc.mip_levels; ++i)
{
int subresource_index;
subresource_index = device->CreateSubresource(&reprojectedDepth, SubresourceType::SRV, 0, 1, i, 1);
assert(subresource_index == i);
subresource_index = device->CreateSubresource(&reprojectedDepth, SubresourceType::UAV, 0, 1, i, 1);
assert(subresource_index == i);
}
}
else
{
reprojectedDepth = {};
}
// Check whether velocity buffer is required:
if (
getMotionBlurEnabled() ||
wi::renderer::GetTemporalAAEnabled() ||
getSSREnabled() ||
getSSGIEnabled() ||
getRaytracedReflectionEnabled() ||
getRaytracedDiffuseEnabled() ||
wi::renderer::GetRaytracedShadowsEnabled() ||
getAO() == AO::AO_RTAO ||
wi::renderer::GetVariableRateShadingClassification() ||
getFSR2Enabled() ||
reprojectedDepth.IsValid()
)
{
if (!rtVelocity.IsValid())
{
TextureDesc desc;
desc.format = Format::R16G16_FLOAT;
desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS | BindFlag::RENDER_TARGET;
desc.width = internalResolution.x;
desc.height = internalResolution.y;
desc.layout = ResourceState::SHADER_RESOURCE_COMPUTE;
device->CreateTexture(&desc, nullptr, &rtVelocity);
device->SetName(&rtVelocity, "rtVelocity");
}
}
else
{
rtVelocity = {};
}
// Check whether shadow mask is required:
if (wi::renderer::GetScreenSpaceShadowsEnabled() || wi::renderer::GetRaytracedShadowsEnabled())
{
if (!rtShadow.IsValid())
{
TextureDesc desc;
desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
desc.format = Format::R8_UNORM;
desc.array_size = 16;
desc.width = internalResolution.x;
desc.height = internalResolution.y;
desc.layout = ResourceState::SHADER_RESOURCE_COMPUTE;
device->CreateTexture(&desc, nullptr, &rtShadow);
device->SetName(&rtShadow, "rtShadow");
}
}
else
{
rtShadow = {};
}
if (getFSR2Enabled())
{
// FSR2 also acts as a temporal AA, so we inform the shaders about it here
// This will allow improved stochastic alpha test transparency
frameCB.options |= OPTION_BIT_TEMPORALAA_ENABLED;
uint x = frameCB.frame_count % 4;
uint y = frameCB.frame_count / 4;
frameCB.temporalaa_samplerotation = (x & 0x000000FF) | ((y & 0x000000FF) << 8);
}
// Check whether visibility resources are required:
if (
visibility_shading_in_compute ||
getSSREnabled() ||
getSSGIEnabled() ||
getRaytracedReflectionEnabled() ||
getRaytracedDiffuseEnabled() ||
wi::renderer::GetScreenSpaceShadowsEnabled() ||
wi::renderer::GetRaytracedShadowsEnabled() ||
wi::renderer::GetVXGIEnabled()
)
{
if (!visibilityResources.IsValid())
{
wi::renderer::CreateVisibilityResources(visibilityResources, internalResolution);
}
}
else
{
visibilityResources = {};
}
// Check for depth of field allocation:
if (getDepthOfFieldEnabled() &&
getDepthOfFieldStrength() > 0 &&
camera->aperture_size > 0
)
{
if (!depthoffieldResources.IsValid())
{
XMUINT2 resolution = GetInternalResolution();
if (getFSR2Enabled())
{
resolution = XMUINT2(GetPhysicalWidth(), GetPhysicalHeight());
}
wi::renderer::CreateDepthOfFieldResources(depthoffieldResources, resolution);
}
}
else
{
depthoffieldResources = {};
}
// Check for motion blur allocation:
if (getMotionBlurEnabled() && getMotionBlurStrength() > 0)
{
if (!motionblurResources.IsValid())
{
XMUINT2 resolution = GetInternalResolution();
if (getFSR2Enabled())
{
resolution = XMUINT2(GetPhysicalWidth(), GetPhysicalHeight());
}
wi::renderer::CreateMotionBlurResources(motionblurResources, resolution);
}
}
else
{
motionblurResources = {};
}
// Keep a copy of last frame's depth buffer for temporal disocclusion checks, so swap with current one every frame:
std::swap(depthBuffer_Copy, depthBuffer_Copy1);
visibilityResources.depthbuffer = &depthBuffer_Copy;
visibilityResources.lineardepth = &rtLinearDepth;
if (getMSAASampleCount() > 1)
{
visibilityResources.primitiveID_resolved = &rtPrimitiveID;
}
else
{
visibilityResources.primitiveID_resolved = nullptr;
}
camera->canvas.init(*this);
camera->width = (float)internalResolution.x;
camera->height = (float)internalResolution.y;
camera->scissor = GetScissorInternalResolution();
camera->sample_count = depthBuffer_Main.desc.sample_count;
camera->shadercamera_options = SHADERCAMERA_OPTION_NONE;
camera->texture_primitiveID_index = device->GetDescriptorIndex(&rtPrimitiveID, SubresourceType::SRV);
camera->texture_depth_index = device->GetDescriptorIndex(&depthBuffer_Copy, SubresourceType::SRV);
camera->texture_lineardepth_index = device->GetDescriptorIndex(&rtLinearDepth, SubresourceType::SRV);
camera->texture_velocity_index = device->GetDescriptorIndex(&rtVelocity, SubresourceType::SRV);
camera->texture_normal_index = device->GetDescriptorIndex(&visibilityResources.texture_normals, SubresourceType::SRV);
camera->texture_roughness_index = device->GetDescriptorIndex(&visibilityResources.texture_roughness, SubresourceType::SRV);
camera->buffer_entitytiles_index = device->GetDescriptorIndex(&tiledLightResources.entityTiles, SubresourceType::SRV);
camera->texture_reflection_index = device->GetDescriptorIndex(&rtReflection_resolved, SubresourceType::SRV);
camera->texture_reflection_depth_index = device->GetDescriptorIndex(&depthBuffer_Reflection_resolved, SubresourceType::SRV);
camera->texture_refraction_index = device->GetDescriptorIndex(&rtSceneCopy, SubresourceType::SRV);
camera->texture_waterriples_index = device->GetDescriptorIndex(&rtWaterRipple, SubresourceType::SRV);
camera->texture_ao_index = device->GetDescriptorIndex(&rtAO, SubresourceType::SRV);
camera->texture_ssr_index = device->GetDescriptorIndex(&rtSSR, SubresourceType::SRV);
camera->texture_ssgi_index = device->GetDescriptorIndex(&rtSSGI, SubresourceType::SRV);
if (rtShadow.IsValid())
{
camera->shadercamera_options |= SHADERCAMERA_OPTION_USE_SHADOW_MASK;
camera->texture_rtshadow_index = device->GetDescriptorIndex(&rtShadow, SubresourceType::SRV);
}
else
{
camera->texture_rtshadow_index = device->GetDescriptorIndex(wi::texturehelper::getWhite(), SubresourceType::SRV); // AMD descriptor branching fix
}
camera->texture_rtdiffuse_index = device->GetDescriptorIndex(&rtRaytracedDiffuse, SubresourceType::SRV);
camera->texture_surfelgi_index = device->GetDescriptorIndex(&surfelGIResources.result, SubresourceType::SRV);
camera->texture_vxgi_diffuse_index = device->GetDescriptorIndex(&vxgiResources.diffuse, SubresourceType::SRV);
if (wi::renderer::GetVXGIReflectionsEnabled())
{
camera->texture_vxgi_specular_index = device->GetDescriptorIndex(&vxgiResources.specular, SubresourceType::SRV);
}
else
{
camera->texture_vxgi_specular_index = -1;
}
camera->texture_reprojected_depth_index = device->GetDescriptorIndex(&reprojectedDepth, SubresourceType::SRV);
camera_reflection.canvas.init(*this);
camera_reflection.width = (float)depthBuffer_Reflection.desc.width;
camera_reflection.height = (float)depthBuffer_Reflection.desc.height;
camera_reflection.scissor.left = 0;
camera_reflection.scissor.top = 0;
camera_reflection.scissor.right = (int)depthBuffer_Reflection.desc.width;
camera_reflection.scissor.bottom = (int)depthBuffer_Reflection.desc.height;
camera_reflection.sample_count = depthBuffer_Reflection.desc.sample_count;
camera_reflection.shadercamera_options = SHADERCAMERA_OPTION_NONE;
camera_reflection.texture_primitiveID_index = -1;
camera_reflection.texture_depth_index = device->GetDescriptorIndex(&depthBuffer_Reflection_resolved, SubresourceType::SRV);
camera_reflection.texture_lineardepth_index = -1;
camera_reflection.texture_velocity_index = -1;
camera_reflection.texture_normal_index = -1;
camera_reflection.texture_roughness_index = -1;
camera_reflection.buffer_entitytiles_index = device->GetDescriptorIndex(&tiledLightResources_planarReflection.entityTiles, SubresourceType::SRV);
camera_reflection.texture_reflection_index = -1;
camera_reflection.texture_reflection_depth_index = -1;
camera_reflection.texture_refraction_index = -1;
camera_reflection.texture_waterriples_index = -1;
camera_reflection.texture_ao_index = -1;
camera_reflection.texture_ssr_index = -1;
camera_reflection.texture_ssgi_index = -1;
camera_reflection.texture_rtshadow_index = device->GetDescriptorIndex(wi::texturehelper::getWhite(), SubresourceType::SRV); // AMD descriptor branching fix
camera_reflection.texture_rtdiffuse_index = -1;
camera_reflection.texture_surfelgi_index = -1;
camera_reflection.texture_vxgi_diffuse_index = -1;
camera_reflection.texture_vxgi_specular_index = -1;
camera_reflection.texture_reprojected_depth_index = -1;
video_cmd = {};
if (getSceneUpdateEnabled() && scene->videos.GetCount() > 0)
{
for (size_t i = 0; i < scene->videos.GetCount(); ++i)
{
const wi::scene::VideoComponent& video = scene->videos[i];
if (wi::video::IsDecodingRequired(&video.videoinstance, scene->dt))
{
video_cmd = device->BeginCommandList(QUEUE_VIDEO_DECODE);
break;
}
}
for (size_t i = 0; i < scene->videos.GetCount(); ++i)
{
wi::scene::VideoComponent& video = scene->videos[i];
wi::video::UpdateVideo(&video.videoinstance, scene->dt, video_cmd);
}
}
prerender_happened = true;
}
void RenderPath3D::Render() const
{
if (!prerender_happened)
{
// Since 0.71.694: PreRender must be called before Render() because it sets up rendering resources!
// The proper fix is to call PreRender() yourself for a manually managed RenderPath3D
// But if you don't do that, as a last resort it will be called here using const_cast
assert(0);
const_cast<RenderPath3D*>(this)->PreRender();
}
prerender_happened = false;
GraphicsDevice* device = wi::graphics::GetDevice();
wi::jobsystem::context ctx;
CommandList cmd_copypages;
if (scene->terrains.GetCount() > 0)
{
cmd_copypages = device->BeginCommandList(QUEUE_COPY);
wi::jobsystem::Execute(ctx, [this, cmd_copypages](wi::jobsystem::JobArgs args) {
for (size_t i = 0; i < scene->terrains.GetCount(); ++i)
{
scene->terrains[i].CopyVirtualTexturePageStatusGPU(cmd_copypages);
}
});
}
// Preparing the frame:
CommandList cmd = device->BeginCommandList();
wi::renderer::ProcessDeferredTextureRequests(cmd); // Execute it first thing in the frame here, on main thread, to not allow other thread steal it and execute on different command list!
CommandList cmd_prepareframe = cmd;
wi::jobsystem::Execute(ctx, [this, cmd](wi::jobsystem::JobArgs args) {
GraphicsDevice* device = wi::graphics::GetDevice();
wi::renderer::BindCameraCB(
*camera,
camera_previous,
camera_reflection,
cmd
);
wi::renderer::UpdateRenderData(visibility_main, frameCB, cmd);
uint32_t num_barriers = 2;
GPUBarrier barriers[] = {
GPUBarrier::Image(&debugUAV, debugUAV.desc.layout, ResourceState::UNORDERED_ACCESS),
GPUBarrier::Aliasing(&rtPostprocess, &rtPrimitiveID),
GPUBarrier::Image(&rtMain, rtMain.desc.layout, ResourceState::SHADER_RESOURCE_COMPUTE), // prepares transition for discard in dx12
};
if (visibility_shading_in_compute)
{
num_barriers++;
}
device->Barrier(barriers, num_barriers, cmd);
});
// async compute parallel with depth prepass
cmd = device->BeginCommandList(QUEUE_COMPUTE);
CommandList cmd_prepareframe_async = cmd;
device->WaitCommandList(cmd, cmd_prepareframe);
if (cmd_copypages.IsValid())
{
device->WaitCommandList(cmd, cmd_copypages);
}
wi::jobsystem::Execute(ctx, [this, cmd](wi::jobsystem::JobArgs args) {
wi::renderer::BindCameraCB(
*camera,
camera_previous,
camera_reflection,
cmd
);
wi::renderer::UpdateRenderDataAsync(visibility_main, frameCB, cmd);
if (scene->IsWetmapProcessingRequired())
{
wi::renderer::RefreshWetmaps(visibility_main, cmd);
}
if (scene->IsAccelerationStructureUpdateRequested())
{
wi::renderer::UpdateRaytracingAccelerationStructures(*scene, cmd);
}
if (scene->weather.IsRealisticSky())
{
wi::renderer::ComputeSkyAtmosphereTextures(cmd);
wi::renderer::ComputeSkyAtmosphereSkyViewLut(cmd);
}
if (wi::renderer::GetSurfelGIEnabled())
{
wi::renderer::SurfelGI(
surfelGIResources,
*scene,
cmd
);
}
if (wi::renderer::GetDDGIEnabled() && getSceneUpdateEnabled())
{
wi::renderer::DDGI(
*scene,
cmd
);
}
});
static const uint32_t drawscene_flags =
wi::renderer::DRAWSCENE_OPAQUE |
wi::renderer::DRAWSCENE_IMPOSTOR |
wi::renderer::DRAWSCENE_HAIRPARTICLE |
wi::renderer::DRAWSCENE_TESSELLATION |
wi::renderer::DRAWSCENE_OCCLUSIONCULLING |
wi::renderer::DRAWSCENE_MAINCAMERA
;
// Main camera depth prepass:
cmd = device->BeginCommandList();
CommandList cmd_maincamera_prepass = cmd;
wi::jobsystem::Execute(ctx, [this, cmd](wi::jobsystem::JobArgs args) {
GraphicsDevice* device = wi::graphics::GetDevice();
wi::renderer::BindCameraCB(
*camera,
camera_previous,
camera_reflection,
cmd
);
wi::renderer::RefreshImpostors(*scene, cmd);
if (reprojectedDepth.IsValid())
{
wi::renderer::ComputeReprojectedDepthPyramid(
depthBuffer_Copy,
rtVelocity,
reprojectedDepth,
cmd
);
}
RenderPassImage rp[] = {
RenderPassImage::DepthStencil(
&depthBuffer_Main,
RenderPassImage::LoadOp::CLEAR,
RenderPassImage::StoreOp::STORE,
ResourceState::DEPTHSTENCIL,
ResourceState::DEPTHSTENCIL,
ResourceState::DEPTHSTENCIL
),
RenderPassImage::RenderTarget(
&rtPrimitiveID_render,
RenderPassImage::LoadOp::CLEAR,
RenderPassImage::StoreOp::STORE,
ResourceState::SHADER_RESOURCE_COMPUTE,
ResourceState::SHADER_RESOURCE_COMPUTE
),
};
device->RenderPassBegin(rp, arraysize(rp), cmd);
device->EventBegin("Opaque Z-prepass", cmd);
auto range = wi::profiler::BeginRangeGPU("Z-Prepass", cmd);
Rect scissor = GetScissorInternalResolution();
device->BindScissorRects(1, &scissor, cmd);
Viewport vp;
vp.width = (float)depthBuffer_Main.GetDesc().width;
vp.height = (float)depthBuffer_Main.GetDesc().height;
// Foreground:
vp.min_depth = 1 - foreground_depth_range;
vp.max_depth = 1;
device->BindViewports(1, &vp, cmd);
wi::renderer::DrawScene(
visibility_main,
RENDERPASS_PREPASS,
cmd,
wi::renderer::DRAWSCENE_OPAQUE |
wi::renderer::DRAWSCENE_FOREGROUND_ONLY |
wi::renderer::DRAWSCENE_MAINCAMERA
);
// Regular:
vp.min_depth = 0;
vp.max_depth = 1;
device->BindViewports(1, &vp, cmd);
wi::renderer::DrawScene(
visibility_main,
RENDERPASS_PREPASS,
cmd,
drawscene_flags
);
wi::profiler::EndRange(range);
device->EventEnd(cmd);
device->RenderPassEnd(cmd);
});
// Main camera compute effects:
// (async compute, parallel to "shadow maps" and "update textures",
// must finish before "main scene opaque color pass")
cmd = device->BeginCommandList(QUEUE_COMPUTE);
device->WaitCommandList(cmd, cmd_maincamera_prepass);
if (video_cmd.IsValid())
{
device->WaitCommandList(cmd, video_cmd);
}
CommandList cmd_maincamera_compute_effects = cmd;
wi::jobsystem::Execute(ctx, [this, cmd](wi::jobsystem::JobArgs args) {
GraphicsDevice* device = wi::graphics::GetDevice();
for (size_t i = 0; i < scene->videos.GetCount(); ++i)
{
wi::scene::VideoComponent& video = scene->videos[i];
wi::video::ResolveVideoToRGB(&video.videoinstance, cmd);
}
wi::renderer::BindCameraCB(
*camera,
camera_previous,
camera_reflection,
cmd
);
wi::renderer::Visibility_Prepare(
visibilityResources,
rtPrimitiveID_render,
cmd
);
wi::renderer::ComputeTiledLightCulling(
tiledLightResources,
visibility_main,
debugUAV,
cmd
);
if (visibility_shading_in_compute)
{
wi::renderer::Visibility_Surface(
visibilityResources,
rtMain,
cmd
);
}
else if (
getSSREnabled() ||
getSSGIEnabled() ||
getRaytracedReflectionEnabled() ||
getRaytracedDiffuseEnabled() ||
wi::renderer::GetScreenSpaceShadowsEnabled() ||
wi::renderer::GetRaytracedShadowsEnabled() ||
wi::renderer::GetVXGIEnabled()
)
{
// These post effects require surface normals and/or roughness
wi::renderer::Visibility_Surface_Reduced(
visibilityResources,
cmd
);
}
if (rtVelocity.IsValid())
{
wi::renderer::Visibility_Velocity(
rtVelocity,
cmd
);
}
if (wi::renderer::GetSurfelGIEnabled())
{
wi::renderer::SurfelGI_Coverage(
surfelGIResources,
*scene,
rtLinearDepth,
debugUAV,
cmd
);
}
RenderAO(cmd);
if (wi::renderer::GetVariableRateShadingClassification() && device->CheckCapability(GraphicsDeviceCapability::VARIABLE_RATE_SHADING_TIER2))
{
wi::renderer::ComputeShadingRateClassification(
rtShadingRate,
debugUAV,
cmd
);
}
RenderSSR(cmd);
RenderSSGI(cmd);
if (wi::renderer::GetScreenSpaceShadowsEnabled())
{
wi::renderer::Postprocess_ScreenSpaceShadow(
screenspaceshadowResources,
tiledLightResources.entityTiles,
rtLinearDepth,
rtShadow,
cmd,
getScreenSpaceShadowRange(),
getScreenSpaceShadowSampleCount()
);
}
if (wi::renderer::GetRaytracedShadowsEnabled())
{
wi::renderer::Postprocess_RTShadow(
rtshadowResources,
*scene,
tiledLightResources.entityTiles,
rtLinearDepth,
rtShadow,
cmd
);
}
if (scene->weather.IsVolumetricClouds() && !scene->weather.IsVolumetricCloudsReceiveShadow())
{
// When volumetric cloud DOESN'T receive shadow it can be done async to shadow maps!
wi::renderer::Postprocess_VolumetricClouds(
volumetriccloudResources,
cmd,
*camera,
camera_previous,
camera_reflection,
wi::renderer::GetTemporalAAEnabled() || getFSR2Enabled(),
scene->weather.volumetricCloudsWeatherMapFirst.IsValid() ? &scene->weather.volumetricCloudsWeatherMapFirst.GetTexture() : nullptr,
scene->weather.volumetricCloudsWeatherMapSecond.IsValid() ? &scene->weather.volumetricCloudsWeatherMapSecond.GetTexture() : nullptr
);
}
});
// Occlusion culling:
CommandList cmd_occlusionculling;
if (getOcclusionCullingEnabled())
{
cmd = device->BeginCommandList();
cmd_occlusionculling = cmd;
wi::jobsystem::Execute(ctx, [this, cmd](wi::jobsystem::JobArgs args) {
GraphicsDevice* device = wi::graphics::GetDevice();
device->EventBegin("Occlusion Culling", cmd);
ScopedGPUProfiling("Occlusion Culling", cmd);
wi::renderer::BindCameraCB(
*camera,
camera_previous,
camera_reflection,
cmd
);
wi::renderer::OcclusionCulling_Reset(visibility_main, cmd); // must be outside renderpass!
RenderPassImage rp[] = {
RenderPassImage::DepthStencil(&depthBuffer_Main),
};
device->RenderPassBegin(rp, arraysize(rp), cmd);
Rect scissor = GetScissorInternalResolution();
device->BindScissorRects(1, &scissor, cmd);
Viewport vp;
vp.width = (float)depthBuffer_Main.GetDesc().width;
vp.height = (float)depthBuffer_Main.GetDesc().height;
device->BindViewports(1, &vp, cmd);
wi::renderer::OcclusionCulling_Render(*camera, visibility_main, cmd);
device->RenderPassEnd(cmd);
wi::renderer::OcclusionCulling_Resolve(visibility_main, cmd); // must be outside renderpass!
device->EventEnd(cmd);
});
}
CommandList cmd_ocean;
if (scene->weather.IsOceanEnabled() && scene->ocean.IsValid())
{
// Ocean simulation can be updated async to opaque passes:
cmd_ocean = device->BeginCommandList(QUEUE_COMPUTE);
if (cmd_occlusionculling.IsValid())
{
// Ocean occlusion culling must be waited
device->WaitCommandList(cmd_ocean, cmd_occlusionculling);
}
wi::renderer::UpdateOcean(visibility_main, cmd_ocean);
// Copying to readback is done on copy queue to use DMA instead of compute warps:
CommandList cmd_oceancopy = device->BeginCommandList(QUEUE_COPY);
device->WaitCommandList(cmd_oceancopy, cmd_ocean);
wi::renderer::ReadbackOcean(visibility_main, cmd_oceancopy);
}
// Shadow maps:
if (getShadowsEnabled())
{
cmd = device->BeginCommandList();
wi::jobsystem::Execute(ctx, [this, cmd](wi::jobsystem::JobArgs args) {
wi::renderer::DrawShadowmaps(visibility_main, cmd);
});
}
if (wi::renderer::GetVXGIEnabled() && getSceneUpdateEnabled())
{
cmd = device->BeginCommandList();
wi::jobsystem::Execute(ctx, [cmd, this](wi::jobsystem::JobArgs args) {
wi::renderer::VXGI_Voxelize(visibility_main, cmd);
});
}
// Updating textures:
if (getSceneUpdateEnabled())
{
cmd = device->BeginCommandList();
device->WaitCommandList(cmd, cmd_prepareframe_async);
wi::jobsystem::Execute(ctx, [cmd, this](wi::jobsystem::JobArgs args) {
wi::renderer::BindCommonResources(cmd);
wi::renderer::BindCameraCB(
*camera,
camera_previous,
camera_reflection,
cmd
);
wi::renderer::RefreshLightmaps(*scene, cmd);
wi::renderer::RefreshEnvProbes(visibility_main, cmd);
wi::renderer::PaintDecals(*scene, cmd);
});
}
if (getReflectionsEnabled() && visibility_main.IsRequestedPlanarReflections())
{
// Planar reflections depth prepass:
cmd = device->BeginCommandList();
wi::jobsystem::Execute(ctx, [cmd, this](wi::jobsystem::JobArgs args) {
GraphicsDevice* device = wi::graphics::GetDevice();
wi::renderer::BindCameraCB(
camera_reflection,
camera_reflection_previous,
camera_reflection,
cmd
);
// Render SkyAtmosphere assets from planar reflections point of view
if (scene->weather.IsRealisticSky())
{
wi::renderer::ComputeSkyAtmosphereSkyViewLut(cmd);
if (scene->weather.IsRealisticSkyAerialPerspective())
{
wi::renderer::ComputeSkyAtmosphereCameraVolumeLut(cmd);
}
}
device->EventBegin("Planar reflections Z-Prepass", cmd);
auto range = wi::profiler::BeginRangeGPU("Planar Reflections Z-Prepass", cmd);
RenderPassImage rp[] = {
RenderPassImage::DepthStencil(
&depthBuffer_Reflection,
RenderPassImage::LoadOp::CLEAR,
RenderPassImage::StoreOp::STORE,
ResourceState::SHADER_RESOURCE,
ResourceState::DEPTHSTENCIL,
ResourceState::SHADER_RESOURCE
)
};
device->RenderPassBegin(rp, arraysize(rp), cmd);
Viewport vp;
vp.width = (float)depthBuffer_Reflection.GetDesc().width;
vp.height = (float)depthBuffer_Reflection.GetDesc().height;
vp.min_depth = 0;
vp.max_depth = 1;
device->BindViewports(1, &vp, cmd);
wi::renderer::DrawScene(
visibility_reflection,
RENDERPASS_PREPASS_DEPTHONLY,
cmd,
wi::renderer::DRAWSCENE_OPAQUE |
wi::renderer::DRAWSCENE_IMPOSTOR |
wi::renderer::DRAWSCENE_HAIRPARTICLE |
wi::renderer::DRAWSCENE_SKIP_PLANAR_REFLECTION_OBJECTS
);
device->RenderPassEnd(cmd);
wi::renderer::ResolveMSAADepthBuffer(depthBuffer_Reflection_resolved, depthBuffer_Reflection, cmd);
if (scene->weather.IsRealisticSky() && scene->weather.IsRealisticSkyAerialPerspective())
{
wi::renderer::Postprocess_AerialPerspective(
aerialperspectiveResources_reflection,
cmd
);
}
wi::profiler::EndRange(range); // Planar Reflections
device->EventEnd(cmd);
});
// Planar reflections color pass:
cmd = device->BeginCommandList();
wi::jobsystem::Execute(ctx, [cmd, this](wi::jobsystem::JobArgs args) {
GraphicsDevice* device = wi::graphics::GetDevice();
wi::renderer::BindCameraCB(
camera_reflection,
camera_reflection_previous,
camera_reflection,
cmd
);
wi::renderer::ComputeTiledLightCulling(
tiledLightResources_planarReflection,
visibility_reflection,
Texture(),
cmd
);
if (scene->weather.IsVolumetricClouds())
{
wi::renderer::Postprocess_VolumetricClouds(
volumetriccloudResources_reflection,
cmd,
camera_reflection,
camera_reflection_previous,
camera_reflection,
wi::renderer::GetTemporalAAEnabled() || getFSR2Enabled(),
scene->weather.volumetricCloudsWeatherMapFirst.IsValid() ? &scene->weather.volumetricCloudsWeatherMapFirst.GetTexture() : nullptr,
scene->weather.volumetricCloudsWeatherMapSecond.IsValid() ? &scene->weather.volumetricCloudsWeatherMapSecond.GetTexture() : nullptr
);
}
device->EventBegin("Planar reflections", cmd);
auto range = wi::profiler::BeginRangeGPU("Planar Reflections", cmd);
RenderPassImage rp[] = {
RenderPassImage::RenderTarget(
&rtReflection,
RenderPassImage::LoadOp::CLEAR,
RenderPassImage::StoreOp::DONTCARE,
ResourceState::RENDERTARGET,
ResourceState::RENDERTARGET
),
RenderPassImage::Resolve(&rtReflection_resolved),
RenderPassImage::DepthStencil(
&depthBuffer_Reflection,
RenderPassImage::LoadOp::LOAD,
RenderPassImage::StoreOp::STORE,
ResourceState::SHADER_RESOURCE,
ResourceState::DEPTHSTENCIL,
ResourceState::SHADER_RESOURCE
),
};
device->RenderPassBegin(rp, arraysize(rp), cmd);
Viewport vp;
vp.width = (float)depthBuffer_Reflection.GetDesc().width;
vp.height = (float)depthBuffer_Reflection.GetDesc().height;
vp.min_depth = 0;
vp.max_depth = 1;
device->BindViewports(1, &vp, cmd);
wi::renderer::DrawScene(
visibility_reflection,
RENDERPASS_MAIN,
cmd,
wi::renderer::DRAWSCENE_OPAQUE |
wi::renderer::DRAWSCENE_IMPOSTOR |
wi::renderer::DRAWSCENE_HAIRPARTICLE |
wi::renderer::DRAWSCENE_SKIP_PLANAR_REFLECTION_OBJECTS
);
wi::renderer::DrawSky(*scene, cmd);
wi::renderer::DrawScene(
visibility_reflection,
RENDERPASS_MAIN,
cmd,
wi::renderer::DRAWSCENE_TRANSPARENT |
wi::renderer::DRAWSCENE_SKIP_PLANAR_REFLECTION_OBJECTS
); // separate renderscene, to be drawn after opaque and transparent sort order
if (scene->weather.IsRealisticSky() && scene->weather.IsRealisticSkyAerialPerspective())
{
// Blend Aerial Perspective on top:
device->EventBegin("Aerial Perspective Reflection Blend", cmd);
wi::image::Params fx;
fx.enableFullScreen();
fx.blendFlag = BLENDMODE_PREMULTIPLIED;
wi::image::Draw(&aerialperspectiveResources_reflection.texture_output, fx, cmd);
device->EventEnd(cmd);
}
// Blend the volumetric clouds on top:
// For planar reflections, we don't use upsample, because there is no linear depth here
if (scene->weather.IsVolumetricClouds())
{
device->EventBegin("Volumetric Clouds Reflection Blend", cmd);
wi::image::Params fx;
fx.enableFullScreen();
fx.blendFlag = BLENDMODE_PREMULTIPLIED;
wi::image::Draw(&volumetriccloudResources_reflection.texture_reproject[volumetriccloudResources_reflection.GetTemporalOutputIndex()], fx, cmd);
device->EventEnd(cmd);
}
wi::renderer::DrawSoftParticles(visibility_reflection, false, cmd);
wi::renderer::DrawSpritesAndFonts(*scene, camera_reflection, false, cmd);
device->RenderPassEnd(cmd);
wi::profiler::EndRange(range); // Planar Reflections
device->EventEnd(cmd);
});
}
// Main camera weather compute effects depending on shadow maps, envmaps, etc, but don't depend on async surface pass:
if (scene->weather.IsRealisticSky() || scene->weather.IsVolumetricClouds())
{
cmd = device->BeginCommandList();
wi::jobsystem::Execute(ctx, [this, cmd](wi::jobsystem::JobArgs args) {
wi::renderer::BindCameraCB(
*camera,
camera_previous,
camera_reflection,
cmd
);
if (scene->weather.IsRealisticSky())
{
wi::renderer::ComputeSkyAtmosphereSkyViewLut(cmd);
if (scene->weather.IsRealisticSkyAerialPerspective())
{
wi::renderer::ComputeSkyAtmosphereCameraVolumeLut(cmd);
}
}
if (scene->weather.IsRealisticSky() && scene->weather.IsRealisticSkyAerialPerspective())
{
wi::renderer::Postprocess_AerialPerspective(
aerialperspectiveResources,
cmd
);
}
if (scene->weather.IsVolumetricClouds() && scene->weather.IsVolumetricCloudsReceiveShadow())
{
// When volumetric cloud receives shadow it must be done AFTER shadow maps!
wi::renderer::Postprocess_VolumetricClouds(
volumetriccloudResources,
cmd,
*camera,
camera_previous,
camera_reflection,
wi::renderer::GetTemporalAAEnabled() || getFSR2Enabled(),
scene->weather.volumetricCloudsWeatherMapFirst.IsValid() ? &scene->weather.volumetricCloudsWeatherMapFirst.GetTexture() : nullptr,
scene->weather.volumetricCloudsWeatherMapSecond.IsValid() ? &scene->weather.volumetricCloudsWeatherMapSecond.GetTexture() : nullptr
);
}
});
}
// Main camera opaque color pass:
cmd = device->BeginCommandList();
device->WaitCommandList(cmd, cmd_maincamera_compute_effects);
wi::jobsystem::Execute(ctx, [this, cmd](wi::jobsystem::JobArgs args) {
GraphicsDevice* device = wi::graphics::GetDevice();
device->EventBegin("Opaque Scene", cmd);
wi::renderer::BindCameraCB(
*camera,
camera_previous,
camera_reflection,
cmd
);
if (getRaytracedReflectionEnabled())
{
wi::renderer::Postprocess_RTReflection(
rtreflectionResources,
*scene,
rtSSR,
cmd,
getRaytracedReflectionsRange(),
getReflectionRoughnessCutoff()
);
}
if (getRaytracedDiffuseEnabled())
{
wi::renderer::Postprocess_RTDiffuse(
rtdiffuseResources,
*scene,
rtRaytracedDiffuse,
cmd,
getRaytracedDiffuseRange()
);
}
if (wi::renderer::GetVXGIEnabled())
{
wi::renderer::VXGI_Resolve(
vxgiResources,
*scene,
rtLinearDepth,
cmd
);
}
// Depth buffers were created on COMPUTE queue, so make them available for pixel shaders here:
{
GPUBarrier barriers[] = {
GPUBarrier::Image(&rtLinearDepth, rtLinearDepth.desc.layout, ResourceState::SHADER_RESOURCE),
GPUBarrier::Image(&depthBuffer_Copy, depthBuffer_Copy.desc.layout, ResourceState::SHADER_RESOURCE),
};
device->Barrier(barriers, arraysize(barriers), cmd);
}
if (wi::renderer::GetRaytracedShadowsEnabled() || wi::renderer::GetScreenSpaceShadowsEnabled())
{
GPUBarrier barrier = GPUBarrier::Image(&rtShadow, rtShadow.desc.layout, ResourceState::SHADER_RESOURCE);
device->Barrier(&barrier, 1, cmd);
}
if (visibility_shading_in_compute)
{
wi::renderer::Visibility_Shade(
visibilityResources,
rtMain,
cmd
);
}
Viewport vp;
vp.width = (float)depthBuffer_Main.GetDesc().width;
vp.height = (float)depthBuffer_Main.GetDesc().height;
device->BindViewports(1, &vp, cmd);
Rect scissor = GetScissorInternalResolution();
device->BindScissorRects(1, &scissor, cmd);
if (getOutlineEnabled())
{
// Cut off outline source from linear depth:
device->EventBegin("Outline Source", cmd);
RenderPassImage rp[] = {
RenderPassImage::RenderTarget(&rtOutlineSource, RenderPassImage::LoadOp::CLEAR),
RenderPassImage::DepthStencil(&depthBuffer_Main, RenderPassImage::LoadOp::LOAD)
};
device->RenderPassBegin(rp, arraysize(rp), cmd);
wi::image::Params params;
params.enableFullScreen();
params.stencilRefMode = wi::image::STENCILREFMODE_ENGINE;
params.stencilComp = wi::image::STENCILMODE_EQUAL;
params.stencilRef = wi::enums::STENCILREF_OUTLINE;
wi::image::Draw(&rtLinearDepth, params, cmd);
params.stencilRef = wi::enums::STENCILREF_CUSTOMSHADER_OUTLINE;
wi::image::Draw(&rtLinearDepth, params, cmd);
device->RenderPassEnd(cmd);
device->EventEnd(cmd);
}
RenderPassImage rp[4] = {};
uint32_t rp_count = 0;
rp[rp_count++] = RenderPassImage::RenderTarget(
&rtMain_render,
visibility_shading_in_compute ? RenderPassImage::LoadOp::LOAD : RenderPassImage::LoadOp::CLEAR
);
if (getMSAASampleCount() > 1)
{
rp[rp_count++] = RenderPassImage::Resolve(&rtMain);
}
if (device->CheckCapability(GraphicsDeviceCapability::VARIABLE_RATE_SHADING_TIER2) && rtShadingRate.IsValid())
{
rp[rp_count++] = RenderPassImage::ShadingRateSource(&rtShadingRate, ResourceState::UNORDERED_ACCESS, ResourceState::UNORDERED_ACCESS);
}
rp[rp_count++] = RenderPassImage::DepthStencil(
&depthBuffer_Main,
RenderPassImage::LoadOp::LOAD,
RenderPassImage::StoreOp::STORE,
ResourceState::DEPTHSTENCIL,
ResourceState::DEPTHSTENCIL,
ResourceState::DEPTHSTENCIL
);
device->RenderPassBegin(rp, rp_count, cmd, RenderPassFlags::ALLOW_UAV_WRITES);
if (visibility_shading_in_compute)
{
// In visibility compute shading, the impostors must still be drawn using rasterization:
wi::renderer::DrawScene(
visibility_main,
RENDERPASS_MAIN,
cmd,
wi::renderer::DRAWSCENE_IMPOSTOR
);
}
else
{
auto range = wi::profiler::BeginRangeGPU("Opaque Scene", cmd);
// Foreground:
vp.min_depth = 1 - foreground_depth_range;
vp.max_depth = 1;
device->BindViewports(1, &vp, cmd);
wi::renderer::DrawScene(
visibility_main,
RENDERPASS_MAIN,
cmd,
wi::renderer::DRAWSCENE_OPAQUE |
wi::renderer::DRAWSCENE_FOREGROUND_ONLY |
wi::renderer::DRAWSCENE_MAINCAMERA
);
// Regular:
vp.min_depth = 0;
vp.max_depth = 1;
device->BindViewports(1, &vp, cmd);
wi::renderer::DrawScene(
visibility_main,
RENDERPASS_MAIN,
cmd,
drawscene_flags
);
wi::renderer::DrawSky(*scene, cmd);
wi::profiler::EndRange(range); // Opaque Scene
}
// Blend Aerial Perspective on top:
if (scene->weather.IsRealisticSky() && scene->weather.IsRealisticSkyAerialPerspective())
{
device->EventBegin("Aerial Perspective Blend", cmd);
wi::image::Params fx;
fx.enableFullScreen();
fx.blendFlag = BLENDMODE_PREMULTIPLIED;
wi::image::Draw(&aerialperspectiveResources.texture_output, fx, cmd);
device->EventEnd(cmd);
}
// Blend the volumetric clouds on top:
if (scene->weather.IsVolumetricClouds())
{
wi::renderer::Postprocess_VolumetricClouds_Upsample(volumetriccloudResources, cmd);
}
RenderOutline(cmd);
device->RenderPassEnd(cmd);
if (wi::renderer::GetRaytracedShadowsEnabled() || wi::renderer::GetScreenSpaceShadowsEnabled())
{
GPUBarrier barrier = GPUBarrier::Image(&rtShadow, ResourceState::SHADER_RESOURCE, rtShadow.desc.layout);
device->Barrier(&barrier, 1, cmd);
}
if (rtAO.IsValid())
{
device->Barrier(GPUBarrier::Aliasing(&rtAO, &rtParticleDistortion), cmd);
}
device->EventEnd(cmd);
});
if (scene->terrains.GetCount() > 0)
{
CommandList cmd_allocation_tilerequest = device->BeginCommandList(QUEUE_COMPUTE);
device->WaitCommandList(cmd_allocation_tilerequest, cmd); // wait for opaque scene
wi::jobsystem::Execute(ctx, [this, cmd_allocation_tilerequest](wi::jobsystem::JobArgs args) {
for (size_t i = 0; i < scene->terrains.GetCount(); ++i)
{
scene->terrains[i].AllocateVirtualTextureTileRequestsGPU(cmd_allocation_tilerequest);
}
});
CommandList cmd_writeback_tilerequest = device->BeginCommandList(QUEUE_COPY);
device->WaitCommandList(cmd_writeback_tilerequest, cmd_allocation_tilerequest);
wi::jobsystem::Execute(ctx, [this, cmd_writeback_tilerequest](wi::jobsystem::JobArgs args) {
for (size_t i = 0; i < scene->terrains.GetCount(); ++i)
{
scene->terrains[i].WritebackTileRequestsGPU(cmd_writeback_tilerequest);
}
});
}
// Transparents, post processes, etc:
cmd = device->BeginCommandList();
if (cmd_ocean.IsValid())
{
device->WaitCommandList(cmd, cmd_ocean);
}
wi::jobsystem::Execute(ctx, [this, cmd](wi::jobsystem::JobArgs args) {
GraphicsDevice* device = wi::graphics::GetDevice();
wi::renderer::BindCameraCB(
*camera,
camera_previous,
camera_reflection,
cmd
);
wi::renderer::BindCommonResources(cmd);
RenderLightShafts(cmd);
RenderVolumetrics(cmd);
RenderTransparents(cmd);
// Depth buffers expect a non-pixel shader resource state as they are generated on compute queue:
{
GPUBarrier barriers[] = {
GPUBarrier::Image(&rtLinearDepth, ResourceState::SHADER_RESOURCE, rtLinearDepth.desc.layout),
GPUBarrier::Image(&depthBuffer_Copy, ResourceState::SHADER_RESOURCE, depthBuffer_Copy.desc.layout),
GPUBarrier::Image(&debugUAV, ResourceState::UNORDERED_ACCESS, debugUAV.desc.layout),
};
device->Barrier(barriers, arraysize(barriers), cmd);
}
});
cmd = device->BeginCommandList();
wi::jobsystem::Execute(ctx, [this, cmd](wi::jobsystem::JobArgs args) {
RenderPostprocessChain(cmd);
wi::renderer::TextureStreamingReadbackCopy(*scene, cmd);
});
RenderPath2D::Render();
wi::jobsystem::Wait(ctx);
first_frame = false;
}
void RenderPath3D::Compose(CommandList cmd) const
{
GraphicsDevice* device = wi::graphics::GetDevice();
device->EventBegin("RenderPath3D::Compose", cmd);
wi::image::Params fx;
fx.blendFlag = BLENDMODE_OPAQUE;
fx.quality = wi::image::QUALITY_LINEAR;
fx.enableFullScreen();
wi::image::Draw(GetLastPostprocessRT(), fx, cmd);
if (
wi::renderer::GetDebugLightCulling() ||
wi::renderer::GetVariableRateShadingClassificationDebug() ||
wi::renderer::GetSurfelGIDebugEnabled()
)
{
fx.enableFullScreen();
fx.blendFlag = BLENDMODE_PREMULTIPLIED;
wi::image::Draw(&debugUAV, fx, cmd);
}
device->EventEnd(cmd);
RenderPath2D::Compose(cmd);
}
void RenderPath3D::Stop()
{
DeleteGPUResources();
}
void RenderPath3D::Start()
{
ResizeBuffers();
}
void RenderPath3D::RenderAO(CommandList cmd) const
{
if (rtAO.IsValid())
{
GetDevice()->Barrier(GPUBarrier::Aliasing(&rtParticleDistortion, &rtAO), cmd);
}
if (getAOEnabled())
{
switch (getAO())
{
case AO_SSAO:
wi::renderer::Postprocess_SSAO(
ssaoResources,
rtLinearDepth,
rtAO,
cmd,
getAORange(),
getAOSampleCount(),
getAOPower()
);
break;
case AO_HBAO:
wi::renderer::Postprocess_HBAO(
ssaoResources,
*camera,
rtLinearDepth,
rtAO,
cmd,
getAOPower()
);
break;
case AO_MSAO:
wi::renderer::Postprocess_MSAO(
msaoResources,
*camera,
rtLinearDepth,
rtAO,
cmd,
getAOPower()
);
break;
case AO_RTAO:
wi::renderer::Postprocess_RTAO(
rtaoResources,
*scene,
rtLinearDepth,
rtAO,
cmd,
getAORange(),
getAOPower()
);
break;
case AO_DISABLED:
break;
}
}
}
void RenderPath3D::RenderSSR(CommandList cmd) const
{
if (getSSREnabled() && !getRaytracedReflectionEnabled())
{
wi::renderer::Postprocess_SSR(
ssrResources,
rtSceneCopy,
rtSSR,
cmd,
getReflectionRoughnessCutoff()
);
}
}
void RenderPath3D::RenderSSGI(CommandList cmd) const
{
if (getSSGIEnabled())
{
wi::renderer::Postprocess_SSGI(
ssgiResources,
rtSceneCopy,
depthBuffer_Copy,
visibilityResources.texture_normals,
rtSSGI,
cmd,
getSSGIDepthRejection()
);
}
}
void RenderPath3D::RenderOutline(CommandList cmd) const
{
if (getOutlineEnabled())
{
wi::renderer::Postprocess_Outline(
rtOutlineSource,
cmd,
getOutlineThreshold(),
getOutlineThickness(),
getOutlineColor()
);
}
}
void RenderPath3D::RenderLightShafts(CommandList cmd) const
{
XMVECTOR sunDirection = XMLoadFloat3(&scene->weather.sunDirection);
if (getLightShaftsEnabled() && XMVectorGetX(XMVector3Dot(sunDirection, camera->GetAt())) > 0)
{
GraphicsDevice* device = wi::graphics::GetDevice();
device->EventBegin("Light Shafts", cmd);
const Texture* texture_fullres = nullptr;
// Render sun stencil cutout:
{
if (getMSAASampleCount() > 1)
{
RenderPassImage rp[] = {
RenderPassImage::RenderTarget(&rtSun[0], RenderPassImage::LoadOp::CLEAR, RenderPassImage::StoreOp::DONTCARE),
RenderPassImage::Resolve(&rtSun_resolved),
RenderPassImage::DepthStencil(
&depthBuffer_Main,
RenderPassImage::LoadOp::LOAD,
RenderPassImage::StoreOp::STORE,
ResourceState::DEPTHSTENCIL,
ResourceState::DEPTHSTENCIL,
ResourceState::DEPTHSTENCIL
),
};
device->RenderPassBegin(rp, arraysize(rp), cmd);
texture_fullres = &rtSun_resolved;
}
else
{
RenderPassImage rp[] = {
RenderPassImage::DepthStencil(
&depthBuffer_Main,
RenderPassImage::LoadOp::LOAD,
RenderPassImage::StoreOp::STORE,
ResourceState::DEPTHSTENCIL,
ResourceState::DEPTHSTENCIL,
ResourceState::DEPTHSTENCIL
),
RenderPassImage::RenderTarget(&rtSun[0], RenderPassImage::LoadOp::CLEAR),
};
device->RenderPassBegin(rp, arraysize(rp), cmd);
texture_fullres = &rtSun[0];
}
Viewport vp;
vp.width = (float)depthBuffer_Main.GetDesc().width;
vp.height = (float)depthBuffer_Main.GetDesc().height;
device->BindViewports(1, &vp, cmd);
Rect scissor = GetScissorInternalResolution();
device->BindScissorRects(1, &scissor, cmd);
wi::renderer::DrawSun(cmd);
if (scene->weather.IsVolumetricClouds())
{
device->EventBegin("Volumetric cloud occlusion mask", cmd);
wi::image::Params fx;
fx.enableFullScreen();
fx.blendFlag = BLENDMODE_MULTIPLY;
wi::image::Draw(&volumetriccloudResources.texture_cloudMask, fx, cmd);
device->EventEnd(cmd);
}
device->RenderPassEnd(cmd);
}
// Radial blur on the sun:
{
XMVECTOR sunPos = XMVector3Project(camera->GetEye() + sunDirection * camera->zFarP, 0, 0,
1.0f, 1.0f, 0.1f, 1.0f,
camera->GetProjection(), camera->GetView(), XMMatrixIdentity());
{
// Downsample to low res first:
wi::renderer::Postprocess_Downsample4x(*texture_fullres, rtSun[2], cmd);
XMFLOAT2 sun;
XMStoreFloat2(&sun, sunPos);
wi::renderer::Postprocess_LightShafts(
rtSun[2],
rtSun[1],
cmd,
sun,
getLightShaftsStrength()
);
}
}
device->EventEnd(cmd);
}
}
void RenderPath3D::RenderVolumetrics(CommandList cmd) const
{
if (getVolumeLightsEnabled() && visibility_main.IsRequestedVolumetricLights())
{
auto range = wi::profiler::BeginRangeGPU("Volumetric Lights", cmd);
GraphicsDevice* device = wi::graphics::GetDevice();
RenderPassImage rp[] = {
RenderPassImage::RenderTarget(&rtVolumetricLights, RenderPassImage::LoadOp::CLEAR),
};
device->RenderPassBegin(rp, arraysize(rp), cmd);
Viewport vp;
vp.width = (float)rtVolumetricLights.GetDesc().width;
vp.height = (float)rtVolumetricLights.GetDesc().height;
device->BindViewports(1, &vp, cmd);
wi::renderer::DrawVolumeLights(visibility_main, cmd);
device->RenderPassEnd(cmd);
wi::profiler::EndRange(range);
}
}
void RenderPath3D::RenderSceneMIPChain(CommandList cmd) const
{
GraphicsDevice* device = wi::graphics::GetDevice();
auto range = wi::profiler::BeginRangeGPU("Scene MIP Chain", cmd);
device->EventBegin("RenderSceneMIPChain", cmd);
{
GPUBarrier barriers[] = {
GPUBarrier::Aliasing(&rtPrimitiveID, &rtSceneCopy_tmp),
GPUBarrier::Image(&rtSceneCopy_tmp, rtSceneCopy_tmp.desc.layout, ResourceState::UNORDERED_ACCESS),
};
device->Barrier(barriers, arraysize(barriers), cmd);
device->ClearUAV(&rtSceneCopy_tmp, 0, cmd);
}
wi::renderer::Postprocess_Downsample4x(rtMain, rtSceneCopy, cmd);
device->Barrier(GPUBarrier::Image(&rtSceneCopy_tmp, ResourceState::UNORDERED_ACCESS, rtSceneCopy_tmp.desc.layout), cmd);
wi::renderer::MIPGEN_OPTIONS mipopt;
mipopt.gaussian_temp = &rtSceneCopy_tmp;
wi::renderer::GenerateMipChain(rtSceneCopy, wi::renderer::MIPGENFILTER_GAUSSIAN, cmd, mipopt);
device->Barrier(GPUBarrier::Aliasing(&rtSceneCopy_tmp, &rtPrimitiveID), cmd);
device->EventEnd(cmd);
wi::profiler::EndRange(range);
}
void RenderPath3D::RenderTransparents(CommandList cmd) const
{
GraphicsDevice* device = wi::graphics::GetDevice();
// Water ripple rendering:
if (!scene->waterRipples.empty())
{
device->Barrier(GPUBarrier::Aliasing(&rtParticleDistortion, &rtWaterRipple), cmd);
RenderPassImage rp[] = {
RenderPassImage::RenderTarget(&rtWaterRipple, RenderPassImage::LoadOp::CLEAR),
};
device->RenderPassBegin(rp, arraysize(rp), cmd);
Viewport vp;
vp.width = (float)rtWaterRipple.GetDesc().width;
vp.height = (float)rtWaterRipple.GetDesc().height;
device->BindViewports(1, &vp, cmd);
wi::renderer::DrawWaterRipples(visibility_main, cmd);
device->RenderPassEnd(cmd);
}
if (getFSR2Enabled())
{
// Save the pre-alpha for FSR2 reactive mask:
// Note that rtFSR temp resource is always larger or equal to rtMain, so CopyTexture is used instead of CopyResource!
GPUBarrier barriers[] = {
GPUBarrier::Image(&rtMain, rtMain.desc.layout, ResourceState::COPY_SRC),
GPUBarrier::Image(&rtFSR[1], rtFSR->desc.layout, ResourceState::COPY_DST),
};
device->Barrier(barriers, arraysize(barriers), cmd);
device->CopyTexture(
&rtFSR[1], 0, 0, 0, 0, 0,
&rtMain, 0, 0,
cmd
);
for (int i = 0; i < arraysize(barriers); ++i)
{
std::swap(barriers[i].image.layout_before, barriers[i].image.layout_after);
}
device->Barrier(barriers, arraysize(barriers), cmd);
}
Rect scissor = GetScissorInternalResolution();
device->BindScissorRects(1, &scissor, cmd);
Viewport vp;
vp.width = (float)depthBuffer_Main.GetDesc().width;
vp.height = (float)depthBuffer_Main.GetDesc().height;
vp.min_depth = 0;
vp.max_depth = 1;
device->BindViewports(1, &vp, cmd);
RenderPassImage rp[3];
uint32_t rp_count = 0;
if (getMSAASampleCount() > 1)
{
rp[rp_count++] = RenderPassImage::RenderTarget(&rtMain_render, RenderPassImage::LoadOp::LOAD);
rp[rp_count++] = RenderPassImage::Resolve(&rtMain);
rp[rp_count++] = RenderPassImage::DepthStencil(
&depthBuffer_Main,
RenderPassImage::LoadOp::LOAD,
RenderPassImage::StoreOp::STORE,
ResourceState::DEPTHSTENCIL,
ResourceState::DEPTHSTENCIL,
ResourceState::DEPTHSTENCIL
);
}
else
{
rp[rp_count++] = RenderPassImage::RenderTarget(&rtMain_render, RenderPassImage::LoadOp::LOAD);
rp[rp_count++] = RenderPassImage::DepthStencil(
&depthBuffer_Main,
RenderPassImage::LoadOp::LOAD,
RenderPassImage::StoreOp::STORE,
ResourceState::DEPTHSTENCIL,
ResourceState::DEPTHSTENCIL,
ResourceState::DEPTHSTENCIL
);
}
// Draw only the ocean first, fog and lightshafts will be blended on top:
if (scene->weather.IsOceanEnabled() && scene->ocean.IsValid() && (!scene->ocean.IsOccluded() || !wi::renderer::GetOcclusionCullingEnabled()))
{
device->EventBegin("Copy scene tex only mip0 for ocean", cmd);
wi::renderer::Postprocess_Downsample4x(rtMain, rtSceneCopy, cmd);
device->EventEnd(cmd);
device->RenderPassBegin(rp, rp_count, cmd);
wi::renderer::DrawScene(
visibility_main,
RENDERPASS_MAIN,
cmd,
wi::renderer::DRAWSCENE_OCEAN
);
device->RenderPassEnd(cmd);
}
if (visibility_main.IsTransparentsVisible())
{
RenderSceneMIPChain(cmd);
}
device->RenderPassBegin(rp, rp_count, cmd);
// Note: volumetrics and light shafts are blended before transparent scene, because they used depth of the opaques
// But the ocean is special, because it does have depth for them implicitly computed from ocean plane
if (getVolumeLightsEnabled() && visibility_main.IsRequestedVolumetricLights())
{
device->EventBegin("Contribute Volumetric Lights", cmd);
wi::renderer::Postprocess_Upsample_Bilateral(
rtVolumetricLights,
rtLinearDepth,
rtMain,
cmd,
true,
1.5f
);
device->EventEnd(cmd);
}
XMVECTOR sunDirection = XMLoadFloat3(&scene->weather.sunDirection);
if (getLightShaftsEnabled() && XMVectorGetX(XMVector3Dot(sunDirection, camera->GetAt())) > 0)
{
device->EventBegin("Contribute LightShafts", cmd);
wi::image::Params fx;
fx.enableFullScreen();
fx.blendFlag = BLENDMODE_ADDITIVE;
wi::image::Draw(&rtSun[1], fx, cmd);
device->EventEnd(cmd);
}
// Transparent scene
if (visibility_main.IsTransparentsVisible())
{
auto range = wi::profiler::BeginRangeGPU("Transparent Scene", cmd);
device->EventBegin("Transparent Scene", cmd);
// Regular:
vp.min_depth = 0;
vp.max_depth = 1;
device->BindViewports(1, &vp, cmd);
wi::renderer::DrawScene(
visibility_main,
RENDERPASS_MAIN,
cmd,
wi::renderer::DRAWSCENE_TRANSPARENT |
wi::renderer::DRAWSCENE_TESSELLATION |
wi::renderer::DRAWSCENE_OCCLUSIONCULLING |
wi::renderer::DRAWSCENE_MAINCAMERA
);
// Foreground:
vp.min_depth = 1 - foreground_depth_range;
vp.max_depth = 1;
device->BindViewports(1, &vp, cmd);
wi::renderer::DrawScene(
visibility_main,
RENDERPASS_MAIN,
cmd,
wi::renderer::DRAWSCENE_TRANSPARENT |
wi::renderer::DRAWSCENE_FOREGROUND_ONLY |
wi::renderer::DRAWSCENE_MAINCAMERA
);
// Reset normal viewport:
vp.min_depth = 0;
vp.max_depth = 1;
device->BindViewports(1, &vp, cmd);
device->EventEnd(cmd);
wi::profiler::EndRange(range); // Transparent Scene
}
wi::renderer::DrawDebugWorld(*scene, *camera, *this, cmd);
wi::renderer::DrawLightVisualizers(visibility_main, cmd);
wi::renderer::DrawSoftParticles(visibility_main, false, cmd);
wi::renderer::DrawSpritesAndFonts(*scene, *camera, false, cmd);
if (getLensFlareEnabled())
{
wi::renderer::DrawLensFlares(
visibility_main,
cmd,
scene->weather.IsVolumetricClouds() ? &volumetriccloudResources.texture_cloudMask : nullptr
);
}
device->RenderPassEnd(cmd);
// Distortion particles:
{
if (rtWaterRipple.IsValid())
{
device->Barrier(GPUBarrier::Aliasing(&rtWaterRipple, &rtParticleDistortion), cmd);
}
if (getMSAASampleCount() > 1)
{
RenderPassImage rp[] = {
RenderPassImage::RenderTarget(&rtParticleDistortion_render, RenderPassImage::LoadOp::CLEAR),
RenderPassImage::Resolve(&rtParticleDistortion),
RenderPassImage::DepthStencil(
&depthBuffer_Main,
RenderPassImage::LoadOp::LOAD,
RenderPassImage::StoreOp::STORE,
ResourceState::DEPTHSTENCIL,
ResourceState::DEPTHSTENCIL,
ResourceState::DEPTHSTENCIL
),
};
device->RenderPassBegin(rp, arraysize(rp), cmd);
}
else
{
RenderPassImage rp[] = {
RenderPassImage::RenderTarget(&rtParticleDistortion, RenderPassImage::LoadOp::CLEAR),
RenderPassImage::DepthStencil(
&depthBuffer_Main,
RenderPassImage::LoadOp::LOAD,
RenderPassImage::StoreOp::STORE,
ResourceState::DEPTHSTENCIL,
ResourceState::DEPTHSTENCIL,
ResourceState::DEPTHSTENCIL
),
};
device->RenderPassBegin(rp, arraysize(rp), cmd);
}
Viewport vp;
vp.width = (float)rtParticleDistortion.GetDesc().width;
vp.height = (float)rtParticleDistortion.GetDesc().height;
device->BindViewports(1, &vp, cmd);
wi::renderer::DrawSoftParticles(visibility_main, true, cmd);
wi::renderer::DrawSpritesAndFonts(*scene, *camera, true, cmd);
device->RenderPassEnd(cmd);
}
wi::renderer::Postprocess_Downsample4x(rtMain, rtSceneCopy, cmd);
}
void RenderPath3D::RenderPostprocessChain(CommandList cmd) const
{
GraphicsDevice* device = wi::graphics::GetDevice();
wi::renderer::BindCommonResources(cmd);
wi::renderer::BindCameraCB(*camera, camera_previous, camera_reflection, cmd);
const Texture* rt_first = nullptr; // not ping-ponged with read / write
const Texture* rt_read = &rtMain;
const Texture* rt_write = &rtPostprocess;
// rtPostprocess aliasing transition:
{
GPUBarrier barriers[] = {
GPUBarrier::Aliasing(&rtPrimitiveID, &rtPostprocess),
GPUBarrier::Image(&rtPostprocess, rtPostprocess.desc.layout, ResourceState::UNORDERED_ACCESS),
};
device->Barrier(barriers, arraysize(barriers), cmd);
device->ClearUAV(&rtPostprocess, 0, cmd);
device->Barrier(GPUBarrier::Image(&rtPostprocess, ResourceState::UNORDERED_ACCESS, rtPostprocess.desc.layout), cmd);
}
// 1.) HDR post process chain
{
if (getFSR2Enabled() && fsr2Resources.IsValid())
{
wi::renderer::Postprocess_FSR2(
fsr2Resources,
*camera,
rtFSR[1],
*rt_read,
depthBuffer_Copy,
rtVelocity,
rtFSR[0],
cmd,
scene->dt,
getFSR2Sharpness()
);
// rebind these, because FSR2 binds other things to those constant buffers:
wi::renderer::BindCameraCB(
*camera,
camera_previous,
camera_reflection,
cmd
);
wi::renderer::BindCommonResources(cmd);
rt_read = &rtFSR[0];
rt_write = &rtFSR[1];
}
else if (wi::renderer::GetTemporalAAEnabled() && !wi::renderer::GetTemporalAADebugEnabled() && temporalAAResources.IsValid())
{
wi::renderer::Postprocess_TemporalAA(
temporalAAResources,
*rt_read,
cmd
);
rt_first = temporalAAResources.GetCurrent();
}
if (scene->weather.IsOceanEnabled())
{
wi::renderer::Postprocess_Underwater(
rt_first == nullptr ? *rt_read : *rt_first,
*rt_write,
cmd
);
rt_first = nullptr;
std::swap(rt_read, rt_write);
}
for (auto& x : custom_post_processes)
{
if (x.stage == CustomPostprocess::Stage::BeforeTonemap)
{
wi::renderer::Postprocess_Custom(
x.computeshader,
rt_first == nullptr ? *rt_read : *rt_first,
*rt_write,
cmd,
x.params0,
x.params1,
x.name.c_str()
);
rt_first = nullptr;
std::swap(rt_read, rt_write);
}
}
if (getDepthOfFieldEnabled() && camera->aperture_size > 0.001f && getDepthOfFieldStrength() > 0.001f && depthoffieldResources.IsValid())
{
wi::renderer::Postprocess_DepthOfField(
depthoffieldResources,
rt_first == nullptr ? *rt_read : *rt_first,
*rt_write,
cmd,
getDepthOfFieldStrength()
);
rt_first = nullptr;
std::swap(rt_read, rt_write);
}
if (getMotionBlurEnabled() && getMotionBlurStrength() > 0 && motionblurResources.IsValid())
{
wi::renderer::Postprocess_MotionBlur(
scene->dt,
motionblurResources,
rt_first == nullptr ? *rt_read : *rt_first,
*rt_write,
cmd,
getMotionBlurStrength()
);
rt_first = nullptr;
std::swap(rt_read, rt_write);
}
}
// 2.) Tone mapping HDR -> LDR
{
// Bloom and eye adaption is not part of post process "chain",
// because they will be applied to the screen in tonemap
if (getEyeAdaptionEnabled())
{
wi::renderer::ComputeLuminance(
luminanceResources,
rt_first == nullptr ? *rt_read : *rt_first,
cmd,
getEyeAdaptionRate(),
getEyeAdaptionKey()
);
}
if (getBloomEnabled())
{
wi::renderer::ComputeBloom(
bloomResources,
rt_first == nullptr ? *rt_read : *rt_first,
cmd,
getBloomThreshold(),
getExposure(),
getEyeAdaptionEnabled() ? &luminanceResources.luminance : nullptr
);
}
wi::renderer::Postprocess_Tonemap(
rt_first == nullptr ? *rt_read : *rt_first,
*rt_write,
cmd,
getExposure(),
getBrightness(),
getContrast(),
getSaturation(),
getDitherEnabled(),
getColorGradingEnabled() ? (scene->weather.colorGradingMap.IsValid() ? &scene->weather.colorGradingMap.GetTexture() : nullptr) : nullptr,
&rtParticleDistortion,
getEyeAdaptionEnabled() ? &luminanceResources.luminance : nullptr,
getBloomEnabled() ? &bloomResources.texture_bloom : nullptr,
colorspace,
getTonemap(),
&distortion_overlay,
getHDRCalibration()
);
rt_first = nullptr;
std::swap(rt_read, rt_write);
}
// 3.) LDR post process chain
{
for (auto& x : custom_post_processes)
{
if (x.stage == CustomPostprocess::Stage::AfterTonemap)
{
wi::renderer::Postprocess_Custom(
x.computeshader,
*rt_read,
*rt_write,
cmd,
x.params0,
x.params1,
x.name.c_str()
);
std::swap(rt_read, rt_write);
}
}
if (getSharpenFilterEnabled())
{
wi::renderer::Postprocess_Sharpen(*rt_read, *rt_write, cmd, getSharpenFilterAmount());
std::swap(rt_read, rt_write);
}
if (getFXAAEnabled())
{
wi::renderer::Postprocess_FXAA(*rt_read, *rt_write, cmd);
std::swap(rt_read, rt_write);
}
if (getChromaticAberrationEnabled())
{
wi::renderer::Postprocess_Chromatic_Aberration(*rt_read, *rt_write, cmd, getChromaticAberrationAmount());
std::swap(rt_read, rt_write);
}
lastPostprocessRT = rt_read;
// GUI Background blurring:
{
auto range = wi::profiler::BeginRangeGPU("GUI Background Blur", cmd);
device->EventBegin("GUI Background Blur", cmd);
wi::renderer::Postprocess_Downsample4x(*rt_read, rtGUIBlurredBackground[0], cmd);
wi::renderer::Postprocess_Downsample4x(rtGUIBlurredBackground[0], rtGUIBlurredBackground[2], cmd);
wi::renderer::Postprocess_Blur_Gaussian(rtGUIBlurredBackground[2], rtGUIBlurredBackground[1], rtGUIBlurredBackground[2], cmd, -1, -1, true);
device->EventEnd(cmd);
wi::profiler::EndRange(range);
}
if (rtFSR[0].IsValid() && getFSREnabled())
{
wi::renderer::Postprocess_FSR(*rt_read, rtFSR[1], rtFSR[0], cmd, getFSRSharpness());
lastPostprocessRT = &rtFSR[0];
}
}
}
void RenderPath3D::setAO(AO value)
{
ao = value;
if (!rtParticleDistortion.IsValid())
return; // ResizeBuffers hasn't been called yet
rtAO = {};
ssaoResources = {};
msaoResources = {};
rtaoResources = {};
if (ao == AO_DISABLED)
{
return;
}
XMUINT2 internalResolution = GetInternalResolution();
if (internalResolution.x == 0 || internalResolution.y == 0)
return;
TextureDesc desc;
desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS | BindFlag::RENDER_TARGET; // render target binding for aliasing (in case resource heap tier < 2)
desc.format = Format::R8_UNORM;
desc.layout = ResourceState::SHADER_RESOURCE_COMPUTE;
switch (ao)
{
case RenderPath3D::AO_SSAO:
case RenderPath3D::AO_HBAO:
desc.width = internalResolution.x / 2;
desc.height = internalResolution.y / 2;
break;
case RenderPath3D::AO_MSAO:
desc.width = internalResolution.x;
desc.height = internalResolution.y;
break;
case RenderPath3D::AO_RTAO:
desc.width = internalResolution.x;
desc.height = internalResolution.y;
break;
default:
break;
}
if (ComputeTextureMemorySizeInBytes(desc) > ComputeTextureMemorySizeInBytes(rtParticleDistortion.desc))
{
// There would be resource aliasing error if we proceed like this!
// looks like ResizeBuffers() hasn't been called yet for the current internal resolution
// if this happens, then ResizeBuffers() will be called next frame probably and then AO resources
// will be created successdully
return;
}
switch (ao)
{
case RenderPath3D::AO_SSAO:
case RenderPath3D::AO_HBAO:
wi::renderer::CreateSSAOResources(ssaoResources, internalResolution);
break;
case RenderPath3D::AO_MSAO:
wi::renderer::CreateMSAOResources(msaoResources, internalResolution);
break;
case RenderPath3D::AO_RTAO:
wi::renderer::CreateRTAOResources(rtaoResources, internalResolution);
break;
default:
break;
}
GraphicsDevice* device = wi::graphics::GetDevice();
assert(ComputeTextureMemorySizeInBytes(desc) <= ComputeTextureMemorySizeInBytes(rtParticleDistortion.desc)); // aliasing check
device->CreateTexture(&desc, nullptr, &rtAO, &rtParticleDistortion); // aliasing!
device->SetName(&rtAO, "rtAO");
}
void RenderPath3D::setSSREnabled(bool value)
{
ssrEnabled = value;
if (value)
{
GraphicsDevice* device = wi::graphics::GetDevice();
XMUINT2 internalResolution = GetInternalResolution();
if (internalResolution.x == 0 || internalResolution.y == 0)
return;
TextureDesc desc;
desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
desc.format = Format::R16G16B16A16_FLOAT;
desc.width = internalResolution.x;
desc.height = internalResolution.y;
desc.layout = ResourceState::SHADER_RESOURCE_COMPUTE;
device->CreateTexture(&desc, nullptr, &rtSSR);
device->SetName(&rtSSR, "rtSSR");
wi::renderer::CreateSSRResources(ssrResources, internalResolution);
}
else
{
ssrResources = {};
}
}
void RenderPath3D::setSSGIEnabled(bool value)
{
ssgiEnabled = value;
if (value)
{
GraphicsDevice* device = wi::graphics::GetDevice();
XMUINT2 internalResolution = GetInternalResolution();
if (internalResolution.x == 0 || internalResolution.y == 0)
return;
TextureDesc desc;
desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
desc.format = Format::R16G16B16A16_FLOAT;
desc.width = internalResolution.x;
desc.height = internalResolution.y;
desc.layout = ResourceState::SHADER_RESOURCE_COMPUTE;
device->CreateTexture(&desc, nullptr, &rtSSGI);
device->SetName(&rtSSGI, "rtSSGI");
wi::renderer::CreateSSGIResources(ssgiResources, internalResolution);
}
else
{
ssgiResources = {};
}
}
void RenderPath3D::setRaytracedReflectionsEnabled(bool value)
{
raytracedReflectionsEnabled = value;
if (value)
{
GraphicsDevice* device = wi::graphics::GetDevice();
XMUINT2 internalResolution = GetInternalResolution();
if (internalResolution.x == 0 || internalResolution.y == 0)
return;
TextureDesc desc;
desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
desc.format = Format::R16G16B16A16_FLOAT;
desc.width = internalResolution.x;
desc.height = internalResolution.y;
device->CreateTexture(&desc, nullptr, &rtSSR);
device->SetName(&rtSSR, "rtSSR");
wi::renderer::CreateRTReflectionResources(rtreflectionResources, internalResolution);
}
else
{
rtreflectionResources = {};
}
}
void RenderPath3D::setRaytracedDiffuseEnabled(bool value)
{
raytracedDiffuseEnabled = value;
if (value)
{
GraphicsDevice* device = wi::graphics::GetDevice();
XMUINT2 internalResolution = GetInternalResolution();
if (internalResolution.x == 0 || internalResolution.y == 0)
return;
TextureDesc desc;
desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
desc.format = Format::R16G16B16A16_FLOAT;
desc.width = internalResolution.x;
desc.height = internalResolution.y;
device->CreateTexture(&desc, nullptr, &rtRaytracedDiffuse);
device->SetName(&rtRaytracedDiffuse, "rtRaytracedDiffuse");
wi::renderer::CreateRTDiffuseResources(rtdiffuseResources, internalResolution);
}
else
{
rtRaytracedDiffuse = {};
rtdiffuseResources = {};
}
}
void RenderPath3D::setFSREnabled(bool value)
{
fsrEnabled = value;
if (resolutionScale < 1.0f && fsrEnabled)
{
GraphicsDevice* device = wi::graphics::GetDevice();
if (GetPhysicalWidth() == 0 || GetPhysicalHeight() == 0)
return;
TextureDesc desc;
desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
desc.format = wi::renderer::format_rendertarget_main;
desc.width = GetPhysicalWidth();
desc.height = GetPhysicalHeight();
device->CreateTexture(&desc, nullptr, &rtFSR[0]);
device->SetName(&rtFSR[0], "rtFSR[0]");
device->CreateTexture(&desc, nullptr, &rtFSR[1]);
device->SetName(&rtFSR[1], "rtFSR[1]");
}
else
{
if (!getFSR2Enabled())
{
// These are used both for FSR and FSR2
rtFSR[0] = {};
rtFSR[1] = {};
}
}
}
void RenderPath3D::setFSR2Enabled(bool value)
{
fsr2Enabled = value;
if (fsr2Enabled)
{
GraphicsDevice* device = wi::graphics::GetDevice();
if (GetPhysicalWidth() == 0 || GetPhysicalHeight() == 0)
return;
XMUINT2 displayResolution = XMUINT2(
std::max(GetPhysicalWidth(), GetInternalResolution().x),
std::max(GetPhysicalHeight(), GetInternalResolution().y)
);
wi::renderer::CreateFSR2Resources(fsr2Resources, GetInternalResolution(), displayResolution);
TextureDesc desc;
desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
desc.format = wi::renderer::format_rendertarget_main;
desc.width = displayResolution.x;
desc.height = displayResolution.y;
device->CreateTexture(&desc, nullptr, &rtFSR[0]);
device->SetName(&rtFSR[0], "rtFSR[0]");
device->CreateTexture(&desc, nullptr, &rtFSR[1]);
device->SetName(&rtFSR[1], "rtFSR[1]");
}
else
{
fsr2Resources = {};
if (!getFSREnabled())
{
// These are used both for FSR and FSR2
rtFSR[0] = {};
rtFSR[1] = {};
}
}
// Depending on FSR2 is on/off, these either need to run at display or internal resolution:
motionblurResources = {};
depthoffieldResources = {};
}
void RenderPath3D::setFSR2Preset(FSR2_Preset preset)
{
wi::graphics::SamplerDesc desc = wi::renderer::GetSampler(wi::enums::SAMPLER_OBJECTSHADER)->GetDesc();
switch (preset)
{
default:
case FSR2_Preset::Quality:
resolutionScale = 1.0f / 1.5f;
desc.mip_lod_bias = -1.58f;
break;
case FSR2_Preset::Balanced:
resolutionScale = 1.0f / 1.7f;
desc.mip_lod_bias = -1.76f;
break;
case FSR2_Preset::Performance:
resolutionScale = 1.0f / 2.0f;
desc.mip_lod_bias = -2.0f;
break;
case FSR2_Preset::Ultra_Performance:
resolutionScale = 1.0f / 3.0f;
desc.mip_lod_bias = -2.58f;
break;
}
wi::renderer::ModifyObjectSampler(desc);
}
void RenderPath3D::setMotionBlurEnabled(bool value)
{
motionBlurEnabled = value;
}
void RenderPath3D::setDepthOfFieldEnabled(bool value)
{
depthOfFieldEnabled = value;
}
void RenderPath3D::setEyeAdaptionEnabled(bool value)
{
eyeAdaptionEnabled = value;
if (value)
{
wi::renderer::CreateLuminanceResources(luminanceResources, GetInternalResolution());
}
else
{
luminanceResources = {};
}
}
void RenderPath3D::setReflectionsEnabled(bool value)
{
reflectionsEnabled = value;
if (value)
{
GraphicsDevice* device = wi::graphics::GetDevice();
XMUINT2 internalResolution = GetInternalResolution();
if (internalResolution.x == 0 || internalResolution.y == 0)
return;
TextureDesc desc;
desc.sample_count = 4;
desc.bind_flags = BindFlag::RENDER_TARGET;
desc.format = wi::renderer::format_rendertarget_main;
desc.width = internalResolution.x / 4;
desc.height = internalResolution.y / 4;
desc.misc_flags = ResourceMiscFlag::TRANSIENT_ATTACHMENT;
desc.layout = ResourceState::RENDERTARGET;
device->CreateTexture(&desc, nullptr, &rtReflection);
device->SetName(&rtReflection, "rtReflection");
desc.misc_flags = ResourceMiscFlag::NONE;
desc.bind_flags = BindFlag::DEPTH_STENCIL | BindFlag::SHADER_RESOURCE;
desc.format = wi::renderer::format_depthbuffer_main;
desc.layout = ResourceState::SHADER_RESOURCE;
device->CreateTexture(&desc, nullptr, &depthBuffer_Reflection);
device->SetName(&depthBuffer_Reflection, "depthBuffer_Reflection");
desc.sample_count = 1;
desc.format = wi::renderer::format_rendertarget_main;
desc.bind_flags = BindFlag::RENDER_TARGET | BindFlag::SHADER_RESOURCE;
device->CreateTexture(&desc, nullptr, &rtReflection_resolved);
device->SetName(&rtReflection_resolved, "rtReflection_resolved");
desc.format = Format::R16_UNORM;
desc.bind_flags = BindFlag::UNORDERED_ACCESS | BindFlag::SHADER_RESOURCE;
device->CreateTexture(&desc, nullptr, &depthBuffer_Reflection_resolved);
device->SetName(&depthBuffer_Reflection_resolved, "depthBuffer_Reflection_resolved");
wi::renderer::CreateTiledLightResources(tiledLightResources_planarReflection, XMUINT2(depthBuffer_Reflection.desc.width, depthBuffer_Reflection.desc.height));
}
else
{
rtReflection = {};
depthBuffer_Reflection = {};
tiledLightResources_planarReflection = {};
}
}
void RenderPath3D::setBloomEnabled(bool value)
{
bloomEnabled = value;
if (value)
{
wi::renderer::CreateBloomResources(bloomResources, GetInternalResolution());
}
else
{
bloomResources = {};
}
}
void RenderPath3D::setVolumeLightsEnabled(bool value)
{
volumeLightsEnabled = value;
if (value)
{
GraphicsDevice* device = wi::graphics::GetDevice();
XMUINT2 internalResolution = GetInternalResolution();
if (internalResolution.x == 0 || internalResolution.y == 0)
return;
TextureDesc desc;
desc.format = Format::R16G16B16A16_FLOAT;
desc.bind_flags = BindFlag::RENDER_TARGET | BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
desc.width = internalResolution.x / 2;
desc.height = internalResolution.y / 2;
device->CreateTexture(&desc, nullptr, &rtVolumetricLights);
device->SetName(&rtVolumetricLights, "rtVolumetricLights");
}
else
{
rtVolumetricLights = {};
}
}
void RenderPath3D::setLightShaftsEnabled(bool value)
{
lightShaftsEnabled = value;
if (value)
{
GraphicsDevice* device = wi::graphics::GetDevice();
XMUINT2 internalResolution = GetInternalResolution();
if (internalResolution.x == 0 || internalResolution.y == 0)
return;
TextureDesc desc;
desc.bind_flags = BindFlag::RENDER_TARGET | BindFlag::SHADER_RESOURCE;
desc.format = wi::renderer::format_rendertarget_main;
desc.width = internalResolution.x;
desc.height = internalResolution.y;
desc.sample_count = getMSAASampleCount();
device->CreateTexture(&desc, nullptr, &rtSun[0]);
device->SetName(&rtSun[0], "rtSun[0]");
desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
desc.sample_count = 1;
desc.width = internalResolution.x / 4;
desc.height = internalResolution.y / 4;
device->CreateTexture(&desc, nullptr, &rtSun[1]);
device->SetName(&rtSun[1], "rtSun[1]");
device->CreateTexture(&desc, nullptr, &rtSun[2]);
device->SetName(&rtSun[2], "rtSun[2]");
if (getMSAASampleCount() > 1)
{
desc.width = internalResolution.x;
desc.height = internalResolution.y;
desc.sample_count = 1;
device->CreateTexture(&desc, nullptr, &rtSun_resolved);
device->SetName(&rtSun_resolved, "rtSun_resolved");
}
}
else
{
rtSun[0] = {};
rtSun[1] = {};
rtSun[2] = {};
rtSun_resolved = {};
}
}
void RenderPath3D::setOutlineEnabled(bool value)
{
outlineEnabled = value;
if (value)
{
GraphicsDevice* device = wi::graphics::GetDevice();
XMUINT2 internalResolution = GetInternalResolution();
if (internalResolution.x == 0 || internalResolution.y == 0)
return;
TextureDesc desc;
desc.bind_flags = BindFlag::RENDER_TARGET | BindFlag::SHADER_RESOURCE;
desc.format = Format::R32_FLOAT;
desc.width = internalResolution.x;
desc.height = internalResolution.y;
device->CreateTexture(&desc, nullptr, &rtOutlineSource);
device->SetName(&rtOutlineSource, "rtOutlineSource");
}
else
{
rtOutlineSource = {};
}
}
}