Files
test2/source/blender/gpu/vulkan/vk_context.cc
Jeroen Bakker bee3f645d8 Vulkan: Rewrite GHOST_ContextVK
This is a rewrite of GHOST_ContextVK to align with Metal backend as described
in #111389 - solution 3 with the adaptation that GHOST is still responsible
for presenting the swap chain image and a post callback is still needed in
case the swapchain is recreated.

This PR also includes some smaller improvements in stability.

Technical documentation: https://developer.blender.org/docs/eevee_and_viewport/gpu/vulkan/swap_chain/

* Renderpasses and framebuffers are not owned anymore by GHOST_ContextVK
* VKFramebuffer doesn't contain a swap chain image.
* Swapchain images can only be used as a blit destination or present source.
  Not as an attachment.
* GHOST_ContextVK::swapBuffers would call a callback with the image the
  GPU module needs to blit the results to.
* Clearing of depth/stencil attachments when no depth write state is set.
* Enable VK_KHR_maintenance4 to relax the stage interface mapping.
* Removes most vulkan validation warnings/errors.
* Detection of frame buffer changes that needs to be applied before
  performing a command requiring render pass (draw/clear attachment)

**Benefits**

* Late retrieval of a swap chain image results in better overall performance as
  Blender doesn't need to wait until the image is presented on the screen.
* Easier API and clearer state (transitions)
* More control over Image layouts and command buffer states. (Better alignment with
  Vulkan API)

Pull Request: https://projects.blender.org/blender/blender/pulls/111473
2023-08-29 15:05:08 +02:00

273 lines
7.5 KiB
C++

/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup gpu
*/
#include "vk_context.hh"
#include "vk_debug.hh"
#include "vk_backend.hh"
#include "vk_framebuffer.hh"
#include "vk_immediate.hh"
#include "vk_memory.hh"
#include "vk_shader.hh"
#include "vk_state_manager.hh"
#include "vk_texture.hh"
#include "GHOST_C-api.h"
namespace blender::gpu {
VKContext::VKContext(void *ghost_window, void *ghost_context)
{
ghost_window_ = ghost_window;
ghost_context_ = ghost_context;
state_manager = new VKStateManager();
imm = new VKImmediate();
/* For off-screen contexts. Default frame-buffer is empty. */
VKFrameBuffer *framebuffer = new VKFrameBuffer("back_left");
back_left = framebuffer;
active_fb = framebuffer;
}
VKContext::~VKContext()
{
if (surface_texture_) {
GPU_texture_free(surface_texture_);
surface_texture_ = nullptr;
}
VKBackend::get().device_.context_unregister(*this);
delete imm;
imm = nullptr;
}
void VKContext::sync_backbuffer()
{
if (ghost_context_) {
VKDevice &device = VKBackend::get().device_;
if (!command_buffer_.is_initialized()) {
command_buffer_.init(device);
command_buffer_.begin_recording();
device.init_dummy_buffer(*this);
}
device.descriptor_pools_get().reset();
}
if (ghost_window_) {
GHOST_VulkanSwapChainData swap_chain_data = {};
GHOST_GetVulkanSwapChainFormat((GHOST_WindowHandle)ghost_window_, &swap_chain_data);
const bool reset_framebuffer = swap_chain_format_ != swap_chain_data.format ||
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;
}
surface_texture_ = GPU_texture_create_2d("back-left",
swap_chain_data.extent.width,
swap_chain_data.extent.height,
1,
to_gpu_format(swap_chain_data.format),
GPU_TEXTURE_USAGE_ATTACHMENT,
nullptr);
back_left->attachment_set(GPU_FB_COLOR_ATTACHMENT0,
GPU_ATTACHMENT_TEXTURE(surface_texture_));
back_left->bind(false);
swap_chain_format_ = swap_chain_data.format;
vk_extent_ = swap_chain_data.extent;
}
}
}
void VKContext::activate()
{
/* Make sure no other context is already bound to this thread. */
BLI_assert(is_active_ == false);
is_active_ = true;
sync_backbuffer();
immActivate();
}
void VKContext::deactivate()
{
immDeactivate();
is_active_ = false;
}
void VKContext::begin_frame() {}
void VKContext::end_frame() {}
void VKContext::flush()
{
command_buffer_.submit();
}
void VKContext::finish()
{
command_buffer_.submit();
}
void VKContext::memory_statistics_get(int * /*total_mem*/, int * /*free_mem*/) {}
/* -------------------------------------------------------------------- */
/** \name State manager
* \{ */
VKStateManager &VKContext::state_manager_get() const
{
return *static_cast<VKStateManager *>(state_manager);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \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();
command_buffer_.begin_render_pass(framebuffer);
}
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);
command_buffer_.end_render_pass(*framebuffer);
active_fb = nullptr;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Compute pipeline
* \{ */
void VKContext::bind_compute_pipeline()
{
VKShader *shader = unwrap(this->shader);
BLI_assert(shader);
VKPipeline &pipeline = shader->pipeline_get();
pipeline.update_and_bind(
*this, shader->vk_pipeline_layout_get(), VK_PIPELINE_BIND_POINT_COMPUTE);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Graphics pipeline
* \{ */
void VKContext::bind_graphics_pipeline(const GPUPrimType prim_type,
const VKVertexAttributeObject &vertex_attribute_object)
{
VKShader *shader = unwrap(this->shader);
BLI_assert(shader);
shader->update_graphics_pipeline(*this, prim_type, vertex_attribute_object);
VKPipeline &pipeline = shader->pipeline_get();
pipeline.update_and_bind(
*this, shader->vk_pipeline_layout_get(), VK_PIPELINE_BIND_POINT_GRAPHICS);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Graphics pipeline
* \{ */
void VKContext::swap_buffers_pre_callback(const GHOST_VulkanSwapChainData *swap_chain_data)
{
VKContext *context = VKContext::get();
BLI_assert(context);
context->swap_buffers_pre_handler(*swap_chain_data);
}
void VKContext::swap_buffers_post_callback()
{
VKContext *context = VKContext::get();
BLI_assert(context);
context->swap_buffers_post_handler();
}
void VKContext::swap_buffers_pre_handler(const GHOST_VulkanSwapChainData &swap_chain_data)
{
VKFrameBuffer &framebuffer = *unwrap(back_left);
VKTexture wrapper("display_texture");
wrapper.init(swap_chain_data.image,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
to_gpu_format(swap_chain_data.format));
wrapper.layout_ensure(*this, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
framebuffer.color_attachment_layout_ensure(*this, 0, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
VKTexture *color_attachment = unwrap(unwrap(framebuffer.color_tex(0)));
color_attachment->layout_ensure(*this, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
VkImageBlit image_blit = {};
image_blit.srcOffsets[0] = {0, int32_t(swap_chain_data.extent.height) - 1, 0};
image_blit.srcOffsets[1] = {int32_t(swap_chain_data.extent.width), 0, 1};
image_blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
image_blit.srcSubresource.mipLevel = 0;
image_blit.srcSubresource.baseArrayLayer = 0;
image_blit.srcSubresource.layerCount = 1;
image_blit.dstOffsets[0] = {0, 0, 0};
image_blit.dstOffsets[1] = {
int32_t(swap_chain_data.extent.width), int32_t(swap_chain_data.extent.height), 1};
image_blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
image_blit.dstSubresource.mipLevel = 0;
image_blit.dstSubresource.baseArrayLayer = 0;
image_blit.dstSubresource.layerCount = 1;
command_buffer_.blit(wrapper,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
*color_attachment,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
Span<VkImageBlit>(&image_blit, 1));
wrapper.layout_ensure(*this, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
command_buffer_.submit();
}
void VKContext::swap_buffers_post_handler()
{
sync_backbuffer();
}
/** \} */
} // namespace blender::gpu