SAO now uses a hierarchical depth to improve performance

- we generate LODs for the depth map using the framegraph
- these LODs are used in the SAO shader to improve data access locality
This commit is contained in:
Mathias Agopian
2019-05-23 18:09:51 -07:00
committed by Mathias Agopian
parent 122eb8c82e
commit 9befaeb3ae
8 changed files with 128 additions and 13 deletions

View File

@@ -124,6 +124,7 @@ set(PRIVATE_HDRS
set(MATERIAL_SRCS
src/materials/defaultMaterial.mat
src/materials/mipmapDepth.mat
src/materials/skybox.mat
src/materials/skyboxRGBM.mat
src/materials/sao.mat

View File

@@ -215,15 +215,15 @@ void FEngine::init() {
.intensity(1.0f)
.build(*this));
mPostProcessManager.init(*this);
mLightManager.init(*this);
mDFG.reset(new DFG(*this));
// Always initialize the default material, most materials' depth shaders fallback on it.
mDefaultMaterial = upcast(
FMaterial::DefaultMaterialBuilder()
.package(MATERIALS_DEFAULTMATERIAL_DATA, MATERIALS_DEFAULTMATERIAL_SIZE)
.build(*const_cast<FEngine*>(this)));
mPostProcessManager.init(*this);
mLightManager.init(*this);
mDFG.reset(new DFG(*this));
}
FEngine::~FEngine() noexcept {

View File

@@ -62,10 +62,13 @@ void PostProcessManager::init(FEngine& engine) noexcept {
mSSAOMaterial = upcast(Material::Builder().package(
MATERIALS_SAO_DATA, MATERIALS_SAO_SIZE).build(engine));
mSSAOMaterialInstance = mSSAOMaterial->getDefaultInstance();
mSSAOProgram = mSSAOMaterial->getProgram(0);
mMipmapDepthMaterial = upcast(Material::Builder().package(
MATERIALS_MIPMAPDEPTH_DATA, MATERIALS_MIPMAPDEPTH_SIZE).build(engine));
mMipmapDepthMaterialInstance = mMipmapDepthMaterial->getDefaultInstance();
mMipmapDepthProgram = mMipmapDepthMaterial->getProgram(0);
}
void PostProcessManager::terminate(backend::DriverApi& driver) noexcept {
@@ -307,12 +310,16 @@ FrameGraphResource PostProcessManager::ssao(FrameGraph& fg, RenderPass& pass,
RenderPass::Command const* first = pass.getCommands().begin();
RenderPass::Command const* last = pass.getCommands().end();
// We limit the level size to 32 pixels (which is where the -5 comes from)
const size_t levelCount = std::max(1, std::ilogbf(std::max(svp.width, svp.height) / 2) + 1 - 5);
// SSAO generates its own depth path at 1/4 resolution
auto& ssaoDepthPass = fg.addPass<DepthPassData>("SSAO Depth Pass",
[&svp](FrameGraph::Builder& builder, DepthPassData& data) {
[&svp, levelCount](FrameGraph::Builder& builder, DepthPassData& data) {
data.depth = builder.createTexture("Depth Buffer", {
.width = svp.width / 2, .height = svp.height / 2,
.levels = uint8_t(levelCount),
.format = TextureFormat::DEPTH24 });
data.depth = builder.useRenderTarget("SSAO Depth Target",
@@ -326,6 +333,49 @@ FrameGraphResource PostProcessManager::ssao(FrameGraph& fg, RenderPass& pass,
FrameGraphResource depth = ssaoDepthPass.getData().depth;
// The first mip already exists, so we process n-1 lods
for (size_t level = 0; level < levelCount - 1; level++) {
struct DepthMipData {
FrameGraphResource in;
FrameGraphResource out;
};
auto& depthMipmappass = fg.addPass<DepthMipData>("Depth Mipmap Pass",
[depth, level](FrameGraph::Builder& builder, DepthMipData& data) {
const char* name = builder.getName(depth);
data.in = builder.useRenderTarget(name, {
.attachments.depth = {
depth, uint8_t(level), FrameGraphRenderTarget::Attachments::READ }}).depth;
data.out = builder.useRenderTarget(name, {
.attachments.depth = {
depth, uint8_t(level + 1), FrameGraphRenderTarget::Attachments::WRITE }}).depth;
},
[this, fullScreenRenderPrimitive, level](FrameGraphPassResources const& resources,
DepthMipData const& data, DriverApi& driver) {
auto in = resources.getTexture(data.in);
auto out = resources.getRenderTarget(data.out, level + 1u);
SamplerParams params;
FMaterialInstance* const pInstance = mMipmapDepthMaterialInstance;
pInstance->setParameter("depth", in, params);
pInstance->setParameter("level", uint32_t(level));
pInstance->commit(driver);
pInstance->use(driver);
PipelineState pipeline;
pipeline.program = mMipmapDepthProgram;
pipeline.rasterState = mMipmapDepthMaterial->getRasterState();
driver.beginRenderPass(out.target, out.params);
driver.draw(pipeline, fullScreenRenderPrimitive);
driver.endRenderPass();
});
depth = depthMipmappass.getData().out;
}
struct SSAOPassData {
FrameGraphResource depth;
FrameGraphResource ssao;
@@ -349,7 +399,7 @@ FrameGraphResource PostProcessManager::ssao(FrameGraph& fg, RenderPass& pass,
.attachments.depth = { data.depth, FrameGraphRenderTarget::Attachments::READ }
}, TargetBufferFlags::NONE).color;
},
[this, fullScreenRenderPrimitive](FrameGraphPassResources const& resources,
[this, levelCount, fullScreenRenderPrimitive](FrameGraphPassResources const& resources,
SSAOPassData const& data, DriverApi& driver) {
auto depth = resources.getTexture(data.depth);
auto ssao = resources.getRenderTarget(data.ssao);
@@ -365,6 +415,7 @@ FrameGraphResource PostProcessManager::ssao(FrameGraph& fg, RenderPass& pass,
pInstance->setParameter("projectionScaleRadius", 500.0f * data.options.radius);
pInstance->setParameter("bias", data.options.bias);
pInstance->setParameter("power", data.options.power);
pInstance->setParameter("maxLevel", uint32_t(levelCount - 1));
pInstance->commit(driver);
pInstance->use(driver);

View File

@@ -79,6 +79,10 @@ private:
details::FMaterialInstance* mSSAOMaterialInstance = nullptr;
backend::Handle<backend::HwProgram> mSSAOProgram;
details::FMaterial* mMipmapDepthMaterial = nullptr;
details::FMaterialInstance* mMipmapDepthMaterialInstance = nullptr;
backend::Handle<backend::HwProgram> mMipmapDepthProgram;
backend::Handle<backend::HwTexture> mNoSSAOTexture;
};

View File

@@ -639,7 +639,7 @@ FrameGraphResource::Descriptor const& FrameGraphPassResources::getDescriptor(
// ------------------------------------------------------------------------------------------------
FrameGraph::FrameGraph()
: mArena("FrameGraph Arena", 16384), // TODO: the Area will eventually come from outside
: mArena("FrameGraph Arena", 32768), // TODO: the Area will eventually come from outside
mPassNodes(mArena),
mResourceNodes(mArena),
mRenderTargets(mArena),

View File

@@ -0,0 +1,46 @@
material {
name : mipmapDepth,
parameters : [
{
type : sampler2d,
name : depth,
precision: high
},
{
type : int,
name : level
}
],
variables : [
],
vertexDomain : device,
depthWrite : true,
depthCulling : false,
shadingModel : unlit,
variantFilter : [ skinning ],
culling: none
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
int level = materialParams.level;
ivec2 xy = ivec2(gl_FragCoord.xy) * 2;
highp float d00 = texelFetch(materialParams_depth, xy + ivec2(0, 0), level).r;
highp float d10 = texelFetch(materialParams_depth, xy + ivec2(1, 0), level).r;
highp float d01 = texelFetch(materialParams_depth, xy + ivec2(0, 1), level).r;
highp float d11 = texelFetch(materialParams_depth, xy + ivec2(1, 1), level).r;
gl_FragDepth =
// conservative occlusion
//max(d00, max(d01, max(d10, d11)));
// conservative visibility
//min(d00, min(d01, min(d10, d11)));
// arithmetic mean: preserve screen-space area
(d00 + d01 + d10 + d11) * 0.25;
}
}

View File

@@ -1,5 +1,5 @@
material {
name : ssao,
name : sao,
parameters : [
{
type : sampler2d,
@@ -29,6 +29,10 @@ material {
{
type : float,
name : power
},
{
type : int,
name : maxLevel
}
],
variables : [
@@ -53,6 +57,8 @@ vertex {
}
fragment {
#define LOG2_LOD_RATE 4
#define NOISE_NONE 0
#define NOISE_PATTERN 1
#define NOISE_RANDOM 2
@@ -162,8 +168,8 @@ fragment {
return vec3(M * kSipralSamples[i].xy, radius * radius);
}
ivec2 clampToEdge(ivec2 uv) {
return clamp(uv, ivec2(0), textureSize(materialParams_depth, 0) - ivec2(1));
ivec2 clampToEdge(ivec2 uv, int level) {
return clamp(uv, ivec2(0), textureSize(materialParams_depth, level) - ivec2(1));
}
float computeAmbientOcclusionSAO(uint i, float ssDiskRadius, const ivec2 ssOrigin, const highp vec3 origin, const vec3 normal, const vec3 noise) {
@@ -172,7 +178,10 @@ fragment {
ivec2 ssSamplePos = ssOrigin + ivec2(ssRadius * tap.xy);
vec2 uvSamplePos = (vec2(ssSamplePos) + vec2(0.5)) * materialParams.resolution.zw;
highp float occlusionDepth = linearizeDepth(texelFetch(materialParams_depth, clampToEdge(ssSamplePos), 0).r);
// level = floor(log2(screenSpaceRadius/rate)))
int level = clamp(int(floor(log2(ssRadius))) - LOG2_LOD_RATE, 0, materialParams.maxLevel);
highp float occlusionDepth = linearizeDepth(texelFetch(materialParams_depth, clampToEdge(ssSamplePos >> level, level), level).r);
highp vec3 p = computeViewSpacePositionFromDepth(uvSamplePos * 2.0 - 1.0, occlusionDepth);
// now we have the sample, compute AO

View File

@@ -29,6 +29,10 @@ material {
{
type : float,
name : power
},
{
type : int,
name : maxLevel
}
],
variables : [