Files
test2/source/blender/gpu/vulkan/vk_buffer.hh
Jeroen Bakker 15d88e544a GPU: Storage buffer allocation alignment
Since the introduction of storage buffers in Blender, the calling
code has been responsible for ensuring the buffer meets allocation
requirements. All backends require the allocation size to be divisible
by 16 bytes. Until now, this was sufficient, but with GPU subdivision
changes, an external library must also adhere to these requirements.

For OpenSubdiv (OSD), some buffers are not 16-byte aligned, leading
to potential misallocation. Currently, this is mitigated by allocating
a few extra bytes, but this approach has the drawback of potentially
reading unintended bytes beyond the source buffer.

This PR adopts a similar approach to vertex buffers: the backend handles
extra byte allocation while ensuring data uploads and downloads function
correctly without requiring those additional bytes.

No changes were needed for Metal, as its allocation size is already
aligned to 256 bytes.

**Alternative solutions considered**:

- Copying the CPU buffer to a larger buffer when needed (performance impact).
- Modifying OSD buffers to allocate extra space (requires changes to an external library).
- Implementing GPU_storagebuf_update_sub.

Ref #135873

Pull Request: https://projects.blender.org/blender/blender/pulls/135716
2025-03-13 15:05:16 +01:00

130 lines
3.0 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup gpu
*/
#pragma once
#include "gpu_context_private.hh"
#include "BLI_utility_mixins.hh"
#include "vk_common.hh"
namespace blender::gpu {
class VKContext;
class VKDevice;
/**
* Class for handing vulkan buffers (allocation/updating/binding).
*/
class VKBuffer : public NonCopyable {
size_t size_in_bytes_ = 0;
size_t alloc_size_in_bytes_ = 0;
VkBuffer vk_buffer_ = VK_NULL_HANDLE;
VmaAllocation allocation_ = VK_NULL_HANDLE;
VkMemoryPropertyFlags vk_memory_property_flags_;
TimelineValue async_timeline_ = 0;
/* Pointer to the virtually mapped memory. */
void *mapped_memory_ = nullptr;
public:
VKBuffer() = default;
virtual ~VKBuffer();
/** Has this buffer been allocated? */
bool is_allocated() const;
/**
* Allocate the buffer.
*/
bool create(size_t size,
VkBufferUsageFlags buffer_usage,
VkMemoryPropertyFlags required_flags,
VkMemoryPropertyFlags preferred_flags,
VmaAllocationCreateFlags vma_allocation_flags);
void clear(VKContext &context, uint32_t clear_value);
void update_immediately(const void *data) const;
void update_sub_immediately(size_t start_offset, size_t data_size, const void *data) const;
/**
* Update the buffer as part of the render graph evaluation. The ownership of data will be
* transferred to the render graph and should have been allocated using guarded alloc.
*/
void update_render_graph(VKContext &context, void *data) const;
void flush() const;
/**
* Read the buffer (synchronously).
*/
void read(VKContext &context, void *data) const;
/**
* Start a async read-back.
*/
void async_flush_to_host(VKContext &context);
/**
* Wait until the async read back is finished and fill the given data with the content of the
* buffer.
*
* Will start a new async read-back when there is no read back in progress.
*/
void read_async(VKContext &context, void *data);
/**
* Free the buffer.
*
* Discards the buffer so it can be destroyed safely later. Buffers can still be used when
* rendering so we can only destroy them after the rendering is completed.
*/
bool free();
/**
* Destroy the buffer immediately.
*/
void free_immediately(VKDevice &device);
int64_t size_in_bytes() const
{
return size_in_bytes_;
}
VkBuffer vk_handle() const
{
return vk_buffer_;
}
/**
* Get the reference to the mapped memory.
*
* Can only be called when the buffer is (still) mapped.
*/
void *mapped_memory_get() const;
/**
* Is this buffer mapped (visible on host)
*/
bool is_mapped() const;
private:
/** Check if this buffer is mapped. */
bool map();
void unmap();
};
/**
* Helper struct to enable buffers to be bound with an offset.
*
* Used for de-interleaved vertex input buffers and immediate mode buffers.
*/
struct VKBufferWithOffset {
const VkBuffer buffer;
VkDeviceSize offset;
};
} // namespace blender::gpu