Skip to content

Commit 7a0a356

Browse files
committed
Feature: Track auto-exposure progress on the CPU so we can determine how long to keep redrawing an on-demand view for
1 parent 41368de commit 7a0a356

File tree

6 files changed

+157
-32
lines changed

6 files changed

+157
-32
lines changed

Source/Plugins/bsfRenderBeast/BsRenderBeast.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,8 @@ namespace bs { namespace ct
486486
for (UINT32 i = 0; i < numViews; i++)
487487
{
488488
RendererView* view = viewGroup.getView(i);
489+
view->updateAsyncOperations();
490+
489491
auto viewId = (UINT64)view;
490492
const RendererViewTargetData& viewTarget = view->getProperties().target;
491493
String title = StringUtil::format("({0} x {1})", viewTarget.targetWidth, viewTarget.targetHeight);

Source/Plugins/bsfRenderBeast/BsRenderBeast.h

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,14 @@ namespace bs
2121
* @{
2222
*/
2323

24-
/** Information about current time and frame index. */
25-
struct FrameTimings
26-
{
27-
float time;
28-
float timeDelta;
29-
UINT64 frameIdx;
30-
};
31-
3224
/** Contains information global to an entire frame. */
3325
struct FrameInfo
3426
{
3527
FrameInfo(const FrameTimings& timings, PerFrameData perFrameData)
36-
:timeDelta(timings.timeDelta), frameIdx(timings.frameIdx), perFrameData(perFrameData)
28+
:timings(timings), perFrameData(perFrameData)
3729
{ }
3830

39-
float timeDelta;
40-
UINT64 frameIdx;
31+
FrameTimings timings;
4132
PerFrameData perFrameData;
4233
};
4334

Source/Plugins/bsfRenderBeast/BsRenderBeastPrerequisites.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ namespace bs { namespace ct
5555
Full,
5656
};
5757

58+
/** Information about current time and frame index. */
59+
struct FrameTimings
60+
{
61+
float time = 0.0f;
62+
float timeDelta = 0.0f;
63+
UINT64 frameIdx = 0;
64+
};
65+
5866
/** @} */
5967

6068
struct RenderBeastOptions;

Source/Plugins/bsfRenderBeast/BsRenderCompositor.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,7 @@ namespace bs { namespace ct
603603
gbuffer.depth = sceneDepthNode->depthTex->texture;
604604

605605
GpuParticleSimulation::instance().simulate(inputs.scene, inputs.frameInfo.perFrameData.particles,
606-
inputs.view.getPerViewBuffer(), gbuffer, inputs.frameInfo.timeDelta);
606+
inputs.view.getPerViewBuffer(), gbuffer, inputs.frameInfo.timings.timeDelta);
607607
}
608608

609609
GpuParticleSimulation::instance().sort(inputs.view);
@@ -1742,7 +1742,7 @@ namespace bs { namespace ct
17421742
eyeAdaptationMat->execute(
17431743
reducedHistogram->texture,
17441744
output->renderTexture,
1745-
inputs.frameInfo.timeDelta,
1745+
inputs.frameInfo.timings.timeDelta,
17461746
settings.autoExposure,
17471747
settings.exposureScale);
17481748
}
@@ -1756,7 +1756,7 @@ namespace bs { namespace ct
17561756
setupMat->execute(
17571757
downsampledScene->texture,
17581758
luminanceTex->renderTexture,
1759-
inputs.frameInfo.timeDelta,
1759+
inputs.frameInfo.timings.timeDelta,
17601760
settings.autoExposure,
17611761
settings.exposureScale);
17621762

@@ -1786,10 +1786,16 @@ namespace bs { namespace ct
17861786
downsampleInput,
17871787
prevFrameEyeAdaptation,
17881788
output->renderTexture,
1789-
inputs.frameInfo.timeDelta,
1789+
inputs.frameInfo.timings.timeDelta,
17901790
settings.autoExposure,
17911791
settings.exposureScale);
17921792
}
1793+
1794+
const RendererView& view = inputs.view;
1795+
1796+
// Notify the view eye adaptation value will change
1797+
SPtr<CommandBuffer> cb = RenderAPI::instance().getMainCommandBuffer();
1798+
view._notifyLuminanceUpdated(inputs.frameInfo.timings.frameIdx, cb, output);
17931799
}
17941800
else
17951801
{

Source/Plugins/bsfRenderBeast/BsRendererView.cpp

Lines changed: 81 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "BsRenderBeast.h"
1313
#include "BsRendererDecal.h"
1414
#include "Animation/BsAnimationManager.h"
15+
#include "RenderAPI/BsCommandBuffer.h"
1516

1617
namespace bs { namespace ct
1718
{
@@ -164,8 +165,18 @@ namespace bs { namespace ct
164165

165166
gPerCameraParamDef.gNDCToPrevNDC.set(mParamBuffer, NDCToPrevNDC);
166167

167-
mFrameDelta = frameInfo.timeDelta;
168+
mFrameTimings = frameInfo.timings;
168169
mAsyncAnim = frameInfo.perFrameData.animation ? frameInfo.perFrameData.animation->async : false;
170+
171+
// Account for auto-exposure taking multiple frames
172+
if (mRedrawThisFrame)
173+
{
174+
// Note: Doing this here instead of _notifyNeedsRedraw because we need an up-to-date frame index
175+
if (mRenderSettings->enableHDR && mRenderSettings->enableAutoExposure)
176+
mWaitingOnAutoExposureFrame = mFrameTimings.frameIdx;
177+
else
178+
mWaitingOnAutoExposureFrame = std::numeric_limits<UINT64>::max();
179+
}
169180
}
170181

171182
void RendererView::endFrame()
@@ -186,36 +197,92 @@ namespace bs { namespace ct
186197
mRedrawForFrames--;
187198

188199
if (mRedrawForSeconds > 0.0f)
189-
mRedrawForSeconds -= mFrameDelta;
200+
mRedrawForSeconds -= mFrameTimings.timeDelta;
201+
202+
mRedrawThisFrame = false;
190203
}
191204

192205
void RendererView::_notifyNeedsRedraw()
193206
{
207+
mRedrawThisFrame = true;
208+
194209
// If doing async animation there is a one frame delay
195210
mRedrawForFrames = mAsyncAnim ? 2 : 1;
196211

197-
// Account for auto-exposure taking multiple frames
198-
if (mRenderSettings->enableAutoExposure)
199-
{
200-
AutoExposureSettings& aeSettings = mRenderSettings->autoExposure;
201-
float maxSpeed = std::max(aeSettings.eyeAdaptationSpeedUp, aeSettings.eyeAdaptationSpeedDown);
202-
203-
// TODO - We should get feedback from the renderer on when the exposure stabilises, as this number will
204-
// be too high for a large majority of cases
205-
mRedrawForSeconds = maxSpeed;
206-
}
207-
else
208-
mRedrawForSeconds = 0.0f;
212+
// This will be set once we get the new luminance data from the GPU
213+
mRedrawForSeconds = 0.0f;
209214
}
210215

211216
bool RendererView::shouldDraw() const
212217
{
213218
if (!mProperties.onDemand)
214219
return true;
215220

221+
if(mRenderSettings->enableHDR && mRenderSettings->enableAutoExposure)
222+
{
223+
constexpr float AUTO_EXPOSURE_TOLERANCE = 0.01f;
224+
225+
// The view was redrawn but we still haven't received the eye adaptation results from the GPU, so
226+
// we keep redrawing until we do
227+
if (mWaitingOnAutoExposureFrame != std::numeric_limits<UINT64>::max())
228+
return true;
229+
230+
// Need to render until the auto-exposure reaches the target exposure
231+
float eyeAdaptationDiff = Math::abs(mCurrentEyeAdaptation - mPreviousEyeAdaptation);
232+
if (eyeAdaptationDiff > AUTO_EXPOSURE_TOLERANCE)
233+
return true;
234+
}
235+
216236
return mRedrawForFrames > 0 || mRedrawForSeconds > 0.0f;
217237
}
218238

239+
void RendererView::updateAsyncOperations()
240+
{
241+
// Find most recent available frame
242+
auto lastFinishedIter = mLuminanceUpdates.end();
243+
for(auto iter = mLuminanceUpdates.begin(); iter != mLuminanceUpdates.end(); ++iter)
244+
{
245+
if (iter->commandBuffer->getState() == CommandBufferState::Executing)
246+
break;
247+
248+
lastFinishedIter = iter;
249+
}
250+
251+
if (lastFinishedIter != mLuminanceUpdates.end())
252+
{
253+
// Get new luminance value
254+
mPreviousEyeAdaptation = mCurrentEyeAdaptation;
255+
256+
PixelData data = lastFinishedIter->outputTexture->texture->lock(GBL_READ_ONLY);
257+
mCurrentEyeAdaptation = data.getColorAt(0, 0).r;
258+
lastFinishedIter->outputTexture->texture->unlock();
259+
260+
// We've received information about eye adaptation, use that to determine if redrawing
261+
// is required (technically we're drawing a few frames extra, as this information is always
262+
// a few frames too late).
263+
if (lastFinishedIter->frameIdx == mWaitingOnAutoExposureFrame)
264+
mWaitingOnAutoExposureFrame = std::numeric_limits<UINT64>::max();
265+
266+
mLuminanceUpdates.erase(mLuminanceUpdates.begin(), lastFinishedIter + 1);
267+
}
268+
}
269+
270+
RendererViewRedrawReason RendererView::getRedrawReason() const
271+
{
272+
if (!mProperties.onDemand)
273+
return RendererViewRedrawReason::PerFrame;
274+
275+
if (mRedrawThisFrame)
276+
return RendererViewRedrawReason::OnDemandThisFrame;
277+
278+
return RendererViewRedrawReason::OnDemandLingering;
279+
}
280+
281+
void RendererView::_notifyLuminanceUpdated(UINT64 frameIdx, SPtr<CommandBuffer> cb, SPtr<PooledRenderTexture> texture) const
282+
{
283+
mLuminanceUpdates.emplace_back(frameIdx, cb, texture);
284+
}
285+
219286
void RendererView::determineVisible(const Vector<RendererRenderable*>& renderables, const Vector<CullInfo>& cullInfos,
220287
Vector<bool>* visibility)
221288
{

Source/Plugins/bsfRenderBeast/BsRendererView.h

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
#include "Math/BsConvexVolume.h"
1111
#include "Shading/BsLightGrid.h"
1212
#include "Shading/BsShadowRendering.h"
13-
#include "BsRendererView.h"
1413
#include "BsRendererRenderable.h"
1514
#include "BsRenderCompositor.h"
1615
#include "BsRendererParticles.h"
@@ -221,6 +220,22 @@ namespace bs { namespace ct
221220
Vector<Camera*> cameras;
222221
};
223222

223+
/** Returns the reason why is a RendererView being redrawn. */
224+
enum class RendererViewRedrawReason
225+
{
226+
/** This particular view isn't on-demand and is redrawn every frame. */
227+
PerFrame,
228+
229+
/** Draws on demand and on-demand drawing was triggered this frame. */
230+
OnDemandThisFrame,
231+
232+
/**
233+
* Draws on demand and on-demand drawing was triggered during an earlier frame but a multi-frame effect is
234+
* requiring the view to get redrawn in later frames.
235+
*/
236+
OnDemandLingering
237+
};
238+
224239
/** Contains information about a single view into the scene, used by the renderer. */
225240
class RendererView
226241
{
@@ -275,7 +290,7 @@ namespace bs { namespace ct
275290

276291
/** Returns the compositor in charge of rendering for this view. */
277292
const RenderCompositor& getCompositor() const { return mCompositor; }
278-
293+
279294
/**
280295
* Populates view render queues by determining visible renderable objects.
281296
*
@@ -417,6 +432,18 @@ namespace bs { namespace ct
417432
/** Determines if view's 3D geometry should be rendered this frame. */
418433
bool shouldDraw3D() const { return !mRenderSettings->overlayOnly && shouldDraw(); }
419434

435+
/**
436+
* Determines if any async operations have completed executing and finalizes them. Should be called once
437+
* per frame.
438+
*/
439+
void updateAsyncOperations();
440+
441+
/**
442+
* Returns the reason that explains why is the view getting redrawn this frame. Only relevant if shouldDraw() returned
443+
* true previously during the frame.
444+
*/
445+
RendererViewRedrawReason getRedrawReason() const;
446+
420447
/** Assigns a view index to the view. To be called by the parent view group when the view is added to it. */
421448
void _setViewIdx(UINT32 viewIdx) { mViewIdx = viewIdx; }
422449

@@ -431,6 +458,12 @@ namespace bs { namespace ct
431458
*/
432459
void _notifyCompositorTargetChanged(const SPtr<RenderTarget>& target) const { mContext.currentTarget = target; }
433460

461+
/**
462+
* Notifies the view that a new average luminance is being calculated on the provided command buffer. The results
463+
* will be read from the provided texture when the command buffer finishes executing.
464+
*/
465+
void _notifyLuminanceUpdated(UINT64 frameIdx, SPtr<CommandBuffer> cb, SPtr<PooledRenderTexture> texture) const;
466+
434467
/**
435468
* Extracts the necessary values from the projection matrix that allow you to transform device Z value (range [0, 1]
436469
* into view Z value.
@@ -457,6 +490,17 @@ namespace bs { namespace ct
457490
*/
458491
static Vector2 getNDCZToDeviceZ();
459492
private:
493+
struct LuminanceUpdate
494+
{
495+
LuminanceUpdate(UINT64 frameIdx, SPtr<CommandBuffer> commandBuffer, const SPtr<PooledRenderTexture>& outputTexture)
496+
: frameIdx(frameIdx), commandBuffer(std::move(commandBuffer)), outputTexture(std::move(outputTexture))
497+
{ }
498+
499+
UINT64 frameIdx;
500+
SPtr<CommandBuffer> commandBuffer;
501+
SPtr<PooledRenderTexture> outputTexture;
502+
};
503+
460504
RendererViewProperties mProperties;
461505
mutable RendererViewContext mContext;
462506
Camera* mCamera;
@@ -476,11 +520,18 @@ namespace bs { namespace ct
476520
UINT32 mViewIdx;
477521

478522
// On-demand drawing
523+
bool mRedrawThisFrame = false;
479524
float mRedrawForSeconds = 0.0f;
480525
UINT32 mRedrawForFrames = 0;
526+
UINT64 mWaitingOnAutoExposureFrame = std::numeric_limits<UINT64>::max();
527+
mutable Vector<LuminanceUpdate> mLuminanceUpdates;
528+
529+
// Exposure
530+
float mPreviousEyeAdaptation = 0.0f;
531+
float mCurrentEyeAdaptation = 0.0f;
481532

482533
// Current frame info
483-
float mFrameDelta = 0.0f;
534+
FrameTimings mFrameTimings;
484535
bool mAsyncAnim = false;
485536
};
486537

0 commit comments

Comments
 (0)