Vulkan: Push constants #104880

Merged
Jeroen Bakker merged 73 commits from Jeroen-Bakker/blender:vulkan-push-constants into main 2023-03-06 12:29:06 +01:00
9 changed files with 307 additions and 26 deletions
Showing only changes of commit fda61f18d1 - Show all commits

View File

@ -16,6 +16,8 @@
# include <vulkan/vulkan.h>
#endif
#include "vk_mem_alloc.h"
namespace blender::gpu {
/**
@ -40,8 +42,8 @@ class VKBuffer {
bool update(VKContext &context, const void *data);
// TODO: add partial_update (update_sub)
bool free(VKContext &context);
bool map(VKContext &context, void **r_mapped_memory)const;
void unmap(VKContext &context)const ;
bool map(VKContext &context, void **r_mapped_memory) const;
void unmap(VKContext &context) const;
int64_t size_in_bytes() const
{

View File

@ -6,8 +6,10 @@
*/
#include "vk_command_buffer.hh"
#include "vk_buffer.hh"
#include "vk_context.hh"
#include "vk_memory.hh"
#include "vk_texture.hh"
#include "BLI_assert.h"
@ -68,6 +70,47 @@ void VKCommandBuffer::bind(const VKDescriptorSet &descriptor_set,
vk_command_buffer_, bind_point, vk_pipeline_layout, 0, 1, &vk_descriptor_set, 0, 0);
}
void VKCommandBuffer::copy(VKBuffer &dst_buffer,
VKTexture &src_texture,
Span<VkBufferImageCopy> regions)
{
vkCmdCopyImageToBuffer(vk_command_buffer_,
src_texture.vk_image_handle(),
VK_IMAGE_LAYOUT_GENERAL,
dst_buffer.vk_handle(),
regions.size(),
regions.data());
}
void VKCommandBuffer::pipeline_barrier(VkPipelineStageFlags source_stages,
VkPipelineStageFlags destination_stages)
{
vkCmdPipelineBarrier(vk_command_buffer_,
source_stages,
destination_stages,
0,
0,
nullptr,
0,
nullptr,
0,
nullptr);
}
void VKCommandBuffer::pipeline_barrier(Span<VkImageMemoryBarrier> image_memory_barriers)
{
vkCmdPipelineBarrier(vk_command_buffer_,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_DEPENDENCY_BY_REGION_BIT,
0,
nullptr,
0,
nullptr,
image_memory_barriers.size(),
image_memory_barriers.data());
}
void VKCommandBuffer::dispatch(int groups_x_len, int groups_y_len, int groups_z_len)
{
vkCmdDispatch(vk_command_buffer_, groups_x_len, groups_y_len, groups_z_len);

View File

@ -16,6 +16,8 @@
#include "vk_pipeline.hh"
namespace blender::gpu {
class VKBuffer;
class VKTexture;
/** Command buffer to keep track of the life-time of a command buffer.*/
class VKCommandBuffer : NonCopyable, NonMovable {
@ -37,6 +39,11 @@ class VKCommandBuffer : NonCopyable, NonMovable {
const VkPipelineLayout vk_pipeline_layout,
VkPipelineBindPoint bind_point);
void dispatch(int groups_x_len, int groups_y_len, int groups_z_len);
/* Copy the contents of a texture mip level to the dst buffer.*/
void copy(VKBuffer &dst_buffer, VKTexture &src_texture, Span<VkBufferImageCopy> regions);
void pipeline_barrier(VkPipelineStageFlags source_stages,
VkPipelineStageFlags destination_stages);
void pipeline_barrier(Span<VkImageMemoryBarrier> image_memory_barriers);
/**
* Stop recording commands, encode + send the recordings to Vulkan, wait for the until the

View File

@ -79,10 +79,12 @@ void VKContext::end_frame()
void VKContext::flush()
{
command_buffer_.submit();
}
void VKContext::finish()
{
command_buffer_.submit();
}
void VKContext::memory_statistics_get(int * /*total_mem*/, int * /*free_mem*/)

View File

@ -56,6 +56,9 @@ void VKDescriptorSet::bind_as_ssbo(VKIndexBuffer &buffer, int location)
void VKDescriptorSet::image_bind(VKTexture &texture, int location)
{
Binding &binding = ensure_location(location);
binding.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
binding.vk_image_view = texture.vk_image_view_handle();
}
VKDescriptorSet::Binding &VKDescriptorSet::ensure_location(int location)
@ -66,9 +69,7 @@ VKDescriptorSet::Binding &VKDescriptorSet::ensure_location(int location)
}
}
Binding binding;
binding.vk_buffer = VK_NULL_HANDLE;
binding.buffer_size = 0;
Binding binding = {};
binding.location = location;
bindings_.append(binding);
return bindings_.last();
@ -78,7 +79,11 @@ void VKDescriptorSet::update(VkDevice vk_device)
{
Vector<VkDescriptorBufferInfo> buffer_infos;
Vector<VkWriteDescriptorSet> descriptor_writes;
for (const Binding &binding : bindings_) {
if (!binding.is_buffer()) {
continue;
}
VkDescriptorBufferInfo buffer_info = {};
buffer_info.buffer = binding.vk_buffer;
buffer_info.range = binding.buffer_size;
@ -91,7 +96,26 @@ void VKDescriptorSet::update(VkDevice vk_device)
write_descriptor.descriptorCount = 1;
write_descriptor.descriptorType = binding.type;
write_descriptor.pBufferInfo = &buffer_infos.last();
descriptor_writes.append(write_descriptor);
}
Vector<VkDescriptorImageInfo> image_infos;
for (const Binding &binding : bindings_) {
if (!binding.is_image()) {
continue;
}
VkDescriptorImageInfo image_info = {};
image_info.imageView = binding.vk_image_view;
image_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
image_infos.append(image_info);
VkWriteDescriptorSet write_descriptor = {};
write_descriptor.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_descriptor.dstSet = vk_descriptor_set_;
write_descriptor.dstBinding = binding.location;
write_descriptor.descriptorCount = 1;
write_descriptor.descriptorType = binding.type;
write_descriptor.pImageInfo = &image_infos.last();
descriptor_writes.append(write_descriptor);
}

View File

@ -24,10 +24,23 @@ class VKTexture;
class VKDescriptorSet : NonCopyable {
struct Binding {
int location;
int location = -1;
VkDescriptorType type;
VkBuffer vk_buffer = VK_NULL_HANDLE;
VkDeviceSize buffer_size;
VkDeviceSize buffer_size = 0;
VkImageView vk_image_view = VK_NULL_HANDLE;
bool is_buffer() const
{
return ELEM(type, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
}
bool is_image() const
{
return ELEM(type, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
}
};
VkDescriptorPool vk_descriptor_pool_ = VK_NULL_HANDLE;

View File

@ -19,6 +19,11 @@ void VKStateManager::force_state()
void VKStateManager::issue_barrier(eGPUBarrier /*barrier_bits*/)
{
VKContext &context = *VKContext::get();
VKCommandBuffer &command_buffer = context.command_buffer_get();
/* TODO: Pipeline barriers should be added. We might be able to extract it from
* the actual pipeline, later on, but for now we submit the work as barrier. */
command_buffer.submit();
}
void VKStateManager::texture_bind(Texture * /*tex*/, eGPUSamplerState /*sampler*/, int /*unit*/)

View File

@ -7,15 +7,81 @@
#include "vk_texture.hh"
#include "vk_buffer.hh"
#include "vk_context.hh"
#include "vk_memory.hh"
#include "vk_shader.hh"
namespace blender::gpu {
static VkImageAspectFlagBits to_vk_image_aspect_flag_bits(const eGPUTextureFormat format)
{
switch (format) {
case GPU_RGBA32F:
case GPU_RGBA8UI:
case GPU_RGBA8I:
case GPU_RGBA8:
case GPU_RGBA32UI:
case GPU_RGBA32I:
case GPU_RGBA16UI:
case GPU_RGBA16I:
case GPU_RGBA16F:
case GPU_RGBA16:
case GPU_RG8UI:
case GPU_RG8I:
case GPU_RG8:
case GPU_RG32UI:
case GPU_RG32I:
case GPU_RG32F:
case GPU_RG16UI:
case GPU_RG16I:
case GPU_RG16F:
case GPU_RG16:
case GPU_R8UI:
case GPU_R8I:
case GPU_R8:
case GPU_R32UI:
case GPU_R32I:
case GPU_R32F:
case GPU_R16UI:
case GPU_R16I:
case GPU_R16F:
case GPU_R16:
case GPU_RGB10_A2:
case GPU_R11F_G11F_B10F:
case GPU_SRGB8_A8:
case GPU_RGB16F:
case GPU_SRGB8_A8_DXT1:
case GPU_SRGB8_A8_DXT3:
case GPU_SRGB8_A8_DXT5:
case GPU_RGBA8_DXT1:
case GPU_RGBA8_DXT3:
case GPU_RGBA8_DXT5:
return VK_IMAGE_ASPECT_COLOR_BIT;
case GPU_DEPTH32F_STENCIL8:
case GPU_DEPTH24_STENCIL8:
return static_cast<VkImageAspectFlagBits>(VK_IMAGE_ASPECT_DEPTH_BIT |
VK_IMAGE_ASPECT_STENCIL_BIT);
case GPU_DEPTH_COMPONENT24:
case GPU_DEPTH_COMPONENT16:
return VK_IMAGE_ASPECT_DEPTH_BIT;
case GPU_DEPTH_COMPONENT32F:
/* Not supported by Vulkan*/
BLI_assert_unreachable();
}
return static_cast<VkImageAspectFlagBits>(0);
}
VKTexture::~VKTexture()
{
VK_ALLOCATION_CALLBACKS
VKContext &context = *VKContext::get();
vmaDestroyImage(context.mem_allocator_get(), vk_image_, allocation_);
vkDestroyImageView(context.device_get(), vk_image_view_, vk_allocation_callbacks);
}
void VKTexture::generate_mipmap()
@ -42,9 +108,49 @@ void VKTexture::mip_range_set(int /*min*/, int /*max*/)
{
}
void *VKTexture::read(int /*mip*/, eGPUDataFormat /*format*/)
void *VKTexture::read(int mip, eGPUDataFormat format)
{
return nullptr;
/* Vulkan images cannot be directly mapped to host memory and requires a staging buffer.*/
VKContext &context = *VKContext::get();
VKBuffer staging_buffer;
/* NOTE: mip_size_get() won't override any dimension that is equal to 0. */
int extent[3] = {1, 1, 1};
mip_size_get(mip, extent);
size_t sample_len = extent[0] * extent[1] * extent[2];
/* NOTE: to_bytesize returns number of bits. */
size_t device_memory_size = sample_len * to_component_len(format_) * to_bytesize(format_) / 8;
/* NOTE: to_bytesize returns number of bytes here. */
size_t host_memory_size = sample_len * to_bytesize(format_, format);
staging_buffer.create(
context, device_memory_size, GPU_USAGE_DEVICE_ONLY, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
VkBufferImageCopy region = {};
region.imageExtent.width = extent[0];
region.imageExtent.height = extent[1];
region.imageExtent.depth = extent[2];
region.imageSubresource.aspectMask = to_vk_image_aspect_flag_bits(format_);
region.imageSubresource.mipLevel = mip;
region.imageSubresource.layerCount = 1;
VKCommandBuffer &command_buffer = context.command_buffer_get();
command_buffer.copy(staging_buffer, *this, Span<VkBufferImageCopy>(&region, 1));
command_buffer.submit();
void *mapped_data;
staging_buffer.map(context, &mapped_data);
void *data = MEM_mallocN(host_memory_size, __func__);
/* TODO: add conversion when data format is different.*/
BLI_assert_msg(device_memory_size == host_memory_size,
"Memory data conversions not implemented yet");
memcpy(data, mapped_data, host_memory_size);
staging_buffer.unmap(context);
return data;
}
void VKTexture::update_sub(int /*mip*/,
@ -103,12 +209,11 @@ static VkFormat to_vk_format(const eGPUTextureFormat format)
case GPU_R16F:
case GPU_R16:
/*
case GPU_RGB10_A2:
case GPU_R11F_G11F_B10F:
case GPU_DEPTH32F_STENCIL8:
case GPU_DEPTH24_STENCIL8:
case GPU_SRGB8_A8:*/
case GPU_SRGB8_A8:
/* Texture only format */
case GPU_RGB16F:
@ -125,7 +230,6 @@ static VkFormat to_vk_format(const eGPUTextureFormat format)
case GPU_DEPTH_COMPONENT32F:
case GPU_DEPTH_COMPONENT24:
case GPU_DEPTH_COMPONENT16:
default:
BLI_assert_unreachable();
}
return VK_FORMAT_UNDEFINED;
@ -152,23 +256,66 @@ bool VKTexture::is_allocated()
return vk_image_ != VK_NULL_HANDLE && allocation_ != VK_NULL_HANDLE;
}
static VkImageViewType to_vk_image_view_type(const eGPUTextureType type)
{
switch (type) {
case GPU_TEXTURE_1D:
case GPU_TEXTURE_BUFFER:
return VK_IMAGE_VIEW_TYPE_1D;
case GPU_TEXTURE_2D:
return VK_IMAGE_VIEW_TYPE_2D;
case GPU_TEXTURE_3D:
return VK_IMAGE_VIEW_TYPE_3D;
case GPU_TEXTURE_CUBE:
return VK_IMAGE_VIEW_TYPE_CUBE;
case GPU_TEXTURE_1D_ARRAY:
return VK_IMAGE_VIEW_TYPE_1D_ARRAY;
case GPU_TEXTURE_2D_ARRAY:
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
case GPU_TEXTURE_CUBE_ARRAY:
return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
case GPU_TEXTURE_ARRAY:
/* GPU_TEXTURE_ARRAY should always be used together with 1D, 2D, or CUBE*/
BLI_assert_unreachable();
break;
}
return VK_IMAGE_VIEW_TYPE_1D;
}
static VkComponentMapping to_vk_component_mapping(const eGPUTextureFormat /*format*/)
{
/* TODO: this should map to OpenGL defaults based on the eGPUTextureFormat*/
VkComponentMapping component_mapping;
component_mapping.r = VK_COMPONENT_SWIZZLE_R;
component_mapping.g = VK_COMPONENT_SWIZZLE_G;
component_mapping.b = VK_COMPONENT_SWIZZLE_B;
component_mapping.a = VK_COMPONENT_SWIZZLE_A;
return component_mapping;
}
bool VKTexture::allocate()
{
BLI_assert(!is_allocated());
VKContext &context = *VKContext::get();
VkImageCreateInfo imgCreateInfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
imgCreateInfo.imageType = VK_IMAGE_TYPE_1D;
imgCreateInfo.extent.width = width_get();
imgCreateInfo.extent.height = 1;
imgCreateInfo.extent.depth = 1;
imgCreateInfo.mipLevels = 1;
imgCreateInfo.arrayLayers = 1;
imgCreateInfo.format = to_vk_format(format_);
imgCreateInfo.tiling = VK_IMAGE_TILING_LINEAR;
imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT;
imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
VkImageCreateInfo image_info = {};
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image_info.imageType = VK_IMAGE_TYPE_1D;
image_info.extent.width = width_get();
image_info.extent.height = 1;
image_info.extent.depth = 1;
image_info.mipLevels = 1;
image_info.arrayLayers = 1;
image_info.format = to_vk_format(format_);
image_info.tiling = VK_IMAGE_TILING_LINEAR;
/* TODO: PREINITIALIZED should be for device only textures. Others should use
* VK_IMAGE_LAYOUT_UNDEFINED and upload its content.*/
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
image_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_STORAGE_BIT;
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
@ -177,7 +324,7 @@ bool VKTexture::allocate()
allocCreateInfo.priority = 1.0f;
VkResult result = vmaCreateImage(context.mem_allocator_get(),
&imgCreateInfo,
&image_info,
&allocCreateInfo,
&vk_image_,
&allocation_,
@ -185,7 +332,32 @@ bool VKTexture::allocate()
if (result != VK_SUCCESS) {
return false;
}
return true;
/* Promote image to the correct layout.*/
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
barrier.image = vk_image_;
barrier.subresourceRange.aspectMask = to_vk_image_aspect_flag_bits(format_);
barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
context.command_buffer_get().pipeline_barrier(Span<VkImageMemoryBarrier>(&barrier, 1));
VK_ALLOCATION_CALLBACKS
VkImageViewCreateInfo image_view_info = {};
image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
image_view_info.image = vk_image_;
image_view_info.viewType = to_vk_image_view_type(type_);
image_view_info.format = to_vk_format(format_);
image_view_info.components = to_vk_component_mapping(format_);
image_view_info.subresourceRange.aspectMask = to_vk_image_aspect_flag_bits(format_);
image_view_info.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
image_view_info.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
result = vkCreateImageView(
context.device_get(), &image_view_info, vk_allocation_callbacks, &vk_image_view_);
return result == VK_SUCCESS;
}
void VKTexture::image_bind(int location)

View File

@ -10,10 +10,13 @@
#include "gpu_texture_private.hh"
#include "vk_context.hh"
#include "vk_mem_alloc.h"
namespace blender::gpu {
class VKTexture : public Texture {
VkImage vk_image_ = VK_NULL_HANDLE;
VkImageView vk_image_view_ = VK_NULL_HANDLE;
VmaAllocation allocation_ = VK_NULL_HANDLE;
public:
@ -40,6 +43,14 @@ class VKTexture : public Texture {
uint gl_bindcode_get() const override;
void image_bind(int location);
VkImage vk_image_handle() const
{
return vk_image_;
}
VkImageView vk_image_view_handle() const
{
return vk_image_view_;
}
protected:
bool init_internal() override;
@ -54,6 +65,8 @@ class VKTexture : public Texture {
* on the device.
*/
bool allocate();
VkImageViewType vk_image_view_type() const;
};
static inline VKTexture *unwrap(Texture *tex)