Add Chromium-only Blender WebEngine parity work
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKBeginQueryData {
|
||||
VkQueryPool vk_query_pool;
|
||||
uint32_t query_index;
|
||||
VkQueryControlFlags vk_query_control_flags;
|
||||
};
|
||||
|
||||
/**
|
||||
* Begin query
|
||||
*
|
||||
* - Contains logic to copy relevant data to the VKRenderGraphNode.
|
||||
* - Determine read/write resource dependencies.
|
||||
* - Add commands to a command builder.
|
||||
*/
|
||||
class VKBeginQueryNode : public VKNodeInfo<VKNodeType::BEGIN_QUERY,
|
||||
VKBeginQueryData,
|
||||
VKBeginQueryData,
|
||||
VK_PIPELINE_STAGE_NONE,
|
||||
VKResourceType::NONE> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
void set_node_data(Node &node, Storage & /* storage */, const CreateInfo &create_info)
|
||||
{
|
||||
node.begin_query = create_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker & /*resources*/,
|
||||
VKRenderGraphLinks & /*links*/,
|
||||
const CreateInfo & /*create_info*/) override
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> /*storage_push_constants*/,
|
||||
VKBoundPipelines & /*r_bound_pipelines*/) override
|
||||
{
|
||||
command_buffer.begin_query(data.vk_query_pool, data.query_index, data.vk_query_control_flags);
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,141 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "render_graph/vk_resource_access_info.hh"
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKBeginRenderingData {
|
||||
VkRenderingAttachmentInfo color_attachments[8];
|
||||
VkRenderingAttachmentInfo depth_attachment;
|
||||
VkRenderingAttachmentInfo stencil_attachment;
|
||||
VkRenderingInfoKHR vk_rendering_info;
|
||||
};
|
||||
|
||||
struct VKBeginRenderingCreateInfo {
|
||||
VKBeginRenderingData node_data = {};
|
||||
const VKResourceAccessInfo &resources;
|
||||
VKBeginRenderingCreateInfo(const VKResourceAccessInfo &resources) : resources(resources) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Begin rendering node
|
||||
*
|
||||
* - Contains logic to copy relevant data to the VKRenderGraphNode.
|
||||
* - Determine read/write resource dependencies.
|
||||
* - Add commands to a command builder.
|
||||
*/
|
||||
class VKBeginRenderingNode : public VKNodeInfo<VKNodeType::BEGIN_RENDERING,
|
||||
VKBeginRenderingCreateInfo,
|
||||
VKBeginRenderingData,
|
||||
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
|
||||
VKResourceType::IMAGE> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info)
|
||||
{
|
||||
BLI_assert_msg(ELEM(create_info.node_data.vk_rendering_info.pColorAttachments,
|
||||
nullptr,
|
||||
create_info.node_data.color_attachments),
|
||||
"When create_info.node_data.vk_rendering_info.pColorAttachments points to "
|
||||
"something, it should point to create_info.node_data.color_attachments.");
|
||||
BLI_assert_msg(ELEM(create_info.node_data.vk_rendering_info.pDepthAttachment,
|
||||
nullptr,
|
||||
&create_info.node_data.depth_attachment),
|
||||
"When create_info.node_data.vk_rendering_info.pDepthAttachment points to "
|
||||
"something, it should point to create_info.node_data.depth_attachment.");
|
||||
BLI_assert_msg(ELEM(create_info.node_data.vk_rendering_info.pStencilAttachment,
|
||||
nullptr,
|
||||
&create_info.node_data.stencil_attachment),
|
||||
"When create_info.node_data.vk_rendering_info.pStencilAttachment points to "
|
||||
"something, it should point to create_info.node_data.stencil_attachment.");
|
||||
node.storage_index = storage.begin_rendering.append_and_get_index(create_info.node_data);
|
||||
/* NOTE: pointers in vk_rendering_info will be set to the correct location just before sending
|
||||
* to the command buffer. In the meantime these pointers are invalid.
|
||||
* VKRenderingAttachmentInfo's should be used instead. */
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const CreateInfo &create_info) override
|
||||
{
|
||||
create_info.resources.build_links(resources, links);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> /*storage_push_constants*/,
|
||||
VKBoundPipelines & /*r_bound_pipelines*/) override
|
||||
{
|
||||
/* Localize pointers just before sending to the command buffer. Pointer can (and will) change
|
||||
* as they are stored in a union which is stored in a vector. When the vector reallocates,
|
||||
* the pointers will become invalid. */
|
||||
if (data.vk_rendering_info.pColorAttachments) {
|
||||
data.vk_rendering_info.pColorAttachments = data.color_attachments;
|
||||
}
|
||||
if (data.vk_rendering_info.pDepthAttachment) {
|
||||
data.vk_rendering_info.pDepthAttachment = &data.depth_attachment;
|
||||
}
|
||||
if (data.vk_rendering_info.pStencilAttachment) {
|
||||
data.vk_rendering_info.pStencilAttachment = &data.stencil_attachment;
|
||||
}
|
||||
command_buffer.begin_rendering(&data.vk_rendering_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconfigure the vk_rendering_info to be restarted.
|
||||
*
|
||||
* When a render scope is restarted the clear/load ops needs to load in the previous stored
|
||||
* results.
|
||||
*/
|
||||
static void reconfigure_for_restart(VKBeginRenderingData &begin_rendering_data)
|
||||
{
|
||||
auto reconfigure_attachment = [](VkRenderingAttachmentInfo &rendering_attachment) {
|
||||
if (ELEM(rendering_attachment.loadOp,
|
||||
VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||
VK_ATTACHMENT_LOAD_OP_DONT_CARE))
|
||||
{
|
||||
rendering_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
}
|
||||
};
|
||||
|
||||
if (begin_rendering_data.vk_rendering_info.pStencilAttachment != nullptr) {
|
||||
reconfigure_attachment(begin_rendering_data.stencil_attachment);
|
||||
}
|
||||
if (begin_rendering_data.vk_rendering_info.pDepthAttachment != nullptr) {
|
||||
reconfigure_attachment(begin_rendering_data.depth_attachment);
|
||||
}
|
||||
for (VkRenderingAttachmentInfo &color_attachment : MutableSpan<VkRenderingAttachmentInfo>(
|
||||
begin_rendering_data.color_attachments,
|
||||
begin_rendering_data.vk_rendering_info.colorAttachmentCount))
|
||||
{
|
||||
reconfigure_attachment(color_attachment);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,85 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKBlitImageData {
|
||||
VkImage src_image;
|
||||
VkImage dst_image;
|
||||
VkImageBlit region;
|
||||
VkFilter filter;
|
||||
};
|
||||
|
||||
/**
|
||||
* Blit Image Node
|
||||
*
|
||||
* - Contains logic to copy relevant data to the VKRenderGraphNode.
|
||||
* - Determine read/write resource dependencies.
|
||||
* - Add commands to a command builder.
|
||||
*/
|
||||
class VKBlitImageNode : public VKNodeInfo<VKNodeType::BLIT_IMAGE,
|
||||
VKBlitImageData,
|
||||
VKBlitImageData,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VKResourceType::IMAGE> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info)
|
||||
{
|
||||
node.storage_index = storage.blit_image.append_and_get_index(create_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const CreateInfo &create_info) override
|
||||
{
|
||||
ResourceWithStamp src_resource = resources.get_image(create_info.src_image);
|
||||
ResourceWithStamp dst_resource = resources.get_image_and_increase_stamp(create_info.dst_image);
|
||||
links.images.append({{src_resource, VK_ACCESS_TRANSFER_READ_BIT},
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT});
|
||||
links.images.append({{dst_resource, VK_ACCESS_TRANSFER_WRITE_BIT},
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> /*storage_push_constants*/,
|
||||
VKBoundPipelines & /*r_bound_pipelines*/) override
|
||||
{
|
||||
command_buffer.blit_image(data.src_image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
data.dst_image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1,
|
||||
&data.region,
|
||||
data.filter);
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,67 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "render_graph/vk_resource_access_info.hh"
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKClearAttachmentsData {
|
||||
uint32_t attachment_count;
|
||||
VkClearAttachment attachments[8];
|
||||
VkClearRect vk_clear_rect;
|
||||
};
|
||||
|
||||
class VKClearAttachmentsNode : public VKNodeInfo<VKNodeType::CLEAR_ATTACHMENTS,
|
||||
VKClearAttachmentsData,
|
||||
VKClearAttachmentsData,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
||||
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
||||
VKResourceType::IMAGE> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info)
|
||||
{
|
||||
node.storage_index = storage.clear_attachments.append_and_get_index(create_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker & /*resources*/,
|
||||
VKRenderGraphLinks & /*links*/,
|
||||
const CreateInfo & /*create_info*/) override
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> /*storage_push_constants*/,
|
||||
VKBoundPipelines & /*r_bound_pipelines*/) override
|
||||
{
|
||||
command_buffer.clear_attachments(
|
||||
data.attachment_count, data.attachments, 1, &data.vk_clear_rect);
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,71 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKClearColorImageData {
|
||||
VkImage vk_image;
|
||||
VkClearColorValue vk_clear_color_value;
|
||||
VkImageSubresourceRange vk_image_subresource_range;
|
||||
};
|
||||
|
||||
class VKClearColorImageNode : public VKNodeInfo<VKNodeType::CLEAR_COLOR_IMAGE,
|
||||
VKClearColorImageData,
|
||||
VKClearColorImageData,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VKResourceType::IMAGE> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
void set_node_data(Node &node, Storage & /* storage */, const CreateInfo &create_info)
|
||||
{
|
||||
node.clear_color_image = create_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const CreateInfo &create_info) override
|
||||
{
|
||||
ResourceWithStamp resource = resources.get_image_and_increase_stamp(create_info.vk_image);
|
||||
links.images.append({{resource, VK_ACCESS_TRANSFER_WRITE_BIT},
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> /*storage_push_constants*/,
|
||||
VKBoundPipelines & /*r_bound_pipelines*/) override
|
||||
{
|
||||
command_buffer.clear_color_image(data.vk_image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
&data.vk_clear_color_value,
|
||||
1,
|
||||
&data.vk_image_subresource_range);
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,83 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKClearDepthStencilImageData {
|
||||
VkImage vk_image;
|
||||
VkClearDepthStencilValue vk_clear_depth_stencil_value;
|
||||
VkImageSubresourceRange vk_image_subresource_range;
|
||||
};
|
||||
|
||||
struct VKClearDepthStencilImageCreateInfo {
|
||||
VKClearDepthStencilImageData node_data;
|
||||
|
||||
/**
|
||||
* All image aspects of the image that will be cleared.
|
||||
*
|
||||
* Used during the pipeline barriers as the full image aspect needs to be known for changing the
|
||||
* layout. Even when only one aspect is eventually cleared. */
|
||||
VkImageAspectFlags vk_image_aspects;
|
||||
};
|
||||
|
||||
class VKClearDepthStencilImageNode : public VKNodeInfo<VKNodeType::CLEAR_DEPTH_STENCIL_IMAGE,
|
||||
VKClearDepthStencilImageCreateInfo,
|
||||
VKClearDepthStencilImageData,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VKResourceType::IMAGE> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
static void set_node_data(Node &node, Storage & /* storage */, const CreateInfo &create_info)
|
||||
{
|
||||
node.clear_depth_stencil_image = create_info.node_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const CreateInfo &create_info) override
|
||||
{
|
||||
ResourceWithStamp resource = resources.get_image_and_increase_stamp(
|
||||
create_info.node_data.vk_image);
|
||||
links.images.append({{resource, VK_ACCESS_TRANSFER_WRITE_BIT},
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
create_info.vk_image_aspects});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> /*storage_push_constants*/,
|
||||
VKBoundPipelines & /*r_bound_pipelines*/) override
|
||||
{
|
||||
command_buffer.clear_depth_stencil_image(data.vk_image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
&data.vk_clear_depth_stencil_value,
|
||||
1,
|
||||
&data.vk_image_subresource_range);
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,67 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKCopyBufferData {
|
||||
VkBuffer src_buffer;
|
||||
VkBuffer dst_buffer;
|
||||
VkBufferCopy region;
|
||||
};
|
||||
|
||||
class VKCopyBufferNode : public VKNodeInfo<VKNodeType::COPY_BUFFER,
|
||||
VKCopyBufferData,
|
||||
VKCopyBufferData,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VKResourceType::BUFFER> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
static void set_node_data(Node &node, Storage & /* storage */, const CreateInfo &create_info)
|
||||
{
|
||||
node.copy_buffer = create_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const CreateInfo &create_info) override
|
||||
{
|
||||
ResourceWithStamp src_resource = resources.get_buffer(create_info.src_buffer);
|
||||
ResourceWithStamp dst_resource = resources.get_buffer_and_increase_stamp(
|
||||
create_info.dst_buffer);
|
||||
links.buffers.append({src_resource, VK_ACCESS_TRANSFER_READ_BIT});
|
||||
links.buffers.append({dst_resource, VK_ACCESS_TRANSFER_WRITE_BIT});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> /*storage_push_constants*/,
|
||||
VKBoundPipelines & /*r_bound_pipelines*/) override
|
||||
{
|
||||
command_buffer.copy_buffer(data.src_buffer, data.dst_buffer, 1, &data.region);
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,75 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKCopyBufferToImageData {
|
||||
VkBuffer src_buffer;
|
||||
VkImage dst_image;
|
||||
VkBufferImageCopy region;
|
||||
};
|
||||
|
||||
struct VKCopyBufferToImageCreateInfo {
|
||||
VKCopyBufferToImageData node_data;
|
||||
VkImageAspectFlags vk_image_aspects;
|
||||
};
|
||||
|
||||
class VKCopyBufferToImageNode : public VKNodeInfo<VKNodeType::COPY_BUFFER_TO_IMAGE,
|
||||
VKCopyBufferToImageCreateInfo,
|
||||
VKCopyBufferToImageData,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VKResourceType::IMAGE | VKResourceType::BUFFER> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
static void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info)
|
||||
{
|
||||
node.storage_index = storage.copy_buffer_to_image.append_and_get_index(create_info.node_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const CreateInfo &create_info) override
|
||||
{
|
||||
ResourceWithStamp src_resource = resources.get_buffer(create_info.node_data.src_buffer);
|
||||
ResourceWithStamp dst_resource = resources.get_image_and_increase_stamp(
|
||||
create_info.node_data.dst_image);
|
||||
links.buffers.append({src_resource, VK_ACCESS_TRANSFER_READ_BIT});
|
||||
links.images.append({{dst_resource, VK_ACCESS_TRANSFER_WRITE_BIT},
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
create_info.vk_image_aspects});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> /*storage_push_constants*/,
|
||||
VKBoundPipelines & /*r_bound_pipelines*/) override
|
||||
{
|
||||
command_buffer.copy_buffer_to_image(
|
||||
data.src_buffer, data.dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &data.region);
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,103 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../vk_command_buffer_wrapper.hh"
|
||||
#include "../vk_render_graph_links.hh"
|
||||
#include "../vk_resource_state_tracker.hh"
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKCopyImageData {
|
||||
VkImage src_image;
|
||||
VkImage dst_image;
|
||||
VkImageCopy region;
|
||||
/**
|
||||
* \brief The number of mip-levels to copy.
|
||||
*
|
||||
* Additional regions are added just before sending to the command buffer.
|
||||
*/
|
||||
int32_t mip_levels;
|
||||
};
|
||||
|
||||
struct VKCopyImageCreateInfo {
|
||||
VKCopyImageData node_data;
|
||||
VkImageAspectFlags vk_image_aspect;
|
||||
};
|
||||
|
||||
class VKCopyImageNode : public VKNodeInfo<VKNodeType::COPY_IMAGE,
|
||||
VKCopyImageCreateInfo,
|
||||
VKCopyImageData,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VKResourceType::IMAGE> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
static void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info)
|
||||
{
|
||||
BLI_assert(create_info.node_data.mip_levels > 0);
|
||||
node.storage_index = storage.copy_image.append_and_get_index(create_info.node_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const CreateInfo &create_info) override
|
||||
{
|
||||
ResourceWithStamp src_resource = resources.get_image(create_info.node_data.src_image);
|
||||
ResourceWithStamp dst_resource = resources.get_image_and_increase_stamp(
|
||||
create_info.node_data.dst_image);
|
||||
links.images.append({{src_resource, VK_ACCESS_TRANSFER_READ_BIT},
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
create_info.vk_image_aspect});
|
||||
links.images.append({{dst_resource, VK_ACCESS_TRANSFER_WRITE_BIT},
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
create_info.vk_image_aspect});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> /*storage_push_constants*/,
|
||||
VKBoundPipelines & /*r_bound_pipelines*/) override
|
||||
{
|
||||
/* Expand the regions to contain all requested mipmap levels. */
|
||||
Array<VkImageCopy> regions(data.mip_levels);
|
||||
regions.fill(data.region);
|
||||
const VkImageCopy &src_region = regions[0];
|
||||
for (int index : regions.index_range().drop_front(1)) {
|
||||
regions[index].extent.width = std::max(src_region.extent.width >> index, 1u);
|
||||
regions[index].extent.height = std::max(src_region.extent.height >> index, 1u);
|
||||
regions[index].extent.depth = std::max(src_region.extent.depth >> index, 1u);
|
||||
regions[index].srcSubresource.mipLevel = src_region.srcSubresource.mipLevel + index;
|
||||
regions[index].dstSubresource.mipLevel = src_region.dstSubresource.mipLevel + index;
|
||||
}
|
||||
|
||||
command_buffer.copy_image(data.src_image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
data.dst_image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
uint32_t(regions.size()),
|
||||
regions.data());
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,79 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../vk_command_buffer_wrapper.hh"
|
||||
#include "../vk_render_graph_links.hh"
|
||||
#include "../vk_resource_state_tracker.hh"
|
||||
#include "vk_common.hh"
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKCopyImageToBufferData {
|
||||
VkImage src_image;
|
||||
VkBuffer dst_buffer;
|
||||
VkBufferImageCopy region;
|
||||
};
|
||||
|
||||
struct VKCopyImageToBufferCreateInfo {
|
||||
VKCopyImageToBufferData node_data;
|
||||
VkImageAspectFlags vk_image_aspects;
|
||||
};
|
||||
|
||||
class VKCopyImageToBufferNode : public VKNodeInfo<VKNodeType::COPY_IMAGE_TO_BUFFER,
|
||||
VKCopyImageToBufferCreateInfo,
|
||||
VKCopyImageToBufferData,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VKResourceType::IMAGE | VKResourceType::BUFFER> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
static void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info)
|
||||
{
|
||||
node.storage_index = storage.copy_image_to_buffer.append_and_get_index(create_info.node_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const CreateInfo &create_info) override
|
||||
{
|
||||
ResourceWithStamp src_resource = resources.get_image(create_info.node_data.src_image);
|
||||
ResourceWithStamp dst_resource = resources.get_buffer_and_increase_stamp(
|
||||
create_info.node_data.dst_buffer);
|
||||
links.images.append({{src_resource, VK_ACCESS_TRANSFER_READ_BIT},
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
create_info.vk_image_aspects});
|
||||
links.buffers.append({dst_resource, VK_ACCESS_TRANSFER_WRITE_BIT});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> /*storage_push_constants*/,
|
||||
VKBoundPipelines & /*r_bound_pipelines*/) override
|
||||
{
|
||||
command_buffer.copy_image_to_buffer(
|
||||
data.src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, data.dst_buffer, 1, &data.region);
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,88 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "render_graph/vk_render_graph_links.hh"
|
||||
#include "render_graph/vk_resource_access_info.hh"
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKDispatchIndirectData {
|
||||
VKPipelineData pipeline_data;
|
||||
VkBuffer buffer;
|
||||
VkDeviceSize offset;
|
||||
};
|
||||
|
||||
/**
|
||||
* Information needed to add a node to the render graph.
|
||||
*/
|
||||
struct VKDispatchIndirectCreateInfo : NonCopyable {
|
||||
VKDispatchIndirectData dispatch_indirect_node = {};
|
||||
const VKResourceAccessInfo &resources;
|
||||
VKDispatchIndirectCreateInfo(const VKResourceAccessInfo &resources) : resources(resources) {}
|
||||
};
|
||||
|
||||
/* Although confusing the spec mentions that VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT should also be
|
||||
* used for dispatches.
|
||||
* (https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineStageFlagBits.html)
|
||||
*/
|
||||
class VKDispatchIndirectNode
|
||||
: public VKNodeInfo<VKNodeType::DISPATCH_INDIRECT,
|
||||
VKDispatchIndirectCreateInfo,
|
||||
VKDispatchIndirectData,
|
||||
VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||
VKResourceType::IMAGE | VKResourceType::BUFFER> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
static void set_node_data(Node &node, Storage & /* storage */, const CreateInfo &create_info)
|
||||
{
|
||||
node.dispatch_indirect = create_info.dispatch_indirect_node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const CreateInfo &create_info) override
|
||||
{
|
||||
create_info.resources.build_links(resources, links);
|
||||
ResourceWithStamp buffer_resource = resources.get_buffer(
|
||||
create_info.dispatch_indirect_node.buffer);
|
||||
links.buffers.append({buffer_resource, VK_ACCESS_INDIRECT_COMMAND_READ_BIT});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> storage_push_constants,
|
||||
VKBoundPipelines &r_bound_pipelines) override
|
||||
{
|
||||
vk_pipeline_data_build_commands(command_buffer,
|
||||
data.pipeline_data,
|
||||
storage_push_constants,
|
||||
r_bound_pipelines.compute,
|
||||
VK_PIPELINE_BIND_POINT_COMPUTE,
|
||||
VK_SHADER_STAGE_COMPUTE_BIT);
|
||||
command_buffer.dispatch_indirect(data.buffer, data.offset);
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,81 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "render_graph/vk_render_graph_links.hh"
|
||||
#include "render_graph/vk_resource_access_info.hh"
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKDispatchData {
|
||||
VKPipelineData pipeline_data;
|
||||
uint32_t group_count_x;
|
||||
uint32_t group_count_y;
|
||||
uint32_t group_count_z;
|
||||
};
|
||||
|
||||
/**
|
||||
* Information needed to add a node to the render graph.
|
||||
*/
|
||||
struct VKDispatchCreateInfo : NonCopyable {
|
||||
VKDispatchData dispatch_node = {};
|
||||
const VKResourceAccessInfo &resources;
|
||||
VKDispatchCreateInfo(const VKResourceAccessInfo &resources) : resources(resources) {}
|
||||
};
|
||||
|
||||
class VKDispatchNode : public VKNodeInfo<VKNodeType::DISPATCH,
|
||||
VKDispatchCreateInfo,
|
||||
VKDispatchData,
|
||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||
VKResourceType::IMAGE | VKResourceType::BUFFER> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
static void set_node_data(Node &node, Storage & /* storage */, const CreateInfo &create_info)
|
||||
{
|
||||
node.dispatch = create_info.dispatch_node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const CreateInfo &create_info) override
|
||||
{
|
||||
create_info.resources.build_links(resources, links);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> storage_push_constants,
|
||||
VKBoundPipelines &r_bound_pipelines) override
|
||||
{
|
||||
vk_pipeline_data_build_commands(command_buffer,
|
||||
data.pipeline_data,
|
||||
storage_push_constants,
|
||||
r_bound_pipelines.compute,
|
||||
VK_PIPELINE_BIND_POINT_COMPUTE,
|
||||
VK_SHADER_STAGE_COMPUTE_BIT);
|
||||
command_buffer.dispatch(data.group_count_x, data.group_count_y, data.group_count_z);
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,96 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "render_graph/vk_resource_access_info.hh"
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKDrawIndexedIndirectData {
|
||||
VKPipelineDataGraphics graphics;
|
||||
VKIndexBufferBinding index_buffer;
|
||||
VKVertexBufferBindings vertex_buffers;
|
||||
VkBuffer indirect_buffer;
|
||||
VkDeviceSize offset;
|
||||
uint32_t draw_count;
|
||||
uint32_t stride;
|
||||
};
|
||||
|
||||
struct VKDrawIndexedIndirectCreateInfo {
|
||||
VKDrawIndexedIndirectData node_data = {};
|
||||
const VKResourceAccessInfo &resources;
|
||||
VKDrawIndexedIndirectCreateInfo(const VKResourceAccessInfo &resources) : resources(resources) {}
|
||||
};
|
||||
|
||||
class VKDrawIndexedIndirectNode
|
||||
: public VKNodeInfo<VKNodeType::DRAW_INDEXED_INDIRECT,
|
||||
VKDrawIndexedIndirectCreateInfo,
|
||||
VKDrawIndexedIndirectData,
|
||||
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
|
||||
VKResourceType::IMAGE | VKResourceType::BUFFER> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
static void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info)
|
||||
{
|
||||
node.storage_index = storage.draw_indexed_indirect.append_and_get_index(create_info.node_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const CreateInfo &create_info) override
|
||||
{
|
||||
create_info.resources.build_links(resources, links);
|
||||
if (create_info.node_data.index_buffer.buffer != VK_NULL_HANDLE) {
|
||||
vk_index_buffer_binding_build_links(resources, links, create_info.node_data.index_buffer);
|
||||
}
|
||||
|
||||
vk_vertex_buffer_bindings_build_links(resources, links, create_info.node_data.vertex_buffers);
|
||||
ResourceWithStamp buffer_resource = resources.get_buffer(
|
||||
create_info.node_data.indirect_buffer);
|
||||
links.buffers.append({buffer_resource, VK_ACCESS_INDIRECT_COMMAND_READ_BIT});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> storage_push_constants,
|
||||
VKBoundPipelines &r_bound_pipelines) override
|
||||
{
|
||||
vk_pipeline_dynamic_graphics_build_commands(command_buffer, data.graphics, r_bound_pipelines);
|
||||
vk_pipeline_data_build_commands(command_buffer,
|
||||
data.graphics.pipeline_data,
|
||||
storage_push_constants,
|
||||
r_bound_pipelines.graphics.pipeline,
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
VK_SHADER_STAGE_ALL_GRAPHICS);
|
||||
vk_index_buffer_binding_build_commands(
|
||||
command_buffer, data.index_buffer, r_bound_pipelines.graphics.index_buffer);
|
||||
vk_vertex_buffer_bindings_build_commands(
|
||||
command_buffer, data.vertex_buffers, r_bound_pipelines.graphics.vertex_buffers);
|
||||
command_buffer.draw_indexed_indirect(
|
||||
data.indirect_buffer, data.offset, data.draw_count, data.stride);
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,95 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "render_graph/vk_resource_access_info.hh"
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKDrawIndexedData {
|
||||
VKPipelineDataGraphics graphics;
|
||||
VKIndexBufferBinding index_buffer;
|
||||
VKVertexBufferBindings vertex_buffers;
|
||||
uint32_t index_count;
|
||||
uint32_t instance_count;
|
||||
uint32_t first_index;
|
||||
uint32_t vertex_offset;
|
||||
uint32_t first_instance;
|
||||
};
|
||||
|
||||
struct VKDrawIndexedCreateInfo {
|
||||
VKDrawIndexedData node_data = {};
|
||||
const VKResourceAccessInfo &resources;
|
||||
VKDrawIndexedCreateInfo(const VKResourceAccessInfo &resources) : resources(resources) {}
|
||||
};
|
||||
|
||||
class VKDrawIndexedNode : public VKNodeInfo<VKNodeType::DRAW_INDEXED,
|
||||
VKDrawIndexedCreateInfo,
|
||||
VKDrawIndexedData,
|
||||
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
|
||||
VKResourceType::IMAGE | VKResourceType::BUFFER> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
static void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info)
|
||||
{
|
||||
node.storage_index = storage.draw_indexed.append_and_get_index(create_info.node_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const CreateInfo &create_info) override
|
||||
{
|
||||
create_info.resources.build_links(resources, links);
|
||||
if (create_info.node_data.index_buffer.buffer != VK_NULL_HANDLE) {
|
||||
vk_index_buffer_binding_build_links(resources, links, create_info.node_data.index_buffer);
|
||||
}
|
||||
vk_vertex_buffer_bindings_build_links(resources, links, create_info.node_data.vertex_buffers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> storage_push_constants,
|
||||
VKBoundPipelines &r_bound_pipelines) override
|
||||
{
|
||||
vk_pipeline_dynamic_graphics_build_commands(command_buffer, data.graphics, r_bound_pipelines);
|
||||
vk_pipeline_data_build_commands(command_buffer,
|
||||
data.graphics.pipeline_data,
|
||||
storage_push_constants,
|
||||
r_bound_pipelines.graphics.pipeline,
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
VK_SHADER_STAGE_ALL_GRAPHICS);
|
||||
vk_index_buffer_binding_build_commands(
|
||||
command_buffer, data.index_buffer, r_bound_pipelines.graphics.index_buffer);
|
||||
vk_vertex_buffer_bindings_build_commands(
|
||||
command_buffer, data.vertex_buffers, r_bound_pipelines.graphics.vertex_buffers);
|
||||
command_buffer.draw_indexed(data.index_count,
|
||||
data.instance_count,
|
||||
data.first_index,
|
||||
data.vertex_offset,
|
||||
data.first_instance);
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,88 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "render_graph/vk_resource_access_info.hh"
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKDrawIndirectData {
|
||||
VKPipelineDataGraphics graphics;
|
||||
VKVertexBufferBindings vertex_buffers;
|
||||
VkBuffer indirect_buffer;
|
||||
VkDeviceSize offset;
|
||||
uint32_t draw_count;
|
||||
uint32_t stride;
|
||||
};
|
||||
|
||||
struct VKDrawIndirectCreateInfo {
|
||||
VKDrawIndirectData node_data = {};
|
||||
const VKResourceAccessInfo &resources;
|
||||
VKDrawIndirectCreateInfo(const VKResourceAccessInfo &resources) : resources(resources) {}
|
||||
};
|
||||
|
||||
class VKDrawIndirectNode : public VKNodeInfo<VKNodeType::DRAW_INDIRECT,
|
||||
VKDrawIndirectCreateInfo,
|
||||
VKDrawIndirectData,
|
||||
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
|
||||
VKResourceType::IMAGE | VKResourceType::BUFFER> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
static void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info)
|
||||
{
|
||||
node.storage_index = storage.draw_indirect.append_and_get_index(create_info.node_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const CreateInfo &create_info) override
|
||||
{
|
||||
create_info.resources.build_links(resources, links);
|
||||
vk_vertex_buffer_bindings_build_links(resources, links, create_info.node_data.vertex_buffers);
|
||||
ResourceWithStamp buffer_resource = resources.get_buffer(
|
||||
create_info.node_data.indirect_buffer);
|
||||
links.buffers.append({buffer_resource, VK_ACCESS_INDIRECT_COMMAND_READ_BIT});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> storage_push_constants,
|
||||
VKBoundPipelines &r_bound_pipelines) override
|
||||
{
|
||||
vk_pipeline_dynamic_graphics_build_commands(command_buffer, data.graphics, r_bound_pipelines);
|
||||
vk_pipeline_data_build_commands(command_buffer,
|
||||
data.graphics.pipeline_data,
|
||||
storage_push_constants,
|
||||
r_bound_pipelines.graphics.pipeline,
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
VK_SHADER_STAGE_ALL_GRAPHICS);
|
||||
vk_vertex_buffer_bindings_build_commands(
|
||||
command_buffer, data.vertex_buffers, r_bound_pipelines.graphics.vertex_buffers);
|
||||
|
||||
command_buffer.draw_indirect(data.indirect_buffer, data.offset, data.draw_count, data.stride);
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,87 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "render_graph/nodes/vk_pipeline_data.hh"
|
||||
#include "render_graph/vk_resource_access_info.hh"
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKDrawData {
|
||||
VKPipelineDataGraphics graphics;
|
||||
VKVertexBufferBindings vertex_buffers;
|
||||
uint32_t vertex_count;
|
||||
uint32_t instance_count;
|
||||
uint32_t first_vertex;
|
||||
uint32_t first_instance;
|
||||
};
|
||||
|
||||
struct VKDrawCreateInfo {
|
||||
VKDrawData node_data = {};
|
||||
const VKResourceAccessInfo &resources;
|
||||
VKDrawCreateInfo(const VKResourceAccessInfo &resources) : resources(resources) {}
|
||||
};
|
||||
|
||||
class VKDrawNode : public VKNodeInfo<VKNodeType::DRAW,
|
||||
VKDrawCreateInfo,
|
||||
VKDrawData,
|
||||
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
|
||||
VKResourceType::IMAGE | VKResourceType::BUFFER> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
static void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info)
|
||||
{
|
||||
node.storage_index = storage.draw.append_and_get_index(create_info.node_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const CreateInfo &create_info) override
|
||||
{
|
||||
create_info.resources.build_links(resources, links);
|
||||
vk_vertex_buffer_bindings_build_links(resources, links, create_info.node_data.vertex_buffers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> storage_push_constants,
|
||||
VKBoundPipelines &r_bound_pipelines) override
|
||||
{
|
||||
vk_pipeline_dynamic_graphics_build_commands(command_buffer, data.graphics, r_bound_pipelines);
|
||||
vk_pipeline_data_build_commands(command_buffer,
|
||||
data.graphics.pipeline_data,
|
||||
storage_push_constants,
|
||||
r_bound_pipelines.graphics.pipeline,
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
VK_SHADER_STAGE_ALL_GRAPHICS);
|
||||
vk_vertex_buffer_bindings_build_commands(
|
||||
command_buffer, data.vertex_buffers, r_bound_pipelines.graphics.vertex_buffers);
|
||||
|
||||
command_buffer.draw(
|
||||
data.vertex_count, data.instance_count, data.first_vertex, data.first_instance);
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,69 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKEndQueryData {
|
||||
VkQueryPool vk_query_pool;
|
||||
uint32_t query_index;
|
||||
};
|
||||
|
||||
/**
|
||||
* End Query Node
|
||||
*
|
||||
* - Contains logic to copy relevant data to the VKRenderGraphNode.
|
||||
* - Determine read/write resource dependencies.
|
||||
* - Add commands to a command builder.
|
||||
*/
|
||||
class VKEndQueryNode : public VKNodeInfo<VKNodeType::END_QUERY,
|
||||
VKEndQueryData,
|
||||
VKEndQueryData,
|
||||
VK_PIPELINE_STAGE_NONE,
|
||||
VKResourceType::IMAGE> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
void set_node_data(Node &node, Storage & /*storage*/, const CreateInfo &create_info)
|
||||
{
|
||||
node.end_query = create_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker & /*resources*/,
|
||||
VKRenderGraphLinks & /*links*/,
|
||||
const CreateInfo & /*create_info*/) override
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> /*storage_push_constants*/,
|
||||
VKBoundPipelines & /*r_bound_pipelines*/) override
|
||||
{
|
||||
command_buffer.end_query(data.vk_query_pool, data.query_index);
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,66 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKEndRenderingData {};
|
||||
|
||||
/**
|
||||
* End rendering node
|
||||
*
|
||||
* - Contains logic to copy relevant data to the VKRenderGraphNode.
|
||||
* - Determine read/write resource dependencies.
|
||||
* - Add commands to a command builder.
|
||||
*/
|
||||
class VKEndRenderingNode : public VKNodeInfo<VKNodeType::END_RENDERING,
|
||||
VKEndRenderingData,
|
||||
VKEndRenderingData,
|
||||
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
|
||||
VKResourceType::NONE> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
void set_node_data(Node &node, Storage & /*storage*/, const CreateInfo &create_info)
|
||||
{
|
||||
node.end_rendering = create_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker & /*resources*/,
|
||||
VKRenderGraphLinks & /*links*/,
|
||||
const CreateInfo & /*create_info*/) override
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data & /*data*/,
|
||||
Span<uint8_t> /*storage_push_constants*/,
|
||||
VKBoundPipelines & /*r_bound_pipelines*/) override
|
||||
{
|
||||
command_buffer.end_rendering();
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,64 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKFillBufferData {
|
||||
VkBuffer vk_buffer;
|
||||
VkDeviceSize size;
|
||||
uint32_t data;
|
||||
};
|
||||
|
||||
class VKFillBufferNode : public VKNodeInfo<VKNodeType::FILL_BUFFER,
|
||||
VKFillBufferData,
|
||||
VKFillBufferData,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VKResourceType::BUFFER> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
static void set_node_data(Node &node, Storage & /* storage */, const CreateInfo &create_info)
|
||||
{
|
||||
node.fill_buffer = create_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const CreateInfo &create_info) override
|
||||
{
|
||||
ResourceWithStamp resource = resources.get_buffer_and_increase_stamp(create_info.vk_buffer);
|
||||
links.buffers.append({resource, VK_ACCESS_TRANSFER_WRITE_BIT});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> /*storage_push_constants*/,
|
||||
VKBoundPipelines & /*r_bound_pipelines*/) override
|
||||
{
|
||||
command_buffer.fill_buffer(data.vk_buffer, 0, data.size, data.data);
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,215 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "render_graph/vk_command_buffer_wrapper.hh"
|
||||
#include "render_graph/vk_render_graph_links.hh"
|
||||
#include "render_graph/vk_resource_state_tracker.hh"
|
||||
#include "vk_common.hh"
|
||||
#include "vk_pipeline_data.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
/**
|
||||
* Type of nodes of the render graph.
|
||||
*/
|
||||
enum class VKNodeType {
|
||||
UNUSED,
|
||||
BEGIN_QUERY,
|
||||
BEGIN_RENDERING,
|
||||
BLIT_IMAGE,
|
||||
CLEAR_ATTACHMENTS,
|
||||
CLEAR_COLOR_IMAGE,
|
||||
CLEAR_DEPTH_STENCIL_IMAGE,
|
||||
COPY_BUFFER,
|
||||
COPY_IMAGE,
|
||||
COPY_IMAGE_TO_BUFFER,
|
||||
COPY_BUFFER_TO_IMAGE,
|
||||
DISPATCH,
|
||||
DISPATCH_INDIRECT,
|
||||
DRAW,
|
||||
DRAW_INDEXED,
|
||||
DRAW_INDEXED_INDIRECT,
|
||||
DRAW_INDIRECT,
|
||||
END_QUERY,
|
||||
END_RENDERING,
|
||||
FILL_BUFFER,
|
||||
RESET_QUERY_POOL,
|
||||
SYNCHRONIZATION,
|
||||
UPDATE_BUFFER,
|
||||
UPDATE_MIPMAPS,
|
||||
};
|
||||
|
||||
BLI_INLINE std::ostream &operator<<(std::ostream &os, const VKNodeType node_type)
|
||||
{
|
||||
switch (node_type) {
|
||||
case VKNodeType::UNUSED:
|
||||
os << "UNUSED";
|
||||
break;
|
||||
case VKNodeType::BEGIN_QUERY:
|
||||
os << "BEGIN_QUERY";
|
||||
break;
|
||||
case VKNodeType::BEGIN_RENDERING:
|
||||
os << "BEGIN_RENDERING";
|
||||
break;
|
||||
case VKNodeType::END_QUERY:
|
||||
os << "END_QUERY";
|
||||
break;
|
||||
case VKNodeType::END_RENDERING:
|
||||
os << "END_RENDERING";
|
||||
break;
|
||||
case VKNodeType::CLEAR_ATTACHMENTS:
|
||||
os << "CLEAR_ATTACHMENTS";
|
||||
break;
|
||||
case VKNodeType::CLEAR_COLOR_IMAGE:
|
||||
os << "CLEAR_COLOR_IMAGE";
|
||||
break;
|
||||
case VKNodeType::CLEAR_DEPTH_STENCIL_IMAGE:
|
||||
os << "CLEAR_DEPTH_STENCIL_IMAGE";
|
||||
break;
|
||||
case VKNodeType::FILL_BUFFER:
|
||||
os << "FILL_BUFFER";
|
||||
break;
|
||||
case VKNodeType::COPY_BUFFER:
|
||||
os << "COPY_BUFFER";
|
||||
break;
|
||||
case VKNodeType::COPY_IMAGE:
|
||||
os << "COPY_IMAGE";
|
||||
break;
|
||||
case VKNodeType::COPY_IMAGE_TO_BUFFER:
|
||||
os << "COPY_IMAGE_TO_BUFFER";
|
||||
break;
|
||||
case VKNodeType::COPY_BUFFER_TO_IMAGE:
|
||||
os << "COPY_BUFFER_TO_IMAGE";
|
||||
break;
|
||||
case VKNodeType::BLIT_IMAGE:
|
||||
os << "BLIT_IMAGE";
|
||||
break;
|
||||
case VKNodeType::DISPATCH:
|
||||
os << "DISPATCH";
|
||||
break;
|
||||
case VKNodeType::DISPATCH_INDIRECT:
|
||||
os << "DISPATCH_INDIRECT";
|
||||
break;
|
||||
case VKNodeType::DRAW:
|
||||
os << "DRAW";
|
||||
break;
|
||||
case VKNodeType::DRAW_INDEXED:
|
||||
os << "DRAW_INDEXED";
|
||||
break;
|
||||
case VKNodeType::DRAW_INDEXED_INDIRECT:
|
||||
os << "DRAW_INDEXED_INDIRECT";
|
||||
break;
|
||||
case VKNodeType::DRAW_INDIRECT:
|
||||
os << "DRAW_INDIRECT";
|
||||
break;
|
||||
case VKNodeType::RESET_QUERY_POOL:
|
||||
os << "RESET_QUERY_POOL";
|
||||
break;
|
||||
case VKNodeType::SYNCHRONIZATION:
|
||||
os << "SYNCHRONIZATION";
|
||||
break;
|
||||
case VKNodeType::UPDATE_BUFFER:
|
||||
os << "UPDATE_BUFFER";
|
||||
break;
|
||||
case VKNodeType::UPDATE_MIPMAPS:
|
||||
os << "UPDATE_MIPMAPS";
|
||||
break;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
BLI_INLINE bool node_type_is_within_rendering(VKNodeType node_type)
|
||||
{
|
||||
return ELEM(node_type,
|
||||
VKNodeType::CLEAR_ATTACHMENTS,
|
||||
VKNodeType::DRAW,
|
||||
VKNodeType::DRAW_INDEXED,
|
||||
VKNodeType::DRAW_INDEXED_INDIRECT,
|
||||
VKNodeType::DRAW_INDIRECT);
|
||||
}
|
||||
|
||||
BLI_INLINE bool node_type_is_rendering(VKNodeType node_type)
|
||||
{
|
||||
return ELEM(node_type, VKNodeType::BEGIN_RENDERING, VKNodeType::END_RENDERING) ||
|
||||
node_type_is_within_rendering(node_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Info class for a node type.
|
||||
*
|
||||
* Nodes can be created using `NodeCreateInfo`. When a node is created the `VKNodeInfo.node_type`
|
||||
* and `VKNodeInfo.set_node_data` are used to fill a VKRenderGraphNode instance. The
|
||||
* VKRenderGraphNode is stored sequentially in the render graph. When the node is created the
|
||||
* dependencies are extracted by calling `VKNodeInfo.build_links`.
|
||||
*
|
||||
* Eventually when a node is recorded to a command buffer `VKNodeInfo.build_commands` is invoked.
|
||||
*/
|
||||
template<VKNodeType NodeType,
|
||||
typename NodeCreateInfo,
|
||||
typename NodeData,
|
||||
VkPipelineStageFlags PipelineStage,
|
||||
VKResourceType ResourceUsages>
|
||||
class VKNodeInfo : public NonCopyable {
|
||||
|
||||
public:
|
||||
using CreateInfo = NodeCreateInfo;
|
||||
using Data = NodeData;
|
||||
|
||||
/**
|
||||
* Node type of this class.
|
||||
*
|
||||
* The node type used to link VKRenderGraphNode instance to a VKNodeInfo.
|
||||
*/
|
||||
static constexpr VKNodeType node_type = NodeType;
|
||||
|
||||
/**
|
||||
* Which pipeline stage does this command belongs to. The pipeline stage is used when generating
|
||||
* pipeline barriers.
|
||||
*/
|
||||
static constexpr VkPipelineStageFlags pipeline_stage = PipelineStage;
|
||||
|
||||
/**
|
||||
* Which resource types are relevant. Some code can be skipped when a node can only depend on
|
||||
* resources of a single type.
|
||||
*/
|
||||
static constexpr VKResourceType resource_usages = ResourceUsages;
|
||||
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`Data`/`CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*
|
||||
* This function must be implemented by all node classes. But due to cyclic inclusion of header
|
||||
* files it is implemented as a template function.
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
static void set_node_data(Node &node, Storage &storage, const CreateInfo &create_info);
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
virtual void build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const CreateInfo &create_info) = 0;
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*
|
||||
* The command buffer is passed as an interface as this is replaced by a logger when running test
|
||||
* cases. The test cases will validate the log to find out if the correct commands where added.
|
||||
*/
|
||||
virtual void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> storage_push_constants,
|
||||
VKBoundPipelines &r_bound_pipelines) = 0;
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,136 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "render_graph/nodes/vk_pipeline_data.hh"
|
||||
#include "render_graph/vk_command_buffer_wrapper.hh"
|
||||
#include "render_graph/vk_render_graph_links.hh"
|
||||
|
||||
#include "vk_backend.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
void vk_pipeline_dynamic_graphics_build_commands(VKCommandBufferInterface &command_buffer,
|
||||
const VKPipelineDataGraphics &graphics,
|
||||
VKBoundPipelines &r_bound_pipelines)
|
||||
{
|
||||
if (assign_if_different(r_bound_pipelines.graphics.viewport_state, graphics.viewport)) {
|
||||
command_buffer.set_viewport(graphics.viewport.viewports);
|
||||
command_buffer.set_scissor(graphics.viewport.scissors);
|
||||
}
|
||||
if (assign_if_different(r_bound_pipelines.graphics.line_width, graphics.line_width)) {
|
||||
if (graphics.line_width.has_value()) {
|
||||
command_buffer.set_line_width(*graphics.line_width);
|
||||
}
|
||||
}
|
||||
if (assign_if_different(r_bound_pipelines.graphics.stencil_state, graphics.stencil_state)) {
|
||||
if (graphics.stencil_state.has_value()) {
|
||||
const StencilState &stencil_state = *graphics.stencil_state;
|
||||
command_buffer.set_stencil_compare_mask(stencil_state.compare_mask);
|
||||
command_buffer.set_stencil_write_mask(stencil_state.write_mask);
|
||||
command_buffer.set_stencil_reference(stencil_state.reference);
|
||||
}
|
||||
}
|
||||
if (assign_if_different(r_bound_pipelines.graphics.front_face, graphics.front_face)) {
|
||||
if (graphics.front_face.has_value()) {
|
||||
command_buffer.set_front_face(*graphics.front_face);
|
||||
}
|
||||
}
|
||||
if (assign_if_different(r_bound_pipelines.graphics.vertex_input_description,
|
||||
graphics.vertex_input_description))
|
||||
{
|
||||
if (graphics.vertex_input_description.has_value()) {
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
const VKVertexInputDescription &description = device.vertex_input_descriptions.get(
|
||||
*graphics.vertex_input_description);
|
||||
command_buffer.set_vertex_input(description.bindings, description.attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void vk_pipeline_data_build_commands(VKCommandBufferInterface &command_buffer,
|
||||
const VKPipelineData &pipeline_data,
|
||||
Span<uint8_t> storage_push_constants,
|
||||
VKBoundPipeline &r_bound_pipeline,
|
||||
VkPipelineBindPoint vk_pipeline_bind_point,
|
||||
VkShaderStageFlags vk_shader_stage_flags)
|
||||
{
|
||||
if (assign_if_different(r_bound_pipeline.vk_pipeline, pipeline_data.vk_pipeline)) {
|
||||
command_buffer.bind_pipeline(vk_pipeline_bind_point, r_bound_pipeline.vk_pipeline);
|
||||
}
|
||||
|
||||
const bool descriptor_set_changed = assign_if_different(r_bound_pipeline.vk_descriptor_set,
|
||||
pipeline_data.vk_descriptor_set);
|
||||
const bool layout_changed = assign_if_different(r_bound_pipeline.vk_pipeline_layout,
|
||||
pipeline_data.vk_pipeline_layout);
|
||||
if ((descriptor_set_changed || layout_changed) &&
|
||||
r_bound_pipeline.vk_descriptor_set != VK_NULL_HANDLE)
|
||||
{
|
||||
command_buffer.bind_descriptor_sets(vk_pipeline_bind_point,
|
||||
pipeline_data.vk_pipeline_layout,
|
||||
0,
|
||||
1,
|
||||
&r_bound_pipeline.vk_descriptor_set,
|
||||
0,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
if (!pipeline_data.push_constants_range.is_empty()) {
|
||||
Span<uint8_t> pipeline_push_constants = storage_push_constants.slice(
|
||||
pipeline_data.push_constants_range);
|
||||
command_buffer.push_constants(pipeline_data.vk_pipeline_layout,
|
||||
vk_shader_stage_flags,
|
||||
0,
|
||||
pipeline_push_constants.size(),
|
||||
pipeline_push_constants.data());
|
||||
}
|
||||
}
|
||||
|
||||
void vk_index_buffer_binding_build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const VKIndexBufferBinding &index_buffer_binding)
|
||||
{
|
||||
ResourceWithStamp resource = resources.get_buffer(index_buffer_binding.buffer);
|
||||
links.buffers.append({resource, VK_ACCESS_INDEX_READ_BIT});
|
||||
}
|
||||
|
||||
void vk_index_buffer_binding_build_commands(VKCommandBufferInterface &command_buffer,
|
||||
const VKIndexBufferBinding &index_buffer_binding,
|
||||
VKIndexBufferBinding &r_bound_index_buffer)
|
||||
{
|
||||
if (assign_if_different(r_bound_index_buffer, index_buffer_binding)) {
|
||||
command_buffer.bind_index_buffer(
|
||||
r_bound_index_buffer.buffer, 0, r_bound_index_buffer.index_type);
|
||||
}
|
||||
}
|
||||
|
||||
void vk_vertex_buffer_bindings_build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const VKVertexBufferBindings &vertex_buffers)
|
||||
{
|
||||
for (const VkBuffer vk_buffer :
|
||||
Span<VkBuffer>(vertex_buffers.buffer, vertex_buffers.buffer_count))
|
||||
{
|
||||
ResourceWithStamp resource = resources.get_buffer(vk_buffer);
|
||||
links.buffers.append({resource, VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT});
|
||||
}
|
||||
}
|
||||
|
||||
void vk_vertex_buffer_bindings_build_commands(VKCommandBufferInterface &command_buffer,
|
||||
const VKVertexBufferBindings &vertex_buffer_bindings,
|
||||
VKVertexBufferBindings &r_bound_vertex_buffers)
|
||||
{
|
||||
if (assign_if_different(r_bound_vertex_buffers, vertex_buffer_bindings) &&
|
||||
r_bound_vertex_buffers.buffer_count)
|
||||
{
|
||||
command_buffer.bind_vertex_buffers(0,
|
||||
r_bound_vertex_buffers.buffer_count,
|
||||
r_bound_vertex_buffers.buffer,
|
||||
r_bound_vertex_buffers.offset);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,201 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vk_vertex_input_description.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
class VKCommandBufferInterface;
|
||||
struct VKRenderGraphLinks;
|
||||
class VKResourceStateTracker;
|
||||
|
||||
/**
|
||||
* Stencil dynamic data: op + compare/write masks + reference.
|
||||
* Moved here so it can be shared between pipeline code and command-buffer wrappers.
|
||||
*/
|
||||
struct StencilState {
|
||||
uint8_t compare_mask;
|
||||
uint8_t reference;
|
||||
uint8_t write_mask;
|
||||
|
||||
bool operator==(const StencilState &other) const noexcept
|
||||
{
|
||||
return compare_mask == other.compare_mask && reference == other.reference &&
|
||||
write_mask == other.write_mask;
|
||||
}
|
||||
bool operator!=(const StencilState &other) const noexcept
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Container for storing shader descriptor set and push constants.
|
||||
*
|
||||
* Compute and graphic shaders use the same structure to setup the pipeline for execution.
|
||||
*/
|
||||
struct VKPipelineData {
|
||||
VkPipeline vk_pipeline;
|
||||
VkPipelineLayout vk_pipeline_layout;
|
||||
VkDescriptorSet vk_descriptor_set;
|
||||
|
||||
/** Range where the push constants are stored in the render graph storage */
|
||||
IndexRange push_constants_range;
|
||||
};
|
||||
|
||||
/**
|
||||
* Container for storing viewport and scissor data used for
|
||||
* draw nodes.
|
||||
*/
|
||||
struct VKViewportData {
|
||||
Vector<VkViewport> viewports;
|
||||
Vector<VkRect2D> scissors;
|
||||
|
||||
bool operator==(const VKViewportData &other) const
|
||||
{
|
||||
if (viewports.size() != other.viewports.size() && scissors.size() != other.scissors.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(viewports.data(), other.viewports.data(), viewports.size() * sizeof(VkViewport)) !=
|
||||
0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(scissors.data(), other.scissors.data(), scissors.size() * sizeof(VkRect2D)) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const VKViewportData &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
struct VKPipelineDataGraphics {
|
||||
VKPipelineData pipeline_data;
|
||||
VKViewportData viewport;
|
||||
std::optional<VKVertexInputDescriptionPool::Key> vertex_input_description;
|
||||
std::optional<float> line_width;
|
||||
std::optional<StencilState> stencil_state;
|
||||
std::optional<VkFrontFace> front_face;
|
||||
};
|
||||
|
||||
/** Resources bound for a compute/graphics pipeline. */
|
||||
struct VKBoundPipeline {
|
||||
VkPipeline vk_pipeline;
|
||||
VkDescriptorSet vk_descriptor_set;
|
||||
VkPipelineLayout vk_pipeline_layout;
|
||||
};
|
||||
|
||||
struct VKIndexBufferBinding {
|
||||
VkBuffer buffer;
|
||||
VkIndexType index_type;
|
||||
|
||||
bool operator==(const VKIndexBufferBinding &other) const
|
||||
{
|
||||
return buffer == other.buffer && index_type == other.index_type;
|
||||
}
|
||||
bool operator!=(const VKIndexBufferBinding &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
struct VKVertexBufferBindings {
|
||||
uint32_t buffer_count;
|
||||
VkBuffer buffer[16];
|
||||
VkDeviceSize offset[16];
|
||||
|
||||
bool operator==(const VKVertexBufferBindings &other) const
|
||||
{
|
||||
return buffer_count == other.buffer_count &&
|
||||
Span<VkBuffer>(buffer, buffer_count) ==
|
||||
Span<VkBuffer>(other.buffer, other.buffer_count) &&
|
||||
Span<VkDeviceSize>(offset, buffer_count) ==
|
||||
Span<VkDeviceSize>(other.offset, buffer_count);
|
||||
}
|
||||
bool operator!=(const VKVertexBufferBindings &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Vulkan keeps track of bound resources for graphics separate from compute.
|
||||
* This struct store last bound resources for both bind points.
|
||||
*/
|
||||
struct VKBoundPipelines {
|
||||
/** Last bound resources for compute pipeline. */
|
||||
VKBoundPipeline compute;
|
||||
/** Last bound resources for graphics pipeline. */
|
||||
struct {
|
||||
VKBoundPipeline pipeline;
|
||||
VKIndexBufferBinding index_buffer;
|
||||
VKVertexBufferBindings vertex_buffers;
|
||||
VKViewportData viewport_state;
|
||||
std::optional<float> line_width;
|
||||
std::optional<StencilState> stencil_state;
|
||||
std::optional<VkFrontFace> front_face;
|
||||
std::optional<VKVertexInputDescriptionPool::Key> vertex_input_description;
|
||||
} graphics;
|
||||
};
|
||||
|
||||
/**
|
||||
* Record commands that update the dynamic state.
|
||||
*
|
||||
* - viewports
|
||||
* - scissors
|
||||
* - line width
|
||||
* - stencil op + compare/write masks + reference
|
||||
* - front face (when VK_EXT_extended_dynamic_state is available)
|
||||
* - vertex input (when VK_EXT_vertex_input_dynamic_state is available)
|
||||
*/
|
||||
void vk_pipeline_dynamic_graphics_build_commands(VKCommandBufferInterface &command_buffer,
|
||||
const VKPipelineDataGraphics &graphics,
|
||||
VKBoundPipelines &r_bound_pipelines);
|
||||
|
||||
/**
|
||||
* Record the commands to the given command buffer to bind the descriptor set, pipeline and push
|
||||
* constants.
|
||||
*
|
||||
* Descriptor set and pipeline are only bound, when they are different than the last bound. The
|
||||
* r_bound_pipelines are checked to identify if they are the last bound. Descriptor set and
|
||||
* pipeline are bound at the given pipeline bind point.
|
||||
*
|
||||
* storage_push_constants contains all the push constants of the render graph. The
|
||||
* pipeline_data.push_constants_range contains the valid range that needs to be bound. Any
|
||||
* available push constants in the pipeline data always update the shader stages provided by
|
||||
* `vk_shader_stage_flags`.
|
||||
*/
|
||||
void vk_pipeline_data_build_commands(VKCommandBufferInterface &command_buffer,
|
||||
const VKPipelineData &pipeline_data,
|
||||
Span<uint8_t> storage_push_constants,
|
||||
VKBoundPipeline &r_bound_pipeline,
|
||||
VkPipelineBindPoint vk_pipeline_bind_point,
|
||||
VkShaderStageFlags vk_shader_stage_flags);
|
||||
|
||||
void vk_index_buffer_binding_build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const VKIndexBufferBinding &index_buffer_binding);
|
||||
void vk_index_buffer_binding_build_commands(VKCommandBufferInterface &command_buffer,
|
||||
const VKIndexBufferBinding &index_buffer_binding,
|
||||
VKIndexBufferBinding &r_bound_index_buffer);
|
||||
void vk_vertex_buffer_bindings_build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const VKVertexBufferBindings &vertex_buffers);
|
||||
void vk_vertex_buffer_bindings_build_commands(VKCommandBufferInterface &command_buffer,
|
||||
const VKVertexBufferBindings &vertex_buffer_bindings,
|
||||
VKVertexBufferBindings &r_bound_vertex_buffers);
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,70 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKResetQueryPoolData {
|
||||
VkQueryPool vk_query_pool;
|
||||
uint32_t first_query;
|
||||
uint32_t query_count;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset query pool.
|
||||
*
|
||||
* - Contains logic to copy relevant data to the VKRenderGraphNode.
|
||||
* - Determine read/write resource dependencies.
|
||||
* - Add commands to a command builder.
|
||||
*/
|
||||
class VKResetQueryPoolNode : public VKNodeInfo<VKNodeType::RESET_QUERY_POOL,
|
||||
VKResetQueryPoolData,
|
||||
VKResetQueryPoolData,
|
||||
VK_PIPELINE_STAGE_NONE,
|
||||
VKResourceType::NONE> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
void set_node_data(Node &node, Storage & /*storage*/, const CreateInfo &create_info)
|
||||
{
|
||||
node.reset_query_pool = create_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker & /*resources*/,
|
||||
VKRenderGraphLinks & /*links*/,
|
||||
const CreateInfo & /*create_info*/) override
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> /*storage_push_constants*/,
|
||||
VKBoundPipelines & /*r_bound_pipelines*/) override
|
||||
{
|
||||
command_buffer.reset_query_pool(data.vk_query_pool, data.first_query, data.query_count);
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,74 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKSynchronizationData {};
|
||||
|
||||
/**
|
||||
* Information needed to add a node to the render graph.
|
||||
*/
|
||||
struct VKSynchronizationCreateInfo {
|
||||
VkImage vk_image;
|
||||
VkImageLayout vk_image_layout;
|
||||
VkImageAspectFlags vk_image_aspect;
|
||||
};
|
||||
|
||||
class VKSynchronizationNode : public VKNodeInfo<VKNodeType::SYNCHRONIZATION,
|
||||
VKSynchronizationCreateInfo,
|
||||
VKSynchronizationData,
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||
VKResourceType::IMAGE | VKResourceType::BUFFER> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
static void set_node_data(Node &node, Storage & /* storage */, const CreateInfo &create_info)
|
||||
{
|
||||
UNUSED_VARS(create_info);
|
||||
node.synchronization = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const CreateInfo &create_info) override
|
||||
{
|
||||
ResourceWithStamp resource = resources.get_image_and_increase_stamp(create_info.vk_image);
|
||||
links.images.append(
|
||||
{{resource, VK_ACCESS_NONE}, create_info.vk_image_layout, create_info.vk_image_aspect});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> /*storage_push_constants*/,
|
||||
VKBoundPipelines & /*r_bound_pipelines*/) override
|
||||
{
|
||||
UNUSED_VARS(command_buffer, data);
|
||||
/* Intentionally left empty: A pipeline barrier has already been send to the command buffer.
|
||||
*/
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,72 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKUpdateBufferData {
|
||||
VkBuffer dst_buffer;
|
||||
VkDeviceSize dst_offset;
|
||||
VkDeviceSize data_size;
|
||||
void *data;
|
||||
};
|
||||
|
||||
class VKUpdateBufferNode : public VKNodeInfo<VKNodeType::UPDATE_BUFFER,
|
||||
VKUpdateBufferData,
|
||||
VKUpdateBufferData,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VKResourceType::BUFFER> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
static void set_node_data(Node &node, Storage & /* storage */, const CreateInfo &create_info)
|
||||
{
|
||||
node.update_buffer = create_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const CreateInfo &create_info) override
|
||||
{
|
||||
ResourceWithStamp dst_resource = resources.get_buffer_and_increase_stamp(
|
||||
create_info.dst_buffer);
|
||||
links.buffers.append({dst_resource, VK_ACCESS_TRANSFER_WRITE_BIT});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> /*storage_push_constants*/,
|
||||
VKBoundPipelines & /*r_bound_pipelines*/) override
|
||||
{
|
||||
command_buffer.update_buffer(data.dst_buffer, data.dst_offset, data.data_size, data.data);
|
||||
}
|
||||
|
||||
void free_data(Data &data)
|
||||
{
|
||||
MEM_delete_void(data.data);
|
||||
data.data = nullptr;
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,145 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_vector.hh"
|
||||
|
||||
#include "vk_node_info.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
/**
|
||||
* Information stored inside the render graph node. See `VKRenderGraphNode`.
|
||||
*/
|
||||
struct VKUpdateMipmapsData {
|
||||
VkImage vk_image;
|
||||
VkImageAspectFlags vk_image_aspect;
|
||||
int mipmaps;
|
||||
int layer_count;
|
||||
int3 l0_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update mipmaps Node
|
||||
*/
|
||||
class VKUpdateMipmapsNode : public VKNodeInfo<VKNodeType::UPDATE_MIPMAPS,
|
||||
VKUpdateMipmapsData,
|
||||
VKUpdateMipmapsData,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VKResourceType::IMAGE> {
|
||||
public:
|
||||
/**
|
||||
* Update the node data with the data inside create_info.
|
||||
*
|
||||
* Has been implemented as a template to ensure all node specific data
|
||||
* (`VK*Data`/`VK*CreateInfo`) types can be included in the same header file as the logic. The
|
||||
* actual node data (`VKRenderGraphNode` includes all header files.)
|
||||
*/
|
||||
template<typename Node, typename Storage>
|
||||
void set_node_data(Node &node, Storage & /*storage*/, const CreateInfo &create_info)
|
||||
{
|
||||
node.update_mipmaps = create_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies from `create_info` and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const CreateInfo &create_info) override
|
||||
{
|
||||
ResourceWithStamp resource = resources.get_image_and_increase_stamp(create_info.vk_image);
|
||||
links.images.append({{resource, VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT},
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
create_info.vk_image_aspect});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the commands and add them to the command_buffer.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
Data &data,
|
||||
Span<uint8_t> /*storage_push_constants*/,
|
||||
VKBoundPipelines & /*r_bound_pipelines*/) override
|
||||
{
|
||||
VkImageMemoryBarrier image_memory_barrier = {};
|
||||
image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
image_memory_barrier.pNext = nullptr;
|
||||
image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
image_memory_barrier.image = data.vk_image;
|
||||
image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
image_memory_barrier.subresourceRange.aspectMask = data.vk_image_aspect;
|
||||
image_memory_barrier.subresourceRange.baseArrayLayer = 0;
|
||||
image_memory_barrier.subresourceRange.layerCount = data.layer_count;
|
||||
image_memory_barrier.subresourceRange.baseMipLevel = 0;
|
||||
image_memory_barrier.subresourceRange.levelCount = 1;
|
||||
|
||||
VkImageBlit image_blit = {};
|
||||
image_blit.srcSubresource.aspectMask = data.vk_image_aspect;
|
||||
image_blit.srcSubresource.layerCount = data.layer_count;
|
||||
image_blit.srcSubresource.mipLevel = 1;
|
||||
image_blit.dstSubresource.aspectMask = data.vk_image_aspect;
|
||||
image_blit.dstSubresource.layerCount = data.layer_count;
|
||||
image_blit.dstSubresource.mipLevel = 1;
|
||||
|
||||
int3 dst_size = data.l0_size;
|
||||
for (int src_mipmap : IndexRange(data.mipmaps - 1)) {
|
||||
int dst_mipmap = src_mipmap + 1;
|
||||
int3 src_size = dst_size;
|
||||
dst_size = math::max(src_size / 2, int3(1));
|
||||
|
||||
/* Update the source mipmap level to be in src optimal layout. */
|
||||
image_memory_barrier.subresourceRange.baseMipLevel = src_mipmap;
|
||||
command_buffer.pipeline_barrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_DEPENDENCY_BY_REGION_BIT,
|
||||
0,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
1,
|
||||
&image_memory_barrier);
|
||||
|
||||
/* Blit the source mipmap level into the destination mipmap level. */
|
||||
image_blit.srcSubresource.mipLevel = src_mipmap;
|
||||
image_blit.srcOffsets[1] = {src_size.x, src_size.y, src_size.z};
|
||||
image_blit.dstSubresource.mipLevel = dst_mipmap;
|
||||
image_blit.dstOffsets[1] = {dst_size.x, dst_size.y, dst_size.z};
|
||||
command_buffer.blit_image(data.vk_image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
data.vk_image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1,
|
||||
&image_blit,
|
||||
VK_FILTER_LINEAR);
|
||||
}
|
||||
|
||||
/* Ensure that all mipmap levels are in the VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL layout.
|
||||
* This is the last known layout that the render graph knows about. */
|
||||
image_memory_barrier.subresourceRange.baseMipLevel = 0;
|
||||
image_memory_barrier.subresourceRange.levelCount = data.mipmaps - 1;
|
||||
image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
command_buffer.pipeline_barrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_DEPENDENCY_BY_REGION_BIT,
|
||||
0,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
1,
|
||||
&image_memory_barrier);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,381 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "vk_render_graph_test_types.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
class VKRenderGraphTestCompute : public VKRenderGraphTest {};
|
||||
|
||||
TEST_F(VKRenderGraphTestCompute, dispatch_read_back)
|
||||
{
|
||||
VkHandle<VkBuffer> buffer(1u);
|
||||
VkHandle<VkPipeline> pipeline(2u);
|
||||
VkHandle<VkPipelineLayout> pipeline_layout(3u);
|
||||
VkHandle<VkDescriptorSet> descriptor_set(4u);
|
||||
|
||||
resources.add_buffer(buffer);
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.buffers.append({buffer, VK_ACCESS_SHADER_WRITE_BIT});
|
||||
VKDispatchNode::CreateInfo dispatch_info(access_info);
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_pipeline = pipeline;
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_pipeline_layout = pipeline_layout;
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_descriptor_set = descriptor_set;
|
||||
dispatch_info.dispatch_node.group_count_x = 1;
|
||||
dispatch_info.dispatch_node.group_count_y = 1;
|
||||
dispatch_info.dispatch_node.group_count_z = 1;
|
||||
render_graph->add_node(dispatch_info);
|
||||
submit(render_graph, command_buffer);
|
||||
EXPECT_EQ(3, log.size());
|
||||
EXPECT_EQ("bind_pipeline(pipeline_bind_point=VK_PIPELINE_BIND_POINT_COMPUTE, pipeline=0x2)",
|
||||
log[0]);
|
||||
EXPECT_EQ(
|
||||
"bind_descriptor_sets(pipeline_bind_point=VK_PIPELINE_BIND_POINT_COMPUTE, layout=0x3, "
|
||||
"p_descriptor_sets=0x4)",
|
||||
log[1]);
|
||||
EXPECT_EQ("dispatch(group_count_x=1, group_count_y=1, group_count_z=1)", log[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the descriptor sets are updated once when chaining dispatching.
|
||||
*/
|
||||
TEST_F(VKRenderGraphTestCompute, dispatch_dispatch_read_back)
|
||||
{
|
||||
VkHandle<VkBuffer> buffer(1u);
|
||||
VkHandle<VkPipeline> pipeline(2u);
|
||||
VkHandle<VkPipelineLayout> pipeline_layout(3u);
|
||||
VkHandle<VkDescriptorSet> descriptor_set(4u);
|
||||
|
||||
resources.add_buffer(buffer);
|
||||
|
||||
{
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.buffers.append({buffer, VK_ACCESS_SHADER_WRITE_BIT});
|
||||
VKDispatchNode::CreateInfo dispatch_info(access_info);
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_pipeline = pipeline;
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_pipeline_layout = pipeline_layout;
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_descriptor_set = descriptor_set;
|
||||
dispatch_info.dispatch_node.group_count_x = 1;
|
||||
dispatch_info.dispatch_node.group_count_y = 1;
|
||||
dispatch_info.dispatch_node.group_count_z = 1;
|
||||
render_graph->add_node(dispatch_info);
|
||||
}
|
||||
{
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.buffers.append({buffer, VK_ACCESS_SHADER_WRITE_BIT});
|
||||
VKDispatchNode::CreateInfo dispatch_info(access_info);
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_pipeline = pipeline;
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_pipeline_layout = pipeline_layout;
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_descriptor_set = descriptor_set;
|
||||
dispatch_info.dispatch_node.group_count_x = 2;
|
||||
dispatch_info.dispatch_node.group_count_y = 2;
|
||||
dispatch_info.dispatch_node.group_count_z = 2;
|
||||
render_graph->add_node(dispatch_info);
|
||||
}
|
||||
submit(render_graph, command_buffer);
|
||||
EXPECT_EQ(5, log.size());
|
||||
EXPECT_EQ("bind_pipeline(pipeline_bind_point=VK_PIPELINE_BIND_POINT_COMPUTE, pipeline=0x2)",
|
||||
log[0]);
|
||||
EXPECT_EQ(
|
||||
"bind_descriptor_sets(pipeline_bind_point=VK_PIPELINE_BIND_POINT_COMPUTE, layout=0x3, "
|
||||
"p_descriptor_sets=0x4)",
|
||||
log[1]);
|
||||
EXPECT_EQ("dispatch(group_count_x=1, group_count_y=1, group_count_z=1)", log[2]);
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT" +
|
||||
endl() +
|
||||
" - buffer_barrier(src_access_mask=VK_ACCESS_SHADER_WRITE_BIT, "
|
||||
"dst_access_mask=VK_ACCESS_SHADER_WRITE_BIT, buffer=0x1, offset=0, "
|
||||
"size=18446744073709551615)" +
|
||||
endl() + ")",
|
||||
log[3]);
|
||||
EXPECT_EQ("dispatch(group_count_x=2, group_count_y=2, group_count_z=2)", log[4]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the descriptor sets are updated when chaining dispatching with different descriptor
|
||||
* sets.
|
||||
*/
|
||||
TEST_F(VKRenderGraphTestCompute, dispatch_dispatch_read_back_with_changing_descriptor_sets)
|
||||
{
|
||||
VkHandle<VkBuffer> buffer(1u);
|
||||
VkHandle<VkPipeline> pipeline(2u);
|
||||
VkHandle<VkPipelineLayout> pipeline_layout(3u);
|
||||
VkHandle<VkDescriptorSet> descriptor_set_a(4u);
|
||||
VkHandle<VkDescriptorSet> descriptor_set_b(5u);
|
||||
|
||||
resources.add_buffer(buffer);
|
||||
|
||||
{
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.buffers.append({buffer, VK_ACCESS_SHADER_WRITE_BIT});
|
||||
VKDispatchNode::CreateInfo dispatch_info(access_info);
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_pipeline = pipeline;
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_pipeline_layout = pipeline_layout;
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_descriptor_set = descriptor_set_a;
|
||||
dispatch_info.dispatch_node.group_count_x = 1;
|
||||
dispatch_info.dispatch_node.group_count_y = 1;
|
||||
dispatch_info.dispatch_node.group_count_z = 1;
|
||||
render_graph->add_node(dispatch_info);
|
||||
}
|
||||
{
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.buffers.append({buffer, VK_ACCESS_SHADER_WRITE_BIT});
|
||||
VKDispatchNode::CreateInfo dispatch_info(access_info);
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_pipeline = pipeline;
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_pipeline_layout = pipeline_layout;
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_descriptor_set = descriptor_set_b;
|
||||
dispatch_info.dispatch_node.group_count_x = 2;
|
||||
dispatch_info.dispatch_node.group_count_y = 2;
|
||||
dispatch_info.dispatch_node.group_count_z = 2;
|
||||
render_graph->add_node(dispatch_info);
|
||||
}
|
||||
submit(render_graph, command_buffer);
|
||||
EXPECT_EQ(6, log.size());
|
||||
EXPECT_EQ("bind_pipeline(pipeline_bind_point=VK_PIPELINE_BIND_POINT_COMPUTE, pipeline=0x2)",
|
||||
log[0]);
|
||||
EXPECT_EQ(
|
||||
"bind_descriptor_sets(pipeline_bind_point=VK_PIPELINE_BIND_POINT_COMPUTE, layout=0x3, "
|
||||
"p_descriptor_sets=0x4)",
|
||||
log[1]);
|
||||
EXPECT_EQ("dispatch(group_count_x=1, group_count_y=1, group_count_z=1)", log[2]);
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT" +
|
||||
endl() +
|
||||
" - buffer_barrier(src_access_mask=VK_ACCESS_SHADER_WRITE_BIT, "
|
||||
"dst_access_mask=VK_ACCESS_SHADER_WRITE_BIT, buffer=0x1, offset=0, "
|
||||
"size=18446744073709551615)" +
|
||||
endl() + ")",
|
||||
log[3]);
|
||||
EXPECT_EQ(
|
||||
"bind_descriptor_sets(pipeline_bind_point=VK_PIPELINE_BIND_POINT_COMPUTE, layout=0x3, "
|
||||
"p_descriptor_sets=0x5)",
|
||||
log[4]);
|
||||
EXPECT_EQ("dispatch(group_count_x=2, group_count_y=2, group_count_z=2)", log[5]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the descriptor sets are updated when chaining dispatching with different pipelines.
|
||||
*/
|
||||
TEST_F(VKRenderGraphTestCompute, dispatch_dispatch_read_back_with_changing_pipelines)
|
||||
{
|
||||
VkHandle<VkBuffer> buffer(1u);
|
||||
VkHandle<VkPipeline> pipeline_a(2u);
|
||||
VkHandle<VkPipeline> pipeline_b(3u);
|
||||
VkHandle<VkPipelineLayout> pipeline_layout(4u);
|
||||
VkHandle<VkDescriptorSet> descriptor_set(5u);
|
||||
|
||||
resources.add_buffer(buffer);
|
||||
|
||||
{
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.buffers.append({buffer, VK_ACCESS_SHADER_WRITE_BIT});
|
||||
VKDispatchNode::CreateInfo dispatch_info(access_info);
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_pipeline = pipeline_a;
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_pipeline_layout = pipeline_layout;
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_descriptor_set = descriptor_set;
|
||||
dispatch_info.dispatch_node.group_count_x = 1;
|
||||
dispatch_info.dispatch_node.group_count_y = 1;
|
||||
dispatch_info.dispatch_node.group_count_z = 1;
|
||||
render_graph->add_node(dispatch_info);
|
||||
}
|
||||
{
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.buffers.append({buffer, VK_ACCESS_SHADER_WRITE_BIT});
|
||||
VKDispatchNode::CreateInfo dispatch_info(access_info);
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_pipeline = pipeline_b;
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_pipeline_layout = pipeline_layout;
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_descriptor_set = descriptor_set;
|
||||
dispatch_info.dispatch_node.group_count_x = 2;
|
||||
dispatch_info.dispatch_node.group_count_y = 2;
|
||||
dispatch_info.dispatch_node.group_count_z = 2;
|
||||
render_graph->add_node(dispatch_info);
|
||||
}
|
||||
submit(render_graph, command_buffer);
|
||||
EXPECT_EQ(6, log.size());
|
||||
EXPECT_EQ("bind_pipeline(pipeline_bind_point=VK_PIPELINE_BIND_POINT_COMPUTE, pipeline=0x2)",
|
||||
log[0]);
|
||||
EXPECT_EQ(
|
||||
"bind_descriptor_sets(pipeline_bind_point=VK_PIPELINE_BIND_POINT_COMPUTE, layout=0x4, "
|
||||
"p_descriptor_sets=0x5)",
|
||||
log[1]);
|
||||
EXPECT_EQ("dispatch(group_count_x=1, group_count_y=1, group_count_z=1)", log[2]);
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT" +
|
||||
endl() +
|
||||
" - buffer_barrier(src_access_mask=VK_ACCESS_SHADER_WRITE_BIT, "
|
||||
"dst_access_mask=VK_ACCESS_SHADER_WRITE_BIT, buffer=0x1, offset=0, "
|
||||
"size=18446744073709551615)" +
|
||||
endl() + ")",
|
||||
log[3]);
|
||||
EXPECT_EQ("bind_pipeline(pipeline_bind_point=VK_PIPELINE_BIND_POINT_COMPUTE, pipeline=0x3)",
|
||||
log[4]);
|
||||
EXPECT_EQ("dispatch(group_count_x=2, group_count_y=2, group_count_z=2)", log[5]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the descriptor sets are updated when chaining dispatching with different pipelines and
|
||||
* descriptor sets.
|
||||
*/
|
||||
TEST_F(VKRenderGraphTestCompute,
|
||||
dispatch_dispatch_read_back_with_changing_pipelines_descriptor_sets)
|
||||
{
|
||||
VkHandle<VkBuffer> buffer(1u);
|
||||
VkHandle<VkPipeline> pipeline_a(2u);
|
||||
VkHandle<VkPipeline> pipeline_b(3u);
|
||||
VkHandle<VkPipelineLayout> pipeline_layout(4u);
|
||||
VkHandle<VkDescriptorSet> descriptor_set_a(5u);
|
||||
VkHandle<VkDescriptorSet> descriptor_set_b(6u);
|
||||
|
||||
resources.add_buffer(buffer);
|
||||
|
||||
{
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.buffers.append({buffer, VK_ACCESS_SHADER_WRITE_BIT});
|
||||
VKDispatchNode::CreateInfo dispatch_info(access_info);
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_pipeline = pipeline_a;
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_pipeline_layout = pipeline_layout;
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_descriptor_set = descriptor_set_a;
|
||||
dispatch_info.dispatch_node.group_count_x = 1;
|
||||
dispatch_info.dispatch_node.group_count_y = 1;
|
||||
dispatch_info.dispatch_node.group_count_z = 1;
|
||||
render_graph->add_node(dispatch_info);
|
||||
}
|
||||
{
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.buffers.append({buffer, VK_ACCESS_SHADER_WRITE_BIT});
|
||||
VKDispatchNode::CreateInfo dispatch_info(access_info);
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_pipeline = pipeline_b;
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_pipeline_layout = pipeline_layout;
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_descriptor_set = descriptor_set_b;
|
||||
dispatch_info.dispatch_node.group_count_x = 2;
|
||||
dispatch_info.dispatch_node.group_count_y = 2;
|
||||
dispatch_info.dispatch_node.group_count_z = 2;
|
||||
render_graph->add_node(dispatch_info);
|
||||
}
|
||||
submit(render_graph, command_buffer);
|
||||
EXPECT_EQ(7, log.size());
|
||||
EXPECT_EQ("bind_pipeline(pipeline_bind_point=VK_PIPELINE_BIND_POINT_COMPUTE, pipeline=0x2)",
|
||||
log[0]);
|
||||
EXPECT_EQ(
|
||||
"bind_descriptor_sets(pipeline_bind_point=VK_PIPELINE_BIND_POINT_COMPUTE, layout=0x4, "
|
||||
"p_descriptor_sets=0x5)",
|
||||
log[1]);
|
||||
EXPECT_EQ("dispatch(group_count_x=1, group_count_y=1, group_count_z=1)", log[2]);
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT" +
|
||||
endl() +
|
||||
" - buffer_barrier(src_access_mask=VK_ACCESS_SHADER_WRITE_BIT, "
|
||||
"dst_access_mask=VK_ACCESS_SHADER_WRITE_BIT, buffer=0x1, offset=0, "
|
||||
"size=18446744073709551615)" +
|
||||
endl() + ")",
|
||||
log[3]);
|
||||
EXPECT_EQ("bind_pipeline(pipeline_bind_point=VK_PIPELINE_BIND_POINT_COMPUTE, pipeline=0x3)",
|
||||
log[4]);
|
||||
EXPECT_EQ(
|
||||
"bind_descriptor_sets(pipeline_bind_point=VK_PIPELINE_BIND_POINT_COMPUTE, layout=0x4, "
|
||||
"p_descriptor_sets=0x6)",
|
||||
log[5]);
|
||||
EXPECT_EQ("dispatch(group_count_x=2, group_count_y=2, group_count_z=2)", log[6]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test dispatch indirect
|
||||
*/
|
||||
TEST_F(VKRenderGraphTestCompute, dispatch_indirect_read_back)
|
||||
{
|
||||
VkHandle<VkBuffer> buffer(1u);
|
||||
VkHandle<VkBuffer> command_buffer(2u);
|
||||
VkHandle<VkPipeline> pipeline(3u);
|
||||
VkHandle<VkPipelineLayout> pipeline_layout(4u);
|
||||
VkHandle<VkDescriptorSet> descriptor_set(5u);
|
||||
|
||||
resources.add_buffer(buffer);
|
||||
resources.add_buffer(command_buffer);
|
||||
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.buffers.append({buffer, VK_ACCESS_SHADER_WRITE_BIT});
|
||||
VKDispatchIndirectNode::CreateInfo dispatch_indirect_info(access_info);
|
||||
dispatch_indirect_info.dispatch_indirect_node.pipeline_data.vk_pipeline = pipeline;
|
||||
dispatch_indirect_info.dispatch_indirect_node.pipeline_data.vk_pipeline_layout = pipeline_layout;
|
||||
dispatch_indirect_info.dispatch_indirect_node.pipeline_data.vk_descriptor_set = descriptor_set;
|
||||
dispatch_indirect_info.dispatch_indirect_node.buffer = command_buffer;
|
||||
dispatch_indirect_info.dispatch_indirect_node.offset = 0;
|
||||
render_graph->add_node(dispatch_indirect_info);
|
||||
submit(render_graph, this->command_buffer);
|
||||
EXPECT_EQ(3, log.size());
|
||||
EXPECT_EQ("bind_pipeline(pipeline_bind_point=VK_PIPELINE_BIND_POINT_COMPUTE, pipeline=0x3)",
|
||||
log[0]);
|
||||
EXPECT_EQ(
|
||||
"bind_descriptor_sets(pipeline_bind_point=VK_PIPELINE_BIND_POINT_COMPUTE, layout=0x4, "
|
||||
"p_descriptor_sets=0x5)",
|
||||
log[1]);
|
||||
EXPECT_EQ("dispatch_indirect(buffer=0x2, offset=0)", log[2]);
|
||||
}
|
||||
|
||||
TEST_F(VKRenderGraphTestCompute, dispatch_indirect_dispatch_indirect_read_back)
|
||||
{
|
||||
VkHandle<VkBuffer> buffer(1u);
|
||||
VkHandle<VkBuffer> command_buffer(2u);
|
||||
VkHandle<VkPipeline> pipeline(3u);
|
||||
VkHandle<VkPipelineLayout> pipeline_layout(4u);
|
||||
VkHandle<VkDescriptorSet> descriptor_set(5u);
|
||||
|
||||
resources.add_buffer(buffer);
|
||||
resources.add_buffer(command_buffer);
|
||||
|
||||
{
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.buffers.append({buffer, VK_ACCESS_SHADER_WRITE_BIT});
|
||||
VKDispatchIndirectNode::CreateInfo dispatch_indirect_info(access_info);
|
||||
dispatch_indirect_info.dispatch_indirect_node.pipeline_data.vk_pipeline = pipeline;
|
||||
dispatch_indirect_info.dispatch_indirect_node.pipeline_data.vk_pipeline_layout =
|
||||
pipeline_layout;
|
||||
dispatch_indirect_info.dispatch_indirect_node.pipeline_data.vk_descriptor_set = descriptor_set;
|
||||
dispatch_indirect_info.dispatch_indirect_node.buffer = command_buffer;
|
||||
dispatch_indirect_info.dispatch_indirect_node.offset = 0;
|
||||
render_graph->add_node(dispatch_indirect_info);
|
||||
}
|
||||
{
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.buffers.append({buffer, VK_ACCESS_SHADER_WRITE_BIT});
|
||||
VKDispatchIndirectNode::CreateInfo dispatch_indirect_info(access_info);
|
||||
dispatch_indirect_info.dispatch_indirect_node.pipeline_data.vk_pipeline = pipeline;
|
||||
dispatch_indirect_info.dispatch_indirect_node.pipeline_data.vk_pipeline_layout =
|
||||
pipeline_layout;
|
||||
dispatch_indirect_info.dispatch_indirect_node.pipeline_data.vk_descriptor_set = descriptor_set;
|
||||
dispatch_indirect_info.dispatch_indirect_node.buffer = command_buffer;
|
||||
dispatch_indirect_info.dispatch_indirect_node.offset = 12;
|
||||
render_graph->add_node(dispatch_indirect_info);
|
||||
}
|
||||
submit(render_graph, this->command_buffer);
|
||||
EXPECT_EQ(5, log.size());
|
||||
|
||||
EXPECT_EQ("bind_pipeline(pipeline_bind_point=VK_PIPELINE_BIND_POINT_COMPUTE, pipeline=0x3)",
|
||||
log[0]);
|
||||
EXPECT_EQ(
|
||||
"bind_descriptor_sets(pipeline_bind_point=VK_PIPELINE_BIND_POINT_COMPUTE, layout=0x4, "
|
||||
"p_descriptor_sets=0x5)",
|
||||
log[1]);
|
||||
EXPECT_EQ("dispatch_indirect(buffer=0x2, offset=0)", log[2]);
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, "
|
||||
"VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT" +
|
||||
endl() +
|
||||
" - buffer_barrier(src_access_mask=VK_ACCESS_SHADER_WRITE_BIT, "
|
||||
"dst_access_mask=VK_ACCESS_SHADER_WRITE_BIT, buffer=0x1, offset=0, "
|
||||
"size=18446744073709551615)" +
|
||||
endl() + ")",
|
||||
log[3]);
|
||||
EXPECT_EQ("dispatch_indirect(buffer=0x2, offset=12)", log[4]);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,94 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "vk_render_graph_test_types.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
class VKRenderGraphTestPresent : public VKRenderGraphTest {};
|
||||
|
||||
TEST_F(VKRenderGraphTestPresent, transfer_and_present)
|
||||
{
|
||||
VkHandle<VkImage> back_buffer(1u);
|
||||
|
||||
resources.add_image(back_buffer, false);
|
||||
{
|
||||
render_graph::VKSynchronizationNode::CreateInfo synchronization = {};
|
||||
synchronization.vk_image = back_buffer;
|
||||
synchronization.vk_image_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
synchronization.vk_image_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
render_graph->add_node(synchronization);
|
||||
}
|
||||
|
||||
submit(render_graph, command_buffer);
|
||||
|
||||
EXPECT_EQ(1, log.size());
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=, dst_access_mask=, "
|
||||
"old_layout=VK_IMAGE_LAYOUT_UNDEFINED, "
|
||||
"new_layout=VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, image=0x1, subresource_range=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, level_count=4294967295, "
|
||||
"base_array_layer=0, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[0]);
|
||||
}
|
||||
|
||||
TEST_F(VKRenderGraphTestPresent, clear_and_present)
|
||||
{
|
||||
VkHandle<VkImage> back_buffer(1u);
|
||||
|
||||
resources.add_image(back_buffer, false);
|
||||
|
||||
VKClearColorImageNode::CreateInfo clear_color_image = {};
|
||||
clear_color_image.vk_image = back_buffer;
|
||||
render_graph->add_node(clear_color_image);
|
||||
|
||||
{
|
||||
render_graph::VKSynchronizationNode::CreateInfo synchronization = {};
|
||||
synchronization.vk_image = back_buffer;
|
||||
synchronization.vk_image_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
synchronization.vk_image_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
render_graph->add_node(synchronization);
|
||||
}
|
||||
|
||||
submit(render_graph, command_buffer);
|
||||
|
||||
EXPECT_EQ(3, log.size());
|
||||
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=, dst_access_mask=VK_ACCESS_TRANSFER_WRITE_BIT, "
|
||||
"old_layout=VK_IMAGE_LAYOUT_UNDEFINED, "
|
||||
"new_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, image=0x1, subresource_range=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, level_count=4294967295, "
|
||||
"base_array_layer=0, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[0]);
|
||||
EXPECT_EQ("clear_color_image(image=0x1, image_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)",
|
||||
log[1]);
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=VK_ACCESS_TRANSFER_WRITE_BIT, "
|
||||
"dst_access_mask=, "
|
||||
"old_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, "
|
||||
"new_layout=VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, image=0x1, subresource_range=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, level_count=4294967295, "
|
||||
"base_array_layer=0, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[2]);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,621 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "vk_render_graph_test_types.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
class VKRenderGraphTestRender : public VKRenderGraphTest_P {};
|
||||
|
||||
TEST_P(VKRenderGraphTestRender, begin_clear_attachments_end_read_back)
|
||||
{
|
||||
VkHandle<VkImage> image(1u);
|
||||
VkHandle<VkImageView> image_view(2u);
|
||||
VkHandle<VkBuffer> buffer(3u);
|
||||
|
||||
resources.add_image(image, false);
|
||||
resources.add_buffer(buffer);
|
||||
|
||||
{
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.images.append(
|
||||
{image, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_ASPECT_COLOR_BIT, {}});
|
||||
VKBeginRenderingNode::CreateInfo begin_rendering(access_info);
|
||||
begin_rendering.node_data.color_attachments[0].sType =
|
||||
VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR;
|
||||
begin_rendering.node_data.color_attachments[0].imageLayout = color_attachment_layout();
|
||||
begin_rendering.node_data.color_attachments[0].imageView = image_view;
|
||||
begin_rendering.node_data.color_attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
begin_rendering.node_data.color_attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
begin_rendering.node_data.vk_rendering_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
|
||||
begin_rendering.node_data.vk_rendering_info.colorAttachmentCount = 1;
|
||||
begin_rendering.node_data.vk_rendering_info.layerCount = 1;
|
||||
begin_rendering.node_data.vk_rendering_info.pColorAttachments =
|
||||
begin_rendering.node_data.color_attachments;
|
||||
|
||||
render_graph->add_node(begin_rendering);
|
||||
}
|
||||
|
||||
{
|
||||
VKClearAttachmentsNode::CreateInfo clear_attachments = {};
|
||||
clear_attachments.attachment_count = 1;
|
||||
clear_attachments.attachments[0].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
clear_attachments.attachments[0].clearValue.color.float32[0] = 0.2;
|
||||
clear_attachments.attachments[0].clearValue.color.float32[1] = 0.4;
|
||||
clear_attachments.attachments[0].clearValue.color.float32[2] = 0.6;
|
||||
clear_attachments.attachments[0].clearValue.color.float32[3] = 1.0;
|
||||
clear_attachments.attachments[0].colorAttachment = 0;
|
||||
clear_attachments.vk_clear_rect.baseArrayLayer = 0;
|
||||
clear_attachments.vk_clear_rect.layerCount = 1;
|
||||
clear_attachments.vk_clear_rect.rect.extent.width = 1920;
|
||||
clear_attachments.vk_clear_rect.rect.extent.height = 1080;
|
||||
render_graph->add_node(clear_attachments);
|
||||
}
|
||||
|
||||
{
|
||||
VKEndRenderingNode::CreateInfo end_rendering = {};
|
||||
render_graph->add_node(end_rendering);
|
||||
}
|
||||
|
||||
{
|
||||
VKCopyImageToBufferNode::CreateInfo copy_image_to_buffer = {};
|
||||
copy_image_to_buffer.node_data.src_image = image;
|
||||
copy_image_to_buffer.node_data.dst_buffer = buffer;
|
||||
copy_image_to_buffer.node_data.region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
copy_image_to_buffer.vk_image_aspects = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
render_graph->add_node(copy_image_to_buffer);
|
||||
}
|
||||
|
||||
submit(render_graph, command_buffer);
|
||||
|
||||
EXPECT_EQ(6, log.size());
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=, "
|
||||
"dst_access_mask=VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, "
|
||||
"old_layout=VK_IMAGE_LAYOUT_UNDEFINED, "
|
||||
"new_layout=" +
|
||||
color_attachment_layout_str() +
|
||||
", image=0x1, "
|
||||
"subresource_range=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, level_count=4294967295, "
|
||||
"base_array_layer=0, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[0]);
|
||||
EXPECT_EQ("begin_rendering(p_rendering_info=flags=, render_area=" + endl() +
|
||||
" offset=" + endl() + " x=0, y=0 , extent=" + endl() +
|
||||
" width=0, height=0 , layer_count=1, view_mask=0, color_attachment_count=1, "
|
||||
"p_color_attachments=" +
|
||||
endl() + " image_view=0x2, image_layout=" + color_attachment_layout_str() +
|
||||
", "
|
||||
"resolve_mode=VK_RESOLVE_MODE_NONE, resolve_image_view=0, "
|
||||
"resolve_image_layout=VK_IMAGE_LAYOUT_UNDEFINED, "
|
||||
"load_op=VK_ATTACHMENT_LOAD_OP_DONT_CARE, store_op=VK_ATTACHMENT_STORE_OP_STORE" +
|
||||
endl() + ")",
|
||||
log[1]);
|
||||
EXPECT_EQ(
|
||||
"clear_attachments( - attachment(aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, "
|
||||
"color_attachment=0)" +
|
||||
endl() + " - rect(rect=" + endl() + " offset=" + endl() +
|
||||
" x=0, y=0 , extent=" + endl() +
|
||||
" width=1920, height=1080 , base_array_layer=0, layer_count=1)" + endl() + ")",
|
||||
log[2]);
|
||||
EXPECT_EQ("end_rendering()", log[3]);
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, "
|
||||
"dst_access_mask=VK_ACCESS_TRANSFER_READ_BIT, "
|
||||
"old_layout=" +
|
||||
color_attachment_layout_str() +
|
||||
", "
|
||||
"new_layout=VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image=0x1, subresource_range=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, level_count=4294967295, "
|
||||
"base_array_layer=0, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[4]);
|
||||
EXPECT_EQ(
|
||||
"copy_image_to_buffer(src_image=0x1, src_image_layout=VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, "
|
||||
"dst_buffer=0x3" +
|
||||
endl() +
|
||||
" - region(buffer_offset=0, buffer_row_length=0, buffer_image_height=0, "
|
||||
"image_subresource=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, mip_level=0, base_array_layer=0, "
|
||||
"layer_count=0 , image_offset=" +
|
||||
endl() + " x=0, y=0, z=0 , image_extent=" + endl() +
|
||||
" width=0, height=0, depth=0 )" + endl() + ")",
|
||||
log[5]);
|
||||
}
|
||||
|
||||
TEST_P(VKRenderGraphTestRender, begin_draw_end)
|
||||
{
|
||||
VkHandle<VkImage> image(1u);
|
||||
VkHandle<VkImageView> image_view(2u);
|
||||
VkHandle<VkPipelineLayout> pipeline_layout(4u);
|
||||
VkHandle<VkPipeline> pipeline(3u);
|
||||
|
||||
resources.add_image(image, false);
|
||||
|
||||
{
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.images.append(
|
||||
{image, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_ASPECT_COLOR_BIT, {}});
|
||||
VKBeginRenderingNode::CreateInfo begin_rendering(access_info);
|
||||
begin_rendering.node_data.color_attachments[0].sType =
|
||||
VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR;
|
||||
begin_rendering.node_data.color_attachments[0].imageLayout = color_attachment_layout();
|
||||
begin_rendering.node_data.color_attachments[0].imageView = image_view;
|
||||
begin_rendering.node_data.color_attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
begin_rendering.node_data.color_attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
begin_rendering.node_data.vk_rendering_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
|
||||
begin_rendering.node_data.vk_rendering_info.colorAttachmentCount = 1;
|
||||
begin_rendering.node_data.vk_rendering_info.layerCount = 1;
|
||||
begin_rendering.node_data.vk_rendering_info.pColorAttachments =
|
||||
begin_rendering.node_data.color_attachments;
|
||||
|
||||
render_graph->add_node(begin_rendering);
|
||||
}
|
||||
|
||||
{
|
||||
VKResourceAccessInfo access_info = {};
|
||||
VKDrawNode::CreateInfo draw(access_info);
|
||||
draw.node_data.first_instance = 0;
|
||||
draw.node_data.first_vertex = 0;
|
||||
draw.node_data.instance_count = 1;
|
||||
draw.node_data.vertex_count = 4;
|
||||
draw.node_data.graphics.pipeline_data.push_constants_range = IndexRange(0);
|
||||
draw.node_data.graphics.pipeline_data.vk_descriptor_set = VK_NULL_HANDLE;
|
||||
draw.node_data.graphics.pipeline_data.vk_pipeline = pipeline;
|
||||
draw.node_data.graphics.pipeline_data.vk_pipeline_layout = pipeline_layout;
|
||||
draw.node_data.graphics.viewport.viewports.append(VkViewport{});
|
||||
draw.node_data.graphics.viewport.scissors.append(VkRect2D{});
|
||||
render_graph->add_node(draw);
|
||||
}
|
||||
|
||||
{
|
||||
VKEndRenderingNode::CreateInfo end_rendering = {};
|
||||
render_graph->add_node(end_rendering);
|
||||
}
|
||||
|
||||
submit(render_graph, command_buffer);
|
||||
EXPECT_EQ(7, log.size());
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=, "
|
||||
"dst_access_mask=VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, "
|
||||
"old_layout=VK_IMAGE_LAYOUT_UNDEFINED, "
|
||||
"new_layout=" +
|
||||
color_attachment_layout_str() + ", image=0x1, subresource_range=" + endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, "
|
||||
"level_count=4294967295, base_array_layer=0, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[0]);
|
||||
EXPECT_EQ("begin_rendering(p_rendering_info=flags=, render_area=" + endl() +
|
||||
" offset=" + endl() + " x=0, y=0 , extent=" + endl() +
|
||||
" width=0, height=0 , layer_count=1, view_mask=0, "
|
||||
"color_attachment_count=1, p_color_attachments=" +
|
||||
endl() + " image_view=0x2, image_layout=" + color_attachment_layout_str() +
|
||||
", "
|
||||
"resolve_mode=VK_RESOLVE_MODE_NONE, resolve_image_view=0, "
|
||||
"resolve_image_layout=VK_IMAGE_LAYOUT_UNDEFINED, "
|
||||
"load_op=VK_ATTACHMENT_LOAD_OP_DONT_CARE, store_op=VK_ATTACHMENT_STORE_OP_STORE" +
|
||||
endl() + ")",
|
||||
log[1]);
|
||||
EXPECT_EQ("set_viewport(num_viewports=1)", log[2]);
|
||||
EXPECT_EQ("set_scissor(num_scissors=1)", log[3]);
|
||||
EXPECT_EQ("bind_pipeline(pipeline_bind_point=VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline=0x3)",
|
||||
log[4]);
|
||||
EXPECT_EQ("draw(vertex_count=4, instance_count=1, first_vertex=0, first_instance=0)", log[5]);
|
||||
EXPECT_EQ("end_rendering()", log[6]);
|
||||
}
|
||||
|
||||
TEST_P(VKRenderGraphTestRender, begin_draw_end__layered)
|
||||
{
|
||||
VkHandle<VkImage> image(1u);
|
||||
VkHandle<VkImageView> image_view(2u);
|
||||
VkHandle<VkPipelineLayout> pipeline_layout(4u);
|
||||
VkHandle<VkPipeline> pipeline(3u);
|
||||
|
||||
resources.add_image(image, true);
|
||||
|
||||
{
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.images.append({image,
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
{0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS}});
|
||||
VKBeginRenderingNode::CreateInfo begin_rendering(access_info);
|
||||
begin_rendering.node_data.color_attachments[0].sType =
|
||||
VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR;
|
||||
begin_rendering.node_data.color_attachments[0].imageLayout = color_attachment_layout();
|
||||
begin_rendering.node_data.color_attachments[0].imageView = image_view;
|
||||
begin_rendering.node_data.color_attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
begin_rendering.node_data.color_attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
begin_rendering.node_data.vk_rendering_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
|
||||
begin_rendering.node_data.vk_rendering_info.colorAttachmentCount = 1;
|
||||
begin_rendering.node_data.vk_rendering_info.layerCount = 1;
|
||||
begin_rendering.node_data.vk_rendering_info.pColorAttachments =
|
||||
begin_rendering.node_data.color_attachments;
|
||||
|
||||
render_graph->add_node(begin_rendering);
|
||||
}
|
||||
|
||||
{
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.images.append({image,
|
||||
VK_ACCESS_SHADER_READ_BIT,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
{0, VK_REMAINING_MIP_LEVELS, 1, VK_REMAINING_ARRAY_LAYERS}});
|
||||
VKDrawNode::CreateInfo draw(access_info);
|
||||
draw.node_data.first_instance = 0;
|
||||
draw.node_data.first_vertex = 0;
|
||||
draw.node_data.instance_count = 1;
|
||||
draw.node_data.vertex_count = 4;
|
||||
draw.node_data.graphics.pipeline_data.push_constants_range = IndexRange(0);
|
||||
draw.node_data.graphics.pipeline_data.vk_descriptor_set = VK_NULL_HANDLE;
|
||||
draw.node_data.graphics.pipeline_data.vk_pipeline = pipeline;
|
||||
draw.node_data.graphics.pipeline_data.vk_pipeline_layout = pipeline_layout;
|
||||
draw.node_data.graphics.viewport.viewports.append(VkViewport{});
|
||||
draw.node_data.graphics.viewport.scissors.append(VkRect2D{});
|
||||
render_graph->add_node(draw);
|
||||
}
|
||||
|
||||
{
|
||||
VKEndRenderingNode::CreateInfo end_rendering = {};
|
||||
render_graph->add_node(end_rendering);
|
||||
}
|
||||
|
||||
submit(render_graph, command_buffer);
|
||||
EXPECT_EQ(9, log.size());
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=, "
|
||||
"dst_access_mask=VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, "
|
||||
"old_layout=VK_IMAGE_LAYOUT_UNDEFINED, "
|
||||
"new_layout=" +
|
||||
color_attachment_layout_str() + ", image=0x1, subresource_range=" + endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, "
|
||||
"level_count=4294967295, base_array_layer=0, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[0]);
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, "
|
||||
"VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, "
|
||||
"VK_PIPELINE_STAGE_ALL_COMMANDS_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, "
|
||||
"VK_ACCESS_TRANSFER_WRITE_BIT, "
|
||||
"dst_access_mask=VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_SHADER_WRITE_BIT, "
|
||||
"VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, "
|
||||
"VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, "
|
||||
"old_layout=" +
|
||||
color_attachment_layout_str() +
|
||||
", "
|
||||
"new_layout=VK_IMAGE_LAYOUT_GENERAL, "
|
||||
"image=0x1, subresource_range=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, level_count=4294967295, "
|
||||
"base_array_layer=1, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[1]);
|
||||
EXPECT_EQ("begin_rendering(p_rendering_info=flags=, render_area=" + endl() +
|
||||
" offset=" + endl() + " x=0, y=0 , extent=" + endl() +
|
||||
" width=0, height=0 , layer_count=1, view_mask=0, "
|
||||
"color_attachment_count=1, p_color_attachments=" +
|
||||
endl() + " image_view=0x2, image_layout=" + color_attachment_layout_str() +
|
||||
", "
|
||||
"resolve_mode=VK_RESOLVE_MODE_NONE, resolve_image_view=0, "
|
||||
"resolve_image_layout=VK_IMAGE_LAYOUT_UNDEFINED, "
|
||||
"load_op=VK_ATTACHMENT_LOAD_OP_DONT_CARE, store_op=VK_ATTACHMENT_STORE_OP_STORE" +
|
||||
endl() + ")",
|
||||
log[2]);
|
||||
EXPECT_EQ("set_viewport(num_viewports=1)", log[3]);
|
||||
EXPECT_EQ("set_scissor(num_scissors=1)", log[4]);
|
||||
EXPECT_EQ("bind_pipeline(pipeline_bind_point=VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline=0x3)",
|
||||
log[5]);
|
||||
EXPECT_EQ("draw(vertex_count=4, instance_count=1, first_vertex=0, first_instance=0)", log[6]);
|
||||
EXPECT_EQ("end_rendering()", log[7]);
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_ALL_COMMANDS_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=VK_ACCESS_SHADER_READ_BIT, "
|
||||
"VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, "
|
||||
"VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, "
|
||||
"VK_ACCESS_TRANSFER_WRITE_BIT, dst_access_mask=VK_ACCESS_SHADER_READ_BIT, "
|
||||
"VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, "
|
||||
"VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, "
|
||||
"VK_ACCESS_TRANSFER_WRITE_BIT, old_layout=VK_IMAGE_LAYOUT_GENERAL, "
|
||||
"new_layout=" +
|
||||
color_attachment_layout_str() + ", image=0x1, subresource_range=" + endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, level_count=4294967295, "
|
||||
"base_array_layer=1, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[8]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a synchronization barrier is generated for input attachment reads on layered images
|
||||
* after a color attachment write. Regression test for #158501.
|
||||
*/
|
||||
TEST_P(VKRenderGraphTestRender, begin_draw_begin_draw_end__layered_input_attachment)
|
||||
{
|
||||
VkHandle<VkImage> image(1u);
|
||||
VkHandle<VkImageView> image_view(2u);
|
||||
VkHandle<VkPipelineLayout> pipeline_layout(4u);
|
||||
VkHandle<VkPipeline> pipeline(3u);
|
||||
|
||||
resources.add_image(image, true);
|
||||
|
||||
/* First rendering scope: write to image as color attachment. */
|
||||
{
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.images.append(
|
||||
{image,
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
{0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS}});
|
||||
VKBeginRenderingNode::CreateInfo begin_rendering(access_info);
|
||||
begin_rendering.node_data.color_attachments[0].sType =
|
||||
VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR;
|
||||
begin_rendering.node_data.color_attachments[0].imageLayout = color_attachment_layout();
|
||||
begin_rendering.node_data.color_attachments[0].imageView = image_view;
|
||||
begin_rendering.node_data.color_attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
begin_rendering.node_data.color_attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
begin_rendering.node_data.vk_rendering_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
|
||||
begin_rendering.node_data.vk_rendering_info.colorAttachmentCount = 1;
|
||||
begin_rendering.node_data.vk_rendering_info.layerCount = 1;
|
||||
begin_rendering.node_data.vk_rendering_info.pColorAttachments =
|
||||
begin_rendering.node_data.color_attachments;
|
||||
|
||||
render_graph->add_node(begin_rendering);
|
||||
}
|
||||
|
||||
{
|
||||
VKResourceAccessInfo empty_access_info = {};
|
||||
VKDrawNode::CreateInfo draw(empty_access_info);
|
||||
draw.node_data.first_instance = 0;
|
||||
draw.node_data.first_vertex = 0;
|
||||
draw.node_data.instance_count = 1;
|
||||
draw.node_data.vertex_count = 4;
|
||||
draw.node_data.graphics.pipeline_data.push_constants_range = IndexRange(0);
|
||||
draw.node_data.graphics.pipeline_data.vk_descriptor_set = VK_NULL_HANDLE;
|
||||
draw.node_data.graphics.pipeline_data.vk_pipeline = pipeline;
|
||||
draw.node_data.graphics.pipeline_data.vk_pipeline_layout = pipeline_layout;
|
||||
draw.node_data.graphics.viewport.viewports.append(VkViewport{});
|
||||
draw.node_data.graphics.viewport.scissors.append(VkRect2D{});
|
||||
render_graph->add_node(draw);
|
||||
}
|
||||
|
||||
/* Second rendering scope: restart without explicit END_RENDERING,
|
||||
* simulating subpass_transition with dynamic_rendering_local_read.
|
||||
* Image is accessed as input attachment. */
|
||||
{
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.images.append(
|
||||
{image,
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
{0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS}});
|
||||
VKBeginRenderingNode::CreateInfo begin_rendering(access_info);
|
||||
begin_rendering.node_data.color_attachments[0].sType =
|
||||
VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR;
|
||||
begin_rendering.node_data.color_attachments[0].imageLayout = color_attachment_layout();
|
||||
begin_rendering.node_data.color_attachments[0].imageView = VK_NULL_HANDLE;
|
||||
begin_rendering.node_data.color_attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
begin_rendering.node_data.color_attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
begin_rendering.node_data.vk_rendering_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
|
||||
begin_rendering.node_data.vk_rendering_info.colorAttachmentCount = 1;
|
||||
begin_rendering.node_data.vk_rendering_info.layerCount = 1;
|
||||
begin_rendering.node_data.vk_rendering_info.pColorAttachments =
|
||||
begin_rendering.node_data.color_attachments;
|
||||
|
||||
render_graph->add_node(begin_rendering);
|
||||
}
|
||||
|
||||
{
|
||||
VkAccessFlags input_access = use_dynamic_rendering_local_read ?
|
||||
VK_ACCESS_INPUT_ATTACHMENT_READ_BIT :
|
||||
VK_ACCESS_SHADER_READ_BIT;
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.images.append({image,
|
||||
input_access,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
{0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS}});
|
||||
VKDrawNode::CreateInfo draw(access_info);
|
||||
draw.node_data.first_instance = 0;
|
||||
draw.node_data.first_vertex = 0;
|
||||
draw.node_data.instance_count = 1;
|
||||
draw.node_data.vertex_count = 4;
|
||||
draw.node_data.graphics.pipeline_data.push_constants_range = IndexRange(0);
|
||||
draw.node_data.graphics.pipeline_data.vk_descriptor_set = VK_NULL_HANDLE;
|
||||
draw.node_data.graphics.pipeline_data.vk_pipeline = pipeline;
|
||||
draw.node_data.graphics.pipeline_data.vk_pipeline_layout = pipeline_layout;
|
||||
draw.node_data.graphics.viewport.viewports.append(VkViewport{});
|
||||
draw.node_data.graphics.viewport.scissors.append(VkRect2D{});
|
||||
render_graph->add_node(draw);
|
||||
}
|
||||
|
||||
{
|
||||
VKEndRenderingNode::CreateInfo end_rendering = {};
|
||||
render_graph->add_node(end_rendering);
|
||||
}
|
||||
|
||||
submit(render_graph, command_buffer);
|
||||
|
||||
/* Verify that a barrier with the read access flag is generated.
|
||||
* Without the fix for #158501, this barrier would be missing because
|
||||
* the image tracker skipped access flag synchronization for tracked
|
||||
* layered color attachments. */
|
||||
const char *expected_access = use_dynamic_rendering_local_read ?
|
||||
"VK_ACCESS_INPUT_ATTACHMENT_READ_BIT" :
|
||||
"VK_ACCESS_SHADER_READ_BIT";
|
||||
bool has_sync_barrier = false;
|
||||
for (const std::string &entry : log) {
|
||||
if (entry.find(expected_access) != std::string::npos) {
|
||||
has_sync_barrier = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(has_sync_barrier)
|
||||
<< "Missing synchronization barrier for input attachment read after color attachment write. "
|
||||
"See #158501.";
|
||||
}
|
||||
|
||||
TEST_P(VKRenderGraphTestRender, begin_draw_end__layered_depth)
|
||||
{
|
||||
VkHandle<VkImage> image(1u);
|
||||
VkHandle<VkImageView> image_view(2u);
|
||||
VkHandle<VkPipelineLayout> pipeline_layout(4u);
|
||||
VkHandle<VkPipeline> pipeline(3u);
|
||||
|
||||
resources.add_image(image, true);
|
||||
|
||||
{
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.images.append({image,
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
||||
VK_IMAGE_ASPECT_DEPTH_BIT,
|
||||
{0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS}});
|
||||
VKBeginRenderingNode::CreateInfo begin_rendering(access_info);
|
||||
begin_rendering.node_data.depth_attachment.sType =
|
||||
VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR;
|
||||
begin_rendering.node_data.depth_attachment.imageLayout =
|
||||
VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL;
|
||||
begin_rendering.node_data.depth_attachment.imageView = image_view;
|
||||
begin_rendering.node_data.depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
begin_rendering.node_data.depth_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
begin_rendering.node_data.vk_rendering_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
|
||||
begin_rendering.node_data.vk_rendering_info.colorAttachmentCount = 0;
|
||||
begin_rendering.node_data.vk_rendering_info.layerCount = 1;
|
||||
begin_rendering.node_data.vk_rendering_info.pDepthAttachment =
|
||||
&begin_rendering.node_data.depth_attachment;
|
||||
|
||||
render_graph->add_node(begin_rendering);
|
||||
}
|
||||
|
||||
{
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.images.append({image,
|
||||
VK_ACCESS_SHADER_READ_BIT,
|
||||
VK_IMAGE_ASPECT_DEPTH_BIT,
|
||||
{0, VK_REMAINING_MIP_LEVELS, 1, VK_REMAINING_ARRAY_LAYERS}});
|
||||
VKDrawNode::CreateInfo draw(access_info);
|
||||
draw.node_data.first_instance = 0;
|
||||
draw.node_data.first_vertex = 0;
|
||||
draw.node_data.instance_count = 1;
|
||||
draw.node_data.vertex_count = 4;
|
||||
draw.node_data.graphics.pipeline_data.push_constants_range = IndexRange(0);
|
||||
draw.node_data.graphics.pipeline_data.vk_descriptor_set = VK_NULL_HANDLE;
|
||||
draw.node_data.graphics.pipeline_data.vk_pipeline = pipeline;
|
||||
draw.node_data.graphics.pipeline_data.vk_pipeline_layout = pipeline_layout;
|
||||
draw.node_data.graphics.viewport.viewports.append(VkViewport{});
|
||||
draw.node_data.graphics.viewport.scissors.append(VkRect2D{});
|
||||
render_graph->add_node(draw);
|
||||
}
|
||||
|
||||
{
|
||||
VKEndRenderingNode::CreateInfo end_rendering = {};
|
||||
render_graph->add_node(end_rendering);
|
||||
}
|
||||
|
||||
submit(render_graph, command_buffer);
|
||||
EXPECT_EQ(9, log.size());
|
||||
|
||||
/* Pre-barrier: UNDEFINED -> DEPTH_ATTACHMENT_OPTIMAL */
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=, "
|
||||
"dst_access_mask=VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, "
|
||||
"old_layout=VK_IMAGE_LAYOUT_UNDEFINED, "
|
||||
"new_layout=VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, image=0x1, subresource_range=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_DEPTH_BIT, base_mip_level=0, "
|
||||
"level_count=4294967295, base_array_layer=0, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[0]);
|
||||
|
||||
/* Layer transition: DEPTH_ATTACHMENT_OPTIMAL -> GENERAL (shader read of layer 1) */
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, "
|
||||
"VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, "
|
||||
"VK_PIPELINE_STAGE_ALL_COMMANDS_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, "
|
||||
"VK_ACCESS_TRANSFER_WRITE_BIT, "
|
||||
"dst_access_mask=VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_SHADER_WRITE_BIT, "
|
||||
"VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, "
|
||||
"VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, "
|
||||
"VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, "
|
||||
"old_layout=VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, "
|
||||
"new_layout=VK_IMAGE_LAYOUT_GENERAL, "
|
||||
"image=0x1, subresource_range=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_DEPTH_BIT, base_mip_level=0, "
|
||||
"level_count=4294967295, "
|
||||
"base_array_layer=1, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[1]);
|
||||
|
||||
EXPECT_EQ("begin_rendering(p_rendering_info=flags=, render_area=" + endl() +
|
||||
" offset=" + endl() + " x=0, y=0 , extent=" + endl() +
|
||||
" width=0, height=0 , layer_count=1, view_mask=0, "
|
||||
"color_attachment_count=0, p_color_attachments=" +
|
||||
endl() + ", p_depth_attachment=" + endl() +
|
||||
" image_view=0x2, "
|
||||
"image_layout=VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, "
|
||||
"resolve_mode=VK_RESOLVE_MODE_NONE, resolve_image_view=0, "
|
||||
"resolve_image_layout=VK_IMAGE_LAYOUT_UNDEFINED, "
|
||||
"load_op=VK_ATTACHMENT_LOAD_OP_DONT_CARE, "
|
||||
"store_op=VK_ATTACHMENT_STORE_OP_STORE" +
|
||||
endl() + ")",
|
||||
log[2]);
|
||||
|
||||
EXPECT_EQ("set_viewport(num_viewports=1)", log[3]);
|
||||
EXPECT_EQ("set_scissor(num_scissors=1)", log[4]);
|
||||
EXPECT_EQ("bind_pipeline(pipeline_bind_point=VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline=0x3)",
|
||||
log[5]);
|
||||
EXPECT_EQ("draw(vertex_count=4, instance_count=1, first_vertex=0, first_instance=0)", log[6]);
|
||||
EXPECT_EQ("end_rendering()", log[7]);
|
||||
|
||||
/* Post-barrier: revert GENERAL -> DEPTH_ATTACHMENT_OPTIMAL */
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_ALL_COMMANDS_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=VK_ACCESS_SHADER_READ_BIT, "
|
||||
"VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, "
|
||||
"VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, "
|
||||
"VK_ACCESS_TRANSFER_WRITE_BIT, dst_access_mask=VK_ACCESS_SHADER_READ_BIT, "
|
||||
"VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, "
|
||||
"VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, "
|
||||
"VK_ACCESS_TRANSFER_WRITE_BIT, old_layout=VK_IMAGE_LAYOUT_GENERAL, "
|
||||
"new_layout=VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, image=0x1, subresource_range=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_DEPTH_BIT, base_mip_level=0, "
|
||||
"level_count=4294967295, "
|
||||
"base_array_layer=1, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[8]);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(, VKRenderGraphTestRender, ::testing::Values(true, false));
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,466 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "vk_render_graph_test_types.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
class VKRenderGraphTestTransfer : public VKRenderGraphTest {};
|
||||
|
||||
/**
|
||||
* Fill a single buffer and read it back.
|
||||
*/
|
||||
TEST_F(VKRenderGraphTestTransfer, fill_and_read_back)
|
||||
{
|
||||
VkHandle<VkBuffer> buffer(1u);
|
||||
|
||||
resources.add_buffer(buffer);
|
||||
VKFillBufferNode::CreateInfo fill_buffer = {buffer, 1024, 42};
|
||||
render_graph->add_node(fill_buffer);
|
||||
submit(render_graph, command_buffer);
|
||||
|
||||
EXPECT_EQ(1, log.size());
|
||||
EXPECT_EQ("fill_buffer(dst_buffer=0x1, dst_offset=0, size=1024, data=42)", log[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill a single buffer, copy it to a staging buffer and read the staging buffer back.
|
||||
*/
|
||||
TEST_F(VKRenderGraphTestTransfer, fill_transfer_and_read_back)
|
||||
{
|
||||
VkHandle<VkBuffer> buffer(1u);
|
||||
VkHandle<VkBuffer> staging_buffer(2u);
|
||||
|
||||
resources.add_buffer(buffer);
|
||||
VKFillBufferNode::CreateInfo fill_buffer = {buffer, 1024, 42};
|
||||
render_graph->add_node(fill_buffer);
|
||||
resources.add_buffer(staging_buffer);
|
||||
|
||||
VKCopyBufferNode::CreateInfo copy_buffer = {};
|
||||
copy_buffer.src_buffer = buffer;
|
||||
copy_buffer.dst_buffer = staging_buffer;
|
||||
copy_buffer.region.srcOffset = 0;
|
||||
copy_buffer.region.dstOffset = 0;
|
||||
copy_buffer.region.size = 1024;
|
||||
render_graph->add_node(copy_buffer);
|
||||
|
||||
submit(render_graph, command_buffer);
|
||||
|
||||
EXPECT_EQ(3, log.size());
|
||||
EXPECT_EQ("fill_buffer(dst_buffer=0x1, dst_offset=0, size=1024, data=42)", log[0]);
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT" +
|
||||
endl() +
|
||||
" - buffer_barrier(src_access_mask=VK_ACCESS_TRANSFER_WRITE_BIT, "
|
||||
"dst_access_mask=VK_ACCESS_TRANSFER_READ_BIT, buffer=0x1, offset=0, "
|
||||
"size=18446744073709551615)" +
|
||||
endl() + ")",
|
||||
log[1]);
|
||||
EXPECT_EQ("copy_buffer(src_buffer=0x1, dst_buffer=0x2" + endl() +
|
||||
" - region(src_offset=0, dst_offset=0, size=1024)" + endl() + ")",
|
||||
log[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill a buffer twice, before reading back.
|
||||
*
|
||||
* Between the two fills a write->write barrier should be created.
|
||||
*/
|
||||
TEST_F(VKRenderGraphTestTransfer, fill_fill_read_back)
|
||||
{
|
||||
VkHandle<VkBuffer> buffer(1u);
|
||||
|
||||
resources.add_buffer(buffer);
|
||||
VKFillBufferNode::CreateInfo fill_buffer_1 = {buffer, 1024, 0};
|
||||
render_graph->add_node(fill_buffer_1);
|
||||
VKFillBufferNode::CreateInfo fill_buffer_2 = {buffer, 1024, 42};
|
||||
render_graph->add_node(fill_buffer_2);
|
||||
submit(render_graph, command_buffer);
|
||||
|
||||
EXPECT_EQ(3, log.size());
|
||||
EXPECT_EQ("fill_buffer(dst_buffer=0x1, dst_offset=0, size=1024, data=0)", log[0]);
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT" +
|
||||
endl() +
|
||||
" - buffer_barrier(src_access_mask=VK_ACCESS_TRANSFER_WRITE_BIT, "
|
||||
"dst_access_mask=VK_ACCESS_TRANSFER_WRITE_BIT, buffer=0x1, offset=0, "
|
||||
"size=18446744073709551615)" +
|
||||
endl() + ")",
|
||||
log[1]);
|
||||
EXPECT_EQ("fill_buffer(dst_buffer=0x1, dst_offset=0, size=1024, data=42)", log[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill a single buffer, copy it to a staging buffer and read the staging buffer back.
|
||||
*/
|
||||
TEST_F(VKRenderGraphTestTransfer, clear_clear_copy_and_read_back)
|
||||
{
|
||||
VkHandle<VkImage> src_image(1u);
|
||||
VkHandle<VkImage> dst_image(2u);
|
||||
VkHandle<VkBuffer> staging_buffer(3u);
|
||||
|
||||
resources.add_image(src_image, false);
|
||||
resources.add_image(dst_image, false);
|
||||
resources.add_buffer(staging_buffer);
|
||||
VkClearColorValue color_white = {};
|
||||
color_white.float32[0] = 1.0f;
|
||||
color_white.float32[1] = 1.0f;
|
||||
color_white.float32[2] = 1.0f;
|
||||
color_white.float32[3] = 1.0f;
|
||||
VkClearColorValue color_black = {};
|
||||
color_black.float32[0] = 0.0f;
|
||||
color_black.float32[1] = 0.0f;
|
||||
color_black.float32[2] = 0.0f;
|
||||
color_black.float32[3] = 1.0f;
|
||||
|
||||
VKClearColorImageNode::CreateInfo clear_color_image_src = {};
|
||||
clear_color_image_src.vk_image = src_image;
|
||||
clear_color_image_src.vk_clear_color_value = color_white;
|
||||
VKClearColorImageNode::CreateInfo clear_color_image_dst = {};
|
||||
clear_color_image_dst.vk_image = dst_image;
|
||||
clear_color_image_dst.vk_clear_color_value = color_black;
|
||||
|
||||
VKCopyImageNode::CreateInfo copy_image = {};
|
||||
copy_image.node_data.src_image = src_image;
|
||||
copy_image.node_data.dst_image = dst_image;
|
||||
copy_image.node_data.mip_levels = 1u;
|
||||
copy_image.node_data.region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
copy_image.node_data.region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
copy_image.vk_image_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
VKCopyImageToBufferNode::CreateInfo copy_dst_image_to_buffer = {};
|
||||
copy_dst_image_to_buffer.node_data.src_image = dst_image;
|
||||
copy_dst_image_to_buffer.node_data.dst_buffer = staging_buffer;
|
||||
copy_dst_image_to_buffer.node_data.region.imageSubresource.aspectMask =
|
||||
VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
copy_dst_image_to_buffer.vk_image_aspects = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
|
||||
render_graph->add_node(clear_color_image_src);
|
||||
render_graph->add_node(clear_color_image_dst);
|
||||
render_graph->add_node(copy_image);
|
||||
render_graph->add_node(copy_dst_image_to_buffer);
|
||||
submit(render_graph, command_buffer);
|
||||
|
||||
EXPECT_EQ(8, log.size());
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=, dst_access_mask=VK_ACCESS_TRANSFER_WRITE_BIT, "
|
||||
"old_layout=VK_IMAGE_LAYOUT_UNDEFINED, new_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, "
|
||||
"image=0x1, subresource_range=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, level_count=4294967295, "
|
||||
"base_array_layer=0, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[0]);
|
||||
EXPECT_EQ("clear_color_image(image=0x1, image_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)",
|
||||
log[1]);
|
||||
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=, dst_access_mask=VK_ACCESS_TRANSFER_WRITE_BIT, "
|
||||
"old_layout=VK_IMAGE_LAYOUT_UNDEFINED, new_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, "
|
||||
"image=0x2, subresource_range=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, level_count=4294967295, "
|
||||
"base_array_layer=0, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[2]);
|
||||
EXPECT_EQ("clear_color_image(image=0x2, image_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)",
|
||||
log[3]);
|
||||
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=VK_ACCESS_TRANSFER_WRITE_BIT, "
|
||||
"dst_access_mask=VK_ACCESS_TRANSFER_READ_BIT, "
|
||||
"old_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, "
|
||||
"new_layout=VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image=0x1, subresource_range=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, level_count=4294967295, "
|
||||
"base_array_layer=0, layer_count=4294967295 )" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=VK_ACCESS_TRANSFER_WRITE_BIT, "
|
||||
"dst_access_mask=VK_ACCESS_TRANSFER_WRITE_BIT, "
|
||||
"old_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, "
|
||||
"new_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, image=0x2, subresource_range=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, level_count=4294967295, "
|
||||
"base_array_layer=0, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[4]);
|
||||
EXPECT_EQ(
|
||||
"copy_image(src_image=0x1, src_image_layout=VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, "
|
||||
"dst_image=0x2, dst_image_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL" +
|
||||
endl() + " - region(src_subresource=" + endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, mip_level=0, base_array_layer=0, "
|
||||
"layer_count=0 , src_offset=" +
|
||||
endl() + " x=0, y=0, z=0 , dst_subresource=" + endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, mip_level=0, base_array_layer=0, "
|
||||
"layer_count=0 , dst_offset=" +
|
||||
endl() + " x=0, y=0, z=0 , extent=" + endl() + " width=0, height=0, depth=0 )" +
|
||||
endl() + ")",
|
||||
log[5]);
|
||||
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=VK_ACCESS_TRANSFER_WRITE_BIT, "
|
||||
"dst_access_mask=VK_ACCESS_TRANSFER_READ_BIT, "
|
||||
"old_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, "
|
||||
"new_layout=VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image=0x2, subresource_range=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, level_count=4294967295, "
|
||||
"base_array_layer=0, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[6]);
|
||||
EXPECT_EQ(
|
||||
"copy_image_to_buffer(src_image=0x2, src_image_layout=VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, "
|
||||
"dst_buffer=0x3" +
|
||||
endl() +
|
||||
" - region(buffer_offset=0, buffer_row_length=0, buffer_image_height=0, "
|
||||
"image_subresource=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, mip_level=0, base_array_layer=0, "
|
||||
"layer_count=0 , image_offset=" +
|
||||
endl() + " x=0, y=0, z=0 , image_extent=\n width=0, height=0, depth=0 )" +
|
||||
endl() + ")",
|
||||
log[7]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear an image, blit it to another image, copy to a staging buffer and read back.
|
||||
*/
|
||||
TEST_F(VKRenderGraphTestTransfer, clear_blit_copy_and_read_back)
|
||||
{
|
||||
VkHandle<VkImage> src_image(1u);
|
||||
VkHandle<VkImage> dst_image(2u);
|
||||
VkHandle<VkBuffer> staging_buffer(3u);
|
||||
|
||||
resources.add_image(src_image, false);
|
||||
resources.add_image(dst_image, false);
|
||||
resources.add_buffer(staging_buffer);
|
||||
VkClearColorValue color_black = {};
|
||||
color_black.float32[0] = 0.0f;
|
||||
color_black.float32[1] = 0.0f;
|
||||
color_black.float32[2] = 0.0f;
|
||||
color_black.float32[3] = 1.0f;
|
||||
VkImageBlit vk_image_blit = {};
|
||||
VKClearColorImageNode::CreateInfo clear_color_image_src = {};
|
||||
clear_color_image_src.vk_image = src_image;
|
||||
clear_color_image_src.vk_clear_color_value = color_black;
|
||||
VKCopyImageToBufferNode::CreateInfo copy_dst_image_to_buffer = {};
|
||||
copy_dst_image_to_buffer.node_data.src_image = dst_image;
|
||||
copy_dst_image_to_buffer.node_data.dst_buffer = staging_buffer;
|
||||
copy_dst_image_to_buffer.node_data.region.imageSubresource.aspectMask =
|
||||
VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
copy_dst_image_to_buffer.vk_image_aspects = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
|
||||
render_graph->add_node(clear_color_image_src);
|
||||
VKBlitImageNode::CreateInfo blit_image = {src_image, dst_image, vk_image_blit, VK_FILTER_LINEAR};
|
||||
render_graph->add_node(blit_image);
|
||||
render_graph->add_node(copy_dst_image_to_buffer);
|
||||
submit(render_graph, command_buffer);
|
||||
|
||||
EXPECT_EQ(6, log.size());
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=, dst_access_mask=VK_ACCESS_TRANSFER_WRITE_BIT, "
|
||||
"old_layout=VK_IMAGE_LAYOUT_UNDEFINED, "
|
||||
"new_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, image=0x1, subresource_range=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, level_count=4294967295, "
|
||||
"base_array_layer=0, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[0]);
|
||||
EXPECT_EQ("clear_color_image(image=0x1, image_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)",
|
||||
log[1]);
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=VK_ACCESS_TRANSFER_WRITE_BIT, "
|
||||
"dst_access_mask=VK_ACCESS_TRANSFER_READ_BIT, "
|
||||
"old_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, "
|
||||
"new_layout=VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image=0x1, subresource_range=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, level_count=4294967295, "
|
||||
"base_array_layer=0, layer_count=4294967295 )" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=, dst_access_mask=VK_ACCESS_TRANSFER_WRITE_BIT, "
|
||||
"old_layout=VK_IMAGE_LAYOUT_UNDEFINED, new_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, "
|
||||
"image=0x2, subresource_range=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, level_count=4294967295, "
|
||||
"base_array_layer=0, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[2]);
|
||||
EXPECT_EQ(
|
||||
"blit_image(src_image=0x1, src_image_layout=VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, "
|
||||
"dst_image=0x2, dst_image_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, "
|
||||
"filter=VK_FILTER_LINEAR" +
|
||||
endl() + " - region(src_subresource=" + endl() +
|
||||
" aspect_mask=, mip_level=0, base_array_layer=0, layer_count=0 , dst_subresource=" +
|
||||
endl() + " aspect_mask=, mip_level=0, base_array_layer=0, layer_count=0 )" + endl() +
|
||||
")",
|
||||
log[3]);
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=VK_ACCESS_TRANSFER_WRITE_BIT, "
|
||||
"dst_access_mask=VK_ACCESS_TRANSFER_READ_BIT, "
|
||||
"old_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, "
|
||||
"new_layout=VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image=0x2, subresource_range=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, level_count=4294967295, "
|
||||
"base_array_layer=0, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[4]);
|
||||
EXPECT_EQ(
|
||||
"copy_image_to_buffer(src_image=0x2, src_image_layout=VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, "
|
||||
"dst_buffer=0x3" +
|
||||
endl() +
|
||||
" - region(buffer_offset=0, buffer_row_length=0, buffer_image_height=0, "
|
||||
"image_subresource=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, mip_level=0, base_array_layer=0, "
|
||||
"layer_count=0 , image_offset=" +
|
||||
endl() + " x=0, y=0, z=0 , image_extent=" + endl() +
|
||||
" width=0, height=0, depth=0 )" + endl() + ")",
|
||||
log[5]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify a previous added copy buffer command.
|
||||
*/
|
||||
TEST_F(VKRenderGraphTestTransfer, copy_buffer_modify_data)
|
||||
{
|
||||
VkHandle<VkBuffer> buffer_src(1u);
|
||||
VkHandle<VkBuffer> buffer_dst(2u);
|
||||
|
||||
resources.add_buffer(buffer_src);
|
||||
resources.add_buffer(buffer_dst);
|
||||
VKCopyBufferNode::CreateInfo copy_buffer = {buffer_src, buffer_dst, {0, 0, 32}};
|
||||
NodeHandle copy_buffer_handle = render_graph->add_node(copy_buffer);
|
||||
VKCopyBufferNode::Data ©_buffer_data = render_graph->get_node_data(copy_buffer_handle);
|
||||
copy_buffer_data.region.size = 64;
|
||||
submit(render_graph, command_buffer);
|
||||
|
||||
EXPECT_EQ(1, log.size());
|
||||
EXPECT_EQ("copy_buffer(src_buffer=0x1, dst_buffer=0x2" + endl() +
|
||||
" - region(src_offset=0, dst_offset=0, size=64)" + endl() + ")",
|
||||
log[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a pipeline barrier between a buffer-to-image copy and a compute dispatch
|
||||
* has the correct srcAccessMask.
|
||||
*
|
||||
* This validates that srcAccessMask is properly updated when adjusting an existing
|
||||
* barrier in add_image_barrier(), preventing WRITE_AFTER_WRITE hazards.
|
||||
*
|
||||
* Reproduces the issue seen with VKTexturePool where a pool texture written by
|
||||
* a staging buffer upload is subsequently read by a compute shader.
|
||||
*/
|
||||
TEST_F(VKRenderGraphTestTransfer, copy_buffer_to_image_dispatch_read_aliased_image)
|
||||
{
|
||||
VkHandle<VkBuffer> buffer(1u);
|
||||
VkHandle<VkImage> image(2u);
|
||||
VkHandle<VkPipeline> pipeline(3u);
|
||||
VkHandle<VkPipelineLayout> pipeline_layout(4u);
|
||||
VkHandle<VkDescriptorSet> descriptor_set(5u);
|
||||
|
||||
resources.add_buffer(buffer);
|
||||
resources.add_aliased_image(image, false);
|
||||
|
||||
VKCopyBufferToImageNode::CreateInfo copy_buffer_to_image = {};
|
||||
copy_buffer_to_image.node_data.src_buffer = buffer;
|
||||
copy_buffer_to_image.node_data.dst_image = image;
|
||||
copy_buffer_to_image.node_data.region.imageExtent.width = 64;
|
||||
copy_buffer_to_image.node_data.region.imageExtent.height = 64;
|
||||
copy_buffer_to_image.node_data.region.imageExtent.depth = 1;
|
||||
copy_buffer_to_image.node_data.region.imageOffset.x = 0;
|
||||
copy_buffer_to_image.node_data.region.imageOffset.y = 0;
|
||||
copy_buffer_to_image.node_data.region.imageOffset.z = 0;
|
||||
copy_buffer_to_image.node_data.region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
copy_buffer_to_image.node_data.region.imageSubresource.mipLevel = 0;
|
||||
copy_buffer_to_image.node_data.region.imageSubresource.baseArrayLayer = 0;
|
||||
copy_buffer_to_image.node_data.region.imageSubresource.layerCount = 1;
|
||||
copy_buffer_to_image.vk_image_aspects = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
render_graph->add_node(copy_buffer_to_image);
|
||||
|
||||
VKResourceAccessInfo access_info = {};
|
||||
access_info.images.append({image, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT, {}});
|
||||
VKDispatchNode::CreateInfo dispatch_info(access_info);
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_pipeline = pipeline;
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_pipeline_layout = pipeline_layout;
|
||||
dispatch_info.dispatch_node.pipeline_data.vk_descriptor_set = descriptor_set;
|
||||
dispatch_info.dispatch_node.group_count_x = 1;
|
||||
dispatch_info.dispatch_node.group_count_y = 1;
|
||||
dispatch_info.dispatch_node.group_count_z = 1;
|
||||
render_graph->add_node(dispatch_info);
|
||||
|
||||
submit(render_graph, command_buffer);
|
||||
|
||||
EXPECT_EQ(6, log.size());
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=VK_ACCESS_MEMORY_READ_BIT, "
|
||||
"VK_ACCESS_MEMORY_WRITE_BIT, "
|
||||
"dst_access_mask=VK_ACCESS_TRANSFER_WRITE_BIT, "
|
||||
"old_layout=VK_IMAGE_LAYOUT_UNDEFINED, new_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, "
|
||||
"image=0x2, subresource_range=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, level_count=4294967295, "
|
||||
"base_array_layer=0, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[0]);
|
||||
EXPECT_EQ(
|
||||
"copy_buffer_to_image(src_buffer=0x1, dst_image=0x2, "
|
||||
"src_image_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL" +
|
||||
endl() +
|
||||
" - region(buffer_offset=0, buffer_row_length=0, buffer_image_height=0, "
|
||||
"image_subresource=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, mip_level=0, base_array_layer=0, "
|
||||
"layer_count=1 , image_offset=" +
|
||||
endl() + " x=0, y=0, z=0 , image_extent=" + endl() +
|
||||
" width=64, height=64, depth=1 )" + endl() + ")",
|
||||
log[1]);
|
||||
EXPECT_EQ(
|
||||
"pipeline_barrier(src_stage_mask=VK_PIPELINE_STAGE_TRANSFER_BIT, "
|
||||
"dst_stage_mask=VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT" +
|
||||
endl() +
|
||||
" - image_barrier(src_access_mask=VK_ACCESS_TRANSFER_WRITE_BIT, "
|
||||
"dst_access_mask=VK_ACCESS_SHADER_READ_BIT, "
|
||||
"old_layout=VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, "
|
||||
"new_layout=VK_IMAGE_LAYOUT_GENERAL, image=0x2, subresource_range=" +
|
||||
endl() +
|
||||
" aspect_mask=VK_IMAGE_ASPECT_COLOR_BIT, base_mip_level=0, level_count=4294967295, "
|
||||
"base_array_layer=0, layer_count=4294967295 )" +
|
||||
endl() + ")",
|
||||
log[2]);
|
||||
EXPECT_EQ("bind_pipeline(pipeline_bind_point=VK_PIPELINE_BIND_POINT_COMPUTE, pipeline=0x3)",
|
||||
log[3]);
|
||||
EXPECT_EQ(
|
||||
"bind_descriptor_sets(pipeline_bind_point=VK_PIPELINE_BIND_POINT_COMPUTE, layout=0x4, "
|
||||
"p_descriptor_sets=0x5)",
|
||||
log[4]);
|
||||
EXPECT_EQ("dispatch(group_count_x=1, group_count_y=1, group_count_z=1)", log[5]);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,589 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
|
||||
#include "render_graph/vk_render_graph.hh"
|
||||
#include "vk_common.hh"
|
||||
#include "vk_to_string.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
BLI_INLINE std::string &endl()
|
||||
{
|
||||
static std::string endl;
|
||||
if (endl.empty()) {
|
||||
std::stringstream ss;
|
||||
ss << std::endl;
|
||||
endl = ss.str();
|
||||
}
|
||||
return endl;
|
||||
}
|
||||
|
||||
class CommandBufferLog : public VKCommandBufferInterface {
|
||||
Vector<std::string> &log_;
|
||||
bool is_recording_ = false;
|
||||
|
||||
public:
|
||||
CommandBufferLog(Vector<std::string> &log, bool use_dynamic_rendering_local_read_ = true)
|
||||
: log_(log)
|
||||
{
|
||||
use_dynamic_rendering_local_read = use_dynamic_rendering_local_read_;
|
||||
}
|
||||
virtual ~CommandBufferLog() {}
|
||||
|
||||
void begin_recording() override
|
||||
{
|
||||
EXPECT_FALSE(is_recording_);
|
||||
is_recording_ = true;
|
||||
}
|
||||
|
||||
void end_recording() override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
is_recording_ = false;
|
||||
}
|
||||
|
||||
void bind_pipeline(VkPipelineBindPoint pipeline_bind_point, VkPipeline pipeline) override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "bind_pipeline(";
|
||||
ss << "pipeline_bind_point=" << to_string(pipeline_bind_point);
|
||||
ss << ", pipeline=" << to_string(pipeline);
|
||||
ss << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void bind_descriptor_sets(VkPipelineBindPoint pipeline_bind_point,
|
||||
VkPipelineLayout layout,
|
||||
uint32_t first_set,
|
||||
uint32_t descriptor_set_count,
|
||||
const VkDescriptorSet *p_descriptor_sets,
|
||||
uint32_t dynamic_offset_count,
|
||||
const uint32_t *p_dynamic_offsets) override
|
||||
{
|
||||
UNUSED_VARS(pipeline_bind_point,
|
||||
layout,
|
||||
first_set,
|
||||
descriptor_set_count,
|
||||
p_descriptor_sets,
|
||||
dynamic_offset_count,
|
||||
p_dynamic_offsets);
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "bind_descriptor_sets(";
|
||||
ss << "pipeline_bind_point=" << to_string(pipeline_bind_point);
|
||||
ss << ", layout=" << to_string(layout);
|
||||
ss << ", p_descriptor_sets=" << to_string(p_descriptor_sets[0]);
|
||||
ss << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void bind_index_buffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType index_type) override
|
||||
{
|
||||
UNUSED_VARS(buffer, offset, index_type);
|
||||
EXPECT_TRUE(is_recording_);
|
||||
GTEST_FAIL() << __func__ << " not implemented!";
|
||||
}
|
||||
|
||||
void bind_vertex_buffers(uint32_t first_binding,
|
||||
uint32_t binding_count,
|
||||
const VkBuffer *p_buffers,
|
||||
const VkDeviceSize *p_offsets) override
|
||||
{
|
||||
UNUSED_VARS(first_binding, binding_count, p_buffers, p_offsets);
|
||||
EXPECT_TRUE(is_recording_);
|
||||
GTEST_FAIL() << __func__ << " not implemented!";
|
||||
}
|
||||
|
||||
void draw(uint32_t vertex_count,
|
||||
uint32_t instance_count,
|
||||
uint32_t first_vertex,
|
||||
uint32_t first_instance) override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "draw(";
|
||||
ss << "vertex_count=" << vertex_count;
|
||||
ss << ", instance_count=" << instance_count;
|
||||
ss << ", first_vertex=" << first_vertex;
|
||||
ss << ", first_instance=" << first_instance;
|
||||
ss << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void draw_indexed(uint32_t index_count,
|
||||
uint32_t instance_count,
|
||||
uint32_t first_index,
|
||||
int32_t vertex_offset,
|
||||
uint32_t first_instance) override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "draw_indexed(";
|
||||
ss << "index_count=" << index_count;
|
||||
ss << ", instance_count=" << instance_count;
|
||||
ss << ", first_index=" << first_index;
|
||||
ss << ", vertex_offset=" << vertex_offset;
|
||||
ss << ", first_instance=" << first_instance;
|
||||
ss << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void draw_indirect(VkBuffer buffer,
|
||||
VkDeviceSize offset,
|
||||
uint32_t draw_count,
|
||||
uint32_t stride) override
|
||||
{
|
||||
UNUSED_VARS(buffer, offset, draw_count, stride);
|
||||
EXPECT_TRUE(is_recording_);
|
||||
GTEST_FAIL() << __func__ << " not implemented!";
|
||||
}
|
||||
|
||||
void draw_indexed_indirect(VkBuffer buffer,
|
||||
VkDeviceSize offset,
|
||||
uint32_t draw_count,
|
||||
uint32_t stride) override
|
||||
{
|
||||
UNUSED_VARS(buffer, offset, draw_count, stride);
|
||||
EXPECT_TRUE(is_recording_);
|
||||
GTEST_FAIL() << __func__ << " not implemented!";
|
||||
}
|
||||
|
||||
void dispatch(uint32_t group_count_x, uint32_t group_count_y, uint32_t group_count_z) override
|
||||
{
|
||||
UNUSED_VARS(group_count_x, group_count_y, group_count_z);
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "dispatch(";
|
||||
ss << "group_count_x=" << group_count_x;
|
||||
ss << ", group_count_y=" << group_count_y;
|
||||
ss << ", group_count_z=" << group_count_z;
|
||||
ss << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void dispatch_indirect(VkBuffer buffer, VkDeviceSize offset) override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "dispatch_indirect(";
|
||||
ss << "buffer=" << to_string(buffer);
|
||||
ss << ", offset=" << offset;
|
||||
ss << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void update_buffer(VkBuffer dst_buffer,
|
||||
VkDeviceSize dst_offset,
|
||||
VkDeviceSize data_size,
|
||||
const void * /*p_data*/) override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "update_buffer(";
|
||||
ss << "dst_buffer=" << to_string(dst_buffer);
|
||||
ss << ", dst_offset=" << dst_offset;
|
||||
ss << ", data_size=" << data_size;
|
||||
ss << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
void copy_buffer(VkBuffer src_buffer,
|
||||
VkBuffer dst_buffer,
|
||||
uint32_t region_count,
|
||||
const VkBufferCopy *p_regions) override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "copy_buffer(";
|
||||
ss << "src_buffer=" << to_string(src_buffer);
|
||||
ss << ", dst_buffer=" << to_string(dst_buffer);
|
||||
ss << std::endl;
|
||||
for (const VkBufferCopy ®ion : Span<const VkBufferCopy>(p_regions, region_count)) {
|
||||
ss << " - region(" << to_string(region, 1) << ")" << std::endl;
|
||||
}
|
||||
ss << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void copy_image(VkImage src_image,
|
||||
VkImageLayout src_image_layout,
|
||||
VkImage dst_image,
|
||||
VkImageLayout dst_image_layout,
|
||||
uint32_t region_count,
|
||||
const VkImageCopy *p_regions) override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "copy_image(";
|
||||
ss << "src_image=" << to_string(src_image);
|
||||
ss << ", src_image_layout=" << to_string(src_image_layout);
|
||||
ss << ", dst_image=" << to_string(dst_image);
|
||||
ss << ", dst_image_layout=" << to_string(dst_image_layout);
|
||||
ss << std::endl;
|
||||
for (const VkImageCopy ®ion : Span<const VkImageCopy>(p_regions, region_count)) {
|
||||
ss << " - region(" << to_string(region, 1) << ")" << std::endl;
|
||||
}
|
||||
ss << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void blit_image(VkImage src_image,
|
||||
VkImageLayout src_image_layout,
|
||||
VkImage dst_image,
|
||||
VkImageLayout dst_image_layout,
|
||||
uint32_t region_count,
|
||||
const VkImageBlit *p_regions,
|
||||
VkFilter filter) override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "blit_image(";
|
||||
ss << "src_image=" << to_string(src_image);
|
||||
ss << ", src_image_layout=" << to_string(src_image_layout);
|
||||
ss << ", dst_image=" << to_string(dst_image);
|
||||
ss << ", dst_image_layout=" << to_string(dst_image_layout);
|
||||
ss << ", filter=" << to_string(filter);
|
||||
ss << std::endl;
|
||||
for (const VkImageBlit ®ion : Span<const VkImageBlit>(p_regions, region_count)) {
|
||||
ss << " - region(" << to_string(region, 1) << ")" << std::endl;
|
||||
}
|
||||
ss << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void copy_buffer_to_image(VkBuffer src_buffer,
|
||||
VkImage dst_image,
|
||||
VkImageLayout dst_image_layout,
|
||||
uint32_t region_count,
|
||||
const VkBufferImageCopy *p_regions) override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "copy_buffer_to_image(";
|
||||
ss << "src_buffer=" << to_string(src_buffer);
|
||||
ss << ", dst_image=" << to_string(dst_image);
|
||||
ss << ", src_image_layout=" << to_string(dst_image_layout);
|
||||
ss << std::endl;
|
||||
for (const VkBufferImageCopy ®ion : Span<const VkBufferImageCopy>(p_regions, region_count))
|
||||
{
|
||||
ss << " - region(" << to_string(region, 1) << ")" << std::endl;
|
||||
}
|
||||
ss << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void copy_image_to_buffer(VkImage src_image,
|
||||
VkImageLayout src_image_layout,
|
||||
VkBuffer dst_buffer,
|
||||
uint32_t region_count,
|
||||
const VkBufferImageCopy *p_regions) override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "copy_image_to_buffer(";
|
||||
ss << "src_image=" << to_string(src_image);
|
||||
ss << ", src_image_layout=" << to_string(src_image_layout);
|
||||
ss << ", dst_buffer=" << to_string(dst_buffer);
|
||||
ss << std::endl;
|
||||
for (const VkBufferImageCopy ®ion : Span<const VkBufferImageCopy>(p_regions, region_count))
|
||||
{
|
||||
ss << " - region(" << to_string(region, 1) << ")" << std::endl;
|
||||
}
|
||||
ss << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void fill_buffer(VkBuffer dst_buffer,
|
||||
VkDeviceSize dst_offset,
|
||||
VkDeviceSize size,
|
||||
uint32_t data) override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "fill_buffer(";
|
||||
ss << "dst_buffer=" << to_string(dst_buffer);
|
||||
ss << ", dst_offset=" << dst_offset;
|
||||
ss << ", size=" << size;
|
||||
ss << ", data=" << data;
|
||||
ss << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void clear_color_image(VkImage image,
|
||||
VkImageLayout image_layout,
|
||||
const VkClearColorValue *p_color,
|
||||
uint32_t range_count,
|
||||
const VkImageSubresourceRange *p_ranges) override
|
||||
{
|
||||
UNUSED_VARS(p_color, range_count, p_ranges);
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "clear_color_image(";
|
||||
ss << "image=" << to_string(image);
|
||||
ss << ", image_layout=" << to_string(image_layout);
|
||||
ss << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void clear_depth_stencil_image(VkImage image,
|
||||
VkImageLayout image_layout,
|
||||
const VkClearDepthStencilValue *p_depth_stencil,
|
||||
uint32_t range_count,
|
||||
const VkImageSubresourceRange *p_ranges) override
|
||||
{
|
||||
UNUSED_VARS(image, image_layout, p_depth_stencil, range_count, p_ranges);
|
||||
EXPECT_TRUE(is_recording_);
|
||||
GTEST_FAIL() << __func__ << " not implemented!";
|
||||
}
|
||||
|
||||
void clear_attachments(uint32_t attachment_count,
|
||||
const VkClearAttachment *p_attachments,
|
||||
uint32_t rect_count,
|
||||
const VkClearRect *p_rects) override
|
||||
{
|
||||
UNUSED_VARS(attachment_count, p_attachments, rect_count, p_rects);
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "clear_attachments(";
|
||||
for (const VkClearAttachment &attachment :
|
||||
Span<VkClearAttachment>(p_attachments, attachment_count))
|
||||
{
|
||||
ss << " - attachment(" << to_string(attachment, 1) << ")" << std::endl;
|
||||
}
|
||||
for (const VkClearRect &rect : Span<VkClearRect>(p_rects, rect_count)) {
|
||||
ss << " - rect(" << to_string(rect, 1) << ")" << std::endl;
|
||||
}
|
||||
ss << ")";
|
||||
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void pipeline_barrier(VkPipelineStageFlags src_stage_mask,
|
||||
VkPipelineStageFlags dst_stage_mask,
|
||||
VkDependencyFlags dependency_flags,
|
||||
uint32_t memory_barrier_count,
|
||||
const VkMemoryBarrier *p_memory_barriers,
|
||||
uint32_t buffer_memory_barrier_count,
|
||||
const VkBufferMemoryBarrier *p_buffer_memory_barriers,
|
||||
uint32_t image_memory_barrier_count,
|
||||
const VkImageMemoryBarrier *p_image_memory_barriers) override
|
||||
{
|
||||
UNUSED_VARS(dependency_flags, memory_barrier_count, p_memory_barriers);
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "pipeline_barrier(";
|
||||
ss << "src_stage_mask=" << to_string_vk_pipeline_stage_flags(src_stage_mask);
|
||||
ss << ", dst_stage_mask=" << to_string_vk_pipeline_stage_flags(dst_stage_mask);
|
||||
ss << std::endl;
|
||||
for (VkImageMemoryBarrier image_barrier :
|
||||
Span<VkImageMemoryBarrier>(p_image_memory_barriers, image_memory_barrier_count))
|
||||
{
|
||||
ss << " - image_barrier(" << to_string(image_barrier, 1) << ")" << std::endl;
|
||||
}
|
||||
for (VkBufferMemoryBarrier buffer_barrier :
|
||||
Span<VkBufferMemoryBarrier>(p_buffer_memory_barriers, buffer_memory_barrier_count))
|
||||
{
|
||||
ss << " - buffer_barrier(" << to_string(buffer_barrier, 1) << ")" << std::endl;
|
||||
}
|
||||
ss << ")";
|
||||
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void push_constants(VkPipelineLayout layout,
|
||||
VkShaderStageFlags stage_flags,
|
||||
uint32_t offset,
|
||||
uint32_t size,
|
||||
const void *p_values) override
|
||||
{
|
||||
UNUSED_VARS(layout, stage_flags, offset, size, p_values);
|
||||
EXPECT_TRUE(is_recording_);
|
||||
GTEST_FAIL() << __func__ << " not implemented!";
|
||||
}
|
||||
|
||||
void begin_rendering(const VkRenderingInfo *p_rendering_info) override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "begin_rendering(";
|
||||
ss << "p_rendering_info=" << to_string(*p_rendering_info);
|
||||
ss << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void end_rendering() override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "end_rendering()";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void begin_query(VkQueryPool /*vk_query_pool*/,
|
||||
uint32_t /*query_index*/,
|
||||
VkQueryControlFlags /*vk_query_control_flags*/) override
|
||||
{
|
||||
}
|
||||
void end_query(VkQueryPool /*vk_query_pool*/, uint32_t /*query_index*/) override {}
|
||||
void reset_query_pool(VkQueryPool /*vk_query_pool*/,
|
||||
uint32_t /*first_query*/,
|
||||
uint32_t /*query_count*/) override
|
||||
{
|
||||
}
|
||||
|
||||
void set_viewport(const Vector<VkViewport> viewports) override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "set_viewport(num_viewports=" << viewports.size() << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void set_scissor(const Vector<VkRect2D> scissors) override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "set_scissor(num_scissors=" << scissors.size() << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void set_line_width(const float line_width) override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "set_line_width(line_width=" << line_width << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void set_stencil_compare_mask(const uint32_t compare_mask) override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "set_stencil_compare_mask(mask=" << compare_mask << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void set_stencil_write_mask(const uint32_t write_mask) override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "set_stencil_write_mask(mask=" << write_mask << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void set_stencil_reference(const uint32_t reference) override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "set_stencil_reference(reference=" << reference << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void set_front_face(const VkFrontFace front_face) override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "set_front_face(front_face=" << front_face << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void set_vertex_input(
|
||||
Span<VkVertexInputBindingDescription2EXT> vertex_binding_descriptions,
|
||||
Span<VkVertexInputAttributeDescription2EXT> vertex_attribute_descriptions) override
|
||||
{
|
||||
EXPECT_TRUE(is_recording_);
|
||||
std::stringstream ss;
|
||||
ss << "set_vertex_input(vertexBindingDescriptionCount=" << vertex_binding_descriptions.size()
|
||||
<< ", vertexAttributeDescriptionCount=" << vertex_attribute_descriptions.size() << ")";
|
||||
log_.append(ss.str());
|
||||
}
|
||||
|
||||
void begin_debug_utils_label(const VkDebugUtilsLabelEXT * /*vk_debug_utils_label*/) override {}
|
||||
void end_debug_utils_label() override {}
|
||||
};
|
||||
|
||||
class VKRenderGraphTest : public ::testing::Test {
|
||||
public:
|
||||
VKRenderGraphTest()
|
||||
{
|
||||
resources.use_dynamic_rendering_local_read = use_dynamic_rendering_local_read;
|
||||
render_graph = std::make_unique<VKRenderGraph>(resources);
|
||||
command_buffer = std::make_unique<CommandBufferLog>(log, use_dynamic_rendering_local_read);
|
||||
}
|
||||
|
||||
protected:
|
||||
Vector<std::string> log;
|
||||
VKResourceStateTracker resources;
|
||||
std::unique_ptr<VKRenderGraph> render_graph;
|
||||
std::unique_ptr<CommandBufferLog> command_buffer;
|
||||
bool use_dynamic_rendering_local_read = true;
|
||||
};
|
||||
|
||||
class VKRenderGraphTest_P : public ::testing::TestWithParam<std::tuple<bool>> {
|
||||
public:
|
||||
VKRenderGraphTest_P()
|
||||
{
|
||||
use_dynamic_rendering_local_read = std::get<0>(GetParam());
|
||||
resources.use_dynamic_rendering_local_read = use_dynamic_rendering_local_read;
|
||||
render_graph = std::make_unique<VKRenderGraph>(resources);
|
||||
command_buffer = std::make_unique<CommandBufferLog>(log, use_dynamic_rendering_local_read);
|
||||
}
|
||||
|
||||
protected:
|
||||
VkImageLayout color_attachment_layout() const
|
||||
{
|
||||
return use_dynamic_rendering_local_read ? VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR :
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
}
|
||||
std::string color_attachment_layout_str() const
|
||||
{
|
||||
return use_dynamic_rendering_local_read ? "VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR" :
|
||||
"VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL";
|
||||
}
|
||||
|
||||
Vector<std::string> log;
|
||||
VKResourceStateTracker resources;
|
||||
std::unique_ptr<VKRenderGraph> render_graph;
|
||||
std::unique_ptr<CommandBufferLog> command_buffer;
|
||||
bool use_dynamic_rendering_local_read = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Union to create a dummy vulkan handler.
|
||||
*
|
||||
* Due to platform differences the actual VKObjectType type can be different (`uint64_t` or
|
||||
* `VkObjectType_T*`).
|
||||
*/
|
||||
template<typename VKObjectType> union VkHandle {
|
||||
VKObjectType vk_handle;
|
||||
uint64_t handle;
|
||||
|
||||
VkHandle(uint64_t handle) : handle(handle) {}
|
||||
|
||||
operator VKObjectType() const
|
||||
{
|
||||
return vk_handle;
|
||||
}
|
||||
};
|
||||
|
||||
static inline void submit(std::unique_ptr<VKRenderGraph> &render_graph,
|
||||
std::unique_ptr<CommandBufferLog> &command_buffer)
|
||||
{
|
||||
VKScheduler scheduler;
|
||||
VKCommandBuilder command_builder;
|
||||
Span<render_graph::NodeHandle> node_handles = scheduler.select_nodes(*render_graph);
|
||||
command_builder.build_nodes(*render_graph, *command_buffer, node_handles);
|
||||
|
||||
command_buffer->begin_recording();
|
||||
command_builder.record_commands(*render_graph, *command_buffer, node_handles);
|
||||
command_buffer->end_recording();
|
||||
|
||||
render_graph->reset();
|
||||
}
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,352 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "vk_command_buffer_wrapper.hh"
|
||||
#include "vk_backend.hh"
|
||||
#include "vk_device.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
VKCommandBufferWrapper::VKCommandBufferWrapper(VkCommandBuffer vk_command_buffer,
|
||||
const VKExtensions &extensions)
|
||||
: vk_command_buffer_(vk_command_buffer)
|
||||
{
|
||||
use_dynamic_rendering_local_read = extensions.dynamic_rendering_local_read;
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::begin_recording()
|
||||
{
|
||||
VkCommandBufferBeginInfo vk_command_buffer_begin_info = {
|
||||
VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
nullptr,
|
||||
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||
nullptr};
|
||||
vkBeginCommandBuffer(vk_command_buffer_, &vk_command_buffer_begin_info);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::end_recording()
|
||||
{
|
||||
vkEndCommandBuffer(vk_command_buffer_);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::bind_pipeline(VkPipelineBindPoint pipeline_bind_point,
|
||||
VkPipeline pipeline)
|
||||
{
|
||||
vkCmdBindPipeline(vk_command_buffer_, pipeline_bind_point, pipeline);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::bind_descriptor_sets(VkPipelineBindPoint pipeline_bind_point,
|
||||
VkPipelineLayout layout,
|
||||
uint32_t first_set,
|
||||
uint32_t descriptor_set_count,
|
||||
const VkDescriptorSet *p_descriptor_sets,
|
||||
uint32_t dynamic_offset_count,
|
||||
const uint32_t *p_dynamic_offsets)
|
||||
{
|
||||
vkCmdBindDescriptorSets(vk_command_buffer_,
|
||||
pipeline_bind_point,
|
||||
layout,
|
||||
first_set,
|
||||
descriptor_set_count,
|
||||
p_descriptor_sets,
|
||||
dynamic_offset_count,
|
||||
p_dynamic_offsets);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::bind_index_buffer(VkBuffer buffer,
|
||||
VkDeviceSize offset,
|
||||
VkIndexType index_type)
|
||||
{
|
||||
vkCmdBindIndexBuffer(vk_command_buffer_, buffer, offset, index_type);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::bind_vertex_buffers(uint32_t first_binding,
|
||||
uint32_t binding_count,
|
||||
const VkBuffer *p_buffers,
|
||||
const VkDeviceSize *p_offsets)
|
||||
{
|
||||
vkCmdBindVertexBuffers(vk_command_buffer_, first_binding, binding_count, p_buffers, p_offsets);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::draw(uint32_t vertex_count,
|
||||
uint32_t instance_count,
|
||||
uint32_t first_vertex,
|
||||
uint32_t first_instance)
|
||||
{
|
||||
vkCmdDraw(vk_command_buffer_, vertex_count, instance_count, first_vertex, first_instance);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::draw_indexed(uint32_t index_count,
|
||||
uint32_t instance_count,
|
||||
uint32_t first_index,
|
||||
int32_t vertex_offset,
|
||||
uint32_t first_instance)
|
||||
{
|
||||
vkCmdDrawIndexed(
|
||||
vk_command_buffer_, index_count, instance_count, first_index, vertex_offset, first_instance);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::draw_indirect(VkBuffer buffer,
|
||||
VkDeviceSize offset,
|
||||
uint32_t draw_count,
|
||||
uint32_t stride)
|
||||
{
|
||||
vkCmdDrawIndirect(vk_command_buffer_, buffer, offset, draw_count, stride);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::draw_indexed_indirect(VkBuffer buffer,
|
||||
VkDeviceSize offset,
|
||||
uint32_t draw_count,
|
||||
uint32_t stride)
|
||||
{
|
||||
vkCmdDrawIndexedIndirect(vk_command_buffer_, buffer, offset, draw_count, stride);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::dispatch(uint32_t group_count_x,
|
||||
uint32_t group_count_y,
|
||||
uint32_t group_count_z)
|
||||
{
|
||||
vkCmdDispatch(vk_command_buffer_, group_count_x, group_count_y, group_count_z);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::dispatch_indirect(VkBuffer buffer, VkDeviceSize offset)
|
||||
{
|
||||
vkCmdDispatchIndirect(vk_command_buffer_, buffer, offset);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::update_buffer(VkBuffer dst_buffer,
|
||||
VkDeviceSize dst_offset,
|
||||
VkDeviceSize data_size,
|
||||
const void *p_data)
|
||||
{
|
||||
vkCmdUpdateBuffer(vk_command_buffer_, dst_buffer, dst_offset, data_size, p_data);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::copy_buffer(VkBuffer src_buffer,
|
||||
VkBuffer dst_buffer,
|
||||
uint32_t region_count,
|
||||
const VkBufferCopy *p_regions)
|
||||
{
|
||||
vkCmdCopyBuffer(vk_command_buffer_, src_buffer, dst_buffer, region_count, p_regions);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::copy_image(VkImage src_image,
|
||||
VkImageLayout src_image_layout,
|
||||
VkImage dst_image,
|
||||
VkImageLayout dst_image_layout,
|
||||
uint32_t region_count,
|
||||
const VkImageCopy *p_regions)
|
||||
{
|
||||
vkCmdCopyImage(vk_command_buffer_,
|
||||
src_image,
|
||||
src_image_layout,
|
||||
dst_image,
|
||||
dst_image_layout,
|
||||
region_count,
|
||||
p_regions);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::blit_image(VkImage src_image,
|
||||
VkImageLayout src_image_layout,
|
||||
VkImage dst_image,
|
||||
VkImageLayout dst_image_layout,
|
||||
uint32_t region_count,
|
||||
const VkImageBlit *p_regions,
|
||||
VkFilter filter)
|
||||
{
|
||||
vkCmdBlitImage(vk_command_buffer_,
|
||||
src_image,
|
||||
src_image_layout,
|
||||
dst_image,
|
||||
dst_image_layout,
|
||||
region_count,
|
||||
p_regions,
|
||||
filter);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::copy_buffer_to_image(VkBuffer src_buffer,
|
||||
VkImage dst_image,
|
||||
VkImageLayout dst_image_layout,
|
||||
uint32_t region_count,
|
||||
const VkBufferImageCopy *p_regions)
|
||||
{
|
||||
vkCmdCopyBufferToImage(
|
||||
vk_command_buffer_, src_buffer, dst_image, dst_image_layout, region_count, p_regions);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::copy_image_to_buffer(VkImage src_image,
|
||||
VkImageLayout src_image_layout,
|
||||
VkBuffer dst_buffer,
|
||||
uint32_t region_count,
|
||||
const VkBufferImageCopy *p_regions)
|
||||
{
|
||||
vkCmdCopyImageToBuffer(
|
||||
vk_command_buffer_, src_image, src_image_layout, dst_buffer, region_count, p_regions);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::fill_buffer(VkBuffer dst_buffer,
|
||||
VkDeviceSize dst_offset,
|
||||
VkDeviceSize size,
|
||||
uint32_t data)
|
||||
{
|
||||
vkCmdFillBuffer(vk_command_buffer_, dst_buffer, dst_offset, size, data);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::clear_color_image(VkImage image,
|
||||
VkImageLayout image_layout,
|
||||
const VkClearColorValue *p_color,
|
||||
uint32_t range_count,
|
||||
const VkImageSubresourceRange *p_ranges)
|
||||
{
|
||||
vkCmdClearColorImage(vk_command_buffer_, image, image_layout, p_color, range_count, p_ranges);
|
||||
}
|
||||
void VKCommandBufferWrapper::clear_depth_stencil_image(
|
||||
VkImage image,
|
||||
VkImageLayout image_layout,
|
||||
const VkClearDepthStencilValue *p_depth_stencil,
|
||||
uint32_t range_count,
|
||||
const VkImageSubresourceRange *p_ranges)
|
||||
{
|
||||
vkCmdClearDepthStencilImage(
|
||||
vk_command_buffer_, image, image_layout, p_depth_stencil, range_count, p_ranges);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::clear_attachments(uint32_t attachment_count,
|
||||
const VkClearAttachment *p_attachments,
|
||||
uint32_t rect_count,
|
||||
const VkClearRect *p_rects)
|
||||
{
|
||||
vkCmdClearAttachments(vk_command_buffer_, attachment_count, p_attachments, rect_count, p_rects);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::pipeline_barrier(
|
||||
VkPipelineStageFlags src_stage_mask,
|
||||
VkPipelineStageFlags dst_stage_mask,
|
||||
VkDependencyFlags dependency_flags,
|
||||
uint32_t memory_barrier_count,
|
||||
const VkMemoryBarrier *p_memory_barriers,
|
||||
uint32_t buffer_memory_barrier_count,
|
||||
const VkBufferMemoryBarrier *p_buffer_memory_barriers,
|
||||
uint32_t image_memory_barrier_count,
|
||||
const VkImageMemoryBarrier *p_image_memory_barriers)
|
||||
{
|
||||
vkCmdPipelineBarrier(vk_command_buffer_,
|
||||
src_stage_mask,
|
||||
dst_stage_mask,
|
||||
dependency_flags,
|
||||
memory_barrier_count,
|
||||
p_memory_barriers,
|
||||
buffer_memory_barrier_count,
|
||||
p_buffer_memory_barriers,
|
||||
image_memory_barrier_count,
|
||||
p_image_memory_barriers);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::push_constants(VkPipelineLayout layout,
|
||||
VkShaderStageFlags stage_flags,
|
||||
uint32_t offset,
|
||||
uint32_t size,
|
||||
const void *p_values)
|
||||
{
|
||||
vkCmdPushConstants(vk_command_buffer_, layout, stage_flags, offset, size, p_values);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::set_viewport(const Vector<VkViewport> viewports)
|
||||
{
|
||||
vkCmdSetViewport(vk_command_buffer_, 0, viewports.size(), viewports.data());
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::set_scissor(const Vector<VkRect2D> scissors)
|
||||
{
|
||||
vkCmdSetScissor(vk_command_buffer_, 0, scissors.size(), scissors.data());
|
||||
}
|
||||
void VKCommandBufferWrapper::set_line_width(const float line_width)
|
||||
{
|
||||
vkCmdSetLineWidth(vk_command_buffer_, line_width);
|
||||
}
|
||||
void VKCommandBufferWrapper::set_stencil_compare_mask(const uint32_t compare_mask)
|
||||
{
|
||||
vkCmdSetStencilCompareMask(vk_command_buffer_, VK_STENCIL_FACE_FRONT_AND_BACK, compare_mask);
|
||||
}
|
||||
void VKCommandBufferWrapper::set_stencil_write_mask(const uint32_t write_mask)
|
||||
{
|
||||
vkCmdSetStencilWriteMask(vk_command_buffer_, VK_STENCIL_FACE_FRONT_AND_BACK, write_mask);
|
||||
}
|
||||
void VKCommandBufferWrapper::set_stencil_reference(const uint32_t reference)
|
||||
{
|
||||
vkCmdSetStencilReference(vk_command_buffer_, VK_STENCIL_FACE_FRONT_AND_BACK, reference);
|
||||
}
|
||||
void VKCommandBufferWrapper::set_front_face(const VkFrontFace front_face)
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
BLI_assert(device.functions.vkCmdSetFrontFace);
|
||||
device.functions.vkCmdSetFrontFace(vk_command_buffer_, front_face);
|
||||
}
|
||||
void VKCommandBufferWrapper::set_vertex_input(
|
||||
Span<VkVertexInputBindingDescription2EXT> vertex_binding_descriptions,
|
||||
Span<VkVertexInputAttributeDescription2EXT> vertex_attribute_descriptions)
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
BLI_assert(device.functions.vkCmdSetVertexInput);
|
||||
device.functions.vkCmdSetVertexInput(vk_command_buffer_,
|
||||
vertex_binding_descriptions.size(),
|
||||
vertex_binding_descriptions.data(),
|
||||
vertex_attribute_descriptions.size(),
|
||||
vertex_attribute_descriptions.data());
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::begin_rendering(const VkRenderingInfo *p_rendering_info)
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
BLI_assert(device.functions.vkCmdBeginRendering);
|
||||
device.functions.vkCmdBeginRendering(vk_command_buffer_, p_rendering_info);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::end_rendering()
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
BLI_assert(device.functions.vkCmdEndRendering);
|
||||
device.functions.vkCmdEndRendering(vk_command_buffer_);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::begin_query(VkQueryPool vk_query_pool,
|
||||
uint32_t query_index,
|
||||
VkQueryControlFlags vk_query_control_flags)
|
||||
{
|
||||
vkCmdBeginQuery(vk_command_buffer_, vk_query_pool, query_index, vk_query_control_flags);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::end_query(VkQueryPool vk_query_pool, uint32_t query_index)
|
||||
{
|
||||
vkCmdEndQuery(vk_command_buffer_, vk_query_pool, query_index);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::reset_query_pool(VkQueryPool vk_query_pool,
|
||||
uint32_t first_query,
|
||||
uint32_t query_count)
|
||||
{
|
||||
vkCmdResetQueryPool(vk_command_buffer_, vk_query_pool, first_query, query_count);
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::begin_debug_utils_label(
|
||||
const VkDebugUtilsLabelEXT *vk_debug_utils_label)
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
if (device.functions.vkCmdBeginDebugUtilsLabel) {
|
||||
device.functions.vkCmdBeginDebugUtilsLabel(vk_command_buffer_, vk_debug_utils_label);
|
||||
}
|
||||
}
|
||||
|
||||
void VKCommandBufferWrapper::end_debug_utils_label()
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
if (device.functions.vkCmdEndDebugUtilsLabel) {
|
||||
device.functions.vkCmdEndDebugUtilsLabel(vk_command_buffer_);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,284 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vk_common.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
struct VKExtensions;
|
||||
}
|
||||
|
||||
namespace gpu::render_graph {
|
||||
class VKCommandBufferInterface {
|
||||
public:
|
||||
bool use_dynamic_rendering_local_read = true;
|
||||
|
||||
VKCommandBufferInterface() {}
|
||||
virtual ~VKCommandBufferInterface() = default;
|
||||
|
||||
virtual void begin_recording() = 0;
|
||||
virtual void end_recording() = 0;
|
||||
|
||||
virtual void bind_pipeline(VkPipelineBindPoint pipeline_bind_point, VkPipeline pipeline) = 0;
|
||||
virtual void bind_descriptor_sets(VkPipelineBindPoint pipeline_bind_point,
|
||||
VkPipelineLayout layout,
|
||||
uint32_t first_set,
|
||||
uint32_t descriptor_set_count,
|
||||
const VkDescriptorSet *p_descriptor_sets,
|
||||
uint32_t dynamic_offset_count,
|
||||
const uint32_t *p_dynamic_offsets) = 0;
|
||||
virtual void bind_index_buffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType index_type) = 0;
|
||||
virtual void bind_vertex_buffers(uint32_t first_binding,
|
||||
uint32_t binding_count,
|
||||
const VkBuffer *p_buffers,
|
||||
const VkDeviceSize *p_offsets) = 0;
|
||||
virtual void draw(uint32_t vertex_count,
|
||||
uint32_t instance_count,
|
||||
uint32_t first_vertex,
|
||||
uint32_t first_instance) = 0;
|
||||
virtual void draw_indexed(uint32_t index_count,
|
||||
uint32_t instance_count,
|
||||
uint32_t first_index,
|
||||
int32_t vertex_offset,
|
||||
uint32_t first_instance) = 0;
|
||||
virtual void draw_indirect(VkBuffer buffer,
|
||||
VkDeviceSize offset,
|
||||
uint32_t draw_count,
|
||||
uint32_t stride) = 0;
|
||||
virtual void draw_indexed_indirect(VkBuffer buffer,
|
||||
VkDeviceSize offset,
|
||||
uint32_t draw_count,
|
||||
uint32_t stride) = 0;
|
||||
virtual void dispatch(uint32_t group_count_x,
|
||||
uint32_t group_count_y,
|
||||
uint32_t group_count_z) = 0;
|
||||
virtual void dispatch_indirect(VkBuffer buffer, VkDeviceSize offset) = 0;
|
||||
virtual void update_buffer(VkBuffer dst_buffer,
|
||||
VkDeviceSize dst_offset,
|
||||
VkDeviceSize data_size,
|
||||
const void *p_data) = 0;
|
||||
virtual void copy_buffer(VkBuffer src_buffer,
|
||||
VkBuffer dst_buffer,
|
||||
uint32_t region_count,
|
||||
const VkBufferCopy *p_regions) = 0;
|
||||
virtual void copy_image(VkImage src_image,
|
||||
VkImageLayout src_image_layout,
|
||||
VkImage dst_image,
|
||||
VkImageLayout dst_image_layout,
|
||||
uint32_t region_count,
|
||||
const VkImageCopy *p_regions) = 0;
|
||||
virtual void blit_image(VkImage src_image,
|
||||
VkImageLayout src_image_layout,
|
||||
VkImage dst_image,
|
||||
VkImageLayout dst_image_layout,
|
||||
uint32_t region_count,
|
||||
const VkImageBlit *p_regions,
|
||||
VkFilter filter) = 0;
|
||||
virtual void copy_buffer_to_image(VkBuffer src_buffer,
|
||||
VkImage dst_image,
|
||||
VkImageLayout dst_image_layout,
|
||||
uint32_t region_count,
|
||||
const VkBufferImageCopy *p_regions) = 0;
|
||||
virtual void copy_image_to_buffer(VkImage src_image,
|
||||
VkImageLayout src_image_layout,
|
||||
VkBuffer dst_buffer,
|
||||
uint32_t region_count,
|
||||
const VkBufferImageCopy *p_regions) = 0;
|
||||
virtual void fill_buffer(VkBuffer dst_buffer,
|
||||
VkDeviceSize dst_offset,
|
||||
VkDeviceSize size,
|
||||
uint32_t data) = 0;
|
||||
virtual void clear_color_image(VkImage image,
|
||||
VkImageLayout image_layout,
|
||||
const VkClearColorValue *p_color,
|
||||
uint32_t range_count,
|
||||
const VkImageSubresourceRange *p_ranges) = 0;
|
||||
virtual void clear_depth_stencil_image(VkImage image,
|
||||
VkImageLayout image_layout,
|
||||
const VkClearDepthStencilValue *p_depth_stencil,
|
||||
uint32_t range_count,
|
||||
const VkImageSubresourceRange *p_ranges) = 0;
|
||||
virtual void clear_attachments(uint32_t attachment_count,
|
||||
const VkClearAttachment *p_attachments,
|
||||
uint32_t rect_count,
|
||||
const VkClearRect *p_rects) = 0;
|
||||
virtual void pipeline_barrier(VkPipelineStageFlags src_stage_mask,
|
||||
VkPipelineStageFlags dst_stage_mask,
|
||||
VkDependencyFlags dependency_flags,
|
||||
uint32_t memory_barrier_count,
|
||||
const VkMemoryBarrier *p_memory_barriers,
|
||||
uint32_t buffer_memory_barrier_count,
|
||||
const VkBufferMemoryBarrier *p_buffer_memory_barriers,
|
||||
uint32_t image_memory_barrier_count,
|
||||
const VkImageMemoryBarrier *p_image_memory_barriers) = 0;
|
||||
virtual void push_constants(VkPipelineLayout layout,
|
||||
VkShaderStageFlags stage_flags,
|
||||
uint32_t offset,
|
||||
uint32_t size,
|
||||
const void *p_values) = 0;
|
||||
virtual void begin_query(VkQueryPool vk_query_pool,
|
||||
uint32_t query_index,
|
||||
VkQueryControlFlags vk_query_control_flags) = 0;
|
||||
virtual void end_query(VkQueryPool vk_query_pool, uint32_t query_index) = 0;
|
||||
virtual void reset_query_pool(VkQueryPool vk_query_pool,
|
||||
uint32_t first_query,
|
||||
uint32_t query_count) = 0;
|
||||
/* Dynamic states. */
|
||||
|
||||
virtual void set_viewport(const Vector<VkViewport> viewports) = 0;
|
||||
virtual void set_scissor(const Vector<VkRect2D> scissors) = 0;
|
||||
virtual void set_line_width(const float line_width) = 0;
|
||||
virtual void set_stencil_compare_mask(const uint32_t compare_mask) = 0;
|
||||
virtual void set_stencil_write_mask(const uint32_t write_mask) = 0;
|
||||
virtual void set_stencil_reference(const uint32_t reference) = 0;
|
||||
/* VK_EXT_extended_dynamic_state */
|
||||
virtual void set_front_face(const VkFrontFace front_face) = 0;
|
||||
/* VK_EXT_vertex_input_dynamic_state */
|
||||
virtual void set_vertex_input(
|
||||
Span<VkVertexInputBindingDescription2EXT> vertex_binding_descriptions,
|
||||
Span<VkVertexInputAttributeDescription2EXT> vertex_attribute_descriptions) = 0;
|
||||
/* VK_KHR_dynamic_rendering */
|
||||
virtual void begin_rendering(const VkRenderingInfo *p_rendering_info) = 0;
|
||||
virtual void end_rendering() = 0;
|
||||
/* VK_EXT_debug_utils */
|
||||
virtual void begin_debug_utils_label(const VkDebugUtilsLabelEXT *vk_debug_utils_label) = 0;
|
||||
virtual void end_debug_utils_label() = 0;
|
||||
};
|
||||
|
||||
class VKCommandBufferWrapper : public VKCommandBufferInterface {
|
||||
private:
|
||||
VkCommandBuffer vk_command_buffer_ = VK_NULL_HANDLE;
|
||||
|
||||
public:
|
||||
VKCommandBufferWrapper(VkCommandBuffer vk_command_buffer, const VKExtensions &extensions);
|
||||
|
||||
void begin_recording() override;
|
||||
void end_recording() override;
|
||||
|
||||
void bind_pipeline(VkPipelineBindPoint pipeline_bind_point, VkPipeline pipeline) override;
|
||||
void bind_descriptor_sets(VkPipelineBindPoint pipeline_bind_point,
|
||||
VkPipelineLayout layout,
|
||||
uint32_t first_set,
|
||||
uint32_t descriptor_set_count,
|
||||
const VkDescriptorSet *p_descriptor_sets,
|
||||
uint32_t dynamic_offset_count,
|
||||
const uint32_t *p_dynamic_offsets) override;
|
||||
void bind_index_buffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType index_type) override;
|
||||
void bind_vertex_buffers(uint32_t first_binding,
|
||||
uint32_t binding_count,
|
||||
const VkBuffer *p_buffers,
|
||||
const VkDeviceSize *p_offsets) override;
|
||||
void draw(uint32_t vertex_count,
|
||||
uint32_t instance_count,
|
||||
uint32_t first_vertex,
|
||||
uint32_t first_instance) override;
|
||||
void draw_indexed(uint32_t index_count,
|
||||
uint32_t instance_count,
|
||||
uint32_t first_index,
|
||||
int32_t vertex_offset,
|
||||
uint32_t first_instance) override;
|
||||
void draw_indirect(VkBuffer buffer,
|
||||
VkDeviceSize offset,
|
||||
uint32_t draw_count,
|
||||
uint32_t stride) override;
|
||||
void draw_indexed_indirect(VkBuffer buffer,
|
||||
VkDeviceSize offset,
|
||||
uint32_t draw_count,
|
||||
uint32_t stride) override;
|
||||
void dispatch(uint32_t group_count_x, uint32_t group_count_y, uint32_t group_count_z) override;
|
||||
void dispatch_indirect(VkBuffer buffer, VkDeviceSize offset) override;
|
||||
void update_buffer(VkBuffer dst_buffer,
|
||||
VkDeviceSize dst_offset,
|
||||
VkDeviceSize data_size,
|
||||
const void *p_data) override;
|
||||
void copy_buffer(VkBuffer src_buffer,
|
||||
VkBuffer dst_buffer,
|
||||
uint32_t region_count,
|
||||
const VkBufferCopy *p_regions) override;
|
||||
void copy_image(VkImage src_image,
|
||||
VkImageLayout src_image_layout,
|
||||
VkImage dst_image,
|
||||
VkImageLayout dst_image_layout,
|
||||
uint32_t region_count,
|
||||
const VkImageCopy *p_regions) override;
|
||||
void blit_image(VkImage src_image,
|
||||
VkImageLayout src_image_layout,
|
||||
VkImage dst_image,
|
||||
VkImageLayout dst_image_layout,
|
||||
uint32_t region_count,
|
||||
const VkImageBlit *p_regions,
|
||||
VkFilter filter) override;
|
||||
void copy_buffer_to_image(VkBuffer src_buffer,
|
||||
VkImage dst_image,
|
||||
VkImageLayout dst_image_layout,
|
||||
uint32_t region_count,
|
||||
const VkBufferImageCopy *p_regions) override;
|
||||
void copy_image_to_buffer(VkImage src_image,
|
||||
VkImageLayout src_image_layout,
|
||||
VkBuffer dst_buffer,
|
||||
uint32_t region_count,
|
||||
const VkBufferImageCopy *p_regions) override;
|
||||
void fill_buffer(VkBuffer dst_buffer,
|
||||
VkDeviceSize dst_offset,
|
||||
VkDeviceSize size,
|
||||
uint32_t data) override;
|
||||
void clear_color_image(VkImage image,
|
||||
VkImageLayout image_layout,
|
||||
const VkClearColorValue *p_color,
|
||||
uint32_t range_count,
|
||||
const VkImageSubresourceRange *p_ranges) override;
|
||||
void clear_depth_stencil_image(VkImage image,
|
||||
VkImageLayout image_layout,
|
||||
const VkClearDepthStencilValue *p_depth_stencil,
|
||||
uint32_t range_count,
|
||||
const VkImageSubresourceRange *p_ranges) override;
|
||||
void clear_attachments(uint32_t attachment_count,
|
||||
const VkClearAttachment *p_attachments,
|
||||
uint32_t rect_count,
|
||||
const VkClearRect *p_rects) override;
|
||||
void pipeline_barrier(VkPipelineStageFlags src_stage_mask,
|
||||
VkPipelineStageFlags dst_stage_mask,
|
||||
VkDependencyFlags dependency_flags,
|
||||
uint32_t memory_barrier_count,
|
||||
const VkMemoryBarrier *p_memory_barriers,
|
||||
uint32_t buffer_memory_barrier_count,
|
||||
const VkBufferMemoryBarrier *p_buffer_memory_barriers,
|
||||
uint32_t image_memory_barrier_count,
|
||||
const VkImageMemoryBarrier *p_image_memory_barriers) override;
|
||||
void push_constants(VkPipelineLayout layout,
|
||||
VkShaderStageFlags stage_flags,
|
||||
uint32_t offset,
|
||||
uint32_t size,
|
||||
const void *p_values) override;
|
||||
void set_viewport(const Vector<VkViewport> viewports) override;
|
||||
void set_scissor(const Vector<VkRect2D> scissors) override;
|
||||
void set_line_width(const float line_width) override;
|
||||
void set_front_face(const VkFrontFace front_face) override;
|
||||
void set_vertex_input(
|
||||
Span<VkVertexInputBindingDescription2EXT> vertex_binding_descriptions,
|
||||
Span<VkVertexInputAttributeDescription2EXT> vertex_attribute_descriptions) override;
|
||||
|
||||
void set_stencil_compare_mask(const uint32_t compare_mask) override;
|
||||
void set_stencil_write_mask(const uint32_t write_mask) override;
|
||||
void set_stencil_reference(const uint32_t reference) override;
|
||||
void begin_query(VkQueryPool vk_query_pool,
|
||||
uint32_t query_index,
|
||||
VkQueryControlFlags vk_query_control_flags) override;
|
||||
void end_query(VkQueryPool vk_query_pool, uint32_t query_index) override;
|
||||
void reset_query_pool(VkQueryPool, uint32_t first_query, uint32_t query_count) override;
|
||||
void begin_rendering(const VkRenderingInfo *p_rendering_info) override;
|
||||
void end_rendering() override;
|
||||
void begin_debug_utils_label(const VkDebugUtilsLabelEXT *vk_debug_utils_label) override;
|
||||
void end_debug_utils_label() override;
|
||||
};
|
||||
} // namespace gpu::render_graph
|
||||
|
||||
} // namespace blender
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,376 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_set.hh"
|
||||
|
||||
#include "vk_common.hh"
|
||||
#include "vk_render_graph_node.hh"
|
||||
#include "vk_scheduler.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
class VKRenderGraph;
|
||||
|
||||
struct LayeredImageBinding {
|
||||
VkImage vk_image;
|
||||
VkImageLayout vk_image_layout;
|
||||
uint32_t layer;
|
||||
uint32_t layer_count;
|
||||
};
|
||||
|
||||
/**
|
||||
* Build the command buffer for sending to the device queue.
|
||||
*
|
||||
* Determine which nodes needs to be scheduled, Then for each node generate the needed pipeline
|
||||
* barriers and commands.
|
||||
*/
|
||||
class VKCommandBuilder {
|
||||
private:
|
||||
/**
|
||||
* List of all extracted VkBufferMemoryBarriers. These barriers will be referenced by
|
||||
* Barrier::buffer_memory_barriers.
|
||||
*/
|
||||
Vector<VkBufferMemoryBarrier> vk_buffer_memory_barriers_;
|
||||
/**
|
||||
* List of all extracted VkImageMemoryBarriers. These barriers will be referenced by
|
||||
* Barrier::image_memory_barriers.
|
||||
*/
|
||||
Vector<VkImageMemoryBarrier> vk_image_memory_barriers_;
|
||||
|
||||
struct Barrier {
|
||||
/** Index range into `VKCommandBuilder::vk_buffer_memory_barriers_` */
|
||||
IndexRange buffer_memory_barriers;
|
||||
/** Index range into `VKCommandBuilder::vk_image_memory_barriers_` */
|
||||
IndexRange image_memory_barriers;
|
||||
|
||||
VkPipelineStageFlags src_stage_mask = VK_PIPELINE_STAGE_NONE;
|
||||
VkPipelineStageFlags dst_stage_mask = VK_PIPELINE_STAGE_NONE;
|
||||
|
||||
bool is_empty() const
|
||||
{
|
||||
return buffer_memory_barriers.is_empty() && image_memory_barriers.is_empty();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* An image can consist out of layers and mipmaps. In some cases each layer/mipmap can get
|
||||
* different VkImageLayouts. To not complicate the global state tracker we track sub-resources
|
||||
* only when building the barriers.
|
||||
*/
|
||||
struct ImageTracker {
|
||||
/**
|
||||
* Local reference to the active command builder.
|
||||
*
|
||||
* The reference is used to add VkImageMemoryBarrier's to the command builder.
|
||||
*/
|
||||
VKCommandBuilder &command_builder;
|
||||
ImageTracker(VKCommandBuilder &command_builder) : command_builder(command_builder) {}
|
||||
|
||||
/**
|
||||
* All layered attachments of the last rendering scope (VKNodeType::BEGIN_RENDERING).
|
||||
*
|
||||
* When binding layer from these images we expect that they aren't used as attachment and can
|
||||
* be transitioned into a different image layout. These image layouts are stored in
|
||||
* `layered_bindings`.
|
||||
*/
|
||||
Set<VkImage> tracked_attachments;
|
||||
|
||||
/**
|
||||
* Keep track of the changes to images since the start of the rendering scope. This allows
|
||||
* reverting and rebuilding the correct state when ending or resuming the render scope.
|
||||
*/
|
||||
struct SubImageChange {
|
||||
VkImage vk_image;
|
||||
VkImageLayout vk_image_layout;
|
||||
VkImageAspectFlags vk_image_aspect;
|
||||
VKSubImageRange subimage;
|
||||
};
|
||||
Vector<SubImageChange> changes;
|
||||
|
||||
/**
|
||||
* Update the layered attachments list when beginning a new render scope.
|
||||
*
|
||||
* node_handle should be a handle that points to a VKNodeType::BEGIN_RENDERING.
|
||||
* Any attachments that are layered will be added to the `tracked_attachments` list.
|
||||
*/
|
||||
void begin(const VKRenderGraph &render_graph, NodeHandle node_handle);
|
||||
|
||||
/**
|
||||
* Is layered tracking enabled for the given vk_image.
|
||||
*/
|
||||
inline bool contains(VkImage vk_image) const
|
||||
{
|
||||
return tracked_attachments.contains(vk_image);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the layout of a layer.
|
||||
*
|
||||
* - `old_layout` should be the expected layout of the full image.
|
||||
*/
|
||||
void update(VkImage vk_image,
|
||||
const VKSubImageRange &subimage,
|
||||
VkImageLayout old_layout,
|
||||
VkImageLayout new_layout,
|
||||
VkImageAspectFlags aspect,
|
||||
Barrier &r_barrier);
|
||||
/**
|
||||
* Ensure the layout of a mipmap level.
|
||||
*
|
||||
* - `old_layout` should be the expected layout of the full image.
|
||||
*/
|
||||
void update(VkImage vk_image,
|
||||
uint32_t mipmap_level,
|
||||
VkImageLayout old_layout,
|
||||
VkImageLayout new_layout,
|
||||
VkImageAspectFlags aspect,
|
||||
Barrier &r_barrier);
|
||||
|
||||
/**
|
||||
* End layer tracking.
|
||||
*
|
||||
* All modified layers (layer_tracking_update) will be changed back to the image layout of
|
||||
* the texture (most likely a `VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL`).
|
||||
*
|
||||
* Render suspension/resuming will not work after calling this method.
|
||||
*/
|
||||
void end(Barrier &r_barrier, bool use_local_read);
|
||||
|
||||
/**
|
||||
* Suspend layer tracking
|
||||
*
|
||||
* Temporarily suspend layer tracking. This transits all modified layers back to its original
|
||||
* layout.
|
||||
* NOTE: Only call this method when you the rendering will be resumed, otherwise use
|
||||
* `layer_tracking_end`.
|
||||
*/
|
||||
void suspend(Barrier &r_barrier, bool use_local_read);
|
||||
|
||||
/**
|
||||
* Resume suspended layer tracking.
|
||||
*
|
||||
* Resume suspended layer tracking. This transits all registered layers back to its modified
|
||||
* state.
|
||||
*/
|
||||
void resume(Barrier &r_barrier, bool use_local_read);
|
||||
};
|
||||
|
||||
/**
|
||||
* Index range of the nodes of a group.
|
||||
*
|
||||
* The indexes are to `VKScheduler::result_` that is passed along `Span<NodeHandle> nodes` of
|
||||
* `build_nodes`.
|
||||
*/
|
||||
using GroupNodes = IndexRange;
|
||||
|
||||
/**
|
||||
* Index range into barrier_list_;
|
||||
*/
|
||||
using Barriers = IndexRange;
|
||||
using BarrierIndex = int64_t;
|
||||
|
||||
/** Per group store the indices of the nodes. */
|
||||
Vector<GroupNodes> group_nodes_;
|
||||
/** Barriers that will be recorded just before the commands of a group are recorded. */
|
||||
Vector<Barriers> group_pre_barriers_;
|
||||
/** Barriers that will be recorded after a group is recorded. */
|
||||
Vector<Barriers> group_post_barriers_;
|
||||
/**
|
||||
* Barriers that will be recorded just before the commands of a specific node are recorded. The
|
||||
* barriers are stored per NodeHandle and in the same order as `Span<NodeHandle> nodes`.
|
||||
*/
|
||||
Vector<Barriers> node_pre_barriers_;
|
||||
|
||||
/** List of all generated barriers. */
|
||||
Vector<Barrier> barrier_list_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Build execution groups and barriers.
|
||||
* This method should be performed when the resources are locked.
|
||||
*/
|
||||
void build_nodes(VKRenderGraph &render_graph,
|
||||
VKCommandBufferInterface &command_buffer,
|
||||
Span<NodeHandle> node_handles);
|
||||
|
||||
/**
|
||||
* Record commands of the nodes provided by the `node_handles` parameter. The commands are
|
||||
* recorded into the given `command_buffer`.
|
||||
*
|
||||
* `build_nodes` needs to be called before calling with exact the same parameters.
|
||||
*/
|
||||
void record_commands(VKRenderGraph &render_graph,
|
||||
VKCommandBufferInterface &command_buffer,
|
||||
Span<NodeHandle> node_handles);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Split the node_handles in logical groups.
|
||||
*
|
||||
* A new group is created when the next node is switching from data/compute to graphics and each
|
||||
* data/compute is also put in its own group.
|
||||
*/
|
||||
void groups_init(const VKRenderGraph &render_graph, Span<NodeHandle> node_handles);
|
||||
|
||||
/**
|
||||
* Extract the memory/buffer/image barriers from the command groups and add them to the pre/post
|
||||
* barriers.
|
||||
*
|
||||
* This process is single threaded as resource states change during the extraction process. The
|
||||
* result of this function would allow the sub builders to be built in parallel.
|
||||
*/
|
||||
void groups_extract_barriers(VKRenderGraph &render_graph,
|
||||
Span<NodeHandle> node_handles,
|
||||
bool use_local_read);
|
||||
|
||||
/**
|
||||
* Record all the commands for all the groups to the command buffer.
|
||||
*/
|
||||
void groups_build_commands(VKRenderGraph &render_graph,
|
||||
VKCommandBufferInterface &command_buffer,
|
||||
Span<NodeHandle> node_handles);
|
||||
|
||||
/**
|
||||
* Build the pipeline barriers that should be recorded before any other commands of the node
|
||||
* group the given node is part of is being recorded.
|
||||
*/
|
||||
void build_pipeline_barriers(VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags pipeline_stage,
|
||||
ImageTracker &image_tracker,
|
||||
Barrier &r_barrier,
|
||||
bool within_rendering = false);
|
||||
void reset_barriers(Barrier &r_barrier);
|
||||
void send_pipeline_barriers(VKCommandBufferInterface &command_buffer,
|
||||
const Barrier &barrier,
|
||||
bool within_rendering);
|
||||
|
||||
void add_buffer_barriers(VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags node_stages,
|
||||
Barrier &r_barrier);
|
||||
void add_buffer_barrier(VkBuffer vk_buffer,
|
||||
Barrier &r_barrier,
|
||||
VkAccessFlags src_access_mask,
|
||||
VkAccessFlags dst_access_mask);
|
||||
void add_buffer_read_barriers(VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags node_stages,
|
||||
Barrier &r_barrier);
|
||||
void add_buffer_write_barriers(VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags node_stages,
|
||||
Barrier &r_barrier);
|
||||
|
||||
void add_image_barriers(VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags node_stages,
|
||||
ImageTracker &image_tracker,
|
||||
Barrier &r_barrier,
|
||||
bool within_rendering);
|
||||
void add_image_barrier(VkImage vk_image,
|
||||
Barrier &r_barrier,
|
||||
VkAccessFlags src_access_mask,
|
||||
VkAccessFlags dst_access_mask,
|
||||
VkImageLayout old_image_layout,
|
||||
VkImageLayout new_image_layout,
|
||||
VkImageAspectFlags aspect_mask,
|
||||
const VKSubImageRange &subimage);
|
||||
void add_image_read_barriers(VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags node_stages,
|
||||
ImageTracker &image_tracker,
|
||||
Barrier &r_barrier,
|
||||
bool within_rendering);
|
||||
void add_image_write_barriers(VKRenderGraph &render_graph,
|
||||
NodeHandle node_handle,
|
||||
VkPipelineStageFlags node_stages,
|
||||
ImageTracker &image_tracker,
|
||||
Barrier &r_barrier,
|
||||
bool within_rendering);
|
||||
|
||||
struct DebugGroups {
|
||||
/**
|
||||
* Index of the active debug_group. Points to an element in
|
||||
* `VKRenderGraph.debug_.used_groups`.
|
||||
*/
|
||||
int64_t active_debug_group_id = -1;
|
||||
/** Current level of debug groups. (number of nested debug groups). */
|
||||
int debug_level = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Ensure that the debug group associated with the given node_handle is activated.
|
||||
*
|
||||
* When activating it determines how to walk from the current debug group to the to be activated
|
||||
* debug group by performing end/begin commands on the command buffer.
|
||||
*
|
||||
* This ensures that when nodes are reordered that they still appear in the right debug group.
|
||||
*/
|
||||
void activate_debug_group(VKRenderGraph &render_graph,
|
||||
VKCommandBufferInterface &command_buffer,
|
||||
DebugGroups &debug_groups,
|
||||
NodeHandle node_handle);
|
||||
|
||||
/**
|
||||
* Make sure no debugging groups are active anymore.
|
||||
*/
|
||||
void finish_debug_groups(VKCommandBufferInterface &command_buffer, DebugGroups &debug_groups);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Update the layered attachments list when beginning a new render scope.
|
||||
*/
|
||||
void layer_tracking_begin(const VKRenderGraph &render_graph, NodeHandle node_handle);
|
||||
|
||||
/**
|
||||
* Ensure the layout of a layer.
|
||||
*
|
||||
* - `old_layout` should be the expected layout of the full image.
|
||||
*/
|
||||
void layer_tracking_update(VkImage vk_image,
|
||||
uint32_t layer,
|
||||
uint32_t layer_count,
|
||||
VkImageLayout old_layout,
|
||||
VkImageLayout new_layout);
|
||||
|
||||
/**
|
||||
* End layer tracking.
|
||||
*
|
||||
* All modified layers (layer_tracking_update) will be changed back to the image layout of
|
||||
* the texture (most likely a `VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL`).
|
||||
*
|
||||
* Render suspension/resuming will not work after calling this method.
|
||||
*/
|
||||
void layer_tracking_end(VKCommandBufferInterface &command_buffer);
|
||||
|
||||
/**
|
||||
* Suspend layer tracking
|
||||
*
|
||||
* Temporarily suspend layer tracking. This transits all modified layers back to its original
|
||||
* layout.
|
||||
* NOTE: Only call this method when you the rendering will be resumed, otherwise use
|
||||
* `layer_tracking_end`.
|
||||
*/
|
||||
void layer_tracking_suspend(VKCommandBufferInterface &command_buffer);
|
||||
|
||||
/**
|
||||
* Resume suspended layer tracking.
|
||||
*
|
||||
* Resume suspended layer tracking. This transits all registered layers back to its modified
|
||||
* state.
|
||||
*/
|
||||
void layer_tracking_resume(VKCommandBufferInterface &command_buffer);
|
||||
|
||||
bool node_has_input_attachments(const VKRenderGraph &render_graph, NodeHandle node);
|
||||
|
||||
std::string to_string_barrier(const Barrier &barrier);
|
||||
};
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,117 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "vk_render_graph.hh"
|
||||
#include "gpu_backend.hh"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Render Graph
|
||||
* \{ */
|
||||
|
||||
VKRenderGraph::VKRenderGraph(VKResourceStateTracker &resources) : resources_(resources) {}
|
||||
|
||||
void VKRenderGraph::reset()
|
||||
{
|
||||
#if 0
|
||||
memstats();
|
||||
#endif
|
||||
|
||||
links_.clear();
|
||||
for (VKRenderGraphNode &node : nodes_) {
|
||||
node.free_data();
|
||||
}
|
||||
nodes_.clear();
|
||||
storage_.reset();
|
||||
|
||||
debug_.node_group_map.clear();
|
||||
debug_.used_groups.clear();
|
||||
debug_.group_stack.clear();
|
||||
debug_.groups.clear();
|
||||
}
|
||||
|
||||
void VKRenderGraph::memstats() const
|
||||
{
|
||||
std::cout << __func__ << " nodes: (" << nodes_.size() << "/" << nodes_.capacity() << "), "
|
||||
<< "links(buffer): (" << links_.buffers.size() << "/" << links_.buffers.capacity()
|
||||
<< "), "
|
||||
<< "links(images): (" << links_.images.size() << "/" << links_.images.capacity()
|
||||
<< ")\n";
|
||||
#define PRINT_STORAGE(name) \
|
||||
std::cout << " " #name " : (" << storage_.name.size() << " / " << storage_.name.capacity() \
|
||||
<< ")\n "
|
||||
|
||||
PRINT_STORAGE(begin_rendering);
|
||||
PRINT_STORAGE(clear_attachments);
|
||||
PRINT_STORAGE(blit_image);
|
||||
PRINT_STORAGE(copy_buffer_to_image);
|
||||
PRINT_STORAGE(copy_image);
|
||||
PRINT_STORAGE(copy_image_to_buffer);
|
||||
PRINT_STORAGE(draw);
|
||||
PRINT_STORAGE(draw_indexed);
|
||||
PRINT_STORAGE(draw_indexed_indirect);
|
||||
PRINT_STORAGE(draw_indirect);
|
||||
#undef PRINT_STORAGE
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Debug
|
||||
* \{ */
|
||||
|
||||
void VKRenderGraph::debug_group_begin(const char *name, const ColorTheme4f &color)
|
||||
{
|
||||
ColorTheme4f useColor = color;
|
||||
if ((color == gpu::debug::GPU_DEBUG_GROUP_COLOR_DEFAULT) && (debug_.group_stack.size() > 0)) {
|
||||
useColor = debug_.groups[debug_.group_stack.last()].color;
|
||||
}
|
||||
DebugGroupNameID name_id = debug_.groups.index_of_or_add({std::string(name), useColor});
|
||||
debug_.group_stack.append(name_id);
|
||||
debug_.group_used = false;
|
||||
}
|
||||
|
||||
void VKRenderGraph::debug_group_end()
|
||||
{
|
||||
debug_.group_stack.pop_last();
|
||||
debug_.group_used = false;
|
||||
}
|
||||
|
||||
void VKRenderGraph::debug_print(NodeHandle node_handle) const
|
||||
{
|
||||
std::ostream &os = std::cout;
|
||||
os << "NODE:\n";
|
||||
const VKRenderGraphNode &node = nodes_[node_handle];
|
||||
os << " type:" << node.type << "\n";
|
||||
}
|
||||
|
||||
std::string VKRenderGraph::full_debug_group(NodeHandle node_handle) const
|
||||
{
|
||||
if ((G.debug & G_DEBUG_GPU) == 0) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
DebugGroupID debug_group = debug_.node_group_map[node_handle];
|
||||
if (debug_group == -1) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
for (const VKRenderGraph::DebugGroupNameID &name_id : debug_.used_groups[debug_group]) {
|
||||
ss << "/" << debug_.groups[name_id].name;
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,298 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* The render graph primarily is a a graph of GPU commands that are then serialized into command
|
||||
* buffers. The submission order can be altered and barriers are added for resource sync.
|
||||
*
|
||||
* # Building render graph
|
||||
*
|
||||
* The graph contains nodes that refers to resources it reads from, or modifies.
|
||||
* The resources that are read from are linked to the node inputs. The resources that are written
|
||||
* to are linked to the node outputs.
|
||||
*
|
||||
* Resources needs to be tracked as usage can alter the content of the resource. For example an
|
||||
* image can be optimized for data transfer, or optimized for sampling which can use a different
|
||||
* pixel layout on the device.
|
||||
*
|
||||
* When adding a node to the render graph the input and output links are extracted from the
|
||||
* See `VKNodeInfo::build_links`.
|
||||
*
|
||||
* # Executing render graph
|
||||
*
|
||||
* Executing a render graph is done by calling `submit_for_read` or `submit_for_present`. When
|
||||
* called the nodes that are needed to render the resource are determined by a `VKScheduler`. The
|
||||
* nodes are converted to `vkCmd*` and recorded in the command buffer by `VKCommandBuilder`.
|
||||
*
|
||||
* # Thread safety
|
||||
*
|
||||
* When the render graph is called the device will be locked. Nodes inside the render graph relies
|
||||
* on the resources which are device specific. The locked time is tiny when adding new nodes.
|
||||
* During execution this takes a longer time, but the lock can be released when the commands have
|
||||
* been queued. So other threads can continue.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "BKE_global.hh"
|
||||
|
||||
#include "BLI_color_types.hh"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_utility_mixins.hh"
|
||||
#include "BLI_vector.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
|
||||
#include "vk_common.hh"
|
||||
|
||||
#include "vk_command_buffer_wrapper.hh"
|
||||
#include "vk_command_builder.hh"
|
||||
#include "vk_render_graph_links.hh"
|
||||
#include "vk_resource_state_tracker.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
class VKScheduler;
|
||||
|
||||
class VKRenderGraph : public NonCopyable {
|
||||
friend class VKCommandBuilder;
|
||||
friend class VKScheduler;
|
||||
using DebugGroupNameID = int64_t;
|
||||
using DebugGroupID = int64_t;
|
||||
|
||||
/** All nodes inside the graph indexable via NodeHandle. */
|
||||
Vector<VKRenderGraphNode, 1024> nodes_;
|
||||
/**
|
||||
* Node read/write links to buffer and image resources.
|
||||
*/
|
||||
VKRenderGraphLinks links_;
|
||||
/** Storage for large node datas to improve CPU cache pre-loading. */
|
||||
VKRenderGraphStorage storage_;
|
||||
|
||||
/**
|
||||
* Not owning pointer to device resources.
|
||||
*
|
||||
* To improve testability the render graph doesn't access VKDevice or VKBackend directly.
|
||||
* resources_ can be replaced by a local variable. This way test cases don't need to create a
|
||||
* fully working context in order to test something render graph specific. Is marked optional as
|
||||
* device could
|
||||
*/
|
||||
VKResourceStateTracker &resources_;
|
||||
|
||||
struct DebugGroup {
|
||||
std::string name;
|
||||
ColorTheme4f color;
|
||||
|
||||
friend bool operator==(const DebugGroup &a, const DebugGroup &b) = default;
|
||||
|
||||
uint64_t hash() const
|
||||
{
|
||||
return get_default_hash<std::string, ColorTheme4f>(name, color);
|
||||
}
|
||||
};
|
||||
|
||||
struct {
|
||||
VectorSet<DebugGroup> groups;
|
||||
|
||||
/** Current stack of debug group names. */
|
||||
Vector<DebugGroupNameID> group_stack;
|
||||
|
||||
/**
|
||||
* Has a node been added to the current stack? If not the group stack will be added to
|
||||
* used_groups.
|
||||
*/
|
||||
bool group_used = false;
|
||||
|
||||
/** All used debug groups. */
|
||||
Vector<Vector<DebugGroupNameID>> used_groups;
|
||||
|
||||
/**
|
||||
* Map of a node_handle to an index of debug group in used_groups.
|
||||
*
|
||||
* \code{.cc}
|
||||
* int used_group_id = node_group_map[node_handle];
|
||||
* const Vector<DebugGroupNameID> &used_group = used_groups[used_group_id];
|
||||
* \endcode
|
||||
*/
|
||||
Vector<DebugGroupID> node_group_map;
|
||||
} debug_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Construct a new render graph instance.
|
||||
*
|
||||
* To improve testability the command buffer and resources they work on are provided as a
|
||||
* parameter.
|
||||
*/
|
||||
VKRenderGraph(VKResourceStateTracker &resources);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Add a node to the render graph.
|
||||
*/
|
||||
template<typename NodeInfo> NodeHandle add_node(const typename NodeInfo::CreateInfo &create_info)
|
||||
{
|
||||
std::scoped_lock lock(resources_.mutex);
|
||||
static VKRenderGraphNode node_template = {};
|
||||
NodeHandle node_handle = nodes_.append_and_get_index(node_template);
|
||||
#if 0
|
||||
/* Useful during debugging. When a validation error occurs during submission we know the node
|
||||
* type and node handle, but we don't know when and by who that specific node was added to the
|
||||
* render graph. By enabling this part of the code and set the correct node_handle and node
|
||||
* type a debugger can break at the moment the node has been added to the render graph. */
|
||||
if (node_handle == 267 && NodeInfo::node_type == VKNodeType::DRAW) {
|
||||
std::cout << "break\n";
|
||||
}
|
||||
#endif
|
||||
VKRenderGraphNode &node = nodes_[node_handle];
|
||||
node.set_node_data<NodeInfo>(storage_, create_info);
|
||||
node.build_links<NodeInfo>(resources_, links_, create_info);
|
||||
|
||||
if (G.debug & G_DEBUG_GPU) {
|
||||
if (!debug_.group_used) {
|
||||
debug_.group_used = true;
|
||||
debug_.used_groups.append(debug_.group_stack);
|
||||
}
|
||||
if (nodes_.size() > debug_.node_group_map.size()) {
|
||||
debug_.node_group_map.resize(nodes_.size());
|
||||
}
|
||||
debug_.node_group_map[node_handle] = debug_.used_groups.size() - 1;
|
||||
}
|
||||
return node_handle;
|
||||
}
|
||||
|
||||
public:
|
||||
#define ADD_NODE(NODE_CLASS) \
|
||||
NodeHandle add_node(const NODE_CLASS::CreateInfo &create_info) \
|
||||
{ \
|
||||
return add_node<NODE_CLASS>(create_info); \
|
||||
}
|
||||
ADD_NODE(VKBeginQueryNode)
|
||||
ADD_NODE(VKBeginRenderingNode)
|
||||
ADD_NODE(VKEndQueryNode)
|
||||
ADD_NODE(VKEndRenderingNode)
|
||||
ADD_NODE(VKClearAttachmentsNode)
|
||||
ADD_NODE(VKClearColorImageNode)
|
||||
ADD_NODE(VKClearDepthStencilImageNode)
|
||||
ADD_NODE(VKFillBufferNode)
|
||||
ADD_NODE(VKCopyBufferNode)
|
||||
ADD_NODE(VKCopyBufferToImageNode)
|
||||
ADD_NODE(VKCopyImageNode)
|
||||
ADD_NODE(VKCopyImageToBufferNode)
|
||||
ADD_NODE(VKBlitImageNode)
|
||||
ADD_NODE(VKDispatchNode)
|
||||
ADD_NODE(VKDispatchIndirectNode)
|
||||
ADD_NODE(VKDrawNode)
|
||||
ADD_NODE(VKDrawIndexedNode)
|
||||
ADD_NODE(VKDrawIndexedIndirectNode)
|
||||
ADD_NODE(VKDrawIndirectNode)
|
||||
ADD_NODE(VKResetQueryPoolNode)
|
||||
ADD_NODE(VKUpdateBufferNode)
|
||||
ADD_NODE(VKUpdateMipmapsNode)
|
||||
ADD_NODE(VKSynchronizationNode)
|
||||
#undef ADD_NODE
|
||||
|
||||
/**
|
||||
* Get the reference to the node data for a VKCopyBufferNode.
|
||||
*
|
||||
* Allows altering a previous added node. Is useful to reduce barriers when a streaming buffer
|
||||
* requires data that can still fit in the previous copy command.
|
||||
*/
|
||||
VKCopyBufferNode::Data &get_node_data(NodeHandle node_handle)
|
||||
{
|
||||
VKRenderGraphNode &node = nodes_[node_handle];
|
||||
BLI_assert(node.type == VKNodeType::COPY_BUFFER);
|
||||
return node.copy_buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* To reduce small allocations the caller can copy push constants inside the render graph.
|
||||
* The returned index range can than be used by the command builder to retrieve the push
|
||||
* constants.
|
||||
*/
|
||||
IndexRange copy_push_constants(Span<uint8_t> push_constants)
|
||||
{
|
||||
int64_t start = storage_.push_constants.size();
|
||||
storage_.push_constants.extend(push_constants);
|
||||
return IndexRange::from_begin_size(start, push_constants.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a new debugging group to the stack with the given name.
|
||||
*
|
||||
* New nodes added to the render graph will be associated with this debug group.
|
||||
*/
|
||||
void debug_group_begin(const char *name, const ColorTheme4f &color);
|
||||
|
||||
/**
|
||||
* Pop the top of the debugging group stack.
|
||||
*
|
||||
* New nodes added to the render graph will be associated with the parent of the current debug
|
||||
* group.
|
||||
*/
|
||||
void debug_group_end();
|
||||
|
||||
/**
|
||||
* Return the full debug group of the given node_handle. Returns an empty string when debug
|
||||
* groups are not enabled (`--debug-gpu`).
|
||||
*/
|
||||
std::string full_debug_group(NodeHandle node_handle) const;
|
||||
|
||||
/**
|
||||
* Utility function that is used during debugging.
|
||||
*
|
||||
* When debugging most of the time know the node_handle that is needed after the node has been
|
||||
* constructed. When haunting a bug it is more useful to query what the next node handle will be
|
||||
* so you can step through the node building process.
|
||||
*/
|
||||
NodeHandle next_node_handle()
|
||||
{
|
||||
return nodes_.size();
|
||||
}
|
||||
|
||||
bool is_empty()
|
||||
{
|
||||
return nodes_.is_empty();
|
||||
}
|
||||
|
||||
void debug_print(NodeHandle node_handle) const;
|
||||
|
||||
/**
|
||||
* Reset the render graph.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
void memstats() const;
|
||||
|
||||
/** Get the images that are linked by the given node. */
|
||||
inline Span<VKRenderGraphImage> linked_images(const VKRenderGraphNode &node) const
|
||||
{
|
||||
return links_.images.as_span().slice(node.links.images);
|
||||
}
|
||||
/** Get the images that are linked by the given node_handle. */
|
||||
inline Span<VKRenderGraphImage> linked_images(const NodeHandle node_handle) const
|
||||
{
|
||||
return linked_images(nodes_[node_handle]);
|
||||
}
|
||||
/** Get the buffers that are linked by the given node. */
|
||||
inline Span<VKRenderGraphBuffer> linked_buffers(const VKRenderGraphNode &node) const
|
||||
{
|
||||
return links_.buffers.as_span().slice(node.links.buffers);
|
||||
}
|
||||
/** Get the buffers that are linked by the given node_handle. */
|
||||
inline Span<VKRenderGraphBuffer> linked_buffers(const NodeHandle node_handle) const
|
||||
{
|
||||
return linked_buffers(nodes_[node_handle]);
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,16 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "vk_render_graph_links.hh"
|
||||
#include "vk_to_string.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,99 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Nodes inside the render graph are connected via links to the resources they use. These links are
|
||||
* determined when adding a node to the render graph.
|
||||
*
|
||||
* The inputs of the node link to the resources that the node reads from. The outputs of the node
|
||||
* link to the resources that the node modifies.
|
||||
*
|
||||
* All links inside the graph are stored inside `VKResourceDependencies`.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_utility_mixins.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "vk_common.hh"
|
||||
#include "vk_resource_state_tracker.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
/** Which access flags are considered for write access. */
|
||||
static constexpr VkAccessFlags VK_ACCESS_WRITE_MASK =
|
||||
VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT |
|
||||
VK_ACCESS_HOST_WRITE_BIT;
|
||||
|
||||
struct VKRenderGraphResource {
|
||||
/**
|
||||
* Which resource is being accessed.
|
||||
*/
|
||||
ResourceWithStamp resource;
|
||||
|
||||
/**
|
||||
* How is the resource being accessed.
|
||||
*
|
||||
* When generating pipeline barriers of a resource, the nodes access flags are evaluated to
|
||||
* create src/dst access masks.
|
||||
*/
|
||||
VkAccessFlags vk_access_flags;
|
||||
|
||||
bool has_write_access() const
|
||||
{
|
||||
return vk_access_flags & VK_ACCESS_WRITE_MASK;
|
||||
}
|
||||
};
|
||||
|
||||
struct VKRenderGraphBuffer : public VKRenderGraphResource {};
|
||||
|
||||
struct VKRenderGraphImage : public VKRenderGraphResource {
|
||||
/**
|
||||
* When resource is an image, which layout should the image be using.
|
||||
*
|
||||
* When generating the commands this attribute is compared with the actual image layout of the
|
||||
* the image. Additional pipeline barriers will be added to transit to the layout stored here.
|
||||
*/
|
||||
VkImageLayout vk_image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
/**
|
||||
* Which aspect of the image is being used.
|
||||
*/
|
||||
VkImageAspectFlags vk_image_aspect = VK_IMAGE_ASPECT_NONE;
|
||||
|
||||
/**
|
||||
* The layers and mipmap levels to bind.
|
||||
*
|
||||
* Used when layer_tracking will be enabled to transit the layout of these layers only.
|
||||
*/
|
||||
VKSubImageRange subimage;
|
||||
};
|
||||
|
||||
struct VKRenderGraphLinks {
|
||||
/**
|
||||
* VKRenderGraphNode.links.buffers contains indices to #VKRenderGraphLinks.buffers.
|
||||
*
|
||||
* This reduces reallocations as otherwise complex data types are needed.
|
||||
*/
|
||||
Vector<VKRenderGraphBuffer> buffers;
|
||||
|
||||
/**
|
||||
* VKRenderGraphNode.links.images only contains indices to the #VKRenderGraphLinks.images.
|
||||
*
|
||||
* This reduces reallocations as otherwise complex data types are needed.
|
||||
*/
|
||||
Vector<VKRenderGraphImage> images;
|
||||
|
||||
void clear()
|
||||
{
|
||||
buffers.clear();
|
||||
images.clear();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,341 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "nodes/vk_begin_query_node.hh"
|
||||
#include "nodes/vk_begin_rendering_node.hh"
|
||||
#include "nodes/vk_blit_image_node.hh"
|
||||
#include "nodes/vk_clear_attachments_node.hh"
|
||||
#include "nodes/vk_clear_color_image_node.hh"
|
||||
#include "nodes/vk_clear_depth_stencil_image_node.hh"
|
||||
#include "nodes/vk_copy_buffer_node.hh"
|
||||
#include "nodes/vk_copy_buffer_to_image_node.hh"
|
||||
#include "nodes/vk_copy_image_node.hh"
|
||||
#include "nodes/vk_copy_image_to_buffer_node.hh"
|
||||
#include "nodes/vk_dispatch_indirect_node.hh"
|
||||
#include "nodes/vk_dispatch_node.hh"
|
||||
#include "nodes/vk_draw_indexed_indirect_node.hh"
|
||||
#include "nodes/vk_draw_indexed_node.hh"
|
||||
#include "nodes/vk_draw_indirect_node.hh"
|
||||
#include "nodes/vk_draw_node.hh"
|
||||
#include "nodes/vk_end_query_node.hh"
|
||||
#include "nodes/vk_end_rendering_node.hh"
|
||||
#include "nodes/vk_fill_buffer_node.hh"
|
||||
#include "nodes/vk_reset_query_pool_node.hh"
|
||||
#include "nodes/vk_synchronization_node.hh"
|
||||
#include "nodes/vk_update_buffer_node.hh"
|
||||
#include "nodes/vk_update_mipmaps_node.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
/**
|
||||
* Index of a node inside the render graph.
|
||||
*/
|
||||
using NodeHandle = uint64_t;
|
||||
|
||||
/**
|
||||
* Node storage for nodes that uses large data structs.
|
||||
*
|
||||
* Some node structs are to large to store them as part of the node. The data are stored as a
|
||||
* vector of structs. Typically structs that occupy more than one cache line (64 bytes) should be
|
||||
* considered to be moved here.
|
||||
*
|
||||
*/
|
||||
struct VKRenderGraphStorage {
|
||||
Vector<VKBeginRenderingNode::Data, 1024> begin_rendering;
|
||||
Vector<VKClearAttachmentsNode::Data, 1024> clear_attachments;
|
||||
Vector<VKBlitImageNode::Data, 1024> blit_image;
|
||||
Vector<VKCopyBufferToImageNode::Data, 1024> copy_buffer_to_image;
|
||||
Vector<VKCopyImageNode::Data, 1024> copy_image;
|
||||
Vector<VKCopyImageToBufferNode::Data, 1024> copy_image_to_buffer;
|
||||
Vector<VKDrawNode::Data, 1024> draw;
|
||||
Vector<VKDrawIndexedNode::Data, 1024> draw_indexed;
|
||||
Vector<VKDrawIndexedIndirectNode::Data, 1024> draw_indexed_indirect;
|
||||
Vector<VKDrawIndirectNode::Data, 1024> draw_indirect;
|
||||
Vector<uint8_t> push_constants;
|
||||
|
||||
void reset()
|
||||
{
|
||||
begin_rendering.clear_and_shrink();
|
||||
clear_attachments.clear_and_shrink();
|
||||
blit_image.clear_and_shrink();
|
||||
copy_buffer_to_image.clear_and_shrink();
|
||||
copy_image.clear_and_shrink();
|
||||
copy_image_to_buffer.clear_and_shrink();
|
||||
draw.clear_and_shrink();
|
||||
draw_indexed.clear_and_shrink();
|
||||
draw_indexed_indirect.clear_and_shrink();
|
||||
draw_indirect.clear_and_shrink();
|
||||
push_constants.clear();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Node stored inside a render graph.
|
||||
*
|
||||
* Node specific data in the render graph are stored in a vector to ensure that the data can be
|
||||
* prefetched and removing a level of indirection. A consequence is that we cannot use class based
|
||||
* nodes.
|
||||
*/
|
||||
struct VKRenderGraphNode {
|
||||
VKNodeType type;
|
||||
union {
|
||||
VKBeginQueryNode::Data begin_query;
|
||||
VKClearColorImageNode::Data clear_color_image;
|
||||
VKClearDepthStencilImageNode::Data clear_depth_stencil_image;
|
||||
VKCopyBufferNode::Data copy_buffer;
|
||||
VKDispatchNode::Data dispatch;
|
||||
VKDispatchIndirectNode::Data dispatch_indirect;
|
||||
VKEndQueryNode::Data end_query;
|
||||
VKEndRenderingNode::Data end_rendering;
|
||||
VKFillBufferNode::Data fill_buffer;
|
||||
VKResetQueryPoolNode::Data reset_query_pool;
|
||||
VKSynchronizationNode::Data synchronization;
|
||||
VKUpdateBufferNode::Data update_buffer;
|
||||
VKUpdateMipmapsNode::Data update_mipmaps;
|
||||
int64_t storage_index = -1;
|
||||
};
|
||||
|
||||
struct {
|
||||
/** Range where the input/output buffers are stored inside #VKRenderGraph.buffer_links_. */
|
||||
IndexRange buffers;
|
||||
/** Range where the input/output images are stored inside #VKRenderGraph.image_links_. */
|
||||
IndexRange images;
|
||||
} links;
|
||||
|
||||
/**
|
||||
* Set the data of the node.
|
||||
*
|
||||
* Pre-conditions:
|
||||
* - type of the node should be `VKNodeType::UNUSED`. Memory allocated for nodes are reused
|
||||
* between consecutive use. Checking for unused node types will ensure that previous usage has
|
||||
* been reset. Resetting is done as part of `free_data`
|
||||
*/
|
||||
template<typename NodeInfo>
|
||||
void set_node_data(VKRenderGraphStorage &storage,
|
||||
const typename NodeInfo::CreateInfo &create_info)
|
||||
{
|
||||
BLI_assert(type == VKNodeType::UNUSED);
|
||||
/* Instance of NodeInfo is needed to call virtual methods. CPP doesn't support overloading of
|
||||
* static methods. */
|
||||
NodeInfo node_info;
|
||||
type = NodeInfo::node_type;
|
||||
node_info.set_node_data(*this, storage, create_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the input/output links for this.
|
||||
*
|
||||
* Newly created links are added to the `links` parameter.
|
||||
*/
|
||||
template<typename NodeInfo>
|
||||
void build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links,
|
||||
const typename NodeInfo::CreateInfo &create_info)
|
||||
{
|
||||
/* Instance of NodeInfo is needed to call virtual methods. CPP doesn't support overloading of
|
||||
* static methods. */
|
||||
NodeInfo node_info;
|
||||
int64_t buffer_index_start = links.buffers.size();
|
||||
int64_t image_index_start = links.images.size();
|
||||
node_info.build_links(resources, links, create_info);
|
||||
this->links.buffers = IndexRange::from_begin_end(buffer_index_start, links.buffers.size());
|
||||
this->links.images = IndexRange::from_begin_end(image_index_start, links.images.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pipeline stage of the node.
|
||||
*
|
||||
* Pipeline stage is used to update `src/dst_stage_masks` of the VKCommandBuilder.
|
||||
*/
|
||||
VkPipelineStageFlags pipeline_stage_get() const
|
||||
{
|
||||
switch (type) {
|
||||
case VKNodeType::UNUSED:
|
||||
return VK_PIPELINE_STAGE_NONE;
|
||||
case VKNodeType::BEGIN_QUERY:
|
||||
return VKBeginQueryNode::pipeline_stage;
|
||||
case VKNodeType::BEGIN_RENDERING:
|
||||
return VKBeginRenderingNode::pipeline_stage;
|
||||
case VKNodeType::CLEAR_ATTACHMENTS:
|
||||
return VKClearAttachmentsNode::pipeline_stage;
|
||||
case VKNodeType::CLEAR_COLOR_IMAGE:
|
||||
return VKClearColorImageNode::pipeline_stage;
|
||||
case VKNodeType::CLEAR_DEPTH_STENCIL_IMAGE:
|
||||
return VKClearDepthStencilImageNode::pipeline_stage;
|
||||
case VKNodeType::END_QUERY:
|
||||
return VKEndQueryNode::pipeline_stage;
|
||||
case VKNodeType::END_RENDERING:
|
||||
return VKEndRenderingNode::pipeline_stage;
|
||||
case VKNodeType::FILL_BUFFER:
|
||||
return VKFillBufferNode::pipeline_stage;
|
||||
case VKNodeType::COPY_BUFFER:
|
||||
return VKCopyBufferNode::pipeline_stage;
|
||||
case VKNodeType::COPY_IMAGE:
|
||||
return VKCopyImageNode::pipeline_stage;
|
||||
case VKNodeType::COPY_IMAGE_TO_BUFFER:
|
||||
return VKCopyImageToBufferNode::pipeline_stage;
|
||||
case VKNodeType::COPY_BUFFER_TO_IMAGE:
|
||||
return VKCopyBufferToImageNode::pipeline_stage;
|
||||
case VKNodeType::BLIT_IMAGE:
|
||||
return VKBlitImageNode::pipeline_stage;
|
||||
case VKNodeType::DISPATCH:
|
||||
return VKDispatchNode::pipeline_stage;
|
||||
case VKNodeType::DISPATCH_INDIRECT:
|
||||
return VKDispatchIndirectNode::pipeline_stage;
|
||||
case VKNodeType::DRAW:
|
||||
return VKDrawNode::pipeline_stage;
|
||||
case VKNodeType::DRAW_INDEXED:
|
||||
return VKDrawIndexedNode::pipeline_stage;
|
||||
case VKNodeType::DRAW_INDEXED_INDIRECT:
|
||||
return VKDrawIndexedIndirectNode::pipeline_stage;
|
||||
case VKNodeType::DRAW_INDIRECT:
|
||||
return VKDrawIndirectNode::pipeline_stage;
|
||||
case VKNodeType::RESET_QUERY_POOL:
|
||||
return VKResetQueryPoolNode::pipeline_stage;
|
||||
case VKNodeType::SYNCHRONIZATION:
|
||||
return VKSynchronizationNode::pipeline_stage;
|
||||
case VKNodeType::UPDATE_BUFFER:
|
||||
return VKUpdateBufferNode::pipeline_stage;
|
||||
case VKNodeType::UPDATE_MIPMAPS:
|
||||
return VKUpdateMipmapsNode::pipeline_stage;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return VK_PIPELINE_STAGE_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build commands for this node and record them in the given command_buffer.
|
||||
*
|
||||
* NOTE: Pipeline barriers should already be added. See
|
||||
* `VKCommandBuilder::build_node` and `VKCommandBuilder::build_pipeline_barriers`.
|
||||
*/
|
||||
void build_commands(VKCommandBufferInterface &command_buffer,
|
||||
VKRenderGraphStorage &storage,
|
||||
VKBoundPipelines &r_bound_pipelines)
|
||||
{
|
||||
switch (type) {
|
||||
case VKNodeType::UNUSED: {
|
||||
break;
|
||||
}
|
||||
#define BUILD_COMMANDS_STORAGE(NODE_TYPE, NODE_CLASS, ATTRIBUTE_NAME) \
|
||||
case NODE_TYPE: { \
|
||||
NODE_CLASS node_info; \
|
||||
node_info.build_commands(command_buffer, \
|
||||
storage.ATTRIBUTE_NAME[storage_index], \
|
||||
storage.push_constants, \
|
||||
r_bound_pipelines); \
|
||||
break; \
|
||||
}
|
||||
|
||||
#define BUILD_COMMANDS(NODE_TYPE, NODE_CLASS, ATTRIBUTE_NAME) \
|
||||
case NODE_TYPE: { \
|
||||
NODE_CLASS node_info; \
|
||||
node_info.build_commands( \
|
||||
command_buffer, ATTRIBUTE_NAME, storage.push_constants, r_bound_pipelines); \
|
||||
break; \
|
||||
}
|
||||
|
||||
BUILD_COMMANDS(VKNodeType::BEGIN_QUERY, VKBeginQueryNode, begin_query)
|
||||
BUILD_COMMANDS_STORAGE(VKNodeType::BEGIN_RENDERING, VKBeginRenderingNode, begin_rendering)
|
||||
BUILD_COMMANDS_STORAGE(
|
||||
VKNodeType::CLEAR_ATTACHMENTS, VKClearAttachmentsNode, clear_attachments)
|
||||
BUILD_COMMANDS(VKNodeType::CLEAR_COLOR_IMAGE, VKClearColorImageNode, clear_color_image)
|
||||
BUILD_COMMANDS(VKNodeType::CLEAR_DEPTH_STENCIL_IMAGE,
|
||||
VKClearDepthStencilImageNode,
|
||||
clear_depth_stencil_image)
|
||||
BUILD_COMMANDS(VKNodeType::END_QUERY, VKEndQueryNode, end_query)
|
||||
BUILD_COMMANDS(VKNodeType::END_RENDERING, VKEndRenderingNode, end_rendering)
|
||||
BUILD_COMMANDS(VKNodeType::FILL_BUFFER, VKFillBufferNode, fill_buffer)
|
||||
BUILD_COMMANDS(VKNodeType::UPDATE_BUFFER, VKUpdateBufferNode, update_buffer)
|
||||
BUILD_COMMANDS(VKNodeType::COPY_BUFFER, VKCopyBufferNode, copy_buffer)
|
||||
BUILD_COMMANDS_STORAGE(
|
||||
VKNodeType::COPY_BUFFER_TO_IMAGE, VKCopyBufferToImageNode, copy_buffer_to_image)
|
||||
BUILD_COMMANDS_STORAGE(VKNodeType::COPY_IMAGE, VKCopyImageNode, copy_image)
|
||||
BUILD_COMMANDS_STORAGE(
|
||||
VKNodeType::COPY_IMAGE_TO_BUFFER, VKCopyImageToBufferNode, copy_image_to_buffer)
|
||||
BUILD_COMMANDS_STORAGE(VKNodeType::BLIT_IMAGE, VKBlitImageNode, blit_image)
|
||||
BUILD_COMMANDS(VKNodeType::RESET_QUERY_POOL, VKResetQueryPoolNode, reset_query_pool)
|
||||
BUILD_COMMANDS(VKNodeType::SYNCHRONIZATION, VKSynchronizationNode, synchronization)
|
||||
BUILD_COMMANDS(VKNodeType::UPDATE_MIPMAPS, VKUpdateMipmapsNode, update_mipmaps)
|
||||
BUILD_COMMANDS(VKNodeType::DISPATCH, VKDispatchNode, dispatch)
|
||||
BUILD_COMMANDS(VKNodeType::DISPATCH_INDIRECT, VKDispatchIndirectNode, dispatch_indirect)
|
||||
BUILD_COMMANDS_STORAGE(VKNodeType::DRAW, VKDrawNode, draw)
|
||||
BUILD_COMMANDS_STORAGE(VKNodeType::DRAW_INDEXED, VKDrawIndexedNode, draw_indexed)
|
||||
BUILD_COMMANDS_STORAGE(
|
||||
VKNodeType::DRAW_INDEXED_INDIRECT, VKDrawIndexedIndirectNode, draw_indexed_indirect)
|
||||
BUILD_COMMANDS_STORAGE(VKNodeType::DRAW_INDIRECT, VKDrawIndirectNode, draw_indirect)
|
||||
#undef BUILD_COMMANDS
|
||||
#undef BUILD_COMMANDS_STORAGE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Free data kept by the node
|
||||
*/
|
||||
void free_data()
|
||||
{
|
||||
switch (type) {
|
||||
|
||||
#define FREE_DATA(NODE_TYPE, NODE_CLASS, ATTRIBUTE_NAME) \
|
||||
case NODE_TYPE: { \
|
||||
NODE_CLASS node_info; \
|
||||
node_info.free_data(ATTRIBUTE_NAME); \
|
||||
break; \
|
||||
}
|
||||
|
||||
FREE_DATA(VKNodeType::UPDATE_BUFFER, VKUpdateBufferNode, update_buffer)
|
||||
#undef FREE_DATA
|
||||
|
||||
case VKNodeType::UNUSED:
|
||||
case VKNodeType::BEGIN_QUERY:
|
||||
case VKNodeType::BEGIN_RENDERING:
|
||||
case VKNodeType::CLEAR_ATTACHMENTS:
|
||||
case VKNodeType::CLEAR_COLOR_IMAGE:
|
||||
case VKNodeType::CLEAR_DEPTH_STENCIL_IMAGE:
|
||||
case VKNodeType::END_QUERY:
|
||||
case VKNodeType::END_RENDERING:
|
||||
case VKNodeType::FILL_BUFFER:
|
||||
case VKNodeType::COPY_BUFFER:
|
||||
case VKNodeType::COPY_IMAGE:
|
||||
case VKNodeType::COPY_IMAGE_TO_BUFFER:
|
||||
case VKNodeType::COPY_BUFFER_TO_IMAGE:
|
||||
case VKNodeType::BLIT_IMAGE:
|
||||
case VKNodeType::RESET_QUERY_POOL:
|
||||
case VKNodeType::SYNCHRONIZATION:
|
||||
case VKNodeType::UPDATE_MIPMAPS:
|
||||
case VKNodeType::DISPATCH:
|
||||
case VKNodeType::DISPATCH_INDIRECT:
|
||||
case VKNodeType::DRAW:
|
||||
case VKNodeType::DRAW_INDEXED:
|
||||
case VKNodeType::DRAW_INDEXED_INDIRECT:
|
||||
case VKNodeType::DRAW_INDIRECT:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset nodes.
|
||||
*
|
||||
* Nodes are reset so they can be reused in consecutive calls. Data allocated by the node are
|
||||
* freed. This function dispatches the free_data to the actual node implementation.
|
||||
*/
|
||||
void reset()
|
||||
{
|
||||
free_data();
|
||||
type = VKNodeType::UNUSED;
|
||||
storage_index = -1;
|
||||
}
|
||||
};
|
||||
|
||||
BLI_STATIC_ASSERT(sizeof(VKRenderGraphNode) <= 96,
|
||||
"VKRenderGraphNode should be kept small. Consider moving data to the "
|
||||
"VKRenderGraphStorage class.");
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,86 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "vk_resource_access_info.hh"
|
||||
#include "vk_backend.hh"
|
||||
#include "vk_render_graph_links.hh"
|
||||
#include "vk_resource_state_tracker.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
VkImageLayout VKImageAccess::to_vk_image_layout(bool supports_local_read) const
|
||||
{
|
||||
if (vk_access_flags & (VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT)) {
|
||||
/* TODO: when read only use VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL */
|
||||
return VK_IMAGE_LAYOUT_GENERAL;
|
||||
}
|
||||
|
||||
if (supports_local_read && vk_access_flags & (VK_ACCESS_INPUT_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT))
|
||||
{
|
||||
return VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR;
|
||||
}
|
||||
if (vk_access_flags &
|
||||
(VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT))
|
||||
{
|
||||
return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
}
|
||||
|
||||
// TODO: Add ATTACHMENT_READ_ONLY_OPTIMAL
|
||||
if (vk_access_flags &
|
||||
(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT))
|
||||
{
|
||||
if (vk_image_aspect == VK_IMAGE_ASPECT_DEPTH_BIT) {
|
||||
return VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL;
|
||||
}
|
||||
if (vk_image_aspect == VK_IMAGE_ASPECT_STENCIL_BIT) {
|
||||
return VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
}
|
||||
BLI_assert(vk_image_aspect == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));
|
||||
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
}
|
||||
|
||||
void VKResourceAccessInfo::build_links(VKResourceStateTracker &resources,
|
||||
VKRenderGraphLinks &links) const
|
||||
{
|
||||
for (const VKBufferAccess &buffer_access : buffers) {
|
||||
const bool writes_to_resource = bool(buffer_access.vk_access_flags & VK_ACCESS_WRITE_MASK);
|
||||
ResourceWithStamp versioned_resource = writes_to_resource ?
|
||||
resources.get_buffer_and_increase_stamp(
|
||||
buffer_access.vk_buffer) :
|
||||
resources.get_buffer(buffer_access.vk_buffer);
|
||||
links.buffers.append({versioned_resource, buffer_access.vk_access_flags});
|
||||
}
|
||||
|
||||
const bool supports_local_read = resources.use_dynamic_rendering_local_read;
|
||||
|
||||
for (const VKImageAccess &image_access : images) {
|
||||
VkImageLayout image_layout = image_access.to_vk_image_layout(supports_local_read);
|
||||
const bool writes_to_resource = bool(image_access.vk_access_flags & VK_ACCESS_WRITE_MASK);
|
||||
ResourceWithStamp versioned_resource = writes_to_resource ?
|
||||
resources.get_image_and_increase_stamp(
|
||||
image_access.vk_image) :
|
||||
resources.get_image(image_access.vk_image);
|
||||
links.images.append({{versioned_resource, image_access.vk_access_flags},
|
||||
image_layout,
|
||||
image_access.vk_image_aspect,
|
||||
image_access.subimage});
|
||||
}
|
||||
}
|
||||
|
||||
void VKResourceAccessInfo::reset()
|
||||
{
|
||||
images.clear();
|
||||
buffers.clear();
|
||||
}
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,68 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Draw and dispatch commands are shader based and resources needs to be bound. The bound resources
|
||||
* are stored inside the state manager. Structures and functions inside this file improve code
|
||||
* re-usage when resources are part of the state manager.
|
||||
*
|
||||
* VKResourceAccessInfo: is a structure that can store the access information of a draw/dispatch
|
||||
* node. This information should be added to the create info of the render graph node
|
||||
* (`VKNodeInfo::CreateInfo`). When the links of the node is build,
|
||||
* `VKResourceAccessInfo.build_links` can be called to build the render graph links for these
|
||||
* resources.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_utility_mixins.hh"
|
||||
|
||||
#include "vk_common.hh"
|
||||
#include "vk_render_graph_links.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
class VKResourceStateTracker;
|
||||
|
||||
/** Struct describing the access to an image. */
|
||||
struct VKImageAccess {
|
||||
VkImage vk_image;
|
||||
VkAccessFlags vk_access_flags;
|
||||
VkImageAspectFlags vk_image_aspect;
|
||||
/* Used for sub-resource tracking within a rendering scope.
|
||||
*
|
||||
* By default all layers of images are tracked as a single resource. Only inside a render scope
|
||||
* we can temporary change a subset of layers, when the image is used as an attachment and a
|
||||
* image load/store.
|
||||
*/
|
||||
VKSubImageRange subimage;
|
||||
|
||||
/** Determine the image layout for the vk_access_flags. */
|
||||
VkImageLayout to_vk_image_layout(bool supports_local_read) const;
|
||||
};
|
||||
|
||||
/** Struct describing the access to a buffer. */
|
||||
struct VKBufferAccess {
|
||||
VkBuffer vk_buffer;
|
||||
VkAccessFlags vk_access_flags;
|
||||
};
|
||||
|
||||
/** Struct describing all resource accesses a draw/dispatch node has. */
|
||||
struct VKResourceAccessInfo : NonCopyable {
|
||||
Vector<VKBufferAccess> buffers;
|
||||
Vector<VKImageAccess> images;
|
||||
|
||||
/**
|
||||
* Extract read/write resource dependencies and add them to `node_links`.
|
||||
*/
|
||||
void build_links(VKResourceStateTracker &resources, VKRenderGraphLinks &links) const;
|
||||
|
||||
/**
|
||||
* Reset the instance for reuse.
|
||||
*/
|
||||
void reset();
|
||||
};
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,266 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "BKE_global.hh"
|
||||
|
||||
#include "BLI_index_range.hh"
|
||||
|
||||
#include "vk_resource_state_tracker.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Adding resources
|
||||
* \{ */
|
||||
ResourceHandle VKResourceStateTracker::create_resource_slot()
|
||||
{
|
||||
ResourceHandle handle;
|
||||
static Resource new_resource = {};
|
||||
if (unused_handles_.is_empty()) {
|
||||
handle = resources_.append_and_get_index(new_resource);
|
||||
}
|
||||
else {
|
||||
handle = unused_handles_.pop_last();
|
||||
resources_[handle] = new_resource;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void VKResourceStateTracker::add_image(VkImage vk_image,
|
||||
bool use_subresource_tracking,
|
||||
VKResourceBarrierState barrier_state,
|
||||
const char *name)
|
||||
{
|
||||
UNUSED_VARS_NDEBUG(name);
|
||||
std::scoped_lock lock(mutex);
|
||||
BLI_assert_msg(!image_resources_.contains(vk_image),
|
||||
"Image resource is added twice to the render graph.");
|
||||
ResourceHandle handle = create_resource_slot();
|
||||
Resource &resource = resources_[handle];
|
||||
image_resources_.add_new(vk_image, handle);
|
||||
|
||||
resource = {
|
||||
.type = VKResourceType::IMAGE,
|
||||
.image = {.vk_image = vk_image, .use_subresource_tracking = use_subresource_tracking},
|
||||
.stamp = 0,
|
||||
.barrier_state = barrier_state,
|
||||
};
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (name) {
|
||||
resource.name = name;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VK_RESOURCE_STATE_TRACKER_VALIDATION
|
||||
validate();
|
||||
#endif
|
||||
}
|
||||
|
||||
void VKResourceStateTracker::add_image(VkImage vk_image,
|
||||
bool use_subresource_tracking,
|
||||
const char *name)
|
||||
{
|
||||
add_image(vk_image, use_subresource_tracking, {}, name);
|
||||
}
|
||||
|
||||
void VKResourceStateTracker::add_aliased_image(VkImage vk_image,
|
||||
bool use_subresource_tracking,
|
||||
const char *name)
|
||||
{
|
||||
add_image(vk_image,
|
||||
use_subresource_tracking,
|
||||
{VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED},
|
||||
name);
|
||||
}
|
||||
|
||||
void VKResourceStateTracker::add_swapchain_image(VkImage vk_image, const char *name)
|
||||
{
|
||||
add_image(vk_image,
|
||||
false,
|
||||
{
|
||||
VK_ACCESS_NONE,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
},
|
||||
name);
|
||||
}
|
||||
|
||||
void VKResourceStateTracker::add_buffer(VkBuffer vk_buffer, const char *name)
|
||||
{
|
||||
UNUSED_VARS_NDEBUG(name);
|
||||
std::scoped_lock lock(mutex);
|
||||
BLI_assert_msg(!buffer_resources_.contains(vk_buffer),
|
||||
"Buffer resource is added twice to the render graph.");
|
||||
ResourceHandle handle = create_resource_slot();
|
||||
Resource &resource = resources_[handle];
|
||||
buffer_resources_.add_new(vk_buffer, handle);
|
||||
|
||||
resource = {
|
||||
.type = VKResourceType::BUFFER,
|
||||
.buffer = {.vk_buffer = vk_buffer},
|
||||
.stamp = 0,
|
||||
.barrier_state = {},
|
||||
};
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (name) {
|
||||
resource.name = name;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VK_RESOURCE_STATE_TRACKER_VALIDATION
|
||||
validate();
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Image layout
|
||||
* \{ */
|
||||
|
||||
void VKResourceStateTracker::update_image_layout(VkImage vk_image, VkImageLayout vk_image_layout)
|
||||
{
|
||||
std::scoped_lock lock(mutex);
|
||||
ResourceHandle handle = image_resources_.lookup(vk_image);
|
||||
Resource &resource = get_image_resource(handle);
|
||||
resource.barrier_state.image_layout = vk_image_layout;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Remove resources
|
||||
* \{ */
|
||||
|
||||
void VKResourceStateTracker::remove_buffer(VkBuffer vk_buffer)
|
||||
{
|
||||
std::scoped_lock lock(mutex);
|
||||
ResourceHandle handle = buffer_resources_.pop(vk_buffer);
|
||||
resources_[handle] = {
|
||||
.type = VKResourceType::NONE,
|
||||
.buffer = {.vk_buffer = VK_NULL_HANDLE},
|
||||
.stamp = 0,
|
||||
};
|
||||
unused_handles_.append(handle);
|
||||
|
||||
#ifdef VK_RESOURCE_STATE_TRACKER_VALIDATION
|
||||
validate();
|
||||
#endif
|
||||
}
|
||||
|
||||
void VKResourceStateTracker::remove_image(VkImage vk_image)
|
||||
{
|
||||
std::scoped_lock lock(mutex);
|
||||
ResourceHandle handle = image_resources_.pop(vk_image);
|
||||
resources_[handle] = {
|
||||
.type = VKResourceType::NONE,
|
||||
.image = {.vk_image = VK_NULL_HANDLE, .use_subresource_tracking = false},
|
||||
.stamp = 0,
|
||||
};
|
||||
unused_handles_.append(handle);
|
||||
|
||||
#ifdef VK_RESOURCE_STATE_TRACKER_VALIDATION
|
||||
validate();
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
ResourceWithStamp VKResourceStateTracker::get_stamp(ResourceHandle handle,
|
||||
const Resource &resource)
|
||||
{
|
||||
ResourceWithStamp result;
|
||||
result.handle = handle;
|
||||
result.stamp = resource.stamp;
|
||||
return result;
|
||||
}
|
||||
|
||||
ResourceWithStamp VKResourceStateTracker::get_and_increase_stamp(ResourceHandle handle,
|
||||
Resource &resource)
|
||||
{
|
||||
ResourceWithStamp result = get_stamp(handle, resource);
|
||||
resource.stamp += 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
ResourceWithStamp VKResourceStateTracker::get_image_and_increase_stamp(VkImage vk_image)
|
||||
{
|
||||
ResourceHandle handle = image_resources_.lookup(vk_image);
|
||||
Resource &resource = get_image_resource(handle);
|
||||
return get_and_increase_stamp(handle, resource);
|
||||
}
|
||||
|
||||
ResourceWithStamp VKResourceStateTracker::get_buffer_and_increase_stamp(VkBuffer vk_buffer)
|
||||
{
|
||||
ResourceHandle handle = buffer_resources_.lookup(vk_buffer);
|
||||
Resource &resource = get_buffer_resource(handle);
|
||||
return get_and_increase_stamp(handle, resource);
|
||||
}
|
||||
|
||||
ResourceWithStamp VKResourceStateTracker::get_buffer(VkBuffer vk_buffer) const
|
||||
{
|
||||
ResourceHandle handle = buffer_resources_.lookup(vk_buffer);
|
||||
const Resource &resource = get_buffer_resource(handle);
|
||||
return get_stamp(handle, resource);
|
||||
}
|
||||
|
||||
ResourceWithStamp VKResourceStateTracker::get_image(VkImage vk_image) const
|
||||
{
|
||||
ResourceHandle handle = image_resources_.lookup(vk_image);
|
||||
const Resource &resource = get_image_resource(handle);
|
||||
return get_stamp(handle, resource);
|
||||
}
|
||||
|
||||
#ifdef VK_RESOURCE_STATE_TRACKER_VALIDATION
|
||||
void VKResourceStateTracker::validate() const
|
||||
{
|
||||
for (const Map<VkImage, ResourceHandle>::Item &item : image_resources_.items()) {
|
||||
for (ResourceHandle buffer_handle : buffer_resources_.values()) {
|
||||
BLI_assert(item.value != buffer_handle);
|
||||
}
|
||||
const Resource &resource = get_image_resource(item.value);
|
||||
BLI_assert(resource.type == VKResourceType::IMAGE);
|
||||
}
|
||||
|
||||
for (const Map<VkBuffer, ResourceHandle>::Item &item : buffer_resources_.items()) {
|
||||
for (ResourceHandle image_handle : image_resources_.values()) {
|
||||
BLI_assert(item.value != image_handle);
|
||||
}
|
||||
const Resource &resource = get_buffer_resource(item.value);
|
||||
BLI_assert(resource.type == VKResourceType::BUFFER);
|
||||
}
|
||||
BLI_assert(
|
||||
image_resources_.size() ==
|
||||
std::ranges::count_if(resources_.begin(), resources_.end(), [](const Resource &resource) {
|
||||
return resource.type == VKResourceType::IMAGE;
|
||||
}));
|
||||
BLI_assert(
|
||||
buffer_resources_.size() ==
|
||||
std::ranges::count_if(resources_.begin(), resources_.end(), [](const Resource &resource) {
|
||||
return resource.type == VKResourceType::BUFFER;
|
||||
}));
|
||||
}
|
||||
#endif
|
||||
|
||||
void VKResourceStateTracker::debug_print() const
|
||||
{
|
||||
std::ostream &os = std::cout;
|
||||
os << "VKResourceStateTracker\n";
|
||||
os << " resources=(" << resources_.size() << "/" << resources_.capacity() << ")\n";
|
||||
os << " buffers=(" << buffer_resources_.size() << "/" << buffer_resources_.capacity() << ")\n";
|
||||
os << " images=(" << image_resources_.size() << "/" << image_resources_.capacity() << ")\n";
|
||||
os << " unused=(" << unused_handles_.size() << "/" << unused_handles_.capacity() << ")\n";
|
||||
}
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,334 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* The state of resources needs to be tracked on device level.
|
||||
*
|
||||
* The state that are being tracked include:
|
||||
* - Modification stamps: Each time a resource is modified, this stamp is increased. Inside the
|
||||
* render graph nodes track the resources including this stamp.
|
||||
* - Image layouts: The layout of pixels of an image on the GPU depends on the command being
|
||||
* executed. A certain `vkCmd*` requires the image to be in a certain layout. Using incorrect
|
||||
* layouts could lead to rendering artifacts.
|
||||
* - Resource ownership: Resources that are externally managed (swap-chain or external) uses a
|
||||
* different workflow as its state can be altered externally and needs to be reset.
|
||||
* - Read/Write access masks: To generate correct and performing pipeline barriers the src/dst
|
||||
* access masks needs to be accurate and precise. When creating pipeline barriers the resource
|
||||
* usage up to that point should be known and the resource usage from that point on.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_enum_flags.hh"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_mutex.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "vk_common.hh"
|
||||
|
||||
/**
|
||||
* Enable VK_RESOURCE_STATE_TRACKER_VALIDATION to perform a consistency check
|
||||
* on the state. The consistency check is time consuming and should only be
|
||||
* turned on when needed.
|
||||
*/
|
||||
// #define VK_RESOURCE_STATE_TRACKER_VALIDATION
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
class VKCommandBuilder;
|
||||
struct VKRenderGraphLink;
|
||||
class VKScheduler;
|
||||
|
||||
using ResourceHandle = uint64_t;
|
||||
|
||||
/**
|
||||
* ModificationStamp is used to track resource modifications.
|
||||
*
|
||||
* When a resource is modified it will generate a new stamp by incrementing the previous stamp
|
||||
* with 1. Consecutive reads should use this new stamp. The stamp stays active until the next
|
||||
* modification to the resources is added to any render graph.
|
||||
*/
|
||||
using ModificationStamp = uint64_t;
|
||||
|
||||
/**
|
||||
* Resource with a stamp.
|
||||
*
|
||||
* This struct represents an image or buffer (handle) and its modification stamp.
|
||||
*/
|
||||
struct ResourceWithStamp {
|
||||
ResourceHandle handle;
|
||||
ModificationStamp stamp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enum containing the different resource types that are being tracked.
|
||||
*/
|
||||
enum class VKResourceType { NONE = (0 << 0), IMAGE = (1 << 0), BUFFER = (1 << 1) };
|
||||
ENUM_OPERATORS(VKResourceType);
|
||||
|
||||
/**
|
||||
* State being tracked for a resource.
|
||||
*/
|
||||
struct VKResourceBarrierState {
|
||||
/** Last used access flags. Will be reset by the last write. Reads will accumulate flags. */
|
||||
VkAccessFlags vk_access = VK_ACCESS_NONE;
|
||||
/* Last known pipeline stage. Will be reset by the last write. Reads will accumulate flags. */
|
||||
VkPipelineStageFlags vk_pipeline_stages = VK_PIPELINE_STAGE_NONE;
|
||||
/** Last known image layout of an image resource. */
|
||||
VkImageLayout image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
bool is_new_stamp() const
|
||||
{
|
||||
return bool(vk_access &
|
||||
(VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT |
|
||||
VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_MEMORY_WRITE_BIT));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Class to track resources.
|
||||
*
|
||||
* Resources are tracked on device level. Their are two kind of resources, namely buffers and
|
||||
* images. Each resource can have multiple versions; every time a resource is changed (written to)
|
||||
* a new version is tracked.
|
||||
*/
|
||||
class VKResourceStateTracker {
|
||||
/* When a command buffer is reset the resources are re-synced.
|
||||
* During the syncing the command builder attributes are resized to reduce reallocations. */
|
||||
friend class VKCommandBuilder;
|
||||
friend struct VKRenderGraphLink;
|
||||
friend class VKScheduler;
|
||||
|
||||
/**
|
||||
* A render resource can be a buffer or an image that needs to be tracked during rendering.
|
||||
*
|
||||
* Resources needs to be tracked as usage can alter the content of the resource. For example an
|
||||
* image can be optimized for data transfer, or optimized for sampling which can use a different
|
||||
* pixel layout on the device.
|
||||
*/
|
||||
struct Resource {
|
||||
/** Is this resource a buffer or an image. */
|
||||
VKResourceType type;
|
||||
union {
|
||||
struct {
|
||||
/** VkBuffer handle of the resource being tracked. */
|
||||
VkBuffer vk_buffer = VK_NULL_HANDLE;
|
||||
} buffer;
|
||||
|
||||
struct {
|
||||
/** VkImage handle of the resource being tracked. */
|
||||
VkImage vk_image = VK_NULL_HANDLE;
|
||||
/** Do we need to track subresources (layers/mipmaps). */
|
||||
bool use_subresource_tracking = false;
|
||||
} image;
|
||||
};
|
||||
|
||||
/** Current modification stamp of the resource. */
|
||||
ModificationStamp stamp = 0;
|
||||
|
||||
/**
|
||||
* State tracking to ensure correct pipeline barriers and command creation.
|
||||
*/
|
||||
VKResourceBarrierState barrier_state = {};
|
||||
|
||||
#ifndef NDEBUG
|
||||
std::string name;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Check if the given resource handle subresources needs to be tracked.
|
||||
*
|
||||
* Returns true when
|
||||
* - handle is an image with subresource tracking enables.
|
||||
*
|
||||
* Returns false when
|
||||
* - handle isn't an image resource or
|
||||
* - handle doesn't have subresource tracking enabled.
|
||||
*/
|
||||
bool use_subresource_tracking()
|
||||
{
|
||||
if (type == VKResourceType::BUFFER) {
|
||||
return false;
|
||||
}
|
||||
return image.use_subresource_tracking;
|
||||
}
|
||||
};
|
||||
|
||||
Vector<Resource> resources_;
|
||||
Vector<ResourceHandle> unused_handles_;
|
||||
Map<VkImage, ResourceHandle> image_resources_;
|
||||
Map<VkBuffer, ResourceHandle> buffer_resources_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Device resource mutex
|
||||
*
|
||||
* The mutex is stored in resources due to:
|
||||
* - It protects resources and their state.
|
||||
* - Allowing test cases to do testing without setting up a device instance which requires ghost.
|
||||
* - Device instance isn't accessible in test cases.
|
||||
*/
|
||||
Mutex mutex;
|
||||
|
||||
/**
|
||||
* Register a buffer resource.
|
||||
*
|
||||
* When a buffer is created in VKBuffer, it needs to be registered in the device resources so the
|
||||
* resource state can be tracked during its lifetime.
|
||||
*/
|
||||
void add_buffer(VkBuffer vk_buffer, const char *name = nullptr);
|
||||
|
||||
/**
|
||||
* Register an image resource.
|
||||
*
|
||||
* When an image is created in VKTexture, it needs to be registered in the device resources so
|
||||
* the resource state can be tracked during its lifetime.
|
||||
*/
|
||||
void add_image(VkImage vk_image, bool use_subresource_tracking, const char *name = nullptr);
|
||||
|
||||
/**
|
||||
* \brief Register an image resource that can have aliased memory.
|
||||
*
|
||||
* The aliased memory can still be in use and requires the image to wait for all commands to be
|
||||
* completed, before it can start writing to the aliased memory.
|
||||
*/
|
||||
void add_aliased_image(VkImage vk_image,
|
||||
bool use_subresource_tracking,
|
||||
const char *name = nullptr);
|
||||
void add_swapchain_image(VkImage vk_image, const char *name = nullptr);
|
||||
|
||||
/**
|
||||
* \brief Update the layout of an image that has been externally modified.
|
||||
*
|
||||
* 'vkTransitionImageLayout' changes the image layout. When used the image layout needs to be
|
||||
* updated to match the current layout, ensuring correct generation of pipeline barriers.
|
||||
*
|
||||
* \name vk_image: VkImage handle to update the image layout for.
|
||||
* \name vk_image_layout: The layout the resource state tracker should now be using, matching the
|
||||
* current layout of the image.
|
||||
*/
|
||||
void update_image_layout(VkImage vk_image, VkImageLayout vk_image_layout);
|
||||
|
||||
/**
|
||||
* Remove an registered image.
|
||||
*
|
||||
* When a image is destroyed by calling `vmaDestroyImage`, a call to `remove_image` is needed to
|
||||
* unregister the resource from state tracking.
|
||||
*/
|
||||
void remove_image(VkImage vk_image);
|
||||
|
||||
/**
|
||||
* Remove an registered buffer.
|
||||
*
|
||||
* When a buffer is destroyed by calling `vmaDestroyBuffer`, a call to `remove_buffer` is needed
|
||||
* to unregister the resource from state tracking.
|
||||
*/
|
||||
void remove_buffer(VkBuffer vk_buffer);
|
||||
|
||||
/**
|
||||
* Return the current stamp of the resource, and increase the stamp.
|
||||
*
|
||||
* When a node writes to an image, this method is called to increase the stamp of the image.
|
||||
* The node that writes to the image will use the current stamp as its input, but generate a new
|
||||
* stamp for future nodes.
|
||||
*
|
||||
* This function is called when adding a node to the render graph, during building resource
|
||||
* dependencies. See `VKNodeInfo.build_links`
|
||||
*/
|
||||
ResourceWithStamp get_image_and_increase_stamp(VkImage vk_image);
|
||||
|
||||
/**
|
||||
* Return the current stamp of the resource, and increase the stamp.
|
||||
*
|
||||
* When a node writes to a buffer, this method is called to increase the stamp of the buffer.
|
||||
* The node that writes to the buffer will use the current stamp as its input, but generate the
|
||||
* new stamp for future nodes.
|
||||
*
|
||||
* This function is called when adding a node to the render graph, during building resource
|
||||
* dependencies. See `VKNodeInfo.build_links`
|
||||
*/
|
||||
ResourceWithStamp get_buffer_and_increase_stamp(VkBuffer vk_buffer);
|
||||
|
||||
/**
|
||||
* Return the current stamp of the resource.
|
||||
*
|
||||
* When a node reads from a buffer, this method is called to get the current stamp the buffer.
|
||||
*
|
||||
* This function is called when adding a node to the render graph, during building resource
|
||||
* dependencies. See `VKNodeInfo.build_links`
|
||||
*/
|
||||
ResourceWithStamp get_buffer(VkBuffer vk_buffer) const;
|
||||
|
||||
/**
|
||||
* Return the current stamp of the resource.
|
||||
*
|
||||
* When a node reads from an image, this method is called to get the current stamp the image.
|
||||
*
|
||||
* This function is called when adding a node to the render graph, during building resource
|
||||
* dependencies. See `VKNodeInfo.build_links`
|
||||
*/
|
||||
ResourceWithStamp get_image(VkImage vk_image) const;
|
||||
|
||||
bool use_dynamic_rendering_local_read = true;
|
||||
|
||||
void debug_print() const;
|
||||
|
||||
private:
|
||||
void add_image(VkImage vk_image,
|
||||
bool use_subresource_tracking,
|
||||
VKResourceBarrierState barrier_state,
|
||||
const char *name = nullptr);
|
||||
|
||||
/**
|
||||
* Get the current stamp of the resource.
|
||||
*/
|
||||
static ResourceWithStamp get_stamp(ResourceHandle handle, const Resource &resource);
|
||||
|
||||
/**
|
||||
* Get the current stamp of the resource and increase the stamp.
|
||||
*/
|
||||
static ResourceWithStamp get_and_increase_stamp(ResourceHandle handle, Resource &resource);
|
||||
|
||||
ResourceHandle create_resource_slot();
|
||||
|
||||
/**
|
||||
* Get the ref to an image resource state.
|
||||
*
|
||||
* NOTE: Can only be called when the mutex has been locked in the same thread.
|
||||
*/
|
||||
inline Resource &get_image_resource(ResourceHandle resource_handle)
|
||||
{
|
||||
BLI_assert(resources_[resource_handle].type == VKResourceType::IMAGE);
|
||||
return resources_[resource_handle];
|
||||
}
|
||||
inline const Resource &get_image_resource(ResourceHandle resource_handle) const
|
||||
{
|
||||
BLI_assert(resources_[resource_handle].type == VKResourceType::IMAGE);
|
||||
return resources_[resource_handle];
|
||||
}
|
||||
/**
|
||||
* Get the ref to an image resource state.
|
||||
*
|
||||
* NOTE: Can only be called when the mutex has been locked in the same thread.
|
||||
*/
|
||||
inline Resource &get_buffer_resource(ResourceHandle resource_handle)
|
||||
{
|
||||
BLI_assert(resources_[resource_handle].type == VKResourceType::BUFFER);
|
||||
return resources_[resource_handle];
|
||||
}
|
||||
inline const Resource &get_buffer_resource(ResourceHandle resource_handle) const
|
||||
{
|
||||
BLI_assert(resources_[resource_handle].type == VKResourceType::BUFFER);
|
||||
return resources_[resource_handle];
|
||||
}
|
||||
|
||||
#ifdef VK_RESOURCE_STATE_TRACKER_VALIDATION
|
||||
void validate() const;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,243 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "vk_render_graph.hh"
|
||||
#include "vk_scheduler.hh"
|
||||
|
||||
#include "BLI_index_range.hh"
|
||||
#include "BLI_set.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
|
||||
Span<NodeHandle> VKScheduler::select_nodes(const VKRenderGraph &render_graph)
|
||||
{
|
||||
result_.clear();
|
||||
for (NodeHandle node_handle : render_graph.nodes_.index_range()) {
|
||||
result_.append(node_handle);
|
||||
}
|
||||
reorder_nodes(render_graph);
|
||||
return result_;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Reorder - move data transfer and dispatches outside rendering scope
|
||||
* \{ */
|
||||
|
||||
void VKScheduler::reorder_nodes(const VKRenderGraph &render_graph)
|
||||
{
|
||||
move_initial_transfer_to_start(render_graph);
|
||||
move_transfer_and_dispatch_outside_rendering_scope(render_graph);
|
||||
}
|
||||
|
||||
std::optional<std::pair<int64_t, int64_t>> VKScheduler::find_rendering_scope(
|
||||
const VKRenderGraph &render_graph, IndexRange search_range) const
|
||||
{
|
||||
int64_t rendering_start = -1;
|
||||
|
||||
for (int64_t index : search_range) {
|
||||
NodeHandle node_handle = result_[index];
|
||||
const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
|
||||
if (node.type == VKNodeType::BEGIN_RENDERING) {
|
||||
rendering_start = index;
|
||||
}
|
||||
if (node.type == VKNodeType::END_RENDERING && rendering_start != -1) {
|
||||
return std::pair(rendering_start, index);
|
||||
}
|
||||
}
|
||||
/* When using VK_EXT_dynamic_rendering_local_read, rendering scopes can be suspended and
|
||||
* resumed without explicit END_RENDERING (see VKFrameBuffer::subpass_transition_impl).
|
||||
* This can leave a BEGIN_RENDERING node without a matching END_RENDERING at the end of
|
||||
* the search range. Orphaned nodes don't need reordering.
|
||||
* When NOT using local read, an unmatched BEGIN_RENDERING is a bug. */
|
||||
BLI_assert(rendering_start == -1 || render_graph.resources_.use_dynamic_rendering_local_read);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Reorder - Move initial data transfers to the start
|
||||
* \{ */
|
||||
|
||||
void VKScheduler::move_initial_transfer_to_start(const VKRenderGraph &render_graph)
|
||||
{
|
||||
Vector<NodeHandle> data_transfers;
|
||||
Vector<NodeHandle> other_nodes;
|
||||
|
||||
data_transfers.reserve(result_.size());
|
||||
other_nodes.reserve(result_.size());
|
||||
|
||||
for (const int64_t index : result_.index_range()) {
|
||||
NodeHandle node_handle = result_[index];
|
||||
const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
|
||||
Span<VKRenderGraphBuffer> node_buffers = render_graph.linked_buffers(node);
|
||||
Span<VKRenderGraphImage> node_images = render_graph.linked_images(node);
|
||||
if (ELEM(node.type, VKNodeType::COPY_BUFFER)) {
|
||||
if (node_buffers[0].resource.stamp == 0 && node_buffers[1].resource.stamp == 0) {
|
||||
data_transfers.append(index);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (ELEM(node.type, VKNodeType::COPY_BUFFER_TO_IMAGE, VKNodeType::COPY_IMAGE_TO_BUFFER)) {
|
||||
if (node_buffers[0].resource.stamp == 0 && node_images[0].resource.stamp == 0) {
|
||||
data_transfers.append(index);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (ELEM(node.type, VKNodeType::FILL_BUFFER, VKNodeType::UPDATE_BUFFER)) {
|
||||
if (node_buffers[0].resource.stamp == 0) {
|
||||
data_transfers.append(index);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
other_nodes.append(index);
|
||||
}
|
||||
|
||||
MutableSpan<NodeHandle> store_data_transfers = result_.as_mutable_span().slice(
|
||||
0, data_transfers.size());
|
||||
MutableSpan<NodeHandle> store_other = result_.as_mutable_span().slice(data_transfers.size(),
|
||||
other_nodes.size());
|
||||
store_data_transfers.copy_from(data_transfers);
|
||||
store_other.copy_from(other_nodes);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Reorder - move data transfer and dispatches outside rendering scope
|
||||
* \{ */
|
||||
|
||||
void VKScheduler::move_transfer_and_dispatch_outside_rendering_scope(
|
||||
const VKRenderGraph &render_graph)
|
||||
{
|
||||
Vector<NodeHandle> pre_rendering_scope;
|
||||
Vector<NodeHandle> rendering_scope;
|
||||
Set<ResourceHandle> used_buffers;
|
||||
|
||||
foreach_rendering_scope(render_graph, [&](int64_t start_index, int64_t end_index) {
|
||||
/* Move end_rendering right after the last graphics node. */
|
||||
for (int index = end_index - 1; index >= start_index; index--) {
|
||||
NodeHandle node_handle = result_[index];
|
||||
const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
|
||||
if (node_type_is_rendering(node.type)) {
|
||||
break;
|
||||
}
|
||||
std::swap(result_[end_index], result_[index]);
|
||||
end_index -= 1;
|
||||
}
|
||||
|
||||
/* Move begin_rendering right before the first graphics node. */
|
||||
for (int index = start_index + 1; index < end_index; index++) {
|
||||
NodeHandle node_handle = result_[index];
|
||||
const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
|
||||
if (node_type_is_rendering(node.type)) {
|
||||
break;
|
||||
}
|
||||
std::swap(result_[start_index], result_[index]);
|
||||
start_index += 1;
|
||||
}
|
||||
|
||||
/* Move buffer update buffer commands to before the rendering scope, unless the buffer is
|
||||
* already being used by a draw command. Images modification could also be moved outside the
|
||||
* rendering scope, but it is more tricky as they could also be attached to the frame-buffer.
|
||||
*/
|
||||
pre_rendering_scope.clear();
|
||||
rendering_scope.clear();
|
||||
used_buffers.clear();
|
||||
|
||||
for (int index = start_index + 1; index < end_index; index++) {
|
||||
NodeHandle node_handle = result_[index];
|
||||
const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
|
||||
/* Should we add this node to the rendering scope. This is only done when we need to reorder
|
||||
* nodes. In that case the rendering_scope has already an item and we should add this node to
|
||||
* or the rendering scope or before the rendering scope. Adding nodes before rendering scope
|
||||
* is done in the VKNodeType::UPDATE_BUFFER branch. */
|
||||
bool add_to_rendering_scope = !rendering_scope.is_empty();
|
||||
if (node.type == VKNodeType::UPDATE_BUFFER) {
|
||||
/* Checking the node links to reduce potential locking the resource mutex. */
|
||||
if (!used_buffers.contains(
|
||||
render_graph.links_.buffers[node.links.buffers.start()].resource.handle))
|
||||
{
|
||||
/* Buffer isn't used by this rendering scope so we can safely move it before the
|
||||
* rendering scope begins. */
|
||||
pre_rendering_scope.append(node_handle);
|
||||
add_to_rendering_scope = false;
|
||||
/* When this is the first time we move a node before the rendering we should start
|
||||
* building up the rendering scope as well. This is postponed so we can safe some cycles
|
||||
* when no nodes needs to be moved at all. */
|
||||
if (rendering_scope.is_empty()) {
|
||||
rendering_scope.extend(Span<NodeHandle>(&result_[start_index], index - start_index));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (add_to_rendering_scope) {
|
||||
/* When rendering scope has an item we are rewriting the execution order and need to track
|
||||
* what should be inside the rendering scope. */
|
||||
rendering_scope.append(node_handle);
|
||||
}
|
||||
|
||||
/* Any read/write to buffer resources should be added to used_buffers in order to detect if
|
||||
* it is safe to move a node before the rendering scope. */
|
||||
for (const VKRenderGraphBuffer &link : render_graph.linked_buffers(node)) {
|
||||
used_buffers.add(link.resource.handle);
|
||||
}
|
||||
}
|
||||
|
||||
/* When pre_rendering_scope has an item we want to rewrite the order.
|
||||
* The number of nodes are not changed, so we can do this inline. */
|
||||
if (!pre_rendering_scope.is_empty()) {
|
||||
MutableSpan<NodeHandle> store_none_rendering = result_.as_mutable_span().slice(
|
||||
start_index, pre_rendering_scope.size());
|
||||
MutableSpan<NodeHandle> store_rendering = result_.as_mutable_span().slice(
|
||||
start_index + pre_rendering_scope.size(), rendering_scope.size());
|
||||
store_none_rendering.copy_from(pre_rendering_scope);
|
||||
store_rendering.copy_from(rendering_scope);
|
||||
start_index += pre_rendering_scope.size();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Debug
|
||||
* \{ */
|
||||
|
||||
void VKScheduler::debug_print(const VKRenderGraph &render_graph) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
int indent = 0;
|
||||
|
||||
for (int index : result_.index_range()) {
|
||||
const NodeHandle node_handle = result_[index];
|
||||
const VKRenderGraphNode &node = render_graph.nodes_[node_handle];
|
||||
if (node.type == VKNodeType::END_RENDERING) {
|
||||
indent--;
|
||||
}
|
||||
for (int i = 0; i < indent; i++) {
|
||||
ss << " ";
|
||||
}
|
||||
ss << node.type << "\n";
|
||||
#if 0
|
||||
render_graph.debug_print(node_handle);
|
||||
#endif
|
||||
if (node.type == VKNodeType::BEGIN_RENDERING) {
|
||||
indent++;
|
||||
}
|
||||
}
|
||||
ss << "\n";
|
||||
|
||||
std::cout << ss.str();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,117 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* The scheduler is responsible to find and reorder the nodes in the render graph to update an
|
||||
* image or buffer to its latest content and state.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "vk_common.hh"
|
||||
#include "vk_render_graph_node.hh"
|
||||
|
||||
namespace blender::gpu::render_graph {
|
||||
class VKRenderGraph;
|
||||
|
||||
/**
|
||||
* VKScheduler is responsible for selecting and reordering of nodes in the render graph. This
|
||||
* selection and order is used to convert the nodes to commands and submitting it to the GPU.
|
||||
*
|
||||
* This scheduler selects all nodes in the order they were added to the render graph.
|
||||
*
|
||||
* This is an initial implementation and should be enhanced for:
|
||||
* - Moving data transfer and compute before drawing, when they are scheduled between drawing nodes
|
||||
* that use the same pipeline.
|
||||
* - Only select the nodes that are only needed for the given vk_image/vk_buffer. When performing
|
||||
* read-backs of buffers should be done with as least as possible nodes as they can block
|
||||
* drawing. It is better to do handle most nodes just before presenting the image. This would
|
||||
* lead to less CPU locks.
|
||||
* - Pruning branches that are not linked to anything. EEVEE can add debug commands that would
|
||||
* eventually not been displayed on screen. These branches should be pruned. The challenge is
|
||||
* that we need to know for certain that it isn't used in a not submitted part of the graph.
|
||||
*
|
||||
* TODO: Walking the render graph isn't implemented yet.
|
||||
* The idea is to have a `Map<ResourceWithStamp, Vector<NodeHandle>> consumers` and
|
||||
* `Map<ResourceWithStamp, NodeHandle> producers`.
|
||||
* These attributes can be stored in the render graph and created when building the
|
||||
* links, or can be created inside the VKScheduler as a variable. The exact detail which one would
|
||||
* be better is unclear as there aren't any users yet. At the moment the scheduler would need them
|
||||
* we need to figure out the best way to store and retrieve the consumers/producers.
|
||||
*/
|
||||
class VKScheduler {
|
||||
private:
|
||||
/**
|
||||
* Results of `select_nodes_for_image`, `select_nodes_for_buffer` are cached in this instance to
|
||||
* reduce memory operations.
|
||||
*/
|
||||
Vector<NodeHandle> result_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Determine which nodes of the render graph should be selected and in what order they should
|
||||
* be executed to update the given vk_image to its latest content and state.
|
||||
*
|
||||
* NOTE: Currently will select all nodes.
|
||||
* NOTE: Result becomes invalid by the next call to VKScheduler.
|
||||
*/
|
||||
[[nodiscard]] Span<NodeHandle> select_nodes(const VKRenderGraph &render_graph);
|
||||
|
||||
private:
|
||||
void reorder_nodes(const VKRenderGraph &render_graph);
|
||||
|
||||
/**
|
||||
* When a data transfer command writes to a resource which is initial it can be grouped at the
|
||||
* beginning of the render graph.
|
||||
*
|
||||
* This reduces context switches when executing commands on the GPU.
|
||||
*/
|
||||
void move_initial_transfer_to_start(const VKRenderGraph &render_graph);
|
||||
|
||||
/**
|
||||
* Any data transfer or dispatch nodes should be scheduled before or after a rendering scope.
|
||||
*
|
||||
* - Data transfer and dispatch nodes at the beginning are scheduled before
|
||||
* the rendering begin.
|
||||
* - Data transfer and dispatch nodes at the end are scheduled after the
|
||||
* rendering end.
|
||||
* - Data transfer and dispatch nodes in between draw commands will be pushed
|
||||
* to the beginning if they are not yet being used.
|
||||
* - When used the rendering will be suspended and the data transfer/dispatch nodes are
|
||||
* scheduled between the suspended rendering and when the suspended rendering is
|
||||
* continued.
|
||||
*
|
||||
* NOTE: Clearing attachments is considered a rendering command as specified by the vulkan
|
||||
* specification.
|
||||
*/
|
||||
void move_transfer_and_dispatch_outside_rendering_scope(const VKRenderGraph &render_graph);
|
||||
|
||||
/**
|
||||
* Find the first rendering scope inside the given search range of the result_.
|
||||
*/
|
||||
std::optional<std::pair<int64_t, int64_t>> find_rendering_scope(
|
||||
const VKRenderGraph &render_graph, IndexRange search_range) const;
|
||||
template<typename FuncT>
|
||||
void foreach_rendering_scope(const VKRenderGraph &render_graph, const FuncT &func) const
|
||||
{
|
||||
for (std::optional<std::pair<int64_t, int64_t>> rendering_scope =
|
||||
find_rendering_scope(render_graph, IndexRange(result_.index_range()));
|
||||
rendering_scope.has_value();
|
||||
rendering_scope =
|
||||
find_rendering_scope(render_graph,
|
||||
IndexRange(rendering_scope.value().second + 1,
|
||||
result_.size() - rendering_scope.value().second - 1)))
|
||||
{
|
||||
func(rendering_scope.value().first, rendering_scope.value().second);
|
||||
}
|
||||
}
|
||||
|
||||
void debug_print(const VKRenderGraph &render_graph) const;
|
||||
};
|
||||
|
||||
} // namespace blender::gpu::render_graph
|
||||
@@ -0,0 +1,42 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "vk_backbuffer_blit_infos.hh"
|
||||
|
||||
COMPUTE_SHADER_CREATE_INFO(vk_backbuffer_blit)
|
||||
|
||||
float srgb_to_linearrgb(float c)
|
||||
{
|
||||
if (c < 0.04045f) {
|
||||
return (c < 0.0f) ? 0.0f : c * (1.0f / 12.92f);
|
||||
}
|
||||
|
||||
return pow((c + 0.055f) * (1.0f / 1.055f), 2.4f);
|
||||
}
|
||||
|
||||
float3 nonlinear_to_linear_scrgb(float3 c)
|
||||
{
|
||||
#ifdef USE_GAMMA22
|
||||
return pow(c, float3(2.2f));
|
||||
#else
|
||||
return float3(srgb_to_linearrgb(c.r), srgb_to_linearrgb(c.g), srgb_to_linearrgb(c.b));
|
||||
#endif
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
int2 dst_texel = int2(gl_GlobalInvocationID.xy);
|
||||
int2 src_size = int2(imageSize(src_img));
|
||||
int2 src_texel = int2(dst_texel.x, src_size.y - dst_texel.y - 1);
|
||||
float4 color = imageLoad(src_img, int2(src_texel));
|
||||
/*
|
||||
* Convert from extended sRGB non-linear to linear.
|
||||
*
|
||||
* Preserves negative wide gamut values with sign/abs. May use either gamma 2.2
|
||||
* decode to match most SDR sRGB displays, or the piecewise sRGB function to
|
||||
* match Windows SDR applications in HDR node.
|
||||
*/
|
||||
color.rgb = sign(color.rgb) * nonlinear_to_linear_scrgb(abs(color.rgb)) * sdr_scale;
|
||||
imageStore(dst_img, int2(dst_texel), color);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifdef GPU_SHADER
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
GPU_SHADER_CREATE_INFO(vk_backbuffer_blit)
|
||||
LOCAL_GROUP_SIZE(16, 16)
|
||||
IMAGE(0, SFLOAT_16_16_16_16, read, Float2D, src_img)
|
||||
IMAGE(1, SFLOAT_16_16_16_16, write, Float2D, dst_img)
|
||||
PUSH_CONSTANT(float, sdr_scale)
|
||||
PUSH_CONSTANT(bool, use_gamma22)
|
||||
COMPUTE_SOURCE("vk_backbuffer_blit_comp.glsl")
|
||||
ADDITIONAL_INFO(gpu_srgb_to_framebuffer_space)
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(vk_backbuffer_blit_gamma22)
|
||||
ADDITIONAL_INFO(vk_backbuffer_blit)
|
||||
DEFINE("USE_GAMMA22")
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
@@ -0,0 +1,147 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "vk_data_conversion.hh"
|
||||
#include "vk_device.hh"
|
||||
|
||||
#include "gpu_vertex_format_private.hh"
|
||||
|
||||
namespace blender::gpu::tests {
|
||||
|
||||
TEST(VulkanDataConversion, clamp_negative_to_zero)
|
||||
{
|
||||
const uint32_t f32_2 = 0b11000000000000000000000000000000;
|
||||
const uint32_t f32_inf_min = 0b11111111100000000000000000000000;
|
||||
const uint32_t f32_inf_max = 0b01111111100000000000000000000000;
|
||||
const uint32_t f32_nan = 0b11111111111111111111111111111111;
|
||||
|
||||
/* F32(-2) doesn't fit in F11 as F11 only supports unsigned values. Clamp to zero. */
|
||||
const uint32_t f11_0_expected = 0b00000000000;
|
||||
const uint32_t f11_2_expected = 0b10000000000;
|
||||
const uint32_t f11_inf_expected = 0b11111000000;
|
||||
const uint32_t f11_nan_expected = 0b11111111111;
|
||||
{
|
||||
const uint32_t f11_0 = convert_float_formats<FormatF11, FormatF32, true>(f32_2);
|
||||
EXPECT_EQ(f11_0, f11_0_expected);
|
||||
const uint32_t f11_0b = convert_float_formats<FormatF11, FormatF32, true>(f32_inf_min);
|
||||
EXPECT_EQ(f11_0b, f11_0_expected);
|
||||
const uint32_t f11_inf = convert_float_formats<FormatF11, FormatF32, true>(f32_inf_max);
|
||||
EXPECT_EQ(f11_inf, f11_inf_expected);
|
||||
const uint32_t f11_nan = convert_float_formats<FormatF11, FormatF32, true>(f32_nan);
|
||||
EXPECT_EQ(f11_nan, f11_nan_expected);
|
||||
}
|
||||
|
||||
/* F32(-2) doesn't fit in F11 as F11 only supports unsigned values. Make absolute. */
|
||||
{
|
||||
const uint32_t f11_2 = convert_float_formats<FormatF11, FormatF32, false>(f32_2);
|
||||
EXPECT_EQ(f11_2, f11_2_expected);
|
||||
const uint32_t f11_inf = convert_float_formats<FormatF11, FormatF32, false>(f32_inf_min);
|
||||
EXPECT_EQ(f11_inf, f11_inf_expected);
|
||||
const uint32_t f11_infb = convert_float_formats<FormatF11, FormatF32, false>(f32_inf_max);
|
||||
EXPECT_EQ(f11_infb, f11_inf_expected);
|
||||
const uint32_t f11_nan = convert_float_formats<FormatF11, FormatF32, false>(f32_nan);
|
||||
EXPECT_EQ(f11_nan, f11_nan_expected);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(VulkanDataConversion, infinity_upper)
|
||||
{
|
||||
const uint32_t f32_inf = 0b01111111100000000000000000000000;
|
||||
|
||||
const uint32_t f11_inf_expected = 0b11111000000;
|
||||
const uint32_t f11_inf = convert_float_formats<FormatF11, FormatF32, true>(f32_inf);
|
||||
EXPECT_EQ(f11_inf, f11_inf_expected);
|
||||
|
||||
const uint32_t f10_inf_expected = 0b1111100000;
|
||||
const uint32_t f10_inf = convert_float_formats<FormatF10, FormatF32, true>(f32_inf);
|
||||
EXPECT_EQ(f10_inf, f10_inf_expected);
|
||||
}
|
||||
|
||||
TEST(VulkanDataConversion, texture_rgb16f_as_floats_to_rgba16f)
|
||||
{
|
||||
const size_t num_pixels = 4;
|
||||
const float input[] = {
|
||||
1.0,
|
||||
0.5,
|
||||
0.2,
|
||||
|
||||
0.2,
|
||||
1.0,
|
||||
0.3,
|
||||
|
||||
0.4,
|
||||
0.2,
|
||||
1.0,
|
||||
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
};
|
||||
|
||||
uint64_t device[num_pixels];
|
||||
convert_host_to_device(device,
|
||||
input,
|
||||
num_pixels,
|
||||
GPU_DATA_FLOAT,
|
||||
TextureFormat::SFLOAT_16_16_16,
|
||||
TextureFormat::SFLOAT_16_16_16_16);
|
||||
|
||||
float read_back[num_pixels * 3];
|
||||
convert_device_to_host(read_back,
|
||||
device,
|
||||
num_pixels,
|
||||
GPU_DATA_FLOAT,
|
||||
TextureFormat::SFLOAT_16_16_16,
|
||||
TextureFormat::SFLOAT_16_16_16_16);
|
||||
|
||||
for (int i : IndexRange(num_pixels * 3)) {
|
||||
EXPECT_NEAR(input[i], read_back[i], 0.01);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(VulkanDataConversion, texture_rgb32f_as_floats_to_rgba32f)
|
||||
{
|
||||
const size_t num_pixels = 4;
|
||||
const float input[] = {
|
||||
1.0,
|
||||
0.5,
|
||||
0.2,
|
||||
|
||||
0.2,
|
||||
1.0,
|
||||
0.3,
|
||||
|
||||
0.4,
|
||||
0.2,
|
||||
1.0,
|
||||
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
};
|
||||
|
||||
float device[num_pixels * 4];
|
||||
convert_host_to_device(device,
|
||||
input,
|
||||
num_pixels,
|
||||
GPU_DATA_FLOAT,
|
||||
TextureFormat::SFLOAT_32_32_32,
|
||||
TextureFormat::SFLOAT_32_32_32_32);
|
||||
|
||||
float read_back[num_pixels * 3];
|
||||
convert_device_to_host(read_back,
|
||||
device,
|
||||
num_pixels,
|
||||
GPU_DATA_FLOAT,
|
||||
TextureFormat::SFLOAT_32_32_32,
|
||||
TextureFormat::SFLOAT_32_32_32_32);
|
||||
|
||||
for (int i : IndexRange(num_pixels * 3)) {
|
||||
EXPECT_NEAR(input[i], read_back[i], 0.01);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::gpu::tests
|
||||
@@ -0,0 +1,149 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "vk_memory_layout.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
template<typename Layout>
|
||||
static void def_attr(const shader::Type type,
|
||||
const int array_size,
|
||||
const uint32_t expected_alignment,
|
||||
const uint32_t expected_reserve,
|
||||
uint32_t *r_offset)
|
||||
{
|
||||
align<Layout>(type, array_size, r_offset);
|
||||
EXPECT_EQ(*r_offset, expected_alignment);
|
||||
reserve<Layout>(type, array_size, r_offset);
|
||||
EXPECT_EQ(*r_offset, expected_reserve);
|
||||
}
|
||||
|
||||
TEST(std140, fl)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
|
||||
def_attr<Std140>(shader::Type::float_t, 0, 0, 4, &offset);
|
||||
|
||||
align_end_of_struct<Std140>(&offset);
|
||||
EXPECT_EQ(offset, 16);
|
||||
}
|
||||
|
||||
TEST(std140, _2fl)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
|
||||
def_attr<Std140>(shader::Type::float_t, 0, 0, 4, &offset);
|
||||
def_attr<Std140>(shader::Type::float_t, 0, 4, 8, &offset);
|
||||
|
||||
align_end_of_struct<Std140>(&offset);
|
||||
EXPECT_EQ(offset, 16);
|
||||
}
|
||||
|
||||
TEST(std140, _3fl)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
|
||||
def_attr<Std140>(shader::Type::float_t, 0, 0, 4, &offset);
|
||||
def_attr<Std140>(shader::Type::float_t, 0, 4, 8, &offset);
|
||||
def_attr<Std140>(shader::Type::float_t, 0, 8, 12, &offset);
|
||||
|
||||
align_end_of_struct<Std140>(&offset);
|
||||
EXPECT_EQ(offset, 16);
|
||||
}
|
||||
|
||||
TEST(std140, _4fl)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
|
||||
def_attr<Std140>(shader::Type::float_t, 0, 0, 4, &offset);
|
||||
def_attr<Std140>(shader::Type::float_t, 0, 4, 8, &offset);
|
||||
def_attr<Std140>(shader::Type::float_t, 0, 8, 12, &offset);
|
||||
def_attr<Std140>(shader::Type::float_t, 0, 12, 16, &offset);
|
||||
|
||||
align_end_of_struct<Std140>(&offset);
|
||||
EXPECT_EQ(offset, 16);
|
||||
}
|
||||
|
||||
TEST(std140, fl2)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
|
||||
def_attr<Std140>(shader::Type::float_t, 2, 0, 32, &offset);
|
||||
|
||||
align_end_of_struct<Std140>(&offset);
|
||||
EXPECT_EQ(offset, 32);
|
||||
}
|
||||
|
||||
TEST(std140, fl_fl2)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
|
||||
def_attr<Std140>(shader::Type::float_t, 0, 0, 4, &offset);
|
||||
def_attr<Std140>(shader::Type::float_t, 2, 16, 48, &offset);
|
||||
|
||||
align_end_of_struct<Std140>(&offset);
|
||||
EXPECT_EQ(offset, 48);
|
||||
}
|
||||
|
||||
TEST(std140, fl_vec2)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
|
||||
def_attr<Std140>(shader::Type::float_t, 0, 0, 4, &offset);
|
||||
def_attr<Std140>(shader::Type::float2_t, 0, 8, 16, &offset);
|
||||
|
||||
align_end_of_struct<Std140>(&offset);
|
||||
EXPECT_EQ(offset, 16);
|
||||
}
|
||||
|
||||
TEST(std140, gpu_shader_2D_widget_base)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
|
||||
def_attr<Std140>(shader::Type::float4_t, 12, 0, 192, &offset);
|
||||
def_attr<Std140>(shader::Type::float4x4_t, 0, 192, 256, &offset);
|
||||
def_attr<Std140>(shader::Type::float3_t, 0, 256, 268, &offset);
|
||||
def_attr<Std140>(shader::Type::bool_t, 0, 268, 272, &offset);
|
||||
|
||||
align_end_of_struct<Std140>(&offset);
|
||||
EXPECT_EQ(offset, 272);
|
||||
}
|
||||
|
||||
TEST(std430, overlay_grid)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
|
||||
def_attr<Std430>(shader::Type::float3_t, 0, 0, 12, &offset);
|
||||
def_attr<Std430>(shader::Type::int_t, 0, 12, 16, &offset);
|
||||
|
||||
align_end_of_struct<Std430>(&offset);
|
||||
EXPECT_EQ(offset, 16);
|
||||
}
|
||||
|
||||
TEST(std430, simple_lighting)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
|
||||
def_attr<Std430>(shader::Type::float4x4_t, 0, 0, 64, &offset);
|
||||
def_attr<Std430>(shader::Type::float3x3_t, 0, 64, 112, &offset);
|
||||
|
||||
align_end_of_struct<Std430>(&offset);
|
||||
EXPECT_EQ(offset, 112);
|
||||
}
|
||||
|
||||
TEST(std430, compositor_cryptomatte_matte_compute)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
|
||||
def_attr<Std430>(shader::Type::float2_t, 0, 0, 8, &offset);
|
||||
def_attr<Std430>(shader::Type::float_t, 0, 8, 12, &offset);
|
||||
def_attr<Std430>(shader::Type::float_t, 32, 12, 140, &offset);
|
||||
|
||||
align_end_of_struct<Std430>(&offset);
|
||||
EXPECT_EQ(offset, 144);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
951
blender-5.2.0/source/blender/gpu/vulkan/vk_backend.cc
Normal file
951
blender-5.2.0/source/blender/gpu/vulkan/vk_backend.cc
Normal file
@@ -0,0 +1,951 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
|
||||
#include "BLI_path_utils.hh"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_threads.h"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "gpu_capabilities_private.hh"
|
||||
#include "gpu_platform_private.hh"
|
||||
|
||||
#include "vk_batch.hh"
|
||||
#include "vk_context.hh"
|
||||
#include "vk_fence.hh"
|
||||
#include "vk_framebuffer.hh"
|
||||
#include "vk_ghost_api.hh"
|
||||
#include "vk_index_buffer.hh"
|
||||
#include "vk_pixel_buffer.hh"
|
||||
#include "vk_query.hh"
|
||||
#include "vk_shader.hh"
|
||||
#include "vk_state_manager.hh"
|
||||
#include "vk_storage_buffer.hh"
|
||||
#include "vk_texture.hh"
|
||||
#include "vk_texture_pool.hh"
|
||||
#include "vk_uniform_buffer.hh"
|
||||
#include "vk_vertex_buffer.hh"
|
||||
|
||||
#include "vk_backend.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static CLG_LogRef LOG = {"gpu.vulkan"};
|
||||
|
||||
namespace gpu {
|
||||
|
||||
static const char *vk_extension_get(int index)
|
||||
{
|
||||
return VKBackend::get().device.extension_name_get(index);
|
||||
}
|
||||
|
||||
bool GPU_vulkan_is_supported_driver(VkPhysicalDevice vk_physical_device)
|
||||
{
|
||||
/* Check for known faulty drivers. */
|
||||
VkPhysicalDeviceProperties2 vk_physical_device_properties = {};
|
||||
VkPhysicalDeviceDriverProperties vk_physical_device_driver_properties = {};
|
||||
vk_physical_device_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
||||
vk_physical_device_driver_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
|
||||
vk_physical_device_properties.pNext = &vk_physical_device_driver_properties;
|
||||
vkGetPhysicalDeviceProperties2(vk_physical_device, &vk_physical_device_properties);
|
||||
|
||||
#ifdef _WIN32
|
||||
/* Intel IRIS on 10th gen CPU (and older) crashes with drivers before 101.2140 due to multiple
|
||||
* driver issues.
|
||||
*
|
||||
* 1) Workbench is working, but EEVEE pipelines are failing. Calling vkCreateGraphicsPipelines
|
||||
* for certain EEVEE shaders (Shadow, Deferred rendering) would return with VK_SUCCESS, but
|
||||
* without a created VkPipeline handle.
|
||||
*
|
||||
* 2) When vkCmdBeginRendering is called some requirements need to be met, that can only be met
|
||||
* when actually calling a vkCmdDraw* command. According to the Vulkan specs the requirements
|
||||
* should only be met when calling a vkCmdDraw* command.
|
||||
*/
|
||||
if (vk_physical_device_driver_properties.driverID == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS &&
|
||||
vk_physical_device_properties.properties.deviceType ==
|
||||
VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU)
|
||||
{
|
||||
const uint32_t driver_version = vk_physical_device_properties.properties.driverVersion;
|
||||
uint32_t driver_version_major = driver_version >> 14u;
|
||||
uint32_t driver_version_minor = driver_version & 0x3fffu;
|
||||
if (driver_version_major < 101 || (driver_version_major == 101 && driver_version_minor < 2140))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
/* NVIDIA drivers below 550 don't work on Linux. When sending command to the GPU there is not
|
||||
* always a reply back when they are finished. The issue is reported on the Internet many times,
|
||||
* but there is no mention of a solution. This means that on Linux we can only support GTX900 and
|
||||
* or use MesaNVK.
|
||||
*/
|
||||
uint32_t conformance_version = VK_MAKE_API_VERSION(
|
||||
vk_physical_device_driver_properties.conformanceVersion.major,
|
||||
vk_physical_device_driver_properties.conformanceVersion.minor,
|
||||
vk_physical_device_driver_properties.conformanceVersion.subminor,
|
||||
vk_physical_device_driver_properties.conformanceVersion.patch);
|
||||
if (vk_physical_device_driver_properties.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY &&
|
||||
conformance_version < VK_MAKE_API_VERSION(1, 3, 7, 2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* NVIDIA driver 580.76.05 doesn't start using specific Wayland configurations #144625. There are
|
||||
* multiple reports also not Blender related and NVIDIA mentions that a new driver will be
|
||||
* released. It is unclear if that driver will fix our issue. For now disabling this driver on
|
||||
* Linux. This also disables it for configurations that are working as well (including X11). */
|
||||
if (vk_physical_device_driver_properties.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY &&
|
||||
((StringRefNull(vk_physical_device_driver_properties.driverInfo).find("580.76.5", 0) !=
|
||||
StringRef::not_found) ||
|
||||
(StringRefNull(vk_physical_device_driver_properties.driverInfo).find("580.76.05", 0) !=
|
||||
StringRef::not_found)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
if (vk_physical_device_driver_properties.driverID == VK_DRIVER_ID_QUALCOMM_PROPRIETARY) {
|
||||
/* Any Qualcomm driver older than 31.0.112.0 will not be capable of running blender due
|
||||
* to an issue in their semaphore timeline implementation. The driver could return
|
||||
* timelines that have not been provided by Blender. As Blender uses timelines for resource
|
||||
* management this resulted in resources to be destroyed, that are still in use. */
|
||||
|
||||
/* Public version 31.0.112 uses vulkan driver version 512.827.0. */
|
||||
const uint32_t driver_version = vk_physical_device_properties.properties.driverVersion;
|
||||
constexpr uint32_t version_31_0_112 = VK_MAKE_VERSION(512, 827, 0);
|
||||
if (driver_version < version_31_0_112) {
|
||||
CLOG_WARN(&LOG,
|
||||
"Detected qualcomm driver is not supported. To run the Vulkan backend "
|
||||
"driver 31.0.112.0 or later is required. Switching to OpenGL.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static Vector<StringRefNull> missing_capabilities_get(VkPhysicalDevice vk_physical_device)
|
||||
{
|
||||
Vector<StringRefNull> missing_capabilities;
|
||||
/* Check device features. */
|
||||
VkPhysicalDeviceVulkan12Features features_12 = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES};
|
||||
VkPhysicalDeviceVulkan11Features features_11 = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES, &features_12};
|
||||
VkPhysicalDeviceFeatures2 features = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
||||
&features_11};
|
||||
|
||||
vkGetPhysicalDeviceFeatures2(vk_physical_device, &features);
|
||||
|
||||
#ifndef __APPLE__
|
||||
/* Features currently not supported by Mesa KosmicKrisp. */
|
||||
if (features.features.geometryShader == VK_FALSE) {
|
||||
missing_capabilities.append("geometry shaders");
|
||||
}
|
||||
#endif
|
||||
if (features.features.vertexPipelineStoresAndAtomics == VK_FALSE) {
|
||||
missing_capabilities.append("vertex pipeline stores and atomics");
|
||||
}
|
||||
if (features.features.multiViewport == VK_FALSE) {
|
||||
missing_capabilities.append("multi viewport");
|
||||
}
|
||||
if (features.features.shaderClipDistance == VK_FALSE) {
|
||||
missing_capabilities.append("shader clip distance");
|
||||
}
|
||||
if (features.features.fragmentStoresAndAtomics == VK_FALSE) {
|
||||
missing_capabilities.append("fragment stores and atomics");
|
||||
}
|
||||
if (features.features.logicOp == VK_FALSE) {
|
||||
missing_capabilities.append("logical operations");
|
||||
}
|
||||
if (features.features.dualSrcBlend == VK_FALSE) {
|
||||
missing_capabilities.append("dual source blending");
|
||||
}
|
||||
if (features.features.imageCubeArray == VK_FALSE) {
|
||||
missing_capabilities.append("image cube array");
|
||||
}
|
||||
if (features.features.multiDrawIndirect == VK_FALSE) {
|
||||
missing_capabilities.append("multi draw indirect");
|
||||
}
|
||||
if (features.features.drawIndirectFirstInstance == VK_FALSE) {
|
||||
missing_capabilities.append("draw indirect first instance");
|
||||
}
|
||||
if (features_11.shaderDrawParameters == VK_FALSE) {
|
||||
missing_capabilities.append("shader draw parameters");
|
||||
}
|
||||
if (features_12.timelineSemaphore == VK_FALSE) {
|
||||
missing_capabilities.append("timeline semaphores");
|
||||
}
|
||||
if (features_12.bufferDeviceAddress == VK_FALSE) {
|
||||
missing_capabilities.append("buffer device address");
|
||||
}
|
||||
|
||||
/* Check device extensions. */
|
||||
uint32_t vk_extension_count;
|
||||
vkEnumerateDeviceExtensionProperties(vk_physical_device, nullptr, &vk_extension_count, nullptr);
|
||||
|
||||
Array<VkExtensionProperties> vk_extensions(vk_extension_count);
|
||||
vkEnumerateDeviceExtensionProperties(
|
||||
vk_physical_device, nullptr, &vk_extension_count, vk_extensions.data());
|
||||
Set<StringRefNull> extensions;
|
||||
for (VkExtensionProperties &vk_extension : vk_extensions) {
|
||||
extensions.add(vk_extension.extensionName);
|
||||
}
|
||||
|
||||
if (!extensions.contains(VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
|
||||
missing_capabilities.append(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
}
|
||||
if (!extensions.contains(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME)) {
|
||||
missing_capabilities.append(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME);
|
||||
}
|
||||
#ifndef __APPLE__
|
||||
/* Metal doesn't support provoking vertex. */
|
||||
if (!extensions.contains(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME)) {
|
||||
missing_capabilities.append(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
|
||||
}
|
||||
#endif
|
||||
|
||||
return missing_capabilities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable implicit layers and only allow layers that we trust.
|
||||
*
|
||||
* Render doc layer is hidden behind a debug flag. There are malicious layers that impersonate
|
||||
* RenderDoc and can crash when loaded. See #139543.
|
||||
*
|
||||
* Must be called before any `vkCreateInstance` so temporary Vulkan instances created during
|
||||
* argument handling (e.g. `--gpu-device help`) don't load implicit layers either.
|
||||
*/
|
||||
static void vk_restrict_loader_layers()
|
||||
{
|
||||
std::stringstream allowed_layers;
|
||||
allowed_layers << "VK_LAYER_KHRONOS_*";
|
||||
allowed_layers << ",VK_LAYER_AMD_*";
|
||||
allowed_layers << ",VK_LAYER_INTEL_*";
|
||||
allowed_layers << ",VK_LAYER_NV_*";
|
||||
allowed_layers << ",VK_LAYER_MESA_*";
|
||||
if (bool(G.debug & G_DEBUG_GPU)) {
|
||||
allowed_layers << ",VK_LAYER_LUNARG_*";
|
||||
allowed_layers << ",VK_LAYER_RENDERDOC_*";
|
||||
}
|
||||
BLI_setenv("VK_LOADER_LAYERS_DISABLE", "~implicit~");
|
||||
BLI_setenv("VK_LOADER_LAYERS_ALLOW", allowed_layers.str().c_str());
|
||||
}
|
||||
|
||||
static bool vk_instance_create_for_platform_checks(VkInstance *r_instance)
|
||||
{
|
||||
vk_restrict_loader_layers();
|
||||
|
||||
/* Initialize an vulkan 1.2 instance. */
|
||||
VkApplicationInfo vk_application_info = {VK_STRUCTURE_TYPE_APPLICATION_INFO};
|
||||
vk_application_info.pApplicationName = "Blender";
|
||||
vk_application_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
|
||||
vk_application_info.pEngineName = "Blender";
|
||||
vk_application_info.engineVersion = VK_MAKE_VERSION(1, 0, 0);
|
||||
vk_application_info.apiVersion = VK_API_VERSION_1_2;
|
||||
|
||||
VkInstanceCreateInfo vk_instance_info = {VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO};
|
||||
vk_instance_info.pApplicationInfo = &vk_application_info;
|
||||
|
||||
*r_instance = VK_NULL_HANDLE;
|
||||
vkCreateInstance(&vk_instance_info, nullptr, r_instance);
|
||||
|
||||
return *r_instance != VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
bool VKBackend::is_supported()
|
||||
{
|
||||
CLG_logref_init(&LOG);
|
||||
|
||||
VkInstance vk_instance = VK_NULL_HANDLE;
|
||||
if (!vk_instance_create_for_platform_checks(&vk_instance)) {
|
||||
CLOG_ERROR(&LOG, "Unable to initialize a Vulkan 1.2 instance.");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Go over all the devices. */
|
||||
uint32_t physical_devices_count = 0;
|
||||
vkEnumeratePhysicalDevices(vk_instance, &physical_devices_count, nullptr);
|
||||
Array<VkPhysicalDevice> vk_physical_devices(physical_devices_count);
|
||||
vkEnumeratePhysicalDevices(vk_instance, &physical_devices_count, vk_physical_devices.data());
|
||||
|
||||
for (VkPhysicalDevice vk_physical_device : vk_physical_devices) {
|
||||
VkPhysicalDeviceProperties vk_properties = {};
|
||||
vkGetPhysicalDeviceProperties(vk_physical_device, &vk_properties);
|
||||
|
||||
if (!GPU_vulkan_is_supported_driver(vk_physical_device)) {
|
||||
CLOG_WARN(&LOG,
|
||||
"Installed driver for device [%s] has known issues and will not be used. Updating "
|
||||
"driver might improve compatibility.",
|
||||
vk_properties.deviceName);
|
||||
continue;
|
||||
}
|
||||
Vector<StringRefNull> missing_capabilities = missing_capabilities_get(vk_physical_device);
|
||||
|
||||
/* Report result. */
|
||||
if (missing_capabilities.is_empty()) {
|
||||
/* This device meets minimum requirements. */
|
||||
CLOG_DEBUG(
|
||||
&LOG,
|
||||
"Device [%s] supports minimum requirements. Skip checking other GPUs. Another GPU "
|
||||
"can still be selected during auto-detection.",
|
||||
vk_properties.deviceName);
|
||||
|
||||
vkDestroyInstance(vk_instance, nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "Device [" << vk_properties.deviceName
|
||||
<< "] does not meet minimum requirements. Missing features are [";
|
||||
for (StringRefNull &feature : missing_capabilities) {
|
||||
ss << feature << ", ";
|
||||
}
|
||||
ss.seekp(-2, std::ios_base::end);
|
||||
ss << "]";
|
||||
CLOG_WARN(&LOG, "%s", ss.str().c_str());
|
||||
}
|
||||
|
||||
/* No device found meeting the minimum requirements. */
|
||||
|
||||
vkDestroyInstance(vk_instance, nullptr);
|
||||
CLOG_ERROR(&LOG,
|
||||
"No Vulkan device found that meets the minimum requirements. "
|
||||
"Updating GPU driver can improve compatibility.");
|
||||
return false;
|
||||
}
|
||||
|
||||
void VKBackend::supported_devices_print(FILE *fp)
|
||||
{
|
||||
CLG_logref_init(&LOG);
|
||||
|
||||
VkInstance vk_instance = VK_NULL_HANDLE;
|
||||
if (!vk_instance_create_for_platform_checks(&vk_instance)) {
|
||||
fprintf(fp, "Unable to initialize a Vulkan 1.2 instance.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t physical_devices_count = 0;
|
||||
vkEnumeratePhysicalDevices(vk_instance, &physical_devices_count, nullptr);
|
||||
Array<VkPhysicalDevice> vk_physical_devices(physical_devices_count);
|
||||
vkEnumeratePhysicalDevices(vk_instance, &physical_devices_count, vk_physical_devices.data());
|
||||
|
||||
struct Row {
|
||||
int index;
|
||||
std::string identifier;
|
||||
std::string name;
|
||||
};
|
||||
Vector<Row> rows;
|
||||
int index = 0;
|
||||
for (VkPhysicalDevice vk_physical_device : vk_physical_devices) {
|
||||
if (missing_capabilities_get(vk_physical_device).is_empty() &&
|
||||
GPU_vulkan_is_supported_driver(vk_physical_device))
|
||||
{
|
||||
VkPhysicalDeviceProperties vk_properties = {};
|
||||
vkGetPhysicalDeviceProperties(vk_physical_device, &vk_properties);
|
||||
std::stringstream identifier;
|
||||
identifier << std::hex << vk_properties.vendorID << "/" << vk_properties.deviceID << "/"
|
||||
<< index;
|
||||
rows.append({index, identifier.str(), std::string(vk_properties.deviceName)});
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
if (rows.is_empty()) {
|
||||
fprintf(fp, " (no supported Vulkan devices found)\n");
|
||||
vkDestroyInstance(vk_instance, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *col_index = "Index";
|
||||
const char *col_id = "Device-ID";
|
||||
const char *col_name = "Name";
|
||||
size_t w_index = strlen(col_index);
|
||||
size_t w_id = strlen(col_id);
|
||||
for (const Row &row : rows) {
|
||||
char buf[16];
|
||||
w_index = std::max(w_index, BLI_snprintf_rlen(buf, sizeof(buf), "%d", row.index));
|
||||
w_id = std::max(w_id, row.identifier.size());
|
||||
}
|
||||
|
||||
fprintf(fp, " %-*s %-*s %s\n", int(w_index), col_index, int(w_id), col_id, col_name);
|
||||
for (const Row &row : rows) {
|
||||
fprintf(fp,
|
||||
" %-*d %-*s %s\n",
|
||||
int(w_index),
|
||||
row.index,
|
||||
int(w_id),
|
||||
row.identifier.c_str(),
|
||||
row.name.c_str());
|
||||
}
|
||||
|
||||
vkDestroyInstance(vk_instance, nullptr);
|
||||
}
|
||||
|
||||
static GPUOSType determine_os_type()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return GPU_OS_WIN;
|
||||
#elif defined(__APPLE__)
|
||||
return GPU_OS_MAC;
|
||||
#else
|
||||
return GPU_OS_UNIX;
|
||||
#endif
|
||||
}
|
||||
|
||||
void VKBackend::platform_init()
|
||||
{
|
||||
GPG.init(GPU_DEVICE_ANY,
|
||||
determine_os_type(),
|
||||
GPU_DRIVER_ANY,
|
||||
GPU_SUPPORT_LEVEL_SUPPORTED,
|
||||
GPU_BACKEND_VULKAN,
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
GPU_ARCHITECTURE_IMR);
|
||||
}
|
||||
|
||||
static void init_device_list(GHOST_IContext *ghost_context)
|
||||
{
|
||||
GHOST_VulkanHandles vulkan_handles = {};
|
||||
ghost_context->getVulkanHandles(vulkan_handles);
|
||||
|
||||
uint32_t physical_devices_count = 0;
|
||||
vkEnumeratePhysicalDevices(vulkan_handles.instance, &physical_devices_count, nullptr);
|
||||
Array<VkPhysicalDevice> vk_physical_devices(physical_devices_count);
|
||||
vkEnumeratePhysicalDevices(
|
||||
vulkan_handles.instance, &physical_devices_count, vk_physical_devices.data());
|
||||
int index = 0;
|
||||
for (VkPhysicalDevice vk_physical_device : vk_physical_devices) {
|
||||
if (missing_capabilities_get(vk_physical_device).is_empty() &&
|
||||
GPU_vulkan_is_supported_driver(vk_physical_device))
|
||||
{
|
||||
VkPhysicalDeviceProperties vk_properties = {};
|
||||
vkGetPhysicalDeviceProperties(vk_physical_device, &vk_properties);
|
||||
std::stringstream identifier;
|
||||
identifier << std::hex << vk_properties.vendorID << "/" << vk_properties.deviceID << "/"
|
||||
<< index;
|
||||
GPG.devices.append({identifier.str(),
|
||||
index,
|
||||
vk_properties.vendorID,
|
||||
vk_properties.deviceID,
|
||||
std::string(vk_properties.deviceName)});
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
std::ranges::sort(GPG.devices, [&](const GPUDevice &a, const GPUDevice &b) {
|
||||
if (a.name == b.name) {
|
||||
return a.index < b.index;
|
||||
}
|
||||
return a.name < b.name;
|
||||
});
|
||||
}
|
||||
|
||||
void VKBackend::platform_init(const VKDevice &device)
|
||||
{
|
||||
const VkPhysicalDeviceProperties &properties = device.physical_device_properties_get();
|
||||
|
||||
GPUDeviceType device_type = device.device_type();
|
||||
GPUDriverType driver = device.driver_type();
|
||||
GPUOSType os = determine_os_type();
|
||||
GPUSupportLevel support_level = GPU_SUPPORT_LEVEL_SUPPORTED;
|
||||
|
||||
std::string vendor_name = device.vendor_name();
|
||||
std::string driver_version = device.driver_version();
|
||||
|
||||
/* GPG has already been initialized, but without a specific device. Calling init twice will
|
||||
* clear the list of devices. Making a copy of the device list and set it after initialization to
|
||||
* make sure the list isn't destroyed at this moment, but only when the backend is destroyed. */
|
||||
Vector<GPUDevice> devices = GPG.devices;
|
||||
GPG.init(device_type,
|
||||
os,
|
||||
driver,
|
||||
support_level,
|
||||
GPU_BACKEND_VULKAN,
|
||||
vendor_name.c_str(),
|
||||
properties.deviceName,
|
||||
driver_version.c_str(),
|
||||
GPU_ARCHITECTURE_IMR);
|
||||
GPG.devices = devices;
|
||||
|
||||
const VkPhysicalDeviceIDProperties &id_properties = device.physical_device_id_properties_get();
|
||||
|
||||
GPG.device_uuid = Array<uint8_t, 16>(Span<uint8_t>(id_properties.deviceUUID, VK_UUID_SIZE));
|
||||
|
||||
if (id_properties.deviceLUIDValid) {
|
||||
GPG.device_luid = Array<uint8_t, 8>(Span<uint8_t>(id_properties.deviceUUID, VK_LUID_SIZE));
|
||||
GPG.device_luid_node_mask = id_properties.deviceNodeMask;
|
||||
}
|
||||
else {
|
||||
GPG.device_luid.reinitialize(0);
|
||||
GPG.device_luid_node_mask = 0;
|
||||
}
|
||||
|
||||
CLOG_INFO(&LOG,
|
||||
"Using vendor [%s] device [%s] driver version [%s].",
|
||||
vendor_name.c_str(),
|
||||
device.vk_physical_device_properties_.deviceName,
|
||||
driver_version.c_str());
|
||||
}
|
||||
|
||||
enum class IntelGpuArch : uint32_t {
|
||||
Gen9AndOlder,
|
||||
Gen11,
|
||||
Gen12,
|
||||
Xe = Gen12,
|
||||
Xe2,
|
||||
Xe3AndNewer,
|
||||
};
|
||||
|
||||
inline IntelGpuArch get_intel_gpu_arch(uint32_t device_id)
|
||||
{
|
||||
/* Source for device IDs:
|
||||
* https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/include/pci_ids/iris_pci_ids.h
|
||||
*/
|
||||
switch (device_id & 0xFF00) {
|
||||
case 0x2900: // Broadwater
|
||||
case 0x2A00: // Broadwater/Eagle Lake
|
||||
case 0x2E00: // Eagle Lake
|
||||
case 0x0000: // Iron Lake
|
||||
case 0x0100: // Ivy Bridge/Sandy Bridge/Baytrail
|
||||
case 0x0F00: // Baytrail
|
||||
case 0x0400: // Haswell
|
||||
case 0x0C00: // Haswell
|
||||
case 0x0D00: // Haswell
|
||||
case 0x0A00: // Haswell / Appollo Lake
|
||||
case 0x2200: // Cherrytrail
|
||||
case 0x1600: // Broadwell
|
||||
case 0x5A00: // Apollo Lake
|
||||
case 0x1900: // Skylake
|
||||
case 0x1A00: // Apollo Lake
|
||||
case 0x3100: // Gemini Lake
|
||||
case 0x5900: // Kaby Lake/Amber Lake
|
||||
case 0x8700: // Kaby Lake/Coffee Lake
|
||||
case 0x3E00: // Coffee Lake/Whiskey Lake
|
||||
case 0x9B00: // Comet Lake
|
||||
return IntelGpuArch::Gen9AndOlder;
|
||||
case 0x8A00: // Ice Lake
|
||||
case 0x4500: // Elkhart Lake
|
||||
case 0x4E00: // Jasper Lake
|
||||
return IntelGpuArch::Gen11;
|
||||
case 0x9A00: // Tiger Lake
|
||||
case 0x4C00: // Rocket Lake
|
||||
case 0x4900: // DG1
|
||||
case 0x4600: // Alder Lake
|
||||
case 0x4F00: // Alchemist
|
||||
case 0x5600: // Alchemist
|
||||
case 0xA700: // Raptor Lake
|
||||
case 0x7D00: // Meteor Lake / Arrow Lake
|
||||
case 0xB600: // Meteor Lake / Arrow Lake
|
||||
return IntelGpuArch::Xe;
|
||||
case 0x6400: // Lunar Lake
|
||||
case 0xE200: // Battlemage
|
||||
return IntelGpuArch::Xe2;
|
||||
case 0xB000: // Panther Lake
|
||||
case 0xFD00: // Wildcat Lake
|
||||
default:
|
||||
return IntelGpuArch::Xe3AndNewer;
|
||||
}
|
||||
}
|
||||
|
||||
void VKBackend::detect_workarounds(VKDevice &device)
|
||||
{
|
||||
VKWorkarounds workarounds;
|
||||
VKExtensions extensions;
|
||||
|
||||
if (G.debug & G_DEBUG_GPU_FORCE_WORKAROUNDS) {
|
||||
CLOG_WARN(&LOG, "Forcing workarounds and disabling features and extensions");
|
||||
|
||||
/* Force workarounds and disable extensions. */
|
||||
workarounds.not_aligned_pixel_formats = true;
|
||||
extensions.shader_output_layer = false;
|
||||
extensions.shader_output_viewport_index = false;
|
||||
extensions.fragment_shader_barycentric = false;
|
||||
extensions.dynamic_rendering_local_read = false;
|
||||
extensions.dynamic_rendering_unused_attachments = false;
|
||||
extensions.pageable_device_local_memory = false;
|
||||
extensions.wide_lines = false;
|
||||
extensions.line_rasterization = false;
|
||||
extensions.extended_dynamic_state = false;
|
||||
GCaps.stencil_export_support = false;
|
||||
GCaps.texture_pool_workaround = true;
|
||||
|
||||
device.workarounds_ = workarounds;
|
||||
device.extensions_ = extensions;
|
||||
return;
|
||||
}
|
||||
|
||||
if (G.debug & G_DEBUG_GPU_NO_TEXTURE_POOL) {
|
||||
GCaps.texture_pool_workaround = true;
|
||||
}
|
||||
|
||||
extensions.shader_output_layer =
|
||||
device.physical_device_vulkan_12_features_get().shaderOutputLayer;
|
||||
extensions.shader_output_viewport_index =
|
||||
device.physical_device_vulkan_12_features_get().shaderOutputViewportIndex;
|
||||
extensions.wide_lines = device.physical_device_features_get().wideLines;
|
||||
extensions.fragment_shader_barycentric = device.supports_extension(
|
||||
VK_KHR_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME);
|
||||
extensions.dynamic_rendering_local_read = device.supports_extension(
|
||||
VK_KHR_DYNAMIC_RENDERING_LOCAL_READ_EXTENSION_NAME);
|
||||
extensions.dynamic_rendering_unused_attachments = device.supports_extension(
|
||||
VK_EXT_DYNAMIC_RENDERING_UNUSED_ATTACHMENTS_EXTENSION_NAME);
|
||||
extensions.logic_ops = device.physical_device_features_get().logicOp;
|
||||
extensions.maintenance4 = device.supports_extension(VK_KHR_MAINTENANCE_4_EXTENSION_NAME);
|
||||
extensions.memory_priority = device.supports_extension(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME);
|
||||
extensions.pageable_device_local_memory = device.supports_extension(
|
||||
VK_EXT_PAGEABLE_DEVICE_LOCAL_MEMORY_EXTENSION_NAME);
|
||||
extensions.graphics_pipeline_library = device.supports_extension(
|
||||
VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME);
|
||||
extensions.line_rasterization = device.supports_extension(
|
||||
VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME);
|
||||
extensions.extended_dynamic_state = device.supports_extension(
|
||||
VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
extensions.vertex_input_dynamic_state = device.supports_extension(
|
||||
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
#if 0
|
||||
extensions.host_image_copy = device.supports_extension(VK_EXT_HOST_IMAGE_COPY_EXTENSION_NAME);
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
extensions.external_memory = device.supports_extension(
|
||||
VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME);
|
||||
#elif not defined(__APPLE__)
|
||||
extensions.external_memory = device.supports_extension(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME);
|
||||
#else
|
||||
extensions.external_memory = false;
|
||||
#endif
|
||||
|
||||
/* AMD GPUs don't support texture formats that use are aligned to 24 or 48 bits. */
|
||||
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY) ||
|
||||
GPU_type_matches(GPU_DEVICE_APPLE, GPU_OS_MAC, GPU_DRIVER_ANY))
|
||||
{
|
||||
workarounds.not_aligned_pixel_formats = true;
|
||||
}
|
||||
|
||||
/* During testing graphics pipeline library feature it was detected that it would crash on
|
||||
* official AMD drivers.
|
||||
*/
|
||||
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OFFICIAL) &&
|
||||
bool(G.debug & G_DEBUG_GPU))
|
||||
{
|
||||
extensions.graphics_pipeline_library = false;
|
||||
extensions.vertex_input_dynamic_state = false;
|
||||
}
|
||||
|
||||
/* Disable vertex input dynamic state for Qualcomm devices (#153414).
|
||||
*
|
||||
* TODO: We should re-validate vertex input dynamic state as there are multiple vendors with
|
||||
* similar issues. It might be an oversight. Will wait for feedback from the driver developers
|
||||
* and perform some out of bounds error checks.
|
||||
*/
|
||||
if (GPU_type_matches(GPU_DEVICE_QUALCOMM, GPU_OS_WIN, GPU_DRIVER_ANY)) {
|
||||
extensions.vertex_input_dynamic_state = false;
|
||||
}
|
||||
|
||||
/* Only enable by default dynamic rendering local read on Qualcomm devices. NVIDIA, AMD and Intel
|
||||
* performance is better when disabled (20%). On Qualcomm devices the improvement can be
|
||||
* substantial (16% on shader_balls.blend).
|
||||
*
|
||||
* `--debug-gpu-vulkan-local-read` can be used to use dynamic rendering local read on any
|
||||
* supported platform.
|
||||
*
|
||||
* TODO: Check if bottleneck is during command building. If so we could fine-tune this after the
|
||||
* device command building landed (T132682).
|
||||
*/
|
||||
if ((G.debug & G_DEBUG_GPU_FORCE_VULKAN_LOCAL_READ) == 0 &&
|
||||
!GPU_type_matches(GPU_DEVICE_QUALCOMM, GPU_OS_ANY, GPU_DRIVER_ANY))
|
||||
{
|
||||
extensions.dynamic_rendering_local_read = false;
|
||||
}
|
||||
|
||||
/* When using host image copy on certain NVIDIA platforms the allocation of textures that only
|
||||
* use GPU_TEXTURE_USAGE_SHADER_READ/WRITE will fail to allocate the memory. This needs some more
|
||||
* research as this might just be a missing flag when allocating. Another solution is to not
|
||||
* allow host_imag_copy when only these two flags are set as the rest seems to work as expected.
|
||||
*
|
||||
* See #151826
|
||||
*/
|
||||
if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_OFFICIAL)) {
|
||||
extensions.host_image_copy = false;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (GPU_type_matches(GPU_DEVICE_INTEL | GPU_DEVICE_INTEL_UHD, GPU_OS_WIN, GPU_DRIVER_OFFICIAL)) {
|
||||
IntelGpuArch gpu_arch = get_intel_gpu_arch(device.physical_device_properties_get().deviceID);
|
||||
|
||||
/* Intel Gen9 iGPUs (Intel 7th to 10th Gen Processor Graphics driver) show a black screen at
|
||||
* application startup when using VK_EXT_vertex_input_dynamic_state.
|
||||
*
|
||||
* See #147721
|
||||
*/
|
||||
if (gpu_arch == IntelGpuArch::Gen9AndOlder) {
|
||||
extensions.vertex_input_dynamic_state = false;
|
||||
}
|
||||
|
||||
/* Using the texture pool causes varying issues on older Intel iGPUs.
|
||||
* Note: Gen12 iGPUs are partly covered by the Intel 11th to 14th Gen Processor Graphics driver
|
||||
* and the Intel Arc Graphics driver (the latter handles Arrow Lake and Meteor Lake).
|
||||
* - Visual corruptions can be seen on Gen9 and older iGPUs (Intel 7th to 10th Gen Processor
|
||||
* Graphics driver; #147721).
|
||||
* - When using the image cache, visual artifacts can be seen on Gen11 and Gen12 iGPUs
|
||||
* (#156496) and Gen12 dGPUs (#160002).
|
||||
* - When using the texture pool without the image cache, memory leaks happen on Gen11 and
|
||||
* Gen12 GPUs (#157777).
|
||||
*/
|
||||
if (gpu_arch <= IntelGpuArch::Gen12) {
|
||||
GCaps.texture_pool_workaround = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
extensions.extended_dynamic_state = false;
|
||||
#endif
|
||||
|
||||
device.workarounds_ = workarounds;
|
||||
device.extensions_ = extensions;
|
||||
}
|
||||
|
||||
void VKBackend::platform_exit()
|
||||
{
|
||||
GPG.clear();
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
if (device.is_initialized()) {
|
||||
device.deinit();
|
||||
}
|
||||
}
|
||||
|
||||
void VKBackend::init_resources()
|
||||
{
|
||||
compiler_ = MEM_new<ShaderCompiler>(
|
||||
__func__, GPU_max_parallel_compilations(), GPUWorker::ContextType::Main);
|
||||
}
|
||||
|
||||
void VKBackend::delete_resources()
|
||||
{
|
||||
MEM_delete(compiler_);
|
||||
}
|
||||
|
||||
void VKBackend::compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len)
|
||||
{
|
||||
VKContext &context = *VKContext::get();
|
||||
render_graph::VKResourceAccessInfo &resources = context.reset_and_get_access_info();
|
||||
render_graph::VKDispatchNode::CreateInfo dispatch_info(resources);
|
||||
context.update_pipeline_data(dispatch_info.dispatch_node.pipeline_data);
|
||||
dispatch_info.dispatch_node.group_count_x = groups_x_len;
|
||||
dispatch_info.dispatch_node.group_count_y = groups_y_len;
|
||||
dispatch_info.dispatch_node.group_count_z = groups_z_len;
|
||||
context.render_graph().add_node(dispatch_info);
|
||||
}
|
||||
|
||||
void VKBackend::compute_dispatch_indirect(StorageBuf *indirect_buf)
|
||||
{
|
||||
BLI_assert(indirect_buf);
|
||||
VKContext &context = *VKContext::get();
|
||||
VKStorageBuffer &indirect_buffer = *unwrap(indirect_buf);
|
||||
render_graph::VKResourceAccessInfo &resources = context.reset_and_get_access_info();
|
||||
render_graph::VKDispatchIndirectNode::CreateInfo dispatch_indirect_info(resources);
|
||||
context.update_pipeline_data(dispatch_indirect_info.dispatch_indirect_node.pipeline_data);
|
||||
dispatch_indirect_info.dispatch_indirect_node.buffer = indirect_buffer.vk_handle();
|
||||
dispatch_indirect_info.dispatch_indirect_node.offset = 0;
|
||||
context.render_graph().add_node(dispatch_indirect_info);
|
||||
}
|
||||
|
||||
Context *VKBackend::context_alloc(GHOST_IWindow *ghost_window, GHOST_IContext *ghost_context)
|
||||
{
|
||||
if (ghost_window) {
|
||||
BLI_assert(ghost_context == nullptr);
|
||||
ghost_context = ghost_window->getDrawingContext();
|
||||
}
|
||||
|
||||
BLI_assert(ghost_context != nullptr);
|
||||
if (!device.is_initialized()) {
|
||||
device.init(ghost_context);
|
||||
device.extensions_get().log();
|
||||
device.workarounds_get().log();
|
||||
init_device_list(ghost_context);
|
||||
}
|
||||
|
||||
VKContext *context = new VKContext(ghost_window, ghost_context);
|
||||
device.context_register(*context);
|
||||
ghost_context->setVulkanSwapBuffersCallbacks(
|
||||
VKContext::swap_buffer_draw_callback,
|
||||
VKContext::swap_buffer_acquired_callback,
|
||||
VKContext::openxr_acquire_framebuffer_image_callback,
|
||||
VKContext::openxr_release_framebuffer_image_callback);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
Batch *VKBackend::batch_alloc()
|
||||
{
|
||||
return new VKBatch();
|
||||
}
|
||||
|
||||
Fence *VKBackend::fence_alloc()
|
||||
{
|
||||
return new VKFence();
|
||||
}
|
||||
|
||||
FrameBuffer *VKBackend::framebuffer_alloc(const char *name)
|
||||
{
|
||||
return new VKFrameBuffer(name);
|
||||
}
|
||||
|
||||
IndexBuf *VKBackend::indexbuf_alloc()
|
||||
{
|
||||
return new VKIndexBuffer();
|
||||
}
|
||||
|
||||
PixelBuffer *VKBackend::pixelbuf_alloc(size_t size)
|
||||
{
|
||||
return new VKPixelBuffer(size);
|
||||
}
|
||||
|
||||
QueryPool *VKBackend::querypool_alloc()
|
||||
{
|
||||
return new VKQueryPool();
|
||||
}
|
||||
|
||||
Shader *VKBackend::shader_alloc(const char *name)
|
||||
{
|
||||
return new VKShader(name);
|
||||
}
|
||||
|
||||
Texture *VKBackend::texture_alloc(const char *name)
|
||||
{
|
||||
return new VKTexture(name);
|
||||
}
|
||||
|
||||
TexturePool *VKBackend::texturepool_alloc()
|
||||
{
|
||||
if (GCaps.texture_pool_workaround) {
|
||||
CLOG_TRACE(&LOG, "Using texture pool \"TexturePoolImpl\".");
|
||||
return new TexturePoolImpl();
|
||||
}
|
||||
CLOG_TRACE(&LOG, "Using texture pool \"VKTexturePool\".");
|
||||
return new VKTexturePool();
|
||||
}
|
||||
|
||||
UniformBuf *VKBackend::uniformbuf_alloc(size_t size, const char *name)
|
||||
{
|
||||
return new VKUniformBuffer(size, name);
|
||||
}
|
||||
|
||||
StorageBuf *VKBackend::storagebuf_alloc(size_t size, GPUUsageType usage, const char *name)
|
||||
{
|
||||
return new VKStorageBuffer(size, usage, name);
|
||||
}
|
||||
|
||||
VertBuf *VKBackend::vertbuf_alloc()
|
||||
{
|
||||
return new VKVertexBuffer();
|
||||
}
|
||||
|
||||
void VKBackend::render_begin()
|
||||
{
|
||||
VKThreadData &thread_data = device.current_thread_data();
|
||||
BLI_assert_msg(thread_data.rendering_depth >= 0, "Unbalanced `GPU_render_begin/end`");
|
||||
thread_data.rendering_depth += 1;
|
||||
}
|
||||
|
||||
void VKBackend::render_end()
|
||||
{
|
||||
VKThreadData &thread_data = device.current_thread_data();
|
||||
thread_data.rendering_depth -= 1;
|
||||
BLI_assert_msg(thread_data.rendering_depth >= 0, "Unbalanced `GPU_render_begin/end`");
|
||||
if (G.background) {
|
||||
if (thread_data.rendering_depth == 0) {
|
||||
VKContext *context = VKContext::get();
|
||||
if (context != nullptr) {
|
||||
context->flush();
|
||||
}
|
||||
std::scoped_lock lock(device.orphaned_data.mutex_get());
|
||||
device.orphaned_data.move_data(device.orphaned_data_render,
|
||||
device.orphaned_data.timeline_ + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* When performing animation render we want to release any discarded resources during rendering
|
||||
* after each frame.
|
||||
*/
|
||||
if (G.is_rendering && thread_data.rendering_depth == 0 && !BLI_thread_is_main()) {
|
||||
std::scoped_lock lock(device.orphaned_data.mutex_get());
|
||||
device.orphaned_data.move_data(device.orphaned_data_render,
|
||||
device.orphaned_data.timeline_ + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void VKBackend::render_step(bool force_resource_release)
|
||||
{
|
||||
if (force_resource_release) {
|
||||
std::scoped_lock lock(device.orphaned_data.mutex_get());
|
||||
device.orphaned_data.move_data(device.orphaned_data_render,
|
||||
device.orphaned_data.timeline_ + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void VKBackend::capabilities_init(VKDevice &device)
|
||||
{
|
||||
const VkPhysicalDeviceProperties &properties = device.physical_device_properties_get();
|
||||
const VkPhysicalDeviceLimits &limits = properties.limits;
|
||||
|
||||
/* Reset all capabilities from previous context. */
|
||||
GCaps = {};
|
||||
GCaps.geometry_shader_support = true;
|
||||
GCaps.stencil_export_support = device.supports_extension(
|
||||
VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME);
|
||||
|
||||
GCaps.max_texture_size = max_ii(limits.maxImageDimension1D, limits.maxImageDimension2D);
|
||||
GCaps.max_texture_3d_size = min_uu(limits.maxImageDimension3D, INT_MAX);
|
||||
GCaps.max_buffer_texture_size = min_uu(limits.maxTexelBufferElements, UINT_MAX);
|
||||
GCaps.max_texture_layers = min_uu(limits.maxImageArrayLayers, INT_MAX);
|
||||
GCaps.max_textures = min_uu(limits.maxPerStageDescriptorSampledImages, INT_MAX);
|
||||
GCaps.max_images = min_uu(limits.maxPerStageDescriptorStorageImages, INT_MAX);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
GCaps.max_work_group_count[i] = min_uu(limits.maxComputeWorkGroupCount[i], INT_MAX);
|
||||
GCaps.max_work_group_size[i] = min_uu(limits.maxComputeWorkGroupSize[i], INT_MAX);
|
||||
}
|
||||
GCaps.max_uniforms_vert = GCaps.max_uniforms_frag = min_uu(
|
||||
limits.maxPerStageDescriptorUniformBuffers, INT_MAX);
|
||||
GCaps.max_batch_indices = min_uu(limits.maxDrawIndirectCount, INT_MAX);
|
||||
GCaps.max_batch_vertices = min_uu(limits.maxDrawIndexedIndexValue, INT_MAX);
|
||||
GCaps.max_vertex_attribs = min_uu(limits.maxVertexInputAttributes, INT_MAX);
|
||||
GCaps.max_varying_floats = min_uu(limits.maxVertexOutputComponents, INT_MAX);
|
||||
GCaps.max_shader_storage_buffer_bindings = GCaps.max_compute_shader_storage_blocks = min_uu(
|
||||
limits.maxPerStageDescriptorStorageBuffers, INT_MAX);
|
||||
GCaps.max_uniform_buffer_size = size_t(limits.maxUniformBufferRange);
|
||||
GCaps.max_storage_buffer_size = size_t(limits.maxStorageBufferRange);
|
||||
GCaps.storage_buffer_alignment = limits.minStorageBufferOffsetAlignment;
|
||||
|
||||
GCaps.max_parallel_compilations = BLI_system_thread_count();
|
||||
GCaps.mem_stats_support = true;
|
||||
|
||||
uint32_t vk_extension_count;
|
||||
vkEnumerateDeviceExtensionProperties(
|
||||
device.physical_device_get(), nullptr, &vk_extension_count, nullptr);
|
||||
GCaps.extensions_len = vk_extension_count;
|
||||
GCaps.extension_get = vk_extension_get;
|
||||
|
||||
detect_workarounds(device);
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
119
blender-5.2.0/source/blender/gpu/vulkan/vk_backend.hh
Normal file
119
blender-5.2.0/source/blender/gpu/vulkan/vk_backend.hh
Normal file
@@ -0,0 +1,119 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "gpu_backend.hh"
|
||||
|
||||
#ifdef WITH_RENDERDOC
|
||||
# include "renderdoc_api.hh"
|
||||
#endif
|
||||
|
||||
#include "vk_common.hh"
|
||||
#include "vk_device.hh"
|
||||
#include "vk_shader_compiler.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class VKContext;
|
||||
class VKDescriptorSet;
|
||||
class VKDescriptorSetTracker;
|
||||
|
||||
class VKBackend : public GPUBackend {
|
||||
private:
|
||||
#ifdef WITH_RENDERDOC
|
||||
renderdoc::api::Renderdoc renderdoc_api_;
|
||||
#endif
|
||||
|
||||
public:
|
||||
/* Global instance to device handles. */
|
||||
VKDevice device;
|
||||
|
||||
VKBackend()
|
||||
{
|
||||
platform_init();
|
||||
}
|
||||
|
||||
virtual ~VKBackend()
|
||||
{
|
||||
VKBackend::platform_exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the running platform contain any device that meets the minimum requirements to start the
|
||||
* Vulkan backend.
|
||||
*
|
||||
* Function is used to validate that a Blender UI can be started. It calls vulkan API commands
|
||||
* directly to ensure no parts of Blender needs to be initialized.
|
||||
*/
|
||||
static bool is_supported();
|
||||
|
||||
/**
|
||||
* Print one line per Vulkan device that meets minimum requirements, in the format used by
|
||||
* `--gpu-device help`. Each line is `<vendor-hex>/<device-hex>/<index> <name>`.
|
||||
* Operates without an active backend (creates a temporary Vulkan instance).
|
||||
*/
|
||||
static void supported_devices_print(FILE *fp);
|
||||
|
||||
void init_resources() override;
|
||||
void delete_resources() override;
|
||||
|
||||
void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) override;
|
||||
void compute_dispatch_indirect(StorageBuf *indirect_buf) override;
|
||||
|
||||
Context *context_alloc(GHOST_IWindow *ghost_window, GHOST_IContext *ghost_context) override;
|
||||
|
||||
Batch *batch_alloc() override;
|
||||
Fence *fence_alloc() override;
|
||||
FrameBuffer *framebuffer_alloc(const char *name) override;
|
||||
IndexBuf *indexbuf_alloc() override;
|
||||
PixelBuffer *pixelbuf_alloc(size_t size) override;
|
||||
QueryPool *querypool_alloc() override;
|
||||
Shader *shader_alloc(const char *name) override;
|
||||
Texture *texture_alloc(const char *name) override;
|
||||
TexturePool *texturepool_alloc() override;
|
||||
UniformBuf *uniformbuf_alloc(size_t size, const char *name) override;
|
||||
StorageBuf *storagebuf_alloc(size_t size, GPUUsageType usage, const char *name) override;
|
||||
VertBuf *vertbuf_alloc() override;
|
||||
|
||||
void shader_cache_dir_clear_old() override
|
||||
{
|
||||
VKShaderCompiler::cache_dir_clear_old();
|
||||
}
|
||||
|
||||
/* Render Frame Coordination --
|
||||
* Used for performing per-frame actions globally */
|
||||
void render_begin() override;
|
||||
void render_end() override;
|
||||
void render_step(bool /*force_resource_release*/) override;
|
||||
|
||||
bool debug_capture_begin(const char *title);
|
||||
void debug_capture_end();
|
||||
|
||||
static VKBackend &get()
|
||||
{
|
||||
return *static_cast<VKBackend *>(GPUBackend::get());
|
||||
}
|
||||
|
||||
static void platform_init(const VKDevice &device);
|
||||
static void capabilities_init(VKDevice &device);
|
||||
|
||||
private:
|
||||
static void detect_workarounds(VKDevice &device);
|
||||
static void platform_init();
|
||||
static void platform_exit();
|
||||
|
||||
/* These classes are allowed to modify the global device. */
|
||||
friend class VKContext;
|
||||
friend class VKDescriptorSet;
|
||||
friend class VKDescriptorSetTracker;
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
132
blender-5.2.0/source/blender/gpu/vulkan/vk_batch.cc
Normal file
132
blender-5.2.0/source/blender/gpu/vulkan/vk_batch.cc
Normal file
@@ -0,0 +1,132 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "vk_batch.hh"
|
||||
|
||||
#include "render_graph/nodes/vk_pipeline_data.hh"
|
||||
#include "vk_context.hh"
|
||||
#include "vk_framebuffer.hh"
|
||||
#include "vk_index_buffer.hh"
|
||||
#include "vk_state_manager.hh"
|
||||
#include "vk_storage_buffer.hh"
|
||||
#include "vk_vertex_attribute_object.hh"
|
||||
#include "vk_vertex_buffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
void VKBatch::draw(int vertex_first, int vertex_count, int instance_first, int instance_count)
|
||||
{
|
||||
VKContext &context = *VKContext::get();
|
||||
render_graph::VKResourceAccessInfo &resource_access_info = context.reset_and_get_access_info();
|
||||
VKVertexAttributeObject vao;
|
||||
vao.update_bindings(context, *this);
|
||||
|
||||
VKIndexBuffer *index_buffer = index_buffer_get();
|
||||
const bool draw_indexed = index_buffer != nullptr;
|
||||
|
||||
/* Upload geometry */
|
||||
if (draw_indexed) {
|
||||
index_buffer->upload_data();
|
||||
}
|
||||
VKFrameBuffer &framebuffer = *context.active_framebuffer_get();
|
||||
framebuffer.rendering_ensure(context);
|
||||
|
||||
if (draw_indexed) {
|
||||
render_graph::VKDrawIndexedNode::CreateInfo draw_indexed(resource_access_info);
|
||||
draw_indexed.node_data.index_count = vertex_count;
|
||||
draw_indexed.node_data.instance_count = instance_count;
|
||||
draw_indexed.node_data.first_index = index_buffer->index_start_get() + vertex_first;
|
||||
draw_indexed.node_data.vertex_offset = index_buffer->index_base_get();
|
||||
draw_indexed.node_data.first_instance = instance_first;
|
||||
|
||||
draw_indexed.node_data.index_buffer.buffer = index_buffer->vk_handle();
|
||||
draw_indexed.node_data.index_buffer.index_type = index_buffer->vk_index_type();
|
||||
vao.bind(draw_indexed.node_data.vertex_buffers);
|
||||
context.update_pipeline_data(framebuffer, prim_type, vao, draw_indexed.node_data.graphics);
|
||||
|
||||
context.render_graph().add_node(draw_indexed);
|
||||
}
|
||||
else {
|
||||
render_graph::VKDrawNode::CreateInfo draw(resource_access_info);
|
||||
draw.node_data.vertex_count = vertex_count;
|
||||
draw.node_data.instance_count = instance_count;
|
||||
draw.node_data.first_vertex = vertex_first;
|
||||
draw.node_data.first_instance = instance_first;
|
||||
|
||||
vao.bind(draw.node_data.vertex_buffers);
|
||||
context.update_pipeline_data(framebuffer, prim_type, vao, draw.node_data.graphics);
|
||||
|
||||
context.render_graph().add_node(draw);
|
||||
}
|
||||
}
|
||||
|
||||
void VKBatch::draw_indirect(StorageBuf *indirect_buf, intptr_t offset)
|
||||
{
|
||||
multi_draw_indirect(indirect_buf, 1, offset, 0);
|
||||
}
|
||||
|
||||
void VKBatch::multi_draw_indirect(StorageBuf *indirect_buf,
|
||||
const int count,
|
||||
const intptr_t offset,
|
||||
const intptr_t stride)
|
||||
{
|
||||
VKStorageBuffer &indirect_buffer = *unwrap(unwrap(indirect_buf));
|
||||
multi_draw_indirect(indirect_buffer.vk_handle(), count, offset, stride);
|
||||
}
|
||||
|
||||
void VKBatch::multi_draw_indirect(const VkBuffer indirect_buffer,
|
||||
const int count,
|
||||
const intptr_t offset,
|
||||
const intptr_t stride)
|
||||
{
|
||||
VKContext &context = *VKContext::get();
|
||||
render_graph::VKResourceAccessInfo &resource_access_info = context.reset_and_get_access_info();
|
||||
VKVertexAttributeObject vao;
|
||||
vao.update_bindings(context, *this);
|
||||
|
||||
VKIndexBuffer *index_buffer = index_buffer_get();
|
||||
const bool draw_indexed = index_buffer != nullptr;
|
||||
|
||||
/* Upload geometry */
|
||||
if (draw_indexed) {
|
||||
index_buffer->upload_data();
|
||||
}
|
||||
VKFrameBuffer &framebuffer = *context.active_framebuffer_get();
|
||||
framebuffer.rendering_ensure(context);
|
||||
|
||||
if (draw_indexed) {
|
||||
render_graph::VKDrawIndexedIndirectNode::CreateInfo draw_indexed_indirect(
|
||||
resource_access_info);
|
||||
draw_indexed_indirect.node_data.indirect_buffer = indirect_buffer;
|
||||
draw_indexed_indirect.node_data.offset = offset;
|
||||
draw_indexed_indirect.node_data.draw_count = count;
|
||||
draw_indexed_indirect.node_data.stride = stride;
|
||||
|
||||
draw_indexed_indirect.node_data.index_buffer.buffer = index_buffer->vk_handle();
|
||||
draw_indexed_indirect.node_data.index_buffer.index_type = index_buffer->vk_index_type();
|
||||
vao.bind(draw_indexed_indirect.node_data.vertex_buffers);
|
||||
context.update_pipeline_data(
|
||||
framebuffer, prim_type, vao, draw_indexed_indirect.node_data.graphics);
|
||||
|
||||
context.render_graph().add_node(draw_indexed_indirect);
|
||||
}
|
||||
else {
|
||||
render_graph::VKDrawIndirectNode::CreateInfo draw(resource_access_info);
|
||||
draw.node_data.indirect_buffer = indirect_buffer;
|
||||
draw.node_data.offset = offset;
|
||||
draw.node_data.draw_count = count;
|
||||
draw.node_data.stride = stride;
|
||||
|
||||
vao.bind(draw.node_data.vertex_buffers);
|
||||
context.update_pipeline_data(framebuffer, prim_type, vao, draw.node_data.graphics);
|
||||
|
||||
context.render_graph().add_node(draw);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
46
blender-5.2.0/source/blender/gpu/vulkan/vk_batch.hh
Normal file
46
blender-5.2.0/source/blender/gpu/vulkan/vk_batch.hh
Normal file
@@ -0,0 +1,46 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vk_index_buffer.hh"
|
||||
#include "vk_vertex_buffer.hh"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
class VKBatch : public Batch {
|
||||
public:
|
||||
void draw(int vertex_first, int vertex_count, int instance_first, int instance_count) override;
|
||||
void draw_indirect(StorageBuf *indirect_buf, intptr_t offset) override;
|
||||
void multi_draw_indirect(StorageBuf *indirect_buf,
|
||||
int count,
|
||||
intptr_t offset,
|
||||
intptr_t stride) override;
|
||||
void multi_draw_indirect(VkBuffer indirect_buf, int count, intptr_t offset, intptr_t stride);
|
||||
|
||||
VKVertexBuffer *vertex_buffer_get(int index);
|
||||
VKIndexBuffer *index_buffer_get();
|
||||
};
|
||||
|
||||
inline VKBatch *unwrap(Batch *batch)
|
||||
{
|
||||
return static_cast<VKBatch *>(batch);
|
||||
}
|
||||
|
||||
inline VKVertexBuffer *VKBatch::vertex_buffer_get(int index)
|
||||
{
|
||||
return unwrap(verts_(index));
|
||||
}
|
||||
|
||||
inline VKIndexBuffer *VKBatch::index_buffer_get()
|
||||
{
|
||||
return unwrap(unwrap(elem));
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
280
blender-5.2.0/source/blender/gpu/vulkan/vk_buffer.cc
Normal file
280
blender-5.2.0/source/blender/gpu/vulkan/vk_buffer.cc
Normal file
@@ -0,0 +1,280 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "vk_buffer.hh"
|
||||
#include "vk_backend.hh"
|
||||
#include "vk_context.hh"
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static CLG_LogRef LOG = {"gpu.vulkan"};
|
||||
|
||||
namespace gpu {
|
||||
|
||||
VKBuffer::~VKBuffer()
|
||||
{
|
||||
if (is_allocated()) {
|
||||
free();
|
||||
}
|
||||
}
|
||||
|
||||
bool VKBuffer::create(size_t size_in_bytes,
|
||||
VkBufferUsageFlags buffer_usage,
|
||||
VmaMemoryUsage vma_memory_usage,
|
||||
VmaAllocationCreateFlags allocation_flags,
|
||||
float priority,
|
||||
bool export_memory,
|
||||
const char *debug_name)
|
||||
{
|
||||
BLI_assert(!is_allocated());
|
||||
BLI_assert(vk_buffer_ == VK_NULL_HANDLE);
|
||||
BLI_assert(mapped_memory_ == nullptr);
|
||||
if (allocation_failed_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_in_bytes_ = size_in_bytes;
|
||||
/*
|
||||
* Vulkan doesn't allow empty buffers but some areas (DrawManager Instance data, PyGPU) create
|
||||
* them.
|
||||
*/
|
||||
alloc_size_in_bytes_ = ceil_to_multiple_ul(max_ulul(size_in_bytes_, 16), 16);
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
|
||||
/* Precheck max buffer size. */
|
||||
if (device.extensions_get().maintenance4 &&
|
||||
alloc_size_in_bytes_ > device.physical_device_maintenance4_properties_get().maxBufferSize)
|
||||
{
|
||||
CLOG_WARN(
|
||||
&LOG,
|
||||
"Couldn't allocate buffer, requested allocation exceeds the maxBufferSize of the device.");
|
||||
allocation_failed_ = true;
|
||||
size_in_bytes_ = 0;
|
||||
alloc_size_in_bytes_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
VmaAllocator allocator = device.mem_allocator_get();
|
||||
VkBufferCreateInfo create_info = {};
|
||||
create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
create_info.flags = 0;
|
||||
create_info.size = alloc_size_in_bytes_;
|
||||
create_info.usage = buffer_usage;
|
||||
/* We use the same command queue for the compute and graphics pipeline, so it is safe to use
|
||||
* exclusive resource handling. */
|
||||
create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
create_info.queueFamilyIndexCount = 1;
|
||||
const uint32_t queue_family_indices[1] = {device.queue_family_get()};
|
||||
create_info.pQueueFamilyIndices = queue_family_indices;
|
||||
|
||||
VkExternalMemoryBufferCreateInfo external_memory_create_info = {
|
||||
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO, nullptr, 0};
|
||||
|
||||
VmaAllocationCreateInfo vma_create_info = {};
|
||||
vma_create_info.flags = allocation_flags;
|
||||
vma_create_info.priority = priority;
|
||||
vma_create_info.usage = vma_memory_usage;
|
||||
|
||||
if (export_memory) {
|
||||
create_info.pNext = &external_memory_create_info;
|
||||
external_memory_create_info.handleTypes = vk_external_memory_handle_type();
|
||||
|
||||
/* Dedicated allocation for zero offset. */
|
||||
vma_create_info.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
|
||||
vma_create_info.pool = device.vma_pools.external_memory_pixel_buffer.pool;
|
||||
}
|
||||
|
||||
if (debug_name && G.debug & G_DEBUG_GPU) {
|
||||
vma_create_info.flags |= VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
|
||||
vma_create_info.pUserData = (void *)debug_name;
|
||||
}
|
||||
|
||||
VkResult result = vmaCreateBuffer(
|
||||
allocator, &create_info, &vma_create_info, &vk_buffer_, &allocation_, nullptr);
|
||||
if (result != VK_SUCCESS) {
|
||||
allocation_failed_ = true;
|
||||
size_in_bytes_ = 0;
|
||||
alloc_size_in_bytes_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
device.resources.add_buffer(vk_buffer_);
|
||||
|
||||
if (debug_name) {
|
||||
debug::object_label(vk_buffer_, debug_name);
|
||||
}
|
||||
|
||||
/* Check if the memory is mappable. Although the Vulkan specs allow to map any memory that is
|
||||
* host visible, VMA checks for specific host access flags.
|
||||
*
|
||||
* Source:
|
||||
* https://gpuopen-librariesandsdks.github.io/VulkanMemoryAllocator/html/memory_mapping.html
|
||||
*/
|
||||
const bool is_mappable = bool(allocation_flags &
|
||||
(VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT |
|
||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT));
|
||||
if (is_mappable) {
|
||||
return map();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VKBuffer::update_immediately(const void *data) const
|
||||
{
|
||||
update_sub_immediately(0, size_in_bytes_, data);
|
||||
}
|
||||
|
||||
void VKBuffer::update_sub_immediately(size_t start_offset,
|
||||
size_t data_size,
|
||||
const void *data) const
|
||||
{
|
||||
BLI_assert_msg(is_mapped(), "Cannot update a non-mapped buffer.");
|
||||
memcpy(static_cast<uint8_t *>(mapped_memory_) + start_offset, data, data_size);
|
||||
}
|
||||
|
||||
void VKBuffer::update_render_graph(VKContext &context, void *data) const
|
||||
{
|
||||
BLI_assert(size_in_bytes_ <= 65536 && size_in_bytes_ % 4 == 0);
|
||||
render_graph::VKUpdateBufferNode::CreateInfo update_buffer = {};
|
||||
update_buffer.dst_buffer = vk_buffer_;
|
||||
update_buffer.data_size = size_in_bytes_;
|
||||
update_buffer.data = data;
|
||||
context.render_graph().add_node(update_buffer);
|
||||
}
|
||||
|
||||
void VKBuffer::flush() const
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
VmaAllocator allocator = device.mem_allocator_get();
|
||||
vmaFlushAllocation(allocator, allocation_, 0, max_ulul(size_in_bytes(), 1));
|
||||
}
|
||||
|
||||
void VKBuffer::clear(VKContext &context, uint32_t clear_value)
|
||||
{
|
||||
render_graph::VKFillBufferNode::CreateInfo fill_buffer = {};
|
||||
fill_buffer.vk_buffer = vk_buffer_;
|
||||
fill_buffer.data = clear_value;
|
||||
fill_buffer.size = alloc_size_in_bytes_;
|
||||
context.render_graph().add_node(fill_buffer);
|
||||
}
|
||||
|
||||
void VKBuffer::async_flush_to_host(VKContext &context)
|
||||
{
|
||||
BLI_assert(async_timeline_ == 0);
|
||||
context.rendering_end();
|
||||
async_timeline_ = context.flush_render_graph(RenderGraphFlushFlags::SUBMIT |
|
||||
RenderGraphFlushFlags::RENEW_RENDER_GRAPH);
|
||||
}
|
||||
|
||||
void VKBuffer::read_async(VKContext &context, void *data)
|
||||
{
|
||||
BLI_assert_msg(is_mapped(), "Cannot read a non-mapped buffer.");
|
||||
if (async_timeline_ == 0) {
|
||||
async_flush_to_host(context);
|
||||
}
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
device.wait_for_timeline(async_timeline_);
|
||||
async_timeline_ = 0;
|
||||
memcpy(data, mapped_memory_, size_in_bytes_);
|
||||
}
|
||||
|
||||
void VKBuffer::read(VKContext &context, void *data) const
|
||||
{
|
||||
|
||||
BLI_assert_msg(is_mapped(), "Cannot read a non-mapped buffer.");
|
||||
BLI_assert(async_timeline_ == 0);
|
||||
context.rendering_end();
|
||||
context.flush_render_graph(RenderGraphFlushFlags::SUBMIT |
|
||||
RenderGraphFlushFlags::WAIT_FOR_COMPLETION |
|
||||
RenderGraphFlushFlags::RENEW_RENDER_GRAPH);
|
||||
memcpy(data, mapped_memory_, size_in_bytes_);
|
||||
}
|
||||
|
||||
bool VKBuffer::map()
|
||||
{
|
||||
BLI_assert(!is_mapped());
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
VmaAllocator allocator = device.mem_allocator_get();
|
||||
VkResult result = vmaMapMemory(allocator, allocation_, &mapped_memory_);
|
||||
return result == VK_SUCCESS;
|
||||
}
|
||||
|
||||
void VKBuffer::unmap()
|
||||
{
|
||||
BLI_assert(is_mapped());
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
VmaAllocator allocator = device.mem_allocator_get();
|
||||
vmaUnmapMemory(allocator, allocation_);
|
||||
mapped_memory_ = nullptr;
|
||||
}
|
||||
|
||||
VkDeviceMemory VKBuffer::export_memory_get(size_t &memory_size)
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
VmaAllocator allocator = device.mem_allocator_get();
|
||||
|
||||
VmaAllocationInfo info = {};
|
||||
vmaGetAllocationInfo(allocator, allocation_, &info);
|
||||
|
||||
/* VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT should ensure this. */
|
||||
if (info.offset != 0) {
|
||||
BLI_assert(!"Failed to get zero offset export memory for Vulkan buffer");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
memory_size = info.size;
|
||||
return info.deviceMemory;
|
||||
}
|
||||
|
||||
void VKBuffer::flush_mapped_memory()
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
VmaAllocator allocator = device.mem_allocator_get();
|
||||
vmaFlushAllocation(allocator, allocation_, 0, size_in_bytes_);
|
||||
}
|
||||
|
||||
void VKBuffer::invalidate_mapped_memory()
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
VmaAllocator allocator = device.mem_allocator_get();
|
||||
vmaInvalidateAllocation(allocator, allocation_, 0, size_in_bytes_);
|
||||
}
|
||||
|
||||
bool VKBuffer::free()
|
||||
{
|
||||
if (is_mapped()) {
|
||||
unmap();
|
||||
}
|
||||
|
||||
VKDiscardPool::discard_pool_get().discard_buffer(vk_buffer_, allocation_);
|
||||
|
||||
allocation_ = VK_NULL_HANDLE;
|
||||
vk_buffer_ = VK_NULL_HANDLE;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VKBuffer::free_immediately(VKDevice &device)
|
||||
{
|
||||
BLI_assert(vk_buffer_ != VK_NULL_HANDLE);
|
||||
BLI_assert(allocation_ != VK_NULL_HANDLE);
|
||||
if (is_mapped()) {
|
||||
unmap();
|
||||
}
|
||||
device.resources.remove_buffer(vk_buffer_);
|
||||
vmaDestroyBuffer(device.mem_allocator_get(), vk_buffer_, allocation_);
|
||||
allocation_ = VK_NULL_HANDLE;
|
||||
vk_buffer_ = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
177
blender-5.2.0/source/blender/gpu/vulkan/vk_buffer.hh
Normal file
177
blender-5.2.0/source/blender/gpu/vulkan/vk_buffer.hh
Normal file
@@ -0,0 +1,177 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_context_private.hh"
|
||||
|
||||
#include "BLI_utility_mixins.hh"
|
||||
#include "vk_common.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
class VKContext;
|
||||
class VKDevice;
|
||||
|
||||
/**
|
||||
* Class for handing vulkan buffers (allocation/updating/binding).
|
||||
*/
|
||||
class VKBuffer : public NonCopyable {
|
||||
size_t size_in_bytes_ = 0;
|
||||
size_t alloc_size_in_bytes_ = 0;
|
||||
VkBuffer vk_buffer_ = VK_NULL_HANDLE;
|
||||
VmaAllocation allocation_ = VK_NULL_HANDLE;
|
||||
TimelineValue async_timeline_ = 0;
|
||||
/** Has a previous allocation failed. Will skip reallocations. */
|
||||
bool allocation_failed_ = false;
|
||||
|
||||
/* Pointer to the virtually mapped memory. */
|
||||
void *mapped_memory_ = nullptr;
|
||||
|
||||
VkDeviceAddress vk_device_address = 0;
|
||||
|
||||
public:
|
||||
VKBuffer() = default;
|
||||
virtual ~VKBuffer();
|
||||
|
||||
/** Has this buffer been allocated? */
|
||||
bool is_allocated() const;
|
||||
|
||||
/**
|
||||
* Allocate the buffer.
|
||||
*/
|
||||
bool create(size_t size,
|
||||
VkBufferUsageFlags buffer_usage,
|
||||
VmaMemoryUsage vma_memory_usage,
|
||||
VmaAllocationCreateFlags vma_allocation_flags,
|
||||
float priority,
|
||||
bool export_memory = false,
|
||||
const char *debug_name = "VKBuffer");
|
||||
void clear(VKContext &context, uint32_t clear_value);
|
||||
void update_immediately(const void *data) const;
|
||||
void update_sub_immediately(size_t start_offset, size_t data_size, const void *data) const;
|
||||
|
||||
/**
|
||||
* Update the buffer as part of the render graph evaluation. The ownership of data will be
|
||||
* transferred to the render graph and should have been allocated using guarded alloc.
|
||||
*/
|
||||
void update_render_graph(VKContext &context, void *data) const;
|
||||
void flush() const;
|
||||
|
||||
/**
|
||||
* Read the buffer (synchronously).
|
||||
*/
|
||||
void read(VKContext &context, void *data) const;
|
||||
|
||||
/**
|
||||
* Start a async read-back.
|
||||
*/
|
||||
void async_flush_to_host(VKContext &context);
|
||||
|
||||
/**
|
||||
* Wait until the async read back is finished and fill the given data with the content of the
|
||||
* buffer.
|
||||
*
|
||||
* Will start a new async read-back when there is no read back in progress.
|
||||
*/
|
||||
void read_async(VKContext &context, void *data);
|
||||
|
||||
/**
|
||||
* Free the buffer.
|
||||
*
|
||||
* Discards the buffer so it can be destroyed safely later. Buffers can still be used when
|
||||
* rendering so we can only destroy them after the rendering is completed.
|
||||
*/
|
||||
bool free();
|
||||
|
||||
/**
|
||||
* Destroy the buffer immediately.
|
||||
*/
|
||||
void free_immediately(VKDevice &device);
|
||||
|
||||
int64_t size_in_bytes() const
|
||||
{
|
||||
return size_in_bytes_;
|
||||
}
|
||||
|
||||
inline int64_t allocated_size_in_bytes() const
|
||||
{
|
||||
return alloc_size_in_bytes_;
|
||||
}
|
||||
|
||||
VkBuffer vk_handle() const
|
||||
{
|
||||
return vk_buffer_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reference to the mapped memory.
|
||||
*
|
||||
* Can only be called when the buffer is (still) mapped.
|
||||
*/
|
||||
void *mapped_memory_get() const;
|
||||
|
||||
VkDeviceAddress device_address_get() const
|
||||
{
|
||||
return vk_device_address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this buffer mapped (visible on host)
|
||||
*/
|
||||
bool is_mapped() const;
|
||||
|
||||
/**
|
||||
* Get allocated device memory.
|
||||
*/
|
||||
VkDeviceMemory export_memory_get(size_t &memory_size);
|
||||
|
||||
/**
|
||||
* Flush the mapped memory after writing to it on the host. Only has an effect if the memory is
|
||||
* not host coherent.
|
||||
*/
|
||||
void flush_mapped_memory();
|
||||
|
||||
/**
|
||||
* Invalidates the mapped memory after writing to it on the device. Only has an effect if the
|
||||
* memory is not host coherent.
|
||||
*/
|
||||
void invalidate_mapped_memory();
|
||||
|
||||
private:
|
||||
/** Check if this buffer is mapped. */
|
||||
bool map();
|
||||
void unmap();
|
||||
};
|
||||
|
||||
inline void *VKBuffer::mapped_memory_get() const
|
||||
{
|
||||
BLI_assert_msg(this->is_mapped(), "Cannot access a non-mapped buffer.");
|
||||
return mapped_memory_;
|
||||
}
|
||||
|
||||
inline bool VKBuffer::is_mapped() const
|
||||
{
|
||||
return mapped_memory_ != nullptr;
|
||||
}
|
||||
|
||||
inline bool VKBuffer::is_allocated() const
|
||||
{
|
||||
return allocation_ != VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper struct to enable buffers to be bound with an offset.
|
||||
*
|
||||
* Used for de-interleaved vertex input buffers and immediate mode buffers.
|
||||
*/
|
||||
struct VKBufferWithOffset {
|
||||
VkBuffer buffer;
|
||||
VkDeviceSize offset;
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
92
blender-5.2.0/source/blender/gpu/vulkan/vk_buffer_pool.cc
Normal file
92
blender-5.2.0/source/blender/gpu/vulkan/vk_buffer_pool.cc
Normal file
@@ -0,0 +1,92 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
|
||||
#include "vk_buffer.hh"
|
||||
#include "vk_buffer_pool.hh"
|
||||
#include "vk_context.hh"
|
||||
#include "vk_debug.hh"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
VKBufferPool::~VKBufferPool()
|
||||
{
|
||||
BLI_assert(buffers_.is_empty());
|
||||
}
|
||||
|
||||
VKBufferWithOffset VKBufferPool::append(Span<uint8_t> data)
|
||||
{
|
||||
const bool should_allocate_new_buffer = buffers_.is_empty() ||
|
||||
(buffers_.last()->size_in_bytes() - buffer_offset_) <
|
||||
data.size();
|
||||
/* Flush last buffer as all data is available. */
|
||||
if (should_allocate_new_buffer && !buffers_.is_empty() && buffer_offset_ != 0) {
|
||||
finalize_active_buffer();
|
||||
}
|
||||
if (should_allocate_new_buffer) {
|
||||
VkDeviceSize allocation_size = std::max(VkDeviceSize(data.size()), default_buffer_size_);
|
||||
std::unique_ptr<VKBuffer> new_buffer = std::make_unique<VKBuffer>();
|
||||
new_buffer->create(allocation_size,
|
||||
vk_buffer_usage_,
|
||||
vma_memory_usage_,
|
||||
vma_allocation_create_flags_,
|
||||
priority_,
|
||||
false,
|
||||
name_.c_str());
|
||||
debug::object_label(new_buffer->vk_handle(), name_);
|
||||
buffers_.append(std::move(new_buffer));
|
||||
buffer_offset_ = 0;
|
||||
}
|
||||
VKBufferWithOffset result = {.buffer = buffers_.last()->vk_handle(), .offset = buffer_offset_};
|
||||
|
||||
/* Resize data_ when more space is needed than the default_buffer_size_. */
|
||||
if (buffer_offset_ + data.size() > data_.size()) {
|
||||
data_.resize(buffer_offset_ + data.size());
|
||||
}
|
||||
data_.as_mutable_span()
|
||||
.slice(IndexRange::from_begin_size(buffer_offset_, data.size()))
|
||||
.copy_from(data);
|
||||
|
||||
buffer_offset_ += data.size();
|
||||
buffer_offset_ = ceil_to_multiple_ul(buffer_offset_, alignment_);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void VKBufferPool::ensure_uploaded()
|
||||
{
|
||||
if (!buffers_.is_empty() && buffer_offset_ != 0) {
|
||||
finalize_active_buffer();
|
||||
}
|
||||
}
|
||||
|
||||
void VKBufferPool::discard()
|
||||
{
|
||||
buffers_.clear();
|
||||
buffer_offset_ = 0;
|
||||
}
|
||||
|
||||
void VKBufferPool::finalize_active_buffer()
|
||||
{
|
||||
BLI_assert(!buffers_.is_empty());
|
||||
std::unique_ptr<VKBuffer> &active_buffer = buffers_.last();
|
||||
if (active_buffer->is_mapped()) {
|
||||
active_buffer->update_sub_immediately(0, buffer_offset_, data_.data());
|
||||
}
|
||||
else {
|
||||
VKContext &context = *VKContext::get();
|
||||
void *data_copy = MEM_new_uninitialized(active_buffer->allocated_size_in_bytes(), __func__);
|
||||
memcpy(data_copy, data_.data(), std::min(data_.size(), int64_t(buffer_offset_)));
|
||||
active_buffer->update_render_graph(context, data_copy);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
78
blender-5.2.0/source/blender/gpu/vulkan/vk_buffer_pool.hh
Normal file
78
blender-5.2.0/source/blender/gpu/vulkan/vk_buffer_pool.hh
Normal file
@@ -0,0 +1,78 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "vk_buffer.hh"
|
||||
#include "vk_common.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
/**
|
||||
* \brief A pool of VkBuffers.
|
||||
*/
|
||||
class VKBufferPool {
|
||||
/**
|
||||
* All allocated buffers in the pool
|
||||
*/
|
||||
Vector<std::unique_ptr<VKBuffer>> buffers_;
|
||||
StringRefNull name_;
|
||||
VkDeviceSize buffer_offset_;
|
||||
VkDeviceSize default_buffer_size_;
|
||||
VkDeviceSize alignment_;
|
||||
Vector<uint8_t> data_;
|
||||
VkBufferUsageFlags vk_buffer_usage_;
|
||||
VmaMemoryUsage vma_memory_usage_;
|
||||
VmaAllocationCreateFlags vma_allocation_create_flags_;
|
||||
float priority_;
|
||||
|
||||
public:
|
||||
VKBufferPool(VKBufferPool &&other) = default;
|
||||
VKBufferPool(StringRefNull name,
|
||||
VkDeviceSize default_buffer_size,
|
||||
VkDeviceSize alignment,
|
||||
VkBufferUsageFlags vk_buffer_usage,
|
||||
VmaMemoryUsage vma_memory_usage,
|
||||
VmaAllocationCreateFlags vma_allocation_create_flags,
|
||||
float priority = 1.0f)
|
||||
: name_(name),
|
||||
default_buffer_size_(default_buffer_size),
|
||||
alignment_(alignment),
|
||||
vk_buffer_usage_(vk_buffer_usage),
|
||||
vma_memory_usage_(vma_memory_usage),
|
||||
vma_allocation_create_flags_(vma_allocation_create_flags),
|
||||
priority_(priority)
|
||||
{
|
||||
data_.resize(default_buffer_size_);
|
||||
}
|
||||
virtual ~VKBufferPool();
|
||||
|
||||
/**
|
||||
* \brief Append the given data.
|
||||
*
|
||||
* \returns the buffer handle and offset in the buffer where the data is stored.
|
||||
* \note data might not be available in the buffer yet. Use `ensure_uploaded` to ensure that all
|
||||
* data is available by the GPU.
|
||||
*/
|
||||
VKBufferWithOffset append(Span<uint8_t> data);
|
||||
|
||||
/**
|
||||
* \brief Ensure that all the data is uploaded.
|
||||
*/
|
||||
void ensure_uploaded();
|
||||
|
||||
/**
|
||||
* \brief Discard the current buffers to the discard pool.
|
||||
*/
|
||||
void discard();
|
||||
|
||||
private:
|
||||
void finalize_active_buffer();
|
||||
};
|
||||
} // namespace blender::gpu
|
||||
906
blender-5.2.0/source/blender/gpu/vulkan/vk_common.cc
Normal file
906
blender-5.2.0/source/blender/gpu/vulkan/vk_common.cc
Normal file
@@ -0,0 +1,906 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "vk_backend.hh"
|
||||
#include "vk_common.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
VkImageAspectFlags to_vk_image_aspect_flag_bits(const TextureFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
/* Formats texture & render-buffer */
|
||||
case TextureFormat::UINT_32_32_32_32:
|
||||
case TextureFormat::UINT_32_32:
|
||||
case TextureFormat::UINT_32:
|
||||
case TextureFormat::UINT_16_16_16_16:
|
||||
case TextureFormat::UINT_16_16:
|
||||
case TextureFormat::UINT_16:
|
||||
case TextureFormat::UINT_8_8_8_8:
|
||||
case TextureFormat::UINT_8_8:
|
||||
case TextureFormat::UINT_8:
|
||||
case TextureFormat::SINT_32_32_32_32:
|
||||
case TextureFormat::SINT_32_32:
|
||||
case TextureFormat::SINT_32:
|
||||
case TextureFormat::SINT_16_16_16_16:
|
||||
case TextureFormat::SINT_16_16:
|
||||
case TextureFormat::SINT_16:
|
||||
case TextureFormat::SINT_8_8_8_8:
|
||||
case TextureFormat::SINT_8_8:
|
||||
case TextureFormat::SINT_8:
|
||||
case TextureFormat::SFLOAT_32_32_32_32:
|
||||
case TextureFormat::SFLOAT_32_32:
|
||||
case TextureFormat::SFLOAT_32:
|
||||
case TextureFormat::SFLOAT_16_16_16_16:
|
||||
case TextureFormat::SFLOAT_16_16:
|
||||
case TextureFormat::SFLOAT_16:
|
||||
case TextureFormat::UNORM_16_16_16_16:
|
||||
case TextureFormat::UNORM_16_16:
|
||||
case TextureFormat::UNORM_16:
|
||||
case TextureFormat::UNORM_8_8_8_8:
|
||||
case TextureFormat::UNORM_8_8:
|
||||
case TextureFormat::UNORM_8:
|
||||
return VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
|
||||
/* Special formats texture & render-buffer */
|
||||
case TextureFormat::UNORM_10_10_10_2:
|
||||
case TextureFormat::UINT_10_10_10_2:
|
||||
case TextureFormat::UFLOAT_11_11_10:
|
||||
case TextureFormat::SRGBA_8_8_8_8:
|
||||
return VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
|
||||
/* Depth Formats. */
|
||||
case TextureFormat::SFLOAT_32_DEPTH:
|
||||
case TextureFormat::UNORM_16_DEPTH:
|
||||
return VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
|
||||
case TextureFormat::SFLOAT_32_DEPTH_UINT_8:
|
||||
return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
|
||||
/* Texture only formats. */
|
||||
case TextureFormat::UINT_32_32_32:
|
||||
case TextureFormat::UINT_16_16_16:
|
||||
case TextureFormat::UINT_8_8_8:
|
||||
case TextureFormat::SINT_32_32_32:
|
||||
case TextureFormat::SINT_16_16_16:
|
||||
case TextureFormat::SINT_8_8_8:
|
||||
case TextureFormat::UNORM_16_16_16:
|
||||
case TextureFormat::UNORM_8_8_8:
|
||||
case TextureFormat::SNORM_16_16_16_16:
|
||||
case TextureFormat::SNORM_16_16_16:
|
||||
case TextureFormat::SNORM_16_16:
|
||||
case TextureFormat::SNORM_16:
|
||||
case TextureFormat::SNORM_8_8_8_8:
|
||||
case TextureFormat::SNORM_8_8_8:
|
||||
case TextureFormat::SNORM_8_8:
|
||||
case TextureFormat::SNORM_8:
|
||||
case TextureFormat::SFLOAT_32_32_32:
|
||||
case TextureFormat::SFLOAT_16_16_16:
|
||||
return VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
|
||||
/* Special formats, texture only. */
|
||||
case TextureFormat::SRGB_DXT1:
|
||||
case TextureFormat::SRGB_DXT3:
|
||||
case TextureFormat::SRGB_DXT5:
|
||||
case TextureFormat::SNORM_DXT1:
|
||||
case TextureFormat::SNORM_DXT3:
|
||||
case TextureFormat::SNORM_DXT5:
|
||||
case TextureFormat::SRGBA_8_8_8:
|
||||
case TextureFormat::UFLOAT_9_9_9_EXP_5:
|
||||
return VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
|
||||
case TextureFormat::Invalid:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
VkImageAspectFlags to_vk_image_aspect_flag_bits(const GPUFrameBufferBits buffers)
|
||||
{
|
||||
VkImageAspectFlags result = 0;
|
||||
if (buffers & GPU_COLOR_BIT) {
|
||||
result |= VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
}
|
||||
if (buffers & GPU_DEPTH_BIT) {
|
||||
result |= VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
}
|
||||
if (buffers & GPU_STENCIL_BIT) {
|
||||
result |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TextureFormat to_gpu_format(const VkFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case VK_FORMAT_R8G8B8A8_UNORM:
|
||||
case VK_FORMAT_B8G8R8A8_UNORM:
|
||||
return TextureFormat::UNORM_8_8_8_8;
|
||||
|
||||
case VK_FORMAT_R16G16B16A16_SFLOAT:
|
||||
return TextureFormat::SFLOAT_16_16_16_16;
|
||||
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
return TextureFormat::SFLOAT_32_32_32_32;
|
||||
}
|
||||
|
||||
std::string to_gpu_format_string(VkFormat format)
|
||||
{
|
||||
#define CASE(a, b, c, blender_enum, vk_enum, e, f, g, h) \
|
||||
case VK_FORMAT_##vk_enum: \
|
||||
return STRINGIFY(blender_enum);
|
||||
switch (format) {
|
||||
GPU_TEXTURE_TARGET_FORMAT_EXPAND(CASE)
|
||||
|
||||
case VK_FORMAT_UNDEFINED:
|
||||
return "UNDEFINED";
|
||||
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
#undef CASE
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
VkFormat to_vk_format(const TextureFormat format)
|
||||
{
|
||||
#define CASE(a, b, c, blender_enum, vk_enum, e, f, g, h) \
|
||||
case TextureFormat::blender_enum: \
|
||||
return VK_FORMAT_##vk_enum;
|
||||
|
||||
switch (format) {
|
||||
GPU_TEXTURE_FORMAT_EXPAND(CASE)
|
||||
case TextureFormat::Invalid:
|
||||
break;
|
||||
}
|
||||
#undef CASE
|
||||
return VK_FORMAT_UNDEFINED;
|
||||
}
|
||||
|
||||
static VkFormat to_vk_format_norm(const GPUVertCompType type, const uint32_t size)
|
||||
{
|
||||
switch (type) {
|
||||
case GPU_COMP_I8:
|
||||
switch (size) {
|
||||
case 1:
|
||||
return VK_FORMAT_R8_SNORM;
|
||||
case 2:
|
||||
return VK_FORMAT_R8G8_SNORM;
|
||||
case 3:
|
||||
return VK_FORMAT_R8G8B8_SNORM;
|
||||
case 4:
|
||||
return VK_FORMAT_R8G8B8A8_SNORM;
|
||||
case 16:
|
||||
return VK_FORMAT_R8G8B8A8_SNORM;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return VK_FORMAT_R8_SNORM;
|
||||
}
|
||||
break;
|
||||
|
||||
case GPU_COMP_U8:
|
||||
switch (size) {
|
||||
case 1:
|
||||
return VK_FORMAT_R8_UNORM;
|
||||
case 2:
|
||||
return VK_FORMAT_R8G8_UNORM;
|
||||
case 3:
|
||||
return VK_FORMAT_R8G8B8_UNORM;
|
||||
case 4:
|
||||
return VK_FORMAT_R8G8B8A8_UNORM;
|
||||
case 16:
|
||||
return VK_FORMAT_R8G8B8A8_UNORM;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return VK_FORMAT_R8_UNORM;
|
||||
}
|
||||
break;
|
||||
|
||||
case GPU_COMP_I16:
|
||||
switch (size) {
|
||||
case 2:
|
||||
return VK_FORMAT_R16_SNORM;
|
||||
case 4:
|
||||
return VK_FORMAT_R16G16_SNORM;
|
||||
case 6:
|
||||
return VK_FORMAT_R16G16B16_SNORM;
|
||||
case 8:
|
||||
return VK_FORMAT_R16G16B16A16_SNORM;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return VK_FORMAT_R16_SNORM;
|
||||
}
|
||||
break;
|
||||
|
||||
case GPU_COMP_U16:
|
||||
switch (size) {
|
||||
case 2:
|
||||
return VK_FORMAT_R16_UNORM;
|
||||
case 4:
|
||||
return VK_FORMAT_R16G16_UNORM;
|
||||
case 6:
|
||||
return VK_FORMAT_R16G16B16_UNORM;
|
||||
case 8:
|
||||
return VK_FORMAT_R16G16B16A16_UNORM;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return VK_FORMAT_R16_UNORM;
|
||||
}
|
||||
break;
|
||||
|
||||
case GPU_COMP_I10:
|
||||
BLI_assert(size == 4);
|
||||
return VK_FORMAT_A2B10G10R10_SNORM_PACK32;
|
||||
|
||||
case GPU_COMP_I32:
|
||||
case GPU_COMP_U32:
|
||||
case GPU_COMP_F32:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return VK_FORMAT_R32_SFLOAT;
|
||||
}
|
||||
|
||||
static VkFormat to_vk_format_float(const GPUVertCompType type, const uint32_t size)
|
||||
{
|
||||
switch (type) {
|
||||
case GPU_COMP_F32:
|
||||
switch (size) {
|
||||
case 4:
|
||||
return VK_FORMAT_R32_SFLOAT;
|
||||
case 8:
|
||||
return VK_FORMAT_R32G32_SFLOAT;
|
||||
case 12:
|
||||
return VK_FORMAT_R32G32B32_SFLOAT;
|
||||
case 16:
|
||||
return VK_FORMAT_R32G32B32A32_SFLOAT;
|
||||
case 64:
|
||||
return VK_FORMAT_R32G32B32A32_SFLOAT;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return VK_FORMAT_R32_SFLOAT;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return VK_FORMAT_R32_SFLOAT;
|
||||
}
|
||||
|
||||
static VkFormat to_vk_format_int(const GPUVertCompType type, const uint32_t size)
|
||||
{
|
||||
switch (type) {
|
||||
case GPU_COMP_I8:
|
||||
switch (size) {
|
||||
case 1:
|
||||
return VK_FORMAT_R8_SINT;
|
||||
case 2:
|
||||
return VK_FORMAT_R8G8_SINT;
|
||||
case 3:
|
||||
return VK_FORMAT_R8G8B8_SINT;
|
||||
case 4:
|
||||
return VK_FORMAT_R8G8B8A8_SINT;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return VK_FORMAT_R8_SINT;
|
||||
}
|
||||
break;
|
||||
|
||||
case GPU_COMP_U8:
|
||||
switch (size) {
|
||||
case 1:
|
||||
return VK_FORMAT_R8_UINT;
|
||||
case 2:
|
||||
return VK_FORMAT_R8G8_UINT;
|
||||
case 3:
|
||||
return VK_FORMAT_R8G8B8_UINT;
|
||||
case 4:
|
||||
return VK_FORMAT_R8G8B8A8_UINT;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return VK_FORMAT_R8_UINT;
|
||||
}
|
||||
break;
|
||||
|
||||
case GPU_COMP_I16:
|
||||
switch (size) {
|
||||
case 2:
|
||||
return VK_FORMAT_R16_SINT;
|
||||
case 4:
|
||||
return VK_FORMAT_R16G16_SINT;
|
||||
case 6:
|
||||
return VK_FORMAT_R16G16B16_SINT;
|
||||
case 8:
|
||||
return VK_FORMAT_R16G16B16A16_SINT;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return VK_FORMAT_R16_SINT;
|
||||
}
|
||||
break;
|
||||
|
||||
case GPU_COMP_U16:
|
||||
switch (size) {
|
||||
case 2:
|
||||
return VK_FORMAT_R16_UINT;
|
||||
case 4:
|
||||
return VK_FORMAT_R16G16_UINT;
|
||||
case 6:
|
||||
return VK_FORMAT_R16G16B16_UINT;
|
||||
case 8:
|
||||
return VK_FORMAT_R16G16B16A16_UINT;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return VK_FORMAT_R16_UINT;
|
||||
}
|
||||
break;
|
||||
|
||||
case GPU_COMP_I32:
|
||||
switch (size) {
|
||||
case 4:
|
||||
return VK_FORMAT_R32_SINT;
|
||||
case 8:
|
||||
return VK_FORMAT_R32G32_SINT;
|
||||
case 12:
|
||||
return VK_FORMAT_R32G32B32_SINT;
|
||||
case 16:
|
||||
return VK_FORMAT_R32G32B32A32_SINT;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return VK_FORMAT_R32_SINT;
|
||||
}
|
||||
break;
|
||||
|
||||
case GPU_COMP_U32:
|
||||
switch (size) {
|
||||
case 4:
|
||||
return VK_FORMAT_R32_UINT;
|
||||
case 8:
|
||||
return VK_FORMAT_R32G32_UINT;
|
||||
case 12:
|
||||
return VK_FORMAT_R32G32B32_UINT;
|
||||
case 16:
|
||||
return VK_FORMAT_R32G32B32A32_UINT;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return VK_FORMAT_R32_UINT;
|
||||
}
|
||||
break;
|
||||
|
||||
case GPU_COMP_F32:
|
||||
switch (size) {
|
||||
case 4:
|
||||
return VK_FORMAT_R32_SINT;
|
||||
case 8:
|
||||
return VK_FORMAT_R32G32_SINT;
|
||||
case 12:
|
||||
return VK_FORMAT_R32G32B32_SINT;
|
||||
case 16:
|
||||
return VK_FORMAT_R32G32B32A32_SINT;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return VK_FORMAT_R32_SINT;
|
||||
}
|
||||
break;
|
||||
|
||||
case GPU_COMP_I10:
|
||||
BLI_assert(size == 4);
|
||||
return VK_FORMAT_A2B10G10R10_SINT_PACK32;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return VK_FORMAT_R32_SFLOAT;
|
||||
}
|
||||
|
||||
VkFormat to_vk_format(const GPUVertCompType type, const uint32_t size, GPUVertFetchMode fetch_mode)
|
||||
{
|
||||
switch (fetch_mode) {
|
||||
case GPU_FETCH_FLOAT:
|
||||
return to_vk_format_float(type, size);
|
||||
case GPU_FETCH_INT:
|
||||
return to_vk_format_int(type, size);
|
||||
break;
|
||||
case GPU_FETCH_INT_TO_FLOAT_UNIT:
|
||||
return to_vk_format_norm(type, size);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return VK_FORMAT_R32_SFLOAT;
|
||||
}
|
||||
|
||||
VkFormat to_vk_format(const shader::Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case shader::Type::float_t:
|
||||
return VK_FORMAT_R32_SFLOAT;
|
||||
case shader::Type::float2_t:
|
||||
return VK_FORMAT_R32G32_SFLOAT;
|
||||
case shader::Type::float3_t:
|
||||
return VK_FORMAT_R32G32B32_SFLOAT;
|
||||
case shader::Type::float4_t:
|
||||
return VK_FORMAT_R32G32B32A32_SFLOAT;
|
||||
case shader::Type::uint_t:
|
||||
return VK_FORMAT_R32_UINT;
|
||||
case shader::Type::uint2_t:
|
||||
return VK_FORMAT_R32G32_UINT;
|
||||
case shader::Type::uint3_t:
|
||||
return VK_FORMAT_R32G32B32_UINT;
|
||||
case shader::Type::uint4_t:
|
||||
return VK_FORMAT_R32G32B32A32_UINT;
|
||||
case shader::Type::int_t:
|
||||
return VK_FORMAT_R32_SINT;
|
||||
case shader::Type::int2_t:
|
||||
return VK_FORMAT_R32G32_SINT;
|
||||
case shader::Type::int3_t:
|
||||
return VK_FORMAT_R32G32B32_SINT;
|
||||
case shader::Type::int4_t:
|
||||
return VK_FORMAT_R32G32B32A32_SINT;
|
||||
case shader::Type::float4x4_t:
|
||||
return VK_FORMAT_R32G32B32A32_SFLOAT;
|
||||
|
||||
case shader::Type::float3x3_t:
|
||||
case shader::Type::bool_t:
|
||||
case shader::Type::float3_10_10_10_2_t:
|
||||
case shader::Type::uchar_t:
|
||||
case shader::Type::uchar2_t:
|
||||
case shader::Type::uchar3_t:
|
||||
case shader::Type::uchar4_t:
|
||||
case shader::Type::char_t:
|
||||
case shader::Type::char2_t:
|
||||
case shader::Type::char3_t:
|
||||
case shader::Type::char4_t:
|
||||
case shader::Type::short_t:
|
||||
case shader::Type::short2_t:
|
||||
case shader::Type::short3_t:
|
||||
case shader::Type::short4_t:
|
||||
case shader::Type::ushort_t:
|
||||
case shader::Type::ushort2_t:
|
||||
case shader::Type::ushort3_t:
|
||||
case shader::Type::ushort4_t:
|
||||
break;
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return VK_FORMAT_R32G32B32A32_SFLOAT;
|
||||
}
|
||||
|
||||
VkQueryType to_vk_query_type(const GPUQueryType query_type)
|
||||
{
|
||||
switch (query_type) {
|
||||
case GPU_QUERY_OCCLUSION:
|
||||
return VK_QUERY_TYPE_OCCLUSION;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return VK_QUERY_TYPE_OCCLUSION;
|
||||
}
|
||||
|
||||
VkImageType to_vk_image_type(const GPUTextureType type)
|
||||
{
|
||||
/* See
|
||||
* https://vulkan.lunarg.com/doc/view/1.3.243.0/linux/1.3-extensions/vkspec.html#resources-image-views-compatibility
|
||||
* for reference */
|
||||
switch (type) {
|
||||
case GPU_TEXTURE_1D:
|
||||
case GPU_TEXTURE_BUFFER:
|
||||
case GPU_TEXTURE_1D_ARRAY:
|
||||
return VK_IMAGE_TYPE_1D;
|
||||
|
||||
case GPU_TEXTURE_2D:
|
||||
case GPU_TEXTURE_2D_ARRAY:
|
||||
case GPU_TEXTURE_CUBE:
|
||||
case GPU_TEXTURE_CUBE_ARRAY:
|
||||
return VK_IMAGE_TYPE_2D;
|
||||
|
||||
case GPU_TEXTURE_3D:
|
||||
return VK_IMAGE_TYPE_3D;
|
||||
|
||||
case GPU_TEXTURE_ARRAY:
|
||||
/* GPU_TEXTURE_ARRAY should always be used together with 1D, 2D, or CUBE. */
|
||||
break;
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return VK_IMAGE_TYPE_1D;
|
||||
}
|
||||
|
||||
VkImageViewType to_vk_image_view_type(const GPUTextureType type,
|
||||
const eImageViewUsage view_type,
|
||||
VKImageViewArrayed arrayed)
|
||||
{
|
||||
VkImageViewType result = VK_IMAGE_VIEW_TYPE_1D;
|
||||
|
||||
switch (type) {
|
||||
case GPU_TEXTURE_1D:
|
||||
case GPU_TEXTURE_BUFFER:
|
||||
result = VK_IMAGE_VIEW_TYPE_1D;
|
||||
break;
|
||||
case GPU_TEXTURE_2D:
|
||||
result = VK_IMAGE_VIEW_TYPE_2D;
|
||||
break;
|
||||
case GPU_TEXTURE_3D:
|
||||
result = VK_IMAGE_VIEW_TYPE_3D;
|
||||
break;
|
||||
case GPU_TEXTURE_CUBE:
|
||||
result = view_type == eImageViewUsage::Attachment ? VK_IMAGE_VIEW_TYPE_2D_ARRAY :
|
||||
VK_IMAGE_VIEW_TYPE_CUBE;
|
||||
break;
|
||||
case GPU_TEXTURE_1D_ARRAY:
|
||||
result = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
|
||||
break;
|
||||
case GPU_TEXTURE_2D_ARRAY:
|
||||
result = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
||||
break;
|
||||
case GPU_TEXTURE_CUBE_ARRAY:
|
||||
result = view_type == eImageViewUsage::Attachment ? VK_IMAGE_VIEW_TYPE_2D_ARRAY :
|
||||
VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
|
||||
break;
|
||||
|
||||
case GPU_TEXTURE_ARRAY:
|
||||
/* GPU_TEXTURE_ARRAY should always be used together with 1D, 2D, or CUBE. */
|
||||
break;
|
||||
}
|
||||
|
||||
if (arrayed == VKImageViewArrayed::NOT_ARRAYED) {
|
||||
if (result == VK_IMAGE_VIEW_TYPE_1D_ARRAY) {
|
||||
result = VK_IMAGE_VIEW_TYPE_1D;
|
||||
}
|
||||
else if (result == VK_IMAGE_VIEW_TYPE_2D_ARRAY) {
|
||||
result = VK_IMAGE_VIEW_TYPE_2D;
|
||||
}
|
||||
else if (result == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY) {
|
||||
result = VK_IMAGE_VIEW_TYPE_CUBE;
|
||||
}
|
||||
}
|
||||
else if (arrayed == VKImageViewArrayed::ARRAYED) {
|
||||
if (result == VK_IMAGE_VIEW_TYPE_1D) {
|
||||
result = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
|
||||
}
|
||||
else if (result == VK_IMAGE_VIEW_TYPE_2D) {
|
||||
result = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
||||
}
|
||||
else if (result == VK_IMAGE_VIEW_TYPE_CUBE) {
|
||||
result = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
VkComponentSwizzle to_vk_component_swizzle(const char swizzle)
|
||||
{
|
||||
switch (swizzle) {
|
||||
case '0':
|
||||
return VK_COMPONENT_SWIZZLE_ZERO;
|
||||
case '1':
|
||||
return VK_COMPONENT_SWIZZLE_ONE;
|
||||
case 'r':
|
||||
return VK_COMPONENT_SWIZZLE_R;
|
||||
case 'g':
|
||||
return VK_COMPONENT_SWIZZLE_G;
|
||||
case 'b':
|
||||
return VK_COMPONENT_SWIZZLE_B;
|
||||
case 'a':
|
||||
return VK_COMPONENT_SWIZZLE_A;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
}
|
||||
|
||||
VkClearColorValue to_vk_clear_color_value(const eGPUDataFormat format, const double4 data)
|
||||
{
|
||||
VkClearColorValue result = {{0.0f}};
|
||||
switch (format) {
|
||||
/* All float-like formats (i.e. everything except literal int/uint) go
|
||||
* into VkClearColorValue float color fields. */
|
||||
case GPU_DATA_FLOAT:
|
||||
case GPU_DATA_HALF_FLOAT:
|
||||
case GPU_DATA_UBYTE:
|
||||
case GPU_DATA_10_11_11_REV:
|
||||
case GPU_DATA_2_10_10_10_REV: {
|
||||
result.float32[0] = float(data[0]);
|
||||
result.float32[1] = float(data[1]);
|
||||
result.float32[2] = float(data[2]);
|
||||
result.float32[3] = float(data[3]);
|
||||
break;
|
||||
}
|
||||
|
||||
case GPU_DATA_INT: {
|
||||
result.int32[0] = int32_t(data[0]);
|
||||
result.int32[1] = int32_t(data[1]);
|
||||
result.int32[2] = int32_t(data[2]);
|
||||
result.int32[3] = int32_t(data[3]);
|
||||
break;
|
||||
}
|
||||
|
||||
case GPU_DATA_UINT: {
|
||||
result.uint32[0] = uint32_t(data[0]);
|
||||
result.uint32[1] = uint32_t(data[1]);
|
||||
result.uint32[2] = uint32_t(data[2]);
|
||||
result.uint32[3] = uint32_t(data[3]);
|
||||
break;
|
||||
}
|
||||
case GPU_DATA_UINT_24_8_DEPRECATED: {
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
VkIndexType to_vk_index_type(const GPUIndexBufType index_type)
|
||||
{
|
||||
switch (index_type) {
|
||||
case GPU_INDEX_U16:
|
||||
return VK_INDEX_TYPE_UINT16;
|
||||
case GPU_INDEX_U32:
|
||||
return VK_INDEX_TYPE_UINT32;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return VK_INDEX_TYPE_UINT16;
|
||||
}
|
||||
|
||||
VkPrimitiveTopology to_vk_primitive_topology(const GPUPrimType prim_type)
|
||||
{
|
||||
switch (prim_type) {
|
||||
case GPU_PRIM_POINTS:
|
||||
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
||||
case GPU_PRIM_LINES:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
|
||||
case GPU_PRIM_TRIS:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
case GPU_PRIM_LINE_STRIP:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
|
||||
case GPU_PRIM_LINE_LOOP:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
|
||||
case GPU_PRIM_TRI_STRIP:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
|
||||
case GPU_PRIM_TRI_FAN:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
|
||||
case GPU_PRIM_LINES_ADJ:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY;
|
||||
case GPU_PRIM_TRIS_ADJ:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY;
|
||||
case GPU_PRIM_LINE_STRIP_ADJ:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY;
|
||||
|
||||
case GPU_PRIM_NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
||||
}
|
||||
|
||||
VkCullModeFlags to_vk_cull_mode_flags(const GPUFaceCullTest cull_test)
|
||||
{
|
||||
switch (cull_test) {
|
||||
case GPU_CULL_FRONT:
|
||||
return VK_CULL_MODE_FRONT_BIT;
|
||||
case GPU_CULL_BACK:
|
||||
return VK_CULL_MODE_BACK_BIT;
|
||||
case GPU_CULL_NONE:
|
||||
return VK_CULL_MODE_NONE;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return VK_CULL_MODE_NONE;
|
||||
}
|
||||
|
||||
VkSamplerAddressMode to_vk_sampler_address_mode(const GPUSamplerExtendMode extend_mode)
|
||||
{
|
||||
switch (extend_mode) {
|
||||
case GPU_SAMPLER_EXTEND_MODE_EXTEND:
|
||||
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
case GPU_SAMPLER_EXTEND_MODE_REPEAT:
|
||||
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
||||
case GPU_SAMPLER_EXTEND_MODE_MIRRORED_REPEAT:
|
||||
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
||||
case GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER:
|
||||
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
||||
}
|
||||
|
||||
static VkDescriptorType to_vk_descriptor_type_image(const shader::ImageType &image_type)
|
||||
{
|
||||
switch (image_type) {
|
||||
case shader::ImageType::Float1D:
|
||||
case shader::ImageType::Float1DArray:
|
||||
case shader::ImageType::Float2D:
|
||||
case shader::ImageType::Float2DArray:
|
||||
case shader::ImageType::Float3D:
|
||||
case shader::ImageType::FloatCube:
|
||||
case shader::ImageType::FloatCubeArray:
|
||||
case shader::ImageType::Int1D:
|
||||
case shader::ImageType::Int1DArray:
|
||||
case shader::ImageType::Int2D:
|
||||
case shader::ImageType::Int2DArray:
|
||||
case shader::ImageType::Int3D:
|
||||
case shader::ImageType::IntCube:
|
||||
case shader::ImageType::IntCubeArray:
|
||||
case shader::ImageType::AtomicInt2D:
|
||||
case shader::ImageType::AtomicInt2DArray:
|
||||
case shader::ImageType::AtomicInt3D:
|
||||
case shader::ImageType::Uint1D:
|
||||
case shader::ImageType::Uint1DArray:
|
||||
case shader::ImageType::Uint2D:
|
||||
case shader::ImageType::Uint2DArray:
|
||||
case shader::ImageType::Uint3D:
|
||||
case shader::ImageType::UintCube:
|
||||
case shader::ImageType::UintCubeArray:
|
||||
case shader::ImageType::AtomicUint2D:
|
||||
case shader::ImageType::AtomicUint2DArray:
|
||||
case shader::ImageType::AtomicUint3D:
|
||||
return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
||||
|
||||
case shader::ImageType::FloatBuffer:
|
||||
case shader::ImageType::IntBuffer:
|
||||
case shader::ImageType::UintBuffer:
|
||||
return VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
|
||||
|
||||
default:
|
||||
BLI_assert_msg(false, "ImageType not supported.");
|
||||
}
|
||||
|
||||
return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
||||
}
|
||||
|
||||
static VkDescriptorType to_vk_descriptor_type_sampler(const shader::ImageType &image_type)
|
||||
{
|
||||
switch (image_type) {
|
||||
case shader::ImageType::undefined:
|
||||
case shader::ImageType::Float1D:
|
||||
case shader::ImageType::Float1DArray:
|
||||
case shader::ImageType::Float2D:
|
||||
case shader::ImageType::Float2DArray:
|
||||
case shader::ImageType::Float3D:
|
||||
case shader::ImageType::FloatCube:
|
||||
case shader::ImageType::FloatCubeArray:
|
||||
case shader::ImageType::Int1D:
|
||||
case shader::ImageType::Int1DArray:
|
||||
case shader::ImageType::Int2D:
|
||||
case shader::ImageType::Int2DArray:
|
||||
case shader::ImageType::Int3D:
|
||||
case shader::ImageType::IntCube:
|
||||
case shader::ImageType::IntCubeArray:
|
||||
case shader::ImageType::AtomicInt2D:
|
||||
case shader::ImageType::AtomicInt2DArray:
|
||||
case shader::ImageType::AtomicInt3D:
|
||||
case shader::ImageType::Uint1D:
|
||||
case shader::ImageType::Uint1DArray:
|
||||
case shader::ImageType::Uint2D:
|
||||
case shader::ImageType::Uint2DArray:
|
||||
case shader::ImageType::Uint3D:
|
||||
case shader::ImageType::UintCube:
|
||||
case shader::ImageType::UintCubeArray:
|
||||
case shader::ImageType::AtomicUint2D:
|
||||
case shader::ImageType::AtomicUint2DArray:
|
||||
case shader::ImageType::AtomicUint3D:
|
||||
case shader::ImageType::Shadow2D:
|
||||
case shader::ImageType::Shadow2DArray:
|
||||
case shader::ImageType::ShadowCube:
|
||||
case shader::ImageType::ShadowCubeArray:
|
||||
case shader::ImageType::Depth2D:
|
||||
case shader::ImageType::Depth2DArray:
|
||||
case shader::ImageType::DepthCube:
|
||||
case shader::ImageType::DepthCubeArray:
|
||||
return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
|
||||
case shader::ImageType::FloatBuffer:
|
||||
case shader::ImageType::IntBuffer:
|
||||
case shader::ImageType::UintBuffer:
|
||||
return VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
|
||||
}
|
||||
|
||||
return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
}
|
||||
|
||||
VkDescriptorType to_vk_descriptor_type(const shader::ShaderCreateInfo::Resource &resource)
|
||||
{
|
||||
switch (resource.bind_type) {
|
||||
case shader::ShaderCreateInfo::Resource::BindType::IMAGE:
|
||||
return to_vk_descriptor_type_image(resource.image.type);
|
||||
case shader::ShaderCreateInfo::Resource::BindType::SAMPLER:
|
||||
return to_vk_descriptor_type_sampler(resource.sampler.type);
|
||||
case shader::ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER:
|
||||
return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
case shader::ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER:
|
||||
return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
}
|
||||
|
||||
VkImageCreateFlags to_vk_image_create(const GPUTextureType texture_type,
|
||||
const GPUTextureFormatFlag format_flag,
|
||||
const eGPUTextureUsage usage)
|
||||
{
|
||||
VkImageCreateFlags result = 0;
|
||||
|
||||
if (ELEM(texture_type, GPU_TEXTURE_CUBE, GPU_TEXTURE_CUBE_ARRAY)) {
|
||||
result |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
|
||||
}
|
||||
|
||||
/* sRGB textures needs to be mutable as they can be used as non-sRGB frame-buffer attachments. */
|
||||
if (usage & GPU_TEXTURE_USAGE_ATTACHMENT && format_flag & GPU_FORMAT_SRGB) {
|
||||
result |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
VkImageUsageFlags to_vk_image_usage(const eGPUTextureUsage usage,
|
||||
const GPUTextureFormatFlag format_flag,
|
||||
bool use_image_host_copy)
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
const VKExtensions &extensions = device.extensions_get();
|
||||
|
||||
VkImageUsageFlags result = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
if (usage & GPU_TEXTURE_USAGE_SHADER_READ) {
|
||||
result |= VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
}
|
||||
if (usage & GPU_TEXTURE_USAGE_SHADER_WRITE) {
|
||||
result |= VK_IMAGE_USAGE_STORAGE_BIT;
|
||||
}
|
||||
if (usage & GPU_TEXTURE_USAGE_ATTACHMENT) {
|
||||
if (format_flag & GPU_FORMAT_COMPRESSED) {
|
||||
/* These formats aren't supported as an attachment. When using GPU_TEXTURE_USAGE_DEFAULT they
|
||||
* are still being evaluated to be attachable. So we need to skip them. */
|
||||
}
|
||||
else {
|
||||
if (format_flag & (GPU_FORMAT_DEPTH | GPU_FORMAT_STENCIL)) {
|
||||
result |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
||||
}
|
||||
else {
|
||||
result |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
result |= extensions.dynamic_rendering_local_read ? VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT :
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (usage & GPU_TEXTURE_USAGE_HOST_READ) {
|
||||
result |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
}
|
||||
if (use_image_host_copy) {
|
||||
result |= VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT;
|
||||
}
|
||||
|
||||
/* Disable some usages based on the given format flag to support more devices. */
|
||||
if (format_flag & GPU_FORMAT_SRGB) {
|
||||
/* NVIDIA devices don't create SRGB textures when it storage bit is set. */
|
||||
result &= ~VK_IMAGE_USAGE_STORAGE_BIT;
|
||||
}
|
||||
if (format_flag & (GPU_FORMAT_DEPTH | GPU_FORMAT_STENCIL)) {
|
||||
/* NVIDIA devices don't create depth textures when it storage bit is set. */
|
||||
result &= ~VK_IMAGE_USAGE_STORAGE_BIT;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
164
blender-5.2.0/source/blender/gpu/vulkan/vk_common.hh
Normal file
164
blender-5.2.0/source/blender/gpu/vulkan/vk_common.hh
Normal file
@@ -0,0 +1,164 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <typeinfo>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include "BLI_winstuff.h"
|
||||
#endif
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#ifdef _WIN32
|
||||
# include <vulkan/vulkan_win32.h>
|
||||
#endif
|
||||
|
||||
#define VMA_VULKAN_VERSION 1002000 // Vulkan 1.2
|
||||
#if !defined(_WIN32) or defined(_M_ARM64)
|
||||
/* Silence compilation warning on non-windows x64 systems. */
|
||||
# define VMA_EXTERNAL_MEMORY_WIN32 0
|
||||
#endif
|
||||
#include "vk_mem_alloc.h"
|
||||
|
||||
#include "GPU_index_buffer.hh"
|
||||
#include "GPU_state.hh"
|
||||
#include "gpu_query.hh"
|
||||
#include "gpu_shader_create_info.hh"
|
||||
#include "gpu_texture_private.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
using TimelineValue = uint64_t;
|
||||
|
||||
/**
|
||||
* Based on the usage of an Image View a different image view type should be created.
|
||||
*
|
||||
* When using a GPU_TEXTURE_CUBE as an frame buffer attachment it will be used as a
|
||||
* GPU_TEXTURE_2D_ARRAY. eg only a single side of the cube map will be attached. But when bound as
|
||||
* a shader resource the cube-map will be used.
|
||||
*/
|
||||
enum class eImageViewUsage {
|
||||
/** Image View will be used as a bindable shader resource. */
|
||||
ShaderBinding,
|
||||
/** Image View will be used as an frame-buffer attachment. */
|
||||
Attachment,
|
||||
};
|
||||
|
||||
enum class VKImageViewArrayed {
|
||||
DONT_CARE,
|
||||
NOT_ARRAYED,
|
||||
ARRAYED,
|
||||
};
|
||||
|
||||
struct VKSubImageRange {
|
||||
uint32_t mipmap_level = 0;
|
||||
uint32_t mipmap_count = VK_REMAINING_MIP_LEVELS;
|
||||
uint32_t layer_base = 0;
|
||||
uint32_t layer_count = VK_REMAINING_ARRAY_LAYERS;
|
||||
};
|
||||
|
||||
VkImageAspectFlags to_vk_image_aspect_flag_bits(const TextureFormat format);
|
||||
VkImageAspectFlags to_vk_image_aspect_flag_bits(const GPUFrameBufferBits buffers);
|
||||
VkFormat to_vk_format(const TextureFormat format);
|
||||
BLI_INLINE VkFormat to_vk_format(const TextureTargetFormat format)
|
||||
{
|
||||
return to_vk_format(to_texture_format(format));
|
||||
}
|
||||
TextureFormat to_gpu_format(const VkFormat format);
|
||||
std::string to_gpu_format_string(VkFormat format);
|
||||
VkFormat to_vk_format(const GPUVertCompType type,
|
||||
const uint32_t size,
|
||||
const GPUVertFetchMode fetch_mode);
|
||||
VkFormat to_vk_format(const shader::Type type);
|
||||
VkQueryType to_vk_query_type(const GPUQueryType query_type);
|
||||
|
||||
VkComponentSwizzle to_vk_component_swizzle(const char swizzle);
|
||||
VkImageViewType to_vk_image_view_type(const GPUTextureType type,
|
||||
eImageViewUsage view_type,
|
||||
VKImageViewArrayed arrayed);
|
||||
VkImageType to_vk_image_type(const GPUTextureType type);
|
||||
VkClearColorValue to_vk_clear_color_value(const eGPUDataFormat format, const double4 data);
|
||||
VkIndexType to_vk_index_type(const GPUIndexBufType index_type);
|
||||
VkPrimitiveTopology to_vk_primitive_topology(const GPUPrimType prim_type);
|
||||
VkCullModeFlags to_vk_cull_mode_flags(const GPUFaceCullTest cull_test);
|
||||
VkSamplerAddressMode to_vk_sampler_address_mode(const GPUSamplerExtendMode extend_mode);
|
||||
VkDescriptorType to_vk_descriptor_type(const shader::ShaderCreateInfo::Resource &resource);
|
||||
VkImageCreateFlags to_vk_image_create(const GPUTextureType texture_type,
|
||||
const GPUTextureFormatFlag format_flag,
|
||||
const eGPUTextureUsage usage);
|
||||
VkImageUsageFlags to_vk_image_usage(const eGPUTextureUsage usage,
|
||||
const GPUTextureFormatFlag format_flag,
|
||||
bool use_image_host_copy);
|
||||
|
||||
template<typename T> VkObjectType to_vk_object_type(T /*vk_obj*/)
|
||||
{
|
||||
const std::type_info &tid = typeid(T);
|
||||
#define VK_EQ_TYPEID(name, name2) \
|
||||
if (tid == typeid(name)) { \
|
||||
return VK_OBJECT_TYPE_##name2; \
|
||||
}
|
||||
|
||||
VK_EQ_TYPEID(VkInstance, INSTANCE);
|
||||
VK_EQ_TYPEID(VkPhysicalDevice, PHYSICAL_DEVICE);
|
||||
VK_EQ_TYPEID(VkDevice, DEVICE);
|
||||
VK_EQ_TYPEID(VkQueue, QUEUE);
|
||||
VK_EQ_TYPEID(VkSemaphore, SEMAPHORE);
|
||||
VK_EQ_TYPEID(VkCommandBuffer, COMMAND_BUFFER);
|
||||
VK_EQ_TYPEID(VkFence, FENCE);
|
||||
VK_EQ_TYPEID(VkDeviceMemory, DEVICE_MEMORY);
|
||||
VK_EQ_TYPEID(VkBuffer, BUFFER);
|
||||
VK_EQ_TYPEID(VkImage, IMAGE);
|
||||
VK_EQ_TYPEID(VkEvent, EVENT);
|
||||
VK_EQ_TYPEID(VkQueryPool, QUERY_POOL);
|
||||
VK_EQ_TYPEID(VkBufferView, BUFFER_VIEW);
|
||||
VK_EQ_TYPEID(VkImageView, IMAGE_VIEW);
|
||||
VK_EQ_TYPEID(VkShaderModule, SHADER_MODULE);
|
||||
VK_EQ_TYPEID(VkPipelineCache, PIPELINE_CACHE);
|
||||
VK_EQ_TYPEID(VkPipelineLayout, PIPELINE_LAYOUT);
|
||||
VK_EQ_TYPEID(VkRenderPass, RENDER_PASS);
|
||||
VK_EQ_TYPEID(VkPipeline, PIPELINE);
|
||||
VK_EQ_TYPEID(VkDescriptorSetLayout, DESCRIPTOR_SET_LAYOUT);
|
||||
VK_EQ_TYPEID(VkSampler, SAMPLER);
|
||||
VK_EQ_TYPEID(VkDescriptorPool, DESCRIPTOR_POOL);
|
||||
VK_EQ_TYPEID(VkDescriptorSet, DESCRIPTOR_SET);
|
||||
VK_EQ_TYPEID(VkFramebuffer, FRAMEBUFFER);
|
||||
VK_EQ_TYPEID(VkCommandPool, COMMAND_POOL);
|
||||
VK_EQ_TYPEID(VkSamplerYcbcrConversion, SAMPLER_YCBCR_CONVERSION);
|
||||
VK_EQ_TYPEID(VkDescriptorUpdateTemplate, DESCRIPTOR_UPDATE_TEMPLATE);
|
||||
VK_EQ_TYPEID(VkSurfaceKHR, SURFACE_KHR);
|
||||
VK_EQ_TYPEID(VkSwapchainKHR, SWAPCHAIN_KHR);
|
||||
VK_EQ_TYPEID(VkDisplayKHR, DISPLAY_KHR);
|
||||
VK_EQ_TYPEID(VkDisplayModeKHR, DISPLAY_MODE_KHR);
|
||||
VK_EQ_TYPEID(VkDebugReportCallbackEXT, DEBUG_REPORT_CALLBACK_EXT);
|
||||
#ifdef VK_ENABLE_BETA_EXTENSIONS
|
||||
VK_EQ_TYPEID(VkVideoSessionKHR, VIDEO_SESSION_KHR);
|
||||
#endif
|
||||
#ifdef VK_ENABLE_BETA_EXTENSIONS
|
||||
VK_EQ_TYPEID(VkVideoSessionParametersKHR, VIDEO_SESSION_PARAMETERS_KHR);
|
||||
#endif
|
||||
VK_EQ_TYPEID(VkCuModuleNVX, CU_MODULE_NVX);
|
||||
VK_EQ_TYPEID(VkCuFunctionNVX, CU_FUNCTION_NVX);
|
||||
VK_EQ_TYPEID(VkDebugUtilsMessengerEXT, DEBUG_UTILS_MESSENGER_EXT);
|
||||
VK_EQ_TYPEID(VkAccelerationStructureKHR, ACCELERATION_STRUCTURE_KHR);
|
||||
VK_EQ_TYPEID(VkValidationCacheEXT, VALIDATION_CACHE_EXT);
|
||||
VK_EQ_TYPEID(VkAccelerationStructureNV, ACCELERATION_STRUCTURE_NV);
|
||||
VK_EQ_TYPEID(VkPerformanceConfigurationINTEL, PERFORMANCE_CONFIGURATION_INTEL);
|
||||
VK_EQ_TYPEID(VkDeferredOperationKHR, DEFERRED_OPERATION_KHR);
|
||||
VK_EQ_TYPEID(VkIndirectCommandsLayoutNV, INDIRECT_COMMANDS_LAYOUT_NV);
|
||||
VK_EQ_TYPEID(VkPrivateDataSlotEXT, PRIVATE_DATA_SLOT_EXT);
|
||||
|
||||
BLI_assert_unreachable();
|
||||
#undef VK_EQ_TYPEID
|
||||
return VK_OBJECT_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
#define NOT_YET_IMPLEMENTED \
|
||||
printf("%s:%d `%s` not implemented yet\n", __FILE__, __LINE__, __func__);
|
||||
|
||||
} // namespace blender::gpu
|
||||
660
blender-5.2.0/source/blender/gpu/vulkan/vk_context.cc
Normal file
660
blender-5.2.0/source/blender/gpu/vulkan/vk_context.cc
Normal file
@@ -0,0 +1,660 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "DNA_userdef_types.h"
|
||||
|
||||
#include "GPU_debug.hh"
|
||||
|
||||
#include "gpu_capabilities_private.hh"
|
||||
|
||||
#include "vk_backend.hh"
|
||||
#include "vk_context.hh"
|
||||
#include "vk_debug.hh"
|
||||
#include "vk_framebuffer.hh"
|
||||
#include "vk_immediate.hh"
|
||||
#include "vk_shader.hh"
|
||||
#include "vk_shader_interface.hh"
|
||||
#include "vk_state_manager.hh"
|
||||
#include "vk_texture.hh"
|
||||
#include "vk_vertex_attribute_object.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
VKContext::VKContext(GHOST_IWindow *ghost_window, GHOST_IContext *ghost_context)
|
||||
: push_constants_pool(VKBufferPool("PushConstants",
|
||||
64 * 1024,
|
||||
VKBackend::get()
|
||||
.device.physical_device_properties_get()
|
||||
.limits.minUniformBufferOffsetAlignment,
|
||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
||||
VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
|
||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
|
||||
0.8f))
|
||||
{
|
||||
ghost_window_ = ghost_window;
|
||||
ghost_context_ = ghost_context;
|
||||
|
||||
state_manager = new VKStateManager();
|
||||
imm = new VKImmediate();
|
||||
|
||||
back_left = new VKFrameBuffer("back_left");
|
||||
front_left = new VKFrameBuffer("front_left");
|
||||
active_fb = back_left;
|
||||
}
|
||||
|
||||
VKContext::~VKContext()
|
||||
{
|
||||
if (surface_texture_) {
|
||||
back_left->attachment_remove(GPU_FB_COLOR_ATTACHMENT0);
|
||||
front_left->attachment_remove(GPU_FB_COLOR_ATTACHMENT0);
|
||||
GPU_texture_free(surface_texture_);
|
||||
surface_texture_ = nullptr;
|
||||
}
|
||||
free_resources();
|
||||
delete imm;
|
||||
imm = nullptr;
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
device.context_unregister(*this);
|
||||
|
||||
this->process_frame_timings();
|
||||
}
|
||||
|
||||
void VKContext::sync_backbuffer()
|
||||
{
|
||||
if (ghost_window_) {
|
||||
GHOST_VulkanSwapChainData swap_chain_data = {};
|
||||
ghost_window_->getVulkanSwapChainFormat(&swap_chain_data);
|
||||
|
||||
const bool reset_framebuffer = swap_chain_format_.format !=
|
||||
swap_chain_data.surface_format.format ||
|
||||
swap_chain_format_.colorSpace !=
|
||||
swap_chain_data.surface_format.colorSpace ||
|
||||
vk_extent_.width != swap_chain_data.extent.width ||
|
||||
vk_extent_.height != swap_chain_data.extent.height;
|
||||
if (reset_framebuffer) {
|
||||
if (has_active_framebuffer()) {
|
||||
deactivate_framebuffer();
|
||||
}
|
||||
if (surface_texture_) {
|
||||
GPU_texture_free(surface_texture_);
|
||||
surface_texture_ = nullptr;
|
||||
}
|
||||
vk_extent_ = swap_chain_data.extent;
|
||||
vk_extent_.width = max_uu(vk_extent_.width, 1u);
|
||||
vk_extent_.height = max_uu(vk_extent_.height, 1u);
|
||||
surface_texture_ = GPU_texture_create_2d(
|
||||
"back-left",
|
||||
vk_extent_.width,
|
||||
vk_extent_.height,
|
||||
1,
|
||||
to_gpu_format(swap_chain_data.surface_format.format),
|
||||
GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_SHADER_READ,
|
||||
nullptr);
|
||||
|
||||
back_left->attachment_set(GPU_FB_COLOR_ATTACHMENT0,
|
||||
GPU_ATTACHMENT_TEXTURE(surface_texture_));
|
||||
front_left->attachment_set(GPU_FB_COLOR_ATTACHMENT0,
|
||||
GPU_ATTACHMENT_TEXTURE(surface_texture_));
|
||||
|
||||
back_left->bind(false);
|
||||
|
||||
swap_chain_format_ = swap_chain_data.surface_format;
|
||||
GCaps.hdr_viewport_support = (swap_chain_format_.format == VK_FORMAT_R16G16B16A16_SFLOAT) &&
|
||||
ELEM(swap_chain_format_.colorSpace,
|
||||
VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT,
|
||||
VK_COLOR_SPACE_PASS_THROUGH_EXT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VKContext::activate()
|
||||
{
|
||||
/* Make sure no other context is already bound to this thread. */
|
||||
BLI_assert(is_active_ == false);
|
||||
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
VKThreadData &thread_data = device.current_thread_data();
|
||||
thread_data_ = std::reference_wrapper<VKThreadData>(thread_data);
|
||||
|
||||
if (!render_graph_.has_value()) {
|
||||
render_graph_ = std::reference_wrapper<render_graph::VKRenderGraph>(
|
||||
*device.render_graph_new());
|
||||
/* Recreate the debug group stack for the new graph.
|
||||
* Note: there is no associated `debug_group_end` as the graph groups
|
||||
* are implicitly closed on submission. */
|
||||
for (const StringRef &group : debug_stack) {
|
||||
std::string str_group = group;
|
||||
render_graph_.value().get().debug_group_begin(str_group.c_str(),
|
||||
debug::get_debug_group_color(str_group));
|
||||
}
|
||||
}
|
||||
|
||||
is_active_ = true;
|
||||
|
||||
sync_backbuffer();
|
||||
|
||||
immActivate();
|
||||
}
|
||||
|
||||
void VKContext::deactivate()
|
||||
{
|
||||
flush_render_graph(RenderGraphFlushFlags(0));
|
||||
immDeactivate();
|
||||
thread_data_.reset();
|
||||
|
||||
is_active_ = false;
|
||||
}
|
||||
|
||||
void VKContext::begin_frame() {}
|
||||
|
||||
void VKContext::end_frame()
|
||||
{
|
||||
this->process_frame_timings();
|
||||
}
|
||||
|
||||
void VKContext::flush()
|
||||
{
|
||||
/* Submit when flushing to avoid out-of-memory errors and TDRs when more and more commands are
|
||||
* added in background mode without ever submitting work to the GPU. */
|
||||
flush_render_graph(RenderGraphFlushFlags::SUBMIT | RenderGraphFlushFlags::RENEW_RENDER_GRAPH);
|
||||
}
|
||||
|
||||
TimelineValue VKContext::flush_render_graph(RenderGraphFlushFlags flags,
|
||||
VkPipelineStageFlags wait_dst_stage_mask,
|
||||
VkSemaphore wait_semaphore,
|
||||
VkSemaphore signal_semaphore,
|
||||
VkFence signal_fence)
|
||||
{
|
||||
if (has_active_framebuffer()) {
|
||||
VKFrameBuffer &framebuffer = *active_framebuffer_get();
|
||||
if (framebuffer.is_rendering()) {
|
||||
framebuffer.rendering_end(*this);
|
||||
}
|
||||
}
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
push_constants_pool.ensure_uploaded();
|
||||
push_constants_pool.discard();
|
||||
descriptor_set_get().upload_descriptor_sets();
|
||||
TimelineValue timeline = device.render_graph_submit(
|
||||
&render_graph_.value().get(),
|
||||
discard_pool,
|
||||
bool(flags & RenderGraphFlushFlags::SUBMIT),
|
||||
bool(flags & RenderGraphFlushFlags::WAIT_FOR_SUBMISSION),
|
||||
bool(flags & RenderGraphFlushFlags::WAIT_FOR_COMPLETION),
|
||||
wait_dst_stage_mask,
|
||||
wait_semaphore,
|
||||
signal_semaphore,
|
||||
signal_fence);
|
||||
render_graph_.reset();
|
||||
streaming_buffers_.clear();
|
||||
if (bool(flags & RenderGraphFlushFlags::RENEW_RENDER_GRAPH)) {
|
||||
render_graph_ = std::reference_wrapper<render_graph::VKRenderGraph>(
|
||||
*device.render_graph_new());
|
||||
/* Recreate the debug group stack for the new graph.
|
||||
* Note: there is no associated `debug_group_end` as the graph groups
|
||||
* are implicitly closed on submission. */
|
||||
for (const StringRef &group : debug_stack) {
|
||||
std::string str_group = group;
|
||||
render_graph_.value().get().debug_group_begin(str_group.c_str(),
|
||||
debug::get_debug_group_color(str_group));
|
||||
}
|
||||
}
|
||||
return timeline;
|
||||
}
|
||||
|
||||
void VKContext::finish()
|
||||
{
|
||||
flush_render_graph(RenderGraphFlushFlags::SUBMIT | RenderGraphFlushFlags::WAIT_FOR_COMPLETION |
|
||||
RenderGraphFlushFlags::RENEW_RENDER_GRAPH);
|
||||
}
|
||||
|
||||
void VKContext::memory_statistics_get(int *r_total_mem_kb, int *r_free_mem_kb)
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
device.memory_statistics_get(r_total_mem_kb, r_free_mem_kb);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name State manager
|
||||
* \{ */
|
||||
|
||||
VKDescriptorPools &VKContext::descriptor_pools_get()
|
||||
{
|
||||
return thread_data_.value().get().descriptor_pools;
|
||||
}
|
||||
|
||||
VKDescriptorSetTracker &VKContext::descriptor_set_get()
|
||||
{
|
||||
return thread_data_.value().get().descriptor_set;
|
||||
}
|
||||
|
||||
VKStateManager &VKContext::state_manager_get() const
|
||||
{
|
||||
return *static_cast<VKStateManager *>(state_manager);
|
||||
}
|
||||
|
||||
void VKContext::debug_unbind_all_ubo()
|
||||
{
|
||||
state_manager_get().uniform_buffer_unbind_all();
|
||||
};
|
||||
|
||||
void VKContext::debug_unbind_all_ssbo()
|
||||
{
|
||||
state_manager_get().storage_buffer_unbind_all();
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Frame-buffer
|
||||
* \{ */
|
||||
|
||||
void VKContext::activate_framebuffer(VKFrameBuffer &framebuffer)
|
||||
{
|
||||
if (has_active_framebuffer()) {
|
||||
deactivate_framebuffer();
|
||||
}
|
||||
|
||||
BLI_assert(active_fb == nullptr);
|
||||
active_fb = &framebuffer;
|
||||
framebuffer.update_size();
|
||||
framebuffer.update_srgb();
|
||||
framebuffer.rendering_reset();
|
||||
}
|
||||
|
||||
VKFrameBuffer *VKContext::active_framebuffer_get() const
|
||||
{
|
||||
return unwrap(active_fb);
|
||||
}
|
||||
|
||||
bool VKContext::has_active_framebuffer() const
|
||||
{
|
||||
return active_framebuffer_get() != nullptr;
|
||||
}
|
||||
|
||||
void VKContext::deactivate_framebuffer()
|
||||
{
|
||||
VKFrameBuffer *framebuffer = active_framebuffer_get();
|
||||
BLI_assert(framebuffer != nullptr);
|
||||
if (framebuffer->is_rendering()) {
|
||||
framebuffer->rendering_end(*this);
|
||||
}
|
||||
active_fb = nullptr;
|
||||
}
|
||||
|
||||
void VKContext::rendering_end()
|
||||
{
|
||||
VKFrameBuffer *framebuffer = active_framebuffer_get();
|
||||
if (framebuffer) {
|
||||
framebuffer->rendering_end(*this);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Pipeline
|
||||
* \{ */
|
||||
|
||||
void VKContext::update_pipeline_data(const VKFrameBuffer &framebuffer,
|
||||
GPUPrimType primitive,
|
||||
VKVertexAttributeObject &vao,
|
||||
render_graph::VKPipelineDataGraphics &r_pipeline_data)
|
||||
{
|
||||
VKShader &vk_shader = unwrap(*shader);
|
||||
VKStateManager &state_manager = state_manager_get();
|
||||
|
||||
/* Disable non-vulkan state flags to reduce unneeded pipeline compilation. */
|
||||
state_manager.state.clip_control = 0;
|
||||
|
||||
framebuffer.vk_viewports_append(r_pipeline_data.viewport.viewports);
|
||||
framebuffer.vk_render_areas_append(r_pipeline_data.viewport.scissors);
|
||||
|
||||
/* Override size of point shader when GPU_point size < 0 */
|
||||
const float point_size = state_manager.mutable_state.point_size;
|
||||
if (primitive == GPU_PRIM_POINTS && point_size < 0.0) {
|
||||
GPU_shader_uniform_1f(shader, "size", -point_size);
|
||||
}
|
||||
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
const VKExtensions &extensions = device.extensions_get();
|
||||
|
||||
/* Dynamic state line width */
|
||||
const bool is_line_primitive = ELEM(primitive,
|
||||
GPU_PRIM_LINES,
|
||||
GPU_PRIM_LINE_LOOP,
|
||||
GPU_PRIM_LINE_STRIP,
|
||||
GPU_PRIM_LINES_ADJ,
|
||||
GPU_PRIM_LINE_STRIP_ADJ);
|
||||
if (is_line_primitive) {
|
||||
r_pipeline_data.line_width = extensions.wide_lines ? state_manager.mutable_state.line_width :
|
||||
1.0f;
|
||||
}
|
||||
else {
|
||||
r_pipeline_data.line_width.reset();
|
||||
}
|
||||
|
||||
/* Dynamic state stencil state */
|
||||
if (framebuffer.stencil_attachment_format_get() != VK_FORMAT_UNDEFINED &&
|
||||
state_manager.state.stencil_test != GPU_STENCIL_NONE)
|
||||
{
|
||||
r_pipeline_data.stencil_state = {state_manager.mutable_state.stencil_compare_mask,
|
||||
state_manager.mutable_state.stencil_reference,
|
||||
state_manager.mutable_state.stencil_write_mask};
|
||||
}
|
||||
else {
|
||||
r_pipeline_data.stencil_state.reset();
|
||||
}
|
||||
|
||||
/* VK_EXT_extended_dynamic_state */
|
||||
if (extensions.extended_dynamic_state) {
|
||||
r_pipeline_data.front_face = state_manager.state.invert_facing ?
|
||||
VK_FRONT_FACE_COUNTER_CLOCKWISE :
|
||||
VK_FRONT_FACE_CLOCKWISE;
|
||||
}
|
||||
|
||||
VKVertexInputDescriptionPool::Key vertex_input_description_key =
|
||||
device.vertex_input_descriptions.get_or_insert(vao.vertex_input);
|
||||
if (extensions.vertex_input_dynamic_state) {
|
||||
r_pipeline_data.vertex_input_description = vertex_input_description_key;
|
||||
}
|
||||
|
||||
update_pipeline_data(
|
||||
vk_shader,
|
||||
vk_shader.ensure_and_get_graphics_pipeline(
|
||||
primitive, vertex_input_description_key, state_manager, framebuffer, constants_state_),
|
||||
r_pipeline_data.pipeline_data);
|
||||
}
|
||||
|
||||
void VKContext::update_pipeline_data(render_graph::VKPipelineData &r_pipeline_data)
|
||||
{
|
||||
VKShader &vk_shader = unwrap(*shader);
|
||||
update_pipeline_data(
|
||||
vk_shader, vk_shader.ensure_and_get_compute_pipeline(constants_state_), r_pipeline_data);
|
||||
}
|
||||
|
||||
void VKContext::update_pipeline_data(VKShader &vk_shader,
|
||||
VkPipeline vk_pipeline,
|
||||
render_graph::VKPipelineData &r_pipeline_data)
|
||||
{
|
||||
r_pipeline_data.vk_pipeline_layout = vk_shader.vk_pipeline_layout;
|
||||
r_pipeline_data.vk_pipeline = vk_pipeline;
|
||||
|
||||
/* Update push constants. */
|
||||
r_pipeline_data.push_constants_range = IndexRange::from_begin_size(0, 0);
|
||||
const VKPushConstants::Layout &push_constants_layout =
|
||||
vk_shader.interface_get().push_constants_layout_get();
|
||||
if (push_constants_layout.storage_type_get() == VKPushConstants::StorageType::PUSH_CONSTANTS) {
|
||||
r_pipeline_data.push_constants_range = render_graph().copy_push_constants(
|
||||
Span<uint8_t>(static_cast<const uint8_t *>(vk_shader.push_constants.data()),
|
||||
push_constants_layout.size_in_bytes()));
|
||||
}
|
||||
|
||||
/* Update descriptor set. */
|
||||
r_pipeline_data.vk_descriptor_set = VK_NULL_HANDLE;
|
||||
if (vk_shader.has_descriptor_set()) {
|
||||
VKDescriptorSetTracker &descriptor_set = descriptor_set_get();
|
||||
descriptor_set.update_descriptor_set(*this, access_info_, r_pipeline_data);
|
||||
}
|
||||
}
|
||||
|
||||
render_graph::VKResourceAccessInfo &VKContext::reset_and_get_access_info()
|
||||
{
|
||||
access_info_.reset();
|
||||
return access_info_;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Graphics pipeline
|
||||
* \{ */
|
||||
|
||||
void VKContext::swap_buffer_acquired_callback()
|
||||
{
|
||||
VKContext *context = VKContext::get();
|
||||
BLI_assert(context);
|
||||
context->swap_buffer_acquired_handler();
|
||||
}
|
||||
|
||||
void VKContext::swap_buffer_draw_callback(const GHOST_VulkanSwapChainData *swap_chain_data,
|
||||
bool wait_for_submission)
|
||||
{
|
||||
VKContext *context = VKContext::get();
|
||||
BLI_assert(context);
|
||||
context->swap_buffer_draw_handler(*swap_chain_data, wait_for_submission);
|
||||
}
|
||||
|
||||
void VKContext::swap_buffer_acquired_handler()
|
||||
{
|
||||
sync_backbuffer();
|
||||
}
|
||||
|
||||
void VKContext::swap_buffer_draw_handler(const GHOST_VulkanSwapChainData &swap_chain_data,
|
||||
bool wait_for_submission)
|
||||
{
|
||||
const bool do_blit_to_swapchain = swap_chain_data.image != VK_NULL_HANDLE;
|
||||
const bool use_shader = ELEM(swap_chain_data.surface_format.colorSpace,
|
||||
VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT,
|
||||
VK_COLOR_SPACE_PASS_THROUGH_EXT);
|
||||
|
||||
/* When swapchain is invalid/minimized we only flush the render graph to free GPU resources. */
|
||||
if (!do_blit_to_swapchain) {
|
||||
flush_render_graph(RenderGraphFlushFlags::SUBMIT | RenderGraphFlushFlags::RENEW_RENDER_GRAPH);
|
||||
return;
|
||||
}
|
||||
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
render_graph::VKRenderGraph &render_graph = this->render_graph();
|
||||
VKFrameBuffer &framebuffer = *unwrap(active_fb);
|
||||
framebuffer.rendering_end(*this);
|
||||
VKTexture *color_attachment = unwrap(unwrap(framebuffer.color_tex(0)));
|
||||
device.resources.add_swapchain_image(swap_chain_data.image, "SwapchainImage");
|
||||
|
||||
GPU_debug_group_begin("BackBuffer.Blit");
|
||||
if (use_shader) {
|
||||
VKTexture swap_chain_texture("swap_chain_texture");
|
||||
swap_chain_texture.init_swapchain(swap_chain_data.image,
|
||||
to_gpu_format(swap_chain_data.surface_format.format));
|
||||
Shader *shader = device.vk_backbuffer_blit_sh_get();
|
||||
GPU_shader_bind(shader);
|
||||
GPU_shader_uniform_1f(shader, "sdr_scale", swap_chain_data.sdr_scale);
|
||||
VKStateManager &state_manager = state_manager_get();
|
||||
state_manager.image_bind(color_attachment, 0);
|
||||
state_manager.image_bind(&swap_chain_texture, 1);
|
||||
int2 dispatch_size = math::divide_ceil(
|
||||
int2(swap_chain_data.extent.width, swap_chain_data.extent.height), int2(16));
|
||||
VKBackend::get().compute_dispatch(UNPACK2(dispatch_size), 1);
|
||||
}
|
||||
else {
|
||||
render_graph::VKBlitImageNode::CreateInfo blit_image = {};
|
||||
blit_image.src_image = color_attachment->vk_image_handle();
|
||||
blit_image.dst_image = swap_chain_data.image;
|
||||
blit_image.filter = VK_FILTER_LINEAR;
|
||||
|
||||
VkImageBlit ®ion = blit_image.region;
|
||||
region.srcOffsets[0] = {0, 0, 0};
|
||||
region.srcOffsets[1] = {color_attachment->width_get(), color_attachment->height_get(), 1};
|
||||
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
region.srcSubresource.mipLevel = 0;
|
||||
region.srcSubresource.baseArrayLayer = 0;
|
||||
region.srcSubresource.layerCount = 1;
|
||||
|
||||
region.dstOffsets[0] = {0, int32_t(swap_chain_data.extent.height), 0};
|
||||
region.dstOffsets[1] = {int32_t(swap_chain_data.extent.width), 0, 1};
|
||||
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
region.dstSubresource.mipLevel = 0;
|
||||
region.dstSubresource.baseArrayLayer = 0;
|
||||
region.dstSubresource.layerCount = 1;
|
||||
|
||||
render_graph.add_node(blit_image);
|
||||
}
|
||||
|
||||
render_graph::VKSynchronizationNode::CreateInfo synchronization = {};
|
||||
synchronization.vk_image = swap_chain_data.image;
|
||||
synchronization.vk_image_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
synchronization.vk_image_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
render_graph.add_node(synchronization);
|
||||
GPU_debug_group_end();
|
||||
|
||||
wait_for_submission |= swap_chain_data.submission_fence != VK_NULL_HANDLE;
|
||||
flush_render_graph(RenderGraphFlushFlags::SUBMIT | RenderGraphFlushFlags::RENEW_RENDER_GRAPH |
|
||||
(wait_for_submission ? RenderGraphFlushFlags::WAIT_FOR_SUBMISSION :
|
||||
RenderGraphFlushFlags::NONE),
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
swap_chain_data.acquire_semaphore,
|
||||
swap_chain_data.present_semaphore,
|
||||
swap_chain_data.submission_fence);
|
||||
/* Discard/remove not owning swapchain handlers.
|
||||
* During a regular swapchain update, NVIDIA can use the same image multiple times in a row.
|
||||
* Placing these images in the discard pool results in incorrect state, best to remove them
|
||||
* directly. */
|
||||
if (wait_for_submission) {
|
||||
device.resources.remove_image(swap_chain_data.image);
|
||||
}
|
||||
else {
|
||||
discard_pool.discard_swapchain_image(swap_chain_data.image);
|
||||
}
|
||||
#if 0
|
||||
device.debug_print();
|
||||
#endif
|
||||
}
|
||||
|
||||
void VKContext::specialization_constants_set(
|
||||
const shader::SpecializationConstants *constants_state)
|
||||
{
|
||||
constants_state_ = (constants_state != nullptr) ? *constants_state :
|
||||
shader::SpecializationConstants{};
|
||||
}
|
||||
|
||||
std::unique_ptr<VKStreamingBuffer> &VKContext::get_or_create_streaming_buffer(
|
||||
VKBuffer &buffer, VkDeviceSize min_offset_alignment)
|
||||
{
|
||||
for (std::unique_ptr<VKStreamingBuffer> &streaming_buffer : streaming_buffers_) {
|
||||
if (streaming_buffer->vk_buffer_dst() == buffer.vk_handle()) {
|
||||
return streaming_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
streaming_buffers_.append(std::make_unique<VKStreamingBuffer>(buffer, min_offset_alignment));
|
||||
return streaming_buffers_.last();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name OpenXR
|
||||
* \{ */
|
||||
|
||||
void VKContext::openxr_acquire_framebuffer_image_callback(GHOST_VulkanOpenXRData *openxr_data)
|
||||
{
|
||||
VKContext *context = VKContext::get();
|
||||
BLI_assert(context);
|
||||
context->openxr_acquire_framebuffer_image_handler(*openxr_data);
|
||||
}
|
||||
|
||||
void VKContext::openxr_release_framebuffer_image_callback(GHOST_VulkanOpenXRData *openxr_data)
|
||||
{
|
||||
VKContext *context = VKContext::get();
|
||||
BLI_assert(context);
|
||||
context->openxr_release_framebuffer_image_handler(*openxr_data);
|
||||
}
|
||||
|
||||
void VKContext::openxr_acquire_framebuffer_image_handler(GHOST_VulkanOpenXRData &openxr_data)
|
||||
{
|
||||
VKFrameBuffer &framebuffer = *unwrap(active_fb);
|
||||
VKTexture *color_attachment = unwrap(unwrap(framebuffer.color_tex(0)));
|
||||
openxr_data.extent.width = color_attachment->width_get();
|
||||
openxr_data.extent.height = color_attachment->height_get();
|
||||
|
||||
/* Determine the data format for data transfer. */
|
||||
const TextureFormat device_format = color_attachment->device_format_get();
|
||||
eGPUDataFormat data_format = GPU_DATA_HALF_FLOAT;
|
||||
if (ELEM(device_format, TextureFormat::UNORM_8_8_8_8)) {
|
||||
data_format = GPU_DATA_UBYTE;
|
||||
}
|
||||
|
||||
switch (openxr_data.data_transfer_mode) {
|
||||
case GHOST_kVulkanXRModeCPU:
|
||||
openxr_data.cpu.image_data = MEM_new_uninitialized(
|
||||
color_attachment->read_size_get(0, data_format), __func__);
|
||||
color_attachment->read(0, data_format, openxr_data.cpu.image_data);
|
||||
break;
|
||||
|
||||
case GHOST_kVulkanXRModeFD: {
|
||||
flush_render_graph(RenderGraphFlushFlags::SUBMIT |
|
||||
RenderGraphFlushFlags::WAIT_FOR_COMPLETION |
|
||||
RenderGraphFlushFlags::RENEW_RENDER_GRAPH);
|
||||
if (openxr_data.gpu.vk_image_blender != color_attachment->vk_image_handle()) {
|
||||
VKMemoryExport exported_memory = color_attachment->export_memory(
|
||||
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT);
|
||||
openxr_data.gpu.image_handle = exported_memory.handle;
|
||||
openxr_data.gpu.new_handle = true;
|
||||
openxr_data.gpu.image_format = to_vk_format(color_attachment->device_format_get());
|
||||
openxr_data.gpu.memory_size = exported_memory.memory_size;
|
||||
openxr_data.gpu.memory_offset = exported_memory.memory_offset;
|
||||
openxr_data.gpu.vk_image_blender = color_attachment->vk_image_handle();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GHOST_kVulkanXRModeWin32: {
|
||||
flush_render_graph(RenderGraphFlushFlags::SUBMIT |
|
||||
RenderGraphFlushFlags::WAIT_FOR_COMPLETION |
|
||||
RenderGraphFlushFlags::RENEW_RENDER_GRAPH);
|
||||
if (openxr_data.gpu.vk_image_blender != color_attachment->vk_image_handle()) {
|
||||
VKMemoryExport exported_memory = color_attachment->export_memory(
|
||||
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT);
|
||||
openxr_data.gpu.image_handle = exported_memory.handle;
|
||||
openxr_data.gpu.new_handle = true;
|
||||
openxr_data.gpu.image_format = to_vk_format(color_attachment->device_format_get());
|
||||
openxr_data.gpu.memory_size = exported_memory.memory_size;
|
||||
openxr_data.gpu.memory_offset = exported_memory.memory_offset;
|
||||
openxr_data.gpu.vk_image_blender = color_attachment->vk_image_handle();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GHOST_kVulkanXRModeRenderGraph:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void VKContext::openxr_release_framebuffer_image_handler(GHOST_VulkanOpenXRData &openxr_data)
|
||||
{
|
||||
switch (openxr_data.data_transfer_mode) {
|
||||
case GHOST_kVulkanXRModeCPU:
|
||||
MEM_delete_void(openxr_data.cpu.image_data);
|
||||
openxr_data.cpu.image_data = nullptr;
|
||||
break;
|
||||
|
||||
case GHOST_kVulkanXRModeFD:
|
||||
/* Nothing to do as import of the handle by the XrInstance removes the ownership of the
|
||||
* handle. Ref
|
||||
* https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_external_memory_fd.html#_issues
|
||||
*/
|
||||
break;
|
||||
|
||||
case GHOST_kVulkanXRModeWin32:
|
||||
#ifdef _WIN32
|
||||
if (openxr_data.gpu.new_handle) {
|
||||
/* Exported handle isn't consumed during import and should be freed after use. */
|
||||
CloseHandle(HANDLE(openxr_data.gpu.image_handle));
|
||||
openxr_data.gpu.image_handle = 0;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case GHOST_kVulkanXRModeRenderGraph:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu
|
||||
186
blender-5.2.0/source/blender/gpu/vulkan/vk_context.hh
Normal file
186
blender-5.2.0/source/blender/gpu/vulkan/vk_context.hh
Normal file
@@ -0,0 +1,186 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_enum_flags.hh"
|
||||
|
||||
#include "gpu_context_private.hh"
|
||||
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
#include "render_graph/vk_render_graph.hh"
|
||||
#include "vk_buffer_pool.hh"
|
||||
#include "vk_common.hh"
|
||||
#include "vk_debug.hh"
|
||||
#include "vk_descriptor_pools.hh"
|
||||
#include "vk_resource_pool.hh"
|
||||
#include "vk_streaming_buffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
class VKFrameBuffer;
|
||||
class VKVertexAttributeObject;
|
||||
class VKBatch;
|
||||
class VKStateManager;
|
||||
class VKShader;
|
||||
class VKThreadData;
|
||||
class VKDevice;
|
||||
|
||||
enum RenderGraphFlushFlags {
|
||||
NONE = 0,
|
||||
RENEW_RENDER_GRAPH = 1 << 0,
|
||||
SUBMIT = 1 << 1,
|
||||
WAIT_FOR_SUBMISSION = 1 << 2,
|
||||
WAIT_FOR_COMPLETION = 1 << 3,
|
||||
};
|
||||
ENUM_OPERATORS(RenderGraphFlushFlags);
|
||||
|
||||
class VKContext : public Context, NonCopyable {
|
||||
friend class VKDevice;
|
||||
|
||||
private:
|
||||
VkExtent2D vk_extent_ = {};
|
||||
VkSurfaceFormatKHR swap_chain_format_ = {};
|
||||
gpu::Texture *surface_texture_ = nullptr;
|
||||
GHOST_IContext *ghost_context_;
|
||||
|
||||
Vector<std::unique_ptr<VKStreamingBuffer>> streaming_buffers_;
|
||||
|
||||
/* Reusable data. Stored inside context to limit reallocations. */
|
||||
render_graph::VKResourceAccessInfo access_info_ = {};
|
||||
|
||||
std::optional<std::reference_wrapper<VKThreadData>> thread_data_;
|
||||
std::optional<std::reference_wrapper<render_graph::VKRenderGraph>> render_graph_;
|
||||
|
||||
/* Active shader specialization constants state. */
|
||||
shader::SpecializationConstants constants_state_;
|
||||
|
||||
/* Debug scope timings. Adapted form GLContext::TimeQuery.
|
||||
* Only supports CPU timings for now. */
|
||||
struct ScopeTimings {
|
||||
using Clock = std::chrono::steady_clock;
|
||||
using TimePoint = Clock::time_point;
|
||||
using Nanoseconds = std::chrono::nanoseconds;
|
||||
|
||||
std::string name;
|
||||
bool finished;
|
||||
TimePoint cpu_start, cpu_end;
|
||||
};
|
||||
Vector<ScopeTimings> scope_timings;
|
||||
|
||||
void process_frame_timings();
|
||||
|
||||
public:
|
||||
VKDiscardPool discard_pool;
|
||||
VKBufferPool push_constants_pool;
|
||||
|
||||
const render_graph::VKRenderGraph &render_graph() const
|
||||
{
|
||||
return render_graph_.value().get();
|
||||
}
|
||||
render_graph::VKRenderGraph &render_graph()
|
||||
{
|
||||
return render_graph_.value().get();
|
||||
}
|
||||
|
||||
VKContext(GHOST_IWindow *ghost_window, GHOST_IContext *ghost_context);
|
||||
virtual ~VKContext();
|
||||
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
void begin_frame() override;
|
||||
void end_frame() override;
|
||||
|
||||
void flush() override;
|
||||
|
||||
TimelineValue flush_render_graph(
|
||||
RenderGraphFlushFlags flags,
|
||||
VkPipelineStageFlags wait_dst_stage_mask = VK_PIPELINE_STAGE_NONE,
|
||||
VkSemaphore wait_semaphore = VK_NULL_HANDLE,
|
||||
VkSemaphore signal_semaphore = VK_NULL_HANDLE,
|
||||
VkFence signal_fence = VK_NULL_HANDLE);
|
||||
void finish() override;
|
||||
|
||||
void memory_statistics_get(int *r_total_mem_kb, int *r_free_mem_kb) override;
|
||||
|
||||
void debug_group_begin(const char *name, int index) override;
|
||||
void debug_group_end() override;
|
||||
bool debug_capture_begin(const char *title) override;
|
||||
void debug_capture_end() override;
|
||||
void *debug_capture_scope_create(const char *name) override;
|
||||
bool debug_capture_scope_begin(void *scope) override;
|
||||
void debug_capture_scope_end(void *scope) override;
|
||||
|
||||
void debug_unbind_all_ubo() override;
|
||||
void debug_unbind_all_ssbo() override;
|
||||
|
||||
bool has_active_framebuffer() const;
|
||||
void activate_framebuffer(VKFrameBuffer &framebuffer);
|
||||
void deactivate_framebuffer();
|
||||
VKFrameBuffer *active_framebuffer_get() const;
|
||||
|
||||
/**
|
||||
* Ensure that the active framebuffer isn't rendering.
|
||||
*
|
||||
* Between `vkCmdBeginRendering` and `vkCmdEndRendering` the framebuffer is rendering. Dispatch
|
||||
* and transfer commands cannot be called between these commands. They can call this method to
|
||||
* ensure that the framebuffer is outside these calls.
|
||||
*/
|
||||
void rendering_end();
|
||||
|
||||
render_graph::VKResourceAccessInfo &reset_and_get_access_info();
|
||||
|
||||
/**
|
||||
* Update the give shader data with the current state of the context.
|
||||
*/
|
||||
void update_pipeline_data(render_graph::VKPipelineData &r_pipeline_data);
|
||||
void update_pipeline_data(const VKFrameBuffer &framebuffer,
|
||||
GPUPrimType primitive,
|
||||
VKVertexAttributeObject &vao,
|
||||
render_graph::VKPipelineDataGraphics &r_pipeline_data);
|
||||
|
||||
void sync_backbuffer();
|
||||
|
||||
static VKContext *get()
|
||||
{
|
||||
return static_cast<VKContext *>(Context::get());
|
||||
}
|
||||
|
||||
VKDescriptorPools &descriptor_pools_get();
|
||||
VKDescriptorSetTracker &descriptor_set_get();
|
||||
VKStateManager &state_manager_get() const;
|
||||
|
||||
static void swap_buffer_draw_callback(const GHOST_VulkanSwapChainData *data,
|
||||
bool wait_for_submission);
|
||||
static void swap_buffer_acquired_callback();
|
||||
static void openxr_acquire_framebuffer_image_callback(GHOST_VulkanOpenXRData *data);
|
||||
static void openxr_release_framebuffer_image_callback(GHOST_VulkanOpenXRData *data);
|
||||
|
||||
void specialization_constants_set(const shader::SpecializationConstants *constants_state);
|
||||
|
||||
std::unique_ptr<VKStreamingBuffer> &get_or_create_streaming_buffer(
|
||||
VKBuffer &buffer, VkDeviceSize min_offset_alignment);
|
||||
|
||||
private:
|
||||
void swap_buffer_draw_handler(const GHOST_VulkanSwapChainData &data, bool wait_for_submission);
|
||||
void swap_buffer_acquired_handler();
|
||||
|
||||
void openxr_acquire_framebuffer_image_handler(GHOST_VulkanOpenXRData &data);
|
||||
void openxr_release_framebuffer_image_handler(GHOST_VulkanOpenXRData &data);
|
||||
|
||||
void update_pipeline_data(VKShader &shader,
|
||||
VkPipeline vk_pipeline,
|
||||
render_graph::VKPipelineData &r_pipeline_data);
|
||||
};
|
||||
|
||||
BLI_INLINE bool operator==(const VKContext &a, const VKContext &b)
|
||||
{
|
||||
return static_cast<const void *>(&a) == static_cast<const void *>(&b);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
1216
blender-5.2.0/source/blender/gpu/vulkan/vk_data_conversion.cc
Normal file
1216
blender-5.2.0/source/blender/gpu/vulkan/vk_data_conversion.cc
Normal file
File diff suppressed because it is too large
Load Diff
240
blender-5.2.0/source/blender/gpu/vulkan/vk_data_conversion.hh
Normal file
240
blender-5.2.0/source/blender/gpu/vulkan/vk_data_conversion.hh
Normal file
@@ -0,0 +1,240 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "gpu_texture_private.hh"
|
||||
|
||||
#include "vk_common.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
struct VKWorkarounds;
|
||||
|
||||
/**
|
||||
* \brief Check if data conversion is needed when uploading host_format to a device_format.
|
||||
*
|
||||
* \param host_format: format of the host buffer.
|
||||
* \param host_texture_format: texture format of the host buffer.
|
||||
* \param device_format: format of the device buffer.
|
||||
*/
|
||||
bool needs_conversion(eGPUDataFormat host_format,
|
||||
TextureFormat host_texture_format,
|
||||
TextureFormat device_format);
|
||||
|
||||
/**
|
||||
* Convert host buffer to device buffer.
|
||||
*
|
||||
* \param dst_buffer: device buffer.
|
||||
* \param src_buffer: host buffer.
|
||||
* \param buffer_size: number of pixels to convert from the start of the given buffer.
|
||||
* \param host_format: format of the host buffer.
|
||||
* \param host_texture_format: texture format of the host buffer.
|
||||
* \param device_format: format of the device buffer.
|
||||
*
|
||||
* \note Will assert when the host_format/device_format combination isn't valid
|
||||
* (#validate_data_format) or supported. Some combinations aren't supported in Vulkan due to
|
||||
* platform incompatibility.
|
||||
*/
|
||||
void convert_host_to_device(void *dst_buffer,
|
||||
const void *src_buffer,
|
||||
size_t buffer_size,
|
||||
eGPUDataFormat host_format,
|
||||
TextureFormat host_texture_format,
|
||||
TextureFormat device_format);
|
||||
|
||||
/**
|
||||
* Convert device buffer to host buffer.
|
||||
*
|
||||
* \param dst_buffer: host buffer
|
||||
* \param src_buffer: device buffer.
|
||||
* \param buffer_size: number of pixels to convert from the start of the given buffer.
|
||||
* \param host_format: format of the host buffer
|
||||
* \param host_texture_format: texture format of the host buffer.
|
||||
* \param device_format: format of the device buffer.
|
||||
*
|
||||
* \note Will assert when the host_format/device_format combination isn't valid
|
||||
* (#validate_data_format) or supported. Some combinations aren't supported in Vulkan due to
|
||||
* platform incompatibility.
|
||||
*/
|
||||
void convert_device_to_host(void *dst_buffer,
|
||||
const void *src_buffer,
|
||||
size_t buffer_size,
|
||||
eGPUDataFormat host_format,
|
||||
TextureFormat host_texture_format,
|
||||
TextureFormat device_format);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Floating point conversions
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Description of a IEEE 754-1985 floating point data type.
|
||||
*/
|
||||
template<bool HasSignBit, uint8_t MantissaBitLen, uint8_t ExponentBitLen>
|
||||
class FloatingPointFormat {
|
||||
public:
|
||||
static constexpr bool HAS_SIGN = HasSignBit;
|
||||
static constexpr uint8_t SIGN_SHIFT = MantissaBitLen + ExponentBitLen;
|
||||
static constexpr uint32_t SIGN_MASK = HasSignBit ? 1 : 0;
|
||||
static constexpr uint8_t MANTISSA_LEN = MantissaBitLen;
|
||||
static constexpr uint8_t MANTISSA_SHIFT = 0;
|
||||
static constexpr uint32_t MANTISSA_MASK = (1 << MantissaBitLen) - 1;
|
||||
static constexpr uint32_t MANTISSA_NAN_MASK = MANTISSA_MASK;
|
||||
static constexpr uint8_t EXPONENT_SHIFT = MantissaBitLen;
|
||||
static constexpr uint8_t EXPONENT_LEN = ExponentBitLen;
|
||||
static constexpr uint32_t EXPONENT_MASK = (1 << ExponentBitLen) - 1;
|
||||
static constexpr int32_t EXPONENT_BIAS = (1 << (ExponentBitLen - 1)) - 1;
|
||||
static constexpr int32_t EXPONENT_SPECIAL_MASK = EXPONENT_MASK;
|
||||
|
||||
static uint32_t get_mantissa(uint32_t floating_point_number)
|
||||
{
|
||||
return (floating_point_number >> MANTISSA_SHIFT) & MANTISSA_MASK;
|
||||
}
|
||||
static uint32_t clear_mantissa(uint32_t floating_point_number)
|
||||
{
|
||||
return floating_point_number & ~(MANTISSA_MASK << MANTISSA_SHIFT);
|
||||
}
|
||||
static uint32_t set_mantissa(uint32_t mantissa, uint32_t floating_point_number)
|
||||
{
|
||||
uint32_t result = clear_mantissa(floating_point_number);
|
||||
result |= mantissa << MANTISSA_SHIFT;
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint32_t get_exponent(uint32_t floating_point_number)
|
||||
{
|
||||
return ((floating_point_number >> EXPONENT_SHIFT) & EXPONENT_MASK);
|
||||
}
|
||||
static uint32_t clear_exponent(uint32_t floating_point_number)
|
||||
{
|
||||
return floating_point_number & ~(EXPONENT_MASK << EXPONENT_SHIFT);
|
||||
}
|
||||
static uint32_t set_exponent(uint32_t exponent, uint32_t floating_point_number)
|
||||
{
|
||||
uint32_t result = clear_exponent(floating_point_number);
|
||||
result |= (exponent) << EXPONENT_SHIFT;
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool is_signed(uint32_t floating_point_number)
|
||||
{
|
||||
if constexpr (HasSignBit) {
|
||||
return (floating_point_number >> SIGN_SHIFT) & SIGN_MASK;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static uint32_t clear_sign(uint32_t floating_point_number)
|
||||
{
|
||||
return floating_point_number & ~(1 << SIGN_SHIFT);
|
||||
}
|
||||
|
||||
static uint32_t set_sign(bool sign, uint32_t floating_point_number)
|
||||
{
|
||||
if constexpr (!HasSignBit) {
|
||||
return floating_point_number;
|
||||
}
|
||||
uint32_t result = clear_sign(floating_point_number);
|
||||
result |= uint32_t(sign) << SIGN_SHIFT;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
using FormatF32 = FloatingPointFormat<true, 23, 8>;
|
||||
using FormatF11 = FloatingPointFormat<false, 6, 5>;
|
||||
using FormatF10 = FloatingPointFormat<false, 5, 5>;
|
||||
|
||||
/**
|
||||
* Convert between low precision floating (including 32 bit floats).
|
||||
*
|
||||
* The input and output values are bits (uint32_t) as this function does a bit-wise operations to
|
||||
* convert between the formats. Additional conversion rules can be applied to the conversion
|
||||
* function. Due to the implementation the compiler would make an optimized version depending on
|
||||
* the actual possibilities.
|
||||
*/
|
||||
template<
|
||||
/**
|
||||
* FloatingPointFormat of the value that is converted to.
|
||||
*/
|
||||
typename DestinationFormat,
|
||||
|
||||
/**
|
||||
* FloatingPointFormat of the value that is converted from.
|
||||
*/
|
||||
typename SourceFormat,
|
||||
|
||||
/**
|
||||
* Should negative values be clamped to zero when DestinationFormat doesn't contain a sign
|
||||
* bit. Also -Inf will be clamped to zero.
|
||||
*
|
||||
* When set to `false` and DestinationFormat doesn't contain a sign bit the value will be
|
||||
* made absolute.
|
||||
*/
|
||||
bool ClampNegativeToZero = true>
|
||||
uint32_t convert_float_formats(uint32_t value)
|
||||
{
|
||||
bool is_signed = SourceFormat::is_signed(value);
|
||||
uint32_t mantissa = SourceFormat::get_mantissa(value);
|
||||
int32_t exponent = SourceFormat::get_exponent(value);
|
||||
|
||||
const bool is_nan = (exponent == SourceFormat::EXPONENT_SPECIAL_MASK) && mantissa;
|
||||
const bool is_inf = (exponent == SourceFormat::EXPONENT_SPECIAL_MASK) && (mantissa == 0);
|
||||
const bool is_zero = (exponent == 0 && mantissa == 0);
|
||||
|
||||
/* Sign conversion */
|
||||
if constexpr (!DestinationFormat::HAS_SIGN && ClampNegativeToZero) {
|
||||
if (is_signed && !is_nan) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (is_zero) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (is_inf) {
|
||||
exponent = DestinationFormat::EXPONENT_SPECIAL_MASK;
|
||||
}
|
||||
else if (is_nan) {
|
||||
exponent = DestinationFormat::EXPONENT_SPECIAL_MASK;
|
||||
mantissa = DestinationFormat::MANTISSA_NAN_MASK;
|
||||
}
|
||||
else {
|
||||
/* Exponent conversion */
|
||||
exponent -= SourceFormat::EXPONENT_BIAS;
|
||||
/* Clamping when destination has lower precision. */
|
||||
if constexpr (SourceFormat::EXPONENT_LEN > DestinationFormat::EXPONENT_LEN) {
|
||||
if (exponent > DestinationFormat::EXPONENT_BIAS) {
|
||||
exponent = 0;
|
||||
mantissa = SourceFormat::MANTISSA_MASK;
|
||||
}
|
||||
else if (exponent < -DestinationFormat::EXPONENT_BIAS) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
exponent += DestinationFormat::EXPONENT_BIAS;
|
||||
|
||||
/* Mantissa conversion */
|
||||
if constexpr (SourceFormat::MANTISSA_LEN > DestinationFormat::MANTISSA_LEN) {
|
||||
mantissa = mantissa >> (SourceFormat::MANTISSA_LEN - DestinationFormat::MANTISSA_LEN);
|
||||
}
|
||||
else if constexpr (SourceFormat::MANTISSA_LEN < DestinationFormat::MANTISSA_LEN) {
|
||||
mantissa = mantissa << (DestinationFormat::MANTISSA_LEN - SourceFormat::MANTISSA_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t result = 0;
|
||||
result = DestinationFormat::set_sign(is_signed, result);
|
||||
result = DestinationFormat::set_exponent(exponent, result);
|
||||
result = DestinationFormat::set_mantissa(mantissa, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
}; // namespace blender::gpu
|
||||
300
blender-5.2.0/source/blender/gpu/vulkan/vk_debug.cc
Normal file
300
blender-5.2.0/source/blender/gpu/vulkan/vk_debug.cc
Normal file
@@ -0,0 +1,300 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "vk_backend.hh"
|
||||
#include "vk_context.hh"
|
||||
#include "vk_debug.hh"
|
||||
#include "vk_to_string.hh"
|
||||
|
||||
#include "gpu_profile_report.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static CLG_LogRef LOG = {"gpu.vulkan"};
|
||||
|
||||
namespace gpu {
|
||||
void VKContext::debug_group_begin(const char *name, int index)
|
||||
{
|
||||
UNUSED_VARS(index);
|
||||
render_graph().debug_group_begin(name, debug::get_debug_group_color(name));
|
||||
|
||||
if (!G.profile_gpu) {
|
||||
return;
|
||||
}
|
||||
|
||||
ScopeTimings timings = {};
|
||||
timings.name = name;
|
||||
timings.finished = false;
|
||||
timings.cpu_start = ScopeTimings::Clock::now();
|
||||
|
||||
scope_timings.append(timings);
|
||||
}
|
||||
|
||||
void VKContext::debug_group_end()
|
||||
{
|
||||
render_graph().debug_group_end();
|
||||
|
||||
if (!G.profile_gpu) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = scope_timings.size() - 1; i >= 0; i--) {
|
||||
ScopeTimings &query = scope_timings[i];
|
||||
if (!query.finished) {
|
||||
query.finished = true;
|
||||
query.cpu_end = ScopeTimings::Clock::now();
|
||||
break;
|
||||
}
|
||||
if (i == 0) {
|
||||
CLOG_ERROR(&LOG, "Profile GPU error: Extra GPU_debug_group_end() call.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VKContext::process_frame_timings()
|
||||
{
|
||||
if (!G.profile_gpu) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector<ScopeTimings> &queries = scope_timings;
|
||||
|
||||
bool frame_is_valid = !queries.is_empty();
|
||||
|
||||
for (int i = queries.size() - 1; i >= 0; i--) {
|
||||
if (!queries[i].finished) {
|
||||
frame_is_valid = false;
|
||||
CLOG_ERROR(&LOG, "Profile GPU error: Missing GPU_debug_group_end() call");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!frame_is_valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (ScopeTimings &query : queries) {
|
||||
ProfileReport::get().add_group_cpu(query.name,
|
||||
query.cpu_start.time_since_epoch().count(),
|
||||
query.cpu_end.time_since_epoch().count());
|
||||
}
|
||||
|
||||
queries.clear();
|
||||
}
|
||||
|
||||
bool VKContext::debug_capture_begin(const char *title)
|
||||
{
|
||||
return VKBackend::get().debug_capture_begin(title);
|
||||
}
|
||||
|
||||
bool VKBackend::debug_capture_begin(const char *title)
|
||||
{
|
||||
#ifdef WITH_RENDERDOC
|
||||
bool result = renderdoc_api_.start_frame_capture(device.instance_get(), nullptr);
|
||||
if (result && title) {
|
||||
renderdoc_api_.set_frame_capture_title(title);
|
||||
}
|
||||
return result;
|
||||
#else
|
||||
UNUSED_VARS(title);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void VKContext::debug_capture_end()
|
||||
{
|
||||
VKBackend::get().debug_capture_end();
|
||||
}
|
||||
|
||||
void VKBackend::debug_capture_end()
|
||||
{
|
||||
#ifdef WITH_RENDERDOC
|
||||
renderdoc_api_.end_frame_capture(device.instance_get(), nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void *VKContext::debug_capture_scope_create(const char *name)
|
||||
{
|
||||
return (void *)name;
|
||||
}
|
||||
|
||||
bool VKContext::debug_capture_scope_begin(void *scope)
|
||||
{
|
||||
#ifdef WITH_RENDERDOC
|
||||
const char *title = (const char *)scope;
|
||||
if (StringRefNull(title) != StringRefNull(G.gpu_debug_scope_name)) {
|
||||
return false;
|
||||
}
|
||||
VKBackend::get().debug_capture_begin(title);
|
||||
#else
|
||||
UNUSED_VARS(scope);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void VKContext::debug_capture_scope_end(void *scope)
|
||||
{
|
||||
#ifdef WITH_RENDERDOC
|
||||
const char *title = (const char *)scope;
|
||||
if (StringRefNull(title) == StringRefNull(G.gpu_debug_scope_name)) {
|
||||
VKBackend::get().debug_capture_end();
|
||||
}
|
||||
#else
|
||||
UNUSED_VARS(scope);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
namespace gpu::debug {
|
||||
|
||||
void VKDebuggingTools::init(VkInstance vk_instance)
|
||||
{
|
||||
CLG_logref_init(&LOG);
|
||||
init_messenger(vk_instance);
|
||||
}
|
||||
|
||||
void VKDebuggingTools::deinit(VkInstance vk_instance)
|
||||
{
|
||||
destroy_messenger(vk_instance);
|
||||
}
|
||||
|
||||
void object_label(VkObjectType vk_object_type, uint64_t object_handle, const char *name)
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
if (G.debug & G_DEBUG_GPU && device.functions.vkSetDebugUtilsObjectName && object_handle != 0) {
|
||||
VkDebugUtilsObjectNameInfoEXT info = {};
|
||||
info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
|
||||
info.objectType = vk_object_type;
|
||||
info.objectHandle = object_handle;
|
||||
info.pObjectName = name;
|
||||
device.functions.vkSetDebugUtilsObjectName(device.vk_handle(), &info);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gpu::debug
|
||||
|
||||
namespace gpu::debug {
|
||||
|
||||
void VKDebuggingTools::print_labels(const VkDebugUtilsMessengerCallbackDataEXT *callback_data)
|
||||
{
|
||||
std::stringstream ss;
|
||||
for (uint32_t object = 0; object < callback_data->objectCount; ++object) {
|
||||
ss << " - ObjectType[" << to_string(callback_data->pObjects[object].objectType) << "],";
|
||||
ss << "Handle[0x" << std::hex << uintptr_t(callback_data->pObjects[object].objectHandle)
|
||||
<< "]";
|
||||
if (callback_data->pObjects[object].pObjectName) {
|
||||
ss << ",Name[" << callback_data->pObjects[object].pObjectName << "]";
|
||||
}
|
||||
ss << std::endl;
|
||||
}
|
||||
for (uint32_t label = 0; label < callback_data->cmdBufLabelCount; ++label) {
|
||||
if (callback_data->pCmdBufLabels[label].pLabelName) {
|
||||
ss << " - CommandBuffer : " << callback_data->pCmdBufLabels[label].pLabelName << std::endl;
|
||||
}
|
||||
}
|
||||
for (uint32_t label = 0; label < callback_data->queueLabelCount; ++label) {
|
||||
if (callback_data->pQueueLabels[label].pLabelName) {
|
||||
ss << " - Queue : " << callback_data->pQueueLabels[label].pLabelName << std::endl;
|
||||
}
|
||||
}
|
||||
ss << std::endl;
|
||||
printf("%s", ss.str().c_str());
|
||||
}
|
||||
|
||||
static VKAPI_ATTR VkBool32 VKAPI_CALL
|
||||
messenger_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT message_type,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT *callback_data,
|
||||
void *user_data)
|
||||
{
|
||||
CLG_Level level = CLG_LEVEL_INFO;
|
||||
if (message_severity & (VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT))
|
||||
{
|
||||
level = CLG_LEVEL_INFO;
|
||||
}
|
||||
if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
|
||||
level = CLG_LEVEL_WARN;
|
||||
}
|
||||
if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
|
||||
level = CLG_LEVEL_ERROR;
|
||||
}
|
||||
|
||||
if (bool(message_type & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)) {
|
||||
const char *format = "{0x%x}%s\n %s";
|
||||
CLOG_AT_LEVEL(&LOG,
|
||||
level,
|
||||
format,
|
||||
callback_data->messageIdNumber,
|
||||
callback_data->pMessageIdName,
|
||||
callback_data->pMessage);
|
||||
const bool do_labels = (callback_data->objectCount + callback_data->cmdBufLabelCount +
|
||||
callback_data->queueLabelCount) > 0;
|
||||
const bool log_active = CLOG_CHECK(&LOG, level);
|
||||
if (do_labels && log_active) {
|
||||
VKDebuggingTools &debugging_tools = *reinterpret_cast<VKDebuggingTools *>(user_data);
|
||||
debugging_tools.print_labels(callback_data);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const char *format = "%s: %s";
|
||||
CLOG_AT_LEVEL(&LOG, level, format, callback_data->pMessageIdName, callback_data->pMessage);
|
||||
}
|
||||
|
||||
return VK_FALSE;
|
||||
};
|
||||
|
||||
void VKDebuggingTools::init_messenger(VkInstance vk_instance)
|
||||
{
|
||||
if (vk_debug_utils_messenger != VK_NULL_HANDLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
if (!device.functions.vkCreateDebugUtilsMessenger) {
|
||||
return;
|
||||
}
|
||||
|
||||
VkDebugUtilsMessengerCreateInfoEXT create_info;
|
||||
create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
||||
create_info.pNext = nullptr;
|
||||
create_info.flags = 0;
|
||||
create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
||||
create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
|
||||
create_info.pfnUserCallback = messenger_callback;
|
||||
create_info.pUserData = this;
|
||||
device.functions.vkCreateDebugUtilsMessenger(
|
||||
vk_instance, &create_info, nullptr, &vk_debug_utils_messenger);
|
||||
}
|
||||
|
||||
void VKDebuggingTools::destroy_messenger(VkInstance vk_instance)
|
||||
{
|
||||
if (vk_debug_utils_messenger == VK_NULL_HANDLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
device.functions.vkDestroyDebugUtilsMessenger(vk_instance, vk_debug_utils_messenger, nullptr);
|
||||
vk_debug_utils_messenger = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
}; // namespace gpu::debug
|
||||
|
||||
} // namespace blender
|
||||
64
blender-5.2.0/source/blender/gpu/vulkan/vk_debug.hh
Normal file
64
blender-5.2.0/source/blender/gpu/vulkan/vk_debug.hh
Normal file
@@ -0,0 +1,64 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "vk_common.hh"
|
||||
|
||||
#include <typeindex>
|
||||
|
||||
namespace blender::gpu {
|
||||
class VKContext;
|
||||
class VKDevice;
|
||||
|
||||
namespace debug {
|
||||
class VKDebuggingTools {
|
||||
public:
|
||||
void init(VkInstance vk_instance);
|
||||
void deinit(VkInstance vk_instance);
|
||||
void init_messenger(VkInstance vk_instance);
|
||||
void destroy_messenger(VkInstance vk_instance);
|
||||
void print_labels(const VkDebugUtilsMessengerCallbackDataEXT *callback_data);
|
||||
|
||||
private:
|
||||
VkDebugUtilsMessengerEXT vk_debug_utils_messenger = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
void object_label(VkObjectType vk_object_type, uint64_t object_handle, const char *name);
|
||||
template<typename T> void object_label(T vk_object_type, const StringRefNull name)
|
||||
{
|
||||
if (!(G.debug & G_DEBUG_GPU)) {
|
||||
return;
|
||||
}
|
||||
const size_t label_size = 64;
|
||||
char label[label_size];
|
||||
memset(label, 0, label_size);
|
||||
static int stats = 0;
|
||||
SNPRINTF(label, "%s_%d", name.c_str(), stats++);
|
||||
object_label(to_vk_object_type(vk_object_type),
|
||||
uint64_t(vk_object_type),
|
||||
const_cast<const char *>(label));
|
||||
};
|
||||
|
||||
/**
|
||||
* How to use:
|
||||
* \code{.cc}
|
||||
* debug::raise_message(0xB41ca2, VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
|
||||
* "This is a raise message. %llx", (uintptr_t)vk_object);
|
||||
* \endcode
|
||||
*/
|
||||
void raise_message(int32_t id_number,
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT vk_severity_flag_bits,
|
||||
const char *fmt,
|
||||
...) ATTR_PRINTF_FORMAT(3, 4);
|
||||
} // namespace debug
|
||||
} // namespace blender::gpu
|
||||
102
blender-5.2.0/source/blender/gpu/vulkan/vk_descriptor_pools.cc
Normal file
102
blender-5.2.0/source/blender/gpu/vulkan/vk_descriptor_pools.cc
Normal file
@@ -0,0 +1,102 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "vk_descriptor_pools.hh"
|
||||
#include "vk_backend.hh"
|
||||
#include "vk_context.hh"
|
||||
#include "vk_device.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
VKDescriptorPools::~VKDescriptorPools()
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
for (const VkDescriptorPool vk_descriptor_pool : recycled_pools_) {
|
||||
vkDestroyDescriptorPool(device.vk_handle(), vk_descriptor_pool, nullptr);
|
||||
}
|
||||
recycled_pools_.clear();
|
||||
if (vk_descriptor_pool_ != VK_NULL_HANDLE) {
|
||||
vkDestroyDescriptorPool(device.vk_handle(), vk_descriptor_pool_, nullptr);
|
||||
vk_descriptor_pool_ = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void VKDescriptorPools::init(const VKDevice &device)
|
||||
{
|
||||
ensure_pool(device);
|
||||
}
|
||||
|
||||
void VKDescriptorPools::ensure_pool(const VKDevice &device)
|
||||
{
|
||||
if (vk_descriptor_pool_ != VK_NULL_HANDLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::scoped_lock lock(mutex_);
|
||||
if (!recycled_pools_.is_empty()) {
|
||||
vk_descriptor_pool_ = recycled_pools_.pop_last();
|
||||
return;
|
||||
}
|
||||
|
||||
Vector<VkDescriptorPoolSize> pool_sizes = {
|
||||
{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, POOL_SIZE_STORAGE_BUFFER},
|
||||
{VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, POOL_SIZE_STORAGE_IMAGE},
|
||||
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, POOL_SIZE_COMBINED_IMAGE_SAMPLER},
|
||||
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, POOL_SIZE_UNIFORM_BUFFER},
|
||||
{VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, POOL_SIZE_UNIFORM_TEXEL_BUFFER},
|
||||
{VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, POOL_SIZE_INPUT_ATTACHMENT}};
|
||||
VkDescriptorPoolCreateInfo pool_info = {};
|
||||
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
pool_info.maxSets = POOL_SIZE_DESCRIPTOR_SETS;
|
||||
pool_info.poolSizeCount = pool_sizes.size();
|
||||
pool_info.pPoolSizes = pool_sizes.data();
|
||||
vkCreateDescriptorPool(device.vk_handle(), &pool_info, nullptr, &vk_descriptor_pool_);
|
||||
}
|
||||
|
||||
void VKDescriptorPools::discard_active_pool(VKContext &context)
|
||||
{
|
||||
context.discard_pool.discard_descriptor_pool_for_reuse(vk_descriptor_pool_, this);
|
||||
vk_descriptor_pool_ = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void VKDescriptorPools::recycle(VkDescriptorPool vk_descriptor_pool)
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
vkResetDescriptorPool(device.vk_handle(), vk_descriptor_pool, 0);
|
||||
std::scoped_lock lock(mutex_);
|
||||
recycled_pools_.append(vk_descriptor_pool);
|
||||
}
|
||||
|
||||
VkDescriptorSet VKDescriptorPools::allocate(const VkDescriptorSetLayout descriptor_set_layout)
|
||||
{
|
||||
BLI_assert(descriptor_set_layout != VK_NULL_HANDLE);
|
||||
BLI_assert(vk_descriptor_pool_ != VK_NULL_HANDLE);
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
|
||||
VkDescriptorSetAllocateInfo allocate_info = {};
|
||||
allocate_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
allocate_info.descriptorPool = vk_descriptor_pool_;
|
||||
allocate_info.descriptorSetCount = 1;
|
||||
allocate_info.pSetLayouts = &descriptor_set_layout;
|
||||
VkDescriptorSet vk_descriptor_set = VK_NULL_HANDLE;
|
||||
VkResult result = vkAllocateDescriptorSets(
|
||||
device.vk_handle(), &allocate_info, &vk_descriptor_set);
|
||||
|
||||
if (ELEM(result, VK_ERROR_OUT_OF_POOL_MEMORY, VK_ERROR_FRAGMENTED_POOL)) {
|
||||
{
|
||||
VKContext &context = *VKContext::get();
|
||||
discard_active_pool(context);
|
||||
ensure_pool(device);
|
||||
}
|
||||
return allocate(descriptor_set_layout);
|
||||
}
|
||||
|
||||
return vk_descriptor_set;
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
@@ -0,0 +1,76 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "vk_descriptor_set.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
class VKDevice;
|
||||
|
||||
/**
|
||||
* List of VkDescriptorPools.
|
||||
*
|
||||
* In Vulkan a pool is constructed with a fixed size per resource type. When more resources are
|
||||
* needed it a next pool should be created. VKDescriptorPools will keep track of those pools and
|
||||
* construct new pools when the previous one is exhausted.
|
||||
*/
|
||||
class VKDescriptorPools {
|
||||
/**
|
||||
* Pool sizes to use. When one descriptor pool is requested to allocate a descriptor but isn't
|
||||
* able to do so, it will fail.
|
||||
*
|
||||
* See VKDescriptorSetTracker::upload_descriptor_sets for rebalancing the pool sizes.
|
||||
*/
|
||||
static constexpr uint32_t POOL_SIZE_STORAGE_BUFFER = 1000;
|
||||
static constexpr uint32_t POOL_SIZE_DESCRIPTOR_SETS = 250;
|
||||
static constexpr uint32_t POOL_SIZE_STORAGE_IMAGE = 250;
|
||||
static constexpr uint32_t POOL_SIZE_COMBINED_IMAGE_SAMPLER = 250;
|
||||
static constexpr uint32_t POOL_SIZE_UNIFORM_BUFFER = 500;
|
||||
static constexpr uint32_t POOL_SIZE_UNIFORM_TEXEL_BUFFER = 100;
|
||||
static constexpr uint32_t POOL_SIZE_INPUT_ATTACHMENT = 100;
|
||||
|
||||
/**
|
||||
* Unused recycled pools.
|
||||
*
|
||||
* When a pool is full it is being discarded (for reuse). After all descriptor sets of the pool
|
||||
* are unused the descriptor pool can be reused.
|
||||
* Note: descriptor pools/sets are pinned to a single thread so the pools should always return to
|
||||
* the instance it was created on.
|
||||
*/
|
||||
Vector<VkDescriptorPool> recycled_pools_;
|
||||
/** Active descriptor pool. Should always be a valid handle. */
|
||||
VkDescriptorPool vk_descriptor_pool_ = VK_NULL_HANDLE;
|
||||
Mutex mutex_;
|
||||
|
||||
public:
|
||||
~VKDescriptorPools();
|
||||
|
||||
void init(const VKDevice &vk_device);
|
||||
|
||||
/**
|
||||
* Allocate a new descriptor set.
|
||||
*
|
||||
* When the active descriptor pool is full it is discarded and another descriptor pool is
|
||||
* ensured.
|
||||
*/
|
||||
VkDescriptorSet allocate(const VkDescriptorSetLayout descriptor_set_layout);
|
||||
|
||||
/**
|
||||
* Recycle a previous discarded descriptor pool.
|
||||
*/
|
||||
void recycle(VkDescriptorPool vk_descriptor_pool);
|
||||
|
||||
private:
|
||||
void add_new_pool(const VKDevice &device);
|
||||
void discard_active_pool(VKContext &vk_context);
|
||||
void ensure_pool(const VKDevice &device);
|
||||
};
|
||||
} // namespace blender::gpu
|
||||
695
blender-5.2.0/source/blender/gpu/vulkan/vk_descriptor_set.cc
Normal file
695
blender-5.2.0/source/blender/gpu/vulkan/vk_descriptor_set.cc
Normal file
@@ -0,0 +1,695 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "vk_descriptor_set.hh"
|
||||
#include "vk_buffer.hh"
|
||||
#include "vk_index_buffer.hh"
|
||||
#include "vk_shader.hh"
|
||||
#include "vk_shader_interface.hh"
|
||||
#include "vk_state_manager.hh"
|
||||
#include "vk_storage_buffer.hh"
|
||||
#include "vk_texture.hh"
|
||||
#include "vk_vertex_buffer.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
static CLG_LogRef LOG = {"gpu.vulkan"};
|
||||
|
||||
void VKDescriptorSetTracker::update_descriptor_set(VKContext &context,
|
||||
render_graph::VKResourceAccessInfo &access_info,
|
||||
render_graph::VKPipelineData &r_pipeline_data)
|
||||
{
|
||||
VKShader &shader = *unwrap(context.shader);
|
||||
VKStateManager &state_manager = context.state_manager_get();
|
||||
|
||||
/* Need to know the exact buffer and offset when shader uses an uniform buffer to store push
|
||||
* constants. */
|
||||
VKBufferWithOffset push_constants_buffer = {};
|
||||
/* Bind uniform push constants to descriptor set. */
|
||||
if (shader.push_constants.layout_get().storage_type_get() ==
|
||||
VKPushConstants::StorageType::BUFFER)
|
||||
{
|
||||
push_constants_buffer = shader.push_constants.update_uniform_buffer(context);
|
||||
}
|
||||
|
||||
update_resource_access_info(context, access_info, push_constants_buffer);
|
||||
|
||||
/* Can we reuse previous descriptor set. */
|
||||
const VkDescriptorSetLayout shader_descriptor_set_layout = shader.vk_descriptor_set_layout_get();
|
||||
if (!state_manager.is_dirty && vk_descriptor_set_layout_ == shader_descriptor_set_layout &&
|
||||
shader.push_constants.layout_get().storage_type_get() !=
|
||||
VKPushConstants::StorageType::BUFFER)
|
||||
{
|
||||
r_pipeline_data.vk_descriptor_set = descriptor_sets.vk_descriptor_set;
|
||||
return;
|
||||
}
|
||||
vk_descriptor_set_layout_ = shader_descriptor_set_layout;
|
||||
state_manager.is_dirty = false;
|
||||
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
VkDescriptorSetLayout vk_descriptor_set_layout = shader.vk_descriptor_set_layout_get();
|
||||
descriptor_sets.allocate_new_descriptor_set(
|
||||
device, context, shader, vk_descriptor_set_layout, r_pipeline_data);
|
||||
descriptor_sets.bind_shader_resources(device, state_manager, shader, push_constants_buffer);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Update resource access info
|
||||
* \{ */
|
||||
|
||||
void VKDescriptorSetTracker::update_resource_access_info_binding_uniform_buffer(
|
||||
const VKStateManager &state_manager,
|
||||
const VKResourceBinding &resource_binding,
|
||||
render_graph::VKResourceAccessInfo &access_info)
|
||||
{
|
||||
VKUniformBuffer &uniform_buffer = *state_manager.uniform_buffers_.get(resource_binding.binding);
|
||||
uniform_buffer.ensure_updated();
|
||||
access_info.buffers.append({uniform_buffer.vk_handle(), resource_binding.access_mask});
|
||||
}
|
||||
|
||||
void VKDescriptorSetTracker::update_resource_access_info_binding_storage_buffer(
|
||||
const VKStateManager &state_manager,
|
||||
const VKResourceBinding &resource_binding,
|
||||
render_graph::VKResourceAccessInfo &access_info)
|
||||
{
|
||||
const BindSpaceStorageBuffers::Elem &elem = state_manager.storage_buffers_.get(
|
||||
resource_binding.binding);
|
||||
VkBuffer vk_buffer = VK_NULL_HANDLE;
|
||||
switch (elem.resource_type) {
|
||||
case BindSpaceStorageBuffers::Type::IndexBuffer: {
|
||||
VKIndexBuffer *index_buffer = static_cast<VKIndexBuffer *>(elem.resource);
|
||||
index_buffer->ensure_updated();
|
||||
vk_buffer = index_buffer->vk_handle();
|
||||
break;
|
||||
}
|
||||
case BindSpaceStorageBuffers::Type::VertexBuffer: {
|
||||
VKVertexBuffer *vertex_buffer = static_cast<VKVertexBuffer *>(elem.resource);
|
||||
vertex_buffer->ensure_updated();
|
||||
vk_buffer = vertex_buffer->vk_handle();
|
||||
break;
|
||||
}
|
||||
case BindSpaceStorageBuffers::Type::UniformBuffer: {
|
||||
VKUniformBuffer *uniform_buffer = static_cast<VKUniformBuffer *>(elem.resource);
|
||||
uniform_buffer->ensure_updated();
|
||||
vk_buffer = uniform_buffer->vk_handle();
|
||||
break;
|
||||
}
|
||||
case BindSpaceStorageBuffers::Type::StorageBuffer: {
|
||||
VKStorageBuffer *storage_buffer = static_cast<VKStorageBuffer *>(elem.resource);
|
||||
storage_buffer->ensure_allocated();
|
||||
vk_buffer = storage_buffer->vk_handle();
|
||||
break;
|
||||
}
|
||||
case BindSpaceStorageBuffers::Type::Buffer: {
|
||||
VKBuffer *buffer = static_cast<VKBuffer *>(elem.resource);
|
||||
vk_buffer = buffer->vk_handle();
|
||||
break;
|
||||
}
|
||||
case BindSpaceStorageBuffers::Type::Unused: {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
if (vk_buffer != VK_NULL_HANDLE) {
|
||||
access_info.buffers.append({vk_buffer, resource_binding.access_mask});
|
||||
}
|
||||
}
|
||||
|
||||
void VKDescriptorSetTracker::update_resource_access_info_binding_sampler(
|
||||
const VKStateManager &state_manager,
|
||||
const VKResourceBinding &resource_binding,
|
||||
render_graph::VKResourceAccessInfo &access_info)
|
||||
{
|
||||
const BindSpaceTextures::Elem *elem_ptr = state_manager.textures_.get(resource_binding.binding);
|
||||
if (!elem_ptr || elem_ptr->resource == nullptr) {
|
||||
/* Unbound resource. */
|
||||
if (bool(G.debug & G_DEBUG_GPU)) {
|
||||
VKContext &context = *VKContext::get();
|
||||
const VKShader &shader = *static_cast<const VKShader *>(context.shader);
|
||||
const VKShaderInterface &interface = shader.interface_get();
|
||||
const ShaderInput &shader_input = *interface.texture_get(resource_binding.binding);
|
||||
CLOG_ERROR(&LOG,
|
||||
"Missing Texture bind at slot %d : %s > %s",
|
||||
shader_input.binding,
|
||||
shader.name_get().c_str(),
|
||||
interface.input_name_get(&shader_input));
|
||||
}
|
||||
return;
|
||||
}
|
||||
const BindSpaceTextures::Elem &elem = *elem_ptr;
|
||||
switch (elem.resource_type) {
|
||||
case BindSpaceTextures::Type::VertexBuffer: {
|
||||
VKVertexBuffer &vertex_buffer = *static_cast<VKVertexBuffer *>(elem.resource);
|
||||
vertex_buffer.ensure_updated();
|
||||
access_info.buffers.append({vertex_buffer.vk_handle(), resource_binding.access_mask});
|
||||
break;
|
||||
}
|
||||
case BindSpaceTextures::Type::Texture: {
|
||||
VKTexture *texture = static_cast<VKTexture *>(elem.resource);
|
||||
if (texture->type_ == GPU_TEXTURE_BUFFER) {
|
||||
VKVertexBuffer &vertex_buffer = *texture->source_buffer_;
|
||||
vertex_buffer.ensure_updated();
|
||||
access_info.buffers.append({vertex_buffer.vk_handle(), resource_binding.access_mask});
|
||||
}
|
||||
else {
|
||||
VKSubImageRange subimage = {};
|
||||
if (texture->is_texture_view()) {
|
||||
IndexRange layer_range = texture->layer_range();
|
||||
IndexRange mipmap_range = texture->mip_map_range();
|
||||
subimage = {uint32_t(mipmap_range.start()),
|
||||
uint32_t(mipmap_range.size()),
|
||||
uint32_t(layer_range.start()),
|
||||
uint32_t(layer_range.size())};
|
||||
}
|
||||
access_info.images.append({texture->vk_image_handle(),
|
||||
resource_binding.access_mask,
|
||||
to_vk_image_aspect_flag_bits(texture->device_format_get()),
|
||||
subimage});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BindSpaceTextures::Type::Unused: {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VKDescriptorSetTracker::update_resource_access_info_binding_image(
|
||||
const VKStateManager &state_manager,
|
||||
const VKResourceBinding &resource_binding,
|
||||
render_graph::VKResourceAccessInfo &access_info)
|
||||
{
|
||||
VKTexture &texture = *state_manager.images_.get(resource_binding.binding);
|
||||
VKSubImageRange subimage = {};
|
||||
if (texture.is_texture_view()) {
|
||||
IndexRange layer_range = texture.layer_range();
|
||||
IndexRange mipmap_range = texture.mip_map_range();
|
||||
subimage = {uint32_t(mipmap_range.start()),
|
||||
uint32_t(mipmap_range.size()),
|
||||
uint32_t(layer_range.start()),
|
||||
uint32_t(layer_range.size())};
|
||||
}
|
||||
access_info.images.append({texture.vk_image_handle(),
|
||||
resource_binding.access_mask,
|
||||
to_vk_image_aspect_flag_bits(texture.device_format_get()),
|
||||
subimage});
|
||||
}
|
||||
|
||||
void VKDescriptorSetTracker::update_resource_access_info_binding_input_attachment(
|
||||
const VKStateManager &state_manager,
|
||||
const VKResourceBinding &resource_binding,
|
||||
render_graph::VKResourceAccessInfo &access_info)
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
VKTexture *texture = nullptr;
|
||||
if (device.extensions_get().dynamic_rendering_local_read) {
|
||||
texture = state_manager.images_.get(resource_binding.binding);
|
||||
}
|
||||
else {
|
||||
texture = static_cast<VKTexture *>(
|
||||
state_manager.textures_.get(resource_binding.binding)->resource);
|
||||
}
|
||||
|
||||
BLI_assert(texture);
|
||||
VkImage vk_image = texture->vk_image_handle();
|
||||
if (vk_image != VK_NULL_HANDLE) {
|
||||
VKSubImageRange subimage = {};
|
||||
if (texture->is_texture_view()) {
|
||||
IndexRange layer_range = texture->layer_range();
|
||||
IndexRange mipmap_range = texture->mip_map_range();
|
||||
subimage = {uint32_t(mipmap_range.start()),
|
||||
uint32_t(mipmap_range.size()),
|
||||
uint32_t(layer_range.start()),
|
||||
uint32_t(layer_range.size())};
|
||||
}
|
||||
|
||||
access_info.images.append({texture->vk_image_handle(),
|
||||
resource_binding.access_mask,
|
||||
to_vk_image_aspect_flag_bits(texture->device_format_get()),
|
||||
subimage});
|
||||
}
|
||||
}
|
||||
|
||||
void VKDescriptorSetTracker::update_resource_access_info_binding(
|
||||
const VKStateManager &state_manager,
|
||||
const VKResourceBinding &resource_binding,
|
||||
render_graph::VKResourceAccessInfo &access_info)
|
||||
{
|
||||
switch (resource_binding.bind_type) {
|
||||
case VKBindType::UNIFORM_BUFFER: {
|
||||
update_resource_access_info_binding_uniform_buffer(
|
||||
state_manager, resource_binding, access_info);
|
||||
break;
|
||||
}
|
||||
|
||||
case VKBindType::STORAGE_BUFFER: {
|
||||
update_resource_access_info_binding_storage_buffer(
|
||||
state_manager, resource_binding, access_info);
|
||||
break;
|
||||
}
|
||||
|
||||
case VKBindType::SAMPLER: {
|
||||
update_resource_access_info_binding_sampler(state_manager, resource_binding, access_info);
|
||||
break;
|
||||
}
|
||||
|
||||
case VKBindType::IMAGE: {
|
||||
update_resource_access_info_binding_image(state_manager, resource_binding, access_info);
|
||||
break;
|
||||
}
|
||||
|
||||
case VKBindType::INPUT_ATTACHMENT: {
|
||||
update_resource_access_info_binding_input_attachment(
|
||||
state_manager, resource_binding, access_info);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VKDescriptorSetTracker::update_resource_access_info(
|
||||
VKContext &context,
|
||||
render_graph::VKResourceAccessInfo &access_info,
|
||||
const VKBufferWithOffset &push_constants_buffer)
|
||||
{
|
||||
VKShader &shader = *unwrap(context.shader);
|
||||
VKStateManager &state_manager = context.state_manager_get();
|
||||
const VKShaderInterface &shader_interface = shader.interface_get();
|
||||
|
||||
for (const VKResourceBinding &resource_binding : shader_interface.resource_bindings_get()) {
|
||||
if (resource_binding.binding == -1) {
|
||||
continue;
|
||||
}
|
||||
update_resource_access_info_binding(state_manager, resource_binding, access_info);
|
||||
}
|
||||
|
||||
/* Bind uniform push constants to descriptor set. */
|
||||
if (shader.push_constants.layout_get().storage_type_get() ==
|
||||
VKPushConstants::StorageType::BUFFER &&
|
||||
push_constants_buffer.buffer != VK_NULL_HANDLE)
|
||||
{
|
||||
access_info.buffers.append({push_constants_buffer.buffer, VK_ACCESS_UNIFORM_READ_BIT});
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
void VKDescriptorSetTracker::upload_descriptor_sets()
|
||||
{
|
||||
descriptor_sets.upload_descriptor_sets();
|
||||
vk_descriptor_set_layout_ = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name VKDescriptorSetUpdator
|
||||
* \{ */
|
||||
|
||||
void VKDescriptorSetUpdator::bind_image_resource(const VKStateManager &state_manager,
|
||||
const VKResourceBinding &resource_binding)
|
||||
{
|
||||
VKTexture &texture = *state_manager.images_.get(resource_binding.binding);
|
||||
bind_image(
|
||||
VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
VK_NULL_HANDLE,
|
||||
texture.image_view_get(resource_binding.arrayed, VKImageViewFlags::NO_SWIZZLING).vk_handle(),
|
||||
VK_IMAGE_LAYOUT_GENERAL,
|
||||
resource_binding.location);
|
||||
}
|
||||
|
||||
void VKDescriptorSetUpdator::bind_texture_resource(const VKDevice &device,
|
||||
const VKStateManager &state_manager,
|
||||
const VKResourceBinding &resource_binding)
|
||||
{
|
||||
const BindSpaceTextures::Elem *elem_ptr = state_manager.textures_.get(resource_binding.binding);
|
||||
if (!elem_ptr || elem_ptr->resource == nullptr) {
|
||||
/* Unbound resource. */
|
||||
return;
|
||||
}
|
||||
const BindSpaceTextures::Elem &elem = *elem_ptr;
|
||||
switch (elem.resource_type) {
|
||||
case BindSpaceTextures::Type::VertexBuffer: {
|
||||
VKVertexBuffer &vertex_buffer = *static_cast<VKVertexBuffer *>(elem.resource);
|
||||
bind_texel_buffer(vertex_buffer, resource_binding.location);
|
||||
break;
|
||||
}
|
||||
case BindSpaceTextures::Type::Texture: {
|
||||
VKTexture *texture = static_cast<VKTexture *>(elem.resource);
|
||||
if (texture->type_ == GPU_TEXTURE_BUFFER) {
|
||||
/* Texture buffers are no textures, but wrap around vertex buffers and need to be
|
||||
* bound as texel buffers. */
|
||||
/* TODO: Investigate if this can be improved in the API. */
|
||||
VKVertexBuffer &vertex_buffer = *texture->source_buffer_;
|
||||
bind_texel_buffer(vertex_buffer, resource_binding.location);
|
||||
}
|
||||
else {
|
||||
const VKSampler &sampler = device.samplers().get(elem.sampler);
|
||||
bind_image(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
sampler.vk_handle(),
|
||||
texture->image_view_get(resource_binding.arrayed, VKImageViewFlags::DEFAULT)
|
||||
.vk_handle(),
|
||||
VK_IMAGE_LAYOUT_GENERAL,
|
||||
resource_binding.location);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BindSpaceTextures::Type::Unused: {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VKDescriptorSetUpdator::bind_input_attachment_resource(
|
||||
const VKDevice &device,
|
||||
const VKStateManager &state_manager,
|
||||
const VKResourceBinding &resource_binding)
|
||||
{
|
||||
const bool supports_local_read = device.extensions_get().dynamic_rendering_local_read;
|
||||
if (supports_local_read) {
|
||||
VKTexture *texture = state_manager.images_.get(resource_binding.binding);
|
||||
BLI_assert(texture);
|
||||
bind_image(VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
|
||||
VK_NULL_HANDLE,
|
||||
texture->image_view_get(resource_binding.arrayed, VKImageViewFlags::NO_SWIZZLING)
|
||||
.vk_handle(),
|
||||
VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR,
|
||||
resource_binding.location);
|
||||
}
|
||||
else {
|
||||
const BindSpaceTextures::Elem *elem_ptr = state_manager.textures_.get(
|
||||
resource_binding.binding);
|
||||
if (!elem_ptr) {
|
||||
/* Unbound resource. */
|
||||
BLI_assert_unreachable();
|
||||
return;
|
||||
}
|
||||
const BindSpaceTextures::Elem &elem = *elem_ptr;
|
||||
VKTexture *texture = static_cast<VKTexture *>(elem.resource);
|
||||
BLI_assert(texture);
|
||||
BLI_assert(elem.resource_type == BindSpaceTextures::Type::Texture);
|
||||
const VKSampler &sampler = device.samplers().get(elem.sampler);
|
||||
bind_image(
|
||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
sampler.vk_handle(),
|
||||
texture->image_view_get(resource_binding.arrayed, VKImageViewFlags::DEFAULT).vk_handle(),
|
||||
VK_IMAGE_LAYOUT_GENERAL,
|
||||
resource_binding.location);
|
||||
}
|
||||
}
|
||||
|
||||
void VKDescriptorSetUpdator::bind_storage_buffer_resource(
|
||||
const VKStateManager &state_manager, const VKResourceBinding &resource_binding)
|
||||
{
|
||||
const BindSpaceStorageBuffers::Elem &elem = state_manager.storage_buffers_.get(
|
||||
resource_binding.binding);
|
||||
VkBuffer vk_buffer = VK_NULL_HANDLE;
|
||||
VkDeviceSize vk_device_size = 0;
|
||||
switch (elem.resource_type) {
|
||||
case BindSpaceStorageBuffers::Type::IndexBuffer: {
|
||||
VKIndexBuffer *index_buffer = static_cast<VKIndexBuffer *>(elem.resource);
|
||||
vk_buffer = index_buffer->vk_handle();
|
||||
/* Using the allocated size of the buffer as conservative depth shader can read additional
|
||||
* bytes when there are an uneven number of indices. */
|
||||
vk_device_size = index_buffer->allocated_size_get() - elem.offset;
|
||||
break;
|
||||
}
|
||||
case BindSpaceStorageBuffers::Type::VertexBuffer: {
|
||||
VKVertexBuffer *vertex_buffer = static_cast<VKVertexBuffer *>(elem.resource);
|
||||
vk_buffer = vertex_buffer->vk_handle();
|
||||
vk_device_size = vertex_buffer->size_used_get() - elem.offset;
|
||||
break;
|
||||
}
|
||||
case BindSpaceStorageBuffers::Type::UniformBuffer: {
|
||||
VKUniformBuffer *uniform_buffer = static_cast<VKUniformBuffer *>(elem.resource);
|
||||
vk_buffer = uniform_buffer->vk_handle();
|
||||
vk_device_size = uniform_buffer->size_in_bytes() - elem.offset;
|
||||
break;
|
||||
}
|
||||
case BindSpaceStorageBuffers::Type::StorageBuffer: {
|
||||
VKStorageBuffer *storage_buffer = static_cast<VKStorageBuffer *>(elem.resource);
|
||||
vk_buffer = storage_buffer->vk_handle();
|
||||
vk_device_size = storage_buffer->usage_size_get();
|
||||
break;
|
||||
}
|
||||
case BindSpaceStorageBuffers::Type::Buffer: {
|
||||
VKBuffer *buffer = static_cast<VKBuffer *>(elem.resource);
|
||||
vk_buffer = buffer->vk_handle();
|
||||
vk_device_size = buffer->size_in_bytes() - elem.offset;
|
||||
break;
|
||||
}
|
||||
case BindSpaceStorageBuffers::Type::Unused: {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
bind_buffer(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
vk_buffer,
|
||||
elem.offset,
|
||||
vk_device_size,
|
||||
resource_binding.location);
|
||||
}
|
||||
|
||||
void VKDescriptorSetUpdator::bind_uniform_buffer_resource(
|
||||
const VKStateManager &state_manager, const VKResourceBinding &resource_binding)
|
||||
{
|
||||
VKUniformBuffer &uniform_buffer = *state_manager.uniform_buffers_.get(resource_binding.binding);
|
||||
bind_buffer(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||
uniform_buffer.vk_handle(),
|
||||
0,
|
||||
uniform_buffer.size_in_bytes(),
|
||||
resource_binding.location);
|
||||
}
|
||||
|
||||
void VKDescriptorSetUpdator::bind_push_constants(VKPushConstants &push_constants,
|
||||
const VKBufferWithOffset &push_constants_buffer)
|
||||
{
|
||||
const VKPushConstants::Layout &push_constants_layout = push_constants.layout_get();
|
||||
if (push_constants_layout.storage_type_get() != VKPushConstants::StorageType::BUFFER) {
|
||||
return;
|
||||
}
|
||||
bind_buffer(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||
push_constants_buffer.buffer,
|
||||
push_constants_buffer.offset,
|
||||
push_constants_layout.size_in_bytes(),
|
||||
push_constants_layout.descriptor_set_location_get());
|
||||
}
|
||||
|
||||
void VKDescriptorSetUpdator::bind_shader_resources(const VKDevice &device,
|
||||
const VKStateManager &state_manager,
|
||||
VKShader &shader,
|
||||
const VKBufferWithOffset &push_constants_buffer)
|
||||
{
|
||||
const VKShaderInterface &shader_interface = shader.interface_get();
|
||||
for (const VKResourceBinding &resource_binding : shader_interface.resource_bindings_get()) {
|
||||
if (resource_binding.binding == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (resource_binding.bind_type) {
|
||||
case VKBindType::UNIFORM_BUFFER:
|
||||
bind_uniform_buffer_resource(state_manager, resource_binding);
|
||||
break;
|
||||
|
||||
case VKBindType::STORAGE_BUFFER:
|
||||
bind_storage_buffer_resource(state_manager, resource_binding);
|
||||
break;
|
||||
|
||||
case VKBindType::SAMPLER:
|
||||
bind_texture_resource(device, state_manager, resource_binding);
|
||||
break;
|
||||
|
||||
case VKBindType::IMAGE:
|
||||
bind_image_resource(state_manager, resource_binding);
|
||||
break;
|
||||
|
||||
case VKBindType::INPUT_ATTACHMENT:
|
||||
bind_input_attachment_resource(device, state_manager, resource_binding);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Bind uniform push constants to descriptor set. */
|
||||
bind_push_constants(shader.push_constants, push_constants_buffer);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name VKDescriptorSetPoolUpdator
|
||||
* \{ */
|
||||
|
||||
void VKDescriptorSetPoolUpdator::allocate_new_descriptor_set(
|
||||
VKDevice & /*device*/,
|
||||
VKContext &context,
|
||||
VKShader &shader,
|
||||
VkDescriptorSetLayout vk_descriptor_set_layout,
|
||||
render_graph::VKPipelineData &r_pipeline_data)
|
||||
{
|
||||
/* Use descriptor pools/sets. */
|
||||
vk_descriptor_set = context.descriptor_pools_get().allocate(vk_descriptor_set_layout);
|
||||
BLI_assert(vk_descriptor_set != VK_NULL_HANDLE);
|
||||
debug::object_label(vk_descriptor_set, shader.name_get());
|
||||
r_pipeline_data.vk_descriptor_set = vk_descriptor_set;
|
||||
}
|
||||
|
||||
void VKDescriptorSetPoolUpdator::bind_buffer(VkDescriptorType vk_descriptor_type,
|
||||
VkBuffer vk_buffer,
|
||||
VkDeviceSize buffer_offset,
|
||||
VkDeviceSize size_in_bytes,
|
||||
VKDescriptorSet::Location location)
|
||||
{
|
||||
if (vk_buffer == VK_NULL_HANDLE) {
|
||||
vk_descriptor_buffer_infos_.append({VK_NULL_HANDLE, 0, VK_WHOLE_SIZE});
|
||||
}
|
||||
else {
|
||||
vk_descriptor_buffer_infos_.append({vk_buffer, buffer_offset, size_in_bytes});
|
||||
}
|
||||
vk_write_descriptor_sets_.append({VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
vk_descriptor_set,
|
||||
location,
|
||||
0,
|
||||
1,
|
||||
vk_descriptor_type,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr});
|
||||
}
|
||||
|
||||
void VKDescriptorSetPoolUpdator::bind_texel_buffer(VKVertexBuffer &vertex_buffer,
|
||||
const VKDescriptorSet::Location location)
|
||||
{
|
||||
vertex_buffer.ensure_buffer_view();
|
||||
vk_buffer_views_.append(vertex_buffer.vk_buffer_view_get());
|
||||
vk_write_descriptor_sets_.append({VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
vk_descriptor_set,
|
||||
location,
|
||||
0,
|
||||
1,
|
||||
VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr});
|
||||
}
|
||||
|
||||
void VKDescriptorSetPoolUpdator::bind_image(VkDescriptorType vk_descriptor_type,
|
||||
VkSampler vk_sampler,
|
||||
VkImageView vk_image_view,
|
||||
VkImageLayout vk_image_layout,
|
||||
VKDescriptorSet::Location location)
|
||||
{
|
||||
vk_descriptor_image_infos_.append({vk_sampler, vk_image_view, vk_image_layout});
|
||||
vk_write_descriptor_sets_.append({VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
vk_descriptor_set,
|
||||
location,
|
||||
0,
|
||||
1,
|
||||
vk_descriptor_type,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr});
|
||||
}
|
||||
|
||||
void VKDescriptorSetPoolUpdator::upload_descriptor_sets()
|
||||
{
|
||||
if (vk_write_descriptor_sets_.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Finalize pointers that could have changed due to reallocations. */
|
||||
int buffer_index = 0;
|
||||
int buffer_view_index = 0;
|
||||
int image_index = 0;
|
||||
for (VkWriteDescriptorSet &vk_write_descriptor_set : vk_write_descriptor_sets_) {
|
||||
switch (vk_write_descriptor_set.descriptorType) {
|
||||
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
||||
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
|
||||
vk_write_descriptor_set.pImageInfo = &vk_descriptor_image_infos_[image_index++];
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
||||
vk_write_descriptor_set.pTexelBufferView = &vk_buffer_views_[buffer_view_index++];
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
||||
vk_write_descriptor_set.pBufferInfo = &vk_descriptor_buffer_infos_[buffer_index++];
|
||||
break;
|
||||
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Uncomment this for rebalancing VKDescriptorPools::POOL_SIZE_* */
|
||||
{
|
||||
int storage_buffer_count = 0;
|
||||
int storage_image_count = 0;
|
||||
int combined_image_sampler_count = 0;
|
||||
int uniform_buffer_count = 0;
|
||||
int uniform_texel_buffer_count = 0;
|
||||
int input_attachment_count = 0;
|
||||
Set<VkDescriptorSet> descriptor_set_count;
|
||||
|
||||
for (VkWriteDescriptorSet &vk_write_descriptor_set : vk_write_descriptor_sets_) {
|
||||
descriptor_set_count.add(vk_write_descriptor_set.dstSet);
|
||||
switch (vk_write_descriptor_set.descriptorType) {
|
||||
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
||||
combined_image_sampler_count += 1;
|
||||
break;
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
||||
storage_image_count += 1;
|
||||
break;
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
||||
uniform_texel_buffer_count += 1;
|
||||
break;
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
||||
uniform_buffer_count += 1;
|
||||
break;
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
||||
storage_buffer_count += 1;
|
||||
break;
|
||||
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
|
||||
input_attachment_count += 1;
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
std::cout << __func__ << ": "
|
||||
<< "descriptor_set=" << descriptor_set_count.size()
|
||||
<< ", combined_image_sampler=" << combined_image_sampler_count
|
||||
<< ", storage_image=" << storage_image_count
|
||||
<< ", uniform_texel_buffer=" << uniform_texel_buffer_count
|
||||
<< ", uniform_buffer=" << uniform_buffer_count
|
||||
<< ", storage_buffer=" << storage_buffer_count
|
||||
<< ", input_attachment=" << input_attachment_count << "\n";
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Update the descriptor set on the device. */
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
vkUpdateDescriptorSets(device.vk_handle(),
|
||||
vk_write_descriptor_sets_.size(),
|
||||
vk_write_descriptor_sets_.data(),
|
||||
0,
|
||||
nullptr);
|
||||
|
||||
vk_descriptor_image_infos_.clear();
|
||||
vk_descriptor_buffer_infos_.clear();
|
||||
vk_buffer_views_.clear();
|
||||
vk_write_descriptor_sets_.clear();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu
|
||||
218
blender-5.2.0/source/blender/gpu/vulkan/vk_descriptor_set.hh
Normal file
218
blender-5.2.0/source/blender/gpu/vulkan/vk_descriptor_set.hh
Normal file
@@ -0,0 +1,218 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_utility_mixins.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "gpu_shader_private.hh"
|
||||
|
||||
#include "render_graph/nodes/vk_pipeline_data.hh"
|
||||
#include "render_graph/vk_resource_access_info.hh"
|
||||
#include "vk_buffer.hh"
|
||||
#include "vk_common.hh"
|
||||
#include "vk_descriptor_set_layouts.hh"
|
||||
#include "vk_uniform_buffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
struct VKResourceBinding;
|
||||
class VKStateManager;
|
||||
class VKDevice;
|
||||
class VKPushConstants;
|
||||
class VKShader;
|
||||
class VKDescriptorSetTracker;
|
||||
class VKVertexBuffer;
|
||||
|
||||
/**
|
||||
* In vulkan shader resources (images and buffers) are grouped in descriptor sets.
|
||||
*
|
||||
* The resources inside a descriptor set can be updated and bound per set.
|
||||
*
|
||||
* Currently Blender only supports a single descriptor set per shader, but it is planned to be able
|
||||
* to use 2 descriptor sets per shader. One for each #gpu::shader::Frequency.
|
||||
*/
|
||||
class VKDescriptorSet : NonCopyable {
|
||||
|
||||
public:
|
||||
/**
|
||||
* Binding location of a resource in a descriptor set.
|
||||
*
|
||||
* Locations and bindings are used for different reasons. In the Vulkan backend we use
|
||||
* ShaderInput.location to store the descriptor set + the resource binding inside the descriptor
|
||||
* set. To ease the development the VKDescriptorSet::Location will be used to hide this
|
||||
* confusion.
|
||||
*
|
||||
* NOTE: [future development] When supporting multiple descriptor sets the encoding/decoding can
|
||||
* be centralized here. Location will then also contain the descriptor set index.
|
||||
*/
|
||||
struct Location {
|
||||
friend class VKDescriptorSetTracker;
|
||||
friend class VKShaderInterface;
|
||||
friend struct VKResourceBinding;
|
||||
|
||||
private:
|
||||
/**
|
||||
* References to a binding in the descriptor set.
|
||||
*/
|
||||
uint32_t binding;
|
||||
|
||||
Location(uint32_t binding) : binding(binding) {}
|
||||
|
||||
public:
|
||||
Location() = default;
|
||||
|
||||
bool operator==(const Location &other) const
|
||||
{
|
||||
return binding == other.binding;
|
||||
}
|
||||
|
||||
operator uint32_t() const
|
||||
{
|
||||
return binding;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class VKDescriptorSetUpdator {
|
||||
public:
|
||||
virtual ~VKDescriptorSetUpdator() {};
|
||||
|
||||
virtual void allocate_new_descriptor_set(VKDevice &device,
|
||||
VKContext &context,
|
||||
VKShader &shader,
|
||||
VkDescriptorSetLayout vk_descriptor_set_layout,
|
||||
render_graph::VKPipelineData &r_pipeline_data) = 0;
|
||||
void bind_shader_resources(const VKDevice &device,
|
||||
const VKStateManager &state_manager,
|
||||
VKShader &shader,
|
||||
const VKBufferWithOffset &push_constants_buffer);
|
||||
virtual void upload_descriptor_sets() = 0;
|
||||
|
||||
private:
|
||||
void bind_image_resource(const VKStateManager &state_manager,
|
||||
const VKResourceBinding &resource_binding);
|
||||
void bind_texture_resource(const VKDevice &device,
|
||||
const VKStateManager &state_manager,
|
||||
const VKResourceBinding &resource_binding);
|
||||
void bind_storage_buffer_resource(const VKStateManager &state_manager,
|
||||
const VKResourceBinding &resource_binding);
|
||||
void bind_uniform_buffer_resource(const VKStateManager &state_manager,
|
||||
const VKResourceBinding &resource_binding);
|
||||
void bind_input_attachment_resource(const VKDevice &device,
|
||||
const VKStateManager &state_manager,
|
||||
const VKResourceBinding &resource_binding);
|
||||
|
||||
void bind_push_constants(VKPushConstants &push_constants,
|
||||
const VKBufferWithOffset &push_constants_buffer);
|
||||
|
||||
protected:
|
||||
virtual void bind_texel_buffer(VKVertexBuffer &vertex_buffer,
|
||||
VKDescriptorSet::Location location) = 0;
|
||||
virtual void bind_buffer(VkDescriptorType vk_descriptor_type,
|
||||
VkBuffer vk_buffer,
|
||||
VkDeviceSize buffer_offset,
|
||||
VkDeviceSize size_in_bytes,
|
||||
VKDescriptorSet::Location location) = 0;
|
||||
virtual void bind_image(VkDescriptorType vk_descriptor_type,
|
||||
VkSampler vk_sampler,
|
||||
VkImageView vk_image_view,
|
||||
VkImageLayout vk_image_layout,
|
||||
VKDescriptorSet::Location location) = 0;
|
||||
};
|
||||
|
||||
class VKDescriptorSetPoolUpdator : public VKDescriptorSetUpdator {
|
||||
public:
|
||||
VkDescriptorSet vk_descriptor_set = VK_NULL_HANDLE;
|
||||
|
||||
void allocate_new_descriptor_set(VKDevice &device,
|
||||
VKContext &context,
|
||||
VKShader &shader,
|
||||
VkDescriptorSetLayout vk_descriptor_set_layout,
|
||||
render_graph::VKPipelineData &r_pipeline_data) override;
|
||||
|
||||
void upload_descriptor_sets() override;
|
||||
|
||||
protected:
|
||||
void bind_texel_buffer(VKVertexBuffer &vertex_buffer,
|
||||
VKDescriptorSet::Location location) override;
|
||||
void bind_buffer(VkDescriptorType vk_descriptor_type,
|
||||
VkBuffer vk_buffer,
|
||||
VkDeviceSize buffer_offset,
|
||||
VkDeviceSize size_in_bytes,
|
||||
VKDescriptorSet::Location location) override;
|
||||
void bind_image(VkDescriptorType vk_descriptor_type,
|
||||
VkSampler vk_sampler,
|
||||
VkImageView vk_image_view,
|
||||
VkImageLayout vk_image_layout,
|
||||
VKDescriptorSet::Location location) override;
|
||||
|
||||
private:
|
||||
Vector<VkBufferView> vk_buffer_views_;
|
||||
Vector<VkDescriptorBufferInfo> vk_descriptor_buffer_infos_;
|
||||
Vector<VkDescriptorImageInfo> vk_descriptor_image_infos_;
|
||||
Vector<VkWriteDescriptorSet> vk_write_descriptor_sets_;
|
||||
};
|
||||
|
||||
class VKDescriptorSetTracker {
|
||||
friend class VKDescriptorSet;
|
||||
|
||||
/* Last used layout to identify changes. */
|
||||
VkDescriptorSetLayout vk_descriptor_set_layout_ = VK_NULL_HANDLE;
|
||||
|
||||
public:
|
||||
class VKDescriptorSetPoolUpdator descriptor_sets;
|
||||
|
||||
VKDescriptorSetTracker() {}
|
||||
|
||||
/**
|
||||
* Update the descriptor set. Reuses previous descriptor set when no changes are detected. This
|
||||
* improves performance when working with large grease pencil scenes.
|
||||
*/
|
||||
void update_descriptor_set(VKContext &context,
|
||||
render_graph::VKResourceAccessInfo &resource_access_info,
|
||||
render_graph::VKPipelineData &r_pipeline_data);
|
||||
|
||||
/**
|
||||
* Upload all descriptor sets to the device.
|
||||
*/
|
||||
void upload_descriptor_sets();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Add resources of the descriptor set to the resource access info.
|
||||
*/
|
||||
static void update_resource_access_info(VKContext &context,
|
||||
render_graph::VKResourceAccessInfo &resource_access_info,
|
||||
const VKBufferWithOffset &push_constants_buffer);
|
||||
static void update_resource_access_info_binding(const VKStateManager &state_manager,
|
||||
const VKResourceBinding &resource_binding,
|
||||
render_graph::VKResourceAccessInfo &access_info);
|
||||
static void update_resource_access_info_binding_uniform_buffer(
|
||||
const VKStateManager &state_manager,
|
||||
const VKResourceBinding &resource_binding,
|
||||
render_graph::VKResourceAccessInfo &access_info);
|
||||
static void update_resource_access_info_binding_image(
|
||||
const VKStateManager &state_manager,
|
||||
const VKResourceBinding &resource_binding,
|
||||
render_graph::VKResourceAccessInfo &access_info);
|
||||
static void update_resource_access_info_binding_sampler(
|
||||
const VKStateManager &state_manager,
|
||||
const VKResourceBinding &resource_binding,
|
||||
render_graph::VKResourceAccessInfo &access_info);
|
||||
static void update_resource_access_info_binding_storage_buffer(
|
||||
const VKStateManager &state_manager,
|
||||
const VKResourceBinding &resource_binding,
|
||||
render_graph::VKResourceAccessInfo &access_info);
|
||||
static void update_resource_access_info_binding_input_attachment(
|
||||
const VKStateManager &state_manager,
|
||||
const VKResourceBinding &resource_binding,
|
||||
render_graph::VKResourceAccessInfo &access_info);
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
@@ -0,0 +1,93 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "vk_descriptor_set_layouts.hh"
|
||||
#include "vk_backend.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
VKDescriptorSetLayouts::VKDescriptorSetLayouts()
|
||||
{
|
||||
vk_descriptor_set_layout_create_info_ = {};
|
||||
vk_descriptor_set_layout_create_info_.sType =
|
||||
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
}
|
||||
|
||||
VKDescriptorSetLayouts::~VKDescriptorSetLayouts()
|
||||
{
|
||||
deinit();
|
||||
}
|
||||
|
||||
VkDescriptorSetLayout VKDescriptorSetLayouts::get_or_create(const VKDescriptorSetLayoutInfo &info,
|
||||
bool &r_created,
|
||||
bool &r_needed)
|
||||
{
|
||||
r_created = false;
|
||||
r_needed = !info.bindings.is_empty();
|
||||
if (r_needed == false) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
std::scoped_lock mutex(mutex_);
|
||||
|
||||
VkDescriptorSetLayout *layout = vk_descriptor_set_layouts_.lookup_ptr(info);
|
||||
if (layout) {
|
||||
return *layout;
|
||||
}
|
||||
|
||||
update_layout_bindings(info);
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
|
||||
vk_descriptor_set_layout_create_info_.bindingCount = vk_descriptor_set_layout_bindings_.size();
|
||||
vk_descriptor_set_layout_create_info_.pBindings = vk_descriptor_set_layout_bindings_.data();
|
||||
|
||||
VkDescriptorSetLayout vk_descriptor_set_layout = VK_NULL_HANDLE;
|
||||
vkCreateDescriptorSetLayout(device.vk_handle(),
|
||||
&vk_descriptor_set_layout_create_info_,
|
||||
nullptr,
|
||||
&vk_descriptor_set_layout);
|
||||
BLI_assert(vk_descriptor_set_layout != VK_NULL_HANDLE);
|
||||
|
||||
vk_descriptor_set_layout_create_info_.bindingCount = 0;
|
||||
vk_descriptor_set_layout_create_info_.pBindings = nullptr;
|
||||
vk_descriptor_set_layout_bindings_.clear();
|
||||
vk_descriptor_set_layouts_.add(info, vk_descriptor_set_layout);
|
||||
r_created = true;
|
||||
|
||||
return vk_descriptor_set_layout;
|
||||
}
|
||||
|
||||
void VKDescriptorSetLayouts::update_layout_bindings(const VKDescriptorSetLayoutInfo &info)
|
||||
{
|
||||
BLI_assert(vk_descriptor_set_layout_bindings_.is_empty());
|
||||
vk_descriptor_set_layout_bindings_.reserve(info.bindings.size());
|
||||
|
||||
uint32_t index = 0;
|
||||
for (const VkDescriptorType &vk_descriptor_type : info.bindings) {
|
||||
VkDescriptorSetLayoutBinding binding = {};
|
||||
binding.binding = index++;
|
||||
binding.descriptorCount = 1;
|
||||
binding.descriptorType = vk_descriptor_type;
|
||||
binding.pImmutableSamplers = VK_NULL_HANDLE;
|
||||
binding.stageFlags = vk_descriptor_type == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT ?
|
||||
VkShaderStageFlags(VK_SHADER_STAGE_FRAGMENT_BIT) :
|
||||
info.vk_shader_stage_flags;
|
||||
vk_descriptor_set_layout_bindings_.append(binding);
|
||||
}
|
||||
}
|
||||
|
||||
void VKDescriptorSetLayouts::deinit()
|
||||
{
|
||||
std::scoped_lock mutex(mutex_);
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
for (VkDescriptorSetLayout &vk_descriptor_set_layout : vk_descriptor_set_layouts_.values()) {
|
||||
vkDestroyDescriptorSetLayout(device.vk_handle(), vk_descriptor_set_layout, nullptr);
|
||||
}
|
||||
vk_descriptor_set_layouts_.clear();
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
@@ -0,0 +1,123 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Multiple shaders can use the same descriptor set layout. VKDescriptorSetLayouts has a mechanism
|
||||
* to create and reuse existing descriptor set layouts.
|
||||
*
|
||||
* This makes it easier to detect layout changes between shaders. If the same layout is used, we
|
||||
* will be able to reuse the descriptor set if the bindings are also the same.
|
||||
*
|
||||
* These resources are freed when the Vulkan backend is freed. Descriptor set layouts are Vulkan
|
||||
* driver resources, but they are virtually unlimited.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_mutex.hh"
|
||||
#include "BLI_utility_mixins.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "vk_common.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
|
||||
class VKDevice;
|
||||
|
||||
/**
|
||||
* Key of descriptor set layout
|
||||
*
|
||||
* Contains information to identify same descriptor set layouts.
|
||||
*/
|
||||
struct VKDescriptorSetLayoutInfo {
|
||||
using Bindings = Vector<VkDescriptorType>;
|
||||
|
||||
Bindings bindings;
|
||||
VkShaderStageFlags vk_shader_stage_flags;
|
||||
|
||||
bool operator==(const VKDescriptorSetLayoutInfo &other) const
|
||||
{
|
||||
return vk_shader_stage_flags == other.vk_shader_stage_flags && bindings == other.bindings;
|
||||
};
|
||||
};
|
||||
|
||||
}; // namespace gpu
|
||||
|
||||
/**
|
||||
* Default hash for gpu::VKDescriptorSetLayoutInfo.
|
||||
*
|
||||
* NOTE: DefaultHash needs to be implemented in namespace `blender`.
|
||||
*/
|
||||
template<> struct DefaultHash<gpu::VKDescriptorSetLayoutInfo> {
|
||||
uint64_t operator()(const gpu::VKDescriptorSetLayoutInfo &key) const
|
||||
{
|
||||
uint64_t hash = uint64_t(key.vk_shader_stage_flags);
|
||||
for (VkDescriptorType vk_descriptor_type : key.bindings) {
|
||||
hash = hash * 33 ^ uint64_t(vk_descriptor_type);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
namespace gpu {
|
||||
|
||||
/**
|
||||
* Registries of descriptor set layouts.
|
||||
*/
|
||||
class VKDescriptorSetLayouts : NonCopyable {
|
||||
friend class VKDevice;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Map containing all created descriptor set layouts.
|
||||
*/
|
||||
Map<VKDescriptorSetLayoutInfo, VkDescriptorSetLayout> vk_descriptor_set_layouts_;
|
||||
|
||||
/**
|
||||
* Reusable descriptor set layout create info.
|
||||
*/
|
||||
VkDescriptorSetLayoutCreateInfo vk_descriptor_set_layout_create_info_;
|
||||
Vector<VkDescriptorSetLayoutBinding> vk_descriptor_set_layout_bindings_;
|
||||
Mutex mutex_;
|
||||
|
||||
public:
|
||||
VKDescriptorSetLayouts();
|
||||
virtual ~VKDescriptorSetLayouts();
|
||||
|
||||
/**
|
||||
* Get an existing descriptor set layout, or create when not available.
|
||||
* `r_created` is set to true when a new descriptor set layout was created, set to false when an
|
||||
* existing descriptor set layout is returned.
|
||||
* `r_needed` is set to true, when a descriptor set layout is needed
|
||||
*/
|
||||
VkDescriptorSetLayout get_or_create(const VKDescriptorSetLayoutInfo &info,
|
||||
bool &r_created,
|
||||
bool &r_needed);
|
||||
|
||||
/**
|
||||
* Free all descriptor set layouts.
|
||||
*
|
||||
* This method is called when the VKDevice is destroyed.
|
||||
*/
|
||||
void deinit();
|
||||
|
||||
/**
|
||||
* Return the number of descriptor set layouts.
|
||||
*/
|
||||
int64_t size() const
|
||||
{
|
||||
return vk_descriptor_set_layouts_.size();
|
||||
}
|
||||
|
||||
private:
|
||||
void update_layout_bindings(const VKDescriptorSetLayoutInfo &info);
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
616
blender-5.2.0/source/blender/gpu/vulkan/vk_device.cc
Normal file
616
blender-5.2.0/source/blender/gpu/vulkan/vk_device.cc
Normal file
@@ -0,0 +1,616 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/ranges.h>
|
||||
#include <sstream>
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "vk_backend.hh"
|
||||
#include "vk_context.hh"
|
||||
#include "vk_device.hh"
|
||||
#include "vk_state_manager.hh"
|
||||
#include "vk_storage_buffer.hh"
|
||||
#include "vk_texture.hh"
|
||||
#include "vk_vertex_buffer.hh"
|
||||
|
||||
#include "gpu_shader_dependency_private.hh"
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "gpu_capabilities_private.hh"
|
||||
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static CLG_LogRef LOG = {"gpu.vulkan"};
|
||||
|
||||
namespace gpu {
|
||||
|
||||
void VKExtensions::log() const
|
||||
{
|
||||
CLOG_DEBUG(&LOG,
|
||||
"Device features\n"
|
||||
" - [%c] shader output viewport index\n"
|
||||
" - [%c] shader output layer\n"
|
||||
" - [%c] fragment shader barycentric\n"
|
||||
" - [%c] wide lines\n"
|
||||
"Device extensions\n"
|
||||
" - [%c] dynamic rendering local read\n"
|
||||
" - [%c] dynamic rendering unused attachments\n"
|
||||
" - [%c] extended dynamic state\n"
|
||||
" - [%c] external memory\n"
|
||||
" - [%c] graphics pipeline library\n"
|
||||
" - [%c] host image copy\n"
|
||||
" - [%c] line rasterization\n"
|
||||
" - [%c] maintenance4\n"
|
||||
" - [%c] memory priority\n"
|
||||
" - [%c] pageable device local memory\n"
|
||||
" - [%c] shader stencil export\n"
|
||||
" - [%c] vertex input dynamic state",
|
||||
shader_output_viewport_index ? 'X' : ' ',
|
||||
shader_output_layer ? 'X' : ' ',
|
||||
fragment_shader_barycentric ? 'X' : ' ',
|
||||
wide_lines ? 'X' : ' ',
|
||||
dynamic_rendering_local_read ? 'X' : ' ',
|
||||
dynamic_rendering_unused_attachments ? 'X' : ' ',
|
||||
extended_dynamic_state ? 'X' : ' ',
|
||||
external_memory ? 'X' : ' ',
|
||||
graphics_pipeline_library ? 'X' : ' ',
|
||||
host_image_copy ? 'X' : ' ',
|
||||
line_rasterization ? 'X' : ' ',
|
||||
maintenance4 ? 'X' : ' ',
|
||||
memory_priority ? 'X' : ' ',
|
||||
pageable_device_local_memory ? 'X' : ' ',
|
||||
GPU_stencil_export_support() ? 'X' : ' ',
|
||||
vertex_input_dynamic_state ? 'X' : ' ');
|
||||
}
|
||||
|
||||
void VKWorkarounds::log() const
|
||||
{
|
||||
CLOG_DEBUG(&LOG,
|
||||
"Activated workarounds\n"
|
||||
" - [%c] Not 16/32 bit aligned image formats\n"
|
||||
" - [%c] No texture pool",
|
||||
not_aligned_pixel_formats ? 'X' : ' ',
|
||||
GCaps.texture_pool_workaround ? 'X' : ' ');
|
||||
}
|
||||
|
||||
void VKDevice::reinit()
|
||||
{
|
||||
samplers_.free();
|
||||
samplers_.init();
|
||||
}
|
||||
|
||||
void VKDevice::deinit()
|
||||
{
|
||||
if (!is_initialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
deinit_submission_pool();
|
||||
|
||||
dummy_buffer.free();
|
||||
samplers_.free();
|
||||
GPU_SHADER_FREE_SAFE(vk_backbuffer_blit_sh_);
|
||||
|
||||
orphaned_data_render.deinit(*this);
|
||||
orphaned_data.deinit(*this);
|
||||
{
|
||||
while (!thread_data_.is_empty()) {
|
||||
VKThreadData *thread_data = thread_data_.pop_last();
|
||||
delete thread_data;
|
||||
}
|
||||
thread_data_.clear();
|
||||
}
|
||||
pipelines.write_to_disk();
|
||||
pipelines.free_data();
|
||||
descriptor_set_layouts_.deinit();
|
||||
vma_pools.deinit(*this);
|
||||
mem_allocator_ = VK_NULL_HANDLE;
|
||||
|
||||
while (!render_graphs_.is_empty()) {
|
||||
render_graph::VKRenderGraph *render_graph = render_graphs_.pop_last();
|
||||
MEM_delete<render_graph::VKRenderGraph>(render_graph);
|
||||
}
|
||||
|
||||
debugging_tools_.deinit(vk_instance_);
|
||||
|
||||
vk_instance_ = VK_NULL_HANDLE;
|
||||
vk_physical_device_ = VK_NULL_HANDLE;
|
||||
vk_device_ = VK_NULL_HANDLE;
|
||||
vk_queue_family_ = 0;
|
||||
vk_queue_ = VK_NULL_HANDLE;
|
||||
vk_physical_device_properties_ = {};
|
||||
glsl_vert_patch_.clear();
|
||||
glsl_frag_patch_.clear();
|
||||
glsl_geom_patch_.clear();
|
||||
glsl_comp_patch_.clear();
|
||||
is_initialized_ = false;
|
||||
}
|
||||
|
||||
void VKDevice::init(GHOST_IContext *ghost_context)
|
||||
{
|
||||
BLI_assert(!is_initialized());
|
||||
GHOST_VulkanHandles handles = {};
|
||||
ghost_context->getVulkanHandles(handles);
|
||||
vk_instance_ = handles.instance;
|
||||
vk_physical_device_ = handles.physical_device;
|
||||
vk_device_ = handles.device;
|
||||
vk_queue_family_ = handles.graphic_queue_family;
|
||||
vk_queue_ = handles.queue;
|
||||
mem_allocator_ = handles.vma_allocator;
|
||||
queue_mutex_ = static_cast<std::mutex *>(handles.queue_mutex);
|
||||
|
||||
init_physical_device_extensions();
|
||||
init_physical_device_properties();
|
||||
init_physical_device_memory_properties();
|
||||
init_physical_device_features();
|
||||
VKBackend::platform_init(*this);
|
||||
VKBackend::capabilities_init(*this);
|
||||
init_functions();
|
||||
init_debug_callbacks();
|
||||
vma_pools.init(*this);
|
||||
pipelines.init();
|
||||
pipelines.read_from_disk();
|
||||
|
||||
samplers_.init();
|
||||
init_dummy_buffer();
|
||||
|
||||
debug::object_label(vk_handle(), "LogicalDevice");
|
||||
debug::object_label(vk_queue_, "GenericQueue");
|
||||
|
||||
resources.use_dynamic_rendering_local_read = extensions_.dynamic_rendering_local_read;
|
||||
orphaned_data.timeline_ = 0;
|
||||
|
||||
init_submission_pool();
|
||||
is_initialized_ = true;
|
||||
}
|
||||
|
||||
void VKDevice::init_functions()
|
||||
{
|
||||
#define LOAD_FUNCTION(name) (PFN_##name) vkGetInstanceProcAddr(vk_instance_, STRINGIFY(name))
|
||||
/* VK_KHR_dynamic_rendering */
|
||||
functions.vkCmdBeginRendering = LOAD_FUNCTION(vkCmdBeginRenderingKHR);
|
||||
functions.vkCmdEndRendering = LOAD_FUNCTION(vkCmdEndRenderingKHR);
|
||||
|
||||
/* VK_EXT_debug_utils */
|
||||
functions.vkCmdBeginDebugUtilsLabel = LOAD_FUNCTION(vkCmdBeginDebugUtilsLabelEXT);
|
||||
functions.vkCmdEndDebugUtilsLabel = LOAD_FUNCTION(vkCmdEndDebugUtilsLabelEXT);
|
||||
functions.vkSetDebugUtilsObjectName = LOAD_FUNCTION(vkSetDebugUtilsObjectNameEXT);
|
||||
functions.vkCreateDebugUtilsMessenger = LOAD_FUNCTION(vkCreateDebugUtilsMessengerEXT);
|
||||
functions.vkDestroyDebugUtilsMessenger = LOAD_FUNCTION(vkDestroyDebugUtilsMessengerEXT);
|
||||
|
||||
/* VK_EXT_extended_dynamic_state */
|
||||
if (extensions_.extended_dynamic_state) {
|
||||
functions.vkCmdSetFrontFace = LOAD_FUNCTION(vkCmdSetFrontFaceEXT);
|
||||
}
|
||||
|
||||
/* VK_EXT_vertex_input_dynamic_state */
|
||||
if (extensions_.vertex_input_dynamic_state) {
|
||||
functions.vkCmdSetVertexInput = LOAD_FUNCTION(vkCmdSetVertexInputEXT);
|
||||
}
|
||||
|
||||
/* VK_EXT_host_image_copy */
|
||||
if (extensions_.host_image_copy) {
|
||||
functions.vkCopyMemoryToImage = LOAD_FUNCTION(vkCopyMemoryToImageEXT);
|
||||
functions.vkTransitionImageLayout = LOAD_FUNCTION(vkTransitionImageLayoutEXT);
|
||||
}
|
||||
|
||||
/* VK_KHR_maintenance4 */
|
||||
if (extensions_.maintenance4) {
|
||||
functions.vkGetDeviceImageMemoryRequirements = LOAD_FUNCTION(
|
||||
vkGetDeviceImageMemoryRequirementsKHR);
|
||||
functions.vkGetDeviceBufferMemoryRequirements = LOAD_FUNCTION(
|
||||
vkGetDeviceBufferMemoryRequirementsKHR);
|
||||
}
|
||||
|
||||
if (extensions_.external_memory) {
|
||||
#ifdef _WIN32
|
||||
/* VK_KHR_external_memory_win32 */
|
||||
functions.vkGetMemoryWin32Handle = LOAD_FUNCTION(vkGetMemoryWin32HandleKHR);
|
||||
#elif not defined(__APPLE__)
|
||||
/* VK_KHR_external_memory_fd */
|
||||
functions.vkGetMemoryFd = LOAD_FUNCTION(vkGetMemoryFdKHR);
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef LOAD_FUNCTION
|
||||
}
|
||||
|
||||
void VKDevice::init_debug_callbacks()
|
||||
{
|
||||
debugging_tools_.init(vk_instance_);
|
||||
}
|
||||
|
||||
void VKDevice::init_physical_device_properties()
|
||||
{
|
||||
BLI_assert(vk_physical_device_ != VK_NULL_HANDLE);
|
||||
|
||||
VkPhysicalDeviceProperties2 vk_physical_device_properties = {};
|
||||
vk_physical_device_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
||||
vk_physical_device_driver_properties_.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
|
||||
vk_physical_device_id_properties_.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES;
|
||||
vk_physical_device_properties.pNext = &vk_physical_device_driver_properties_;
|
||||
vk_physical_device_driver_properties_.pNext = &vk_physical_device_id_properties_;
|
||||
|
||||
if (supports_extension(VK_KHR_MAINTENANCE_4_EXTENSION_NAME)) {
|
||||
vk_physical_device_maintenance4_properties_.pNext = vk_physical_device_properties.pNext;
|
||||
vk_physical_device_properties.pNext = &vk_physical_device_maintenance4_properties_;
|
||||
}
|
||||
|
||||
if (supports_extension(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME)) {
|
||||
vk_physical_device_graphics_pipeline_library_properties_.pNext =
|
||||
vk_physical_device_properties.pNext;
|
||||
vk_physical_device_properties.pNext =
|
||||
&vk_physical_device_graphics_pipeline_library_properties_;
|
||||
}
|
||||
|
||||
vkGetPhysicalDeviceProperties2(vk_physical_device_, &vk_physical_device_properties);
|
||||
vk_physical_device_properties_ = vk_physical_device_properties.properties;
|
||||
}
|
||||
|
||||
void VKDevice::init_physical_device_memory_properties()
|
||||
{
|
||||
BLI_assert(vk_physical_device_ != VK_NULL_HANDLE);
|
||||
vkGetPhysicalDeviceMemoryProperties(vk_physical_device_, &vk_physical_device_memory_properties_);
|
||||
}
|
||||
|
||||
void VKDevice::init_physical_device_features()
|
||||
{
|
||||
BLI_assert(vk_physical_device_ != VK_NULL_HANDLE);
|
||||
|
||||
VkPhysicalDeviceFeatures2 features = {};
|
||||
features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
||||
vk_physical_device_vulkan_11_features_.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
|
||||
vk_physical_device_vulkan_12_features_.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
|
||||
|
||||
features.pNext = &vk_physical_device_vulkan_11_features_;
|
||||
vk_physical_device_vulkan_11_features_.pNext = &vk_physical_device_vulkan_12_features_;
|
||||
|
||||
vkGetPhysicalDeviceFeatures2(vk_physical_device_, &features);
|
||||
vk_physical_device_features_ = features.features;
|
||||
}
|
||||
|
||||
void VKDevice::init_physical_device_extensions()
|
||||
{
|
||||
uint32_t count = 0;
|
||||
vkEnumerateDeviceExtensionProperties(vk_physical_device_, nullptr, &count, nullptr);
|
||||
device_extensions_ = Array<VkExtensionProperties>(count);
|
||||
vkEnumerateDeviceExtensionProperties(
|
||||
vk_physical_device_, nullptr, &count, device_extensions_.data());
|
||||
}
|
||||
|
||||
bool VKDevice::supports_extension(const char *extension_name) const
|
||||
{
|
||||
for (const VkExtensionProperties &vk_extension_properties : device_extensions_) {
|
||||
if (STREQ(vk_extension_properties.extensionName, extension_name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void VKDevice::init_dummy_buffer()
|
||||
{
|
||||
dummy_buffer.create(sizeof(float4x4),
|
||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
|
||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
|
||||
1.0f,
|
||||
false,
|
||||
"DummyBuffer");
|
||||
/* Default dummy buffer. Set the 4th element to 1 to fix missing orcos. */
|
||||
float data[16] = {
|
||||
0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
||||
dummy_buffer.update_immediately(static_cast<void *>(data));
|
||||
}
|
||||
|
||||
shader::GeneratedSource VKDevice::extensions_define(StringRefNull stage_define) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "#version 450\n";
|
||||
{
|
||||
/* Required extension. */
|
||||
ss << "#extension GL_ARB_shader_draw_parameters : enable\n";
|
||||
ss << "#define GPU_ARB_shader_draw_parameters\n";
|
||||
ss << "#define gpu_BaseInstance (gl_BaseInstanceARB)\n";
|
||||
}
|
||||
ss << "#define GPU_ARB_clip_control\n";
|
||||
ss << "#define GPU_ARB_derivative_control\n";
|
||||
|
||||
ss << "#define gl_VertexID gl_VertexIndex\n";
|
||||
ss << "#define gpu_InstanceIndex (gl_InstanceIndex)\n";
|
||||
ss << "#define gl_InstanceID (gpu_InstanceIndex - gpu_BaseInstance)\n";
|
||||
|
||||
ss << "#extension GL_ARB_shader_viewport_layer_array: enable\n";
|
||||
if (GPU_stencil_export_support()) {
|
||||
ss << "#extension GL_ARB_shader_stencil_export: enable\n";
|
||||
ss << "#define GPU_ARB_shader_stencil_export 1\n";
|
||||
}
|
||||
if (extensions_.fragment_shader_barycentric) {
|
||||
ss << "#extension GL_EXT_fragment_shader_barycentric : require\n";
|
||||
ss << "#define gpu_BaryCoord gl_BaryCoordEXT\n";
|
||||
ss << "#define gpu_BaryCoordNoPersp gl_BaryCoordNoPerspEXT\n";
|
||||
}
|
||||
ss << stage_define;
|
||||
|
||||
return shader::GeneratedSource{"gpu_shader_glsl_extension.glsl", {}, ss.str()};
|
||||
}
|
||||
|
||||
std::string VKDevice::glsl_vertex_patch_get() const
|
||||
{
|
||||
shader::GeneratedSourceList sources{extensions_define("#define GPU_VERTEX_SHADER\n")};
|
||||
return fmt::to_string(fmt::join(
|
||||
gpu_shader_dependency_get_resolved_source("gpu_shader_compat_glsl.glsl", sources), ""));
|
||||
}
|
||||
|
||||
std::string VKDevice::glsl_geometry_patch_get() const
|
||||
{
|
||||
shader::GeneratedSourceList sources{extensions_define("#define GPU_GEOMETRY_SHADER\n")};
|
||||
return fmt::to_string(fmt::join(
|
||||
gpu_shader_dependency_get_resolved_source("gpu_shader_compat_glsl.glsl", sources), ""));
|
||||
}
|
||||
|
||||
std::string VKDevice::glsl_fragment_patch_get() const
|
||||
{
|
||||
shader::GeneratedSourceList sources{extensions_define("#define GPU_FRAGMENT_SHADER\n")};
|
||||
return fmt::to_string(fmt::join(
|
||||
gpu_shader_dependency_get_resolved_source("gpu_shader_compat_glsl.glsl", sources), ""));
|
||||
}
|
||||
|
||||
std::string VKDevice::glsl_compute_patch_get() const
|
||||
{
|
||||
shader::GeneratedSourceList sources{extensions_define("#define GPU_COMPUTE_SHADER\n")};
|
||||
return fmt::to_string(fmt::join(
|
||||
gpu_shader_dependency_get_resolved_source("gpu_shader_compat_glsl.glsl", sources), ""));
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Platform/driver/device information
|
||||
* \{ */
|
||||
|
||||
constexpr int32_t PCI_ID_NVIDIA = 0x10de;
|
||||
constexpr int32_t PCI_ID_INTEL = 0x8086;
|
||||
constexpr int32_t PCI_ID_AMD = 0x1002;
|
||||
constexpr int32_t PCI_ID_ATI = 0x1022;
|
||||
constexpr int32_t PCI_ID_APPLE = 0x106b;
|
||||
|
||||
GPUDeviceType VKDevice::device_type() const
|
||||
{
|
||||
switch (vk_physical_device_driver_properties_.driverID) {
|
||||
case VK_DRIVER_ID_AMD_PROPRIETARY:
|
||||
case VK_DRIVER_ID_AMD_OPEN_SOURCE:
|
||||
case VK_DRIVER_ID_MESA_RADV:
|
||||
return GPU_DEVICE_ATI;
|
||||
|
||||
case VK_DRIVER_ID_NVIDIA_PROPRIETARY:
|
||||
case VK_DRIVER_ID_MESA_NVK:
|
||||
return GPU_DEVICE_NVIDIA;
|
||||
|
||||
case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS:
|
||||
case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA:
|
||||
return GPU_DEVICE_INTEL;
|
||||
|
||||
case VK_DRIVER_ID_QUALCOMM_PROPRIETARY:
|
||||
return GPU_DEVICE_QUALCOMM;
|
||||
|
||||
case VK_DRIVER_ID_MOLTENVK:
|
||||
return GPU_DEVICE_APPLE;
|
||||
|
||||
case VK_DRIVER_ID_MESA_LLVMPIPE:
|
||||
return GPU_DEVICE_SOFTWARE;
|
||||
|
||||
default:
|
||||
return GPU_DEVICE_UNKNOWN;
|
||||
}
|
||||
|
||||
return GPU_DEVICE_UNKNOWN;
|
||||
}
|
||||
|
||||
GPUDriverType VKDevice::driver_type() const
|
||||
{
|
||||
switch (vk_physical_device_driver_properties_.driverID) {
|
||||
case VK_DRIVER_ID_AMD_PROPRIETARY:
|
||||
case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS:
|
||||
case VK_DRIVER_ID_NVIDIA_PROPRIETARY:
|
||||
case VK_DRIVER_ID_QUALCOMM_PROPRIETARY:
|
||||
/* NOTE: Marking AMDVLK as an official driver to make distinction between Mesa and AMD open
|
||||
* source. AMDVLK is being replaced by Mesa in the official driver stack. */
|
||||
case VK_DRIVER_ID_AMD_OPEN_SOURCE:
|
||||
return GPU_DRIVER_OFFICIAL;
|
||||
|
||||
case VK_DRIVER_ID_MOLTENVK:
|
||||
case VK_DRIVER_ID_MESA_RADV:
|
||||
case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA:
|
||||
case VK_DRIVER_ID_MESA_NVK:
|
||||
return GPU_DRIVER_OPENSOURCE;
|
||||
|
||||
case VK_DRIVER_ID_MESA_LLVMPIPE:
|
||||
return GPU_DRIVER_SOFTWARE;
|
||||
|
||||
default:
|
||||
return GPU_DRIVER_ANY;
|
||||
}
|
||||
|
||||
return GPU_DRIVER_ANY;
|
||||
}
|
||||
|
||||
std::string VKDevice::vendor_name() const
|
||||
{
|
||||
/* Below 0x10000 are the PCI vendor IDs (https://pcisig.com/membership/member-companies) */
|
||||
if (vk_physical_device_properties_.vendorID < 0x10000) {
|
||||
switch (vk_physical_device_properties_.vendorID) {
|
||||
case PCI_ID_AMD:
|
||||
case PCI_ID_ATI:
|
||||
return "Advanced Micro Devices";
|
||||
case PCI_ID_NVIDIA:
|
||||
return "NVIDIA Corporation";
|
||||
case PCI_ID_INTEL:
|
||||
return "Intel Corporation";
|
||||
case PCI_ID_APPLE:
|
||||
return "Apple";
|
||||
default:
|
||||
return std::to_string(vk_physical_device_properties_.vendorID);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* above 0x10000 should be vkVendorIDs
|
||||
* NOTE: When debug_messaging landed we can use something similar to
|
||||
* vk::to_string(vk::VendorId(properties.vendorID));
|
||||
*/
|
||||
return std::to_string(vk_physical_device_properties_.vendorID);
|
||||
}
|
||||
}
|
||||
|
||||
std::string VKDevice::driver_version() const
|
||||
{
|
||||
return StringRefNull(vk_physical_device_driver_properties_.driverName) + " " +
|
||||
StringRefNull(vk_physical_device_driver_properties_.driverInfo);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name VKThreadData
|
||||
* \{ */
|
||||
|
||||
VKThreadData::VKThreadData(VKDevice &device, pthread_t thread_id) : thread_id(thread_id)
|
||||
{
|
||||
descriptor_pools.init(device);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Resource management
|
||||
* \{ */
|
||||
|
||||
VKThreadData &VKDevice::current_thread_data()
|
||||
{
|
||||
std::scoped_lock mutex(resources.mutex);
|
||||
pthread_t current_thread_id = pthread_self();
|
||||
|
||||
for (VKThreadData *thread_data : thread_data_) {
|
||||
if (pthread_equal(thread_data->thread_id, current_thread_id)) {
|
||||
return *thread_data;
|
||||
}
|
||||
}
|
||||
|
||||
VKThreadData *thread_data = new VKThreadData(*this, current_thread_id);
|
||||
thread_data_.append(thread_data);
|
||||
return *thread_data;
|
||||
}
|
||||
|
||||
void VKDevice::context_register(VKContext &context)
|
||||
{
|
||||
contexts_.append(std::reference_wrapper(context));
|
||||
}
|
||||
|
||||
void VKDevice::context_unregister(VKContext &context)
|
||||
{
|
||||
if (context.render_graph_.has_value()) {
|
||||
render_graph::VKRenderGraph &render_graph = context.render_graph();
|
||||
context.render_graph_.reset();
|
||||
BLI_assert_msg(render_graph.is_empty(),
|
||||
"Unregistering a context that still has an unsubmitted render graph.");
|
||||
render_graph.reset();
|
||||
BLI_thread_queue_push(
|
||||
unused_render_graphs_, &render_graph, BLI_THREAD_QUEUE_WORK_PRIORITY_NORMAL);
|
||||
}
|
||||
{
|
||||
std::scoped_lock lock(orphaned_data.mutex_get());
|
||||
orphaned_data.move_data(context.discard_pool, timeline_value_ + 1);
|
||||
}
|
||||
|
||||
contexts_.remove(contexts_.first_index_of(std::reference_wrapper(context)));
|
||||
}
|
||||
Span<std::reference_wrapper<VKContext>> VKDevice::contexts_get() const
|
||||
{
|
||||
return contexts_;
|
||||
};
|
||||
|
||||
void VKDevice::memory_statistics_get(int *r_total_mem_kb, int *r_free_mem_kb) const
|
||||
{
|
||||
VmaBudget budgets[VK_MAX_MEMORY_HEAPS];
|
||||
vmaGetHeapBudgets(mem_allocator_get(), budgets);
|
||||
VkDeviceSize total_mem = 0;
|
||||
VkDeviceSize used_mem = 0;
|
||||
|
||||
for (int memory_heap_index : IndexRange(vk_physical_device_memory_properties_.memoryHeapCount)) {
|
||||
const VkMemoryHeap &memory_heap =
|
||||
vk_physical_device_memory_properties_.memoryHeaps[memory_heap_index];
|
||||
const VmaBudget &budget = budgets[memory_heap_index];
|
||||
|
||||
/* Skip host memory-heaps. */
|
||||
if (!bool(memory_heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
total_mem += memory_heap.size;
|
||||
used_mem += budget.usage;
|
||||
}
|
||||
|
||||
*r_total_mem_kb = int(total_mem / 1024);
|
||||
*r_free_mem_kb = int((total_mem - used_mem) / 1024);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Debugging/statistics
|
||||
* \{ */
|
||||
|
||||
void VKDevice::debug_print() const
|
||||
{
|
||||
BLI_assert_msg(BLI_thread_is_main(),
|
||||
"VKDevice::debug_print can only be called from the main thread.");
|
||||
|
||||
resources.debug_print();
|
||||
std::ostream &os = std::cout;
|
||||
os << "Pipelines\n";
|
||||
os << " Graphics: " << pipelines.graphics_.size() << "\n";
|
||||
os << " Compute: " << pipelines.compute_.size() << "\n";
|
||||
os << " VertexInLib: " << pipelines.vertex_input_libs_.size() << "\n";
|
||||
os << " ShaderLib: " << pipelines.shaders_libs_.size() << "\n";
|
||||
os << " FragmentOutLib: " << pipelines.fragment_output_libs_.size() << "\n";
|
||||
os << "Descriptor sets\n";
|
||||
os << " VkDescriptorSetLayouts: " << descriptor_set_layouts_.size() << "\n";
|
||||
for (const VKThreadData *thread_data : thread_data_) {
|
||||
/* NOTE: Assumption that this is always called form the main thread. This could be solved by
|
||||
* keeping track of the main thread inside the thread data. */
|
||||
const bool is_main = pthread_equal(thread_data->thread_id, pthread_self());
|
||||
os << "ThreadData" << (is_main ? " (main-thread)" : "") << ")\n";
|
||||
os << " Rendering_depth: " << thread_data->rendering_depth << "\n";
|
||||
}
|
||||
os << "Discard pool\n";
|
||||
os << orphaned_data << "\n";
|
||||
os << "Discard pool (render)\n";
|
||||
os << orphaned_data_render << "\n";
|
||||
os << "\n";
|
||||
|
||||
for (const std::reference_wrapper<VKContext> &context : contexts_) {
|
||||
os << " VKContext \n";
|
||||
os << context.get().discard_pool << "\n";
|
||||
}
|
||||
|
||||
int total_mem_kb;
|
||||
int free_mem_kb;
|
||||
memory_statistics_get(&total_mem_kb, &free_mem_kb);
|
||||
os << "\nMemory: total=" << total_mem_kb << ", free=" << free_mem_kb << "\n";
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
500
blender-5.2.0/source/blender/gpu/vulkan/vk_device.hh
Normal file
500
blender-5.2.0/source/blender/gpu/vulkan/vk_device.hh
Normal file
@@ -0,0 +1,500 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "BLI_task.h"
|
||||
#include "BLI_threads.h"
|
||||
#include "BLI_utility_mixins.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "render_graph/vk_render_graph.hh"
|
||||
#include "render_graph/vk_resource_state_tracker.hh"
|
||||
#include "vk_buffer.hh"
|
||||
#include "vk_common.hh"
|
||||
#include "vk_debug.hh"
|
||||
#include "vk_descriptor_pools.hh"
|
||||
#include "vk_descriptor_set_layouts.hh"
|
||||
#include "vk_memory_pool.hh"
|
||||
#include "vk_pipeline_pool.hh"
|
||||
#include "vk_resource_pool.hh"
|
||||
#include "vk_samplers.hh"
|
||||
#include "vk_vertex_attribute_object.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
class VKBackend;
|
||||
|
||||
struct VKExtensions {
|
||||
/** Does the device support VkPhysicalDeviceVulkan12Features::shaderOutputViewportIndex. */
|
||||
bool shader_output_viewport_index = false;
|
||||
/** Does the device support VkPhysicalDeviceVulkan12Features::shaderOutputLayer. */
|
||||
bool shader_output_layer = false;
|
||||
/**
|
||||
* Does the device support
|
||||
* VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR::fragmentShaderBarycentric.
|
||||
*/
|
||||
bool fragment_shader_barycentric = false;
|
||||
|
||||
/**
|
||||
* Does the device support wide line rendering
|
||||
* VkPhysicalDeviceFeatures::wideLines
|
||||
*/
|
||||
bool wide_lines = false;
|
||||
|
||||
/**
|
||||
* Does the device support VK_KHR_dynamic_rendering_local_read enabled.
|
||||
*/
|
||||
bool dynamic_rendering_local_read = false;
|
||||
|
||||
/**
|
||||
* Does the device support VK_EXT_dynamic_rendering_unused_attachments.
|
||||
*/
|
||||
bool dynamic_rendering_unused_attachments = false;
|
||||
|
||||
/**
|
||||
* Does the device support VK_EXT_external_memory_win32/VK_EXT_external_memory_fd
|
||||
*/
|
||||
bool external_memory = false;
|
||||
|
||||
/** VK_KHR_maintenance4 */
|
||||
bool maintenance4 = false;
|
||||
|
||||
/**
|
||||
* Does the device support logic ops.
|
||||
*/
|
||||
bool logic_ops = false;
|
||||
|
||||
/**
|
||||
* Does the device support VK_EXT_memory_priority
|
||||
*/
|
||||
bool memory_priority = false;
|
||||
|
||||
/**
|
||||
* Does the device support VK_EXT_pageable_device_local_memory
|
||||
*/
|
||||
bool pageable_device_local_memory = false;
|
||||
|
||||
/**
|
||||
* Does the device support VK_EXT_graphics_pipeline_library
|
||||
*/
|
||||
bool graphics_pipeline_library = false;
|
||||
|
||||
/**
|
||||
* Does the device support VK_EXT_line_rasterization
|
||||
*/
|
||||
bool line_rasterization = false;
|
||||
|
||||
/**
|
||||
* Does the device support VK_EXT_extended_dynamic_state
|
||||
*/
|
||||
bool extended_dynamic_state = false;
|
||||
|
||||
/**
|
||||
* Does the device support VK_EXT_vertex_input_dynamic_state
|
||||
*/
|
||||
bool vertex_input_dynamic_state = false;
|
||||
|
||||
/**
|
||||
* Does the device support VK_EXT_host_image_copy
|
||||
*/
|
||||
bool host_image_copy = false;
|
||||
|
||||
/** Log enabled features and extensions. */
|
||||
void log() const;
|
||||
};
|
||||
|
||||
struct VKWorkarounds {
|
||||
/**
|
||||
* Some devices don't support pixel formats that are aligned to 24 and 48 bits.
|
||||
* In this case we need to use a different texture format.
|
||||
*
|
||||
* If set to true we should work around this issue by using a different texture format.
|
||||
*/
|
||||
bool not_aligned_pixel_formats = false;
|
||||
|
||||
/** Log enabled workarounds. */
|
||||
void log() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Shared resources between contexts that run in the same thread.
|
||||
*/
|
||||
class VKThreadData : public NonCopyable, NonMovable {
|
||||
public:
|
||||
/** Thread ID this instance belongs to. */
|
||||
pthread_t thread_id;
|
||||
VKDescriptorPools descriptor_pools;
|
||||
VKDescriptorSetTracker descriptor_set;
|
||||
|
||||
/**
|
||||
* The current rendering depth.
|
||||
*
|
||||
* GPU_rendering_begin can be called multiple times forming a hierarchy. The same resource pool
|
||||
* should be used for the whole hierarchy. rendering_depth is increased for every
|
||||
* GPU_rendering_begin and decreased when GPU_rendering_end is called. Resources pools are cycled
|
||||
* when the rendering_depth set to 0.
|
||||
*/
|
||||
int32_t rendering_depth = 0;
|
||||
|
||||
VKThreadData(VKDevice &device, pthread_t thread_id);
|
||||
};
|
||||
|
||||
class VKDevice : public NonCopyable {
|
||||
private:
|
||||
/** Copies of the handles owned by the GHOST context. */
|
||||
VkInstance vk_instance_ = VK_NULL_HANDLE;
|
||||
VkPhysicalDevice vk_physical_device_ = VK_NULL_HANDLE;
|
||||
VkDevice vk_device_ = VK_NULL_HANDLE;
|
||||
uint32_t vk_queue_family_ = 0;
|
||||
VkQueue vk_queue_ = VK_NULL_HANDLE;
|
||||
std::mutex *queue_mutex_ = nullptr;
|
||||
|
||||
bool is_initialized_ = false;
|
||||
|
||||
/**
|
||||
* Task pool for render graph submission.
|
||||
*
|
||||
* Multiple threads in Blender can build a render graph. Building the command buffer for a render
|
||||
* graph is faster when doing it in serial. Submission pool ensures that only one task is
|
||||
* building at a time (background_serial).
|
||||
*/
|
||||
TaskPool *submission_pool_ = nullptr;
|
||||
/**
|
||||
* All created render graphs.
|
||||
*/
|
||||
Vector<render_graph::VKRenderGraph *> render_graphs_;
|
||||
ThreadQueue *submitted_render_graphs_ = nullptr;
|
||||
ThreadQueue *unused_render_graphs_ = nullptr;
|
||||
VkSemaphore vk_timeline_semaphore_ = VK_NULL_HANDLE;
|
||||
/**
|
||||
* Last used timeline value.
|
||||
*
|
||||
* Must be externally synced by orphaned_data.mutex_get()
|
||||
*/
|
||||
TimelineValue timeline_value_ = 0;
|
||||
|
||||
VKSamplers samplers_;
|
||||
VKDescriptorSetLayouts descriptor_set_layouts_;
|
||||
|
||||
/**
|
||||
* Available Contexts for this device.
|
||||
*
|
||||
* Device keeps track of each contexts. When buffers/images are freed they need to be removed
|
||||
* from all contexts state managers.
|
||||
*
|
||||
* The contexts inside this list aren't owned by the VKDevice. Caller of `GPU_context_create`
|
||||
* holds the ownership.
|
||||
*/
|
||||
Vector<std::reference_wrapper<VKContext>> contexts_;
|
||||
|
||||
/** Allocator used for texture and buffers and other resources. */
|
||||
VmaAllocator mem_allocator_ = VK_NULL_HANDLE;
|
||||
|
||||
/** Limits of the device linked to this context. */
|
||||
VkPhysicalDeviceProperties vk_physical_device_properties_ = {};
|
||||
VkPhysicalDeviceDriverProperties vk_physical_device_driver_properties_ = {};
|
||||
VkPhysicalDeviceIDProperties vk_physical_device_id_properties_ = {};
|
||||
VkPhysicalDeviceMemoryProperties vk_physical_device_memory_properties_ = {};
|
||||
VkPhysicalDeviceMaintenance4Properties vk_physical_device_maintenance4_properties_ = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_PROPERTIES};
|
||||
VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT
|
||||
vk_physical_device_graphics_pipeline_library_properties_ = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_PROPERTIES_EXT};
|
||||
/** Features support. */
|
||||
VkPhysicalDeviceFeatures vk_physical_device_features_ = {};
|
||||
VkPhysicalDeviceVulkan11Features vk_physical_device_vulkan_11_features_ = {};
|
||||
VkPhysicalDeviceVulkan12Features vk_physical_device_vulkan_12_features_ = {};
|
||||
Array<VkExtensionProperties> device_extensions_;
|
||||
|
||||
/** Functions of vk_ext_debugutils for this device/instance. */
|
||||
debug::VKDebuggingTools debugging_tools_;
|
||||
|
||||
/* Workarounds */
|
||||
VKWorkarounds workarounds_;
|
||||
VKExtensions extensions_;
|
||||
|
||||
std::string glsl_vert_patch_;
|
||||
std::string glsl_geom_patch_;
|
||||
std::string glsl_frag_patch_;
|
||||
std::string glsl_comp_patch_;
|
||||
Vector<VKThreadData *> thread_data_;
|
||||
|
||||
Shader *vk_backbuffer_blit_sh_ = nullptr;
|
||||
|
||||
public:
|
||||
render_graph::VKResourceStateTracker resources;
|
||||
VKDiscardPool orphaned_data;
|
||||
/** Discard pool for resources that could still be used during rendering. */
|
||||
VKDiscardPool orphaned_data_render;
|
||||
VKPipelinePool pipelines;
|
||||
VKVertexInputDescriptionPool vertex_input_descriptions;
|
||||
|
||||
/** Buffer to bind to unbound resource locations. */
|
||||
VKBuffer dummy_buffer;
|
||||
|
||||
/**
|
||||
* This struct contains the functions pointer to extension provided functions.
|
||||
*/
|
||||
struct {
|
||||
/* Extension: VK_KHR_dynamic_rendering */
|
||||
PFN_vkCmdBeginRendering vkCmdBeginRendering = nullptr;
|
||||
PFN_vkCmdEndRendering vkCmdEndRendering = nullptr;
|
||||
|
||||
/* Extension: VK_EXT_debug_utils */
|
||||
PFN_vkCmdBeginDebugUtilsLabelEXT vkCmdBeginDebugUtilsLabel = nullptr;
|
||||
PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabel = nullptr;
|
||||
PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectName = nullptr;
|
||||
PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessenger = nullptr;
|
||||
PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessenger = nullptr;
|
||||
|
||||
/* Extension: VK_EXT_extended_dynamic_state */
|
||||
PFN_vkCmdSetFrontFace vkCmdSetFrontFace = nullptr;
|
||||
|
||||
/* Extension: VK_EXT_vertex_input_dynamic_state */
|
||||
PFN_vkCmdSetVertexInputEXT vkCmdSetVertexInput = nullptr;
|
||||
|
||||
/* Extension: VK_KHR_external_memory_fd */
|
||||
PFN_vkGetMemoryFdKHR vkGetMemoryFd = nullptr;
|
||||
|
||||
/* Extension: VK_EXT_host_image_copy */
|
||||
PFN_vkCopyMemoryToImageEXT vkCopyMemoryToImage = nullptr;
|
||||
PFN_vkTransitionImageLayoutEXT vkTransitionImageLayout = nullptr;
|
||||
|
||||
/* Extension: VK_KHR_maintenance4 */
|
||||
PFN_vkGetDeviceImageMemoryRequirements vkGetDeviceImageMemoryRequirements = nullptr;
|
||||
PFN_vkGetDeviceBufferMemoryRequirements vkGetDeviceBufferMemoryRequirements = nullptr;
|
||||
|
||||
#ifdef _WIN32
|
||||
/* Extension: VK_KHR_external_memory_win32 */
|
||||
PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32Handle = nullptr;
|
||||
#endif
|
||||
|
||||
} functions;
|
||||
|
||||
VKMemoryPools vma_pools;
|
||||
|
||||
const char *extension_name_get(int index) const
|
||||
{
|
||||
return device_extensions_[index].extensionName;
|
||||
}
|
||||
|
||||
VkPhysicalDevice physical_device_get() const
|
||||
{
|
||||
return vk_physical_device_;
|
||||
}
|
||||
|
||||
const VkPhysicalDeviceProperties &physical_device_properties_get() const
|
||||
{
|
||||
return vk_physical_device_properties_;
|
||||
}
|
||||
|
||||
inline const VkPhysicalDeviceMaintenance4Properties &
|
||||
physical_device_maintenance4_properties_get() const
|
||||
{
|
||||
return vk_physical_device_maintenance4_properties_;
|
||||
}
|
||||
|
||||
const VkPhysicalDeviceIDProperties &physical_device_id_properties_get() const
|
||||
{
|
||||
return vk_physical_device_id_properties_;
|
||||
}
|
||||
|
||||
inline const VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT &
|
||||
physical_device_graphics_pipeline_properties_get()
|
||||
{
|
||||
return vk_physical_device_graphics_pipeline_library_properties_;
|
||||
}
|
||||
|
||||
const VkPhysicalDeviceFeatures &physical_device_features_get() const
|
||||
{
|
||||
return vk_physical_device_features_;
|
||||
}
|
||||
|
||||
const VkPhysicalDeviceVulkan11Features &physical_device_vulkan_11_features_get() const
|
||||
{
|
||||
return vk_physical_device_vulkan_11_features_;
|
||||
}
|
||||
|
||||
const VkPhysicalDeviceVulkan12Features &physical_device_vulkan_12_features_get() const
|
||||
{
|
||||
return vk_physical_device_vulkan_12_features_;
|
||||
}
|
||||
|
||||
VkInstance instance_get() const
|
||||
{
|
||||
return vk_instance_;
|
||||
};
|
||||
|
||||
VkDevice vk_handle() const
|
||||
{
|
||||
return vk_device_;
|
||||
}
|
||||
|
||||
uint32_t queue_family_get() const
|
||||
{
|
||||
return vk_queue_family_;
|
||||
}
|
||||
|
||||
inline VmaAllocator mem_allocator_get() const
|
||||
{
|
||||
return mem_allocator_;
|
||||
}
|
||||
|
||||
VKDescriptorSetLayouts &descriptor_set_layouts_get()
|
||||
{
|
||||
return descriptor_set_layouts_;
|
||||
}
|
||||
|
||||
debug::VKDebuggingTools &debugging_tools_get()
|
||||
{
|
||||
return debugging_tools_;
|
||||
}
|
||||
|
||||
const debug::VKDebuggingTools &debugging_tools_get() const
|
||||
{
|
||||
return debugging_tools_;
|
||||
}
|
||||
|
||||
const VKSamplers &samplers() const
|
||||
{
|
||||
return samplers_;
|
||||
}
|
||||
|
||||
void init(GHOST_IContext *ghost_context);
|
||||
void reinit();
|
||||
void deinit();
|
||||
bool is_initialized() const
|
||||
{
|
||||
return is_initialized_;
|
||||
}
|
||||
|
||||
GPUDeviceType device_type() const;
|
||||
GPUDriverType driver_type() const;
|
||||
std::string vendor_name() const;
|
||||
std::string driver_version() const;
|
||||
|
||||
/**
|
||||
* Check if a specific extension is supported by the device.
|
||||
*
|
||||
* This should be called from vk_backend to set the correct capabilities and workarounds needed
|
||||
* for this device.
|
||||
*/
|
||||
bool supports_extension(const char *extension_name) const;
|
||||
|
||||
const VKWorkarounds &workarounds_get() const
|
||||
{
|
||||
return workarounds_;
|
||||
}
|
||||
inline const VKExtensions &extensions_get() const
|
||||
{
|
||||
return extensions_;
|
||||
}
|
||||
|
||||
std::string glsl_vertex_patch_get() const;
|
||||
std::string glsl_geometry_patch_get() const;
|
||||
std::string glsl_fragment_patch_get() const;
|
||||
std::string glsl_compute_patch_get() const;
|
||||
shader::GeneratedSource extensions_define(StringRefNull stage_define) const;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Render graph
|
||||
* \{ */
|
||||
static void submission_runner(TaskPool *__restrict pool, void *task_data);
|
||||
render_graph::VKRenderGraph *render_graph_new();
|
||||
|
||||
TimelineValue render_graph_submit(render_graph::VKRenderGraph *render_graph,
|
||||
VKDiscardPool &context_discard_pool,
|
||||
bool submit_to_device,
|
||||
bool wait_for_submission,
|
||||
bool wait_for_completion,
|
||||
VkPipelineStageFlags wait_dst_stage_mask,
|
||||
VkSemaphore wait_semaphore,
|
||||
VkSemaphore signal_semaphore,
|
||||
VkFence signal_fence);
|
||||
void wait_for_timeline(TimelineValue timeline);
|
||||
void wait_queue_idle();
|
||||
|
||||
/**
|
||||
* Retrieve the last finished submission timeline.
|
||||
*/
|
||||
TimelineValue submission_finished_timeline_get() const
|
||||
{
|
||||
BLI_assert(vk_timeline_semaphore_ != VK_NULL_HANDLE);
|
||||
TimelineValue current_timeline;
|
||||
VkResult result = vkGetSemaphoreCounterValue(
|
||||
vk_device_, vk_timeline_semaphore_, ¤t_timeline);
|
||||
UNUSED_VARS(result);
|
||||
BLI_assert_msg(
|
||||
result == VK_SUCCESS && current_timeline != UINT64_MAX,
|
||||
"Potential driver crash has happened. Several drivers will report UINT64_MAX when "
|
||||
"requesting a counter value of an timeline semaphore right after/during a driver reset. "
|
||||
"If this happen we should investigate what makes the driver crash. In the past this has "
|
||||
"been detected on QUALCOMM and NVIDIA drivers. The result code of the call is "
|
||||
"VK_SUCCESS.");
|
||||
return current_timeline;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Resource management
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Get or create current thread data.
|
||||
*/
|
||||
VKThreadData ¤t_thread_data();
|
||||
|
||||
void context_register(VKContext &context);
|
||||
void context_unregister(VKContext &context);
|
||||
Span<std::reference_wrapper<VKContext>> contexts_get() const;
|
||||
|
||||
void memory_statistics_get(int *r_total_mem_kb, int *r_free_mem_kb) const;
|
||||
void debug_print() const;
|
||||
|
||||
/** \} */
|
||||
|
||||
Shader *vk_backbuffer_blit_sh_get()
|
||||
{
|
||||
if (vk_backbuffer_blit_sh_ == nullptr) {
|
||||
/* See #system_extended_srgb_transfer_function in libocio_display_processor.cc for
|
||||
* details on this choice. */
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
vk_backbuffer_blit_sh_ = GPU_shader_create_from_info_name("vk_backbuffer_blit");
|
||||
#else
|
||||
vk_backbuffer_blit_sh_ = GPU_shader_create_from_info_name("vk_backbuffer_blit_gamma22");
|
||||
#endif
|
||||
}
|
||||
return vk_backbuffer_blit_sh_;
|
||||
}
|
||||
|
||||
private:
|
||||
void init_physical_device_properties();
|
||||
void init_physical_device_memory_properties();
|
||||
void init_physical_device_features();
|
||||
void init_physical_device_extensions();
|
||||
void init_debug_callbacks();
|
||||
void init_submission_pool();
|
||||
void deinit_submission_pool();
|
||||
/**
|
||||
* Initialize the functions struct with extension specific function pointer.
|
||||
*/
|
||||
void init_functions();
|
||||
|
||||
/**
|
||||
* Initialize a dummy buffer that can be bound for missing attributes.
|
||||
*/
|
||||
void init_dummy_buffer();
|
||||
|
||||
/* During initialization the backend requires access to update the workarounds. */
|
||||
friend VKBackend;
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
325
blender-5.2.0/source/blender/gpu/vulkan/vk_device_submission.cc
Normal file
325
blender-5.2.0/source/blender/gpu/vulkan/vk_device_submission.cc
Normal file
@@ -0,0 +1,325 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
|
||||
#include "BLI_mutex.hh"
|
||||
#include "BLI_task.h"
|
||||
|
||||
#include "vk_device.hh"
|
||||
#include "vk_to_string.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static CLG_LogRef LOG = {"gpu.vulkan"};
|
||||
|
||||
namespace gpu {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Render graph
|
||||
* \{ */
|
||||
|
||||
struct VKRenderGraphWait {
|
||||
Mutex is_submitted_mutex;
|
||||
std::condition_variable_any is_submitted_condition;
|
||||
bool is_submitted;
|
||||
};
|
||||
|
||||
struct VKRenderGraphSubmitTask {
|
||||
render_graph::VKRenderGraph *render_graph;
|
||||
uint64_t timeline;
|
||||
bool submit_to_device;
|
||||
VkPipelineStageFlags wait_dst_stage_mask;
|
||||
VkSemaphore wait_semaphore;
|
||||
VkSemaphore signal_semaphore;
|
||||
VkFence signal_fence;
|
||||
VKRenderGraphWait *wait_for_submission;
|
||||
};
|
||||
|
||||
TimelineValue VKDevice::render_graph_submit(render_graph::VKRenderGraph *render_graph,
|
||||
VKDiscardPool &context_discard_pool,
|
||||
bool submit_to_device,
|
||||
bool wait_for_submission,
|
||||
bool wait_for_completion,
|
||||
VkPipelineStageFlags wait_dst_stage_mask,
|
||||
VkSemaphore wait_semaphore,
|
||||
VkSemaphore signal_semaphore,
|
||||
VkFence signal_fence)
|
||||
{
|
||||
/* Syncing input flags. */
|
||||
/* When we wait for completion/submission we must submit to device. */
|
||||
submit_to_device |= wait_for_completion;
|
||||
submit_to_device |= wait_for_submission;
|
||||
/* We don't need to wait for submission when waiting for completion. */
|
||||
wait_for_submission &= !wait_for_completion;
|
||||
|
||||
VKRenderGraphSubmitTask *submit_task = MEM_new<VKRenderGraphSubmitTask>(__func__);
|
||||
submit_task->render_graph = render_graph;
|
||||
submit_task->submit_to_device = submit_to_device;
|
||||
submit_task->wait_dst_stage_mask = wait_dst_stage_mask;
|
||||
submit_task->wait_semaphore = wait_semaphore;
|
||||
submit_task->signal_semaphore = signal_semaphore;
|
||||
submit_task->signal_fence = signal_fence;
|
||||
submit_task->wait_for_submission = nullptr;
|
||||
|
||||
VKRenderGraphWait wait_condition{};
|
||||
if (wait_for_submission) {
|
||||
submit_task->wait_for_submission = &wait_condition;
|
||||
}
|
||||
TimelineValue timeline = 0;
|
||||
{
|
||||
std::scoped_lock lock(orphaned_data.mutex_get());
|
||||
timeline = submit_task->timeline = submit_to_device ? ++timeline_value_ : timeline_value_ + 1;
|
||||
orphaned_data.timeline_ = timeline;
|
||||
orphaned_data.move_data(context_discard_pool, timeline);
|
||||
BLI_thread_queue_push(
|
||||
submitted_render_graphs_, submit_task, BLI_THREAD_QUEUE_WORK_PRIORITY_NORMAL);
|
||||
}
|
||||
submit_task = nullptr;
|
||||
|
||||
if (wait_for_submission) {
|
||||
std::unique_lock<Mutex> lock(wait_condition.is_submitted_mutex);
|
||||
wait_condition.is_submitted_condition.wait(lock, [&] { return wait_condition.is_submitted; });
|
||||
}
|
||||
|
||||
if (wait_for_completion) {
|
||||
wait_for_timeline(timeline);
|
||||
}
|
||||
return timeline;
|
||||
}
|
||||
|
||||
void VKDevice::wait_for_timeline(TimelineValue timeline)
|
||||
{
|
||||
if (timeline == 0) {
|
||||
return;
|
||||
}
|
||||
VkSemaphoreWaitInfo vk_semaphore_wait_info = {
|
||||
VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO, nullptr, 0, 1, &vk_timeline_semaphore_, &timeline};
|
||||
VkResult wait_result = vkWaitSemaphores(vk_device_, &vk_semaphore_wait_info, UINT64_MAX);
|
||||
if (wait_result != VK_SUCCESS) {
|
||||
CLOG_ERROR(
|
||||
&LOG, "Vulkan: failed to wait for synchronization timeline [%s]", to_string(wait_result));
|
||||
}
|
||||
}
|
||||
|
||||
void VKDevice::wait_queue_idle()
|
||||
{
|
||||
std::scoped_lock lock(*queue_mutex_);
|
||||
vkQueueWaitIdle(vk_queue_);
|
||||
}
|
||||
|
||||
render_graph::VKRenderGraph *VKDevice::render_graph_new()
|
||||
{
|
||||
render_graph::VKRenderGraph *render_graph = static_cast<render_graph::VKRenderGraph *>(
|
||||
BLI_thread_queue_pop_timeout(unused_render_graphs_, 0));
|
||||
if (render_graph) {
|
||||
return render_graph;
|
||||
}
|
||||
|
||||
std::scoped_lock lock(resources.mutex);
|
||||
render_graph = MEM_new<render_graph::VKRenderGraph>(__func__, resources);
|
||||
render_graphs_.append(render_graph);
|
||||
return render_graph;
|
||||
}
|
||||
|
||||
void VKDevice::submission_runner(TaskPool *__restrict pool, void *task_data)
|
||||
{
|
||||
CLOG_TRACE(&LOG, "Submission runner has started");
|
||||
UNUSED_VARS(task_data);
|
||||
|
||||
VKDevice *device = static_cast<VKDevice *>(BLI_task_pool_user_data(pool));
|
||||
VkCommandPool vk_command_pool = VK_NULL_HANDLE;
|
||||
VkCommandPoolCreateInfo vk_command_pool_create_info = {
|
||||
VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
nullptr,
|
||||
VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
||||
device->vk_queue_family_};
|
||||
vkCreateCommandPool(device->vk_device_, &vk_command_pool_create_info, nullptr, &vk_command_pool);
|
||||
|
||||
render_graph::VKScheduler scheduler;
|
||||
render_graph::VKCommandBuilder command_builder;
|
||||
Vector<VkCommandBuffer> command_buffers_unused;
|
||||
TimelineResources<VkCommandBuffer> command_buffers_in_use;
|
||||
VkCommandBuffer vk_command_buffer = VK_NULL_HANDLE;
|
||||
std::optional<render_graph::VKCommandBufferWrapper> command_buffer;
|
||||
uint64_t previous_gc_timeline = 0;
|
||||
uint64_t num_nodes = 0;
|
||||
|
||||
CLOG_TRACE(&LOG, "Submission runner initialized");
|
||||
while (!BLI_task_pool_current_canceled(pool)) {
|
||||
VKRenderGraphSubmitTask *submit_task = static_cast<VKRenderGraphSubmitTask *>(
|
||||
BLI_thread_queue_pop_timeout(device->submitted_render_graphs_, 1));
|
||||
if (submit_task == nullptr) {
|
||||
continue;
|
||||
}
|
||||
uint64_t current_timeline = device->submission_finished_timeline_get();
|
||||
if (assign_if_different(previous_gc_timeline, current_timeline)) {
|
||||
device->orphaned_data.destroy_discarded_resources(*device, current_timeline);
|
||||
}
|
||||
|
||||
if (!command_buffer.has_value()) {
|
||||
/* Check for completed command buffers that can be reused. */
|
||||
if (command_buffers_unused.is_empty()) {
|
||||
command_buffers_in_use.remove_old(current_timeline,
|
||||
[&](VkCommandBuffer vk_command_buffer) {
|
||||
command_buffers_unused.append(vk_command_buffer);
|
||||
});
|
||||
}
|
||||
|
||||
/* Create new command buffers when there are no left to be reused. */
|
||||
if (command_buffers_unused.is_empty()) {
|
||||
command_buffers_unused.resize(10, VK_NULL_HANDLE);
|
||||
VkCommandBufferAllocateInfo vk_command_buffer_allocate_info = {
|
||||
VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
nullptr,
|
||||
vk_command_pool,
|
||||
VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
10};
|
||||
vkAllocateCommandBuffers(
|
||||
device->vk_device_, &vk_command_buffer_allocate_info, command_buffers_unused.data());
|
||||
};
|
||||
|
||||
vk_command_buffer = command_buffers_unused.pop_last();
|
||||
command_buffer = std::make_optional<render_graph::VKCommandBufferWrapper>(
|
||||
vk_command_buffer, device->extensions_);
|
||||
command_buffer->begin_recording();
|
||||
}
|
||||
|
||||
BLI_assert(vk_command_buffer != VK_NULL_HANDLE);
|
||||
|
||||
render_graph::VKRenderGraph &render_graph = *submit_task->render_graph;
|
||||
Span<render_graph::NodeHandle> node_handles = scheduler.select_nodes(render_graph);
|
||||
{
|
||||
std::scoped_lock lock_resources(device->resources.mutex);
|
||||
command_builder.build_nodes(render_graph, *command_buffer, node_handles);
|
||||
}
|
||||
command_builder.record_commands(render_graph, *command_buffer, node_handles);
|
||||
num_nodes += node_handles.size();
|
||||
|
||||
if (submit_task->submit_to_device) {
|
||||
/* Finalize current command buffer. */
|
||||
command_buffer->end_recording();
|
||||
|
||||
uint32_t wait_semaphore_len = submit_task->wait_semaphore == VK_NULL_HANDLE ? 1 : 2;
|
||||
VkSemaphore wait_semaphores[2] = {device->vk_timeline_semaphore_,
|
||||
submit_task->wait_semaphore};
|
||||
uint64_t wait_semaphore_values[2] = {submit_task->timeline - 1, 0};
|
||||
|
||||
uint32_t signal_semaphore_len = submit_task->signal_semaphore == VK_NULL_HANDLE ? 1 : 2;
|
||||
VkSemaphore signal_semaphores[2] = {device->vk_timeline_semaphore_,
|
||||
submit_task->signal_semaphore};
|
||||
uint64_t signal_semaphore_values[2] = {submit_task->timeline, 0};
|
||||
VkPipelineStageFlags pipeline_stage_flags[2] = {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
submit_task->wait_dst_stage_mask};
|
||||
|
||||
VkTimelineSemaphoreSubmitInfo vk_timeline_semaphore_submit_info = {
|
||||
VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
|
||||
nullptr,
|
||||
wait_semaphore_len,
|
||||
wait_semaphore_values,
|
||||
signal_semaphore_len,
|
||||
signal_semaphore_values};
|
||||
VkSubmitInfo vk_submit_info = {VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
&vk_timeline_semaphore_submit_info,
|
||||
wait_semaphore_len,
|
||||
wait_semaphores,
|
||||
pipeline_stage_flags,
|
||||
1,
|
||||
&vk_command_buffer,
|
||||
signal_semaphore_len,
|
||||
signal_semaphores};
|
||||
|
||||
CLOG_TRACE(&LOG, "Submitting %u render graph nodes to device.", uint32_t(num_nodes));
|
||||
num_nodes = 0;
|
||||
|
||||
{
|
||||
std::scoped_lock lock_queue(*device->queue_mutex_);
|
||||
vkQueueSubmit(device->vk_queue_, 1, &vk_submit_info, submit_task->signal_fence);
|
||||
}
|
||||
if (submit_task->wait_for_submission != nullptr) {
|
||||
std::unique_lock<Mutex> lock(submit_task->wait_for_submission->is_submitted_mutex);
|
||||
submit_task->wait_for_submission->is_submitted = true;
|
||||
submit_task->wait_for_submission->is_submitted_condition.notify_one();
|
||||
}
|
||||
command_buffers_in_use.append_timeline(submit_task->timeline, vk_command_buffer);
|
||||
vk_command_buffer = VK_NULL_HANDLE;
|
||||
command_buffer.reset();
|
||||
}
|
||||
|
||||
render_graph.reset();
|
||||
BLI_thread_queue_push(device->unused_render_graphs_,
|
||||
std::move(submit_task->render_graph),
|
||||
BLI_THREAD_QUEUE_WORK_PRIORITY_NORMAL);
|
||||
MEM_delete<VKRenderGraphSubmitTask>(submit_task);
|
||||
}
|
||||
CLOG_TRACE(&LOG, "Submission runner is being canceled");
|
||||
|
||||
/* Clear command buffers and pool */
|
||||
{
|
||||
std::scoped_lock lock(*device->queue_mutex_);
|
||||
vkDeviceWaitIdle(device->vk_device_);
|
||||
}
|
||||
command_buffers_in_use.remove_old(UINT64_MAX, [&](VkCommandBuffer vk_command_buffer) {
|
||||
command_buffers_unused.append(vk_command_buffer);
|
||||
});
|
||||
vkFreeCommandBuffers(device->vk_device_,
|
||||
vk_command_pool,
|
||||
command_buffers_unused.size(),
|
||||
command_buffers_unused.data());
|
||||
vkDestroyCommandPool(device->vk_device_, vk_command_pool, nullptr);
|
||||
CLOG_TRACE(&LOG, "Submission runner finished");
|
||||
}
|
||||
|
||||
void VKDevice::init_submission_pool()
|
||||
{
|
||||
CLOG_TRACE(&LOG, "Create submission pool");
|
||||
submission_pool_ = BLI_task_pool_create_background_serial(this, TASK_PRIORITY_HIGH);
|
||||
submitted_render_graphs_ = BLI_thread_queue_init();
|
||||
unused_render_graphs_ = BLI_thread_queue_init();
|
||||
|
||||
VkSemaphoreTypeCreateInfo vk_semaphore_type_create_info = {
|
||||
VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO, nullptr, VK_SEMAPHORE_TYPE_TIMELINE, 0};
|
||||
VkSemaphoreCreateInfo vk_semaphore_create_info = {
|
||||
VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, &vk_semaphore_type_create_info, 0};
|
||||
vkCreateSemaphore(vk_device_, &vk_semaphore_create_info, nullptr, &vk_timeline_semaphore_);
|
||||
|
||||
BLI_task_pool_push(submission_pool_, VKDevice::submission_runner, nullptr, false, nullptr);
|
||||
}
|
||||
|
||||
void VKDevice::deinit_submission_pool()
|
||||
{
|
||||
CLOG_TRACE(&LOG, "Cancelling submission pool");
|
||||
BLI_task_pool_cancel(submission_pool_);
|
||||
CLOG_TRACE(&LOG, "Waiting for completion");
|
||||
BLI_task_pool_work_and_wait(submission_pool_);
|
||||
CLOG_TRACE(&LOG, "Freeing submission pool");
|
||||
BLI_task_pool_free(submission_pool_);
|
||||
submission_pool_ = nullptr;
|
||||
|
||||
while (!BLI_thread_queue_is_empty(submitted_render_graphs_)) {
|
||||
VKRenderGraphSubmitTask *submit_task = static_cast<VKRenderGraphSubmitTask *>(
|
||||
BLI_thread_queue_pop(submitted_render_graphs_));
|
||||
MEM_delete<VKRenderGraphSubmitTask>(submit_task);
|
||||
}
|
||||
BLI_thread_queue_free(submitted_render_graphs_);
|
||||
submitted_render_graphs_ = nullptr;
|
||||
BLI_thread_queue_free(unused_render_graphs_);
|
||||
unused_render_graphs_ = nullptr;
|
||||
|
||||
vkDestroySemaphore(vk_device_, vk_timeline_semaphore_, nullptr);
|
||||
vk_timeline_semaphore_ = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
35
blender-5.2.0/source/blender/gpu/vulkan/vk_fence.cc
Normal file
35
blender-5.2.0/source/blender/gpu/vulkan/vk_fence.cc
Normal file
@@ -0,0 +1,35 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "vk_fence.hh"
|
||||
#include "vk_backend.hh"
|
||||
#include "vk_common.hh"
|
||||
#include "vk_context.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
void VKFence::signal()
|
||||
{
|
||||
VKContext &context = *VKContext::get();
|
||||
timeline_value_ = context.flush_render_graph(RenderGraphFlushFlags::SUBMIT |
|
||||
RenderGraphFlushFlags::RENEW_RENDER_GRAPH);
|
||||
signalled_ = true;
|
||||
}
|
||||
|
||||
void VKFence::wait()
|
||||
{
|
||||
if (!signalled_) {
|
||||
return;
|
||||
}
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
device.wait_for_timeline(timeline_value_);
|
||||
timeline_value_ = 0;
|
||||
signalled_ = false;
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
26
blender-5.2.0/source/blender/gpu/vulkan/vk_fence.hh
Normal file
26
blender-5.2.0/source/blender/gpu/vulkan/vk_fence.hh
Normal file
@@ -0,0 +1,26 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_state_private.hh"
|
||||
|
||||
#include "vk_common.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class VKFence : public Fence {
|
||||
private:
|
||||
TimelineValue timeline_value_;
|
||||
|
||||
public:
|
||||
void signal() override;
|
||||
void wait() override;
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
792
blender-5.2.0/source/blender/gpu/vulkan/vk_framebuffer.cc
Normal file
792
blender-5.2.0/source/blender/gpu/vulkan/vk_framebuffer.cc
Normal file
@@ -0,0 +1,792 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "vk_framebuffer.hh"
|
||||
#include "vk_backend.hh"
|
||||
#include "vk_context.hh"
|
||||
#include "vk_state_manager.hh"
|
||||
#include "vk_texture.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/**
|
||||
* The default load store action when not using load stores.
|
||||
*/
|
||||
constexpr GPULoadStore default_load_store()
|
||||
{
|
||||
return {GPU_LOADACTION_LOAD, GPU_STOREACTION_STORE, {0.0f, 0.0f, 0.0f, 0.0f}};
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Creation & Deletion
|
||||
* \{ */
|
||||
|
||||
VKFrameBuffer::VKFrameBuffer(const char *name)
|
||||
: FrameBuffer(name),
|
||||
load_stores(GPU_FB_MAX_ATTACHMENT, default_load_store()),
|
||||
attachment_states_(GPU_FB_MAX_ATTACHMENT, GPU_ATTACHMENT_WRITE)
|
||||
{
|
||||
size_set(1, 1);
|
||||
srgb_ = false;
|
||||
enabled_srgb_ = false;
|
||||
}
|
||||
|
||||
VKFrameBuffer::~VKFrameBuffer()
|
||||
{
|
||||
VKContext *context = VKContext::get();
|
||||
if (context && context->active_framebuffer_get() == this) {
|
||||
context->deactivate_framebuffer();
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
void VKFrameBuffer::bind(bool enabled_srgb)
|
||||
{
|
||||
VKContext &context = *VKContext::get();
|
||||
/* Updating attachments can issue pipeline barriers, this should be done outside the render pass.
|
||||
* When done inside a render pass there should be a self-dependency between sub-passes on the
|
||||
* active render pass. As the active render pass isn't aware of the new render pass (and should
|
||||
* not) it is better to deactivate it before updating the attachments. For more information check
|
||||
* `VkSubpassDependency`. */
|
||||
if (context.has_active_framebuffer()) {
|
||||
context.deactivate_framebuffer();
|
||||
}
|
||||
|
||||
context.activate_framebuffer(*this);
|
||||
update_size();
|
||||
viewport_reset();
|
||||
scissor_reset();
|
||||
|
||||
enabled_srgb_ = enabled_srgb;
|
||||
Shader::set_framebuffer_srgb_target(enabled_srgb && srgb_);
|
||||
load_stores.fill(default_load_store());
|
||||
attachment_states_.fill(GPU_ATTACHMENT_WRITE);
|
||||
}
|
||||
|
||||
uint32_t VKFrameBuffer::viewport_size() const
|
||||
{
|
||||
return this->multi_viewport_ ? GPU_MAX_VIEWPORTS : 1;
|
||||
}
|
||||
|
||||
void VKFrameBuffer::vk_viewports_append(Vector<VkViewport> &r_viewports) const
|
||||
{
|
||||
BLI_assert(r_viewports.is_empty());
|
||||
for (int64_t index : IndexRange(this->multi_viewport_ ? GPU_MAX_VIEWPORTS : 1)) {
|
||||
VkViewport viewport;
|
||||
viewport.x = viewport_[index][0];
|
||||
viewport.y = viewport_[index][1];
|
||||
viewport.width = viewport_[index][2];
|
||||
viewport.height = viewport_[index][3];
|
||||
viewport.minDepth = 0.0f;
|
||||
viewport.maxDepth = 1.0f;
|
||||
r_viewports.append(viewport);
|
||||
}
|
||||
}
|
||||
|
||||
void VKFrameBuffer::render_area_update(VkRect2D &render_area) const
|
||||
{
|
||||
if (scissor_test_get()) {
|
||||
int scissor_rect[4];
|
||||
scissor_get(scissor_rect);
|
||||
render_area.offset.x = clamp_i(scissor_rect[0], 0, width_);
|
||||
render_area.offset.y = clamp_i(scissor_rect[1], 0, height_);
|
||||
render_area.extent.width = clamp_i(scissor_rect[2], 1, width_ - scissor_rect[0]);
|
||||
render_area.extent.height = clamp_i(scissor_rect[3], 1, height_ - scissor_rect[1]);
|
||||
}
|
||||
else {
|
||||
render_area.offset.x = 0;
|
||||
render_area.offset.y = 0;
|
||||
render_area.extent.width = width_;
|
||||
render_area.extent.height = height_;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
BLI_assert(render_area.offset.x + render_area.extent.width <=
|
||||
device.physical_device_properties_get().limits.maxFramebufferWidth);
|
||||
BLI_assert(render_area.offset.y + render_area.extent.height <=
|
||||
device.physical_device_properties_get().limits.maxFramebufferHeight);
|
||||
#endif
|
||||
}
|
||||
|
||||
void VKFrameBuffer::vk_render_areas_append(Vector<VkRect2D> &r_render_areas) const
|
||||
{
|
||||
BLI_assert(r_render_areas.is_empty());
|
||||
VkRect2D render_area;
|
||||
render_area_update(render_area);
|
||||
r_render_areas.append_n_times(render_area, this->multi_viewport_ ? GPU_MAX_VIEWPORTS : 1);
|
||||
}
|
||||
|
||||
bool VKFrameBuffer::check(char /*err_out*/[256])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void VKFrameBuffer::build_clear_attachments_depth_stencil(
|
||||
const GPUFrameBufferBits buffers,
|
||||
float clear_depth,
|
||||
uint32_t clear_stencil,
|
||||
render_graph::VKClearAttachmentsNode::CreateInfo &clear_attachments) const
|
||||
{
|
||||
VkImageAspectFlags aspect_mask = (buffers & GPU_DEPTH_BIT ? VK_IMAGE_ASPECT_DEPTH_BIT : 0) |
|
||||
(buffers & GPU_STENCIL_BIT ? VK_IMAGE_ASPECT_STENCIL_BIT : 0);
|
||||
|
||||
VkClearAttachment &clear_attachment =
|
||||
clear_attachments.attachments[clear_attachments.attachment_count++];
|
||||
clear_attachment.aspectMask = aspect_mask;
|
||||
clear_attachment.clearValue.depthStencil.depth = clear_depth;
|
||||
clear_attachment.clearValue.depthStencil.stencil = clear_stencil;
|
||||
clear_attachment.colorAttachment = 0;
|
||||
}
|
||||
|
||||
void VKFrameBuffer::build_clear_attachments_color(
|
||||
Span<double4> clear_colors,
|
||||
const bool multi_clear_colors,
|
||||
render_graph::VKClearAttachmentsNode::CreateInfo &clear_attachments) const
|
||||
{
|
||||
int color_index = 0;
|
||||
for (int color_slot = 0; color_slot < GPU_FB_MAX_COLOR_ATTACHMENT; color_slot++) {
|
||||
const GPUAttachment &attachment = attachments_[GPU_FB_COLOR_ATTACHMENT0 + color_slot];
|
||||
if (attachment.tex == nullptr) {
|
||||
continue;
|
||||
}
|
||||
VkClearAttachment &clear_attachment =
|
||||
clear_attachments.attachments[clear_attachments.attachment_count++];
|
||||
clear_attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
clear_attachment.colorAttachment = color_slot;
|
||||
eGPUDataFormat data_format = to_texture_data_format(GPU_texture_format(attachment.tex));
|
||||
clear_attachment.clearValue.color = to_vk_clear_color_value(data_format,
|
||||
clear_colors[color_index]);
|
||||
|
||||
color_index += multi_clear_colors ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Clear
|
||||
* \{ */
|
||||
|
||||
void VKFrameBuffer::clear(render_graph::VKClearAttachmentsNode::CreateInfo &clear_attachments)
|
||||
{
|
||||
VKContext &context = *VKContext::get();
|
||||
rendering_ensure(context);
|
||||
context.render_graph().add_node(clear_attachments);
|
||||
}
|
||||
|
||||
void VKFrameBuffer::clear(const GPUFrameBufferBits buffers,
|
||||
const double4 clear_color,
|
||||
float clear_depth,
|
||||
uint clear_stencil)
|
||||
{
|
||||
render_graph::VKClearAttachmentsNode::CreateInfo clear_attachments = {};
|
||||
render_area_update(clear_attachments.vk_clear_rect.rect);
|
||||
clear_attachments.vk_clear_rect.baseArrayLayer = 0;
|
||||
clear_attachments.vk_clear_rect.layerCount = 1;
|
||||
|
||||
if (buffers & (GPU_DEPTH_BIT | GPU_STENCIL_BIT)) {
|
||||
VKContext &context = *VKContext::get();
|
||||
GPUWriteMask needed_mask = GPU_WRITE_NONE;
|
||||
if (buffers & GPU_DEPTH_BIT) {
|
||||
needed_mask |= GPU_WRITE_DEPTH;
|
||||
}
|
||||
if (buffers & GPU_STENCIL_BIT) {
|
||||
needed_mask |= GPU_WRITE_STENCIL;
|
||||
}
|
||||
|
||||
/* Clearing depth via #vkCmdClearAttachments requires a render pass with write depth or stencil
|
||||
* enabled. When not enabled, clearing should be done via texture directly. */
|
||||
/* WORKAROUND: Clearing depth attachment when using dynamic rendering are not working on AMD
|
||||
* official drivers.
|
||||
* See #129265 */
|
||||
if ((context.state_manager_get().state.write_mask & needed_mask) == needed_mask &&
|
||||
!GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OFFICIAL))
|
||||
{
|
||||
build_clear_attachments_depth_stencil(
|
||||
buffers, clear_depth, clear_stencil, clear_attachments);
|
||||
}
|
||||
else {
|
||||
const GPUAttachment &attachment = depth_attachment();
|
||||
VKTexture *depth_texture = unwrap(unwrap(attachment.tex));
|
||||
if (depth_texture != nullptr) {
|
||||
depth_texture->clear_depth_stencil(
|
||||
buffers,
|
||||
clear_depth,
|
||||
clear_stencil,
|
||||
attachment.layer == -1 ? std::nullopt : std::make_optional(attachment.layer));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (buffers & GPU_COLOR_BIT) {
|
||||
double4 clear_color_single = double4(clear_color);
|
||||
build_clear_attachments_color({&clear_color_single, 1}, false, clear_attachments);
|
||||
}
|
||||
|
||||
if (clear_attachments.attachment_count) {
|
||||
clear(clear_attachments);
|
||||
}
|
||||
}
|
||||
|
||||
void VKFrameBuffer::clear_multi(Span<double4> clear_cols)
|
||||
{
|
||||
render_graph::VKClearAttachmentsNode::CreateInfo clear_attachments = {};
|
||||
render_area_update(clear_attachments.vk_clear_rect.rect);
|
||||
clear_attachments.vk_clear_rect.baseArrayLayer = 0;
|
||||
clear_attachments.vk_clear_rect.layerCount = 1;
|
||||
|
||||
build_clear_attachments_color(clear_cols, true, clear_attachments);
|
||||
if (clear_attachments.attachment_count) {
|
||||
clear(clear_attachments);
|
||||
}
|
||||
}
|
||||
|
||||
void VKFrameBuffer::clear_attachment(GPUAttachmentType /*type*/, const double4 /*clear_value*/)
|
||||
{
|
||||
/* Clearing of a single attachment was added to implement `clear_multi` in OpenGL. As
|
||||
* `clear_multi` is supported in Vulkan it isn't needed to implement this method.
|
||||
*/
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Load/Store operations
|
||||
* \{ */
|
||||
|
||||
void VKFrameBuffer::attachment_set_loadstore_op(GPUAttachmentType type, GPULoadStore ls)
|
||||
{
|
||||
load_stores[type] = ls;
|
||||
}
|
||||
|
||||
static VkAttachmentLoadOp to_vk_attachment_load_op(GPULoadOp load_op)
|
||||
{
|
||||
switch (load_op) {
|
||||
case GPU_LOADACTION_DONT_CARE:
|
||||
return VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
case GPU_LOADACTION_CLEAR:
|
||||
return VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
case GPU_LOADACTION_LOAD:
|
||||
return VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
}
|
||||
|
||||
static VkAttachmentStoreOp to_vk_attachment_store_op(GPUStoreOp store_op)
|
||||
{
|
||||
switch (store_op) {
|
||||
case GPU_STOREACTION_DONT_CARE:
|
||||
return VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
case GPU_STOREACTION_STORE:
|
||||
return VK_ATTACHMENT_STORE_OP_STORE;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return VK_ATTACHMENT_STORE_OP_STORE;
|
||||
}
|
||||
|
||||
static void set_load_store(VkRenderingAttachmentInfo &r_rendering_attachment,
|
||||
eGPUDataFormat data_format,
|
||||
const GPULoadStore &ls)
|
||||
{
|
||||
r_rendering_attachment.clearValue.color = to_vk_clear_color_value(data_format, ls.clear_value);
|
||||
r_rendering_attachment.loadOp = to_vk_attachment_load_op(ls.load_action);
|
||||
r_rendering_attachment.storeOp = to_vk_attachment_store_op(ls.store_action);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Sub-pass transition
|
||||
* \{ */
|
||||
|
||||
void VKFrameBuffer::subpass_transition_impl(const GPUAttachmentState depth_attachment_state,
|
||||
Span<GPUAttachmentState> color_attachment_states)
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
const bool supports_local_read = device.extensions_get().dynamic_rendering_local_read;
|
||||
|
||||
attachment_states_[GPU_FB_DEPTH_ATTACHMENT] = depth_attachment_state;
|
||||
attachment_states_.as_mutable_span()
|
||||
.slice(GPU_FB_COLOR_ATTACHMENT0, color_attachment_states.size())
|
||||
.copy_from(color_attachment_states);
|
||||
|
||||
if (supports_local_read) {
|
||||
VKContext &context = *VKContext::get();
|
||||
rendering_ensure(context);
|
||||
|
||||
for (int index : IndexRange(color_attachment_states.size())) {
|
||||
if (color_attachment_states[index] == GPU_ATTACHMENT_READ) {
|
||||
VKTexture *texture = unwrap(unwrap(color_tex(index)));
|
||||
if (texture) {
|
||||
context.state_manager_get().image_bind(texture, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
is_rendering_ = false;
|
||||
}
|
||||
else {
|
||||
VKContext &context = *VKContext::get();
|
||||
rendering_end(context);
|
||||
|
||||
for (int index : IndexRange(color_attachment_states.size())) {
|
||||
if (color_attachment_states[index] == GPU_ATTACHMENT_READ) {
|
||||
VKTexture *texture = unwrap(unwrap(color_tex(index)));
|
||||
if (texture) {
|
||||
context.state_manager_get().texture_bind(
|
||||
texture, GPUSamplerState::default_sampler(), index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Read back
|
||||
* \{ */
|
||||
|
||||
void VKFrameBuffer::read(GPUFrameBufferBits plane,
|
||||
eGPUDataFormat format,
|
||||
const int area[4],
|
||||
int /*channel_len*/,
|
||||
int slot,
|
||||
void *r_data)
|
||||
{
|
||||
GPUAttachment *attachment = nullptr;
|
||||
switch (plane) {
|
||||
case GPU_COLOR_BIT:
|
||||
attachment = &attachments_[GPU_FB_COLOR_ATTACHMENT0 + slot];
|
||||
break;
|
||||
|
||||
case GPU_DEPTH_BIT:
|
||||
attachment = attachments_[GPU_FB_DEPTH_ATTACHMENT].tex ?
|
||||
&attachments_[GPU_FB_DEPTH_ATTACHMENT] :
|
||||
&attachments_[GPU_FB_DEPTH_STENCIL_ATTACHMENT];
|
||||
break;
|
||||
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return;
|
||||
}
|
||||
|
||||
VKTexture *texture = unwrap(unwrap(attachment->tex));
|
||||
BLI_assert_msg(texture,
|
||||
"Trying to read back texture from framebuffer, but no texture is available in "
|
||||
"requested slot.");
|
||||
if (texture == nullptr) {
|
||||
return;
|
||||
}
|
||||
const int region[6] = {area[0], area[1], 0, area[0] + area[2], area[1] + area[3], 1};
|
||||
IndexRange layers(max_ii(attachment->layer, 0), 1);
|
||||
texture->read_sub(0, format, region, layers, r_data);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Blit operations
|
||||
* \{ */
|
||||
|
||||
static void blit_aspect(VKContext &context,
|
||||
VKTexture &dst_texture,
|
||||
VKTexture &src_texture,
|
||||
int dst_offset_x,
|
||||
int dst_offset_y,
|
||||
VkImageAspectFlags image_aspect)
|
||||
{
|
||||
/* Prefer texture copy, as some platforms don't support using D32_SFLOAT_S8_UINT to be used as
|
||||
* a blit destination. */
|
||||
if (dst_offset_x == 0 && dst_offset_y == 0 &&
|
||||
dst_texture.device_format_get() == src_texture.device_format_get() &&
|
||||
src_texture.width_get() == dst_texture.width_get() &&
|
||||
src_texture.height_get() == dst_texture.height_get())
|
||||
{
|
||||
src_texture.copy_to(dst_texture, IndexRange(1), image_aspect);
|
||||
return;
|
||||
}
|
||||
|
||||
render_graph::VKBlitImageNode::CreateInfo blit_image = {};
|
||||
|
||||
blit_image.src_image = src_texture.vk_image_handle();
|
||||
blit_image.dst_image = dst_texture.vk_image_handle();
|
||||
blit_image.filter = VK_FILTER_NEAREST;
|
||||
|
||||
VkImageBlit ®ion = blit_image.region;
|
||||
region.srcSubresource.aspectMask = image_aspect;
|
||||
region.srcSubresource.mipLevel = 0;
|
||||
region.srcSubresource.baseArrayLayer = 0;
|
||||
region.srcSubresource.layerCount = 1;
|
||||
region.srcOffsets[0].x = 0;
|
||||
region.srcOffsets[0].y = 0;
|
||||
region.srcOffsets[0].z = 0;
|
||||
region.srcOffsets[1].x = src_texture.width_get();
|
||||
region.srcOffsets[1].y = src_texture.height_get();
|
||||
region.srcOffsets[1].z = 1;
|
||||
|
||||
region.dstSubresource.aspectMask = image_aspect;
|
||||
region.dstSubresource.mipLevel = 0;
|
||||
region.dstSubresource.baseArrayLayer = 0;
|
||||
region.dstSubresource.layerCount = 1;
|
||||
region.dstOffsets[0].x = clamp_i(dst_offset_x, 0, dst_texture.width_get());
|
||||
region.dstOffsets[0].y = clamp_i(dst_offset_y, 0, dst_texture.height_get());
|
||||
region.dstOffsets[0].z = 0;
|
||||
region.dstOffsets[1].x = clamp_i(
|
||||
dst_offset_x + src_texture.width_get(), 0, dst_texture.width_get());
|
||||
region.dstOffsets[1].y = clamp_i(
|
||||
dst_offset_y + src_texture.height_get(), 0, dst_texture.height_get());
|
||||
region.dstOffsets[1].z = 1;
|
||||
|
||||
/* Early exit when no pixels needs to be blitted. This should never occur, but has happened
|
||||
* during development as retina displays are not yet detected and the intermediate back-buffer
|
||||
* would be to small, resulting in cropping the full blit source image. */
|
||||
if (region.dstOffsets[0].x == region.dstOffsets[1].x ||
|
||||
region.dstOffsets[0].y == region.dstOffsets[1].y ||
|
||||
region.dstOffsets[0].z == region.dstOffsets[1].z)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
context.render_graph().add_node(blit_image);
|
||||
}
|
||||
|
||||
void VKFrameBuffer::blit_to(GPUFrameBufferBits planes,
|
||||
int src_slot,
|
||||
FrameBuffer *dst,
|
||||
int dst_slot,
|
||||
int dst_offset_x,
|
||||
int dst_offset_y)
|
||||
{
|
||||
BLI_assert(dst);
|
||||
BLI_assert_msg(ELEM(planes, GPU_COLOR_BIT, GPU_DEPTH_BIT),
|
||||
"VKFrameBuffer::blit_to only supports a single color or depth aspect.");
|
||||
UNUSED_VARS_NDEBUG(planes);
|
||||
|
||||
VKContext &context = *VKContext::get();
|
||||
if (!context.has_active_framebuffer()) {
|
||||
BLI_assert_unreachable();
|
||||
return;
|
||||
}
|
||||
|
||||
VKFrameBuffer &dst_framebuffer = *unwrap(dst);
|
||||
if (planes & GPU_COLOR_BIT) {
|
||||
const GPUAttachment &src_attachment = attachments_[GPU_FB_COLOR_ATTACHMENT0 + src_slot];
|
||||
const GPUAttachment &dst_attachment =
|
||||
dst_framebuffer.attachments_[GPU_FB_COLOR_ATTACHMENT0 + dst_slot];
|
||||
if (src_attachment.tex && dst_attachment.tex) {
|
||||
VKTexture &src_texture = *unwrap(unwrap(src_attachment.tex));
|
||||
VKTexture &dst_texture = *unwrap(unwrap(dst_attachment.tex));
|
||||
blit_aspect(context,
|
||||
dst_texture,
|
||||
src_texture,
|
||||
dst_offset_x,
|
||||
dst_offset_y,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
if (planes & GPU_DEPTH_BIT) {
|
||||
/* Retrieve source texture. */
|
||||
const GPUAttachment &src_attachment = attachments_[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex ?
|
||||
attachments_[GPU_FB_DEPTH_STENCIL_ATTACHMENT] :
|
||||
attachments_[GPU_FB_DEPTH_ATTACHMENT];
|
||||
const GPUAttachment &dst_attachment =
|
||||
dst_framebuffer.attachments_[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex ?
|
||||
dst_framebuffer.attachments_[GPU_FB_DEPTH_STENCIL_ATTACHMENT] :
|
||||
dst_framebuffer.attachments_[GPU_FB_DEPTH_ATTACHMENT];
|
||||
if (src_attachment.tex && dst_attachment.tex) {
|
||||
VKTexture &src_texture = *unwrap(unwrap(src_attachment.tex));
|
||||
VKTexture &dst_texture = *unwrap(unwrap(dst_attachment.tex));
|
||||
blit_aspect(context,
|
||||
dst_texture,
|
||||
src_texture,
|
||||
dst_offset_x,
|
||||
dst_offset_y,
|
||||
VK_IMAGE_ASPECT_DEPTH_BIT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Update attachments
|
||||
* \{ */
|
||||
|
||||
void VKFrameBuffer::update_size()
|
||||
{
|
||||
if (!dirty_attachments_) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < GPU_FB_MAX_ATTACHMENT; i++) {
|
||||
GPUAttachment &attachment = attachments_[i];
|
||||
if (attachment.tex) {
|
||||
int size[3];
|
||||
GPU_texture_get_mipmap_size(attachment.tex, attachment.mip, size);
|
||||
size_set(size[0], size[1]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VKFrameBuffer::update_srgb()
|
||||
{
|
||||
for (int i : IndexRange(GPU_FB_MAX_COLOR_ATTACHMENT)) {
|
||||
VKTexture *texture = unwrap(unwrap(color_tex(i)));
|
||||
if (texture) {
|
||||
srgb_ = (texture->format_flag_get() & GPU_FORMAT_SRGB) != 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int VKFrameBuffer::color_attachments_resource_size() const
|
||||
{
|
||||
int size = 0;
|
||||
for (int color_slot : IndexRange(GPU_FB_MAX_COLOR_ATTACHMENT)) {
|
||||
if (color_tex(color_slot) != nullptr) {
|
||||
size = max_ii(color_slot + 1, size);
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
void VKFrameBuffer::rendering_reset()
|
||||
{
|
||||
is_rendering_ = false;
|
||||
}
|
||||
|
||||
void VKFrameBuffer::rendering_ensure_dynamic_rendering(VKContext &context,
|
||||
const VKExtensions &extensions)
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
const bool supports_local_read = device.extensions_get().dynamic_rendering_local_read;
|
||||
|
||||
depth_attachment_format_ = VK_FORMAT_UNDEFINED;
|
||||
stencil_attachment_format_ = VK_FORMAT_UNDEFINED;
|
||||
|
||||
render_graph::VKResourceAccessInfo access_info;
|
||||
render_graph::VKBeginRenderingNode::CreateInfo begin_rendering(access_info);
|
||||
begin_rendering.node_data.vk_rendering_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
|
||||
begin_rendering.node_data.vk_rendering_info.layerCount = 1;
|
||||
render_area_update(begin_rendering.node_data.vk_rendering_info.renderArea);
|
||||
|
||||
color_attachment_formats_.clear();
|
||||
int32_t max_filled_slot_index = -1;
|
||||
for (int color_attachment_index :
|
||||
IndexRange(GPU_FB_COLOR_ATTACHMENT0, GPU_FB_MAX_COLOR_ATTACHMENT))
|
||||
{
|
||||
const GPUAttachment &attachment = attachments_[color_attachment_index];
|
||||
if (attachment.tex == nullptr) {
|
||||
color_attachment_formats_.append(VK_FORMAT_UNDEFINED);
|
||||
VkRenderingAttachmentInfo &attachment_info =
|
||||
begin_rendering.node_data.color_attachments[begin_rendering.node_data.vk_rendering_info
|
||||
.colorAttachmentCount++];
|
||||
attachment_info = {VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
|
||||
nullptr,
|
||||
VK_NULL_HANDLE,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_RESOLVE_MODE_NONE,
|
||||
VK_NULL_HANDLE,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
VK_ATTACHMENT_STORE_OP_DONT_CARE};
|
||||
continue;
|
||||
}
|
||||
max_filled_slot_index = color_attachment_index - GPU_FB_COLOR_ATTACHMENT0;
|
||||
|
||||
VKTexture &color_texture = *unwrap(unwrap(attachment.tex));
|
||||
BLI_assert_msg(color_texture.usage_get() & GPU_TEXTURE_USAGE_ATTACHMENT,
|
||||
"Texture is used as an attachment, but doesn't have the "
|
||||
"GPU_TEXTURE_USAGE_ATTACHMENT flag.");
|
||||
/* To support `gpu_Layer` we need to set the layerCount to the number of layers it can
|
||||
* access.
|
||||
*/
|
||||
int layer_count = color_texture.layer_count();
|
||||
if (attachment.layer == -1 && layer_count != 1) {
|
||||
begin_rendering.node_data.vk_rendering_info.layerCount = max_ii(
|
||||
begin_rendering.node_data.vk_rendering_info.layerCount, layer_count);
|
||||
}
|
||||
|
||||
VkRenderingAttachmentInfo &attachment_info =
|
||||
begin_rendering.node_data
|
||||
.color_attachments[begin_rendering.node_data.vk_rendering_info.colorAttachmentCount++];
|
||||
attachment_info.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
|
||||
|
||||
VkImageView vk_image_view = VK_NULL_HANDLE;
|
||||
uint32_t layer_base = max_ii(attachment.layer, 0);
|
||||
GPUAttachmentState attachment_state = attachment_states_[color_attachment_index];
|
||||
VkFormat vk_format = to_vk_format(color_texture.device_format_get());
|
||||
if (attachment_state == GPU_ATTACHMENT_WRITE) {
|
||||
VKImageViewInfo image_view_info = {
|
||||
eImageViewUsage::Attachment,
|
||||
IndexRange(layer_base,
|
||||
layer_count != 1 ? max_ii(layer_count - layer_base, 1) : layer_count),
|
||||
IndexRange(attachment.mip, 1),
|
||||
{{'r', 'g', 'b', 'a'}},
|
||||
false,
|
||||
srgb_ && enabled_srgb_,
|
||||
VKImageViewArrayed::DONT_CARE};
|
||||
const VKImageView &image_view = color_texture.image_view_get(image_view_info);
|
||||
vk_image_view = image_view.vk_handle();
|
||||
vk_format = image_view.vk_format();
|
||||
}
|
||||
attachment_info.imageView = vk_image_view;
|
||||
attachment_info.imageLayout = supports_local_read ? VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR :
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
eGPUDataFormat data_format = to_texture_data_format(color_texture.format_get());
|
||||
|
||||
set_load_store(attachment_info, data_format, load_stores[color_attachment_index]);
|
||||
|
||||
access_info.images.append(
|
||||
{color_texture.vk_image_handle(),
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
{uint32_t(attachment.mip),
|
||||
1,
|
||||
layer_base,
|
||||
uint32_t(max_ii(layer_count - layer_base, 1))}});
|
||||
color_attachment_formats_.append(
|
||||
(!extensions.dynamic_rendering_unused_attachments && vk_image_view == VK_NULL_HANDLE) ?
|
||||
VK_FORMAT_UNDEFINED :
|
||||
vk_format);
|
||||
}
|
||||
uint32_t color_attachment_size = uint32_t(max_filled_slot_index + 1);
|
||||
color_attachment_formats_.resize(color_attachment_size);
|
||||
|
||||
begin_rendering.node_data.vk_rendering_info.colorAttachmentCount = color_attachment_size;
|
||||
begin_rendering.node_data.vk_rendering_info.pColorAttachments =
|
||||
begin_rendering.node_data.color_attachments;
|
||||
|
||||
for (int depth_attachment_index : IndexRange(GPU_FB_DEPTH_ATTACHMENT, 2)) {
|
||||
const GPUAttachment &attachment = attachments_[depth_attachment_index];
|
||||
|
||||
if (attachment.tex == nullptr) {
|
||||
continue;
|
||||
}
|
||||
bool is_stencil_attachment = depth_attachment_index == GPU_FB_DEPTH_STENCIL_ATTACHMENT;
|
||||
VKTexture &depth_texture = *unwrap(unwrap(attachment.tex));
|
||||
BLI_assert_msg(depth_texture.usage_get() & GPU_TEXTURE_USAGE_ATTACHMENT,
|
||||
"Texture is used as an attachment, but doesn't have the "
|
||||
"GPU_TEXTURE_USAGE_ATTACHMENT flag.");
|
||||
bool is_depth_stencil_attachment = to_vk_image_aspect_flag_bits(
|
||||
depth_texture.device_format_get()) &
|
||||
VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
VkImageLayout vk_image_layout = is_depth_stencil_attachment ?
|
||||
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL :
|
||||
VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL;
|
||||
GPUAttachmentState attachment_state = attachment_states_[GPU_FB_DEPTH_ATTACHMENT];
|
||||
VkImageView depth_image_view = VK_NULL_HANDLE;
|
||||
if (attachment_state == GPU_ATTACHMENT_WRITE) {
|
||||
VKImageViewInfo image_view_info = {eImageViewUsage::Attachment,
|
||||
IndexRange(max_ii(attachment.layer, 0), 1),
|
||||
IndexRange(attachment.mip, 1),
|
||||
{{'r', 'g', 'b', 'a'}},
|
||||
is_stencil_attachment,
|
||||
false,
|
||||
VKImageViewArrayed::DONT_CARE};
|
||||
depth_image_view = depth_texture.image_view_get(image_view_info).vk_handle();
|
||||
}
|
||||
VkFormat vk_format = (!extensions.dynamic_rendering_unused_attachments &&
|
||||
depth_image_view == VK_NULL_HANDLE) ?
|
||||
VK_FORMAT_UNDEFINED :
|
||||
to_vk_format(depth_texture.device_format_get());
|
||||
|
||||
/* TODO: we should be able to use a single attachment info and only set the
|
||||
* #pDepthAttachment/#pStencilAttachment to the same struct.
|
||||
* But perhaps the stencil clear op might be different. */
|
||||
{
|
||||
VkRenderingAttachmentInfo &attachment_info = begin_rendering.node_data.depth_attachment;
|
||||
attachment_info.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
|
||||
attachment_info.imageView = depth_image_view;
|
||||
attachment_info.imageLayout = vk_image_layout;
|
||||
|
||||
set_load_store(attachment_info, GPU_DATA_FLOAT, load_stores[depth_attachment_index]);
|
||||
depth_attachment_format_ = vk_format;
|
||||
begin_rendering.node_data.vk_rendering_info.pDepthAttachment =
|
||||
&begin_rendering.node_data.depth_attachment;
|
||||
}
|
||||
|
||||
if (is_stencil_attachment) {
|
||||
VkRenderingAttachmentInfo &attachment_info = begin_rendering.node_data.stencil_attachment;
|
||||
attachment_info.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
|
||||
attachment_info.imageView = depth_image_view;
|
||||
attachment_info.imageLayout = vk_image_layout;
|
||||
|
||||
set_load_store(attachment_info, GPU_DATA_UINT, load_stores[depth_attachment_index]);
|
||||
stencil_attachment_format_ = vk_format;
|
||||
begin_rendering.node_data.vk_rendering_info.pStencilAttachment =
|
||||
&begin_rendering.node_data.stencil_attachment;
|
||||
}
|
||||
|
||||
access_info.images.append(
|
||||
{depth_texture.vk_image_handle(),
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
||||
is_depth_stencil_attachment ?
|
||||
static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT |
|
||||
VK_IMAGE_ASPECT_STENCIL_BIT) :
|
||||
static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT),
|
||||
{uint32_t(attachment.mip), 1, uint32_t(max_ii(attachment.layer, 0)), 1}});
|
||||
break;
|
||||
}
|
||||
|
||||
context.render_graph().add_node(begin_rendering);
|
||||
}
|
||||
|
||||
void VKFrameBuffer::rendering_ensure(VKContext &context)
|
||||
{
|
||||
if (!dirty_state_ && is_rendering_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_rendering_) {
|
||||
rendering_end(context);
|
||||
}
|
||||
|
||||
const VKExtensions &extensions = VKBackend::get().device.extensions_get();
|
||||
is_rendering_ = true;
|
||||
rendering_ensure_dynamic_rendering(context, extensions);
|
||||
dirty_attachments_ = false;
|
||||
dirty_state_ = false;
|
||||
use_explicit_load_store_ = false;
|
||||
load_stores.fill(default_load_store());
|
||||
}
|
||||
|
||||
VkFormat VKFrameBuffer::depth_attachment_format_get() const
|
||||
{
|
||||
return depth_attachment_format_;
|
||||
}
|
||||
VkFormat VKFrameBuffer::stencil_attachment_format_get() const
|
||||
{
|
||||
return stencil_attachment_format_;
|
||||
};
|
||||
Span<VkFormat> VKFrameBuffer::color_attachment_formats_get() const
|
||||
{
|
||||
return color_attachment_formats_.as_span();
|
||||
}
|
||||
|
||||
void VKFrameBuffer::rendering_end(VKContext &context)
|
||||
{
|
||||
if (!is_rendering_ && use_explicit_load_store_) {
|
||||
rendering_ensure(context);
|
||||
}
|
||||
|
||||
if (is_rendering_) {
|
||||
render_graph::VKEndRenderingNode::CreateInfo end_rendering = {};
|
||||
context.render_graph().add_node(end_rendering);
|
||||
is_rendering_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
154
blender-5.2.0/source/blender/gpu/vulkan/vk_framebuffer.hh
Normal file
154
blender-5.2.0/source/blender/gpu/vulkan/vk_framebuffer.hh
Normal file
@@ -0,0 +1,154 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "gpu_framebuffer_private.hh"
|
||||
|
||||
#include "render_graph/vk_render_graph.hh"
|
||||
#include "vk_common.hh"
|
||||
#include "vk_image_view.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
class VKContext;
|
||||
|
||||
class VKFrameBuffer : public FrameBuffer {
|
||||
private:
|
||||
/** Is the first attachment an SRGB texture. */
|
||||
bool srgb_;
|
||||
bool enabled_srgb_;
|
||||
bool is_rendering_ = false;
|
||||
|
||||
VkFormat depth_attachment_format_ = VK_FORMAT_UNDEFINED;
|
||||
VkFormat stencil_attachment_format_ = VK_FORMAT_UNDEFINED;
|
||||
Vector<VkFormat> color_attachment_formats_;
|
||||
|
||||
Array<GPULoadStore, GPU_FB_MAX_ATTACHMENT> load_stores;
|
||||
Array<GPUAttachmentState, GPU_FB_MAX_ATTACHMENT> attachment_states_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a conventional frame-buffer to attach texture to.
|
||||
*/
|
||||
VKFrameBuffer(const char *name);
|
||||
virtual ~VKFrameBuffer();
|
||||
|
||||
void bind(bool enabled_srgb) override;
|
||||
bool check(char err_out[256]) override;
|
||||
void clear(GPUFrameBufferBits buffers,
|
||||
const double4 clear_color,
|
||||
float clear_depth,
|
||||
uint clear_stencil) override;
|
||||
void clear_multi(Span<double4> clear_cols) override;
|
||||
void clear_attachment(GPUAttachmentType type, const double4 clear_value) override;
|
||||
|
||||
void attachment_set_loadstore_op(GPUAttachmentType type, GPULoadStore /*ls*/) override;
|
||||
|
||||
protected:
|
||||
void subpass_transition_impl(const GPUAttachmentState depth_attachment_state,
|
||||
Span<GPUAttachmentState> color_attachment_states) override;
|
||||
|
||||
public:
|
||||
void read(GPUFrameBufferBits planes,
|
||||
eGPUDataFormat format,
|
||||
const int area[4],
|
||||
int channel_len,
|
||||
int slot,
|
||||
void *r_data) override;
|
||||
|
||||
void blit_to(GPUFrameBufferBits planes,
|
||||
int src_slot,
|
||||
FrameBuffer *dst,
|
||||
int dst_slot,
|
||||
int dst_offset_x,
|
||||
int dst_offset_y) override;
|
||||
uint32_t viewport_size() const;
|
||||
void vk_viewports_append(Vector<VkViewport> &r_viewports) const;
|
||||
void vk_render_areas_append(Vector<VkRect2D> &r_render_areas) const;
|
||||
void render_area_update(VkRect2D &render_area) const;
|
||||
VkFormat depth_attachment_format_get() const;
|
||||
VkFormat stencil_attachment_format_get() const;
|
||||
/**
|
||||
* \brief Get color attachment formats in used by the framebuffer.
|
||||
*/
|
||||
Span<VkFormat> color_attachment_formats_get() const;
|
||||
|
||||
/**
|
||||
* Ensure that the size of the frame-buffer matches the first attachment resolution.
|
||||
*
|
||||
* Frame buffers attachments are updated when actually used as the image layout has to be
|
||||
* correct. After binding frame-buffers the layout of images can still be modified.
|
||||
*
|
||||
* But for correct behavior of blit/clear operation the size of the frame-buffer should be
|
||||
* set, when activating the frame buffer.
|
||||
*/
|
||||
void update_size();
|
||||
|
||||
void update_srgb();
|
||||
|
||||
/**
|
||||
* Mark this framebuffer to be not being rendered on.
|
||||
*
|
||||
* Between binding a framebuffer and actually using it the state and clear operations can change.
|
||||
* The rendering state is used to find out if the framebuffer begin rendering command should be
|
||||
* recorded
|
||||
*/
|
||||
void rendering_reset();
|
||||
|
||||
/**
|
||||
* Ensure that the framebuffer is ready to be rendered on and that its state is up to date with
|
||||
* the latest changes that can happen between drawing commands inside `VKStateManager`.
|
||||
*/
|
||||
void rendering_ensure(VKContext &context);
|
||||
void rendering_ensure_dynamic_rendering(VKContext &context, const VKExtensions &extensions);
|
||||
|
||||
/**
|
||||
* End the rendering on this framebuffer.
|
||||
* Is being triggered when framebuffer is deactivated or when
|
||||
*/
|
||||
void rendering_end(VKContext &context);
|
||||
|
||||
bool is_rendering() const
|
||||
{
|
||||
return is_rendering_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of color attachments of this frame buffer, including unused color
|
||||
* attachments.
|
||||
*
|
||||
* Frame-buffers can have unused attachments. When higher attachment slots are being used, unused
|
||||
* lower attachment slots will be counted as they are required resources in render-passes.
|
||||
*/
|
||||
int color_attachments_resource_size() const;
|
||||
|
||||
private:
|
||||
/* Clearing attachments */
|
||||
void build_clear_attachments_depth_stencil(
|
||||
GPUFrameBufferBits buffers,
|
||||
float clear_depth,
|
||||
uint32_t clear_stencil,
|
||||
render_graph::VKClearAttachmentsNode::CreateInfo &clear_attachments) const;
|
||||
void build_clear_attachments_color(
|
||||
Span<double4> clear_colors,
|
||||
const bool multi_clear_colors,
|
||||
render_graph::VKClearAttachmentsNode::CreateInfo &clear_attachments) const;
|
||||
void clear(render_graph::VKClearAttachmentsNode::CreateInfo &clear_attachments);
|
||||
};
|
||||
|
||||
static inline VKFrameBuffer *unwrap(gpu::FrameBuffer *framebuffer)
|
||||
{
|
||||
return static_cast<VKFrameBuffer *>(framebuffer);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
32
blender-5.2.0/source/blender/gpu/vulkan/vk_ghost_api.hh
Normal file
32
blender-5.2.0/source/blender/gpu/vulkan/vk_ghost_api.hh
Normal file
@@ -0,0 +1,32 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** This file contains API that the GHOST_ContextVK can invoke directly. */
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/**
|
||||
* Is the driver of the given physical device supported?
|
||||
*
|
||||
* There are some drivers that have known issues and should not be used. This check needs to be
|
||||
* identical between GPU module and GHOST, otherwise GHOST can still select a device which isn't
|
||||
* supported.
|
||||
*
|
||||
* For example on a Linux machine where LLVMPIPE is installed and an not supported NVIDIA driver
|
||||
* Blender would detect a supported configuration using LLVMPIPE, but GHOST could still select the
|
||||
* unsupported NVIDIA driver.
|
||||
*
|
||||
* Returns true when supported, false when not supported.
|
||||
*/
|
||||
bool GPU_vulkan_is_supported_driver(VkPhysicalDevice vk_physical_device);
|
||||
|
||||
const char *to_string(VkResult result);
|
||||
|
||||
} // namespace blender::gpu
|
||||
766
blender-5.2.0/source/blender/gpu/vulkan/vk_graphics_pipeline.hh
Normal file
766
blender-5.2.0/source/blender/gpu/vulkan/vk_graphics_pipeline.hh
Normal file
@@ -0,0 +1,766 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* To create graphics pipelines multiple structs are needed. With graphics pipeline libraries only
|
||||
* parts of these structs doesn't need to be filled. This header file ensures that all code to
|
||||
* create `VkGraphicsPipelineCreateInfo` and related structs are grouped and the different
|
||||
* configurations can be created.
|
||||
*/
|
||||
|
||||
/* TODO: separate in the different configuration and add a main configuration that includes all.
|
||||
* Unsure yet if how to organize this in structs to keep stack allocation small. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vk_common.hh"
|
||||
#include "vk_device.hh"
|
||||
#include "vk_pipeline_pool.hh"
|
||||
#include "vk_vertex_attribute_object.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
struct VKGraphicsPipelineCreateInfoBuilder {
|
||||
VkPipelineRenderingCreateInfo vk_pipeline_rendering_create_info;
|
||||
std::array<VkPipelineShaderStageCreateInfo, 3> vk_pipeline_shader_stage_create_info;
|
||||
VkSpecializationInfo vk_specialization_info;
|
||||
Array<VkSpecializationMapEntry> vk_specialization_map_entries;
|
||||
VkPipelineInputAssemblyStateCreateInfo vk_pipeline_input_assembly_state_create_info;
|
||||
VkPipelineVertexInputStateCreateInfo vk_pipeline_vertex_input_state_create_info;
|
||||
VkPipelineRasterizationStateCreateInfo vk_pipeline_rasterization_state_create_info;
|
||||
VkPipelineRasterizationProvokingVertexStateCreateInfoEXT
|
||||
vk_pipeline_rasterization_provoking_vertex_state_info;
|
||||
VkPipelineRasterizationLineStateCreateInfoEXT vk_pipeline_rasterization_line_state_info;
|
||||
Vector<VkDynamicState, 7> vk_dynamic_states;
|
||||
VkPipelineDynamicStateCreateInfo vk_pipeline_dynamic_state_create_info;
|
||||
VkPipelineViewportStateCreateInfo vk_pipeline_viewport_state_create_info;
|
||||
VkPipelineDepthStencilStateCreateInfo vk_pipeline_depth_stencil_state_create_info;
|
||||
VkPipelineMultisampleStateCreateInfo vk_pipeline_multisample_state_create_info;
|
||||
Vector<VkPipelineColorBlendAttachmentState> vk_pipeline_color_blend_attachment_states;
|
||||
VkPipelineColorBlendStateCreateInfo vk_pipeline_color_blend_state_create_info;
|
||||
VkGraphicsPipelineCreateInfo vk_graphics_pipeline_create_info;
|
||||
VkGraphicsPipelineLibraryCreateInfoEXT vk_graphics_pipeline_library_create_info;
|
||||
|
||||
/* Only used when VK_EXT_vertex_input_dynamic_state isn't supported. */
|
||||
Vector<VkVertexInputBindingDescription> vk_vertex_input_binding_descriptions;
|
||||
Vector<VkVertexInputAttributeDescription> vk_vertex_input_attribute_descriptions;
|
||||
|
||||
/**
|
||||
* When VK_EXT_dynamic_rendering_local_read is used and the shader declares input attachments,
|
||||
* the shaders library pipeline must declare enough color attachments and provide
|
||||
* VkRenderingInputAttachmentIndexInfo to satisfy the input attachment index constraint.
|
||||
*/
|
||||
VkRenderingInputAttachmentIndexInfo vk_rendering_input_attachment_index_info_;
|
||||
Vector<VkFormat> dummy_color_attachment_formats_;
|
||||
|
||||
/**
|
||||
* Initialize graphics pipeline create info and related structs for a full pipeline build.
|
||||
*/
|
||||
void build_full(VKDevice &device,
|
||||
const VKGraphicsInfo &graphics_info,
|
||||
VkPipeline vk_pipeline_base)
|
||||
{
|
||||
const VKExtensions &extensions = device.extensions_get();
|
||||
build_graphics_pipeline(extensions, graphics_info, vk_pipeline_base);
|
||||
|
||||
build_input_assembly_state(graphics_info.vertex_in);
|
||||
if (!extensions.vertex_input_dynamic_state) {
|
||||
build_vertex_input_state(device, graphics_info.vertex_in);
|
||||
}
|
||||
|
||||
build_shader_stages(graphics_info.shaders);
|
||||
const bool do_specialization_constants =
|
||||
!graphics_info.shaders.specialization_constants.is_empty();
|
||||
if (do_specialization_constants) {
|
||||
build_specialization_constants(graphics_info.shaders);
|
||||
}
|
||||
build_dynamic_state(graphics_info.shaders, extensions);
|
||||
build_multisample_state();
|
||||
build_viewport_state(graphics_info.shaders);
|
||||
build_rasterization_state(graphics_info.shaders, extensions);
|
||||
build_depth_stencil_state(graphics_info.shaders);
|
||||
|
||||
build_color_blend_attachment_states(graphics_info.fragment_out);
|
||||
build_color_blend_state(graphics_info.fragment_out, extensions);
|
||||
build_dynamic_rendering(graphics_info.fragment_out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize graphics pipeline create info and related structs for a vertex input library build.
|
||||
*/
|
||||
void build_vertex_input_lib(VKDevice &device,
|
||||
const VKGraphicsInfo::VertexIn &vertex_input_info,
|
||||
VkPipeline vk_pipeline_base)
|
||||
{
|
||||
const VKExtensions &extensions = device.extensions_get();
|
||||
build_graphics_pipeline_library(VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT);
|
||||
build_graphics_pipeline_vertex_input_lib(vk_pipeline_base, extensions);
|
||||
build_input_assembly_state(vertex_input_info);
|
||||
build_dynamic_state_for_vertex_input(extensions);
|
||||
|
||||
if (!extensions.vertex_input_dynamic_state) {
|
||||
build_vertex_input_state(device, vertex_input_info);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize graphics pipeline create info and related structs for a shaders library build.
|
||||
*/
|
||||
void build_shaders_lib(const VKGraphicsInfo::Shaders &shaders_info,
|
||||
const VKExtensions &extensions,
|
||||
VkPipeline vk_pipeline_base)
|
||||
{
|
||||
build_graphics_pipeline_library(
|
||||
VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT |
|
||||
VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT);
|
||||
build_graphics_pipeline_shaders_lib(shaders_info, vk_pipeline_base);
|
||||
|
||||
build_shader_stages(shaders_info);
|
||||
const bool do_specialization_constants = !shaders_info.specialization_constants.is_empty();
|
||||
if (do_specialization_constants) {
|
||||
build_specialization_constants(shaders_info);
|
||||
}
|
||||
build_dynamic_state(shaders_info, extensions);
|
||||
build_multisample_state();
|
||||
build_viewport_state(shaders_info);
|
||||
build_rasterization_state(shaders_info, extensions);
|
||||
build_depth_stencil_state(shaders_info);
|
||||
build_dynamic_rendering_shaders_lib(extensions, shaders_info.max_input_attachment_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize graphics pipeline create info and related structs for a fragment output library
|
||||
* build.
|
||||
*/
|
||||
void build_fragment_output_lib(const VKGraphicsInfo::FragmentOut &fragment_output_info,
|
||||
const VKExtensions &extensions,
|
||||
VkPipeline vk_pipeline_base)
|
||||
{
|
||||
build_graphics_pipeline_library(
|
||||
VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT);
|
||||
build_graphics_pipeline_fragment_output_lib(vk_pipeline_base);
|
||||
build_multisample_state();
|
||||
build_color_blend_attachment_states(fragment_output_info);
|
||||
build_color_blend_state(fragment_output_info, extensions);
|
||||
build_dynamic_rendering(fragment_output_info);
|
||||
}
|
||||
|
||||
private:
|
||||
void build_graphics_pipeline(const VKExtensions &extensions,
|
||||
const VKGraphicsInfo &graphics_info,
|
||||
VkPipeline vk_pipeline_base)
|
||||
{
|
||||
vk_graphics_pipeline_create_info = {};
|
||||
vk_graphics_pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
vk_graphics_pipeline_create_info.pNext = &vk_pipeline_rendering_create_info;
|
||||
vk_graphics_pipeline_create_info.stageCount = graphics_info.shaders.vk_geometry_module ==
|
||||
VK_NULL_HANDLE ?
|
||||
2 :
|
||||
3;
|
||||
vk_graphics_pipeline_create_info.pStages = vk_pipeline_shader_stage_create_info.data();
|
||||
vk_graphics_pipeline_create_info.pInputAssemblyState =
|
||||
&vk_pipeline_input_assembly_state_create_info;
|
||||
vk_graphics_pipeline_create_info.pVertexInputState =
|
||||
extensions.vertex_input_dynamic_state ? nullptr :
|
||||
&vk_pipeline_vertex_input_state_create_info;
|
||||
vk_graphics_pipeline_create_info.pRasterizationState =
|
||||
&vk_pipeline_rasterization_state_create_info;
|
||||
vk_graphics_pipeline_create_info.pDepthStencilState =
|
||||
&vk_pipeline_depth_stencil_state_create_info;
|
||||
vk_graphics_pipeline_create_info.pDynamicState = &vk_pipeline_dynamic_state_create_info;
|
||||
vk_graphics_pipeline_create_info.pViewportState = &vk_pipeline_viewport_state_create_info;
|
||||
vk_graphics_pipeline_create_info.pMultisampleState =
|
||||
&vk_pipeline_multisample_state_create_info;
|
||||
vk_graphics_pipeline_create_info.pColorBlendState = &vk_pipeline_color_blend_state_create_info;
|
||||
vk_graphics_pipeline_create_info.layout = graphics_info.shaders.vk_pipeline_layout;
|
||||
vk_graphics_pipeline_create_info.basePipelineHandle = vk_pipeline_base;
|
||||
}
|
||||
|
||||
void build_graphics_pipeline_vertex_input_lib(VkPipeline vk_pipeline_base,
|
||||
const VKExtensions &extensions)
|
||||
{
|
||||
vk_graphics_pipeline_create_info = {
|
||||
VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
&vk_graphics_pipeline_library_create_info,
|
||||
VK_PIPELINE_CREATE_LIBRARY_BIT_KHR |
|
||||
VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT,
|
||||
0,
|
||||
nullptr,
|
||||
extensions.vertex_input_dynamic_state ? nullptr :
|
||||
&vk_pipeline_vertex_input_state_create_info,
|
||||
&vk_pipeline_input_assembly_state_create_info,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&vk_pipeline_dynamic_state_create_info,
|
||||
VK_NULL_HANDLE,
|
||||
VK_NULL_HANDLE,
|
||||
0,
|
||||
vk_pipeline_base,
|
||||
0};
|
||||
}
|
||||
|
||||
void build_graphics_pipeline_shaders_lib(const VKGraphicsInfo::Shaders &shaders_info,
|
||||
VkPipeline vk_pipeline_base)
|
||||
{
|
||||
vk_graphics_pipeline_create_info = {
|
||||
VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
&vk_graphics_pipeline_library_create_info,
|
||||
VK_PIPELINE_CREATE_LIBRARY_BIT_KHR |
|
||||
VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT,
|
||||
shaders_info.vk_geometry_module == VK_NULL_HANDLE ? 2u : 3u,
|
||||
vk_pipeline_shader_stage_create_info.data(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&vk_pipeline_viewport_state_create_info,
|
||||
&vk_pipeline_rasterization_state_create_info,
|
||||
&vk_pipeline_multisample_state_create_info,
|
||||
&vk_pipeline_depth_stencil_state_create_info,
|
||||
nullptr,
|
||||
&vk_pipeline_dynamic_state_create_info,
|
||||
shaders_info.vk_pipeline_layout,
|
||||
VK_NULL_HANDLE,
|
||||
0,
|
||||
vk_pipeline_base,
|
||||
0};
|
||||
vk_graphics_pipeline_library_create_info.pNext = &vk_pipeline_rendering_create_info;
|
||||
}
|
||||
|
||||
void build_graphics_pipeline_fragment_output_lib(VkPipeline vk_pipeline_base)
|
||||
{
|
||||
vk_graphics_pipeline_create_info = {
|
||||
VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
&vk_graphics_pipeline_library_create_info,
|
||||
VK_PIPELINE_CREATE_LIBRARY_BIT_KHR |
|
||||
VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&vk_pipeline_multisample_state_create_info,
|
||||
nullptr,
|
||||
&vk_pipeline_color_blend_state_create_info,
|
||||
nullptr,
|
||||
VK_NULL_HANDLE,
|
||||
VK_NULL_HANDLE,
|
||||
0,
|
||||
vk_pipeline_base,
|
||||
0};
|
||||
vk_graphics_pipeline_library_create_info.pNext = &vk_pipeline_rendering_create_info;
|
||||
}
|
||||
|
||||
void build_graphics_pipeline_library(VkGraphicsPipelineLibraryFlagsEXT flags)
|
||||
{
|
||||
vk_graphics_pipeline_library_create_info = {
|
||||
VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT, nullptr, flags};
|
||||
}
|
||||
|
||||
void build_shader_stages(const VKGraphicsInfo::Shaders &shaders_info)
|
||||
{
|
||||
vk_pipeline_shader_stage_create_info[0] = {VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
VK_SHADER_STAGE_VERTEX_BIT,
|
||||
shaders_info.vk_vertex_module,
|
||||
"main",
|
||||
nullptr};
|
||||
vk_pipeline_shader_stage_create_info[1] = {VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
shaders_info.vk_fragment_module,
|
||||
"main",
|
||||
nullptr};
|
||||
vk_pipeline_shader_stage_create_info[2] = {VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
VK_SHADER_STAGE_GEOMETRY_BIT,
|
||||
shaders_info.vk_geometry_module,
|
||||
"main",
|
||||
nullptr};
|
||||
}
|
||||
|
||||
void build_specialization_constants(const VKGraphicsInfo::Shaders &shaders_info)
|
||||
{
|
||||
vk_specialization_map_entries.reinitialize(shaders_info.specialization_constants.size());
|
||||
for (int index : IndexRange(3)) {
|
||||
vk_pipeline_shader_stage_create_info[index].pSpecializationInfo = &vk_specialization_info;
|
||||
}
|
||||
for (uint32_t index : IndexRange(shaders_info.specialization_constants.size())) {
|
||||
vk_specialization_map_entries[index] = {
|
||||
index, uint32_t(index * sizeof(uint32_t)), sizeof(uint32_t)};
|
||||
}
|
||||
vk_specialization_info = {uint32_t(vk_specialization_map_entries.size()),
|
||||
vk_specialization_map_entries.data(),
|
||||
shaders_info.specialization_constants.size() * sizeof(uint32_t),
|
||||
shaders_info.specialization_constants.data()};
|
||||
}
|
||||
|
||||
void build_multisample_state()
|
||||
{
|
||||
vk_pipeline_multisample_state_create_info = {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
VK_SAMPLE_COUNT_1_BIT,
|
||||
VK_FALSE,
|
||||
1.0f,
|
||||
nullptr,
|
||||
VK_FALSE,
|
||||
VK_FALSE};
|
||||
}
|
||||
|
||||
void build_viewport_state(const VKGraphicsInfo::Shaders &shaders_info)
|
||||
{
|
||||
vk_pipeline_viewport_state_create_info = {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
shaders_info.viewport_count,
|
||||
nullptr,
|
||||
shaders_info.viewport_count,
|
||||
nullptr};
|
||||
}
|
||||
|
||||
void build_input_assembly_state(const VKGraphicsInfo::VertexIn &vertex_input_info)
|
||||
{
|
||||
vk_pipeline_input_assembly_state_create_info = {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO};
|
||||
vk_pipeline_input_assembly_state_create_info.topology = vertex_input_info.vk_topology;
|
||||
vk_pipeline_input_assembly_state_create_info.primitiveRestartEnable =
|
||||
ELEM(vertex_input_info.vk_topology,
|
||||
VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
|
||||
VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
|
||||
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
||||
VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY) ?
|
||||
VK_FALSE :
|
||||
VK_TRUE;
|
||||
}
|
||||
|
||||
void build_vertex_input_state(VKDevice &device,
|
||||
const VKGraphicsInfo::VertexIn &vertex_input_info)
|
||||
{
|
||||
BLI_assert_msg(!device.extensions_get().vertex_input_dynamic_state,
|
||||
"No need to set vertex input state as dynamic state is supported.");
|
||||
const VKVertexInputDescription &description = device.vertex_input_descriptions.get(
|
||||
vertex_input_info.vertex_input_key);
|
||||
vk_pipeline_vertex_input_state_create_info = {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO};
|
||||
vk_vertex_input_attribute_descriptions.reserve(description.attributes.size());
|
||||
vk_vertex_input_binding_descriptions.reserve(description.bindings.size());
|
||||
for (const VkVertexInputAttributeDescription2EXT &attr : description.attributes) {
|
||||
vk_vertex_input_attribute_descriptions.append(
|
||||
{attr.location, attr.binding, attr.format, attr.offset});
|
||||
}
|
||||
for (const VkVertexInputBindingDescription2EXT &bind : description.bindings) {
|
||||
vk_vertex_input_binding_descriptions.append({bind.binding, bind.stride, bind.inputRate});
|
||||
}
|
||||
|
||||
vk_pipeline_vertex_input_state_create_info.pVertexAttributeDescriptions =
|
||||
vk_vertex_input_attribute_descriptions.data();
|
||||
vk_pipeline_vertex_input_state_create_info.vertexAttributeDescriptionCount =
|
||||
vk_vertex_input_attribute_descriptions.size();
|
||||
vk_pipeline_vertex_input_state_create_info.pVertexBindingDescriptions =
|
||||
vk_vertex_input_binding_descriptions.data();
|
||||
vk_pipeline_vertex_input_state_create_info.vertexBindingDescriptionCount =
|
||||
vk_vertex_input_binding_descriptions.size();
|
||||
}
|
||||
|
||||
void build_rasterization_state(const VKGraphicsInfo::Shaders &shaders_info,
|
||||
const VKExtensions &extensions)
|
||||
{
|
||||
vk_pipeline_rasterization_provoking_vertex_state_info = {};
|
||||
vk_pipeline_rasterization_provoking_vertex_state_info.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT;
|
||||
vk_pipeline_rasterization_provoking_vertex_state_info.provokingVertexMode =
|
||||
VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT;
|
||||
vk_pipeline_rasterization_provoking_vertex_state_info.provokingVertexMode =
|
||||
shaders_info.state.provoking_vert == GPU_VERTEX_LAST ?
|
||||
VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT :
|
||||
VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT;
|
||||
|
||||
vk_pipeline_rasterization_state_create_info = {};
|
||||
vk_pipeline_rasterization_state_create_info.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||
vk_pipeline_rasterization_state_create_info.lineWidth = 1.0f;
|
||||
vk_pipeline_rasterization_state_create_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||
vk_pipeline_rasterization_state_create_info.pNext =
|
||||
&vk_pipeline_rasterization_provoking_vertex_state_info;
|
||||
|
||||
vk_pipeline_rasterization_state_create_info.cullMode = to_vk_cull_mode_flags(
|
||||
static_cast<GPUFaceCullTest>(shaders_info.state.culling_test));
|
||||
if (!extensions.extended_dynamic_state && !shaders_info.state.invert_facing) {
|
||||
vk_pipeline_rasterization_state_create_info.frontFace = VK_FRONT_FACE_CLOCKWISE;
|
||||
}
|
||||
|
||||
if (extensions.line_rasterization) {
|
||||
/* Request use of Bresenham algorithm if supported. */
|
||||
vk_pipeline_rasterization_line_state_info = {};
|
||||
vk_pipeline_rasterization_line_state_info.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT;
|
||||
vk_pipeline_rasterization_line_state_info.lineRasterizationMode =
|
||||
VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT;
|
||||
vk_pipeline_rasterization_line_state_info.stippledLineEnable = VK_FALSE;
|
||||
vk_pipeline_rasterization_line_state_info.lineStippleFactor = 0u;
|
||||
vk_pipeline_rasterization_line_state_info.lineStipplePattern = 0u;
|
||||
vk_pipeline_rasterization_line_state_info.pNext =
|
||||
vk_pipeline_rasterization_state_create_info.pNext;
|
||||
vk_pipeline_rasterization_state_create_info.pNext =
|
||||
&vk_pipeline_rasterization_line_state_info;
|
||||
}
|
||||
}
|
||||
|
||||
void build_dynamic_state(const VKGraphicsInfo::Shaders &shaders_info,
|
||||
const VKExtensions &extensions)
|
||||
{
|
||||
vk_dynamic_states = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
|
||||
const bool is_line_topology = ELEM(shaders_info.vk_topology,
|
||||
VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
|
||||
VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY,
|
||||
VK_PRIMITIVE_TOPOLOGY_LINE_STRIP);
|
||||
if (is_line_topology) {
|
||||
vk_dynamic_states.append(VK_DYNAMIC_STATE_LINE_WIDTH);
|
||||
}
|
||||
if (shaders_info.has_stencil && shaders_info.state.stencil_test != GPU_STENCIL_NONE) {
|
||||
vk_dynamic_states.append(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK);
|
||||
vk_dynamic_states.append(VK_DYNAMIC_STATE_STENCIL_REFERENCE);
|
||||
vk_dynamic_states.append(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK);
|
||||
}
|
||||
if (extensions.extended_dynamic_state) {
|
||||
vk_dynamic_states.append(VK_DYNAMIC_STATE_FRONT_FACE);
|
||||
}
|
||||
if (extensions.vertex_input_dynamic_state) {
|
||||
vk_dynamic_states.append(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
|
||||
}
|
||||
vk_pipeline_dynamic_state_create_info = {VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
uint32_t(vk_dynamic_states.size()),
|
||||
vk_dynamic_states.data()};
|
||||
}
|
||||
|
||||
void build_dynamic_state_for_vertex_input(const VKExtensions &extensions)
|
||||
{
|
||||
if (extensions.vertex_input_dynamic_state) {
|
||||
vk_dynamic_states = {VK_DYNAMIC_STATE_VERTEX_INPUT_EXT};
|
||||
}
|
||||
vk_pipeline_dynamic_state_create_info = {VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
uint32_t(vk_dynamic_states.size()),
|
||||
vk_dynamic_states.data()};
|
||||
}
|
||||
|
||||
void build_depth_stencil_state(const VKGraphicsInfo::Shaders &shaders_info)
|
||||
{
|
||||
vk_pipeline_depth_stencil_state_create_info = {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO};
|
||||
if (shaders_info.has_depth) {
|
||||
vk_graphics_pipeline_create_info.pDepthStencilState =
|
||||
&vk_pipeline_depth_stencil_state_create_info;
|
||||
vk_pipeline_depth_stencil_state_create_info.depthWriteEnable =
|
||||
(shaders_info.state.write_mask & GPU_WRITE_DEPTH) ? VK_TRUE : VK_FALSE;
|
||||
|
||||
vk_pipeline_depth_stencil_state_create_info.depthTestEnable = VK_TRUE;
|
||||
switch (shaders_info.state.depth_test) {
|
||||
case GPU_DEPTH_LESS:
|
||||
vk_pipeline_depth_stencil_state_create_info.depthCompareOp = VK_COMPARE_OP_LESS;
|
||||
break;
|
||||
case GPU_DEPTH_LESS_EQUAL:
|
||||
vk_pipeline_depth_stencil_state_create_info.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
|
||||
break;
|
||||
case GPU_DEPTH_EQUAL:
|
||||
vk_pipeline_depth_stencil_state_create_info.depthCompareOp = VK_COMPARE_OP_EQUAL;
|
||||
break;
|
||||
case GPU_DEPTH_GREATER:
|
||||
vk_pipeline_depth_stencil_state_create_info.depthCompareOp = VK_COMPARE_OP_GREATER;
|
||||
break;
|
||||
case GPU_DEPTH_GREATER_EQUAL:
|
||||
vk_pipeline_depth_stencil_state_create_info.depthCompareOp =
|
||||
VK_COMPARE_OP_GREATER_OR_EQUAL;
|
||||
break;
|
||||
case GPU_DEPTH_ALWAYS:
|
||||
vk_pipeline_depth_stencil_state_create_info.depthCompareOp = VK_COMPARE_OP_ALWAYS;
|
||||
break;
|
||||
case GPU_DEPTH_NONE:
|
||||
vk_pipeline_depth_stencil_state_create_info.depthTestEnable = VK_FALSE;
|
||||
vk_pipeline_depth_stencil_state_create_info.depthCompareOp = VK_COMPARE_OP_NEVER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (shaders_info.has_stencil) {
|
||||
vk_graphics_pipeline_create_info.pDepthStencilState =
|
||||
&vk_pipeline_depth_stencil_state_create_info;
|
||||
|
||||
switch (shaders_info.state.stencil_test) {
|
||||
case GPU_STENCIL_NEQUAL:
|
||||
vk_pipeline_depth_stencil_state_create_info.stencilTestEnable = VK_TRUE;
|
||||
vk_pipeline_depth_stencil_state_create_info.front.compareOp = VK_COMPARE_OP_NOT_EQUAL;
|
||||
break;
|
||||
case GPU_STENCIL_EQUAL:
|
||||
vk_pipeline_depth_stencil_state_create_info.stencilTestEnable = VK_TRUE;
|
||||
vk_pipeline_depth_stencil_state_create_info.front.compareOp = VK_COMPARE_OP_EQUAL;
|
||||
break;
|
||||
case GPU_STENCIL_ALWAYS:
|
||||
vk_pipeline_depth_stencil_state_create_info.stencilTestEnable = VK_TRUE;
|
||||
vk_pipeline_depth_stencil_state_create_info.front.compareOp = VK_COMPARE_OP_ALWAYS;
|
||||
break;
|
||||
case GPU_STENCIL_NONE:
|
||||
vk_pipeline_depth_stencil_state_create_info.stencilTestEnable = VK_FALSE;
|
||||
vk_pipeline_depth_stencil_state_create_info.front.compareOp = VK_COMPARE_OP_ALWAYS;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (shaders_info.state.stencil_op) {
|
||||
case GPU_STENCIL_OP_REPLACE:
|
||||
vk_pipeline_depth_stencil_state_create_info.front.failOp = VK_STENCIL_OP_KEEP;
|
||||
vk_pipeline_depth_stencil_state_create_info.front.passOp = VK_STENCIL_OP_REPLACE;
|
||||
vk_pipeline_depth_stencil_state_create_info.front.depthFailOp = VK_STENCIL_OP_KEEP;
|
||||
vk_pipeline_depth_stencil_state_create_info.back =
|
||||
vk_pipeline_depth_stencil_state_create_info.front;
|
||||
break;
|
||||
|
||||
case GPU_STENCIL_OP_COUNT_DEPTH_PASS:
|
||||
vk_pipeline_depth_stencil_state_create_info.front.failOp = VK_STENCIL_OP_KEEP;
|
||||
vk_pipeline_depth_stencil_state_create_info.front.passOp =
|
||||
VK_STENCIL_OP_DECREMENT_AND_WRAP;
|
||||
vk_pipeline_depth_stencil_state_create_info.front.depthFailOp = VK_STENCIL_OP_KEEP;
|
||||
vk_pipeline_depth_stencil_state_create_info.back =
|
||||
vk_pipeline_depth_stencil_state_create_info.front;
|
||||
vk_pipeline_depth_stencil_state_create_info.back.passOp =
|
||||
VK_STENCIL_OP_INCREMENT_AND_WRAP;
|
||||
break;
|
||||
|
||||
case GPU_STENCIL_OP_COUNT_DEPTH_FAIL:
|
||||
vk_pipeline_depth_stencil_state_create_info.front.failOp = VK_STENCIL_OP_KEEP;
|
||||
vk_pipeline_depth_stencil_state_create_info.front.passOp = VK_STENCIL_OP_KEEP;
|
||||
vk_pipeline_depth_stencil_state_create_info.front.depthFailOp =
|
||||
VK_STENCIL_OP_INCREMENT_AND_WRAP;
|
||||
vk_pipeline_depth_stencil_state_create_info.back =
|
||||
vk_pipeline_depth_stencil_state_create_info.front;
|
||||
vk_pipeline_depth_stencil_state_create_info.back.depthFailOp =
|
||||
VK_STENCIL_OP_DECREMENT_AND_WRAP;
|
||||
break;
|
||||
|
||||
case GPU_STENCIL_OP_NONE:
|
||||
default:
|
||||
vk_pipeline_depth_stencil_state_create_info.front.failOp = VK_STENCIL_OP_KEEP;
|
||||
vk_pipeline_depth_stencil_state_create_info.front.passOp = VK_STENCIL_OP_KEEP;
|
||||
vk_pipeline_depth_stencil_state_create_info.front.depthFailOp = VK_STENCIL_OP_KEEP;
|
||||
vk_pipeline_depth_stencil_state_create_info.back =
|
||||
vk_pipeline_depth_stencil_state_create_info.front;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void build_dynamic_rendering(const VKGraphicsInfo::FragmentOut &fragment_output_info)
|
||||
{
|
||||
vk_pipeline_rendering_create_info = {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
uint32_t(fragment_output_info.color_attachment_formats.size()),
|
||||
fragment_output_info.color_attachment_formats.data(),
|
||||
fragment_output_info.depth_attachment_format,
|
||||
fragment_output_info.stencil_attachment_format};
|
||||
}
|
||||
|
||||
/* Shaders lib only requires the view-mask to be set. When dynamic rendering local read is
|
||||
* used and the shader declares input attachments, we must set colorAttachmentCount to cover
|
||||
* the input attachment indices and provide VkRenderingInputAttachmentIndexInfo to satisfy the
|
||||
* VUID-VkGraphicsPipelineCreateInfo-renderPass-09652 constraint. */
|
||||
void build_dynamic_rendering_shaders_lib(const VKExtensions &extensions,
|
||||
uint32_t max_input_attachment_index)
|
||||
{
|
||||
vk_pipeline_rendering_create_info = {VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO};
|
||||
if (extensions.dynamic_rendering_local_read && max_input_attachment_index > 0) {
|
||||
vk_pipeline_rendering_create_info.colorAttachmentCount = max_input_attachment_index + 1;
|
||||
dummy_color_attachment_formats_.resize(max_input_attachment_index + 1, VK_FORMAT_UNDEFINED);
|
||||
vk_pipeline_rendering_create_info.pColorAttachmentFormats =
|
||||
dummy_color_attachment_formats_.data();
|
||||
|
||||
vk_rendering_input_attachment_index_info_ = {};
|
||||
vk_rendering_input_attachment_index_info_.sType =
|
||||
VK_STRUCTURE_TYPE_RENDERING_INPUT_ATTACHMENT_INDEX_INFO;
|
||||
vk_rendering_input_attachment_index_info_.colorAttachmentCount = max_input_attachment_index +
|
||||
1;
|
||||
vk_pipeline_rendering_create_info.pNext = &vk_rendering_input_attachment_index_info_;
|
||||
}
|
||||
}
|
||||
|
||||
void build_color_blend_attachment_states(const VKGraphicsInfo::FragmentOut &fragment_output_info)
|
||||
{
|
||||
VkPipelineColorBlendAttachmentState attachment_state = {VK_TRUE,
|
||||
VK_BLEND_FACTOR_DST_ALPHA,
|
||||
VK_BLEND_FACTOR_ONE,
|
||||
VK_BLEND_OP_ADD,
|
||||
VK_BLEND_FACTOR_ZERO,
|
||||
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
|
||||
VK_BLEND_OP_ADD,
|
||||
0};
|
||||
|
||||
switch (fragment_output_info.state.blend) {
|
||||
default:
|
||||
case GPU_BLEND_ALPHA:
|
||||
attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
|
||||
case GPU_BLEND_ALPHA_PREMULT:
|
||||
attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
|
||||
case GPU_BLEND_ADDITIVE:
|
||||
attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
break;
|
||||
|
||||
/* Factors are not use in min or max mode, but avoid uninitialized values. */;
|
||||
case GPU_BLEND_MIN:
|
||||
case GPU_BLEND_MAX:
|
||||
case GPU_BLEND_SUBTRACT:
|
||||
case GPU_BLEND_ADDITIVE_PREMULT:
|
||||
attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
break;
|
||||
|
||||
case GPU_BLEND_MULTIPLY:
|
||||
attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_DST_COLOR;
|
||||
attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_DST_ALPHA;
|
||||
attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
break;
|
||||
|
||||
case GPU_BLEND_INVERT:
|
||||
attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
|
||||
attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
break;
|
||||
|
||||
case GPU_BLEND_OIT:
|
||||
attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
|
||||
case GPU_BLEND_BACKGROUND:
|
||||
attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
|
||||
attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
break;
|
||||
|
||||
case GPU_BLEND_ALPHA_UNDER_PREMUL:
|
||||
attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
|
||||
attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
|
||||
attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
break;
|
||||
|
||||
case GPU_BLEND_CUSTOM:
|
||||
attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_SRC1_COLOR;
|
||||
attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_SRC1_ALPHA;
|
||||
break;
|
||||
|
||||
case GPU_BLEND_OVERLAY_MASK_FROM_ALPHA:
|
||||
attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
|
||||
case GPU_BLEND_TRANSPARENCY:
|
||||
attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fragment_output_info.state.blend == GPU_BLEND_MIN) {
|
||||
attachment_state.alphaBlendOp = VK_BLEND_OP_MIN;
|
||||
attachment_state.colorBlendOp = VK_BLEND_OP_MIN;
|
||||
}
|
||||
else if (fragment_output_info.state.blend == GPU_BLEND_MAX) {
|
||||
attachment_state.alphaBlendOp = VK_BLEND_OP_MAX;
|
||||
attachment_state.colorBlendOp = VK_BLEND_OP_MAX;
|
||||
}
|
||||
else if (fragment_output_info.state.blend == GPU_BLEND_SUBTRACT) {
|
||||
attachment_state.alphaBlendOp = VK_BLEND_OP_REVERSE_SUBTRACT;
|
||||
attachment_state.colorBlendOp = VK_BLEND_OP_REVERSE_SUBTRACT;
|
||||
}
|
||||
else {
|
||||
attachment_state.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
attachment_state.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
}
|
||||
|
||||
if (fragment_output_info.state.blend != GPU_BLEND_NONE) {
|
||||
attachment_state.blendEnable = VK_TRUE;
|
||||
}
|
||||
else {
|
||||
attachment_state.blendEnable = VK_FALSE;
|
||||
}
|
||||
|
||||
/* Adjust the template with the color components in the write mask. */
|
||||
if ((fragment_output_info.state.write_mask & GPU_WRITE_RED) != 0) {
|
||||
attachment_state.colorWriteMask |= VK_COLOR_COMPONENT_R_BIT;
|
||||
}
|
||||
if ((fragment_output_info.state.write_mask & GPU_WRITE_GREEN) != 0) {
|
||||
attachment_state.colorWriteMask |= VK_COLOR_COMPONENT_G_BIT;
|
||||
}
|
||||
if ((fragment_output_info.state.write_mask & GPU_WRITE_BLUE) != 0) {
|
||||
attachment_state.colorWriteMask |= VK_COLOR_COMPONENT_B_BIT;
|
||||
}
|
||||
if ((fragment_output_info.state.write_mask & GPU_WRITE_ALPHA) != 0) {
|
||||
attachment_state.colorWriteMask |= VK_COLOR_COMPONENT_A_BIT;
|
||||
}
|
||||
|
||||
vk_pipeline_color_blend_attachment_states.append_n_times(
|
||||
attachment_state, fragment_output_info.color_attachment_formats.size());
|
||||
}
|
||||
|
||||
void build_color_blend_state(const VKGraphicsInfo::FragmentOut &fragment_output_info,
|
||||
const VKExtensions &extensions)
|
||||
{
|
||||
vk_pipeline_color_blend_state_create_info = {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
VK_FALSE,
|
||||
VK_LOGIC_OP_CLEAR,
|
||||
uint32_t(vk_pipeline_color_blend_attachment_states.size()),
|
||||
vk_pipeline_color_blend_attachment_states.data(),
|
||||
{1.0f, 1.0f, 1.0f, 1.0f}};
|
||||
/* Logic ops. */
|
||||
if (fragment_output_info.state.logic_op_xor && extensions.logic_ops) {
|
||||
vk_pipeline_color_blend_state_create_info.logicOpEnable = VK_TRUE;
|
||||
vk_pipeline_color_blend_state_create_info.logicOp = VK_LOGIC_OP_XOR;
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace blender::gpu
|
||||
82
blender-5.2.0/source/blender/gpu/vulkan/vk_image_view.cc
Normal file
82
blender-5.2.0/source/blender/gpu/vulkan/vk_image_view.cc
Normal file
@@ -0,0 +1,82 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "vk_image_view.hh"
|
||||
#include "vk_backend.hh"
|
||||
#include "vk_debug.hh"
|
||||
#include "vk_device.hh"
|
||||
#include "vk_texture.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
static VkFormat to_non_srgb_format(const VkFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case VK_FORMAT_R8G8B8_SRGB:
|
||||
return VK_FORMAT_R8G8B8_UNORM;
|
||||
case VK_FORMAT_R8G8B8A8_SRGB:
|
||||
return VK_FORMAT_R8G8B8A8_UNORM;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
VKImageView::VKImageView(VKTexture &texture, const VKImageViewInfo &info, StringRefNull name)
|
||||
: info(info)
|
||||
{
|
||||
const VkImageAspectFlags allowed_bits = VK_IMAGE_ASPECT_COLOR_BIT |
|
||||
(info.use_stencil ? VK_IMAGE_ASPECT_STENCIL_BIT :
|
||||
VK_IMAGE_ASPECT_DEPTH_BIT);
|
||||
TextureFormat device_format = texture.device_format_get();
|
||||
VkImageAspectFlags image_aspect = to_vk_image_aspect_flag_bits(device_format) & allowed_bits;
|
||||
|
||||
vk_format_ = to_vk_format(device_format);
|
||||
if (texture.format_flag_get() & GPU_FORMAT_SRGB && !info.use_srgb) {
|
||||
vk_format_ = to_non_srgb_format(vk_format_);
|
||||
}
|
||||
|
||||
VkImageViewCreateInfo image_view_info = {};
|
||||
image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
image_view_info.image = texture.vk_image_handle();
|
||||
image_view_info.viewType = to_vk_image_view_type(texture.type_get(), info.usage, info.arrayed);
|
||||
image_view_info.format = vk_format_;
|
||||
image_view_info.components.r = to_vk_component_swizzle(info.swizzle[0]);
|
||||
image_view_info.components.g = to_vk_component_swizzle(info.swizzle[1]);
|
||||
image_view_info.components.b = to_vk_component_swizzle(info.swizzle[2]);
|
||||
image_view_info.components.a = to_vk_component_swizzle(info.swizzle[3]);
|
||||
image_view_info.subresourceRange.aspectMask = image_aspect;
|
||||
image_view_info.subresourceRange.baseMipLevel = info.mip_range.first();
|
||||
image_view_info.subresourceRange.levelCount = info.mip_range.size();
|
||||
image_view_info.subresourceRange.baseArrayLayer = info.layer_range.first();
|
||||
image_view_info.subresourceRange.layerCount = info.layer_range.size();
|
||||
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
vkCreateImageView(device.vk_handle(), &image_view_info, nullptr, &vk_image_view_);
|
||||
debug::object_label(vk_image_view_, name.c_str());
|
||||
}
|
||||
|
||||
VKImageView::VKImageView(VKImageView &&other) : info(other.info)
|
||||
{
|
||||
vk_image_view_ = other.vk_image_view_;
|
||||
other.vk_image_view_ = VK_NULL_HANDLE;
|
||||
vk_format_ = other.vk_format_;
|
||||
other.vk_format_ = VK_FORMAT_UNDEFINED;
|
||||
}
|
||||
|
||||
VKImageView::~VKImageView()
|
||||
{
|
||||
if (vk_image_view_ != VK_NULL_HANDLE) {
|
||||
VKDiscardPool::discard_pool_get().discard_image_view(vk_image_view_);
|
||||
vk_image_view_ = VK_NULL_HANDLE;
|
||||
}
|
||||
vk_format_ = VK_FORMAT_UNDEFINED;
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
70
blender-5.2.0/source/blender/gpu/vulkan/vk_image_view.hh
Normal file
70
blender-5.2.0/source/blender/gpu/vulkan/vk_image_view.hh
Normal file
@@ -0,0 +1,70 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vk_common.hh"
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_utility_mixins.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
class VKTexture;
|
||||
|
||||
struct VKImageViewInfo {
|
||||
eImageViewUsage usage;
|
||||
IndexRange layer_range;
|
||||
IndexRange mip_range;
|
||||
union {
|
||||
char swizzle[4];
|
||||
uint32_t swizzle_data;
|
||||
};
|
||||
bool use_stencil;
|
||||
bool use_srgb;
|
||||
/**
|
||||
* When binding an image to a shader it needs to match the operations used inside the shader.
|
||||
*
|
||||
* If an shader accesses an image via an image view using the operation should match the view.
|
||||
* arrayed will ensure the right image view is created.
|
||||
*/
|
||||
VKImageViewArrayed arrayed;
|
||||
|
||||
bool operator==(const VKImageViewInfo &other) const
|
||||
{
|
||||
return usage == other.usage && layer_range == other.layer_range &&
|
||||
mip_range == other.mip_range && swizzle_data == other.swizzle_data &&
|
||||
use_stencil == other.use_stencil && use_srgb == other.use_srgb &&
|
||||
arrayed == other.arrayed;
|
||||
}
|
||||
};
|
||||
|
||||
class VKImageView : NonCopyable {
|
||||
VkImageView vk_image_view_ = VK_NULL_HANDLE;
|
||||
VkFormat vk_format_ = VK_FORMAT_UNDEFINED;
|
||||
|
||||
public:
|
||||
const VKImageViewInfo info;
|
||||
|
||||
VKImageView(VKTexture &texture, const VKImageViewInfo &info, StringRefNull name);
|
||||
|
||||
VKImageView(VKImageView &&other);
|
||||
~VKImageView();
|
||||
|
||||
VkImageView vk_handle() const
|
||||
{
|
||||
BLI_assert(vk_image_view_ != VK_NULL_HANDLE);
|
||||
return vk_image_view_;
|
||||
}
|
||||
|
||||
VkFormat vk_format() const
|
||||
{
|
||||
return vk_format_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
153
blender-5.2.0/source/blender/gpu/vulkan/vk_immediate.cc
Normal file
153
blender-5.2.0/source/blender/gpu/vulkan/vk_immediate.cc
Normal file
@@ -0,0 +1,153 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Mimics old style OpenGL immediate mode drawing.
|
||||
*/
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "GPU_matrix.hh"
|
||||
|
||||
#include "vk_backend.hh"
|
||||
#include "vk_context.hh"
|
||||
#include "vk_framebuffer.hh"
|
||||
#include "vk_immediate.hh"
|
||||
#include "vk_state_manager.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
static CLG_LogRef LOG = {"gpu.vulkan"};
|
||||
|
||||
uchar *VKImmediate::begin()
|
||||
{
|
||||
uint add_vertex = prim_type == GPU_PRIM_LINE_LOOP ? 1 : 0;
|
||||
const size_t bytes_needed = vertex_buffer_size(&vertex_format, vertex_len + add_vertex);
|
||||
size_t offset_alignment = GPU_storage_buffer_alignment();
|
||||
VKBuffer &buffer = ensure_space(bytes_needed, offset_alignment);
|
||||
|
||||
/* Apply alignment when allocating new sub buffer, to reduce signed/unsigned data conversion
|
||||
* later on. */
|
||||
buffer_offset_ += offset_alignment - 1;
|
||||
buffer_offset_ &= ~(offset_alignment - 1);
|
||||
BLI_assert((buffer_offset_ & (offset_alignment - 1)) == 0);
|
||||
|
||||
current_subbuffer_len_ = bytes_needed;
|
||||
uchar *data = static_cast<uchar *>(buffer.mapped_memory_get());
|
||||
return data + buffer_offset_;
|
||||
}
|
||||
|
||||
void VKImmediate::end()
|
||||
{
|
||||
BLI_assert_msg(prim_type != GPU_PRIM_NONE, "Illegal state: not between an immBegin/End pair.");
|
||||
if (vertex_idx == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (prim_type == GPU_PRIM_LINE_LOOP) {
|
||||
uchar *first_vertex_ptr = static_cast<uchar *>(active_buffer_->mapped_memory_get()) +
|
||||
buffer_offset_;
|
||||
size_t vertex_stride = current_subbuffer_len_ / (vertex_len + 1);
|
||||
uchar *last_vertex_ptr = first_vertex_ptr + vertex_stride * vertex_len;
|
||||
memcpy(last_vertex_ptr, first_vertex_ptr, vertex_stride);
|
||||
|
||||
prim_type = GPU_PRIM_LINE_STRIP;
|
||||
vertex_idx += 1;
|
||||
}
|
||||
|
||||
VKContext &context = *VKContext::get();
|
||||
BLI_assert(context.shader == shader);
|
||||
Shader &shader = *this->shader;
|
||||
if (shader.is_polyline) {
|
||||
VKBuffer &buffer = active_buffer_.value();
|
||||
VKStateManager &state_manager = context.state_manager_get();
|
||||
state_manager.storage_buffer_bind(BindSpaceStorageBuffers::Type::Buffer,
|
||||
&buffer,
|
||||
GPU_SSBO_POLYLINE_POS_BUF_SLOT,
|
||||
buffer_offset_);
|
||||
state_manager.storage_buffer_bind(BindSpaceStorageBuffers::Type::Buffer,
|
||||
&buffer,
|
||||
GPU_SSBO_POLYLINE_COL_BUF_SLOT,
|
||||
buffer_offset_);
|
||||
/* Not used. Satisfy the binding. */
|
||||
state_manager.storage_buffer_bind(
|
||||
BindSpaceStorageBuffers::Type::Buffer, &buffer, GPU_SSBO_INDEX_BUF_SLOT, buffer_offset_);
|
||||
this->polyline_draw_workaround(0);
|
||||
}
|
||||
else {
|
||||
GPU_matrix_bind(context.shader);
|
||||
render_graph::VKResourceAccessInfo &resource_access_info = context.reset_and_get_access_info();
|
||||
vertex_attributes_.update_bindings(*this);
|
||||
VKFrameBuffer &framebuffer = *context.active_framebuffer_get();
|
||||
framebuffer.rendering_ensure(context);
|
||||
|
||||
render_graph::VKDrawNode::CreateInfo draw(resource_access_info);
|
||||
draw.node_data.vertex_count = vertex_idx;
|
||||
draw.node_data.instance_count = 1;
|
||||
draw.node_data.first_vertex = 0;
|
||||
draw.node_data.first_instance = 0;
|
||||
|
||||
vertex_attributes_.bind(draw.node_data.vertex_buffers);
|
||||
context.update_pipeline_data(
|
||||
framebuffer, prim_type, vertex_attributes_, draw.node_data.graphics);
|
||||
|
||||
context.render_graph().add_node(draw);
|
||||
}
|
||||
|
||||
buffer_offset_ += current_subbuffer_len_;
|
||||
current_subbuffer_len_ = 0;
|
||||
}
|
||||
|
||||
VKBufferWithOffset VKImmediate::active_buffer() const
|
||||
{
|
||||
VKBufferWithOffset result = {active_buffer_->vk_handle(), buffer_offset_};
|
||||
return result;
|
||||
}
|
||||
|
||||
VkDeviceSize VKImmediate::buffer_bytes_free()
|
||||
{
|
||||
return active_buffer_->size_in_bytes() - buffer_offset_;
|
||||
}
|
||||
|
||||
static VkDeviceSize new_buffer_size(VkDeviceSize sub_buffer_size)
|
||||
{
|
||||
return max_ulul(sub_buffer_size, DEFAULT_INTERNAL_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
VKBuffer &VKImmediate::ensure_space(VkDeviceSize bytes_needed, VkDeviceSize offset_alignment)
|
||||
{
|
||||
VkDeviceSize bytes_required = bytes_needed + offset_alignment;
|
||||
|
||||
/* Last used buffer still has space. */
|
||||
if (active_buffer_.has_value() && buffer_bytes_free() >= bytes_required) {
|
||||
return active_buffer_.value();
|
||||
}
|
||||
|
||||
/* Offset alignment isn't needed when creating buffers as it is managed by VMA. */
|
||||
VkDeviceSize alloc_size = new_buffer_size(bytes_needed);
|
||||
CLOG_TRACE(&LOG,
|
||||
"Immediate buffer cannot hold another %d bytes, it contains %d bytes. A new "
|
||||
"buffer will be allocated (size=%d)",
|
||||
int(bytes_required),
|
||||
int(buffer_offset_),
|
||||
int(alloc_size));
|
||||
buffer_offset_ = 0;
|
||||
VKBuffer &result = active_buffer_.emplace();
|
||||
result.create(alloc_size,
|
||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
|
||||
VMA_ALLOCATION_CREATE_MAPPED_BIT |
|
||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
|
||||
0.8,
|
||||
false,
|
||||
"Immediate");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
52
blender-5.2.0/source/blender/gpu/vulkan/vk_immediate.hh
Normal file
52
blender-5.2.0/source/blender/gpu/vulkan/vk_immediate.hh
Normal file
@@ -0,0 +1,52 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Mimics old style OpenGL immediate mode drawing.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "gpu_immediate_private.hh"
|
||||
#include "gpu_vertex_format_private.hh"
|
||||
|
||||
#include "vk_buffer.hh"
|
||||
#include "vk_data_conversion.hh"
|
||||
#include "vk_mem_alloc.h"
|
||||
#include "vk_vertex_attribute_object.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class VKDevice;
|
||||
|
||||
/* Size of internal buffer. */
|
||||
constexpr size_t DEFAULT_INTERNAL_BUFFER_SIZE = 4 * 1024 * 1024;
|
||||
|
||||
class VKImmediate : public Immediate {
|
||||
private:
|
||||
VKVertexAttributeObject vertex_attributes_;
|
||||
|
||||
VkDeviceSize buffer_offset_ = 0;
|
||||
VkDeviceSize current_subbuffer_len_ = 0;
|
||||
|
||||
std::optional<VKBuffer> active_buffer_;
|
||||
|
||||
public:
|
||||
uchar *begin() override;
|
||||
void end() override;
|
||||
|
||||
friend class VKVertexAttributeObject;
|
||||
|
||||
private:
|
||||
VKBufferWithOffset active_buffer() const;
|
||||
VkDeviceSize buffer_bytes_free();
|
||||
|
||||
VKBuffer &ensure_space(VkDeviceSize bytes_needed, VkDeviceSize offset_alignment);
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
133
blender-5.2.0/source/blender/gpu/vulkan/vk_index_buffer.cc
Normal file
133
blender-5.2.0/source/blender/gpu/vulkan/vk_index_buffer.cc
Normal file
@@ -0,0 +1,133 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "vk_index_buffer.hh"
|
||||
#include "vk_shader.hh"
|
||||
#include "vk_shader_interface.hh"
|
||||
#include "vk_staging_buffer.hh"
|
||||
#include "vk_state_manager.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static CLG_LogRef LOG = {"gpu.vulkan"};
|
||||
|
||||
namespace gpu {
|
||||
|
||||
void VKIndexBuffer::ensure_updated()
|
||||
{
|
||||
if (is_subrange_) {
|
||||
src_->upload_data();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!buffer_.is_allocated()) {
|
||||
allocate();
|
||||
if (!buffer_.is_allocated()) {
|
||||
CLOG_ERROR(&LOG, "Unable to allocate index buffer. Most likely an out of memory issue.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (data_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data_uploaded_ && buffer_.is_mapped()) {
|
||||
buffer_.update_immediately(data_);
|
||||
MEM_SAFE_DELETE_VOID(data_);
|
||||
}
|
||||
else {
|
||||
VKContext &context = *VKContext::get();
|
||||
VKStagingBuffer staging_buffer(buffer_, VKStagingBuffer::Direction::HostToDevice);
|
||||
VKBuffer &buffer = staging_buffer.host_buffer_get();
|
||||
if (buffer.is_allocated()) {
|
||||
staging_buffer.host_buffer_get().update_immediately(data_);
|
||||
staging_buffer.copy_to_device(context);
|
||||
}
|
||||
else {
|
||||
buffer_.clear(context, 0u);
|
||||
CLOG_ERROR(
|
||||
&LOG,
|
||||
"Unable to upload data to index buffer via a staging buffer as the staging buffer "
|
||||
"could not be allocated. Index buffer will be filled with on zeros to reduce "
|
||||
"drawing artifacts due to read from uninitialized memory.");
|
||||
buffer_.clear(context, 0u);
|
||||
}
|
||||
MEM_SAFE_DELETE_VOID(data_);
|
||||
}
|
||||
|
||||
data_uploaded_ = true;
|
||||
}
|
||||
|
||||
void VKIndexBuffer::upload_data()
|
||||
{
|
||||
ensure_updated();
|
||||
}
|
||||
|
||||
void VKIndexBuffer::bind_as_ssbo(uint binding)
|
||||
{
|
||||
if (is_subrange_) {
|
||||
src_->bind_as_ssbo(binding);
|
||||
return;
|
||||
}
|
||||
|
||||
VKContext::get()->state_manager_get().storage_buffer_bind(
|
||||
BindSpaceStorageBuffers::Type::IndexBuffer, this, binding);
|
||||
}
|
||||
|
||||
void VKIndexBuffer::read(uint32_t *data) const
|
||||
{
|
||||
VKContext &context = *VKContext::get();
|
||||
VKStagingBuffer staging_buffer(buffer_, VKStagingBuffer::Direction::DeviceToHost);
|
||||
VKBuffer &buffer = staging_buffer.host_buffer_get();
|
||||
if (buffer.is_mapped()) {
|
||||
staging_buffer.copy_from_device(context);
|
||||
staging_buffer.host_buffer_get().read(context, data);
|
||||
}
|
||||
else {
|
||||
CLOG_ERROR(&LOG,
|
||||
"Unable to read data from index buffer via a staging buffer as the staging buffer "
|
||||
"could not be allocated. ");
|
||||
}
|
||||
}
|
||||
|
||||
void VKIndexBuffer::update_sub(uint /*start*/, uint /*len*/, const void * /*data*/)
|
||||
{
|
||||
NOT_YET_IMPLEMENTED
|
||||
}
|
||||
|
||||
void VKIndexBuffer::strip_restart_indices()
|
||||
{
|
||||
NOT_YET_IMPLEMENTED
|
||||
}
|
||||
|
||||
void VKIndexBuffer::allocate()
|
||||
{
|
||||
buffer_.create(size_get(),
|
||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
VMA_MEMORY_USAGE_AUTO,
|
||||
VmaAllocationCreateFlags(0),
|
||||
0.8f,
|
||||
false,
|
||||
"IndexBuffer");
|
||||
}
|
||||
|
||||
const VKBuffer &VKIndexBuffer::buffer_get() const
|
||||
{
|
||||
return is_subrange_ ? unwrap(src_)->buffer_ : buffer_;
|
||||
}
|
||||
VKBuffer &VKIndexBuffer::buffer_get()
|
||||
{
|
||||
return is_subrange_ ? unwrap(src_)->buffer_ : buffer_;
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
62
blender-5.2.0/source/blender/gpu/vulkan/vk_index_buffer.hh
Normal file
62
blender-5.2.0/source/blender/gpu/vulkan/vk_index_buffer.hh
Normal file
@@ -0,0 +1,62 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_index_buffer.hh"
|
||||
|
||||
#include "vk_buffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class VKIndexBuffer : public IndexBuf {
|
||||
VKBuffer buffer_;
|
||||
bool data_uploaded_ = false;
|
||||
|
||||
public:
|
||||
void upload_data() override;
|
||||
|
||||
void bind_as_ssbo(uint binding) override;
|
||||
|
||||
void read(uint32_t *data) const override;
|
||||
|
||||
void update_sub(uint start, uint len, const void *data) override;
|
||||
|
||||
VkBuffer vk_handle() const
|
||||
{
|
||||
return buffer_get().vk_handle();
|
||||
}
|
||||
inline VkDeviceAddress device_address_get() const
|
||||
{
|
||||
return buffer_get().device_address_get();
|
||||
}
|
||||
VkIndexType vk_index_type() const
|
||||
{
|
||||
return to_vk_index_type(index_type_);
|
||||
}
|
||||
/** \brief Return the allocated size of the buffer in bytes. */
|
||||
inline VkDeviceSize allocated_size_get() const
|
||||
{
|
||||
return buffer_get().allocated_size_in_bytes();
|
||||
}
|
||||
|
||||
void ensure_updated();
|
||||
|
||||
private:
|
||||
void strip_restart_indices() override;
|
||||
void allocate();
|
||||
VKBuffer &buffer_get();
|
||||
const VKBuffer &buffer_get() const;
|
||||
};
|
||||
|
||||
static inline VKIndexBuffer *unwrap(IndexBuf *index_buffer)
|
||||
{
|
||||
return static_cast<VKIndexBuffer *>(index_buffer);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
30
blender-5.2.0/source/blender/gpu/vulkan/vk_memory.hh
Normal file
30
blender-5.2.0/source/blender/gpu/vulkan/vk_memory.hh
Normal file
@@ -0,0 +1,30 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vk_common.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
/** Information about an exported buffer/image. */
|
||||
struct VKMemoryExport {
|
||||
/** Handle that has been exported. */
|
||||
uint64_t handle;
|
||||
/**
|
||||
* Allocated memory size. Allocation size can be larger than actually requested due to memory
|
||||
* alignment/allocation rules.
|
||||
*/
|
||||
VkDeviceSize memory_size;
|
||||
/**
|
||||
* Actually content offset inside the exported memory. A memory allocation can contain multiple
|
||||
* buffers or images. The offset points to the specific buffer/image that is exported.
|
||||
*/
|
||||
VkDeviceSize memory_offset;
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
218
blender-5.2.0/source/blender/gpu/vulkan/vk_memory_layout.cc
Normal file
218
blender-5.2.0/source/blender/gpu/vulkan/vk_memory_layout.cc
Normal file
@@ -0,0 +1,218 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "vk_memory_layout.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Std430 memory layout
|
||||
* \{ */
|
||||
|
||||
uint32_t Std430::component_mem_size(const shader::Type /*type*/)
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
uint32_t Std430::element_alignment(const shader::Type type, const bool /*is_array*/)
|
||||
{
|
||||
switch (type) {
|
||||
case shader::Type::float_t:
|
||||
case shader::Type::uint_t:
|
||||
case shader::Type::int_t:
|
||||
case shader::Type::bool_t:
|
||||
return 4;
|
||||
case shader::Type::float2_t:
|
||||
case shader::Type::uint2_t:
|
||||
case shader::Type::int2_t:
|
||||
return 8;
|
||||
case shader::Type::float3_t:
|
||||
case shader::Type::uint3_t:
|
||||
case shader::Type::int3_t:
|
||||
case shader::Type::float4_t:
|
||||
case shader::Type::uint4_t:
|
||||
case shader::Type::int4_t:
|
||||
case shader::Type::float3x3_t:
|
||||
case shader::Type::float4x4_t:
|
||||
return 16;
|
||||
default:
|
||||
BLI_assert_msg(false, "Type not supported in dynamic structs.");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Std430::element_components_len(const shader::Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case shader::Type::float_t:
|
||||
case shader::Type::uint_t:
|
||||
case shader::Type::int_t:
|
||||
case shader::Type::bool_t:
|
||||
return 1;
|
||||
case shader::Type::float2_t:
|
||||
case shader::Type::uint2_t:
|
||||
case shader::Type::int2_t:
|
||||
return 2;
|
||||
case shader::Type::float3_t:
|
||||
case shader::Type::uint3_t:
|
||||
case shader::Type::int3_t:
|
||||
return 3;
|
||||
case shader::Type::float4_t:
|
||||
case shader::Type::uint4_t:
|
||||
case shader::Type::int4_t:
|
||||
return 4;
|
||||
case shader::Type::float3x3_t:
|
||||
return 12;
|
||||
case shader::Type::float4x4_t:
|
||||
return 16;
|
||||
default:
|
||||
BLI_assert_msg(false, "Type not supported in dynamic structs.");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Std430::array_components_len(const shader::Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case shader::Type::float_t:
|
||||
case shader::Type::uint_t:
|
||||
case shader::Type::int_t:
|
||||
case shader::Type::bool_t:
|
||||
return 1;
|
||||
case shader::Type::float2_t:
|
||||
case shader::Type::uint2_t:
|
||||
case shader::Type::int2_t:
|
||||
return 2;
|
||||
case shader::Type::float3_t:
|
||||
case shader::Type::uint3_t:
|
||||
case shader::Type::int3_t:
|
||||
case shader::Type::float4_t:
|
||||
case shader::Type::uint4_t:
|
||||
case shader::Type::int4_t:
|
||||
return 4;
|
||||
case shader::Type::float3x3_t:
|
||||
return 12;
|
||||
case shader::Type::float4x4_t:
|
||||
return 16;
|
||||
default:
|
||||
BLI_assert_msg(false, "Type not supported in dynamic structs.");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Std430::inner_row_padding(const shader::Type type)
|
||||
{
|
||||
return type == shader::Type::float3x3_t ? 3 : 0;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Std140 memory layout
|
||||
* \{ */
|
||||
|
||||
uint32_t Std140::component_mem_size(const shader::Type /*type*/)
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
uint32_t Std140::element_alignment(const shader::Type type, const bool is_array)
|
||||
{
|
||||
if (is_array) {
|
||||
return 16;
|
||||
}
|
||||
switch (type) {
|
||||
case shader::Type::float_t:
|
||||
case shader::Type::uint_t:
|
||||
case shader::Type::int_t:
|
||||
case shader::Type::bool_t:
|
||||
return 4;
|
||||
case shader::Type::float2_t:
|
||||
case shader::Type::uint2_t:
|
||||
case shader::Type::int2_t:
|
||||
return 8;
|
||||
case shader::Type::float3_t:
|
||||
case shader::Type::uint3_t:
|
||||
case shader::Type::int3_t:
|
||||
case shader::Type::float4_t:
|
||||
case shader::Type::uint4_t:
|
||||
case shader::Type::int4_t:
|
||||
case shader::Type::float3x3_t:
|
||||
case shader::Type::float4x4_t:
|
||||
return 16;
|
||||
default:
|
||||
BLI_assert_msg(false, "Type not supported in dynamic structs.");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Std140::element_components_len(const shader::Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case shader::Type::float_t:
|
||||
case shader::Type::uint_t:
|
||||
case shader::Type::int_t:
|
||||
case shader::Type::bool_t:
|
||||
return 1;
|
||||
case shader::Type::float2_t:
|
||||
case shader::Type::uint2_t:
|
||||
case shader::Type::int2_t:
|
||||
return 2;
|
||||
case shader::Type::float3_t:
|
||||
case shader::Type::uint3_t:
|
||||
case shader::Type::int3_t:
|
||||
return 3;
|
||||
case shader::Type::float4_t:
|
||||
case shader::Type::uint4_t:
|
||||
case shader::Type::int4_t:
|
||||
return 4;
|
||||
case shader::Type::float3x3_t:
|
||||
return 12;
|
||||
case shader::Type::float4x4_t:
|
||||
return 16;
|
||||
default:
|
||||
BLI_assert_msg(false, "Type not supported in dynamic structs.");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Std140::array_components_len(const shader::Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case shader::Type::float_t:
|
||||
case shader::Type::uint_t:
|
||||
case shader::Type::int_t:
|
||||
case shader::Type::bool_t:
|
||||
case shader::Type::float2_t:
|
||||
case shader::Type::uint2_t:
|
||||
case shader::Type::int2_t:
|
||||
case shader::Type::float3_t:
|
||||
case shader::Type::uint3_t:
|
||||
case shader::Type::int3_t:
|
||||
case shader::Type::float4_t:
|
||||
case shader::Type::uint4_t:
|
||||
case shader::Type::int4_t:
|
||||
return 4;
|
||||
case shader::Type::float3x3_t:
|
||||
return 12;
|
||||
case shader::Type::float4x4_t:
|
||||
return 16;
|
||||
default:
|
||||
BLI_assert_msg(false, "Type not supported in dynamic structs.");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Std140::inner_row_padding(const shader::Type /*type*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu
|
||||
150
blender-5.2.0/source/blender/gpu/vulkan/vk_memory_layout.hh
Normal file
150
blender-5.2.0/source/blender/gpu/vulkan/vk_memory_layout.hh
Normal file
@@ -0,0 +1,150 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/**
|
||||
* Information about alignment/components and memory size for types when using std140 layout.
|
||||
*/
|
||||
struct Std140 {
|
||||
/** Get the memory size in bytes of a single component using by the given type. */
|
||||
static uint32_t component_mem_size(const shader::Type type);
|
||||
/** Get to alignment of the given type in bytes. */
|
||||
static uint32_t element_alignment(const shader::Type type, bool is_array);
|
||||
/** Get the number of components that should be allocated for the given type. */
|
||||
static uint32_t element_components_len(const shader::Type type);
|
||||
/** Get the number of components of the given type when used in an array. */
|
||||
static uint32_t array_components_len(const shader::Type type);
|
||||
/**
|
||||
* After how many input values do we expect row padding.
|
||||
*
|
||||
* Mat3 in Std430 needs to be padded so every row would contain 4 floats.
|
||||
* inner_row_padding would contain `3` in this case, otherwise this is set to 0.
|
||||
*/
|
||||
static uint32_t inner_row_padding(const shader::Type type);
|
||||
};
|
||||
|
||||
/**
|
||||
* Information about alignment/components and memory size for types when using std430 layout.
|
||||
*/
|
||||
struct Std430 {
|
||||
/** Get the memory size in bytes of a single component using by the given type. */
|
||||
static uint32_t component_mem_size(const shader::Type type);
|
||||
/** Get to alignment of the given type in bytes. */
|
||||
static uint32_t element_alignment(const shader::Type type, bool is_array);
|
||||
/** Get the number of components that should be allocated for the given type. */
|
||||
static uint32_t element_components_len(const shader::Type type);
|
||||
/** Get the number of components of the given type when used in an array. */
|
||||
static uint32_t array_components_len(const shader::Type type);
|
||||
/**
|
||||
* After how many input values do we expect row padding.
|
||||
*
|
||||
* Mat3 in Std430 needs to be padded so every row would contain 4 floats.
|
||||
* inner_row_padding would contain `3` in this case, otherwise this is set to 0.
|
||||
*/
|
||||
static uint32_t inner_row_padding(const shader::Type type);
|
||||
};
|
||||
|
||||
template<typename LayoutT> static uint32_t element_stride(const shader::Type type)
|
||||
{
|
||||
return LayoutT::element_components_len(type) * LayoutT::component_mem_size(type);
|
||||
}
|
||||
|
||||
template<typename LayoutT> static uint32_t array_stride(const shader::Type type)
|
||||
{
|
||||
return LayoutT::array_components_len(type) * LayoutT::component_mem_size(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the r_offset to the next alignment where the given type+array_size can be
|
||||
* reserved.
|
||||
*
|
||||
* 'type': The type that needs to be aligned.
|
||||
* 'array_size': The array_size that needs to be aligned. (0=no array).
|
||||
* 'r_offset': After the call it will point to the byte where the reservation
|
||||
* can happen.
|
||||
*/
|
||||
template<typename LayoutT>
|
||||
static void align(const shader::Type &type, const int32_t array_size, uint32_t *r_offset)
|
||||
{
|
||||
uint32_t alignment = LayoutT::element_alignment(type, array_size != 0);
|
||||
uint32_t alignment_mask = alignment - 1;
|
||||
uint32_t offset = *r_offset;
|
||||
if ((offset & alignment_mask) != 0) {
|
||||
offset &= ~alignment_mask;
|
||||
offset += alignment;
|
||||
*r_offset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserve space for the given type and array size.
|
||||
*
|
||||
* This function doesn't handle alignment this needs to be done up front by calling
|
||||
* 'align<Layout>' function. Caller is responsible for this.
|
||||
*
|
||||
* 'type': The type that needs to be reserved.
|
||||
* 'array_size': The array_size that needs to be reserved. (0=no array).
|
||||
* 'r_offset': When calling needs to be pointing to the aligned location where to
|
||||
* reserve space. After the call it will point to the byte just after reserved
|
||||
* space.
|
||||
*/
|
||||
template<typename LayoutT>
|
||||
static void reserve(const shader::Type type, int32_t array_size, uint32_t *r_offset)
|
||||
{
|
||||
uint32_t size = array_size == 0 ? element_stride<LayoutT>(type) :
|
||||
array_stride<LayoutT>(type) * array_size;
|
||||
*r_offset += size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update 'r_offset' to be aligned to the end of the struct.
|
||||
*
|
||||
* Call this function when all attributes have been added to make sure that the struct size is
|
||||
* correct.
|
||||
*/
|
||||
template<typename LayoutT> static void align_end_of_struct(uint32_t *r_offset)
|
||||
{
|
||||
align<LayoutT>(shader::Type::float4_t, 0, r_offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Image transfers can use multiple transfer buffers for a single request.
|
||||
*
|
||||
* This helper class helps in splitting the transfers in smaller chunks.
|
||||
*/
|
||||
struct TransferRegion {
|
||||
int3 offset;
|
||||
int3 extent;
|
||||
IndexRange layers;
|
||||
|
||||
int64_t sample_count() const
|
||||
{
|
||||
return int64_t(extent.x) * int64_t(extent.y) * int64_t(extent.z) * layers.size();
|
||||
}
|
||||
|
||||
int64_t result_offset(int3 sub_offset, int layer) const
|
||||
{
|
||||
int64_t diff_x = sub_offset.x - offset.x;
|
||||
int64_t diff_y = sub_offset.y - offset.y;
|
||||
int64_t diff_z = sub_offset.z - offset.z;
|
||||
int64_t diff_l = layer - layers.start();
|
||||
|
||||
return (diff_l * extent.x * extent.y * extent.z) + (diff_z * extent.x * extent.y) +
|
||||
(diff_y * extent.x) + diff_x;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
104
blender-5.2.0/source/blender/gpu/vulkan/vk_memory_pool.cc
Normal file
104
blender-5.2.0/source/blender/gpu/vulkan/vk_memory_pool.cc
Normal file
@@ -0,0 +1,104 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "vk_memory_pool.hh"
|
||||
|
||||
#include "vk_device.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
void VKMemoryPools::init(VKDevice &device)
|
||||
{
|
||||
if (device.extensions_get().external_memory) {
|
||||
init_external_memory_image(device);
|
||||
init_external_memory_pixel_buffer(device);
|
||||
}
|
||||
}
|
||||
|
||||
void VKMemoryPools::init_external_memory_image(VKDevice &device)
|
||||
{
|
||||
VkExternalMemoryImageCreateInfo external_image_create_info = {
|
||||
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
|
||||
nullptr,
|
||||
vk_external_memory_handle_type()};
|
||||
VkImageCreateInfo image_create_info = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
&external_image_create_info,
|
||||
0,
|
||||
VK_IMAGE_TYPE_2D,
|
||||
VK_FORMAT_R8G8B8A8_UNORM,
|
||||
{1024, 1024, 1},
|
||||
1,
|
||||
1,
|
||||
VK_SAMPLE_COUNT_1_BIT,
|
||||
VK_IMAGE_TILING_OPTIMAL,
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
VK_SHARING_MODE_EXCLUSIVE,
|
||||
0,
|
||||
nullptr,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED};
|
||||
VmaAllocationCreateInfo allocation_create_info = {};
|
||||
allocation_create_info.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
|
||||
allocation_create_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
|
||||
uint32_t memory_type_index;
|
||||
vmaFindMemoryTypeIndexForImageInfo(
|
||||
device.mem_allocator_get(), &image_create_info, &allocation_create_info, &memory_type_index);
|
||||
|
||||
external_memory_image.info.handleTypes = vk_external_memory_handle_type();
|
||||
VmaPoolCreateInfo pool_create_info = {};
|
||||
pool_create_info.memoryTypeIndex = memory_type_index;
|
||||
pool_create_info.pMemoryAllocateNext = &external_memory_image.info;
|
||||
pool_create_info.priority = 1.0f;
|
||||
vmaCreatePool(device.mem_allocator_get(), &pool_create_info, &external_memory_image.pool);
|
||||
}
|
||||
|
||||
void VKMemoryPools::init_external_memory_pixel_buffer(VKDevice &device)
|
||||
{
|
||||
VkExternalMemoryBufferCreateInfo external_buffer_create_info = {
|
||||
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO,
|
||||
nullptr,
|
||||
vk_external_memory_handle_type()};
|
||||
VkBufferCreateInfo buffer_create_info = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
&external_buffer_create_info,
|
||||
0,
|
||||
1024,
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT |
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
VK_SHARING_MODE_EXCLUSIVE,
|
||||
0,
|
||||
nullptr};
|
||||
VmaAllocationCreateInfo allocation_create_info = {};
|
||||
allocation_create_info.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
|
||||
allocation_create_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
|
||||
uint32_t memory_type_index;
|
||||
vmaFindMemoryTypeIndexForBufferInfo(device.mem_allocator_get(),
|
||||
&buffer_create_info,
|
||||
&allocation_create_info,
|
||||
&memory_type_index);
|
||||
|
||||
external_memory_pixel_buffer.info.handleTypes = vk_external_memory_handle_type();
|
||||
VmaPoolCreateInfo pool_create_info = {};
|
||||
pool_create_info.memoryTypeIndex = memory_type_index;
|
||||
pool_create_info.pMemoryAllocateNext = &external_memory_pixel_buffer.info;
|
||||
pool_create_info.priority = 1.0f;
|
||||
vmaCreatePool(device.mem_allocator_get(), &pool_create_info, &external_memory_pixel_buffer.pool);
|
||||
}
|
||||
|
||||
void VKMemoryPools::deinit(VKDevice &device)
|
||||
{
|
||||
external_memory_image.deinit(device);
|
||||
external_memory_pixel_buffer.deinit(device);
|
||||
}
|
||||
|
||||
void VKMemoryPool::deinit(VKDevice &device)
|
||||
{
|
||||
vmaDestroyPool(device.mem_allocator_get(), pool);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
49
blender-5.2.0/source/blender/gpu/vulkan/vk_memory_pool.hh
Normal file
49
blender-5.2.0/source/blender/gpu/vulkan/vk_memory_pool.hh
Normal file
@@ -0,0 +1,49 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vk_common.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
class VKDevice;
|
||||
|
||||
constexpr VkExternalMemoryHandleTypeFlags vk_external_memory_handle_type()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
|
||||
#else
|
||||
return VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* VMA related data for a memory pool.
|
||||
*/
|
||||
struct VKMemoryPool {
|
||||
/* NOTE: This attribute needs to be kept alive as it will be read by VMA when allocating inside
|
||||
* the pool. */
|
||||
VkExportMemoryAllocateInfoKHR info = {VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR};
|
||||
VmaPool pool = VK_NULL_HANDLE;
|
||||
|
||||
void deinit(VKDevice &device);
|
||||
};
|
||||
|
||||
struct VKMemoryPools {
|
||||
VKMemoryPool external_memory_image = {};
|
||||
VKMemoryPool external_memory_pixel_buffer = {};
|
||||
|
||||
void init(VKDevice &device);
|
||||
void deinit(VKDevice &device);
|
||||
|
||||
private:
|
||||
void init_external_memory_image(VKDevice &device);
|
||||
void init_external_memory_pixel_buffer(VKDevice &device);
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
755
blender-5.2.0/source/blender/gpu/vulkan/vk_pipeline_pool.cc
Normal file
755
blender-5.2.0/source/blender/gpu/vulkan/vk_pipeline_pool.cc
Normal file
@@ -0,0 +1,755 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "BKE_appdir.hh"
|
||||
#include "BKE_blender_version.h"
|
||||
|
||||
#include "BLI_fileops.hh"
|
||||
#include "BLI_path_utils.hh"
|
||||
#include "BLI_time.h"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "vk_backend.hh"
|
||||
#include "vk_graphics_pipeline.hh"
|
||||
#include "vk_pipeline_pool.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#ifdef WITH_BUILDINFO
|
||||
extern "C" char build_hash[];
|
||||
#endif
|
||||
static CLG_LogRef LOG = {"gpu.vulkan"};
|
||||
|
||||
namespace gpu {
|
||||
|
||||
void VKPipelinePool::init()
|
||||
{
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
VkPipelineCacheCreateInfo create_info = {};
|
||||
create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
|
||||
vkCreatePipelineCache(device.vk_handle(), &create_info, nullptr, &vk_pipeline_cache_static_);
|
||||
debug::object_label(vk_pipeline_cache_static_, "VkPipelineCache.Static");
|
||||
vkCreatePipelineCache(device.vk_handle(), &create_info, nullptr, &vk_pipeline_cache_non_static_);
|
||||
debug::object_label(vk_pipeline_cache_non_static_, "VkPipelineCache.Dynamic");
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Compute pipelines
|
||||
* \{ */
|
||||
|
||||
VkPipeline VKPipelinePool::get_or_create_compute_pipeline(const VKComputeInfo &compute_info,
|
||||
const bool is_static_shader,
|
||||
VkPipeline vk_pipeline_base,
|
||||
StringRefNull name)
|
||||
{
|
||||
bool created = false;
|
||||
VkPipelineCache vk_pipeline_cache = is_static_shader ? vk_pipeline_cache_static_ :
|
||||
vk_pipeline_cache_non_static_;
|
||||
return compute_.get_or_create(compute_info, vk_pipeline_cache, vk_pipeline_base, name, created);
|
||||
}
|
||||
|
||||
template<>
|
||||
VkPipeline VKPipelineMap<VKComputeInfo>::create(const VKComputeInfo &compute_info,
|
||||
VkPipelineCache vk_pipeline_cache,
|
||||
VkPipeline vk_pipeline_base,
|
||||
StringRefNull name)
|
||||
{
|
||||
/* Building compute pipeline create info */
|
||||
const bool do_specialization_constants = !compute_info.specialization_constants.is_empty();
|
||||
VkComputePipelineCreateInfo vk_compute_pipeline_create_info = {
|
||||
VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
compute_info.vk_shader_module,
|
||||
"main",
|
||||
nullptr},
|
||||
compute_info.vk_pipeline_layout,
|
||||
vk_pipeline_base,
|
||||
0};
|
||||
|
||||
/* Specialization constants */
|
||||
VkSpecializationInfo vk_specialization_info;
|
||||
Array<VkSpecializationMapEntry> vk_specialization_map_entries(
|
||||
compute_info.specialization_constants.size());
|
||||
if (do_specialization_constants) {
|
||||
vk_compute_pipeline_create_info.stage.pSpecializationInfo = &vk_specialization_info;
|
||||
for (uint32_t index : IndexRange(compute_info.specialization_constants.size())) {
|
||||
vk_specialization_map_entries[index] = {
|
||||
index, uint32_t(index * sizeof(uint32_t)), sizeof(uint32_t)};
|
||||
}
|
||||
vk_specialization_info = {uint32_t(vk_specialization_map_entries.size()),
|
||||
vk_specialization_map_entries.data(),
|
||||
compute_info.specialization_constants.size() * sizeof(uint32_t),
|
||||
compute_info.specialization_constants.data()};
|
||||
}
|
||||
|
||||
/* Create pipeline. */
|
||||
VKBackend &backend = VKBackend::get();
|
||||
VKDevice &device = backend.device;
|
||||
|
||||
double start_time = BLI_time_now_seconds();
|
||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
vkCreateComputePipelines(device.vk_handle(),
|
||||
vk_pipeline_cache,
|
||||
1,
|
||||
&vk_compute_pipeline_create_info,
|
||||
nullptr,
|
||||
&pipeline);
|
||||
double end_time = BLI_time_now_seconds();
|
||||
debug::object_label(pipeline, name);
|
||||
CLOG_DEBUG(&LOG,
|
||||
"Compiled compute pipeline %s in %fms ",
|
||||
name.c_str(),
|
||||
(end_time - start_time) * 1000.0);
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Graphics pipelines
|
||||
* \{ */
|
||||
|
||||
VkPipeline VKPipelinePool::get_or_create_graphics_pipeline(const VKGraphicsInfo &graphics_info,
|
||||
const bool is_static_shader,
|
||||
VkPipeline vk_pipeline_base,
|
||||
StringRefNull name,
|
||||
bool &r_created)
|
||||
{
|
||||
BLI_assert_msg(
|
||||
graphics_info.shaders.state == graphics_info.fragment_out.state,
|
||||
"VKGraphicsInfo.shader.state and VKGraphicsInfo.fragment_out.state should be identical, "
|
||||
"otherwise an incorrect fragment output library will be linked.");
|
||||
VkPipelineCache vk_pipeline_cache = is_static_shader ? vk_pipeline_cache_static_ :
|
||||
vk_pipeline_cache_non_static_;
|
||||
return graphics_.get_or_create(
|
||||
graphics_info, vk_pipeline_cache, vk_pipeline_base, name, r_created);
|
||||
}
|
||||
|
||||
static VkPipeline create_graphics_pipeline_no_libs(const VKGraphicsInfo &graphics_info,
|
||||
VkPipelineCache vk_pipeline_cache,
|
||||
VkPipeline vk_pipeline_base,
|
||||
StringRefNull name)
|
||||
{
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
VKGraphicsPipelineCreateInfoBuilder builder;
|
||||
builder.build_full(device, graphics_info, vk_pipeline_base);
|
||||
|
||||
/* Build pipeline. */
|
||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
double start_time = BLI_time_now_seconds();
|
||||
vkCreateGraphicsPipelines(device.vk_handle(),
|
||||
vk_pipeline_cache,
|
||||
1,
|
||||
&builder.vk_graphics_pipeline_create_info,
|
||||
nullptr,
|
||||
&pipeline);
|
||||
double end_time = BLI_time_now_seconds();
|
||||
debug::object_label(pipeline, name);
|
||||
CLOG_DEBUG(&LOG,
|
||||
"Compiled graphics pipeline %s in %fms ",
|
||||
name.c_str(),
|
||||
(end_time - start_time) * 1000.0);
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
static VkPipeline create_graphics_pipeline_libs(const VKGraphicsInfo &graphics_info,
|
||||
VkPipelineCache vk_pipeline_cache,
|
||||
VkPipeline vk_pipeline_base,
|
||||
StringRefNull name)
|
||||
{
|
||||
double start_time = BLI_time_now_seconds();
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
|
||||
VkPipeline vertex_input_lib = device.pipelines.get_or_create_vertex_input_lib(
|
||||
graphics_info.vertex_in);
|
||||
VkPipeline shaders_lib = device.pipelines.get_or_create_shaders_lib(graphics_info.shaders);
|
||||
VkPipeline fragment_output_lib = device.pipelines.get_or_create_fragment_output_lib(
|
||||
graphics_info.fragment_out);
|
||||
|
||||
std::array<VkPipeline, 3> pipeline_libraries = {
|
||||
vertex_input_lib, shaders_lib, fragment_output_lib};
|
||||
|
||||
/* Linking */
|
||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
VkPipelineLibraryCreateInfoKHR vk_pipeline_library_create_info = {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR,
|
||||
nullptr,
|
||||
uint32_t(pipeline_libraries.size()),
|
||||
pipeline_libraries.data()};
|
||||
VkGraphicsPipelineCreateInfo linking_pipeline_create_info = {
|
||||
VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
&vk_pipeline_library_create_info,
|
||||
VK_PIPELINE_CREATE_LINK_TIME_OPTIMIZATION_BIT_EXT,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
graphics_info.shaders.vk_pipeline_layout,
|
||||
VK_NULL_HANDLE,
|
||||
0,
|
||||
vk_pipeline_base,
|
||||
0};
|
||||
double start_link_time = BLI_time_now_seconds();
|
||||
vkCreateGraphicsPipelines(
|
||||
device.vk_handle(), vk_pipeline_cache, 1, &linking_pipeline_create_info, nullptr, &pipeline);
|
||||
double end_time = BLI_time_now_seconds();
|
||||
debug::object_label(pipeline, name);
|
||||
CLOG_TRACE(&LOG,
|
||||
"Linking graphics pipeline %s in %fms ",
|
||||
name.c_str(),
|
||||
(end_time - start_link_time) * 1000.0);
|
||||
CLOG_DEBUG(&LOG,
|
||||
"Compiling graphics pipeline %s in %fms ",
|
||||
name.c_str(),
|
||||
(end_time - start_time) * 1000.0);
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
template<>
|
||||
VkPipeline VKPipelineMap<VKGraphicsInfo>::create(const VKGraphicsInfo &graphics_info,
|
||||
VkPipelineCache vk_pipeline_cache,
|
||||
VkPipeline vk_pipeline_base,
|
||||
StringRefNull name)
|
||||
{
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
const VKExtensions &extensions = device.extensions_get();
|
||||
if (extensions.graphics_pipeline_library) {
|
||||
return create_graphics_pipeline_libs(graphics_info, vk_pipeline_cache, vk_pipeline_base, name);
|
||||
}
|
||||
return create_graphics_pipeline_no_libs(
|
||||
graphics_info, vk_pipeline_cache, vk_pipeline_base, name);
|
||||
}
|
||||
|
||||
std::string VKGraphicsInfo::pipeline_info_source() const
|
||||
{
|
||||
std::stringstream result;
|
||||
result << "info.pipeline_state()\n";
|
||||
result << " .primitive(";
|
||||
switch (vertex_in.vk_topology) {
|
||||
case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
|
||||
result << "GPU_PRIM_POINTS";
|
||||
break;
|
||||
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
|
||||
result << "GPU_PRIM_LINES";
|
||||
break;
|
||||
case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
|
||||
result << "GPU_PRIM_LINE_STRIP";
|
||||
break;
|
||||
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
|
||||
result << "GPU_PRIM_TRIS";
|
||||
break;
|
||||
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
|
||||
result << "GPU_PRIM_TRI_STRIP";
|
||||
break;
|
||||
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
|
||||
result << "GPU_PRIM_TRI_FAN";
|
||||
break;
|
||||
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
|
||||
result << "GPU_PRIM_LINES_ADJ";
|
||||
break;
|
||||
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
|
||||
result << "GPU_PRIM_TRIS_ADJ";
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
};
|
||||
result << ")\n";
|
||||
result << " .state(";
|
||||
/* Write mask */
|
||||
Vector<std::string> write_masks;
|
||||
if (fragment_out.state.write_mask & GPU_WRITE_COLOR) {
|
||||
if ((fragment_out.state.write_mask & GPU_WRITE_COLOR) == GPU_WRITE_COLOR) {
|
||||
write_masks.append("GPU_WRITE_COLOR");
|
||||
}
|
||||
else {
|
||||
if (fragment_out.state.write_mask & GPU_WRITE_RED) {
|
||||
write_masks.append("GPU_WRITE_RED");
|
||||
}
|
||||
if (fragment_out.state.write_mask & GPU_WRITE_GREEN) {
|
||||
write_masks.append("GPU_WRITE_GREEN");
|
||||
}
|
||||
if (fragment_out.state.write_mask & GPU_WRITE_BLUE) {
|
||||
write_masks.append("GPU_WRITE_BLUE");
|
||||
}
|
||||
if (fragment_out.state.write_mask & GPU_WRITE_ALPHA) {
|
||||
write_masks.append("GPU_WRITE_ALPHA");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fragment_out.state.write_mask & GPU_WRITE_DEPTH) {
|
||||
write_masks.append("GPU_WRITE_DEPTH");
|
||||
}
|
||||
if (fragment_out.state.write_mask & GPU_WRITE_STENCIL) {
|
||||
write_masks.append("GPU_WRITE_STENCIL");
|
||||
}
|
||||
if (write_masks.is_empty()) {
|
||||
write_masks.append("GPU_WRITE_NONE");
|
||||
}
|
||||
for (const std::string &write_mask : write_masks) {
|
||||
result << write_mask;
|
||||
if (&write_mask != &write_masks.last()) {
|
||||
result << " | ";
|
||||
}
|
||||
}
|
||||
/* Blending mode */
|
||||
result << ",\n ";
|
||||
switch (fragment_out.state.blend) {
|
||||
case GPU_BLEND_NONE:
|
||||
result << "GPU_BLEND_NONE";
|
||||
break;
|
||||
case GPU_BLEND_ALPHA:
|
||||
result << "GPU_BLEND_ALPHA";
|
||||
break;
|
||||
case GPU_BLEND_ALPHA_PREMULT:
|
||||
result << "GPU_BLEND_ALPHA_PREMULT";
|
||||
break;
|
||||
case GPU_BLEND_ADDITIVE:
|
||||
result << "GPU_BLEND_ADDITIVE";
|
||||
break;
|
||||
case GPU_BLEND_ADDITIVE_PREMULT:
|
||||
result << "GPU_BLEND_ADDITIVE_PREMULT";
|
||||
break;
|
||||
case GPU_BLEND_MULTIPLY:
|
||||
result << "GPU_BLEND_MULTIPLY";
|
||||
break;
|
||||
case GPU_BLEND_SUBTRACT:
|
||||
result << "GPU_BLEND_SUBTRACT";
|
||||
break;
|
||||
case GPU_BLEND_INVERT:
|
||||
result << "GPU_BLEND_INVERT";
|
||||
break;
|
||||
case GPU_BLEND_MIN:
|
||||
result << "GPU_BLEND_MIN";
|
||||
break;
|
||||
case GPU_BLEND_MAX:
|
||||
result << "GPU_BLEND_MAX";
|
||||
break;
|
||||
case GPU_BLEND_OIT:
|
||||
result << "GPU_BLEND_OIT";
|
||||
break;
|
||||
case GPU_BLEND_BACKGROUND:
|
||||
result << "GPU_BLEND_BACKGROUND";
|
||||
break;
|
||||
case GPU_BLEND_CUSTOM:
|
||||
result << "GPU_BLEND_CUSTOM";
|
||||
break;
|
||||
case GPU_BLEND_ALPHA_UNDER_PREMUL:
|
||||
result << "GPU_BLEND_ALPHA_UNDER_PREMUL";
|
||||
break;
|
||||
case GPU_BLEND_OVERLAY_MASK_FROM_ALPHA:
|
||||
result << "GPU_BLEND_OVERLAY_MASK_FROM_ALPHA";
|
||||
break;
|
||||
case GPU_BLEND_TRANSPARENCY:
|
||||
result << "GPU_BLEND_TRANSPARENCY";
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
/* Culling test */
|
||||
result << ",\n ";
|
||||
switch (shaders.state.culling_test) {
|
||||
case GPU_CULL_NONE:
|
||||
result << "GPU_CULL_NONE";
|
||||
break;
|
||||
case GPU_CULL_FRONT:
|
||||
result << "GPU_CULL_FRONT";
|
||||
break;
|
||||
case GPU_CULL_BACK:
|
||||
result << "GPU_CULL_BACK";
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
/* Depth test */
|
||||
result << ",\n ";
|
||||
switch (shaders.state.depth_test) {
|
||||
case GPU_DEPTH_NONE:
|
||||
result << "GPU_DEPTH_NONE";
|
||||
break;
|
||||
case GPU_DEPTH_LESS:
|
||||
result << "GPU_DEPTH_LESS";
|
||||
break;
|
||||
case GPU_DEPTH_LESS_EQUAL:
|
||||
result << "GPU_DEPTH_LESS_EQUAL";
|
||||
break;
|
||||
case GPU_DEPTH_EQUAL:
|
||||
result << "GPU_DEPTH_EQUAL";
|
||||
break;
|
||||
case GPU_DEPTH_GREATER:
|
||||
result << "GPU_DEPTH_GREATER";
|
||||
break;
|
||||
case GPU_DEPTH_GREATER_EQUAL:
|
||||
result << "GPU_DEPTH_GREATER_EQUAL";
|
||||
break;
|
||||
case GPU_DEPTH_ALWAYS:
|
||||
result << "GPU_DEPTH_ALWAYS";
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
/* Stencil test */
|
||||
result << ",\n ";
|
||||
switch (shaders.state.stencil_test) {
|
||||
case GPU_STENCIL_NONE:
|
||||
result << "GPU_STENCIL_NONE";
|
||||
break;
|
||||
case GPU_STENCIL_ALWAYS:
|
||||
result << "GPU_STENCIL_ALWAYS";
|
||||
break;
|
||||
case GPU_STENCIL_EQUAL:
|
||||
result << "GPU_STENCIL_EQUAL";
|
||||
break;
|
||||
case GPU_STENCIL_NEQUAL:
|
||||
result << "GPU_STENCIL_NEQUAL";
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
/* Stencil operation */
|
||||
result << ",\n ";
|
||||
switch (shaders.state.stencil_op) {
|
||||
case GPU_STENCIL_OP_NONE:
|
||||
result << "GPU_STENCIL_OP_NONE";
|
||||
break;
|
||||
case GPU_STENCIL_OP_REPLACE:
|
||||
result << "GPU_STENCIL_OP_REPLACE";
|
||||
break;
|
||||
case GPU_STENCIL_OP_COUNT_DEPTH_PASS:
|
||||
result << "GPU_STENCIL_OP_COUNT_DEPTH_PASS";
|
||||
break;
|
||||
case GPU_STENCIL_OP_COUNT_DEPTH_FAIL:
|
||||
result << "GPU_STENCIL_OP_COUNT_DEPTH_FAIL";
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
/* Provoking vertex */
|
||||
result << ",\n ";
|
||||
switch (shaders.state.provoking_vert) {
|
||||
case GPU_VERTEX_FIRST:
|
||||
result << "GPU_VERTEX_FIRST";
|
||||
break;
|
||||
case GPU_VERTEX_LAST:
|
||||
result << "GPU_VERTEX_LAST";
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
result << ")\n";
|
||||
/* viewports */
|
||||
result << " .viewports(" << shaders.viewport_count << ")\n";
|
||||
/* Depth format */
|
||||
if (fragment_out.depth_attachment_format != VK_FORMAT_UNDEFINED) {
|
||||
result << " .depth_format(gpu::TextureTargetFormat::"
|
||||
<< to_gpu_format_string(fragment_out.depth_attachment_format) << ")\n";
|
||||
}
|
||||
/* Stencil format */
|
||||
if (fragment_out.stencil_attachment_format != VK_FORMAT_UNDEFINED) {
|
||||
result << " .stencil_format(gpu::TextureTargetFormat::"
|
||||
<< to_gpu_format_string(fragment_out.stencil_attachment_format) << ")\n";
|
||||
}
|
||||
/* Color formats */
|
||||
for (const VkFormat format : fragment_out.color_attachment_formats) {
|
||||
result << " .color_format(gpu::TextureTargetFormat::" << to_gpu_format_string(format)
|
||||
<< ")\n";
|
||||
}
|
||||
result << ";";
|
||||
return result.str();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Vertex input library
|
||||
* \{ */
|
||||
|
||||
VkPipeline VKPipelinePool::get_or_create_vertex_input_lib(
|
||||
const VKGraphicsInfo::VertexIn &vertex_input_info)
|
||||
{
|
||||
bool created = false;
|
||||
return vertex_input_libs_.get_or_create(
|
||||
vertex_input_info, vk_pipeline_cache_static_, VK_NULL_HANDLE, "VertexInLib", created);
|
||||
}
|
||||
|
||||
template<>
|
||||
VkPipeline VKPipelineMap<VKGraphicsInfo::VertexIn>::create(
|
||||
const VKGraphicsInfo::VertexIn &vertex_input_info,
|
||||
VkPipelineCache vk_pipeline_cache,
|
||||
VkPipeline vk_pipeline_base,
|
||||
StringRefNull name)
|
||||
{
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
VKGraphicsPipelineCreateInfoBuilder builder;
|
||||
builder.build_vertex_input_lib(device, vertex_input_info, vk_pipeline_base);
|
||||
|
||||
/* Build pipeline. */
|
||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
double start_time = BLI_time_now_seconds();
|
||||
vkCreateGraphicsPipelines(device.vk_handle(),
|
||||
vk_pipeline_cache,
|
||||
1,
|
||||
&builder.vk_graphics_pipeline_create_info,
|
||||
nullptr,
|
||||
&pipeline);
|
||||
double end_time = BLI_time_now_seconds();
|
||||
debug::object_label(pipeline, name);
|
||||
CLOG_TRACE(&LOG, "Compiled vertex input library in %fms ", (end_time - start_time) * 1000.0);
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Shaders library
|
||||
* \{ */
|
||||
|
||||
VkPipeline VKPipelinePool::get_or_create_shaders_lib(const VKGraphicsInfo::Shaders &shaders_info)
|
||||
{
|
||||
bool created = false;
|
||||
return shaders_libs_.get_or_create(
|
||||
shaders_info, vk_pipeline_cache_non_static_, VK_NULL_HANDLE, "ShadersLib", created);
|
||||
}
|
||||
|
||||
template<>
|
||||
VkPipeline VKPipelineMap<VKGraphicsInfo::Shaders>::create(
|
||||
const VKGraphicsInfo::Shaders &shaders_info,
|
||||
VkPipelineCache vk_pipeline_cache,
|
||||
VkPipeline vk_pipeline_base,
|
||||
StringRefNull name)
|
||||
{
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
const VKExtensions &extensions = device.extensions_get();
|
||||
VKGraphicsPipelineCreateInfoBuilder builder;
|
||||
builder.build_shaders_lib(shaders_info, extensions, vk_pipeline_base);
|
||||
|
||||
/* Build pipeline. */
|
||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
double start_time = BLI_time_now_seconds();
|
||||
vkCreateGraphicsPipelines(device.vk_handle(),
|
||||
vk_pipeline_cache,
|
||||
1,
|
||||
&builder.vk_graphics_pipeline_create_info,
|
||||
nullptr,
|
||||
&pipeline);
|
||||
double end_time = BLI_time_now_seconds();
|
||||
debug::object_label(pipeline, name);
|
||||
CLOG_TRACE(&LOG, "Compiled shaders library in %fms ", (end_time - start_time) * 1000.0);
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Fragment output library
|
||||
* \{ */
|
||||
|
||||
VkPipeline VKPipelinePool::get_or_create_fragment_output_lib(
|
||||
const VKGraphicsInfo::FragmentOut &fragment_output_info)
|
||||
{
|
||||
bool created = false;
|
||||
return fragment_output_libs_.get_or_create(
|
||||
fragment_output_info, vk_pipeline_cache_static_, VK_NULL_HANDLE, "FragmentOutLib", created);
|
||||
}
|
||||
|
||||
template<>
|
||||
VkPipeline VKPipelineMap<VKGraphicsInfo::FragmentOut>::create(
|
||||
const VKGraphicsInfo::FragmentOut &fragment_output_info,
|
||||
VkPipelineCache vk_pipeline_cache,
|
||||
VkPipeline vk_pipeline_base,
|
||||
StringRefNull name)
|
||||
{
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
const VKExtensions &extensions = device.extensions_get();
|
||||
VKGraphicsPipelineCreateInfoBuilder builder;
|
||||
builder.build_fragment_output_lib(fragment_output_info, extensions, vk_pipeline_base);
|
||||
|
||||
/* Build pipeline. */
|
||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
double start_time = BLI_time_now_seconds();
|
||||
vkCreateGraphicsPipelines(device.vk_handle(),
|
||||
vk_pipeline_cache,
|
||||
1,
|
||||
&builder.vk_graphics_pipeline_create_info,
|
||||
nullptr,
|
||||
&pipeline);
|
||||
double end_time = BLI_time_now_seconds();
|
||||
debug::object_label(pipeline, name);
|
||||
CLOG_TRACE(&LOG, "Compiled fragment output library in %fms ", (end_time - start_time) * 1000.0);
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
void VKPipelinePool::discard(VKDiscardPool &discard_pool, VkPipelineLayout vk_pipeline_layout)
|
||||
{
|
||||
graphics_.discard(discard_pool, vk_pipeline_layout);
|
||||
compute_.discard(discard_pool, vk_pipeline_layout);
|
||||
shaders_libs_.discard(discard_pool, vk_pipeline_layout);
|
||||
/* vertex_input_libs_ and fragment_output_libs_ are NOT dependent on vk_pipeline_layout. */
|
||||
}
|
||||
|
||||
void VKPipelinePool::free_data()
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
const VkDevice vk_device = device.vk_handle();
|
||||
|
||||
graphics_.free_data(vk_device);
|
||||
compute_.free_data(vk_device);
|
||||
vertex_input_libs_.free_data(vk_device);
|
||||
shaders_libs_.free_data(vk_device);
|
||||
fragment_output_libs_.free_data(vk_device);
|
||||
|
||||
vkDestroyPipelineCache(device.vk_handle(), vk_pipeline_cache_static_, nullptr);
|
||||
vkDestroyPipelineCache(device.vk_handle(), vk_pipeline_cache_non_static_, nullptr);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Persistent cache
|
||||
* \{ */
|
||||
|
||||
#ifdef WITH_BUILDINFO
|
||||
struct VKPipelineCachePrefixHeader {
|
||||
/* `BC` stands for "Blender Cache" + 2 bytes for file versioning. */
|
||||
uint32_t magic = 0xBC00;
|
||||
uint32_t blender_version = BLENDER_VERSION;
|
||||
uint32_t blender_version_patch = BLENDER_VERSION_PATCH;
|
||||
char commit_hash[8];
|
||||
uint32_t data_size;
|
||||
uint32_t vendor_id;
|
||||
uint32_t device_id;
|
||||
uint32_t driver_version;
|
||||
uint8_t pipeline_cache_uuid[VK_UUID_SIZE];
|
||||
|
||||
VKPipelineCachePrefixHeader()
|
||||
{
|
||||
const VKDevice &device = VKBackend::get().device;
|
||||
data_size = 0;
|
||||
const VkPhysicalDeviceProperties &properties = device.physical_device_properties_get();
|
||||
vendor_id = properties.vendorID;
|
||||
device_id = properties.deviceID;
|
||||
driver_version = properties.driverVersion;
|
||||
memcpy(&pipeline_cache_uuid, &properties.pipelineCacheUUID, VK_UUID_SIZE);
|
||||
|
||||
memset(commit_hash, 0, sizeof(commit_hash));
|
||||
STRNCPY(commit_hash, build_hash);
|
||||
}
|
||||
};
|
||||
|
||||
static std::string pipeline_cache_filepath_get()
|
||||
{
|
||||
static char tmp_dir_buffer[1024];
|
||||
BKE_appdir_folder_caches(tmp_dir_buffer, sizeof(tmp_dir_buffer));
|
||||
|
||||
std::string cache_dir = std::string(tmp_dir_buffer) + "vk-pipeline-cache" + SEP_STR;
|
||||
BLI_dir_create_recursive(cache_dir.c_str());
|
||||
std::string cache_file = cache_dir + "static.bin";
|
||||
return cache_file;
|
||||
}
|
||||
#endif
|
||||
|
||||
void VKPipelinePool::read_from_disk()
|
||||
{
|
||||
#ifdef WITH_BUILDINFO
|
||||
/* Don't read the shader cache when GPU debugging is enabled. When enabled we use different
|
||||
* shaders and compilation settings. Previous generated pipelines will not be used. */
|
||||
if (bool(G.debug & G_DEBUG_GPU)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string cache_file = pipeline_cache_filepath_get();
|
||||
if (!BLI_exists(cache_file.c_str())) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Prevent old cache files from being deleted if they're still being used. */
|
||||
BLI_file_touch(cache_file.c_str());
|
||||
/* Read cached binary. */
|
||||
fstream file(cache_file, std::ios::binary | std::ios::in | std::ios::ate);
|
||||
std::streamsize data_size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
Vector<char> buffer(data_size);
|
||||
file.read(buffer.data(), data_size);
|
||||
file.close();
|
||||
|
||||
/* Validate the prefix header. */
|
||||
VKPipelineCachePrefixHeader prefix;
|
||||
VKPipelineCachePrefixHeader &read_prefix = *reinterpret_cast<VKPipelineCachePrefixHeader *>(
|
||||
buffer.data());
|
||||
prefix.data_size = read_prefix.data_size;
|
||||
if (memcmp(&read_prefix, &prefix, sizeof(VKPipelineCachePrefixHeader)) != 0) {
|
||||
/* Headers are different, most likely the cache will not work and potentially crash the driver.
|
||||
* [https://medium.com/@zeuxcg/creating-a-robust-pipeline-cache-with-vulkan-961d09416cda]
|
||||
*/
|
||||
CLOG_INFO(&LOG,
|
||||
"Pipeline cache on disk [%s] is ignored as it was written by a different driver or "
|
||||
"Blender version. Cache will be overwritten when exiting.",
|
||||
cache_file.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
CLOG_INFO(&LOG, "Initialize static pipeline cache from disk [%s].", cache_file.c_str());
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
VkPipelineCacheCreateInfo create_info = {};
|
||||
create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
|
||||
create_info.initialDataSize = read_prefix.data_size;
|
||||
create_info.pInitialData = buffer.data() + sizeof(VKPipelineCachePrefixHeader);
|
||||
VkPipelineCache vk_pipeline_cache = VK_NULL_HANDLE;
|
||||
vkCreatePipelineCache(device.vk_handle(), &create_info, nullptr, &vk_pipeline_cache);
|
||||
|
||||
vkMergePipelineCaches(device.vk_handle(), vk_pipeline_cache_static_, 1, &vk_pipeline_cache);
|
||||
vkDestroyPipelineCache(device.vk_handle(), vk_pipeline_cache, nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void VKPipelinePool::write_to_disk()
|
||||
{
|
||||
#ifdef WITH_BUILDINFO
|
||||
/* Don't write the pipeline cache when GPU debugging is enabled. When enabled we use different
|
||||
* shaders and compilation settings. Writing them to disk will clutter the pipeline cache. */
|
||||
if (bool(G.debug & G_DEBUG_GPU)) {
|
||||
return;
|
||||
}
|
||||
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
size_t data_size;
|
||||
vkGetPipelineCacheData(device.vk_handle(), vk_pipeline_cache_static_, &data_size, nullptr);
|
||||
Vector<char> buffer(data_size);
|
||||
vkGetPipelineCacheData(device.vk_handle(), vk_pipeline_cache_static_, &data_size, buffer.data());
|
||||
|
||||
std::string cache_file = pipeline_cache_filepath_get();
|
||||
CLOG_INFO(&LOG, "Writing static pipeline cache to disk [%s].", cache_file.c_str());
|
||||
|
||||
fstream file(cache_file, std::ios::binary | std::ios::out);
|
||||
|
||||
VKPipelineCachePrefixHeader header;
|
||||
header.data_size = data_size;
|
||||
file.write(reinterpret_cast<char *>(&header), sizeof(VKPipelineCachePrefixHeader));
|
||||
file.write(buffer.data(), data_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
488
blender-5.2.0/source/blender/gpu/vulkan/vk_pipeline_pool.hh
Normal file
488
blender-5.2.0/source/blender/gpu/vulkan/vk_pipeline_pool.hh
Normal file
@@ -0,0 +1,488 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "xxhash.h"
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_mutex.hh"
|
||||
#include "BLI_utility_mixins.hh"
|
||||
|
||||
#include "gpu_state_private.hh"
|
||||
|
||||
#include "vk_resource_pool.hh"
|
||||
#include "vk_vertex_attribute_object.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
class VKDevice;
|
||||
|
||||
/**
|
||||
* Struct containing key information to identify a compute pipeline.
|
||||
*/
|
||||
struct VKComputeInfo {
|
||||
VkShaderModule vk_shader_module;
|
||||
VkPipelineLayout vk_pipeline_layout;
|
||||
Vector<shader::SpecializationConstant::Value> specialization_constants;
|
||||
|
||||
bool operator==(const VKComputeInfo &other) const
|
||||
{
|
||||
return vk_shader_module == other.vk_shader_module &&
|
||||
vk_pipeline_layout == other.vk_pipeline_layout &&
|
||||
specialization_constants == other.specialization_constants;
|
||||
};
|
||||
|
||||
uint64_t hash() const
|
||||
{
|
||||
uint64_t hash = uint64_t(vk_shader_module);
|
||||
hash = hash * 33 ^ uint64_t(vk_pipeline_layout);
|
||||
hash = hash * 33 ^ specialization_constants.hash();
|
||||
return hash;
|
||||
}
|
||||
|
||||
VkPipelineLayout vk_pipeline_layout_get() const
|
||||
{
|
||||
return vk_pipeline_layout;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Struct containing key information to identify graphics pipelines.
|
||||
*
|
||||
* The struct has already been separated into 4 stages to be compatible with
|
||||
* `VK_EXT_graphics_pipeline_library` being vertex_in, pre_rasterization, fragment_shader and
|
||||
* fragment_out.
|
||||
*/
|
||||
struct VKGraphicsInfo {
|
||||
struct VertexIn {
|
||||
VkPrimitiveTopology vk_topology;
|
||||
VKVertexInputDescriptionPool::Key vertex_input_key;
|
||||
|
||||
bool operator==(const VertexIn &other) const
|
||||
{
|
||||
return vk_topology == other.vk_topology && vertex_input_key == other.vertex_input_key;
|
||||
}
|
||||
|
||||
uint64_t hash() const
|
||||
{
|
||||
uint64_t hash = uint64_t(vk_topology);
|
||||
hash = hash * 33 ^ vertex_input_key;
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
struct Shaders {
|
||||
VkPipelineLayout vk_pipeline_layout;
|
||||
VkShaderModule vk_vertex_module;
|
||||
VkShaderModule vk_geometry_module;
|
||||
VkShaderModule vk_fragment_module;
|
||||
VkPrimitiveTopology vk_topology;
|
||||
uint32_t viewport_count;
|
||||
GPUState state;
|
||||
Vector<shader::SpecializationConstant::Value> specialization_constants;
|
||||
bool has_depth;
|
||||
bool has_stencil;
|
||||
uint32_t max_input_attachment_index = 0;
|
||||
|
||||
bool operator==(const Shaders &other) const
|
||||
{
|
||||
return vk_vertex_module == other.vk_vertex_module &&
|
||||
vk_geometry_module == other.vk_geometry_module &&
|
||||
vk_fragment_module == other.vk_fragment_module &&
|
||||
vk_pipeline_layout == other.vk_pipeline_layout && vk_topology == other.vk_topology &&
|
||||
viewport_count == other.viewport_count && state == other.state &&
|
||||
specialization_constants == other.specialization_constants &&
|
||||
has_depth == other.has_depth && has_stencil == other.has_stencil &&
|
||||
max_input_attachment_index == other.max_input_attachment_index;
|
||||
}
|
||||
|
||||
uint64_t hash() const
|
||||
{
|
||||
uint64_t hash = viewport_count;
|
||||
hash = hash * 33 ^ uint64_t(vk_vertex_module);
|
||||
hash = hash * 33 ^ uint64_t(vk_geometry_module);
|
||||
hash = hash * 33 ^ uint64_t(vk_fragment_module);
|
||||
hash = hash * 33 ^ uint64_t(vk_pipeline_layout);
|
||||
hash = hash * 33 ^ uint64_t(vk_topology);
|
||||
hash = hash * 33 ^ state.data;
|
||||
hash = hash * 33 ^ specialization_constants.hash();
|
||||
hash = hash * 33 ^ (uint64_t(has_depth) << 1 | uint64_t(has_stencil));
|
||||
hash = hash * 33 ^ uint64_t(max_input_attachment_index);
|
||||
return hash;
|
||||
}
|
||||
|
||||
VkPipelineLayout vk_pipeline_layout_get() const
|
||||
{
|
||||
return vk_pipeline_layout;
|
||||
}
|
||||
};
|
||||
struct FragmentOut {
|
||||
GPUState state;
|
||||
|
||||
/* Dynamic rendering */
|
||||
VkFormat depth_attachment_format;
|
||||
VkFormat stencil_attachment_format;
|
||||
Vector<VkFormat> color_attachment_formats;
|
||||
|
||||
bool operator==(const FragmentOut &other) const
|
||||
{
|
||||
#if 0
|
||||
return hash() == other.hash();
|
||||
#else
|
||||
if (state != other.state || depth_attachment_format != other.depth_attachment_format ||
|
||||
stencil_attachment_format != other.stencil_attachment_format ||
|
||||
color_attachment_formats != other.color_attachment_formats)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t hash() const
|
||||
{
|
||||
uint64_t hash = uint64_t(depth_attachment_format);
|
||||
hash = hash * 33 ^ uint64_t(stencil_attachment_format);
|
||||
hash = hash * 33 ^ XXH3_64bits(color_attachment_formats.data(),
|
||||
color_attachment_formats.size() * sizeof(VkFormat));
|
||||
hash = hash * 33 ^ state.data;
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
VertexIn vertex_in;
|
||||
Shaders shaders;
|
||||
FragmentOut fragment_out;
|
||||
|
||||
bool operator==(const VKGraphicsInfo &other) const
|
||||
{
|
||||
return vertex_in == other.vertex_in && shaders == other.shaders &&
|
||||
fragment_out == other.fragment_out;
|
||||
};
|
||||
uint64_t hash() const
|
||||
{
|
||||
uint64_t hash = 0;
|
||||
hash = hash * 33 ^ vertex_in.hash();
|
||||
hash = hash * 33 ^ shaders.hash();
|
||||
hash = hash * 33 ^ fragment_out.hash();
|
||||
return hash;
|
||||
}
|
||||
|
||||
VkPipelineLayout vk_pipeline_layout_get() const
|
||||
{
|
||||
return shaders.vk_pipeline_layout;
|
||||
}
|
||||
|
||||
/** Generate a code snippet for configuring ShaderCreateInfo pipeline. */
|
||||
std::string pipeline_info_source() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Thread safe map to pipelines of a certain type (graphics or compute).
|
||||
*/
|
||||
template<typename PipelineInfo> class VKPipelineMap {
|
||||
Map<PipelineInfo, VkPipeline> pipelines_;
|
||||
Mutex mutex_;
|
||||
std::condition_variable_any new_pipeline_added_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Get an existing or create a new pipeline based on the provided PipelineInfo.
|
||||
*
|
||||
* When vk_pipeline_base is a valid pipeline handle, the pipeline base will be used to speed up
|
||||
* pipeline creation process.
|
||||
*
|
||||
* \param pipeline_info: Description of the pipeline to compile.
|
||||
* \param vk_pipeline_cache: Pipeline cache to use.
|
||||
* \param vk_pipeline_base: An already existing pipeline that can be used as a base when
|
||||
* compiling the pipeline.
|
||||
* \param name: Name to give as a debug label when creating a pipeline.
|
||||
* \returns The handle of the compiled pipeline.
|
||||
*/
|
||||
VkPipeline get_or_create(const PipelineInfo &pipeline_info,
|
||||
VkPipelineCache vk_pipeline_cache,
|
||||
VkPipeline vk_pipeline_base,
|
||||
StringRefNull name,
|
||||
bool &r_created)
|
||||
{
|
||||
bool do_wait_for_pipeline = false;
|
||||
bool do_compile_pipeline = false;
|
||||
r_created = false;
|
||||
{
|
||||
std::scoped_lock lock(mutex_);
|
||||
const VkPipeline *found_pipeline = pipelines_.lookup_ptr(pipeline_info);
|
||||
if (found_pipeline) {
|
||||
if (*found_pipeline == VK_NULL_HANDLE) {
|
||||
do_wait_for_pipeline = true;
|
||||
}
|
||||
else {
|
||||
/* Early exit: pipeline_info found and has a valid pipeline. */
|
||||
return *found_pipeline;
|
||||
}
|
||||
}
|
||||
else {
|
||||
pipelines_.add_new(pipeline_info, VK_NULL_HANDLE);
|
||||
do_compile_pipeline = true;
|
||||
}
|
||||
}
|
||||
if (do_wait_for_pipeline) {
|
||||
return wait_for_completion(pipeline_info);
|
||||
}
|
||||
if (do_compile_pipeline) {
|
||||
VkPipeline pipeline = create(pipeline_info, vk_pipeline_cache, vk_pipeline_base, name);
|
||||
r_created = true;
|
||||
/* Store result in the compute pipelines map. */
|
||||
{
|
||||
std::scoped_lock lock(mutex_);
|
||||
VkPipeline &pipeline_item = pipelines_.lookup(pipeline_info);
|
||||
pipeline_item = pipeline;
|
||||
}
|
||||
/* Notify other threads that a new pipeline is available. */
|
||||
{
|
||||
new_pipeline_added_.notify_all();
|
||||
}
|
||||
return pipeline;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discard all pipelines associated with the given layout
|
||||
*/
|
||||
void discard(VKDiscardPool &discard_pool, VkPipelineLayout vk_pipeline_layout)
|
||||
{
|
||||
std::scoped_lock lock(mutex_);
|
||||
pipelines_.remove_if([&](auto item) {
|
||||
if (item.key.vk_pipeline_layout_get() == vk_pipeline_layout) {
|
||||
discard_pool.discard_pipeline(item.value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Free all data.
|
||||
*
|
||||
* \note Handle is passed to fix recursive inclusion of vk_device.hh
|
||||
*/
|
||||
void free_data(VkDevice vk_device)
|
||||
{
|
||||
std::scoped_lock lock(mutex_);
|
||||
for (VkPipeline &vk_pipeline : pipelines_.values()) {
|
||||
vkDestroyPipeline(vk_device, vk_pipeline, nullptr);
|
||||
}
|
||||
pipelines_.clear();
|
||||
}
|
||||
/**
|
||||
* Number of pipelines stored inside the instance.
|
||||
*/
|
||||
uint64_t size() const
|
||||
{
|
||||
return pipelines_.size();
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Create a new pipeline based on the provided PipelineInfo.
|
||||
*
|
||||
* When vk_pipeline_base is a valid pipeline handle, the pipeline base will be used to speed up
|
||||
* pipeline creation process.
|
||||
*
|
||||
* \param pipeline_info: Description of the pipeline to compile.
|
||||
* \param vk_pipeline_cache: Pipeline cache to use.
|
||||
* \param vk_pipeline_base: An already existing pipeline that can be used as a base when
|
||||
* compiling the pipeline.
|
||||
* \param name: Name to give as a debug label when creating a pipeline.
|
||||
* \returns The handle of the compiled pipeline.
|
||||
*/
|
||||
VkPipeline create(const PipelineInfo &pipeline_info,
|
||||
VkPipelineCache vk_pipeline_cache,
|
||||
VkPipeline vk_pipeline_base,
|
||||
StringRefNull name);
|
||||
/**
|
||||
* \brief wait for another thread to complete the same pipeline info.
|
||||
*
|
||||
* \param pipeline_info: Description of the pipeline to wait for.
|
||||
*/
|
||||
VkPipeline wait_for_completion(const PipelineInfo &pipeline_info)
|
||||
{
|
||||
std::unique_lock<Mutex> lock(mutex_);
|
||||
const VkPipeline *pipeline = nullptr;
|
||||
new_pipeline_added_.wait(lock, [&]() {
|
||||
pipeline = pipelines_.lookup_ptr(pipeline_info);
|
||||
BLI_assert(pipeline != nullptr);
|
||||
return *pipeline != VK_NULL_HANDLE;
|
||||
});
|
||||
return *pipeline;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Pipelines are lazy initialized and same pipelines should share their handle.
|
||||
*
|
||||
* A requirement of our render graph implementation is that changes of the pipeline can be detected
|
||||
* based on the VkPipeline handle. We only want to rebind the pipeline handle when the handle
|
||||
* actually changes. This improves performance (especially on NVIDIA) devices where pipeline binds
|
||||
* are known to be costly.
|
||||
*
|
||||
* Especially for graphics pipelines many parameters are needed to compile a graphics pipeline.
|
||||
* Some of the information would be boiler plating; or at least from Blender point of view. To
|
||||
* improve lookup performance we use a slimmed down version of the pipeline create info structs.
|
||||
* The idea is that we can limit the required data because we control which data we actually use,
|
||||
* removing the boiler plating and improve hashing performance better than the VkPipelineCache can
|
||||
* give us.
|
||||
*
|
||||
* TODO: Extensions like `VK_EXT_graphics_pipeline_library` should fit in this class and ease the
|
||||
* development for graphics pipelines. Geometry in and frame-buffer out could be cached separately
|
||||
* to reduce pipeline creation times. Most likely we will add support when we work on graphic
|
||||
* pipelines. Recent drivers all support this extension, but the full coverage is still <20%. A
|
||||
* fallback should made available for older drivers is required.
|
||||
*
|
||||
* TODO: Creation of shader modules needs to be revisited.
|
||||
* VK_EXT_graphics_pipeline_library deprecates the use of shader modules and use the `spriv` bin
|
||||
* directly. In this extension the pipeline and shader module are the same. The current approach
|
||||
* should also be revisited as the latest drivers all implement pipeline libraries, but there are
|
||||
* some platforms where the driver isn't been updated and doesn't implement this extension. In
|
||||
* that case shader modules should still be used.
|
||||
*/
|
||||
class VKPipelinePool : public NonCopyable {
|
||||
friend class VKDevice;
|
||||
|
||||
private:
|
||||
VkPipelineCache vk_pipeline_cache_static_;
|
||||
VkPipelineCache vk_pipeline_cache_non_static_;
|
||||
|
||||
VKPipelineMap<VKComputeInfo> compute_;
|
||||
VKPipelineMap<VKGraphicsInfo> graphics_;
|
||||
|
||||
/* Cached graphics libraries (VK_EXT_graphics_pipeline_library)
|
||||
* When using graphics pipeline libraries the shaders is compiled in 3 chunks, vertex input,
|
||||
* shaders and fragment output. */
|
||||
VKPipelineMap<VKGraphicsInfo::VertexIn> vertex_input_libs_;
|
||||
VKPipelineMap<VKGraphicsInfo::Shaders> shaders_libs_;
|
||||
VKPipelineMap<VKGraphicsInfo::FragmentOut> fragment_output_libs_;
|
||||
|
||||
public:
|
||||
void init();
|
||||
|
||||
/**
|
||||
* Get an existing or create a new compute pipeline based on the provided ComputeInfo.
|
||||
*
|
||||
* When vk_pipeline_base is a valid pipeline handle, the pipeline base will be used to speed up
|
||||
* pipeline creation process.
|
||||
*
|
||||
* \param compute_info: Description of the pipeline to compile.
|
||||
* \param is_static_shader: Pipelines from static shaders are cached between Blender sessions.
|
||||
* Pipelines from dynamic shaders are only cached for the duration of a
|
||||
* single Blender session.
|
||||
* \param vk_pipeline_base: An already existing pipeline that can be used as a base when
|
||||
* compiling the pipeline.
|
||||
* \param name: Name to give as a debug label when creating a pipeline.
|
||||
* \returns The handle of the compiled pipeline.
|
||||
*/
|
||||
VkPipeline get_or_create_compute_pipeline(const VKComputeInfo &compute_info,
|
||||
bool is_static_shader,
|
||||
VkPipeline vk_pipeline_base,
|
||||
StringRefNull name);
|
||||
|
||||
/**
|
||||
* Get an existing or create a new graphics pipeline based on the provided GraphicsInfo.
|
||||
*
|
||||
* When vk_pipeline_base is a valid pipeline handle, the pipeline base will be used to speed up
|
||||
* pipeline creation process.
|
||||
*
|
||||
* \param graphics_info: Description of the pipeline to compile.
|
||||
* \param is_static_shader: Pipelines from static shaders are cached between Blender sessions.
|
||||
* Pipelines from dynamic shaders are only cached for the duration of
|
||||
* a single Blender session.
|
||||
* \param vk_pipeline_base: An already existing pipeline that can be used as a base when
|
||||
* compiling the pipeline.
|
||||
* \param name: Name to give as a debug label when creating a pipeline.
|
||||
* \param r_created: Is set to true when this call has compiled a new pipeline. Otherwise
|
||||
* it is set to false.
|
||||
* \returns The handle of the compiled pipeline.
|
||||
*/
|
||||
VkPipeline get_or_create_graphics_pipeline(const VKGraphicsInfo &graphics_info,
|
||||
bool is_static_shader,
|
||||
VkPipeline vk_pipeline_base,
|
||||
StringRefNull name,
|
||||
bool &r_created);
|
||||
|
||||
/**
|
||||
* Get an existing or create a new vertex input library pipeline based on the provided info.
|
||||
*
|
||||
* \param vertex_input_info: Description of the pipeline to compile.
|
||||
* \returns The handle of the compiled pipeline.
|
||||
*/
|
||||
VkPipeline get_or_create_vertex_input_lib(const VKGraphicsInfo::VertexIn &vertex_input_info);
|
||||
|
||||
/**
|
||||
* Get an existing or create a new shaders library pipeline based on the provided info.
|
||||
*
|
||||
* \param shaders_info: Description of the pipeline to compile.
|
||||
* \returns The handle of the compiled pipeline.
|
||||
*/
|
||||
VkPipeline get_or_create_shaders_lib(const VKGraphicsInfo::Shaders &shaders_info);
|
||||
|
||||
/**
|
||||
* Get an existing or create a new fragment output library pipeline based on the provided info.
|
||||
*
|
||||
* \param fragment_output_info: Description of the pipeline to compile.
|
||||
* \returns The handle of the compiled pipeline.
|
||||
*/
|
||||
VkPipeline get_or_create_fragment_output_lib(
|
||||
const VKGraphicsInfo::FragmentOut &fragment_output_info);
|
||||
|
||||
/**
|
||||
* Discard all pipelines that uses the given pipeline_layout.
|
||||
*/
|
||||
void discard(VKDiscardPool &discard_pool, VkPipelineLayout vk_pipeline_layout);
|
||||
|
||||
/**
|
||||
* Destroy all created pipelines.
|
||||
*
|
||||
* Function is called just before the device is removed. This cannot be done in the destructor as
|
||||
* that would be called after the device is removed.
|
||||
*/
|
||||
void free_data();
|
||||
|
||||
/**
|
||||
* Read the static pipeline cache from cache file.
|
||||
*
|
||||
* Pipeline caches requires blender to be build with `WITH_BUILDINFO` enabled . Between commits
|
||||
* shader modules can change and shader module identifiers cannot be used. We use the build info
|
||||
* to check if the identifiers can be reused.
|
||||
*
|
||||
* Previous stored pipeline cache will not be read when G_DEBUG_GPU is enabled. In this case the
|
||||
* shader modules will be compiled with other settings and any cached pipeline will not be used
|
||||
* during this session.
|
||||
*
|
||||
* NOTE: When developing shaders we assume that `WITH_BUILDINFO` is turned off or `G_DEBUG_GPU`
|
||||
* flag is set.
|
||||
*/
|
||||
void read_from_disk();
|
||||
|
||||
/**
|
||||
* Store the static pipeline cache to disk.
|
||||
*
|
||||
* Pipeline caches requires blender to be build with `WITH_BUILDINFO` enabled . Between commits
|
||||
* shader modules can change and shader module identifiers cannot be used. We use the build info
|
||||
* to check if the identifiers can be reused.
|
||||
*
|
||||
* The cache will not be written when G_DEBUG_GPU is active. In this case the shader modules will
|
||||
* been generated with debug information and other compiler settings are used. This will clutter
|
||||
* the pipeline cache.
|
||||
*
|
||||
* NOTE: When developing shaders we assume that `WITH_BUILDINFO` is turned off or `G_DEBUG_GPU`
|
||||
* flag is set.
|
||||
*/
|
||||
void write_to_disk();
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
121
blender-5.2.0/source/blender/gpu/vulkan/vk_pixel_buffer.cc
Normal file
121
blender-5.2.0/source/blender/gpu/vulkan/vk_pixel_buffer.cc
Normal file
@@ -0,0 +1,121 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "vk_pixel_buffer.hh"
|
||||
|
||||
#include "vk_backend.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
static CLG_LogRef LOG = {"gpu.vulkan"};
|
||||
|
||||
VKPixelBuffer::VKPixelBuffer(size_t size) : PixelBuffer(size) {}
|
||||
|
||||
void VKPixelBuffer::create(bool memory_export)
|
||||
{
|
||||
/* Create on demand with or without memory export. When memory export is
|
||||
* enabled there is no host mapping. */
|
||||
if (buffer_initialized_ && buffer_memory_export_ == memory_export) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer_.is_allocated()) {
|
||||
buffer_.free();
|
||||
}
|
||||
|
||||
buffer_.create(size_,
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,
|
||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
|
||||
0.8f,
|
||||
memory_export,
|
||||
"PixelBuffer");
|
||||
|
||||
buffer_initialized_ = true;
|
||||
buffer_memory_export_ = memory_export;
|
||||
}
|
||||
|
||||
void *VKPixelBuffer::map()
|
||||
{
|
||||
/* Vulkan buffers are always mapped between allocation and freeing. */
|
||||
create(false);
|
||||
return buffer_.mapped_memory_get();
|
||||
}
|
||||
|
||||
void VKPixelBuffer::unmap()
|
||||
{
|
||||
/* Vulkan buffers are always mapped between allocation and freeing. */
|
||||
}
|
||||
|
||||
GPUPixelBufferNativeHandle VKPixelBuffer::get_native_handle()
|
||||
{
|
||||
VKDevice &device = VKBackend::get().device;
|
||||
|
||||
GPUPixelBufferNativeHandle native_handle;
|
||||
|
||||
/* Functionality supported? */
|
||||
if (!device.extensions_get().external_memory) {
|
||||
return native_handle;
|
||||
}
|
||||
|
||||
/* Create buffer. */
|
||||
create(true);
|
||||
|
||||
/* Get device memory. */
|
||||
size_t memory_size = 0;
|
||||
VkDeviceMemory memory = buffer_.export_memory_get(memory_size);
|
||||
if (memory == VK_NULL_HANDLE) {
|
||||
CLOG_ERROR(&LOG, "Failed to get device memory for Vulkan pixel buffer");
|
||||
return native_handle;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
/* Opaque Windows handle. */
|
||||
VkMemoryGetWin32HandleInfoKHR info = {};
|
||||
info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR;
|
||||
info.pNext = nullptr;
|
||||
info.memory = memory;
|
||||
info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
|
||||
|
||||
HANDLE handle = 0;
|
||||
if (device.functions.vkGetMemoryWin32Handle(device.vk_handle(), &info, &handle) != VK_SUCCESS) {
|
||||
CLOG_ERROR(&LOG, "Failed to get Windows handle for Vulkan pixel buffer");
|
||||
return native_handle;
|
||||
}
|
||||
|
||||
native_handle.handle = int64_t(handle);
|
||||
native_handle.size = memory_size;
|
||||
#else
|
||||
/* Opaque file descriptor. */
|
||||
VkMemoryGetFdInfoKHR info = {};
|
||||
info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR;
|
||||
info.pNext = nullptr;
|
||||
info.memory = memory;
|
||||
info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
|
||||
|
||||
int fd = -1;
|
||||
if (device.functions.vkGetMemoryFd(device.vk_handle(), &info, &fd) != VK_SUCCESS) {
|
||||
CLOG_ERROR(&LOG, "Failed to get file descriptor for Vulkan pixel buffer");
|
||||
return native_handle;
|
||||
}
|
||||
|
||||
native_handle.handle = int64_t(fd);
|
||||
native_handle.size = memory_size;
|
||||
#endif
|
||||
|
||||
return native_handle;
|
||||
}
|
||||
|
||||
size_t VKPixelBuffer::get_size()
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
43
blender-5.2.0/source/blender/gpu/vulkan/vk_pixel_buffer.hh
Normal file
43
blender-5.2.0/source/blender/gpu/vulkan/vk_pixel_buffer.hh
Normal file
@@ -0,0 +1,43 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_texture_private.hh"
|
||||
|
||||
#include "vk_buffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class VKPixelBuffer : public PixelBuffer {
|
||||
VKBuffer buffer_;
|
||||
bool buffer_initialized_ = false;
|
||||
bool buffer_memory_export_ = false;
|
||||
|
||||
public:
|
||||
VKPixelBuffer(size_t size);
|
||||
void *map() override;
|
||||
void unmap() override;
|
||||
GPUPixelBufferNativeHandle get_native_handle() override;
|
||||
size_t get_size() override;
|
||||
|
||||
VKBuffer &buffer_get()
|
||||
{
|
||||
return buffer_;
|
||||
}
|
||||
|
||||
protected:
|
||||
void create(bool memory_export);
|
||||
};
|
||||
|
||||
static inline VKPixelBuffer *unwrap(PixelBuffer *pixel_buffer)
|
||||
{
|
||||
return static_cast<VKPixelBuffer *>(pixel_buffer);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
164
blender-5.2.0/source/blender/gpu/vulkan/vk_push_constants.cc
Normal file
164
blender-5.2.0/source/blender/gpu/vulkan/vk_push_constants.cc
Normal file
@@ -0,0 +1,164 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "vk_push_constants.hh"
|
||||
#include "vk_backend.hh"
|
||||
#include "vk_buffer.hh"
|
||||
#include "vk_context.hh"
|
||||
#include "vk_memory_layout.hh"
|
||||
#include "vk_shader.hh"
|
||||
#include "vk_shader_interface.hh"
|
||||
#include "vk_storage_buffer.hh"
|
||||
#include "vk_uniform_buffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
template<typename LayoutT>
|
||||
static VKPushConstants::Layout::PushConstant init_constant(
|
||||
const shader::ShaderCreateInfo::PushConst &push_constant,
|
||||
const ShaderInput &shader_input,
|
||||
uint32_t *r_offset)
|
||||
{
|
||||
align<LayoutT>(push_constant.type, push_constant.array_size, r_offset);
|
||||
|
||||
VKPushConstants::Layout::PushConstant layout;
|
||||
layout.location = shader_input.location;
|
||||
layout.type = push_constant.type;
|
||||
layout.array_size = push_constant.array_size;
|
||||
layout.offset = *r_offset;
|
||||
layout.inner_row_padding = LayoutT::inner_row_padding(push_constant.type);
|
||||
|
||||
reserve<LayoutT>(push_constant.type, push_constant.array_size, r_offset);
|
||||
return layout;
|
||||
}
|
||||
|
||||
template<typename LayoutT>
|
||||
uint32_t struct_size(Span<shader::ShaderCreateInfo::PushConst> push_constants)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
for (const shader::ShaderCreateInfo::PushConst &push_constant : push_constants) {
|
||||
align<LayoutT>(push_constant.type, push_constant.array_size, &offset);
|
||||
reserve<LayoutT>(push_constant.type, push_constant.array_size, &offset);
|
||||
}
|
||||
|
||||
align_end_of_struct<LayoutT>(&offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
VKPushConstants::StorageType VKPushConstants::Layout::determine_storage_type(
|
||||
const shader::ShaderCreateInfo &info, const VKDevice &device)
|
||||
{
|
||||
if (info.push_constants_.is_empty()) {
|
||||
return StorageType::NONE;
|
||||
}
|
||||
|
||||
uint32_t max_push_constants_size =
|
||||
device.physical_device_properties_get().limits.maxPushConstantsSize;
|
||||
uint32_t size = struct_size<Std430>(info.push_constants_);
|
||||
return size <= max_push_constants_size ? STORAGE_TYPE_DEFAULT : STORAGE_TYPE_FALLBACK;
|
||||
}
|
||||
|
||||
template<typename LayoutT>
|
||||
void init_struct(const shader::ShaderCreateInfo &info,
|
||||
const VKShaderInterface &interface,
|
||||
Vector<VKPushConstants::Layout::PushConstant> &r_struct,
|
||||
uint32_t *r_offset)
|
||||
{
|
||||
for (const shader::ShaderCreateInfo::PushConst &push_constant : info.push_constants_) {
|
||||
const ShaderInput *shader_input = interface.uniform_get(push_constant.name.c_str());
|
||||
r_struct.append(init_constant<LayoutT>(push_constant, *shader_input, r_offset));
|
||||
}
|
||||
align_end_of_struct<Std140>(r_offset);
|
||||
}
|
||||
|
||||
void VKPushConstants::Layout::init(const shader::ShaderCreateInfo &info,
|
||||
const VKShaderInterface &interface,
|
||||
const StorageType storage_type,
|
||||
const VKDescriptorSet::Location location)
|
||||
{
|
||||
BLI_assert(push_constants.is_empty());
|
||||
storage_type_ = storage_type;
|
||||
|
||||
size_in_bytes_ = 0;
|
||||
if (storage_type == StorageType::BUFFER) {
|
||||
descriptor_set_location_ = location;
|
||||
init_struct<Std140>(info, interface, push_constants, &size_in_bytes_);
|
||||
}
|
||||
else {
|
||||
init_struct<Std430>(info, interface, push_constants, &size_in_bytes_);
|
||||
}
|
||||
}
|
||||
|
||||
const VKPushConstants::Layout::PushConstant *VKPushConstants::Layout::find(int32_t location) const
|
||||
{
|
||||
for (const PushConstant &push_constant : push_constants) {
|
||||
if (push_constant.location == location) {
|
||||
return &push_constant;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void VKPushConstants::Layout::debug_print() const
|
||||
{
|
||||
std::ostream &stream = std::cout;
|
||||
stream << "VKPushConstants::Layout::debug_print()\n";
|
||||
for (const PushConstant &push_constant : push_constants) {
|
||||
stream << " - location:" << push_constant.location;
|
||||
stream << ", offset:" << push_constant.offset;
|
||||
stream << ", array_size:" << push_constant.array_size;
|
||||
stream << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
VKPushConstants::VKPushConstants() = default;
|
||||
VKPushConstants::VKPushConstants(const Layout *layout) : layout_(layout)
|
||||
{
|
||||
data_ = MEM_new_uninitialized(layout->size_in_bytes(), __func__);
|
||||
if (G.debug & G_DEBUG_GPU) {
|
||||
memset(data_, 0xFD, layout->size_in_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
VKPushConstants::VKPushConstants(VKPushConstants &&other) : layout_(other.layout_)
|
||||
{
|
||||
data_ = other.data_;
|
||||
other.data_ = nullptr;
|
||||
}
|
||||
|
||||
VKPushConstants::~VKPushConstants()
|
||||
{
|
||||
if (data_ != nullptr) {
|
||||
MEM_delete_void(data_);
|
||||
data_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
VKPushConstants &VKPushConstants::operator=(VKPushConstants &&other)
|
||||
{
|
||||
layout_ = other.layout_;
|
||||
|
||||
data_ = other.data_;
|
||||
other.data_ = nullptr;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
VKBufferWithOffset VKPushConstants::update_uniform_buffer(VKContext &context)
|
||||
{
|
||||
BLI_assert(layout_->storage_type_get() == StorageType::BUFFER);
|
||||
BLI_assert(data_ != nullptr);
|
||||
|
||||
VKBufferWithOffset result =
|
||||
|
||||
context.push_constants_pool.append(
|
||||
Span<uint8_t>(static_cast<const uint8_t *>(data_), layout_->size_in_bytes()));
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
263
blender-5.2.0/source/blender/gpu/vulkan/vk_push_constants.hh
Normal file
263
blender-5.2.0/source/blender/gpu/vulkan/vk_push_constants.hh
Normal file
@@ -0,0 +1,263 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Push constants is a way to quickly provide a small amount of uniform data to shaders. It should
|
||||
* be much quicker than UBOs but a huge limitation is the size of data - spec requires 128 bytes to
|
||||
* be available for a push constant range. Hardware vendors may support more, but compared to other
|
||||
* means it is still very little (for example 256 bytes).
|
||||
*
|
||||
* Due to this size requirements we try to use push constants when it fits on the device. If it
|
||||
* doesn't fit we fall back to use an uniform buffer.
|
||||
*
|
||||
* Shader developers are responsible to fine-tune the performance of the shader. One way to do this
|
||||
* is to tailor what will be sent as a push constant to keep the push constants within the limits.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
#include "vk_buffer.hh"
|
||||
#include "vk_descriptor_set.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
class VKShaderInterface;
|
||||
class VKUniformBuffer;
|
||||
class VKContext;
|
||||
class VKDevice;
|
||||
|
||||
/**
|
||||
* Container to store push constants in a buffer.
|
||||
*
|
||||
* Can handle buffers with different memory layouts (std140/std430)
|
||||
* Which memory layout is used is based on the storage type.
|
||||
*
|
||||
* VKPushConstantsLayout only describes the buffer, an instance of this
|
||||
* class can handle setting/modifying/duplicating push constants.
|
||||
*
|
||||
* It should also keep track of the submissions in order to reuse the allocated
|
||||
* data.
|
||||
*/
|
||||
class VKPushConstants {
|
||||
friend class VKContext;
|
||||
|
||||
public:
|
||||
/** Different methods to store push constants. */
|
||||
enum class StorageType {
|
||||
/** Push constants aren't in use. */
|
||||
NONE,
|
||||
|
||||
/** Store push constants as regular vulkan push constants. */
|
||||
PUSH_CONSTANTS,
|
||||
|
||||
/**
|
||||
* Fallback when push constants doesn't meet the device requirements.
|
||||
*/
|
||||
BUFFER,
|
||||
};
|
||||
|
||||
/**
|
||||
* Describe the layout of the push constants and the storage type that should be used.
|
||||
*/
|
||||
struct Layout {
|
||||
static constexpr StorageType STORAGE_TYPE_DEFAULT = StorageType::PUSH_CONSTANTS;
|
||||
static constexpr StorageType STORAGE_TYPE_FALLBACK = StorageType::BUFFER;
|
||||
|
||||
struct PushConstant {
|
||||
/* Used as lookup based on ShaderInput. */
|
||||
int32_t location;
|
||||
|
||||
/** Offset in the push constant data (in bytes). */
|
||||
uint32_t offset;
|
||||
shader::Type type;
|
||||
int array_size;
|
||||
uint inner_row_padding;
|
||||
};
|
||||
|
||||
private:
|
||||
Vector<PushConstant> push_constants;
|
||||
uint32_t size_in_bytes_ = 0;
|
||||
StorageType storage_type_ = StorageType::NONE;
|
||||
/**
|
||||
* Binding index in the descriptor set when the push constants use an uniform buffer.
|
||||
*/
|
||||
VKDescriptorSet::Location descriptor_set_location_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Return the desired storage type that can fit the push constants of the given shader create
|
||||
* info, matching the limits of the given device.
|
||||
*
|
||||
* Returns:
|
||||
* - StorageType::NONE: No push constants are needed.
|
||||
* - StorageType::PUSH_CONSTANTS: Regular vulkan push constants can be used.
|
||||
* - StorageType::BUFFER: The push constants don't fit in the limits of the given
|
||||
* device. A buffer should be used as a fallback method.
|
||||
*/
|
||||
static StorageType determine_storage_type(const shader::ShaderCreateInfo &info,
|
||||
const VKDevice &device);
|
||||
|
||||
/**
|
||||
* Initialize the push constants of the given shader create info with the
|
||||
* binding location.
|
||||
*
|
||||
* interface: Uniform locations of the interface are used as lookup key.
|
||||
* storage_type: The type of storage for push constants to use.
|
||||
* location: When storage_type=StorageType::BUFFER this contains
|
||||
* the location in the descriptor set where the buffer can be
|
||||
* bound.
|
||||
*/
|
||||
void init(const shader::ShaderCreateInfo &info,
|
||||
const VKShaderInterface &interface,
|
||||
StorageType storage_type,
|
||||
VKDescriptorSet::Location location);
|
||||
|
||||
/**
|
||||
* Return the storage type that is used.
|
||||
*/
|
||||
StorageType storage_type_get() const
|
||||
{
|
||||
return storage_type_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the binding location for the buffer.
|
||||
*
|
||||
* Only valid when storage_type=StorageType::BUFFER.
|
||||
*/
|
||||
VKDescriptorSet::Location descriptor_set_location_get() const
|
||||
{
|
||||
return descriptor_set_location_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size needed to store the push constants.
|
||||
*/
|
||||
uint32_t size_in_bytes() const
|
||||
{
|
||||
return size_in_bytes_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the push constant layout for the given location.
|
||||
* Location = ShaderInput.location.
|
||||
*/
|
||||
const PushConstant *find(int32_t location) const;
|
||||
|
||||
void debug_print() const;
|
||||
};
|
||||
|
||||
private:
|
||||
const Layout *layout_ = nullptr;
|
||||
void *data_ = nullptr;
|
||||
|
||||
public:
|
||||
VKPushConstants();
|
||||
VKPushConstants(const Layout *layout);
|
||||
VKPushConstants(VKPushConstants &&other);
|
||||
virtual ~VKPushConstants();
|
||||
|
||||
VKPushConstants &operator=(VKPushConstants &&other);
|
||||
|
||||
size_t offset() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const Layout &layout_get() const
|
||||
{
|
||||
return *layout_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reference to the active data.
|
||||
*
|
||||
* Data can get inactive when push constants are modified, after being added to the command
|
||||
* queue. We still keep track of the old data for reuse and make sure we don't overwrite data
|
||||
* that is still not on the GPU.
|
||||
*/
|
||||
const void *data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify a push constant.
|
||||
*
|
||||
* location: ShaderInput.location of the push constant to update.
|
||||
* comp_len: number of components has the data type that is being updated.
|
||||
* array_size: number of elements when an array to update. (0=no array)
|
||||
* input_data: packed source data to use.
|
||||
*/
|
||||
template<typename T>
|
||||
void push_constant_set(int32_t location,
|
||||
int32_t comp_len,
|
||||
int32_t array_size,
|
||||
const T *input_data)
|
||||
{
|
||||
const Layout::PushConstant *push_constant_layout = layout_->find(location);
|
||||
if (push_constant_layout == nullptr) {
|
||||
/* Legacy code can still try to update push constants when they don't exist. For example
|
||||
* `immDrawPixelsTexSetup` will bind an image slot manually. This works in OpenGL, but in
|
||||
* vulkan images aren't stored as push constants. */
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *bytes = static_cast<uint8_t *>(data_);
|
||||
T *dst = static_cast<T *>(static_cast<void *>(&bytes[push_constant_layout->offset]));
|
||||
const int inner_row_padding = push_constant_layout->inner_row_padding;
|
||||
const bool is_tightly_std140_packed = (comp_len % 4) == 0;
|
||||
/* Vec3[] are not tightly packed in std430. */
|
||||
const bool is_tightly_std430_packed = comp_len != 3 || array_size == 0;
|
||||
if (inner_row_padding == 0 &&
|
||||
((layout_->storage_type_get() == StorageType::PUSH_CONSTANTS &&
|
||||
is_tightly_std430_packed) ||
|
||||
array_size == 0 || push_constant_layout->array_size == 0 || is_tightly_std140_packed))
|
||||
{
|
||||
const size_t copy_size_in_bytes = comp_len * max_ii(array_size, 1) * sizeof(T);
|
||||
BLI_assert_msg(push_constant_layout->offset + copy_size_in_bytes <= layout_->size_in_bytes(),
|
||||
"Tried to write outside the push constant allocated memory.");
|
||||
memcpy(dst, input_data, copy_size_in_bytes);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Store elements in buffer as array. In Std140 arrays have an element stride of 16
|
||||
* bytes. */
|
||||
BLI_assert(sizeof(T) == 4);
|
||||
const T *src = input_data;
|
||||
if (inner_row_padding == 0) {
|
||||
for (const int i : IndexRange(array_size)) {
|
||||
UNUSED_VARS(i);
|
||||
memcpy(dst, src, comp_len * sizeof(T));
|
||||
src += comp_len;
|
||||
dst += 4;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BLI_assert_msg(array_size == 1, "No support for MAT3 arrays, but can be added when needed");
|
||||
for (const int component_index : IndexRange(comp_len)) {
|
||||
*dst = *src;
|
||||
dst += 1;
|
||||
src += 1;
|
||||
if ((component_index % inner_row_padding) == (inner_row_padding - 1)) {
|
||||
dst += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When storage type = StorageType::BUFFER use this method to update the buffer.
|
||||
*
|
||||
* It must be called just before adding a draw/compute command to the command queue.
|
||||
*/
|
||||
VKBufferWithOffset update_uniform_buffer(VKContext &context);
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
121
blender-5.2.0/source/blender/gpu/vulkan/vk_query.cc
Normal file
121
blender-5.2.0/source/blender/gpu/vulkan/vk_query.cc
Normal file
@@ -0,0 +1,121 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "vk_query.hh"
|
||||
#include "vk_backend.hh"
|
||||
#include "vk_context.hh"
|
||||
|
||||
#include "GPU_debug.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
VKQueryPool::~VKQueryPool()
|
||||
{
|
||||
VKBackend &backend = VKBackend::get();
|
||||
const VKDevice &device = backend.device;
|
||||
|
||||
while (!vk_query_pools_.is_empty()) {
|
||||
VkQueryPool vk_query_pool = vk_query_pools_.pop_last();
|
||||
vkDestroyQueryPool(device.vk_handle(), vk_query_pool, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t VKQueryPool::query_index_in_pool() const
|
||||
{
|
||||
return queries_issued_ - (vk_query_pools_.size() - 1) * query_chunk_len_;
|
||||
}
|
||||
|
||||
void VKQueryPool::init(GPUQueryType type)
|
||||
{
|
||||
BLI_assert(vk_query_pools_.is_empty());
|
||||
queries_issued_ = 0;
|
||||
vk_query_type_ = to_vk_query_type(type);
|
||||
}
|
||||
|
||||
void VKQueryPool::begin_query()
|
||||
{
|
||||
VKBackend &backend = VKBackend::get();
|
||||
const VKDevice &device = backend.device;
|
||||
|
||||
uint32_t pool_index = queries_issued_ / query_chunk_len_;
|
||||
bool is_new_pool = (queries_issued_ % query_chunk_len_) == 0;
|
||||
|
||||
if (pool_index == vk_query_pools_.size()) {
|
||||
BLI_assert(is_new_pool);
|
||||
|
||||
VkQueryPoolCreateInfo create_info = {};
|
||||
create_info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
|
||||
create_info.queryType = vk_query_type_;
|
||||
create_info.queryCount = query_chunk_len_;
|
||||
|
||||
VkQueryPool vk_query_pool = VK_NULL_HANDLE;
|
||||
vkCreateQueryPool(device.vk_handle(), &create_info, nullptr, &vk_query_pool);
|
||||
vk_query_pools_.append(vk_query_pool);
|
||||
}
|
||||
BLI_assert(pool_index < vk_query_pools_.size());
|
||||
|
||||
/* When using a new query pool make sure to reset it before first usage. */
|
||||
VKContext &context = *VKContext::get();
|
||||
VkQueryPool vk_query_pool = vk_query_pools_[pool_index];
|
||||
if (is_new_pool) {
|
||||
render_graph::VKResetQueryPoolNode::Data reset_query_pool = {};
|
||||
reset_query_pool.vk_query_pool = vk_query_pool;
|
||||
reset_query_pool.first_query = 0;
|
||||
reset_query_pool.query_count = query_chunk_len_;
|
||||
context.render_graph().add_node(reset_query_pool);
|
||||
}
|
||||
|
||||
render_graph::VKBeginQueryNode::Data begin_query = {};
|
||||
begin_query.vk_query_pool = vk_query_pool;
|
||||
begin_query.query_index = query_index_in_pool();
|
||||
context.render_graph().add_node(begin_query);
|
||||
}
|
||||
|
||||
void VKQueryPool::end_query()
|
||||
{
|
||||
VKContext &context = *VKContext::get();
|
||||
render_graph::VKEndQueryNode::Data end_query = {};
|
||||
end_query.vk_query_pool = vk_query_pools_.last();
|
||||
end_query.query_index = query_index_in_pool();
|
||||
context.render_graph().add_node(end_query);
|
||||
queries_issued_ += 1;
|
||||
}
|
||||
|
||||
void VKQueryPool::get_occlusion_result(MutableSpan<uint32_t> r_values)
|
||||
{
|
||||
VKContext &context = *VKContext::get();
|
||||
/* During selection the frame buffer is still rendering. It needs to finish the render scope to
|
||||
* ensure the END_RENDERING node */
|
||||
context.rendering_end();
|
||||
context.flush_render_graph(RenderGraphFlushFlags::SUBMIT |
|
||||
RenderGraphFlushFlags::WAIT_FOR_COMPLETION |
|
||||
RenderGraphFlushFlags::RENEW_RENDER_GRAPH);
|
||||
|
||||
int queries_left = queries_issued_;
|
||||
int pool_index = 0;
|
||||
VKBackend &backend = VKBackend::get();
|
||||
const VKDevice &device = backend.device;
|
||||
while (queries_left) {
|
||||
VkQueryPool vk_query_pool = vk_query_pools_[pool_index];
|
||||
uint32_t *r_values_chunk = &r_values[pool_index * query_chunk_len_];
|
||||
uint32_t values_chunk_size = min_ii(queries_left, query_chunk_len_);
|
||||
vkGetQueryPoolResults(device.vk_handle(),
|
||||
vk_query_pool,
|
||||
0,
|
||||
values_chunk_size,
|
||||
values_chunk_size * sizeof(uint32_t),
|
||||
r_values_chunk,
|
||||
sizeof(uint32_t),
|
||||
VK_QUERY_RESULT_WAIT_BIT);
|
||||
|
||||
queries_left = max_ii(queries_left - query_chunk_len_, 0);
|
||||
pool_index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user