/* 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_shader_interface.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, render_graph::VKRenderGraph &render_graph) : render_graph(render_graph) { 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; compiler = new ShaderCompilerGeneric(); } VKContext::~VKContext() { if (surface_texture_) { GPU_texture_free(surface_texture_); surface_texture_ = nullptr; } VKBackend::get().device.context_unregister(*this); delete imm; imm = nullptr; delete compiler; } void VKContext::sync_backbuffer() { if (ghost_context_) { VKDevice &device = VKBackend::get().device; if (!is_init_) { is_init_ = true; descriptor_pools_.init(device); device.init_dummy_buffer(*this); } descriptor_pools_.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() {} void VKContext::flush_render_graph() { if (has_active_framebuffer()) { VKFrameBuffer &framebuffer = *active_framebuffer_get(); if (framebuffer.is_rendering()) { framebuffer.rendering_end(*this); } } render_graph.submit(); } void VKContext::finish() {} 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 * \{ */ VKStateManager &VKContext::state_manager_get() const { return *static_cast(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); 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(GPUPrimType primitive, VKVertexAttributeObject &vao, render_graph::VKPipelineData &r_pipeline_data) { VKShader &vk_shader = unwrap(*shader); BLI_assert(vk_shader.is_graphics_shader()); VKFrameBuffer &framebuffer = *active_framebuffer_get(); update_pipeline_data( vk_shader, vk_shader.ensure_and_get_graphics_pipeline(primitive, vao, state_manager_get(), framebuffer), r_pipeline_data); } void VKContext::update_pipeline_data(render_graph::VKPipelineData &r_pipeline_data) { VKShader &vk_shader = unwrap(*shader); BLI_assert(vk_shader.is_compute_shader()); update_pipeline_data(vk_shader, vk_shader.ensure_and_get_compute_pipeline(), 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_data = nullptr; r_pipeline_data.push_constants_size = 0; const VKPushConstants::Layout &push_constants_layout = vk_shader.interface_get().push_constants_layout_get(); vk_shader.push_constants.update(*this); if (push_constants_layout.storage_type_get() == VKPushConstants::StorageType::PUSH_CONSTANTS) { r_pipeline_data.push_constants_size = push_constants_layout.size_in_bytes(); r_pipeline_data.push_constants_data = vk_shader.push_constants.data(); } /* When using the push constant fallback we need to add a read access dependency to the uniform * buffer after the buffer has been updated. * NOTE: this alters the context instance variable `access_info_` which isn't clear from the API. */ if (push_constants_layout.storage_type_get() == VKPushConstants::StorageType::UNIFORM_BUFFER) { access_info_.buffers.append( {vk_shader.push_constants.uniform_buffer_get()->vk_handle(), VK_ACCESS_UNIFORM_READ_BIT}); } /* Update descriptor set. */ r_pipeline_data.vk_descriptor_set = VK_NULL_HANDLE; if (vk_shader.has_descriptor_set()) { descriptor_set_.update(*this); r_pipeline_data.vk_descriptor_set = descriptor_set_get().active_descriptor_set()->vk_handle(); } } render_graph::VKResourceAccessInfo &VKContext::update_and_get_access_info() { access_info_.reset(); state_manager_get().apply_bindings(*this, access_info_); return access_info_; } /** \} */ /* -------------------------------------------------------------------- */ /** \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 *color_attachment = unwrap(unwrap(framebuffer.color_tex(0))); 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_NEAREST; VkImageBlit ®ion = blit_image.region; region.srcOffsets[0] = {0, color_attachment->height_get() - 1, 0}; region.srcOffsets[1] = {color_attachment->width_get(), 0, 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, 0, 0}; region.dstOffsets[1] = { int32_t(swap_chain_data.extent.width), int32_t(swap_chain_data.extent.height), 1}; region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.dstSubresource.mipLevel = 0; region.dstSubresource.baseArrayLayer = 0; region.dstSubresource.layerCount = 1; /* Swap chain commands are CPU synchronized at this moment, allowing to temporary add the swap * chain image as device resources. When we move towards GPU swap chain synchronization we need * to keep track of the swap chain image between frames. */ VKDevice &device = VKBackend::get().device; device.resources.add_image(swap_chain_data.image, 1, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, render_graph::ResourceOwner::SWAP_CHAIN, "SwapchainImage"); framebuffer.rendering_end(*this); render_graph.add_node(blit_image); render_graph.submit_for_present(swap_chain_data.image); device.resources.remove_image(swap_chain_data.image); #if 0 device.debug_print(); #endif device.destroy_discarded_resources(); } void VKContext::swap_buffers_post_handler() { sync_backbuffer(); } /** \} */ } // namespace blender::gpu