Vulkan: Initial Graphics Pipeline

Initial graphic pipeline targeting. The goal of this PR is to have an initial
graphics pipeline with missing features. It should help identifying
areas that requires engineering.

Current state is that developers of the GPU module can help with the many
smaller pieces that needs to be engineered in order to get it working. It is not
intended for users or developers from other modules, but your welcome to learn
and give feedback on the code and engineering part.

We do expect that large parts of the code still needs to be re-engineered into
a more future-proof implementation.

**Some highlights**:
- In Vulkan the state is kept in the pipeline. Therefore the state is tracked
  per pipeline. In the near future this could be used as a cache. More research
  is needed against the default pipeline cache that vulkan already provides.
- This PR is based on the work that Kazashi Yoshioka already did. And include
  work from him in the next areas
  - Vertex attributes
  - Vertex data conversions
  - Pipeline state
- Immediate support working.
- This PR modifies the VKCommandBuffer to keep track of the framebuffer and its
  binding state(render pass). Some Vulkan commands require no render pass to be
  active, other require a render pass. As the order of our commands on API level
  can not be separated this PR introduces a state engine to keep track of the
  current state and desired state. This is a temporary solution, the final
  solution will be proposed when we have a pixel on the screen. At that time
  I expect that we can design a command encoder that supports all the cases
  we need.

**Notices**:
- This branch works on NVIDIA GPUs and has been validated on a Linux system. AMD
  is known not to work (stalls) and Intel GPUs have not been tested at all. Windows might work
  but hasn't been validated yet.
- The graphics pipeline is implemented with pixels in mind, not with performance. Currently
  when a draw call is scheduled it is flushed and waited until it is finished drawing, before
  other draw calls can be scheduled. We expected the performance to be worse that it actually
  is, but we expect huge performance gains in the future.
- Any advanced drawing (that is used by the image editor, compositor or 3d viewport) isn't
  implemented and might crash when used.
- Using multiple windows or resizing of window isn't supported and will stall the system.

Pull Request: https://projects.blender.org/blender/blender/pulls/106224
This commit is contained in:
Jeroen Bakker 2023-05-11 13:01:56 +02:00
parent 6a20b0022c
commit 809a5aa418
35 changed files with 1505 additions and 103 deletions

View File

@ -212,14 +212,17 @@ set(VULKAN_SRC
vulkan/vk_drawlist.cc
vulkan/vk_fence.cc
vulkan/vk_framebuffer.cc
vulkan/vk_immediate.cc
vulkan/vk_index_buffer.cc
vulkan/vk_memory.cc
vulkan/vk_memory_layout.cc
vulkan/vk_pipeline_state.cc
vulkan/vk_pipeline.cc
vulkan/vk_pixel_buffer.cc
vulkan/vk_push_constants.cc
vulkan/vk_query.cc
vulkan/vk_resource_tracker.cc
vulkan/vk_sampler.cc
vulkan/vk_shader.cc
vulkan/vk_shader_interface.cc
vulkan/vk_shader_log.cc
@ -227,6 +230,7 @@ set(VULKAN_SRC
vulkan/vk_storage_buffer.cc
vulkan/vk_texture.cc
vulkan/vk_uniform_buffer.cc
vulkan/vk_vertex_attribute_object.cc
vulkan/vk_vertex_buffer.cc
vulkan/vk_backend.hh
@ -243,14 +247,17 @@ set(VULKAN_SRC
vulkan/vk_drawlist.hh
vulkan/vk_fence.hh
vulkan/vk_framebuffer.hh
vulkan/vk_immediate.hh
vulkan/vk_index_buffer.hh
vulkan/vk_memory.hh
vulkan/vk_memory_layout.hh
vulkan/vk_pipeline_state.hh
vulkan/vk_pipeline.hh
vulkan/vk_pixel_buffer.hh
vulkan/vk_push_constants.hh
vulkan/vk_query.hh
vulkan/vk_resource_tracker.hh
vulkan/vk_sampler.hh
vulkan/vk_shader.hh
vulkan/vk_shader_interface.hh
vulkan/vk_shader_log.hh
@ -258,6 +265,7 @@ set(VULKAN_SRC
vulkan/vk_storage_buffer.hh
vulkan/vk_texture.hh
vulkan/vk_uniform_buffer.hh
vulkan/vk_vertex_attribute_object.hh
vulkan/vk_vertex_buffer.hh
)

View File

@ -64,17 +64,8 @@ void VKBackend::samplers_update() {}
void VKBackend::compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len)
{
VKContext &context = *VKContext::get();
VKShader *shader = static_cast<VKShader *>(context.shader);
context.bind_compute_pipeline();
VKCommandBuffer &command_buffer = context.command_buffer_get();
VKPipeline &pipeline = shader->pipeline_get();
VKDescriptorSetTracker &descriptor_set = pipeline.descriptor_set_get();
VKPushConstants &push_constants = pipeline.push_constants_get();
push_constants.update(context);
descriptor_set.update(context);
command_buffer.bind(*descriptor_set.active_descriptor_set(),
shader->vk_pipeline_layout_get(),
VK_PIPELINE_BIND_POINT_COMPUTE);
command_buffer.dispatch(groups_x_len, groups_y_len, groups_z_len);
}

View File

@ -7,9 +7,42 @@
#include "vk_batch.hh"
#include "vk_context.hh"
#include "vk_index_buffer.hh"
#include "vk_vertex_attribute_object.hh"
#include "vk_vertex_buffer.hh"
namespace blender::gpu {
void VKBatch::draw(int /*v_first*/, int /*v_count*/, int /*i_first*/, int /*i_count*/) {}
void VKBatch::draw(int vertex_first, int vertex_count, int instance_first, int instance_count)
{
/* Currently the pipeline is rebuild on each draw command. Clearing the dirty flag for
* consistency with the internals of GPU module. */
flag &= ~GPU_BATCH_DIRTY;
/* Finalize graphics pipeline */
VKContext &context = *VKContext::get();
context.state_manager->apply_state();
VKVertexAttributeObject vao;
vao.update_bindings(context, *this);
context.bind_graphics_pipeline(prim_type, vao);
/* Bind geometry resources. */
vao.bind(context);
VKIndexBuffer *index_buffer = index_buffer_get();
const bool draw_indexed = index_buffer != nullptr;
if (draw_indexed) {
index_buffer->upload_data();
index_buffer->bind(context);
context.command_buffer_get().draw(
index_buffer->index_len_get(), instance_count, index_buffer->index_start_get(), vertex_first, instance_first);
}
else {
context.command_buffer_get().draw(vertex_first, vertex_count, instance_first, instance_count);
}
context.command_buffer_get().submit();
}
void VKBatch::draw_indirect(GPUStorageBuf * /*indirect_buf*/, intptr_t /*offset*/) {}
@ -20,4 +53,19 @@ void VKBatch::multi_draw_indirect(GPUStorageBuf * /*indirect_buf*/,
{
}
VKVertexBuffer *VKBatch::vertex_buffer_get(int index)
{
return unwrap(verts_(index));
}
VKVertexBuffer *VKBatch::instance_buffer_get(int index)
{
return unwrap(inst_(index));
}
VKIndexBuffer *VKBatch::index_buffer_get()
{
return unwrap(unwrap(elem));
}
} // namespace blender::gpu

View File

@ -10,15 +10,21 @@
#include "gpu_batch_private.hh"
namespace blender::gpu {
class VKVertexBuffer;
class VKIndexBuffer;
class VKBatch : public Batch {
public:
void draw(int v_first, int v_count, int i_first, int i_count) override;
void draw(int vertex_first, int vertex_count, int instance_first, int instance_count) override;
void draw_indirect(GPUStorageBuf *indirect_buf, intptr_t offset) override;
void multi_draw_indirect(GPUStorageBuf *indirect_buf,
int count,
intptr_t offset,
intptr_t stride) override;
VKVertexBuffer *vertex_buffer_get(int index);
VKVertexBuffer *instance_buffer_get(int index);
VKIndexBuffer *index_buffer_get();
};
} // namespace blender::gpu

View File

@ -61,4 +61,15 @@ class VKBuffer {
void unmap();
};
/**
* Helper struct to enable buffers to be bound with an offset.
*
* VKImmediate mode uses a single VKBuffer with multiple vertex layouts. Those layouts are send to
* the command buffer containing an offset.
*/
struct VKBufferWithOffset {
VKBuffer &buffer;
VkDeviceSize offset;
};
} // namespace blender::gpu

View File

@ -38,6 +38,13 @@ void VKCommandBuffer::init(const VkDevice vk_device,
submission_id_.reset();
state.stage = Stage::Initial;
/* When a the last GHOST context is destroyed the device is deallocate. A moment later the GPU
* context is destroyed. The first step is to activate it. Activating would retrieve the device
* from GHOST which in that case is a VK_NULL_HANDLE.*/
if (vk_device == VK_NULL_HANDLE) {
return;
}
if (vk_fence_ == VK_NULL_HANDLE) {
VK_ALLOCATION_CALLBACKS;
VkFenceCreateInfo fenceInfo{};
@ -95,6 +102,11 @@ void VKCommandBuffer::bind(const uint32_t binding,
bind(binding, vertex_buffer.vk_handle(), offset);
}
void VKCommandBuffer::bind(const uint32_t binding, const VKBufferWithOffset &vertex_buffer)
{
bind(binding, vertex_buffer.buffer.vk_handle(), vertex_buffer.offset);
}
void VKCommandBuffer::bind(const uint32_t binding,
const VkBuffer &vk_vertex_buffer,
const VkDeviceSize offset)

View File

@ -14,6 +14,7 @@
namespace blender::gpu {
class VKBuffer;
struct VKBufferWithOffset;
class VKDescriptorSet;
class VKFrameBuffer;
class VKIndexBuffer;
@ -141,6 +142,7 @@ class VKCommandBuffer : NonCopyable, NonMovable {
const VKVertexBuffer &vertex_buffer,
const VkDeviceSize offset);
/* Bind the given buffer as a vertex buffer. */
void bind(const uint32_t binding, const VKBufferWithOffset &vertex_buffer);
void bind(const uint32_t binding, const VkBuffer &vk_vertex_buffer, const VkDeviceSize offset);
void bind(const VKIndexBuffer &index_buffer, VkIndexType index_type);

View File

@ -9,8 +9,11 @@
#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"
@ -29,6 +32,7 @@ VKContext::VKContext(void *ghost_window, void *ghost_context)
}
state_manager = new VKStateManager();
imm = new VKImmediate();
/* For off-screen contexts. Default frame-buffer is empty. */
VKFrameBuffer *framebuffer = new VKFrameBuffer("back_left");
@ -36,7 +40,11 @@ VKContext::VKContext(void *ghost_window, void *ghost_context)
active_fb = framebuffer;
}
VKContext::~VKContext() {}
VKContext::~VKContext()
{
delete imm;
imm = nullptr;
}
void VKContext::sync_backbuffer()
{
@ -85,9 +93,15 @@ void VKContext::activate()
is_active_ = true;
sync_backbuffer();
immActivate();
}
void VKContext::deactivate() {}
void VKContext::deactivate()
{
immDeactivate();
is_active_ = false;
}
void VKContext::begin_frame()
{
@ -106,9 +120,6 @@ void VKContext::flush()
void VKContext::finish()
{
if (has_active_framebuffer()) {
deactivate_framebuffer();
}
command_buffer_.submit();
}
@ -123,8 +134,17 @@ const VKStateManager &VKContext::state_manager_get() const
return *static_cast<const VKStateManager *>(state_manager);
}
VKStateManager &VKContext::state_manager_get()
{
return *static_cast<VKStateManager *>(state_manager);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Framebuffer
* \{ */
void VKContext::activate_framebuffer(VKFrameBuffer &framebuffer)
{
if (has_active_framebuffer()) {
@ -148,12 +168,47 @@ bool VKContext::has_active_framebuffer() const
void VKContext::deactivate_framebuffer()
{
BLI_assert(active_fb != nullptr);
VKFrameBuffer *framebuffer = active_framebuffer_get();
BLI_assert(framebuffer != nullptr);
if (framebuffer->is_valid()) {
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);
}
/** \} */
} // namespace blender::gpu

View File

@ -15,6 +15,8 @@
namespace blender::gpu {
class VKFrameBuffer;
class VKVertexAttributeObject;
class VKBatch;
class VKStateManager;
class VKContext : public Context, NonCopyable {
@ -50,6 +52,9 @@ class VKContext : public Context, NonCopyable {
void deactivate_framebuffer();
VKFrameBuffer *active_framebuffer_get() const;
void bind_compute_pipeline();
void bind_graphics_pipeline(const GPUPrimType prim_type,
const VKVertexAttributeObject &vertex_attribute_object);
void sync_backbuffer();
static VKContext *get(void)
@ -63,6 +68,7 @@ class VKContext : public Context, NonCopyable {
}
const VKStateManager &state_manager_get() const;
VKStateManager &state_manager_get();
};
} // namespace blender::gpu

View File

@ -118,6 +118,7 @@ void object_label(VkObjectType vk_object_type, uint64_t object_handle, const cha
const VKDevice &device = VKBackend::get().device_get();
const VKDebuggingTools &debugging_tools = device.debugging_tools_get();
if (debugging_tools.enabled) {
const VKDevice &device = VKBackend::get().device_get();
VkDebugUtilsObjectNameInfoEXT info = {};
info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
info.objectType = vk_object_type;

View File

@ -81,6 +81,7 @@ bool VKDescriptorPools::is_last_pool_active()
std::unique_ptr<VKDescriptorSet> VKDescriptorPools::allocate(
const VkDescriptorSetLayout &descriptor_set_layout)
{
BLI_assert(descriptor_set_layout != VK_NULL_HANDLE);
VkDescriptorSetAllocateInfo allocate_info = {};
VkDescriptorPool pool = active_pool_get();
allocate_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;

View File

@ -7,6 +7,7 @@
#include "vk_descriptor_set.hh"
#include "vk_index_buffer.hh"
#include "vk_sampler.hh"
#include "vk_shader.hh"
#include "vk_storage_buffer.hh"
#include "vk_texture.hh"
@ -27,7 +28,6 @@ VKDescriptorSet::~VKDescriptorSet()
{
if (vk_descriptor_set_ != VK_NULL_HANDLE) {
/* Handle should be given back to the pool. */
BLI_assert(VKContext::get());
VKDevice &device = VKBackend::get().device_;
device.descriptor_pools_get().free(*this);
BLI_assert(vk_descriptor_set_ == VK_NULL_HANDLE);
@ -81,7 +81,17 @@ void VKDescriptorSetTracker::image_bind(VKTexture &texture,
{
Binding &binding = ensure_location(location);
binding.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
binding.vk_image_view = texture.vk_image_view_handle();
binding.texture = &texture;
}
void VKDescriptorSetTracker::bind(VKTexture &texture,
const VKDescriptorSet::Location location,
VKSampler &sampler)
{
Binding &binding = ensure_location(location);
binding.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
binding.texture = &texture;
binding.vk_sampler = sampler.vk_handle();
}
VKDescriptorSetTracker::Binding &VKDescriptorSetTracker::ensure_location(
@ -101,6 +111,7 @@ VKDescriptorSetTracker::Binding &VKDescriptorSetTracker::ensure_location(
void VKDescriptorSetTracker::update(VKContext &context)
{
BLI_assert(layout_ != VK_NULL_HANDLE);
tracked_resource_for(context, !bindings_.is_empty());
std::unique_ptr<VKDescriptorSet> &descriptor_set = active_descriptor_set();
VkDescriptorSet vk_descriptor_set = descriptor_set->vk_handle();
@ -132,9 +143,12 @@ void VKDescriptorSetTracker::update(VKContext &context)
if (!binding.is_image()) {
continue;
}
/* When updating the descriptor sets the layout of the texture should already be updated. */
binding.texture->layout_ensure(context, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
VkDescriptorImageInfo image_info = {};
image_info.imageView = binding.vk_image_view;
image_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
image_info.sampler = binding.vk_sampler;
image_info.imageView = binding.texture->vk_image_view_handle();
image_info.imageLayout = binding.texture->current_layout_get();
image_infos.append(image_info);
VkWriteDescriptorSet write_descriptor = {};
@ -150,7 +164,6 @@ void VKDescriptorSetTracker::update(VKContext &context)
BLI_assert_msg(image_infos.size() + buffer_infos.size() == descriptor_writes.size(),
"Not all changes have been converted to a write descriptor. Check "
"`Binding::is_buffer` and `Binding::is_image`.");
const VKDevice &device = VKBackend::get().device_get();
vkUpdateDescriptorSets(
device.device_get(), descriptor_writes.size(), descriptor_writes.data(), 0, nullptr);
@ -158,7 +171,7 @@ void VKDescriptorSetTracker::update(VKContext &context)
bindings_.clear();
}
std::unique_ptr<VKDescriptorSet> VKDescriptorSetTracker::create_resource(VKContext &context)
std::unique_ptr<VKDescriptorSet> VKDescriptorSetTracker::create_resource(VKContext & /*context*/)
{
VKDevice &device = VKBackend::get().device_;
return device.descriptor_pools_get().allocate(layout_);

View File

@ -25,6 +25,7 @@ class VKTexture;
class VKUniformBuffer;
class VKVertexBuffer;
class VKDescriptorSetTracker;
class VKSampler;
/**
* In vulkan shader resources (images and buffers) are grouped in descriptor sets.
@ -117,7 +118,8 @@ class VKDescriptorSetTracker : protected VKResourceTracker<VKDescriptorSet> {
VkBuffer vk_buffer = VK_NULL_HANDLE;
VkDeviceSize buffer_size = 0;
VkImageView vk_image_view = VK_NULL_HANDLE;
VKTexture *texture = nullptr;
VkSampler vk_sampler = VK_NULL_HANDLE;
Binding()
{
@ -131,14 +133,17 @@ class VKDescriptorSetTracker : protected VKResourceTracker<VKDescriptorSet> {
bool is_image() const
{
return ELEM(type, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
return ELEM(type,
VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) &&
texture != nullptr;
}
};
private:
/** A list of bindings that needs to be updated. */
Vector<Binding> bindings_;
VkDescriptorSetLayout layout_;
VkDescriptorSetLayout layout_ = VK_NULL_HANDLE;
public:
VKDescriptorSetTracker() {}
@ -149,7 +154,20 @@ class VKDescriptorSetTracker : protected VKResourceTracker<VKDescriptorSet> {
void bind_as_ssbo(VKIndexBuffer &buffer, VKDescriptorSet::Location location);
void bind(VKStorageBuffer &buffer, VKDescriptorSet::Location location);
void bind(VKUniformBuffer &buffer, VKDescriptorSet::Location location);
/* TODO: bind as image */
void image_bind(VKTexture &texture, VKDescriptorSet::Location location);
void bind(VKTexture &texture, VKDescriptorSet::Location location, VKSampler &sampler);
/**
* Some shaders don't need any descriptor sets so we don't need to bind them.
*
* The result of this function determines if the descriptor set has any layout assigned.
* TODO: we might want to make descriptor sets optional for pipelines.
*/
bool has_layout() const
{
return layout_ != VK_NULL_HANDLE;
}
/**
* Update the descriptor set on the device.

View File

@ -503,8 +503,10 @@ void VKFrameBuffer::render_pass_free()
VK_ALLOCATION_CALLBACKS
const VKDevice &device = VKBackend::get().device_get();
vkDestroyRenderPass(device.device_get(), vk_render_pass_, vk_allocation_callbacks);
vkDestroyFramebuffer(device.device_get(), vk_framebuffer_, vk_allocation_callbacks);
if (device.is_initialized()) {
vkDestroyRenderPass(device.device_get(), vk_render_pass_, vk_allocation_callbacks);
vkDestroyFramebuffer(device.device_get(), vk_framebuffer_, vk_allocation_callbacks);
}
vk_render_pass_ = VK_NULL_HANDLE;
vk_framebuffer_ = VK_NULL_HANDLE;
}

View File

@ -0,0 +1,88 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2023 Blender Foundation */
/** \file
* \ingroup gpu
*
* Mimics old style OpenGL immediate mode drawing.
*/
#include "vk_immediate.hh"
#include "vk_data_conversion.hh"
namespace blender::gpu {
VKImmediate::VKImmediate() {}
VKImmediate::~VKImmediate() {}
uchar *VKImmediate::begin()
{
VKContext &context = *VKContext::get();
const size_t bytes_needed = vertex_buffer_size(&vertex_format, vertex_len);
const bool new_buffer_needed = !has_active_resource() || buffer_bytes_free() < bytes_needed;
std::unique_ptr<VKBuffer> &buffer = tracked_resource_for(context, new_buffer_needed);
current_subbuffer_len_ = bytes_needed;
uchar *data = static_cast<uchar *>(buffer->mapped_memory_get());
return data + subbuffer_offset_get();
}
void VKImmediate::end()
{
BLI_assert_msg(prim_type != GPU_PRIM_NONE, "Illegal state: not between an immBegin/End pair.");
if (vertex_len == 0) {
return;
}
if (conversion_needed(vertex_format)) {
// Slow path
/* Determine the start of the subbuffer. The `vertex_data` attribute changes when new vertices
* are loaded.
*/
uchar *data = static_cast<uchar *>(active_resource()->mapped_memory_get()) +
subbuffer_offset_get();
convert_in_place(data, vertex_format, vertex_len);
}
VKContext &context = *VKContext::get();
BLI_assert(context.shader == unwrap(shader));
context.state_manager->apply_state();
vertex_attributes_.update_bindings(*this);
context.bind_graphics_pipeline(prim_type, vertex_attributes_);
vertex_attributes_.bind(context);
context.command_buffer_get().draw(0, vertex_len, 0, 1);
buffer_offset_ += current_subbuffer_len_;
current_subbuffer_len_ = 0;
}
VkDeviceSize VKImmediate::subbuffer_offset_get()
{
return buffer_offset_;
}
VkDeviceSize VKImmediate::buffer_bytes_free()
{
return active_resource()->size_in_bytes() - subbuffer_offset_get();
}
static VkDeviceSize new_buffer_size(size_t sub_buffer_size)
{
return max_ii(sub_buffer_size, DEFAULT_INTERNAL_BUFFER_SIZE);
}
std::unique_ptr<VKBuffer> VKImmediate::create_resource(VKContext & /*context*/)
{
const size_t bytes_needed = vertex_buffer_size(&vertex_format, vertex_len);
std::unique_ptr<VKBuffer> result = std::make_unique<VKBuffer>();
result->create(new_buffer_size(bytes_needed),
GPU_USAGE_DYNAMIC,
static_cast<VkBufferUsageFlagBits>(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT));
debug::object_label(result->vk_handle(), "Immediate");
buffer_offset_ = 0;
return result;
}
} // namespace blender::gpu

View File

@ -0,0 +1,51 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2023 Blender Foundation */
/** \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.h"
#include "vk_buffer.hh"
#include "vk_context.hh"
#include "vk_mem_alloc.h"
#include "vk_resource_tracker.hh"
#include "vk_vertex_attribute_object.hh"
namespace blender::gpu {
/* Size of internal buffer. */
constexpr size_t DEFAULT_INTERNAL_BUFFER_SIZE = (4 * 1024 * 1024);
class VKImmediate : public Immediate, VKResourceTracker<VKBuffer> {
private:
VKVertexAttributeObject vertex_attributes_;
VkDeviceSize buffer_offset_ = 0;
VkDeviceSize current_subbuffer_len_ = 0;
public:
VKImmediate();
virtual ~VKImmediate();
uchar *begin(void) override;
void end(void) override;
friend class VKVertexAttributeObject;
private:
VkDeviceSize subbuffer_offset_get();
VkDeviceSize buffer_bytes_free();
std::unique_ptr<VKBuffer> create_resource(VKContext &context) override;
};
} // namespace blender::gpu

View File

@ -7,26 +7,39 @@
#include "vk_pipeline.hh"
#include "vk_backend.hh"
#include "vk_batch.hh"
#include "vk_context.hh"
#include "vk_framebuffer.hh"
#include "vk_memory.hh"
#include "vk_state_manager.hh"
#include "vk_vertex_attribute_object.hh"
namespace blender::gpu {
VKPipeline::VKPipeline(VkDescriptorSetLayout vk_descriptor_set_layout,
VKPushConstants &&push_constants)
: active_vk_pipeline_(VK_NULL_HANDLE),
descriptor_set_(vk_descriptor_set_layout),
push_constants_(std::move(push_constants))
{
}
VKPipeline::VKPipeline(VkPipeline vk_pipeline,
VkDescriptorSetLayout vk_descriptor_set_layout,
VKPushConstants &&push_constants)
: vk_pipeline_(vk_pipeline),
: active_vk_pipeline_(vk_pipeline),
descriptor_set_(vk_descriptor_set_layout),
push_constants_(std::move(push_constants))
{
vk_pipelines_.append(vk_pipeline);
}
VKPipeline::~VKPipeline()
{
VK_ALLOCATION_CALLBACKS
const VKDevice &device = VKBackend::get().device_get();
if (vk_pipeline_ != VK_NULL_HANDLE) {
vkDestroyPipeline(device.device_get(), vk_pipeline_, vk_allocation_callbacks);
for (VkPipeline vk_pipeline : vk_pipelines_) {
vkDestroyPipeline(device.device_get(), vk_pipeline, vk_allocation_callbacks);
}
}
@ -64,14 +77,137 @@ VKPipeline VKPipeline::create_compute_pipeline(
return VKPipeline(vk_pipeline, descriptor_set_layout, std::move(push_constants));
}
VKPipeline VKPipeline::create_graphics_pipeline(
VkDescriptorSetLayout &descriptor_set_layout,
const VKPushConstants::Layout &push_constants_layout)
{
VKPushConstants push_constants(&push_constants_layout);
return VKPipeline(descriptor_set_layout, std::move(push_constants));
}
VkPipeline VKPipeline::vk_handle() const
{
return vk_pipeline_;
return active_vk_pipeline_;
}
bool VKPipeline::is_valid() const
{
return vk_pipeline_ != VK_NULL_HANDLE;
return active_vk_pipeline_ != VK_NULL_HANDLE;
}
void VKPipeline::finalize(VKContext &context,
VkShaderModule vertex_module,
VkShaderModule geometry_module,
VkShaderModule fragment_module,
VkPipelineLayout &pipeline_layout,
const GPUPrimType prim_type,
const VKVertexAttributeObject &vertex_attribute_object)
{
BLI_assert(vertex_module != VK_NULL_HANDLE);
VK_ALLOCATION_CALLBACKS
Vector<VkPipelineShaderStageCreateInfo> pipeline_stages;
VkPipelineShaderStageCreateInfo vertex_stage_info = {};
vertex_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertex_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertex_stage_info.module = vertex_module;
vertex_stage_info.pName = "main";
pipeline_stages.append(vertex_stage_info);
if (geometry_module != VK_NULL_HANDLE) {
VkPipelineShaderStageCreateInfo geometry_stage_info = {};
geometry_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
geometry_stage_info.stage = VK_SHADER_STAGE_GEOMETRY_BIT;
geometry_stage_info.module = geometry_module;
geometry_stage_info.pName = "main";
pipeline_stages.append(geometry_stage_info);
}
if (fragment_module != VK_NULL_HANDLE) {
VkPipelineShaderStageCreateInfo fragment_stage_info = {};
fragment_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragment_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragment_stage_info.module = fragment_module;
fragment_stage_info.pName = "main";
pipeline_stages.append(fragment_stage_info);
}
VKFrameBuffer &framebuffer = *context.active_framebuffer_get();
VkGraphicsPipelineCreateInfo pipeline_create_info = {};
pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_create_info.stageCount = pipeline_stages.size();
pipeline_create_info.pStages = pipeline_stages.data();
pipeline_create_info.layout = pipeline_layout;
pipeline_create_info.renderPass = framebuffer.vk_render_pass_get();
pipeline_create_info.subpass = 0;
/* Vertex input state. */
VkPipelineVertexInputStateCreateInfo vertex_input_state = {};
vertex_input_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_input_state.vertexBindingDescriptionCount = vertex_attribute_object.bindings.size();
vertex_input_state.pVertexBindingDescriptions = vertex_attribute_object.bindings.data();
vertex_input_state.vertexAttributeDescriptionCount = vertex_attribute_object.attributes.size();
vertex_input_state.pVertexAttributeDescriptions = vertex_attribute_object.attributes.data();
pipeline_create_info.pVertexInputState = &vertex_input_state;
/* Input assembly state. */
VkPipelineInputAssemblyStateCreateInfo pipeline_input_assembly = {};
pipeline_input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
pipeline_input_assembly.topology = to_vk_primitive_topology(prim_type);
pipeline_create_info.pInputAssemblyState = &pipeline_input_assembly;
/* Viewport state. */
VkPipelineViewportStateCreateInfo viewport_state = {};
viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
VkViewport viewport = framebuffer.vk_viewport_get();
viewport_state.pViewports = &viewport;
viewport_state.viewportCount = 1;
VkRect2D scissor = framebuffer.vk_render_area_get();
viewport_state.pScissors = &scissor;
viewport_state.scissorCount = 1;
pipeline_create_info.pViewportState = &viewport_state;
/* Multisample state. */
VkPipelineMultisampleStateCreateInfo multisample_state = {};
multisample_state.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisample_state.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
multisample_state.minSampleShading = 1.0f;
pipeline_create_info.pMultisampleState = &multisample_state;
/* States from the state manager. */
const VKPipelineStateManager &state_manager = state_manager_get();
pipeline_create_info.pColorBlendState = &state_manager.pipeline_color_blend_state;
pipeline_create_info.pRasterizationState = &state_manager.rasterization_state;
pipeline_create_info.pDepthStencilState = &state_manager.depth_stencil_state;
const VKDevice &device = VKBackend::get().device_get();
vkCreateGraphicsPipelines(device.device_get(),
VK_NULL_HANDLE,
1,
&pipeline_create_info,
vk_allocation_callbacks,
&active_vk_pipeline_);
/* TODO: we should cache several pipeline instances and detect pipelines we can reuse. This might
* also be done using a VkPipelineCache. For now we just destroy any available pipeline so it
* won't be overwritten by the newly created one. */
vk_pipelines_.append(active_vk_pipeline_);
debug::object_label(active_vk_pipeline_, "GraphicsPipeline");
}
void VKPipeline::update_and_bind(VKContext &context,
VkPipelineLayout vk_pipeline_layout,
VkPipelineBindPoint vk_pipeline_bind_point)
{
VKCommandBuffer &command_buffer = context.command_buffer_get();
command_buffer.bind(*this, vk_pipeline_bind_point);
push_constants_.update(context);
if (descriptor_set_.has_layout()) {
descriptor_set_.update(context);
command_buffer.bind(
*descriptor_set_.active_descriptor_set(), vk_pipeline_layout, vk_pipeline_bind_point);
}
}
} // namespace blender::gpu

View File

@ -14,29 +14,49 @@
#include "vk_common.hh"
#include "vk_descriptor_set.hh"
#include "vk_pipeline_state.hh"
#include "vk_push_constants.hh"
namespace blender::gpu {
class VKContext;
class VKShader;
class VKVertexAttributeObject;
class VKBatch;
/**
* Pipeline can be a compute pipeline or a graphic pipeline.
*
* Compute pipelines can be constructed early on, but graphics
* pipelines depends on the actual GPU state/context.
*
* - TODO: we should sanitize the interface. There we can also
* use late construction for compute pipelines.
*/
class VKPipeline : NonCopyable {
VkPipeline vk_pipeline_ = VK_NULL_HANDLE;
/* Active pipeline handle. */
VkPipeline active_vk_pipeline_ = VK_NULL_HANDLE;
/** Keep track of all pipelines as they can still be in flight. */
Vector<VkPipeline> vk_pipelines_;
VKDescriptorSetTracker descriptor_set_;
VKPushConstants push_constants_;
VKPipelineStateManager state_manager_;
public:
VKPipeline() = default;
virtual ~VKPipeline();
VKPipeline(VkDescriptorSetLayout vk_descriptor_set_layout, VKPushConstants &&push_constants);
VKPipeline(VkPipeline vk_pipeline,
VkDescriptorSetLayout vk_descriptor_set_layout,
VKPushConstants &&push_constants);
VKPipeline &operator=(VKPipeline &&other)
{
vk_pipeline_ = other.vk_pipeline_;
other.vk_pipeline_ = VK_NULL_HANDLE;
active_vk_pipeline_ = other.active_vk_pipeline_;
other.active_vk_pipeline_ = VK_NULL_HANDLE;
descriptor_set_ = std::move(other.descriptor_set_);
push_constants_ = std::move(other.push_constants_);
vk_pipelines_ = std::move(other.vk_pipelines_);
other.vk_pipelines_.clear();
return *this;
}
@ -44,6 +64,8 @@ class VKPipeline : NonCopyable {
VkDescriptorSetLayout &descriptor_set_layout,
VkPipelineLayout &pipeline_layouts,
const VKPushConstants::Layout &push_constants_layout);
static VKPipeline create_graphics_pipeline(VkDescriptorSetLayout &descriptor_set_layout,
const VKPushConstants::Layout &push_constants_layout);
VKDescriptorSetTracker &descriptor_set_get()
{
@ -55,8 +77,28 @@ class VKPipeline : NonCopyable {
return push_constants_;
}
VKPipelineStateManager &state_manager_get()
{
return state_manager_;
}
VkPipeline vk_handle() const;
bool is_valid() const;
void finalize(VKContext &context,
VkShaderModule vertex_module,
VkShaderModule geometry_module,
VkShaderModule fragment_module,
VkPipelineLayout &pipeline_layout,
const GPUPrimType prim_type,
const VKVertexAttributeObject &vertex_attribute_object);
/**
* Update PushConstants, DescriptorSets and bind pipeline to command buffer.
*/
void update_and_bind(VKContext &context,
VkPipelineLayout vk_pipeline_layout,
VkPipelineBindPoint vk_pipeline_bind_point);
};
} // namespace blender::gpu

View File

@ -0,0 +1,367 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2023 Blender Foundation */
/** \file
* \ingroup gpu
*/
#include "vk_pipeline_state.hh"
namespace blender::gpu {
VKPipelineStateManager::VKPipelineStateManager()
{
rasterization_state = {};
rasterization_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterization_state.lineWidth = 1.0f;
pipeline_color_blend_state = {};
pipeline_color_blend_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
depth_stencil_state = {};
depth_stencil_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
/* TODO should be extracted from current framebuffer and should not be done here and now. */
/* When the attachments differ the state should be forced. */
VkPipelineColorBlendAttachmentState color_blend_attachment = {};
color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
color_blend_attachments.append(color_blend_attachment);
pipeline_color_blend_state.attachmentCount = color_blend_attachments.size();
pipeline_color_blend_state.pAttachments = color_blend_attachments.data();
}
void VKPipelineStateManager::set_state(const GPUState &state, const GPUStateMutable &mutable_state)
{
GPUState changed = state ^ current_;
if (changed.blend) {
set_blend(static_cast<eGPUBlend>(state.blend));
}
if (changed.write_mask != 0) {
set_write_mask((eGPUWriteMask)state.write_mask);
}
if (changed.depth_test != 0) {
set_depth_test((eGPUDepthTest)state.depth_test);
}
if (changed.stencil_test != 0 || changed.stencil_op != 0) {
set_stencil_test((eGPUStencilTest)state.stencil_test, (eGPUStencilOp)state.stencil_op);
set_stencil_mask((eGPUStencilTest)state.stencil_test, mutable_state);
}
if (changed.clip_distances != 0) {
set_clip_distances(state.clip_distances, current_.clip_distances);
}
if (changed.culling_test != 0) {
set_backface_culling((eGPUFaceCullTest)state.culling_test);
}
if (changed.logic_op_xor != 0) {
set_logic_op(state.logic_op_xor);
}
if (changed.invert_facing != 0) {
set_facing(state.invert_facing);
}
if (changed.provoking_vert != 0) {
set_provoking_vert((eGPUProvokingVertex)state.provoking_vert);
}
if (changed.shadow_bias != 0) {
set_shadow_bias(state.shadow_bias);
}
current_ = state;
}
void VKPipelineStateManager::force_state(const GPUState &state,
const GPUStateMutable &mutable_state)
{
current_ = ~state;
set_state(state, mutable_state);
}
void VKPipelineStateManager::set_blend(const eGPUBlend blend)
{
VkPipelineColorBlendStateCreateInfo &cb = pipeline_color_blend_state;
VkPipelineColorBlendAttachmentState &att_state = color_blend_attachments.last();
att_state.blendEnable = VK_TRUE;
att_state.alphaBlendOp = VK_BLEND_OP_ADD;
att_state.colorBlendOp = VK_BLEND_OP_ADD;
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_DST_ALPHA;
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
cb.blendConstants[0] = 1.0f;
cb.blendConstants[1] = 1.0f;
cb.blendConstants[2] = 1.0f;
cb.blendConstants[3] = 1.0f;
switch (blend) {
default:
case GPU_BLEND_ALPHA:
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
break;
case GPU_BLEND_ALPHA_PREMULT:
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
break;
case GPU_BLEND_ADDITIVE:
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
break;
case GPU_BLEND_SUBTRACT:
case GPU_BLEND_ADDITIVE_PREMULT:
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
break;
case GPU_BLEND_MULTIPLY:
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_DST_COLOR;
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_DST_ALPHA;
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
break;
case GPU_BLEND_INVERT:
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
break;
case GPU_BLEND_OIT:
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
break;
case GPU_BLEND_BACKGROUND:
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
break;
case GPU_BLEND_ALPHA_UNDER_PREMUL:
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
break;
case GPU_BLEND_CUSTOM:
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_SRC1_COLOR;
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_SRC1_ALPHA;
break;
}
if (blend == GPU_BLEND_SUBTRACT) {
att_state.alphaBlendOp = VK_BLEND_OP_REVERSE_SUBTRACT;
att_state.colorBlendOp = VK_BLEND_OP_REVERSE_SUBTRACT;
}
else {
att_state.alphaBlendOp = VK_BLEND_OP_ADD;
att_state.colorBlendOp = VK_BLEND_OP_ADD;
}
if (blend != GPU_BLEND_NONE) {
att_state.blendEnable = VK_TRUE;
}
else {
att_state.blendEnable = VK_FALSE;
}
}
void VKPipelineStateManager::set_write_mask(const eGPUWriteMask write_mask)
{
depth_stencil_state.depthWriteEnable = (write_mask & GPU_WRITE_DEPTH) ? VK_TRUE : VK_FALSE;
VkPipelineColorBlendAttachmentState &att_state = color_blend_attachments.last();
att_state.colorWriteMask = 0;
if ((write_mask & GPU_WRITE_RED) != 0) {
att_state.colorWriteMask |= VK_COLOR_COMPONENT_R_BIT;
}
if ((write_mask & GPU_WRITE_GREEN) != 0) {
att_state.colorWriteMask |= VK_COLOR_COMPONENT_G_BIT;
}
if ((write_mask & GPU_WRITE_BLUE) != 0) {
att_state.colorWriteMask |= VK_COLOR_COMPONENT_B_BIT;
}
if ((write_mask & GPU_WRITE_ALPHA) != 0) {
att_state.colorWriteMask |= VK_COLOR_COMPONENT_A_BIT;
}
}
void VKPipelineStateManager::set_depth_test(const eGPUDepthTest value)
{
switch (value) {
case GPU_DEPTH_LESS:
depth_stencil_state.depthCompareOp = VK_COMPARE_OP_LESS;
break;
case GPU_DEPTH_LESS_EQUAL:
depth_stencil_state.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
break;
case GPU_DEPTH_EQUAL:
depth_stencil_state.depthCompareOp = VK_COMPARE_OP_EQUAL;
break;
case GPU_DEPTH_GREATER:
depth_stencil_state.depthCompareOp = VK_COMPARE_OP_GREATER;
break;
case GPU_DEPTH_GREATER_EQUAL:
depth_stencil_state.depthCompareOp = VK_COMPARE_OP_GREATER_OR_EQUAL;
break;
case GPU_DEPTH_ALWAYS:
default:
depth_stencil_state.depthCompareOp = VK_COMPARE_OP_ALWAYS;
break;
}
if (value != GPU_DEPTH_NONE) {
depth_stencil_state.depthTestEnable = VK_TRUE;
}
else {
depth_stencil_state.depthTestEnable = VK_FALSE;
depth_stencil_state.depthCompareOp = VK_COMPARE_OP_NEVER;
}
depth_stencil_state.depthBoundsTestEnable = VK_TRUE;
}
void VKPipelineStateManager::set_stencil_test(const eGPUStencilTest test,
const eGPUStencilOp operation)
{
depth_stencil_state.front.compareOp = VK_COMPARE_OP_ALWAYS;
depth_stencil_state.front.compareMask = 0;
depth_stencil_state.front.reference = 0;
switch (operation) {
case GPU_STENCIL_OP_REPLACE:
depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP;
depth_stencil_state.front.passOp = VK_STENCIL_OP_KEEP;
depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_REPLACE;
depth_stencil_state.back = depth_stencil_state.front;
break;
case GPU_STENCIL_OP_COUNT_DEPTH_PASS:
depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP;
depth_stencil_state.front.passOp = VK_STENCIL_OP_KEEP;
depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_DECREMENT_AND_WRAP;
depth_stencil_state.back = depth_stencil_state.front;
depth_stencil_state.back.depthFailOp = VK_STENCIL_OP_INCREMENT_AND_WRAP;
break;
case GPU_STENCIL_OP_COUNT_DEPTH_FAIL:
depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP;
depth_stencil_state.front.passOp = VK_STENCIL_OP_INCREMENT_AND_WRAP;
depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_KEEP;
depth_stencil_state.back = depth_stencil_state.front;
depth_stencil_state.back.depthFailOp = VK_STENCIL_OP_DECREMENT_AND_WRAP;
break;
case GPU_STENCIL_OP_NONE:
default:
depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP;
depth_stencil_state.front.passOp = VK_STENCIL_OP_KEEP;
depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_KEEP;
depth_stencil_state.back = depth_stencil_state.front;
break;
}
if (test != GPU_STENCIL_NONE) {
depth_stencil_state.stencilTestEnable = VK_TRUE;
}
else {
depth_stencil_state.stencilTestEnable = VK_FALSE;
}
}
void VKPipelineStateManager::set_stencil_mask(const eGPUStencilTest test,
const GPUStateMutable &mutable_state)
{
depth_stencil_state.front.writeMask = static_cast<uint32_t>(mutable_state.stencil_write_mask);
depth_stencil_state.front.reference = static_cast<uint32_t>(mutable_state.stencil_reference);
depth_stencil_state.front.compareOp = VK_COMPARE_OP_ALWAYS;
depth_stencil_state.front.compareMask = static_cast<uint32_t>(
mutable_state.stencil_compare_mask);
switch (test) {
case GPU_STENCIL_NEQUAL:
depth_stencil_state.front.compareOp = VK_COMPARE_OP_NOT_EQUAL;
break;
case GPU_STENCIL_EQUAL:
depth_stencil_state.front.compareOp = VK_COMPARE_OP_EQUAL;
break;
case GPU_STENCIL_ALWAYS:
depth_stencil_state.front.compareOp = VK_COMPARE_OP_ALWAYS;
break;
case GPU_STENCIL_NONE:
default:
depth_stencil_state.front.compareMask = 0x00;
depth_stencil_state.front.compareOp = VK_COMPARE_OP_ALWAYS;
return;
}
depth_stencil_state.back = depth_stencil_state.front;
}
void VKPipelineStateManager::set_clip_distances(const int /*new_dist_len*/,
const int /*old_dist_len*/)
{
/* TODO: needs to be implemented. */
}
void VKPipelineStateManager::set_logic_op(const bool enable)
{
if (enable) {
pipeline_color_blend_state.logicOpEnable = VK_TRUE;
pipeline_color_blend_state.logicOp = VK_LOGIC_OP_XOR;
}
else {
pipeline_color_blend_state.logicOpEnable = VK_FALSE;
}
}
void VKPipelineStateManager::set_facing(const bool invert)
{
rasterization_state.frontFace = invert ? VK_FRONT_FACE_COUNTER_CLOCKWISE :
VK_FRONT_FACE_CLOCKWISE;
}
void VKPipelineStateManager::set_backface_culling(const eGPUFaceCullTest cull_test)
{
rasterization_state.cullMode = to_vk_cull_mode_flags(cull_test);
}
void VKPipelineStateManager::set_provoking_vert(const eGPUProvokingVertex /*vert*/)
{
/* TODO: Requires VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, See:
* https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineRasterizationProvokingVertexStateCreateInfoEXT.html
*/
}
void VKPipelineStateManager::set_shadow_bias(const bool enable)
{
if (enable) {
rasterization_state.depthBiasEnable = VK_TRUE;
rasterization_state.depthBiasSlopeFactor = 2.f;
rasterization_state.depthBiasConstantFactor = 1.f;
rasterization_state.depthBiasClamp = 0.f;
}
else {
rasterization_state.depthBiasEnable = VK_FALSE;
}
}
} // namespace blender::gpu

View File

@ -0,0 +1,46 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2023 Blender Foundation */
/** \file
* \ingroup gpu
*/
#include "gpu_state_private.hh"
#include "vk_common.hh"
#include "BLI_vector.hh"
namespace blender::gpu {
class VKPipelineStateManager {
private:
GPUState current_;
GPUStateMutable current_mutable_;
public:
VkPipelineColorBlendStateCreateInfo pipeline_color_blend_state;
Vector<VkPipelineColorBlendAttachmentState> color_blend_attachments;
VkPipelineRasterizationStateCreateInfo rasterization_state;
VkPipelineDepthStencilStateCreateInfo depth_stencil_state;
VKPipelineStateManager();
void set_state(const GPUState &state, const GPUStateMutable &mutable_state);
void force_state(const GPUState &state, const GPUStateMutable &mutable_state);
private:
void set_blend(eGPUBlend blend);
void set_write_mask(eGPUWriteMask write_mask);
void set_depth_test(eGPUDepthTest value);
void set_stencil_test(eGPUStencilTest test, eGPUStencilOp operation);
void set_stencil_mask(eGPUStencilTest test, const GPUStateMutable &mutable_state);
void set_clip_distances(int new_dist_len, int old_dist_len);
void set_logic_op(bool enable);
void set_facing(bool invert);
void set_backface_culling(eGPUFaceCullTest test);
void set_provoking_vert(eGPUProvokingVertex vert);
void set_shadow_bias(bool enable);
};
} // namespace blender::gpu

View File

@ -158,6 +158,14 @@ template<typename Resource> class VKResourceTracker : NonCopyable {
*/
virtual std::unique_ptr<Resource> create_resource(VKContext &context) = 0;
/**
* Does this instance have an active resource.
*/
bool has_active_resource()
{
return !tracked_resources_.is_empty();
}
/**
* Return the active resource of the tracker.
*/

View File

@ -0,0 +1,45 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2023 Blender Foundation */
/** \file
* \ingroup gpu
*/
#include "vk_sampler.hh"
#include "vk_backend.hh"
#include "vk_context.hh"
#include "vk_memory.hh"
namespace blender::gpu {
VKSampler::~VKSampler()
{
free();
}
void VKSampler::create()
{
BLI_assert(vk_sampler_ == VK_NULL_HANDLE);
VK_ALLOCATION_CALLBACKS
VkSamplerCreateInfo sampler_info = {};
sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
const VKDevice &device = VKBackend::get().device_get();
vkCreateSampler(device.device_get(), &sampler_info, vk_allocation_callbacks, &vk_sampler_);
debug::object_label(vk_sampler_, "DummySampler");
}
void VKSampler::free()
{
VK_ALLOCATION_CALLBACKS
if (vk_sampler_ != VK_NULL_HANDLE) {
const VKDevice &device = VKBackend::get().device_get();
if (device.device_get() != VK_NULL_HANDLE) {
vkDestroySampler(device.device_get(), vk_sampler_, vk_allocation_callbacks);
}
vk_sampler_ = VK_NULL_HANDLE;
}
}
} // namespace blender::gpu

View File

@ -0,0 +1,34 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2023 Blender Foundation */
/** \file
* \ingroup gpu
*/
#pragma once
#include "gpu_shader_private.hh"
#include "vk_common.hh"
#include "BLI_utility_mixins.hh"
namespace blender::gpu {
class VKContext;
class VKSampler : public NonCopyable {
VkSampler vk_sampler_ = VK_NULL_HANDLE;
public:
virtual ~VKSampler();
void create();
void free();
VkSampler vk_handle()
{
BLI_assert(vk_sampler_ != VK_NULL_HANDLE);
return vk_sampler_;
}
};
} // namespace blender::gpu

View File

@ -578,7 +578,6 @@ VKShader::VKShader(const char *name) : Shader(name)
VKShader::~VKShader()
{
VK_ALLOCATION_CALLBACKS
const VKDevice &device = VKBackend::get().device_get();
if (vertex_module_ != VK_NULL_HANDLE) {
vkDestroyShaderModule(device.device_get(), vertex_module_, vk_allocation_callbacks);
@ -667,16 +666,18 @@ bool VKShader::finalize(const shader::ShaderCreateInfo *info)
BLI_assert((fragment_module_ != VK_NULL_HANDLE && info->tf_type_ == GPU_SHADER_TFB_NONE) ||
(fragment_module_ == VK_NULL_HANDLE && info->tf_type_ != GPU_SHADER_TFB_NONE));
BLI_assert(compute_module_ == VK_NULL_HANDLE);
result = finalize_graphics_pipeline(device.device_get());
pipeline_ = VKPipeline::create_graphics_pipeline(layout_,
vk_interface->push_constants_layout_get());
result = true;
}
else {
BLI_assert(vertex_module_ == VK_NULL_HANDLE);
BLI_assert(geometry_module_ == VK_NULL_HANDLE);
BLI_assert(fragment_module_ == VK_NULL_HANDLE);
BLI_assert(compute_module_ != VK_NULL_HANDLE);
compute_pipeline_ = VKPipeline::create_compute_pipeline(
pipeline_ = VKPipeline::create_compute_pipeline(
compute_module_, layout_, pipeline_layout_, vk_interface->push_constants_layout_get());
result = compute_pipeline_.is_valid();
result = pipeline_.is_valid();
}
if (result) {
@ -688,36 +689,6 @@ bool VKShader::finalize(const shader::ShaderCreateInfo *info)
return result;
}
bool VKShader::finalize_graphics_pipeline(VkDevice /*vk_device */)
{
Vector<VkPipelineShaderStageCreateInfo> pipeline_stages;
VkPipelineShaderStageCreateInfo vertex_stage_info = {};
vertex_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertex_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertex_stage_info.module = vertex_module_;
vertex_stage_info.pName = "main";
pipeline_stages.append(vertex_stage_info);
if (geometry_module_ != VK_NULL_HANDLE) {
VkPipelineShaderStageCreateInfo geo_stage_info = {};
geo_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
geo_stage_info.stage = VK_SHADER_STAGE_GEOMETRY_BIT;
geo_stage_info.module = geometry_module_;
geo_stage_info.pName = "main";
pipeline_stages.append(geo_stage_info);
}
if (fragment_module_ != VK_NULL_HANDLE) {
VkPipelineShaderStageCreateInfo fragment_stage_info = {};
fragment_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragment_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragment_stage_info.module = fragment_module_;
fragment_stage_info.pName = "main";
pipeline_stages.append(fragment_stage_info);
}
return true;
}
bool VKShader::finalize_pipeline_layout(VkDevice vk_device,
const VKShaderInterface &shader_interface)
{
@ -958,26 +929,27 @@ bool VKShader::transform_feedback_enable(GPUVertBuf *)
void VKShader::transform_feedback_disable() {}
void VKShader::update_graphics_pipeline(VKContext &context,
const GPUPrimType prim_type,
const VKVertexAttributeObject &vertex_attribute_object)
{
BLI_assert(is_graphics_shader());
pipeline_get().finalize(context,
vertex_module_,
geometry_module_,
fragment_module_,
pipeline_layout_,
prim_type,
vertex_attribute_object);
}
void VKShader::bind()
{
VKContext *context = VKContext::get();
if (is_compute_shader()) {
context->command_buffer_get().bind(compute_pipeline_, VK_PIPELINE_BIND_POINT_COMPUTE);
}
else {
BLI_assert_unreachable();
}
/* Intentionally empty. Binding of the pipeline are done just before drawing/dispatching.
* See #VKPipeline.update_and_bind */
}
void VKShader::unbind()
{
if (is_compute_shader()) {
}
else {
BLI_assert_unreachable();
}
}
void VKShader::unbind() {}
void VKShader::uniform_float(int location, int comp_len, int array_size, const float *data)
{
@ -1217,7 +1189,7 @@ int VKShader::program_handle_get() const
VKPipeline &VKShader::pipeline_get()
{
return compute_pipeline_;
return pipeline_;
}
const VKShaderInterface &VKShader::interface_get() const

View File

@ -28,7 +28,7 @@ class VKShader : public Shader {
bool compilation_failed_ = false;
VkDescriptorSetLayout layout_ = VK_NULL_HANDLE;
VkPipelineLayout pipeline_layout_ = VK_NULL_HANDLE;
VKPipeline compute_pipeline_;
VKPipeline pipeline_;
public:
VKShader(const char *name);
@ -70,6 +70,10 @@ class VKShader : public Shader {
const VKShaderInterface &interface_get() const;
void update_graphics_pipeline(VKContext &context,
const GPUPrimType prim_type,
const VKVertexAttributeObject &vertex_attribute_object);
private:
Vector<uint32_t> compile_glsl_to_spirv(Span<const char *> sources, shaderc_shader_kind kind);
void build_shader_module(Span<uint32_t> spirv_module, VkShaderModule *r_shader_module);
@ -80,7 +84,6 @@ class VKShader : public Shader {
const VKShaderInterface &shader_interface,
const shader::ShaderCreateInfo &info);
bool finalize_pipeline_layout(VkDevice vk_device, const VKShaderInterface &shader_interface);
bool finalize_graphics_pipeline(VkDevice vk_device);
bool is_graphics_shader() const
{
@ -93,4 +96,14 @@ class VKShader : public Shader {
}
};
static inline VKShader &unwrap(Shader &shader)
{
return static_cast<VKShader &>(shader);
}
static inline VKShader *unwrap(Shader *shader)
{
return static_cast<VKShader *>(shader);
}
} // namespace blender::gpu

View File

@ -6,12 +6,67 @@
*/
#include "vk_state_manager.hh"
#include "vk_context.hh"
#include "vk_pipeline.hh"
#include "vk_shader.hh"
#include "vk_texture.hh"
namespace blender::gpu {
void VKStateManager::apply_state() {}
#include "GPU_capabilities.h"
void VKStateManager::force_state() {}
namespace blender::gpu {
VKStateManager::VKStateManager()
{
sampler_.create();
constexpr int max_bindings = 16;
image_bindings_ = Array<ImageBinding>(max_bindings);
image_bindings_.fill(ImageBinding());
texture_bindings_ = Array<ImageBinding>(max_bindings);
texture_bindings_.fill(ImageBinding());
uniform_buffer_bindings_ = Array<UniformBufferBinding>(max_bindings);
uniform_buffer_bindings_.fill(UniformBufferBinding());
}
void VKStateManager::apply_state()
{
VKContext &context = *VKContext::get();
if (context.shader) {
VKShader &shader = unwrap(*context.shader);
VKPipeline &pipeline = shader.pipeline_get();
pipeline.state_manager_get().set_state(state, mutable_state);
for (int binding : IndexRange(image_bindings_.size())) {
if (image_bindings_[binding].texture == nullptr) {
continue;
}
image_bindings_[binding].texture->image_bind(binding);
}
for (int binding : IndexRange(image_bindings_.size())) {
if (texture_bindings_[binding].texture == nullptr) {
continue;
}
texture_bindings_[binding].texture->bind(binding, sampler_);
}
for (int binding : IndexRange(uniform_buffer_bindings_.size())) {
if (uniform_buffer_bindings_[binding].buffer == nullptr) {
continue;
}
uniform_buffer_bindings_[binding].buffer->bind(
binding, shader::ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER);
}
}
}
void VKStateManager::force_state()
{
VKContext &context = *VKContext::get();
BLI_assert(context.shader);
VKShader &shader = unwrap(*context.shader);
VKPipeline &pipeline = shader.pipeline_get();
pipeline.state_manager_get().force_state(state, mutable_state);
}
void VKStateManager::issue_barrier(eGPUBarrier /*barrier_bits*/)
{
@ -22,21 +77,69 @@ void VKStateManager::issue_barrier(eGPUBarrier /*barrier_bits*/)
command_buffer.submit();
}
void VKStateManager::texture_bind(Texture * /*tex*/, GPUSamplerState /*sampler*/, int /*unit*/) {}
void VKStateManager::texture_bind(Texture *tex, GPUSamplerState /*sampler*/, int unit)
{
VKTexture *texture = unwrap(tex);
texture_bindings_[unit].texture = texture;
}
void VKStateManager::texture_unbind(Texture * /*tex*/) {}
void VKStateManager::texture_unbind(Texture *tex)
{
VKTexture *texture = unwrap(tex);
for (ImageBinding &binding : texture_bindings_) {
if (binding.texture == texture) {
binding.texture = nullptr;
}
}
}
void VKStateManager::texture_unbind_all() {}
void VKStateManager::texture_unbind_all()
{
for (ImageBinding &binding : texture_bindings_) {
if (binding.texture != nullptr) {
binding.texture = nullptr;
}
}
}
void VKStateManager::image_bind(Texture *tex, int binding)
{
VKTexture *texture = unwrap(tex);
texture->image_bind(binding);
image_bindings_[binding].texture = texture;
}
void VKStateManager::image_unbind(Texture * /*tex*/) {}
void VKStateManager::image_unbind(Texture *tex)
{
VKTexture *texture = unwrap(tex);
for (ImageBinding &binding : image_bindings_) {
if (binding.texture == texture) {
binding.texture = nullptr;
}
}
}
void VKStateManager::image_unbind_all() {}
void VKStateManager::image_unbind_all()
{
for (ImageBinding &binding : texture_bindings_) {
if (binding.texture != nullptr) {
binding.texture = nullptr;
}
}
}
void VKStateManager::uniform_buffer_bind(VKUniformBuffer *uniform_buffer, int slot)
{
uniform_buffer_bindings_[slot].buffer = uniform_buffer;
}
void VKStateManager::uniform_buffer_unbind(VKUniformBuffer *uniform_buffer)
{
for (UniformBufferBinding &binding : uniform_buffer_bindings_) {
if (binding.buffer == uniform_buffer) {
binding.buffer = nullptr;
}
}
}
void VKStateManager::texture_unpack_row_length_set(uint len)
{

View File

@ -9,11 +9,33 @@
#include "gpu_state_private.hh"
#include "BLI_array.hh"
#include "vk_sampler.hh"
namespace blender::gpu {
class VKTexture;
class VKUniformBuffer;
class VKStateManager : public StateManager {
uint texture_unpack_row_length_;
/* Dummy sampler for now.*/
VKSampler sampler_;
uint texture_unpack_row_length_ = 0;
struct ImageBinding {
VKTexture *texture = nullptr;
};
struct UniformBufferBinding {
VKUniformBuffer *buffer = nullptr;
};
Array<ImageBinding> image_bindings_;
Array<ImageBinding> texture_bindings_;
Array<UniformBufferBinding> uniform_buffer_bindings_;
public:
VKStateManager();
void apply_state() override;
void force_state() override;
@ -27,6 +49,9 @@ class VKStateManager : public StateManager {
void image_unbind(Texture *tex) override;
void image_unbind_all() override;
void uniform_buffer_bind(VKUniformBuffer *uniform_buffer, int slot);
void uniform_buffer_unbind(VKUniformBuffer *uniform_buffer);
void texture_unpack_row_length_set(uint len) override;
/**

View File

@ -229,6 +229,16 @@ static VkImageUsageFlagBits to_vk_image_usage(const eGPUTextureUsage usage,
result = static_cast<VkImageUsageFlagBits>(result | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
}
/* 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 = static_cast<VkImageUsageFlagBits>(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 = static_cast<VkImageUsageFlagBits>(result & ~VK_IMAGE_USAGE_STORAGE_BIT);
}
return result;
}
@ -311,6 +321,24 @@ bool VKTexture::allocate()
return result == VK_SUCCESS;
}
// TODO: move texture/image bindings to shader.
void VKTexture::bind(int unit, VKSampler &sampler)
{
if (!is_allocated()) {
allocate();
}
VKContext &context = *VKContext::get();
VKShader *shader = static_cast<VKShader *>(context.shader);
const VKShaderInterface &shader_interface = shader->interface_get();
const std::optional<VKDescriptorSet::Location> location =
shader_interface.descriptor_set_location(
shader::ShaderCreateInfo::Resource::BindType::SAMPLER, unit);
if (location) {
VKDescriptorSetTracker &descriptor_set = shader->pipeline_get().descriptor_set_get();
descriptor_set.bind(*this, *location, sampler);
}
}
void VKTexture::image_bind(int binding)
{
if (!is_allocated()) {

View File

@ -12,6 +12,8 @@
namespace blender::gpu {
class VKSampler;
class VKTexture : public Texture {
VkImage vk_image_ = VK_NULL_HANDLE;
VkImageView vk_image_view_ = VK_NULL_HANDLE;
@ -25,6 +27,7 @@ class VKTexture : public Texture {
public:
VKTexture(const char *name) : Texture(name) {}
virtual ~VKTexture() override;
void init(VkImage vk_image, VkImageLayout layout);
@ -46,7 +49,9 @@ class VKTexture : public Texture {
/* TODO(fclem): Legacy. Should be removed at some point. */
uint gl_bindcode_get() const override;
void bind(int unit, VKSampler &sampler);
void image_bind(int location);
VkImage vk_image_handle() const
{
BLI_assert(vk_image_ != VK_NULL_HANDLE);

View File

@ -9,6 +9,7 @@
#include "vk_context.hh"
#include "vk_shader.hh"
#include "vk_shader_interface.hh"
#include "vk_state_manager.hh"
namespace blender::gpu {
@ -54,7 +55,9 @@ void VKUniformBuffer::bind(int slot, shader::ShaderCreateInfo::Resource::BindTyp
void VKUniformBuffer::bind(int slot)
{
bind(slot, shader::ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER);
/* Uniform buffers can be bound without an shader. */
VKContext &context = *VKContext::get();
context.state_manager_get().uniform_buffer_bind(this, slot);
}
void VKUniformBuffer::bind_as_ssbo(int slot)
@ -62,6 +65,10 @@ void VKUniformBuffer::bind_as_ssbo(int slot)
bind(slot, shader::ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER);
}
void VKUniformBuffer::unbind() {}
void VKUniformBuffer::unbind()
{
VKContext &context = *VKContext::get();
context.state_manager_get().uniform_buffer_unbind(this);
}
} // namespace blender::gpu

View File

@ -25,6 +25,7 @@ class VKUniformBuffer : public UniformBuf, NonCopyable {
void clear_to_zero() override;
void bind(int slot) override;
void bind_as_ssbo(int slot) override;
void bind(int slot, shader::ShaderCreateInfo::Resource::BindType bind_type);
void unbind() override;
VkBuffer vk_handle() const
@ -39,7 +40,6 @@ class VKUniformBuffer : public UniformBuf, NonCopyable {
private:
void allocate();
void bind(int slot, shader::ShaderCreateInfo::Resource::BindType bind_type);
};
} // namespace blender::gpu

View File

@ -0,0 +1,198 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2023 Blender Foundation. All rights reserved. */
#include "vk_vertex_attribute_object.hh"
#include "vk_batch.hh"
#include "vk_context.hh"
#include "vk_immediate.hh"
#include "vk_shader.hh"
#include "vk_shader_interface.hh"
#include "vk_vertex_buffer.hh"
#include "BLI_array.hh"
namespace blender::gpu {
VKVertexAttributeObject::VKVertexAttributeObject()
{
clear();
}
void VKVertexAttributeObject::clear()
{
is_valid = false;
info.pNext = NULL;
bindings.clear();
attributes.clear();
vbos.clear();
buffers.clear();
}
VKVertexAttributeObject &VKVertexAttributeObject::operator=(const VKVertexAttributeObject &other)
{
if (this == &other) {
return *this;
}
is_valid = other.is_valid;
info = other.info;
bindings.clear();
bindings.extend(other.bindings);
attributes.clear();
attributes.extend(other.attributes);
vbos.clear();
vbos.extend(other.vbos);
buffers.clear();
buffers.extend(other.buffers);
return *this;
}
void VKVertexAttributeObject::bind(VKContext &context)
{
Array<bool> visited_bindings(bindings.size());
visited_bindings.fill(false);
for (VkVertexInputAttributeDescription attribute : attributes) {
if (visited_bindings[attribute.binding]) {
continue;
}
visited_bindings[attribute.binding] = true;
/* Bind VBOS from batches. */
if (attribute.binding < vbos.size()) {
BLI_assert(vbos[attribute.binding]);
VKVertexBuffer &vbo = *vbos[attribute.binding];
vbo.upload();
context.command_buffer_get().bind(attribute.binding, vbo, 0);
}
/* Bind dynamic buffers from immediate mode. */
if (attribute.binding < buffers.size()) {
VKBufferWithOffset &buffer = buffers[attribute.binding];
context.command_buffer_get().bind(attribute.binding, buffer);
}
}
}
void VKVertexAttributeObject::update_bindings(const VKContext &context, VKBatch &batch)
{
clear();
const VKShaderInterface &interface = unwrap(context.shader)->interface_get();
AttributeMask occupied_attributes = 0;
for (int v = 0; v < GPU_BATCH_INST_VBO_MAX_LEN; v++) {
VKVertexBuffer *vbo = batch.instance_buffer_get(v);
if (vbo) {
update_bindings(
vbo->format, vbo, nullptr, vbo->vertex_len, interface, occupied_attributes, true);
}
}
for (int v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) {
VKVertexBuffer *vbo = batch.vertex_buffer_get(v);
if (vbo) {
update_bindings(
vbo->format, vbo, nullptr, vbo->vertex_len, interface, occupied_attributes, false);
}
}
is_valid = true;
BLI_assert(interface.enabled_attr_mask_ == occupied_attributes);
}
void VKVertexAttributeObject::update_bindings(VKImmediate &immediate)
{
clear();
const VKShaderInterface &interface = unwrap(unwrap(immediate.shader))->interface_get();
AttributeMask occupied_attributes = 0;
VKBufferWithOffset immediate_buffer = {*immediate.active_resource(),
immediate.subbuffer_offset_get()};
update_bindings(immediate.vertex_format,
nullptr,
&immediate_buffer,
immediate.vertex_len,
interface,
occupied_attributes,
false);
is_valid = true;
BLI_assert(interface.enabled_attr_mask_ == occupied_attributes);
}
void VKVertexAttributeObject::update_bindings(const GPUVertFormat &vertex_format,
VKVertexBuffer *vertex_buffer,
VKBufferWithOffset *immediate_vertex_buffer,
const int64_t vertex_len,
const VKShaderInterface &interface,
AttributeMask &r_occupied_attributes,
const bool use_instancing)
{
BLI_assert(vertex_buffer || immediate_vertex_buffer);
BLI_assert(!(vertex_buffer && immediate_vertex_buffer));
if (vertex_format.attr_len <= 0) {
return;
}
uint32_t offset = 0;
uint32_t stride = vertex_format.stride;
for (uint32_t attribute_index = 0; attribute_index < vertex_format.attr_len; attribute_index++) {
const GPUVertAttr &attribute = vertex_format.attrs[attribute_index];
if (vertex_format.deinterleaved) {
offset += ((attribute_index == 0) ? 0 : vertex_format.attrs[attribute_index - 1].size) *
vertex_len;
stride = attribute.size;
}
else {
offset = attribute.offset;
}
const uint32_t binding = bindings.size();
bool attribute_used_by_shader = false;
for (uint32_t name_index = 0; name_index < attribute.name_len; name_index++) {
const char *name = GPU_vertformat_attr_name_get(&vertex_format, &attribute, name_index);
const ShaderInput *shader_input = interface.attr_get(name);
if (shader_input == nullptr || shader_input->location == -1) {
continue;
}
/* Don't overwrite attributes that are already occupied. */
AttributeMask attribute_mask = 1 << shader_input->location;
if (r_occupied_attributes & attribute_mask) {
continue;
}
r_occupied_attributes |= attribute_mask;
attribute_used_by_shader = true;
VkVertexInputAttributeDescription attribute_description = {};
attribute_description.binding = binding;
attribute_description.location = shader_input->location;
attribute_description.offset = offset;
attribute_description.format = to_vk_format(
static_cast<GPUVertCompType>(attribute.comp_type),
attribute.size,
static_cast<GPUVertFetchMode>(attribute.fetch_mode));
attributes.append(attribute_description);
}
if (attribute_used_by_shader) {
VkVertexInputBindingDescription vk_binding_descriptor = {};
vk_binding_descriptor.binding = binding;
vk_binding_descriptor.stride = stride;
vk_binding_descriptor.inputRate = use_instancing ? VK_VERTEX_INPUT_RATE_INSTANCE :
VK_VERTEX_INPUT_RATE_VERTEX;
bindings.append(vk_binding_descriptor);
if (vertex_buffer) {
vbos.append(vertex_buffer);
}
if (immediate_vertex_buffer) {
buffers.append(*immediate_vertex_buffer);
}
}
}
}
} // namespace blender::gpu

View File

@ -0,0 +1,58 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2023 Blender Foundation. All rights reserved. */
/** \file
* \ingroup gpu
*/
#include "vk_buffer.hh"
#include "vk_common.hh"
#include "BLI_vector.hh"
#pragma once
namespace blender::gpu {
class VKVertexBuffer;
class VKContext;
class VKBatch;
class VKShaderInterface;
class VKImmediate;
using AttributeMask = uint16_t;
struct VKVertexAttributeObject {
bool is_valid = false;
VkPipelineVertexInputStateCreateInfo info = {
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, NULL};
Vector<VkVertexInputBindingDescription> bindings;
Vector<VkVertexInputAttributeDescription> attributes;
/* Used for batches. */
Vector<VKVertexBuffer *> vbos;
/* Used for immediate mode. */
Vector<VKBufferWithOffset> buffers;
VKVertexAttributeObject();
void clear();
void bind(VKContext &context);
// Copy assignment operator.
VKVertexAttributeObject &operator=(const VKVertexAttributeObject &other);
void update_bindings(const VKContext &context, VKBatch &batch);
void update_bindings(VKImmediate &immediate);
private:
void update_bindings(const GPUVertFormat &vertex_format,
VKVertexBuffer *vertex_buffer,
VKBufferWithOffset *immediate_vertex_buffer,
const int64_t vertex_len,
const VKShaderInterface &interface,
AttributeMask &r_occupied_attributes,
const bool use_instancing);
};
} // namespace blender::gpu

View File

@ -56,6 +56,7 @@ void VKVertexBuffer::acquire_data()
}
/* Discard previous data if any. */
/* TODO: Use mapped memory. */
MEM_SAFE_FREE(data);
data = (uchar *)MEM_mallocN(sizeof(uchar) * this->size_alloc_get(), __func__);
}

View File

@ -28,6 +28,7 @@ class VKVertexBuffer : public VertBuf {
VkBuffer vk_handle() const
{
BLI_assert(buffer_.is_allocated());
return buffer_.vk_handle();
}