Vulkan: Initial Graphics Pipeline #106224
@ -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
|
||||
)
|
||||
|
||||
|
@ -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
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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
Jeroen Bakker
commented
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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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_);
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
88
source/blender/gpu/vulkan/vk_immediate.cc
Normal file
88
source/blender/gpu/vulkan/vk_immediate.cc
Normal 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
|
51
source/blender/gpu/vulkan/vk_immediate.hh
Normal file
51
source/blender/gpu/vulkan/vk_immediate.hh
Normal 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
|
@ -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;
|
||||
}
|
||||
|
||||
Jeroen Bakker
commented
Don't pass VKBatch, but 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
|
||||
|
@ -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
|
||||
|
367
source/blender/gpu/vulkan/vk_pipeline_state.cc
Normal file
367
source/blender/gpu/vulkan/vk_pipeline_state.cc
Normal 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;
|
||||
Add late binding of compute pipeline