EDIT: More tight render graph system(barrier resource cond)
This commit is contained in:
@@ -232,17 +232,32 @@ bool RenderGraph::compile()
|
||||
|
||||
struct ImageState
|
||||
{
|
||||
bool initialized = false;
|
||||
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
VkPipelineStageFlags2 stage = VK_PIPELINE_STAGE_2_NONE;
|
||||
VkAccessFlags2 access = 0;
|
||||
// Accumulate read stages/accesses since last barrier or write.
|
||||
VkPipelineStageFlags2 readStage = VK_PIPELINE_STAGE_2_NONE;
|
||||
VkAccessFlags2 readAccess = 0;
|
||||
// Track last write since last barrier.
|
||||
VkPipelineStageFlags2 writeStage = VK_PIPELINE_STAGE_2_NONE;
|
||||
VkAccessFlags2 writeAccess = 0;
|
||||
};
|
||||
|
||||
struct BufferState
|
||||
{
|
||||
bool initialized = false;
|
||||
VkPipelineStageFlags2 stage = VK_PIPELINE_STAGE_2_NONE;
|
||||
VkAccessFlags2 access = 0;
|
||||
VkPipelineStageFlags2 readStage = VK_PIPELINE_STAGE_2_NONE;
|
||||
VkAccessFlags2 readAccess = 0;
|
||||
VkPipelineStageFlags2 writeStage = VK_PIPELINE_STAGE_2_NONE;
|
||||
VkAccessFlags2 writeAccess = 0;
|
||||
};
|
||||
|
||||
auto access_has_write = [](VkAccessFlags2 access) -> bool {
|
||||
constexpr VkAccessFlags2 WRITE_MASK =
|
||||
VK_ACCESS_2_TRANSFER_WRITE_BIT |
|
||||
VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT |
|
||||
VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_2_HOST_WRITE_BIT |
|
||||
VK_ACCESS_2_MEMORY_WRITE_BIT;
|
||||
return (access & WRITE_MASK) != 0;
|
||||
};
|
||||
|
||||
auto is_depth_format = [](VkFormat format) {
|
||||
@@ -415,6 +430,53 @@ bool RenderGraph::compile()
|
||||
std::vector<ImageState> imageStates(imageCount);
|
||||
std::vector<BufferState> bufferStates(bufferCount);
|
||||
|
||||
// Seed initial states from imported/transient records. If an imported image has a known
|
||||
// starting layout but no stage/access, be conservative and assume an unknown prior write.
|
||||
for (size_t i = 0; i < imageCount; ++i)
|
||||
{
|
||||
const RGImageRecord *rec = _resources.get_image(RGImageHandle{static_cast<uint32_t>(i)});
|
||||
if (!rec) continue;
|
||||
imageStates[i].layout = rec->initialLayout;
|
||||
if (rec->initialLayout == VK_IMAGE_LAYOUT_UNDEFINED) continue;
|
||||
|
||||
VkPipelineStageFlags2 st = rec->initialStage;
|
||||
VkAccessFlags2 ac = rec->initialAccess;
|
||||
if (st == VK_PIPELINE_STAGE_2_NONE && ac == 0)
|
||||
{
|
||||
st = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
|
||||
ac = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT;
|
||||
}
|
||||
if (access_has_write(ac))
|
||||
{
|
||||
imageStates[i].writeStage = st;
|
||||
imageStates[i].writeAccess = ac;
|
||||
}
|
||||
else if (ac != 0)
|
||||
{
|
||||
imageStates[i].readStage = st;
|
||||
imageStates[i].readAccess = ac;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < bufferCount; ++i)
|
||||
{
|
||||
const RGBufferRecord *rec = _resources.get_buffer(RGBufferHandle{static_cast<uint32_t>(i)});
|
||||
if (!rec) continue;
|
||||
VkPipelineStageFlags2 st = rec->initialStage;
|
||||
VkAccessFlags2 ac = rec->initialAccess;
|
||||
if (st == VK_PIPELINE_STAGE_2_NONE) st = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT;
|
||||
if (access_has_write(ac))
|
||||
{
|
||||
bufferStates[i].writeStage = st;
|
||||
bufferStates[i].writeAccess = ac;
|
||||
}
|
||||
else if (ac != 0)
|
||||
{
|
||||
bufferStates[i].readStage = st;
|
||||
bufferStates[i].readAccess = ac;
|
||||
}
|
||||
}
|
||||
|
||||
// Track first/last use for lifetime diagnostics and future aliasing
|
||||
std::vector<int> imageFirst(imageCount, -1), imageLast(imageCount, -1);
|
||||
std::vector<int> bufferFirst(bufferCount, -1), bufferLast(bufferCount, -1);
|
||||
@@ -457,26 +519,50 @@ bool RenderGraph::compile()
|
||||
|
||||
ImageUsageInfo desired = usage_info_image(usage);
|
||||
|
||||
ImageState prev = imageStates[id];
|
||||
VkImageLayout prevLayout = prev.initialized ? prev.layout : _resources.initial_layout(RGImageHandle{id});
|
||||
VkPipelineStageFlags2 srcStage = prev.initialized
|
||||
? prev.stage
|
||||
: (prevLayout == VK_IMAGE_LAYOUT_UNDEFINED
|
||||
? VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT
|
||||
: VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT);
|
||||
VkAccessFlags2 srcAccess = prev.initialized
|
||||
? prev.access
|
||||
: (prevLayout == VK_IMAGE_LAYOUT_UNDEFINED
|
||||
? VkAccessFlags2{0}
|
||||
: (VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT));
|
||||
ImageState &state = imageStates[id];
|
||||
const VkImageLayout prevLayout = state.layout;
|
||||
const bool layoutChange = prevLayout != desired.layout;
|
||||
const bool desiredWrite = access_has_write(desired.access);
|
||||
const bool prevHasWrite = state.writeAccess != 0;
|
||||
const bool prevHasReads = state.readAccess != 0;
|
||||
|
||||
bool needBarrier = !prev.initialized
|
||||
|| prevLayout != desired.layout
|
||||
|| prev.stage != desired.stage
|
||||
|| prev.access != desired.access;
|
||||
bool needBarrier = layoutChange || (prevHasReads && desiredWrite);
|
||||
if (prevHasWrite)
|
||||
{
|
||||
// Keep previous behavior: don't force a barrier if we stay in the exact same write state.
|
||||
if (!(desiredWrite && !layoutChange && state.writeStage == desired.stage && state.writeAccess == desired.access))
|
||||
{
|
||||
needBarrier = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needBarrier)
|
||||
{
|
||||
VkPipelineStageFlags2 srcStage = VK_PIPELINE_STAGE_2_NONE;
|
||||
VkAccessFlags2 srcAccess = 0;
|
||||
if (prevHasWrite)
|
||||
{
|
||||
srcStage = state.writeStage;
|
||||
srcAccess = state.writeAccess;
|
||||
}
|
||||
else if (prevHasReads)
|
||||
{
|
||||
srcStage = state.readStage;
|
||||
srcAccess = state.readAccess;
|
||||
}
|
||||
else if (prevLayout == VK_IMAGE_LAYOUT_UNDEFINED)
|
||||
{
|
||||
srcStage = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT;
|
||||
srcAccess = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Known layout but unknown access; be conservative.
|
||||
srcStage = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
|
||||
srcAccess = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT;
|
||||
}
|
||||
if (srcStage == VK_PIPELINE_STAGE_2_NONE) srcStage = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT;
|
||||
|
||||
VkImageMemoryBarrier2 barrier{.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2};
|
||||
barrier.srcStageMask = srcStage;
|
||||
barrier.srcAccessMask = srcAccess;
|
||||
@@ -525,10 +611,28 @@ bool RenderGraph::compile()
|
||||
}
|
||||
}
|
||||
|
||||
imageStates[id].initialized = true;
|
||||
imageStates[id].layout = desired.layout;
|
||||
imageStates[id].stage = desired.stage;
|
||||
imageStates[id].access = desired.access;
|
||||
if (needBarrier)
|
||||
{
|
||||
state.readStage = VK_PIPELINE_STAGE_2_NONE;
|
||||
state.readAccess = 0;
|
||||
state.writeStage = VK_PIPELINE_STAGE_2_NONE;
|
||||
state.writeAccess = 0;
|
||||
}
|
||||
state.layout = desired.layout;
|
||||
if (desiredWrite)
|
||||
{
|
||||
state.readStage = VK_PIPELINE_STAGE_2_NONE;
|
||||
state.readAccess = 0;
|
||||
state.writeStage = desired.stage;
|
||||
state.writeAccess = desired.access;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.writeStage = VK_PIPELINE_STAGE_2_NONE;
|
||||
state.writeAccess = 0;
|
||||
state.readStage |= desired.stage;
|
||||
state.readAccess |= desired.access;
|
||||
}
|
||||
}
|
||||
|
||||
if (bufferCount == 0) continue;
|
||||
@@ -563,24 +667,37 @@ bool RenderGraph::compile()
|
||||
|
||||
BufferUsageInfo desired = usage_info_buffer(usage);
|
||||
|
||||
BufferState prev = bufferStates[id];
|
||||
VkPipelineStageFlags2 srcStage = prev.initialized
|
||||
? prev.stage
|
||||
: _resources.initial_stage(RGBufferHandle{id});
|
||||
if (srcStage == VK_PIPELINE_STAGE_2_NONE)
|
||||
{
|
||||
srcStage = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT;
|
||||
}
|
||||
VkAccessFlags2 srcAccess = prev.initialized
|
||||
? prev.access
|
||||
: _resources.initial_access(RGBufferHandle{id});
|
||||
BufferState &state = bufferStates[id];
|
||||
const bool desiredWrite = access_has_write(desired.access);
|
||||
const bool prevHasWrite = state.writeAccess != 0;
|
||||
const bool prevHasReads = state.readAccess != 0;
|
||||
|
||||
bool needBarrier = !prev.initialized
|
||||
|| prev.stage != desired.stage
|
||||
|| prev.access != desired.access;
|
||||
bool needBarrier = (prevHasReads && desiredWrite);
|
||||
if (prevHasWrite)
|
||||
{
|
||||
// Keep previous behavior: no barrier if staying in the exact same write state.
|
||||
if (!(desiredWrite && state.writeStage == desired.stage && state.writeAccess == desired.access))
|
||||
{
|
||||
needBarrier = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needBarrier)
|
||||
{
|
||||
VkPipelineStageFlags2 srcStage = VK_PIPELINE_STAGE_2_NONE;
|
||||
VkAccessFlags2 srcAccess = 0;
|
||||
if (prevHasWrite)
|
||||
{
|
||||
srcStage = state.writeStage;
|
||||
srcAccess = state.writeAccess;
|
||||
}
|
||||
else if (prevHasReads)
|
||||
{
|
||||
srcStage = state.readStage;
|
||||
srcAccess = state.readAccess;
|
||||
}
|
||||
if (srcStage == VK_PIPELINE_STAGE_2_NONE) srcStage = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT;
|
||||
|
||||
VkBufferMemoryBarrier2 barrier{.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2};
|
||||
barrier.srcStageMask = srcStage;
|
||||
barrier.srcAccessMask = srcAccess;
|
||||
@@ -616,9 +733,27 @@ bool RenderGraph::compile()
|
||||
}
|
||||
}
|
||||
|
||||
bufferStates[id].initialized = true;
|
||||
bufferStates[id].stage = desired.stage;
|
||||
bufferStates[id].access = desired.access;
|
||||
if (needBarrier)
|
||||
{
|
||||
state.readStage = VK_PIPELINE_STAGE_2_NONE;
|
||||
state.readAccess = 0;
|
||||
state.writeStage = VK_PIPELINE_STAGE_2_NONE;
|
||||
state.writeAccess = 0;
|
||||
}
|
||||
if (desiredWrite)
|
||||
{
|
||||
state.readStage = VK_PIPELINE_STAGE_2_NONE;
|
||||
state.readAccess = 0;
|
||||
state.writeStage = desired.stage;
|
||||
state.writeAccess = desired.access;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.writeStage = VK_PIPELINE_STAGE_2_NONE;
|
||||
state.writeAccess = 0;
|
||||
state.readStage |= desired.stage;
|
||||
state.readAccess |= desired.access;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -760,8 +895,6 @@ void RenderGraph::execute(VkCommandBuffer cmd)
|
||||
{
|
||||
depthInfo.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
}
|
||||
if (p.depthAttachment.clearOnLoad) depthInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
else depthInfo.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
if (!p.depthAttachment.store) depthInfo.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
hasDepth = true;
|
||||
if (rec->extent.width && rec->extent.height) set_or_clamp(rec->extent);
|
||||
|
||||
@@ -28,6 +28,9 @@ RGImageHandle RGResourceRegistry::add_imported(const RGImportedImageDesc& d)
|
||||
rec.format = d.format;
|
||||
rec.extent = d.extent;
|
||||
rec.initialLayout = d.currentLayout;
|
||||
// Keep the earliest known stage/access if set; otherwise record provided
|
||||
if (rec.initialStage == VK_PIPELINE_STAGE_2_NONE) rec.initialStage = d.currentStage;
|
||||
if (rec.initialAccess == 0) rec.initialAccess = d.currentAccess;
|
||||
return RGImageHandle{it->second};
|
||||
}
|
||||
|
||||
@@ -39,6 +42,8 @@ RGImageHandle RGResourceRegistry::add_imported(const RGImportedImageDesc& d)
|
||||
rec.format = d.format;
|
||||
rec.extent = d.extent;
|
||||
rec.initialLayout = d.currentLayout;
|
||||
rec.initialStage = d.currentStage;
|
||||
rec.initialAccess = d.currentAccess;
|
||||
_images.push_back(rec);
|
||||
uint32_t id = static_cast<uint32_t>(_images.size() - 1);
|
||||
if (d.image != VK_NULL_HANDLE) _imageLookup[d.image] = id;
|
||||
@@ -53,6 +58,8 @@ RGImageHandle RGResourceRegistry::add_transient(const RGImageDesc& d)
|
||||
rec.format = d.format;
|
||||
rec.extent = d.extent;
|
||||
rec.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
rec.initialStage = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT;
|
||||
rec.initialAccess = 0;
|
||||
rec.creationUsage = d.usage;
|
||||
|
||||
VkExtent3D size{ d.extent.width, d.extent.height, 1 };
|
||||
@@ -171,6 +178,18 @@ VkFormat RGResourceRegistry::image_format(RGImageHandle h) const
|
||||
return rec ? rec->format : VK_FORMAT_UNDEFINED;
|
||||
}
|
||||
|
||||
VkPipelineStageFlags2 RGResourceRegistry::initial_stage(RGImageHandle h) const
|
||||
{
|
||||
const RGImageRecord* rec = get_image(h);
|
||||
return rec ? rec->initialStage : VK_PIPELINE_STAGE_2_NONE;
|
||||
}
|
||||
|
||||
VkAccessFlags2 RGResourceRegistry::initial_access(RGImageHandle h) const
|
||||
{
|
||||
const RGImageRecord* rec = get_image(h);
|
||||
return rec ? rec->initialAccess : VkAccessFlags2{0};
|
||||
}
|
||||
|
||||
VkPipelineStageFlags2 RGResourceRegistry::initial_stage(RGBufferHandle h) const
|
||||
{
|
||||
const RGBufferRecord* rec = get_buffer(h);
|
||||
|
||||
@@ -19,6 +19,8 @@ struct RGImageRecord
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
VkExtent2D extent{0, 0};
|
||||
VkImageLayout initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
VkPipelineStageFlags2 initialStage = VK_PIPELINE_STAGE_2_NONE;
|
||||
VkAccessFlags2 initialAccess = 0;
|
||||
VkImageUsageFlags creationUsage = 0; // if transient; 0 for imported
|
||||
|
||||
// If transient, keep allocation owner for cleanup
|
||||
@@ -75,6 +77,8 @@ public:
|
||||
|
||||
VkImageLayout initial_layout(RGImageHandle h) const;
|
||||
VkFormat image_format(RGImageHandle h) const;
|
||||
VkPipelineStageFlags2 initial_stage(RGImageHandle h) const;
|
||||
VkAccessFlags2 initial_access(RGImageHandle h) const;
|
||||
|
||||
VkPipelineStageFlags2 initial_stage(RGBufferHandle h) const;
|
||||
VkAccessFlags2 initial_access(RGBufferHandle h) const;
|
||||
|
||||
@@ -64,6 +64,11 @@ struct RGImportedImageDesc
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
VkExtent2D extent{0, 0};
|
||||
VkImageLayout currentLayout = VK_IMAGE_LAYOUT_UNDEFINED; // layout at graph begin
|
||||
// Optional: last known access state at graph begin. If left as NONE/0 and
|
||||
// currentLayout is not UNDEFINED, the graph conservatively assumes an
|
||||
// unknown prior write (ALL_COMMANDS + MEMORY_READ|WRITE) for the first barrier.
|
||||
VkPipelineStageFlags2 currentStage = VK_PIPELINE_STAGE_2_NONE;
|
||||
VkAccessFlags2 currentAccess = 0;
|
||||
};
|
||||
|
||||
struct RGImportedBufferDesc
|
||||
|
||||
Reference in New Issue
Block a user