DoF: use only a single low res source for both fg and bg

Before we were keeping a foreground and background version of the
low resolution source, but in practice it really doesn't make a big
difference and we save a fair amount of bandwidth this way.

The bulk of the change is to remove a texture/target which mostly
affects the downsample and mipmap passes.
Also renamed some variables to be less confusing (MaxMin to MinMax for e.g.)
and fixed a few typos.
This commit is contained in:
Mathias Agopian
2021-03-29 21:28:55 -07:00
committed by Mathias Agopian
parent c4eb790065
commit e5bfe81d0c
10 changed files with 210 additions and 283 deletions

View File

@@ -940,9 +940,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
struct PostProcessDofDownsample {
FrameGraphId<FrameGraphTexture> color;
FrameGraphId<FrameGraphTexture> depth;
FrameGraphId<FrameGraphTexture> outForeground;
FrameGraphId<FrameGraphTexture> outBackground;
FrameGraphId<FrameGraphTexture> outCocFgBg;
FrameGraphId<FrameGraphTexture> outColor;
FrameGraphId<FrameGraphTexture> outCoc;
};
auto& ppDoFDownsample = fg.addPass<PostProcessDofDownsample>("DoF Downsample",
@@ -950,21 +949,21 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
data.color = builder.sample(input);
data.depth = builder.sample(depth);
data.outForeground = builder.createTexture("dof foreground output", {
data.outColor = builder.createTexture("dof downsample output", {
.width = width, .height = height, .levels = mipmapCount, .format = format
});
data.outBackground = builder.createTexture("dof background output", {
.width = width, .height = height, .levels = mipmapCount, .format = format
});
data.outCocFgBg = builder.createTexture("dof CoC output", {
data.outCoc = builder.createTexture("dof CoC output", {
.width = width, .height = height, .levels = mipmapCount,
.format = TextureFormat::RG16F
.format = TextureFormat::R16F,
.swizzle = {
// the next stage expects min/max CoC in the red/green channel
.r = backend::TextureSwizzle::CHANNEL_0,
.g = backend::TextureSwizzle::CHANNEL_0 },
});
data.outForeground = builder.write(data.outForeground, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
data.outBackground = builder.write(data.outBackground, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
data.outCocFgBg = builder.write(data.outCocFgBg, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
data.outColor = builder.write(data.outColor, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
data.outCoc = builder.write(data.outCoc, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
builder.declareRenderPass("DoF Target", { .attachments = {
.color = { data.outForeground, data.outBackground, data.outCocFgBg }
.color = { data.outColor, data.outCoc }
}
});
},
@@ -990,9 +989,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
*/
struct PostProcessDofMipmap {
FrameGraphId<FrameGraphTexture> inOutForeground;
FrameGraphId<FrameGraphTexture> inOutBackground;
FrameGraphId<FrameGraphTexture> inOutCocFgBg;
FrameGraphId<FrameGraphTexture> inOutColor;
FrameGraphId<FrameGraphTexture> inOutCoc;
uint32_t rp[maxMipLevels];
};
@@ -1000,25 +998,22 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
auto& ppDoFMipmap = fg.addPass<PostProcessDofMipmap>("DoF Mipmap",
[&](FrameGraph::Builder& builder, auto& data) {
data.inOutForeground = builder.sample(ppDoFDownsample->outForeground);
data.inOutBackground = builder.sample(ppDoFDownsample->outBackground);
data.inOutCocFgBg = builder.sample(ppDoFDownsample->outCocFgBg);
data.inOutColor = builder.sample(ppDoFDownsample->outColor);
data.inOutCoc = builder.sample(ppDoFDownsample->outCoc);
for (size_t i = 0; i < mipmapCount - 1u; i++) {
// make sure inputs are always multiple of two (should be true by construction)
// (this is so that we can compute clean mip levels)
assert_invariant((FTexture::valueForLevel(uint8_t(i), fg.getDescriptor(data.inOutForeground).width ) & 0x1u) == 0);
assert_invariant((FTexture::valueForLevel(uint8_t(i), fg.getDescriptor(data.inOutForeground).height) & 0x1u) == 0);
assert_invariant((FTexture::valueForLevel(uint8_t(i), fg.getDescriptor(data.inOutColor).width ) & 0x1u) == 0);
assert_invariant((FTexture::valueForLevel(uint8_t(i), fg.getDescriptor(data.inOutColor).height) & 0x1u) == 0);
auto inOutForeground = builder.createSubresource(data.inOutForeground, "Forground mip", { .level = uint8_t(i + 1) });
auto inOutBackground = builder.createSubresource(data.inOutBackground, "Background mip", { .level = uint8_t(i + 1) });
auto inOutCocFgBg = builder.createSubresource(data.inOutCocFgBg , "CocFgBg mip", { .level = uint8_t(i + 1) });
auto inOutColor = builder.createSubresource(data.inOutColor, "Color mip", { .level = uint8_t(i + 1) });
auto inOutCoc = builder.createSubresource(data.inOutCoc, "Coc mip", { .level = uint8_t(i + 1) });
inOutForeground = builder.write(inOutForeground, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
inOutBackground = builder.write(inOutBackground, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
inOutCocFgBg = builder.write(inOutCocFgBg, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
inOutColor = builder.write(inOutColor, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
inOutCoc = builder.write(inOutCoc, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
data.rp[i] = builder.declareRenderPass("DoF Target", { .attachments = {
.color = { inOutForeground, inOutBackground, inOutCocFgBg }
.color = { inOutColor, inOutCoc }
}
});
}
@@ -1026,15 +1021,13 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
[=](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
auto inOutForeground = resources.getTexture(data.inOutForeground);
auto inOutBackground = resources.getTexture(data.inOutBackground);
auto inOutCocFgBg = resources.getTexture(data.inOutCocFgBg);
auto inOutColor = resources.getTexture(data.inOutColor);
auto inOutCoc = resources.getTexture(data.inOutCoc);
auto const& material = getPostProcessMaterial("dofMipmap");
FMaterialInstance* const mi = material.getMaterialInstance();
mi->setParameter("foreground", inOutForeground, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
mi->setParameter("background", inOutBackground, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
mi->setParameter("cocFgBg", inOutCocFgBg, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
mi->setParameter("color", inOutColor, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
mi->setParameter("coc", inOutCoc, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
mi->use(driver);
const PipelineState pipeline(material.getPipelineState(variant));
@@ -1055,7 +1048,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
* - Generate min/max tiles for far/near fields (continued)
*/
auto inTilesCocMaxMin = ppDoFDownsample->outCocFgBg;
auto inTilesCocMinMax = ppDoFDownsample->outCoc;
// TODO: Should the tile size be in real pixels? i.e. always 16px instead of being dependant on
// the DoF effect resolution?
@@ -1067,8 +1060,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
const size_t tileReductionCount = std::log2(tileSize / dofResolution);
struct PostProcessDofTiling1 {
FrameGraphId<FrameGraphTexture> inCocMaxMin;
FrameGraphId<FrameGraphTexture> outTilesCocMaxMin;
FrameGraphId<FrameGraphTexture> inCocMinMax;
FrameGraphId<FrameGraphTexture> outTilesCocMinMax;
};
for (size_t i = 0; i < tileReductionCount; i++) {
@@ -1076,29 +1069,29 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
[&](FrameGraph::Builder& builder, auto& data) {
assert_invariant(((tileBufferWidth >> i) & 1u) == 0);
assert_invariant(((tileBufferHeight >> i) & 1u) == 0);
data.inCocMaxMin = builder.sample(inTilesCocMaxMin);
data.outTilesCocMaxMin = builder.createTexture("dof tiles output", {
data.inCocMinMax = builder.sample(inTilesCocMinMax);
data.outTilesCocMinMax = builder.createTexture("dof tiles output", {
.width = tileBufferWidth >> (i + 1u),
.height = tileBufferHeight >> (i + 1u),
.format = TextureFormat::RG16F
});
data.outTilesCocMaxMin = builder.declareRenderPass(data.outTilesCocMaxMin);
data.outTilesCocMinMax = builder.declareRenderPass(data.outTilesCocMinMax);
},
[=](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
auto const& inputDesc = resources.getDescriptor(data.inCocMaxMin);
auto const& outputDesc = resources.getDescriptor(data.outTilesCocMaxMin);
auto const& inputDesc = resources.getDescriptor(data.inCocMinMax);
auto const& outputDesc = resources.getDescriptor(data.outTilesCocMinMax);
auto const& out = resources.getRenderPassInfo();
auto inCocMaxMin = resources.getTexture(data.inCocMaxMin);
auto inCocMinMax = resources.getTexture(data.inCocMinMax);
auto const& material = getPostProcessMaterial("dofTiles");
FMaterialInstance* const mi = material.getMaterialInstance();
mi->setParameter("cocMaxMin", inCocMaxMin, { .filterMin = SamplerMinFilter::NEAREST });
mi->setParameter("cocMinMax", inCocMinMax, { .filterMin = SamplerMinFilter::NEAREST });
mi->setParameter("uvscale", float4{
outputDesc.width, outputDesc.height,
1.0f / inputDesc.width, 1.0f / inputDesc.height });
commitAndRender(out, material, driver);
});
inTilesCocMaxMin = ppDoFTiling->outTilesCocMaxMin;
inTilesCocMinMax = ppDoFTiling->outTilesCocMinMax;
}
/*
@@ -1109,33 +1102,33 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
auto dilate = [&](FrameGraphId<FrameGraphTexture> input) -> FrameGraphId<FrameGraphTexture> {
struct PostProcessDofDilate {
FrameGraphId<FrameGraphTexture> inTilesCocMaxMin;
FrameGraphId<FrameGraphTexture> outTilesCocMaxMin;
FrameGraphId<FrameGraphTexture> inTilesCocMinMax;
FrameGraphId<FrameGraphTexture> outTilesCocMinMax;
};
auto& ppDoFDilate = fg.addPass<PostProcessDofDilate>("DoF Dilate",
[&](FrameGraph::Builder& builder, auto& data) {
auto const& inputDesc = fg.getDescriptor(input);
data.inTilesCocMaxMin = builder.sample(input);
data.outTilesCocMaxMin = builder.createTexture("dof dilated tiles output", inputDesc);
data.outTilesCocMaxMin = builder.declareRenderPass(data.outTilesCocMaxMin );
data.inTilesCocMinMax = builder.sample(input);
data.outTilesCocMinMax = builder.createTexture("dof dilated tiles output", inputDesc);
data.outTilesCocMinMax = builder.declareRenderPass(data.outTilesCocMinMax );
},
[=](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
auto const& out = resources.getRenderPassInfo();
auto inTilesCocMaxMin = resources.getTexture(data.inTilesCocMaxMin);
auto inTilesCocMinMax = resources.getTexture(data.inTilesCocMinMax);
auto const& material = getPostProcessMaterial("dofDilate");
FMaterialInstance* const mi = material.getMaterialInstance();
mi->setParameter("tiles", inTilesCocMaxMin, { .filterMin = SamplerMinFilter::NEAREST });
mi->setParameter("tiles", inTilesCocMinMax, { .filterMin = SamplerMinFilter::NEAREST });
commitAndRender(out, material, driver);
});
return ppDoFDilate->outTilesCocMaxMin;
return ppDoFDilate->outTilesCocMinMax;
};
// Tiles of 16 full-resolution pixels requires two dilate rounds to accommodate our max Coc of 32 pixels
// (note: when running at half-res, the tiles are 8 half-resolution pixels, and still need two
// dilate rounds to accommodate the mac CoC pf 16 half-resolution pixels)
auto dilated = dilate(inTilesCocMaxMin);
auto dilated = dilate(inTilesCocMinMax);
dilated = dilate(dilated);
/*
@@ -1143,64 +1136,59 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
*/
struct PostProcessDof {
FrameGraphId<FrameGraphTexture> foreground;
FrameGraphId<FrameGraphTexture> background;
FrameGraphId<FrameGraphTexture> cocFgBg;
FrameGraphId<FrameGraphTexture> tilesCocMaxMin;
FrameGraphId<FrameGraphTexture> outForeground;
FrameGraphId<FrameGraphTexture> color;
FrameGraphId<FrameGraphTexture> coc;
FrameGraphId<FrameGraphTexture> tilesCocMinMax;
FrameGraphId<FrameGraphTexture> outColor;
FrameGraphId<FrameGraphTexture> outAlpha;
};
auto& ppDoF = fg.addPass<PostProcessDof>("DoF",
[&](FrameGraph::Builder& builder, auto& data) {
data.foreground = builder.sample(ppDoFMipmap->inOutForeground);
data.background = builder.sample(ppDoFMipmap->inOutBackground);
data.cocFgBg = builder.sample(ppDoFMipmap->inOutCocFgBg);
data.tilesCocMaxMin = builder.sample(dilated);
data.color = builder.sample(ppDoFMipmap->inOutColor);
data.coc = builder.sample(ppDoFMipmap->inOutCoc);
data.tilesCocMinMax = builder.sample(dilated);
// The DoF buffer (output) doesn't need to be a multiple of 8 because it's not
// mipmapped. We just need to adjust the uv properly.
data.outForeground = builder.createTexture("dof color output", {
data.outColor = builder.createTexture("dof color output", {
.width = (colorDesc.width + (dofResolution / 2u)) / dofResolution,
.height = (colorDesc.height + (dofResolution / 2u)) / dofResolution,
.format = fg.getDescriptor(data.foreground).format
.format = fg.getDescriptor(data.color).format
});
data.outAlpha = builder.createTexture("dof alpha output", {
.width = builder.getDescriptor(data.outForeground).width,
.height = builder.getDescriptor(data.outForeground).height,
.width = builder.getDescriptor(data.outColor).width,
.height = builder.getDescriptor(data.outColor).height,
.format = TextureFormat::R8
});
data.outForeground = builder.write(data.outForeground, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
data.outAlpha = builder.write(data.outAlpha, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
data.outColor = builder.write(data.outColor, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
data.outAlpha = builder.write(data.outAlpha, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
builder.declareRenderPass("DoF Target", {
.attachments = { .color = { data.outForeground, data.outAlpha }}
.attachments = { .color = { data.outColor, data.outAlpha }}
});
},
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
auto const& out = resources.getRenderPassInfo();
auto foreground = resources.getTexture(data.foreground);
auto background = resources.getTexture(data.background);
auto cocFgBg = resources.getTexture(data.cocFgBg);
auto tilesCocMaxMin = resources.getTexture(data.tilesCocMaxMin);
auto color = resources.getTexture(data.color);
auto coc = resources.getTexture(data.coc);
auto tilesCocMinMax = resources.getTexture(data.tilesCocMinMax);
auto const& inputDesc = resources.getDescriptor(data.cocFgBg);
auto const& outputDesc = resources.getDescriptor(data.outForeground);
auto const& tilesDesc = resources.getDescriptor(data.tilesCocMaxMin);
auto const& inputDesc = resources.getDescriptor(data.coc);
auto const& outputDesc = resources.getDescriptor(data.outColor);
auto const& tilesDesc = resources.getDescriptor(data.tilesCocMinMax);
auto const& material = getPostProcessMaterial("dof");
FMaterialInstance* const mi = material.getMaterialInstance();
// it's not safe to use bilinear filtering in the general case (causes artifacts around edges)
mi->setParameter("foreground", foreground,
mi->setParameter("color", color,
{ .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
mi->setParameter("foregroundLinear", foreground,
mi->setParameter("colorLinear", color,
{ .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST });
mi->setParameter("background", background,
mi->setParameter("coc", coc,
{ .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
mi->setParameter("cocFgBg", cocFgBg,
{ .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
mi->setParameter("tiles", tilesCocMaxMin,
mi->setParameter("tiles", tilesCocMinMax,
{ .filterMin = SamplerMinFilter::NEAREST });
mi->setParameter("cocToTexelScale", (1.0f / dofResolution) / float2{ inputDesc.width, inputDesc.height });
mi->setParameter("cocToPixelScale", (1.0f / dofResolution));
@@ -1220,43 +1208,43 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
struct PostProcessDofMedian {
FrameGraphId<FrameGraphTexture> inForeground;
FrameGraphId<FrameGraphTexture> inColor;
FrameGraphId<FrameGraphTexture> inAlpha;
FrameGraphId<FrameGraphTexture> tilesCocMaxMin;
FrameGraphId<FrameGraphTexture> outForeground;
FrameGraphId<FrameGraphTexture> tilesCocMinMax;
FrameGraphId<FrameGraphTexture> outColor;
FrameGraphId<FrameGraphTexture> outAlpha;
};
auto& ppDoFMedian = fg.addPass<PostProcessDofMedian>("DoF Median",
[&](FrameGraph::Builder& builder, auto& data) {
data.inForeground = builder.sample(ppDoF->outForeground);
data.inColor = builder.sample(ppDoF->outColor);
data.inAlpha = builder.sample(ppDoF->outAlpha);
data.tilesCocMaxMin = builder.sample(dilated);
data.tilesCocMinMax = builder.sample(dilated);
data.outForeground = builder.createTexture("dof color output", fg.getDescriptor(data.inForeground));
data.outAlpha = builder.createTexture("dof alpha output", fg.getDescriptor(data.inAlpha));
data.outForeground = builder.write(data.outForeground, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
data.outAlpha = builder.write(data.outAlpha, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
data.outColor = builder.createTexture("dof color output", fg.getDescriptor(data.inColor));
data.outAlpha = builder.createTexture("dof alpha output", fg.getDescriptor(data.inAlpha));
data.outColor = builder.write(data.outColor, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
data.outAlpha = builder.write(data.outAlpha, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
builder.declareRenderPass("DoF Target", {
.attachments = { .color = { data.outForeground, data.outAlpha }}
.attachments = { .color = { data.outColor, data.outAlpha }}
});
},
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
auto const& out = resources.getRenderPassInfo();
auto inForeground = resources.getTexture(data.inForeground);
auto inColor = resources.getTexture(data.inColor);
auto inAlpha = resources.getTexture(data.inAlpha);
auto tilesCocMaxMin = resources.getTexture(data.tilesCocMaxMin);
auto tilesCocMinMax = resources.getTexture(data.tilesCocMinMax);
auto const& outputDesc = resources.getDescriptor(data.outForeground);
auto const& tilesDesc = resources.getDescriptor(data.tilesCocMaxMin);
auto const& outputDesc = resources.getDescriptor(data.outColor);
auto const& tilesDesc = resources.getDescriptor(data.tilesCocMinMax);
auto const& material = getPostProcessMaterial("dofMedian");
FMaterialInstance* const mi = material.getMaterialInstance();
mi->setParameter("dof", inForeground, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
mi->setParameter("dof", inColor, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
mi->setParameter("alpha", inAlpha, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
mi->setParameter("tiles", tilesCocMaxMin, { .filterMin = SamplerMinFilter::NEAREST });
mi->setParameter("tiles", tilesCocMinMax, { .filterMin = SamplerMinFilter::NEAREST });
mi->setParameter("uvscale", float2{
outputDesc.width / float(tileSize / dofResolution * tilesDesc.width),
outputDesc.height / float(tileSize / dofResolution * tilesDesc.height)
@@ -1269,10 +1257,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
* DoF recombine
*/
auto outForeground = ppDoFMedian->outForeground;
auto outColor = ppDoFMedian->outColor;
auto outAlpha = ppDoFMedian->outAlpha;
if (dofOptions.filter == View::DepthOfFieldOptions::Filter::NONE) {
outForeground = ppDoF->outForeground;
outColor = ppDoF->outColor;
outAlpha = ppDoF->outAlpha;
}
@@ -1280,16 +1268,16 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> color;
FrameGraphId<FrameGraphTexture> dof;
FrameGraphId<FrameGraphTexture> alpha;
FrameGraphId<FrameGraphTexture> tilesCocMaxMin;
FrameGraphId<FrameGraphTexture> tilesCocMinMax;
FrameGraphId<FrameGraphTexture> output;
};
auto& ppDoFCombine = fg.addPass<PostProcessDofCombine>("DoF combine",
[&](FrameGraph::Builder& builder, auto& data) {
data.color = builder.sample(input);
data.dof = builder.sample(outForeground);
data.dof = builder.sample(outColor);
data.alpha = builder.sample(outAlpha);
data.tilesCocMaxMin = builder.sample(dilated);
data.tilesCocMinMax = builder.sample(dilated);
auto const& inputDesc = fg.getDescriptor(data.color);
data.output = builder.createTexture("DoF output", inputDesc);
data.output = builder.declareRenderPass(data.output);
@@ -1297,20 +1285,20 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
[=](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
auto const& dofDesc = resources.getDescriptor(data.dof);
auto const& tilesDesc = resources.getDescriptor(data.tilesCocMaxMin);
auto const& tilesDesc = resources.getDescriptor(data.tilesCocMinMax);
auto const& out = resources.getRenderPassInfo();
auto color = resources.getTexture(data.color);
auto dof = resources.getTexture(data.dof);
auto alpha = resources.getTexture(data.alpha);
auto tilesCocMaxMin = resources.getTexture(data.tilesCocMaxMin);
auto tilesCocMinMax = resources.getTexture(data.tilesCocMinMax);
auto const& material = getPostProcessMaterial("dofCombine");
FMaterialInstance* const mi = material.getMaterialInstance();
mi->setParameter("color", color, { .filterMin = SamplerMinFilter::NEAREST });
mi->setParameter("dof", dof, { .filterMag = SamplerMagFilter::NEAREST });
mi->setParameter("alpha", alpha, { .filterMag = SamplerMagFilter::NEAREST });
mi->setParameter("tiles", tilesCocMaxMin, { .filterMin = SamplerMinFilter::NEAREST });
mi->setParameter("tiles", tilesCocMinMax, { .filterMin = SamplerMinFilter::NEAREST });
mi->setParameter("uvscale", float4{
colorDesc.width / (dofDesc.width * float(dofResolution)),
colorDesc.height / (dofDesc.height * float(dofResolution)),

View File

@@ -445,13 +445,13 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
});
// resolve depth -- which might be needed because of TAA or DoF. This pass will be culled
// if the depth is not use below.
// if the depth is not used below.
auto& blackboard = fg.getBlackboard();
auto depth = blackboard.get<FrameGraphTexture>("depth");
depth = ppm.resolve(fg, "Resolved Depth Buffer", depth);
blackboard.put("depth", depth);
// TODO: DoF should be applied here, before TAA -- but if we do this it'll result is a lot
// TODO: DoF should be applied here, before TAA -- but if we do this it'll result in a lot of
// fireflies due to the instability of the highlights. This can be fixed with a
// dedicated TAA pass for the DoF, as explained in
// "Life of a Bokeh" by Guillaume Abadie, SIGGRAPH 2018

View File

@@ -3,22 +3,17 @@ material {
parameters : [
{
type : sampler2d,
name : foreground,
name : color,
precision: medium
},
{
type : sampler2d,
name : foregroundLinear,
name : colorLinear,
precision: medium
},
{
type : sampler2d,
name : background,
precision: medium
},
{
type : sampler2d,
name : cocFgBg,
name : coc,
precision: medium
},
{
@@ -207,8 +202,9 @@ struct Bucket {
precision mediump float;
struct Sample {
vec4 s; // sample color
float coc; // sample coc
vec4 s; // sample color
float coc; // sample coc
float inLayer; // whether is sample is in the considered layer
};
void initBucket(out Bucket ring) {
@@ -268,7 +264,7 @@ void mergeRings(inout Bucket curr, inout Bucket prev, const float count) {
void accumulate(inout Bucket curr, inout Bucket prev, const Sample tap,
const float radius, const float border, const float mip, const bool first) {
float inLayer = isBackground(tap.coc);
float inLayer = tap.inLayer;
float coc = abs(tap.coc);
float w = inLayer * intersection(radius, coc, mip) * sampleWeight(coc, mip);
@@ -303,11 +299,9 @@ void accumulate(inout Bucket curr, inout Bucket prev, const Sample tap,
void accumulateBackground(inout Bucket curr, inout Bucket prev, const highp vec2 pos,
const float radius, const float border, const float mip, const bool first) {
Sample tap;
// FIXME: there are typos below. We should use materialParams_background and
// materialParams_cocFgBg's green (background coc) channel.
// however doing so, looks much worse and is much more unstable.
tap.s = textureLod(materialParams_foreground, pos, mip);
tap.coc = textureLod(materialParams_cocFgBg, pos, mip).r;
tap.s = textureLod(materialParams_color, pos, mip);
tap.coc = textureLod(materialParams_coc, pos, mip).r;
tap.inLayer = isBackground(tap.coc);
accumulate(curr, prev, tap, radius, border, mip, first);
}
@@ -387,8 +381,8 @@ float foregroundFadding(const float coc) {
void accumulateForeground(inout Layer layer[2], const vec2 tiles,
const highp vec2 pos, const float border, const float mip) {
float coc = textureLod(materialParams_cocFgBg, pos, mip).r;
vec4 s = textureLod(materialParams_foreground, pos, mip);
float coc = textureLod(materialParams_coc, pos, mip).r;
vec4 s = textureLod(materialParams_color, pos, mip);
float inLayer = isForeground(coc) * foregroundFadding(coc);
float i = intersection(border, abs(coc), mip) * sampleWeight(coc, mip);
@@ -417,12 +411,12 @@ void accumulateForegroundMirror(inout Layer layer[2], const vec2 tiles,
// the geometry.
highp vec2 pos0 = diaphragm(center, offset);
float coc0 = textureLod(materialParams_cocFgBg, pos0, mip).r;
vec4 s0 = textureLod(materialParams_foreground, pos0, mip);
float coc0 = textureLod(materialParams_coc, pos0, mip).r;
vec4 s0 = textureLod(materialParams_color, pos0, mip);
highp vec2 pos1 = diaphragm(center, -offset);
float coc1 = textureLod(materialParams_cocFgBg, pos1, mip).r;
vec4 s1 = textureLod(materialParams_foreground, pos1, mip);
float coc1 = textureLod(materialParams_coc, pos1, mip).r;
vec4 s1 = textureLod(materialParams_color, pos1, mip);
float coc = min(coc0, coc1);
float inLayer = isForeground(coc) * foregroundFadding(coc);
@@ -460,7 +454,7 @@ void fastTile(inout vec4 color, inout float alpha,
highp vec2 uvCenter = uv + noise * cocToTexelScale;
color = textureLod(materialParams_foregroundLinear, uvCenter, mip);
color = textureLod(materialParams_colorLinear, uvCenter, mip);
for (float i = 1.0; i < ringCountFast; i += 1.0) {
float radius;
float count;
@@ -470,9 +464,9 @@ void fastTile(inout vec4 color, inout float alpha,
// we must use this temporary in order to accomodate mediump precision
vec4 t = vec4(0.0);
for (float j = 0.0; j < count; j += 2.0) {
t += textureLod(materialParams_foregroundLinear,
t += textureLod(materialParams_colorLinear,
diaphragm(uvCenter, p * cocToTexelScale), mip)
+ textureLod(materialParams_foregroundLinear,
+ textureLod(materialParams_colorLinear,
diaphragm(uvCenter, -p * cocToTexelScale), mip);
p = r * p;
}
@@ -545,7 +539,7 @@ void backgroundTile(inout vec4 background, inout float bgOpacity,
// we select a ring cound per tile, to avoid divergence between pixels
float ringCountGather = min(float(RING_COUNT_GATHER), computeNeededRings(tiles.g * cocToPixelScale));
float centerCoc = textureLod(materialParams_cocFgBg, uv, 0.0).g;
float centerCoc = textureLod(materialParams_coc, uv, 0.0).r;
float kernelSize = abs(centerCoc);
float mip = getMipLevel(ringCountGather, kernelSize * cocToPixelScale);
float noiseRadius = 0.0;
@@ -650,7 +644,7 @@ void postProcess(inout PostProcessInputs postProcess) {
// TODO: make this a quality setting
if (fgOpacity > 0.0 && fgOpacity < 1.0 && bgOpacity == 0.0) {
// this happens on very few pixels
float centerCoc = textureLod(materialParams_cocFgBg, uv, 0.0).r;
float centerCoc = textureLod(materialParams_coc, uv, 0.0).r;
float correctedOpacity = max(fgOpacity, cocToAlpha(centerCoc));
float updateOpacity = correctedOpacity / fgOpacity;
foreground *= updateOpacity;

View File

@@ -31,14 +31,9 @@ material {
type : float4
},
{
name : background,
name : coc,
target : color,
type : float4
},
{
name : cocFgBg,
target : color,
type : float2
type : float
}
],
domain : postprocess,
@@ -64,26 +59,30 @@ void dummy(){}
void postProcess(inout PostProcessInputs postProcess) {
highp vec2 uv = variable_vertex.xy;
vec4 color = textureLod(materialParams_color, uv, 0.0);
vec4 outColor = textureLod(materialParams_color, uv, 0.0);
float d = textureLod(materialParams_depth, uv, 0.0).r;
// Get the CoC radius.
// We multiply by 0.5 to convert from diameter to radius.
float coc = getCOC(d, materialParams.cocParams) * 0.5;
float outCoc = getCOC(d, materialParams.cocParams) * 0.5;
// Clamp to maximum allowable CoC radius.
outCoc = clamp(outCoc, -MAX_COC_RADIUS, MAX_COC_RADIUS);
// If CoC is less that 0.5 full resolution pixel, we clamp to 0, this will reduce
// artifacts in the "in focus" area and allow us to skip more tiles trivially.
if (abs(coc) < MAX_IN_FOCUS_COC) {
coc = 0.0;
if (abs(outCoc) < MAX_IN_FOCUS_COC) {
outCoc = 0.0;
}
// Clamp to maximum allowable CoC radius.
coc = clamp(coc, -MAX_COC_RADIUS, MAX_COC_RADIUS);
#if POST_PROCESS_OPAQUE
// kill the work performed above
outColor.a = 1.0;
#endif
// Output to MRTs
postProcess.color = color;
postProcess.background = color;
postProcess.cocFgBg = vec2(coc, coc);
postProcess.color = outColor;
postProcess.coc = outCoc;
}
}

View File

@@ -64,9 +64,9 @@ void postProcess(inout PostProcessInputs postProcess) {
highp vec2 uv0 = variable_vertex.xy;
highp vec2 uv1 = variable_vertex.xy * materialParams.uvscale.xy;
float alpha = textureLod(materialParams_alpha, uv1, 0.0).r;
vec4 foreground = textureLod(materialParams_dof, uv1, 0.0);
vec4 dof = textureLod(materialParams_dof, uv1, 0.0);
color = textureLod(materialParams_color, uv0, 0.0);
color = foreground + (1.0 - alpha) * color;
color = dof + (1.0 - alpha) * color;
}
#if POST_PROCESS_OPAQUE

View File

@@ -35,20 +35,8 @@ vec2 tap(const vec2 uv, const vec2 offset) {
}
vec2 dilate(inout vec2 center, const vec2 tap) {
// 2.24 tiles is the longest distance between 2 pixels of adjacent tiles.
// sqrt(2^2 + 1^2) = 2.24
const float maxDistance = max(-MAX_COC_RADIUS, -2.24 * TILE_SIZE);
// Tiles that can affect us need to transfer their CoC to us (for the foreground it's
// min(ourCoc, theirCoc) because CoC are negative (the min is the larger radius).
// TODO: However, if their CoC is very large, we can limit it by the distance to that
// tile, because we only need to reach *them*).
// The difficuty here is the this clamping depends on if we're negative or positive.
// e.g. for positive values
// center.g = min(center.g, max(tap.g, maxDistance));
// center.r = max(center.r, max(tap.r, maxDistance));
center.r = min(center.r, tap.r);
center.g = max(center.g, tap.g);
return center;

View File

@@ -31,14 +31,9 @@ material {
type : float4
},
{
name : background,
name : coc,
target : color,
type : float4
},
{
name : cocFgBg,
target : color,
type : float2
type : float
}
],
domain : postprocess,
@@ -73,63 +68,45 @@ void postProcess(inout PostProcessInputs postProcess) {
vec4 hdr;
// fireflies/flickering filtering
hdr.x = 1.0 / (1.0 + max3(s00.rgb));
hdr.y = 1.0 / (1.0 + max3(s11.rgb));
hdr.z = 1.0 / (1.0 + max3(s10.rgb));
hdr.w = 1.0 / (1.0 + max3(s00.rgb));
hdr[0] = 1.0 / (1.0 + max3(s01.rgb));
hdr[1] = 1.0 / (1.0 + max3(s11.rgb));
hdr[2] = 1.0 / (1.0 + max3(s10.rgb));
hdr[3] = 1.0 / (1.0 + max3(s00.rgb));
vec4 d;
#if defined(TARGET_VULKAN_ENVIRONMENT) || !defined(TARGET_MOBILE)
d = textureGather(materialParams_depth, uv, 0); // 01, 11, 10, 00
#else
d.x = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(0, 1)).r;
d.y = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(1, 1)).r;
d.z = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(1, 0)).r;
d.w = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(0, 0)).r;
d[0] = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(0, 1)).r;
d[1] = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(1, 1)).r;
d[2] = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(1, 0)).r;
d[3] = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(0, 0)).r;
#endif
// Get the CoC radius of each four samples to downsample.
// We multiply by 0.5 to convert from diameter to radius.
vec4 w = getCOC(d, materialParams.cocParams) * 0.5;
vec4 c = getCOC(d, materialParams.cocParams) * 0.5;
float outCoc = downsampleCoC(c);
vec4 w = downsampleCocWeights(c, outCoc, 1.0) * hdr;
vec4 outColor = (s01 * w[0] + s11 * w[1] + s10 * w[2] + s00 * w[3]) * rcp(dot(vec4(1.0), w));
// We calculates the downsampled CoC to be conservative, that is if any
// of the 4 texels are foreground or background, then the whole new texel is too.
float fgCoc = min2(min(w.xy, w.zw));
// The forground bilateral weight is calculated as saturate(1.0 - (fgCoc - w)), which always
// yields 1.0. Note the "missing" abs(), this is because we want to let the background layer
// leak into the foreground layer, to avoid aliasing artifacts.
vec4 foreground = (s01 * hdr.x + s11 * hdr.y + s10 * hdr.z + s00 * hdr.w) * (1.0 / (hdr.x + hdr.y + hdr.z + hdr.w));
// the background bilateral weight is calculated as saturate(1.0 - abs(bgCoc - w)), but the
// abs() is not needed since bgCoc - w is guaranteed to be >= 0.
float bgCoc = max2(max(w.xy, w.zw));
vec4 bw = saturate(1.0 - (bgCoc - w)) * hdr;
vec4 background = (s01 * bw.x + s11 * bw.y + s10 * bw.z + s00 * bw.w) * (1.0 / (bw.x + bw.y + bw.z + bw.w));
// Clamp to maximum allowable CoC radius.
outCoc = clamp(outCoc, -MAX_COC_RADIUS, MAX_COC_RADIUS);
// If CoC is less that 0.5 full resolution pixel, we clamp to 0, this will reduce
// artifacts in the "in focus" area and allow us to skip more tiles trivially.
if (abs(fgCoc) < MAX_IN_FOCUS_COC) {
fgCoc = 0.0;
if (abs(outCoc) < MAX_IN_FOCUS_COC) {
outCoc = 0.0;
}
if (abs(bgCoc) < MAX_IN_FOCUS_COC){
bgCoc = 0.0;
}
// Clamp to maximum allowable CoC radius.
fgCoc = clamp(fgCoc, -MAX_COC_RADIUS, MAX_COC_RADIUS);
bgCoc = clamp(bgCoc, -MAX_COC_RADIUS, MAX_COC_RADIUS);
#if POST_PROCESS_OPAQUE
// kill the work performed above
foreground.a = 1.0;
background.a = 1.0;
outColor.a = 1.0;
#endif
// Output to MRTs
postProcess.color = foreground;
postProcess.background = background;
postProcess.cocFgBg = vec2(fgCoc, bgCoc);
postProcess.color = outColor;
postProcess.coc = outCoc;
}
}

View File

@@ -3,17 +3,12 @@ material {
parameters : [
{
type : sampler2d,
name : foreground,
name : color,
precision: medium
},
{
type : sampler2d,
name : background,
precision: medium
},
{
type : sampler2d,
name : cocFgBg,
name : coc,
precision: medium
},
{
@@ -35,14 +30,9 @@ material {
type : float4
},
{
name : background,
name : coc,
target : color,
type : float4
},
{
name : cocFgBg,
target : color,
type : float2
type : float
}
],
domain : postprocess,
@@ -63,7 +53,7 @@ fragment {
void dummy(){}
void postProcess(inout PostProcessInputs postProcess) {
highp vec2 pixelSize = 1.0 / vec2(textureSize(materialParams_foreground, materialParams.mip));
highp vec2 pixelSize = 1.0 / vec2(textureSize(materialParams_color, materialParams.mip));
highp vec2 uv = variable_vertex.xy - pixelSize.xy * 0.5;
// the source is guaranteed to be a multiple of two, so we now the bilinear weights are 0.25
@@ -72,60 +62,30 @@ void postProcess(inout PostProcessInputs postProcess) {
float weightScale = materialParams.weightScale;
float mip = float(materialParams.mip);
// fetch the 4 corresponding CoC
vec4 d0, d1;
d0.xy = textureLodOffset(materialParams_cocFgBg, uv, mip, ivec2(0, 1)).rg;
d0.zw = textureLodOffset(materialParams_cocFgBg, uv, mip, ivec2(1, 1)).rg;
d1.xy = textureLodOffset(materialParams_cocFgBg, uv, mip, ivec2(1, 0)).rg;
d1.zw = textureLodOffset(materialParams_cocFgBg, uv, mip, ivec2(0, 0)).rg;
vec4 fw = vec4(d0.xz, d1.xz);
vec4 bw = vec4(d0.yw, d1.yw);
vec4 s01 = textureLodOffset(materialParams_color, uv, mip, ivec2(0, 1));
vec4 s11 = textureLodOffset(materialParams_color, uv, mip, ivec2(1, 1));
vec4 s10 = textureLodOffset(materialParams_color, uv, mip, ivec2(1, 0));
vec4 s00 = textureLodOffset(materialParams_color, uv, mip, ivec2(0, 0));
/*
* foreground
*/
// fetch the 4 corresponding CoC (textureGather with LOD doesn't exist)
vec4 c;
c[0] = textureLodOffset(materialParams_coc, uv, mip, ivec2(0, 1)).r;
c[1] = textureLodOffset(materialParams_coc, uv, mip, ivec2(1, 1)).r;
c[2] = textureLodOffset(materialParams_coc, uv, mip, ivec2(1, 0)).r;
c[3] = textureLodOffset(materialParams_coc, uv, mip, ivec2(0, 0)).r;
vec4 fg01 = textureLodOffset(materialParams_foreground, uv, mip, ivec2(0, 1));
vec4 fg11 = textureLodOffset(materialParams_foreground, uv, mip, ivec2(1, 1));
vec4 fg10 = textureLodOffset(materialParams_foreground, uv, mip, ivec2(1, 0));
vec4 fg00 = textureLodOffset(materialParams_foreground, uv, mip, ivec2(0, 0));
// We calculates the downsampled CoC to be conservative, that is if any
// of the 4 texels are foreground or background, then the whole new texel is too.
float fgCoc = min4(fw);
// The forground bilateral weight is calculated as saturate(1.0 - (fgCoc - w)), which always
// yields 1.0. Note the "missing" abs(), this is because we want to let the background layer
// leak into the foreground layer, to avoid aliasing artifacts.
vec4 foreground = (fg01 + fg11 + fg10 + fg00) * 0.25;
/*
* background
*/
vec4 bg01 = textureLodOffset(materialParams_background, uv, mip, ivec2(0, 1));
vec4 bg11 = textureLodOffset(materialParams_background, uv, mip, ivec2(1, 1));
vec4 bg10 = textureLodOffset(materialParams_background, uv, mip, ivec2(1, 0));
vec4 bg00 = textureLodOffset(materialParams_background, uv, mip, ivec2(0, 0));
float bgCoc = max4(bw);
// The background bilateral weight is calculated as saturate(1.0 - abs(bgCoc - w)),
// but the abs() is not needed since bgCoc - w is guaranteed to be >= 0.
bw = saturate(1.0 - (bgCoc - bw) * weightScale);
float bgScale = 1.0 / (bw.x + bw.y + bw.z + bw.w);
vec4 background = (bg01 * bw.x + bg11 * bw.y + bg10 * bw.z + bg00 * bw.w) * bgScale;
float outCoc = downsampleCoC(c);
vec4 w = downsampleCocWeights(c, outCoc, weightScale);
vec4 outColor = (s01 * w[0] + s11 * w[1] + s10 * w[2] + s00 * w[3]) * rcp(dot(vec4(1.0), w));
#if POST_PROCESS_OPAQUE
// kill the work performed above
foreground.a = 1.0;
background.a = 1.0;
outColor.a = 1.0;
#endif
// Output to MRTs
postProcess.color = foreground;
postProcess.background = background;
postProcess.cocFgBg = vec2(fgCoc, bgCoc);
postProcess.color = outColor;
postProcess.coc = outCoc;
}
}

View File

@@ -3,7 +3,7 @@ material {
parameters : [
{
type : sampler2d,
name : cocMaxMin,
name : cocMinMax,
precision: medium
},
{
@@ -42,27 +42,28 @@ float min4(const vec4 f) {
void postProcess(inout PostProcessInputs postProcess) {
highp vec2 uv = variable_vertex.xy;
highp vec2 size = vec2(textureSize(materialParams_cocMaxMin, 0));
highp vec2 size = vec2(textureSize(materialParams_cocMinMax, 0));
ivec2 i = ivec2(uv * size - 0.5);
// note: the first time, the source texture is swizzled -- so .r and .g are the same channel
#if defined(TARGET_VULKAN_ENVIRONMENT) || !defined(TARGET_MOBILE)
vec4 f = textureGather(materialParams_cocMaxMin, uv, 0); // 01, 11, 10, 00
vec4 b = textureGather(materialParams_cocMaxMin, uv, 1); // 01, 11, 10, 00
vec4 mi = textureGather(materialParams_cocMinMax, uv, 0); // 01, 11, 10, 00
vec4 ma = textureGather(materialParams_cocMinMax, uv, 1); // 01, 11, 10, 00
#else
vec2 s01 = texelFetchOffset(materialParams_cocMaxMin, i, 0, ivec2(0, 1)).rg;
vec2 s11 = texelFetchOffset(materialParams_cocMaxMin, i, 0, ivec2(1, 1)).rg;
vec2 s10 = texelFetchOffset(materialParams_cocMaxMin, i, 0, ivec2(1, 0)).rg;
vec2 s00 = texelFetchOffset(materialParams_cocMaxMin, i, 0, ivec2(0, 0)).rg;
vec4 f = vec4(s01.r, s11.r, s10.r, s00.r);
vec4 b = vec4(s01.g, s11.g, s10.g, s00.g);
vec2 s01 = texelFetchOffset(materialParams_cocMinMax, i, 0, ivec2(0, 1)).rg;
vec2 s11 = texelFetchOffset(materialParams_cocMinMax, i, 0, ivec2(1, 1)).rg;
vec2 s10 = texelFetchOffset(materialParams_cocMinMax, i, 0, ivec2(1, 0)).rg;
vec2 s00 = texelFetchOffset(materialParams_cocMinMax, i, 0, ivec2(0, 0)).rg;
vec4 mi = vec4(s01.r, s11.r, s10.r, s00.r);
vec4 ma = vec4(s01.g, s11.g, s10.g, s00.g);
#endif
// compute tile's min CoC
postProcess.color.r = min4(min(f, b));
postProcess.color.r = min4(mi);
// compute tile's max CoC
postProcess.color.g = max4(max(f, b));
postProcess.color.g = max4(ma);
}
}

View File

@@ -96,3 +96,23 @@ bool isTrivialTile(const vec2 tiles) {
float maxCocRadius = max(abs(tiles.r), abs(tiles.g));
return maxCocRadius < MAX_IN_FOCUS_COC;
}
float downsampleCoC(const vec4 c) {
// We need to compute a suitable CoC to represent the 4 pixels that are downsampled.
// We pick the min because this always selects the most foreground sample if there is one,
// because the foreground can leak on the background, but not the reverse.
return min4(c);
}
vec4 downsampleCocWeights(const vec4 c, const float outCoc, const float scale) {
// The bilateral weight is normally computed as saturate(1 - |outCoc - c|) which selects
// the sample with the outCoc weight (and does a little bit of cross-fade if other samples
// are close). However, this can also cause some aliasing with dithered objects, so by
// omitting the abs() we allow samples in the background to "leak" into the foreground, which
// is not completely different from the "mirror hole filling" of the DoF pass.
// See "Life of a Bokeh" by Guillaume Abadie, SIGGRAPH 2018.
// Note: With the implementation of downsampleCoC() above, this always resolves to 1.0;
// return saturate(1.0 - (outCoc - c) * scale);
return vec4(1.0);
}