Files
test/source/blender/gpu/vulkan/vk_state_manager.hh
Miguel Pozo a8975bf651 Fix #142097: Vulkan: Crash with unbound textures
On the Vulkan side, ensure that unbound textures don't result in
accessing uninitialized or out of bounds memory.
On the Draw side, ensure all Hair and Curves attributes have, at least,
a dummy attribute bound.

Pull Request: https://projects.blender.org/blender/blender/pulls/142265
2025-07-21 15:14:10 +02:00

270 lines
6.5 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup gpu
*/
#pragma once
#include "gpu_state_private.hh"
#include "BLI_array.hh"
#include "render_graph/vk_resource_access_info.hh"
namespace blender::gpu {
class VKTexture;
class VKUniformBuffer;
class VKVertexBuffer;
class VKStorageBuffer;
class VKIndexBuffer;
class VKContext;
class VKDescriptorSetTracker;
/**
* Offset when searching for bindings.
*
* When shaders combine images and samplers, the images have to be offset to find the correct
* shader input. Both textures and images are stored in the uniform list and their ID can be
* overlapping.
*/
static constexpr int BIND_SPACE_IMAGE_OFFSET = 512;
/** Bind space for a uniform buffers. */
class BindSpaceUniformBuffers {
public:
Vector<VKUniformBuffer *> bound_resources;
void bind(VKUniformBuffer *resource, int binding)
{
if (bound_resources.size() <= binding) {
bound_resources.resize(binding + 1);
}
bound_resources[binding] = resource;
}
VKUniformBuffer *get(int binding) const
{
return bound_resources[binding];
}
void unbind(void *resource)
{
for (int index : IndexRange(bound_resources.size())) {
if (bound_resources[index] == resource) {
bound_resources[index] = nullptr;
}
}
}
void unbind_all()
{
bound_resources.clear();
}
};
/**
* Bind space for image resources.
*/
template<int Offset> class BindSpaceImages {
public:
Vector<VKTexture *> bound_resources;
void bind(VKTexture *resource, int binding)
{
if (binding >= Offset) {
binding -= Offset;
}
if (bound_resources.size() <= binding) {
bound_resources.resize(binding + 1);
}
bound_resources[binding] = resource;
}
VKTexture *get(int binding) const
{
if (binding >= Offset) {
binding -= Offset;
}
return bound_resources[binding];
}
void unbind(void *resource)
{
for (int index : IndexRange(bound_resources.size())) {
if (bound_resources[index] == resource) {
bound_resources[index] = nullptr;
}
}
}
void unbind_all()
{
bound_resources.clear();
}
};
/** Bind space for storage buffers. */
class BindSpaceStorageBuffers {
public:
enum class Type {
Unused,
UniformBuffer,
VertexBuffer,
IndexBuffer,
StorageBuffer,
Buffer,
};
struct Elem {
Type resource_type;
void *resource;
VkDeviceSize offset;
};
Vector<Elem> bound_resources;
void bind(Type resource_type, void *resource, int binding, VkDeviceSize offset)
{
if (bound_resources.size() <= binding) {
bound_resources.resize(binding + 1);
}
bound_resources[binding].resource_type = resource_type;
bound_resources[binding].resource = resource;
bound_resources[binding].offset = offset;
}
const Elem &get(int binding) const
{
return bound_resources[binding];
}
void unbind(void *resource)
{
for (int index : IndexRange(bound_resources.size())) {
if (bound_resources[index].resource == resource) {
bound_resources[index].resource = nullptr;
bound_resources[index].resource_type = Type::Unused;
bound_resources[index].offset = 0u;
}
}
}
void unbind_all()
{
bound_resources.clear();
}
};
/** Bind space for textures. */
class BindSpaceTextures {
public:
enum class Type {
Unused,
Texture,
VertexBuffer,
};
struct Elem {
Type resource_type;
void *resource;
GPUSamplerState sampler;
};
Vector<Elem> bound_resources;
void bind(Type resource_type, void *resource, GPUSamplerState sampler, int binding)
{
if (bound_resources.size() <= binding) {
bound_resources.resize(binding + 1, {});
}
bound_resources[binding].resource_type = resource_type;
bound_resources[binding].resource = resource;
bound_resources[binding].sampler = sampler;
}
const Elem *get(int binding) const
{
if (binding >= bound_resources.size()) {
/* TODO: Check with @Jeroen-Bakker.
* Could we ensure state_manager adds default initialized bindings for each ShaderInterface
* resource? (See #142097). */
return nullptr;
}
return &bound_resources[binding];
}
void unbind(void *resource)
{
for (int index : IndexRange(bound_resources.size())) {
if (bound_resources[index].resource == resource) {
bound_resources[index].resource = nullptr;
bound_resources[index].resource_type = Type::Unused;
bound_resources[index].sampler = GPUSamplerState::default_sampler();
}
}
}
void unbind_all()
{
bound_resources.clear();
}
};
class VKStateManager : public StateManager {
friend class VKDescriptorSetUpdator;
uint texture_unpack_row_length_ = 0;
BindSpaceTextures textures_;
BindSpaceImages<BIND_SPACE_IMAGE_OFFSET> images_;
BindSpaceUniformBuffers uniform_buffers_;
BindSpaceStorageBuffers storage_buffers_;
public:
bool is_dirty = false;
void apply_state() override;
void force_state() override;
void issue_barrier(eGPUBarrier barrier_bits) override;
void texture_bind(Texture *tex, GPUSamplerState sampler, int unit) override;
void texture_unbind(Texture *tex) override;
void texture_unbind_all() override;
void image_bind(Texture *tex, int unit) override;
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 uniform_buffer_unbind_all();
void texel_buffer_bind(VKVertexBuffer &vertex_buffer, int slot);
void texel_buffer_unbind(VKVertexBuffer &vertex_buffer);
void storage_buffer_bind(BindSpaceStorageBuffers::Type resource_type,
void *resource,
int binding)
{
storage_buffer_bind(resource_type, resource, binding, 0u);
}
void storage_buffer_bind(BindSpaceStorageBuffers::Type resource_type,
void *resource,
int binding,
VkDeviceSize offset);
void storage_buffer_unbind(void *resource);
void storage_buffer_unbind_all();
void unbind_from_all_namespaces(void *resource);
void texture_unpack_row_length_set(uint len) override;
/**
* Row length for unpacking host data when uploading texture data.
*
* When set to zero (0) host data can be assumed to be stored sequential.
*/
uint texture_unpack_row_length_get() const;
};
} // namespace blender::gpu