2D scene rendering + image loading #15
@@ -86,22 +86,28 @@ void RenderContext::endFrame() {
|
|||||||
FrameData& frame = this->frames[this->currentFrameIndex];
|
FrameData& frame = this->frames[this->currentFrameIndex];
|
||||||
frame.commandBuffer.end();
|
frame.commandBuffer.end();
|
||||||
|
|
||||||
vk::Semaphore waitSemaphore = frame.imageAvailable.get();
|
|
||||||
vk::Semaphore signalSemaphore =
|
vk::Semaphore signalSemaphore =
|
||||||
this->renderFinishedSemaphores.at(this->currentImageIndex).get();
|
this->renderFinishedSemaphores.at(this->currentImageIndex).get();
|
||||||
vk::PipelineStageFlags waitStage = SWAPCHAIN_ACQUIRE_STAGE;
|
|
||||||
vk::CommandBuffer commandBuffer = frame.commandBuffer;
|
|
||||||
|
|
||||||
vk::SubmitInfo submitInfo{};
|
vk::SemaphoreSubmitInfo waitInfo{};
|
||||||
submitInfo.waitSemaphoreCount = 1;
|
waitInfo.semaphore = frame.imageAvailable.get();
|
||||||
submitInfo.pWaitSemaphores = &waitSemaphore;
|
waitInfo.stageMask = SWAPCHAIN_ACQUIRE_STAGE;
|
||||||
submitInfo.pWaitDstStageMask = &waitStage;
|
|
||||||
submitInfo.commandBufferCount = 1;
|
|
||||||
submitInfo.pCommandBuffers = &commandBuffer;
|
|
||||||
submitInfo.signalSemaphoreCount = 1;
|
|
||||||
submitInfo.pSignalSemaphores = &signalSemaphore;
|
|
||||||
|
|
||||||
this->device.getGraphicsQueue().submit(submitInfo, frame.inFlight.get());
|
// Present has no pipeline stage to wait on, so signal once everything in
|
||||||
|
// the submission is done.
|
||||||
|
vk::SemaphoreSubmitInfo signalInfo{};
|
||||||
|
signalInfo.semaphore = signalSemaphore;
|
||||||
|
signalInfo.stageMask = vk::PipelineStageFlagBits2::eAllCommands;
|
||||||
|
|
||||||
|
vk::CommandBufferSubmitInfo commandBufferInfo{};
|
||||||
|
commandBufferInfo.commandBuffer = frame.commandBuffer;
|
||||||
|
|
||||||
|
vk::SubmitInfo2 submitInfo{};
|
||||||
|
submitInfo.setWaitSemaphoreInfos(waitInfo);
|
||||||
|
submitInfo.setCommandBufferInfos(commandBufferInfo);
|
||||||
|
submitInfo.setSignalSemaphoreInfos(signalInfo);
|
||||||
|
|
||||||
|
this->device.getGraphicsQueue().submit2(submitInfo, frame.inFlight.get());
|
||||||
|
|
||||||
vk::Result result = this->swapchain->present(
|
vk::Result result = this->swapchain->present(
|
||||||
this->device.getPresentQueue(), this->currentImageIndex, signalSemaphore);
|
this->device.getPresentQueue(), this->currentImageIndex, signalSemaphore);
|
||||||
|
|||||||
@@ -55,8 +55,6 @@ void RenderGraph::transitionTo(ImageRef ref, const AccessState& next,
|
|||||||
pending.barriers.push_back(makeImageBarrier(
|
pending.barriers.push_back(makeImageBarrier(
|
||||||
imported.image->get(), imported.image->getSubresourceRange(),
|
imported.image->get(), imported.image->getSubresourceRange(),
|
||||||
imported.state, next));
|
imported.state, next));
|
||||||
pending.srcStages |= imported.state.stage;
|
|
||||||
pending.dstStages |= next.stage;
|
|
||||||
imported.state = next;
|
imported.state = next;
|
||||||
} else {
|
} else {
|
||||||
// Same-layout read after read. Nothing to record, but a later write has
|
// Same-layout read after read. Nothing to record, but a later write has
|
||||||
@@ -68,12 +66,7 @@ void RenderGraph::transitionTo(ImageRef ref, const AccessState& next,
|
|||||||
|
|
||||||
void RenderGraph::flush(vk::CommandBuffer commandBuffer,
|
void RenderGraph::flush(vk::CommandBuffer commandBuffer,
|
||||||
PendingBarriers& pending) {
|
PendingBarriers& pending) {
|
||||||
if (pending.barriers.empty()) {
|
recordImageBarriers(commandBuffer, pending.barriers);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
commandBuffer.pipelineBarrier(pending.srcStages, pending.dstStages, {},
|
|
||||||
nullptr, nullptr, pending.barriers);
|
|
||||||
pending = PendingBarriers{};
|
pending = PendingBarriers{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -104,9 +104,7 @@ class RenderGraph {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct PendingBarriers {
|
struct PendingBarriers {
|
||||||
std::vector<vk::ImageMemoryBarrier> barriers;
|
std::vector<vk::ImageMemoryBarrier2> barriers;
|
||||||
vk::PipelineStageFlags srcStages;
|
|
||||||
vk::PipelineStageFlags dstStages;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void transitionTo(ImageRef ref, const AccessState& next,
|
void transitionTo(ImageRef ref, const AccessState& next,
|
||||||
|
|||||||
@@ -14,10 +14,11 @@ constexpr std::array<Access, 7> ALL_ACCESSES = {
|
|||||||
Access::Present,
|
Access::Present,
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr vk::AccessFlags WRITE_ACCESSES =
|
constexpr vk::AccessFlags2 WRITE_ACCESSES =
|
||||||
vk::AccessFlagBits::eColorAttachmentWrite |
|
vk::AccessFlagBits2::eColorAttachmentWrite |
|
||||||
vk::AccessFlagBits::eDepthStencilAttachmentWrite |
|
vk::AccessFlagBits2::eDepthStencilAttachmentWrite |
|
||||||
vk::AccessFlagBits::eShaderWrite | vk::AccessFlagBits::eTransferWrite;
|
vk::AccessFlagBits2::eShaderStorageWrite |
|
||||||
|
vk::AccessFlagBits2::eTransferWrite;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -25,34 +26,37 @@ AccessState getAccessState(Access access) {
|
|||||||
switch (access) {
|
switch (access) {
|
||||||
case Access::ColorWrite:
|
case Access::ColorWrite:
|
||||||
return {vk::ImageLayout::eColorAttachmentOptimal,
|
return {vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
vk::PipelineStageFlagBits::eColorAttachmentOutput,
|
vk::PipelineStageFlagBits2::eColorAttachmentOutput,
|
||||||
vk::AccessFlagBits::eColorAttachmentRead |
|
vk::AccessFlagBits2::eColorAttachmentRead |
|
||||||
vk::AccessFlagBits::eColorAttachmentWrite};
|
vk::AccessFlagBits2::eColorAttachmentWrite};
|
||||||
case Access::DepthWrite:
|
case Access::DepthWrite:
|
||||||
return {vk::ImageLayout::eDepthStencilAttachmentOptimal,
|
return {vk::ImageLayout::eDepthStencilAttachmentOptimal,
|
||||||
vk::PipelineStageFlagBits::eEarlyFragmentTests |
|
vk::PipelineStageFlagBits2::eEarlyFragmentTests |
|
||||||
vk::PipelineStageFlagBits::eLateFragmentTests,
|
vk::PipelineStageFlagBits2::eLateFragmentTests,
|
||||||
vk::AccessFlagBits::eDepthStencilAttachmentRead |
|
vk::AccessFlagBits2::eDepthStencilAttachmentRead |
|
||||||
vk::AccessFlagBits::eDepthStencilAttachmentWrite};
|
vk::AccessFlagBits2::eDepthStencilAttachmentWrite};
|
||||||
case Access::SampledRead:
|
case Access::SampledRead:
|
||||||
return {vk::ImageLayout::eShaderReadOnlyOptimal,
|
return {vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||||
vk::PipelineStageFlagBits::eFragmentShader,
|
vk::PipelineStageFlagBits2::eFragmentShader,
|
||||||
vk::AccessFlagBits::eShaderRead};
|
vk::AccessFlagBits2::eShaderSampledRead};
|
||||||
case Access::StorageReadWrite:
|
case Access::StorageReadWrite:
|
||||||
return {
|
return {vk::ImageLayout::eGeneral,
|
||||||
vk::ImageLayout::eGeneral, vk::PipelineStageFlagBits::eComputeShader,
|
vk::PipelineStageFlagBits2::eComputeShader,
|
||||||
vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite};
|
vk::AccessFlagBits2::eShaderStorageRead |
|
||||||
|
vk::AccessFlagBits2::eShaderStorageWrite};
|
||||||
case Access::TransferSrc:
|
case Access::TransferSrc:
|
||||||
return {vk::ImageLayout::eTransferSrcOptimal,
|
return {vk::ImageLayout::eTransferSrcOptimal,
|
||||||
vk::PipelineStageFlagBits::eTransfer,
|
vk::PipelineStageFlagBits2::eTransfer,
|
||||||
vk::AccessFlagBits::eTransferRead};
|
vk::AccessFlagBits2::eTransferRead};
|
||||||
case Access::TransferDst:
|
case Access::TransferDst:
|
||||||
return {vk::ImageLayout::eTransferDstOptimal,
|
return {vk::ImageLayout::eTransferDstOptimal,
|
||||||
vk::PipelineStageFlagBits::eTransfer,
|
vk::PipelineStageFlagBits2::eTransfer,
|
||||||
vk::AccessFlagBits::eTransferWrite};
|
vk::AccessFlagBits2::eTransferWrite};
|
||||||
case Access::Present:
|
case Access::Present:
|
||||||
|
// Presentation isn't a pipeline stage; the present waits on a semaphore
|
||||||
|
// instead, so the barrier into this state has nothing to block.
|
||||||
return {vk::ImageLayout::ePresentSrcKHR,
|
return {vk::ImageLayout::ePresentSrcKHR,
|
||||||
vk::PipelineStageFlagBits::eBottomOfPipe, vk::AccessFlags{}};
|
vk::PipelineStageFlagBits2::eNone, vk::AccessFlags2{}};
|
||||||
}
|
}
|
||||||
|
|
||||||
log::fatal("Unknown Access value.");
|
log::fatal("Unknown Access value.");
|
||||||
@@ -83,22 +87,37 @@ bool needsBarrier(const AccessState& from, const AccessState& to) {
|
|||||||
return from.layout != to.layout || writesImage(from) || writesImage(to);
|
return from.layout != to.layout || writesImage(from) || writesImage(to);
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::ImageMemoryBarrier makeImageBarrier(vk::Image image,
|
vk::ImageMemoryBarrier2 makeImageBarrier(vk::Image image,
|
||||||
const vk::ImageSubresourceRange& range,
|
const vk::ImageSubresourceRange& range,
|
||||||
const AccessState& from,
|
const AccessState& from,
|
||||||
const AccessState& to) {
|
const AccessState& to) {
|
||||||
vk::ImageMemoryBarrier barrier{};
|
vk::ImageMemoryBarrier2 barrier{};
|
||||||
|
barrier.srcStageMask = from.stage;
|
||||||
|
barrier.dstStageMask = to.stage;
|
||||||
|
// Only writes need to be made available; reads just need the execution
|
||||||
|
// dependency the stage masks already give.
|
||||||
|
barrier.srcAccessMask = from.access & WRITE_ACCESSES;
|
||||||
|
barrier.dstAccessMask = to.access;
|
||||||
barrier.oldLayout = from.layout;
|
barrier.oldLayout = from.layout;
|
||||||
barrier.newLayout = to.layout;
|
barrier.newLayout = to.layout;
|
||||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
barrier.image = image;
|
barrier.image = image;
|
||||||
barrier.subresourceRange = range;
|
barrier.subresourceRange = range;
|
||||||
// Only writes need to be made available; reads just need the execution
|
|
||||||
// dependency the stage masks already give.
|
|
||||||
barrier.srcAccessMask = from.access & WRITE_ACCESSES;
|
|
||||||
barrier.dstAccessMask = to.access;
|
|
||||||
return barrier;
|
return barrier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void recordImageBarriers(
|
||||||
|
vk::CommandBuffer commandBuffer,
|
||||||
|
vk::ArrayProxy<const vk::ImageMemoryBarrier2> barriers) {
|
||||||
|
if (barriers.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::DependencyInfo dependency{};
|
||||||
|
dependency.imageMemoryBarrierCount = barriers.size();
|
||||||
|
dependency.pImageMemoryBarriers = barriers.data();
|
||||||
|
commandBuffer.pipelineBarrier2(dependency);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sophia
|
} // namespace sophia
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ enum class Access {
|
|||||||
*/
|
*/
|
||||||
struct AccessState {
|
struct AccessState {
|
||||||
vk::ImageLayout layout = vk::ImageLayout::eUndefined;
|
vk::ImageLayout layout = vk::ImageLayout::eUndefined;
|
||||||
vk::PipelineStageFlags stage = vk::PipelineStageFlagBits::eTopOfPipe;
|
vk::PipelineStageFlags2 stage = vk::PipelineStageFlagBits2::eNone;
|
||||||
vk::AccessFlags access;
|
vk::AccessFlags2 access;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,8 +35,8 @@ struct AccessState {
|
|||||||
* just-acquired state has to use this as its source stage, otherwise the
|
* just-acquired state has to use this as its source stage, otherwise the
|
||||||
* layout transition isn't ordered after the acquire.
|
* layout transition isn't ordered after the acquire.
|
||||||
*/
|
*/
|
||||||
inline constexpr vk::PipelineStageFlags SWAPCHAIN_ACQUIRE_STAGE =
|
inline constexpr vk::PipelineStageFlags2 SWAPCHAIN_ACQUIRE_STAGE =
|
||||||
vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
vk::PipelineStageFlagBits2::eColorAttachmentOutput;
|
||||||
|
|
||||||
AccessState getAccessState(Access access);
|
AccessState getAccessState(Access access);
|
||||||
|
|
||||||
@@ -55,9 +55,17 @@ bool writesImage(const AccessState& state);
|
|||||||
*/
|
*/
|
||||||
bool needsBarrier(const AccessState& from, const AccessState& to);
|
bool needsBarrier(const AccessState& from, const AccessState& to);
|
||||||
|
|
||||||
vk::ImageMemoryBarrier makeImageBarrier(vk::Image image,
|
/**
|
||||||
const vk::ImageSubresourceRange& range,
|
* A barrier carrying its own stage masks, for recording with
|
||||||
const AccessState& from,
|
* pipelineBarrier2. Barriers flushed together don't share stages.
|
||||||
const AccessState& to);
|
*/
|
||||||
|
vk::ImageMemoryBarrier2 makeImageBarrier(vk::Image image,
|
||||||
|
const vk::ImageSubresourceRange& range,
|
||||||
|
const AccessState& from,
|
||||||
|
const AccessState& to);
|
||||||
|
|
||||||
|
void recordImageBarriers(
|
||||||
|
vk::CommandBuffer commandBuffer,
|
||||||
|
vk::ArrayProxy<const vk::ImageMemoryBarrier2> barriers);
|
||||||
|
|
||||||
} // namespace sophia
|
} // namespace sophia
|
||||||
|
|||||||
@@ -56,10 +56,9 @@ void Image::transitionLayout(vk::CommandBuffer commandBuffer,
|
|||||||
AccessState to = getStateForLayout(newLayout);
|
AccessState to = getStateForLayout(newLayout);
|
||||||
|
|
||||||
if (needsBarrier(from, to)) {
|
if (needsBarrier(from, to)) {
|
||||||
vk::ImageMemoryBarrier barrier =
|
vk::ImageMemoryBarrier2 barrier =
|
||||||
makeImageBarrier(this->get(), this->getSubresourceRange(), from, to);
|
makeImageBarrier(this->get(), this->getSubresourceRange(), from, to);
|
||||||
commandBuffer.pipelineBarrier(from.stage, to.stage, {}, nullptr, nullptr,
|
recordImageBarriers(commandBuffer, barrier);
|
||||||
barrier);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->layout = newLayout;
|
this->layout = newLayout;
|
||||||
|
|||||||
Reference in New Issue
Block a user