Envprobe MSAA (#466)

* envprobe MSAA support;
vulkan: transient attachment support;
dx12: renderpass resolve subresource improvement;

* envprobe window update

* envprobe gi boost fix

* probes will render fully diffuse scene, because inside probes there is no fallback reflection
This commit is contained in:
Turánszki János
2022-06-19 12:15:47 +02:00
committed by GitHub
parent af9187d846
commit d9174eb4bb
11 changed files with 185 additions and 54 deletions
+34 -5
View File
@@ -8,12 +8,21 @@ using namespace wi::scene;
void EnvProbeWindow::Create(EditorComponent* editor)
{
wi::gui::Window::Create("Environment Probe Window");
SetSize(XMFLOAT2(300, 200));
SetSize(XMFLOAT2(420, 220));
float x = 100, y = 0, step = 35;
float x = 5, y = 0, step = 35;
infoLabel.Create("");
infoLabel.SetText("Environment probes can be used to capture the scene from a specific location in a 360 degrees panorama. The probes will be used for reflections fallback, where a better reflection type is not available. The probes can affect the ambient colors slightly.\nTip: You can scale, rotate and move the probes to set up parallax correct rendering to affect a specific area only. The parallax correction will take effect inside the probe's bounds (indicated with a cyan colored box).");
infoLabel.SetSize(XMFLOAT2(400 - 10, 100));
infoLabel.SetPos(XMFLOAT2(x, y));
infoLabel.SetColor(wi::Color::Transparent());
AddWidget(&infoLabel);
y += infoLabel.GetScale().y + 5;
realTimeCheckBox.Create("RealTime: ");
realTimeCheckBox.SetPos(XMFLOAT2(x, y));
realTimeCheckBox.SetTooltip("Enable continuous rendering of the probe in every frame.");
realTimeCheckBox.SetPos(XMFLOAT2(x + 100, y));
realTimeCheckBox.SetEnabled(false);
realTimeCheckBox.OnClick([&](wi::gui::EventArgs args) {
EnvironmentProbeComponent* probe = wi::scene::GetScene().probes.GetComponent(entity);
@@ -25,7 +34,22 @@ void EnvProbeWindow::Create(EditorComponent* editor)
});
AddWidget(&realTimeCheckBox);
msaaCheckBox.Create("MSAA: ");
msaaCheckBox.SetTooltip("Enable Multi Sampling Anti Aliasing for the probe, this will improve its quality.");
msaaCheckBox.SetPos(XMFLOAT2(x + 200, y));
msaaCheckBox.SetEnabled(false);
msaaCheckBox.OnClick([&](wi::gui::EventArgs args) {
EnvironmentProbeComponent* probe = wi::scene::GetScene().probes.GetComponent(entity);
if (probe != nullptr)
{
probe->SetMSAA(args.bValue);
probe->SetDirty();
}
});
AddWidget(&msaaCheckBox);
generateButton.Create("Put");
generateButton.SetTooltip("Put down a new probe in front of the camera and capture the scene.");
generateButton.SetPos(XMFLOAT2(x, y += step));
generateButton.OnClick([=](wi::gui::EventArgs args) {
XMFLOAT3 pos;
@@ -48,7 +72,8 @@ void EnvProbeWindow::Create(EditorComponent* editor)
AddWidget(&generateButton);
refreshButton.Create("Refresh");
refreshButton.SetPos(XMFLOAT2(x, y += step));
refreshButton.SetTooltip("Re-renders the selected probe.");
refreshButton.SetPos(XMFLOAT2(x + 120, y));
refreshButton.SetEnabled(false);
refreshButton.OnClick([&](wi::gui::EventArgs args) {
EnvironmentProbeComponent* probe = wi::scene::GetScene().probes.GetComponent(entity);
@@ -60,7 +85,8 @@ void EnvProbeWindow::Create(EditorComponent* editor)
AddWidget(&refreshButton);
refreshAllButton.Create("Refresh All");
refreshAllButton.SetPos(XMFLOAT2(x, y += step));
refreshAllButton.SetTooltip("Re-renders all probes in the scene.");
refreshAllButton.SetPos(XMFLOAT2(x + 240, y));
refreshAllButton.SetEnabled(true);
refreshAllButton.OnClick([&](wi::gui::EventArgs args) {
Scene& scene = wi::scene::GetScene();
@@ -90,12 +116,15 @@ void EnvProbeWindow::SetEntity(Entity entity)
if (probe == nullptr)
{
realTimeCheckBox.SetEnabled(false);
msaaCheckBox.SetEnabled(false);
refreshButton.SetEnabled(false);
}
else
{
realTimeCheckBox.SetCheck(probe->IsRealTime());
realTimeCheckBox.SetEnabled(true);
msaaCheckBox.SetCheck(probe->IsMSAA());
msaaCheckBox.SetEnabled(true);
refreshButton.SetEnabled(true);
}
}
+2
View File
@@ -11,7 +11,9 @@ public:
wi::ecs::Entity entity;
void SetEntity(wi::ecs::Entity entity);
wi::gui::Label infoLabel;
wi::gui::CheckBox realTimeCheckBox;
wi::gui::CheckBox msaaCheckBox;
wi::gui::Button generateButton;
wi::gui::Button refreshButton;
wi::gui::Button refreshAllButton;
+1 -1
View File
@@ -569,7 +569,7 @@ inline void ForwardLighting(inout Surface surface, inout Lighting lighting)
[branch]
if ((surface.flags & SURFACE_FLAG_GI_APPLIED) == 0 && GetScene().ddgi.color_texture >= 0)
{
lighting.indirect.diffuse = ddgi_sample_irradiance(surface.P, surface.N);
lighting.indirect.diffuse = ddgi_sample_irradiance(surface.P, surface.N) * GetFrame().gi_boost;
surface.flags |= SURFACE_FLAG_GI_APPLIED;
}
+2
View File
@@ -161,7 +161,9 @@ struct Surface
roughness = material.roughness;
f0 = material.GetSpecular() * specularMap.rgb * specularMap.a;
#ifndef ENVMAPRENDERING
if (GetFrame().options & OPTION_BIT_FORCE_DIFFUSE_LIGHTING)
#endif // ENVMAPRENDERING
{
f0 = material.metalness = material.reflectance = 0;
}
+10 -3
View File
@@ -344,6 +344,7 @@ namespace wi::graphics
BUFFER_STRUCTURED = 1 << 3,
RAY_TRACING = 1 << 4,
PREDICATION = 1 << 5,
TRANSIENT_ATTACHMENT = 1 << 6, // hint: used in renderpass, without needing to write content to memory (VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT)
};
enum class GraphicsDeviceCapability
@@ -656,7 +657,8 @@ namespace wi::graphics
StoreOp store_op = StoreOp::STORE,
ResourceState initial_layout = ResourceState::SHADER_RESOURCE,
ResourceState subpass_layout = ResourceState::RENDERTARGET,
ResourceState final_layout = ResourceState::SHADER_RESOURCE
ResourceState final_layout = ResourceState::SHADER_RESOURCE,
int subresource_RTV = -1
)
{
RenderPassAttachment attachment;
@@ -667,6 +669,7 @@ namespace wi::graphics
attachment.initial_layout = initial_layout;
attachment.subpass_layout = subpass_layout;
attachment.final_layout = final_layout;
attachment.subresource = subresource_RTV;
return attachment;
}
@@ -676,7 +679,8 @@ namespace wi::graphics
StoreOp store_op = StoreOp::STORE,
ResourceState initial_layout = ResourceState::DEPTHSTENCIL,
ResourceState subpass_layout = ResourceState::DEPTHSTENCIL,
ResourceState final_layout = ResourceState::DEPTHSTENCIL
ResourceState final_layout = ResourceState::DEPTHSTENCIL,
int subresource_DSV = -1
)
{
RenderPassAttachment attachment;
@@ -687,13 +691,15 @@ namespace wi::graphics
attachment.initial_layout = initial_layout;
attachment.subpass_layout = subpass_layout;
attachment.final_layout = final_layout;
attachment.subresource = subresource_DSV;
return attachment;
}
static RenderPassAttachment Resolve(
const Texture* resource = nullptr,
ResourceState initial_layout = ResourceState::SHADER_RESOURCE,
ResourceState final_layout = ResourceState::SHADER_RESOURCE
ResourceState final_layout = ResourceState::SHADER_RESOURCE,
int subresource_SRV = -1
)
{
RenderPassAttachment attachment;
@@ -701,6 +707,7 @@ namespace wi::graphics
attachment.texture = resource;
attachment.initial_layout = initial_layout;
attachment.final_layout = final_layout;
attachment.subresource = subresource_SRV;
return attachment;
}
+18 -9
View File
@@ -1481,7 +1481,7 @@ namespace dx12_internal
const Texture* shading_rate_image = nullptr;
// Due to a API bug, this resolve_subresources array must be kept alive between BeginRenderpass() and EndRenderpass()!
D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_SUBRESOURCE_PARAMETERS resolve_subresources[D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT] = {};
wi::vector<D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_SUBRESOURCE_PARAMETERS> resolve_subresources[D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT] = {};
};
struct SwapChain_DX12
{
@@ -2314,7 +2314,7 @@ using namespace dx12_internal;
}
D3D_FEATURE_LEVEL featurelevels[] = {
D3D_FEATURE_LEVEL_12_2,
//D3D_FEATURE_LEVEL_12_2,
D3D_FEATURE_LEVEL_12_1,
D3D_FEATURE_LEVEL_12_0,
D3D_FEATURE_LEVEL_11_1,
@@ -3827,22 +3827,31 @@ using namespace dx12_internal;
if (resolve_src_counter == resolve_dst_counter)
{
auto src_internal = to_internal(src.texture);
int src_subresource = src.subresource;
const SingleDescriptor& src_descriptor = src_subresource < 0 ? src_internal->rtv : src_internal->subresources_rtv[src_subresource];
D3D12_RENDER_PASS_RENDER_TARGET_DESC& src_RTV = internal_state->RTVs[resolve_src_counter];
src_RTV.EndingAccess.Resolve.PreserveResolveSource = src_RTV.EndingAccess.Type == D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE;
src_RTV.EndingAccess.Type = D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_RESOLVE;
src_RTV.EndingAccess.Resolve.Format = clear_value.Format;
src_RTV.EndingAccess.Resolve.ResolveMode = D3D12_RESOLVE_MODE_AVERAGE;
src_RTV.EndingAccess.Resolve.SubresourceCount = 1;
src_RTV.EndingAccess.Resolve.PreserveResolveSource = src_RTV.EndingAccess.Type == D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_DISCARD ? FALSE : TRUE;
src_RTV.EndingAccess.Resolve.pDstResource = texture_internal->resource.Get();
src_RTV.EndingAccess.Resolve.pSrcResource = src_internal->resource.Get();
src_RTV.EndingAccess.Resolve.pSubresourceParameters = &internal_state->resolve_subresources[resolve_src_counter];
internal_state->resolve_subresources[resolve_src_counter].SrcRect.left = 0;
internal_state->resolve_subresources[resolve_src_counter].SrcRect.right = (LONG)texture->desc.width;
internal_state->resolve_subresources[resolve_src_counter].SrcRect.bottom = (LONG)texture->desc.height;
internal_state->resolve_subresources[resolve_src_counter].SrcRect.top = 0;
const SingleDescriptor& descriptor = subresource < 0 ? texture_internal->srv : texture_internal->subresources_srv[subresource];
src_RTV.EndingAccess.Resolve.SubresourceCount = std::max(1u, std::min(src_descriptor.rtv.Texture2DMSArray.ArraySize, src.texture->desc.array_size));
internal_state->resolve_subresources[resolve_src_counter].resize(src_RTV.EndingAccess.Resolve.SubresourceCount);
src_RTV.EndingAccess.Resolve.pSubresourceParameters = internal_state->resolve_subresources[resolve_src_counter].data();
for (UINT i = 0; i < src_RTV.EndingAccess.Resolve.SubresourceCount; ++i)
{
internal_state->resolve_subresources[resolve_src_counter][i].SrcSubresource = D3D12CalcSubresource(0, src_descriptor.rtv.Texture2DMSArray.FirstArraySlice + i, 0, src.texture->desc.mip_levels, src.texture->desc.array_size);
internal_state->resolve_subresources[resolve_src_counter][i].DstSubresource = D3D12CalcSubresource(0, descriptor.srv.Texture2DArray.FirstArraySlice + i, 0, texture->desc.mip_levels, texture->desc.array_size);
internal_state->resolve_subresources[resolve_src_counter][i].SrcRect.left = 0;
internal_state->resolve_subresources[resolve_src_counter][i].SrcRect.right = (LONG)texture->desc.width;
internal_state->resolve_subresources[resolve_src_counter][i].SrcRect.bottom = (LONG)texture->desc.height;
internal_state->resolve_subresources[resolve_src_counter][i].SrcRect.top = 0;
}
break;
}
resolve_src_counter++;
+14 -2
View File
@@ -2485,6 +2485,11 @@ using namespace vulkan_internal;
enabled_deviceExtensions = required_deviceExtensions;
if (checkExtensionSupport(VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, available_deviceExtensions))
{
// The shader compiler can still be using this extension, even though it is core in Vulkan 1.2, so enable it for now:
enabled_deviceExtensions.push_back(VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME);
}
if (checkExtensionSupport(VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME, available_deviceExtensions))
{
enabled_deviceExtensions.push_back(VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME);
@@ -3710,8 +3715,15 @@ using namespace vulkan_internal;
{
imageInfo.usage |= VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR;
}
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
if (has_flag(texture->desc.misc_flags, ResourceMiscFlag::TRANSIENT_ATTACHMENT))
{
imageInfo.usage |= VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
}
else
{
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
imageInfo.flags = 0;
if (has_flag(texture->desc.misc_flags, ResourceMiscFlag::TEXTURECUBE))
+8 -1
View File
@@ -6319,7 +6319,14 @@ void RefreshEnvProbes(const Visibility& vis, CommandList cmd)
RefreshAtmosphericScatteringTextures(cmd);
}
device->RenderPassBegin(&vis.scene->renderpasses_envmap[probe.textureIndex], cmd);
if (probe.IsMSAA())
{
device->RenderPassBegin(&vis.scene->renderpasses_envmap_MSAA[probe.textureIndex], cmd);
}
else
{
device->RenderPassBegin(&vis.scene->renderpasses_envmap[probe.textureIndex], cmd);
}
// Scene will only be rendered if this is a real probe entity:
if (probe_aabb.layerMask & vis.layerMask)
+88 -32
View File
@@ -3733,20 +3733,31 @@ namespace wi::scene
TextureDesc desc;
desc.array_size = 6;
desc.bind_flags = BindFlag::DEPTH_STENCIL;
desc.format = Format::D16_UNORM;
desc.height = envmapRes;
desc.width = envmapRes;
desc.mip_levels = 1;
desc.misc_flags = ResourceMiscFlag::TEXTURECUBE;
desc.usage = Usage::DEFAULT;
desc.layout = ResourceState::DEPTHSTENCIL;
desc.bind_flags = BindFlag::DEPTH_STENCIL;
desc.format = Format::D16_UNORM;
desc.layout = ResourceState::DEPTHSTENCIL;
desc.misc_flags = ResourceMiscFlag::TRANSIENT_ATTACHMENT;
device->CreateTexture(&desc, nullptr, &envrenderingDepthBuffer);
device->SetName(&envrenderingDepthBuffer, "envrenderingDepthBuffer");
desc.sample_count = envmapMSAASampleCount;
device->CreateTexture(&desc, nullptr, &envrenderingDepthBuffer_MSAA);
device->SetName(&envrenderingDepthBuffer_MSAA, "envrenderingDepthBuffer_MSAA");
desc.bind_flags = BindFlag::RENDER_TARGET;
desc.format = Format::R11G11B10_FLOAT;
desc.layout = ResourceState::RENDERTARGET;
desc.misc_flags = ResourceMiscFlag::TRANSIENT_ATTACHMENT;
device->CreateTexture(&desc, nullptr, &envrenderingColorBuffer_MSAA);
device->SetName(&envrenderingColorBuffer_MSAA, "envrenderingColorBuffer_MSAA");
desc.sample_count = 1;
desc.array_size = envmapCount * 6;
desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::RENDER_TARGET | BindFlag::UNORDERED_ACCESS;
desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS | BindFlag::RENDER_TARGET;
desc.format = Format::R11G11B10_FLOAT;
desc.height = envmapRes;
desc.width = envmapRes;
@@ -3758,32 +3769,7 @@ namespace wi::scene
device->CreateTexture(&desc, nullptr, &envmapArray);
device->SetName(&envmapArray, "envmapArray");
renderpasses_envmap.resize(envmapCount);
for (uint32_t i = 0; i < envmapCount; ++i)
{
int subresource_index;
subresource_index = device->CreateSubresource(&envmapArray, SubresourceType::RTV, i * 6, 6, 0, 1);
assert(subresource_index == i);
RenderPassDesc renderpassdesc;
renderpassdesc.attachments.push_back(
RenderPassAttachment::RenderTarget(&envmapArray,
RenderPassAttachment::LoadOp::DONTCARE
)
);
renderpassdesc.attachments.back().subresource = subresource_index;
renderpassdesc.attachments.push_back(
RenderPassAttachment::DepthStencil(
&envrenderingDepthBuffer,
RenderPassAttachment::LoadOp::CLEAR,
RenderPassAttachment::StoreOp::DONTCARE
)
);
device->CreateRenderPass(&renderpassdesc, &renderpasses_envmap[subresource_index]);
}
// Cube arrays per mip level:
for (uint32_t i = 0; i < envmapArray.desc.mip_levels; ++i)
{
int subresource_index;
@@ -3793,13 +3779,83 @@ namespace wi::scene
assert(subresource_index == i);
}
// debug probe views, individual cubes:
// individual cubes with mips:
for (uint32_t i = 0; i < envmapCount; ++i)
{
int subresource_index;
subresource_index = device->CreateSubresource(&envmapArray, SubresourceType::SRV, i * 6, 6, 0, -1);
assert(subresource_index == envmapArray.desc.mip_levels + i);
}
// individual cubes only mip0:
for (uint32_t i = 0; i < envmapCount; ++i)
{
int subresource_index;
subresource_index = device->CreateSubresource(&envmapArray, SubresourceType::SRV, i * 6, 6, 0, 1);
assert(subresource_index == envmapArray.desc.mip_levels + envmapCount + i);
}
renderpasses_envmap.resize(envmapCount);
renderpasses_envmap_MSAA.resize(envmapCount);
for (uint32_t i = 0; i < envmapCount; ++i)
{
// Non MSAA:
{
int subresource_index;
subresource_index = device->CreateSubresource(&envmapArray, SubresourceType::RTV, i * 6, 6, 0, 1);
assert(subresource_index == i);
RenderPassDesc renderpassdesc;
renderpassdesc.attachments.push_back(
RenderPassAttachment::DepthStencil(
&envrenderingDepthBuffer,
RenderPassAttachment::LoadOp::CLEAR,
RenderPassAttachment::StoreOp::DONTCARE
)
);
renderpassdesc.attachments.push_back(
RenderPassAttachment::RenderTarget(&envmapArray,
RenderPassAttachment::LoadOp::DONTCARE,
RenderPassAttachment::StoreOp::STORE,
ResourceState::SHADER_RESOURCE,
ResourceState::RENDERTARGET,
ResourceState::SHADER_RESOURCE,
subresource_index
)
);
device->CreateRenderPass(&renderpassdesc, &renderpasses_envmap[i]);
}
// MSAA:
{
RenderPassDesc renderpassdesc;
renderpassdesc.attachments.clear();
renderpassdesc.attachments.push_back(
RenderPassAttachment::DepthStencil(
&envrenderingDepthBuffer_MSAA,
RenderPassAttachment::LoadOp::CLEAR,
RenderPassAttachment::StoreOp::DONTCARE
)
);
renderpassdesc.attachments.push_back(
RenderPassAttachment::RenderTarget(&envrenderingColorBuffer_MSAA,
RenderPassAttachment::LoadOp::DONTCARE,
RenderPassAttachment::StoreOp::DONTCARE,
ResourceState::RENDERTARGET,
ResourceState::RENDERTARGET,
ResourceState::RENDERTARGET
)
);
renderpassdesc.attachments.push_back(
RenderPassAttachment::Resolve(&envmapArray,
ResourceState::SHADER_RESOURCE,
ResourceState::SHADER_RESOURCE,
envmapArray.desc.mip_levels + envmapCount + i // subresource: individual cubes only mip0
)
);
device->CreateRenderPass(&renderpassdesc, &renderpasses_envmap_MSAA[i]);
}
}
}
// reconstruct envmap array status:
+7
View File
@@ -967,6 +967,7 @@ namespace wi::scene
EMPTY = 0,
DIRTY = 1 << 0,
REALTIME = 1 << 1,
MSAA = 1 << 2,
};
uint32_t _flags = DIRTY;
@@ -979,9 +980,11 @@ namespace wi::scene
inline void SetDirty(bool value = true) { if (value) { _flags |= DIRTY; } else { _flags &= ~DIRTY; } }
inline void SetRealTime(bool value) { if (value) { _flags |= REALTIME; } else { _flags &= ~REALTIME; } }
inline void SetMSAA(bool value) { if (value) { _flags |= MSAA; } else { _flags &= ~MSAA; } }
inline bool IsDirty() const { return _flags & DIRTY; }
inline bool IsRealTime() const { return _flags & REALTIME; }
inline bool IsMSAA() const { return _flags & MSAA; }
void Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri);
};
@@ -1385,9 +1388,13 @@ namespace wi::scene
static constexpr uint32_t envmapCount = 16;
static constexpr uint32_t envmapRes = 128;
static constexpr uint32_t envmapMIPs = 8;
static constexpr uint32_t envmapMSAASampleCount = 8;
wi::graphics::Texture envrenderingDepthBuffer;
wi::graphics::Texture envrenderingDepthBuffer_MSAA;
wi::graphics::Texture envrenderingColorBuffer_MSAA;
wi::graphics::Texture envmapArray;
wi::vector<wi::graphics::RenderPass> renderpasses_envmap;
wi::vector<wi::graphics::RenderPass> renderpasses_envmap_MSAA;
// Impostor state:
static constexpr uint32_t maxImpostorCount = 8;
+1 -1
View File
@@ -9,7 +9,7 @@ namespace wi::version
// minor features, major updates, breaking compatibility changes
const int minor = 60;
// minor bug fixes, alterations, refactors, updates
const int revision = 96;
const int revision = 97;
const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);