Vulkan: Initial Graphics Pipeline #106224

Merged
Jeroen Bakker merged 104 commits from Jeroen-Bakker/blender:vulkan-offscreen-rendering into main 2023-05-11 13:02:03 +02:00
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();
Jeroen-Bakker marked this conversation as resolved
Review

Add late binding of compute pipeline

Add late binding of 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)
Jeroen-Bakker marked this conversation as resolved Outdated

Use full names to make a difference between indexed and instanced.

Use full names to make a difference between indexed and instanced.
{
/* 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;
}

Don't pass VKBatch, but GPUPrimType. This makes the function also usable when using immediate mode.

Don't pass VKBatch, but `GPUPrimType`. This makes the function also usable when using immediate mode.
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)

This should be done as part of this change.

This should be done as part of this change.
{
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();
}