refactor Renderer to make it more flexible
This is in preparation to supporting screen-space effects. There are two major changes: - RenderPass is now copiable and intended to be passed by copy to the execute stage of frame graph passes - The color pass is in its own function now This actually simplify RenderPass api.
This commit is contained in:
committed by
Mathias Agopian
parent
d9ee2fa10d
commit
e27158507e
@@ -473,7 +473,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssao(FrameGraph& fg, RenderP
|
||||
return ssao;
|
||||
}
|
||||
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::depthPass(FrameGraph& fg, RenderPass& pass,
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::depthPass(FrameGraph& fg, RenderPass const& pass,
|
||||
uint32_t width, uint32_t height,
|
||||
View::AmbientOcclusionOptions const& options) noexcept {
|
||||
|
||||
@@ -483,9 +483,6 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::depthPass(FrameGraph& fg, Re
|
||||
FrameGraphRenderTargetHandle rt;
|
||||
};
|
||||
|
||||
RenderPass::Command const* first = pass.getCommands().begin();
|
||||
RenderPass::Command const* last = pass.getCommands().end();
|
||||
|
||||
// sanitize a bit the user provided scaling factor
|
||||
const float scale = std::min(std::abs(options.resolution), 1.0f);
|
||||
width = std::ceil(width * scale);
|
||||
@@ -510,10 +507,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::depthPass(FrameGraph& fg, Re
|
||||
data.rt = builder.createRenderTarget("SSAO Depth Target", d,
|
||||
TargetBufferFlags::DEPTH);
|
||||
},
|
||||
[=, &pass](FrameGraphPassResources const& resources,
|
||||
[pass](FrameGraphPassResources const& resources,
|
||||
DepthPassData const& data, DriverApi& driver) {
|
||||
auto out = resources.getRenderTarget(data.rt);
|
||||
pass.execute(resources.getPassName(), out.target, out.params, first, last);
|
||||
pass.execute(resources.getPassName(), out.target, out.params);
|
||||
});
|
||||
|
||||
return ssaoDepthPass.getData().depth;
|
||||
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
private:
|
||||
details::FEngine& mEngine;
|
||||
|
||||
FrameGraphId<FrameGraphTexture> depthPass(FrameGraph& fg, details::RenderPass& pass,
|
||||
FrameGraphId<FrameGraphTexture> depthPass(FrameGraph& fg, details::RenderPass const& pass,
|
||||
uint32_t width, uint32_t height, View::AmbientOcclusionOptions const& options) noexcept;
|
||||
|
||||
FrameGraphId<FrameGraphTexture> mipmapPass(FrameGraph& fg,
|
||||
|
||||
@@ -74,6 +74,12 @@ void RenderPass::overrideMaterial(FMaterial const* material, FMaterialInstance c
|
||||
mMaterialInstanceOverride = mi;
|
||||
}
|
||||
|
||||
RenderPass::Command* RenderPass::newCommandBuffer() noexcept {
|
||||
GrowingSlice<Command>& commands = mCommands;
|
||||
commands = GrowingSlice<Command>(commands.end(), commands.capacity() - commands.size());
|
||||
return commands.begin();
|
||||
}
|
||||
|
||||
RenderPass::Command* RenderPass::appendCommands(CommandTypeFlags const commandTypeFlags) noexcept {
|
||||
SYSTRACE_CONTEXT();
|
||||
|
||||
@@ -149,15 +155,15 @@ RenderPass::Command* RenderPass::appendCustomCommand(Pass pass, CustomCommand cu
|
||||
return curr + 1;
|
||||
}
|
||||
|
||||
RenderPass::Command* RenderPass::sortCommands(Command* curr) noexcept {
|
||||
RenderPass::Command* RenderPass::sortCommands() noexcept {
|
||||
SYSTRACE_NAME("sort and trim commands");
|
||||
|
||||
GrowingSlice<Command>& commands = mCommands;
|
||||
|
||||
std::sort(curr, commands.end());
|
||||
std::sort(commands.begin(), commands.end());
|
||||
|
||||
// find the last command
|
||||
Command const* const last = std::partition_point(curr, commands.end(),
|
||||
Command const* const last = std::partition_point(commands.begin(), commands.end(),
|
||||
[](Command const& c) {
|
||||
return c.key != uint64_t(Pass::SENTINEL);
|
||||
});
|
||||
@@ -169,10 +175,11 @@ RenderPass::Command* RenderPass::sortCommands(Command* curr) noexcept {
|
||||
|
||||
void RenderPass::execute(const char* name,
|
||||
backend::Handle<backend::HwRenderTarget> renderTarget,
|
||||
backend::RenderPassParams params,
|
||||
Command const* first, Command const* last) const noexcept {
|
||||
backend::RenderPassParams params) const noexcept {
|
||||
|
||||
FEngine& engine = mEngine;
|
||||
Command const* const first = mCommands.begin();
|
||||
Command const* const last = mCommands.end();
|
||||
|
||||
// Take care not to upload data within the render pass (synchronize can commit froxel data)
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
@@ -256,6 +256,8 @@ public:
|
||||
void setCamera(const CameraInfo& camera) noexcept;
|
||||
void setRenderFlags(RenderFlags flags) noexcept;
|
||||
|
||||
Command* newCommandBuffer() noexcept;
|
||||
|
||||
// returns mCommands.end()
|
||||
Command* appendCommands(CommandTypeFlags commandTypeFlags) noexcept;
|
||||
|
||||
@@ -263,14 +265,13 @@ public:
|
||||
Command* appendCustomCommand(Pass pass, CustomCommand custom, uint32_t order,
|
||||
std::function<void()> command);
|
||||
|
||||
// sorts commands from curr to mCommands.end(), then trims sentinels and returns
|
||||
// sorts commands, then trims sentinels and returns
|
||||
// the new mCommands.end()
|
||||
Command* sortCommands(Command* curr) noexcept;
|
||||
Command* sortCommands() noexcept;
|
||||
|
||||
void execute(const char* name,
|
||||
backend::Handle<backend::HwRenderTarget> renderTarget,
|
||||
backend::RenderPassParams params,
|
||||
Command const* first, Command const* last) const noexcept;
|
||||
backend::RenderPassParams params) const noexcept;
|
||||
|
||||
utils::GrowingSlice<Command>& getCommands() { return mCommands; }
|
||||
utils::Slice<Command> const& getCommands() const { return mCommands; }
|
||||
@@ -317,8 +318,7 @@ private:
|
||||
// a reference to the Engine, mostly to get to things like JobSystem
|
||||
FEngine& mEngine;
|
||||
|
||||
// a reference to the command vector (so we can for e.g. append to it)
|
||||
utils::GrowingSlice<Command>& mCommands;
|
||||
utils::GrowingSlice<Command> mCommands;
|
||||
|
||||
// the SOA containing the renderables we're interested in
|
||||
FScene::RenderableSoa const* mRenderableSoa = nullptr;
|
||||
|
||||
@@ -219,7 +219,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
[&engine, &view](JobSystem&, JobSystem::Job*) { view.froxelize(engine); }));
|
||||
|
||||
/*
|
||||
* Allocate command buffer.
|
||||
* Allocate command buffer
|
||||
*/
|
||||
|
||||
FScene& scene = *view.getScene();
|
||||
@@ -229,7 +229,6 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
GrowingSlice<Command> commands(
|
||||
arena.allocate<Command>(commandsCount, CACHELINE_SIZE), commandsCount);
|
||||
|
||||
|
||||
RenderPass pass(engine, commands);
|
||||
RenderPass::RenderFlags renderFlags = 0;
|
||||
if (view.hasShadowing()) renderFlags |= RenderPass::HAS_SHADOWING;
|
||||
@@ -238,16 +237,16 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
if (view.isFrontFaceWindingInverted()) renderFlags |= RenderPass::HAS_INVERSE_FRONT_FACES;
|
||||
pass.setRenderFlags(renderFlags);
|
||||
|
||||
|
||||
/*
|
||||
* Shadow pass
|
||||
*/
|
||||
|
||||
if (view.hasShadowing()) {
|
||||
view.getShadowMap().render(driver, pass, view);
|
||||
// TODO: use the framegraph for the shadow passes
|
||||
RenderPass shadowMapPass = pass;
|
||||
view.getShadowMap().render(driver, shadowMapPass, view);
|
||||
driver.flush(); // Kick the GPU since we're done with this render target
|
||||
engine.flush(); // Wake-up the driver thread
|
||||
commands.clear();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -284,34 +283,62 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
pass.setCamera(cameraInfo);
|
||||
pass.setGeometry(scene.getRenderableData(), view.getVisibleRenderables(), scene.getRenderableUBO());
|
||||
|
||||
view.updatePrimitivesLod(engine, cameraInfo,
|
||||
scene.getRenderableData(), view.getVisibleRenderables());
|
||||
view.updatePrimitivesLod(engine, cameraInfo,scene.getRenderableData(), view.getVisibleRenderables());
|
||||
view.prepareCamera(cameraInfo, svp);
|
||||
view.commitUniforms(driver);
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// SSAO pass
|
||||
|
||||
const bool useSSAO = view.getAmbientOcclusion() != View::AmbientOcclusion::NONE;
|
||||
|
||||
// SSAO pass -- automatically culled if not used
|
||||
if (useSSAO) {
|
||||
auto curr = pass.getCommands().end();
|
||||
// don't generate commands if we don't have SSAO
|
||||
// TODO: ideally this should be a FrameGraph pass to participate to automatic culling
|
||||
pass.newCommandBuffer();
|
||||
pass.appendCommands(RenderPass::CommandTypeFlags::SSAO);
|
||||
pass.sortCommands(curr);
|
||||
pass.sortCommands();
|
||||
}
|
||||
|
||||
FrameGraphId<FrameGraphTexture> ssao = ppm.ssao(fg, pass, svp, cameraInfo, view.getAmbientOcclusionOptions());
|
||||
// SSAO pass -- automatically culled if not used
|
||||
FrameGraphId<FrameGraphTexture> ssao = ppm.ssao(fg, pass, svp, cameraInfo,
|
||||
view.getAmbientOcclusionOptions());
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// Color passes
|
||||
|
||||
// generate the normal commands
|
||||
// TODO: ideally this should be a FrameGraph pass to participate to automatic culling
|
||||
RenderPass::CommandTypeFlags commandType = getCommandType(view.getDepthPrepass());
|
||||
Command* colorPassBegin = pass.getCommands().end();
|
||||
pass.newCommandBuffer();
|
||||
pass.appendCommands(commandType);
|
||||
Command const* colorPassEnd = pass.sortCommands(colorPassBegin);
|
||||
pass.sortCommands();
|
||||
|
||||
// We only honor the view's color buffer clear flags, depth/stencil are handled by the framefraph
|
||||
// We use a framegraph pass to commit the View's uniforms and wait for froxelization to finish
|
||||
auto& prepareColorPasses = fg.addPass<PrepareColorPassesData>("Prepare Color Passes",
|
||||
[useSSAO, ssao, msaa, hdrFormat, &svp]
|
||||
(FrameGraph::Builder& builder, PrepareColorPassesData& data) {
|
||||
if (useSSAO) {
|
||||
data.ssao = builder.sample(ssao);
|
||||
}
|
||||
data.svp = svp;
|
||||
data.hdrFormat = hdrFormat;
|
||||
data.msaa = msaa;
|
||||
builder.sideEffect();
|
||||
},
|
||||
[&ppm, &js, &view, jobFroxelize]
|
||||
(FrameGraphPassResources const& resources,
|
||||
PrepareColorPassesData const& data, DriverApi& driver) {
|
||||
view.prepareSSAO(data.ssao.isValid() ? resources.getTexture(data.ssao)
|
||||
: ppm.getNoSSAOTexture());
|
||||
view.commitUniforms(driver);
|
||||
if (jobFroxelize) {
|
||||
auto sync = jobFroxelize;
|
||||
js.waitAndRelease(sync);
|
||||
view.commitFroxels(driver);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// We only honor the view's color buffer clear flags, depth/stencil are handled by the FrameGraph
|
||||
uint8_t viewClearFlags = view.getClearFlags() & (uint8_t)TargetBufferFlags::ALL;
|
||||
|
||||
// FIXME: when the view doesn't ask for a clear, but it's drawn in an intermediate buffer
|
||||
@@ -319,71 +346,18 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
TargetBufferFlags clearFlags = (TargetBufferFlags(viewClearFlags) & TargetBufferFlags::COLOR)
|
||||
| TargetBufferFlags::DEPTH;
|
||||
|
||||
struct ColorPassData {
|
||||
FrameGraphId<FrameGraphTexture> color;
|
||||
FrameGraphId<FrameGraphTexture> depth;
|
||||
FrameGraphId<FrameGraphTexture> ssao;
|
||||
FrameGraphRenderTargetHandle rt{};
|
||||
};
|
||||
FrameGraphId<FrameGraphTexture> colorPassOutput = colorPass(fg, prepareColorPasses.getData(),
|
||||
pass, clearFlags, view.getClearColor());
|
||||
|
||||
auto& colorPass = fg.addPass<ColorPassData>("Color Pass",
|
||||
[&svp, hdrFormat, msaa, clearFlags, useSSAO, ssao]
|
||||
(FrameGraph::Builder& builder, ColorPassData& data) {
|
||||
FrameGraphId<FrameGraphTexture> input = colorPassOutput;
|
||||
|
||||
if (useSSAO) {
|
||||
data.ssao = builder.sample(ssao);
|
||||
}
|
||||
fg.simpleSideEffectPass("Finish Color Passes", [&view]() {
|
||||
// Unbind SSAO sampler, b/c the FrameGraph will delete the texture at the end of the pass.
|
||||
view.cleanupSSAO();
|
||||
});
|
||||
|
||||
data.color = builder.createTexture("Color Buffer",
|
||||
{ .width = svp.width, .height = svp.height, .format = hdrFormat });
|
||||
|
||||
data.depth = builder.createTexture("Depth Buffer", {
|
||||
.width = svp.width, .height = svp.height,
|
||||
.format = TextureFormat::DEPTH24
|
||||
});
|
||||
data.depth = builder.write(builder.read(data.depth));
|
||||
|
||||
data.color = builder.write(builder.read(data.color));
|
||||
data.rt = builder.createRenderTarget("Color Pass Target", {
|
||||
.attachments = { data.color, data.depth },
|
||||
.samples = msaa,
|
||||
}, clearFlags);
|
||||
},
|
||||
[&pass, &ppm, colorPassBegin, colorPassEnd, jobFroxelize, &js, &view]
|
||||
(FrameGraphPassResources const& resources,
|
||||
ColorPassData const& data, DriverApi& driver) {
|
||||
auto out = resources.getRenderTarget(data.rt);
|
||||
Handle<HwTexture> ssao;
|
||||
if (data.ssao.isValid()) {
|
||||
ssao = resources.getTexture(data.ssao);
|
||||
} else {
|
||||
ssao = ppm.getNoSSAOTexture();
|
||||
}
|
||||
view.prepareSSAO(ssao);
|
||||
view.commitUniforms(driver);
|
||||
|
||||
out.params.clearColor = view.getClearColor();
|
||||
|
||||
if (jobFroxelize) {
|
||||
auto sync = jobFroxelize;
|
||||
js.waitAndRelease(sync);
|
||||
view.commitFroxels(driver);
|
||||
}
|
||||
|
||||
pass.execute(resources.getPassName(), out.target, out.params,
|
||||
colorPassBegin, colorPassEnd);
|
||||
|
||||
// Unbind the SSAO sampler, as the frame graph will delete the texture at the end of
|
||||
// the pass.
|
||||
view.cleanupSSAO();
|
||||
});
|
||||
|
||||
jobFroxelize = nullptr;
|
||||
FrameGraphId<FrameGraphTexture> input = colorPass.getData().color;
|
||||
|
||||
/*
|
||||
* Post Processing...
|
||||
*/
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// Post Processing...
|
||||
|
||||
const TextureFormat ldrFormat = (toneMapping && fxaa) ?
|
||||
TextureFormat::RGBA8 : getLdrFormat(translucent); // e.g. RGB8 or RGBA8
|
||||
@@ -403,7 +377,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
// We need to do special processing when rendering directly into the swap-chain (see
|
||||
// comments below). That is when the viewRenderTarget is the default render target
|
||||
// (mRenderTarget) and we're rendering into it.
|
||||
if (input == colorPass.getData().color) {
|
||||
if (input == colorPassOutput) {
|
||||
// here we know we're not scaled because either post-processing is disabled (which implies
|
||||
// no scaling, or scaled==false because otherwise we wouldn't be rendering in the
|
||||
// default target.
|
||||
@@ -439,6 +413,58 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
recordHighWatermark(pass.getCommandsHighWatermark());
|
||||
}
|
||||
|
||||
FrameGraphId<FrameGraphTexture> FRenderer::colorPass(FrameGraph& fg,
|
||||
PrepareColorPassesData const& blackboard,
|
||||
RenderPass const& pass, TargetBufferFlags clearFlags, float4 clearColor) noexcept {
|
||||
|
||||
struct ColorPassData {
|
||||
FrameGraphId<FrameGraphTexture> color;
|
||||
FrameGraphId<FrameGraphTexture> depth;
|
||||
FrameGraphId<FrameGraphTexture> ssao;
|
||||
FrameGraphRenderTargetHandle rt{};
|
||||
};
|
||||
|
||||
auto& colorPass = fg.addPass<ColorPassData>("Color Pass",
|
||||
[&blackboard, clearFlags]
|
||||
(FrameGraph::Builder& builder, ColorPassData& data) {
|
||||
|
||||
auto& svp = blackboard.svp;
|
||||
auto hdrFormat = blackboard.hdrFormat;
|
||||
auto msaa = blackboard.msaa;
|
||||
|
||||
if (blackboard.ssao.isValid()) {
|
||||
data.ssao = builder.sample(blackboard.ssao);
|
||||
}
|
||||
|
||||
data.color = builder.createTexture("Color Buffer",
|
||||
{ .width = svp.width, .height = svp.height, .format = hdrFormat });
|
||||
|
||||
data.depth = builder.createTexture("Depth Buffer", {
|
||||
.width = svp.width, .height = svp.height,
|
||||
.format = TextureFormat::DEPTH24
|
||||
});
|
||||
|
||||
data.color = builder.write(builder.read(data.color));
|
||||
data.depth = builder.write(builder.read(data.depth));
|
||||
|
||||
data.rt = builder.createRenderTarget("Color Pass Target", {
|
||||
.attachments = { data.color, data.depth },
|
||||
.samples = msaa,
|
||||
}, clearFlags);
|
||||
},
|
||||
[pass, clearColor]
|
||||
(FrameGraphPassResources const& resources,
|
||||
ColorPassData const& data, DriverApi& driver) {
|
||||
auto out = resources.getRenderTarget(data.rt);
|
||||
|
||||
out.params.clearColor = clearColor;
|
||||
|
||||
pass.execute(resources.getPassName(), out.target, out.params);
|
||||
});
|
||||
|
||||
return colorPass.getData().color;
|
||||
}
|
||||
|
||||
void FRenderer::copyFrame(FSwapChain* dstSwapChain, filament::Viewport const& dstViewport,
|
||||
filament::Viewport const& srcViewport, CopyFrameFlag flags) {
|
||||
SYSTRACE_CALL();
|
||||
@@ -645,7 +671,7 @@ Handle<HwRenderTarget> FRenderer::getRenderTarget(FView& view) const noexcept {
|
||||
return viewRenderTarget ? viewRenderTarget : mRenderTarget;
|
||||
}
|
||||
|
||||
RenderPass::CommandTypeFlags FRenderer::getCommandType(View::DepthPrepass prepass) const noexcept {
|
||||
RenderPass::CommandTypeFlags FRenderer::getCommandType(View::DepthPrepass prepass) noexcept {
|
||||
RenderPass::CommandTypeFlags commandType;
|
||||
switch (prepass) {
|
||||
case View::DepthPrepass::DEFAULT:
|
||||
|
||||
@@ -186,14 +186,9 @@ void ShadowMap::render(DriverApi& driver, RenderPass& pass, FView& view) noexcep
|
||||
view.commitUniforms(driver);
|
||||
|
||||
pass.overridePolygonOffset(&mPolygonOffset);
|
||||
|
||||
auto curr = pass.getCommands().end();
|
||||
pass.appendCommands(RenderPass::SHADOW);
|
||||
pass.sortCommands(curr);
|
||||
|
||||
pass.execute("Shadow map Pass", getRenderTarget(), params,
|
||||
pass.getCommands().begin(), pass.getCommands().end());
|
||||
pass.overridePolygonOffset(nullptr);
|
||||
pass.sortCommands();
|
||||
pass.execute("Shadow map Pass", getRenderTarget(), params);
|
||||
}
|
||||
|
||||
void ShadowMap::terminate(DriverApi& driverApi) noexcept {
|
||||
|
||||
@@ -100,7 +100,18 @@ private:
|
||||
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
||||
backend::PixelBufferDescriptor&& buffer);
|
||||
|
||||
RenderPass::CommandTypeFlags getCommandType(View::DepthPrepass prepass) const noexcept;
|
||||
static RenderPass::CommandTypeFlags getCommandType(View::DepthPrepass prepass) noexcept;
|
||||
|
||||
struct PrepareColorPassesData {
|
||||
FrameGraphId<FrameGraphTexture> ssao;
|
||||
Viewport svp;
|
||||
backend::TextureFormat hdrFormat;
|
||||
uint8_t msaa;
|
||||
};
|
||||
|
||||
static FrameGraphId<FrameGraphTexture> colorPass(FrameGraph& fg,
|
||||
PrepareColorPassesData const& blackboard, RenderPass const& pass,
|
||||
backend::TargetBufferFlags clearFlags, math::float4 clearColor = {}) noexcept;
|
||||
|
||||
void recordHighWatermark(size_t watermark) noexcept {
|
||||
mCommandsHighWatermark = std::max(mCommandsHighWatermark, watermark);
|
||||
|
||||
@@ -216,7 +216,7 @@ FrameGraphHandle FrameGraph::moveResource(FrameGraphHandle from, FrameGraphHandl
|
||||
}
|
||||
|
||||
void FrameGraph::present(FrameGraphHandle input) {
|
||||
addPass<std::tuple<>>("Present",
|
||||
addPass<Empty>("Present",
|
||||
[&](Builder& builder, auto& data) {
|
||||
builder.read(input);
|
||||
builder.sideEffect();
|
||||
|
||||
@@ -147,6 +147,8 @@ public:
|
||||
FrameGraph& operator = (FrameGraph const&) = delete;
|
||||
~FrameGraph();
|
||||
|
||||
struct Empty{};
|
||||
|
||||
/*
|
||||
* Add a pass to the framegraph.
|
||||
* The Setup lambda is called synchronously and used to declare which and how resources are
|
||||
@@ -175,6 +177,16 @@ public:
|
||||
// Adds a reference to 'input', preventing it from being culled.
|
||||
void present(FrameGraphHandle input);
|
||||
|
||||
// Adds a simple execute-only pass with side effect (so it's not culled)
|
||||
template<typename Execute>
|
||||
void simpleSideEffectPass(const char* name, Execute&& execute) {
|
||||
addPass<Empty>(name, [](FrameGraph::Builder& builder, auto& data) { builder.sideEffect(); },
|
||||
[execute](FrameGraphPassResources const& resources, auto const& data,
|
||||
backend::DriverApi& driver) {
|
||||
execute();
|
||||
});
|
||||
}
|
||||
|
||||
// Returns whether the resource handle is valid. A resource handle becomes invalid after
|
||||
// it's used to declare a resource write (see Builder::write()).
|
||||
bool isValid(FrameGraphHandle r) const noexcept;
|
||||
|
||||
@@ -163,6 +163,8 @@ public:
|
||||
using size_type = typename Slice<T, SIZE_TYPE>::size_type;
|
||||
|
||||
GrowingSlice() noexcept = default;
|
||||
GrowingSlice(GrowingSlice const& rhs) noexcept = default;
|
||||
GrowingSlice(GrowingSlice&& rhs) noexcept = default;
|
||||
|
||||
template<typename Iter>
|
||||
GrowingSlice(Iter begin, size_type count) noexcept
|
||||
@@ -180,7 +182,7 @@ public:
|
||||
template<typename Iter>
|
||||
void set(Iter begin, size_type count) UTILS_RESTRICT noexcept {
|
||||
this->Slice<T, SIZE_TYPE>::set(begin, count);
|
||||
mCapOffset = count;
|
||||
mCapOffset = count;
|
||||
}
|
||||
|
||||
template<typename Iter>
|
||||
|
||||
Reference in New Issue
Block a user