Resource binding was over-complicated as I didn't understood the state manager and vulkan to make the correct decisions at that time. This refactor will remove a lot of the complexity and improves the performance. **Performance** The performance improvement is noticeable in complex grease pencil scenes. Grease pencil benchmark file picknick: - `NVIDIA Quadro RTX 6000` 17 fps -> 24 fps - `Intel(R) Arc(tm) A750 Graphics (DG2)` 6 -> 21 fps **Bottle-neck** The performance improvements originates from moving the update entry point from state manager to shader interface. The previous implementation (state manager) had to loop over all the bound resources and find in the shader interface where it was located in the descriptor set. Ignoring resources that were not used by the shader. But also making it hard to determine if descriptor sets actually changed. Previous implementation assumed descriptor sets always changed. When descriptor set changed a new descriptor set needed to be allocated. Most drivers this is a fast operation, but on Intel/Mesa this was measurable slow. Using an allocation pool doesn't fit the Vulkan API as you are only able to reuse when the layout matches exactly. Of course doable, but requires another structure to keep track of the actual layouts. **Solution** By using the shader interface as entry point we can: 1. Keep track if there are any changes in the state manager. If not and the layout is the same, the previous shader can be reused. 2. In stead of looping over each bound resource, we loop over bind points. **Future extensions** Bundle all descriptor set uploads just before use. This would be more in line with how 'modern' Vulkan should be implemented. This PR already separates the uploading from the updating and technically allows to upload more than one descriptor set. Instead of looking 1 set back we should measure if we can handle multiple or keep track of the different layouts resources to improve the performance even further. Optional use `VK_KHR_descriptor_buffer` when available. Pull Request: https://projects.blender.org/blender/blender/pulls/128068
167 lines
4.3 KiB
C++
167 lines
4.3 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup gpu
|
|
*/
|
|
|
|
#include "vk_state_manager.hh"
|
|
#include "vk_context.hh"
|
|
#include "vk_index_buffer.hh"
|
|
#include "vk_shader.hh"
|
|
#include "vk_storage_buffer.hh"
|
|
#include "vk_texture.hh"
|
|
#include "vk_vertex_buffer.hh"
|
|
|
|
#include "GPU_capabilities.hh"
|
|
|
|
namespace blender::gpu {
|
|
|
|
void VKStateManager::apply_state()
|
|
{
|
|
/* Intentionally empty. State is polled during pipeline creation and doesn't need to be applied.
|
|
* If this leads to issues we should have an active state. */
|
|
}
|
|
|
|
void VKStateManager::force_state()
|
|
{
|
|
/* Intentionally empty. State is polled during pipeline creation and is always forced. */
|
|
}
|
|
|
|
void VKStateManager::issue_barrier(eGPUBarrier barrier_bits)
|
|
{
|
|
/**
|
|
* Workaround for EEVEE ThicknessFromShadow shader.
|
|
*
|
|
* EEVEE light evaluation uses layered sub-pass tracking. Currently, the tracking supports
|
|
* transitioning a layer to a different layout once per rendering scope. When using the thickness
|
|
* from shadow, the layers need to be transitioned twice: once to image load/store for the
|
|
* thickness from shadow shader and then to a sampler for the light evaluation shader. We work
|
|
* around this limitation by suspending the rendering.
|
|
*
|
|
* The reason we need to suspend the rendering is that Vulkan, by default, doesn't support layout
|
|
* transitions between the begin and end of rendering. By suspending the render, the graph will
|
|
* create a new node group that allows the necessary image layout transition.
|
|
*
|
|
* This limitation could also be addressed in the render graph scheduler, but that would be quite
|
|
* a hassle to track and might not be worth the effort.
|
|
*/
|
|
if (bool(barrier_bits & GPU_BARRIER_SHADER_IMAGE_ACCESS)) {
|
|
VKContext &context = *VKContext::get();
|
|
context.rendering_end();
|
|
}
|
|
}
|
|
|
|
void VKStateManager::texture_bind(Texture *texture, GPUSamplerState sampler, int binding)
|
|
{
|
|
textures_.bind(BindSpaceTextures::Type::Texture, texture, sampler, binding);
|
|
is_dirty = true;
|
|
}
|
|
|
|
void VKStateManager::texture_unbind(Texture *texture)
|
|
{
|
|
textures_.unbind(texture);
|
|
is_dirty = true;
|
|
}
|
|
|
|
void VKStateManager::texture_unbind_all()
|
|
{
|
|
textures_.unbind_all();
|
|
is_dirty = true;
|
|
}
|
|
|
|
void VKStateManager::image_bind(Texture *tex, int binding)
|
|
{
|
|
VKTexture *texture = unwrap(tex);
|
|
images_.bind(texture, binding);
|
|
is_dirty = true;
|
|
}
|
|
|
|
void VKStateManager::image_unbind(Texture *tex)
|
|
{
|
|
VKTexture *texture = unwrap(tex);
|
|
images_.unbind(texture);
|
|
is_dirty = true;
|
|
}
|
|
|
|
void VKStateManager::image_unbind_all()
|
|
{
|
|
images_.unbind_all();
|
|
is_dirty = true;
|
|
}
|
|
|
|
void VKStateManager::uniform_buffer_bind(VKUniformBuffer *uniform_buffer, int binding)
|
|
{
|
|
uniform_buffers_.bind(uniform_buffer, binding);
|
|
is_dirty = true;
|
|
}
|
|
|
|
void VKStateManager::uniform_buffer_unbind(VKUniformBuffer *uniform_buffer)
|
|
{
|
|
uniform_buffers_.unbind(uniform_buffer);
|
|
is_dirty = true;
|
|
}
|
|
|
|
void VKStateManager::uniform_buffer_unbind_all()
|
|
{
|
|
uniform_buffers_.unbind_all();
|
|
is_dirty = true;
|
|
}
|
|
|
|
void VKStateManager::unbind_from_all_namespaces(void *resource)
|
|
{
|
|
uniform_buffers_.unbind(resource);
|
|
storage_buffers_.unbind(resource);
|
|
images_.unbind(resource);
|
|
textures_.unbind(resource);
|
|
is_dirty = true;
|
|
}
|
|
|
|
void VKStateManager::texel_buffer_bind(VKVertexBuffer &vertex_buffer, int binding)
|
|
{
|
|
textures_.bind(BindSpaceTextures::Type::VertexBuffer,
|
|
&vertex_buffer,
|
|
GPUSamplerState::default_sampler(),
|
|
binding);
|
|
is_dirty = true;
|
|
}
|
|
|
|
void VKStateManager::texel_buffer_unbind(VKVertexBuffer &vertex_buffer)
|
|
{
|
|
textures_.unbind(&vertex_buffer);
|
|
is_dirty = true;
|
|
}
|
|
|
|
void VKStateManager::storage_buffer_bind(BindSpaceStorageBuffers::Type resource_type,
|
|
void *resource,
|
|
int binding)
|
|
{
|
|
storage_buffers_.bind(resource_type, resource, binding);
|
|
is_dirty = true;
|
|
}
|
|
|
|
void VKStateManager::storage_buffer_unbind(void *resource)
|
|
{
|
|
storage_buffers_.unbind(resource);
|
|
is_dirty = true;
|
|
}
|
|
|
|
void VKStateManager::storage_buffer_unbind_all()
|
|
{
|
|
storage_buffers_.unbind_all();
|
|
is_dirty = true;
|
|
}
|
|
|
|
void VKStateManager::texture_unpack_row_length_set(uint len)
|
|
{
|
|
texture_unpack_row_length_ = len;
|
|
}
|
|
|
|
uint VKStateManager::texture_unpack_row_length_get() const
|
|
{
|
|
return texture_unpack_row_length_;
|
|
}
|
|
|
|
} // namespace blender::gpu
|