Skip to content
Permalink
master
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
/* Copyright (c) 2017-2022 Hans-Kristian Arntzen
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <vector>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <string>
#include <functional>
#include "vulkan_headers.hpp"
#include "device.hpp"
#include "small_vector.hpp"
#include "stack_allocator.hpp"
#include "application_wsi_events.hpp"
#include "quirks.hpp"
#include "thread_group.hpp"
namespace Granite
{
class RenderGraph;
class RenderPass;
class TaskComposer;
// A more stateful variant of the lambdas.
// It can be somewhat awkward to marshal shared data between N different lambdas.
class RenderPassInterface : public Util::IntrusivePtrEnabled<RenderPassInterface,
std::default_delete<RenderPassInterface>,
Util::SingleThreadCounter>
{
public:
virtual ~RenderPassInterface() = default;
// This information must remain fixed.
virtual bool render_pass_is_conditional() const;
virtual bool render_pass_is_separate_layered() const;
// Can change per frame.
virtual bool need_render_pass() const;
virtual bool get_clear_depth_stencil(VkClearDepthStencilValue *value) const;
virtual bool get_clear_color(unsigned attachment, VkClearColorValue *value) const;
// Called once before bake().
virtual void setup_dependencies(RenderPass &self, RenderGraph &graph);
// Called once after bake().
virtual void setup(Vulkan::Device &device);
// Called every frame, useful for building dependent resources like custom views, etc.
virtual void enqueue_prepare_render_pass(RenderGraph &graph, TaskComposer &composer);
virtual void build_render_pass(Vulkan::CommandBuffer &cmd);
virtual void build_render_pass_separate_layer(Vulkan::CommandBuffer &cmd, unsigned layer);
};
using RenderPassInterfaceHandle = Util::IntrusivePtr<RenderPassInterface>;
// An interface which manages external synchronization.
// Used primarily by cluster shadow map rendering,
// since its resource management is highly specific
// and it makes more sense to use semaphores here,
// rather than try to hack it to fit it into a render graph node.
class RenderPassExternalLockInterface
{
public:
virtual ~RenderPassExternalLockInterface() = default;
virtual Vulkan::Semaphore external_acquire() = 0;
virtual void external_release(Vulkan::Semaphore semaphore) = 0;
};
enum SizeClass
{
Absolute,
SwapchainRelative,
InputRelative
};
enum RenderGraphQueueFlagBits
{
RENDER_GRAPH_QUEUE_GRAPHICS_BIT = 1 << 0,
RENDER_GRAPH_QUEUE_COMPUTE_BIT = 1 << 1,
RENDER_GRAPH_QUEUE_ASYNC_COMPUTE_BIT = 1 << 2,
RENDER_GRAPH_QUEUE_ASYNC_GRAPHICS_BIT = 1 << 3
};
using RenderGraphQueueFlags = uint32_t;
enum AttachmentInfoFlagBits
{
ATTACHMENT_INFO_PERSISTENT_BIT = 1 << 0,
ATTACHMENT_INFO_UNORM_SRGB_ALIAS_BIT = 1 << 1,
ATTACHMENT_INFO_SUPPORTS_PREROTATE_BIT = 1 << 2,
ATTACHMENT_INFO_MIPGEN_BIT = 1 << 3
};
enum AttachmentInfoInternalFlagBits
{
ATTACHMENT_INFO_INTERNAL_TRANSIENT_BIT = 1 << 16,
ATTACHMENT_INFO_INTERNAL_PROXY_BIT = 1 << 17
};
using AttachmentInfoFlags = uint32_t;
struct AttachmentInfo
{
SizeClass size_class = SizeClass::SwapchainRelative;
float size_x = 1.0f;
float size_y = 1.0f;
float size_z = 0.0f;
VkFormat format = VK_FORMAT_UNDEFINED;
std::string size_relative_name;
unsigned samples = 1;
unsigned levels = 1;
unsigned layers = 1;
VkImageUsageFlags aux_usage = 0;
AttachmentInfoFlags flags = ATTACHMENT_INFO_PERSISTENT_BIT;
};
struct BufferInfo
{
VkDeviceSize size = 0;
VkBufferUsageFlags usage = 0;
AttachmentInfoFlags flags = ATTACHMENT_INFO_PERSISTENT_BIT;
bool operator==(const BufferInfo &other) const
{
return size == other.size &&
usage == other.usage &&
flags == other.flags;
}
bool operator!=(const BufferInfo &other) const
{
return !(*this == other);
}
};
struct ResourceDimensions
{
VkFormat format = VK_FORMAT_UNDEFINED;
BufferInfo buffer_info;
unsigned width = 0;
unsigned height = 0;
unsigned depth = 1;
unsigned layers = 1;
unsigned levels = 1;
unsigned samples = 1;
AttachmentInfoFlags flags = ATTACHMENT_INFO_PERSISTENT_BIT;
VkSurfaceTransformFlagBitsKHR transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
RenderGraphQueueFlags queues = 0;
VkImageUsageFlags image_usage = 0;
bool operator==(const ResourceDimensions &other) const
{
return format == other.format &&
width == other.width &&
height == other.height &&
depth == other.depth &&
layers == other.layers &&
levels == other.levels &&
buffer_info == other.buffer_info &&
flags == other.flags &&
transform == other.transform;
// image_usage is deliberately not part of this test.
// queues is deliberately not part of this test.
}
bool operator!=(const ResourceDimensions &other) const
{
return !(*this == other);
}
bool uses_semaphore() const
{
if ((flags & ATTACHMENT_INFO_INTERNAL_PROXY_BIT) != 0)
return true;
// If more than one queue is used for a resource, we need to use semaphores.
auto physical_queues = queues;
// Regular compute uses regular graphics queue.
if (physical_queues & RENDER_GRAPH_QUEUE_COMPUTE_BIT)
physical_queues |= RENDER_GRAPH_QUEUE_GRAPHICS_BIT;
physical_queues &= ~RENDER_GRAPH_QUEUE_COMPUTE_BIT;
return (physical_queues & (physical_queues - 1)) != 0;
}
bool is_storage_image() const
{
return (image_usage & VK_IMAGE_USAGE_STORAGE_BIT) != 0;
}
bool is_buffer_like() const
{
return is_storage_image() || (buffer_info.size != 0) || (flags & ATTACHMENT_INFO_INTERNAL_PROXY_BIT) != 0;
}
std::string name;
};
class RenderResource
{
public:
enum class Type
{
Buffer,
Texture,
Proxy
};
enum { Unused = ~0u };
RenderResource(Type type_, unsigned index_)
: resource_type(type_), index(index_)
{
}
virtual ~RenderResource() = default;
Type get_type() const
{
return resource_type;
}
void written_in_pass(unsigned index_)
{
written_in_passes.insert(index_);
}
void read_in_pass(unsigned index_)
{
read_in_passes.insert(index_);
}
const std::unordered_set<unsigned> &get_read_passes() const
{
return read_in_passes;
}
const std::unordered_set<unsigned> &get_write_passes() const
{
return written_in_passes;
}
std::unordered_set<unsigned> &get_read_passes()
{
return read_in_passes;
}
std::unordered_set<unsigned> &get_write_passes()
{
return written_in_passes;
}
unsigned get_index() const
{
return index;
}
void set_physical_index(unsigned index_)
{
physical_index = index_;
}
unsigned get_physical_index() const
{
return physical_index;
}
void set_name(const std::string &name_)
{
name = name_;
}
const std::string &get_name() const
{
return name;
}
void add_queue(RenderGraphQueueFlagBits queue)
{
used_queues |= queue;
}
RenderGraphQueueFlags get_used_queues() const
{
return used_queues;
}
private:
Type resource_type;
unsigned index;
unsigned physical_index = Unused;
std::unordered_set<unsigned> written_in_passes;
std::unordered_set<unsigned> read_in_passes;
std::string name;
RenderGraphQueueFlags used_queues = 0;
};
class RenderBufferResource : public RenderResource
{
public:
explicit RenderBufferResource(unsigned index_)
: RenderResource(RenderResource::Type::Buffer, index_)
{
}
void set_buffer_info(const BufferInfo &info_)
{
info = info_;
}
const BufferInfo &get_buffer_info() const
{
return info;
}
void add_buffer_usage(VkBufferUsageFlags flags)
{
buffer_usage |= flags;
}
VkBufferUsageFlags get_buffer_usage() const
{
return buffer_usage;
}
private:
BufferInfo info;
VkBufferUsageFlags buffer_usage = 0;
};
class RenderTextureResource : public RenderResource
{
public:
explicit RenderTextureResource(unsigned index_)
: RenderResource(RenderResource::Type::Texture, index_)
{
}
void set_attachment_info(const AttachmentInfo &info_)
{
info = info_;
}
const AttachmentInfo &get_attachment_info() const
{
return info;
}
AttachmentInfo &get_attachment_info()
{
return info;
}
void set_transient_state(bool enable)
{
transient = enable;
}
bool get_transient_state() const
{
return transient;
}
void add_image_usage(VkImageUsageFlags flags)
{
image_usage |= flags;
}
VkImageUsageFlags get_image_usage() const
{
return image_usage;
}
private:
AttachmentInfo info;
VkImageUsageFlags image_usage = 0;
bool transient = false;
};
class RenderPass
{
public:
RenderPass(RenderGraph &graph_, unsigned index_, RenderGraphQueueFlagBits queue_)
: graph(graph_), index(index_), queue(queue_)
{
}
enum { Unused = ~0u };
struct AccessedResource
{
VkPipelineStageFlags stages = 0;
VkAccessFlags access = 0;
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
};
struct AccessedTextureResource : AccessedResource
{
RenderTextureResource *texture = nullptr;
};
struct AccessedBufferResource : AccessedResource
{
RenderBufferResource *buffer = nullptr;
};
struct AccessedProxyResource : AccessedResource
{
RenderResource *proxy = nullptr;
};
struct AccessedExternalLockInterface
{
RenderPassExternalLockInterface *iface;
VkPipelineStageFlags stages;
};
RenderGraphQueueFlagBits get_queue() const
{
return queue;
}
RenderGraph &get_graph()
{
return graph;
}
unsigned get_index() const
{
return index;
}
void add_external_lock(const std::string &name, VkPipelineStageFlags stages);
RenderTextureResource &set_depth_stencil_input(const std::string &name);
RenderTextureResource &set_depth_stencil_output(const std::string &name, const AttachmentInfo &info);
RenderTextureResource &add_color_output(const std::string &name, const AttachmentInfo &info, const std::string &input = "");
RenderTextureResource &add_resolve_output(const std::string &name, const AttachmentInfo &info);
RenderTextureResource &add_attachment_input(const std::string &name);
RenderTextureResource &add_history_input(const std::string &name);
RenderTextureResource &add_texture_input(const std::string &name,
VkPipelineStageFlags stages = 0);
RenderTextureResource &add_blit_texture_read_only_input(const std::string &name);
RenderBufferResource &add_uniform_input(const std::string &name,
VkPipelineStageFlags stages = 0);
RenderBufferResource &add_storage_read_only_input(const std::string &name,
VkPipelineStageFlags stages = 0);
RenderBufferResource &add_storage_output(const std::string &name, const BufferInfo &info, const std::string &input = "");
RenderBufferResource &add_transfer_output(const std::string &name, const BufferInfo &info);
RenderTextureResource &add_storage_texture_output(const std::string &name, const AttachmentInfo &info, const std::string &input = "");
RenderTextureResource &add_blit_texture_output(const std::string &name, const AttachmentInfo &info, const std::string &input = "");
RenderBufferResource &add_vertex_buffer_input(const std::string &name);
RenderBufferResource &add_index_buffer_input(const std::string &name);
RenderBufferResource &add_indirect_buffer_input(const std::string &name);
void add_proxy_output(const std::string &name, VkPipelineStageFlags stages);
void add_proxy_input(const std::string &name, VkPipelineStageFlags stages);
void add_fake_resource_write_alias(const std::string &from, const std::string &to);
void make_color_input_scaled(unsigned index_)
{
std::swap(color_scale_inputs[index_], color_inputs[index_]);
}
const std::vector<RenderTextureResource *> &get_color_outputs() const
{
return color_outputs;
}
const std::vector<RenderTextureResource *> &get_resolve_outputs() const
{
return resolve_outputs;
}
const std::vector<RenderTextureResource *> &get_color_inputs() const
{
return color_inputs;
}
const std::vector<RenderTextureResource *> &get_color_scale_inputs() const
{
return color_scale_inputs;
}
const std::vector<RenderTextureResource *> &get_storage_texture_outputs() const
{
return storage_texture_outputs;
}
const std::vector<RenderTextureResource *> &get_storage_texture_inputs() const
{
return storage_texture_inputs;
}
const std::vector<RenderTextureResource *> &get_blit_texture_inputs() const
{
return blit_texture_inputs;
}
const std::vector<RenderTextureResource *> &get_blit_texture_outputs() const
{
return blit_texture_outputs;
}
const std::vector<RenderTextureResource *> &get_attachment_inputs() const
{
return attachments_inputs;
}
const std::vector<RenderTextureResource *> &get_history_inputs() const
{
return history_inputs;
}
const std::vector<RenderBufferResource *> &get_storage_inputs() const
{
return storage_inputs;
}
const std::vector<RenderBufferResource *> &get_storage_outputs() const
{
return storage_outputs;
}
const std::vector<RenderBufferResource *> &get_transfer_outputs() const
{
return transfer_outputs;
}
const std::vector<AccessedTextureResource> &get_generic_texture_inputs() const
{
return generic_texture;
}
const std::vector<AccessedBufferResource> &get_generic_buffer_inputs() const
{
return generic_buffer;
}
const std::vector<AccessedProxyResource> &get_proxy_inputs() const
{
return proxy_inputs;
}
const std::vector<AccessedProxyResource> &get_proxy_outputs() const
{
return proxy_outputs;
}
const std::vector<std::pair<RenderTextureResource *, RenderTextureResource *>> &get_fake_resource_aliases() const
{
return fake_resource_alias;
}
RenderTextureResource *get_depth_stencil_input() const
{
return depth_stencil_input;
}
RenderTextureResource *get_depth_stencil_output() const
{
return depth_stencil_output;
}
unsigned get_physical_pass_index() const
{
return physical_pass;
}
void set_physical_pass_index(unsigned index_)
{
physical_pass = index_;
}
bool need_render_pass() const
{
if (render_pass_handle)
return render_pass_handle->need_render_pass();
else
return true;
}
bool render_pass_is_multiview() const
{
if (render_pass_handle)
return !render_pass_handle->render_pass_is_separate_layered();
else
return true;
}
bool may_not_need_render_pass() const
{
if (render_pass_handle)
return render_pass_handle->render_pass_is_conditional();
else
return false;
}
bool get_clear_color(unsigned index_, VkClearColorValue *value = nullptr) const
{
if (render_pass_handle)
return render_pass_handle->get_clear_color(index_, value);
else if (get_clear_color_cb)
return get_clear_color_cb(index_, value);
else
return false;
}
bool get_clear_depth_stencil(VkClearDepthStencilValue *value = nullptr) const
{
if (render_pass_handle)
return render_pass_handle->get_clear_depth_stencil(value);
else if (get_clear_depth_stencil_cb)
return get_clear_depth_stencil_cb(value);
else
return false;
}
void prepare_render_pass(TaskComposer &composer)
{
if (render_pass_handle)
render_pass_handle->enqueue_prepare_render_pass(graph, composer);
}
void setup(Vulkan::Device &device)
{
if (render_pass_handle)
render_pass_handle->setup(device);
}
void setup_dependencies()
{
if (render_pass_handle)
render_pass_handle->setup_dependencies(*this, graph);
}
void build_render_pass(Vulkan::CommandBuffer &cmd, unsigned layer)
{
if (render_pass_handle)
{
if (render_pass_handle->render_pass_is_separate_layered())
render_pass_handle->build_render_pass_separate_layer(cmd, layer);
else
render_pass_handle->build_render_pass(cmd);
}
else if (build_render_pass_cb)
build_render_pass_cb(cmd);
}
void set_render_pass_interface(RenderPassInterfaceHandle handle)
{
render_pass_handle = std::move(handle);
}
void set_build_render_pass(std::function<void (Vulkan::CommandBuffer &)> func)
{
build_render_pass_cb = std::move(func);
}
void set_get_clear_depth_stencil(std::function<bool (VkClearDepthStencilValue *)> func)
{
get_clear_depth_stencil_cb = std::move(func);
}
void set_get_clear_color(std::function<bool (unsigned, VkClearColorValue *)> func)
{
get_clear_color_cb = std::move(func);
}
void set_name(const std::string &name)
{
pass_name = name;
}
const std::string &get_name() const
{
return pass_name;
}
const std::vector<AccessedExternalLockInterface> &get_lock_interfaces() const
{
return lock_interfaces;
}
private:
RenderGraph &graph;
unsigned index;
unsigned physical_pass = Unused;
RenderGraphQueueFlagBits queue;
RenderPassInterfaceHandle render_pass_handle;
std::function<void (Vulkan::CommandBuffer &)> build_render_pass_cb;
std::function<bool (VkClearDepthStencilValue *)> get_clear_depth_stencil_cb;
std::function<bool (unsigned, VkClearColorValue *)> get_clear_color_cb;
std::vector<RenderTextureResource *> color_outputs;
std::vector<RenderTextureResource *> resolve_outputs;
std::vector<RenderTextureResource *> color_inputs;
std::vector<RenderTextureResource *> color_scale_inputs;
std::vector<RenderTextureResource *> storage_texture_inputs;
std::vector<RenderTextureResource *> storage_texture_outputs;
std::vector<RenderTextureResource *> blit_texture_inputs;
std::vector<RenderTextureResource *> blit_texture_outputs;
std::vector<RenderTextureResource *> attachments_inputs;
std::vector<RenderTextureResource *> history_inputs;
std::vector<RenderBufferResource *> storage_outputs;
std::vector<RenderBufferResource *> storage_inputs;
std::vector<RenderBufferResource *> transfer_outputs;
std::vector<AccessedTextureResource> generic_texture;
std::vector<AccessedBufferResource> generic_buffer;
std::vector<AccessedProxyResource> proxy_inputs;
std::vector<AccessedProxyResource> proxy_outputs;
std::vector<AccessedExternalLockInterface> lock_interfaces;
RenderTextureResource *depth_stencil_input = nullptr;
RenderTextureResource *depth_stencil_output = nullptr;
std::vector<std::pair<RenderTextureResource *, RenderTextureResource *>> fake_resource_alias;
std::string pass_name;
RenderBufferResource &add_generic_buffer_input(const std::string &name,
VkPipelineStageFlags stages,
VkAccessFlags access,
VkBufferUsageFlags usage);
};
class RenderGraph : public Vulkan::NoCopyNoMove, public EventHandler
{
public:
RenderGraph();
void set_device(Vulkan::Device *device_)
{
device = device_;
}
Vulkan::Device &get_device()
{
assert(device);
return *device;
}
void add_external_lock_interface(const std::string &name, RenderPassExternalLockInterface *iface);
RenderPassExternalLockInterface *find_external_lock_interface(const std::string &name) const;
RenderPass &add_pass(const std::string &name, RenderGraphQueueFlagBits queue);
RenderPass *find_pass(const std::string &name);
void set_backbuffer_source(const std::string &name);
void set_backbuffer_dimensions(const ResourceDimensions &dim)
{
swapchain_dimensions = dim;
}
const ResourceDimensions &get_backbuffer_dimensions() const
{
return swapchain_dimensions;
}
ResourceDimensions get_resource_dimensions(const RenderBufferResource &resource) const;
ResourceDimensions get_resource_dimensions(const RenderTextureResource &resource) const;
void enable_timestamps(bool enable);
void bake();
void reset();
void log();
void setup_attachments(Vulkan::Device &device, Vulkan::ImageView *swapchain);
void enqueue_render_passes(Vulkan::Device &device, TaskComposer &composer);
RenderTextureResource &get_texture_resource(const std::string &name);
RenderBufferResource &get_buffer_resource(const std::string &name);
RenderResource &get_proxy_resource(const std::string &name);
Vulkan::ImageView &get_physical_texture_resource(unsigned index)
{
assert(index != RenderResource::Unused);
assert(physical_attachments[index]);
return *physical_attachments[index];
}
Vulkan::ImageView *get_physical_history_texture_resource(unsigned index)
{
assert(index != RenderResource::Unused);
if (!physical_history_image_attachments[index])
return nullptr;
return &physical_history_image_attachments[index]->get_view();
}
Vulkan::Buffer &get_physical_buffer_resource(unsigned index)
{
assert(index != RenderResource::Unused);
assert(physical_buffers[index]);
return *physical_buffers[index];
}
Vulkan::ImageView &get_physical_texture_resource(const RenderTextureResource &resource)
{
assert(resource.get_physical_index() != RenderResource::Unused);
return get_physical_texture_resource(resource.get_physical_index());
}
Vulkan::ImageView *maybe_get_physical_texture_resource(RenderTextureResource *resource)
{
if (resource && resource->get_physical_index() != RenderResource::Unused)
return &get_physical_texture_resource(*resource);
else
return nullptr;
}
Vulkan::ImageView *get_physical_history_texture_resource(const RenderTextureResource &resource)
{
return get_physical_history_texture_resource(resource.get_physical_index());
}
Vulkan::Buffer &get_physical_buffer_resource(const RenderBufferResource &resource)
{
assert(resource.get_physical_index() != RenderResource::Unused);
return get_physical_buffer_resource(resource.get_physical_index());
}
Vulkan::Buffer *maybe_get_physical_buffer_resource(RenderBufferResource *resource)
{
if (resource && resource->get_physical_index() != RenderResource::Unused)
return &get_physical_buffer_resource(*resource);
else
return nullptr;
}
// For keeping feed-back resources alive during rebaking.
Vulkan::BufferHandle consume_persistent_physical_buffer_resource(unsigned index) const;
void install_persistent_physical_buffer_resource(unsigned index, Vulkan::BufferHandle buffer);
// Utility to consume all physical buffer handles and install them.
std::vector<Vulkan::BufferHandle> consume_physical_buffers() const;
void install_physical_buffers(std::vector<Vulkan::BufferHandle> buffers);
static inline RenderGraphQueueFlagBits get_default_post_graphics_queue()
{
if (Vulkan::ImplementationQuirks::get().use_async_compute_post &&
!Vulkan::ImplementationQuirks::get().render_graph_force_single_queue)
{
return RENDER_GRAPH_QUEUE_ASYNC_GRAPHICS_BIT;
}
else
{
return RENDER_GRAPH_QUEUE_GRAPHICS_BIT;
}
}
static inline RenderGraphQueueFlagBits get_default_compute_queue()
{
if (Vulkan::ImplementationQuirks::get().render_graph_force_single_queue)
return RENDER_GRAPH_QUEUE_COMPUTE_BIT;
else
return RENDER_GRAPH_QUEUE_ASYNC_COMPUTE_BIT;
}
private:
Vulkan::Device *device = nullptr;
std::vector<std::unique_ptr<RenderPass>> passes;
std::vector<std::unique_ptr<RenderResource>> resources;
std::unordered_map<std::string, unsigned> pass_to_index;
std::unordered_map<std::string, unsigned> resource_to_index;
std::unordered_map<std::string, RenderPassExternalLockInterface *> external_lock_interfaces;
std::string backbuffer_source;
std::vector<unsigned> pass_stack;
struct Barrier
{
unsigned resource_index;
VkImageLayout layout;
VkAccessFlags access;
VkPipelineStageFlags stages;
bool history;
};
struct Barriers
{
std::vector<Barrier> invalidate;
std::vector<Barrier> flush;
};
std::vector<Barriers> pass_barriers;
void filter_passes(std::vector<unsigned> &list);
void validate_passes();
void build_barriers();
ResourceDimensions swapchain_dimensions;
struct ColorClearRequest
{
RenderPass *pass;
VkClearColorValue *target;
unsigned index;
};
struct DepthClearRequest
{
RenderPass *pass;
VkClearDepthStencilValue *target;
};
struct ScaledClearRequests
{
unsigned target;
unsigned physical_resource;
};
struct MipmapRequests
{
unsigned physical_resource;
VkPipelineStageFlags stages;
VkAccessFlags access;
VkImageLayout layout;
};
struct PhysicalPass
{
std::vector<unsigned> passes;
std::vector<unsigned> discards;
std::vector<Barrier> invalidate;
std::vector<Barrier> flush;
std::vector<Barrier> history;
std::vector<std::pair<unsigned, unsigned>> alias_transfer;
Vulkan::RenderPassInfo render_pass_info;
std::vector<Vulkan::RenderPassInfo::Subpass> subpasses;
std::vector<unsigned> physical_color_attachments;
unsigned physical_depth_stencil_attachment = RenderResource::Unused;
std::vector<ColorClearRequest> color_clear_requests;
DepthClearRequest depth_clear_request;
std::vector<std::vector<ScaledClearRequests>> scaled_clear_requests;
std::vector<MipmapRequests> mipmap_requests;
unsigned layers = 1;
};
std::vector<PhysicalPass> physical_passes;
void build_physical_passes();
void build_transients();
void build_physical_resources();
void build_physical_barriers();
void build_render_pass_info();
void build_aliases();
bool enabled_timestamps = false;
std::vector<ResourceDimensions> physical_dimensions;
std::vector<Vulkan::ImageView *> physical_attachments;
std::vector<Vulkan::BufferHandle> physical_buffers;
std::vector<Vulkan::ImageHandle> physical_image_attachments;
std::vector<Vulkan::ImageHandle> physical_history_image_attachments;
struct PipelineEvent
{
VkPipelineStageFlags pipeline_barrier_src_stages = 0;
// Need two separate semaphores so we can wait in both queues independently.
// Waiting for a semaphore resets it.
Vulkan::Semaphore wait_graphics_semaphore;
Vulkan::Semaphore wait_compute_semaphore;
// Stages to wait for are stored inside the events.
VkAccessFlags to_flush_access = 0;
VkAccessFlags invalidated_in_stage[32] = {};
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
};
std::vector<PipelineEvent> physical_events;
std::vector<PipelineEvent> physical_history_events;
std::vector<bool> physical_image_has_history;
std::vector<unsigned> physical_aliases;
Vulkan::ImageView *swapchain_attachment = nullptr;
unsigned swapchain_physical_index = RenderResource::Unused;
void enqueue_scaled_requests(Vulkan::CommandBuffer &cmd, const std::vector<ScaledClearRequests> &requests);
void enqueue_mipmap_requests(Vulkan::CommandBuffer &cmd, const std::vector<MipmapRequests> &requests);
void on_swapchain_changed(const Vulkan::SwapchainParameterEvent &e);
void on_swapchain_destroyed(const Vulkan::SwapchainParameterEvent &e);
void on_device_created(const Vulkan::DeviceCreatedEvent &e);
void on_device_destroyed(const Vulkan::DeviceCreatedEvent &e);
void setup_physical_buffer(Vulkan::Device &device, unsigned attachment);
void setup_physical_image(Vulkan::Device &device, unsigned attachment);
void depend_passes_recursive(const RenderPass &pass, const std::unordered_set<unsigned> &passes,
unsigned stack_count, bool no_check, bool ignore_self, bool merge_dependency);
void traverse_dependencies(const RenderPass &pass, unsigned stack_count);
std::vector<std::unordered_set<unsigned>> pass_dependencies;
std::vector<std::unordered_set<unsigned>> pass_merge_dependencies;
bool depends_on_pass(unsigned dst_pass, unsigned src_pass);
void reorder_passes(std::vector<unsigned> &passes);
static bool need_invalidate(const Barrier &barrier, const PipelineEvent &event);
struct PassSubmissionState
{
Util::SmallVector<VkBufferMemoryBarrier> buffer_barriers;
Util::SmallVector<VkImageMemoryBarrier> image_barriers;
// Immediate buffer barriers are useless because they don't need any layout transition,
// and the API guarantees that submitting a batch makes memory visible to GPU resources.
// Immediate image barriers are purely for doing layout transitions without waiting (srcStage = TOP_OF_PIPE).
Util::SmallVector<VkImageMemoryBarrier> immediate_image_barriers;
// Barriers which are used when waiting for a semaphore, and then doing a transition.
// We need to use pipeline barriers here so we can have srcStage = dstStage,
// and hand over while not breaking the pipeline.
Util::SmallVector<VkImageMemoryBarrier> semaphore_handover_barriers;
Util::SmallVector<VkSubpassContents> subpass_contents;
VkPipelineStageFlags post_pipeline_barrier_stages = 0;
VkPipelineStageFlags pre_dst_stages = 0;
VkPipelineStageFlags immediate_dst_stages = 0;
VkPipelineStageFlags pre_src_stages = 0;
VkPipelineStageFlags handover_stages = 0;
Util::SmallVector<Vulkan::Semaphore> wait_semaphores;
Util::SmallVector<VkPipelineStageFlags> wait_semaphore_stages;
Util::SmallVector<RenderPass::AccessedExternalLockInterface> external_locks;
Vulkan::Semaphore proxy_semaphores[2];
bool need_submission_semaphore = false;
Vulkan::CommandBufferHandle cmd;
Vulkan::CommandBuffer::Type queue_type = Vulkan::CommandBuffer::Type::Count;
bool graphics = false;
bool active = false;
TaskGroupHandle rendering_dependency;
void emit_pre_pass_barriers();
void submit();
};
std::vector<PassSubmissionState> pass_submission_state;
void enqueue_render_pass(Vulkan::Device &device, PhysicalPass &physical_pass, PassSubmissionState &state, TaskComposer &composer);
void enqueue_swapchain_scale_pass(Vulkan::Device &device);
bool physical_pass_requires_work(const PhysicalPass &pass) const;
void physical_pass_transfer_ownership(const PhysicalPass &pass);
void physical_pass_invalidate_attachments(const PhysicalPass &pass);
void physical_pass_enqueue_graphics_commands(const PhysicalPass &pass, PassSubmissionState &state);
void physical_pass_enqueue_compute_commands(const PhysicalPass &pass, PassSubmissionState &state);
void physical_pass_handle_invalidate_barrier(const Barrier &barrier, PassSubmissionState &state, bool physical_graphics_queue);
void physical_pass_handle_external_acquire(const PhysicalPass &pass, PassSubmissionState &state);
void physical_pass_handle_signal(Vulkan::Device &device, const PhysicalPass &pass, PassSubmissionState &state);
void physical_pass_handle_flush_barrier(const Barrier &barrier, PassSubmissionState &state);
void physical_pass_handle_cpu_timeline(Vulkan::Device &device, const PhysicalPass &pass, PassSubmissionState &state,
TaskComposer &composer);
void physical_pass_handle_gpu_timeline(ThreadGroup &group, Vulkan::Device &device,
const PhysicalPass &pass, PassSubmissionState &state);
};
}