Vulkan: Support for Framebuffer with Missing Attachments

Wireframe mode wasn't working and viewport had flickering artifacts.
Reason was that the render pass creation failed for its framebuffer as
the input data was filled with garbage for attachments that were unused.

Vulkan requires every attachment to be filled upto the highest used attachment
slot. This PR fills missing attachments with a dummy texture.

Pull Request: https://projects.blender.org/blender/blender/pulls/113141
This commit is contained in:
Jeroen Bakker
2023-10-10 09:27:07 +02:00
parent 89efad1dbd
commit d67ec32747
6 changed files with 75 additions and 14 deletions

View File

@@ -55,6 +55,7 @@ void VKContext::sync_backbuffer()
command_buffer_.init(device);
command_buffer_.begin_recording();
device.init_dummy_buffer(*this);
device.init_dummy_color_attachment();
}
device.descriptor_pools_get().reset();
}

View File

@@ -27,6 +27,10 @@ void VKDevice::deinit()
vkDestroyCommandPool(vk_device_, vk_command_pool_, vk_allocation_callbacks);
dummy_buffer_.free();
if (dummy_color_attachment_.has_value()) {
delete &(*dummy_color_attachment_).get();
dummy_color_attachment_.reset();
}
sampler_.free();
destroy_discarded_resources();
vmaDestroyAllocator(mem_allocator_);
@@ -135,6 +139,19 @@ void VKDevice::init_dummy_buffer(VKContext &context)
dummy_buffer_.clear(context, 0);
}
void VKDevice::init_dummy_color_attachment()
{
if (dummy_color_attachment_.has_value()) {
return;
}
GPUTexture *texture = GPU_texture_create_2d(
"dummy_attachment", 1, 1, 1, GPU_R32F, GPU_TEXTURE_USAGE_ATTACHMENT, nullptr);
BLI_assert(texture);
VKTexture &vk_texture = *unwrap(unwrap(texture));
dummy_color_attachment_ = std::make_optional(std::reference_wrapper(vk_texture));
}
/* -------------------------------------------------------------------- */
/** \name Platform/driver/device information
* \{ */

View File

@@ -72,6 +72,7 @@ class VKDevice : public NonCopyable {
/** Buffer to bind to unbound resource locations. */
VKBuffer dummy_buffer_;
std::optional<std::reference_wrapper<VKTexture>> dummy_color_attachment_;
Vector<std::pair<VkImage, VmaAllocation>> discarded_images_;
Vector<std::pair<VkBuffer, VmaAllocation>> discarded_buffers_;
@@ -158,6 +159,7 @@ class VKDevice : public NonCopyable {
* Dummy buffer can only be initialized after the command buffer of the context is retrieved.
*/
void init_dummy_buffer(VKContext &context);
void init_dummy_color_attachment();
void deinit();
eGPUDeviceType device_type() const;
@@ -183,6 +185,12 @@ class VKDevice : public NonCopyable {
return dummy_buffer_;
}
VKTexture &dummy_color_attachment_get() const
{
BLI_assert(dummy_color_attachment_.has_value());
return (*dummy_color_attachment_).get();
}
void discard_image(VkImage vk_image, VmaAllocation vma_allocation);
void discard_image_view(VkImageView vk_image_view);
void discard_buffer(VkBuffer vk_buffer, VmaAllocation vma_allocation);

View File

@@ -413,6 +413,8 @@ void VKFrameBuffer::render_pass_create()
bool has_depth_attachment = false;
bool found_attachment = false;
const VKImageView &dummy_attachment =
VKBackend::get().device_get().dummy_color_attachment_get().image_view_get();
int depth_location = -1;
for (int type = GPU_FB_MAX_ATTACHMENT - 1; type >= 0; type--) {
@@ -436,8 +438,14 @@ void VKFrameBuffer::render_pass_create()
int attachment_location = type >= GPU_FB_COLOR_ATTACHMENT0 ? type - GPU_FB_COLOR_ATTACHMENT0 :
depth_location;
const bool is_depth_attachment = ELEM(
type, GPU_FB_DEPTH_ATTACHMENT, GPU_FB_DEPTH_STENCIL_ATTACHMENT);
if (attachment.tex) {
BLI_assert_msg(!is_depth_attachment || !has_depth_attachment,
"There can only be one depth/stencil attachment.");
has_depth_attachment |= is_depth_attachment;
/* Ensure texture is allocated to ensure the image view. */
VKTexture &texture = *static_cast<VKTexture *>(unwrap(attachment.tex));
image_views_.append(VKImageView(texture,
@@ -461,18 +469,31 @@ void VKFrameBuffer::render_pass_create()
attachment_description.finalLayout = texture.current_layout_get();
/* Create the attachment reference. */
const bool is_depth_attachment = ELEM(
type, GPU_FB_DEPTH_ATTACHMENT, GPU_FB_DEPTH_STENCIL_ATTACHMENT);
BLI_assert_msg(!is_depth_attachment || !has_depth_attachment,
"There can only be one depth/stencil attachment.");
has_depth_attachment |= is_depth_attachment;
VkAttachmentReference &attachment_reference = attachment_references[attachment_location];
attachment_reference.attachment = attachment_location;
attachment_reference.layout = is_depth_attachment ?
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL :
VK_IMAGE_LAYOUT_GENERAL;
}
else if (!is_depth_attachment) {
image_views[attachment_location] = dummy_attachment.vk_handle();
VkAttachmentDescription &attachment_description =
attachment_descriptions[attachment_location];
attachment_description.flags = 0;
attachment_description.format = VK_FORMAT_R32_SFLOAT;
attachment_description.samples = VK_SAMPLE_COUNT_1_BIT;
attachment_description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment_description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment_description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment_description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment_description.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
attachment_description.finalLayout = VK_IMAGE_LAYOUT_GENERAL;
VkAttachmentReference &attachment_reference = attachment_references[attachment_location];
attachment_reference.attachment = VK_ATTACHMENT_UNUSED;
attachment_reference.layout = VK_IMAGE_LAYOUT_UNDEFINED;
}
}
/* Update the size, viewport & scissor based on the first attachment. */
@@ -492,7 +513,6 @@ void VKFrameBuffer::render_pass_create()
scissor_reset();
/* Create render pass. */
const int attachment_len = has_depth_attachment ? depth_location + 1 : depth_location;
const int color_attachment_len = depth_location;
VkSubpassDescription subpass = {};
@@ -594,6 +614,17 @@ void VKFrameBuffer::update_size()
size_set(1, 1);
}
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;
}
/** \} */
} // namespace blender::gpu

View File

@@ -109,6 +109,15 @@ class VKFrameBuffer : public FrameBuffer {
*/
void update_size();
/**
* Return the number of color attachments of this frame buffer, including unused color
* attachments.
*
* Framebuffers can have unused attachments. When higher attachment slots are being used, unused
* lower attachment slots will be counted as they are required resources in renderpasses.
*/
int color_attachments_resource_size() const;
private:
void update_attachments();
void render_pass_free();

View File

@@ -78,13 +78,8 @@ void VKPipelineStateManager::force_state(const GPUState &state,
void VKPipelineStateManager::finalize_color_blend_state(const VKFrameBuffer &framebuffer)
{
color_blend_attachments.clear();
for (int color_slot = 0; color_slot < GPU_FB_MAX_COLOR_ATTACHMENT; color_slot++) {
VKTexture *texture = unwrap(unwrap(framebuffer.color_tex(color_slot)));
if (texture) {
color_blend_attachments.append(color_blend_attachment_template);
}
}
color_blend_attachments.append_n_times(color_blend_attachment_template,
framebuffer.color_attachments_resource_size());
pipeline_color_blend_state.attachmentCount = color_blend_attachments.size();
pipeline_color_blend_state.pAttachments = color_blend_attachments.data();
}