Vulkan: Add Support For Texture Buffers #108193

Merged
Jeroen Bakker merged 3 commits from Jeroen-Bakker/blender:vulkan-buffer-texture into main 2023-05-30 13:54:57 +02:00
15 changed files with 278 additions and 37 deletions

View File

@ -569,6 +569,7 @@ set(GLSL_SRC
set(GLSL_SRC_TEST
tests/shaders/gpu_math_test.glsl
tests/shaders/gpu_buffer_texture_test.glsl
tests/shaders/gpu_compute_1d_test.glsl
tests/shaders/gpu_compute_2d_test.glsl
tests/shaders/gpu_compute_ibo_test.glsl
@ -850,6 +851,7 @@ if(WITH_GTESTS)
set(TEST_SRC
tests/gpu_testing.cc
tests/buffer_texture_test.cc
tests/framebuffer_test.cc
tests/immediate_test.cc
tests/index_buffer_test.cc

View File

@ -85,6 +85,13 @@ GPU_SHADER_CREATE_INFO(gpu_push_constants_512bytes_test)
.push_constant(Type::FLOAT, "filler3", 64)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(gpu_buffer_texture_test)
.local_group_size(1)
.sampler(0, ImageType::FLOAT_BUFFER, "bufferTexture")
.storage_buf(0, Qualifier::WRITE, "float", "data_out[]")
.compute_source("gpu_buffer_texture_test.glsl")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_shadow_test)
.fragment_source("eevee_shadow_test.glsl")
.additional_info("gpu_shader_test")

View File

@ -0,0 +1,66 @@
/* SPDX-License-Identifier: Apache-2.0 */
#include "testing/testing.h"
#include "GPU_capabilities.h"
#include "GPU_compute.h"
#include "GPU_vertex_buffer.h"
#include "GPU_vertex_format.h"
#include "BLI_index_range.hh"
#include "BLI_math_vector_types.hh"
#include "gpu_testing.hh"
namespace blender::gpu::tests {
static void test_buffer_texture()
Jeroen-Bakker marked this conversation as resolved
Review

Remove unused const

Remove unused const
{
if (!GPU_compute_shader_support() && !GPU_shader_storage_buffer_objects_support()) {
/* We can't test as a the platform does not support compute shaders. */
std::cout << "Skipping compute shader test: platform not supported";
GTEST_SKIP();
}
/* Build compute shader. */
GPUShader *shader = GPU_shader_create_from_info_name("gpu_buffer_texture_test");
EXPECT_NE(shader, nullptr);
GPU_shader_bind(shader);
/* Vertex buffer. */
GPUVertFormat format = {};
uint value_pos = GPU_vertformat_attr_add(&format, "value", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
GPUVertBuf *vertex_buffer = GPU_vertbuf_create_with_format_ex(
&format, GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
float4 value = float4(42.42, 23.23, 1.0, -1.0);
GPU_vertbuf_data_alloc(vertex_buffer, 4);
GPU_vertbuf_attr_fill(vertex_buffer, value_pos, &value);
GPU_vertbuf_bind_as_texture(vertex_buffer,
GPU_shader_get_sampler_binding(shader, "bufferTexture"));
/* Construct SSBO. */
GPUStorageBuf *ssbo = GPU_storagebuf_create_ex(
4 * sizeof(float), nullptr, GPU_USAGE_DEVICE_ONLY, __func__);
GPU_storagebuf_bind(ssbo, GPU_shader_get_ssbo_binding(shader, "data_out"));
/* Dispatch compute task. */
GPU_compute_dispatch(shader, 4, 1, 1);
/* Check if compute has been done. */
GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE);
/* Download the storage buffer. */
Jeroen-Bakker marked this conversation as resolved Outdated

storage buffer

storage buffer
float4 read_data;
GPU_storagebuf_read(ssbo, read_data);
EXPECT_EQ(read_data, value);
/* Cleanup. */
GPU_shader_unbind();
GPU_storagebuf_free(ssbo);
GPU_vertbuf_discard(vertex_buffer);
GPU_shader_free(shader);
}
GPU_TEST(buffer_texture)
} // namespace blender::gpu::tests

View File

@ -243,7 +243,7 @@ static void test_shader_compute_ssbo()
/* Check if compute has been done. */
GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE);
/* Download the index buffer. */
/* Download the storage buffer. */
uint32_t data[SIZE];
GPU_storagebuf_read(ssbo, data);
for (int index = 0; index < SIZE; index++) {

View File

@ -0,0 +1,6 @@
void main()
{
int index = int(gl_GlobalInvocationID.x);
float value = texelFetch(bufferTexture, index).r;
data_out[index] = value;
}

View File

@ -17,6 +17,7 @@
#include "vk_pixel_buffer.hh"
#include "vk_query.hh"
#include "vk_shader.hh"
#include "vk_state_manager.hh"
#include "vk_storage_buffer.hh"
#include "vk_texture.hh"
#include "vk_uniform_buffer.hh"
@ -88,6 +89,7 @@ void VKBackend::samplers_update() {}
void VKBackend::compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len)
{
VKContext &context = *VKContext::get();
context.state_manager_get().apply_bindings();
context.bind_compute_pipeline();
VKCommandBuffer &command_buffer = context.command_buffer_get();
command_buffer.dispatch(groups_x_len, groups_y_len, groups_z_len);

View File

@ -9,6 +9,7 @@
#include "vk_context.hh"
#include "vk_index_buffer.hh"
#include "vk_state_manager.hh"
#include "vk_vertex_attribute_object.hh"
#include "vk_vertex_buffer.hh"
@ -22,7 +23,9 @@ void VKBatch::draw(int vertex_first, int vertex_count, int instance_first, int i
/* Finalize graphics pipeline */
VKContext &context = *VKContext::get();
context.state_manager->apply_state();
VKStateManager &state_manager = context.state_manager_get();
state_manager.apply_state();
state_manager.apply_bindings();
VKVertexAttributeObject vao;
vao.update_bindings(context, *this);
context.bind_graphics_pipeline(prim_type, vao);

View File

@ -94,6 +94,15 @@ void VKDescriptorSetTracker::bind(VKTexture &texture,
binding.vk_sampler = sampler.vk_handle();
}
void VKDescriptorSetTracker::bind(VKVertexBuffer &vertex_buffer,
const VKDescriptorSet::Location location)
{
Binding &binding = ensure_location(location);
binding.type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
binding.vk_buffer_view = vertex_buffer.vk_buffer_view_get();
binding.buffer_size = vertex_buffer.size_alloc_get();
}
VKDescriptorSetTracker::Binding &VKDescriptorSetTracker::ensure_location(
const VKDescriptorSet::Location location)
{
@ -115,8 +124,10 @@ void VKDescriptorSetTracker::update(VKContext &context)
tracked_resource_for(context, !bindings_.is_empty());
std::unique_ptr<VKDescriptorSet> &descriptor_set = active_descriptor_set();
VkDescriptorSet vk_descriptor_set = descriptor_set->vk_handle();
BLI_assert(vk_descriptor_set != VK_NULL_HANDLE);
Vector<VkDescriptorBufferInfo> buffer_infos;
buffer_infos.reserve(16);
Vector<VkWriteDescriptorSet> descriptor_writes;
for (const Binding &binding : bindings_) {
@ -138,7 +149,22 @@ void VKDescriptorSetTracker::update(VKContext &context)
descriptor_writes.append(write_descriptor);
}
for (const Binding &binding : bindings_) {
if (!binding.is_texel_buffer()) {
continue;
}
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.pTexelBufferView = &binding.vk_buffer_view;
descriptor_writes.append(write_descriptor);
}
Vector<VkDescriptorImageInfo> image_infos;
image_infos.reserve(16);
for (const Binding &binding : bindings_) {
if (!binding.is_image()) {
continue;
@ -161,9 +187,6 @@ void VKDescriptorSetTracker::update(VKContext &context)
descriptor_writes.append(write_descriptor);
}
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);

View File

@ -83,12 +83,14 @@ class VKDescriptorSet : NonCopyable {
VKDescriptorSet(VkDescriptorPool vk_descriptor_pool, VkDescriptorSet vk_descriptor_set)
: vk_descriptor_pool_(vk_descriptor_pool), vk_descriptor_set_(vk_descriptor_set)
{
BLI_assert(vk_descriptor_set_ != VK_NULL_HANDLE);
}
VKDescriptorSet(VKDescriptorSet &&other);
virtual ~VKDescriptorSet();
VKDescriptorSet &operator=(VKDescriptorSet &&other)
{
BLI_assert(other.vk_descriptor_set_ != VK_NULL_HANDLE);
vk_descriptor_set_ = other.vk_descriptor_set_;
vk_descriptor_pool_ = other.vk_descriptor_pool_;
other.mark_freed();
@ -118,6 +120,8 @@ class VKDescriptorSetTracker : protected VKResourceTracker<VKDescriptorSet> {
VkBuffer vk_buffer = VK_NULL_HANDLE;
VkDeviceSize buffer_size = 0;
VkBufferView vk_buffer_view = VK_NULL_HANDLE;
VKTexture *texture = nullptr;
VkSampler vk_sampler = VK_NULL_HANDLE;
@ -131,6 +135,11 @@ class VKDescriptorSetTracker : protected VKResourceTracker<VKDescriptorSet> {
return ELEM(type, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
}
bool is_texel_buffer() const
{
return ELEM(type, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER);
}
bool is_image() const
{
return ELEM(type,
@ -157,7 +166,8 @@ class VKDescriptorSetTracker : protected VKResourceTracker<VKDescriptorSet> {
/* TODO: bind as image */
void image_bind(VKTexture &texture, VKDescriptorSet::Location location);
void bind(VKTexture &texture, VKDescriptorSet::Location location, VKSampler &sampler);
/* Bind as uniform texel buffer. */
void bind(VKVertexBuffer &vertex_buffer, VKDescriptorSet::Location location);
/**
* Some shaders don't need any descriptor sets so we don't need to bind them.
*

View File

@ -9,6 +9,7 @@
#include "vk_immediate.hh"
#include "vk_data_conversion.hh"
#include "vk_state_manager.hh"
namespace blender::gpu {
@ -47,7 +48,9 @@ void VKImmediate::end()
VKContext &context = *VKContext::get();
BLI_assert(context.shader == unwrap(shader));
context.state_manager->apply_state();
VKStateManager &state_manager = context.state_manager_get();
state_manager.apply_state();
state_manager.apply_bindings();
vertex_attributes_.update_bindings(*this);
context.bind_graphics_pipeline(prim_type, vertex_attributes_);
vertex_attributes_.bind(context);

View File

@ -10,6 +10,7 @@
#include "vk_pipeline.hh"
#include "vk_shader.hh"
#include "vk_texture.hh"
#include "vk_vertex_buffer.hh"
#include "GPU_capabilities.h"
@ -21,8 +22,8 @@ VKStateManager::VKStateManager()
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());
texture_bindings_ = Array<TextureBinding>(max_bindings);
texture_bindings_.fill(TextureBinding());
uniform_buffer_bindings_ = Array<UniformBufferBinding>(max_bindings);
uniform_buffer_bindings_.fill(UniformBufferBinding());
}
@ -34,27 +35,33 @@ void VKStateManager::apply_state()
VKShader &shader = unwrap(*context.shader);
VKPipeline &pipeline = shader.pipeline_get();
pipeline.state_manager_get().set_state(state, mutable_state);
}
}
void VKStateManager::apply_bindings()
{
VKContext &context = *VKContext::get();
if (context.shader) {
for (int binding : IndexRange(image_bindings_.size())) {
if (image_bindings_[binding].texture == nullptr) {
continue;
if (image_bindings_[binding].texture) {
image_bindings_[binding].texture->image_bind(binding);
}
image_bindings_[binding].texture->image_bind(binding);
}
for (int binding : IndexRange(image_bindings_.size())) {
if (texture_bindings_[binding].texture == nullptr) {
continue;
for (int binding : IndexRange(texture_bindings_.size())) {
if (texture_bindings_[binding].texture) {
texture_bindings_[binding].texture->bind(binding, sampler_);
}
else if (texture_bindings_[binding].vertex_buffer) {
texture_bindings_[binding].vertex_buffer->bind(binding);
}
texture_bindings_[binding].texture->bind(binding, sampler_);
}
for (int binding : IndexRange(uniform_buffer_bindings_.size())) {
if (uniform_buffer_bindings_[binding].buffer == nullptr) {
continue;
if (uniform_buffer_bindings_[binding].buffer) {
uniform_buffer_bindings_[binding].buffer->bind(
binding, shader::ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER);
}
uniform_buffer_bindings_[binding].buffer->bind(
binding, shader::ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER);
}
}
}
@ -81,12 +88,13 @@ void VKStateManager::texture_bind(Texture *tex, GPUSamplerState /*sampler*/, int
{
VKTexture *texture = unwrap(tex);
texture_bindings_[unit].texture = texture;
texture_bindings_[unit].vertex_buffer = nullptr;
}
void VKStateManager::texture_unbind(Texture *tex)
{
VKTexture *texture = unwrap(tex);
for (ImageBinding &binding : texture_bindings_) {
for (TextureBinding &binding : texture_bindings_) {
if (binding.texture == texture) {
binding.texture = nullptr;
}
@ -95,10 +103,8 @@ void VKStateManager::texture_unbind(Texture *tex)
void VKStateManager::texture_unbind_all()
{
for (ImageBinding &binding : texture_bindings_) {
if (binding.texture != nullptr) {
binding.texture = nullptr;
}
for (TextureBinding &binding : texture_bindings_) {
binding.texture = nullptr;
}
}
@ -120,10 +126,8 @@ void VKStateManager::image_unbind(Texture *tex)
void VKStateManager::image_unbind_all()
{
for (ImageBinding &binding : texture_bindings_) {
if (binding.texture != nullptr) {
binding.texture = nullptr;
}
for (TextureBinding &binding : texture_bindings_) {
binding.texture = nullptr;
}
}
@ -141,6 +145,22 @@ void VKStateManager::uniform_buffer_unbind(VKUniformBuffer *uniform_buffer)
}
}
void VKStateManager::texel_buffer_bind(VKVertexBuffer *vertex_buffer, int slot)
{
texture_bindings_[slot].vertex_buffer = vertex_buffer;
texture_bindings_[slot].texture = nullptr;
}
void VKStateManager::texel_buffer_unbind(VKVertexBuffer *vertex_buffer)
{
for (TextureBinding &binding : texture_bindings_) {
if (binding.vertex_buffer == vertex_buffer) {
binding.vertex_buffer = nullptr;
binding.texture = nullptr;
}
}
}
void VKStateManager::texture_unpack_row_length_set(uint len)
{
texture_unpack_row_length_ = len;

View File

@ -16,6 +16,7 @@
namespace blender::gpu {
class VKTexture;
class VKUniformBuffer;
class VKVertexBuffer;
class VKStateManager : public StateManager {
/* Dummy sampler for now.*/
@ -23,6 +24,11 @@ class VKStateManager : public StateManager {
uint texture_unpack_row_length_ = 0;
struct TextureBinding {
VKTexture *texture = nullptr;
/* bufferTextures and samplers share the same namespace. */
VKVertexBuffer *vertex_buffer = nullptr;
};
struct ImageBinding {
VKTexture *texture = nullptr;
};
@ -30,7 +36,7 @@ class VKStateManager : public StateManager {
VKUniformBuffer *buffer = nullptr;
};
Array<ImageBinding> image_bindings_;
Array<ImageBinding> texture_bindings_;
Array<TextureBinding> texture_bindings_;
Array<UniformBufferBinding> uniform_buffer_bindings_;
public:
@ -41,6 +47,9 @@ class VKStateManager : public StateManager {
void issue_barrier(eGPUBarrier barrier_bits) override;
/** Apply resources to the bindings of the active shader.*/
void apply_bindings();
void texture_bind(Texture *tex, GPUSamplerState sampler, int unit) override;
void texture_unbind(Texture *tex) override;
void texture_unbind_all() override;
@ -52,6 +61,9 @@ class VKStateManager : public StateManager {
void uniform_buffer_bind(VKUniformBuffer *uniform_buffer, int slot);
void uniform_buffer_unbind(VKUniformBuffer *uniform_buffer);
void texel_buffer_bind(VKVertexBuffer *vertex_buffer, int slot);
void texel_buffer_unbind(VKVertexBuffer *vertex_buffer);
void texture_unpack_row_length_set(uint len) override;
/**

View File

@ -14,6 +14,7 @@
#include "vk_shader.hh"
#include "vk_shader_interface.hh"
#include "vk_state_manager.hh"
#include "vk_vertex_buffer.hh"
#include "BLI_math_vector.hh"
@ -222,10 +223,29 @@ bool VKTexture::init_internal()
return true;
}
bool VKTexture::init_internal(GPUVertBuf * /*vbo*/)
bool VKTexture::init_internal(GPUVertBuf *vbo)
{
NOT_YET_IMPLEMENTED;
return false;
if (!allocate()) {
return false;
}
VKVertexBuffer *vertex_buffer = unwrap(unwrap(vbo));
VkBufferImageCopy region = {};
region.imageExtent.width = w_;
region.imageExtent.height = 1;
region.imageExtent.depth = 1;
region.imageSubresource.aspectMask = to_vk_image_aspect_flag_bits(format_);
region.imageSubresource.mipLevel = 0;
region.imageSubresource.layerCount = 1;
VKContext &context = *VKContext::get();
layout_ensure(context, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
VKCommandBuffer &command_buffer = context.command_buffer_get();
command_buffer.copy(*this, vertex_buffer->buffer_, Span<VkBufferImageCopy>(&region, 1));
command_buffer.submit();
return true;
}
bool VKTexture::init_internal(GPUTexture * /*src*/,

View File

@ -8,8 +8,10 @@
#include "MEM_guardedalloc.h"
#include "vk_data_conversion.hh"
#include "vk_memory.hh"
#include "vk_shader.hh"
#include "vk_shader_interface.hh"
#include "vk_state_manager.hh"
#include "vk_vertex_buffer.hh"
namespace blender::gpu {
@ -35,9 +37,44 @@ void VKVertexBuffer::bind_as_ssbo(uint binding)
shader->pipeline_get().descriptor_set_get().bind_as_ssbo(*this, *location);
}
void VKVertexBuffer::bind_as_texture(uint /*binding*/)
void VKVertexBuffer::bind_as_texture(uint binding)
{
NOT_YET_IMPLEMENTED
VKContext &context = *VKContext::get();
VKStateManager &state_manager = context.state_manager_get();
state_manager.texel_buffer_bind(this, binding);
should_unbind_ = true;
}
void VKVertexBuffer::bind(uint binding)
{
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, binding);
if (!location) {
return;
}
upload_data();
if (vk_buffer_view_ == VK_NULL_HANDLE) {
VkBufferViewCreateInfo buffer_view_info = {};
eGPUTextureFormat texture_format = to_texture_format(&format);
buffer_view_info.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
buffer_view_info.buffer = buffer_.vk_handle();
buffer_view_info.format = to_vk_format(texture_format);
buffer_view_info.range = buffer_.size_in_bytes();
VK_ALLOCATION_CALLBACKS;
const VKDevice &device = VKBackend::get().device_get();
vkCreateBufferView(
device.device_get(), &buffer_view_info, vk_allocation_callbacks, &vk_buffer_view_);
}
shader->pipeline_get().descriptor_set_get().bind(*this, *location);
}
void VKVertexBuffer::wrap_handle(uint64_t /*handle*/)
@ -81,6 +118,18 @@ void VKVertexBuffer::resize_data()
void VKVertexBuffer::release_data()
{
if (should_unbind_) {
VKContext &context = *VKContext::get();
context.state_manager_get().texel_buffer_unbind(this);
}
if (vk_buffer_view_ != VK_NULL_HANDLE) {
VK_ALLOCATION_CALLBACKS;
const VKDevice &device = VKBackend::get().device_get();
vkDestroyBufferView(device.device_get(), vk_buffer_view_, vk_allocation_callbacks);
vk_buffer_view_ = VK_NULL_HANDLE;
}
MEM_SAFE_FREE(data);
}
@ -105,6 +154,9 @@ void VKVertexBuffer::upload_data()
if (!buffer_.is_allocated()) {
allocate();
}
if (!ELEM(usage_, GPU_USAGE_STATIC, GPU_USAGE_STREAM, GPU_USAGE_DYNAMIC)) {
return;
}
if (flag & GPU_VERTBUF_DATA_DIRTY) {
void *data_to_upload = data;
@ -133,8 +185,10 @@ void VKVertexBuffer::allocate()
{
buffer_.create(size_alloc_get(),
usage_,
static_cast<VkBufferUsageFlagBits>(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT));
static_cast<VkBufferUsageFlagBits>(VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT));
debug::object_label(buffer_.vk_handle(), "VertexBuffer");
}

View File

@ -15,12 +15,16 @@ namespace blender::gpu {
class VKVertexBuffer : public VertBuf {
VKBuffer buffer_;
bool should_unbind_ = false;
/** When a vertex buffer is used as a UNIFORM_TEXEL_BUFFER the buffer requires a buffer view. */
VkBufferView vk_buffer_view_ = VK_NULL_HANDLE;
public:
~VKVertexBuffer();
void bind_as_ssbo(uint binding) override;
void bind_as_texture(uint binding) override;
void bind(uint binding);
void wrap_handle(uint64_t handle) override;
void update_sub(uint start, uint len, const void *data) override;
@ -32,6 +36,12 @@ class VKVertexBuffer : public VertBuf {
return buffer_.vk_handle();
}
VkBufferView vk_buffer_view_get() const
{
BLI_assert(vk_buffer_view_ != VK_NULL_HANDLE);
return vk_buffer_view_;
}
protected:
void acquire_data() override;
void resize_data() override;
@ -42,6 +52,9 @@ class VKVertexBuffer : public VertBuf {
private:
void allocate();
void *convert() const;
/* VKTexture requires access to `buffer_` to convert a vertex buffer to a texture.*/
friend class VKTexture;
};
static inline VKVertexBuffer *unwrap(VertBuf *vertex_buffer)